@segnavia/idml 0.30.0 → 0.32.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/fonts.d.ts +10 -0
- package/dist/fonts.js +105 -0
- package/dist/import.d.ts +2 -2
- package/dist/import.js +2 -2
- package/dist/index.d.ts +5 -4
- package/dist/index.js +2 -1
- package/dist/map-book.js +15 -6
- package/dist/masters.js +22 -7
- package/dist/model.d.ts +2 -1
- package/dist/model.js +21 -135
- package/dist/openers.js +134 -0
- package/dist/placement-report.js +15 -0
- package/dist/placements.js +300 -0
- package/dist/positioned-box.js +14 -0
- package/dist/report.d.ts +7 -0
- package/dist/report.js +42 -5
- package/dist/stories.js +1 -1
- package/dist/styles.js +17 -20
- package/dist/svg-path.js +33 -0
- package/dist/swatch-color.js +25 -0
- package/dist/types.d.ts +59 -0
- package/dist/unsupported-elements.js +17 -1
- package/dist/xml.js +19 -1
- package/package.json +3 -3
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/placement-report.ts
|
|
2
|
+
function reportPage(page, consumed, report) {
|
|
3
|
+
for (const link of page.links ?? []) {
|
|
4
|
+
report.link(link.uri, link.location);
|
|
5
|
+
if (!consumed) report.add(report.unsupportedElements, "link", link.location);
|
|
6
|
+
}
|
|
7
|
+
for (const item of page.unmapped ?? []) report.add(report.unsupportedElements, item.name, item.location);
|
|
8
|
+
if (!consumed) for (const art of page.art ?? []) report.add(report.unsupportedElements, "polygon art group", art.location);
|
|
9
|
+
}
|
|
10
|
+
function reportPlacements(model, consumedPages, report) {
|
|
11
|
+
for (const spread of model.spreads) for (const page of spread.pages) reportPage(page, consumedPages.has(page), report);
|
|
12
|
+
for (const master of model.masterSpreads) for (const page of master.pages) reportPage(page, false, report);
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
15
|
+
export { reportPlacements };
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { attribute, elements, localName } from "./xml.js";
|
|
2
|
+
import { framePreference } from "./frame-preferences.js";
|
|
3
|
+
import { GRAPHIC_ELEMENTS, SHAPE_ELEMENTS } from "./unsupported-elements.js";
|
|
4
|
+
import { isTag } from "domhandler";
|
|
5
|
+
//#region src/placements.ts
|
|
6
|
+
const IDENTITY = [
|
|
7
|
+
1,
|
|
8
|
+
0,
|
|
9
|
+
0,
|
|
10
|
+
1,
|
|
11
|
+
0,
|
|
12
|
+
0
|
|
13
|
+
];
|
|
14
|
+
function transform(element) {
|
|
15
|
+
const values = (attribute(element, "ItemTransform") ?? "").trim().split(/\s+/).map(Number);
|
|
16
|
+
return values.length === 6 && values.every(Number.isFinite) ? values : IDENTITY;
|
|
17
|
+
}
|
|
18
|
+
function compose(parent, child) {
|
|
19
|
+
const [a, b, c, d, x, y] = parent;
|
|
20
|
+
const [e, f, g, h, u, v] = child;
|
|
21
|
+
return [
|
|
22
|
+
a * e + c * f,
|
|
23
|
+
b * e + d * f,
|
|
24
|
+
a * g + c * h,
|
|
25
|
+
b * g + d * h,
|
|
26
|
+
a * u + c * v + x,
|
|
27
|
+
b * u + d * v + y
|
|
28
|
+
];
|
|
29
|
+
}
|
|
30
|
+
function apply(value, point) {
|
|
31
|
+
return [value[0] * point[0] + value[2] * point[1] + value[4], value[1] * point[0] + value[3] * point[1] + value[5]];
|
|
32
|
+
}
|
|
33
|
+
function pageArea(element) {
|
|
34
|
+
const value = (attribute(element, "GeometricBounds") ?? "").trim().split(/\s+/).map(Number);
|
|
35
|
+
if (value.length !== 4 || !value.every(Number.isFinite)) return void 0;
|
|
36
|
+
const [top, left, bottom, right] = value;
|
|
37
|
+
const matrix = transform(element);
|
|
38
|
+
return [
|
|
39
|
+
apply(matrix, [left, top]),
|
|
40
|
+
apply(matrix, [right, top]),
|
|
41
|
+
apply(matrix, [right, bottom]),
|
|
42
|
+
apply(matrix, [left, bottom])
|
|
43
|
+
];
|
|
44
|
+
}
|
|
45
|
+
function placementBounds(points) {
|
|
46
|
+
if (points.length === 0) return void 0;
|
|
47
|
+
const xs = points.map(([x]) => x);
|
|
48
|
+
const ys = points.map(([, y]) => y);
|
|
49
|
+
return {
|
|
50
|
+
left: Math.min(...xs),
|
|
51
|
+
top: Math.min(...ys),
|
|
52
|
+
right: Math.max(...xs),
|
|
53
|
+
bottom: Math.max(...ys)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
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)));
|
|
58
|
+
}
|
|
59
|
+
function contains(area, [x, y]) {
|
|
60
|
+
let inside = false;
|
|
61
|
+
for (let index = 0, previous = area.length - 1; index < area.length; previous = index, index += 1) {
|
|
62
|
+
const [xi, yi] = area[index];
|
|
63
|
+
const [xj, yj] = area[previous];
|
|
64
|
+
if (yi === yj && y === yi && x >= Math.min(xi, xj) && x <= Math.max(xi, xj)) return true;
|
|
65
|
+
if (yi > y !== yj > y && x <= (xj - xi) * (y - yi) / (yj - yi) + xi) inside = !inside;
|
|
66
|
+
}
|
|
67
|
+
return inside;
|
|
68
|
+
}
|
|
69
|
+
function centered(box) {
|
|
70
|
+
return [(box.left + box.right) / 2, (box.top + box.bottom) / 2];
|
|
71
|
+
}
|
|
72
|
+
function framePlacement(element, matrix, location) {
|
|
73
|
+
const story = attribute(element, "ParentStory");
|
|
74
|
+
if (!story) return void 0;
|
|
75
|
+
const box = frameBounds(element, matrix);
|
|
76
|
+
const preference = framePreference(element, location);
|
|
77
|
+
return {
|
|
78
|
+
id: attribute(element, "Self") ?? story,
|
|
79
|
+
story,
|
|
80
|
+
...box ? {
|
|
81
|
+
bounds: box,
|
|
82
|
+
center: centered(box)
|
|
83
|
+
} : {},
|
|
84
|
+
...preference ? { preference } : {},
|
|
85
|
+
location
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
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;
|
|
91
|
+
}
|
|
92
|
+
function polygonPlacement(element, matrix, location, group) {
|
|
93
|
+
const paths = elements(element, "GeometryPathType").flatMap((path) => {
|
|
94
|
+
const points = elements(path, "PathPointType").flatMap((candidate) => {
|
|
95
|
+
const anchor = pointAttribute(candidate, "Anchor");
|
|
96
|
+
return anchor ? [{
|
|
97
|
+
anchor: apply(matrix, anchor),
|
|
98
|
+
left: apply(matrix, pointAttribute(candidate, "LeftDirection", anchor)),
|
|
99
|
+
right: apply(matrix, pointAttribute(candidate, "RightDirection", anchor))
|
|
100
|
+
}] : [];
|
|
101
|
+
});
|
|
102
|
+
return points.length > 0 ? [{
|
|
103
|
+
open: attribute(path, "PathOpen") === "true",
|
|
104
|
+
points
|
|
105
|
+
}] : [];
|
|
106
|
+
});
|
|
107
|
+
const box = placementBounds(paths.flatMap((path) => path.points.flatMap((point) => [
|
|
108
|
+
point.anchor,
|
|
109
|
+
point.left,
|
|
110
|
+
point.right
|
|
111
|
+
])));
|
|
112
|
+
if (!box) return void 0;
|
|
113
|
+
const weight = Number(attribute(element, "StrokeWeight"));
|
|
114
|
+
const color = attribute(element, "StrokeColor");
|
|
115
|
+
return {
|
|
116
|
+
polygon: {
|
|
117
|
+
paths,
|
|
118
|
+
fill: attribute(element, "FillColor") ?? "Swatch/None",
|
|
119
|
+
...Number.isFinite(weight) && weight > 0 && color && !color.endsWith("/None") ? { stroke: {
|
|
120
|
+
color,
|
|
121
|
+
weight
|
|
122
|
+
} } : {},
|
|
123
|
+
location
|
|
124
|
+
},
|
|
125
|
+
bounds: box,
|
|
126
|
+
center: centered(box),
|
|
127
|
+
group
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function linkedUri(element) {
|
|
131
|
+
const graphic = elements(element).find((candidate) => GRAPHIC_ELEMENTS.includes(localName(candidate.name)));
|
|
132
|
+
return graphic ? elements(graphic, "Link").map((link) => attribute(link, "LinkResourceURI")).find((uri) => Boolean(uri)) : void 0;
|
|
133
|
+
}
|
|
134
|
+
function collectShape(result, element, name, matrix, location, group) {
|
|
135
|
+
const box = frameBounds(element, matrix);
|
|
136
|
+
const uri = linkedUri(element);
|
|
137
|
+
if (uri) result.links.push({
|
|
138
|
+
...box ? {
|
|
139
|
+
bounds: box,
|
|
140
|
+
center: centered(box)
|
|
141
|
+
} : {},
|
|
142
|
+
uri,
|
|
143
|
+
location
|
|
144
|
+
});
|
|
145
|
+
else if (name === "Polygon") {
|
|
146
|
+
const polygon = polygonPlacement(element, matrix, location, group ?? attribute(element, "Self") ?? location);
|
|
147
|
+
if (polygon) result.polygons.push(polygon);
|
|
148
|
+
else result.unmapped.push({
|
|
149
|
+
name,
|
|
150
|
+
location,
|
|
151
|
+
...box ? { center: centered(box) } : {}
|
|
152
|
+
});
|
|
153
|
+
} else result.unmapped.push({
|
|
154
|
+
name,
|
|
155
|
+
location,
|
|
156
|
+
...box ? { center: centered(box) } : {}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
function collectTerminal(result, element, name, matrix, location, group) {
|
|
160
|
+
if (name === "TextFrame") {
|
|
161
|
+
const frame = framePlacement(element, matrix, location);
|
|
162
|
+
if (frame) result.frames.push(frame);
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
if (SHAPE_ELEMENTS.includes(name)) {
|
|
166
|
+
collectShape(result, element, name, matrix, location, group);
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
function spreadPlacements(spread, location) {
|
|
172
|
+
const result = {
|
|
173
|
+
frames: [],
|
|
174
|
+
polygons: [],
|
|
175
|
+
links: [],
|
|
176
|
+
unmapped: []
|
|
177
|
+
};
|
|
178
|
+
let sequence = 0;
|
|
179
|
+
const pending = [...spread.children].reverse().map((node) => ({
|
|
180
|
+
node,
|
|
181
|
+
matrix: IDENTITY,
|
|
182
|
+
group: void 0
|
|
183
|
+
}));
|
|
184
|
+
while (pending.length > 0) {
|
|
185
|
+
const current = pending.pop();
|
|
186
|
+
if (!current || !isTag(current.node)) continue;
|
|
187
|
+
const name = localName(current.node.name);
|
|
188
|
+
if (name === "Page" || name === "Spread" || name === "MasterSpread") continue;
|
|
189
|
+
const matrix = [
|
|
190
|
+
"Group",
|
|
191
|
+
"TextFrame",
|
|
192
|
+
...SHAPE_ELEMENTS
|
|
193
|
+
].includes(name) ? compose(current.matrix, transform(current.node)) : current.matrix;
|
|
194
|
+
const itemLocation = `${location}#${name}[${++sequence}]`;
|
|
195
|
+
const group = name === "Group" ? current.group ?? attribute(current.node, "Self") ?? itemLocation : current.group;
|
|
196
|
+
if (collectTerminal(result, current.node, name, matrix, itemLocation, group)) continue;
|
|
197
|
+
for (let index = current.node.children.length - 1; index >= 0; index -= 1) pending.push({
|
|
198
|
+
node: current.node.children[index],
|
|
199
|
+
matrix,
|
|
200
|
+
group
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
205
|
+
const relativeBounds = (value, area) => ({
|
|
206
|
+
left: value.left - area.left,
|
|
207
|
+
top: value.top - area.top,
|
|
208
|
+
right: value.right - area.left,
|
|
209
|
+
bottom: value.bottom - area.top
|
|
210
|
+
});
|
|
211
|
+
function relativePolygon(polygon, area) {
|
|
212
|
+
const relative = ([x, y]) => [x - area.left, y - area.top];
|
|
213
|
+
return {
|
|
214
|
+
...polygon,
|
|
215
|
+
paths: polygon.paths.map((path) => ({
|
|
216
|
+
...path,
|
|
217
|
+
points: path.points.map((point) => ({
|
|
218
|
+
anchor: relative(point.anchor),
|
|
219
|
+
left: relative(point.left),
|
|
220
|
+
right: relative(point.right)
|
|
221
|
+
}))
|
|
222
|
+
}))
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function placeFrames(frames, pageIndex, areas, pages) {
|
|
226
|
+
for (const frame of frames) {
|
|
227
|
+
const index = pageIndex(frame.center);
|
|
228
|
+
const area = areas[index];
|
|
229
|
+
const page = pages[index];
|
|
230
|
+
if (!area || !page || !frame.bounds) continue;
|
|
231
|
+
page.bodyFrames = [...page.bodyFrames ?? [], {
|
|
232
|
+
story: frame.story,
|
|
233
|
+
bounds: relativeBounds(frame.bounds, area),
|
|
234
|
+
location: frame.location
|
|
235
|
+
}];
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function placeArt(polygons, pageIndex, areas, pages) {
|
|
239
|
+
const groups = /* @__PURE__ */ new Map();
|
|
240
|
+
for (const item of polygons) {
|
|
241
|
+
const index = pageIndex(item.center);
|
|
242
|
+
const area = areas[index];
|
|
243
|
+
if (!area || !pages[index]) continue;
|
|
244
|
+
const group = groups.get(`${index}:${item.group}`) ?? {
|
|
245
|
+
index,
|
|
246
|
+
polygons: [],
|
|
247
|
+
boxes: [],
|
|
248
|
+
location: item.polygon.location
|
|
249
|
+
};
|
|
250
|
+
group.polygons.push(relativePolygon(item.polygon, area));
|
|
251
|
+
group.boxes.push(relativeBounds(item.bounds, area));
|
|
252
|
+
groups.set(`${index}:${item.group}`, group);
|
|
253
|
+
}
|
|
254
|
+
for (const group of groups.values()) {
|
|
255
|
+
const page = pages[group.index];
|
|
256
|
+
if (!page) continue;
|
|
257
|
+
const box = {
|
|
258
|
+
left: Math.min(...group.boxes.map((item) => item.left)),
|
|
259
|
+
top: Math.min(...group.boxes.map((item) => item.top)),
|
|
260
|
+
right: Math.max(...group.boxes.map((item) => item.right)),
|
|
261
|
+
bottom: Math.max(...group.boxes.map((item) => item.bottom))
|
|
262
|
+
};
|
|
263
|
+
page.art = [...page.art ?? [], {
|
|
264
|
+
bounds: box,
|
|
265
|
+
polygons: group.polygons,
|
|
266
|
+
location: group.location
|
|
267
|
+
}];
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
function attachPlacements(placements, pageElements, pages, report) {
|
|
271
|
+
const pageAreas = pageElements.map(pageArea);
|
|
272
|
+
const areas = pageAreas.map((points) => points ? placementBounds(points) : void 0);
|
|
273
|
+
const pageIndex = (center) => center ? pageAreas.findIndex((area) => area !== void 0 && contains(area, center)) : -1;
|
|
274
|
+
placeFrames(placements.frames, pageIndex, areas, pages);
|
|
275
|
+
placeArt(placements.polygons, pageIndex, areas, pages);
|
|
276
|
+
for (const link of placements.links) {
|
|
277
|
+
const index = pageIndex(link.center);
|
|
278
|
+
const area = areas[index];
|
|
279
|
+
const page = pages[index];
|
|
280
|
+
if (area && page && link.bounds) page.links = [...page.links ?? [], {
|
|
281
|
+
...link,
|
|
282
|
+
bounds: relativeBounds(link.bounds, area)
|
|
283
|
+
}];
|
|
284
|
+
else {
|
|
285
|
+
report.link(link.uri, link.location);
|
|
286
|
+
report.add(report.unsupportedElements, "link", link.location);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
for (const item of placements.unmapped) {
|
|
290
|
+
const page = pages[pageIndex(item.center)];
|
|
291
|
+
if (page) page.unmapped = [...page.unmapped ?? [], {
|
|
292
|
+
name: item.name,
|
|
293
|
+
location: item.location
|
|
294
|
+
}];
|
|
295
|
+
else report.add(report.unsupportedElements, item.name, item.location);
|
|
296
|
+
}
|
|
297
|
+
for (const item of placements.polygons) if (pageIndex(item.center) < 0) report.add(report.unsupportedElements, "polygon art group", item.polygon.location);
|
|
298
|
+
}
|
|
299
|
+
//#endregion
|
|
300
|
+
export { attachPlacements, contains, pageArea, placementBounds, spreadPlacements };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { MM_PER_PT } from "@segnavia/format";
|
|
2
|
+
//#region src/positioned-box.ts
|
|
3
|
+
function formatRoundedNumber(value, digits = 2) {
|
|
4
|
+
const rounded = Number(value.toFixed(digits));
|
|
5
|
+
return String(Object.is(rounded, -0) ? 0 : rounded);
|
|
6
|
+
}
|
|
7
|
+
function positionedBoxStyle(bounds, options) {
|
|
8
|
+
const scale = options.unit === "mm" ? MM_PER_PT : 1;
|
|
9
|
+
const digits = options.unit === "mm" ? 6 : 2;
|
|
10
|
+
const value = (points) => `${formatRoundedNumber(points * scale, digits)}${options.unit}`;
|
|
11
|
+
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
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { formatRoundedNumber, positionedBoxStyle };
|
package/dist/report.d.ts
CHANGED
|
@@ -10,6 +10,10 @@ export interface IdmlMixedMasterChapter {
|
|
|
10
10
|
masters: string[];
|
|
11
11
|
chosen: string;
|
|
12
12
|
}
|
|
13
|
+
export interface IdmlLinkUse {
|
|
14
|
+
uri: string;
|
|
15
|
+
locations: string[];
|
|
16
|
+
}
|
|
13
17
|
export interface IdmlReportData {
|
|
14
18
|
unsupportedElements: Record<string, number>;
|
|
15
19
|
unsupportedStyleProperties: Record<string, number>;
|
|
@@ -19,6 +23,9 @@ export interface IdmlReportData {
|
|
|
19
23
|
spotColors: Record<string, number>;
|
|
20
24
|
locations: Record<string, string[]>;
|
|
21
25
|
fonts: IdmlFontUse[];
|
|
26
|
+
unmappedFonts: IdmlFontUse[];
|
|
27
|
+
links: IdmlLinkUse[];
|
|
28
|
+
strippedAssetContent: Record<string, string[]>;
|
|
22
29
|
mixedMasterChapters: IdmlMixedMasterChapter[];
|
|
23
30
|
}
|
|
24
31
|
//#endregion
|
package/dist/report.js
CHANGED
|
@@ -7,9 +7,15 @@ const BUCKET_NAMES = {
|
|
|
7
7
|
unsupportedLanguages: "language",
|
|
8
8
|
spotColors: "spot"
|
|
9
9
|
};
|
|
10
|
+
function fontUseKey(appliedFont, fontStyle) {
|
|
11
|
+
return `${appliedFont}\u0000${fontStyle ?? ""}`;
|
|
12
|
+
}
|
|
10
13
|
var ReportCollector = class {
|
|
11
14
|
locations = /* @__PURE__ */ new Map();
|
|
12
15
|
fonts = /* @__PURE__ */ new Map();
|
|
16
|
+
unmappedFonts = /* @__PURE__ */ new Map();
|
|
17
|
+
links = /* @__PURE__ */ new Map();
|
|
18
|
+
strippedAssetContent = /* @__PURE__ */ new Map();
|
|
13
19
|
mixedMasterChapters;
|
|
14
20
|
buckets = Object.fromEntries(Object.keys(BUCKET_NAMES).map((name) => [name, /* @__PURE__ */ new Map()]));
|
|
15
21
|
get unsupportedElements() {
|
|
@@ -34,7 +40,13 @@ var ReportCollector = class {
|
|
|
34
40
|
this.mixedMasterChapters = [...seed?.mixedMasterChapters ?? []];
|
|
35
41
|
for (const name of Object.keys(BUCKET_NAMES)) for (const [key, count] of Object.entries(seed?.[name] ?? {})) this.buckets[name].set(key, count);
|
|
36
42
|
for (const [key, locations] of Object.entries(seed?.locations ?? {})) this.locations.set(key, [...locations]);
|
|
43
|
+
this.seedKeyedRecords(seed);
|
|
44
|
+
}
|
|
45
|
+
seedKeyedRecords(seed) {
|
|
37
46
|
for (const font of seed?.fonts ?? []) this.font(font.appliedFont, font.fontStyle, font.locations[0] ?? "Resources/Styles.xml");
|
|
47
|
+
for (const font of seed?.unmappedFonts ?? []) for (const location of font.locations) this.unmappedFont(font.appliedFont, font.fontStyle, location);
|
|
48
|
+
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);
|
|
38
50
|
}
|
|
39
51
|
add(bucket, name, location, count = 1) {
|
|
40
52
|
bucket.set(name, (bucket.get(name) ?? 0) + count);
|
|
@@ -50,14 +62,36 @@ var ReportCollector = class {
|
|
|
50
62
|
}
|
|
51
63
|
font(appliedFont, fontStyle, location) {
|
|
52
64
|
if (!appliedFont) return;
|
|
53
|
-
|
|
54
|
-
|
|
65
|
+
this.record(this.fonts, appliedFont, fontStyle, location);
|
|
66
|
+
}
|
|
67
|
+
unmappedFont(appliedFont, fontStyle, location) {
|
|
68
|
+
this.record(this.unmappedFonts, appliedFont, fontStyle, location);
|
|
69
|
+
}
|
|
70
|
+
link(uri, location) {
|
|
71
|
+
const found = this.links.get(uri) ?? {
|
|
72
|
+
uri,
|
|
73
|
+
locations: []
|
|
74
|
+
};
|
|
75
|
+
this.recordLocation(found.locations, location);
|
|
76
|
+
this.links.set(uri, found);
|
|
77
|
+
}
|
|
78
|
+
strippedAsset(uri, finding) {
|
|
79
|
+
const found = this.strippedAssetContent.get(uri) ?? [];
|
|
80
|
+
this.recordLocation(found, finding);
|
|
81
|
+
this.strippedAssetContent.set(uri, found);
|
|
82
|
+
}
|
|
83
|
+
record(target, appliedFont, fontStyle, location) {
|
|
84
|
+
const key = fontUseKey(appliedFont, fontStyle);
|
|
85
|
+
const found = target.get(key) ?? {
|
|
55
86
|
appliedFont,
|
|
56
87
|
...fontStyle ? { fontStyle } : {},
|
|
57
88
|
locations: []
|
|
58
89
|
};
|
|
59
|
-
|
|
60
|
-
|
|
90
|
+
this.recordLocation(found.locations, location);
|
|
91
|
+
target.set(key, found);
|
|
92
|
+
}
|
|
93
|
+
recordLocation(target, location) {
|
|
94
|
+
if (target.length < 5 && !target.includes(location)) target.push(location);
|
|
61
95
|
}
|
|
62
96
|
data() {
|
|
63
97
|
const record = (values) => Object.fromEntries([...values].sort(([a], [b]) => a.localeCompare(b)));
|
|
@@ -65,6 +99,9 @@ var ReportCollector = class {
|
|
|
65
99
|
...Object.fromEntries(Object.keys(BUCKET_NAMES).map((name) => [name, record(this.buckets[name])])),
|
|
66
100
|
locations: Object.fromEntries([...this.locations].sort(([a], [b]) => a.localeCompare(b))),
|
|
67
101
|
fonts: [...this.fonts.values()],
|
|
102
|
+
unmappedFonts: [...this.unmappedFonts.values()],
|
|
103
|
+
links: [...this.links.values()],
|
|
104
|
+
strippedAssetContent: Object.fromEntries([...this.strippedAssetContent].sort(([a], [b]) => a.localeCompare(b))),
|
|
68
105
|
mixedMasterChapters: [...this.mixedMasterChapters]
|
|
69
106
|
};
|
|
70
107
|
}
|
|
@@ -75,4 +112,4 @@ var ReportCollector = class {
|
|
|
75
112
|
}
|
|
76
113
|
};
|
|
77
114
|
//#endregion
|
|
78
|
-
export { ReportCollector };
|
|
115
|
+
export { ReportCollector, fontUseKey };
|
package/dist/stories.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BOOK_FURNITURE, MOTIF_ATTRIBUTE, motifFieldAttribute, resolveDirection } from "@segnavia/format";
|
|
2
|
+
import { Element, Text } from "domhandler";
|
|
2
3
|
import { BOOK_COLUMN_MAX, BOOK_COLUMN_MIN } from "@segnavia/html";
|
|
3
4
|
import serialize from "dom-serializer";
|
|
4
|
-
import { Element, Text } from "domhandler";
|
|
5
5
|
//#region src/stories.ts
|
|
6
6
|
const OMITTED = /* @__PURE__ */ new Set([
|
|
7
7
|
"TextFrame",
|
package/dist/styles.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { idmlDirection, localName } from "./xml.js";
|
|
2
|
+
import { resolveSwatchColor } from "./swatch-color.js";
|
|
2
3
|
import { stylePropertyMapping } from "./style-mappings.js";
|
|
3
4
|
import { reportableStyleProperties } from "./style-property-report.js";
|
|
4
5
|
import { isTag } from "domhandler";
|
|
@@ -84,19 +85,28 @@ function textDeclarations(values, mapped, output) {
|
|
|
84
85
|
if (mapped.has("Capitalization") && values.Capitalization === "AllCaps") output.push("text-transform:uppercase");
|
|
85
86
|
if (mapped.has("Underline") && (values.Underline === "true" || values.Underline === "Underline")) output.push("text-decoration-line:underline");
|
|
86
87
|
}
|
|
88
|
+
function resolvedFontProperties(changed, resolved) {
|
|
89
|
+
if (changed.AppliedFont === void 0 && changed.FontStyle === void 0) return {};
|
|
90
|
+
return {
|
|
91
|
+
...resolved.AppliedFont === void 0 ? {} : { AppliedFont: resolved.AppliedFont },
|
|
92
|
+
...resolved.FontStyle === void 0 ? {} : { FontStyle: resolved.FontStyle }
|
|
93
|
+
};
|
|
94
|
+
}
|
|
87
95
|
var StyleMapper = class {
|
|
88
96
|
swatches;
|
|
89
97
|
report;
|
|
90
98
|
bookDirection;
|
|
99
|
+
fonts;
|
|
91
100
|
byId;
|
|
92
101
|
classes = /* @__PURE__ */ new Map();
|
|
93
102
|
used = /* @__PURE__ */ new Set();
|
|
94
103
|
overrides = /* @__PURE__ */ new Map();
|
|
95
104
|
reportedStyleProperties = /* @__PURE__ */ new Set();
|
|
96
|
-
constructor(styles, swatches, report, bookDirection) {
|
|
105
|
+
constructor(styles, swatches, report, bookDirection, fonts) {
|
|
97
106
|
this.swatches = swatches;
|
|
98
107
|
this.report = report;
|
|
99
108
|
this.bookDirection = bookDirection;
|
|
109
|
+
this.fonts = fonts;
|
|
100
110
|
this.byId = new Map(styles.map((style) => [style.id, style]));
|
|
101
111
|
const taken = /* @__PURE__ */ new Set();
|
|
102
112
|
for (const style of styles) {
|
|
@@ -140,6 +150,7 @@ var StyleMapper = class {
|
|
|
140
150
|
for (const name of reportableStyleProperties(changed, resolved)) this.report.add(this.report.unsupportedOverrideProperties, name, location);
|
|
141
151
|
if (changed.AppliedFont !== void 0 || changed.FontStyle !== void 0) this.report.font(resolved.AppliedFont, resolved.FontStyle, location);
|
|
142
152
|
const cssProperties = Object.fromEntries(Object.entries(changed).filter(([name]) => stylePropertyMapping(name)?.kind === "css"));
|
|
153
|
+
Object.assign(cssProperties, resolvedFontProperties(changed, resolved));
|
|
143
154
|
if (styleDirection && styleDirection !== directionValue) {
|
|
144
155
|
for (const name of ["LeftIndent", "RightIndent"]) cssProperties[name] = base[name] ?? "0";
|
|
145
156
|
for (const name of ["Justification"]) if (base[name] !== void 0 && cssProperties[name] === void 0) cssProperties[name] = base[name];
|
|
@@ -152,7 +163,8 @@ var StyleMapper = class {
|
|
|
152
163
|
this.overrides.set(key, {
|
|
153
164
|
name,
|
|
154
165
|
properties: cssProperties,
|
|
155
|
-
direction: directionValue
|
|
166
|
+
direction: directionValue,
|
|
167
|
+
location
|
|
156
168
|
});
|
|
157
169
|
return name;
|
|
158
170
|
}
|
|
@@ -186,13 +198,14 @@ var StyleMapper = class {
|
|
|
186
198
|
rules.push(`.${name}{${declarations}}`);
|
|
187
199
|
}
|
|
188
200
|
for (const value of this.overrides.values()) {
|
|
189
|
-
const declarations = this.declarations(value.properties, value.direction, value.
|
|
201
|
+
const declarations = this.declarations(value.properties, value.direction, value.location, this.report.unsupportedOverrideProperties);
|
|
190
202
|
if (declarations) rules.push(`.${value.name}{${declarations}}`);
|
|
191
203
|
}
|
|
192
204
|
return rules.join("");
|
|
193
205
|
}
|
|
194
206
|
declarations(values, dir, location, unsupported = this.report.unsupportedStyleProperties) {
|
|
195
207
|
const output = [];
|
|
208
|
+
output.push(...this.fonts?.declarations(values.AppliedFont, values.FontStyle, location) ?? []);
|
|
196
209
|
const mapped = new Set(Object.keys(values).filter((name) => stylePropertyMapping(name)?.kind === "css"));
|
|
197
210
|
const pt = (name, property) => {
|
|
198
211
|
if (!mapped.has(name)) return;
|
|
@@ -215,7 +228,7 @@ var StyleMapper = class {
|
|
|
215
228
|
if (mapped.has("Justification")) this.justification(values.Justification, dir, output, location, unsupported);
|
|
216
229
|
if (hasCssMapping(mapped, "keeps")) this.keeps(values, output);
|
|
217
230
|
if (mapped.has("StartParagraph") && values.StartParagraph && values.StartParagraph !== "Anywhere") this.report.add(unsupported, `StartParagraph:${values.StartParagraph}`, location);
|
|
218
|
-
const color = mapped.has("FillColor") ?
|
|
231
|
+
const color = mapped.has("FillColor") ? resolveSwatchColor(values.FillColor, this.swatches, this.report, location, "text") : void 0;
|
|
219
232
|
if (color) output.push(`color:${color}`);
|
|
220
233
|
return output.join(";");
|
|
221
234
|
}
|
|
@@ -264,22 +277,6 @@ var StyleMapper = class {
|
|
|
264
277
|
if (Number.isInteger(last) && last > 0) output.push(`widows:${last}`);
|
|
265
278
|
}
|
|
266
279
|
}
|
|
267
|
-
color(id, location) {
|
|
268
|
-
if (!id || id.endsWith("/None")) return void 0;
|
|
269
|
-
if (id.endsWith("/Black")) return "#000";
|
|
270
|
-
if (id.endsWith("/Paper")) return "#fff";
|
|
271
|
-
const swatch = this.swatches.find((value) => value.id === id || value.name === id);
|
|
272
|
-
if (!swatch) return void 0;
|
|
273
|
-
if (swatch.model === "spot") {
|
|
274
|
-
this.report.add(this.report.spotColors, swatch.name, location);
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
if (swatch.model === "rgb" && swatch.values?.length === 3) return `rgb(${swatch.values.join(" ")})`;
|
|
278
|
-
if (swatch.model === "cmyk" && swatch.values?.length === 4) {
|
|
279
|
-
const [c, m, y, k] = swatch.values.map((value) => value / 100);
|
|
280
|
-
return `rgb(${Math.round(255 * (1 - c) * (1 - k))} ${Math.round(255 * (1 - m) * (1 - k))} ${Math.round(255 * (1 - y) * (1 - k))})`;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
280
|
};
|
|
284
281
|
//#endregion
|
|
285
282
|
export { StyleMapper, parseStyles, parseSwatches };
|
package/dist/svg-path.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { formatRoundedNumber } from "./positioned-box.js";
|
|
2
|
+
import { resolveSwatchColor } from "./swatch-color.js";
|
|
3
|
+
//#region src/svg-path.ts
|
|
4
|
+
const escapeAttribute = (value) => value.replaceAll("&", "&").replaceAll("\"", """).replaceAll("<", "<").replaceAll(">", ">");
|
|
5
|
+
function point(value, origin) {
|
|
6
|
+
return `${formatRoundedNumber(value[0] - origin[0])} ${formatRoundedNumber(value[1] - origin[1])}`;
|
|
7
|
+
}
|
|
8
|
+
function pathData(path, origin) {
|
|
9
|
+
const first = path.points[0];
|
|
10
|
+
if (!first) return "";
|
|
11
|
+
const output = [`M${point(first.anchor, origin)}`];
|
|
12
|
+
for (let index = 1; index < path.points.length; index += 1) {
|
|
13
|
+
const previous = path.points[index - 1];
|
|
14
|
+
const current = path.points[index];
|
|
15
|
+
if (previous && current) output.push(`C${point(previous.right, origin)} ${point(current.left, origin)} ${point(current.anchor, origin)}`);
|
|
16
|
+
}
|
|
17
|
+
const last = path.points.at(-1);
|
|
18
|
+
if (!path.open && last && path.points.length > 1) output.push(`C${point(last.right, origin)} ${point(first.left, origin)} ${point(first.anchor, origin)}Z`);
|
|
19
|
+
return output.join("");
|
|
20
|
+
}
|
|
21
|
+
function artSvg(art, swatches, positionStyle, report) {
|
|
22
|
+
const width = art.bounds.right - art.bounds.left;
|
|
23
|
+
const height = art.bounds.bottom - art.bounds.top;
|
|
24
|
+
const origin = [art.bounds.left, art.bounds.top];
|
|
25
|
+
const paths = art.polygons.flatMap((polygon) => {
|
|
26
|
+
if (polygon.stroke) report.add(report.unsupportedStyleProperties, `StrokeColor:${polygon.stroke.color}`, polygon.location);
|
|
27
|
+
const fill = resolveSwatchColor(polygon.fill, swatches, report, polygon.location, "fill") ?? "currentColor";
|
|
28
|
+
return polygon.paths.map((path) => `<path d="${escapeAttribute(pathData(path, origin))}" fill="${escapeAttribute(fill)}"/>`);
|
|
29
|
+
});
|
|
30
|
+
return `<svg style="${escapeAttribute(positionStyle)}" viewBox="0 0 ${formatRoundedNumber(width)} ${formatRoundedNumber(height)}" width="${formatRoundedNumber(width)}pt" height="${formatRoundedNumber(height)}pt" xmlns="http://www.w3.org/2000/svg">${paths.join("")}</svg>`;
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { artSvg };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/swatch-color.ts
|
|
2
|
+
const colorComponent = (value) => Math.round(value + 1e-9);
|
|
3
|
+
function unresolved(id, location, use, report) {
|
|
4
|
+
report.add(report.unsupportedStyleProperties, `FillColor:${id}`, location);
|
|
5
|
+
return use === "fill" ? "currentColor" : void 0;
|
|
6
|
+
}
|
|
7
|
+
function resolveSwatchColor(id, swatches, report, location, use) {
|
|
8
|
+
if (!id || id.endsWith("/None") || id === "None") return use === "fill" ? "none" : void 0;
|
|
9
|
+
if (id.endsWith("/Black") || id === "Black") return "#000";
|
|
10
|
+
if (id.endsWith("/Paper") || id === "Paper") return "#fff";
|
|
11
|
+
const swatch = swatches.find((value) => value.id === id || value.name === id);
|
|
12
|
+
if (!swatch) return unresolved(id, location, use, report);
|
|
13
|
+
if (swatch.model === "spot") {
|
|
14
|
+
report.add(report.spotColors, swatch.name, location);
|
|
15
|
+
return use === "fill" ? "currentColor" : void 0;
|
|
16
|
+
}
|
|
17
|
+
if (swatch.model === "rgb" && swatch.values?.length === 3) return `rgb(${swatch.values.map(colorComponent).join(" ")})`;
|
|
18
|
+
if (swatch.model === "cmyk" && swatch.values?.length === 4) {
|
|
19
|
+
const [c, m, y, k] = swatch.values.map((value) => value / 100);
|
|
20
|
+
return `rgb(${colorComponent(255 * (1 - c) * (1 - k))} ${colorComponent(255 * (1 - m) * (1 - k))} ${colorComponent(255 * (1 - y) * (1 - k))})`;
|
|
21
|
+
}
|
|
22
|
+
return unresolved(id, location, use, report);
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { resolveSwatchColor };
|
package/dist/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FontReferenceInput } from "@segnavia/format";
|
|
1
2
|
//#region src/types.d.ts
|
|
2
3
|
export interface IdmlTextNode {
|
|
3
4
|
name: string;
|
|
@@ -14,6 +15,60 @@ export interface IdmlStyle {
|
|
|
14
15
|
properties: Record<string, string>;
|
|
15
16
|
location: string;
|
|
16
17
|
}
|
|
18
|
+
export interface IdmlFont {
|
|
19
|
+
family: string;
|
|
20
|
+
style?: string;
|
|
21
|
+
postscriptName?: string;
|
|
22
|
+
}
|
|
23
|
+
export type IdmlFontMap = (font: IdmlFont) => FontReferenceInput | undefined;
|
|
24
|
+
export type IdmlAssetMap = (uri: string) => string | undefined;
|
|
25
|
+
export interface IdmlImportOptions {
|
|
26
|
+
font?: IdmlFontMap;
|
|
27
|
+
asset?: IdmlAssetMap;
|
|
28
|
+
}
|
|
29
|
+
export type IdmlPoint = [number, number];
|
|
30
|
+
export interface IdmlBounds {
|
|
31
|
+
left: number;
|
|
32
|
+
top: number;
|
|
33
|
+
right: number;
|
|
34
|
+
bottom: number;
|
|
35
|
+
}
|
|
36
|
+
export interface IdmlPathPoint {
|
|
37
|
+
anchor: IdmlPoint;
|
|
38
|
+
left: IdmlPoint;
|
|
39
|
+
right: IdmlPoint;
|
|
40
|
+
}
|
|
41
|
+
export interface IdmlPolygon {
|
|
42
|
+
paths: {
|
|
43
|
+
open: boolean;
|
|
44
|
+
points: IdmlPathPoint[];
|
|
45
|
+
}[];
|
|
46
|
+
fill: string;
|
|
47
|
+
stroke?: {
|
|
48
|
+
color: string;
|
|
49
|
+
weight: number;
|
|
50
|
+
};
|
|
51
|
+
location: string;
|
|
52
|
+
}
|
|
53
|
+
export interface IdmlArtPlacement {
|
|
54
|
+
bounds: IdmlBounds;
|
|
55
|
+
polygons: IdmlPolygon[];
|
|
56
|
+
location: string;
|
|
57
|
+
}
|
|
58
|
+
export interface IdmlFramePlacement {
|
|
59
|
+
bounds: IdmlBounds;
|
|
60
|
+
story: string;
|
|
61
|
+
location: string;
|
|
62
|
+
}
|
|
63
|
+
export interface IdmlLinkPlacement {
|
|
64
|
+
bounds: IdmlBounds;
|
|
65
|
+
uri: string;
|
|
66
|
+
location: string;
|
|
67
|
+
}
|
|
68
|
+
export interface IdmlUnmappedPlacement {
|
|
69
|
+
name: string;
|
|
70
|
+
location: string;
|
|
71
|
+
}
|
|
17
72
|
export interface IdmlSwatch {
|
|
18
73
|
id: string;
|
|
19
74
|
name: string;
|
|
@@ -58,6 +113,10 @@ export interface IdmlPage {
|
|
|
58
113
|
appliedMaster?: string;
|
|
59
114
|
stories: string[];
|
|
60
115
|
side?: 'left' | 'right' | 'single';
|
|
116
|
+
bodyFrames?: IdmlFramePlacement[];
|
|
117
|
+
art?: IdmlArtPlacement[];
|
|
118
|
+
links?: IdmlLinkPlacement[];
|
|
119
|
+
unmapped?: IdmlUnmappedPlacement[];
|
|
61
120
|
}
|
|
62
121
|
export interface IdmlSpread {
|
|
63
122
|
id: string;
|