Skip to content

Draw QR codes as vectors, encoded in pure Ruby - #103

Merged
czj merged 1 commit into
mainfrom
claude/confident-pasteur-jz0fj6
Nov 13, 2026
Merged

czj merged 1 commit into
mainfrom
claude/confident-pasteur-jz0fj6

Conversation

@czj

@czj czj commented Nov 13, 2026

Copy link
Copy Markdown
Contributor

Invoices carry a payment QR code, but drawing one took another gem that makes a PNG, plus glue code to size it, and a bitmap scans worse once scaled. This is the first part of #74; the linear codes and DataMatrix follow in their own pull requests.

qr_code encodes the text and draws it as vector rectangles: one path and one fill per symbol, with no dependency.

  • The whole standard: versions 1 to 40, levels :l, :m, :q and :h, and numeric, alphanumeric and byte modes; UTF-8 text is announced by an ECI. The smallest version that holds the text is chosen, and text too long for any version raises.
  • Sized for scanners: size is the symbol's width with its four-module quiet zone, or module_size sets one module (2 pt by default). quiet_zone: false and color are options too.
  • Placed like images: at, or position and vposition, and the cursor moves past the code.
  • Native API: qr_code is a box with size, module_size, level, quiet_zone, color and align.
  • Read back in CI: CI installs zbar, which reads every test code from the rendered page; the README and AGENTS.md list it among the test tools.
Proof

test/qr_code_test.rb draws 9 texts at each of the 4 levels and reads them back with zbar from the page rendered at 150 dpi: a URL, digits, the alphanumeric set, UTF-8 with CJK, an empty string, and random ASCII up to 400 characters. A version 28 code of 1500 bytes reads back too. Version choice matches the standard's capacities: 41 digits fit version 1-L and 42 do not, 7089 digits, 4296 alphanumerics and 2953 bytes fit version 40-L, 1273 bytes fit 40-H and 1274 raise. One code is one fill, at the expected corner and width. Encoding a 37-character URL takes 5.7 ms without JIT, most of it spent scoring the 8 masks; a full version 40 code takes 0.22 s. rake test: 1354 runs, 0 failures; RuboCop is clean.


Generated by Claude Code

qr_code encodes text in the smallest version that holds it at the level asked for, in numeric, alphanumeric or byte mode with UTF-8 announced by an ECI, and draws it as one path of rectangles and one fill. size counts the quiet zone, module_size sets one module; without at, the code is placed as an image is. Gamba's own API takes qr_code as a box.

CI installs zbar, which reads every code the tests draw back from the rendered page.

Assisted-by: Claude [Claude Code]
@czj
czj merged commit 9cbb77b into main Nov 13, 2026
8 checks passed
@czj
czj deleted the claude/confident-pasteur-jz0fj6 branch November 13, 2026 09:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants