Polotno
Export & Import

PDF Import

Convert PDF files into editable Polotno designs

The @polotno/pdf-import package converts PDF files into Polotno JSON format, enabling you to import PDF designs into Polotno for further editing.

Beta Feature: This library is new and may not work correctly for all PDF files. Please report issues to help us improve it.

Try it without code: The PDF to JSON, PDF to SVG, and PDF to HTML converters run this exact parser in the browser — drop your own file to test it before integrating.

Importing other formats? See SVG Import and PSD Import for the equivalent packages.

What you get back

Content is reconstructed as native, editable Polotno elements wherever the PDF allows it:

  • Text — paragraphs keep their alignment (including justified text and first-line indents), mixed font weights and sizes within a paragraph, and small caps.
  • Tables — ruled grids become real table elements with editable cells, borders, and per-cell alignment, not text floating over lines.
  • Bullet lists — bullet columns become one text element with real <ul> markup, so the marker and hanging indent are part of the element rather than faked with spaces.
  • Images — including masked and stencil images, and text painted through a glyph clip (image- or gradient-filled headings), which comes back as editable text.
  • Text shadows — a shadow baked into a bitmap is folded into the text element's own shadow, so it follows the text when you edit the wording instead of leaving a ghost of the old one behind.

Page size

Every page is sized from its own viewport, which accounts for that page's CropBox and rotation. A document that mixes page sizes or rotations imports with each page at its true size.

Installation

npm install @polotno/pdf-import

Basic Usage

import { pdfToJson } from '@polotno/pdf-import';

const json = await pdfToJson({ pdf: buffer });

// Load into Polotno store
store.loadJSON(json);

Options

const json = await pdfToJson({
  pdf: buffer,
  scale: 96 / 72,
  fontStrategy: 'auto',
  password: 'secret',
  onWarning: (warning) => console.warn(warning),
});

scale

Multiplier applied to all geometry in the returned design. The default 96 / 72 converts PDF points (1/72 inch) to CSS pixels (1/96 inch), so a design exported from a pixel-based tool comes back at its original size — a 1080×1350 Canva design imports as 1080×1350 rather than 810×1012.5. dpi scales along with it, so the physical print size is unchanged.

Pass scale: 1 to keep raw PDF points.

Changed in 0.1.0: output is in CSS pixels by default. Previously pdfToJson returned raw PDF points, so a US Letter page came back as 612×792; it is now 816×1056. If your integration depends on the old numbers, pass scale: 1.

fontStrategy

Controls how fonts in the PDF are resolved. Defaults to 'auto'.

ValueBehavior
'auto'Fonts that resolve to a real Google family keep their real name and load from Google Fonts — editable text with full glyph coverage. Custom fonts are embedded as data URIs.
'embed'Every font is embedded; Google families are renamed "Name (PDF)". Maximum fidelity and offline-safe, but the text is tied to the embedded subset.
'googleFontsMatch'Nothing is embedded; unknown fonts map to the metrically closest Google family. Smallest output.

Changed in 0.1.0: the default was previously 'embed'. Pass fontStrategy: 'embed' to keep the old behavior.

password

The password a PDF asks for to open it. A PDF that only restricts printing or copying imports normally and needs nothing here.

try {
  return await pdfToJson({ pdf: buffer, password });
} catch (e) {
  if (e.details?.reason === 'password-required') {
    // the file is encrypted — ask the user for a password
  }
  if (e.details?.reason === 'password-incorrect') {
    // wrong password — ask again
  }
  throw e; // no reason: the bytes are not a readable PDF
}

Both reasons mean the file itself is intact, so you can prompt and import again. An IMPORT_FAILED with no reason means the bytes are not a readable document.

onWarning

Called for each non-fatal problem, such as an image that could not be extracted. Without it these cases only reach console.warn, so a caller cannot tell a clean import from one that silently dropped content.

const warnings = [];
const json = await pdfToJson({
  pdf: buffer,
  onWarning: (warning) => warnings.push(warning),
});

if (warnings.length) {
  // surface a "some content could not be imported" notice to the user
}

warning.code is stable — branch on it rather than on warning.message. Every warning carries details.pageIndex (zero-based), and details.imageName when the source names the image.

codedetails.reasonMeaning
IMAGE_SKIPPEDextract-failedThe image threw while being built.
IMAGE_SKIPPEDno-sourceNothing in the PDF payload yielded usable pixels.
IMAGE_SKIPPEDclipped-outThe image and its clip do not intersect, so it paints nothing. Usually harmless, but it is also where a misplaced image lands.

Node.js Example

import { pdfToJson } from '@polotno/pdf-import';
import fs from 'fs';

const buffer = fs.readFileSync('design.pdf');
const json = await pdfToJson({ pdf: buffer });
store.loadJSON(json);

Browser Example

import { pdfToJson } from '@polotno/pdf-import';

async function handleFileUpload(file: File) {
  const buffer = await file.arrayBuffer();
  const json = await pdfToJson({ pdf: buffer });
  store.loadJSON(json);
}

pdfToJson returns a complete, valid Design Format document, ready for store.loadJSON(). See the reference for every field and type.

Adobe Illustrator (.ai) Support

Adobe Illustrator .ai files are based on the PDF format, so you can import them using the same pdfToJson() function.

Platform Support

This package works both client-side (browser) and server-side (Node.js). All processing runs locally — no data is sent to any external server.

Demo

Error Handling

If a file can't be parsed, the importer throws a plain Error with code: 'IMPORT_FAILED' and details.format: 'pdf'; the low-level parser error is chained as error.cause. An encrypted PDF sets details.reason to password-required or password-incorrect — see password. See Error Handling.

Troubleshooting

Import produces unexpected results:

  • Check if the PDF contains vector elements or rasterized content
  • Try re-exporting the PDF from your design tool with different settings

Missing elements:

  • Some advanced PDF features may not be supported
  • Pass an onWarning callback to capture what was dropped — warnings also go to the browser console

Import fails on a protected PDF:

  • A PDF that needs a password to open requires the password option; check error.details.reason to tell that apart from an unreadable file

Need help? Report issues and get support at community.polotno.com.

On this page