Files
pdfcraft/crates/audit
mtlcl410andClaude Opus 5.5 9dfc91e0e8 M8: the redaction audit finds a cover over one word of a long line (#2662)
A cover counted only when it hid 40% of the line's words or box, so a
black box over one number in a long line, and an unapplied redaction
mark over it, audited clean while the number was still extractable. A
cover that hides half of any one word now counts as well.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RUNQSPVXx3dDBabRZHAnjz
2026-10-16 06:25:35 +08:00
..

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 half of one of the line's words, 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 re or as a single m/l subpath whose corners form an axis-aligned rectangle (not other paths, shadings or patterns), and a translucent one (/ca below 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 /Rotate pages may misalign;
  • a cover that hides less than half of every word it touches, less than 40% of a line's box and less than 40% of its words (a sliver of a long value) 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.