You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by le...@apache.org on 2026/09/26 10:45:58 UTC
svn commit: r1938531 - pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser
Author: lehmi
Date: Sat Sep 26 10:45:58 2026
New Revision: 1938531
Log:
PDFBOX-5032: don't access the underlying source of the COSParser in classes which don't subclass COSParser
Modified:
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/BaseParser.java
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/BruteForceParser.java
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/XrefParser.java
Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/BaseParser.java
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/BaseParser.java Sat Sep 26 10:27:43 2026 (r1938530)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/BaseParser.java Sat Sep 26 10:45:58 2026 (r1938531)
@@ -684,6 +684,18 @@ public abstract class BaseParser
}
/**
+ * Read a single byte of data.
+ *
+ * @return The byte of data that is being read.
+ *
+ * @throws IOException If there is an error while reading the data.
+ */
+ protected int read() throws IOException
+ {
+ return source.read();
+ }
+
+ /**
* Read a buffer of data.
*
* @param b The buffer to write the data to.
@@ -698,6 +710,18 @@ public abstract class BaseParser
}
/**
+ * This will peek at the next byte.
+ *
+ * @return The next byte on the stream, leaving it as available to read.
+ *
+ * @throws IOException If there is an error reading the next byte.
+ */
+ protected int peek() throws IOException
+ {
+ return source.peek();
+ }
+
+ /**
* The total number of bytes that are available.
*
* @return The number of bytes available.
@@ -709,4 +733,16 @@ public abstract class BaseParser
return source.length();
}
+ /**
+ * Returns offset of next byte to be returned by a read method.
+ *
+ * @return offset of next byte which will be returned with next {@link #read()} (if no more bytes are left it
+ * returns a value >= length of source)
+ *
+ * @throws IOException If there was an error while getting the current position
+ */
+ protected long getPosition() throws IOException
+ {
+ return source.getPosition();
+ }
}
Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/BruteForceParser.java
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/BruteForceParser.java Sat Sep 26 10:27:43 2026 (r1938530)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/BruteForceParser.java Sat Sep 26 10:45:58 2026 (r1938531)
@@ -35,7 +35,6 @@ import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.cos.COSObject;
import org.apache.pdfbox.cos.COSObjectKey;
import org.apache.pdfbox.cos.COSStream;
-import org.apache.pdfbox.io.RandomAccessRead;
import org.apache.pdfbox.pdfparser.XrefTrailerResolver.XRefType;
import org.apache.pdfbox.pdmodel.encryption.ProtectionPolicy;
import org.apache.pdfbox.pdmodel.encryption.SecurityHandler;
@@ -80,7 +79,6 @@ public class BruteForceParser
private final COSParser parser;
private final COSDocument document;
- private final RandomAccessRead source;
/**
* Constructor. Triggers a brute force search for all objects of the document.
@@ -94,7 +92,6 @@ public class BruteForceParser
{
document = cosDocument;
parser = cosParser;
- source = parser.source;
}
/**
@@ -133,7 +130,7 @@ public class BruteForceParser
private void bfSearchForObjects() throws IOException
{
long lastEOFMarker = bfSearchForLastEOFMarker();
- long originOffset = source.getPosition();
+ long originOffset = parser.getPosition();
long currentOffset = MINIMUM_SEARCH_OFFSET;
long lastObjectId = Long.MIN_VALUE;
int lastGenID = Integer.MIN_VALUE;
@@ -143,35 +140,35 @@ public class BruteForceParser
boolean endOfObjFound = false;
do
{
- source.seek(currentOffset);
- int nextChar = source.read();
+ parser.seek(currentOffset);
+ int nextChar = parser.read();
currentOffset++;
if (BaseParser.isWhitespace(nextChar) && parser.isString(OBJ_MARKER))
{
long tempOffset = currentOffset - 2;
- source.seek(tempOffset);
- int genID = source.peek();
+ parser.seek(tempOffset);
+ int genID = parser.peek();
// is the next char a digit?
if (BaseParser.isDigit(genID))
{
genID -= 48;
tempOffset--;
- source.seek(tempOffset);
+ parser.seek(tempOffset);
if (parser.isWhitespace())
{
while (tempOffset > MINIMUM_SEARCH_OFFSET && parser.isWhitespace())
{
- source.seek(--tempOffset);
+ parser.seek(--tempOffset);
}
boolean objectIDFound = false;
while (tempOffset > MINIMUM_SEARCH_OFFSET && parser.isDigit())
{
- source.seek(--tempOffset);
+ parser.seek(--tempOffset);
objectIDFound = true;
}
if (objectIDFound)
{
- source.read();
+ parser.read();
long objectId = parser.readObjectNumber();
if (lastObjOffset > 0)
{
@@ -194,7 +191,7 @@ public class BruteForceParser
else if (nextChar == 'e' && parser.isString(endobjString))
{
currentOffset += endobjString.length;
- source.seek(currentOffset);
+ parser.seek(currentOffset);
if (parser.isEOF())
{
endOfObjFound = true;
@@ -214,7 +211,7 @@ public class BruteForceParser
lastObjOffset);
}
// reestablish origin position
- source.seek(originOffset);
+ parser.seek(originOffset);
}
/**
@@ -304,7 +301,7 @@ public class BruteForceParser
protected void bfSearchForObjStreams(Map<COSObjectKey, Long> xrefTable) throws IOException
{
// save origin offset
- long originOffset = source.getPosition();
+ long originOffset = parser.getPosition();
Map<Long, COSObjectKey> bfSearchForObjStreamOffsets = bfSearchForObjStreamOffsets();
Map<COSObjectKey, Long> bfCOSObjectOffsets = getBFCOSObjectOffsets();
@@ -325,7 +322,7 @@ public class BruteForceParser
SecurityHandler<? extends ProtectionPolicy> securityHandler = parser.getSecurityHandler();
for (Long offset : objStreamOffsets)
{
- source.seek(offset);
+ parser.seek(offset);
long stmObjNumber = parser.readObjectNumber();
int stmGenNumber = parser.readGenerationNumber();
parser.readExpectedString(OBJ_MARKER, true);
@@ -371,7 +368,7 @@ public class BruteForceParser
}
}
// restore origin offset
- source.seek(originOffset);
+ parser.seek(originOffset);
}
/**
@@ -383,8 +380,8 @@ public class BruteForceParser
*/
private boolean bfSearchForTrailer(COSDictionary trailer) throws IOException
{
- long originOffset = source.getPosition();
- source.seek(MINIMUM_SEARCH_OFFSET);
+ long originOffset = parser.getPosition();
+ parser.seek(MINIMUM_SEARCH_OFFSET);
// search for trailer marker
long trailerOffset = findString(TRAILER_MARKER);
while (trailerOffset != -1)
@@ -441,7 +438,7 @@ public class BruteForceParser
}
trailerOffset = findString(TRAILER_MARKER);
}
- source.seek(originOffset);
+ parser.seek(originOffset);
return false;
}
@@ -520,8 +517,8 @@ public class BruteForceParser
private long bfSearchForLastEOFMarker() throws IOException
{
long lastEOFMarker = -1;
- long originOffset = source.getPosition();
- source.seek(MINIMUM_SEARCH_OFFSET);
+ long originOffset = parser.getPosition();
+ parser.seek(MINIMUM_SEARCH_OFFSET);
long tempMarker = findString(EOF_MARKER);
while (tempMarker != -1)
{
@@ -546,7 +543,7 @@ public class BruteForceParser
}
tempMarker = findString(EOF_MARKER);
}
- source.seek(originOffset);
+ parser.seek(originOffset);
// no EOF marker found
if (lastEOFMarker == -1)
{
@@ -564,7 +561,7 @@ public class BruteForceParser
private Map<Long, COSObjectKey> bfSearchForObjStreamOffsets() throws IOException
{
HashMap<Long, COSObjectKey> bfSearchObjStreamsOffsets = new HashMap<>();
- source.seek(MINIMUM_SEARCH_OFFSET);
+ parser.seek(MINIMUM_SEARCH_OFFSET);
char[] string = " obj".toCharArray();
// search for object stream marker
long positionObjStream = findString(OBJ_STREAM);
@@ -578,31 +575,31 @@ public class BruteForceParser
long currentOffset = positionObjStream - (i * 10);
if (currentOffset > 0)
{
- source.seek(currentOffset);
+ parser.seek(currentOffset);
for (int j = 0; j < 10; j++)
{
if (parser.isString(string))
{
long tempOffset = currentOffset - 1;
- source.seek(tempOffset);
+ parser.seek(tempOffset);
// is the next char a digit?
if (parser.isDigit())
{
tempOffset--;
- source.seek(tempOffset);
+ parser.seek(tempOffset);
if (parser.isSpace())
{
int length = 0;
- source.seek(--tempOffset);
+ parser.seek(--tempOffset);
while (tempOffset > MINIMUM_SEARCH_OFFSET && parser.isDigit())
{
- source.seek(--tempOffset);
+ parser.seek(--tempOffset);
length++;
}
if (length > 0)
{
- source.read();
- newOffset = source.getPosition();
+ parser.read();
+ newOffset = parser.getPosition();
long objNumber = parser.readObjectNumber();
int genNumber = parser.readGenerationNumber();
COSObjectKey streamObjectKey = new COSObjectKey(objNumber,
@@ -618,12 +615,12 @@ public class BruteForceParser
else
{
currentOffset++;
- source.read();
+ parser.read();
}
}
}
}
- source.seek(positionObjStream + OBJ_STREAM.length);
+ parser.seek(positionObjStream + OBJ_STREAM.length);
positionObjStream = findString(OBJ_STREAM);
}
return bfSearchObjStreamsOffsets;
@@ -638,18 +635,18 @@ public class BruteForceParser
{
List<Long> bfSearchXRefTablesOffsets = new ArrayList<>();
// a pdf may contain more than one xref entry
- source.seek(MINIMUM_SEARCH_OFFSET);
+ parser.seek(MINIMUM_SEARCH_OFFSET);
// search for xref tables
long newOffset = findString(XREF_TABLE);
while (newOffset != -1)
{
- source.seek(newOffset - 1);
+ parser.seek(newOffset - 1);
// ensure that we don't read "startxref" instead of "xref"
if (parser.isWhitespace())
{
bfSearchXRefTablesOffsets.add(newOffset);
}
- source.seek(newOffset + 4);
+ parser.seek(newOffset + 4);
newOffset = findString(XREF_TABLE);
}
return bfSearchXRefTablesOffsets;
@@ -664,7 +661,7 @@ public class BruteForceParser
{
List<Long> bfSearchXRefStreamsOffsets = new ArrayList<>();
// a pdf may contain more than one /XRef entry
- source.seek(MINIMUM_SEARCH_OFFSET);
+ parser.seek(MINIMUM_SEARCH_OFFSET);
// search for XRef streams
String objString = " obj";
char[] string = objString.toCharArray();
@@ -679,31 +676,31 @@ public class BruteForceParser
long currentOffset = xrefOffset - (i * 10);
if (currentOffset > 0)
{
- source.seek(currentOffset);
+ parser.seek(currentOffset);
for (int j = 0; j < 10; j++)
{
if (parser.isString(string))
{
long tempOffset = currentOffset - 1;
- source.seek(tempOffset);
+ parser.seek(tempOffset);
// is the next char a digit?
if (parser.isDigit())
{
tempOffset--;
- source.seek(tempOffset);
+ parser.seek(tempOffset);
if (parser.isSpace())
{
int length = 0;
- source.seek(--tempOffset);
+ parser.seek(--tempOffset);
while (tempOffset > MINIMUM_SEARCH_OFFSET && parser.isDigit())
{
- source.seek(--tempOffset);
+ parser.seek(--tempOffset);
length++;
}
if (length > 0)
{
- source.read();
- newOffset = source.getPosition();
+ parser.read();
+ newOffset = parser.getPosition();
}
}
}
@@ -715,7 +712,7 @@ public class BruteForceParser
else
{
currentOffset++;
- source.read();
+ parser.read();
}
}
}
@@ -724,7 +721,7 @@ public class BruteForceParser
{
bfSearchXRefStreamsOffsets.add(newOffset);
}
- source.seek(xrefOffset + 5);
+ parser.seek(xrefOffset + 5);
xrefOffset = findString(XREF_STREAM);
}
return bfSearchXRefStreamsOffsets;
@@ -778,14 +775,14 @@ public class BruteForceParser
long position = -1L;
int stringLength = string.length;
int counter = 0;
- int readChar = source.read();
+ int readChar = parser.read();
while (readChar != -1)
{
if (readChar == string[counter])
{
if (counter == 0)
{
- position = source.getPosition() - 1;
+ position = parser.getPosition() - 1;
}
counter++;
if (counter == stringLength)
@@ -799,7 +796,7 @@ public class BruteForceParser
position = -1L;
continue;
}
- readChar = source.read();
+ readChar = parser.read();
}
return position;
}
Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/XrefParser.java
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/XrefParser.java Sat Sep 26 10:27:43 2026 (r1938530)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/XrefParser.java Sat Sep 26 10:45:58 2026 (r1938531)
@@ -32,7 +32,6 @@ import org.apache.pdfbox.cos.COSDocument
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.cos.COSObjectKey;
import org.apache.pdfbox.cos.COSStream;
-import org.apache.pdfbox.io.RandomAccessRead;
import org.apache.pdfbox.pdfparser.XrefTrailerResolver.XRefType;
/**
@@ -55,7 +54,6 @@ public class XrefParser
private final XrefTrailerResolver xrefTrailerResolver = new XrefTrailerResolver();
private final COSParser parser;
- private final RandomAccessRead source;
/**
* Default constructor.
@@ -66,7 +64,6 @@ public class XrefParser
public XrefParser(COSParser cosParser)
{
parser = cosParser;
- source = parser.source;
}
/**
@@ -91,7 +88,7 @@ public class XrefParser
*/
public COSDictionary parseXref(COSDocument document, long startXRefOffset) throws IOException
{
- source.seek(startXRefOffset);
+ parser.seek(startXRefOffset);
long startXrefOffset = Math.max(0, parseStartXref());
// check the startxref offset
long fixedOffset = checkXRefOffset(startXrefOffset);
@@ -109,20 +106,20 @@ public class XrefParser
// save expected position for loop detection
prevSet.add(prev);
// seek to xref table
- source.seek(prev);
+ parser.seek(prev);
// skip white spaces
parser.skipSpaces();
// save current position as well due to skipped spaces
- prevSet.add(source.getPosition());
+ prevSet.add(parser.getPosition());
// -- parse xref
- if (source.peek() == X)
+ if (parser.peek() == X)
{
// xref table and trailer
// use existing parser to parse xref table
if (!parseXrefTable(prev) || !parseTrailer())
{
throw new IOException("Expected trailer object at offset "
- + source.getPosition());
+ + parser.getPosition());
}
trailer = xrefTrailerResolver.getCurrentTrailer();
// check for a XRef stream, it may contain some object ids of compressed objects
@@ -140,7 +137,7 @@ public class XrefParser
}
if (streamOffset > 0)
{
- source.seek(streamOffset);
+ parser.seek(streamOffset);
parser.skipSpaces();
try
{
@@ -208,25 +205,25 @@ public class XrefParser
private boolean parseTrailer() throws IOException
{
// parse the last trailer.
- long trailerOffset = source.getPosition();
+ long trailerOffset = parser.getPosition();
// PDFBOX-1739 skip extra xref entries in RegisSTAR documents
- int nextCharacter = source.peek();
+ int nextCharacter = parser.peek();
while (nextCharacter != 't' && BaseParser.isDigit(nextCharacter))
{
- if (source.getPosition() == trailerOffset)
+ if (parser.getPosition() == trailerOffset)
{
// warn only the first time
LOG.warn("Expected trailer object at offset {}, keep trying", trailerOffset);
}
parser.readLine();
- nextCharacter = source.peek();
+ nextCharacter = parser.peek();
}
- if (source.peek() != 't')
+ if (parser.peek() != 't')
{
return false;
}
// read "trailer"
- long currentOffset = source.getPosition();
+ long currentOffset = parser.getPosition();
String nextLine = parser.readLine();
if (!nextLine.trim().equals("trailer"))
{
@@ -239,7 +236,7 @@ public class XrefParser
// we can't just unread a portion of the read data as we don't know if the EOL consist of 1 or 2 bytes
int len = "trailer".length();
// jump back right after "trailer"
- source.seek(currentOffset + len);
+ parser.seek(currentOffset + len);
}
else
{
@@ -296,7 +293,7 @@ public class XrefParser
*/
private long checkXRefOffset(long startXRefOffset) throws IOException
{
- source.seek(startXRefOffset);
+ parser.seek(startXRefOffset);
parser.skipSpaces();
if (parser.isString(XREF_TABLE))
{
@@ -358,8 +355,8 @@ public class XrefParser
return true;
}
// seek to offset-1
- source.seek(startXRefOffset - 1);
- int nextValue = source.read();
+ parser.seek(startXRefOffset - 1);
+ int nextValue = parser.read();
// the first character has to be a whitespace, and then a digit
if (BaseParser.isWhitespace(nextValue))
{
@@ -374,7 +371,7 @@ public class XrefParser
parser.readObjectMarker();
// check the dictionary to avoid false positives
COSDictionary dict = parser.parseCOSDictionary(false);
- source.seek(startXRefOffset);
+ parser.seek(startXRefOffset);
if ("XRef".equals(dict.getNameAsString(COSName.TYPE)))
{
return true;
@@ -384,7 +381,7 @@ public class XrefParser
{
// there wasn't an object of a xref stream
LOG.debug("No Xref stream at given location {}", startXRefOffset, exception);
- source.seek(startXRefOffset);
+ parser.seek(startXRefOffset);
}
}
}
@@ -484,25 +481,25 @@ public class XrefParser
}
try
{
- source.seek(offset);
+ parser.seek(offset);
parser.skipWhiteSpaces();
- if (source.getPosition() == offset)
+ if (parser.getPosition() == offset)
{
// ensure that at least one whitespace is skipped in front of the object number
- source.seek(offset - 1);
- if (source.getPosition() < offset)
+ parser.seek(offset - 1);
+ if (parser.getPosition() < offset)
{
if (!parser.isDigit())
{
// anything else but a digit may be some garbage of the previous object -> just ignore it
- source.read();
+ parser.read();
}
else
{
- long current = source.getPosition();
- source.seek(--current);
+ long current = parser.getPosition();
+ parser.seek(--current);
while (parser.isDigit())
- source.seek(--current);
+ parser.seek(--current);
long newObjNr = parser.readObjectNumber();
int newGenNr = parser.readGenerationNumber();
COSObjectKey newObjKey = new COSObjectKey(newObjNr, newGenNr);
@@ -517,7 +514,7 @@ public class XrefParser
return null;
}
// something seems to be wrong but it's hard to determine what exactly -> simply continue
- source.seek(offset);
+ parser.seek(offset);
}
}
}
@@ -578,7 +575,7 @@ public class XrefParser
*/
private boolean parseXrefTable(long startByteOffset) throws IOException
{
- if (source.peek() != 'x')
+ if (parser.peek() != 'x')
{
return false;
}
@@ -591,7 +588,7 @@ public class XrefParser
// check for trailer after xref
String str = parser.readString();
byte[] b = str.getBytes(StandardCharsets.ISO_8859_1);
- source.seek(source.getPosition() - b.length);
+ parser.seek(parser.getPosition() - b.length);
// signal start of new XRef
xrefTrailerResolver.nextXrefObj( startByteOffset, XRefType.TABLE );
@@ -643,7 +640,7 @@ public class XrefParser
{
break;
}
- int nextChar = source.peek();
+ int nextChar = parser.peek();
if (nextChar == 't' || BaseParser.isEndOfName(nextChar))
{
break;