reamkit 1.18.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 +6 -0
- package/dist/esm/core/drawingml/colors.js +13 -6
- package/dist/esm/core/po-helpers.d.ts +9 -0
- package/dist/esm/core/po-helpers.js +9 -1
- package/dist/esm/layout/styled-layout.js +163 -27
- package/dist/esm/word/drawing-parser.js +5 -2
- 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
|
@@ -992,6 +992,12 @@ export interface ShapeFill {
|
|
|
992
992
|
readonly kind: ShapeFillKind;
|
|
993
993
|
readonly colorHex?: string;
|
|
994
994
|
readonly gradient?: ShapeGradient;
|
|
995
|
+
/**
|
|
996
|
+
* §14.1.2.5 `@type="tile"` — the picture REPEATS at its own size rather than
|
|
997
|
+
* stretching over the box. A parchment behind a page, a texture behind a
|
|
998
|
+
* text box: stretched, such a fill is a blur where it should be a pattern.
|
|
999
|
+
*/
|
|
1000
|
+
readonly tiled?: boolean;
|
|
995
1001
|
/**
|
|
996
1002
|
* §20.1.8.14 `a:blipFill` — the picture painted across the shape's box. A
|
|
997
1003
|
* DrawingML picture IS a shape with one of these, which is how a `pic:pic`
|
|
@@ -69,6 +69,13 @@ function hslToRgb(h, s, l) {
|
|
|
69
69
|
b + m
|
|
70
70
|
];
|
|
71
71
|
}
|
|
72
|
+
function toLinear(c) {
|
|
73
|
+
return c <= .04045 ? c / 12.92 : Math.pow((c + .055) / 1.055, 2.4);
|
|
74
|
+
}
|
|
75
|
+
function toSrgb(c) {
|
|
76
|
+
const v = clamp01(c);
|
|
77
|
+
return v <= .0031308 ? v * 12.92 : 1.055 * Math.pow(v, 1 / 2.4) - .055;
|
|
78
|
+
}
|
|
72
79
|
/**
|
|
73
80
|
* Apply DrawingML colour transforms ({@link ColorMod}s) to a 6-hex value,
|
|
74
81
|
* returning a 6-hex value. `shade`/`tint` scale in RGB; `lumMod`/`lumOff` adjust
|
|
@@ -85,13 +92,13 @@ function applyColorMods(hex, mods) {
|
|
|
85
92
|
let g = (n >> 8 & 255) / 255;
|
|
86
93
|
let b = (n & 255) / 255;
|
|
87
94
|
for (const m of mods) if (m.kind === "shade") {
|
|
88
|
-
r
|
|
89
|
-
g
|
|
90
|
-
b
|
|
95
|
+
r = toSrgb(toLinear(r) * m.val);
|
|
96
|
+
g = toSrgb(toLinear(g) * m.val);
|
|
97
|
+
b = toSrgb(toLinear(b) * m.val);
|
|
91
98
|
} else if (m.kind === "tint") {
|
|
92
|
-
r = r * m.val + (1 - m.val);
|
|
93
|
-
g = g * m.val + (1 - m.val);
|
|
94
|
-
b = b * m.val + (1 - m.val);
|
|
99
|
+
r = toSrgb(toLinear(r) * m.val + (1 - m.val));
|
|
100
|
+
g = toSrgb(toLinear(g) * m.val + (1 - m.val));
|
|
101
|
+
b = toSrgb(toLinear(b) * m.val + (1 - m.val));
|
|
95
102
|
} else if (m.kind === "lumMod" || m.kind === "lumOff") {
|
|
96
103
|
const [h, s, l] = rgbToHsl(r, g, b);
|
|
97
104
|
const l2 = m.kind === "lumMod" ? l * m.val : clamp01(l + m.val);
|
|
@@ -34,6 +34,15 @@ export declare function poAttrLocal(node: PoNode | undefined, name: string): str
|
|
|
34
34
|
export declare function poIsLocal(node: PoNode | undefined, name: string): boolean;
|
|
35
35
|
/** {@link poAttr} parsed as a finite number, or undefined when absent/non-numeric. */
|
|
36
36
|
export declare function poIntAttr(node: PoNode | undefined, name: string): number | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* §22.9.2.15 ST_UniversalMeasure — a number with a UNIT (`"28.35pt"`, `"1cm"`)
|
|
39
|
+
* as twips. Producers write whole page geometries, tab stops and indents this
|
|
40
|
+
* way; read as a plain number they are nothing at all.
|
|
41
|
+
*
|
|
42
|
+
* @param raw The attribute value.
|
|
43
|
+
* @returns The measure in twips, or undefined when it names no unit.
|
|
44
|
+
*/
|
|
45
|
+
export declare function universalMeasureTwips(raw: string): number | undefined;
|
|
37
46
|
/** Shorthand for the `val` attribute (`@w:val`/…). */
|
|
38
47
|
export declare function poVal(node: PoNode | undefined): string | undefined;
|
|
39
48
|
/**
|
|
@@ -76,6 +76,14 @@ var MEASURE_TWIPS = new Map([
|
|
|
76
76
|
["cm", 1440 / 2.54],
|
|
77
77
|
["mm", 144 / 2.54]
|
|
78
78
|
]);
|
|
79
|
+
/**
|
|
80
|
+
* §22.9.2.15 ST_UniversalMeasure — a number with a UNIT (`"28.35pt"`, `"1cm"`)
|
|
81
|
+
* as twips. Producers write whole page geometries, tab stops and indents this
|
|
82
|
+
* way; read as a plain number they are nothing at all.
|
|
83
|
+
*
|
|
84
|
+
* @param raw The attribute value.
|
|
85
|
+
* @returns The measure in twips, or undefined when it names no unit.
|
|
86
|
+
*/
|
|
79
87
|
function universalMeasureTwips(raw) {
|
|
80
88
|
const m = /^\s*(-?[0-9]+(?:\.[0-9]+)?)(pt|pc|pi|in|cm|mm)\s*$/iu.exec(raw);
|
|
81
89
|
if (!m) return void 0;
|
|
@@ -134,4 +142,4 @@ function poFindDescendant(node, tag) {
|
|
|
134
142
|
}
|
|
135
143
|
}
|
|
136
144
|
//#endregion
|
|
137
|
-
export { poAttr, poAttrLocal, poChildren, poChildrenWith, poFindByPath, poFindDescendant, poFirstChild, poIntAttr, poIs, poIsLocal, poTag, poText, poToggle, poVal };
|
|
145
|
+
export { poAttr, poAttrLocal, poChildren, poChildrenWith, poFindByPath, poFindDescendant, poFirstChild, poIntAttr, poIs, poIsLocal, poTag, poText, poToggle, poVal, universalMeasureTwips };
|
|
@@ -432,7 +432,7 @@ function layoutStyledDocument(body, options) {
|
|
|
432
432
|
const commentNotes = options.commentAnnotations && options.comments ? buildCommentNotes(noteAssigned.comments, options.comments) : void 0;
|
|
433
433
|
const reflowParagraph = (paragraph, width, widths) => layoutParagraphBlock(paragraph, options, fontResources, imageResources, width, widths);
|
|
434
434
|
return {
|
|
435
|
-
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)),
|
|
436
436
|
resources: options.resources ?? new ResourceStore(),
|
|
437
437
|
fontResources,
|
|
438
438
|
imageResources,
|
|
@@ -609,7 +609,7 @@ function balancedColumnHeight(bandHeightPt, colCount, roomPt) {
|
|
|
609
609
|
/** Total laid-out height of a run of blocks, paragraph spacing included. */
|
|
610
610
|
function blocksHeight(blocks) {
|
|
611
611
|
return blocks.reduce((sum, b) => {
|
|
612
|
-
if (b.kind !== "paragraph" &&
|
|
612
|
+
if (b.kind !== "paragraph" && isOutOfFlowFloat(b.float)) return sum;
|
|
613
613
|
return sum + (b.kind === "paragraph" ? b.spacingBeforePt + b.heightPt + b.spacingAfterPt : b.heightPt);
|
|
614
614
|
}, 0);
|
|
615
615
|
}
|
|
@@ -1024,7 +1024,12 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
|
|
|
1024
1024
|
heightPt = maxHeight;
|
|
1025
1025
|
}
|
|
1026
1026
|
const fillGradient = shape.fill.kind === "gradient" ? shape.fill.gradient : void 0;
|
|
1027
|
-
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;
|
|
1028
1033
|
const fillColorHex = shape.fill.kind === "solid" ? shape.fill.colorHex : fillGradient ? gradientToSolid(fillGradient) : void 0;
|
|
1029
1034
|
const stroke = buildStroke(shape.line);
|
|
1030
1035
|
const t = shape.transform;
|
|
@@ -1178,6 +1183,7 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
|
|
|
1178
1183
|
paths,
|
|
1179
1184
|
...markerPaths.length > 0 ? { markerPaths } : {},
|
|
1180
1185
|
...fillImageResourceName ? { fillImageResourceName } : {},
|
|
1186
|
+
...fillImageTile ? { fillImageTile } : {},
|
|
1181
1187
|
...fillImageResourceName && shape.fill.imageCrop ? { fillImageCrop: shape.fill.imageCrop } : {},
|
|
1182
1188
|
...fillColorHex ? { fillColorHex } : {},
|
|
1183
1189
|
...fillGradient ? { fillGradient } : {},
|
|
@@ -1612,6 +1618,21 @@ function makeChartLabelLine(text, font, sizePt, colorHex) {
|
|
|
1612
1618
|
isLastInParagraph: true
|
|
1613
1619
|
};
|
|
1614
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
|
+
}
|
|
1615
1636
|
function collectImageResources(body, headersFooters, options) {
|
|
1616
1637
|
const out = /* @__PURE__ */ new Map();
|
|
1617
1638
|
if (!options.resources || options.resources.size === 0) return out;
|
|
@@ -1794,20 +1815,38 @@ function emitCellFloat(cf, ctx, frame) {
|
|
|
1794
1815
|
if (float.wrap !== "none") ctx.exclude(float, x, topYUp, cf.block.widthPt, cf.block.heightPt);
|
|
1795
1816
|
}
|
|
1796
1817
|
function emitShapeItems(sh, x, bottomYUp, sink, pageHeight, figId) {
|
|
1797
|
-
if (sh.fillImageResourceName)
|
|
1798
|
-
|
|
1799
|
-
x: pt(x),
|
|
1800
|
-
y: pt(pageHeight - (bottomYUp + sh.heightPt)),
|
|
1801
|
-
width: pt(sh.widthPt),
|
|
1802
|
-
height: pt(sh.heightPt),
|
|
1803
|
-
imageResourceName: sh.fillImageResourceName,
|
|
1804
|
-
...sh.fillImageCrop ? { crop: sh.fillImageCrop } : {},
|
|
1805
|
-
clip: {
|
|
1818
|
+
if (sh.fillImageResourceName) {
|
|
1819
|
+
const clip = {
|
|
1806
1820
|
paths: sh.paths,
|
|
1807
1821
|
transform: flipTransform(buildShapeTransform(x, bottomYUp, sh.widthPt, sh.heightPt, sh.rotation60k, sh.flipH, sh.flipV), pageHeight)
|
|
1808
|
-
}
|
|
1809
|
-
|
|
1810
|
-
|
|
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
|
+
}
|
|
1811
1850
|
if (sh.markerPaths && sh.stroke) sink.push({
|
|
1812
1851
|
type: "shape",
|
|
1813
1852
|
shape: {
|
|
@@ -1906,6 +1945,15 @@ function drawBlocksSequentially(blocks, startX, startY, pageHeight, contentWidth
|
|
|
1906
1945
|
const ordered = blocks.some(isBehind) ? [...blocks.filter(isBehind), ...blocks.filter((b) => !isBehind(b))] : blocks;
|
|
1907
1946
|
for (const block of ordered) {
|
|
1908
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
|
+
}
|
|
1909
1957
|
const tableX = startX + block.xOffsetPt;
|
|
1910
1958
|
for (const row of block.rows) {
|
|
1911
1959
|
emitRowChunk(out, row, tableX, cursorY, pageHeight, block.colCount);
|
|
@@ -3211,9 +3259,67 @@ function alignmentOffset(alignment, lineWidth, available) {
|
|
|
3211
3259
|
if (alignment === "center") return Math.max(0, (available - lineWidth) / 2);
|
|
3212
3260
|
return 0;
|
|
3213
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
|
+
}
|
|
3214
3319
|
function layoutTableBlock(table, options, fontResources, imageResources, contentWidth) {
|
|
3215
3320
|
const columnWidthsPt = computeColumnWidths(table, options, fontResources, contentWidth);
|
|
3216
3321
|
const mergeRoles = table.rows.map((r) => r.cells.map((c) => c.properties.merge ?? "standalone"));
|
|
3322
|
+
table = withMergedShading(table, mergeRoles);
|
|
3217
3323
|
const rows = [];
|
|
3218
3324
|
const colCount = columnWidthsPt.length;
|
|
3219
3325
|
let aboveBordersByCol = [];
|
|
@@ -3792,12 +3898,12 @@ var PageAssembler = class PageAssembler {
|
|
|
3792
3898
|
* `undefined` when there are none.
|
|
3793
3899
|
* @param bookmarkPositions Out-param: bookmark name → its GoTo destination.
|
|
3794
3900
|
*/
|
|
3795
|
-
constructor(sectionCtxs, builder, notes, bookmarkPositions,
|
|
3901
|
+
constructor(sectionCtxs, builder, notes, bookmarkPositions, backgroundImage) {
|
|
3796
3902
|
this.sectionCtxs = sectionCtxs;
|
|
3797
3903
|
this.builder = builder;
|
|
3798
3904
|
this.notes = notes;
|
|
3799
3905
|
this.bookmarkPositions = bookmarkPositions;
|
|
3800
|
-
this.
|
|
3906
|
+
this.backgroundImage = backgroundImage;
|
|
3801
3907
|
this.ctx = sectionCtxs[0];
|
|
3802
3908
|
this.pageStartCtx = this.ctx;
|
|
3803
3909
|
this.pageNumber = this.ctx.properties.pageNumberStart ?? 1;
|
|
@@ -4305,19 +4411,49 @@ var PageAssembler = class PageAssembler {
|
|
|
4305
4411
|
*
|
|
4306
4412
|
* @returns The background items for this page (at most one).
|
|
4307
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
|
+
};
|
|
4308
4436
|
pageBackgroundItems = () => {
|
|
4309
4437
|
const options = this.ctx.options;
|
|
4310
4438
|
const w = this.ctx.pageWidth;
|
|
4311
4439
|
const h = this.ctx.pageHeight;
|
|
4312
4440
|
const fill = options?.pageBackgroundFill;
|
|
4313
|
-
if (fill?.kind === "picture" && this.
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
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
|
+
}
|
|
4321
4457
|
if (fill?.kind === "gradient" && fill.gradient) return [{
|
|
4322
4458
|
type: "shape",
|
|
4323
4459
|
shape: {
|
|
@@ -4401,9 +4537,9 @@ var PageAssembler = class PageAssembler {
|
|
|
4401
4537
|
this.balanceHeightPt = 0;
|
|
4402
4538
|
};
|
|
4403
4539
|
};
|
|
4404
|
-
function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", notes, bookmarkPositions, reflowParagraph,
|
|
4540
|
+
function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", notes, bookmarkPositions, reflowParagraph, backgroundImage) {
|
|
4405
4541
|
if (sectionCtxs.length === 0) return [];
|
|
4406
|
-
const asm = new PageAssembler(sectionCtxs, builder, notes, bookmarkPositions,
|
|
4542
|
+
const asm = new PageAssembler(sectionCtxs, builder, notes, bookmarkPositions, backgroundImage);
|
|
4407
4543
|
const balanceIfEndsContinuous = (fromBlock) => {
|
|
4408
4544
|
const here = asm.ctx;
|
|
4409
4545
|
const next = sectionCtxs[asm.secIdx + 1];
|
|
@@ -838,11 +838,13 @@ function vmlFill(shape, shapeType, resolveImage) {
|
|
|
838
838
|
const fillEl = poChildren(shape).find((c) => poIs(c, "v:fill"));
|
|
839
839
|
if (fillEl && (poAttr(fillEl, "type") === "none" || poAttr(fillEl, "on") === "f")) return { kind: "none" };
|
|
840
840
|
const base = vmlColor(poAttr(shape, "fillcolor")) ?? (fillEl ? vmlColor(poAttr(fillEl, "color")) : void 0) ?? vmlColor(shapeType ? poAttr(shapeType, "fillcolor") : void 0) ?? "FFFFFF";
|
|
841
|
-
const
|
|
841
|
+
const fillType = fillEl ? poAttr(fillEl, "type") : void 0;
|
|
842
|
+
const fillRelId = fillType === "frame" || fillType === "tile" ? poAttr(fillEl, "r:id") : void 0;
|
|
842
843
|
const picture = fillRelId !== void 0 ? resolveImage?.(fillRelId) : void 0;
|
|
843
844
|
if (picture !== void 0) return {
|
|
844
845
|
kind: "picture",
|
|
845
|
-
imageResource: picture
|
|
846
|
+
imageResource: picture,
|
|
847
|
+
...fillType === "tile" ? { tiled: true } : {}
|
|
846
848
|
};
|
|
847
849
|
const gradient = fillEl ? vmlGradient(fillEl, base) : void 0;
|
|
848
850
|
if (gradient) return {
|
|
@@ -1802,6 +1804,7 @@ function fillFromNode(child, resolveColor, resolveImage) {
|
|
|
1802
1804
|
return {
|
|
1803
1805
|
kind: "picture",
|
|
1804
1806
|
imageResource: resource,
|
|
1807
|
+
...poChildren(child).some((c) => poIs(c, "a:tile")) ? { tiled: true } : {},
|
|
1805
1808
|
...crop ? { imageCrop: crop } : {}
|
|
1806
1809
|
};
|
|
1807
1810
|
}
|
|
@@ -131,8 +131,8 @@ function parseParagraphProperties(pPr, autoSpacingPt = 0) {
|
|
|
131
131
|
}
|
|
132
132
|
if ("w:ind" in el) {
|
|
133
133
|
const node = el["w:ind"];
|
|
134
|
-
const left = parseIntAttr(node, "left");
|
|
135
|
-
const right = parseIntAttr(node, "right");
|
|
134
|
+
const left = parseIntAttr(node, "left") ?? parseIntAttr(node, "start");
|
|
135
|
+
const right = parseIntAttr(node, "right") ?? parseIntAttr(node, "end");
|
|
136
136
|
const firstLine = parseIntAttr(node, "firstLine");
|
|
137
137
|
const hanging = parseIntAttr(node, "hanging");
|
|
138
138
|
if (left !== void 0) out.indentLeft = twipsToPt(left);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { halfPtToPt, twipsToPt } from "../core/ir/units.js";
|
|
2
|
-
import { asElement, getAttr, getVal,
|
|
2
|
+
import { asElement, getAttr, getVal, parseHalfPointAttr, parseToggle } from "./xml-helpers.js";
|
|
3
3
|
import { shadingFillHex } from "./shading.js";
|
|
4
4
|
//#region src/word/run-properties.ts
|
|
5
5
|
var UNDERLINE_STYLES = new Set([
|
|
@@ -81,7 +81,7 @@ function parseRunProperties(rPr) {
|
|
|
81
81
|
if (c && /^[0-9A-Fa-f]{6}$/u.test(c)) out.underlineColorHex = c.toUpperCase();
|
|
82
82
|
}
|
|
83
83
|
if ("w:sz" in el) {
|
|
84
|
-
const v =
|
|
84
|
+
const v = parseHalfPointAttr(el["w:sz"], "val");
|
|
85
85
|
if (v !== void 0) out.fontSizePt = halfPtToPt(v);
|
|
86
86
|
}
|
|
87
87
|
if ("w:color" in el) {
|
|
@@ -36,6 +36,18 @@ export declare function getVal(node: unknown): string | undefined;
|
|
|
36
36
|
* @param name The local attribute name.
|
|
37
37
|
* @returns The parsed number, or `undefined` when absent or not finite.
|
|
38
38
|
*/
|
|
39
|
+
/**
|
|
40
|
+
* §17.3.2.38 ST_HpsMeasure — a size in HALF-POINTS, or the same universal
|
|
41
|
+
* measure with a unit on it ("20pt"). The two units differ by a factor of ten
|
|
42
|
+
* from the twips a length is written in, so a size is read here rather than
|
|
43
|
+
* through {@link parseIntAttr}: tdf108408.docx writes `w:sz w:val="20pt"` and
|
|
44
|
+
* read as twips it set its sample text 200 points tall.
|
|
45
|
+
*
|
|
46
|
+
* @param node The element carrying the attribute.
|
|
47
|
+
* @param name The attribute name.
|
|
48
|
+
* @returns The size in half-points, or undefined.
|
|
49
|
+
*/
|
|
50
|
+
export declare function parseHalfPointAttr(node: unknown, name: string): number | undefined;
|
|
39
51
|
export declare function parseIntAttr(node: unknown, name: string): number | undefined;
|
|
40
52
|
/**
|
|
41
53
|
* Parse a toggle (boolean) property per ECMA-376 Part 1 §17.17.4:
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { universalMeasureTwips } from "../core/po-helpers.js";
|
|
1
2
|
//#region src/word/xml-helpers.ts
|
|
2
3
|
/**
|
|
3
4
|
* Narrow an arbitrary parsed value to an {@link XmlElement}.
|
|
@@ -47,12 +48,31 @@ function getVal(node) {
|
|
|
47
48
|
* @param name The local attribute name.
|
|
48
49
|
* @returns The parsed number, or `undefined` when absent or not finite.
|
|
49
50
|
*/
|
|
51
|
+
/**
|
|
52
|
+
* §17.3.2.38 ST_HpsMeasure — a size in HALF-POINTS, or the same universal
|
|
53
|
+
* measure with a unit on it ("20pt"). The two units differ by a factor of ten
|
|
54
|
+
* from the twips a length is written in, so a size is read here rather than
|
|
55
|
+
* through {@link parseIntAttr}: tdf108408.docx writes `w:sz w:val="20pt"` and
|
|
56
|
+
* read as twips it set its sample text 200 points tall.
|
|
57
|
+
*
|
|
58
|
+
* @param node The element carrying the attribute.
|
|
59
|
+
* @param name The attribute name.
|
|
60
|
+
* @returns The size in half-points, or undefined.
|
|
61
|
+
*/
|
|
62
|
+
function parseHalfPointAttr(node, name) {
|
|
63
|
+
const v = getAttr(node, name);
|
|
64
|
+
if (v === void 0) return void 0;
|
|
65
|
+
const n = Number(v);
|
|
66
|
+
if (Number.isFinite(n)) return n;
|
|
67
|
+
const twips = universalMeasureTwips(v);
|
|
68
|
+
return twips === void 0 ? void 0 : twips / 10;
|
|
69
|
+
}
|
|
50
70
|
function parseIntAttr(node, name) {
|
|
51
71
|
const v = getAttr(node, name);
|
|
52
72
|
if (v === void 0) return void 0;
|
|
53
73
|
const n = Number(v);
|
|
54
|
-
if (
|
|
55
|
-
return
|
|
74
|
+
if (Number.isFinite(n)) return n;
|
|
75
|
+
return universalMeasureTwips(v);
|
|
56
76
|
}
|
|
57
77
|
/**
|
|
58
78
|
* Parse a toggle (boolean) property per ECMA-376 Part 1 §17.17.4:
|
|
@@ -71,4 +91,4 @@ function parseToggle(node) {
|
|
|
71
91
|
return !(v === "false" || v === "0" || v === "off");
|
|
72
92
|
}
|
|
73
93
|
//#endregion
|
|
74
|
-
export { asArray, asElement, getAttr, getVal, parseIntAttr, parseToggle };
|
|
94
|
+
export { asArray, asElement, getAttr, getVal, parseHalfPointAttr, parseIntAttr, parseToggle };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reamkit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.19.0",
|
|
4
4
|
"description": "Ream — convert DOCX, XLSX, PPTX and PDF to PDF, SVG, HTML, DOCX and XLSX, built from scratch on the ECMA-376 and ISO 32000 specifications. Parse once, convert anywhere.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Alex Krassavin <info@reamkit.dev>",
|