From f916db452294db4a5fddb6d9807812fba187fc46 Mon Sep 17 00:00:00 2001 From: PrinsFrank <25006490+PrinsFrank@users.noreply.github.com> Date: Sat, 3 Oct 2026 19:41:14 +0200 Subject: [PATCH 1/3] Pass font to getText method as it's reused later --- .../ContentStream/PositionedText/PositionedTextElement.php | 3 +-- src/Extraction/MarkdownExtractor.php | 2 +- src/Extraction/TextExtractor.php | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Document/ContentStream/PositionedText/PositionedTextElement.php b/src/Document/ContentStream/PositionedText/PositionedTextElement.php index 4fcee351..5a796242 100644 --- a/src/Document/ContentStream/PositionedText/PositionedTextElement.php +++ b/src/Document/ContentStream/PositionedText/PositionedTextElement.php @@ -28,8 +28,7 @@ public function getFont(Page $page): Font { } /** @throws ParseFailureException */ - public function getText(Page $page): string { - $font = $this->getFont($page); + public function getText(Font $font): string { $differences = $font->getDifferences(); $encoding = $font->getEncoding(); $toUnicodeCMap = $font->getToUnicodeCMap() ?? $font->getToUnicodeCMapDescendantFont(); diff --git a/src/Extraction/MarkdownExtractor.php b/src/Extraction/MarkdownExtractor.php index 69d271ef..1b7b30ac 100644 --- a/src/Extraction/MarkdownExtractor.php +++ b/src/Extraction/MarkdownExtractor.php @@ -48,8 +48,8 @@ public static function extractContent(array $positionedTextElements, Page $page) $previousFontOnLine = null; $previousTextElementEndsWithSpace = false; foreach ($positionedTextElementsForLine as $positionedTextElement) { - $elementText = $positionedTextElement->getText($page); $font = $positionedTextElement->getFont($page); + $elementText = $positionedTextElement->getText($font); if ($elementText === '') { $previousTextElementOnLine = $positionedTextElement; $previousFontOnLine = $font; diff --git a/src/Extraction/TextExtractor.php b/src/Extraction/TextExtractor.php index 1ddba7ec..679b980b 100644 --- a/src/Extraction/TextExtractor.php +++ b/src/Extraction/TextExtractor.php @@ -26,8 +26,8 @@ public static function extractContent(array $positionedTextElements, Page $page) $previousFontOnLine = null; $previousTextElementEndsWithSpace = false; foreach ($positionedTextElementsForLine as $positionedTextElement) { - $elementText = $positionedTextElement->getText($page); $font = $positionedTextElement->getFont($page); + $elementText = $positionedTextElement->getText($font); if ($elementText === '') { $previousTextElementOnLine = $positionedTextElement; $previousFontOnLine = $font; From 23c04895d2090d483aec26570df814acb11bc8cd Mon Sep 17 00:00:00 2001 From: PrinsFrank <25006490+PrinsFrank@users.noreply.github.com> Date: Sat, 3 Oct 2026 19:46:03 +0200 Subject: [PATCH 2/3] Move font retrieval out of PositionedTextElement --- .../PositionedText/PositionedTextElement.php | 13 ++----------- src/Document/Object/Decorator/Page.php | 6 +++++- src/Extraction/MarkdownExtractor.php | 4 +++- src/Extraction/TextExtractor.php | 4 +++- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/Document/ContentStream/PositionedText/PositionedTextElement.php b/src/Document/ContentStream/PositionedText/PositionedTextElement.php index 5a796242..628b3759 100644 --- a/src/Document/ContentStream/PositionedText/PositionedTextElement.php +++ b/src/Document/ContentStream/PositionedText/PositionedTextElement.php @@ -4,7 +4,6 @@ use PrinsFrank\PdfParser\Document\ContentStream\PositionedText\TextSegment\TextSegment; use PrinsFrank\PdfParser\Document\Object\Decorator\Font; -use PrinsFrank\PdfParser\Document\Object\Decorator\Page; use PrinsFrank\PdfParser\Exception\ParseFailureException; readonly class PositionedTextElement { @@ -18,20 +17,12 @@ public function __construct( public TextState $textState, ) {} - public function getFont(Page $page): Font { - if ($this->textState->fontName === null) { - throw new ParseFailureException('Unable to locate font for text element'); - } - - return $page->getFont($this->textState->fontName) - ?? throw new ParseFailureException(sprintf('Unable to locate font with reference "/%s"', $this->textState->fontName->value)); - } - /** @throws ParseFailureException */ public function getText(Font $font): string { $differences = $font->getDifferences(); $encoding = $font->getEncoding(); - $toUnicodeCMap = $font->getToUnicodeCMap() ?? $font->getToUnicodeCMapDescendantFont(); + $toUnicodeCMap = $font->getToUnicodeCMap() + ?? $font->getToUnicodeCMapDescendantFont(); $text = ''; $previousOffset = null; diff --git a/src/Document/Object/Decorator/Page.php b/src/Document/Object/Decorator/Page.php index 89efba17..0d02299a 100644 --- a/src/Document/Object/Decorator/Page.php +++ b/src/Document/Object/Decorator/Page.php @@ -104,7 +104,11 @@ public function getFontDictionary(): ?Dictionary { ?->getSubDictionary($this->document, DictionaryKey::FONT); } - public function getFont(DictionaryKey|ExtendedDictionaryKey $dictionaryKey): ?Font { + public function getFont(DictionaryKey|ExtendedDictionaryKey|null $dictionaryKey): ?Font { + if ($dictionaryKey === null) { + return null; + } + $font = $this->getFontDictionary() ?->getObjectForReference($this->document, $dictionaryKey, Font::class); if ($font !== null) { diff --git a/src/Extraction/MarkdownExtractor.php b/src/Extraction/MarkdownExtractor.php index 1b7b30ac..5439b65a 100644 --- a/src/Extraction/MarkdownExtractor.php +++ b/src/Extraction/MarkdownExtractor.php @@ -13,6 +13,7 @@ use PrinsFrank\MarkDownDom\Node\Inline\Text; use PrinsFrank\PdfParser\Document\ContentStream\PositionedText\PositionedTextElement; use PrinsFrank\PdfParser\Document\Object\Decorator\Page; +use PrinsFrank\PdfParser\Exception\ParseFailureException; use PrinsFrank\PdfParser\Exception\PdfParserException; use PrinsFrank\PdfParser\Extraction\SpaceDetection\SpaceDetector; use PrinsFrank\PdfParser\Extraction\TextGrouping\LineGrouping\TextOverlapStrategy; @@ -48,7 +49,8 @@ public static function extractContent(array $positionedTextElements, Page $page) $previousFontOnLine = null; $previousTextElementEndsWithSpace = false; foreach ($positionedTextElementsForLine as $positionedTextElement) { - $font = $positionedTextElement->getFont($page); + $font = $page->getFont($positionedTextElement->textState->fontName) + ?? throw new ParseFailureException(sprintf('Unable to locate font with reference "/%s"', $positionedTextElement->textState->fontName->value ?? 'null')); $elementText = $positionedTextElement->getText($font); if ($elementText === '') { $previousTextElementOnLine = $positionedTextElement; diff --git a/src/Extraction/TextExtractor.php b/src/Extraction/TextExtractor.php index 679b980b..20855fd0 100644 --- a/src/Extraction/TextExtractor.php +++ b/src/Extraction/TextExtractor.php @@ -4,6 +4,7 @@ use PrinsFrank\PdfParser\Document\ContentStream\PositionedText\PositionedTextElement; use PrinsFrank\PdfParser\Document\Object\Decorator\Page; +use PrinsFrank\PdfParser\Exception\ParseFailureException; use PrinsFrank\PdfParser\Exception\PdfParserException; use PrinsFrank\PdfParser\Extraction\SpaceDetection\SpaceDetector; use PrinsFrank\PdfParser\Extraction\TextGrouping\LineGrouping\TextOverlapStrategy; @@ -26,7 +27,8 @@ public static function extractContent(array $positionedTextElements, Page $page) $previousFontOnLine = null; $previousTextElementEndsWithSpace = false; foreach ($positionedTextElementsForLine as $positionedTextElement) { - $font = $positionedTextElement->getFont($page); + $font = $page->getFont($positionedTextElement->textState->fontName) + ?? throw new ParseFailureException(sprintf('Unable to locate font with reference "/%s"', $positionedTextElement->textState->fontName->value ?? 'null')); $elementText = $positionedTextElement->getText($font); if ($elementText === '') { $previousTextElementOnLine = $positionedTextElement; From 22fab18f83ff66867d84851f19fbab8a01c0d741 Mon Sep 17 00:00:00 2001 From: PrinsFrank <25006490+PrinsFrank@users.noreply.github.com> Date: Sat, 3 Oct 2026 19:51:18 +0200 Subject: [PATCH 3/3] Implement fontCache for text extractior to reuse existing font objects instead of retrieving them again --- src/Document/Object/Decorator/Page.php | 6 +----- src/Extraction/MarkdownExtractor.php | 10 +++++++--- src/Extraction/TextExtractor.php | 9 +++++++-- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/Document/Object/Decorator/Page.php b/src/Document/Object/Decorator/Page.php index 0d02299a..89efba17 100644 --- a/src/Document/Object/Decorator/Page.php +++ b/src/Document/Object/Decorator/Page.php @@ -104,11 +104,7 @@ public function getFontDictionary(): ?Dictionary { ?->getSubDictionary($this->document, DictionaryKey::FONT); } - public function getFont(DictionaryKey|ExtendedDictionaryKey|null $dictionaryKey): ?Font { - if ($dictionaryKey === null) { - return null; - } - + public function getFont(DictionaryKey|ExtendedDictionaryKey $dictionaryKey): ?Font { $font = $this->getFontDictionary() ?->getObjectForReference($this->document, $dictionaryKey, Font::class); if ($font !== null) { diff --git a/src/Extraction/MarkdownExtractor.php b/src/Extraction/MarkdownExtractor.php index 5439b65a..955b717c 100644 --- a/src/Extraction/MarkdownExtractor.php +++ b/src/Extraction/MarkdownExtractor.php @@ -26,7 +26,7 @@ class MarkdownExtractor { public static function extractContent(array $positionedTextElements, Page $page): Document { $lineGroupedElements = TextOverlapStrategy::group($positionedTextElements); - $blockNodes = $inLineNodes = []; + $blockNodes = $inLineNodes = $fontCache = []; $textBuffer = ''; $previousElementIsBold = $previousElementIsItalic = false; $previousHeadingLevel = null; @@ -49,8 +49,12 @@ public static function extractContent(array $positionedTextElements, Page $page) $previousFontOnLine = null; $previousTextElementEndsWithSpace = false; foreach ($positionedTextElementsForLine as $positionedTextElement) { - $font = $page->getFont($positionedTextElement->textState->fontName) - ?? throw new ParseFailureException(sprintf('Unable to locate font with reference "/%s"', $positionedTextElement->textState->fontName->value ?? 'null')); + if ($positionedTextElement->textState->fontName === null) { + throw new ParseFailureException('Unable to locate font'); + } + + $font = $fontCache[$positionedTextElement->textState->fontName->value] ??= $page->getFont($positionedTextElement->textState->fontName) + ?? throw new ParseFailureException(sprintf('Unable to locate font with reference "/%s"', $positionedTextElement->textState->fontName->value)); $elementText = $positionedTextElement->getText($font); if ($elementText === '') { $previousTextElementOnLine = $positionedTextElement; diff --git a/src/Extraction/TextExtractor.php b/src/Extraction/TextExtractor.php index 20855fd0..d2b74c53 100644 --- a/src/Extraction/TextExtractor.php +++ b/src/Extraction/TextExtractor.php @@ -17,6 +17,7 @@ class TextExtractor { public static function extractContent(array $positionedTextElements, Page $page): string { $lineGroupedElements = TextOverlapStrategy::group($positionedTextElements); + $fontCache = []; $textBuffer = ''; foreach ($lineGroupedElements as $i => $positionedTextElementsForLine) { if ($i !== 0) { @@ -27,8 +28,12 @@ public static function extractContent(array $positionedTextElements, Page $page) $previousFontOnLine = null; $previousTextElementEndsWithSpace = false; foreach ($positionedTextElementsForLine as $positionedTextElement) { - $font = $page->getFont($positionedTextElement->textState->fontName) - ?? throw new ParseFailureException(sprintf('Unable to locate font with reference "/%s"', $positionedTextElement->textState->fontName->value ?? 'null')); + if ($positionedTextElement->textState->fontName === null) { + throw new ParseFailureException('Unable to locate font'); + } + + $font = $fontCache[$positionedTextElement->textState->fontName->value] ??= $page->getFont($positionedTextElement->textState->fontName) + ?? throw new ParseFailureException(sprintf('Unable to locate font with reference "/%s"', $positionedTextElement->textState->fontName->value)); $elementText = $positionedTextElement->getText($font); if ($elementText === '') { $previousTextElementOnLine = $positionedTextElement;