import { parseBboxLayout } from "./tesseract.provider"; /** * Shaped like real `pdftotext -bbox-layout` output: the gas invoice lays its * header out as two columns of independent text flows, so poppler puts a label * and the value printed beside it in *different* `` elements. Trusting * that grouping is what left `PERIODO FACTURADO` with no value next to it and * every period field empty on a batch whose text was perfectly readable. */ function word(x: number, y: number, text: string): string { return `${text}`; } function doc(...lines: string[]): string { return `${lines .map((l) => `${l}`) .join("")}`; } /** Enough words on the page to clear the "is this a real text layer" floor. */ function padding(): string { return Array.from({ length: 50 }, (_, i) => word(10, 400 + i * 10, `w${i}`)).join(""); } describe("parseBboxLayout", () => { it("rejoins a label with the value printed beside it in another flow", () => { const [page] = parseBboxLayout( doc( word(20, 100, "PERIODO") + word(45, 100, "FACTURADO:"), word(300, 100.4, "20260630-20260630"), padding(), ), 1, ); expect(page).not.toBeNull(); expect(page!.text).toContain("PERIODO FACTURADO: 20260630-20260630"); }); it("keeps genuinely separate lines apart", () => { const [page] = parseBboxLayout( doc(word(20, 100, "Cuenta:") + word(80, 100, "0900003463"), word(20, 130, "Nombre:"), padding()), 1, ); expect(page!.text.split("\n")).toContain("Cuenta: 0900003463"); expect(page!.text.split("\n")).toContain("Nombre:"); }); it("scales point coordinates into the render's pixel space", () => { // Word boxes have to land in the same coordinate space tesseract reports, // or the geometric helpers the parsers share silently stop finding values. const [page] = parseBboxLayout(doc(word(72, 144, "X") + padding()), 300 / 72); const x = page!.words.find((w) => w.text === "X")!; expect(x.left).toBeCloseTo(300); expect(x.top).toBeCloseTo(600); }); it("reports no text layer for a scan carrying a few stray glyphs", () => { expect(parseBboxLayout(doc(word(10, 10, "3") + word(40, 10, "of") + word(60, 10, "5")), 1)).toEqual([ null, ]); }); it("decodes the entities poppler escapes", () => { const [page] = parseBboxLayout(doc(word(10, 10, "A&B") + padding()), 1); expect(page!.text).toContain("A&B"); }); });