├── ConditionTree.md ├── README.md ├── VARC.md ├── avar2-in-designspace.md ├── avar2.md ├── beyond-64k.md ├── glyf1-cubicOutlines.md ├── glyf1-varComposites.md ├── glyf1.md └── iso_docs ├── 2024-06-28-behdad-review.odt ├── 2024-06-28-behdad-review.pdf ├── README.md ├── WG03-beyond-64k-glyphs-2023-11-20.md ├── WG03-beyond-64k-glyphs-2023-11-20.odt ├── WG03-beyond-64k-glyphs-2023-11-20.pdf ├── WG03-beyond-64k-glyphs-2024-01.md ├── WG03-beyond-64k-glyphs-2024-01.odt ├── WG03-beyond-64k-glyphs-2024-01.pdf ├── WG03-beyond-64k-glyphs-2024-01b.md ├── WG03-beyond-64k-glyphs-2024-01b.odt ├── WG03-beyond-64k-glyphs-2024-01b.pdf ├── WG03-beyond-64k-glyphs-2024-01d.docx ├── WG03-beyond-64k-glyphs-2024-01d.md ├── WG03-beyond-64k-glyphs-2024-01d.odt ├── WG03-beyond-64k-glyphs-2024-01d.pdf ├── WG03-beyond-64k-glyphs-2024-01e.docx ├── WG03-beyond-64k-glyphs-2024-01e.md ├── WG03-beyond-64k-glyphs-2024-01e.odt ├── WG03-beyond-64k-glyphs-2024-01e.pdf ├── WG03-beyond-64k-glyphs-proposal.docx ├── WG03-beyond-64k-glyphs-proposal.md ├── WG03-beyond-64k-glyphs-proposal.odt ├── WG03-beyond-64k-glyphs-proposal.pdf ├── WG03-dmap-2024-01.md ├── WG03-dmap-2024-01.odt ├── WG03-dmap-2024-01.pdf ├── WG03-dmap-2024-03.md ├── WG03-dmap-2024-03.odt ├── WG03-dmap-2024-03.pdf ├── WG03-dmap-2024-2024-04-15.docx ├── WG03-dmap-2024-2024-04-15.md ├── WG03-dmap-2024-2024-04-15.odt ├── WG03-dmap-2024-2024-04-15.pdf ├── WG03-fvar-2024-03.md ├── WG03-fvar-2024-03.odt ├── WG03-fvar-2024-03.pdf ├── WG03-fvar-2024-04.md ├── WG03-fvar-2024-04.odt ├── WG03-fvar-2024-04.pdf ├── WG03-fvar-2024-2024-04-15.docx ├── WG03-fvar-2024-2024-04-15.md ├── WG03-fvar-2024-2024-04-15.odt ├── WG03-fvar-2024-2024-04-15.pdf ├── WG03-varc-and-other-updates-03.md ├── WG03-varc-and-other-updates-03.odt ├── WG03-varc-and-other-updates-03.pdf ├── WG03-varc-and-other-updates-2024-04-15.docx ├── WG03-varc-and-other-updates-2024-04-15.md ├── WG03-varc-and-other-updates-2024-04-15.odt ├── WG03-varc-and-other-updates-2024-04-15.pdf ├── WG03-varc-and-other-updates.md ├── WG03-varc-and-other-updates.odt ├── WG03-varc-and-other-updates.pdf ├── WG03_otf-improvements.docx ├── WG03_otf-improvements.pdf ├── changes.docx ├── changes.odt ├── changes2.odt ├── comment-2024-12-10.odt ├── comment-2024-12-10.pdf ├── m64287-AVAR_OFF_updates.docx ├── m64287-AVAR_OFF_updates.md ├── m64287-AVAR_OFF_updates.odt ├── m64287-AVAR_OFF_updates.pdf ├── m64287-avar2-combined-wd7.odt ├── m64287-avar2-combined-wd7.pdf ├── m64287-combined.docx ├── m64287-combined.odt ├── m64287-combined.pdf ├── m70320-kemer.docx ├── m70320-kemer.odt └── m70320-kemer.pdf /ConditionTree.md: -------------------------------------------------------------------------------- 1 | The following new Condition types are added, which enable encoding a tree representing a boolean expression: 2 | ``` 3 | struct ConditionAnd 4 | { 5 | uint16 format; // 3 6 | uint8 conditionCount; // Number of conditions for this conjunction expression. 7 | Offset24To conditionOffsets[conditionCount]; 8 | }; 9 | 10 | struct ConditionOr 11 | { 12 | uint16 format; // 4 13 | uint8 conditionCount; // Number of conditions for this disjunction expression. 14 | Offset24To conditionOffsets[conditionCount]; 15 | }; 16 | 17 | struct ConditionNegate 18 | { 19 | uint16 format; // 5 20 | Offset24To condition; 21 | }; 22 | ``` 23 | 24 | While not directly relevant to representing condition trees, the following Condition type is also added: 25 | ``` 26 | struct ConditionValue 27 | { 28 | uint16 format; // 2 29 | int16 defaultValue; 30 | VarIdx varIdx; 31 | }; 32 | ``` 33 | Here, the condition evaluates to true if and only if `defaultValue` plus the evaluation of `varIdx` in the respective variation-store for current font coordinates is a positive number. 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # boring-expansion-spec 2 | 3 | ## What do we want to change? 4 | 5 | 1. Break the 65k glyph limit on glyphs in a single font file 6 | * [why?](#1-break-the-65k-glyph-limit) 7 | * [spec proposal](./beyond-64k.md) 8 | * [test fonts](https://drive.google.com/drive/folders/1iRBebQS5z0bjG3EBGZ53ZB6TOBg3LhUb) 9 | 1. Enable cubic curves in `glyf` outlines 10 | * [why?](#2-enable-cubic-curves-in-glyf-outlines) 11 | * [spec proposal](./glyf1-cubicOutlines.md) 12 | * [test fonts](https://drive.google.com/drive/folders/1SdoQYRz-4K9x5mB5LxGBFYmj9UpF_MOf) 13 | 1. Enable components to take advantage of variations 14 | * [why?](#3-enable-components-to-take-advantage-of-variations) 15 | * [spec proposal](./glyf1-varComposites.md) 16 | * [test fonts](https://github.com/googlefonts/smarties/tree/main/fonts) 17 | 1. Enhance core variation capability 18 | 1. Improve support for user-facing vs internal axis distinction and the mapping between them 19 | * [why?](#4i-user-facing-vs-internal-axes) 20 | * [spec proposal](./avar2.md) 21 | * [test fonts](https://drive.google.com/drive/folders/1OjZFadiSKwzPnGWWCmNwscJN8bsjfqR0) 22 | 1. Explicitly support non-linear interpolation 23 | * [why?](#4ii-explicitly-support-non-linear-interpolation) 24 | * _no spec proposal so far_ 25 | 1. Efficient storage of sparse variation data 26 | * [why?](#4iii-efficient-storage-fo-sparse-variation-data) 27 | * _no spec proposal so far_ 28 | 29 | ## Why? 30 | 31 | The proposed changes, taken as a whole, allow us to create compact pan-Unicode fonts made up of 32 | reusable parts that are built using enhanced variation capabilities. Further, the designer is 33 | empowered to separate how the parts are crafted and assembled from how they are presented to the user. 34 | 35 | ### 1. Break the 65k glyph limit 36 | 37 | See https://rsheeter.github.io/more_gids. 38 | 39 | ### 2. Enable cubic curves in `glyf` outlines 40 | 41 | The input to a font is often from a design tool that uses cubic curves. Quadratic approximation reduces quality and increases filesize. Explicit support for cubic curves in `glyf` resolves both 42 | issues. 43 | 44 | ### 3. Enable components to take advantage of variations 45 | 46 | Design tool features like [smart components](https://glyphsapp.com/learn/smart-components) allow 47 | designers to build fonts out of reusable parts. For CJK we've seen experiments where tens of 48 | thousands of glyphs are built from at least an order of magnitude fewer parts. 49 | 50 | By enabling reuse of a glyph at a different position in designspace the font binary can 51 | leverage this to significantly reduce filesize. See prior art in 52 | [Variable Components for COLRv1](https://github.com/googlefonts/colr-gradients-spec/issues/277). 53 | 54 | This can be done by leveraging `COLR` or by enhancing `glyf`. 55 | 56 | ### 4. Enhance core variation capability 57 | 58 | #### 4.i user-facing vs internal axes 59 | 60 | Reduce complexity in the design of variable fonts and produce more compact files. 61 | 62 | User-facing axes (weight, width, etc) are currently the same axes the designer uses. However, 63 | these axes are not naturally orthogonal. Designers are forced to resolve this by introducing 64 | additional masters, which costs the designer time and the end user bytes. A better system would 65 | enable user-facing axes to be assembled from other axes, which could (and should) be orthogonal. 66 | 67 | The Type Network [axis set](https://variationsguide.typenetwork.com/) is a relatively well known example. 68 | 69 | To assemble a set of non-orthogonal user facing axes from a set of orthogonal axes requires a 70 | non-linear many:many mapping. `avar` provides a piecewise linear mapping from an input position 71 | on a single axes to a different position on that axes. This is inadequate; a new and more powerful 72 | mapping mechanism is required. 73 | 74 | #### 4.ii Explicitly support non-linear interpolation 75 | 76 | It's clear non-linear interpolation (NLI) is desired and, perhaps unintentionally, possible due to being 77 | able to provide multiple scalars which are multiplied together. See [NLI in Variable Fonts](https://github.com/PeterConstable/OT_Drafts/blob/master/NLI/UnderstandingNLI.md). 78 | 79 | Today NLI is complex and not necessarily size-efficient. Add additional basis functions 80 | to support NLI, clean mapping of an axis value to rotation, and extrapolation. 81 | 82 | #### 4.iii Efficient storage fo sparse variation data 83 | 84 | If, as proposed, we add variation aware components and stronger mapping between user-facing and internal axes it becomes increasingly desirable to have larger numbers of axes, each performing a narrow task. Currently use of an axis comes with a huge penalty: variation stores encode positions 85 | for all axes rather than only the axes that contribute to the result. 86 | 87 | Upgrade the variation stores to only consume bytes for axes that influence the result. 88 | 89 | ## ISO Submissions 90 | 91 | * WG03_otf-improvements ([docx](iso_docs/WG03_otf-improvements.docx?raw=true), [pdf](iso_docs/WG03_otf-improvements.pdf?raw=true)) 92 | * MPEG document identifier: m62947, submitted April 2023 93 | * Initial submission to spur ISO discussion 94 | 95 | ## References 96 | 97 | 1. [Better Engineered Font Formats](https://docs.google.com/presentation/d/1dVfuU7YhUBXg9MtU6kYBXVs9082PiHpwhGPYa--yA7c/edit?usp=sharing) 98 | * Notes from initial presentation 99 | -------------------------------------------------------------------------------- /VARC.md: -------------------------------------------------------------------------------- 1 | # `VARC` table: Variable Composites / Components 2 | 3 | This table encodes *variable-composite* glyphs in a way that can be combined 4 | with any of `glyf`/`gvar` or `CFF2`. Since the primary purpose of 5 | variable-composites is to make font files (especially the CJK fonts) smaller, 6 | several new data-structures are introduced to achieve more compression compared 7 | to an equivalent non-variable-composite font. 8 | 9 | 10 | ## Data Structures 11 | 12 | The following foundational data-structures are used in this able: 13 | 14 | - `uint32var` is a variable-length encoding of `uint32` values, which uses 1 to 15 | 5 bytes depending on the magnitude of the value being encoded. It borrows 16 | ideas from the UTF-8 encoding, but is more efficient because it does not have 17 | some of the random-access properties of UTF-8 encoding. 18 | 19 | - `TupleValues` is borrowed from the `TupleVariationStore` [Packed 20 | Deltas](https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#packed-deltas) 21 | with minor modification to allow storing 32-bit values. 22 | 23 | - `CFF2IndexOf` is simply a CFF2-style `Index` structure containing data of a 24 | particular type. CFF2 `Index` is defined 25 | [here](https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#5-index-data). 26 | The purpose of `CFF2IndexOf` is to efficiently store a list of variable-sized 27 | data, for example glyph records. 28 | 29 | - `MultiItemVariationStore`: This is a new data-structure. It is a hybrid 30 | between `ItemVariationStore`, and `TupleVariationStore`, borrowing ideas from 31 | both and improving upon them for more efficient storage of variations of 32 | *tuples* of numbers. 33 | 34 | 35 | ### `uint32var` 36 | 37 | To be added to [The OpenType Font File Data 38 | Types](https://learn.microsoft.com/en-us/typography/opentype/spec/otff#data-types) 39 | 40 | A `uint32var` is a variable-length encoding of a `uint32`. If the number fits in 41 | 7 bits, then it is encoded as is. Otherwise, it encodes the number of 42 | subsequent bytes as top bits of the first byte. The subsequent bytes use the 43 | full 8 bits for storage. 44 | 45 | *TODO:* Add table of encoding bytes. 46 | 47 | Here is Python code to read and write `uint32var` values: 48 | 49 | ```python 50 | def read_uint32var(data, i): 51 | """Read a variable-length uint32 number from data starting at index i. 52 | 53 | Return the number and the next index. 54 | """ 55 | 56 | b0 = data[i] 57 | if b0 < 0x80: 58 | return b0, i + 1 59 | elif b0 < 0xC0: 60 | return (b0 - 0x80) << 8 | data[i + 1], i + 2 61 | elif b0 < 0xE0: 62 | return (b0 - 0xC0) << 16 | data[i + 1] << 8 | data[i + 2], i + 3 63 | elif b0 < 0xF0: 64 | return (b0 - 0xE0) << 24 | data[i + 1] << 16 | data[i + 2] << 8 | data[ 65 | i + 3 66 | ], i + 4 67 | else: 68 | return (b0 - 0xF0) << 32 | data[i + 1] << 24 | data[i + 2] << 16 | data[ 69 | i + 3 70 | ] << 8 | data[i + 4], i + 5 71 | ``` 72 | ```python 73 | def write_uint32var(v): 74 | """Write a variable-length uint32 number. 75 | 76 | Return the data. 77 | """ 78 | if v < 0x80: 79 | return struct.pack(">B", v) 80 | elif v < 0x4000: 81 | return struct.pack(">H", (v | 0x8000)) 82 | elif v < 0x200000: 83 | return struct.pack(">L", (v | 0xC00000))[1:] 84 | elif v < 0x10000000: 85 | return struct.pack(">L", (v | 0xE0000000)) 86 | else: 87 | return struct.pack(">B", 0xF0) + struct.pack(">L", v) 88 | ``` 89 | 90 | ### `TupleValues` 91 | 92 | `TupleValues` is similar to the `TupleVariationStore` [Packed 93 | Deltas](https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#packed-deltas) 94 | with a minor modification: if the top two bits of the control byte 95 | (`DELTAS_ARE_ZERO` and `DELTAS_ARE_WORDS`) are both set, then the following 96 | values are 32-bit. That difference should be incorporated in the Packed Deltas 97 | section of `TupleVariationStore`, and is backwards-compatible because the two 98 | top bits can currently never be set at the same time. 99 | 100 | `TupleValues` can be used in two different ways: 101 | - When the number of values to be decoded is known in advance, decoding stops 102 | when the needed number of values are decoded. 103 | - When the number of values to be decoded is _not_ known in advance, but the 104 | number of encoded bytes is known, then values are decoded until the bytes are 105 | depleted. 106 | 107 | 108 | ### `CFF2IndexOf` 109 | 110 | `CFF2IndexOf` is simply a CFF2-style `Index` structure containing data of a particular type. 111 | CFF2 `Index` is defined 112 | [here](https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#5-index-data). 113 | The purpose of `CFF2IndexOf` is to efficiently store a list of variable-sized 114 | data, for example glyph records, or other data. 115 | 116 | Note that the `count` field of CFF2 `Index` structure is `uint32`, unlike the 117 | count field of CFF `Index` structure. 118 | 119 | ### `MultiItemVariationStore` 120 | 121 | To be added to [OpenType Font Variations Common Table 122 | Formats](https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats). 123 | 124 | A `MultiItemVariationStore` is a new data-structure. It is a hybrid between 125 | `ItemVariationStore`, and `TupleVariationStore`, borrowing ideas from both and 126 | improving upon them for more efficient storage of variations of *tuples* of 127 | numbers. 128 | 129 | Like `ItemVariationStore`, entries are addressed using a 32-bit `VarIdx`, 130 | with the top 16 bits called "outer" index, and lower 16 bits called the "inner" 131 | index. 132 | 133 | Whereas the `ItemVariationStore` stores deltas for a single scalar value for 134 | each `VarIdx`, the `MultiItemVariationStore` stores deltas for a tuple for each 135 | `VarIdx`. Compared to `ItemVariationStore`, the `MultiItemVariationStore` uses a 136 | sparse encoding of the _active_ axes for each region, which is more efficient 137 | in fonts with high number of axes. 138 | 139 | Compared to `TupleVariationStore`, the `MultiItemVariationStore` is optimized 140 | for smaller tuples and allows tuple-sharing, which is important for its 141 | efficiency over the `TupleVariationStore`. It also does not have some of the 142 | limitations that `TupleVariationStore` has, like the total size of an entry 143 | being limited to 64kb. 144 | 145 | The following structures form the `MultiItemVariationStore`. Its processing is 146 | fairly similar to that of the `ItemVariationStore`, except that the deltas 147 | encoded for each entry consist of multiple numbers per region. The 148 | `TupleValues` for each entry is as such the concatenation of the tuple deltas 149 | for each region. 150 | 151 | ```c++ 152 | struct MultiItemVariationStore 153 | { 154 | uint16 format; // Set to 1 155 | Offset32To variationRegionListOffset; 156 | uint16 itemVariationDataCount; 157 | Offset32To itemVariationDataOffsets[itemVariationDataCount]; 158 | }; 159 | ``` 160 | ```c++ 161 | struct SparseVariationRegionList 162 | { 163 | uint16 regionCount; 164 | Offset32To variationRegionOffsets[regionCount]; 165 | } 166 | ``` 167 | ```c++ 168 | struct SparseVariationRegion 169 | { 170 | uint16 regionAxisCount; 171 | SparseRegionAxisCoordinates regionAxes[regionAxisCount]; 172 | }; 173 | ``` 174 | ```c++ 175 | struct SparseRegionAxisCoordinates 176 | { 177 | uint16 axisIndex; 178 | F2DOT14 startCoord; 179 | F2DOT14 peakCoord; 180 | F2DOT14 endCoord; 181 | }; 182 | ``` 183 | 184 | ```c++ 185 | struct MultiItemVariationData 186 | { 187 | uint8 Format; // 1 188 | uint16 regionIndexCount; 189 | uint16 regionIndexes[regionIndexCount]; 190 | CFF2IndexOf deltaSets; 191 | }; 192 | ``` 193 | 194 | The `deltaSets` in a `MultiItemVariationData` table store the delta-set for a 195 | single tuple, addressed by the "inner" index of the `VarIdx`, whereas a 196 | `MultiItemVariationData` table itself represents the data for all values 197 | sharing the same "outer" index. 198 | 199 | The `TupleValues` for each entry are the concatenation of the tuple deltas for 200 | each region. The length of the tuple is calculate by dividing the number of 201 | entries in the `TupleValues` structure by the number of regions. 202 | 203 | 204 | ## Variable Composite Description 205 | 206 | A Variable Composite record is a concatenation of Variable Component records. 207 | Variable Component records have varying sizes. 208 | 209 | ```c++ 210 | struct VarCompositeGlyph 211 | { 212 | VarComponent components[]; 213 | }; 214 | ``` 215 | 216 | When decoding a `VarCompositeGlyph`, the decoder stops when the bytes for the 217 | glyph are depleted. 218 | 219 | ## Variable Component Record 220 | 221 | A Variable Component record encodes one component's glyph index, variations 222 | location, and transformation in a variable-sized and efficient manner. 223 | 224 | | type | name | notes | 225 | |-|-|-| 226 | | uint32var | `flags` | See below. | 227 | | GlyphID16 or GlyphID24 | `gid` | This is a GlyphID16 if `GID_IS_24BIT` bit of `flags` is clear, else GlyphID24. | 228 | | uint32var | `conditionIndex` | Optional, only present if `HAVE_CONDITION` bit of `flags` is set. | 229 | | uint32var | `axisIndicesIndex` | Optional, only present if `HAVE_AXES` bit of `flags` is set. | 230 | | TupleValues | `axisValues` | Optional, only present if `HAVE_AXES` bit of `flags` is set. The axis value for each axis. | 231 | | uint32var | `axisValuesVarIndex` | Optional, only present if `AXIS_VALUES_HAVE_VARIATION` bit of `flags` is set. | 232 | | uint32var | `transformVarIndex` | Optional, only present if `TRANSFORM_HAS_VARIATION` bit of `flags` is set. | 233 | | FWORD | `TranslateX` | Optional, only present if `HAVE_TRANSLATE_X` bit of `flags` is set. | 234 | | FWORD | `TranslateY` | Optional, only present if `HAVE_TRANSLATE_Y` bit of `flags` is set. | 235 | | F4DOT12 | `Rotation` | Optional, only present if `HAVE_ROTATION` bit of `flags` is set. Counter-clockwise. | 236 | | F6DOT10 | `ScaleX` | Optional, only present if `HAVE_SCALE_X` bit of `flags` is set. | 237 | | F6DOT10 | `ScaleY` | Optional, only present if `HAVE_SCALE_Y` bit of `flags` is set. | 238 | | F4DOT12 | `SkewX` | Optional, only present if `HAVE_SKEW_X` bit of `flags` is set. Counter-clockwise. | 239 | | F4DOT12 | `SkewY` | Optional, only present if `HAVE_SKEW_Y` bit of `flags` is set. Counter-clockwise. | 240 | | FWORD | `TCenterX` | Optional, only present if `HAVE_TCENTER_X` bit of `flags` is set. | 241 | | FWORD | `TCenterY` | Optional, only present if `HAVE_TCENTER_Y` bit of `flags` is set. | 242 | | uint32var[] | `reserved` | Optional, process and discard one `uint32var` per each set bit in `RESERVED_MASK`. | 243 | 244 | ### Variable Component Flags 245 | 246 | | bit number | name | 247 | |-|-| 248 | | 0 | `RESET_UNSPECIFIED_AXES` | 249 | | 1 | `HAVE_AXES` | 250 | | 2 | `AXIS_VALUES_HAVE_VARIATION` | 251 | | 3 | `TRANSFORM_HAS_VARIATION` | 252 | | 4 | `HAVE_TRANSLATE_X` | 253 | | 5 | `HAVE_TRANSLATE_Y` | 254 | | 6 | `HAVE_ROTATION` | 255 | | 7 | `HAVE_CONDITION` | 256 | | 8 | `HAVE_SCALE_X` | 257 | | 9 | `HAVE_SCALE_Y` | 258 | | 10 | `HAVE_TCENTER_X` | 259 | | 11 | `HAVE_TCENTER_Y` | 260 | | 12 | `GID_IS_24BIT` | 261 | | 13 | `HAVE_SKEW_X` | 262 | | 14 | `HAVE_SKEW_Y` | 263 | | 15-31 | `RESERVED_MASK`. Set to 0 | 264 | 265 | The flags are arranged in the order of likely use, to minimize the storage 266 | bytes of the `flags` field. 267 | 268 | ### Variable Component Transformation 269 | 270 | The transformation data consists of individual optional fields, which can be 271 | used to construct a transformation matrix. 272 | 273 | Transformation fields: 274 | 275 | | name | default value | 276 | |-|-| 277 | | TranslateX | 0 | 278 | | TranslateY | 0 | 279 | | Rotation | 0 | 280 | | ScaleX | 1 | 281 | | ScaleY | ScaleX | 282 | | SkewX | 0 | 283 | | SkewY | 0 | 284 | | TCenterX | 0 | 285 | | TCenterY | 0 | 286 | 287 | The `TCenterX` and `TCenterY` values represent the “center of transformation”. 288 | 289 | The rotation and skew parameters are in angles as multiples of Pi. 290 | 291 | Two new types, `F4DOT12` and `F6DOT10` need to be added to the [Data 292 | Types](https://learn.microsoft.com/en-us/typography/opentype/spec/otff#data-types) 293 | section of the specification. 294 | 295 | Details of how to build a transformation matrix, as Python code: 296 | ```python 297 | # Using fontTools.misc.transform.Transform 298 | t = Transform() # Identity 299 | t = t.translate(TranslateX + TCenterX, TranslateY + TCenterY) 300 | t = t.rotate(Rotation * math.pi) 301 | t = t.scale(ScaleX, ScaleY) 302 | t = t.skew(-SkewX * math.pi, SkewY * math.pi) 303 | t = t.translate(-TCenterX, -TCenterY) 304 | ``` 305 | 306 | ## `VARC` table header 307 | 308 | The top-level `VARC` table header is as follows: 309 | ```c++ 310 | struct VARC 311 | { 312 | uint16 majorVersion; // 1 313 | uint16 minorVersion; // 0 314 | Offset32To coverage; 315 | Offset32To varStore; 316 | Offset32To conditionList; 317 | Offset32To> axisIndicesList; 318 | Offset32To> glyphRecords; 319 | }; 320 | ``` 321 | ```c++ 322 | struct ConditionList 323 | { 324 | Array32Of>; // Array of offsets from the beginning of the ConditionList table 325 | } 326 | ``` 327 | 328 | The `coverage` table enumerates all glyphs that have Variable-Composite records 329 | in this table. The records are encoded in `glyphRecords` by coverage index as 330 | usual. 331 | 332 | The `varStore` stores the variations of axis-values and transform values as 333 | referenced in the Variable Component records. 334 | 335 | The `axisIndicesList` encodes a list of axis-indices tuples, and is shared by 336 | the glyphs which address it using an index. 337 | 338 | 339 | ## Processing 340 | 341 | The component glyphs to be loaded use the coordinate values specified (with any 342 | variations applied if present). The outlines from all components are concatenated 343 | to form the outline for the main glyph, before any rasterization. 344 | 345 | For each parsed component, if `HAVE_CONDITION` flag is set, then the component 346 | is loaded but _not_ used (eg. not displayed) unless the referred `Condition` 347 | (using `conditionIndex`) in the top-level `conditionList` evaluates to true. 348 | For `Condition` types that require a variation-store, the `MultiItemVariationStore` 349 | `varStore` shall be used to get deltas for an outer/inner variation numbers, 350 | with a tuple as appropriate. 351 | 352 | For any unspecified axis, the value used depends on flag 353 | `RESET_UNSPECIFIED_AXES`. If the flag is set, then the normalized values from 354 | font's current variation settings is used. 355 | If the flag is clear the axis values from current glyph being processed 356 | (which itself might recursively come from the font or its own parent glyphs) 357 | are used. For example, if the font variations have `wght`=.25 (normalized), 358 | and current glyph being processed is using `wght`=.5 because it was referenced 359 | from another VarComposite glyph itself, when referring to a component that does 360 | _not_ specify the `wght` axis, if the flag bit is set, then the value of 361 | `wght`=.25 (from font) will be used. If the flag bit is clear, `wght`=.5 (from 362 | current glyph) will be used. 363 | 364 | The component location and transform can vary. These variations are stored in 365 | the `MultiItemVariationStore` data-structure. The variations for location are 366 | referred to by the `axisValuesVarIndex` member of a component if any, and 367 | variations for transform are referred to by the `transformVarIndex` if any. For 368 | transform variations, only those fields specified and as such encoded as per 369 | the flags have variations. 370 | 371 | The component then is recursively loaded from the `VARC` table, if it is 372 | present in this table; otherwise, falls back to `glyf`/`gvar`, `CFF2`, or any 373 | other mechanism to get raw outlines. An exception to the recursive rule is 374 | that if a component refers to the glyphID of the current glyph, instead of 375 | recursing (which would result in infinite recursion), the glyph outline for the 376 | component is loaded directly instead, as if the glyphID had no `VARC` entry. 377 | 378 | **Note:** While it is the (undocumented?) behavior of the `glyf` table that 379 | glyphs loaded are shifted to align their LSB to that specified in the `hmtx` 380 | table, much like regular Composite glyphs, this does not apply to component 381 | glyphs being loaded as part of a variable-composite glyph. 382 | 383 | **Note:** A static (non-variable) font that uses the `VARC` table, _would not_ 384 | have `fvar` / `avar` tables but _would_ have the `gvar` table in a 385 | TrueType-flavored font, or `CFF2` variations in a CFF-flavored font. This is 386 | because the components themselves store their variables in the classic way. The 387 | exception to this situation is a font with `VARC` table that does NOT vary the 388 | component locations and transforms, and does not encode any location for the 389 | components either. In practice, such a font would just use the `VARC` table 390 | in the manner of classic components in a `glyf` table. 391 | -------------------------------------------------------------------------------- /avar2-in-designspace.md: -------------------------------------------------------------------------------- 1 | # Proposal for representing avar2 mappings in designspace documents 2 | 3 | This document discusses the representation of the `avar` version 2 table (“avar2”) in [.designspace documents](https://github.com/fonttools/fonttools/blob/main/Doc/source/designspaceLib/xml.rst). 4 | 5 | ## Introduction 6 | 7 | A .designspace document is a human-readable XML document used for compiling variable fonts from sources, and also as a source document for generating instances of variable fonts. The axes to be used in the variable font are specified. Optionally, “avar mappings” can be included. These modify the font’s internal axis values (the “output” axes) depending on the axis locations set by users (the “input” axes), presenting users with more intuitive axis controls. The [avar table version 1](https://learn.microsoft.com/en-us/typography/opentype/spec/avar) (avar1), in use for many years, is limited to single-axis mappings; each axis has its own set of mappings, with no interaction beween axes. 8 | 9 | The avar table version 2 (avar2) is a way to express complex relationships beween the set of “input” axis values (controlled by the user) and the set of “output” axis values (used internally by the font). In particular, avar2 represents a many-to-many relationship between input and output axis locations. In other words, multiple axes’ input locations can influence a single output location, and a single input location can influence multiple output locations. 10 | 11 | The avar2 table uses variation math itself to express relationships betewen input and output axes, making use of the the ItemVariationStore structure to represent active regions and deltas. These concepts are somewhat alien to .designspace authors, so in .designspace documents, we use a VariationModel solver to determine regions and deltas. The VariationModel solver acts the same way when compiling glyph sources into a variable font, resolving their designspace locations into variation regions. 12 | 13 | ## Goals 14 | 15 | * Try to build on the avar1 representation, avoiding new terminology if possible. 16 | 17 | * Represent avar2 in absolute user coordinates, so no explicit delta values and no normalized coordinates. (A significant benefit of absolute coordinates in .designspace is that it is easy to choose a different default source. It is not clear whether this benefit can be retained in avar2 representations.) 18 | 19 | * Avoid explicit terminology of *ItemVariationStore* objects: regions, deltas. Avoid discussion of compiler optimizations of ItemVariationStore. 20 | 21 | * Try to avoid explicit specification of minimum and maximum values of a region. Leave that to the VariationModel solver. 22 | 23 | ## avar1 representation (example for reference) 24 | 25 | ```xml 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | ``` 38 | 39 | ## avar2 representation 40 | The proposed syntax keeps the mappings definition within the \ element, and retains the \ element in avar1 mappings without change. A new \ element is introducted at the same level as the \ elements, and its contents is as follows. 41 | 42 | 1. The \ element contains one or more \ elements. 43 | 44 | 2. Each \ element contains one \ element and one \ element. 45 | 46 | 3. The \ element contains one or more \ elements, where each \ element specifies an absolute location on a single axis. The _tag_ attribute defines the axis, and the _xvalue_ attritube defines the location. Within an \ element, each axis is referenced either once or not at all. 47 | 48 | 4. The \ element contains one or more \ elements, where each \ element specifies either an absolute location or a delta on a single axis. The _tag_ attribute defines the axis, the _xvalue_ attritube defines the location, and the _delta_ attribute defines the delta. Within an \ element, each axis is referenced either once or not at all. If the _delta_ attribute is used, it is a value in the range [-1,1]. 49 | 50 | 5. The number of \ elements within an \ or \ element is often less than the number of axes, as is normal in variation math. 51 | 52 | 6. The set of \ axes may be disjoint from the set of \ axes. 53 | 54 | 7. A region where \ contains a single axis and \ contains the same single axis is equivalent to the \ mappings of avar1 (except that in avar2 there are no restrictions regarding mapping from one side of the default to the other). 55 | 56 | 8. Allow axis tag as well as axis name is allowed to identify axes. 57 | 58 | 9. Each output axis has either an explicit or implicit delta value. If implicit, the avar2 compiler normalizes the input and output xvalues to 2.14, then subtracts the normalized input value from the normalized output value, yielding the delta value. If there is no input for an axis, the axis default value is used as input. When storing deltas in _ItemVariationStore_, the compiler first multiplies the delta by 16384 to become an int16 value. 59 | 60 | 10. The \ element defines a variation region as defined in the OpenType spefication. Regions involving custom minimum and maximum values are not expressed explicitly, but are handled by a VariationModel solver. 61 | 62 | Here follows a generic example of avar2 \ where AX_0, AX_2 and AX_4 values are adjusted when the user designspace location is within the region specified as determined by its AX_0, AX_1 and AX_2 values: 63 | 64 | ```xml 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | ``` 80 | 81 | 82 | ## avar2 use case examples 83 | 84 | Here follow some suggestions for how to represent the distortion, parametric and HOI use-cases classes in .designspace. (Fences, the other proposed use case for avar2, are a special case of distortion.) 85 | 86 | ### Distortion use case 87 | 88 | ```xml 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | ``` 108 | 109 | This example distorts the location (wght=700, wdth=150) to (wght=650, wdth=140), a 2-dimensional mapping not possible with avar1. Due to variations math, the delta acts with a 1x scalar at (wght=700, wdth=150), reducing to 0x (no change) at the edges of the quadrant. 110 | 111 | To determine delta values, the compiler normalizes input xvalue and output xvalue to 2.14 using the normal avar1 process, then subtracts the normalized input value from the normalized output value. If there is an output for an axis but no input, the axis default value is used. Normalizing input and output xvalues gives: 112 | * `wght` input: 700 → (700-400)/(900-400) = 0.6 113 | * `wght` output: 650 → (650-400)/(900-400) = 0.5 114 | * `wdth` input: 150 → (150-100)/(200-100) = 0.5 115 | * `wdth` output: 140 → (140-100)/(200-100) = 0.4 116 | 117 | Subtracting the normalized input value from the normalized output value gives: 118 | * `wght`: 0.5 - 0.6 = -0.1 119 | * `wdth`: 0.4 - 0.5 = -0.1 120 | 121 | So for the region defined by `wght` [400, 700, 900], `wdth` [100, 150, 200] we have a delta set of [-0.1, -0.1] for the axes `wght` and `wdth`. 122 | 123 | This region covers the whole “upper right quadrant” of the designspace. 124 | 125 | ### Parametric use case 126 | 127 | This .designspace is for a parametric font with 3 user axes and 5 hidden parametric axes. We show a single mapping for the Weight axis acting on 3 parametric axes. Exactly the same method is used by the compiler as described above. The key difference with parametrics is that, since the output axis values are not specified in the input, the subtraction to determine the delta operates using the default values. 128 | 129 | For clarity only a single region is represented, that being from default to max `wght`. A full parametric font would need “monovar” regions for both sides of default for all 3 user axes, and optionally “duovar” and “trivar” regions for the combinations of user axes (3^3-1 = 26 regions in all for a 3-axis font, assuming no intermediate regions). 130 | 131 | ```xml 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | ``` 157 | 158 | To calculate the deltas for compilation, we normalize input and output values: 159 | * `XOPQ` input: 176 → (176-176)/(263-176) = 0.0 160 | * `XOPQ` output: 260 → (260-176)/(263-176) = 0.966 161 | * `XTRA` input: 562 → (562-562)/(640-562) = 0.0 162 | * `XTRA` output: 370 → (370-562)/(324-562) = -0.807 163 | * `YOPQ` input: 124 → (124-124)/(132-124) = 0.0 164 | * `YOPQ` output: 132 → (132-124)/(132-124) = 0.966 165 | 166 | Subtracting the normalized input value from the normalized output value gives: 167 | * `XOPQ`: 0.966 - 0.0 = 0.966 168 | * `XTRA`: -0.807 - 0.0 = -0.807 169 | * `YOPQ`: 0.966 - 0.0 = 0.966 170 | 171 | These values are multiplied by 16384 to become int16 values, stored as deltaSets in ItemVariationStore. 172 | 173 | As with the calculation of outline variation deltas from master outline coordinates, we need to recall that deltas are based on accumulated values at the “corners”. This may be difficult to generalize given a) the deltas are ultimately in 2.14, and b) we want to allow arbitrary regions, not just orthogonal ones. 174 | 175 | ### HOI use case 176 | 177 | This .designspace is for a HOI font with 1 user axis and 2 hidden subservient axes. 178 | 179 | ```xml 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | ``` 199 | 200 | 201 | ## Further notes 202 | 203 | ### Adding avar2 tables to existing variable fonts 204 | 205 | It will likely be a common occurence that we wish to add avar2 table to a compiled variable font. It seems natural to use identical .designspace mapping syntax in such cases, but this means a .designspace document without any UFO data. Perhaps `` can optionally refer to a single compiled variable font, and `` elements can be omitted: 206 | ```xml 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | ``` 217 | ### Combining avar1 and avar2 tables in the same font 218 | 219 | The avar2 specification allows for mappings for avar1 to coexist with mappings for avar2. The .designspace spec can support this: the avar1 maps are within each `` element; the avar2 maps are at the same level. For example: 220 | 221 | ```xml 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | ``` 246 | 247 | ### Compiler optimizations 248 | If an avar2 map is representable as avar1, the compiler should be free to use that option. 249 | 250 | 251 | ### Explicit deltas and normalized coordinates 252 | We might consider: 253 | * a relative mode, in case you want to work in deltas, with suggested representations within \\: 254 | * `mode="absolute"` (default), `mode="relative"` (deltas, optional) 255 | * `delta="..."` instead of `xvalue="..."` 256 | * allow direct specification of normalized coordinates, `normalized="1"` 257 | 258 | 259 | ## Appendix: Comparison with other XML methods of representing variation math 260 | 261 | ### TTX gvar 262 | 263 | ```xml 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | ... 273 | 274 | ``` 275 | 276 | ### TTX ItemVariationStore 277 | 278 | ```xml 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | ... 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | ``` 330 | 331 | 332 | ### TTX CFF2 blend 333 | 334 | CFF2 requires an ItemVariationStore, as well as delta sets defined with the `blend` operator: 335 | ```xml 336 | 337 | 338 | 339 | 340 | 341 | 342 | ``` 343 | 344 | 345 | ### designspace \\ 346 | 347 | ```xml 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | ... 368 | 369 | 370 | 371 | -------------------------------------------------------------------------------- /avar2.md: -------------------------------------------------------------------------------- 1 | # `avar`—Axis Variations Table Version 2 2 | 3 | The axis variations table (`avar`) is an optional table used in variable fonts. Version 1 of `avar` modifies aspects of how a design varies for different instances along a particular design-variation axis. It does this by piecewise linear remapping on a per-axis basis, with certain restrictions. See the [OpenType specification](https://docs.microsoft.com/en-us/typography/opentype/spec/avar) for details. 4 | 5 | Version 2, as proposed here, enables flexible axis remapping where each design-variation axis is modified according to the coordinates of multiple design-variation axes. In order to combine the effects of multiple input values, `avar` version 2 uses the OpenType variation mechanism itself to determine interpolated delta values and add them to design-variation axis coordinates. 6 | 7 | The efficiency of `avar` version 2 enables fonts that: 8 | 9 | * are significantly smaller; 10 | * are easier for users to control; 11 | * simplify source maintenance by avoiding redundant data. 12 | 13 | Use cases include: 14 | 15 | * warped variable font designspaces to reflect a typeface designer’s intention accurately; 16 | * parametric fonts with intuitive control methods and a much reduced data footprint; 17 | * offering simpler methods of control for specialized variable fonts. 18 | 19 | # avar version 2.0 header format 20 | 21 | | Type | Name | Description | 22 | | ------|-------|-------------| 23 | | `uint16` | `majorVersion` | Major version number of the axis variations table — set to 2. | 24 | | `uint16` | `minorVersion` | Minor version number of the axis variations table — set to 0. | 25 | | `uint16` | `` | Permanently reserved; set to zero. | 26 | | `uint16` | `axisSegmentMapCount` | The number of *axisSegmentMap*s for this font. If this is not 0, it must be the same as *axisCount* in the 'fvar' table. | 27 | | `SegmentMaps` | `axisSegmentMaps[axisSegmentMapCount]` | The segment maps array — one segment map for each axis, in the order of axes specified in the 'fvar' table. | 28 | | `Offset32To` | `axisIndexMapOffset` | Offset from beginning of the table to `axisIndexMap`. | 29 | | `Offset32To` | `varStoreOffset` | Offset from beginning of the table to `varStore`. | 30 | 31 | The table format for `avar` version 2 is the same as `avar` version 1, appended with offsets to two extra structures, `axisIndexMap` and `varStore`, and with the data for those structures. 32 | 33 | `axisIndexMap` is a *DeltaSetIndexMap* structure that maps the axis indices implied in `fvar` to indices used in `varStore`. The outer index identifies an *ItemVariationData* structure in `varStore`. The inner index identifies a *deltaSet* within an *ItemVariationData*. 34 | 35 | `varStore` is an *ItemVariationStore* structure that points to a *VariationRegions* array and a list of *ItemVariationData* structures. Each *ItemVariationData* specifies a subset of *VariationRegions* and an array of *deltaSets*. Each *deltaSet* specifies a delta value for each region. 36 | 37 | Delta values are typically in (but not limited to) the range [-1.0, 1.0]. 38 | 39 | Delta values are stored as if they were signed integers by multiplying their true value by 16384. Thus 1.0 is stored as 16384; -1.0 is stored as -16384. 40 | 41 | The *DeltaSetIndexMap* and *ItemVariationStore* formats are given in [OpenType Font Variations Common Table Formats](https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats). 42 | 43 | # Processing 44 | 45 | Processing of axis values in an `avar` version 2 table happens in 3 stages for a given instance: 46 | 47 | 1. Initial normalization to convert user coordinates of each axis to initial normalized coordinates in the range [-1.0, 1.0]. 48 | 2. Remap initial normalized coordinates of each axis via the *axisSegmentMap*s of `avar` version 1, providing intermediate coordinates also in the range [-1.0, 1.0]. 49 | 3. Calculate interpolated deltas for each axis via `avar` version 2 and add them to intermediate coordinates, providing final coordinates that are clamped to the range [-1,1]. 50 | 51 | In more detail, step 3 proceeds as follows. Considering the coordinates provided by step 2, a scalar is determined for each *variationRegion* by the usual variation interpolation algorithm. Then, the *deltaSet*s of each *ItemVariationData* are processed such that each delta value is multiplied by its associated scalar. Summing those products gives an interpolated delta to add to a particular axis coordinate, the axis index being defined in `axisIndexMap`. After the summing operations for all *ItemVariationData* structures is complete, axis values are clamped to to the range [-1.0, 1.0], giving final axis coordinates. 52 | 53 | The final axis coordinates obtained by step 3 are subsequently used in the standard variation process described in [Algorithm for Interpolation of Instance Values](https://docs.microsoft.com/en-us/typography/opentype/spec/otvaroverview#algorithm-for-interpolation-of-instance-values), applying to all `gvar` data and all `ItemVariationStore` data elsewhere in the font. 54 | 55 | The following algorithm implements step 3 above, producing the final normalized axis coordinates: 56 | 57 | ```c++ 58 | // let coords be the vector of current normalized coordinates. 59 | 60 | std::vector out; 61 | for (unsigned i = 0; i < coords.size(); i++) 62 | { 63 | int v = coords[i]; 64 | uint32_t varidx = i; 65 | 66 | if (axisIdxMap != 0) 67 | varidx = (this+axisIdxMap).map(varidx); 68 | 69 | float delta = 0; 70 | 71 | if (varStore != 0) 72 | delta = (this+varStore).get_delta (varidx, coords); 73 | 74 | v += std::roundf (delta); 75 | v = std::clamp (v, -(1<<14), +(1<<14)); 76 | 77 | out.push_back (v); 78 | } 79 | for (unsigned i = 0; i < coords.size(); i++) 80 | coords[i] = out[i]; 81 | ``` 82 | 83 | # Construction 84 | 85 | The way `ItemVariationStore`s are built is typically by using a variation modeler, that takes a series of _master_ values at certain locations in the design-space, and produces `VariationRegion`s and `deltaSets` to be stored in one or more `ItemVariationData` structures. This usage is identical, except that delta values apply to normalized axis coordinates rather than distances measured in font units. To build the `avar` version 2 mapping tables, the designer will need to produce a mapping of input axis locations and their respective output axis locations. This data then will constitute the set of _masters_ to be fed to the variation modeler and populate the `ItemVariationStore` that will go into the `avar` version 2 table. The variation index for each axis will be stored in `axisIndexMap`. 86 | 87 | 88 | # Use cases 89 | 90 | ## 1. Designspace warping 91 | 92 | In the OpenType variations specification, [registered axes](https://docs.microsoft.com/en-us/typography/opentype/spec/dvaraxisreg) offer a standard way to offer users control of a variable font on reasonably well defined scales. For example, users learn that the Regular version of a font has Weight=400, Width=100, while the Bold has wght=700, wdth=100. A typical 2-axis variable font with `wght` and `wdth` axes might have the following nine Named Instances: 93 | 94 | 95 | | | **wdth=75** | **wdth=100** | **wdth=125** | 96 | | --- |---|---|---| 97 | | **wght=300** | Light Condensed | Light | Light Extended | 98 | | **wght=400** | Condensed | Regular | Extended | 99 | | **wght=700** | Bold Condensed | Bold | Bold Extended | 100 | 101 | However, in fonts with more than one design axis, this approach lacks the flexibility of older methods of interpolation, where the type designer used a font design application to specify freely the axis values for, say, the Bold Condensed. Notably, the `wght` coordinate of Bold Condensed did not need to match the `wght` coordinate of Bold, nor did the `wdth` coordinate of Bold Condensed need to match the `wdth` coordinate of Condensed. A Bold Condensed with `wght`,`wdth` of (677,81) rather than (700,75) was perfectly reasonable. Our grid of instances in such as case would look something like this: 102 | 103 | | | **Condensed** | **Regular** | **Expanded** | 104 | | --- |---|---|---| 105 | | **Light** | 300,75 | 300,100 | 300,115 | 106 | | **Regular** | 400,75 | 400,100 | 400,120 | 107 | | **Bold** | 677,81 | 695,100 | 700,125 | 108 | 109 | While with OpenType 1.8 it is possible to set up a designspace like this, it has the significant drawback that many applications and systems expect all Bold weights to be at `wght`=700 and all Condensed weights to be at `wdth`=75, whatever the values of other axes. In practice, many OpenType 1.8 fonts encode numerous additional masters that ensure the design is as intended at all designspace locations. This not only increases the data footprint significantly, but also adds to the maintenance burden of the font. 110 | 111 | Furthermore, there are many cases where a type designer does not intend to offer instances in certain regions of the design space. For example, a Black Condensed instance is often difficult to design, and many type families omit it. Desired behaviour, in case a user requests `wght`=900, `wdth`=75, may be for the font to provide the same instance for the Black Condensed as it does for the Bold Condensed. Luc(as) de Groot analyses the issue in his [Typo Labs 2018 talk](https://www.youtube.com/watch?v=I75Efo7whrs&t=35m48s), where he calls for “virtual masters”. 112 | 113 | By applying deltas to axis values anywhere in the designspace, the `avar` table version 2 “warps” the designspace and so resolves the issues described. 114 | 115 | As with variation deltas in general, we also benefit from interpolation when axis values are influenced by a delta but are not exactly at the location of the delta. Each delta value is encoded as F2DOT14. Its interpolated value is determined by the standard OpenType variations algorithm, then added to the value obtained from the standard normalization process. Thus, assuming the Bold Condensed instance at (700,75) has normalized coordinates (1,-1) and assuming the designspace location (677,81) has normalized coordinates ((677-400)/(700-400), -1 + (81-75)/(100-75)) = (0.9233,-0.76), then the `ItemVariationStore` needs a delta set with peaks at (-1,1), thus with region ((-1,-1,0),(0,1,1)) and the following delta values: 116 | 117 | * `wght` -0.0767 118 | * `wdth` +0.24 119 | 120 | In practice there may be other delta sets required at the partial default locations (-1,0) and (0,1), which then influence the delta values required at (-1,1), in line with normal variations math. 121 | 122 | The result of implementing designspace warping this way is that we preserve the original axis values where applications and systems expect them, namely all Bold instances at `wght`=700 and all Condensed weights at `wdth`=75, while at the same time minimizing the data footprint. 123 | 124 | 125 | ## 2. Duplication of axis values for non-linear interpolation 126 | 127 | Certain variable fonts encode delta sets in such a way that, when multiple axes are synchronized, outline points move in curves rather than in straight lines. This technique was [invented by Underware](https://underware.nl/case-studies/hoi/) and [presented in 2018](https://www.youtube.com/watch?v=YVMukTiElUQ) under the name HOI (higher order interpolation). The details of the non-linear encoding need not concern us here, but a practical drawback under OpenType 1.8 is that it requires users to set multiple design axes to identical values in order to obtain valid instances. When axes are not synchronized, the resulting glyphs are severely distorted and useless. Synchronization using JavaScript has been proposed as a solution. A clever exploit of the CFF2 format, allowing a single axis to perform all the necessary non-linear adjustment of outline points, has also been demonstrated (but this method cannot be adapted to adjust delta values in an *ItemVariationStore* non-linearly). 128 | 129 | The `avar` version 2 table provides a solution to the synchonization problem, such that a user adjusts one “primary” axis and that value is cloned to other subordinate axes. Ideally the subordinate axes have the Hidden flag set in `fvar` to discourage manual operation. For a font with 3 axes requiring identical values, if the first axis is considered primary, then the other two can encode delta sets in `avar`’s *ItemVariationStore* to clone the value of the first axis. Assuming the subordinate axes have the same minimum, default and maximum values as the primary axis, each subordinate axis requires either one or two delta sets to clone the negative and positive regions of the normalized value of the primary axis: 130 | 131 | * If the axes use only the normalized region [0,1] then one delta set per subordinate axis is needed, referring to the region (0,1,1) of the primary axis and having the delta value of 1. This is the common case. 132 | 133 | * If the axes use only the normalized region [-1,0] then one delta set per subordinate axis is needed, referring to the region (-1,-1,0) of the primary axis and having the delta value of -1. 134 | 135 | * If the axes use both the normalized regions [-1,0] and [0,1] then two delta sets per subordinate axis are needed, one referring to the region (0,1,1) of the primary axis and having the delta value of 1, the other referring to the region (-1,-1,0) of the primary axis and having the delta value of -1. 136 | 137 | 138 | ## 3. Simplified controls for parametric fonts without redundant data 139 | 140 | Parametric fonts, explored by [Donald Knuth](https://en.wikipedia.org/wiki/Metafont) and others, offer fine-tuned control of specific font-wide aspects of their design. Such aspects typically include the thickness of vertical strokes, thickness of horizontal strokes, the overall width of the font, and direct control over vertical zones including cap-height, ascender height, x-height and descender height. 141 | 142 | In an OpenType variable font these parameters can be encoded as design-variation axes. The [Type Network Variations Proposal](https://variationsguide.typenetwork.com) of 2017 provides the basis for parametric OpenType fonts [Amstelvar](https://github.com/googlefonts/amstelvar), [Roboto Flex](https://github.com/googlefonts/roboto-flex) and others, demonstrating the potential for usable parametric fonts with large character sets. That flexibility comes from a designspace of many axes – sometimes 10 or more – which raises issues of control. Faced with 10 sliders, users are unlikely to be able to find the instance they want and are likely to get “lost” in a unusable region of the designspace. 143 | 144 | In OpenType 1.8, the solution to getting “lost” is to include “blended” axes that combine the deltas of parametric axes in such a way that they function in exactly the same way as they would in a non-parametric font. The blended axes are exposed using the registered axes of `wght`, `wdth`, `opsz`, etc., or well specified custom axes, and they are encoded in the standard OpenType 1.8 manner using `gvar` and related data. The major problem with such fonts is their large data footprint, as the blended axes effectively replicate much of what the parametric axes do. For a font containing both parametric and blended axes, the problem is particularly acute, so the parametric axes may be omitted to save space — thereby removing core functionality. In such cases the parametric nature of the font is thus reduced to a convenience of sources. 145 | 146 | The `avar` table version 2 provides a method to deploy a purely parametric font with user-friendly axes, while avoiding the data bloat of blended axes. The user axes, for example `wght` and `wdth`, on their own do nothing (i.e. they have no `gvar` or similar data), but they control the parametric axes by means of `avar`. The blending of parametric values into user-facing values effectively happens live in the font engine. The parametric axes are expected to have the Hidden flag set in `fvar` to discourage (but not block) manual operation. 147 | 148 | 149 | # Inverse avar processing 150 | 151 | Normally it is not possible for an application to obtain normalized axis values directly, these remaining private to the font engine. In fonts with an `avar` version 2 table, however, it can be useful to know the axis values that would invoke a given instance if the font engine did not support `avar` version 2. Use cases include: 152 | 153 | * informing users of the effective settings of parametric axes; 154 | * providing a polyfill for `avar` version 2. 155 | 156 | The solution is for the application itself to implement the `avar` algorithms, both version 1 and version 2. This requires not only access to the binary `avar` table, but also knowledge of the font’s axis extents as defined in the `fvar` table. Once an application has a complete set of final normalized values as defined above, the inverse of the `avar` version 1 algorithm can be applied to obtain user coordinates for all axes. (Care must be taken to avoid a divide-by-zero error in implementing an inverse `avar` version 1 mapping, in the case where consecutive `toCoordinate` values are identical.) 157 | 158 | Almost all fonts can provide a valid set of user axis coordinates for a given set of final normalized axis coordinates in this way. Exceptions are those fonts that encode deltas in a region that is not accessible without `avar` version 2, namely those fonts where the `fvar` definition of an axis normally precludes an active negative region or a active positive region by having its default equal to its minimum or its maximum respectively. Such cases are expected to be rare. 159 | 160 | 161 | # Recommendations and notes 162 | 163 | ## Hidden axes 164 | 165 | Since many `avar` version 2 fonts have axes not not intended for manual adjustment, it is recommended that such axes set the “hidden” flag in `VariationAxisRecord` of the [`fvar`](https://docs.microsoft.com/en-us/typography/opentype/spec/fvar) table. 166 | 167 | ## Efficient axisIdxMap and ItemVariationData construction 168 | 169 | In order to reduce proliferation of zero deltas, it is recommended to store in `axisIndexMap` only those axes that are remapped by `avar` version 2. For the same reason, if there are multiple different types of axis mapping affecting different sets of axes, consider using multiple `ItemVariationData` structures. 170 | 171 | ## Other notes 172 | * The set of axes involved in adjusting the coordinate for a given axis may include the axis itself. 173 | 174 | * It is expected that implementations that handle only `avar` version 1 will ignore the entire table by rejecting the `majorVersion` value. 175 | 176 | -------------------------------------------------------------------------------- /beyond-64k.md: -------------------------------------------------------------------------------- 1 | # Beyond 64K 2 | 3 | This proposal enables fonts that have more than 65,535 glyphs. 4 | 5 | 6 | ## Design 7 | 8 | We propose extending the number of glyphs in the font from 65,535 (16-bit) to 16,777,216 (24-bit). 9 | 10 | Currently the number of glyphs in the font is encoded in the `maxp` table and is limited to 65535. In this proposal, the number of glyphs in the font will be calculated based on the length of the `loca` table. Moreover, tables that encode glyph indices as two-byte numbers are extended with versions of structures that encode 24-bit glyph indices. 11 | 12 | While extending tables to encode 24-bit glyph indices, many tables are also extended to use 24-bit offsets instead of 16-bit offsets to allow for larger tables necessary for the larger number of glyphs. 13 | 14 | 15 | ## Per-table changes 16 | 17 | 18 | ### `maxp` table 19 | 20 | The `numGlyphs` field of the `maxp` table for a font with more than 65,535 glyphs should be set to 65,535, although this is not required for the functioning of the design. 21 | 22 | 23 | ### `loca` / `glyf` tables 24 | 25 | The `loca` / `glyf` tables are not required to have the same number of glyphs as specified in the `maxp` table. In fact, the length of the `loca` table now determines the number of glyphs in the font, which can be larger than `numGlyphs.maxp`. 26 | 27 | For a Format 0 `loca` table (`head.indexToLocFormat` = 0), the number of glyphs in the font is defined as the length of the `loca` table divided by 2, minus 1; for a Format 1 `loca` table (`head.indexToLocFormat` = 1), the number of glyphs in the font is defined as the length of the `loca` table divided by 4, minus 1. 28 | 29 | [issue](https://github.com/harfbuzz/boring-expansion-spec/issues/8) 30 | 31 | 32 | #### Composite glyphs 33 | 34 | Add a new flag to allow encoding 24bit glyph indices in composite glyphs: 35 | 36 | ``` 37 | enum ComponentGlyphFlags 38 | ARG_1_AND_2_ARE_WORDS = 0x0001, 39 | ARGS_ARE_XY_VALUES = 0x0002, 40 | ROUND_XY_TO_GRID = 0x0004, 41 | WE_HAVE_A_SCALE = 0x0008, 42 | MORE_COMPONENTS = 0x0020, 43 | WE_HAVE_AN_X_AND_Y_SCALE = 0x0040, 44 | WE_HAVE_A_TWO_BY_TWO = 0x0080, 45 | WE_HAVE_INSTRUCTIONS = 0x0100, 46 | USE_MY_METRICS = 0x0200, 47 | OVERLAP_COMPOUND = 0x0400, 48 | SCALED_COMPONENT_OFFSET = 0x0800, 49 | UNSCALED_COMPONENT_OFFSET = 0x1000, 50 | GID_IS_24BIT = 0x2000 51 | }; 52 | ``` 53 | 54 | [issue](https://github.com/harfbuzz/boring-expansion-spec/issues/59) 55 | 56 | 57 | ### `gvar` table 58 | 59 | Currently the `gvar` table encodes a `uint16 glyphCount` that must match "the number of glyphs stored elsewhere in the font." That field is then used in a subsequent array: 60 | 61 | | Type | Name | Description | 62 | | ------- | --------- | ----------------- | 63 | | Offset16 or Offset32 | glyphVariationDataOffsets[glyphCount+1] | Offsets from the start of the GlyphVariationData array to each GlyphVariationData table. | 64 | 65 | We deprecate the `glyphCount` field, and make the array size match the number of glyphs stored elsewhere in the font instead. 66 | 67 | This allows us to expand `gvar` without needing to bump to table format 2, which we like to reserve for more extensive table overhaul. 68 | 69 | [issue](https://github.com/harfbuzz/boring-expansion-spec/issues/85) 70 | 71 | 72 | ### `cmap` table 73 | 74 | The `cmap` formats 12 and 13 subtables already support 32-bit glyph indices. 75 | 76 | A new sub-table format 16 will be added which is similar to format 14 but uses 24-bit glyph indices: 77 | ``` 78 | struct UVSMapping24 { 79 | uint24 unicodeValue; 80 | uint24 glyphID; 81 | }; 82 | ``` 83 | The rest of format 16 subtable is similar to format 14. 84 | 85 | [issue](https://github.com/harfbuzz/boring-expansion-spec/issues/13) 86 | 87 | 88 | ### `hmtx` / `vmtx` tables 89 | 90 | Below we discuss `hmtx`. The case of `vmtx` is similar. 91 | 92 | Currently the `hmtx` table length is determined by `hhea`/`maxp` in the following way: 93 | 94 | * `hhea.metricDataFormat` is required to be 0 for the current format; 95 | * `hmtx` is required to be `2 * hhea.numberOfHMetrics + 2 * maxp.numGlyphs` bytes long. 96 | 97 | We describe how we to relax the second requirement, to allow encoding advance width of arbitrary number of glyphs in the `hmtx` table, regardless of the value of `maxp.numGlyphs`. 98 | 99 | This is how an advance width is assigned to glyph indices beyond `maxp.numGlyphs`: 100 | 101 | Let B be the excess bytes at the end of the `hmtx` table beyond the `2 * hhea.numberOfHMetrics + 2 * maxp.numGlyphs` bytes. 102 | 103 | - If the length of B is odd, ignore the last byte of B. 104 | - If B is empty, use the last advanceWidth in the `hmtx` table for all extra glyphs. 105 | - Treat B as an array of `uint16` advance-width numbers of glyph indices starting at `maxp.numGlyphs`. For any glyph index that is in range in the font but out of range in this array, use the last item of the array. 106 | 107 | No `lsb` (leftside-bearing) is encoded for glyphs beyond `maxp.numGlyphs`. 108 | 109 | [issue](https://github.com/harfbuzz/boring-expansion-spec/issues/7) 110 | 111 | 112 | #### `lsb` offsetting of glyphs 113 | 114 | It is an undocumented behavior of OpenType that glyphs from the `glyf` table are shifted by `lsb - xMin` when rasterized. Since our extension to the `hmtx` table does _not_ encode `lsb` for new gids, no shifting is done for such glyphs. In other words, the `lsb` of such glyphs is assumed to be the same as `xMin`. 115 | 116 | 117 | #### `tsb` and vertical glyphs 118 | 119 | The vertical origin of glyphs in the `glyf` table is computed based on `tsb` encoded in the `vmtx` table. Since our extension to the `vmtx` table does not encode `tsb` for new glyphs, the `VORG` table _must_ be used to encode vertical glyph origins instead. 120 | 121 | [issue](https://github.com/harfbuzz/boring-expansion-spec/issues/10) 122 | 123 | 124 | ### `HVAR` / `VVAR` tables 125 | 126 | No changes needed. 127 | 128 | [issue](https://github.com/harfbuzz/boring-expansion-spec/issues/12) 129 | 130 | 131 | ### `VORG` table 132 | 133 | Currently the length of the `VORG` table is determined from its major/minor version and the subsequent content. The only currently defined version 1.0 can only encode 16bit glyph indices. Here is how we expand the table's definition to encode glyph vertical origin for glyph indices beyond `maxp.numGlyphs`: 134 | 135 | Derive the length of the table from the version number and the table struct, then derive the excess bytes, then follow the same algorithm for deriving advance-width in the hmtx/vmtx expansion and use those numbers as vertical origin instead. 136 | 137 | [issue](https://github.com/harfbuzz/boring-expansion-spec/issues/11) 138 | 139 | 140 | ### `COLR` table 141 | 142 | To be done as part of `COLRv2`. 143 | 144 | [issue](https://github.com/harfbuzz/boring-expansion-spec/issues/47) 145 | 146 | 147 | ### `sbix` table 148 | 149 | The number of glyphs referenced by the table is the number of glyphs in the font as defined earlier. Same applies to non-OpenType tables `kerx` and `morx`. 150 | 151 | [issue](https://github.com/harfbuzz/boring-expansion-spec/issues/44) 152 | 153 | 154 | ### `GDEF` / `GSUB` / `GPOS` tables 155 | 156 | #### `GDEF` version 2 157 | 158 | The main `GDEF` struct is augmented with a version 2 to alleviate offset-overflows when classDef and other structs grow large: 159 | ``` 160 | struct GDEFVersion2 { 161 | Version version; // 0x00020000 162 | Offset24To glyphClassDef; 163 | Offset24To attachList; 164 | Offset24To ligCaretList; 165 | Offset24To markAttachClassDef; 166 | Offset24To markGlyphSets; 167 | Offset32To varStore; 168 | }; 169 | ``` 170 | Note that the varStore offset is 32bit, for compatibility with 0x00010003 version. 171 | 172 | [issue](https://github.com/harfbuzz/boring-expansion-spec/issues/36) 173 | 174 | 175 | #### `GSUB` 176 | 177 | The following sections when combined, fully cover the `GSUB` table extension: 178 | * `GSUB` / `GPOS` version 2 179 | * `Coverage` / `ClassDef` formats 3 & 4 180 | * `GSUB` `SingleSubst` formats 3 & 4 181 | * `GSUB` `MultipleSubst` / `AlternateSubst` format 2 182 | * `GSUB` `LigatureSubst` format 2 183 | * `GSUB` / `GPOS` `(Chain)Context` format 4 & 5 184 | 185 | [issue](https://github.com/harfbuzz/boring-expansion-spec/issues/35) 186 | 187 | 188 | #### `GPOS` 189 | 190 | The following sections when combined, fully cover the `GSUB` table extension: 191 | * `GSUB` / `GPOS` version 2 192 | * `Coverage` / `ClassDef` formats 3 & 4 193 | * `GPOS` `PairPos` formats 3 & 4 194 | * `GPOS` `MarkBasePos` / `MarkLigPos` / `MarkMarkPos` format 2 195 | * `GSUB` / `GPOS` `(Chain)Context` format 4 & 5 196 | 197 | [issue](https://github.com/harfbuzz/boring-expansion-spec/issues/39) 198 | 199 | 200 | #### `GSUB` / `GPOS` version 2 201 | 202 | The main `GSUB` / `GPOS` structs currently result in an offset-overflow with more than ~3k lookups. They are augmented with a version 2 that alleviates this problem: 203 | ``` 204 | struct GSUBGPOSVersion2 { 205 | Version version; // 0x00020000 206 | Offset24To scripts; 207 | Offset24To features; 208 | Offset24To lookups; 209 | Offset32To featureVars; 210 | }; 211 | ``` 212 | Note that the last item is a 32bit offset, for compatibility with version 0x00010001 tables. 213 | 214 | `LookupList24` is a `List16` of `Offset24` to `Lookup` structures: 215 | ``` 216 | using LookupList24 = List16OfOffse24tTo; 217 | ``` 218 | 219 | [issue](https://github.com/harfbuzz/boring-expansion-spec/issues/58) 220 | 221 | 222 | #### `Coverage` / `ClassDef` formats 3 & 4 223 | 224 | Coverage and ClassDef tables are augmented with formats 3 and 4 to allow for gid24, which parallel formats 1 & 2 respectively: 225 | ``` 226 | struct CoverageFormat3 { 227 | uint16 format; // == 3 228 | Array16Of glyphs; 229 | }; 230 | 231 | struct CoverageFormat4 { 232 | uint16 format; // == 4 233 | Array16Of ranges; 234 | }; 235 | 236 | struct ClassDefFormat3 { 237 | uint16 format; // == 3 238 | GlyphID24 startGlyphID; 239 | Array24Of classes; 240 | }; 241 | 242 | struct ClassDefFormat4 { 243 | uint16 format; // == 4 244 | Array24Of ranges; 245 | }; 246 | 247 | struct Range24Record { 248 | GlyphID24 startGlyphID; 249 | GlyphID24 endGlyphID; 250 | uint16 value; 251 | }; 252 | ``` 253 | 254 | [issue](https://github.com/harfbuzz/boring-expansion-spec/issues/30) 255 | 256 | 257 | #### `GSUB` `SingleSubst` formats 3 & 4 258 | 259 | Clarify that in format 1, delta addition math only affects the lower 16bits of the gid. Format 3 delta addition math is module 2^24. 260 | 261 | Two new formats, 3 & 4, are introduced, that parallel formats 1 & 2 respectively: 262 | ``` 263 | struct SingleSubstFormat3 { 264 | uint16 format; // == 3 265 | Offset24To coverage; 266 | int24 deltaGlyphID; 267 | }; 268 | 269 | struct SingleSubstFormat4 { 270 | uint16 format; // == 4 271 | Offset24To coverage; 272 | Array16Of substitutes; 273 | }; 274 | ``` 275 | 276 | [issue](https://github.com/harfbuzz/boring-expansion-spec/issues/31) 277 | 278 | 279 | #### `GSUB` `MultipleSubst` / `AlternateSubst` format 2 280 | 281 | Format 2 is introduced to enable gid24: 282 | ``` 283 | struct MultipleSubstFormat2 { 284 | uint16 format; // == 2 285 | Offset24To coverage; 286 | Array16Of> sequences; 287 | }; 288 | 289 | typedef Array16Of Sequence24; 290 | 291 | struct AlternateSubstFormat2 { 292 | uint16 format; // == 2 293 | Offset24To coverage; 294 | Array16Of> alternateSets; 295 | }; 296 | 297 | typedef Array16Of AlternateSet24; 298 | ``` 299 | 300 | [issue](https://github.com/harfbuzz/boring-expansion-spec/issues/32) 301 | 302 | 303 | #### `GSUB` `LigatureSubst` format 2 304 | 305 | Format 2 is introduced to enable gid24: 306 | ``` 307 | struct LigatureSubstFormat2 { 308 | uint16 format; == 2 309 | Offset24To coverage; 310 | Array16Of> ligatureSets; 311 | }; 312 | 313 | struct LigatureSet24 { 314 | Array16Of> ligatures; 315 | }; 316 | 317 | struct Ligature24 { 318 | GlyphID24 ligatureGlyph; 319 | uint16 componentCount; 320 | GlyphID24 componentGlyphIDs[componentCount - 1]; 321 | }; 322 | ``` 323 | 324 | [issue](https://github.com/harfbuzz/boring-expansion-spec/issues/33) 325 | 326 | 327 | #### `GPOS` `PairPos` formats 3 & 4 328 | 329 | Two new formats, 3 & 4, are introduced that parallel formats 1 & 2. 330 | 331 | Format 4 is only introduced to alleviate offset-overflow issues and is not otherwise needed for gid24 support. 332 | 333 | ``` 334 | struct PosFormat3 { 335 | uint16 format; == 3 336 | Offset24To coverage; 337 | uint16 valueFormat1; 338 | uint16 valueFormat2; 339 | Array16Of> pairSets; 340 | }; 341 | 342 | struct PairSet24 { 343 | uint24 pairValueCount; 344 | PairValueRecord24 pairValueRecords[pairValueCount]; 345 | }; 346 | 347 | struct PairValueRecord24 { 348 | GlyphID24 secondGlyph; 349 | ValueRecord valueRecord1; 350 | ValueRecord valueRecord2; 351 | }; 352 | ``` 353 | 354 | ``` 355 | struct PosFormat4 { 356 | uint16 format; == 4 357 | Offset24To coverage; 358 | uint16 valueFormat1; 359 | uint16 valueFormat2; 360 | Offset24To classDef1; 361 | Offset24To classDef2; 362 | uint16 class1Count; 363 | uint16 class2Count; 364 | Class1Record class1Records[class1Count]; 365 | }; 366 | ``` 367 | 368 | [issue](https://github.com/harfbuzz/boring-expansion-spec/issues/38) 369 | 370 | 371 | #### `GPOS` `MarkBasePos` / `MarkLigPos` / `MarkMarkPos` format 2 372 | 373 | Format 2 is introduce just to alleviate offset-overflow issues at the top-level structure. All downstream structures are reused: 374 | ``` 375 | 376 | The Coverage table parts are covered in #30. 377 | 378 | Add one new format of each, just to upgrade offsets of the top-level subtable to 24bit. All downstream structs are reused and not expanded. 379 | 380 | struct MarkBasePosFormat2 { 381 | uint16 format; // == 2 382 | Offset24To markCoverage; 383 | Offset24To baseCoverage; 384 | uint16 markClassCount; 385 | Offset24To markArray; 386 | Offset24To baseArray; 387 | }; 388 | 389 | struct MarkLigaturePosFormat2 { 390 | uint16 format; // == 2 391 | Offset24To markCoverage; 392 | Offset24To ligatureCoverage; 393 | uint16 markClassCount; 394 | Offset24To markArray; 395 | Offset24To ligatureArray; 396 | }; 397 | 398 | struct MarkMarkPosFormat2 { 399 | uint16 format; // == 2 400 | Offset24To mark1Coverage; 401 | Offset24To mark2Coverage; 402 | uint16 markClassCount; 403 | Offset24To mark1Array; 404 | Offset24To mark2Array; 405 | }; 406 | ``` 407 | 408 | [issue](https://github.com/harfbuzz/boring-expansion-spec/issues/40) 409 | 410 | 411 | #### `GSUB` / `GPOS` `(Chain)Context` format 4 & 5 412 | 413 | Add `Context` and `ChainContext` format 4 that parallels format 1 for gid24: 414 | ``` 415 | struct ContextFormat4 { 416 | uint16 format; == 4 417 | Offset24To coverage; 418 | Array16Of> ruleSets; 419 | }; 420 | 421 | struct GlyphRuleSet24 { 422 | Array16Of> rules; 423 | }; 424 | 425 | struct GlyphRule24 { 426 | uint16 glyphCount; 427 | GlyphID24 glyphs[inputGlyphCount - 1]; 428 | uint16 seqLookupCount; 429 | SequenceLookupRecord seqLookupRecords[seqLookupCount]; 430 | }; 431 | ``` 432 | ``` 433 | struct ChainContextFormat4 { 434 | uint16 format; == 4 435 | Offset24To coverage; 436 | Array16Of> ruleSets; 437 | }; 438 | 439 | struct ChainGlyphRuleSet24 { 440 | Array16Of> rules; 441 | }; 442 | 443 | struct ChainGlyphRule24 { 444 | uint16 backtrackGlyphCount; 445 | GlyphID24 backtrackGlyphs[backtrackGlyphCount]; 446 | uint16 inputGlyphCount; 447 | GlyphID24 inputGlyphs[inputGlyphCount - 1]; 448 | uint16 lookaheadGlyphCount; 449 | GlyphID24 lookaheadGlyphs[lookaheadGlyphCount]; 450 | uint16 seqLookupCount; 451 | SequenceLookupRecord seqLookupRecords[seqLookupCount]; 452 | }; 453 | ``` 454 | 455 | Add `Context` and `ChainContext` format 5 that parallels format 2 for offset-overflow alleviation: 456 | ``` 457 | struct ContextFormat5 { 458 | uint16 format; == 5 459 | Offset24To coverage; 460 | Offset24To classDef; 461 | Array16Of> ruleSets; 462 | }; 463 | ``` 464 | ``` 465 | struct ChainContextFormat5 { 466 | uint16 format; == 5 467 | Offset24To coverage; 468 | Offset24To backtrackClassDef; 469 | Offset24To inputClassDef; 470 | Offset24To lookaheadClassDef; 471 | Array16Of> ruleSets; 472 | }; 473 | ``` 474 | The `ClassRuleSet` and `ChainClassRuleSet` are _not_ extended, because they are class-based, not glyph-based, so no extension is necessary. 475 | 476 | Format 3 (Coverage-based format) is _not_ extended, because it only encodes one rule, so overflows are unlikely. 477 | 478 | [issue](https://github.com/harfbuzz/boring-expansion-spec/issues/34) 479 | -------------------------------------------------------------------------------- /glyf1-cubicOutlines.md: -------------------------------------------------------------------------------- 1 | # `glyf`—Glyf Data Table Format 1: Cubic-Bezier Outlines 2 | 3 | The glyf data table ([`glyf`](https://docs.microsoft.com/en-us/typography/opentype/spec/glyf)) stores the glyph outline data. The format number for this table is stored in the [`head`](https://docs.microsoft.com/en-us/typography/opentype/spec/head) table's `glyphDataFormat` field. The only currently defined format of the `glyf` table is format 0. We propose format 1 of the `glyf` table to add multiple features. See full [proposal](glyf1.md) for all features. In this proposal we cover: _Cubic-Bezier Outlines_. 4 | 5 | In `glyf` table format 0, glyph outlines use quadratic Bezier curve segments. 6 | 7 | In `glyf` table format 1, glyph outlines can mix quadratic and cubic Bezier curve segments. 8 | 9 | ## Specification 10 | 11 | Add the following flag to the [Simple Glyph Description](https://learn.microsoft.com/en-us/typography/opentype/spec/glyf#simple-glyph-description) section's _Simple Glyph Flags_: 12 | | Mask | Name | Description | 13 | |------|-------|-------------| 14 | | 0x80 | `CUBIC` | Bit 7: Off-curve point belongs to a cubic-Bezier segment | 15 | 16 | ### Restrictions 17 | 18 | Currently, there are several restrictions on how the `CUBIC` flag can be used. If any of the conditions below are not met, the behavior is undefined. 19 | 20 | In the text below, wrap-around in a contour means whether the point after the last point of the contour is considered to be the first point of the contour, and symmetrically, the point before the first point of the contour is considered to be the last point of the contour. When we say "without wrap-around", no such consideration is made. 21 | 22 | The number of consecutive cubic off-curve points within a contour, without wrap-around, _must_ be even. 23 | 24 | All the off-curve points between two on-curve points, with wrap-around, `must` either have the `CUBIC` flag clear, or have the `CUBIC` flag set. 25 | 26 | The `CUBIC` flag _must_ only be used on off-curve points. It is _reserved_ and _must_ be set to zero, for on-curve points. 27 | 28 | ## Processing 29 | 30 | Every successive two off-curve points that have the `CUBIC` bit set define a cubic Bezier segment. Within any consecutive set of cubic off-curve points within a contour, with wrap-around, an implied on-curve point is inserted at the mid-point between every second off-curve point and the next one. 31 | 32 | If there are no on-curve points and all (even number of) off-curve points are `CUBIC`, the first off-curve point is considered the first control-point of a cubic-Bezier, and implied on-curve points are inserted between the every second point and the next one as usual. 33 | 34 | As in the `glyf` version 0 table, an implied on-curve point is inserted between any two neighboring quadratic off-curve points. 35 | 36 | ## Hinting 37 | 38 | Hinting works exactly the same for quadratic-in-glyf, with points addressed by index number. 39 | -------------------------------------------------------------------------------- /glyf1-varComposites.md: -------------------------------------------------------------------------------- 1 | # `glyf`—Glyf Data Table Format 1: Variable Composites / Components 2 | 3 | > [!NOTE] 4 | > This document is now superceded by [VARC.md](VARC.md) 5 | 6 | The glyf data table ([`glyf`](https://docs.microsoft.com/en-us/typography/opentype/spec/glyf)) stores the glyph outline data. The format number for this table is stored in the [`head`](https://docs.microsoft.com/en-us/typography/opentype/spec/head) table's `glyphDataFormat` field. The only currently defined format of the `glyf` table is format 0. We propose format 1 of the `glyf` table to add multiple features. See full [proposal](glyf1.md) for all features. In this proposal we cover: _Variable Composites / Components_. 7 | 8 | In `glyf` table format 0, there exist two types of glyphs: Simple glyphs which have a `numberOfContours` of >= 0, and Composite glyphs that have a `numberOfContours` of < 0, with a recommended value of -1. 9 | 10 | In `glyf` table format 1, the old-style Simple and Composite glyphs exist as well. The old-style Composite glyphs shall always use a `numberOfContours` of -1. A new composite glyph type call VariableComposite will be introduced which uses `numberOfContours` of -2. A VariableComposite glyph has the ability to refer to _variable components_. 11 | 12 | 13 | ## Variable Composite Description 14 | 15 | A Variable Composite glyph starts with the standard glyph header with a `numberOfContours` of -2, followed by a number of Variable Component records. Each Variable Component record is at least five bytes long. 16 | 17 | 18 | ## Variable Component Record 19 | 20 | 21 | | type | name | notes | 22 | |-|-|-| 23 | | uint16 | flags | see below | 24 | | uint8 | numAxes | Number of axes to follow | 25 | | GlyphID16 or GlyphID24 | gid | This is a GlyphID16 if bit 12 of `flags` is clear, else GlyphID24 | 26 | | uint8 or uint16 | axisIndices[numAxes] | This is a uint16 if bit 1 of `flags` is set, else a uint8 | 27 | | F2DOT14 | axisValues[numAxes] | The axis value for each axis | 28 | | FWORD | TranslateX | Optional, only present if bit 3 of `flags` is set | 29 | | FWORD | TranslateY | Optional, only present if bit 4 of `flags` is set | 30 | | F4DOT12 | Rotation | Optional, only present if bit 5 of `flags` is set | 31 | | F6DOT10 | ScaleX | Optional, only present if bit 6 of `flags` is set | 32 | | F6DOT10 | ScaleY | Optional, only present if bit 7 of `flags` is set | 33 | | F4DOT12 | SkewX | Optional, only present if bit 8 of `flags` is set | 34 | | F4DOT12 | SkewY | Optional, only present if bit 9 of `flags` is set | 35 | | FWORD | TCenterX | Optional, only present if bit 10 of `flags` is set | 36 | | FWORD | TCenterY | Optional, only present if bit 11 of `flags` is set | 37 | 38 | 39 | ### Variable Component Transformation 40 | 41 | The transformation data consists of individual optional fields, which can be 42 | used to construct a transformation matrix. 43 | 44 | Transformation fields: 45 | 46 | | name | default value | 47 | |-|-| 48 | | TranslateX | 0 | 49 | | TranslateY | 0 | 50 | | Rotation | 0 | 51 | | ScaleX | 1 | 52 | | ScaleY | 1 | 53 | | SkewX | 0 | 54 | | SkewY | 0 | 55 | | TCenterX | 0 | 56 | | TCenterY | 0 | 57 | 58 | The `TCenterX` and `TCenterY` values represent the “center of transformation”. 59 | 60 | Details of how to build a transformation matrix, as pseudo-Python code: 61 | 62 | ```python 63 | # Using fontTools.misc.transform.Transform 64 | t = Transform() # Identity 65 | t = t.translate(TranslateX + TCenterX, TranslateY + TCenterY) 66 | t = t.rotate(Rotation * math.pi) 67 | t = t.scale(ScaleX, ScaleY) 68 | t = t.skew(-SkewX * math.pi, SkewY * math.pi) 69 | t = t.translate(-TCenterX, -TCenterY) 70 | ``` 71 | 72 | ### Variable Component Flags 73 | 74 | | bit number | meaning | 75 | |-|-| 76 | | 0 | Use my metrics | 77 | | 1 | axis indices are shorts (clear = bytes, set = shorts) | 78 | | 2 | if ScaleY is missing: take value from ScaleX | 79 | | 3 | have TranslateX | 80 | | 4 | have TranslateY | 81 | | 5 | have Rotation | 82 | | 6 | have ScaleX | 83 | | 7 | have ScaleY | 84 | | 8 | have SkewX | 85 | | 9 | have SkewY | 86 | | 10 | have TCenterX | 87 | | 11 | have TCenterY | 88 | | 12 | gid is 24 bit | 89 | | 13 | axis indices have variation | 90 | | 14 | reset unspecified axes | 91 | | 15 | (reserved, set to 0) | 92 | 93 | ## Processing 94 | 95 | Variations of composite glyphs are processed this way: For each composite glyph, a vector of coordinate points is prepared, and its variation applied from the `gvar` table. The coordinate points for the composite glyph are a concatenation of those for each variable component in order. For the purposes of `gvar` delta IUP calculations, each point is considered to be in its own contour. 96 | 97 | The coordinate points for each variable component consist of those for each axis value if flag bit 13 is set, represented as the X value of a coordinate point; followed by up to five points representing the transformation. The five possible points encode, in order, in their X,Y components, the following transformation components: (`TranslateX`,`TranslateY`), (`Rotation`,0), (`ScaleX`,`ScaleY`), (`SkewX`,`SkewY`), (`TCenterX`,`TCenterY`). Only the transformation components present according to the flag bits are encoded. For example, the point (`TranslateX`,`TranslateY`) is encoded if and only if either of flag 3 or 4 is set. If that point is encoded, the variations from it will be applied to the transformation components even if a specific component has its flag bit off. For example, if only flag 3 is set (`TranslateX`), any variations for `TranslateY` are still applied to the `TranslateY` of the transformation. 98 | 99 | The component glyphs to be loaded use the coordinate values specified (with any variations applied if present). For any unspecified axis, the value used depends on flag bit 14. If the flag is set, then the normalized value zero is used. If the flag is clear the axis values from current glyph being processed (which itself might recursively come from the font or its own parent glyphs) are used. For example, if the font variations have `wght`=.25 (normalized), and current glyph being processed is using `wght`=.5 because it was referenced from another VarComposite glyph itself, when referring to a component that does _not_ specify the `wght` axis, if flag bit 14 is set, then the value of `wght`=0 (default) will be used. If flag bit 14 is clear, `wght`=.5 (from current glyph) will be used. 100 | 101 | **Note:** While it is the (undocumented?) behavior of the `glyf` table that glyphs loaded are shifted to align their LSB to that specified in the `hmtx` table, much like regular Composite glyphs, this does not apply to component glyphs being loaded as part of a VariableComposite glyph. 102 | 103 | **Note:** A static (non-variable) font that uses VarComposite glyphs, _would not_ have `fvar` / `avar` tables but _would_ have the `gvar` table. This combination is possible because the `gvar` table encodes its own number-of-axes, and the axes in this proposal are specified in the normalized space. 104 | -------------------------------------------------------------------------------- /glyf1.md: -------------------------------------------------------------------------------- 1 | # `glyf`—Glyf Data Table Format 1 2 | 3 | The glyf data table ([`glyf`](https://docs.microsoft.com/en-us/typography/opentype/spec/glyf)) stores the glyph outline data. The format number for this table is stored in the [`head`](https://docs.microsoft.com/en-us/typography/opentype/spec/head) table's `glyphDataFormat` field. The only currently defined format of the `glyf` table is format 0. In this proposal we propose format 1 of the `glyf` table to add the following features: 4 | 5 | * **Cubic-Bezier Outlines:** [Proposal](glyf1-cubicOutlines.md). 6 | * **Variable Composites / Components:** [Proposal](glyf1-varComposites.md). 7 | 8 | **Note:** Format 1 `glyf` table is backwards-compatible with format 0, in that any format 0 `glyf` table is a valid format 1 table as well. 9 | -------------------------------------------------------------------------------- /iso_docs/2024-06-28-behdad-review.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/2024-06-28-behdad-review.odt -------------------------------------------------------------------------------- /iso_docs/2024-06-28-behdad-review.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/2024-06-28-behdad-review.pdf -------------------------------------------------------------------------------- /iso_docs/README.md: -------------------------------------------------------------------------------- 1 | # Documents submitted to ISO 2 | 3 | The documents in this folder were submitted to ISO for discussion. 4 | *they are not final specifications or even working drafts* 5 | 6 | The docx (Microsoft Word) files are authoritative; 7 | other formats are for convenience but were not submitted to ISO. 8 | 9 | ## 2024-12-18 10 | 11 | We noticed that large fonts caused overflow in the GlyphVariationData header, 12 | so dataOffset has to be changed from Offset16 to Offset24. 13 | 14 | This not been formally submitted to ISO at the time of writing, 15 | but will form a DIS ballot comment when the current draft is voted on. 16 | 17 | * [comment-2024-12-10.odt - LibreOffice file](comment-2024-12-10.odt) 18 | * [comment-2024-12-10.odt - PDF file](comment-2024-12-10.pdf) 19 | 20 | ## 2024-10-31 (October 2024) 21 | 22 | The draft has been in ballot for several months. Some changes were 23 | submitted at the start of that period, and in October some comments 24 | were submitted via national bodies in the UK, Canada, USA. The comments 25 | were sent in multiple batches because the different national bodies had 26 | different meeting dates and we weren’t quite ready in time for the first 27 | one. The comments document here contains all of the comments except 28 | one, which just said that we supported the changes. 29 | 30 | ### Comments 31 | 32 | A formal submission to the ISO MPEG meeting listed all of the comments 33 | on the Committee Draft, which was publicly available: 34 | 35 | * [m70320-kemer.odt - LibreOffice file](m70320-kemer.odt) 36 | * [m70320-kemer.docx - Microsoft Word file](m70320-kemer.docx) 37 | * [m70320-kemer.pdf - PDF file](m70320-kemer.pdf) 38 | 39 | ### Minor changes 40 | 41 | These were submitted directly to the editor; they relate to a 42 | working draft that may not itself be public, but are included here 43 | in the spirit of being open. 44 | 45 | * [changes2.odt - LibreOffice file](changes2.odt) 46 | * [changes.odt - LibreOffice file](changes.odt) 47 | * [changes.docx - Microsoft Word file](changes.docx) 48 | 49 | ## 2024-04-15 (April 2024) 50 | 51 | Three documents formally submitted to ISO for inclusion in OpenFontFormat, each in four formats: 52 | 53 | ### DMAP 54 | 55 | Delta mapping to reduce the file size of TrueType collections (TTC) 56 | 57 | * [WG03-dmap-2024-2024-04-15.docx - Microsoft Office file](WG03-dmap-2024-2024-04-15.docx) 58 | * [WG03-dmap-2024-2024-04-15.md - markdown generated by pandoc](WG03-dmap-2024-2024-04-15.md) 59 | * [WG03-dmap-2024-2024-04-15.odt - LibreOffice file](WG03-dmap-2024-2024-04-15.odt) 60 | * [WG03-dmap-2024-2024-04-15.pdf - PDF exported from LinreOffice](WG03-dmap-2024-2024-04-15.pdf) 61 | 62 | ### FVAR 63 | 64 | Describe what happens if multiple entries in the 'fvar' table use the same axis name, 65 | something in use today in practice but not explicitly described in the specification. 66 | 67 | * [WG03-fvar-2024-2024-04-15.docx - Microsoft Office file](WG03-fvar-2024-2024-04-15.docx) 68 | * [WG03-fvar-2024-2024-04-15.md - markdown generated by pandoc](WG03-fvar-2024-2024-04-15.md) 69 | * [WG03-fvar-2024-2024-04-15.odt - LibreOffice file](WG03-fvar-2024-2024-04-15.odt) 70 | * [WG03-fvar-2024-2024-04-15.pdf - PDF exported from LinreOffice](WG03-fvar-2024-2024-04-15.pdf) 71 | 72 | ### Var ious updates 73 | 74 | Updates primarily from public feedback 75 | 76 | * [WG03-varc-and-other-updates-2024-04-15.docx - Microsoft Office file](WG03-varc-and-other-updates-2024-04-15.docx) 77 | * [WG03-varc-and-other-updates-2024-04-15.md - markdown generated by pandoc](WG03-varc-and-other-updates-2024-04-15.md) 78 | * [WG03-varc-and-other-updates-2024-04-15.odt - LibreOffice file](WG03-varc-and-other-updates-2024-04-15.odt) 79 | * [WG03-varc-and-other-updates-2024-04-15.pdf - PDF exported from LinreOffice](WG03-varc-and-other-updates-2024-04-15.pdf) 80 | 81 | 82 | 83 | ## 2024-04 (April 2024) 84 | 85 | Informal drafts for ad-hoc discussion 86 | 87 | ### FVAR 88 | 89 | 90 | * [WG03-fvar-2024-04.pdf - PDF exported from LibreOffice](WG03-fvar-2024-04.pdf) 91 | * [WG03-fvar-2024-04.odt - LibreOffice file](WG03-fvar-2024-04.odt) 92 | * [WG03-fvar-2024-04.md - markdown generated by pandoc](WG03-fvar-2024-04.md) 93 | 94 | ## 2024-03 (March 2024) 95 | 96 | These documents update the 2024-02 ones; there are no docx (Word) files because 97 | the proposals have not yet been submitted to ISO, but only to an _ad-hoc_ public 98 | group for discussion. 99 | 100 | Note that the _Beyond-64k_ document was accepted, as was _avar2_, and they are 101 | now part of the ISO OpenFontFormat draft (WD9) 102 | 103 | ### VARC and other updates 104 | 105 | This incorporates public feedback on the earlier Beyond 64K proposal, which has now been adopted as part of the 106 | OpenFont Format Working Draft. VARC in particular is a highly compressed encoding for variable components (that is, 107 | for some of the glyphs in variable fomts that might be from other glyphs, 108 | but which might need transforming as axis values are changed). There is now implementation experience as well 109 | as feedback. 110 | 111 | * [WG03-varc-and-other-updates-03.pdf - PDF exported from LibreOffice](WG03-varc-and-other-updates-03.pdf) 112 | * [WG03-varc-and-other-updates-03.odt - LibreOffice file](WG03-varc-and-other-updates-03.odt) 113 | * [WG03-varc-and-other-updates-03.md - markdown generated by pandoc](WG03-varc-and-other-updates-03.md) 114 | 115 | ### FVAR 116 | 117 | This document describes what happens if multiple entries in the 'fvar' table use the same axis name, 118 | something in use today in practice but not explicitly described in the specification: 119 | 120 | * [WG03-fvar-2024-03.pdf - PDF exported from LibreOffice](WG03-fvar-2024-03.pdf) 121 | * [WG03-fvar-2024-03.odt - LibreOffice file](WG03-fvar-2024-03.odt) 122 | * [WG03-fvar-2024-03.md - markdown generated by pandoc](WG03-fvar-2024-03.md) 123 | 124 | 125 | ### DMAP 126 | 127 | Delta Maps would allow sub-fonts in a Collection (TTC) to share a single CMAP table, with a local override; 128 | this could reduce the size of font files significantly. 129 | 130 | * [WG03-dmap-2024-03.pdf - PDF exported from LibreOffice](WG03-dmap-2024-03.pdf) 131 | * [WG03-dmap-2024-03.odt - LibreOffice file](WG03-dmap-2024-03.odt) 132 | * [WG03-dmap-2024-03.md - markdown generated by pandoc](WG03-dmap-2024-03.md) 133 | 134 | 135 | ## 2024-02 (February 2024) 136 | 137 | These douments are drafts, not yet formal proposals to ISO: 138 | 139 | VARC and other updates incorporates public feedback on the earlier Beyond 64K proposal, which has now been adopted as part of the 140 | OpenFont Format Working Draft: 141 | * [WG03-varc-and-other-updates.pdf - PDF exported from LibreOffice](WG03-varc-and-other-updates.pdf) 142 | * [WG03-varc-and-other-updates.odt - LibreOffice file](WG03-varc-and-other-updates.odt) 143 | * [WG03-varc-and-other-updates.md - markdown generated by pandoc](WG03-varc-and-other-updates.md) 144 | 145 | DMAP was included in an earlier draft of the Beyond 64K proposal, and is now a separate document, to facilitate discussion: 146 | * [WG03-dmap-2024-01.pdf - PDF exported from LibreOffice](WG03-dmap-2024-01.pdf) 147 | * [WG03-dmap-2024-01.odt - LibreOffice file](WG03-dmap-2024-01.odt) 148 | * [WG03-dmap-2024-01.md - markdown generated by pandoc](WG03-dmap-2024-01.md) 149 | 150 | ## 2024-01 (January 2024) 151 | 152 | After the ad-hoc call (see next paragraph), a new version was prepared 153 | for submission to the ISO MPEG OFF committee. 154 | It lacks some of the introductory text, had a DMAP proposal removed, and some offsets fixed. 155 | In VARC, CFF2Index has been renamed to (CFF2-style) INDEX. 156 | The documents were updated on 2024-01-23 to reflect feedback during the ISO committee meeting. 157 | 158 | The “e” versions are revised based on feedback: 159 | * [WG03-beyond-64k-glyphs-2024-01e.docx - Microsoft Office file](WG03-beyond-64k-glyphs-2024-01e.docx) 160 | * [WG03-beyond-64k-glyphs-2024-01e.odt - LibreOffice file](WG03-beyond-64k-glyphs-2024-01e.odt) 161 | * [WG03-beyond-64k-glyphs-2024-01e.pdf - PDF exported from LibreOffice](WG03-beyond-64k-glyphs-2024-01e.pdf) 162 | * [WG03-beyond-64k-glyphs-2024-01e.md - markdown generated by pandoc](WG03-beyond-64k-glyphs-2024-01e.md) 163 | 164 | The “c”/“d” versions were sent to the ISO committee for consideration: 165 | * [WG03-beyond-64k-glyphs-2024-01d.docx - Microsoft Office file](WG03-beyond-64k-glyphs-2024-01d.docx) 166 | * [WG03-beyond-64k-glyphs-2024-01d.odt - LibreOffice file](WG03-beyond-64k-glyphs-2024-01d.odt) 167 | * [WG03-beyond-64k-glyphs-2024-01d.pdf - PDF exported from LibreOffice](WG03-beyond-64k-glyphs-2024-01d.pdf) 168 | * [WG03-beyond-64k-glyphs-2024-01d.md - markdown generated by pandoc](WG03-beyond-64k-glyphs-2024-01d.md) 169 | 170 | During an ad-hoc committee telephone in January 2024, some changes were 171 | requested. The primary substantive changes were to remove the DMAP propsal 172 | (it will become a separate document), and to extend BASE to allow a reference 173 | glyph for baselines with a 24-bit Glyph ID. Editorial changes, including moving 174 | definitions of type and adding some notes, are also included: 175 | * [WG03-beyond-64k-glyphs-2024-01b.odt - LibreOffice file](WG03-beyond-64k-glyphs-2024-01b.odt) 176 | * [WG03-beyond-64k-glyphs-2024-01b.pdf - PDF exported from LibreOffice](WG03-beyond-64k-glyphs-2024-01b.pdf) 177 | * [WG03-beyond-64k-glyphs-2024-01b.md - markdown generated by pandoc](WG03-beyond-64k-glyphs-2024-01b.md) 178 | 179 | 180 | After an ad-hoc committee teleconference in December, and much email and github 181 | issue discussion, these documents incorporate DMAP, a separate VARC, 182 | a new TTC header, and more changes, all of which are listed at the beginning. 183 | 184 | * [WG03-beyond-64k-glyphs-2024-01.odt - LibreOffice file](WG03-beyond-64k-glyphs-2024-01.odt) 185 | * [WG03-beyond-64k-glyphs-2024-01.pdf - PDF exported from LibreOffice](WG03-beyond-64k-glyphs-2024-01.pdf) 186 | * [WG03-beyond-64k-glyphs-2024-01.md - markdown generated by pandoc](WG03-beyond-64k-glyphs-2024-01.md) 187 | 188 | 189 | ## 2023-11 (November 2023) 190 | 191 | The proposal from September, revised after an informal _ad hoc_ committee meeting by telephone in Octoner 2023. 192 | The main changes from the September draft are: 193 | * Text for the main new or changed tables such as GLYF is copied from the existing text (such as for glyf) and updated; this adds substantially to the number of pages. Updated text is highlighted in yellow. 194 | * The headers for GSUB, GPOS and GDEF are now minor revisions to the pre-existing headers, not entirely new versions, and are made compatible. 195 | * Some more tables, notably sbix, have been handled. 196 | * Sub-tables and new fields now have names like _dancingPaul2_ instead of _dancingPaul32_. 197 | 198 | The files are here: 199 | * [WG03-beyond-64k-glyphs-2023-11-20.odt - LibreOffice file](WG03-beyond-64k-glyphs-2023-11-20.odt) 200 | * [WG03-beyond-64k-glyphs-2023-11-20.pdf - PDF exported from LibreOffice](WG03-beyond-64k-glyphs-2023-11-20.pdf) 201 | * [WG03-beyond-64k-glyphs-2023-11-20.md - markdown generated by pandoc](WG03-beyond-64k-glyphs-2023-11-20.md) 202 | 203 | 204 | ## 2023-09 (September 2023) 205 | 206 | A proposal, 207 | agreed upon informally by an _ad hoc_ committee meeting, 208 | in Portland and online, 209 | in August 2023, to use a new GLYF table to support: 210 | * Variable Glyph Components 211 | * Mixed Cubic and Quadratic Bézier curves in glyph outline paths 212 | * More than 65535 glyphs in a single font 213 | 214 | The document has been submitted for discussion: 215 | 216 | * [WG03-beyond-64k-glyphs-proposal.odt - LibreOffice file](WG03-beyond-64k-glyphs-proposal.odt) 217 | * [WG03-beyond-64k-glyphs-proposal.docx - Microsoft Word file](WG03-beyond-64k-glyphs-proposal.docx) 218 | * [WG03-beyond-64k-glyphs-proposal.pdf - PDF exported from LibreOffice](WG03-beyond-64k-glyphs-proposal.pdf) 219 | * [WG03-beyond-64k-glyphs-proposal.md - markdown generated by pandoc](WG03-beyond-64k-glyphs-proposal.md) 220 | 221 | 222 | 223 | 224 | ## 2023-08 (August 2023) 225 | 226 | The AVAR 2 proposal was accepted by the ISO Working Group, and is now part of 227 | the OpenFont Working Draft. The text here is taken from that Working Draft 228 | (Draft 7): 229 | 230 | * [m64287-avar2-combined-wd7.odt - LibreOffice file](m64287-avar2-combined-wd7.odt) 231 | * [m64287-avar2-combined-wd7.pdf - PDF exported from LibreOffice](m64287-avar2-combined-wd7.pdf) 232 | 233 | ## 2023-07 (July 2023) 234 | 235 | m64287 combined, _Updating AVAR table in OFF to support the needs of modern computing platforms._ 236 | 237 | This is a later version of m64287, and includes the full text, not just the changes. Note that section 238 | numbers relate to the current working draft of _Open Font Format_ at the time this document was 239 | submitted to the MPEG committee. 240 | 241 | * [m64287-combined.docx - saved from LibreOffice, sent to ISO](m64287-combined.docx) 242 | * [m64287-combined.odt - original LibreOffice file](m64287-combined.odt) 243 | * [m64287-combined.pdf - PDF exported from LibreOffice](m64287-combined.pdf) 244 | 245 | 246 | m64287, _Updating AVAR table in OFF to support the needs of modern computing platforms._ 247 | 248 | This propsoal supersedes the AVAR 2 section in m62937. 249 | 250 | * [m64287-AVAR_OFF_updates.docx - saved from LibreOffice, sent to ISO](m64287-AVAR_OFF_updates.docx) 251 | * [m64287-AVAR_OFF_updates.odt - original LibreOffice file](m64287-AVAR_OFF_updates.odt) 252 | * [m64287-AVAR_OFF_updates.pdf - PDF exported from LibreOffice](m64287-AVAR_OFF_updates.pdf) 253 | * [markdown generated by pandoc](m64287-AVAR_OFF_updates.md) 254 | 255 | ## 2023-04 (April 2023) 256 | 257 | m62937, _Proposed improvements and new functionality for the Open Font Format standard for global font support._ 258 | 259 | The last section of this document is superseded by m64287; see next item. 260 | 261 | * [WG03_otf-improvements.docx](./WG03_otf-improvements.docx) 262 | * [Same in PDF](./WG03_otf-improvements.pdf) 263 | 264 | -------------------------------------------------------------------------------- /iso_docs/WG03-beyond-64k-glyphs-2023-11-20.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-beyond-64k-glyphs-2023-11-20.odt -------------------------------------------------------------------------------- /iso_docs/WG03-beyond-64k-glyphs-2023-11-20.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-beyond-64k-glyphs-2023-11-20.pdf -------------------------------------------------------------------------------- /iso_docs/WG03-beyond-64k-glyphs-2024-01.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-beyond-64k-glyphs-2024-01.odt -------------------------------------------------------------------------------- /iso_docs/WG03-beyond-64k-glyphs-2024-01.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-beyond-64k-glyphs-2024-01.pdf -------------------------------------------------------------------------------- /iso_docs/WG03-beyond-64k-glyphs-2024-01b.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-beyond-64k-glyphs-2024-01b.odt -------------------------------------------------------------------------------- /iso_docs/WG03-beyond-64k-glyphs-2024-01b.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-beyond-64k-glyphs-2024-01b.pdf -------------------------------------------------------------------------------- /iso_docs/WG03-beyond-64k-glyphs-2024-01d.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-beyond-64k-glyphs-2024-01d.docx -------------------------------------------------------------------------------- /iso_docs/WG03-beyond-64k-glyphs-2024-01d.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-beyond-64k-glyphs-2024-01d.odt -------------------------------------------------------------------------------- /iso_docs/WG03-beyond-64k-glyphs-2024-01d.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-beyond-64k-glyphs-2024-01d.pdf -------------------------------------------------------------------------------- /iso_docs/WG03-beyond-64k-glyphs-2024-01e.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-beyond-64k-glyphs-2024-01e.docx -------------------------------------------------------------------------------- /iso_docs/WG03-beyond-64k-glyphs-2024-01e.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-beyond-64k-glyphs-2024-01e.odt -------------------------------------------------------------------------------- /iso_docs/WG03-beyond-64k-glyphs-2024-01e.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-beyond-64k-glyphs-2024-01e.pdf -------------------------------------------------------------------------------- /iso_docs/WG03-beyond-64k-glyphs-proposal.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-beyond-64k-glyphs-proposal.docx -------------------------------------------------------------------------------- /iso_docs/WG03-beyond-64k-glyphs-proposal.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-beyond-64k-glyphs-proposal.odt -------------------------------------------------------------------------------- /iso_docs/WG03-beyond-64k-glyphs-proposal.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-beyond-64k-glyphs-proposal.pdf -------------------------------------------------------------------------------- /iso_docs/WG03-dmap-2024-01.md: -------------------------------------------------------------------------------- 1 | **INTERNATIONAL ORGANISATION FOR STANDARDISATION** 2 | 3 | **ORGANISATION INTERNATIONALE DE NORMALISATION** 4 | 5 | **ISO/IEC JTC 1/SC 29/WG 3** 6 | 7 | **CODING OF MOVING PICTURES AND AUDIO** 8 | 9 | **ISO/IEC JTC 1/SC 29/WG 3 m** **yyyy** 10 | 11 | **Milford, Ontario, Canada – February 2024** 12 | 13 | **Title: Increasing file-size and bandwidth efficiency, and improving 14 | internationalization support, of OpenFont files using delta tables 15 | (DMAP)** 16 | 17 | **Author: Peter Constable (Microsoft, Inc., pconstable@microsoft.com), 18 | Dave Crossland (Google Inc., dcrossland@google.com), Behdad Esfahbod 19 | (behdad@behdad.org), Laurence Penney (lorp@lorp.org), Liam Quin 20 | (Delightful Computing, liam@delightfulcomputing.com), Rod Sheeter 21 | (Google Inc., rsheeter@google.com)** 22 | 23 | # Introduction 24 | 25 | (This introduction is not part of the proposal) 26 | 27 | The purpose of a DMAP table in a Collection is to avoid duplication of 28 | the cmap table, which for a CJK font can be large. It is a per-subfont 29 | table that is consulted *before* cmap, overriding cmap. 30 | 31 | The ‘DMAP’ table was previously proposed by Ned Holbrook, Ken Lunds, 32 | Dwayne Robinson, and Peter Constable. 33 | 34 | DMAP—Delta map 35 | table \[5.6.13\] \[add at end of 5.6 optional 36 | tables section, before 6 Advanced Open Font layout tables\] 37 | 38 | An optional ‘DMAP’ table, if present, shall take priority over 39 | ‘cmap’—that is, the character-to-glyph lookup shall first look in the 40 | ‘DMAP’ table (including any variation-selectors). If a match is not 41 | found, ‘cmap’ shall be consulted. The ‘DMAP’ table is identical in 42 | structure to the ‘cmap’ table. 43 | 44 | This allows individual members of a TTC collection to override or supply 45 | parts of a shared character map, saving space in the font. 46 | 47 | DMAP Table 48 | 49 | | | | | 50 | |--------|---------------------|------------------------------------------------------------------------------------| 51 | | Type | Name | Description | 52 | | uint16 | version | Set to zero | 53 | | uint16 | NumTables | Number off offset fields to follow | 54 | | uint32 | offset\[numTables\] | Array of offsets to cmap subtables, measured from the beginning of the DMAP table. | 55 | 56 | The DMAP subtables are in the same formats as CMAP subtables. 57 | 58 | The *language* field for a format 4, 12, or 13 DMAP subtable shall be 59 | set to zero. 60 | 61 | > Note: Any subtables in a DMAP table, 62 | > including Format 14 and Format 15, shall supersede any such subtables 63 | > in the corresponding ‘cmap’ table in entirety. 64 | 65 | Near the end of 66 | **The 67 | Font Collection file structure** \[4.6.2\] we add a paragraph: 68 | 69 | ### **The Font Collection file structure** \[4.6.2\] 70 | 71 | A font collection file consists of a single TTC header table, one or 72 | more table directories (each corresponding to a different font 73 | resource), and a number of OFF tables. The TTC header shall be located 74 | at the beginning of the TTC file. 75 | 76 | The TTC file shall contain a complete table directory for each font 77 | resource. The same TableDirectory format is used for each font resource 78 | in a collection file as is used in a non-collection file. The table 79 | offsets in all table directories within a TTC file are measured from the 80 | beginning of the TTC file. 81 | 82 | Each OFF table in a TTC file is referenced through the table directory 83 | of each font which uses that table. Some of the OFF tables must appear 84 | multiple times, once for each font included in the TTC; while other 85 | tables may be shared by multiple fonts in the TTC. 86 | 87 | As an example, consider a TTC file which combines two Japanese fonts 88 | (Font1 and Font2). The fonts have different kana designs (Kana1 and 89 | Kana2) but use the same design for kanji. The TTC file contains a single 90 | 'glyf' table which includes both designs of kana together with the 91 | kanji; both fonts' table directories point to this 'glyf' table. But 92 | each font's table directory points to a different 'cmap' table, which 93 | identifies the glyph set to use. Font1's 'cmap' table points to the 94 | Kana1 region of the 'loca' and 'glyf' tables for kana glyphs, and to the 95 | kanji region for the kanji. Font2's 'cmap' table points to the Kana2 96 | region of the 'loca' and 'glyf' tables for kana glyphs, and to the same 97 | kanji region for the kanji. 98 | 99 | The tables that should have a unique copy per font are those that are 100 | used by the system in identifying the font and its character mapping, 101 | including 'cmap', 'name', and 'OS/2'. The tables that should be shared 102 | by fonts in the TTC are those that define glyph and instruction data or 103 | use glyph indices to access data: 'glyf', 'loca', 'hmtx', 'hdmx', 104 | 'LTSH', 'cvt ', 'fpgm', 'prep', 'EBLC', 'EBDT', 'EBSC', 'maxp', and so 105 | on. In practice, any tables which have identical data for two or more 106 | fonts may be shared. 107 | 108 | Each font in the TTC may contain its own DMAP table. In addition, a font 109 | using CMAP may support characters and GlyphIDs also defined by another 110 | font; in the case of overlapping coverage the order of precedence is 111 | first DMAP, then CMAP, and then finally cmap. 112 | 113 | > NOTE When building a collection file from separate font files, close 114 | > attention needs to be paid to the issue of glyph renumbering in a font 115 | > and the side effects that can result in the 'cmap' table and 116 | > elsewhere. The fonts to be merged also need to have compatible 117 | > TrueType instructions; that is, their preprograms, function 118 | > definitions, and control values cannot conflict. 119 | 120 | Collection files containing TrueType glyph outlines should use the 121 | filename suffix .TTC. Collection files containing CFF or CFF2 outlines 122 | should use the file extension .OTC. 123 | 124 | In CMAP just before the cmap header (5.1.1.2) 125 | 126 | An optional ‘DMAP’ table, if present, shall 127 | take priority over ‘cmap’—that is, the character-to-glyph lookup shall 128 | first look in the ‘DMAP’ table (including any variation-selectors). If a 129 | match is not found, ‘cmap’ is to be consulted. The ‘DMAP’ table is 130 | identical in structure to the ‘cmap’ table. 131 | 132 | 133 | 134 | \[end\] 135 | -------------------------------------------------------------------------------- /iso_docs/WG03-dmap-2024-01.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-dmap-2024-01.odt -------------------------------------------------------------------------------- /iso_docs/WG03-dmap-2024-01.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-dmap-2024-01.pdf -------------------------------------------------------------------------------- /iso_docs/WG03-dmap-2024-03.md: -------------------------------------------------------------------------------- 1 | **INTERNATIONAL ORGANISATION FOR STANDARDISATION** 2 | 3 | **ORGANISATION INTERNATIONALE DE NORMALISATION** 4 | 5 | **ISO/IEC JTC 1/SC 29/WG 3** 6 | 7 | **CODING OF MOVING PICTURES AND AUDIO** 8 | 9 | **ISO/IEC JTC 1/SC 29/WG 3 m** **yyyy** 10 | 11 | **Milford, Ontario, Canada – March 2024** 12 | 13 | **Title: Increasing file-size and bandwidth efficiency, and improving 14 | internationalization support, of OpenFont files using delta tables 15 | (DMAP)** 16 | 17 | **Author: Peter Constable (Microsoft, Inc., pconstable@microsoft.com), 18 | Dave Crossland (Google Inc., dcrossland@google.com), Behdad Esfahbod 19 | (behdad@behdad.org), Laurence Penney (lorp@lorp.org), Liam Quin 20 | (Delightful Computing, liam@delightfulcomputing.com), Rod Sheeter 21 | (Google Inc., rsheeter@google.com)** 22 | 23 | # Introduction 24 | 25 | (This introduction is not part of the proposal) 26 | 27 | The purpose of a DMAP table in a Collection is to avoid duplication of 28 | the cmap table, which for a CJK font can be large. It is a per-subfont 29 | table that is consulted *before* cmap, overriding cmap. 30 | 31 | The ‘DMAP’ table was previously proposed by Ned Holbrook, Ken Lunde, 32 | Dwayne Robinson, and Peter Constable. 33 | 34 | DMAP—Delta map 35 | table \[5.6.15\] \[add at end of optional 36 | tables section, before 6 Advanced Open Font layout tables\] 37 | 38 | An optional ‘DMAP’ table, if present, shall take priority over 39 | ‘cmap’—that is, the character-to-glyph lookup shall first look in the 40 | ‘DMAP’ table (including any variation-selectors). If a match is not 41 | found, ‘cmap’ shall be consulted. The ‘DMAP’ table is identical in 42 | structure to the ‘cmap’ table. 43 | 44 | This allows individual members of a TTC collection to override or supply 45 | parts of a shared character map, saving space in the font. 46 | 47 | ‘DMAP’ Header 48 | 49 | | | | | 50 | |----------------|------------------------------|--------------------------------------------------------------------------------| 51 | | Type | Name | Description | 52 | | uint16 | version | Set to zero | 53 | | uint16 | NumTables | Number of encoding tables that follow | 54 | | EncodingRecord | encodingRecords\[numTables\] | Array of cmap-format subtables, measured from the beginning of the DMAP table. | 55 | 56 | The DMAP subtables are in the same formats as CMAP subtables. 57 | 58 | The *language* field for a format 4, 12, or 13 DMAP subtable shall be 59 | set to zero. 60 | 61 | Any subtables in a DMAP table, including 62 | Format 14 and Format 15, shall supersede any such subtables in the 63 | corresponding ‘cmap’ table in entirety: all character processing, 64 | including for Format 14 lookups, shall first consult DMAP. 65 | 66 | Near the end of 67 | **The 68 | Font Collection file structure** \[4.6.2\] we add a paragraph: 69 | 70 | ### **The Font Collection file structure** \[4.6.2\] 71 | 72 | A font collection file consists of a single TTC header table, one or 73 | more table directories (each corresponding to a different font 74 | resource), and a number of OFF tables. The TTC header shall be located 75 | at the beginning of the TTC file. 76 | 77 | The TTC file shall contain a complete table directory for each font 78 | resource. The same TableDirectory format is used for each font resource 79 | in a collection file as is used in a non-collection file. The table 80 | offsets in all table directories within a TTC file are measured from the 81 | beginning of the TTC file. 82 | 83 | Each OFF table in a TTC file is referenced through the table directory 84 | of each font which uses that table. Some of the OFF tables must appear 85 | multiple times, once for each font included in the TTC; while other 86 | tables may be shared by multiple fonts in the TTC. 87 | 88 | As an example, consider a TTC file which combines two Japanese fonts 89 | (Font1 and Font2). The fonts have different kana designs (Kana1 and 90 | Kana2) but use the same design for kanji. The TTC file contains a single 91 | 'glyf' table which includes both designs of kana together with the 92 | kanji; both fonts' table directories point to this 'glyf' table. But 93 | each font's table directory points to a different 'cmap' table, which 94 | identifies the glyph set to use. Font1's 'cmap' table points to the 95 | Kana1 region of the 'loca' and 'glyf' tables for kana glyphs, and to the 96 | kanji region for the kanji. Font2's 'cmap' table points to the Kana2 97 | region of the 'loca' and 'glyf' tables for kana glyphs, and to the same 98 | kanji region for the kanji. 99 | 100 | The tables that should have a unique copy per font are those that are 101 | used by the system in identifying the font and its character mapping, 102 | including 'cmap', 'name', and 'OS/2'. The tables that should be shared 103 | by fonts in the TTC are those that define glyph and instruction data or 104 | use glyph indices to access data: 'glyf', 'loca', 'hmtx', 'hdmx', 105 | 'LTSH', 'cvt ', 'fpgm', 'prep', 'EBLC', 'EBDT', 'EBSC', 'maxp', and so 106 | on. In practice, any tables which have identical data for two or more 107 | fonts may be shared. 108 | 109 | Each font in the TTC may contain its own DMAP table. In addition, a font 110 | using CMAP may support characters and GlyphIDs also defined by another 111 | font; in the case of overlapping coverage the order of precedence is 112 | first DMAP, then CMAP, and then finally cmap. 113 | 114 | > NOTE When building a collection file from separate font files, close 115 | > attention needs to be paid to the issue of glyph renumbering in a font 116 | > and the side effects that can result in the 'cmap' table and 117 | > elsewhere. The fonts to be merged also need to have compatible 118 | > TrueType instructions; that is, their preprograms, function 119 | > definitions, and control values cannot conflict. 120 | 121 | Collection files containing TrueType glyph outlines should use the 122 | filename suffix .TTC. Collection files containing CFF or CFF2 outlines 123 | should use the file extension .OTC. 124 | 125 | In CMAP just before the cmap header (5.1.2.1) 126 | 127 | An optional ‘DMAP’ table, if present, shall 128 | take priority over ‘cmap’—that is, the character-to-glyph lookup shall 129 | first look in the ‘DMAP’ table (including any variation-selectors). If a 130 | match is not found, ‘cmap’ is to be consulted. The ‘DMAP’ table is 131 | identical in structure to the ‘cmap’ table. 132 | 133 | 134 | 135 | \[end\] 136 | -------------------------------------------------------------------------------- /iso_docs/WG03-dmap-2024-03.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-dmap-2024-03.odt -------------------------------------------------------------------------------- /iso_docs/WG03-dmap-2024-03.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-dmap-2024-03.pdf -------------------------------------------------------------------------------- /iso_docs/WG03-dmap-2024-2024-04-15.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-dmap-2024-2024-04-15.docx -------------------------------------------------------------------------------- /iso_docs/WG03-dmap-2024-2024-04-15.md: -------------------------------------------------------------------------------- 1 | **INTERNATIONAL ORGANISATION FOR STANDARDISATION** 2 | 3 | **ORGANISATION INTERNATIONALE DE NORMALISATION** 4 | 5 | **ISO/IEC JTC 1/SC 29/WG 3** 6 | 7 | **CODING OF MOVING PICTURES AND AUDIO** 8 | 9 | **ISO/IEC JTC 1/SC 29/WG 3 m** **67466** 10 | 11 | **Rennes, France – April 2024** 12 | 13 | **Title: Improving efficiency and reducing sizes of OFF files using 14 | delta mapping tables (DMAP)** 15 | 16 | **Author: Peter Constable (Microsoft, Inc., pconstable@microsoft.com), 17 | Dave Crossland (Google Inc., dcrossland@google.com), Behdad Esfahbod 18 | (behdad@behdad.org), Laurence Penney (lorp@lorp.org), Liam Quin 19 | (Delightful Computing, liam@delightfulcomputing.com), Rod Sheeter 20 | (Google Inc., rsheeter@google.com)** 21 | 22 | # Introduction 23 | 24 | (This introduction is not part of the proposal) 25 | 26 | The purpose of a DMAP table in a Collection is to avoid duplication of 27 | the cmap table, which for a CJK font can be large. It is a per-subfont 28 | table that is consulted *before* cmap, overriding cmap. 29 | 30 | The ‘DMAP’ table was previously proposed by Ned Holbrook, Ken Lunde, 31 | Dwayne Robinson, and Peter Constable. 32 | 33 | DMAP—Delta map 34 | table \[5.6.15\] \[add at end of optional 35 | tables section, before 6 Advanced Open Font layout tables\] 36 | 37 | An optional ‘DMAP’ table, if present, shall take priority over 38 | ‘cmap’—that is, the character-to-glyph lookup shall first look in the 39 | ‘DMAP’ table (including any variation-selectors). If a match is not 40 | found, ‘cmap’ shall be consulted. The ‘DMAP’ table is identical in 41 | structure to the ‘cmap’ table. 42 | 43 | This allows individual members of a TTC collection to override or supply 44 | parts of a shared character map, saving space in the font. 45 | 46 | ‘DMAP’ Header 47 | 48 | | | | | 49 | |----------------|------------------------------|--------------------------------------------------------------------------------| 50 | | Type | Name | Description | 51 | | uint16 | version | Set to zero | 52 | | uint16 | NumTables | Number of encoding tables that follow | 53 | | EncodingRecord | encodingRecords\[numTables\] | Array of cmap-format subtables, measured from the beginning of the DMAP table. | 54 | 55 | The DMAP subtables are in the same formats as cmap subtables. 56 | 57 | The *language* field for a format 4, 12, or 13 DMAP subtable shall be 58 | set to zero. 59 | 60 | Any subtables in a DMAP table, including 61 | Format 14 and Format 15, shall supersede any such subtables in the 62 | corresponding ‘cmap’ table in entirety: all character processing, 63 | including for Format 14 lookups, shall first consult DMAP. 64 | 65 | Near the end of 66 | **The 67 | Font Collection file structure** \[4.6.2\] we add a paragraph: 68 | 69 | ### **The Font Collection file structure** \[4.6.2\] 70 | 71 | A font collection file consists of a single TTC header table, one or 72 | more table directories (each corresponding to a different font 73 | resource), and a number of OFF tables. The TTC header shall be located 74 | at the beginning of the TTC file. 75 | 76 | The TTC file shall contain a complete table directory for each font 77 | resource. The same TableDirectory format is used for each font resource 78 | in a collection file as is used in a non-collection file. The table 79 | offsets in all table directories within a TTC file are measured from the 80 | beginning of the TTC file. 81 | 82 | Each OFF table in a TTC file is referenced through the table directory 83 | of each font which uses that table. Some of the OFF tables must appear 84 | multiple times, once for each font included in the TTC; while other 85 | tables may be shared by multiple fonts in the TTC. 86 | 87 | As an example, consider a TTC file which combines two Japanese fonts 88 | (Font1 and Font2). The fonts have different kana designs (Kana1 and 89 | Kana2) but use the same design for kanji. The TTC file contains a single 90 | 'glyf' table which includes both designs of kana together with the 91 | kanji; both fonts' table directories point to this 'glyf' table. But 92 | each font's table directory points to a different 'cmap' table, which 93 | identifies the glyph set to use. Font1's 'cmap' table points to the 94 | Kana1 region of the 'loca' and 'glyf' tables for kana glyphs, and to the 95 | kanji region for the kanji. Font2's 'cmap' table points to the Kana2 96 | region of the 'loca' and 'glyf' tables for kana glyphs, and to the same 97 | kanji region for the kanji. 98 | 99 | The tables that should have a unique copy per font are those that are 100 | used by the system in identifying the font and its character mapping, 101 | including 'cmap', 'name', and 'OS/2'. The tables that should be shared 102 | by fonts in the TTC are those that define glyph and instruction data or 103 | use glyph indices to access data: 'glyf', 'loca', 'hmtx', 'hdmx', 104 | 'LTSH', 'cvt ', 'fpgm', 'prep', 'EBLC', 'EBDT', 'EBSC', 'maxp', and so 105 | on. In practice, any tables which have identical data for two or more 106 | fonts may be shared. 107 | 108 | Each font in the TTC may contain its own DMAP table. In addition, a font 109 | using CMAP may support characters and GlyphIDs also defined by another 110 | font; in the case of overlapping coverage the order of precedence is 111 | first DMAP, then cmap. 112 | 113 | > NOTE When building a collection file from separate font files, close 114 | > attention needs to be paid to the issue of glyph renumbering in a font 115 | > and the side effects that can result in the 'cmap' table and 116 | > elsewhere. The fonts to be merged also need to have compatible 117 | > TrueType instructions; that is, their preprograms, function 118 | > definitions, and control values cannot conflict. 119 | 120 | Collection files containing TrueType glyph outlines should use the 121 | filename suffix .TTC. Collection files containing CFF or CFF2 outlines 122 | should use the file extension .OTC. 123 | 124 | In CMAP just before the cmap header (5.1.2.1) 125 | 126 | An optional ‘DMAP’ table, if present, shall 127 | take priority over ‘cmap’—that is, the character-to-glyph lookup shall 128 | first look in the ‘DMAP’ table (including any variation-selectors). If a 129 | match is not found, ‘cmap’ is to be consulted. The ‘DMAP’ table is 130 | identical in structure to the ‘cmap’ table. 131 | 132 | 133 | 134 | \[end\] 135 | -------------------------------------------------------------------------------- /iso_docs/WG03-dmap-2024-2024-04-15.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-dmap-2024-2024-04-15.odt -------------------------------------------------------------------------------- /iso_docs/WG03-dmap-2024-2024-04-15.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-dmap-2024-2024-04-15.pdf -------------------------------------------------------------------------------- /iso_docs/WG03-fvar-2024-03.md: -------------------------------------------------------------------------------- 1 | **INTERNATIONAL ORGANISATION FOR STANDARDISATION** 2 | 3 | **ORGANISATION INTERNATIONALE DE NORMALISATION** 4 | 5 | **ISO/IEC JTC 1/SC 29/WG 3** 6 | 7 | **CODING OF MOVING PICTURES AND AUDIO** 8 | 9 | **ISO/IEC JTC 1/SC 29/WG 3 m** **yyyy** 10 | 11 | **Milford, Ontario, Canada – March 2024** 12 | 13 | **Title: ****Improving interoperability of 14 | OpenFontFormat files (fonts) in the area of duplicated axis names** 15 | 16 | **Author: Dave Crossland (Google Inc., dcrossland@google.com), Behdad 17 | Esfahbod (behdad@behdad.org), Laurence Penney (lorp@lorp.org), Liam Quin 18 | (Delightful Computing, liam@delightfulcomputing.com), Rod Sheeter 19 | (Google Inc., rsheeter@google.com)** 20 | 21 | # Introduction 22 | 23 | (This introduction is not part of the proposal) 24 | 25 | This proposal does not introduce any new 26 | features. Rather, it provides documentation for what should happen in 27 | what might previously have been thought to be a rare edge case, so that 28 | different implementations can interoperate correctly. 29 | 30 | Currently, a variable font can have multiple entries in ‘fvar’ for the 31 | same axis. The specification does not disallow this. However, if more 32 | than one such multiple entry is visible to the user interface, the 33 | result is unpredictable and may be confusing for users. 34 | 35 | Therefore, we explicitly state that only one duplicated axis entry may 36 | be visible, and require, for interoperability with existing tools, that 37 | it be the first. 38 | 39 | Fonts with multiple entries for the same axis are in use on the Web 40 | today, including as part of a technique referred to as Higher Order 41 | Interpolation (HOI). It is not a new feature, but the current 42 | specification is silent about it. 43 | 44 | 45 | 46 | 47 | 48 | In 7.3.3 fvar—Font variations table, after 49 | VariationAxisRecord, after the paragraph about the HIDDEN_AXIS tag, add 50 | a new final paragraph as follows: 51 | 52 | For smooth animation, and for non-linear 53 | interpolation, it may be necessary for a font to use multiple axes with 54 | the same axisTag. 55 | 56 | If a font contains more than one axis with the same axisTag, at most one 57 | of those axes shall be visible (i.e. have the HIDDEN_AXIS bit set to 58 | zero). The VariationAxisRecord for such a visible axis in this case 59 | shall appear first, before the records for any of the other axes with 60 | that same axisTag, all of which shall have their HIDDEN_AXIS flag set to 61 | 1. 62 | 63 | \[end\] 64 | -------------------------------------------------------------------------------- /iso_docs/WG03-fvar-2024-03.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-fvar-2024-03.odt -------------------------------------------------------------------------------- /iso_docs/WG03-fvar-2024-03.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-fvar-2024-03.pdf -------------------------------------------------------------------------------- /iso_docs/WG03-fvar-2024-04.md: -------------------------------------------------------------------------------- 1 | **INTERNATIONAL ORGANISATION FOR STANDARDISATION** 2 | 3 | **ORGANISATION INTERNATIONALE DE NORMALISATION** 4 | 5 | **ISO/IEC JTC 1/SC 29/WG 3** 6 | 7 | **CODING OF MOVING PICTURES AND AUDIO** 8 | 9 | **ISO/IEC JTC 1/SC 29/WG 3 m** **yyyy** 10 | 11 | **Milford, Ontario, Canada – April 2024** 12 | 13 | **Title: ****Improving interoperability of 14 | OpenFontFormat files (fonts) in the area of duplicated axis names** 15 | 16 | **Author: Dave Crossland (Google Inc., dcrossland@google.com), Behdad 17 | Esfahbod (behdad@behdad.org), Laurence Penney (lorp@lorp.org), Liam Quin 18 | (Delightful Computing, liam@delightfulcomputing.com), Rod Sheeter 19 | (Google Inc., rsheeter@google.com)** 20 | 21 | # Introduction 22 | 23 | (This introduction is not part of the proposal) 24 | 25 | This proposal does not introduce any new 26 | features. Rather, it provides documentation for what should happen in 27 | what might previously have been thought to be a rare edge case, so that 28 | different implementations can interoperate correctly. 29 | 30 | Currently, a variable font can have multiple entries in ‘fvar’ for the 31 | same axis. The specification does not disallow this. However, if more 32 | than one such multiple entry is visible to the user interface, the 33 | result is unpredictable and may be confusing for users. 34 | 35 | Therefore, we explicitly state that only one duplicated axis entry may 36 | be visible, and require, for interoperability with existing tools, that 37 | it be the first. 38 | 39 | Fonts with multiple entries for the same axis are in use on the Web 40 | today, including as part of a technique referred to as Higher Order 41 | Interpolation (HOI). It is not a new feature, but the current 42 | specification is silent about it. 43 | 44 | 45 | 46 | 47 | 48 | In 7.3.3 fvar—Font variations table, after 49 | VariationAxisRecord, after the paragraph about the HIDDEN_AXIS tag, add 50 | a new final paragraph as follows: 51 | 52 | For smooth animation, and for non-linear 53 | interpolation, it may be necessary for a font to use multiple axes with 54 | the same axisTag. 55 | 56 | If a font contains more than one axis with the same axisTag, at most one 57 | of those axes shall be visible (i.e. have the HIDDEN_AXIS bit set to 58 | zero). The VariationAxisRecord for such a visible axis in this case 59 | shall appear first, before the records for any of the other axes with 60 | that same axisTag, all of which shall have their HIDDEN_AXIS flag set to 61 | 1. 62 | 63 | The range of values for that first axis should be used for user 64 | interfaces or API access. Where the ranges of the axes with a given 65 | axisTag differ in minimum or maximum, the value shall be clamped to be 66 | within the minimum and maximum of the each axis. The default (initial 67 | value) shall be taken from the non-hidden axis entry. 68 | 69 | \[end\] 70 | -------------------------------------------------------------------------------- /iso_docs/WG03-fvar-2024-04.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-fvar-2024-04.odt -------------------------------------------------------------------------------- /iso_docs/WG03-fvar-2024-04.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-fvar-2024-04.pdf -------------------------------------------------------------------------------- /iso_docs/WG03-fvar-2024-2024-04-15.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-fvar-2024-2024-04-15.docx -------------------------------------------------------------------------------- /iso_docs/WG03-fvar-2024-2024-04-15.md: -------------------------------------------------------------------------------- 1 | **INTERNATIONAL ORGANISATION FOR STANDARDISATION** 2 | 3 | **ORGANISATION INTERNATIONALE DE NORMALISATION** 4 | 5 | **ISO/IEC JTC 1/SC 29/WG 3** 6 | 7 | **CODING OF MOVING PICTURES AND AUDIO** 8 | 9 | **ISO/IEC JTC 1/SC 29/WG 3 m** **67465** 10 | 11 | **Rennes, France – April 2024** 12 | 13 | **Title: ****Improving interoperability of OFF 14 | fonts with duplicated axis names** 15 | 16 | **Author: Dave Crossland (Google Inc., dcrossland@google.com), Behdad 17 | Esfahbod (behdad@behdad.org), Laurence Penney (lorp@lorp.org), Liam Quin 18 | (Delightful Computing, liam@delightfulcomputing.com), Rod Sheeter 19 | (Google Inc., rsheeter@google.com)** 20 | 21 | # Introduction 22 | 23 | (This introduction is not part of the proposal) 24 | 25 | This proposal does not introduce any new 26 | features. Rather, it provides documentation for what should happen in 27 | what might previously have been thought to be a rare edge case, so that 28 | different implementations can interoperate correctly. 29 | 30 | Currently, a variable font can have multiple entries in ‘fvar’ for the 31 | same axis. The specification does not disallow this. However, if more 32 | than one such multiple entry is visible to the user interface, the 33 | result is unpredictable and may be confusing for users. 34 | 35 | Therefore, we explicitly state that at most one duplicated axis entry 36 | may be visible, and require, for interoperability with existing tools, 37 | that it be the first. 38 | 39 | Fonts with multiple entries for the same axis are in use on the Web 40 | today, including as part of a technique referred to as Higher Order 41 | Interpolation (HOI). It is not a new feature, but the current 42 | specification is silent about it. 43 | 44 | 45 | 46 | 47 | 48 | In 7.3.3 fvar—Font variations table, after 49 | VariationAxisRecord, after the paragraph about the HIDDEN_AXIS tag, add 50 | a new final paragraph as follows: 51 | 52 | Fonts may have multiple axes with the same axisTag; this can be helpful 53 | for smooth animation and for non-linear interpolation. For each set of 54 | axes that has an identical axisTag: 55 | 56 | - At most one axis in the set shall be visible (i.e. have its 57 | HIDDEN_AXIS bit set to zero); 58 | - If the set contains a visible axis, it shall come before any 59 | non-visible axes of that set in the axes array; 60 | - In a UI or an API, the minimum, maximum and default values for the 61 | common axisTag shall be taken from the minValue, maxValue and 62 | defaultValue of the axis in the set that comes earliest in the axes 63 | array; 64 | - All axes in the set shall have the same defaultValue; 65 | - Although the axis locations for all axes in the set are defined to be 66 | identical, their normalized values may be different when their 67 | minValue or maxValue are different; 68 | - Independent adjustment of multiple axes having same axisTag shall not 69 | be offered or processed. 70 | 71 | > NOTE 1 An implementation may choose to allow adjustment of a set of 72 | > axes with no visible member, using the first member as described 73 | > above, but there is no requirement to do so. 74 | 75 | \[end\] 76 | -------------------------------------------------------------------------------- /iso_docs/WG03-fvar-2024-2024-04-15.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-fvar-2024-2024-04-15.odt -------------------------------------------------------------------------------- /iso_docs/WG03-fvar-2024-2024-04-15.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-fvar-2024-2024-04-15.pdf -------------------------------------------------------------------------------- /iso_docs/WG03-varc-and-other-updates-03.md: -------------------------------------------------------------------------------- 1 | **INTERNATIONAL ORGANISATION FOR STANDARDISATION** 2 | 3 | **ORGANISATION INTERNATIONALE DE NORMALISATION** 4 | 5 | **ISO/IEC JTC 1/SC 29/WG 3** 6 | 7 | **CODING OF MOVING PICTURES AND AUDIO** 8 | 9 | **ISO/IEC JTC 1/SC 29/WG 3 m** **NNNN** 10 | 11 | **Mirkwood Service Station – March 2024** 12 | 13 | **Title: Updates based on Public Feedback to Changes to the OFF Font 14 | Format** 15 | 16 | **Author: Dave Crossland (Google Inc., dcrossland@google.com), Behdad 17 | Esfahbod (behdad@behdad.org), Laurence Penney (lorp@lorp.org), Liam Quin 18 | (Delightful Computing, liam@delightfulcomputing.com), Rod Sheeter 19 | (Google Inc., rsheeter@google.com)** 20 | 21 | # Introduction 22 | 23 | This introduction is to give context and is not itself proposed text. 24 | 25 | A recent proposal to the ISO MPEG OpenFont committee, 26 | [m66260](https://github.com/harfbuzz/boring-expansion-spec/blob/main/iso_docs/WG03-beyond-64k-glyphs-2024-01e.pdf), 27 | was accepted in January of 2024. Since that time there have been a 28 | number of comments on the proposal. 29 | 30 | Some of these pointed out small typographical errors, and these are 31 | included in this Proposal. 32 | 33 | Some of the comments made technical suggestions, and, where appropriate, 34 | these are also incorporated in this proposal. 35 | 36 | The primary goal is to enable font producers to create fonts containing 37 | more than 65535 glyphs, and to guide implementors, technical writers, 38 | trainers, and others, in their use. 39 | 40 | Technical discussion of most the items proposed here may be found in 41 | Github issues, as noted for each change. 42 | 43 | # hdmx 44 | 45 | [](https://github.com/harfbuzz/boring-expansion-spec/issues/131) 46 | 47 | 48 | 49 | In 5.6.2 hdmx—Horizontal device metrics, in the Device Record table 50 | headed Each DeviceRecord for format 0 looks like this, change the table 51 | as follows: 52 | 53 | Each DeviceRecord for format 0 looks like this. 54 | 55 | | | | | 56 | |---------------|---------------------|------------------------------------------------------------------------------------| 57 | | Device Record | | | 58 | | Type | Name | Description | 59 | | uint8 | pixelSize | Pixel size for following widths (as ppem). | 60 | | uint8 | maxWidth | Maximum width. | 61 | | uint8 | widths\[numGlyphs\] | Array of widths (numGlyphs is from the 'MAXP' table if present, otherwise ‘maxp’). | 62 | 63 | # LTSH 64 | 65 | 66 | 67 | In 5.6.4 LTSH—Linear threshold, at the end of the section, add Format 1 68 | as follows: 69 | 70 | Format 1 of the ‘LTSH’ table supports more than 65535 glyphs in a font: 71 | 72 | | | | | 73 | |--------|--------------------|----------------------------------------------------------------------------------------------------| 74 | | Type | Name | Description | 75 | | uint16 | version | Version number (set to 1). | 76 | | uint24 | numGlyphs | Number of glyphs (numGlyphs is from the 'MAXP' table if present, otherwise ‘maxp’). | 77 | | uint8 | yPels\[numGlyphs\] | The vertical pel height at which the glyph can be assumed to scale linearly. On a per glyph basis. | 78 | 79 | In 6.3.5.1 JSTF—The justification table, after JsfScriptRecord and 80 | before Justification script table, insert the following new subsection, 81 | just after “Example 1 at the end of this clause shows a JSTF Header 82 | table and JstfScriptRecord.” 83 | 84 | **JSTF header **1.1** 85 | 86 | | | | | 87 | |-------------------|---------------------------------------|---------------------------------------------------------------------| 88 | | Type | Name | Description | 89 | | uint16 | majorVersion | Major version of the JSTF table, = 1 | 90 | | uint16 | minorVersion | Minor version of the JSTF table, = 1 | 91 | | uint16 | jstfScriptCount | Number of JstfScriptRecords in this table | 92 | | JstfScriptRecord | jstfScriptRecords\[jstfScriptCount\] | Array of JstfScriptRecords, in alphabetical order by jstfScriptTag | 93 | | uint16 | jstfScriptCount2 | Number of JstfScriptRecords2 in this table | 94 | | JstfScriptRecord2 | jstfScriptRecords2\[jstfScriptCount\] | Array of JstfScriptRecords2, in alphabetical order by jstfScriptTag | 95 | 96 | **JstfScriptRecord**2** 97 | 98 | | | | | 99 | |----------|------------------|------------------------------------------------------------| 100 | | Type | Name | Description | 101 | | Tag | jstfScriptTag | 4-byte JstfScript identification | 102 | | Offset32 | jstfScriptOffset | Offset to JstfScript2 table, from beginning of JSTF Header | 103 | 104 | **** 105 | 106 | **After the JstfScript table, add:** 107 | 108 | ****The JstfScript2 table is based on the JstfScript table, but has 109 | 32-bit offsets:**** 110 | 111 | **JstfScript**2* table * 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 125 | 126 | 127 | 128 | 129 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 141 | 143 | 144 | 145 |
TypeNameDescription
Offset32extenderGlyphOffsetOffset to ExtenderGlyph table, from beginning of JstfScript table 124 | (may be NULL)
Offset32defJstfLangSysOffsetOffset to Default JstfLangSys table, from beginning of JstfScript2 130 | table (may be NULL)
uint16jstfLangSysCountNumber of JstfLangSysRecords in this table, may be zero (0)
JstfLangSysRecordjstfLangSysRecords
140 | [jstfLangSysCount]
Array of JstfLangSysRecords, in alphabetical order by 142 | jstfLangSysTag
146 | 147 | After **the Extender Glyph table, just before Justification Language 148 | System table, insert:** 149 | 150 | **ExtenderGlyph**2* table* 151 | 152 | **The *ExtenderGlyph2* table supports fonts containing more than 65535 153 | glyphs.** 154 | 155 | | | | | 156 | |--------|------------------------------|----------------------------------------------------| 157 | | Type | Name | Description | 158 | | uint16 | glyphCount | Number of extender glyphs in this script | 159 | | uint24 | extenderGlyphs\[glyphCount\] | Extender glyph IDs – in increasing numerical order | 160 | 161 | **In the MultiItemVariationData table** 162 | 163 | [****https://github.com/harfbuzz/boring-expansion-spec/commit/3a911400bdd0ab5b9f1816a542a6202d048394f0****](https://github.com/harfbuzz/boring-expansion-spec/commit/3a911400bdd0ab5b9f1816a542a6202d048394f0) 164 | 165 | ****Change the format from uint16 to uint8.**** 166 | 167 | ****Add a note immediately after the table:**** 168 | 169 | > ****NOTE 1 The format is encoded as an 8-bit value to save space.**** 170 | 171 | > **** 172 | 173 | **In the **Variable Composite Description, add a row to Variable 174 | Component Record, between glyphID and axisIndicesIndex:** 175 | 176 | ****uint32var conditionSetIndex Optional, only present if HAVE_CONDITION 177 | bit of flags is ****set.**** 178 | 179 | **Add another row at the end of the same table (Variable Component 180 | Record), after **FWORD **T**C**entery:** 181 | 182 | ****uint32var reserved\[\] Optional: Process and discard one 183 | uint****32****var per each set bit in RESERVED****\_MASK**** 184 | 185 | **After the list of flags and before Variable Component Flags, add,** 186 | 187 | ****This specification does not define a meaning for any bits in 188 | RESERVED****\_MASK****, and ****conforming fonts shall therefore set all 189 | such bits to zero. ****Font processing software that encounters bits in 190 | RESERVED****\_MASK**** that are set to one shall process them ****in 191 | turn by reading**** uint32var values, one for each set bit, for 192 | extension purposes.**** 193 | 194 | **Again i**n the **Variable Composite Description, **in Variable 195 | Component Flags, rename item 7 from USE_MY_METRICS to HAVE_CONDITION, 196 | **and change the last entry (Reserved):** 197 | 198 | ****6 HAVE_CONDITION**** 199 | 200 | ****. . .**** 201 | 202 | ****15-31 RESERVED****\_MASK**** 203 | 204 | **To the VARC table header, add a new row **below varStore and above 205 | axisIndicesList **(not to be confused with axisIndicesIndex!)** 206 | 207 | ****Offset32 conditionSetList Offset to ****ConditionSetList, from the 208 | start of VAC table header.**** 209 | 210 | **Just before Processing of Variable Composite Glyphs, ** add the new 211 | ConditionSetList type:** 212 | 213 | *****ConditionSetList ******table***** 214 | 215 | | | | | 216 | |----------|------------------|-------------------------------------------------------------------| 217 | | Type | Name | Description | 218 | | Offset32 | ConditionSet\[\] | Array of offsets from the beginning of the ConditionSetList table | 219 | 220 | ** 221 | 222 | *After the first paragraph of Processing of Variable Composite Glyphs 223 | (The component glyphs to be loaded…) insert the following *(just before 224 | the paragraph “For any unspecified axis”)** 225 | 226 | **For each parsed component, if the HAVE_CONDITION flag is set, th**at** 227 | component **shall be loaded but not **used (for example, not displayed), 228 | unless the referenced ConditionSet evaluates to true. The referenced 229 | ConditionSet is found using conditionSetIndex and consulting the 230 | top-level condisionSetList.** 231 | 232 | ** 233 | 234 | NOTE: dmap and fvar changes were moved to separate documents. 235 | 236 | > **** 237 | 238 | \[end\] 239 | -------------------------------------------------------------------------------- /iso_docs/WG03-varc-and-other-updates-03.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-varc-and-other-updates-03.odt -------------------------------------------------------------------------------- /iso_docs/WG03-varc-and-other-updates-03.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-varc-and-other-updates-03.pdf -------------------------------------------------------------------------------- /iso_docs/WG03-varc-and-other-updates-2024-04-15.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-varc-and-other-updates-2024-04-15.docx -------------------------------------------------------------------------------- /iso_docs/WG03-varc-and-other-updates-2024-04-15.md: -------------------------------------------------------------------------------- 1 | **INTERNATIONAL ORGANISATION FOR STANDARDISATION** 2 | 3 | **ORGANISATION INTERNATIONALE DE NORMALISATION** 4 | 5 | **ISO/IEC JTC 1/SC 29/WG 3** 6 | 7 | **CODING OF MOVING PICTURES AND AUDIO** 8 | 9 | **ISO/IEC JTC 1/SC 29/WG 3 m** **67464** 10 | 11 | **Rennes, France – April 2024** 12 | 13 | **Title: Proposed updates based on public feedback to changes to the OFF 14 | specification** 15 | 16 | **Author: Dave Crossland (Google Inc., dcrossland@google.com), Behdad 17 | Esfahbod (behdad@behdad.org), Laurence Penney (lorp@lorp.org), Liam Quin 18 | (Delightful Computing, liam@delightfulcomputing.com), Rod Sheeter 19 | (Google Inc., rsheeter@google.com)** 20 | 21 | # Introduction 22 | 23 | This introduction is to give context and is not itself proposed text. 24 | 25 | A recent proposal to the ISO MPEG OpenFont committee, 26 | [m66260](https://github.com/harfbuzz/boring-expansion-spec/blob/main/iso_docs/WG03-beyond-64k-glyphs-2024-01e.pdf), 27 | was accepted in January of 2024. Since that time there have been a 28 | number of comments on the proposal. 29 | 30 | Some of these pointed out small typographical errors, and these are 31 | included in this Proposal. 32 | 33 | Some of the comments made technical suggestions, and, where appropriate, 34 | these are also incorporated in this proposal. 35 | 36 | The primary goal is to enable font producers to create fonts containing 37 | more than 65535 glyphs, and to guide implementors, technical writers, 38 | trainers, and others, in their use. 39 | 40 | Technical discussion of most the items proposed here may be found in 41 | Github issues, as noted for each change. 42 | 43 | # hdmx 44 | 45 | In 5.6.2 hdmx—Horizontal device metrics, add after (or before?) the 46 | first paragraph: 47 | 48 | The ‘hdmx’ table is used only with fonts containing at most 65535 49 | glyphs; it has not been updated for use with GLYF and MAXP. 50 | 51 | # vdmx 52 | 53 | In 5.5.8 VDMX—Vertical device metrics, add after (or before?) the first 54 | paragraph: 55 | 56 | The ‘VDMX’ table is used only with fonts containing at most 65535 57 | glyphs; it has not been updated for use with GLYF and MAXP. 58 | 59 | # LTSH 60 | 61 | 62 | 63 | In 5.5.4 LTSH—Linear threshold, change the last sentence of the first 64 | paragrap as follows: 65 | 66 | The 'LTSH' table (Linear ThreSHold) is a second, complementary method 67 | for use in fonts with no more than 65535 glyphs (not using MAXP or 68 | GLYF). 69 | 70 | In 6.3.5.1 JSTF—The justification table, after JsfScriptRecord and 71 | before Justification script table, insert the following new subsection, 72 | just after “Example 1 at the end of this clause shows a JSTF Header 73 | table and JstfScriptRecord.” 74 | 75 | **JSTF header **1.1** 76 | 77 | | | | | 78 | |-------------------|---------------------------------------|---------------------------------------------------------------------| 79 | | Type | Name | Description | 80 | | uint16 | majorVersion | Major version of the JSTF table, = 1 | 81 | | uint16 | minorVersion | Minor version of the JSTF table, = 1 | 82 | | uint16 | jstfScriptCount | Number of JstfScriptRecords in this table | 83 | | JstfScriptRecord | jstfScriptRecords\[jstfScriptCount\] | Array of JstfScriptRecords, in alphabetical order by jstfScriptTag | 84 | | uint16 | jstfScriptCount2 | Number of JstfScriptRecords2 in this table | 85 | | JstfScriptRecord2 | jstfScriptRecords2\[jstfScriptCount\] | Array of JstfScriptRecords2, in alphabetical order by jstfScriptTag | 86 | 87 | **JstfScriptRecord**2** 88 | 89 | | | | | 90 | |----------|------------------|------------------------------------------------------------| 91 | | Type | Name | Description | 92 | | Tag | jstfScriptTag | 4-byte JstfScript identification | 93 | | Offset32 | jstfScriptOffset | Offset to JstfScript2 table, from beginning of JSTF Header | 94 | 95 | **** 96 | 97 | **After the JstfScript table, add:** 98 | 99 | ****The JstfScript2 table is based on the JstfScript table, but has 100 | 32-bit offsets:**** 101 | 102 | **JstfScript**2* table * 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 116 | 117 | 118 | 119 | 120 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 132 | 134 | 135 | 136 |
TypeNameDescription
Offset32extenderGlyphOffsetOffset to ExtenderGlyph table, from beginning of JstfScript table 115 | (may be NULL)
Offset32defJstfLangSysOffsetOffset to Default JstfLangSys table, from beginning of JstfScript2 121 | table (may be NULL)
uint16jstfLangSysCountNumber of JstfLangSysRecords in this table, may be zero (0)
JstfLangSysRecordjstfLangSysRecords
131 | [jstfLangSysCount]
Array of JstfLangSysRecords, in alphabetical order by 133 | jstfLangSysTag
137 | 138 | After **the Extender Glyph table, just before Justification Language 139 | System table, insert:** 140 | 141 | **ExtenderGlyph**2* table* 142 | 143 | **The *ExtenderGlyph2* table supports fonts containing more than 65535 144 | glyphs.** 145 | 146 | | | | | 147 | |--------|------------------------------|----------------------------------------------------| 148 | | Type | Name | Description | 149 | | uint16 | glyphCount | Number of extender glyphs in this script | 150 | | uint24 | extenderGlyphs\[glyphCount\] | Extender glyph IDs – in increasing numerical order | 151 | 152 | **In the MultiItemVariationData table** 153 | 154 | [****https://github.com/harfbuzz/boring-expansion-spec/commit/3a911400bdd0ab5b9f1816a542a6202d048394f0****](https://github.com/harfbuzz/boring-expansion-spec/commit/3a911400bdd0ab5b9f1816a542a6202d048394f0) 155 | 156 | ****Change the format from uint16 to uint8.**** 157 | 158 | ****Add a note immediately after the table:**** 159 | 160 | > ****NOTE 1 The format is encoded as an 8-bit value to save space.**** 161 | 162 | > **** 163 | 164 | **In the **Variable Composite Description, add a row to Variable 165 | Component Record, between glyphID and axisIndicesIndex:** 166 | 167 | ****uint32var conditionSetIndex Optional, only present if HAVE_CONDITION 168 | bit of flags is set.**** 169 | 170 | **Add another row at the end of the same table (Variable Component 171 | Record), after **FWORD **T**C**entery:** 172 | 173 | ****uint32var reserved\[\] Optional: Process and discard one 174 | uint****32****var per each set bit in RESERVED****\_MASK**** 175 | 176 | **After the list of flags and before Variable Component Flags, add,** 177 | 178 | ****This specification does not define a meaning for any bits in 179 | RESERVED****\_MASK****, and ****conforming fonts shall therefore set all 180 | such bits to zero. ****Font processing software that encounters bits in 181 | RESERVED****\_MASK**** that are set to one shall process them ****in 182 | turn by reading**** uint32var values, one for each set bit, for 183 | extension purposes.**** 184 | 185 | **Again i**n the **Variable Composite Description, **in Variable 186 | Component Flags, rename item 7 from USE_MY_METRICS to HAVE_CONDITION, 187 | **and change the last entry (Reserved):** 188 | 189 | ****6 HAVE_CONDITION**** 190 | 191 | ****. . .**** 192 | 193 | ****15-31 RESERVED****\_MASK**** 194 | 195 | **To the VARC table header, add a new row **below varStore and above 196 | axisIndicesList **(not to be confused with axisIndicesIndex!)** 197 | 198 | ****Offset32 conditionSetList Offset to ****ConditionSetList, from the 199 | start of VAC table header.**** 200 | 201 | **Just before Processing of Variable Composite Glyphs, ** add the new 202 | ConditionSetList type:** 203 | 204 | *****ConditionSetList ******table***** 205 | 206 | | | | | 207 | |----------|------------------|-------------------------------------------------------------------| 208 | | Type | Name | Description | 209 | | Offset32 | ConditionSet\[\] | Array of offsets from the beginning of the ConditionSetList table | 210 | 211 | ** 212 | 213 | **In* the first paragraph of Processing of Variable Composite Glyphs 214 | (The component glyphs to be loaded…) *append the following **highlighted 215 | sentence**s** **as per email **discussion **and ad-hoc meeting:** 216 | 217 | [**https://github.com/harfbuzz/boring-expansion-spec/commit/c6a534e41e88fe1b560b05f1c18fb780c2af480f**](https://github.com/harfbuzz/boring-expansion-spec/commit/c6a534e41e88fe1b560b05f1c18fb780c2af480f) 218 | 219 | ** 220 | 221 | **The component glyphs to be loaded shall use the coordinate values 222 | specified (with any variations applied if present). **The outline**s** 223 | from all components are concatenated to form the outline for the main 224 | glyph, before any rasterization. **Component glyphs shall **not mix 225 | source types: for example, if one component is taken from CFF2, all 226 | shall be, and if one is from GLYF, all shall be, and so on.** 227 | 228 | *After the first paragraph of Processing of Variable Composite Glyphs 229 | (The component glyphs to be loaded…) insert the following *(just before 230 | the paragraph “For any unspecified axis”)** 231 | 232 | **For each parsed component, if the HAVE_CONDITION flag is set, th**at** 233 | component **shall be loaded but not used (for example, not displayed), 234 | unless the referenced ConditionSet evaluates to true. The referenced 235 | ConditionSet is found using conditionSetIndex and consulting the 236 | top-level condisionSetList.** 237 | 238 | ** 239 | 240 | NOTE: dmap and fvar changes were moved to separate documents. 241 | 242 | > **** 243 | 244 | \[end\] 245 | -------------------------------------------------------------------------------- /iso_docs/WG03-varc-and-other-updates-2024-04-15.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-varc-and-other-updates-2024-04-15.odt -------------------------------------------------------------------------------- /iso_docs/WG03-varc-and-other-updates-2024-04-15.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-varc-and-other-updates-2024-04-15.pdf -------------------------------------------------------------------------------- /iso_docs/WG03-varc-and-other-updates.md: -------------------------------------------------------------------------------- 1 | **INTERNATIONAL ORGANISATION FOR STANDARDISATION** 2 | 3 | **ORGANISATION INTERNATIONALE DE NORMALISATION** 4 | 5 | **ISO/IEC JTC 1/SC 29/WG 3** 6 | 7 | **CODING OF MOVING PICTURES AND AUDIO** 8 | 9 | **ISO/IEC JTC 1/SC 29/WG 3 m** **NNNN** 10 | 11 | **Rivendell – February 2024** 12 | 13 | **Title: Updates based on Public Feedback to Changes to the OFF Font 14 | Format** 15 | 16 | **Author: Dave Crossland (Google Inc., dcrossland@google.com), Behdad 17 | Esfahbod (behdad@behdad.org), Laurence Penney (lorp@lorp.org), Liam Quin 18 | (Delightful Computing, liam@delightfulcomputing.com), Rod Sheeter 19 | (Google Inc., rsheeter@google.com)** 20 | 21 | # Introduction 22 | 23 | This introduction is to give context and is not itself proposed text. 24 | 25 | A recent proposal to the ISO MPEG OpenFont committee, 26 | [m66260](https://github.com/harfbuzz/boring-expansion-spec/blob/main/iso_docs/WG03-beyond-64k-glyphs-2024-01e.pdf), 27 | was accepted in January of 2024. Since that time there have been a 28 | number of comments on the proposal. 29 | 30 | Some of these pointed out small typographical errors, and these are 31 | included in this Proposal. 32 | 33 | Some of the comments made technical suggestions, and, where appropriate, 34 | these are also incorporated in this proposal. 35 | 36 | The primary goal is to enable font producers to create fonts containing 37 | more than 65535 glyphs, and to guide implementors, technical writers, 38 | trainers, and others, in their use. 39 | 40 | Technical discussion of most the items proposed here may be found in 41 | Github issues, as noted for each change. 42 | 43 | # hdmx 44 | 45 | [](https://github.com/harfbuzz/boring-expansion-spec/issues/131) 46 | 47 | 48 | 49 | In 5.6.2 hdmx—Horizontal device metrics, in the Device Record table 50 | headed Each DeviceRecord for format 0 looks like this, change the table 51 | as follows: 52 | 53 | Each DeviceRecord for format 0 looks like this. 54 | 55 | | | | | 56 | |---------------|---------------------|------------------------------------------------------------------------------------| 57 | | Device Record | | | 58 | | Type | Name | Description | 59 | | uint8 | pixelSize | Pixel size for following widths (as ppem). | 60 | | uint8 | maxWidth | Maximum width. | 61 | | uint8 | widths\[numGlyphs\] | Array of widths (numGlyphs is from the 'MAXP' table if present, otherwise ‘maxp’). | 62 | 63 | # LTSH 64 | 65 | 66 | 67 | In 5.6.4 LTSH—Linear threshold, at the end of the section, add Format 1 68 | as follows: 69 | 70 | Format 1 of the ‘LTSH’ table supports more than 6535 glyphs in a font: 71 | 72 | | | | | 73 | |--------|--------------------|----------------------------------------------------------------------------------------------------| 74 | | Type | Name | Description | 75 | | uint16 | version | Version number (set to 1). | 76 | | uint24 | numGlyphs | Number of glyphs (numGlyphs is from the 'MAXP' table if present, otherwise ‘maxp’). | 77 | | uint8 | yPels\[numGlyphs\] | The vertical pel height at which the glyph can be assumed to scale linearly. On a per glyph basis. | 78 | 79 | # BASE 80 | 81 | 82 | 83 | # 84 | 85 | In 6.3.1.4 BASE table structure, update BaseCoordFormat3=4 table to fix 86 | a typo: format should be 4, not 2. 87 | 88 | *BaseCoordFormat*4* table: Design units plus contour point *(24-bit 89 | glyph ID)** 90 | 91 | | | | | 92 | |--------|-----------------|-----------------------------------------------| 93 | | Type | Name | Description | 94 | | uint16 | baseCoordFormat | Format identifier – format = 4 | 95 | | int16 | coordinate | X or Y value, in design units | 96 | | uint24 | referenceGlyph | Glyph ID of control glyph | 97 | | uint16 | baseCoordPoint | Index of contour point on the reference glyph | 98 | 99 | **** 100 | 101 | # JSTF 102 | 103 | In 6.3.5.1 JSTF—The justification table, after JsfScriptRecord and 104 | before Justification script table, insert the following new subsection, 105 | just after “Example 1 at the end of this clause shows a JSTF Header 106 | table and JstfScriptRecord.” 107 | 108 | **JSTF header **1.1** 109 | 110 | | | | | 111 | |-------------------|---------------------------------------|---------------------------------------------------------------------| 112 | | Type | Name | Description | 113 | | uint16 | majorVersion | Major version of the JSTF table, = 1 | 114 | | uint16 | minorVersion | Minor version of the JSTF table, = 1 | 115 | | uint16 | jstfScriptCount | Number of JstfScriptRecords in this table | 116 | | JstfScriptRecord | jstfScriptRecords\[jstfScriptCount\] | Array of JstfScriptRecords, in alphabetical order by jstfScriptTag | 117 | | uint16 | jstfScriptCount2 | Number of JstfScriptRecords2 in this table | 118 | | JstfScriptRecord2 | jstfScriptRecords2\[jstfScriptCount\] | Array of JstfScriptRecords2, in alphabetical order by jstfScriptTag | 119 | 120 | **JstfScriptRecord**2** 121 | 122 | | | | | 123 | |----------|------------------|------------------------------------------------------------| 124 | | Type | Name | Description | 125 | | Tag | jstfScriptTag | 4-byte JstfScript identification | 126 | | Offset32 | jstfScriptOffset | Offset to JstfScript2 table, from beginning of JSTF Header | 127 | 128 | **** 129 | 130 | **After the JstfScript table, add:** 131 | 132 | ****The JstfScript2 table is based on the JstfScript table, but has 133 | 32-bit offsets:**** 134 | 135 | **JstfScript**2* table * 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 149 | 150 | 151 | 152 | 153 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 165 | 167 | 168 | 169 |
TypeNameDescription
Offset32extenderGlyphOffsetOffset to ExtenderGlyph table, from beginning of JstfScript table 148 | (may be NULL)
Offset32defJstfLangSysOffsetOffset to Default JstfLangSys table, from beginning of JstfScript2 154 | table (may be NULL)
uint16jstfLangSysCountNumber of JstfLangSysRecords in this table, may be zero (0)
JstfLangSysRecordjstfLangSysRecords
164 | [jstfLangSysCount]
Array of JstfLangSysRecords, in alphabetical order by 166 | jstfLangSysTag
170 | 171 | ** 172 | 173 | After **the Extender Glyph table, just before Justification Language 174 | System table, insert:** 175 | 176 | **** 177 | 178 | **ExtenderGlyph**2* table* 179 | 180 | **The *ExtenderGlyph2* table supports fonts containing more than 65535 181 | glyphs.** 182 | 183 | | | | | 184 | |--------|------------------------------|----------------------------------------------------| 185 | | Type | Name | Description | 186 | | uint32 | glyphCount | Number of extender glyphs in this script | 187 | | uint32 | extenderGlyphs\[glyphCount\] | Extender glyph IDs – in increasing numerical order | 188 | 189 | # FVAR 190 | 191 | [](https://github.com/harfbuzz/boring-expansion-spec/issues/15) 192 | 193 | 194 | 195 | In 7.3.3 fvar—Font variations table, after VariationAxisRecord, after 196 | the paragraph about the HIDDEN_AXIS tag, add a new final paragraph as 197 | follows: 198 | 199 | For smooth animation, and for non-linear interpolation, it may be 200 | necessary for a font to use multiple axes with the same axisTag. 201 | 202 | If a font contains more than one axis with the same *axisTag*, at most 203 | one of those axes shall be visible (i.e. have the HIDDEN_AXIS bit set to 204 | zero). The VariationAxisRecord for such a visible axis in this case 205 | shall appear first, before the records for any of the other axes with 206 | that same axisTag, all of which shall have their HIDDEN_AXIS flag set to 207 | 1. 208 | 209 | > **** 210 | 211 | \[end\] 212 | -------------------------------------------------------------------------------- /iso_docs/WG03-varc-and-other-updates.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-varc-and-other-updates.odt -------------------------------------------------------------------------------- /iso_docs/WG03-varc-and-other-updates.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03-varc-and-other-updates.pdf -------------------------------------------------------------------------------- /iso_docs/WG03_otf-improvements.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03_otf-improvements.docx -------------------------------------------------------------------------------- /iso_docs/WG03_otf-improvements.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/WG03_otf-improvements.pdf -------------------------------------------------------------------------------- /iso_docs/changes.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/changes.docx -------------------------------------------------------------------------------- /iso_docs/changes.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/changes.odt -------------------------------------------------------------------------------- /iso_docs/changes2.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/changes2.odt -------------------------------------------------------------------------------- /iso_docs/comment-2024-12-10.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/comment-2024-12-10.odt -------------------------------------------------------------------------------- /iso_docs/comment-2024-12-10.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/comment-2024-12-10.pdf -------------------------------------------------------------------------------- /iso_docs/m64287-AVAR_OFF_updates.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/m64287-AVAR_OFF_updates.docx -------------------------------------------------------------------------------- /iso_docs/m64287-AVAR_OFF_updates.md: -------------------------------------------------------------------------------- 1 | **INTERNATIONAL ORGANISATION FOR STANDARDISATION** 2 | 3 | **ORGANISATION INTERNATIONALE DE NORMALISATION** 4 | 5 | **ISO/IEC JTC 1/SC 29/WG 3** 6 | 7 | **CODING OF MOVING PICTURES AND AUDIO** 8 | 9 | **ISO/IEC JTC 1/SC 29/WG 3 m** **64287** 10 | 11 | **Geneva, Switzerland – July 2023** 12 | 13 | **Title: Updating AVAR table in OFF to support the needs of modern 14 | computing platforms.** 15 | 16 | **Author: Dave Crossland (Google Inc., dcrossland@google.com), Behdad 17 | Esfahbod (behdad@behdad.org), Laurence Penney (lorp@lorp.org), Liam Quin 18 | (Delightful Computing, liam@delightfulcomputing.com), Rod Sheeter 19 | (Google Inc., rsheeter@google.com)** 20 | 21 | Contents Page 22 | 23 | # Introduction 24 | 25 | The axis variations table ‘avar’ is an optional table used in variable 26 | fonts. Version 1 of ‘avar’ modifies aspects of how a design varies for 27 | different instances along a particular design-variation axis. It does 28 | this by piecewise linear remapping on a per-axis basis, with certain 29 | restrictions. 30 | 31 | Version 2, proposed here, enables flexible axis remapping where each 32 | design-variation axis is modified according to the coordinates of 33 | multiple design-variation axes. In order to combine the effects of 34 | multiple input valuess, ‘avar’ version 2 uses the OFF variation 35 | mechanism itself to determine interpolated delta values and add them to 36 | design-variation axis coordinates. The final interpolated axis 37 | coordinates are used in all subsequent variation operations. 38 | 39 | The efficiency of ‘avar’ version 2 enables fonts that: 40 | 41 | - are significantly smaller; 42 | - are easier for users to control; 43 | - simplify source maintenance by avoiding redundant data. 44 | 45 | Use cases include: 46 | 47 | - warped variable font design spaces to reflect a typeface designer’s 48 | intention accurately; 49 | - parametric fonts with intuitive control methods and a much reduced 50 | data footprint; 51 | - simpler methods of control for specialized variable fonts. 52 | 53 | This proposal does not include glyph-specific transformations, for 54 | example for advanced text justification, but does not preclude data to 55 | support such such features being added to the table at a later revision. 56 | 57 | This draft submission replaces Section 6 in m62937, *Proposed 58 | improvements and new functionality for the Open Font Format standard for 59 | global font support.* 60 | 61 | # The ‘avar’ version 2.0 header format 62 | 63 | The 'avar' table is comprised of a small header plus segment maps for 64 | each axis. 65 | 66 | Axis variation table 67 | 68 | | | | | 69 | |----------------------------------|----------------------------------------|-----------------------------------------------------------------------------------------------------------------------| 70 | | uint16 | majorVersion | Major version number of the axis variations table — set to 2. | 71 | | uint16 | minorVersion | Minor version number of the axis variations table — set to 0. | 72 | | uint16 | \ | Permanently reserved; set to zero. | 73 | | uint16 | axisSegmentMapCount | The number of axisSegmentMaps for this font. If this is not 0, it shall be the same as axisCount in the 'fvar' table. | 74 | | SegmentMaps | axisSegmentMaps\[axisSegmentMapCount\] | The segment maps array — one segment map for each axis, in the order of axes specified in the 'fvar' table. | 75 | | Offset32To\ | axisIndexMapOffset | Offset from beginning of the table to axisIndexMap. | 76 | | Offset32To\ | itemVariationStoreOffset | Offset from beginning of the table to varStore. | 77 | 78 | The table format for ‘avar’ version 2 is the same as avar version 1, but 79 | offsets to two extra structures are appended to it: axisIndexMap and 80 | varStore. 81 | 82 | axisIndexMap is a **DeltaSetIndexMap** structure that maps the axis 83 | indices implied in ‘fvar’ to indices used in varStore. The outer index 84 | identifies an **ItemVariationData** structure in varStore. The inner 85 | index identifies a **deltaSet** within an **ItemVariationData**. 86 | 87 | varStore is an **ItemVariationStore** structure that points to a 88 | **VariationRegions** array and a list of **ItemVariationData** 89 | structures. Each **ItemVariationData** specifies a subset of 90 | **VariationRegions** and an array of **deltaSets**. Each **deltaSet** 91 | specifies a delta value for each region. 92 | 93 | Delta values are typically in (but not limited to) the range \[-1.0, 94 | 1.0\]. 95 | 96 | Delta values are stored as if they were signed integers by multiplying 97 | their true value by 16384. Thus 1.0 is stored as 16384; -1.0 is stored 98 | as -16384. 99 | 100 | The DeltaSetIndexMap and ItemVariationStore formats are given in [OFF 101 | “Font variations common table formats](#_Font_variations_common)”. 102 | 103 | ## Processing 104 | 105 | Processing of axis values in an ‘avar’ version 2 table happens in 3 106 | stages for a given instance: 107 | 108 | 1. Initial normalization to convert user coordinates of each axis to 109 | initial normalized coordinates in the range \[-1.0, 1.0\]. 110 | 2. Remap initial normalized coordinates of each axis via the 111 | axisSegmentMaps of ‘avar’ version 1, providing intermediate 112 | coordinates also in the range \[-1.0, 1.0\]. 113 | 3. Calculate interpolated deltas for each axis via ‘avar’ version 2 and 114 | add them to intermediate coordinates, providing final coordinates 115 | that are clamped to the range \[-1,1\]. 116 | 117 | In more detail, step 3 proceeds as follows. Considering the coordinates 118 | provided by step 2, a scalar is determined for each variationRegion by 119 | the standard variation interpolation algorithm. Then, the deltaSets of 120 | each ItemVariationData are processed such that each delta value is 121 | multiplied by its associated scalar. Summing those products gives an 122 | interpolated delta to add to a particular axis coordinate, the axis 123 | index being defined in axisIndexMap. After the summing operations for 124 | all ItemVariationData structures is complete, axis values are clamped to 125 | to the range \[-1.0, 1.0\], giving final axis coordinates. 126 | 127 | The final axis coordinates obtained by step 3 are subsequently used in 128 | the standard variation process described in [Algorithm for Interpolation 129 | of Instance Values](#algorithm-for-interpolation-of-instance-values), 130 | applying to all ‘gvar’ data and all ItemVariationStore data elsewhere in 131 | the font. 132 | 133 | The following algorithm implements step 3 above, producing the final 134 | normalized axis coordinates: 135 | 136 | // let coords be the vector of current normalized coordinates. 137 | 138 | std::vector\ out; 139 | 140 | for (unsigned i = 0; i \< coords.size(); i++) 141 | 142 | { 143 | 144 | int v = coords\[i\]; 145 | 146 | uint32_t varidx = i; 147 | 148 | if (axisIdxMap != 0) 149 | 150 | varidx = (this+axisIdxMap).map(varidx); 151 | 152 | float delta = 0; 153 | 154 | if (varStore != 0) 155 | 156 | delta = (this+varStore).get_delta (varidx, coords); 157 | 158 | v += std::roundf (delta); 159 | 160 | v = std::clamp (v, -(1\<\<14), +(1\<\<14)); 161 | 162 | out.push_back (v); 163 | 164 | } 165 | 166 | for (unsigned i = 0; i \< coords.size(); i++) 167 | 168 | coords\[i\] = out\[i\]; 169 | 170 | ## Construction 171 | 172 | The way ItemVariationStores are built is typically by using a variation 173 | modeler, that takes a series of master values at certain locations in 174 | the design-space, and produces VariationRegions and deltaSets to be 175 | stored in one or more ItemVariationData structures. This usage is 176 | identical, except that delta values apply to normalized axis coordinates 177 | rather than distances measured in font units. To build the avar version 178 | 2 mapping tables, the designer will need to produce a mapping of input 179 | axis locations and their respective output axis locations. This data 180 | then will constitute the set of masters to be fed to the variation 181 | modeler and populate the ItemVariationStore that will go into the avar 182 | version 2 table. The variation index for each axis will be stored in 183 | axisIndexMap. 184 | 185 | ## Use cases 186 | 187 | ### Designspace warping 188 | 189 | In the OFF variations specification, [registered axes](#varaxisreg) 190 | offer a standard way to offer users control of a variable font on 191 | reasonably well defined scales. For example, users learn that the 192 | Regular version of a font has Weight=400, Width=100, while the Bold has 193 | wght=700, wdth=100. A typical 2-axis variable font with wght and wdth 194 | axes might have the following nine Named Instances: 195 | 196 | | | | | | 197 | |--------------|-----------------|---------|----------------| 198 | | **wght=300** | Light Condensed | Light | Light Extended | 199 | | **wght=400** | Condensed | Regular | Extended | 200 | | **wght=700** | Bold Condensed | Bold | Bold Extended | 201 | 202 | However, in fonts with more than one design axis, this approach lacks 203 | the flexibility of older methods of interpolation, where the type 204 | designer used a font design application to specify freely the axis 205 | values for, say, the Bold Condensed. Notably, the wght coordinate of 206 | Bold Condensed did not need to match the wght coordinate of Bold, nor 207 | did the wdth coordinate of Bold Condensed need to match the wdth 208 | coordinate of Condensed. A Bold Condensed with wght,wdth of (677,81) 209 | rather than (700,75) was perfectly reasonable. Our grid of instances in 210 | such as case would look something like this: 211 | 212 | | | | | | 213 | |-------------|--------|---------|---------| 214 | | **Light** | 300,75 | 300,100 | 300,115 | 215 | | **Regular** | 400,75 | 400,100 | 400,120 | 216 | | **Bold** | 677,81 | 695,100 | 700,125 | 217 | 218 | While with the existing Open Font Format specification it is possible to 219 | set up a designspace like this, it has the significant drawback that 220 | many applications and systems expect all Bold weights to be at wght=700 221 | and all Condensed weights to be at wdth=75, whatever the values of other 222 | axes. In practice, many OFF fonts encode numerous additional masters 223 | that ensure the design is as intended at all designspace locations. This 224 | not only increases the data footprint significantly, but also adds to 225 | the maintenance burden of the font. 226 | 227 | Furthermore, there are many cases where a type designer does not intend 228 | to offer instances in certain regions of the design space. For example, 229 | a Black Condensed instance is often difficult to design, and many type 230 | families omit it. Desired behaviour, in case a user requests wght=900, 231 | wdth=75, may be for the font to provide the same instance for the Black 232 | Condensed as it does for the Bold Condensed. 233 | 234 | By applying deltas to axis values anywhere in the designspace, the avar 235 | table version 2 “warps” the designspace and so resolves the issues 236 | described. 237 | 238 | As with variation deltas in general, we also benefit from interpolation 239 | when axis values are influenced by a delta but are not exactly at the 240 | location of the delta. Each delta value is encoded as F2DOT14. Its 241 | interpolated value is determined by the standard OFF variations 242 | algorithm, then added to the value obtained from the standard 243 | normalization process. Thus, assuming the Bold Condensed instance at 244 | (700,75) has normalized coordinates (1,-1) and assuming the designspace 245 | location (677,81) has normalized coordinates ((677-400)/(700-400), -1 + 246 | (81-75)/(100-75)) = (0.9233,-0.76), then the ItemVariationStore needs a 247 | delta set with peaks at (-1,1), thus with region ((-1,-1,0),(0,1,1)) and 248 | the following delta values: 249 | 250 | - wght -0.0767 251 | - wdth +0.24 252 | 253 | In practice there may be other delta sets required at the partial 254 | default locations (-1,0) and (0,1), which then influence the delta 255 | values required at (-1,1), in line with normal variations math. 256 | 257 | The result of implementing designspace warping this way is that we 258 | preserve the original axis values where applications and systems expect 259 | them, namely all Bold instances at wght=700 and all Condensed weights at 260 | wdth=75, while at the same time minimizing the data footprint. 261 | 262 | ### Duplication of axis values for non-linear interpolation 263 | 264 | Certain variable fonts encode delta sets in such a way that, when 265 | multiple axes are synchronized, outline points move in curves rather 266 | than in straight lines. This technique has been referred to as HOI 267 | (higher order interpolation). The details of the non-linear encoding 268 | need not concern us here, but a practical drawback under the existing 269 | Open Font Format specification is that it requires users to set multiple 270 | design axes to identical values in order to obtain valid instances. When 271 | axes are not synchronized, the resulting glyphs are severely distorted 272 | and useless. Synchronization using JavaScript has been proposed as a 273 | solution. A clever exploit of the CFF2 format, allowing a single axis to 274 | perform all the necessary non-linear adjustment of outline points, has 275 | also been demonstrated (but this method cannot be adapted to adjust 276 | delta values in an ItemVariationStore non-linearly). 277 | 278 | The avar version 2 table provides a solution to the synchonization 279 | problem, such that a user adjusts one “primary” axis and that value is 280 | cloned to other subordinate axes. Ideally the subordinate axes have the 281 | Hidden flag set in fvar to discourage manual operation. For a font with 282 | 3 axes requiring identical values, if the first axis is considered 283 | primary, then the other two can encode delta sets in avar’s 284 | ItemVariationStore to clone the value of the first axis. Assuming the 285 | subordinate axes have the same minimum, default and maximum values as 286 | the primary axis, each subordinate axis requires either one or two delta 287 | sets to clone the negative and positive regions of the normalized value 288 | of the primary axis: 289 | 290 | - If the axes use only the normalized region \[0,1\] then one delta set 291 | per subordinate axis is needed, referring to the region (0,1,1) of the 292 | primary axis and having the delta value of 1. This is the common case. 293 | - If the axes use only the normalized region \[-1,0\] then one delta set 294 | per subordinate axis is needed, referring to the region (-1,-1,0) of 295 | the primary axis and having the delta value of -1. 296 | - If the axes use both the normalized regions \[-1,0\] and \[0,1\] then 297 | two delta sets per subordinate axis are needed, one referring to the 298 | region (0,1,1) of the primary axis and having the delta value of 1, 299 | the other referring to the region (-1,-1,0) of the primary axis and 300 | having the delta value of -1. 301 | 302 | ### Simplified controls for parametric fonts without redundant data 303 | 304 | Parametric fonts, explored by Donald Knuth in Metafont and by others, 305 | offer fine-tuned control of specific font-wide aspects of their design. 306 | Such aspects typically include the thickness of vertical strokes, 307 | thickness of horizontal strokes, the overall width of the font, and 308 | direct control over vertical zones including cap-height, ascender 309 | height, x-height and descender height. 310 | 311 | In an OFF variable font these parameters can be encoded as 312 | design-variation axes. The Type Network Variations Proposal[^1] of 2017 313 | provides the basis for parametric OFF fonts Amstelvar, Roboto Flex and 314 | others, demonstrating the potential for usable parametric fonts with 315 | large character sets. That flexibility comes from a designspace of many 316 | axes – sometimes 10 or more – which raises issues of control. Faced with 317 | 10 sliders, users are unlikely to be able to find the instance they want 318 | and are likely to get “lost” in a unusable region of the designspace. 319 | 320 | In existing OFF fonts, the solution to getting “lost” is to include 321 | “blended” axes that combine the deltas of parametric axes in such a way 322 | that they function in exactly the same way as they would in a 323 | non-parametric font. The blended axes are exposed using the registered 324 | axes of wght, wdth, opsz, etc., or well specified custom axes, and they 325 | are encoded in the standard OFF manner using gvar and related data. The 326 | major problem with such fonts is their large data footprint, as the 327 | blended axes effectively replicate much of what the parametric axes do. 328 | For a font containing both parametric and blended axes, the problem is 329 | particularly acute, so the parametric axes may be omitted to save space 330 | — thereby removing core functionality. In such cases the parametric 331 | nature of the font is thus reduced to a convenience of sources. 332 | 333 | The avar table version 2 provides a method to deploy a purely parametric 334 | font with user-friendly axes, while avoiding the data bloat of blended 335 | axes. The user axes, for example wght and wdth, on their own do nothing 336 | (i.e. they have no gvar or similar data), but they control the 337 | parametric axes by means of avar. The blending of parametric values into 338 | user-facing values effectively happens live in the font engine. The 339 | parametric axes are expected to have the Hidden flag set in fvar to 340 | discourage (but not block) manual operation. 341 | 342 | ## Inverse avar processing 343 | 344 | Normally it is not possible for an application to obtain normalized axis 345 | values directly, these remaining private to the font engine. In fonts 346 | with an avar version 2 table, however, it can be useful to know the axis 347 | values that would invoke a given instance if the font engine did not 348 | support avar version 2. Use cases include: 349 | 350 | - informing users of the effective settings of parametric axes; 351 | - providing a polyfill for avar version 2. 352 | 353 | The solution is for the application itself to implement the avar 354 | algorithms, both version 1 and version 2. This requires not only access 355 | to the binary avar table, but also knowledge of the font’s axis extents 356 | as defined in the fvar table. Once an application has a complete set of 357 | final normalized values as defined above, the inverse of the avar 358 | version 1 algorithm can be applied to obtain user coordinates for all 359 | axes. (Processors shall be written in such a manner as to avoid a 360 | divide-by-zero error in implementing an inverse avar version 1 mapping, 361 | in the case where consecutive toCoordinate values are identical.) 362 | 363 | Almost all fonts can provide a valid set of user axis coordinates for a 364 | given set of final normalized axis coordinates in this way. Exceptions 365 | are those fonts that encode deltas in a region that is not accessible 366 | without avar version 2, namely those fonts where the fvar definition of 367 | an axis normally precludes an active negative region or a active 368 | positive region by having its default equal to its minimum or its 369 | maximum respectively. Such cases are expected to be rare. 370 | 371 | # Recommendations and notes 372 | 373 | ## Hidden axes 374 | 375 | Since many avar version 2 fonts have axes not not intended for manual 376 | adjustment, it is recommended that such axes set the “hidden” flag in 377 | VariationAxisRecord of the fvar table. 378 | 379 | ## Efficient axisIdxMap and ItemVariationData construction 380 | 381 | In order to reduce proliferation of zero deltas, it is recommended to 382 | store in axisIndexMap only those axes that are remapped by avar version 383 | 2. For the same reason, if there are multiple different types of axis 384 | mapping affecting different sets of axes, consider using multiple 385 | ItemVariationData structures. 386 | 387 | ## Other notes 388 | 389 | - The set of axes involved in adjusting the coordinate for a given axis 390 | may include the axis itself. 391 | - It is expected that implementations that handle only avar version 1 392 | will ignore the entire table by rejecting the majorVersion value. 393 | 394 | [^1]: 395 | > 396 | -------------------------------------------------------------------------------- /iso_docs/m64287-AVAR_OFF_updates.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/m64287-AVAR_OFF_updates.odt -------------------------------------------------------------------------------- /iso_docs/m64287-AVAR_OFF_updates.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/m64287-AVAR_OFF_updates.pdf -------------------------------------------------------------------------------- /iso_docs/m64287-avar2-combined-wd7.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/m64287-avar2-combined-wd7.odt -------------------------------------------------------------------------------- /iso_docs/m64287-avar2-combined-wd7.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/m64287-avar2-combined-wd7.pdf -------------------------------------------------------------------------------- /iso_docs/m64287-combined.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/m64287-combined.docx -------------------------------------------------------------------------------- /iso_docs/m64287-combined.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/m64287-combined.odt -------------------------------------------------------------------------------- /iso_docs/m64287-combined.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/m64287-combined.pdf -------------------------------------------------------------------------------- /iso_docs/m70320-kemer.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/m70320-kemer.docx -------------------------------------------------------------------------------- /iso_docs/m70320-kemer.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/m70320-kemer.odt -------------------------------------------------------------------------------- /iso_docs/m70320-kemer.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harfbuzz/boring-expansion-spec/644552bf0eb15352a102f5f67ec0698bc1a88e6d/iso_docs/m70320-kemer.pdf --------------------------------------------------------------------------------