Гнездо Js - метод возвращает пустой Pdf без заполнения форм и не выдает никаких ошибок - PullRequest
0 голосов
/ 26 апреля 2020

Я пытаюсь использовать метод машинописного текста, который я видел в этом посте о пакете "pdf-lib": https://github.com/Hopding/pdf-lib/issues/362 Итак, я создал службу с гнездом:

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);
})();
}


}

и контроллер:

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();
  }
}

Таким образом, код возвращает нужный мне pdf, но он не заполняет формы. Кто-нибудь знает почему? Я не вижу, что здесь не так, потому что нет ошибки. Спасибо.

...