Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions src/Document/Object/Decorator/Page.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
namespace PrinsFrank\PdfParser\Document\Object\Decorator;

use PrinsFrank\MarkDownDom\Renderer\MarkdownRenderer;
use PrinsFrank\MarkDownDom\Renderer\TextRenderer;
use PrinsFrank\PdfParser\Document\ContentStream\ContentStream;
use PrinsFrank\PdfParser\Document\ContentStream\ContentStreamParser;
use PrinsFrank\PdfParser\Document\ContentStream\PositionedText\PositionedTextElement;
Expand All @@ -16,7 +15,8 @@
use PrinsFrank\PdfParser\Exception\InvalidArgumentException;
use PrinsFrank\PdfParser\Exception\ParseFailureException;
use PrinsFrank\PdfParser\Exception\PdfParserException;
use PrinsFrank\PdfParser\Extraction\Markdown\MarkdownExtractor;
use PrinsFrank\PdfParser\Extraction\MarkdownExtractor;
use PrinsFrank\PdfParser\Extraction\TextExtractor;

class Page extends DecoratedObject {
/**
Expand All @@ -30,8 +30,7 @@ public function getPositionedTextElements(): array {

/** @throws PdfParserException */
public function getText(): string {
return (new TextRenderer())
->render(MarkdownExtractor::extractContent($this->getPositionedTextElements(), $this));
return TextExtractor::extractContent($this->getPositionedTextElements(), $this);
}

public function getMarkdown(): string {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php declare(strict_types=1);

namespace PrinsFrank\PdfParser\Extraction\Markdown;
namespace PrinsFrank\PdfParser\Extraction;

use PrinsFrank\MarkDownDom\Contract\BlockNode;
use PrinsFrank\MarkDownDom\Contract\InlineNode;
Expand All @@ -14,7 +14,8 @@
use PrinsFrank\PdfParser\Document\ContentStream\PositionedText\PositionedTextElement;
use PrinsFrank\PdfParser\Document\Object\Decorator\Page;
use PrinsFrank\PdfParser\Exception\PdfParserException;
use PrinsFrank\PdfParser\Extraction\Markdown\TextGrouping\LineGrouping\TextOverlapStrategy;
use PrinsFrank\PdfParser\Extraction\SpaceDetection\SpaceDetector;
use PrinsFrank\PdfParser\Extraction\TextGrouping\LineGrouping\TextOverlapStrategy;

class MarkdownExtractor {
/**
Expand Down Expand Up @@ -63,23 +64,12 @@ public static function extractContent(array $positionedTextElements, Page $page)
self::flushInLineNodes($inLineNodes, $textBuffer, $previousElementIsBold, $previousElementIsItalic);
}

if ($previousTextElementOnLine !== null && $previousFontOnLine !== null) {
$gap = $positionedTextElement->absoluteMatrix->offsetX
- $previousTextElementOnLine->absoluteMatrix->offsetX
- $previousTextElementOnLine->getAdvanceWidth($previousFontOnLine);

$wordBreakThreshold = $previousTextElementOnLine->textState->getFontSize()
* $previousTextElementOnLine->absoluteMatrix->scaleX
* ($previousTextElementOnLine->textState->scale / 100)
* PositionedTextElement::WORD_BREAK_THRESHOLD_EM;

if (
$gap >= $wordBreakThreshold
&& $previousTextElementEndsWithSpace === false
&& str_starts_with($elementText, ' ') === false
) {
$textBuffer .= ' ';
}
if ($previousTextElementEndsWithSpace === false
&& str_starts_with($elementText, ' ') === false
&& $previousTextElementOnLine !== null
&& $previousFontOnLine !== null
&& SpaceDetector::shouldContainExtraSpace($positionedTextElement, $previousTextElementOnLine, $previousFontOnLine)) {
$textBuffer .= ' ';
}

$previousTextElementOnLine = $positionedTextElement;
Expand Down
25 changes: 25 additions & 0 deletions src/Extraction/SpaceDetection/SpaceDetector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php declare(strict_types=1);

namespace PrinsFrank\PdfParser\Extraction\SpaceDetection;

use PrinsFrank\PdfParser\Document\ContentStream\PositionedText\PositionedTextElement;
use PrinsFrank\PdfParser\Document\Object\Decorator\Font;

class SpaceDetector {
public static function shouldContainExtraSpace(
PositionedTextElement $positionedTextElement,
PositionedTextElement $previousTextElementOnLine,
Font $previousFontOnLine,
): bool {
$gap = $positionedTextElement->absoluteMatrix->offsetX
- $previousTextElementOnLine->absoluteMatrix->offsetX
- $previousTextElementOnLine->getAdvanceWidth($previousFontOnLine);

$wordBreakThreshold = $previousTextElementOnLine->textState->getFontSize()
* $previousTextElementOnLine->absoluteMatrix->scaleX
* ($previousTextElementOnLine->textState->scale / 100)
* PositionedTextElement::WORD_BREAK_THRESHOLD_EM;

return $gap >= $wordBreakThreshold;
}
}
54 changes: 54 additions & 0 deletions src/Extraction/TextExtractor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php declare(strict_types=1);

namespace PrinsFrank\PdfParser\Extraction;

use PrinsFrank\PdfParser\Document\ContentStream\PositionedText\PositionedTextElement;
use PrinsFrank\PdfParser\Document\Object\Decorator\Page;
use PrinsFrank\PdfParser\Exception\PdfParserException;
use PrinsFrank\PdfParser\Extraction\SpaceDetection\SpaceDetector;
use PrinsFrank\PdfParser\Extraction\TextGrouping\LineGrouping\TextOverlapStrategy;

class TextExtractor {
/**
* @param list<PositionedTextElement> $positionedTextElements
* @throws PdfParserException
*/
public static function extractContent(array $positionedTextElements, Page $page): string {
$lineGroupedElements = TextOverlapStrategy::group($positionedTextElements);

$textBuffer = '';
foreach ($lineGroupedElements as $i => $positionedTextElementsForLine) {
if ($i !== 0) {
$textBuffer .= "\n";
}

$previousTextElementOnLine = null;
$previousFontOnLine = null;
$previousTextElementEndsWithSpace = false;
foreach ($positionedTextElementsForLine as $positionedTextElement) {
$elementText = $positionedTextElement->getText($page);
$font = $positionedTextElement->getFont($page);
if ($elementText === '') {
$previousTextElementOnLine = $positionedTextElement;
$previousFontOnLine = $font;
continue;
}

if ($previousTextElementEndsWithSpace === false
&& str_starts_with($elementText, ' ') === false
&& $previousTextElementOnLine !== null
&& $previousFontOnLine !== null
&& SpaceDetector::shouldContainExtraSpace($positionedTextElement, $previousTextElementOnLine, $previousFontOnLine)) {
$textBuffer .= ' ';
}

$previousTextElementOnLine = $positionedTextElement;
$previousFontOnLine = $font;
$previousTextElementEndsWithSpace = str_ends_with($elementText, ' ');
$textBuffer .= $elementText;
}
}

return $textBuffer;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php declare(strict_types=1);

namespace PrinsFrank\PdfParser\Extraction\Markdown\TextGrouping\LineGrouping;
namespace PrinsFrank\PdfParser\Extraction\TextGrouping\LineGrouping;

use PrinsFrank\PdfParser\Document\ContentStream\PositionedText\PositionedTextElement;

Expand Down
3 changes: 1 addition & 2 deletions tests/Samples/files/issue-215/contents.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ modificationDate: 2025-09-01T16:24:09+02:00
pages:
-
text: |-
Deutscher Bundestag
Drucksache 21/1407
Deutscher Bundestag Drucksache 21/1407
21. Wahlperiode 28.08.2025
Antwort
der Bundesregierung
Expand Down
6 changes: 2 additions & 4 deletions tests/Samples/files/issue-235/contents.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@ pages:
-
text: |-

Niedersächsisches

Gesetz- und Verordnungsblatt

Niedersächsisches
Gesetz- und Verordnungsblatt
79. Jahrgang Hannover, den 10. April 2025 Nummer 25

Verordnung
Expand Down
3 changes: 1 addition & 2 deletions tests/Samples/files/issue-290/contents.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ modificationDate: 2025-09-12T14:25:52+02:00
pages:
-
text: |-
Deutscher Bundestag
Drucksache 21/1576
Deutscher Bundestag Drucksache 21/1576
21. Wahlperiode 10.09.2025
Antwort
der Bundesregierung
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ creationDate: 2025-12-02T18:58:35+01:00
modificationDate: null
pages:
-
text: |
text: |-
Etiam vitae lectus sodales
Vestibulum et augue vitae orci sagittis faucibus
Donec risus risus
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,8 +298,7 @@ pages:
RS-422
Control Panel output
will contend with 7707DT
Incorrect Method
output. Will not work.
Incorrect Method output. Will not work.
7707DT
Set to
RS-422
Expand Down
3 changes: 1 addition & 2 deletions tests/Samples/files/type1-differences-non-ascii/contents.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ creationDate: null
modificationDate: null
pages:
-
text: |
Für Größe Übung
text: 'Für Größe Übung'
markdown: |+
### Für Größe Übung
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use PrinsFrank\PdfParser\Document\ContentStream\PositionedText\TextState;
use PrinsFrank\PdfParser\Document\ContentStream\PositionedText\TransformationMatrix;
use PrinsFrank\PdfParser\Document\Dictionary\DictionaryValue\TextString\LiteralStringValue;
use PrinsFrank\PdfParser\Extraction\Markdown\TextGrouping\LineGrouping\TextOverlapStrategy;
use PrinsFrank\PdfParser\Extraction\TextGrouping\LineGrouping\TextOverlapStrategy;

#[CoversClass(TextOverlapStrategy::class)]
class TextOverlapStrategyTest extends TestCase {
Expand Down
8 changes: 4 additions & 4 deletions tests/Unit/PdfParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class PdfParserTest extends TestCase {
public function testParseStringInMemory(): void {
static::assertNotFalse($fileContent = file_get_contents(dirname(__DIR__) . '/Feature/samples/h3-simple-string.pdf'));
static::assertSame(
' Hello World ' . PHP_EOL,
' Hello World ',
(new PdfParser())
->parseString(
$fileContent,
Expand All @@ -23,7 +23,7 @@ public function testParseStringInMemory(): void {
public function testParseStringWithFileCache(): void {
static::assertNotFalse($fileContent = file_get_contents(dirname(__DIR__) . '/Feature/samples/h3-simple-string.pdf'));
static::assertSame(
' Hello World ' . PHP_EOL,
' Hello World ',
(new PdfParser())
->parseString(
$fileContent,
Expand All @@ -34,7 +34,7 @@ public function testParseStringWithFileCache(): void {

public function testParseFileInMemory(): void {
static::assertSame(
' Hello World ' . PHP_EOL,
' Hello World ',
(new PdfParser())
->parseFile(
dirname(__DIR__) . '/Feature/samples/h3-simple-string.pdf',
Expand All @@ -45,7 +45,7 @@ public function testParseFileInMemory(): void {

public function testParseFileWithoutMemoryStream(): void {
static::assertSame(
' Hello World ' . PHP_EOL,
' Hello World ',
(new PdfParser())
->parseFile(
dirname(__DIR__) . '/Feature/samples/h3-simple-string.pdf',
Expand Down
Loading