The latest release of pdf-lib
(v2.0.0
) introduced a breaking change in the API for drawSVG, making it asynchronous. You'll also need to manually embed the images that might be contained inside your svg file.
To update your code, you just need to convert it into synchronous mode:
const svg = `<svg width="100" height="100">
<rect y="0" x="0" width="100" height="100" fill="none" stroke="black"/>
<rect y="25" x="25" width="50" height="50" fill="black"/>
</svg>`;
await pdfPage.drawSvg(svg)
Will become:
const svg = `<svg width="100" height="100">
<rect y="0" x="0" width="100" height="100" fill="none" stroke="black"/>
<rect y="25" x="25" width="50" height="50" fill="black"/>
</svg>`;
pdfPage.drawSvg(svg)
To update your code, you must call embedSvg first
const svg = '<svg><image href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVQYV2NgYAAAAAMAAWgmWQ0AAAAASUVORK5CYII="/></svg>'
const pdfSvg = await pdfDoc.embedSvg(svg)
pdfPage.drawSvg(pdfSvg)
The latest release of pdf-lib
(v1.0.0
) includes several breaking API changes. If you have code written for older versions of pdf-lib
(v0.x.x
), you can use the following instructions to help migrate your code to v1.0.0.
Note that many of the API methods are now asynchronous and return promises, so you'll need to await
on them (or use promise chaining: .then(res => ...)
).
- Rename
PDFDocumentFactory
toPDFDocument
.PDFDocument.create
andPDFDocument.load
are now async (they return promises), so you'll need toawait
on them.
-
To create a new PDF document:
const pdfDoc = await PDFDocument.create();
-
To retrieve and load a PDF where
pdfUrl
points to the PDF to be loaded:const pdfBuffer = await fetch(pdfUrl).then((res) => res.arrayBuffer()); const pdfDoc = await PDFDocument.load(pdfBuffer);
-
The purpose of making these methods asynchronous is to avoid blocking the event loop (especially for browser-based usage). If you aren't running this code client-side and are not concerned about blocking the event loop, you can speed up parsing times with:
PDFDocument.load(..., { parseSpeed: ParseSpeeds.Fastest })
You can do a similar thing for save:
PDFDocument.save({ objectsPerTick: Infinity });
-
To draw content on a page in old versions of
pdf-lib
, you needed to create a content stream, invoke some operators, register the content stream, and add it to the document. Something like the following:const contentStream = pdfDoc.createContentStream( drawText( timesRomanFont.encodeText('Creating PDFs in JavaScript is awesome!'), { x: 50, y: 450, size: 15, font: 'TimesRoman', colorRgb: [0, 0.53, 0.71], }, ), ); page.addContentStreams(pdfDoc.register(contentStream));
However, in new versions of
pdf-lib
, this is much simpler. You simply invoke drawing methods on the page, such asPDFPage.drawText
,PDFPage.drawImage
,PDFPage.drawRectangle
, orPDFPage.drawSvgPath
. So the above example becomes:page.drawText('Creating PDFs in JavaScript is awesome!', { x: 50, y: 450, size: 15, font: timesRomanFont, color: rgb(0, 0.53, 0.71), });
Please see the Usage Examples for more in depth examples of drawing content on a page in the new versions of
pdf-lib
. You may also find the Complete Examples to be a useful reference. -
Change
getMaybe
function calls toget
calls. If a property doesn't exist, thenundefined
will be returned. Note, however, that PDF name strings with need to be wrapped inPDFName.of(...)
. For example, to look up the AcroForm object you'll need to changepdfDoc.catalog.getMaybe('AcroForm')
topdfDoc.catalog.get(PDFName.of('AcroForm'))
.const acroForm = await pdfDoc.context.lookup( pdfDoc.catalog.get(PDFName.of('AcroForm')), );
v0.x.x converted the strings passed to
get
andgetMaybe
toPDFName
objects, but v1.0.0 does not do this conversion for you. So you must always pass actualPDFName
objects instead of strings. -
To find the AcroForm field references now becomes:
const acroFieldRefs = await pdfDoc.context.lookup( acroForm.get(PDFName.of('Fields')), );
-
To add a new page replace
pdfDoc.createPage([width, height])
withpdfDoc.addPage([width, height])
const page = pdfDoc.addPage([500, 750]);
or simply:
const page = pdfDoc.addPage();
-
To get the size of the page:
const { width, height } = page.getSize(); page.getWidth(); page.getHeight();
-
To add images replace
pdfDoc.embedPNG
withpdfDoc.embedPng
andpdfDoc.embedJPG
withpdfDoc.embedJpg
-
The
pdfDoc.embedPng
andpdfDoc.embedJpg
methods now returnPDFImage
objects which have the width and height of the image as properties. You can also scale down the width and height by a constant factor using thePDFImage.scale
method:const aBigImage = await pdfDoc.embedPng(aBigImageBytes); const { width, height } = aBigImage.scale(0.25);
So,
const [image, dims] = pdfDoc.embedJPG(mediaBuffer)
becomes:const image = await pdfDoc.embedJpg(mediaBuffer); // image.width, image.height can be used instead of the dims object.
-
To save the PDF replace
PDFDocumentWriter.saveToBytes(pdfDoc)
withpdfDoc.save()
const pdfDocBytes = await pdfDoc.save();
-
To display the saved PDF now becomes:
const pdfUrl = URL.createObjectURL( new Blob([await pdfDoc.save()], { type: 'application/pdf' }), ); window.open(pdfUrl, '_blank');
(note:
URL.revokeObjectURL
should be called later to free up memory) -
To get the PDF page count:
pdfDoc.getPages().length;
-
To copy pages from one document to another you must now call
destPdf.copyPages(srcPdf, srcPageIndexesArray)
to copy pages. You can see an example of this in the Copy Pages usage example. Admittedly, this API is slightly less ergonomic than what exists in v0.x.x, but it has two key benefits:-
It avoids making PDFDocument.addPage and PDFDocument.insertPage async. When copying multiple pages from the source document, the resulting merged document should have a smaller file size. This is because the page copying API that exists in v0.x.x was intended for copying just one or two pages.
-
When copying large numbers of pages, it could result in redundant objects being created. This new page copying API should eliminate that.
async function mergePdfs(pdfsToMerge: string[]) { const mergedPdf = await PDFDocument.create(); for (const pdfCopyDoc of pdfsToMerge) { const pdfBytes = fs.readFileSync(pdfCopyDoc); const pdf = await PDFDocument.load(pdfBytes); const copiedPages = await mergedPdf.copyPages(pdf, pdf.getPageIndices()); copiedPages.forEach((page) => { mergedPdf.addPage(page); }); } const mergedPdfFile = await mergedPdf.save(); return mergedPdfFile; }
-
-
If required, you can retrieve the CropBox or MediaBox of a page like so:
const cropBox = page.node.CropBox() || page.node.MediaBox();