@oim-media/spec 1.27.8 → 1.27.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/BINARY_SPEC.md +190 -186
  2. package/CANONICAL_SPEC.md +409 -409
  3. package/LICENSE +55 -55
  4. package/NOTICE +6 -6
  5. package/README.md +446 -444
  6. package/action-cases.json +13 -13
  7. package/artistic-text-cases.json +690 -690
  8. package/canonical-cases.json +6 -0
  9. package/keyframe-cases.json +32 -32
  10. package/keys.json +22 -22
  11. package/marks-cases.json +34 -34
  12. package/package.json +2 -1
  13. package/pattern-cases.json +552 -552
  14. package/schema/oim.schema.json +2920 -2920
  15. package/schema/ovm.schema.json +2701 -2701
  16. package/text-effect-cases.json +835 -835
  17. package/tools/parity-core.mjs +122 -122
  18. package/tools/parity.mjs +86 -86
  19. package/vectors/README.md +103 -103
  20. package/vectors/accessibility.expected.json +67 -67
  21. package/vectors/artistic-text.expected.json +84 -84
  22. package/vectors/asymmetric-borders.expected.json +29 -29
  23. package/vectors/background-pattern-order.expected.json +95 -95
  24. package/vectors/basic-text.expected.json +29 -29
  25. package/vectors/color-blending-opacity.expected.json +47 -47
  26. package/vectors/crop.expected.json +32 -32
  27. package/vectors/embedded-font-woff.expected.json +57 -57
  28. package/vectors/extreme-aspect-ratios.expected.json +29 -29
  29. package/vectors/filter-effects.expected.json +33 -33
  30. package/vectors/flip.expected.json +47 -47
  31. package/vectors/frame-shapes.expected.json +73 -73
  32. package/vectors/full-monty-composite.expected.json +96 -96
  33. package/vectors/golden/exceptions.json +3 -3
  34. package/vectors/groups-and-parallax.expected.json +90 -90
  35. package/vectors/image-fill.expected.json +54 -54
  36. package/vectors/interactive-chained-actions.expected.json +77 -77
  37. package/vectors/interactive-hover.expected.json +36 -36
  38. package/vectors/interactive.expected.json +121 -121
  39. package/vectors/layer-blur.expected.json +48 -48
  40. package/vectors/layer-grain.expected.json +52 -52
  41. package/vectors/layer-ordering-zindex.expected.json +62 -62
  42. package/vectors/legacy-text-chunk.expected.json +29 -29
  43. package/vectors/media-filters.expected.json +50 -50
  44. package/vectors/multi-container-layout.expected.json +45 -45
  45. package/vectors/multilang-cjk-rtl.expected.json +30 -30
  46. package/vectors/nested-groups.expected.json +68 -68
  47. package/vectors/palettes.expected.json +57 -57
  48. package/vectors/parallax-infinite.expected.json +39 -39
  49. package/vectors/pattern-color-roles.expected.json +68 -68
  50. package/vectors/reedit-english.expected.json +45 -45
  51. package/vectors/reveal-and-play.expected.json +107 -107
  52. package/vectors/shadow-effects.expected.json +34 -34
  53. package/vectors/shape-shadows.expected.json +62 -62
  54. package/vectors/strokes-and-borders.expected.json +31 -31
  55. package/vectors/text-runs-precalculated.expected.json +46 -46
  56. package/vectors/transparent-background.expected.json +30 -30
  57. package/vectors/typography-styling.expected.json +34 -34
package/BINARY_SPEC.md CHANGED
@@ -1,186 +1,190 @@
1
- # OIM (Open Interactive Media) Container Binary Specification
2
-
3
- **Version:** 1.0
4
- **Date:** 2026-09-22
5
- **Status:** Normative / In force
6
- **Authors:** OIM Architecture Team
7
-
8
- ---
9
-
10
- ## 1. Summary and Objectives
11
-
12
- A **`.oim`** (or **`.oim.png`**) file is an enriched, interactive and verifiable multimedia document carried inside an image file that conforms to the standard **ISO/IEC 15948:2004 (PNG)** specification.
13
-
14
- The objectives of this binary design are:
15
- 1. **Universal compatibility (visual fallback):** Any image viewer, browser or client without OIM support can decode and render the file as a standard, high-fidelity static PNG image.
16
- 2. **Zero network dependency (self-contained):** The document encapsulates all of its vector geometry, multilingual texts, animations, typefaces and interactive metadata within the image itself.
17
- 3. **Cryptographic integrity and ECDSA signature (zero-trust):** The file embeds an asymmetric digital signature that indelibly binds the interactive metadata to the rendered pixels (`IDAT` chunks), preventing substitution or manipulation of the canvas or its layers.
18
-
19
- ---
20
-
21
- ## 2. Anatomy of the PNG Binary Container
22
-
23
- Every OIM file begins strictly with the canonical 8-byte PNG signature:
24
-
25
- ```binary
26
- 89 50 4E 47 0D 0A 1A 0A (\x89PNG\r\n\x1a\n)
27
- ```
28
-
29
- ### 2.1. PNG Chunk Structure
30
-
31
- Each chunk in the PNG file follows the standard 4-field layout:
32
-
33
- | Field | Length | Description |
34
- |---|---|---|
35
- | **Length** | 4 bytes (Big-Endian) | Length of the `Data` field (excludes `Type` and `CRC`). |
36
- | **Type** | 4 bytes (ASCII) | Four-character identifier (e.g. `IHDR`, `tEXt`, `IDAT`, `IEND`). |
37
- | **Data** | *Length* bytes | Chunk payload. |
38
- | **CRC-32** | 4 bytes (Big-Endian) | CRC-32 checksum computed over `Type` + `Data`. |
39
-
40
- ### 2.2. Placement and Injection of OIM Metadata
41
-
42
- OIM metadata is preferably stored in an ancillary text chunk:
43
-
44
- - **Chunk type:** `zTXt` (written since 29 Oct 2026). Readers also accept `tEXt` (earlier files) and `iTXt`.
45
- - **Chunk keyword:** `OIM_Data` (historically `OIM_JSON_DATA` was also recognised).
46
- - **Location in the file:**
47
- - **AFTER** `IHDR` (and any palette/colour header chunks, if present).
48
- - **BEFORE** or **AFTER** the image `IDAT` chunks.
49
- - **ALWAYS BEFORE** the closing `IEND` chunk.
50
-
51
- > **Critical note on injection:** The metadata chunk must be inserted **surgically at byte level** into the PNG binary stream, without re-rendering or re-encoding the `IDAT` blocks, preserving the exact cryptographic hash of the canvas.
52
-
53
- ---
54
-
55
- ## 3. Encoding and Compression of the Payload
56
-
57
- The payload of the `zTXt` chunk has the following structure:
58
-
59
- ```
60
- [ Keyword: "OIM_Data" ] + [ Null Separator: 0x00 ] + [ Method: 0x00 ] + [ zlib(JSON), binary ]
61
- ```
62
-
63
- No Base64: measured on 29 Oct 2026, the file is 12–23 % smaller than with the
64
- earlier `tEXt` form (`"OIM_Data\0"` + Base64 of raw Deflate), which readers
65
- still accept.
66
-
67
- ### 3.1. Packaging Flow (Serializer)
68
-
69
- 1. The OIM metadata object (conforming to `OimMetadataSchema`) is serialised to a JSON string encoded in **UTF-8**.
70
- 2. The UTF-8 string is compressed with **zlib** (RFC 1950: 2-byte header, DEFLATE, Adler-32), level 9.
71
- 3. The `zTXt` chunk is assembled:
72
- - `Length`: `8` (keyword) + `1` (null byte) + `1` (method) + `len(zlib)`
73
- - `Type`: `7A 54 58 74` (`zTXt`)
74
- - `Data`: `"OIM_Data\0\0"` + `<zlib_bytes>`
75
- - `CRC-32`: computed over `Type` and `Data`.
76
-
77
- ### 3.2. Extraction and Decompression Flow (Reader)
78
-
79
- A `zTXt` is read by skipping the NUL and the method byte and decompressing with
80
- zlib. For the earlier `tEXt` form, a conforming decoder must process the payload
81
- with the following fault tolerance:
82
- 1. Extract the value of the chunk after the `0x00` delimiter.
83
- 2. Decode the Base64 string into binary bytes.
84
- 3. If the decoded plain text already starts with `{` (uncompressed JSON from debugging/draft), process it directly with `JSON.parse`.
85
- 4. If it contains compressed binary data:
86
- - Try to decompress with **Zlib Deflate** (`new DecompressionStream('deflate')` or `zlib.inflateSync`).
87
- - If that fails, try to decompress with **Raw Deflate** (`new DecompressionStream('deflate-raw')` or `zlib.inflateRawSync`).
88
- 5. Decode the resulting bytes with `TextDecoder("utf-8")` and parse the JSON.
89
-
90
- ---
91
-
92
- ## 4. The Image Integrity Seal (`image_hash`)
93
-
94
- To guarantee that a set of valid, signed interactive metadata cannot be cut out and pasted onto an arbitrary image (canvas-transplant attack), the format implements an image seal over the IDAT stream:
95
-
96
- ### 4.1. Calculation Algorithm
97
-
98
- ```
99
- image_hash = SHA256( IDAT_payload_1 || IDAT_payload_2 || ... || IDAT_payload_n )
100
- ```
101
-
102
- - All chunks with the exact type `IDAT` are traversed, in the strict order in which they appear in the PNG container.
103
- - Only the payload of each chunk is extracted (`Data`, discarding the 4 length bytes, the 4 `IDAT` type bytes and the 4 CRC bytes).
104
- - All the bytes of the compressed raw image data are concatenated.
105
- - The **SHA-256** digest is computed and expressed in **lowercase hexadecimal** (64 characters).
106
-
107
- ### 4.2. Metadata Fields
108
- - `image_hash`: Resulting SHA-256 hexadecimal string.
109
- - `image_hash_alg`: Literal `"sha256-idat"`.
110
-
111
- ---
112
-
113
- ## 5. ECDSA P-256 Cryptographic Signature Specification
114
-
115
- A signed `.oim` file guarantees the authorship, integrity and non-repudiation of the design.
116
-
117
- - **Cryptosystem:** ECDSA over the **NIST P-256** elliptic curve (secp256r1 / prime256v1).
118
- - **Hash function:** SHA-256.
119
- - **Signature format:** raw IEEE P1363 (`r || s`, 32 bytes for $r$ + 32 bytes for $s$ = exactly **64 binary bytes**).
120
- - **JSON serialisation:** Base64 of the 64 binary bytes in the `signature` property. *(No ASN.1/DER encoding is used.)*
121
-
122
- ### 5.1. Canonical Serialisation Rules (JCS - JSON Canonicalization)
123
-
124
- So that any implementation in any language (TypeScript, Python, Rust, Go, Swift) verifies the signature identically, without a single byte of discrepancy, the payload to be signed or verified is built by strictly following these **7 rules**:
125
-
126
- 1. **Base object:** Take the decoded OIM metadata dictionary.
127
- 2. **Root exclusions:** Exclude from the root only the fields:
128
- - `signature`
129
- - `key_version`
130
- - `timestamp`
131
- - `creator_user_id` *(excluded for privacy, GDPR / zero-knowledge)*.
132
- *(Note: if any of these keys exist in nested objects or styles, they are not removed.)*
133
- 3. **Numeric normalisation:**
134
- - If a decimal number has an exact integer value (e.g. `2.0` or `-0.0`), it is formatted as an integer (`2`, `0`).
135
- - Floating-point numbers are formatted to a maximum of **6 decimal places** with standard symmetric rounding.
136
- 4. **Lexical key ordering:** All object keys (at the root and recursively at every level and inside arrays) are sorted by ascending Unicode code point.
137
- 5. **Compact serialisation:** No whitespace between keys, colons or commas: `{"a":1,"b":"dos"}`.
138
- 6. **UTF-8 preservation:** Do not escape non-ASCII Unicode characters with `\uXXXX` sequences. They are emitted directly as native UTF-8 (e.g. `"Zürich"` instead of `"Z\u00fcrich"`).
139
- 7. **Digest:** The resulting bytes encoded in UTF-8 are passed directly to the SHA-256 + ECDSA P-256 verification/signing function.
140
-
141
- ---
142
-
143
- ## 6. Embedded Font Handling (`fonts`)
144
-
145
- Since version 1.3.0, the `.oim` format is self-contained and makes no calls to third-party CDNs to render typefaces:
146
-
147
- - **Structure of the `fonts` array at the root:**
148
- ```json
149
- [
150
- {
151
- "family": "Bricolage Grotesque",
152
- "name": "oim-a9f81-BricolageGrotesque",
153
- "weight": 700,
154
- "style": "normal",
155
- "format": "woff2",
156
- "data": "<base64_string_of_the_subsetted_woff2_binary>"
157
- }
158
- ]
159
- ```
160
- - **Strict subsetting:** The embedded WOFF2 binaries must be subsetted only to the glyphs present in the project's texts, to minimise the total weight of the PNG (typically 4 KB to 18 KB per variant).
161
- - **Registration in the reader:**
162
- 1. Decode `data` from Base64.
163
- 2. Register the font via `FontFace` (in browsers) or register it in the native system's font engine under the unique identifier `name`.
164
- 3. Bind the slots that refer to `family` to the resolved variant.
165
-
166
- ---
167
-
168
- ## 7. Summary Table of Fields and Compatibility
169
-
170
- | Feature | Chunks / Key | Format | Mandatory |
171
- |---|---|---|---|
172
- | **Base image** | `IDAT` | Standard compressed PNG | Yes |
173
- | **OIM metadata** | `zTXt` (`OIM_Data`) | zlib(JSON), binary | Yes |
174
- | **IDAT seal** | `image_hash` | SHA-256 hex | Yes (since v1.2) |
175
- | **Digital signature** | `signature` | Base64(ECDSA P1363 64B) | Optional in drafts, required in published files |
176
- | **Key version** | `key_version` | String (e.g. `"v1"`) | Required if signed |
177
- | **Signing date** | `signed_at` | ISO-8601 UTC | Required if signed |
178
- | **Typefaces** | `fonts` | Array with WOFF2 Base64 | Recommended |
179
- | **Named palettes** (1.20.0) | `palettes`, `defaultPalette` | Object name → (role → CSS colour); string | Optional |
180
-
181
- ### Named palettes (1.20.0)
182
-
183
- `palettes` and `defaultPalette` are optional at the root of the document. The
184
- rules for names, roles, the limit of six palettes and the resolution order are
185
- in `README.md` (section "Named palettes"). A reader that does not know them
186
- ignores both keys and paints each layer's fixed colours.
1
+ # OIM (Object Interactive Media) Container Binary Specification
2
+
3
+ **Version:** 1.0
4
+ **Date:** 2026-09-22
5
+ **Status:** Normative / In force
6
+ **Authors:** OIM Architecture Team
7
+
8
+ ---
9
+
10
+ ## 1. Summary and Objectives
11
+
12
+ A **`.oim`** (or **`.oim.png`**) file is an enriched, interactive and verifiable multimedia document carried inside an image file that conforms to the standard **ISO/IEC 15948:2004 (PNG)** specification.
13
+
14
+ The objectives of this binary design are:
15
+ 1. **Universal compatibility (visual fallback):** Any image viewer, browser or client without OIM support can decode and render the file as a standard, high-fidelity static PNG image.
16
+ 2. **Zero network dependency (self-contained):** The document encapsulates all of its vector geometry, multilingual texts, animations, typefaces and interactive metadata within the image itself.
17
+ 3. **Cryptographic integrity and ECDSA signature (zero-trust):** The file embeds an asymmetric digital signature that indelibly binds the interactive metadata to the rendered pixels (`IDAT` chunks), preventing substitution or manipulation of the canvas or its layers.
18
+
19
+ ---
20
+
21
+ ## 2. Anatomy of the PNG Binary Container
22
+
23
+ Every OIM file begins strictly with the canonical 8-byte PNG signature:
24
+
25
+ ```binary
26
+ 89 50 4E 47 0D 0A 1A 0A (\x89PNG\r\n\x1a\n)
27
+ ```
28
+
29
+ ### 2.1. PNG Chunk Structure
30
+
31
+ Each chunk in the PNG file follows the standard 4-field layout:
32
+
33
+ | Field | Length | Description |
34
+ |---|---|---|
35
+ | **Length** | 4 bytes (Big-Endian) | Length of the `Data` field (excludes `Type` and `CRC`). |
36
+ | **Type** | 4 bytes (ASCII) | Four-character identifier (e.g. `IHDR`, `tEXt`, `IDAT`, `IEND`). |
37
+ | **Data** | *Length* bytes | Chunk payload. |
38
+ | **CRC-32** | 4 bytes (Big-Endian) | CRC-32 checksum computed over `Type` + `Data`. |
39
+
40
+ ### 2.2. Placement and Injection of OIM Metadata
41
+
42
+ OIM metadata is preferably stored in an ancillary text chunk:
43
+
44
+ - **Chunk type:** `zTXt` (written since 29 Oct 2026). Readers also accept `tEXt` (earlier files) and `iTXt`.
45
+ - **Chunk keyword:** `OIM_Data` (historically `OIM_JSON_DATA` was also recognised).
46
+ - **Location in the file:**
47
+ - **AFTER** `IHDR` (and any palette/colour header chunks, if present).
48
+ - **BEFORE** or **AFTER** the image `IDAT` chunks.
49
+ - **ALWAYS BEFORE** the closing `IEND` chunk.
50
+
51
+ > **Critical note on injection:** The metadata chunk must be inserted **surgically at byte level** into the PNG binary stream, without re-rendering or re-encoding the `IDAT` blocks, preserving the exact cryptographic hash of the canvas.
52
+
53
+ ---
54
+
55
+ ## 3. Encoding and Compression of the Payload
56
+
57
+ The payload of the `zTXt` chunk has the following structure:
58
+
59
+ ```
60
+ [ Keyword: "OIM_Data" ] + [ Null Separator: 0x00 ] + [ Method: 0x00 ] + [ zlib(JSON), binary ]
61
+ ```
62
+
63
+ No Base64: measured on 29 Oct 2026, the file is 12–23 % smaller than with the
64
+ earlier `tEXt` form (`"OIM_Data\0"` + Base64 of raw Deflate), which readers
65
+ still accept.
66
+
67
+ ### 3.1. Packaging Flow (Serializer)
68
+
69
+ 1. The OIM metadata object (conforming to `OimMetadataSchema`) is serialised to a JSON string encoded in **UTF-8**.
70
+ 2. The UTF-8 string is compressed with **zlib** (RFC 1950: 2-byte header, DEFLATE, Adler-32), level 9.
71
+ 3. The `zTXt` chunk is assembled:
72
+ - `Length`: `8` (keyword) + `1` (null byte) + `1` (method) + `len(zlib)`
73
+ - `Type`: `7A 54 58 74` (`zTXt`)
74
+ - `Data`: `"OIM_Data\0\0"` + `<zlib_bytes>`
75
+ - `CRC-32`: computed over `Type` and `Data`.
76
+
77
+ ### 3.2. Extraction and Decompression Flow (Reader)
78
+
79
+ A `zTXt` is read by skipping the NUL and the method byte and decompressing with
80
+ zlib. For the earlier `tEXt` form, a conforming decoder must process the payload
81
+ with the following fault tolerance:
82
+ 1. Extract the value of the chunk after the `0x00` delimiter.
83
+ 2. Decode the Base64 string into binary bytes.
84
+ 3. If the decoded plain text already starts with `{` (uncompressed JSON from debugging/draft), process it directly with `JSON.parse`.
85
+ 4. If it contains compressed binary data:
86
+ - Try to decompress with **Zlib Deflate** (`new DecompressionStream('deflate')` or `zlib.inflateSync`).
87
+ - If that fails, try to decompress with **Raw Deflate** (`new DecompressionStream('deflate-raw')` or `zlib.inflateRawSync`).
88
+ 5. Decode the resulting bytes with `TextDecoder("utf-8")` and parse the JSON.
89
+
90
+ ---
91
+
92
+ ## 4. The Image Integrity Seal (`image_hash`)
93
+
94
+ To guarantee that a set of valid, signed interactive metadata cannot be cut out and pasted onto an arbitrary image (canvas-transplant attack), the format implements an image seal over the IDAT stream:
95
+
96
+ ### 4.1. Calculation Algorithm
97
+
98
+ ```
99
+ image_hash = SHA256( IDAT_payload_1 || IDAT_payload_2 || ... || IDAT_payload_n )
100
+ ```
101
+
102
+ - All chunks with the exact type `IDAT` are traversed, in the strict order in which they appear in the PNG container.
103
+ - Only the payload of each chunk is extracted (`Data`, discarding the 4 length bytes, the 4 `IDAT` type bytes and the 4 CRC bytes).
104
+ - All the bytes of the compressed raw image data are concatenated.
105
+ - The **SHA-256** digest is computed and expressed in **lowercase hexadecimal** (64 characters).
106
+
107
+ ### 4.2. Metadata Fields
108
+ - `image_hash`: Resulting SHA-256 hexadecimal string.
109
+ - `image_hash_alg`: Literal `"sha256-idat"`.
110
+
111
+ ---
112
+
113
+ ## 5. ECDSA P-256 Cryptographic Signature Specification
114
+
115
+ A signed `.oim` file guarantees the authorship, integrity and non-repudiation of the design.
116
+
117
+ - **Cryptosystem:** ECDSA over the **NIST P-256** elliptic curve (secp256r1 / prime256v1).
118
+ - **Hash function:** SHA-256.
119
+ - **Signature format:** raw IEEE P1363 (`r || s`, 32 bytes for $r$ + 32 bytes for $s$ = exactly **64 binary bytes**).
120
+ - **JSON serialisation:** Base64 of the 64 binary bytes in the `signature` property. *(No ASN.1/DER encoding is used.)*
121
+
122
+ ### 5.1. Canonical Serialisation Rules (OIM profile)
123
+
124
+ So that any implementation in any language (TypeScript, Python, Rust, Go, Swift) verifies the signature identically, without a single byte of discrepancy, the payload to be signed or verified is built by strictly following these **7 rules**.
125
+
126
+ This is OIM's own profile, **not RFC 8785 (JCS)**: rule 3 rounds numbers and rule 4 orders keys by code point, where JCS keeps the number and orders by UTF-16 code unit. A stock JCS library does not produce these bytes. [`canonical-cases.json`](canonical-cases.json) is a known-answer case every implementation in this repository is tested against.
127
+
128
+
129
+ 1. **Base object:** Take the decoded OIM metadata dictionary.
130
+ 2. **Root exclusions:** Exclude from the root only the fields:
131
+ - `signature`
132
+ - `key_version`
133
+ - `timestamp`
134
+ - `creator_user_id` *(excluded for privacy, GDPR / zero-knowledge)*.
135
+ *(Note: if any of these keys exist in nested objects or styles, they are not removed.)*
136
+ 3. **Numeric normalisation:**
137
+ - If a decimal number has an exact integer value (e.g. `2.0` or `-0.0`), it is formatted as an integer (`2`, `0`).
138
+ - Floating-point numbers are formatted to a maximum of **6 decimal places** with standard symmetric rounding.
139
+ - **Range:** every number in the metadata, at any depth, must be finite and no larger in magnitude than **2^53 − 1** (`9007199254740991`). Beyond that, JSON parsers disagree on the value they read, so the number has no canonical form: a signer must refuse the metadata and a verifier must report the signature as invalid.
140
+ 4. **Lexical key ordering:** All object keys (at the root and recursively at every level and inside arrays) are sorted by ascending Unicode code point.
141
+ 5. **Compact serialisation:** No whitespace between keys, colons or commas: `{"a":1,"b":"dos"}`.
142
+ 6. **UTF-8 preservation:** Do not escape non-ASCII Unicode characters with `\uXXXX` sequences. They are emitted directly as native UTF-8 (e.g. `"Zürich"` instead of `"Z\u00fcrich"`).
143
+ 7. **Digest:** The resulting bytes encoded in UTF-8 are passed directly to the SHA-256 + ECDSA P-256 verification/signing function.
144
+
145
+ ---
146
+
147
+ ## 6. Embedded Font Handling (`fonts`)
148
+
149
+ Since version 1.3.0, the `.oim` format is self-contained and makes no calls to third-party CDNs to render typefaces:
150
+
151
+ - **Structure of the `fonts` array at the root:**
152
+ ```json
153
+ [
154
+ {
155
+ "family": "Bricolage Grotesque",
156
+ "name": "oim-a9f81-BricolageGrotesque",
157
+ "weight": 700,
158
+ "style": "normal",
159
+ "format": "woff2",
160
+ "data": "<base64_string_of_the_subsetted_woff2_binary>"
161
+ }
162
+ ]
163
+ ```
164
+ - **Strict subsetting:** The embedded WOFF2 binaries must be subsetted only to the glyphs present in the project's texts, to minimise the total weight of the PNG (typically 4 KB to 18 KB per variant).
165
+ - **Registration in the reader:**
166
+ 1. Decode `data` from Base64.
167
+ 2. Register the font via `FontFace` (in browsers) or register it in the native system's font engine under the unique identifier `name`.
168
+ 3. Bind the slots that refer to `family` to the resolved variant.
169
+
170
+ ---
171
+
172
+ ## 7. Summary Table of Fields and Compatibility
173
+
174
+ | Feature | Chunks / Key | Format | Mandatory |
175
+ |---|---|---|---|
176
+ | **Base image** | `IDAT` | Standard compressed PNG | Yes |
177
+ | **OIM metadata** | `zTXt` (`OIM_Data`) | zlib(JSON), binary | Yes |
178
+ | **IDAT seal** | `image_hash` | SHA-256 hex | Yes (since v1.2) |
179
+ | **Digital signature** | `signature` | Base64(ECDSA P1363 64B) | Optional in drafts, required in published files |
180
+ | **Key version** | `key_version` | String (e.g. `"v1"`) | Required if signed |
181
+ | **Signing date** | `signed_at` | ISO-8601 UTC | Required if signed |
182
+ | **Typefaces** | `fonts` | Array with WOFF2 Base64 | Recommended |
183
+ | **Named palettes** (1.20.0) | `palettes`, `defaultPalette` | Object name → (role → CSS colour); string | Optional |
184
+
185
+ ### Named palettes (1.20.0)
186
+
187
+ `palettes` and `defaultPalette` are optional at the root of the document. The
188
+ rules for names, roles, the limit of six palettes and the resolution order are
189
+ in `README.md` (section "Named palettes"). A reader that does not know them
190
+ ignores both keys and paints each layer's fixed colours.