doc_audit_redactions and `pdfcraft-cli audit` returned rectangles in PDF user space (origin at the bottom-left of the media box), while comment_list, doc_info, text_lines and the redaction tools give points from the top-left of the page as displayed. The same mark read [144.14, 91.2, 265.95, 102.2] in comment_list and [144.138, 739.8, 265.952, 750.8] in the audit. Both surfaces now convert with the helper doc_info uses (view_rect, made public for the CLI), so they report the same rectangles, after /Rotate. The CLI reads the page geometry only when there is a finding to place. AuditFinding::rect stays in user space; the audit README says which space each surface uses. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: OpenAI Codex <noreply@openai.com> Co-authored-by: Brandon Thomas <echelon@gmail.com>
pdfcraft-audit
Layer L4 (may use edit): verification tooling, the faux-redaction audit.
let findings: Vec<Finding> = audit_redactions(&doc)?; // page, kind, rect, covered_text
It reports two kinds of finding:
- faux redaction: an opaque filled rectangle painted after text it overlaps, dark enough against the text's colour (WCAG contrast below 3) to hide it, while the text is still extractable underneath;
- unapplied mark: a Redact annotation that was marked but never applied.
A properly applied redaction removes the text, so it never flags. Page backgrounds, table shading and highlights don't either: they're painted before the text, or light against it.
A rectangle covers a text line when it hides at least 40% of the area of the words the line
draws, or at least 40% of the line's box together with a positive-area intersection with one
of its words. The gap an applied redaction leaves (a TJ adjustment where the text was) is in the line's box but
holds no word, so a redaction box over it alone does not count.
Limits, by design and documented rather than hidden:
- only filled rectangles count as covers, drawn with
reor as a singlem/lsubpath whose corners form an axis-aligned rectangle (not other paths, shadings or patterns), and a translucent one (/cabelow 1) is not flagged; - covers inside form XObjects are found, but only text drawn directly in the page content is checked under them;
- annotation rectangles are compared in unrotated user space, so
/Rotatepages may misalign; - a cover that hides less than 40% of a line's box and less than 40% of its words (a short value in the middle of a long line) is not flagged.
It fails closed: an undecodable content stream, malformed /Annots, form XObjects nested deeper
than 32 or in a cycle, or a walk past the operator bound is an error, never "no findings".
Surfaces: the doc_audit_redactions automation tool and pdfcraft-cli audit. Both report each
rectangle in points from the top-left of the page as displayed (y down, after /Rotate), like
the other tools; AuditFinding::rect itself stays in PDF user space.