summaryrefslogtreecommitdiff
path: root/docs/handbook/genqrcode/micro-qr.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/handbook/genqrcode/micro-qr.md')
-rw-r--r--docs/handbook/genqrcode/micro-qr.md456
1 files changed, 456 insertions, 0 deletions
diff --git a/docs/handbook/genqrcode/micro-qr.md b/docs/handbook/genqrcode/micro-qr.md
new file mode 100644
index 0000000000..d202ceb76c
--- /dev/null
+++ b/docs/handbook/genqrcode/micro-qr.md
@@ -0,0 +1,456 @@
+# genqrcode / libqrencode — Micro QR Code Support
+
+## Overview
+
+Micro QR Code is a compact variant of QR Code standardized in JIS X0510:2004 and ISO/IEC 18004. libqrencode supports Micro QR versions M1 through M4 via dedicated spec tables in `mqrspec.c`, masking in `mmask.c`, and encoding paths in `qrencode.c`.
+
+---
+
+## Micro QR vs. Full QR
+
+| Feature | Full QR Code | Micro QR Code |
+|---|---|---|
+| Versions | 1–40 | M1–M4 (1–4 internally) |
+| Max constant | `QRSPEC_VERSION_MAX = 40` | `MQRSPEC_VERSION_MAX = 4` |
+| Finder patterns | 3 | 1 |
+| Alignment patterns | 0–46 per version | None |
+| Timing patterns | Horizontal + Vertical | Horizontal + Vertical |
+| Version information | Versions 7+ | Never |
+| Mask patterns | 8 | 4 |
+| Mask selection | Minimize penalty | Maximize edge score |
+| EC Levels | L, M, Q, H | Version-dependent subset |
+| Structured append | Supported | Not supported |
+| ECI mode | Supported | Not supported |
+| FNC1 mode | Supported | Not supported |
+
+---
+
+## Version Capacities
+
+From `mqrspec.c`:
+
+```c
+typedef struct {
+ int width;
+ int ec[4];
+} MQRspec_Capacity;
+
+static const MQRspec_Capacity mqrspecCapacity[MQRSPEC_VERSION_MAX + 1] = {
+ {0, { 0, 0, 0, 0}},
+ {11, { 2, 0, 0, 0}}, // M1: 11×11
+ {13, { 5, 4, 0, 0}}, // M2: 13×13
+ {15, {11, 9, 7, 0}}, // M3: 15×15
+ {17, {16, 14, 10, 0}} // M4: 17×17
+};
+```
+
+Width formula: `version * 2 + 9` (vs. `version * 4 + 17` for full QR).
+
+### Detailed Capacities
+
+| Version | Width | Total Data Words | L Data | M Data | Q Data | H Data |
+|---|---|---|---|---|---|---|
+| M1 | 11 | 5 | 2* | — | — | — |
+| M2 | 13 | 10 | 5 | 4 | — | — |
+| M3 | 15 | 17 | 11 | 9 | 7 | — |
+| M4 | 17 | 24 | 16 | 14 | 10 | — |
+
+*M1 has error detection only, not error correction.
+
+### ECC Lengths
+
+```c
+int MQRspec_getECCLength(int version, QRecLevel level)
+{
+ return mqrspecCapacity[version].width * mqrspecCapacity[version].width
+ - mqrspecCapacity[version].ec[level]
+ - ... (function pattern modules) ...;
+}
+```
+
+Returns 0 for unsupported version/level combinations, which `QRinput_newMQR()` uses to reject invalid inputs.
+
+### Character Capacities by Mode
+
+| Version | Level | Numeric | Alphanumeric | Byte | Kanji |
+|---|---|---|---|---|---|
+| M1 | — | 5 | — | — | — |
+| M2 | L | 10 | 6 | — | — |
+| M2 | M | 8 | 5 | — | — |
+| M3 | L | 23 | 14 | 9 | 6 |
+| M3 | M | 18 | 11 | 7 | 4 |
+| M4 | L | 35 | 21 | 15 | 9 |
+| M4 | M | 30 | 18 | 13 | 8 |
+| M4 | Q | 21 | 12 | 9 | 5 |
+
+---
+
+## Mode Restrictions
+
+Not all encoding modes are available in every Micro QR version:
+
+| Version | Numeric | Alphanumeric | 8-bit | Kanji | ECI | FNC1 | Structured |
+|---|---|---|---|---|---|---|---|
+| M1 | Yes | No | No | No | No | No | No |
+| M2 | Yes | Yes | No | No | No | No | No |
+| M3 | Yes | Yes | Yes | Yes | No | No | No |
+| M4 | Yes | Yes | Yes | Yes | No | No | No |
+
+This is enforced by `QRinput_encodeBitStream()` which calls validation functions:
+
+```c
+static int QRinput_isModeNumValid(int version, QRinput_List *entry, int mqr)
+{
+ if(mqr) {
+ if(MQRspec_maximumWords(QR_MODE_NUM, version) < entry->size)
+ return -1;
+ }
+ return 0;
+}
+```
+
+`MQRspec_maximumWords()` returns 0 for unsupported modes at a given version.
+
+---
+
+## MQR-Specific Encoding
+
+### Mode Indicator Sizes
+
+Micro QR uses shorter mode indicators than full QR. From `mqrspec.c`:
+
+```c
+// MQR mode indicator bit lengths per version:
+// M1: 0 bits (only numeric, implied)
+// M2: 1 bit
+// M3: 2 bits
+// M4: 3 bits
+```
+
+These are retrieved by `MQRspec_lengthIndicator()`.
+
+### Character Count Indicator Sizes
+
+```c
+static const int lengthTableBits[4][4] = {
+ { 3, 0, 0, 0}, // QR_MODE_NUM: M1=3, M2=4, M3=5, M4=6
+ { 0, 3, 0, 0}, // QR_MODE_AN: M1=0, M2=3, M3=4, M4=5
+ { 0, 0, 4, 0}, // QR_MODE_8: M1=0, M2=0, M3=4, M4=5
+ { 0, 0, 3, 0}, // QR_MODE_KANJI: M1=0, M2=0, M3=3, M4=4
+};
+```
+
+A value of 0 means the mode is unsupported for that version.
+
+### Data Length in Bits
+
+A key difference: `MQRspec_getDataLengthBit()` returns data length in **bits**, not bytes:
+
+```c
+int MQRspec_getDataLengthBit(int version, QRecLevel level)
+{
+ int w = mqrspecCapacity[version].width - 1;
+ return w * w - 64 - MQRspec_getECCLength(version, level) * 8;
+}
+```
+
+This matters because M1 and some M2 configurations have data lengths that are not byte-aligned.
+
+---
+
+## MQRRawCode — Micro QR Block Structure
+
+From `qrencode.c`:
+
+```c
+typedef struct {
+ int version;
+ int dataLength;
+ int eccLength;
+ unsigned char *datacode;
+ unsigned char *ecccode;
+ int b1;
+ int rsblock_num;
+ RSblock *rsblock;
+ int count;
+ int oddbits; // Number of "odd" bits in last data byte
+} MQRRawCode;
+```
+
+Micro QR always has exactly **one RS block** (no block interleaving):
+
+```c
+static MQRRawCode *MQRraw_new(QRinput *input)
+{
+ MQRRawCode *raw;
+ raw->version = input->version;
+ raw->dataLength = MQRspec_getDataLength(input->version, input->level);
+ raw->eccLength = MQRspec_getECCLength(input->version, input->level);
+ raw->oddbits = raw->dataLength * 8 - MQRspec_getDataLengthBit(input->version, input->level);
+
+ raw->datacode = QRinput_getByteStream(input);
+ raw->ecccode = (unsigned char *)malloc(raw->eccLength);
+ raw->rsblock_num = 1;
+ raw->rsblock = calloc(1, sizeof(RSblock));
+
+ RSblock_initBlock(raw->rsblock, raw->dataLength, raw->datacode,
+ raw->eccLength, raw->ecccode, RSECC_encode);
+ raw->count = 0;
+ return raw;
+}
+```
+
+### Odd Bits Handling
+
+The `oddbits` field handles versions where data capacity is not byte-aligned:
+
+```c
+unsigned char MQRraw_getCode(MQRRawCode *raw)
+{
+ if(raw->count < raw->dataLength) {
+ return raw->datacode[raw->count++];
+ } else {
+ return raw->ecccode[raw->count++ - raw->dataLength];
+ }
+}
+```
+
+In `QRcode_encodeMaskMQR()`, the odd bits are handled after placing full codewords:
+
+```c
+j = MQRspec_getDataLengthBit(input->version, input->level)
+ - MQRraw_getDataLength(raw) * 8;
+if(j > 0) {
+ // Place remaining odd bits from last data byte
+ code = MQRraw_getCode(raw);
+ bit = 0x80;
+ for(i = 0; i < j; i++) {
+ p = FrameFiller_next(filler);
+ *p = 0x02 | ((code & bit) != 0);
+ bit >>= 1;
+ }
+}
+```
+
+---
+
+## Frame Creation
+
+`MQRspec_createFrame()` builds the base frame with function patterns:
+
+```c
+unsigned char *MQRspec_createFrame(int version)
+{
+ unsigned char *frame;
+ int width = mqrspecCapacity[version].width;
+
+ frame = (unsigned char *)calloc(width * width, 1);
+
+ // 1. Finder pattern (only ONE, top-left)
+ putFinderPattern(frame, width, 0, 0);
+
+ // 2. Separator (no full separator ring — only right and bottom)
+
+ // 3. Timing pattern (horizontal and vertical)
+ for(int i = 0; i < width - 8; i++) {
+ // horizontal timing along row 0, starting at column 8
+ frame[8 + i] = 0x90 | (i & 1);
+ // vertical timing along column 0, starting at row 8
+ frame[(8 + i) * width] = 0x90 | (i & 1);
+ }
+
+ // 4. Format information area (reserved)
+ // No version information (unlike QR versions 7+)
+ // No alignment patterns (unlike QR versions 2+)
+
+ return frame;
+}
+```
+
+Key differences from `QRspec_createFrame()`:
+- Single finder pattern instead of three
+- No alignment patterns
+- No version information area
+- Simpler separator structure
+
+---
+
+## Micro QR Masking
+
+See [masking-algorithms.md](masking-algorithms.md) for the full details. Summary:
+
+### 4 Patterns
+
+```c
+MMask_mask0: y % 2 == 0
+MMask_mask1: (y/2 + x/3) % 2 == 0
+MMask_mask2: ((y*x)%2 + (y*x)%3) % 2 == 0
+MMask_mask3: ((y+x)%2 + (y*x)%3) % 2 == 0
+```
+
+### Selection Criterion
+
+Micro QR picks the mask with the **highest** score (opposite of full QR):
+
+```c
+// Sum of dark modules in bottom row × 16 + sum of dark modules in right column
+score = sum1 * 16 + sum2;
+```
+
+The right column gets lower weight (×1) than the bottom row (×16).
+
+---
+
+## Format Information
+
+Micro QR encodes version, EC level, and mask pattern in a single 15-bit format info:
+
+```c
+static const unsigned int typeTable[MQRSPEC_VERSION_MAX + 1][3] = {
+ {0x00000, 0x00000, 0x00000}, // unused
+ {0x04445, 0x04172, 0x04e2b}, // M1
+ {0x02f7f, 0x02a48, 0x02511}, // M2
+ {0x07f46, 0x07a71, 0x07528}, // M3
+ {0x00dc5, 0x008f2, 0x007ab} // M4
+};
+```
+
+Indexed as `typeTable[version][typeNumber]` where `typeNumber` depends on the EC level:
+
+```c
+unsigned int MQRspec_getFormatInfo(int mask, int version, QRecLevel level)
+{
+ // ... compute typeNumber from version and level ...
+ // ... XOR with mask-dependent pattern ...
+}
+```
+
+Written into the symbol by `MMask_writeFormatInformation()` in a single strip around the finder pattern (8 bits on the left side, 7 bits on the top).
+
+---
+
+## API for Micro QR
+
+### Input Creation
+
+```c
+QRinput *input = QRinput_newMQR(int version, QRecLevel level);
+```
+
+- `version` must be 1–4 (no auto-detection for manual input)
+- Invalid version/level combinations are rejected
+
+### High-Level Encoding
+
+```c
+QRcode *QRcode_encodeStringMQR(const char *string, int version,
+ QRecLevel level, QRencodeMode hint,
+ int casesensitive);
+QRcode *QRcode_encodeString8bitMQR(const char *string, int version,
+ QRecLevel level);
+QRcode *QRcode_encodeDataMQR(int size, const unsigned char *data,
+ int version, QRecLevel level);
+```
+
+When `version` is 0, these functions try versions M1 through M4 incrementally:
+
+```c
+if(version == 0) {
+ for(i = 1; i <= MQRSPEC_VERSION_MAX; i++) {
+ QRcode *code = QRcode_encodeDataReal(data, size, i, level, 1);
+ if(code != NULL) return code;
+ }
+}
+```
+
+### Version/Level Validation
+
+Use `QRinput_setVersionAndErrorCorrectionLevel()` for Micro QR — it validates the combination. Using `QRinput_setVersion()` or `QRinput_setErrorCorrectionLevel()` individually on MQR inputs returns `EINVAL`.
+
+---
+
+## Structured Append — Not Supported
+
+Micro QR does not support structured append mode. Attempting to encode structured append with MQR results in an error:
+
+```c
+static int QRinput_encodeModeStructure(QRinput_List *entry, int mqr)
+{
+ if(mqr) {
+ errno = EINVAL;
+ return -1;
+ }
+ // ...
+}
+```
+
+`QRinput_Struct_appendInput()` also rejects MQR inputs:
+
+```c
+int QRinput_Struct_appendInput(QRinput_Struct *s, QRinput *input)
+{
+ if(input == NULL || input->mqr) {
+ errno = EINVAL;
+ return -1;
+ }
+ // ...
+}
+```
+
+---
+
+## Encoding Pipeline Differences
+
+`QRcode_encodeMaskMQR()` in `qrencode.c` follows a modified pipeline:
+
+1. **Create frame**: `MQRspec_createFrame(version)` — single finder, no alignment
+2. **Initialize RS**: `MQRraw_new(input)` — single block, with odd bits tracking
+3. **Place data**: Via `FrameFiller_next()`, but handles odd bits at boundary:
+ ```c
+ // Place full data bytes
+ for(i = 0; i < MQRraw_getDataLength(raw) - 1; i++) {
+ code = MQRraw_getCode(raw);
+ bit = 0x80;
+ for(j = 0; j < 8; j++) {
+ p = FrameFiller_next(filler);
+ *p = 0x02 | ((bit & code) != 0);
+ bit >>= 1;
+ }
+ }
+ // Handle odd bits from last data byte
+ ```
+4. **Place ECC**: Full ECC bytes, then remainder bits (if any)
+5. **Apply mask**: `MMask_mask(version, frame, level)` — 4 patterns, maximize score
+6. **Package**: `QRcode_new(version, width, masked)`
+
+---
+
+## CLI Support
+
+The `qrencode` CLI tool supports Micro QR via the `-M` / `--micro` flag:
+
+```bash
+qrencode -M -v 3 -l M -o output.png "Hello"
+```
+
+In `qrenc.c`:
+
+```c
+case 'M':
+ micro = 1;
+ break;
+```
+
+When `micro` is set, `encode()` calls `QRcode_encodeStringMQR()` or `QRcode_encodeDataMQR()` instead of the standard variants.
+
+---
+
+## Limitations Summary
+
+1. **No H level**: Maximum EC is Q (M4 only)
+2. **No structured append**: Cannot split data across multiple symbols
+3. **No ECI**: Cannot specify character encodings
+4. **No FNC1**: Cannot create GS1-compatible codes
+5. **Small capacity**: Maximum 35 numeric or 15 byte characters (M4-L)
+6. **Single finder**: Only top-left finder pattern — orientation from timing patterns
+7. **Version must be specified**: For `QRinput_newMQR()`, version 0 is not auto-detect (though high-level `QRcode_encodeStringMQR()` with version 0 does try all versions)