Create and Modify
Create and Modify
Create PDF documents from scratch, or modify existing PDF documents. Draw text, images, and vector graphics. Embed your own fonts. Even embed and draw pages from other PDFs.
Pure JavaScript
Pure JavaScript
Written in TypeScript and compiled to pure JavaScript with no native dependencies. Works in any JavaScript runtime, including browsers, Node, Deno, and even React Native.
Split and Merge
Split and Merge
Add, insert, and remove pages. Split a single PDF into separate ones. Or merge multiple PDFs into a single document.
Fill Forms
Fill Forms
Create new forms or fill and read existing fields. Check boxes, buttons, radio groups, dropdowns, option lists, and text fields are all supported.
<html>
<head>
<meta charset="utf-8" />
<script src="https://unpkg.com/pdf-lib"></script>
</head>
<body>
<iframe id="pdf" style="width: 100%; height: 100%;"></iframe>
</body>
<script>
createPdf();
async function createPdf() {
const pdfDoc = await PDFLib.PDFDocument.create();
const page = pdfDoc.addPage([350, 400]);
page.moveTo(110, 200);
page.drawText('Hello World!');
const pdfDataUri = await pdfDoc.saveAsBase64({ dataUri: true });
document.getElementById('pdf').src = pdfDataUri;
}
</script>
</html>
$ deno run --allow-write https://pdf-lib.js.org/deno/quick_start.ts
If you are using npm or yarn as your package manager:
# With npm
npm install --save pdf-lib
# With yarn
yarn add pdf-lib
If you aren't using a package manager, UMD modules are available on the unpkg and jsDelivr CDNs:
- https://unpkg.com/pdf-lib/dist/pdf-lib.js
- https://unpkg.com/pdf-lib/dist/pdf-lib.min.js
- https://cdn.jsdelivr.net/npm/pdf-lib/dist/pdf-lib.js
- https://cdn.jsdelivr.net/npm/pdf-lib/dist/pdf-lib.min.js
NOTE: if you are using the CDN scripts in production, you should include a specific version number in the URL, for example:
import { PDFDocument, StandardFonts, rgb } from 'pdf-lib'
async function createPdf() {
const pdfDoc = await PDFDocument.create()
const timesRomanFont = await pdfDoc.embedFont(StandardFonts.TimesRoman)
const page = pdfDoc.addPage()
const { width, height } = page.getSize()
const fontSize = 30
page.drawText('Creating PDFs in JavaScript is awesome!', {
x: 50,
y: height - 4 * fontSize,
size: fontSize,
font: timesRomanFont,
color: rgb(0, 0.53, 0.71),
})
const pdfBytes = await pdfDoc.save()
}
import { degrees, PDFDocument, rgb, StandardFonts } from 'pdf-lib';
async function modifyPdf() {
const url = 'https://pdf-lib.js.org/assets/with_update_sections.pdf'
const existingPdfBytes = await fetch(url).then(res => res.arrayBuffer())
const pdfDoc = await PDFDocument.load(existingPdfBytes)
const helveticaFont = await pdfDoc.embedFont(StandardFonts.Helvetica)
const pages = pdfDoc.getPages()
const firstPage = pages[0]
const { width, height } = firstPage.getSize()
firstPage.drawText('This text was added with JavaScript!', {
x: 5,
y: height / 2 + 300,
size: 50,
font: helveticaFont,
color: rgb(0.95, 0.1, 0.1),
rotate: degrees(-45),
})
const pdfBytes = await pdfDoc.save()
}
import { PDFDocument } from 'pdf-lib'
async function createForm() {
const pdfDoc = await PDFDocument.create()
const page = pdfDoc.addPage([550, 750])
const form = pdfDoc.getForm()
page.drawText('Enter your favorite superhero:', { x: 50, y: 700, size: 20 })
const superheroField = form.createTextField('favorite.superhero')
superheroField.setText('One Punch Man')
superheroField.addToPage(page, { x: 55, y: 640 })
page.drawText('Select your favorite rocket:', { x: 50, y: 600, size: 20 })
page.drawText('Falcon Heavy', { x: 120, y: 560, size: 18 })
page.drawText('Saturn IV', { x: 120, y: 500, size: 18 })
page.drawText('Delta IV Heavy', { x: 340, y: 560, size: 18 })
page.drawText('Space Launch System', { x: 340, y: 500, size: 18 })
const rocketField = form.createRadioGroup('favorite.rocket')
rocketField.addOptionToPage('Falcon Heavy', page, { x: 55, y: 540 })
rocketField.addOptionToPage('Saturn IV', page, { x: 55, y: 480 })
rocketField.addOptionToPage('Delta IV Heavy', page, { x: 275, y: 540 })
rocketField.addOptionToPage('Space Launch System', page, { x: 275, y: 480 })
rocketField.select('Saturn IV')
page.drawText('Select your favorite gundams:', { x: 50, y: 440, size: 20 })
page.drawText('Exia', { x: 120, y: 400, size: 18 })
page.drawText('Kyrios', { x: 120, y: 340, size: 18 })
page.drawText('Virtue', { x: 340, y: 400, size: 18 })
page.drawText('Dynames', { x: 340, y: 340, size: 18 })
const exiaField = form.createCheckBox('gundam.exia')
const kyriosField = form.createCheckBox('gundam.kyrios')
const virtueField = form.createCheckBox('gundam.virtue')
const dynamesField = form.createCheckBox('gundam.dynames')
exiaField.addToPage(page, { x: 55, y: 380 })
kyriosField.addToPage(page, { x: 55, y: 320 })
virtueField.addToPage(page, { x: 275, y: 380 })
dynamesField.addToPage(page, { x: 275, y: 320 })
exiaField.check()
dynamesField.check()
page.drawText('Select your favorite planet*:', { x: 50, y: 280, size: 20 })
const planetsField = form.createDropdown('favorite.planet')
planetsField.addOptions(['Venus', 'Earth', 'Mars', 'Pluto'])
planetsField.select('Pluto')
planetsField.addToPage(page, { x: 55, y: 220 })
page.drawText('Select your favorite person:', { x: 50, y: 180, size: 18 })
const personField = form.createOptionList('favorite.person')
personField.addOptions([
'Julius Caesar',
'Ada Lovelace',
'Cleopatra',
'Aaron Burr',
'Mark Antony',
])
personField.select('Ada Lovelace')
personField.addToPage(page, { x: 55, y: 70 })
page.drawText(`* Pluto should be a planet too!`, { x: 15, y: 15, size: 15 })
const pdfBytes = await pdfDoc.save()
}
import { PDFDocument } from 'pdf-lib'
async function fillForm() {
const formUrl = 'https://pdf-lib.js.org/assets/dod_character.pdf'
const formPdfBytes = await fetch(formUrl).then(res => res.arrayBuffer())
const marioUrl = 'https://pdf-lib.js.org/assets/small_mario.png'
const marioImageBytes = await fetch(marioUrl).then(res => res.arrayBuffer())
const emblemUrl = 'https://pdf-lib.js.org/assets/mario_emblem.png'
const emblemImageBytes = await fetch(emblemUrl).then(res => res.arrayBuffer())
const pdfDoc = await PDFDocument.load(formPdfBytes)
const marioImage = await pdfDoc.embedPng(marioImageBytes)
const emblemImage = await pdfDoc.embedPng(emblemImageBytes)
const form = pdfDoc.getForm()
const nameField = form.getTextField('CharacterName 2')
const ageField = form.getTextField('Age')
const heightField = form.getTextField('Height')
const weightField = form.getTextField('Weight')
const eyesField = form.getTextField('Eyes')
const skinField = form.getTextField('Skin')
const hairField = form.getTextField('Hair')
const alliesField = form.getTextField('Allies')
const factionField = form.getTextField('FactionName')
const backstoryField = form.getTextField('Backstory')
const traitsField = form.getTextField('Feat+Traits')
const treasureField = form.getTextField('Treasure')
const characterImageField = form.getButton('CHARACTER IMAGE')
const factionImageField = form.getButton('Faction Symbol Image')
nameField.setText('Mario')
ageField.setText('24 years')
heightField.setText(`5' 1"`)
weightField.setText('196 lbs')
eyesField.setText('blue')
skinField.setText('white')
hairField.setText('brown')
characterImageField.setImage(marioImage)
alliesField.setText(
[
`Allies:`,
` β’ Princess Daisy`,
` β’ Princess Peach`,
` β’ Rosalina`,
` β’ Geno`,
` β’ Luigi`,
` β’ Donkey Kong`,
` β’ Yoshi`,
` β’ Diddy Kong`,
``,
`Organizations:`,
` β’ Italian Plumbers Association`,
].join('\n'),
)
factionField.setText(`Mario's Emblem`)
factionImageField.setImage(emblemImage)
backstoryField.setText(
[
`Mario is a fictional character in the Mario video game franchise, `,
`owned by Nintendo and created by Japanese video game designer Shigeru `,
`Miyamoto. Serving as the company's mascot and the eponymous `,
`protagonist of the series, Mario has appeared in over 200 video games `,
`since his creation. Depicted as a short, pudgy, Italian plumber who `,
`resides in the Mushroom Kingdom, his adventures generally center `,
`upon rescuing Princess Peach from the Koopa villain Bowser. His `,
`younger brother and sidekick is Luigi.`,
].join('\n'),
)
traitsField.setText(
[
`Mario can use three basic three power-ups:`,
` β’ the Super Mushroom, which causes Mario to grow larger`,
` β’ the Fire Flower, which allows Mario to throw fireballs`,
` β’ the Starman, which gives Mario temporary invincibility`,
].join('\n'),
)
treasureField.setText(['β’ Gold coins', 'β’ Treasure chests'].join('\n'))
const pdfBytes = await pdfDoc.save()
}
import { PDFDocument } from 'pdf-lib'
async function flattenForm() {
const formUrl = 'https://pdf-lib.js.org/assets/form_to_flatten.pdf'
const formPdfBytes = await fetch(formUrl).then(res => res.arrayBuffer())
const pdfDoc = await PDFDocument.load(formPdfBytes)
const form = pdfDoc.getForm()
form.getTextField('Text1').setText('Some Text');
form.getRadioGroup('Group2').select('Choice1');
form.getRadioGroup('Group3').select('Choice3');
form.getRadioGroup('Group4').select('Choice1');
form.getCheckBox('Check Box3').check();
form.getCheckBox('Check Box4').uncheck();
form.getDropdown('Dropdown7').select('Infinity');
form.getOptionList('List Box6').select('Honda');
form.flatten();
const pdfBytes = await pdfDoc.save()
}
import { PDFDocument } from 'pdf-lib'
async function copyPages() {
const url1 = 'https://pdf-lib.js.org/assets/with_update_sections.pdf'
const url2 = 'https://pdf-lib.js.org/assets/with_large_page_count.pdf'
const firstDonorPdfBytes = await fetch(url1).then(res => res.arrayBuffer())
const secondDonorPdfBytes = await fetch(url2).then(res => res.arrayBuffer())
const firstDonorPdfDoc = await PDFDocument.load(firstDonorPdfBytes)
const secondDonorPdfDoc = await PDFDocument.load(secondDonorPdfBytes)
const pdfDoc = await PDFDocument.create();
const [firstDonorPage] = await pdfDoc.copyPages(firstDonorPdfDoc, [0])
const [secondDonorPage] = await pdfDoc.copyPages(secondDonorPdfDoc, [742])
pdfDoc.addPage(firstDonorPage)
pdfDoc.insertPage(0, secondDonorPage)
const pdfBytes = await pdfDoc.save()
}
import { PDFDocument } from 'pdf-lib'
async function embedImages() {
const jpgUrl = 'https://pdf-lib.js.org/assets/cat_riding_unicorn.jpg'
const pngUrl = 'https://pdf-lib.js.org/assets/minions_banana_alpha.png'
const jpgImageBytes = await fetch(jpgUrl).then((res) => res.arrayBuffer())
const pngImageBytes = await fetch(pngUrl).then((res) => res.arrayBuffer())
const pdfDoc = await PDFDocument.create()
const jpgImage = await pdfDoc.embedJpg(jpgImageBytes)
const pngImage = await pdfDoc.embedPng(pngImageBytes)
const jpgDims = jpgImage.scale(0.5)
const pngDims = pngImage.scale(0.5)
const page = pdfDoc.addPage()
page.drawImage(jpgImage, {
x: page.getWidth() / 2 - jpgDims.width / 2,
y: page.getHeight() / 2 - jpgDims.height / 2 + 250,
width: jpgDims.width,
height: jpgDims.height,
})
page.drawImage(pngImage, {
x: page.getWidth() / 2 - pngDims.width / 2 + 75,
y: page.getHeight() / 2 - pngDims.height + 250,
width: pngDims.width,
height: pngDims.height,
})
const pdfBytes = await pdfDoc.save()
}
import { PDFDocument } from 'pdf-lib'
async function embedPdfPages() {
const flagUrl = 'https://pdf-lib.js.org/assets/american_flag.pdf';
const constitutionUrl = 'https://pdf-lib.js.org/assets/us_constitution.pdf';
const flagPdfBytes = await fetch(flagUrl).then((res) => res.arrayBuffer());
const constitutionPdfBytes = await fetch(constitutionUrl).then((res) =>
res.arrayBuffer(),
);
const pdfDoc = await PDFDocument.create();
const [americanFlag] = await pdfDoc.embedPdf(flagPdfBytes);
const usConstitutionPdf = await PDFDocument.load(constitutionPdfBytes);
const preamble = await pdfDoc.embedPage(usConstitutionPdf.getPages()[1], {
left: 55,
bottom: 485,
right: 300,
top: 575,
});
const americanFlagDims = americanFlag.scale(0.3);
const preambleDims = preamble.scale(2.25);
const page = pdfDoc.addPage();
page.drawPage(americanFlag, {
...americanFlagDims,
x: page.getWidth() / 2 - americanFlagDims.width / 2,
y: page.getHeight() - americanFlagDims.height - 150,
});
page.drawPage(preamble, {
...preambleDims,
x: page.getWidth() / 2 - preambleDims.width / 2,
y: page.getHeight() / 2 - preambleDims.height / 2 - 50,
});
const pdfBytes = await pdfDoc.save();
}
The@pdf-lib/fontkit
module must be installed to embed custom fonts.
import { PDFDocument, rgb } from 'pdf-lib'
import fontkit from '@pdf-lib/fontkit'
async function embedFontAndMeasureText() {
const url = 'https://pdf-lib.js.org/assets/ubuntu/Ubuntu-R.ttf'
const fontBytes = await fetch(url).then(res => res.arrayBuffer())
const pdfDoc = await PDFDocument.create()
pdfDoc.registerFontkit(fontkit)
const customFont = await pdfDoc.embedFont(fontBytes)
const page = pdfDoc.addPage()
const text = 'This is text in an embedded font!'
const textSize = 35
const textWidth = customFont.widthOfTextAtSize(text, textSize)
const textHeight = customFont.heightAtSize(textSize)
page.drawText(text, {
x: 40,
y: 450,
size: textSize,
font: customFont,
color: rgb(0, 0.53, 0.71),
})
page.drawRectangle({
x: 40,
y: 450,
width: textWidth,
height: textHeight,
borderColor: rgb(1, 0, 0),
borderWidth: 1.5,
})
const pdfBytes = await pdfDoc.save()
}
import { PDFDocument } from 'pdf-lib'
async function addAttachments() {
const jpgUrl = 'https://pdf-lib.js.org/assets/cat_riding_unicorn.jpg'
const pdfUrl = 'https://pdf-lib.js.org/assets/us_constitution.pdf';
const jpgAttachmentBytes = await fetch(jpgUrl).then(res => res.arrayBuffer())
const pdfAttachmentBytes = await fetch(pdfUrl).then(res => res.arrayBuffer())
const pdfDoc = await PDFDocument.create()
await pdfDoc.attach(jpgAttachmentBytes, 'cat_riding_unicorn.jpg', {
mimeType: 'image/jpeg',
description: 'Cool cat riding a unicorn! π¦ππΆοΈ',
creationDate: new Date('2019/12/01'),
modificationDate: new Date('2020/04/19'),
})
await pdfDoc.attach(pdfAttachmentBytes, 'us_constitution.pdf', {
mimeType: 'application/pdf',
description: 'Constitution of the United States πΊπΈπ¦
',
creationDate: new Date('1787/09/17'),
modificationDate: new Date('1992/05/07'),
})
const page = pdfDoc.addPage();
page.drawText('This PDF has two attachments', { x: 135, y: 415 })
const pdfBytes = await pdfDoc.save()
}
Note that only some PDF readers can view attachments. This includes Adobe Reader, Foxit Reader, and Firefox.
import { PDFDocument, StandardFonts } from 'pdf-lib'
async function setDocumentMetadata() {
const pdfDoc = await PDFDocument.create()
const timesRomanFont = await pdfDoc.embedFont(StandardFonts.TimesRoman)
const page = pdfDoc.addPage([500, 600])
page.setFont(timesRomanFont)
page.drawText('The Life of an Egg', { x: 60, y: 500, size: 50 })
page.drawText('An Epic Tale of Woe', { x: 125, y: 460, size: 25 })
// Note that these fields are visible in the "Document Properties" section of
// most PDF readers.
pdfDoc.setTitle('π₯ The Life of an Egg π³')
pdfDoc.setAuthor('Humpty Dumpty')
pdfDoc.setSubject('π An Epic Tale of Woe π')
pdfDoc.setKeywords(['eggs', 'wall', 'fall', 'king', 'horses', 'men'])
pdfDoc.setProducer('PDF App 9000 π€')
pdfDoc.setCreator('pdf-lib (https://github.com/Hopding/pdf-lib)')
pdfDoc.setCreationDate(new Date('2018-06-24T01:58:37.228Z'))
pdfDoc.setModificationDate(new Date('2019-12-21T07:00:11.000Z'))
const pdfBytes = await pdfDoc.save()
}
import { PDFDocument } from 'pdf-lib'
async function readDocumentMetadata() {
const url = 'https://pdf-lib.js.org/assets/with_cropbox.pdf'
const existingPdfBytes = await fetch(url).then(res => res.arrayBuffer())
const pdfDoc = await PDFDocument.load(existingPdfBytes, {
updateMetadata: false
})
console.log('Title:', pdfDoc.getTitle())
console.log('Author:', pdfDoc.getAuthor())
console.log('Subject:', pdfDoc.getSubject())
console.log('Creator:', pdfDoc.getCreator())
console.log('Keywords:', pdfDoc.getKeywords())
console.log('Producer:', pdfDoc.getProducer())
console.log('Creation Date:', pdfDoc.getCreationDate())
console.log('Modification Date:', pdfDoc.getModificationDate())
}
This script outputs the following:
Title: Microsoft Word - Basic Curriculum Vitae example.doc
Author: Administrator
Subject: undefined
Creator: PScript5.dll Version 5.2
Keywords: undefined
Producer: Acrobat Distiller 8.1.0 (Windows)
Creation Date: 2010-07-29T14:26:00.000Z
Modification Date: 2010-07-29T14:26:00.000Z
import { PDFDocument, rgb } from 'pdf-lib'
async function drawSvgPaths() {
const svgPath =
'M 0,20 L 100,160 Q 130,200 150,120 C 190,-40 200,200 300,150 L 400,90'
const pdfDoc = await PDFDocument.create()
const page = pdfDoc.addPage()
page.moveTo(100, page.getHeight() - 5)
page.moveDown(25)
page.drawSvgPath(svgPath)
page.moveDown(200)
page.drawSvgPath(svgPath, { borderColor: rgb(0, 1, 0), borderWidth: 5 })
page.moveDown(200)
page.drawSvgPath(svgPath, { color: rgb(1, 0, 0) })
page.moveDown(200)
page.drawSvgPath(svgPath, { scale: 0.5 })
const pdfBytes = await pdfDoc.save()
}