etiket

repository·main·Indexed 19 days ago

https://github.com/productdevbook/etiket

A zero-dependency TypeScript library for generating barcodes and QR codes in SVG and PNG formats. It supports over 40 symbologies, including 1D barcodes, 2D codes, and specialized postal barcodes. Features include a CLI, batch generation for label sheets, raw encoding access, and specialized support for industry standards like GS1, HIBC, ISBT 128, and Swiss QR-bills. The library is tree-shakeable and compatible with various frameworks including React, Next.js, Vue, Svelte, Angular, and Astro.

Tokens
102.5K
Snippets
341
Records
433
Agent score
61%

What's inside etiket

  1. Key features of etiket

    main

    Core Capabilities

    • Zero dependencies: Pure ESM and edge-runtime compatible (Cloudflare Workers, Deno, Bun).
    • TypeScript-first: Strict types (TypeScript 7).
    • Tree-shakeable: Sub-path exports allow for minimal bundle sizes.
    • Output Formats: Generates SVG strings (no DOM required) and PNG raster output via etiket/png.
    • Advanced Symbologies: Supports 1D barcodes (22 types), 2D codes (QR, Data Matrix, Aztec, PDF417), GS1 (including DataBar family), and 4-state postal barcodes.
    • QR Customization: Supports 12 dot styles, corner styling, and automatic EC (Error Correction) upgrade to 'H' when a logo is present.
    • Print Ready: Supports measurement units like px, mm, in, cm, and pt. Supports CSS currentColor for theme-aware rendering.
  2. Understand HIBC as a data format

    main

    HIBC (Health Industry Bar Code, ANSI/HIBC 2.6) is a data format, not a symbology. It defines how to structure data like labeller codes, product numbers, expiry, and lot numbers.

    To create a physical barcode, you must first use etiket's HIBC functions to generate the formatted string, and then pass that string to a barcode generator like barcode() (for Code 128 or Code 39) or datamatrix().

  3. How GS1 DataBar expanded encodation works

    main

    The gs1-databar-expanded and gs1databarExpandedStacked variants use parenthesized Application Identifier (AI) strings. The encoder automatically selects the most efficient compression method from ISO/IEC 24724 Table 9.

    To trigger the most efficient compressed methods (Methods 3–14), the GTIN must use a (01) indicator digit of 9 (the variable-measure trade item indicator). If the data does not qualify for compression, the encoder falls back to standard methods.

    Examples of encodation:

    • Method 3 (Compressed): (01)9... followed by weight AI like (3103).
    • Method 1 (Standard): (01)9... followed by general data like (10)LOT42.
    • Method 2 (No GTIN): Data without a (01) prefix, e.g., (10)LOT42(21)SERIAL.
    import { encodeGS1DataBarExpanded } from "etiket"
    
    // Method 3: compressed GTIN + weight
    encodeGS1DataBarExpanded("(01)90012345678908(3103)001750")
    
    // Method 1: GTIN plus general data
    encodeGS1DataBarExpanded("(01)90012345678908(10)LOT42")
    
    // Method 2: no GTIN at all
    encodeGS1DataBarExpanded("(10)LOT42(21)SERIAL")
  4. Use Structured Carrier Messages (Modes 2 and 3)

    main

    Modes 2 and 3 are used for parcel carrier labels (like UPS) to pack a 60-bit primary message (postal code, country, and service class) ahead of the secondary message.

    • Mode 2: Uses a numeric postcode. A 5-digit US ZIP (country 840) is zero-filled to 9 digits. The postcode must be 1–9 digits.
    • Mode 3: Uses an alphanumeric postcode (1–6 characters from A-Z, space, and ASCII range " to :).

    If the postalCode is malformed (e.g., non-numeric in Mode 2, or out-of-charset in Mode 3), the library will throw an InvalidInputError.

    import { maxicode } from "etiket"
    
    // US: numeric ZIP+4 (Mode 2)
    maxicode("SHIPMENT 4711", {
      mode: 2,
      postalCode: "12345",
      countryCode: 840,
      serviceClass: 1,
    })
    
    // UK: alphanumeric postcode (Mode 3)
    maxicode("PACKAGE", {
      mode: 3,
      postalCode: "SN34RD",
      countryCode: 826,
      serviceClass: 11,
    })
  5. Configure QR Code Encoding Modes

    main

    The mode option allows you to force a specific encoding mode for the entire payload. By default, mode: "auto" is used, which optimizes the payload by picking the cheapest split between modes (e.g., mixing numeric and alphanumeric segments).

    ModeCharactersEfficiency
    numeric0-93.3 digits/10 bits
    alphanumeric0-9, A-Z, $%*+-./:2 chars/11 bits
    byteAny (UTF-8)1 byte/8 bits
    kanjiShift-JIS double byte1 char/13 bits
    autoAny — picks the cheapest splitdefault

    Note: Forcing a mode encodes the whole payload as a single segment.

    import { qrcode } from "etiket"
    
    qrcode("12345", { mode: "numeric" })
    qrcode("HELLO", { mode: "alphanumeric" })
    qrcode("hello", { mode: "byte" })
    qrcode("漢字テスト", { mode: "kanji" })
    qrcode("auto detected") // mode: "auto" (default)
  6. Use Payload Helpers to generate QR code strings

    main

    QR codes require specific string formats (conventions) to trigger actions like joining a Wi-Fi network or opening a contact card. etiket provides payload helpers that build these convention-compliant strings and return a rendered SVG.

    Each helper accepts the same options as qrcode() as its last argument (QRCodeSVGOptions & QRCodeOptions), allowing you to control styling, error correction level (ecLevel), and sizing.

    import { url } from "etiket"
    
    // The helper returns a rendered SVG string
    url("https://example.com", { size: 320, ecLevel: "H", dotType: "rounded" })
  7. Understand the RasterData structure

    main

    The RasterData object provides a lightweight representation of the barcode/matrix pixels. It uses one byte per pixel, where 0 represents the background and 1 represents the foreground.

    Note on performance: Rows are shared where they repeat (e.g., in 1D barcodes). Treat the rows array as read-only; mutating a single Uint8Array in the rows array can change multiple visible rows in the final image.

    interface RasterData {
      width: number          // pixels
      height: number         // pixels
      rows: Uint8Array[]     // one row per pixel row; 0 = background, 1 = foreground
    }
  8. KIX input requirements and constraints

    main

    Input Format

    • Allowed Characters: Uppercase A-Z and digits 0-9.
    • Payload Composition: PostNL requires a specific concatenation of postcode, house number, and addition (padded out) with no separators. This composition is the user's responsibility.

    Important Caveats

    • No Check Character: KIX does not have a check character. A transcription error will result in a valid-looking barcode for the wrong address. Always validate the address upstream.
    • Error Handling: Lowercase letters or punctuation will throw an InvalidInputError.
    • No Framing: Because there are no start/stop bars, a KIX barcode is structurally different from an RM4SCC barcode even if they contain the same data. Ensure your scanner is configured specifically for KIX.
    • Encoding Restriction: Do not use encodeBars() for postal types; KIX data is encoded in bar height, not width.
  9. Use JAB Code for visual mock-ups

    main

    JAB Code (ISO/IEC 23634) is a polychrome symbology where modules carry color indices.

    WARNING

    Experimental — not conformant. The etiket JAB Code encoder is not ISO/IEC 23634 conformant. It uses an XOR parity scheme instead of LDPC codes, and its metadata/finder patterns differ from the spec. No conforming reader will decode these symbols. Use it only for layout experiments and visual mock-ups, not for production scanning.

  10. GS1 Composite implementation caveats

    main

    When using encodeGS1Composite, be aware of the following technical limitations:

    • Linkage Flag: The library does not set the linkage flag in the linear component. A reader will decode both parts but will not treat them as a single linked pair.
    • Encoding Standards: The composite component is generated by encoding the AI string directly through MicroPDF417 or PDF417. It does not implement the CC-A specific base-928 encodation required by GS1 General Specifications.
    • Input Validation:
      • Input wrapped in parentheses (e.g., (10)) is validated as an AI string. Malformed AI strings raise InvalidInputError.
      • Input without parentheses is treated as raw element string data and is not validated.
      • Empty data or unknown composite types raise InvalidInputError.
    • Capacity Management: There is no automatic promotion from CC-A to CC-B or CC-C. You must manually handle CapacityError and retry with a larger type.
  11. Plessey character set and check digit rules

    main

    When encoding data for Plessey, follow these constraints:

    • Character Set: Only hexadecimal digits 0-9 and A-F are allowed. Input is case-insensitive.
    • Check Digits: The symbology automatically appends two CRC check digits using the polynomial x⁸ + x⁷ + x⁶ + x⁵ + x³ + 1.
    • Warning: Do not include check digits in your input string, as they will be treated as data and additional check digits will be appended on top of them.
  12. Understand Symbology Selector types

    main

    etiket uses specific types to categorize different symbology families. Use these types to ensure your configuration matches the capabilities of the function you are calling:

    • BarcodeType: Represents the 27 width-modulated linear (1D) symbologies. Used by barcode and encodeBars.
    • EncodeType: A superset that includes BarcodeType plus postal, 2D, and stacked symbologies. Used by the general encode function.
    • PostalType: Specifically for postal symbologies. Includes values like "postnet", "planet", "rm4scc", "kix", "auspost", "jppost", and "imb". Used by postal and encodePostal.
    import type { BarcodeType, EncodeType, PostalType } from "etiket"
    import { barcode, encode, postal } from "etiket"
    
    const linear: BarcodeType = "code39"
    const anything: EncodeType = "aztec"
    const mail: PostalType = "rm4scc"
    
    barcode("HELLO", { type: linear })
    encode("Hello", { type: anything })
    postal("SN34RD1A", { type: mail })