reamkit 1.15.4 → 1.16.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/converter/facade.d.ts +5 -0
- package/dist/esm/core/converter/ream.d.ts +6 -0
- package/dist/esm/core/converter/ream.js +34 -8
- package/dist/esm/core/document-model/index.d.ts +1 -1
- package/dist/esm/core/document-model/types.d.ts +187 -1
- package/dist/esm/core/drawingml/chart-geometry.d.ts +28 -0
- package/dist/esm/core/drawingml/chart-geometry.js +371 -88
- package/dist/esm/core/drawingml/chart-parser.js +262 -11
- package/dist/esm/core/drawingml/colors.d.ts +6 -6
- package/dist/esm/core/drawingml/colors.js +58 -18
- package/dist/esm/core/drawingml/preset-geometry.js +7 -6
- package/dist/esm/core/drawingml/sparkline-geometry.js +6 -4
- package/dist/esm/core/drawingml/theme-parser.d.ts +41 -0
- package/dist/esm/core/drawingml/theme-parser.js +74 -1
- package/dist/esm/core/font/ttf-subset.js +26 -1
- package/dist/esm/core/images.js +172 -43
- package/dist/esm/core/indexed-colors.d.ts +11 -0
- package/dist/esm/core/indexed-colors.js +81 -0
- package/dist/esm/core/ir/sheet.d.ts +61 -0
- package/dist/esm/{excel → core}/number-format.d.ts +37 -0
- package/dist/esm/core/number-format.js +800 -0
- package/dist/esm/core/opc/alternate-content.d.ts +13 -0
- package/dist/esm/core/opc/alternate-content.js +72 -0
- package/dist/esm/core/opc/core-properties.js +1 -0
- package/dist/esm/core/opc/package.d.ts +14 -1
- package/dist/esm/core/opc/package.js +43 -4
- package/dist/esm/core/opc/relationships.js +1 -0
- package/dist/esm/core/opc/xml-entities.d.ts +12 -0
- package/dist/esm/core/opc/xml-entities.js +91 -0
- package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
- package/dist/esm/core/spreadsheet-model/types.d.ts +137 -0
- package/dist/esm/core/vector.d.ts +13 -0
- package/dist/esm/excel/activex-parser.js +1 -0
- package/dist/esm/excel/column-bands.d.ts +26 -1
- package/dist/esm/excel/column-bands.js +156 -20
- package/dist/esm/excel/comments-parser.js +5 -3
- package/dist/esm/excel/conditional-format.d.ts +25 -2
- package/dist/esm/excel/conditional-format.js +63 -19
- package/dist/esm/excel/escaped-text.d.ts +9 -0
- package/dist/esm/excel/escaped-text.js +20 -0
- package/dist/esm/excel/form-control-parser.js +1 -0
- package/dist/esm/excel/formula/dates.js +1 -1
- package/dist/esm/excel/header-footer.d.ts +9 -1
- package/dist/esm/excel/header-footer.js +162 -14
- package/dist/esm/excel/index.d.ts +1 -1
- package/dist/esm/excel/index.js +2 -2
- package/dist/esm/excel/pivot-table-parser.js +1 -0
- package/dist/esm/excel/print-model.d.ts +75 -0
- package/dist/esm/excel/print-model.js +898 -76
- package/dist/esm/excel/printer-settings.d.ts +16 -0
- package/dist/esm/excel/printer-settings.js +36 -0
- package/dist/esm/excel/rich-value.d.ts +13 -0
- package/dist/esm/excel/rich-value.js +120 -0
- package/dist/esm/excel/shared-strings-parser.js +11 -7
- package/dist/esm/excel/sheet-drawing.d.ts +11 -0
- package/dist/esm/excel/sheet-drawing.js +26 -3
- package/dist/esm/excel/sheet-shape-parser.d.ts +53 -1
- package/dist/esm/excel/sheet-shape-parser.js +461 -32
- package/dist/esm/excel/sheet-to-flow.d.ts +23 -2
- package/dist/esm/excel/sheet-to-flow.js +551 -36
- package/dist/esm/excel/slicer-parser.js +1 -0
- package/dist/esm/excel/styles-parser.d.ts +30 -2
- package/dist/esm/excel/styles-parser.js +177 -40
- package/dist/esm/excel/table-parser.js +1 -0
- package/dist/esm/excel/vml-drawing.d.ts +68 -0
- package/dist/esm/excel/vml-drawing.js +158 -0
- package/dist/esm/excel/workbook-parser.d.ts +5 -0
- package/dist/esm/excel/workbook-parser.js +5 -1
- package/dist/esm/excel/worksheet-parser.d.ts +2 -1
- package/dist/esm/excel/worksheet-parser.js +150 -61
- package/dist/esm/excel/xlsx-reader.js +242 -11
- package/dist/esm/excel/xlsx-to-pdf.js +12 -1
- package/dist/esm/html/html-writer.js +11 -2
- package/dist/esm/layout/page-doc.d.ts +26 -0
- package/dist/esm/layout/styled-layout.d.ts +2 -0
- package/dist/esm/layout/styled-layout.js +493 -64
- package/dist/esm/pdf/cid-font.js +20 -9
- package/dist/esm/pdf/objects.d.ts +13 -0
- package/dist/esm/pdf/objects.js +18 -1
- package/dist/esm/pdf/styled-page-emitter.js +164 -32
- package/dist/esm/pdf/text-page-renderer.js +1 -0
- package/dist/esm/pdf/vector-graphics.d.ts +3 -1
- package/dist/esm/pdf/vector-graphics.js +19 -2
- package/dist/esm/pptx/pptx-reader.js +1 -0
- package/dist/esm/pptx/slide-parser.js +12 -1
- package/dist/esm/svg/svg-writer.js +23 -4
- package/dist/esm/word/document-parser.js +1 -0
- package/dist/esm/word/drawing-parser.d.ts +34 -1
- package/dist/esm/word/drawing-parser.js +52 -2
- package/dist/esm/word/numbering-parser.js +1 -0
- package/dist/esm/word/settings-parser.js +1 -0
- package/dist/esm/word/styles-parser.js +1 -0
- package/package.json +7 -3
- package/dist/esm/excel/number-format.js +0 -476
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { emuToPt, pt } from "../core/ir/units.js";
|
|
2
|
-
import { poChildren, poFirstChild, poIntAttr, poIs, poText } from "../core/po-helpers.js";
|
|
3
|
-
import {
|
|
2
|
+
import { poAttr, poChildren, poFirstChild, poIntAttr, poIs, poTag, poText } from "../core/po-helpers.js";
|
|
3
|
+
import { applyColorMods, resolveColorNode } from "../core/drawingml/colors.js";
|
|
4
|
+
import { parseFill, parseLine, parseShadow, parseXfrm, shadowFromOuterShdw } from "../word/drawing-parser.js";
|
|
4
5
|
import { parseGeometry, parseTxBody } from "../pptx/slide-parser.js";
|
|
5
6
|
import { parseXml } from "../pptx/pptx-reader.js";
|
|
6
7
|
import { makeColWidthPt, makeRowHeightPt } from "./sheet-drawing.js";
|
|
@@ -11,6 +12,65 @@ var ANCHOR_KINDS = [
|
|
|
11
12
|
"xdr:absoluteAnchor"
|
|
12
13
|
];
|
|
13
14
|
/**
|
|
15
|
+
* One `xdr:sp` / `dsp:sp` as a {@link ShapeBlock} in the box it occupies, or
|
|
16
|
+
* undefined when it would draw nothing at all.
|
|
17
|
+
*
|
|
18
|
+
* @param sp The shape element.
|
|
19
|
+
* @param box The box it occupies on the page.
|
|
20
|
+
* @param ns Which namespace its own children carry.
|
|
21
|
+
* @param colors The theme colour resolver.
|
|
22
|
+
* @param themeLineWidths `a:lnStyleLst` widths, indexed by `<a:lnRef idx>`.
|
|
23
|
+
* @param themeFillStyles `a:fillStyleLst` nodes, indexed by `<a:fillRef idx>`.
|
|
24
|
+
* @param themeEffectStyles `a:effectStyleLst` nodes, indexed by `<a:effectRef idx>`.
|
|
25
|
+
*/
|
|
26
|
+
function buildShape(sp, box, ns, colors, themeLineWidths, themeFillStyles, themeEffectStyles) {
|
|
27
|
+
const spPr = poChildren(sp).find((c) => poIs(c, `${ns}:spPr`));
|
|
28
|
+
const txBody = poChildren(sp).find((c) => poIs(c, `${ns}:txBody`));
|
|
29
|
+
const geometry = parseGeometry(spPr);
|
|
30
|
+
const parsed = txBody ? parseTxBody(txBody, void 0, void 0, colors, void 0) : void 0;
|
|
31
|
+
const lettered = (parsed?.content ?? []).some((b) => b.kind === "paragraph" && b.paragraph.runs.some((r) => r.text.trim().length > 0));
|
|
32
|
+
const xfrm = spPr ? poChildren(spPr).find((c) => poIs(c, "a:xfrm")) : void 0;
|
|
33
|
+
const transform = xfrm ? parseXfrm(xfrm) : void 0;
|
|
34
|
+
const own = transform !== void 0 && Object.keys(transform).length > 0 && xfrm && !lettered ? poChildren(xfrm).find((c) => poIs(c, "a:ext")) : void 0;
|
|
35
|
+
const ownW = own ? emuToPt(poIntAttr(own, "cx") ?? 0) : 0;
|
|
36
|
+
const ownH = own ? emuToPt(poIntAttr(own, "cy") ?? 0) : 0;
|
|
37
|
+
const useOwn = ownW > 0 && ownH > 0;
|
|
38
|
+
const widthPt = useOwn ? ownW : box.widthPt;
|
|
39
|
+
const heightPt = useOwn ? ownH : box.heightPt;
|
|
40
|
+
const xPt = useOwn ? box.xPt + (box.widthPt - ownW) / 2 : box.xPt;
|
|
41
|
+
const yPt = useOwn ? box.yPt + (box.heightPt - ownH) / 2 : box.yPt;
|
|
42
|
+
const spFill = spPr ? parseFill(spPr, colors) : { kind: "none" };
|
|
43
|
+
const fill = spFill.kind === "none" ? styleFill(sp, ns, colors, themeFillStyles) : spFill;
|
|
44
|
+
const directLine = spPr ? parseLine(spPr, colors) : void 0;
|
|
45
|
+
const line = directLine && Object.keys(directLine).length > 0 ? directLine : styleLine(sp, ns, colors, themeLineWidths);
|
|
46
|
+
const text = parsed ? withStyleTextColor(parsed, sp, ns, colors) : void 0;
|
|
47
|
+
const shadow = (spPr ? parseShadow(spPr, colors) : void 0) ?? styleShadow(sp, ns, colors, themeEffectStyles);
|
|
48
|
+
const visibleLine = line !== void 0 && line.fill !== "none";
|
|
49
|
+
if (!text && fill.kind === "none" && !visibleLine) return void 0;
|
|
50
|
+
return {
|
|
51
|
+
float: {
|
|
52
|
+
wrap: "none",
|
|
53
|
+
posH: {
|
|
54
|
+
relativeFrom: "margin",
|
|
55
|
+
offsetPt: pt(xPt)
|
|
56
|
+
},
|
|
57
|
+
posV: {
|
|
58
|
+
relativeFrom: "margin",
|
|
59
|
+
offsetPt: pt(yPt)
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
width: pt(widthPt),
|
|
63
|
+
height: pt(heightPt),
|
|
64
|
+
geometry,
|
|
65
|
+
fill,
|
|
66
|
+
...transform && Object.keys(transform).length > 0 ? { transform } : {},
|
|
67
|
+
...line ? { line } : {},
|
|
68
|
+
...text ? { text } : {},
|
|
69
|
+
...shadow ? { shadow } : {},
|
|
70
|
+
paragraphProperties: {}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
14
74
|
* Parse a drawing's `xdr:sp` shape anchors (§20.5.2.30) into anchor-ordered
|
|
15
75
|
* {@link ShapeBlock}s (E-SHEET W2), reusing the shared DrawingML geometry / fill /
|
|
16
76
|
* line / text readers. A shape's box comes from its sheet anchor (from/to tracks),
|
|
@@ -21,8 +81,14 @@ var ANCHOR_KINDS = [
|
|
|
21
81
|
* preserveOrder tree the shape readers expect).
|
|
22
82
|
* @param worksheet The host worksheet, for the column/row track geometry.
|
|
23
83
|
* @param colors The theme colour resolver threaded into fill/line parsing.
|
|
84
|
+
* @param themeLineWidths The theme's `a:lnStyleLst` widths in points, which an
|
|
85
|
+
* `<a:lnRef idx>` indexes for a gallery-styled outline.
|
|
86
|
+
* @param themeFillStyles The theme's `a:fillStyleLst` nodes, which an
|
|
87
|
+
* `<a:fillRef idx>` indexes for a gallery-styled fill.
|
|
88
|
+
* @param themeEffectStyles The theme's `a:effectStyleLst` nodes, which an
|
|
89
|
+
* `<a:effectRef idx>` indexes for a gallery-styled shadow.
|
|
24
90
|
*/
|
|
25
|
-
function parseSheetShapes(drawingXml, worksheet, colors) {
|
|
91
|
+
function parseSheetShapes(drawingXml, worksheet, colors, themeLineWidths = [], themeFillStyles = [], themeEffectStyles = []) {
|
|
26
92
|
const wsDr = parseXml(drawingXml).find((n) => poIs(n, "xdr:wsDr"));
|
|
27
93
|
if (!wsDr) return [];
|
|
28
94
|
const colWidthPt = makeColWidthPt(worksheet);
|
|
@@ -30,33 +96,361 @@ function parseSheetShapes(drawingXml, worksheet, colors) {
|
|
|
30
96
|
const shapes = [];
|
|
31
97
|
for (const anchor of poChildren(wsDr)) {
|
|
32
98
|
if (!ANCHOR_KINDS.some((k) => poIs(anchor, k))) continue;
|
|
33
|
-
const
|
|
34
|
-
if (!
|
|
99
|
+
const anchored = anchorBox(anchor, colWidthPt, rowHeightPt);
|
|
100
|
+
if (!anchored) continue;
|
|
101
|
+
const placed = [];
|
|
102
|
+
const direct = poChildren(anchor).find((c) => poIs(c, "xdr:sp"));
|
|
103
|
+
if (direct) placed.push({
|
|
104
|
+
sp: direct,
|
|
105
|
+
box: anchored
|
|
106
|
+
});
|
|
107
|
+
for (const group of poChildren(anchor).filter((c) => poIs(c, "xdr:grpSp"))) collectGrouped(group, anchored, placed);
|
|
108
|
+
for (const { sp, box } of placed) {
|
|
109
|
+
if (isHiddenDrawing(sp, "xdr:nvSpPr")) continue;
|
|
110
|
+
const shape = buildShape(sp, box, "xdr", colors, themeLineWidths, themeFillStyles, themeEffectStyles);
|
|
111
|
+
if (!shape) continue;
|
|
112
|
+
shapes.push({
|
|
113
|
+
shape,
|
|
114
|
+
anchorRow: box.anchorRow
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
shapes.sort((a, b) => a.anchorRow - b.anchorRow);
|
|
119
|
+
return shapes.map((s) => s.shape);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* The boxes the diagram frames of a drawing occupy, in document order.
|
|
123
|
+
*
|
|
124
|
+
* §20.5.2.16 `xdr:graphicFrame` is how a sheet hosts a diagram: the frame is
|
|
125
|
+
* anchored like any other drawing and the diagram itself lives in its own
|
|
126
|
+
* parts. The frame gives the corner its fallback shapes are laid out from —
|
|
127
|
+
* see {@link parseDiagramShapes}.
|
|
128
|
+
*
|
|
129
|
+
* @param drawingXml The drawing part bytes.
|
|
130
|
+
* @param worksheet The host worksheet, for the column/row track geometry.
|
|
131
|
+
* @returns One box per diagram frame, in the order the drawing writes them.
|
|
132
|
+
*/
|
|
133
|
+
function parseDiagramFrames(drawingXml, worksheet) {
|
|
134
|
+
const wsDr = parseXml(drawingXml).find((n) => poIs(n, "xdr:wsDr"));
|
|
135
|
+
if (!wsDr) return [];
|
|
136
|
+
const colWidthPt = makeColWidthPt(worksheet);
|
|
137
|
+
const rowHeightPt = makeRowHeightPt(worksheet);
|
|
138
|
+
const out = [];
|
|
139
|
+
for (const anchor of poChildren(wsDr)) {
|
|
140
|
+
if (!ANCHOR_KINDS.some((k) => poIs(anchor, k))) continue;
|
|
141
|
+
const frame = poChildren(anchor).find((c) => poIs(c, "xdr:graphicFrame"));
|
|
142
|
+
if (!frame) continue;
|
|
143
|
+
if (!poFirstChild(poFirstChild(poFirstChild(frame, "a:graphic"), "a:graphicData"), "dgm:relIds")) continue;
|
|
35
144
|
const box = anchorBox(anchor, colWidthPt, rowHeightPt);
|
|
145
|
+
if (box) out.push(box);
|
|
146
|
+
}
|
|
147
|
+
return out;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Parse the fallback drawing of a SmartArt diagram (§ MS-ODRAWXML 2.1
|
|
151
|
+
* `dsp:drawing`) into the shapes it lays out.
|
|
152
|
+
*
|
|
153
|
+
* A diagram is FOUR parts of description — data, layout, quick style, colours —
|
|
154
|
+
* that a renderer is meant to lay out itself, and nobody outside Office does.
|
|
155
|
+
* The producer therefore also writes what it drew: a plain DrawingML picture of
|
|
156
|
+
* the result, under `dsp:`, reachable from the drawing part by a
|
|
157
|
+
* `diagramDrawing` relationship. Reading it is the difference between a diagram
|
|
158
|
+
* and a blank space — tdf83671_SmartArt_import.xlsx draws three nested circles
|
|
159
|
+
* where we drew nothing between its "start" and its "end".
|
|
160
|
+
*
|
|
161
|
+
* The shapes' `a:xfrm` offsets are relative to the frame the diagram sits in,
|
|
162
|
+
* so the frame's own corner is all that has to be added.
|
|
163
|
+
*
|
|
164
|
+
* @param diagramXml The `dsp:drawing` part bytes.
|
|
165
|
+
* @param frame The box the graphic frame occupies on the page.
|
|
166
|
+
* @param colors The theme colour resolver.
|
|
167
|
+
* @param themeLineWidths `a:lnStyleLst` widths, indexed by `<a:lnRef idx>`.
|
|
168
|
+
* @param themeFillStyles `a:fillStyleLst` nodes, indexed by `<a:fillRef idx>`.
|
|
169
|
+
* @param themeEffectStyles `a:effectStyleLst` nodes, indexed by `<a:effectRef idx>`.
|
|
170
|
+
* @returns The shapes, in the order the diagram stacks them.
|
|
171
|
+
*/
|
|
172
|
+
function parseDiagramShapes(diagramXml, frame, colors, themeLineWidths = [], themeFillStyles = [], themeEffectStyles = []) {
|
|
173
|
+
const drawing = parseXml(diagramXml).find((n) => poIs(n, "dsp:drawing"));
|
|
174
|
+
const spTree = drawing ? poFirstChild(drawing, "dsp:spTree") : void 0;
|
|
175
|
+
if (!spTree) return [];
|
|
176
|
+
const out = [];
|
|
177
|
+
for (const sp of poChildren(spTree)) {
|
|
178
|
+
if (!poIs(sp, "dsp:sp")) continue;
|
|
179
|
+
const box = diagramBox(sp, frame);
|
|
36
180
|
if (!box) continue;
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
181
|
+
const shape = buildShape(sp, box, "dsp", colors, themeLineWidths, themeFillStyles, themeEffectStyles);
|
|
182
|
+
if (!shape) continue;
|
|
183
|
+
const text = shape.text;
|
|
184
|
+
const label = text ? textBox(sp, frame) : void 0;
|
|
185
|
+
if (!label || !text) {
|
|
186
|
+
out.push(shape);
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
const { text: _moved, ...graphic } = shape;
|
|
190
|
+
out.push(graphic);
|
|
191
|
+
out.push({
|
|
192
|
+
float: {
|
|
193
|
+
wrap: "none",
|
|
194
|
+
posH: {
|
|
195
|
+
relativeFrom: "margin",
|
|
196
|
+
offsetPt: pt(label.xPt)
|
|
197
|
+
},
|
|
198
|
+
posV: {
|
|
199
|
+
relativeFrom: "margin",
|
|
200
|
+
offsetPt: pt(label.yPt)
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
width: pt(label.widthPt),
|
|
204
|
+
height: pt(label.heightPt),
|
|
205
|
+
geometry: {
|
|
206
|
+
kind: "preset",
|
|
207
|
+
preset: "rect",
|
|
208
|
+
adjust: /* @__PURE__ */ new Map()
|
|
54
209
|
},
|
|
55
|
-
|
|
210
|
+
fill: { kind: "none" },
|
|
211
|
+
text,
|
|
212
|
+
paragraphProperties: {}
|
|
56
213
|
});
|
|
57
214
|
}
|
|
58
|
-
|
|
59
|
-
|
|
215
|
+
return out;
|
|
216
|
+
}
|
|
217
|
+
/** A diagram label's box: its `dsp:txXfrm`, moved to the frame's corner. */
|
|
218
|
+
function textBox(sp, frame) {
|
|
219
|
+
const xfrm = poFirstChild(sp, "dsp:txXfrm");
|
|
220
|
+
const ext = xfrm ? poFirstChild(xfrm, "a:ext") : void 0;
|
|
221
|
+
if (!ext) return void 0;
|
|
222
|
+
const widthPt = emuToPt(poIntAttr(ext, "cx") ?? 0);
|
|
223
|
+
const heightPt = emuToPt(poIntAttr(ext, "cy") ?? 0);
|
|
224
|
+
if (!(widthPt > 0 && heightPt > 0)) return void 0;
|
|
225
|
+
const off = poFirstChild(xfrm, "a:off");
|
|
226
|
+
return {
|
|
227
|
+
widthPt,
|
|
228
|
+
heightPt,
|
|
229
|
+
anchorRow: frame.anchorRow,
|
|
230
|
+
xPt: frame.xPt + emuToPt(poIntAttr(off, "x") ?? 0),
|
|
231
|
+
yPt: frame.yPt + emuToPt(poIntAttr(off, "y") ?? 0)
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
/** A diagram shape's box: its own `a:xfrm`, moved to the frame's corner. */
|
|
235
|
+
function diagramBox(sp, frame) {
|
|
236
|
+
const spPr = poFirstChild(sp, "dsp:spPr");
|
|
237
|
+
const xfrm = spPr ? poFirstChild(spPr, "a:xfrm") : void 0;
|
|
238
|
+
const off = xfrm ? poFirstChild(xfrm, "a:off") : void 0;
|
|
239
|
+
const ext = xfrm ? poFirstChild(xfrm, "a:ext") : void 0;
|
|
240
|
+
if (!ext) return void 0;
|
|
241
|
+
const widthPt = emuToPt(poIntAttr(ext, "cx") ?? 0);
|
|
242
|
+
const heightPt = emuToPt(poIntAttr(ext, "cy") ?? 0);
|
|
243
|
+
if (!(widthPt > 0 && heightPt > 0)) return void 0;
|
|
244
|
+
return {
|
|
245
|
+
widthPt,
|
|
246
|
+
heightPt,
|
|
247
|
+
anchorRow: frame.anchorRow,
|
|
248
|
+
xPt: frame.xPt + emuToPt(poIntAttr(off, "x") ?? 0),
|
|
249
|
+
yPt: frame.yPt + emuToPt(poIntAttr(off, "y") ?? 0)
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Place a group's children inside the box the group itself occupies.
|
|
254
|
+
*
|
|
255
|
+
* §20.5.2.17 `xdr:grpSp` — a group gives its children a coordinate space of its
|
|
256
|
+
* own: `a:xfrm/a:chOff` + `a:chExt` name that space, and each child's own
|
|
257
|
+
* `a:xfrm` is a rectangle inside it. Mapping the rectangle onto the group's box
|
|
258
|
+
* places the child; a nested group recurses through the same map.
|
|
259
|
+
*
|
|
260
|
+
* @param group The `xdr:grpSp` element.
|
|
261
|
+
* @param box The box the group occupies on the page.
|
|
262
|
+
* @param out Collects each `xdr:sp` with the box it lands in.
|
|
263
|
+
*/
|
|
264
|
+
function collectGrouped(group, box, out) {
|
|
265
|
+
const grpPr = poChildren(group).find((c) => poIs(c, "xdr:grpSpPr"));
|
|
266
|
+
const xfrm = grpPr ? poChildren(grpPr).find((c) => poIs(c, "a:xfrm")) : void 0;
|
|
267
|
+
const chOff = xfrm ? poChildren(xfrm).find((c) => poIs(c, "a:chOff")) : void 0;
|
|
268
|
+
const chExt = xfrm ? poChildren(xfrm).find((c) => poIs(c, "a:chExt")) : void 0;
|
|
269
|
+
const ox = chOff ? poIntAttr(chOff, "x") ?? 0 : 0;
|
|
270
|
+
const oy = chOff ? poIntAttr(chOff, "y") ?? 0 : 0;
|
|
271
|
+
const cw = chExt ? poIntAttr(chExt, "cx") ?? 0 : 0;
|
|
272
|
+
const ch = chExt ? poIntAttr(chExt, "cy") ?? 0 : 0;
|
|
273
|
+
if (!(cw > 0) || !(ch > 0)) return;
|
|
274
|
+
const place = (node, prTag) => {
|
|
275
|
+
const pr = poChildren(node).find((c) => poIs(c, prTag));
|
|
276
|
+
const x = pr ? poChildren(pr).find((c) => poIs(c, "a:xfrm")) : void 0;
|
|
277
|
+
const off = x ? poChildren(x).find((c) => poIs(c, "a:off")) : void 0;
|
|
278
|
+
const ext = x ? poChildren(x).find((c) => poIs(c, "a:ext")) : void 0;
|
|
279
|
+
if (!off || !ext) return void 0;
|
|
280
|
+
const w = (poIntAttr(ext, "cx") ?? 0) / cw * box.widthPt;
|
|
281
|
+
const h = (poIntAttr(ext, "cy") ?? 0) / ch * box.heightPt;
|
|
282
|
+
if (!(w > 0) || !(h > 0)) return void 0;
|
|
283
|
+
return {
|
|
284
|
+
widthPt: w,
|
|
285
|
+
heightPt: h,
|
|
286
|
+
anchorRow: box.anchorRow,
|
|
287
|
+
xPt: box.xPt + ((poIntAttr(off, "x") ?? 0) - ox) / cw * box.widthPt,
|
|
288
|
+
yPt: box.yPt + ((poIntAttr(off, "y") ?? 0) - oy) / ch * box.heightPt
|
|
289
|
+
};
|
|
290
|
+
};
|
|
291
|
+
for (const child of poChildren(group)) if (poIs(child, "xdr:sp")) {
|
|
292
|
+
const b = place(child, "xdr:spPr");
|
|
293
|
+
if (b) out.push({
|
|
294
|
+
sp: child,
|
|
295
|
+
box: b
|
|
296
|
+
});
|
|
297
|
+
} else if (poIs(child, "xdr:grpSp")) {
|
|
298
|
+
const b = place(child, "xdr:grpSpPr");
|
|
299
|
+
if (b) collectGrouped(child, b, out);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Whether a drawing says it is not to be shown.
|
|
304
|
+
*
|
|
305
|
+
* §20.1.2.2.8 `cNvPr@hidden` — "Specifies whether this DrawingML object shall
|
|
306
|
+
* be displayed", default false. POI writes one white rectangle per cell comment
|
|
307
|
+
* under the name `_xssf_cell_comment` and marks it hidden; read without the
|
|
308
|
+
* flag, 51850.xlsx grew a 494 × 677pt outlined box across both its pages.
|
|
309
|
+
*
|
|
310
|
+
* @param node The `xdr:sp` / `xdr:pic` / `xdr:graphicFrame` element.
|
|
311
|
+
* @param nvPropTag Its non-visual properties wrapper.
|
|
312
|
+
* @returns True when the object declares itself hidden.
|
|
313
|
+
*/
|
|
314
|
+
function isHiddenDrawing(node, nvPropTag) {
|
|
315
|
+
const nv = poChildren(node).find((c) => poIs(c, nvPropTag));
|
|
316
|
+
const cNvPr = nv ? poChildren(nv).find((c) => poIs(c, "xdr:cNvPr")) : void 0;
|
|
317
|
+
const hidden = cNvPr ? poAttr(cNvPr, "hidden") : void 0;
|
|
318
|
+
return hidden === "1" || hidden === "true";
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* The outline `<xdr:style><a:lnRef>` names, if it names one.
|
|
322
|
+
*
|
|
323
|
+
* §20.1.4.2.19: the reference's `idx` is a 1-based index into the theme's
|
|
324
|
+
* `a:lnStyleLst`, which is where the WIDTH lives — the reference itself carries
|
|
325
|
+
* only the colour. Assuming a hairline drew 50299.xlsx's `idx="2"` rectangle in
|
|
326
|
+
* 0.75pt where its theme asks for 2pt.
|
|
327
|
+
*/
|
|
328
|
+
function styleLine(sp, ns, colors, themeLineWidths) {
|
|
329
|
+
const style = poChildren(sp).find((c) => poIs(c, `${ns}:style`));
|
|
330
|
+
const lnRef = style ? poChildren(style).find((c) => poIs(c, "a:lnRef")) : void 0;
|
|
331
|
+
const child = poFirstElement(lnRef);
|
|
332
|
+
const colorHex = child ? resolveColorNode(child, colors) : void 0;
|
|
333
|
+
if (colorHex === void 0) return void 0;
|
|
334
|
+
const idx = Number(poAttr(lnRef, "idx") ?? "");
|
|
335
|
+
return {
|
|
336
|
+
colorHex,
|
|
337
|
+
width: pt((Number.isFinite(idx) ? themeLineWidths[idx - 1] : void 0) ?? .75)
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* The fill `<xdr:style><a:fillRef>` names, if it names one.
|
|
342
|
+
*
|
|
343
|
+
* §20.1.4.2.10, the same mechanism as the outline beside it: a shape drawn from
|
|
344
|
+
* a gallery style carries no `a:solidFill` in its `spPr` at all, and read alone
|
|
345
|
+
* it says the shape has no fill. 50299.xlsx's rectangle asks for `accent1` that
|
|
346
|
+
* way, and we drew it empty on all six sheets.
|
|
347
|
+
*
|
|
348
|
+
* `idx="0"` is the explicit "no fill" slot. Above it the reference is a 1-based
|
|
349
|
+
* index into the theme's `a:fillStyleLst` (§20.1.4.1.13), and THAT is the fill —
|
|
350
|
+
* the reference only says which colour goes where the style says `phClr`. The
|
|
351
|
+
* standard Office theme's third slot is a gradient, which is what 47504.xlsx
|
|
352
|
+
* asks for and what both references draw; painting the referenced colour flat
|
|
353
|
+
* instead lost the gradient on every gallery shape.
|
|
354
|
+
*/
|
|
355
|
+
/**
|
|
356
|
+
* The first CHILD ELEMENT of a style reference — `<a:lnRef>`, `<a:fillRef>`,
|
|
357
|
+
* `<a:effectRef>`, `<a:fontRef>` — each of which wraps exactly one colour.
|
|
358
|
+
*
|
|
359
|
+
* Not `poChildren(ref)[0]`: a part saved with indentation puts a whitespace
|
|
360
|
+
* text node there, and the colour behind it went unread. Every gallery shape in
|
|
361
|
+
* tdf139763ShapeAnchor.xlsx lost its fill AND its outline that way and drew
|
|
362
|
+
* nothing at all on a page LibreOffice fills with two blue arrows.
|
|
363
|
+
*
|
|
364
|
+
* @param ref The style reference, or undefined.
|
|
365
|
+
* @returns Its first element child, or undefined.
|
|
366
|
+
*/
|
|
367
|
+
function poFirstElement(ref) {
|
|
368
|
+
return ref ? poChildren(ref).find((c) => poTag(c) !== void 0) : void 0;
|
|
369
|
+
}
|
|
370
|
+
function styleFill(sp, ns, colors, themeFillStyles) {
|
|
371
|
+
const style = poChildren(sp).find((c) => poIs(c, `${ns}:style`));
|
|
372
|
+
const fillRef = style ? poChildren(style).find((c) => poIs(c, "a:fillRef")) : void 0;
|
|
373
|
+
if (!fillRef || poAttr(fillRef, "idx") === "0") return { kind: "none" };
|
|
374
|
+
const child = poFirstElement(fillRef);
|
|
375
|
+
const colorHex = child ? resolveColorNode(child, colors) : void 0;
|
|
376
|
+
if (colorHex === void 0) return { kind: "none" };
|
|
377
|
+
const idx = Number(poAttr(fillRef, "idx") ?? "");
|
|
378
|
+
const slot = Number.isFinite(idx) ? themeFillStyles[idx - 1] : void 0;
|
|
379
|
+
if (!slot) return {
|
|
380
|
+
kind: "solid",
|
|
381
|
+
colorHex
|
|
382
|
+
};
|
|
383
|
+
const themed = parseFill({ "a:spPr": [slot] }, placeholderColors(colors, colorHex));
|
|
384
|
+
return themed.kind === "none" ? {
|
|
385
|
+
kind: "solid",
|
|
386
|
+
colorHex
|
|
387
|
+
} : themed;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* §20.1.4.2.10 — inside a theme's style list, `phClr` stands for "the colour the
|
|
391
|
+
* reference names". Every other colour resolves as it always did.
|
|
392
|
+
*
|
|
393
|
+
* @param colors The workbook's own resolver.
|
|
394
|
+
* @param phHex The colour the `a:fillRef` / `a:lnRef` carries.
|
|
395
|
+
* @returns A resolver that answers `phClr` with that colour, transforms and all.
|
|
396
|
+
*/
|
|
397
|
+
function placeholderColors(colors, phHex) {
|
|
398
|
+
return (raw) => "scheme" in raw && raw.scheme === "phClr" ? applyColorMods(phHex, raw.mods ?? []) : colors(raw);
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* The shadow `<xdr:style><a:effectRef>` names, if it names one.
|
|
402
|
+
*
|
|
403
|
+
* §20.1.4.2.8, the same mechanism as the fill and the outline beside it: a
|
|
404
|
+
* shape drawn from a gallery style carries no `a:effectLst`, only an index into
|
|
405
|
+
* the theme's `a:effectStyleLst`, and the colour to put where the style says
|
|
406
|
+
* `phClr`. 47504.xlsx asks for slot 2 that way — the soft shadow both
|
|
407
|
+
* references draw under it, and which we drew not at all.
|
|
408
|
+
*
|
|
409
|
+
* @param sp The `xdr:sp` node.
|
|
410
|
+
* @param colors The workbook's colour resolver.
|
|
411
|
+
* @param themeEffectStyles The theme's `a:effectStyleLst` nodes.
|
|
412
|
+
* @returns The shadow, or undefined when the shape names no effect style.
|
|
413
|
+
*/
|
|
414
|
+
function styleShadow(sp, ns, colors, themeEffectStyles) {
|
|
415
|
+
const style = poChildren(sp).find((c) => poIs(c, `${ns}:style`));
|
|
416
|
+
const ref = style ? poChildren(style).find((c) => poIs(c, "a:effectRef")) : void 0;
|
|
417
|
+
if (!ref) return void 0;
|
|
418
|
+
const idx = Number(poAttr(ref, "idx") ?? "");
|
|
419
|
+
if (!Number.isFinite(idx) || idx < 1) return void 0;
|
|
420
|
+
const slot = themeEffectStyles[idx - 1];
|
|
421
|
+
const list = slot ? poFirstChild(slot, "a:effectLst") : void 0;
|
|
422
|
+
const shdw = list ? poFirstChild(list, "a:outerShdw") : void 0;
|
|
423
|
+
if (!shdw) return void 0;
|
|
424
|
+
const child = poFirstElement(ref);
|
|
425
|
+
const phHex = child ? resolveColorNode(child, colors) : void 0;
|
|
426
|
+
return shadowFromOuterShdw(shdw, phHex ? placeholderColors(colors, phHex) : colors);
|
|
427
|
+
}
|
|
428
|
+
/** The colour `<xdr:style><a:fontRef>` names, if it names one. */
|
|
429
|
+
function styleFontColor(sp, ns, colors) {
|
|
430
|
+
const style = poChildren(sp).find((c) => poIs(c, `${ns}:style`));
|
|
431
|
+
const child = poFirstElement(style ? poChildren(style).find((c) => poIs(c, "a:fontRef")) : void 0);
|
|
432
|
+
return child ? resolveColorNode(child, colors) : void 0;
|
|
433
|
+
}
|
|
434
|
+
/** The text body with that colour filled in wherever a run declares none. */
|
|
435
|
+
function withStyleTextColor(text, sp, ns, colors) {
|
|
436
|
+
const colorHex = styleFontColor(sp, ns, colors);
|
|
437
|
+
if (colorHex === void 0) return text;
|
|
438
|
+
return {
|
|
439
|
+
...text,
|
|
440
|
+
content: text.content.map((block) => block.kind === "paragraph" ? {
|
|
441
|
+
...block,
|
|
442
|
+
paragraph: {
|
|
443
|
+
...block.paragraph,
|
|
444
|
+
runs: block.paragraph.runs.map((run) => run.properties.colorHex === void 0 ? {
|
|
445
|
+
...run,
|
|
446
|
+
properties: {
|
|
447
|
+
...run.properties,
|
|
448
|
+
colorHex
|
|
449
|
+
}
|
|
450
|
+
} : run)
|
|
451
|
+
}
|
|
452
|
+
} : block)
|
|
453
|
+
};
|
|
60
454
|
}
|
|
61
455
|
function anchorBox(anchor, colWidthPt, rowHeightPt) {
|
|
62
456
|
const from = marker(poChildren(anchor).find((c) => poIs(c, "xdr:from")));
|
|
@@ -65,21 +459,56 @@ function anchorBox(anchor, colWidthPt, rowHeightPt) {
|
|
|
65
459
|
if (!from || !to) return void 0;
|
|
66
460
|
const widthPt = span(from.col, from.colOffPt, to.col, to.colOffPt, colWidthPt);
|
|
67
461
|
const heightPt = span(from.row, from.rowOffPt, to.row, to.rowOffPt, rowHeightPt);
|
|
68
|
-
|
|
462
|
+
if (!(widthPt > 0 && heightPt > 0)) return void 0;
|
|
463
|
+
return {
|
|
69
464
|
widthPt,
|
|
70
465
|
heightPt,
|
|
71
|
-
anchorRow: from.row
|
|
72
|
-
|
|
466
|
+
anchorRow: from.row,
|
|
467
|
+
xPt: origin(from.col, from.colOffPt, colWidthPt),
|
|
468
|
+
yPt: origin(from.row, from.rowOffPt, rowHeightPt)
|
|
469
|
+
};
|
|
73
470
|
}
|
|
74
471
|
const ext = poChildren(anchor).find((c) => poIs(c, "xdr:ext"));
|
|
75
472
|
if (!ext) return void 0;
|
|
473
|
+
const pos = absolutePos(anchor);
|
|
76
474
|
const widthPt = emuToPt(poIntAttr(ext, "cx") ?? 0);
|
|
77
475
|
const heightPt = emuToPt(poIntAttr(ext, "cy") ?? 0);
|
|
78
|
-
|
|
476
|
+
if (!(widthPt > 0 && heightPt > 0)) return void 0;
|
|
477
|
+
return {
|
|
79
478
|
widthPt,
|
|
80
479
|
heightPt,
|
|
81
|
-
anchorRow: from?.row ?? 0
|
|
82
|
-
|
|
480
|
+
anchorRow: from?.row ?? 0,
|
|
481
|
+
xPt: from ? origin(from.col, from.colOffPt, colWidthPt) : pos.xPt,
|
|
482
|
+
yPt: from ? origin(from.row, from.rowOffPt, rowHeightPt) : pos.yPt
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* §20.5.2.1 `absoluteAnchor` names no cell: it gives the distance from the
|
|
487
|
+
* sheet's own corner in `<xdr:pos>`. Read nowhere, both of
|
|
488
|
+
* tdf139763ShapeAnchor.xlsx's arrows piled into the top-left corner.
|
|
489
|
+
*
|
|
490
|
+
* @param anchor The anchor element.
|
|
491
|
+
* @returns The offset in points; zero for an anchor that states none.
|
|
492
|
+
*/
|
|
493
|
+
function absolutePos(anchor) {
|
|
494
|
+
const node = poChildren(anchor).find((c) => poIs(c, "xdr:pos"));
|
|
495
|
+
if (!node) return {
|
|
496
|
+
xPt: 0,
|
|
497
|
+
yPt: 0
|
|
498
|
+
};
|
|
499
|
+
return {
|
|
500
|
+
xPt: emuToPt(poIntAttr(node, "x") ?? 0),
|
|
501
|
+
yPt: emuToPt(poIntAttr(node, "y") ?? 0)
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* The distance from the sheet's origin to a marker: every whole track before it
|
|
506
|
+
* plus the marker's own offset into the one it lands in.
|
|
507
|
+
*/
|
|
508
|
+
function origin(index, offsetPt, trackPt) {
|
|
509
|
+
let total = 0;
|
|
510
|
+
for (let i = 0; i < index; i++) total += trackPt(i);
|
|
511
|
+
return total + offsetPt;
|
|
83
512
|
}
|
|
84
513
|
function marker(node) {
|
|
85
514
|
if (!node) return void 0;
|
|
@@ -105,4 +534,4 @@ function span(from, fromOffPt, to, toOffPt, trackPt) {
|
|
|
105
534
|
return total - fromOffPt + toOffPt;
|
|
106
535
|
}
|
|
107
536
|
//#endregion
|
|
108
|
-
export { parseSheetShapes };
|
|
537
|
+
export { parseDiagramFrames, parseDiagramShapes, parseSheetShapes };
|
|
@@ -21,12 +21,33 @@ export interface ProjectSheetOptions {
|
|
|
21
21
|
* unreported.
|
|
22
22
|
*/
|
|
23
23
|
readonly losses?: Array<Loss>;
|
|
24
|
+
/**
|
|
25
|
+
* Width of a digit in the font the document will actually be rendered in, at
|
|
26
|
+
* the workbook's default size, in points.
|
|
27
|
+
*
|
|
28
|
+
* §18.3.1.13 measures a column in Maximum Digit Widths of the workbook's own
|
|
29
|
+
* default font — the unit is a property of the font, not a constant. Laying
|
|
30
|
+
* columns out in one font's digit and then drawing the text in another makes
|
|
31
|
+
* every column the wrong width for its contents: with Calibri's 5.25 pt
|
|
32
|
+
* assumed and Roboto's 6.18 pt drawn, a column holds a sixth less than the
|
|
33
|
+
* file says, and the text that does not fit is clipped away.
|
|
34
|
+
*
|
|
35
|
+
* Omitted ⇒ Excel's own 7 px, which is right only if the render font matches
|
|
36
|
+
* the workbook's.
|
|
37
|
+
*/
|
|
38
|
+
readonly digitWidthPt?: number;
|
|
39
|
+
/**
|
|
40
|
+
* §18.3.1.34 `&F` — the workbook's file name, for a header or footer that
|
|
41
|
+
* prints it. The reader takes bytes and cannot know it, so the caller
|
|
42
|
+
* supplies it; absent, the code is dropped exactly as before.
|
|
43
|
+
*/
|
|
44
|
+
readonly fileName?: string;
|
|
24
45
|
}
|
|
25
46
|
/**
|
|
26
47
|
* Project a {@link SheetDoc} into a {@link FlowDoc} (E-SHEET SA2): each grid sheet
|
|
27
48
|
* becomes flow blocks (a table + chart/picture/shape/slicer frames, then comment
|
|
28
|
-
* / control listings), with sheets after the first starting on a new page.
|
|
29
|
-
*
|
|
49
|
+
* / control listings), with sheets after the first starting on a new page. Each
|
|
50
|
+
* sheet gets its own section — its page geometry and its own header/footer.
|
|
30
51
|
*
|
|
31
52
|
* @param sheet The SpreadsheetML IR tree.
|
|
32
53
|
* @param options Projection knobs (the W9 reference date).
|