Я собираюсь глубоко погрузиться в шестнадцатеричную операцию над вашим файлом, причину исключения в iText и, в конечном итоге, причину этой ошибки. Затем я перейду к стяжке, описывающей, почему это происходит.
Ваш файл структурирован так, что основной IFD находится в конце файла. Вот заголовок файла:
49 49 2A 00 96 6C 00 00
intel magic offset-----
В котором говорится: «Я TIFF в порядке байтов Intel (с прямым порядком байтов), и мой основной IFD начинается со смещения 0x6c9c.
Если вы пропустите это место, вы увидите это:
0F 00 <- this is the total number of tags, each tag is 12 bytes
# | ID |Type | Count | Value |
01. 00 01 04 00 01 00 00 00 A2 06 00 00 width = 6a2
02. 01 01 04 00 01 00 00 00 4A 04 00 00 height = 44a
03. 02 01 03 00 01 00 00 00 01 00 00 00 bits per sample = 1
04. 03 01 03 00 01 00 00 00 04 00 00 00 Compression = CCITT G4
05. 06 01 03 00 01 00 00 00 00 00 00 00 Photometric = min is white
06. 0A 01 04 00 01 00 00 00 01 00 00 00 Fill order = msb to lsb
07. 11 01 04 00 01 00 00 00 08 00 00 00 Offset of strips = 8
08. 15 01 03 00 01 00 00 00 01 00 00 00 Samples per pixel = 4
09. 16 01 04 00 01 00 00 00 4A 04 00 00 Rows per strip = 448
0a. 17 01 04 00 01 00 00 00 5B 6C 00 00 Strip byte counts = 6c5b
0b. 1A 01 05 00 01 00 00 00 63 6C 00 00 Offset to x resolution = 6c63
0c. 1B 01 05 00 01 00 00 00 6B 6C 00 00 Offset to y resolution = 6c6b
0d. 1C 01 03 00 01 00 00 00 01 00 00 00 Planar Config = Contiguous
0e. 28 01 03 00 01 00 00 00 02 00 00 00 Resolution unit = inches
0f. 31 01 02 00 23 00 00 00 73 6C 00 00 Software string offset = 6c73
Location of next IFD, 0 means no more
00 00 00 00
Теперь, глядя на стек вызовов и прослеживая его до источника, я вижу, что вызов выполняется для получения порядка заполнения. Порядок заполнения для 1-битных файлов описывает, является ли бит старшего или младшего разряда в байте самым левым на дисплее.
TIFFField fillOrderField = dir.getField(TIFFConstants.TIFFTAG_FILLORDER);
if (fillOrderField != null)
fillOrder = fillOrderField.getAsInt(0);
Мы знаем, что это будет вызвано, так как в вашем IFD есть тег порядка заполнения, который представляет собой 4-байтовое целое число со значением 1.
К сожалению для вас, этот вызов TIFFFIELD.getAsInt(0)
вызывает сбой.
Если вы посмотрите на этот код:
public int getAsInt(int index) {
switch (type) {
case TIFF_BYTE: case TIFF_UNDEFINED:
return ((byte[])data)[index] & 0xff;
case TIFF_SBYTE:
return ((byte[])data)[index];
case TIFF_SHORT:
return ((char[])data)[index] & 0xffff;
case TIFF_SSHORT:
return ((short[])data)[index];
case TIFF_SLONG:
return ((int[])data)[index];
default:
throw new ClassCastException();
}
}
Вы можете видеть, что он может генерировать исключение ClassCastException, если тип не соответствует, и в этом случае это произойдет, поскольку эти константы типа в случаях равны 1, 7, 6, 3, 8 и 9 соответственно, а тип тега равен 4.
Так почему код неверный?
Проблема с тегами TIFF заключается в том, что, несмотря на то, что в спецификации довольно четко указано, что тег FillOrder (10a) должен быть беззнаковым коротким (тип 3), тег в вашем файле представляет собой беззнаковое 4-байтовое целое число (тип 4), но оператор switch не учитывает это (для TIFF_LONG нет случая).
Почему для этого нет дела? Глядя на окружающий код, эта библиотека обрабатывает 4-байтовые целые числа без знака как тип java «long», и попытка обрабатывать 4-байтовое целое число без знака как 4-байтовое целое число со знаком может привести к переполнению знакового бита (даже если ни одно из допустимых значений поскольку этот тег вызовет это), поэтому, поскольку это приведение может вызвать ошибку, оно всегда будет рассматриваться как одно.
В конечном счете причиной этой ошибки являются две вещи:
- Java имеет только один беззнаковый целочисленный тип (
char
, для тех из вас, кто играет дома), и эта библиотека решила использовать long
для представления беззнакового 4-байтового целого числа.
- Этот конкретный файл не соответствует спецификации и использует
unsigned int
для этого тега.
Или, точнее, существует несоответствие импеданса между выбранными типами Java и этим файлом TIFF. Этот код поля пытается быть типоустойчивым. Вызывающий код пытается принимать самые разные типы. Он пропустил этот случай.
Я посмотрел на свой собственный код тега для ухмылки, чтобы увидеть, будет ли он страдать от этой конкретной проблемы. Ответ - нет, так как моя версия getIntValue() позволит вам переполниться битом знака, если это то, что вы хотите сделать.
Таким образом, реальное исправление состоит в том, чтобы изменить код на:
TIFFField fillOrderField = dir.getField(TIFFConstants.TIFFTAG_FILLORDER);
if (fillOrderField != null)
fillOrder = (int)fillOrderField.getAsLong(0);
или, альтернативно, выполнить операцию HEX над вашим файлом и изменить тип данных тега порядка заполнения на unsigned short
. В конечном счете, это плохое решение, поскольку потребляющий код по-прежнему восприимчив к плохим файлам TIFF.
Бесплатная стяжка
Одна вещь, которую я усвоил за последние 10 лет работы с файлами TIFF, заключается в том, что нет недостатка в сломанных файлах TIFF и нет недостатка в инженерах, которые либо не читали спецификацию, либо не смогли правильно ее реализовать, создав новые поврежденные файлы ( и время от времени я был этим инженером). Некоторые из них являются аспирантами, которым нужен вывод TIFF ПРЯМО СЕЙЧАС, и они пишут быстрый и грязный (сломанный) кодировщик, который они считают правильным, когда IrfanView может открыть их вывод (что является недопустимым тестом, поскольку IrfanView, а также мой кодек TIFF, открывает множество принципиально испорченных файлов TIFF).
спецификация TIFF обманчиво проста. Я говорю это потому, что сам формат кажется, что его относительно легко создать. Теги логичны, IFD — это простые наборы тегов, теги-указатели могут быть сложными, но ими можно управлять. Что происходит, так это то, что написан код, которому не хватает уровня абстракции, который предотвратил бы классы ошибок, которые в противном случае проскользнули бы.
Этот конкретный файл не был написан аспирантом. По крайней мере, я так не думаю.
В данном случае проблема, скорее всего, была вызвана fCoder. Мы знаем это, потому что они поместили это в строку программного обеспечения Created by fCoder Graphics Processor
. Я вызываю их, потому что они используют программную строку, чтобы идентифицировать себя. Эта ошибка (неправильный тип, вероятно, из-за ошибки копирования-вставки в их источнике), хотя и незначительная ошибка, вызывает проблемы, и, возможно, они это исправят. В моем мире первоочередная ошибка № 1 при удалении всего — это «создание плохого файла». и если бы я сделал это, я, черт возьми, хотел бы знать, чтобы исправить свой код. Между тем, iText также должен обновить свой код, чтобы иметь возможность принимать этот класс файлов.
Уроки выучены:
- Спецификация — это ответ на вопрос «правилен ли мой файл».
- Трудно написать приличный кодировщик или декодер TIFF. Рассмотрите коммерческую библиотеку, прежде чем писать свою (хотя в этом примере мы нашли ошибки не в одной, а в двух коммерческих библиотеках).
- Введите строку программного обеспечения при создании файла, чтобы мы могли связаться с вами в случае возникновения проблемы.
Вот и конец урока.
person
plinth
schedule
12.05.2015
Image tempImage=TiffImage.getTiffImage(myTiffFile, i, true);
иImage tempImage=Image.getInstance("C:\\local\\docs\\test.01.tif",true);
- person Timm-ah   schedule 11.05.2015