@ricsam/excel-to-formula-engine 0.2.1 → 0.3.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 +23 -0
- package/dist/index.cjs +42 -21
- package/dist/index.cjs.map +6 -6
- package/dist/index.mjs +42 -21
- package/dist/index.mjs.map +6 -6
- package/dist/types/src/types.d.ts +12 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -150,6 +150,29 @@ await excelToFormulaEngine(file, {
|
|
|
150
150
|
| Number formats, hyperlinks | Cell metadata | Stored for your UI; the engine does not interpret them. |
|
|
151
151
|
| Merges, frozen panes, column widths, hidden sheets | Sheet metadata | Same — stored for your UI to apply. |
|
|
152
152
|
|
|
153
|
+
### Effective cell fonts
|
|
154
|
+
|
|
155
|
+
Existing worksheet cells resolve their formatting in this order: explicit cell
|
|
156
|
+
`s` (including `s="0"`), row `s` when `customFormat` is enabled, column `style`,
|
|
157
|
+
then workbook cell XF 0. Column ranges remain sparse, including full-sheet
|
|
158
|
+
ranges. Named-style font inheritance (`cellStyleXfs` / `xfId`) is resolved;
|
|
159
|
+
`applyFont="0"` selects the base font, while an explicit applied font replaces
|
|
160
|
+
it as a unit. Supported font decorations include explicit false values so a
|
|
161
|
+
plain font can clear inherited bold, italic, or underline.
|
|
162
|
+
|
|
163
|
+
The resolved XF 0 is also retained as `workbookMetadata.defaultCellStyle`
|
|
164
|
+
(engine units: font sizes are pixels). This is additive metadata for clients;
|
|
165
|
+
actual imported cells receive coalesced direct styles without requiring a new
|
|
166
|
+
engine API. `styles:false` disables both. Empty coordinates not present in the
|
|
167
|
+
worksheet are not materialized. Older imported snapshots do not retain XF 0
|
|
168
|
+
and cannot reconstruct it losslessly from neighboring styles.
|
|
169
|
+
|
|
170
|
+
Literal Excel font names are retained as `fontFamily` (clients must provide
|
|
171
|
+
those fonts). This is not full Excel rendering fidelity: theme font substitution,
|
|
172
|
+
table style themes, rich-text runs, and named-style inheritance of non-font
|
|
173
|
+
properties are not translated by this change. Double/accounting underline
|
|
174
|
+
variants become a single boolean underline.
|
|
175
|
+
|
|
153
176
|
### Formulas the engine cannot evaluate
|
|
154
177
|
|
|
155
178
|
The engine implements around 40 functions. A formula using anything else — an
|
package/dist/index.cjs
CHANGED
|
@@ -280,6 +280,11 @@ function parseWorksheet(pkg, partPath, sharedStrings) {
|
|
|
280
280
|
function parseCells(worksheet, sharedStrings, result) {
|
|
281
281
|
const sheetData = worksheet["sheetData"];
|
|
282
282
|
const shared = new Map;
|
|
283
|
+
const columnStyles = asArray(worksheet["cols"]?.["col"]).map((col) => ({
|
|
284
|
+
min: numAttr(col, "min"),
|
|
285
|
+
max: numAttr(col, "max"),
|
|
286
|
+
style: numAttr(col, "style")
|
|
287
|
+
})).filter((col) => col.min !== undefined && col.max !== undefined && col.style !== undefined);
|
|
283
288
|
for (const rawRow of asArray(sheetData?.["row"])) {
|
|
284
289
|
const row = rawRow;
|
|
285
290
|
const rowNumber = numAttr(row, "r");
|
|
@@ -297,11 +302,14 @@ function parseCells(worksheet, sharedStrings, result) {
|
|
|
297
302
|
if (!address) {
|
|
298
303
|
continue;
|
|
299
304
|
}
|
|
300
|
-
const
|
|
305
|
+
const explicitStyle = numAttr(cell, "s");
|
|
306
|
+
const rowStyle = boolAttr(row, "customFormat") ? numAttr(row, "s") : undefined;
|
|
307
|
+
const columnStyle = columnStyles.findLast((col) => address.colIndex + 1 >= col.min && address.colIndex + 1 <= col.max)?.style;
|
|
308
|
+
const styleIndex = explicitStyle ?? rowStyle ?? columnStyle ?? 0;
|
|
301
309
|
const type = attr(cell, "t") ?? "n";
|
|
302
310
|
const formula = readFormula(cell, address, shared);
|
|
303
311
|
const { kind, value } = readValue(cell, type, sharedStrings);
|
|
304
|
-
if (kind === "empty" && formula === undefined &&
|
|
312
|
+
if (kind === "empty" && formula === undefined && explicitStyle === undefined && rowStyle === undefined && columnStyle === undefined) {
|
|
305
313
|
continue;
|
|
306
314
|
}
|
|
307
315
|
result.cells.push({
|
|
@@ -691,14 +699,19 @@ function parseStyles(pkg) {
|
|
|
691
699
|
const fills = asArray(styleSheet["fills"]?.["fill"]);
|
|
692
700
|
const borders = asArray(styleSheet["borders"]?.["border"]);
|
|
693
701
|
const cellXfs = asArray(styleSheet["cellXfs"]?.["xf"]);
|
|
702
|
+
const styleXfs = asArray(styleSheet["cellStyleXfs"]?.["xf"]);
|
|
694
703
|
const formats = cellXfs.map((rawXf) => {
|
|
695
704
|
const xf = rawXf;
|
|
696
705
|
const style = {};
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
706
|
+
const baseXf = styleXfs[numAttr(xf, "xfId") ?? -1];
|
|
707
|
+
const baseFontId = boolAttr(baseXf, "applyFont", true) ? numAttr(baseXf, "fontId") ?? 0 : 0;
|
|
708
|
+
const fontId = boolAttr(xf, "applyFont", true) ? numAttr(xf, "fontId") ?? baseFontId : baseFontId;
|
|
709
|
+
const font = fonts[fontId];
|
|
710
|
+
if (font) {
|
|
711
|
+
style.bold = false;
|
|
712
|
+
style.italic = false;
|
|
713
|
+
style.underline = false;
|
|
714
|
+
applyFont(style, font, theme, indexedColors);
|
|
702
715
|
}
|
|
703
716
|
if (boolAttr(xf, "applyFill", true)) {
|
|
704
717
|
const fill = fills[numAttr(xf, "fillId") ?? -1];
|
|
@@ -743,14 +756,18 @@ function parseStyles(pkg) {
|
|
|
743
756
|
return { formats, differentialFormats };
|
|
744
757
|
}
|
|
745
758
|
function applyFont(style, font, theme, indexedColors) {
|
|
746
|
-
|
|
747
|
-
|
|
759
|
+
const fontFamily = attr(font["name"], "val");
|
|
760
|
+
if (fontFamily) {
|
|
761
|
+
style.fontFamily = fontFamily;
|
|
762
|
+
}
|
|
763
|
+
if (font["b"] !== undefined) {
|
|
764
|
+
style.bold = isOn(font["b"]);
|
|
748
765
|
}
|
|
749
|
-
if (font["i"] !== undefined
|
|
750
|
-
style.italic =
|
|
766
|
+
if (font["i"] !== undefined) {
|
|
767
|
+
style.italic = isOn(font["i"]);
|
|
751
768
|
}
|
|
752
|
-
if (font["u"] !== undefined
|
|
753
|
-
style.underline =
|
|
769
|
+
if (font["u"] !== undefined) {
|
|
770
|
+
style.underline = attr(font["u"], "val") !== "none";
|
|
754
771
|
}
|
|
755
772
|
const size = numAttr(font["sz"], "val");
|
|
756
773
|
if (size !== undefined) {
|
|
@@ -1110,6 +1127,7 @@ function contractThisRowReferences(formulaBody, containingTableName) {
|
|
|
1110
1127
|
var import_formula_engine2 = require("@ricsam/formula-engine");
|
|
1111
1128
|
function translateConditionalFormat(rule, options) {
|
|
1112
1129
|
const diagnostics = [];
|
|
1130
|
+
const ranges = rule.ranges.map((reference) => reference.replace(/\$/g, ""));
|
|
1113
1131
|
const areas = [];
|
|
1114
1132
|
for (const reference of rule.ranges) {
|
|
1115
1133
|
const range = rangeReferenceToSpreadsheetRange(reference);
|
|
@@ -1121,14 +1139,16 @@ function translateConditionalFormat(rule, options) {
|
|
|
1121
1139
|
return { diagnostics };
|
|
1122
1140
|
}
|
|
1123
1141
|
const anchor = rule.ranges[0]?.split(":")[0]?.replace(/\$/g, "") ?? "A1";
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
return { diagnostics };
|
|
1142
|
+
const result = rule.type === "colorScale" ? translateColorScale(rule, areas, options, diagnostics).result : withCondition(areas, buildFormulaCondition(rule, anchor, options, diagnostics));
|
|
1143
|
+
for (const diagnostic of diagnostics) {
|
|
1144
|
+
if (diagnostic.ranges === undefined && ranges.length > 0) {
|
|
1145
|
+
diagnostic.ranges = ranges;
|
|
1146
|
+
}
|
|
1130
1147
|
}
|
|
1131
|
-
return { result
|
|
1148
|
+
return { result, diagnostics };
|
|
1149
|
+
}
|
|
1150
|
+
function withCondition(areas, condition) {
|
|
1151
|
+
return condition ? { areas, condition } : undefined;
|
|
1132
1152
|
}
|
|
1133
1153
|
function buildFormulaCondition(rule, anchor, options, diagnostics) {
|
|
1134
1154
|
const color = ruleColor(rule, options);
|
|
@@ -1441,6 +1461,7 @@ async function convertWorkbook(source, options = {}) {
|
|
|
1441
1461
|
cellStyles: cellStyles.length > 0 ? cellStyles : undefined,
|
|
1442
1462
|
conditionalStyles: conditionalStyles.length > 0 ? conditionalStyles : undefined,
|
|
1443
1463
|
workbookMetadata: {
|
|
1464
|
+
defaultCellStyle: importStyles ? styleTable.formats[0]?.style : undefined,
|
|
1444
1465
|
fileName: options.fileName,
|
|
1445
1466
|
sheetOrder: sheetEntries.map((entry) => entry.name),
|
|
1446
1467
|
diagnostics: diagnostics.length > 0 ? diagnostics : undefined
|
|
@@ -1900,4 +1921,4 @@ function withCsvFileName(source, options) {
|
|
|
1900
1921
|
};
|
|
1901
1922
|
}
|
|
1902
1923
|
|
|
1903
|
-
//# debugId=
|
|
1924
|
+
//# debugId=F34603A2925533A764756E2164756E21
|