rqrcode.cr

rqrcode.cr

A 1:1 Crystal port of whitesmith/rqrcode 2.2.0 (+ the core of rqrcode_core 1.2.0) — QR code encoding and rendering to HTML/ANSI/SVG/PNG. Suite: 83 examples, 0 failures; the oracle replays matrices, errors and render output recorded from the real gems, 1:1.

Installation

dependencies:
  rqrcode_cr:
    github: OrelSokolov/rqrcode.cr
shards install

No external dependencies (the PNG renderer replaces chunky_png, see below).

Usage

require "rqrcode_cr"

qr = RQRCode::QRCode.new("https://example.com")

qr.as_html
qr.as_ansi(dark: "█", light: " ")
qr.as_svg(module_size: 6, use_path: true)
qr.as_png(size: 240, border_modules: 4)  # => PNG blob
qr.checked?(0, 0)
qr.modules

# multi-segment input
core = RQRCodeCore::QRCode.new([
  {data: "123", mode: :number},
  {data: "abc", mode: :byte_8bit},
])

Feature comparison vs rqrcode 2.2.0 / rqrcode_core 1.2.0

Encoding (rqrcode_core)

Feature Original (rqrcode_core) Port (rqrcode.cr) Status
QRCode.new(data, level:, size:, max_size:, mode:) — String, QRSegment or segment array ✅ Same ✅
Modes :number / :alphanumeric / :byte_8bit (auto-detect) ✅ Same ✅
Error-correction levels :l/:m/:q/:h (default :h), versions 1–40, auto version selection ✅ Same ✅
Multi-segment UTF-8 input ✅ Same ✅
checked?(row, col) (+ deprecated dark?), modules, module_count, version, mode, error_correction_level, multi_segment?, inspect, to_s(dark:, light:, quiet_zone_size:) ✅ Same ✅
Errors: QRCodeArgumentError (< ArgumentError), QRCodeRunTimeError — same messages ✅ Same ✅
Non-string scalar data (CORE.new(123)) raises QRCodeArgumentError Compile-time error in Crystal (case excluded from the oracle) ❌ (equivalent: compile-time check)

Rendering (rqrcode)

Feature Original Port Status
as_html ✅ Same ✅
as_ansi(light:, dark:, fill_character:, quiet_zone_size:) ✅ Same ✅
as_svg(fill:, color:, offset:, module_size:, shape_rendering:, standalone:, use_path:, viewbox:, svg_attributes:) — rect and path variants ✅ Same ✅
as_png(size:, border_modules:, module_px_size:, border:, resize_exactly_to:, resize_gte_to:, fill:, color:, file:) — both sizing algorithms (exactly / gte) with exact pixel geometry ✅ Same 🔶
PNG colors "black"/"white", hex 3/6, [r,g,b]/[r,g,b,a] ✅ Same ✅
PNG color_mode/bit_depth/interlace/compression options ✅ Accepted but ignored 🔶

Quirks preserved intentionally (oracle-confirmed)

  • as_png(..., border:) is effectively ignored (border_modules wins).
  • Integer division in demerit_points_4.
  • get_mask compares > instead of >=.

Deviations & Crystal-specific notes

  • Options are named arguments instead of Ruby trailing-hash (extract_options! → def to_s(dark = "x", ...)); as_png keeps **options because the "passed or not" distinction selects the sizing algorithm.
  • Int#between? is a monkeypatch (Ruby builtin, absent from the Crystal stdlib).
  • The unreachable :mode_multi branch of QRSegment in the writer is omitted.

PNG without chunky_png

RQRCode::Export::PNG::Image is a minimal chunky_png replacement: an RGBA canvas with new/[]=/save/to_s/resize/width/height. Resize is an exact port of chunky_png's resample_nearest_neighbor (Bresenham error accumulation, not floor scaling). The encoder writes 8-bit grayscale (chunky_png writes 1-bit — files are larger but valid, verified with the ChunkyPNG decoder). CRC32 is a built-in table (not exported by the Crystal stdlib).

Testing

ruby oracle/cases.rb > oracle/expected.yml   # re-record against the real gems
crystal spec                                  # 83 examples
  • spec/oracle_spec.cr — 57 cases recorded from the real gems: matrices (v1–v40, all L/M/Q/H levels, all modes, multi-segment, UTF-8), errors (class + message), HTML/ANSI/SVG (rect + path)/PNG (pixel geometry of both sizing algorithms + resize).
  • spec/rqrcode_spec.cr — 26 unit tests on the API (QRMath, QRUtil, RS blocks, QRSegment, buffer, renderers), ported from README/API scenarios (the originals ship no test files in the gem package).
Repository

rqrcode.cr

Owner
Statistic
  • 0
  • 0
  • 0
  • 1
  • 0
  • 2 days ago
  • October 12, 2026
License

Links
Synced at

Mon, 12 Oct 2026 05:23:58 GMT

Languages