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/Font/FontWidths.php b/src/Document/Font/FontWidths.php index 2314d886..301e02bd 100644 --- a/src/Document/Font/FontWidths.php +++ b/src/Document/Font/FontWidths.php @@ -2,19 +2,26 @@ 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; } } diff --git a/src/Document/Object/Decorator/Font.php b/src/Document/Object/Decorator/Font.php index 7d730a9d..90971976 100644 --- a/src/Document/Object/Decorator/Font.php +++ b/src/Document/Object/Decorator/Font.php @@ -34,6 +34,8 @@ class Font extends DecoratedObject { private readonly array $descendantFontsCache; 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 { @@ -53,32 +55,46 @@ 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 { - 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 */ 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) { @@ -86,15 +102,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 +125,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();