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

@gum-jsx/pdf

v2.0.0

Published

Gum rendering to PDF.

Readme

@gum-jsx/pdf

PDF export for completed Gum fragments, preserving vector drawings and embedded PNG images. Layout, text shaping, and glyph outlines are supplied by @gum-jsx/core (and optionally @gum-jsx/math); the exporter does not load fonts or perform layout.

See the Gum project for getting started and the package overview.

Usage

import { LayoutPass, Text, px } from '@gum-jsx/core'
import { render_pdf } from '@gum-jsx/pdf'

const fragment = new LayoutPass().layout(
  new Text({ children: 'Hello, PDF!', font_size: px(32) }),
)
const bytes = render_pdf(fragment, { title: 'Hello', background: 'white' })
await Bun.write('hello.pdf', bytes)

render_pdf(fragmentOrPages, options?): Uint8Array is synchronous and works in Bun or the browser. Numeric serialization uses core's shared formatter; PNG decoding and compression use fast-png and fflate, with no native bindings or filesystem access. In a browser, the returned bytes can be used in a Blob with type application/pdf.

Known limitation: fast-png 8.0.0 rejects RGB PNGs with only one or two pixels and a tRNS transparency key. PDF export throws for these images; convert them to RGBA before embedding. Ordinary RGBA PNGs, including transparent 1×1 images, are unaffected. The package uses the unmodified decoder.

| Option | Default | Meaning | | --- | --- | --- | | title | omitted | Unicode PDF document title. | | background | omitted | Page background color; otherwise unpainted. | | points_per_pixel | 0.75 | Physical scale: 96 layout pixels per inch, 72 PDF points per inch. Use 1 to treat each layout pixel as one point. | | precision | 10 | Decimal places in numeric output; use 0–100 or 'full' for unrounded values. |

Pass a fragment for one page, or a nonempty array of fragments for a multipage document. Pages follow array order, and each page matches its own fragment.size, clipping any overflow to that viewport. Options apply to the whole document; images and drawing resources are reused across pages.

const pages = ['First slide', 'Second slide'].map(children =>
  new LayoutPass().layout(new Text({ children, font_size: px(32) })),
)
await Bun.write('slides.pdf', render_pdf(pages, { title: 'Slides' }))

Both page dimensions and the scale must be positive and finite. PDF 1.4 page dimensions are limited to 14,400 points per side; larger dimensions are rejected.

Supported drawing features:

  • PNG images, including grayscale, RGB, indexed color, interlacing, and alpha. Image samples are compressed losslessly at their original dimensions; 16-bit samples retain their precision. Transparency uses a grayscale soft mask, and repeated images share one embedded resource. PNG color profiles and gamma metadata are not applied; samples use PDF DeviceRGB or DeviceGray.
  • Rectangles, ellipses, individually rounded corners, and paths (M, L, Q, C, Z). Quadratics convert exactly to cubics; elliptical arcs use the usual cubic approximation.
  • Affine placement transforms, including rotation, reflection, skew, and nonuniform scaling. Consecutive placements are combined without flattening stroke geometry or pushing a graphics state for every layout wrapper.
  • Nested rectangular and rounded clipping, nonzero winding fills, strokes, line caps, joins, miter limits, and dash patterns.
  • Color alpha and drawing opacity. When necessary, isolated transparency groups apply opacity to the combined fill and stroke, matching SVG compositing.
  • All CSS named colors, transparent, none, 3/4/6/8-digit hex, and numeric rgb()/rgba()/hsl()/hsla() colors with comma or space/slash syntax. Percentages and hue units (deg, rad, grad, turn) are supported.

Unsupported color expressions (including var(), currentColor, paint URLs, wide-gamut colors, and CSS calculations) throw a descriptive error. Resolve them to one of the supported color formats before export.

Text remains vector outlines: appearance is preserved, but text is not searchable or selectable. Live text from a color font, such as emoji, has no outline and no embedded font data here, so exporting it throws an error naming the family. Fragment labels and debug overlays are not exported. The exporter writes deterministic PDF 1.4 files with uncompressed vector content and compressed image streams; it does not automatically split content across pages or produce tagged/accessibility or archival PDF variants.

Development

From the workspace root:

bun --filter @gum-jsx/pdf test
bun --filter @gum-jsx/pdf typecheck
bun --filter @gum-jsx/pdf test:visual

The regular tests require only workspace development dependencies. The optional visual checks additionally require qpdf, pdfinfo, and pdftoppm on PATH. They validate actual PDFs and compare their rasterized pixels with the WASM fragment renderer, leaving PDF, SVG, PNG, and PPM artifacts in out/visual/. Small edge differences are expected between rasterizers. The PNG package and math package are used only for development checks.