smart-package-tracker 0.4.0

Generate package tracking IDs, render them as Code 128 barcodes (PNG and SVG), and read them back out of images
Documentation
//! Mint a tracking ID and write a shipping-label barcode as PNG and SVG.
//!
//! Run with: `cargo run --example label`

use smart_package_tracker::scan;
use smart_package_tracker::symbology::{Ecc, Qr};
use smart_package_tracker::{
    Barcode, Checksum, Color, IdGenerator, Length, QuietZone, RenderOptions, TrackingId,
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // A production-grade policy: 64 bits of entropy plus a check character.
    let generator = IdGenerator::builder()
        .prefix("PKG")
        .entropy_bits(64)
        .checksum(Checksum::Iso7064Mod37_36)
        .build()?;

    let id = generator.generate()?;
    println!("tracking id: {id}");

    let barcode = Barcode::code128(&id)?;

    // 300 dpi thermal label printer, 13 mil X-dimension.
    let options = RenderOptions::builder()
        .module_width(Length::Mils(13.0))
        .height(Length::Mm(25.0))
        .quiet_zone(QuietZone::Standard)
        .dpi(300)
        .colors(Color::BLACK, Color::WHITE)
        .human_readable(true)
        .build()?;

    let layout = options.layout(barcode.symbol())?;
    println!(
        "label size: {}x{} px ({:.1}x{:.1} mm at {} dpi)",
        layout.width_px,
        layout.height_px,
        Length::Px(f64::from(layout.width_px)).to_mm(options.dpi()),
        Length::Px(f64::from(layout.height_px)).to_mm(options.dpi()),
        options.dpi()
    );

    barcode.to_png_file("label.png", &options)?;
    barcode.to_svg_file("label.svg", &options)?;
    println!("wrote label.png and label.svg");

    // The barcode decodes back to the identifier it was built from.
    assert_eq!(barcode.decode()?, id.as_str());
    println!("round-trip verified");

    // And the *printed* label reads back, which is a stronger claim: this goes
    // through the PNG decoder, the binariser and the scan lines rather than
    // through the symbol still sitting in memory.
    let scanned = scan::scan_png_file("label.png")?;
    let recovered = TrackingId::parse(scanned.payload())?;
    println!("scanned label.png: {recovered} ({})", scanned.kind());
    assert_eq!(recovered, id);

    // The same identifier as a QR code. Note that nothing about the rendering
    // setup changes: a QR symbol is a square bit grid where Code 128 is a
    // single row, and the renderers consume the grid either way.
    let qr = Barcode::qr_with(Qr::new().ecc(Ecc::Quartile), &id)?;
    let qr_options = RenderOptions::builder()
        .module_width(Length::Mm(0.5))
        .dpi(300)
        .human_readable(false)
        .build()?;

    let qr_layout = qr_options.layout(qr.symbol())?;
    println!(
        "qr: {}x{} modules, {}x{} px",
        qr.symbol().modules().width(),
        qr.symbol().modules().height(),
        qr_layout.width_px,
        qr_layout.height_px
    );

    qr.to_png_file("label-qr.png", &qr_options)?;
    qr.to_svg_file("label-qr.svg", &qr_options)?;
    println!("wrote label-qr.png and label-qr.svg");

    Ok(())
}