test: harden offline font raster evidence

This commit is contained in:
Amruth Pillai
2026-09-06 05:47:56 +02:00
parent ae8e2f76f1
commit c8a10b3d3b
6 changed files with 252 additions and 52 deletions
@@ -0,0 +1,22 @@
# Plan 27A remediation round 2
Date: 2026-09-06
Base: `ae8e2f76f`
## Focused fixes
- Removed multilingual markers from the fixture headline. Each marker now exists only in its dedicated summary paragraph.
- Added pure marker-location helpers. Marker lookup joins PDF text items, supports markers split across items, rejects duplicate occurrences, and rejects non-whitespace neighbors that could contaminate a local crop.
- Raster measurement still scans with antialiasing padding but counts ink only inside the marker box, preventing neighboring glyphs from making blank or tofu-like evidence pass.
- Browser PDF download now separates download errors from post-download evidence errors. A received download with failed rasterization is reported as `unresolved-raster-evidence-error` and fails the opt-in test rather than passing as a generic download error.
- Added focused pure tests covering duplicate, split, neighboring, blank, and tofu-like cases.
- Removed trailing spaces from `plan-27a-remediation.md`.
## Verification
- `pnpm exec vitest run tests/e2e/fixtures/offline-font-markers.test.ts` — 5/5 passed.
- `pnpm exec biome check tests/e2e/specs/offline-fonts.spec.ts tests/e2e/fixtures/offline-fonts.ts tests/e2e/fixtures/offline-font-markers.ts tests/e2e/fixtures/offline-font-markers.test.ts` — passed.
- `pnpm exec playwright test tests/e2e/specs/offline-fonts.spec.ts --list` — 4 tests collected.
- `git diff --check` — passed after remediation-document whitespace cleanup.
Full diagnostic E2E remains opt-in and was not run in this focused round. Server outbound request capture and verifiable restart identity remain explicit external host-level blockers; no production resolver changes were made.
+2 -2
View File
@@ -1,7 +1,7 @@
# Plan 27A remediation
Date: 2026-09-06
Base: `61b58ae9a`
Date: 2026-09-06
Base: `61b58ae9a`
Scope: concrete findings from `.orchestration/plan-27a-independent-review.md` only.
## Remediated findings
@@ -0,0 +1,44 @@
import { describe, expect, it } from "vitest";
import { classifyRasterInk, locatePdfMarkerBoxes } from "./offline-font-markers";
const marker = { name: "cjk", marker: "简体中文" } as const;
const item = (str: string, x = 10, y = 100, width = str.length * 5) => ({
str,
x,
y,
width,
height: 12,
});
describe("locatePdfMarkerBoxes", () => {
it("rejects duplicate marker sources instead of selecting an arbitrary crop", () => {
const result = locatePdfMarkerBoxes([item(marker.marker), item(marker.marker, 10, 70)], [marker], 200);
expect(result).toEqual([{ ...marker, box: null }]);
});
it("unions text items when one marker is split across PDF text items", () => {
const result = locatePdfMarkerBoxes([item("简体"), item("中文", 20)], [marker], 200);
expect(result[0]?.box).toEqual({ x: 10, y: 88, width: 20, height: 12 });
});
it("rejects text-item neighbors that could contaminate marker-local raster evidence", () => {
const result = locatePdfMarkerBoxes([item(`prefix${marker.marker}suffix`)], [marker], 200);
expect(result).toEqual([{ ...marker, box: null }]);
});
});
describe("classifyRasterInk", () => {
it("does not treat a blank crop as visible", () => {
expect(classifyRasterInk({ inkPixels: 0, interiorInk: 0, trimmedWidth: 0, trimmedHeight: 0 })).toBe("blank");
});
it("does not treat tofu-like outline as visible", () => {
expect(classifyRasterInk({ inkPixels: 100, interiorInk: 0, trimmedWidth: 20, trimmedHeight: 20 })).toBe(
"tofu-like",
);
});
});
+114
View File
@@ -0,0 +1,114 @@
export type PdfTextItem = {
str: string;
x: number;
y: number;
width: number;
height: number;
};
export type PdfMarker = {
name: string;
marker: string;
};
export type PdfMarkerBox = {
x: number;
y: number;
width: number;
height: number;
};
export type PdfMarkerLocation<TMarker extends PdfMarker = PdfMarker> = TMarker & {
box: PdfMarkerBox | null;
};
export type RasterInkMeasurements = {
inkPixels: number;
interiorInk: number;
trimmedWidth: number;
trimmedHeight: number;
};
export type RasterGlyphStatus = "visible" | "blank" | "tofu-like";
type TextItemRange = PdfTextItem & {
start: number;
end: number;
};
const textItemRanges = (textItems: PdfTextItem[]): TextItemRange[] => {
let offset = 0;
return textItems.map((item) => {
const start = offset;
offset += item.str.length;
return { ...item, start, end: offset };
});
};
const findMarkerOccurrences = (text: string, marker: string): number[] => {
if (marker.length === 0) return [];
const occurrences: number[] = [];
let start = 0;
while (start <= text.length - marker.length) {
const match = text.indexOf(marker, start);
if (match === -1) break;
occurrences.push(match);
start = match + marker.length;
}
return occurrences;
};
export const locatePdfMarkerBoxes = <TMarker extends PdfMarker>(
textItems: PdfTextItem[],
markers: readonly TMarker[],
pageHeight: number,
): PdfMarkerLocation<TMarker>[] => {
const ranges = textItemRanges(textItems);
const text = textItems.map((item) => item.str).join("");
return markers.map((marker) => {
const [start] = findMarkerOccurrences(text, marker.marker);
const end = start === undefined ? undefined : start + marker.marker.length;
if (start === undefined || end === undefined || findMarkerOccurrences(text, marker.marker).length !== 1) {
return { ...marker, box: null };
}
const coveredRanges = ranges.filter((range) => range.start < end && range.end > start);
const firstRange = coveredRanges[0];
const lastRange = coveredRanges.at(-1);
if (!firstRange || !lastRange) return { ...marker, box: null };
const firstLocalStart = start - firstRange.start;
const lastLocalEnd = end - lastRange.start;
const leadingText = firstRange.str.slice(0, firstLocalStart);
const trailingText = lastRange.str.slice(lastLocalEnd);
if (leadingText.trim() || trailingText.trim()) return { ...marker, box: null };
const left = Math.min(...coveredRanges.map((range) => range.x));
const top = Math.min(...coveredRanges.map((range) => pageHeight - range.y - Math.max(range.height, 1)));
const right = Math.max(...coveredRanges.map((range) => range.x + Math.max(range.width, 1)));
const bottom = Math.max(...coveredRanges.map((range) => pageHeight - range.y));
return {
...marker,
box: {
x: left,
y: top,
width: Math.max(right - left, 1),
height: Math.max(bottom - top, 1),
},
};
});
};
export const classifyRasterInk = ({
inkPixels,
interiorInk,
trimmedWidth,
trimmedHeight,
}: RasterInkMeasurements): RasterGlyphStatus => {
if (inkPixels === 0) return "blank";
const interiorRatio = interiorInk / Math.max(inkPixels, 1);
return interiorRatio < 0.08 && trimmedWidth >= 8 && trimmedHeight >= 8 ? "tofu-like" : "visible";
};
+2 -3
View File
@@ -10,7 +10,6 @@ export const offlineFontScriptSamples = [
{ name: "emoji", marker: "Emoji 🚀" },
] as const;
const offlineFontFixtureText = offlineFontScriptSamples.map(({ marker }) => marker).join(" | ");
const offlineFontFixtureSummary = offlineFontScriptSamples.map(({ marker }) => `<p>${marker}</p>`).join("");
export type OfflineFontResumeFixture = {
@@ -31,8 +30,8 @@ export async function seedOfflineFontResume(page: Page): Promise<OfflineFontResu
const patches: Array<{ path: string[]; value: unknown }> = [
{ path: ["picture", "hidden"], value: true },
{ path: ["basics", "name"], value: "Offline Font Diagnostic Fixture" },
{ path: ["basics", "headline"], value: offlineFontFixtureText },
// Keep markers in separate blocks so raster evidence can map each script to a local PDF crop.
{ path: ["basics", "headline"], value: "Offline font diagnostic" },
// Keep each marker in one dedicated block so raster evidence can map each script to a local PDF crop.
{ path: ["summary", "content"], value: offlineFontFixtureSummary },
{ path: ["metadata", "typography", "body", "fontFamily"], value: "IBM Plex Serif" },
{ path: ["metadata", "typography", "body", "fontWeights"], value: ["400", "700"] },
+68 -47
View File
@@ -2,6 +2,7 @@ import type { Browser, BrowserContext, Page, TestInfo } from "@playwright/test";
import { readFile, writeFile } from "node:fs/promises";
import { createRequire } from "node:module";
import { getDocument } from "pdfjs-dist/legacy/build/pdf.mjs";
import { locatePdfMarkerBoxes } from "../fixtures/offline-font-markers";
import { offlineFontScriptSamples, seedOfflineFontResume } from "../fixtures/offline-fonts";
import { createSampleResumeFromDashboard, openSidebarSection } from "../fixtures/resume";
import { expect, test } from "../fixtures/test";
@@ -82,9 +83,11 @@ test.describe("offline font diagnostic", () => {
const pages: string[] = [];
for (let pageNumber = 1; pageNumber <= document.numPages; pageNumber += 1) {
const pdfPage = await document.getPage(pageNumber);
pages.push(...(await pdfPage.getTextContent()).items.flatMap((item) => ("str" in item ? [item.str] : [])));
pages.push(
(await pdfPage.getTextContent()).items.flatMap((item) => ("str" in item ? [item.str] : [])).join(""),
);
}
return pages.join(" ");
return pages.join("\n");
} finally {
await loadingTask.destroy();
}
@@ -114,8 +117,8 @@ test.describe("offline font diagnostic", () => {
});
});
return page.evaluate(
async ({ bytes, markers }) => {
const textEvidence = await page.evaluate(
async ({ bytes }) => {
const moduleUrl = `${location.origin}/__offline_font_pdfjs/pdf.mjs`;
const pdfjs: typeof import("pdfjs-dist/legacy/build/pdf.mjs") = await import(moduleUrl);
pdfjs.GlobalWorkerOptions.workerSrc = `${location.origin}/__offline_font_pdfjs/worker.mjs`;
@@ -124,20 +127,40 @@ test.describe("offline font diagnostic", () => {
const pdfDocument = await loadingTask.promise;
const pdfPage = await pdfDocument.getPage(1);
const textContent = await pdfPage.getTextContent();
const textItems = textContent.items.flatMap((item) =>
"str" in item
? [
{
str: item.str,
x: item.transform[4] ?? 0,
y: item.transform[5] ?? 0,
width: item.width,
height: Math.max(item.height, Math.abs(item.transform[3] ?? 0), 1),
},
]
: [],
);
const textLayer = textItems.map(({ str }) => str).join(" ");
return {
pageHeight: pdfPage.getViewport({ scale: 1 }).height,
textItems: textContent.items.flatMap((item) =>
"str" in item
? [
{
str: item.str,
x: item.transform[4] ?? 0,
y: item.transform[5] ?? 0,
width: item.width,
height: Math.max(item.height, Math.abs(item.transform[3] ?? 0), 1),
},
]
: [],
),
};
} finally {
await loadingTask.destroy();
}
},
{ bytes: Array.from(bytes) },
);
const boxes = locatePdfMarkerBoxes(textEvidence.textItems, offlineFontScriptSamples, textEvidence.pageHeight);
const textLayerMarkers = extractedMarkerResult(textEvidence.textItems.map(({ str }) => str).join(""));
const rasterEvidence = await page.evaluate(
async ({ bytes, boxes }) => {
const moduleUrl = `${location.origin}/__offline_font_pdfjs/pdf.mjs`;
const pdfjs: typeof import("pdfjs-dist/legacy/build/pdf.mjs") = await import(moduleUrl);
pdfjs.GlobalWorkerOptions.workerSrc = `${location.origin}/__offline_font_pdfjs/worker.mjs`;
const loadingTask = pdfjs.getDocument({ data: Uint8Array.from(bytes), useSystemFonts: false });
try {
const pdfDocument = await loadingTask.promise;
const pdfPage = await pdfDocument.getPage(1);
const baseViewport = pdfPage.getViewport({ scale: 1 });
const rasterScale = 4;
const viewport = pdfPage.getViewport({ scale: rasterScale });
@@ -154,29 +177,16 @@ test.describe("offline font diagnostic", () => {
background: "white",
}).promise;
const boxes = markers.map((sample) => {
const item = textItems.find(({ str }) => str.includes(sample.marker));
if (!item) return { name: sample.name, marker: sample.marker, box: null };
const height = Math.max(item.height, 1);
const baseline = baseViewport.height - item.y;
return {
name: sample.name,
marker: sample.marker,
box: {
x: item.x,
y: baseline - height,
width: Math.max(item.width, height),
height,
},
};
});
function measure(canvas: HTMLCanvasElement, scale: number): RasterGlyphEvidence[] {
const context = canvas.getContext("2d");
if (!context) throw new Error("Missing PDF preview raster context.");
const pixels = context.getImageData(0, 0, canvas.width, canvas.height);
return boxes.map(({ name, box }) => {
if (!box) return { name, status: "not-located", inkPixels: 0, trimmedWidth: 0, trimmedHeight: 0 };
const markerLeft = box.x * scale;
const markerTop = box.y * scale;
const markerRight = (box.x + box.width) * scale;
const markerBottom = (box.y + box.height) * scale;
const padding = 2 * scale;
const left = Math.max(0, Math.floor(box.x * scale - padding));
const top = Math.max(0, Math.floor(box.y * scale - padding));
@@ -189,6 +199,9 @@ test.describe("offline font diagnostic", () => {
let maxY = top;
for (let y = top; y < bottom; y += 1) {
for (let x = left; x < right; x += 1) {
// Scan with padding for antialiasing, but count ink only inside marker box.
// Neighboring glyphs must never make blank/tofu marker evidence pass.
if (x < markerLeft || x >= markerRight || y < markerTop || y >= markerBottom) continue;
const index = (y * pixels.width + x) * 4;
const red = pixels.data[index] ?? 255;
const green = pixels.data[index + 1] ?? 255;
@@ -235,9 +248,6 @@ test.describe("offline font diagnostic", () => {
const preview = document.querySelector<HTMLCanvasElement>('[aria-hidden="false"] canvas');
return {
rasterDataUrl: raster.toDataURL(),
textLayerMarkers: Object.fromEntries(
markers.map((sample) => [sample.name, textLayer.includes(sample.marker)]),
),
referenceGlyphs: measure(raster, rasterScale),
previewGlyphs: preview ? measure(preview, preview.width / baseViewport.width) : [],
};
@@ -245,8 +255,9 @@ test.describe("offline font diagnostic", () => {
await loadingTask.destroy();
}
},
{ bytes: Array.from(bytes), markers: offlineFontScriptSamples },
{ bytes: Array.from(bytes), boxes },
);
return { ...rasterEvidence, textLayerMarkers };
}
async function report(testInfo: TestInfo, name: string, reportData: Record<string, unknown>) {
@@ -335,6 +346,7 @@ test.describe("offline font diagnostic", () => {
let markerResultValue: PdfMarkers | null = null;
let rasterEvidence: PdfRasterEvidence | null = null;
let downloadStatus = "not-started";
let rasterEvidenceStatus = "not-attempted";
try {
await page.goto(fixture.builderURL);
await openSidebarSection(page, "Export");
@@ -346,26 +358,35 @@ test.describe("offline font diagnostic", () => {
const path = testInfo.outputPath("offline-font-browser-download.pdf");
await download.saveAs(path);
const bytes = new Uint8Array(await readFile(path));
markerResultValue = extractedMarkerResult(await readPdfText(bytes));
rasterEvidence = await renderPdfRasterEvidence(page, bytes);
await testInfo.attach("browser-download-raster.png", {
body: Buffer.from(rasterEvidence.rasterDataUrl.split(",")[1] ?? "", "base64"),
contentType: "image/png",
});
try {
markerResultValue = extractedMarkerResult(await readPdfText(bytes));
rasterEvidence = await renderPdfRasterEvidence(page, bytes);
await testInfo.attach("browser-download-raster.png", {
body: Buffer.from(rasterEvidence.rasterDataUrl.split(",")[1] ?? "", "base64"),
contentType: "image/png",
});
rasterEvidenceStatus = "received";
} catch {
rasterEvidenceStatus = "unresolved-raster-evidence-error";
}
} catch {
downloadStatus = "renderer-or-network-error";
downloadStatus = "download-error";
} finally {
await report(testInfo, "browser-download", {
cache: "new-browser-context",
blockedExternalFontRequests: cold.blockedRequests,
networkStatus: networkStatus(cold.blockedRequests),
downloadStatus,
rasterEvidenceStatus,
textLayerMarkers: markerResultValue ?? "not-extracted",
glyphStatus: rasterEvidence?.referenceGlyphs ?? "not-rasterized",
});
await cold.context.close();
}
if (rasterEvidence) {
if (downloadStatus === "received") {
expect(rasterEvidenceStatus).toBe("received");
expect(rasterEvidence).not.toBeNull();
if (!rasterEvidence) return;
expect(rasterEvidence.referenceGlyphs).toHaveLength(offlineFontScriptSamples.length);
expect(rasterEvidence.referenceGlyphs.every(({ status }) => status === "visible")).toBe(true);
}