Pop PDF
Extract

Extracting Text

Pdf::extractTextFromFile() and ::extractTextFromData() pull the text layer back out of an existing PDF. Extraction is native to Pop PDF — no external binary, no third-party PDF library — and decodes text against embedded and CID fonts as well as the standard names; see Embedded and CID Fonts.

Extracting From a File#

extractTextFromFile($file, $pages = null, $pageLimit = null, $password = null) takes a path and returns one string:

PHP
use Pop\Pdf\Pdf;

$text = Pdf::extractTextFromFile(__DIR__ . '/report.pdf');

Given a three-page report.pdf, one line of text on each page, that returns every page's text joined into a single value:

PHP
"Page One text.\n\nPage Two text.\n\nPage Three text."

echoing it prints each page on its own line with a blank line between:

TEXT
Page One text.

Page Two text.

Page Three text.

How Pages Are Separated covers what produces that blank line, and what a page with no text contributes.

$file has to point at something on disk — a path that does not exist raises Pop\Pdf\Extract\Exception:

PHP
use Pop\Pdf\Pdf;
use Pop\Pdf\Extract\Exception;

try {
    Pdf::extractTextFromFile(__DIR__ . '/missing.pdf');
} catch (Exception $exception) {
    echo $exception->getMessage();
}

That prints Error: That PDF file does not exist. A path that exists but isn't a valid PDF raises the same exception with a different message — extractTextFromFile() on a plain text file, or extractTextFromData() on a string that isn't PDF bytes at all, both fail with Error: Could not resolve the PDF document catalog (Root).

Extracting From Raw Bytes#

extractTextFromData($data, $pages = null, $pageLimit = null) is the same call against a string of PDF bytes instead of a path — for a document that was uploaded, fetched over HTTP, or generated in memory and never written to disk:

PHP
use Pop\Pdf\Pdf;

$data = file_get_contents(__DIR__ . '/report.pdf');
$text = Pdf::extractTextFromData($data);

Both methods load their input into the same Extract\Document and run the identical walk over it, so the same file returns the identical string either way — which one to call is a question of what you already have in hand, a path or a buffer.

Selecting Pages#

$pages is the third argument on both methods — an array of 1-based page numbers, or a single number on its own; 2 and [2] mean the same thing:

PHP
use Pop\Pdf\Pdf;

$second        = Pdf::extractTextFromFile(__DIR__ . '/report.pdf', pages: 2);
$firstAndThird = Pdf::extractTextFromFile(__DIR__ . '/report.pdf', pages: [1, 3]);

$second is "Page Two text."; $firstAndThird is "Page One text.\n\nPage Three text.".

  • selection follows document order, not list order — pages: [3, 1] returns page one's text first, exactly what pages: [1, 3] returns
  • a page number past the end of the document drops out of the selection rather than raising — [1, 9] on a three-page document returns page 1's text alone
  • a selection that matches nothing — every number out of range, or an empty array — returns '' rather than raising, the same string an empty page returns; counting the result against the request is the only way to tell the two apart
PHP
use Pop\Pdf\Pdf;

Pdf::extractTextFromFile(__DIR__ . '/report.pdf', pages: [9]); // '' — page 9 doesn't exist
Pdf::extractTextFromFile(__DIR__ . '/report.pdf', pages: []);  // '' — nothing was asked for

Limiting How Much Is Walked#

$pageLimit is the fourth argument, and caps extraction to the document's first n pages:

PHP
use Pop\Pdf\Pdf;

$firstTwo = Pdf::extractTextFromFile(__DIR__ . '/report.pdf', pageLimit: 2);

That returns "Page One text.\n\nPage Two text.". A limit larger than the document, 99 on a three-page file, returns every page, the same as no limit at all — and the limit is enforced during the walk, not by truncating the joined string: pages beyond it are never decompressed or interpreted, so a low limit on a long document skips real work, not only output.

An explicit $pages list wins outright over $pageLimit, which only applies when $pages is null:

PHP
use Pop\Pdf\Pdf;

$text = Pdf::extractTextFromFile(__DIR__ . '/report.pdf', pages: [3], pageLimit: 1);
// "Page Three text." — the explicit list, not the first page the limit would have picked

How Pages Are Separated#

Pages join with a blank line, "\n\n", and only between pages that actually produced text — a page with no text at all contributes nothing, not even an empty entry:

PHP
use Pop\Pdf\Pdf;

// A three-page document where the middle page carries no text at all
$text = Pdf::extractTextFromFile(__DIR__ . '/mixed-report.pdf');

That returns "First page has text.\n\nThird page has text." — no trace of the blank page between them. Selecting it on its own, pages: 2, returns '', the same as any other selection matching nothing.

Embedded and CID Fonts#

Extraction decodes text against whatever font drew it, including a font embedded in the document itself, not only the standard names: an embedded TrueType or OpenType font decodes as two-byte glyph IDs through its own character map, while a standard or embedded Type1 font stays on a single-byte path. A compressed content stream — Flate, LZW, RunLength, ASCII85 or ASCIIHex, in any combination a producer chose to apply — extracts the same as one left uncompressed.

The round trip is the way to see this working rather than take it on faith: write non-ASCII text through an embedded TrueType font and extract it straight back.

PHP
use Pop\Pdf\Document;
use Pop\Pdf\Document\Page;
use Pop\Pdf\Document\Page\Text;
use Pop\Pdf\Document\Font;
use Pop\Pdf\Pdf;

$font = new Font(__DIR__ . '/fonts/DejaVuSans.ttf');

$document = new Document(new Page(Page::LETTER));
$document->embedFont($font);

$original = 'Café Müller — €120 naïve';
$document->getPage(1)->addText(new Text($original, size: 24), $font->getName(), x: 72, y: 700);

Pdf::writeToFile($document, filename: __DIR__ . '/embedded.pdf');

$extracted = Pdf::extractTextFromFile(__DIR__ . '/embedded.pdf');

$extracted === $original; // true

The accented letter, umlaut, em dash and euro sign all survive, and the bytes match exactly, not only the visible characters:

PHP
bin2hex($extracted); // '436166c3a9204dc3bc6c6c657220e2809420e282ac313230206e61c3af7665'
bin2hex($original);  // '436166c3a9204dc3bc6c6c657220e2809420e282ac313230206e61c3af7665'

Identical hex confirms the same UTF-8 sequence went in through addText() and came back out unchanged, not merely a string that happens to display the same way. Embedding Fonts covers registering the font and checking its glyph coverage before you draw with it.

See Also#

  • Image-Only Pages — calling isImageOnlyDocument() to decide whether a document has a text layer to extract from at all
  • Pages as Images — rasterizing pages that turn out to be scans rather than text
  • Opening Encrypted PDFs — the $password argument these methods share
  • Embedding Fonts — registering a TrueType, OpenType or Type1 font and checking its glyph coverage before drawing text with it