barcode
@profile(t1);
import io from "io";
import fs from "fs";
import gfx from "gfx";
import image from "image";
import barcode from "barcode";
fn main() -> i32 {
let code: barcode.Symbol = barcode.qr("https://tapelang.org") or {
io.println("no QR code: {err}");
return 1;
};
var picture: gfx.Surface = barcode.render(&code, &barcode.Style {}) or { return 2; };
let png = image.encode(&picture, image.Format.Png) or { return 3; };
fs.write_file("tapelang.png", png.slice()) or { return 4; };
picture.free();
return 0;
}A barcode is made in two steps. encode turns your data into a
Symbol, the grid of light and dark modules the standard asks for. Then
you draw the symbol: render gives a gfx.Surface (what
image.decode gives for a PNG, so you can save it, draw it in
a window or print it), svg gives an SVG document. barcode is a pure
module: it reads and writes no files.
| Function | Does |
|---|---|
barcode.qr(data) -> Symbol or BarcodeError | A QR code at level M, the smallest that holds the data |
barcode.encode(kind, data, options) -> Symbol or BarcodeError | Any kind, with options |
barcode.render(&symbol, &style) -> gfx.Surface or BarcodeError | The picture, style.scale pixels a module; free() it |
barcode.svg(&symbol, &style) -> string or BarcodeError | The same picture as an SVG document, a module one unit |
barcode.size_px(&symbol, &style) -> gfx.Point | The picture’s size before you draw it |
barcode.contrast(a, b) -> f64 | How far apart two colours are (1 to 21) |
Kinds
Kind | For |
|---|---|
Qr | QR Code: links, Wi-Fi details, payments, tickets. Versions 1-40 |
DataMatrix | Small parts and labels, GS1 health care codes |
Aztec | Boarding passes and train tickets: no quiet zone needed |
Pdf417 | ID cards, shipping labels: a stack of rows |
Code128 | Shipping and logistics; GS1-128 with gs1 |
Ean13, Ean8, UpcA, UpcE | Retail products |
Code39, Code93 | Industry, inventory |
Itf | Cartons (pairs of digits) |
Codabar | Libraries, blood banks |
Options:
| Field | Is |
|---|---|
ec: Ec = Ec.Medium | Error correction: Low, Medium, Quartile, High. QR’s L, M, Q, H; Aztec’s 10, 23, 36, 50 %. More of it is a bigger symbol that survives more damage |
size: i64 = 0 | 0: the smallest symbol that fits. Else QR’s version (1-40), Aztec’s layers (negative: compact), Data Matrix’s size, PDF417’s columns |
mask: i64 = -1 | QR: the mask, else the best by the standard’s scoring |
utf8: bool = false | Mark the data as UTF-8 (ECI 26) for readers that would guess otherwise |
gs1: bool = false | A GS1 code: put an ASCII 29 ("\x1d") between variable-length fields |
check: bool = false | Code 39: add the optional check character |
A 2D symbol grows with its data; QR then uses the error correction left
over in that size, so a short link at level M may come out at level Q.
Each symbology is also a module of its own with every option it has
(barcode_qr, barcode_datamatrix, barcode_aztec, barcode_pdf417,
barcode_linear); its encode gives the same Symbol.
BarcodeError:
| Variant | When |
|---|---|
TooLong | The data does not fit the largest symbol (or the size you asked for) |
InvalidChar | A character the kind has no code for: a letter in an EAN, lowercase in Code 39 |
InvalidLength | EAN-13 with 11 digits, ITF with an odd count |
BadCheckDigit | The check digit you gave is wrong |
InvalidOption | An option out of range |
LogoTooLarge | The logo hides more than the error correction can restore |
NoLogo | A logo on a symbol without error correction (a 1D barcode) |
InvalidSize | scale below 1, or a picture too large |
OutOfMemory | Out of memory |
Style
let style: barcode.Style = barcode.Style {
shape: barcode.Shape.Rounded; // modules joined into round blobs
eye: barcode.EyeShape.Rounded; // the three corner squares
eye_core: barcode.EyeShape.Circle;
fill: barcode.Fill.Linear; // a gradient
dark: #6A00F4;
dark2: #00B4D8;
angle: 45.0;
};| Field | Is |
|---|---|
scale: i64 = 8 | Pixels a module (render) |
quiet: i64 = -1 | The light margin in modules; -1: what the standard asks (QR 4) |
dark: color = #000000, light: color = #FFFFFF | The colours; a light with alpha 0 leaves the background transparent |
fill: Fill = Fill.Solid | Linear or Radial: a gradient from dark to dark2 over the symbol |
dark2: color, angle: f64 | The gradient’s other colour; Linear’s direction in degrees (0: left to right, 90: top to bottom) |
shape: Shape = Shape.Square | Square, Dot, Rounded (neighbours joined, lone modules round), Diamond |
dot: f64 = 1.0 | How much of its cell a module fills, 0.5 to 1: below 1 leaves gaps |
eye, eye_core: EyeShape | The finder’s frame and centre: Square, Rounded, Circle |
eye_color, core_color: color | Their colours; alpha 0 (the default): as the modules |
logo: *gfx.Surface = null | A picture in the middle (below) |
logo_href: []const u8 = "" | The logo in svg’s output: a URL or a data: URI |
logo_size: f64 = 0.2 | The logo’s width, a share of the symbol’s |
logo_pad: f64 = 1.0 | Modules kept clear round it |
logo_round: f64 = 0.0 | The cleared area’s corner radius, in modules |
bar_height: i64 = 50 | 1D: the bars’ height, in modules |
show_text: bool = true | 1D: the digits under the bars |
The finders are always drawn whole in their own shape, whatever the modules’ shape, so a reader still finds them.
Keep the colours far apart: dark modules on a light background, with a
barcode.contrast of 3 or more for the modules, the eyes and the
gradient’s ends. Gold eyes on white (2.2) already fail; most phone
cameras do not read light modules on a dark background either.
A logo
var logo: gfx.Surface = image.decode(logo_png) or { return 1; };
let code: barcode.Symbol = barcode.encode(barcode.Kind.Qr, url, barcode.Options {
ec: barcode.Ec.High;
}) or { return 2; };
var picture: gfx.Surface = barcode.render(&code, &barcode.Style {
logo: &logo;
logo_size: 0.22;
logo_round: 1.5;
}) or {
io.println("{err}"); // LogoTooLarge: a smaller logo, or more error correction
return 3;
};The modules under the logo are cleared, so the data they held is lost and
the error correction has to restore it. render and svg count what the
logo hides and refuse it (LogoTooLarge) when it hides more than three
quarters of what the code can restore, leaving the rest for a crease or a
glare. Ask for Ec.High when you put a logo on a QR code: it takes a logo
of about a fifth of its width; at the default level M only a small one
fits. The finders, timing and format modules are never cleared.
Logos are for QR codes. An Aztec code has its bullseye in the middle, so
it takes none; a Data Matrix code takes a small one in the sizes up to
26 x 26 (one data region, no alignment lines through the middle) when
it has room to spare; PDF417’s error correction is spread too thin for
one. Each refuses with LogoTooLarge rather than giving a code that
does not scan.
For svg, give the logo as logo_href: a URL the SVG will be shown
next to, or a data:image/png;base64,... URI you make with
image.encode and encoding.base64.
1D barcodes
let ean: barcode.Symbol = barcode.encode(barcode.Kind.Ean13, "400638133393", barcode.Options {}) or { return 1; };
io.println(ean.text); // "4006381333931": the check digit addedGive EAN and UPC codes without their check digit and it is added, or with
it and it is checked (BadCheckDigit). A 1D symbol is one module high:
render draws its bars bar_height modules tall with the text under them.
Your own symbology
A Symbol is a grid you can fill yourself, so a barcode the stdlib lacks
is drawn like the others:
import barcode_core from "barcode_core";
var s: barcode_core.Symbol = barcode_core.symbol(9, 9);
s.set(4, 4, true, barcode_core.ROLE_DATA);
s.quiet = 2;
var picture: gfx.Surface = barcode.render(&s, &barcode.Style { shape: barcode.Shape.Dot; }) or { return 1; };Last modified: