2026-01-09 07:13:59 +00:00

113 lines
5.3 KiB
PHP

<?php declare(strict_types=1);
namespace PrinsFrank\PdfParser\Document\Object\Item\UncompressedObject;
use Override;
use PrinsFrank\PdfParser\Document\Dictionary\Dictionary;
use PrinsFrank\PdfParser\Document\Dictionary\DictionaryParser;
use PrinsFrank\PdfParser\Document\Dictionary\DictionaryValue\Name\TypeNameValue;
use PrinsFrank\PdfParser\Document\Document;
use PrinsFrank\PdfParser\Document\Generic\Character\DelimiterCharacter;
use PrinsFrank\PdfParser\Document\Generic\Character\WhitespaceCharacter;
use PrinsFrank\PdfParser\Document\Generic\Marker;
use PrinsFrank\PdfParser\Document\Object\Item\CompressedObject\CompressedObject;
use PrinsFrank\PdfParser\Document\Object\Item\CompressedObject\CompressedObjectByteOffsetParser;
use PrinsFrank\PdfParser\Document\Object\Item\CompressedObject\CompressedObjectByteOffsets;
use PrinsFrank\PdfParser\Document\Object\Item\CompressedObject\CompressedObjectContent\CompressedObjectContentParser;
use PrinsFrank\PdfParser\Document\Object\Item\ObjectItem;
use PrinsFrank\PdfParser\Exception\InvalidArgumentException;
use PrinsFrank\PdfParser\Exception\ParseFailureException;
use PrinsFrank\PdfParser\Stream\FileStream;
use PrinsFrank\PdfParser\Stream\Stream;
/** @api */
class UncompressedObject implements ObjectItem {
private readonly Dictionary $dictionary;
private readonly CompressedObjectByteOffsets $byteOffsets;
public function __construct(
public readonly int $objectNumber,
public readonly int $generationNumber,
public readonly int $startOffset,
public readonly int $endOffset,
) {
}
#[Override]
public function getDictionary(Document $document): Dictionary {
if (isset($this->dictionary)) {
return $this->dictionary;
}
$startDictionaryPos = $document->stream->firstPos(DelimiterCharacter::LESS_THAN_SIGN, $this->startOffset, $this->endOffset);
if ($startDictionaryPos === null) {
return $this->dictionary = new Dictionary();
}
$endDictionaryPos = $document->stream->firstPos(Marker::STREAM, $startDictionaryPos, $this->endOffset)
?? $document->stream->lastPos(Marker::END_OBJ, $document->stream->getSizeInBytes() - $this->endOffset)
?? throw new ParseFailureException('Unable to locate start of stream or end of current object');
return $this->dictionary = DictionaryParser::parse($document->stream, $startDictionaryPos, $endDictionaryPos - $startDictionaryPos);
}
public function getCompressedObject(int $objectNumber, Document $document): CompressedObject {
$byteOffsets = $this->getByteOffsets($document);
$startByteOffset = $byteOffsets->getRelativeByteOffsetForObject($objectNumber)
?? throw new InvalidArgumentException('Compressed object does not exist in this uncompressed object');
return new CompressedObject(
$objectNumber,
$this,
$startByteOffset,
$byteOffsets->getNextRelativeByteOffset($startByteOffset),
);
}
public function getByteOffsets(Document $document): CompressedObjectByteOffsets {
if (isset($this->byteOffsets)) {
return $this->byteOffsets;
}
$dictionary = $this->getDictionary($document);
if ($dictionary->getType() !== TypeNameValue::OBJ_STM) {
throw new ParseFailureException('Unable to get stream data from item that is not a stream');
}
return $this->byteOffsets = CompressedObjectByteOffsetParser::parse(
$document->stream,
$this->startOffset,
$this->endOffset,
$dictionary
);
}
#[Override]
public function getContent(Document $document): Stream {
if (($startStreamPos = $document->stream->getStartNextLineAfter(Marker::STREAM, $this->startOffset, $this->endOffset)) !== null
&& ($endStreamPos = $document->stream->lastPos(Marker::END_STREAM, $document->stream->getSizeInBytes() - $this->endOffset)) !== null) {
return CompressedObjectContentParser::parseBinary(
$document,
$startStreamPos,
($document->stream->getEndOfCurrentLine($endStreamPos - 1, $this->endOffset)
?? throw new ParseFailureException(sprintf('Unable to locate marker %s', WhitespaceCharacter::LINE_FEED->value))) - $startStreamPos,
$this->getDictionary($document),
);
}
$nextLineAfterStartObj = $document->stream->getStartNextLineAfter(Marker::OBJ, $this->startOffset, $this->endOffset)
?? throw new ParseFailureException(sprintf('Unable to locate newline after marker %s', Marker::OBJ->value));
$endObjPos = $document->stream->lastPos(Marker::END_OBJ, $document->stream->getSizeInBytes() - $this->endOffset)
?? throw new ParseFailureException(sprintf('Unable to locate marker %s', Marker::END_OBJ->value));
$eolObjContent = $document->stream->getEndOfCurrentLine($endObjPos - 2, $this->endOffset)
?? throw new ParseFailureException(sprintf('Unable to locate newline after marker %s', Marker::END_OBJ->value));
return FileStream::fromString(
$document->stream->read(
$nextLineAfterStartObj,
$eolObjContent - $nextLineAfterStartObj,
)
);
}
}