Bug Report
Description of the problem
0.20.0 restricted AcroForm options to documented mappings. Three things a tagged, PDF/UA-compliant form field needs became unreachable through the public API. All three worked in 0.19.1 through the documented passthrough, so this is a regression rather than a feature request:
v0.19.1 docs/forms.md — "Consult the PDF Reference and set these attributes in the options object. Any options that are not listed above will be added directly to the corresponding PDF Object."
v0.20.2's docs/forms.md asks that unsupported options be raised as issues, which is what this is.
1. TU has no mapping. PDF/UA-1 requires an alternate field name on form fields. mapStrings recognises only VALUE_MAP = { value: 'V', defaultValue: 'DV' } plus label → MK.CA, so no option produces TU.
2. structParent is dropped between formAnnotation and annotate. annotate() already accepts and consumes structParent — it destructures it and calls structParent.add(new PDFAnnotationReference(ref)). But formAnnotation() passes annotate() the dictionary returned by _fieldDict(), which builds a fresh object, so a structParent given to formText() never reaches annotate(). The widget is therefore never nested in its Form structure element.
This one looks like plumbing rather than an intentional removal: the capability exists on both sides, only the hand-off is missing.
3. fontSize is documented but silently ignored when the field font matches the form's default font. docs/forms.md lists it as a common option — "Sets the font size used in the field appearance string. The default, 0, means auto sizing." _mapFont only writes DA when _acroform.defaultFont !== _font.name, so a field using the same font as initForm ignores fontSize and inherits the form-level /DA … 0 Tf, i.e. auto sizing. Nothing documents that condition.
Together, (1) and (2) make a form field fail PDF/UA-1 validation with no supported workaround. veraPDF 1.28.2 (ua1) on the output of the sample below reports these three form-related failures:
7.18.1-3 A form field shall have a TU key present or all its Widget annotations
shall have alternative descriptions
7.18.4-1 A Widget annotation shall be nested within a Form tag
7.18.4-2 If the Form element omits a Role attribute, it shall have only one child:
an object reference identifying the widget annotation
(The minimal sample also fails 7.1-9, 7.1-10 and 5-1, because it omits the document title, viewer preferences and the PDF/UA identifier. Those are artifacts of keeping the sample short and are unrelated to this report. In our real document, which sets all of them, the three above are the only failures.)
Code sample
import PDFDocument from 'pdfkit';
import { writeFileSync } from 'node:fs';
const doc = new PDFDocument({ pdfVersion: '1.7', tagged: true, autoFirstPage: false });
const chunks = [];
doc.on('data', (c) => chunks.push(c));
doc.on('end', () => {
const pdf = Buffer.concat(chunks);
writeFileSync('out.pdf', pdf);
const s = pdf.toString('latin1');
console.log('has /TU :', /\/TU\s*\(/.test(s));
console.log('/S /Form:', /\/S\s*\/Form[\s\S]{0,200}?\/K\s*(\[[^\]]*\])/.exec(s)?.[1]);
});
doc.addPage();
doc.font('Helvetica');
doc.initForm();
const formStruct = doc.struct('Form', { title: 'Contract number' });
doc.addStructure(formStruct);
doc.font('Helvetica').fontSize(9);
doc.formText('contractNumber', 100, 100, 120, 12, {
align: 'right',
fontSize: 9, // (3) ignored: field font == default font
TU: new String('Contract No.'), // (1) dropped
structParent: formStruct, // (2) dropped
});
doc.end();
Prints:
has /TU : false
/S /Form: []
Expected /TU (Contract No.) and a /K holding an /OBJR that references the widget.
To isolate (3), change the field's font to one other than the form's default — doc.font('Helvetica-Bold') before formText — and /DA (/F2 9 Tf 0 g) appears. With Helvetica on both, the only /DA in the file is the form-level /DA (/F1 0 Tf 0 g).
Your environment
- pdfkit version: 0.20.2 (the AcroForm change landed in 0.20.0)
- Node version: 24.18.0
- Browser version (if applicable): n/a — Node
- Operating System: macOS 27.0
- Validator: veraPDF 1.28.2, profile
ua1
Proposed fix
For (1), a documented option mapping to TU. The PDF spec calls it the alternate field name, so alternateName would match the existing naming style (defaultValue, backgroundColor); tooltip is what people are more likely to search for. Happy to follow whichever you prefer — it is one more entry in VALUE_MAP.
For (2), forward options.structParent onto the dictionary formAnnotation() hands to annotate(). annotate() needs no change.
For (3), I have not proposed a patch, because always emitting DA would change output for existing users who pass fontSize with a matching font, and I do not know whether the defaultFont !== _font.name guard is deliberate. Reporting it rather than guessing.
I am happy to open a pull request for (1) and (2) if the approach and the option name sound right.
Context
We generate a tagged, PDF/UA-1 vehicle sales contract with a single text field on AWS Lambda, and validate it in CI with veraPDF. On 0.19.1 the field is compliant; on 0.20.2 it is not, with no supported way to fix it. We are holding at 0.19.1 for now.
Separately, we independently hit #1801 before finding it already reported — same TypeError: Invalid URL from a CJS esbuild bundle, also via AWS CDK's NodejsFunction. Mentioning it only as a second data point; it is a different bug from this one, and either alone is enough to keep us on 0.19.1.
Disclosure: this issue was written with an AI coding assistant (Claude Code). The reproduction above was run, not only written.
Bug Report
Description of the problem
0.20.0 restricted AcroForm options to documented mappings. Three things a tagged, PDF/UA-compliant form field needs became unreachable through the public API. All three worked in 0.19.1 through the documented passthrough, so this is a regression rather than a feature request:
v0.20.2's
docs/forms.mdasks that unsupported options be raised as issues, which is what this is.1.
TUhas no mapping. PDF/UA-1 requires an alternate field name on form fields.mapStringsrecognises onlyVALUE_MAP = { value: 'V', defaultValue: 'DV' }pluslabel → MK.CA, so no option producesTU.2.
structParentis dropped betweenformAnnotationandannotate.annotate()already accepts and consumesstructParent— it destructures it and callsstructParent.add(new PDFAnnotationReference(ref)). ButformAnnotation()passesannotate()the dictionary returned by_fieldDict(), which builds a fresh object, so astructParentgiven toformText()never reachesannotate(). The widget is therefore never nested in itsFormstructure element.This one looks like plumbing rather than an intentional removal: the capability exists on both sides, only the hand-off is missing.
3.
fontSizeis documented but silently ignored when the field font matches the form's default font.docs/forms.mdlists it as a common option — "Sets the font size used in the field appearance string. The default,0, means auto sizing."_mapFontonly writesDAwhen_acroform.defaultFont !== _font.name, so a field using the same font asinitFormignoresfontSizeand inherits the form-level/DA … 0 Tf, i.e. auto sizing. Nothing documents that condition.Together, (1) and (2) make a form field fail PDF/UA-1 validation with no supported workaround. veraPDF 1.28.2 (
ua1) on the output of the sample below reports these three form-related failures:(The minimal sample also fails
7.1-9,7.1-10and5-1, because it omits the document title, viewer preferences and the PDF/UA identifier. Those are artifacts of keeping the sample short and are unrelated to this report. In our real document, which sets all of them, the three above are the only failures.)Code sample
Prints:
Expected
/TU (Contract No.)and a/Kholding an/OBJRthat references the widget.To isolate (3), change the field's font to one other than the form's default —
doc.font('Helvetica-Bold')beforeformText— and/DA (/F2 9 Tf 0 g)appears. WithHelveticaon both, the only/DAin the file is the form-level/DA (/F1 0 Tf 0 g).Your environment
ua1Proposed fix
For (1), a documented option mapping to
TU. The PDF spec calls it the alternate field name, soalternateNamewould match the existing naming style (defaultValue,backgroundColor);tooltipis what people are more likely to search for. Happy to follow whichever you prefer — it is one more entry inVALUE_MAP.For (2), forward
options.structParentonto the dictionaryformAnnotation()hands toannotate().annotate()needs no change.For (3), I have not proposed a patch, because always emitting
DAwould change output for existing users who passfontSizewith a matching font, and I do not know whether thedefaultFont !== _font.nameguard is deliberate. Reporting it rather than guessing.I am happy to open a pull request for (1) and (2) if the approach and the option name sound right.
Context
We generate a tagged, PDF/UA-1 vehicle sales contract with a single text field on AWS Lambda, and validate it in CI with veraPDF. On 0.19.1 the field is compliant; on 0.20.2 it is not, with no supported way to fix it. We are holding at 0.19.1 for now.
Separately, we independently hit #1801 before finding it already reported — same
TypeError: Invalid URLfrom a CJS esbuild bundle, also via AWS CDK'sNodejsFunction. Mentioning it only as a second data point; it is a different bug from this one, and either alone is enough to keep us on 0.19.1.Disclosure: this issue was written with an AI coding assistant (Claude Code). The reproduction above was run, not only written.