Acrofields are not filled using Nest-js and typescript
See original GitHub issueHi, i have tried to use nestjs in order to make a service that fills acrofields the way i’ve seen it in this issue : https://github.com/Hopding/pdf-lib/issues/362. But, the problem is that i get the pdf but the fields are not filled at all. But they should since i am using pretty much the same code. Here is the pdf i got : https://www.pdfhost.net/index.php?Action=Download&File=5e3ca19d8d36dd95925c325af149b212
and here is the code of my service :
import { Injectable } from '@nestjs/common';
import * as fs from "fs";
import fetch from 'node-fetch';
import {
asPDFName,
degrees,
drawImage,
drawText,
PDFArray,
PDFContentStream,
PDFDict,
PDFDocument,
PDFFont,
PDFHexString,
PDFImage,
PDFName,
PDFNumber,
PDFOperator,
PDFOperatorNames as Ops,
popGraphicsState,
pushGraphicsState,
rgb,
rotateDegrees,
StandardFonts,
translate,
} from 'pdf-lib';
@Injectable()
export class AppService {
generatepdf(){
const getAcroForm = (pdfDoc: PDFDocument) =>
pdfDoc.catalog.lookupMaybe(PDFName.of('AcroForm'), PDFDict);
const getAcroFields = (pdfDoc: PDFDocument): PDFDict[] => {
const acroForm = getAcroForm(pdfDoc);
if (!acroForm) return [];
const fieldRefs = acroForm.lookupMaybe(PDFName.of('Fields'), PDFArray);
if (!fieldRefs) return [];
const fields = new Array(fieldRefs.size());
for (let idx = 0, len = fieldRefs.size(); idx < len; idx++) {
fields[idx] = fieldRefs.lookup(idx);
}
return fields;
};
const findAcroFieldByName = (pdfDoc: PDFDocument, name: string) => {
const acroFields = getAcroFields(pdfDoc);
return acroFields.find((acroField) => {
const fieldName = acroField.get(PDFName.of('T'));
return fieldName instanceof PDFName && fieldName.value() === name;
});
};
const imageAppearanceStream = (
image: PDFImage,
rotation: number,
width: number,
height: number,
) => {
const dict = image.doc.context.obj({
Type: 'XObject',
Subtype: 'Form',
FormType: 1,
BBox: [0, 0, width, height],
Resources: { XObject: { Image: image.ref } },
});
const operators = [
rotateDegrees(rotation),
translate(0, rotation % 90 === 0 ? -width : 0),
...drawImage('Image', {
x: 0,
y: 0,
width: height,
height: width,
rotate: degrees(0),
xSkew: degrees(0),
ySkew: degrees(0),
}),
];
const stream = PDFContentStream.of(dict, operators, false);
return image.doc.context.register(stream);
};
const fillAcroTextField = (acroField: PDFDict, text: string, font: PDFFont) => {
const rect = acroField.lookup(PDFName.of('Rect'), PDFArray);
const width =
rect.lookup(2, PDFNumber).value() - rect.lookup(0, PDFNumber).value();
const height =
rect.lookup(3, PDFNumber).value() - rect.lookup(1, PDFNumber).value();
const MK = acroField.lookupMaybe(PDFName.of('MK'), PDFDict);
const R = MK && MK.lookupMaybe(PDFName.of('R'), PDFNumber);
const rotation = R ? R.value() : 0;
const N = singleLineAppearanceStream(font, text, rotation, width, height);
acroField.set(PDFName.of('AP'), acroField.context.obj({ N }));
acroField.set(PDFName.of('Ff'), PDFNumber.of(1 /* Read Only */));
acroField.set(PDFName.of('V'), PDFHexString.fromText(text));
};
const beginMarkedContent = (tag: string) =>
PDFOperator.of(Ops.BeginMarkedContent, [asPDFName(tag)]);
const endMarkedContent = () => PDFOperator.of(Ops.EndMarkedContent);
const singleLineAppearanceStream = (
font: PDFFont,
text: string,
rotation: number,
width: number,
height: number,
) => {
const rotationCorrectedHeight = rotation % 90 === 0 ? width : height;
const size = font.sizeAtHeight(rotationCorrectedHeight - 8);
const encodedText = font.encodeText(text);
const x = 0;
const y = rotationCorrectedHeight - size;
return textFieldAppearanceStream(
font,
size,
encodedText,
rotation,
x,
y,
width,
height,
);
};
const textFieldAppearanceStream = (
font: PDFFont,
size: number,
encodedText: PDFHexString,
rotation: number,
x: number,
y: number,
width: number,
height: number,
) => {
const dict = font.doc.context.obj({
Type: 'XObject',
Subtype: 'Form',
FormType: 1,
BBox: [0, 0, width, height],
Resources: { Font: { F0: font.ref } },
});
const operators = [
rotateDegrees(rotation),
translate(0, rotation % 90 === 0 ? -width : 0),
beginMarkedContent('Tx'),
pushGraphicsState(),
...drawText(encodedText, {
color: rgb(0, 0, 0),
font: 'F0',
size,
rotate: degrees(0),
xSkew: degrees(0),
ySkew: degrees(0),
x,
y,
}),
popGraphicsState(),
endMarkedContent(),
];
const stream = PDFContentStream.of(dict, operators);
return font.doc.context.register(stream);
};
(async () => {
const ticketTemplateBytes = await fetch(
'https://vivere.s3.amazonaws.com/b2936be1256f34a05dcfe14541ea0ab02f82607ab8282f149d4a157dddaad2da/e2dc9810897d9d40260d25548e5b3633d29d45fa80baedd136800d3a2bee1e84.pdf',
).then((res) => res.arrayBuffer());
const pdfDoc = await PDFDocument.load(ticketTemplateBytes);
// Fill Form ---------------------------------------------
const helveticaFont = await pdfDoc.embedFont(StandardFonts.Helvetica);
const fillInField = (fieldName: string, text: string) => {
const field = findAcroFieldByName(pdfDoc, fieldName);
if (field) fillAcroTextField(field, text, helveticaFont);
};
const lockField = (acroField: any) => {
const fieldType = acroField.lookup(PDFName.of('FT'));
if (fieldType === PDFName.of('Tx')) {
acroField.set(PDFName.of('Ff'), PDFNumber.of(1 << 0 /* Read Only */));
}
};
fillInField('STARTDATE', '03/01/2020');
fillInField('STARTTIME', '1:18 PM');
fillInField('ADDRESS', '123 Yolk Drive');
fillInField('FULLNAME', 'Humpty Dumpty');
fillInField('SERIALNO', '876-ABC-5');
fillInField('PRICE', '$2500');
const acroFields = getAcroFields(pdfDoc);
acroFields.forEach((field) => lockField(field));
const pdfBytes = await pdfDoc.save();
fs.writeFileSync('bmr.pdf', pdfBytes);
})();
}
}
and the controller :
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get("pdf")
getHello() {
return this.appService.generatepdf();
}
}
Basically, I don’t understand why it doesn’t work and fill the fields. If someone can tell me why or what’s wrong in my code (but to me, it seems perfectly valid), it will be great. Thank you.
Issue Analytics
- State:
- Created 3 years ago
- Comments:6 (2 by maintainers)
Top Results From Across the Web
Fill Data In Existing PDF Form Using NodeJS - Stack Overflow
There's a nice thread on how to fill in PDF form text fields using the pdf-lib Node.js package: https://github.com/Hopding/pdf-lib/issues/48.
Read more >Mapped types | NestJS - A progressive Node.js framework
We can generate a derived type that has every property except email as shown below. In this construct, the second argument to OmitType...
Read more >Why are the AcroFields in my document empty?
I have a PDF form with filled out fields. I can change the values and save them. But when I try to read...
Read more >pdftk-fill-pdf-options - npm
pdftk-fill-pdf · fills multiple form fields using same acrofield name · handles checkboxes · handles undefined and null values with empty string ...
Read more >How can I get the full object in Node.js's console.log(), rather than ...
const util = require('util') var myObject = { /* nested properties not shown */ } myObject[util.inspect.custom] = function(){ return JSON.stringify( this, ...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found

Hello @snakerv!
I’ve provided an updated version of the
findAcroFieldByNamefunction in the example you are referencing: https://github.com/Hopding/pdf-lib/issues/362#issuecomment-620884706. If you use this new version of the function it should fix your issue.I hope this helps!
pdf-libnow has form creation and filling APIs that should be used instead of the above example(s). See the form filling JSFiddle for a working example. Additional information is available in the README and API docs. ThePDFTextField.setTextmethod is of particular relevance to this issue.