Creating and Filling PDF Forms Programmatically in JavaScript
In this post, you’ll learn how to generate a PDF form and populate its fields with values using PSPDFKit’s JavaScript PDF fillable form library.
PSPDFKit for Web offers multiple options for filling PDF form fields programmatically. Here are a few options this post will explore:
-
Using Instant JSON
-
Using XFDF
-
Importing data into PDFs from a database
PSPDFKit for Web
We at PSPDFKit work on the next generation of PDF Web SDKs. We offer a commercial JavaScript PDF viewer library that can easily be integrated into your web application. It comes with 30+ features that let you view, annotate, edit, and sign documents directly in your browser.
- A prebuilt and polished UI
- 15+ annotation tools
- Support for multiple file types
- Dedicated support from engineers
Steps to Create and Fill PDF Forms in JavaScript
Presenting a PDF Document in Your Browser
-
Create a new project directory with the following:
mkdir CreatingPdfForms
PSPDFKit for Web can be integrated into a new project or an already existing project.
-
Change into the project directory and add PSPDFKit as a dependency:
cd CreatingPdfForms npm install pspdfkit
-
Make a new directory called
assets
, and then copy the PSPDFKit for Web distribution into it:
mkdir assets cp -R ./node_modules/pspdfkit/dist/ ./assets/
Make sure your
assets
directory contains thepspdfkit.js
file and apspdfkit-lib
directory with the library assets.
-
Rename the PDF document you want to display in your application to
document.pdf
, and then add the PDF document to your project’s root directory. Below is a screenshot of the PDF this tutorial will use.
-
Next, create the
index.html
andindex.js
files. Then, open the project directory in your favorite code editor:
touch index.html index.js
Your file structure should look like what’s shown above.
-
Import
pspdfkit
into your application and initialize PSPDFKit for Web in JavaScript by callingPSPDFKit.load()
:import './assets/pspdfkit.js'; // We need to inform PSPDFKit where to look for its library assets, i.e. the location of the `pspdfkit-lib` directory. const baseUrl = `${window.location.protocol}//${window.location.host}/assets/`; PSPDFKit.load({ baseUrl, container: '#pspdfkit', document: 'document.pdf', }) .then((instance) => { console.log('PSPDFKit loaded', instance); }) .catch((error) => { console.error(error.message); });
The code above should be the contents of your
index.js
file.
-
In your
index.html
file, add an empty<div>
element with a definedwidth
andheight
to where PSPDFKit will be mounted:<div id="pspdfkit" style="width: 100%; height: 100vh;"></div>
-
Then, import
index.js
into your HTML page. Yourindex.html
will look like this:<!DOCTYPE html> <html> <head> <title>My App</title> <!-- Provide proper viewport information so that the layout works on mobile devices. --> <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" /> </head> <body> <!-- Element where PSPDFKit will be mounted. --> <div id="pspdfkit" style="width: 100%; height: 100vh;"></div> <script type="module" src="index.js"></script> </body> </html>
-
Use the
serve
package as a simple HTTP server:npm install serve
-
Finally, serve your app with the following:
npx serve -l 8080 .
Creating Form Fields in the PDF Document
To create any form field, first create the widget annotation and then the form field itself. The widget annotation dictates the position and dimensions of a form element:
const firstNameWidget = new PSPDFKit.Annotations.WidgetAnnotation({ id: PSPDFKit.generateInstantId(), pageIndex: 0, formFieldName: 'First Name', boundingBox: new PSPDFKit.Geometry.Rect({ left: 115, top: 98, width: 200, height: 25, }), });
The formFieldName
property on the widget annotation links to the name
property of the corresponding form field:
const firstNameFormField = new PSPDFKit.FormFields.TextFormField({ name: 'First Name', annotationIds: new PSPDFKit.Immutable.List([firstNameWidget.id]), });
Likewise, the annotationIds
property of the form field needs to be properly set with a PSPDFKit.Immutable.List
of widget annotations linked to it. Then, you can create the form field with the following:
await instance.create([firstNameWidget, firstNameFormField]);
Similarly, a signature form field can be created with this:
const signatureWidget = new PSPDFKit.Annotations.WidgetAnnotation({ id: PSPDFKit.generateInstantId(), pageIndex: 0, boundingBox: new PSPDFKit.Geometry.Rect({ left: 43, top: 325, width: 150, height: 75, }), formFieldName: 'Signature', }); const signatureFormField = new PSPDFKit.FormFields.SignatureFormField({ name: 'Signature', annotationIds: new PSPDFKit.Immutable.List([signatureWidget.id]), }); await instance.create([signatureWidget, signatureFormField]);
However, PSPDFKit.FormFields.RadioButtonFormField
and PSPDFKit.FormFields.CheckBoxFormField
may use multiple widget annotations to represent different form field values:
// Create a new radio button form field. const yesRadioWidget = new PSPDFKit.Annotations.WidgetAnnotation({ id: PSPDFKit.generateInstantId(), pageIndex: 0, formFieldName: 'Human', boundingBox: new PSPDFKit.Geometry.Rect({ left: 80, top: 188, width: 20, height: 20, }), }); const noRadioWidget = new PSPDFKit.Annotations.WidgetAnnotation({ id: PSPDFKit.generateInstantId(), pageIndex: 0, formFieldName: 'Human', boundingBox: new PSPDFKit.Geometry.Rect({ left: 80, top: 214, width: 20, height: 20, }), }); const maybeRadioWidget = new PSPDFKit.Annotations.WidgetAnnotation({ id: PSPDFKit.generateInstantId(), pageIndex: 0, formFieldName: 'Human', boundingBox: new PSPDFKit.Geometry.Rect({ left: 80, top: 240, width: 20, height: 20, }), }); const radioFormField = new PSPDFKit.FormFields.RadioButtonFormField({ name: 'Human', annotationIds: new PSPDFKit.Immutable.List([ yesRadioWidget.id, noRadioWidget.id, maybeRadioWidget.id, ]), options: new PSPDFKit.Immutable.List([ new PSPDFKit.FormOption({ label: 'Yes', value: '1', }), new PSPDFKit.FormOption({ label: 'No', value: '2', }), new PSPDFKit.FormOption({ label: 'Maybe', value: '3', }), ]), defaultValue: 'Maybe', }); // Create a new checkbox form field. const checkBoxWidget = new PSPDFKit.Annotations.WidgetAnnotation({ id: PSPDFKit.generateInstantId(), pageIndex: 0, formFieldName: 'Fun', boundingBox: new PSPDFKit.Geometry.Rect({ left: 128, top: 269.5, width: 20, height: 20, }), }); const checkBoxFormField = new PSPDFKit.FormFields.CheckBoxFormField({ name: 'Fun', annotationIds: new PSPDFKit.Immutable.List([checkBoxWidget.id]), options: new PSPDFKit.Immutable.List([ new PSPDFKit.FormOption({ label: 'FunCheck', value: '1', }), ]), }); await instance.create([ yesRadioWidget, noRadioWidget, maybeRadioWidget, radioFormField, checkBoxWidget, checkBoxFormField, ]);
After putting it all together, you’ll have the following in your index.js
file:
import './assets/pspdfkit.js'; // We need to inform PSPDFKit where to look for its library assets, i.e. the location of the `pspdfkit-lib` directory. const baseUrl = `${window.location.protocol}//${window.location.host}/assets/`; PSPDFKit.load({ baseUrl, container: '#pspdfkit', document: 'document.pdf', }) .then(async (instance) => { console.log('PSPDFKit loaded', instance); // Creating the first name text form field. const firstNameWidget = new PSPDFKit.Annotations.WidgetAnnotation( { id: PSPDFKit.generateInstantId(), pageIndex: 0, formFieldName: 'First Name', boundingBox: new PSPDFKit.Geometry.Rect({ left: 115, top: 98, width: 200, height: 25, }), }, ); const firstNameFormField = new PSPDFKit.FormFields.TextFormField({ name: 'First Name', annotationIds: new PSPDFKit.Immutable.List([ firstNameWidget.id, ]), }); // Creating the last name text form field. const lastNameWidget = new PSPDFKit.Annotations.WidgetAnnotation({ id: PSPDFKit.generateInstantId(), pageIndex: 0, formFieldName: 'Last Name', boundingBox: new PSPDFKit.Geometry.Rect({ left: 115, top: 128, width: 200, height: 25, }), }); const lastNameFormField = new PSPDFKit.FormFields.TextFormField({ name: 'Last Name', annotationIds: new PSPDFKit.Immutable.List([ lastNameWidget.id, ]), }); // Creating a new radio button form field. const yesRadioWidget = new PSPDFKit.Annotations.WidgetAnnotation({ id: PSPDFKit.generateInstantId(), pageIndex: 0, formFieldName: 'Human', boundingBox: new PSPDFKit.Geometry.Rect({ left: 80, top: 188, width: 20, height: 20, }), }); const noRadioWidget = new PSPDFKit.Annotations.WidgetAnnotation({ id: PSPDFKit.generateInstantId(), pageIndex: 0, formFieldName: 'Human', boundingBox: new PSPDFKit.Geometry.Rect({ left: 80, top: 214, width: 20, height: 20, }), }); const maybeRadioWidget = new PSPDFKit.Annotations.WidgetAnnotation( { id: PSPDFKit.generateInstantId(), pageIndex: 0, formFieldName: 'Human', boundingBox: new PSPDFKit.Geometry.Rect({ left: 80, top: 240, width: 20, height: 20, }), }, ); const radioFormField = new PSPDFKit.FormFields.RadioButtonFormField( { name: 'Human', annotationIds: new PSPDFKit.Immutable.List([ yesRadioWidget.id, noRadioWidget.id, maybeRadioWidget.id, ]), options: new PSPDFKit.Immutable.List([ new PSPDFKit.FormOption({ label: 'Yes', value: '1', }), new PSPDFKit.FormOption({ label: 'No', value: '2', }), new PSPDFKit.FormOption({ label: 'Maybe', value: '3', }), ]), defaultValue: 'Maybe', }, ); // Creating a new checkbox form field. const checkBoxWidget = new PSPDFKit.Annotations.WidgetAnnotation({ id: PSPDFKit.generateInstantId(), pageIndex: 0, formFieldName: 'Fun', boundingBox: new PSPDFKit.Geometry.Rect({ left: 128, top: 269.5, width: 20, height: 20, }), }); const checkBoxFormField = new PSPDFKit.FormFields.CheckBoxFormField( { name: 'Fun', annotationIds: new PSPDFKit.Immutable.List([ checkBoxWidget.id, ]), options: new PSPDFKit.Immutable.List([ new PSPDFKit.FormOption({ label: 'FunCheck', value: '1', }), ]), }, ); // Creating a new signature form field. const signatureWidget = new PSPDFKit.Annotations.WidgetAnnotation( { id: PSPDFKit.generateInstantId(), pageIndex: 0, boundingBox: new PSPDFKit.Geometry.Rect({ left: 43, top: 325, width: 150, height: 75, }), formFieldName: 'Signature', }, ); const signatureFormField = new PSPDFKit.FormFields.SignatureFormField( { name: 'Signature', annotationIds: new PSPDFKit.Immutable.List([ signatureWidget.id, ]), }, ); await instance.create([ firstNameWidget, firstNameFormField, lastNameWidget, lastNameFormField, yesRadioWidget, noRadioWidget, maybeRadioWidget, radioFormField, checkBoxWidget, checkBoxFormField, signatureWidget, signatureFormField, ]); }) .catch((error) => { console.error(error.message); });
Serve your app with the following:
npx serve -l 8080 .
Now, export the resulting document and use it in the next section to fill out the form fields. To do that, add the following after instance.create();
:
instance.exportPDF().then((buffer) => { const blob = new Blob([buffer], { type: 'application/pdf' }); const fileName = 'document.pdf'; if (window.navigator.msSaveOrOpenBlob) { window.navigator.msSaveOrOpenBlob(blob, fileName); } else { const objectUrl = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = objectUrl; a.style = 'display: none'; a.download = fileName; document.body.appendChild(a); a.click(); URL.revokeObjectURL(objectUrl); document.body.removeChild(a); } });
Refresh your app in the browser to get a copy of the document with the form fields.
Populating the Form Fields Using Instant JSON
Now you’ll create a fresh project and present the document you exported in the previous section. Follow the instructions in the Presenting a PDF Document in Your Browser section and use either the document from the previous section, or any PDF document with form fields.
Before you proceed, it’s helpful to have an overview of the form fields present in a document. This can be achieved with the instance.getFormFieldValues()
method:
const formFieldValues = instance.getFormFieldValues();
console.log(formFieldValues);
Here, the keys refer to the name
property of the form field, and the values are either null
, string
, or Array<string>
, depending upon the type of the FormField
.
Fill in the form fields above using the following instance JSON:
instantJSON: { format: "https://pspdfkit.com/instant-json/v1", formFieldValues: [ { name: "First Name", value: "John", type: "pspdfkit/form-field-value", v: 1, }, { name: "Last Name", value: "Appleseed", type: "pspdfkit/form-field-value", v: 1, }, { name: "Human", value: "3", type: "pspdfkit/form-field-value", v: 1, }, { name: "Fun", value: "1", type: "pspdfkit/form-field-value", v: 1, }, ], }
Note that signature form fields are an exception. To add an ink signature to your signature form field, you’ll use the PSPDFKit.Annotations.InkAnnotation()
API:
// The name of the signature field you want. // const formFieldName = 'Signature'; // First, get all `FormFields` in the `Document`. // const formFields = await instance.getFormFields(); // Get a signature form with the specific name you want. // const field = formFields.find( (formField) => formField.name === formFieldName && formField instanceof PSPDFKit.FormFields.SignatureFormField, ); // In this example, assume the widget you need is on the first page. // const annotations = await instance.getAnnotations(0); // Find that widget. // const widget = annotations.find( (annotation) => annotation instanceof PSPDFKit.Annotations.WidgetAnnotation && annotation.formFieldName === field.name, ); // Make a new ink annotation. // const annotation = new PSPDFKit.Annotations.InkAnnotation({ pageIndex: 0, lines: PSPDFKit.Immutable.List([ PSPDFKit.Immutable.List([ new PSPDFKit.Geometry.DrawingPoint({ x: widget.boundingBox.left + 10, y: widget.boundingBox.top + 10, }), new PSPDFKit.Geometry.DrawingPoint({ x: widget.boundingBox.left + widget.boundingBox.width - 10, y: widget.boundingBox.top + widget.boundingBox.height - 10, }), ]), PSPDFKit.Immutable.List([ new PSPDFKit.Geometry.DrawingPoint({ x: widget.boundingBox.left + widget.boundingBox.width - 10, y: widget.boundingBox.top + 10, }), new PSPDFKit.Geometry.DrawingPoint({ x: widget.boundingBox.left + 10, y: widget.boundingBox.top + widget.boundingBox.height - 10, }), ]), ]), boundingBox: widget.boundingBox, isSignature: true, }); instance.create(annotation);
Putting it all together, your index.js
file will look like this:
import './assets/pspdfkit.js'; // We need to inform PSPDFKit where to look for its library assets, i.e. the location of the `pspdfkit-lib` directory. const baseUrl = `${window.location.protocol}//${window.location.host}/assets/`; PSPDFKit.load({ baseUrl, container: '#pspdfkit', document: 'document.pdf', instantJSON: { format: 'https://pspdfkit.com/instant-json/v1', formFieldValues: [ { name: 'First Name', value: 'John', type: 'pspdfkit/form-field-value', v: 1, }, { name: 'Last Name', value: 'Appleseed', type: 'pspdfkit/form-field-value', v: 1, }, { name: 'Human', value: '3', type: 'pspdfkit/form-field-value', v: 1, }, { name: 'Fun', value: '1', type: 'pspdfkit/form-field-value', v: 1, }, ], }, }) .then(async (instance) => { console.log('PSPDFKit loaded', instance); // The name of the signature field you want. // const formFieldName = 'Signature'; // First get all `FormFields` in the `Document`. // const formFields = await instance.getFormFields(); // Get a signature form with the specific name you want. // const field = formFields.find( (formField) => formField.name === formFieldName && formField instanceof PSPDFKit.FormFields.SignatureFormField, ); // In this example, assume the widget you need is on the first page. // const annotations = await instance.getAnnotations(0); // Find that widget. // const widget = annotations.find( (annotation) => annotation instanceof PSPDFKit.Annotations.WidgetAnnotation && annotation.formFieldName === field.name, ); // Make a new ink annotation. // const annotation = new PSPDFKit.Annotations.InkAnnotation({ pageIndex: 0, lines: PSPDFKit.Immutable.List([ PSPDFKit.Immutable.List([ new PSPDFKit.Geometry.DrawingPoint({ x: widget.boundingBox.left + 10, y: widget.boundingBox.top + 10, }), new PSPDFKit.Geometry.DrawingPoint({ x: widget.boundingBox.left + widget.boundingBox.width - 10, y: widget.boundingBox.top + widget.boundingBox.height - 10, }), ]), PSPDFKit.Immutable.List([ new PSPDFKit.Geometry.DrawingPoint({ x: widget.boundingBox.left + widget.boundingBox.width - 10, y: widget.boundingBox.top + 10, }), new PSPDFKit.Geometry.DrawingPoint({ x: widget.boundingBox.left + 10, y: widget.boundingBox.top + widget.boundingBox.height - 10, }), ]), ]), boundingBox: widget.boundingBox, isSignature: true, }); instance.create(annotation); }) .catch((error) => { console.error(error.message); });
Serve your app with the following:
npx serve -l 8080 .
Populating the Form Fields Using XFDF
Use the following XML to set form field values in a document by importing them as XFDF:
import './assets/pspdfkit.js'; // We need to inform PSPDFKit where to look for its library assets, i.e. the location of the `pspdfkit-lib` directory. const baseUrl = `${window.location.protocol}//${window.location.host}/assets/`; const XFDF = `<?xml version="1.0" encoding="UTF-8"?> <xfdf xml:space="preserve" xmlns="http://ns.adobe.com/xfdf/"> <annots></annots> <fields> <field name="First Name"> <value>John</value> </field> <field name="Last Name"> <value>Appleseed</value> </field> <field name="Human"> <value>3</value> </field> <field name="Fun"> <value>1</value> </field> </fields> </xfdf> `; PSPDFKit.load({ baseUrl, container: '#pspdfkit', document: 'document.pdf', XFDF, });
Serve your app with the following:
npx serve -l 8080 .
Populating the Form Fields from a Database
Assume you have a /user
endpoint that returns the user’s data in a JSON format, like this:
{ "firstName": "John", "lastName": "Appleseed", "human": "3", "fun": "1" }
You can insert the form field values into the Instant JSON object and import it when the document is opened:
import './assets/pspdfkit.js'; // We need to inform PSPDFKit where to look for its library assets, i.e. the location of the `pspdfkit-lib` directory. const baseUrl = `${window.location.protocol}//${window.location.host}/assets/`; async function fetchData() { // This loads the profile information from your server which, in turn, imports it from the database. const userResponse = await fetch('https://[YOUR-SERVER]/user'); const { firstName, lastName, human, fun, } = await userResponse.json(); // Insert the form field values into Instant JSON. return { format: 'https://pspdfkit.com/instant-json/v1', formFieldValues: [ { v: 1, type: 'pspdfkit/form-field-value', name: 'First Name', value: firstName, }, { v: 1, type: 'pspdfkit/form-field-value', name: 'Last Name', value: lastName, }, { v: 1, type: 'pspdfkit/form-field-value', name: 'Human', value: human, }, { v: 1, type: 'pspdfkit/form-field-value', name: 'Fun', value: fun, }, ], }; } const instantJSON = await fetchData(); PSPDFKit.load({ baseUrl, container: '#pspdfkit', document: 'document.pdf', instantJSON, });
Serve your app with the following:
npx serve -l 8080 .
Conclusion
In this post, you learned how to create and fill PDF forms using JavaScript and PSPDFKit for Web. In case of any issues, don’t hesitate to reach out to our Support team for help.
To learn more about our SDK, you can explore our documentation. Or, launch our demo to see our JavaScript PDF fillable forms library in action.