docxodus 12.6.4 → 13.0.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/README.md +8 -5
- package/dist/canonical.d.ts.map +1 -1
- package/dist/canonical.js +7 -3
- package/dist/canonical.js.map +1 -1
- package/dist/conversion-options.d.ts +16 -0
- package/dist/conversion-options.d.ts.map +1 -0
- package/dist/conversion-options.js +31 -0
- package/dist/conversion-options.js.map +1 -0
- package/dist/core.d.ts +17 -9
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +23 -24
- package/dist/core.js.map +1 -1
- package/dist/docxodus.worker.js +2 -1073
- package/dist/docxodus.worker.js.map +7 -1
- package/dist/editor.bundle.js +35 -14296
- package/dist/editor.bundle.js.map +7 -0
- package/dist/editor.d.ts +33 -2
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +6739 -4773
- package/dist/editor.js.map +7 -1
- package/dist/embed.bundle.js +159 -18703
- package/dist/embed.bundle.js.map +7 -0
- package/dist/embed.d.ts +1 -1
- package/dist/embed.iife.js +37 -18595
- package/dist/embed.iife.js.map +7 -0
- package/dist/embed.js +1 -1
- package/dist/export-assets.json +27 -27
- package/dist/export-browser.bundle.js +1388 -477
- package/dist/export-browser.d.ts +20 -0
- package/dist/export-browser.d.ts.map +1 -1
- package/dist/export-browser.js +132 -37
- package/dist/export-browser.js.map +1 -1
- package/dist/font-runtime.d.ts.map +1 -1
- package/dist/font-runtime.js +54 -0
- package/dist/font-runtime.js.map +1 -1
- package/dist/line-metrics.d.ts +60 -0
- package/dist/line-metrics.d.ts.map +1 -0
- package/dist/line-metrics.js +343 -0
- package/dist/line-metrics.js.map +1 -0
- package/dist/pagination.bundle.js +3 -3214
- package/dist/pagination.bundle.js.map +7 -0
- package/dist/pagination.d.ts +86 -9
- package/dist/pagination.d.ts.map +1 -1
- package/dist/pagination.js +358 -55
- package/dist/pagination.js.map +1 -1
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +63 -12
- package/dist/react.js.map +1 -1
- package/dist/render-report-v2.schema.json +29 -0
- package/dist/session.bundle.js +2 -2013
- package/dist/session.bundle.js.map +7 -0
- package/dist/session.d.ts +14 -5
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +62 -29
- package/dist/session.js.map +1 -1
- package/dist/types.d.ts +137 -21
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +14 -0
- package/dist/types.js.map +1 -1
- package/dist/wasm/_framework/Docxodus.wasm +0 -0
- package/dist/wasm/_framework/Docxodus.wasm.br +0 -0
- package/dist/wasm/_framework/DocxodusWasm.wasm +0 -0
- package/dist/wasm/_framework/DocxodusWasm.wasm.br +0 -0
- package/dist/wasm/_framework/System.Collections.wasm +0 -0
- package/dist/wasm/_framework/System.Collections.wasm.br +0 -0
- package/dist/wasm/_framework/System.IO.Compression.wasm +0 -0
- package/dist/wasm/_framework/System.IO.Compression.wasm.br +0 -0
- package/dist/wasm/_framework/System.Linq.wasm +0 -0
- package/dist/wasm/_framework/System.Linq.wasm.br +0 -0
- package/dist/wasm/_framework/System.Memory.wasm +0 -0
- package/dist/wasm/_framework/System.Memory.wasm.br +0 -0
- package/dist/wasm/_framework/System.Private.CoreLib.wasm +0 -0
- package/dist/wasm/_framework/System.Private.CoreLib.wasm.br +0 -0
- package/dist/wasm/_framework/System.Private.Xml.Linq.wasm +0 -0
- package/dist/wasm/_framework/System.Private.Xml.Linq.wasm.br +0 -0
- package/dist/wasm/_framework/System.Text.RegularExpressions.wasm +0 -0
- package/dist/wasm/_framework/System.Text.RegularExpressions.wasm.br +0 -0
- package/dist/wasm/_framework/dotnet.boot.js +11 -11
- package/dist/wasm/_framework/dotnet.boot.js.br +0 -0
- package/dist/wasm/_framework/dotnet.native.wasm +0 -0
- package/dist/wasm/_framework/dotnet.native.wasm.br +0 -0
- package/dist/word-text.d.ts +14 -0
- package/dist/word-text.d.ts.map +1 -0
- package/dist/word-text.js +377 -0
- package/dist/word-text.js.map +1 -0
- package/dist/worker-proxy.bundle.js +2 -536
- package/dist/worker-proxy.bundle.js.map +7 -0
- package/dist/worker-proxy.d.ts +14 -1
- package/dist/worker-proxy.d.ts.map +1 -1
- package/dist/worker-proxy.js +357 -339
- package/dist/worker-proxy.js.map +1 -1
- package/package.json +13 -12
|
@@ -91,10 +91,11 @@ function canonicalValue(value) {
|
|
|
91
91
|
if (prototype !== Object.prototype && prototype !== null) {
|
|
92
92
|
throw new TypeError("Canonical JSON supports only plain objects");
|
|
93
93
|
}
|
|
94
|
-
const
|
|
95
|
-
|
|
94
|
+
const record = value;
|
|
95
|
+
const result = /* @__PURE__ */ Object.create(null);
|
|
96
|
+
for (const key of Object.keys(record).sort()) {
|
|
96
97
|
assertWellFormedUnicode(key);
|
|
97
|
-
const member =
|
|
98
|
+
const member = record[key];
|
|
98
99
|
if (member !== void 0) result[key] = canonicalValue(member);
|
|
99
100
|
}
|
|
100
101
|
return result;
|
|
@@ -105,6 +106,548 @@ function canonicalJson(value) {
|
|
|
105
106
|
return JSON.stringify(canonicalValue(value));
|
|
106
107
|
}
|
|
107
108
|
|
|
109
|
+
// src/line-metrics.ts
|
|
110
|
+
var PROBE_FONT_SIZE_PX = 1e3;
|
|
111
|
+
var LATIN_PROBE = "Hg";
|
|
112
|
+
var MAX_PROBE_CHARACTERS = 256;
|
|
113
|
+
function probeText(element) {
|
|
114
|
+
const characters = /* @__PURE__ */ new Set();
|
|
115
|
+
for (const node of Array.from(element.childNodes)) {
|
|
116
|
+
if (node.nodeType !== 3) continue;
|
|
117
|
+
for (const character of node.textContent ?? "") {
|
|
118
|
+
if (/\s/.test(character)) continue;
|
|
119
|
+
characters.add(character);
|
|
120
|
+
if (characters.size >= MAX_PROBE_CHARACTERS) break;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const beyondLatin = Array.from(characters).filter((character) => character.codePointAt(0) > 591);
|
|
124
|
+
return beyondLatin.length === 0 ? LATIN_PROBE : LATIN_PROBE + beyondLatin.sort().join("");
|
|
125
|
+
}
|
|
126
|
+
function naturalLineHeightRatios(document2) {
|
|
127
|
+
const ratios = /* @__PURE__ */ new Map();
|
|
128
|
+
return (style, text) => {
|
|
129
|
+
const key = `${style.fontStyle}|${style.fontWeight}|${style.fontStretch}|${style.fontFamily}|${text}`;
|
|
130
|
+
let ratio = ratios.get(key);
|
|
131
|
+
if (ratio === void 0) {
|
|
132
|
+
const probe = document2.createElement("span");
|
|
133
|
+
probe.setAttribute("aria-hidden", "true");
|
|
134
|
+
probe.style.cssText = "position:absolute;left:-100000px;top:0;visibility:hidden;display:inline-block;white-space:nowrap;line-height:normal;margin:0;padding:0;border:0;";
|
|
135
|
+
probe.style.fontFamily = style.fontFamily;
|
|
136
|
+
probe.style.fontStyle = style.fontStyle;
|
|
137
|
+
probe.style.fontWeight = style.fontWeight;
|
|
138
|
+
probe.style.fontStretch = style.fontStretch;
|
|
139
|
+
probe.style.fontSize = `${PROBE_FONT_SIZE_PX}px`;
|
|
140
|
+
probe.textContent = text;
|
|
141
|
+
(document2.body ?? document2.documentElement).appendChild(probe);
|
|
142
|
+
ratio = probe.getBoundingClientRect().height / PROBE_FONT_SIZE_PX;
|
|
143
|
+
probe.remove();
|
|
144
|
+
ratios.set(key, ratio);
|
|
145
|
+
}
|
|
146
|
+
return ratio;
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function applyUnroundedNormalLineHeights(root) {
|
|
150
|
+
const document2 = root.ownerDocument;
|
|
151
|
+
const view = document2.defaultView;
|
|
152
|
+
if (!view) return 0;
|
|
153
|
+
const ratioFor = naturalLineHeightRatios(document2);
|
|
154
|
+
const targets = [];
|
|
155
|
+
for (const element of [root, ...Array.from(root.querySelectorAll("*"))]) {
|
|
156
|
+
if (!(element instanceof view.HTMLElement)) continue;
|
|
157
|
+
const style = view.getComputedStyle(element);
|
|
158
|
+
if (style.lineHeight !== "normal") continue;
|
|
159
|
+
const fontSize = Number.parseFloat(style.fontSize);
|
|
160
|
+
const ratio = ratioFor(style, probeText(element));
|
|
161
|
+
if (!(fontSize > 0) || !(ratio > 0)) continue;
|
|
162
|
+
targets.push({ element, lineHeight: ratio * fontSize });
|
|
163
|
+
}
|
|
164
|
+
for (const { element, lineHeight } of targets) {
|
|
165
|
+
element.style.setProperty("line-height", `${lineHeight.toFixed(4)}px`);
|
|
166
|
+
}
|
|
167
|
+
return targets.length;
|
|
168
|
+
}
|
|
169
|
+
var PARAGRAPH_SELECTOR = "p, h1, h2, h3, h4, h5, h6";
|
|
170
|
+
function baselineOffsets(document2) {
|
|
171
|
+
const offsets = /* @__PURE__ */ new Map();
|
|
172
|
+
const host = document2.body ?? document2.documentElement;
|
|
173
|
+
return (paragraph, child) => {
|
|
174
|
+
const key = [
|
|
175
|
+
paragraph.fontStyle,
|
|
176
|
+
paragraph.fontWeight,
|
|
177
|
+
paragraph.fontStretch,
|
|
178
|
+
paragraph.fontFamily,
|
|
179
|
+
paragraph.fontSize,
|
|
180
|
+
paragraph.lineHeight,
|
|
181
|
+
child.fontStyle,
|
|
182
|
+
child.fontWeight,
|
|
183
|
+
child.fontStretch,
|
|
184
|
+
child.fontFamily,
|
|
185
|
+
child.fontSize,
|
|
186
|
+
child.lineHeight,
|
|
187
|
+
child.verticalAlign,
|
|
188
|
+
child.top
|
|
189
|
+
].join("|");
|
|
190
|
+
let offset = offsets.get(key);
|
|
191
|
+
if (offset === void 0) {
|
|
192
|
+
const probe = document2.createElement("div");
|
|
193
|
+
probe.setAttribute("aria-hidden", "true");
|
|
194
|
+
probe.style.cssText = "position:absolute;left:-100000px;top:0;visibility:hidden;width:max-content;margin:0;padding:0;border:0;white-space:nowrap;";
|
|
195
|
+
for (const property of ["font-family", "font-style", "font-weight", "font-stretch", "font-size", "line-height"])
|
|
196
|
+
probe.style.setProperty(property, paragraph.getPropertyValue(property));
|
|
197
|
+
const span = document2.createElement("span");
|
|
198
|
+
for (const property of [
|
|
199
|
+
"font-family",
|
|
200
|
+
"font-style",
|
|
201
|
+
"font-weight",
|
|
202
|
+
"font-stretch",
|
|
203
|
+
"font-size",
|
|
204
|
+
"line-height",
|
|
205
|
+
"vertical-align",
|
|
206
|
+
"position",
|
|
207
|
+
"top"
|
|
208
|
+
])
|
|
209
|
+
span.style.setProperty(property, child.getPropertyValue(property));
|
|
210
|
+
const marker = document2.createElement("span");
|
|
211
|
+
marker.style.cssText = "display:inline-block;width:0;height:0;vertical-align:baseline;margin:0;padding:0;border:0;";
|
|
212
|
+
span.append(marker, LATIN_PROBE);
|
|
213
|
+
probe.append(span);
|
|
214
|
+
host.appendChild(probe);
|
|
215
|
+
offset = marker.getBoundingClientRect().bottom - probe.getBoundingClientRect().top;
|
|
216
|
+
probe.remove();
|
|
217
|
+
offsets.set(key, offset);
|
|
218
|
+
}
|
|
219
|
+
return offset;
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function alignExactLineBaselines(root) {
|
|
223
|
+
const view = root.ownerDocument.defaultView;
|
|
224
|
+
if (!view) return;
|
|
225
|
+
const offsetFor = baselineOffsets(root.ownerDocument);
|
|
226
|
+
for (const run of Array.from(root.querySelectorAll("[data-docx-exact-run]"))) {
|
|
227
|
+
const paragraph = run.closest(PARAGRAPH_SELECTOR);
|
|
228
|
+
if (!paragraph || !movable(run, view)) continue;
|
|
229
|
+
const style = view.getComputedStyle(paragraph);
|
|
230
|
+
if (!style.getPropertyValue("--docx-exact-line-height")) continue;
|
|
231
|
+
const height = Number.parseFloat(style.lineHeight);
|
|
232
|
+
const runStyle = view.getComputedStyle(run);
|
|
233
|
+
if (!(height > 0) || runStyle.display !== "inline" || runStyle.verticalAlign !== "top") continue;
|
|
234
|
+
const top = Number.parseFloat(runStyle.top) || 0;
|
|
235
|
+
const offset = offsetFor(style, runStyle) - top;
|
|
236
|
+
const shift = height * 0.8 - offset;
|
|
237
|
+
if (!Number.isFinite(shift)) continue;
|
|
238
|
+
run.style.position = "relative";
|
|
239
|
+
run.style.top = `${(top + shift).toFixed(4)}px`;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
var EXACT_INK_ATTRIBUTE = "data-docx-exact-ink";
|
|
243
|
+
function preserveExactLineInk(content, topLimit, bottomLimit, paper) {
|
|
244
|
+
const view = content.ownerDocument.defaultView;
|
|
245
|
+
if (!view) return;
|
|
246
|
+
const band = content.getBoundingClientRect();
|
|
247
|
+
const scale = band.width / content.offsetWidth || 1;
|
|
248
|
+
let above = 0;
|
|
249
|
+
let below = 0;
|
|
250
|
+
for (const paragraph of Array.from(content.querySelectorAll(PARAGRAPH_SELECTOR))) {
|
|
251
|
+
if (!view.getComputedStyle(paragraph).getPropertyValue("--docx-exact-line-height")) continue;
|
|
252
|
+
const box = paragraph.getBoundingClientRect();
|
|
253
|
+
if (box.top < band.top - 0.5 || box.bottom > band.bottom + 0.5) continue;
|
|
254
|
+
if (!paragraph.querySelector("[data-docx-exact-run]") || paragraph.querySelector("img, svg, canvas, video, object, iframe"))
|
|
255
|
+
continue;
|
|
256
|
+
if (Array.from(paragraph.querySelectorAll("*")).some((element) => {
|
|
257
|
+
const style = view.getComputedStyle(element);
|
|
258
|
+
return style.display !== "inline" || !["static", "relative"].includes(style.position);
|
|
259
|
+
})) continue;
|
|
260
|
+
const range = content.ownerDocument.createRange();
|
|
261
|
+
range.selectNodeContents(paragraph);
|
|
262
|
+
const rects = Array.from(range.getClientRects()).filter((rect) => rect.width > 0 && rect.height > 0);
|
|
263
|
+
if (rects.length === 0) continue;
|
|
264
|
+
const top = Math.min(...rects.map((rect) => rect.top));
|
|
265
|
+
const bottom = Math.max(...rects.map((rect) => rect.bottom));
|
|
266
|
+
if (top < topLimit || bottom > bottomLimit) continue;
|
|
267
|
+
const extra = Math.max(0, band.top - top, bottom - band.bottom);
|
|
268
|
+
if (extra === 0) continue;
|
|
269
|
+
paragraph.setAttribute(EXACT_INK_ATTRIBUTE, "true");
|
|
270
|
+
above = Math.max(above, band.top - top);
|
|
271
|
+
below = Math.max(below, bottom - band.bottom);
|
|
272
|
+
}
|
|
273
|
+
if (above > 0 || below > 0) {
|
|
274
|
+
const up = Math.ceil(above / scale * 64) / 64;
|
|
275
|
+
const down = Math.ceil(below / scale * 64) / 64;
|
|
276
|
+
content.style.overflowY = "visible";
|
|
277
|
+
content.style.clipPath = `inset(${-up}px ${(band.right - paper.right) / scale}px ${-down}px ${(paper.left - band.left) / scale}px)`;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
function insetClipBounds(value, box, scale) {
|
|
281
|
+
const match = /^inset\(((?:-?\d+(?:\.\d+)?px\s*){1,4})\)$/.exec(value);
|
|
282
|
+
if (!match) return null;
|
|
283
|
+
const parts = match[1].trim().split(/\s+/).map(Number.parseFloat);
|
|
284
|
+
const [top, right = top, bottom = top, left = right] = parts;
|
|
285
|
+
return {
|
|
286
|
+
top: box.top + top * scale,
|
|
287
|
+
right: box.right - right * scale,
|
|
288
|
+
bottom: box.bottom - bottom * scale,
|
|
289
|
+
left: box.left + left * scale
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
function isPreservedExactLineInk(element, content) {
|
|
293
|
+
const paragraph = element.closest(`[${EXACT_INK_ATTRIBUTE}]`);
|
|
294
|
+
const view = content.ownerDocument.defaultView;
|
|
295
|
+
if (!paragraph || !view || element === paragraph || !content.contains(paragraph)) return false;
|
|
296
|
+
if (view.getComputedStyle(element).display !== "inline") return false;
|
|
297
|
+
const band = content.getBoundingClientRect();
|
|
298
|
+
const box = paragraph.getBoundingClientRect();
|
|
299
|
+
const clip = insetClipBounds(view.getComputedStyle(content).clipPath, band, band.width / content.offsetWidth || 1);
|
|
300
|
+
const ink = element.getBoundingClientRect();
|
|
301
|
+
return clip !== null && box.top >= band.top - 0.5 && box.bottom <= band.bottom + 0.5 && ink.top >= clip.top - 0.02 && ink.bottom <= clip.bottom + 0.02;
|
|
302
|
+
}
|
|
303
|
+
function alignBaselinesToWord(root) {
|
|
304
|
+
const document2 = root.ownerDocument;
|
|
305
|
+
const view = document2.defaultView;
|
|
306
|
+
if (!view) return 0;
|
|
307
|
+
const descents = /* @__PURE__ */ new Map();
|
|
308
|
+
const ratioFor = naturalLineHeightRatios(document2);
|
|
309
|
+
const chromiumOffset = baselineOffsets(document2);
|
|
310
|
+
const descentRatio = (style) => {
|
|
311
|
+
const font = `${style.fontStyle} ${style.fontWeight} ${PROBE_FONT_SIZE_PX}px ${style.fontFamily}`;
|
|
312
|
+
let ratio = descents.get(font);
|
|
313
|
+
if (ratio === void 0) {
|
|
314
|
+
const context = document2.createElement("canvas").getContext("2d");
|
|
315
|
+
ratio = Number.NaN;
|
|
316
|
+
if (context) {
|
|
317
|
+
context.font = font;
|
|
318
|
+
if (context.font.includes(`${PROBE_FONT_SIZE_PX}px`))
|
|
319
|
+
ratio = context.measureText(LATIN_PROBE).fontBoundingBoxDescent / PROBE_FONT_SIZE_PX;
|
|
320
|
+
}
|
|
321
|
+
descents.set(font, ratio);
|
|
322
|
+
}
|
|
323
|
+
return ratio;
|
|
324
|
+
};
|
|
325
|
+
const moves = [];
|
|
326
|
+
for (const paragraph of Array.from(root.querySelectorAll(PARAGRAPH_SELECTOR))) {
|
|
327
|
+
if (!(paragraph instanceof view.HTMLElement)) continue;
|
|
328
|
+
const style = view.getComputedStyle(paragraph);
|
|
329
|
+
const candidates = Array.from(paragraph.children).filter((child) => child instanceof view.HTMLElement && (child.textContent ?? "").trim() !== "" && movable(child, view) && view.getComputedStyle(child).display === "inline" && view.getComputedStyle(child).verticalAlign === "baseline");
|
|
330
|
+
const first = candidates.find((child) => view.getComputedStyle(child).fontFamily === style.fontFamily) ?? candidates[0];
|
|
331
|
+
if (!first) continue;
|
|
332
|
+
const lineHeight = Number.parseFloat(style.lineHeight);
|
|
333
|
+
const fontSize = Number.parseFloat(style.fontSize);
|
|
334
|
+
if (!(lineHeight > 0) || !(fontSize > 0) || Math.abs(lineHeight - ratioFor(style, LATIN_PROBE) * fontSize) > 0.01) continue;
|
|
335
|
+
const descent = descentRatio(style);
|
|
336
|
+
if (!(descent > 0)) continue;
|
|
337
|
+
const childStyle = view.getComputedStyle(first);
|
|
338
|
+
const shift = lineHeight - descent * fontSize - chromiumOffset(style, childStyle);
|
|
339
|
+
if (!Number.isFinite(shift) || Math.abs(shift) < 1 / 64 || Math.abs(shift) >= lineHeight / 2) continue;
|
|
340
|
+
for (const child of Array.from(paragraph.children))
|
|
341
|
+
if (child instanceof view.HTMLElement && movable(child, view)) {
|
|
342
|
+
const top = Number.parseFloat(view.getComputedStyle(child).top);
|
|
343
|
+
moves.push({ element: child, top: (Number.isFinite(top) ? top : 0) + shift });
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
for (const { element, top } of moves) {
|
|
347
|
+
element.style.setProperty("position", "relative");
|
|
348
|
+
element.style.setProperty("top", `${top.toFixed(4)}px`);
|
|
349
|
+
}
|
|
350
|
+
return moves.length;
|
|
351
|
+
}
|
|
352
|
+
function movable(element, view) {
|
|
353
|
+
const position = view.getComputedStyle(element).position;
|
|
354
|
+
if (position !== "static" && position !== "relative") return false;
|
|
355
|
+
if (!["inline", "inline-block"].includes(view.getComputedStyle(element).display)) return false;
|
|
356
|
+
if (element.querySelector("img, svg")) return false;
|
|
357
|
+
for (const descendant of Array.from(element.querySelectorAll("*")))
|
|
358
|
+
if (["absolute", "fixed"].includes(view.getComputedStyle(descendant).position)) return false;
|
|
359
|
+
return true;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// src/word-text.ts
|
|
363
|
+
var PREPARED = "data-docx-word-text-prepared";
|
|
364
|
+
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
365
|
+
var PARAGRAPHS = "p, h1, h2, h3, h4, h5, h6";
|
|
366
|
+
function plainDocument(root) {
|
|
367
|
+
if (root.querySelector("table, svg, math, canvas, video, audio, iframe, object, a, ol, ul, aside, template, header, footer, [data-hf-type], section.footnotes, section.endnotes"))
|
|
368
|
+
return false;
|
|
369
|
+
if (Array.from(root.querySelectorAll("img")).some((img) => !img.closest(PARAGRAPHS))) return false;
|
|
370
|
+
return Array.from(root.querySelectorAll(PARAGRAPHS)).every((p) => {
|
|
371
|
+
const run = plainRun(p);
|
|
372
|
+
return run !== null && Number(root.ownerDocument.defaultView?.getComputedStyle(p).getPropertyValue("--docx-auto-line-spacing")) > 0;
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
function pictureSibling(element, view) {
|
|
376
|
+
if (!(element instanceof view.HTMLElement) || element.localName !== "span" || element.children.length !== 1)
|
|
377
|
+
return false;
|
|
378
|
+
const wrapper = element.firstElementChild;
|
|
379
|
+
if (wrapper.localName !== "span" || wrapper.children.length !== 1 || wrapper.firstElementChild?.localName !== "img")
|
|
380
|
+
return false;
|
|
381
|
+
if (wrapper.dataset.docxEdgePicture !== "true") return false;
|
|
382
|
+
const style = view.getComputedStyle(wrapper);
|
|
383
|
+
const outer = view.getComputedStyle(element);
|
|
384
|
+
const image = view.getComputedStyle(wrapper.firstElementChild);
|
|
385
|
+
return ["left", "right"].includes(style.cssFloat) && style.position === "static" && style.transform === "none" && outer.display === "inline" && ["static", "relative"].includes(outer.position) && outer.transform === "none" && image.transform === "none" && image.position === "static";
|
|
386
|
+
}
|
|
387
|
+
function plainRun(paragraph) {
|
|
388
|
+
const view = paragraph.ownerDocument.defaultView;
|
|
389
|
+
if (!view) return null;
|
|
390
|
+
const children = Array.from(paragraph.children);
|
|
391
|
+
const content = children.filter((child) => child.textContent !== "");
|
|
392
|
+
if (content.length !== 1) return null;
|
|
393
|
+
const run = content[0];
|
|
394
|
+
if (children.length !== 1) {
|
|
395
|
+
const runStyle = view.getComputedStyle(run);
|
|
396
|
+
for (const empty of children.filter((child) => child !== run)) {
|
|
397
|
+
if (pictureSibling(empty, view)) {
|
|
398
|
+
const pictureStyle = view.getComputedStyle(empty);
|
|
399
|
+
if (["font-size", "font-style", "font-weight", "vertical-align"].some((property) => pictureStyle.getPropertyValue(property) !== runStyle.getPropertyValue(property))) return null;
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
if (paragraph.dataset.columnBreakBefore !== "true") return null;
|
|
403
|
+
if (!(empty instanceof view.HTMLElement) || empty.localName !== "span" || empty.childNodes.length || Array.from(empty.attributes).some((a) => !["class", "style", "lang", "dir"].includes(a.name))) return null;
|
|
404
|
+
const emptyStyle = view.getComputedStyle(empty);
|
|
405
|
+
if ([
|
|
406
|
+
"font-family",
|
|
407
|
+
"font-size",
|
|
408
|
+
"font-style",
|
|
409
|
+
"font-weight",
|
|
410
|
+
"font-stretch",
|
|
411
|
+
"line-height",
|
|
412
|
+
"display",
|
|
413
|
+
"position",
|
|
414
|
+
"top",
|
|
415
|
+
"vertical-align"
|
|
416
|
+
].some((property) => emptyStyle.getPropertyValue(property) !== runStyle.getPropertyValue(property))) return null;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
if (!(run instanceof view.HTMLElement) || run.localName !== "span" || run.children.length || run.childNodes.length !== 1 || run.firstChild?.nodeType !== 3) return null;
|
|
420
|
+
const text = run.textContent ?? "";
|
|
421
|
+
if (!text.trim() || /^\s/.test(text) || !/^[\u0020-\u024f]*$/u.test(text)) return null;
|
|
422
|
+
if (Array.from(run.attributes).some((a) => !["class", "style", "lang", "dir"].includes(a.name))) return null;
|
|
423
|
+
const style = view.getComputedStyle(run);
|
|
424
|
+
const parent = view.getComputedStyle(paragraph);
|
|
425
|
+
if (["Top", "Right", "Bottom", "Left"].some((side) => parent.getPropertyValue(`padding-${side.toLowerCase()}`) !== "0px" || parent.getPropertyValue(`border-${side.toLowerCase()}-width`) !== "0px")) return null;
|
|
426
|
+
if (parent.direction !== "ltr" || parent.writingMode !== "horizontal-tb" || style.display !== "inline" || !["static", "relative"].includes(style.position) || style.textTransform !== "none" || style.fontVariantCaps !== "normal" || style.textDecorationLine !== "none" || !["normal", "0px"].includes(style.letterSpacing) || !["normal", "100%"].includes(style.fontStretch) || style.backgroundColor !== "rgba(0, 0, 0, 0)" || style.textShadow !== "none" || style.transform !== "none" || style.opacity !== "1" || style.verticalAlign !== "baseline") return null;
|
|
427
|
+
return run;
|
|
428
|
+
}
|
|
429
|
+
function contourRows(image, paragraph, run, lineCount, checkpoint) {
|
|
430
|
+
const view = paragraph.ownerDocument.defaultView;
|
|
431
|
+
const wrapper = image.parentElement;
|
|
432
|
+
if (wrapper.dataset.docxWrapText !== "bothSides" || image.closest(".page-content") !== paragraph.closest(".page-content")) return null;
|
|
433
|
+
const wrapStyle = view.getComputedStyle(wrapper);
|
|
434
|
+
if (wrapStyle.marginTop !== "0px" || wrapStyle.marginBottom !== "0px") return null;
|
|
435
|
+
const polygon = /^polygon\((.*)\) border-box$/.exec(wrapStyle.shapeOutside);
|
|
436
|
+
if (!polygon) return null;
|
|
437
|
+
const points = polygon[1].split(",").map((point) => {
|
|
438
|
+
const match = /^\s*([\d.+-]+)%\s+([\d.+-]+)%\s*$/.exec(point);
|
|
439
|
+
return match ? { x: Number(match[1]) / 100, y: Number(match[2]) / 100 } : null;
|
|
440
|
+
});
|
|
441
|
+
if (points.length < 3 || points.some((p) => !p || !Number.isFinite(p.x + p.y))) return null;
|
|
442
|
+
const vertices = points;
|
|
443
|
+
let orientation = 0;
|
|
444
|
+
for (let i = 0; i < vertices.length; i++) {
|
|
445
|
+
const a = vertices[i], b = vertices[(i + 1) % vertices.length], c = vertices[(i + 2) % vertices.length];
|
|
446
|
+
const cross = (b.x - a.x) * (c.y - b.y) - (b.y - a.y) * (c.x - b.x);
|
|
447
|
+
if (Math.abs(cross) < 1e-8) continue;
|
|
448
|
+
if (orientation && Math.sign(cross) !== orientation) return null;
|
|
449
|
+
orientation = Math.sign(cross);
|
|
450
|
+
}
|
|
451
|
+
if (!orientation) return null;
|
|
452
|
+
const left = Number.parseFloat(wrapStyle.getPropertyValue("--docx-wrap-distance-left")) * 4 / 3;
|
|
453
|
+
const right = Number.parseFloat(wrapStyle.getPropertyValue("--docx-wrap-distance-right")) * 4 / 3;
|
|
454
|
+
if (!Number.isFinite(left + right) || left < 0 || right < 0) return null;
|
|
455
|
+
const box = paragraph.getBoundingClientRect();
|
|
456
|
+
const picture = image.getBoundingClientRect();
|
|
457
|
+
if (box.top >= picture.bottom || box.bottom <= picture.top) return null;
|
|
458
|
+
const style = view.getComputedStyle(run);
|
|
459
|
+
if (!["start", "left"].includes(view.getComputedStyle(paragraph).textAlign)) return null;
|
|
460
|
+
const pitch = Number.parseFloat(style.lineHeight);
|
|
461
|
+
const nativeHeight = Number.parseFloat(style.getPropertyValue("--docx-word-native-height"));
|
|
462
|
+
if (!(nativeHeight > 0) || !(pitch > 0) || !(picture.width > 0) || !(picture.height > 0)) return null;
|
|
463
|
+
const context = paragraph.ownerDocument.createElement("canvas").getContext("2d");
|
|
464
|
+
if (!context) return null;
|
|
465
|
+
const measureScale = Number.parseFloat(style.fontSize) / 1e4;
|
|
466
|
+
context.font = `${style.fontStyle} ${style.fontWeight} 10000px ${style.fontFamily}`;
|
|
467
|
+
context.fontKerning = style.fontKerning;
|
|
468
|
+
if (!context.font.includes("10000px")) return null;
|
|
469
|
+
const words = Array.from(run.textContent.matchAll(/\S+\s*/gu), (match, index) => {
|
|
470
|
+
if ((index & 63) === 0) checkpoint?.();
|
|
471
|
+
return {
|
|
472
|
+
text: match[0],
|
|
473
|
+
width: context.measureText(match[0].trimEnd()).width * measureScale,
|
|
474
|
+
advance: context.measureText(match[0]).width * measureScale
|
|
475
|
+
};
|
|
476
|
+
});
|
|
477
|
+
const rows = /* @__PURE__ */ new Map();
|
|
478
|
+
let next = 0;
|
|
479
|
+
for (let line = 0; line < lineCount && next < words.length; line++) {
|
|
480
|
+
checkpoint?.();
|
|
481
|
+
const top = (box.top + line * pitch - picture.top) / picture.height;
|
|
482
|
+
const bottom = top + nativeHeight / picture.height;
|
|
483
|
+
const crossings = [];
|
|
484
|
+
for (let i = 0; i < vertices.length; i++) {
|
|
485
|
+
const a = vertices[i], b = vertices[(i + 1) % vertices.length];
|
|
486
|
+
if (a.y >= top && a.y <= bottom) crossings.push(a.x);
|
|
487
|
+
for (const y of [top, bottom])
|
|
488
|
+
if (a.y !== b.y && y >= Math.min(a.y, b.y) && y <= Math.max(a.y, b.y))
|
|
489
|
+
crossings.push(a.x + (b.x - a.x) * (y - a.y) / (b.y - a.y));
|
|
490
|
+
}
|
|
491
|
+
const slots = crossings.length ? [
|
|
492
|
+
[box.left, Math.min(box.right, picture.left + Math.min(...crossings) * picture.width - left)],
|
|
493
|
+
[Math.max(box.left, picture.left + Math.max(...crossings) * picture.width + right), box.right]
|
|
494
|
+
] : [[box.left, box.right]];
|
|
495
|
+
const row = [];
|
|
496
|
+
for (const [start, end] of slots) {
|
|
497
|
+
let x = start;
|
|
498
|
+
while (next < words.length && x + words[next].width <= end + 1 / 64) {
|
|
499
|
+
const word = words[next++];
|
|
500
|
+
row.push({ text: word.text, x: x - box.left });
|
|
501
|
+
x += word.advance;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
if (!row.length) return null;
|
|
505
|
+
rows.set(line, row);
|
|
506
|
+
}
|
|
507
|
+
return next === words.length && rows.size === lineCount ? rows : null;
|
|
508
|
+
}
|
|
509
|
+
function prepareWordText(root, checkpoint) {
|
|
510
|
+
if (!plainDocument(root)) return 0;
|
|
511
|
+
const view = root.ownerDocument.defaultView;
|
|
512
|
+
if (!view) return 0;
|
|
513
|
+
let prepared = 0;
|
|
514
|
+
for (const paragraph of Array.from(root.querySelectorAll(PARAGRAPHS))) {
|
|
515
|
+
checkpoint?.();
|
|
516
|
+
const run = plainRun(paragraph);
|
|
517
|
+
if (!run) continue;
|
|
518
|
+
const style = view.getComputedStyle(paragraph);
|
|
519
|
+
const multiple = Number(style.getPropertyValue("--docx-auto-line-spacing"));
|
|
520
|
+
const runStyle = view.getComputedStyle(run);
|
|
521
|
+
const kerning = runStyle.getPropertyValue("--docx-font-kerning").trim() || "none";
|
|
522
|
+
const height = Number.parseFloat(runStyle.lineHeight);
|
|
523
|
+
if (!(multiple > 0) || !(height > 0) || !["none", "normal"].includes(kerning)) continue;
|
|
524
|
+
const nativeTwips = Math.round(height / multiple * 15);
|
|
525
|
+
const twips = Math.floor(nativeTwips * multiple);
|
|
526
|
+
if (!(twips > 0)) continue;
|
|
527
|
+
run.style.fontKerning = kerning;
|
|
528
|
+
run.style.lineHeight = `${twips / 15}px`;
|
|
529
|
+
run.style.setProperty("--docx-word-native-height", `${nativeTwips / 15}px`);
|
|
530
|
+
for (const picture of Array.from(paragraph.children).filter((child) => pictureSibling(child, view))) {
|
|
531
|
+
picture.style.lineHeight = run.style.lineHeight;
|
|
532
|
+
picture.style.top = "0";
|
|
533
|
+
}
|
|
534
|
+
for (const empty of Array.from(paragraph.children).filter((child) => child !== run && !child.children.length)) empty.remove();
|
|
535
|
+
paragraph.style.lineHeight = run.style.lineHeight;
|
|
536
|
+
paragraph.setAttribute(PREPARED, "true");
|
|
537
|
+
prepared++;
|
|
538
|
+
}
|
|
539
|
+
return prepared;
|
|
540
|
+
}
|
|
541
|
+
function paintWordText(root, checkpoint) {
|
|
542
|
+
const document2 = root.ownerDocument;
|
|
543
|
+
const view = document2.defaultView;
|
|
544
|
+
if (!view) return 0;
|
|
545
|
+
const paragraphs = Array.from(root.querySelectorAll(`[${PREPARED}]`));
|
|
546
|
+
const pictures = Array.from(root.querySelectorAll("img"));
|
|
547
|
+
const hasPictures = pictures.length > 0;
|
|
548
|
+
const columnOf = (paragraph) => {
|
|
549
|
+
for (let parent = paragraph.parentElement; parent && parent !== root; parent = parent.parentElement)
|
|
550
|
+
if (Number(view.getComputedStyle(parent).columnCount) > 1) return parent;
|
|
551
|
+
return null;
|
|
552
|
+
};
|
|
553
|
+
const fragmentedColumns = /* @__PURE__ */ new Set();
|
|
554
|
+
for (const paragraph of paragraphs) {
|
|
555
|
+
checkpoint?.();
|
|
556
|
+
const column = columnOf(paragraph);
|
|
557
|
+
if (column && paragraph.getClientRects().length !== 1) fragmentedColumns.add(column);
|
|
558
|
+
}
|
|
559
|
+
const metrics = /* @__PURE__ */ new Map();
|
|
560
|
+
function fontMetrics(style) {
|
|
561
|
+
const font = `${style.fontStyle} ${style.fontWeight} 10000px ${style.fontFamily}`;
|
|
562
|
+
const cached = metrics.get(font);
|
|
563
|
+
if (cached) return cached;
|
|
564
|
+
const probe = document2.createElement("span");
|
|
565
|
+
probe.setAttribute("aria-hidden", "true");
|
|
566
|
+
probe.style.cssText = `position:absolute;left:-100000px;top:0;visibility:hidden;display:inline-block;white-space:nowrap;margin:0;padding:0;border:0;font:${font};line-height:normal`;
|
|
567
|
+
probe.textContent = "Hg";
|
|
568
|
+
document2.body.append(probe);
|
|
569
|
+
const height = probe.getBoundingClientRect().height / 1e4;
|
|
570
|
+
probe.remove();
|
|
571
|
+
const context = document2.createElement("canvas").getContext("2d");
|
|
572
|
+
if (!context) return null;
|
|
573
|
+
context.font = font;
|
|
574
|
+
if (!context.font.includes("10000px")) return null;
|
|
575
|
+
const descent = context.measureText("Hg").fontBoundingBoxDescent / 1e4;
|
|
576
|
+
if (!(height > 0) || !(descent > 0)) return null;
|
|
577
|
+
const result = { height, descent };
|
|
578
|
+
metrics.set(font, result);
|
|
579
|
+
return result;
|
|
580
|
+
}
|
|
581
|
+
const pending = [];
|
|
582
|
+
for (const paragraph of paragraphs) {
|
|
583
|
+
checkpoint?.();
|
|
584
|
+
const column = columnOf(paragraph);
|
|
585
|
+
if (column && fragmentedColumns.has(column)) {
|
|
586
|
+
paragraph.removeAttribute(PREPARED);
|
|
587
|
+
continue;
|
|
588
|
+
}
|
|
589
|
+
const run = plainRun(paragraph);
|
|
590
|
+
if (!run) continue;
|
|
591
|
+
const style = view.getComputedStyle(run);
|
|
592
|
+
const metric = fontMetrics(style);
|
|
593
|
+
const fontSize = Number.parseFloat(style.fontSize);
|
|
594
|
+
const pitch = Number.parseFloat(style.lineHeight);
|
|
595
|
+
const box = paragraph.getBoundingClientRect();
|
|
596
|
+
if (!metric || !(fontSize > 0) || !(pitch > 0) || !(box.width > 0) || !(box.height > 0)) continue;
|
|
597
|
+
const columnBand = column || hasPictures ? paragraph.closest(".page-content") : null;
|
|
598
|
+
const band = columnBand?.getBoundingClientRect() ?? box;
|
|
599
|
+
const text = run.textContent;
|
|
600
|
+
let rows = /* @__PURE__ */ new Map();
|
|
601
|
+
let wordCount = 0;
|
|
602
|
+
for (const match of text.matchAll(/\S+\s*/gu)) {
|
|
603
|
+
if ((wordCount++ & 63) === 0) checkpoint?.();
|
|
604
|
+
const range = document2.createRange();
|
|
605
|
+
range.setStart(run.firstChild, match.index);
|
|
606
|
+
range.setEnd(run.firstChild, match.index + match[0].trimEnd().length);
|
|
607
|
+
const rect = range.getBoundingClientRect();
|
|
608
|
+
const row = Math.round(rect.top * 4);
|
|
609
|
+
const words = rows.get(row) ?? [];
|
|
610
|
+
words.push({ text: match[0], x: rect.left - box.left });
|
|
611
|
+
rows.set(row, words);
|
|
612
|
+
}
|
|
613
|
+
if (!rows.size) continue;
|
|
614
|
+
if (pictures.length === 1) rows = contourRows(pictures[0], paragraph, run, rows.size, checkpoint) ?? rows;
|
|
615
|
+
const svg = document2.createElementNS(SVG_NS, "svg");
|
|
616
|
+
svg.setAttribute("width", String(band.width));
|
|
617
|
+
svg.setAttribute("height", String(band.height));
|
|
618
|
+
svg.setAttribute("viewBox", `0 0 ${band.width * 4} ${band.height * 4}`);
|
|
619
|
+
svg.setAttribute("role", "group");
|
|
620
|
+
svg.style.cssText = columnBand ? "position:absolute;left:0;top:0;display:block;overflow:visible" : "display:block;overflow:visible";
|
|
621
|
+
const baseline = (metric.height - metric.descent) * fontSize;
|
|
622
|
+
Array.from(rows.values()).forEach((words, index) => {
|
|
623
|
+
const line = document2.createElementNS(SVG_NS, "text");
|
|
624
|
+
line.setAttribute("y", String((box.top - band.top + baseline + index * pitch) * 4));
|
|
625
|
+
line.setAttribute("xml:space", "preserve");
|
|
626
|
+
for (const property of ["font-family", "font-weight", "font-style", "font-stretch", "font-kerning"])
|
|
627
|
+
line.style.setProperty(property, style.getPropertyValue(property));
|
|
628
|
+
line.style.fontSize = `${fontSize * 4}px`;
|
|
629
|
+
line.style.fill = style.color;
|
|
630
|
+
for (const word of words) {
|
|
631
|
+
const span = document2.createElementNS(SVG_NS, "tspan");
|
|
632
|
+
span.setAttribute("x", String((box.left - band.left + word.x) * 4));
|
|
633
|
+
span.textContent = word.text;
|
|
634
|
+
line.append(span);
|
|
635
|
+
}
|
|
636
|
+
svg.append(line);
|
|
637
|
+
});
|
|
638
|
+
pending.push({ paragraph, run, svg, height: box.height });
|
|
639
|
+
}
|
|
640
|
+
for (const { paragraph, run, svg, height } of pending) {
|
|
641
|
+
checkpoint?.();
|
|
642
|
+
if (Array.from(paragraph.children).some((child) => pictureSibling(child, view))) run.replaceWith(svg);
|
|
643
|
+
else paragraph.replaceChildren(svg);
|
|
644
|
+
paragraph.style.setProperty("--docx-word-text-height", `${height}px`);
|
|
645
|
+
paragraph.style.height = "var(--docx-word-text-height)";
|
|
646
|
+
paragraph.removeAttribute(PREPARED);
|
|
647
|
+
}
|
|
648
|
+
return pending.length;
|
|
649
|
+
}
|
|
650
|
+
|
|
108
651
|
// src/page-number-format.ts
|
|
109
652
|
var ROMAN_ONES = ["", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix"];
|
|
110
653
|
var ROMAN_TENS = ["", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc"];
|
|
@@ -235,6 +778,128 @@ function samePageBox(a, b) {
|
|
|
235
778
|
var MAX_FOOTNOTE_AREA_RATIO = 0.6;
|
|
236
779
|
var FOOTNOTE_MEASUREMENT_GUARD_PT = 2;
|
|
237
780
|
var FOOTNOTE_SOURCE_POSITION_ATTR = "data-pagination-footnote-source-position";
|
|
781
|
+
function collapsedBlockMarginsPx(view, element) {
|
|
782
|
+
return {
|
|
783
|
+
topPx: collapseMarginChain(view, element, "top"),
|
|
784
|
+
bottomPx: collapseMarginChain(view, element, "bottom")
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
function collapseMarginChain(view, element, edge) {
|
|
788
|
+
const margins = [];
|
|
789
|
+
let current = element;
|
|
790
|
+
while (current) {
|
|
791
|
+
const style = view.getComputedStyle(current);
|
|
792
|
+
margins.push(parseFloat(edge === "top" ? style.marginTop : style.marginBottom) || 0);
|
|
793
|
+
current = collapsesThroughTo(view, current, style, edge);
|
|
794
|
+
}
|
|
795
|
+
const positive = Math.max(0, ...margins);
|
|
796
|
+
const negative = Math.min(0, ...margins);
|
|
797
|
+
return positive + negative;
|
|
798
|
+
}
|
|
799
|
+
function collapsesThroughTo(view, parent, style, edge) {
|
|
800
|
+
const ordinaryBlock = (style.display === "block" || style.display === "list-item") && style.overflowX === "visible" && style.overflowY === "visible" && style.float === "none" && style.position !== "absolute" && style.position !== "fixed";
|
|
801
|
+
if (!ordinaryBlock) return null;
|
|
802
|
+
if (style.columnCount !== "auto" || style.columnWidth !== "auto") return null;
|
|
803
|
+
const border = parseFloat(edge === "top" ? style.borderTopWidth : style.borderBottomWidth) || 0;
|
|
804
|
+
const padding = parseFloat(edge === "top" ? style.paddingTop : style.paddingBottom) || 0;
|
|
805
|
+
if (border !== 0 || padding !== 0) return null;
|
|
806
|
+
if (edge === "bottom" && parent.style.height !== "") return null;
|
|
807
|
+
const nodes = Array.from(parent.childNodes);
|
|
808
|
+
if (edge === "bottom") nodes.reverse();
|
|
809
|
+
for (const node of nodes) {
|
|
810
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
811
|
+
if ((node.textContent ?? "").trim() !== "") return null;
|
|
812
|
+
continue;
|
|
813
|
+
}
|
|
814
|
+
if (node.nodeType !== Node.ELEMENT_NODE) continue;
|
|
815
|
+
const child = node;
|
|
816
|
+
const childStyle = view.getComputedStyle(child);
|
|
817
|
+
if (childStyle.display === "none") continue;
|
|
818
|
+
if (childStyle.position === "absolute" || childStyle.position === "fixed" || childStyle.float !== "none") continue;
|
|
819
|
+
const blockLevel = childStyle.display === "block" || childStyle.display === "list-item" || childStyle.display === "table" || childStyle.display === "flow-root" || childStyle.display === "flex" || childStyle.display === "grid";
|
|
820
|
+
return blockLevel ? child : null;
|
|
821
|
+
}
|
|
822
|
+
return null;
|
|
823
|
+
}
|
|
824
|
+
var DRAWING_HOST_ATTRIBUTE = "data-docx-anchor-host";
|
|
825
|
+
var HOSTS_PROMOTED_DRAWING_ATTRIBUTE = "data-docx-hosts-promoted-drawing";
|
|
826
|
+
function rendersOnlyItsLine(element) {
|
|
827
|
+
return (element.textContent ?? "").replace(/[\s\u00a0]/g, "") === "" && element.querySelector("img, svg, canvas, [data-docx-drawing-anchor]") === null;
|
|
828
|
+
}
|
|
829
|
+
function createRenderedSourceMeasurer(clipInset, drawingHostAttribute, hostsPromotedDrawingAttribute) {
|
|
830
|
+
function visibleWithinPage(view, element, page, pageRect, rect) {
|
|
831
|
+
let left = Math.max(rect.left, pageRect.left);
|
|
832
|
+
let top = Math.max(rect.top, pageRect.top);
|
|
833
|
+
let right = Math.min(rect.right, pageRect.right);
|
|
834
|
+
let bottom = Math.min(rect.bottom, pageRect.bottom);
|
|
835
|
+
const clips = (value) => value === "hidden" || value === "clip" || value === "scroll" || value === "auto";
|
|
836
|
+
for (let ancestor = element.parentElement; ancestor && ancestor !== page; ancestor = ancestor.parentElement) {
|
|
837
|
+
const style = view.getComputedStyle(ancestor);
|
|
838
|
+
const clipsX = clips(style.overflowX);
|
|
839
|
+
const clipsY = clips(style.overflowY);
|
|
840
|
+
if (!clipsX && !clipsY && !style.clipPath.startsWith("inset(")) continue;
|
|
841
|
+
const ancestorRect = ancestor.getBoundingClientRect();
|
|
842
|
+
const scale = ancestorRect.width / ancestor.offsetWidth || 1;
|
|
843
|
+
const inset = clipInset(style.clipPath, ancestorRect, scale);
|
|
844
|
+
if (inset) {
|
|
845
|
+
left = Math.max(left, inset.left);
|
|
846
|
+
top = Math.max(top, inset.top);
|
|
847
|
+
right = Math.min(right, inset.right);
|
|
848
|
+
bottom = Math.min(bottom, inset.bottom);
|
|
849
|
+
}
|
|
850
|
+
if (clipsX) {
|
|
851
|
+
left = Math.max(left, ancestorRect.left);
|
|
852
|
+
right = Math.min(right, ancestorRect.right);
|
|
853
|
+
}
|
|
854
|
+
if (clipsY) {
|
|
855
|
+
top = Math.max(top, ancestorRect.top);
|
|
856
|
+
bottom = Math.min(bottom, ancestorRect.bottom);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
return { left, top, right, bottom };
|
|
860
|
+
}
|
|
861
|
+
return function measureRenderedSource2(view, element, page, pageRect = page.getBoundingClientRect()) {
|
|
862
|
+
const own = element.getBoundingClientRect();
|
|
863
|
+
const ownResult = () => ({ rect: own, visible: visibleWithinPage(view, element, page, pageRect, own) });
|
|
864
|
+
const ownMeasurable = own.width > 0 && own.height > 0;
|
|
865
|
+
const hostsPromotedDrawing = element.hasAttribute(hostsPromotedDrawingAttribute);
|
|
866
|
+
if (ownMeasurable && !hostsPromotedDrawing) return ownResult();
|
|
867
|
+
const hostAnchorId = element.dataset.sourceAnchorId;
|
|
868
|
+
const promoted = hostAnchorId ? Array.from(page.querySelectorAll(`[${drawingHostAttribute}]`)).filter((drawing) => drawing.getAttribute(drawingHostAttribute) === hostAnchorId) : [];
|
|
869
|
+
if (ownMeasurable && promoted.length === 0) return ownResult();
|
|
870
|
+
const rendered = [
|
|
871
|
+
...ownMeasurable ? [element] : Array.from(element.querySelectorAll("*")),
|
|
872
|
+
...promoted.flatMap((drawing) => [drawing, ...Array.from(drawing.querySelectorAll("*"))])
|
|
873
|
+
];
|
|
874
|
+
let left = Infinity;
|
|
875
|
+
let top = Infinity;
|
|
876
|
+
let right = -Infinity;
|
|
877
|
+
let bottom = -Infinity;
|
|
878
|
+
for (const child of rendered) {
|
|
879
|
+
const style = view.getComputedStyle(child);
|
|
880
|
+
if (style.display === "none" || style.visibility === "hidden") continue;
|
|
881
|
+
const childRect = child.getBoundingClientRect();
|
|
882
|
+
if (childRect.width <= 0 || childRect.height <= 0) continue;
|
|
883
|
+
const visible = visibleWithinPage(view, child, page, pageRect, childRect);
|
|
884
|
+
if (visible.right <= visible.left || visible.bottom <= visible.top) continue;
|
|
885
|
+
left = Math.min(left, visible.left);
|
|
886
|
+
top = Math.min(top, visible.top);
|
|
887
|
+
right = Math.max(right, visible.right);
|
|
888
|
+
bottom = Math.max(bottom, visible.bottom);
|
|
889
|
+
}
|
|
890
|
+
if (!(left < right && top < bottom)) return ownResult();
|
|
891
|
+
return { rect: new DOMRect(left, top, right - left, bottom - top), visible: { left, top, right, bottom } };
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
var measureRenderedSource = createRenderedSourceMeasurer(
|
|
895
|
+
insetClipBounds,
|
|
896
|
+
DRAWING_HOST_ATTRIBUTE,
|
|
897
|
+
HOSTS_PROMOTED_DRAWING_ATTRIBUTE
|
|
898
|
+
);
|
|
899
|
+
function renderedSourceMeasurerExpression() {
|
|
900
|
+
return `(${createRenderedSourceMeasurer.toString()})(${insetClipBounds.toString()},
|
|
901
|
+
${JSON.stringify(DRAWING_HOST_ATTRIBUTE)}, ${JSON.stringify(HOSTS_PROMOTED_DRAWING_ATTRIBUTE)})`;
|
|
902
|
+
}
|
|
238
903
|
var PaginationEngine = class {
|
|
239
904
|
/**
|
|
240
905
|
* Creates a new pagination engine.
|
|
@@ -278,6 +943,7 @@ var PaginationEngine = class {
|
|
|
278
943
|
this.pageGap = options.pageGap ?? 20;
|
|
279
944
|
this.fragmentParagraphs = options.fragmentParagraphs ?? false;
|
|
280
945
|
this.deferFragmentIdentities = options.deferFragmentIdentities ?? false;
|
|
946
|
+
this.bodyBudgetReductionsPt = options.bodyBudgetReductionsPt ?? {};
|
|
281
947
|
this.cancellationCheckpoint = options.checkCancellation;
|
|
282
948
|
this.pageCountCheckpoint = options.checkPageCount;
|
|
283
949
|
this.layoutToken = options.layoutToken;
|
|
@@ -426,7 +1092,8 @@ var PaginationEngine = class {
|
|
|
426
1092
|
sectionDims,
|
|
427
1093
|
sectionIndex,
|
|
428
1094
|
columnCount,
|
|
429
|
-
Number.isFinite(gap) ? gap : 36
|
|
1095
|
+
Number.isFinite(gap) ? gap : 36,
|
|
1096
|
+
section.dataset.sectionType === "continuous" && section === sectionsToProcess[sectionsToProcess.length - 1]
|
|
430
1097
|
));
|
|
431
1098
|
} else {
|
|
432
1099
|
blocks.push(...this.measureBlocks(section, sectionDims, sectionIndex));
|
|
@@ -533,12 +1200,11 @@ var PaginationEngine = class {
|
|
|
533
1200
|
if (!anchorId || !element.dataset.pageFragmentId || !Number.isInteger(parseInt(element.dataset.fragmentIndex || "", 10))) {
|
|
534
1201
|
throw new Error(`page ${page.pageNumber} contains an unqualified source anchor`);
|
|
535
1202
|
}
|
|
536
|
-
const rect = element.getBoundingClientRect();
|
|
537
1203
|
const style = this.view.getComputedStyle(element);
|
|
538
1204
|
const deliberatelyHidden = style.display === "none" || style.visibility === "hidden";
|
|
539
1205
|
if (deliberatelyHidden) continue;
|
|
1206
|
+
const { rect, visible: visibleRect } = measureRenderedSource(this.view, element, page.element, pageRect);
|
|
540
1207
|
requiredAnchorIds.add(anchorId);
|
|
541
|
-
const visibleRect = this.intersectWithClippingAncestors(element, page.element, pageRect, rect);
|
|
542
1208
|
const left = visibleRect.left;
|
|
543
1209
|
const top = visibleRect.top;
|
|
544
1210
|
const right = visibleRect.right;
|
|
@@ -599,8 +1265,7 @@ var PaginationEngine = class {
|
|
|
599
1265
|
if (!anchorId) continue;
|
|
600
1266
|
const style = this.view.getComputedStyle(element);
|
|
601
1267
|
if (style.display === "none" || style.visibility === "hidden") continue;
|
|
602
|
-
const rect = element.
|
|
603
|
-
const visible = this.intersectWithClippingAncestors(element, page.element, pageRect, rect);
|
|
1268
|
+
const { rect, visible } = measureRenderedSource(this.view, element, page.element, pageRect);
|
|
604
1269
|
if (rect.width <= 0 || rect.height <= 0 || visible.right <= visible.left || visible.bottom <= visible.top) continue;
|
|
605
1270
|
const fragmentIndex = emittedFragmentCounts.get(anchorId) ?? 0;
|
|
606
1271
|
emittedFragmentCounts.set(anchorId, fragmentIndex + 1);
|
|
@@ -610,34 +1275,6 @@ var PaginationEngine = class {
|
|
|
610
1275
|
}
|
|
611
1276
|
}
|
|
612
1277
|
}
|
|
613
|
-
/**
|
|
614
|
-
* Intersect an element with every ancestor that establishes an overflow clip before the page
|
|
615
|
-
* root. getBoundingClientRect() reports layout outside those clips, which is not rendered and
|
|
616
|
-
* therefore must not satisfy PageMap completeness or inflate portable geometry.
|
|
617
|
-
*/
|
|
618
|
-
intersectWithClippingAncestors(element, page, pageRect, rect) {
|
|
619
|
-
let left = Math.max(rect.left, pageRect.left);
|
|
620
|
-
let top = Math.max(rect.top, pageRect.top);
|
|
621
|
-
let right = Math.min(rect.right, pageRect.right);
|
|
622
|
-
let bottom = Math.min(rect.bottom, pageRect.bottom);
|
|
623
|
-
const clips = (value) => value === "hidden" || value === "clip" || value === "scroll" || value === "auto";
|
|
624
|
-
for (let ancestor = element.parentElement; ancestor && ancestor !== page; ancestor = ancestor.parentElement) {
|
|
625
|
-
const style = this.view.getComputedStyle(ancestor);
|
|
626
|
-
const clipsX = clips(style.overflowX);
|
|
627
|
-
const clipsY = clips(style.overflowY);
|
|
628
|
-
if (!clipsX && !clipsY) continue;
|
|
629
|
-
const ancestorRect = ancestor.getBoundingClientRect();
|
|
630
|
-
if (clipsX) {
|
|
631
|
-
left = Math.max(left, ancestorRect.left);
|
|
632
|
-
right = Math.min(right, ancestorRect.right);
|
|
633
|
-
}
|
|
634
|
-
if (clipsY) {
|
|
635
|
-
top = Math.max(top, ancestorRect.top);
|
|
636
|
-
bottom = Math.min(bottom, ancestorRect.bottom);
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
return { left, top, right, bottom };
|
|
640
|
-
}
|
|
641
1278
|
storyForCanonicalAnchor(anchorId) {
|
|
642
1279
|
const first = anchorId.indexOf(":");
|
|
643
1280
|
const second = first < 0 ? -1 : anchorId.indexOf(":", first + 1);
|
|
@@ -829,9 +1466,7 @@ var PaginationEngine = class {
|
|
|
829
1466
|
}
|
|
830
1467
|
}
|
|
831
1468
|
const rect = child.getBoundingClientRect();
|
|
832
|
-
const
|
|
833
|
-
const marginTopPx = parseFloat(style.marginTop) || 0;
|
|
834
|
-
const marginBottomPx = parseFloat(style.marginBottom) || 0;
|
|
1469
|
+
const { topPx: marginTopPx, bottomPx: marginBottomPx } = collapsedBlockMarginsPx(this.view, child);
|
|
835
1470
|
const heightPt = pxToPt(rect.height);
|
|
836
1471
|
const marginTopPt = pxToPt(marginTopPx);
|
|
837
1472
|
const marginBottomPt = pxToPt(marginBottomPx);
|
|
@@ -874,7 +1509,24 @@ var PaginationEngine = class {
|
|
|
874
1509
|
for (const rule of sectionChildren.filter((child) => child.tagName === "HR")) {
|
|
875
1510
|
this.checkpoint();
|
|
876
1511
|
const clonedRule = rule.cloneNode(true);
|
|
877
|
-
|
|
1512
|
+
const ruleStyle = this.view.getComputedStyle(rule);
|
|
1513
|
+
for (const property of [
|
|
1514
|
+
"height",
|
|
1515
|
+
"width",
|
|
1516
|
+
"max-width",
|
|
1517
|
+
"border",
|
|
1518
|
+
"background-color",
|
|
1519
|
+
"color",
|
|
1520
|
+
"margin-top",
|
|
1521
|
+
"margin-right",
|
|
1522
|
+
"margin-bottom",
|
|
1523
|
+
"margin-left"
|
|
1524
|
+
]) {
|
|
1525
|
+
clonedRule.style.setProperty(property, ruleStyle.getPropertyValue(property));
|
|
1526
|
+
}
|
|
1527
|
+
if (blocks.length === 0) {
|
|
1528
|
+
clonedRule.style.marginTop = `calc(${sectionStyle.marginTop} + ${ruleStyle.marginTop})`;
|
|
1529
|
+
}
|
|
878
1530
|
blocks.push(this.measureElement(clonedRule, dims, sectionIndex));
|
|
879
1531
|
}
|
|
880
1532
|
const listStyle = this.view.getComputedStyle(list);
|
|
@@ -908,12 +1560,14 @@ var PaginationEngine = class {
|
|
|
908
1560
|
if (clone.id && clone.id !== itemId) return null;
|
|
909
1561
|
clone.id = itemId;
|
|
910
1562
|
}
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
1563
|
+
if (!clone.querySelector("[data-endnote-number]")) {
|
|
1564
|
+
const value = parseInt(item.getAttribute("value") || String(itemIndex + 1), 10);
|
|
1565
|
+
const marker = this.formatOrderedListMarker(
|
|
1566
|
+
Number.isFinite(value) ? value : itemIndex + 1,
|
|
1567
|
+
listStyle.listStyleType
|
|
1568
|
+
);
|
|
1569
|
+
clone.insertBefore(this.document.createTextNode(`${marker}. `), clone.firstChild);
|
|
1570
|
+
}
|
|
917
1571
|
}
|
|
918
1572
|
blocks.push(this.measureElement(clone, dims, sectionIndex));
|
|
919
1573
|
}
|
|
@@ -943,15 +1597,16 @@ var PaginationEngine = class {
|
|
|
943
1597
|
/**
|
|
944
1598
|
* Flows a multi-column (`w:cols`) section's children into CSS-multicol container
|
|
945
1599
|
* blocks. Word lays such a section out as N columns inside the same body extent;
|
|
946
|
-
* a
|
|
1600
|
+
* a `column-count` container reproduces that geometry, and the paginator
|
|
947
1601
|
* then places each container as one ordinary measured block. A container grows
|
|
948
|
-
* greedily until
|
|
949
|
-
*
|
|
1602
|
+
* greedily until it would exceed the smallest page body available to the section.
|
|
1603
|
+
* Final continuous sections fill that height sequentially; other sections retain
|
|
1604
|
+
* balancing. A long columned section still splits across pages at block
|
|
950
1605
|
* boundaries. Each child lands in exactly one container, so anchors never
|
|
951
1606
|
* duplicate. An explicit page break child passes through as its own block, which
|
|
952
1607
|
* ends the current container and lets the normal flow logic turn the page.
|
|
953
1608
|
*/
|
|
954
|
-
buildColumnBlocks(section, dims, sectionIndex, columnCount, columnGapPt) {
|
|
1609
|
+
buildColumnBlocks(section, dims, sectionIndex, columnCount, columnGapPt, sequential = false) {
|
|
955
1610
|
const children = Array.from(section.children);
|
|
956
1611
|
const blocks = [];
|
|
957
1612
|
const maxFragmentHeight = this.smallestEffectiveContentHeight(dims, sectionIndex);
|
|
@@ -960,9 +1615,15 @@ var PaginationEngine = class {
|
|
|
960
1615
|
const container = this.document.createElement("div");
|
|
961
1616
|
container.style.columnCount = String(columnCount);
|
|
962
1617
|
container.style.columnGap = `${columnGapPt}pt`;
|
|
1618
|
+
if (sequential) {
|
|
1619
|
+
container.dataset.paginationSequentialColumns = "true";
|
|
1620
|
+
container.style.columnFill = "auto";
|
|
1621
|
+
container.style.maxHeight = `${maxFragmentHeight}pt`;
|
|
1622
|
+
}
|
|
963
1623
|
for (const child of slice) {
|
|
964
1624
|
container.appendChild(child.cloneNode(true));
|
|
965
1625
|
}
|
|
1626
|
+
if (sequential) this.liftColumnSpaceBefore(container, dims);
|
|
966
1627
|
return container;
|
|
967
1628
|
};
|
|
968
1629
|
let start = 0;
|
|
@@ -975,12 +1636,17 @@ var PaginationEngine = class {
|
|
|
975
1636
|
}
|
|
976
1637
|
let end = start + 1;
|
|
977
1638
|
let container = makeContainer(children.slice(start, end));
|
|
1639
|
+
if (sequential && this.columnContainerOverflows(container, dims)) {
|
|
1640
|
+
delete container.dataset.paginationSequentialColumns;
|
|
1641
|
+
container.style.columnFill = "balance";
|
|
1642
|
+
container.style.maxHeight = "";
|
|
1643
|
+
}
|
|
978
1644
|
let measured = this.measureElement(container, dims, sectionIndex);
|
|
979
1645
|
while (end < children.length && !isBreak(children[end])) {
|
|
980
1646
|
this.checkpoint();
|
|
981
1647
|
const candidate = makeContainer(children.slice(start, end + 1));
|
|
982
1648
|
const candidateMeasured = this.measureElement(candidate, dims, sectionIndex);
|
|
983
|
-
if (candidateMeasured.heightPt > maxFragmentHeight) break;
|
|
1649
|
+
if (candidateMeasured.heightPt > maxFragmentHeight || sequential && this.columnContainerOverflows(candidate, dims)) break;
|
|
984
1650
|
container = candidate;
|
|
985
1651
|
measured = candidateMeasured;
|
|
986
1652
|
end++;
|
|
@@ -1001,6 +1667,76 @@ var PaginationEngine = class {
|
|
|
1001
1667
|
}
|
|
1002
1668
|
return blocks;
|
|
1003
1669
|
}
|
|
1670
|
+
/** Keep Word's paragraph space-before in the ordinary inter-block spacing budget. */
|
|
1671
|
+
liftColumnSpaceBefore(container, dims) {
|
|
1672
|
+
container.style.marginTop = "";
|
|
1673
|
+
const first = container.firstElementChild;
|
|
1674
|
+
if (!first || !this.isWordParagraphElement(first)) return;
|
|
1675
|
+
const host = this.document.createElement("div");
|
|
1676
|
+
host.style.width = `${dims.contentWidth}pt`;
|
|
1677
|
+
host.appendChild(container);
|
|
1678
|
+
this.stagingElement.appendChild(host);
|
|
1679
|
+
try {
|
|
1680
|
+
const spaceBefore = first.dataset.paginationColumnSpaceBefore ?? this.view.getComputedStyle(first).marginTop;
|
|
1681
|
+
first.dataset.paginationColumnSpaceBefore = spaceBefore;
|
|
1682
|
+
first.style.marginTop = "0";
|
|
1683
|
+
container.style.marginTop = spaceBefore;
|
|
1684
|
+
} finally {
|
|
1685
|
+
container.remove();
|
|
1686
|
+
host.remove();
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
/** A height-constrained multicol box overflows horizontally when it needs another column. */
|
|
1690
|
+
columnContainerOverflows(element, dims) {
|
|
1691
|
+
const host = this.document.createElement("div");
|
|
1692
|
+
host.style.width = `${dims.contentWidth}pt`;
|
|
1693
|
+
const clone = element.cloneNode(true);
|
|
1694
|
+
host.appendChild(clone);
|
|
1695
|
+
this.stagingElement.appendChild(host);
|
|
1696
|
+
try {
|
|
1697
|
+
if (clone.scrollWidth > clone.clientWidth + 1) return true;
|
|
1698
|
+
return Array.from(clone.querySelectorAll('[data-keep-lines="true"]')).some((paragraph) => paragraph.getClientRects().length > 1);
|
|
1699
|
+
} finally {
|
|
1700
|
+
host.remove();
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
/**
|
|
1704
|
+
* Reflow a final continuous column section against the actual remaining body band.
|
|
1705
|
+
* A preceding title, running story or note reservation can make it shorter than the
|
|
1706
|
+
* fresh-page band used while collecting source blocks. Split at source-child boundaries
|
|
1707
|
+
* when the narrower band would create an extra horizontal column; the tail is reconsidered
|
|
1708
|
+
* normally on the following page. No source child is duplicated or discarded.
|
|
1709
|
+
*/
|
|
1710
|
+
reflowSequentialColumns(block, dims, availableHeightPt) {
|
|
1711
|
+
if (block.element.dataset.paginationSequentialColumns !== "true" || availableHeightPt <= 0) return null;
|
|
1712
|
+
const maximumHeight = this.smallestEffectiveContentHeight(dims, block.sectionIndex);
|
|
1713
|
+
const height = Math.min(availableHeightPt, maximumHeight);
|
|
1714
|
+
if (Math.abs(parseFloat(block.element.style.maxHeight) - height) < 0.01) return null;
|
|
1715
|
+
const children = Array.from(block.element.children);
|
|
1716
|
+
const make = (slice, limit) => {
|
|
1717
|
+
const container = block.element.cloneNode(false);
|
|
1718
|
+
container.style.maxHeight = `${limit}pt`;
|
|
1719
|
+
for (const child of slice) container.appendChild(child.cloneNode(true));
|
|
1720
|
+
this.liftColumnSpaceBefore(container, dims);
|
|
1721
|
+
return container;
|
|
1722
|
+
};
|
|
1723
|
+
const whole = make(children, height);
|
|
1724
|
+
if (!this.columnContainerOverflows(whole, dims)) {
|
|
1725
|
+
return [this.measureElement(whole, dims, block.sectionIndex)];
|
|
1726
|
+
}
|
|
1727
|
+
let end = 0;
|
|
1728
|
+
for (let count = 1; count < children.length; count++) {
|
|
1729
|
+
this.checkpoint();
|
|
1730
|
+
const candidate = make(children.slice(0, count), height);
|
|
1731
|
+
if (this.columnContainerOverflows(candidate, dims)) break;
|
|
1732
|
+
if (children[count - 1].dataset.keepWithNext !== "true") end = count;
|
|
1733
|
+
}
|
|
1734
|
+
if (end === 0) return null;
|
|
1735
|
+
return [
|
|
1736
|
+
this.measureElement(make(children.slice(0, end), height), dims, block.sectionIndex),
|
|
1737
|
+
this.measureElement(make(children.slice(end), maximumHeight), dims, block.sectionIndex)
|
|
1738
|
+
];
|
|
1739
|
+
}
|
|
1004
1740
|
/**
|
|
1005
1741
|
* Measures one element in the same hidden staging context used for the source blocks.
|
|
1006
1742
|
* This is intentionally DOM-based: table row heights cannot be inferred from individual
|
|
@@ -1016,13 +1752,13 @@ var PaginationEngine = class {
|
|
|
1016
1752
|
measurementHost.appendChild(measuredElement);
|
|
1017
1753
|
this.stagingElement.appendChild(measurementHost);
|
|
1018
1754
|
const rect = measuredElement.getBoundingClientRect();
|
|
1019
|
-
const
|
|
1755
|
+
const margins = collapsedBlockMarginsPx(this.view, measuredElement);
|
|
1020
1756
|
const measured = {
|
|
1021
1757
|
element,
|
|
1022
1758
|
sectionIndex,
|
|
1023
1759
|
heightPt: pxToPt(rect.height),
|
|
1024
|
-
marginTopPt: pxToPt(
|
|
1025
|
-
marginBottomPt: pxToPt(
|
|
1760
|
+
marginTopPt: pxToPt(margins.topPx),
|
|
1761
|
+
marginBottomPt: pxToPt(margins.bottomPx),
|
|
1026
1762
|
keepWithNext: element.dataset.keepWithNext === "true",
|
|
1027
1763
|
keepLines: element.dataset.keepLines === "true",
|
|
1028
1764
|
pageBreakBefore: element.dataset.pageBreakBefore === "true",
|
|
@@ -2250,6 +2986,10 @@ var PaginationEngine = class {
|
|
|
2250
2986
|
* Deterministic: it depends only on the section's page setup and the registry's pre-measured
|
|
2251
2987
|
* story heights, never on the page's content, which is what keeps it lazy-loading compatible.
|
|
2252
2988
|
*/
|
|
2989
|
+
/** The body height a physical page may fill: its body band less any caller-requested reduction. */
|
|
2990
|
+
bodyBudgetPt(bodyHeightPt, physicalPageNumber) {
|
|
2991
|
+
return Math.max(0, bodyHeightPt - (this.bodyBudgetReductionsPt[physicalPageNumber] ?? 0));
|
|
2992
|
+
}
|
|
2253
2993
|
getPageBands(dims, sectionIndex, pageInSection, displayedPageNumber) {
|
|
2254
2994
|
const sectionHf = this.hfRegistry.get(sectionIndex);
|
|
2255
2995
|
return resolvePageBands(
|
|
@@ -2334,12 +3074,12 @@ var PaginationEngine = class {
|
|
|
2334
3074
|
const currentPhysicalNumber = ownerNumbering.start !== void 0 ? ownerNumbering.start + pageInSection(pageSectionIndex) - 1 : precedingDisplayedPageNumber + 1;
|
|
2335
3075
|
return currentPhysicalNumber + physicalPage - pageNumber;
|
|
2336
3076
|
};
|
|
2337
|
-
let
|
|
3077
|
+
let effectiveContentHeight = this.bodyBudgetPt(this.getPageBands(
|
|
2338
3078
|
dimensionsFor(),
|
|
2339
3079
|
pageSectionIndex,
|
|
2340
3080
|
pageInSection(),
|
|
2341
3081
|
displayedPageNumber()
|
|
2342
|
-
);
|
|
3082
|
+
).bodyHeight, pageNumber);
|
|
2343
3083
|
let remainingHeight = effectiveContentHeight;
|
|
2344
3084
|
let prevMarginBottomPt = 0;
|
|
2345
3085
|
let currentFootnoteIds = [];
|
|
@@ -2359,7 +3099,7 @@ var PaginationEngine = class {
|
|
|
2359
3099
|
pageInSection(owner),
|
|
2360
3100
|
displayedPageNumber(owner)
|
|
2361
3101
|
);
|
|
2362
|
-
effectiveContentHeight = bands.bodyHeight;
|
|
3102
|
+
effectiveContentHeight = this.bodyBudgetPt(bands.bodyHeight, pageNumber);
|
|
2363
3103
|
remainingHeight = effectiveContentHeight;
|
|
2364
3104
|
};
|
|
2365
3105
|
const admitCurrentPage = () => {
|
|
@@ -2494,7 +3234,7 @@ var PaginationEngine = class {
|
|
|
2494
3234
|
pageInSection(),
|
|
2495
3235
|
displayedPageNumber()
|
|
2496
3236
|
);
|
|
2497
|
-
effectiveContentHeight = newBands.bodyHeight;
|
|
3237
|
+
effectiveContentHeight = this.bodyBudgetPt(newBands.bodyHeight, pageNumber);
|
|
2498
3238
|
remainingHeight = effectiveContentHeight;
|
|
2499
3239
|
prevMarginBottomPt = 0;
|
|
2500
3240
|
currentFootnoteIds = [];
|
|
@@ -2657,6 +3397,16 @@ var PaginationEngine = class {
|
|
|
2657
3397
|
isFirstOnPage,
|
|
2658
3398
|
pageInSection(block.sectionIndex)
|
|
2659
3399
|
);
|
|
3400
|
+
const columnFragments = this.reflowSequentialColumns(
|
|
3401
|
+
block,
|
|
3402
|
+
blockDimensions,
|
|
3403
|
+
remainingHeight - currentFootnoteHeight - additionalFootnoteHeight - effectiveMarginTop
|
|
3404
|
+
);
|
|
3405
|
+
if (columnFragments) {
|
|
3406
|
+
blocks.splice(i, 1, ...columnFragments);
|
|
3407
|
+
i--;
|
|
3408
|
+
continue;
|
|
3409
|
+
}
|
|
2660
3410
|
const blockSpace = effectiveMarginTop + block.heightPt + additionalFootnoteHeight;
|
|
2661
3411
|
const effectiveRemainingHeight = remainingHeight - currentFootnoteHeight;
|
|
2662
3412
|
const bodyContentUsed = effectiveContentHeight - remainingHeight;
|
|
@@ -2891,7 +3641,7 @@ var PaginationEngine = class {
|
|
|
2891
3641
|
context
|
|
2892
3642
|
);
|
|
2893
3643
|
const widthPercent = this.anchorNumber(anchor, "width-percent");
|
|
2894
|
-
if (widthPercent !== void 0) {
|
|
3644
|
+
if (widthPercent !== void 0 && widthPercent > 0) {
|
|
2895
3645
|
anchor.style.width = `${widthReference.size * widthPercent / 100}pt`;
|
|
2896
3646
|
}
|
|
2897
3647
|
const heightReference = this.verticalAnchorReference(
|
|
@@ -2901,7 +3651,7 @@ var PaginationEngine = class {
|
|
|
2901
3651
|
context
|
|
2902
3652
|
);
|
|
2903
3653
|
const heightPercent = this.anchorNumber(anchor, "height-percent");
|
|
2904
|
-
if (heightPercent !== void 0 && anchor.dataset.docxAnchorAutofit !== "true") {
|
|
3654
|
+
if (heightPercent !== void 0 && heightPercent > 0 && anchor.dataset.docxAnchorAutofit !== "true") {
|
|
2905
3655
|
anchor.style.height = `${heightReference.size * heightPercent / 100}pt`;
|
|
2906
3656
|
}
|
|
2907
3657
|
const sizedRect = anchor.getBoundingClientRect();
|
|
@@ -2933,7 +3683,12 @@ var PaginationEngine = class {
|
|
|
2933
3683
|
this.anchorNumber(anchor, "v-offset"),
|
|
2934
3684
|
pageNumber
|
|
2935
3685
|
);
|
|
3686
|
+
const hostElement = anchor.parentElement?.closest("[data-source-anchor-id]");
|
|
3687
|
+
const hostAnchorId = hostElement?.dataset.sourceAnchorId;
|
|
3688
|
+
if (hostAnchorId) anchor.setAttribute(DRAWING_HOST_ATTRIBUTE, hostAnchorId);
|
|
2936
3689
|
pageBox.appendChild(anchor);
|
|
3690
|
+
if (hostElement && rendersOnlyItsLine(hostElement))
|
|
3691
|
+
hostElement.setAttribute(HOSTS_PROMOTED_DRAWING_ATTRIBUTE, "true");
|
|
2937
3692
|
anchor.style.position = "absolute";
|
|
2938
3693
|
anchor.style.left = `${left}pt`;
|
|
2939
3694
|
anchor.style.top = `${top}pt`;
|
|
@@ -3171,6 +3926,20 @@ var PaginationEngine = class {
|
|
|
3171
3926
|
contentArea.style.height = `${Math.max(0, contentAreaHeight - notesHeightPt)}pt`;
|
|
3172
3927
|
}
|
|
3173
3928
|
}
|
|
3929
|
+
alignExactLineBaselines(contentArea);
|
|
3930
|
+
const paper = pageBox.getBoundingClientRect();
|
|
3931
|
+
const header = pageBox.querySelector(`.${this.cssPrefix}header`);
|
|
3932
|
+
const footer = pageBox.querySelector(`.${this.cssPrefix}footer`);
|
|
3933
|
+
preserveExactLineInk(
|
|
3934
|
+
contentArea,
|
|
3935
|
+
Math.max(paper.top, header?.getBoundingClientRect().bottom ?? paper.top),
|
|
3936
|
+
Math.min(
|
|
3937
|
+
paper.bottom,
|
|
3938
|
+
footer?.getBoundingClientRect().top ?? paper.bottom,
|
|
3939
|
+
notesEl?.getBoundingClientRect().top ?? paper.bottom
|
|
3940
|
+
),
|
|
3941
|
+
paper
|
|
3942
|
+
);
|
|
3174
3943
|
return {
|
|
3175
3944
|
pageNumber,
|
|
3176
3945
|
sectionIndex,
|
|
@@ -3222,6 +3991,22 @@ var WorkerOperationError = class extends Error {
|
|
|
3222
3991
|
function workerError(message, code) {
|
|
3223
3992
|
return new WorkerOperationError(message || "Unknown error", code);
|
|
3224
3993
|
}
|
|
3994
|
+
function requireField(response, field) {
|
|
3995
|
+
const value = response[field];
|
|
3996
|
+
if (value === void 0 || value === null) {
|
|
3997
|
+
throw workerError(
|
|
3998
|
+
`The worker's "${response.type ?? "unknown"}" response reported success without its ${String(field)}`,
|
|
3999
|
+
"missing_result"
|
|
4000
|
+
);
|
|
4001
|
+
}
|
|
4002
|
+
return value;
|
|
4003
|
+
}
|
|
4004
|
+
function checkTimeout(timeoutMs, name) {
|
|
4005
|
+
if (timeoutMs !== void 0 && !(Number.isFinite(timeoutMs) && timeoutMs > 0)) {
|
|
4006
|
+
throw new RangeError(`${name} must be a positive, finite number of milliseconds; got ${timeoutMs}`);
|
|
4007
|
+
}
|
|
4008
|
+
return timeoutMs;
|
|
4009
|
+
}
|
|
3225
4010
|
function workerErrorCode(error) {
|
|
3226
4011
|
return error instanceof WorkerOperationError ? error.code : void 0;
|
|
3227
4012
|
}
|
|
@@ -3250,6 +4035,7 @@ async function createWorkerDocxodus(options) {
|
|
|
3250
4035
|
if (options?.signal?.aborted) {
|
|
3251
4036
|
throw new Error("Worker creation aborted");
|
|
3252
4037
|
}
|
|
4038
|
+
const requestTimeoutMs = checkTimeout(options?.requestTimeoutMs, "requestTimeoutMs");
|
|
3253
4039
|
const wasmBasePath = options?.wasmBasePath ?? deriveWasmBasePath();
|
|
3254
4040
|
const workerScriptPath = new URL("./docxodus.worker.js", import.meta.url).href;
|
|
3255
4041
|
const worker = new Worker(workerScriptPath, { type: "module" });
|
|
@@ -3292,13 +4078,54 @@ async function createWorkerDocxodus(options) {
|
|
|
3292
4078
|
worker.onerror = (error) => {
|
|
3293
4079
|
stopWorker(`Worker error: ${error.message}`);
|
|
3294
4080
|
};
|
|
3295
|
-
|
|
4081
|
+
worker.onmessageerror = () => {
|
|
4082
|
+
for (const pending of [...pendingRequests.values()]) {
|
|
4083
|
+
pending.reject(workerError(
|
|
4084
|
+
"A worker response could not be deserialized, so every request in flight was rejected",
|
|
4085
|
+
"message_error"
|
|
4086
|
+
));
|
|
4087
|
+
}
|
|
4088
|
+
};
|
|
4089
|
+
function sendRequest(request, transfer, requestOptions) {
|
|
3296
4090
|
return new Promise((resolve, reject) => {
|
|
3297
4091
|
if (!isWorkerActive) {
|
|
3298
4092
|
reject(new Error("Worker has been terminated"));
|
|
3299
4093
|
return;
|
|
3300
4094
|
}
|
|
3301
|
-
|
|
4095
|
+
const signal = requestOptions?.signal;
|
|
4096
|
+
if (signal?.aborted) {
|
|
4097
|
+
reject(workerError(`The worker request "${request.type}" was aborted before it was sent`, "aborted"));
|
|
4098
|
+
return;
|
|
4099
|
+
}
|
|
4100
|
+
const timeoutMs = requestOptions?.timeoutMs ?? requestTimeoutMs;
|
|
4101
|
+
let timer;
|
|
4102
|
+
let onAbort;
|
|
4103
|
+
const finish = () => {
|
|
4104
|
+
pendingRequests.delete(request.id);
|
|
4105
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
4106
|
+
if (onAbort) signal?.removeEventListener("abort", onAbort);
|
|
4107
|
+
};
|
|
4108
|
+
pendingRequests.set(request.id, {
|
|
4109
|
+
resolve: (value) => {
|
|
4110
|
+
finish();
|
|
4111
|
+
resolve(value);
|
|
4112
|
+
},
|
|
4113
|
+
reject: (error) => {
|
|
4114
|
+
finish();
|
|
4115
|
+
reject(error);
|
|
4116
|
+
}
|
|
4117
|
+
});
|
|
4118
|
+
const fail2 = (error) => pendingRequests.get(request.id)?.reject(error);
|
|
4119
|
+
if (timeoutMs !== void 0) {
|
|
4120
|
+
timer = setTimeout(() => fail2(workerError(
|
|
4121
|
+
`The worker request "${request.type}" timed out after ${timeoutMs} ms`,
|
|
4122
|
+
"timeout"
|
|
4123
|
+
)), timeoutMs);
|
|
4124
|
+
}
|
|
4125
|
+
if (signal) {
|
|
4126
|
+
onAbort = () => fail2(workerError(`The worker request "${request.type}" was aborted`, "aborted"));
|
|
4127
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
4128
|
+
}
|
|
3302
4129
|
try {
|
|
3303
4130
|
if (transfer && transfer.length > 0) {
|
|
3304
4131
|
worker.postMessage(request, transfer);
|
|
@@ -3306,8 +4133,7 @@ async function createWorkerDocxodus(options) {
|
|
|
3306
4133
|
worker.postMessage(request);
|
|
3307
4134
|
}
|
|
3308
4135
|
} catch (error) {
|
|
3309
|
-
|
|
3310
|
-
reject(error instanceof Error ? error : new Error(String(error)));
|
|
4136
|
+
fail2(error instanceof Error ? error : new Error(String(error)));
|
|
3311
4137
|
}
|
|
3312
4138
|
});
|
|
3313
4139
|
}
|
|
@@ -3321,391 +4147,349 @@ async function createWorkerDocxodus(options) {
|
|
|
3321
4147
|
stopWorker("Worker initialization failed");
|
|
3322
4148
|
throw error;
|
|
3323
4149
|
}
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
leftBytes,
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
{ id: generateId(), type: "validateExternalAnnotations", documentBytes: bytes, annotationSet },
|
|
3408
|
-
[bytes.buffer]
|
|
3409
|
-
);
|
|
3410
|
-
if (!response.success || !response.validation) {
|
|
3411
|
-
throw new Error(response.error ?? "validateExternalAnnotations failed");
|
|
3412
|
-
}
|
|
3413
|
-
return response.validation;
|
|
3414
|
-
},
|
|
3415
|
-
async projectAnnotationsOntoHtml(html, annotationSet, projectionOptions) {
|
|
3416
|
-
const response = await sendRequest({
|
|
3417
|
-
id: generateId(),
|
|
3418
|
-
type: "projectAnnotationsOntoHtml",
|
|
3419
|
-
html,
|
|
3420
|
-
annotationSet,
|
|
3421
|
-
projectionOptions
|
|
3422
|
-
});
|
|
3423
|
-
if (!response.success || response.html === void 0) {
|
|
3424
|
-
throw new Error(response.error ?? "projectAnnotationsOntoHtml failed");
|
|
3425
|
-
}
|
|
3426
|
-
return response.html;
|
|
3427
|
-
},
|
|
3428
|
-
async convertDocxToHtmlWithExternalAnnotations(document2, annotationSet, conversionOptions2, projectionOptions) {
|
|
3429
|
-
const bytes = await toBytes(document2);
|
|
3430
|
-
const response = await sendRequest(
|
|
3431
|
-
{
|
|
4150
|
+
const createApi = (requestOptions) => {
|
|
4151
|
+
const send = (request, transfer) => sendRequest(request, transfer, requestOptions);
|
|
4152
|
+
return {
|
|
4153
|
+
async generatePackageManifest(document2, limits) {
|
|
4154
|
+
const bytes = await toBytes(document2);
|
|
4155
|
+
const response = await send(
|
|
4156
|
+
{
|
|
4157
|
+
id: generateId(),
|
|
4158
|
+
type: "generatePackageManifest",
|
|
4159
|
+
documentBytes: bytes,
|
|
4160
|
+
limits,
|
|
4161
|
+
representation: "object"
|
|
4162
|
+
},
|
|
4163
|
+
[bytes.buffer]
|
|
4164
|
+
);
|
|
4165
|
+
return requireField(response, "manifest");
|
|
4166
|
+
},
|
|
4167
|
+
async verifyDeliverable(document2, baseline, request) {
|
|
4168
|
+
const bytes = await toBytes(document2);
|
|
4169
|
+
const baselineBytes = baseline === void 0 ? void 0 : await toBytes(baseline);
|
|
4170
|
+
const transfer = [bytes.buffer];
|
|
4171
|
+
if (baselineBytes !== void 0) transfer.push(baselineBytes.buffer);
|
|
4172
|
+
const response = await send(
|
|
4173
|
+
{
|
|
4174
|
+
id: generateId(),
|
|
4175
|
+
type: "verifyDeliverable",
|
|
4176
|
+
documentBytes: bytes,
|
|
4177
|
+
baselineBytes,
|
|
4178
|
+
requestJson: request === void 0 ? void 0 : serializeVerificationRequest(request)
|
|
4179
|
+
},
|
|
4180
|
+
transfer
|
|
4181
|
+
);
|
|
4182
|
+
return requireField(response, "verification");
|
|
4183
|
+
},
|
|
4184
|
+
async proveRedlineReversibility(baseline, intendedFinal, redline) {
|
|
4185
|
+
const baselineBytes = await toBytes(baseline);
|
|
4186
|
+
const intendedFinalBytes = await toBytes(intendedFinal);
|
|
4187
|
+
const redlineBytes = await toBytes(redline);
|
|
4188
|
+
const response = await send(
|
|
4189
|
+
{
|
|
4190
|
+
id: generateId(),
|
|
4191
|
+
type: "proveRedlineReversibility",
|
|
4192
|
+
baselineBytes,
|
|
4193
|
+
intendedFinalBytes,
|
|
4194
|
+
redlineBytes
|
|
4195
|
+
},
|
|
4196
|
+
[baselineBytes.buffer, intendedFinalBytes.buffer, redlineBytes.buffer]
|
|
4197
|
+
);
|
|
4198
|
+
return requireField(response, "proof");
|
|
4199
|
+
},
|
|
4200
|
+
async getSemanticChanges(left, right, settings) {
|
|
4201
|
+
const leftBytes = await toBytes(left);
|
|
4202
|
+
const rightBytes = await toBytes(right);
|
|
4203
|
+
const response = await send(
|
|
4204
|
+
{
|
|
4205
|
+
id: generateId(),
|
|
4206
|
+
type: "getSemanticChanges",
|
|
4207
|
+
leftBytes,
|
|
4208
|
+
rightBytes,
|
|
4209
|
+
settings
|
|
4210
|
+
},
|
|
4211
|
+
[leftBytes.buffer, rightBytes.buffer]
|
|
4212
|
+
);
|
|
4213
|
+
return requireField(response, "semanticChanges");
|
|
4214
|
+
},
|
|
4215
|
+
async createExternalAnnotationSet(document2, documentId) {
|
|
4216
|
+
const bytes = await toBytes(document2);
|
|
4217
|
+
const response = await send(
|
|
4218
|
+
{ id: generateId(), type: "createExternalAnnotationSet", documentBytes: bytes, documentId },
|
|
4219
|
+
[bytes.buffer]
|
|
4220
|
+
);
|
|
4221
|
+
return requireField(response, "annotationSet");
|
|
4222
|
+
},
|
|
4223
|
+
async validateExternalAnnotations(document2, annotationSet) {
|
|
4224
|
+
const bytes = await toBytes(document2);
|
|
4225
|
+
const response = await send(
|
|
4226
|
+
{ id: generateId(), type: "validateExternalAnnotations", documentBytes: bytes, annotationSet },
|
|
4227
|
+
[bytes.buffer]
|
|
4228
|
+
);
|
|
4229
|
+
return requireField(response, "validation");
|
|
4230
|
+
},
|
|
4231
|
+
async projectAnnotationsOntoHtml(html, annotationSet, projectionOptions) {
|
|
4232
|
+
const response = await send({
|
|
3432
4233
|
id: generateId(),
|
|
3433
|
-
type: "
|
|
3434
|
-
|
|
4234
|
+
type: "projectAnnotationsOntoHtml",
|
|
4235
|
+
html,
|
|
3435
4236
|
annotationSet,
|
|
3436
|
-
conversionOptions: conversionOptions2,
|
|
3437
4237
|
projectionOptions
|
|
3438
|
-
}
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
},
|
|
3446
|
-
async exportToOpenContract(document2) {
|
|
3447
|
-
const bytes = await toBytes(document2);
|
|
3448
|
-
const response = await sendRequest(
|
|
3449
|
-
{ id: generateId(), type: "exportToOpenContract", documentBytes: bytes },
|
|
3450
|
-
[bytes.buffer]
|
|
3451
|
-
);
|
|
3452
|
-
if (!response.success || !response.export) {
|
|
3453
|
-
throw new Error(response.error ?? "exportToOpenContract failed");
|
|
3454
|
-
}
|
|
3455
|
-
return response.export;
|
|
3456
|
-
},
|
|
3457
|
-
async generatePackageManifestJson(document2, limits) {
|
|
3458
|
-
const bytes = await toBytes(document2);
|
|
3459
|
-
const response = await sendRequest(
|
|
3460
|
-
{
|
|
3461
|
-
id: generateId(),
|
|
3462
|
-
type: "generatePackageManifest",
|
|
3463
|
-
documentBytes: bytes,
|
|
3464
|
-
limits,
|
|
3465
|
-
representation: "json"
|
|
3466
|
-
},
|
|
3467
|
-
[bytes.buffer]
|
|
3468
|
-
);
|
|
3469
|
-
if (response.manifestJson === void 0) {
|
|
3470
|
-
throw new Error("Package manifest worker response omitted canonical JSON");
|
|
3471
|
-
}
|
|
3472
|
-
return response.manifestJson;
|
|
3473
|
-
},
|
|
3474
|
-
async projectReviewProfile(document2, profile, maximumOutputBytes) {
|
|
3475
|
-
const bytes = await toBytes(document2);
|
|
3476
|
-
const response = await sendRequest(
|
|
3477
|
-
{
|
|
3478
|
-
id: generateId(),
|
|
3479
|
-
type: "projectReviewProfile",
|
|
3480
|
-
documentBytes: bytes,
|
|
3481
|
-
profile,
|
|
3482
|
-
maximumOutputBytes
|
|
3483
|
-
},
|
|
3484
|
-
[bytes.buffer]
|
|
3485
|
-
);
|
|
3486
|
-
if (!response.documentBytes || response.documentBytes.byteLength === 0) {
|
|
3487
|
-
throw new Error(`Failed to derive the ${profile} review profile`);
|
|
3488
|
-
}
|
|
3489
|
-
return response.documentBytes;
|
|
3490
|
-
},
|
|
3491
|
-
async convertDocxToHtml(document2, options2, maximumOutputBytes) {
|
|
3492
|
-
const bytes = await toBytes(document2);
|
|
3493
|
-
const response = await sendRequest(
|
|
3494
|
-
{
|
|
3495
|
-
id: generateId(),
|
|
3496
|
-
type: "convertDocxToHtml",
|
|
3497
|
-
documentBytes: bytes,
|
|
3498
|
-
options: options2,
|
|
3499
|
-
maximumOutputBytes
|
|
3500
|
-
},
|
|
3501
|
-
[bytes.buffer]
|
|
3502
|
-
);
|
|
3503
|
-
return response.html;
|
|
3504
|
-
},
|
|
3505
|
-
async compareDocuments(original, modified, options2) {
|
|
3506
|
-
const originalBytes = await toBytes(original);
|
|
3507
|
-
const modifiedBytes = await toBytes(modified);
|
|
3508
|
-
const response = await sendRequest(
|
|
3509
|
-
{
|
|
3510
|
-
id: generateId(),
|
|
3511
|
-
type: "compareDocuments",
|
|
3512
|
-
originalBytes,
|
|
3513
|
-
modifiedBytes,
|
|
3514
|
-
options: options2
|
|
3515
|
-
},
|
|
3516
|
-
[originalBytes.buffer, modifiedBytes.buffer]
|
|
3517
|
-
);
|
|
3518
|
-
return response.documentBytes;
|
|
3519
|
-
},
|
|
3520
|
-
async compareDocumentsToHtml(original, modified, options2) {
|
|
3521
|
-
const originalBytes = await toBytes(original);
|
|
3522
|
-
const modifiedBytes = await toBytes(modified);
|
|
3523
|
-
const response = await sendRequest(
|
|
3524
|
-
{
|
|
3525
|
-
id: generateId(),
|
|
3526
|
-
type: "compareDocumentsToHtml",
|
|
3527
|
-
originalBytes,
|
|
3528
|
-
modifiedBytes,
|
|
3529
|
-
options: options2
|
|
3530
|
-
},
|
|
3531
|
-
[originalBytes.buffer, modifiedBytes.buffer]
|
|
3532
|
-
);
|
|
3533
|
-
return response.html;
|
|
3534
|
-
},
|
|
3535
|
-
async getRevisions(document2) {
|
|
3536
|
-
const bytes = await toBytes(document2);
|
|
3537
|
-
const response = await sendRequest(
|
|
3538
|
-
{
|
|
3539
|
-
id: generateId(),
|
|
3540
|
-
type: "getRevisions",
|
|
3541
|
-
documentBytes: bytes
|
|
3542
|
-
},
|
|
3543
|
-
[bytes.buffer]
|
|
3544
|
-
);
|
|
3545
|
-
return response.revisions;
|
|
3546
|
-
},
|
|
3547
|
-
async getComments(document2) {
|
|
3548
|
-
const bytes = await toBytes(document2);
|
|
3549
|
-
const response = await sendRequest(
|
|
3550
|
-
{ id: generateId(), type: "getComments", documentBytes: bytes },
|
|
3551
|
-
[bytes.buffer]
|
|
3552
|
-
);
|
|
3553
|
-
return response.comments;
|
|
3554
|
-
},
|
|
3555
|
-
async getDocumentMetadata(document2) {
|
|
3556
|
-
const bytes = await toBytes(document2);
|
|
3557
|
-
const response = await sendRequest(
|
|
3558
|
-
{
|
|
3559
|
-
id: generateId(),
|
|
3560
|
-
type: "getDocumentMetadata",
|
|
3561
|
-
documentBytes: bytes
|
|
3562
|
-
},
|
|
3563
|
-
[bytes.buffer]
|
|
3564
|
-
);
|
|
3565
|
-
return response.metadata;
|
|
3566
|
-
},
|
|
3567
|
-
async getVersion() {
|
|
3568
|
-
const response = await sendRequest({
|
|
3569
|
-
id: generateId(),
|
|
3570
|
-
type: "getVersion"
|
|
3571
|
-
});
|
|
3572
|
-
return response.version;
|
|
3573
|
-
},
|
|
3574
|
-
prepare() {
|
|
3575
|
-
if (preparePromise) {
|
|
3576
|
-
return preparePromise;
|
|
3577
|
-
}
|
|
3578
|
-
preparePromise = sendRequest({
|
|
3579
|
-
id: generateId(),
|
|
3580
|
-
type: "prepare"
|
|
3581
|
-
}).then(() => void 0);
|
|
3582
|
-
preparePromise.catch(() => {
|
|
3583
|
-
preparePromise = null;
|
|
3584
|
-
});
|
|
3585
|
-
return preparePromise;
|
|
3586
|
-
},
|
|
3587
|
-
async openDocxSession(document2, settings) {
|
|
3588
|
-
const bytes = await toBytes(document2);
|
|
3589
|
-
const settingsJson = settings ? JSON.stringify(settings) : "";
|
|
3590
|
-
const openResponse = await sendRequest(
|
|
3591
|
-
{
|
|
3592
|
-
id: generateId(),
|
|
3593
|
-
type: "sessionOpen",
|
|
3594
|
-
documentBytes: bytes,
|
|
3595
|
-
settingsJson
|
|
3596
|
-
},
|
|
3597
|
-
[bytes.buffer]
|
|
3598
|
-
);
|
|
3599
|
-
if (!openResponse.success || openResponse.handle === void 0) {
|
|
3600
|
-
throw new Error(
|
|
3601
|
-
`Failed to open worker DocxSession: ${openResponse.error ?? "unknown error"}`
|
|
3602
|
-
);
|
|
3603
|
-
}
|
|
3604
|
-
const handle = openResponse.handle;
|
|
3605
|
-
return {
|
|
3606
|
-
async getPackageManifest() {
|
|
3607
|
-
const res = await sendRequest({
|
|
4238
|
+
});
|
|
4239
|
+
return requireField(response, "html");
|
|
4240
|
+
},
|
|
4241
|
+
async convertDocxToHtmlWithExternalAnnotations(document2, annotationSet, conversionOptions2, projectionOptions) {
|
|
4242
|
+
const bytes = await toBytes(document2);
|
|
4243
|
+
const response = await send(
|
|
4244
|
+
{
|
|
3608
4245
|
id: generateId(),
|
|
3609
|
-
type: "
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
}
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
4246
|
+
type: "convertDocxToHtmlWithExternalAnnotations",
|
|
4247
|
+
documentBytes: bytes,
|
|
4248
|
+
annotationSet,
|
|
4249
|
+
conversionOptions: conversionOptions2,
|
|
4250
|
+
projectionOptions
|
|
4251
|
+
},
|
|
4252
|
+
[bytes.buffer]
|
|
4253
|
+
);
|
|
4254
|
+
return requireField(response, "html");
|
|
4255
|
+
},
|
|
4256
|
+
async exportToOpenContract(document2) {
|
|
4257
|
+
const bytes = await toBytes(document2);
|
|
4258
|
+
const response = await send(
|
|
4259
|
+
{ id: generateId(), type: "exportToOpenContract", documentBytes: bytes },
|
|
4260
|
+
[bytes.buffer]
|
|
4261
|
+
);
|
|
4262
|
+
return requireField(response, "export");
|
|
4263
|
+
},
|
|
4264
|
+
async generatePackageManifestJson(document2, limits) {
|
|
4265
|
+
const bytes = await toBytes(document2);
|
|
4266
|
+
const response = await send(
|
|
4267
|
+
{
|
|
3619
4268
|
id: generateId(),
|
|
3620
|
-
type: "
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
4269
|
+
type: "generatePackageManifest",
|
|
4270
|
+
documentBytes: bytes,
|
|
4271
|
+
limits,
|
|
4272
|
+
representation: "json"
|
|
4273
|
+
},
|
|
4274
|
+
[bytes.buffer]
|
|
4275
|
+
);
|
|
4276
|
+
return requireField(response, "manifestJson");
|
|
4277
|
+
},
|
|
4278
|
+
async projectReviewProfile(document2, profile, maximumOutputBytes) {
|
|
4279
|
+
const bytes = await toBytes(document2);
|
|
4280
|
+
const response = await send(
|
|
4281
|
+
{
|
|
3630
4282
|
id: generateId(),
|
|
3631
|
-
type: "
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
4283
|
+
type: "projectReviewProfile",
|
|
4284
|
+
documentBytes: bytes,
|
|
4285
|
+
profile,
|
|
4286
|
+
maximumOutputBytes
|
|
4287
|
+
},
|
|
4288
|
+
[bytes.buffer]
|
|
4289
|
+
);
|
|
4290
|
+
if (!response.documentBytes || response.documentBytes.byteLength === 0) {
|
|
4291
|
+
throw new Error(`Failed to derive the ${profile} review profile`);
|
|
4292
|
+
}
|
|
4293
|
+
return response.documentBytes;
|
|
4294
|
+
},
|
|
4295
|
+
async convertDocxToHtml(document2, options2, maximumOutputBytes) {
|
|
4296
|
+
const bytes = await toBytes(document2);
|
|
4297
|
+
const response = await send(
|
|
4298
|
+
{
|
|
3642
4299
|
id: generateId(),
|
|
3643
|
-
type: "
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
4300
|
+
type: "convertDocxToHtml",
|
|
4301
|
+
documentBytes: bytes,
|
|
4302
|
+
options: options2,
|
|
4303
|
+
maximumOutputBytes
|
|
4304
|
+
},
|
|
4305
|
+
[bytes.buffer]
|
|
4306
|
+
);
|
|
4307
|
+
return requireField(response, "html");
|
|
4308
|
+
},
|
|
4309
|
+
async compareDocuments(original, modified, options2) {
|
|
4310
|
+
const originalBytes = await toBytes(original);
|
|
4311
|
+
const modifiedBytes = await toBytes(modified);
|
|
4312
|
+
const response = await send(
|
|
4313
|
+
{
|
|
3656
4314
|
id: generateId(),
|
|
3657
|
-
type: "
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
4315
|
+
type: "compareDocuments",
|
|
4316
|
+
originalBytes,
|
|
4317
|
+
modifiedBytes,
|
|
4318
|
+
options: options2
|
|
4319
|
+
},
|
|
4320
|
+
[originalBytes.buffer, modifiedBytes.buffer]
|
|
4321
|
+
);
|
|
4322
|
+
return requireField(response, "documentBytes");
|
|
4323
|
+
},
|
|
4324
|
+
async compareDocumentsToHtml(original, modified, options2) {
|
|
4325
|
+
const originalBytes = await toBytes(original);
|
|
4326
|
+
const modifiedBytes = await toBytes(modified);
|
|
4327
|
+
const response = await send(
|
|
4328
|
+
{
|
|
3668
4329
|
id: generateId(),
|
|
3669
|
-
type: "
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
}
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
4330
|
+
type: "compareDocumentsToHtml",
|
|
4331
|
+
originalBytes,
|
|
4332
|
+
modifiedBytes,
|
|
4333
|
+
options: options2
|
|
4334
|
+
},
|
|
4335
|
+
[originalBytes.buffer, modifiedBytes.buffer]
|
|
4336
|
+
);
|
|
4337
|
+
return requireField(response, "html");
|
|
4338
|
+
},
|
|
4339
|
+
async getRevisions(document2) {
|
|
4340
|
+
const bytes = await toBytes(document2);
|
|
4341
|
+
const response = await send(
|
|
4342
|
+
{
|
|
3681
4343
|
id: generateId(),
|
|
3682
|
-
type: "
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
4344
|
+
type: "getRevisions",
|
|
4345
|
+
documentBytes: bytes
|
|
4346
|
+
},
|
|
4347
|
+
[bytes.buffer]
|
|
4348
|
+
);
|
|
4349
|
+
return requireField(response, "revisions");
|
|
4350
|
+
},
|
|
4351
|
+
async getComments(document2) {
|
|
4352
|
+
const bytes = await toBytes(document2);
|
|
4353
|
+
const response = await send(
|
|
4354
|
+
{ id: generateId(), type: "getComments", documentBytes: bytes },
|
|
4355
|
+
[bytes.buffer]
|
|
4356
|
+
);
|
|
4357
|
+
return requireField(response, "comments");
|
|
4358
|
+
},
|
|
4359
|
+
async getDocumentMetadata(document2) {
|
|
4360
|
+
const bytes = await toBytes(document2);
|
|
4361
|
+
const response = await send(
|
|
4362
|
+
{
|
|
3695
4363
|
id: generateId(),
|
|
3696
|
-
type: "
|
|
3697
|
-
|
|
3698
|
-
}
|
|
4364
|
+
type: "getDocumentMetadata",
|
|
4365
|
+
documentBytes: bytes
|
|
4366
|
+
},
|
|
4367
|
+
[bytes.buffer]
|
|
4368
|
+
);
|
|
4369
|
+
return requireField(response, "metadata");
|
|
4370
|
+
},
|
|
4371
|
+
async getVersion() {
|
|
4372
|
+
const response = await send({
|
|
4373
|
+
id: generateId(),
|
|
4374
|
+
type: "getVersion"
|
|
4375
|
+
});
|
|
4376
|
+
return requireField(response, "version");
|
|
4377
|
+
},
|
|
4378
|
+
prepare() {
|
|
4379
|
+
if (preparePromise) {
|
|
4380
|
+
return preparePromise;
|
|
3699
4381
|
}
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
4382
|
+
preparePromise = send({
|
|
4383
|
+
id: generateId(),
|
|
4384
|
+
type: "prepare"
|
|
4385
|
+
}).then(() => void 0);
|
|
4386
|
+
preparePromise.catch(() => {
|
|
4387
|
+
preparePromise = null;
|
|
4388
|
+
});
|
|
4389
|
+
return preparePromise;
|
|
4390
|
+
},
|
|
4391
|
+
async openDocxSession(document2, settings) {
|
|
4392
|
+
const bytes = await toBytes(document2);
|
|
4393
|
+
const settingsJson = settings ? JSON.stringify(settings) : "";
|
|
4394
|
+
const openResponse = await send(
|
|
4395
|
+
{
|
|
4396
|
+
id: generateId(),
|
|
4397
|
+
type: "sessionOpen",
|
|
4398
|
+
documentBytes: bytes,
|
|
4399
|
+
settingsJson
|
|
4400
|
+
},
|
|
4401
|
+
[bytes.buffer]
|
|
4402
|
+
);
|
|
4403
|
+
const handle = requireField(openResponse, "handle");
|
|
4404
|
+
return {
|
|
4405
|
+
async getPackageManifest() {
|
|
4406
|
+
const res = await send({
|
|
4407
|
+
id: generateId(),
|
|
4408
|
+
type: "sessionGetPackageManifest",
|
|
4409
|
+
handle
|
|
4410
|
+
});
|
|
4411
|
+
return requireField(res, "manifest");
|
|
4412
|
+
},
|
|
4413
|
+
async getSemanticChanges() {
|
|
4414
|
+
const res = await send({
|
|
4415
|
+
id: generateId(),
|
|
4416
|
+
type: "sessionGetSemanticChanges",
|
|
4417
|
+
handle
|
|
4418
|
+
});
|
|
4419
|
+
return requireField(res, "semanticChanges");
|
|
4420
|
+
},
|
|
4421
|
+
async verifyDeliverable(request) {
|
|
4422
|
+
const res = await send({
|
|
4423
|
+
id: generateId(),
|
|
4424
|
+
type: "sessionVerifyDeliverable",
|
|
4425
|
+
handle,
|
|
4426
|
+
requestJson: request === void 0 ? void 0 : serializeVerificationRequest(request)
|
|
4427
|
+
});
|
|
4428
|
+
return requireField(res, "verification");
|
|
4429
|
+
},
|
|
4430
|
+
async addAnnotation(anchorId, span, annotation) {
|
|
4431
|
+
const res = await send({
|
|
4432
|
+
id: generateId(),
|
|
4433
|
+
type: "sessionAddAnnotation",
|
|
4434
|
+
handle,
|
|
4435
|
+
anchorId,
|
|
4436
|
+
spanJson: span ? JSON.stringify(span) : "",
|
|
4437
|
+
annotationJson: JSON.stringify(annotation)
|
|
4438
|
+
});
|
|
4439
|
+
return requireField(res, "result");
|
|
4440
|
+
},
|
|
4441
|
+
async removeAnnotation(annotationId) {
|
|
4442
|
+
const res = await send({
|
|
4443
|
+
id: generateId(),
|
|
4444
|
+
type: "sessionRemoveAnnotation",
|
|
4445
|
+
handle,
|
|
4446
|
+
annotationId
|
|
4447
|
+
});
|
|
4448
|
+
return requireField(res, "result");
|
|
4449
|
+
},
|
|
4450
|
+
async updateAnnotation(annotationId, update) {
|
|
4451
|
+
const res = await send({
|
|
4452
|
+
id: generateId(),
|
|
4453
|
+
type: "sessionUpdateAnnotation",
|
|
4454
|
+
handle,
|
|
4455
|
+
annotationId,
|
|
4456
|
+
updateJson: JSON.stringify(update)
|
|
4457
|
+
});
|
|
4458
|
+
return requireField(res, "result");
|
|
4459
|
+
},
|
|
4460
|
+
async moveAnnotation(annotationId, newAnchorId, newSpan) {
|
|
4461
|
+
const res = await send({
|
|
4462
|
+
id: generateId(),
|
|
4463
|
+
type: "sessionMoveAnnotation",
|
|
4464
|
+
handle,
|
|
4465
|
+
annotationId,
|
|
4466
|
+
newAnchorId,
|
|
4467
|
+
newSpanJson: newSpan ? JSON.stringify(newSpan) : ""
|
|
4468
|
+
});
|
|
4469
|
+
return requireField(res, "result");
|
|
4470
|
+
},
|
|
4471
|
+
async close() {
|
|
4472
|
+
await sendRequest(
|
|
4473
|
+
{ id: generateId(), type: "sessionClose", handle },
|
|
4474
|
+
void 0,
|
|
4475
|
+
{ timeoutMs: requestOptions?.timeoutMs }
|
|
4476
|
+
);
|
|
4477
|
+
}
|
|
4478
|
+
};
|
|
4479
|
+
},
|
|
4480
|
+
terminate() {
|
|
4481
|
+
stopWorker("Worker terminated");
|
|
4482
|
+
},
|
|
4483
|
+
withRequestOptions(more) {
|
|
4484
|
+
checkTimeout(more.timeoutMs, "timeoutMs");
|
|
4485
|
+
return createApi({ ...requestOptions, ...more });
|
|
4486
|
+
},
|
|
4487
|
+
isActive() {
|
|
4488
|
+
return isWorkerActive;
|
|
4489
|
+
}
|
|
4490
|
+
};
|
|
3708
4491
|
};
|
|
4492
|
+
return createApi();
|
|
3709
4493
|
}
|
|
3710
4494
|
|
|
3711
4495
|
// src/font-contract.ts
|
|
@@ -4048,6 +4832,45 @@ function collectFontInventory(document2, limits) {
|
|
|
4048
4832
|
}
|
|
4049
4833
|
uses.push({ element, requestKey: key, originalStyle: element.getAttribute("style") });
|
|
4050
4834
|
}
|
|
4835
|
+
const requestsByStack = /* @__PURE__ */ new Map();
|
|
4836
|
+
for (const [key, request] of requests) {
|
|
4837
|
+
const stackKey = JSON.stringify([request.familyStack, request.familyKinds]);
|
|
4838
|
+
const list = requestsByStack.get(stackKey) ?? [];
|
|
4839
|
+
list.push({ key, request });
|
|
4840
|
+
requestsByStack.set(stackKey, list);
|
|
4841
|
+
}
|
|
4842
|
+
const closestRequestKey = (descriptor) => {
|
|
4843
|
+
const candidates = requestsByStack.get(JSON.stringify([descriptor.familyStack, descriptor.familyKinds]));
|
|
4844
|
+
if (!candidates) return void 0;
|
|
4845
|
+
let best;
|
|
4846
|
+
for (const { key, request } of candidates) {
|
|
4847
|
+
const distance = (request.style === descriptor.style ? 0 : 1e4) + Math.abs(request.weight - descriptor.weight) + Math.abs(request.stretch - descriptor.stretch);
|
|
4848
|
+
if (!best || distance < best.distance || distance === best.distance && compareText(key, best.key) < 0) {
|
|
4849
|
+
best = { key, distance };
|
|
4850
|
+
}
|
|
4851
|
+
}
|
|
4852
|
+
return best?.key;
|
|
4853
|
+
};
|
|
4854
|
+
const used = new Set(uses.map((use) => use.element));
|
|
4855
|
+
for (const element of Array.from(document2.body.querySelectorAll("*"))) {
|
|
4856
|
+
if (!(element instanceof view.HTMLElement) || used.has(element)) continue;
|
|
4857
|
+
if (/^(?:script|style|template|noscript)$/i.test(element.localName)) continue;
|
|
4858
|
+
if (!participatesInRendering(element, view)) continue;
|
|
4859
|
+
const computed = view.getComputedStyle(element);
|
|
4860
|
+
const parsedFamilies = parseCssFontFamilyTokens(computed.fontFamily);
|
|
4861
|
+
if (parsedFamilies.length === 0) continue;
|
|
4862
|
+
const descriptor = {
|
|
4863
|
+
familyStack: parsedFamilies.map(({ name }) => name),
|
|
4864
|
+
familyKinds: parsedFamilies.map(({ kind }) => kind),
|
|
4865
|
+
style: faceStyle(computed.fontStyle),
|
|
4866
|
+
weight: faceWeight(computed.fontWeight),
|
|
4867
|
+
stretch: faceStretch(computed.fontStretch)
|
|
4868
|
+
};
|
|
4869
|
+
const exactKey = requestKey(descriptor);
|
|
4870
|
+
const key = requests.has(exactKey) ? exactKey : closestRequestKey(descriptor);
|
|
4871
|
+
if (key === void 0) continue;
|
|
4872
|
+
uses.push({ element, requestKey: key, originalStyle: element.getAttribute("style") });
|
|
4873
|
+
}
|
|
4051
4874
|
const ordered = Array.from(requests, ([key, request]) => ({ key, request })).sort((left, right) => compareText(left.key, right.key));
|
|
4052
4875
|
const idByKey = /* @__PURE__ */ new Map();
|
|
4053
4876
|
const result = Object.freeze(ordered.map(({ key, request }, index) => {
|
|
@@ -4908,6 +5731,7 @@ var REPORT_SCHEMA = "https://docxodus.dev/schemas/render/render-report/v2";
|
|
|
4908
5731
|
var TEXT_ENCODER2 = new TextEncoder();
|
|
4909
5732
|
var ALLOWED_REVIEW_PROFILES = /* @__PURE__ */ new Set(["final", "original", "markup"]);
|
|
4910
5733
|
var ALLOWED_COMMENT_PROFILES = /* @__PURE__ */ new Set(["hidden", "inline", "endnotes", "margin"]);
|
|
5734
|
+
var ALLOWED_MARKUP_PRESENTATIONS = /* @__PURE__ */ new Set(["docxodus", "word"]);
|
|
4911
5735
|
var ALLOWED_UNSUPPORTED_POLICIES = /* @__PURE__ */ new Set(["warn", "strict"]);
|
|
4912
5736
|
var PACKAGE_LIMIT_FINDINGS = /* @__PURE__ */ new Set([
|
|
4913
5737
|
"entry_count_limit_exceeded",
|
|
@@ -4983,6 +5807,23 @@ function normalizeOptions(options) {
|
|
|
4983
5807
|
"Use unchanged source bytes for markup, or choose final/original."
|
|
4984
5808
|
);
|
|
4985
5809
|
}
|
|
5810
|
+
const markupPresentation = options.markupPresentation ?? "docxodus";
|
|
5811
|
+
if (!ALLOWED_MARKUP_PRESENTATIONS.has(markupPresentation)) {
|
|
5812
|
+
fail(
|
|
5813
|
+
"invalid_argument",
|
|
5814
|
+
"input_validation",
|
|
5815
|
+
"markupPresentation is invalid.",
|
|
5816
|
+
"Use docxodus or word."
|
|
5817
|
+
);
|
|
5818
|
+
}
|
|
5819
|
+
if (markupPresentation !== "docxodus" && options.reviewProfile !== "markup") {
|
|
5820
|
+
fail(
|
|
5821
|
+
"invalid_argument",
|
|
5822
|
+
"input_validation",
|
|
5823
|
+
"markupPresentation applies only to the markup profile.",
|
|
5824
|
+
"Omit markupPresentation, or use reviewProfile markup."
|
|
5825
|
+
);
|
|
5826
|
+
}
|
|
4986
5827
|
const unsupportedContent = options.unsupportedContent ?? "warn";
|
|
4987
5828
|
if (!ALLOWED_UNSUPPORTED_POLICIES.has(unsupportedContent)) {
|
|
4988
5829
|
fail(
|
|
@@ -5121,6 +5962,7 @@ function normalizeOptions(options) {
|
|
|
5121
5962
|
expectedSourceDigest: options.expectedSourceDigest,
|
|
5122
5963
|
reviewProfile: options.reviewProfile,
|
|
5123
5964
|
reviewProfileAlreadyApplied,
|
|
5965
|
+
markupPresentation,
|
|
5124
5966
|
commentProfile: options.commentProfile,
|
|
5125
5967
|
title,
|
|
5126
5968
|
unsupportedContent,
|
|
@@ -5169,6 +6011,22 @@ async function ownedBytes(document2, maximum, signal) {
|
|
|
5169
6011
|
function monotonicNow() {
|
|
5170
6012
|
return globalThis.performance?.now() ?? Date.now();
|
|
5171
6013
|
}
|
|
6014
|
+
function checkpointWordText(state) {
|
|
6015
|
+
if (state.signal?.aborted)
|
|
6016
|
+
fail(
|
|
6017
|
+
"operation_cancelled",
|
|
6018
|
+
state.phase,
|
|
6019
|
+
"Export was cancelled during text layout.",
|
|
6020
|
+
"Retry with a non-aborted signal."
|
|
6021
|
+
);
|
|
6022
|
+
if (monotonicNow() >= state.deadline)
|
|
6023
|
+
fail(
|
|
6024
|
+
"readiness_timeout",
|
|
6025
|
+
state.phase,
|
|
6026
|
+
"Export timed out during text layout.",
|
|
6027
|
+
"Increase timeoutMs or reduce document complexity."
|
|
6028
|
+
);
|
|
6029
|
+
}
|
|
5172
6030
|
async function runPhase(state, phase, pending, operation) {
|
|
5173
6031
|
state.phase = phase;
|
|
5174
6032
|
const started = monotonicNow();
|
|
@@ -5382,6 +6240,26 @@ async function boundedResponseBytes(response, maximum, label) {
|
|
|
5382
6240
|
}
|
|
5383
6241
|
return bytes;
|
|
5384
6242
|
}
|
|
6243
|
+
var hostVerifiedAssetManifestSha256;
|
|
6244
|
+
function attestHostVerifiedRuntimeAssets(manifestSha256) {
|
|
6245
|
+
if (typeof manifestSha256 !== "string" || !/^[0-9a-f]{64}$/.test(manifestSha256)) {
|
|
6246
|
+
fail(
|
|
6247
|
+
"unsupported_runtime",
|
|
6248
|
+
"wasm_initialization",
|
|
6249
|
+
"The host runtime asset attestation is not a SHA-256 digest.",
|
|
6250
|
+
"Use the bootstrap module @docxodus/export generates."
|
|
6251
|
+
);
|
|
6252
|
+
}
|
|
6253
|
+
if (hostVerifiedAssetManifestSha256 !== void 0 && hostVerifiedAssetManifestSha256 !== manifestSha256) {
|
|
6254
|
+
fail(
|
|
6255
|
+
"unsupported_runtime",
|
|
6256
|
+
"wasm_initialization",
|
|
6257
|
+
"The host runtime asset attestation may be set only once.",
|
|
6258
|
+
"Load one host bootstrap module per page."
|
|
6259
|
+
);
|
|
6260
|
+
}
|
|
6261
|
+
hostVerifiedAssetManifestSha256 = manifestSha256;
|
|
6262
|
+
}
|
|
5385
6263
|
async function loadRuntimeAssetIdentity(wasmBasePath, signal) {
|
|
5386
6264
|
const manifestUrl = new URL("./export-assets.json", import.meta.url);
|
|
5387
6265
|
const response = await globalThis.fetch(manifestUrl, {
|
|
@@ -5539,7 +6417,8 @@ async function loadRuntimeAssetIdentity(wasmBasePath, signal) {
|
|
|
5539
6417
|
"Deploy the bundle and asset graph from the same Docxodus build."
|
|
5540
6418
|
);
|
|
5541
6419
|
}
|
|
5542
|
-
const
|
|
6420
|
+
const hostVerified = hostVerifiedAssetManifestSha256 !== void 0 && await sha2562(graphBytes) === hostVerifiedAssetManifestSha256;
|
|
6421
|
+
const verifiedRuntimeAssets = hostVerified ? [] : assets.filter((entry) => entry.path === "./docxodus.worker.js" || entry.path.startsWith("./wasm/_framework/"));
|
|
5543
6422
|
const resolvedWasmBasePath = new URL(
|
|
5544
6423
|
wasmBasePath.endsWith("/") ? wasmBasePath : `${wasmBasePath}/`,
|
|
5545
6424
|
import.meta.url
|
|
@@ -6181,6 +7060,7 @@ function conversionOptions(options) {
|
|
|
6181
7060
|
renderFootnotesAndEndnotes: true,
|
|
6182
7061
|
renderHeadersAndFooters: true,
|
|
6183
7062
|
renderTrackedChanges: options.reviewProfile === "markup",
|
|
7063
|
+
revisionPresentation: options.markupPresentation === "word" ? 1 /* Word */ : 0 /* Docxodus */,
|
|
6184
7064
|
showDeletedContent: true,
|
|
6185
7065
|
renderMoveOperations: true,
|
|
6186
7066
|
renderUnsupportedContentPlaceholders: true,
|
|
@@ -7070,7 +7950,7 @@ function finalizePageTree(document2, pages, state, options) {
|
|
|
7070
7950
|
style.textContent = standaloneStyle(pages);
|
|
7071
7951
|
document2.head.appendChild(style);
|
|
7072
7952
|
}
|
|
7073
|
-
function
|
|
7953
|
+
function findClippedContent(document2) {
|
|
7074
7954
|
for (const footnotes of Array.from(document2.querySelectorAll(".page-footnotes"))) {
|
|
7075
7955
|
if (footnotes.scrollHeight <= footnotes.clientHeight + 1) continue;
|
|
7076
7956
|
const boundary = footnotes.getBoundingClientRect().bottom;
|
|
@@ -7084,24 +7964,33 @@ function assertNoClippedContent(document2) {
|
|
|
7084
7964
|
);
|
|
7085
7965
|
}
|
|
7086
7966
|
}
|
|
7967
|
+
const clipped = [];
|
|
7087
7968
|
for (const content of Array.from(document2.querySelectorAll(".page-content"))) {
|
|
7088
7969
|
if (content.scrollHeight > content.clientHeight + 1) {
|
|
7089
7970
|
const pageNumber = content.closest(".page-box")?.dataset.pageNumber ?? "unknown";
|
|
7090
7971
|
const boundary = content.getBoundingClientRect().bottom;
|
|
7091
|
-
const
|
|
7972
|
+
const overflowing = Array.from(content.querySelectorAll("*")).map((element) => ({
|
|
7092
7973
|
element,
|
|
7093
7974
|
bottom: element.getBoundingClientRect().bottom
|
|
7094
|
-
})).filter(({ bottom }) => bottom > boundary + 1).sort((left, right) => right.bottom - left.bottom)
|
|
7095
|
-
if (
|
|
7096
|
-
|
|
7097
|
-
|
|
7098
|
-
|
|
7099
|
-
|
|
7100
|
-
|
|
7101
|
-
);
|
|
7975
|
+
})).filter(({ element, bottom }) => bottom > boundary + 1 && !isPreservedExactLineInk(element, content)).sort((left, right) => right.bottom - left.bottom);
|
|
7976
|
+
if (overflowing.length === 0) continue;
|
|
7977
|
+
const overflow = overflowing.slice(0, 3).map(({ element, bottom }) => `${element.localName}${element.className ? `.${String(element.className).trim().replace(/\s+/g, ".")}` : ""} (+${(bottom - boundary).toFixed(1)}px)`);
|
|
7978
|
+
clipped.push({
|
|
7979
|
+
pageNumber: Number.parseInt(pageNumber, 10),
|
|
7980
|
+
overflowPx: overflowing[0].bottom - boundary,
|
|
7981
|
+
message: `Page ${pageNumber} body content is clipped (${content.scrollHeight}px scroll height in a ${content.clientHeight}px band; ${overflow.join(", ")}).`
|
|
7982
|
+
});
|
|
7102
7983
|
}
|
|
7103
7984
|
}
|
|
7985
|
+
return clipped;
|
|
7104
7986
|
}
|
|
7987
|
+
var MAX_BODY_REFLOW_ROUNDS = 3;
|
|
7988
|
+
var BodyReflowRequest = class extends Error {
|
|
7989
|
+
constructor() {
|
|
7990
|
+
super("body reflow requested");
|
|
7991
|
+
this.name = "BodyReflowRequest";
|
|
7992
|
+
}
|
|
7993
|
+
};
|
|
7105
7994
|
function treeSignature(document2, pages) {
|
|
7106
7995
|
const geometry = pages.map((page) => {
|
|
7107
7996
|
const rect = page.getBoundingClientRect();
|
|
@@ -7241,11 +8130,16 @@ function automaticResourceCount(document2) {
|
|
|
7241
8130
|
}
|
|
7242
8131
|
return { count, bytes };
|
|
7243
8132
|
}
|
|
8133
|
+
function markupPresentationField(options) {
|
|
8134
|
+
return options.markupPresentation === "word" ? { markupPresentation: "word" } : {};
|
|
8135
|
+
}
|
|
7244
8136
|
async function layoutDigestForOptions(options) {
|
|
7245
8137
|
const layoutContract = {
|
|
7246
8138
|
title: options.title,
|
|
7247
8139
|
reviewProfile: options.reviewProfile,
|
|
7248
8140
|
reviewProfileAlreadyApplied: options.reviewProfileAlreadyApplied,
|
|
8141
|
+
// Absent for the default, so default layouts keep the digest they had before the option.
|
|
8142
|
+
...markupPresentationField(options),
|
|
7249
8143
|
commentProfile: options.commentProfile,
|
|
7250
8144
|
pagination: {
|
|
7251
8145
|
mode: "paginated",
|
|
@@ -7349,28 +8243,6 @@ function storyForAnchor(anchorId) {
|
|
|
7349
8243
|
if (scope === "cmt") return "comment";
|
|
7350
8244
|
return "body";
|
|
7351
8245
|
}
|
|
7352
|
-
function visibleRectWithinPage(document2, element, page) {
|
|
7353
|
-
const pageRect = page.getBoundingClientRect();
|
|
7354
|
-
const rect = element.getBoundingClientRect();
|
|
7355
|
-
let left = Math.max(rect.left, pageRect.left);
|
|
7356
|
-
let top = Math.max(rect.top, pageRect.top);
|
|
7357
|
-
let right = Math.min(rect.right, pageRect.right);
|
|
7358
|
-
let bottom = Math.min(rect.bottom, pageRect.bottom);
|
|
7359
|
-
const clips = (value) => value === "hidden" || value === "clip" || value === "scroll" || value === "auto";
|
|
7360
|
-
for (let ancestor = element.parentElement; ancestor && ancestor !== page; ancestor = ancestor.parentElement) {
|
|
7361
|
-
const style = document2.defaultView.getComputedStyle(ancestor);
|
|
7362
|
-
const ancestorRect = ancestor.getBoundingClientRect();
|
|
7363
|
-
if (clips(style.overflowX)) {
|
|
7364
|
-
left = Math.max(left, ancestorRect.left);
|
|
7365
|
-
right = Math.min(right, ancestorRect.right);
|
|
7366
|
-
}
|
|
7367
|
-
if (clips(style.overflowY)) {
|
|
7368
|
-
top = Math.max(top, ancestorRect.top);
|
|
7369
|
-
bottom = Math.min(bottom, ancestorRect.bottom);
|
|
7370
|
-
}
|
|
7371
|
-
}
|
|
7372
|
-
return { left, top, right, bottom };
|
|
7373
|
-
}
|
|
7374
8246
|
function assertStandaloneResourceAudit(document2) {
|
|
7375
8247
|
if (document2.querySelector(
|
|
7376
8248
|
"script, iframe, object, embed, video, audio, source, track, link, base, meta[http-equiv='refresh' i]"
|
|
@@ -7475,8 +8347,7 @@ async function verifyOfflineReopen(hostDocument, html, expectedPageMap, state) {
|
|
|
7475
8347
|
)) {
|
|
7476
8348
|
const style = reopened.defaultView.getComputedStyle(element);
|
|
7477
8349
|
if (style.display === "none" || style.visibility === "hidden") continue;
|
|
7478
|
-
const rect = element
|
|
7479
|
-
const visible = visibleRectWithinPage(reopened, element, page);
|
|
8350
|
+
const { rect, visible } = measureRenderedSource(reopened.defaultView, element, page, pageRect);
|
|
7480
8351
|
if (rect.width <= 0 || rect.height <= 0 || visible.right <= visible.left || visible.bottom <= visible.top) continue;
|
|
7481
8352
|
const anchorId = element.dataset.sourceAnchorId;
|
|
7482
8353
|
actualFragments.push({
|
|
@@ -7529,6 +8400,7 @@ function reportBase(manifest, sourceBytes, derivedManifest, derivedBytes, option
|
|
|
7529
8400
|
options: {
|
|
7530
8401
|
reviewProfile: options.reviewProfile,
|
|
7531
8402
|
reviewProfileAlreadyApplied: options.reviewProfileAlreadyApplied,
|
|
8403
|
+
...markupPresentationField(options),
|
|
7532
8404
|
commentProfile: options.commentProfile,
|
|
7533
8405
|
title: options.title,
|
|
7534
8406
|
outputs: ["html"],
|
|
@@ -7779,6 +8651,8 @@ async function convertDocxToPaginatedHtml(document2, requestedOptions) {
|
|
|
7779
8651
|
});
|
|
7780
8652
|
preflightConvertedHtml(convertedHtml, options);
|
|
7781
8653
|
const attemptCheckpoint = checkpointAttemptState(state);
|
|
8654
|
+
const bodyBudgetReductionsPt = {};
|
|
8655
|
+
let bodyReflowRounds = 0;
|
|
7782
8656
|
let finalized;
|
|
7783
8657
|
let firstAttemptSignature;
|
|
7784
8658
|
let firstAttemptFontIdentity;
|
|
@@ -7849,6 +8723,8 @@ async function convertDocxToPaginatedHtml(document2, requestedOptions) {
|
|
|
7849
8723
|
"Use the paginated converter contract and report the malformed conversion output."
|
|
7850
8724
|
);
|
|
7851
8725
|
}
|
|
8726
|
+
applyUnroundedNormalLineHeights(renderDocument2.body);
|
|
8727
|
+
prepareWordText(renderDocument2.body, () => checkpointWordText(state));
|
|
7852
8728
|
const engine2 = new PaginationEngine(staging, container, {
|
|
7853
8729
|
scale: 1,
|
|
7854
8730
|
cssPrefix: "page-",
|
|
@@ -7859,6 +8735,7 @@ async function convertDocxToPaginatedHtml(document2, requestedOptions) {
|
|
|
7859
8735
|
// restamps through normalizePageMapFragmentIdentities(); stamping inside paginate()
|
|
7860
8736
|
// would be a second full forced-layout pass whose result is immediately overwritten.
|
|
7861
8737
|
deferFragmentIdentities: true,
|
|
8738
|
+
bodyBudgetReductionsPt,
|
|
7862
8739
|
checkCancellation: () => {
|
|
7863
8740
|
if (state.signal?.aborted) {
|
|
7864
8741
|
fail(
|
|
@@ -7899,11 +8776,32 @@ async function convertDocxToPaginatedHtml(document2, requestedOptions) {
|
|
|
7899
8776
|
"Verify that the DOCX has a renderable main document body."
|
|
7900
8777
|
);
|
|
7901
8778
|
}
|
|
7902
|
-
|
|
7903
|
-
|
|
7904
|
-
|
|
7905
|
-
|
|
7906
|
-
|
|
8779
|
+
const clippedPages = await runPhase(
|
|
8780
|
+
state,
|
|
8781
|
+
"running_story_placement",
|
|
8782
|
+
["headers, footers, and notes"],
|
|
8783
|
+
() => {
|
|
8784
|
+
finalizePageTree(renderDocument2, pages2, state, options);
|
|
8785
|
+
engine2.normalizePageMapFragmentIdentities();
|
|
8786
|
+
const clipped = findClippedContent(renderDocument2);
|
|
8787
|
+
if (clipped.length > 0 && bodyReflowRounds >= MAX_BODY_REFLOW_ROUNDS) {
|
|
8788
|
+
fail(
|
|
8789
|
+
"pagination_failure",
|
|
8790
|
+
"running_story_placement",
|
|
8791
|
+
clipped[0].message,
|
|
8792
|
+
"Split the oversized block or reduce its dimensions before export."
|
|
8793
|
+
);
|
|
8794
|
+
}
|
|
8795
|
+
return clipped;
|
|
8796
|
+
}
|
|
8797
|
+
);
|
|
8798
|
+
if (clippedPages.length > 0) {
|
|
8799
|
+
bodyReflowRounds++;
|
|
8800
|
+
for (const page of clippedPages) {
|
|
8801
|
+
bodyBudgetReductionsPt[page.pageNumber] = (bodyBudgetReductionsPt[page.pageNumber] ?? 0) + (page.overflowPx + 1) * 72 / 96;
|
|
8802
|
+
}
|
|
8803
|
+
throw new BodyReflowRequest();
|
|
8804
|
+
}
|
|
7907
8805
|
countDomNodes(renderDocument2, options.limits.domNodes, "running_story_placement");
|
|
7908
8806
|
const automaticResources = automaticResourceCount(renderDocument2);
|
|
7909
8807
|
enforceLimit(
|
|
@@ -7936,6 +8834,12 @@ async function convertDocxToPaginatedHtml(document2, requestedOptions) {
|
|
|
7936
8834
|
} catch (error) {
|
|
7937
8835
|
frame?.remove();
|
|
7938
8836
|
frame = void 0;
|
|
8837
|
+
if (error instanceof BodyReflowRequest) {
|
|
8838
|
+
restoreAttemptState(state, attemptCheckpoint);
|
|
8839
|
+
firstAttemptSignature = void 0;
|
|
8840
|
+
attempt = 0;
|
|
8841
|
+
continue;
|
|
8842
|
+
}
|
|
7939
8843
|
if (!(error instanceof PageTreeInstabilityError) || attempt === 2) throw error;
|
|
7940
8844
|
restoreAttemptState(state, attemptCheckpoint);
|
|
7941
8845
|
addWarning(state, {
|
|
@@ -7957,6 +8861,11 @@ async function convertDocxToPaginatedHtml(document2, requestedOptions) {
|
|
|
7957
8861
|
}
|
|
7958
8862
|
frame = finalized.frame;
|
|
7959
8863
|
const { document: renderDocument, engine, pages } = finalized;
|
|
8864
|
+
alignBaselinesToWord(renderDocument.body);
|
|
8865
|
+
await runPhase(state, "output_verification", ["Word text baselines"], () => paintWordText(
|
|
8866
|
+
renderDocument.getElementById("pagination-container"),
|
|
8867
|
+
() => checkpointWordText(state)
|
|
8868
|
+
));
|
|
7960
8869
|
const renderer = await runPhase(state, "output_verification", ["renderer identity"], () => rendererIdentity(
|
|
7961
8870
|
renderDocument,
|
|
7962
8871
|
layoutDigest,
|
|
@@ -8112,11 +9021,13 @@ export {
|
|
|
8112
9021
|
FONT_SUBSTITUTION_CONTRACT_VERSION,
|
|
8113
9022
|
HARD_EXPORT_RESOURCE_LIMITS,
|
|
8114
9023
|
HARD_EXPORT_TIMEOUT_MS,
|
|
9024
|
+
attestHostVerifiedRuntimeAssets,
|
|
8115
9025
|
canonicalJson,
|
|
8116
9026
|
convertDocxToPaginatedHtml,
|
|
8117
9027
|
fontFamilyKey,
|
|
8118
9028
|
inventoryDocumentFontRequests,
|
|
8119
9029
|
normalizeFontFamilyName,
|
|
8120
9030
|
parseCssFontFamily,
|
|
8121
|
-
reconstructDocxodusExportError
|
|
9031
|
+
reconstructDocxodusExportError,
|
|
9032
|
+
renderedSourceMeasurerExpression
|
|
8122
9033
|
};
|