36782-vm/includes/pdfparser/Document/Object/Item/CompressedObject/CompressedObjectByteOffsetParser.php
2026-01-09 07:13:59 +00:00

70 lines
3.6 KiB
PHP

<?php declare(strict_types=1);
namespace PrinsFrank\PdfParser\Document\Object\Item\CompressedObject;
use PrinsFrank\PdfParser\Document\Dictionary\Dictionary;
use PrinsFrank\PdfParser\Document\Dictionary\DictionaryKey\DictionaryKey;
use PrinsFrank\PdfParser\Document\Dictionary\DictionaryValue\Integer\IntegerValue;
use PrinsFrank\PdfParser\Document\Generic\Character\WhitespaceCharacter;
use PrinsFrank\PdfParser\Document\Generic\Marker;
use PrinsFrank\PdfParser\Document\Generic\Parsing\InfiniteBuffer;
use PrinsFrank\PdfParser\Document\Object\Item\CompressedObject\CompressedObjectContent\CompressedObjectContentParser;
use PrinsFrank\PdfParser\Exception\ParseFailureException;
use PrinsFrank\PdfParser\Exception\PdfParserException;
use PrinsFrank\PdfParser\Exception\RuntimeException;
use PrinsFrank\PdfParser\Stream\Stream;
/** @internal */
class CompressedObjectByteOffsetParser {
/** @throws PdfParserException */
public static function parse(Stream $stream, int $startOffsetObject, int $endOffsetObject, Dictionary $dictionary): CompressedObjectByteOffsets {
$startStreamPos = $stream->getStartNextLineAfter(Marker::STREAM, $startOffsetObject, $endOffsetObject)
?? throw new ParseFailureException(sprintf('Unable to locate marker %s', Marker::STREAM->value));
if ($dictionary->getTypeForKey(DictionaryKey::LENGTH) === IntegerValue::class && ($lengthInteger = $dictionary->getValueForKey(DictionaryKey::LENGTH, IntegerValue::class)) !== null) {
$length = $lengthInteger->value;
} else {
$endStreamPos = $stream->lastPos(Marker::END_STREAM, $stream->getSizeInBytes() - $endOffsetObject)
?? throw new ParseFailureException(sprintf('Unable to locate marker %s', Marker::END_STREAM->value));
$eolPos = $stream->getEndOfCurrentLine($endStreamPos - 1, $endOffsetObject)
?? throw new ParseFailureException(sprintf('Unable to locate marker %s', WhitespaceCharacter::LINE_FEED->value));
$length = $eolPos - $startStreamPos;
}
$content = bin2hex(CompressedObjectContentParser::parseBinary($stream, $startStreamPos, $length, $dictionary)->toString());
$first = $dictionary->getValueForKey(DictionaryKey::FIRST, IntegerValue::class)
?? throw new RuntimeException('Expected a dictionary entry for "First", none found');
$buffer = new InfiniteBuffer();
$previousObjectNumber = null;
$byteOffsets = [];
foreach (str_split(substr($content, 0, $first->value * 2), 2) as $char) {
$decodedChar = mb_chr((int) hexdec($char));
if (WhitespaceCharacter::tryFrom($decodedChar) !== null) {
$numberInBuffer = $buffer->__toString();
if (trim($numberInBuffer) === '') {
$buffer->flush();
continue;
}
if ($numberInBuffer !== (string)(int) $numberInBuffer) {
throw new ParseFailureException(sprintf('Number "%s" in buffer is not a valid number', $numberInBuffer));
}
$numberInBuffer = (int) $numberInBuffer;
if ($previousObjectNumber !== null) {
$byteOffsets[$previousObjectNumber] = $numberInBuffer;
$previousObjectNumber = null;
} else {
$previousObjectNumber = $numberInBuffer;
}
$buffer->flush();
continue;
}
$buffer->addChar($decodedChar);
}
return new CompressedObjectByteOffsets($byteOffsets);
}
}