reamkit 1.17.0 → 1.19.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/esm/core/document-model/types.d.ts +59 -1
- package/dist/esm/core/drawingml/colors.js +13 -6
- package/dist/esm/core/images.d.ts +2 -2
- package/dist/esm/core/images.js +38 -0
- package/dist/esm/core/lzw.d.ts +17 -0
- package/dist/esm/core/lzw.js +83 -0
- package/dist/esm/core/metafile/emf.d.ts +16 -0
- package/dist/esm/core/metafile/emf.js +635 -0
- package/dist/esm/core/metafile/picture.d.ts +106 -0
- package/dist/esm/core/metafile/picture.js +55 -0
- package/dist/esm/core/metafile/wmf.d.ts +9 -0
- package/dist/esm/core/metafile/wmf.js +371 -0
- package/dist/esm/core/po-helpers.d.ts +9 -0
- package/dist/esm/core/po-helpers.js +9 -1
- package/dist/esm/core/tiff.d.ts +20 -0
- package/dist/esm/core/tiff.js +339 -0
- package/dist/esm/core/vector.d.ts +5 -0
- package/dist/esm/excel/print-model.js +3 -1
- package/dist/esm/html/html-writer.js +3 -2
- package/dist/esm/layout/page-doc.d.ts +46 -3
- package/dist/esm/layout/styled-layout.js +649 -108
- package/dist/esm/pdf/styled-page-emitter.js +78 -3
- package/dist/esm/pdf/vector-graphics.d.ts +1 -1
- package/dist/esm/pdf/vector-graphics.js +2 -1
- package/dist/esm/pdf-reader/image-decode.js +7 -75
- package/dist/esm/svg/svg-writer.js +1 -1
- package/dist/esm/word/document-parser.js +3 -0
- package/dist/esm/word/drawing-parser.d.ts +5 -0
- package/dist/esm/word/drawing-parser.js +100 -14
- package/dist/esm/word/paragraph-properties.js +2 -2
- package/dist/esm/word/run-properties.js +2 -2
- package/dist/esm/word/xml-helpers.d.ts +12 -0
- package/dist/esm/word/xml-helpers.js +23 -3
- package/package.json +1 -1
|
@@ -4,6 +4,9 @@ import { createFontMeasure } from "../core/font/measure.js";
|
|
|
4
4
|
import { halfPtToPt, pt } from "../core/ir/units.js";
|
|
5
5
|
import { ResourceStore } from "../core/ir/resources.js";
|
|
6
6
|
import { prepareImage } from "../core/images.js";
|
|
7
|
+
import { PathBuilder, flipTransform } from "../core/vector.js";
|
|
8
|
+
import { isEmf, readEmf } from "../core/metafile/emf.js";
|
|
9
|
+
import { isWmf, readWmf } from "../core/metafile/wmf.js";
|
|
7
10
|
import { segmentLevels } from "../core/bidi/segments.js";
|
|
8
11
|
import { analyzeString, hasBidiCharacters } from "../core/bidi/index.js";
|
|
9
12
|
import { FORCED_BREAK, breakLines } from "../core/line-breaker/knuth-plass.js";
|
|
@@ -15,7 +18,6 @@ import { resolveParagraphProperties, resolveRunProperties } from "../core/style-
|
|
|
15
18
|
import "../core/style-cascade/index.js";
|
|
16
19
|
import { applyNumbering, applyNumberingToHeadersFooters } from "../core/numbering/apply.js";
|
|
17
20
|
import "../core/numbering/index.js";
|
|
18
|
-
import { PathBuilder, flipTransform } from "../core/vector.js";
|
|
19
21
|
import { buildSparkline } from "../core/drawingml/sparkline-geometry.js";
|
|
20
22
|
import { arcPoint, arcToBeziers } from "../core/arc-to-bezier.js";
|
|
21
23
|
import { buildChartScene, legendSeriesName } from "../core/drawingml/chart-geometry.js";
|
|
@@ -55,6 +57,35 @@ var MIN_WRAP_WIDTH = 36;
|
|
|
55
57
|
function isOutOfFlowFloat(f) {
|
|
56
58
|
return f !== void 0 && f.wrap !== "topAndBottom";
|
|
57
59
|
}
|
|
60
|
+
/** The float's left edge, resolved from its horizontal anchor (§20.4.3.3). */
|
|
61
|
+
function floatXIn(frame, f, widthPt) {
|
|
62
|
+
const h = f.posH;
|
|
63
|
+
if (!h) return frame.columnLeft;
|
|
64
|
+
const textRight = frame.marginLeft + frame.contentWidth;
|
|
65
|
+
const pageRight = frame.pageLeft + frame.pageWidth;
|
|
66
|
+
const base = h.relativeFrom === "page" || h.relativeFrom === "leftMargin" ? frame.pageLeft : h.relativeFrom === "column" ? frame.columnLeft : h.relativeFrom === "rightMargin" ? textRight : frame.marginLeft;
|
|
67
|
+
const span = h.relativeFrom === "page" ? frame.pageWidth : h.relativeFrom === "column" ? frame.columnWidth : h.relativeFrom === "leftMargin" ? frame.marginLeft - frame.pageLeft : h.relativeFrom === "rightMargin" ? pageRight - textRight : frame.contentWidth;
|
|
68
|
+
if (h.align === "center") return base + (span - widthPt) / 2;
|
|
69
|
+
if (h.align === "right") return base + span - widthPt;
|
|
70
|
+
return base + (h.offsetPt ?? 0);
|
|
71
|
+
}
|
|
72
|
+
/** The float's TOP, y-up, resolved from its vertical anchor (§20.4.3.4). */
|
|
73
|
+
function floatTopYUpIn(frame, f, heightPt = 0) {
|
|
74
|
+
const v = f.posV;
|
|
75
|
+
if (!v) return frame.anchorY;
|
|
76
|
+
if (v.align !== void 0) {
|
|
77
|
+
const page = v.relativeFrom === "page";
|
|
78
|
+
const topYUp = page ? frame.pageTopYUp : frame.marginTopYUp;
|
|
79
|
+
const bottomYUp = page ? frame.pageBottomYUp : frame.marginBottomYUp;
|
|
80
|
+
if (v.align === "top") return topYUp;
|
|
81
|
+
if (v.align === "bottom") return bottomYUp + heightPt;
|
|
82
|
+
return bottomYUp + (topYUp - bottomYUp + heightPt) / 2;
|
|
83
|
+
}
|
|
84
|
+
if (v.relativeFrom === "page" || v.relativeFrom === "topMargin") return frame.pageTopYUp - (v.offsetPt ?? 0);
|
|
85
|
+
if (v.relativeFrom === "bottomMargin") return frame.marginBottomYUp - (v.offsetPt ?? 0);
|
|
86
|
+
if (v.relativeFrom === "margin") return frame.marginTopYUp - (v.offsetPt ?? 0);
|
|
87
|
+
return frame.anchorY - (v.offsetPt ?? 0);
|
|
88
|
+
}
|
|
58
89
|
var FOOTNOTE_RULE_WIDTH = 144;
|
|
59
90
|
function assignNoteNumbers(body, commentIds) {
|
|
60
91
|
const footnotes = /* @__PURE__ */ new Map();
|
|
@@ -273,8 +304,8 @@ function layoutStyledDocument(body, options) {
|
|
|
273
304
|
const pdfaProfile = options.pdfA ? parsePdfAProfile(options.pdfA) : void 0;
|
|
274
305
|
const tagged = options.tagged === true || options.pdfUA === true || (pdfaProfile?.tagged ?? false);
|
|
275
306
|
const structBuilder = tagged ? new StructTreeBuilder() : void 0;
|
|
276
|
-
const fontResources = collectFontResources(numberedBody, numberedHeadersFooters, options);
|
|
277
307
|
const imageResources = collectImageResources(numberedBody, numberedHeadersFooters, options);
|
|
308
|
+
const fontResources = collectFontResources(numberedBody, numberedHeadersFooters, options, imageResources);
|
|
278
309
|
const sectionCtxs = sectionList.map((s) => buildSectionContext(s, options, numberedHeadersFooters, fontResources, imageResources));
|
|
279
310
|
let sectionIdx = 0;
|
|
280
311
|
const bodyFrames = frameGroups(numberedBody);
|
|
@@ -309,6 +340,7 @@ function layoutStyledDocument(body, options) {
|
|
|
309
340
|
spacingBeforePt: 0
|
|
310
341
|
};
|
|
311
342
|
}
|
|
343
|
+
flowLinkedTextBoxes(blocks);
|
|
312
344
|
const noteBlockCache = /* @__PURE__ */ new Map();
|
|
313
345
|
const notePlan = options.footnotes && noteAssigned.footnotes.size > 0 ? {
|
|
314
346
|
numbers: noteAssigned.footnotes,
|
|
@@ -400,7 +432,7 @@ function layoutStyledDocument(body, options) {
|
|
|
400
432
|
const commentNotes = options.commentAnnotations && options.comments ? buildCommentNotes(noteAssigned.comments, options.comments) : void 0;
|
|
401
433
|
const reflowParagraph = (paragraph, width, widths) => layoutParagraphBlock(paragraph, options, fontResources, imageResources, width, widths);
|
|
402
434
|
return {
|
|
403
|
-
pages: paginateSections(blocks, sectionCtxs, structBuilder, options.language ?? "en-US", notePlan, bookmarks, reflowParagraph, options
|
|
435
|
+
pages: paginateSections(blocks, sectionCtxs, structBuilder, options.language ?? "en-US", notePlan, bookmarks, reflowParagraph, backgroundImageOf(options, imageResources)),
|
|
404
436
|
resources: options.resources ?? new ResourceStore(),
|
|
405
437
|
fontResources,
|
|
406
438
|
imageResources,
|
|
@@ -577,7 +609,7 @@ function balancedColumnHeight(bandHeightPt, colCount, roomPt) {
|
|
|
577
609
|
/** Total laid-out height of a run of blocks, paragraph spacing included. */
|
|
578
610
|
function blocksHeight(blocks) {
|
|
579
611
|
return blocks.reduce((sum, b) => {
|
|
580
|
-
if (b.kind !== "paragraph" &&
|
|
612
|
+
if (b.kind !== "paragraph" && isOutOfFlowFloat(b.float)) return sum;
|
|
581
613
|
return sum + (b.kind === "paragraph" ? b.spacingBeforePt + b.heightPt + b.spacingAfterPt : b.heightPt);
|
|
582
614
|
}, 0);
|
|
583
615
|
}
|
|
@@ -775,8 +807,98 @@ function frameShape(frame, paragraphs, widthPt, exact) {
|
|
|
775
807
|
};
|
|
776
808
|
}
|
|
777
809
|
/** §17.3.1.11 → §20.4.2.3: the frame's anchor, in the terms floats are placed in. */
|
|
810
|
+
/**
|
|
811
|
+
* `wps:linkedTxbx` — carry each chain's overflow from one box to the next.
|
|
812
|
+
* The words all live in the chain's FIRST box; a box shows as many of its lines
|
|
813
|
+
* as its height holds and hands the rest on, so LinkedTextBoxes.docx reads down
|
|
814
|
+
* one column and continues at the top of the other. A box that ends a chain
|
|
815
|
+
* keeps whatever is left, overflowing as it would have anyway.
|
|
816
|
+
*
|
|
817
|
+
* The lines are broken at the first box's width, so a chain of boxes of
|
|
818
|
+
* different widths sets its later columns to the wrong measure; re-breaking
|
|
819
|
+
* needs the source paragraphs split at the carry point, which the laid-out
|
|
820
|
+
* lines no longer know.
|
|
821
|
+
*/
|
|
822
|
+
function flowLinkedTextBoxes(blocks) {
|
|
823
|
+
const chains = /* @__PURE__ */ new Map();
|
|
824
|
+
blocks.forEach((b, i) => {
|
|
825
|
+
if (b.kind !== "shape" || !b.textChain) return;
|
|
826
|
+
const at = chains.get(b.textChain.id) ?? [];
|
|
827
|
+
at.push(i);
|
|
828
|
+
chains.set(b.textChain.id, at);
|
|
829
|
+
});
|
|
830
|
+
for (const indices of chains.values()) {
|
|
831
|
+
if (indices.length < 2) continue;
|
|
832
|
+
indices.sort((a, b) => {
|
|
833
|
+
return (blocks[a].textChain?.seq ?? 0) - (blocks[b].textChain?.seq ?? 0);
|
|
834
|
+
});
|
|
835
|
+
let carried = [];
|
|
836
|
+
indices.forEach((idx, n) => {
|
|
837
|
+
const box = blocks[idx];
|
|
838
|
+
const all = n === 0 ? boxLines(box) : carried;
|
|
839
|
+
const last = n === indices.length - 1;
|
|
840
|
+
const room = box.heightPt - box.insetTopPt - box.insetBottomPt;
|
|
841
|
+
const kept = [];
|
|
842
|
+
let used = 0;
|
|
843
|
+
for (const l of all) {
|
|
844
|
+
const h = computeLineHeight(l.line, l.line.resolved) + l.gapPt;
|
|
845
|
+
if (!last && kept.length > 0 && used + h > room) break;
|
|
846
|
+
kept.push(l);
|
|
847
|
+
used += h;
|
|
848
|
+
}
|
|
849
|
+
carried = all.slice(kept.length);
|
|
850
|
+
blocks[idx] = rebuildBoxLines(box, kept);
|
|
851
|
+
});
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
function boxLines(box) {
|
|
855
|
+
return box.textLines.map((line, i) => ({
|
|
856
|
+
line,
|
|
857
|
+
gapPt: box.textLineGaps?.get(i) ?? 0,
|
|
858
|
+
...box.textCharts?.get(i) ? { chart: box.textCharts.get(i) } : {},
|
|
859
|
+
...box.textShapes?.get(i) ? { shape: box.textShapes.get(i) } : {},
|
|
860
|
+
...box.textTables?.get(i) ? { table: box.textTables.get(i) } : {}
|
|
861
|
+
}));
|
|
862
|
+
}
|
|
863
|
+
function rebuildBoxLines(box, lines) {
|
|
864
|
+
const charts = /* @__PURE__ */ new Map();
|
|
865
|
+
const shapes = /* @__PURE__ */ new Map();
|
|
866
|
+
const tables = /* @__PURE__ */ new Map();
|
|
867
|
+
const gaps = /* @__PURE__ */ new Map();
|
|
868
|
+
let height = 0;
|
|
869
|
+
lines.forEach((l, i) => {
|
|
870
|
+
if (l.chart) charts.set(i, l.chart);
|
|
871
|
+
if (l.shape) shapes.set(i, l.shape);
|
|
872
|
+
if (l.table) tables.set(i, l.table);
|
|
873
|
+
if (l.gapPt > 0) gaps.set(i, l.gapPt);
|
|
874
|
+
height += computeLineHeight(l.line, l.line.resolved) + l.gapPt;
|
|
875
|
+
});
|
|
876
|
+
const { textCharts: _c, textShapes: _s, textTables: _t, textLineGaps: _g, ...rest } = box;
|
|
877
|
+
return {
|
|
878
|
+
...rest,
|
|
879
|
+
textLines: lines.map((l) => l.line),
|
|
880
|
+
...charts.size > 0 ? { textCharts: charts } : {},
|
|
881
|
+
...shapes.size > 0 ? { textShapes: shapes } : {},
|
|
882
|
+
...tables.size > 0 ? { textTables: tables } : {},
|
|
883
|
+
...gaps.size > 0 ? { textLineGaps: gaps } : {},
|
|
884
|
+
textHeightPt: height
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
/**
|
|
888
|
+
* §17.3.1.11 — whether the frame is placed DOWN THE PAGE by itself, rather than
|
|
889
|
+
* where the flow has reached. `w:vAnchor="text"` (the default) measures `w:y`
|
|
890
|
+
* from the paragraph's own position, so such frames stack as paragraphs do —
|
|
891
|
+
* and a float takes no flow space, so lifting them out puts every one of them
|
|
892
|
+
* on the same line: tdf105035_framePrB.docx writes its title and its author
|
|
893
|
+
* line as text-anchored frames and both landed on the cursor, one over the
|
|
894
|
+
* other. Only a frame anchored to the page or the margin, or aligned by
|
|
895
|
+
* keyword, truly stands somewhere of its own.
|
|
896
|
+
*/
|
|
897
|
+
function framePositioned(frame) {
|
|
898
|
+
return frame.yAlign !== void 0 || frame.vAnchor !== void 0 && frame.vAnchor !== "text";
|
|
899
|
+
}
|
|
778
900
|
function frameFloat(frame) {
|
|
779
|
-
const wrap = frame.wrap === "none" ? "none" : frame.wrap === "notBeside" ? "topAndBottom" : "square";
|
|
901
|
+
const wrap = frame.wrap === "none" ? "none" : frame.wrap === "notBeside" ? framePositioned(frame) ? "notBeside" : "topAndBottom" : "square";
|
|
780
902
|
const hRel = frame.hAnchor === "page" ? "page" : frame.hAnchor === "margin" ? "margin" : "column";
|
|
781
903
|
const vRel = frame.vAnchor === "page" ? "page" : frame.vAnchor === "margin" ? "margin" : "paragraph";
|
|
782
904
|
const align = frame.xAlign === "left" || frame.xAlign === "center" || frame.xAlign === "right" ? frame.xAlign : void 0;
|
|
@@ -825,7 +947,10 @@ function laidOutBlocksFor(elements, options, fontResources, contentWidth, imageR
|
|
|
825
947
|
function layoutBodyElement(el, options, fontResources, imageResources, contentWidth, maxHeight, box) {
|
|
826
948
|
if (el.kind === "paragraph") return layoutParagraphBlock(el.paragraph, options, fontResources, imageResources, contentWidth);
|
|
827
949
|
if (el.kind === "table") return layoutTableBlock(el.table, options, fontResources, imageResources, contentWidth);
|
|
828
|
-
if (el.kind === "image")
|
|
950
|
+
if (el.kind === "image") {
|
|
951
|
+
const meta = el.image.resource ? imageResources?.get(el.image.resource)?.metafile : void 0;
|
|
952
|
+
return meta ? layoutMetafileBlock(el.image, meta, options, fontResources, contentWidth, maxHeight, box) : layoutImageBlock(el.image, imageResources, contentWidth, maxHeight, box);
|
|
953
|
+
}
|
|
829
954
|
if (el.kind === "chart") return layoutChartBlock(el.chart, options, fontResources, contentWidth, maxHeight);
|
|
830
955
|
return layoutShapeBlock(el.shape, options, fontResources, imageResources, contentWidth, maxHeight, box);
|
|
831
956
|
}
|
|
@@ -873,6 +998,7 @@ function layoutImageBlock(image, imageResources, contentWidth, maxHeight, box) {
|
|
|
873
998
|
resolvedAlignment,
|
|
874
999
|
resourceName: res?.resourceName ?? "",
|
|
875
1000
|
...image.outline ? { outline: image.outline } : {},
|
|
1001
|
+
...image.wash ? { wash: image.wash } : {},
|
|
876
1002
|
...image.crop ? { crop: image.crop } : {},
|
|
877
1003
|
...image.rotation60k ? { rotationDeg: image.rotation60k / 6e4 } : {},
|
|
878
1004
|
...image.flipH ? { flipH: true } : {},
|
|
@@ -898,7 +1024,12 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
|
|
|
898
1024
|
heightPt = maxHeight;
|
|
899
1025
|
}
|
|
900
1026
|
const fillGradient = shape.fill.kind === "gradient" ? shape.fill.gradient : void 0;
|
|
901
|
-
const
|
|
1027
|
+
const fillImage = shape.fill.kind === "picture" && shape.fill.imageResource !== void 0 ? imageResources?.get(shape.fill.imageResource) : void 0;
|
|
1028
|
+
const fillImageResourceName = fillImage?.resourceName;
|
|
1029
|
+
const fillImageTile = shape.fill.kind === "picture" && shape.fill.tiled === true && fillImage?.prepared ? {
|
|
1030
|
+
widthPt: fillImage.prepared.widthPx * 72 / 96,
|
|
1031
|
+
heightPt: fillImage.prepared.heightPx * 72 / 96
|
|
1032
|
+
} : void 0;
|
|
902
1033
|
const fillColorHex = shape.fill.kind === "solid" ? shape.fill.colorHex : fillGradient ? gradientToSolid(fillGradient) : void 0;
|
|
903
1034
|
const stroke = buildStroke(shape.line);
|
|
904
1035
|
const t = shape.transform;
|
|
@@ -1052,9 +1183,11 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
|
|
|
1052
1183
|
paths,
|
|
1053
1184
|
...markerPaths.length > 0 ? { markerPaths } : {},
|
|
1054
1185
|
...fillImageResourceName ? { fillImageResourceName } : {},
|
|
1186
|
+
...fillImageTile ? { fillImageTile } : {},
|
|
1055
1187
|
...fillImageResourceName && shape.fill.imageCrop ? { fillImageCrop: shape.fill.imageCrop } : {},
|
|
1056
1188
|
...fillColorHex ? { fillColorHex } : {},
|
|
1057
1189
|
...fillGradient ? { fillGradient } : {},
|
|
1190
|
+
...shape.fill.alpha !== void 0 && shape.fill.alpha < 1 ? { fillAlpha: shape.fill.alpha } : {},
|
|
1058
1191
|
...stroke ? { stroke } : {},
|
|
1059
1192
|
...shape.shadow ? { shadow: shape.shadow } : {},
|
|
1060
1193
|
rotation60k: t?.rotation60k ?? 0,
|
|
@@ -1069,6 +1202,7 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
|
|
|
1069
1202
|
...textTables.size > 0 ? { textTables } : {},
|
|
1070
1203
|
...textLineGaps.size > 0 ? { textLineGaps } : {},
|
|
1071
1204
|
textHeightPt,
|
|
1205
|
+
...text?.chain ? { textChain: text.chain } : {},
|
|
1072
1206
|
insetLeftPt,
|
|
1073
1207
|
insetRightPt,
|
|
1074
1208
|
insetTopPt,
|
|
@@ -1079,6 +1213,144 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
|
|
|
1079
1213
|
...shape.float ? { float: shape.float } : {}
|
|
1080
1214
|
};
|
|
1081
1215
|
}
|
|
1216
|
+
/**
|
|
1217
|
+
* MS-EMF / MS-WMF — a metafile picture as the primitives a chart is made of,
|
|
1218
|
+
* scaled from its own logical box into the box the drawing reserves.
|
|
1219
|
+
*
|
|
1220
|
+
* A metafile IS a little drawing program, and what it draws is paths and words:
|
|
1221
|
+
* the same two things a chart's layout carries, placed in the same local y-up
|
|
1222
|
+
* frame. So one is turned into the other rather than growing a block kind, a
|
|
1223
|
+
* pagination arm and an emitter of its own.
|
|
1224
|
+
*/
|
|
1225
|
+
function layoutMetafileBlock(image, pic, options, fontResources, contentWidth, maxHeight, box) {
|
|
1226
|
+
const placed = layoutImageBlock(image, void 0, contentWidth, maxHeight, box);
|
|
1227
|
+
const widthPt = placed.widthPt;
|
|
1228
|
+
const heightPt = placed.heightPt;
|
|
1229
|
+
const layout = rotateDrawing(metafileDrawing(pic, widthPt, heightPt, options, fontResources), (image.rotation60k ?? 0) / 6e4, widthPt / 2, heightPt / 2);
|
|
1230
|
+
const pp = image.paragraphProperties;
|
|
1231
|
+
return {
|
|
1232
|
+
kind: "chart",
|
|
1233
|
+
widthPt,
|
|
1234
|
+
heightPt,
|
|
1235
|
+
layout,
|
|
1236
|
+
resolvedAlignment: pp.alignment ?? "left",
|
|
1237
|
+
spacingBeforePt: pp.spacingBefore ?? 0,
|
|
1238
|
+
spacingAfterPt: pp.spacingAfter ?? 0,
|
|
1239
|
+
figureRole: "Image",
|
|
1240
|
+
...image.altText ? { altText: image.altText } : {},
|
|
1241
|
+
...image.float ? { float: image.float } : {}
|
|
1242
|
+
};
|
|
1243
|
+
}
|
|
1244
|
+
/** The same primitives, turned `deg` CLOCKWISE about a point of the local frame. */
|
|
1245
|
+
function rotateDrawing(layout, deg, cx, cy) {
|
|
1246
|
+
if (!deg) return layout;
|
|
1247
|
+
const a = -deg * Math.PI / 180;
|
|
1248
|
+
const cos = Math.cos(a);
|
|
1249
|
+
const sin = Math.sin(a);
|
|
1250
|
+
const at = (x, y) => ({
|
|
1251
|
+
x: cx + (x - cx) * cos - (y - cy) * sin,
|
|
1252
|
+
y: cy + (x - cx) * sin + (y - cy) * cos
|
|
1253
|
+
});
|
|
1254
|
+
return {
|
|
1255
|
+
shapes: layout.shapes.map((sh) => ({
|
|
1256
|
+
...sh,
|
|
1257
|
+
paths: sh.paths.map((path) => ({
|
|
1258
|
+
...path,
|
|
1259
|
+
segments: path.segments.map((seg) => {
|
|
1260
|
+
if (seg.op === "close") return seg;
|
|
1261
|
+
if (seg.op === "cubic") {
|
|
1262
|
+
const c1 = at(seg.x1, seg.y1);
|
|
1263
|
+
const c2 = at(seg.x2, seg.y2);
|
|
1264
|
+
const e = at(seg.x, seg.y);
|
|
1265
|
+
return {
|
|
1266
|
+
op: "cubic",
|
|
1267
|
+
x1: c1.x,
|
|
1268
|
+
y1: c1.y,
|
|
1269
|
+
x2: c2.x,
|
|
1270
|
+
y2: c2.y,
|
|
1271
|
+
x: e.x,
|
|
1272
|
+
y: e.y
|
|
1273
|
+
};
|
|
1274
|
+
}
|
|
1275
|
+
const p = at(seg.x, seg.y);
|
|
1276
|
+
return {
|
|
1277
|
+
op: seg.op,
|
|
1278
|
+
x: p.x,
|
|
1279
|
+
y: p.y
|
|
1280
|
+
};
|
|
1281
|
+
})
|
|
1282
|
+
}))
|
|
1283
|
+
})),
|
|
1284
|
+
texts: layout.texts.map((t) => {
|
|
1285
|
+
const p = at(t.x, t.y);
|
|
1286
|
+
return {
|
|
1287
|
+
...t,
|
|
1288
|
+
x: p.x,
|
|
1289
|
+
y: p.y,
|
|
1290
|
+
rotationDeg: (t.rotationDeg ?? 0) - deg
|
|
1291
|
+
};
|
|
1292
|
+
})
|
|
1293
|
+
};
|
|
1294
|
+
}
|
|
1295
|
+
/**
|
|
1296
|
+
* A metafile's primitives, scaled from its own logical box into a `widthPt` ×
|
|
1297
|
+
* `heightPt` one and turned y-up about its bottom-left corner — the frame both
|
|
1298
|
+
* a chart block and an inline picture token are drawn in.
|
|
1299
|
+
*/
|
|
1300
|
+
function metafileDrawing(pic, widthPt, heightPt, options, fontResources) {
|
|
1301
|
+
const sx = widthPt / pic.width;
|
|
1302
|
+
const sy = heightPt / pic.height;
|
|
1303
|
+
const mapX = (x) => (x - pic.left) * sx;
|
|
1304
|
+
const mapY = (y) => heightPt - (y - pic.top) * sy;
|
|
1305
|
+
const shapes = [];
|
|
1306
|
+
const texts = [];
|
|
1307
|
+
for (const prim of pic.prims) {
|
|
1308
|
+
if (prim.kind === "path") {
|
|
1309
|
+
shapes.push({
|
|
1310
|
+
paths: prim.paths.map((path) => ({
|
|
1311
|
+
...path,
|
|
1312
|
+
segments: path.segments.map((seg) => seg.op === "close" ? seg : seg.op === "cubic" ? {
|
|
1313
|
+
op: "cubic",
|
|
1314
|
+
x1: mapX(seg.x1),
|
|
1315
|
+
y1: mapY(seg.y1),
|
|
1316
|
+
x2: mapX(seg.x2),
|
|
1317
|
+
y2: mapY(seg.y2),
|
|
1318
|
+
x: mapX(seg.x),
|
|
1319
|
+
y: mapY(seg.y)
|
|
1320
|
+
} : {
|
|
1321
|
+
op: seg.op,
|
|
1322
|
+
x: mapX(seg.x),
|
|
1323
|
+
y: mapY(seg.y)
|
|
1324
|
+
})
|
|
1325
|
+
})),
|
|
1326
|
+
...prim.fillColorHex !== void 0 ? { fillColorHex: prim.fillColorHex } : {},
|
|
1327
|
+
...prim.stroke ? { stroke: {
|
|
1328
|
+
...prim.stroke,
|
|
1329
|
+
widthPt: Math.max(.25, prim.stroke.widthPt * sx)
|
|
1330
|
+
} } : {}
|
|
1331
|
+
});
|
|
1332
|
+
continue;
|
|
1333
|
+
}
|
|
1334
|
+
if (prim.kind !== "text") continue;
|
|
1335
|
+
const { variant } = options.registry.resolveByStyle(prim.bold === true, prim.italic === true);
|
|
1336
|
+
const font = fontResources.get(variant);
|
|
1337
|
+
if (!font) continue;
|
|
1338
|
+
const sizePt = Math.max(1, prim.sizeLu * sy);
|
|
1339
|
+
const line = makeChartLabelLine(prim.text, font, sizePt, prim.colorHex);
|
|
1340
|
+
const shift = prim.alignH === "center" ? -line.contentWidthPt / 2 : prim.alignH === "right" ? -line.contentWidthPt : 0;
|
|
1341
|
+
const baselineY = mapY(prim.y) - (prim.alignBaseline ? 0 : sizePt * .8);
|
|
1342
|
+
texts.push({
|
|
1343
|
+
line,
|
|
1344
|
+
x: mapX(prim.x) + shift,
|
|
1345
|
+
y: baselineY,
|
|
1346
|
+
...prim.escapement ? { rotationDeg: prim.escapement / 10 } : {}
|
|
1347
|
+
});
|
|
1348
|
+
}
|
|
1349
|
+
return {
|
|
1350
|
+
shapes,
|
|
1351
|
+
texts
|
|
1352
|
+
};
|
|
1353
|
+
}
|
|
1082
1354
|
function layoutChartBlock(block, options, fontResources, contentWidth, maxHeight) {
|
|
1083
1355
|
let widthPt = block.width;
|
|
1084
1356
|
let heightPt = block.height;
|
|
@@ -1346,6 +1618,21 @@ function makeChartLabelLine(text, font, sizePt, colorHex) {
|
|
|
1346
1618
|
isLastInParagraph: true
|
|
1347
1619
|
};
|
|
1348
1620
|
}
|
|
1621
|
+
/**
|
|
1622
|
+
* §17.2.1 — the page background's picture, with the size a TILE of it is drawn
|
|
1623
|
+
* at: the picture's own pixels read at 96 dpi, which is the resolution Office
|
|
1624
|
+
* measures a picture in.
|
|
1625
|
+
*/
|
|
1626
|
+
function backgroundImageOf(options, imageResources) {
|
|
1627
|
+
const id = options.pageBackgroundFill?.imageResource;
|
|
1628
|
+
const res = id === void 0 ? void 0 : imageResources.get(id);
|
|
1629
|
+
if (!res?.resourceName || !res.prepared) return void 0;
|
|
1630
|
+
return {
|
|
1631
|
+
name: res.resourceName,
|
|
1632
|
+
widthPt: res.prepared.widthPx * 72 / 96,
|
|
1633
|
+
heightPt: res.prepared.heightPx * 72 / 96
|
|
1634
|
+
};
|
|
1635
|
+
}
|
|
1349
1636
|
function collectImageResources(body, headersFooters, options) {
|
|
1350
1637
|
const out = /* @__PURE__ */ new Map();
|
|
1351
1638
|
if (!options.resources || options.resources.size === 0) return out;
|
|
@@ -1376,6 +1663,15 @@ function collectImageResources(body, headersFooters, options) {
|
|
|
1376
1663
|
for (const resourceId of seen) {
|
|
1377
1664
|
const bytes = options.resources.get(resourceId);
|
|
1378
1665
|
if (!bytes) continue;
|
|
1666
|
+
if (isEmf(bytes) || isWmf(bytes)) {
|
|
1667
|
+
try {
|
|
1668
|
+
out.set(resourceId, {
|
|
1669
|
+
resourceName: "",
|
|
1670
|
+
metafile: isEmf(bytes) ? readEmf(bytes) : readWmf(bytes)
|
|
1671
|
+
});
|
|
1672
|
+
} catch {}
|
|
1673
|
+
continue;
|
|
1674
|
+
}
|
|
1379
1675
|
let prepared;
|
|
1380
1676
|
try {
|
|
1381
1677
|
prepared = prepareImage(bytes, { flattenAlpha });
|
|
@@ -1481,21 +1777,76 @@ function emitShapeText(sh, x, bottomYUp, sink, pageHeight, figId) {
|
|
|
1481
1777
|
textY -= sh.textLineGaps?.get(i) ?? 0;
|
|
1482
1778
|
});
|
|
1483
1779
|
}
|
|
1780
|
+
/** The page a table is being drawn onto, as a cell's floats need to see it. */
|
|
1781
|
+
function cellAnchorCtx(asm) {
|
|
1782
|
+
return {
|
|
1783
|
+
sink: (behind, zOrder) => PageAssembler.zSink(behind ? asm.floatsBehind : asm.floatsFront, zOrder),
|
|
1784
|
+
frame: asm.floatFrame(),
|
|
1785
|
+
exclude: (f, x, topYUp, widthPt, heightPt) => {
|
|
1786
|
+
asm.excludeFloat(f, x, topYUp, widthPt, heightPt);
|
|
1787
|
+
},
|
|
1788
|
+
pageHeight: asm.ctx.pageHeight
|
|
1789
|
+
};
|
|
1790
|
+
}
|
|
1791
|
+
function emitCellFloat(cf, ctx, frame) {
|
|
1792
|
+
const float = cf.block.float;
|
|
1793
|
+
if (!float) return;
|
|
1794
|
+
const x = floatXIn(frame, float, cf.block.widthPt);
|
|
1795
|
+
const topYUp = floatTopYUpIn(frame, float, cf.block.heightPt);
|
|
1796
|
+
const sink = ctx.sink(float.behind === true, float.zOrder);
|
|
1797
|
+
if (cf.block.kind === "shape") emitShapeItems(cf.block, x, topYUp - cf.block.heightPt, sink, ctx.pageHeight, void 0);
|
|
1798
|
+
else {
|
|
1799
|
+
const ins = cf.block.drawInset;
|
|
1800
|
+
sink.push({
|
|
1801
|
+
type: "image",
|
|
1802
|
+
x: pt(x + (ins?.dxPt ?? 0)),
|
|
1803
|
+
y: pt(ctx.pageHeight - (topYUp - (ins?.dyTopPt ?? 0))),
|
|
1804
|
+
width: pt(ins?.widthPt ?? cf.block.widthPt),
|
|
1805
|
+
height: pt(ins?.heightPt ?? cf.block.heightPt),
|
|
1806
|
+
imageResourceName: cf.block.resourceName,
|
|
1807
|
+
...cf.block.crop ? { crop: cf.block.crop } : {},
|
|
1808
|
+
...cf.block.wash ? { wash: cf.block.wash } : {},
|
|
1809
|
+
...cf.block.rotationDeg ? { rotationDeg: cf.block.rotationDeg } : {},
|
|
1810
|
+
...cf.block.flipH ? { flipH: true } : {},
|
|
1811
|
+
...cf.block.flipV ? { flipV: true } : {}
|
|
1812
|
+
});
|
|
1813
|
+
if (cf.block.outline) sink.push(...pictureOutlineItems(cf.block.outline, x + (ins?.dxPt ?? 0), ctx.pageHeight - (topYUp - (ins?.dyTopPt ?? 0)), ins?.widthPt ?? cf.block.widthPt, ins?.heightPt ?? cf.block.heightPt, void 0));
|
|
1814
|
+
}
|
|
1815
|
+
if (float.wrap !== "none") ctx.exclude(float, x, topYUp, cf.block.widthPt, cf.block.heightPt);
|
|
1816
|
+
}
|
|
1484
1817
|
function emitShapeItems(sh, x, bottomYUp, sink, pageHeight, figId) {
|
|
1485
|
-
if (sh.fillImageResourceName)
|
|
1486
|
-
|
|
1487
|
-
x: pt(x),
|
|
1488
|
-
y: pt(pageHeight - (bottomYUp + sh.heightPt)),
|
|
1489
|
-
width: pt(sh.widthPt),
|
|
1490
|
-
height: pt(sh.heightPt),
|
|
1491
|
-
imageResourceName: sh.fillImageResourceName,
|
|
1492
|
-
...sh.fillImageCrop ? { crop: sh.fillImageCrop } : {},
|
|
1493
|
-
clip: {
|
|
1818
|
+
if (sh.fillImageResourceName) {
|
|
1819
|
+
const clip = {
|
|
1494
1820
|
paths: sh.paths,
|
|
1495
1821
|
transform: flipTransform(buildShapeTransform(x, bottomYUp, sh.widthPt, sh.heightPt, sh.rotation60k, sh.flipH, sh.flipV), pageHeight)
|
|
1496
|
-
}
|
|
1497
|
-
|
|
1498
|
-
|
|
1822
|
+
};
|
|
1823
|
+
const fig = figId !== void 0 ? { structId: figId } : {};
|
|
1824
|
+
const tile = sh.fillImageTile;
|
|
1825
|
+
if (tile) for (const item of PageAssembler.tileItems(sh.fillImageResourceName, tile, {
|
|
1826
|
+
x,
|
|
1827
|
+
y: pageHeight - (bottomYUp + sh.heightPt),
|
|
1828
|
+
width: sh.widthPt,
|
|
1829
|
+
height: sh.heightPt
|
|
1830
|
+
})) {
|
|
1831
|
+
if (item.type !== "image") continue;
|
|
1832
|
+
sink.push({
|
|
1833
|
+
...item,
|
|
1834
|
+
clip,
|
|
1835
|
+
...fig
|
|
1836
|
+
});
|
|
1837
|
+
}
|
|
1838
|
+
else sink.push({
|
|
1839
|
+
type: "image",
|
|
1840
|
+
x: pt(x),
|
|
1841
|
+
y: pt(pageHeight - (bottomYUp + sh.heightPt)),
|
|
1842
|
+
width: pt(sh.widthPt),
|
|
1843
|
+
height: pt(sh.heightPt),
|
|
1844
|
+
imageResourceName: sh.fillImageResourceName,
|
|
1845
|
+
...sh.fillImageCrop ? { crop: sh.fillImageCrop } : {},
|
|
1846
|
+
clip,
|
|
1847
|
+
...fig
|
|
1848
|
+
});
|
|
1849
|
+
}
|
|
1499
1850
|
if (sh.markerPaths && sh.stroke) sink.push({
|
|
1500
1851
|
type: "shape",
|
|
1501
1852
|
shape: {
|
|
@@ -1511,6 +1862,7 @@ function emitShapeItems(sh, x, bottomYUp, sink, pageHeight, figId) {
|
|
|
1511
1862
|
paths: sh.paths,
|
|
1512
1863
|
...sh.fillColorHex ? { fillColorHex: sh.fillColorHex } : {},
|
|
1513
1864
|
...sh.fillGradient ? { fillGradient: sh.fillGradient } : {},
|
|
1865
|
+
...sh.fillAlpha !== void 0 ? { fillAlpha: sh.fillAlpha } : {},
|
|
1514
1866
|
...sh.stroke ? { stroke: sh.stroke } : {},
|
|
1515
1867
|
...sh.shadow ? { shadow: sh.shadow } : {},
|
|
1516
1868
|
transform: flipTransform(buildShapeTransform(x, bottomYUp, sh.widthPt, sh.heightPt, sh.rotation60k, sh.flipH, sh.flipV), pageHeight)
|
|
@@ -1593,6 +1945,15 @@ function drawBlocksSequentially(blocks, startX, startY, pageHeight, contentWidth
|
|
|
1593
1945
|
const ordered = blocks.some(isBehind) ? [...blocks.filter(isBehind), ...blocks.filter((b) => !isBehind(b))] : blocks;
|
|
1594
1946
|
for (const block of ordered) {
|
|
1595
1947
|
if (block.kind === "table") {
|
|
1948
|
+
const anchored = isOutOfFlowFloat(block.float) ? bandAnchor(block.float, block.totalWidthPt, contentWidth, startX) : void 0;
|
|
1949
|
+
if (anchored !== void 0 && block.float) {
|
|
1950
|
+
let rowY = bandAnchorTop(block.float, cursorY, pageHeight, block.heightPt, bandMargins);
|
|
1951
|
+
for (const row of block.rows) {
|
|
1952
|
+
emitRowChunk(out, row, startX + anchored, rowY, pageHeight, block.colCount);
|
|
1953
|
+
rowY -= row.heightPt;
|
|
1954
|
+
}
|
|
1955
|
+
continue;
|
|
1956
|
+
}
|
|
1596
1957
|
const tableX = startX + block.xOffsetPt;
|
|
1597
1958
|
for (const row of block.rows) {
|
|
1598
1959
|
emitRowChunk(out, row, tableX, cursorY, pageHeight, block.colCount);
|
|
@@ -1620,6 +1981,7 @@ function drawBlocksSequentially(blocks, startX, startY, pageHeight, contentWidth
|
|
|
1620
1981
|
height: pt(block.heightPt),
|
|
1621
1982
|
imageResourceName: block.resourceName,
|
|
1622
1983
|
...block.crop ? { crop: block.crop } : {},
|
|
1984
|
+
...block.wash ? { wash: block.wash } : {},
|
|
1623
1985
|
...block.rotationDeg ? { rotationDeg: block.rotationDeg } : {},
|
|
1624
1986
|
...block.flipH ? { flipH: true } : {},
|
|
1625
1987
|
...block.flipV ? { flipV: true } : {}
|
|
@@ -1739,7 +2101,7 @@ function runFontKeyAndParsed(options, ascii, bold, italic) {
|
|
|
1739
2101
|
function lookupFont(resources, fontKey) {
|
|
1740
2102
|
return resources.get(fontKey) ?? resources.values().next().value;
|
|
1741
2103
|
}
|
|
1742
|
-
function collectFontResources(body, headersFooters, options) {
|
|
2104
|
+
function collectFontResources(body, headersFooters, options, imageResources) {
|
|
1743
2105
|
const used = /* @__PURE__ */ new Map();
|
|
1744
2106
|
const addRun = (run, para) => {
|
|
1745
2107
|
const resolved = resolveRunProperties(run.properties, para.properties, options.styles);
|
|
@@ -1774,30 +2136,48 @@ function collectFontResources(body, headersFooters, options) {
|
|
|
1774
2136
|
for (const ch of seg.text) bucket.gids.add(parsed.glyphForCodepoint(ch.codePointAt(0)));
|
|
1775
2137
|
}
|
|
1776
2138
|
};
|
|
1777
|
-
const
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
if (
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
continue;
|
|
1790
|
-
}
|
|
1791
|
-
if (run.footnoteRef !== void 0 || run.endnoteRef !== void 0 || run.noteNumber) {
|
|
1792
|
-
addRun({
|
|
1793
|
-
text: `${run.text}0123456789 `,
|
|
1794
|
-
properties: run.properties
|
|
1795
|
-
}, el.paragraph);
|
|
1796
|
-
continue;
|
|
2139
|
+
const addMetafile = (resource) => {
|
|
2140
|
+
const pic = resource ? imageResources.get(resource)?.metafile : void 0;
|
|
2141
|
+
for (const prim of pic?.prims ?? []) {
|
|
2142
|
+
if (prim.kind !== "text") continue;
|
|
2143
|
+
const reg = options.registry.resolveByStyle(prim.bold === true, prim.italic === true);
|
|
2144
|
+
let bucket = used.get(reg.variant);
|
|
2145
|
+
if (!bucket) {
|
|
2146
|
+
bucket = {
|
|
2147
|
+
parsed: reg.parsed,
|
|
2148
|
+
gids: /* @__PURE__ */ new Set()
|
|
2149
|
+
};
|
|
2150
|
+
used.set(reg.variant, bucket);
|
|
1797
2151
|
}
|
|
1798
|
-
|
|
2152
|
+
for (const ch of prim.text) bucket.gids.add(reg.parsed.glyphForCodepoint(ch.codePointAt(0)));
|
|
1799
2153
|
}
|
|
1800
|
-
|
|
2154
|
+
};
|
|
2155
|
+
const visit = (elements) => {
|
|
2156
|
+
for (const el of elements) if (el.kind === "paragraph") {
|
|
2157
|
+
for (const run of el.paragraph.runs) addMetafile(run.inlineImage?.resource);
|
|
2158
|
+
for (const run of displayCapsRuns(el.paragraph, options)) {
|
|
2159
|
+
if (run.math) {
|
|
2160
|
+
addMath(run.math);
|
|
2161
|
+
continue;
|
|
2162
|
+
}
|
|
2163
|
+
if (run.inlineImage && !run.text) continue;
|
|
2164
|
+
if (run.field !== void 0) {
|
|
2165
|
+
addRun({
|
|
2166
|
+
text: `${run.text}0123456789`,
|
|
2167
|
+
properties: run.properties
|
|
2168
|
+
}, el.paragraph);
|
|
2169
|
+
continue;
|
|
2170
|
+
}
|
|
2171
|
+
if (run.footnoteRef !== void 0 || run.endnoteRef !== void 0 || run.noteNumber) {
|
|
2172
|
+
addRun({
|
|
2173
|
+
text: `${run.text}0123456789 `,
|
|
2174
|
+
properties: run.properties
|
|
2175
|
+
}, el.paragraph);
|
|
2176
|
+
continue;
|
|
2177
|
+
}
|
|
2178
|
+
addRun(run, el.paragraph);
|
|
2179
|
+
}
|
|
2180
|
+
} else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) {
|
|
1801
2181
|
if (cell.properties.hashOnOverflow === true) for (const inner of cell.content) {
|
|
1802
2182
|
if (inner.kind !== "paragraph") continue;
|
|
1803
2183
|
for (const run of inner.paragraph.runs) addRun({
|
|
@@ -1813,7 +2193,8 @@ function collectFontResources(body, headersFooters, options) {
|
|
|
1813
2193
|
for (const child of sh.children ?? []) shapeText(child.shape);
|
|
1814
2194
|
};
|
|
1815
2195
|
shapeText(el.shape);
|
|
1816
|
-
} else if (el.kind === "
|
|
2196
|
+
} else if (el.kind === "image") addMetafile(el.image.resource);
|
|
2197
|
+
else {
|
|
1817
2198
|
const chart = options.charts?.get(el.chart.chartRelId);
|
|
1818
2199
|
if (chart) {
|
|
1819
2200
|
const reg = options.registry.resolveByStyle(false, false);
|
|
@@ -2104,8 +2485,10 @@ function tokenizeParagraph(paragraph, options, fontResources, imageResources, co
|
|
|
2104
2485
|
imageWidthPt: widthPt,
|
|
2105
2486
|
imageHeightPt: heightPt,
|
|
2106
2487
|
imageResourceName: res?.resourceName ?? "",
|
|
2488
|
+
...res?.metafile ? { imageMetafile: metafileDrawing(res.metafile, drawBox?.widthPt ?? widthPt, drawBox?.heightPt ?? heightPt, options, fontResources) } : {},
|
|
2107
2489
|
...run.inlineImage.outline ? { imageOutline: run.inlineImage.outline } : {},
|
|
2108
2490
|
...run.inlineImage.shadow ? { imageShadow: run.inlineImage.shadow } : {},
|
|
2491
|
+
...run.inlineImage.wash ? { imageWash: run.inlineImage.wash } : {},
|
|
2109
2492
|
...run.inlineImage.crop ? { imageCrop: run.inlineImage.crop } : {},
|
|
2110
2493
|
...run.inlineImage.rotation60k ? { imageRotationDeg: run.inlineImage.rotation60k / 6e4 } : {},
|
|
2111
2494
|
...run.inlineImage.flipH ? { imageFlipH: true } : {},
|
|
@@ -2189,6 +2572,8 @@ function tokenizePlansLtr(plans) {
|
|
|
2189
2572
|
heightPt: plan.imageHeightPt,
|
|
2190
2573
|
...plan.imageOutline ? { outline: plan.imageOutline } : {},
|
|
2191
2574
|
...plan.imageShadow ? { shadow: plan.imageShadow } : {},
|
|
2575
|
+
...plan.imageWash ? { wash: plan.imageWash } : {},
|
|
2576
|
+
...plan.imageMetafile ? { metafile: plan.imageMetafile } : {},
|
|
2192
2577
|
...plan.imageCrop ? { crop: plan.imageCrop } : {},
|
|
2193
2578
|
...plan.imageRotationDeg ? { rotationDeg: plan.imageRotationDeg } : {},
|
|
2194
2579
|
...plan.imageFlipH ? { flipH: true } : {},
|
|
@@ -2260,6 +2645,8 @@ function tokenizePlansBidi(plans, realLevels) {
|
|
|
2260
2645
|
heightPt: plan.imageHeightPt,
|
|
2261
2646
|
...plan.imageOutline ? { outline: plan.imageOutline } : {},
|
|
2262
2647
|
...plan.imageShadow ? { shadow: plan.imageShadow } : {},
|
|
2648
|
+
...plan.imageWash ? { wash: plan.imageWash } : {},
|
|
2649
|
+
...plan.imageMetafile ? { metafile: plan.imageMetafile } : {},
|
|
2263
2650
|
...plan.imageCrop ? { crop: plan.imageCrop } : {},
|
|
2264
2651
|
...plan.imageRotationDeg ? { rotationDeg: plan.imageRotationDeg } : {},
|
|
2265
2652
|
...plan.imageFlipH ? { flipH: true } : {},
|
|
@@ -2872,9 +3259,67 @@ function alignmentOffset(alignment, lineWidth, available) {
|
|
|
2872
3259
|
if (alignment === "center") return Math.max(0, (available - lineWidth) / 2);
|
|
2873
3260
|
return 0;
|
|
2874
3261
|
}
|
|
3262
|
+
/**
|
|
3263
|
+
* §17.4.85 `w:vMerge` — a vertical merge is ONE cell, so its continuation rows
|
|
3264
|
+
* are painted with the START cell's shading. Word writes the fill on the cell
|
|
3265
|
+
* that opens the merge and nothing on the rest; painting only what is written,
|
|
3266
|
+
* tdf123104.docx's three-row banner came out a staircase of yellow with white
|
|
3267
|
+
* notches where every merged cell continued.
|
|
3268
|
+
*/
|
|
3269
|
+
function withMergedShading(table, roles) {
|
|
3270
|
+
const columnOf = table.rows.map((row) => {
|
|
3271
|
+
const at = [];
|
|
3272
|
+
let col = row.properties.gridBefore ?? 0;
|
|
3273
|
+
for (const cell of row.cells) {
|
|
3274
|
+
at.push(col);
|
|
3275
|
+
col += Math.max(1, cell.properties.colSpan ?? 1);
|
|
3276
|
+
}
|
|
3277
|
+
return at;
|
|
3278
|
+
});
|
|
3279
|
+
let changed = false;
|
|
3280
|
+
const rows = table.rows.map((row, r) => {
|
|
3281
|
+
const cells = row.cells.map((cell, c) => {
|
|
3282
|
+
const role = roles[r]?.[c] ?? "standalone";
|
|
3283
|
+
if (role !== "middle" && role !== "end") return cell;
|
|
3284
|
+
if (cell.properties.shading?.colorHex) return cell;
|
|
3285
|
+
const col = columnOf[r]?.[c];
|
|
3286
|
+
if (col === void 0) return cell;
|
|
3287
|
+
for (let up = r - 1; up >= 0; up--) {
|
|
3288
|
+
const idx = columnOf[up]?.indexOf(col) ?? -1;
|
|
3289
|
+
if (idx < 0) break;
|
|
3290
|
+
const above = table.rows[up]?.cells[idx];
|
|
3291
|
+
const aboveRole = roles[up]?.[idx] ?? "standalone";
|
|
3292
|
+
if (!above) break;
|
|
3293
|
+
if (aboveRole === "start") {
|
|
3294
|
+
const shading = above.properties.shading;
|
|
3295
|
+
if (!shading?.colorHex) break;
|
|
3296
|
+
changed = true;
|
|
3297
|
+
return {
|
|
3298
|
+
...cell,
|
|
3299
|
+
properties: {
|
|
3300
|
+
...cell.properties,
|
|
3301
|
+
shading
|
|
3302
|
+
}
|
|
3303
|
+
};
|
|
3304
|
+
}
|
|
3305
|
+
if (aboveRole !== "middle") break;
|
|
3306
|
+
}
|
|
3307
|
+
return cell;
|
|
3308
|
+
});
|
|
3309
|
+
return {
|
|
3310
|
+
...row,
|
|
3311
|
+
cells
|
|
3312
|
+
};
|
|
3313
|
+
});
|
|
3314
|
+
return changed ? {
|
|
3315
|
+
...table,
|
|
3316
|
+
rows
|
|
3317
|
+
} : table;
|
|
3318
|
+
}
|
|
2875
3319
|
function layoutTableBlock(table, options, fontResources, imageResources, contentWidth) {
|
|
2876
3320
|
const columnWidthsPt = computeColumnWidths(table, options, fontResources, contentWidth);
|
|
2877
3321
|
const mergeRoles = table.rows.map((r) => r.cells.map((c) => c.properties.merge ?? "standalone"));
|
|
3322
|
+
table = withMergedShading(table, mergeRoles);
|
|
2878
3323
|
const rows = [];
|
|
2879
3324
|
const colCount = columnWidthsPt.length;
|
|
2880
3325
|
let aboveBordersByCol = [];
|
|
@@ -3158,7 +3603,8 @@ function layoutTableRow(row, rowIdx, rowCount, colCount, tableProps, columnWidth
|
|
|
3158
3603
|
cells: cells.map((c) => clipCellToHeight(c, heightPt)),
|
|
3159
3604
|
columnXOffsets,
|
|
3160
3605
|
rowIdx,
|
|
3161
|
-
rowCount
|
|
3606
|
+
rowCount,
|
|
3607
|
+
...row.properties.cantSplit ? { cantSplit: true } : {}
|
|
3162
3608
|
};
|
|
3163
3609
|
} else if (row.properties.height && row.properties.heightRule === "atLeast") heightPt = Math.max(heightPt, row.properties.height);
|
|
3164
3610
|
return {
|
|
@@ -3166,7 +3612,8 @@ function layoutTableRow(row, rowIdx, rowCount, colCount, tableProps, columnWidth
|
|
|
3166
3612
|
cells,
|
|
3167
3613
|
columnXOffsets,
|
|
3168
3614
|
rowIdx,
|
|
3169
|
-
rowCount
|
|
3615
|
+
rowCount,
|
|
3616
|
+
...row.properties.cantSplit ? { cantSplit: true } : {}
|
|
3170
3617
|
};
|
|
3171
3618
|
}
|
|
3172
3619
|
/**
|
|
@@ -3212,6 +3659,7 @@ function imageRun(image) {
|
|
|
3212
3659
|
inlineImage: {
|
|
3213
3660
|
...image.resource ? { resource: image.resource } : {},
|
|
3214
3661
|
...image.outline ? { outline: image.outline } : {},
|
|
3662
|
+
...image.wash ? { wash: image.wash } : {},
|
|
3215
3663
|
width: image.width,
|
|
3216
3664
|
height: image.height,
|
|
3217
3665
|
...image.crop ? { crop: image.crop } : {},
|
|
@@ -3235,6 +3683,7 @@ function layoutTableCell(cell, tableProps, widthPt, options, fontResources, imag
|
|
|
3235
3683
|
const lines = [];
|
|
3236
3684
|
const nestedTables = [];
|
|
3237
3685
|
const cellShapes = [];
|
|
3686
|
+
const cellFloats = [];
|
|
3238
3687
|
const lineGaps = /* @__PURE__ */ new Map();
|
|
3239
3688
|
let contentHeightPt = 0;
|
|
3240
3689
|
let clipped = false;
|
|
@@ -3288,7 +3737,14 @@ function layoutTableCell(cell, tableProps, widthPt, options, fontResources, imag
|
|
|
3288
3737
|
});
|
|
3289
3738
|
contentHeightPt += block.heightPt;
|
|
3290
3739
|
pendingGapPt = block.spacingAfterPt;
|
|
3291
|
-
}
|
|
3740
|
+
} else if (el.kind === "image" && isOutOfFlowFloat(el.image.float)) cellFloats.push({
|
|
3741
|
+
block: layoutImageBlock(el.image, imageResources, innerWidth),
|
|
3742
|
+
afterLine: lines.length
|
|
3743
|
+
});
|
|
3744
|
+
else if (el.kind === "shape" && isOutOfFlowFloat(el.shape.float)) cellFloats.push({
|
|
3745
|
+
block: layoutShapeBlock(el.shape, options, fontResources, imageResources, innerWidth),
|
|
3746
|
+
afterLine: lines.length
|
|
3747
|
+
});
|
|
3292
3748
|
contentHeightPt += pendingGapPt;
|
|
3293
3749
|
}
|
|
3294
3750
|
const colEnd = colStart + colSpan - 1;
|
|
@@ -3309,6 +3765,7 @@ function layoutTableCell(cell, tableProps, widthPt, options, fontResources, imag
|
|
|
3309
3765
|
...lineGaps.size > 0 ? { lineGaps } : {},
|
|
3310
3766
|
...nestedTables.length > 0 ? { nestedTables } : {},
|
|
3311
3767
|
...cellShapes.length > 0 ? { shapes: cellShapes } : {},
|
|
3768
|
+
...cellFloats.length > 0 ? { floats: cellFloats } : {},
|
|
3312
3769
|
contentHeightPt,
|
|
3313
3770
|
totalHeightPt,
|
|
3314
3771
|
...cell.properties.verticalAlign ? { verticalAlign: cell.properties.verticalAlign } : {},
|
|
@@ -3441,12 +3898,12 @@ var PageAssembler = class PageAssembler {
|
|
|
3441
3898
|
* `undefined` when there are none.
|
|
3442
3899
|
* @param bookmarkPositions Out-param: bookmark name → its GoTo destination.
|
|
3443
3900
|
*/
|
|
3444
|
-
constructor(sectionCtxs, builder, notes, bookmarkPositions,
|
|
3901
|
+
constructor(sectionCtxs, builder, notes, bookmarkPositions, backgroundImage) {
|
|
3445
3902
|
this.sectionCtxs = sectionCtxs;
|
|
3446
3903
|
this.builder = builder;
|
|
3447
3904
|
this.notes = notes;
|
|
3448
3905
|
this.bookmarkPositions = bookmarkPositions;
|
|
3449
|
-
this.
|
|
3906
|
+
this.backgroundImage = backgroundImage;
|
|
3450
3907
|
this.ctx = sectionCtxs[0];
|
|
3451
3908
|
this.pageStartCtx = this.ctx;
|
|
3452
3909
|
this.pageNumber = this.ctx.properties.pageNumberStart ?? 1;
|
|
@@ -3488,6 +3945,12 @@ var PageAssembler = class PageAssembler {
|
|
|
3488
3945
|
/** The current column's width (the section content width when single-column). */
|
|
3489
3946
|
colWidth = () => this.ctx.columns?.[this.colIdx]?.widthPt ?? this.ctx.contentWidth;
|
|
3490
3947
|
/**
|
|
3948
|
+
* The measure the section's blocks were BROKEN at — the first column's, since
|
|
3949
|
+
* that is all the block pass can know. A column of another width has to
|
|
3950
|
+
* re-break what lands in it.
|
|
3951
|
+
*/
|
|
3952
|
+
builtWidth = () => this.ctx.columns?.[0]?.widthPt ?? this.ctx.contentWidth;
|
|
3953
|
+
/**
|
|
3491
3954
|
* Whether the current column is already in use.
|
|
3492
3955
|
*
|
|
3493
3956
|
* This gates every overflow break, so that a block too tall for an empty page
|
|
@@ -3639,44 +4102,36 @@ var PageAssembler = class PageAssembler {
|
|
|
3639
4102
|
*/
|
|
3640
4103
|
excludeFloat = (f, x, topYUp, w, h) => {
|
|
3641
4104
|
const d = f.wrapDist;
|
|
4105
|
+
const wide = f.wrap === "notBeside";
|
|
3642
4106
|
this.exclusions.push({
|
|
3643
|
-
x0: x - (d?.leftPt ?? 0),
|
|
3644
|
-
x1: x + w + (d?.rightPt ?? 0),
|
|
4107
|
+
x0: wide ? this.colLeft() : x - (d?.leftPt ?? 0),
|
|
4108
|
+
x1: wide ? this.colLeft() + this.colWidth() : x + w + (d?.rightPt ?? 0),
|
|
3645
4109
|
topYUp: topYUp + (d?.topPt ?? 0),
|
|
3646
|
-
bottomYUp: topYUp - h - (d?.bottomPt ?? 0)
|
|
4110
|
+
bottomYUp: topYUp - h - (d?.bottomPt ?? 0),
|
|
4111
|
+
...f.wrapSide === "bothSides" ? { wrapBothSides: true } : {}
|
|
3647
4112
|
});
|
|
3648
4113
|
};
|
|
4114
|
+
/** The frame a float anchored in the body flow is resolved against. */
|
|
4115
|
+
floatFrame = () => ({
|
|
4116
|
+
pageLeft: 0,
|
|
4117
|
+
pageWidth: this.ctx.pageWidth,
|
|
4118
|
+
pageTopYUp: this.ctx.pageHeight,
|
|
4119
|
+
pageBottomYUp: 0,
|
|
4120
|
+
marginLeft: this.ctx.marginLeft,
|
|
4121
|
+
contentWidth: this.ctx.contentWidth,
|
|
4122
|
+
marginTopYUp: this.ctx.pageHeight - this.ctx.marginTop,
|
|
4123
|
+
marginBottomYUp: this.ctx.marginBottom,
|
|
4124
|
+
columnLeft: this.colLeft(),
|
|
4125
|
+
columnWidth: this.colWidth(),
|
|
4126
|
+
anchorY: this.cursorY
|
|
4127
|
+
});
|
|
3649
4128
|
/** The float's left edge on the page, resolved from its horizontal anchor. */
|
|
3650
|
-
floatX = (f, widthPt) =>
|
|
3651
|
-
const h = f.posH;
|
|
3652
|
-
if (!h) return this.colLeft();
|
|
3653
|
-
const textRight = this.ctx.marginLeft + this.ctx.contentWidth;
|
|
3654
|
-
const base = h.relativeFrom === "page" || h.relativeFrom === "leftMargin" ? 0 : h.relativeFrom === "column" ? this.colLeft() : h.relativeFrom === "rightMargin" ? textRight : this.ctx.marginLeft;
|
|
3655
|
-
const span = h.relativeFrom === "page" ? this.ctx.pageWidth : h.relativeFrom === "column" ? this.colWidth() : h.relativeFrom === "leftMargin" ? this.ctx.marginLeft : h.relativeFrom === "rightMargin" ? this.ctx.pageWidth - textRight : this.ctx.contentWidth;
|
|
3656
|
-
if (h.align === "center") return base + (span - widthPt) / 2;
|
|
3657
|
-
if (h.align === "right") return base + span - widthPt;
|
|
3658
|
-
return base + (h.offsetPt ?? 0);
|
|
3659
|
-
};
|
|
4129
|
+
floatX = (f, widthPt) => floatXIn(this.floatFrame(), f, widthPt);
|
|
3660
4130
|
/**
|
|
3661
4131
|
* The drawing's TOP in the y-up cursor frame. Paragraph/line-relative offsets
|
|
3662
4132
|
* hang off the anchoring paragraph's current position.
|
|
3663
4133
|
*/
|
|
3664
|
-
floatTopYUp = (f, heightPt = 0) =>
|
|
3665
|
-
const v = f.posV;
|
|
3666
|
-
if (!v) return this.cursorY;
|
|
3667
|
-
if (v.align !== void 0) {
|
|
3668
|
-
const page = v.relativeFrom === "page";
|
|
3669
|
-
const topYUp = page ? this.ctx.pageHeight : this.ctx.pageHeight - this.ctx.marginTop;
|
|
3670
|
-
const bottomYUp = page ? 0 : this.ctx.marginBottom;
|
|
3671
|
-
if (v.align === "top") return topYUp;
|
|
3672
|
-
if (v.align === "bottom") return bottomYUp + heightPt;
|
|
3673
|
-
return bottomYUp + (topYUp - bottomYUp + heightPt) / 2;
|
|
3674
|
-
}
|
|
3675
|
-
if (v.relativeFrom === "page" || v.relativeFrom === "topMargin") return this.ctx.pageHeight - (v.offsetPt ?? 0);
|
|
3676
|
-
if (v.relativeFrom === "bottomMargin") return this.ctx.marginBottom - (v.offsetPt ?? 0);
|
|
3677
|
-
if (v.relativeFrom === "margin") return this.ctx.pageHeight - this.ctx.marginTop - (v.offsetPt ?? 0);
|
|
3678
|
-
return this.cursorY - (v.offsetPt ?? 0);
|
|
3679
|
-
};
|
|
4134
|
+
floatTopYUp = (f, heightPt = 0) => floatTopYUpIn(this.floatFrame(), f, heightPt);
|
|
3680
4135
|
/**
|
|
3681
4136
|
* Tagged PDF: the stack of open list levels (for L/LI/LBody nesting). Cleared
|
|
3682
4137
|
* whenever a non-list-item block interrupts the run of list paragraphs.
|
|
@@ -3739,6 +4194,14 @@ var PageAssembler = class PageAssembler {
|
|
|
3739
4194
|
xOffset: 0,
|
|
3740
4195
|
blockedUntilYUp: e.bottomYUp
|
|
3741
4196
|
};
|
|
4197
|
+
if (leftRoom >= MIN_WRAP_WIDTH && rightRoom >= MIN_WRAP_WIDTH && e.wrapBothSides) return {
|
|
4198
|
+
width: leftRoom,
|
|
4199
|
+
xOffset: 0,
|
|
4200
|
+
pair: {
|
|
4201
|
+
width: rightRoom,
|
|
4202
|
+
xOffset: full - rightRoom
|
|
4203
|
+
}
|
|
4204
|
+
};
|
|
3742
4205
|
if (rightRoom >= leftRoom) return {
|
|
3743
4206
|
width: rightRoom,
|
|
3744
4207
|
xOffset: full - rightRoom
|
|
@@ -3770,6 +4233,7 @@ var PageAssembler = class PageAssembler {
|
|
|
3770
4233
|
continue;
|
|
3771
4234
|
}
|
|
3772
4235
|
widths.push(g.width);
|
|
4236
|
+
if (g.pair) widths.push(g.pair.width);
|
|
3773
4237
|
if (g.width < this.colWidth()) narrowed = true;
|
|
3774
4238
|
else if (narrowed) break;
|
|
3775
4239
|
y -= h0;
|
|
@@ -3947,19 +4411,49 @@ var PageAssembler = class PageAssembler {
|
|
|
3947
4411
|
*
|
|
3948
4412
|
* @returns The background items for this page (at most one).
|
|
3949
4413
|
*/
|
|
4414
|
+
/**
|
|
4415
|
+
* §14.1.2.5 `@type="tile"` — the picture repeated over a box at its own size,
|
|
4416
|
+
* as image items. A tile is a texture, so its natural size is what makes it
|
|
4417
|
+
* one: stretched over the page it is a blur, and its 128 pixels are 96 points
|
|
4418
|
+
* at the resolution Office measures a picture in.
|
|
4419
|
+
*/
|
|
4420
|
+
static tileItems = (resourceName, natural, box) => {
|
|
4421
|
+
const tw = Math.max(1, natural.widthPt);
|
|
4422
|
+
const th = Math.max(1, natural.heightPt);
|
|
4423
|
+
const cols = Math.min(200, Math.ceil(box.width / tw));
|
|
4424
|
+
const rows = Math.min(200, Math.ceil(box.height / th));
|
|
4425
|
+
const out = [];
|
|
4426
|
+
for (let r = 0; r < rows; r++) for (let c = 0; c < cols; c++) out.push({
|
|
4427
|
+
type: "image",
|
|
4428
|
+
x: pt(box.x + c * tw),
|
|
4429
|
+
y: pt(box.y + r * th),
|
|
4430
|
+
width: pt(tw),
|
|
4431
|
+
height: pt(th),
|
|
4432
|
+
imageResourceName: resourceName
|
|
4433
|
+
});
|
|
4434
|
+
return out;
|
|
4435
|
+
};
|
|
3950
4436
|
pageBackgroundItems = () => {
|
|
3951
4437
|
const options = this.ctx.options;
|
|
3952
4438
|
const w = this.ctx.pageWidth;
|
|
3953
4439
|
const h = this.ctx.pageHeight;
|
|
3954
4440
|
const fill = options?.pageBackgroundFill;
|
|
3955
|
-
if (fill?.kind === "picture" && this.
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
4441
|
+
if (fill?.kind === "picture" && this.backgroundImage !== void 0) {
|
|
4442
|
+
const bg = this.backgroundImage;
|
|
4443
|
+
return fill.tiled ? PageAssembler.tileItems(bg.name, bg, {
|
|
4444
|
+
x: 0,
|
|
4445
|
+
y: 0,
|
|
4446
|
+
width: w,
|
|
4447
|
+
height: h
|
|
4448
|
+
}) : [{
|
|
4449
|
+
type: "image",
|
|
4450
|
+
x: pt(0),
|
|
4451
|
+
y: pt(0),
|
|
4452
|
+
width: pt(w),
|
|
4453
|
+
height: pt(h),
|
|
4454
|
+
imageResourceName: bg.name
|
|
4455
|
+
}];
|
|
4456
|
+
}
|
|
3963
4457
|
if (fill?.kind === "gradient" && fill.gradient) return [{
|
|
3964
4458
|
type: "shape",
|
|
3965
4459
|
shape: {
|
|
@@ -4043,9 +4537,9 @@ var PageAssembler = class PageAssembler {
|
|
|
4043
4537
|
this.balanceHeightPt = 0;
|
|
4044
4538
|
};
|
|
4045
4539
|
};
|
|
4046
|
-
function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", notes, bookmarkPositions, reflowParagraph,
|
|
4540
|
+
function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", notes, bookmarkPositions, reflowParagraph, backgroundImage) {
|
|
4047
4541
|
if (sectionCtxs.length === 0) return [];
|
|
4048
|
-
const asm = new PageAssembler(sectionCtxs, builder, notes, bookmarkPositions,
|
|
4542
|
+
const asm = new PageAssembler(sectionCtxs, builder, notes, bookmarkPositions, backgroundImage);
|
|
4049
4543
|
const balanceIfEndsContinuous = (fromBlock) => {
|
|
4050
4544
|
const here = asm.ctx;
|
|
4051
4545
|
const next = sectionCtxs[asm.secIdx + 1];
|
|
@@ -4083,9 +4577,11 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
|
|
|
4083
4577
|
if ((block.resolved.pageBreakBefore || block.pageBreakBefore) && asm.pageHasContent()) asm.flushPage();
|
|
4084
4578
|
asm.cursorY -= block.spacingBeforePt;
|
|
4085
4579
|
let pb = block;
|
|
4580
|
+
const colW = asm.colWidth();
|
|
4581
|
+
if (reflowParagraph && pb.source && Math.abs(colW - asm.builtWidth()) > .5) pb = reflowParagraph(pb.source, colW);
|
|
4086
4582
|
if (reflowParagraph && pb.source && asm.exclusions.length > 0) {
|
|
4087
|
-
const widths = asm.lineWidthsFor(
|
|
4088
|
-
if (widths) pb = reflowParagraph(pb.source,
|
|
4583
|
+
const widths = asm.lineWidthsFor(pb, asm.cursorY);
|
|
4584
|
+
if (widths) pb = reflowParagraph(pb.source, colW, widths);
|
|
4089
4585
|
}
|
|
4090
4586
|
const borderTopY = asm.cursorY;
|
|
4091
4587
|
const borderPage = asm.pages.length;
|
|
@@ -4103,6 +4599,7 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
|
|
|
4103
4599
|
if (lang && lang !== defaultLang) builder.node(structId).lang = lang;
|
|
4104
4600
|
}
|
|
4105
4601
|
let firstLineOfBlock = true;
|
|
4602
|
+
let pairPending;
|
|
4106
4603
|
for (const [lineIdx, line] of pb.lines.entries()) {
|
|
4107
4604
|
if (pb.columnBreakLines?.has(lineIdx) && asm.colHasContent()) asm.advanceColumn();
|
|
4108
4605
|
const h = computeLineHeight(line, pb.resolved);
|
|
@@ -4132,16 +4629,22 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
|
|
|
4132
4629
|
});
|
|
4133
4630
|
}
|
|
4134
4631
|
}
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
asm.
|
|
4632
|
+
const pairedNow = pairPending;
|
|
4633
|
+
pairPending = void 0;
|
|
4634
|
+
if (pairedNow === void 0) {
|
|
4635
|
+
for (let guard = 0; guard < 8 && asm.exclusions.length > 0; guard++) {
|
|
4636
|
+
const blocked = asm.lineGeometryAt(asm.cursorY, h).blockedUntilYUp;
|
|
4637
|
+
if (blocked === void 0 || blocked >= asm.cursorY) break;
|
|
4638
|
+
asm.cursorY = blocked;
|
|
4639
|
+
}
|
|
4640
|
+
asm.cursorY -= h;
|
|
4139
4641
|
}
|
|
4140
|
-
asm.cursorY -= h;
|
|
4141
4642
|
const indentLeft = pb.resolved.indentLeft + (line.firstLine ? pb.resolved.indentFirstLine : 0);
|
|
4142
4643
|
const offset = alignmentOffset(pb.resolved.alignment, line.contentWidthPt, line.availableWidthPt);
|
|
4143
4644
|
const baselineY = pt(asm.ctx.pageHeight - (asm.cursorY + lineBaselineOffset(line, pb.resolved)));
|
|
4144
|
-
const
|
|
4645
|
+
const geometry = asm.exclusions.length > 0 ? asm.lineGeometryAt(asm.cursorY + h, h) : void 0;
|
|
4646
|
+
const exclusionShift = pairedNow ?? geometry?.xOffset ?? 0;
|
|
4647
|
+
if (pairedNow === void 0 && geometry?.pair && Math.abs(line.availableWidthPt - geometry.width) < 1) pairPending = geometry.pair.xOffset;
|
|
4145
4648
|
const originX = asm.colLeft() + indentLeft + offset + exclusionShift;
|
|
4146
4649
|
if (markerLblId !== void 0 && line === firstLine) {
|
|
4147
4650
|
let k = 0;
|
|
@@ -4197,6 +4700,7 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
|
|
|
4197
4700
|
height: pt(ins?.heightPt ?? block.heightPt),
|
|
4198
4701
|
imageResourceName: block.resourceName,
|
|
4199
4702
|
...block.crop ? { crop: block.crop } : {},
|
|
4703
|
+
...block.wash ? { wash: block.wash } : {},
|
|
4200
4704
|
...block.rotationDeg ? { rotationDeg: block.rotationDeg } : {},
|
|
4201
4705
|
...block.flipH ? { flipH: true } : {},
|
|
4202
4706
|
...block.flipV ? { flipV: true } : {},
|
|
@@ -4236,7 +4740,7 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
|
|
|
4236
4740
|
asm.cursorY -= block.spacingAfterPt;
|
|
4237
4741
|
}
|
|
4238
4742
|
} else if (block.kind === "chart") {
|
|
4239
|
-
const figId = builder ? createFigure(builder, block.altText, "Chart") : void 0;
|
|
4743
|
+
const figId = builder ? createFigure(builder, block.altText, block.figureRole ?? "Chart") : void 0;
|
|
4240
4744
|
const emitChartAt = (x, bottomYUp, sink) => {
|
|
4241
4745
|
sink.push(...chartPageItems(block, x, bottomYUp, asm.ctx.pageHeight, figId));
|
|
4242
4746
|
};
|
|
@@ -4262,7 +4766,7 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
|
|
|
4262
4766
|
const sink = PageAssembler.zSink(block.float.behind ? asm.floatsBehind : asm.floatsFront, block.float.zOrder);
|
|
4263
4767
|
let rowY = fy;
|
|
4264
4768
|
for (const row of block.rows) {
|
|
4265
|
-
emitRowChunk(sink, row, fx, rowY, asm.ctx.pageHeight, colCount);
|
|
4769
|
+
emitRowChunk(sink, row, fx, rowY, asm.ctx.pageHeight, colCount, void 0, void 0, cellAnchorCtx(asm));
|
|
4266
4770
|
rowY -= row.heightPt;
|
|
4267
4771
|
}
|
|
4268
4772
|
if (block.float.wrap !== "none") asm.excludeFloat(block.float, fx, fy, block.totalWidthPt, block.heightPt);
|
|
@@ -4291,7 +4795,9 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
|
|
|
4291
4795
|
return b.create("P", cellNode).id;
|
|
4292
4796
|
});
|
|
4293
4797
|
}
|
|
4294
|
-
const
|
|
4798
|
+
const room = asm.cursorY - asm.bottomLimit();
|
|
4799
|
+
const page = asm.ctx.pageContentHeight;
|
|
4800
|
+
const chunks = !row.cantSplit && row.heightPt > room && asm.colHasContent() && room > 0 ? splitRowIntoChunks(row, room, page) : row.heightPt > page ? splitRowIntoChunks(row, page) : [row];
|
|
4295
4801
|
for (let ci = 0; ci < chunks.length; ci++) {
|
|
4296
4802
|
const chunk = chunks[ci];
|
|
4297
4803
|
const forcedBreak = ci === 0 && row.breakBefore && !isLeadingHeader && asm.current.length > 0;
|
|
@@ -4305,7 +4811,7 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
|
|
|
4305
4811
|
}
|
|
4306
4812
|
}
|
|
4307
4813
|
const mergedHeights = chunks.length === 1 && row.cells.some((c) => c.mergeRole === "start") ? row.cells.map((c) => c.mergeRole === "start" ? mergedBoxHeightPt(block.rows, ri, c.colStart, asm.cursorY - asm.bottomLimit()) : void 0) : void 0;
|
|
4308
|
-
emitRowChunk(asm.current, chunk, tableX, asm.cursorY, asm.ctx.pageHeight, colCount, cellStructIds, mergedHeights);
|
|
4814
|
+
emitRowChunk(asm.current, chunk, tableX, asm.cursorY, asm.ctx.pageHeight, colCount, cellStructIds, mergedHeights, cellAnchorCtx(asm));
|
|
4309
4815
|
asm.cursorY -= chunk.heightPt;
|
|
4310
4816
|
if (ci === chunks.length - 1 && ri < block.rows.length - 1) {
|
|
4311
4817
|
const next = block.rows[ri + 1];
|
|
@@ -4374,7 +4880,7 @@ function tableCellRowSpan(rows, startRowIdx, colStart) {
|
|
|
4374
4880
|
}
|
|
4375
4881
|
return span;
|
|
4376
4882
|
}
|
|
4377
|
-
function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellStructIds, mergedHeights) {
|
|
4883
|
+
function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellStructIds, mergedHeights, anchored) {
|
|
4378
4884
|
const rowTop = cursorY;
|
|
4379
4885
|
const rowBottom = cursorY - row.heightPt;
|
|
4380
4886
|
{
|
|
@@ -4553,7 +5059,7 @@ function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellS
|
|
|
4553
5059
|
if (nt.afterLine !== n) continue;
|
|
4554
5060
|
for (const nrow of nt.block.rows) {
|
|
4555
5061
|
const nestedIds = structId !== void 0 ? nrow.cells.map(() => structId) : void 0;
|
|
4556
|
-
emitRowChunk(out, nrow, nestedX, textY, pageHeight, nt.block.colCount, nestedIds);
|
|
5062
|
+
emitRowChunk(out, nrow, nestedX, textY, pageHeight, nt.block.colCount, nestedIds, void 0, anchored);
|
|
4557
5063
|
textY -= nrow.heightPt;
|
|
4558
5064
|
}
|
|
4559
5065
|
}
|
|
@@ -4563,6 +5069,29 @@ function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellS
|
|
|
4563
5069
|
textY -= cs.block.heightPt;
|
|
4564
5070
|
emitShapeItems(cs.block, nestedX + offset, textY, out, pageHeight, structId);
|
|
4565
5071
|
}
|
|
5072
|
+
for (const cf of cell.floats ?? []) {
|
|
5073
|
+
if (cf.afterLine !== n || !anchored) continue;
|
|
5074
|
+
const inner = cell.widthPt - cell.padLeftPt - cell.padRightPt;
|
|
5075
|
+
const cellTop = rowTop - cell.padTopPt;
|
|
5076
|
+
const cellBottom = rowTop - (mergedHeights?.[i] ?? row.heightPt) + cell.padBottomPt;
|
|
5077
|
+
const own = cf.block.float?.inCell !== false;
|
|
5078
|
+
emitCellFloat(cf, anchored, {
|
|
5079
|
+
...anchored.frame,
|
|
5080
|
+
...own ? {
|
|
5081
|
+
pageLeft: nestedX,
|
|
5082
|
+
pageWidth: inner,
|
|
5083
|
+
pageTopYUp: cellTop,
|
|
5084
|
+
pageBottomYUp: cellBottom,
|
|
5085
|
+
marginLeft: nestedX,
|
|
5086
|
+
contentWidth: inner,
|
|
5087
|
+
marginTopYUp: cellTop,
|
|
5088
|
+
marginBottomYUp: cellBottom
|
|
5089
|
+
} : {},
|
|
5090
|
+
columnLeft: nestedX,
|
|
5091
|
+
columnWidth: inner,
|
|
5092
|
+
anchorY: textY
|
|
5093
|
+
});
|
|
5094
|
+
}
|
|
4566
5095
|
};
|
|
4567
5096
|
for (const [lineIdx, line] of cell.lines.entries()) {
|
|
4568
5097
|
drawNestedBefore(lineIdx);
|
|
@@ -4588,10 +5117,13 @@ function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellS
|
|
|
4588
5117
|
drawNestedBefore(cell.lines.length);
|
|
4589
5118
|
}
|
|
4590
5119
|
}
|
|
4591
|
-
function splitRowIntoChunks(row, capacity) {
|
|
5120
|
+
function splitRowIntoChunks(row, capacity, restCapacity = capacity) {
|
|
4592
5121
|
if (row.cells.some((c) => c.nestedTables && c.nestedTables.length > 0)) return [row];
|
|
4593
5122
|
const queues = row.cells.map((c) => ({
|
|
4594
|
-
remaining:
|
|
5123
|
+
remaining: c.lines.map((line, i) => ({
|
|
5124
|
+
line,
|
|
5125
|
+
gapPt: c.lineGaps?.get(i) ?? 0
|
|
5126
|
+
})),
|
|
4595
5127
|
template: c
|
|
4596
5128
|
}));
|
|
4597
5129
|
const anyHasLines = () => queues.some((q) => q.template.mergeRole === "middle" || q.template.mergeRole === "end" ? false : q.remaining.length > 0);
|
|
@@ -4609,23 +5141,31 @@ function splitRowIntoChunks(row, capacity) {
|
|
|
4609
5141
|
continue;
|
|
4610
5142
|
}
|
|
4611
5143
|
const padTop = isFirst ? tpl.padTopPt : 0;
|
|
4612
|
-
const capacityForLines = Math.max(0, capacity - padTop);
|
|
5144
|
+
const capacityForLines = Math.max(0, (isFirst ? capacity : restCapacity) - padTop);
|
|
4613
5145
|
const taken = [];
|
|
4614
5146
|
let takenHeight = 0;
|
|
4615
5147
|
while (q.remaining.length > 0) {
|
|
4616
5148
|
const next = q.remaining[0];
|
|
4617
|
-
const lh = computeLineHeight(next, next.resolved);
|
|
5149
|
+
const lh = computeLineHeight(next.line, next.line.resolved) + next.gapPt;
|
|
4618
5150
|
if (taken.length > 0 && takenHeight + lh > capacityForLines) break;
|
|
4619
5151
|
taken.push(next);
|
|
4620
5152
|
takenHeight += lh;
|
|
4621
5153
|
q.remaining.shift();
|
|
4622
5154
|
}
|
|
4623
5155
|
const cellHeight = padTop + takenHeight;
|
|
5156
|
+
const gaps = /* @__PURE__ */ new Map();
|
|
5157
|
+
taken.forEach((t, li) => {
|
|
5158
|
+
if (t.gapPt > 0) gaps.set(li, t.gapPt);
|
|
5159
|
+
});
|
|
5160
|
+
const { lineGaps: _g, shapes: _s, floats: _f, ...restOfCell } = tpl;
|
|
4624
5161
|
const chunkCell = {
|
|
4625
|
-
...
|
|
5162
|
+
...restOfCell,
|
|
5163
|
+
...isFirst && tpl.shapes ? { shapes: tpl.shapes } : {},
|
|
5164
|
+
...isFirst && tpl.floats ? { floats: tpl.floats } : {},
|
|
5165
|
+
...gaps.size > 0 ? { lineGaps: gaps } : {},
|
|
4626
5166
|
padTopPt: padTop,
|
|
4627
5167
|
padBottomPt: 0,
|
|
4628
|
-
lines: taken,
|
|
5168
|
+
lines: taken.map((t) => t.line),
|
|
4629
5169
|
contentHeightPt: takenHeight,
|
|
4630
5170
|
totalHeightPt: cellHeight
|
|
4631
5171
|
};
|
|
@@ -4664,7 +5204,8 @@ function splitRowIntoChunks(row, capacity) {
|
|
|
4664
5204
|
cells: chunkCells,
|
|
4665
5205
|
columnXOffsets: row.columnXOffsets,
|
|
4666
5206
|
rowIdx: row.rowIdx,
|
|
4667
|
-
rowCount: row.rowCount
|
|
5207
|
+
rowCount: row.rowCount,
|
|
5208
|
+
...row.isHeader ? { isHeader: true } : {}
|
|
4668
5209
|
});
|
|
4669
5210
|
isFirst = false;
|
|
4670
5211
|
}
|