The format for the metamorphosis (
‘mort’) table was first designed in 1988. At that time, 680x0 processors were the design center, and memory was comparatively expensive. Because of these constraints, the table format was designed to take as little space as possible, and to use “tricks” which sped up processing. An example of this is the use of even numbers for
‘cmap’ formats: because they were even, the format could be added to a base address directly to compute a jump address.
In a world of hundred-megabyte machines running gigahertz RISC processors, however, this design is anachronistic—and worse, an impediment. Developers wished to create fonts which required more than the original
'mort' table could supply. To this end, an extended glyph metamorphosis table, the
'morx' table, has been introduced.
'morx' table structure is related to the
'mort' table structure; developers should be familiar with the latter. Because so much is changed within a
'morx' table, however, this new table bumps to version 2.0. However, in order to allow the essentially limitless expansion supported by the current format, we keep the notion of chains.
Unfortunately, Apple's AAT metamorphosis shaping code neglected to check the version in the morph code. This means we could not rely solely on the version, since older versions of ATSUI presented with fonts using version 2 morph tables would choke. This was solved by introducing the new table format. Version checking has been added for the
‘morx’ table, so future versions past version 2 can be dealt with gracefully in older software. Eventually Apple will deprecate the
|fixed32||version||Version number of the extended glyph metamorphosis table (0x00020000 for the current version)|
|uint32||nChains||Number of metamorphosis chains contained in this table.|
As with version 1.0
'mort' tables, this header is followed by the chains. Each chain starts with the chain header:
|uint32||defaultFlags||The default specification for subtables.|
|uint32||chainLength||Total byte count, including this header; must be a multiple of 4.|
|uint32||nFeatureEntries||Number of feature subtable entries.|
|uint32||nSubtables||The number of subtables in the chain.|
The feature subtable immediately follows the chain header. It is an array of nFeatureEntries of the following structures:
|uint16||featureType||The feature type.|
|uint16||featureSetting||The feature selector.|
|uint32||enableFlags||The OR’ed enable flags.|
|uint32||disableFlags||The AND’ed disable flags.|
After the feature subtable come the actual subtables, a total of nSubtables of them. Each one starts with a subtable header in this format:
|uint32||length||Total subtable length, including this header.|
|uint32||coverage||Coverage flags and subtable type.|
|uint32||subFeatureFlags||The 32-bit mask identifying which subtable this is (the subtable being executed if the AND of this value and the processed defaultFlags is nonzero)|
The coverage value is interpreted in the following way:
|0x80000000||If set, this subtable will only be applied to vertical text. If clear, this subtable will only be applied to horizontal text.|
|0x40000000||If set, this subtable will process glyphs in descending order. If clear, it will process the glyphs in ascending order.|
|0x20000000||If set, this subtable will be applied to both horizontal and vertical text (i.e. the state of bit 0x80000000 is ignored).|
|0x1FFFFF00||Reserved, set to zero.|
|0x000000FF||Subtable type; see following table.|
The subtable types are defined as follows:
|4||Noncontextual (“swash”) subtable.|
The principal difference in a version 2.0
‘morx’ table is in the expanded range of the state table and its defining fields. Here is the new definition of the state table header:
|uint32||nClasses||Number of classes, which is the number of 16-bit entry indices in a single line in the state array.|
|uint32||classTableOffset||Offset from the start of this state table header to the start of the class table.|
|uint32||stateArrayOffset||Offset from the start of this state table header to the start of the state array.|
|uint32||entryTableOffset||Offset from the start of this state table header to the start of the entry table.|
Historically the class table has been a tight array of 8-bit values. However, in certain cases (such as Asian fonts) the potential wide separation between glyph indices covered by the same class table has led to much wasted space in the table. Therefore, the class tables in version 2
‘morx’ tables are now simply LookupTables, where the looked-up value is a 16-bit class value. Note that a format 8 LookupTable (trimmed array) yields the same results as class array defined in the version 1 ‘mort’ table.
See Apple Advanced Typography Font Tables for a full description of a LookupTable. Note that the current LookupTable definition limits us to 16-bit lookup results (i.e. classes); at some point this may be revised, but 32-bit support is not needed for the scope of the initial
Another important difference between the old-style and new-style state table is that the entries in the state array are now 16-bit zero-based indices into the entry table, instead of being 8-bit indices. This enormously increases the range and power of state tables using this new format.
The remainder of this section discusses changes to each of the specific subtable types. As a general note, the entries in the entry table here refer to new states not by byte offset but rather by a zero-based 16-bit index. This allows full coverage of even the largest state array we might imagine, subject to the current 16-bit glyph index limitation.
The rearrangement subtable starts with a new-style state table header, as defined above. There are no additional offset, nor any per-glyph tables.
The interpretation of the rearrangement subtable’s action flags is unchanged. As mentioned above, the reference to the new state is now a zero-based index instead of a byte offset. No additional verbs are defined.
The contextual subtable starts with a new-style state table header, as defined above. Immediately after the entryTableOffset is a UInt32 byte offset from the start of the state table header to the start of the per-glyph lookup tables (which are new, and replace the old substitution tables).
|uint16||newState||Zero-based index to the new state.|
|uint16||markIndex||Index of the substitution table for the marked glyph.|
|uint16||currentIndex||Index of the substitution table for the current glyph.|
There are still four 16-bit fields in the entry table’s entry, but their interpretation is somewhat different. As mentioned above, the reference to the new state is now a zero-based index instead of a byte offset. Similarly, what used to be the markOffset and currentOffset fields are now zero-based indices into the set of per-glyph LookupTables whose base offset is the
UInt32 following the entryTableOffset, as described above. Since they are no longer offsets they have been renamed to markIndex and currentIndex. If no substitution is to be done, use the value –1.
Replacing the old per-glyph substitution table is a set of LookupTables. A glyph whose action indicates a substitution should happen will specify the index of one of these LookupTables. That glyphcode will be put through the specified LookupTable in order to ascertain the new glyphcode.
In order to speed up the process of locating the LookupTable being indexed, the very start of the per-glyph lookup tables is an array of
UInt32 offsets from the start of the per-glyph lookup table space (whose offset itself is the
UInt32 after the entryTableOffset, as described above). The markIndex or currentIndex is thus used to first look up the offset in this table, and that offset then allows immediate access to the LookupTable, which is then used to look up the glyphcode.
Note that nowhere is there specified the number of LookupTables. Since this number is an artifact of the font production process, and is not needed by the runtime metamorphosis software, there was no need to include it explicitly.
An example of a
‘morx’ contextual substitution table is included in the Examples section, later in this document.
Because the changes to the ligature subtable are a little more extensive, this section will be a little more thorough in documenting the data structures. It will also help the reader to see the examples section at the end of this document.
After the standard subtable header, the ligature subtable starts off with an extended state table header:
|STXHeader||stateHeader||New-style state table header, as described above.|
|UInt32||ligActionOffset||Byte offset from stateHeader to the start of the ligature action table.|
|UInt32||componentOffset||Byte offset from stateHeader to the start of the component table.|
|UInt32||ligatureOffset||Byte offset from stateHeader to the start of the actual ligature lists.|
‘mort’ tables, the ligature subtable in a
‘morx’ table makes use of the three extra offsets listed here.
The state header is then followed by each of the 7 state table pieces, as specified by their offsets. They may be in any order; for discussion here and in the examples section, we will assume the pieces are in the same order as their offset fields in the state table header.
The class table is just a LookupTable, as described above.
The state array is a two dimensional array of UInt16 indices into the entry table. The rows of the state array correspond to states, and the columns to the classes (the four fixed classes plus the ones specific to this table, for a total of nClasses).
The entry table for a new-style ligature subtable has three UInt16s per entry, instead of two in the old-style subtable:
|UInt16||nextStateIndex||Row index in the state array for the state which will be used by the next glyph.|
|UInt16||entryFlags||Flags; see below (note a new flag).|
|UInt16||ligActionIndex||Index to the first ligActionTable entry for processing this group, if indicated by the flags.|
The flags use the following bits:
|0x8000||setComponent||Push this glyph onto the component stack for eventual processing.|
|0x4000||dontAdvance||Leave the glyph pointer at this glyph for the next iteration.|
|0x2000||performAction||use the ligActionIndex to process a ligature group. Note this flag is new; it was not present in the old-style ‘mort’ table’s ligature subtable|
|0x3FFF||N/A||Reserved; set to zero. Note this interpretation is different too; the byte offset which used to be here has been replaced with a full UInt16 index, the ligActionIndex|
The ligature action table is next. A key difference between this new ligature subtable format and the old-style one is that the choice of where to start in the table is made by index, not offset, as described above. However, the processing is overall the same: the glyphs which were pushed onto the stack by the entry table flags are now popped off (in reverse order, remember) and acted upon as indicated by the ligature action value. The format of a ligature action is a UInt32, broken up as follows:
|0x80000000||last||This is the last action in the list. This also implies storage.|
|0x40000000||store||Store the ligature at the current cumulated index in the ligature table in place of the marked (i.e. currently-popped) glyph.|
|0x3FFFFFFF||offset||A 30-bit value which is sign-extended to 32-bits and added to the glyph ID, resulting in an index into the component table.|
If this seems confusing, please see the examples section for an illustration of how this all works.
The component table is an array of
UInt16 values which, once looked up as described above, are added together to obtain an index (not an offset!) into the final piece, the actual ligature list.
The ligature list itself is unchanged: an array of output UInt16 ligatures.
Since no state tables are involved here, there is no change in this format.
The insertion subtable starts with a new-style state table header, as defined above. Immediately after the entryTableOffset is a new
UInt32 byte offset from the start of the state table header to the start of the insertion glyph table.
There are still four 16-bit fields in the entry table’s entry, but their interpretation is somewhat different. As mentioned above, the reference to the new state is now a zero-based index instead of a byte offset. Similarly, what used to be the markInsertList and currentInsertList fields are now zero-based indices into the insertion glyph table. A value of -1 means no insert list is used.
The interpretation of the insertion subtable’s action flags is unchanged.
In order to help clarify how the new subtables look, here are some examples.
Suppose I have some swash glyphs which I wish to encode in a feature. For input glyphs 50, 51, 201 and 202 I wish to map to swash glyphs 600, 601, 602 and 900, provided the input glyph is preceded by glyph 80. This contextual table would look like this; note that this table is far more compact than an equivalent ‘mort’ table would be, because of the use of LookupTables in two key places:
|0||5||Number of classes|
|4||20||Byte offset to class lookup table|
|8||56||Byte offset to state array|
|12||92||Byte offset to entry table|
|16||xxx||16 xxx Byte offset to per-glyph table|
*** The class lookup table starts here ***
|20||6||Lookup table format 6 (single)|
|22||4||Unit size (4 bytes)|
|24||5||Number of units|
|32||50,4||Input glyph 50 maps to class 4|
|36||51,4||Input glyph 51 maps to class 4|
|40||80,5||Input glyph 80 maps to class 5|
|44||201,4||Input glyph 201 maps to class 4|
|48||202,4||Input glyph 202 maps to class 4|
|52||-1,1||Guard at end (not counted in nUnits)|
*** The state array starts here ***
|56||0 0 0 0 0 1||The entry table indices for the first row of the state array (start of text), indexed by class|
|68||0 0 0 0 0 1||The entry table indices for the second row of the state array (start of line), indexed by class|
|80||0 0 0 0 2 1||The entry table indices for the final row of the state array (saw80 state)|
*** The entry table starts here ***
|92||0||Entry 0: goto state row 0|
|94||0||Entry 0: no flags|
|96||-1||Entry 0: no mark substitution|
|98||-1||Entry 0: no current substitution|
|100||2||Entry 1: goto state row 2 (saw 80)|
|102||0||Entry 1: no flags|
|104||-1||Entry 1: no mark substitution|
|106||-1||Entry 1: no current substitution|
|108||0||Entry 2: goto state row 0|
|110||0||Entry 2: no flags|
|112||-1||Entry 2: no mark substitution|
|114||0||Entry 2: Use per-glyph lookup 0|
*** The per-glyph lookup tables start here, pad to UInt32 if needed ***
|116||4||Offset from this point to start of per-glyph lookup 0|
*** Per-glyph lookup 0 starts here ***
|120||6||Lookup table format 6 (single)|
|122||4||Unit size (4 bytes)|
|124||4||Number of units|
|132||50, 600||Input glyph 50 maps to glyph 600|
|136||51, 601||Input glyph 51 maps to glyph 601|
|144||201, 602||Input glyph 201 maps to glyph 602|
|148||202, 900||Input glyph 202 maps to 900|
|152||-1, 1||Guard at end (not counted in nUnits)|
Suppose I have this group of ligatures in a font:
adf, adg, adh, adi, aef, aeg, aeh, aei glyphs 1000 – 1007
bdf, bdg, bdh, bdi, bef, beg, beh, bei glyphs 1008 – 1015
cdf, cdg, cdh, cdi, cef, ceg, ceh, cei glyphs 1500 – 1506 and 1511
Notice that the ligatures have ‘a’, ‘b’ or ‘c’ as their first elements, ‘d’ or ‘e’ as their second, and ‘f’, ‘g’, ‘h’ or ‘i’ as their third. Let’s suppose that ‘a’ is glyph 20, ‘b’ glyph 21, and so on through ‘i’ at glyph 28. Let’s see what a ligature subtable to encode these ligatures would look like, starting with the state table header:
|0||7||Number of classes|
|4||28||Byte offset to class lookup table|
|8||64||Byte offset to state array|
|12||120||Byte offset to entry table|
|16||144||Byte offset to ligature actions|
|20||156||Byte offset to component base|
|24||174||Byte offset to ligature list base|
*** The class lookup table starts here ***
|28||2||Lookup table format 2 (segment single)|
|30||6||Unit size (6 bytes)|
|32||3||Number of units|
|40||22, 20, 4||First segment, mapping glyphs 20 through 22 (‘a’ through ‘c’) to class 4|
|46||24, 23, 5||Second segment, mapping glyph 23 and 24 (‘d’ and ‘e’) to class 5|
|52||24, 23, 5||Third segment, mapping glyphs 25 through 28 (‘f’ through ‘i’) to class 6|
|58||-1, -1, 1||Special guard segment (not counted in nUnits above, note!)|
*** The state array starts here ***
|64||0 0 0 0 1 0 0||The entry table indices for the first row of the state array (start of text), indexed by class|
|78||0 0 0 0 1 0 0||The entry table indices for the second row of the state array (start of line), indexed by class|
|92||0 0 0 0 1 2 0||The entry table indices for the third row of the state array (the “saw a, b, or c” state), indexed by class|
|106||0 0 0 0 1 2 3||The entry table indices for the last row of the state array (the “saw a, b, or c followed by d or e” state), indexed by class|
*** The entry table starts here ***
|120||0||Entry 0: goto state row 0|
|122||0||Entry 0: no flags|
|124||0||Entry 0: no 0x2000 flag, so ignore|
|126||2||Entry 1: goto state row 2|
|128||0x8000||Entry 1: set a component|
|130||0||Entry 1: no 0x2000 flag, so ignore|
|132||3||Entry 2: goto state row 3|
|134||0x8000||Entry 2: set a component|
|136||0||Entry 2: no 0x2000 flag, so ignore|
|138||0||Entry 3: goto state row 0|
|140||0xA000||Entry 3: set component and act|
|142||0||Entry 3: Start at ligActions|
*** The ligature actions table starts here ***
|144||0x3FFFFFE7||Action 0, part 1: since ‘f’, ‘g’, ‘h’ or ‘i’ are first popped, add their glyph indices to sign extended lower 30 bits, which is -25. So ‘f’ (glyph 25) plus -25 yields component entry 0; ‘g’ yields 1; ‘h’ yields 2; and ‘i’ yields 3|
|148||0x3FFFFFED||Action 0, part 2: since ‘d’ or ‘e’ are second popped, add their glyph indices to sign-extended lower 30 bits, which is -19. So ‘d’ (glyph 23) plus -19 yields component entry 4; and ‘e’ yields 5|
|152||0xBFFFFFF2||Action 0, part 3 (last part): since ‘a’, ‘b’ or ‘c’ are last popped, add their glyph indices to sign-extended lower 30 bits, which is -14. So ‘a’ (glyph 20) plus -14 yields component entry 6; ‘b’ yields 7; and ‘c’ yields 8|
*** The component table starts here ***
|156||0||component entry 0|
|158||1||component entry 1|
|160||2||component entry 2|
|162||3||component entry 3|
|164||0||component entry 4|
|166||4||component entry 5|
|168||0||component entry 6|
|170||8||component entry 7|
|172||16||component entry 8|
*** The ligature table starts here ***
|174||1000||ligature list -- ‘adf’|
|176||1001||ligature list -- ‘adg’|
|178||1002||ligature list -- ‘adh’|
|180||1003||ligature list -- ‘adi’|
|182||1004||ligature list -- ‘aef’|
|184||1005||ligature list -- ‘aeg’|
|186||1006||ligature list -- ‘aeh’|
|188||1007||ligature list -- ‘aei’|
|190||1008||ligature list -- ‘bdf’|
|192||1009||ligature list -- ‘bdg’|
|194||1010||ligature list -- ‘bdh’|
|196||1011||ligature list -- ‘bdi’|
|198||1012||ligature list -- ‘bef’|
|200||1013||ligature list -- ‘beg’|
|202||1014||ligature list -- ‘beh’|
|204||1015||ligature list -- ‘bei’|
|206||1500||ligature list -- ‘cdf’|
|208||1501||ligature list -- ‘cdg’|
|210||1502||ligature list -- ‘cdh’|
|212||1503||ligature list -- ‘cdi’|
|214||1504||ligature list -- ‘cef’|
|216||1505||ligature list -- ‘ceg’|
|218||1506||ligature list -- ‘ceh’|
|220||1511||ligature list -- ‘cei’|
174 1000 ligature list -- ‘adf’
176 1001 ligature list -- ‘adg’
178 1002 ligature list -- ‘adh’
180 1003 ligature list -- ‘adi’
182 1004 ligature list -- ‘aef’
184 1005 ligature list -- ‘aeg’
186 1006 ligature list -- ‘aeh’
188 1007 ligature list -- ‘aei’
190 1008 ligature list -- ‘bdf’
192 1009 ligature list -- ‘bdg’
194 1010 ligature list -- ‘bdh’
196 1011 ligature list -- ‘bdi’
198 1012 ligature list -- ‘bef’
200 1013 ligature list -- ‘beg’
202 1014 ligature list -- ‘beh’
204 1015 ligature list -- ‘bei’
206 1500 ligature list -- ‘cdf’
208 1501 ligature list -- ‘cdg’
210 1502 ligature list -- ‘cdh’
212 1503 ligature list -- ‘cdi’
214 1504 ligature list -- ‘cef’
216 1505 ligature list -- ‘ceg’
218 1506 ligature list -- ‘ceh’
220 1511 ligature list -- ‘cei’
morx' is supported on Mac OS X from its initial release onwards. It is not supported on pre-Mac OS X versions of the Mac OS.
'morx' table is not used by the Newton OS.
morx' table references to glyph indices; these may need to be updated whenever the glyph count recorded in the
'maxp' table drops.
Hex editing of the
'morx' table is possible using TrueEdit. There are currently no other tools available to create or edit
[Table of Contents]
Last updated: JHJ