Generate and scan barcodes — as a desktop app, a command-line tool, and a JSON API on localhost. Same engine behind all three.
payload ──encode──▶ Symbol ──layout──▶ Scene ──render──▶ bytes
(modules) (geometry) (svg / pdf / eps / png / …)
Encodes 14 symbologies
| Matrix | QR Code, Data Matrix, Aztec |
| Stacked | PDF417 |
| Retail | EAN-13, EAN-8, UPC-A, UPC-E |
| Logistics & general | Code 128, Code 39, Code 93, Interleaved 2 of 5, Codabar, Telepen |
Data Matrix is the small square-or-rectangular block code with the solid L-shaped edge — the one on circuit boards and pharmaceutical packaging.
Decodes those plus MaxiCode, Micro QR, Rectangular Micro QR, GS1 DataBar, GS1 DataBar Expanded and DX Film Edge.
Outputs SVG, PDF, EPS, PNG, WebP, GIF, JPEG, BMP, TIFF, the raw module matrix as JSON, and three terminal renderings (ASCII, Unicode half-blocks, and 24-bit ANSI colour).
Reads from PNG, JPEG, GIF, WebP, BMP, TIFF, raw camera frames, drag-and-drop, and the clipboard.
cargo build --release # CLI at target/release/barq
cargo tauri build # desktop app
cargo tauri dev # desktop app, live-reloadingLinux needs the usual WebKitGTK development packages for the desktop app
(webkit2gtk-4.1, libsoup-3.0, gtk3). The CLI and the API need none of
them.
# straight into your terminal
barq encode "https://example.com"
# a styled PNG
barq encode "https://example.com" -o code.png --preset ocean --scale 12
# a retail barcode; the check digit is calculated for you
barq encode 590123412345 -s ean13 -o label.svg
# Data Matrix for a small part, forced square
barq encode "PART-40219-REV-C" -s data_matrix --dm-shape square -o part.eps
# print-ready vector with a caption
barq encode "SHIP-1029384" -s code128 -o ship.pdf --frame 3 --caption "Crate 12 of 40"
# read one back
barq decode code.png
barq decode photo.jpg --json | jq -r '.results[].text'
cat screenshot.png | barq decode -
# what's supported
barq formats
barq formats data_matrix
barq presetsThe output format is taken from the flag, or the file extension, or — when
writing to a terminal — Unicode blocks. - means stdin/stdout.
Styling flags
--preset classic|soft|dots|fluid|midnight|sunset|ocean|thermal
--fg COLOR --bg COLOR # hex, rgb(), CSS name, or `transparent`
--gradient A,B[,ANGLE]
--shape square|rounded|circle|dot|diamond|squircle|cross|star|fluid|horizontal|vertical
--gap 0.05 --radius 0.3 # fractions of a module
--finder-shape match|square|rounded|circle|leaf
--finder-ring COLOR --finder-center COLOR
--logo logo.png --logo-scale 0.2
--scale 12 --quiet-zone 4 --bar-height 60
--rotate 90|180|270 --invert
--no-hri --hri-size 7 --hri-font Courier
--frame 3 --caption "Product name"
--style-json '{"module_gap":0.08}' # or @file.json — everything the API takesStyle flags merge over the preset, so --preset ocean --gap 0.1 keeps Ocean's
gradient and squircles and changes only the gap.
Runs on 127.0.0.1:8731 — started by the desktop app automatically, or by
barq serve from the command line.
barq serve
barq serve --port 9000 --token "$(openssl rand -hex 16)"| Route | Purpose |
|---|---|
GET /healthz |
liveness; never requires a token |
GET /v1/capabilities |
every symbology, format, preset and shape this build supports |
GET /v1/symbologies |
just the symbology catalogue |
GET /v1/encode?data=… |
an image straight from a URL |
POST /v1/encode |
full request → JSON with metrics, warnings and a data URL |
POST /v1/encode/raw |
full request → the image bytes |
POST /v1/decode |
{"image": "<base64 or data URL>"} or a raw frame |
POST /v1/decode/raw |
an image as the request body |
# quick
curl "http://127.0.0.1:8731/v1/encode?data=hello&format=png&scale=8&fg=%230e6ba8" -o code.png
# full
curl -X POST http://127.0.0.1:8731/v1/encode \
-H 'content-type: application/json' \
-d '{
"data": "https://example.com",
"symbology": "qr",
"options": { "ecc": "H" },
"preset": "ocean",
"style": { "scale": 12, "module_gap": 0.06 },
"output": { "format": "svg" }
}'
# read
curl -X POST --data-binary @photo.jpg http://127.0.0.1:8731/v1/decode/rawThe desktop app's Request panel shows the exact JSON behind whatever is on
screen — paste it into curl and you get the same bytes.
- Binds loopback only by default.
--hostcan change that, and warns when you do. - No path parameter anywhere: encode returns bytes, decode accepts bytes. Path traversal and arbitrary file reads are not filtered for — they are absent.
--tokenrequiresAuthorization: Bearer …on every route except/healthz, compared in constant time. Worth setting on a shared machine: loopback is not a trust boundary, and a browser page can reach it cross-origin.- Request bodies are capped at 48 MB; output is capped at 20 000 px per side and 64 Mpx.
Two modes, Create and Scan.
Create gives you live preview, every styling control the engine has, a scannability readout, and export to any format. Scan takes a dropped file, a pasted image, a file picker, or the camera — with continuous decoding of the video stream.
Camera capture needs getUserMedia, which is reliable on macOS and Windows;
on Linux it depends on how your WebKitGTK was built. Drop, paste and file
picking always work.
One geometry, four backends. layout turns a symbol and a style into a
Scene — a small vector scene of filled paths, images and text. SVG, PDF and
EPS are written directly from it, and the rasterisers go through the SVG writer
and resvg. That is why a PNG matches its SVG exactly, and why the test suite
can assert that all four agree on the page size.
Gradients resolve per module. Rather than emitting gradient definitions each backend implements differently, the layout stage samples the paint at each module's centre. Every backend then only has to fill a shape with one solid colour. At module scale the result is indistinguishable from a continuous gradient, and all four outputs agree.
Styling is bounded by what still scans. Module shapes never leave their cell, so a decoder sampling the centre always hits ink. Options that a symbology cannot survive are dropped rather than honoured: reshaped modules on a linear barcode, a logo on Aztec, gaps on PDF417. What remains is checked and reported — contrast below 3:1, inverted polarity, an undersized quiet zone, a logo too large for the error-correction level.
No embedded fonts. Human-readable text uses the base-14 fonts, which PDF and
PostScript already have. The width tables in text.rs are what let those two
backends centre a string themselves, and what lets the layout widen a quiet
zone when EAN's outset digits would not otherwise fit.
crates/barq-core/ the engine — encode, style, layout, render, decode
crates/barq-api/ axum router for the JSON API
crates/barq-cli/ the `barq` binary
src-tauri/ desktop app: Tauri commands + the bundled API server
ui/ the window (no framework, no build step)
barq-core has no async, no HTTP and no GUI dependency; it is usable as a
library on its own.
.github/workflows/ci.yml runs on every push and pull request:
- core / api / cli on Linux, macOS and Windows — build, test, and a smoke test that encodes a code with the real binary and decodes it back.
- vector cross-check — installs Ghostscript and librsvg so the cross-renderer tests actually execute rather than skipping themselves.
- desktop app on all three platforms, with the WebKitGTK toolchain on Linux.
.github/workflows/release.yml fires on a v* tag: it builds the CLI for four
targets, verifies each binary encodes and decodes before upload, bundles the
desktop app, and publishes everything with checksums. Anything tagged alpha,
beta or rc is marked as a pre-release.
The interface follows the RAGBAZ design system
tokens verbatim: a warm solarized-dark ground (#0f0f10 through #1a1a1a with a
warm-tinted elevated surface), orange ember as the primary (#ff9900 hot,
#f3c46c signature), cyan-blue as the secondary, and gruvbox semantics for
success, warning and error. Radii are square-leaning — 0px is the default and
rounding is reserved for buttons and pills. Borders carry the structure; shadows
are kept minimal.
The system is dark-first and defines no light palette, so the app follows suit rather than inventing one.
Type roles come from the same tokens:
| Role | Family | Used for |
|---|---|---|
| mono | Intel One Mono | labels, eyebrows, data, numerics, controls, code |
| sans | Noto Sans | structure and UI copy |
| serif | Noto Serif | prose — descriptions, notes, explanations |
All three are bundled as Latin-subset variable WOFF2, 91 KB for the set. See ui/fonts/.
Note that the generated symbols still default to black on white regardless of
the interface theme: a barcode's job is to scan, and dark-on-light is what
scanners want. The ragbaz preset puts the brand into the finder eyes while
keeping the data modules at full contrast.
cargo test --workspaceThe suite renders every symbology through every output format and scans the result back, including through third-party renderers (Ghostscript for PDF and EPS, librsvg for SVG) — so a geometry bug in a backend fails the build rather than shipping as an unreadable label.
GPL-2.0-or-later — GNU General Public License version 2, or (at your option) any later version. The v2 text is in COPYING.
barq links rxing, a Rust port of ZXing, which
is Apache-2.0 only. The FSF considers Apache-2.0 incompatible with
GPL-2.0-only: its patent-termination clause is an additional restriction that
GPLv2 section 6 does not permit.
The "or later" option resolves it. Apache-2.0 is GPLv3-compatible, so anyone distributing a combined binary can take the v3 option and the conflict does not arise. This is the same reason most GPL projects with modern dependencies are "or later" rather than version-locked.
Every other dependency offers an MIT or BSD option and is GPLv2-compatible:
| Crate | Licence |
|---|---|
| rxing, rxing-one-d-proc-derive | Apache-2.0 — GPLv3-compatible, hence "or later" |
| image, resvg, usvg, tauri, clap, serde, flate2, base64 | MIT OR Apache-2.0 |
| axum, tokio, tower-http | MIT |
| tiny-skia | BSD-3-Clause |
| encoding_rs | (Apache-2.0 OR MIT) AND BSD-3-Clause |
The bundled Montserrat and Merriweather typefaces are SIL Open Font License
1.1; see ui/fonts/. The OFL permits redistribution alongside
software of any licence and does not affect barq's terms.