zpl_renderer

Offline ZPL to PNG/PDF renderer for Ruby and Rails.

Add the gem, render labels, and get barcodes/QR codes that scanners can decode. No Labelary account, no network calls, no printer hardware required.

Install

# Gemfile
gem "zpl_renderer"
bundle install

Precompiled renderer binaries ship for common platforms (Linux/macOS/Windows). If your platform is missing a binary, build it once:

cd renderer && cargo build --release
bundle exec rake build_renderer

Or point to a binary explicitly:

export ZPL_RENDERER_BINARY=/absolute/path/to/zpl-renderer

Quick start

require "zpl_renderer"

zpl = <<~ZPL
  ^XA
  ^FO50,50^A0N,40,40^FDHello^FS
  ^FO50,120^BY3
  ^BCN,100,Y,N,N
  ^FDTRACK123456789^FS
  ^FO50,280^BQN,2,6
  ^FDQA,https://example.com/track/ABC123^FS
  ^XZ
ZPL

png = ZplRenderer.render(zpl, format: :png, width: 4, height: 6, dpi: 203)
File.binwrite("label.png", png)

pdf = ZplRenderer.render(zpl, format: :pdf, width: 4, height: 6, dpi: 300)
File.binwrite("label.pdf", pdf)

Rails

png = ZplRenderer.render(@label.zpl, format: :png, width: 4, height: 6, dpi: 203)
send_data png,
          type: ZplRenderer.content_type(:png),
          disposition: "inline",
          filename: "label.png"

See examples/rails_controller.rb.

CLI

zpl-render label.zpl -o label.png --dpi 203 --width 4 --height 6
zpl-render label.zpl -o label.pdf --format pdf --dpi 300
zpl-render --validate-only label.zpl

Options

Option Default Notes
format :png :png or :pdf
width / height 4 / 6 Inches by default
unit :inches :inches or :mm
dpi 203 203, 300, or 600
dpmm derived 8, 12, or 24 (overrides dpi)
index 0 Multi-label ^XA...^XZ selection
timeout 30 Seconds
strict true Raise on unsupported commands
clip_to_print_width false Emulate printer ^PW clipping instead of widening

Canvas size is printer-dot accurate: ceil(mm * dpmm).

Accuracy and barcodes

This gem targets common shipping-label ZPL, not every Zebra firmware quirk.

Supported first-release command set and barcode guidance live in COMPATIBILITY.md.

Carrier quirks are sanitized automatically before render: bad field origins such as ^FO464,--, and ^PW values narrower than the canvas that would clip rotated text at the right edge. Inspect repairs with ZplRenderer.sanitize(zpl, canvas_dots: 813).

Barcode/QR payloads are independently verified in CI with a ZXing-based decoder:

hits = ZplRenderer.decode_png(png)
hits.map(&:text)
# => ["TRACK123456789", "https://example.com/track/ABC123"]

Scanner tips

  • Prefer ^BY3 (or wider) at 203 DPI
  • Prefer QR magnification >= 4 for handheld scanners
  • Keep quiet zones clear of text/graphics
  • Avoid clipping barcodes with tiny ^PW / label widths

Development

bundle install
source "$HOME/.cargo/env"   # if needed
bundle exec rake build_renderer
bundle exec rake golden
bundle exec rspec
cd renderer && cargo test

Fixtures under spec/fixtures/ must stay synthetic. Do not commit real customer or production ZPL/PNG samples.

Packaging

bundle exec rake build_renderer
gem build zpl_renderer.gemspec

Platform CI builds native binaries for Linux/macOS/Windows and packages them under vendor/bin/<rust-triple>/.

Publishing to RubyGems is intentional and separate from local gem builds.

License

MIT. Renderer engine depends on labelize (MIT).