Image-Only Pages
Pdf::isImageOnlyDocument() and its companions answer one question — does a page carry a real
text layer, or is it nothing but a single scanned or drawn image — without running any OCR.
Extract\Content\PageClassifier reads each page's content stream operator by operator, the same
walk Extracting Text runs, and disqualifies a page the moment it finds:
- a text-showing operator
- a path-painting operator (a fill, a stroke, or both)
- a second image
- an inline image drawn directly in the stream rather than referenced as an XObject
Real scan-to-PDF output places one full-page image XObject and draws nothing else, whatever a
scanner or producer's own metadata claims about it. An unresolved image reference — a missing
resource entry, a broken reference — is treated the same way: not provably image-only, so the
answer is false rather than a guess.
Asking About a Whole Document#
isImageOnlyDocument($file, $pages = null, $pageLimit = null) returns one boolean for the whole
document — true only if every page checked is a lone image with nothing else on it:
use Pop\Pdf\Pdf;
Pdf::isImageOnlyDocument(__DIR__ . '/scan.pdf'); // true — two pages, each one full-page image
Pdf::isImageOnlyDocument(__DIR__ . '/report.pdf'); // false — real text on every page
A document where only some pages are scans comes back false too — the whole-document check has
no partial answer:
use Pop\Pdf\Pdf;
// page 1 and page 3 are scanned images, page 2 is real text
Pdf::isImageOnlyDocument(__DIR__ . '/mixed.pdf'); // false
Asking Page by Page tells the two image pages in that document apart from the text page between them. A page with OCR text already burned onto it, even invisibly, is not image-only either — the text-showing operator that write puts down disqualifies it, the same as any other real content sharing space with the image.
Asking Page by Page#
getImageOnlyPages($file, $pages = null, $pageLimit = null) classifies each page individually and
returns an array of booleans, one per page:
use Pop\Pdf\Pdf;
$pages = Pdf::getImageOnlyPages(__DIR__ . '/mixed.pdf');
On the three-page mixed document above, getImageOnlyPages() returns [0 => true, 1 => false, 2 => true]:
- page 1 is a scan, page 2 is text, page 3 is a scan
- the array is keyed from zero, not the page number — index
0is page 1's result, index1is page 2's, and so on - that's the opposite of
extractAsImages(), covered on Pages as Images, whose array is keyed by the source document's own 1-based page numbers
Selecting the same two pages from both methods on the same document makes the difference concrete:
use Pop\Pdf\Pdf;
$classified = Pdf::getImageOnlyPages(__DIR__ . '/mixed.pdf', pages: [1, 3]);
$rasterized = Pdf::extractAsImages(__DIR__ . '/mixed.pdf', __DIR__ . '/pages', format: 'png', resolution: 150, pages: [1, 3]);
array_keys($classified); // [0, 1] — position in the result, zero-based
array_keys($rasterized); // [1, 3] — the source page number, one-based
Both calls select the same two pages and both return two entries — the keys mean different things.
Reading $classified for "is page 3 a scan" means checking whichever position page 3 landed at,
not looking up key 3; reading $rasterized for page 3's file means looking up key 3 directly.
The Raw-Data Forms#
isImageOnlyData($data, ...) and getImageOnlyPagesFromData($data, ...) are the same two checks
against a string of PDF bytes instead of a path, for a document that arrived over HTTP or was
never written to disk:
use Pop\Pdf\Pdf;
$data = file_get_contents(__DIR__ . '/scan.pdf');
Pdf::isImageOnlyData($data); // true
Pdf::getImageOnlyPagesFromData($data); // [0 => true, 1 => true]
Both agree with their file-path counterparts on the same bytes — identical boolean, matching
per-page arrays entry for entry. Reach for the data forms exactly when you'd reach for
extractTextFromData() over extractTextFromFile(): the bytes are already in memory.
Page Selection and Limits#
$pages and $pageLimit work identically here to every other extract-style method on this site —
an explicit $pages list, an array of 1-based numbers or a single number on its own, wins
outright, and $pageLimit only applies when $pages is null:
use Pop\Pdf\Pdf;
$firstTwo = Pdf::getImageOnlyPages(__DIR__ . '/mixed.pdf', pageLimit: 2);
// [0 => true, 1 => false] — pages 1 and 2 only, page 3 never classified
$oneAndThree = Pdf::getImageOnlyPages(__DIR__ . '/mixed.pdf', pages: [1, 3]);
// [0 => true, 1 => true] — the pair from Asking Page by Page, above
Selecting Pages and Limiting How Much Is
Walked cover the full rule against
extractTextFromFile() — the two methods share the same selection code, so everything said there
about page numbering, out-of-range pages and the explicit-list-wins rule applies here unchanged.
Routing a Scanned Document to OCR#
isImageOnlyDocument() is the gate in front of Pages as Images's
extractAsImages(): a document with no text layer has nothing for Extracting
Text to pull out, and the only way to get its content is to rasterize each page
and hand it to OCR.
use Pop\Pdf\Pdf;
$file = __DIR__ . '/scan.pdf';
if (Pdf::isImageOnlyDocument($file)) {
$images = Pdf::extractAsImages($file, __DIR__ . '/pages', format: 'png', resolution: 300);
// hand each $images[$pageNumber] to an OCR engine
}
A mixed document routes each page on its own terms instead of an all-or-nothing check, using
getImageOnlyPages() to decide per page and extractTextFromFile() for whichever pages already
have text:
use Pop\Pdf\Pdf;
$file = __DIR__ . '/mixed.pdf';
foreach (Pdf::getImageOnlyPages($file) as $i => $isScan) {
$pageNumber = $i + 1; // the array is zero-keyed; the document's pages are not
if ($isScan) {
$images = Pdf::extractAsImages($file, __DIR__ . '/pages', format: 'png', resolution: 300, pages: [$pageNumber]);
// hand $images[$pageNumber] to an OCR engine
} else {
$text = Pdf::extractTextFromFile($file, pages: [$pageNumber]);
}
}
That $i + 1 is the zero-to-one conversion Asking Page by Page covers —
carry it forward the moment a page index needs to reach a page-number-keyed call like
extractAsImages().
See Also#
- Opening Encrypted PDFs — the
$passwordargument these methods share - Extracting Text — pulling the text layer out of the pages that have one
- Pages as Images — rasterizing pages that classify as image-only
- Importing — reading an existing PDF back in before classifying or extracting from it