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

@codyps/zpl

v0.3.2

Published

Render ZPL locally to PNG, SVG and PDF with the Rust zpl renderer and WebAssembly

Readme

@codyps/zpl

Render ZPL to PNG, SVG, or multipage PDF locally in Node.js using the Rust zpl renderer compiled to WebAssembly. The packed package includes its Wasm binary, has no runtime npm dependencies, and requires no Rust installation, bundler, asynchronous initialization, printer, or network service.

Installation

Requires Node.js 22 or newer:

npm install @codyps/zpl

The published package is ready to use. ESM, CommonJS, and TypeScript are supported. For a browser application, use the browser preview or the Wasm bindings.

Quick start

import { render, libraryVersion } from '@codyps/zpl';
import { writeFileSync } from 'node:fs';

const result = render('^XA^FO20,20^A0N,30,30^FDHello^FS^XZ', {
  format: 'png',
  width: 812,
  height: 1218,
  dpi: 203,
  profile: 'specification',
});
writeFileSync('label.png', result.data);
console.log(libraryVersion(), result.warnings);

CommonJS: const { render } = require('@codyps/zpl'). TypeScript declarations are included; Node TypeScript applications should have @types/node installed.

render(input, options?) accepts a string (encoded as UTF-8), Buffer, or Uint8Array. Use bytes for binary graphics or legacy character encodings; select the matching ZPL ^CI mode in the source. It returns:

  • data: an owned Buffer; for SVG text use data.toString('utf8').
  • format: png (default), svg, or pdf.
  • width, height: actual dimensions in dots of the selected label, or the first page for multipage PDF.
  • labels: total number of labels in the input.
  • warnings: renderer warning strings for the whole document.

Options width, height, and dpi override the selected profile's initial settings. ZPL commands and profile behavior can change the resulting dimensions. profile supports zd621 (the Rust default, 832 × 1218 at 203 DPI), specification (812 × 1218 at 203 DPI), and zq610-plus (384 × 2030 at 203 DPI). Printer profiles reflect measured preview behavior, not universal device parity.

label is a zero-based index. PNG and SVG select label 0 by default. PDF includes all labels unless an index is supplied:

const { data } = render('^XA^FDOne^FS^XZ^XA^FDTwo^FS^XZ', { format: 'pdf' });
writeFileSync('labels.pdf', data);

Parsing

parse(input, options?) frames a complete ZPL byte stream without rendering it:

import { parse, ParseError } from '@codyps/zpl';

const { elements, syntax } = parse('^XA^FO20,20^FDHello^FS^XZ');
for (const { kind, offset, data } of elements) {
  console.log(kind, offset, data);
}
const next = parse('^XA^XZ', { syntax });

Input accepts the same string, Buffer, and Uint8Array types as rendering. Each element has a kind (before_first_command, format_command, control_command, or control_character), a byte offset, and an owned data Buffer. Concatenating all element buffers reproduces the input exactly, including whitespace, unknown commands, non-UTF-8 bytes, and binary payloads. Offsets in string input count UTF-8 bytes, not JavaScript characters.

options.syntax accepts optional numeric bytes formatPrefix, controlPrefix, and delimiter (0–255), defaulting to 94 (^), 126 (~), and 44 (comma). The result includes the final syntax after in-stream changes. You can carry it into the next complete stream; this API does not buffer partial network chunks.

A framing failure throws ParseError with offset and kind properties; no partial result is returned. Parsing does not validate operands, decompress downloads, or authorize commands for a printer. It does not apply rendering budgets such as the 1 MiB input limit. See parser coverage for framing rules and limitations.

External and inline fonts

Use resolveFont to supply quadratic TrueType bytes for virtual printer filenames selected by ^A@ or assigned to an ID by ^CW:

import { readFileSync } from 'node:fs';
import { render, resolveRomFont } from '@codyps/zpl';

const brand = readFileSync('brand.ttf');
const result = render('^CWZ,R:BRAND.TTF^XA^FO20,20^AZN,32,24^FDHello^FS^XZ', {
  resolveFont(name) {
    if (name === 'R:BRAND.TTF') return brand;
    return resolveRomFont(name); // Explicitly retain bundled ROM lookup.
  },
});

The synchronous callback receives a validated uppercase device:path; omitted devices become R:. It runs once per resolved name per render. Return a Buffer or Uint8Array; the renderer copies and validates the bytes. Supplying a callback replaces the default named-font resolver, matching Rust's Fonts::set_resolver. Returning null or undefined means unresolved and fails the render, even for bundled ROM names. With no callback, bundled ROM lookup remains the default.

To retain ROM lookup explicitly, return resolveRomFont(name). This helper returns an opaque, reusable face handle for a known bundled font, null for an unknown valid name, and throws for an invalid path. It is case-insensitive, requires no disposal, and can also map a virtual filename to another ROM face. Thrown errors fail the render at the selecting command. Promises are rejected; load fonts before rendering if your storage API is asynchronous. No host files are opened automatically, and font registrations never persist between renders.

In-job downloads take precedence over the resolver. External fonts support the Rust engine's quadratic TrueType outlines and native hinting, with a 16 MiB per-font ceiling. CFF/CFF2, variable fonts, and collections are unsupported. Missing glyphs fail rather than falling back to a different face. The callback resolves filenames used by ^A@/^CW; it does not intercept resident font-ID selections. Custom bitmap glyph-provider callbacks remain unexposed; resolveRomFont() handles refer to the bundled ROM faces.

Inline ~DB, ~DT, ~DU, and ~DY font downloads use the Rust renderer's existing support. The default 1 MiB input budget, rather than a Wasm argument size limit, can reject large downloads. Raise it explicitly for trusted jobs:

const job = readFileSync('label-with-font.zpl');
const result = render(job, {
  limits: {
    inputBytes: 32 * 1024 * 1024,
    fontBytes: 16 * 1024 * 1024,
  },
});

inputBytes bounds both raw input and expanded stored formats separately. fontBytes bounds total decoded inline downloads, including replacements and bitmap allocation overhead; its default is 16 MiB. Both accept nonnegative integers up to 4294967295. Hex downloads need roughly twice their decoded size in the input budget. Use byte input for binary downloads. Other renderer/output budgets and Wasm memory constraints still apply; raising these limits does not disable them.

Invalid options, missing labels, unsupported commands, and resource-limit failures throw errors. The Rust renderer's default input, label, geometry, and pixel budgets apply. Rendering is synchronous; use Node worker threads for substantial jobs in servers. Wasm result memory is freed internally; callers need no disposal.

This package exposes command framing, rendering, and output encoding. It does not yet expose scene objects, bitmap glyph-provider callbacks, or individual compatibility flags. See renderer coverage, barcode support, and printer accuracy.

Links and license

Licensed under the Open Software License 3.0.