@stll/folio-core 0.47.7 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/controller/fontReadiness.js +3 -3
- package/dist/controller/hyphenationReadiness.d.ts +19 -0
- package/dist/controller/hyphenationReadiness.js +47 -0
- package/dist/controller/layoutPipeline.d.ts +3 -0
- package/dist/controller/layoutPipeline.js +59 -17
- package/dist/display-list/build/buildDisplayList.js +39 -12
- package/dist/display-list/build/pageFurniture.d.ts +7 -1
- package/dist/display-list/build/pageFurniture.js +21 -8
- package/dist/display-list/build/paragraphPrimitives.js +5 -4
- package/dist/display-list/build/tablePrimitives.js +10 -6
- package/dist/docx/attributeRemainder.js +10 -2
- package/dist/docx/blockContentParser.d.ts +2 -0
- package/dist/docx/blockContentParser.js +77 -62
- package/dist/docx/commentParser.d.ts +2 -1
- package/dist/docx/commentParser.js +57 -45
- package/dist/docx/containerChildren.d.ts +21 -8
- package/dist/docx/containerChildren.js +39 -9
- package/dist/docx/documentParser.d.ts +5 -1
- package/dist/docx/documentParser.js +8 -5
- package/dist/docx/documentSectionFacts.d.ts +22 -0
- package/dist/docx/documentSectionFacts.js +43 -0
- package/dist/docx/fieldParser.d.ts +8 -1
- package/dist/docx/fieldParser.js +47 -3
- package/dist/docx/fontTableParser.js +34 -27
- package/dist/docx/footnoteParser.d.ts +3 -2
- package/dist/docx/footnoteParser.js +15 -12
- package/dist/docx/groupDrawingParser.d.ts +2 -1
- package/dist/docx/groupDrawingParser.js +3 -6
- package/dist/docx/headerFooterParser.d.ts +7 -2
- package/dist/docx/headerFooterParser.js +15 -5
- package/dist/docx/hyperlinkParser.d.ts +48 -36
- package/dist/docx/hyperlinkParser.js +97 -82
- package/dist/docx/numberingParser.js +108 -84
- package/dist/docx/paragraphParser.d.ts +5 -1
- package/dist/docx/paragraphParser.js +381 -362
- package/dist/docx/paragraphProperties.js +123 -109
- package/dist/docx/paragraphPropertySource.d.ts +6 -1
- package/dist/docx/paragraphPropertySource.js +9 -1
- package/dist/docx/paragraphTextBoxEnrichment.d.ts +2 -1
- package/dist/docx/paragraphTextBoxEnrichment.js +17 -6
- package/dist/docx/parser.d.ts +7 -1
- package/dist/docx/parser.js +33 -16
- package/dist/docx/previewBudget.d.ts +55 -33
- package/dist/docx/previewBudget.js +59 -65
- package/dist/docx/rezip.js +41 -58
- package/dist/docx/runParser.d.ts +4 -1
- package/dist/docx/runParser.js +172 -184
- package/dist/docx/sdtProperties.js +87 -84
- package/dist/docx/sectionParser.js +169 -160
- package/dist/docx/serializer/paragraphSerializer.js +42 -38
- package/dist/docx/serializer/settingsSerializer.js +19 -1
- package/dist/docx/settingsParser.js +8 -0
- package/dist/docx/streamingXmlParser.d.ts +6 -2
- package/dist/docx/streamingXmlParser.js +12 -7
- package/dist/docx/styleParser.js +34 -8
- package/dist/docx/tableParser.d.ts +21 -12
- package/dist/docx/tableParser.js +518 -441
- package/dist/docx/textBoxParser.d.ts +8 -3
- package/dist/docx/textBoxParser.js +3 -3
- package/dist/docx/verbatimCapture.d.ts +10 -8
- package/dist/docx/verbatimCapture.js +45 -2
- package/dist/docx/vmlImageParser.d.ts +15 -13
- package/dist/docx/vmlImageParser.js +75 -60
- package/dist/docx/vmlPreview.d.ts +3 -3
- package/dist/docx/vmlPreview.js +3 -4
- package/dist/docx/xmlNamespaceContext.d.ts +9 -0
- package/dist/docx/xmlNamespaceContext.js +54 -0
- package/dist/docx/xmlParser.d.ts +6 -3
- package/dist/docx/xmlParser.js +11 -41
- package/dist/docx/xmlResourceLimits.d.ts +2 -1
- package/dist/docx/xmlResourceLimits.js +4 -1
- package/dist/fonts/headlessMeasure.js +0 -0
- package/dist/headless-layout.d.ts +11 -2
- package/dist/headless-layout.js +153 -107
- package/dist/internal/paragraphFormattingSerialization.js +3 -3
- package/dist/layout-bridge/convert/endnoteLayout.d.ts +65 -0
- package/dist/layout-bridge/convert/endnoteLayout.js +227 -0
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +19 -3
- package/dist/layout-bridge/convert/footnoteLayout.js +40 -31
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -7
- package/dist/layout-bridge/convert/headerFooterLayout.js +5 -20
- package/dist/layout-bridge/convert/paragraphMarkFormatting.d.ts +10 -0
- package/dist/layout-bridge/convert/paragraphMarkFormatting.js +59 -0
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +29 -1
- package/dist/layout-bridge/convert/toFlowBlocks.js +258 -35
- package/dist/layout-bridge/engine/hitTest.js +5 -3
- package/dist/layout-bridge/engine/selectionRects.js +5 -3
- package/dist/layout-engine/anchorLayoutInCellCompatibility.d.ts +10 -0
- package/dist/layout-engine/anchorLayoutInCellCompatibility.js +22 -0
- package/dist/layout-engine/index.d.ts +2 -2
- package/dist/layout-engine/index.js +70 -11
- package/dist/layout-engine/justificationCompatibility.d.ts +1 -1
- package/dist/layout-engine/justificationCompatibility.js +9 -5
- package/dist/layout-engine/keep-together.d.ts +11 -5
- package/dist/layout-engine/keep-together.js +39 -16
- package/dist/layout-engine/layoutInstrumentation.d.ts +8 -2
- package/dist/layout-engine/layoutInstrumentation.js +8 -1
- package/dist/layout-engine/measure/advanceComposition.js +4 -7
- package/dist/layout-engine/measure/hyphenationDictionaries.d.ts +74 -0
- package/dist/layout-engine/measure/hyphenationDictionaries.js +138 -0
- package/dist/layout-engine/measure/hyphenationPreload.d.ts +11 -0
- package/dist/layout-engine/measure/hyphenationPreload.js +24 -0
- package/dist/layout-engine/measure/lineBreakProvider.d.ts +12 -2
- package/dist/layout-engine/measure/lineBreakProvider.js +30 -30
- package/dist/layout-engine/measure/lineBreaks.d.ts +6 -1
- package/dist/layout-engine/measure/lineBreaks.js +10 -3
- package/dist/layout-engine/measure/listMarkerWidth.d.ts +9 -6
- package/dist/layout-engine/measure/listMarkerWidth.js +20 -22
- package/dist/layout-engine/measure/measureBlocks.js +42 -8
- package/dist/layout-engine/measure/measureContainer.js +5 -5
- package/dist/layout-engine/measure/measureHelpers.d.ts +15 -3
- package/dist/layout-engine/measure/measureHelpers.js +21 -5
- package/dist/layout-engine/measure/measureParagraph.js +109 -30
- package/dist/layout-engine/measure/measureTypes.d.ts +5 -0
- package/dist/layout-engine/measure/tabCalculator.d.ts +2 -0
- package/dist/layout-engine/measure/tabCalculator.js +2 -1
- package/dist/layout-engine/measure/tableCellFloating.d.ts +2 -0
- package/dist/layout-engine/measure/tableCellFloating.js +51 -19
- package/dist/layout-engine/measure/tableCellGrid.d.ts +28 -3
- package/dist/layout-engine/measure/tableCellGrid.js +45 -5
- package/dist/layout-engine/measure/tableFragmentBorderGeometry.js +2 -1
- package/dist/layout-engine/noteAreaFlow.d.ts +19 -0
- package/dist/layout-engine/noteAreaFlow.js +83 -0
- package/dist/layout-engine/paginator.d.ts +10 -1
- package/dist/layout-engine/paginator.js +13 -1
- package/dist/layout-engine/tableRowBreak.js +3 -1
- package/dist/layout-engine/types.d.ts +89 -3
- package/dist/layout-engine/types.js +96 -1
- package/dist/layout-painter/index.d.ts +3 -0
- package/dist/layout-painter/renderPage.js +32 -83
- package/dist/layout-painter/renderParagraph.d.ts +6 -1
- package/dist/layout-painter/renderParagraph.js +34 -22
- package/dist/layout-painter/renderTable.js +26 -13
- package/dist/prosemirror/attrs/index.d.ts +9 -9
- package/dist/prosemirror/attrs/index.js +43 -11
- package/dist/prosemirror/conversion/fromProseDoc.js +8 -1
- package/dist/prosemirror/conversion/markInterner.d.ts +21 -0
- package/dist/prosemirror/conversion/markInterner.js +90 -0
- package/dist/prosemirror/conversion/toProseDoc.js +77 -41
- package/dist/prosemirror/extensions/core/DocExtension.js +2 -1
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +1 -0
- package/dist/prosemirror/extensions/marks/markUtils.d.ts +6 -2
- package/dist/prosemirror/extensions/marks/markUtils.js +39 -51
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +2 -1
- package/dist/prosemirror/listMarker.d.ts +1 -0
- package/dist/prosemirror/listMarker.js +2 -0
- package/dist/prosemirror/listRenderingAttrs.d.ts +1 -0
- package/dist/prosemirror/listRenderingAttrs.js +3 -0
- package/dist/prosemirror/schema/nodes.d.ts +13 -1
- package/dist/prosemirror/textBoxHostParagraph.d.ts +13 -0
- package/dist/prosemirror/textBoxHostParagraph.js +21 -0
- package/dist/prosemirror/validation.js +91 -80
- package/dist/utils/createDocument.js +10 -1
- package/dist/utils/scriptSegments.d.ts +33 -4
- package/dist/utils/scriptSegments.js +39 -6
- package/dist/utils/textFormattingMerge.d.ts +17 -3
- package/dist/utils/textFormattingMerge.js +18 -12
- package/dist/utils/trailingText.d.ts +22 -0
- package/dist/utils/trailingText.js +32 -0
- package/package.json +2 -2
|
@@ -17,9 +17,13 @@ import { Result } from "better-result";
|
|
|
17
17
|
* names.
|
|
18
18
|
*/
|
|
19
19
|
type DocumentPaintFeatures = DocumentFeatures;
|
|
20
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* A story this entry does not paginate, named so a caller can see the hole.
|
|
22
|
+
* Every story is laid out now, so `story` admits none; a story that stops
|
|
23
|
+
* being laid out widens it again, and {@link UNPAGINATED_STORIES} must name it.
|
|
24
|
+
*/
|
|
21
25
|
type HeadlessLayoutGap = {
|
|
22
|
-
readonly story:
|
|
26
|
+
readonly story: never;
|
|
23
27
|
readonly detail: string;
|
|
24
28
|
};
|
|
25
29
|
declare const HeadlessLayoutError_base: import("better-result").TaggedErrorClass<"HeadlessLayoutError">;
|
|
@@ -70,6 +74,11 @@ type HeadlessLayoutResult = {
|
|
|
70
74
|
readonly embeddedFonts: readonly EmbeddedFont[];
|
|
71
75
|
readonly unsupported: readonly HeadlessLayoutGap[];
|
|
72
76
|
};
|
|
77
|
+
/**
|
|
78
|
+
* Parse and paginate a package. Fails rather than defaulting when no
|
|
79
|
+
* measurement backend is installed: a layout measured by the wrong provider
|
|
80
|
+
* is wrong in a way no downstream check catches.
|
|
81
|
+
*/
|
|
73
82
|
declare const layoutDocxHeadless: (input: DocxInput, options?: HeadlessLayoutOptions) => Promise<Result<HeadlessLayoutResult, HeadlessLayoutError>>;
|
|
74
83
|
//#endregion
|
|
75
84
|
export { DocumentPaintFeatures, HeadlessLayoutError, HeadlessLayoutGap, HeadlessLayoutOptions, HeadlessLayoutResult, layoutDocxHeadless };
|
package/dist/headless-layout.js
CHANGED
|
@@ -6,11 +6,14 @@ import { parseDocx } from "./docx/parser.js";
|
|
|
6
6
|
import { buildHeaderFooterFieldValues } from "./fields/resolveFieldValues.js";
|
|
7
7
|
import { extractEmbeddedFonts } from "./fonts/embeddedFonts.js";
|
|
8
8
|
import { buildFontAlternates } from "./fonts/fontAlternates.js";
|
|
9
|
+
import { prepareEndnoteAreas, resolveEndnotePosition, spliceEndnoteAreas, trailingEndnoteIds } from "./layout-bridge/convert/endnoteLayout.js";
|
|
9
10
|
import { buildFootnoteContentMap, collectEndnoteRefs, collectFootnoteRefs, computeNoteDisplayNumbers, remapNoteMarkerText } from "./layout-bridge/convert/footnoteLayout.js";
|
|
10
11
|
import { convertHeaderFooterToContent } from "./layout-bridge/convert/headerFooterLayout.js";
|
|
11
12
|
import { toFlowBlocks } from "./layout-bridge/convert/toFlowBlocks.js";
|
|
12
13
|
import { resolveSectionHeaderFooterRefs } from "./layout-engine/headerFooterRefs.js";
|
|
13
14
|
import { layoutDocument } from "./layout-engine/index.js";
|
|
15
|
+
import { collectRequestedHyphenationDictionaries } from "./layout-engine/measure/hyphenationDictionaries.js";
|
|
16
|
+
import { preloadHyphenationDictionaries } from "./layout-engine/measure/hyphenationPreload.js";
|
|
14
17
|
import { measureBlocks } from "./layout-engine/measure/measureBlocks.js";
|
|
15
18
|
import { getMeasureProvider } from "./layout-engine/measure/measureProvider.js";
|
|
16
19
|
import "./layout-engine/types.js";
|
|
@@ -34,14 +37,14 @@ import { createHash } from "node:crypto";
|
|
|
34
37
|
*
|
|
35
38
|
* ## The other stories
|
|
36
39
|
*
|
|
37
|
-
* Headers, footers and
|
|
38
|
-
* lays out as their own ProseMirror views, but
|
|
39
|
-
* `convertHeaderFooterToContent
|
|
40
|
-
* `toFlowBlocks → measureBlocks` chain the
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
40
|
+
* Headers, footers and notes are separate OOXML stories that the editor
|
|
41
|
+
* lays out as their own ProseMirror views, but no conversion needs a view:
|
|
42
|
+
* `convertHeaderFooterToContent`, `buildFootnoteContentMap` and
|
|
43
|
+
* `prepareEndnoteAreas` run the same `toFlowBlocks → measureBlocks` chain the
|
|
44
|
+
* body runs. This entry therefore lays them out too: headers, footers and
|
|
45
|
+
* footnotes come back as {@link HeadlessLayoutResult.furniture}, and endnotes
|
|
46
|
+
* paginate with the body, so an export paints the pages the editor paints
|
|
47
|
+
* rather than bare bodies.
|
|
45
48
|
*
|
|
46
49
|
* What this entry still does not do is push the body down for a header taller
|
|
47
50
|
* than its margin: the editor's margin clearing lives in the controller, and a
|
|
@@ -67,21 +70,9 @@ var HeadlessLayoutError = class extends TaggedError("HeadlessLayoutError") {};
|
|
|
67
70
|
* than content that vanishes without a report, and a story that starts being
|
|
68
71
|
* laid out cannot keep a stale entry, because removing it narrows the union.
|
|
69
72
|
*
|
|
70
|
-
*
|
|
71
|
-
* document rather than reserved per page, so nothing in `Layout` and nothing in
|
|
72
|
-
* the painter's render options places them yet; laying them out is a
|
|
73
|
-
* pagination feature, not a conversion this entry could call.
|
|
73
|
+
* None is left: endnotes, the last, paginate with the body.
|
|
74
74
|
*/
|
|
75
|
-
const UNPAGINATED_STORIES = {
|
|
76
|
-
story: "endnote",
|
|
77
|
-
detail: "Endnotes are their own story and are not paginated with the body."
|
|
78
|
-
} };
|
|
79
|
-
const hasStory = (document, story) => {
|
|
80
|
-
switch (story) {
|
|
81
|
-
case "endnote": return (document.package.endnotes?.length ?? 0) > 0;
|
|
82
|
-
default: return false;
|
|
83
|
-
}
|
|
84
|
-
};
|
|
75
|
+
const UNPAGINATED_STORIES = {};
|
|
85
76
|
const readPaintFeatures = (document) => ({
|
|
86
77
|
pageBorders: (document.package.document.sections ?? []).some((section) => Object.values(section.properties.pageBorders ?? {}).some((border) => border !== void 0)),
|
|
87
78
|
watermark: getDocumentWatermark(document) !== void 0
|
|
@@ -178,26 +169,36 @@ const NONCE_LENGTH = 12;
|
|
|
178
169
|
/** Whether `fontTable.xml` declares any embedded face at all. */
|
|
179
170
|
const declaresEmbeddedFonts = (document) => (document.package.fontTable?.fonts ?? []).some((font) => font.embedRegular !== void 0 || font.embedBold !== void 0 || font.embedItalic !== void 0 || font.embedBoldItalic !== void 0);
|
|
180
171
|
/**
|
|
181
|
-
*
|
|
182
|
-
* measurement backend is installed: a layout measured by the wrong provider
|
|
183
|
-
* is wrong in a way no downstream check catches.
|
|
184
|
-
*/
|
|
185
|
-
/**
|
|
186
|
-
* Whether any text in the package needs shaping.
|
|
172
|
+
* Whether `test` holds for any record in the package's content model.
|
|
187
173
|
*
|
|
188
174
|
* Every story a page paints, body, headers, footers and notes alike, is built
|
|
189
175
|
* from the same content model, so one walk over the package answers for all of
|
|
190
|
-
* them.
|
|
191
|
-
*
|
|
176
|
+
* them. Headers and footers are keyed by relationship id in `Map`s, which
|
|
177
|
+
* `Object.entries` sees as empty, so the walk descends into map values too.
|
|
192
178
|
*/
|
|
193
|
-
const
|
|
194
|
-
if (Array.isArray(value)
|
|
179
|
+
const somePackageRecord = (value, test) => {
|
|
180
|
+
if (Array.isArray(value) || value instanceof Map) {
|
|
181
|
+
for (const child of value.values()) if (somePackageRecord(child, test)) return true;
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
195
184
|
if (typeof value !== "object" || value === null) return false;
|
|
196
185
|
const entries = Object.entries(value);
|
|
197
|
-
|
|
198
|
-
if (entries.find(([key]) => key === "type")?.[1] === "text" && typeof text === "string" && needsShaping(text)) return true;
|
|
199
|
-
return entries.some(([, child]) => packageNeedsShaping(child));
|
|
186
|
+
return test(entries) || entries.some(([, child]) => somePackageRecord(child, test));
|
|
200
187
|
};
|
|
188
|
+
/**
|
|
189
|
+
* Whether any text in the package needs shaping. The answer decides whether
|
|
190
|
+
* the shaper is fetched at all: a document in Latin, Cyrillic or Greek must not
|
|
191
|
+
* pay for an artifact it has no use for.
|
|
192
|
+
*/
|
|
193
|
+
const packageNeedsShaping = (value) => somePackageRecord(value, (entries) => {
|
|
194
|
+
const text = entries.find(([key]) => key === "text")?.[1];
|
|
195
|
+
return entries.find(([key]) => key === "type")?.[1] === "text" && typeof text === "string" && needsShaping(text);
|
|
196
|
+
});
|
|
197
|
+
/**
|
|
198
|
+
* Parse and paginate a package. Fails rather than defaulting when no
|
|
199
|
+
* measurement backend is installed: a layout measured by the wrong provider
|
|
200
|
+
* is wrong in a way no downstream check catches.
|
|
201
|
+
*/
|
|
201
202
|
const layoutDocxHeadless = async (input, options = {}) => {
|
|
202
203
|
const providerCheck = Result.try(() => getMeasureProvider().getFontMetrics({ fontSize: 11 }));
|
|
203
204
|
if (providerCheck.isErr()) return Result.err(new HeadlessLayoutError({
|
|
@@ -245,32 +246,49 @@ const layoutDocxHeadless = async (input, options = {}) => {
|
|
|
245
246
|
const margins = getMargins(firstSection);
|
|
246
247
|
const contentWidth = pageSize.w - margins.left - margins.right;
|
|
247
248
|
const pageContentHeight = pageSize.h - margins.top - margins.bottom;
|
|
249
|
+
const endnotePosition = resolveEndnotePosition(document.package.settings?.endnotePr?.position, finalSection);
|
|
248
250
|
const flowOptions = buildFlowOptions(document, pageContentHeight);
|
|
251
|
+
const bodyTrailingEndnoteIds = trailingEndnoteIds(document.package.endnotes, endnotePosition);
|
|
252
|
+
if (bodyTrailingEndnoteIds.size > 0) flowOptions.trailingEndnoteIds = bodyTrailingEndnoteIds;
|
|
249
253
|
const storyOptions = buildStoryOptions(flowOptions);
|
|
250
|
-
const
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
254
|
+
const layOut = () => {
|
|
255
|
+
const laidOut = Result.try(() => {
|
|
256
|
+
const authored = toFlowBlocks(projected.value, flowOptions);
|
|
257
|
+
const footnotes = document.package.footnotes ?? [];
|
|
258
|
+
const endnotes = document.package.endnotes ?? [];
|
|
259
|
+
const footnoteRefs = collectFootnoteRefs(authored);
|
|
260
|
+
const footnoteNumbers = computeNoteDisplayNumbers(footnotes, footnoteRefs.map((ref) => ref.footnoteId));
|
|
261
|
+
const endnoteNumbers = computeNoteDisplayNumbers(endnotes, collectEndnoteRefs(authored).map((ref) => ref.endnoteId));
|
|
262
|
+
const endnoteNumberFormat = finalSection?.endnotePr?.numFmt ?? document.package.settings?.endnotePr?.numFmt ?? "lowerRoman";
|
|
263
|
+
const endnoteTexts = formatEndnoteTexts(endnoteNumbers, (displayNumber) => formatOoxmlCounter(displayNumber, endnoteNumberFormat));
|
|
264
|
+
const blocks = remapNoteMarkerText(authored, {
|
|
265
|
+
footnoteNumbers,
|
|
266
|
+
endnoteTexts
|
|
267
|
+
});
|
|
268
|
+
const measures = measureBlocks(blocks, contentWidth);
|
|
269
|
+
const footnoteContentById = footnoteRefs.length === 0 ? void 0 : buildFootnoteContentMap(footnotes, footnoteRefs, contentWidth, {
|
|
270
|
+
...storyOptions,
|
|
271
|
+
measureBlocks
|
|
272
|
+
});
|
|
273
|
+
const footnoteHeightById = /* @__PURE__ */ new Map();
|
|
274
|
+
for (const [id, content] of footnoteContentById ?? []) footnoteHeightById.set(id, content.height + 0);
|
|
275
|
+
const endnoteAreas = prepareEndnoteAreas({
|
|
276
|
+
blocks,
|
|
277
|
+
endnotes,
|
|
278
|
+
displayNumbers: endnoteNumbers,
|
|
279
|
+
displayTexts: endnoteTexts,
|
|
280
|
+
position: endnotePosition,
|
|
281
|
+
sections: sections.map(({ properties }) => properties),
|
|
282
|
+
options: {
|
|
283
|
+
...storyOptions,
|
|
284
|
+
measureBlocks
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
const flow = spliceEndnoteAreas(blocks, measures, endnoteAreas);
|
|
288
|
+
const sectionHeaderFooterRefs = resolveSectionHeaderFooterRefs(document);
|
|
289
|
+
const finalPageSize = getPageSize(finalSection);
|
|
290
|
+
const finalMargins = getMargins(finalSection);
|
|
291
|
+
const layoutOptions = {
|
|
274
292
|
pageSize,
|
|
275
293
|
margins,
|
|
276
294
|
pageNumbering: getPageNumbering(firstSection),
|
|
@@ -283,51 +301,79 @@ const layoutDocxHeadless = async (input, options = {}) => {
|
|
|
283
301
|
evenAndOddHeaders: document.package.settings?.evenAndOddHeaders === true,
|
|
284
302
|
mirrorMargins: readsMirrorMargins(document.package.settings),
|
|
285
303
|
...footnoteHeightById.size === 0 ? {} : { footnoteHeightById },
|
|
286
|
-
...sectionHeaderFooterRefs === void 0 ? {} : { sectionHeaderFooterRefs }
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
304
|
+
...sectionHeaderFooterRefs === void 0 ? {} : { sectionHeaderFooterRefs },
|
|
305
|
+
...endnoteAreas === void 0 ? {} : { noteAreas: endnoteAreas.noteAreas }
|
|
306
|
+
};
|
|
307
|
+
const blockLookup = buildBlockLookup(flow.blocks, flow.measures);
|
|
308
|
+
for (const [blockId, noteStory] of endnoteAreas?.noteStoryByBlockId ?? []) {
|
|
309
|
+
const entry = blockLookup.get(blockId);
|
|
310
|
+
if (entry !== void 0) entry.noteStory = noteStory;
|
|
311
|
+
}
|
|
312
|
+
const continuationSeparator = endnoteAreas?.noteAreas.continuationSeparator;
|
|
313
|
+
if (continuationSeparator !== void 0) blockLookup.set(String(continuationSeparator.block.id), continuationSeparator);
|
|
314
|
+
return {
|
|
315
|
+
layout: layoutDocument(flow.blocks, flow.measures, layoutOptions),
|
|
316
|
+
blockLookup,
|
|
317
|
+
footnoteContentById
|
|
318
|
+
};
|
|
319
|
+
});
|
|
320
|
+
if (laidOut.isErr()) return Result.err(new HeadlessLayoutError({
|
|
321
|
+
message: "The document could not be paginated.",
|
|
322
|
+
cause: laidOut.error
|
|
323
|
+
}));
|
|
324
|
+
const { layout, blockLookup, footnoteContentById } = laidOut.value;
|
|
325
|
+
const stories = Result.try(() => {
|
|
326
|
+
const pageCount = layout.pages.length;
|
|
327
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
328
|
+
const shared = {
|
|
329
|
+
contentWidth,
|
|
330
|
+
storyOptions,
|
|
331
|
+
pageCount,
|
|
332
|
+
now
|
|
333
|
+
};
|
|
334
|
+
return {
|
|
335
|
+
headerContentByRId: convertStories({
|
|
336
|
+
...shared,
|
|
337
|
+
parts: document.package.headers,
|
|
338
|
+
metrics: {
|
|
339
|
+
section: "header",
|
|
340
|
+
pageSize,
|
|
341
|
+
margins
|
|
342
|
+
}
|
|
343
|
+
}),
|
|
344
|
+
footerContentByRId: convertStories({
|
|
345
|
+
...shared,
|
|
346
|
+
parts: document.package.footers,
|
|
347
|
+
metrics: {
|
|
348
|
+
section: "footer",
|
|
349
|
+
pageSize,
|
|
350
|
+
margins
|
|
351
|
+
}
|
|
352
|
+
})
|
|
353
|
+
};
|
|
354
|
+
});
|
|
355
|
+
if (stories.isErr()) return Result.err(new HeadlessLayoutError({
|
|
356
|
+
message: "The header and footer stories could not be laid out.",
|
|
357
|
+
cause: stories.error
|
|
358
|
+
}));
|
|
359
|
+
return Result.ok({
|
|
360
|
+
layout,
|
|
361
|
+
blockLookup,
|
|
362
|
+
footnoteContentById,
|
|
363
|
+
...stories.value
|
|
364
|
+
});
|
|
365
|
+
};
|
|
366
|
+
let pass = collectRequestedHyphenationDictionaries(layOut);
|
|
367
|
+
if (pass.missing.size > 0) {
|
|
368
|
+
const loaded = await preloadHyphenationDictionaries(pass.missing);
|
|
369
|
+
if (loaded.isErr()) return Result.err(new HeadlessLayoutError({
|
|
370
|
+
message: "A hyphenation dictionary could not be loaded, and this document needs one.",
|
|
371
|
+
cause: loaded.error
|
|
372
|
+
}));
|
|
373
|
+
pass = collectRequestedHyphenationDictionaries(layOut);
|
|
374
|
+
}
|
|
375
|
+
if (pass.result.isErr()) return Result.err(pass.result.error);
|
|
376
|
+
const { layout, blockLookup, footnoteContentById, headerContentByRId, footerContentByRId } = pass.result.value;
|
|
331
377
|
const embeddedFonts = declaresEmbeddedFonts(document) ? await Result.tryPromise({
|
|
332
378
|
try: async () => {
|
|
333
379
|
const source = await toArrayBuffer(input);
|
|
@@ -356,13 +402,13 @@ const layoutDocxHeadless = async (input, options = {}) => {
|
|
|
356
402
|
...watermark === void 0 ? {} : { watermark },
|
|
357
403
|
watermarkByHeaderRId: watermarksByHeader,
|
|
358
404
|
...watermarkImageSrc === void 0 ? {} : { watermarkImageSrc },
|
|
359
|
-
headerContentByRId
|
|
360
|
-
footerContentByRId
|
|
405
|
+
headerContentByRId,
|
|
406
|
+
footerContentByRId,
|
|
361
407
|
titlePg: firstSection?.titlePg === true,
|
|
362
408
|
...footnoteContentById === void 0 ? {} : { footnoteContentById }
|
|
363
409
|
},
|
|
364
410
|
embeddedFonts: embeddedFonts.value,
|
|
365
|
-
unsupported: Object.values(UNPAGINATED_STORIES)
|
|
411
|
+
unsupported: Object.values(UNPAGINATED_STORIES)
|
|
366
412
|
});
|
|
367
413
|
};
|
|
368
414
|
//#endregion
|
|
@@ -3,7 +3,7 @@ import { serializeBorder } from "../docx/serializer/borderSerializer.js";
|
|
|
3
3
|
import { serializeShading, serializeTextFormatting } from "../docx/serializer/textFormattingSerializer.js";
|
|
4
4
|
import { intAttr } from "../docx/serializer/xmlUtils.js";
|
|
5
5
|
import { TRANSITIONAL_NAME_BY_STRICT_NAME } from "../docx/strictNames.gen.js";
|
|
6
|
-
import {
|
|
6
|
+
import { createCapturedXmlSanitizer } from "../docx/verbatimCapture.js";
|
|
7
7
|
import { NAMESPACES, OOXML_NAMESPACE_SCOPE } from "../docx/xmlParser.js";
|
|
8
8
|
import { escapeXmlAttribute, serializeOnOffElement } from "@stll/docx-core";
|
|
9
9
|
import { outlineLevelStatedValue, paragraphNumberingSlots, sameEffectiveParagraphNumbering } from "@stll/docx-core/model";
|
|
@@ -86,12 +86,12 @@ const serializeNumbering = ({ numPr, numberingChangeXml, numberingInsertionXml }
|
|
|
86
86
|
const NUMBERING_CHANGE_ROOT_NAME = /* @__PURE__ */ new Set(["numberingChange"]);
|
|
87
87
|
const NUMBERING_INSERTION_ROOT_NAME = /* @__PURE__ */ new Set(["ins"]);
|
|
88
88
|
const WORDPROCESSINGML_NAMESPACE = /* @__PURE__ */ new Set([NAMESPACES.w]);
|
|
89
|
-
const replayableNumberingChangeXml = (
|
|
89
|
+
const replayableNumberingChangeXml = createCapturedXmlSanitizer({
|
|
90
90
|
allowedLocalNames: NUMBERING_CHANGE_ROOT_NAME,
|
|
91
91
|
allowedNamespaceUris: WORDPROCESSINGML_NAMESPACE,
|
|
92
92
|
inheritedNamespaceScope: OOXML_NAMESPACE_SCOPE
|
|
93
93
|
});
|
|
94
|
-
const replayableNumberingInsertionXml = (
|
|
94
|
+
const replayableNumberingInsertionXml = createCapturedXmlSanitizer({
|
|
95
95
|
allowedLocalNames: NUMBERING_INSERTION_ROOT_NAME,
|
|
96
96
|
allowedNamespaceUris: WORDPROCESSINGML_NAMESPACE,
|
|
97
97
|
inheritedNamespaceScope: OOXML_NAMESPACE_SCOPE
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { document_d_exports } from "../../types/document.js";
|
|
2
|
+
import { FlowBlock, Measure, NoteAreaLayout } from "../../layout-engine/types.js";
|
|
3
|
+
import { ConvertFootnoteOptions, MeasureBlocksFn } from "./footnoteLayout.js";
|
|
4
|
+
import { NoteStoryKey } from "../../types/editor-story.js";
|
|
5
|
+
//#region src/layout-bridge/convert/endnoteLayout.d.ts
|
|
6
|
+
/** One endnote area and the body position it follows. */
|
|
7
|
+
type EndnoteArea = {
|
|
8
|
+
/**
|
|
9
|
+
* The body `sectionBreak` block the area precedes, counted from zero; `null`
|
|
10
|
+
* places it after the last body block.
|
|
11
|
+
*/
|
|
12
|
+
beforeSectionBreak: number | null;
|
|
13
|
+
blocks: FlowBlock[];
|
|
14
|
+
measures: Measure[];
|
|
15
|
+
};
|
|
16
|
+
type PreparedEndnoteAreas = {
|
|
17
|
+
areas: readonly EndnoteArea[];
|
|
18
|
+
noteAreas: NoteAreaLayout;
|
|
19
|
+
/** The endnote each area content block belongs to, by block id. */
|
|
20
|
+
noteStoryByBlockId: ReadonlyMap<string, NoteStoryKey>;
|
|
21
|
+
};
|
|
22
|
+
type PrepareEndnoteAreasInput = {
|
|
23
|
+
/** The body's top-level flow blocks, markers already renumbered. */
|
|
24
|
+
blocks: readonly FlowBlock[];
|
|
25
|
+
endnotes: readonly document_d_exports.Endnote[] | undefined;
|
|
26
|
+
/** Endnote `w:id` → reference-order number, as the body markers show it. */
|
|
27
|
+
displayNumbers: ReadonlyMap<number, number>;
|
|
28
|
+
/** Endnote `w:id` → the formatted number the body marker shows. */
|
|
29
|
+
displayTexts: ReadonlyMap<number, string>;
|
|
30
|
+
position: document_d_exports.EndnotePosition;
|
|
31
|
+
/**
|
|
32
|
+
* Section properties by section index: `w:noEndnote`, and the column width
|
|
33
|
+
* an area wraps to in the section it follows.
|
|
34
|
+
*/
|
|
35
|
+
sections: readonly (document_d_exports.SectionProperties | undefined)[];
|
|
36
|
+
options: ConvertFootnoteOptions & {
|
|
37
|
+
measureBlocks: MeasureBlocksFn;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Convert and measure every referenced endnote into the areas `w:pos` asks
|
|
42
|
+
* for. `undefined` when no reference names a note, so a document without
|
|
43
|
+
* endnotes lays out exactly as it did before.
|
|
44
|
+
*/
|
|
45
|
+
declare function prepareEndnoteAreas(input: PrepareEndnoteAreasInput): PreparedEndnoteAreas | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* The body blocks with each endnote area in place: before the section break
|
|
48
|
+
* that ends the section it follows, or after the last body block.
|
|
49
|
+
*/
|
|
50
|
+
declare function spliceEndnoteAreas(blocks: FlowBlock[], measures: Measure[], prepared: PreparedEndnoteAreas | undefined): {
|
|
51
|
+
blocks: FlowBlock[];
|
|
52
|
+
measures: Measure[];
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* The endnotes a `docEnd` area collects after the body's last block, for
|
|
56
|
+
* `toFlowBlocks`' `trailingEndnoteIds`. Empty for `sectEnd`, whose final area
|
|
57
|
+
* depends on where the references fall.
|
|
58
|
+
*/
|
|
59
|
+
declare function trailingEndnoteIds(endnotes: readonly document_d_exports.Endnote[] | undefined, position: document_d_exports.EndnotePosition): ReadonlySet<number>;
|
|
60
|
+
/** `w:pos` for the document: the settings value, else the final section's. */
|
|
61
|
+
declare function resolveEndnotePosition(settingsPosition: document_d_exports.EndnotePosition | undefined, finalSection: document_d_exports.SectionProperties | undefined): document_d_exports.EndnotePosition;
|
|
62
|
+
/** The first column's width in a section: what an endnote area there wraps to. */
|
|
63
|
+
declare function endnoteAreaWidth(section: document_d_exports.SectionProperties | undefined): number;
|
|
64
|
+
//#endregion
|
|
65
|
+
export { PrepareEndnoteAreasInput, PreparedEndnoteAreas, endnoteAreaWidth, prepareEndnoteAreas, resolveEndnotePosition, spliceEndnoteAreas, trailingEndnoteIds };
|