#1091·marker

Silent image loss on ~14% of pages in a mixed text/code/screenshot PDF — no error or log output

Author: mfh7Created Aug 28, 2026Updated Aug 28, 2026

On a 36-page PDF with embedded screenshots and code-box images, Marker failed to export the embedded image on 5 of 36 pages (page indices in a 0-indexed 36-page excerpt: 8, 16, 18, 19, 27) — roughly 14% of pages with qualifying images. Two of the five (pages 16 and 27) are large, readable code-box screenshots — genuine content loss, not decorative icons.

Confirmation this is a real defect, not a downstream/dedup artifact:

  • Verified directly against the source PDF with pypdfium2: each affected page has one embedded image page-object (FPDF_PAGEOBJ_IMAGE) at ≥300px in both dimensions (after Marker's own render scale), but the corresponding page has zero exported image files of that size in Marker's own output directory.
  • Confirmed this happens on the raw Marker output, before any local post-processing (dedup, glyph fixes, etc.) touches it.
  • No warning, error, or any stdout/stderr signal accompanies the missing export — the run reports success.
  • Retested explicitly with marker_single (single-file invocation, no --workers, no batch/folder conversion) to rule out the known batch-multiprocessing image-loss issues (#292, #617 — both require converting a folder of multiple PDFs via the marker batch command, and both lose images entirely, on every page). Result: identical per-page pattern reproduced under single-file marker_single — this rules out that root cause (CUDA multiprocessing context sharing); this defect reproduces with the simplest possible single-document, single-process invocation.

Minimal, fully-synthetic reproduction — root cause identified:

A single-page PDF with real PDF text (title, two body paragraphs, a "Figure 16:" caption) plus one embedded raster image between the paragraphs — the image is a synthetic "code editor screenshot" (dark background, colored monospace-style text, 400×240pt) — reproduces the defect exactly: the surrounding text and caption extract correctly, but the image is dropped entirely (no exported file, no reference in the markdown).

Generator script (self-contained, no proprietary content):

python
"""Minimal, fully-synthetic repro for: Marker silently drops an embedded
image when its rendered pixel content visually resembles a code block
(dark background, colored monospace-style text) — the layout model
classifies the region as a "Code" text block instead of "Figure"/"Picture",
and the underlying image data is never exported.

Usage:
    python generate_code_screenshot_repro.py
    marker_single code_screenshot_repro.pdf --output_dir out

Expected (buggy) result: `out/code_screenshot_repro/` contains the
markdown with the surrounding text and the "Figure 16: ..." caption, but
NO image file, and no image reference in the markdown. Confirmed against
marker-pdf==1.10.2. `..._meta.json`'s `block_counts` for the page shows a
"Code" block and no "Figure"/"Picture" block — this is the layout
misclassification, not a plain extraction failure.

Requires: pypdfium2, pillow.
"""
import ctypes

import pypdfium2 as pdfium
import pypdfium2.raw as pdfium_r
from PIL import Image, ImageDraw

OUT_PATH = "code_screenshot_repro.pdf"


def _add_text(pdf, page, text, x, y, size=12):
    tobj = pdfium_r.FPDFPageObj_NewTextObj(pdf.raw, "Helvetica".encode("utf-8"), ctypes.c_float(size))
    buf = (text + "\x00").encode("utf-16-le")
    pdfium_r.FPDFText_SetText(tobj, ctypes.cast(buf, ctypes.POINTER(ctypes.c_ushort)))
    pdfium_r.FPDFPageObj_Transform(tobj, 1, 0, 0, 1, x, y)
    pdfium_r.FPDFPage_InsertObject(page.raw, tobj)


def main():
    pdf = pdfium.PdfDocument.new()
    page = pdf.new_page(612, 792)  # US Letter, points

    _add_text(pdf, page, "SAP ABAP Training - Chapter 5: Data Handling", 60, 740, 16)
    _add_text(pdf, page, "This section demonstrates how to declare local variables", 60, 700)
    _add_text(pdf, page, "and use them inside a LOOP...ENDLOOP construct to build a", 60, 685)
    _add_text(pdf, page, "result string from an internal table's contents.", 60, 670)
    _add_text(pdf, page, "Figure 16: Declaring and using gv_result", 60, 240)
    _add_text(pdf, page, "The following screenshot shows the ABAP editor with the", 60, 210)
    _add_text(pdf, page, "completed code example, ready to be activated and tested.", 60, 195)

    # A synthetic "code editor screenshot": dark background, green
    # monospace-ish text — visually a code block, but embedded as a raster
    # image (as a real screenshot would be), not as real PDF text.
    img = Image.new("RGB", (1000, 600), color=(30, 30, 30))
    draw = ImageDraw.Draw(img)
    code_lines = [
        "DATA gv_result TYPE string.",
        "LOOP AT lt_table INTO ls_line.",
        "  gv_result = gv_result && ls_line-value.",
        "ENDLOOP.",
        "WRITE: / gv_result.",
    ]
    y = 30
    for line in code_lines:
        draw.text((30, y), line, fill=(0, 255, 0))
        y += 40

    image = pdfium.PdfImage.new(pdf)
    image.set_bitmap(pdfium.PdfBitmap.from_pil(img))
    w, h = 400, 240
    x, y = 100, 260  # between the two text blocks, like a real figure
    image.set_matrix(pdfium.PdfMatrix(w, 0, 0, h, x, y))
    page.insert_obj(image)

    page.gen_content()
    pdf.save(OUT_PATH)
    print(f"wrote {OUT_PATH}")


if __name__ == "__main__":
    main()

Root cause, from the run's own *_meta.json: the page's block_counts shows Code: 1, Caption: 1, SectionHeader: 1, Text: 2no Figure or Picture block at all. The layout model classifies the image region as a Code text block (likely because its pixel content — dark background, colored monospace-looking text — visually resembles a code listing) rather than a Figure/Picture image block. Once a region is classified as a text block type, its image data is apparently never exported — the loss isn't in image extraction itself, it's upstream in layout classification: the region needs OCR/text treatment as far as that stage is concerned, not image treatment. This directly explains why the two known real-document losses (pages 16, 27) are specifically described as "code-box screenshots" — a screenshot of code is exactly the pixel pattern this misclassification targets.

Impact: silent content loss is worse than a loud failure — a pipeline consuming Marker's output has no signal to detect or retry.

Ask: given the root cause above, two independent fixes would help: (a) when a Code (or other text-type) block's source region was actually an image page-object rather than real extractable text, still export the underlying image — don't drop it just because layout classified the region as text; (b) regardless of (a), log a warning whenever an image-type page object present in the source PDF has no corresponding export, so downstream consumers get a signal instead of silent loss.

Environment: marker-pdf==1.10.2.