reamkit 1.20.0 → 1.22.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/bytes.d.ts +22 -4
- package/dist/esm/core/bytes.js +73 -5
- package/dist/esm/core/crypto/offcrypto.d.ts +1 -0
- package/dist/esm/core/crypto/offcrypto.js +2 -1
- package/dist/esm/core/document-model/types.d.ts +60 -0
- package/dist/esm/core/drawingml/chart-parser.d.ts +2 -1
- package/dist/esm/core/drawingml/chart-parser.js +23 -1
- package/dist/esm/core/drawingml/colors.d.ts +27 -3
- package/dist/esm/core/drawingml/colors.js +30 -6
- package/dist/esm/core/drawingml/preset-geometry.js +236 -0
- package/dist/esm/core/metafile/emf.js +2 -1
- package/dist/esm/core/metafile/symbol-fonts.d.ts +42 -0
- package/dist/esm/core/metafile/symbol-fonts.js +154 -0
- package/dist/esm/core/metafile/wmf.js +35 -2
- package/dist/esm/core/vector.d.ts +5 -0
- package/dist/esm/excel/sheet-shape-parser.js +1 -12
- package/dist/esm/excel/xlsx-reader.d.ts +1 -1
- package/dist/esm/excel/xlsx-reader.js +3 -3
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +2 -1
- package/dist/esm/layout/page-doc.d.ts +38 -0
- package/dist/esm/layout/page-doc.js +34 -18
- package/dist/esm/layout/styled-layout.js +174 -49
- package/dist/esm/pdf/shading.d.ts +19 -0
- package/dist/esm/pdf/shading.js +84 -11
- package/dist/esm/pdf/styled-page-emitter.js +614 -444
- package/dist/esm/pptx/ole-preview.d.ts +10 -0
- package/dist/esm/pptx/ole-preview.js +52 -0
- package/dist/esm/pptx/placeholder-cascade.d.ts +69 -5
- package/dist/esm/pptx/placeholder-cascade.js +162 -25
- package/dist/esm/pptx/pptx-reader.js +247 -39
- package/dist/esm/pptx/slide-parser.d.ts +72 -10
- package/dist/esm/pptx/slide-parser.js +310 -82
- package/dist/esm/pptx/sp-helpers.js +19 -4
- package/dist/esm/pptx/table-style.d.ts +56 -0
- package/dist/esm/pptx/table-style.js +161 -0
- package/dist/esm/svg/svg-writer.js +53 -5
- package/dist/esm/word/docx-reader.js +2 -2
- package/dist/esm/word/drawing-parser.d.ts +34 -0
- package/dist/esm/word/drawing-parser.js +159 -13
- package/package.json +1 -1
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Border, CellBorders, TableCell } from '../core/document-model/index.js';
|
|
2
|
+
import { ColorResolver } from '../core/drawingml/colors.js';
|
|
3
|
+
import { PoNode } from '../core/po-helpers.js';
|
|
4
|
+
/** Which conditional parts of a style a table asks for (`a:tblPr` flags). */
|
|
5
|
+
export interface TableStyleFlags {
|
|
6
|
+
readonly firstRow: boolean;
|
|
7
|
+
readonly lastRow: boolean;
|
|
8
|
+
readonly firstCol: boolean;
|
|
9
|
+
readonly lastCol: boolean;
|
|
10
|
+
readonly bandRow: boolean;
|
|
11
|
+
readonly bandCol: boolean;
|
|
12
|
+
}
|
|
13
|
+
/** What one part of a table style says about a cell. */
|
|
14
|
+
export interface TableStylePart {
|
|
15
|
+
readonly shadingHex?: string;
|
|
16
|
+
readonly borders?: CellBorders;
|
|
17
|
+
readonly bold?: boolean;
|
|
18
|
+
readonly italic?: boolean;
|
|
19
|
+
readonly colorHex?: string;
|
|
20
|
+
}
|
|
21
|
+
/** Where a cell sits, which decides the parts that reach it. */
|
|
22
|
+
export interface CellPosition {
|
|
23
|
+
readonly row: number;
|
|
24
|
+
readonly rowCount: number;
|
|
25
|
+
readonly col: number;
|
|
26
|
+
readonly colCount: number;
|
|
27
|
+
}
|
|
28
|
+
/** The `a:tblPr` flags, all off unless the table says otherwise. */
|
|
29
|
+
export declare function tableStyleFlags(tblPr: PoNode | undefined): TableStyleFlags;
|
|
30
|
+
/** The GUID a table names, if it names one (`a:tblPr/a:tableStyleId`). */
|
|
31
|
+
export declare function tableStyleId(tblPr: PoNode | undefined): string | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* The style a cell wears, composed from every part that reaches it.
|
|
34
|
+
*
|
|
35
|
+
* @param style The `a:tblStyle` node.
|
|
36
|
+
* @param flags Which conditional parts the table asks for.
|
|
37
|
+
* @param at Where the cell sits.
|
|
38
|
+
* @param colors The deck's colour resolver.
|
|
39
|
+
* @returns The composed part, empty when the style says nothing.
|
|
40
|
+
*/
|
|
41
|
+
export declare function cellStyle(style: PoNode, flags: TableStyleFlags, at: CellPosition, colors: ColorResolver): TableStylePart;
|
|
42
|
+
/**
|
|
43
|
+
* §20.1.2.1 `a:ln` → the rule it draws on one side of a cell.
|
|
44
|
+
*
|
|
45
|
+
* Shared with the cell's OWN `a:lnL`/`a:lnR`/`a:lnT`/`a:lnB`, which are the
|
|
46
|
+
* same element under a different name.
|
|
47
|
+
*
|
|
48
|
+
* @param ln The line node.
|
|
49
|
+
* @param colors The deck's colour resolver.
|
|
50
|
+
* @returns The border, `none` when the line is one that draws nothing.
|
|
51
|
+
*/
|
|
52
|
+
export declare function lineBorder(ln: PoNode, colors: ColorResolver): Border;
|
|
53
|
+
/** The same part with the style's fill dropped, for a cell that says `a:noFill`. */
|
|
54
|
+
export declare function withoutFill(part: TableStylePart): TableStylePart;
|
|
55
|
+
/** A cell with the style's fill, borders and run properties filled in under its own. */
|
|
56
|
+
export declare function withCellStyle(cell: TableCell, part: TableStylePart): TableCell;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { pt } from "../core/ir/units.js";
|
|
2
|
+
import { poAttr, poChildren, poIntAttr, poIs, poTag, poText } from "../core/po-helpers.js";
|
|
3
|
+
import { readColorMods, resolveColorNode } from "../core/drawingml/colors.js";
|
|
4
|
+
//#region src/pptx/table-style.ts
|
|
5
|
+
/** The `a:tblPr` flags, all off unless the table says otherwise. */
|
|
6
|
+
function tableStyleFlags(tblPr) {
|
|
7
|
+
const on = (name) => tblPr !== void 0 && (poAttr(tblPr, name) === "1" || poAttr(tblPr, name) === "true");
|
|
8
|
+
return {
|
|
9
|
+
firstRow: on("firstRow"),
|
|
10
|
+
lastRow: on("lastRow"),
|
|
11
|
+
firstCol: on("firstCol"),
|
|
12
|
+
lastCol: on("lastCol"),
|
|
13
|
+
bandRow: on("bandRow"),
|
|
14
|
+
bandCol: on("bandCol")
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/** The GUID a table names, if it names one (`a:tblPr/a:tableStyleId`). */
|
|
18
|
+
function tableStyleId(tblPr) {
|
|
19
|
+
const id = tblPr ? poChildren(tblPr).find((c) => poIs(c, "a:tableStyleId")) : void 0;
|
|
20
|
+
return id ? poText(id).trim() || void 0 : void 0;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The style a cell wears, composed from every part that reaches it.
|
|
24
|
+
*
|
|
25
|
+
* @param style The `a:tblStyle` node.
|
|
26
|
+
* @param flags Which conditional parts the table asks for.
|
|
27
|
+
* @param at Where the cell sits.
|
|
28
|
+
* @param colors The deck's colour resolver.
|
|
29
|
+
* @returns The composed part, empty when the style says nothing.
|
|
30
|
+
*/
|
|
31
|
+
function cellStyle(style, flags, at, colors) {
|
|
32
|
+
const names = ["a:wholeTbl"];
|
|
33
|
+
if (flags.bandCol) names.push(bandName(at.col, flags.firstCol, "V"));
|
|
34
|
+
if (flags.bandRow) names.push(bandName(at.row, flags.firstRow, "H"));
|
|
35
|
+
if (flags.firstCol && at.col === 0) names.push("a:firstCol");
|
|
36
|
+
if (flags.lastCol && at.col === at.colCount - 1) names.push("a:lastCol");
|
|
37
|
+
if (flags.firstRow && at.row === 0) names.push("a:firstRow");
|
|
38
|
+
if (flags.lastRow && at.row === at.rowCount - 1) names.push("a:lastRow");
|
|
39
|
+
let out = {};
|
|
40
|
+
for (const name of names) {
|
|
41
|
+
const part = poChildren(style).find((c) => poIs(c, name));
|
|
42
|
+
if (part) out = {
|
|
43
|
+
...out,
|
|
44
|
+
...partStyle(part, colors)
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
function bandName(index, edge, axis) {
|
|
50
|
+
return (edge ? index - 1 : index) % 2 === 0 ? `a:band1${axis}` : `a:band2${axis}`;
|
|
51
|
+
}
|
|
52
|
+
function partStyle(part, colors) {
|
|
53
|
+
const tcStyle = poChildren(part).find((c) => poIs(c, "a:tcStyle"));
|
|
54
|
+
const txStyle = poChildren(part).find((c) => poIs(c, "a:tcTxStyle"));
|
|
55
|
+
const fill = tcStyle ? poChildren(tcStyle).find((c) => poIs(c, "a:fill")) : void 0;
|
|
56
|
+
const solid = fill ? poChildren(fill).find((c) => poIs(c, "a:solidFill")) : void 0;
|
|
57
|
+
const shadingHex = solid ? colorOf(solid, colors) : void 0;
|
|
58
|
+
const bdr = tcStyle ? poChildren(tcStyle).find((c) => poIs(c, "a:tcBdr")) : void 0;
|
|
59
|
+
const borders = bdr ? partBorders(bdr, colors) : void 0;
|
|
60
|
+
const on = (name) => poAttr(txStyle, name) === "on";
|
|
61
|
+
const colorHex = txStyle ? colorOf(txStyle, colors) : void 0;
|
|
62
|
+
return {
|
|
63
|
+
...shadingHex ? { shadingHex } : {},
|
|
64
|
+
...borders ? { borders } : {},
|
|
65
|
+
...txStyle && on("b") ? { bold: true } : {},
|
|
66
|
+
...txStyle && on("i") ? { italic: true } : {},
|
|
67
|
+
...colorHex ? { colorHex } : {}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
var SIDES = [
|
|
71
|
+
["a:left", "left"],
|
|
72
|
+
["a:right", "right"],
|
|
73
|
+
["a:top", "top"],
|
|
74
|
+
["a:bottom", "bottom"],
|
|
75
|
+
["a:insideH", "insideH"],
|
|
76
|
+
["a:insideV", "insideV"]
|
|
77
|
+
];
|
|
78
|
+
function partBorders(bdr, colors) {
|
|
79
|
+
const out = {};
|
|
80
|
+
for (const [tag, side] of SIDES) {
|
|
81
|
+
const holder = poChildren(bdr).find((c) => poIs(c, tag));
|
|
82
|
+
const ln = holder ? poChildren(holder).find((c) => poIs(c, "a:ln")) : void 0;
|
|
83
|
+
if (ln) out[side] = lineBorder(ln, colors);
|
|
84
|
+
}
|
|
85
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* §20.1.2.1 `a:ln` → the rule it draws on one side of a cell.
|
|
89
|
+
*
|
|
90
|
+
* Shared with the cell's OWN `a:lnL`/`a:lnR`/`a:lnT`/`a:lnB`, which are the
|
|
91
|
+
* same element under a different name.
|
|
92
|
+
*
|
|
93
|
+
* @param ln The line node.
|
|
94
|
+
* @param colors The deck's colour resolver.
|
|
95
|
+
* @returns The border, `none` when the line is one that draws nothing.
|
|
96
|
+
*/
|
|
97
|
+
function lineBorder(ln, colors) {
|
|
98
|
+
if (poChildren(ln).some((c) => poIs(c, "a:noFill")) || isTransparent(ln)) return { style: "none" };
|
|
99
|
+
const colorHex = colorOf(ln, colors);
|
|
100
|
+
const w = poIntAttr(ln, "w");
|
|
101
|
+
return {
|
|
102
|
+
style: "single",
|
|
103
|
+
...colorHex ? { colorHex } : {},
|
|
104
|
+
...w !== void 0 ? { width: pt(Math.max(.25, w / 12700)) } : {}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function isTransparent(ln) {
|
|
108
|
+
const fill = poChildren(ln).find((c) => poIs(c, "a:solidFill"));
|
|
109
|
+
const color = fill ? poChildren(fill).find((c) => poTag(c) !== void 0) : void 0;
|
|
110
|
+
const alpha = color ? readColorMods(color).find((m) => m.kind === "alpha") : void 0;
|
|
111
|
+
return alpha !== void 0 && alpha.val <= .001;
|
|
112
|
+
}
|
|
113
|
+
/** The same part with the style's fill dropped, for a cell that says `a:noFill`. */
|
|
114
|
+
function withoutFill(part) {
|
|
115
|
+
const { shadingHex: _fill, ...rest } = part;
|
|
116
|
+
return rest;
|
|
117
|
+
}
|
|
118
|
+
function colorOf(node, colors) {
|
|
119
|
+
for (const child of poChildren(node)) {
|
|
120
|
+
const hex = resolveColorNode(child, colors);
|
|
121
|
+
if (hex !== void 0) return hex;
|
|
122
|
+
const deeper = poTag(child) !== void 0 ? colorOf(child, colors) : void 0;
|
|
123
|
+
if (deeper !== void 0) return deeper;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/** A cell with the style's fill, borders and run properties filled in under its own. */
|
|
127
|
+
function withCellStyle(cell, part) {
|
|
128
|
+
const runProps = {
|
|
129
|
+
...part.bold === true ? { bold: true } : {},
|
|
130
|
+
...part.italic === true ? { italic: true } : {},
|
|
131
|
+
...part.colorHex !== void 0 ? { colorHex: part.colorHex } : {}
|
|
132
|
+
};
|
|
133
|
+
const content = Object.keys(runProps).length === 0 ? cell.content : cell.content.map((block) => block.kind === "paragraph" ? {
|
|
134
|
+
...block,
|
|
135
|
+
paragraph: {
|
|
136
|
+
...block.paragraph,
|
|
137
|
+
runs: block.paragraph.runs.map((run) => ({
|
|
138
|
+
...run,
|
|
139
|
+
properties: {
|
|
140
|
+
...runProps,
|
|
141
|
+
...run.properties
|
|
142
|
+
}
|
|
143
|
+
}))
|
|
144
|
+
}
|
|
145
|
+
} : block);
|
|
146
|
+
const borders = part.borders || cell.properties.borders ? {
|
|
147
|
+
...part.borders,
|
|
148
|
+
...cell.properties.borders
|
|
149
|
+
} : void 0;
|
|
150
|
+
return {
|
|
151
|
+
...cell,
|
|
152
|
+
properties: {
|
|
153
|
+
...part.shadingHex !== void 0 ? { shading: { colorHex: part.shadingHex } } : {},
|
|
154
|
+
...cell.properties,
|
|
155
|
+
...borders ? { borders } : {}
|
|
156
|
+
},
|
|
157
|
+
content
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
//#endregion
|
|
161
|
+
export { cellStyle, lineBorder, tableStyleFlags, tableStyleId, withCellStyle, withoutFill };
|
|
@@ -56,12 +56,9 @@ var svgWriter = {
|
|
|
56
56
|
};
|
|
57
57
|
function emitPage(out, page, laid, losses, idc) {
|
|
58
58
|
const plan = paintPlan(page.commands);
|
|
59
|
+
for (const item of plan.behind) emitPageItem(out, item, laid, losses, idc);
|
|
59
60
|
for (const f of plan.fills) out.push(`<rect x="${fmt(f.x)}" y="${fmt(f.y)}" width="${fmt(f.width)}" height="${fmt(f.height)}" fill="#${f.fillColorHex}"/>`);
|
|
60
|
-
for (const img of plan.images)
|
|
61
|
-
const href = imageHref(img.imageResourceName, laid);
|
|
62
|
-
if (!href) continue;
|
|
63
|
-
out.push(`<image x="${fmt(img.x)}" y="${fmt(img.y)}" width="${fmt(img.width)}" height="${fmt(img.height)}" href="${href}" preserveAspectRatio="none"/>`);
|
|
64
|
-
}
|
|
61
|
+
for (const img of plan.images) emitImage(out, img, laid, idc);
|
|
65
62
|
for (const b of plan.borders) {
|
|
66
63
|
const x2 = b.x + b.width;
|
|
67
64
|
const yTop = b.y;
|
|
@@ -90,8 +87,59 @@ function emitPage(out, page, laid, losses, idc) {
|
|
|
90
87
|
out.push(`<line x1="${fmt(ax)}" y1="${fmt(ay)}" x2="${fmt(bx)}" y2="${fmt(by)}" stroke="#${b.borderColorHex}" stroke-width="${fmt(b.borderSizePt)}"${b.borderStyle === "dashed" ? " stroke-dasharray=\"8 2.5\"" : b.borderStyle === "dashSmallGap" ? " stroke-dasharray=\"3 1\"" : b.borderStyle === "dotted" ? " stroke-dasharray=\"0.5 1\"" : ""}/>`);
|
|
91
88
|
}
|
|
92
89
|
for (const sh of plan.shapes) emitShape(out, sh.shape, idc);
|
|
90
|
+
for (const picture of plan.pictures) for (const item of picture) emitPageItem(out, item, laid, losses, idc);
|
|
93
91
|
for (const t of plan.lines) emitTextLine(out, t, losses, idc);
|
|
94
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* One picture. §20.1.8.23 `a:duotone` recolours it between two colours — its
|
|
95
|
+
* dark end and its light end — which is a luminance-to-two-tone map, and SVG
|
|
96
|
+
* states exactly that: luminance into every channel, then a two-entry transfer
|
|
97
|
+
* table per channel.
|
|
98
|
+
*
|
|
99
|
+
* @param out The SVG fragment sink.
|
|
100
|
+
* @param img The image item.
|
|
101
|
+
* @param laid The laid-out document, for the image's data URI.
|
|
102
|
+
* @param idc The shared id counter, for the filter's id.
|
|
103
|
+
*/
|
|
104
|
+
function emitImage(out, img, laid, idc) {
|
|
105
|
+
const href = imageHref(img.imageResourceName, laid);
|
|
106
|
+
if (!href) return;
|
|
107
|
+
let filter = "";
|
|
108
|
+
if (img.duotone) {
|
|
109
|
+
const id = `duo${String(idc.n++)}`;
|
|
110
|
+
const chan = (hex, at) => parseInt(hex.slice(at, at + 2), 16) / 255;
|
|
111
|
+
const table = (at) => `${fmt(chan(img.duotone.shadowHex, at))} ${fmt(chan(img.duotone.highlightHex, at))}`;
|
|
112
|
+
out.push(`<filter id="${id}" color-interpolation-filters="sRGB"><feColorMatrix type="matrix" values="0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0 0 0 1 0"/><feComponentTransfer><feFuncR type="table" tableValues="${table(0)}"/><feFuncG type="table" tableValues="${table(2)}"/><feFuncB type="table" tableValues="${table(4)}"/></feComponentTransfer></filter>`);
|
|
113
|
+
filter = ` filter="url(#${id})"`;
|
|
114
|
+
}
|
|
115
|
+
out.push(`<image x="${fmt(img.x)}" y="${fmt(img.y)}" width="${fmt(img.width)}" height="${fmt(img.height)}" href="${href}" preserveAspectRatio="none"${filter}/>`);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* One page item, whatever its kind — for the runs that paint in their own order
|
|
119
|
+
* (what stands behind the content, and a picture's own primitives) rather than
|
|
120
|
+
* in the by-kind passes.
|
|
121
|
+
*
|
|
122
|
+
* @param out The SVG fragment sink.
|
|
123
|
+
* @param item The item to draw.
|
|
124
|
+
* @param laid The laid-out document, for image resources.
|
|
125
|
+
* @param losses Where an unrenderable item records itself.
|
|
126
|
+
* @param idc The shared id counter for generated defs.
|
|
127
|
+
*/
|
|
128
|
+
function emitPageItem(out, item, laid, losses, idc) {
|
|
129
|
+
if (item.type === "shape") {
|
|
130
|
+
emitShape(out, item.shape, idc);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (item.type === "line") {
|
|
134
|
+
emitTextLine(out, item, losses, idc);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (item.type === "image") {
|
|
138
|
+
emitImage(out, item, laid, idc);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (item.type === "fill") out.push(`<rect x="${fmt(item.x)}" y="${fmt(item.y)}" width="${fmt(item.width)}" height="${fmt(item.height)}" fill="#${item.fillColorHex}"/>`);
|
|
142
|
+
}
|
|
95
143
|
function emitTextLine(out, item, losses, idc) {
|
|
96
144
|
const clip = item.clip;
|
|
97
145
|
if (clip) {
|
|
@@ -6,7 +6,7 @@ import "../core/style-cascade/index.js";
|
|
|
6
6
|
import { applyNumbering, applyNumberingToHeadersFooters } from "../core/numbering/apply.js";
|
|
7
7
|
import "../core/numbering/index.js";
|
|
8
8
|
import { poFindDescendant } from "../core/po-helpers.js";
|
|
9
|
-
import {
|
|
9
|
+
import { packageHasPart } from "../core/bytes.js";
|
|
10
10
|
import { DEFAULT_THEME_PALETTE, makeColorResolver } from "../core/drawingml/colors.js";
|
|
11
11
|
import { parseChart, withChartColorStyle } from "../core/drawingml/chart-parser.js";
|
|
12
12
|
import { parseTheme, parseThemeBgFillStyles, parseThemeEffectStyles, parseThemeFillStyles, parseThemeLineWidths } from "../core/drawingml/theme-parser.js";
|
|
@@ -166,7 +166,7 @@ var docxReader = {
|
|
|
166
166
|
FEATURES.trackedChanges,
|
|
167
167
|
FEATURES.fontsEmbedding
|
|
168
168
|
]),
|
|
169
|
-
sniff: (bytes) => bytes[0] === 80 && bytes[1] === 75 && (
|
|
169
|
+
sniff: (bytes) => bytes[0] === 80 && bytes[1] === 75 && (packageHasPart(bytes, "word/document.xml") || packageHasPart(bytes, "word/styles.xml") || packageHasPart(bytes, "word/_rels/")),
|
|
170
170
|
read: (bytes) => readDocx(bytes)
|
|
171
171
|
};
|
|
172
172
|
function infoFromCore(core) {
|
|
@@ -157,6 +157,26 @@ export declare function parseVmlPicture(node: PoNode, parseBody?: ParseBody): Dr
|
|
|
157
157
|
* @returns The fill.
|
|
158
158
|
*/
|
|
159
159
|
export declare function vmlFill(shape: PoNode, shapeType?: PoNode, resolveImage?: (id: string) => ResourceId | undefined): ShapeFill;
|
|
160
|
+
/**
|
|
161
|
+
* Whether an `spPr` states a fill AT ALL — including `a:noFill`, which is a
|
|
162
|
+
* shape saying it has none rather than saying nothing. A placeholder that says
|
|
163
|
+
* nothing inherits its prototype's.
|
|
164
|
+
*
|
|
165
|
+
* @param spPr The shape properties, or undefined.
|
|
166
|
+
* @returns Whether a fill element is present.
|
|
167
|
+
*/
|
|
168
|
+
export declare function statesFill(spPr: PoNode | undefined): boolean;
|
|
169
|
+
export declare function styleRefFill(style: PoNode, resolveColor: ColorResolver, themeStyles?: ThemeStyles): ShapeFill;
|
|
170
|
+
export declare function styleRefLine(style: PoNode, resolveColor: ColorResolver, themeLineWidths?: ReadonlyArray<number>): ShapeLine | undefined;
|
|
171
|
+
/**
|
|
172
|
+
* §20.1.4.2.14 `a:fontRef` — the colour a gallery style writes its text in.
|
|
173
|
+
*
|
|
174
|
+
* @param style The shape's `p:style`/`wps:style` node.
|
|
175
|
+
* @param resolveColor The document's colour resolver.
|
|
176
|
+
* @returns The 6-hex colour, or `undefined` when the style names none.
|
|
177
|
+
*/
|
|
178
|
+
export declare function styleRefFontColor(style: PoNode, resolveColor: ColorResolver): string | undefined;
|
|
179
|
+
export declare function withStyleFontColor(text: ShapeTextBody, style: PoNode, resolveColor: ColorResolver): ShapeTextBody;
|
|
160
180
|
/**
|
|
161
181
|
* §20.1.7.6 `a:xfrm` — how a shape sits in its box: rotated about its centre by
|
|
162
182
|
* `@rot` (sixtieth-thousandths of a degree) and mirrored by `@flipH`/`@flipV`.
|
|
@@ -190,6 +210,20 @@ export declare function parseCustGeom(cust: PoNode): ShapeGeometry;
|
|
|
190
210
|
* @param resolveColor Resolver for theme/scheme colours.
|
|
191
211
|
*/
|
|
192
212
|
export declare function parseFill(spPr: PoNode, resolveColor: ColorResolver, resolveImage?: (relId: string) => ResourceId | undefined): ShapeFill;
|
|
213
|
+
/**
|
|
214
|
+
* §20.1.8.16 `a:clrChange` — the colour a picture declares away, and what it
|
|
215
|
+
* becomes. `useA` (default true) says the destination's alpha counts, so a
|
|
216
|
+
* destination at zero alpha knocks the colour OUT rather than replacing it.
|
|
217
|
+
*
|
|
218
|
+
* @param blip The `a:blip` node.
|
|
219
|
+
* @param resolveColor The colour resolver.
|
|
220
|
+
* @returns The change, or `undefined` when the blip declares none.
|
|
221
|
+
*/
|
|
222
|
+
export declare function colorChangeOf(blip: PoNode, resolveColor: ColorResolver): {
|
|
223
|
+
readonly fromHex: string;
|
|
224
|
+
readonly toHex: string;
|
|
225
|
+
readonly transparent: boolean;
|
|
226
|
+
} | undefined;
|
|
193
227
|
/**
|
|
194
228
|
* Parse a shape's drop shadow from its `a:spPr` (§20.1.8.40 `a:effectLst` →
|
|
195
229
|
* `a:outerShdw`).
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { emuToPt, pt } from "../core/ir/units.js";
|
|
2
2
|
import { buildStroke } from "../core/drawingml/shape-render.js";
|
|
3
3
|
import { poAttr, poAttrLocal, poChildren, poFindDescendant, poIntAttr, poIs, poIsLocal, poTag, poText } from "../core/po-helpers.js";
|
|
4
|
-
import {
|
|
4
|
+
import { placeholderColors, readColorMods, resolveColorNode } from "../core/drawingml/colors.js";
|
|
5
5
|
//#region src/word/drawing-parser.ts
|
|
6
6
|
var WPS_URI = "http://schemas.microsoft.com/office/word/2010/wordprocessingShape";
|
|
7
7
|
var DRAWINGML_URI_BASES = ["http://schemas.openxmlformats.org/drawingml/2006/", "http://purl.oclc.org/ooxml/drawingml/"];
|
|
@@ -1524,9 +1524,6 @@ function styleRefShadow(style, resolveColor, themeEffectStyles) {
|
|
|
1524
1524
|
const phHex = child ? resolveColorNode(child, resolveColor) : void 0;
|
|
1525
1525
|
return shadowFromOuterShdw(shdw, phHex ? placeholderColors(resolveColor, phHex) : resolveColor);
|
|
1526
1526
|
}
|
|
1527
|
-
function placeholderColors(base, phHex) {
|
|
1528
|
-
return (raw) => "scheme" in raw && raw.scheme === "phClr" ? applyColorMods(phHex, raw.mods ?? []) : base(raw);
|
|
1529
|
-
}
|
|
1530
1527
|
var FILL_TAGS = new Set([
|
|
1531
1528
|
"a:noFill",
|
|
1532
1529
|
"a:solidFill",
|
|
@@ -1535,6 +1532,14 @@ var FILL_TAGS = new Set([
|
|
|
1535
1532
|
"a:pattFill",
|
|
1536
1533
|
"a:grpFill"
|
|
1537
1534
|
]);
|
|
1535
|
+
/**
|
|
1536
|
+
* Whether an `spPr` states a fill AT ALL — including `a:noFill`, which is a
|
|
1537
|
+
* shape saying it has none rather than saying nothing. A placeholder that says
|
|
1538
|
+
* nothing inherits its prototype's.
|
|
1539
|
+
*
|
|
1540
|
+
* @param spPr The shape properties, or undefined.
|
|
1541
|
+
* @returns Whether a fill element is present.
|
|
1542
|
+
*/
|
|
1538
1543
|
function statesFill(spPr) {
|
|
1539
1544
|
return spPr !== void 0 && poChildren(spPr).some((c) => FILL_TAGS.has(poTag(c) ?? ""));
|
|
1540
1545
|
}
|
|
@@ -1567,9 +1572,19 @@ function styleRefLine(style, resolveColor, themeLineWidths) {
|
|
|
1567
1572
|
width: pt((idx !== void 0 && idx > 0 ? themeLineWidths?.[idx - 1] : void 0) ?? .75)
|
|
1568
1573
|
};
|
|
1569
1574
|
}
|
|
1570
|
-
|
|
1575
|
+
/**
|
|
1576
|
+
* §20.1.4.2.14 `a:fontRef` — the colour a gallery style writes its text in.
|
|
1577
|
+
*
|
|
1578
|
+
* @param style The shape's `p:style`/`wps:style` node.
|
|
1579
|
+
* @param resolveColor The document's colour resolver.
|
|
1580
|
+
* @returns The 6-hex colour, or `undefined` when the style names none.
|
|
1581
|
+
*/
|
|
1582
|
+
function styleRefFontColor(style, resolveColor) {
|
|
1571
1583
|
const child = firstElementChild(poChildren(style).find((c) => poIs(c, "a:fontRef")));
|
|
1572
|
-
|
|
1584
|
+
return child ? resolveColorNode(child, resolveColor) : void 0;
|
|
1585
|
+
}
|
|
1586
|
+
function withStyleFontColor(text, style, resolveColor) {
|
|
1587
|
+
const colorHex = styleRefFontColor(style, resolveColor);
|
|
1573
1588
|
if (colorHex === void 0) return text;
|
|
1574
1589
|
return {
|
|
1575
1590
|
...text,
|
|
@@ -1801,11 +1816,20 @@ function fillFromNode(child, resolveColor, resolveImage) {
|
|
|
1801
1816
|
const resource = relId !== void 0 ? resolveImage?.(relId) : void 0;
|
|
1802
1817
|
if (resource === void 0) return { kind: "none" };
|
|
1803
1818
|
const crop = parseSrcRect(poFindDescendant(child, "a:srcRect")) ?? fillRectCrop(child);
|
|
1819
|
+
const rect = fillRectBox(child);
|
|
1820
|
+
const duotone = blip ? duotoneOf(blip, resolveColor) : void 0;
|
|
1821
|
+
const tiled = poChildren(child).some((c) => poIs(c, "a:tile"));
|
|
1822
|
+
const fixed = blip ? poChildren(blip).find((c) => poIs(c, "a:alphaModFix")) : void 0;
|
|
1823
|
+
const amt = fixed ? poIntAttr(fixed, "amt") : void 0;
|
|
1824
|
+
const alpha = amt === void 0 ? void 0 : Math.min(1, Math.max(0, amt / 1e5));
|
|
1804
1825
|
return {
|
|
1805
1826
|
kind: "picture",
|
|
1806
1827
|
imageResource: resource,
|
|
1807
|
-
...
|
|
1808
|
-
...crop ? { imageCrop: crop } : {}
|
|
1828
|
+
...tiled ? { tiled: true } : {},
|
|
1829
|
+
...crop ? { imageCrop: crop } : {},
|
|
1830
|
+
...rect ? { imageFillRect: rect } : {},
|
|
1831
|
+
...duotone ? { duotone } : {},
|
|
1832
|
+
...alpha !== void 0 && alpha < 1 ? { alpha } : {}
|
|
1809
1833
|
};
|
|
1810
1834
|
}
|
|
1811
1835
|
if (poIs(child, "a:solidFill")) {
|
|
@@ -1830,10 +1854,13 @@ function fillFromNode(child, resolveColor, resolveImage) {
|
|
|
1830
1854
|
}
|
|
1831
1855
|
if (poIs(child, "a:gradFill")) {
|
|
1832
1856
|
const gradient = parseGradient(child, resolveColor);
|
|
1833
|
-
|
|
1857
|
+
if (!gradient) return { kind: "none" };
|
|
1858
|
+
const alpha = gradientAlpha(gradient.stops);
|
|
1859
|
+
return {
|
|
1834
1860
|
kind: "gradient",
|
|
1835
|
-
gradient
|
|
1836
|
-
|
|
1861
|
+
gradient,
|
|
1862
|
+
...alpha !== void 0 ? { alpha } : {}
|
|
1863
|
+
};
|
|
1837
1864
|
}
|
|
1838
1865
|
}
|
|
1839
1866
|
/**
|
|
@@ -1907,6 +1934,90 @@ function fillRectCrop(blipFill) {
|
|
|
1907
1934
|
};
|
|
1908
1935
|
}
|
|
1909
1936
|
/**
|
|
1937
|
+
* §20.1.8.16 `a:clrChange` — the colour a picture declares away, and what it
|
|
1938
|
+
* becomes. `useA` (default true) says the destination's alpha counts, so a
|
|
1939
|
+
* destination at zero alpha knocks the colour OUT rather than replacing it.
|
|
1940
|
+
*
|
|
1941
|
+
* @param blip The `a:blip` node.
|
|
1942
|
+
* @param resolveColor The colour resolver.
|
|
1943
|
+
* @returns The change, or `undefined` when the blip declares none.
|
|
1944
|
+
*/
|
|
1945
|
+
function colorChangeOf(blip, resolveColor) {
|
|
1946
|
+
const change = poChildren(blip).find((c) => poIs(c, "a:clrChange"));
|
|
1947
|
+
if (!change) return void 0;
|
|
1948
|
+
const side = (tag) => poChildren(change).find((c) => poIs(c, tag));
|
|
1949
|
+
const from = side("a:clrFrom");
|
|
1950
|
+
const to = side("a:clrTo");
|
|
1951
|
+
const inner = (holder) => holder ? poChildren(holder).find((c) => poTag(c) !== void 0) : void 0;
|
|
1952
|
+
const fromNode = inner(from);
|
|
1953
|
+
const toNode = inner(to);
|
|
1954
|
+
const fromHex = fromNode ? resolveColorNode(fromNode, resolveColor) : void 0;
|
|
1955
|
+
if (fromHex === void 0) return void 0;
|
|
1956
|
+
const toHex = (toNode ? resolveColorNode(toNode, resolveColor) : void 0) ?? fromHex;
|
|
1957
|
+
const useA = poAttr(change, "useA") !== "0";
|
|
1958
|
+
const alpha = toNode ? nodeAlpha(toNode) : void 0;
|
|
1959
|
+
return {
|
|
1960
|
+
fromHex,
|
|
1961
|
+
toHex,
|
|
1962
|
+
transparent: useA && alpha !== void 0 && alpha <= .001
|
|
1963
|
+
};
|
|
1964
|
+
}
|
|
1965
|
+
/** §20.1.2.3.1 — a colour node's own `a:alpha`, as a fraction. */
|
|
1966
|
+
function nodeAlpha(color) {
|
|
1967
|
+
const alpha = poChildren(color).find((c) => poIs(c, "a:alpha"));
|
|
1968
|
+
const val = alpha ? poIntAttr(alpha, "val") : void 0;
|
|
1969
|
+
return val === void 0 ? void 0 : val / 1e5;
|
|
1970
|
+
}
|
|
1971
|
+
/**
|
|
1972
|
+
* §20.1.8.23 `a:duotone` — the two colours a picture is recoloured between,
|
|
1973
|
+
* dark end first. Both are ordinary colour containers, so a theme's `phClr`
|
|
1974
|
+
* resolves through whatever resolver the caller bound.
|
|
1975
|
+
*
|
|
1976
|
+
* @param blip The `a:blip` node.
|
|
1977
|
+
* @param resolveColor The colour resolver.
|
|
1978
|
+
* @returns The pair, or `undefined` when the blip states no duotone.
|
|
1979
|
+
*/
|
|
1980
|
+
function duotoneOf(blip, resolveColor) {
|
|
1981
|
+
const duotone = poChildren(blip).find((c) => poIs(c, "a:duotone"));
|
|
1982
|
+
if (!duotone) return void 0;
|
|
1983
|
+
const [shadowHex, highlightHex] = poChildren(duotone).map((c) => resolveColorNode(c, resolveColor)).filter((hex) => hex !== void 0);
|
|
1984
|
+
return shadowHex !== void 0 && highlightHex !== void 0 ? {
|
|
1985
|
+
shadowHex,
|
|
1986
|
+
highlightHex
|
|
1987
|
+
} : void 0;
|
|
1988
|
+
}
|
|
1989
|
+
/**
|
|
1990
|
+
* §20.1.8.30 `a:stretch/a:fillRect` with POSITIVE insets — the part of the box
|
|
1991
|
+
* the picture is stretched INTO. The negative case is the zoom {@link
|
|
1992
|
+
* fillRectCrop} reads; this is the other one, and unread it drew a background
|
|
1993
|
+
* picture inset into the corner of a slide across the whole of it
|
|
1994
|
+
* (tdf153466.pptx: a triangle five times its size).
|
|
1995
|
+
*
|
|
1996
|
+
* @param blipFill The `a:blipFill` node.
|
|
1997
|
+
* @returns The rect as fractions of the box, or `undefined` when every inset
|
|
1998
|
+
* is zero or negative.
|
|
1999
|
+
*/
|
|
2000
|
+
function fillRectBox(blipFill) {
|
|
2001
|
+
const stretch = poChildren(blipFill).find((c) => poIs(c, "a:stretch"));
|
|
2002
|
+
const rect = stretch ? poChildren(stretch).find((c) => poIs(c, "a:fillRect")) : void 0;
|
|
2003
|
+
if (!rect) return void 0;
|
|
2004
|
+
const side = (name) => Math.max(0, (poIntAttr(rect, name) ?? 0) / 1e5);
|
|
2005
|
+
const [left, top, right, bottom] = [
|
|
2006
|
+
side("l"),
|
|
2007
|
+
side("t"),
|
|
2008
|
+
side("r"),
|
|
2009
|
+
side("b")
|
|
2010
|
+
];
|
|
2011
|
+
if (left + top + right + bottom === 0) return void 0;
|
|
2012
|
+
if (left + right >= 1 || top + bottom >= 1) return void 0;
|
|
2013
|
+
return {
|
|
2014
|
+
left,
|
|
2015
|
+
top,
|
|
2016
|
+
right,
|
|
2017
|
+
bottom
|
|
2018
|
+
};
|
|
2019
|
+
}
|
|
2020
|
+
/**
|
|
1910
2021
|
* Parse a shape's drop shadow from its `a:spPr` (§20.1.8.40 `a:effectLst` →
|
|
1911
2022
|
* `a:outerShdw`).
|
|
1912
2023
|
*
|
|
@@ -2089,6 +2200,38 @@ function colorFromContainer(parent, resolveColor) {
|
|
|
2089
2200
|
if (hex) return hex;
|
|
2090
2201
|
}
|
|
2091
2202
|
}
|
|
2203
|
+
/**
|
|
2204
|
+
* §20.1.2.3.1 — a gradient's stop transparencies, settled.
|
|
2205
|
+
*
|
|
2206
|
+
* A page paints a gradient at ONE transparency, so only a gradient whose every
|
|
2207
|
+
* stop is translucent can carry it: the strongest stop decides whether the
|
|
2208
|
+
* shape is there at all, which is the question a 7%-and-fading glow asks
|
|
2209
|
+
* (tdf123684's master draws one over a dark slide, and composited over the
|
|
2210
|
+
* paper instead it was an opaque white disc).
|
|
2211
|
+
*
|
|
2212
|
+
* A gradient that mixes a translucent stop with an opaque one has no such
|
|
2213
|
+
* answer, so it keeps the colours the resolver already washed toward the paper
|
|
2214
|
+
* — an approximation, but the one that has always been drawn: smartart-simple's
|
|
2215
|
+
* cyan-to-purple sweep sets 75% on its purple end alone.
|
|
2216
|
+
*/
|
|
2217
|
+
function normalizeStopAlpha(stops) {
|
|
2218
|
+
if (gradientAlpha(stops) === void 0) {
|
|
2219
|
+
for (const [i, s] of stops.entries()) if (s.alpha !== void 0) stops[i] = {
|
|
2220
|
+
offset: s.offset,
|
|
2221
|
+
colorHex: s.colorHex
|
|
2222
|
+
};
|
|
2223
|
+
return;
|
|
2224
|
+
}
|
|
2225
|
+
for (const [i, s] of stops.entries()) stops[i] = {
|
|
2226
|
+
...s,
|
|
2227
|
+
colorHex: unblendWhite(s.colorHex, s.alpha ?? 1)
|
|
2228
|
+
};
|
|
2229
|
+
}
|
|
2230
|
+
/** The one transparency a gradient can be drawn at; see {@link normalizeStopAlpha}. */
|
|
2231
|
+
function gradientAlpha(stops) {
|
|
2232
|
+
const strongest = Math.max(...stops.map((s) => s.alpha ?? 1));
|
|
2233
|
+
return strongest < 1 ? strongest : void 0;
|
|
2234
|
+
}
|
|
2092
2235
|
function parseGradient(grad, resolveColor) {
|
|
2093
2236
|
const gsLst = poChildren(grad).find((c) => poIs(c, "a:gsLst"));
|
|
2094
2237
|
if (!gsLst) return void 0;
|
|
@@ -2103,13 +2246,16 @@ function parseGradient(grad, resolveColor) {
|
|
|
2103
2246
|
if (!hex) continue;
|
|
2104
2247
|
const pos = poIntAttr(gs, "pos");
|
|
2105
2248
|
const offset = pos !== void 0 ? clampUnit(pos / 1e5) : stops.length === 0 ? 0 : 1;
|
|
2249
|
+
const alpha = containerAlpha(gs);
|
|
2106
2250
|
stops.push({
|
|
2107
2251
|
offset,
|
|
2108
|
-
colorHex: hex
|
|
2252
|
+
colorHex: hex,
|
|
2253
|
+
...alpha !== void 0 ? { alpha } : {}
|
|
2109
2254
|
});
|
|
2110
2255
|
}
|
|
2111
2256
|
if (stops.length === 0) return void 0;
|
|
2112
2257
|
stops.sort((a, b) => a.offset - b.offset);
|
|
2258
|
+
normalizeStopAlpha(stops);
|
|
2113
2259
|
const path = poChildren(grad).find((c) => poIs(c, "a:path"));
|
|
2114
2260
|
if (path) {
|
|
2115
2261
|
const rect = poChildren(path).find((c) => poIs(c, "a:fillToRect"));
|
|
@@ -2144,4 +2290,4 @@ function clampUnit(x) {
|
|
|
2144
2290
|
return x < 0 ? 0 : x > 1 ? 1 : x;
|
|
2145
2291
|
}
|
|
2146
2292
|
//#endregion
|
|
2147
|
-
export { expandMcChildren, parseCustGeom, parseDrawing, parseFill, parseLine, parsePrstGeom, parseShadow, parseVmlPicture, parseXfrm, shadowFromOuterShdw, vmlFill };
|
|
2293
|
+
export { colorChangeOf, expandMcChildren, parseCustGeom, parseDrawing, parseFill, parseLine, parsePrstGeom, parseShadow, parseVmlPicture, parseXfrm, shadowFromOuterShdw, statesFill, styleRefFill, styleRefFontColor, styleRefLine, vmlFill };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reamkit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.22.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>",
|