@segnavia/idml 0.28.0 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,45 @@
1
+ import { localName, numberAttribute } from "./xml.js";
2
+ import { isTag } from "domhandler";
3
+ //#region src/frame-preferences.ts
4
+ function preferenceElement(frame) {
5
+ const pending = [...frame.children];
6
+ while (pending.length > 0) {
7
+ const node = pending.pop();
8
+ if (!node || !isTag(node)) continue;
9
+ if (localName(node.name) === "TextFramePreference") return node;
10
+ pending.push(...node.children);
11
+ }
12
+ }
13
+ function framePreference(frame, location) {
14
+ const element = preferenceElement(frame);
15
+ if (!element) return void 0;
16
+ return {
17
+ columns: numberAttribute(element, "TextColumnCount", 1),
18
+ gutter: numberAttribute(element, "TextColumnGutter", 0),
19
+ location
20
+ };
21
+ }
22
+ var FramePreferenceIndex = class {
23
+ byStory = /* @__PURE__ */ new Map();
24
+ add(story, preference) {
25
+ if (!preference || preference.columns < 2) return;
26
+ const values = this.byStory.get(story) ?? [];
27
+ values.push(preference);
28
+ this.byStory.set(story, values);
29
+ }
30
+ preferred(story) {
31
+ const values = this.byStory.get(story) ?? [];
32
+ const counts = /* @__PURE__ */ new Map();
33
+ for (const value of values) {
34
+ const key = `${value.columns}\u0000${value.gutter}`;
35
+ counts.set(key, (counts.get(key) ?? 0) + 1);
36
+ }
37
+ const selected = [...counts].sort((left, right) => right[1] - left[1])[0]?.[0];
38
+ return values.find((value) => `${value.columns}\u0000${value.gutter}` === selected);
39
+ }
40
+ values() {
41
+ return [...this.byStory.values()].flat();
42
+ }
43
+ };
44
+ //#endregion
45
+ export { FramePreferenceIndex, framePreference };
package/dist/import.d.ts CHANGED
@@ -1,9 +1,9 @@
1
+ import { IdmlReportData } from "./report.js";
1
2
  import { Book } from "@segnavia/format";
2
3
  //#region src/import.d.ts
3
4
  export type IdmlImportOptions = Record<never, never>;
4
- export interface IdmlImportReport {
5
+ export interface IdmlImportReport extends IdmlReportData {
5
6
  partsRead: string[];
6
- unsupportedElements: Record<string, number>;
7
7
  }
8
8
  export interface IdmlImportResult {
9
9
  book: Book;
package/dist/import.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { refusal } from "./refusal.js";
2
+ import { ReportCollector } from "./report.js";
2
3
  import { modelToBook } from "./map-book.js";
3
4
  import { idmlModel } from "./model.js";
4
5
  import { readZip } from "./zip.js";
@@ -13,11 +14,12 @@ async function importIdml(bytes, _options = {}) {
13
14
  if (value !== IDML_MIMETYPE) throw refusal(`the IDML mimetype entry is ${JSON.stringify(value)}, not ${IDML_MIMETYPE}`, "use the IDML package mimetype");
14
15
  if (!byName.has("designmap.xml")) throw refusal("the IDML designmap.xml entry is missing", "add the required designmap.xml entry");
15
16
  const model = idmlModel(entries);
17
+ const collector = new ReportCollector(model.report);
16
18
  return {
17
- book: modelToBook(model),
19
+ book: modelToBook(model, collector),
18
20
  report: {
19
21
  partsRead: model.partsRead,
20
- unsupportedElements: model.unsupportedElements
22
+ ...collector.data()
21
23
  }
22
24
  };
23
25
  }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
+ import { IdmlFontUse, IdmlReportData } from "./report.js";
1
2
  import { IdmlImportOptions, IdmlImportReport, IdmlImportResult, importIdml } from "./import.js";
2
3
  import { IDML_LIMITS, MAX_IDML_ENTRIES, MAX_IDML_PACKAGE_BYTES, MAX_IDML_PART_BYTES, MAX_IDML_TOTAL_INFLATED_BYTES, MAX_IDML_XML_DEPTH } from "./limits.js";
3
- import { IdmlDocumentPreferences, IdmlMargin, IdmlMasterSpread, IdmlModel, IdmlPage, IdmlSpread, IdmlStory } from "./model.js";
4
- export { IDML_LIMITS, type IdmlDocumentPreferences, type IdmlImportOptions, type IdmlImportReport, type IdmlImportResult, type IdmlMargin, type IdmlMasterSpread, type IdmlModel, type IdmlPage, type IdmlSpread, type IdmlStory, MAX_IDML_ENTRIES, MAX_IDML_PACKAGE_BYTES, MAX_IDML_PART_BYTES, MAX_IDML_TOTAL_INFLATED_BYTES, MAX_IDML_XML_DEPTH, importIdml };
4
+ import { IdmlDocumentPreferences, IdmlMargin, IdmlMasterSpread, IdmlPage, IdmlSpread, IdmlStory } from "./types.js";
5
+ import { IdmlModel } from "./model.js";
6
+ export { IDML_LIMITS, type IdmlDocumentPreferences, type IdmlFontUse, type IdmlImportOptions, type IdmlImportReport, type IdmlImportResult, type IdmlMargin, type IdmlMasterSpread, type IdmlModel, type IdmlPage, type IdmlReportData, type IdmlSpread, type IdmlStory, MAX_IDML_ENTRIES, MAX_IDML_PACKAGE_BYTES, MAX_IDML_PART_BYTES, MAX_IDML_TOTAL_INFLATED_BYTES, MAX_IDML_XML_DEPTH, importIdml };
package/dist/map-book.js CHANGED
@@ -1,27 +1,65 @@
1
1
  import { refusal } from "./refusal.js";
2
+ import { logicalMargin } from "./margins.js";
3
+ import { storyHtml } from "./stories.js";
4
+ import { chapterParents, mapMasters } from "./masters.js";
5
+ import { ReportCollector } from "./report.js";
6
+ import { StyleMapper } from "./styles.js";
7
+ import { matchVariables } from "./variables.js";
2
8
  import { Book, MM_PER_PT } from "@segnavia/format";
3
9
  import { findActiveContent } from "@segnavia/html";
4
- import serialize from "dom-serializer";
5
- import { Element } from "domhandler";
6
10
  //#region src/map-book.ts
7
- const emptyParagraph = () => serialize(new Element("p", {}, []), { xmlMode: false });
8
11
  function orderedStories(model) {
9
12
  const order = /* @__PURE__ */ new Map();
10
13
  for (const spread of model.spreads) for (const page of spread.pages) for (const story of page.stories) if (!order.has(story)) order.set(story, order.size);
11
14
  const masterStories = new Set(model.masterStoryIds);
12
15
  return model.stories.filter((story) => !masterStories.has(story.id)).sort((left, right) => (order.get(left.id) ?? Number.MAX_SAFE_INTEGER) - (order.get(right.id) ?? Number.MAX_SAFE_INTEGER));
13
16
  }
14
- function modelToBook(model) {
17
+ function bodyColumnGap(model, stories, report) {
18
+ const storyFrames = stories.flatMap((story) => story.frame?.columns && story.frame.columns >= 2 ? [story.frame] : []);
19
+ const frames = (model.bodyFramePreferences ?? storyFrames).filter((frame) => frame.columns >= 2);
20
+ const representativeFrames = storyFrames.length > 0 ? storyFrames : frames;
21
+ const counts = /* @__PURE__ */ new Map();
22
+ for (const frame of representativeFrames) counts.set(frame.gutter, (counts.get(frame.gutter) ?? 0) + 1);
23
+ const selected = [...counts].sort((left, right) => right[1] - left[1])[0]?.[0];
24
+ if (selected === void 0) return void 0;
25
+ for (const gutter of new Set(frames.map((frame) => frame.gutter).filter((gutter) => gutter !== selected))) {
26
+ const frame = frames.find((candidate) => candidate.gutter === gutter);
27
+ report.add(report.unsupportedElements, "differing body frame gutters", frame?.location ?? model.id);
28
+ }
29
+ return selected;
30
+ }
31
+ function refuseActiveBookHtml(chapters, parents) {
32
+ const html = [...chapters.map((chapter) => chapter.html), ...parents.flatMap((parent) => [parent.head ?? "", parent.foot ?? ""])].join("");
33
+ const active = findActiveContent(html);
34
+ if (active.length > 0) throw refusal(`the imported Book contains active content (${active.join(", ")})`, "remove that active content");
35
+ }
36
+ function modelToBook(model, report = new ReportCollector(model.report)) {
15
37
  const preferences = model.preferences;
16
38
  const rightBinding = preferences.binding === "right-to-left";
17
- const chapters = orderedStories(model).map((story) => ({
18
- title: story.id,
19
- html: emptyParagraph()
20
- }));
39
+ const bookDirection = rightBinding ? "rtl" : "ltr";
40
+ const styles = new StyleMapper(model.styles ?? [], model.swatches ?? [], report, bookDirection);
41
+ const matchedVariables = matchVariables(model.textVariables ?? [], report);
42
+ const bookLanguage = styles.rootLanguage();
43
+ const stories = orderedStories(model);
44
+ const masters = mapMasters(model, matchedVariables, styles, report);
45
+ const chapters = stories.map((story) => ({
46
+ story,
47
+ mapped: storyHtml({
48
+ ...story,
49
+ textVariables: matchedVariables
50
+ }, styles, report, bookDirection, bookLanguage)
51
+ })).map(({ story, mapped: value }) => {
52
+ const parents = chapterParents(story.id, model, masters, report);
53
+ return {
54
+ title: story.id,
55
+ html: value.html,
56
+ ...parents ? { parents } : {}
57
+ };
58
+ });
21
59
  if (chapters.length === 0) throw refusal("the IDML has no stories", "add at least one Story");
22
- const html = chapters.map((chapter) => chapter.html).join("");
23
- const active = findActiveContent(html);
24
- if (active.length > 0) throw refusal(`the imported Book contains active content (${active.join(", ")})`, "remove that active content");
60
+ refuseActiveBookHtml(chapters, masters.parents);
61
+ const columnGap = bodyColumnGap(model, stories, report);
62
+ const css = `${styles.css()}${masters.css}`;
25
63
  return Book.parse({
26
64
  title: model.id,
27
65
  mode: "paginated",
@@ -31,17 +69,16 @@ function modelToBook(model) {
31
69
  height: preferences.pageHeight,
32
70
  unit: "pt"
33
71
  },
34
- margin: {
35
- top: preferences.margin.top * MM_PER_PT,
36
- bottom: preferences.margin.bottom * MM_PER_PT,
37
- inner: (rightBinding ? preferences.margin.right : preferences.margin.left) * MM_PER_PT,
38
- outer: (rightBinding ? preferences.margin.left : preferences.margin.right) * MM_PER_PT
39
- }
72
+ margin: masters.bookMargin ?? logicalMargin(preferences.margin, rightBinding ? "left" : "right"),
73
+ ...columnGap === void 0 ? {} : { columnGap: columnGap * MM_PER_PT }
40
74
  },
41
- dir: rightBinding ? "rtl" : "ltr",
75
+ dir: bookDirection,
76
+ ...bookLanguage ? { lang: bookLanguage } : {},
77
+ ...css ? { css } : {},
42
78
  rectoOpenings: preferences.facingPages,
79
+ parentPages: masters.parents,
43
80
  chapters
44
81
  });
45
82
  }
46
83
  //#endregion
47
- export { modelToBook };
84
+ export { modelToBook, refuseActiveBookHtml };
@@ -0,0 +1,21 @@
1
+ import { numberAttribute } from "./xml.js";
2
+ import { MM_PER_PT } from "@segnavia/format";
3
+ //#region src/margins.ts
4
+ function readMargin(element) {
5
+ return {
6
+ top: numberAttribute(element, "Top", 0),
7
+ bottom: numberAttribute(element, "Bottom", 0),
8
+ left: numberAttribute(element, "Left", 0),
9
+ right: numberAttribute(element, "Right", 0)
10
+ };
11
+ }
12
+ function logicalMargin(margin, side) {
13
+ return {
14
+ top: margin.top * MM_PER_PT,
15
+ bottom: margin.bottom * MM_PER_PT,
16
+ inner: (side === "left" ? margin.right : margin.left) * MM_PER_PT,
17
+ outer: (side === "left" ? margin.left : margin.right) * MM_PER_PT
18
+ };
19
+ }
20
+ //#endregion
21
+ export { logicalMargin, readMargin };
@@ -0,0 +1,200 @@
1
+ import { logicalMargin } from "./margins.js";
2
+ import { storyHtml } from "./stories.js";
3
+ import { BOOK_PARENT_NAMES, MM_PER_PT } from "@segnavia/format";
4
+ import serialize from "dom-serializer";
5
+ import { Element } from "domhandler";
6
+ import { parseDocument } from "htmlparser2";
7
+ const slug = (value) => value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "master";
8
+ const mm = (points) => `${Number((points * MM_PER_PT).toFixed(6))}mm`;
9
+ function roleFor(page, rtl, ordinal) {
10
+ if (!page.side || page.side === "single") return (ordinal ?? 0) % 2 === 0 ? "recto" : "verso";
11
+ return page.side === (rtl ? "left" : "right") ? "recto" : "verso";
12
+ }
13
+ function masterPage(master, role, rtl) {
14
+ if (master.pages.length === 1) return master.pages[0];
15
+ const side = role === "recto" ? rtl ? "left" : "right" : rtl ? "right" : "left";
16
+ return master.pages.find((page) => page.side === side);
17
+ }
18
+ function parentName(master, role) {
19
+ return `idml-${slug(master.name)}-${master.pages.length === 1 ? "recto" : role}`;
20
+ }
21
+ function frameRegion(page, frame, height) {
22
+ if (frame.bounds.top + frame.bounds.height <= page.margin.top) return "head";
23
+ if (frame.bounds.top >= height - page.margin.bottom) return "foot";
24
+ }
25
+ function regionHtml(master, page, region, context) {
26
+ const boxes = [];
27
+ for (const frame of page.frames) {
28
+ if (frameRegion(page, frame, context.model.preferences.pageHeight) !== region) continue;
29
+ const story = context.stories.get(frame.story);
30
+ if (!story) {
31
+ context.report.add(context.report.unsupportedElements, "master story missing", `${master.name}#${frame.id}`);
32
+ continue;
33
+ }
34
+ const className = `idml-master-frame-${context.css.length}`;
35
+ const mapped = storyHtml({
36
+ ...story,
37
+ textVariables: context.variables,
38
+ master: true
39
+ }, context.styles, context.report, context.rtl ? "rtl" : "ltr", void 0);
40
+ const nodes = parseDocument(mapped.html, { xmlMode: false }).children;
41
+ boxes.push(new Element("div", { class: className }, nodes));
42
+ const right = frame.bounds.left + frame.bounds.width;
43
+ const inlineStart = context.rtl ? context.model.preferences.pageWidth - page.margin.right - right : frame.bounds.left - page.margin.left;
44
+ const blockStart = region === "head" ? frame.bounds.top : frame.bounds.top - (context.model.preferences.pageHeight - page.margin.bottom);
45
+ context.css.push(`:where(.${className}){position:absolute;inset-inline-start:${mm(inlineStart)};inline-size:${mm(frame.bounds.width)};inset-block-start:${mm(blockStart)};block-size:${mm(frame.bounds.height)}}`);
46
+ context.mappedStories.add(frame.story);
47
+ }
48
+ if (boxes.length === 0) return void 0;
49
+ return serialize(new Element("div", { class: "idml-master-region" }, boxes), {
50
+ xmlMode: false,
51
+ encodeEntities: "utf8"
52
+ });
53
+ }
54
+ function pageSide(page, role, rtl) {
55
+ if (page.side === "left" || page.side === "right") return page.side;
56
+ return role === "recto" ? rtl ? "left" : "right" : rtl ? "right" : "left";
57
+ }
58
+ function buildParent(master, role, byId, context) {
59
+ const page = masterPage(master, role, context.rtl);
60
+ if (!page) return void 0;
61
+ const base = master.basedOn ? byId.get(master.basedOn) : void 0;
62
+ const head = regionHtml(master, page, "head", context);
63
+ const foot = regionHtml(master, page, "foot", context);
64
+ for (const frame of page.frames) if (!frameRegion(page, frame, context.model.preferences.pageHeight)) context.report.add(context.report.unsupportedElements, "master TextFrame", `${master.name}#${frame.id}`);
65
+ return {
66
+ name: parentName(master, role),
67
+ ...base ? { basedOn: parentName(base, role) } : {},
68
+ margin: logicalMargin(page.margin, pageSide(page, role, context.rtl)),
69
+ ...head ? { head } : {},
70
+ ...foot ? { foot } : {}
71
+ };
72
+ }
73
+ function reportExtraPages(master, selected, report) {
74
+ for (const page of master.pages) {
75
+ if (selected.has(page.id)) continue;
76
+ report.add(report.unsupportedElements, "master page", `${master.name}#${page.id}`);
77
+ for (const frame of page.frames) report.add(report.unsupportedElements, "master TextFrame", `${master.name}#${frame.id}`);
78
+ }
79
+ }
80
+ function buildParents(masters, byId, context) {
81
+ const result = [];
82
+ for (const master of masters) {
83
+ const roles = master.pages.length === 1 ? ["recto"] : ["recto", "verso"];
84
+ const selected = /* @__PURE__ */ new Set();
85
+ for (const role of roles) {
86
+ const page = masterPage(master, role, context.rtl);
87
+ if (page) selected.add(page.id);
88
+ const parent = buildParent(master, role, byId, context);
89
+ if (parent) result.push(parent);
90
+ }
91
+ reportExtraPages(master, selected, context.report);
92
+ for (const item of master.unmappedItems) context.report.add(context.report.unsupportedElements, `master ${item.type}`, `${master.name}#${item.id}`);
93
+ }
94
+ return result;
95
+ }
96
+ function emittedMasters(model, applied) {
97
+ const byId = new Map(model.masterSpreads.map((master) => [master.id, master]));
98
+ const emitted = new Set(applied);
99
+ for (const id of emitted) {
100
+ const basedOn = byId.get(id)?.basedOn;
101
+ if (basedOn) emitted.add(basedOn);
102
+ }
103
+ return model.masterSpreads.filter((master) => emitted.has(master.id));
104
+ }
105
+ function appliedBookMargin(model, applied, report) {
106
+ const masters = model.masterSpreads.filter((master) => applied.has(master.id));
107
+ if (masters.length !== applied.size) return void 0;
108
+ const pages = masters.flatMap((master) => master.pages.map((page) => ({
109
+ master,
110
+ page
111
+ })));
112
+ const first = pages[0]?.page.margin;
113
+ if (!first) return void 0;
114
+ const signature = (margin) => `${margin.top},${margin.bottom},${margin.left},${margin.right}`;
115
+ if (pages.some(({ page }) => signature(page.margin) !== signature(first))) {
116
+ for (const master of masters) {
117
+ const margins = [...new Set(master.pages.map((page) => signature(page.margin)))];
118
+ report.add(report.unsupportedElements, "master margins disagree", `${master.name}:${margins.join("|")}`);
119
+ }
120
+ return;
121
+ }
122
+ return logicalMargin(first, model.preferences.binding === "right-to-left" ? "left" : "right");
123
+ }
124
+ function mapMasters(model, variables, styles, report) {
125
+ const rtl = model.preferences.binding === "right-to-left";
126
+ const applied = new Set(model.spreads.flatMap((spread) => spread.pages.map((page) => page.appliedMaster).filter((id) => Boolean(id && id !== "n"))));
127
+ const masters = emittedMasters(model, applied);
128
+ const byId = new Map(model.masterSpreads.map((master) => [master.id, master]));
129
+ for (const spread of model.spreads) for (const page of spread.pages) {
130
+ const id = page.appliedMaster;
131
+ if (id && id !== "n" && !byId.has(id)) report.addOnce(report.unsupportedElements, `applied master unknown:${id}`, page.id);
132
+ }
133
+ const css = [":where(.idml-master-region){position:relative;block-size:100%}"];
134
+ const mappedStories = /* @__PURE__ */ new Set();
135
+ const parents = buildParents(masters, byId, {
136
+ model,
137
+ variables,
138
+ styles,
139
+ report,
140
+ rtl,
141
+ stories: new Map(model.stories.map((story) => [story.id, story])),
142
+ mappedStories,
143
+ css
144
+ });
145
+ if (model.spreads.some((spread) => spread.pages.some((page) => !page.appliedMaster || page.appliedMaster === "n"))) parents.push({
146
+ name: "idml-none",
147
+ basedOn: BOOK_PARENT_NAMES.blank
148
+ });
149
+ for (const story of model.masterStoryIds) if (!mappedStories.has(story)) report.add(report.unsupportedElements, "master story", story);
150
+ const parentFor = (masterId, role, location) => {
151
+ if (!masterId || masterId === "n") return "idml-none";
152
+ const master = byId.get(masterId);
153
+ if (master && applied.has(masterId)) return parentName(master, role);
154
+ report.addOnce(report.unsupportedElements, `applied master unknown:${masterId}`, location);
155
+ return role === "recto" ? BOOK_PARENT_NAMES.recto : BOOK_PARENT_NAMES.verso;
156
+ };
157
+ return {
158
+ parents,
159
+ css: css.join(""),
160
+ bookMargin: appliedBookMargin(model, applied, report),
161
+ parentFor,
162
+ mappedStories
163
+ };
164
+ }
165
+ function chapterParents(story, model, mapping, report) {
166
+ const rtl = model.preferences.binding === "right-to-left";
167
+ const pages = [];
168
+ let ordinal = 0;
169
+ for (const spread of model.spreads) for (const page of spread.pages) {
170
+ if (page.stories.includes(story)) pages.push({
171
+ page,
172
+ ordinal
173
+ });
174
+ ordinal += 1;
175
+ }
176
+ const first = pages[0];
177
+ if (!first) return void 0;
178
+ const firstRole = roleFor(first.page, rtl, first.ordinal);
179
+ const result = { opener: mapping.parentFor(first.page.appliedMaster, firstRole, first.page.id) };
180
+ for (const role of ["recto", "verso"]) {
181
+ const candidates = pages.slice(1).filter((entry) => roleFor(entry.page, rtl, entry.ordinal) === role);
182
+ if (candidates.length === 0) continue;
183
+ const counts = /* @__PURE__ */ new Map();
184
+ for (const entry of candidates) {
185
+ const id = entry.page.appliedMaster ?? "n";
186
+ counts.set(id, (counts.get(id) ?? 0) + 1);
187
+ }
188
+ const chosen = [...counts].sort((a, b) => b[1] - a[1])[0]?.[0];
189
+ if (chosen) result[role] = mapping.parentFor(chosen, role, candidates[0]?.page.id ?? story);
190
+ if (chosen && counts.size > 1) report.mixedMasterChapters.push({
191
+ chapter: story,
192
+ side: role,
193
+ masters: [...counts.keys()],
194
+ chosen
195
+ });
196
+ }
197
+ return result;
198
+ }
199
+ //#endregion
200
+ export { chapterParents, mapMasters };
package/dist/model.d.ts CHANGED
@@ -1,34 +1,6 @@
1
+ import { IdmlReportData } from "./report.js";
2
+ import { IdmlDocumentPreferences, IdmlFramePreference, IdmlMargin, IdmlMasterPage, IdmlMasterSpread, IdmlPage, IdmlSpread, IdmlStory, IdmlStyle, IdmlSwatch, IdmlTextVariable } from "./types.js";
1
3
  //#region src/model.d.ts
2
- export interface IdmlMargin {
3
- top: number;
4
- bottom: number;
5
- left: number;
6
- right: number;
7
- }
8
- export interface IdmlDocumentPreferences {
9
- pageWidth: number;
10
- pageHeight: number;
11
- facingPages: boolean;
12
- binding: 'left-to-right' | 'right-to-left';
13
- margin: IdmlMargin;
14
- }
15
- export interface IdmlPage {
16
- id: string;
17
- appliedMaster?: string;
18
- stories: string[];
19
- }
20
- export interface IdmlSpread {
21
- id: string;
22
- pages: IdmlPage[];
23
- }
24
- export interface IdmlMasterSpread {
25
- id: string;
26
- pages: IdmlPage[];
27
- }
28
- export interface IdmlStory {
29
- id: string;
30
- part: string;
31
- }
32
4
  export interface IdmlModel {
33
5
  id: string;
34
6
  preferences: IdmlDocumentPreferences;
@@ -37,6 +9,11 @@ export interface IdmlModel {
37
9
  stories: IdmlStory[];
38
10
  masterStoryIds: string[];
39
11
  partsRead: string[];
40
- unsupportedElements: Record<string, number>;
12
+ styles?: IdmlStyle[];
13
+ swatches?: IdmlSwatch[];
14
+ bodyFramePreferences?: IdmlFramePreference[];
15
+ textVariables?: IdmlTextVariable[];
16
+ report?: IdmlReportData;
41
17
  }
42
- //#endregion
18
+ //#endregion
19
+ export type { IdmlDocumentPreferences, IdmlMargin, IdmlMasterSpread, IdmlPage, IdmlSpread, IdmlStory };