From e8616d9ee799770c0c0a171b3034d0b6774a8256 Mon Sep 17 00:00:00 2001 From: PrinsFrank <25006490+PrinsFrank@users.noreply.github.com> Date: Sat, 3 Oct 2026 20:00:52 +0200 Subject: [PATCH 1/5] Properly cache toUnicodeCMap on descendant fonts --- .../PositionedText/PositionedTextElement.php | 3 +-- src/Document/Object/Decorator/Font.php | 11 +++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Document/ContentStream/PositionedText/PositionedTextElement.php b/src/Document/ContentStream/PositionedText/PositionedTextElement.php index 628b3759..fd683ef3 100644 --- a/src/Document/ContentStream/PositionedText/PositionedTextElement.php +++ b/src/Document/ContentStream/PositionedText/PositionedTextElement.php @@ -21,8 +21,7 @@ public function __construct( public function getText(Font $font): string { $differences = $font->getDifferences(); $encoding = $font->getEncoding(); - $toUnicodeCMap = $font->getToUnicodeCMap() - ?? $font->getToUnicodeCMapDescendantFont(); + $toUnicodeCMap = $font->getToUnicodeCMap(); $text = ''; $previousOffset = null; diff --git a/src/Document/Object/Decorator/Font.php b/src/Document/Object/Decorator/Font.php index 7d730a9d..aa083e4b 100644 --- a/src/Document/Object/Decorator/Font.php +++ b/src/Document/Object/Decorator/Font.php @@ -86,15 +86,18 @@ public function getToUnicodeCMap(): ?ToUnicodeCMap { ->getValueForKey($this->document, DictionaryKey::TO_UNICODE, ToUnicodeCMapNameValue::class) ?? throw new ParseFailureException(); - return $this->toUnicodeCMap = $toUnicodeCMapNameValue - ->getToUnicodeCMap(); + return $this->toUnicodeCMap = $toUnicodeCMapNameValue->getToUnicodeCMap(); } $toUnicodeObject = $this->getDictionary() ->getObjectForReference($this->document, DictionaryKey::TO_UNICODE); if ($toUnicodeObject === null) { - $this->toUnicodeCMap = false; + $descendantUnicodeCMap = $this->getToUnicodeCMapDescendantFont(); + if ($descendantUnicodeCMap !== null) { + return $this->toUnicodeCMap = $descendantUnicodeCMap; + } + $this->toUnicodeCMap = false; return null; } @@ -106,7 +109,7 @@ public function getToUnicodeCMap(): ?ToUnicodeCMap { return $this->toUnicodeCMap = ToUnicodeCMapParser::parse($stream, 0, $stream->getSizeInBytes()); } - public function getToUnicodeCMapDescendantFont(): ?ToUnicodeCMap { + private function getToUnicodeCMapDescendantFont(): ?ToUnicodeCMap { foreach ($this->getDescendantFonts() as $descendantFont) { $fontDictionary = $descendantFont instanceof Dictionary ? $descendantFont : $descendantFont->getDictionary(); From c3bf0fd23402bb93e0f0c480a2768c928d520d9c Mon Sep 17 00:00:00 2001 From: PrinsFrank <25006490+PrinsFrank@users.noreply.github.com> Date: Sat, 3 Oct 2026 20:13:13 +0200 Subject: [PATCH 2/5] Cache FonthWidths for character codes --- src/Document/Font/FontWidths.php | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Document/Font/FontWidths.php b/src/Document/Font/FontWidths.php index 2314d886..4797a6cf 100644 --- a/src/Document/Font/FontWidths.php +++ b/src/Document/Font/FontWidths.php @@ -2,19 +2,25 @@ namespace PrinsFrank\PdfParser\Document\Font; -readonly class FontWidths { +class FontWidths { + /** @var array */ + private array $widthCache = []; /** @param list $widths */ public function __construct( - public int $firstChar, - public array $widths, + public readonly int $firstChar, + public readonly array $widths, ) {} public function getWidthForCharacter(int $characterCode): ?float { + if (isset($this->widthCache[$characterCode])) { + return $this->widthCache[$characterCode]; + } + $width = $this->widths[$characterCode - $this->firstChar] ?? null; if ($width === null) { - return null; + return $this->widthCache[$characterCode] = null; } - return $width / 1000; + return $this->widthCache[$characterCode] = $width / 1000; } } From b891cbbc4382185a8b39ab156f0a9a180e54960c Mon Sep 17 00:00:00 2001 From: PrinsFrank <25006490+PrinsFrank@users.noreply.github.com> Date: Sat, 3 Oct 2026 20:17:53 +0200 Subject: [PATCH 3/5] Cache differences for fonts --- src/Document/Font/FontWidths.php | 1 + src/Document/Object/Decorator/Font.php | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Document/Font/FontWidths.php b/src/Document/Font/FontWidths.php index 4797a6cf..301e02bd 100644 --- a/src/Document/Font/FontWidths.php +++ b/src/Document/Font/FontWidths.php @@ -5,6 +5,7 @@ class FontWidths { /** @var array */ private array $widthCache = []; + /** @param list $widths */ public function __construct( public readonly int $firstChar, diff --git a/src/Document/Object/Decorator/Font.php b/src/Document/Object/Decorator/Font.php index aa083e4b..c10cac8d 100644 --- a/src/Document/Object/Decorator/Font.php +++ b/src/Document/Object/Decorator/Font.php @@ -34,6 +34,7 @@ class Font extends DecoratedObject { private readonly array $descendantFontsCache; private readonly ToUnicodeCMap|false $toUnicodeCMap; private readonly CIDFontWidths|FontWidths|false $widths; + private readonly DifferencesArrayValue|false $differencesCache; /** @throws PdfParserException */ public function getBaseFont(): ?string { @@ -67,8 +68,18 @@ public function getEncoding(): ?EncodingNameValue { } public function getDifferences(): ?DifferencesArrayValue { - return $this->getEncodingDictionary() + if (isset($this->differencesCache)) { + return $this->differencesCache === false ? null : $this->differencesCache; + } + + $differences = $this->getEncodingDictionary() ?->getValueForKey($this->document, DictionaryKey::DIFFERENCES, DifferencesArrayValue::class); + if ($differences === null) { + $this->differencesCache = false; + return null; + } + + return $this->differencesCache = $differences; } /** @throws PdfParserException */ From 203b34ea5f5974ca8a35f601e10728b78695de52 Mon Sep 17 00:00:00 2001 From: PrinsFrank <25006490+PrinsFrank@users.noreply.github.com> Date: Sat, 3 Oct 2026 20:22:48 +0200 Subject: [PATCH 4/5] Cache encoding for fonts --- src/Document/Object/Decorator/Font.php | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/Document/Object/Decorator/Font.php b/src/Document/Object/Decorator/Font.php index c10cac8d..e4fc27db 100644 --- a/src/Document/Object/Decorator/Font.php +++ b/src/Document/Object/Decorator/Font.php @@ -35,6 +35,7 @@ class Font extends DecoratedObject { private readonly ToUnicodeCMap|false $toUnicodeCMap; private readonly CIDFontWidths|FontWidths|false $widths; private readonly DifferencesArrayValue|false $differencesCache; + private readonly EncodingNameValue|false $encodingCache; /** @throws PdfParserException */ public function getBaseFont(): ?string { @@ -54,17 +55,23 @@ public function getEncodingDictionary(): ?Dictionary { /** @throws PdfParserException */ public function getEncoding(): ?EncodingNameValue { - $encodingType = $this->getDictionary()->getTypeForKey(DictionaryKey::ENCODING); - if ($encodingType === null) { - return null; + if (isset($this->encodingCache)) { + return $this->encodingCache === false ? null : $this->encodingCache; + } + + if ($this->getDictionary()->getTypeForKey(DictionaryKey::ENCODING) === EncodingNameValue::class) { + $encoding = $this->getDictionary()->getValueForKey($this->document, DictionaryKey::ENCODING, EncodingNameValue::class); + } else { + $encoding = $this->getEncodingDictionary() + ?->getValueForKey($this->document, DictionaryKey::BASE_ENCODING, EncodingNameValue::class); } - if ($encodingType === EncodingNameValue::class) { - return $this->getDictionary()->getValueForKey($this->document, DictionaryKey::ENCODING, EncodingNameValue::class); + if ($encoding === null) { + $this->encodingCache = false; + return null; } - return $this->getEncodingDictionary() - ?->getValueForKey($this->document, DictionaryKey::BASE_ENCODING, EncodingNameValue::class); + return $this->encodingCache = $encoding; } public function getDifferences(): ?DifferencesArrayValue { From a21c0e592836c3100cb96c1f1bbc4e91c1ff47a1 Mon Sep 17 00:00:00 2001 From: PrinsFrank <25006490+PrinsFrank@users.noreply.github.com> Date: Sat, 3 Oct 2026 20:23:37 +0200 Subject: [PATCH 5/5] Clean up toUnicodeCMap cache --- src/Document/Object/Decorator/Font.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Document/Object/Decorator/Font.php b/src/Document/Object/Decorator/Font.php index e4fc27db..90971976 100644 --- a/src/Document/Object/Decorator/Font.php +++ b/src/Document/Object/Decorator/Font.php @@ -92,11 +92,9 @@ public function getDifferences(): ?DifferencesArrayValue { /** @throws PdfParserException */ public function getToUnicodeCMap(): ?ToUnicodeCMap { if (isset($this->toUnicodeCMap)) { - if ($this->toUnicodeCMap === false) { - return null; - } - - return $this->toUnicodeCMap; + return $this->toUnicodeCMap === false + ? null + : $this->toUnicodeCMap; } if ($this->getDictionary()->getTypeForKey(DictionaryKey::TO_UNICODE) === ToUnicodeCMapNameValue::class) {