▩ Chilkat.Formats · Encoding

Payloads & QR Encoding
payload · encoding

What goes into the symbol and how it is encoded: text or binary payloads, QR version and error correction, masks, modes, ECI and structured append.

Payload

What to encode

The payload object is required for generate, measure and validate. Two payload types are available.

MemberValuesDefaultNotes
payload.type"text" · "binary""text"Structured payload types (url, wifi, vcard, …) are reserved and currently rejected with unsupportedFeature.
payload.textUTF-8 string, up to 65,536 bytes—Required when type is text. The text is encoded as its UTF-8 bytes. Embedded NUL characters are allowed. Must be well-formed UTF-8.
payload.base64base64 string, up to 65,536 decoded bytes—Required when type is binary. Any byte values, 0–255. The text must be standard base64 (A–Z a–z 0–9 + /): white space is ignored and the = padding may be omitted, but anything else, including the URL-safe alphabet (- and _) and misplaced padding, is rejected with wrongType rather than decoded as different bytes.
text payload
{ "payload": { "text": "https://www.chilkatsoft.com" } }
binary payload
{ "payload": { "type": "binary",
               "base64": "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=" } }
A QR symbol holds at most 2,953 bytes (version 40, level L). Larger payloads need structured append; a payload that fits no admitted version fails with capacityExceeded at /payload. Use validate with scope capacity to check ahead of time.

Encoding mode

Numeric, alphanumeric and byte

QR has compact encodings for digits and for an upper-case alphanumeric set. By default Chilkat picks the densest mode the whole payload fits. The selected mode is reported in symbols[0].selectedEncoding.mode.

ModeCharactersBits per characterExample
numeric0–93⅓0123456789012345 → version 1, 16 digits
alphanumeric0–9, A–Z (upper case only), space, $ % * + - . / :5½HELLO WORLD 2026 → version 1
byteany UTF-8 text or binary bytes8Grüße aus München → version 2

Force a mode with encoding.qr.mode ("auto", "numeric", "alphanumeric", "byte"). A mode the payload cannot use — for example numeric for "abc" — is rejected with conflictingOptions at /encoding/qr/mode. kanji is reserved and not yet available. Each payload is encoded as one segment in v11.7.0; encoding.qr.optimizeSegments is accepted for forward compatibility.

force byte mode for an all-digit payload
{ "payload": { "text": "20261024" }, "encoding": { "qr": { "mode": "byte" } } }

Error correction

Level L, M, Q or H

Reed–Solomon error correction lets a damaged or partly obscured symbol still be read. Higher levels cost capacity, so the same payload often needs a larger version.

LevelRecoverable codewordsTypical use
L~7%Clean screens and labels where size matters most.
M default~15%General purpose. Also what "auto" selects.
Q~25%Industrial labels, dirt and wear.
H~30%Maximum robustness; needed if part of the symbol will be covered.
request
{ "payload": { "text": "https://www.chilkatsoft.com" },
  "encoding": { "qr": { "errorCorrection": "H" } } }

boostErrorCorrection

When the chosen version has spare capacity, "boostErrorCorrection": true raises the level as far as the payload still fits in the same version, so robustness improves at no cost in size. The level actually used is reported in selectedEncoding.errorCorrection.

request
{ "payload": { "text": "x" }, "encoding": { "qr": { "boostErrorCorrection": true } } }
// response: "selectedEncoding": { "version": 1, "errorCorrection": "H", ... }

Version (size)

Automatic, exact, or bounded

A QR version is its size in modules: version 1 is 21 × 21, each further version adds 4 modules, up to version 40 at 177 × 177. By default the smallest version that holds the payload at the selected error-correction level is used.

MemberValuesDefaultNotes
encoding.qr.version1–40 or "auto""auto"An exact version. If the payload does not fit it, capacityExceeded.
encoding.qr.minVersion1–401Lower bound for automatic selection — e.g. to keep all labels in a batch the same size.
encoding.qr.maxVersion1–4040Upper bound for automatic selection. Must not be below minVersion, and an exact version must satisfy both.
fix the size of every label in a batch
{ "payload": { "text": "SKU-00042" }, "encoding": { "qr": { "minVersion": 4, "maxVersion": 4 } } }

Mask

Automatic by the standard penalty rules, or explicit

Before a QR symbol is finished, one of eight mask patterns is XOR-ed over the data area to break up large uniform regions. ISO/IEC 18004 scores each candidate with four penalty rules and takes the lowest score; Chilkat does exactly that for "auto" (ties are resolved deterministically, so the same request always gives the same symbol). The chosen mask is reported in selectedEncoding.mask.

Set encoding.qr.mask to 0–7 to force a pattern — useful for regression tests or to reproduce a symbol generated elsewhere. Values outside 0–7 are outOfRange.

request
{ "payload": { "text": "https://www.chilkatsoft.com" }, "encoding": { "qr": { "mask": 5 } } }

Character set

UTF-8 and ECI

Text is always encoded as UTF-8 bytes. By default no ECI (Extended Channel Interpretation) header is emitted, which is what virtually every reader expects. Set encoding.eci to 26 to declare UTF-8 explicitly inside the symbol.

MemberValuesDefaultNotes
encoding.charset"utf-8""utf-8"Other charsets are reserved (unsupportedFeature). Not applicable to binary payloads.
encoding.eci26 or "none""none"26 = UTF-8 (ISO/IEC 18004 ECI assignment). Other assignment numbers are reserved. Not applicable to binary payloads (conflictingOptions).
request
{ "payload": { "text": "日本語のテキスト" }, "encoding": { "eci": 26 } }

Structured append

Split a large payload across up to 16 symbols

A structured-append set is a sequence of QR symbols that a reader reassembles into one payload. Each part carries its index, the total count and a parity byte computed over the whole payload. Chilkat splits the payload as evenly as possible.

MemberValuesDefaultNotes
encoding.structuredAppend.enabledboolfalseTurns on splitting. With true and no count, the smallest number of parts (1–maxSymbols) that fits is used; a set of 1 is an ordinary symbol.
encoding.structuredAppend.maxSymbols1–1616Upper bound for automatic splitting.
encoding.structuredAppend.symbolCount1–16autoAn exact part count. Must not exceed maxSymbols; each part must still hold its share of the payload.

The response has one symbols[] entry, one measurements[] entry and one outputs[] entry per part, in order. Each symbol reports structuredAppend: { "index", "total", "parity" } (index is 0-based) and its own payloadBytes.

request: three parts, written to numbered files
{
  "payload":  { "text": "...a 120-character string..." },
  "encoding": { "structuredAppend": { "enabled": true, "symbolCount": 3 } },
  "output":   { "return": "none", "pathTemplate": "/labels/part{index}.png" }
}
response (abridged)
{
  "symbols": [
    { "symbology": "qr", "selectedEncoding": { "version": 3, "errorCorrection": "M", "mask": 2, "mode": "byte" },
      "dimensions": { "modulesWide": 29, "modulesHigh": 29, ... },
      "structuredAppend": { "index": 0, "total": 3, "parity": 120 }, "payloadBytes": 40 },
    { ... "structuredAppend": { "index": 1, "total": 3, "parity": 120 }, "payloadBytes": 40 },
    { ... "structuredAppend": { "index": 2, "total": 3, "parity": 120 }, "payloadBytes": 40 }
  ],
  "outputs": [
    { "index": 0, "format": "png", ..., "path": "/labels/part1.png" },
    { "index": 1, "format": "png", ..., "path": "/labels/part2.png" },
    { "index": 2, "format": "png", ..., "path": "/labels/part3.png" }
  ], ...
}
With more than one part, output.path (a single destination) is a conflictingOptions error; use output.pathTemplate with {index} (1-based), or return the images inline. See writing files.

Reading the result

selectedEncoding and dimensions

The response never echoes "auto": selectedEncoding holds the resolved choices, so you always know exactly what was generated.

MemberMeaning
symbols[i].symbology"qr"
symbols[i].selectedEncoding.versionThe QR version used, 1–40.
symbols[i].selectedEncoding.errorCorrectionL, M, Q or H (after any boost).
symbols[i].selectedEncoding.maskThe mask pattern, 0–7.
symbols[i].selectedEncoding.modenumeric, alphanumeric or byte.
symbols[i].selectedEncoding.eciPresent only when an ECI header was emitted (26).
symbols[i].dimensionsmodulesWide, modulesHigh and the four quiet-zone widths in modules.
symbols[i].structuredAppendPresent for a structured-append set: index (0-based), total, parity.
symbols[i].payloadBytesBytes encoded in this symbol.
Read the selected encoding — C#
int version = jsonOut.IntOf("symbols[0].selectedEncoding.version");
string level = jsonOut.StringOf("symbols[0].selectedEncoding.errorCorrection");
int modules = jsonOut.IntOf("symbols[0].dimensions.modulesWide");
int numParts = jsonOut.SizeOfArray("symbols");   // > 1 for structured append
Read the selected encoding — C++
int version = jsonOut.IntOf("symbols[0].selectedEncoding.version");
const char *level = jsonOut.stringOf("symbols[0].selectedEncoding.errorCorrection");
int modules = jsonOut.IntOf("symbols[0].dimensions.modulesWide");
int numParts = jsonOut.SizeOfArray("symbols");   // > 1 for structured append
Read the selected encoding — Python
version = jsonOut.IntOf("symbols[0].selectedEncoding.version")
level = jsonOut.StringOf("symbols[0].selectedEncoding.errorCorrection")
modules = jsonOut.IntOf("symbols[0].dimensions.modulesWide")
numParts = jsonOut.SizeOfArray("symbols")   # > 1 for structured append
Not using C#, C++ or Python? The example is shown in three languages only to give a feel for the code. The Chilkat classes, including Formats, have the same methods and the same JSON requests in every supported language: C, C++, C#, VB.NET, Java, Objective-C, Swift, Python, PHP, Perl, Ruby, Node.js, Electron, React Native, Dart / Flutter, Go, Rust, Zig, Delphi, C++Builder, Free Pascal / Lazarus, Classic ASP, VB6, VBScript, FoxPro, DataFlex, PowerBuilder, Xojo, Xbase++, PureBasic, AutoIt, B4A / B4J, LabVIEW, SQL Server and Tcl. See Chilkat Downloads for Every Language.