question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Acrofields are not filled using Nest-js and typescript

See original GitHub 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 :

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:closed
  • Created 3 years ago
  • Comments:6 (2 by maintainers)

github_iconTop GitHub Comments

2reactions
Hopdingcommented, Apr 28, 2020

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!

0reactions
Hopdingcommented, Sep 16, 2020

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.

Read more comments on GitHub >

github_iconTop 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 >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found