PDF-LIB
Create and modify PDF documents in any JavaScript environment
README
Create and modify PDF documents in any JavaScript environment.
Designed to work in any modern JavaScript runtime. Tested in Node, Browser, Deno, and React Native environments.
<img
src="https://img.shields.io/npm/v/pdf-lib.svg?style=flat-square"
alt="NPM Version"
/>
<img
src="https://img.shields.io/circleci/project/github/Hopding/pdf-lib/master.svg?style=flat-square&label=CircleCI"
alt="CircleCI Build Status"
/>
<img
src="https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square"
alt="Prettier Badge"
/>
<img
src="https://img.shields.io/static/v1?label=discord&message=pdf-lib&color=566fbb&style=flat-square"
alt="Discord Badge"
/>
Learn more at pdf-lib.js.org
Table of Contents
- Features
- License
Features
- Create new PDFs
- Modify existing PDFs
- Create forms
- Fill forms
- Flatten forms
- Add Pages
- Insert Pages
- Remove Pages
- Copy pages between PDFs
- Draw Text
- Draw Images
- Draw PDF Pages
- Draw Vector Graphics
- Draw SVG Paths
- Measure width and height of text
- Embed Fonts (supports UTF-8 and UTF-16 character sets)
- Set document metadata
- Read document metadata
- Set viewer preferences
- Read viewer preferences
- Add attachments
Motivation
pdf-lib was created to address the JavaScript ecosystem's lack of robust support for PDF manipulation (especially for PDF _modification_).
Two of pdf-lib's distinguishing features are:
1. Supporting modification (editing) of existing documents.
2. Working in all JavaScript environments - not just in Node or the Browser.
There are other good open source JavaScript PDF libraries available. However, most of them can only _create_ documents, they cannot _modify_ existing ones. And many of them only work in particular environments.
Usage Examples
Create Document
_This example produces this PDF._
- ``` js
- import { PDFDocument, StandardFonts, rgb } from 'pdf-lib'
- // Create a new PDFDocument
- const pdfDoc = await PDFDocument.create()
- // Embed the Times Roman font
- const timesRomanFont = await pdfDoc.embedFont(StandardFonts.TimesRoman)
- // Add a blank page to the document
- const page = pdfDoc.addPage()
- // Get the width and height of the page
- const { width, height } = page.getSize()
- // Draw a string of text toward the top of the page
- 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),
- })
- // Serialize the PDFDocument to bytes (a Uint8Array)
- const pdfBytes = await pdfDoc.save()
- // For example, `pdfBytes` can be:
- // • Written to a file in Node
- // • Downloaded from the browser
- // • Rendered in an
- ```
Modify Document
- ``` js
- import { degrees, PDFDocument, rgb, StandardFonts } from 'pdf-lib';
- // This should be a Uint8Array or ArrayBuffer
- // This data can be obtained in a number of different ways
- // If your running in a Node environment, you could use fs.readFile()
- // In the browser, you could make a fetch() call and use res.arrayBuffer()
- const existingPdfBytes = ...
- // Load a PDFDocument from the existing PDF bytes
- const pdfDoc = await PDFDocument.load(existingPdfBytes)
- // Embed the Helvetica font
- const helveticaFont = await pdfDoc.embedFont(StandardFonts.Helvetica)
- // Get the first page of the document
- const pages = pdfDoc.getPages()
- const firstPage = pages[0]
- // Get the width and height of the first page
- const { width, height } = firstPage.getSize()
- // Draw a string of text diagonally across the first page
- 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),
- })
- // Serialize the PDFDocument to bytes (a Uint8Array)
- const pdfBytes = await pdfDoc.save()
- // For example, `pdfBytes` can be:
- // • Written to a file in Node
- // • Downloaded from the browser
- // • Rendered in an
- ```
Create Form
_This example produces this PDF._
See also Creating and Filling Forms
- ``` js
- import { PDFDocument } from 'pdf-lib'
- // Create a new PDFDocument
- const pdfDoc = await PDFDocument.create()
- // Add a blank page to the document
- const page = pdfDoc.addPage([550, 750])
- // Get the form so we can add fields to it
- const form = pdfDoc.getForm()
- // Add the superhero text field and description
- 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 })
- // Add the rocket radio group, labels, and description
- 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')
- // Add the gundam check boxes, labels, and description
- 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()
- // Add the planet dropdown and description
- 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 })
- // Add the person option list and description
- 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 })
- // Just saying...
- page.drawText(`* Pluto should be a planet too!`, { x: 15, y: 15, size: 15 })
- // Serialize the PDFDocument to bytes (a Uint8Array)
- const pdfBytes = await pdfDoc.save()
- // For example, `pdfBytes` can be:
- // • Written to a file in Node
- // • Downloaded from the browser
- // • Rendered in an
- ```
Fill Form
_This example produces this PDF_ (when this PDF is used for theformPdfBytes variable, this image is used for themarioImageBytes variable, and this image is used for theemblemImageBytes variable).
See also Creating and Filling Forms
- ``` js
- import { PDFDocument } from 'pdf-lib'
- // These should be Uint8Arrays or ArrayBuffers
- // This data can be obtained in a number of different ways
- // If your running in a Node environment, you could use fs.readFile()
- // In the browser, you could make a fetch() call and use res.arrayBuffer()
- const formPdfBytes = ...
- const marioImageBytes = ...
- const emblemImageBytes = ...
- // Load a PDF with form fields
- const pdfDoc = await PDFDocument.load(formPdfBytes)
- // Embed the Mario and emblem images
- const marioImage = await pdfDoc.embedPng(marioImageBytes)
- const emblemImage = await pdfDoc.embedPng(emblemImageBytes)
- // Get the form containing all the fields
- const form = pdfDoc.getForm()
- // Get all fields in the PDF by their names
- 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.getTextField('Faction Symbol Image')
- // Fill in the basic info fields
- nameField.setText('Mario')
- ageField.setText('24 years')
- heightField.setText(`5' 1"`)
- weightField.setText('196 lbs')
- eyesField.setText('blue')
- skinField.setText('white')
- hairField.setText('brown')
- // Fill the character image field with our Mario image
- characterImageField.setImage(marioImage)
- // Fill in the allies field
- alliesField.setText(
- [
- `Allies:`,
- ` • Princess Daisy`,
- ` • Princess Peach`,
- ` • Rosalina`,
- ` • Geno`,
- ` • Luigi`,
- ` • Donkey Kong`,
- ` • Yoshi`,
- ` • Diddy Kong`,
- ``,
- `Organizations:`,
- ` • Italian Plumbers Association`,
- ].join('\n'),
- )
- // Fill in the faction name field
- factionField.setText(`Mario's Emblem`)
- // Fill the faction image field with our emblem image
- factionImageField.setImage(emblemImage)
- // Fill in the backstory field
- 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.`,
- )
- // Fill in the traits field
- 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'),
- )
- // Fill in the treasure field
- treasureField.setText(['• Gold coins', '• Treasure chests'].join('\n'))
- // Serialize the PDFDocument to bytes (a Uint8Array)
- const pdfBytes = await pdfDoc.save()
- // For example, `pdfBytes` can be:
- // • Written to a file in Node
- // • Downloaded from the browser
- // • Rendered in an
- ```
Flatten Form
- ``` js
- import { PDFDocument } from 'pdf-lib'
- // This should be a Uint8Array or ArrayBuffer
- // This data can be obtained in a number of different ways
- // If your running in a Node environment, you could use fs.readFile()
- // In the browser, you could make a fetch() call and use res.arrayBuffer()
- const formPdfBytes = ...
- // Load a PDF with form fields
- const pdfDoc = await PDFDocument.load(formPdfBytes)
- // Get the form containing all the fields
- const form = pdfDoc.getForm()
- // Fill the form's fields
- 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');
- // Flatten the form's fields
- form.flatten();
- // Serialize the PDFDocument to bytes (a Uint8Array)
- const pdfBytes = await pdfDoc.save()
- // For example, `pdfBytes` can be:
- // • Written to a file in Node
- // • Downloaded from the browser
- // • Rendered in an
- ```
Copy Pages
_This example produces this PDF_ (when this PDF is used for thefirstDonorPdfBytes variable and this PDF is used for thesecondDonorPdfBytes variable).
- ``` js
- import { PDFDocument } from 'pdf-lib'
- // Create a new PDFDocument
- const pdfDoc = await PDFDocument.create()
- // These should be Uint8Arrays or ArrayBuffers
- // This data can be obtained in a number of different ways
- // If your running in a Node environment, you could use fs.readFile()
- // In the browser, you could make a fetch() call and use res.arrayBuffer()
- const firstDonorPdfBytes = ...
- const secondDonorPdfBytes = ...
- // Load a PDFDocument from each of the existing PDFs
- const firstDonorPdfDoc = await PDFDocument.load(firstDonorPdfBytes)
- const secondDonorPdfDoc = await PDFDocument.load(secondDonorPdfBytes)
- // Copy the 1st page from the first donor document, and
- // the 743rd page from the second donor document
- const [firstDonorPage] = await pdfDoc.copyPages(firstDonorPdfDoc, [0])
- const [secondDonorPage] = await pdfDoc.copyPages(secondDonorPdfDoc, [742])
- // Add the first copied page
- pdfDoc.addPage(firstDonorPage)
- // Insert the second copied page to index 0, so it will be the
- // first page in `pdfDoc`
- pdfDoc.insertPage(0, secondDonorPage)
- // Serialize the PDFDocument to bytes (a Uint8Array)
- const pdfBytes = await pdfDoc.save()
- // For example, `pdfBytes` can be:
- // • Written to a file in Node
- // • Downloaded from the browser
- // • Rendered in an
- ```
Embed PNG and JPEG Images
_This example produces this PDF_ (when this image is used for thejpgImageBytes variable and this image is used for thepngImageBytes variable).
- ``` js
- import { PDFDocument } from 'pdf-lib'
- // These should be Uint8Arrays or ArrayBuffers
- // This data can be obtained in a number of different ways
- // If your running in a Node environment, you could use fs.readFile()
- // In the browser, you could make a fetch() call and use res.arrayBuffer()
- const jpgImageBytes = ...
- const pngImageBytes = ...
- // Create a new PDFDocument
- const pdfDoc = await PDFDocument.create()
- // Embed the JPG image bytes and PNG image bytes
- const jpgImage = await pdfDoc.embedJpg(jpgImageBytes)
- const pngImage = await pdfDoc.embedPng(pngImageBytes)
- // Get the width/height of the JPG image scaled down to 25% of its original size
- const jpgDims = jpgImage.scale(0.25)
- // Get the width/height of the PNG image scaled down to 50% of its original size
- const pngDims = pngImage.scale(0.5)
- // Add a blank page to the document
- const page = pdfDoc.addPage()
- // Draw the JPG image in the center of the page
- page.drawImage(jpgImage, {
- x: page.getWidth() / 2 - jpgDims.width / 2,
- y: page.getHeight() / 2 - jpgDims.height / 2,
- width: jpgDims.width,
- height: jpgDims.height,
- })
- // Draw the PNG image near the lower right corner of the JPG image
- page.drawImage(pngImage, {
- x: page.getWidth() / 2 - pngDims.width / 2 + 75,
- y: page.getHeight() / 2 - pngDims.height,
- width: pngDims.width,
- height: pngDims.height,
- })
- // Serialize the PDFDocument to bytes (a Uint8Array)
- const pdfBytes = await pdfDoc.save()
- // For example, `pdfBytes` can be:
- // • Written to a file in Node
- // • Downloaded from the browser
- // • Rendered in an
- ```
Embed PDF Pages
_This example produces this PDF_ (when this PDF is used for theamericanFlagPdfBytes variable and this PDF is used for theusConstitutionPdfBytes variable).
- ``` js
- import { PDFDocument } from 'pdf-lib'
- // These should be Uint8Arrays or ArrayBuffers
- // This data can be obtained in a number of different ways
- // If your running in a Node environment, you could use fs.readFile()
- // In the browser, you could make a fetch() call and use res.arrayBuffer()
- const americanFlagPdfBytes = ...
- const usConstitutionPdfBytes = ...
- // Create a new PDFDocument
- const pdfDoc = await PDFDocument.create()
- // Embed the American flag PDF bytes
- const [americanFlag] = await pdfDoc.embedPdf(americanFlagPdfBytes)
- // Load the U.S. constitution PDF bytes
- const usConstitutionPdf = await PDFDocument.load(usConstitutionPdfBytes)
- // Embed the second page of the constitution and clip the preamble
- const preamble = await pdfDoc.embedPage(usConstitutionPdf.getPages()[1], {
- left: 55,
- bottom: 485,
- right: 300,
- top: 575,
- })
- // Get the width/height of the American flag PDF scaled down to 30% of
- // its original size
- const americanFlagDims = americanFlag.scale(0.3)
- // Get the width/height of the preamble clipping scaled up to 225% of
- // its original size
- const preambleDims = preamble.scale(2.25)
- // Add a blank page to the document
- const page = pdfDoc.addPage()
- // Draw the American flag image in the center top of the page
- page.drawPage(americanFlag, {
- ...americanFlagDims,
- x: page.getWidth() / 2 - americanFlagDims.width / 2,
- y: page.getHeight() - americanFlagDims.height - 150,
- })
- // Draw the preamble clipping in the center bottom of the page
- page.drawPage(preamble, {
- ...preambleDims,
- x: page.getWidth() / 2 - preambleDims.width / 2,
- y: page.getHeight() / 2 - preambleDims.height / 2 - 50,
- })
- // Serialize the PDFDocument to bytes (a Uint8Array)
- const pdfBytes = await pdfDoc.save()
- // For example, `pdfBytes` can be:
- // • Written to a file in Node
- // • Downloaded from the browser
- // • Rendered in an
- ```
Embed Font and Measure Text
pdf-lib relies on a sister module to support embedding custom fonts: [@pdf-lib/fontkit](https://www.npmjs.com/package/@pdf-lib/fontkit). You must add the @pdf-lib/fontkit module to your project and register it using pdfDoc.registerFontkit(...) before embedding custom fonts.
[See below for detailed installation instructions on installing @pdf-lib/fontkit as a UMD or NPM module.](#fontkit-installation)
- ``` js
- import { PDFDocument, rgb } from 'pdf-lib'
- import fontkit from '@pdf-lib/fontkit'
- // This should be a Uint8Array or ArrayBuffer
- // This data can be obtained in a number of different ways
- // If you're running in a Node environment, you could use fs.readFile()
- // In the browser, you could make a fetch() call and use res.arrayBuffer()
- const fontBytes = ...
- // Create a new PDFDocument
- const pdfDoc = await PDFDocument.create()
- // Register the `fontkit` instance
- pdfDoc.registerFontkit(fontkit)
- // Embed our custom font in the document
- const customFont = await pdfDoc.embedFont(fontBytes)
- // Add a blank page to the document
- const page = pdfDoc.addPage()
- // Create a string of text and measure its width and height in our custom font
- const text = 'This is text in an embedded font!'
- const textSize = 35
- const textWidth = customFont.widthOfTextAtSize(text, textSize)
- const textHeight = customFont.heightAtSize(textSize)
- // Draw the string of text on the page
- page.drawText(text, {
- x: 40,
- y: 450,
- size: textSize,
- font: customFont,
- color: rgb(0, 0.53, 0.71),
- })
- // Draw a box around the string of text
- page.drawRectangle({
- x: 40,
- y: 450,
- width: textWidth,
- height: textHeight,
- borderColor: rgb(1, 0, 0),
- borderWidth: 1.5,
- })
- // Serialize the PDFDocument to bytes (a Uint8Array)
- const pdfBytes = await pdfDoc.save()
- // For example, `pdfBytes` can be:
- // • Written to a file in Node
- // • Downloaded from the browser
- // • Rendered in an
- ```
Add Attachments
_This example produces this PDF_ (when this image is used for thejpgAttachmentBytes variable and this PDF is used for thepdfAttachmentBytes variable).
- ``` js
- import { PDFDocument } from 'pdf-lib'
- // These should be Uint8Arrays or ArrayBuffers
- // This data can be obtained in a number of different ways
- // If your running in a Node environment, you could use fs.readFile()
- // In the browser, you could make a fetch() call and use res.arrayBuffer()
- const jpgAttachmentBytes = ...
- const pdfAttachmentBytes = ...
- // Create a new PDFDocument
- const pdfDoc = await PDFDocument.create()
- // Add the JPG attachment
- 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'),
- })
- // Add the PDF attachment
- 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'),
- })
- // Add a page with some text
- const page = pdfDoc.addPage();
- page.drawText('This PDF has two attachments', { x: 135, y: 415 })
- // Serialize the PDFDocument to bytes (a Uint8Array)
- const pdfBytes = await pdfDoc.save()
- // For example, `pdfBytes` can be:
- // • Written to a file in Node
- // • Downloaded from the browser
- // • Rendered in an
- ```
Set Document Metadata
_This example produces this PDF_.
- ``` js
- import { PDFDocument, StandardFonts } from 'pdf-lib'
- // Create a new PDFDocument
- const pdfDoc = await PDFDocument.create()
- // Embed the Times Roman font
- const timesRomanFont = await pdfDoc.embedFont(StandardFonts.TimesRoman)
- // Add a page and draw some text on it
- 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 })
- // Set all available metadata fields on the PDFDocument. 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'))
- // Serialize the PDFDocument to bytes (a Uint8Array)
- const pdfBytes = await pdfDoc.save()
- // For example, `pdfBytes` can be:
- // • Written to a file in Node
- // • Downloaded from the browser
- // • Rendered in an
- ```
Read Document Metadata
- ``` js
- import { PDFDocument } from 'pdf-lib'
- // This should be a Uint8Array or ArrayBuffer
- // This data can be obtained in a number of different ways
- // If your running in a Node environment, you could use fs.readFile()
- // In the browser, you could make a fetch() call and use res.arrayBuffer()
- const existingPdfBytes = ...
- // Load a PDFDocument without updating its existing metadata
- const pdfDoc = await PDFDocument.load(existingPdfBytes, {
- updateMetadata: false
- })
- // Print all available metadata fields
- 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 (_when this PDF is used for theexistingPdfBytes variable_):
- ```
- 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
- ```
Set Viewer Preferences
- ``` js
- import {
- PDFDocument,
- StandardFonts,
- NonFullScreenPageMode,
- ReadingDirection,
- PrintScaling,
- Duplex,
- PDFName,
- } from 'pdf-lib'
- // Create a new PDFDocument
- const pdfDoc = await PDFDocument.create()
- // Embed the Times Roman font
- const timesRomanFont = await pdfDoc.embedFont(StandardFonts.TimesRoman)
- // Add a page and draw some text on it
- 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 })
- // Set all available viewer preferences on the PDFDocument:
- const viewerPrefs = pdfDoc.catalog.getOrCreateViewerPreferences()
- viewerPrefs.setHideToolbar(true)
- viewerPrefs.setHideMenubar(true)
- viewerPrefs.setHideWindowUI(true)
- viewerPrefs.setFitWindow(true)
- viewerPrefs.setCenterWindow(true)
- viewerPrefs.setDisplayDocTitle(true)
- // Set the PageMode (otherwise setting NonFullScreenPageMode has no meaning)
- pdfDoc.catalog.set(PDFName.of('PageMode'), PDFName.of('FullScreen'))
- // Set what happens when fullScreen is closed
- viewerPrefs.setNonFullScreenPageMode(NonFullScreenPageMode.UseOutlines)
- viewerPrefs.setReadingDirection(ReadingDirection.L2R)
- viewerPrefs.setPrintScaling(PrintScaling.None)
- viewerPrefs.setDuplex(Duplex.DuplexFlipLongEdge)
- viewerPrefs.setPickTrayByPDFSize(true)
- // We can set the default print range to only the first page
- viewerPrefs.setPrintPageRange({ start: 0, end: 0 })
- // Or we can supply noncontiguous ranges (e.g. pages 1, 3, and 5-7)
- viewerPrefs.setPrintPageRange([
- { start: 0, end: 0 },
- { start: 2, end: 2 },
- { start: 4, end: 6 },
- ])
- viewerPrefs.setNumCopies(2)
- // Serialize the PDFDocument to bytes (a Uint8Array)
- const pdfBytes = await pdfDoc.save()
- // For example, `pdfBytes` can be:
- // • Written to a file in Node
- // • Downloaded from the browser
- // • Rendered in an
- ```
Read Viewer Preferences
- ``` js
- import { PDFDocument } from 'pdf-lib'
- // This should be a Uint8Array or ArrayBuffer
- // This data can be obtained in a number of different ways
- // If your running in a Node environment, you could use fs.readFile()
- // In the browser, you could make a fetch() call and use res.arrayBuffer()
- const existingPdfBytes = ...
- // Load a PDFDocument without updating its existing metadata
- const pdfDoc = await PDFDocument.load(existingPdfBytes)
- const viewerPrefs = pdfDoc.catalog.getOrCreateViewerPreferences()
- // Print all available viewer preference fields
- console.log('HideToolbar:', viewerPrefs.getHideToolbar())
- console.log('HideMenubar:', viewerPrefs.getHideMenubar())
- console.log('HideWindowUI:', viewerPrefs.getHideWindowUI())
- console.log('FitWindow:', viewerPrefs.getFitWindow())
- console.log('CenterWindow:', viewerPrefs.getCenterWindow())
- console.log('DisplayDocTitle:', viewerPrefs.getDisplayDocTitle())
- console.log('NonFullScreenPageMode:', viewerPrefs.getNonFullScreenPageMode())
- console.log('ReadingDirection:', viewerPrefs.getReadingDirection())
- console.log('PrintScaling:', viewerPrefs.getPrintScaling())
- console.log('Duplex:', viewerPrefs.getDuplex())
- console.log('PickTrayByPDFSize:', viewerPrefs.getPickTrayByPDFSize())
- console.log('PrintPageRange:', viewerPrefs.getPrintPageRange())
- console.log('NumCopies:', viewerPrefs.getNumCopies())
- ```
This script outputs the following (_when this PDF is used for theexistingPdfBytes variable_):
- ```
- HideToolbar: true
- HideMenubar: true
- HideWindowUI: false
- FitWindow: true
- CenterWindow: true
- DisplayDocTitle: true
- NonFullScreenPageMode: UseNone
- ReadingDirection: R2L
- PrintScaling: None
- Duplex: DuplexFlipLongEdge
- PickTrayByPDFSize: true
- PrintPageRange: [ { start: 1, end: 1 }, { start: 3, end: 4 } ]
- NumCopies: 2
- ```
Draw SVG Paths
_This example produces this PDF_.
- ``` js
- import { PDFDocument, rgb } from 'pdf-lib'
- // SVG path for a wavy line
- const svgPath =
- 'M 0,20 L 100,160 Q 130,200 150,120 C 190,-40 200,200 300,150 L 400,90'
- // Create a new PDFDocument
- const pdfDoc = await PDFDocument.create()
- // Add a blank page to the document
- const page = pdfDoc.addPage()
- page.moveTo(100, page.getHeight() - 5)
- // Draw the SVG path as a black line
- page.moveDown(25)
- page.drawSvgPath(svgPath)
- // Draw the SVG path as a thick green line
- page.moveDown(200)
- page.drawSvgPath(svgPath, { borderColor: rgb(0, 1, 0), borderWidth: 5 })
- // Draw the SVG path and fill it with red
- page.moveDown(200)
- page.drawSvgPath(svgPath, { color: rgb(1, 0, 0) })
- // Draw the SVG path at 50% of its original size
- page.moveDown(200)
- page.drawSvgPath(svgPath, { scale: 0.5 })
- // Serialize the PDFDocument to bytes (a Uint8Array)
- const pdfBytes = await pdfDoc.save()
- // For example, `pdfBytes` can be:
- // • Written to a file in Node
- // • Downloaded from the browser
- // • Rendered in an
- ```
Deno Usage
pdf-lib fully supports the exciting new Deno runtime! All of the usage examples work in Deno. The only thing you need to do is change the imports forpdf-lib and @pdf-lib/fontkit to use the Skypack CDN, because Deno requires all modules to be referenced via URLs.
Creating a Document with Deno
Below is the [create document](#create-document) example modified for Deno:
- ``` js
- import {
- PDFDocument,
- StandardFonts,
- rgb,
- } from 'https://cdn.skypack.dev/pdf-lib@^1.11.1?dts';
- 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();
- await Deno.writeFile('out.pdf', pdfBytes);
- ```
If you save this script as create-document.ts, you can execute it using Deno with the following command:
- ```
- deno run --allow-write create-document.ts
- ```
The resulting out.pdf file will look like this PDF.
Embedding a Font with Deno
Here's a slightly more complicated example demonstrating how to embed a font and measure text in Deno:
- ``` js
- import {
- degrees,
- PDFDocument,
- rgb,
- StandardFonts,
- } from 'https://cdn.skypack.dev/pdf-lib@^1.11.1?dts';
- import fontkit from 'https://cdn.skypack.dev/@pdf-lib/fontkit@^1.0.0?dts';
- 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();
- await Deno.writeFile('out.pdf', pdfBytes);
- ```
If you save this script as custom-font.ts, you can execute it with the following command:
- ```
- deno run --allow-write --allow-net custom-font.ts
- ```
The resulting out.pdf file will look like this PDF.
Complete Examples
The usage examples provide code that is brief and to the point, demonstrating the different features ofpdf-lib. You can find complete working examples in the [apps/](apps/) directory. These apps are used to do manual testing of pdf-lib before every release (in addition to the automated tests).
There are currently four apps:
- [node](apps/node/) - contains tests forpdf-lib in Node environments. These tests are a handy reference when trying to save/load PDFs, fonts, or images with pdf-lib from the filesystem. They also allow you to quickly open your PDFs in different viewers (Acrobat, Preview, Foxit, Chrome, Firefox, etc...) to ensure compatibility.
- [web](apps/web/) - contains tests forpdf-lib in browser environments. These tests are a handy reference when trying to save/load PDFs, fonts, or images with pdf-lib in a browser environment.
- [rn](apps/rn) - contains tests forpdf-lib in React Native environments. These tests are a handy reference when trying to save/load PDFs, fonts, or images with pdf-lib in a React Native environment.
- [deno](apps/deno) - contains tests forpdf-lib in Deno environments. These tests are a handy reference when trying to save/load PDFs, fonts, or images with pdf-lib from the filesystem.
Installation
NPM Module
To install the latest stable version:
- ``` sh
- # With npm
- npm install --save pdf-lib
- # With yarn
- yarn add pdf-lib
- ```