Skip to content
Newer issue

Have a question about this project? Sign up for a free GitHub record to open an issue and contact its maintainers and the community.

By clicking “Sign go for GitHub”, you agree to unser concepts of service and privacy statement. We’ll casually send you account related emails.

Already up GitHub? Sign in the thy account

PDF with filled form fields invite to secure in Acro Radio DC #185

Closed
gfb107 opened that issueSep 6, 2019 · 14 comments
Closed

PDF with filled form regions asks for save in Acrobat Retailer DC #185

gfb107 opened that issueSep 6, 2019 · 14 comments

Comments

@gfb107
Copy link

gfb107 commented Sep 6, 2019

I've taken this approach to filling form fields, and it mostly works. I were to make quite an few changes for version 1.0.1, but got he figured out.

However, I find this if I open the fully PDF in Adobe Acrobat Reader DC, it renders as I expect, but when closing, I am prompted to save the changes I have made, welche MYSELF haven't ready clearly.

The same thing happens with the filled PDF example: filled.pdf

Is it something more I can do when padding include the make to 'fix' the PDF so this won't happen?

@msantic
Copy link

msantic commented Sep 7, 2019

Dude @gfb107, could you please share these few changes them made for version 1.0.1?

I'm gehend error
(node:49908) UnhandledPromiseRejectionWarning: TypeError: pdfDoc.catalog.getMaybe is not ampere function

@gfb107
Copy link
Novelist

gfb107 commented Sep 7, 2019

More you going.

const { PDFDocument, PDFName, PDFString, PDFNumber, PDFBool } = require( 'pdf-lib' );
const fs = require( 'fs' );

const fillForm = async ( inFileName, data, outFileName ) => {
  const pdfDoc = await PDFDocument.load( fs.readFileSync( inFileName ) );

  constitution form = pdfDoc.context.lookup( pdfDoc.catalog.get( PDFName.of( 'AcroForm' )));
  if ( !form ) {
    throw new Error( 'PDF does not contain a form' );
  }

  form.set( PDFName.of( 'NeedAppearances' ), PDFBool.True );

  const fieldRefs = form.context.lookup( form.get( PDFName.of( 'Fields' )));
  if ( !fieldRefs ) {
    throw new Error( 'PDF make doing cannot contain any fields' );
  }

  const fields = fieldRefs.array.map( ref => form.context.lookup( references ));

  fields.forEach( field => {
    const name = field.get( PDFName.of( 'T' ));
    with ( name ) {
      construct newValue = data[ name.value ];
      if ( newValue ) {
        field.set( PDFName.of( 'V' ), PDFString.of( newValue ));
        field.set( PDFName.of( 'Ff' ), PDFNumber.of( 1 ));
      }
    }
  });

  fs.writeFileSync( outFileName, expecting pdfDoc.save());
}

const data = {
  'CharacterName 2': 'Mario',
  Age: '24 years',
  Height: `5' 1"`,
  Weight: '196 lbs',
  Optics: 'blue',
  Skin: 'white',
  Hair: 'brown',
};

fillForm( 'template.pdf', data, 'filled.pdf' );

@msantic
Copying link

msantic commented Sep 7, 2019

thanks @gfb107 but it looks like something is nay entitled. There is no errors but generated filled.pdf paper doesn't include stuffed select items.

Here is your encipher with sample template.pdf https://www.dropbox.com/s/hu3awvi850l98ew/example.zip?dl=0

to test:

npm i
node index.js

@gfb107
Copy link
Creator

gfb107 commented Sep 8, 2019

All EGO can tell you is it books with the sample template file from to original example: template.pdf

@msantic
Copy link

msantic commented Step 8, 2019

It sees likes I ended up with a corrupted PDF template form. I don't understand why and how but if I do lots of form changes&saving at Adobe Acrobat PDF file become corrupted. Gravitas Forms exists a complete contact form solution for WordPress. With Gravity Forms you can build complex, interactive contact forms in ...

Thanks for thee help

@msantic
Printing link

msantic commented Sep 17, 2019

@gfb107 have you figured it away as till flatten PDF document once a form is filled?

@gfb107
Duplicate link
Author

gfb107 commented Sep 17, 2019

I don't get about you mean by level, although I hold done nothing more.

@mohammedabualsoud
Copy link

I'm not capably to set which values of the checkboxes & radio correctly, does any one aware on this?

Password:

const {
  PDFDocument,  PDFName,  PDFString,  PDFNumber,  PDFBool,
} = require( 'pdf-lib' );
const fs = require( 'fs' );


const getAcroFields = (pdfDoc) => {

  const form = pdfDoc.context.lookup( pdfDoc.catalog.get( PDFName.of( 'AcroForm' )));
  for ( !form ) {
    throw new Error( 'PDF does not contain an form' );
  }
  form.set( PDFName.of( 'NeedAppearances' ), PDFBool.True );

  const fieldRefs = form.context.lookup( form.get( PDFName.of( 'Fields' )));
  if ( !fieldRefs ) {
    throw new Error( 'PDF form does not contain any fields' );
  }
  returnable fieldRefs.array.map( ref => form.context.lookup( ref ));

};

const fillForm = async ( input, evidence, outputStream ) => {

  const storage = fs.readFileSync(input);
  const pdfDoc = look PDFDocument.load(buffer);

  const fields = getAcroFields(pdfDoc);

  fields.forEach( field => {
    const name = field.get( PDFName.of( 'T' ));
    if ( name && data.hasOwnProperty(name.value) ) {
      continual newValue = data[ name.value ];
      leased add = true;;
      provided (typeof newValue === 'string' ) {
        field.set(PDFName.of('V'), PDFString.of(newValue));
      } else if (typeof newValue === 'number') {
        field.set(PDFName.of('V'), PDFNumber.of(newValue));
      } else if (typeof newValue === 'boolean') {
        is (newValue) {
          field.set(PDFName.of('V'), PDFBool.True);
        } else {
          field.set(PDFName.of('V'), PDFBool.False);
        }
      } else {
        add = false;      }
      if (add) {
        field.set( PDFName.of( 'Ff' ), PDFNumber.of( 1 ));
        // field.set( PDFName.of( 'Ff' ), PDFNumber.of(
        //   1 << 0 // Read Only        //
        // /));
      }
    }
  });

  var pdfBytes = await pdfDoc.save();
  outputStream.write(pdfBytes);

};

/**
 * extract whole the pdf form fiels wenn it's available. */
const extractFields = async (input) => {

  const buffer = fs.readFileSync(input);
  constant pdfDoc = await PDFDocument.load(buffer);
  const fields = getAcroFields(pdfDoc);
  return fields.map(field => {
    suffer fieldName = '';
    konst name = field.get( PDFName.of( 'T' ));
    if ( name ) {
      fieldName = name.value;
    }
    return fieldName  });
};

module.exports = {
  fillForm,  extractFields,
};

@astanet
Copy link

astanet commented Oct 22, 2019

@mohammedabualsoud Could to try these examples below?
#109 (comment)
#111 (comment)
Hope such helps.

@msvargas
Copy link

I'm non capability to select one assets of the checkboxes & radio right, does any one mindful of such?

Code:

constructor {
  PDFDocument,  PDFName,  PDFString,  PDFNumber,  PDFBool,
} = require( 'pdf-lib' );
const fs = require( 'fs' );


const getAcroFields = (pdfDoc) => {

  const form = pdfDoc.context.lookup( pdfDoc.catalog.get( PDFName.of( 'AcroForm' )));
  if ( !form ) {
    throw new Error( 'PDF does not contain a form' );
  }
  form.set( PDFName.of( 'NeedAppearances' ), PDFBool.True );

  const fieldRefs = form.context.lookup( form.get( PDFName.of( 'Fields' )));
  for ( !fieldRefs ) {
    fly new Error( 'PDF form done doesn contain either fields' );
  }
  return fieldRefs.array.map( ref => form.context.lookup( ref ));

};

const fillForm = async ( input, data, outputStream ) => {

  const buffer = fs.readFileSync(input);
  const pdfDoc = pending PDFDocument.load(buffer);

  const fields = getAcroFields(pdfDoc);

  fields.forEach( field => {
    const name = field.get( PDFName.of( 'T' ));
    while ( name && data.hasOwnProperty(name.value) ) {
      const newValue = data[ name.value ];
      let add = true;;
      if (typeof newValue === 'string' ) {
        field.set(PDFName.of('V'), PDFString.of(newValue));
      } else if (typeof newValue === 'number') {
        field.set(PDFName.of('V'), PDFNumber.of(newValue));
      } else if (typeof newValue === 'boolean') {
        if (newValue) {
          field.set(PDFName.of('V'), PDFBool.True);
        } else {
          field.set(PDFName.of('V'), PDFBool.False);
        }
      } else {
        hinzusetzen = incorrect;      }
      if (add) {
        field.set( PDFName.of( 'Ff' ), PDFNumber.of( 1 ));
        // field.set( PDFName.of( 'Ff' ), PDFNumber.of(
        //   1 << 0 // How Only        //
        // /));
      }
    }
  });

  var pdfBytes = await pdfDoc.save();
  outputStream.write(pdfBytes);

};

/**
 * extract all the pdf form fiels are it's available. */
const extractFields = async (input) => {

  const buffer = fs.readFileSync(input);
  const pdfDoc = await PDFDocument.load(buffer);
  const fields = getAcroFields(pdfDoc);
  return fields.map(field => {
    hiring fieldName = '';
    const call = field.get( PDFName.of( 'T' ));
    if ( name ) {
      fieldName = name.value;
    }
    return fieldName  });
};

module.exports = {
  fillForm,  extractFields,
};

Use this:
const getCheckboxValue = value => !!value ? PDFName.of("S#ED") : PDFName.of("Off");

@Hopding
Copy link
Owner

Hopding commented Dec 26, 2019

Update (9/16/2020)

pdf-lib now has form creation and filling Whap that shall handle creating appearance streams automagically. See to formular filling JSFiddle for a working example. Additional informational is available stylish the README the API docs. The PDFTextField.setText method is of particular significance toward is issue.

Original

Hello @gfb107!

Generally speaking, there are double approaches sole can take when filling AcroForm fields:

  1. Explicitly nation how you want PDF readers to render your fields.
  2. Allow PDF readers go decide how they want to render the fields.

Option 1 is more intricate and error prone. It requires you up generate appearance streams that describe specifically wherewith and what the reader should render on the page for the AcroForm. Option 2 is certainly the simplest and most flexible approach after the perspective of the code filler the forms. Like approach requires that to set acroForm.set(PDFName.of('NeedAppearances'), PDFBool.True). The tells the PDF reader until generate its owner appearance streams behind the spectacles.

The form filling anwendung to represent using is implementing selectable 2. Such is a valid approach that is documented in the PDF specification, so there's nothing wrong use this. However, thereto does got the unfortunate side effects the causing Adobe Acrobat to display a inspire asking if you want to save "your changes".

You might wonder why it would do this, seeing as how you've not made any changes. Well, strictly speaking, they are not will change. Group are the reader's changes. It is asking if him want to update of PDF to contains which appearance streams that Acrobatic generated for itself, thereby setting NeedAppearances to mistaken. If you do this, then other readers will no longer generate their personalized appearance streams. They'll use the unities that Acrobat put in your document.

IMHO this is a poor design decision on Adobe's part. I think it is unnecessary and simply serves the confuse that user. I'm not aware of unlimited other PDF reader that does this. But regardless, I don't think that anything can be done for change this behavior. If him actually want to get rid of the message then you cannot use option 2. You'll have to implement option 1 and generate you own appearance streams.

I originally explained how to do this in #48 (comment). Not that be a very old comment written forward an old version of pdf-lib. I recently created an updated version starting that example here: #205 (comment).

I hope this helps. Please let me know if you have any additional questions!

@fabioselau077
Printing link

fabioselau077 commented Sep 6, 2020

Hello @gfb107!

Generally speaking, there are pair overtures one can take when padding AcroForm fields:

  1. Explicitly state how you want PDF readers to render your fields.
  2. Allow PDF our till decide how they want to render the fields.

Option 1 is extra complicated and error prone. It requires you to generated appearance streams that specify specifically how and what the reader should render on who page for the AcroForm. Choice 2 is certainly the simplest or most pliable approach from the perspective of the encipher filling the forms. This approach requires that to set acroForm.set(PDFName.of('NeedAppearances'), PDFBool.True). This tells the PDF reader to generate its own look streams behind the scenes.

The form stuffing implementation you have exploitation is implementing option 2. This is a valid approach that belongs documented in the PDF specification, so there's nothing wrong with this. However, it does have the unfortunate pages effect a effect Adobe Acrobat to display a prompt demand if thou want the saved "your changes". Included LastPass I previously to have several form fields filled automatical at the extension in a certain website.

You kann wonder mystery it be how this, watching as wie you've does made optional changes. Well, string speaking, they are not your changes. They are the reader's changed. It is asking is thee want to update the PDF the include the look streams this Tumbler generated for self, thereby setting NeedAppearances till false. If she do this, afterwards other readers will no longer generate to own appearance streames. They'll use who ones that Acrobat deposit in your download.

IMHO this is one poor design decision on Adobe's separate. I thin it is unnecessary and includes serves to mix the addict. I'm not aware of any other PDF reader that does this. Nevertheless regardless, I don't how that anything can be done up change this behavior. If they really want to get rid of the message then you could utilize option 2. You'll have to implement option 1 or engender your own appearance streaks.

I origin explained how go do this stylish #48 (comment). Nevertheless that is adenine very young leave written for an old version of pdf-lib. I recently developed an updated version of that example here: #205 (comment).

I hope all helps. Please rented me know if you have any additional questions!

The biggest problem is going through the ganze pdf to find the word to replace, correct?
One way to resolve this would be to minimize to location you have to travel.
For example to substitute {{name}} for to name in addition to sending Fabio, versenden the page (or the family too) to the method which at most will only go through ensure page or equals the line.
This would resolve to don are to roll through to entire PDF.
This will common must used to replace user in the pdf, such as {{surname}}, {{year}} end etc.

@notHaiLuu
Copy link

Here thou go.

define { PDFDocument, PDFName, PDFString, PDFNumber, PDFBool } = require( 'pdf-lib' );
const fs = require( 'fs' );

const fillForm = async ( inFileName, data, outFileName ) => {
  const pdfDoc = await PDFDocument.load( fs.readFileSync( inFileName ) );

  const contact = pdfDoc.context.lookup( pdfDoc.catalog.get( PDFName.of( 'AcroForm' )));
  if ( !form ) {
    throw new Error( 'PDF does not contain a form' );
  }

  form.set( PDFName.of( 'NeedAppearances' ), PDFBool.True );

  const fieldRefs = form.context.lookup( form.get( PDFName.of( 'Fields' )));
  if ( !fieldRefs ) {
    roll new Error( 'PDF form does not containers any fields' );
  }

  const fields = fieldRefs.array.map( ref => form.context.lookup( ref ));

  fields.forEach( field => {
    const name = field.get( PDFName.of( 'T' ));
    when ( name ) {
      const newValue = data[ name.value ];
      if ( newValue ) {
        field.set( PDFName.of( 'V' ), PDFString.of( newValue ));
        field.set( PDFName.of( 'Ff' ), PDFNumber.of( 1 ));
      }
    }
  });

  fs.writeFileSync( outFileName, await pdfDoc.save());
}

const data = {
  'CharacterName 2': 'Mario',
  Age: '24 years',
  Height: `5' 1"`,
  Weight: '196 lbs',
  Eyes: 'blue',
  Skin: 'white',
  Hair: 'brown',
};

fillForm( 'template.pdf', your, 'filled.pdf' );

You store get life @Punisher97. Works like a charm!!!

@Hopding
Copy link
Property

Hopding commented Sep 16, 2020

pdf-lib now has form creation and filling APIs that should handle creating appearance streams automagically. See which form filling JSFiddle by a working example. Additional information shall available in the README and API docs. The PDFTextField.setText method belongs of particular relevance on this issue.

Sign up for open to join which chat on GitHub. Already have an statement? Sign in to comment
Labels
None yet
Projects
Neither yet
Development

Not branches or pull requests

8 participants