zpl-forge 0.3.0

A fast, memory-safe ZPL (Zebra Programming Language) parser and renderer.
Documentation
# ZPL-Forge

A fast, memory-safe ZPL (Zebra Programming Language) parser and renderer for Rust. It converts ZPL code into PNG images or native, selectable vector PDFs.

[![Crates.io](https://img.shields.io/crates/v/zpl_forge.svg)](https://crates.io/crates/zpl_forge)
[![Docs.rs](https://docs.rs/zpl-forge/badge.svg)](https://docs.rs/zpl-forge)

## The Purpose

ZPL-Forge provides a quick and simple alternative for creating documents like **shipping guides, delivery receipts, and tickets**. It is optimized for use cases where speed and ease of integration are preferred over extreme document complexity.

## The Results

|                                             Standard Complex Label                                             |                                                 Custom Image Extensions                                                  |
| :------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------: |
| <img src="https://raw.githubusercontent.com/rafael-arreola/zpl-forge/main/examples/test_01.png" width="300" /> | <img src="https://raw.githubusercontent.com/rafael-arreola/zpl-forge/main/examples/test_image_color2.png" width="300" /> |

Check out the [**Visual Documentation (EXAMPLES.md)**](https://github.com/rafael-arreola/zpl-forge/blob/main/examples/EXAMPLES.md) for more ready-to-run code samples and their generated output images.

## Performance

ZPL-Forge is engineered to deliver enterprise-grade performance and ultra-low latency, making it perfect for both instant single-label previews and high-throughput bulk generation.

**Single Label Render Times (Measured in release mode using the embedded 64 KB Iosevka Term Slab font):**

- **Routing/Dispatch Label (`test_02`):**
  - PNG Output (`PngBackend`): **0.63 ms**
  - Native PDF Output (`PdfNativeBackend`): **2.50 ms**
- **Standard Shipping Label (`test_01`):**
  - PNG Output (`PngBackend`): **15.62 ms**
  - Native PDF Output (`PdfNativeBackend`): **2.58 ms**

**Massive Bulk Batching (Measured in release mode):**

- **Native Vector PDF (1,000 Combined Labels):** **97.24 ms** total time (**0.1 ms / page**), producing a compact, searchable file of only **0.82 MB** (a throughput of over **10,000 pages per second**!).

## Installation

Add this to your `Cargo.toml`:

```toml
[dependencies]
zpl-forge = "0.3.0"
```

### Cargo Features

| Feature   | Default | Enables                                              |
| :-------- | :-----: | :--------------------------------------------------- |
| `png`     || `PngBackend` raster output (`image`, `imageproc`)    |
| `pdf`     || `PdfNativeBackend` vector output (`lopdf`, `flate2`) |
| `tracing` || Internal debug logging via the `tracing` crate       |

If you only need one output format, disable default features to cut compile time and binary size:

```toml
[dependencies]
zpl-forge = { version = "0.3.0", default-features = false, features = ["pdf"] }
```

## Quick Start

The library provides two backends for different needs.

### 1. Render to PNG

Best for web previews or raster printing. Gated under the `png` cargo feature.

```rust
use zpl_forge::{ZplEngine, PngBackend};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let zpl = "^XA^FO50,50^A0N,50,50^FDHello World^FS^XZ";

    // Parse the ZPL and define label constraints (4x4 inches at 203 DPI)
    let engine = ZplEngine::new(
        zpl,
        Unit::Inches(4.0),
        Unit::Inches(4.0),
        Resolution::Dpi203,
    )?;

    // Create the PNG rendering backend
    let backend = PngBackend::new();

    // Render the label (returns a Vec<u8> of raw PNG bytes)
    let png_bytes = engine.render(backend, &HashMap::new())?;

    // Save the bytes to a file
    std::fs::write("label.png", png_bytes)?;
    Ok(())
}
```

### 2. Render to Native Vector PDF

Outputs selectable text and native vector graphics. Ultra-fast, extremely small file size, and requires zero rasterization. Gated under the `pdf` cargo feature.

```rust
use zpl_forge::{ZplEngine, PdfNativeBackend};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let zpl = "^XA^FO50,50^A0N,50,50^FDSelectable Text!^FS^XZ";

    // Parse the ZPL and define label constraints (4x4 inches at 203 DPI)
    let engine = ZplEngine::new(
        zpl,
        Unit::Inches(4.0),
        Unit::Inches(4.0),
        Resolution::Dpi203,
    )?;

    // Create the native vector PDF rendering backend
    let backend = PdfNativeBackend::new();

    // Render the label (returns a Vec<u8> of raw PDF bytes)
    let pdf_bytes = engine.render(backend, &HashMap::new())?;

    // Save the bytes to a file
    std::fs::write("label.pdf", pdf_bytes)?;
    Ok(())
}
```

## Advanced Usage

### Templating (Variables)

Inject dynamic data into your ZPL without extra allocations. Simply use the `{{variable_name}}` syntax in your ZPL code and pass a variables map to `.render()`.

```rust
use zpl_forge::{ZplEngine, PdfBackend};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let zpl = "^XA^FO50,50^A0N,50,50^FDHello {{NAME}}^FS^XZ";
    let engine = ZplEngine::new(
        zpl,
        Unit::Inches(4.0),
        Unit::Inches(2.0),
        Resolution::Dpi203,
    )?;

    // Populate the template variables
    let mut variables = HashMap::new();
    variables.insert("NAME".to_string(), "ZPL-Forge".to_string());

    // Render with variables injected
    let png_bytes = engine.render(PngBackend::new(), &variables)?;
    std::fs::write("hello.png", png_bytes)?;
    Ok(())
}
```

### Conditional Rendering (`^IFC`)

A custom extension to render elements only if a variable matches a specific value.

```rust
let zpl = "^XA^FO50,50^IFCuser_type,admin^A0N,50,50^FDAdmin Only Area^FS^XZ";
// "Admin Only Area" will only render if a variable ("user_type", "admin") is passed to render.
```

### Multi-Page PDF Batching & Compression

You can combine multiple physical labels into a single multi-page PDF document simply by concatenating multiple `^XA...^XZ` blocks in your ZPL input. The `PdfNativeBackend` automatically treats each block as a separate page, drawing it natively. You can also customize the zlib compression level using `flate2::Compression`.

```rust
use std::collections::HashMap;
use zpl_forge::{ZplEngine, Unit, Resolution};
use zpl_forge::forge::pdf_native::PdfNativeBackend;
use flate2::Compression;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Generate concatenated ZPL representing a multi-page document
    let mut batch_zpl = String::new();
    for i in 1..=3 {
        batch_zpl.push_str(&format!(
            "^XA^FO50,50^A0N,40,40^FDThis is native PDF page {i}^FS^XZ\n"
        ));
    }

    let engine = ZplEngine::new(
        &batch_zpl,
        Unit::Inches(4.0),
        Unit::Inches(3.0),
        Resolution::Dpi203,
    )?;

    // Instantiate backend with Best compression & document title metadata
    let backend = PdfNativeBackend::new()
        .with_compression(Compression::best())
        .with_title("Batch PDF Generation");

    // Renders all pages in one extremely efficient vector PDF document
    let pdf_bytes = engine.render(backend, &HashMap::new())?;
    std::fs::write("batch.pdf", pdf_bytes)?;
    Ok(())
}
```

## Supported ZPL Commands

| Command | Name             | Parameters    | Description                                                                                                 |
| :------ | :--------------- | :------------ | :---------------------------------------------------------------------------------------------------------- |
| `^A`    | Font Spec        | `f,o,h,w`     | Specifies font (A..Z, 0..9), orientation (N, R, I, B — text rotation supported), height, and width in dots. |
| `^B2`   | Interleaved 2/5  | `o,h,f,g,e`   | Interleaved 2 of 5 Barcode (cartons, ITF-14).                                                               |
| `^B3`   | Code 39          | `o,e,h,f,g`   | Code 39 Barcode.                                                                                            |
| `^B7`   | PDF417           | `o,h,s,c,r,t` | PDF417 two-dimensional Barcode.                                                                             |
| `^BA`   | Code 93          | `o,h,f,g,e`   | Code 93 Barcode.                                                                                            |
| `^BC`   | Code 128         | `o,h,f,g,e,m` | Code 128 Barcode.                                                                                           |
| `^BE`   | EAN-13           | `o,h,f,g`     | EAN-13 Barcode (retail).                                                                                    |
| `^BQ`   | QR Code          | `o,m,s,e,k`   | QR Code (Model 1 or 2).                                                                                     |
| `^BU`   | UPC-A            | `o,h,f,g,e`   | UPC-A Barcode (retail).                                                                                     |
| `^BX`   | Data Matrix      | `o,h,s,c,r`   | Data Matrix (ECC 200) two-dimensional Barcode.                                                              |
| `^BY`   | Barcode Default  | `w,r,h`       | Sets default values for barcodes (module width, ratio, and height).                                         |
| `^CF`   | Change Def. Font | `f,h,w`       | Changes the default alphanumeric font.                                                                      |
| `^FB`   | Field Block      | `w,l,s,j,i`   | Wraps text in a block: width, max lines, line spacing, justification (L/C/R), indent. `\&` breaks lines.    |
| `^FD`   | Field Data       | `d`           | Data to print in the current field.                                                                         |
| `^FO`   | Field Origin     | `x,y`         | Sets the top-left corner of the field.                                                                      |
| `^FR`   | Field Reverse    | N/A           | Inverts the field color (white on black).                                                                   |
| `^FS`   | Field Separator  | N/A           | Indicates the end of a field definition.                                                                    |
| `^FT`   | Field Typeset    | `x,y`         | Sets field position relative to the text baseline.                                                          |
| `^GB`   | Graphic Box      | `w,h,t,c,r`   | Draws a box, line, or rectangle with rounded corners.                                                       |
| `^GC`   | Graphic Circle   | `d,t,c`       | Draws a circle by specifying its diameter.                                                                  |
| `^GD`   | Graphic Diagonal | `w,h,t,c,o`   | Draws a diagonal line (`/` or `\`).                                                                         |
| `^GE`   | Graphic Ellipse  | `w,h,t,c`     | Draws an ellipse.                                                                                           |
| `^GF`   | Graphic Field    | `c,b,f,p,d`   | Renders a bitmap image (supports A/Hex type compression).                                                   |
| `^XA`   | Start Format     | N/A           | Indicates the start of a label. Multiple `^XA...^XZ` blocks become pages in the native PDF backend.         |
| `^XZ`   | End Format       | N/A           | Indicates the end of a label.                                                                               |

## Custom Commands (Extensions)

| Command | Name         | Parameters | Description                                                                                                                                                   |
| :------ | :----------- | :--------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `^GIC`  | Custom Image | `w,h,d`    | Renders a color image. **w** and **h** define size. **d** is the binary (PNG/JPG) in **Base64**.                                                              |
| `^GLC`  | Line Color   | `c`        | Sets the color for graphic elements in hexadecimal format (e.g., `#FF0000`).                                                                                  |
| `^GTC`  | Text Color   | `c`        | Sets the color for text fields in hexadecimal format (e.g., `#0000FF`).                                                                                       |
| `^IFC`  | Cond. Render | `var,val`  | **If Condition Custom:** Evaluates if a variable matches a specific value. If false, the field won't be rendered. Scope limited up to the next `^FS` command. |

## Security and Limits

ZPL-Forge imposes reasonable limits to prevent resource exhaustion from malformed or malicious ZPL inputs.

- **Maximum Document Size:** Bounded to prevent memory overflow on excessively large labels.
- **Graphic Field Maximums:** Prevents malicious `^GF` commands from allocating unlimited memory.
- **Maximum Text Size:** Prevents excessively large font sizes.

## License

Dual-licensed under either:

- MIT License ([LICENSE-MIT]LICENSE-MIT or http://opensource.org/licenses/MIT)
- Apache License, Version 2.0 ([LICENSE-APACHE]LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)

At your option.