@stll/folio-core 0.7.0 → 0.9.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 +39 -0
- package/dist/ai-edits/apply.js +18 -4
- package/dist/ai-edits/blockRange.d.ts +13 -2
- package/dist/ai-edits/blockRange.js +23 -2
- package/dist/ai-edits/headless.d.ts +31 -4
- package/dist/ai-edits/headless.js +148 -35
- package/dist/ai-edits/index.d.ts +6 -4
- package/dist/ai-edits/index.js +4 -1
- package/dist/ai-edits/scoped-reading.d.ts +9 -0
- package/dist/ai-edits/scoped-reading.js +60 -0
- package/dist/ai-edits/snapshot.js +53 -7
- package/dist/ai-edits/types.d.ts +46 -3
- package/dist/compat/eigenpal.d.ts +8 -3
- package/dist/compat/eigenpal.js +7 -2
- package/dist/controller/fontReadiness.d.ts +29 -0
- package/dist/controller/fontReadiness.js +139 -0
- package/dist/controller/latestRequestGate.d.ts +13 -0
- package/dist/controller/latestRequestGate.js +16 -0
- package/dist/controller/layoutPipeline.js +88 -10
- package/dist/document-operations.d.ts +10 -3
- package/dist/document-operations.js +29 -14
- package/dist/docx/blockContentParser.js +53 -10
- package/dist/docx/commentParser.js +1 -1
- package/dist/docx/compatibility.d.ts +30 -2
- package/dist/docx/compatibility.js +118 -39
- package/dist/docx/conformance.d.ts +6 -0
- package/dist/docx/conformance.js +18 -0
- package/dist/docx/encryption/agileDecryption.js +1 -1
- package/dist/docx/encryption/encryptionInfo.js +1 -1
- package/dist/docx/encryption/openEncryptedDocx.js +1 -1
- package/dist/docx/ensureParaIds.d.ts +29 -0
- package/dist/docx/ensureParaIds.js +423 -0
- package/dist/docx/fontTableParser.d.ts +6 -0
- package/dist/docx/fontTableParser.js +72 -0
- package/dist/docx/groupDrawingParser.js +7 -2
- package/dist/docx/headerFooterParser.js +1 -1
- package/dist/docx/normalizeBaseDirection.js +1 -1
- package/dist/docx/numberingParser.js +2 -0
- package/dist/docx/paragraphParser.js +21 -1
- package/dist/docx/paragraphTextBoxEnrichment.js +141 -2
- package/dist/docx/parser.d.ts +1 -1
- package/dist/docx/parser.js +14 -9
- package/dist/docx/rezip.js +70 -5
- package/dist/docx/runConsolidator.js +4 -0
- package/dist/docx/runParser.js +19 -1
- package/dist/docx/selectiveSave.js +3 -3
- package/dist/docx/selectiveXmlPatch.d.ts +10 -1
- package/dist/docx/selectiveXmlPatch.js +1 -1
- package/dist/docx/serializer/fontTableSerializer.d.ts +6 -0
- package/dist/docx/serializer/fontTableSerializer.js +26 -0
- package/dist/docx/serializer/headerFooterSerializer.js +1 -1
- package/dist/docx/serializer/paragraphSerializer.js +8 -0
- package/dist/docx/serializer/runSerializer.js +14 -2
- package/dist/docx/serializer/settingsSerializer.d.ts +6 -0
- package/dist/docx/serializer/settingsSerializer.js +16 -0
- package/dist/docx/serializer/stylesSerializer.d.ts +6 -0
- package/dist/docx/serializer/stylesSerializer.js +56 -0
- package/dist/docx/serializer/tableSerializer.js +29 -26
- package/dist/docx/serializer/themeSerializer.d.ts +6 -0
- package/dist/docx/serializer/themeSerializer.js +36 -0
- package/dist/docx/settingsParser.js +46 -0
- package/dist/docx/styleParser.js +26 -1
- package/dist/docx/tableParser.js +19 -1
- package/dist/docx/textBoxParser.js +6 -1
- package/dist/docx/vmlImageParser.js +146 -1
- package/dist/i18n/messages/catalogs.gen.d.ts +5916 -5916
- package/dist/i18n/messages/catalogs.gen.js +5910 -5910
- package/dist/index.d.ts +8 -3
- package/dist/index.js +7 -2
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +3 -0
- package/dist/layout-bridge/convert/footnoteLayout.js +3 -1
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +3 -0
- package/dist/layout-bridge/convert/headerFooterLayout.js +22 -3
- package/dist/layout-bridge/convert/paragraphFrames.d.ts +7 -0
- package/dist/layout-bridge/convert/paragraphFrames.js +136 -0
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +14 -2
- package/dist/layout-bridge/convert/toFlowBlocks.js +93 -17
- package/dist/layout-bridge/engine/hitTest.js +2 -1
- package/dist/layout-bridge/engine/measuring/index.d.ts +2 -1
- package/dist/layout-bridge/engine/measuring/index.js +2 -1
- package/dist/layout-bridge/engine/selectionRects.js +2 -1
- package/dist/layout-engine/headerFooterRefs.d.ts +7 -0
- package/dist/layout-engine/headerFooterRefs.js +40 -0
- package/dist/layout-engine/index.d.ts +3 -2
- package/dist/layout-engine/index.js +68 -106
- package/dist/layout-engine/measure/cache.js +11 -3
- package/dist/layout-engine/measure/font-metrics.worker.js +1 -0
- package/dist/layout-engine/measure/index.d.ts +2 -1
- package/dist/layout-engine/measure/index.js +2 -1
- package/dist/layout-engine/measure/lineBreakProvider.d.ts +33 -0
- package/dist/layout-engine/measure/lineBreakProvider.js +252 -0
- package/dist/layout-engine/measure/lineBreaks.d.ts +7 -2
- package/dist/layout-engine/measure/lineBreaks.js +11 -18
- package/dist/layout-engine/measure/listMarkerWidth.d.ts +4 -1
- package/dist/layout-engine/measure/listMarkerWidth.js +33 -8
- package/dist/layout-engine/measure/measureBlocks.d.ts +4 -2
- package/dist/layout-engine/measure/measureBlocks.js +138 -86
- package/dist/layout-engine/measure/measureContainer.js +32 -9
- package/dist/layout-engine/measure/measureHelpers.js +5 -2
- package/dist/layout-engine/measure/measureParagraph.js +346 -74
- package/dist/layout-engine/measure/measureTypes.d.ts +2 -1
- package/dist/layout-engine/measure/measureWorker.d.ts +2 -2
- package/dist/layout-engine/measure/measureWorker.js +8 -19
- package/dist/layout-engine/measure/measureWorkerProtocol.d.ts +3 -14
- package/dist/layout-engine/measure/tableCellFloating.d.ts +10 -3
- package/dist/layout-engine/measure/tableCellFloating.js +37 -29
- package/dist/layout-engine/measure/tableCellFlow.d.ts +27 -0
- package/dist/layout-engine/measure/tableCellFlow.js +73 -0
- package/dist/layout-engine/measure/tableCellGrid.d.ts +15 -0
- package/dist/layout-engine/measure/tableCellGrid.js +62 -0
- package/dist/layout-engine/measure/textBoxParagraphLayout.d.ts +18 -0
- package/dist/layout-engine/measure/textBoxParagraphLayout.js +32 -0
- package/dist/layout-engine/measure/textMeasurementPolicy.d.ts +18 -0
- package/dist/layout-engine/measure/textMeasurementPolicy.js +24 -0
- package/dist/layout-engine/paginator.d.ts +2 -1
- package/dist/layout-engine/paginator.js +7 -6
- package/dist/layout-engine/paragraphFrame.d.ts +22 -0
- package/dist/layout-engine/paragraphFrame.js +17 -0
- package/dist/layout-engine/paragraphSequence.d.ts +7 -0
- package/dist/layout-engine/paragraphSequence.js +25 -0
- package/dist/layout-engine/paragraphSpacing.d.ts +17 -0
- package/dist/layout-engine/paragraphSpacing.js +30 -0
- package/dist/layout-engine/renderedBreakReconciliation.d.ts +63 -0
- package/dist/layout-engine/renderedBreakReconciliation.js +86 -0
- package/dist/layout-engine/tableRowBreak.d.ts +5 -2
- package/dist/layout-engine/tableRowBreak.js +62 -29
- package/dist/layout-engine/types.d.ts +54 -12
- package/dist/layout-engine/types.js +15 -2
- package/dist/layout-painter/anchoredImagePosition.d.ts +23 -0
- package/dist/layout-painter/anchoredImagePosition.js +95 -0
- package/dist/layout-painter/borderStroke.d.ts +23 -0
- package/dist/layout-painter/borderStroke.js +39 -0
- package/dist/layout-painter/renderImage.d.ts +5 -4
- package/dist/layout-painter/renderImage.js +18 -4
- package/dist/layout-painter/renderPage.d.ts +2 -27
- package/dist/layout-painter/renderPage.js +15 -101
- package/dist/layout-painter/renderParagraph.js +45 -26
- package/dist/layout-painter/renderTable.d.ts +2 -0
- package/dist/layout-painter/renderTable.js +257 -47
- package/dist/layout-painter/renderTextBox.js +17 -7
- package/dist/managers/DocumentLoaderManager.d.ts +1 -1
- package/dist/managers/DocumentLoaderManager.js +2 -2
- package/dist/paged-layout/sectionBlockWidths.d.ts +3 -0
- package/dist/paged-layout/sectionBlockWidths.js +9 -0
- package/dist/paged-layout/sectionGeometry.js +1 -1
- package/dist/prosemirror/attrs/index.d.ts +4 -2
- package/dist/prosemirror/attrs/index.js +32 -2
- package/dist/prosemirror/commands/propertyChangeScope.js +1 -1
- package/dist/prosemirror/conversion/fromProseDoc.js +35 -18
- package/dist/prosemirror/conversion/toProseDoc.js +73 -37
- package/dist/prosemirror/extensions/StarterKit.js +2 -0
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +8 -2
- package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +1 -1
- package/dist/prosemirror/extensions/features/ListExtension.js +2 -0
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +68 -16
- package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +1 -0
- package/dist/prosemirror/extensions/marks/HighlightExtension.js +1 -1
- package/dist/prosemirror/extensions/marks/LanguageExtension.d.ts +7 -0
- package/dist/prosemirror/extensions/marks/LanguageExtension.js +30 -0
- package/dist/prosemirror/extensions/marks/RunShadingExtension.js +1 -1
- package/dist/prosemirror/extensions/marks/TextColorExtension.js +1 -1
- package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +1 -1
- package/dist/prosemirror/extensions/marks/markUtils.js +12 -0
- package/dist/prosemirror/extensions/nodes/ImageExtension.js +2 -1
- package/dist/prosemirror/extensions/nodes/TableExtension.js +4 -2
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +2 -1
- package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +7 -0
- package/dist/prosemirror/schema/index.d.ts +2 -2
- package/dist/prosemirror/schema/marks.d.ts +6 -1
- package/dist/prosemirror/schema/nodes.d.ts +16 -12
- package/dist/prosemirror/styles/resolvedStyleAttrs.js +2 -0
- package/dist/prosemirror/utils/tabCalculator.d.ts +3 -1
- package/dist/prosemirror/utils/tabCalculator.js +12 -10
- package/dist/prosemirror/validation.js +4 -1
- package/dist/redline.js +13 -3
- package/dist/server.d.ts +9 -4
- package/dist/server.js +7 -2
- package/dist/style-sets/extract.d.ts +35 -0
- package/dist/style-sets/extract.js +123 -0
- package/dist/style-sets/stellaStyle.d.ts +13 -0
- package/dist/style-sets/stellaStyle.js +516 -0
- package/dist/style-sets/types.d.ts +31 -0
- package/dist/style-sets/types.js +5 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/utils/createDocument.d.ts +12 -2
- package/dist/utils/createDocument.js +55 -36
- package/dist/utils/fontResolver.js +6 -6
- package/dist/utils/formatToStyle.js +1 -1
- package/dist/utils/hexId.d.ts +8 -1
- package/dist/utils/hexId.js +11 -3
- package/dist/utils/paragraphFormattingMerge.d.ts +14 -3
- package/dist/utils/paragraphFormattingMerge.js +14 -4
- package/package.json +5 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MeasureWorkerRequest, MeasureWorkerResponse } from "./measureWorkerProtocol.js";
|
|
1
|
+
import { MeasureRequestEntry, MeasureWorkerRequest, MeasureWorkerResponse } from "./measureWorkerProtocol.js";
|
|
2
2
|
|
|
3
3
|
//#region src/layout-engine/measure/measureWorker.d.ts
|
|
4
4
|
/**
|
|
@@ -32,7 +32,7 @@ declare function __setMeasureWorkerTransport(factory: TransportFactory | null):
|
|
|
32
32
|
* `OffscreenCanvas`/`Worker`, or when the proxy has been disabled by
|
|
33
33
|
* a previous error.
|
|
34
34
|
*/
|
|
35
|
-
declare function prefetchMeasurement(
|
|
35
|
+
declare function prefetchMeasurement(request: MeasureRequestEntry): void;
|
|
36
36
|
declare function canPrefetchMeasurement(): boolean;
|
|
37
37
|
/**
|
|
38
38
|
* Test-only: synchronously drain the pending queue. The caller is
|
|
@@ -142,16 +142,10 @@ function flush(current) {
|
|
|
142
142
|
const entries = [];
|
|
143
143
|
for (const entry of current.pending.values()) {
|
|
144
144
|
if (entries.length >= MAX_BATCH_SIZE) break;
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
font: entry.font,
|
|
150
|
-
fontCacheKey: entry.fontCacheKey,
|
|
151
|
-
fontFingerprintWidth: entry.fontFingerprintWidth,
|
|
152
|
-
letterSpacing: entry.letterSpacing,
|
|
153
|
-
horizontalScale: entry.horizontalScale
|
|
154
|
-
});
|
|
145
|
+
const { cacheGeneration: entryGeneration, ...request } = entry;
|
|
146
|
+
current.pending.delete(makeKey(request.text, request.fontCacheKey, request.letterSpacing));
|
|
147
|
+
if (entryGeneration !== cacheGeneration) continue;
|
|
148
|
+
entries.push(request);
|
|
155
149
|
}
|
|
156
150
|
if (entries.length === 0) {
|
|
157
151
|
if (current.pending.size > 0) scheduleFlush(current);
|
|
@@ -196,20 +190,15 @@ function handleResponse(current, message) {
|
|
|
196
190
|
* `OffscreenCanvas`/`Worker`, or when the proxy has been disabled by
|
|
197
191
|
* a previous error.
|
|
198
192
|
*/
|
|
199
|
-
function prefetchMeasurement(
|
|
193
|
+
function prefetchMeasurement(request) {
|
|
200
194
|
if (!isWorkerFontMetricsEnabled()) return;
|
|
201
|
-
if (!text) return;
|
|
195
|
+
if (!request.text) return;
|
|
202
196
|
const current = ensureProxy();
|
|
203
197
|
if (current === null) return;
|
|
204
|
-
const key = makeKey(text, fontCacheKey, letterSpacing);
|
|
198
|
+
const key = makeKey(request.text, request.fontCacheKey, request.letterSpacing);
|
|
205
199
|
if (current.pending.has(key)) return;
|
|
206
200
|
current.pending.set(key, {
|
|
207
|
-
|
|
208
|
-
font,
|
|
209
|
-
fontCacheKey,
|
|
210
|
-
fontFingerprintWidth,
|
|
211
|
-
letterSpacing,
|
|
212
|
-
horizontalScale,
|
|
201
|
+
...request,
|
|
213
202
|
cacheGeneration: getTextWidthCacheGeneration()
|
|
214
203
|
});
|
|
215
204
|
scheduleFlush(current);
|
|
@@ -1,18 +1,6 @@
|
|
|
1
|
+
import { FontKerningMode } from "./textMeasurementPolicy.js";
|
|
2
|
+
|
|
1
3
|
//#region src/layout-engine/measure/measureWorkerProtocol.d.ts
|
|
2
|
-
/**
|
|
3
|
-
* Wire format shared between the main-thread proxy
|
|
4
|
-
* (`measureWorker.ts`) and the worker entry
|
|
5
|
-
* (`font-metrics.worker.ts`).
|
|
6
|
-
*
|
|
7
|
-
* The protocol is intentionally minimal: a batched request carrying an
|
|
8
|
-
* array of measurement inputs, and a matching response with widths in
|
|
9
|
-
* the same order. The worker is a pure function — no state, no cache.
|
|
10
|
-
* The main thread owns the LRU cache and is the sole authority on what
|
|
11
|
-
* is worth measuring.
|
|
12
|
-
*
|
|
13
|
-
* Keep this file tiny and dependency-free so it can be imported from
|
|
14
|
-
* both contexts.
|
|
15
|
-
*/
|
|
16
4
|
/**
|
|
17
5
|
* Single measurement request inside a batch. Mirrors the inputs that
|
|
18
6
|
* `measureTextWidth` already takes on the main thread.
|
|
@@ -40,6 +28,7 @@ type MeasureRequestEntry = {
|
|
|
40
28
|
fontFingerprintWidth: number;
|
|
41
29
|
letterSpacing: number;
|
|
42
30
|
horizontalScale: number;
|
|
31
|
+
fontKerning: FontKerningMode;
|
|
43
32
|
};
|
|
44
33
|
declare const WORKER_FONT_FINGERPRINT_TEXT = "HAMBURGEFONTS ivwqy 0123456789";
|
|
45
34
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TableCell, TableCellMeasure } from "../types.js";
|
|
1
|
+
import { ImageRun, TableCell, TableCellMeasure } from "../types.js";
|
|
2
2
|
import { FloatingImageZone } from "./floatingZones.js";
|
|
3
3
|
|
|
4
4
|
//#region src/layout-engine/measure/tableCellFloating.d.ts
|
|
@@ -8,6 +8,11 @@ type TableCellFloatingImage = {
|
|
|
8
8
|
height: number;
|
|
9
9
|
alt?: string;
|
|
10
10
|
transform?: string;
|
|
11
|
+
opacity?: number;
|
|
12
|
+
cropTop?: number;
|
|
13
|
+
cropRight?: number;
|
|
14
|
+
cropBottom?: number;
|
|
15
|
+
cropLeft?: number;
|
|
11
16
|
x: number;
|
|
12
17
|
y: number;
|
|
13
18
|
side: "left" | "right";
|
|
@@ -19,8 +24,10 @@ type TableCellFloatingImage = {
|
|
|
19
24
|
pmStart?: number;
|
|
20
25
|
pmEnd?: number;
|
|
21
26
|
};
|
|
27
|
+
type TableCellFloatingPosition = Pick<TableCellFloatingImage, "x" | "y" | "side">;
|
|
28
|
+
type ResolveTableCellFloatingPosition = (run: ImageRun, paragraphY: number) => TableCellFloatingPosition;
|
|
22
29
|
declare function getTableCellContentWidth(cell: TableCell | undefined, cellMeasure: TableCellMeasure): number;
|
|
23
|
-
declare function getTableCellFloatingImages(cell: TableCell, cellMeasure: TableCellMeasure, contentWidth: number): TableCellFloatingImage[];
|
|
30
|
+
declare function getTableCellFloatingImages(cell: TableCell, cellMeasure: TableCellMeasure, contentWidth: number, resolvePosition?: ResolveTableCellFloatingPosition): TableCellFloatingImage[];
|
|
24
31
|
declare function buildTableCellFloatingZones(floatingImages: TableCellFloatingImage[], contentWidth: number): FloatingImageZone[];
|
|
25
32
|
//#endregion
|
|
26
|
-
export { TableCellFloatingImage, buildTableCellFloatingZones, getTableCellContentWidth, getTableCellFloatingImages };
|
|
33
|
+
export { ResolveTableCellFloatingPosition, TableCellFloatingImage, buildTableCellFloatingZones, getTableCellContentWidth, getTableCellFloatingImages };
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import { emuToPixels } from "../../utils/units.js";
|
|
2
2
|
import { isFloatingImageRun } from "../types.js";
|
|
3
3
|
import { clampFloatingWrapMargins } from "./clampFloatingWrapMargins.js";
|
|
4
|
+
import { createTableCellFlowState, placeTableCellBlock } from "./tableCellFlow.js";
|
|
4
5
|
//#region src/layout-engine/measure/tableCellFloating.ts
|
|
5
6
|
function getTableCellContentWidth(cell, cellMeasure) {
|
|
6
7
|
const padLeft = cell?.padding?.left ?? 7;
|
|
7
8
|
const padRight = cell?.padding?.right ?? 7;
|
|
8
9
|
return Math.max(0, cellMeasure.width - padLeft - padRight);
|
|
9
10
|
}
|
|
10
|
-
function getTableCellFloatingImages(cell, cellMeasure, contentWidth) {
|
|
11
|
+
function getTableCellFloatingImages(cell, cellMeasure, contentWidth, resolvePosition) {
|
|
11
12
|
const result = [];
|
|
12
|
-
|
|
13
|
+
const flowState = createTableCellFlowState();
|
|
13
14
|
for (let blockIndex = 0; blockIndex < cell.blocks.length; blockIndex++) {
|
|
14
15
|
const block = cell.blocks[blockIndex];
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
16
|
+
const blockMeasure = cellMeasure.blocks[blockIndex];
|
|
17
|
+
if (!block || !blockMeasure) continue;
|
|
18
|
+
const placement = placeTableCellBlock(flowState, block, blockMeasure);
|
|
19
|
+
if (block.kind !== "paragraph") continue;
|
|
20
20
|
for (const run of block.runs) {
|
|
21
21
|
if (run.kind !== "image" || !isFloatingImageRun(run)) continue;
|
|
22
22
|
const position = run.position;
|
|
@@ -24,30 +24,35 @@ function getTableCellFloatingImages(cell, cellMeasure, contentWidth) {
|
|
|
24
24
|
const distBottom = run.distBottom ?? 0;
|
|
25
25
|
const distLeft = run.distLeft ?? 12;
|
|
26
26
|
const distRight = run.distRight ?? 12;
|
|
27
|
-
let side
|
|
28
|
-
let x
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
let side;
|
|
28
|
+
let x;
|
|
29
|
+
let y;
|
|
30
|
+
if (resolvePosition) ({side, x, y} = resolvePosition(run, placement.contentTop));
|
|
31
|
+
else {
|
|
32
|
+
side = "left";
|
|
33
|
+
x = 0;
|
|
34
|
+
if (position?.horizontal) {
|
|
35
|
+
const horizontal = position.horizontal;
|
|
36
|
+
if (horizontal.align === "right") {
|
|
37
|
+
side = "right";
|
|
38
|
+
x = contentWidth - run.width;
|
|
39
|
+
} else if (horizontal.align === "center") x = (contentWidth - run.width) / 2;
|
|
40
|
+
else if (horizontal.posOffset !== void 0) {
|
|
41
|
+
x = emuToPixels(horizontal.posOffset);
|
|
42
|
+
side = x > contentWidth / 2 ? "right" : "left";
|
|
43
|
+
}
|
|
44
|
+
} else if (run.cssFloat === "right") {
|
|
32
45
|
side = "right";
|
|
33
46
|
x = contentWidth - run.width;
|
|
34
|
-
} else if (horizontal.align === "left") x = 0;
|
|
35
|
-
else if (horizontal.align === "center") x = (contentWidth - run.width) / 2;
|
|
36
|
-
else if (horizontal.posOffset !== void 0) {
|
|
37
|
-
x = emuToPixels(horizontal.posOffset);
|
|
38
|
-
side = x > contentWidth / 2 ? "right" : "left";
|
|
39
47
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (vertical.posOffset !== void 0) y = paragraphY + emuToPixels(vertical.posOffset);
|
|
48
|
-
else if (vertical.align === "top") y = 0;
|
|
48
|
+
y = placement.contentTop;
|
|
49
|
+
if (position?.vertical) {
|
|
50
|
+
const vertical = position.vertical;
|
|
51
|
+
if (vertical.posOffset !== void 0) y = placement.contentTop + emuToPixels(vertical.posOffset);
|
|
52
|
+
else if (vertical.align === "top") y = 0;
|
|
53
|
+
}
|
|
54
|
+
x = Math.max(0, Math.min(x, contentWidth - run.width));
|
|
49
55
|
}
|
|
50
|
-
x = Math.max(0, Math.min(x, contentWidth - run.width));
|
|
51
56
|
let wrapText = "bothSides";
|
|
52
57
|
if (run.cssFloat === "left") wrapText = "right";
|
|
53
58
|
else if (run.cssFloat === "right") wrapText = "left";
|
|
@@ -57,6 +62,11 @@ function getTableCellFloatingImages(cell, cellMeasure, contentWidth) {
|
|
|
57
62
|
height: run.height,
|
|
58
63
|
...run.alt !== void 0 ? { alt: run.alt } : {},
|
|
59
64
|
...run.transform !== void 0 ? { transform: run.transform } : {},
|
|
65
|
+
...run.opacity != null ? { opacity: run.opacity } : {},
|
|
66
|
+
...run.cropTop != null ? { cropTop: run.cropTop } : {},
|
|
67
|
+
...run.cropRight != null ? { cropRight: run.cropRight } : {},
|
|
68
|
+
...run.cropBottom != null ? { cropBottom: run.cropBottom } : {},
|
|
69
|
+
...run.cropLeft != null ? { cropLeft: run.cropLeft } : {},
|
|
60
70
|
x,
|
|
61
71
|
y,
|
|
62
72
|
side,
|
|
@@ -69,8 +79,6 @@ function getTableCellFloatingImages(cell, cellMeasure, contentWidth) {
|
|
|
69
79
|
...run.pmEnd !== void 0 ? { pmEnd: run.pmEnd } : {}
|
|
70
80
|
});
|
|
71
81
|
}
|
|
72
|
-
const blockMeasure = cellMeasure.blocks[blockIndex];
|
|
73
|
-
if (blockMeasure?.kind === "paragraph") paragraphY += blockMeasure.totalHeight;
|
|
74
82
|
}
|
|
75
83
|
return result;
|
|
76
84
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { FlowBlock, Measure } from "../types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/layout-engine/measure/tableCellFlow.d.ts
|
|
4
|
+
type TableCellBlockPlacement = {
|
|
5
|
+
top: number;
|
|
6
|
+
contentTop: number;
|
|
7
|
+
contentHeight: number;
|
|
8
|
+
leadingSpacing: number;
|
|
9
|
+
};
|
|
10
|
+
type TableCellFlowState = {
|
|
11
|
+
height: number;
|
|
12
|
+
previousParagraphWasEmpty: boolean;
|
|
13
|
+
trailingSpacing: number;
|
|
14
|
+
};
|
|
15
|
+
declare const createTableCellFlowState: () => TableCellFlowState;
|
|
16
|
+
declare const finishTableCellFlow: (state: TableCellFlowState) => number;
|
|
17
|
+
declare const getTableCellBlockContentHeight: (block: FlowBlock, measure: Measure) => number;
|
|
18
|
+
/**
|
|
19
|
+
* Place one block in an unpaginated table-cell story.
|
|
20
|
+
*
|
|
21
|
+
* Adjacent paragraph spacing collapses to the larger side. Consecutive
|
|
22
|
+
* authored empty paragraphs remain independent vertical spacers, including
|
|
23
|
+
* at the end of the cell.
|
|
24
|
+
*/
|
|
25
|
+
declare const placeTableCellBlock: (state: TableCellFlowState, block: FlowBlock, measure: Measure) => TableCellBlockPlacement;
|
|
26
|
+
//#endregion
|
|
27
|
+
export { TableCellBlockPlacement, TableCellFlowState, createTableCellFlowState, finishTableCellFlow, getTableCellBlockContentHeight, placeTableCellBlock };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { measuredLineRangeHeight } from "../lineFlow.js";
|
|
2
|
+
import { isFloatingTextBoxBlock } from "../types.js";
|
|
3
|
+
import { getParagraphSpacingAfter, getParagraphSpacingBefore, isEmptyParagraph } from "../paragraphSpacing.js";
|
|
4
|
+
//#region src/layout-engine/measure/tableCellFlow.ts
|
|
5
|
+
const createTableCellFlowState = () => ({
|
|
6
|
+
height: 0,
|
|
7
|
+
previousParagraphWasEmpty: false,
|
|
8
|
+
trailingSpacing: 0
|
|
9
|
+
});
|
|
10
|
+
const finishTableCellFlow = (state) => state.height + state.trailingSpacing;
|
|
11
|
+
const isSuppressedParagraphMeasure = (measure) => measure.totalHeight === 0 && measure.lines.every(({ lineHeight }) => lineHeight === 0);
|
|
12
|
+
const paragraphSpacing = (block, measure) => {
|
|
13
|
+
if (isSuppressedParagraphMeasure(measure)) return {
|
|
14
|
+
before: 0,
|
|
15
|
+
after: 0
|
|
16
|
+
};
|
|
17
|
+
return {
|
|
18
|
+
before: getParagraphSpacingBefore(block),
|
|
19
|
+
after: getParagraphSpacingAfter(block)
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
const getTableCellBlockContentHeight = (block, measure) => {
|
|
23
|
+
if (block.kind === "textBox" && isFloatingTextBoxBlock(block)) return 0;
|
|
24
|
+
if (block.kind === "paragraph" && measure.kind === "paragraph") return measuredLineRangeHeight(measure.lines, 0, measure.lines.length);
|
|
25
|
+
if ("totalHeight" in measure) return measure.totalHeight;
|
|
26
|
+
if ("height" in measure) return measure.height;
|
|
27
|
+
return 0;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Place one block in an unpaginated table-cell story.
|
|
31
|
+
*
|
|
32
|
+
* Adjacent paragraph spacing collapses to the larger side. Consecutive
|
|
33
|
+
* authored empty paragraphs remain independent vertical spacers, including
|
|
34
|
+
* at the end of the cell.
|
|
35
|
+
*/
|
|
36
|
+
const placeTableCellBlock = (state, block, measure) => {
|
|
37
|
+
const top = state.height;
|
|
38
|
+
const contentHeight = getTableCellBlockContentHeight(block, measure);
|
|
39
|
+
if (block.kind === "paragraph" && measure.kind === "paragraph" && isSuppressedParagraphMeasure(measure)) return {
|
|
40
|
+
top,
|
|
41
|
+
contentTop: top,
|
|
42
|
+
contentHeight: 0,
|
|
43
|
+
leadingSpacing: 0
|
|
44
|
+
};
|
|
45
|
+
if (block.kind !== "paragraph" || measure.kind !== "paragraph") {
|
|
46
|
+
const leadingSpacing = state.trailingSpacing;
|
|
47
|
+
const contentTop = top + leadingSpacing;
|
|
48
|
+
state.height = contentTop + contentHeight;
|
|
49
|
+
state.previousParagraphWasEmpty = false;
|
|
50
|
+
state.trailingSpacing = 0;
|
|
51
|
+
return {
|
|
52
|
+
top,
|
|
53
|
+
contentTop,
|
|
54
|
+
contentHeight,
|
|
55
|
+
leadingSpacing
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const spacing = paragraphSpacing(block, measure);
|
|
59
|
+
const empty = isEmptyParagraph(block);
|
|
60
|
+
const leadingSpacing = empty && state.previousParagraphWasEmpty ? spacing.before + state.trailingSpacing : Math.max(spacing.before, state.trailingSpacing);
|
|
61
|
+
const contentTop = top + leadingSpacing;
|
|
62
|
+
state.height = contentTop + contentHeight;
|
|
63
|
+
state.previousParagraphWasEmpty = empty;
|
|
64
|
+
state.trailingSpacing = spacing.after;
|
|
65
|
+
return {
|
|
66
|
+
top,
|
|
67
|
+
contentTop,
|
|
68
|
+
contentHeight,
|
|
69
|
+
leadingSpacing
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
//#endregion
|
|
73
|
+
export { createTableCellFlowState, finishTableCellFlow, getTableCellBlockContentHeight, placeTableCellBlock };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { TableCell, TableRow } from "../types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/layout-engine/measure/tableCellGrid.d.ts
|
|
4
|
+
type TableCellGrid = {
|
|
5
|
+
occupiedColumnsByRow: ReadonlyMap<number, ReadonlySet<number>>;
|
|
6
|
+
sourceCellsByRow: ReadonlyMap<number, ReadonlyMap<number, TableCell>>;
|
|
7
|
+
sourceColumnsByCell: ReadonlyMap<TableCell, number>;
|
|
8
|
+
};
|
|
9
|
+
declare const buildTableCellGrid: (rows: readonly TableRow[], columnCount: number) => TableCellGrid;
|
|
10
|
+
declare const getFirstAvailableColumn: (grid: TableCellGrid, rowIndex: number, startingColumn: number) => number;
|
|
11
|
+
declare const getSourceCellAt: (grid: TableCellGrid, rowIndex: number, columnIndex: number) => TableCell | undefined;
|
|
12
|
+
declare const getSourceCellColumn: (grid: TableCellGrid, cell: TableCell) => number | undefined;
|
|
13
|
+
declare const getTableCellVerticalBorderHeight: (grid: TableCellGrid, cell: TableCell | undefined, rowIndex: number) => number;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { TableCellGrid, buildTableCellGrid, getFirstAvailableColumn, getSourceCellAt, getSourceCellColumn, getTableCellVerticalBorderHeight };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
//#region src/layout-engine/measure/tableCellGrid.ts
|
|
2
|
+
const buildTableCellGrid = (rows, columnCount) => {
|
|
3
|
+
const occupiedColumnsByRow = /* @__PURE__ */ new Map();
|
|
4
|
+
const sourceCellsByRow = /* @__PURE__ */ new Map();
|
|
5
|
+
const sourceColumnsByCell = /* @__PURE__ */ new Map();
|
|
6
|
+
for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
|
|
7
|
+
const row = rows[rowIndex];
|
|
8
|
+
if (!row) continue;
|
|
9
|
+
let columnIndex = firstAvailableColumn(occupiedColumnsByRow.get(rowIndex), row.gridBefore ?? 0);
|
|
10
|
+
for (const cell of row.cells) {
|
|
11
|
+
const columnSpan = cell.colSpan ?? 1;
|
|
12
|
+
const rowSpan = cell.rowSpan ?? 1;
|
|
13
|
+
sourceColumnsByCell.set(cell, columnIndex);
|
|
14
|
+
const rowEnd = Math.min(rows.length, rowIndex + rowSpan);
|
|
15
|
+
const columnEnd = Math.min(columnCount, columnIndex + columnSpan);
|
|
16
|
+
for (let gridRowIndex = rowIndex; gridRowIndex < rowEnd; gridRowIndex++) {
|
|
17
|
+
const cellsByColumn = getOrCreateCellRow(sourceCellsByRow, gridRowIndex);
|
|
18
|
+
for (let gridColumnIndex = columnIndex; gridColumnIndex < columnEnd; gridColumnIndex++) cellsByColumn.set(gridColumnIndex, cell);
|
|
19
|
+
}
|
|
20
|
+
if (rowSpan > 1) for (let spannedRowIndex = rowIndex + 1; spannedRowIndex < rowIndex + rowSpan; spannedRowIndex++) {
|
|
21
|
+
const occupied = getOrCreateOccupiedRow(occupiedColumnsByRow, spannedRowIndex);
|
|
22
|
+
for (let columnOffset = 0; columnOffset < columnSpan; columnOffset++) occupied.add(columnIndex + columnOffset);
|
|
23
|
+
}
|
|
24
|
+
columnIndex = firstAvailableColumn(occupiedColumnsByRow.get(rowIndex), columnIndex + columnSpan);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
occupiedColumnsByRow,
|
|
29
|
+
sourceCellsByRow,
|
|
30
|
+
sourceColumnsByCell
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
const getFirstAvailableColumn = (grid, rowIndex, startingColumn) => firstAvailableColumn(grid.occupiedColumnsByRow.get(rowIndex), startingColumn);
|
|
34
|
+
const getSourceCellAt = (grid, rowIndex, columnIndex) => grid.sourceCellsByRow.get(rowIndex)?.get(columnIndex);
|
|
35
|
+
const getSourceCellColumn = (grid, cell) => grid.sourceColumnsByCell.get(cell);
|
|
36
|
+
const getTableCellVerticalBorderHeight = (grid, cell, rowIndex) => {
|
|
37
|
+
const sourceColumn = cell ? getSourceCellColumn(grid, cell) : void 0;
|
|
38
|
+
const aboveBottom = (sourceColumn === void 0 ? void 0 : getSourceCellAt(grid, rowIndex - 1, sourceColumn))?.borders?.bottom;
|
|
39
|
+
const aboveOwnsEdge = aboveBottom !== void 0 && aboveBottom.style !== "none" && aboveBottom.style !== "nil";
|
|
40
|
+
return (rowIndex === 0 || !aboveOwnsEdge ? cell?.borders?.top?.width ?? 0 : 0) + (cell?.borders?.bottom?.width ?? 0);
|
|
41
|
+
};
|
|
42
|
+
const firstAvailableColumn = (occupiedColumns, startingColumn) => {
|
|
43
|
+
let columnIndex = startingColumn;
|
|
44
|
+
while (occupiedColumns?.has(columnIndex)) columnIndex++;
|
|
45
|
+
return columnIndex;
|
|
46
|
+
};
|
|
47
|
+
const getOrCreateCellRow = (sourceCellsByRow, rowIndex) => {
|
|
48
|
+
const existing = sourceCellsByRow.get(rowIndex);
|
|
49
|
+
if (existing !== void 0) return existing;
|
|
50
|
+
const cellsByColumn = /* @__PURE__ */ new Map();
|
|
51
|
+
sourceCellsByRow.set(rowIndex, cellsByColumn);
|
|
52
|
+
return cellsByColumn;
|
|
53
|
+
};
|
|
54
|
+
const getOrCreateOccupiedRow = (occupiedColumnsByRow, rowIndex) => {
|
|
55
|
+
const existing = occupiedColumnsByRow.get(rowIndex);
|
|
56
|
+
if (existing !== void 0) return existing;
|
|
57
|
+
const occupiedColumns = /* @__PURE__ */ new Set();
|
|
58
|
+
occupiedColumnsByRow.set(rowIndex, occupiedColumns);
|
|
59
|
+
return occupiedColumns;
|
|
60
|
+
};
|
|
61
|
+
//#endregion
|
|
62
|
+
export { buildTableCellGrid, getFirstAvailableColumn, getSourceCellAt, getSourceCellColumn, getTableCellVerticalBorderHeight };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ParagraphBlock, ParagraphMeasure } from "../types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/layout-engine/measure/textBoxParagraphLayout.d.ts
|
|
4
|
+
type TextBoxParagraphPlacement = {
|
|
5
|
+
leadingSpacing: number;
|
|
6
|
+
contentHeight: number;
|
|
7
|
+
};
|
|
8
|
+
type TextBoxParagraphLayout = {
|
|
9
|
+
placements: TextBoxParagraphPlacement[];
|
|
10
|
+
totalHeight: number;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Lay out a text box's paragraph story as one unpaginated flow. Adjacent
|
|
14
|
+
* before/after spacing collapses to the larger value, matching body flow.
|
|
15
|
+
*/
|
|
16
|
+
declare function layoutTextBoxParagraphs(blocks: readonly ParagraphBlock[], measures: readonly ParagraphMeasure[]): TextBoxParagraphLayout;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { TextBoxParagraphLayout, TextBoxParagraphPlacement, layoutTextBoxParagraphs };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { measuredLineRangeHeight } from "../lineFlow.js";
|
|
2
|
+
import { getParagraphSpacingAfter, getParagraphSpacingBefore } from "../paragraphSpacing.js";
|
|
3
|
+
import { panic } from "better-result";
|
|
4
|
+
//#region src/layout-engine/measure/textBoxParagraphLayout.ts
|
|
5
|
+
/**
|
|
6
|
+
* Lay out a text box's paragraph story as one unpaginated flow. Adjacent
|
|
7
|
+
* before/after spacing collapses to the larger value, matching body flow.
|
|
8
|
+
*/
|
|
9
|
+
function layoutTextBoxParagraphs(blocks, measures) {
|
|
10
|
+
if (blocks.length !== measures.length) panic("layoutTextBoxParagraphs: block and measure counts must match");
|
|
11
|
+
const placements = [];
|
|
12
|
+
let totalHeight = 0;
|
|
13
|
+
let trailingSpacing = 0;
|
|
14
|
+
for (let index = 0; index < blocks.length; index += 1) {
|
|
15
|
+
const block = blocks[index];
|
|
16
|
+
const measure = measures[index];
|
|
17
|
+
const leadingSpacing = Math.max(getParagraphSpacingBefore(block), trailingSpacing);
|
|
18
|
+
const contentHeight = measuredLineRangeHeight(measure.lines, 0, measure.lines.length);
|
|
19
|
+
placements.push({
|
|
20
|
+
leadingSpacing,
|
|
21
|
+
contentHeight
|
|
22
|
+
});
|
|
23
|
+
totalHeight += leadingSpacing + contentHeight;
|
|
24
|
+
trailingSpacing = getParagraphSpacingAfter(block);
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
placements,
|
|
28
|
+
totalHeight: totalHeight + trailingSpacing
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { layoutTextBoxParagraphs };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { RunFormatting } from "../types.js";
|
|
2
|
+
import { FontStyle } from "./measureTypes.js";
|
|
3
|
+
|
|
4
|
+
//#region src/layout-engine/measure/textMeasurementPolicy.d.ts
|
|
5
|
+
declare const FONT_KERNING_MODE: {
|
|
6
|
+
readonly enabled: "normal";
|
|
7
|
+
readonly disabled: "none";
|
|
8
|
+
};
|
|
9
|
+
type FontKerningMode = (typeof FONT_KERNING_MODE)[keyof typeof FONT_KERNING_MODE];
|
|
10
|
+
type RunKerningInput = Pick<RunFormatting, "fontSize" | "kerningMinPt">;
|
|
11
|
+
/** Resolve an authored kerning threshold against the effective run size. */
|
|
12
|
+
declare function getRunFontKerningMode(run: RunKerningInput, fallbackFontSize: number): FontKerningMode;
|
|
13
|
+
/** Convert normalized measurement style into the canvas/CSS kerning mode. */
|
|
14
|
+
declare function getFontKerningMode(style: Pick<FontStyle, "kerning">): FontKerningMode;
|
|
15
|
+
/** Count ordinary ASCII spaces that justification may compress. */
|
|
16
|
+
declare function countCompressibleSpaces(text: string): number;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { FONT_KERNING_MODE, FontKerningMode, countCompressibleSpaces, getFontKerningMode, getRunFontKerningMode };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region src/layout-engine/measure/textMeasurementPolicy.ts
|
|
2
|
+
const FONT_KERNING_MODE = {
|
|
3
|
+
enabled: "normal",
|
|
4
|
+
disabled: "none"
|
|
5
|
+
};
|
|
6
|
+
/** Resolve an authored kerning threshold against the effective run size. */
|
|
7
|
+
function getRunFontKerningMode(run, fallbackFontSize) {
|
|
8
|
+
const threshold = run.kerningMinPt;
|
|
9
|
+
const fontSize = run.fontSize ?? fallbackFontSize;
|
|
10
|
+
if (threshold === void 0 || fontSize < threshold) return FONT_KERNING_MODE.disabled;
|
|
11
|
+
return FONT_KERNING_MODE.enabled;
|
|
12
|
+
}
|
|
13
|
+
/** Convert normalized measurement style into the canvas/CSS kerning mode. */
|
|
14
|
+
function getFontKerningMode(style) {
|
|
15
|
+
return style.kerning ? FONT_KERNING_MODE.enabled : FONT_KERNING_MODE.disabled;
|
|
16
|
+
}
|
|
17
|
+
/** Count ordinary ASCII spaces that justification may compress. */
|
|
18
|
+
function countCompressibleSpaces(text) {
|
|
19
|
+
let count = 0;
|
|
20
|
+
for (const char of text) if (char === " ") count += 1;
|
|
21
|
+
return count;
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
export { FONT_KERNING_MODE, countCompressibleSpaces, getFontKerningMode, getRunFontKerningMode };
|
|
@@ -35,7 +35,8 @@ type PaginatorOptions = {
|
|
|
35
35
|
* its title page (typically extended top margin to clear an overflowing
|
|
36
36
|
* first-page header) vs. its body pages. Pages 2+ use `margins`.
|
|
37
37
|
*/
|
|
38
|
-
firstPageMargins?: PageMargins; /**
|
|
38
|
+
firstPageMargins?: PageMargins; /** Per-section body margins used on even section pages. */
|
|
39
|
+
sectionEvenPageMargins?: (PageMargins | undefined)[]; /** Column configuration (optional). */
|
|
39
40
|
columns?: ColumnLayout; /** Per-page footnote reserved heights (pageNumber → height in pixels). */
|
|
40
41
|
footnoteReservedHeights?: Map<number, number>; /** Header/footer refs by section index. */
|
|
41
42
|
sectionHeaderFooterRefs?: PageHeaderFooterRefs[]; /** Callback when a new page is created. */
|
|
@@ -47,7 +47,7 @@ function createPaginator(options) {
|
|
|
47
47
|
}
|
|
48
48
|
function currentPageUsesActiveLayout(state) {
|
|
49
49
|
if (pendingPageSize || pendingMargins) return false;
|
|
50
|
-
return arePageSizesEqual(state.page.size, pageSize) && areMarginsEqual(state.page.margins, getPageMargins(state.page.number));
|
|
50
|
+
return arePageSizesEqual(state.page.size, pageSize) && areMarginsEqual(state.page.margins, getPageMargins(state.page.number, state.page.sectionPageNumber ?? currentSectionPageNumber));
|
|
51
51
|
}
|
|
52
52
|
if (getContentHeight() <= 0) panic("Paginator: page size and margins yield no content area");
|
|
53
53
|
let columnWidths = calculateColumnWidths(pageSize.w, margins.left, margins.right, columns);
|
|
@@ -62,8 +62,9 @@ function createPaginator(options) {
|
|
|
62
62
|
if (getContentHeight() <= 0) panic("Paginator: section page size and margins yield no content area");
|
|
63
63
|
recalculateColumnWidths();
|
|
64
64
|
}
|
|
65
|
-
function getPageMargins(pageNumber) {
|
|
66
|
-
const
|
|
65
|
+
function getPageMargins(pageNumber, sectionPageNumber) {
|
|
66
|
+
const evenMargins = sectionPageNumber % 2 === 0 ? options.sectionEvenPageMargins?.[currentSectionIndex] : void 0;
|
|
67
|
+
const pageMargins = pageNumber === 1 && options.firstPageMargins ? { ...options.firstPageMargins } : { ...evenMargins ?? margins };
|
|
67
68
|
if (options.mirrorMargins === true && pageNumber % 2 === 0) return {
|
|
68
69
|
...pageMargins,
|
|
69
70
|
left: pageMargins.right,
|
|
@@ -77,7 +78,7 @@ function createPaginator(options) {
|
|
|
77
78
|
* Get X position for a given column index.
|
|
78
79
|
*/
|
|
79
80
|
function getColumnX(columnIndex) {
|
|
80
|
-
let x = states.at(-1)?.page.margins.left ?? getPageMargins(1).left;
|
|
81
|
+
let x = states.at(-1)?.page.margins.left ?? getPageMargins(1, 1).left;
|
|
81
82
|
for (let index = 0; index < columnIndex; index++) x += (columnWidths[index] ?? columnWidths[0] ?? 0) + gapAfterColumn(columns, index);
|
|
82
83
|
return x;
|
|
83
84
|
}
|
|
@@ -88,7 +89,7 @@ function createPaginator(options) {
|
|
|
88
89
|
if (pendingPageSize || pendingMargins) applyPendingLayout();
|
|
89
90
|
const pageNumber = pages.length + 1;
|
|
90
91
|
currentSectionPageNumber += 1;
|
|
91
|
-
const pageMargins = getPageMargins(pageNumber);
|
|
92
|
+
const pageMargins = getPageMargins(pageNumber, currentSectionPageNumber);
|
|
92
93
|
const topMargin = pageMargins.top;
|
|
93
94
|
const contentBottom = pageSize.h - pageMargins.bottom;
|
|
94
95
|
const footnoteHeight = options.footnoteReservedHeights?.get(pageNumber) ?? 0;
|
|
@@ -244,7 +245,7 @@ function createPaginator(options) {
|
|
|
244
245
|
const current = states.at(-1);
|
|
245
246
|
if (!current || current.page.fragments.length > 0 || current.cursorY !== current.topMargin) return false;
|
|
246
247
|
if (pendingPageSize || pendingMargins) applyPendingLayout();
|
|
247
|
-
const pageMargins = getPageMargins(current.page.number);
|
|
248
|
+
const pageMargins = getPageMargins(current.page.number, 1);
|
|
248
249
|
const topMargin = pageMargins.top;
|
|
249
250
|
const rawContentBottom = pageSize.h - pageMargins.bottom;
|
|
250
251
|
const footnoteHeight = options.footnoteReservedHeights?.get(current.page.number) ?? 0;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ParagraphBlock, TextBoxBlock } from "./types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/layout-engine/paragraphFrame.d.ts
|
|
4
|
+
type ParagraphFrame = {
|
|
5
|
+
width?: number;
|
|
6
|
+
height?: number;
|
|
7
|
+
hSpace?: number;
|
|
8
|
+
vSpace?: number;
|
|
9
|
+
hAnchor?: "text" | "margin" | "page";
|
|
10
|
+
vAnchor?: "text" | "margin" | "page";
|
|
11
|
+
x?: number;
|
|
12
|
+
y?: number;
|
|
13
|
+
xAlign?: "left" | "center" | "right" | "inside" | "outside";
|
|
14
|
+
yAlign?: "top" | "center" | "bottom" | "inside" | "outside" | "inline";
|
|
15
|
+
wrap?: "around" | "auto" | "none" | "notBeside" | "through" | "tight";
|
|
16
|
+
};
|
|
17
|
+
declare function setParagraphFrame(block: ParagraphBlock, frame: ParagraphFrame): void;
|
|
18
|
+
declare function getParagraphFrame(block: ParagraphBlock): ParagraphFrame | undefined;
|
|
19
|
+
declare function markParagraphFrameTextBox(block: TextBoxBlock): void;
|
|
20
|
+
declare function isParagraphFrameTextBox(block: TextBoxBlock): boolean;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { ParagraphFrame, getParagraphFrame, isParagraphFrameTextBox, markParagraphFrameTextBox, setParagraphFrame };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/layout-engine/paragraphFrame.ts
|
|
2
|
+
const paragraphFrames = /* @__PURE__ */ new WeakMap();
|
|
3
|
+
const paragraphFrameTextBoxes = /* @__PURE__ */ new WeakSet();
|
|
4
|
+
function setParagraphFrame(block, frame) {
|
|
5
|
+
paragraphFrames.set(block, frame);
|
|
6
|
+
}
|
|
7
|
+
function getParagraphFrame(block) {
|
|
8
|
+
return paragraphFrames.get(block);
|
|
9
|
+
}
|
|
10
|
+
function markParagraphFrameTextBox(block) {
|
|
11
|
+
paragraphFrameTextBoxes.add(block);
|
|
12
|
+
}
|
|
13
|
+
function isParagraphFrameTextBox(block) {
|
|
14
|
+
return paragraphFrameTextBoxes.has(block);
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
export { getParagraphFrame, isParagraphFrameTextBox, markParagraphFrameTextBox, setParagraphFrame };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { FlowBlock } from "./types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/layout-engine/paragraphSequence.d.ts
|
|
4
|
+
declare const continuesNumberedSequence: (previous: FlowBlock | undefined, current: FlowBlock) => boolean;
|
|
5
|
+
declare const continuesTabbedParagraphSequence: (previous: FlowBlock | undefined, current: FlowBlock) => boolean;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { continuesNumberedSequence, continuesTabbedParagraphSequence };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/layout-engine/paragraphSequence.ts
|
|
2
|
+
const continuesNumberedSequence = (previous, current) => {
|
|
3
|
+
if (previous?.kind !== "paragraph" || current.kind !== "paragraph") return false;
|
|
4
|
+
const previousNumPr = previous.attrs?.numPr;
|
|
5
|
+
const currentNumPr = current.attrs?.numPr;
|
|
6
|
+
if (previousNumPr?.numId === void 0 || currentNumPr?.numId === void 0) return false;
|
|
7
|
+
return previousNumPr.numId === currentNumPr.numId && previousNumPr.ilvl === currentNumPr.ilvl;
|
|
8
|
+
};
|
|
9
|
+
const continuesTabbedParagraphSequence = (previous, current) => {
|
|
10
|
+
if (previous?.kind !== "paragraph" || current.kind !== "paragraph") return false;
|
|
11
|
+
if (!previous.attrs?.styleId || previous.attrs.styleId !== current.attrs?.styleId) return false;
|
|
12
|
+
if (!previous.runs.some((run) => run.kind === "tab") || !current.runs.some((run) => run.kind === "tab")) return false;
|
|
13
|
+
const previousIndent = previous.attrs.indent;
|
|
14
|
+
const currentIndent = current.attrs.indent;
|
|
15
|
+
if (previousIndent?.left !== currentIndent?.left || previousIndent?.right !== currentIndent?.right || previousIndent?.firstLine !== currentIndent?.firstLine || previousIndent?.hanging !== currentIndent?.hanging) return false;
|
|
16
|
+
const previousTabs = previous.attrs.tabs ?? [];
|
|
17
|
+
const currentTabs = current.attrs.tabs ?? [];
|
|
18
|
+
return previousTabs.length === currentTabs.length && previousTabs.every((tab, index) => {
|
|
19
|
+
const currentTab = currentTabs[index];
|
|
20
|
+
if (!currentTab) return false;
|
|
21
|
+
return tab.val === currentTab.val && tab.pos === currentTab.pos && tab.leader === currentTab.leader;
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
//#endregion
|
|
25
|
+
export { continuesNumberedSequence, continuesTabbedParagraphSequence };
|