A PDF page showed half an image. Every extractor I tried returned the whole thing
I assumed that extracting an image from a PDF gives you what the page shows. Then I built a one-page PDF where the page shows only the left half of a picture, ran four extractors on it, and every one of them handed back
I assumed that extracting an image from a PDF gives you what the page shows. Then I built a one-page PDF where the page shows only the left half of a picture, ran four extractors on it, and every one of them handed back the whole picture. The crop was never part of the image. It lives in the page.
The test file is one page of raw PDF bytes I wrote by hand, with four lossless images. This post uses the first: a 240×160 RGB picture with a red square top left, green top right, blue bottom left and yellow bottom right, so I can tell which part I'm looking at. Sizes are in pixels, and pt means 1/72 inch. The extractors were pdfimages 26.08.0, PyMuPDF 1.26.5, the decoded bitmap that pdf.js 4.10.38 gives me in Chromium 149 (pdf.js has no public extract call, so that is the closest I could get), and ImgIng (https://imging.ai/) in a desktop browser. One file, one sample, default settings.
The page shows 90 pt of an image that is 180 pt wide
Here is the line in the page's content stream that draws the picture.
q 60 540 90 120 re W n 180 0 0 120 60 540 cm /ImA Do Q
My reading, which took me a while: re W n sets a clip rectangle 90 pt wide and 120 pt high, cm stretches the image to 180×120 pt, and Do paints it. Anything outside the rectangle is simply not drawn. The page keeps the left half, and the image data in the file is untouched.
Four tools, one answer
All four returned the full 240×160 image. For pdfimages, PyMuPDF and the pdf.js bitmap the pixel hash matched the stored data exactly (5ce5032b49b6, the first 12 hex digits of a SHA-256 over the RGB bytes). ImgIng also returned the whole 240×160 image.
Look at column A only. The top picture is the page: red and blue squares on the left, the letter A sliced off at the right edge. The bottom picture is what pdfimages, PyMuPDF and pdf.js returned, with the green and yellow squares and the whole A. Columns B and C show the same thing for a rotated and a mirrored image, and D is a palette case I'm leaving for another day. The PDF is self-built, no third-party material.
ImgIng finds image objects that are really painted on the page rather than screenshotting it, which fits what I saw. Its limits: one PDF per run, 0 results for a page with no images, and ZIP entries named only by number, with the page numbers left on its on-screen cards.
Asking the page where the picture sits
If the extractor won't tell me what is visible, the page has to. I asked PyMuPDF.
import fitz
doc = fitz.open("four_images.pdf")
info = doc[0].get_image_info(xrefs=True)[0]
print(info["width"], info["height"], tuple(info["bbox"]))
print(fitz.Pixmap(doc, info["xref"]).width)
240 160 (60.0, 132.0, 240.0, 252.0)
240
The bbox is 180 pt wide. That is the whole drawn picture, so the clip is not subtracted. pdf.js shows the same split: in getOperatorList the image paint comes after a clip operation, and the transform is readable there too.
Getting the half I could see
As far as I can tell there are two ways to get "what the page shows": render the page and crop at the picture's position, or read the clip rectangle and crop the extracted image. The second works on this file. The rectangle is 90 pt wide and the image is drawn 180 pt wide for 240 pixels, which is 96 pixels per inch, so the visible part is the left 120 columns. Cropped to 120×160, the extracted image matches a 96 dpi render of the page exactly (average error 0.00 on a 0 to 255 scale). At 72, 150, 300 and 600 dpi the error is 0.98 to 2.51, from resampling.
What I haven't worked out
A hand-written PDF is a clean case. The real one I tried is the Creative Commons Indonesia brochure (CC BY 4.0, via Wikimedia Commons): PyMuPDF lists 9 image placements and 20 clip entries across its two pages. The list doesn't say which clip touches which image and I didn't match them, so I can't tell how many of its 8 images are partly hidden. I'd guess files from InDesign or Word behave like my hand-written one, but that is a guess.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.