@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.
package/dist/styles.js CHANGED
@@ -1,11 +1,13 @@
1
1
  import { idmlDirection, localName } from "./xml.js";
2
2
  import { resolveSwatchColor } from "./swatch-color.js";
3
- import { stylePropertyMapping } from "./style-mappings.js";
3
+ import { paragraphBoxDeclarations } from "./paragraph-box.js";
4
+ import { STYLE_PROPERTY_MAPPINGS, stylePropertyMapping } from "./style-mappings.js";
4
5
  import { reportableStyleProperties } from "./style-property-report.js";
5
6
  import { isTag } from "domhandler";
6
7
  //#region src/styles.ts
7
8
  const PARAGRAPH_ROOT = "$ID/[No paragraph style]";
8
9
  const CHARACTER_ROOT = "$ID/[No character style]";
10
+ const OBJECT_ROOT = "$ID/[None]";
9
11
  function text(element) {
10
12
  return element.children.map((child) => "data" in child ? child.data : isTag(child) ? text(child) : "").join("").trim();
11
13
  }
@@ -21,14 +23,18 @@ function properties(element) {
21
23
  }
22
24
  function parseStyles(roots, part) {
23
25
  const result = [];
24
- for (const kind of ["ParagraphStyle", "CharacterStyle"]) for (const element of roots.filter((candidate) => localName(candidate.name) === kind)) {
26
+ for (const kind of [
27
+ "ParagraphStyle",
28
+ "CharacterStyle",
29
+ "ObjectStyle"
30
+ ]) for (const element of roots.filter((candidate) => localName(candidate.name) === kind)) {
25
31
  const values = properties(element);
26
32
  const id = values.Self;
27
33
  if (!id) continue;
28
34
  result.push({
29
35
  id,
30
36
  name: values.Name ?? id.split("/").at(-1) ?? id,
31
- kind: kind === "ParagraphStyle" ? "paragraph" : "character",
37
+ kind: kind === "ParagraphStyle" ? "paragraph" : kind === "CharacterStyle" ? "character" : "object",
32
38
  ...values.BasedOn ? { basedOn: values.BasedOn } : {},
33
39
  properties: Object.fromEntries(Object.entries(values).filter(([name]) => ![
34
40
  "Self",
@@ -59,7 +65,32 @@ function parseSwatches(roots) {
59
65
  return result;
60
66
  }
61
67
  function rootName(kind) {
62
- return kind === "paragraph" ? PARAGRAPH_ROOT : CHARACTER_ROOT;
68
+ return kind === "paragraph" ? PARAGRAPH_ROOT : kind === "character" ? CHARACTER_ROOT : OBJECT_ROOT;
69
+ }
70
+ function findStyle(styles, id, kind) {
71
+ if (!id) return void 0;
72
+ const exact = styles.find((style) => style.id === id);
73
+ if (exact?.kind === kind) return exact;
74
+ return styles.find((style) => style.kind === kind && (style.name === id || style.id.endsWith(`/${id}`)));
75
+ }
76
+ function rootStyle(styles, kind) {
77
+ return styles.find((style) => style.kind === kind && (style.name === rootName(kind) || style.id.endsWith(`/${rootName(kind)}`)));
78
+ }
79
+ function styleChain(styles, id, kind) {
80
+ const root = rootStyle(styles, kind);
81
+ const chain = [];
82
+ const seen = /* @__PURE__ */ new Set();
83
+ let current = findStyle(styles, id, kind);
84
+ while (current && !seen.has(current.id)) {
85
+ seen.add(current.id);
86
+ chain.unshift(current);
87
+ current = findStyle(styles, current.basedOn, kind);
88
+ }
89
+ if (root && !chain.includes(root)) chain.unshift(root);
90
+ return chain;
91
+ }
92
+ function flattenedStyle(styles, id, kind) {
93
+ return Object.assign({}, ...styleChain(styles, id, kind).map((style) => style.properties));
63
94
  }
64
95
  function slug(value) {
65
96
  return value.normalize("NFKD").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "style";
@@ -92,6 +123,21 @@ function resolvedFontProperties(changed, resolved) {
92
123
  ...resolved.FontStyle === void 0 ? {} : { FontStyle: resolved.FontStyle }
93
124
  };
94
125
  }
126
+ const PARAGRAPH_BOX_PROPERTIES = [
127
+ "SpaceBefore",
128
+ "SpaceAfter",
129
+ "LeftIndent",
130
+ "RightIndent",
131
+ ...Object.keys(STYLE_PROPERTY_MAPPINGS).filter((name) => name.startsWith("ParagraphShading"))
132
+ ];
133
+ function paragraphBoxOverrideProperties(changed, resolved, base) {
134
+ const touchesBox = Object.keys(changed).some((name) => PARAGRAPH_BOX_PROPERTIES.includes(name));
135
+ const hasShading = [base.ParagraphShadingOn, resolved.ParagraphShadingOn].some((value) => value?.trim().toLowerCase() === "true");
136
+ if (!touchesBox || !hasShading) return changed;
137
+ const output = { ...changed };
138
+ for (const name of PARAGRAPH_BOX_PROPERTIES) if (resolved[name] !== void 0) output[name] = resolved[name];
139
+ return output;
140
+ }
95
141
  var StyleMapper = class {
96
142
  swatches;
97
143
  report;
@@ -110,6 +156,7 @@ var StyleMapper = class {
110
156
  this.byId = new Map(styles.map((style) => [style.id, style]));
111
157
  const taken = /* @__PURE__ */ new Set();
112
158
  for (const style of styles) {
159
+ if (style.kind === "object") continue;
113
160
  const base = `${style.kind === "paragraph" ? "idml-p-" : "idml-c-"}${slug(style.name)}`;
114
161
  let candidate = base;
115
162
  let suffix = 2;
@@ -119,20 +166,10 @@ var StyleMapper = class {
119
166
  }
120
167
  }
121
168
  flattened(id, kind) {
122
- return Object.assign({}, ...this.styleChain(id, kind).map((style) => style.properties));
169
+ return flattenedStyle([...this.byId.values()], id, kind);
123
170
  }
124
171
  styleChain(id, kind) {
125
- const root = this.rootStyle(kind);
126
- const chain = [];
127
- const seen = /* @__PURE__ */ new Set();
128
- let current = this.findStyle(id, kind);
129
- while (current && !seen.has(current.id)) {
130
- seen.add(current.id);
131
- chain.unshift(current);
132
- current = this.findStyle(current.basedOn, kind);
133
- }
134
- if (root && !chain.includes(root)) chain.unshift(root);
135
- return chain;
172
+ return styleChain([...this.byId.values()], id, kind);
136
173
  }
137
174
  styleClass(id, _kind) {
138
175
  if (!id || id.includes("[None]") || id.includes("[No character style]")) return void 0;
@@ -142,11 +179,13 @@ var StyleMapper = class {
142
179
  return value;
143
180
  }
144
181
  overrideClass(values, base, directionValue, location, styleDirection) {
145
- const changed = Object.fromEntries(Object.entries(values).filter(([name, value]) => stylePropertyMapping(name)?.kind !== "non-formatting" && value !== base[name]));
182
+ let changed = Object.fromEntries(Object.entries(values).filter(([name, value]) => stylePropertyMapping(name)?.kind !== "non-formatting" && value !== base[name]));
146
183
  const resolved = {
147
184
  ...base,
148
185
  ...values
149
186
  };
187
+ const resetShading = base.ParagraphShadingOn?.trim().toLowerCase() === "true" && values.ParagraphShadingOn?.trim().toLowerCase() === "false";
188
+ changed = paragraphBoxOverrideProperties(changed, resolved, base);
150
189
  for (const name of reportableStyleProperties(changed, resolved)) this.report.add(this.report.unsupportedOverrideProperties, name, location);
151
190
  if (changed.AppliedFont !== void 0 || changed.FontStyle !== void 0) this.report.font(resolved.AppliedFont, resolved.FontStyle, location);
152
191
  const cssProperties = Object.fromEntries(Object.entries(changed).filter(([name]) => stylePropertyMapping(name)?.kind === "css"));
@@ -156,7 +195,7 @@ var StyleMapper = class {
156
195
  for (const name of ["Justification"]) if (base[name] !== void 0 && cssProperties[name] === void 0) cssProperties[name] = base[name];
157
196
  }
158
197
  if (Object.keys(cssProperties).length === 0) return void 0;
159
- const key = JSON.stringify(Object.entries(cssProperties).sort(([a], [b]) => a.localeCompare(b))) + directionValue;
198
+ const key = JSON.stringify(Object.entries(cssProperties).sort(([a], [b]) => a.localeCompare(b))) + directionValue + resetShading;
160
199
  const found = this.overrides.get(key);
161
200
  if (found) return found.name;
162
201
  const name = `idml-o-${this.overrides.size + 1}`;
@@ -164,7 +203,8 @@ var StyleMapper = class {
164
203
  name,
165
204
  properties: cssProperties,
166
205
  direction: directionValue,
167
- location
206
+ location,
207
+ resetShading
168
208
  });
169
209
  return name;
170
210
  }
@@ -194,16 +234,16 @@ var StyleMapper = class {
194
234
  this.reportUnmappedStyleProperties(style);
195
235
  this.report.font(values.AppliedFont, values.FontStyle, style.location);
196
236
  const dir = style.kind === "paragraph" ? this.paragraphStyleDirection(values) : this.bookDirection;
197
- const declarations = this.declarations(values, dir, style.location);
237
+ const declarations = this.declarations(values, dir, style.location, this.report.unsupportedStyleProperties, false);
198
238
  rules.push(`.${name}{${declarations}}`);
199
239
  }
200
240
  for (const value of this.overrides.values()) {
201
- const declarations = this.declarations(value.properties, value.direction, value.location, this.report.unsupportedOverrideProperties);
241
+ const declarations = this.declarations(value.properties, value.direction, value.location, this.report.unsupportedOverrideProperties, value.resetShading);
202
242
  if (declarations) rules.push(`.${value.name}{${declarations}}`);
203
243
  }
204
244
  return rules.join("");
205
245
  }
206
- declarations(values, dir, location, unsupported = this.report.unsupportedStyleProperties) {
246
+ declarations(values, dir, location, unsupported, resetShading) {
207
247
  const output = [];
208
248
  output.push(...this.fonts?.declarations(values.AppliedFont, values.FontStyle, location) ?? []);
209
249
  const mapped = new Set(Object.keys(values).filter((name) => stylePropertyMapping(name)?.kind === "css"));
@@ -217,13 +257,17 @@ var StyleMapper = class {
217
257
  else pt("Leading", "line-height");
218
258
  const tracking = Number(values.Tracking);
219
259
  if (mapped.has("Tracking") && Number.isFinite(tracking)) output.push(`letter-spacing:${tracking / 1e3}em`);
220
- pt("SpaceBefore", "margin-block-start");
221
- pt("SpaceAfter", "margin-block-end");
222
260
  pt("FirstLineIndent", "text-indent");
223
- const startIndent = dir === "rtl" ? "RightIndent" : "LeftIndent";
224
- const endIndent = dir === "rtl" ? "LeftIndent" : "RightIndent";
225
- pt(startIndent, "padding-inline-start");
226
- pt(endIndent, "padding-inline-end");
261
+ output.push(...paragraphBoxDeclarations({
262
+ values,
263
+ mapped,
264
+ resetShading,
265
+ direction: dir,
266
+ swatches: this.swatches,
267
+ report: this.report,
268
+ unsupported,
269
+ location
270
+ }));
227
271
  textDeclarations(values, mapped, output);
228
272
  if (mapped.has("Justification")) this.justification(values.Justification, dir, output, location, unsupported);
229
273
  if (hasCssMapping(mapped, "keeps")) this.keeps(values, output);
@@ -233,13 +277,10 @@ var StyleMapper = class {
233
277
  return output.join(";");
234
278
  }
235
279
  findStyle(id, kind) {
236
- if (!id) return void 0;
237
- const exact = this.byId.get(id);
238
- if (exact?.kind === kind) return exact;
239
- return [...this.byId.values()].find((style) => style.kind === kind && (style.name === id || style.id.endsWith(`/${id}`)));
280
+ return findStyle([...this.byId.values()], id, kind);
240
281
  }
241
282
  rootStyle(kind) {
242
- return [...this.byId.values()].find((style) => style.kind === kind && (style.name === rootName(kind) || style.id.endsWith(`/${rootName(kind)}`)));
283
+ return rootStyle([...this.byId.values()], kind);
243
284
  }
244
285
  reportUnmappedStyleProperties(style) {
245
286
  const root = this.rootStyle(style.kind);
@@ -279,4 +320,4 @@ var StyleMapper = class {
279
320
  }
280
321
  };
281
322
  //#endregion
282
- export { StyleMapper, parseStyles, parseSwatches };
323
+ export { StyleMapper, flattenedStyle, parseStyles, parseSwatches };
@@ -1,15 +1,15 @@
1
1
  //#region src/swatch-color.ts
2
2
  const colorComponent = (value) => Math.round(value + 1e-9);
3
- function unresolved(id, location, use, report) {
4
- report.add(report.unsupportedStyleProperties, `FillColor:${id}`, location);
3
+ function unresolved(id, location, use, report, property) {
4
+ report.add(report.unsupportedStyleProperties, `${property}:${id}`, location);
5
5
  return use === "fill" ? "currentColor" : void 0;
6
6
  }
7
- function resolveSwatchColor(id, swatches, report, location, use) {
7
+ function resolveSwatchColor(id, swatches, report, location, use, property = "FillColor") {
8
8
  if (!id || id.endsWith("/None") || id === "None") return use === "fill" ? "none" : void 0;
9
9
  if (id.endsWith("/Black") || id === "Black") return "#000";
10
10
  if (id.endsWith("/Paper") || id === "Paper") return "#fff";
11
11
  const swatch = swatches.find((value) => value.id === id || value.name === id);
12
- if (!swatch) return unresolved(id, location, use, report);
12
+ if (!swatch) return unresolved(id, location, use, report, property);
13
13
  if (swatch.model === "spot") {
14
14
  report.add(report.spotColors, swatch.name, location);
15
15
  return use === "fill" ? "currentColor" : void 0;
@@ -19,7 +19,7 @@ function resolveSwatchColor(id, swatches, report, location, use) {
19
19
  const [c, m, y, k] = swatch.values.map((value) => value / 100);
20
20
  return `rgb(${colorComponent(255 * (1 - c) * (1 - k))} ${colorComponent(255 * (1 - m) * (1 - k))} ${colorComponent(255 * (1 - y) * (1 - k))})`;
21
21
  }
22
- return unresolved(id, location, use, report);
22
+ return unresolved(id, location, use, report, property);
23
23
  }
24
24
  //#endregion
25
25
  export { resolveSwatchColor };
package/dist/types.d.ts CHANGED
@@ -10,7 +10,7 @@ export interface IdmlTextNode {
10
10
  export interface IdmlStyle {
11
11
  id: string;
12
12
  name: string;
13
- kind: 'paragraph' | 'character';
13
+ kind: 'paragraph' | 'character' | 'object';
14
14
  basedOn?: string;
15
15
  properties: Record<string, string>;
16
16
  location: string;
@@ -87,6 +87,24 @@ export interface IdmlStory {
87
87
  root?: IdmlTextNode;
88
88
  frame?: IdmlFramePreference;
89
89
  }
90
+ export interface IdmlAnchoredFrame {
91
+ story: string;
92
+ location: string;
93
+ width?: number;
94
+ position: string;
95
+ anchorYoffset?: number;
96
+ fill?: string;
97
+ stroke?: {
98
+ color: string;
99
+ weight: number;
100
+ };
101
+ corners?: {
102
+ topLeft?: number;
103
+ topRight?: number;
104
+ bottomLeft?: number;
105
+ bottomRight?: number;
106
+ };
107
+ }
90
108
  export interface IdmlTextVariable {
91
109
  id: string;
92
110
  name: string;
@@ -111,6 +129,7 @@ export interface IdmlDocumentPreferences {
111
129
  export interface IdmlPage {
112
130
  id: string;
113
131
  appliedMaster?: string;
132
+ showMasterItems?: boolean;
114
133
  stories: string[];
115
134
  side?: 'left' | 'right' | 'single';
116
135
  bodyFrames?: IdmlFramePlacement[];
@@ -140,6 +159,7 @@ export interface IdmlMasterSpread {
140
159
  id: string;
141
160
  name: string;
142
161
  basedOn?: string;
162
+ showMasterItems?: boolean;
143
163
  pages: IdmlMasterPage[];
144
164
  unmappedItems: {
145
165
  type: string;
@@ -39,6 +39,7 @@ const MAPPED_ELEMENTS = /* @__PURE__ */ new Set([
39
39
  "CharacterStyleGroup",
40
40
  "ParagraphStyle",
41
41
  "CharacterStyle",
42
+ "AnchoredObjectSetting",
42
43
  "Color",
43
44
  "TextVariable",
44
45
  "MatchCharacterStylePreference",
@@ -48,7 +49,7 @@ function insideMappedResource(element) {
48
49
  let parent = element.parent;
49
50
  while (parent && "name" in parent) {
50
51
  const name = localName(parent.name);
51
- if (name === "ParagraphStyle" || name === "CharacterStyle" || name === "Color") return true;
52
+ if (name === "ParagraphStyle" || name === "CharacterStyle" || name === "ObjectStyle" || name === "Color") return true;
52
53
  parent = parent.parent;
53
54
  }
54
55
  return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@segnavia/idml",
3
- "version": "0.33.0",
3
+ "version": "0.34.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -26,8 +26,8 @@
26
26
  }
27
27
  },
28
28
  "dependencies": {
29
- "@segnavia/format": "0.33.0",
30
- "@segnavia/html": "0.33.0",
29
+ "@segnavia/format": "0.34.0",
30
+ "@segnavia/html": "0.34.0",
31
31
  "dom-serializer": "^2.0.0",
32
32
  "domhandler": "^5.0.3",
33
33
  "htmlparser2": "^10.0.0"