@@ -52,6 +52,7 @@ This file is part of the iText (R) project.
52
52
import com .itextpdf .io .source .RandomAccessSourceFactory ;
53
53
import com .itextpdf .io .source .WindowRandomAccessSource ;
54
54
import com .itextpdf .commons .utils .MessageFormatUtil ;
55
+ import com .itextpdf .kernel .exceptions .InvalidXRefPrevException ;
55
56
import com .itextpdf .kernel .exceptions .MemoryLimitsAwareException ;
56
57
import com .itextpdf .kernel .exceptions .PdfException ;
57
58
import com .itextpdf .kernel .crypto .securityhandler .UnsupportedSecurityHandlerException ;
@@ -730,16 +731,20 @@ protected void readPdf() throws IOException {
730
731
}
731
732
try {
732
733
readXref ();
733
- } catch (XrefCycledReferencesException | MemoryLimitsAwareException ex ) {
734
+ } catch (XrefCycledReferencesException | MemoryLimitsAwareException | InvalidXRefPrevException ex ) {
734
735
// Throws an exception when xref stream has cycled references(due to lack of opportunity to fix such an
735
736
// issue) or xref tables have cycled references and PdfReader.StrictnessLevel set to CONSERVATIVE.
736
737
// Also throw an exception when xref structure size exceeds jvm memory limit.
737
738
throw ex ;
738
739
} catch (RuntimeException ex ) {
739
- Logger logger = LoggerFactory .getLogger (PdfReader .class );
740
- logger .error (IoLogMessageConstant .XREF_ERROR_WHILE_READING_TABLE_WILL_BE_REBUILT , ex );
740
+ if (StrictnessLevel .CONSERVATIVE .isStricter (this .getStrictnessLevel ())) {
741
+ Logger logger = LoggerFactory .getLogger (PdfReader .class );
742
+ logger .error (IoLogMessageConstant .XREF_ERROR_WHILE_READING_TABLE_WILL_BE_REBUILT , ex );
741
743
742
- rebuildXref ();
744
+ rebuildXref ();
745
+ } else {
746
+ throw ex ;
747
+ }
743
748
}
744
749
pdfDocument .getXref ().markReadingCompleted ();
745
750
readDecryptObj ();
@@ -989,7 +994,9 @@ protected void readXref() throws IOException {
989
994
xrefStm = true ;
990
995
return ;
991
996
}
992
- } catch (XrefCycledReferencesException | MemoryLimitsAwareException exceptionWhileReadingXrefStream ) {
997
+ } catch (XrefCycledReferencesException
998
+ | MemoryLimitsAwareException
999
+ | InvalidXRefPrevException exceptionWhileReadingXrefStream ) {
993
1000
throw exceptionWhileReadingXrefStream ;
994
1001
} catch (Exception ignored ) {
995
1002
// Do nothing.
@@ -1008,7 +1015,7 @@ protected void readXref() throws IOException {
1008
1015
final Set <Long > alreadyVisitedXrefTables = new HashSet <>();
1009
1016
while (true ) {
1010
1017
alreadyVisitedXrefTables .add (startxref );
1011
- PdfNumber prev = ( PdfNumber ) trailer2 .get (PdfName .Prev );
1018
+ PdfNumber prev = getXrefPrev ( trailer2 .get (PdfName .Prev , false ) );
1012
1019
if (prev == null ) {
1013
1020
break ;
1014
1021
}
@@ -1175,7 +1182,7 @@ protected boolean readXrefStream(long ptr) throws IOException {
1175
1182
}
1176
1183
PdfArray w = xrefStream .getAsArray (PdfName .W );
1177
1184
long prev = -1 ;
1178
- obj = xrefStream .get (PdfName .Prev );
1185
+ obj = getXrefPrev ( xrefStream .get (PdfName .Prev , false ) );
1179
1186
if (obj != null )
1180
1187
prev = ((PdfNumber ) obj ).longValue ();
1181
1188
xref .setCapacity (size );
@@ -1321,6 +1328,26 @@ protected void rebuildXref() throws IOException {
1321
1328
throw new PdfException (KernelExceptionMessageConstant .TRAILER_NOT_FOUND );
1322
1329
}
1323
1330
1331
+ protected PdfNumber getXrefPrev (PdfObject prevObjectToCheck ) {
1332
+ if (prevObjectToCheck == null ) {
1333
+ return null ;
1334
+ }
1335
+
1336
+ if (prevObjectToCheck .getType () == PdfObject .NUMBER ) {
1337
+ return (PdfNumber ) prevObjectToCheck ;
1338
+ } else {
1339
+ if (prevObjectToCheck .getType () == PdfObject .INDIRECT_REFERENCE &&
1340
+ StrictnessLevel .CONSERVATIVE .isStricter (this .getStrictnessLevel ())) {
1341
+ final PdfObject value = ((PdfIndirectReference ) prevObjectToCheck ).getRefersTo (true );
1342
+ if (value != null && value .getType () == PdfObject .NUMBER ) {
1343
+ return (PdfNumber ) value ;
1344
+ }
1345
+ }
1346
+ throw new InvalidXRefPrevException (
1347
+ KernelExceptionMessageConstant .XREF_PREV_SHALL_BE_DIRECT_NUMBER_OBJECT );
1348
+ }
1349
+ }
1350
+
1324
1351
boolean isMemorySavingMode () {
1325
1352
return memorySavingMode ;
1326
1353
}
@@ -1359,33 +1386,6 @@ private void readDecryptObj() {
1359
1386
}
1360
1387
}
1361
1388
1362
- /**
1363
- * Utility method that checks the provided byte source to see if it has junk bytes at the beginning. If junk bytes
1364
- * are found, construct a tokeniser that ignores the junk. Otherwise, construct a tokeniser for the byte source as it is
1365
- *
1366
- * @param byteSource the source to check
1367
- * @return a tokeniser that is guaranteed to start at the PDF header
1368
- * @throws IOException if there is a problem reading the byte source
1369
- */
1370
- private static PdfTokenizer getOffsetTokeniser (IRandomAccessSource byteSource , boolean closeStream )
1371
- throws IOException {
1372
- PdfTokenizer tok = new PdfTokenizer (new RandomAccessFileOrArray (byteSource ));
1373
- int offset ;
1374
- try {
1375
- offset = tok .getHeaderOffset ();
1376
- } catch (com .itextpdf .io .exceptions .IOException ex ) {
1377
- if (closeStream ) {
1378
- tok .close ();
1379
- }
1380
- throw ex ;
1381
- }
1382
- if (offset != 0 ) {
1383
- IRandomAccessSource offsetSource = new WindowRandomAccessSource (byteSource , offset );
1384
- tok = new PdfTokenizer (new RandomAccessFileOrArray (offsetSource ));
1385
- }
1386
- return tok ;
1387
- }
1388
-
1389
1389
private PdfObject readObject (PdfIndirectReference reference , boolean fixXref ) {
1390
1390
if (reference == null )
1391
1391
return null ;
@@ -1497,6 +1497,33 @@ private PdfObject createPdfNullInstance(boolean readAsDirect) {
1497
1497
}
1498
1498
}
1499
1499
1500
+ /**
1501
+ * Utility method that checks the provided byte source to see if it has junk bytes at the beginning. If junk bytes
1502
+ * are found, construct a tokeniser that ignores the junk. Otherwise, construct a tokeniser for the byte source as it is
1503
+ *
1504
+ * @param byteSource the source to check
1505
+ * @return a tokeniser that is guaranteed to start at the PDF header
1506
+ * @throws IOException if there is a problem reading the byte source
1507
+ */
1508
+ private static PdfTokenizer getOffsetTokeniser (IRandomAccessSource byteSource , boolean closeStream )
1509
+ throws IOException {
1510
+ PdfTokenizer tok = new PdfTokenizer (new RandomAccessFileOrArray (byteSource ));
1511
+ int offset ;
1512
+ try {
1513
+ offset = tok .getHeaderOffset ();
1514
+ } catch (com .itextpdf .io .exceptions .IOException ex ) {
1515
+ if (closeStream ) {
1516
+ tok .close ();
1517
+ }
1518
+ throw ex ;
1519
+ }
1520
+ if (offset != 0 ) {
1521
+ IRandomAccessSource offsetSource = new WindowRandomAccessSource (byteSource , offset );
1522
+ tok = new PdfTokenizer (new RandomAccessFileOrArray (offsetSource ));
1523
+ }
1524
+ return tok ;
1525
+ }
1526
+
1500
1527
protected static class ReusableRandomAccessSource implements IRandomAccessSource {
1501
1528
private ByteBuffer buffer ;
1502
1529
0 commit comments