@cj-tech-master/excelts 8.1.2 → 9.0.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 +2 -2
- package/README_zh.md +2 -2
- package/dist/browser/modules/excel/cell.js +11 -7
- package/dist/browser/modules/excel/column.js +7 -6
- package/dist/browser/modules/excel/row.js +5 -1
- package/dist/browser/modules/excel/stream/worksheet-reader.js +3 -2
- package/dist/browser/modules/excel/utils/cell-format.js +64 -2
- package/dist/browser/modules/pdf/excel-bridge.d.ts +4 -3
- package/dist/browser/modules/pdf/excel-bridge.js +18 -5
- package/dist/browser/modules/pdf/index.d.ts +3 -3
- package/dist/browser/modules/pdf/index.js +3 -3
- package/dist/browser/modules/pdf/pdf.d.ts +7 -6
- package/dist/browser/modules/pdf/pdf.js +7 -6
- package/dist/browser/modules/pdf/reader/pdf-reader.d.ts +8 -7
- package/dist/browser/modules/pdf/reader/pdf-reader.js +81 -74
- package/dist/browser/modules/pdf/render/constants.d.ts +30 -0
- package/dist/browser/modules/pdf/render/constants.js +30 -0
- package/dist/browser/modules/pdf/render/layout-engine.d.ts +2 -1
- package/dist/browser/modules/pdf/render/layout-engine.js +359 -156
- package/dist/browser/modules/pdf/render/page-renderer.d.ts +2 -2
- package/dist/browser/modules/pdf/render/page-renderer.js +245 -107
- package/dist/browser/modules/pdf/render/pdf-exporter.d.ts +3 -2
- package/dist/browser/modules/pdf/render/pdf-exporter.js +145 -105
- package/dist/browser/modules/pdf/render/style-converter.js +27 -26
- package/dist/browser/modules/pdf/types.d.ts +8 -0
- package/dist/browser/utils/utils.base.d.ts +5 -0
- package/dist/browser/utils/utils.base.js +10 -0
- package/dist/cjs/modules/excel/cell.js +11 -7
- package/dist/cjs/modules/excel/column.js +7 -6
- package/dist/cjs/modules/excel/row.js +5 -1
- package/dist/cjs/modules/excel/stream/worksheet-reader.js +3 -2
- package/dist/cjs/modules/excel/utils/cell-format.js +64 -2
- package/dist/cjs/modules/pdf/excel-bridge.js +18 -5
- package/dist/cjs/modules/pdf/index.js +3 -3
- package/dist/cjs/modules/pdf/pdf.js +7 -6
- package/dist/cjs/modules/pdf/reader/pdf-reader.js +81 -74
- package/dist/cjs/modules/pdf/render/constants.js +33 -0
- package/dist/cjs/modules/pdf/render/layout-engine.js +359 -156
- package/dist/cjs/modules/pdf/render/page-renderer.js +245 -107
- package/dist/cjs/modules/pdf/render/pdf-exporter.js +145 -105
- package/dist/cjs/modules/pdf/render/style-converter.js +27 -26
- package/dist/cjs/utils/utils.base.js +11 -0
- package/dist/esm/modules/excel/cell.js +11 -7
- package/dist/esm/modules/excel/column.js +7 -6
- package/dist/esm/modules/excel/row.js +5 -1
- package/dist/esm/modules/excel/stream/worksheet-reader.js +3 -2
- package/dist/esm/modules/excel/utils/cell-format.js +64 -2
- package/dist/esm/modules/pdf/excel-bridge.js +18 -5
- package/dist/esm/modules/pdf/index.js +3 -3
- package/dist/esm/modules/pdf/pdf.js +7 -6
- package/dist/esm/modules/pdf/reader/pdf-reader.js +81 -74
- package/dist/esm/modules/pdf/render/constants.js +30 -0
- package/dist/esm/modules/pdf/render/layout-engine.js +359 -156
- package/dist/esm/modules/pdf/render/page-renderer.js +245 -107
- package/dist/esm/modules/pdf/render/pdf-exporter.js +145 -105
- package/dist/esm/modules/pdf/render/style-converter.js +27 -26
- package/dist/esm/utils/utils.base.js +10 -0
- package/dist/iife/excelts.iife.js +1022 -677
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +48 -48
- package/dist/types/modules/pdf/excel-bridge.d.ts +4 -3
- package/dist/types/modules/pdf/index.d.ts +3 -3
- package/dist/types/modules/pdf/pdf.d.ts +7 -6
- package/dist/types/modules/pdf/reader/pdf-reader.d.ts +8 -7
- package/dist/types/modules/pdf/render/constants.d.ts +30 -0
- package/dist/types/modules/pdf/render/layout-engine.d.ts +2 -1
- package/dist/types/modules/pdf/render/page-renderer.d.ts +2 -2
- package/dist/types/modules/pdf/render/pdf-exporter.d.ts +3 -2
- package/dist/types/modules/pdf/types.d.ts +8 -0
- package/dist/types/utils/utils.base.d.ts +5 -0
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @cj-tech-master/excelts
|
|
2
|
+
* @cj-tech-master/excelts v9.0.0
|
|
3
3
|
* Zero-dependency TypeScript toolkit — Excel (XLSX), PDF, CSV, Markdown, XML, ZIP/TAR, and streaming.
|
|
4
4
|
* (c) 2026 cjnoname
|
|
5
5
|
* Released under the MIT License
|
|
@@ -942,6 +942,44 @@ var ExcelTS = (function(exports) {
|
|
|
942
942
|
});
|
|
943
943
|
}
|
|
944
944
|
//#endregion
|
|
945
|
+
//#region src/modules/excel/utils/copy-style.ts
|
|
946
|
+
const oneDepthCopy = (obj, nestKeys) => ({
|
|
947
|
+
...obj,
|
|
948
|
+
...nestKeys.reduce((memo, key) => {
|
|
949
|
+
if (obj[key]) memo[key] = { ...obj[key] };
|
|
950
|
+
return memo;
|
|
951
|
+
}, {})
|
|
952
|
+
});
|
|
953
|
+
const setIfExists = (src, dst, key, nestKeys = []) => {
|
|
954
|
+
if (src[key]) dst[key] = oneDepthCopy(src[key], nestKeys);
|
|
955
|
+
};
|
|
956
|
+
const isEmptyObj = (obj) => Object.keys(obj).length === 0;
|
|
957
|
+
const copyStyle = (style) => {
|
|
958
|
+
if (!style) return style;
|
|
959
|
+
if (isEmptyObj(style)) return {};
|
|
960
|
+
const copied = { ...style };
|
|
961
|
+
setIfExists(style, copied, "font", ["color"]);
|
|
962
|
+
setIfExists(style, copied, "alignment");
|
|
963
|
+
setIfExists(style, copied, "protection");
|
|
964
|
+
if (style.border) {
|
|
965
|
+
setIfExists(style, copied, "border");
|
|
966
|
+
setIfExists(style.border, copied.border, "top", ["color"]);
|
|
967
|
+
setIfExists(style.border, copied.border, "left", ["color"]);
|
|
968
|
+
setIfExists(style.border, copied.border, "bottom", ["color"]);
|
|
969
|
+
setIfExists(style.border, copied.border, "right", ["color"]);
|
|
970
|
+
setIfExists(style.border, copied.border, "diagonal", ["color"]);
|
|
971
|
+
}
|
|
972
|
+
if (style.fill) {
|
|
973
|
+
setIfExists(style, copied, "fill", [
|
|
974
|
+
"fgColor",
|
|
975
|
+
"bgColor",
|
|
976
|
+
"center"
|
|
977
|
+
]);
|
|
978
|
+
if (style.fill.stops) copied.fill.stops = style.fill.stops.map((s) => oneDepthCopy(s, ["color"]));
|
|
979
|
+
}
|
|
980
|
+
return copied;
|
|
981
|
+
};
|
|
982
|
+
//#endregion
|
|
945
983
|
//#region src/modules/excel/cell.ts
|
|
946
984
|
const hasOwnKeys = (v) => !!v && (typeof v !== "object" || Object.keys(v).length > 0);
|
|
947
985
|
var Cell = class Cell {
|
|
@@ -1011,15 +1049,15 @@ var ExcelTS = (function(exports) {
|
|
|
1011
1049
|
const numFmt = rowStyle && rowStyle.numFmt || colStyle && colStyle.numFmt;
|
|
1012
1050
|
if (numFmt) style.numFmt = numFmt;
|
|
1013
1051
|
const font = rowStyle && hasOwnKeys(rowStyle.font) && rowStyle.font || colStyle && hasOwnKeys(colStyle.font) && colStyle.font;
|
|
1014
|
-
if (font) style.font = font;
|
|
1052
|
+
if (font) style.font = structuredClone(font);
|
|
1015
1053
|
const alignment = rowStyle && hasOwnKeys(rowStyle.alignment) && rowStyle.alignment || colStyle && hasOwnKeys(colStyle.alignment) && colStyle.alignment;
|
|
1016
|
-
if (alignment) style.alignment = alignment;
|
|
1054
|
+
if (alignment) style.alignment = structuredClone(alignment);
|
|
1017
1055
|
const border = rowStyle && hasOwnKeys(rowStyle.border) && rowStyle.border || colStyle && hasOwnKeys(colStyle.border) && colStyle.border;
|
|
1018
|
-
if (border) style.border = border;
|
|
1056
|
+
if (border) style.border = structuredClone(border);
|
|
1019
1057
|
const fill = rowStyle && hasOwnKeys(rowStyle.fill) && rowStyle.fill || colStyle && hasOwnKeys(colStyle.fill) && colStyle.fill;
|
|
1020
|
-
if (fill) style.fill = fill;
|
|
1058
|
+
if (fill) style.fill = structuredClone(fill);
|
|
1021
1059
|
const protection = rowStyle && hasOwnKeys(rowStyle.protection) && rowStyle.protection || colStyle && hasOwnKeys(colStyle.protection) && colStyle.protection;
|
|
1022
|
-
if (protection) style.protection = protection;
|
|
1060
|
+
if (protection) style.protection = structuredClone(protection);
|
|
1023
1061
|
return style;
|
|
1024
1062
|
}
|
|
1025
1063
|
get address() {
|
|
@@ -1055,7 +1093,7 @@ var ExcelTS = (function(exports) {
|
|
|
1055
1093
|
merge(master, ignoreStyle) {
|
|
1056
1094
|
this._value.release();
|
|
1057
1095
|
this._value = Value.create(Cell.Types.Merge, this, master);
|
|
1058
|
-
if (!ignoreStyle) this.style = master.style;
|
|
1096
|
+
if (!ignoreStyle) this.style = copyStyle(master.style) ?? {};
|
|
1059
1097
|
}
|
|
1060
1098
|
unmerge() {
|
|
1061
1099
|
if (this.type === Cell.Types.Merge) {
|
|
@@ -1187,7 +1225,7 @@ var ExcelTS = (function(exports) {
|
|
|
1187
1225
|
this._comment = Note.fromModel(value.comment);
|
|
1188
1226
|
break;
|
|
1189
1227
|
}
|
|
1190
|
-
if (value.style) this.style = value.style;
|
|
1228
|
+
if (value.style) this.style = copyStyle(value.style) ?? {};
|
|
1191
1229
|
else this.style = {};
|
|
1192
1230
|
}
|
|
1193
1231
|
};
|
|
@@ -1816,44 +1854,6 @@ var ExcelTS = (function(exports) {
|
|
|
1816
1854
|
}
|
|
1817
1855
|
};
|
|
1818
1856
|
//#endregion
|
|
1819
|
-
//#region src/modules/excel/utils/copy-style.ts
|
|
1820
|
-
const oneDepthCopy = (obj, nestKeys) => ({
|
|
1821
|
-
...obj,
|
|
1822
|
-
...nestKeys.reduce((memo, key) => {
|
|
1823
|
-
if (obj[key]) memo[key] = { ...obj[key] };
|
|
1824
|
-
return memo;
|
|
1825
|
-
}, {})
|
|
1826
|
-
});
|
|
1827
|
-
const setIfExists = (src, dst, key, nestKeys = []) => {
|
|
1828
|
-
if (src[key]) dst[key] = oneDepthCopy(src[key], nestKeys);
|
|
1829
|
-
};
|
|
1830
|
-
const isEmptyObj = (obj) => Object.keys(obj).length === 0;
|
|
1831
|
-
const copyStyle = (style) => {
|
|
1832
|
-
if (!style) return style;
|
|
1833
|
-
if (isEmptyObj(style)) return {};
|
|
1834
|
-
const copied = { ...style };
|
|
1835
|
-
setIfExists(style, copied, "font", ["color"]);
|
|
1836
|
-
setIfExists(style, copied, "alignment");
|
|
1837
|
-
setIfExists(style, copied, "protection");
|
|
1838
|
-
if (style.border) {
|
|
1839
|
-
setIfExists(style, copied, "border");
|
|
1840
|
-
setIfExists(style.border, copied.border, "top", ["color"]);
|
|
1841
|
-
setIfExists(style.border, copied.border, "left", ["color"]);
|
|
1842
|
-
setIfExists(style.border, copied.border, "bottom", ["color"]);
|
|
1843
|
-
setIfExists(style.border, copied.border, "right", ["color"]);
|
|
1844
|
-
setIfExists(style.border, copied.border, "diagonal", ["color"]);
|
|
1845
|
-
}
|
|
1846
|
-
if (style.fill) {
|
|
1847
|
-
setIfExists(style, copied, "fill", [
|
|
1848
|
-
"fgColor",
|
|
1849
|
-
"bgColor",
|
|
1850
|
-
"center"
|
|
1851
|
-
]);
|
|
1852
|
-
if (style.fill.stops) copied.fill.stops = style.fill.stops.map((s) => oneDepthCopy(s, ["color"]));
|
|
1853
|
-
}
|
|
1854
|
-
return copied;
|
|
1855
|
-
};
|
|
1856
|
-
//#endregion
|
|
1857
1857
|
//#region src/modules/excel/row.ts
|
|
1858
1858
|
var Row = class {
|
|
1859
1859
|
constructor(worksheet, number) {
|
|
@@ -2084,7 +2084,7 @@ var ExcelTS = (function(exports) {
|
|
|
2084
2084
|
_applyStyle(name, value) {
|
|
2085
2085
|
this.style[name] = value;
|
|
2086
2086
|
this._cells.forEach((cell) => {
|
|
2087
|
-
if (cell) cell.style[name] = value;
|
|
2087
|
+
if (cell) cell.style[name] = typeof value === "object" && value !== null ? structuredClone(value) : value;
|
|
2088
2088
|
});
|
|
2089
2089
|
}
|
|
2090
2090
|
get numFmt() {
|
|
@@ -2249,7 +2249,7 @@ var ExcelTS = (function(exports) {
|
|
|
2249
2249
|
this.key = value.key;
|
|
2250
2250
|
this.width = value.width !== void 0 ? value.width : DEFAULT_COLUMN_WIDTH$1;
|
|
2251
2251
|
this.outlineLevel = value.outlineLevel;
|
|
2252
|
-
if (value.style) this.style = value.style;
|
|
2252
|
+
if (value.style) this.style = copyStyle(value.style) ?? {};
|
|
2253
2253
|
else this.style = {};
|
|
2254
2254
|
this.header = value.header;
|
|
2255
2255
|
this._hidden = !!value.hidden;
|
|
@@ -2412,7 +2412,7 @@ var ExcelTS = (function(exports) {
|
|
|
2412
2412
|
set font(value) {
|
|
2413
2413
|
this.style.font = value;
|
|
2414
2414
|
this.eachCell((cell) => {
|
|
2415
|
-
cell.font = value;
|
|
2415
|
+
cell.font = value ? structuredClone(value) : value;
|
|
2416
2416
|
});
|
|
2417
2417
|
}
|
|
2418
2418
|
get alignment() {
|
|
@@ -2421,7 +2421,7 @@ var ExcelTS = (function(exports) {
|
|
|
2421
2421
|
set alignment(value) {
|
|
2422
2422
|
this.style.alignment = value;
|
|
2423
2423
|
this.eachCell((cell) => {
|
|
2424
|
-
cell.alignment = value;
|
|
2424
|
+
cell.alignment = value ? structuredClone(value) : value;
|
|
2425
2425
|
});
|
|
2426
2426
|
}
|
|
2427
2427
|
get protection() {
|
|
@@ -2430,7 +2430,7 @@ var ExcelTS = (function(exports) {
|
|
|
2430
2430
|
set protection(value) {
|
|
2431
2431
|
this.style.protection = value;
|
|
2432
2432
|
this.eachCell((cell) => {
|
|
2433
|
-
cell.protection = value;
|
|
2433
|
+
cell.protection = value ? structuredClone(value) : value;
|
|
2434
2434
|
});
|
|
2435
2435
|
}
|
|
2436
2436
|
get border() {
|
|
@@ -2439,7 +2439,7 @@ var ExcelTS = (function(exports) {
|
|
|
2439
2439
|
set border(value) {
|
|
2440
2440
|
this.style.border = value;
|
|
2441
2441
|
this.eachCell((cell) => {
|
|
2442
|
-
cell.border = value;
|
|
2442
|
+
cell.border = value ? structuredClone(value) : value;
|
|
2443
2443
|
});
|
|
2444
2444
|
}
|
|
2445
2445
|
get fill() {
|
|
@@ -2448,7 +2448,7 @@ var ExcelTS = (function(exports) {
|
|
|
2448
2448
|
set fill(value) {
|
|
2449
2449
|
this.style.fill = value;
|
|
2450
2450
|
this.eachCell((cell) => {
|
|
2451
|
-
cell.fill = value;
|
|
2451
|
+
cell.fill = value ? structuredClone(value) : value;
|
|
2452
2452
|
});
|
|
2453
2453
|
}
|
|
2454
2454
|
static toModel(columns) {
|
|
@@ -3837,6 +3837,13 @@ var ExcelTS = (function(exports) {
|
|
|
3837
3837
|
}
|
|
3838
3838
|
return bytes;
|
|
3839
3839
|
}
|
|
3840
|
+
/**
|
|
3841
|
+
* Yield to the event loop via a macrotask.
|
|
3842
|
+
* Uses `setTimeout(0)` which works in both Node.js and browsers.
|
|
3843
|
+
*/
|
|
3844
|
+
function yieldToEventLoop() {
|
|
3845
|
+
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
3846
|
+
}
|
|
3840
3847
|
//#endregion
|
|
3841
3848
|
//#region src/modules/excel/pivot-table.ts
|
|
3842
3849
|
/**
|
|
@@ -4537,7 +4544,20 @@ var ExcelTS = (function(exports) {
|
|
|
4537
4544
|
decFmt = workFmt.substring(decimalIdx + 1);
|
|
4538
4545
|
}
|
|
4539
4546
|
const decimalPlaces = decFmt.replace(/[^0#?]/g, "").length;
|
|
4540
|
-
const
|
|
4547
|
+
const roundedVal = roundTo(scaledVal, decimalPlaces);
|
|
4548
|
+
if (roundedVal === 0 && !intFmt.includes("0") && !decFmt.includes("0")) {
|
|
4549
|
+
let result = "";
|
|
4550
|
+
for (const ch of intFmt) if (ch === "?") result += " ";
|
|
4551
|
+
else if (ch !== "#" && ch !== ",") result += ch;
|
|
4552
|
+
if (decimalPlaces > 0) {
|
|
4553
|
+
if (/[0?]/.test(decFmt)) {
|
|
4554
|
+
result += ".";
|
|
4555
|
+
for (const ch of decFmt) if (ch === "?") result += " ";
|
|
4556
|
+
}
|
|
4557
|
+
}
|
|
4558
|
+
return sign + result;
|
|
4559
|
+
}
|
|
4560
|
+
const [intPart, decPart = ""] = roundedVal.toString().split(".");
|
|
4541
4561
|
const hasLiteralInFormat = /[0#?][^0#?,.\s][0#?]/.test(intFmt);
|
|
4542
4562
|
let formattedInt;
|
|
4543
4563
|
if (hasLiteralInFormat) {
|
|
@@ -4559,10 +4579,23 @@ var ExcelTS = (function(exports) {
|
|
|
4559
4579
|
formattedInt = intPart;
|
|
4560
4580
|
if (intFmt.includes(",")) formattedInt = commaify(intPart);
|
|
4561
4581
|
const minIntDigits = (intFmt.match(/0/g) ?? []).length;
|
|
4582
|
+
const totalIntSlots = (intFmt.match(/[0?]/g) ?? []).length;
|
|
4562
4583
|
if (formattedInt.length < minIntDigits) formattedInt = "0".repeat(minIntDigits - formattedInt.length) + formattedInt;
|
|
4584
|
+
if (formattedInt.length < totalIntSlots) formattedInt = " ".repeat(totalIntSlots - formattedInt.length) + formattedInt;
|
|
4585
|
+
if (formattedInt === "0" && minIntDigits === 0 && totalIntSlots === 0) formattedInt = "";
|
|
4563
4586
|
}
|
|
4564
4587
|
let formattedDec = "";
|
|
4565
|
-
if (decimalPlaces > 0)
|
|
4588
|
+
if (decimalPlaces > 0) {
|
|
4589
|
+
const decChars = (decPart + "0".repeat(decimalPlaces)).substring(0, decimalPlaces).split("");
|
|
4590
|
+
for (let i = decFmt.length - 1; i >= 0; i--) {
|
|
4591
|
+
if (i >= decChars.length) continue;
|
|
4592
|
+
if (decFmt[i] === "#" && decChars[i] === "0") decChars[i] = "";
|
|
4593
|
+
else if (decFmt[i] === "?" && decChars[i] === "0") decChars[i] = " ";
|
|
4594
|
+
else break;
|
|
4595
|
+
}
|
|
4596
|
+
const decStr = decChars.join("");
|
|
4597
|
+
if (decStr.length > 0) formattedDec = "." + decStr;
|
|
4598
|
+
}
|
|
4566
4599
|
return sign + formattedInt + formattedDec;
|
|
4567
4600
|
}
|
|
4568
4601
|
/**
|
|
@@ -38704,7 +38737,7 @@ onmessage = async (ev) => {
|
|
|
38704
38737
|
if (node.attributes.s !== void 0) {
|
|
38705
38738
|
const styleId = parseInt(node.attributes.s, 10);
|
|
38706
38739
|
const style = styles.getStyleModel(styleId);
|
|
38707
|
-
if (style) row.style = style;
|
|
38740
|
+
if (style) row.style = copyStyle(style) ?? {};
|
|
38708
38741
|
}
|
|
38709
38742
|
}
|
|
38710
38743
|
break;
|
|
@@ -38786,7 +38819,7 @@ onmessage = async (ev) => {
|
|
|
38786
38819
|
const cell = row.getCell(address.col);
|
|
38787
38820
|
if (c.s !== void 0) {
|
|
38788
38821
|
const style = styles.getStyleModel(c.s);
|
|
38789
|
-
if (style) cell.style = style;
|
|
38822
|
+
if (style) cell.style = copyStyle(style) ?? {};
|
|
38790
38823
|
}
|
|
38791
38824
|
if (c.f) {
|
|
38792
38825
|
const cellValue = { formula: c.f.text };
|
|
@@ -47524,29 +47557,29 @@ onmessage = async (ev) => {
|
|
|
47524
47557
|
b: 0
|
|
47525
47558
|
},
|
|
47526
47559
|
{
|
|
47527
|
-
r: .
|
|
47528
|
-
g: .
|
|
47529
|
-
b: .
|
|
47560
|
+
r: .906,
|
|
47561
|
+
g: .902,
|
|
47562
|
+
b: .902
|
|
47530
47563
|
},
|
|
47531
47564
|
{
|
|
47532
47565
|
r: .267,
|
|
47533
|
-
g: .
|
|
47534
|
-
b: .
|
|
47566
|
+
g: .329,
|
|
47567
|
+
b: .416
|
|
47535
47568
|
},
|
|
47536
47569
|
{
|
|
47537
|
-
r: .
|
|
47538
|
-
g: .
|
|
47539
|
-
b: .
|
|
47570
|
+
r: .267,
|
|
47571
|
+
g: .447,
|
|
47572
|
+
b: .769
|
|
47540
47573
|
},
|
|
47541
47574
|
{
|
|
47542
|
-
r: .
|
|
47543
|
-
g: .
|
|
47544
|
-
b: .
|
|
47575
|
+
r: .929,
|
|
47576
|
+
g: .49,
|
|
47577
|
+
b: .192
|
|
47545
47578
|
},
|
|
47546
47579
|
{
|
|
47547
|
-
r: .
|
|
47548
|
-
g: .
|
|
47549
|
-
b: .
|
|
47580
|
+
r: .647,
|
|
47581
|
+
g: .647,
|
|
47582
|
+
b: .647
|
|
47550
47583
|
},
|
|
47551
47584
|
{
|
|
47552
47585
|
r: 1,
|
|
@@ -47554,9 +47587,9 @@ onmessage = async (ev) => {
|
|
|
47554
47587
|
b: 0
|
|
47555
47588
|
},
|
|
47556
47589
|
{
|
|
47557
|
-
r: .
|
|
47558
|
-
g: .
|
|
47559
|
-
b: .
|
|
47590
|
+
r: .357,
|
|
47591
|
+
g: .608,
|
|
47592
|
+
b: .835
|
|
47560
47593
|
},
|
|
47561
47594
|
{
|
|
47562
47595
|
r: .439,
|
|
@@ -47641,20 +47674,20 @@ onmessage = async (ev) => {
|
|
|
47641
47674
|
*/
|
|
47642
47675
|
function borderStyleToLineWidth(style) {
|
|
47643
47676
|
switch (style) {
|
|
47644
|
-
case "thin": return .
|
|
47645
|
-
case "medium": return
|
|
47646
|
-
case "thick": return 1
|
|
47647
|
-
case "double": return .
|
|
47648
|
-
case "hair": return .
|
|
47649
|
-
case "dotted": return .
|
|
47650
|
-
case "dashed": return .
|
|
47651
|
-
case "dashDot": return .
|
|
47652
|
-
case "dashDotDot": return .
|
|
47653
|
-
case "slantDashDot": return .
|
|
47654
|
-
case "mediumDashed": return
|
|
47655
|
-
case "mediumDashDot": return
|
|
47656
|
-
case "mediumDashDotDot": return
|
|
47657
|
-
default: return .
|
|
47677
|
+
case "thin": return .25;
|
|
47678
|
+
case "medium": return .5;
|
|
47679
|
+
case "thick": return 1;
|
|
47680
|
+
case "double": return .25;
|
|
47681
|
+
case "hair": return .1;
|
|
47682
|
+
case "dotted": return .25;
|
|
47683
|
+
case "dashed": return .25;
|
|
47684
|
+
case "dashDot": return .25;
|
|
47685
|
+
case "dashDotDot": return .25;
|
|
47686
|
+
case "slantDashDot": return .25;
|
|
47687
|
+
case "mediumDashed": return .5;
|
|
47688
|
+
case "mediumDashDot": return .5;
|
|
47689
|
+
case "mediumDashDotDot": return .5;
|
|
47690
|
+
default: return .25;
|
|
47658
47691
|
}
|
|
47659
47692
|
}
|
|
47660
47693
|
/**
|
|
@@ -47700,7 +47733,8 @@ onmessage = async (ev) => {
|
|
|
47700
47733
|
return {
|
|
47701
47734
|
width: borderStyleToLineWidth(border.style),
|
|
47702
47735
|
color: excelColorToPdf(border.color) ?? DEFAULT_COLORS.black,
|
|
47703
|
-
dashPattern: borderStyleToDashPattern(border.style)
|
|
47736
|
+
dashPattern: borderStyleToDashPattern(border.style),
|
|
47737
|
+
isDouble: border.style === "double"
|
|
47704
47738
|
};
|
|
47705
47739
|
}
|
|
47706
47740
|
/**
|
|
@@ -47744,489 +47778,6 @@ onmessage = async (ev) => {
|
|
|
47744
47778
|
}
|
|
47745
47779
|
}
|
|
47746
47780
|
//#endregion
|
|
47747
|
-
//#region src/modules/pdf/render/layout-engine.ts
|
|
47748
|
-
const EXCEL_CHAR_WIDTH_TO_POINTS = 7;
|
|
47749
|
-
const DEFAULT_COLUMN_WIDTH = 8.43;
|
|
47750
|
-
const DEFAULT_ROW_HEIGHT = 15;
|
|
47751
|
-
const MIN_COLUMN_WIDTH = 5;
|
|
47752
|
-
/**
|
|
47753
|
-
* Compute the layout for a sheet across one or more PDF pages.
|
|
47754
|
-
*/
|
|
47755
|
-
function layoutSheet(sheet, options, fontManager) {
|
|
47756
|
-
const { margins } = options;
|
|
47757
|
-
let pageWidth = options.pageSize.width;
|
|
47758
|
-
let pageHeight = options.pageSize.height;
|
|
47759
|
-
if (options.orientation === "landscape") [pageWidth, pageHeight] = [pageHeight, pageWidth];
|
|
47760
|
-
const contentWidth = pageWidth - margins.left - margins.right;
|
|
47761
|
-
const contentHeight = pageHeight - margins.top - margins.bottom;
|
|
47762
|
-
const headerHeight = options.showSheetNames ? 20 : 0;
|
|
47763
|
-
const footerHeight = options.showPageNumbers ? 20 : 0;
|
|
47764
|
-
const availableHeight = contentHeight - headerHeight - footerHeight;
|
|
47765
|
-
const printRange = getPrintRange(sheet);
|
|
47766
|
-
const { columnWidths, visibleCols } = computeColumnWidths(sheet, printRange);
|
|
47767
|
-
if (visibleCols.length === 0) return [emptyPage(pageWidth, pageHeight, sheet.name, options)];
|
|
47768
|
-
const totalTableWidth = columnWidths.reduce((sum, w) => sum + w, 0);
|
|
47769
|
-
let scaleFactor = options.scale;
|
|
47770
|
-
if (options.fitToPage && totalTableWidth > 0) {
|
|
47771
|
-
const fitScale = contentWidth / totalTableWidth;
|
|
47772
|
-
if (fitScale < 1) scaleFactor *= fitScale;
|
|
47773
|
-
}
|
|
47774
|
-
const scaledColumnWidths = columnWidths.map((w) => w * scaleFactor);
|
|
47775
|
-
const { rowHeights, visibleRows } = computeRowHeights(sheet, scaleFactor, printRange);
|
|
47776
|
-
const mergeMap = buildMergeMap(sheet);
|
|
47777
|
-
const rowPages = paginateRows(rowHeights, availableHeight, typeof options.repeatRows === "number" ? options.repeatRows : 0, buildRowBreakSet(sheet, visibleRows));
|
|
47778
|
-
const colGroups = paginateColumns(scaledColumnWidths, contentWidth, sheet, visibleCols);
|
|
47779
|
-
const layoutPages = [];
|
|
47780
|
-
for (const rowPage of rowPages) for (const colGroup of colGroups) {
|
|
47781
|
-
const cells = [];
|
|
47782
|
-
const groupColWidths = colGroup.map((ci) => scaledColumnWidths[ci]);
|
|
47783
|
-
const groupTotalWidth = groupColWidths.reduce((s, w) => s + w, 0);
|
|
47784
|
-
const groupColOffsets = [];
|
|
47785
|
-
let gx = margins.left;
|
|
47786
|
-
if (groupTotalWidth < contentWidth) gx = margins.left + (contentWidth - groupTotalWidth) / 2;
|
|
47787
|
-
for (const w of groupColWidths) {
|
|
47788
|
-
groupColOffsets.push(gx);
|
|
47789
|
-
gx += w;
|
|
47790
|
-
}
|
|
47791
|
-
const rowYPositions = [];
|
|
47792
|
-
const pageRowHeights = [];
|
|
47793
|
-
let currentY = pageHeight - margins.top - headerHeight;
|
|
47794
|
-
for (const rowIdx of rowPage) {
|
|
47795
|
-
const rowH = rowHeights[rowIdx] ?? DEFAULT_ROW_HEIGHT * scaleFactor;
|
|
47796
|
-
rowYPositions.push(currentY);
|
|
47797
|
-
pageRowHeights.push(rowH);
|
|
47798
|
-
currentY -= rowH;
|
|
47799
|
-
}
|
|
47800
|
-
for (let ri = 0; ri < rowPage.length; ri++) {
|
|
47801
|
-
const visibleRowIdx = rowPage[ri];
|
|
47802
|
-
const wsRowNumber = visibleRows[visibleRowIdx];
|
|
47803
|
-
for (let gci = 0; gci < colGroup.length; gci++) {
|
|
47804
|
-
const wsColNumber = visibleCols[colGroup[gci]];
|
|
47805
|
-
const mergeKey = `${wsRowNumber}:${wsColNumber}`;
|
|
47806
|
-
const mergeInfo = mergeMap.get(mergeKey);
|
|
47807
|
-
if (mergeInfo && !mergeInfo.isMaster) continue;
|
|
47808
|
-
const cell = sheet.rows.get(wsRowNumber)?.cells.get(wsColNumber);
|
|
47809
|
-
let colSpan = 1;
|
|
47810
|
-
let rowSpan = 1;
|
|
47811
|
-
if (mergeInfo && mergeInfo.isMaster) {
|
|
47812
|
-
const mergeEndCol = wsColNumber + mergeInfo.colSpan - 1;
|
|
47813
|
-
colSpan = 0;
|
|
47814
|
-
for (let s = gci; s < colGroup.length; s++) if (visibleCols[colGroup[s]] <= mergeEndCol) colSpan++;
|
|
47815
|
-
else break;
|
|
47816
|
-
const mergeEndRow = wsRowNumber + mergeInfo.rowSpan - 1;
|
|
47817
|
-
rowSpan = 0;
|
|
47818
|
-
for (let s = visibleRowIdx; s < visibleRows.length; s++) if (visibleRows[s] <= mergeEndRow) rowSpan++;
|
|
47819
|
-
else break;
|
|
47820
|
-
colSpan = Math.max(colSpan, 1);
|
|
47821
|
-
rowSpan = Math.max(rowSpan, 1);
|
|
47822
|
-
}
|
|
47823
|
-
const cellX = groupColOffsets[gci];
|
|
47824
|
-
const cellY = rowYPositions[ri];
|
|
47825
|
-
let cellWidth = 0;
|
|
47826
|
-
for (let s = 0; s < colSpan && gci + s < groupColWidths.length; s++) cellWidth += groupColWidths[gci + s];
|
|
47827
|
-
let cellHeight = 0;
|
|
47828
|
-
for (let s = 0; s < rowSpan && ri + s < pageRowHeights.length; s++) cellHeight += pageRowHeights[ri + s];
|
|
47829
|
-
const rectY = cellY - cellHeight;
|
|
47830
|
-
cells.push(buildLayoutCell(cell, cellX, rectY, cellWidth, cellHeight, colSpan, rowSpan, options, fontManager, scaleFactor));
|
|
47831
|
-
}
|
|
47832
|
-
}
|
|
47833
|
-
layoutPages.push({
|
|
47834
|
-
pageNumber: layoutPages.length + 1,
|
|
47835
|
-
options,
|
|
47836
|
-
cells,
|
|
47837
|
-
width: pageWidth,
|
|
47838
|
-
height: pageHeight,
|
|
47839
|
-
sheetName: sheet.name,
|
|
47840
|
-
sheetCols: colGroup.map((ci) => visibleCols[ci]),
|
|
47841
|
-
columnOffsets: groupColOffsets,
|
|
47842
|
-
columnWidths: groupColWidths,
|
|
47843
|
-
sheetRows: rowPage.map((ri) => visibleRows[ri]),
|
|
47844
|
-
rowYPositions,
|
|
47845
|
-
rowHeights: pageRowHeights,
|
|
47846
|
-
images: []
|
|
47847
|
-
});
|
|
47848
|
-
}
|
|
47849
|
-
if (layoutPages.length > 0 && sheet.images) assignImagesToPages(sheet.images, layoutPages);
|
|
47850
|
-
return layoutPages;
|
|
47851
|
-
}
|
|
47852
|
-
function emptyPage(width, height, sheetName, options) {
|
|
47853
|
-
return {
|
|
47854
|
-
pageNumber: 1,
|
|
47855
|
-
options,
|
|
47856
|
-
cells: [],
|
|
47857
|
-
width,
|
|
47858
|
-
height,
|
|
47859
|
-
sheetName,
|
|
47860
|
-
sheetCols: [],
|
|
47861
|
-
columnOffsets: [],
|
|
47862
|
-
columnWidths: [],
|
|
47863
|
-
sheetRows: [],
|
|
47864
|
-
rowYPositions: [],
|
|
47865
|
-
rowHeights: [],
|
|
47866
|
-
images: []
|
|
47867
|
-
};
|
|
47868
|
-
}
|
|
47869
|
-
/**
|
|
47870
|
-
* Parse a cell reference like "A1" into 0-indexed { c, r }.
|
|
47871
|
-
*/
|
|
47872
|
-
function parseCellRef(ref) {
|
|
47873
|
-
const upper = ref.replace(/\$/g, "").toUpperCase();
|
|
47874
|
-
let col = 0;
|
|
47875
|
-
let i = 0;
|
|
47876
|
-
while (i < upper.length && upper.charCodeAt(i) >= 65 && upper.charCodeAt(i) <= 90) {
|
|
47877
|
-
col = col * 26 + (upper.charCodeAt(i) - 64);
|
|
47878
|
-
i++;
|
|
47879
|
-
}
|
|
47880
|
-
const row = parseInt(upper.substring(i), 10);
|
|
47881
|
-
return {
|
|
47882
|
-
c: col - 1,
|
|
47883
|
-
r: row - 1
|
|
47884
|
-
};
|
|
47885
|
-
}
|
|
47886
|
-
/**
|
|
47887
|
-
* Parse a range string like "A1:B2" into 0-indexed start/end.
|
|
47888
|
-
*/
|
|
47889
|
-
function parseRangeRef(range) {
|
|
47890
|
-
const idx = range.indexOf(":");
|
|
47891
|
-
if (idx === -1) {
|
|
47892
|
-
const cell = parseCellRef(range);
|
|
47893
|
-
return {
|
|
47894
|
-
s: cell,
|
|
47895
|
-
e: { ...cell }
|
|
47896
|
-
};
|
|
47897
|
-
}
|
|
47898
|
-
return {
|
|
47899
|
-
s: parseCellRef(range.slice(0, idx)),
|
|
47900
|
-
e: parseCellRef(range.slice(idx + 1))
|
|
47901
|
-
};
|
|
47902
|
-
}
|
|
47903
|
-
/**
|
|
47904
|
-
* Get the print area range from the sheet's pageSetup.
|
|
47905
|
-
* Returns null if no print area is set.
|
|
47906
|
-
*/
|
|
47907
|
-
function getPrintRange(sheet) {
|
|
47908
|
-
const printArea = sheet.pageSetup?.printArea;
|
|
47909
|
-
if (!printArea || typeof printArea !== "string") return null;
|
|
47910
|
-
const firstRange = printArea.split("&&")[0].trim();
|
|
47911
|
-
if (!firstRange) return null;
|
|
47912
|
-
try {
|
|
47913
|
-
const range = parseRangeRef(firstRange);
|
|
47914
|
-
return {
|
|
47915
|
-
startRow: range.s.r + 1,
|
|
47916
|
-
endRow: range.e.r + 1,
|
|
47917
|
-
startCol: range.s.c + 1,
|
|
47918
|
-
endCol: range.e.c + 1
|
|
47919
|
-
};
|
|
47920
|
-
} catch {
|
|
47921
|
-
return null;
|
|
47922
|
-
}
|
|
47923
|
-
}
|
|
47924
|
-
function computeColumnWidths(sheet, printRange) {
|
|
47925
|
-
const bounds = sheet.bounds;
|
|
47926
|
-
if (!(bounds.top > 0 && bounds.left > 0)) return {
|
|
47927
|
-
columnWidths: [],
|
|
47928
|
-
visibleCols: []
|
|
47929
|
-
};
|
|
47930
|
-
const startCol = printRange?.startCol ?? bounds.left;
|
|
47931
|
-
const endCol = printRange?.endCol ?? bounds.right;
|
|
47932
|
-
const columnWidths = [];
|
|
47933
|
-
const visibleCols = [];
|
|
47934
|
-
for (let c = startCol; c <= endCol; c++) {
|
|
47935
|
-
const col = sheet.columns.get(c);
|
|
47936
|
-
if (col?.hidden) continue;
|
|
47937
|
-
const excelWidth = col?.width ?? DEFAULT_COLUMN_WIDTH;
|
|
47938
|
-
const pointWidth = Math.max(excelWidth * EXCEL_CHAR_WIDTH_TO_POINTS, MIN_COLUMN_WIDTH);
|
|
47939
|
-
columnWidths.push(pointWidth);
|
|
47940
|
-
visibleCols.push(c);
|
|
47941
|
-
}
|
|
47942
|
-
return {
|
|
47943
|
-
columnWidths,
|
|
47944
|
-
visibleCols
|
|
47945
|
-
};
|
|
47946
|
-
}
|
|
47947
|
-
function computeRowHeights(sheet, scaleFactor, printRange) {
|
|
47948
|
-
const bounds = sheet.bounds;
|
|
47949
|
-
if (bounds.top <= 0) return {
|
|
47950
|
-
rowHeights: [],
|
|
47951
|
-
visibleRows: []
|
|
47952
|
-
};
|
|
47953
|
-
const startRow = printRange?.startRow ?? bounds.top;
|
|
47954
|
-
const endRow = printRange?.endRow ?? bounds.bottom;
|
|
47955
|
-
const rowHeights = [];
|
|
47956
|
-
const visibleRows = [];
|
|
47957
|
-
for (let r = startRow; r <= endRow; r++) {
|
|
47958
|
-
const row = sheet.rows.get(r);
|
|
47959
|
-
if (row && row.hidden) continue;
|
|
47960
|
-
let height;
|
|
47961
|
-
if (row?.height) height = row.height;
|
|
47962
|
-
else {
|
|
47963
|
-
height = DEFAULT_ROW_HEIGHT;
|
|
47964
|
-
if (row) for (const cell of row.cells.values()) {
|
|
47965
|
-
let fontSize = cell.style?.font?.size ?? 11;
|
|
47966
|
-
const rtValue = cell.value;
|
|
47967
|
-
if (rtValue?.richText) for (const run of rtValue.richText) {
|
|
47968
|
-
const runSize = run.font?.size ?? fontSize;
|
|
47969
|
-
if (runSize > fontSize) fontSize = runSize;
|
|
47970
|
-
}
|
|
47971
|
-
const lineHeight = fontSize * 1.5;
|
|
47972
|
-
const text = cell.text ?? "";
|
|
47973
|
-
const lineCount = Math.max(1, (text.match(/\n/g) ?? []).length + 1);
|
|
47974
|
-
let wrapLineCount = lineCount;
|
|
47975
|
-
if (cell.style?.alignment?.wrapText && lineCount === 1 && text.length > 0) {
|
|
47976
|
-
const colPts = (sheet.columns.get(cell.col)?.width ?? DEFAULT_COLUMN_WIDTH) * EXCEL_CHAR_WIDTH_TO_POINTS * scaleFactor;
|
|
47977
|
-
const avgCharWidth = fontSize * .55;
|
|
47978
|
-
const charsPerLine = Math.max(1, Math.floor(colPts / avgCharWidth));
|
|
47979
|
-
wrapLineCount = Math.ceil(text.length / charsPerLine);
|
|
47980
|
-
}
|
|
47981
|
-
const neededHeight = lineHeight * wrapLineCount;
|
|
47982
|
-
if (neededHeight > height) height = neededHeight;
|
|
47983
|
-
}
|
|
47984
|
-
}
|
|
47985
|
-
rowHeights.push(height * scaleFactor);
|
|
47986
|
-
visibleRows.push(r);
|
|
47987
|
-
}
|
|
47988
|
-
return {
|
|
47989
|
-
rowHeights,
|
|
47990
|
-
visibleRows
|
|
47991
|
-
};
|
|
47992
|
-
}
|
|
47993
|
-
/**
|
|
47994
|
-
* Build a set of visible-row indices where manual page breaks occur.
|
|
47995
|
-
*/
|
|
47996
|
-
function buildRowBreakSet(sheet, visibleRows) {
|
|
47997
|
-
const breaks = /* @__PURE__ */ new Set();
|
|
47998
|
-
const rowBreaks = sheet.rowBreaks ?? [];
|
|
47999
|
-
if (rowBreaks.length === 0) return breaks;
|
|
48000
|
-
const rowToIndex = /* @__PURE__ */ new Map();
|
|
48001
|
-
for (let i = 0; i < visibleRows.length; i++) rowToIndex.set(visibleRows[i], i);
|
|
48002
|
-
for (const brk of rowBreaks) {
|
|
48003
|
-
const idx = rowToIndex.get(brk);
|
|
48004
|
-
if (idx !== void 0) breaks.add(idx + 1);
|
|
48005
|
-
}
|
|
48006
|
-
return breaks;
|
|
48007
|
-
}
|
|
48008
|
-
/**
|
|
48009
|
-
* Build a map of all merged cell regions.
|
|
48010
|
-
* Key: "row:col" (1-based), Value: merge info
|
|
48011
|
-
*/
|
|
48012
|
-
function buildMergeMap(sheet) {
|
|
48013
|
-
const map = /* @__PURE__ */ new Map();
|
|
48014
|
-
const merges = sheet.merges;
|
|
48015
|
-
if (!merges || merges.length === 0) return map;
|
|
48016
|
-
for (const rangeStr of merges) {
|
|
48017
|
-
const range = parseRangeRef(rangeStr);
|
|
48018
|
-
const top = range.s.r + 1;
|
|
48019
|
-
const left = range.s.c + 1;
|
|
48020
|
-
const bottom = range.e.r + 1;
|
|
48021
|
-
const right = range.e.c + 1;
|
|
48022
|
-
const rowSpan = bottom - top + 1;
|
|
48023
|
-
const colSpan = right - left + 1;
|
|
48024
|
-
for (let r = top; r <= bottom; r++) for (let c = left; c <= right; c++) map.set(`${r}:${c}`, {
|
|
48025
|
-
isMaster: r === top && c === left,
|
|
48026
|
-
rowSpan,
|
|
48027
|
-
colSpan
|
|
48028
|
-
});
|
|
48029
|
-
}
|
|
48030
|
-
return map;
|
|
48031
|
-
}
|
|
48032
|
-
function paginateRows(rowHeights, availableHeight, repeatRowCount, rowBreaks) {
|
|
48033
|
-
if (rowHeights.length === 0) return [[]];
|
|
48034
|
-
const pages = [];
|
|
48035
|
-
let currentPage = [];
|
|
48036
|
-
let currentPageHeight = 0;
|
|
48037
|
-
let isFirstPage = true;
|
|
48038
|
-
let repeatedPrefixCount = 0;
|
|
48039
|
-
const addRepeatRows = () => {
|
|
48040
|
-
repeatedPrefixCount = 0;
|
|
48041
|
-
for (let h = 0; h < repeatRowCount && h < rowHeights.length; h++) {
|
|
48042
|
-
if (currentPageHeight + rowHeights[h] > availableHeight && currentPage.length > 0) break;
|
|
48043
|
-
currentPage.push(h);
|
|
48044
|
-
currentPageHeight += rowHeights[h];
|
|
48045
|
-
repeatedPrefixCount++;
|
|
48046
|
-
}
|
|
48047
|
-
};
|
|
48048
|
-
for (let i = 0; i < rowHeights.length; i++) {
|
|
48049
|
-
const rowHeight = rowHeights[i];
|
|
48050
|
-
const pageAvailable = availableHeight;
|
|
48051
|
-
let skipRepeatedRow = false;
|
|
48052
|
-
while (true) {
|
|
48053
|
-
const forceBreak = rowBreaks.has(i) && currentPage.length > 0;
|
|
48054
|
-
if ((forceBreak || currentPageHeight + rowHeight > pageAvailable) && currentPage.length > 0) {
|
|
48055
|
-
if (!forceBreak && !isFirstPage && currentPage.length > 0 && currentPage.length === repeatedPrefixCount) {
|
|
48056
|
-
currentPage = [];
|
|
48057
|
-
currentPageHeight = 0;
|
|
48058
|
-
repeatedPrefixCount = 0;
|
|
48059
|
-
continue;
|
|
48060
|
-
}
|
|
48061
|
-
pages.push(currentPage);
|
|
48062
|
-
currentPage = [];
|
|
48063
|
-
currentPageHeight = 0;
|
|
48064
|
-
repeatedPrefixCount = 0;
|
|
48065
|
-
isFirstPage = false;
|
|
48066
|
-
addRepeatRows();
|
|
48067
|
-
continue;
|
|
48068
|
-
}
|
|
48069
|
-
if (!isFirstPage && i < repeatRowCount && currentPage.includes(i)) {
|
|
48070
|
-
skipRepeatedRow = true;
|
|
48071
|
-
break;
|
|
48072
|
-
}
|
|
48073
|
-
currentPage.push(i);
|
|
48074
|
-
currentPageHeight += rowHeight;
|
|
48075
|
-
break;
|
|
48076
|
-
}
|
|
48077
|
-
if (skipRepeatedRow) continue;
|
|
48078
|
-
}
|
|
48079
|
-
if (currentPage.length > 0) pages.push(currentPage);
|
|
48080
|
-
return pages.length > 0 ? pages : [[]];
|
|
48081
|
-
}
|
|
48082
|
-
/**
|
|
48083
|
-
* Split columns into groups for horizontal pagination.
|
|
48084
|
-
*/
|
|
48085
|
-
function paginateColumns(columnWidths, contentWidth, sheet, visibleCols) {
|
|
48086
|
-
if (columnWidths.length === 0) return [[]];
|
|
48087
|
-
const colBreaks = /* @__PURE__ */ new Set();
|
|
48088
|
-
const wsColBreaks = sheet.colBreaks ?? [];
|
|
48089
|
-
if (wsColBreaks.length > 0) {
|
|
48090
|
-
const colToIndex = /* @__PURE__ */ new Map();
|
|
48091
|
-
for (let i = 0; i < visibleCols.length; i++) colToIndex.set(visibleCols[i], i);
|
|
48092
|
-
for (const brk of wsColBreaks) {
|
|
48093
|
-
const idx = colToIndex.get(brk);
|
|
48094
|
-
if (idx !== void 0) colBreaks.add(idx + 1);
|
|
48095
|
-
}
|
|
48096
|
-
}
|
|
48097
|
-
const groups = [];
|
|
48098
|
-
let currentGroup = [];
|
|
48099
|
-
let currentWidth = 0;
|
|
48100
|
-
for (let i = 0; i < columnWidths.length; i++) {
|
|
48101
|
-
const colWidth = columnWidths[i];
|
|
48102
|
-
if ((colBreaks.has(i) && currentGroup.length > 0 || currentWidth + colWidth > contentWidth + .01) && currentGroup.length > 0) {
|
|
48103
|
-
groups.push(currentGroup);
|
|
48104
|
-
currentGroup = [];
|
|
48105
|
-
currentWidth = 0;
|
|
48106
|
-
}
|
|
48107
|
-
currentGroup.push(i);
|
|
48108
|
-
currentWidth += colWidth;
|
|
48109
|
-
}
|
|
48110
|
-
if (currentGroup.length > 0) groups.push(currentGroup);
|
|
48111
|
-
return groups.length > 0 ? groups : [Array.from({ length: columnWidths.length }, (_, i) => i)];
|
|
48112
|
-
}
|
|
48113
|
-
function buildLayoutCell(cell, x, y, width, height, colSpan, rowSpan, options, fontManager, scaleFactor) {
|
|
48114
|
-
const text = cell?.text ?? "";
|
|
48115
|
-
const style = cell?.style ?? {};
|
|
48116
|
-
const fontProps = extractFontProperties(style.font, options.defaultFontFamily, options.defaultFontSize);
|
|
48117
|
-
const scaledFontSize = fontProps.fontSize * scaleFactor;
|
|
48118
|
-
if (fontManager.hasEmbeddedFont()) fontManager.trackText(text);
|
|
48119
|
-
else {
|
|
48120
|
-
const pdfFontName = resolvePdfFontName(fontProps.fontFamily, fontProps.bold, fontProps.italic);
|
|
48121
|
-
fontManager.ensureFont(pdfFontName);
|
|
48122
|
-
}
|
|
48123
|
-
const richText = buildRichTextRuns(cell, options, fontManager, scaleFactor);
|
|
48124
|
-
return {
|
|
48125
|
-
text,
|
|
48126
|
-
rect: {
|
|
48127
|
-
x,
|
|
48128
|
-
y,
|
|
48129
|
-
width,
|
|
48130
|
-
height
|
|
48131
|
-
},
|
|
48132
|
-
fontFamily: fontProps.fontFamily,
|
|
48133
|
-
fontSize: scaledFontSize,
|
|
48134
|
-
bold: fontProps.bold,
|
|
48135
|
-
italic: fontProps.italic,
|
|
48136
|
-
strike: fontProps.strike,
|
|
48137
|
-
underline: fontProps.underline,
|
|
48138
|
-
textColor: fontProps.textColor,
|
|
48139
|
-
fillColor: excelFillToPdfColor(style.fill),
|
|
48140
|
-
horizontalAlign: excelHAlignToPdf(style.alignment),
|
|
48141
|
-
verticalAlign: excelVAlignToPdf(style.alignment),
|
|
48142
|
-
wrapText: style.alignment?.wrapText ?? false,
|
|
48143
|
-
borders: excelBordersToPdf(style.border),
|
|
48144
|
-
colSpan,
|
|
48145
|
-
rowSpan,
|
|
48146
|
-
hyperlink: cell?.hyperlink ?? null,
|
|
48147
|
-
richText,
|
|
48148
|
-
indent: style.alignment?.indent ?? 0,
|
|
48149
|
-
textRotation: style.alignment?.textRotation ?? 0
|
|
48150
|
-
};
|
|
48151
|
-
}
|
|
48152
|
-
/**
|
|
48153
|
-
* Assign pre-collected images to the pages that contain their top-left anchor.
|
|
48154
|
-
*/
|
|
48155
|
-
function assignImagesToPages(images, layoutPages) {
|
|
48156
|
-
for (const img of images) {
|
|
48157
|
-
const tl = img.range.tl;
|
|
48158
|
-
const tlCol = (tl.nativeCol ?? tl.col ?? 0) + 1;
|
|
48159
|
-
const tlRow = (tl.nativeRow ?? tl.row ?? 0) + 1;
|
|
48160
|
-
const targetPage = layoutPages.find((page) => page.sheetCols.includes(tlCol) && page.sheetRows.includes(tlRow));
|
|
48161
|
-
if (!targetPage) continue;
|
|
48162
|
-
const pageColIndex = targetPage.sheetCols.indexOf(tlCol);
|
|
48163
|
-
const pageRowIndex = targetPage.sheetRows.indexOf(tlRow);
|
|
48164
|
-
const baseX = targetPage.columnOffsets[pageColIndex] ?? targetPage.options.margins.left;
|
|
48165
|
-
const baseY = targetPage.rowYPositions[pageRowIndex] ?? targetPage.height - targetPage.options.margins.top - (targetPage.options.showSheetNames ? 20 : 0);
|
|
48166
|
-
const tlColOff = (tl.nativeColOff ?? 0) / 12700 || 0;
|
|
48167
|
-
const tlRowOff = (tl.nativeRowOff ?? 0) / 12700 || 0;
|
|
48168
|
-
const imgX = baseX + tlColOff;
|
|
48169
|
-
const imgY = baseY - tlRowOff;
|
|
48170
|
-
let imgWidth = 100;
|
|
48171
|
-
let imgHeight = 100;
|
|
48172
|
-
if (img.range.ext) {
|
|
48173
|
-
imgWidth = (img.range.ext.width ?? 100) * .75;
|
|
48174
|
-
imgHeight = (img.range.ext.height ?? 100) * .75;
|
|
48175
|
-
} else if (img.range.br) {
|
|
48176
|
-
const br = img.range.br;
|
|
48177
|
-
const brCol = (br.nativeCol ?? br.col ?? 0) + 1;
|
|
48178
|
-
const brRow = (br.nativeRow ?? br.row ?? 0) + 1;
|
|
48179
|
-
const brPageColIndex = targetPage.sheetCols.indexOf(brCol);
|
|
48180
|
-
const brPageRowIndex = targetPage.sheetRows.indexOf(brRow);
|
|
48181
|
-
const brBaseX = brPageColIndex >= 0 ? targetPage.columnOffsets[brPageColIndex] : imgX + (targetPage.columnWidths[pageColIndex] ?? 100);
|
|
48182
|
-
const brBaseY = brPageRowIndex >= 0 ? targetPage.rowYPositions[brPageRowIndex] : imgY - (targetPage.rowHeights[pageRowIndex] ?? 100);
|
|
48183
|
-
const brColOff = (br.nativeColOff ?? 0) / 12700 || 0;
|
|
48184
|
-
const brRowOff = (br.nativeRowOff ?? 0) / 12700 || 0;
|
|
48185
|
-
const brX = brBaseX + brColOff;
|
|
48186
|
-
const brY = brBaseY - brRowOff;
|
|
48187
|
-
imgWidth = brX - imgX;
|
|
48188
|
-
imgHeight = imgY - brY;
|
|
48189
|
-
}
|
|
48190
|
-
targetPage.images.push({
|
|
48191
|
-
data: img.data,
|
|
48192
|
-
format: img.format,
|
|
48193
|
-
rect: {
|
|
48194
|
-
x: imgX,
|
|
48195
|
-
y: imgY - imgHeight,
|
|
48196
|
-
width: Math.abs(imgWidth),
|
|
48197
|
-
height: Math.abs(imgHeight)
|
|
48198
|
-
}
|
|
48199
|
-
});
|
|
48200
|
-
}
|
|
48201
|
-
}
|
|
48202
|
-
/**
|
|
48203
|
-
* Build rich text runs from a RichText cell.
|
|
48204
|
-
* Returns null for non-RichText cells.
|
|
48205
|
-
*/
|
|
48206
|
-
function buildRichTextRuns(cell, options, fontManager, scaleFactor) {
|
|
48207
|
-
if (!cell || cell.type !== PdfCellType.RichText) return null;
|
|
48208
|
-
const rtValue = cell.value;
|
|
48209
|
-
if (!rtValue?.richText || rtValue.richText.length === 0) return null;
|
|
48210
|
-
return rtValue.richText.map((run) => {
|
|
48211
|
-
const fontProps = extractFontProperties(run.font, options.defaultFontFamily, options.defaultFontSize);
|
|
48212
|
-
if (fontManager.hasEmbeddedFont()) fontManager.trackText(run.text);
|
|
48213
|
-
else {
|
|
48214
|
-
const pdfFontName = resolvePdfFontName(fontProps.fontFamily, fontProps.bold, fontProps.italic);
|
|
48215
|
-
fontManager.ensureFont(pdfFontName);
|
|
48216
|
-
}
|
|
48217
|
-
return {
|
|
48218
|
-
text: run.text,
|
|
48219
|
-
fontFamily: fontProps.fontFamily,
|
|
48220
|
-
fontSize: fontProps.fontSize * scaleFactor,
|
|
48221
|
-
bold: fontProps.bold,
|
|
48222
|
-
italic: fontProps.italic,
|
|
48223
|
-
strike: fontProps.strike,
|
|
48224
|
-
underline: fontProps.underline,
|
|
48225
|
-
textColor: fontProps.textColor
|
|
48226
|
-
};
|
|
48227
|
-
});
|
|
48228
|
-
}
|
|
48229
|
-
//#endregion
|
|
48230
47781
|
//#region src/modules/pdf/core/pdf-stream.ts
|
|
48231
47782
|
/**
|
|
48232
47783
|
* PDF content stream builder.
|
|
@@ -48617,6 +48168,18 @@ onmessage = async (ev) => {
|
|
|
48617
48168
|
return 63;
|
|
48618
48169
|
}
|
|
48619
48170
|
//#endregion
|
|
48171
|
+
//#region src/modules/pdf/render/constants.ts
|
|
48172
|
+
/**
|
|
48173
|
+
* Line-height multiplier applied to the font size.
|
|
48174
|
+
*
|
|
48175
|
+
* Excel's default row height for an 11pt font is 15pt, which after removing
|
|
48176
|
+
* vertical padding (2 × 2 = 4pt) leaves 11pt × 1.0 — but Excel also adds
|
|
48177
|
+
* internal leading. A factor of 1.2 matches standard PDF/typographic practice
|
|
48178
|
+
* and keeps text readable without inflating row heights.
|
|
48179
|
+
*/
|
|
48180
|
+
const LINE_HEIGHT_FACTOR = 1.2;
|
|
48181
|
+
const PX_TO_PT = 72 / 96;
|
|
48182
|
+
//#endregion
|
|
48620
48183
|
//#region src/modules/pdf/render/page-renderer.ts
|
|
48621
48184
|
/**
|
|
48622
48185
|
* Page renderer for PDF generation.
|
|
@@ -48629,9 +48192,6 @@ onmessage = async (ev) => {
|
|
|
48629
48192
|
* - Grid lines
|
|
48630
48193
|
* - Page headers (sheet names) and footers (page numbers)
|
|
48631
48194
|
*/
|
|
48632
|
-
/** Internal cell padding in points */
|
|
48633
|
-
const CELL_PADDING_H = 3;
|
|
48634
|
-
const CELL_PADDING_V = 2;
|
|
48635
48195
|
/**
|
|
48636
48196
|
* Render a single page to a PDF content stream.
|
|
48637
48197
|
*/
|
|
@@ -48641,7 +48201,8 @@ onmessage = async (ev) => {
|
|
|
48641
48201
|
if (options.showGridLines) drawGridLines(stream, page, options);
|
|
48642
48202
|
for (const cell of page.cells) if (cell.fillColor) drawCellFill(stream, cell, alphaValues);
|
|
48643
48203
|
for (const cell of page.cells) drawCellBorders(stream, cell);
|
|
48644
|
-
|
|
48204
|
+
const sf = page.scaleFactor;
|
|
48205
|
+
for (const cell of page.cells) if (cell.text) drawCellText(stream, cell, fontManager, alphaValues, sf);
|
|
48645
48206
|
if (options.showSheetNames) drawPageHeader(stream, page, options, fontManager);
|
|
48646
48207
|
if (options.showPageNumbers) drawPageFooter(stream, page, options, fontManager, totalPages);
|
|
48647
48208
|
return {
|
|
@@ -48690,23 +48251,36 @@ onmessage = async (ev) => {
|
|
|
48690
48251
|
function drawCellBorders(stream, cell) {
|
|
48691
48252
|
const { rect, borders } = cell;
|
|
48692
48253
|
const { x, y, width, height } = rect;
|
|
48693
|
-
if (borders.top) drawBorderLine(stream, borders.top, x, y + height, x + width, y + height);
|
|
48694
|
-
if (borders.bottom) drawBorderLine(stream, borders.bottom, x, y, x + width, y);
|
|
48695
|
-
if (borders.left) drawBorderLine(stream, borders.left, x, y, x, y + height);
|
|
48696
|
-
if (borders.right) drawBorderLine(stream, borders.right, x + width, y, x + width, y + height);
|
|
48697
|
-
}
|
|
48698
|
-
function drawBorderLine(stream, border, x1, y1, x2, y2) {
|
|
48699
|
-
|
|
48254
|
+
if (borders.top) drawBorderLine(stream, borders.top, x, y + height, x + width, y + height, true);
|
|
48255
|
+
if (borders.bottom) drawBorderLine(stream, borders.bottom, x, y, x + width, y, true);
|
|
48256
|
+
if (borders.left) drawBorderLine(stream, borders.left, x, y, x, y + height, false);
|
|
48257
|
+
if (borders.right) drawBorderLine(stream, borders.right, x + width, y, x + width, y + height, false);
|
|
48258
|
+
}
|
|
48259
|
+
function drawBorderLine(stream, border, x1, y1, x2, y2, isHorizontal) {
|
|
48260
|
+
if (border.isDouble) {
|
|
48261
|
+
const offset = .4;
|
|
48262
|
+
const thinWidth = Math.min(border.width, .25);
|
|
48263
|
+
if (isHorizontal) {
|
|
48264
|
+
stream.drawLine(x1, y1 + offset, x2, y2 + offset, border.color, thinWidth, border.dashPattern);
|
|
48265
|
+
stream.drawLine(x1, y1 - offset, x2, y2 - offset, border.color, thinWidth, border.dashPattern);
|
|
48266
|
+
} else {
|
|
48267
|
+
stream.drawLine(x1 + offset, y1, x2 + offset, y2, border.color, thinWidth, border.dashPattern);
|
|
48268
|
+
stream.drawLine(x1 - offset, y1, x2 - offset, y2, border.color, thinWidth, border.dashPattern);
|
|
48269
|
+
}
|
|
48270
|
+
} else stream.drawLine(x1, y1, x2, y2, border.color, border.width, border.dashPattern);
|
|
48700
48271
|
}
|
|
48701
|
-
function drawCellText(stream, cell, fontManager, alphaValues) {
|
|
48272
|
+
function drawCellText(stream, cell, fontManager, alphaValues, scaleFactor = 1) {
|
|
48702
48273
|
const { rect, text, fontSize, horizontalAlign, verticalAlign, wrapText } = cell;
|
|
48703
48274
|
if (!text && !cell.richText) return;
|
|
48704
|
-
const
|
|
48705
|
-
const
|
|
48275
|
+
const padH = 3 * scaleFactor;
|
|
48276
|
+
const padV = 2 * scaleFactor;
|
|
48277
|
+
const availWidth = rect.width - padH * 2;
|
|
48278
|
+
const availHeight = rect.height - padV * 2;
|
|
48706
48279
|
if (availWidth <= 0 || availHeight <= 0) return;
|
|
48707
|
-
const indentPts = cell.indent *
|
|
48280
|
+
const indentPts = cell.indent * 10 * scaleFactor;
|
|
48281
|
+
const clipWidth = rect.width + (cell.textOverflowWidth || 0);
|
|
48708
48282
|
stream.save();
|
|
48709
|
-
stream.rect(rect.x, rect.y,
|
|
48283
|
+
stream.rect(rect.x, rect.y, clipWidth, rect.height);
|
|
48710
48284
|
stream.clip();
|
|
48711
48285
|
stream.endPath();
|
|
48712
48286
|
const textAlpha = cell.textColor.a;
|
|
@@ -48715,34 +48289,34 @@ onmessage = async (ev) => {
|
|
|
48715
48289
|
stream.setGraphicsState(alphaGsName(textAlpha));
|
|
48716
48290
|
}
|
|
48717
48291
|
if (cell.textRotation === "vertical") {
|
|
48718
|
-
drawVerticalStackedText(stream, cell, fontManager, indentPts);
|
|
48292
|
+
drawVerticalStackedText(stream, cell, fontManager, indentPts, scaleFactor);
|
|
48719
48293
|
stream.restore();
|
|
48720
48294
|
return;
|
|
48721
48295
|
}
|
|
48722
48296
|
if (typeof cell.textRotation === "number" && cell.textRotation !== 0) {
|
|
48723
|
-
drawRotatedText(stream, cell, fontManager, indentPts);
|
|
48297
|
+
drawRotatedText(stream, cell, fontManager, indentPts, scaleFactor);
|
|
48724
48298
|
stream.restore();
|
|
48725
48299
|
return;
|
|
48726
48300
|
}
|
|
48727
48301
|
if (cell.richText && cell.richText.length > 0) {
|
|
48728
|
-
drawRichText(stream, cell, fontManager, indentPts);
|
|
48302
|
+
drawRichText(stream, cell, fontManager, indentPts, scaleFactor);
|
|
48729
48303
|
stream.restore();
|
|
48730
48304
|
return;
|
|
48731
48305
|
}
|
|
48732
48306
|
const resourceName = fontManager.hasEmbeddedFont() ? fontManager.getEmbeddedResourceName() : fontManager.ensureFont(resolvePdfFontName(cell.fontFamily, cell.bold, cell.italic));
|
|
48733
48307
|
const measure = (s) => fontManager.measureText(s, resourceName, fontSize);
|
|
48734
|
-
const effectiveWidth = availWidth - indentPts
|
|
48735
|
-
const lines = wrapText ? wrapTextLines(text, measure, effectiveWidth) :
|
|
48736
|
-
const lineHeight = fontSize *
|
|
48308
|
+
const effectiveWidth = availWidth - indentPts;
|
|
48309
|
+
const lines = wrapText ? wrapTextLines(text, measure, effectiveWidth) : text.split(/\r?\n/);
|
|
48310
|
+
const lineHeight = fontSize * LINE_HEIGHT_FACTOR;
|
|
48737
48311
|
const ascent = fontManager.getFontAscent(resourceName, fontSize);
|
|
48738
|
-
const textStartY = computeTextStartY(verticalAlign, rect, lines.length * lineHeight, ascent);
|
|
48312
|
+
const textStartY = computeTextStartY(verticalAlign, rect, lines.length * lineHeight, ascent, padV);
|
|
48739
48313
|
stream.setFillColor(cell.textColor);
|
|
48740
48314
|
stream.beginText();
|
|
48741
48315
|
stream.setFont(resourceName, fontSize);
|
|
48742
48316
|
for (let i = 0; i < lines.length; i++) {
|
|
48743
48317
|
const line = lines[i];
|
|
48744
48318
|
const lineY = textStartY - i * lineHeight;
|
|
48745
|
-
const textX = computeTextX(horizontalAlign, rect, measure(line), indentPts);
|
|
48319
|
+
const textX = computeTextX(horizontalAlign, rect, measure(line), indentPts, padH);
|
|
48746
48320
|
stream.setTextMatrix(1, 0, 0, 1, textX, lineY);
|
|
48747
48321
|
const hexEncoded = fontManager.encodeText(line, resourceName);
|
|
48748
48322
|
if (hexEncoded) stream.showTextHex(hexEncoded);
|
|
@@ -48752,17 +48326,19 @@ onmessage = async (ev) => {
|
|
|
48752
48326
|
drawTextDecorations(stream, cell, lines, lineHeight, textStartY, measure, resourceName, fontManager, indentPts);
|
|
48753
48327
|
stream.restore();
|
|
48754
48328
|
}
|
|
48755
|
-
function drawRichText(stream, cell, fontManager, indentPts) {
|
|
48329
|
+
function drawRichText(stream, cell, fontManager, indentPts, scaleFactor = 1) {
|
|
48756
48330
|
const { rect, horizontalAlign, verticalAlign, wrapText } = cell;
|
|
48757
48331
|
const runs = cell.richText;
|
|
48332
|
+
const padH = 3 * scaleFactor;
|
|
48333
|
+
const padV = 2 * scaleFactor;
|
|
48758
48334
|
let maxFontSize = cell.fontSize;
|
|
48759
48335
|
for (const run of runs) if (run.fontSize > maxFontSize) maxFontSize = run.fontSize;
|
|
48760
48336
|
const primaryFontSize = maxFontSize;
|
|
48761
|
-
const lineHeight = primaryFontSize *
|
|
48337
|
+
const lineHeight = primaryFontSize * LINE_HEIGHT_FACTOR;
|
|
48762
48338
|
const isEmbedded = fontManager.hasEmbeddedFont();
|
|
48763
48339
|
const runResource = (run) => isEmbedded ? fontManager.getEmbeddedResourceName() : fontManager.ensureFont(resolvePdfFontName(run.fontFamily, run.bold, run.italic));
|
|
48764
48340
|
if (wrapText) {
|
|
48765
|
-
const availWidth = rect.width -
|
|
48341
|
+
const availWidth = rect.width - padH * 2 - indentPts;
|
|
48766
48342
|
if (availWidth <= 0) return;
|
|
48767
48343
|
const fullText = runs.map((r) => r.text).join("");
|
|
48768
48344
|
const primaryResource = runResource(runs[0]);
|
|
@@ -48772,7 +48348,7 @@ onmessage = async (ev) => {
|
|
|
48772
48348
|
for (let ri = 0; ri < runs.length; ri++) for (let ci = 0; ci < runs[ri].text.length; ci++) runForChar.push(ri);
|
|
48773
48349
|
const primaryResourceName = runResource(runs[0]);
|
|
48774
48350
|
const ascent = fontManager.getFontAscent(primaryResourceName, primaryFontSize);
|
|
48775
|
-
const textStartY = computeTextStartY(verticalAlign, rect, lines.length * lineHeight, ascent);
|
|
48351
|
+
const textStartY = computeTextStartY(verticalAlign, rect, lines.length * lineHeight, ascent, padV);
|
|
48776
48352
|
let charPos = 0;
|
|
48777
48353
|
for (let li = 0; li < lines.length; li++) {
|
|
48778
48354
|
const lineY = textStartY - li * lineHeight;
|
|
@@ -48800,7 +48376,7 @@ onmessage = async (ev) => {
|
|
|
48800
48376
|
}
|
|
48801
48377
|
let lineWidth = 0;
|
|
48802
48378
|
for (const seg of segments) lineWidth += fontManager.measureText(seg.text, seg.resourceName, seg.run.fontSize);
|
|
48803
|
-
let textX = computeTextX(horizontalAlign, rect, lineWidth, indentPts);
|
|
48379
|
+
let textX = computeTextX(horizontalAlign, rect, lineWidth, indentPts, padH);
|
|
48804
48380
|
for (const seg of segments) {
|
|
48805
48381
|
const { run, text, resourceName } = seg;
|
|
48806
48382
|
const segWidth = fontManager.measureText(text, resourceName, run.fontSize);
|
|
@@ -48837,8 +48413,8 @@ onmessage = async (ev) => {
|
|
|
48837
48413
|
totalWidth += w;
|
|
48838
48414
|
}
|
|
48839
48415
|
const primaryResourceName = runMetrics[0]?.resourceName ?? "F1";
|
|
48840
|
-
const textStartY = computeTextStartY(verticalAlign, rect, lineHeight, fontManager.getFontAscent(primaryResourceName, primaryFontSize));
|
|
48841
|
-
let textX = computeTextX(horizontalAlign, rect, totalWidth, indentPts);
|
|
48416
|
+
const textStartY = computeTextStartY(verticalAlign, rect, lineHeight, fontManager.getFontAscent(primaryResourceName, primaryFontSize), padV);
|
|
48417
|
+
let textX = computeTextX(horizontalAlign, rect, totalWidth, indentPts, padH);
|
|
48842
48418
|
for (let i = 0; i < runs.length; i++) {
|
|
48843
48419
|
const run = runs[i];
|
|
48844
48420
|
const { resourceName } = runMetrics[i];
|
|
@@ -48862,69 +48438,158 @@ onmessage = async (ev) => {
|
|
|
48862
48438
|
textX += runMetrics[i].width;
|
|
48863
48439
|
}
|
|
48864
48440
|
}
|
|
48865
|
-
function drawRotatedText(stream, cell, fontManager, indentPts) {
|
|
48866
|
-
const { rect,
|
|
48441
|
+
function drawRotatedText(stream, cell, fontManager, indentPts, scaleFactor = 1) {
|
|
48442
|
+
const { rect, wrapText } = cell;
|
|
48867
48443
|
let { fontSize } = cell;
|
|
48444
|
+
const padH = 3 * scaleFactor;
|
|
48445
|
+
const padV = 2 * scaleFactor;
|
|
48868
48446
|
const resourceName = fontManager.hasEmbeddedFont() ? fontManager.getEmbeddedResourceName() : fontManager.ensureFont(resolvePdfFontName(cell.fontFamily, cell.bold, cell.italic));
|
|
48869
48447
|
let degrees;
|
|
48870
|
-
if (typeof cell.textRotation === "number")
|
|
48871
|
-
else degrees = -(cell.textRotation - 90);
|
|
48448
|
+
if (typeof cell.textRotation === "number") degrees = cell.textRotation <= 90 ? cell.textRotation : -(cell.textRotation - 90);
|
|
48872
48449
|
else degrees = 0;
|
|
48873
48450
|
const radians = degrees * Math.PI / 180;
|
|
48874
48451
|
const cos = Math.cos(radians);
|
|
48875
48452
|
const sin = Math.sin(radians);
|
|
48876
|
-
const textWidth = fontManager.measureText(text, resourceName, fontSize);
|
|
48877
48453
|
const absSin = Math.abs(sin);
|
|
48878
48454
|
const absCos = Math.abs(cos);
|
|
48879
|
-
const
|
|
48880
|
-
const
|
|
48881
|
-
|
|
48882
|
-
|
|
48883
|
-
if (
|
|
48884
|
-
|
|
48885
|
-
|
|
48886
|
-
|
|
48887
|
-
|
|
48888
|
-
|
|
48889
|
-
const
|
|
48890
|
-
const
|
|
48455
|
+
const maxWidth = rect.width - padH * 2;
|
|
48456
|
+
const maxHeight = rect.height - padV * 2;
|
|
48457
|
+
let availTextLength;
|
|
48458
|
+
if (absSin > .01 && absCos > .01) availTextLength = Math.min(maxHeight / absSin, maxWidth / absCos);
|
|
48459
|
+
else if (absSin > .01) availTextLength = maxHeight / absSin;
|
|
48460
|
+
else availTextLength = maxWidth;
|
|
48461
|
+
const measure = (s) => fontManager.measureText(s, resourceName, fontSize);
|
|
48462
|
+
let lines;
|
|
48463
|
+
if (wrapText) lines = wrapTextLines(cell.text, measure, Math.max(availTextLength - 1, 1));
|
|
48464
|
+
else lines = cell.text.split(/\r?\n/);
|
|
48465
|
+
const lineHeight = fontSize * LINE_HEIGHT_FACTOR;
|
|
48466
|
+
const totalTextHeight = lines.length * lineHeight;
|
|
48467
|
+
if (!wrapText) {
|
|
48468
|
+
let maxLineWidth = 0;
|
|
48469
|
+
for (const line of lines) {
|
|
48470
|
+
const w = measure(line);
|
|
48471
|
+
if (w > maxLineWidth) maxLineWidth = w;
|
|
48472
|
+
}
|
|
48473
|
+
const rotatedWidth = maxLineWidth * absCos + totalTextHeight * absSin;
|
|
48474
|
+
const rotatedHeight = maxLineWidth * absSin + totalTextHeight * absCos;
|
|
48475
|
+
if (maxWidth > 0 && maxHeight > 0 && (rotatedWidth > maxWidth || rotatedHeight > maxHeight)) {
|
|
48476
|
+
const fitScale = Math.min(maxWidth / rotatedWidth, maxHeight / rotatedHeight);
|
|
48477
|
+
if (fitScale < 1) fontSize = fontSize * fitScale;
|
|
48478
|
+
}
|
|
48479
|
+
}
|
|
48480
|
+
const scaledLineHeight = fontSize * LINE_HEIGHT_FACTOR;
|
|
48891
48481
|
const ascent = fontManager.getFontAscent(resourceName, fontSize);
|
|
48892
|
-
const
|
|
48893
|
-
const
|
|
48894
|
-
const tx = cx + offsetX * cos - offsetY * sin;
|
|
48895
|
-
const ty = cy + offsetX * sin + offsetY * cos;
|
|
48482
|
+
const is90 = Math.abs(degrees - 90) < .01;
|
|
48483
|
+
const isMinus90 = Math.abs(degrees + 90) < .01;
|
|
48896
48484
|
stream.setFillColor(cell.textColor);
|
|
48897
|
-
|
|
48898
|
-
stream
|
|
48899
|
-
stream
|
|
48900
|
-
|
|
48901
|
-
|
|
48485
|
+
if (is90) drawRotated90(stream, cell, lines, fontManager, resourceName, fontSize, scaledLineHeight, ascent, padH, padV);
|
|
48486
|
+
else if (isMinus90) drawRotatedMinus90(stream, cell, lines, fontManager, resourceName, fontSize, scaledLineHeight, ascent, padH, padV);
|
|
48487
|
+
else drawRotatedGeneral(stream, cell, lines, fontManager, resourceName, fontSize, scaledLineHeight, ascent, cos, sin, indentPts);
|
|
48488
|
+
}
|
|
48489
|
+
/** 90° CCW: text reads bottom-to-top, lines stack left-to-right. */
|
|
48490
|
+
function drawRotated90(stream, cell, lines, fontManager, resourceName, fontSize, lineHeight, ascent, padH, padV) {
|
|
48491
|
+
const { rect, horizontalAlign, verticalAlign } = cell;
|
|
48492
|
+
const totalColumnsWidth = lines.length * lineHeight;
|
|
48493
|
+
let startX;
|
|
48494
|
+
if (horizontalAlign === "center" || lines.length === 1) startX = rect.x + rect.width / 2 - totalColumnsWidth / 2 + ascent;
|
|
48495
|
+
else if (horizontalAlign === "right") startX = rect.x + rect.width - padH - totalColumnsWidth + ascent;
|
|
48496
|
+
else startX = rect.x + padH + ascent;
|
|
48497
|
+
for (let i = 0; i < lines.length; i++) {
|
|
48498
|
+
const line = lines[i];
|
|
48499
|
+
const lineWidth = fontManager.measureText(line, resourceName, fontSize);
|
|
48500
|
+
const colX = startX + i * lineHeight;
|
|
48501
|
+
let ty;
|
|
48502
|
+
if (verticalAlign === "top") ty = rect.y + padV;
|
|
48503
|
+
else if (verticalAlign === "middle") ty = rect.y + (rect.height - lineWidth) / 2;
|
|
48504
|
+
else ty = rect.y + rect.height - padV - lineWidth;
|
|
48505
|
+
ty = Math.max(ty, rect.y + padV);
|
|
48506
|
+
stream.beginText();
|
|
48507
|
+
stream.setFont(resourceName, fontSize);
|
|
48508
|
+
stream.setTextMatrix(0, 1, -1, 0, colX, ty);
|
|
48509
|
+
emitText(stream, fontManager, line, resourceName);
|
|
48510
|
+
stream.endText();
|
|
48511
|
+
}
|
|
48512
|
+
}
|
|
48513
|
+
/** -90° (270° CW): text reads top-to-bottom, lines stack right-to-left. */
|
|
48514
|
+
function drawRotatedMinus90(stream, cell, lines, fontManager, resourceName, fontSize, lineHeight, ascent, padH, padV) {
|
|
48515
|
+
const { rect, horizontalAlign, verticalAlign } = cell;
|
|
48516
|
+
const totalColumnsWidth = lines.length * lineHeight;
|
|
48517
|
+
let startX;
|
|
48518
|
+
if (horizontalAlign === "center" || lines.length === 1) startX = rect.x + rect.width / 2 + totalColumnsWidth / 2 - lineHeight + ascent;
|
|
48519
|
+
else if (horizontalAlign === "right") startX = rect.x + rect.width - padH - lineHeight + ascent;
|
|
48520
|
+
else startX = rect.x + padH + totalColumnsWidth - lineHeight + ascent;
|
|
48521
|
+
for (let i = 0; i < lines.length; i++) {
|
|
48522
|
+
const line = lines[i];
|
|
48523
|
+
const lineWidth = fontManager.measureText(line, resourceName, fontSize);
|
|
48524
|
+
const colX = startX - i * lineHeight;
|
|
48525
|
+
let ty;
|
|
48526
|
+
if (verticalAlign === "top") ty = rect.y + rect.height - padV;
|
|
48527
|
+
else if (verticalAlign === "middle") ty = rect.y + (rect.height + lineWidth) / 2;
|
|
48528
|
+
else ty = rect.y + padV + lineWidth;
|
|
48529
|
+
ty = Math.min(ty, rect.y + rect.height - padV);
|
|
48530
|
+
stream.beginText();
|
|
48531
|
+
stream.setFont(resourceName, fontSize);
|
|
48532
|
+
stream.setTextMatrix(0, -1, 1, 0, colX, ty);
|
|
48533
|
+
emitText(stream, fontManager, line, resourceName);
|
|
48534
|
+
stream.endText();
|
|
48535
|
+
}
|
|
48536
|
+
}
|
|
48537
|
+
/** General rotation — center a multi-line text block in the cell. */
|
|
48538
|
+
function drawRotatedGeneral(stream, cell, lines, fontManager, resourceName, fontSize, lineHeight, ascent, cos, sin, indentPts) {
|
|
48539
|
+
const { rect, horizontalAlign } = cell;
|
|
48540
|
+
const indentOffset = horizontalAlign === "left" ? indentPts / 2 : horizontalAlign === "right" ? -indentPts / 2 : 0;
|
|
48541
|
+
const cx = rect.x + rect.width / 2 + indentOffset;
|
|
48542
|
+
const cy = rect.y + rect.height / 2;
|
|
48543
|
+
for (let i = 0; i < lines.length; i++) {
|
|
48544
|
+
const line = lines[i];
|
|
48545
|
+
const lineWidth = fontManager.measureText(line, resourceName, fontSize);
|
|
48546
|
+
const lineOffset = (i - (lines.length - 1) / 2) * lineHeight;
|
|
48547
|
+
const offsetX = -lineWidth / 2;
|
|
48548
|
+
const offsetY = -ascent / 2 - lineOffset;
|
|
48549
|
+
const tx = cx + offsetX * cos - offsetY * sin;
|
|
48550
|
+
const ty = cy + offsetX * sin + offsetY * cos;
|
|
48551
|
+
stream.beginText();
|
|
48552
|
+
stream.setFont(resourceName, fontSize);
|
|
48553
|
+
stream.setTextMatrix(cos, sin, -sin, cos, tx, ty);
|
|
48554
|
+
emitText(stream, fontManager, line, resourceName);
|
|
48555
|
+
stream.endText();
|
|
48556
|
+
}
|
|
48557
|
+
}
|
|
48558
|
+
/** Emit a text string with hex encoding if available. */
|
|
48559
|
+
function emitText(stream, fontManager, text, resourceName) {
|
|
48560
|
+
const hex = fontManager.encodeText(text, resourceName);
|
|
48561
|
+
if (hex) stream.showTextHex(hex);
|
|
48902
48562
|
else stream.showText(text);
|
|
48903
|
-
stream.endText();
|
|
48904
48563
|
}
|
|
48905
48564
|
/**
|
|
48906
48565
|
* Draw vertical stacked text (each character top-to-bottom).
|
|
48566
|
+
* Newlines (\n) start a new column to the right.
|
|
48907
48567
|
*/
|
|
48908
|
-
function drawVerticalStackedText(stream, cell, fontManager, _indentPts) {
|
|
48568
|
+
function drawVerticalStackedText(stream, cell, fontManager, _indentPts, scaleFactor = 1) {
|
|
48909
48569
|
const { rect, text, fontSize } = cell;
|
|
48570
|
+
const padV = 2 * scaleFactor;
|
|
48910
48571
|
const resourceName = fontManager.hasEmbeddedFont() ? fontManager.getEmbeddedResourceName() : fontManager.ensureFont(resolvePdfFontName(cell.fontFamily, cell.bold, cell.italic));
|
|
48911
48572
|
const charHeight = fontSize * 1.3;
|
|
48912
48573
|
const ascent = fontManager.getFontAscent(resourceName, fontSize);
|
|
48913
|
-
const
|
|
48914
|
-
|
|
48574
|
+
const columns = text.split(/\r?\n/);
|
|
48575
|
+
const columnWidth = fontSize * 1.4;
|
|
48576
|
+
const totalColumnsWidth = columns.length * columnWidth;
|
|
48577
|
+
const startX = rect.x + rect.width / 2 - totalColumnsWidth / 2 + columnWidth / 2;
|
|
48915
48578
|
stream.setFillColor(cell.textColor);
|
|
48916
|
-
for (let
|
|
48917
|
-
|
|
48918
|
-
const
|
|
48919
|
-
|
|
48920
|
-
|
|
48921
|
-
|
|
48922
|
-
|
|
48923
|
-
|
|
48924
|
-
|
|
48925
|
-
|
|
48926
|
-
|
|
48927
|
-
|
|
48579
|
+
for (let colIdx = 0; colIdx < columns.length; colIdx++) {
|
|
48580
|
+
const colText = columns[colIdx];
|
|
48581
|
+
const colX = startX + colIdx * columnWidth;
|
|
48582
|
+
let currentY = rect.y + rect.height - padV - ascent;
|
|
48583
|
+
for (const ch of colText) {
|
|
48584
|
+
if (currentY < rect.y + padV) break;
|
|
48585
|
+
const charWidth = fontManager.measureText(ch, resourceName, fontSize);
|
|
48586
|
+
stream.beginText();
|
|
48587
|
+
stream.setFont(resourceName, fontSize);
|
|
48588
|
+
stream.setTextMatrix(1, 0, 0, 1, colX - charWidth / 2, currentY);
|
|
48589
|
+
emitText(stream, fontManager, ch, resourceName);
|
|
48590
|
+
stream.endText();
|
|
48591
|
+
currentY -= charHeight;
|
|
48592
|
+
}
|
|
48928
48593
|
}
|
|
48929
48594
|
}
|
|
48930
48595
|
/**
|
|
@@ -48935,41 +48600,39 @@ onmessage = async (ev) => {
|
|
|
48935
48600
|
function alphaGsName(alpha) {
|
|
48936
48601
|
return `GS${Math.round(alpha * 1e4)}`;
|
|
48937
48602
|
}
|
|
48938
|
-
|
|
48939
|
-
const INDENT_WIDTH = 10;
|
|
48940
|
-
function computeTextStartY(verticalAlign, rect, totalTextHeight, ascent) {
|
|
48603
|
+
function computeTextStartY(verticalAlign, rect, totalTextHeight, ascent, padV = 2) {
|
|
48941
48604
|
let y;
|
|
48942
48605
|
switch (verticalAlign) {
|
|
48943
48606
|
case "top":
|
|
48944
|
-
y = rect.y + rect.height -
|
|
48607
|
+
y = rect.y + rect.height - padV - ascent;
|
|
48945
48608
|
break;
|
|
48946
48609
|
case "middle":
|
|
48947
48610
|
y = rect.y + rect.height / 2 + totalTextHeight / 2 - ascent;
|
|
48948
48611
|
break;
|
|
48949
48612
|
default:
|
|
48950
|
-
y = rect.y +
|
|
48613
|
+
y = rect.y + padV + (totalTextHeight - ascent);
|
|
48951
48614
|
break;
|
|
48952
48615
|
}
|
|
48953
|
-
const maxY = rect.y + rect.height -
|
|
48616
|
+
const maxY = rect.y + rect.height - padV - ascent;
|
|
48954
48617
|
if (y > maxY) y = maxY;
|
|
48955
|
-
const minY = rect.y +
|
|
48618
|
+
const minY = rect.y + padV;
|
|
48956
48619
|
if (y < minY) y = minY;
|
|
48957
48620
|
return y;
|
|
48958
48621
|
}
|
|
48959
|
-
function computeTextX(align, rect, textWidth, indentPts = 0) {
|
|
48622
|
+
function computeTextX(align, rect, textWidth, indentPts = 0, padH = 3) {
|
|
48960
48623
|
let x;
|
|
48961
48624
|
switch (align) {
|
|
48962
48625
|
case "center":
|
|
48963
48626
|
x = rect.x + (rect.width - textWidth) / 2;
|
|
48964
48627
|
break;
|
|
48965
48628
|
case "right":
|
|
48966
|
-
x = rect.x + rect.width -
|
|
48629
|
+
x = rect.x + rect.width - padH - textWidth;
|
|
48967
48630
|
break;
|
|
48968
48631
|
default:
|
|
48969
|
-
x = rect.x +
|
|
48632
|
+
x = rect.x + padH + indentPts;
|
|
48970
48633
|
break;
|
|
48971
48634
|
}
|
|
48972
|
-
const minX = rect.x +
|
|
48635
|
+
const minX = rect.x + padH;
|
|
48973
48636
|
if (x < minX) x = minX;
|
|
48974
48637
|
return x;
|
|
48975
48638
|
}
|
|
@@ -49067,6 +48730,630 @@ onmessage = async (ev) => {
|
|
|
49067
48730
|
stream.restore();
|
|
49068
48731
|
}
|
|
49069
48732
|
//#endregion
|
|
48733
|
+
//#region src/modules/pdf/render/layout-engine.ts
|
|
48734
|
+
const DEFAULT_COLUMN_WIDTH = 8.43;
|
|
48735
|
+
const DEFAULT_ROW_HEIGHT = 15;
|
|
48736
|
+
const MIN_COLUMN_WIDTH = 3;
|
|
48737
|
+
/**
|
|
48738
|
+
* Resolve horizontal alignment, using Excel's type-based defaults when
|
|
48739
|
+
* no explicit alignment is set (or when alignment is "general"):
|
|
48740
|
+
* - Numbers/Dates → right
|
|
48741
|
+
* - Booleans/Errors → center
|
|
48742
|
+
* - Text/RichText/Hyperlink → left
|
|
48743
|
+
* - Formulas → based on result type
|
|
48744
|
+
*/
|
|
48745
|
+
function resolveHorizontalAlign(alignment, cellType, formulaResult) {
|
|
48746
|
+
if (alignment?.horizontal && alignment.horizontal !== "general") return excelHAlignToPdf(alignment);
|
|
48747
|
+
if (cellType !== void 0) switch (cellType) {
|
|
48748
|
+
case PdfCellType.Number:
|
|
48749
|
+
case PdfCellType.Date: return "right";
|
|
48750
|
+
case PdfCellType.Boolean:
|
|
48751
|
+
case PdfCellType.Error: return "center";
|
|
48752
|
+
case PdfCellType.Formula:
|
|
48753
|
+
if (typeof formulaResult === "number" || formulaResult instanceof Date) return "right";
|
|
48754
|
+
if (typeof formulaResult === "boolean") return "center";
|
|
48755
|
+
return "left";
|
|
48756
|
+
default: return "left";
|
|
48757
|
+
}
|
|
48758
|
+
return "left";
|
|
48759
|
+
}
|
|
48760
|
+
/**
|
|
48761
|
+
* Compute the layout for a sheet across one or more PDF pages.
|
|
48762
|
+
* Yields to the event loop between each output page.
|
|
48763
|
+
*/
|
|
48764
|
+
async function layoutSheet(sheet, options, fontManager) {
|
|
48765
|
+
const ctx = prepareLayout(sheet, options, fontManager);
|
|
48766
|
+
if (!ctx) return [createEmptyPage(sheet, options)];
|
|
48767
|
+
const layoutPages = [];
|
|
48768
|
+
const totalOutputPages = ctx.rowPages.length * ctx.colGroups.length;
|
|
48769
|
+
for (const rowPage of ctx.rowPages) for (const colGroup of ctx.colGroups) {
|
|
48770
|
+
layoutPages.push(buildPageLayout(ctx, rowPage, colGroup, layoutPages.length, sheet, options, fontManager));
|
|
48771
|
+
if (layoutPages.length < totalOutputPages) await yieldToEventLoop();
|
|
48772
|
+
}
|
|
48773
|
+
if (layoutPages.length > 0 && sheet.images) assignImagesToPages(sheet.images, layoutPages, ctx.scaleFactor);
|
|
48774
|
+
return layoutPages;
|
|
48775
|
+
}
|
|
48776
|
+
/**
|
|
48777
|
+
* Steps 1–5: compute columns, scale, rows, merges, pagination.
|
|
48778
|
+
* Returns null if the sheet has no visible columns (→ caller should emit an empty page).
|
|
48779
|
+
*/
|
|
48780
|
+
function prepareLayout(sheet, options, fontManager) {
|
|
48781
|
+
const { margins } = options;
|
|
48782
|
+
let pageWidth = options.pageSize.width;
|
|
48783
|
+
let pageHeight = options.pageSize.height;
|
|
48784
|
+
if (options.orientation === "landscape") [pageWidth, pageHeight] = [pageHeight, pageWidth];
|
|
48785
|
+
const contentWidth = pageWidth - margins.left - margins.right;
|
|
48786
|
+
const contentHeight = pageHeight - margins.top - margins.bottom;
|
|
48787
|
+
const headerHeight = options.showSheetNames ? 20 : 0;
|
|
48788
|
+
const footerHeight = options.showPageNumbers ? 20 : 0;
|
|
48789
|
+
const availableHeight = contentHeight - headerHeight - footerHeight;
|
|
48790
|
+
const printRange = getPrintRange(sheet);
|
|
48791
|
+
const { columnWidths, visibleCols } = computeColumnWidths(sheet, printRange);
|
|
48792
|
+
if (visibleCols.length === 0) return null;
|
|
48793
|
+
const totalTableWidth = columnWidths.reduce((sum, w) => sum + w, 0);
|
|
48794
|
+
let scaleFactor = options.scale;
|
|
48795
|
+
if (options.fitToPage && totalTableWidth > 0) {
|
|
48796
|
+
const fitScale = contentWidth / totalTableWidth;
|
|
48797
|
+
if (fitScale < 1) scaleFactor *= fitScale;
|
|
48798
|
+
}
|
|
48799
|
+
const scaledColumnWidths = columnWidths.map((w) => w * scaleFactor);
|
|
48800
|
+
const { rowHeights, visibleRows } = computeRowHeights(sheet, scaleFactor, printRange, fontManager, options);
|
|
48801
|
+
const mergeMap = buildMergeMap(sheet);
|
|
48802
|
+
const rowPages = paginateRows(rowHeights, availableHeight, typeof options.repeatRows === "number" ? options.repeatRows : 0, buildRowBreakSet(sheet, visibleRows));
|
|
48803
|
+
const colGroups = paginateColumns(scaledColumnWidths, contentWidth, sheet, visibleCols);
|
|
48804
|
+
return {
|
|
48805
|
+
pageWidth,
|
|
48806
|
+
pageHeight,
|
|
48807
|
+
contentWidth,
|
|
48808
|
+
headerHeight,
|
|
48809
|
+
scaleFactor,
|
|
48810
|
+
scaledColumnWidths,
|
|
48811
|
+
rowHeights,
|
|
48812
|
+
visibleRows,
|
|
48813
|
+
visibleCols,
|
|
48814
|
+
mergeMap,
|
|
48815
|
+
rowPages,
|
|
48816
|
+
colGroups,
|
|
48817
|
+
margins
|
|
48818
|
+
};
|
|
48819
|
+
}
|
|
48820
|
+
/**
|
|
48821
|
+
* Build the LayoutPage for a single rowPage × colGroup combination.
|
|
48822
|
+
*/
|
|
48823
|
+
function buildPageLayout(ctx, rowPage, colGroup, currentPageCount, sheet, options, fontManager) {
|
|
48824
|
+
const { scaledColumnWidths, rowHeights, visibleRows, visibleCols, mergeMap, pageWidth, pageHeight, contentWidth, headerHeight, scaleFactor, margins } = ctx;
|
|
48825
|
+
const cells = [];
|
|
48826
|
+
const groupColWidths = colGroup.map((ci) => scaledColumnWidths[ci]);
|
|
48827
|
+
const groupTotalWidth = groupColWidths.reduce((s, w) => s + w, 0);
|
|
48828
|
+
const groupColOffsets = [];
|
|
48829
|
+
let gx = margins.left;
|
|
48830
|
+
if (groupTotalWidth < contentWidth) gx = margins.left + (contentWidth - groupTotalWidth) / 2;
|
|
48831
|
+
for (const w of groupColWidths) {
|
|
48832
|
+
groupColOffsets.push(gx);
|
|
48833
|
+
gx += w;
|
|
48834
|
+
}
|
|
48835
|
+
const rowYPositions = [];
|
|
48836
|
+
const pageRowHeights = [];
|
|
48837
|
+
let currentY = pageHeight - margins.top - headerHeight;
|
|
48838
|
+
for (const rowIdx of rowPage) {
|
|
48839
|
+
const rowH = rowHeights[rowIdx] ?? DEFAULT_ROW_HEIGHT * scaleFactor;
|
|
48840
|
+
rowYPositions.push(currentY);
|
|
48841
|
+
pageRowHeights.push(rowH);
|
|
48842
|
+
currentY -= rowH;
|
|
48843
|
+
}
|
|
48844
|
+
const cellGrid = /* @__PURE__ */ new Map();
|
|
48845
|
+
for (let ri = 0; ri < rowPage.length; ri++) {
|
|
48846
|
+
const visibleRowIdx = rowPage[ri];
|
|
48847
|
+
const wsRowNumber = visibleRows[visibleRowIdx];
|
|
48848
|
+
for (let gci = 0; gci < colGroup.length; gci++) {
|
|
48849
|
+
const wsColNumber = visibleCols[colGroup[gci]];
|
|
48850
|
+
const mergeKey = `${wsRowNumber}:${wsColNumber}`;
|
|
48851
|
+
const mergeInfo = mergeMap.get(mergeKey);
|
|
48852
|
+
if (mergeInfo && !mergeInfo.isMaster) continue;
|
|
48853
|
+
const cell = sheet.rows.get(wsRowNumber)?.cells.get(wsColNumber);
|
|
48854
|
+
let colSpan = 1;
|
|
48855
|
+
let rowSpan = 1;
|
|
48856
|
+
if (mergeInfo && mergeInfo.isMaster) {
|
|
48857
|
+
const mergeEndCol = wsColNumber + mergeInfo.colSpan - 1;
|
|
48858
|
+
colSpan = 0;
|
|
48859
|
+
for (let s = gci; s < colGroup.length; s++) if (visibleCols[colGroup[s]] <= mergeEndCol) colSpan++;
|
|
48860
|
+
else break;
|
|
48861
|
+
const mergeEndRow = wsRowNumber + mergeInfo.rowSpan - 1;
|
|
48862
|
+
rowSpan = 0;
|
|
48863
|
+
for (let s = visibleRowIdx; s < visibleRows.length; s++) if (visibleRows[s] <= mergeEndRow) rowSpan++;
|
|
48864
|
+
else break;
|
|
48865
|
+
colSpan = Math.max(colSpan, 1);
|
|
48866
|
+
rowSpan = Math.max(rowSpan, 1);
|
|
48867
|
+
}
|
|
48868
|
+
const cellX = groupColOffsets[gci];
|
|
48869
|
+
const cellY = rowYPositions[ri];
|
|
48870
|
+
let cellWidth = 0;
|
|
48871
|
+
for (let s = 0; s < colSpan && gci + s < groupColWidths.length; s++) cellWidth += groupColWidths[gci + s];
|
|
48872
|
+
let cellHeight = 0;
|
|
48873
|
+
for (let s = 0; s < rowSpan && ri + s < pageRowHeights.length; s++) cellHeight += pageRowHeights[ri + s];
|
|
48874
|
+
const rectY = cellY - cellHeight;
|
|
48875
|
+
cells.push(buildLayoutCell(cell, cellX, rectY, cellWidth, cellHeight, colSpan, rowSpan, options, fontManager, scaleFactor));
|
|
48876
|
+
const layoutCell = cells[cells.length - 1];
|
|
48877
|
+
if (mergeInfo?.isMaster) propagateMergeBorders(layoutCell, mergeInfo, wsRowNumber, wsColNumber, sheet);
|
|
48878
|
+
cellGrid.set(`${ri}:${gci}`, layoutCell);
|
|
48879
|
+
}
|
|
48880
|
+
}
|
|
48881
|
+
computeTextOverflows(cellGrid, rowPage, colGroup, visibleRows, visibleCols, groupColWidths, mergeMap, fontManager);
|
|
48882
|
+
return {
|
|
48883
|
+
pageNumber: currentPageCount + 1,
|
|
48884
|
+
options,
|
|
48885
|
+
cells,
|
|
48886
|
+
width: pageWidth,
|
|
48887
|
+
height: pageHeight,
|
|
48888
|
+
sheetName: sheet.name,
|
|
48889
|
+
sheetCols: colGroup.map((ci) => visibleCols[ci]),
|
|
48890
|
+
columnOffsets: groupColOffsets,
|
|
48891
|
+
columnWidths: groupColWidths,
|
|
48892
|
+
sheetRows: rowPage.map((ri) => visibleRows[ri]),
|
|
48893
|
+
rowYPositions,
|
|
48894
|
+
rowHeights: pageRowHeights,
|
|
48895
|
+
images: [],
|
|
48896
|
+
scaleFactor
|
|
48897
|
+
};
|
|
48898
|
+
}
|
|
48899
|
+
function createEmptyPage(sheet, options) {
|
|
48900
|
+
let pageWidth = options.pageSize.width;
|
|
48901
|
+
let pageHeight = options.pageSize.height;
|
|
48902
|
+
if (options.orientation === "landscape") [pageWidth, pageHeight] = [pageHeight, pageWidth];
|
|
48903
|
+
return {
|
|
48904
|
+
pageNumber: 1,
|
|
48905
|
+
options,
|
|
48906
|
+
cells: [],
|
|
48907
|
+
width: pageWidth,
|
|
48908
|
+
height: pageHeight,
|
|
48909
|
+
sheetName: sheet.name,
|
|
48910
|
+
sheetCols: [],
|
|
48911
|
+
columnOffsets: [],
|
|
48912
|
+
columnWidths: [],
|
|
48913
|
+
sheetRows: [],
|
|
48914
|
+
rowYPositions: [],
|
|
48915
|
+
rowHeights: [],
|
|
48916
|
+
images: [],
|
|
48917
|
+
scaleFactor: 1
|
|
48918
|
+
};
|
|
48919
|
+
}
|
|
48920
|
+
/**
|
|
48921
|
+
* Parse a cell reference like "A1" into 0-indexed { c, r }.
|
|
48922
|
+
*/
|
|
48923
|
+
function parseCellRef(ref) {
|
|
48924
|
+
const upper = ref.replace(/\$/g, "").toUpperCase();
|
|
48925
|
+
let col = 0;
|
|
48926
|
+
let i = 0;
|
|
48927
|
+
while (i < upper.length && upper.charCodeAt(i) >= 65 && upper.charCodeAt(i) <= 90) {
|
|
48928
|
+
col = col * 26 + (upper.charCodeAt(i) - 64);
|
|
48929
|
+
i++;
|
|
48930
|
+
}
|
|
48931
|
+
const row = parseInt(upper.substring(i), 10);
|
|
48932
|
+
return {
|
|
48933
|
+
c: col - 1,
|
|
48934
|
+
r: row - 1
|
|
48935
|
+
};
|
|
48936
|
+
}
|
|
48937
|
+
/**
|
|
48938
|
+
* Parse a range string like "A1:B2" into 0-indexed start/end.
|
|
48939
|
+
*/
|
|
48940
|
+
function parseRangeRef(range) {
|
|
48941
|
+
const idx = range.indexOf(":");
|
|
48942
|
+
if (idx === -1) {
|
|
48943
|
+
const cell = parseCellRef(range);
|
|
48944
|
+
return {
|
|
48945
|
+
s: cell,
|
|
48946
|
+
e: { ...cell }
|
|
48947
|
+
};
|
|
48948
|
+
}
|
|
48949
|
+
return {
|
|
48950
|
+
s: parseCellRef(range.slice(0, idx)),
|
|
48951
|
+
e: parseCellRef(range.slice(idx + 1))
|
|
48952
|
+
};
|
|
48953
|
+
}
|
|
48954
|
+
/**
|
|
48955
|
+
* Get the print area range from the sheet's pageSetup.
|
|
48956
|
+
* Returns null if no print area is set.
|
|
48957
|
+
*/
|
|
48958
|
+
function getPrintRange(sheet) {
|
|
48959
|
+
const printArea = sheet.pageSetup?.printArea;
|
|
48960
|
+
if (!printArea || typeof printArea !== "string") return null;
|
|
48961
|
+
const firstRange = printArea.split("&&")[0].trim();
|
|
48962
|
+
if (!firstRange) return null;
|
|
48963
|
+
try {
|
|
48964
|
+
const range = parseRangeRef(firstRange);
|
|
48965
|
+
return {
|
|
48966
|
+
startRow: range.s.r + 1,
|
|
48967
|
+
endRow: range.e.r + 1,
|
|
48968
|
+
startCol: range.s.c + 1,
|
|
48969
|
+
endCol: range.e.c + 1
|
|
48970
|
+
};
|
|
48971
|
+
} catch {
|
|
48972
|
+
return null;
|
|
48973
|
+
}
|
|
48974
|
+
}
|
|
48975
|
+
function computeColumnWidths(sheet, printRange) {
|
|
48976
|
+
const bounds = sheet.bounds;
|
|
48977
|
+
if (!(bounds.top > 0 && bounds.left > 0)) return {
|
|
48978
|
+
columnWidths: [],
|
|
48979
|
+
visibleCols: []
|
|
48980
|
+
};
|
|
48981
|
+
const startCol = printRange?.startCol ?? bounds.left;
|
|
48982
|
+
const endCol = printRange?.endCol ?? bounds.right;
|
|
48983
|
+
const columnWidths = [];
|
|
48984
|
+
const visibleCols = [];
|
|
48985
|
+
for (let c = startCol; c <= endCol; c++) {
|
|
48986
|
+
const col = sheet.columns.get(c);
|
|
48987
|
+
if (col?.hidden) continue;
|
|
48988
|
+
const pixelWidth = (col?.width ?? DEFAULT_COLUMN_WIDTH) * 7 + 5;
|
|
48989
|
+
const pointWidth = Math.max(pixelWidth * PX_TO_PT, MIN_COLUMN_WIDTH);
|
|
48990
|
+
columnWidths.push(pointWidth);
|
|
48991
|
+
visibleCols.push(c);
|
|
48992
|
+
}
|
|
48993
|
+
return {
|
|
48994
|
+
columnWidths,
|
|
48995
|
+
visibleCols
|
|
48996
|
+
};
|
|
48997
|
+
}
|
|
48998
|
+
function computeRowHeights(sheet, scaleFactor, printRange, fontManager, options) {
|
|
48999
|
+
const bounds = sheet.bounds;
|
|
49000
|
+
if (bounds.top <= 0) return {
|
|
49001
|
+
rowHeights: [],
|
|
49002
|
+
visibleRows: []
|
|
49003
|
+
};
|
|
49004
|
+
const startRow = printRange?.startRow ?? bounds.top;
|
|
49005
|
+
const endRow = printRange?.endRow ?? bounds.bottom;
|
|
49006
|
+
const rowHeights = [];
|
|
49007
|
+
const visibleRows = [];
|
|
49008
|
+
for (let r = startRow; r <= endRow; r++) {
|
|
49009
|
+
const row = sheet.rows.get(r);
|
|
49010
|
+
if (row?.hidden) continue;
|
|
49011
|
+
let height;
|
|
49012
|
+
if (row?.height && row.customHeight) height = row.height;
|
|
49013
|
+
else if (row?.height) height = row.height;
|
|
49014
|
+
else {
|
|
49015
|
+
height = DEFAULT_ROW_HEIGHT;
|
|
49016
|
+
if (row) for (const cell of row.cells.values()) {
|
|
49017
|
+
const fontSize = getCellFontSize(cell);
|
|
49018
|
+
const wrapLineCount = countWrapLines(cell, fontSize, scaleFactor, sheet, fontManager, options);
|
|
49019
|
+
const lineHeight = fontSize * LINE_HEIGHT_FACTOR;
|
|
49020
|
+
const neededHeight = fontSize + (wrapLineCount - 1) * lineHeight + 4;
|
|
49021
|
+
if (neededHeight > height) height = neededHeight;
|
|
49022
|
+
}
|
|
49023
|
+
}
|
|
49024
|
+
rowHeights.push(height * scaleFactor);
|
|
49025
|
+
visibleRows.push(r);
|
|
49026
|
+
}
|
|
49027
|
+
return {
|
|
49028
|
+
rowHeights,
|
|
49029
|
+
visibleRows
|
|
49030
|
+
};
|
|
49031
|
+
}
|
|
49032
|
+
/**
|
|
49033
|
+
* Get the largest font size for a cell, checking rich text runs.
|
|
49034
|
+
*/
|
|
49035
|
+
function getCellFontSize(cell) {
|
|
49036
|
+
let fontSize = cell.style?.font?.size ?? 11;
|
|
49037
|
+
if (cell.type === PdfCellType.RichText) {
|
|
49038
|
+
const value = cell.value;
|
|
49039
|
+
if (value && typeof value === "object" && "richText" in value) {
|
|
49040
|
+
const runs = value.richText;
|
|
49041
|
+
for (const run of runs) {
|
|
49042
|
+
const runSize = run.font?.size ?? fontSize;
|
|
49043
|
+
if (runSize > fontSize) fontSize = runSize;
|
|
49044
|
+
}
|
|
49045
|
+
}
|
|
49046
|
+
}
|
|
49047
|
+
return fontSize;
|
|
49048
|
+
}
|
|
49049
|
+
/**
|
|
49050
|
+
* Count the wrap-line count for a cell, using actual font measurements
|
|
49051
|
+
* so row heights match the page renderer exactly.
|
|
49052
|
+
*/
|
|
49053
|
+
function countWrapLines(cell, fontSize, scaleFactor, sheet, fontManager, options) {
|
|
49054
|
+
const text = typeof cell.text === "string" ? cell.text : String(cell.text ?? "");
|
|
49055
|
+
const lineCount = Math.max(1, (text.match(/\n/g) ?? []).length + 1);
|
|
49056
|
+
if (!cell.style?.alignment?.wrapText || text.length === 0) return lineCount;
|
|
49057
|
+
const scaledColPts = ((sheet.columns.get(cell.col)?.width ?? DEFAULT_COLUMN_WIDTH) * 7 + 5) * PX_TO_PT * scaleFactor;
|
|
49058
|
+
const padding = 6 + (cell.style.alignment.indent ?? 0) * 10;
|
|
49059
|
+
const effectiveWidth = Math.max(scaledColPts - padding, 1);
|
|
49060
|
+
const scaledFontSize = fontSize * scaleFactor;
|
|
49061
|
+
const fontProps = extractFontProperties(cell.style.font, options.defaultFontFamily, options.defaultFontSize);
|
|
49062
|
+
const pdfFontName = resolvePdfFontName(fontProps.fontFamily, fontProps.bold, fontProps.italic);
|
|
49063
|
+
const resourceName = fontManager.hasEmbeddedFont() ? fontManager.getEmbeddedResourceName() : fontManager.ensureFont(pdfFontName);
|
|
49064
|
+
const measure = (s) => fontManager.measureText(s, resourceName, scaledFontSize);
|
|
49065
|
+
const wrappedLines = wrapTextLines(text, measure, effectiveWidth);
|
|
49066
|
+
return Math.max(lineCount, wrappedLines.length);
|
|
49067
|
+
}
|
|
49068
|
+
/**
|
|
49069
|
+
* Build a set of visible-row indices where manual page breaks occur.
|
|
49070
|
+
*/
|
|
49071
|
+
function buildRowBreakSet(sheet, visibleRows) {
|
|
49072
|
+
const breaks = /* @__PURE__ */ new Set();
|
|
49073
|
+
const rowBreaks = sheet.rowBreaks ?? [];
|
|
49074
|
+
if (rowBreaks.length === 0) return breaks;
|
|
49075
|
+
const rowToIndex = /* @__PURE__ */ new Map();
|
|
49076
|
+
for (let i = 0; i < visibleRows.length; i++) rowToIndex.set(visibleRows[i], i);
|
|
49077
|
+
for (const brk of rowBreaks) {
|
|
49078
|
+
const idx = rowToIndex.get(brk);
|
|
49079
|
+
if (idx !== void 0) breaks.add(idx + 1);
|
|
49080
|
+
}
|
|
49081
|
+
return breaks;
|
|
49082
|
+
}
|
|
49083
|
+
/**
|
|
49084
|
+
* Build a map of all merged cell regions.
|
|
49085
|
+
* Key: "row:col" (1-based), Value: merge info
|
|
49086
|
+
*/
|
|
49087
|
+
function buildMergeMap(sheet) {
|
|
49088
|
+
const map = /* @__PURE__ */ new Map();
|
|
49089
|
+
const merges = sheet.merges;
|
|
49090
|
+
if (!merges || merges.length === 0) return map;
|
|
49091
|
+
for (const rangeStr of merges) {
|
|
49092
|
+
const range = parseRangeRef(rangeStr);
|
|
49093
|
+
const top = range.s.r + 1;
|
|
49094
|
+
const left = range.s.c + 1;
|
|
49095
|
+
const bottom = range.e.r + 1;
|
|
49096
|
+
const right = range.e.c + 1;
|
|
49097
|
+
const rowSpan = bottom - top + 1;
|
|
49098
|
+
const colSpan = right - left + 1;
|
|
49099
|
+
for (let r = top; r <= bottom; r++) for (let c = left; c <= right; c++) map.set(`${r}:${c}`, {
|
|
49100
|
+
isMaster: r === top && c === left,
|
|
49101
|
+
rowSpan,
|
|
49102
|
+
colSpan
|
|
49103
|
+
});
|
|
49104
|
+
}
|
|
49105
|
+
return map;
|
|
49106
|
+
}
|
|
49107
|
+
function paginateRows(rowHeights, availableHeight, repeatRowCount, rowBreaks) {
|
|
49108
|
+
if (rowHeights.length === 0) return [[]];
|
|
49109
|
+
const pages = [];
|
|
49110
|
+
let currentPage = [];
|
|
49111
|
+
let currentPageHeight = 0;
|
|
49112
|
+
let isFirstPage = true;
|
|
49113
|
+
let repeatedPrefixCount = 0;
|
|
49114
|
+
const addRepeatRows = () => {
|
|
49115
|
+
repeatedPrefixCount = 0;
|
|
49116
|
+
for (let h = 0; h < repeatRowCount && h < rowHeights.length; h++) {
|
|
49117
|
+
if (currentPageHeight + rowHeights[h] > availableHeight && currentPage.length > 0) break;
|
|
49118
|
+
currentPage.push(h);
|
|
49119
|
+
currentPageHeight += rowHeights[h];
|
|
49120
|
+
repeatedPrefixCount++;
|
|
49121
|
+
}
|
|
49122
|
+
};
|
|
49123
|
+
for (let i = 0; i < rowHeights.length; i++) {
|
|
49124
|
+
const rowHeight = rowHeights[i];
|
|
49125
|
+
const pageAvailable = availableHeight;
|
|
49126
|
+
let skipRepeatedRow = false;
|
|
49127
|
+
while (true) {
|
|
49128
|
+
const forceBreak = rowBreaks.has(i) && currentPage.length > 0;
|
|
49129
|
+
if ((forceBreak || currentPageHeight + rowHeight > pageAvailable) && currentPage.length > 0) {
|
|
49130
|
+
if (!forceBreak && !isFirstPage && currentPage.length > 0 && currentPage.length === repeatedPrefixCount) {
|
|
49131
|
+
currentPage = [];
|
|
49132
|
+
currentPageHeight = 0;
|
|
49133
|
+
repeatedPrefixCount = 0;
|
|
49134
|
+
continue;
|
|
49135
|
+
}
|
|
49136
|
+
pages.push(currentPage);
|
|
49137
|
+
currentPage = [];
|
|
49138
|
+
currentPageHeight = 0;
|
|
49139
|
+
repeatedPrefixCount = 0;
|
|
49140
|
+
isFirstPage = false;
|
|
49141
|
+
addRepeatRows();
|
|
49142
|
+
continue;
|
|
49143
|
+
}
|
|
49144
|
+
if (!isFirstPage && i < repeatRowCount && currentPage.includes(i)) {
|
|
49145
|
+
skipRepeatedRow = true;
|
|
49146
|
+
break;
|
|
49147
|
+
}
|
|
49148
|
+
currentPage.push(i);
|
|
49149
|
+
currentPageHeight += rowHeight;
|
|
49150
|
+
break;
|
|
49151
|
+
}
|
|
49152
|
+
if (skipRepeatedRow) continue;
|
|
49153
|
+
}
|
|
49154
|
+
if (currentPage.length > 0) pages.push(currentPage);
|
|
49155
|
+
return pages.length > 0 ? pages : [[]];
|
|
49156
|
+
}
|
|
49157
|
+
/**
|
|
49158
|
+
* Split columns into groups for horizontal pagination.
|
|
49159
|
+
*/
|
|
49160
|
+
function paginateColumns(columnWidths, contentWidth, sheet, visibleCols) {
|
|
49161
|
+
if (columnWidths.length === 0) return [[]];
|
|
49162
|
+
const colBreaks = /* @__PURE__ */ new Set();
|
|
49163
|
+
const wsColBreaks = sheet.colBreaks ?? [];
|
|
49164
|
+
if (wsColBreaks.length > 0) {
|
|
49165
|
+
const colToIndex = /* @__PURE__ */ new Map();
|
|
49166
|
+
for (let i = 0; i < visibleCols.length; i++) colToIndex.set(visibleCols[i], i);
|
|
49167
|
+
for (const brk of wsColBreaks) {
|
|
49168
|
+
const idx = colToIndex.get(brk);
|
|
49169
|
+
if (idx !== void 0) colBreaks.add(idx + 1);
|
|
49170
|
+
}
|
|
49171
|
+
}
|
|
49172
|
+
const groups = [];
|
|
49173
|
+
let currentGroup = [];
|
|
49174
|
+
let currentWidth = 0;
|
|
49175
|
+
for (let i = 0; i < columnWidths.length; i++) {
|
|
49176
|
+
const colWidth = columnWidths[i];
|
|
49177
|
+
if ((colBreaks.has(i) && currentGroup.length > 0 || currentWidth + colWidth > contentWidth + .01) && currentGroup.length > 0) {
|
|
49178
|
+
groups.push(currentGroup);
|
|
49179
|
+
currentGroup = [];
|
|
49180
|
+
currentWidth = 0;
|
|
49181
|
+
}
|
|
49182
|
+
currentGroup.push(i);
|
|
49183
|
+
currentWidth += colWidth;
|
|
49184
|
+
}
|
|
49185
|
+
if (currentGroup.length > 0) groups.push(currentGroup);
|
|
49186
|
+
return groups.length > 0 ? groups : [Array.from({ length: columnWidths.length }, (_, i) => i)];
|
|
49187
|
+
}
|
|
49188
|
+
function buildLayoutCell(cell, x, y, width, height, colSpan, rowSpan, options, fontManager, scaleFactor) {
|
|
49189
|
+
const text = cell?.text ?? "";
|
|
49190
|
+
const style = cell?.style ?? {};
|
|
49191
|
+
const fontProps = extractFontProperties(style.font, options.defaultFontFamily, options.defaultFontSize);
|
|
49192
|
+
const scaledFontSize = fontProps.fontSize * scaleFactor;
|
|
49193
|
+
if (fontManager.hasEmbeddedFont()) fontManager.trackText(text);
|
|
49194
|
+
else {
|
|
49195
|
+
const pdfFontName = resolvePdfFontName(fontProps.fontFamily, fontProps.bold, fontProps.italic);
|
|
49196
|
+
fontManager.ensureFont(pdfFontName);
|
|
49197
|
+
}
|
|
49198
|
+
const richText = buildRichTextRuns(cell, options, fontManager, scaleFactor);
|
|
49199
|
+
return {
|
|
49200
|
+
text,
|
|
49201
|
+
rect: {
|
|
49202
|
+
x,
|
|
49203
|
+
y,
|
|
49204
|
+
width,
|
|
49205
|
+
height
|
|
49206
|
+
},
|
|
49207
|
+
fontFamily: fontProps.fontFamily,
|
|
49208
|
+
fontSize: scaledFontSize,
|
|
49209
|
+
bold: fontProps.bold,
|
|
49210
|
+
italic: fontProps.italic,
|
|
49211
|
+
strike: fontProps.strike,
|
|
49212
|
+
underline: fontProps.underline,
|
|
49213
|
+
textColor: fontProps.textColor,
|
|
49214
|
+
fillColor: excelFillToPdfColor(style.fill),
|
|
49215
|
+
horizontalAlign: resolveHorizontalAlign(style.alignment, cell?.type, cell?.result),
|
|
49216
|
+
verticalAlign: excelVAlignToPdf(style.alignment),
|
|
49217
|
+
wrapText: style.alignment?.wrapText ?? false,
|
|
49218
|
+
borders: excelBordersToPdf(style.border),
|
|
49219
|
+
colSpan,
|
|
49220
|
+
rowSpan,
|
|
49221
|
+
hyperlink: cell?.hyperlink ?? null,
|
|
49222
|
+
richText,
|
|
49223
|
+
indent: style.alignment?.indent ?? 0,
|
|
49224
|
+
textRotation: style.alignment?.textRotation === 255 ? "vertical" : style.alignment?.textRotation ?? 0,
|
|
49225
|
+
textOverflowWidth: 0
|
|
49226
|
+
};
|
|
49227
|
+
}
|
|
49228
|
+
/**
|
|
49229
|
+
* Assign pre-collected images to the pages that contain their top-left anchor.
|
|
49230
|
+
*/
|
|
49231
|
+
function assignImagesToPages(images, layoutPages, scaleFactor) {
|
|
49232
|
+
for (const img of images) {
|
|
49233
|
+
const tl = img.range.tl;
|
|
49234
|
+
const tlCol = (tl.nativeCol ?? tl.col ?? 0) + 1;
|
|
49235
|
+
const tlRow = (tl.nativeRow ?? tl.row ?? 0) + 1;
|
|
49236
|
+
const targetPage = layoutPages.find((page) => page.sheetCols.includes(tlCol) && page.sheetRows.includes(tlRow));
|
|
49237
|
+
if (!targetPage) continue;
|
|
49238
|
+
const pageColIndex = targetPage.sheetCols.indexOf(tlCol);
|
|
49239
|
+
const pageRowIndex = targetPage.sheetRows.indexOf(tlRow);
|
|
49240
|
+
const baseX = targetPage.columnOffsets[pageColIndex] ?? targetPage.options.margins.left;
|
|
49241
|
+
const baseY = targetPage.rowYPositions[pageRowIndex] ?? targetPage.height - targetPage.options.margins.top - (targetPage.options.showSheetNames ? 20 : 0);
|
|
49242
|
+
const tlColOff = ((tl.nativeColOff ?? 0) / 12700 || 0) * scaleFactor;
|
|
49243
|
+
const tlRowOff = ((tl.nativeRowOff ?? 0) / 12700 || 0) * scaleFactor;
|
|
49244
|
+
const imgX = baseX + tlColOff;
|
|
49245
|
+
const imgY = baseY - tlRowOff;
|
|
49246
|
+
let imgWidth = 100;
|
|
49247
|
+
let imgHeight = 100;
|
|
49248
|
+
if (img.range.ext) {
|
|
49249
|
+
imgWidth = (img.range.ext.width ?? 100) * .75 * scaleFactor;
|
|
49250
|
+
imgHeight = (img.range.ext.height ?? 100) * .75 * scaleFactor;
|
|
49251
|
+
} else if (img.range.br) {
|
|
49252
|
+
const br = img.range.br;
|
|
49253
|
+
const brCol = (br.nativeCol ?? br.col ?? 0) + 1;
|
|
49254
|
+
const brRow = (br.nativeRow ?? br.row ?? 0) + 1;
|
|
49255
|
+
const brPageColIndex = targetPage.sheetCols.indexOf(brCol);
|
|
49256
|
+
const brPageRowIndex = targetPage.sheetRows.indexOf(brRow);
|
|
49257
|
+
const brBaseX = brPageColIndex >= 0 ? targetPage.columnOffsets[brPageColIndex] : imgX + (targetPage.columnWidths[pageColIndex] ?? 100);
|
|
49258
|
+
const brBaseY = brPageRowIndex >= 0 ? targetPage.rowYPositions[brPageRowIndex] : imgY - (targetPage.rowHeights[pageRowIndex] ?? 100);
|
|
49259
|
+
const brColOff = ((br.nativeColOff ?? 0) / 12700 || 0) * scaleFactor;
|
|
49260
|
+
const brRowOff = ((br.nativeRowOff ?? 0) / 12700 || 0) * scaleFactor;
|
|
49261
|
+
const brX = brBaseX + brColOff;
|
|
49262
|
+
const brY = brBaseY - brRowOff;
|
|
49263
|
+
imgWidth = brX - imgX;
|
|
49264
|
+
imgHeight = imgY - brY;
|
|
49265
|
+
}
|
|
49266
|
+
targetPage.images.push({
|
|
49267
|
+
data: img.data,
|
|
49268
|
+
format: img.format,
|
|
49269
|
+
rect: {
|
|
49270
|
+
x: imgX,
|
|
49271
|
+
y: imgY - imgHeight,
|
|
49272
|
+
width: Math.abs(imgWidth),
|
|
49273
|
+
height: Math.abs(imgHeight)
|
|
49274
|
+
}
|
|
49275
|
+
});
|
|
49276
|
+
}
|
|
49277
|
+
}
|
|
49278
|
+
/**
|
|
49279
|
+
* Excel stores merged-cell borders on the boundary cells, not on the master.
|
|
49280
|
+
* Copy the right border from the rightmost column cell and the bottom border
|
|
49281
|
+
* from the bottom row cell so the layout cell renders them correctly.
|
|
49282
|
+
*/
|
|
49283
|
+
function propagateMergeBorders(layoutCell, mergeInfo, wsRowNumber, wsColNumber, sheet) {
|
|
49284
|
+
if (mergeInfo.colSpan > 1) {
|
|
49285
|
+
const rightCol = wsColNumber + mergeInfo.colSpan - 1;
|
|
49286
|
+
const rightCellData = sheet.rows.get(wsRowNumber)?.cells.get(rightCol);
|
|
49287
|
+
if (rightCellData?.style?.border?.right) {
|
|
49288
|
+
const converted = excelBordersToPdf({ right: rightCellData.style.border.right });
|
|
49289
|
+
if (converted.right) layoutCell.borders.right = converted.right;
|
|
49290
|
+
}
|
|
49291
|
+
}
|
|
49292
|
+
if (mergeInfo.rowSpan > 1) {
|
|
49293
|
+
const bottomRowNum = wsRowNumber + mergeInfo.rowSpan - 1;
|
|
49294
|
+
const bottomCellData = sheet.rows.get(bottomRowNum)?.cells.get(wsColNumber);
|
|
49295
|
+
if (bottomCellData?.style?.border?.bottom) {
|
|
49296
|
+
const converted = excelBordersToPdf({ bottom: bottomCellData.style.border.bottom });
|
|
49297
|
+
if (converted.bottom) layoutCell.borders.bottom = converted.bottom;
|
|
49298
|
+
}
|
|
49299
|
+
}
|
|
49300
|
+
}
|
|
49301
|
+
/**
|
|
49302
|
+
* In Excel, non-wrapped text overflows into adjacent empty cells.
|
|
49303
|
+
* Fill color alone does NOT block overflow — only text content does.
|
|
49304
|
+
* Computes `textOverflowWidth` for cells whose text exceeds the cell width.
|
|
49305
|
+
*/
|
|
49306
|
+
function computeTextOverflows(cellGrid, rowPage, colGroup, visibleRows, visibleCols, groupColWidths, mergeMap, fontManager) {
|
|
49307
|
+
for (let ri = 0; ri < rowPage.length; ri++) for (let gci = 0; gci < colGroup.length; gci++) {
|
|
49308
|
+
const cell = cellGrid.get(`${ri}:${gci}`);
|
|
49309
|
+
if (!cell || cell.wrapText || cell.colSpan > 1 || !cell.text || cell.richText || typeof cell.textRotation === "number" && cell.textRotation !== 0 || cell.textRotation === "vertical") continue;
|
|
49310
|
+
const resourceName = fontManager.hasEmbeddedFont() ? fontManager.getEmbeddedResourceName() : fontManager.ensureFont(resolvePdfFontName(cell.fontFamily, cell.bold, cell.italic));
|
|
49311
|
+
const textWidth = fontManager.measureText(cell.text, resourceName, cell.fontSize);
|
|
49312
|
+
const cellContentWidth = cell.rect.width - 6;
|
|
49313
|
+
if (textWidth <= cellContentWidth) continue;
|
|
49314
|
+
const overflowNeeded = textWidth - cellContentWidth;
|
|
49315
|
+
let overflowAvailable = 0;
|
|
49316
|
+
for (let j = gci + 1; j < colGroup.length; j++) {
|
|
49317
|
+
const wsRow = visibleRows[rowPage[ri]];
|
|
49318
|
+
const wsCol = visibleCols[colGroup[j]];
|
|
49319
|
+
if (mergeMap.has(`${wsRow}:${wsCol}`)) break;
|
|
49320
|
+
if (cellGrid.get(`${ri}:${j}`)?.text) break;
|
|
49321
|
+
overflowAvailable += groupColWidths[j];
|
|
49322
|
+
if (overflowAvailable >= overflowNeeded) break;
|
|
49323
|
+
}
|
|
49324
|
+
if (overflowAvailable > 0) cell.textOverflowWidth = Math.min(overflowNeeded, overflowAvailable);
|
|
49325
|
+
}
|
|
49326
|
+
}
|
|
49327
|
+
/**
|
|
49328
|
+
* Build rich text runs from a RichText cell.
|
|
49329
|
+
* Returns null for non-RichText cells.
|
|
49330
|
+
*/
|
|
49331
|
+
function buildRichTextRuns(cell, options, fontManager, scaleFactor) {
|
|
49332
|
+
if (!cell || cell.type !== PdfCellType.RichText) return null;
|
|
49333
|
+
const value = cell.value;
|
|
49334
|
+
if (!value || typeof value !== "object" || !("richText" in value)) return null;
|
|
49335
|
+
const runs = value.richText;
|
|
49336
|
+
if (runs.length === 0) return null;
|
|
49337
|
+
return runs.map((run) => {
|
|
49338
|
+
const fontProps = extractFontProperties(run.font, options.defaultFontFamily, options.defaultFontSize);
|
|
49339
|
+
if (fontManager.hasEmbeddedFont()) fontManager.trackText(run.text);
|
|
49340
|
+
else {
|
|
49341
|
+
const pdfFontName = resolvePdfFontName(fontProps.fontFamily, fontProps.bold, fontProps.italic);
|
|
49342
|
+
fontManager.ensureFont(pdfFontName);
|
|
49343
|
+
}
|
|
49344
|
+
return {
|
|
49345
|
+
text: run.text,
|
|
49346
|
+
fontFamily: fontProps.fontFamily,
|
|
49347
|
+
fontSize: fontProps.fontSize * scaleFactor,
|
|
49348
|
+
bold: fontProps.bold,
|
|
49349
|
+
italic: fontProps.italic,
|
|
49350
|
+
strike: fontProps.strike,
|
|
49351
|
+
underline: fontProps.underline,
|
|
49352
|
+
textColor: fontProps.textColor
|
|
49353
|
+
};
|
|
49354
|
+
});
|
|
49355
|
+
}
|
|
49356
|
+
//#endregion
|
|
49070
49357
|
//#region src/modules/pdf/render/png-decoder.ts
|
|
49071
49358
|
/**
|
|
49072
49359
|
* Minimal PNG decoder for PDF image embedding.
|
|
@@ -49289,13 +49576,23 @@ onmessage = async (ev) => {
|
|
|
49289
49576
|
*/
|
|
49290
49577
|
/**
|
|
49291
49578
|
* Export a PdfWorkbook to PDF format.
|
|
49579
|
+
* Yields to the event loop between each output page during layout and rendering.
|
|
49292
49580
|
*
|
|
49293
49581
|
* @param workbook - The workbook data to export
|
|
49294
49582
|
* @param options - Export options controlling layout, pagination, and appearance
|
|
49295
|
-
* @returns PDF file as a Uint8Array
|
|
49583
|
+
* @returns Promise of PDF file as a Uint8Array
|
|
49296
49584
|
* @throws {PdfError} If the workbook has no sheets or export fails
|
|
49297
49585
|
*/
|
|
49298
|
-
function exportPdf(workbook, options) {
|
|
49586
|
+
async function exportPdf(workbook, options) {
|
|
49587
|
+
const ctx = prepareExport(workbook, options);
|
|
49588
|
+
for (const sheet of ctx.sheets) await layoutSheetInto(ctx, sheet, options);
|
|
49589
|
+
return finishExport(ctx, workbook, options);
|
|
49590
|
+
}
|
|
49591
|
+
/**
|
|
49592
|
+
* Shared setup: validate sheets, create font manager and writer,
|
|
49593
|
+
* register embedded font.
|
|
49594
|
+
*/
|
|
49595
|
+
function prepareExport(workbook, options) {
|
|
49299
49596
|
const sheets = selectSheets(workbook, options?.sheets);
|
|
49300
49597
|
if (sheets.length === 0) throw new PdfError("No sheets to export. The workbook is empty or no sheets matched.");
|
|
49301
49598
|
const fontManager = new FontManager();
|
|
@@ -49306,14 +49603,38 @@ onmessage = async (ev) => {
|
|
|
49306
49603
|
} catch (err) {
|
|
49307
49604
|
throw new PdfRenderError("Failed to parse TrueType font", { cause: err });
|
|
49308
49605
|
}
|
|
49309
|
-
|
|
49310
|
-
|
|
49311
|
-
|
|
49312
|
-
|
|
49606
|
+
return {
|
|
49607
|
+
sheets,
|
|
49608
|
+
fontManager,
|
|
49609
|
+
writer,
|
|
49610
|
+
allPages: []
|
|
49611
|
+
};
|
|
49612
|
+
}
|
|
49613
|
+
/**
|
|
49614
|
+
* Layout a single sheet and append its pages to the context.
|
|
49615
|
+
*/
|
|
49616
|
+
async function layoutSheetInto(ctx, sheet, options) {
|
|
49617
|
+
try {
|
|
49618
|
+
const pages = await layoutSheet(sheet, resolveOptions(options, sheet), ctx.fontManager);
|
|
49619
|
+
ctx.allPages.push(...pages);
|
|
49313
49620
|
} catch (err) {
|
|
49314
49621
|
throw new PdfRenderError(`Failed to layout sheet "${sheet.name}"`, { cause: err });
|
|
49315
49622
|
}
|
|
49623
|
+
}
|
|
49624
|
+
/**
|
|
49625
|
+
* After layout: fix page numbers, track fonts, write resources,
|
|
49626
|
+
* render pages, and build the final PDF binary.
|
|
49627
|
+
*/
|
|
49628
|
+
async function finishExport(ctx, workbook, options) {
|
|
49629
|
+
const { allPages, fontManager, writer, sheets } = ctx;
|
|
49316
49630
|
const documentOptions = resolveOptions(options, sheets[0]);
|
|
49631
|
+
ensureAtLeastOnePage(allPages, documentOptions, sheets);
|
|
49632
|
+
fixPageNumbers(allPages);
|
|
49633
|
+
trackFontsForHeaders(allPages, fontManager);
|
|
49634
|
+
const { pageObjNums, sheetFirstPage, pagesTreeObjNum } = await renderAllPages(allPages, fontManager, writer, fontManager.writeFontResources(writer));
|
|
49635
|
+
return buildFinalPdf(writer, pageObjNums, pagesTreeObjNum, sheetFirstPage, documentOptions, workbook, options);
|
|
49636
|
+
}
|
|
49637
|
+
function ensureAtLeastOnePage(allPages, documentOptions, sheets) {
|
|
49317
49638
|
if (allPages.length === 0) allPages.push({
|
|
49318
49639
|
pageNumber: 1,
|
|
49319
49640
|
options: documentOptions,
|
|
@@ -49327,10 +49648,14 @@ onmessage = async (ev) => {
|
|
|
49327
49648
|
sheetRows: [],
|
|
49328
49649
|
rowYPositions: [],
|
|
49329
49650
|
rowHeights: [],
|
|
49330
|
-
images: []
|
|
49651
|
+
images: [],
|
|
49652
|
+
scaleFactor: 1
|
|
49331
49653
|
});
|
|
49654
|
+
}
|
|
49655
|
+
function fixPageNumbers(allPages) {
|
|
49332
49656
|
for (let i = 0; i < allPages.length; i++) allPages[i].pageNumber = i + 1;
|
|
49333
|
-
|
|
49657
|
+
}
|
|
49658
|
+
function trackFontsForHeaders(allPages, fontManager) {
|
|
49334
49659
|
if (fontManager.hasEmbeddedFont()) {
|
|
49335
49660
|
for (const page of allPages) if (page.options.showSheetNames) fontManager.trackText(page.sheetName);
|
|
49336
49661
|
}
|
|
@@ -49338,11 +49663,24 @@ onmessage = async (ev) => {
|
|
|
49338
49663
|
if (!fontManager.hasEmbeddedFont()) {
|
|
49339
49664
|
for (const page of allPages) if (page.options.showSheetNames) fontManager.ensureFont(resolvePdfFontName(page.options.defaultFontFamily, true, false));
|
|
49340
49665
|
}
|
|
49341
|
-
|
|
49666
|
+
}
|
|
49667
|
+
async function renderAllPages(allPages, fontManager, writer, fontObjectMap) {
|
|
49342
49668
|
const pageObjNums = [];
|
|
49343
49669
|
const pagesTreeObjNum = writer.allocObject();
|
|
49344
49670
|
const sheetFirstPage = /* @__PURE__ */ new Map();
|
|
49345
|
-
|
|
49671
|
+
const totalPages = allPages.length;
|
|
49672
|
+
for (let i = 0; i < allPages.length; i++) {
|
|
49673
|
+
renderSinglePage(allPages[i], fontManager, writer, fontObjectMap, totalPages, pageObjNums, pagesTreeObjNum, sheetFirstPage);
|
|
49674
|
+
if (i < allPages.length - 1) await yieldToEventLoop();
|
|
49675
|
+
}
|
|
49676
|
+
return {
|
|
49677
|
+
pageObjNums,
|
|
49678
|
+
sheetFirstPage,
|
|
49679
|
+
pagesTreeObjNum
|
|
49680
|
+
};
|
|
49681
|
+
}
|
|
49682
|
+
function renderSinglePage(page, fontManager, writer, fontObjectMap, totalPages, pageObjNums, pagesTreeObjNum, sheetFirstPage) {
|
|
49683
|
+
try {
|
|
49346
49684
|
const { stream: contentStream, alphaValues } = renderPage(page, page.options, fontManager, totalPages);
|
|
49347
49685
|
const imageXObjects = /* @__PURE__ */ new Map();
|
|
49348
49686
|
if (page.images.length > 0) for (let imgIdx = 0; imgIdx < page.images.length; imgIdx++) {
|
|
@@ -49397,6 +49735,8 @@ onmessage = async (ev) => {
|
|
|
49397
49735
|
} catch (err) {
|
|
49398
49736
|
throw new PdfRenderError(`Failed to render page ${page.pageNumber} of "${page.sheetName}"`, { cause: err });
|
|
49399
49737
|
}
|
|
49738
|
+
}
|
|
49739
|
+
function buildFinalPdf(writer, pageObjNums, pagesTreeObjNum, sheetFirstPage, documentOptions, workbook, options) {
|
|
49400
49740
|
const pagesKids = "[" + pageObjNums.map((n) => pdfRef(n)).join(" ") + "]";
|
|
49401
49741
|
const pagesDict = new PdfDict().set("Type", "/Pages").set("Kids", pagesKids).set("Count", String(pageObjNums.length));
|
|
49402
49742
|
writer.addObject(pagesTreeObjNum, pagesDict);
|
|
@@ -49454,7 +49794,7 @@ onmessage = async (ev) => {
|
|
|
49454
49794
|
orientation,
|
|
49455
49795
|
margins,
|
|
49456
49796
|
fitToPage: options?.fitToPage !== void 0 ? options.fitToPage : true,
|
|
49457
|
-
scale: Math.max(.1, Math.min(3, options?.scale ?? (ps?.scale ? ps.scale / 100 : 1))),
|
|
49797
|
+
scale: Math.max(.1, Math.min(3, options?.scale ?? ((options?.fitToPage !== void 0 ? options.fitToPage : true) ? 1 : ps?.scale ? ps.scale / 100 : 1))),
|
|
49458
49798
|
showGridLines: options?.showGridLines ?? ps?.showGridLines ?? false,
|
|
49459
49799
|
gridLineColor,
|
|
49460
49800
|
repeatRows,
|
|
@@ -49607,7 +49947,7 @@ onmessage = async (ev) => {
|
|
|
49607
49947
|
* ```typescript
|
|
49608
49948
|
* import { pdf } from "@cj-tech-master/excelts/pdf";
|
|
49609
49949
|
*
|
|
49610
|
-
* const bytes = pdf([
|
|
49950
|
+
* const bytes = await pdf([
|
|
49611
49951
|
* ["Product", "Revenue"],
|
|
49612
49952
|
* ["Widget", 1000],
|
|
49613
49953
|
* ["Gadget", 2500]
|
|
@@ -49616,7 +49956,7 @@ onmessage = async (ev) => {
|
|
|
49616
49956
|
*
|
|
49617
49957
|
* @example With options:
|
|
49618
49958
|
* ```typescript
|
|
49619
|
-
* const bytes = pdf([
|
|
49959
|
+
* const bytes = await pdf([
|
|
49620
49960
|
* ["Name", "Score"],
|
|
49621
49961
|
* ["Alice", 95],
|
|
49622
49962
|
* ["Bob", 87]
|
|
@@ -49625,7 +49965,7 @@ onmessage = async (ev) => {
|
|
|
49625
49965
|
*
|
|
49626
49966
|
* @example Multiple sheets:
|
|
49627
49967
|
* ```typescript
|
|
49628
|
-
* const bytes = pdf({
|
|
49968
|
+
* const bytes = await pdf({
|
|
49629
49969
|
* sheets: [
|
|
49630
49970
|
* { name: "Sales", data: [["Product", "Revenue"], ["Widget", 1000]] },
|
|
49631
49971
|
* { name: "Costs", data: [["Item", "Amount"], ["Rent", 500]] }
|
|
@@ -49635,7 +49975,7 @@ onmessage = async (ev) => {
|
|
|
49635
49975
|
*
|
|
49636
49976
|
* @example With column widths and styles:
|
|
49637
49977
|
* ```typescript
|
|
49638
|
-
* const bytes = pdf({
|
|
49978
|
+
* const bytes = await pdf({
|
|
49639
49979
|
* name: "Report",
|
|
49640
49980
|
* columns: [{ width: 25 }, { width: 15 }],
|
|
49641
49981
|
* data: [
|
|
@@ -49649,12 +49989,13 @@ onmessage = async (ev) => {
|
|
|
49649
49989
|
* Generate a PDF.
|
|
49650
49990
|
*
|
|
49651
49991
|
* Accepts anything from a plain 2D array to a multi-sheet workbook.
|
|
49992
|
+
* Yields to the event loop between each output page during layout and rendering.
|
|
49652
49993
|
*
|
|
49653
49994
|
* @param input - 2D array, sheet object, or workbook object
|
|
49654
49995
|
* @param options - PDF export options (page size, margins, etc.)
|
|
49655
|
-
* @returns PDF file as Uint8Array
|
|
49996
|
+
* @returns Promise of PDF file as Uint8Array
|
|
49656
49997
|
*/
|
|
49657
|
-
function pdf(input, options) {
|
|
49998
|
+
async function pdf(input, options) {
|
|
49658
49999
|
return exportPdf(normalizeInput(input), options);
|
|
49659
50000
|
}
|
|
49660
50001
|
function normalizeInput(input) {
|
|
@@ -49814,12 +50155,13 @@ onmessage = async (ev) => {
|
|
|
49814
50155
|
*
|
|
49815
50156
|
* This is a convenience function that converts the Workbook to the PDF module's
|
|
49816
50157
|
* data model and then generates the PDF.
|
|
50158
|
+
* Yields to the event loop between each output page during layout and rendering.
|
|
49817
50159
|
*
|
|
49818
50160
|
* @param workbook - An Excel Workbook instance
|
|
49819
50161
|
* @param options - PDF export options
|
|
49820
|
-
* @returns PDF file as a Uint8Array
|
|
50162
|
+
* @returns Promise of PDF file as a Uint8Array
|
|
49821
50163
|
*/
|
|
49822
|
-
function excelToPdf(workbook, options) {
|
|
50164
|
+
async function excelToPdf(workbook, options) {
|
|
49823
50165
|
return exportPdf(excelWorkbookToPdf(workbook), options);
|
|
49824
50166
|
}
|
|
49825
50167
|
/**
|
|
@@ -49860,12 +50202,15 @@ onmessage = async (ev) => {
|
|
|
49860
50202
|
const row = ws.findRow(r);
|
|
49861
50203
|
if (!row) continue;
|
|
49862
50204
|
const cells = /* @__PURE__ */ new Map();
|
|
49863
|
-
row.eachCell({ includeEmpty:
|
|
49864
|
-
|
|
50205
|
+
row.eachCell({ includeEmpty: true }, (cell) => {
|
|
50206
|
+
const hasValue = cell.type !== ValueType.Null && cell.type !== ValueType.Merge;
|
|
50207
|
+
const hasStyle = cell.style && (cell.style.border && (cell.style.border.top || cell.style.border.right || cell.style.border.bottom || cell.style.border.left) || cell.style.fill || cell.style.font);
|
|
50208
|
+
if (hasValue || hasStyle) cells.set(cell.col, convertCell(cell));
|
|
49865
50209
|
});
|
|
49866
50210
|
rows.set(r, {
|
|
49867
50211
|
hidden: row.hidden || void 0,
|
|
49868
50212
|
height: row.height ?? void 0,
|
|
50213
|
+
customHeight: row.customHeight || void 0,
|
|
49869
50214
|
cells
|
|
49870
50215
|
});
|
|
49871
50216
|
}
|