Packages

qr

Make QR codes and barcodes as images, read them back from any image, and get a code's grid to draw it yourself.

import "std:qr"

std:qr makes QR codes and barcodes (Code 128, EAN-13, UPC-A and Code 39) and reads them back. A code is a plain *img.Image from img, so whatever takes an image takes a code: save it as a file, resize it, draw it into another image, or show it in a ui view.

Reach for it for sign-in links, tickets, labels and anything a phone camera should read.

A QR code#

qr.code(text) returns a result holding the image. It is 256 pixels square by default, black on white, with a blank border (the quiet zone) that scanners need:

main.osl
import "std:qr"

auto code = qr.code("https://rotur.dev/link?code=AB12").unwrap()
log code.width() code.height()
Click the code to edit it

Reading it back#

qr.read(image) finds a code in an image and returns its text. It reads codes qr.code made and ones in photos or screenshots at any angle, and it also reads barcodes. When there is no code, it fails with NotFoundError:

main.osl
import "std:qr"
import "std:img"

auto code = qr.code("https://rotur.dev/link?code=AB12").unwrap()
log qr.read(code).unwrap()

*img.Image blank = img.new(50, 50, {fill: "white"})
log qr.read(blank).unwrapErr().kind
Click the code to edit it

Options#

size is the image's width and height in pixels (it grows if needed to give each dot a whole pixel). margin is the border in modules, color and background are colours given as "#rrggbb", "#rrggbbaa" or {r, g, b, a}. level is how much of the code may be damaged and still read: "L", "M" (the default), "Q" or "H".

main.osl
import "std:qr"

auto code = qr.code("hi", {
  size: 100,
  margin: 2,
  level: "H",
  color: "#000080",
  background: "#ffffff"
}).unwrap()
log code.width()
log code.pixel(1, 1)
log qr.read(code).unwrap()

log qr.code("x" * 5000).unwrapErr().kind
Click the code to edit it

Text too long for a QR code fails with ValueError. A higher level makes a denser code that survives more damage, which suits a logo laid over the middle.

Saving and showing a code#

Because it is an image, a code saves like one, and a ui view shows it with an <image> tag:

main.osl
import "std:qr"
import "std:img"
import "std:ui"
import "std:fs"

fs.remove("code.png", {force: true}).unwrap()

*img.Image code = qr.code("hello").unwrap()
code.save("code.png").unwrap()
log qr.read(img.decodeFile("code.png").unwrap()).unwrap()

def View() ui.node (
  return <column pad=10>
    <text>Scan me</text>
    <image src=${code} width=100 height=100/>
  </column>
)
log ui.html(View).contains("data:image/png")
fs.remove("code.png").unwrap()
Click the code to edit it

Barcodes#

qr.barcode(text, options) makes a barcode. kind is "code128" (the default, any text), "ean13", "upca" or "code39" (upper-case letters, digits and - . $ / + % and space). height is in pixels. EAN-13 takes 12 or 13 digits, and UPC-A 11 or 12, adding or checking the last digit:

main.osl
import "std:qr"

auto label = qr.barcode("HELLO-123", {kind: "code39", height: 40}).unwrap()
log label.height()
log qr.read(label).unwrap()

auto ean = qr.barcode("590123412345", {kind: "ean13"}).unwrap()
log qr.read(ean).unwrap()
log qr.checkDigit("590123412345").unwrap()

log qr.barcode("abc", {kind: "ean13"}).unwrapErr().kind
Click the code to edit it

qr.checkDigit(digits) computes the final digit for the code without it. A barcode that doesn't fit its kind fails with ValueError.

Drawing a code yourself#

qr.modules(text) returns the code as a grid of booleans, true for dark, with no quiet zone. Use it to draw a code another way, such as with raylib rectangles or in a terminal:

main.osl
import "std:qr"

auto grid = qr.modules("hi").unwrap()
log grid.len grid[1].len

for row in grid (
  string line = ""
  for dark in row (
    line = line ++ (if dark then "##" else "  ")
  )
  log line
)
Click the code to edit it

Pass {kind: "code128"} or another barcode kind to get one row of bars instead.

Quick check

qr.read is given a blank white image. What does it return?

Your turn

Round trip

Make a QR code for "OSL", read it back, and print the text.

main.osl
Ctrl Enter runs
Expected output
OSL

API reference#

5 members, generated from the std:qr source of OSL 9.3.0.

Functions

#qr.barcode(string text, object? options) result<*img.Image, error>

barcode makes text into a barcode image. Options: kind ("code128", the default, "ean13", "upca" or "code39"), height (in pixels, default 120), and color and background (default black on white). EAN-13 takes 12 or 13 digits and UPC-A 11 or 12, computing or checking the last (qr.read gives an EAN-13 that starts with 0 back as its UPC-A, without the 0); Code 39 takes upper-case letters, digits and - . $ / + % and space. Fails with ValueError when the text can't be written as that kind.

#qr.checkDigit(string digits) result<int, error>

checkDigit gives the check digit an EAN-13 or UPC-A barcode ends with for digits, the code without it. Fails with ValueError when digits is empty or has anything but 0-9.

#qr.code(string text, object? options) result<*img.Image, error>

code makes text into a QR code image. Options: size (the image's width and height in pixels, default 256; it grows to fit a module per pixel), margin (the blank border, in modules, default 4), color and background (default black on white), and level ("L", "M", "Q" or "H": how much of the code may be damaged and still read; default M, or L when the text only fits there). Fails with ValueError when the text is too long for a QR code.

#qr.modules(string text, object? options) result<boolean[][], error>

modules gives text's code as a grid, true for dark, without its quiet zone, for drawing it some other way: the rows of a QR code, or one row of a barcode's bars. Options: kind ("qr", the default, or a barcode kind as qr.barcode takes) and level (QR only, as qr.code takes). Fails with ValueError when the text can't be encoded as that kind.

#qr.read(*img.Image picture) result<string, error>

read gives the text of the QR code or barcode in picture: a code qr.code or qr.barcode made, or one in a photo or screenshot, at any angle. It reads QR codes, Code 128, Code 39, Code 93, EAN-13, EAN-8, UPC-A, UPC-E, ITF and Codabar; a UPC-A barcode reads as its 12 digits, so an EAN-13 that starts with 0 reads as the UPC-A it is, without that 0. Fails with NotFoundError when the image holds no code it can read, and StateError when it is closed.