jabcode

package module
v0.0.0-...-b4425be Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 22, 2026 License: MIT Imports: 5 Imported by: 0

README

jabcode

PkgGoDev MIT license DeepWiki experimental

Pure-Go JAB Code encoder and decoder.

JAB Code is a high-capacity color matrix barcode standardized as ISO/IEC 23634:2022.

This module is experimental. The API may still change, and scanning real-world captures is still being hardened. When you find any errors, please report them as issues.

Status

Single- and multi-symbol encode/decode work, including normative 4- and 8-color ISO modes, docked secondary symbols, diagnostics, and a frame-sequence decoder. Tagged builds add high-color, BSI and historical decoder families. The main active work is print-capture robustness, stream integration, performance, and validation of the ISO target.

Install

go get github.com/srlehn/jabcode

Install the CLI:

go install github.com/srlehn/jabcode/cmd/jabcode@latest

Library

package main

import (
    "bytes"
    "image/png"
    "os"

    "github.com/srlehn/jabcode"
)

func main() {
    img, err := jabcode.NewEncoder(
        jabcode.WithColors(8),
        jabcode.WithModuleSize(12),
    ).Encode([]byte("hello"))
    if err != nil {
        panic(err)
    }

    var buf bytes.Buffer
    if err := png.Encode(&buf, img); err != nil {
        panic(err)
    }
    if err := os.WriteFile("hello.png", buf.Bytes(), 0o644); err != nil {
        panic(err)
    }
}

Decoding accepts any image.Image, so file format support is provided by the decoders registered by the caller.

f, err := os.Open("hello.png")
if err != nil {
    panic(err)
}
defer f.Close()

img, err := png.Decode(f)
if err != nil {
    panic(err)
}

data, err := jabcode.Decode(img)
if err != nil {
    panic(err)
}
_ = data

For ordered, coherent frame sequences, use jabcode.NewStream(). Frames may come from a live camera, network video, or a decoded recording. The stream reuses previous read hypotheses and compatible evidence within a fixed per-frame work budget, and automatically consumes every decoder capability compiled into the build through one integrated detector pipeline. Call Stream.Reset before reusing it for an unrelated coherent sequence.

Decode and Stream accept an already constructed image.Image; they cannot bound storage that a byte-buffer adapter allocated first. Adapters for untrusted camera buffers must validate width, height, stride, and their products and enforce an input-dimension limit before allocating the image. A permanent regular-Go js/wasm gate executes one fixed opaque-byte symbol through the public plan and Stream APIs. It proves the platform and byte contract, not production readiness for repeating loops of changing symbols; that transition and emission work remains separate.

Native applications that only render symbols can import the dependency-light encoder package directly:

import "github.com/srlehn/jabcode/jabenc"

img, err := jabenc.New(
    jabenc.WithColors(8),
    jabenc.WithModuleSize(12),
).Encode([]byte("hello"))

That package does not depend on the read or detect packages, Vulki, or purego. With CGO_ENABLED=0, an encoder-only consumer remains statically linked. The root package keeps the same encoder facade for applications that also decode.

For fixed-size binary transport frames, create one immutable byte-mode plan and use its exact capacity before splitting any data:

plan, err := jabenc.NewOpaquePlan(
    image.Pt(8, 8),
    jabenc.WithColors(8),
    jabenc.WithModuleSize(4),
)
if err != nil {
    panic(err)
}
frameCapacity := plan.Capacity()
img, err := plan.Encode(frame[:frameCapacity])

The plan fixes color count, side versions, ECC level, module size, and output geometry. It rejects empty data and any payload one byte beyond its reported capacity.

Commands

Encode payload bytes from stdin to PNG:

printf hello | jabcode encode --output hello.png

For shell demos, literal text input is also available:

jabcode encode --input "hello" --output hello.png

Decode an image to stdout:

jabcode decode hello.png

Write decoded bytes to a file:

jabcode decode --output payload.bin hello.png

ISO decode output is the reader transmission defined by the standard: it starts with the ]j1, ]j4 or ]j5 symbology identifier, encodes ECI assignments as a backslash plus six digits, and doubles literal data backslashes. Consumers that need application bytes use DecodeMessage or Stream.DecodeMessage. Their Message.Data is decoded directly from the mode stream, while Message.ReaderTransmission retains the standards-facing form and Message.Controls records ECI, FNC1, and ISO/IEC 15434 structure.

jabcode decode registers PNG, JPEG, HEIC, AVIF, TIFF, and WebP decoders (including WebP VP8 and VP8L).

Detector diagnostics for difficult captures write the payload to stdout and the diagnostic report to stderr; annotated diagnostic images go to --diag-out. The diagnostic mode observes the authoritative read once and does not replay a second decode pipeline:

jabcode decode --diag --diag-out ./diag-images capture.png > payload.bin

Multi-symbol encodes use one compact symbol spec per symbol:

jabcode encode --symbols 0:4x4:0,2:4x4:0 --output cascade.png < payload.bin

Compatibility

  • The default encoder targets ISO/IEC 23634:2022 with the normative 4- and 8-color modes. Its Annex F range reduction still lacks an independent wire oracle, so strict-conformance verification is not yet complete.
  • Decoder build tags are additive. Untagged Decode accepts ISO only; jabcode_high_color, jabcode_bsi, and jabcode_legacy add their compiled routes to the same automatic read. The CLI-only --only flag restricts the read to a comma-separated subset of them, for debugging and conformance work.
  • jabcode_high_color adds decoding of the non-standard ISO-derived 16- through 256-color modes. jabcode_non_iso_encode adds the public encoder profile selector with hc and bsi output. Use the corresponding decoder tag as well when the same binary must read what it writes. Physical robustness decreases with color density: capture limits range from camera-grade 16/32 colors to scanner-grade 128 colors, while 256 colors remain pixel-exact only. See WithColors for details.
  • jabcode_legacy adds read-only current and pre-v2.0 C-reference formats, including docked multi-symbol codes. No legacy encoder is exposed.
  • jabcode_bsi adds exact BSI TR-03137 primary and recursively docked-secondary decoding. jabcode_non_iso_encode exposes ProfileBSI and CLI --profile bsi for single- and multi-symbol output. BSI supports its specified 4- through 256-color layouts; the CLI warns above 8 colors because capture robustness still falls as palette density rises.
  • jabcode_ldpc_catalog_blob embeds the precomputed LDPC pivot catalogs instead of sweeping them on first use. It adds no wire capability, and is off by default because the artifacts would grow every dependant binary by 13 MB with the ISO catalog alone and by 26 MB where jabcode_bsi or jabcode_legacy add the C-family one. Both forms carry identical bytes; the default computes the catalog once at run time instead, and only when a decode reaches the GPU parity-matrix path.
  • Decode is intended to return errors, not panic, on malformed or hostile images. Callers should still bound untrusted image dimensions before decoding.
  • Native large resolution-pyramid reads use Vulkan preprocessing automatically when the selected adapter reports a discrete-GPU device type. There is no GPU build tag or required runtime configuration; smaller images, unavailable Vulkan and software implementations such as llvmpipe use the CPU path transparently. GOOS=js builds run the same reader over a WebGPU session when a large enough frame has a browser GPU, and over the CPU path otherwise; Vulki and purego are excluded there. Regular Go js/wasm is the tested browser target. The OS-only build constraint also selects the CPU files for GopherJS without making a GopherJS execution or language compatibility claim. The native GPU path persists a Vulkan pipeline cache under the user cache directory (vulki/pipeline-*.bin); set VULKI_PIPELINE_CACHE=off to disable it or VULKI_PIPELINE_CACHE_PATH to relocate it.

Layout

  • Root package: public Encoder facade, Decode, and Stream.
  • jabenc: dependency-light public encoder API for sender-only consumers.
  • internal/encode: data encoding, matrix placement, masking, and rendering.
  • internal/core: shared pixel buffers, geometry, decoded-symbol types, and status values used by the read path.
  • internal/read, internal/detect, internal/decode: image search, detection, sampling, metadata, palette, ECC, and payload decoding.
  • internal/diag: staged text and image diagnostics over the decoder.
  • internal/ecc, internal/palette, internal/spec, internal/tables: shared format machinery.
  • cmd/: user-facing CLIs.

Development

More detail:

  • ARCHITECTURE.md describes the package boundaries, invariants, robustness extensions, and verification strategy.
  • WIRE_FORMAT.md records the C-reference wire format and known ISO and pre-ISO deltas.

Documentation

Overview

Package jabcode is a pure-Go port of the JAB Code (Just Another Bar Code) reference library, a high-capacity 2D color matrix symbology standardized as ISO/IEC 23634:2022.

The default encoder targets the ISO/IEC 23634 wire format. The dependency-light encoder subpackage provides the authoritative public write path for applications that do not need the reader; this root package keeps a facade over it, including fixed byte-mode plans with exact capacity. An untagged decoder accepts the ISO variant; optional build tags add high-color, BSI, and historical C-reference decoder capabilities without replacing ISO. Decode automatically uses every compiled capability; DecodeMessage returns raw data and reader transmission from the same read. Forced single-variant decoding remains internal for CLI oracle and test work. The jabcode_non_iso_encode tag adds public ISO high-color and BSI encoder profiles without changing the untagged ISO default.

Index

Constants

View Source
const (
	ControlECI           = jabenc.ControlECI
	ControlFNC1Start     = jabenc.ControlFNC1Start
	ControlFNC1Separator = jabenc.ControlFNC1Separator
	ControlFNC1End       = jabenc.ControlFNC1End
)

Variables

This section is empty.

Functions

func Decode

func Decode(img image.Image) ([]byte, error)

Decode decodes the data of a JAB Code from img: the primary symbol and any docked secondary symbols. The untagged build accepts ISO/IEC 23634; optional decoder build tags add their wire families to the same automatic read. They never replace the ISO decoder. Reading a JAB Code from a file is stdlib decoding (e.g. png.Decode) followed by Decode.

When the ISO variant succeeds, Decode returns the ECI-capable reader transmission rather than the raw encoded payload: every message starts with ]j1, ]j4 or ]j5, literal data backslashes are doubled, ECI assignments are escaped, and the JAB ISO/IEC 15434 switch expands its message envelope. That expansion validates the JAB macro controls, not the application data inside the format envelope. The ISO variant rejects reserved color modes. Its Annex F range reduction has not been independently validated.

Types

type Control

type Control = jabenc.Control

Control places a structured encoder control relative to application data.

type ControlKind

type ControlKind = jabenc.ControlKind

ControlKind identifies a structured encoder control.

type Encoder

type Encoder = jabenc.Encoder

Encoder encodes data into a JAB Code. Configure it with the With* options; NewEncoder defaults to the ISO/IEC 23634 format, 8 colors, module size 12 and the default ECC level.

func NewEncoder

func NewEncoder(opts ...Option) *Encoder

NewEncoder returns an Encoder configured by opts.

type Message

type Message struct {
	Data               []byte
	ReaderTransmission []byte
	Controls           []MessageControl
}

Message contains raw decoded data and the standards-facing reader transmission produced from the same corrected message bits. Data excludes symbology identifiers and ECI escape fields and restores literal backslashes. Controls preserves the non-data structure.

func DecodeMessage

func DecodeMessage(img image.Image) (Message, error)

DecodeMessage decodes img once and returns raw application data alongside the reader transmission. Decode remains the standards-facing shorthand for Message.ReaderTransmission.

type MessageControl

type MessageControl struct {
	Kind       MessageControlKind
	Offset     int
	Assignment int
}

MessageControl records a control at an offset in Message.Data. Assignment is set only for MessageControlECI. A FNC1 separator's offset points at the GS byte inserted into Data.

type MessageControlKind

type MessageControlKind uint8

MessageControlKind identifies a structured message control that is not a literal byte in Message.Data.

const (
	MessageControlECI MessageControlKind = iota + 1
	MessageControlFNC1Start
	MessageControlFNC1Separator
	MessageControlFNC1End
	MessageControlISO15434Start
	MessageControlISO15434End
)

type OpaquePlan

type OpaquePlan = jabenc.OpaquePlan

OpaquePlan is an immutable fixed-symbol byte-mode encoder plan.

func NewOpaquePlan

func NewOpaquePlan(version image.Point, opts ...Option) (*OpaquePlan, error)

NewOpaquePlan creates a fixed single-symbol plan whose reported capacity is exact for arbitrary byte values.

type Option

type Option = jabenc.Option

Option configures an Encoder.

func WithColors

func WithColors(n int) Option

WithColors sets the number of module colors.

The default ISO encoder accepts 4 or 8 colors. More-than-8-color output requires jabcode_non_iso_encode and a non-ISO profile. Those denser modes have materially lower physical capture robustness; see jabenc.WithColors for the measured limits.

func WithControls

func WithControls(controls []Control) Option

WithControls adds structured ECI and FNC1 controls to the encoded message.

func WithECCLevel

func WithECCLevel(level int) Option

WithECCLevel sets the error-correction level (0..10); 0 selects the default.

func WithModuleSize

func WithModuleSize(px int) Option

WithModuleSize sets the side length, in pixels, of each module.

func WithSymbols

func WithSymbols(positions []int, versions []image.Point, eccLevels []int) Option

WithSymbols configures a fixed primary or a multi-symbol code. Each slice is indexed by symbol, with the primary first.

type Stream

type Stream struct {
	// contains filtered or unexported fields
}

Stream decodes successive images from one coherent frame sequence. Frames may come from a live camera, network video, or a decoded recording. Each frame has a fixed route and correction budget: recent geometry is replayed first, unused search hypotheses carry forward, and the exhaustive single-image ladder is never entered implicitly. Four- and eight-colour primary-only symbols may also combine bounded, compatible module evidence across frames when no individual frame is sufficient. Consequently one frame can return a decode error even when the exhaustive Decode function would succeed; later frames can complete the bounded search or add the missing evidence.

Stream automatically accepts every decoder capability compiled into the build. Optional finder signatures are classified inside the same image traversal, compatible wire variants share the physical-family sample, and the scheduler chooses at most one irreducible wire correction per frame. Disabled capabilities add no stream route.

The zero value is ready to use. A Stream is not safe for concurrent use; decode one coherent frame sequence in order. Results are deterministic for a given frame sequence. For isolated images or an exhaustive attempt use Decode.

func NewStream

func NewStream() *Stream

NewStream returns a Stream ready for its first frame.

func (*Stream) Decode

func (st *Stream) Decode(img image.Image) ([]byte, error)

Decode reads one frame within the stream's fixed work budget, reusing geometry and compatible evidence retained from earlier frames.

func (*Stream) DecodeMessage

func (st *Stream) DecodeMessage(img image.Image) (Message, error)

DecodeMessage reads one frame once and returns raw application data alongside its standards-facing reader transmission.

func (*Stream) Reset

func (st *Stream) Reset()

Reset discards retained geometry, pending searches, and cross-frame evidence. Call it before reusing a Stream for a different coherent frame sequence.

Directories

Path Synopsis
cmd
jabcode command
Command jabcode encodes and decodes JAB Code symbols.
Command jabcode encodes and decodes JAB Code symbols.
internal
core
Package core holds the types shared by the detection and decoding stages: the pixel Bitmap, floating-point geometry (PointF, Perspective), the decoded-symbol result types, the shared status codes, and small per-pixel colour statistics.
Package core holds the types shared by the detection and decoding stages: the pixel Bitmap, floating-point geometry (PointF, Perspective), the decoded-symbol result types, the shared status codes, and small per-pixel colour statistics.
decode
Package decode turns a sampled symbol matrix into message bits: metadata and palette decoding, module colour classification, and the LDPC/demask/deinterleave message decode, for primary and docked secondary symbols.
Package decode turns a sampled symbol matrix into message bits: metadata and palette decoding, module colour classification, and the LDPC/demask/deinterleave message decode, for primary and docked secondary symbols.
detect
Package detect locates JAB Code symbols in an image: channel balancing and binarization (with descreen retries sized from the image's own lattice pitch), finder- and alignment-pattern detection, side-size estimation, perspective sampling of the module grid, and the region-of-interest proposer.
Package detect locates JAB Code symbols in an image: channel balancing and binarization (with descreen retries sized from the image's own lattice pitch), finder- and alignment-pattern detection, side-size estimation, perspective sampling of the module grid, and the region-of-interest proposer.
diag
Package diag renders the observation trace produced by the authoritative decoder behind jabcode decode --diag.
Package diag renders the observation trace produced by the authoritative decoder behind jabcode decode --diag.
ecc
Package ecc implements the JAB Code forward-error-correction stage: systematic LDPC coding (hard- and soft-decision), the fixed byte (de)interleaving permutation, and the seeded PRNG they share.
Package ecc implements the JAB Code forward-error-correction stage: systematic LDPC coding (hard- and soft-decision), the fixed byte (de)interleaving permutation, and the seeded PRNG they share.
encode
Package encode implements the JAB Code encoding pipeline: data analysis and bit-stream generation, LDPC and interleaving, module placement, masking, and bitmap rendering, for single- and multi-symbol codes.
Package encode implements the JAB Code encoding pipeline: data analysis and bit-stream generation, LDPC and interleaving, module placement, masking, and bitmap rendering, for single- and multi-symbol codes.
ldpccatalog
Package ldpccatalog carries the precomputed pivot transcripts of every message parity-check code the decoder can select.
Package ldpccatalog carries the precomputed pivot transcripts of every message parity-check code the decoder can select.
ldpccatalog/gen command
Command gen writes the precomputed pivot transcripts a jabcode_ldpc_catalog_blob build embeds.
Command gen writes the precomputed pivot transcripts a jabcode_ldpc_catalog_blob build embeds.
palette
Package palette holds the JAB Code module color palettes shared by the encoder and decoder.
Package palette holds the JAB Code module color palettes shared by the encoder and decoder.
phaseprobe
Package phaseprobe provides opt-in process-timeline instrumentation for GPU route diagnostics.
Package phaseprobe provides opt-in process-timeline instrumentation for GPU route diagnostics.
read
Package read coordinates detection and decoding into the full JAB Code reading pipeline: it owns the orientation and region-of-interest retries, the detect-then-decode handoff for the primary symbol (including the alignment-pattern fallback that needs the decoded side version), and the docked-secondary walk that derives each secondary's geometry from its decoded host metadata.
Package read coordinates detection and decoding into the full JAB Code reading pipeline: it owns the orientation and region-of-interest retries, the detect-then-decode handoff for the primary symbol (including the alignment-pattern fallback that needs the decoded side version), and the docked-secondary walk that derives each secondary's geometry from its decoded host metadata.
spec
Package spec holds JAB Code symbol geometry, metadata layout, masking and finder core-color constants shared by the encoder and decoder.
Package spec holds JAB Code symbol geometry, metadata layout, masking and finder core-color constants shared by the encoder and decoder.
tables
Package tables holds the JAB Code static lookup tables (encoding, alignment-pattern, and palette/finder geometry) shared by the encoder and decoder.
Package tables holds the JAB Code static lookup tables (encoding, alignment-pattern, and palette/finder geometry) shared by the encoder and decoder.
testutil
Package testutil provides shared helpers for the test suites of jabcode's internal packages.
Package testutil provides shared helpers for the test suites of jabcode's internal packages.
wasmgate command
Command wasmgate executes the public fixed-plan and stream path.
Command wasmgate executes the public fixed-plan and stream path.
wire
Package wire defines internal JAB Code wire variants, decoder capability sets and encoder format choices.
Package wire defines internal JAB Code wire variants, decoder capability sets and encoder format choices.
Package jabenc provides the dependency-light public JAB Code write path.
Package jabenc provides the dependency-light public JAB Code write path.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL