reamkit 1.31.0 → 1.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/esm/core/bidi/index.d.ts +10 -0
- package/dist/esm/core/bidi/index.js +18 -2
- package/dist/esm/core/converter/facade.js +10 -5
- package/dist/esm/core/converter/project.js +1 -0
- package/dist/esm/core/converter/ream.js +17 -6
- package/dist/esm/core/converter/sheet-images.d.ts +17 -0
- package/dist/esm/core/converter/sheet-images.js +33 -0
- package/dist/esm/core/document-model/index.d.ts +1 -1
- package/dist/esm/core/document-model/types.d.ts +112 -0
- package/dist/esm/core/drawingml/chart-geometry.d.ts +21 -8
- package/dist/esm/core/drawingml/chart-geometry.js +198 -108
- package/dist/esm/core/drawingml/chart-parser.js +73 -2
- package/dist/esm/core/drawingml/colors.d.ts +6 -0
- package/dist/esm/core/drawingml/colors.js +23 -3
- package/dist/esm/core/drawingml/shape-render.d.ts +3 -1
- package/dist/esm/core/drawingml/shape-render.js +4 -2
- package/dist/esm/core/font/measure.d.ts +9 -0
- package/dist/esm/core/font/measure.js +9 -1
- package/dist/esm/core/fonts/families.js +4 -2
- package/dist/esm/core/fonts/scripts.js +4 -2
- package/dist/esm/core/ir/flow.d.ts +6 -0
- package/dist/esm/core/ir/sheet.d.ts +43 -0
- package/dist/esm/core/number-format.js +16 -11
- package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
- package/dist/esm/core/spreadsheet-model/types.d.ts +96 -0
- package/dist/esm/core/style-cascade/resolver.js +4 -2
- package/dist/esm/excel/column-bands.d.ts +39 -6
- package/dist/esm/excel/column-bands.js +41 -17
- package/dist/esm/excel/comments-parser.js +19 -0
- package/dist/esm/excel/conditional-format.d.ts +7 -3
- package/dist/esm/excel/conditional-format.js +77 -39
- package/dist/esm/excel/formula/context.d.ts +18 -0
- package/dist/esm/excel/formula/eval.js +51 -0
- package/dist/esm/excel/formula/index.d.ts +2 -2
- package/dist/esm/excel/formula/lexer.d.ts +1 -1
- package/dist/esm/excel/formula/lexer.js +41 -0
- package/dist/esm/excel/formula/parser.d.ts +25 -0
- package/dist/esm/excel/formula/parser.js +95 -0
- package/dist/esm/excel/print-model.d.ts +38 -1
- package/dist/esm/excel/print-model.js +522 -80
- package/dist/esm/excel/shared-strings-parser.d.ts +9 -0
- package/dist/esm/excel/shared-strings-parser.js +11 -1
- package/dist/esm/excel/sheet-drawing.d.ts +38 -2
- package/dist/esm/excel/sheet-drawing.js +47 -13
- package/dist/esm/excel/sheet-shape-parser.d.ts +6 -2
- package/dist/esm/excel/sheet-shape-parser.js +18 -6
- package/dist/esm/excel/sheet-to-flow.d.ts +17 -0
- package/dist/esm/excel/sheet-to-flow.js +434 -47
- package/dist/esm/excel/styles-parser.d.ts +9 -0
- package/dist/esm/excel/styles-parser.js +80 -26
- package/dist/esm/excel/table-parser.js +5 -0
- package/dist/esm/excel/table-style-presets.d.ts +26 -0
- package/dist/esm/excel/table-style-presets.js +1555 -0
- package/dist/esm/excel/table-style.d.ts +13 -0
- package/dist/esm/excel/table-style.js +75 -0
- package/dist/esm/excel/tint.d.ts +10 -0
- package/dist/esm/excel/tint.js +75 -0
- package/dist/esm/excel/vml-drawing.d.ts +41 -3
- package/dist/esm/excel/vml-drawing.js +143 -10
- package/dist/esm/excel/worksheet-parser.js +89 -15
- package/dist/esm/excel/xls/biff-reader.js +10 -2
- package/dist/esm/excel/xlsx-reader.js +195 -14
- package/dist/esm/excel/xlsx-writer.js +8 -7
- package/dist/esm/html/html-writer.js +202 -20
- package/dist/esm/layout/fit-pages.d.ts +11 -0
- package/dist/esm/layout/fit-pages.js +86 -0
- package/dist/esm/layout/line-paint.d.ts +58 -0
- package/dist/esm/layout/line-paint.js +107 -0
- package/dist/esm/layout/page-doc.d.ts +51 -0
- package/dist/esm/layout/page-doc.js +141 -1
- package/dist/esm/layout/styled-layout.d.ts +7 -0
- package/dist/esm/layout/styled-layout.js +274 -51
- package/dist/esm/layout/turned-section.js +14 -2
- package/dist/esm/markdown/markdown-writer.js +17 -6
- package/dist/esm/pdf/styled-page-emitter.js +49 -71
- package/dist/esm/pdf/styled-page-renderer.js +1 -1
- package/dist/esm/pdf-reader/font.js +3 -3
- package/dist/esm/svg/svg-text.d.ts +58 -0
- package/dist/esm/svg/svg-text.js +322 -0
- package/dist/esm/svg/svg-writer.js +135 -71
- package/dist/esm/word/docx-writer.js +11 -2
- package/package.json +5 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { pt } from "../core/ir/units.js";
|
|
2
2
|
import { EMPTY_STYLE_SHEET, resolveBodyStyles } from "../core/style-cascade/resolver.js";
|
|
3
3
|
import "../core/style-cascade/index.js";
|
|
4
|
-
import {
|
|
4
|
+
import { parseCellRef } from "./cell-reference.js";
|
|
5
|
+
import { cellPaintsVisibly, noteKey, printableHeightPt, printableWidthPt, resolvePrintArea, resolvePrintTitleRows, sectionFromWorksheet, slicerTable, worksheetToBody } from "./print-model.js";
|
|
5
6
|
import { buildHeaderFooterContent } from "./header-footer.js";
|
|
6
7
|
//#region src/excel/sheet-to-flow.ts
|
|
7
8
|
var HEADER_REL = "_xlsxHeaderDefault";
|
|
@@ -15,6 +16,29 @@ var PAGE_BREAK_PARAGRAPH = {
|
|
|
15
16
|
runs: []
|
|
16
17
|
};
|
|
17
18
|
/**
|
|
19
|
+
* A page with room for any sheet, and the sliver of margin a sheet drawn as an
|
|
20
|
+
* image keeps round itself, so that the rules on its outer edges are not cut
|
|
21
|
+
* in half.
|
|
22
|
+
*/
|
|
23
|
+
var WHOLE_SHEET_PAGE_PT = 1e6;
|
|
24
|
+
var WHOLE_SHEET_MARGIN_PT = 2;
|
|
25
|
+
var WHOLE_SHEET_SECTION = {
|
|
26
|
+
pageSize: {
|
|
27
|
+
width: pt(WHOLE_SHEET_PAGE_PT),
|
|
28
|
+
height: pt(WHOLE_SHEET_PAGE_PT)
|
|
29
|
+
},
|
|
30
|
+
margins: {
|
|
31
|
+
top: pt(WHOLE_SHEET_MARGIN_PT),
|
|
32
|
+
right: pt(WHOLE_SHEET_MARGIN_PT),
|
|
33
|
+
bottom: pt(WHOLE_SHEET_MARGIN_PT),
|
|
34
|
+
left: pt(WHOLE_SHEET_MARGIN_PT),
|
|
35
|
+
header: pt(0),
|
|
36
|
+
footer: pt(0)
|
|
37
|
+
},
|
|
38
|
+
headers: [],
|
|
39
|
+
footers: []
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
18
42
|
* Project a {@link SheetDoc} into a {@link FlowDoc} (E-SHEET SA2): each grid sheet
|
|
19
43
|
* becomes flow blocks (a table + chart/picture/shape/slicer frames, then comment
|
|
20
44
|
* / control listings), with sheets after the first starting on a new page. Each
|
|
@@ -28,6 +52,8 @@ function projectSheetDoc(sheet, options = {}) {
|
|
|
28
52
|
const body = [];
|
|
29
53
|
const sheetSections = [];
|
|
30
54
|
const sheetEnds = [];
|
|
55
|
+
const sheetNames = [];
|
|
56
|
+
const screen = options.screen === true;
|
|
31
57
|
let firstSheetSection;
|
|
32
58
|
const headersFooters = /* @__PURE__ */ new Map();
|
|
33
59
|
const sheetGrids = new Map(sheet.sheets.map((s) => [s.name, s.grid]));
|
|
@@ -38,19 +64,28 @@ function projectSheetDoc(sheet, options = {}) {
|
|
|
38
64
|
for (let sheetIdx = 0; sheetIdx < sheet.sheets.length; sheetIdx++) {
|
|
39
65
|
const ws = sheet.sheets[sheetIdx];
|
|
40
66
|
if (ws.hidden) continue;
|
|
41
|
-
if (!sheetPrintsAnything(ws, sheet.styles) && ws !== fallbackSheet) {
|
|
67
|
+
if (!screen && !sheetPrintsAnything(ws, sheet.styles) && ws !== fallbackSheet) {
|
|
42
68
|
printableSheets--;
|
|
43
69
|
continue;
|
|
44
70
|
}
|
|
45
71
|
const scaleSink = { value: 1 };
|
|
46
72
|
const bandSink = { lefts: [0] };
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
73
|
+
const headingSink = {
|
|
74
|
+
dxPt: 0,
|
|
75
|
+
dyPt: 0
|
|
76
|
+
};
|
|
77
|
+
const notesShown = screen || ws.grid.pageSetup?.cellComments === "asDisplayed";
|
|
78
|
+
const drawingExtentPt = drawingReachPt(ws, notesShown, screen);
|
|
79
|
+
const controlBlocks = controlShapeBlocks(ws.formControls, ws.activeXControls);
|
|
80
|
+
const noteBlocks = notesShown ? (ws.comments ?? []).flatMap((comment) => noteShapes(comment, ws.grid.rightToLeft === true)) : [];
|
|
81
|
+
const printArea = screen ? void 0 : resolvePrintArea(sheet.definedNames, sheetIdx);
|
|
82
|
+
const titleRows = screen ? void 0 : resolvePrintTitleRows(sheet.definedNames, sheetIdx);
|
|
50
83
|
const gridBody = worksheetToBody(ws.grid, sheet.sharedStrings, sheet.styles, sheet.date1904, {
|
|
84
|
+
...screen ? { screen } : {},
|
|
85
|
+
...screen && options.wholeSheetPages === true ? { image: true } : {},
|
|
51
86
|
...printArea ? { printArea } : {},
|
|
52
87
|
...titleRows ? { titleRows } : {},
|
|
53
|
-
gridLines: ws.grid.printOptions?.gridLines === true,
|
|
88
|
+
gridLines: screen ? ws.grid.showGridLines !== false : ws.grid.printOptions?.gridLines === true,
|
|
54
89
|
sheetGrids,
|
|
55
90
|
sheetName: ws.name,
|
|
56
91
|
definedNames: sheet.definedNames,
|
|
@@ -60,19 +95,23 @@ function projectSheetDoc(sheet, options = {}) {
|
|
|
60
95
|
...options.digitWidthPt !== void 0 ? { digitWidthPt: options.digitWidthPt } : {},
|
|
61
96
|
...sheet.styles.fonts[0]?.sizePt !== void 0 ? { defaultFontPt: sheet.styles.fonts[0].sizePt } : {},
|
|
62
97
|
...options.losses ? { losses: options.losses } : {},
|
|
98
|
+
...screen && ws.comments && ws.comments.length > 0 ? { noteFlags: noteFlagsOf(ws.comments) } : {},
|
|
63
99
|
scaleSink,
|
|
64
100
|
bandSink,
|
|
65
|
-
|
|
101
|
+
headingSink,
|
|
102
|
+
...drawingExtentPt ? { drawingExtentPt } : {},
|
|
103
|
+
...screen ? {} : { drawingBoxesPt: drawingBoxes(ws, [...controlBlocks, ...noteBlocks]) }
|
|
66
104
|
});
|
|
67
|
-
const sheetSection = withHeaderFooter(sectionFromWorksheet(ws.grid), ws, headersFooters, scaleSink.value, sheet.styles.fonts[0]?.sizePt, printed, options.fileName, sheet.themePalette, options.now);
|
|
105
|
+
const sheetSection = screen ? options.wholeSheetPages === true ? WHOLE_SHEET_SECTION : sectionFromWorksheet(ws.grid) : withHeaderFooter(sectionFromWorksheet(ws.grid), ws, headersFooters, scaleSink.value, sheet.styles.fonts[0]?.sizePt, printed, options.fileName, sheet.themePalette, options.now);
|
|
68
106
|
if (printed === 0) firstSheetSection = sheetSection;
|
|
69
107
|
sheetSections.push(sheetSection);
|
|
108
|
+
sheetNames.push(ws.name);
|
|
70
109
|
if (options.sheetHeadings === true) body.push({
|
|
71
110
|
kind: "paragraph",
|
|
72
111
|
paragraph: {
|
|
73
112
|
properties: {
|
|
74
113
|
outlineLevel: 0,
|
|
75
|
-
...printed > 0 ? { pageBreakBefore: true } : {}
|
|
114
|
+
...printed > 0 && !screen ? { pageBreakBefore: true } : {}
|
|
76
115
|
},
|
|
77
116
|
runs: [{
|
|
78
117
|
text: ws.name,
|
|
@@ -80,37 +119,52 @@ function projectSheetDoc(sheet, options = {}) {
|
|
|
80
119
|
}]
|
|
81
120
|
}
|
|
82
121
|
});
|
|
83
|
-
else if (printed > 0) body.push({
|
|
122
|
+
else if (printed > 0 && !screen) body.push({
|
|
84
123
|
kind: "paragraph",
|
|
85
124
|
paragraph: PAGE_BREAK_PARAGRAPH
|
|
86
125
|
});
|
|
87
126
|
const drawings = [];
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
for (const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
127
|
+
const pageFilling = ws.grid.chartSheet === true && !screen;
|
|
128
|
+
for (const ref of ws.charts ?? []) {
|
|
129
|
+
if (ref.screenOnly && !screen) continue;
|
|
130
|
+
drawings.push({
|
|
131
|
+
kind: "chart",
|
|
132
|
+
chart: pageFilling ? {
|
|
133
|
+
...anchorFloat(0, 0, 1),
|
|
134
|
+
chartRelId: ref.chartPartPath,
|
|
135
|
+
width: pt(printableWidthPt(ws.grid)),
|
|
136
|
+
height: pt(printableHeightPt(ws.grid)),
|
|
137
|
+
paragraphProperties: {}
|
|
138
|
+
} : {
|
|
139
|
+
...anchorFloat(ref.xPt, ref.yPt, scaleSink.value),
|
|
140
|
+
chartRelId: ref.chartPartPath,
|
|
141
|
+
width: pt(ref.widthPt * scaleSink.value),
|
|
142
|
+
height: pt(ref.heightPt * scaleSink.value),
|
|
143
|
+
paragraphProperties: {}
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
for (const img of ws.images ?? []) {
|
|
148
|
+
if (img.screenOnly && !screen) continue;
|
|
149
|
+
drawings.push({
|
|
150
|
+
kind: "image",
|
|
151
|
+
image: {
|
|
152
|
+
...anchorFloat(img.xPt, img.yPt, scaleSink.value),
|
|
153
|
+
resource: img.resourceId,
|
|
154
|
+
width: pt(img.widthPt * scaleSink.value),
|
|
155
|
+
height: pt(img.heightPt * scaleSink.value),
|
|
156
|
+
paragraphProperties: {}
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
for (const [i, shape] of (ws.shapes ?? []).entries()) {
|
|
161
|
+
if (!screen && ws.screenOnlyShapes?.has(i)) continue;
|
|
162
|
+
drawings.push({
|
|
163
|
+
kind: "shape",
|
|
164
|
+
shape: screen ? shape : scaleShape(shape, scaleSink.value)
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
const drawingBands = gridBody.length === 0 && !screen ? bandDrawings(controlBlocks, printableHeightPt(ws.grid), scaleSink.value, DOWN).flatMap((row) => bandDrawings(row, printableWidthPt(ws.grid), scaleSink.value)) : [controlBlocks];
|
|
114
168
|
for (let band = 0; band < drawingBands.length; band++) {
|
|
115
169
|
if (band > 0) drawings.push({
|
|
116
170
|
kind: "paragraph",
|
|
@@ -118,22 +172,43 @@ function projectSheetDoc(sheet, options = {}) {
|
|
|
118
172
|
});
|
|
119
173
|
for (const shape of drawingBands[band]) drawings.push({
|
|
120
174
|
kind: "shape",
|
|
121
|
-
shape:
|
|
175
|
+
shape: screen ? shape : scaleShape(shape, scaleSink.value)
|
|
122
176
|
});
|
|
123
177
|
}
|
|
124
|
-
|
|
178
|
+
for (const shape of noteBlocks) drawings.push({
|
|
179
|
+
kind: "shape",
|
|
180
|
+
shape: screen ? shape : scaleShape(shape, scaleSink.value)
|
|
181
|
+
});
|
|
182
|
+
const { grid, loose } = screen ? {
|
|
183
|
+
grid: gridBody,
|
|
184
|
+
loose: drawings
|
|
185
|
+
} : intoOverlays(gridBody, drawings, printArea !== void 0);
|
|
186
|
+
const placed = withDrawingsByBand(screen ? loose : loose.map((el) => el.kind === "shape" ? {
|
|
187
|
+
...el,
|
|
188
|
+
shape: centreShape(el.shape, ws.grid, drawingExtentPt, scaleSink.value)
|
|
189
|
+
} : el), grid, bandSink.lefts).map((el) => headingSink.dxPt > 0 || headingSink.dyPt > 0 ? nudgeFloat(el, headingSink.dxPt, headingSink.dyPt) : el);
|
|
190
|
+
body.push(...ws.grid.rightToLeft === true ? mirrorDrawings(placed, screen ? void 0 : printableWidthPt(ws.grid), (drawingExtentPt?.widthPt ?? 0) * scaleSink.value) : placed);
|
|
125
191
|
for (const slicer of ws.slicers ?? []) body.push({
|
|
126
192
|
kind: "table",
|
|
127
193
|
table: slicerTable(slicer)
|
|
128
194
|
});
|
|
129
|
-
const
|
|
130
|
-
if (ws.comments && ws.comments.length > 0) if (
|
|
131
|
-
else options.losses?.push({
|
|
195
|
+
const listComments = !screen && ws.grid.pageSetup?.cellComments === "atEnd";
|
|
196
|
+
if (ws.comments && ws.comments.length > 0) if (listComments) body.push(...commentBlocks(ws.comments));
|
|
197
|
+
else if (!notesShown) options.losses?.push({
|
|
132
198
|
severity: "dropped",
|
|
133
199
|
feature: "comments",
|
|
134
200
|
detail: `${ws.comments.length} cell note(s) not printed — <pageSetup cellComments> is "${ws.grid.pageSetup?.cellComments ?? "none"}"`,
|
|
135
201
|
where: `sheet "${ws.name}"`
|
|
136
202
|
});
|
|
203
|
+
else {
|
|
204
|
+
const hidden = ws.comments.filter((c) => c.shown === void 0).length;
|
|
205
|
+
if (hidden > 0) options.losses?.push({
|
|
206
|
+
severity: "dropped",
|
|
207
|
+
feature: "comments",
|
|
208
|
+
detail: screen ? `${hidden} cell note(s) not shown — a hidden note shows only while its cell is pointed at` : `${hidden} cell note(s) not printed — "asDisplayed" prints the notes the sheet shows`,
|
|
209
|
+
where: `sheet "${ws.name}"`
|
|
210
|
+
});
|
|
211
|
+
}
|
|
137
212
|
if (ws.formControls && ws.formControls.length > 0) body.push(...formControlBlocks(ws.formControls));
|
|
138
213
|
if (ws.activeXControls && ws.activeXControls.length > 0) body.push(...activeXBlocks(ws.activeXControls));
|
|
139
214
|
sheetEnds.push(body.length);
|
|
@@ -149,9 +224,10 @@ function projectSheetDoc(sheet, options = {}) {
|
|
|
149
224
|
feature: "images",
|
|
150
225
|
detail: `${String(sheet.metafilePictures)} picture(s) not rendered — a WMF/EMF/PICT metafile is replayed, not embedded; the anchor keeps its space`
|
|
151
226
|
});
|
|
152
|
-
const sections = sheetSections.length > 1 ? sheetSections.map((properties, i) => ({
|
|
227
|
+
const sections = sheetSections.length > 1 || screen ? sheetSections.map((properties, i) => ({
|
|
153
228
|
properties,
|
|
154
|
-
endIndex: sheetEnds[i]
|
|
229
|
+
endIndex: sheetEnds[i],
|
|
230
|
+
...screen ? { sheet: { name: sheetNames[i] } } : {}
|
|
155
231
|
})) : [];
|
|
156
232
|
return {
|
|
157
233
|
kind: "flow",
|
|
@@ -162,7 +238,169 @@ function projectSheetDoc(sheet, options = {}) {
|
|
|
162
238
|
resources: sheet.resources,
|
|
163
239
|
...sheet.chartData && sheet.chartData.size > 0 ? { charts: sheet.chartData } : {},
|
|
164
240
|
...headersFooters.size > 0 ? { headersFooters } : {},
|
|
165
|
-
...sheet.info ? { info: sheet.info } : {}
|
|
241
|
+
...sheet.info ? { info: sheet.info } : {},
|
|
242
|
+
floatsOverText: true
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* E-SHEET W7 — what a window flags: each cell that carries a note or a
|
|
247
|
+
* comment, with what hovering over it shows. A cell with a threaded comment is
|
|
248
|
+
* flagged as one, and reads as its conversation — Excel writes a legacy
|
|
249
|
+
* placeholder note beside every thread ("[Threaded comment] Your version of
|
|
250
|
+
* Excel allows you to read…"), which is no part of what anyone wrote.
|
|
251
|
+
*
|
|
252
|
+
* @param comments The sheet's legacy notes and threaded comments.
|
|
253
|
+
* @returns The flags by absolute cell (print-model `noteKey`).
|
|
254
|
+
*/
|
|
255
|
+
function noteFlagsOf(comments) {
|
|
256
|
+
const byCell = /* @__PURE__ */ new Map();
|
|
257
|
+
for (const c of comments) {
|
|
258
|
+
let cell;
|
|
259
|
+
try {
|
|
260
|
+
cell = parseCellRef(c.ref);
|
|
261
|
+
} catch {
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
const k = noteKey(cell.row, cell.column);
|
|
265
|
+
const entry = byCell.get(k) ?? {
|
|
266
|
+
notes: [],
|
|
267
|
+
thread: []
|
|
268
|
+
};
|
|
269
|
+
(c.threaded ? entry.thread : entry.notes).push(c.author ? `${c.author}: ${c.text}` : c.text);
|
|
270
|
+
byCell.set(k, entry);
|
|
271
|
+
}
|
|
272
|
+
const out = /* @__PURE__ */ new Map();
|
|
273
|
+
for (const [k, { notes, thread }] of byCell) out.set(k, thread.length > 0 ? {
|
|
274
|
+
kind: "thread",
|
|
275
|
+
text: thread.join("\n")
|
|
276
|
+
} : {
|
|
277
|
+
kind: "note",
|
|
278
|
+
text: notes.join("\n")
|
|
279
|
+
});
|
|
280
|
+
return out;
|
|
281
|
+
}
|
|
282
|
+
var NOTE_INSET_PT = 1.5;
|
|
283
|
+
var NOTE_SHADOW_PT = 2;
|
|
284
|
+
var NOTE_LINE_PT = .75;
|
|
285
|
+
var NOTE_FACE = "Tahoma";
|
|
286
|
+
var NOTE_SIZE_PT = 9;
|
|
287
|
+
/**
|
|
288
|
+
* A shown note as Excel draws it: a pale box at the place its VML anchor
|
|
289
|
+
* names, holding the note's text run by run (the bold "Author:" line first),
|
|
290
|
+
* with a hard shadow down and to the right — and a thin line from the box to
|
|
291
|
+
* its cell's top-right corner, ending there in a small arrowhead.
|
|
292
|
+
*
|
|
293
|
+
* Built in the file's column order like every other drawing. On a sheet that
|
|
294
|
+
* reads from the right mirrorDrawings moves both, and the line, which has a
|
|
295
|
+
* direction the box has not, is turned round here so it still meets the
|
|
296
|
+
* cell's corner — the corner, too, is mirrored.
|
|
297
|
+
*
|
|
298
|
+
* @param comment The note, with the box its shape shows, if it shows one.
|
|
299
|
+
* @param rtl Whether the sheet reads from the right.
|
|
300
|
+
* @returns The line then the box (the box over the line), or nothing.
|
|
301
|
+
*/
|
|
302
|
+
function noteShapes(comment, rtl) {
|
|
303
|
+
const box = comment.shown;
|
|
304
|
+
if (!box) return [];
|
|
305
|
+
const runs = comment.runs ?? [{ text: comment.author ? `${comment.author}:\n${comment.text}` : comment.text }];
|
|
306
|
+
const frame = {
|
|
307
|
+
...anchorFloat(box.xPt, box.yPt, 1),
|
|
308
|
+
width: pt(box.widthPt),
|
|
309
|
+
height: pt(box.heightPt),
|
|
310
|
+
geometry: {
|
|
311
|
+
kind: "preset",
|
|
312
|
+
preset: "rect"
|
|
313
|
+
},
|
|
314
|
+
fill: {
|
|
315
|
+
kind: "solid",
|
|
316
|
+
colorHex: box.fillHex
|
|
317
|
+
},
|
|
318
|
+
line: {
|
|
319
|
+
width: pt(NOTE_LINE_PT),
|
|
320
|
+
colorHex: box.lineHex
|
|
321
|
+
},
|
|
322
|
+
...box.shadow ? { shadow: {
|
|
323
|
+
dxPt: NOTE_SHADOW_PT,
|
|
324
|
+
dyPt: NOTE_SHADOW_PT,
|
|
325
|
+
blurPt: 0,
|
|
326
|
+
colorHex: "000000",
|
|
327
|
+
alpha: 1
|
|
328
|
+
} } : {},
|
|
329
|
+
text: {
|
|
330
|
+
content: noteParagraphs(runs, box.textAlign),
|
|
331
|
+
insetLeft: pt(NOTE_INSET_PT),
|
|
332
|
+
insetTop: pt(NOTE_INSET_PT),
|
|
333
|
+
insetRight: pt(NOTE_INSET_PT),
|
|
334
|
+
insetBottom: pt(NOTE_INSET_PT),
|
|
335
|
+
anchor: "t"
|
|
336
|
+
},
|
|
337
|
+
paragraphProperties: {}
|
|
338
|
+
};
|
|
339
|
+
const cx = box.cornerXPt;
|
|
340
|
+
const cy = box.cornerYPt;
|
|
341
|
+
const sx = Math.min(Math.max(cx, box.xPt), box.xPt + box.widthPt);
|
|
342
|
+
const sy = Math.min(Math.max(cy, box.yPt), box.yPt + box.heightPt);
|
|
343
|
+
if (sx === cx && sy === cy) return [frame];
|
|
344
|
+
const flipH = sx > cx !== rtl;
|
|
345
|
+
const flipV = sy > cy;
|
|
346
|
+
return [{
|
|
347
|
+
...anchorFloat(Math.min(sx, cx), Math.min(sy, cy), 1),
|
|
348
|
+
width: pt(Math.abs(cx - sx)),
|
|
349
|
+
height: pt(Math.abs(cy - sy)),
|
|
350
|
+
geometry: {
|
|
351
|
+
kind: "preset",
|
|
352
|
+
preset: "line"
|
|
353
|
+
},
|
|
354
|
+
fill: { kind: "none" },
|
|
355
|
+
line: {
|
|
356
|
+
width: pt(NOTE_LINE_PT),
|
|
357
|
+
colorHex: box.lineHex,
|
|
358
|
+
tailEnd: {
|
|
359
|
+
type: "triangle",
|
|
360
|
+
width: "sm",
|
|
361
|
+
length: "sm"
|
|
362
|
+
}
|
|
363
|
+
},
|
|
364
|
+
...flipH || flipV ? { transform: {
|
|
365
|
+
...flipH ? { flipH: true } : {},
|
|
366
|
+
...flipV ? { flipV: true } : {}
|
|
367
|
+
} } : {},
|
|
368
|
+
paragraphProperties: {}
|
|
369
|
+
}, frame];
|
|
370
|
+
}
|
|
371
|
+
function noteParagraphs(runs, align) {
|
|
372
|
+
const lines = [[]];
|
|
373
|
+
for (const r of runs) r.text.split(/\r\n|\r|\n/).forEach((part, i) => {
|
|
374
|
+
if (i > 0) lines.push([]);
|
|
375
|
+
if (part.length > 0) lines[lines.length - 1].push({
|
|
376
|
+
text: part,
|
|
377
|
+
properties: noteRunProps(r)
|
|
378
|
+
});
|
|
379
|
+
});
|
|
380
|
+
while (lines.length > 1 && lines[lines.length - 1].length === 0) lines.pop();
|
|
381
|
+
return lines.map((line) => ({
|
|
382
|
+
kind: "paragraph",
|
|
383
|
+
paragraph: {
|
|
384
|
+
properties: align ? { alignment: align } : {},
|
|
385
|
+
runs: line
|
|
386
|
+
}
|
|
387
|
+
}));
|
|
388
|
+
}
|
|
389
|
+
function noteRunProps(r) {
|
|
390
|
+
const face = r.fontName ?? NOTE_FACE;
|
|
391
|
+
return {
|
|
392
|
+
fontFamily: {
|
|
393
|
+
ascii: face,
|
|
394
|
+
hAnsi: face,
|
|
395
|
+
cs: face
|
|
396
|
+
},
|
|
397
|
+
fontSizePt: pt(r.sizePt ?? NOTE_SIZE_PT),
|
|
398
|
+
colorHex: r.colorHex ?? "000000",
|
|
399
|
+
...r.bold ? { bold: true } : {},
|
|
400
|
+
...r.italic ? { italic: true } : {},
|
|
401
|
+
...r.underline ? { underline: "single" } : {},
|
|
402
|
+
...r.strike ? { strike: true } : {},
|
|
403
|
+
...r.vertAlign ? { verticalAlign: r.vertAlign } : {}
|
|
166
404
|
};
|
|
167
405
|
}
|
|
168
406
|
function commentBlocks(comments) {
|
|
@@ -433,6 +671,113 @@ function withDrawingsByBand(drawings, gridBody, lefts) {
|
|
|
433
671
|
}
|
|
434
672
|
return out;
|
|
435
673
|
}
|
|
674
|
+
/**
|
|
675
|
+
* §20.5 — each printed table of a sheet takes the drawings over the columns it
|
|
676
|
+
* prints, into its overlay (TableOverlay): the layout then shows every page
|
|
677
|
+
* the part of them over its own rows. A table none of them lies over keeps no
|
|
678
|
+
* overlay.
|
|
679
|
+
*
|
|
680
|
+
* @param grid The sheet's grid, its tables framed by the print model.
|
|
681
|
+
* @param drawings The sheet's drawings at the print scale, in the sheet's frame.
|
|
682
|
+
* @param printArea Whether a print area bounds what prints.
|
|
683
|
+
* @returns The grid with the drawings in it, and those over none of its
|
|
684
|
+
* columns that still print — none, when a print area leaves them out.
|
|
685
|
+
*/
|
|
686
|
+
function intoOverlays(grid, drawings, printArea) {
|
|
687
|
+
if (!grid.some((el) => el.kind === "table" && el.table.overlay)) return {
|
|
688
|
+
grid: [...grid],
|
|
689
|
+
loose: [...drawings]
|
|
690
|
+
};
|
|
691
|
+
const taken = /* @__PURE__ */ new Set();
|
|
692
|
+
return {
|
|
693
|
+
grid: grid.map((el) => {
|
|
694
|
+
if (el.kind !== "table" || !el.table.overlay) return el;
|
|
695
|
+
const o = el.table.overlay;
|
|
696
|
+
const over = drawings.filter((d) => {
|
|
697
|
+
const x = floatLeftPt(d);
|
|
698
|
+
const w = floatWidthPt(d) ?? 0;
|
|
699
|
+
return x !== void 0 && x < o.sheetLeftPt + o.widthPt && x + w > o.sheetLeftPt;
|
|
700
|
+
});
|
|
701
|
+
for (const d of over) taken.add(d);
|
|
702
|
+
if (over.length === 0) {
|
|
703
|
+
const { overlay: _none, ...table } = el.table;
|
|
704
|
+
return {
|
|
705
|
+
kind: "table",
|
|
706
|
+
table
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
return {
|
|
710
|
+
kind: "table",
|
|
711
|
+
table: {
|
|
712
|
+
...el.table,
|
|
713
|
+
overlay: {
|
|
714
|
+
...o,
|
|
715
|
+
drawings: over
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
}),
|
|
720
|
+
loose: drawings.filter((d) => !taken.has(d) && (!printArea || floatLeftPt(d) === void 0))
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Where each of the sheet's printed drawings lies, unscaled, in the sheet's
|
|
725
|
+
* own frame — what a band keeps its rows under (PrintModelOptions
|
|
726
|
+
* .drawingBoxesPt).
|
|
727
|
+
*
|
|
728
|
+
* @param ws The sheet.
|
|
729
|
+
* @param shapes The controls and shown notes drawn as shapes, besides its own.
|
|
730
|
+
* @returns The boxes.
|
|
731
|
+
*/
|
|
732
|
+
function drawingBoxes(ws, shapes) {
|
|
733
|
+
const boxes = [];
|
|
734
|
+
const add = (xPt, yPt, w, h) => {
|
|
735
|
+
if (xPt !== void 0 && yPt !== void 0) boxes.push({
|
|
736
|
+
xPt,
|
|
737
|
+
yPt,
|
|
738
|
+
widthPt: w,
|
|
739
|
+
heightPt: h
|
|
740
|
+
});
|
|
741
|
+
};
|
|
742
|
+
for (const c of ws.charts ?? []) if (!c.screenOnly) add(c.xPt, c.yPt, c.widthPt, c.heightPt);
|
|
743
|
+
for (const p of ws.images ?? []) if (!p.screenOnly) add(p.xPt, p.yPt, p.widthPt, p.heightPt);
|
|
744
|
+
for (const [i, shape] of [...ws.shapes ?? [], ...shapes].entries()) {
|
|
745
|
+
if (i < (ws.shapes ?? []).length && ws.screenOnlyShapes?.has(i)) continue;
|
|
746
|
+
add(shape.float?.posH?.offsetPt, shape.float?.posV?.offsetPt, shape.width, shape.height);
|
|
747
|
+
}
|
|
748
|
+
return boxes;
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* §18.3.1.87 `rightToLeft` — a drawing on a sheet that reads from the right is
|
|
752
|
+
* anchored to the same cells, and they stand mirrored: it keeps its size and
|
|
753
|
+
* its own orientation (Excel does not turn a right arrow round) but its place
|
|
754
|
+
* is counted from the grid's right edge. Each float is measured against the
|
|
755
|
+
* table it was placed beside — a band's own when the sheet bands, the first
|
|
756
|
+
* one for the floats that go in ahead of the grid — and on paper that table
|
|
757
|
+
* stands against the right margin (mirrorTable), or in the middle when the
|
|
758
|
+
* sheet asks to be centred.
|
|
759
|
+
*
|
|
760
|
+
* @param elements The sheet's grid and floats, as withDrawingsByBand placed them.
|
|
761
|
+
* @param contentWidthPt The width between the margins, or undefined for a screen.
|
|
762
|
+
* @param extentWidthPt How far the drawings reach, for a sheet with no table.
|
|
763
|
+
*/
|
|
764
|
+
function mirrorDrawings(elements, contentWidthPt, extentWidthPt) {
|
|
765
|
+
let table = elements.find((el) => el.kind === "table")?.table;
|
|
766
|
+
const out = [];
|
|
767
|
+
for (const el of elements) {
|
|
768
|
+
if (el.kind === "table") table = el.table;
|
|
769
|
+
const x = floatLeftPt(el);
|
|
770
|
+
const w = floatWidthPt(el);
|
|
771
|
+
if (el.kind === "table" || x === void 0 || w === void 0) {
|
|
772
|
+
out.push(el);
|
|
773
|
+
continue;
|
|
774
|
+
}
|
|
775
|
+
const gridWidth = table ? table.grid.reduce((sum, c) => sum + c, 0) : extentWidthPt;
|
|
776
|
+
const left = contentWidthPt === void 0 ? 0 : table?.properties.alignment === "center" ? (contentWidthPt - gridWidth) / 2 : contentWidthPt - gridWidth;
|
|
777
|
+
out.push(shiftFloatLeft(el, x - (left + gridWidth - x - w)));
|
|
778
|
+
}
|
|
779
|
+
return out;
|
|
780
|
+
}
|
|
436
781
|
/** A float's horizontal anchor, for the block kinds a sheet anchors. */
|
|
437
782
|
function floatLeftPt(el) {
|
|
438
783
|
return (el.kind === "chart" ? el.chart.float : el.kind === "image" ? el.image.float : el.kind === "shape" ? el.shape.float : void 0)?.posH?.offsetPt;
|
|
@@ -443,6 +788,40 @@ function floatWidthPt(el) {
|
|
|
443
788
|
if (el.kind === "image") return el.image.width;
|
|
444
789
|
if (el.kind === "shape") return el.shape.width;
|
|
445
790
|
}
|
|
791
|
+
/** The same block with its anchor moved `dx` right and `dy` down. */
|
|
792
|
+
function nudgeFloat(el, dx, dy) {
|
|
793
|
+
const moved = (block) => {
|
|
794
|
+
const f = block.float;
|
|
795
|
+
if (!f) return block;
|
|
796
|
+
return {
|
|
797
|
+
...block,
|
|
798
|
+
float: {
|
|
799
|
+
...f,
|
|
800
|
+
...f.posH ? { posH: {
|
|
801
|
+
...f.posH,
|
|
802
|
+
offsetPt: pt((f.posH.offsetPt ?? 0) + dx)
|
|
803
|
+
} } : {},
|
|
804
|
+
...f.posV ? { posV: {
|
|
805
|
+
...f.posV,
|
|
806
|
+
offsetPt: pt((f.posV.offsetPt ?? 0) + dy)
|
|
807
|
+
} } : {}
|
|
808
|
+
}
|
|
809
|
+
};
|
|
810
|
+
};
|
|
811
|
+
if (el.kind === "chart") return {
|
|
812
|
+
...el,
|
|
813
|
+
chart: moved(el.chart)
|
|
814
|
+
};
|
|
815
|
+
if (el.kind === "image") return {
|
|
816
|
+
...el,
|
|
817
|
+
image: moved(el.image)
|
|
818
|
+
};
|
|
819
|
+
if (el.kind === "shape") return {
|
|
820
|
+
...el,
|
|
821
|
+
shape: moved(el.shape)
|
|
822
|
+
};
|
|
823
|
+
return el;
|
|
824
|
+
}
|
|
446
825
|
/** The same block with its horizontal anchor measured from `by` points later. */
|
|
447
826
|
function shiftFloatLeft(el, by) {
|
|
448
827
|
if (by === 0) return el;
|
|
@@ -650,21 +1029,29 @@ function activeXLabel(c) {
|
|
|
650
1029
|
* past the last value is still printed, and on a sheet whose values sit in a
|
|
651
1030
|
* handful of cells it is the drawing that decides the page.
|
|
652
1031
|
*/
|
|
653
|
-
function drawingReachPt(ws) {
|
|
1032
|
+
function drawingReachPt(ws, notesShown, screen) {
|
|
654
1033
|
let widthPt = 0;
|
|
655
1034
|
let heightPt = 0;
|
|
656
|
-
for (const shape of ws.shapes ?? []) {
|
|
1035
|
+
for (const [i, shape] of (ws.shapes ?? []).entries()) {
|
|
1036
|
+
if (!screen && ws.screenOnlyShapes?.has(i)) continue;
|
|
657
1037
|
widthPt = Math.max(widthPt, (shape.float?.posH?.offsetPt ?? 0) + shape.width);
|
|
658
1038
|
heightPt = Math.max(heightPt, (shape.float?.posV?.offsetPt ?? 0) + shape.height);
|
|
659
1039
|
}
|
|
660
1040
|
for (const c of ws.charts ?? []) {
|
|
1041
|
+
if (c.screenOnly && !screen) continue;
|
|
661
1042
|
widthPt = Math.max(widthPt, (c.xPt ?? 0) + c.widthPt);
|
|
662
1043
|
heightPt = Math.max(heightPt, (c.yPt ?? 0) + c.heightPt);
|
|
663
1044
|
}
|
|
664
1045
|
for (const p of ws.images ?? []) {
|
|
1046
|
+
if (p.screenOnly && !screen) continue;
|
|
665
1047
|
widthPt = Math.max(widthPt, (p.xPt ?? 0) + p.widthPt);
|
|
666
1048
|
heightPt = Math.max(heightPt, (p.yPt ?? 0) + p.heightPt);
|
|
667
1049
|
}
|
|
1050
|
+
for (const c of notesShown ? ws.comments ?? [] : []) {
|
|
1051
|
+
if (!c.shown) continue;
|
|
1052
|
+
widthPt = Math.max(widthPt, c.shown.xPt + c.shown.widthPt + NOTE_SHADOW_PT);
|
|
1053
|
+
heightPt = Math.max(heightPt, c.shown.yPt + c.shown.heightPt + NOTE_SHADOW_PT);
|
|
1054
|
+
}
|
|
668
1055
|
return widthPt > 0 || heightPt > 0 ? {
|
|
669
1056
|
widthPt,
|
|
670
1057
|
heightPt
|
|
@@ -685,7 +1072,7 @@ function centreShape(shape, worksheet, extent, scale) {
|
|
|
685
1072
|
const size = section.pageSize;
|
|
686
1073
|
const margins = section.margins;
|
|
687
1074
|
if (!size || !margins) return shape;
|
|
688
|
-
const dx = options.horizontalCentered ? (size.width - margins.left - margins.right - extent.widthPt * scale) / 2 : 0;
|
|
1075
|
+
const dx = options.horizontalCentered && worksheet.rightToLeft !== true ? (size.width - margins.left - margins.right - extent.widthPt * scale) / 2 : 0;
|
|
689
1076
|
const dy = options.verticalCentered ? (size.height - margins.top - margins.bottom - extent.heightPt * scale) / 2 : 0;
|
|
690
1077
|
if (dx <= 0 && dy <= 0) return shape;
|
|
691
1078
|
const posH = shape.float?.posH;
|
|
@@ -37,3 +37,12 @@ export declare function parseXlsxStyles(data: Uint8Array, theme?: ThemePalette):
|
|
|
37
37
|
* @returns The format; empty when the node states nothing.
|
|
38
38
|
*/
|
|
39
39
|
export declare function parseDxf(item: unknown, colors: WorkbookColors): Dxf;
|
|
40
|
+
/**
|
|
41
|
+
* A `<color>` of this workbook as 6-hex: its `rgb`, its `indexed` entry or its
|
|
42
|
+
* `theme` slot, with the `tint` applied (§18.8.3).
|
|
43
|
+
*
|
|
44
|
+
* @param node The `<color>` element, parsed.
|
|
45
|
+
* @param colors What the workbook's colours point at.
|
|
46
|
+
* @returns The colour, or undefined for one that names nothing reachable.
|
|
47
|
+
*/
|
|
48
|
+
export declare function workbookColorHex(node: unknown, colors: WorkbookColors): string | undefined;
|