Media

QR and barcode reader

QR codes and barcodes read from pictures: QR, Data Matrix, PDF417, EAN-13, UPC-A, Code 128 and more, for tickets, parcels and products.

Version 1.0.0 · 3 functions · 2 crates

#qr#barcodes#images#uploads

Functions

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

qr_read(path: str) → stror an error

The text of the QR code in the picture (PNG, JPEG, WebP, GIF), or an error when there is none it can read.

qr_read_any(path: str) → anyor an error

The first code of any kind in the picture: { text, format, points }, format like "qr", "ean13", "upca", "code128", "datamatrix"; points the corners or ends it was found at, in pixels. Null when there is none.

qr_read_all(path: str) → listor an error

Every code in the picture, each { text, format, points }: a sheet of labels, a box with several barcodes. Empty when there is none.

Get it

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

Or with curl:

$ curl -O https://quillolang.org/plugins/files/qr_read.qwer$ curl -O https://quillolang.org/plugins/files/qr_read.qwe$ curl -O https://quillolang.org/plugins/files/qr_read_sample.png

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

main.qwe
use qr_read.qweruse qr_read, qr_read_any from qr_read      // Quillo 0.6: only these

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

[dependencies]
rxing = { version = "0.7", default-features = false }image = { version = "0.25", default-features = false, features = ["png", "jpeg", "webp", "gif"] }

Example

qr_read.qwe
// A ticket scanner: a photo of a QR code sent from a phone is read, and its link checked.////   quillo run qr_read.qwe      then open http://localhost:8080/scanuse "qr_read.qwer"route GET /scan = { text: qr_read("qr_read_sample.png")?, code: qr_read_any("qr_read_sample.png")? }route GET /all = qr_read_all("qr_read_sample.png")? | map((c) => `${c.format}: ${c.text}`)route POST /scan {    let f = request.files.photo ?? reply 400 "Send a photo of the code"    let path = save_upload(f, `data/scans/${uuid()}`)?    let code = qr_read_any(path)    delete_file(path)    if is_err(code) || code == null { reply 422 "No code found: try again closer, with more light" }    { text: code.text, format: code.format, ours: starts_with(code.text, "https://quillolang.org/") }}
$ quillo run qr_read.qwe$ curl 'localhost:8080/scan'$ curl 'localhost:8080/all'

Source

qr_read.qwer
//! QR codes and barcodes read from pictures: QR, Data Matrix, Aztec, PDF417, EAN-13, EAN-8, UPC-A, UPC-E, Code 128, Code 39, ITF and more, for tickets, parcels and products.//! version: 1.0.0//!//! [dependencies]//! rxing = { version = "0.7", default-features = false }//! image = { version = "0.25", default-features = false, features = ["png", "jpeg", "webp", "gif"] }use rxing::{BarcodeFormat, DecodeHints, RXingResult};use serde_json::{json, Value};use std::collections::HashSet;const MAX_SIDE: u32 = 3000;// The picture in grey, made smaller when it is very large (codes in photos read fine at 3000 px).fn luma(path: &str) -> Result<(Vec<u8>, u32, u32), String> {    let mut r = image::ImageReader::open(path).map_err(|e| format!("{}: {}", path, e))?.with_guessed_format().map_err(|e| format!("{}: {}", path, e))?;    let mut l = image::Limits::default();    l.max_image_width = Some(12_000);    l.max_image_height = Some(12_000);    l.max_alloc = Some(512 * 1024 * 1024);    r.limits(l);    let mut img = r.decode().map_err(|e| format!("{}: {}", path, e))?;    if img.width().max(img.height()) > MAX_SIDE {        img = img.resize(MAX_SIDE, MAX_SIDE, image::imageops::FilterType::Triangle);    }    let g = img.to_luma8();    let (w, h) = g.dimensions();    Ok((g.into_raw(), w, h))}fn format_name(f: &BarcodeFormat) -> String {    match f {        BarcodeFormat::QR_CODE => "qr".into(),        f => f.to_string().replace(' ', ""),    }}fn record(r: &RXingResult) -> Value {    let pts: Vec<Value> = r.getPoints().iter().map(|p| json!([p.x.round(), p.y.round()])).collect();    json!({ "text": r.getText(), "format": format_name(r.getBarcodeFormat()), "points": pts })}fn hints(formats: Option<HashSet<BarcodeFormat>>) -> DecodeHints {    let mut h = DecodeHints::default();    h.TryHarder = Some(true);    h.PossibleFormats = formats;    h}fn catch<T>(f: impl FnOnce() -> T + std::panic::UnwindSafe) -> Result<T, String> {    std::panic::catch_unwind(f).map_err(|_| "the picture could not be read".to_string())}/// The text of the QR code in the picture (PNG, JPEG, WebP, GIF), or an error when there is none it can read.pub fn qr_read(path: &str) -> Result<String, String> {    let (l, w, h) = luma(path)?;    let r = catch(move || rxing::helpers::detect_in_luma_with_hints(l, w, h, None, &mut hints(Some(HashSet::from([BarcodeFormat::QR_CODE])))))?;    r.map(|r| r.getText().to_string()).map_err(|_| format!("{}: no QR code found", path))}/// The first code of any kind in the picture: `{ text, format, points }`, `format` like "qr", "ean13", "upca", "code128", "datamatrix";/// `points` the corners or ends it was found at, in pixels. Null when there is none.pub fn qr_read_any(path: &str) -> Result<Value, String> {    let (l, w, h) = luma(path)?;    let r = catch(move || rxing::helpers::detect_in_luma_with_hints(l, w, h, None, &mut hints(None)))?;    Ok(r.map(|r| record(&r)).unwrap_or(Value::Null))}/// Every code in the picture, each `{ text, format, points }`: a sheet of labels, a box with several barcodes. Empty when there is none.pub fn qr_read_all(path: &str) -> Result<Vec<Value>, String> {    let (l, w, h) = luma(path)?;    let r = catch(move || rxing::helpers::detect_multiple_in_luma_with_hints(l, w, h, &mut hints(None)))?;    Ok(r.map(|v| v.iter().take(100).map(record).collect()).unwrap_or_default())}
Thread
87Useful? Like it, or add to its thread below.

Thread 0 posts

On QR and barcode reader: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.