What happens
Tables drawn sideways on a portrait page (rotated text matrix, glyph rotation_degrees = 90) extract as scrambled text: word order reversed inside runs, adjacent words glued, and the table's 2D structure destroyed. The words all survive, so nothing errors and nothing looks empty — the page is just unreadable.
From the capacities table of a car manual (page 264), xberg 1.0.8:
CAPACITIES AND part FordequivalentMotorcraftPerformanceVehicle MotorFluid...
SPECIFICATIONS Capacityon 18.6(17.6L) quarts
BetweenMAX 6.0
The same page through kreuzberg 4.x (and pypdf):
Engine coolant 18.6 quarts (17.6L) Motorcraft Premium Gold Engine Coolant ...
Engine oil 6.0 quarts (5.7L)
A footnote paragraph on page 266 comes out fully word-reversed: the meet only need oil Engine for "Engine oil need only meet the …".
Reproduce
cv-manual.pdf from the gist used in #1220 (https://gist.github.com/tobocop2/0d30905e2375e09b153ce50296316f91), pages 264–266:
import asyncio
from xberg import ExtractInput, ExtractInputKind, ExtractionConfig, PageConfig, extract
async def main():
cfg = ExtractionConfig(pages=PageConfig(extract_pages=True))
inp = ExtractInput(kind=ExtractInputKind.BYTES, bytes=open("cv-manual.pdf", "rb").read(),
mime_type="application/pdf", filename="cv-manual.pdf")
doc = (await extract(inp, cfg)).results[0]
print(doc.pages[263].content) # scrambled; "Engine oil" nowhere near "6.0 quarts"
asyncio.run(main())
Reproduces identically on 1.0.4, 1.0.8 (current PyPI), and a CLI built from main (7539de03e4). Layout config makes no difference: AUTO and ALWAYS strategies, CPU and CoreML providers all return byte-identical page text for these pages, so the reading-order machinery never rescues them.
Root cause
These pages are ~90% rotated text: pdf_oxide.extract_page_text reports 795 of 886 chars on page 264 at rotation_degrees = 90 (the remainder is the upright running footer). The glyph geometry is correct; the assembly of rotated runs is what garbles. This is exactly yfedoseev/pdf_oxide#806 ("reversed reading, glued words, destroyed table/chart structure"), still open there. The #1345 two-column fix (v1.0.5) does not cover rotated runs.
Why it matters
This is the dominant layout for wide spec tables in manuals and datasheets — capacities, fluids, torque specs — which are exactly the pages people point a RAG pipeline at. The failure is silent: retrieval indexes the shredded fragments, surfaces adjacent prose instead, and the answer looks considered rather than uninformed. kreuzberg 4.x extracted these pages cleanly, so for this document class 1.0 is a regression.
Possible xberg-side handling
Until pdf_oxide#806 lands, xberg can see the condition coming: a text-layer page whose chars are predominantly rotation_degrees = ±90. Candidates, in rough order of ambition: reassemble those runs in the rotated frame (the per-char geometry is already correct); route such pages through the existing OCR orientation-classifier path the way scanned rotated pages are handled; or at minimum flag the page so callers can quarantine it instead of indexing scrambled text.
Workaround
Use OCR extraction for these types of files.
What happens
Tables drawn sideways on a portrait page (rotated text matrix, glyph
rotation_degrees = 90) extract as scrambled text: word order reversed inside runs, adjacent words glued, and the table's 2D structure destroyed. The words all survive, so nothing errors and nothing looks empty — the page is just unreadable.From the capacities table of a car manual (page 264), xberg 1.0.8:
The same page through kreuzberg 4.x (and pypdf):
A footnote paragraph on page 266 comes out fully word-reversed:
the meet only need oil Enginefor "Engine oil need only meet the …".Reproduce
cv-manual.pdffrom the gist used in #1220 (https://gist.github.com/tobocop2/0d30905e2375e09b153ce50296316f91), pages 264–266:Reproduces identically on 1.0.4, 1.0.8 (current PyPI), and a CLI built from main (
7539de03e4). Layout config makes no difference: AUTO and ALWAYS strategies, CPU and CoreML providers all return byte-identical page text for these pages, so the reading-order machinery never rescues them.Root cause
These pages are ~90% rotated text:
pdf_oxide.extract_page_textreports 795 of 886 chars on page 264 atrotation_degrees = 90(the remainder is the upright running footer). The glyph geometry is correct; the assembly of rotated runs is what garbles. This is exactly yfedoseev/pdf_oxide#806 ("reversed reading, glued words, destroyed table/chart structure"), still open there. The #1345 two-column fix (v1.0.5) does not cover rotated runs.Why it matters
This is the dominant layout for wide spec tables in manuals and datasheets — capacities, fluids, torque specs — which are exactly the pages people point a RAG pipeline at. The failure is silent: retrieval indexes the shredded fragments, surfaces adjacent prose instead, and the answer looks considered rather than uninformed. kreuzberg 4.x extracted these pages cleanly, so for this document class 1.0 is a regression.
Possible xberg-side handling
Until pdf_oxide#806 lands, xberg can see the condition coming: a text-layer page whose chars are predominantly
rotation_degrees = ±90. Candidates, in rough order of ambition: reassemble those runs in the rotated frame (the per-char geometry is already correct); route such pages through the existing OCR orientation-classifier path the way scanned rotated pages are handled; or at minimum flag the page so callers can quarantine it instead of indexing scrambled text.Workaround
Use OCR extraction for these types of files.