etiket

Zero-dependency barcode & QR code SVG, PNG generator. 40+ formats, styled QR codes, tree-shakeable. Pure TypeScript.

442
15
442
1
TypeScript
public


etiket — Zero-dependency barcode & QR code generator (SVG & PNG)

etiket

Zero-dependency barcode & QR code generator — SVG & PNG output.
40+ formats, styled QR codes, tree-shakeable. Pure TypeScript, works everywhere.

npm version npm downloads bundle size license

[!IMPORTANT]
Verified formats (18): QR Code, Data Matrix, PDF417, Aztec, Micro QR, rMQR, MaxiCode, MicroPDF417, Code 128, EAN-13, EAN-8, UPC-A, Code 39, Code 93, ITF, Codabar, GS1-128, Codablock F — verified with round-trip scan tests (zxing-wasm, rxing, gozxing) and/or 100% bit-match against Zint/bwip-js reference.

Experimental formats: DotCode, Han Xin, JAB Code — no open-source decoder exists for these formats. Encoders produce structurally valid output (Han Xin 75% reference match, finders 100%). PRs welcome.

Contributions welcome! If you find a scanning issue or want to improve an encoder, please open an issue or submit a PR. See Contributing below.

Quick Start

npm install etiket
import { barcode, qrcode } from "etiket"

const svg = barcode("Hello World")
const qr = qrcode("https://example.com", { dotType: "dots", ecLevel: "H" })

CLI

npx etiket list                       # every supported symbology
npx etiket qr "Hello World" -o qr.svg
npx etiket qr "Hello" --terminal
npx etiket qr "Hello" --size 300 --ec H --dot-type dots
npx etiket qr "Hello" -o qr.png       # .png output writes a PNG
npx etiket barcode "4006381333931" --type ean13 --show-text -o barcode.svg
npx etiket postal "SN34RD1A" --type rm4scc -o postal.svg
npx etiket datamatrix "Hello" -o dm.svg
npx etiket pdf417 "Hello" -o pdf.svg
npx etiket aztec "Hello" -o aztec.svg
npx etiket maxicode "Hello" -o maxi.svg
npx etiket wifi "MyNetwork" "secret123" -o wifi.svg
npx etiket contact "Ada Lovelace" --email ada@example.com -o card.svg

Every symbology has a subcommand (qr, microqr, rmqr, barcode, postal,
datamatrix, pdf417, micropdf417, aztec, maxicode, dotcode, hanxin,
codablockf, code16k, jabcode) plus the wifi, contact and link
helpers. Add --png or use a .png output path for raster output.

Tree Shaking

Import only what you need:

import { barcode, barcodeDataURI, barcodeBase64 } from "etiket/barcode"
import { postal, encodePostal } from "etiket/postal"
import { qrcode, qrcodeDataURI, qrcodeBase64, qrcodeTerminal, microqr, rmqr } from "etiket/qr"
import { datamatrix, gs1datamatrix } from "etiket/datamatrix"
import { pdf417, micropdf417 } from "etiket/pdf417"
import { aztec } from "etiket/aztec"
import { maxicode, dotcode, hanxin, codablockf, code16k } from "etiket/2d"
import { barcodePNG, qrcodePNG, postalPNG } from "etiket/png" // PNG output
import { validateBarcode, validateQRInput } from "etiket/validators"
import { EtiketError, InvalidInputError, CapacityError } from "etiket/errors"

Supported Formats

1D Barcodes

Format Type Description
Code 128 code128 Auto charset (A/B/C)
Code 39 code39 43-char set, optional check
Code 39 Ext code39ext Full ASCII
Code 93 code93 Higher density, 2 check digits
Code 93 Ext code93ext Full ASCII
EAN-13 ean13 Auto check digit
EAN-8 ean8 Auto check digit
EAN-5 ean5 Addon (book price)
EAN-2 ean2 Addon (issue number)
UPC-A upca 12-digit, auto check digit
UPC-E upce Compressed 8-digit
ITF itf Interleaved 2 of 5
ITF-14 itf14 14-digit with bearer bars
Codabar codabar Libraries, blood banks
MSI Plessey msi Mod10/11/1010/1110
Pharmacode pharmacode Pharmaceutical
Code 11 code11 Telecommunications
GS1-128 gs1-128 AI parsing, FNC1, 100+ AIs
GS1 DataBar gs1-databar Omnidirectional, 14-digit GTIN
GS1 DataBar Limited gs1-databar-limited GTIN starting with 0/1
GS1 DataBar Expanded gs1-databar-expanded Variable-length AI data
Identcode identcode Deutsche Post / DHL
Leitcode leitcode Deutsche Post routing
POSTNET postnet USPS legacy postal
PLANET planet USPS confirmation tracking
Plessey plessey UK library systems

2D Codes

Format Function Description
QR Code qrcode() Versions 1-40, all EC levels, all modes
Micro QR microqr() M1-M4 (11x11 to 17x17)
Data Matrix datamatrix() ECC 200, ASCII/C40/Text auto encoding
GS1 DataMatrix gs1datamatrix() FNC1 + AI parsing
PDF417 pdf417() Text/Byte/Numeric, 9 EC levels, ISO-8859-15
MicroPDF417 micropdf417() Compact PDF417 for small items
Aztec aztec() Compact + full-range, no quiet zone
MaxiCode maxicode() 33×30 hexagonal, UPS shipping labels
rMQR rmqr() Rectangular Micro QR (R7x43 to R17x139)
Codablock F codablockf() Stacked Code 128
Code 16K code16k() Stacked barcode, 2-16 rows
DotCode dotcode() Checkerboard dots, high-speed printing
Han Xin hanxin() Chinese market, 84 versions, 4 finders
JAB Code jabcode() Polychrome (4/8 color), ISO/IEC 23634

Each also has a raw encoder (encodeMicroQR, encodeMaxiCode, …) and, except
for JAB Code, a *PNG() variant.

Postal Barcodes

Postal symbologies are height-modulated — the data lives in each bar’s vertical
extent, not its width — so they have their own encoder and renderer.

Format type Description
POSTNET postnet USPS ZIP (5, 9 or 11 digits)
PLANET planet USPS PLANET (11 or 13 digits)
RM4SCC rm4scc Royal Mail (UK)
KIX kix PostNL (Netherlands)
Australia Post auspost Australia Post
Japan Post jppost Japan Post (Kasutama)
USPS IMb imb Intelligent Mail (US)
import { postal, encodePostal, postalPNG } from "etiket"

postal("12345-6789", { type: "postnet" }) // SVG
postal("SN34RD1A", { type: "rm4scc" })
postal("12345678", { type: "auspost", fcc: "59" })
postal("01234567094987654321", { type: "imb", routingCode: "01234567891" })

postalPNG("12345", { type: "postnet" }) // Uint8Array

// Raw bar states: 'T' | 'A' | 'D' | 'F' (4-state), or 1 / 0 (POSTNET, PLANET)
const bars = encodePostal("SN34RD1A", { type: "rm4scc" })

barcode() accepts postnet and planet and routes them to the postal
renderer automatically. The per-format raw encoders (encodeRM4SCC,
encodeKIX, encodeAustraliaPost, encodeJapanPost, encodeIMb,
encodePOSTNET, encodePLANET) remain available.

Usage

Barcodes

import { barcode } from "etiket"

barcode("Hello World") // Code 128 (default)
barcode("4006381333931", { type: "ean13", showText: true })
barcode("00012345678905", { type: "itf14", bearerBars: true })
barcode("(01)12345678901234(17)260101", { type: "gs1-128" })
barcode("HELLO", { type: "code39", code39CheckDigit: true })
Option Type Default Description
type BarcodeType 'code128' Barcode format
height number 80 Bar height in pixels
barWidth number 2 Width multiplier per module
color string '#000' Bar color
background string '#fff' Background color
showText boolean false Show human-readable text
textPosition 'bottom' | 'top' 'bottom' Text position
fontSize number 14 Text font size
fontFamily string 'monospace' Text font family
margin number 10 Margin around barcode
marginTop number margin Top margin
marginBottom number margin Bottom margin
marginLeft number margin Left margin
marginRight number margin Right margin
textAlign 'center' | 'left' | 'right' 'center' Text alignment
rotation 0 | 90 | 180 | 270 0 Barcode rotation
bearerBars boolean false Bearer bars (ITF-14)
barGap number 0 Extra spacing between bars
unit 'px' | 'mm' | 'in' | 'cm' 'px' Measurement unit
ariaLabel string SVG aria-label attribute
title string SVG <title> element
desc string SVG <desc> element

QR Codes

import { qrcode } from "etiket"

qrcode("https://example.com")
qrcode("Hello", { size: 300, ecLevel: "H", dotType: "rounded" })

// With gradient
qrcode("Test", {
  color: {
    type: "linear",
    rotation: 45,
    stops: [
      { offset: 0, color: "#ff0000" },
      { offset: 1, color: "#0000ff" },
    ],
  },
})

// With corner styling
qrcode("Test", {
  dotType: "dots",
  corners: {
    topLeft: { outerShape: "rounded", innerShape: "dots", outerColor: "#ff0000" },
    topRight: { outerShape: "extra-rounded" },
    bottomLeft: { outerShape: "dots" },
  },
})
Option Type Default Description
size number 200 SVG size in pixels
ecLevel 'L' | 'M' | 'Q' | 'H' 'M' Error correction level
version number auto QR version (1-40)
mode 'numeric' | 'alphanumeric' | 'byte' | 'auto' 'auto' Encoding mode
mask 0-7 auto Mask pattern
color string | GradientOptions '#000' Module color
background string | GradientOptions '#fff' Background color
margin number 4 Quiet zone in modules
dotType DotType 'square' Module shape
dotSize number 1 Module size (0.1-1)
shape 'square' | 'circle' 'square' Overall QR shape
corners object Finder pattern styling
logo LogoOptions Center logo embedding
xmlDeclaration boolean false Add XML declaration
unit 'px' | 'mm' | 'in' | 'cm' 'px' Measurement unit
ariaLabel string SVG aria-label
title string SVG <title> element
desc string SVG <desc> element

Dot types: square, rounded, dots, diamond, classy, classy-rounded, extra-rounded, vertical-line, horizontal-line, small-square, tiny-square

2D Codes

Every 2D, stacked and polychrome symbology has a high-level function returning
SVG:

import {
  datamatrix,
  gs1datamatrix,
  pdf417,
  micropdf417,
  aztec,
  microqr,
  rmqr,
  maxicode,
  dotcode,
  hanxin,
  codablockf,
  code16k,
  jabcode,
} from "etiket"

datamatrix("Hello World")
gs1datamatrix("(01)12345678901231")
pdf417("Hello World", { ecLevel: 4, columns: 5 })
micropdf417("Hello", { columns: 2 })
aztec("Hello World", { ecPercent: 33 })

microqr("12345", { version: 3 })
rmqr("Hello", { ecLevel: "H" })
maxicode("Hello", { mode: 2, postalCode: "123456789", countryCode: 840 })
dotcode("Hello")
hanxin("Hello", { ecLevel: 3 })

// Stacked linear symbologies (rows taller than modules are wide)
codablockf("Hello World", { columns: 8 })
code16k("Hello World")

// Polychrome
jabcode("Hello", { colors: 8 })

Output Formats

import {
  barcode,
  qrcode,
  barcodeDataURI,
  qrcodeDataURI,
  barcodeBase64,
  qrcodeBase64,
  qrcodeTerminal,
  barcodePNG,
  qrcodePNG,
  barcodePNGDataURI,
  qrcodePNGDataURI,
} from "etiket"

// SVG
const svg = qrcode("Hello") // SVG string
const uri = qrcodeDataURI("Hello") // data:image/svg+xml,...
const b64 = qrcodeBase64("Hello") // data:image/svg+xml;base64,...
const term = qrcodeTerminal("Hello") // Terminal (UTF-8 blocks)

// PNG (zero-dependency raster output — no canvas, no native deps)
const png = qrcodePNG("Hello") // Uint8Array
const pngUri = qrcodePNGDataURI("Hello") // data:image/png;base64,...
const barPng = barcodePNG("12345", { type: "code128" }) // Uint8Array

PNG output is available for every format except JAB Code, each with a matching
*PNGDataURI variant:

barcodePNG, postalPNG, qrcodePNG, microqrPNG, rmqrPNG,
datamatrixPNG, gs1datamatrixPNG, pdf417PNG, micropdf417PNG, aztecPNG,
maxicodePNG, dotcodePNG, hanxinPNG, codablockfPNG, code16kPNG.

Raw Encoding

encode() returns the underlying data for any symbology without rendering:

import { encode } from "etiket"

const result = encode("Hello", { type: "qr", qr: { ecLevel: "H" } })

if (result.type === "1d")
  result.bars // bar/space widths
else if (result.type === "2d")
  result.matrix // boolean[][]
else result.bars // postal bar states

Convenience Helpers

import { wifi, email, sms, geo, url, phone, vcard, mecard, event } from "etiket"

wifi("MyNetwork", "password123") // WiFi QR
email("test@example.com") // mailto: QR
sms("+1234567890", "Hello!") // SMS QR
geo(37.7749, -122.4194) // Location QR
url("https://example.com") // URL QR
phone("+1234567890") // tel: QR

// vCard QR
vcard({
  firstName: "John",
  lastName: "Doe",
  phone: "+1234567890",
  email: "john@example.com",
  org: "Acme Inc",
})

// MeCard QR (simpler, used by Android)
mecard({ name: "John Doe", phone: "+1234567890", email: "john@example.com" })

// Calendar event QR
event({
  title: "Meeting",
  start: "2026-04-01T10:00:00",
  end: "2026-04-01T11:00:00",
  location: "Office",
})

Batch Generation

import { barcodes, barcodeSheet, qrcodeSheet } from "etiket"

// Many symbols, one call, shared options
const labels = barcodes(["SKU-001", "SKU-002", "SKU-003"], {
  type: "code128",
  height: 50,
})

// Or a single SVG document holding a grid of them — a label sheet
const sheet = barcodeSheet(
  orders.map((o) => o.tracking),
  {
    type: "code128",
    columns: 3,
    gap: 12,
    labels: orders.map((o) => o.reference),
  },
)

// Progress on a long batch
qrcodeSheet(tickets, { columns: 4, onProgress: (done, total) => bar.update(done / total) })

Validation

import { validateBarcode, isValidInput, validateQRInput } from "etiket"

validateBarcode("4006381333931", "ean13") // { valid: true }
validateBarcode("ABC", "ean13") // { valid: false, error: '...' }
isValidInput("HELLO", "code39") // true

Swiss QR Code

Generate QR-bill payment codes (mandatory in Switzerland since 2022):

import { swissQR } from "etiket"

swissQR({
  iban: "CH4431999123000889012",
  creditor: { name: "Max Muster", postalCode: "8000", city: "Zürich", country: "CH" },
  amount: 1949.75,
  currency: "CHF",
  reference: "210000000003139471430009017",
  referenceType: "QRR",
})

Raw Encoders

Access encoding functions directly for custom rendering:

import {
  encodeCode128,
  encodeEAN13,
  encodeQR,
  encodeDataMatrix,
  encodePDF417,
  encodeAztec,
  renderBarcodeSVG,
  renderQRCodeSVG,
  renderMatrixSVG,
  renderBarcodePNG,
  renderMatrixPNG,
} from "etiket"

const bars = encodeCode128("data") // number[] (bar/space widths)
const matrix = encodeQR("data") // boolean[][] (QR matrix)
const dm = encodeDataMatrix("data") // boolean[][] (Data Matrix)

// SVG rendering
const svg = renderBarcodeSVG(bars, { height: 100 })
const qrSvg = renderQRCodeSVG(matrix, { size: 400, dotType: "dots" })

// PNG rendering
const png = renderBarcodePNG(bars, { height: 100, scale: 2 })
const qrPng = renderMatrixPNG(matrix, { moduleSize: 10, margin: 4 })

Industry Standards

import {
  swissQR,
  gs1datamatrix,
  gs1DigitalLink,
  encodeHIBCPrimary,
  encodeHIBCSecondary,
} from "etiket"

// Swiss QR-bill (mandatory in Switzerland since 2022)
swissQR({
  iban: "CH4431999123000889012",
  creditor: { name: "Max Muster", postalCode: "8000", city: "Zürich", country: "CH" },
  amount: 1949.75,
  currency: "CHF",
})

// GS1 DataMatrix (healthcare, supply chain)
gs1datamatrix("(01)12345678901234(17)260101(10)BATCH01")

// GS1 Digital Link (2027 retail migration)
gs1DigitalLink({ gtin: "09520123456788", batch: "ABC123", serial: "12345" })

// HIBC (medical device labeling, FDA UDI)
const hibc = encodeHIBCPrimary("A123", "PROD456")
barcode(hibc, { type: "code128" }) // Encode in any symbology

// ISBT 128 (blood bank labeling, ISO 7064 Mod 37-2 check character)
const din = encodeISBT128DIN("US", "12345", "26", "000001")
barcode(din, { type: "code128" })

// MaxiCode (UPS shipping labels)
const mc = encodeMaxiCode("Test shipment", {
  mode: 2,
  postalCode: "12345",
  countryCode: 840,
  serviceClass: 1,
})

SVG Accessibility

All SVG renderers support accessibility attributes out of the box:

barcode("123456789", {
  type: "ean13",
  ariaLabel: "EAN-13 barcode for product 123456789",
  title: "Product Barcode",
  desc: "EAN-13 barcode encoding the GTIN 123456789",
})

qrcode("https://example.com", {
  ariaLabel: "QR code linking to example.com",
  title: "Website QR Code",
})

// CSS currentColor support for theme-aware barcodes
barcode("HELLO", { color: "currentColor", background: "transparent" })

Framework Integration

etiket generates plain SVG strings — no DOM required. Works with any framework:

React / Next.js

import { qrcode } from "etiket"

function QRCode({ url }: { url: string }) {
  const svg = qrcode(url, {
    size: 200,
    dotType: "dots",
    ecLevel: "H",
    corners: {
      topLeft: { outerShape: "dots", innerShape: "dots", outerColor: "#ff0000" },
      topRight: { outerShape: "dots", innerShape: "dots" },
      bottomLeft: { outerShape: "dots", innerShape: "dots" },
    },
  })

  return <div dangerouslySetInnerHTML={{ __html: svg }} />
}

Or use a data URI for <img>:

import { qrcodeDataURI } from "etiket"

function QRImage({ url }: { url: string }) {
  return <img src={qrcodeDataURI(url)} alt="QR Code" width={200} height={200} />
}

PNG output (useful for downloads or <canvas>):

import { qrcodePNGDataURI } from "etiket/png"

function QRCodePNG({ url }: { url: string }) {
  return <img src={qrcodePNGDataURI(url, { size: 200 })} alt="QR Code" width={200} height={200} />
}

Note: qrcode() is a pure function with zero DOM dependencies — it works in both Server Components (RSC) and Client Components.

Vue

<script setup lang="ts">
import { qrcode } from "etiket"

const props = defineProps<{ url: string }>()
const svg = computed(() => qrcode(props.url, { dotType: "dots", ecLevel: "H" }))
</script>

<template>
  <div v-html="svg" />
</template>

Svelte

<script lang="ts">
  import { qrcode } from "etiket";

  let { url }: { url: string } = $props();
  let svg = $derived(qrcode(url, { dotType: "dots", ecLevel: "H" }));
</script>

{@html svg}

Angular

import { Component, Input } from "@angular/core"
import { DomSanitizer, SafeHtml } from "@angular/platform-browser"
import { qrcode } from "etiket"

@Component({
  selector: "app-qrcode",
  template: `<div [innerHTML]="svg"></div>`,
})
export class QRCodeComponent {
  svg: SafeHtml = ""

  @Input() set url(value: string) {
    this.svg = this.sanitizer.bypassSecurityTrustHtml(
      qrcode(value, { dotType: "dots", ecLevel: "H" }),
    )
  }

  constructor(private sanitizer: DomSanitizer) {}
}

Astro

---
import { qrcode } from "etiket";

const svg = qrcode("https://example.com", { dotType: "dots", ecLevel: "H" });
---

<Fragment set:html={svg} />

Features

  • Zero dependencies
  • Pure ESM, edge-runtime compatible (Cloudflare Workers, Deno, Bun)
  • TypeScript-first with strict types (TypeScript 7)
  • Tree-shakeable sub-path exports
  • CLI tool (npx etiket)
  • SVG string output (no DOM required) + optimizeSVG() for compact inline
  • PNG raster output (pure JS, zero dependencies) via etiket/png
  • SVG accessibility (ariaLabel, role, title, desc)
  • Measurement units (px, mm, in, cm, pt) for print use cases
  • CSS currentColor support for theme-aware barcodes
  • Auto EC upgrade to H when QR logo is present (supports PNG, JPEG, SVG, ICO)
  • GS1 support (100+ AIs, Digital Link, GS1 QR, GS1 DataMatrix, the full GS1
    DataBar family including the stacked variants)
  • ECI on QR, Data Matrix, PDF417 and Aztec — non-Latin-1 data is declared, not
    truncated
  • QR kanji mode with the real Shift-JIS table, Structured Append across up to
    16 symbols, and optimal multi-segment encoding
  • Batch generation and label sheets (barcodes(), qrcodeSheet())
  • HIBC medical device encoding + ISBT 128 blood bank labeling
  • Swiss QR-bill payments
  • 4-state postal barcodes (RM4SCC, KIX, Australia Post, Japan Post, USPS IMb)
  • Works in browser, Node.js, Deno, Bun, Cloudflare Workers

Verification

Producing a symbol is easy; producing one a scanner accepts is not. Every
symbology here is checked against something that is not this library:

  • Decoded back with zxing-wasm or
    jsQR — QR, Micro QR, rMQR, Data Matrix, PDF417, MicroPDF417, Aztec, MaxiCode,
    Code 128, EAN, UPC, Code 39, Code 93, ITF, Codabar, GS1-128 and every GS1
    DataBar variant.
  • Compared module for module with bwip-js
    (BWIPP) for the formats no JavaScript decoder implements — Code 16K,
    Codablock F, DotCode, Han Xin, MSI, Plessey, Code 11, Pharmacode, Identcode,
    Leitcode, HIBC, POSTNET, PLANET, RM4SCC, KIX, Australia Post, Japan Post and
    USPS IMb.
  • JAB Code is the exception, and says so in its own API docs: no JavaScript
    or WebAssembly decoder exists and neither zxing nor BWIPP implements it, so
    its output cannot be verified and is marked experimental.

test/bwip-compare.test.ts keeps the differences visible rather than asserting
them away: a format that diverges is listed with the issue tracking it, and the
test turns red the moment it starts matching.

Comparison

Feature etiket uqr bwip-js JsBarcode qr-code-styling
Zero dependencies :white_check_mark: :white_check_mark: :x: (1.5MB+) :x: (xmldom) :x: (qrcode)
TypeScript-first :white_check_mark: :white_check_mark: :x: :x: Partial
Tree-shakeable :white_check_mark: :x: :x: :x: :x:
1D barcodes (22 types) :white_check_mark: :x: :white_check_mark: (100+) :white_check_mark: (13) :x:
QR Code (v1-40, all EC) :white_check_mark: :white_check_mark: :white_check_mark: :x: :white_check_mark:
Data Matrix :white_check_mark: :x: :white_check_mark: :x: :x:
PDF417 :white_check_mark: :x: :white_check_mark: :x: :x:
Aztec Code :white_check_mark: :x: :white_check_mark: :x: :x:
QR dot styling (12 types) :white_check_mark: :x: :x: :x: :white_check_mark:
QR gradients :white_check_mark: :x: :x: :x: :white_check_mark:
QR corner styling :white_check_mark: :x: :x: :x: :white_check_mark:
QR logo embedding :white_check_mark: :x: :x: :x: :white_check_mark:
CLI tool :white_check_mark: :x: :white_check_mark: :x: :x:
Terminal output :white_check_mark: :white_check_mark: :x: :x: :x:
Convenience helpers (WiFi, vCard…) :white_check_mark: :x: :x: :x: :x:
Input validation :white_check_mark: :x: :x: :x: :x:
SVG output :white_check_mark: :white_check_mark: :white_check_mark: :white_check_mark: :white_check_mark:
PNG output :white_check_mark: :x: :white_check_mark: :white_check_mark: :white_check_mark:
Pure ESM :white_check_mark: :white_check_mark: :x: (CJS) :x: (CJS) :x: (CJS)
Bundle size (gzip) ~24KB ~12KB ~160KB ~15KB ~30KB+deps

etiket is the only library that combines 1D barcodes + 2D codes + styled QR codes + SVG & PNG output + zero dependencies + tree-shaking in a single package.

Inspiration & Credits

Built from scratch, inspired by these excellent libraries:

  • uqr — Pure SVG QR approach, terminal rendering
  • bwip-js — Comprehensive barcode format reference (100+ types)
  • JsBarcode — Encoding table validation, barcode rendering patterns
  • qr-code-styling — QR styling concepts (dot types, gradients, corners, logos)

Standards: ISO/IEC 15417 (Code 128), ISO/IEC 15420 (EAN/UPC), ISO/IEC 18004 (QR), ISO/IEC 16022 (Data Matrix), ISO/IEC 15438 (PDF417), ISO/IEC 24778 (Aztec), ISO/IEC 24724 (GS1 DataBar), ISO/IEC 16023 (MaxiCode), ISO/IEC 23941 (rMQR), ISO/IEC 20830 (Han Xin), ISO/IEC 23634 (JAB Code), AIM ISS DotCode 4.0 (DotCode), USPS-B-3200 (IMb).

Contributing

Contributions are welcome! Here are some areas where help is especially appreciated:

Encoder improvements needed:

  • DotCode — Symbol size selection tables and mask pattern per AIM ISS DotCode 4.0
  • Han Xin — Separator bands, data placement, and GB 18030 Chinese character encoding (75% reference match, finders 100%)
  • JAB Code — Full LDPC error correction per ISO/IEC 23634

Other contributions:

  • Data Matrix DMRE rectangular sizes (#71)
  • GS1 DataBar stacked variants (#61)
  • Round-trip scan tests for experimental formats
  • Documentation improvements
pnpm install    # Install dependencies
pnpm dev        # Run tests in watch mode
pnpm test       # Lint + typecheck + test
pnpm build      # Build for production

License

Published under the MIT license.

v0.3.3[beta]