Closed snakerv closed 4 years ago
i have the same issue, try disable font and custom appeareance:
...
const fillAcroTextField = (acroField: PDFDict, text: string, font?: PDFFont) => {
acroField.set(PDFName.of('V'), PDFString.of(text));
acroField.delete(PDFName.of('AP'));
}
...
NOTE: i testing de example in #205 and working properly with custom font!
i have the same issue, try disable font and custom appeareance:
... const fillAcroTextField = (acroField: PDFDict, text: string, font?: PDFFont) => { acroField.set(PDFName.of('V'), PDFString.of(text)); acroField.delete(PDFName.of('AP')); } ...
NOTE: i testing de example in #205 and working properly with custom font!
thanks. I have try this in the service, but still does not work. Have you got an idea only in order to fill the field (i don't care about styling)? Thank you.
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,
PDFString,
} 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 fillAcroTextField = (acroField: PDFDict, text: string, font?: PDFFont) => {
acroField.set(PDFName.of('V'), PDFString.of(text));
acroField.delete(PDFName.of('AP'));
}
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 timesFont = await pdfDoc.embedFont(StandardFonts.TimesRoman);
const fillInField = (fieldName: string, text: string) => {
const field = findAcroFieldByName(pdfDoc, fieldName);
if (field) fillAcroTextField(field, text, timesFont);
};
// 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('nope.pdf', pdfBytes);
})();
}
}
Sorry i forgot you need enable NeedAppearances, maybe your original code working properly:
const acroForm = getAcroForm(pdfDoc);
acroForm.set(PDFName.of('NeedAppearances'), PDFBool.True)
Hello @snakerv!
I've provided an updated version of the findAcroFieldByName
function 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!
Thank you guys, working perfectly fine thanks to you both. Thanks again for the help! :)
pdf-lib
now 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. The PDFTextField.setText
method is of particular relevance to this issue.
Hi, 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 :
and the controller :
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.