A library news story that turns into an arson case
Both PDFs render a harmless article about a library reopening. Asked for a summary, each chatbot reports an arson-and-fraud investigation that is nowhere on the page.
Split-View PDFs in Document-to-LLM Supply Chains
Tulane University
Illustration. The PDF is a real split-view file from our classroom demo, and the four extractor outputs were measured on it. The chat assistant is simulated. Recordings of real LLM services are in §05.
A PDF viewer renders glyphs onto a page. An LLM app first extracts text from the same file, and the model reasons over that text. The PDF standard does not require the two to agree, and users are never shown the extracted version.
We mined the ISO 32000 object model for places where rendering and extraction can be pulled apart, and confirmed 25 extraction gaps in four families. Several hide nothing at all: the page is fully visible, yet extraction returns different words.
Every gap diverges on at least one of 16 PDF processing stacks. Every one of 7 commercial LLM services we tested is exposed to at least 12 of them.
A renderer follows glyph programs, colors, positions and visibility state. An extractor follows Unicode mappings, accessibility metadata, content-stream order and font fallbacks. A split-view PDF makes these two paths disagree.
Each gap has a one-page canary PDF in our artifact. Pick one to see how it renders and what each of ten PDF engines extracts from it. Colors mark whether that engine's output diverges from the rendered page.
These are end-to-end attack documents from our benchmark, run against real services with benign prompts. The attacker controls only the PDF. Every claim and answer shown in red appears nowhere on the rendered page.
Both PDFs render a harmless article about a library reopening. Asked for a summary, each chatbot reports an arson-and-fraud investigation that is nowhere on the page.
The page shows review code LARK-27. A remapped glyph extracts as LARK-27XQ9. Asked a one-line question, the official APIs answer with the extractor's version.
Visible: Review code: LARK-27
All seven official APIs we tested returned LARK-27XQ9 in 10/10 trials. The same Sonnet 4.6 reached through Amazon Bedrock returned the visible LARK-27: same model, different ingestion path.
The bulletin pairs each cause with its effect. The content stream writes the table column by column, so an extractor that follows stream order loses which effect belongs to which cause. No text is hidden.
Visible pairing: battery room stayed wet → basement stayed locked
Same PDF, same question, two services, two answers. This matches the paper's main finding: exposure is set by how each service's ingestion stack linearizes the page.
Every service exposes at least 12 of the 25 gaps. Which ones depends on how the service turns the PDF into text, and that changes across APIs, web apps, cloud routes, local runtimes and service updates, even when the model name stays the same.
Official API, Corpus B. A gap counts once if any attack form succeeds in ≥1 of 10 calls.
Exposed gaps (of 25) through the official API vs. the vendor's web chat.
† Qwen-Long is API-only; the web test uses Qwen 3.5.
GPT-5.4 via Azure loses off-page, tiny and zero-height text plus one reading-order split, but keeps semantic overrides and font splits. Sonnet 4.6 via Bedrock stays at 15.
Model capability doesn't change exposure. A service update does.
Ollama and LM Studio pass their bundled extractor's text straight into the local model. LM Studio also times out on three font gaps.
Gemini's web chat exposes 0/25 on one-page files, but a 120-page version falls back to a text route and exposes 21/25.
Every extractor breaks in its own way, so a gap is also a probe. Upload two canary PDFs to an LLM app, ask it to quote them, and the answers point to its PDF loader. Two probes separate all seven LangChain loaders we tested. A four-page canary separates all 16 parser paths in our panel.
LARK-27. The suffix XQ9 sits on a zero-height text matrix, so it never renders.ABC, drawn by a Type 3 font with no /ToUnicode. Loaders differ in how they guess the text.In black-box deployments, wrappers, OCR fallbacks and version drift can blur signatures. Read a match as evidence about the loader family, not an exact version.
| LangChain loader | EG12 | EG18 |
|---|