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

144 lines
6.2 KiB
PHP

<?php
declare(strict_types=1);
namespace PrinsFrank\PdfParser\Document\Dictionary;
use PrinsFrank\PdfParser\Document\Dictionary\DictionaryEntry\DictionaryEntry;
use PrinsFrank\PdfParser\Document\Dictionary\DictionaryKey\DictionaryKey;
use PrinsFrank\PdfParser\Document\Dictionary\DictionaryKey\ExtendedDictionaryKey;
use PrinsFrank\PdfParser\Document\Dictionary\DictionaryValue\Array\DictionaryArrayValue;
use PrinsFrank\PdfParser\Document\Dictionary\DictionaryValue\DictionaryValue;
use PrinsFrank\PdfParser\Document\Dictionary\DictionaryValue\Name\NameValue;
use PrinsFrank\PdfParser\Document\Dictionary\DictionaryValue\Name\SubtypeNameValue;
use PrinsFrank\PdfParser\Document\Dictionary\DictionaryValue\Name\TypeNameValue;
use PrinsFrank\PdfParser\Document\Dictionary\DictionaryValue\Reference\ReferenceValue;
use PrinsFrank\PdfParser\Document\Dictionary\DictionaryValue\Reference\ReferenceValueArray;
use PrinsFrank\PdfParser\Document\Document;
use PrinsFrank\PdfParser\Document\Object\Decorator\DecoratedObject;
use PrinsFrank\PdfParser\Exception\InvalidArgumentException;
use PrinsFrank\PdfParser\Exception\ParseFailureException;
use PrinsFrank\PdfParser\Exception\RuntimeException;
class Dictionary {
/** @var array<DictionaryEntry> */
public readonly array $dictionaryEntries;
/** @no-named-arguments */
public function __construct(
DictionaryEntry... $dictionaryEntries
) {
$this->dictionaryEntries = $dictionaryEntries;
}
/**
* @template T of DictionaryValue|NameValue|Dictionary
* @param class-string<T> $valueType
* @return T
*/
public function getValueForKey(DictionaryKey|ExtendedDictionaryKey $dictionaryKey, string $valueType): DictionaryValue|Dictionary|NameValue|null {
foreach ($this->dictionaryEntries as $dictionaryEntry) {
if (($dictionaryKey instanceof DictionaryKey && $dictionaryEntry->key === $dictionaryKey)
|| ($dictionaryKey instanceof ExtendedDictionaryKey && $dictionaryEntry->key instanceof ExtendedDictionaryKey && $dictionaryEntry->key->value === $dictionaryKey->value)) {
$value = $dictionaryEntry->value;
if (is_a($value, $valueType) === false) {
throw new InvalidArgumentException(sprintf('Expected value with value %s to be of type %s, got %s', $dictionaryKey->value, $valueType, get_class($value)));
}
return $value;
}
}
return null;
}
/** @return class-string<DictionaryValue|NameValue|Dictionary> */
public function getTypeForKey(DictionaryKey $dictionaryKey): ?string {
foreach ($this->dictionaryEntries as $dictionaryEntry) {
if ($dictionaryEntry->key === $dictionaryKey) {
return $dictionaryEntry->value::class;
}
}
return null;
}
public function getSubDictionary(?Document $document, DictionaryKey $dictionaryKey): ?Dictionary {
$subDictionaryType = $this->getTypeForKey($dictionaryKey);
if ($subDictionaryType === null) {
return null;
}
if ($subDictionaryType === Dictionary::class) {
return $this->getValueForKey($dictionaryKey, Dictionary::class) ?? throw new RuntimeException();
}
if ($subDictionaryType === DictionaryArrayValue::class) {
return ($this->getValueForKey($dictionaryKey, DictionaryArrayValue::class) ?? throw new RuntimeException())->toSingleDictionary();
}
if ($subDictionaryType === ReferenceValue::class) {
if ($document === null) {
throw new ParseFailureException('Document is required to get subDictionary for reference');
}
return ($this->getObjectForReference($document, $dictionaryKey) ?? throw new ParseFailureException())
->getDictionary();
}
throw new ParseFailureException(sprintf('Invalid type "%s" for subDictionary with key %s', $subDictionaryType, $dictionaryKey->name));
}
/**
* @template T of DecoratedObject
* @param class-string<T>|null $expectedDecoratorFQN
* @return ($expectedDecoratorFQN is null ? DecoratedObject : T)
*/
public function getObjectForReference(Document $document, DictionaryKey|ExtendedDictionaryKey $dictionaryKey, ?string $expectedDecoratorFQN = null): ?DecoratedObject {
$reference = $this->getValueForKey($dictionaryKey, ReferenceValue::class);
if ($reference === null) {
return null;
}
return $document->getObject($reference->objectNumber, $expectedDecoratorFQN)
?? throw new ParseFailureException();
}
/**
* @template T of DecoratedObject
* @param class-string<T>|null $expectedDecoratorFQN
* @return ($expectedDecoratorFQN is null ? list<DecoratedObject> : list<T>)
*/
public function getObjectsForReference(Document $document, DictionaryKey|ExtendedDictionaryKey $dictionaryKey, ?string $expectedDecoratorFQN = null): array {
$references = $this->getValueForKey($dictionaryKey, ReferenceValueArray::class);
if ($references === null) {
return [];
}
$objects = [];
foreach ($references->referenceValues as $referenceValue) {
$objects[] = $document->getObject($referenceValue->objectNumber, $expectedDecoratorFQN)
?? throw new ParseFailureException();
}
return $objects;
}
public function getType(): ?TypeNameValue {
if ($this->getTypeForKey(DictionaryKey::TYPE) === Dictionary::class) {
return $this->getValueForKey(DictionaryKey::TYPE, Dictionary::class)
?->getValueForKey(DictionaryKey::TYPE, TypeNameValue::class);
}
return $this->getValueForKey(DictionaryKey::TYPE, TypeNameValue::class);
}
public function getSubType(): ?SubtypeNameValue {
if ($this->getTypeForKey(DictionaryKey::SUBTYPE) === Dictionary::class) {
return $this->getValueForKey(DictionaryKey::SUBTYPE, Dictionary::class)
?->getValueForKey(DictionaryKey::SUBTYPE, SubtypeNameValue::class);
}
return $this->getValueForKey(DictionaryKey::SUBTYPE, SubtypeNameValue::class);
}
}