docling-project/docling · error · RuntimeError
Page image dimensions {im.size} do not match page geometry (
Error message
Page image dimensions {im.size} do not match page geometry ({page_size.width}x{page_size.height}). What it means
RuntimeError from MetsGbsDocumentBackend.get_page_image(): the backend loads a page image from the METS/GBS tar archive and compares its pixel size against the page geometry declared in the METS OCR XML (page div attributes). If the image file's width/height do not equal the declared page size, the archive is internally inconsistent and coordinate transforms would be wrong, so it refuses to continue.
Source
Thrown at docling/backend/mets_gbs_backend.py:156
images = dpage.bitmap_resources
for img in images:
cropbox = img.rect.to_bounding_box().to_top_left_origin(
self.get_size().height
)
if cropbox.area() > AREA_THRESHOLD:
cropbox = cropbox.scaled(scale=scale)
yield cropbox
def get_page_image(
self, scale: float = 1, cropbox: BoundingBox | None = None
) -> Image.Image:
im = self._require_image()
page_size = self.get_size()
if page_size.width != im.size[0] or page_size.height != im.size[1]:
raise RuntimeError(
f"Page image dimensions {im.size} do not match page geometry "
f"({page_size.width}x{page_size.height})."
)
if not cropbox:
cropbox = BoundingBox(
l=0,
r=page_size.width,
t=0,
b=page_size.height,
coord_origin=CoordOrigin.TOPLEFT,
)
image = im.resize(
size=(round(page_size.width * scale), round(page_size.height * scale))
).crop(cropbox.scaled(scale=scale).as_tuple())
return image
View on GitHub (pinned to 61d76f1ff3)
Solutions
- Verify the archive is a well-formed METS/GBS export where image dimensions match the OCR page geometry (extract and compare with PIL before conversion).
- Re-generate or obtain a consistent archive from the source digitization system.
- If you must repair: rewrite the ocr_page div attributes to the true image size, or resample images to the declared size, then reconvert.
- Report upstream if a stock Google Books export triggers it — stock exports should always be consistent.
Example fix
# before
result = converter.convert(mets_tar_path) # RuntimeError: dims mismatch
# after (pre-validate the archive)
import tarfile, io
from PIL import Image
with tarfile.open(mets_tar_path) as t:
for m in t.getmembers():
if m.name.endswith(('.png', '.jpg')):
im = Image.open(t.extractfile(m))
assert im.size == declared_size.get(m.name), m.name # repair before converting
result = converter.convert(mets_tar_path) Defensive patterns
Strategy: validation
Validate before calling
import tarfile, io, re
from PIL import Image
def mets_image_dims_match(tar_path: str) -> bool:
with tarfile.open(tar_path) as t:
ocr = next((m for m in t.getmembers() if m.name.endswith('.html') or 'coordOCR' in m.name), None)
img = next((m for m in t.getmembers() if m.name.endswith(('.png', '.jpg', '.jpeg'))), None)
if not (ocr and img):
return True # cannot pre-check; let backend decide
m = re.search(rb"bbox\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)", t.extractfile(ocr).read(4096))
if not m:
return True
w, h = int(m.group(3)) - int(m.group(1)), int(m.group(4)) - int(m.group(2))
return Image.open(t.extractfile(img)).size == (w, h) Try / catch
try:
result = converter.convert(mets_path)
except RuntimeError as e:
if 'do not match page geometry' in str(e):
log.error('inconsistent METS archive %s — image/OCR mismatch', mets_path)
quarantine(mets_path) Prevention
- Never regenerate page images without updating OCR bbox metadata.
- Validate image-vs-OCR dimensions when assembling METS packages.
- Keep archives from digitization systems unmodified rather than repacking by hand.
When it happens
Trigger: Converting a METS/GBS (.tar.gz with Google Books scan structure) archive where the ocr_page div's width/height attributes disagree with the actual embedded PNG/JPEG dimensions — e.g. mismatched image/OCR pairs, rescaled images, or a corrupted/hand-edited archive.
Common situations: Digitization pipelines that regenerate page images at a different resolution without updating OCR metadata, archives assembled from mixed sources, or truncated image members that PIL still opens with a partial header.
Related errors
- Image file {image_info.path} exceeds individual file size li
- Archive exceeds maximum member count limit of {self.options.
- XML file {member.name} exceeds size limit of {self.options.m
- Archive exceeds maximum total extraction size of {self.optio
- METS GBS backend could not load document {self.document_hash
AI-assisted analysis of docling-project/docling@61d76f1ff3 (2026-08-14).
Data as JSON: /api/errors/5a73bd1bdf7f866a.
Report an issue: GitHub.