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
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 errorA 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 errorThe 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 errorThe 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) → boolWhether 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.qweOr with curl:
$ curl -O https://quillolang.org/plugins/files/barcode.qwer$ curl -O https://quillolang.org/plugins/files/barcode.qweThen, in your program, all of it or only what you call:
use barcode.qweruse barcode_svg, barcode_png from barcode // Quillo 0.6: only theseIt uses these crates, which cargo fetches the first time the plugin is built:
barcoders = { version = "2", default-features = false, features = ["std"] }image = { version = "0.25", default-features = false, features = ["png"] }Example
// 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
//! 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('&', "&").replace('<', "<"); 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()}Related
qr_read.qwer · 1.0.0QR codes and barcodes read from pictures: QR, Data Matrix, PDF417, EAN-13, UPC-A, Code 128 and more, for tickets, parcels and products.8 views · 7 downloadsAnimated GIF gif.qwer · 1.0.0Animated GIFs made from a list of pictures with the delay and size you choose, and GIFs read back: frames, size and length.6 views · 7 downloadsASCII art ascii_art.qwer · 1.0.0Pictures turned into ASCII art: characters from light to dark, plain for terminals and emails, or coloured as HTML.6 views · 10 downloadsBlurHash blurhash.qwer · 1.0.0A photo as a short BlurHash, shown blurred while the real one loads, and back as a small PNG data URL.7 views · 6 downloadsCaptions on images caption.qwer · 1.0.0Text drawn on pictures with a built-in font: captions at the top, middle or bottom, wrapped to fit, with an outline or a band behind them, meme-style top and bottom lines, and text measured before drawing.7 views · 7 downloadsCollage collage.qwer · 1.0.0Several pictures made into one: a grid of equal cells with gaps, or justified rows that keep each picture's shape, on your background.7 views · 7 downloadsThread 0 posts
On Barcodes: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.