Media

Barcodes

EAN-13, EAN-8, UPC-A, Code 128 and Code 39 barcodes as SVG or PNG, with the digits underneath and check digits worked out.

Version 1.0.0 · 4 functions · 2 crates

#barcodes#labels#images#shop

Functions

What use barcode.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use barcode_svg, barcode_png from barcode (Quillo 0.6).

barcode_svg(kind: str, data: str, options: any) → stror an error

A barcode as an SVG image: kind is "ean13", "ean8", "upca", "code128" or "code39"; for EAN and UPC the check digit is added when left out, and checked when given. options (or null): height of the bars in pixels (80), module the width of the thinnest bar (2), text false to leave out the digits, color and background as "#rrggbb".

barcode_png(kind: str, data: str, path: str, options: any) → anyor an error

The same barcode as a PNG file at path (folders made), without the text: for labels and printers. Its { width, height } in pixels.

barcode_check_digit(digits: str) → stror an error

The digits with their check digit added: 12 digits give an EAN-13, 11 a UPC-A, 7 an EAN-8. For numbering your own products.

barcode_valid(kind: str, data: str) → bool

Whether data can be a barcode of kind (and, for EAN and UPC with all their digits, whether the check digit is right): for checking a product form.

Get it

$ quillo install barcode               # barcode.qwer into this folder$ quillo install barcode --example     # and its example, barcode.qwe

Or with curl:

$ curl -O https://quillolang.org/plugins/files/barcode.qwer$ curl -O https://quillolang.org/plugins/files/barcode.qwe

Then, in your program, all of it or only what you call:

main.qwe
use barcode.qweruse barcode_svg, barcode_png from barcode      // Quillo 0.6: only these

It uses these crates, which cargo fetches the first time the plugin is built:

[dependencies]
barcoders = { version = "2", default-features = false, features = ["std"] }image = { version = "0.25", default-features = false, features = ["png"] }

Example

barcode.qwe
// Product labels with barcodes, and a check of the numbers a form is given.////   quillo run barcode.qwe      then open http://localhost:8080/labeluse "barcode.qwer"route GET /ean.svg ? { code: str = "400638133393" } =    respond(200, barcode_svg("ean13", code, null)?, { "content-type": "image/svg+xml" })route GET /parcel.svg ? { id: str = "PKG-2026-0042" } =    respond(200, barcode_svg("code128", id, { height: 60, module: 2, color: "#1b1630" })?, { "content-type": "image/svg+xml" })route GET /label = <html><body style="font-family: sans-serif">    <h2>Dark roast, 250 g</h2>    <div>{raw(barcode_svg("ean13", "400638133393", { height: 50 })?)}</div>    <div>{raw(barcode_svg("code39", "SKU-778", { module: 1, height: 40 })?)}</div></body></html>route GET /check = {    full: barcode_check_digit("400638133393")?,    upc: barcode_check_digit("03600029145")?,    good: barcode_valid("ean13", "4006381333931"),    typo: barcode_valid("ean13", "4006381333932"),    png: barcode_png("upca", "036000291452", "data/labels/upc.png", { height: 60 })?,}
$ quillo run barcode.qwe$ curl 'localhost:8080/check'$ curl 'localhost:8080/ean.svg'$ curl 'localhost:8080/parcel.svg'

Source

barcode.qwer
//! Barcodes for products, parcels and tickets: EAN-13, EAN-8, UPC-A, Code 128 and Code 39 as SVG or PNG, with the digits underneath, and check digits worked out.//! version: 1.0.0//!//! [dependencies]//! barcoders = { version = "2", default-features = false, features = ["std"] }//! image = { version = "0.25", default-features = false, features = ["png"] }use barcoders::sym::{code128::Code128, code39::Code39, ean13::EAN13, ean8::EAN8};use serde_json::Value;fn digits_only(s: &str) -> bool {    !s.is_empty() && s.bytes().all(|b| b.is_ascii_digit())}// The EAN/UPC check digit: weights 3 and 1 from the right.fn check_digit(body: &str) -> u32 {    let sum: u32 = body.bytes().rev().enumerate().map(|(i, b)| (b - b'0') as u32 * if i % 2 == 0 { 3 } else { 1 }).sum();    (10 - sum % 10) % 10}fn kind_of(kind: &str) -> String {    kind.to_lowercase().replace(['-', '_', ' '], "")}// The bars, one 0 or 1 per module, and the text to print under them.fn bars(kind: &str, data: &str) -> Result<(Vec<u8>, String), String> {    let data = data.trim();    let bad = |e: barcoders::error::Error| format!("`{}` cannot be a {} barcode: {}", data, kind, e);    match kind_of(kind).as_str() {        "ean13" | "upca" => {            let want = if kind_of(kind) == "upca" { 11 } else { 12 };            if !digits_only(data) || !(data.len() == want || data.len() == want + 1) {                return Err(format!("{} takes {} digits, or {} with the check digit", kind, want, want + 1));            }            let full = if kind_of(kind) == "upca" { format!("0{}", data) } else { data.to_string() };            Ok((EAN13::new(&full).map_err(bad)?.encode(), with_check(data, want)))        }        "ean8" => {            if !digits_only(data) || !(data.len() == 7 || data.len() == 8) {                return Err("EAN-8 takes 7 digits, or 8 with the check digit".into());            }            Ok((EAN8::new(data).map_err(bad)?.encode(), with_check(data, 7)))        }        "code128" => {            if data.is_empty() || data.len() > 80 || !data.bytes().all(|b| (32..127).contains(&b)) {                return Err("Code 128 takes 1 to 80 printable ASCII characters".into());            }            // Digits in pairs pack tighter in character set C; the rest uses set B.            let set = if digits_only(data) && data.len() % 2 == 0 && data.len() >= 4 { 'Ć' } else { 'Ɓ' };            Ok((Code128::new(format!("{}{}", set, data)).map_err(bad)?.encode(), data.to_string()))        }        "code39" => {            let up = data.to_uppercase();            if up.is_empty() || up.len() > 60 || !up.chars().all(|c| c.is_ascii_alphanumeric() || " -.$/+%".contains(c)) {                return Err("Code 39 takes 1 to 60 of A-Z, 0-9, space and - . $ / + %".into());            }            Ok((Code39::new(&up).map_err(bad)?.encode(), up))        }        _ => Err(format!("`{}` is not one of ean13, ean8, upca, code128, code39", kind)),    }}fn with_check(data: &str, body_len: usize) -> String {    if data.len() == body_len { format!("{}{}", data, check_digit(data)) } else { data.to_string() }}struct Look {    height: u32,    module: u32,    text: bool,    color: String,    background: String,}fn look(o: &Value) -> Look {    let hex = |k: &str, d: &str| o.get(k).and_then(Value::as_str).filter(|s| s.starts_with('#') && s.len() <= 9 && s[1..].chars().all(|c| c.is_ascii_hexdigit())).unwrap_or(d).to_string();    Look {        height: o.get("height").and_then(Value::as_u64).unwrap_or(80).clamp(10, 1000) as u32,        module: o.get("module").and_then(Value::as_u64).unwrap_or(2).clamp(1, 20) as u32,        text: o.get("text").and_then(Value::as_bool).unwrap_or(true),        color: hex("color", "#000000"),        background: hex("background", "#ffffff"),    }}const QUIET: u32 = 10;/// A barcode as an SVG image: `kind` is "ean13", "ean8", "upca", "code128" or "code39"; for EAN and UPC the check digit is added when left out, and checked when given./// `options` (or null): `height` of the bars in pixels (80), `module` the width of the thinnest bar (2), `text` false to leave out the digits, `color` and `background` as "#rrggbb".pub fn barcode_svg(kind: &str, data: &str, options: serde_json::Value) -> Result<String, String> {    let (b, text) = bars(kind, data)?;    let l = look(&options);    let w = (b.len() as u32 + 2 * QUIET) * l.module;    let th = if l.text { l.module * 9 + 6 } else { 0 };    let h = l.height + th;    let mut out = format!(r#"<svg xmlns="http://www.w3.org/2000/svg" width="{w}" height="{h}" viewBox="0 0 {w} {h}" shape-rendering="crispEdges"><rect width="{w}" height="{h}" fill="{}"/><path fill="{}" d=""#, l.background, l.color);    let mut i = 0;    while i < b.len() {        if b[i] == 1 {            let start = i;            while i < b.len() && b[i] == 1 {                i += 1;            }            out += &format!("M{} 0h{}v{}h-{}z", (start as u32 + QUIET) * l.module, (i - start) as u32 * l.module, l.height, (i - start) as u32 * l.module);        } else {            i += 1;        }    }    out += "\"/>";    if l.text {        let esc = text.replace('&', "&amp;").replace('<', "&lt;");        out += &format!(r#"<text x="{}" y="{}" font-family="monospace" font-size="{}" text-anchor="middle" fill="{}">{}</text>"#, w / 2, h - 3, l.module * 8, l.color, esc);    }    out += "</svg>";    Ok(out)}/// The same barcode as a PNG file at `path` (folders made), without the text: for labels and printers. Its `{ width, height }` in pixels.pub fn barcode_png(kind: &str, data: &str, path: &str, options: serde_json::Value) -> Result<Value, String> {    let (b, _) = bars(kind, data)?;    let l = look(&options);    let rgb = |h: &str| {        let v = u32::from_str_radix(&h[1..7.min(h.len())], 16).unwrap_or(0);        image::Rgb([(v >> 16) as u8, (v >> 8) as u8, v as u8])    };    let (w, h) = ((b.len() as u32 + 2 * QUIET) * l.module, l.height);    let (fg, bg) = (rgb(&l.color), rgb(&l.background));    let img = image::RgbImage::from_fn(w, h, |x, _| {        let m = (x / l.module) as i64 - QUIET as i64;        if m >= 0 && (m as usize) < b.len() && b[m as usize] == 1 { fg } else { bg }    });    if let Some(dir) = std::path::Path::new(path).parent().filter(|d| !d.as_os_str().is_empty()) {        std::fs::create_dir_all(dir).map_err(|e| format!("{}: {}", dir.display(), e))?;    }    img.save_with_format(path, image::ImageFormat::Png).map_err(|e| format!("{}: {}", path, e))?;    Ok(serde_json::json!({ "width": w, "height": h }))}/// The digits with their check digit added: 12 digits give an EAN-13, 11 a UPC-A, 7 an EAN-8. For numbering your own products.pub fn barcode_check_digit(digits: &str) -> Result<String, String> {    let d = digits.trim();    if !digits_only(d) || ![7, 11, 12].contains(&d.len()) {        return Err("give 7, 11 or 12 digits".into());    }    Ok(format!("{}{}", d, check_digit(d)))}/// Whether `data` can be a barcode of `kind` (and, for EAN and UPC with all their digits, whether the check digit is right): for checking a product form.pub fn barcode_valid(kind: &str, data: &str) -> bool {    bars(kind, data).is_ok()}
Thread
65Useful? Like it, or add to its thread below.

Thread 0 posts

On Barcodes: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.