Skip to content
This repository was archived by the owner on May 29, 2026. It is now read-only.
This repository was archived by the owner on May 29, 2026. It is now read-only.

PDF Anonymisation: SAPP byte-replace Path A implementation #1997

Description

@rjzondervan

Summary

Implement PDF anonymisation in OpenRegister via byte-level text replacement through the SAPP library — Approach H from the pdf-anonymisation-discovery research.

PDFs currently fall through to DocumentProcessingHandler::replaceWordsInTextDocument, which calls str_ireplace on binary content and produces an unopenable file. PDFs are binary containers with FlateDecode-compressed content streams, font-encoded text (Identity-H + ToUnicode CMaps in Word-generated documents), and kerning-split text-showing operators (TJ arrays). The current implementation breaks every real-world Woo PDF.

Approach

SAPP byte-level text replacement with Helvetica fallback. The required SAPP primitives don't exist upstream — we've forked dealfonso/sapp at ConductionNL/sapp and implemented the missing pieces as an 8-PR series on the work/text-replacement integration branch:

PR Capability
ConductionNL/sapp#3 Filter chain dispatcher (foundation)
#4 ASCIIHexDecode
#5 RunLengthDecode
#6 ASCII85Decode
#7 LZWDecode + FlateDecode predictor refactor
#8 ToUnicode CMap + text-space matching ⭐
#9 TJ kerning-array flattening
#10 Helvetica fallback + parameter validation + stable API

The OR-side consumer integration is what this issue tracks.

Specs

Already drafted on feat/pdf-anonymisation — proposal, design (10 decisions D1-D10), spec.md with normative requirements, tasks.md (13 sections including the composer-VCS wiring as §10). Will move to the new feature branch.

Tasks (high level)

  • Bring in the pdf-anonymisation OpenSpec change from feat/pdf-anonymisation
  • Wire ddn/sapp via composer VCS repository (pinned to feat/text-replacement-api SHA 25db760 for now; moves to work/text-replacement once the 8 PRs merge there; eventually upstream tag)
  • Implement lib/Service/File/Pdf/PdfTextReplacer wrapping PDFDoc::replaceTextInDocument
  • Implement lib/Service/File/Pdf/PdfMetadataSanitizer (parity with the sister office-document-sanitization change)
  • Route PDF inputs in DocumentProcessingHandler::anonymizeDocument to PdfTextReplacer instead of the broken str_ireplace path
  • Validation gate: re-extract output via smalot/pdfparser, assert no residual entity text, fail-closed on miss
  • Adjacent-duplicate placeholder collapse post-pass ([P:7] [P:7][P:7])
  • Tests + Newman integration tests

Out of scope

  • Path B (Collabora ODT round-trip fallback) — deferred to a follow-up issue once Path A is in operators' hands and we have fall-through-rate data from real Woo PDFs.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions