1
1
/*
2
- * Copyright 2002-2019 Drew Noakes and contributors
2
+ * Copyright 2002-2022 Drew Noakes and contributors
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
@@ -40,17 +40,15 @@ public class TiffReader
40
40
*
41
41
* @param reader the {@link RandomAccessReader} from which the data should be read
42
42
* @param handler the {@link TiffHandler} that will coordinate processing and accept read values
43
- * @param tiffHeaderOffset the offset within <code>reader</code> at which the TIFF header starts
44
43
* @throws TiffProcessingException if an error occurred during the processing of TIFF data that could not be
45
44
* ignored or recovered from
46
45
* @throws IOException an error occurred while accessing the required data
47
46
*/
48
47
public void processTiff (@ NotNull final RandomAccessReader reader ,
49
- @ NotNull final TiffHandler handler ,
50
- final int tiffHeaderOffset ) throws TiffProcessingException , IOException
48
+ @ NotNull final TiffHandler handler ) throws TiffProcessingException , IOException
51
49
{
52
50
// This must be either "MM" or "II".
53
- short byteOrderIdentifier = reader .getInt16 (tiffHeaderOffset );
51
+ short byteOrderIdentifier = reader .getInt16 (0 );
54
52
55
53
if (byteOrderIdentifier == 0x4d4d ) { // "MM"
56
54
reader .setMotorolaByteOrder (true );
@@ -61,21 +59,21 @@ public void processTiff(@NotNull final RandomAccessReader reader,
61
59
}
62
60
63
61
// Check the next two values for correctness.
64
- final int tiffMarker = reader .getUInt16 (2 + tiffHeaderOffset );
62
+ final int tiffMarker = reader .getUInt16 (2 );
65
63
handler .setTiffMarker (tiffMarker );
66
64
67
- int firstIfdOffset = reader .getInt32 (4 + tiffHeaderOffset ) + tiffHeaderOffset ;
65
+ int firstIfdOffset = reader .getInt32 (4 ) ;
68
66
69
67
// David Ekholm sent a digital camera image that has this problem
70
68
// TODO getLength should be avoided as it causes RandomAccessStreamReader to read to the end of the stream
71
69
if (firstIfdOffset >= reader .getLength () - 1 ) {
72
70
handler .warn ("First IFD offset is beyond the end of the TIFF data segment -- trying default offset" );
73
71
// First directory normally starts immediately after the offset bytes, so try that
74
- firstIfdOffset = tiffHeaderOffset + 2 + 2 + 4 ;
72
+ firstIfdOffset = 2 + 2 + 4 ;
75
73
}
76
74
77
75
Set <Integer > processedIfdOffsets = new HashSet <Integer >();
78
- processIfd (handler , reader , processedIfdOffsets , firstIfdOffset , tiffHeaderOffset );
76
+ processIfd (handler , reader , processedIfdOffsets , firstIfdOffset );
79
77
}
80
78
81
79
/**
@@ -96,27 +94,28 @@ public void processTiff(@NotNull final RandomAccessReader reader,
96
94
*
97
95
* @param handler the {@link com.drew.imaging.tiff.TiffHandler} that will coordinate processing and accept read values
98
96
* @param reader the {@link com.drew.lang.RandomAccessReader} from which the data should be read
99
- * @param processedIfdOffsets the set of visited IFD offsets, to avoid revisiting the same IFD in an endless loop
97
+ * @param processedGlobalIfdOffsets the set of visited IFD offsets, to avoid revisiting the same IFD in an endless loop
100
98
* @param ifdOffset the offset within <code>reader</code> at which the IFD data starts
101
- * @param tiffHeaderOffset the offset within <code>reader</code> at which the TIFF header starts
102
99
* @throws IOException an error occurred while accessing the required data
103
100
*/
104
101
public static void processIfd (@ NotNull final TiffHandler handler ,
105
102
@ NotNull final RandomAccessReader reader ,
106
- @ NotNull final Set <Integer > processedIfdOffsets ,
107
- final int ifdOffset ,
108
- final int tiffHeaderOffset ) throws IOException
103
+ @ NotNull final Set <Integer > processedGlobalIfdOffsets ,
104
+ final int ifdOffset ) throws IOException
109
105
{
110
106
Boolean resetByteOrder = null ;
111
107
try {
112
- // check for directories we've already visited to avoid stack overflows when recursive/cyclic directory structures exist
113
- if (processedIfdOffsets .contains (Integer .valueOf (ifdOffset ))) {
108
+ // Check for directories we've already visited to avoid stack overflows when recursive/cyclic directory structures exist.
109
+ // Note that we track these offsets in the global frame, not the reader's local frame.
110
+ int globalIfdOffset = reader .toUnshiftedOffset (ifdOffset );
111
+ if (processedGlobalIfdOffsets .contains (Integer .valueOf (globalIfdOffset ))) {
114
112
return ;
115
113
}
116
114
117
115
// remember that we've visited this directory so that we don't visit it again later
118
- processedIfdOffsets .add (ifdOffset );
116
+ processedGlobalIfdOffsets .add (globalIfdOffset );
119
117
118
+ // Validate IFD offset
120
119
if (ifdOffset >= reader .getLength () || ifdOffset < 0 ) {
121
120
handler .error ("Ignored IFD marked to start outside data segment" );
122
121
return ;
@@ -180,13 +179,12 @@ public static void processIfd(@NotNull final TiffHandler handler,
180
179
final long tagValueOffset ;
181
180
if (byteCount > 4 ) {
182
181
// If it's bigger than 4 bytes, the dir entry contains an offset.
183
- final long offsetVal = reader .getUInt32 (tagOffset + 8 );
184
- if (offsetVal + byteCount > reader .getLength ()) {
182
+ tagValueOffset = reader .getUInt32 (tagOffset + 8 );
183
+ if (tagValueOffset + byteCount > reader .getLength ()) {
185
184
// Bogus pointer offset and / or byteCount value
186
185
handler .error ("Illegal TIFF tag pointer offset" );
187
186
continue ;
188
187
}
189
- tagValueOffset = tiffHeaderOffset + offsetVal ;
190
188
} else {
191
189
// 4 bytes or less and value is in the dir entry itself.
192
190
tagValueOffset = tagOffset + 8 ;
@@ -210,14 +208,14 @@ public static void processIfd(@NotNull final TiffHandler handler,
210
208
for (int i = 0 ; i < componentCount ; i ++) {
211
209
if (handler .tryEnterSubIfd (tagId )) {
212
210
isIfdPointer = true ;
213
- int subDirOffset = tiffHeaderOffset + reader .getInt32 ((int ) (tagValueOffset + i * 4 ));
214
- processIfd (handler , reader , processedIfdOffsets , subDirOffset , tiffHeaderOffset );
211
+ long subDirOffset = reader .getUInt32 ((int ) (tagValueOffset + i * 4 ));
212
+ processIfd (handler , reader , processedGlobalIfdOffsets , ( int ) subDirOffset );
215
213
}
216
214
}
217
215
}
218
216
219
217
// If it wasn't an IFD pointer, allow custom tag processing to occur
220
- if (!isIfdPointer && !handler .customProcessTag ((int ) tagValueOffset , processedIfdOffsets , tiffHeaderOffset , reader , tagId , (int ) byteCount )) {
218
+ if (!isIfdPointer && !handler .customProcessTag ((int ) tagValueOffset , processedGlobalIfdOffsets , reader , tagId , (int ) byteCount )) {
221
219
// If no custom processing occurred, process the tag in the standard fashion
222
220
processTag (handler , tagId , (int ) tagValueOffset , (int ) componentCount , formatCode , reader );
223
221
}
@@ -227,10 +225,8 @@ public static void processIfd(@NotNull final TiffHandler handler,
227
225
final int finalTagOffset = calculateTagOffset (ifdOffset , dirTagCount );
228
226
int nextIfdOffset = reader .getInt32 (finalTagOffset );
229
227
if (nextIfdOffset != 0 ) {
230
- nextIfdOffset += tiffHeaderOffset ;
231
228
if (nextIfdOffset >= reader .getLength ()) {
232
229
// Last 4 bytes of IFD reference another IFD with an address that is out of bounds
233
- // Note this could have been caused by jhead 1.3 cropping too much
234
230
return ;
235
231
} else if (nextIfdOffset < ifdOffset ) {
236
232
// TODO is this a valid restriction?
@@ -239,7 +235,7 @@ public static void processIfd(@NotNull final TiffHandler handler,
239
235
}
240
236
241
237
if (handler .hasFollowerIfd ()) {
242
- processIfd (handler , reader , processedIfdOffsets , nextIfdOffset , tiffHeaderOffset );
238
+ processIfd (handler , reader , processedGlobalIfdOffsets , nextIfdOffset );
243
239
}
244
240
}
245
241
} finally {
@@ -326,7 +322,7 @@ private static void processTag(@NotNull final TiffHandler handler,
326
322
break ;
327
323
case TiffDataFormat .CODE_INT16_S :
328
324
if (componentCount == 1 ) {
329
- handler .setInt16s (tagId , ( int ) reader .getInt16 (tagValueOffset ));
325
+ handler .setInt16s (tagId , reader .getInt16 (tagValueOffset ));
330
326
} else {
331
327
short [] array = new short [componentCount ];
332
328
for (int i = 0 ; i < componentCount ; i ++)
0 commit comments