How to anonymise sensitive data in PDF documents with Python

작성자

카테고리:

← 피드로
DEV Community · Jason Farrar · 2026-07-31 개발(SW)
Cover image for How to anonymise sensitive data in PDF documents with Python

Jason Farrar

Have you ever needed to share a bank statement with someone? Maybe you’ve needed a user of your project to share one with you for troubleshooting.

I hit this exact problem. I’ve been building an extensible tool to convert UK bank statements into a relational database, but I only have access to statements from my own bank. How can I get access to other bank statements without asking people to send me sensitive, personally identifiable information?

I needed anonymised bank statements that retained their layout and font encoding so the statements could be processed in exactly the same way as the original. I also wanted to scramble everything by default, but enable the protection of dates and transaction types. Finally, it would be helpful if scrambling multiple statements from the same account could be configured to always scramble to the same fake value!

So I built a Python tool that does it properly — scrambles the sensitive data while keeping the PDF looking exactly the same.

The problem with PDF redaction

Traditional redaction tools typically do one of two things:

  1. Black boxes — overlay opaque rectangles. The text is still underneath (visible as soon as my parser processes it). Not actually secure.
  2. Text replacement — swap the text, but break the font encoding, layout, or both. The PDF not only looks wrong, but even the slightest change in the makeup of the statement could invalidate my config files and render them useless.

Neither is ideal. What I needed was to actually replace the bytes in the PDF’s content stream — the raw instructions that tell the PDF viewer what to draw — while preserving the font encoding and positioning.

Why pikepdf?

I looked at three Python PDF libraries:

Library Level Why it didn’t work PyPDF2 High-level Merging, splitting, extracting — no content stream access pdfplumber High-level I already use this for text extraction, but it doesn’t modify content streams pikepdf Low-level Direct access to content stream operators and font encodings

pikepdf gives you access to the raw PDF operators — Tj (show text), TJ (show text with positioning), Tm (set text matrix). This is where the actual text lives, and where I needed to make changes.

How it works

The core idea is simple:

  1. Parse the PDF’s content stream into operators
  2. Scan for sensitive patterns (sort codes, account numbers, IBANs, card numbers)
  3. Replace the matching bytes with scrambled alternatives
  4. Write the modified content stream back

Here’s the tricky part: PDF font encodings. Different banks use different encoding strategies:

  • Latin-1 (single-byte, straightforward)
  • ToUnicode CMap (maps character codes to Unicode — common in modern PDFs)
  • Identity-H (CID fonts, multi-byte — used by some banking software)

The tool handles all three, so the scrambled text renders correctly regardless of the bank’s PDF generator.

Usage

One function, one import:

from bank_statement_anonymiser import anonymise_pdf

anonymise_pdf("statement.pdf", "anonymised.pdf")

Enter fullscreen mode Exit fullscreen mode

That’s it. The output PDF looks identical to the input, but with sensitive data scrambled.

What gets anonymised

The tool targets these patterns:

  • Sort codes (UK-specific: XX-XX-XX format)
  • Account numbers (8-digit UK accounts)
  • IBANs (international bank account numbers)
  • Card numbers (Visa, Mastercard patterns)
  • Merchant names (configurable via config files)

What doesn’t get anonymised

  • Transaction Values (as my dependent projects validate the value of transaction lines against the total statement movement)
  • Dates (various formats to cover transaction and statement dates)
  • Transaction Types (specified in the never_anonymise_system.toml containing known transaction types for supported banks)

Configuration

You can further customise what gets anonymised by passing your own TOML config files:

# always_anonymise.toml — force-replace these patterns
"12-12-12" = "99-99-99"
"Jason Farrar" = "John Doe"

Enter fullscreen mode Exit fullscreen mode

# never_anonymise.toml — protect these phrases
exclude = [
    "Balance Brought Forward",
    "Account Summary",
]

Enter fullscreen mode Exit fullscreen mode

The system uses an exclusion-based approach: everything is scrambled by default, then you specify what to protect.

Limitations

This is UK-specific. The regex patterns for sort codes, account numbers, and IBAN formats are included for UK banks. Currently tested successfully on:

  • HSBC
  • Natwest
  • TSB
  • Halifax

It may also work for other banks, so please give it a go and let me know!
Other banks may require adding new regex patterns, and will almost certainly use different transaction types. There may also be other font encodings I haven’t come across yet.
All contributions are very much appreciated!

Installation

pip install uk-bank-statement-anonymiser

Enter fullscreen mode Exit fullscreen mode

Further instructions on installation and usage are available in the README

Open source

MIT licensed. All processing is local — no network requests, no accounts, no data collection.

GitHub: github.com/boscorat/uk-bank-statement-anonymiser

Have you encountered similar PDF anonymisation challenges? I’d be interested to hear about other banks’ PDF formats — the more patterns I can add, the more useful this becomes.

Any questions or comments are very welcome. I’d love to hear if you think there’s a better way to approach this problem, or changes I can make to the structure to widen the useful scope of this project.

원문에서 계속 ↗

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다