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
package/dist/export-browser.d.ts
CHANGED
|
@@ -7,11 +7,18 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { canonicalJson } from "./canonical.js";
|
|
9
9
|
import { type PageMap } from "./pagination.js";
|
|
10
|
+
export { renderedSourceMeasurerExpression } from "./pagination.js";
|
|
10
11
|
import type { FontConfigurationIdentity, FontResolution, FontResolver } from "./font-contract.js";
|
|
11
12
|
export { fontFamilyKey, FONT_RESOLVER_CONTRACT_ID, FONT_RESOLVER_SCHEMA_VERSION, FONT_SUBSTITUTION_CONTRACT, FONT_SUBSTITUTION_CONTRACT_MATERIAL, FONT_SUBSTITUTION_CONTRACT_VERSION, normalizeFontFamilyName, } from "./font-contract.js";
|
|
12
13
|
export { inventoryDocumentFontRequests, parseCssFontFamily } from "./font-runtime.js";
|
|
13
14
|
export type { FontConfigurationIdentity, FontEmbeddingKind, FontFamilyKind, FontFaceMatch, FontFaceStyle, FontFileFormat, FontGlyphCoverage, FontLicenseEvidence, FontMediaType, FontRequest, FontResolution, FontResolutionSource, FontResolutionStatus, FontResolver, FontResolverFace, FontResolverOutcome, FontResolverRequest, FontResolverResponse, FontSubstitutionEntry, } from "./font-contract.js";
|
|
14
15
|
export type ReviewProfile = "final" | "original" | "markup";
|
|
16
|
+
/**
|
|
17
|
+
* How the markup profile draws tracked changes (issue #851): `docxodus` (default) is Docxodus's own
|
|
18
|
+
* review style; `word` is close to Word's All Markup print — per-author colours, underline and
|
|
19
|
+
* strikethrough only, no fills or pilcrows, and left-margin change bars.
|
|
20
|
+
*/
|
|
21
|
+
export type MarkupPresentation = "docxodus" | "word";
|
|
15
22
|
export type CommentProfile = "hidden" | "inline" | "endnotes" | "margin";
|
|
16
23
|
export type UnsupportedContentPolicy = "warn" | "strict";
|
|
17
24
|
export type ExportPhase = "input_validation" | "package_preflight" | "browser_launch" | "wasm_initialization" | "docx_conversion" | "font_loading" | "image_decoding" | "chart_svg_materialization" | "pagination" | "running_story_placement" | "page_tree_stability" | "pdf_print" | "output_verification" | "output_write" | "filesystem_commit" | "cleanup";
|
|
@@ -49,6 +56,8 @@ export interface PaginatedHtmlOptions {
|
|
|
49
56
|
expectedSourceDigest?: string;
|
|
50
57
|
reviewProfile: ReviewProfile;
|
|
51
58
|
reviewProfileAlreadyApplied?: boolean;
|
|
59
|
+
/** Only with reviewProfile "markup"; default "docxodus". */
|
|
60
|
+
markupPresentation?: MarkupPresentation;
|
|
52
61
|
commentProfile: CommentProfile;
|
|
53
62
|
title?: string;
|
|
54
63
|
unsupportedContent?: UnsupportedContentPolicy;
|
|
@@ -106,6 +115,8 @@ export interface RenderReportBase {
|
|
|
106
115
|
options: {
|
|
107
116
|
reviewProfile: ReviewProfile;
|
|
108
117
|
reviewProfileAlreadyApplied: boolean;
|
|
118
|
+
/** Present only when the markup profile used the Word presentation. */
|
|
119
|
+
markupPresentation?: "word";
|
|
109
120
|
commentProfile: CommentProfile;
|
|
110
121
|
title: string;
|
|
111
122
|
outputs: Array<"html" | "pdf">;
|
|
@@ -257,6 +268,15 @@ export declare class DocxodusExportError extends Error {
|
|
|
257
268
|
* recognizably a serialized `DocxodusExportError`.
|
|
258
269
|
*/
|
|
259
270
|
export declare function reconstructDocxodusExportError(value: unknown): DocxodusExportError | undefined;
|
|
271
|
+
/**
|
|
272
|
+
* Host integration only — `@docxodus/export` calls this from the bootstrap module it generates and
|
|
273
|
+
* serves. The host reads and hashes the whole runtime asset graph once per process and serves exactly
|
|
274
|
+
* those verified bytes, so when the manifest this page fetches hashes to the attested digest, the page
|
|
275
|
+
* does not fetch and hash all ~26 MB of runtime assets again for every document. The manifest itself
|
|
276
|
+
* and the materializer bundle are still checked here. A page that never receives an attestation (any
|
|
277
|
+
* browser-only deployment) verifies every asset as before.
|
|
278
|
+
*/
|
|
279
|
+
export declare function attestHostVerifiedRuntimeAssets(manifestSha256: string): void;
|
|
260
280
|
export { canonicalJson };
|
|
261
281
|
/**
|
|
262
282
|
* Convert DOCX bytes into a complete offline HTML document containing only the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"export-browser.d.ts","sourceRoot":"","sources":["../src/export-browser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAA2B,aAAa,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"export-browser.d.ts","sourceRoot":"","sources":["../src/export-browser.ts"],"names":[],"mappings":"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"}
|
package/dist/export-browser.js
CHANGED
|
@@ -7,9 +7,12 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import limitsContractJson from "./export-resource-limits-v1.json";
|
|
9
9
|
import { assertWellFormedUnicode, canonicalJson } from "./canonical.js";
|
|
10
|
-
import {
|
|
10
|
+
import { alignBaselinesToWord, applyUnroundedNormalLineHeights, isPreservedExactLineInk } from "./line-metrics.js";
|
|
11
|
+
import { prepareWordText, paintWordText } from "./word-text.js";
|
|
12
|
+
import { measureRenderedSource, PaginationEngine } from "./pagination.js";
|
|
13
|
+
export { renderedSourceMeasurerExpression } from "./pagination.js";
|
|
11
14
|
import { createWorkerDocxodus, workerErrorCode, } from "./worker-proxy.js";
|
|
12
|
-
import { CommentRenderMode, PaginationMode, } from "./types.js";
|
|
15
|
+
import { CommentRenderMode, PaginationMode, RevisionPresentation, } from "./types.js";
|
|
13
16
|
import { BrowserFontError, createBrowserFontTask, } from "./font-runtime.js";
|
|
14
17
|
export { fontFamilyKey, FONT_RESOLVER_CONTRACT_ID, FONT_RESOLVER_SCHEMA_VERSION, FONT_SUBSTITUTION_CONTRACT, FONT_SUBSTITUTION_CONTRACT_MATERIAL, FONT_SUBSTITUTION_CONTRACT_VERSION, normalizeFontFamilyName, } from "./font-contract.js";
|
|
15
18
|
export { inventoryDocumentFontRequests, parseCssFontFamily } from "./font-runtime.js";
|
|
@@ -122,6 +125,7 @@ const REPORT_SCHEMA = "https://docxodus.dev/schemas/render/render-report/v2";
|
|
|
122
125
|
const TEXT_ENCODER = new TextEncoder();
|
|
123
126
|
const ALLOWED_REVIEW_PROFILES = new Set(["final", "original", "markup"]);
|
|
124
127
|
const ALLOWED_COMMENT_PROFILES = new Set(["hidden", "inline", "endnotes", "margin"]);
|
|
128
|
+
const ALLOWED_MARKUP_PRESENTATIONS = new Set(["docxodus", "word"]);
|
|
125
129
|
const ALLOWED_UNSUPPORTED_POLICIES = new Set(["warn", "strict"]);
|
|
126
130
|
const PACKAGE_LIMIT_FINDINGS = new Set([
|
|
127
131
|
"entry_count_limit_exceeded",
|
|
@@ -172,6 +176,13 @@ function normalizeOptions(options) {
|
|
|
172
176
|
if (reviewProfileAlreadyApplied && options.reviewProfile === "markup") {
|
|
173
177
|
fail("invalid_argument", "input_validation", "reviewProfileAlreadyApplied is invalid with the markup profile.", "Use unchanged source bytes for markup, or choose final/original.");
|
|
174
178
|
}
|
|
179
|
+
const markupPresentation = options.markupPresentation ?? "docxodus";
|
|
180
|
+
if (!ALLOWED_MARKUP_PRESENTATIONS.has(markupPresentation)) {
|
|
181
|
+
fail("invalid_argument", "input_validation", "markupPresentation is invalid.", "Use docxodus or word.");
|
|
182
|
+
}
|
|
183
|
+
if (markupPresentation !== "docxodus" && options.reviewProfile !== "markup") {
|
|
184
|
+
fail("invalid_argument", "input_validation", "markupPresentation applies only to the markup profile.", "Omit markupPresentation, or use reviewProfile markup.");
|
|
185
|
+
}
|
|
175
186
|
const unsupportedContent = options.unsupportedContent ?? "warn";
|
|
176
187
|
if (!ALLOWED_UNSUPPORTED_POLICIES.has(unsupportedContent)) {
|
|
177
188
|
fail("invalid_argument", "input_validation", "unsupportedContent is invalid.", "Use warn or strict.");
|
|
@@ -250,6 +261,7 @@ function normalizeOptions(options) {
|
|
|
250
261
|
expectedSourceDigest: options.expectedSourceDigest,
|
|
251
262
|
reviewProfile: options.reviewProfile,
|
|
252
263
|
reviewProfileAlreadyApplied,
|
|
264
|
+
markupPresentation,
|
|
253
265
|
commentProfile: options.commentProfile,
|
|
254
266
|
title,
|
|
255
267
|
unsupportedContent,
|
|
@@ -283,6 +295,12 @@ async function ownedBytes(document, maximum, signal) {
|
|
|
283
295
|
function monotonicNow() {
|
|
284
296
|
return globalThis.performance?.now() ?? Date.now();
|
|
285
297
|
}
|
|
298
|
+
function checkpointWordText(state) {
|
|
299
|
+
if (state.signal?.aborted)
|
|
300
|
+
fail("operation_cancelled", state.phase, "Export was cancelled during text layout.", "Retry with a non-aborted signal.");
|
|
301
|
+
if (monotonicNow() >= state.deadline)
|
|
302
|
+
fail("readiness_timeout", state.phase, "Export timed out during text layout.", "Increase timeoutMs or reduce document complexity.");
|
|
303
|
+
}
|
|
286
304
|
async function runPhase(state, phase, pending, operation) {
|
|
287
305
|
state.phase = phase;
|
|
288
306
|
const started = monotonicNow();
|
|
@@ -445,6 +463,29 @@ async function boundedResponseBytes(response, maximum, label) {
|
|
|
445
463
|
}
|
|
446
464
|
return bytes;
|
|
447
465
|
}
|
|
466
|
+
/**
|
|
467
|
+
* SHA-256 of the exact `export-assets.json` bytes a trusted Node host has already verified, with
|
|
468
|
+
* every asset that manifest names, before serving them to this page (issue #852). Set once, by the
|
|
469
|
+
* host's own bootstrap module, before any render.
|
|
470
|
+
*/
|
|
471
|
+
let hostVerifiedAssetManifestSha256;
|
|
472
|
+
/**
|
|
473
|
+
* Host integration only — `@docxodus/export` calls this from the bootstrap module it generates and
|
|
474
|
+
* serves. The host reads and hashes the whole runtime asset graph once per process and serves exactly
|
|
475
|
+
* those verified bytes, so when the manifest this page fetches hashes to the attested digest, the page
|
|
476
|
+
* does not fetch and hash all ~26 MB of runtime assets again for every document. The manifest itself
|
|
477
|
+
* and the materializer bundle are still checked here. A page that never receives an attestation (any
|
|
478
|
+
* browser-only deployment) verifies every asset as before.
|
|
479
|
+
*/
|
|
480
|
+
export function attestHostVerifiedRuntimeAssets(manifestSha256) {
|
|
481
|
+
if (typeof manifestSha256 !== "string" || !/^[0-9a-f]{64}$/.test(manifestSha256)) {
|
|
482
|
+
fail("unsupported_runtime", "wasm_initialization", "The host runtime asset attestation is not a SHA-256 digest.", "Use the bootstrap module @docxodus/export generates.");
|
|
483
|
+
}
|
|
484
|
+
if (hostVerifiedAssetManifestSha256 !== undefined && hostVerifiedAssetManifestSha256 !== manifestSha256) {
|
|
485
|
+
fail("unsupported_runtime", "wasm_initialization", "The host runtime asset attestation may be set only once.", "Load one host bootstrap module per page.");
|
|
486
|
+
}
|
|
487
|
+
hostVerifiedAssetManifestSha256 = manifestSha256;
|
|
488
|
+
}
|
|
448
489
|
async function loadRuntimeAssetIdentity(wasmBasePath, signal) {
|
|
449
490
|
const manifestUrl = new URL("./export-assets.json", import.meta.url);
|
|
450
491
|
const response = await globalThis.fetch(manifestUrl, {
|
|
@@ -543,7 +584,11 @@ async function loadRuntimeAssetIdentity(wasmBasePath, signal) {
|
|
|
543
584
|
if (materializerDigest !== materializer.sha256) {
|
|
544
585
|
fail("unsupported_runtime", "wasm_initialization", "The loaded browser materializer does not match export-assets.json.", "Deploy the bundle and asset graph from the same Docxodus build.");
|
|
545
586
|
}
|
|
546
|
-
|
|
587
|
+
// A host that verified this exact manifest and its assets serves only those bytes; hashing all of
|
|
588
|
+
// them again here cost roughly half a second per document (issue #852).
|
|
589
|
+
const hostVerified = hostVerifiedAssetManifestSha256 !== undefined
|
|
590
|
+
&& await sha256(graphBytes) === hostVerifiedAssetManifestSha256;
|
|
591
|
+
const verifiedRuntimeAssets = hostVerified ? [] : assets.filter((entry) => entry.path === "./docxodus.worker.js" || entry.path.startsWith("./wasm/_framework/"));
|
|
547
592
|
const resolvedWasmBasePath = new URL(wasmBasePath.endsWith("/") ? wasmBasePath : `${wasmBasePath}/`, import.meta.url);
|
|
548
593
|
// Assets are verified concurrently, but every outcome is captured rather than thrown so the
|
|
549
594
|
// reported failure is always the first asset in declaration order, not whichever lost the race.
|
|
@@ -1072,6 +1117,9 @@ function conversionOptions(options) {
|
|
|
1072
1117
|
renderFootnotesAndEndnotes: true,
|
|
1073
1118
|
renderHeadersAndFooters: true,
|
|
1074
1119
|
renderTrackedChanges: options.reviewProfile === "markup",
|
|
1120
|
+
revisionPresentation: options.markupPresentation === "word"
|
|
1121
|
+
? RevisionPresentation.Word
|
|
1122
|
+
: RevisionPresentation.Docxodus,
|
|
1075
1123
|
showDeletedContent: true,
|
|
1076
1124
|
renderMoveOperations: true,
|
|
1077
1125
|
renderUnsupportedContentPlaceholders: true,
|
|
@@ -2008,7 +2056,12 @@ function finalizePageTree(document, pages, state, options) {
|
|
|
2008
2056
|
style.textContent = standaloneStyle(pages);
|
|
2009
2057
|
document.head.appendChild(style);
|
|
2010
2058
|
}
|
|
2011
|
-
|
|
2059
|
+
/**
|
|
2060
|
+
* Fails on a clipped footnote continuation, and reports (rather than fails on) every page whose
|
|
2061
|
+
* body band clips content: the caller can still correct those by laying the document out again
|
|
2062
|
+
* with a smaller budget for the page (issue #848).
|
|
2063
|
+
*/
|
|
2064
|
+
function findClippedContent(document) {
|
|
2012
2065
|
for (const footnotes of Array.from(document.querySelectorAll(".page-footnotes"))) {
|
|
2013
2066
|
if (footnotes.scrollHeight <= footnotes.clientHeight + 1)
|
|
2014
2067
|
continue;
|
|
@@ -2019,26 +2072,47 @@ function assertNoClippedContent(document) {
|
|
|
2019
2072
|
fail("pagination_failure", "running_story_placement", "A footnote continuation is clipped in the final page tree.", "Report the unsupported note structure; eligible text paragraphs are continued losslessly.");
|
|
2020
2073
|
}
|
|
2021
2074
|
}
|
|
2075
|
+
const clipped = [];
|
|
2022
2076
|
for (const content of Array.from(document.querySelectorAll(".page-content"))) {
|
|
2023
2077
|
if (content.scrollHeight > content.clientHeight + 1) {
|
|
2024
2078
|
const pageNumber = content.closest(".page-box")?.dataset.pageNumber ?? "unknown";
|
|
2025
2079
|
const boundary = content.getBoundingClientRect().bottom;
|
|
2026
|
-
const
|
|
2080
|
+
const overflowing = Array.from(content.querySelectorAll("*"))
|
|
2027
2081
|
.map((element) => ({
|
|
2028
2082
|
element,
|
|
2029
2083
|
bottom: element.getBoundingClientRect().bottom,
|
|
2030
2084
|
}))
|
|
2031
|
-
.filter(({ bottom }) => bottom > boundary + 1)
|
|
2032
|
-
.sort((left, right) => right.bottom - left.bottom)
|
|
2033
|
-
.slice(0, 3)
|
|
2034
|
-
.map(({ element, bottom }) => `${element.localName}${element.className ? `.${String(element.className).trim().replace(/\s+/g, ".")}` : ""} (+${(bottom - boundary).toFixed(1)}px)`);
|
|
2085
|
+
.filter(({ element, bottom }) => bottom > boundary + 1 && !isPreservedExactLineInk(element, content))
|
|
2086
|
+
.sort((left, right) => right.bottom - left.bottom);
|
|
2035
2087
|
// Collapsed trailing margins contribute to scrollHeight even though no
|
|
2036
2088
|
// descendant pixels are clipped (a common final blank Word paragraph).
|
|
2037
|
-
if (
|
|
2089
|
+
if (overflowing.length === 0)
|
|
2038
2090
|
continue;
|
|
2039
|
-
|
|
2091
|
+
const overflow = overflowing
|
|
2092
|
+
.slice(0, 3)
|
|
2093
|
+
.map(({ element, bottom }) => `${element.localName}${element.className ? `.${String(element.className).trim().replace(/\s+/g, ".")}` : ""} (+${(bottom - boundary).toFixed(1)}px)`);
|
|
2094
|
+
clipped.push({
|
|
2095
|
+
pageNumber: Number.parseInt(pageNumber, 10),
|
|
2096
|
+
overflowPx: overflowing[0].bottom - boundary,
|
|
2097
|
+
message: `Page ${pageNumber} body content is clipped (${content.scrollHeight}px scroll height in a ${content.clientHeight}px band; ${overflow.join(", ")}).`,
|
|
2098
|
+
});
|
|
2040
2099
|
}
|
|
2041
2100
|
}
|
|
2101
|
+
return clipped;
|
|
2102
|
+
}
|
|
2103
|
+
/**
|
|
2104
|
+
* How many times the export may lay a document out again to correct a page that still clips its
|
|
2105
|
+
* body. Each round withholds the measured overflow from that page's budget, so the clipped block
|
|
2106
|
+
* moves or splits onto the next page; a round is needed only where the placement model and the
|
|
2107
|
+
* finished layout disagree, so one normally suffices.
|
|
2108
|
+
*/
|
|
2109
|
+
const MAX_BODY_REFLOW_ROUNDS = 3;
|
|
2110
|
+
/** Thrown inside an attempt to restart it from pristine HTML with corrected page budgets. */
|
|
2111
|
+
class BodyReflowRequest extends Error {
|
|
2112
|
+
constructor() {
|
|
2113
|
+
super("body reflow requested");
|
|
2114
|
+
this.name = "BodyReflowRequest";
|
|
2115
|
+
}
|
|
2042
2116
|
}
|
|
2043
2117
|
function treeSignature(document, pages) {
|
|
2044
2118
|
const geometry = pages.map((page) => {
|
|
@@ -2189,11 +2263,17 @@ function automaticResourceCount(document) {
|
|
|
2189
2263
|
}
|
|
2190
2264
|
return { count, bytes };
|
|
2191
2265
|
}
|
|
2266
|
+
/** The report's and layout digest's record of the presentation: only the non-default one is named. */
|
|
2267
|
+
function markupPresentationField(options) {
|
|
2268
|
+
return options.markupPresentation === "word" ? { markupPresentation: "word" } : {};
|
|
2269
|
+
}
|
|
2192
2270
|
async function layoutDigestForOptions(options) {
|
|
2193
2271
|
const layoutContract = {
|
|
2194
2272
|
title: options.title,
|
|
2195
2273
|
reviewProfile: options.reviewProfile,
|
|
2196
2274
|
reviewProfileAlreadyApplied: options.reviewProfileAlreadyApplied,
|
|
2275
|
+
// Absent for the default, so default layouts keep the digest they had before the option.
|
|
2276
|
+
...markupPresentationField(options),
|
|
2197
2277
|
commentProfile: options.commentProfile,
|
|
2198
2278
|
pagination: {
|
|
2199
2279
|
mode: "paginated",
|
|
@@ -2296,28 +2376,6 @@ function storyForAnchor(anchorId) {
|
|
|
2296
2376
|
return "comment";
|
|
2297
2377
|
return "body";
|
|
2298
2378
|
}
|
|
2299
|
-
function visibleRectWithinPage(document, element, page) {
|
|
2300
|
-
const pageRect = page.getBoundingClientRect();
|
|
2301
|
-
const rect = element.getBoundingClientRect();
|
|
2302
|
-
let left = Math.max(rect.left, pageRect.left);
|
|
2303
|
-
let top = Math.max(rect.top, pageRect.top);
|
|
2304
|
-
let right = Math.min(rect.right, pageRect.right);
|
|
2305
|
-
let bottom = Math.min(rect.bottom, pageRect.bottom);
|
|
2306
|
-
const clips = (value) => value === "hidden" || value === "clip" || value === "scroll" || value === "auto";
|
|
2307
|
-
for (let ancestor = element.parentElement; ancestor && ancestor !== page; ancestor = ancestor.parentElement) {
|
|
2308
|
-
const style = document.defaultView.getComputedStyle(ancestor);
|
|
2309
|
-
const ancestorRect = ancestor.getBoundingClientRect();
|
|
2310
|
-
if (clips(style.overflowX)) {
|
|
2311
|
-
left = Math.max(left, ancestorRect.left);
|
|
2312
|
-
right = Math.min(right, ancestorRect.right);
|
|
2313
|
-
}
|
|
2314
|
-
if (clips(style.overflowY)) {
|
|
2315
|
-
top = Math.max(top, ancestorRect.top);
|
|
2316
|
-
bottom = Math.min(bottom, ancestorRect.bottom);
|
|
2317
|
-
}
|
|
2318
|
-
}
|
|
2319
|
-
return { left, top, right, bottom };
|
|
2320
|
-
}
|
|
2321
2379
|
function assertStandaloneResourceAudit(document) {
|
|
2322
2380
|
if (document.querySelector("script, iframe, object, embed, video, audio, source, track, link, base, meta[http-equiv='refresh' i]")) {
|
|
2323
2381
|
throw new Error("offline output contains active or external-loading content");
|
|
@@ -2408,8 +2466,7 @@ async function verifyOfflineReopen(hostDocument, html, expectedPageMap, state) {
|
|
|
2408
2466
|
const style = reopened.defaultView.getComputedStyle(element);
|
|
2409
2467
|
if (style.display === "none" || style.visibility === "hidden")
|
|
2410
2468
|
continue;
|
|
2411
|
-
const rect = element
|
|
2412
|
-
const visible = visibleRectWithinPage(reopened, element, page);
|
|
2469
|
+
const { rect, visible } = measureRenderedSource(reopened.defaultView, element, page, pageRect);
|
|
2413
2470
|
if (rect.width <= 0 || rect.height <= 0
|
|
2414
2471
|
|| visible.right <= visible.left || visible.bottom <= visible.top)
|
|
2415
2472
|
continue;
|
|
@@ -2472,6 +2529,7 @@ function reportBase(manifest, sourceBytes, derivedManifest, derivedBytes, option
|
|
|
2472
2529
|
options: {
|
|
2473
2530
|
reviewProfile: options.reviewProfile,
|
|
2474
2531
|
reviewProfileAlreadyApplied: options.reviewProfileAlreadyApplied,
|
|
2532
|
+
...markupPresentationField(options),
|
|
2475
2533
|
commentProfile: options.commentProfile,
|
|
2476
2534
|
title: options.title,
|
|
2477
2535
|
outputs: ["html"],
|
|
@@ -2675,6 +2733,10 @@ export async function convertDocxToPaginatedHtml(document, requestedOptions) {
|
|
|
2675
2733
|
});
|
|
2676
2734
|
preflightConvertedHtml(convertedHtml, options);
|
|
2677
2735
|
const attemptCheckpoint = checkpointAttemptState(state);
|
|
2736
|
+
// Physical page → points withheld from its body budget; grows by one entry per corrected page
|
|
2737
|
+
// and is carried into every later attempt so the stability comparison sees one layout.
|
|
2738
|
+
const bodyBudgetReductionsPt = {};
|
|
2739
|
+
let bodyReflowRounds = 0;
|
|
2678
2740
|
let finalized;
|
|
2679
2741
|
let firstAttemptSignature;
|
|
2680
2742
|
let firstAttemptFontIdentity;
|
|
@@ -2740,6 +2802,11 @@ export async function convertDocxToPaginatedHtml(document, requestedOptions) {
|
|
|
2740
2802
|
if (!staging || !container) {
|
|
2741
2803
|
fail("conversion_failure", "docx_conversion", "Paginated conversion did not produce staging and page containers.", "Use the paginated converter contract and report the malformed conversion output.");
|
|
2742
2804
|
}
|
|
2805
|
+
// Chromium rounds a `normal` line height to a whole pixel, and that error accumulates
|
|
2806
|
+
// down the page (issue #850). Fix each line's height before anything is measured; both
|
|
2807
|
+
// attempts start from pristine HTML, so both apply it identically.
|
|
2808
|
+
applyUnroundedNormalLineHeights(renderDocument.body);
|
|
2809
|
+
prepareWordText(renderDocument.body, () => checkpointWordText(state));
|
|
2743
2810
|
const engine = new PaginationEngine(staging, container, {
|
|
2744
2811
|
scale: 1,
|
|
2745
2812
|
cssPrefix: "page-",
|
|
@@ -2750,6 +2817,7 @@ export async function convertDocxToPaginatedHtml(document, requestedOptions) {
|
|
|
2750
2817
|
// restamps through normalizePageMapFragmentIdentities(); stamping inside paginate()
|
|
2751
2818
|
// would be a second full forced-layout pass whose result is immediately overwritten.
|
|
2752
2819
|
deferFragmentIdentities: true,
|
|
2820
|
+
bodyBudgetReductionsPt,
|
|
2753
2821
|
checkCancellation: () => {
|
|
2754
2822
|
if (state.signal?.aborted) {
|
|
2755
2823
|
fail("operation_cancelled", state.phase, `Export was cancelled during ${state.phase}.`, "Retry with a non-aborted signal.", { pending: ["page layout"] });
|
|
@@ -2768,14 +2836,29 @@ export async function convertDocxToPaginatedHtml(document, requestedOptions) {
|
|
|
2768
2836
|
if (pages.length === 0) {
|
|
2769
2837
|
fail("pagination_failure", "pagination", "Pagination produced no pages.", "Verify that the DOCX has a renderable main document body.");
|
|
2770
2838
|
}
|
|
2771
|
-
await runPhase(state, "running_story_placement", ["headers, footers, and notes"], () => {
|
|
2839
|
+
const clippedPages = await runPhase(state, "running_story_placement", ["headers, footers, and notes"], () => {
|
|
2772
2840
|
finalizePageTree(renderDocument, pages, state, options);
|
|
2773
2841
|
// Running-story placement changes the visible fragment set. This is
|
|
2774
2842
|
// the sole identity-writing normalization pass; PageMap measurement
|
|
2775
2843
|
// below remains read-only.
|
|
2776
2844
|
engine.normalizePageMapFragmentIdentities();
|
|
2777
|
-
|
|
2845
|
+
const clipped = findClippedContent(renderDocument);
|
|
2846
|
+
// Refusing to emit clipped content is right; refusing to emit the document is not —
|
|
2847
|
+
// until the correction rounds are spent (an indivisible block taller than the page).
|
|
2848
|
+
if (clipped.length > 0 && bodyReflowRounds >= MAX_BODY_REFLOW_ROUNDS) {
|
|
2849
|
+
fail("pagination_failure", "running_story_placement", clipped[0].message, "Split the oversized block or reduce its dimensions before export.");
|
|
2850
|
+
}
|
|
2851
|
+
return clipped;
|
|
2778
2852
|
});
|
|
2853
|
+
if (clippedPages.length > 0) {
|
|
2854
|
+
// Withhold each page's overflow (plus a pixel) from its budget and lay out again.
|
|
2855
|
+
bodyReflowRounds++;
|
|
2856
|
+
for (const page of clippedPages) {
|
|
2857
|
+
bodyBudgetReductionsPt[page.pageNumber] =
|
|
2858
|
+
(bodyBudgetReductionsPt[page.pageNumber] ?? 0) + (page.overflowPx + 1) * 72 / 96;
|
|
2859
|
+
}
|
|
2860
|
+
throw new BodyReflowRequest();
|
|
2861
|
+
}
|
|
2779
2862
|
countDomNodes(renderDocument, options.limits.domNodes, "running_story_placement");
|
|
2780
2863
|
const automaticResources = automaticResourceCount(renderDocument);
|
|
2781
2864
|
enforceLimit(automaticResources.count, options.limits.automaticResources, "automaticResources", "running_story_placement");
|
|
@@ -2797,6 +2880,13 @@ export async function convertDocxToPaginatedHtml(document, requestedOptions) {
|
|
|
2797
2880
|
catch (error) {
|
|
2798
2881
|
frame?.remove();
|
|
2799
2882
|
frame = undefined;
|
|
2883
|
+
if (error instanceof BodyReflowRequest) {
|
|
2884
|
+
// Start over at attempt 1: both stability attempts must lay out with the same budgets.
|
|
2885
|
+
restoreAttemptState(state, attemptCheckpoint);
|
|
2886
|
+
firstAttemptSignature = undefined;
|
|
2887
|
+
attempt = 0;
|
|
2888
|
+
continue;
|
|
2889
|
+
}
|
|
2800
2890
|
if (!(error instanceof PageTreeInstabilityError) || attempt === 2)
|
|
2801
2891
|
throw error;
|
|
2802
2892
|
restoreAttemptState(state, attemptCheckpoint);
|
|
@@ -2814,6 +2904,11 @@ export async function convertDocxToPaginatedHtml(document, requestedOptions) {
|
|
|
2814
2904
|
}
|
|
2815
2905
|
frame = finalized.frame;
|
|
2816
2906
|
const { document: renderDocument, engine, pages } = finalized;
|
|
2907
|
+
// Chromium also puts each baseline up to a pixel off Word's (issue #942). Relative offsets take that back
|
|
2908
|
+
// without changing a line box. They run on the final page tree, after the clipping check: a glyph moved
|
|
2909
|
+
// down stays inside its line, but its inline box can pass a page band's edge by a pixel.
|
|
2910
|
+
alignBaselinesToWord(renderDocument.body);
|
|
2911
|
+
await runPhase(state, "output_verification", ["Word text baselines"], () => paintWordText(renderDocument.getElementById("pagination-container"), () => checkpointWordText(state)));
|
|
2817
2912
|
const renderer = await runPhase(state, "output_verification", ["renderer identity"], () => rendererIdentity(renderDocument, layoutDigest, runtimePolicyDigest, runtimeAssets, runtimeVersion, state.fonts, state.fontIdentity));
|
|
2818
2913
|
rendererFingerprint = renderer.rendererFingerprint;
|
|
2819
2914
|
observed = renderer.observed;
|