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,5 +1,6 @@
|
|
|
1
1
|
import { eighthPtToPt, halfPtToPt, pt, twipsToPt } from "../core/ir/units.js";
|
|
2
2
|
import { FEATURES } from "../core/ir/features.js";
|
|
3
|
+
import { firstStrongDirection } from "../core/bidi/index.js";
|
|
3
4
|
import { applyNumberFormat, generalToWidth, numberFormatColorHex } from "../core/number-format.js";
|
|
4
5
|
import { parseAreaRef, parseTitleRowRange } from "./defined-name-ref.js";
|
|
5
6
|
import "./index.js";
|
|
@@ -35,27 +36,67 @@ function columnTwips(chars, charTwips) {
|
|
|
35
36
|
return columnTwipsOf(chars, charTwips);
|
|
36
37
|
}
|
|
37
38
|
/**
|
|
38
|
-
*
|
|
39
|
-
* sheet
|
|
39
|
+
* How far the first `count` rows (or columns) of a sheet reach, in twips: each
|
|
40
|
+
* the default unless the sheet gives it a size of its own.
|
|
40
41
|
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
*
|
|
48
|
-
*
|
|
42
|
+
* @param count How many tracks, from the first.
|
|
43
|
+
* @param defaultTwips The size of a track the sheet says nothing about.
|
|
44
|
+
* @param sized The tracks it does size, by 0-based index.
|
|
45
|
+
* @returns Their total.
|
|
46
|
+
*/
|
|
47
|
+
/**
|
|
48
|
+
* §20.5 — the part of the sheet a printed table shows, for the drawings over
|
|
49
|
+
* it (TableOverlay); empty until the drawings are put in. Nothing on a screen,
|
|
50
|
+
* which draws them over the whole sheet as it is.
|
|
51
|
+
*
|
|
52
|
+
* @param sheetLeftPt Where the table's first column stands on the sheet.
|
|
53
|
+
* @param columns The widths of the sheet's columns it prints.
|
|
54
|
+
* @param leftPt Where those begin in the table — past a row-number column.
|
|
55
|
+
* @returns The frame to spread into the table, or nothing.
|
|
56
|
+
*/
|
|
57
|
+
function sheetFrame(sheetLeftPt, columns, leftPt) {
|
|
58
|
+
if (sheetLeftPt === void 0) return {};
|
|
59
|
+
return { overlay: {
|
|
60
|
+
sheetLeftPt: pt(sheetLeftPt),
|
|
61
|
+
widthPt: pt(columns.reduce((sum, w) => sum + w, 0)),
|
|
62
|
+
leftPt: pt(leftPt),
|
|
63
|
+
drawings: []
|
|
64
|
+
} };
|
|
65
|
+
}
|
|
66
|
+
/** The drawings' boxes at the print scale. */
|
|
67
|
+
function scaledBoxes(boxes, scale) {
|
|
68
|
+
return (boxes ?? []).map((b) => ({
|
|
69
|
+
xPt: b.xPt * scale,
|
|
70
|
+
yPt: b.yPt * scale,
|
|
71
|
+
widthPt: b.widthPt * scale,
|
|
72
|
+
heightPt: b.heightPt * scale
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
function tracksBeforeTwips(count, defaultTwips, sized) {
|
|
76
|
+
let total = count * defaultTwips;
|
|
77
|
+
const seen = /* @__PURE__ */ new Set();
|
|
78
|
+
for (const t of sized) {
|
|
79
|
+
if (t.index >= count || seen.has(t.index)) continue;
|
|
80
|
+
seen.add(t.index);
|
|
81
|
+
total += t.twips - defaultTwips;
|
|
82
|
+
}
|
|
83
|
+
return total;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* §18.3.1.81 — the DEFAULT column, in twips: the sheet's `defaultColWidth`
|
|
87
|
+
* where it declares one — a stored width, padding included — and otherwise
|
|
88
|
+
* `baseColWidth` digits and their padding, rounded up to Excel's step of eight
|
|
89
|
+
* pixels. Padding a `baseColWidth` twice once stood in for that rounding: it
|
|
90
|
+
* gives 47668.xlsx's 80px as well, and nothing else.
|
|
49
91
|
*
|
|
50
|
-
* @param worksheet
|
|
51
|
-
* @param charTwips
|
|
52
|
-
* @param fallbackChars The width to use when the sheet declares neither.
|
|
92
|
+
* @param worksheet The sheet.
|
|
93
|
+
* @param charTwips The Maximum Digit Width, in twips.
|
|
53
94
|
* @returns The default column width in twips.
|
|
54
95
|
*/
|
|
55
|
-
function defaultColumnTwips(worksheet, charTwips
|
|
96
|
+
function defaultColumnTwips(worksheet, charTwips) {
|
|
56
97
|
if (worksheet.defaultColWidthChars !== void 0) return columnTwipsOf(worksheet.defaultColWidthChars, charTwips);
|
|
57
|
-
|
|
58
|
-
return
|
|
98
|
+
const px = ((worksheet.baseColWidthChars ?? DEFAULT_BASE_COL_CHARS) * charTwips + 75) / TWIPS_PER_PIXEL;
|
|
99
|
+
return Math.ceil(px / 8) * 8 * TWIPS_PER_PIXEL;
|
|
59
100
|
}
|
|
60
101
|
function columnTwipsOf(chars, charTwips) {
|
|
61
102
|
const mdwPx = charTwips / TWIPS_PER_PIXEL;
|
|
@@ -68,14 +109,8 @@ function columnTwipsOf(chars, charTwips) {
|
|
|
68
109
|
* four times as much.
|
|
69
110
|
*/
|
|
70
111
|
var EXCEL_CELL_INSET_PT = 1.5;
|
|
71
|
-
/**
|
|
72
|
-
|
|
73
|
-
*
|
|
74
|
-
* In the STORED unit, which is what {@link columnTwips} reads: Excel's default
|
|
75
|
-
* column is "8.43 characters" in its own interface and `width="9.140625"` in
|
|
76
|
-
* every file that writes it out, because the stored form carries the padding.
|
|
77
|
-
*/
|
|
78
|
-
var DEFAULT_COL_CHARS = 9.140625;
|
|
112
|
+
/** §18.3.1.81 `baseColWidth` — the digits a default column holds when the sheet does not say. */
|
|
113
|
+
var DEFAULT_BASE_COL_CHARS = 8;
|
|
79
114
|
/**
|
|
80
115
|
* The column-width unit in twips: the Maximum Digit Width of the font the
|
|
81
116
|
* document is rendered in (§18.3.1.13), or Excel's own 7 px when the caller has
|
|
@@ -89,12 +124,8 @@ var DEFAULT_COL_CHARS = 9.140625;
|
|
|
89
124
|
* are listed beside their originals because a file naming one is measured in
|
|
90
125
|
* the other's unit by every reader that has to substitute.
|
|
91
126
|
*
|
|
92
|
-
* Every entry here is measured.
|
|
93
|
-
*
|
|
94
|
-
* 72px, and §18.3.1.13's forward formula turns that into exactly the
|
|
95
|
-
* `width="9"` 54524.xlsx writes for its default band — but I have no copy of
|
|
96
|
-
* the face to measure, and fitting 0.55 to it moved 50299.xlsx from 16 pages
|
|
97
|
-
* to 22 against LibreOffice's 17. A face we cannot measure keeps the old 7px.
|
|
127
|
+
* Every Latin entry here is measured. A third number, where there is one, is
|
|
128
|
+
* what Excel adds in whole pixels to the digit it measures (see the CJK entry).
|
|
98
129
|
*/
|
|
99
130
|
var DIGIT_EM = [
|
|
100
131
|
[/^(calibri|carlito)$/i, 1063 / 2048],
|
|
@@ -102,9 +133,45 @@ var DIGIT_EM = [
|
|
|
102
133
|
[/^(times new roman|liberation serif|tinos)$/i, 1024 / 2048],
|
|
103
134
|
[/^(tahoma)$/i, 1118 / 2048],
|
|
104
135
|
[/^(verdana|dejavu sans)$/i, 1303 / 2048],
|
|
105
|
-
[
|
|
136
|
+
[
|
|
137
|
+
/* @__PURE__ */ new RegExp("^(新細明體|細明體|pmingliu|mingliu|標楷體|dfkai-sb|宋体|新宋体|simsun|nsimsun|黑体|simhei|微软雅黑|microsoft yahei|微軟正黑體|microsoft jhenghei|MS [P]?明朝|MS [P]?ゴシック|ms [p]?mincho|ms [p]?gothic|meiryo|メイリオ|游ゴシック|yu gothic|굴림체?|gulim(che)?|돋움체?|dotum(che)?|바탕체?|batang(che)?|맑은 고딕|malgun gothic)$", "iu"),
|
|
138
|
+
1024 / 2048,
|
|
139
|
+
1
|
|
140
|
+
]
|
|
106
141
|
];
|
|
107
142
|
/**
|
|
143
|
+
* The space advance, as a fraction of the em, of the faces a spreadsheet names
|
|
144
|
+
* for its normal style — §18.8.1 measures a cell's indent in them. Read off
|
|
145
|
+
* each face's `hmtx` for U+0020, the metric twins beside their originals; a
|
|
146
|
+
* face not listed takes a quarter em, which is where most spaces sit.
|
|
147
|
+
*/
|
|
148
|
+
var SPACE_EM = [
|
|
149
|
+
[/^(calibri|carlito)$/i, 463 / 2048],
|
|
150
|
+
[/^(arial|helvetica|liberation sans|arimo|arial unicode ms)$/i, 569 / 2048],
|
|
151
|
+
[/^(times new roman|liberation serif|tinos)$/i, 512 / 2048],
|
|
152
|
+
[/^(cambria|caladea)$/i, 220 / 1e3],
|
|
153
|
+
[/^(tahoma)$/i, 640 / 2048],
|
|
154
|
+
[/^(verdana|dejavu sans)$/i, 720 / 2048]
|
|
155
|
+
];
|
|
156
|
+
/**
|
|
157
|
+
* One level of §18.8.1's `indent`, in twips: "an increment of 1 represents 3
|
|
158
|
+
* spaces … of the normal style font". It was counted in DIGITS, three of them,
|
|
159
|
+
* which is twice as far — simple-monthly-budget.xlsx indents its "Amount"
|
|
160
|
+
* headings two levels and we set them 31.5pt in where LibreOffice sets them
|
|
161
|
+
* 15pt (it measures 7.49pt a level in Calibri 11, 8.39pt in Arial 10: three
|
|
162
|
+
* spaces of each).
|
|
163
|
+
*
|
|
164
|
+
* @param styles The workbook's styles; the first font is the normal style's.
|
|
165
|
+
* @returns The width of one indent level, in twips.
|
|
166
|
+
*/
|
|
167
|
+
function indentLevelTwips(styles) {
|
|
168
|
+
const font = styles.fonts[0];
|
|
169
|
+
const size = font?.sizePt !== void 0 && Number.isFinite(font.sizePt) && font.sizePt > 0 ? font.sizePt : DEFAULT_FONT_PT;
|
|
170
|
+
const name = font?.name?.trim() ?? "";
|
|
171
|
+
const em = SPACE_EM.find(([re]) => re.test(name))?.[1] ?? .25;
|
|
172
|
+
return Math.round(3 * em * size * TWIPS_PER_POINT);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
108
175
|
* §18.3.1.13 — the Maximum Digit Width, in twips, of the normal style's font.
|
|
109
176
|
*
|
|
110
177
|
* "The number of characters of the maximum digit width of the numbers 0, 1,
|
|
@@ -123,11 +190,29 @@ var DIGIT_EM = [
|
|
|
123
190
|
*/
|
|
124
191
|
function maximumDigitTwips(name, sizePt) {
|
|
125
192
|
const size = sizePt !== void 0 && Number.isFinite(sizePt) && sizePt > 0 ? sizePt : 11;
|
|
126
|
-
const
|
|
127
|
-
if (
|
|
128
|
-
const
|
|
193
|
+
const face = DIGIT_EM.find(([re]) => re.test(name.trim()));
|
|
194
|
+
if (face === void 0) return 105;
|
|
195
|
+
const [, em, extraPx = 0] = face;
|
|
196
|
+
const px = Math.trunc(em * size * (96 / 72)) + extraPx;
|
|
129
197
|
return px > 0 ? px * TWIPS_PER_PIXEL : 105;
|
|
130
198
|
}
|
|
199
|
+
/**
|
|
200
|
+
* §18.3.1.13 — the unit a sheet's columns are measured in: the Maximum Digit
|
|
201
|
+
* Width of the workbook's Normal font, in twips — the font's own where the
|
|
202
|
+
* workbook names it, the face we render with where it names none. A drawing's
|
|
203
|
+
* anchor counts columns in the same unit, or it lands beside the cells it is
|
|
204
|
+
* anchored to: 45540_form_Footer.xlsx sets its form in 宋体 12, whose digit
|
|
205
|
+
* is 8px, and anchored at Excel's 7px its check boxes stood a column off
|
|
206
|
+
* their labels.
|
|
207
|
+
*
|
|
208
|
+
* @param styles The workbook's styles (its first font is the Normal style's).
|
|
209
|
+
* @param digitWidthPt The render font's digit width, for a workbook that names no font.
|
|
210
|
+
* @returns The unit, in twips.
|
|
211
|
+
*/
|
|
212
|
+
function columnUnitTwips(styles, digitWidthPt) {
|
|
213
|
+
const font = styles.fonts[0];
|
|
214
|
+
return font?.name === void 0 ? digitTwips(digitWidthPt) : maximumDigitTwips(font.name, font.sizePt);
|
|
215
|
+
}
|
|
131
216
|
function digitTwips(digitWidthPt) {
|
|
132
217
|
if (digitWidthPt === void 0 || !Number.isFinite(digitWidthPt) || digitWidthPt <= 0) return 105;
|
|
133
218
|
return digitWidthPt * TWIPS_PER_POINT;
|
|
@@ -398,6 +483,11 @@ function scaleRunFont(props, scale) {
|
|
|
398
483
|
};
|
|
399
484
|
}
|
|
400
485
|
var PRINT_GRIDLINE_HEX = "C0C0C0";
|
|
486
|
+
var SCREEN_GRIDLINE = {
|
|
487
|
+
style: "single",
|
|
488
|
+
width: pt(.5),
|
|
489
|
+
colorHex: "D4D4D4"
|
|
490
|
+
};
|
|
401
491
|
var CF_NUMBER_FORMAT_ID = 1000001;
|
|
402
492
|
var MAX_SHEET_TEXT_CHARS = 1e6;
|
|
403
493
|
/**
|
|
@@ -417,8 +507,18 @@ var MAX_SHEET_TEXT_CHARS = 1e6;
|
|
|
417
507
|
* blank or empty-print-area sheet.
|
|
418
508
|
*/
|
|
419
509
|
function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
|
|
510
|
+
const body = gridBody(worksheet, sharedStrings, styles, date1904, print);
|
|
511
|
+
if (worksheet.rightToLeft !== true) return body;
|
|
512
|
+
return body.map((el) => el.kind === "table" ? {
|
|
513
|
+
...el,
|
|
514
|
+
table: mirrorTable(el.table, print.screen === true)
|
|
515
|
+
} : el);
|
|
516
|
+
}
|
|
517
|
+
function gridBody(worksheet, sharedStrings, styles, date1904, print) {
|
|
420
518
|
if (worksheet.maxRow < 0 || worksheet.maxColumn < 0) return [];
|
|
421
|
-
const
|
|
519
|
+
const rtl = worksheet.rightToLeft === true;
|
|
520
|
+
const indentTwips = indentLevelTwips(styles);
|
|
521
|
+
const charTwipsUnit = columnUnitTwips(styles, print.digitWidthPt);
|
|
422
522
|
const textTwipsUnit = 105;
|
|
423
523
|
const rowStyleAt = /* @__PURE__ */ new Map();
|
|
424
524
|
for (const r of worksheet.rowStyles ?? []) rowStyleAt.set(r.row, r.styleIndex);
|
|
@@ -449,7 +549,7 @@ function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
|
|
|
449
549
|
}
|
|
450
550
|
if (usedRow >= 0 && usedCol >= 0 && print.drawingExtentPt) {
|
|
451
551
|
const wantTwips = Math.round(print.drawingExtentPt.widthPt * TWIPS_PER_POINT);
|
|
452
|
-
const defTwips = defaultColumnTwips(worksheet, charTwipsUnit
|
|
552
|
+
const defTwips = defaultColumnTwips(worksheet, charTwipsUnit);
|
|
453
553
|
const widthAt = (abs) => {
|
|
454
554
|
for (const col of worksheet.columns) {
|
|
455
555
|
if (abs < col.min - 1 || abs > col.max - 1) continue;
|
|
@@ -462,6 +562,15 @@ function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
|
|
|
462
562
|
acc += widthAt(col);
|
|
463
563
|
if (col > usedCol) usedCol = col;
|
|
464
564
|
}
|
|
565
|
+
const wantRowTwips = Math.round(print.drawingExtentPt.heightPt * TWIPS_PER_POINT);
|
|
566
|
+
const defRowTwips = Math.round((worksheet.defaultRowHeightPt ?? defaultRowHeightPtFor(print.defaultFontPt)) * TWIPS_PER_POINT);
|
|
567
|
+
const heightAt = new Map(worksheet.rowHeights.map((h) => [h.row, h.hidden ? 0 : Math.round(h.heightPt * TWIPS_PER_POINT)]));
|
|
568
|
+
const rowReach = usedRow + PAINT_REACH_ROWS;
|
|
569
|
+
let down = 0;
|
|
570
|
+
for (let row = 0; row <= rowReach && down <= wantRowTwips; row++) {
|
|
571
|
+
down += heightAt.get(row) ?? defRowTwips;
|
|
572
|
+
if (row > usedRow) usedRow = row;
|
|
573
|
+
}
|
|
465
574
|
}
|
|
466
575
|
if (usedRow >= 0 && usedCol >= 0) {
|
|
467
576
|
const colReach = usedCol + PAINT_REACH_COLUMNS;
|
|
@@ -476,7 +585,7 @@ function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
|
|
|
476
585
|
}
|
|
477
586
|
if (usedRow < 0 && usedCol < 0) {
|
|
478
587
|
const pageTwips = sheetContentWidthTwips(worksheet);
|
|
479
|
-
const defTwips = defaultColumnTwips(worksheet, charTwipsUnit
|
|
588
|
+
const defTwips = defaultColumnTwips(worksheet, charTwipsUnit);
|
|
480
589
|
let acc = 0;
|
|
481
590
|
let colLimit = 0;
|
|
482
591
|
while (colLimit < PAINT_REACH_COLUMNS && acc < pageTwips) {
|
|
@@ -501,7 +610,7 @@ function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
|
|
|
501
610
|
});
|
|
502
611
|
return [];
|
|
503
612
|
}
|
|
504
|
-
usedCol += overflowColumnsPastUsedRange(worksheet, usedCol, sharedStrings, styles, date1904, charTwipsUnit);
|
|
613
|
+
usedCol += overflowColumnsPastUsedRange(worksheet, usedCol, sharedStrings, styles, date1904, charTwipsUnit, print.screen === true, print.image === true);
|
|
505
614
|
let rowStart = 0;
|
|
506
615
|
let colStart = 0;
|
|
507
616
|
let rowEnd = usedRow;
|
|
@@ -569,7 +678,7 @@ function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
|
|
|
569
678
|
const cEnd = Math.min(m.endColumn, colWindowEnd);
|
|
570
679
|
for (let r = Math.max(m.startRow, rowStart); r <= rEnd; r++) for (let c = Math.max(m.startColumn, colStart); c <= cEnd; c++) if (!(r === m.startRow && c === m.startColumn)) insideMerge.add(key(r, c));
|
|
571
680
|
}
|
|
572
|
-
const defaultColTwips = defaultColumnTwips(worksheet, charTwipsUnit
|
|
681
|
+
const defaultColTwips = defaultColumnTwips(worksheet, charTwipsUnit);
|
|
573
682
|
const columnWidths = new Array(colCount).fill(defaultColTwips);
|
|
574
683
|
const hiddenCols = /* @__PURE__ */ new Set();
|
|
575
684
|
for (const col of worksheet.columns) {
|
|
@@ -607,7 +716,7 @@ function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
|
|
|
607
716
|
if (manualColBreaks.has(c + colStart)) fitColBreaks.add(i);
|
|
608
717
|
});
|
|
609
718
|
const drawing = print.drawingExtentPt;
|
|
610
|
-
const printScale = computePrintScale(worksheet, Math.max(totalGridTwips, Math.round((drawing?.widthPt ?? 0) * TWIPS_PER_POINT)), sheetContentWidthTwips(worksheet), Math.max(totalGridHeightTwips, Math.round((drawing?.heightPt ?? 0) * TWIPS_PER_POINT)), sheetContentHeightTwips(worksheet), {
|
|
719
|
+
const printScale = print.screen ? 1 : computePrintScale(worksheet, Math.max(totalGridTwips, Math.round((drawing?.widthPt ?? 0) * TWIPS_PER_POINT)), sheetContentWidthTwips(worksheet), Math.max(totalGridHeightTwips, Math.round((drawing?.heightPt ?? 0) * TWIPS_PER_POINT)), sheetContentHeightTwips(worksheet), {
|
|
611
720
|
rowTwips: fitRowTwips,
|
|
612
721
|
rowBreaks: fitRowBreaks,
|
|
613
722
|
titleTwips: fitTitleTwips,
|
|
@@ -616,7 +725,20 @@ function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
|
|
|
616
725
|
});
|
|
617
726
|
const scaled = printScale < .999;
|
|
618
727
|
if (print.scaleSink) print.scaleSink.value = printScale;
|
|
619
|
-
|
|
728
|
+
let sheetTopTwips = print.screen ? void 0 : tracksBeforeTwips(rowStart, defaultRowTwips, worksheet.rowHeights.map((h) => ({
|
|
729
|
+
index: h.row,
|
|
730
|
+
twips: h.hidden ? 0 : Math.round(h.heightPt * TWIPS_PER_POINT)
|
|
731
|
+
}))) * printScale;
|
|
732
|
+
const sheetLeftPt = print.screen ? void 0 : twipsToPt(Math.round(tracksBeforeTwips(colStart, defaultColTwips, worksheet.columns.flatMap((col) => {
|
|
733
|
+
const twips = col.hidden ? 0 : columnTwips(col.widthChars, charTwipsUnit);
|
|
734
|
+
const out = [];
|
|
735
|
+
for (let abs = col.min - 1; abs <= Math.min(col.max - 1, colStart - 1); abs++) out.push({
|
|
736
|
+
index: abs,
|
|
737
|
+
twips
|
|
738
|
+
});
|
|
739
|
+
return out;
|
|
740
|
+
})) * printScale));
|
|
741
|
+
const breakRows = new Set(print.screen ? [] : honouredBreaks(worksheet, "rows"));
|
|
620
742
|
let textBudget = MAX_SHEET_TEXT_CHARS;
|
|
621
743
|
let textBudgetReported = false;
|
|
622
744
|
const runPropsByXf = /* @__PURE__ */ new Map();
|
|
@@ -630,17 +752,23 @@ function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
|
|
|
630
752
|
return props;
|
|
631
753
|
};
|
|
632
754
|
const paraPropsByAlignment = /* @__PURE__ */ new Map();
|
|
633
|
-
const cellParaProps = (alignment) => {
|
|
634
|
-
|
|
755
|
+
const cellParaProps = (alignment, rtlText) => {
|
|
756
|
+
const memoKey = `${alignment ?? ""}|${rtlText ? "rtl" : ""}`;
|
|
757
|
+
let props = paraPropsByAlignment.get(memoKey);
|
|
635
758
|
if (props === void 0) {
|
|
636
|
-
|
|
637
|
-
|
|
759
|
+
const stored = rtlText ? crossedOver(alignment) : alignment;
|
|
760
|
+
props = {
|
|
761
|
+
...stored ? { alignment: stored } : {},
|
|
762
|
+
...rtlText ? { bidi: true } : {}
|
|
763
|
+
};
|
|
764
|
+
paraPropsByAlignment.set(memoKey, props);
|
|
638
765
|
}
|
|
639
766
|
return props;
|
|
640
767
|
};
|
|
641
768
|
const cfFormatter = buildConditionalFormatter(worksheet.conditionalFormats, styles, worksheet.cells, (cell) => resolveCellText(cell, sharedStrings, styles, date1904), date1904, print.now, print.sheetGrids, print.sheetName, print.definedNames);
|
|
642
769
|
const sparklineByCell = buildSparklineLookup(worksheet, print);
|
|
643
770
|
const tableFormatByCell = buildTableFormatLookup(worksheet);
|
|
771
|
+
const normalColorRef = styles.fonts[0]?.colorRef;
|
|
644
772
|
const dropdownRanges = listDropdownRanges(worksheet.dataValidations);
|
|
645
773
|
const bandEndOfCol = /* @__PURE__ */ new Map();
|
|
646
774
|
{
|
|
@@ -653,7 +781,7 @@ function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
|
|
|
653
781
|
}
|
|
654
782
|
const wide = worksheet.fitToPage ? worksheet.pageSetup?.fitToWidth ?? 1 : 1;
|
|
655
783
|
const width = sheetContentWidthTwips(worksheet);
|
|
656
|
-
if (colCount > 1 && bandsAcross(worksheet, wide) && (total > width || breaks.size > 0)) for (const band of computeColumnBands(widthsForBands, width, breaks)) for (let i = band.start; i <= band.end; i++) bandEndOfCol.set(visibleCols[i] ?? i, visibleCols[band.end] ?? band.end);
|
|
784
|
+
if (!print.screen && colCount > 1 && bandsAcross(worksheet, wide) && (total > width || breaks.size > 0)) for (const band of computeColumnBands(widthsForBands, width, breaks)) for (let i = band.start; i <= band.end; i++) bandEndOfCol.set(visibleCols[i] ?? i, visibleCols[band.end] ?? band.end);
|
|
657
785
|
}
|
|
658
786
|
const rows = [];
|
|
659
787
|
const rowNumbers = [];
|
|
@@ -709,22 +837,35 @@ function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
|
|
|
709
837
|
textBudget -= text.length;
|
|
710
838
|
const xf = ws ? styles.cellXfs[ws.styleIndex ?? 0] : defaultStyleAt(absR, absC, styles.cellXfs);
|
|
711
839
|
let runProps = cellRunProps(xf);
|
|
840
|
+
let normalColored = (xf ? styles.fonts[xf.fontId] : styles.fonts[0])?.colorRef === normalColorRef;
|
|
712
841
|
if (ws?.type === "n" && xf?.numFmtId) {
|
|
713
842
|
const fmtColor = numberFormatColorHex(ws.rawValue, xf.numFmtId, styles.numFmts);
|
|
714
|
-
if (fmtColor !== void 0 && fmtColor !== runProps.colorHex)
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
843
|
+
if (fmtColor !== void 0 && fmtColor !== runProps.colorHex) {
|
|
844
|
+
runProps = {
|
|
845
|
+
...runProps,
|
|
846
|
+
colorHex: fmtColor
|
|
847
|
+
};
|
|
848
|
+
normalColored = false;
|
|
849
|
+
}
|
|
718
850
|
}
|
|
719
|
-
const alignment = alignmentFromXf(xf, ws?.type);
|
|
851
|
+
const alignment = alignmentFromXf(xf, ws?.type, text);
|
|
852
|
+
const flow = flowAlignment(alignment, rtl);
|
|
720
853
|
let shading = xf ? shadingFromXf(xf, styles) : void 0;
|
|
721
854
|
const tableFmt = tableFormatByCell.get(key(absR, absC));
|
|
722
855
|
if (!shading && tableFmt?.shading) shading = tableFmt.shading;
|
|
723
|
-
if (tableFmt?.fontColorHex) runProps = {
|
|
856
|
+
if (tableFmt?.fontColorHex && normalColored) runProps = {
|
|
724
857
|
...runProps,
|
|
725
858
|
colorHex: tableFmt.fontColorHex
|
|
726
859
|
};
|
|
860
|
+
if (tableFmt?.bold && !runProps.bold) runProps = {
|
|
861
|
+
...runProps,
|
|
862
|
+
bold: true
|
|
863
|
+
};
|
|
727
864
|
let borders = xf ? scaleBorders(bordersFromXf(xf, styles), printScale) : void 0;
|
|
865
|
+
if (tableFmt?.borders) borders = {
|
|
866
|
+
...scaleBorders(mapXlsxBorder(tableFmt.borders), printScale),
|
|
867
|
+
...borders
|
|
868
|
+
};
|
|
728
869
|
let dataBar;
|
|
729
870
|
let icon;
|
|
730
871
|
const sparkline = sparklineByCell.get(key(absR, absC));
|
|
@@ -751,7 +892,7 @@ function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
|
|
|
751
892
|
const rotation = xf?.alignment?.textRotation;
|
|
752
893
|
const rotated = rotation !== void 0 && rotation !== 0 && text.length > 0;
|
|
753
894
|
const shrinkToFit = xf?.alignment?.shrinkToFit === true && !wrapText && !rotated;
|
|
754
|
-
if (text.length > 0 && !merge && !wrapText && !rotated && !shrinkToFit && ws && (ws.type === "s" || ws.type === "str" || ws.type === "inlineStr") && (
|
|
895
|
+
if (text.length > 0 && !merge && !wrapText && !rotated && !shrinkToFit && ws && (ws.type === "s" || ws.type === "str" || ws.type === "inlineStr") && (flow === "left" || flow === "center") && estimateChars(text) * charTwips(xf, styles, textTwipsUnit) > columnWidths[c]) {
|
|
755
896
|
let availTwips = columnWidths[c];
|
|
756
897
|
let cc = c + 1;
|
|
757
898
|
const neighbourIsFree = (col) => !cellHasContent(cellMatrix[r]?.[col]) && !insideMerge.has(key(absR, col + colStart)) && !mergeOrigins.has(key(absR, col + colStart)) && (!cellPaintsSomething(cellMatrix[r]?.[col], styles) || spanPreservesPaint(cellMatrix[r]?.[col], styles, shading, borders)) && !sparklineByCell.has(key(absR, col + colStart)) && !(dropdownRanges.length > 0 && rangesCover(dropdownRanges, absR, col + colStart));
|
|
@@ -798,6 +939,7 @@ function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
|
|
|
798
939
|
};
|
|
799
940
|
}
|
|
800
941
|
}
|
|
942
|
+
const noteFlag = print.noteFlags?.get(noteKey(absR, absC));
|
|
801
943
|
const dropdown = dropdownRanges.length > 0 && rangesCover(dropdownRanges, absR, absC);
|
|
802
944
|
const href = print.hyperlinks && print.hyperlinks.length > 0 ? hyperlinkUrlAt(print.hyperlinks, absR, absC) : void 0;
|
|
803
945
|
const visibleEndCol = merge ? Math.min(merge.endColumn, colWindowEnd) : 0;
|
|
@@ -820,15 +962,19 @@ function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
|
|
|
820
962
|
...overflowRightRule ? { right: overflowRightRule } : {}
|
|
821
963
|
} } : {},
|
|
822
964
|
...dropdown ? { dropdown: true } : {},
|
|
965
|
+
...noteFlag ? { noteFlag } : {},
|
|
823
966
|
...!wrapText && !rotated ? { noWrap: true } : {},
|
|
824
967
|
...!wrapText && !rotated && !shrinkToFit && !merge && ws?.type === "n" && (xf?.numFmtId ?? 0) !== 0 && text.length > 0 && tooWideToShow(text, charTwips(xf, styles, textTwipsUnit), columnWidths[c]) ? { hashOnOverflow: true } : {},
|
|
825
968
|
verticalAlign: verticalAlignOf(xf)
|
|
826
969
|
};
|
|
827
970
|
const indentLevels = xf?.alignment?.indent ?? 0;
|
|
828
|
-
const
|
|
829
|
-
const
|
|
971
|
+
const rtlText = textDirection(xf, text) === "rtl";
|
|
972
|
+
const baseParaProps = cellParaProps(alignment, rtlText);
|
|
973
|
+
const indentPt = twipsToPt(indentLevels * indentTwips);
|
|
974
|
+
const indentAtRight = alignment === "right" !== rtlText;
|
|
975
|
+
const paragraphProps = indentLevels > 0 && alignment !== "center" ? {
|
|
830
976
|
...baseParaProps,
|
|
831
|
-
|
|
977
|
+
...indentAtRight ? { indentRight: indentPt } : { indentLeft: indentPt }
|
|
832
978
|
} : baseParaProps;
|
|
833
979
|
const richRuns = ws && ws.type === "s" && print.sharedStringRuns ? print.sharedStringRuns[Number(ws.rawValue)] : void 0;
|
|
834
980
|
const cellRuns = richRuns && richRuns.length > 0 ? richRuns.map((rr) => ({
|
|
@@ -847,7 +993,7 @@ function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
|
|
|
847
993
|
runs
|
|
848
994
|
}
|
|
849
995
|
}));
|
|
850
|
-
if (text.length > 0 && !merge && !wrapText && !rotated && !shrinkToFit && c > 0 &&
|
|
996
|
+
if (text.length > 0 && !merge && !wrapText && !rotated && !shrinkToFit && c > 0 && flow === "right" && estimateChars(text) * charTwips(xf, styles, textTwipsUnit) > columnWidths[c]) leftOverflow.push({
|
|
851
997
|
index: cells.length,
|
|
852
998
|
column: c,
|
|
853
999
|
needTwips: estimateChars(text) * charTwips(xf, styles, textTwipsUnit) - columnWidths[c],
|
|
@@ -884,17 +1030,27 @@ function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
|
|
|
884
1030
|
};
|
|
885
1031
|
if (isTitleRow && titleRowIndex < 0) titleRowIndex = rows.length;
|
|
886
1032
|
rowNumbers.push(absR + 1);
|
|
887
|
-
rows.push({
|
|
1033
|
+
if (sheetTopTwips === void 0) rows.push({
|
|
888
1034
|
properties: rowProps,
|
|
889
1035
|
cells
|
|
890
1036
|
});
|
|
1037
|
+
else {
|
|
1038
|
+
rows.push({
|
|
1039
|
+
properties: {
|
|
1040
|
+
...rowProps,
|
|
1041
|
+
sheetTopPt: twipsToPt(sheetTopTwips)
|
|
1042
|
+
},
|
|
1043
|
+
cells
|
|
1044
|
+
});
|
|
1045
|
+
sheetTopTwips += rowHeightTwips ?? defaultRowTwips;
|
|
1046
|
+
}
|
|
891
1047
|
}
|
|
892
1048
|
const thin = {
|
|
893
1049
|
style: "single",
|
|
894
1050
|
width: eighthPtToPt(2),
|
|
895
1051
|
colorHex: PRINT_GRIDLINE_HEX
|
|
896
1052
|
};
|
|
897
|
-
const centered = worksheet.printOptions?.horizontalCentered === true;
|
|
1053
|
+
const centered = !print.screen && worksheet.printOptions?.horizontalCentered === true;
|
|
898
1054
|
const tableProperties = {
|
|
899
1055
|
defaultCellMargins: {
|
|
900
1056
|
left: pt(EXCEL_CELL_INSET_PT * printScale),
|
|
@@ -903,7 +1059,8 @@ function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
|
|
|
903
1059
|
bottom: pt(0)
|
|
904
1060
|
},
|
|
905
1061
|
layout: "fixed",
|
|
906
|
-
...print.gridLines ? {
|
|
1062
|
+
...print.gridLines && print.screen ? { gridlines: SCREEN_GRIDLINE } : {},
|
|
1063
|
+
...print.gridLines && !print.screen ? { borders: {
|
|
907
1064
|
top: thin,
|
|
908
1065
|
bottom: thin,
|
|
909
1066
|
left: thin,
|
|
@@ -914,6 +1071,23 @@ function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
|
|
|
914
1071
|
...centered ? { alignment: "center" } : {}
|
|
915
1072
|
};
|
|
916
1073
|
const contentWidthTwips = sheetContentWidthTwips(worksheet);
|
|
1074
|
+
const headingColTwips = scaled ? Math.max(1, Math.round(460 * printScale)) : 460;
|
|
1075
|
+
const headings = !print.screen && worksheet.printOptions?.headings ? {
|
|
1076
|
+
dxPt: twipsToPt(headingColTwips),
|
|
1077
|
+
dyPt: (worksheet.defaultRowHeightPt ?? defaultRowHeightPtFor(print.defaultFontPt)) * printScale,
|
|
1078
|
+
band: {
|
|
1079
|
+
colTwips: headingColTwips,
|
|
1080
|
+
...scaled ? {
|
|
1081
|
+
fontPt: (print.defaultFontPt ?? DEFAULT_FONT_PT) * printScale,
|
|
1082
|
+
lineWidthPt: .5 * printScale
|
|
1083
|
+
} : {}
|
|
1084
|
+
}
|
|
1085
|
+
} : void 0;
|
|
1086
|
+
const reportHeadings = () => {
|
|
1087
|
+
if (!headings || !print.headingSink) return;
|
|
1088
|
+
print.headingSink.dxPt = headings.dxPt;
|
|
1089
|
+
print.headingSink.dyPt = headings.dyPt;
|
|
1090
|
+
};
|
|
917
1091
|
const colBreaksLocal = /* @__PURE__ */ new Set();
|
|
918
1092
|
for (const brk of worksheet.colBreaks ?? []) {
|
|
919
1093
|
const local = brk - colStart;
|
|
@@ -922,14 +1096,23 @@ function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
|
|
|
922
1096
|
const fitWide = worksheet.fitToPage ? worksheet.pageSetup?.fitToWidth ?? 1 : 1;
|
|
923
1097
|
const bandWidths = scaledColumnWidths(visibleWidths, printScale, scaled);
|
|
924
1098
|
const bandTotal = bandWidths.reduce((sum, w) => sum + w, 0);
|
|
925
|
-
if (colCount > 1 && bandsAcross(worksheet, fitWide) && (bandTotal > contentWidthTwips || colBreaksLocal.size > 0)) {
|
|
1099
|
+
if (!print.screen && colCount > 1 && bandsAcross(worksheet, fitWide) && (bandTotal > contentWidthTwips || colBreaksLocal.size > 0)) {
|
|
926
1100
|
const bands = computeColumnBands(bandWidths, contentWidthTwips, colBreaksLocal);
|
|
927
1101
|
if (bands.length > 1) {
|
|
928
1102
|
if (print.bandSink) print.bandSink.lefts = bands.map((band) => twipsToPt(bandWidths.slice(0, band.start).reduce((sum, w) => sum + w, 0)));
|
|
929
|
-
|
|
930
|
-
colStart,
|
|
931
|
-
rowNumbers
|
|
1103
|
+
const banded = bandedTables(rows, bandWidths, bands, tableProperties, titleRowIndex, Math.round((print.drawingExtentPt?.widthPt ?? 0) * TWIPS_PER_POINT * printScale), headings ? {
|
|
1104
|
+
columns: visibleCols.map((c) => c + colStart),
|
|
1105
|
+
rowNumbers,
|
|
1106
|
+
band: {
|
|
1107
|
+
...headings.band,
|
|
1108
|
+
lettersPt: headings.dyPt
|
|
1109
|
+
}
|
|
1110
|
+
} : void 0, sheetLeftPt !== void 0 ? {
|
|
1111
|
+
sheetLeftPt,
|
|
1112
|
+
drawings: scaledBoxes(print.drawingBoxesPt, printScale)
|
|
932
1113
|
} : void 0);
|
|
1114
|
+
reportHeadings();
|
|
1115
|
+
return banded;
|
|
933
1116
|
}
|
|
934
1117
|
}
|
|
935
1118
|
const frozen = worksheet.pane && (worksheet.pane.frozenRows > 0 || worksheet.pane.frozenCols > 0) ? {
|
|
@@ -939,23 +1122,30 @@ function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
|
|
|
939
1122
|
if (titleRowIndex > 0 && titleRowIndex < rows.length) {
|
|
940
1123
|
const titleStart = titleRowIndex;
|
|
941
1124
|
const grid = bandWidths.map((w) => twipsToPt(w));
|
|
1125
|
+
const overlay = sheetFrame(sheetLeftPt, grid, 0);
|
|
942
1126
|
return [{
|
|
943
1127
|
kind: "table",
|
|
944
1128
|
table: {
|
|
945
1129
|
properties: tableProperties,
|
|
946
1130
|
grid,
|
|
947
|
-
rows: rows.slice(0, titleStart)
|
|
1131
|
+
rows: rows.slice(0, titleStart),
|
|
1132
|
+
...overlay
|
|
948
1133
|
}
|
|
949
1134
|
}, {
|
|
950
1135
|
kind: "table",
|
|
951
1136
|
table: {
|
|
952
1137
|
properties: tableProperties,
|
|
953
1138
|
grid,
|
|
954
|
-
rows: rows.slice(titleStart)
|
|
1139
|
+
rows: rows.slice(titleStart),
|
|
1140
|
+
...overlay
|
|
955
1141
|
}
|
|
956
1142
|
}];
|
|
957
1143
|
}
|
|
958
|
-
const headed =
|
|
1144
|
+
const headed = headings ? withHeadingBand(rows, bandWidths, visibleCols.map((c) => c + colStart), rowNumbers, {
|
|
1145
|
+
...headings.band,
|
|
1146
|
+
lettersPt: headings.dyPt
|
|
1147
|
+
}) : void 0;
|
|
1148
|
+
if (headed) reportHeadings();
|
|
959
1149
|
return [{
|
|
960
1150
|
kind: "table",
|
|
961
1151
|
table: {
|
|
@@ -964,7 +1154,8 @@ function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
|
|
|
964
1154
|
frozen
|
|
965
1155
|
} : tableProperties,
|
|
966
1156
|
grid: (headed?.widths ?? bandWidths).map((w) => twipsToPt(w)),
|
|
967
|
-
rows: headed?.rows ?? rows
|
|
1157
|
+
rows: headed?.rows ?? rows,
|
|
1158
|
+
...sheetFrame(sheetLeftPt, bandWidths.map((w) => twipsToPt(w)), headed ? twipsToPt(headingColTwips) : 0)
|
|
968
1159
|
}
|
|
969
1160
|
}];
|
|
970
1161
|
}
|
|
@@ -1070,20 +1261,49 @@ function applyCfOverride(base, o) {
|
|
|
1070
1261
|
* the sheets where it matters most, since a column of figures is the common
|
|
1071
1262
|
* case.
|
|
1072
1263
|
*/
|
|
1073
|
-
function alignmentFromXf(xf, type) {
|
|
1264
|
+
function alignmentFromXf(xf, type, text = "") {
|
|
1074
1265
|
const explicit = xf?.alignment ? mapAlignment(xf.alignment.horizontal) : void 0;
|
|
1075
1266
|
if (explicit) return explicit;
|
|
1076
|
-
return generalAlignment(type);
|
|
1267
|
+
return generalAlignment(type, textDirection(xf, text));
|
|
1077
1268
|
}
|
|
1078
|
-
|
|
1269
|
+
/**
|
|
1270
|
+
* §18.8.1 General: numbers to the right, logical values centred, and text to
|
|
1271
|
+
* the side it STARTS from — the left for a word that reads from the left, the
|
|
1272
|
+
* right for one that reads from the right. Excel sets "שלום" against the right
|
|
1273
|
+
* edge of a General cell on any sheet, and the sheet's own direction does not
|
|
1274
|
+
* enter into it: the numbers on a right-to-left sheet still sit at the right.
|
|
1275
|
+
*/
|
|
1276
|
+
function generalAlignment(type, direction) {
|
|
1079
1277
|
switch (type) {
|
|
1080
1278
|
case "n":
|
|
1081
1279
|
case "d": return "right";
|
|
1082
1280
|
case "b":
|
|
1083
1281
|
case "e": return "center";
|
|
1084
|
-
default: return "left";
|
|
1282
|
+
default: return direction === "rtl" ? "right" : "left";
|
|
1085
1283
|
}
|
|
1086
1284
|
}
|
|
1285
|
+
/**
|
|
1286
|
+
* The direction a cell's text reads in: its own §18.8.1 `readingOrder`, or
|
|
1287
|
+
* else "context" — that of its first strong character.
|
|
1288
|
+
*/
|
|
1289
|
+
function textDirection(xf, text) {
|
|
1290
|
+
return xf?.alignment?.readingOrder ?? firstStrongDirection(text) ?? "ltr";
|
|
1291
|
+
}
|
|
1292
|
+
/**
|
|
1293
|
+
* The side a cell's text runs ON to, in the file's own column order. The grid
|
|
1294
|
+
* is built A first whatever the sheet's direction and turned round at the end
|
|
1295
|
+
* (mirrorTable), so on a sheet that reads from the right a cell set against
|
|
1296
|
+
* its right edge is one whose text runs FORWARD — into B, which stands to the
|
|
1297
|
+
* left of A — and the overflow has to reason about the mirror of what the
|
|
1298
|
+
* cell shows.
|
|
1299
|
+
*/
|
|
1300
|
+
function flowAlignment(alignment, rtl) {
|
|
1301
|
+
return rtl ? crossedOver(alignment) : alignment;
|
|
1302
|
+
}
|
|
1303
|
+
/** Left for right and right for left; every other alignment is its own mirror. */
|
|
1304
|
+
function crossedOver(alignment) {
|
|
1305
|
+
return alignment === "left" ? "right" : alignment === "right" ? "left" : alignment;
|
|
1306
|
+
}
|
|
1087
1307
|
function mapAlignment(h) {
|
|
1088
1308
|
if (!h) return void 0;
|
|
1089
1309
|
if (h === "left") return "left";
|
|
@@ -1268,6 +1488,10 @@ function mapBorderStyle(style) {
|
|
|
1268
1488
|
function key(row, col) {
|
|
1269
1489
|
return `${row},${col}`;
|
|
1270
1490
|
}
|
|
1491
|
+
/** The key {@link PrintModelOptions.noteFlags} is looked up by: absolute row and column. */
|
|
1492
|
+
function noteKey(row, col) {
|
|
1493
|
+
return key(row, col);
|
|
1494
|
+
}
|
|
1271
1495
|
function listDropdownRanges(dvs) {
|
|
1272
1496
|
if (!dvs || dvs.length === 0) return [];
|
|
1273
1497
|
const out = [];
|
|
@@ -1345,6 +1569,147 @@ function collectSeriesValues(cells, area, print) {
|
|
|
1345
1569
|
for (let r = area.startRow; r <= area.endRow; r++) for (let col = area.startColumn; col <= area.endColumn; col++) out.push(byKey.get(key(r, col)) ?? null);
|
|
1346
1570
|
return out;
|
|
1347
1571
|
}
|
|
1572
|
+
/** §18.8.41 — the order a table style's regions apply in: a later one wins. */
|
|
1573
|
+
var TABLE_STYLE_ORDER = [
|
|
1574
|
+
"wholeTable",
|
|
1575
|
+
"firstColumnStripe",
|
|
1576
|
+
"secondColumnStripe",
|
|
1577
|
+
"firstRowStripe",
|
|
1578
|
+
"secondRowStripe",
|
|
1579
|
+
"lastColumn",
|
|
1580
|
+
"firstColumn",
|
|
1581
|
+
"headerRow",
|
|
1582
|
+
"totalRow",
|
|
1583
|
+
"firstHeaderCell",
|
|
1584
|
+
"lastHeaderCell",
|
|
1585
|
+
"firstTotalCell",
|
|
1586
|
+
"lastTotalCell"
|
|
1587
|
+
];
|
|
1588
|
+
/**
|
|
1589
|
+
* §18.8.40 — what a table's style makes of each of its cells. Each region
|
|
1590
|
+
* the style formats is laid over the table in the order §18.8.41 gives —
|
|
1591
|
+
* the whole table, its column and row stripes, its last and first columns,
|
|
1592
|
+
* its header and total rows, their corner cells — and a later region's fill,
|
|
1593
|
+
* weight, colour and edges win over an earlier one's. A region's `left`,
|
|
1594
|
+
* `right`, `top` and `bottom` rule its own outer edges, its `vertical` and
|
|
1595
|
+
* `horizontal` the lines between its cells. Stripes run through the data rows
|
|
1596
|
+
* only, in bands of their own size, the first band at the first data row;
|
|
1597
|
+
* the first and last columns, and the stripes, only where the table turns them
|
|
1598
|
+
* on.
|
|
1599
|
+
*
|
|
1600
|
+
* @param t The table, its style resolved.
|
|
1601
|
+
* @param out Where each cell's format goes, by absolute key.
|
|
1602
|
+
*/
|
|
1603
|
+
function styledTableFormats(t, out) {
|
|
1604
|
+
const style = t.style;
|
|
1605
|
+
if (!style) return;
|
|
1606
|
+
const { startRow: r0, endRow: r1, startColumn: c0, endColumn: c1 } = t.ref;
|
|
1607
|
+
const dataTop = r0 + t.headerRowCount;
|
|
1608
|
+
const dataBottom = r1 - (t.totalsRowCount ?? 0);
|
|
1609
|
+
const bands = (from, to, first, second, which) => {
|
|
1610
|
+
const spans = [];
|
|
1611
|
+
let at = from;
|
|
1612
|
+
for (let i = 0; at <= to; i++) {
|
|
1613
|
+
const size = i % 2 === 0 ? first : second;
|
|
1614
|
+
if (i % 2 === which) spans.push([at, Math.min(to, at + size - 1)]);
|
|
1615
|
+
at += size;
|
|
1616
|
+
}
|
|
1617
|
+
return spans;
|
|
1618
|
+
};
|
|
1619
|
+
const rowSize = [style.firstRowStripe?.size ?? 1, style.secondRowStripe?.size ?? 1];
|
|
1620
|
+
const colSize = [style.firstColumnStripe?.size ?? 1, style.secondColumnStripe?.size ?? 1];
|
|
1621
|
+
const regionsOf = (type) => {
|
|
1622
|
+
const whole = {
|
|
1623
|
+
top: r0,
|
|
1624
|
+
bottom: r1,
|
|
1625
|
+
left: c0,
|
|
1626
|
+
right: c1
|
|
1627
|
+
};
|
|
1628
|
+
switch (type) {
|
|
1629
|
+
case "wholeTable": return [whole];
|
|
1630
|
+
case "firstRowStripe":
|
|
1631
|
+
case "secondRowStripe": return t.showRowStripes && dataBottom >= dataTop ? bands(dataTop, dataBottom, rowSize[0], rowSize[1], type === "firstRowStripe" ? 0 : 1).map(([top, bottom]) => ({
|
|
1632
|
+
top,
|
|
1633
|
+
bottom,
|
|
1634
|
+
left: c0,
|
|
1635
|
+
right: c1
|
|
1636
|
+
})) : [];
|
|
1637
|
+
case "firstColumnStripe":
|
|
1638
|
+
case "secondColumnStripe": return t.showColumnStripes && dataBottom >= dataTop ? bands(c0, c1, colSize[0], colSize[1], type === "firstColumnStripe" ? 0 : 1).map(([left, right]) => ({
|
|
1639
|
+
top: dataTop,
|
|
1640
|
+
bottom: dataBottom,
|
|
1641
|
+
left,
|
|
1642
|
+
right
|
|
1643
|
+
})) : [];
|
|
1644
|
+
case "lastColumn": return t.showLastColumn ? [{
|
|
1645
|
+
...whole,
|
|
1646
|
+
left: c1
|
|
1647
|
+
}] : [];
|
|
1648
|
+
case "firstColumn": return t.showFirstColumn ? [{
|
|
1649
|
+
...whole,
|
|
1650
|
+
right: c0
|
|
1651
|
+
}] : [];
|
|
1652
|
+
case "headerRow": return t.headerRowCount > 0 ? [{
|
|
1653
|
+
...whole,
|
|
1654
|
+
bottom: dataTop - 1
|
|
1655
|
+
}] : [];
|
|
1656
|
+
case "totalRow": return dataBottom < r1 ? [{
|
|
1657
|
+
...whole,
|
|
1658
|
+
top: dataBottom + 1
|
|
1659
|
+
}] : [];
|
|
1660
|
+
case "firstHeaderCell": return t.headerRowCount > 0 && t.showFirstColumn ? [{
|
|
1661
|
+
top: r0,
|
|
1662
|
+
bottom: dataTop - 1,
|
|
1663
|
+
left: c0,
|
|
1664
|
+
right: c0
|
|
1665
|
+
}] : [];
|
|
1666
|
+
case "lastHeaderCell": return t.headerRowCount > 0 && t.showLastColumn ? [{
|
|
1667
|
+
top: r0,
|
|
1668
|
+
bottom: dataTop - 1,
|
|
1669
|
+
left: c1,
|
|
1670
|
+
right: c1
|
|
1671
|
+
}] : [];
|
|
1672
|
+
case "firstTotalCell": return dataBottom < r1 && t.showFirstColumn ? [{
|
|
1673
|
+
top: dataBottom + 1,
|
|
1674
|
+
bottom: r1,
|
|
1675
|
+
left: c0,
|
|
1676
|
+
right: c0
|
|
1677
|
+
}] : [];
|
|
1678
|
+
case "lastTotalCell": return dataBottom < r1 && t.showLastColumn ? [{
|
|
1679
|
+
top: dataBottom + 1,
|
|
1680
|
+
bottom: r1,
|
|
1681
|
+
left: c1,
|
|
1682
|
+
right: c1
|
|
1683
|
+
}] : [];
|
|
1684
|
+
}
|
|
1685
|
+
};
|
|
1686
|
+
for (const type of TABLE_STYLE_ORDER) {
|
|
1687
|
+
const dxf = style[type]?.dxf;
|
|
1688
|
+
if (!dxf) continue;
|
|
1689
|
+
const fill = dxf.fill?.patternType === "none" ? void 0 : dxf.fill?.bgColorHex ?? dxf.fill?.fgColorHex;
|
|
1690
|
+
const b = dxf.border;
|
|
1691
|
+
for (const region of regionsOf(type)) for (let r = region.top; r <= region.bottom; r++) for (let c = region.left; c <= region.right; c++) {
|
|
1692
|
+
const k = key(r, c);
|
|
1693
|
+
const was = out.get(k);
|
|
1694
|
+
const edges = { ...was?.borders };
|
|
1695
|
+
const top = r === region.top ? b?.top : b?.horizontal;
|
|
1696
|
+
const bottom = r === region.bottom ? b?.bottom : b?.horizontal;
|
|
1697
|
+
const left = c === region.left ? b?.left : b?.vertical;
|
|
1698
|
+
const right = c === region.right ? b?.right : b?.vertical;
|
|
1699
|
+
if (top) edges.top = top;
|
|
1700
|
+
if (bottom) edges.bottom = bottom;
|
|
1701
|
+
if (left) edges.left = left;
|
|
1702
|
+
if (right) edges.right = right;
|
|
1703
|
+
out.set(k, {
|
|
1704
|
+
...was,
|
|
1705
|
+
...fill !== void 0 ? { shading: { colorHex: fill } } : {},
|
|
1706
|
+
...dxf.font?.colorHex !== void 0 ? { fontColorHex: dxf.font.colorHex } : {},
|
|
1707
|
+
...dxf.font?.bold !== void 0 ? { bold: dxf.font.bold } : {},
|
|
1708
|
+
...Object.keys(edges).length > 0 ? { borders: edges } : {}
|
|
1709
|
+
});
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1348
1713
|
function isPivotTotal(t) {
|
|
1349
1714
|
return t !== void 0 && t !== "data" && t !== "blank";
|
|
1350
1715
|
}
|
|
@@ -1370,7 +1735,8 @@ function buildTableFormatLookup(worksheet) {
|
|
|
1370
1735
|
for (let c = ref.startColumn; c <= ref.endColumn; c++) out.set(key(r, c), fmt);
|
|
1371
1736
|
}
|
|
1372
1737
|
};
|
|
1373
|
-
for (const t of worksheet.tables ?? [])
|
|
1738
|
+
for (const t of worksheet.tables ?? []) if (t.style) styledTableFormats(t, out);
|
|
1739
|
+
else band(t.ref, t.headerRowCount, t);
|
|
1374
1740
|
for (const p of worksheet.pivotTables ?? []) band(p.ref, p.firstDataRow, p, (off) => isPivotTotal(p.rowItemTypes?.[off]));
|
|
1375
1741
|
for (const p of worksheet.pivotTables ?? []) {
|
|
1376
1742
|
if (!p.colItemTypes || p.headerHex === void 0) continue;
|
|
@@ -1466,14 +1832,17 @@ function cellPaintsSomething(cell, styles) {
|
|
|
1466
1832
|
}
|
|
1467
1833
|
/**
|
|
1468
1834
|
* How many empty columns past the used range the last column's text needs to
|
|
1469
|
-
* run into, bounded by the printable width (Excel stops at the page edge too)
|
|
1835
|
+
* run into, bounded by the printable width (Excel stops at the page edge too)
|
|
1836
|
+
* — or, on a `screen`, which has no edge, only by the column cap; an `image`
|
|
1837
|
+
* of the sheet gives it none, its edge being the used range's.
|
|
1470
1838
|
*
|
|
1471
1839
|
* Only the last used column can want them — anywhere else the grid already has
|
|
1472
1840
|
* neighbours. Zero for the overwhelming majority of sheets, which keeps their
|
|
1473
1841
|
* projection exactly as it was.
|
|
1474
1842
|
*/
|
|
1475
|
-
function overflowColumnsPastUsedRange(worksheet, usedCol, sharedStrings, styles, date1904, charTwipsUnit) {
|
|
1476
|
-
|
|
1843
|
+
function overflowColumnsPastUsedRange(worksheet, usedCol, sharedStrings, styles, date1904, charTwipsUnit, screen, image) {
|
|
1844
|
+
if (image) return 0;
|
|
1845
|
+
const defaultTwips = defaultColumnTwips(worksheet, charTwipsUnit);
|
|
1477
1846
|
const widthOf = (abs) => {
|
|
1478
1847
|
for (const col of worksheet.columns) {
|
|
1479
1848
|
if (abs < col.min - 1 || abs > col.max - 1) continue;
|
|
@@ -1493,16 +1862,16 @@ function overflowColumnsPastUsedRange(worksheet, usedCol, sharedStrings, styles,
|
|
|
1493
1862
|
const xf = styles.cellXfs[cell.styleIndex ?? 0];
|
|
1494
1863
|
const align = xf?.alignment;
|
|
1495
1864
|
if (align?.wrapText || align?.shrinkToFit || align?.textRotation) continue;
|
|
1496
|
-
|
|
1865
|
+
const text = resolveCellText(cell, sharedStrings, styles, date1904);
|
|
1866
|
+
if (flowAlignment(alignmentFromXf(xf, cell.type, text), worksheet.rightToLeft === true) !== "left") continue;
|
|
1497
1867
|
let room = 0;
|
|
1498
1868
|
for (let abs = cell.column; abs <= usedCol; abs++) room += widthOf(abs);
|
|
1499
|
-
const text = resolveCellText(cell, sharedStrings, styles, date1904);
|
|
1500
1869
|
needTwips = Math.max(needTwips, estimateChars(text) * charTwips(xf, styles, 105) - room);
|
|
1501
1870
|
}
|
|
1502
1871
|
if (needTwips <= 0) return 0;
|
|
1503
1872
|
let gridTwips = 0;
|
|
1504
1873
|
for (let abs = 0; abs <= usedCol; abs++) gridTwips += widthOf(abs);
|
|
1505
|
-
const limit = sheetContentWidthTwips(worksheet);
|
|
1874
|
+
const limit = screen ? Infinity : sheetContentWidthTwips(worksheet);
|
|
1506
1875
|
const MAX_OVERFLOW_COLS = 256;
|
|
1507
1876
|
let extra = 0;
|
|
1508
1877
|
while (needTwips > 0 && extra < MAX_OVERFLOW_COLS) {
|
|
@@ -1671,6 +2040,79 @@ function absorbLeftwards(cells, cellColumns, candidates, styles, neighbourAt) {
|
|
|
1671
2040
|
}
|
|
1672
2041
|
}
|
|
1673
2042
|
/**
|
|
2043
|
+
* §18.3.1.87 `rightToLeft` — the table turned round, the way Excel draws a
|
|
2044
|
+
* sheet that reads from the right: column A last in every row and so at the
|
|
2045
|
+
* right edge, and each cell's left and right borders crossed over with it
|
|
2046
|
+
* (its diagonals too). What a cell shows is untouched — its text keeps the
|
|
2047
|
+
* side its alignment names, and its overflow already runs the right way (see
|
|
2048
|
+
* flowAlignment). On paper the sheet stands against the right margin, as
|
|
2049
|
+
* Excel prints it, unless it asks to be centred.
|
|
2050
|
+
*
|
|
2051
|
+
* @param table The grid as built, column A first.
|
|
2052
|
+
* @param screen Whether it is drawn for a window, which has no margin to meet.
|
|
2053
|
+
*/
|
|
2054
|
+
function mirrorTable(table, screen) {
|
|
2055
|
+
const { frozen, ...properties } = table.properties;
|
|
2056
|
+
return {
|
|
2057
|
+
...table,
|
|
2058
|
+
properties: {
|
|
2059
|
+
...properties,
|
|
2060
|
+
...frozen && frozen.rows > 0 ? { frozen: {
|
|
2061
|
+
rows: frozen.rows,
|
|
2062
|
+
cols: 0
|
|
2063
|
+
} } : {},
|
|
2064
|
+
...!screen && properties.alignment !== "center" ? { alignment: "right" } : {}
|
|
2065
|
+
},
|
|
2066
|
+
grid: [...table.grid].reverse(),
|
|
2067
|
+
rows: table.rows.map((row) => ({
|
|
2068
|
+
...row,
|
|
2069
|
+
cells: [...row.cells].reverse().map(mirrorCell)
|
|
2070
|
+
})),
|
|
2071
|
+
...table.overlay ? { overlay: {
|
|
2072
|
+
...table.overlay,
|
|
2073
|
+
leftPt: pt(table.grid.reduce((sum, w) => sum + w, 0) - table.overlay.leftPt - table.overlay.widthPt),
|
|
2074
|
+
mirrored: true
|
|
2075
|
+
} } : {}
|
|
2076
|
+
};
|
|
2077
|
+
}
|
|
2078
|
+
function mirrorCell(cell) {
|
|
2079
|
+
const p = cell.properties;
|
|
2080
|
+
const span = p.colSpan ?? 1;
|
|
2081
|
+
const paintAtEnd = p.paintColumns !== void 0 && p.paintColumns < span;
|
|
2082
|
+
if (!p.borders && !p.dataBar && !p.noteFlag && !paintAtEnd) return cell;
|
|
2083
|
+
return {
|
|
2084
|
+
...cell,
|
|
2085
|
+
properties: {
|
|
2086
|
+
...p,
|
|
2087
|
+
...p.borders ? { borders: mirrorBorders(p.borders) } : {},
|
|
2088
|
+
...p.dataBar ? { dataBar: mirrorDataBar(p.dataBar) } : {},
|
|
2089
|
+
...p.noteFlag ? { noteFlag: {
|
|
2090
|
+
...p.noteFlag,
|
|
2091
|
+
atLeft: !p.noteFlag.atLeft
|
|
2092
|
+
} } : {},
|
|
2093
|
+
...paintAtEnd ? { paintAtEnd: true } : {}
|
|
2094
|
+
}
|
|
2095
|
+
};
|
|
2096
|
+
}
|
|
2097
|
+
function mirrorBorders(b) {
|
|
2098
|
+
const { left, right, diagonalUp, diagonalDown, ...rest } = b;
|
|
2099
|
+
return {
|
|
2100
|
+
...rest,
|
|
2101
|
+
...right ? { left: right } : {},
|
|
2102
|
+
...left ? { right: left } : {},
|
|
2103
|
+
...diagonalUp ? { diagonalDown: diagonalUp } : {},
|
|
2104
|
+
...diagonalDown ? { diagonalUp: diagonalDown } : {}
|
|
2105
|
+
};
|
|
2106
|
+
}
|
|
2107
|
+
function mirrorDataBar(bar) {
|
|
2108
|
+
const start = bar.startFraction ?? 0;
|
|
2109
|
+
return {
|
|
2110
|
+
...bar,
|
|
2111
|
+
startFraction: Math.max(0, 1 - start - bar.fraction),
|
|
2112
|
+
negative: !bar.negative
|
|
2113
|
+
};
|
|
2114
|
+
}
|
|
2115
|
+
/**
|
|
1674
2116
|
* Whether spanning a cell's overflowing text across `neighbour` would leave the
|
|
1675
2117
|
* page looking the same as drawing over it would.
|
|
1676
2118
|
*
|
|
@@ -1805,4 +2247,4 @@ function slicerParagraph(text, runProps) {
|
|
|
1805
2247
|
};
|
|
1806
2248
|
}
|
|
1807
2249
|
//#endregion
|
|
1808
|
-
export { cellPaintsVisibly, columnTwips, printableHeightPt, printableWidthPt, resolveCellText, resolvePrintArea, resolvePrintTitleRows, sectionFromWorksheet, slicerTable, worksheetToBody };
|
|
2250
|
+
export { cellPaintsVisibly, columnTwips, columnUnitTwips, defaultColumnTwips, noteKey, printableHeightPt, printableWidthPt, resolveCellText, resolvePrintArea, resolvePrintTitleRows, sectionFromWorksheet, slicerTable, worksheetToBody };
|