BASALT · JOURNAL
Redacting every instance of a name across a long document
A witness name appears somewhere on 340 pages of a transcript and it has to come out of every one of them. The task is easy to describe and easy to get wrong, because a PDF does not store words and the search box will not find everything. The reliable way to search and redact all instances in PDF documents is to know every shape the value takes and to look at every hit before anything is applied.
Why a PDF search finds fewer hits than you expect
A page is a content stream: operators that place glyphs at coordinates. Text arrives through text-showing operators, Tj for a single string and TJ for an array of strings with kerning adjustments between the elements. There is no word object in the file; a word is whatever a person reads off the page once the glyphs land.
So a typesetter that tightens the spacing between two letters emits one word as two elements of a TJ array, or as two Tj operators with a positioning operator between them. A search over the raw stream looks for a contiguous string and finds nothing, while a search that reconstructs glyph runs before matching finds it. Which behavior you have is rarely stated anywhere in the interface.
Glyph codes in a subset font are arbitrary numbers, and the /ToUnicode CMap is what tells a consumer which characters they represent. It is optional, occasionally wrong, and often missing for the decorative fonts in a letterhead or a stamp. An /ActualText entry, where present, overrides the glyphs for extraction, so visible text and extractable text can disagree in either direction.
The forms one value takes
Write down the variants before you search, because one value is almost never one string. A phone number appears with hyphens, parentheses, periods, spaces, and a country code, sometimes all in one document.
A name is worse. It appears bare, possessive, in capitals inside a running header, as an initial plus surname, reversed with a comma in an index, and hyphenated at a line break, where the halves sit on separate lines as separate glyph runs with nothing joining them. A search matching only the bare form reports a clean sweep and leaves the header untouched on every page.
The places a hit hides outside the page text
Running headers, footers, Bates stamps, and confidentiality bands are drawn per page, so a client name in a letterhead is one occurrence per page rather than one per document.
Annotations carry text in several fields: /Contents holds a comment body, /T holds the author, and an appearance stream draws the same text onto the page. Form fields live in AcroForm field dictionaries, where /V, /DV, and /TU are strings that survive a widget being painted over.
Outline entries carry titles that name parties. Optional content groups hold content on layers switched off, rendering nowhere and extracting perfectly. Scanned pages carry an OCR layer in rendering mode 3, invisible over the image and holding a full transcription, covered in redacting a scanned PDF. Off the page entirely sit the document information dictionary, the XMP packet, embedded attachments, and the file name, listed in what a PDF still carries.
How to search and redact all instances in PDF documents
Take a structural census first, so you have numbers to compare against later.
qpdf --qdf --object-streams=disable source.pdf expanded.pdf
grep -a -i -c "smith" expanded.pdf
pdftotext -layout source.pdf - | grep -i -c "smith"
pdfdetach -list source.pdf
The counts will differ, and the difference is informative. pdftotext reports what a text extractor sees, while the grep catches strings anywhere in the object structure, so a much larger second number means hits outside the page text.
Search one term at a time inside the tool, since batching terms produces one undifferentiated pile of hits and makes review slower. Review every hit and assign a reason as you go: privilege, PII, work product, HIPAA, confidential. Doing that at review time is what makes a privilege log fall out of the redactions rather than being reconstructed later from memory.
Mark repeated page furniture as a region across a page range instead of as search hits, covered in redacting the same area across many pages.
Apply everything in one operation, written as a single generation, because a redaction saved as an incremental update leaves the earlier revision in the file. Then re-run the census on the output and expect zero, using the method in how to check whether a PDF was really redacted.
Why every hit gets reviewed
Applying all hits automatically fails in both directions. Over-redaction is the quieter failure: searching for "Cole" strikes the middle of "Nicole" and the start of "Coleman". A document returned with holes in innocuous words invites a re-production and signals that nobody read the output.
Under-redaction is worse, and it happens when the hit list looked complete and a variant was never in it. Review catches this indirectly: reading hits in page context is how you notice the header form, the reversed form, and the hyphenated break.
Count hits, not pages
"Redacted on 40 pages" is not a completeness statement. It says where marks landed, not whether anything was missed, and one page of a deposition index can carry a dozen occurrences of a surname while the next carries one.
The number that means something is hits per term, before and after: how many variants were in the list, how many hits each found, how many were applied, and how many the output still yields on re-extraction. Page counts also hide structural hits, since metadata, attachments, and bookmark titles belong to no page.
Frequently asked questions
How do I search and redact all instances of a word in a PDF?
Build a term list covering every form the value takes, run the search once per term, and review each hit in page context before applying anything. Take a structural count first with pdftotext and with grep over a qpdf --qdf expansion, then re-run both on the output afterward.
Why does PDF search miss words that are visible on the page?
Because a PDF stores glyph placements rather than words, and a single word can be split across two text-showing operators when the typesetter adjusts kerning. A search matching contiguous strings in the raw content stream misses the split, while one that reconstructs glyph runs finds it. Missing or incorrect /ToUnicode maps cause the same symptom.
Does find and replace work for redacting a PDF?
No. Find and replace changes what the page shows without dependably removing the original, and it does not touch annotation values, form field values, bookmark titles, metadata, or an OCR layer. Redaction excises the glyphs from the content stream, re-emits the surviving text in place, and strips the non-page carriers in one operation.
How do I find a name that is split across two lines?
Search for the fragments as well as the whole word, and read hits in page context so hyphenated line breaks are visible. A name broken at a justified line end sits on two lines as two glyph runs with a hyphen and nothing else joining them, so no whole-word search matches it.
Should I redact every search hit automatically?
No, review each one. Automatic application over-redacts by striking substrings inside unrelated words and under-redacts whenever a variant was never in the term list, and both are visible to the receiving party. Review is also where reason codes get assigned, and a privilege log is built from those.
How do I know the redaction caught everything?
Re-extract the output with an independent parser and count. Run pdftotext and grep for each term, decompress with qpdf --qdf --object-streams=disable and grep the expanded structure, check metadata with exiftool, list attachments with pdfdetach -list, and confirm a single %%EOF.
Do headers and footers count as separate instances?
Yes, which is why the hit count is usually far higher than people expect. Headers, footers, letterheads, watermarks, and Bates stamps are drawn independently into every page rather than shared, so a name in a letterhead is one occurrence per page. A single region mark across a page range handles them efficiently.
Will redacting text remove it from a scanned page?
Not by itself. A scanned page is an image with an invisible OCR text layer drawn over it in rendering mode 3, so removing the text layer leaves the pixels readable and destroying the pixels leaves the transcription readable. Both have to go in the same operation.
Doing it in Basalt
Basalt searches a value across the whole document, reconstructing glyph runs so split words are found, and it looks in annotation values, form field dictionaries, bookmark titles, and the OCR layer as well as the page content. Every hit becomes a mark you review and accept or reject, each mark carries a reason, and nothing is destroyed until you apply. Applying excises the text glyph by glyph, re-emits the surviving text in place, destroys image pixels in marked regions, and strips metadata, XMP, attachments, and hidden layers in the same pass, written as one generation to a new file. A verifier then re-opens the written bytes with an independent parser and proves the content is gone before anything is saved, and if it cannot prove that, nothing is written at all. It is $29 once, lifetime, up to three Macs, free for 24 hours, and the engine has no network entitlement, so nothing is uploaded.
Redaction that proves itself
Basalt destroys the content you mark, then re-opens the file it wrote and proves the content is gone before it saves anything. Your documents never leave your Mac.