Guide
Filling encrypted government PDFs: what actually works, measured
You download an official blank from a court or tax site, it opens in
every viewer without asking for anything, and then your code throws:
Input document to PDFDocument.load is encrypted. There is
no password to supply, because the file was published for the public.
We ran two real forms in this class, California's FL-100 divorce
petition and Canada's TD1 tax credits return, through seven
independent fill tools and recorded exactly which ones cope.
Why a public form rejects your library
These blanks are AES-encrypted with an empty user password. The PDF spec allows a document to carry two passwords: a user password that gates opening, and an owner password that gates modification. Publishers set the user password to the empty string, so any viewer decrypts the file silently, and set a real owner password to discourage edits. Strict parsers see the encryption dictionary, notice they were given no credentials, and refuse the whole file. That is the correct reading of the spec and a useless outcome for you.
pdf-lib and its maintained fork @cantoo/pdf-lib both
reject FL-100 and TD1 at load. The
PDFops fill API is built on the same
family and refuses too, with a 400 encrypted_pdf that
tells you the workaround instead of leaving you to guess. pypdf
opens both files directly when installed as
pypdf[crypto], but writes text fields only. The full
per-tool results live on the registry pages for
ca-fl-100 and
cra-td1.
The shell answer: pdftk fills them natively
The one mainstream tool that treats an empty user password like a
viewer does is pdftk-java. It enumerated all 158 fields
of FL-100, filled 148 of them (every text field, checkbox and radio
group; the rest take no value by design), and re-reading its output
confirmed all 148 values landed. Same story on TD1: 27 of 27. No
decrypt step, and the output file comes out unencrypted.
# enumerate fields (works despite the encryption)
pdftk fl100.pdf dump_data_fields > fields.txt
# fill from an FDF file
cat > values.fdf <<'EOF'
%FDF-1.2
1 0 obj
<< /FDF << /Fields [
<< /T (FL-100[0].Page1[0].CaptionP1_sf[0].AttyInfo[0].AttyFor_ft[0]) /V (Jane Doe) >>
] >> >>
endobj
trailer
<< /Root 1 0 R >>
%%EOF
EOF
pdftk fl100.pdf fill_form values.fdf output filled.pdf
pdftk prints a warning about the owner password and carries on. The owner password restricts editing as a matter of policy, and pdftk takes the position that a file the public can open is a file the public can fill. Adobe's own tools take the stricter position. Know which side your compliance story needs before you rely on this.
The dump_data_fields trap
One gotcha cost us an hour and produces a convincing false negative.
On forms that carry an XFA layer alongside the normal field tree,
dump_data_fields prints two
FieldValue lines per field: an empty one first, then
the real value.
FieldName: FL-100[0].Page1[0].CaptionP1_sf[0].AttyInfo[0].AttyFor_ft[0]
FieldNameAlt: ATTORNEY FOR (name):
FieldFlags: 8388608
FieldValue:
FieldValue: Jane Doe
A verification script that greps the first FieldValue
line reads an empty string, concludes the fill silently failed, and
sends you debugging a problem that does not exist. Collect every
FieldValue line for a field and check whether any of
them matches what you wrote.
The in-process answer: decrypt first
If your fill runs inside a JS or Python service rather than a shell pipeline, remove the restriction up front with qpdf. The transformation is lossless: same pages, same fields, same appearance.
qpdf --decrypt fl100.pdf fl100-open.pdf
curl -X POST https://pdfops.dev/api/fill-form \
-F "pdf=@fl100-open.pdf" \
-F 'fields={"FL-100[0].Page1[0].CaptionP1_sf[0].AttyInfo[0].AttyFor_ft[0]":"Jane Doe"}' \
-F "flatten=true" \
-o filled.pdf
After the decrypt, the file behaves like any other AcroForm: pdf-lib loads it, the API fills and flattens it, and field values verify on re-inspection. In our runs this path landed every supported field on both forms.
How common is this class?
More common than two unlucky picks. After this post was drafted we extended the corpus to 22 forms, and five of them ship empty-password AES: FL-100 and FL-150 (so it is the California Judicial Council's house style, on 2 of 2 sampled), TD1 and T2200 (same for the CRA, 2 of 2), and SSA-89, the SSN verification consent signed in most US mortgage closings. pdftk filled all five natively; every pdf-lib-family tool refused all five. If your product touches court filings, Canadian tax paperwork or lending, you will meet this class.
The forms this does not save
Encryption is sometimes the smaller half of the problem. Every current USCIS form we sampled except the I-9 combines this same empty-password AES with a dynamic XFA body, which means the visible form is rendered from an embedded XML template rather than from the AcroForm field tree. qpdf gets you through the encryption, but fillers in the pdf-lib family then write the AcroForm layer and drop XFA on save, and pdftk inherits the same limits. For that class, plan on the AcroForm output being correct in mainstream viewers and blank in old XFA-first Adobe products, or use the agency's own e-filing channel.
Related
- CA FL-100 registry page: the full seven-tool matrix, field schema, and provenance hash for the encrypted court form
- CRA TD1 registry page: the same measurements for the Canadian tax form, which adds an XFA layer on top of the encryption
- Deterministic PDF filling: why two runs of the same fill should produce byte-identical output, and how PDFops guarantees it