@segnavia/idml 0.33.0 → 0.34.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,123 @@
1
+ import { formatRoundedNumber, logicalCornerRadii } from "./positioned-box.js";
2
+ import { resolveSwatchColor } from "./swatch-color.js";
3
+ //#region src/paragraph-box.ts
4
+ function enabled(value) {
5
+ return value?.trim().toLowerCase() === "true";
6
+ }
7
+ function number(values, mapped, name) {
8
+ if (!mapped.has(name)) return void 0;
9
+ const value = Number(values[name]);
10
+ return Number.isFinite(value) ? value : void 0;
11
+ }
12
+ function points(output, property, value) {
13
+ output.push(`${property}:${formatRoundedNumber(value)}pt`);
14
+ }
15
+ function optionalPoints(output, property, value, scale = 1) {
16
+ if (value !== void 0) points(output, property, value * scale);
17
+ }
18
+ function combinedPoints(output, property, base, adjustment, scale = 1) {
19
+ if (base === void 0 && adjustment === void 0) return;
20
+ points(output, property, (base ?? 0) + (adjustment ?? 0) * scale);
21
+ }
22
+ function padding(options, output, property, base, offset, offsetName) {
23
+ if (base === void 0 && offset === void 0) return;
24
+ const value = (base ?? 0) + (offset ?? 0);
25
+ if (value < 0) options.report.add(options.unsupported, offsetName, options.location);
26
+ points(output, property, Math.max(0, value));
27
+ }
28
+ function boxDeclarations(options, shaded, reset, output) {
29
+ const { values, mapped, direction } = options;
30
+ const top = shaded ? number(values, mapped, "ParagraphShadingTopOffset") : void 0;
31
+ const bottom = shaded ? number(values, mapped, "ParagraphShadingBottomOffset") : void 0;
32
+ const left = shaded ? number(values, mapped, "ParagraphShadingLeftOffset") : void 0;
33
+ const right = shaded ? number(values, mapped, "ParagraphShadingRightOffset") : void 0;
34
+ const before = number(values, mapped, "SpaceBefore");
35
+ const after = number(values, mapped, "SpaceAfter");
36
+ const startIndent = number(values, mapped, direction === "rtl" ? "RightIndent" : "LeftIndent");
37
+ const endIndent = number(values, mapped, direction === "rtl" ? "LeftIndent" : "RightIndent");
38
+ const startOffset = direction === "rtl" ? right : left;
39
+ const endOffset = direction === "rtl" ? left : right;
40
+ const startOffsetName = direction === "rtl" ? "ParagraphShadingRightOffset" : "ParagraphShadingLeftOffset";
41
+ const endOffsetName = direction === "rtl" ? "ParagraphShadingLeftOffset" : "ParagraphShadingRightOffset";
42
+ if (reset) {
43
+ points(output, "margin-block-start", before ?? 0);
44
+ points(output, "margin-block-end", after ?? 0);
45
+ points(output, "padding-block-start", 0);
46
+ points(output, "padding-block-end", 0);
47
+ points(output, "padding-inline-start", Math.max(0, startIndent ?? 0));
48
+ points(output, "padding-inline-end", Math.max(0, endIndent ?? 0));
49
+ points(output, "margin-inline-start", 0);
50
+ points(output, "margin-inline-end", 0);
51
+ return;
52
+ }
53
+ combinedPoints(output, "margin-block-start", before, top, -1);
54
+ combinedPoints(output, "margin-block-end", after, bottom, -1);
55
+ padding(options, output, "padding-block-start", void 0, top, "ParagraphShadingTopOffset");
56
+ padding(options, output, "padding-block-end", void 0, bottom, "ParagraphShadingBottomOffset");
57
+ padding(options, output, "padding-inline-start", startIndent, startOffset, startOffsetName);
58
+ padding(options, output, "padding-inline-end", endIndent, endOffset, endOffsetName);
59
+ optionalPoints(output, "margin-inline-start", startOffset, -1);
60
+ optionalPoints(output, "margin-inline-end", endOffset, -1);
61
+ }
62
+ const PHYSICAL_CORNERS = [
63
+ "TopLeft",
64
+ "TopRight",
65
+ "BottomLeft",
66
+ "BottomRight"
67
+ ];
68
+ function cornerDeclarations(options, output) {
69
+ const { values, mapped, direction, report, unsupported, location } = options;
70
+ const radii = {
71
+ topLeft: void 0,
72
+ topRight: void 0,
73
+ bottomLeft: void 0,
74
+ bottomRight: void 0
75
+ };
76
+ for (const physical of PHYSICAL_CORNERS) {
77
+ const optionName = `ParagraphShading${physical}CornerOption`;
78
+ const option = values[optionName];
79
+ if (!option || option === "None" || option.endsWith("/None")) continue;
80
+ if (option !== "RoundedCorner" && !option.endsWith("/RoundedCorner")) {
81
+ report.add(unsupported, optionName, location);
82
+ continue;
83
+ }
84
+ const radius = number(values, mapped, `ParagraphShading${physical}CornerRadius`);
85
+ if (radius !== void 0) radii[`${physical[0]?.toLowerCase()}${physical.slice(1)}`] = radius;
86
+ }
87
+ for (const [property, radius] of Object.entries(logicalCornerRadii(radii, direction))) if (radius !== void 0) points(output, property, radius);
88
+ }
89
+ function reportUnmappedOptions(options) {
90
+ const { values, report, unsupported, location } = options;
91
+ if (values.ParagraphShadingWidth && !["ColumnWidth", "Column"].includes(values.ParagraphShadingWidth)) report.add(unsupported, "ParagraphShadingWidth", location);
92
+ if (values.ParagraphShadingTopOrigin && !["Ascent", "AscentTopOrigin"].includes(values.ParagraphShadingTopOrigin)) report.add(unsupported, "ParagraphShadingTopOrigin", location);
93
+ if (values.ParagraphShadingBottomOrigin && !["Descent", "DescentBottomOrigin"].includes(values.ParagraphShadingBottomOrigin)) report.add(unsupported, "ParagraphShadingBottomOrigin", location);
94
+ if (enabled(values.ParagraphShadingClipToFrame)) report.add(unsupported, "ParagraphShadingClipToFrame", location);
95
+ if (enabled(values.ParagraphShadingSuppressPrinting)) report.add(unsupported, "ParagraphShadingSuppressPrinting", location);
96
+ }
97
+ function paragraphBoxDeclarations(options) {
98
+ const { values, swatches, report, unsupported, location } = options;
99
+ const output = [];
100
+ const shaded = enabled(values.ParagraphShadingOn);
101
+ boxDeclarations(options, shaded, options.resetShading, output);
102
+ if (options.resetShading) {
103
+ output.push("background-color:transparent");
104
+ for (const property of Object.keys(logicalCornerRadii({
105
+ topLeft: 0,
106
+ topRight: 0,
107
+ bottomLeft: 0,
108
+ bottomRight: 0
109
+ }, options.direction))) points(output, property, 0);
110
+ }
111
+ if (!shaded) return output;
112
+ if (!values.ParagraphShadingColor) report.add(unsupported, "ParagraphShadingColor", location);
113
+ const color = values.ParagraphShadingColor ? resolveSwatchColor(values.ParagraphShadingColor, swatches, report, location, "text", "ParagraphShadingColor") : void 0;
114
+ if (color) {
115
+ const tint = Number(values.ParagraphShadingTint);
116
+ output.push(`background-color:${Number.isFinite(tint) && tint >= 0 && tint < 100 ? `color-mix(in srgb, ${color} ${tint}%, #fff)` : color}`);
117
+ }
118
+ cornerDeclarations(options, output);
119
+ reportUnmappedOptions(options);
120
+ return output;
121
+ }
122
+ //#endregion
123
+ export { paragraphBoxDeclarations };
@@ -11,10 +11,13 @@ const IDENTITY = [
11
11
  0,
12
12
  0
13
13
  ];
14
- function transform(element) {
15
- const values = (attribute(element, "ItemTransform") ?? "").trim().split(/\s+/).map(Number);
14
+ function itemTransform(value) {
15
+ const values = (value ?? "").trim().split(/\s+/).map(Number);
16
16
  return values.length === 6 && values.every(Number.isFinite) ? values : IDENTITY;
17
17
  }
18
+ function transform(element) {
19
+ return itemTransform(attribute(element, "ItemTransform"));
20
+ }
18
21
  function compose(parent, child) {
19
22
  const [a, b, c, d, x, y] = parent;
20
23
  const [e, f, g, h, u, v] = child;
@@ -27,19 +30,23 @@ function compose(parent, child) {
27
30
  b * u + d * v + y
28
31
  ];
29
32
  }
30
- function apply(value, point) {
33
+ function applyTransform(value, point) {
31
34
  return [value[0] * point[0] + value[2] * point[1] + value[4], value[1] * point[0] + value[3] * point[1] + value[5]];
32
35
  }
36
+ function pointValue(value, fallback) {
37
+ const values = (value ?? "").trim().split(/\s+/).map(Number);
38
+ return values.length === 2 && values.every(Number.isFinite) ? values : fallback;
39
+ }
33
40
  function pageArea(element) {
34
41
  const value = (attribute(element, "GeometricBounds") ?? "").trim().split(/\s+/).map(Number);
35
42
  if (value.length !== 4 || !value.every(Number.isFinite)) return void 0;
36
43
  const [top, left, bottom, right] = value;
37
44
  const matrix = transform(element);
38
45
  return [
39
- apply(matrix, [left, top]),
40
- apply(matrix, [right, top]),
41
- apply(matrix, [right, bottom]),
42
- apply(matrix, [left, bottom])
46
+ applyTransform(matrix, [left, top]),
47
+ applyTransform(matrix, [right, top]),
48
+ applyTransform(matrix, [right, bottom]),
49
+ applyTransform(matrix, [left, bottom])
43
50
  ];
44
51
  }
45
52
  function placementBounds(points) {
@@ -54,7 +61,7 @@ function placementBounds(points) {
54
61
  };
55
62
  }
56
63
  function frameBounds(element, matrix) {
57
- return placementBounds(elements(element).map((candidate) => attribute(candidate, "Anchor")).filter((value) => value !== void 0).map((value) => value.trim().split(/\s+/).map(Number)).filter((value) => value.length === 2 && value.every(Number.isFinite)).map((point) => apply(matrix, point)));
64
+ return placementBounds(elements(element).map((candidate) => attribute(candidate, "Anchor")).filter((value) => value !== void 0).map((value) => value.trim().split(/\s+/).map(Number)).filter((value) => value.length === 2 && value.every(Number.isFinite)).map((point) => applyTransform(matrix, point)));
58
65
  }
59
66
  function contains(area, [x, y]) {
60
67
  let inside = false;
@@ -86,17 +93,16 @@ function framePlacement(element, matrix, location) {
86
93
  };
87
94
  }
88
95
  function pointAttribute(element, name, fallback) {
89
- const values = (attribute(element, name) ?? "").trim().split(/\s+/).map(Number);
90
- return values.length === 2 && values.every(Number.isFinite) ? values : fallback;
96
+ return pointValue(attribute(element, name), fallback);
91
97
  }
92
98
  function polygonPlacement(element, matrix, location, group) {
93
99
  const paths = elements(element, "GeometryPathType").flatMap((path) => {
94
100
  const points = elements(path, "PathPointType").flatMap((candidate) => {
95
101
  const anchor = pointAttribute(candidate, "Anchor");
96
102
  return anchor ? [{
97
- anchor: apply(matrix, anchor),
98
- left: apply(matrix, pointAttribute(candidate, "LeftDirection", anchor)),
99
- right: apply(matrix, pointAttribute(candidate, "RightDirection", anchor))
103
+ anchor: applyTransform(matrix, anchor),
104
+ left: applyTransform(matrix, pointAttribute(candidate, "LeftDirection", anchor)),
105
+ right: applyTransform(matrix, pointAttribute(candidate, "RightDirection", anchor))
100
106
  }] : [];
101
107
  });
102
108
  return points.length > 0 ? [{
@@ -297,4 +303,4 @@ function attachPlacements(placements, pageElements, pages, report) {
297
303
  for (const item of placements.polygons) if (pageIndex(item.center) < 0) report.add(report.unsupportedElements, "polygon art group", item.polygon.location);
298
304
  }
299
305
  //#endregion
300
- export { attachPlacements, contains, pageArea, placementBounds, spreadPlacements };
306
+ export { applyTransform, attachPlacements, contains, itemTransform, pageArea, placementBounds, pointValue, spreadPlacements };
@@ -4,6 +4,14 @@ function formatRoundedNumber(value, digits = 2) {
4
4
  const rounded = Number(value.toFixed(digits));
5
5
  return String(Object.is(rounded, -0) ? 0 : rounded);
6
6
  }
7
+ function logicalCornerRadii(corners, direction) {
8
+ return {
9
+ "border-start-start-radius": direction === "rtl" ? corners.topRight : corners.topLeft,
10
+ "border-start-end-radius": direction === "rtl" ? corners.topLeft : corners.topRight,
11
+ "border-end-start-radius": direction === "rtl" ? corners.bottomRight : corners.bottomLeft,
12
+ "border-end-end-radius": direction === "rtl" ? corners.bottomLeft : corners.bottomRight
13
+ };
14
+ }
7
15
  function positionedBoxStyle(bounds, options) {
8
16
  const scale = options.unit === "mm" ? MM_PER_PT : 1;
9
17
  const digits = options.unit === "mm" ? 6 : 2;
@@ -11,4 +19,4 @@ function positionedBoxStyle(bounds, options) {
11
19
  return `position:absolute;inset-inline-start:${value(options.rtl ? options.pageWidth - options.margin.right - bounds.right : bounds.left - options.margin.left)};inline-size:${value(bounds.right - bounds.left)};inset-block-start:${value(bounds.top - options.blockOrigin)};block-size:${value(bounds.bottom - bounds.top)}`;
12
20
  }
13
21
  //#endregion
14
- export { formatRoundedNumber, positionedBoxStyle };
22
+ export { formatRoundedNumber, logicalCornerRadii, positionedBoxStyle };
package/dist/report.d.ts CHANGED
@@ -26,6 +26,7 @@ export interface IdmlReportData {
26
26
  unmappedFonts: IdmlFontUse[];
27
27
  links: IdmlLinkUse[];
28
28
  strippedAssetContent: Record<string, string[]>;
29
+ versoOpenings: Record<string, string[]>;
29
30
  mixedMasterChapters: IdmlMixedMasterChapter[];
30
31
  }
31
32
  //#endregion
package/dist/report.js CHANGED
@@ -7,6 +7,7 @@ const BUCKET_NAMES = {
7
7
  unsupportedLanguages: "language",
8
8
  spotColors: "spot"
9
9
  };
10
+ const KEYED_RECORD_NAMES = ["strippedAssetContent", "versoOpenings"];
10
11
  function fontUseKey(appliedFont, fontStyle) {
11
12
  return `${appliedFont}\u0000${fontStyle ?? ""}`;
12
13
  }
@@ -16,6 +17,7 @@ var ReportCollector = class {
16
17
  unmappedFonts = /* @__PURE__ */ new Map();
17
18
  links = /* @__PURE__ */ new Map();
18
19
  strippedAssetContent = /* @__PURE__ */ new Map();
20
+ versoOpenings = /* @__PURE__ */ new Map();
19
21
  mixedMasterChapters;
20
22
  buckets = Object.fromEntries(Object.keys(BUCKET_NAMES).map((name) => [name, /* @__PURE__ */ new Map()]));
21
23
  get unsupportedElements() {
@@ -46,7 +48,7 @@ var ReportCollector = class {
46
48
  for (const font of seed?.fonts ?? []) this.font(font.appliedFont, font.fontStyle, font.locations[0] ?? "Resources/Styles.xml");
47
49
  for (const font of seed?.unmappedFonts ?? []) for (const location of font.locations) this.unmappedFont(font.appliedFont, font.fontStyle, location);
48
50
  for (const link of seed?.links ?? []) for (const location of link.locations) this.link(link.uri, location);
49
- for (const [uri, findings] of Object.entries(seed?.strippedAssetContent ?? {})) for (const finding of findings) this.strippedAsset(uri, finding);
51
+ for (const name of KEYED_RECORD_NAMES) for (const [key, locations] of Object.entries(seed?.[name] ?? {})) for (const location of locations) this.recordKeyed(this[name], key, location);
50
52
  }
51
53
  add(bucket, name, location, count = 1) {
52
54
  bucket.set(name, (bucket.get(name) ?? 0) + count);
@@ -76,9 +78,15 @@ var ReportCollector = class {
76
78
  this.links.set(uri, found);
77
79
  }
78
80
  strippedAsset(uri, finding) {
79
- const found = this.strippedAssetContent.get(uri) ?? [];
80
- this.recordLocation(found, finding);
81
- this.strippedAssetContent.set(uri, found);
81
+ this.recordKeyed(this.strippedAssetContent, uri, finding);
82
+ }
83
+ versoOpening(story, page) {
84
+ this.recordKeyed(this.versoOpenings, story, page);
85
+ }
86
+ recordKeyed(target, key, location) {
87
+ const found = target.get(key) ?? [];
88
+ this.recordLocation(found, location);
89
+ target.set(key, found);
82
90
  }
83
91
  record(target, appliedFont, fontStyle, location) {
84
92
  const key = fontUseKey(appliedFont, fontStyle);
@@ -95,13 +103,15 @@ var ReportCollector = class {
95
103
  }
96
104
  data() {
97
105
  const record = (values) => Object.fromEntries([...values].sort(([a], [b]) => a.localeCompare(b)));
106
+ const buckets = Object.fromEntries(Object.keys(BUCKET_NAMES).map((name) => [name, record(this.buckets[name])]));
107
+ const keyedRecords = Object.fromEntries(KEYED_RECORD_NAMES.map((name) => [name, Object.fromEntries([...this[name]].sort(([a], [b]) => a.localeCompare(b)))]));
98
108
  return {
99
- ...Object.fromEntries(Object.keys(BUCKET_NAMES).map((name) => [name, record(this.buckets[name])])),
109
+ ...buckets,
110
+ ...keyedRecords,
100
111
  locations: Object.fromEntries([...this.locations].sort(([a], [b]) => a.localeCompare(b))),
101
112
  fonts: [...this.fonts.values()],
102
113
  unmappedFonts: [...this.unmappedFonts.values()],
103
114
  links: [...this.links.values()],
104
- strippedAssetContent: Object.fromEntries([...this.strippedAssetContent].sort(([a], [b]) => a.localeCompare(b))),
105
115
  mixedMasterChapters: [...this.mixedMasterChapters]
106
116
  };
107
117
  }
package/dist/stories.js CHANGED
@@ -2,9 +2,9 @@ import { BOOK_FURNITURE, MOTIF_ATTRIBUTE, motifFieldAttribute, resolveDirection
2
2
  import { Element, Text } from "domhandler";
3
3
  import { BOOK_COLUMN_MAX, BOOK_COLUMN_MIN } from "@segnavia/html";
4
4
  import serialize from "dom-serializer";
5
+ import { parseDocument } from "htmlparser2";
5
6
  //#region src/stories.ts
6
7
  const OMITTED = /* @__PURE__ */ new Set([
7
- "TextFrame",
8
8
  "Rectangle",
9
9
  "Group",
10
10
  "Polygon",
@@ -30,7 +30,7 @@ function rangeAttributes(node) {
30
30
  function tokens(root, report) {
31
31
  const result = [];
32
32
  const terminal = (node, character) => {
33
- if (node.name === "Content") result.push({
33
+ if (node.name === "#text") result.push({
34
34
  kind: "text",
35
35
  text: node.text ?? "",
36
36
  character,
@@ -52,6 +52,10 @@ function tokens(root, report) {
52
52
  character,
53
53
  location: node.location
54
54
  });
55
+ else if (node.name === "TextFrame") result.push({
56
+ kind: "anchored",
57
+ location: node.location
58
+ });
55
59
  else if (OMITTED.has(node.name)) report.add(report.unsupportedElements, node.name, node.location);
56
60
  else if (node.name === "Properties") return true;
57
61
  else if (node.name === "Change" && node.attributes.ChangeType !== void 0 && !node.attributes.ChangeType.includes("Inserted")) report.add(report.unsupportedElements, "Change", node.location);
@@ -59,6 +63,16 @@ function tokens(root, report) {
59
63
  return true;
60
64
  };
61
65
  const walk = (node, character) => {
66
+ if (node.name === "Content") {
67
+ if (node.children.length === 0) result.push({
68
+ kind: "text",
69
+ text: node.text ?? "",
70
+ character,
71
+ location: node.location
72
+ });
73
+ else for (const child of node.children) walk(child, character);
74
+ return;
75
+ }
62
76
  if (terminal(node, character)) return;
63
77
  const nextCharacter = node.name === "CharacterStyleRange" ? node : character;
64
78
  if (WRAPPERS.has(node.name)) report.add(report.unsupportedElements, node.name, node.location);
@@ -121,7 +135,7 @@ function furnitureNode(token, variables, master, styles, direction, report) {
121
135
  if (master && variable) return markNode(variable, className);
122
136
  report.add(report.unsupportedElements, variable ? `TextVariable:${variable.name}` : `TextVariableInstance:${token.variable ?? "unknown"}`, token.location);
123
137
  }
124
- function paragraphChildren(paragraphTokens, styles, report, direction, variables, master) {
138
+ function paragraphChildren(paragraphTokens, styles, report, direction, variables, master, options) {
125
139
  const children = [];
126
140
  let marked;
127
141
  for (const token of paragraphTokens) {
@@ -143,6 +157,12 @@ function paragraphChildren(paragraphTokens, styles, report, direction, variables
143
157
  continue;
144
158
  }
145
159
  marked = void 0;
160
+ if (token.kind === "anchored") {
161
+ const html = options.anchored?.(token.location, direction);
162
+ if (html) children.push(...parseDocument(html, { xmlMode: false }).children.filter((child) => child instanceof Text || child instanceof Element));
163
+ else if (!options.anchored) report.add(report.unsupportedElements, "TextFrame", token.location);
164
+ continue;
165
+ }
146
166
  const furniture = furnitureNode(token, variables, master, styles, direction, report);
147
167
  if (furniture) children.push(furniture);
148
168
  }
@@ -157,7 +177,7 @@ function textNodes(value) {
157
177
  }
158
178
  return result;
159
179
  }
160
- function paragraphNode(paragraph, paragraphTokens, styles, report, bookDirection, bookLanguage, storyDirection, frame, variables, master) {
180
+ function paragraphNode(paragraph, paragraphTokens, styles, report, bookDirection, bookLanguage, storyDirection, frame, variables, master, options) {
161
181
  const attributes = rangeAttributes(paragraph);
162
182
  const styleId = attributes.AppliedParagraphStyle;
163
183
  const flattened = styles.flattened(styleId, "paragraph");
@@ -178,15 +198,21 @@ function paragraphNode(paragraph, paragraphTokens, styles, report, bookDirection
178
198
  else report.addOnce(report.unsupportedElements, `TextColumnCount:${frame.columns}`, frame.location ?? paragraph.location);
179
199
  }
180
200
  if (span === "SplitColumns") report.add(report.unsupportedStyleProperties, "SpanColumnType:SplitColumns", paragraph.location);
181
- const children = paragraphChildren(paragraphTokens, styles, report, resolvedDirection, variables, master);
201
+ const children = paragraphChildren(paragraphTokens, styles, report, resolvedDirection, variables, master, options);
182
202
  if (children.length === 0) children.push(new Element("br", {}, []));
183
- return new Element("p", htmlAttributes, children);
203
+ return new Element(options.inlineParagraphs ? "span" : "p", options.inlineParagraphs ? {
204
+ ...htmlAttributes,
205
+ style: "display:block"
206
+ } : htmlAttributes, children);
184
207
  }
185
- function storyHtml(story, styles, report, bookDirection, bookLanguage) {
186
- if (!story.root) return { html: serialize(new Element("p", {}, [new Element("br", {}, [])]), {
187
- xmlMode: false,
188
- encodeEntities: "utf8"
189
- }) };
208
+ function storyHtml(story, styles, report, bookDirection, bookLanguage, options = {}) {
209
+ if (!story.root) {
210
+ const empty = new Element(options.inlineParagraphs ? "span" : "p", options.inlineParagraphs ? { style: "display:block" } : {}, [new Element("br", {}, [])]);
211
+ return { html: serialize(empty, {
212
+ xmlMode: false,
213
+ encodeEntities: "utf8"
214
+ }) };
215
+ }
190
216
  const paragraphs = [];
191
217
  const paragraphRanges = [];
192
218
  const collectParagraphs = (node) => {
@@ -198,14 +224,14 @@ function storyHtml(story, styles, report, bookDirection, bookLanguage) {
198
224
  const all = tokens(paragraph, report);
199
225
  let current = [];
200
226
  const flush = () => {
201
- paragraphs.push(paragraphNode(paragraph, current, styles, report, bookDirection, bookLanguage, story.direction, story.frame, story.textVariables ?? [], story.master ?? false));
227
+ paragraphs.push(paragraphNode(paragraph, current, styles, report, bookDirection, bookLanguage, story.direction, story.frame, story.textVariables ?? [], story.master ?? false, options));
202
228
  current = [];
203
229
  };
204
230
  for (const token of all) if (token.kind === "paragraph") flush();
205
231
  else current.push(token);
206
232
  if (current.length > 0 || all.length === 0 || all.at(-1)?.kind !== "paragraph") flush();
207
233
  }
208
- if (paragraphs.length === 0) paragraphs.push(new Element("p", {}, [new Element("br", {}, [])]));
234
+ if (paragraphs.length === 0) paragraphs.push(new Element(options.inlineParagraphs ? "span" : "p", options.inlineParagraphs ? { style: "display:block" } : {}, [new Element("br", {}, [])]));
209
235
  return {
210
236
  html: serialize(paragraphs, {
211
237
  xmlMode: false,
@@ -5,9 +5,17 @@ import { isTag } from "domhandler";
5
5
  function textNode(node, part, path) {
6
6
  const children = [];
7
7
  let childIndex = 0;
8
+ const content = localName(node.name) === "Content";
8
9
  for (const child of node.children) {
9
10
  childIndex += 1;
10
11
  if (isTag(child)) children.push(textNode(child, part, `${path}/${localName(child.name)}[${childIndex}]`));
12
+ else if (child.type === "text" && content && child.data) children.push({
13
+ name: "#text",
14
+ attributes: {},
15
+ text: child.data,
16
+ children: [],
17
+ location: `${part}${path}#text${childIndex}`
18
+ });
11
19
  else if (child.type === "directive") children.push({
12
20
  name: "#processing-instruction",
13
21
  attributes: {},
@@ -16,7 +24,7 @@ function textNode(node, part, path) {
16
24
  location: `${part}${path}#pi${childIndex}`
17
25
  });
18
26
  }
19
- const directText = node.children.filter((child) => child.type === "text").map((child) => child.data).join("");
27
+ const directText = content ? "" : node.children.filter((child) => child.type === "text").map((child) => child.data).join("");
20
28
  return {
21
29
  name: localName(node.name),
22
30
  attributes: { ...node.attribs },
@@ -24,6 +24,26 @@ const STYLE_PROPERTY_MAPPINGS = {
24
24
  KeepFirstLines: css("keeps"),
25
25
  KeepLastLines: css("keeps"),
26
26
  StartParagraph: css("starts"),
27
+ ParagraphShadingOn: css("paragraph-shading"),
28
+ ParagraphShadingColor: css("paragraph-shading"),
29
+ ParagraphShadingTint: css("paragraph-shading"),
30
+ ParagraphShadingTopOffset: css("paragraph-shading"),
31
+ ParagraphShadingBottomOffset: css("paragraph-shading"),
32
+ ParagraphShadingLeftOffset: css("paragraph-shading"),
33
+ ParagraphShadingRightOffset: css("paragraph-shading"),
34
+ ParagraphShadingTopLeftCornerOption: css("paragraph-shading"),
35
+ ParagraphShadingTopLeftCornerRadius: css("paragraph-shading"),
36
+ ParagraphShadingTopRightCornerOption: css("paragraph-shading"),
37
+ ParagraphShadingTopRightCornerRadius: css("paragraph-shading"),
38
+ ParagraphShadingBottomLeftCornerOption: css("paragraph-shading"),
39
+ ParagraphShadingBottomLeftCornerRadius: css("paragraph-shading"),
40
+ ParagraphShadingBottomRightCornerOption: css("paragraph-shading"),
41
+ ParagraphShadingBottomRightCornerRadius: css("paragraph-shading"),
42
+ ParagraphShadingWidth: css("paragraph-shading"),
43
+ ParagraphShadingTopOrigin: css("paragraph-shading"),
44
+ ParagraphShadingBottomOrigin: css("paragraph-shading"),
45
+ ParagraphShadingClipToFrame: css("paragraph-shading"),
46
+ ParagraphShadingSuppressPrinting: css("paragraph-shading"),
27
47
  ParagraphDirection: { kind: "direction" },
28
48
  AppliedLanguage: { kind: "language" },
29
49
  AppliedFont: { kind: "font" },
@@ -99,11 +99,6 @@ const SWITCHED_GROUPS = [
99
99
  enabled: (values) => truthy(values.ParagraphBorderOn),
100
100
  reportAs: "ParagraphBorderOn"
101
101
  },
102
- {
103
- matches: (name) => name.startsWith("ParagraphShading"),
104
- enabled: (values) => truthy(values.ParagraphShadingOn),
105
- reportAs: "ParagraphShadingOn"
106
- },
107
102
  {
108
103
  matches: (name) => name.startsWith("Underline"),
109
104
  enabled: (values) => truthy(values.Underline) || values.Underline === "Underline",
@@ -192,6 +187,22 @@ function activeIdmlValue(value) {
192
187
  function isHyphenationProperty(name) {
193
188
  return name.startsWith("Hyphenat") || name === "HyphenWeight" || name === "ProviderHyphenationStyle";
194
189
  }
190
+ function alwaysHandledElsewhere(name) {
191
+ return isHyphenationProperty(name) || JUSTIFICATION_SETTINGS.has(name) || [
192
+ "EmptyGrepStyles",
193
+ "EmptyNestedStyles",
194
+ "EmptyLineStyles"
195
+ ].includes(name);
196
+ }
197
+ const COLLECTION_PROPERTIES = /* @__PURE__ */ new Set([
198
+ "AllGREPStyles",
199
+ "AllNestedStyles",
200
+ "AllLineStyles",
201
+ "TabList"
202
+ ]);
203
+ function isCollectionProperty(name) {
204
+ return COLLECTION_PROPERTIES.has(name);
205
+ }
195
206
  function groupedProperties(names, resolved) {
196
207
  const output = [];
197
208
  if (names.some(isHyphenationProperty) && truthy(resolved.Hyphenation)) output.push("Hyphenation");
@@ -204,17 +215,9 @@ function groupedProperties(names, resolved) {
204
215
  }
205
216
  function reportableProperty(name, value, resolved, root) {
206
217
  if (stylePropertyMapping(name)) return void 0;
207
- if (isHyphenationProperty(name) || JUSTIFICATION_SETTINGS.has(name)) return void 0;
208
- if ([
209
- "EmptyGrepStyles",
210
- "EmptyNestedStyles",
211
- "EmptyLineStyles"
212
- ].includes(name)) return void 0;
213
- if ([
214
- "AllGREPStyles",
215
- "AllNestedStyles",
216
- "AllLineStyles"
217
- ].includes(name) || name === "TabList") return hasEntries(value) ? name : void 0;
218
+ if (alwaysHandledElsewhere(name)) return void 0;
219
+ if (isCollectionProperty(name)) return hasEntries(value) ? name : void 0;
220
+ if (name.startsWith("ParagraphShading")) return truthy(resolved.ParagraphShadingOn) && activeIdmlValue(value) ? name : void 0;
218
221
  const switched = SWITCHED_GROUPS.find((group) => group.matches(name));
219
222
  if (switched) return switched.enabled(resolved) ? switched.reportAs : void 0;
220
223
  if ((CJK_PREFIXES.some((prefix) => name.startsWith(prefix)) || CJK_PROPERTIES.has(name)) && !isCjkLanguage(resolved.AppliedLanguage)) return void 0;