# INDD objects How the byte stream of a database object (see `database.md`) is structured, and what the classes and chunks used by the converter mean. Implemented in `src/object.rs` and `src/model/`. ## How this was learned IDML exported by InDesign names most objects `Self="u"`, and that hex number is the object's UID in the INDD file. So each corpus pair gives, for every such IDML element, the INDD object, its class, and the attribute values its chunks must encode. Facts below were found by comparing the two, and are checked by `tools/compare.py`, which converts every pair (78 unique pairs, InDesign 7.5–21) and compares the output with the reference IDML attribute by attribute. ## Chunks Most objects (99.9 % of 773,753 in the little-endian corpus) are a sequence of chunks: | Size | Field | |---|---| | 4 | Chunk ID | | 4 | Data length *n* | | *n* | Data | The rest are plain byte streams: objects of class 0x129 (raw data, below) and the XMP packet (UID 0x80000001). In the 245 distinct little-endian files, all 1,171 class 0x129 objects are plain byte streams. Chunk IDs are in the same number space as class IDs; a chunk often lists objects of the class with the same number (for example chunk 0x501 of the document lists spreads, class 0x501). ## Primitive encodings Object data is in the byte order of the header's flag (`header.md`). All samples except two are little-endian, and the examples here are little-endian. Big-endian data and the layouts of InDesign 3.0 and 4.0 are in `big-endian.md`. - **UID lists:** u32 count, then that many u32 UIDs. - **Matrices:** six f64, in IDML `ItemTransform` order (a b c d tx ty). - **Text segments:** a u16 header, flags in bits 14–15 and a count in bits 0–13. 0x4000: *count* single-byte characters follow. 0x8000: *count* UTF-16 code units follow. A text of *n* code units is a sequence of segments adding up to *n*. Example: "WOMEN’S\r" is `05 40 "WOMEN" 01 80 19 20 02 40 "S\r"`. - **In-object strings:** u8 2, u8 (usually 0; a script code in some strings, below), u16 length in code units, then segments. 4,135 occurrences in three sample files. In files from InDesign 2.0 the first byte is 1: in the four distinct 2.0 files, the pattern tag, 0, length *n*, 0, segment header 0x4000 + *n* occurs 154 to 353 times per file with tag 1 and never with tag 2. In the 83 distinct 3.0, 4.0 and 5.0 little-endian files the same pattern has tag 2 (76 to 4,296 times per file); tag 1 matches once in each of two of them. ## Document (class 0xE01, always UID 1) | Chunk | Contents | |---|---| | 0x501 | UID list: spreads, in order | | 0x1401 | UID list: master spreads | | 0x301 | UID list: layers (the first is an internal layer that holds pages) | | 0x313 | u32: active layer (IDML `ActiveLayer`) | | 0x222 | Two UID lists: the stories, then the XML backing story; IDML `StoryList` is both (`xml.md`) | | 0x4C01 | UID list: sections | | 0xA443 | Document users: u32 count, then per user a flag byte, the name as an in-object string, u32 colour | | 0x1D8 | Save history: u32 count, then one record per session (below) | | 0x1630B | Label: u16 0x7B7B, u32 count *n*, then *n* pairs of key and value, each a flag byte and an in-object string | | 0x21B03 | UID list: articles, in order (below, articles) | **Document label.** IDML writes `Document/Properties/Label` with one `KeyValuePair` (`Key`, `Value`) per stored pair, in stored order, with the strings as stored. Without the chunk there is no `Label`. Evidence: 436 of the 438 trustworthy pairs whose IDML has a document label match (one differs, one has no chunk). The flag byte is 1 on 4 keys in 4 pairs, all others 0; the 3 of these pairs whose IDML has a document label write the key without the `$ID/` prefix of built-in names. **Script byte of the user names.** The second byte of each user name's in-object string (the byte after the string tag) is a script code: 0 in documents in Latin script, 1 in all 9 trustworthy pairs with Japanese user or assignment names, 7 in a Russian, 25 in a Simplified Chinese and 29 in a Czech document. The same codes are font writing scripts (1 for Japanese fonts, 25 for Simplified Chinese fonts). All users of a document have the same byte, except in one pair with users of bytes 0 and 7. The converter uses the byte of the last user to choose font family names (`fonts.md`, native family names). Other in-object strings carry the same kind of byte, so a search for a string must accept any second byte. In the 149 pairs typeset in Japanese it is 1 for most user-made names, and 3, 5, 7, 0x19 and 0x1D also occur, in style names, style GUIDs, layer names and ruby font styles. Layer names and style GUIDs (below) rely on this. **Document users.** IDML lists one `DocumentUser` per user in chunk 0xA443, `Self` being `dDocumentUser` and the index in hexadecimal (491 of 495 trustworthy pairs have as many users as the chunk; the other 4 are DOM 7 and 16 files with one user more in the IDML). A user with flag 0 has its stored name as `UserName` (1,724 of 1,724). Flag 2 marks the placeholder for an unknown user, stored with a name such as `Unknown User Name` or the same words in the language of the computer that saved it; IDML writes `$ID/Unknown User Name` for 495 of the 586 such users, and the stored name for the other 91. The rule is the saving edition's name for an unknown user (saving edition, below): IDML writes `$ID/Unknown User Name` for every user, of either flag, whose stored name is that edition's, and the stored name for every other user, also a flag 2 user named in another language (for example `Unknown User Name` in a document last saved by a Korean edition). Without a black name the converter writes `$ID/Unknown User Name` for every flag 2 user (the schema requires `UserName`). The colour UID names an interface colour, but the IDML `UserColor` does not follow from it (users stored with 0.6, 0.4, 0 are `BrickRed` in 4 files and `Gold` in 1), so it is left out of the conversion and of the measurement (`measurement.md`). **Save history (chunk 0x1D8).** A u32 count, then one record per session that saved the document, oldest first: | Field | Contents | |---|---| | u16 | kind (not used) | | u16 | platform: 0 or 3 (style shortcuts, below) | | u16, u16 | not identified | | u16 | language code (below) | | flag byte, in-object string | application version, such as `16.1.0.20` | | u16 | build (not used) | | u32, u32 | FILETIME of the session, high part first (not used) | Evidence: the layout parses every record of 4,431 of the 4,439 distinct valid corpus files outside the privately held samples (4,272 little-endian, 159 big-endian), from InDesign 3.0 to 21. Of the other 8, six are InDesign 2.x files whose strings have another tag (in-object strings, above) and two (one 3.x, one 13.x) have no chunk. All 803 pairs parse. **Language code.** The u16 of the last record is 0x0100 in 4,018 of those files, 0x0101 in 335, 0x0103 in 53 and 0x0400 in 25. It follows the language edition of the application of that session: - All 149 pairs typeset in Japanese that the corpus gained in 2026-10 have 0x0101; so do 11 earlier pairs, among them the 7 whose default story tag name (preferences chunk 0xBF4F) is Japanese or Chinese. No document with a tag name in another language (Korean, German, French, Russian, …) has 0x0101 (654 pairs before 2026-10). - The values IDML writes in the exporting InDesign's language follow it (below): in 0x0101 documents the flattener resolution, the index title and separator and the endnote separator are those of a Japanese edition. - 0x0103 occurs in documents in Latin and Hebrew script; it gives the same values as 0x0100 in all 12 pairs that have it. The converter reads the code of the last record only, and only uses whether it is 0x0101. **Application version.** The version string of the last record can be later than the version in the header: InDesign 13.1 saved files whose header says 13.0. IDML writes some values only from 13.1, and follows the application, not the header. Over the trustworthy pairs of version 12.0 to 14.0 (corpus after 2026-10), the document's `TextFramePreference` has the `Footnotes…` values: | Header | Last session | IDML `DOMVersion` | Pairs | With the values | |---|---|---|---:|---:| | 12.0, 12.1 | same | same | 72 | 0 | | 13.0 | 13.0 | 13.0 | 42 | 0 | | 13.0 | 13.0 | 13.1 | 16 | 16 | | 13.0 | 13.1 | 13.1 | 93 | 93 | | 13.1 | 13.1 | 13.1 | 148 | 148 | | 14.0 | 14.0 or 14.3 | 14.0 | 43 | 43 | The 16 were exported by InDesign 13.1 without saving, which the INDD cannot show. The converter takes a 13.1 boundary as reached when the header or the last session is at least 13.1: for the `Footnotes…` values of the document, of text frames and of object styles, and for `MergeConsecutiveParaBorders`. This adds 93 to 253 values for each of these keys and no wrong or extra value. The record count is checked against the chunk size; a chunk that does not parse gives no code. **Values that follow the language of the last session.** Over all 803 pairs (160 with code 0x0101): | Value | 0x0101 | Other codes | |---|---|---| | Spread `FlattenerPreference` resolutions, no chunk 0x10833 | 400, 400 | 300, 150 | | `IndexOptions` `BetweenEntriesSeparator`, no chunk 0x13010 (`preferences.md`) | `、` in 157 of 159 | `; ` in 632 of 632 | | `IndexOptions` `Title`, no chunk 0x13010 | `索引` in 158 of 159 | `Index` in 624 of 632, and `Indice`, `Rejstřík`, `Índice`, `Указатель` | | `EndnoteOption` `EndnoteSeparatorText` and `EndnoteMarkerPositioning`, no chunk 0x2261E (`footnotes.md`) | U+3000 and `RubyMarker` in 17 of 18 | tab and `SuperscriptMarker` in 123 of 123 | | `EndnoteOption` `EndnoteTitle`, no chunk 0x2261E | `文末脚注` (15, DOM 13), `後注` (2, DOM 19 and 20), `Endnotes` (1) | `Endnotes` in 93 of 123, and 4 other titles | | `Assignment` `Name` | `$ID/UnassignedInCopy` in 157 of 160 | `$ID/UnassignedInCopy` in 612 of 643 | One trustworthy pair from version 16.1 has code 0x0101 but the values of another language in every row (flattener 300 and 150 on its 9 spreads, `Index`, `; `, tab, `Endnotes`). Its IDML is dated 55.5 hours after the last session in the INDD, while the IDMLs of 9 other 0x0101 documents are dated 0 to 40 seconds after it (plus their time zone). So that IDML was exported in a session the INDD does not record, by an application of another language. The converter uses the code where it decides the value: the flattener resolutions, the index separator and the endnote separator. The index title, the endnote title and marker position and the unknown user follow the saving edition (below). It writes no assignment `Name`: names that are not `$ID/UnassignedInCopy` occur with every code. **Saving edition (the XMP black name).** The XMP packet of the INDD lists the document's colours in `xmpTPg:Colorants`, each with `xmpG:swatchName`, `xmpG:mode`, `xmpG:type` and its values (`cyan` to `black` in percent, `red`, `green`, `blue` 0 to 255). Tints have an `xmpG:tint` and keep the stored name of their base colour. The black swatch is listed with the name the InDesign that saved the document gives it: `Black`, `검정`, `黒`, `黑色`, `Schwarz`, `Noir`, `Nero`, `Zwart`, `Černá`, `Preto`, `Черный`. The converter takes as the black name the `swatchName` of the one colorant that is not a tint, is a process colour, has the mode and values of the document's black colour (`Color/Black`, as IDML writes its values) and whose name no other colour of the document has. Over the 1,251 trustworthy pairs of the corpus after 2026-10 there is exactly one such colorant in each: 502 `검정`, 431 `Black`, 126 `黒`, 93 `黑色`, 45 `Schwarz`, 44 `Noir`, 4 `Nero`, 3 `Zwart` and one each of `Černá`, `Preto` and `Черный`. This is a rule over stored data: the packet records the edition of the last save, and IDML follows the edition that exported it. Where a document was exported by another installation than the one that saved it last, the IDML has the other edition's values; the exceptions below are such documents. IDML writes these values in the language of that edition: | Black name | Tint of black | Unknown user | Endnote title (no chunk 0x2261E) | Index title (no chunk 0x13010) | |---|---|---|---|---| | `Black` | `[Black] 40%` | `Unknown User Name` | `Endnotes` | `Index` | | `검정` | `[검정] 40%` | `알 수 없는 사용자 이름` | `미주` | `색인` | | `黒` | | `不明なユーザー名` | `文末脚注` (DOM 13), `後注` (DOM 19, 20) | `索引` | | `黑色` | `[黑色] 40%` | `不明的使用者名稱`, `未知用户名` | | `索引` | | `Schwarz` | `[Schwarz] 40%` | `Unbekannter Benutzername` | `Endnoten` | `Index` | | `Noir` | `[Noir] 40%` | `Nom d'utilisateur inconnu` | `Notes de fin` | `Index` | | `Nero` | | `Nome ut. sconosciuto` | `Note di chiusura` | `Indice` | | `Zwart` | | `Onbekende gebruikersnaam` | `Eindnoten` | `Index` | | `Černá` | | `Neznámé jméno uživatele` | | `Rejstřík` | | `Preto` | | `Nome de usuário desconhecido` | | `Índice` | | `Черный` | | `Неизвестное имя пользователя` | | `Указатель` | An empty cell has no sample. The endnote marker is `RubyMarker` for `黒` and `SuperscriptMarker` for every other black name. With these rules the converter reproduces, over the trustworthy pairs: | Value | Reproduced | Before (assignment name and code rules) | |---|---|---| | `Tint` elements and their names (tints of black) | 204 of 204 tints, no extra tint | 171, and 409 extra values in 27 documents | | `DocumentUser` `UserName` | 3,208 of 3,212 | 3,000 | | `EndnoteOption` `EndnoteTitle` | 917 of 918 | 692 (no title without the chunk) | | `EndnoteOption` `EndnoteMarkerPositioning` | 916 of 918 | 876 | | `IndexOptions` `Title` | 1,250 of 1,251 | 1,162 | The remaining differences are the 16.1 document above (exported by another edition: three users, its endnote title and its index title), one 20.0 document whose black name is `Black` but whose IDML writes the German name of the unknown user as `$ID/Unknown User Name` (two users), and DOM 7 documents whose IDML lists one user more than the INDD. In 8 stale pairs the rule loses 1 to 3 values each: their IDML was exported by another edition than the one that saved the INDD later (for example a Dutch endnote title in a document whose last save has `Black`). Not explained by the black name: `BaselineFrameGridColor`, `ColumnGuideColor`, the grid and gutter defaults and the print marks of the Korean documents with code 0x0101 whose IDML has the values of a Roman edition (`preferences.md`, values of the exporting edition), the language quotes and the trimmed language lists. ## Spreads (0x501) and master spreads (0x1401) | Chunk | Contents | |---|---| | 0x503 | u32 self, u32 0, UID list of spread layers | | 0x56E | Matrix: `ItemTransform` | | 0x1B8 | u32: `BindingLocation` | | 0x1A8 | u16: 0 = `AllowPageShuffle="true"`, 1 = `false` | | 0x10833 | Flattener settings (below) | | 0x14580 | Tab orders: u32 count, then per page u32 page UID and a UID list (the page's `TabOrder`) | | 0x140D | Master spreads: u16, 0 = `ShowMasterItems="false"` | | 0x140A | Master spreads: u32 story of the primary text frame (below) | | 0x2050C | Page transition (12 bytes): u32 type code, u32 40, u32 direction code (below) | | 0x2056D | Timing lists (animation and timing, below) | Evidence, over the 5,129 spreads and 914 master spreads of the 495 trustworthy pairs: - `AllowPageShuffle`: 0x1A8 is 0 in 5,064 spreads, all `true`, and 1 in 53, all `false`. The 12 spreads without the chunk (in 7 documents) are all `false`. - `ShowMasterItems` of master spreads: 913 have no chunk 0x140D and are `true`; the one with the chunk (0) is `false`. - `PrimaryTextFrame` of master spreads, over all pairs of the corpus of 2026-10: IDML writes `n` when chunk 0x140A is absent (1,471 master spreads) or holds 0 (6). Otherwise it names the text frame on that master spread whose `ParentStory` is the chunk's story and whose `PreviousTextFrame` is `n` (50 of 50; in 40 of them the story has a second frame on the spread, which is not the primary one). IDML of DOM 7 has no `PrimaryTextFrame` (9 master spreads). The attribute is the last one of `MasterSpread`. The converter writes it from DOM 8 and leaves it out when the spread has no such frame: 1,521 of 1,521 values over all pairs with the same major version. - `TabOrder` of pages: the list for the page in its spread's chunk 0x14580 equals the IDML list for 9,367 of 9,367 pages (8 with a tab order); pages not in the chunk have an empty `TabOrder`. **Page transitions.** Chunk 0x2050C gives `PageTransitionType` and `PageTransitionDirection`. Over the 11,676 spreads of the distinct little-endian pairs with the same major version (corpus after 2026-10): | u32 at 0 | `PageTransitionType` | u32 at 8 | `PageTransitionDirection` | Spreads (documents) | |---|---|---|---|---| | no chunk | `None` | | `NotApplicable` | 11,646 (1,453) | | 0 | `None` | 0 | `NotApplicable` | 12 (2) | | 0x205C8 | `BlindsTransition` | 0xB | `Horizontal` | 8 (4) | | 0x205CA | `CombTransition` | 0xB | `Horizontal` | 2 (2) | | 0x205CC | `DissolveTransition` | 0 | `NotApplicable` | 4 (4) | | 0x205CF | `PushTransition` | 1 | `Down` | 2 (2) | | 0x205D0 | `SplitTransition` | 0xD | `HorizontalIn` | 2 (2) | The u32 at 4 is 40 in every chunk, and every spread has `PageTransitionDuration="Medium"` (`idml-values.md`), so the field is not mapped. Master spreads have neither the chunk nor the attributes (2,395 of 2,395). The converter writes the two attributes on spreads for these codes, and leaves out an attribute whose code is not in the table. **Flattener settings.** Every IDML spread has a `FlattenerPreference` child. Chunk 0x10833 (52 bytes) is in 20 spreads of 5 trustworthy documents, always with the same bytes: f64 0.5 at offset 8, f64 400 at offsets 20 and 28, 2 at 36 and 800 at 44. Their IDML has `LineArtAndTextResolution` and `GradientAndMeshResolution` 400 and `RasterVectorBalance` 50. In the corpus of 2026-10, 182 spreads of 20 trustworthy documents store 150 at 20 and 300 at 28, and their IDML has `LineArtAndTextResolution="300"` and `GradientAndMeshResolution="150"` (182 of 182): the f64 at 28 is `LineArtAndTextResolution` and the f64 at 20 `GradientAndMeshResolution`. Without the chunk, the IDML has 400 and 400 when the last session of the save history (document, above) has code 0x0101, and 300 and 150 otherwise: 7,481 of the 7,490 spreads of all 803 pairs match, the other 9 being the spreads of the pair exported in a session the INDD does not record. No stored resolution decides it: the bytes of 400 as f64 occur in 1 of the 10 such documents of the 654 pairs before 2026-10, and no byte of the spread chunks separates the two groups. The converter writes the resolutions by this rule, and none when the history cannot be read. The other four values are the same in every IDML (`idml-values.md`). **Spread layers (0x301)** hold the items of one document layer on one spread. Chunk 0x302: u32 document layer UID, u16 1 for the layer's guide part. Chunk 0x303: u32 spread, u32 spread, UID list of children. Pages are children of the spread layer for the internal pages layer. ## Guides (0x3301) Ruler guides are objects of class 0x3301. They are children of a spread layer (listed in its chunk 0x303), like page items, and their chunk 0x15B names the spread and the spread layer. The spread layer's document layer is IDML `ItemLayer`. IDML writes a guide as a `Guide` element inside a `Page` element. Chunk 0x3308 (52 bytes): | Offset | Contents | IDML | |---|---|---| | 0 | f64 position in spread coordinates: y for a horizontal guide, x for a vertical one | `Location` (below) | | 8 | u32 owner: a page of the spread, or the spread itself | the `Page` element the guide is in | | 12 | u16 orientation: 1 horizontal, 0 vertical | `Orientation` | | 14 | f64, 0.05 in every sample | `ViewThreshold="5"` in every sample | | 22 | u32, 6 in every sample | `GuideColor` `Cyan` in every sample | | 26 | u16 fit to page: 1 true, 0 false | `FitToPage` | | 28 | f64, not identified | | | 36 | u32, not identified (24, 25, 30 or 31) | | | 40 | u32 guide type: 0 ruler, 1 liquid | `GuideType` (`Ruler`, `Liquid`) | | 44 | f64, 1 in every sample | | **Evidence.** The 137 distinct little-endian files hold 847 guides; 815 have this 52-byte record. The other 32 are in one InDesign 7.0 file with no IDML and have a 40-byte record, the first 40 bytes of the layout above; the InDesign 3.0 and 4.0 files have it too (`big-endian.md`). The converter reads it and leaves out `GuideType`. In the corpus pairs, 71 IDML guides in 33 distinct files have an INDD object with the same UID. Two more pairs have the same number of guides on both sides but different UIDs (the IDML was exported from another save) and are not used. For the 71: - `Orientation`: 71 of 71 (50 horizontal, 21 vertical). - `FitToPage`: 71 of 71 (49 true, 22 false). - `GuideType`: 71 of 71 (59 ruler, 12 liquid). - `ItemLayer`: 71 of 71. - Owner: the 49 guides owned by a page are in that page's element, the 22 owned by the spread are in the spread's first page (71 of 71). All 22 spread guides have `FitToPage="false"` and all 49 page guides `FitToPage="true"`. - The view threshold and colour have one value each in the INDD and in IDML, so the fields are not proven. The converter writes `ViewThreshold="5"` only for 0.05 and `GuideColor` `Cyan` only for 6. **Location.** IDML measures `Location` from the ruler origin (`ViewPreference` `RulerOrigin`, chunk 0x808 of the preferences, `preferences.md`). It is the stored position minus the origin's y (horizontal guides) or x (vertical guides). A page's corner is its `GeometricBounds` corner mapped by its `ItemTransform`. The origin is: - `SpreadOrigin`: the top-left corner of the spread's pages (the smallest left and top edge of any page). - `PageOrigin`: the top-left corner of the guide's own page; for a guide of the spread, the corner of the spread's pages as with `SpreadOrigin`. - `SpineOrigin`: for vertical guides on a spread of two pages, the left edge of the right page (x = 0 in the samples); otherwise as with `SpreadOrigin`. IDML computes the difference after adding the spread's translation (`ItemTransform` of the spread) to both values: `(position + t) − (origin + t)`. This only changes the last bits, but it decides whether a guide on the origin gets 0 or a remainder such as −1.1e-13. Over the 1,252 trustworthy pairs it gives the IDML number exactly for 8,580 of the 9,127 guides in `SpreadOrigin` documents (7,782 without the translation); the others differ only in the last digits. **Evidence.** All 553 guides of the 23 trustworthy pairs with `PageOrigin` follow the rule: 435 page guides (304 horizontal, 131 vertical, on spreads of one, two and five pages) and 118 spread guides (116 horizontal, 2 vertical on one-page spreads). The 9,127 guides of `SpreadOrigin` documents follow it but for 25 (below), including pages whose bounds do not start at 0. The one `SpineOrigin` file (a stale pair, InDesign 15.0) has three vertical guides: on a two-page master spread at stored −552.76 and 42.52 with IDML −552.76 and 42.52 (from x = 0), on a one-page master spread at stored 0 with IDML 297.64 (from the page's left edge at −297.64); its horizontal guides are measured from the top of the pages. No spread of more than two pages with `SpineOrigin` was seen. **Guides at the spread origin.** In 5 trustworthy pairs (InDesign 17.0, one collection), 5 guides each that belong to a page with `FitToPage="true"` have `Location` values that put them at 0 in spread coordinates: vertical guides stored at −457.14 and −58.47 get the spine's position, a horizontal guide stored at −9.92 gets y = 0. The stored positions lie inside their pages. No field of the guide record or the guide's other chunks separates them from the other guides of the same documents (`unknowns.md`). The converter writes the stored position. **Spread guides outside the pages.** IDML leaves out a guide owned by the spread when its position lies outside the bounding box of the spread's pages (each page's corners mapped by its `ItemTransform`; the bleed does not count): for a horizontal guide when its y is above the top or below the bottom, for a vertical one the same with x. Over the 489 trustworthy pairs before 2026-10: 84 of 85 such guides are left out (positions such as −15, −66 or 1215 on pages 1200 wide), all 1,198 spread guides inside are written, and all 5,740 page guides are written wherever they are. With the bleed added, 30 of the left-out guides would be inside. The exception is a horizontal guide at −9 on a master spread, which IDML writes; its object has chunks 0x1424 and 0x1425, which no left-out guide has. The converter leaves out such guides (with a tolerance of 1e-6). **Other settings.** A guide has chunks 0x2C2D (`Locked`) and 0x1424 (`OverriddenPageItemProps`) as page items do (`Page item settings`), and the f64 at offset 44 of the 52-byte record is `GuideZone`. Over the 7,003 guides of the trustworthy pairs: `Locked` 7,003 of 7,003 (3 locked), `GuideZone` 6,988 of the 6,988 with a 52-byte record (464 with 0), `OverriddenPageItemProps` 6,988 of 6,988; the 15 guides with a 40-byte record have neither attribute in IDML. `PageIndex` counts from the spine of the spread: 0 for a guide of the spread itself; for a page guide, the pages left of the spine are −1, −2, … (counting outwards) and those right of it 1, 2, …, the spine being after `BindingLocation` pages; on master spreads it is at the left edge. This gives 6,998 of 7,003 (1,215 spread guides 0; 2,266 of 2,269 page guides on document spreads, 473 of them −1 and 12 −2; 3,517 of 3,519 on master spreads). ## Layers (0x302) Chunk 0x304: | Offset | Contents | IDML | |---|---|---| | 0 | u16 | `Locked` (1 = true) | | 2 | u16 | `Visible` | | 4 | u16 | `Printable` | | 6 | u16 | `LockGuides` | | 8 | u16, 1 in every sample | | | 10 | u32 UID of an interface colour | `LayerColor` (below) | | 14 | u16 | `UI` | | 16 | u16, 1 in every sample | | | 18 | u8 flag, in-object string | `Name` | | after the name | u16 | `IgnoreWrap` | The internal layer is the first UID of the document's layer list (chunk 0x301 of UID 1) and has no IDML element: in 489 of 489 trustworthy pairs that layer is not written. It is usually named `Internal_pages_layer_name` (467), but also `Pages` (20) or has an empty name (2), so the name does not identify it. In all 1,053 layers of the trustworthy pairs the name starts at offset 19, and each field above equals the IDML value for 1,053 of 1,053 layers (127 locked, 55 hidden, 59 not printable, 4 with locked guides, 1 without `UI`, 2 ignoring text wrap). In the corpus of 2026-10, the string at offset 19 gives the IDML `Name` of 1,683 of 1,683 layers in all pairs, whatever its second byte (in the trustworthy pairs, 188 layers have a byte other than 0, all with the IDML name). The converter reads the name at offset 19 when byte 19 is the string tag, and otherwise searches for a string from offset 18. `ShowGuides` and `Expendable` are true in every layer; the two fields that are 1 in every sample may hold them. The converter reads the fields only when the name starts at offset 19 (in all little-endian corpus pairs, 1,465 layers). ## Pasted smooth shades (0x5533) Objects of class 0x5533 hold shadings. Chunk 0x5531: a flag byte (1 = built-in key) and the name, then a u32. Chunk 0x5532 holds the shading; a constant shade has 92 bytes, with u32 28 (the length of what follows) at offset 60, then a u32 and three f64. IDML writes it as a `PastedSmoothShade` with `ContentsType="ConstantShade"` and `Contents` the base64 of the same u32 and three f64 in big-endian order (`AAAAAUBv4AAA…` for 1 and 255, 0, 0). Every trustworthy pair has one constant shade in IDML, and it is the class 0x5533 object of lowest UID with a constant shade (495 of 495); in 84 pairs the INDD has two more that IDML leaves out. The converter writes that one shade, and matches the IDML on all values in 495 of 495 (name `$ID/` from flag 1 and an empty name; two contents values, 255 and 1). **Axial shades.** Chunk 0x5532 in general: u32 0 at 0; six f64 at 4 (a matrix); u32 0 at 52; u32 shading type at 56; u32 *n* at 60; *n* bytes of shading data from 64, to the end of the chunk. A constant shade has type 0 and *n* = 28. Type 5 is an axial shade, which IDML writes as `ContentsType="AxialShade"` with `ContentsMatrix` the six f64 and `Contents` the base64 of the shading data with every field converted to big-endian. The data is a sequence of fields: ``` shading: f64 ×6 coordinates u16 ×4 (0 or 1) u32 colour space (2 in every sample) u32 function count F function ×F u32 (4 in every sample) f64 ×6 u32 (2 in every sample) u16 ×2 (0 or 1) function: u32 function type (0 in every sample) u32 inputs, u32 outputs (1 and 1 in every sample) u16, u32 c, f64 ×2c domain u16, u32 c, f64 ×2c range u32, u32 c, f64 ×2c encode u32 c, f64 ×2c decode u16 u32, u32 bits per sample (8 in every sample), u32 byte count B u8 ×B samples, not swapped ``` Over all pairs of the corpus of 2026-10 the INDD files hold 3,725 axial shades (3,704 in 37 trustworthy pairs); the grammar consumes every chunk to its end. IDML has 900 axial shades (891 in 12 trustworthy pairs), and the converter's `Contents`, `ContentsMatrix` and other attributes equal IDML's for all 900. The name chunk is that of constant shades (`$ID/`). The other attributes are those every IDML has on an axial shade (`idml-values.md`), and `SwatchColorGroupReference` from DOM 12 as for other swatches. Type 6 also occurs (43 objects in trustworthy pairs); IDML writes none of them, so its layout is not needed. A shading with another value where the grammar names the value of every sample, or with bytes left over, is left out with a warning. **Which axial shades IDML writes.** The rule of unnamed colours (colours, below): a shade is written when a value in the package refers to it, or when the page item defaults name it (chunk 0x6E07 or entry 0 of chunk 0x6E06, by class 0x5533). In the trustworthy pairs IDML writes 891 of the 3,704 axial shades, all 891 that the rule selects and none of the other 2,813. Of the 891, 884 are referred to by a page item's `FillColor` (attribute 0x6E68 of the item's chunk 0x6E03 holds the shade's UID; 744 rectangles of spreads and 140 of master spreads) and 7 are named by the defaults only. All references are rectangle fills. **Order.** In `Graphic.xml` the shades follow the inks in UID order (the constant shade has the lowest UID), before `Swatch/None`: 12 of 12 trustworthy pairs with axial shades. ## Index sort options Chunk 0x1307E of the preferences object (class 0x2202) holds the index groups. A "string" is a flag byte and an in-object string; a "u32 string" is a u32 length in UTF-16 units and text segments. ``` u32 group count group: string group name (kIndexGroup_Symbol, kWRIndexGroup_GreekAlphabet, ...) u8 include u8 (not identified) u16 header variant u16 (not identified) u32 variant count variant: string internal name string UI string u32 string document string u8 1 = visible u8 (0) u32 section count section: u32 string sorting string u32 string document header string string UI header string u16 language ``` Every chunk of the 654 pairs parses to its end with this layout. The variants and sections have the same fields as the list that IDML writes under `IndexHeaderSetting` (`preferences.md`), which the converter takes from chunk 0x1300E. The converter uses the name, include byte and header variant of each group, and the internal name of the variant that the header variant indexes in the group's variant list. IDML writes an `IndexingSortOption` per group, in the stored order: `Self` `dIndexingSortOptionn` and the name, `Name` `$ID/` and the name, `Include`, `Priority` (the position, from 0) and `HeaderType`, which follows the internal name of the chosen variant: | Variant name | `HeaderType` | |---|---| | `IDX_Basic` | `BasicLatin` | | `IDX_Spanish` | `Spanish` | | `IDX_Czech` | `Czech` | | `IDX_Russian` | `Russian` | | `IDX_AllHira` | `HiraganaAll` | | `IDX_ChinesePinyin` | `ChinesePinyin` | | `IDX_ChineseStroke` | `ChineseStrokeCount` | | `IDX_KoreanConsonant` | `KoreanConsonant` | | the group's own name (symbol, numeric, Greek, Arabic and Hebrew groups) | `Nothing` | The converter leaves `HeaderType` out for other names. Evidence: the 495 trustworthy pairs before 2026-10 have 11 different lists (8 or 10 groups, in different orders, with different groups included). Over the 1,251 trustworthy pairs of the corpus after 2026-10 the converter reproduces all 12,472 `HeaderType` values. The variant index decides where the list order differs: the Chinese group has variant 0 in every file, but in 83 documents its list starts with `IDX_ChineseStroke`, and their IDML has `ChineseStrokeCount` (83 of 83); a table by group and variant number, used before, gave `ChinesePinyin` there. Over all 654 pairs before 2026-10 the Alphabet variant is 0 in 652 files, 3 and 5 in one each, and the Cyrillic variant is 2 in all. ## Numbering lists (0x1A483) Chunk 0x1A49B of the preferences object: u32 UID of the default list, u16 count *n*, *n* list UIDs, i32 −1. Each list has chunk 0x1A49A: a flag byte (1 for a built-in key; 0 and 2 plain) and the name, then u8 `ContinueNumbersAcrossStories` and, two bytes after the name, u8 `ContinueNumbersAcrossDocuments` (1 `true`); the rest is not identified. IDML writes one `NumberingList` per list in `designmap.xml`, the default (`$ID/[Default]`) first, then the *n* lists in stored order, with `Self` `NumberingList/` and the name. The paragraph attribute 0x1A417 (`AppliedNumberingList`, a `Properties` child of type `object`) is the UID of a list. Evidence: 705 of 705 lists of the trustworthy pairs (953 of 953 in all pairs), names, both flags and order; `AppliedNumberingList` 519 of 519 paragraph styles and 489 of 489 text defaults. ## Assignments (0x1BE01) Every document has one object of class 0x1BE01, listed in chunk 0x1BE13 of the document; IDML writes it as an `Assignment` with `Self` its UID (495 of 495 trustworthy pairs). Chunk 0x1BE1B starts with a flag byte and the name in the language of the edition the document was made with (`Unassigned InCopy Content`, `Contenu InCopy non affecté`, …; the converter uses it for values of that edition, `preferences.md`), then two empty strings and fields that are the same in every pair. IDML names it `$ID/UnassignedInCopy` in 476 pairs and gives the stored name in 19, in files of several languages and versions, so the name follows the computer that exported the IDML; the converter leaves `Name` out (it is optional in the schema) and the measurement too. The language code of the save history does not decide it either (document, above). The other attributes are the same in every IDML (`idml-values.md`). ## Articles (0x21B01) **Article list.** Chunk 0x21B03 of the document object: u32 count, then that many article UIDs. IDML writes one `Article` element in `designmap.xml` per listed UID, in list order (not UID order). Evidence: the 19 pairs whose IDML has `Article` elements or `ArticleChildrenList` (DOM 8.0, 10.1, 13.x, 20.5): the list order equals the IDML order for all 31 articles; the chunk is in all 19 (count 0 in the 6 with no articles), and its length is exactly 4 + 4 × count. In all 66 corpus INDD files that have article objects (INDD 7.5 to 21.2, 203 articles), the list holds exactly the document's 0x21B01 objects. **Article object.** | Chunk | Layout | IDML | |---|---|---| | 0x13C | flag byte, in-object string | `Name` | | 0x21B02 | u32 count, then that many page item UIDs | one `ArticleMember` per UID, in order, `ItemRef="u"` | | 0x1A8 | u16 | not mapped (below) | - `Self` is `u`: 31 of 31 in pairs. - Name: the flag byte is 2 in all 203 articles of the corpus, and the string ends at the chunk's end in all 203. 31 of 31 names match in pairs (Latin, Hangul and Han text; segments with 0x40 and 0x80 encodings). - Members: 31 of 31 lists match the IDML `ArticleMember` `ItemRef`s in order. Without chunk 0x21B02 (2 articles) or with count 0 (2 in pairs), the IDML `Article` has no children. In the corpus, members are page items (class 0x6201) or groups (0x401); no other class appears (201 lists). - `ArticleMember` `Self` is the article's `Self`, `ArticleMember`, and the member's index from 0: `u281ArticleMember0`, `u281ArticleMember1`. 44 of 44 members in pairs. No corpus IDML has an article with more than 10 members, so whether the index is decimal or hexadecimal is not shown; the converter writes it in decimal. - `ArticleExportStatus` is `true` on every `Article` of every corpus IDML (31 in pairs; no other value in 3,557 IDML files). No stored field is identified; the converter writes `true` (`idml-values.md`, defaults). - Chunk 0x1A8 (u16) is in 50 of 203 articles: 1 in 47, 0 in 3. The one in a pair has 1 and `ArticleExportStatus="true"`, as do the articles without the chunk; it is not mapped. - No corpus IDML has an `Article` `Properties/Label`, so an article's label chunk is not shown. **Page items.** Chunk 0x21B04 of a page item or group: u32 count and the UIDs of the articles the item is a member of. 44 of 44 members in pairs have count 1 and their article's UID; 331 other items have count 0. IDML has no attribute for it; the converter does not read it. **`ArticleChildrenList` on groups.** Chunk 0x21B05 of a group (class 0x401): u32 count, then UIDs. IDML `Group` has `ArticleChildrenList="u u …"` with these UIDs in order, exactly when the group has the chunk: 68 of 68 groups in 12 pairs; no group has the attribute without the chunk or the chunk without the attribute. The list is the group's children in reverse order of chunk 0x15B in 68 of 68 groups in pairs and 259 of 259 in the files with articles. Groups with the chunk also occur in documents without articles (4 pairs of 13.x). IDML writes the attribute directly after `Self` (68 of 68). **Element order.** In `designmap.xml` the `Article` elements come last, after `Assignment` (all 13 pairs with articles). The schema puts `Article` in the final interleaved group with `Assignment` and others, so writing them after the assignments is valid. `Article` allows `Self`, `Name`, `ArticleExportStatus`, `Properties/Label`, then `ArticleMember*` (`Self` and `ItemRef`, both required). **Versions.** Article objects occur in INDD 7.5 to 21.2. Pairs show DOM 8.0, 10.x and 20.x; no 7.x INDD with articles has an IDML. The converter writes articles for every version. **Damaged input.** The converter writes each listed article once and skips listed UIDs that are not articles. An article without a readable name or member list is left out with a warning. A member that is not written as a page item element is left out with a warning, and so is an `ArticleChildrenList` UID that is not a child of the group. ## Named grids (0xCD12) Chunk 0xCD28: u32, a flag byte (1 = built-in key) and the name (`[Page Grid]`, IDML `$ID/[Page Grid]`). IDML writes a `NamedGrid` per object, in UID order, with a `GridDataInformation`: - A grid made by the user has its own chunk 0xCD02, with the layout of the page chunk (layout grid, below), and IDML writes those settings. - The built-in `[Page Grid]` has only chunk 0xCD28. Its `GridDataInformation` equals the document's layout grid preference (`LayoutGridDataInformation`, chunk 0xCD2F, `preferences.md`) in every attribute that preference has: 803 of 803 pairs of the corpus of 2026-10 (606 trustworthy). It does not always equal the pages' grid: 4 of the 574 trustworthy pairs whose pages all have the same grid differ. The converter writes each named grid with its own settings, or with the layout grid preference. Over all pairs: 876 of 876 named grids, and every attribute of their `GridDataInformation` (`AppliedFont` 875: one family that IDML names in another form, `fonts.md`); 38 pairs have grids made by the user (2 or 3 named grids). **Applied named grid (chunk 0xCD32).** A story (class 0x201) or object style (class 0x1B901) that uses a named grid has chunk 0xCD32: u16 1, then the UID of the named grid object (6 bytes). An object style can also have u16 0 (2 bytes). IDML writes `AppliedNamedGrid` as the grid's `Self` (`NamedGrid/`, `NamedGrid/$ID/[Page Grid]` for the built-in grid), and `n` for u16 0 or without the chunk. Evidence over all pairs of the corpus of 2026-10, compared by `compare.py`: - Stories: 26,805 of 26,805 (19,424 of 19,424 in the trustworthy pairs). 106 IDML stories name a grid made by the user (two grids, in 13 pairs of version 12.1), and their INDD stories have the chunk with that grid's UID. - Object styles: 3,515 of 3,515 (2,661 of 2,661 trustworthy). 55 name `[Page Grid]` (u16 1); the others have u16 0 or no chunk and `n`, among them all root styles `[None]` but one, which names `[Page Grid]`. Over the whole little-endian corpus, every story chunk 0xCD32 has 6 bytes, u16 1 and the UID of a named grid object. The converter writes `n` for u16 0 or no chunk, and the grid's `Self` for u16 1 with the UID of a named grid of the document; otherwise it leaves the attribute out. ## Table of contents styles (0x11605) Chunk 0x11605: a flag byte (1 = built-in key) and the name, a u16 (0 in every style), a u16 `IncludeBookDocuments` and a u32 `CreateBookmarks` (1 = true), the u32 UID of the title's paragraph style, a flag byte and the title, a flag byte and a string not identified, then u16 fields: at 0 `SetStoryDirection` (0 `Horizontal`, 1 `Vertical`), at 2 `NumberedParagraphs` (0 `IncludeFullParagraph`, 2 `ExcludeNumbers`), at 4 `MakeAnchor` and at 6 `RemoveForcedLineBreak` (1 = true; the chunk of older files ends before the fields IDML does not have yet), then the entries. IDML writes one `TOCStyle` per object in `Styles.xml`. A story made by a table of contents has chunk 0x8C40, the UID of an object of class 0x8C20 whose chunk 0x11613 is the TOC style; IDML writes that style as the story's `AppliedTOCStyle`, and `n` for other stories. Evidence over the trustworthy pairs: the 511 TOC styles the converter writes match on `Name`, `Title` and `TitleStyle` (511 of 511), `NumberedParagraphs` (511, one `ExcludeNumbers`), `SetStoryDirection` (620 of 620 in the corpus of 2026-10; over all its pairs 823 of 824, 2 `Vertical` in two pairs of version 18), `MakeAnchor` (445 of 445 from DOM 9 on, 4 true) and `RemoveForcedLineBreak` (340 of 340 from DOM 13 on, 4 true); `AppliedTOCStyle` matches for 18,923 of 18,923 stories (21 made by a table of contents). `IncludeBookDocuments` and `CreateBookmarks` match all 1,520 TOC styles of the corpus of 2026-10 whose IDML style has the same name (1,304 trustworthy; 15 with `IncludeBookDocuments="true"`, one with `CreateBookmarks="false"`, in a stale pair). The flagged string after the title is the path of a book file in some of these styles, but it does not decide `IncludeBookDocuments`. **Entries.** After the u16 fields (two before version 9, three before 13, four from 13) the chunk holds a u16 entry count and the entries: | Field | IDML | |---|---| | flag byte, in-object string | `Name`: the paragraph style's name, with `$ID/` first for flag 1; the group separator U+E00B is written `:` | | flag byte, in-object string | a document path; not in IDML | | u32 | `FormatStyle`: paragraph style UID | | u16 | `Level` | | u16 | 0 in every entry of the pairs; 1 in some entries of files without a pair (not mapped) | | u16 | `PageNumberPosition`: 0 `AfterEntry`, 1 `BeforeEntry`, 2 `None` | | u32 | `PageNumberStyle`: character style UID | | u32 *n*, segments | `Separator` | | u32 | `SeparatorStyle`: character style UID | | u16 | 0 in every entry | | u32 *n*, segments | `; `, `;` or `、`; not in IDML | | u16 | 1 in every entry | IDML writes each entry as a `TOCStyleEntry` child of its `TOCStyle`: `Self` is `u`, the TOC style's UID in hexadecimal, `TOCStyleEntry` and the entry's index (0 to 5 in the samples, so whether the index is hexadecimal is not shown), then `Name`, `Level`, `PageNumberPosition`, `Separator`, `SortAlphabet="false"` (every entry), and `Properties` with `FormatStyle`, `PageNumberStyle` and `SeparatorStyle`: a user style as `type="object"` and its `Self`; the root style as `type="string"` `$ID/[No paragraph style]` or `$ID/[No character style]`; UID 0 as `type="string"`, `$ID/kSameStyleWithBracket` for `FormatStyle` and `$ID/[Same Style]` for the other two. `Separator` is `$ID/` and the text, with tab written `^t`, U+0008 `^y`, line feed `^n`, U+2003 `^m` and U+2014 `^_`. Evidence: every TOC style with entries in the trustworthy pairs (versions 8.0 to 21.3) parses to the end of the chunk with this layout, and the converter reproduces all 255 `TOCStyleEntry` elements with all their values: `Name`, `Level`, `PageNumberPosition`, `Separator`, `SortAlphabet` and the three style properties, 255 of 255 each. `SortAlphabet` is `false` in all; neither constant u16 can be shown to hold it. A chunk whose entries do not parse to its end keeps its style and loses its entries, with a warning. Two TOC styles of the corpus pairs have another value at the `SetStoryDirection` field: 0x40 with no `SetStoryDirection` in the IDML (version 15.0) and 0xF6FC with `Horizontal` (a stale pair of version 12.0). The converter writes the attribute for 0 and 1 only. ## Languages (0x2D07) Chunk 0x2D0F: a flag byte and the name (`English: USA`), a flag byte and the primary name (`English`), a flag byte and the secondary name (`USA`, often empty), u16 `Id`, then two vendors, each a flag byte, a u32 and a string: first the spelling vendor, then the hyphenation vendor (`Hunspell`, `Proximity`, `Duden`, `WinSoft`); last the locale (`en_US`). IDML lists the language objects in UID order as `Language` elements at the start of `designmap.xml`: `Name`, `PrimaryLanguageName` and `SublanguageName` are `$ID/` and the stored names, except that the language named `Neutral` is `$ID/[No Language]` in all three. Evidence over the trustworthy pairs: in 488 of 495 the IDML list equals the INDD objects in that order; in the other 7 the INDD has 4 to 61 language objects and the IDML lists only some of them (1 or 2), for no reason found. For the 1,249 IDML languages the names and `Id` are equal in 1,249. `HyphenationVendor` is the second vendor and `SpellingVendor` the first when their flag is 1 (1,146 and 1,166 of 1,249); otherwise the stored string is empty or names a vendor the IDML does not show, and IDML gives `$ID/`, `$ID/InDihyph` or a vendor such as `Hunspell` that depends on the computer that exported it. **Vendors without a stored name.** Over the 3,139 language objects of the corpus of 2026-10 matched with an IDML `Language` by name, an empty vendor (flag 0, u32 0x01000000, empty string) is written `$ID/` for every language whose vendor of that kind is never stored with flag 1 anywhere in the corpus: hyphenation 914 of 914 (847 trustworthy), spelling 894 of 895 (the other, in a stale pair, has `Hunspell`). For the languages that have a stored vendor in some document, an empty vendor is written as the exporting computer's dictionary (`Hunspell` 14 and 19, `Duden` 1 and 1, `$ID/` 1 and 1), which no stored value shows; the converter leaves those out. The two lists of languages without a stored vendor: - Hyphenation (33): `Albanian`, `Arabic`, `Byelorussian`, `Chinese: Hong Kong`, `Chinese: Simplified`, `Chinese: Taiwan`, `Chinese: Traditional`, `Icelandic`, `Japanese`, `Korean`, `Neutral`, `Vietnamese`, `cy_GB`, `en_US+Medical`, `eu_ES`, `fo_FO`, `fy_NL`, `ga_IE`, `gl_ES`, `gv_GB`, `ko_KR`, `la_VA`, `lb_LU`, `mk_MK`, `mn_MN`, `ms_MY`, `ne_NE`, `oc_FR`, `sr_RS`, `sr_RS-Cyr`, `sr_RS-Lat`, `tk_TM`, `uz_UZ`. - Spelling (29): the same without `Arabic`, `Vietnamese`, `en_US+Medical` and `ko_KR`. Two other hyphenation vendor flags occur: flag 3 with the text `DefaultLinguistic` is written `$ID/` (24 of 24, Korean and Chinese: Traditional), flag 10 with `InDihyph` is written `$ID/InDihyph` (2 of 2). Two spelling vendors with flag 1 and the text `InDitect` are `Hunspell` in IDML; the converter writes the stored text for flag 1. **Quotes (chunk 0x2D26).** 34 language objects of the corpus have a second chunk 0x2D26: a flag byte, the language name as an in-object string, then four UTF-16 units, the two single quotes and the two double quotes. IDML writes them as `SingleQuotes` and `DoubleQuotes`: 34 of 34 (Japanese `‘’` `“”`, Romanian `‘’` `»«`, Hebrew `’’` `””`, Korean). Without the chunk, the quotes come from observation (`idml-values.md`, language quotes). ## Interface colours (0x1F11) Layers, pages and XML tags name their colour by the UID of an object of class 0x1F11. Chunk 0x1F01 of that object: u32 5 (RGB), u16 3, three f64 fractions. IDML writes the colour as one of its named colours, or, for other colours, as a list of three numbers (0 to 255): | Red, green, blue | IDML | Red, green, blue | IDML | |---|---|---|---| | 0.31, 0.6, 1 | `LightBlue` | 1, 0.6, 0 | `Gold` | | 1, 0, 0 | `Red` | 1, 0.4, 0 | `Orange` | | 0.31, 1, 0.31 | `Green` | 0, 0.33, 0 | `DarkGreen` | | 0, 0, 1 | `Blue` | 0.6, 0.6, 1 | `Lavender` | | 1, 1, 0.31 | `Yellow` | 0.67, 0.64, 0.71 | `Charcoal` | | 1, 0.31, 1 | `Magenta` | 0.6, 0.2, 1 | `Violet` | | 0, 1, 1 | `Cyan` | 1, 1, 1 | `White` | | 0.5, 0.5, 0.5 | `Gray` | 1, 0.6, 0.8 | `Pink` | | 0, 0, 0 | `Black` | 0.61, 0.87, 0.61 | `GridGreen` | | 0.6, 0, 0 | `BrickRed` | 0, 0, 0.53 | `DarkBlue` | | 0.6, 0.8, 0 | `GrassGreen` | 0.81, 0.51, 0.71 | `Lipstick` | | 1, 0.71, 0.42 | `GridOrange` | 0.97, 0.35, 0.42 | `Fiesta` | | 0, 0.6, 0.6 | `Teal` | 0.73, 0.73, 0.73 | `LightGray` | | 0.48, 0.73, 0.85 | `GridBlue` | 1, 0.6, 0.6 | `Peach` | | 0.6, 0, 0.2 | `Burgundy` | | | Evidence: in the layers and pages of the trustworthy pairs every colour object with these components has this name (1,107 layers and pages; from 491 `LightBlue` down to 1 `Teal`). The last four are from the guide, grid and pasteboard colours of the preferences (`preferences.md`), where every colour with these components has this name. Two other colours occur, 1, 0.4863, 0.651 (3 layers) and 0.9412 three times (1 layer); IDML writes them as the lists 255, 124, 166 and 240, 240, 240, the components times 255. The converter writes such a list for a colour whose components are whole multiples of 1/255 and that is not in the table, and leaves the colour out otherwise. XML tags use the table but no list, as before. ## Pages (0x50F) | Chunk | Contents | |---|---| | 0x140F | u32 applied master spread, u16 (unknown), matrix `MasterPageTransform` | | 0x5CC | Matrix: `ItemTransform` | | 0x5DD | Four f64: left, top, right, bottom. IDML `GeometricBounds` is top, left, bottom, right. | Files from InDesign 3.0 and 4.0 store the page transform and bounds in chunks 0x151 and 0x154, and chunk 0x140F has no matrix (`big-endian.md`). **Page settings.** | Chunk | Contents | IDML | Without the chunk | |---|---|---|---| | 0x1404 | u32 count; if not 0, a UID list of master page items and a UID list of their overrides | `OverrideList`: each item and its override (`n` for 0) | empty | | 0xCD04 | 6 bytes: u32 at 0, then u16 | `GridStartingPoint` (u32: 0 `TopOutside`, 4 `CenterVertical`; below), `UseMasterGrid` (u16, 1 = true) | | | 0x563 | u32, u32 code: 1 `Recenter`, 2 `ObjectBased`, 3 `Scale`, 4 `GuideBased`, 5 `UseMaster` | `LayoutRule` | `Off` | | 0x5FF | u32: 0 `Nothing`, 1 `UseMasterColor`, else an interface colour | `PageColor` | `UseMasterColor` | Evidence over the 9,366 pages (document and master pages) of the trustworthy pairs that the converter writes: `OverrideList`, `UseMasterGrid` and `PageColor` 9,366 of 9,366 each, `LayoutRule` 9,347 of the 9,347 pages that have it. Of the 9,367 IDML pages, 1,352 have overrides and 2,445 `UseMasterGrid="false"`; 5,111 have no chunk 0x563 and `Off`, 4,117 `UseMaster`, 92 `ObjectBased`, 24 `GuideBased`, 3 `Recenter` and 1 `Scale`; 9,260 have no chunk 0x5FF, 44 code 1, 5 code 0 and 58 an interface colour. `LayoutRule` is in IDML from DOM 8 on (no page of the DOM 7 files has it). **Layout grid starting point.** The u32 at 0 of chunk 0xCD04 is `GridStartingPoint`. Over all pairs of the corpus of 2026-10, 13,458 of 13,458 pages that the converter writes match (10,080 of 10,080 in the trustworthy pairs): 13,430 have 0 and `TopOutside`, 28 have 4 and `CenterVertical` (master pages in 14 pairs of version 18.4). Every page in the corpus has the chunk. Codes 1, 5 and 6 occur only in files without an IDML (versions 3, 6, 19 and 21), so their values are not shown; the converter leaves the attribute out for them. | 0x51A | Margins: four f64 (left, top, right, bottom), u16 own-margins flag | | 0x528 | Columns: u32 count *n*, *n* f64 column edges, f64 gutter, u16 own-columns flag, u16, u16 column direction (1 vertical, 0 horizontal) | | 0xCD02 | Layout grid (`GridDataInformation`, below) | **Master spread colour.** IDML gives each `MasterSpread` a `PageColor` property equal to the `PageColor` of its pages, which is the same for all pages of a master in all 1,359 master spreads of the 654 pairs (`UseMasterColor` in 1,326, a named colour or `Nothing` in the others). The converter writes the pages' colour. Over all pairs this reproduces 1,352 of 1,352 compared values. **Margins and columns.** IDML writes them as `MarginPreference`. The column edges are `ColumnsPositions` (left and right edge of each column, so `ColumnCount` is *n*/2), the gutter is `ColumnGutter`. A flag of 1 means the page has its own values. With 0, or without the chunk, the page shows the values in effect on its master page: the page at the same position, counted from the left by `ItemTransform`, in the applied master spread, or that spread's only page. That master page can itself take them from its own master. The stored values of such a page are often stale. Evidence: all 325 IDML pages of the same-version pairs that have an INDD page with the same UID (document and master pages): - Margins: 156 pages with flag 1 match their own values; 169 take them from a master page (146 directly, 23 through two or three masters) and match. 325 of 325. Without the rule, 72 pages with flag 0 differ. - Columns, gutter and positions: 156 with flag 1, 169 from a master page, 325 of 325. Without the rule, 83 differ. - The order of the margins: the four values differ in many pages (for example 56.69, 42.52, 56.69, 56.69 with IDML `Top="42.51968503937008"`). In the 250 distinct little-endian files, 2,246 pages hold 0x51A in 2,160 (34 bytes each, flag 0 or 1) and 0x528 in 2,211 (always 18 bytes after the positions). The last u16 of 0x528 is the column direction: IDML `ColumnDirection` is `Vertical` where it is 1 and `Horizontal` where it is 0, taking it from the master page with the columns (above). Evidence: 9,274 of 9,274 pages of the trustworthy pairs (14 vertical, in 3 documents), 12,224 of 12,224 over all pairs. A page whose flag is 0 takes the direction from its master page: 44 such pages store 1 but have horizontal columns in IDML, like their master pages. **Layout grid (chunk 0xCD02).** u32 font family UID, a flag byte (1 = built-in key, `$ID/`), the font style as an in-object string, five f64, four u32. IDML writes `GridDataInformation`: | Field | IDML | Evidence | |---|---|---| | Font family | `AppliedFont` (Properties, the family name) | 283 of 325 (below) | | Font style | `FontStyle` | 325 of 325 (`Regular`, `Roman`) | | f64 1 to 5 | `PointSize`, `CharacterAki`, `LineAki`, `HorizontalScale` (×100), `VerticalScale` (×100) | see below | | u32 1 to 4 | `LineAlignment`, `GridAlignment`, `CharacterAlignment`; one not identified | see below | In the first corpus the numbers were 12, 0, 9, 1, 1 and the codes 3, 0, 3, 1 in all 2,246 pages of the 250 distinct little-endian files, and all 832 `GridDataInformation` elements of the corpus IDML files had `PointSize="12"`, `CharacterAki="0"`, `LineAki="9"`, scales of 100, `LineAlignment="LeftOrTopLineJustify"` and `AlignEmCenter` for both alignments. The numbers 12 and 9 occur once, so those fields were located. The corpus of 2026-10 confirms them: pages with other sizes (such as 9.2126 and 6.9094) are in many pairs typeset in Japanese, and `PointSize` and `LineAki` match in 13,458 of 13,458 pages of all pairs. Named grids made by the user (above) locate `CharacterAki`: 37 of them over all pairs have a second f64 other than 0 (such as −0.354), and their IDML has the same value in 37 of 37. With the flag, `FontStyle` matches in all 13,458 pages (44 trustworthy pages have flag 1 and IDML `$ID/` before the style). The scales and the codes still have one value only: the converter writes the scales only when they are 1, and the three alignments only when the codes are 3, 0, 3, 1. The other 42 pages are in one pair whose IDML names the family `Minion Pro (OTF)` where the INDD family name is `Minion Pro`; its `FontFamily` element has the same difference (`fonts.md`). **Applied master of a master page.** Pages of master spreads use the same chunk 0x140F. When it is absent or names UID 0, IDML writes `AppliedMaster="n"` and `MasterPageTransform="1 0 0 1 0 0"` (220 of 220 master pages in the corpus pairs; 2 of them have the chunk with UID 0 and an identity matrix). In 147 master pages of 73 distinct INDD files without an IDML, chunk 0x140F names another master spread of the same document, never the page's own spread. No pair shows the IDML for this case; the converter writes it as `AppliedMaster`, as for document pages. ## Page items Frames, shapes and lines are all class 0x6201; groups are 0x401. | Chunk | Contents | |---|---| | 0x151 | Matrix: `ItemTransform` | | 0x40D | Groups without chunk 0x151: the matrix (`ItemTransform`) | | 0x162B | Path geometry (below) | | 0x15B | Hierarchy: u32 spread, u32 parent, UID list of children | | 0x6E03 | Attribute list: local formatting (see `attributes.md`) | | 0x1B916 | u32: applied object style | **Content type.** A rectangle, oval or polygon with a placed graphic is `ContentType="GraphicType"`. Without one, chunk 0x1623 (u16) decides: 1 is `GraphicType` (an empty graphic frame), 0 or no chunk `Unassigned`. In 200 of the trustworthy pairs, the 757 empty shapes with `GraphicType` all have 1, and the 15,768 with `Unassigned` have 0 (13,226) or no chunk. Groups store their matrix in chunk 0x40D when they have no chunk 0x151: with it, the `ItemTransform` of all 5,670 groups of the trustworthy pairs (8,788 of 8,789 over all pairs) matches; without it, 5,277 of them were written as the identity, though they have a translation in IDML. **Path geometry:** u32 path count; per path: u32 point count, points, u16 1 if the path is open. Each point is a u32 type, then f64 values: type 2 = anchor (x, y) only; types 0 and 1 = left direction, anchor, right direction. All 3,191 path chunks in the samples parse exactly, and the points of every page item path match the IDML `PathPointType` values. **IDML element type.** A frame is a `TextFrame` if a child of class 0x263 exists. Otherwise chunk 0x6204 (u16, then u32) gives a shape code: 1 `GraphicLine`, 2 or 3 `Rectangle`, 4 or 5 `Oval`, 0 and 6 to 8 `Polygon`; code 9 means none. With code 9 or without the chunk the converter uses the path: two points and open → `GraphicLine`; four corner points on two distinct x and y values → `Rectangle`; four points with direction points, all of point type 0 → `Oval`; anything else → `Polygon`. Path points of type 1 have direction points too, and a closed four-point path with one or more of them is a `Polygon` in IDML (1,672 shapes in the trustworthy pairs). Evidence over the 62,908 rectangles, ovals, polygons and lines of the trustworthy pairs: the code gives the IDML element for 22,579 of 22,587 shapes that have one (7 polygons have code 2 or 3, 1 rectangle code 4); the path for 40,070 of 40,321 without one. Most of the others (215) are polygons whose four points of type 0 form an ellipse, as ovals do. **Text frames:** the frame's child of class 0x263 (multi-column frame) has a child of class 0x227 (frame column). Chunk 0x220 of the column holds the frame list (class 0x228). Its chunk 0x205: u32 story, then a UID list of the columns of all threaded frames in order, which gives `PreviousTextFrame` and `NextTextFrame`. **Page item labels.** A page item's `Properties/Label` has the layout and rule of the document label: u16 0x7B7B, u32 count, then flagged key and value strings, written in stored order as stored. It is stored in: | Item | Chunk | Offset | |---|---|---| | Frames and shapes (class 0x6201) | 0x6204, after the shape code | 6 | | Groups (0x401) | 0x2C0B | 0 | | Buttons (0x1450C) and multi-state objects (0x145D8) | 0x14582 | 0 | Without these bytes the item has no `Label`. In `Properties` the `Label` comes first. Evidence: all 278 labelled items of these kinds in the IDMLs of all pairs (258 in 27 trustworthy pairs: 108 rectangles, 44 text frames, 40 lines, 36 groups, 18 buttons, 9 multi-state objects, 3 polygons) have the bytes and match key for key; no item has the bytes without a `Label` in IDML. Flag 1 appears on labelled frames whose IDML keys and values have no `$ID/` prefix. ## Page item settings Every page item element in IDML (`TextFrame`, `Rectangle`, `Oval`, `Polygon`, `GraphicLine`, `Group`) has the attributes below. Each is stored in a chunk of the item (class 0x6201 or 0x401), or in its attribute list (chunk 0x6E03, `attributes.md`); without the chunk the IDML value is the default in the table. | Chunk | Contents | IDML | Without the chunk | |---|---|---|---| | 0x2C10 (groups: 0x418) | u8 1 if a built-in key, then an in-object string | `Name`: the string, or `$ID/` and the key | `$ID/` | | 0x2C32 | u16, 0 = hidden | `Visible` | `true` | | 0x2C2D | u32, 1 = locked | `Locked` | `false` | | 0x142D | u32, 1 = overrides not allowed | `AllowOverrides` on items of master spreads (the spread in chunk 0x15B is a master spread): `false` for 1, else `true`; no item of a document spread and no placed graphic has it (11,791 of 11,791 trustworthy items) | `true` on master spreads | | 0x21D4E | u32 count *n*, *n* pairs of u32 | `ParentInterfaceChangeCount`: the numbers | empty | | 0x21D50 | same | `TargetInterfaceChangeCount` | empty | | 0x21D53 | same | `LastUpdatedInterfaceChangeCount` | empty | | 0x1424 | u32 master page item, UID list | `OverriddenPageItemProps`: the list in decimal; empty if the master item is 0 | empty | | 0x22228 | u8 flags | `HorizontalLayoutConstraints` (bits 4–6), `VerticalLayoutConstraints` (bits 0–2): per bit `FixedDimension` if set, else `FlexibleDimension` | as 0x22 | | 0x324 | u32 at 0, a layer or spread layer UID | `BeforeGroupingLayerReference`, from 21.4 (below) | none | | 0x324 | i32 at 4 | `BeforeGroupingLayerPosition`, after `ItemTransform`, from 21.4 (also on placed graphics) | `-1` | | Attribute | IDML | Without the attribute | |---|---|---| | 0x5520, 0x551F, 0x551E | `GradientFillStart`, `GradientFillLength`, `GradientFillAngle` | `0 0`, `0`, `0` | | 0x5526, 0x5525, 0x5524 | `GradientStrokeStart`, `GradientStrokeLength`, `GradientStrokeAngle` | same | | 0x5522, 0x5523 | `GradientFillHiliteLength`, `GradientFillHiliteAngle` | `0` | | 0x5528, 0x5529 | `GradientStrokeHiliteLength`, `GradientStrokeHiliteAngle` | `0` | Rules the IDML follows, over the 92,409 page item elements of the 495 trustworthy pairs: - `Locked` is on the 36,817 items that are children of a spread or anchored in text, and on none of the 55,592 items inside a group or another page item. - `ItemLayer` is on the 35,276 items that are children of a `Spread` or `MasterSpread` (35,276 of 35,276), and on none of the items inside a group (48,289 of 48,291), inside a rectangle, oval or polygon (2,925 of 2,925) or anchored in text (1,999 of 1,999). The layer of a nested item always equals its parent's. Counted over the 489 trustworthy pairs of the corpus without the privately held samples. - The change counts, `OverriddenPageItemProps` and the layout constraints are in IDML from DOM 8 on: every page item of the distinct corpus IDML files from DOM 8 on has them, none of the DOM 7 files does (145 text frames, 335 rectangles, 61 groups). - A group has no gradient attributes of its own (no group in the pairs has 0x551E to 0x5529). IDML gives it, for each gradient attribute, the value all its child items have, and leaves the attribute out when they differ: 58,068 of the 58,080 gradient values of the 5,808 groups. Evidence: the INDD values, decoded as above, against the IDML of the same item, over the page items of the trustworthy pairs that have an INDD object: | IDML attribute | Equal | |---|---| | `Name` | 92,264 of 92,402 | | `Visible` | 92,402 of 92,402 | | `Locked` | 36,810 of 36,810 | | The three change counts and `OverriddenPageItemProps` | 91,812 of 91,812 each | | Both layout constraints | 91,812 of 91,812 each | | Gradient start and length, fill and stroke (four attributes), items other than groups | 86,594 of 86,594 each | | Gradient angles and highlights (six attributes) | all, 92,396 to 92,402 per attribute (groups by the rule above) | The 138 names that differ belong to groups that are the states of form fields; IDML names them by their state type (form fields). Values other than the default: 206 items with a name given by the user, 176 hidden, 1,241 with change counts, 1,113 with overridden properties. Flags 0x22 are in 66,820 items, 0x55 (`FixedDimension FlexibleDimension FixedDimension` both ways) in 643 and 0x02 (`FlexibleDimension` three times horizontally) in 13; with another bit set the converter leaves both attributes out. In 14,918 items with chunk 0x1424 the master item is 0; in 197 of them the list still holds IDs, and IDML writes the attribute empty. **Layer position before grouping.** From 21.4 every page item and placed graphic in IDML has `BeforeGroupingLayerPosition`; no IDML before 21.4 has it (13 distinct corpus IDML files from 21.4, none of the 1,673 earlier ones). Its value is the i32 at 4 of chunk 0x324, or −1 without the chunk. The converter writes it for files saved by 21.4 or later: 1,793 of 1,793 values over the trustworthy pairs (1,308 polygons, 241 text frames, 131 rectangles, 53 groups, 38 images, 16 EPS, 3 graphic lines and 3 SVG). Chunk 0x324 of page items and placed graphics: | Offset | Contents | IDML | |---|---|---| | 0 | u32 UID: 0, a layer (class 0x302) or a spread layer (class 0x301) | `BeforeGroupingLayerReference` | | 4 | i32 | `BeforeGroupingLayerPosition` | | 8 | u8 0, 1 or 2, then bytes not decoded (in some items an in-object string with a layer name) | | `BeforeGroupingLayerReference` is the layer the UID gives: the UID itself for a layer, the u32 at 0 of the spread layer's chunk 0x302 for a spread layer. 0, no chunk or another class give no attribute. Over the 1,831 page items and placed graphics of the 12 DOM 21.4–21.5 pairs: | Chunk 0x324 | IDML | Items | |---|---|---| | u32 at 0 is 0 | no `BeforeGroupingLayerReference` | 1,667 of 1,667 | | a spread layer | that spread layer's layer | 36 of 36 | | a layer | that layer | 14 of 15 | | no chunk | no reference, position −1 | 113 of 113 | The exception is one rectangle on a master spread whose chunk names a layer and position 1; its IDML has position −1 and no reference. No field that sets it apart was found. The converter's output has 50 of the 50 references of the pairs (19 polygons, 15 rectangles, 10 text frames, 6 groups) and no other. **Stroke weight without an attribute.** An item whose attribute list has no stroke weight (attribute 0x6E65) takes it from the document's base list (class 0x6E02, `attributes.md`, values an item does not store): 1 in most documents, 0.2835 in others. IDML writes it when it differs from the object style's, so items with `[None]` (stroke weight 0) have it. Groups vary (1,166 without the attribute, 625 with 1 and 30 with other values, in 200 of the pairs) and get none. **Overprint.** The attribute list of a page item holds the overprint flags of the fill (0x6E6A) and the stroke (0x6E67), u16 1 `true`, as in object styles (object style settings). IDML writes `OverprintFill` on a rectangle, oval, polygon, graphic line, text frame or EPS text when its fill puts ink on the page, by the object style rule: the fill colour (the item's 0x6E68, else its object style's) is not `None`, `Paper` or a white process colour, and the tint is not 0. The value is the item's flag; an item without the flag has `false`, which IDML writes only when the object style turns the fill category off (as for the fill colour, attributes.md, values an item does not store). `OverprintStroke` follows the same rule with the stroke colour (0x6E64), tint (0x6E66) and category. Trustworthy pairs: the rule reproduces all 3,315 `Polygon`, 64 `Rectangle`, 33 `TextFrame`, 33 `EPSText` and 3 `Oval` `OverprintFill` values and all 68, 30, 20, 2 and 1 `OverprintStroke` values of polygons, graphic lines, rectangles, ovals and text frames, with no extra value. Of the items that store 0x6E6A, 27 have a `Paper` fill and no `OverprintFill`. A group stores no flag. IDML gives it the flag that its children share, counting only children whose fill (for `OverprintFill`) or stroke (for `OverprintStroke`) puts ink on the page by the rule above: the child's own flag, `false` without one; for a child group, its shared flag. A group whose children with ink differ, or that has none, gets no attribute, and neither does a group whose shared flag equals its object style's value (`false` where the style has none). In documents saved by 21.4 or later, where every object style has the overprint attributes (object style settings, overprint), children without ink count too, with their own flag, else their style's value. Over all 1,462 pairs this gives 320 of 320 `OverprintFill` values of groups (313 in 82 trustworthy pairs) and 2 of 2 `OverprintStroke` values, all `true`, with no extra value; 4 more are written in a stale pair on groups that its IDML does not have. 75 of the 320 groups have a child that has no attribute in IDML. The 21.4 case rests on one group of one trustworthy pair, whose child without ink has the style value `false` and whose IDML has no attribute; before 21.4 a child whose stroke is `Paper` under a style with `OverprintStroke="false"` does not count (one group). ## Object export options Page items (class 0x6201 and groups, 0x401) may have chunk 0x1E206, the alternative text and tagging settings that IDML writes in `ObjectExportOption`: | Size | Contents | IDML `ObjectExportOption` | |---|---|---| | 4 | u32 alternative text source: 0 `SourceCustom`, 5 `SourceXMLStructure`, 8 `SourceDecorativeImage` | `AltTextSourceType` | | flagged string | custom alternative text | `CustomAltText` | | flagged string | | `Properties/AltMetadataProperty NamespacePrefix` | | flagged string | | `Properties/AltMetadataProperty PropertyPath` | | 4 | u32 actual text source: 0 `SourceCustom`, 5 `SourceXMLStructure`, 6 `SourceXMPAltText` | `ActualTextSourceType` | | flagged string | custom actual text | `CustomActualText` | | flagged string ×2 | | `Properties/ActualMetadataProperty` (as above) | | 4 | u32 tagging: 0 `TagFromStructure`, 1 `TagArtifact` | `ApplyTagType` | | rest | 64 bytes (DOM 8–9), 83 (10–20), 88 (21) | not decoded | A flagged string is a u8 (1: a built-in key, written `$ID/` and the string; 2: a plain string) and an in-object string. An empty key is `$ID/`. Evidence: 69,814 page items with the chunk in the trustworthy pairs; all eight values match in 69,814 of 69,814 (other than the default: 30 custom and 199 decorative alternative text sources, 197 custom and 14 XMP actual text sources, 205 artifacts, 31 custom alternative texts, 197 custom actual texts). The 22,005 items without the chunk all have the values `SourceXMLStructure`, `$ID/` and `TagFromStructure`. The chunk parses in all 363,145 page items of the distinct little-endian corpus files. The converter leaves out an attribute whose code is not in the table. Which other attributes `ObjectExportOption` has depends on the version (`idml-values.md`, export options of page items). **Object styles** (class 0x1B901) may have chunk 0x1E206 with the same layout, and IDML writes its first eight values in the style's `ObjectExportOption` the same way. In the 1,503 pairs outside the privately held samples, 3,643 object styles have the chunk: 20 store tagging 1 and have `ApplyTagType="TagArtifact"` (20 of 20, in 2 DOM 20 pairs); all others store the default values and have them in IDML. One privately held sample adds 1 object style with source 0 and a custom alternative text: `AltTextSourceType="SourceCustom"` and the text in `CustomAltText` (1 of 1). Styles without the chunk have the defaults. **Export size (chunk 0x1E22B).** A list of keys and values: u32 count; per entry a u32 length and the key in text segments, a flag byte, two bytes, a u16 value count and the values, each a u8 length, a u8 and that many bytes of text. The keys are `UseOrigImage`, `CustomSizeType`, `CustomSizeValue` and `CustomContentConversion`. `CustomSizeType` is IDML `SizeType`: `1` `DefaultSize`, `2` `FixedSize`, `3` `RelativeToTextFlow`, `4` `RelativeToTextSize`; an item without the chunk has `DefaultSize`. **Generated alternative text.** From 21.4 page items have three more chunks: | Chunk | Contents | IDML `ObjectExportOption` | |---|---|---| | 0x1E260 | u16: 1 `true`, 0 `false` | `AltTextGenerationError` | | 0x1E261 | four f64 | `AltTextCropSyncRect`: empty when all four are 0, else the four numbers (below) | | 0x1E262 | u16: 1 `true`, 0 `false` | `AIGeneratedAltText` | Without the chunk the values are `false`, `false` and empty; the converter also writes these for another u16 or a chunk too short for its values. Evidence, the 1,701 page items other than groups of the 12 DOM 21.4–21.5 pairs: `AltTextGenerationError` 1,701 of 1,701 (22 `true`), `AIGeneratedAltText` 1,701 of 1,701 (3 `true`), `AltTextCropSyncRect` 1,701 of 1,701 (8 not empty, two distinct rectangles). Items with a `true` flag have `AltTextSourceType="SourceCustom"` and the generated or error text in `CustomAltText` (chunk 0x1E206); the tail of chunk 0x1E206 is the same in all of them and does not hold the flags. Object styles have `AltTextCropSyncRect=""` (49 of 49). `AltTextCropSyncRect` writes each f64 with exactly 8 decimals, cut, not rounded, from the shortest decimal form, joined by commas: `415.60850393700787` gives `415.60850393` (rounding would give `…394`), `306.7741732283466` gives `306.77417322`, `370.32` gives `370.32000000`. **Groups** have no `AIGeneratedAltText` and `AltTextGenerationError` in their `ObjectExportOption`: none of the 53 groups of the 7 trustworthy documents from 21.4 has them. The converter leaves them out on groups, which removes 106 extra values. ## Text wrap Page items (class 0x6201 and groups, 0x401) and placed graphics have chunk 0x3703 (44 bytes): | Offset | Contents | |---|---| | 0 | u32 wrap mode: 0 `None`, 1 `JumpObjectTextWrap`, 3 `BoundingBoxTextWrap`, 6 `Contour` | | 4 | u32 UID of the wrap path object (class 0x3702), or 0 | | 8 | Four f64 offsets: left, top, right, bottom (IDML `TextWrapOffset`) | | 40 | u16 master page flag: 1 `ApplyToMasterPageOnly="false"`, 0 `true` | | 42 | u16 `TextWrapSide`: 0 `BothSides`, 1 `LeftSide`, 2 `RightSide`, 3 `SideTowardsSpine`, 4 `SideAwayFromSpine`, 5 `LargestArea` | **Wrap path.** Every item with wrap mode 1, 2, 3 or 6 (and the codes 0x10003 and 0x10006) has a wrap path object; mode 0 items have none (9,061 objects in 735 files of the corpus of 2026-10). The u32 at 4 equals the object's UID in 8,824 of the 8,824 objects whose owner was found. Its chunks: | Chunk | Contents | |---|---| | 0x162B | Path geometry, the layout of page items. Parses in 8,846 of the 8,846 little-endian objects that have it; 18 have none | | 0x3709 | 10 bytes: u32 UID of the item (9,061 of 9,061), u32 0 (9,061 of 9,061), u16 custom path flag | | 0x2C0B, 0x2C2D, 0x377A | On 4,813 of 9,061; not read | IDML writes the path as `TextWrapPreference/Properties/PathGeometry`, before `TextWrapOffset`, only when the custom path flag is 1. Over the 2,342 IDML wrap elements that have a wrap path object (1,412 in trustworthy pairs): | u16 at 8 of 0x3709 | IDML `PathGeometry` | All pairs | Trustworthy | |---|---|---|---| | 1 | present | 35 of 35 | 32 of 32 | | 0 | absent | 2,307 of 2,307 | 1,380 of 1,380 | The 35 are 28 polygons (22 `BoundingBoxTextWrap`, 6 `Contour`), one rectangle, one group, one text frame (`JumpObjectTextWrap`) and 4 images (`Contour`). In all 35 the IDML paths equal the 0x162B paths: number of paths (1 to 3), point count, `Anchor`, `LeftDirection` and `RightDirection` from the point's anchor, left and right, and `PathOpen` from the u16 after the points (0 `false`). The numbers are the same (tolerance 1e-6), so no transform applies: the path is in the item's inner coordinates, those of its own `PathGeometry` or, for a placed graphic, of its `GraphicBounds` (before the graphic's `ItemTransform`). It already includes the wrap offsets: the path of an image with a 8.5 pt top offset and a scale of 0.40885 extends 20.79 (8.5 / 0.40885) beyond the graphic bounds on each side. With flag 0 the object still stores a path: for frames and groups the item's path bounding box grown by the offsets (1,538 of 1,538 cases compared; in about 500 more the box is also grown by 0.25 to 1.5 pt on each side), for contour wraps of images the computed contour. IDML omits these, and the converter writes the path only for flag 1. Placed graphics also have chunk 0x373D, the contour (IDML `ContourOption`). See "Contour" below. **Evidence from the pairs.** For every element with a `Self` in the corpus pairs, the IDML `TextWrapPreference` was compared with chunk 0x3703 of the INDD object with that UID. Same-version pairs first, then the pairs whose IDML is from an older version: | INDD | IDML | Same version | Older IDML | |---|---|---|---| | Mode 0 | `TextWrapMode="None"` | 2,625 of 2,625 | 615 of 615 | | No chunk 0x3703 | `TextWrapMode="None"` | 1,468 of 1,468 | 873 of 873 | | Mode 1 | `TextWrapMode="JumpObjectTextWrap"` | 2 of 2 (one file) | 1, matched by geometry (below) | | Mode 3 | `TextWrapMode="BoundingBoxTextWrap"` | 1 of 1 (an image) | none | | Mode 6 | `TextWrapMode="Contour"` | 3 of 3 (one file) | 1 of 1 (an image) | | Offsets as left, top, right, bottom | `TextWrapOffset` | 2,631 of 2,631 | 616 of 616 | | u32 at 40 = 1 (little-endian) | `Inverse="false"`, `ApplyToMasterPageOnly="false"`, `TextWrapSide="BothSides"` | 2,630 of 2,630 | 616 of 616 | | 0x373D type 5 | `ContourType="SameAsClipping"` | 224 of 225 (1 has no `ContourOption`) | 75 of 75 | **Contour.** Chunk 0x373D of placed graphics (29 bytes) holds the contour that IDML writes as `ContourOption`. The preferences chunk 0x373F (`preferences.md`) and the object style chunk 0x3777 (object style settings, below) have the same layout. | Offset | Contents | |---|---| | 0 | u32 contour type: 0 `BoundingBox`, 1 `DetectEdges`, 2 `AlphaChannel`, 3 `PhotoshopPath`, 4 `GraphicFrame`, 5 `SameAsClipping`, 6 `SelectSubject` | | 4 | f64: 5 in every placed graphic and the preferences, 5 or 8 in object styles | | 12 | u8: 25 everywhere | | 13 | `00 00 FF FF` everywhere | | 17 | u8 `IncludeInsideEdges`: 0 `false`, 1 `true` | | 18 | 11 zero bytes | Evidence, over the placed graphics of the 1,251 trustworthy pairs (corpus of 2026-10) whose IDML has a `ContourOption`: | INDD | IDML | Graphics | |---|---|---| | Type 5 | `ContourType="SameAsClipping"` | 2,693 of 2,698 (5 have no `ContourType`, below) | | Type 2 | `AlphaChannel` | 13 of 13 | | Type 1 | `DetectEdges` | 8 of 8 | | Type 0 | `BoundingBox` | 4 of 4 | | Type 6 | `SelectSubject` | 1 of 1 | | Byte 17 = 1 | `IncludeInsideEdges="true"` | 1 of 1 (type 2) | | Byte 17 = 0 | `IncludeInsideEdges="false"` | every other graphic with `ContourType` | Types 3 and 4 occur in no pair: no IDML in the corpus has either code. The IDML schema's `ContourOptionsTypes` list leaves `PhotoshopPath` and `GraphicFrame` as the two names without a code, and the INDD shows which is which: - The graphic's chunk 0x1635 holds path data in the layout of chunk 0x162B (an outline of the picture content). All 5 type 3 graphics of v7.0 and later files have it (3 images, 2 EPS, 5 files); none of the 12 type 4 graphics (3 files) does. Types 1, 2 and 6 have it too (118 of 118 sampled), types 0 and 5 do not (56 of 56 sampled). - The wrap outline (chunk 0x162B of the 0x3702 object) of a type 4 graphic is the frame outline grown by the Top offset on every side. Two graphics have a frame that differs from the image bounds (one crops the image, one is wider than the placed bounds); both outlines follow the frame, not the image. The wrap outline of a type 3 graphic is its 0x1635 path, simplified and grown by the Top offset (3 of 3 with an outline; one is 2.1 units short at the bottom), not the image box. So type 3 is drawn from a path of the picture (`PhotoshopPath`) and type 4 from the frame (`GraphicFrame`). The converter leaves the `ContourOption` of other codes out. **Contour parameters.** From DOM 21.4 every `ContourOption` has `ContourTolerance="5"`, `ContourThreshold="25"`, `ContourAlphaIndex="0"` and `ContourPathIndex="-1"`, after `ContourPathName`: 211 of 211 elements in the 12 corpus IDML files of DOM 21.4 and 21.5 (graphics, frames, object styles and preferences), all with these values. No `ContourOption` of an earlier DOM has them (32,227 elements in the 1,444 IDML files of DOM 7 to 21.3). Every `ContourOption` of DOM 21.4 and later also has `ContourType`. The contour chunks (graphic 0x373D, object style 0x3777, preferences 0x373F) may hold them as the f64 at 4, the u8 at 12 and the i16 at 13 and 15, in the layout of the clipping chunk 0x2C1A (f64 tolerance, u8 threshold, i16 index): the 102 contour chunks of the DOM 21.4–21.5 pairs hold 5, 25, 0 and −1 there. Over all 1,462 pairs, the only other value is f64 8 in 72 object style chunks (24 files of DOM 10.0, 10.2 and 17.0), whose IDML has no `ContourTolerance`, so the assignment is not shown. The converter writes the four values on every `ContourOption` with a `ContourType` from DOM 21.4. A `ContourOption` with only `ContourPathName` has no sample from DOM 21.4; the converter writes it without them. `ContourPathName` is not in the chunk. Every `ContourOption` of the corpus IDMLs has it (25,551 of 25,551 in 1,693 IDML). It is `$ID/` in 25,525; the other 26 name an alpha channel of an image: 13 images with `ContourType="AlphaChannel"` and their 13 frames, in five documents. The name comes from image chunk 0x177C: | Offset | Contents | |---|---| | 0 | u32 channel count *n* (0 in most images, which then have 4 bytes) | | 4 | per channel: a flag byte (3 or 0; meaning unknown), an in-object string (the channel name), then bytes not decoded | For example `01 00 00 00 | 03 | 02 03 03 00 | 03 80` and 3 UTF-16 units. Over the images of the pairs that have a `ContourOption`: | Image | `ContourPathName` | Images | |---|---|---| | Type 2, *n* ≥ 1 | the first channel name | 13 of 13 | | Type 2, *n* = 0 or no chunk | `$ID/` | 10 of 10 | | Any other type | `$ID/` | all (8,629), including 1,075 images with 1 to 7 channels | No chunk of the type 3 graphics holds a path name (strings searched in all chunks), so the converter writes `$ID/` for type 3 as for the other types. Which channel a type 2 contour names when *n* > 1 is not shown (no sample); the converter takes the first. Image chunk 0x1714 is not the source: its alpha channel name is `$ID/` in all 22 type 2 images. A graphic without chunk 0x373D has the contour of the preferences chunk 0x373F, and without that chunk, type 5 and `IncludeInsideEdges="false"`. Of the 9,535 graphics (images, EPS, PDF, SVG and imported pages) whose `ContourOption` the converter writes and the trustworthy IDMLs have, `ContourType` and `IncludeInsideEdges` are equal in all; this includes the 319 images without the chunk that have `DetectEdges` because their document's chunk 0x373F has type 1. A frame with one placed graphic (`Rectangle`, `Oval`, `Polygon`) has the contour of its graphic, unless its object style turns the text wrap category on (all of 0x1B942, 0x37C8 and 0x37C9 in chunk 0x1B92E, IDML `EnableTextWrapAndOthers="true"`). Then its `ContourOption` has only `ContourPathName`: 517 of 518 such frames in the trustworthy pairs. In all corpus pairs, the value is `$ID/` in all 568 `ContourOption` elements with only that attribute (554 frames and the 14 graphics below); the converter writes the name of the graphic's contour, which is `$ID/` unless the graphic has type 2 and a channel (no sample of that in such a frame). The 8,965 other frames with `ContourType` take their graphic's type and `IncludeInsideEdges` in all cases, and the frames of the 13 named images their `ContourPathName`. Frames without a graphic have no `ContourOption` (all but one). The IDML has no `ContourType` and `IncludeInsideEdges` on 14 graphics: 5 with type 5 in chunk 0x373D and 9 without the chunk. 12 of them are in frames whose object style turns the text wrap category on, but 495 graphics in such frames have both attributes. No INDD field is known that separates them, and the converter writes the type for all 14. **Master page flag and side.** The 4 bytes at 40 are two u16 in the file's byte order. The big-endian pair shows it: its 261 page items store `00 01 00 00` (251, IDML `ApplyToMasterPageOnly="false"`) or `00 00 00 00` (10, `true`), all with `TextWrapSide="BothSides"`. In little-endian files the u32 1 is therefore the u16 1 at 40 and the u16 0 at 42. With this layout the converter reproduces, over the 1,251 trustworthy pairs of the corpus after 2026-10, all 256,651 `ApplyToMasterPageOnly` and `TextWrapSide` values of page items, form fields and placed graphics, where the u32 reading had left 6,322 of each out (items storing 0 at 40 or a side code). Side codes 1 to 5 occur on few items (14, 4, 9, 1 and 32 in the trustworthy pairs). Items without the chunk have `false` and `BothSides`. `Inverse` is `false` on every item of the corpus IDML files (256,651 of 256,651 in the trustworthy pairs); no INDD field is known for it, and the converter writes `false`. **Order of the offsets.** Most offsets in the pairs are 0, so the order rests on a few elements: - Offset 24 (third) is `Right`: the bounding-box image has 2.83 there and 0 elsewhere; its IDML has `Right="2.834645669291339"` and 0 for the other sides. - Offset 32 (fourth) is `Bottom`: both jump-object frames have 14.17 there and 0 elsewhere; their IDML has `Bottom="14.173228346456694"` and 0 for the other sides. - Offset 16 (second) is `Top`. In a pair whose IDML is from an older version (IDML 17.0, INDD 18.1), the INDD has one jump-object frame with 11.34 at offset 16 and 5.67 at offset 32. Its UID differs from the IDML element's, but its path and `ItemTransform` are identical to those of the IDML's only jump-object frame, which has `Top="11.338582677165356"` and `Bottom="5.669291338582678"`. A sample and its print PDF agree; see below. - Offset 8 (first) is therefore `Left`. It is 0 in every sample that has a reference. - The two contour frames have the same value at all four offsets, as in their IDML. **Evidence from a sample and its print PDF.** The sample has frames with mode 1. Offset 8 is 0 in all of them; offset 16 is 0, negative or positive. With jump-object wrap, no text is set beside a frame, and text above the frame must end above the top of the wrap area. Text line positions in the PDF were taken from pdftotext word boxes and compared with the frame bounds: - Where offset 16 is 0, the body line above a frame ends at most 1.5 pt below the frame's top edge. - Where offset 16 is negative, the body line above may end inside the frame, never further below its top edge than the offset's size plus 1.5 pt. Where the next line position would end further down than that, it is left empty. - A line that lies wholly between a frame's top and bottom edges falls inside the band that a large negative offset opens. So the value at offset 16 moves the top edge of the wrap area, and a negative value lets text into the frame from above. **Other values.** One image in the same-version pairs has 0x40001 at offset 40 and `TextWrapSide="SideAwayFromSpine"`; no other value is shown, so the converter writes the side and inverse settings only when the u32 at 40 is 1. Modes other than 0, 1, 3 and 6 have no IDML evidence; the converter leaves out `TextWrapPreference` for them. ## Frame fitting The frame fitting settings are attributes in the page item attribute list (chunk 0x6E03, `attributes.md`). Object styles hold all of them in chunk 0x1B956 (a u16 count, then records of the same layout). | ID | IDML `FrameFittingOption` attribute | Encoding | |---|---|---| | 0x6E83 | `AutoFit` | u32: 0 `false`, 1 `true` | | 0x6E7E | `LeftCrop` | f64 | | 0x6E7F | `TopCrop` | f64 | | 0x6E80 | `RightCrop` | f64 | | 0x6E81 | `BottomCrop` | f64 | | 0x6E7C | `FittingOnEmptyFrame` | u32: 0 `None`, 1 `ContentToFrame`, 2 `Proportionally`, 3 `FillProportionally` | | 0x6E7D | `FittingAlignment` | u32: 0 `TopLeftAnchor`, 1 `TopCenterAnchor`, 3 `LeftCenterAnchor`, 4 `CenterAnchor` | Evidence, from the same-version pairs: - **Object styles.** All 337 object styles paired by name hold all seven IDs in chunk 0x1B956, and all seven match the style's IDML `FrameFittingOption` in 337 of 337 (`FittingAlignment` 4 in 313 styles, 0 in 24). - **Crops.** Each crop ID is told apart by non-zero values: wherever IDML writes the attribute, the frame's own value, or its style's if the frame has none, matches: left 429, top 397, right 433, bottom 451, no mismatch. Non-zero values: left 235, top 204, right 239, bottom 258. - **Fitting on an empty frame.** 301 of 301; codes 0, 1, 2 and 3 occur (1,154, 9, 77 and 16 values). - **Alignment.** 200 of 205. The other 5 are frames in one file with the style `[None]`, no local value and a style value of 0, where IDML says `CenterAnchor`; not explained. Code 0 is shown in 9 frames, code 4 in object styles. Other codes are left out. - **AutoFit.** 0 and `false` wherever IDML writes it (200 of 200); other values are left out. Codes 1 and 3 of `FittingAlignment` and code 1 of `AutoFit` come from the corpus after 2026-10, over all pairs (item and object style lists against their IDML): `AutoFit` 1 `true` in 927 items and styles, 0 `false` in 2,548; `FittingAlignment` 1 `TopCenterAnchor` in 21 object styles of one 14.0 document, 3 `LeftCenterAnchor` in 1 item. Codes 2 and 5 to 8 do not occur; the converter leaves them out. **Where IDML writes it.** Only on rectangles, ovals and polygons, never on text frames (1,154) or lines (136), even when their list has these attributes. The rule depends on the applied object style's category list (chunk 0x1B92E, object style settings): 1. **Fitting category off.** When the list lacks 0x1B960 (`EnableFrameFittingOptions`), IDML writes all seven attributes: the item's value where its list has the ID, the style's otherwise. This holds for the root `[None]` too, although its IDML element has no `Enable…` attributes. In the trustworthy pairs of the corpus after 2026-10, the root style's list lacks 0x1B960 in 22 documents, and all 9,560 items there that use the root style have all seven attributes; in the other 883 documents with such items, no item that uses the root style has all seven. 2. **Otherwise**, let D be the item's local attributes whose value differs from its object style's. IDML writes D; with D empty it writes no element. An object style without a category list (older files) gives all seven of its values when D is empty, unless it is the root `[None]`. Over the trustworthy pairs, the converter reproduces 33,274 of 33,702 `Rectangle/FrameFittingOption` elements, 13,131 of 13,648 for polygons and 6,929 of 7,207 for ovals, with every attribute of the written elements except 131 `FittingAlignment` values and 7 values of each of three crops. Of the elements not reproduced, 585 belong to items the converter does not write with that tag, and 632 are on items that use the root `[None]` (style value 0) and do not store 0x6E7D, where IDML writes only `FittingAlignment="CenterAnchor"` (609 of them in one 20.3 document); 6 rectangles remain. The 131 differing values are of the same kind, on items of other styles. No stored value tells these items apart from the items where IDML writes the style's value; the converter writes the style's value. ## Clipping path settings Images, PDF and EPS graphics may have chunk 0x2C1A (37 bytes): | Offset | Contents | IDML `ClippingPathSettings` | |---|---|---| | 0 | u32: 0 `None`, 1 `DetectEdges`, 2 `AlphaChannel`, 3 `PhotoshopPath` | `ClippingType` | | 4 | f64 | `Tolerance` | | 12 | f64 | `InsetFrame` | | 20 | u8 | `Threshold` | | 23 | i16 | `Index` (not for type 2, below) | | 25 | u8 flags: 0x02 `UseHighResolutionImage`; 0x01 and 0x08 `InvertPath` and `RestrictToFrame` | | | 26 | 11 bytes, 0 in every sample | | Evidence: 75 of 235 images in the same-version pairs have the chunk, and all 75 match on the tolerance, inset, threshold and index (tolerance 2 or 0, threshold 25 or 0). PDF (153 of 206) and EPS (6 of 18) graphics have the chunk with the same values as images that use the defaults. Graphics without the chunk (160 images, 53 PDF, 12 EPS) have `UseHighResolutionImage="true"`, `Threshold="25"`, `Tolerance="2"`, `InsetFrame="0"` and `Index="-1"` in IDML, and the converter writes those values for them. Over the trustworthy pairs of the corpus after 2026-10, every graphic with the chunk was compared with its IDML element (2,686 graphics): | INDD | IDML | Graphics | |---|---|---| | Type 0 | `ClippingType="None"` | 2,655 of 2,655 | | Type 1 | `DetectEdges` | 2 of 2 (2 pairs) | | Type 2 | `AlphaChannel` | 1 of 1 | | Type 3 | `PhotoshopPath` | 28 of 28 (12 pairs) | | Flags 0x02 | `UseHighResolutionImage="true"`, `InvertPath="false"`, `RestrictToFrame="false"` | 2,680 of 2,680 | | Flags 0x00 | `UseHighResolutionImage="false"`, `InvertPath="false"`, `RestrictToFrame="false"` | 5 of 5 | | Flags 0x09 | `UseHighResolutionImage="false"`, `InvertPath="true"`, `RestrictToFrame="true"` | 1 of 1 (type 1, threshold 111, tolerance 3.9) | | i16 at 23, types 0, 1 and 3 | `Index` (-1, or 0 in 9 type 3 images) | all | Bits 0x01 and 0x08 are set together in the one sample, so which is `InvertPath` and which `RestrictToFrame` is not shown. The converter writes both when the two bits are equal and leaves both out otherwise. Other type codes have no sample; the converter leaves out the element for them. The one type 2 image stores -1 at 23, and its IDML has `Index="0"`. No other field of the chunk differs from the type 1 and 3 images, so the index of the alpha channel is not in this chunk; the converter leaves `Index` out for type 2. `IncludeInsideEdges="false"` has no identified field: all 11,823 `ClippingPathSettings` elements in the IDML files of the 1,462 corpus pairs have it, and the converter writes it. **Path name.** `AppliedPathName` names the image's path or channel: | Type | `AppliedPathName` | Graphics | |---|---|---| | 0 | `$ID/` | all 12,280 `None` elements of the corpus IDML files | | 1 | `$ID/` | 2 of 2 | | 2 | the first channel name of image chunk 0x177C (text wrap, contour) | 1 of 1 | | 3 | the name in key 0x7D of image chunk 0x1708 | 17 of 17 with an ASCII name | Key 0x7D of the image properties (13 bytes in all samples) holds a u8 length *n*, *n* bytes of name, then 6 bytes (`00 00 33 33 00 01` or `00 00 00 00 00 01`). The name bytes are not Unicode. The other 11 type 3 images name their path in a legacy code page: in 5 (Korean documents) key 0x7D holds bytes that decode in code page 949 to the IDML name; the other 6 (Japanese documents) have no key 0x7D, and key 0x80 of the same chunk holds the name as bytes that decode in Shift-JIS. None of these INDD files has the name as UTF-16 or UTF-8 text, except two where it is part of a longer text in another record. The file does not record the code page, so the converter writes the name only when every byte is printable ASCII (0x20 to 0x7E) and leaves `AppliedPathName` out otherwise. **Geometry.** IDML writes `Properties/ClippingPathGeometry` for every graphic of type 1, 2 or 3 (31 of 31 images, 13 pairs), and for no graphic of type 0. The paths are those of the graphic's own chunk 0x162B, in the layout of page item paths: the converter's output equals the IDML element exactly, number for number, in all 31 (1 to 8 paths of 4 to 179 points). The 2,655 graphics of type 0 with chunk 0x162B (1,797 images, 329 PDF, 528 EPS, 1 WMF graphic) have no `ClippingPathGeometry`. IDML puts it before `GraphicBounds`. **Placed pages.** IDML has `ClippingPathSettings` on every `ImportedPage` (42 in 8 pairs, 38 in 7 trustworthy pairs). No placed page has chunk 0x2C1A, and all 42 have the values of graphics without the chunk; the converter writes those values for them. **ImageIOPreference.** Chunk 0x1714 of an image: | Offset | Size | IDML `ImageIOPreference` | |---|---|---| | 0 | u32 | 72 in all 1,300 images with the chunk | | 4 | u16 | `ApplyPhotoshopClippingPath`: 1 `true`, 0 `false` | | 6 | u16 | 1 in all (`AllowAutoEmbedding="true"` in all IDML) | | 8 | flagged string | `AlphaChannelName` (an empty key is `$ID/`) | Without the chunk, IDML has `ApplyPhotoshopClippingPath="true"` and `AlphaChannelName="$ID/"`. Evidence: 3,607 of 3,607 images of the trustworthy pairs for both attributes (14 `false`, 92 `$ID/kNoneName`, 2 plain names). Chunk 0x8C39 (u16 at 0), which looked like the clipping flag in fewer samples, gives the wrong value in 6 images and is missing in 549. `AllowAutoEmbedding="true"` is written from observation. ## Placed graphics Images (0x1702), PDF (0x2501), EPS (0x6601) and SVG (0x6639) are children of a frame (in its 0x15B list) and share these chunks: | Chunk | Contents | |---|---| | 0x151 | Matrix: `ItemTransform` (437/437 match) | | 0x1633 | Four f64: `GraphicBounds` left, top, right, bottom | | 0x8CBC | link list (story links and placed text, link lists): the link is the first UID, at offset 8 | **Links (0x8C42).** Links of linked stories and linked page items have another class with the same chunks (story links and placed text). `LinkClassID` is the class of the link object in decimal: 2,386 of 2,386 IDML links in the 158 little-endian documents whose UID is a link object in the INDD (19 more UIDs hold other objects, all in stale pairs). Chunk 0x8C9B: | Offset | Size | Contents | IDML `Link` | |---|---|---|---| | 0 | 4 | 0 in all links | | | 4 | 4 | 0x101 in all links | (`LinkClientID="257"`) | | 8 | 4 | UID of the link resource (class 0x8C41) | | | 12 | 4 | 1 or 0 | `LinkResourceModified`: 1 `false`, 0 `true` | | 16 | 4 | UID of the graphic (or story) | | | 20 | 2 | 1 or 0 | `ShowInUI`: 1 `true`, 0 `false` | | 22 | 6 | three u16, 1 in all links | | | 28 | 4 | 0 in all links | | | 32 | 4 + text | import stamp: u32 length, then text segments | `LinkImportStamp` | | then | 8 | modification time of the file (FILETIME) | `LinkImportModificationTime` | | then | 8 | time the file was placed or updated (FILETIME) | `LinkImportTime` | A FILETIME here is the count of 100 ns intervals since 1601-01-01 UTC, stored as two u32: the high half first, then the low half (each in the file's byte order). Evidence: the stamp text is `file `, and `` equals the first time value in 4,686 of 4,686 links with a stamp. A link with an empty stamp (length 0) has neither `LinkImportStamp` nor `LinkImportModificationTime` in IDML, but has `LinkImportTime` (13 of 13). Chunk 0x1B6 of a link (u32, on 59 links) is `PDFIdentifier`, which IDML writes from DOM 21 on; links without the chunk have `PDFIdentifier="0"` (81 of 81 DOM 21 links). **Link resources (0x8C41)**: chunk 0x8C92 is a flag byte, u32 length, then the URI as bytes (`LinkResourceURI`). After the URI, chunk 0x8C92 continues: | Offset after the URI | Field | |---|---| | 0 | u8 1, u8 1, u16 0 (all resources) | | 4 | u32 0 or 1, not identified | | 8 | u32 stored state: 0 `Normal`, 2 `Embedded`, 3 `Contained` (`StoredState`, below) | | 12 | u32 UID of a raw data object (class 0x129), or 0 | | 16 | stamp: u32 length, text segments (as in the link) | | then | modification time (FILETIME, as in the link) | | then | file size: u32 high half, u32 low half; `LinkResourceSize` is `~` in lower-case hexadecimal without leading zeros (`0~42ebd`) | | then | u8, 1 in all resources | | then | in-object string: the format name; `LinkResourceFormat` is `$ID/` and the name, and the graphic's `ImageTypeName` is the same | Evidence over the 4,699 links of the trustworthy pairs (4,697 with a resource object), with the converter's output compared with the reference: | IDML | Equal | |---|---| | `LinkResourceModified` | all 4,699 (5 with 0 = `true`) | | `ShowInUI` | all 4,699 (2 with 0 = `false`) | | `LinkImportStamp` | 4,105 of 4,105 links with the same URI in the INDD and the IDML | | `LinkResourceSize`, `LinkResourceFormat` | 4,118 of 4,118 links with the same URI | | `PDFIdentifier` | 81 of 81 | The resource's stamp differs from the link's in 184 links; IDML then has the link's stamp and the resource's size. All 63,470 link chunks and their resource chunks in the distinct little-endian corpus files parse with this layout. **Links resolved to another file at export.** In 19 trustworthy documents, 529 links differ from the INDD: the IDML names the same file in another folder (344) or a newer file (185). InDesign wrote the location and state of the file it found on the exporting computer; the INDD keeps the state of its last save. These values cannot come from the INDD, and `compare.py` leaves such links out (`docs/measurement.md`). **Link times.** IDML writes `LinkImportTime` and `LinkImportModificationTime` as `YYYY-MM-DDTHH:MM:SS` (seconds truncated) in the local time of the computer that exported the IDML: - IDML equals the INDD's UTC time plus a whole number of hours in 9,009 of 9,385 time values; the other 376 belong to links resolved to another file at export or have no stamp. - The offset differs between documents (−8 to +13 hours) and, in 71 of 284 documents, between links of the same document in step with the season (for example 1 and 2 hours, −5 and −4, +10 and +11), with the larger offset on summer dates of that hemisphere: the daylight-saving rule of the exporting computer's time zone, which the INDD does not store. The converter estimates the offset from the dates of the XMP packet that have a UTC offset (`xmp:CreateDate`, `xmp:MetadataDate`, `xmp:ModifyDate`, every `stEvt:when`): it takes the offset of the date nearest to the link time in day of the year (difference of the days of the year, not wrapping at the year end; ties: the latest date), and UTC when there is no such date. Over the trustworthy pairs this gives `LinkImportTime` for 3,886 of 4,446 links and `LinkImportModificationTime` for 3,316 of 4,434 (links with the same URI: 3,748 of 4,118 and 3,019 of 4,105). Other rules give less: UTC about 155 values of 9,385, the offset of `xmp:ModifyDate` 6,488, the same rule with the day difference wrapping at the year end 3,840 import times. `compare.py` leaves both times out of the value coverage (`docs/measurement.md`). **Embedded links.** A link is embedded (`StoredState="Embedded"`) when its resource names a raw data object at offset 12. That object holds the linked file's bytes, which IDML writes as the graphic's `Contents`. Evidence: - In the 245 distinct little-endian files there are 883 link resources. The field at offset 8 is 2 in 123 of them, and exactly those 123 name a raw data object at offset 12. The other 760 have 0 at offset 12 (742 with 0 at offset 8, 18 with 3). - In the corpus pairs, all 10 resources of embedded links (12 links) name a raw data object, and none of the 369 resources of normal links do. - For all 12 embedded links, the raw data object's bytes equal the base64-decoded `Contents` of the IDML graphic (JPEG, PNG, PDF, EPS and SVG files, 2 KB to 11 MB). **Contained links.** The field at offset 8 is 3 on exactly the 50 links IDML marks `StoredState="Contained"`: 30 linked stories, 17 stories placed from Word and 3 linked page items. It is 0 on 2,237 `Normal` links and 2 on 99 `Embedded` links (the 158 little-endian documents whose IDML has a story link, a link source ID, style mappings or import options; 19 more `Normal` links have a UID that holds another object, in stale pairs). **Settings shared with page items.** Images, PDF, EPS and SVG graphics carry the page item setting chunks (page item settings, above): 0x2C10 name, 0x2C32 visibility, 0x21D4E, 0x21D50 and 0x21D53 change counts, 0x1424 overridden properties, 0x22228 layout constraints, and 0x1B916 object style (`AppliedObjectStyle`). Many graphics (2,396 images) lack 0x2C10 and 0x2C32; the defaults of the page item table then apply. Graphics never have `Locked`. Evidence over the 5,131 graphics of the trustworthy pairs: `Visible`, `Name` and `AppliedObjectStyle` 5,131 of 5,131; the change counts, `OverriddenPageItemProps` and both layout constraints 5,079 of 5,079 from DOM 8 on, and absent in all 52 DOM 7 graphics. Images also have the five fill gradient attributes (`GradientFillStart`, `…Length`, `…Angle`, `…HiliteLength`, `…HiliteAngle`) from their attribute list (chunk 0x6E03) with the defaults of page items, and `FillColor` when the list has attribute 0x6E68 (77 of 77 images; no image without it has `FillColor`). PDF, EPS and SVG graphics have no gradient attributes. **`ImageTypeName`** is `$ID/` and the format name of the link resource (links, above), on graphics with a link only: 4,696 of 4,699 linked graphics; no graphic without a link has it (308 images, 111 PDF, 12 EPS). **Image properties (chunk 0x1708 of an image).** u32 count, then per record u32 key, u32 data length *n*, a u8 (meaning unknown), *n* bytes; then 4 zero bytes: | Key | Data | Meaning | |---|---|---| | 0x64 | u32 | width in pixels | | 0x65 | u32 | height in pixels | | 0x67 | u32 | colour space: 1 grey, 2 RGB, 4 CMYK | | 0x6C | u32, 16.16 fixed point | horizontal resolution (ppi) | | 0x6D | u32, 16.16 fixed point | vertical resolution | | 0x6F | colour table | present only for indexed colour | In all 3,607 images of the trustworthy pairs, `GraphicBounds` width and height equal pixels × 72 / resolution. All 29,055 image property chunks of the distinct little-endian corpus files parse. IDML writes these values only on images with a link (3,299 of 3,299; no image without a link has them): | IDML | Rule | Equal | |---|---|---| | `Space` | 1 `$ID/#Links_Grayscale`, 2 `$ID/#Links_RGB`, 2 with key 0x6F `$ID/#Links_Indexed RGB`, 4 `$ID/#Links_CMYK` | 3,299 of 3,299 | | `ActualPpi` | both resolutions rounded half up to integers (72.009 → 72) | 3,299 of 3,299 | | `EffectivePpi` | each `ActualPpi` value divided by the image's scale on the spread, rounded half up | 3,299 of 3,299 | The scale on the spread: multiply the `ItemTransform` of the image with those of all its ancestors (frames, groups, the spread; for an item anchored in text, its own chain only) to get *a b c d tx ty*. The horizontal scale is √(a² + b²); the vertical scale is |ad − bc| divided by the horizontal scale. Using √(c² + d²) for the vertical scale fails on 87 skewed images; rounding half to even fails on one image whose value is exactly 76.5 (IDML: 77). **Profile** (`Properties/Profile` of `Image`, chunk 0x7C0F): u32 at 0 is 3 → `$ID/Embedded`, 1 → `$ID/Use Document Default`; 0 or no chunk → `$ID/None`. A profile name follows as an in-object string. 3,607 of 3,607 images (751, 1,652 and 1,204). **Vector colour policies of PDF and EPS graphics (chunk 0x7C42).** 16 bytes, four u32. Offset 4 is `RGBVectorPolicy`, offset 12 `CMYKVectorPolicy`: 1 `IgnoreAll`, 3 `HonorAllProfiles`. Offset 0 is 1 and offset 8 is 3 in all 334 graphics with the chunk. Without the chunk the policies follow the document's colour policies (chunk 0x7C44 of the preferences, `preferences.md`): `RGBVectorPolicy` is `IgnoreAll` when `RGBPolicy` is `ColorPolicyOff`, else `HonorAllProfiles`; `CMYKVectorPolicy` is `IgnoreAll` for `ColorPolicyOff` and `CombinationOfPreserveAndSafeCmyk`, `HonorAllProfiles` for `PreserveEmbeddedProfiles` and `ConvertToWorkingSpace`. Evidence: with the chunk 334 of 334 graphics; without it 1,005 of 1,005 graphics in 128 documents (5 combinations of document policies). **PDF placement (chunk 0x251B of a PDF).** | Offset | Size | IDML `PDFAttribute` | |---|---|---| | 0 | u32 | `PageNumber` | | 6 | u8 | `TransparentBackground`: 1 `true`, 0 `false` | | 8 | u32 | `PDFCrop`: 0 `CropContentVisibleLayers`, 2 `CropArt`, 3 `CropPDF`, 4 `CropTrim`, 6 `CropMedia`, 7 `CropContentAllLayers` | Evidence: 492 of 492 PDFs with the chunk (28 bytes in 486, 64 in 6), all three fields. Without the chunk, IDML has `PageNumber="1"`, `TransparentBackground="true"` and `PDFCrop="CropContentVisibleLayers"` in 507 of 509 PDFs; the other 2 (one document) have `CropArt`, not explained. Every PDF has `PDFAttribute` (1,001 of 1,001). **Graphic layers (chunk 0x177A of images, PDFs and imported pages).** | Offset | Size | Contents | |---|---|---| | 0 | 2 | 1 if an image has a `GraphicLayerOption` | | 2 | 8 | unknown | | 10 | 4 | layer count *n* | | 14 | | *n* layer records, in IDML order | A layer record is a flagged string (`Name`; a key is written with `$ID/`), u32 layer ID (`Id`), u32 original and u32 current visibility (1 = visible: `OriginalVisibility`, `CurrentVisibility`), i32 ID of the parent layer (−1 for a top-level layer), and u32 flags: 0x01 `SeparatorLayer`, 0x04 `FXLayer`, 0x08 `Locked`. A layer whose parent ID is *p* is a child element of the `GraphicLayer` with `Id` *p*. `Self` is the graphic's `Self`, `GraphicLayerOption1`, and `i` and the layer ID in hexadecimal for each layer from the top level down (`u5dbcGraphicLayerOption1i92i8e` is layer 0x8E inside layer 0x92). Evidence: all 1,064 `GraphicLayer` elements of the trustworthy pairs (679 PDF, 291 image and 94 imported page layers, 262 of them nested): name, `Id`, `Self`, both visibilities and nesting 1,064 of 1,064; flag 0x04 ↔ `FXLayer="true"` 11 of 11; flag 0x08 ↔ `Locked="true"` 37 of 37; flag 0x01 ↔ `SeparatorLayer="true"` 1 of 1. Flags 0x400 (10 group layers) and 0x800 (5) have no IDML counterpart. `AdjustmentLayer`, `HasViewState`, `ViewState`, `HasExportState`, `ExportState`, `HasPrintState` and `PrintState` are `false` in all 1,064. `GraphicLayerOption` with `UpdateLinkOption="KeepOverrides"` is on every PDF and imported page (1,039 of 1,039, also when the layer count is 0) and on the images whose chunk has 1 at offset 0 (11 of 11; the 1,212 images with 0 there have no element). All 38,087 layer chunks of the distinct little-endian corpus files from InDesign 4.0 on parse; 172 in InDesign 3.0 files do not, and the converter leaves their layers out with a warning. **Layer comps.** Images with a `GraphicLayerOption` also have `LayerCompOption AppliedLayerComp`: chunk 0x9209 of the image, i32 at offset 4 (−1, −2 or a comp number). 11 of 11. ## Movies and sounds (classes 0x14801 and 0x14F01) Evidence: 21 `Movie` and 8 `Sound` elements in 17 trustworthy pairs (DOM 8.0: 2 pairs, 10.2: 11, 11.0: 1, 13.1: 1, 18.0: 2), and 128 movies in 42 files and 42 sounds in 12 files of the corpus (distinct, little-endian, InDesign 3.0 to 18.2; no big-endian sample). **Where they are.** A movie or sound is listed in the child list (chunk 0x15B) of a spline item (class 0x6201) and is that frame's only child (170 of 170 objects). Its own chunk 0x15B names the frame as parent (29 of 29 paired) and lists its poster, if any. IDML writes it as a `Movie` or `Sound` element, the last child of the frame's `Rectangle` (29 of 29), with `Self` the object's UID. The frame is written as a frame without a graphic: `ContentType="GraphicType"` (its chunk 0x1623 is 1) and no `ContourOption` in its text wrap (29 of 29 frames). **Chunks.** | Chunk | Contents | IDML | Evidence | |---|---|---|---| | 0x151 | matrix | `ItemTransform`; identity without the chunk | 29 of 29 (one sound has no chunk and `1 0 0 1 0 0`) | | 0x162B | path geometry, as page items | `Properties/PathGeometry` | 29 of 29 | | 0x1633 | four f64 | `Properties/GraphicBounds` (left, top, right, bottom) | 29 of 29 | | 0x8CBC | link list, as placed graphics | `Link` | 29 of 29 | | 0x15B | hierarchy: spread, parent frame, children (the poster) | | 29 of 29 | | 0x14004 | playback settings (below) | `Name`, `Description`, flags, `ControllerSkin` | 29 of 29 | | 0x1480C (movie), 0x14F03 (sound) | label at offset 0, as group labels (u16 0x7B7B, u32 count, flagged key and value strings) | `Properties/Label` | 2 movies with one pair match; 6 with count 0 and 21 without the chunk have no `Label` | | 0x1B916 | object style | `AppliedObjectStyle` | 29 of 29 | | 0x21D4E, 0x21D50, 0x21D53, 0x1424, 0x22228, 0x2C32 | page item settings | change counts, `OverriddenPageItemProps`, layout constraints, `Visible` | 29 of 29 each | | 0x3703 | text wrap | `TextWrapPreference` | 29 of 29 (7 with mode 0; 22 without the chunk, all `None` with offsets 0) | | 0x6E03 | attribute list | `TransparencySetting` | 1 movie with a gradient feather | | 0x2C10 | name chunk of page items | not used: flag 1 and an empty string where present (7 objects); `Name` comes from chunk 0x14004 | | Not read: 0x14022 (below), 0x8C39, 0x8C24, 0x1623, 0x2C5D, 0x14009, 0x6E20, 0x6EDD and the other page item chunks that 7 of the 29 paired objects carry (0x1E206, 0x1E22B, 0xB30A, 0x2056D, 0x2055F, 0x3723). IDML has no `ObjectExportOption`, `AnimationSetting` or `TimingSetting` on any of the 29 (all 0x2056D counts are 0 and all 0x2055F are `00 00`). **Playback settings (chunk 0x14004).** A sequence of lists, each a u32 count and that many entries of u32 key and a value whose type depends on the list: list 1 u16, list 2 u32, lists 3 to 6 unknown (count 0 in all 168 chunks that parse), list 7 a flag byte and an in-object string. The chunk ends after list 7 in 167 of 168; one 10.1 movie has one more list (count 1, key 0x14009, 16 bytes) with no IDML counterpart. A 3.0 movie uses keys 0x14800 to 0x14804 and another layout. Keys within a list are in no fixed order. Every u16 value is 1: IDML has `true` when the key is present and `false` when it is not. | Key | List | `Movie` | `Sound` | Evidence (paired) | |---|---|---|---|---| | 0x14000 | string | `Name`, as stored | `Name` | 21 of 21, 8 of 8 | | 0x14002 | string | `Description`; `""` without the key | `Description` (no sample with the key) | 1 with the key, 28 without | | 0x14008 | string | `ControllerSkin`; `""` without the key | | 19 of 19 (11 `SkinOverAll`, 1 empty, 7 without the key) | | 0x14003 | u16 | `CustomPoster` | | 21 of 21 (14 `true`) | | 0x14006 | u16 | `PlayOnPageTurn` | `PlayOnPageTurn` | 21 of 21 (3 `true`), 8 of 8 (7 `true`) | | 0x1400C | u16 | `ShowController` | | 19 of 19 (5 `true`) | | 0x1400E | u16 | `MovieLoop="true"` and `PlayMode="RepeatPlay"`; without it `false` and `Once` | `SoundLoop` | 21 of 21 (2 with the key), 8 of 8 (5 `true`) | | 0x14004 | u16 | `EmbedInPDF` (never stored on a movie: `false` in 21 of 21) | `EmbedInPDF` | 8 of 8 sounds have it and `true` | | 0x1400D | u16 | | `StopOnPageTurn` | 8 of 8 (2 `true`) | | 0x140FF | u16 | not identified (1 movie; no attribute differs) | | | | 0x14007 | u32 | not identified (0 in 139, 5485 in one unpaired movie) | | | | 0x140FE | u32 | not identified (5 in all 170) | | | The 14 movies with `CustomPoster="true"` have a poster without a link, the 7 with `false` a linked poster; the converter takes the key. A chunk whose lists 3 to 6 are not empty gives the flags without the strings and a warning; a damaged chunk gives the defaults and a warning. **Values without a stored field.** These are the same in every IDML of the corpus with these elements (19 files, paired or not: DOM 8.0, 10.1, 10.2, 11.0, 13.1, 18.0). No stored field was found for them; the converter writes them as defaults. | Element | Attribute | Value | Samples | |---|---|---|---| | `Movie` | `FloatingWindow` | `false` | 27 of 27 | | `Movie` | `FloatingWindowPosition` | `Center` | 27 of 27 | | `Movie` | `FloatingWindowSize` | `Full` | 27 of 27 | | `Movie` | `ShowControls` | `false` | 26 of 26 (DOM 8.0 to 13.1) | | `Sound` | `DoNotPrintPoster` | `false` | 13 of 13 | | both | `LocalDisplaySetting` | `Default` | 40 of 40 | `ShowControls`, `ControllerSkin` and `ShowController` are on all 26 movies of DOM 8.0 to 13.1, also on the 7 whose chunk has neither key 0x14008 nor 0x1400C, and on none in the one DOM 18.0 file. They follow the version, not the stored keys. No IDML of DOM 14 to 17 has a movie; the converter writes them below DOM 18. **`FilePath`** is the `LinkResourceURI` of the media's own link as a native path: without `file:`, percent-decoded as UTF-8, and with `\` for `/` in a path that starts with a drive letter (`C:/`). From the IDML's own link URI this gives `FilePath` in 29 of 29 (25 Windows paths, 4 Mac paths). From the INDD's link it gives 8 of 29: the other 21 links were resolved to another file on the exporting computer, and the IDML has that file's path in both `Link` and `FilePath`. A media object without a link has no `FilePath`. **Children.** IDML order (29 of 29): `Properties` (`PathGeometry`, `GraphicBounds`, then `Label`), `TransparencySetting`, `TextWrapPreference`, the poster `Image`, `Link`. - Text wrap: as for placed graphics (chunk 0x3703; without it mode `None` and offsets 0), 29 of 29. `ContourOption` is in the wrap of every movie and sound with a poster (26 of 26) and of none without (3 of 3), as a frame has one only with a graphic. All 26 are `SameAsClipping`, `false`, `$ID/`; no media object has chunk 0x373D, and the poster's contour and the preferences' contour agree in all 26, so either can be the source. The converter uses the poster's. - Poster: 125 of 128 movies and 9 of 42 sounds list one child, an image (class 0x1702). IDML writes it as an `Image` child (26 of 26 paired), like any image: 12 linked, 14 with their file in the document (`Properties/Contents`). Its `EffectivePpi` uses the transforms of the image, the movie or sound, the frame and their ancestors (12 of 12 linked posters). - `Link`: the layout of image links (chunks 0x8C9B and 0x8C92). For the 8 links whose URI is the same in INDD and IDML, `LinkResourceFormat` (`$ID/Flash H264 Video`, `$ID/MP3`, `$ID/Flash Video`, `$ID/SWF (Shockwave Flash)`), `LinkResourceSize` and `LinkImportStamp` match 8 of 8. **Media file properties (chunk 0x14022).** The same kind of typed list, with list types u16, u32, u64, f64, four f64 and flagged string (29 of 29 paired chunks end after the sixth list). Keys seen: 0x14002 and 0x14004 (u16); 0x140FE, 0x14F00 (sounds: 44100, the sample rate) and 0x14F02 (sounds: 128) (u32); 0x14000 (u64); 0x14006 (f64, movies: the duration in seconds); 0x14001 (four f64, movies: the frame size, for example 0 0 640 640); 0x14003 and 0x14005 (strings; 0x14005 of sounds is an older import path that matches neither link URI). IDML writes none of these: no IDML in the corpus has `IntrinsicBounds`, `CanChoosePosters` or `PosterAvailable`, a `NavigationPoint` or a `Movie` `URL`. **References.** `MovieBehavior/@MovieItem` (6 in 5 pairs) and `SoundBehavior/@SoundItem` (2 in 1 pair) name a `Movie` or `Sound` of the same IDML (8 of 8); see button behaviours. `TimingTarget` elements with `TargetRole` 132354 (3 movies) and 132356 (7 sounds), in 9 pairs, name a written `Movie` or `Sound` in 10 of 10; see timing lists. ## Text on a path (class 0xB320) A shape or text frame with text on its path names a text path object in chunk 0xB30A (600, 392 or 336 bytes). Items without text on their path can have the chunk too, with 0 in the first u32. | Offset in 0xB30A | Contents | IDML `TextPath` | |---|---|---| | 0 | u32 text path UID (class 0xB320), 0 for none | `Self` | | 4 | u32, the same UID | | | 8 | u16 1, u16 4, u32 3 in all 41 | `PathAlignment="CenterPathAlignment"`, `TextAlignment="BaselineTextAlignment"`, `PathEffect="RainbowPathEffect"`, `PathSpacing="0"` (which field is which is not known) | | 16 | u8: 1 `Flipped`, 0 `NotFlipped` | `FlipPathEffect` | | 18 | f64 | `StartBracket` | | 26 | f64 | `EndBracket` | The text path object has chunk 0xB334: u32 (its own UID), u32 0, then a UID list with one multi-column frame (class 0x263). That frame's 0x15B child list holds a column of class 0xB318, which gives the story and the thread as the column of a text frame does (text frames, below): `ParentStory`, `PreviousTextFrame` and `NextTextFrame` (`n` in every sample). IDML writes `TextPath` as a child of its shape or frame, directly after `Properties`, with the attributes in the order of the table (then the two thread attributes). The converter writes the four constant values only when the bytes at 8 are those of every sample. Evidence: 42 `TextPath` elements with an INDD object in the pairs (36 in trustworthy pairs; parents: polygons, an oval and text frames), all values equal in 42 of 42, both `FlipPathEffect` values occurring. ## EPS text (class 0x660B) Text kept from a placed EPS or PDF graphic as text that cannot be edited. IDML writes it as an `EPSText` page item. It is listed in the 0x15B child list of a spread layer or a group (483 of 634 are in groups), and has the usual page item chunks (0x151 transform, 0x6E03 attributes, 0x1B916 object style, 0x3703 text wrap, 0x2C10 name, …) but no path. | Chunk | Contents | IDML | |---|---|---| | 0x151 | Matrix | `ItemTransform` | | 0x154 | Four f64: left, top, right, bottom | `Properties/PathBoundingBox` `Left`, `Top`, `Right`, `Bottom` | | 0x6612 | 60 bytes: four f64 in the same order, then f64 72 and zeros (not identified) | `Properties/EPSTextAttributeBounds` `Top`, `Left`, `Bottom`, `Right`, from the four f64 | | 0x6611 | Text record (below) | `Properties/EPSTextData` | **Text record (chunk 0x6611).** u32 version (0x00010002 in all 634), a flagged in-object string (the font name), nine f64 (a matrix of font size, 0, 0, font size, x, y, then three numbers), a flagged in-object string (the text) and a tail of 210 bytes: u32 at 0 (16 in all), zeros to 72, u32 at 72 (varies), u16 at 76 (0 or 1), u32 at 78 (14 in all), zeros to 210. IDML `EPSTextData` is this record in big-endian byte order, without the u32 at 72 and at 78 of the tail, in base64 in lines of 76 characters: each number big-endian, each string as in big-endian data (tag 2, flag, the byte after the tag, u16 length, each segment header big-endian, byte segments copied and UTF-16 code units big-endian), and the zero areas copied. So `EPSTextData` is 8 bytes shorter than the chunk. IDML writes `Properties` (`PathBoundingBox`, `EPSTextData`, `EPSTextAttributeBounds`) and then `TextWrapPreference`; `EPSText` has no `ContentType`, no path and no export options. Its other attributes are those of other page items (page item settings, above); the values that every `EPSText` has come from `element_values.xml`. Evidence: 634 `EPSText` elements in 14 pairs (633 in 13 trustworthy pairs), versions 8.1 to 20.5. The transform, both bounds, the text data and the text wrap equal the IDML in 634 of 634. `StrokeType` (634), `OverprintFill` (33) and `StrokeDashAndGap` (7) are not reproduced yet: IDML writes `StrokeType="StrokeStyle/$ID/Solid"` on 627 items whose attribute list has no stroke type. ## Form fields and multi-state objects Seven classes are page items that IDML writes as interactive elements: | Class | IDML | Elements (documents) | |---|---|---| | 0x1450C | `Button` | 284 (43) | | 0x1450D | `CheckBox` | 72 (5) | | 0x1450E | `RadioButton` | 11 (4) | | 0x1450F | `TextBox` | 27 (4) | | 0x14511 | `ComboBox` | 1 (1) | | 0x14512 | `SignatureField` | 2 (2) | | 0x145D8 | `MultiStateObject` | 18 (16) | Counts are over the pairs of the corpus of 2026-10: 48 documents, all trustworthy, saved by InDesign 8 to 20. Each element's `Self` is the object's UID (415 of 415). A field is listed like any page item, in the child list of its spread, group or anchor, and its fields and states nest (a button inside a group inside a multi-state object's state). **Fields with states** (button, check box, radio button, multi-state object) have no chunk 0x15B and no 0x151: - Chunk 0x14521: u32 active state ID, u32 count, then per state u32 state ID, u16 enabled (1, or 0 for `Enabled="false"`), u16 key, u16 (0). IDML writes one `State` per entry, in order (705 of 705), with `Self` the field's `Self`, `i` and the state ID in hexadecimal, `Active="true"` for the active state ID and `Enabled` from the u16 (705 of 705; 4 not enabled). - Chunk 0x14526: u32 spread, u32 parent, u32 count, then per key u32 key, u32 *k*, *k* UIDs: the items each state shows, by its key (one group per state in 705 of 705). The chunks parse to their end in all 385 fields. The spread decides `AllowOverrides` as chunk 0x15B does for other items. - Chunk 0x14527: six f64, the transform (`ItemTransform`, 385 of 385). **State types.** `Properties/Statetype` follows from the state ID. For a multi-state object it is the ID itself (`type="long"`, IDs 9 to 15; 72 of 72 states). For the others it is an enumeration (633 of 633): | ID | `Statetype` | States | Name of the state's group without a name of its own | |---:|---|---:|---| | 0 | `Up` | 284 | `$ID/$$$/StateType/Normal` | | 1 | `Rollover` | 109 | `$ID/$$$/StateType/Over` | | 2 | `Down` | 61 | `$ID/$$$/StateType/Down` | | 3 | `UpOn` | 83 | `$ID/$$$/StateType/NormalOn` | | 4 | `UpOff` | 83 | `$ID/$$$/StateType/NormalOff` | | 6 | `RolloverOff` | 7 | `$ID/$$$/StateType/OverOff` | | 7 | `DownOn` | 3 | `$ID/$$$/StateType/DownOn` | | 8 | `DownOff` | 3 | `$ID/$$$/StateType/DownOff` | The key is not the state type: a button's second state has key 2 whether it is `Rollover` (ID 1) or `Down` (ID 2). ID 5 has no sample; a state with an ID not in the table is left out with a warning. A state's group has no name chunk (0x418), or one with flag 1 and an empty name; IDML names it by the state type, as in the table (633 of 633 groups of buttons, check boxes and radio buttons). The groups of multi-state objects have names of their own (72 of 72). **Fields with child items** (text box, combo box, signature field) have the usual chunks 0x151 (transform) and 0x15B, whose children IDML writes inside the element: a `Rectangle` or `TextFrame` (30 of 30). Text boxes can be anchored in text, with the anchor's `AnchoredObjectSetting`. **`Properties/PathBoundingBox`.** The bounds of the paths of the items the field shows, in the field's own coordinates: the curves through the anchor and direction points (not only the anchors) of the items of every state, or of the child items, each through its transform and those of the groups and fields between it and the field. Items inside a frame (an oval inside an oval) do not count. The converter takes each item's box in its own coordinates and the box around its corners through its transform, from the innermost item out: 415 of 415 equal to 6 significant digits, 379 of them as text (the others differ in the last digit or two). The field has no `PathGeometry`. **Attributes from the attribute list (chunk 0x6E03).** Strings have value type 0x1451F or 0x1B02 (a flag byte and an in-object string), numbers 0x6E68 (f64), flags 0x6E65 (u16). | ID | IDML | On | Evidence | |---|---|---|---| | 0x14534 | `Name` | all | 415 of 415, 237 distinct | | 0x14535 | `Description` | all but multi-state objects | 397 of 397 (23 distinct); 3 multi-state objects have `Description=""` without it | | 0x1453D | `Required` (1 `true`) | check box, radio button, text box, combo box, signature field | 113 of 113 (6 `true`); buttons have the attribute but IDML writes no `Required` | | 0x14550 | `ExportValue` | check box, radio button | 83 of 83 (9 distinct) | | 0x14552 | `Multiline` | text box | 27 of 27 (8 `true`) | | 0x14553 | `Scrollable` | text box | 27 of 27 (9 `true`) | | 0x14540 | `FontSize` | text box, combo box | 28 of 28 (4 distinct); signature fields have it, IDML writes none | | 0x1455F | `AppliedFont` | text box | 24 of 24 (2 distinct); the 3 text boxes without it have neither `AppliedFont` nor `FontStyle` | | 0x14542 | `FontStyle` | text box with 0x1455F | 24 of 24 | The flags `ReadOnly`, `PrintableInPdf`, `CheckedByDefault`, `Password`, `RightToLeftField`, `SortChoices`, `HiddenUntilTriggered`, `InitiallyHidden` and `VisibilityInPdf` have one value in every sample, and so do the candidate u16 attributes (0x14539 to 0x1453C, 0x14545, 0x14549, 0x14551, 0x14554 to 0x14558, 0x145E1); which holds which cannot be shown. Where every IDML has the value, it comes from the value files (`idml-values.md`, form fields). `ChoiceList` of the combo box (0x1455D, one sample) is not decoded. **Values of the page item.** `Visible`, `Locked`, the change counts, the layout constraints and `ItemLayer` follow the page item rules (page item settings). The gradient attributes are, as for a group, the values all the items the field shows directly have (the state groups or the child items), and IDML leaves an attribute out where they differ: the six gradient attributes are equal in 400 of 415 fields (4 of them without the start and length values). The fill, stroke and corner values in the field's attribute list are not IDML's: IDML's `FillColor` and corner values of buttons are not in the list, and other stored values differ, so they are not written. Fields with states have no object style chunk (0x1B916); IDML names `[None]` for 363 of their 385 and another style for 22, and where that is stored is not known, so `AppliedObjectStyle` is left out for them, and so are the anchor settings of such a field anchored in text, which IDML compares with the style (12 anchored check boxes and radio buttons with a style other than `[None]` have no `AnchoredObjectSetting`). A field has no `ObjectExportOption`. Timing lists of buttons and animations: animation and timing, below. Behaviours of buttons: button behaviours, below. ### Button behaviours Evidence: 206 behaviour elements in 193 buttons of 26 pairs (all trustworthy, versions 8.0 to 18.0), and 547 behaviour objects in 78 documents of the corpus (InDesign 3.0 to 18.2, both byte orders). The converter writes every behaviour type that has a paired sample, and `compare.py` finds all their values in its output. **Behaviour list (chunk 0x15601).** A form field lists its behaviours by event: ``` u32 groups per group: u32 event code, u32 count k, k behaviour UIDs ``` - The chunk parses to its end in 712 of 712 fields (all field classes; 271 have 0 groups). Only buttons have groups (441 buttons). - Every behaviour object is listed in exactly one group of one field (547 of 547). No group is empty. - IDML writes the behaviours as children of the button, in group order and, within a group, in stored order (193 of 193 buttons). In the five buttons with two groups the stored order is also ascending event code, so these two orders cannot be told apart. - They are the last children of the button, after every `State` (193 of 193). The schema allows them in any order there. | Event code | `BehaviorEvent` | Paired behaviours | |---|---|---:| | 0x15602 | `MouseDown` | 32 | | 0x15603 | `MouseUp` | 145 | | 0x15604 | `MouseEnter` | 29 | Unpaired groups also have 0x15605 (31) and 0x15607 (1). Their events are not known: the behaviour is written without `BehaviorEvent`, with a warning. **Behaviour objects.** Each behaviour type is a class. IDML `Self` is the object's UID (206 of 206); timing targets of role 132353 reference these UIDs (53 of 53 resolve in the converter's output). The object has one data chunk, and some also chunk 0x156E1, a label with the layout of the document label. Chunk 0x156E1 has count 0 in all 112 occurrences, and no IDML behaviour has `Properties/Label`; a label with pairs would be written there (no sample). | Class | IDML element | Data chunk | Type code | Objects (paired elements) | |---|---|---|---|---:| | 0x15613 | `GotoFirstPageBehavior` | 0x15614 | 0x15601 | 10 (7) | | 0x15614 | (go to last page, no pair) | 0x15619 | 0x15602 | 2 (0) | | 0x15615 | `GotoNextPageBehavior` | 0x1561A | 0x15603 | 88 (21) | | 0x15616 | `GotoPreviousPageBehavior` | 0x1561B | 0x15604 | 37 (24) | | 0x1561B | `GotoURLBehavior` | 0x15622 | 0x15605 | 45 (11) | | 0x1562C | `MovieBehavior` | 0x1562C | 0x15606 | 99 (6) | | 0x15636 | `ShowHideFieldsBehavior` | 0x15634 | 0x15607 | 29 (26) | | 0x15664 | `ClearFormBehavior` | 0x15650 | 0x15609 | 3 (1) | | 0x1566E | `SubmitFormBehavior` | 0x1565A | 0x1560A | 5 (1) | | 0x15678 | `OpenFileBehavior` | 0x15664 | 0x1560B | 1 (1) | | 0x156D3 | `GotoAnchorBehavior` | 0x156F0 | 0x15617 | 27 (18) | | 0x156DD | `GotoNextViewBehavior` | 0x15703 | 0x15619 | 12 (12) | | 0x156E8 | (view zoom, no pair) | 0x1570A | 0x1561A | 5 (0) | | 0x156F4 | `SoundBehavior` | 0x1571C | 0x1561B | 7 (2) | | 0x15625 | `GotoPageBehavior` | 0x156DE | 0x1561C | 54 (30) | | 0x1562F | (animation, no pair) | 0x1566C | 0x1561D | 16 (0) | | 0x1563F | `GotoStateBehavior` | 0x1566D | 0x1561E | 80 (37) | | 0x15643 | `GotoNextStateBehavior` | 0x1567D | 0x1561F | 17 (4) | | 0x15660 | `GotoPreviousStateBehavior` | 0x15680 | 0x15620 | 8 (4) | | 0x156F9 | `PrintFormBehavior` | 0x15721 | 0x15621 | 2 (1) | The class and the element match in 206 of 206 paired behaviours. The type code is constant per class in 547 of 547 objects. The three classes without a pair are named by their built-in name keys (`$$$/Forms Action/GoTo Last Page`, `…/View Zoom`, `…/Animation`); the converter leaves them out with a warning. **Data chunk.** All data chunks start with a common header: | Field | Contents | Evidence | |---|---|---| | u32 | UID of the field that owns the behaviour | 206 of 206 equal to the IDML parent button | | u32 | type code (table above) | constant per class, 547 of 547 | | flag byte, in-object string | the name: flag 1 and a built-in key `$$$/Forms Action/…` | flag 1 in 547 of 547 | | u16 | `EnableBehavior` (1 `true`) | 1 in 547 of 547; all 206 paired are `true`. That 0 is `false` is assumed | The type's fields follow (all u32 unless stated otherwise); the chunk ends after them in 546 of 547 objects. The exception is a 3.0 movie behaviour that ends after the operation. Big-endian objects (2 files of 8.0, 3 files of 4.0) parse to their end with the flag and string tag swapped (`big-endian.md`). | Type | Fields | IDML | Evidence | |---|---|---|---| | first, last, next, previous page, next view | navigation kind (0 first, 1 last, 2 previous, 3 next, 5 next view), zoom | `ZoomSetting` from the zoom | kind constant per class, 203 of 203 objects; zoom 64 of 64 | | go to page | navigation kind 6, page index, zoom | `PageNumber` = index + 1, `ZoomSetting` | 30 of 30 (pages 1 to 6) | | go to anchor | flag byte (0 in 27 of 27), in-object string, anchor u32, zoom | `FilePath`, `ZoomSetting`, `Properties/AnchorItem` (`type="long"`) | 18 of 18 | | go to URL | flag byte, in-object string | `URL` | 11 of 11, plain and `navto://` URLs | | open file | flag byte, in-object string | `FilePath` | 1 of 1 | | submit form | u32 (0 in 5 of 5), flag byte, in-object string | `URL` | 1 of 1 | | clear form, print form | none | | 1 and 1 | | show/hide fields | count n, n × (field UID, action: 0 hide, 1 show) | `FieldsToHide`, `FieldsToShow` | 26 of 26 | | go to state, next state, previous state | multi-state object UID, kind (0, 1, 2; constant per class, 105 of 105), u16 (0 in 105), u16 (1 in 45 paired, 0 in 3 unpaired), i32 state ID (-1 for next and previous) | `AssociatedMultiStateObject`; go to state `Properties/Statetype` (`type="long"`) = state ID | 45 of 45; state IDs 37 of 37 | | movie | movie UID, operation (0 `Play`), i32 navigation point | `Operation`, `NavigationPointID` | 6 of 6 (all -1) | | sound | sound UID, operation (0 `Play`, 1 `Stop`), f64 (1.0 in 5, 0.0 in 2 of 7), u32 (0 in 7) | `Operation` | 2 of 2 | | view zoom (no pair) | u32 (0 in 5) | | | - Zoom codes: 0 `InheritZoom` (every page navigation behaviour and 8 anchors), 2 `FitWindow` (10 anchors). No other code occurs. Movie operation 3 (2 unpaired objects) and sound operation 2 (1) are not known. An unknown code leaves the attribute out, with a warning. - `FieldsToHide` lists the UIDs with action 0 and `FieldsToShow` those with action 1, each in stored order, as references separated by spaces. With no such UID the attribute is written empty. The action is 0 or 1 in all 149 corpus entries. - `AnchorItem` is the stored u32 when `FilePath` is not empty (5 of 5, anchors in another file). When `FilePath` is empty it is 0 (13 of 13): the stored u32 is 0 in 8, and in 5 it is a UID with no object. - In the converter's output the button (206 of 206) and every `AssociatedMultiStateObject` (45 of 45), `FieldsToHide` UID (124 of 124) and `FieldsToShow` UID (25 of 25) exist as `Self`. - `MovieItem` and `SoundItem` are the stored movie and sound UIDs: each names a `Movie` or `Sound` of the same IDML (6 and 2 of 6 and 2; movies and sounds). A UID that is not a written movie or sound is left out. - The sound's f64 and last u32 and the submit u32 have no IDML attribute. **Values with no stored field.** These are the same in every sample, and no stored field was found for them. The converter writes them as defaults: | IDML | Value | Samples | |---|---|---| | `GotoAnchorBehavior` `AnchorName` | `$ID/` | 18 of 18 | | `GotoStateBehavior` `GoBackOnRollOff` | `false` | 37 of 37 | | `GotoNextStateBehavior`, `GotoPreviousStateBehavior` `LoopsToNextOrPrevious` | `true` | 8 of 8 | The second u16 of a state behaviour is 1 in all paired samples, which is consistent with `LoopsToNextOrPrevious="true"` but does not prove it. **`Name`.** No behaviour has a name of its own: the name is a built-in key with flag 1 in all 547 objects. IDML writes the key's name in the language of the exporting InDesign, which follows the saving edition (the XMP black name, saving edition): in all 26 pairs the edition matches the language of every behaviour name (1 English, 7 Korean and 18 Chinese documents). The converter writes the names below and leaves `Name` out for a key or edition without a sample. A name with flag 0 (no sample) would be written as stored. | Key (`$$$/Forms Action/…`) | English | Korean | Chinese | |---|---|---|---| | `GoTo Anchor` | `Go To Destination` (10) | `대상으로 이동` (8) | | | `GoTo First Page` | | `첫 페이지로 이동` (3) | `跳至第一頁` (4) | | `GoTo Next Page` | | `다음 페이지로 이동` (1) | `跳至下一頁` (20) | | `GoTo Previous Page` | | `이전 페이지로 이동` (1) | `跳至上一頁` (23) | | `GoTo Next View` | | `다음 보기로 이동` (12) | | | `GoTo Page` | | | `跳至頁面` (30) | | `GoTo URL` | | | `前往 URL` (11) | | `SwitchToMSOState` | | `상태로 이동` (13) | `跳至狀態` (24) | | `SwitchToNextMSOState` | | `다음 상태로 이동` (1) | `跳至下一個狀態` (3) | | `SwitchToPrevMSOState` | | `이전 상태로 이동` (1) | `跳至上一個狀態` (3) | | `Movie` | | | `影片` (6) | | `Sound` | | | `聲音` (2) | | `Show/Hide Fields` | | | `顯示/隱藏欄位` (26) | | `Open File` | | | `開啟檔案` (1) | | `Submit Form` | | | `送出表格` (1) | | `Reset Form` | | | `清除表格` (1) | | `Print Form` | | | `列印表格` (1) | The Chinese names are Traditional Chinese. Of the 93 trustworthy pairs with the black name `黑色`, 92 name their paper swatch `紙張` (Traditional Chinese) and one has no paper swatch; the corpus has no Simplified Chinese saving edition. **Attribute order.** The type's attributes first, in schema order, then `Name`, `EnableBehavior` and `BehaviorEvent`. Example: ``. **Not shown.** Behaviours of other field types (the schema allows most types on check boxes, radio buttons, text boxes, combo boxes, list boxes and signature fields; the corpus has none, and the converter leaves them out with a warning); `AnchorItem` of a live anchor in the same document; URLs stored with flag 1 (6 unpaired objects, key `$$$/Dialog/Behaviors/GoToView/DefaultURL`, and one empty submit URL), which the converter leaves out. ## Animation and timing Spreads and buttons keep timing lists, page items an animation, and the document a list of motion presets. Evidence: the 43 pairs of the corpus whose IDML has any of these elements (little-endian, versions 8.0 to 18.0; all trustworthy). The layouts are the same in all versions. A sweep of the first occurrence of each chunk in the spreads, buttons, spline items, groups and preferences of all 1,251 trustworthy pairs found non-empty timing lists, preset lists or animations only in these 43 documents. The converter writes all of them by the rules below, and `compare.py` finds all 3,783 of their values in its output for these 43 pairs. Strings below: - **Text string**: u32 length in UTF-16 code units, then text segments. - **Name**: a flag byte (1 = built-in key, `$ID/` in IDML) and an in-object string. ### Timing lists (chunks 0x2056D and 0x2056E) Spreads keep their timing lists in chunk 0x2056D, buttons (class 0x1450C) in chunk 0x2056E. Most page items also have chunk 0x2056D, with a count of 0. ``` u32 lists per list: u32 trigger, u32 groups per group: u32 targets per target: u32 UID, u32 role, u32 action, u32 (0 in all 163), f64 delay then: u32 plays, u16 (0 in all 159) ``` The chunk parses to its end in 96 of 96 owners. IDML writes a `TimingSetting` exactly when the list count is not 0: 43 of 43 spreads and 53 of 53 buttons with lists have one; 20 spreads and 119 buttons with count 0 have none. | IDML | Source | Evidence | |---|---|---| | `TimingList` `Self` | owner `Self` + `TimingSetting1TimingList` + list index (from 0) | 96 of 96 | | `TriggerEvent` | trigger code: 1 `OnPageLoad`, 5 `OnClick`, 6 `OnRelease`, 7 `OnRollover` | 43 / 2 / 45 / 6; spreads all 1 | | `TimingGroup` `Self` | list `Self` + `TimingGroup` + group index (from 0) | 159 of 159 | | `Plays` | u32 plays | 159 of 159 (all 1) | | `TimingTarget` `Self` | group `Self` + `i` + target UID in hex | 163 of 163 | | `DynamicTarget` | `u` + target UID in hex | 163 of 163 | | `DelaySeconds` | f64 delay | 163 of 163 (0 and 0.25) | | `TargetRole` | u32 role, in decimal | 163 of 163 | | `TargetAction` | u32 action, in decimal | 163 of 163 (all 132353) | | `Placement` | target index in its group (from 0) | 163 of 163 | Roles by target class: 132353 button behaviours (classes 0x1562C, 0x1563F, 0x15643, 0x15660, 0x156F4; 53 targets), 132354 movies (3), 132355 spline items, groups and multi-state objects (100), 132356 sounds (7). The stored value is written as is. Every target of the samples names an element the converter writes: a page item, a button behaviour, or a movie or sound (7 sound and 3 movie targets of 163; movies and sounds). The converter leaves out a target whose element it does not write, with a warning, and numbers `Placement` over the targets kept. A group or list left empty is left out too: no sample has an empty one (159 groups, 96 lists). Example, a spread with one list, one group and one target: `01 00 00 00 | 01 00 00 00 01 00 00 00 | 01 00 00 00 | 63 04 00 00 03 05 02 00 01 05 02 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | 01 00 00 00 00 00`. IDML writes the spread's `TimingSetting` after `FlattenerPreference` and before the pages, and the button's after `TextWrapPreference` and before the `State` elements (53 of 53). Multi-state objects have chunk 0x2056D with count 0 in all samples; where their timing lists would be stored is not shown. ### Motion presets (class 0x20517) Chunk 0x2053E of the preferences object (class 0x2202): u32 *n*, then *n* preset UIDs. IDML writes one `MotionPreset` in designmap.xml per UID, in this order, after the `NamedGrid` elements (63 presets in 30 documents; 5 more documents have the chunk with *n* = 0 and no `MotionPreset`). Chunk 0x20532 of a preset: | Field | IDML | |---|---| | text string | `Properties/Contents`, as is (63 of 63; IDML writes it as CDATA) | | 4 bytes | not mapped; they vary without pattern (`00 00 00 00`, `5C 00 50 00`, …) | | name | empty, flag 1 in all 63 | | name | `Name`: `$ID/` and the string for flag 1 (63 of 63, all flag 1) | The chunk parses to its end in 63 of 63. The stored name is not always the one inside the preset's XML (`appear` stored, `Appear` in the XML's ``); IDML takes the stored name (2 of 2). ### Animation settings (chunk 0x2055F) Every spline item (class 0x6201), group (0x401), button (0x1450C) and multi-state object (0x145D8) has chunk 0x2055F, and so do placed graphics. Two bytes `00 00` mean no animation: IDML writes no `AnimationSetting` (1,996 of 1,996 objects with this chunk in the 43 documents, items and graphics). A longer chunk means an animation, and IDML writes `AnimationSetting` (100 of 100: 57 rectangles, 22 text frames, 13 ovals, 6 multi-state objects, 2 groups). Longer chunks also occur in 12 objects that are not written as page items (classes 0x129, 0x1D6, 0x301, 0x3E03, 0x8C41, 0x8C4B, 0xCA8B). | Offset | Type | IDML | Evidence | |---:|---|---|---| | 0 | u16 | not mapped | 1 in 100 of 100 | | 2 | u16 | `HasCustomSettings`: 1 `true`, 0 `false` | 100 of 100 (20 `true`) | | 4 | u32 | `Properties/Preset type="object"`: the preset UID | 100 of 100 | | 8 | f64 | `Duration`, in seconds | 100 of 100 (1, 0.5833…, 1.0416…) | | 16 | u32 | not in IDML: 24 (frames per second) | 100 of 100 | | 20 | u32 | not in IDML: frames, `Duration` × 24 | 100 of 100 | | 24 | u32 | `DesignOption`: 0 `FromCurrentAppearance`, 2 `ToCurrentLocation` | 44 / 56 | | 28 | u32 | not mapped: 0 in all | 100 of 100 | | 32 | u32 | `Plays` | 100 of 100 (87 × 1, 13 × 2) | | 36 | u16 | not mapped: 0 in all | 100 of 100 | | 38 | u32 | `EaseType`: 0 `NoEase`, 4 `CustomEase` | 3 / 97 | | 42 | 2 × f64 | `TransformOffsets`, each rounded to 6 significant digits | 100 of 100 (`0.499924 0.499886`) | | 58 | | motion path key frames, motion path points, properties (below) | | From offset 58: ``` u32 n key frames of the motion path per key frame: u32 frame, u32 (2 in all 155), f64 x, f64 y u32 m points of a motion path without key frames per point: u32 (2 in all 4), f64 x, f64 y u32 p animated properties per property: text string (name), text string (the same name again), u32 k keys, per key: u32 frame, u32 (6 in all 528), f64 value ``` The chunk parses to its end in 100 of 100. - `Properties/MotionPath`: one `AnimationDataPathKeyFrameType KeyFrame="frame"` per key frame, with `PathPoint Anchor="x y" LeftDirection="x y" RightDirection="x y"` (98 of 98 with *n* > 0; left and right direction equal the anchor in all 155 points). No `MotionPath` when *n* = 0 (2 of 2). - `Properties/MotionPathPoints`: when *m* > 0, a `PathPointArray` with one `PathPointType` per point, anchor and both directions `x y` (2 of 2; both have *n* = 0). - Properties by name: `Alpha_Amount` → `OpacityArray`, `Rotation_Z` → `RotationArray`, `Scale_X` → `ScaleXArray`, `Scale_Y` → `ScaleYArray`; each key is `AnimationDataKeyFrameType KeyFrame="frame" Value="value"` (all 396 arrays). A property with no keys has no element (1 of 1), and neither has a property that is not stored (4 items have no `Alpha_Amount`, and no `OpacityArray`). The second name equals the first in 396 of 396. - `Properties` children are in the order `Preset`, `MotionPath` or `MotionPathPoints`, `OpacityArray`, `RotationArray`, `ScaleXArray`, `ScaleYArray` (100 of 100). Numbers other than `TransformOffsets` are in the shortest form (`764.0000000000001`, `-552.6140386840932`, `0.5833333333333334`; all 100 elements). IDML writes `AnimationSetting` after `ObjectExportOption` and before child items and placed graphics, and after the `State` elements of a multi-state object (100 of 100). ### Values without a stored field These attributes have one value in every sample, and no stored field was found for them. The converter writes that value: | Attribute | Value | Samples | |---|---|---:| | `TimingGroup/@PlaysLoop` | `false` | 159 | | `TimingTarget/@ReverseAnimation` | `false` | 163 | | `AnimationSetting/@PlaysLoop` | `false` | 100 | | `AnimationSetting/@InitiallyHidden` | `false` | 100 | | `AnimationSetting/@HiddenAfter` | `false` | 100 | | `MotionPreset/@EditLocked`, `@DeleteLocked`, `@NameLocked` | `false` | 63 each | | `MotionPathPoints/@PathOpen` | `true` | 2 | Candidate fields are 0 in every sample: in a timing target the u32 after the action (`ReverseAnimation`?), after a group's plays the u16 (`PlaysLoop`?), in chunk 0x2055F the u32 at 28 (`InitiallyHidden` and `HiddenAfter`?) and the u16 at 36 (`PlaysLoop`?). Mapping them needs a sample with a `true` value. ### Not shown - Codes without samples: `DesignOption` 1 (the schema's third value is `ToCurrentAppearance`), `EaseType` 1 to 3, trigger codes other than 1, 5, 6 and 7. The converter leaves such an attribute out with a warning. - Curved motion paths: every path point has u32 2 before its coordinates and left and right directions equal to the anchor. - Chunk 0x2056C of spreads (a UID list), the 4 bytes after a preset's contents and the frame fields at 16 and 20 of chunk 0x2055F are not needed for IDML. - Big-endian files: no samples. ## Placed InDesign pages (class 0x6607) A placed page of another InDesign document is a child of a frame, like an image, and has the chunks of placed graphics (transform 0x151, bounds 0x1633, link 0x8CBC, text wrap). IDML writes it as `ImportedPage`. Chunk 0x2505 (68 bytes) holds the page: its u32 at 0 plus 1 is `PageNumber`. The converter writes `ImportedPage` with `PageNumber`, the attributes of placed graphics (`ImageTypeName` from the link format, `$ID/InDesign Format Name`), `Properties/GraphicBounds`, `TextWrapPreference` and `Link`. Not decoded, and left out: `ImportedPageCrop` (41 of 42 are `CropContent`, 1 `CropBleed`; the byte at 18 of 0x2505 is 0 in 28 and 1 in 13 `CropContent` items, so it is not that field), `MetadataPacketPreference` (an XMP packet of an object of class 0x12609 that chunk 0x12629 names), `GraphicLayerOption` and `PDFAttribute`. `ClippingPathSettings` is described under clipping path settings. Evidence: 42 `ImportedPage` elements in 8 pairs (38 in 7 trustworthy pairs). `ItemTransform`, `GraphicBounds` and `PageNumber` equal the IDML in 42 of 42. ## Graphics pasted without a link A graphic with no link (its chunk 0x8CBC names no link) can hold its file itself: | Class | Chunk | Contents | |---|---|---| | Image (0x1702), EPS (0x6601) | 0x8C23 | u32 raw data object (0 = none) | | PDF (0x2501) | 0x2521 | u32 raw data object (0 = none) | EPS graphics (class 0x6601) without a link name their raw data object in chunk 0x8C23 too: over the trustworthy pairs of the corpus after 2026-10 the converter reproduces 497 of the 515 EPS `Contents` (483 were missing before; 18 differ and are not examined). Linked graphics can have the chunk as well; their IDML has no `Contents`, and the converter does not use it for them. Evidence from the corpus pairs: the raw data object equals the IDML `Contents` for 124 of 124 PDFs and 2 of 2 images without a link. In the whole corpus, two linked and embedded PDFs also have a 0x2521 object, different from the link resource's; with no pair to compare, the converter uses the link resource's. ### Images that keep their file in their preview An image (class 0x1702) without a link whose chunk 0x8C23 is 0 or absent keeps its file in its preview (next section): chunk 0x170D names an object of class 0x1708, whose chunk 0x119 names a raw data object. IDML writes that object's bytes as `Contents`. The converter does so only for images, and not for an image whose chunk 0x8C1B names an object (see the end of this section). Linked graphics, EPS and PDF graphics do not use their preview as `Contents`. Evidence, over the 9,608 graphics (`Image`, `EPS`, `PDF`, `SVG`, `WMF`, `ImportedPage`) in the reference IDMLs of the 1,252 trustworthy pairs, each paired with its INDD object by `Self`: | Images | Count | IDML `Contents` | Preview data equals `Contents` | |---|---:|---:|---:| | No link, 0x8C23 = 0 | 359 | 359 | 359 | | No link, no chunk 0x8C23 | 17 | 17 | 17 | | No link, 0x8C23 names a raw data object | 77 | 77 | 4 (the 0x8C23 object equals it in 77) | | Linked, embedded | 101 | 101 | 38 | | Linked, not embedded | 6,886 | 0 | – | - The 376 images of the first two rows are in 31 pairs, InDesign 8.0 to 18.x. None of them lacks `Contents` in its IDML. Of the 362 that the converter writes, 357 have chunk 0x8C1B set to 0 and 5 have no chunk 0x8C1B. - Over the whole little-endian corpus (5,734 distinct files), 2,456 images without a link have 0x8C23 = 0 or no 0x8C23. All of them have the complete chain: 0x170D names a class 0x1708 object whose chunk 0x119 names a raw data object. No image without a link has a 0x8C23 that names anything other than 0 or a raw data object. 1,773 of them, in 150 files (InDesign 3.0 to 21.x), have 0x8C1B = 0 or no 0x8C1B; the converter writes their preview data as `Contents`. 15 more are in 3 big-endian files (InDesign 8.0 and 11.x). - The 17 images with no chunk 0x8C23 have no chunk 0x8CBC either; 5 of them (InDesign 8.0 and 11.x) have only 11 or 12 chunks. - Every EPS (483) and PDF (111) graphic without a link names a raw data object, so the case occurs only for images. For EPS graphics without a link the preview never equals `Contents` (0 of 483). - Formats of the 376 `Contents`: 357 in the `EPSImage` form below, 17 JPEG, 2 TIFF. Of the 2,456 in the whole corpus: `EPSImage` 1,539, JPEG 514, TIFF 391, PNG 12. - The converter reproduces 362 of 362 such `Contents` that it writes in the trustworthy pairs, in 20 documents, with no `Contents` where the reference has none. The other 14 images belong to structures the converter leaves out. **The `EPSImage` form.** 357 of the 376 `Contents` start with the 8 ASCII bytes `EPSImage`, then five u32 (little-endian): bytes per row, width, height, bits per channel, channels. Pixel data follows, height × bytes per row bytes, with nothing after it: the length is 28 + height × width × channels in 357 of 357 (8 bits per channel in all; 3 channels in 182, 4 in 175). All 357 have 0x8C23 = 0. The converter copies the bytes and does not read this form. **Images that name an old link object.** 762 images without a link and with 0x8C23 = 0 or no 0x8C23, in 79 files of InDesign 2.0 to 5.0 (683 images in 53 little-endian files, 79 in 26 big-endian files), have no chunk 0x8CBC, and their chunk 0x8C1B (u32) names an object of class 0x8C01. That object's chunk 0x8C2A names the image back in 759 of 762. No file of these versions has an IDML pair, so it is not known whether IDML would write the preview as `Contents`. The converter treats these images as linked graphics whose link it does not read: no `Contents`, and the preview goes to `Conversion::previews`. In files of later versions, 0x8C1B of such an image is 0 or absent in every case. Not known: why these images keep their file in the preview rather than in a raw data object named by 0x8C23. None of the 77 images whose 0x8C23 names a raw data object has an `EPSImage` `Contents`. The one WMF graphic without a link in the pairs has a `Contents` different from its preview; it is not examined. ## Graphic previews Placed graphics also have chunk 0x170D, the UID of an object of class 0x1708, whose chunk 0x119 names a raw data object. In the 245 distinct little-endian files, these objects are TIFF (613), JPEG (411), PNG (307), GIF (27) and one other. They equal the IDML `Contents` of embedded links in 38 of 101 and of EPS graphics without a link in 0 of 483, so they are most likely screen previews. The exception is the images of the previous section, whose preview is their file. The package does not include the other previews: IDML has no place for a preview. `indd::convert` returns the previews of graphics without `Contents` in `Conversion::previews`, so an application can show one when the linked file is missing. An image whose `Contents` is its preview's data is not in that list: the package holds its file. ## Embedded data in IDML The reference IDML files write `Contents` inside the graphic's `Properties` as base64 (with `=` padding) in lines of 76 characters joined by a line feed, with no line feed at the end. The text is split into CDATA sections of 262,144 characters. The converter writes the same form. ## Stories (0x201) Chunk 0x223: u32 length, u16, u32 first strand, UID list of more strands, then other fields. Each strand has chunk 0x261: u16 count, then (u32 length, u32 data object UID) pairs. Each data object has chunk 0x262: | Field | Contents | |---|---| | u32 | Kind: 0x202 text, 0x203 character style runs, 0x204 paragraph style runs | | u32 | Owning strand | | u16 | Run count | | runs | Each: u32 record size, then the record | | u32 | Total length | Each record starts with a u32 run length in UTF-16 code units. Text records continue with text segments. Style records continue with the style UID. **Paragraph-style records and paragraph ranges.** Counts are over the INDD files of the 1,252 trustworthy pairs and the 37,156 stories that both IDMLs have. - Every paragraph-style record ends at a paragraph end (U+000D): 87,496 of 87,496 records. - After its text attribute list (`attributes.md`), a paragraph-style record has a u16 count and that many u32 values. In 82,842 of the 87,496 records they are the lengths of the record's paragraphs, in order, and add up to the run length. In the other 4,654 some values have high bits set (for example 0x10000F74). The converter does not read these values. - IDML starts a `ParagraphStyleRange` at every paragraph-style record, also between neighbouring records with the same style and the same decoded attributes. There are 2,496 such boundaries in 459 stories: 913 between two data objects of the strand and 1,583 within one. 153 of them are between records whose bytes after the run length are identical. Other rules split a record's range further (XML elements, `xml.md`; tracked changes, below). - With one range per record, the text lengths of the non-empty paragraph ranges equal the reference in 37,133 of 37,156 stories, including 455 of the 459 stories with equal neighbours. No story has more ranges than the reference. Merging neighbours with equal formatting matches 36,810 stories. Of the 23 other stories, 3 have different text and 20 have ranges that the reference splits inside one record (`unknowns.md`, resources and text). - Character-style records with the same style and decoded attributes are neighbours only across a data-object boundary: 3,288 boundaries in 533 stories, none within one data object. IDML splits there too. The converter writes a `CharacterStyleRange` per character-style record (and further cuts), and the character range lengths equal the reference in 525 of these 533 stories and in 37,112 of all 37,156. **Stories outside the document's list.** The document's story list does not name the stories of text frames anchored in the XML backing story. IDML writes them like the others and lists them at the end of `StoryList`: one trustworthy document (in two pairs) has 9 such stories, whose frames are anchored in its backing story; its IDML has all 9 after the listed stories. The converter adds the stories of the text frames anchored in the backing story, and of frames anchored in those stories, after the listed ones, in text order. The order IDML gives them is not that order (it starts with a later one), so `StoryList` stays different there. **Story settings.** Three chunks of the story object (trustworthy pairs, 18,610 stories): | Chunk | Layout | IDML | Without the chunk | Evidence | |---|---|---|---|---| | 0xA44C | flag byte (1 for a built-in key), in-object string | `Story` `StoryTitle` (`$ID/` and the key for flag 1) | `$ID/` | 18,610 of 18,610 (30 with a name) | | 0x50F96 | u16: 1 | `StoryPreference` `StoryDirection="RightToLeftDirection"` | `LeftToRightDirection` | 18,610 of 18,610 (780 right to left, 10 documents) | | 0x2EE (16 bytes) | u16 at 0; f64 at 2; u16 at 10 (not identified); u16 at 12 (1 `true`); u16 at 14 | `StoryPreference` `StoryOrientation` (u16 at 0: 0 `Horizontal`, 1 `Vertical`), `OpticalMarginSize`, `OpticalMarginAlignment`, `FrameType` (u16 at 14: 0 `TextFrameType`, 1 `FrameGridType`) | 12 (13 Q where text sizes are in Q, below), `false`, `Horizontal`, `TextFrameType` | alignment 18,610 of 18,610; size below; orientation and frame type below | `OpticalMarginSize` over the 37,156 stories of the 1,252 trustworthy pairs of 2026-10 (DOM 8.1 to 21): | Story | Stories | IDML `OpticalMarginSize` | |---|---:|---| | with chunk 0x2EE | 3,805 | the f64 at 2 (all) | | no chunk, `TextSizeMeasurementUnits` `Points` | 32,979 | 12 (all) | | no chunk, `TextSizeMeasurementUnits` `Q` | 372 (76 pairs) | `9.2125984251969` (13 Q, all) | The Q documents are DOM 12.1, 13, 16.2, 18 and 20.3. The text size unit is the u32 at 12 of preferences chunk 0x1202 (`preferences.md`, view). The size of new frames in the preferences is not the source: in 3 pairs it differs from the stories without the chunk. Two DOM 8.1 pairs in points have 13 Q in the preferences and 12 in their 13 stories; one DOM 16.2 pair in Q has 12 in the preferences and 13 Q in its 3 stories. The text size unit gives the story value in all three. Another value of the direction or alignment u16 leaves the attribute out. Orientation and frame type, over the 28,669 stories of all corpus pairs (stale pairs included), with chunk 0x2EE as (u16 at 0, u16 at 14): | Chunk | Stories | IDML `FrameType`, `StoryOrientation` | |---|---:|---| | none | 25,206 | `TextFrameType`, `Horizontal` (all) | | (0, 0) | 3,359 | `TextFrameType`, `Horizontal` (all) | | (0, 1) | 4 | `FrameGridType`, `Horizontal` (all) | | (1, 0) | 47 | `TextFrameType`, `Vertical` (all) | | (1, 1) | 53 | `FrameGridType`, `Vertical` (all) | The vertical and frame grid stories are in documents typeset in Japanese (DOM 12, 13 and 18). Another frame type value leaves `FrameType` out. For another orientation value the converter uses the orientation of the story's frames (text orientation, below). **Frame grids.** Over all pairs of the corpus of 2026-10: - Chunk 0xCD41 of a text frame's multi-column frame object (class 0x263, the child that the frame's chunk 0x15B lists) is `00 00` for a plain frame (23,537 of 23,537) and starts with u16 1 for a frame grid (248 of 248; 100, 146, 192 or 238 bytes). IDML writes an empty `GridDataInformation` in the `TextFrame` for exactly these 248 frames, after `Properties`, with `GridView="GridViewEnum"`, `CharacterCountLocation="BottomAlign"` and `CharacterCountSize` equal to the `CharacterCountSize` of the document's story grid preference (chunk 0xCD2E, `preferences.md`): 248 of 248 in 39 documents. The rest of the chunk is not decoded. - The stories of these frames are the frame grid stories of chunk 0x2EE (above): 57 of 57. They have chunk 0xCD02 in the story object, with the layout of the page chunk (layout grid, below), and IDML writes a `GridDataInformation` in the `Story`, after `StoryPreference` and before `InCopyExportOption`, with the same values: 57 of 57 for every attribute. 223 other stories have the chunk but are not frame grid stories, and IDML writes no `GridDataInformation` for them. One frame grid story in the trustworthy pairs has the IDML element but no chunk. The converter writes both elements by these rules. **Owned items (run kind 0x209).** Each record: u32 run length, u16 count, then that many (u32 class, u32 UID) pairs, the objects owned by the text position at the start of the run. An item anchored in text has the character U+FFFC at that position and is owned through an object of class 0x262, whose chunk 0x2C8 (u32, u32, UID list) lists the anchored page item. IDML writes the page item element in place of the U+FFFC. A text variable instance (class 0xCA64) is owned by a U+0018 at its position; see `text-variables.md`. Footnotes (class 0x24F) and endnotes (class 0x2805) are owned by a U+0004 (`footnotes.md`), text and paragraph destinations through class 0x1353C by a U+FEFF (`hyperlinks.md`). **Text owners.** The text-owner strand (run kind 0x2A4, `tables.md`) gives each stretch of a story's text an owner. In the 495 trustworthy pairs of the research run the owners were the story itself (19,418 stretches), a table (class 0xB608, 11,844), a deleted text (class 0xA40A, 1,889), a footnote (class 0x24F, 502) and a note (class 0xA429, 186). The cell field is 1 for footnotes and 0 for the story, deleted texts and notes. Each footnote, note and deleted-text stretch ends with U+000D (2,577 of 2,577), which IDML does not write, as for a table cell. **Ruby strand (run kind 0x420D).** A story can list a strand of class 0x420C (chunk 0x4216: u32 data object, u32 story) whose data objects (class 0x42B9) have chunk 0x262 with run kind 0x420D. The runs cover the whole story text. After the run length a record has u16 1 if attributes follow (0 otherwise), a u16 count and text attribute records (`attributes.md`). Over the little-endian corpus: 911 stories, 11,514 runs, 7,442 with attributes. The records hold only ruby attributes, a copy of those on the character runs. IDML does not use the copy: all 185 IDML ruby ranges of the pairs have the character run's values, and where the two differ (5 ranges, whose strand string lacks the U+3000 separators; 16 ranges with 0x423E only on the character run) IDML follows the character run. IDs found only in the strand, such as 0x4232 in 6,176 records, are not in IDML. The converter skips this strand. **Notes (class 0xA429).** A note is owned by a U+FEFF (186 of 186), and IDML writes a `Note` element in its place, inside the character range; the `Note` holds the note's text stretch as paragraph and character ranges. Chunk 0xA412: u32 length *n* and *n* UTF-16 units of text segments (the user name), then two FILETIME values (u32 high part, u32 low part; 100 ns since 1601-01-01 UTC), creation and modification, then u16 (0 in all 186). Attributes, in this order: | Attribute | Value | |---|---| | `Collapsed` | `false` in all; the trailing u16 may hold it, but it never varies | | `CreationDate`, `ModificationDate` | the two times as local time, by the link time rule below | | `UserName` | the user name; `$ID/Unknown User Name` when the first document user of that name has flag 2 | | `AppliedDocumentUser` | `dDocumentUser` of the first document user (document chunk 0xA443) with that name; `n` if none has it | Check: over all pairs the converter's notes equal the IDML notes on these five attributes in 211 of 211 (compared as multisets per pair, since `Note` has no `Self`). **Tracked changes.** Deleted text is an object of class 0xA40A, owned by the character that follows the deletion (any character; 1,696 of 1,889 in the research run were U+F035). Its text is the stretch the text-owner strand gives to its UID, without the final U+000D. IDML writes it as `` inside the character range of the owning character, before that character and before any element that replaces it, holding the deleted text as paragraph and character ranges (one `Change` per object: 1,889 of 1,889 in 21 pairs). A story strand of class 0xA465 has runs of kind 0xA466 that cover the whole story text (positions count characters). Each record: u32 run length, u16 entry count, then per entry a flag byte (0), a string (the user name, in-object form), u16 kind, FILETIME (u32 high part, u32 low part) and u16 (0). Kind 1 is a deletion at the start of the run, with the user and time of that deleted text; kind 2 marks the run's text as inserted, which IDML writes as `` around it (2,073 of 2,073 entries, one element each). Records of deleted-text stretches have no entries. `Change` attributes, in this order: `Date`, `ChangeType`, `UserName`, `AppliedDocumentUser`, the last two by the rule for notes. **Where IDML puts an inserted-text `Change`.** IDML writes it at the lowest level that holds its whole extent, as for hyperlink sources (`hyperlinks.md`, extent and placement). The 3,962 `Change` elements of the 21 trustworthy pairs with tracked changes sit as follows: | Parent | Content | Elements | |---|---|---:| | `CharacterStyleRange` | inserted text within one range, also across a `Br` where the range goes on, and empty changes | 1,989 | | `CharacterStyleRange` | deleted text | 1,887 | | `CharacterStyleRange` | a `HyperlinkTextSource` with the change's extent | 43 | | character-level `HyperlinkTextSource` | inserted text from inside the source to its end | 13 | | `ParagraphStyleRange` (story or cell) | `CharacterStyleRange` children, and paragraph-level sources inside the extent | 23 | | paragraph-level `HyperlinkTextSource` | `CharacterStyleRange` children | 4 | | `Cell` | deleted text at the cell's end | 2 | | `Story` | `ParagraphStyleRange` children | 1 | So a change over several character ranges of one paragraph range is a child of the `ParagraphStyleRange`, one over several paragraph ranges a child of the `Story`, and one over several character ranges inside a paragraph-level source a child of the source. A change with the extent of a source holds it (`hyperlinks.md`, sources in tracked changes). The ranges are split at the ends of such a change, as at a source. The converter writes deleted text before the owning character. It writes inserted text that lies in one text run in a `Change` inside the character range. Inserted text over several runs (once the runs are split at the paragraph-level sources) is written above them: as a child of the paragraph range, or of the story where the runs belong to more than one paragraph range, which then ends and starts again at the change's ends. Inside a lifted change the character ranges have no `Change` of their own. A table, an anchored item (the schema allows no page item in a `Change`) or an XML marker in the extent keeps the change inside each character range, opened again after the element; so does a change that does not nest with a paragraph-level source. Over the 21 trustworthy pairs the output has 3,969 `Change` elements, and 3,960 of the 3,962 reference elements have an equal one (`Date`, `ChangeType`, `UserName`, compared as multisets per pair). The differences are the 4 changes of the export defect below and a few changes still split. compare.py gains 13 range starts at the ends of lifted changes. Where a hyperlink source starts with inserted text, IDML writes an empty `Change` before the source (`hyperlinks.md`, sources at inserted text). **Export defect.** In one trustworthy pair, 4 inserted-text changes start inside a paragraph-level source and end after it. IDML writes each inside the source, holding the text past the source's end, and writes text after the source that the INDD stores once. The converter writes the INDD's text once, so this story counts as "story text differs" in compare.py. A deletion owned by the U+000D that ends a table cell, which no character range holds, is the last child of the `Cell`, after its last `ParagraphStyleRange` (2 in one trustworthy pair, 1 in a stale pair; every other deletion of the corpus is owned by a character inside a range). With it the converter writes all 1,889 deleted-text `Change` elements of the trustworthy pairs. **Dates of notes and changes.** IDML writes `CreationDate`, `ModificationDate` and `Date` as local time of the exporting computer, like link times: the converter applies the link time rule below (the offset of the XMP date nearest in day of the year). With it, 211 of 211 notes and 6,024 of 6,027 changes have an equal element in the output; the other 3 are deleted texts, not examined further. The 21 pairs with tracked changes come from at most two computers in one time zone, so the rule is checked on one time zone only. **Index markers (class 0x13006).** An index marker is owned by a U+FEFF, which is not text in IDML: IDML writes a `PageReference` in its place. Its layout and attributes are in `index.md`. **Anchored object settings (chunk 0x2800).** The anchor object (class 0x262), object styles and the preferences have a 62-byte chunk 0x2800: | Offset | Contents | IDML `AnchoredObjectSetting` | |---|---|---| | 0 | f64 | `AnchorYoffset` | | 16 | f64 | `AnchorXoffset` | | 32 | f64, the negative of the value at 0 in every sample | | | 40 | u16: 0 `InlinePosition`, 1 `Anchored`, 2 `AboveLine` | `AnchoredPosition` | | 42 | u16: 1 `TextFrame`, 2 `PageMargins`, 3 `PageEdge`, 4 `AnchorLocation` | `HorizontalReferencePoint` | | 44 | u16: 2 `PageMargins`, 3 `PageEdge`, 4 `LineBaseline`, 6 `Capheight` | `VerticalReferencePoint` | | 46, 50 | u16, u16 (below) | `AnchorPoint` | | 48 | u16: 0 `RightAlign`, 1 `CenterAlign`, 2 `LeftAlign` | `HorizontalAlignment` | | 52 | u16: 0 `TopAlign`, 1 `CenterAlign`, 2 `BottomAlign` | `VerticalAlignment` | | 54 | u16: 0 `false`, 1 `true` | `SpineRelative` | | 56 | u16: 0 `false`, 1 `true` | `PinPosition` | `AnchorPoint` from the u16 at 46 and at 50: (0, 2) `BottomRightAnchor`, (2, 2) `BottomLeftAnchor`, (2, 0) `TopLeftAnchor`, (0, 0) `TopRightAnchor`, (2, 1) `LeftCenterAnchor`. The field at 46 is 0 for a right and 2 for a left point, the one at 50 is 0 for top, 1 for centre and 2 for bottom, as the alignment codes; the converter writes only these five combinations. Evidence, corpus of 2026-10: the 6,363 object styles whose name matches one IDML style (5,457 in trustworthy pairs), the 1,076 anchored items matched by `Self` with their IDML element (944 trustworthy; where the item element lacks an attribute, its object style's value), and the 1,295 preference chunks (`preferences.md`). Every field above matches in every one. Codes seen: `AnchoredPosition` `Anchored` in 41 and `AboveLine` in 37 styles, in 116 and 2 items; `SpineRelative` `true` in 2 styles (the only ones with 1 at 54); `HorizontalAlignment` `RightAlign` in 24 and `CenterAlign` in 37 styles. On the items, 42 and 44 are (4, 6) in 4 items, (2, 2) in one, (3, 3) in one and (1, 4) in the others. The two fields change together in every sample, so which one is the horizontal point is assumed from the order of the IDML attributes; codes 2 and 3 mean the same in both. IDML writes an anchored item's `AnchoredObjectSetting` by the category rule of other object style settings: every value when the item's object style turns the category off (`EnableAnchoredObjectOptions`, ID 0xCA2F missing from the style's category list; 670 items, all with every value), and otherwise the values that differ from its object style (406 items, of `[None]` or of styles with the category on). Where it writes every value, it also writes `LockPosition="false"` and `AnchorSpaceAbove="0"`, the values of every such element in the corpus (`idml-values.md`, anchored object settings of page items). **Items inside an anchored item.** The children of an anchored group (and the items inside them) have an `AnchoredObjectSetting` too. IDML applies the rule above to each of them with the anchor's chunk 0x2800 values and the child's own object style: every value where the child's style turns the category off, else the values that differ from that style. With this rule the converter writes 584 more `AnchoredObjectSetting` elements over the trustworthy pairs of the corpus after 2026-10 (344 on text frames, 114 ovals, 61 rectangles, 30 polygons, 22 graphic lines, 7 groups, 6 text boxes), with every value equal and no extra value; 6,503 values of these elements were missing before, and 388 in 2 or 3 documents per element still are (not examined). **Positions count characters.** A text record's run length counts UTF-16 code units, but every other position counts characters, a surrogate pair being one: the lengths in a strand's chunk 0x261 (also for the text strand), and the run lengths of style runs, owned items and text owners. Evidence: the 6 stories of the little-endian corpus whose text has a character outside the Basic Multilingual Plane (emoji, all in one pair). In all 6 the text records add up to the number of UTF-16 units, and the other strands and all chunk 0x261 lengths to the number of characters; with that reading, each emoji gets the character style range IDML gives it (6 of 6). The converter applies the same reading to hyperlink range trees, for which no sample with such a character exists. U+FEFF characters can be XML markers, which IDML does not write (`xml.md`). INDD stores a forced line break as U+000A; IDML writes it as U+2028. The last paragraph return of a story is not written to IDML. ## Story links and placed text Stories placed from another story (linked stories), from a Word or Excel file, and the text frames that linked page items copy, carry the elements below. Evidence base: the 159 distinct corpus pairs whose IDML has any of them (158 little-endian), and for the presence rules all 45,640 stories of the distinct little-endian pairs. No story has an element below without its stored data, and none has the data without the element. ### Link lists (chunk 0x8CBC) Chunk 0x8CBC is a list of link UIDs: 4 bytes (`01 01 00 00`), u32 count *n*, then *n* UIDs. It occurs on stories (0x201), spline items (0x6201), groups (0x401), graphics, link resources and more; the empty list (8 bytes) is the common case (28,569 spline items). Graphics and movies read the UID at offset 8, the first entry. No object in the corpus has more than one entry. ### Story links A story with a non-empty 0x8CBC list has a `Link` child in IDML, and `Link Self` is `u` and the listed UID in hexadecimal: 47 of 47 stories whose UID is in both files; no story with a link lacks the IDML `Link`, and none the other way round, over the 45,640 stories. The link object has the layout of a graphic link (placed graphics, links), with the story's UID at offset 16 of chunk 0x8C9B (47 of 47), and its resource (class 0x8C41) the same chunk 0x8C92: | IDML | Equal | |---|---| | `LinkResourceURI` | 45 of 47 (2 links to another document, resolved to another path at export) | | `LinkResourceFormat` (`$ID/` and the format name) | 47 of 47: `Internal Linked Story` 28, `External Linked Story` 2, `Microsoft Office Word 2007 Import Filter` 17 | | `LinkImportStamp` | 47 of 47 (a plain number such as `1653409666` for linked stories, `file …` for Word files) | | `LinkResourceSize` | 47 of 47 (`0~0` for linked stories, the file size for Word files) | | `LinkResourceModified`, `ShowInUI` | 47 of 47 each | | `StoredState="Contained"` | 47 of 47 (resource field 3, placed graphics, link resources) | `RenditionData` and `PDFIdentifier` follow the rules of graphic links: present on the 17 version-14 Word links and the 49 version-21.0 links, absent on the 30 version-10 links. Link object classes (`LinkClassID` is the class in decimal): | Class | `LinkClassID` | Links | URI | |---|---|---|---| | 0x8C42 | `35906` | all graphic links; 17 stories placed from Word | `file:` | | 0x21D03 | `138499` | 30 linked stories | `idinternalcontent:`, `idexternalcontent:?` | | 0x21D32 | `138546` | 3 linked page items (text frame, group) | `idinternalSharedArticle:`, `idexternalSharedArticle:` | The converter writes story links. Links of page items (class 0x21D32) are not written: their page item's `LinkedPageItemOption` was not studied. ### Link source IDs (`LinkResourceId`) `idinternalcontent:` names the story a link copies by an ID, and that story has `LinkResourceId=""`. A text frame can have one too (the source of a linked page item). The IDs are in chunk 0x21D06 of the document object (UID 1): | Offset | Type | Contents | |---|---|---| | 0 | u32 | not identified (non-zero even when the list is empty) | | 4 | u32 | count *n* | | 8 | *n* × (u32, u32) | ID, UID of a story or spline item | IDML writes `LinkResourceId=""` (decimal) on the element whose `Self` is that UID. In 36 of 36 distinct pairs whose IDML has any `LinkResourceId`, the set of (UID, ID) equals the chunk's list exactly (195 values: 189 `Story`, 6 `TextFrame`; 31 trustworthy documents). In the other 1,500 pairs the list is empty. Listed UIDs are stories and spline items, all `TextFrame` in IDML. IDs are below 32,768. The converter writes the attribute on stories and text frames; a listed UID that is not written is ignored. ### Linked story options (chunk 0x21D12) Chunk 0x21D12 (10 bytes) on a story. IDML writes `Story/LinkedStoryOption` when the story has the chunk and a link (30 of 30). Two stories with the chunk and no link have no `LinkedStoryOption`; stories placed from Word have no chunk. | Offset | Seen | IDML | |---|---|---| | 0–5 | `00 00 01 00 00 00` in all 30 | `UpdateWhileSaving="false"`, `WarnOnUpdateOfEditedStory="true"`, `RemoveForcedLineBreaks="false"` in all 30 (which byte holds which is not shown; the converter writes these values) | | 6 | u32 | `ApplyStyleMappings`: 1 `true` (28), 0 `false` (2) | The preferences object (0x2202) has the same chunk (`00 00 01 00 00 00 00 00 00 00` in every document); the document's `LinkedStoryOption` is written from observed values (`idml-values.md`). ### Style mappings (chunk 0x21D30) Chunk 0x21D30 holds five lists. Each list: u32 count, then per entry a kind byte (2 or 3), an in-object string (source name), a kind byte (2), an in-object string (destination name) and a u32 rule. | List | IDML | |---|---| | 1 | `ParaStyleMapping` | | 2 | `CharStyleMapping` | | 3–5 | empty in every sample (the schema has `TableStyleMapping` and `CellStyleMapping`; which list is which is not shown) | | Rule | `MappingRuleType` | IDML name | Seen | |---|---|---|---| | 0 | `StyleMappingRule` | as stored | 4 | | 1 | `GroupMappingRule` | `:*` after source and destination | 60 | | 2 | `StyleToGroupMappingRule` | `:*` after the destination | 30 | The kind byte does not add `$ID/` (kind 3 is on `[Basic Paragraph]` under a localized name). Group names may contain `/` (`/`), written as stored. Attributes: `Self` is the owner's `Self`, then `ParaStyleMapping` or `CharStyleMapping` and the index in its list from 0; `SourceStyleName`, `DestinationStyleName`, `MappingRuleType`. - **Story**: a story chunk longer than 20 bytes gives the story's elements (32 of 32 stories; 107 elements equal, `Self` included). The empty form is 20 zero bytes (75 stories), and no story with it has mappings. - **Document**: chunk 0x21D30 of the preferences object (class 0x2202), in `designmap.xml` with `Self="dParaStyleMapping"`. Non-empty in 1 of 1,553 documents, the only one with `ParaStyleMapping` in `designmap.xml` (2 `StyleMappingRule` entries, equal). 1,431 documents have the empty chunk, 121 none. Rule code 3 and entries in lists 3 to 5 are not seen; the converter leaves them out with a warning. ### Import options of placed text (chunk 0x12202) Story chunk 0x12202 is 8 bytes: u32 UID of an import options object, u32 0 (in all). The class of that object decides the `Properties` child: | Class | IDML | Stories | |---|---|---| | 0x12210 | `WordRTFImportPreferences` | 39 of 39 | | 0x12213 | `ExcelImportPreferences` | 9 of 9 | | UID 0 | none | 20 | **Word (class 0x12210).** Chunk 0x12210: 19 u32 (*s0* … *s18*). IDML writes a list of 19 items for versions 8 to 12 and 20 for 14 to 20 (no version-13 sample). This mapping reproduces all 39 stories (8 with 19 items, 31 with 20; 10 distinct option sets): | Item (20-item list) | Type | Value | |---|---|---| | 0, 1 | boolean | *s0* ≠ 0, *s1* ≠ 0 | | 2 | enumeration | *s5*: 0 `PageBreak`, 2 `None` (1 sample) | | 3 | boolean | `true` (no stored field) | | 4 | enumeration | `UnformattedTable` (no stored field) | | 5, 6 | boolean | *s8* ≠ 0, *s9* ≠ 0 | | 7 | boolean | `false` (no stored field) | | 8, 9 | enumeration | *s11*, *s12*: 0 `ResolveClashUseExisting`, 2 `ResolveClashAutoRename` (1 sample each) | | 10 to 13 | boolean | *s14*, *s15*, *s16*, *s17* ≠ 0 | | 14 | boolean | `false`, only in the 20-item list (no stored field) | | 15, 16 | boolean | `true` (no stored field) | | 17 | long | *s4* (1 or 2) | | 18, 19 | boolean | `false` (no stored field) | The 19-item list is the same without item 14. In every sample *s0* = *s1*, *s14* = *s15* and *s16* = *s17*, so the order within each pair is not shown, and items 0/1, 10/11 and 12 are told apart only by assuming the same stored layout in both list lengths. Code 1 of *s5*, *s11* and *s12* is not seen; the converter leaves the property out with a warning for a code not in the table. **Excel (class 0x12213).** Chunk 0x12213: | Offset | Type | Seen | IDML item | |---|---|---|---| | 0 | u32 | 1 | | | 4 | u32 | 0 | | | 8 | u32 | 3 | | | 12 | u32 | 0 | | | 16 | u32 | 0 or 1 | 3: 0 `ExcelFormattedTable` (5), 1 `ExcelUnformattedTable` (4) | | 20 | u32 | 1 | | | 24 | flag byte, in-object string | flag 1, `Sheet1` | 7: `$ID/Sheet1` | | then | flag byte, in-object string | flag 0, the range (4 distinct values) | 8: the range | | then | flag byte, in-object string | flag 1, empty | 6: `$ID/` | | then | u32 | 1 | | The strings are written as names (`$ID/` and the string for flag 1). Every sample has the list `Spreadsheet` (enumeration), `3` (long), `false`, the table option, `true`, `true`, item 6, item 7, item 8, `1` (long). Items 0, 1, 2, 4, 5 and 9 have no identified field: the longs `3` and `1` equal the u32 at 8 and the last u32 in every sample, which does not show that they come from them. ### Style mapping preferences (chunk 0x12216) Story chunk 0x12216: u32 *p*, u32 *c*, then 2·*p* names (paragraph styles: source, destination, …) and 2·*c* names (character styles), each a flag byte (0 to 3) and an in-object string. When *p* + *c* > 0, IDML writes `Properties/StyleMappingPreferences`: a list of two lists of strings, the paragraph names and the character names (empty when *c* = 0), as stored (flag 1 does not add `$ID/`). 67 of 67 stories with the chunk (29 non-empty; *p* and *c* up to 13); no story has `StyleMappingPreferences` without the chunk. The import options come before it in `Properties`; no story has Excel options together with it. ### Element order In all 107 stories with these elements InDesign writes: `Properties` (import options, then `StyleMappingPreferences`), `StoryPreference`, `GridDataInformation`, `LinkedStoryOption`, `InCopyExportOption`, `ParaStyleMapping`, `CharStyleMapping`, `Link`, then the text. The schema allows this order (`Story.rnc`). In `designmap.xml` the document mappings follow the `idPkg:Story` references. ### Plugin data Stories of 39 version-14.0 documents also carry data of two third-party plugins (`DocsFlowImportSettings`, the `WordsFlow…` properties, the `Story` attributes `Observers` … `ComponentId`). The published IDML schema rejects it, and it follows the plugins of the exporting computer; the converter does not write it (`unknowns.md`, plugin data; `docs/measurement.md`). ## Styles (0x205) Paragraph and character styles share the class. Chunk 0x230: u32 next style (0 = itself), u32 based-on style, fields not yet identified, u16 1 for paragraph styles or 0 for character styles, u16 `Imported` (1 = true), u8 1 if the name is a built-in key (`$ID/` in IDML), the name as an in-object string, then, in newer files, a GUID string (`StyleUniqueId`). The name's offset varies (23–26 bytes), so the converter locates it as a flag byte followed by a valid in-object string. All 486 style names and 291 `NextStyle` values in the pairs match. **Imported and unique ID.** Over the 4,694 paragraph and character styles of the trustworthy pairs whose IDML style has the same name, the u16 after the kind equals `Imported` in 4,694 (180 imported). The GUID is a 36-character in-object string after the name's string; the byte after its string tag is not always 0. Every `StyleUniqueId` in the corpus IDML files is a GUID of lowercase hexadecimal digits in groups of 8, 4, 4, 4 and 12 (7,953) or `$ID/` (4,299), so the converter accepts only that form, searching from the end of the name. A search from the name's own flag byte took a style name of 36 characters for the GUID. IDML has `StyleUniqueId` on every paragraph and character style, the root styles included, when the document was saved by 11.2 or later (header or last session of the save history): the stored GUID, or `$ID/` for a style without one. Earlier documents have none. Over the trustworthy pairs the converter reproduces 6,313 of 6,316 paragraph and 3,734 of 3,735 character style values. The exceptions are pairs whose IDML was exported by another minor version than the INDD records: four 11.4 documents whose IDML has DOM 11.0 and no `StyleUniqueId` (34 values written though the IDML has none), and one 11.0 document whose IDML (DOM 11.4) has `$ID/` (3 values missing); one paragraph style has another GUID in IDML. **Kind field.** The kind and the `Imported` u16 after it can be read as one u32 in most files, because the second u16 is 0. The InDesign 7.5 template fixture `scml-template/scml.indt` (no IDML) has 1 there in 600 of its styles: 151 have `00 00 01 00` before the flag and 449 have `01 00 01 00`. The 151 are all listed in the tree of the root character style group (chunk 0x28C2 = 0xCA0D, below) and the 449 in the tree of the root paragraph style group, so the kind is the first u16 alone. Read as a u32, the 151 character styles were taken for paragraph styles, which gave `ParagraphStyle/…` references and a `NextStyle` that the schema does not allow on a character style. In every other distinct little-endian corpus file the u32 is 0 or 1 (2,125 styles), and the kind agrees with the root group in all of them. In the two big-endian files the u16 kind is also the first two bytes (`00 01` for their three paragraph styles, `00 00` for their two character styles). IDML writes a `BasedOn` of the root `[No paragraph style]` or `[No character style]` as a string (`$ID/[No paragraph style]`), and any other base as an object reference. **Attribute list (chunk 0x23F).** The style's text attributes (`attributes.md`, text attribute lists). IDML writes on a style exactly the text attributes this list stores. It adds no value from the base style, and it writes a stored value also when it equals the base's. Evidence: the 11,983 paragraph and character styles of the trustworthy pairs that match an INDD style by kind and name. In every one, each stored ID with a known mapping is an attribute of the IDML style, and every IDML text attribute comes from a stored ID, except `Label` (6 styles) and `HyphenQuality` (7 styles). Most lists are short. The root paragraph style stores the full list (287 to 362 IDs, more in later versions). Over all pairs, no other paragraph style stores more than 14 % of its root's list (the most is 41 of 289), and `NormalParagraphStyle` stores at most 21. The only other styles with a full list are 7 character styles, the same style object in 7 files of one document family (DOM 10.0 and 10.2): each stores 120 IDs, and IDML writes 126 or 128 attributes on them, almost all with the root values. Nothing else in these styles differs from the short-list styles: they have a base (the root character style), `Imported` is false, and their chunks are the same. **Keyboard shortcuts.** Ten bytes before the name's flag byte is a u32 key; the two bytes 6 and 5 before the flag are modifier bytes *m0* and *m1*. Evidence: the 6,957 paragraph and character styles of all pairs whose IDML style has the same name. | Key | `KeyboardShortcut` | `ExtendedKeyboardShortcut` | |---|---|---| | 0 (5,272 styles) | `0 0` | `0 0 0` | | 0xC000*xx*, *xx* a digit `0`–`9` | *m0* + 256 × *m1* and the code below | `0 0 0` | | 0x8000*xx* | `0 0` | *m0* + 256 × *m1*, *xx*, then 1 | The 103 styles with a key other than 0 are in 38 files. With key 0x8000*xx*, *xx* is a character code: over all pairs, the 21 styles of 11 trustworthy pairs with such a key and DOM 15 or later (codes 49 to 52, 66 and 73; modifiers 1, 17, 256 and 257) have this `ExtendedKeyboardShortcut` in IDML, for example `256 49 1` for *m0* 0, *m1* 1 and code 0x31. The key code follows the platform of the last session of the save history (the second u16 of its record), not the style: with platform 0 digits 0 to 7 give 82 to 89, 8 gives 91 and 9 gives 92 (every digit occurs); with platform 3 the code is 96 + the digit. This is a rule of the exporting edition (`preferences.md`): over the trustworthy pairs of the corpus of 2026-10 it reproduces `KeyboardShortcut` for 3,154 of 3,154 character styles and 6,927 of 6,928 paragraph styles (a rule by *m1* failed for 26 character and 33 paragraph styles with *m1* 0 and platform 3). The converter writes the attribute on every style other than the root styles. IDML has `ExtendedKeyboardShortcut` from DOM 15 on; the converter writes it from version 15. **Preview colour.** Fourteen bytes before the name's flag byte is a u32: 0, or the UID of an interface colour (class 0x1F11). IDML writes it as the style's `PreviewColor` property: `Nothing` for 0, otherwise the colour named as for layers. Over all pairs of the corpus after 2026-10 (styles other than the root styles, matched by name): 8,457 of 8,457 paragraph styles and 3,638 of 3,638 character styles. Two character styles of one document (two pairs) have a colour: UID 0x20, RGB 0.5 0.5 0.5, `Gray`, and UID 0x35, RGB 0.48 0.73 0.85, `GridBlue`. The root styles have no `PreviewColor` in IDML. The converter leaves the property to the observed values when the u32 is neither 0 nor an interface colour. **Export settings (chunk 0x28F0).** A u32 count of export tag maps, the maps, then u16 values to the end of the chunk: | Field | Contents | |---|---| | u32 | number of tag maps *n* | | *n* maps | export type, tag, class (three strings), u32 attribute count *k*, *k* pairs of strings | | u16 | `SplitDocument` (1 `true`, 0 `false`) | | u16 | `EmitCss` | | u16 | `IncludeClass` (DOM 13 on) | | u16 … | more values in later versions, not identified | A string is a u32 length in UTF-16 units followed by text segments (as the font version, `fonts.md`); an empty string is a length of 0. Export types are `EPUB` and `PDF`; tags such as `p` or `H3` and classes may be empty. No map in the corpus has attributes, so the layout of the pairs is assumed and the converter writes no map that has them. The chunks of every paragraph, character and object style of all pairs parse. IDML has `SplitDocument` and `EmitCss` when the document was saved by 10.1 or later, and `IncludeClass` from 13.0 (header or last session of the save history), on every paragraph and character style, the root styles included. Without the chunk the values are `false`, `true`, `true`. The trustworthy pairs include four whose INDD header says 10.0 and whose IDML (DOM 10.2) has the attributes, and six of 10.0 whose IDML (DOM 10.0) has none. Over the trustworthy pairs the converter reproduces `SplitDocument` and `EmitCss` of 7,287 of 7,287 paragraph styles and 4,067 of 4,067 character styles, and `IncludeClass` of 5,760 and 3,500, with no extra value. **Tag maps.** IDML writes one `StyleExportTagMap` per map after the style's `Properties`, in stored order: `Self="u