npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@polotno/pdf-import

v0.6.0

Published

Convert PDFs into editable Polotno JSON designs with text, images, tables, and vector graphics in browsers and Node.js

Readme

@polotno/pdf-import — PDF to editable Polotno JSON

Turn a PDF back into an editable design.

Works without the editor. The converter returns plain Polotno design JSON.

Runtime: Browser and Node.js 20 or newer. Conversion runs locally.

Documentation · Try PDF to JSON in your browser

License: Non-production use is free, with no time limit. Production use requires a valid Polotno subscription. Read the license.

Most PDF libraries give you a wall of text. This one gives you the design back: text elements with their fonts, real tables with rows and cells, vector shapes, images, and page order. The output is Polotno JSON. You can edit it, and you can render it again.

No OCR. No flattened page image. It reads the PDF content stream directly.

| A text extractor returns | @polotno/pdf-import returns | | ------------------------------ | --------------------------------------------------------------- | | One string per page | Text elements with font, size, color, alignment and line height | | The cell text in reading order | One table element with rows, columns and cells | | Nothing, or a page screenshot | SVG paths, gradients, patterns and native rectangle shapes | | A list of embedded image blobs | Images with position, rotation, crop, flip and border | | Flat lines of text | Paragraphs, columns, bullet lists and rotated text |

Features

  • Structure, not a text dump — It groups glyphs into lines, paragraphs, columns and blocks. Alignment (left, center, right, justified) and line height come from the geometry, not from a guess.
  • Native tables — A ruled grid becomes one Polotno table element with real rows and cells. Cell text and vertical alignment come from the source. A grid that is not a clean data table falls back to lines and text.
  • Bullet lists — A marker column beside a body column becomes one list element. Continuation lines stay with their own item.
  • Rich text runs — Bold, italic, color and size changes inside one paragraph survive as styled runs. Paragraph indents survive as leading spaces.
  • Vector art — Paths, linear and radial gradients, tiling patterns and clip paths become SVG elements. A single axis-aligned rectangle becomes a native figure with an editable fill and stroke.
  • Fonts — It extracts the embedded font program and re-embeds it, or matches the font to the closest Google family. It handles small caps, letter spacing and CJK encodings. The Adobe CMap tables ship in the package.
  • Images — It recovers position, rotation, mirror and crop from the transform matrix, and transcodes CMYK JPEGs. A stroked frame around an image folds into the border of that image.
  • Effects that stay editable — A rasterized drop shadow becomes shadowEnabled on the text element, so the shadow follows your edits. It also reads soft masks, blend modes and text used as a clipping path.
  • Curved text — It finds text set along a circular path and emits one curved text element.
  • Invisible content stays invisible — It drops hidden optional-content (OCG) layers and OCR text layers, so the JSON matches what a PDF reader shows.
  • Form fields — Widget annotation text imports with its own font, size and rotation.
  • Browser and Node — One API, one package, no extra setup.

Install

npm install @polotno/pdf-import

Quick start

Node.js:

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

const pdfBuffer = fs.readFileSync('document.pdf');
const json = await pdfToJson({ pdf: pdfBuffer });

Browser:

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

const input = document.querySelector('input[type="file"]');
input.addEventListener('change', async (e) => {
  const file = e.target.files[0];
  const json = await pdfToJson({ pdf: await file.arrayBuffer() });
  // Optional: load the design into a Polotno store.
  store.loadJSON(json);
});

What comes out

A Polotno design. Each page holds typed elements in paint order:

{
  "width": 816,          // US Letter in CSS pixels at 96 dpi
  "height": 1056,
  "unit": "px",
  "dpi": 96,
  "fonts": [{ "fontFamily": "Lexend", "url": "data:font/opentype;base64,..." }],
  "pages": [
    {
      "id": "id_1",
      "background": "#FFFFFF",
      "children": [
        { "type": "image", "x": 315.8, "y": 143.8, "width": 183.3, "rotation": 0 },
        { "type": "text", "text": "Quarterly report", "fontSize": 32, "align": "center" },
        { "type": "figure", "subType": "rect", "fill": "#0B5FFF", "strokeWidth": 2 },
        { "type": "svg", "src": "data:image/svg+xml;...", "keepRatio": false },
        { "type": "table", "rows": 6, "cols": 4, "colWidths": [], "cells": [] }
      ]
    }
  ]
}

Every field is a normal Polotno property. Change the text, swap a color, move an element, then write the file out again.

Round trip

The same JSON goes back out through the Polotno export packages:

To give your users a canvas, load the JSON into the Polotno editor with store.loadJSON(json).

Options

const json = await pdfToJson({
  pdf: pdfBuffer,

  // 'auto' (default): a font that resolves to a real Google family keeps its
  //   name and loads from Google Fonts. Custom fonts are embedded as data URIs.
  // 'embed': every font is embedded. Google families are renamed "Name (PDF)".
  //   Highest fidelity, and it works offline.
  // 'googleFontsMatch': nothing is embedded. An unknown font maps to the
  //   metrically closest Google family.
  fontStrategy: 'auto',

  // Multiplier for all geometry. The default 96/72 converts PDF points to CSS
  // pixels, so a 1080x1350 Canva design comes back as 1080x1350, not
  // 810x1012.5. The dpi scales with it, so the physical print size does not
  // change. Pass 1 to keep raw PDF points.
  scale: 96 / 72,

  // Only for a PDF that asks for a password to open it.
  password: 'secret',

  // Called for each non-fatal problem. See "Warnings" below.
  onWarning: (warning) => console.warn(warning.code, warning.details),
});

Warnings

A PDF can be valid and still hold content that cannot convert. Without onWarning these cases only reach console.warn. You then cannot tell a clean result from one that dropped an image.

await pdfToJson({
  pdf: pdfBuffer,
  onWarning: (warning) => {
    // warning.code is stable. Branch on it, not on warning.message.
    if (warning.code === 'IMAGE_SKIPPED') {
      // warning.details.reason: 'extract-failed' | 'no-source' | 'clipped-out'
      // warning.details.pageIndex: zero-based page index
      report(warning.details);
    }
  },
});

Password-protected PDFs

A PDF that only restricts printing or copying imports normally. One that needs a password to open takes password. Without it, or with the wrong one, pdfToJson throws IMPORT_FAILED with a reason. You can then ask the user and try again:

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

Documentation

Full guide and a live demo: PDF Import Guide.

License

See LICENSE.md.