Blog Post

Creating and Filling PDF Forms Programmatically in JavaScript

Illustration: 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

  1. Create a new project directory with the following:

mkdir CreatingPdfForms
Information

PSPDFKit for Web can be integrated into a new project or an already existing project.

  1. Change into the project directory and add PSPDFKit as a dependency:

cd CreatingPdfForms
npm install pspdfkit
  1. 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/
Information

Make sure your assets directory contains the pspdfkit.js file and a pspdfkit-lib directory with the library assets.

  1. 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.

image showing a screenshot of the pdf and its contents

  1. Next, create the index.html and index.js files. Then, open the project directory in your favorite code editor:

touch index.html index.js

image showing the file structure in VS code

Information

Your file structure should look like what’s shown above.

  1. Import pspdfkit into your application and initialize PSPDFKit for Web in JavaScript by calling PSPDFKit.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);
    	});
Information

The code above should be the contents of your index.js file.

  1. In your index.html file, add an empty <div> element with a defined width and height to where PSPDFKit will be mounted:

    <div id="pspdfkit" style="width: 100%; height: 100vh;"></div>
  2. Then, import index.js into your HTML page. Your index.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>
  3. Use the serve package as a simple HTTP server:

    npm install serve
  4. Finally, serve your app with the following:

    npx serve -l 8080 .

gif showing the result of ‘npx serve -l 8080’ displaying the pdf.

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 .

gif showing the result of ‘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.

gif showing the result of ‘instance.exportPDF()’

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

image showing the result of ‘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 .

gif showing the result of ‘npx serve -l 8080 JSON instance.’

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 .

image showing the result of ‘npx serve -l 8080 XFDF.’

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 .

gif showing the result of ‘npx serve -l 8080  filling forms database.’

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.

Explore related topics

Related products
Share post
Free trial Ready to get started?
Free trial