xls-codec 4.12.5 → 4.13.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.
Files changed (76) hide show
  1. package/README.md +3 -1
  2. package/dist/biff/font.cjs +127 -0
  3. package/dist/biff/font.d.cts +2 -0
  4. package/dist/biff/font.d.ts +2 -0
  5. package/dist/biff/font.js +119 -0
  6. package/dist/biff/ptg-writer.cjs +1 -1
  7. package/dist/biff/ptg-writer.js +1 -1
  8. package/dist/biff/string-writer.cjs +1 -1
  9. package/dist/biff/string-writer.js +1 -1
  10. package/dist/biff/xf-writer.cjs +1 -22
  11. package/dist/biff/xf-writer.d.cts +1 -2
  12. package/dist/biff/xf-writer.d.ts +1 -2
  13. package/dist/biff/xf-writer.js +2 -22
  14. package/dist/{conditional-format-12-C3nwy1f8.d.ts → conditional-format-12-BTN59oHJ.d.ts} +2 -2
  15. package/dist/{conditional-format-12-D65-ruTq.d.cts → conditional-format-12-ZkrPfAP4.d.cts} +2 -2
  16. package/dist/{conditional-format-Cth_afMy.d.cts → conditional-format-BzTJe8Fm.d.cts} +1 -1
  17. package/dist/{conditional-format-BRMUTRP1.d.ts → conditional-format-D4U0eLcY.d.ts} +1 -1
  18. package/dist/content.cjs +52 -6
  19. package/dist/content.js +52 -6
  20. package/dist/{data-validation-BgUt8CbE.d.cts → data-validation-CTeIudWM.d.cts} +1 -1
  21. package/dist/{data-validation-Cq6iD8lB.d.ts → data-validation-Cjdk9imN.d.ts} +1 -1
  22. package/dist/defined-names-CqBJJmOJ.d.cts +39 -0
  23. package/dist/defined-names-DJZ27hRp.d.ts +39 -0
  24. package/dist/font-CBvAGxD8.d.cts +45 -0
  25. package/dist/font-DcXHYgZ2.d.ts +45 -0
  26. package/dist/index.cjs +5 -6
  27. package/dist/index.d.cts +4 -4
  28. package/dist/index.d.ts +4 -4
  29. package/dist/index.js +7 -7
  30. package/dist/workbook/chart.d.cts +1 -1
  31. package/dist/workbook/chart.d.ts +1 -1
  32. package/dist/workbook/comment-writer.cjs +1 -1
  33. package/dist/workbook/comment-writer.js +1 -1
  34. package/dist/workbook/conditional-format-12.d.cts +1 -1
  35. package/dist/workbook/conditional-format-12.d.ts +1 -1
  36. package/dist/workbook/conditional-format-ex.d.cts +3 -3
  37. package/dist/workbook/conditional-format-ex.d.ts +3 -3
  38. package/dist/workbook/conditional-format-write.cjs +1 -1
  39. package/dist/workbook/conditional-format-write.js +1 -1
  40. package/dist/workbook/conditional-format.cjs +1 -1
  41. package/dist/workbook/conditional-format.d.cts +1 -1
  42. package/dist/workbook/conditional-format.d.ts +1 -1
  43. package/dist/workbook/conditional-format.js +1 -1
  44. package/dist/workbook/data-validation-write.cjs +1 -1
  45. package/dist/workbook/data-validation-write.js +1 -1
  46. package/dist/workbook/data-validation.d.cts +1 -1
  47. package/dist/workbook/data-validation.d.ts +1 -1
  48. package/dist/workbook/defined-names.cjs +186 -0
  49. package/dist/workbook/defined-names.d.cts +2 -0
  50. package/dist/workbook/defined-names.d.ts +2 -0
  51. package/dist/workbook/defined-names.js +182 -0
  52. package/dist/workbook/drawing.d.cts +1 -1
  53. package/dist/workbook/drawing.d.ts +1 -1
  54. package/dist/workbook/globals-writer.cjs +7 -7
  55. package/dist/workbook/globals-writer.d.cts +9 -2
  56. package/dist/workbook/globals-writer.d.ts +9 -2
  57. package/dist/workbook/globals-writer.js +8 -8
  58. package/dist/workbook/globals.cjs +8 -2
  59. package/dist/workbook/globals.d.cts +6 -1
  60. package/dist/workbook/globals.d.ts +6 -1
  61. package/dist/workbook/globals.js +8 -2
  62. package/dist/workbook/print-names.cjs +1 -1
  63. package/dist/workbook/print-names.js +1 -1
  64. package/dist/workbook/sheet-writer.cjs +4 -4
  65. package/dist/workbook/sheet-writer.js +4 -4
  66. package/dist/workbook/sheet.cjs +1 -1
  67. package/dist/workbook/sheet.d.cts +4 -4
  68. package/dist/workbook/sheet.d.ts +4 -4
  69. package/dist/workbook/sheet.js +1 -1
  70. package/dist/write.cjs +56 -12
  71. package/dist/write.js +57 -13
  72. package/dist/written-cells.cjs +4 -2
  73. package/dist/written-cells.d.cts +2 -2
  74. package/dist/written-cells.d.ts +2 -2
  75. package/dist/written-cells.js +4 -2
  76. package/package.json +3 -3
@@ -1,10 +1,11 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_biff_record_types = require("../biff/record-types.cjs");
3
3
  const require_biff_records = require("../biff/records.cjs");
4
- const require_biff_substreams = require("../biff/substreams.cjs");
5
- const require_biff_xf_colors = require("../biff/xf-colors.cjs");
6
4
  const require_biff_cursor = require("../biff/cursor.cjs");
7
5
  const require_biff_strings = require("../biff/strings.cjs");
6
+ const require_biff_xf_colors = require("../biff/xf-colors.cjs");
7
+ const require_biff_font = require("../biff/font.cjs");
8
+ const require_biff_substreams = require("../biff/substreams.cjs");
8
9
  const require_workbook_print_names = require("./print-names.cjs");
9
10
  let excel_number_format = require("excel-number-format");
10
11
  //#region src/workbook/globals.ts
@@ -19,6 +20,7 @@ const MIN_SST_ENTRY_BYTES = 3;
19
20
  function readWorkbookGlobals(records) {
20
21
  const sheets = [];
21
22
  const cellFormats = [];
23
+ const fonts = [];
22
24
  const customFormats = /* @__PURE__ */ new Map();
23
25
  const supBooks = [];
24
26
  let sharedStrings = [];
@@ -40,6 +42,9 @@ function readWorkbookGlobals(records) {
40
42
  case 224:
41
43
  cellFormats.push(readCellFormat(record));
42
44
  break;
45
+ case 49:
46
+ fonts.push(require_biff_font.readFontRecord(record));
47
+ break;
43
48
  case 34:
44
49
  date1904 = readDate1904(record);
45
50
  break;
@@ -58,6 +63,7 @@ function readWorkbookGlobals(records) {
58
63
  sheets,
59
64
  sharedStrings,
60
65
  cellFormats,
66
+ fonts,
61
67
  numberFormats,
62
68
  date1904,
63
69
  sheetRanges,
@@ -1,5 +1,6 @@
1
- import { a as SheetRange, t as ExternalSheetLabel } from "../ptg-75v7MhaA.cjs";
2
1
  import { t as RecordGroup } from "../substreams-XDNgDvRJ.cjs";
2
+ import { r as XfFontFields } from "../font-CBvAGxD8.cjs";
3
+ import { a as SheetRange, t as ExternalSheetLabel } from "../ptg-75v7MhaA.cjs";
3
4
  import { E as XfDecorationFields, w as XfAlignmentFields } from "../xf-colors-aeH9Ax3Q.cjs";
4
5
  import { n as SheetPrintNames } from "../print-names-B2OR0fvs.cjs";
5
6
  import { Color } from "document-schema.js";
@@ -36,6 +37,10 @@ interface WorkbookGlobals {
36
37
  readonly sharedStrings: readonly string[];
37
38
  /** The XF table, indexed by a cell's own ixfe. */
38
39
  readonly cellFormats: readonly CellFormat[];
40
+ /**
41
+ * The font table, indexed by an XF record's own ifnt ([MS-XLS] 2.4.122). Entry 0 is the Normal style's font -- the workbook's own default, which content.ts diffs each cell's font against to decide which of its properties the cell genuinely states (a cell has no way to say "no font", only an index into this table, so entry 0 is what "the format's default" concretely means here).
42
+ */
43
+ readonly fonts: readonly XfFontFields[];
39
44
  /** Number-format codes by identifier: the file's own Format records laid over the built-in table. */
40
45
  readonly numberFormats: ReadonlyMap<number, string>;
41
46
  /** Whether serials count from the 1904 epoch rather than the 1900 one ([MS-XLS] 2.4.77). */
@@ -1,5 +1,6 @@
1
- import { a as SheetRange, t as ExternalSheetLabel } from "../ptg-75v7MhaA.js";
2
1
  import { t as RecordGroup } from "../substreams-CgTHx5kX.js";
2
+ import { r as XfFontFields } from "../font-DcXHYgZ2.js";
3
+ import { a as SheetRange, t as ExternalSheetLabel } from "../ptg-75v7MhaA.js";
3
4
  import { E as XfDecorationFields, w as XfAlignmentFields } from "../xf-colors-kdNsiNYt.js";
4
5
  import { n as SheetPrintNames } from "../print-names-BXcmmwVi.js";
5
6
  import { Color } from "document-schema.js";
@@ -36,6 +37,10 @@ interface WorkbookGlobals {
36
37
  readonly sharedStrings: readonly string[];
37
38
  /** The XF table, indexed by a cell's own ixfe. */
38
39
  readonly cellFormats: readonly CellFormat[];
40
+ /**
41
+ * The font table, indexed by an XF record's own ifnt ([MS-XLS] 2.4.122). Entry 0 is the Normal style's font -- the workbook's own default, which content.ts diffs each cell's font against to decide which of its properties the cell genuinely states (a cell has no way to say "no font", only an index into this table, so entry 0 is what "the format's default" concretely means here).
42
+ */
43
+ readonly fonts: readonly XfFontFields[];
39
44
  /** Number-format codes by identifier: the file's own Format records laid over the built-in table. */
40
45
  readonly numberFormats: ReadonlyMap<number, string>;
41
46
  /** Whether serials count from the 1904 epoch rather than the 1900 one ([MS-XLS] 2.4.77). */
@@ -1,9 +1,10 @@
1
1
  import { RECORD_FORMAT } from "../biff/record-types.js";
2
2
  import { BiffFormatError } from "../biff/records.js";
3
- import { recordByteLength } from "../biff/substreams.js";
4
- import { readLongRgbColor, unpackXfAlignment, unpackXfDecoration } from "../biff/xf-colors.js";
5
3
  import { BlockCursor } from "../biff/cursor.js";
6
4
  import { readRichExtendedString, readShortXLUnicodeString, readXLUnicodeString, readXLUnicodeStringNoCch } from "../biff/strings.js";
5
+ import { readLongRgbColor, unpackXfAlignment, unpackXfDecoration } from "../biff/xf-colors.js";
6
+ import { readFontRecord } from "../biff/font.js";
7
+ import { recordByteLength } from "../biff/substreams.js";
7
8
  import { readPrintNames } from "./print-names.js";
8
9
  import { BUILTIN_NUMBER_FORMATS } from "excel-number-format";
9
10
  //#region src/workbook/globals.ts
@@ -18,6 +19,7 @@ const MIN_SST_ENTRY_BYTES = 3;
18
19
  function readWorkbookGlobals(records) {
19
20
  const sheets = [];
20
21
  const cellFormats = [];
22
+ const fonts = [];
21
23
  const customFormats = /* @__PURE__ */ new Map();
22
24
  const supBooks = [];
23
25
  let sharedStrings = [];
@@ -39,6 +41,9 @@ function readWorkbookGlobals(records) {
39
41
  case 224:
40
42
  cellFormats.push(readCellFormat(record));
41
43
  break;
44
+ case 49:
45
+ fonts.push(readFontRecord(record));
46
+ break;
42
47
  case 34:
43
48
  date1904 = readDate1904(record);
44
49
  break;
@@ -57,6 +62,7 @@ function readWorkbookGlobals(records) {
57
62
  sheets,
58
63
  sharedStrings,
59
64
  cellFormats,
65
+ fonts,
60
66
  numberFormats,
61
67
  date1904,
62
68
  sheetRanges,
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  require("../biff/record-types.cjs");
3
- const require_biff_cursor = require("../biff/cursor.cjs");
4
3
  const require_biff_builder = require("../biff/builder.cjs");
4
+ const require_biff_cursor = require("../biff/cursor.cjs");
5
5
  const require_biff_record_writer = require("../biff/record-writer.cjs");
6
6
  //#region src/workbook/print-names.ts
7
7
  /** The built-in name index a Print_Area Lbl carries in place of a name ([MS-XLS] 2.4.150's own built-in name table). */
@@ -1,6 +1,6 @@
1
1
  import "../biff/record-types.js";
2
- import { BlockCursor } from "../biff/cursor.js";
3
2
  import { RecordBuilder } from "../biff/builder.js";
3
+ import { BlockCursor } from "../biff/cursor.js";
4
4
  import { writeRecord } from "../biff/record-writer.js";
5
5
  //#region src/workbook/print-names.ts
6
6
  /** The built-in name index a Print_Area Lbl carries in place of a name ([MS-XLS] 2.4.150's own built-in name table). */
@@ -2,13 +2,13 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_biff_record_types = require("../biff/record-types.cjs");
3
3
  const require_units = require("../units.cjs");
4
4
  const require_biff_print_setup = require("../biff/print-setup.cjs");
5
- const require_biff_errors = require("../biff/errors.cjs");
6
- const require_biff_write_errors = require("../biff/write-errors.cjs");
7
- const require_serial = require("../serial.cjs");
8
5
  const require_biff_builder = require("../biff/builder.cjs");
6
+ const require_biff_write_errors = require("../biff/write-errors.cjs");
9
7
  const require_biff_record_writer = require("../biff/record-writer.cjs");
10
- const require_biff_bof_writer = require("../biff/bof-writer.cjs");
8
+ const require_biff_errors = require("../biff/errors.cjs");
11
9
  const require_biff_string_writer = require("../biff/string-writer.cjs");
10
+ const require_serial = require("../serial.cjs");
11
+ const require_biff_bof_writer = require("../biff/bof-writer.cjs");
12
12
  const require_workbook_globals_writer = require("./globals-writer.cjs");
13
13
  const require_biff_ptg_writer = require("../biff/ptg-writer.cjs");
14
14
  const require_written_cells = require("../written-cells.cjs");
@@ -1,13 +1,13 @@
1
1
  import { RECORD_BOF } from "../biff/record-types.js";
2
2
  import { pointsToColumnWidth, pointsToInches, pointsToTwips } from "../units.js";
3
3
  import { packSetupFlags, paperSelectionFor } from "../biff/print-setup.js";
4
- import { errorCodeOf } from "../biff/errors.js";
5
- import { BiffWriteError } from "../biff/write-errors.js";
6
- import { isoDateTimeToSerial, isoDateToSerial, isoTimeToSerial } from "../serial.js";
7
4
  import { RecordBuilder } from "../biff/builder.js";
5
+ import { BiffWriteError } from "../biff/write-errors.js";
8
6
  import { concatRecords, writeRecord } from "../biff/record-writer.js";
9
- import { writeBofData } from "../biff/bof-writer.js";
7
+ import { errorCodeOf } from "../biff/errors.js";
10
8
  import { writeXLUnicodeString } from "../biff/string-writer.js";
9
+ import { isoDateTimeToSerial, isoDateToSerial, isoTimeToSerial } from "../serial.js";
10
+ import { writeBofData } from "../biff/bof-writer.js";
11
11
  import { GENERAL_CELL_XF_INDEX } from "./globals-writer.js";
12
12
  import { compileFormulaText } from "../biff/ptg-writer.js";
13
13
  import { cellCarriesFormatting, writesCellRecord } from "../written-cells.js";
@@ -3,9 +3,9 @@ const require_biff_record_types = require("../biff/record-types.cjs");
3
3
  const require_biff_records = require("../biff/records.cjs");
4
4
  const require_units = require("../units.cjs");
5
5
  const require_biff_print_setup = require("../biff/print-setup.cjs");
6
- const require_biff_substreams = require("../biff/substreams.cjs");
7
6
  const require_biff_cursor = require("../biff/cursor.cjs");
8
7
  const require_biff_strings = require("../biff/strings.cjs");
8
+ const require_biff_substreams = require("../biff/substreams.cjs");
9
9
  const require_biff_errors = require("../biff/errors.cjs");
10
10
  const require_biff_ptg = require("../biff/ptg.cjs");
11
11
  const require_biff_rk = require("../biff/rk.cjs");
@@ -1,9 +1,9 @@
1
+ import { t as RecordGroup } from "../substreams-XDNgDvRJ.cjs";
1
2
  import { n as SetupFields } from "../print-setup-B_ihDvm5.cjs";
2
3
  import { r as FormulaSheetContext } from "../ptg-75v7MhaA.cjs";
3
- import { t as RecordGroup } from "../substreams-XDNgDvRJ.cjs";
4
- import { r as RawConditionalFormat } from "../conditional-format-Cth_afMy.cjs";
5
- import { a as RawConditionalFormat12 } from "../conditional-format-12-D65-ruTq.cjs";
6
- import { t as RawDataValidation } from "../data-validation-BgUt8CbE.cjs";
4
+ import { r as RawConditionalFormat } from "../conditional-format-BzTJe8Fm.cjs";
5
+ import { a as RawConditionalFormat12 } from "../conditional-format-12-ZkrPfAP4.cjs";
6
+ import { t as RawDataValidation } from "../data-validation-CTeIudWM.cjs";
7
7
  //#region src/workbook/sheet.d.ts
8
8
  /** A cell's value as its own record carries it, before number-format classification decides whether a number is really a date, a percentage, or an amount of money. */
9
9
  type RawCellValue = {
@@ -1,9 +1,9 @@
1
+ import { t as RecordGroup } from "../substreams-CgTHx5kX.js";
1
2
  import { n as SetupFields } from "../print-setup-B_ihDvm5.js";
2
3
  import { r as FormulaSheetContext } from "../ptg-75v7MhaA.js";
3
- import { t as RecordGroup } from "../substreams-CgTHx5kX.js";
4
- import { r as RawConditionalFormat } from "../conditional-format-BRMUTRP1.js";
5
- import { a as RawConditionalFormat12 } from "../conditional-format-12-C3nwy1f8.js";
6
- import { t as RawDataValidation } from "../data-validation-Cq6iD8lB.js";
4
+ import { r as RawConditionalFormat } from "../conditional-format-D4U0eLcY.js";
5
+ import { a as RawConditionalFormat12 } from "../conditional-format-12-BTN59oHJ.js";
6
+ import { t as RawDataValidation } from "../data-validation-Cjdk9imN.js";
7
7
  //#region src/workbook/sheet.d.ts
8
8
  /** A cell's value as its own record carries it, before number-format classification decides whether a number is really a date, a percentage, or an amount of money. */
9
9
  type RawCellValue = {
@@ -2,9 +2,9 @@ import { RECORD_CFEX, RECORD_CONDFMT12 } from "../biff/record-types.js";
2
2
  import { BiffFormatError } from "../biff/records.js";
3
3
  import { columnWidthToPoints, inchesToPoints, twipsToPoints } from "../units.js";
4
4
  import { unpackSetupFlags } from "../biff/print-setup.js";
5
- import { recordByteLength } from "../biff/substreams.js";
6
5
  import { BlockCursor } from "../biff/cursor.js";
7
6
  import { readXLUnicodeString } from "../biff/strings.js";
7
+ import { recordByteLength } from "../biff/substreams.js";
8
8
  import { errorTextOf } from "../biff/errors.js";
9
9
  import { parseFormulaText, readPtgExpBase } from "../biff/ptg.js";
10
10
  import { decodeRkNumber } from "../biff/rk.js";
package/dist/write.cjs CHANGED
@@ -1,7 +1,9 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_container = require("./container.cjs");
3
- const require_biff_xf_colors = require("./biff/xf-colors.cjs");
4
3
  const require_biff_write_errors = require("./biff/write-errors.cjs");
4
+ const require_biff_xf_colors = require("./biff/xf-colors.cjs");
5
+ const require_biff_font = require("./biff/font.cjs");
6
+ const require_workbook_defined_names = require("./workbook/defined-names.cjs");
5
7
  const require_workbook_print_names = require("./workbook/print-names.cjs");
6
8
  const require_metadata = require("./metadata.cjs");
7
9
  const require_workbook_globals_writer = require("./workbook/globals-writer.cjs");
@@ -41,8 +43,16 @@ function defaultFormatIdForKind(kind) {
41
43
  case "empty": return GENERAL_FORMAT_ID;
42
44
  }
43
45
  }
44
- /** The number-format code a cell resolves through -- its own explicit numberFormatCode, or the built-in code for its value kind's own default identifier. This is called identically during the workbook-wide format scan and later per cell, so the two can never resolve a cell to different codes. */
46
+ /**
47
+ * The number-format code a cell resolves through -- its own explicit numberFormatCode, a `[$USD]#,##0.00`-shaped code for a currency cell that names an ISO 4217 code of its own (see below), or the built-in code for its value kind's own default identifier. This is called identically during the workbook-wide format scan and later per cell, so the two can never resolve a cell to different codes.
48
+ *
49
+ * The ISO-code bracket is the one carrier a currency code survives the round trip through: the format string IS where BIFF8 states a cell's currency, and the classifier this package's own reader reads it back through recovers the code from exactly that bracket -- writing the symbol instead would render identically and lose the code permanently, since no faithful symbol-to-code mapping exists on the way back ('$' alone is USD, CAD, AUD and a dozen others). That is the identical encoding ooxml.js's own typed/xlsx/number-format.ts currencyNumberFormat already states for the same schema field, so an amount round-trips through either legacy format under the same spelling. A currency string that is not an ISO-code shape (a display symbol like "£") cannot go inside that bracket without producing a malformed format code, so it falls back to the plain built-in currency format -- the value kind preserved, the code honestly lost, since inventing a code for a symbol would state a currency the cell never named.
50
+ */
45
51
  function formatCodeForCell(cell) {
52
+ if (cell.numberFormatCode === void 0 && cell.value.kind === "currency") {
53
+ const currency = cell.value.currency;
54
+ if (currency !== void 0 && (0, excel_number_format.isIsoCurrencyCodeShape)(currency)) return `[$${currency.toUpperCase()}]#,##0.00`;
55
+ }
46
56
  return cell.numberFormatCode ?? builtinCode(defaultFormatIdForKind(cell.value.kind));
47
57
  }
48
58
  /** Scans every sheet's cells once, assigning each distinct number-format code a formatId: reusing a built-in id for a code matching one of excel-number-format's own BUILTIN_NUMBER_FORMATS strings exactly, minting a new custom id from FIRST_CUSTOM_FORMAT_ID otherwise. Cell XF index assignment is a separate, later pass (buildCellXfPlan below) -- a formatId alone no longer determines a cell's XF index once decoration exists, since two cells sharing a format but differing in background/borders need two distinct XFs. */
@@ -103,6 +113,7 @@ function buildPalettePlan(sheets) {
103
113
  for (const cell of sheet.cells) {
104
114
  if (!require_written_cells.writesCellRecord(cell)) continue;
105
115
  recordFill(cell.background);
116
+ record(cell.font?.color);
106
117
  record(cell.borders?.left?.color);
107
118
  record(cell.borders?.right?.color);
108
119
  record(cell.borders?.top?.color);
@@ -201,33 +212,63 @@ function resolveDecorationForCell(cell, icvOf) {
201
212
  bottom: resolveWriteEdge(cell.borders?.bottom, icvOf)
202
213
  };
203
214
  }
204
- /** A deterministic signature for one cell XF's own (formatId, alignment, verticalAlignment, decoration) tuple, so two cells sharing all four share one XF record -- the interning key buildCellXfPlan below dedupes on, mirroring how CellFormatTable in ooxml.js's typed/xlsx/styles.ts dedupes an <xf> on (number format, decoration) together rather than on format alone, widened here by the cell's own alignment. */
205
- function signatureOfCellXf(formatId, alignment, verticalAlignment, decoration) {
206
- let signature = `f${formatId}|a${alignment ?? ""}|v${verticalAlignment ?? ""}`;
215
+ /** A deterministic signature for one cell XF's own (formatId, fontIndex, alignment, verticalAlignment, decoration) tuple, so two cells sharing all five share one XF record -- the interning key buildCellXfPlan below dedupes on, mirroring how CellFormatTable in ooxml.js's typed/xlsx/styles.ts dedupes an <xf> on (number format, decoration) together rather than on format alone, widened here by the cell's own font and alignment. */
216
+ function signatureOfCellXf(formatId, fontIndex, alignment, verticalAlignment, decoration) {
217
+ let signature = `f${formatId}|n${fontIndex}|a${alignment ?? ""}|v${verticalAlignment ?? ""}`;
207
218
  if (decoration === void 0) return signature;
208
219
  signature += `|p${decoration.fillPattern}:${decoration.fillForegroundIcv}:${decoration.fillBackgroundIcv}|l${decoration.left.style}:${decoration.left.icv}|r${decoration.right.style}:${decoration.right.icv}|t${decoration.top.style}:${decoration.top.icv}|b${decoration.bottom.style}:${decoration.bottom.icv}`;
209
220
  return signature;
210
221
  }
222
+ /** A deterministic signature for one font-table entry, the interning key below dedupes on -- name, height, the four flags, and the colour index, since those are the whole record as far as this package's reader is concerned. */
223
+ function signatureOfFont(fields) {
224
+ return `${fields.name}|${fields.heightTwips}|${fields.bold ? 1 : 0}${fields.italic ? 1 : 0}${fields.strikeout ? 1 : 0}${fields.underline ? 1 : 0}|${fields.colorIcv}`;
225
+ }
226
+ /**
227
+ * Scans every sheet's cells once, interning each distinct cell font into its own font-table entry: the Normal font is always entry 0 (every style XF and the implicit General cell XF reference it, whether or not any cell states a font of its own), and each distinct ContentFont the workbook's cells resolve to mints one further entry the first time it is seen. A ContentFont that normalises back to the Normal font's own fields -- absent, empty, or restating only default values -- resolves to entry 0 and mints nothing, the write-side mirror of the reader's own diff against entry 0.
228
+ */
229
+ function buildFontPlan(sheets, palettePlan) {
230
+ const fontEntries = [require_biff_font.NORMAL_FONT_FIELDS];
231
+ const indexBySignature = /* @__PURE__ */ new Map([[signatureOfFont(require_biff_font.NORMAL_FONT_FIELDS), 0]]);
232
+ const fieldsOf = (cell) => require_biff_font.xfFontFieldsOf(cell.font, palettePlan.icvOf);
233
+ for (const sheet of sheets) for (const cell of sheet.cells) {
234
+ if (!require_written_cells.writesCellRecord(cell)) continue;
235
+ const fields = fieldsOf(cell);
236
+ const signature = signatureOfFont(fields);
237
+ if (indexBySignature.has(signature)) continue;
238
+ indexBySignature.set(signature, fontEntries.length);
239
+ fontEntries.push(fields);
240
+ }
241
+ return {
242
+ fontEntries,
243
+ fontIndexForCell: (cell) => {
244
+ const index = indexBySignature.get(signatureOfFont(fieldsOf(cell)));
245
+ if (index === void 0) throw new require_biff_write_errors.BiffWriteError(`internal error: the cell at row ${cell.row}, column ${cell.column} resolves to a font the workbook-wide font scan never saw -- the writer's own "does this cell get a record" predicate and its font-interning pass disagree about this cell`);
246
+ return index;
247
+ }
248
+ };
249
+ }
211
250
  /**
212
- * Scans every sheet's cells once, interning each distinct (number format, decoration) combination into its own cell XF index -- a cell with General formatting and no decoration resolves to the workbook's own implicit GENERAL_CELL_XF_INDEX with no new XF record at all, exactly as before; every other combination mints one XF record the first time it is seen and is reused by every later cell sharing it.
251
+ * Scans every sheet's cells once, interning each distinct (number format, font, alignment, decoration) combination into its own cell XF index -- a cell with General formatting, the Normal font, and no decoration resolves to the workbook's own implicit GENERAL_CELL_XF_INDEX with no new XF record at all, exactly as before; every other combination mints one XF record the first time it is seen and is reused by every later cell sharing it.
213
252
  *
214
253
  * The returned xfIndexForCell only ever LOOKS UP -- it cannot mint an entry, and refuses a signature this scan never saw. buildWorkbookGlobals is handed cellXfEntries before any sheet's records are built, so an entry minted later than this scan would be one no XF record was written for, and the cell record naming its index would point past the end of the workbook's XF table. Nothing about the resulting bytes says so: a reader resolves that index to whatever XF happens to sit there, or to none, and the cell's format is silently wrong either way. Refusing the lookup is the only place that divergence can still be caught.
215
254
  */
216
- function buildCellXfPlan(sheets, formatPlan, palettePlan) {
255
+ function buildCellXfPlan(sheets, formatPlan, palettePlan, fontPlan) {
217
256
  const cellXfEntries = [];
218
- const xfIndexBySignature = /* @__PURE__ */ new Map([[signatureOfCellXf(GENERAL_FORMAT_ID, void 0, void 0, void 0), require_workbook_globals_writer.GENERAL_CELL_XF_INDEX]]);
257
+ const xfIndexBySignature = /* @__PURE__ */ new Map([[signatureOfCellXf(GENERAL_FORMAT_ID, 0, void 0, void 0, void 0), require_workbook_globals_writer.GENERAL_CELL_XF_INDEX]]);
219
258
  let nextXfIndex = require_workbook_globals_writer.GENERAL_CELL_XF_INDEX + 1;
220
- const signatureOf = (cell) => signatureOfCellXf(formatPlan.formatIdOf(formatCodeForCell(cell)), cell.alignment, cell.verticalAlignment, resolveDecorationForCell(cell, palettePlan.icvOf));
259
+ const signatureOf = (cell) => signatureOfCellXf(formatPlan.formatIdOf(formatCodeForCell(cell)), fontPlan.fontIndexForCell(cell), cell.alignment, cell.verticalAlignment, resolveDecorationForCell(cell, palettePlan.icvOf));
221
260
  for (const sheet of sheets) for (const cell of sheet.cells) {
222
261
  if (!require_written_cells.writesCellRecord(cell)) continue;
223
262
  const formatId = formatPlan.formatIdOf(formatCodeForCell(cell));
263
+ const fontIndex = fontPlan.fontIndexForCell(cell);
224
264
  const decoration = resolveDecorationForCell(cell, palettePlan.icvOf);
225
- const signature = signatureOfCellXf(formatId, cell.alignment, cell.verticalAlignment, decoration);
265
+ const signature = signatureOfCellXf(formatId, fontIndex, cell.alignment, cell.verticalAlignment, decoration);
226
266
  if (xfIndexBySignature.has(signature)) continue;
227
267
  xfIndexBySignature.set(signature, nextXfIndex);
228
268
  nextXfIndex += 1;
229
269
  cellXfEntries.push({
230
270
  formatId,
271
+ fontIndex,
231
272
  alignment: cell.alignment,
232
273
  verticalAlignment: cell.verticalAlignment,
233
274
  decoration
@@ -297,15 +338,18 @@ function buildWorkbookStream(content) {
297
338
  const formatPlan = buildFormatPlan(content.sheets);
298
339
  const sstPlan = buildSstPlan(content.sheets);
299
340
  const palettePlan = buildPalettePlan(content.sheets);
300
- const cellXfPlan = buildCellXfPlan(content.sheets, formatPlan, palettePlan);
341
+ const fontPlan = buildFontPlan(content.sheets, palettePlan);
342
+ const cellXfPlan = buildCellXfPlan(content.sheets, formatPlan, palettePlan, fontPlan);
301
343
  const globalsPlan = {
302
344
  sheetNames: content.sheets.map((sheet) => sheet.name),
345
+ fonts: fontPlan.fontEntries,
303
346
  customFormats: formatPlan.customFormats,
304
347
  cellXfEntries: cellXfPlan.cellXfEntries,
305
348
  sharedStrings: sstPlan.strings,
306
349
  sharedStringTotalCount: sstPlan.totalCount,
307
350
  paletteColors: palettePlan.paletteColors,
308
- printNames: buildPrintNamePlan(content.sheets)
351
+ printNames: buildPrintNamePlan(content.sheets),
352
+ definedNames: require_workbook_defined_names.definedNameEntriesFor(content.names ?? [], content.sheets)
309
353
  };
310
354
  const globals = require_workbook_globals_writer.buildWorkbookGlobals(globalsPlan);
311
355
  const sheetContext = {
package/dist/write.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import { SUMMARY_INFORMATION_STREAM } from "./container.js";
2
- import { DEFAULT_PALETTE_HEX_TO_ICV, PATTERN_TYPE_TO_FILL_PATTERN, borderStyleTokenFor } from "./biff/xf-colors.js";
3
2
  import { BiffWriteError } from "./biff/write-errors.js";
3
+ import { DEFAULT_PALETTE_HEX_TO_ICV, PATTERN_TYPE_TO_FILL_PATTERN, borderStyleTokenFor } from "./biff/xf-colors.js";
4
+ import { NORMAL_FONT_FIELDS, xfFontFieldsOf } from "./biff/font.js";
5
+ import { definedNameEntriesFor } from "./workbook/defined-names.js";
4
6
  import { printNameEntriesFor } from "./workbook/print-names.js";
5
7
  import { layoutMetadataToSummaryInformation as layoutMetadataToSummaryInformation$1 } from "./metadata.js";
6
8
  import { GENERAL_CELL_XF_INDEX, buildWorkbookGlobals } from "./workbook/globals-writer.js";
@@ -8,7 +10,7 @@ import { cellCarriesFormatting, writesCellRecord } from "./written-cells.js";
8
10
  import { buildWorksheetSubstream } from "./workbook/sheet-writer.js";
9
11
  import { hasSummaryInformationFields, writeCompoundFile, writeSummaryInformationStream } from "archive-codec";
10
12
  import { colorToRgbHex, flattenTree, unrecognizedFillKind } from "document-schema.js";
11
- import { BUILTIN_NUMBER_FORMATS } from "excel-number-format";
13
+ import { BUILTIN_NUMBER_FORMATS, isIsoCurrencyCodeShape } from "excel-number-format";
12
14
  //#region src/write.ts
13
15
  const WORKBOOK_STREAM_NAME = "Workbook";
14
16
  /** [MS-XLS] 2.4.126: a Format record's own ifmt is constrained to the ranges 5-8, 23-26, 41-44, 63-66, and 164-382 for a custom (non-built-in) code; 164 is the first identifier every real producer actually uses for a custom code, and this writer follows suit, incrementing sequentially and refusing to exceed the range's own ceiling. */
@@ -40,8 +42,16 @@ function defaultFormatIdForKind(kind) {
40
42
  case "empty": return GENERAL_FORMAT_ID;
41
43
  }
42
44
  }
43
- /** The number-format code a cell resolves through -- its own explicit numberFormatCode, or the built-in code for its value kind's own default identifier. This is called identically during the workbook-wide format scan and later per cell, so the two can never resolve a cell to different codes. */
45
+ /**
46
+ * The number-format code a cell resolves through -- its own explicit numberFormatCode, a `[$USD]#,##0.00`-shaped code for a currency cell that names an ISO 4217 code of its own (see below), or the built-in code for its value kind's own default identifier. This is called identically during the workbook-wide format scan and later per cell, so the two can never resolve a cell to different codes.
47
+ *
48
+ * The ISO-code bracket is the one carrier a currency code survives the round trip through: the format string IS where BIFF8 states a cell's currency, and the classifier this package's own reader reads it back through recovers the code from exactly that bracket -- writing the symbol instead would render identically and lose the code permanently, since no faithful symbol-to-code mapping exists on the way back ('$' alone is USD, CAD, AUD and a dozen others). That is the identical encoding ooxml.js's own typed/xlsx/number-format.ts currencyNumberFormat already states for the same schema field, so an amount round-trips through either legacy format under the same spelling. A currency string that is not an ISO-code shape (a display symbol like "£") cannot go inside that bracket without producing a malformed format code, so it falls back to the plain built-in currency format -- the value kind preserved, the code honestly lost, since inventing a code for a symbol would state a currency the cell never named.
49
+ */
44
50
  function formatCodeForCell(cell) {
51
+ if (cell.numberFormatCode === void 0 && cell.value.kind === "currency") {
52
+ const currency = cell.value.currency;
53
+ if (currency !== void 0 && isIsoCurrencyCodeShape(currency)) return `[$${currency.toUpperCase()}]#,##0.00`;
54
+ }
45
55
  return cell.numberFormatCode ?? builtinCode(defaultFormatIdForKind(cell.value.kind));
46
56
  }
47
57
  /** Scans every sheet's cells once, assigning each distinct number-format code a formatId: reusing a built-in id for a code matching one of excel-number-format's own BUILTIN_NUMBER_FORMATS strings exactly, minting a new custom id from FIRST_CUSTOM_FORMAT_ID otherwise. Cell XF index assignment is a separate, later pass (buildCellXfPlan below) -- a formatId alone no longer determines a cell's XF index once decoration exists, since two cells sharing a format but differing in background/borders need two distinct XFs. */
@@ -102,6 +112,7 @@ function buildPalettePlan(sheets) {
102
112
  for (const cell of sheet.cells) {
103
113
  if (!writesCellRecord(cell)) continue;
104
114
  recordFill(cell.background);
115
+ record(cell.font?.color);
105
116
  record(cell.borders?.left?.color);
106
117
  record(cell.borders?.right?.color);
107
118
  record(cell.borders?.top?.color);
@@ -200,33 +211,63 @@ function resolveDecorationForCell(cell, icvOf) {
200
211
  bottom: resolveWriteEdge(cell.borders?.bottom, icvOf)
201
212
  };
202
213
  }
203
- /** A deterministic signature for one cell XF's own (formatId, alignment, verticalAlignment, decoration) tuple, so two cells sharing all four share one XF record -- the interning key buildCellXfPlan below dedupes on, mirroring how CellFormatTable in ooxml.js's typed/xlsx/styles.ts dedupes an <xf> on (number format, decoration) together rather than on format alone, widened here by the cell's own alignment. */
204
- function signatureOfCellXf(formatId, alignment, verticalAlignment, decoration) {
205
- let signature = `f${formatId}|a${alignment ?? ""}|v${verticalAlignment ?? ""}`;
214
+ /** A deterministic signature for one cell XF's own (formatId, fontIndex, alignment, verticalAlignment, decoration) tuple, so two cells sharing all five share one XF record -- the interning key buildCellXfPlan below dedupes on, mirroring how CellFormatTable in ooxml.js's typed/xlsx/styles.ts dedupes an <xf> on (number format, decoration) together rather than on format alone, widened here by the cell's own font and alignment. */
215
+ function signatureOfCellXf(formatId, fontIndex, alignment, verticalAlignment, decoration) {
216
+ let signature = `f${formatId}|n${fontIndex}|a${alignment ?? ""}|v${verticalAlignment ?? ""}`;
206
217
  if (decoration === void 0) return signature;
207
218
  signature += `|p${decoration.fillPattern}:${decoration.fillForegroundIcv}:${decoration.fillBackgroundIcv}|l${decoration.left.style}:${decoration.left.icv}|r${decoration.right.style}:${decoration.right.icv}|t${decoration.top.style}:${decoration.top.icv}|b${decoration.bottom.style}:${decoration.bottom.icv}`;
208
219
  return signature;
209
220
  }
221
+ /** A deterministic signature for one font-table entry, the interning key below dedupes on -- name, height, the four flags, and the colour index, since those are the whole record as far as this package's reader is concerned. */
222
+ function signatureOfFont(fields) {
223
+ return `${fields.name}|${fields.heightTwips}|${fields.bold ? 1 : 0}${fields.italic ? 1 : 0}${fields.strikeout ? 1 : 0}${fields.underline ? 1 : 0}|${fields.colorIcv}`;
224
+ }
225
+ /**
226
+ * Scans every sheet's cells once, interning each distinct cell font into its own font-table entry: the Normal font is always entry 0 (every style XF and the implicit General cell XF reference it, whether or not any cell states a font of its own), and each distinct ContentFont the workbook's cells resolve to mints one further entry the first time it is seen. A ContentFont that normalises back to the Normal font's own fields -- absent, empty, or restating only default values -- resolves to entry 0 and mints nothing, the write-side mirror of the reader's own diff against entry 0.
227
+ */
228
+ function buildFontPlan(sheets, palettePlan) {
229
+ const fontEntries = [NORMAL_FONT_FIELDS];
230
+ const indexBySignature = /* @__PURE__ */ new Map([[signatureOfFont(NORMAL_FONT_FIELDS), 0]]);
231
+ const fieldsOf = (cell) => xfFontFieldsOf(cell.font, palettePlan.icvOf);
232
+ for (const sheet of sheets) for (const cell of sheet.cells) {
233
+ if (!writesCellRecord(cell)) continue;
234
+ const fields = fieldsOf(cell);
235
+ const signature = signatureOfFont(fields);
236
+ if (indexBySignature.has(signature)) continue;
237
+ indexBySignature.set(signature, fontEntries.length);
238
+ fontEntries.push(fields);
239
+ }
240
+ return {
241
+ fontEntries,
242
+ fontIndexForCell: (cell) => {
243
+ const index = indexBySignature.get(signatureOfFont(fieldsOf(cell)));
244
+ if (index === void 0) throw new BiffWriteError(`internal error: the cell at row ${cell.row}, column ${cell.column} resolves to a font the workbook-wide font scan never saw -- the writer's own "does this cell get a record" predicate and its font-interning pass disagree about this cell`);
245
+ return index;
246
+ }
247
+ };
248
+ }
210
249
  /**
211
- * Scans every sheet's cells once, interning each distinct (number format, decoration) combination into its own cell XF index -- a cell with General formatting and no decoration resolves to the workbook's own implicit GENERAL_CELL_XF_INDEX with no new XF record at all, exactly as before; every other combination mints one XF record the first time it is seen and is reused by every later cell sharing it.
250
+ * Scans every sheet's cells once, interning each distinct (number format, font, alignment, decoration) combination into its own cell XF index -- a cell with General formatting, the Normal font, and no decoration resolves to the workbook's own implicit GENERAL_CELL_XF_INDEX with no new XF record at all, exactly as before; every other combination mints one XF record the first time it is seen and is reused by every later cell sharing it.
212
251
  *
213
252
  * The returned xfIndexForCell only ever LOOKS UP -- it cannot mint an entry, and refuses a signature this scan never saw. buildWorkbookGlobals is handed cellXfEntries before any sheet's records are built, so an entry minted later than this scan would be one no XF record was written for, and the cell record naming its index would point past the end of the workbook's XF table. Nothing about the resulting bytes says so: a reader resolves that index to whatever XF happens to sit there, or to none, and the cell's format is silently wrong either way. Refusing the lookup is the only place that divergence can still be caught.
214
253
  */
215
- function buildCellXfPlan(sheets, formatPlan, palettePlan) {
254
+ function buildCellXfPlan(sheets, formatPlan, palettePlan, fontPlan) {
216
255
  const cellXfEntries = [];
217
- const xfIndexBySignature = /* @__PURE__ */ new Map([[signatureOfCellXf(GENERAL_FORMAT_ID, void 0, void 0, void 0), GENERAL_CELL_XF_INDEX]]);
256
+ const xfIndexBySignature = /* @__PURE__ */ new Map([[signatureOfCellXf(GENERAL_FORMAT_ID, 0, void 0, void 0, void 0), GENERAL_CELL_XF_INDEX]]);
218
257
  let nextXfIndex = GENERAL_CELL_XF_INDEX + 1;
219
- const signatureOf = (cell) => signatureOfCellXf(formatPlan.formatIdOf(formatCodeForCell(cell)), cell.alignment, cell.verticalAlignment, resolveDecorationForCell(cell, palettePlan.icvOf));
258
+ const signatureOf = (cell) => signatureOfCellXf(formatPlan.formatIdOf(formatCodeForCell(cell)), fontPlan.fontIndexForCell(cell), cell.alignment, cell.verticalAlignment, resolveDecorationForCell(cell, palettePlan.icvOf));
220
259
  for (const sheet of sheets) for (const cell of sheet.cells) {
221
260
  if (!writesCellRecord(cell)) continue;
222
261
  const formatId = formatPlan.formatIdOf(formatCodeForCell(cell));
262
+ const fontIndex = fontPlan.fontIndexForCell(cell);
223
263
  const decoration = resolveDecorationForCell(cell, palettePlan.icvOf);
224
- const signature = signatureOfCellXf(formatId, cell.alignment, cell.verticalAlignment, decoration);
264
+ const signature = signatureOfCellXf(formatId, fontIndex, cell.alignment, cell.verticalAlignment, decoration);
225
265
  if (xfIndexBySignature.has(signature)) continue;
226
266
  xfIndexBySignature.set(signature, nextXfIndex);
227
267
  nextXfIndex += 1;
228
268
  cellXfEntries.push({
229
269
  formatId,
270
+ fontIndex,
230
271
  alignment: cell.alignment,
231
272
  verticalAlignment: cell.verticalAlignment,
232
273
  decoration
@@ -296,15 +337,18 @@ function buildWorkbookStream(content) {
296
337
  const formatPlan = buildFormatPlan(content.sheets);
297
338
  const sstPlan = buildSstPlan(content.sheets);
298
339
  const palettePlan = buildPalettePlan(content.sheets);
299
- const cellXfPlan = buildCellXfPlan(content.sheets, formatPlan, palettePlan);
340
+ const fontPlan = buildFontPlan(content.sheets, palettePlan);
341
+ const cellXfPlan = buildCellXfPlan(content.sheets, formatPlan, palettePlan, fontPlan);
300
342
  const globalsPlan = {
301
343
  sheetNames: content.sheets.map((sheet) => sheet.name),
344
+ fonts: fontPlan.fontEntries,
302
345
  customFormats: formatPlan.customFormats,
303
346
  cellXfEntries: cellXfPlan.cellXfEntries,
304
347
  sharedStrings: sstPlan.strings,
305
348
  sharedStringTotalCount: sstPlan.totalCount,
306
349
  paletteColors: palettePlan.paletteColors,
307
- printNames: buildPrintNamePlan(content.sheets)
350
+ printNames: buildPrintNamePlan(content.sheets),
351
+ definedNames: definedNameEntriesFor(content.names ?? [], content.sheets)
308
352
  };
309
353
  const globals = buildWorkbookGlobals(globalsPlan);
310
354
  const sheetContext = {
@@ -1,14 +1,16 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_biff_font = require("./biff/font.cjs");
2
3
  //#region src/written-cells.ts
3
4
  /**
4
- * Whether a cell carries real formatting beyond General/bottom-aligned/undecorated: a background fill, a border on at least one side, a non-default horizontal alignment, or a non-default vertical alignment.
5
+ * Whether a cell carries real formatting beyond General/bottom-aligned/undecorated/default-font: a background fill, a border on at least one side, a non-default horizontal alignment, a non-default vertical alignment, or a font that differs from the workbook's own Normal font.
5
6
  *
6
- * A present `borders` object is not enough on its own -- one with no side set describes no border at all, and treating it as formatting would mint an XF byte-identical to the undecorated one and, for an empty cell, a Blank record the reader would then correctly drop again. write.ts's own resolveDecorationForCell answers "no decoration" for exactly this set through this same predicate, so the two cannot disagree about what an empty-but-present borders object means.
7
+ * A present `borders` object is not enough on its own -- one with no side set describes no border at all, and treating it as formatting would mint an XF byte-identical to the undecorated one and, for an empty cell, a Blank record the reader would then correctly drop again. write.ts's own resolveDecorationForCell answers "no decoration" for exactly this set through this same predicate, so the two cannot disagree about what an empty-but-present borders object means. A font is held to the identical standard through biff/font.ts's cellFontDiffersFromNormal, so a ContentFont that merely restates a default value (fontFamily "Arial", sizePt 10, bold false) is none either, for the same reason.
7
8
  */
8
9
  function cellCarriesFormatting(cell) {
9
10
  if (cell.background !== void 0) return true;
10
11
  if (cell.alignment !== void 0) return true;
11
12
  if (cell.verticalAlignment !== void 0) return true;
13
+ if (cell.font !== void 0 && require_biff_font.cellFontDiffersFromNormal(cell.font)) return true;
12
14
  const { borders } = cell;
13
15
  if (borders === void 0) return false;
14
16
  return borders.left !== void 0 || borders.right !== void 0 || borders.top !== void 0 || borders.bottom !== void 0;
@@ -1,9 +1,9 @@
1
1
  import { ContentSheetCell } from "document-schema.js";
2
2
  //#region src/written-cells.d.ts
3
3
  /**
4
- * Whether a cell carries real formatting beyond General/bottom-aligned/undecorated: a background fill, a border on at least one side, a non-default horizontal alignment, or a non-default vertical alignment.
4
+ * Whether a cell carries real formatting beyond General/bottom-aligned/undecorated/default-font: a background fill, a border on at least one side, a non-default horizontal alignment, a non-default vertical alignment, or a font that differs from the workbook's own Normal font.
5
5
  *
6
- * A present `borders` object is not enough on its own -- one with no side set describes no border at all, and treating it as formatting would mint an XF byte-identical to the undecorated one and, for an empty cell, a Blank record the reader would then correctly drop again. write.ts's own resolveDecorationForCell answers "no decoration" for exactly this set through this same predicate, so the two cannot disagree about what an empty-but-present borders object means.
6
+ * A present `borders` object is not enough on its own -- one with no side set describes no border at all, and treating it as formatting would mint an XF byte-identical to the undecorated one and, for an empty cell, a Blank record the reader would then correctly drop again. write.ts's own resolveDecorationForCell answers "no decoration" for exactly this set through this same predicate, so the two cannot disagree about what an empty-but-present borders object means. A font is held to the identical standard through biff/font.ts's cellFontDiffersFromNormal, so a ContentFont that merely restates a default value (fontFamily "Arial", sizePt 10, bold false) is none either, for the same reason.
7
7
  */
8
8
  declare function cellCarriesFormatting(cell: ContentSheetCell): boolean;
9
9
  /**
@@ -1,9 +1,9 @@
1
1
  import { ContentSheetCell } from "document-schema.js";
2
2
  //#region src/written-cells.d.ts
3
3
  /**
4
- * Whether a cell carries real formatting beyond General/bottom-aligned/undecorated: a background fill, a border on at least one side, a non-default horizontal alignment, or a non-default vertical alignment.
4
+ * Whether a cell carries real formatting beyond General/bottom-aligned/undecorated/default-font: a background fill, a border on at least one side, a non-default horizontal alignment, a non-default vertical alignment, or a font that differs from the workbook's own Normal font.
5
5
  *
6
- * A present `borders` object is not enough on its own -- one with no side set describes no border at all, and treating it as formatting would mint an XF byte-identical to the undecorated one and, for an empty cell, a Blank record the reader would then correctly drop again. write.ts's own resolveDecorationForCell answers "no decoration" for exactly this set through this same predicate, so the two cannot disagree about what an empty-but-present borders object means.
6
+ * A present `borders` object is not enough on its own -- one with no side set describes no border at all, and treating it as formatting would mint an XF byte-identical to the undecorated one and, for an empty cell, a Blank record the reader would then correctly drop again. write.ts's own resolveDecorationForCell answers "no decoration" for exactly this set through this same predicate, so the two cannot disagree about what an empty-but-present borders object means. A font is held to the identical standard through biff/font.ts's cellFontDiffersFromNormal, so a ContentFont that merely restates a default value (fontFamily "Arial", sizePt 10, bold false) is none either, for the same reason.
7
7
  */
8
8
  declare function cellCarriesFormatting(cell: ContentSheetCell): boolean;
9
9
  /**
@@ -1,13 +1,15 @@
1
+ import { cellFontDiffersFromNormal } from "./biff/font.js";
1
2
  //#region src/written-cells.ts
2
3
  /**
3
- * Whether a cell carries real formatting beyond General/bottom-aligned/undecorated: a background fill, a border on at least one side, a non-default horizontal alignment, or a non-default vertical alignment.
4
+ * Whether a cell carries real formatting beyond General/bottom-aligned/undecorated/default-font: a background fill, a border on at least one side, a non-default horizontal alignment, a non-default vertical alignment, or a font that differs from the workbook's own Normal font.
4
5
  *
5
- * A present `borders` object is not enough on its own -- one with no side set describes no border at all, and treating it as formatting would mint an XF byte-identical to the undecorated one and, for an empty cell, a Blank record the reader would then correctly drop again. write.ts's own resolveDecorationForCell answers "no decoration" for exactly this set through this same predicate, so the two cannot disagree about what an empty-but-present borders object means.
6
+ * A present `borders` object is not enough on its own -- one with no side set describes no border at all, and treating it as formatting would mint an XF byte-identical to the undecorated one and, for an empty cell, a Blank record the reader would then correctly drop again. write.ts's own resolveDecorationForCell answers "no decoration" for exactly this set through this same predicate, so the two cannot disagree about what an empty-but-present borders object means. A font is held to the identical standard through biff/font.ts's cellFontDiffersFromNormal, so a ContentFont that merely restates a default value (fontFamily "Arial", sizePt 10, bold false) is none either, for the same reason.
6
7
  */
7
8
  function cellCarriesFormatting(cell) {
8
9
  if (cell.background !== void 0) return true;
9
10
  if (cell.alignment !== void 0) return true;
10
11
  if (cell.verticalAlignment !== void 0) return true;
12
+ if (cell.font !== void 0 && cellFontDiffersFromNormal(cell.font)) return true;
11
13
  const { borders } = cell;
12
14
  if (borders === void 0) return false;
13
15
  return borders.left !== void 0 || borders.right !== void 0 || borders.top !== void 0 || borders.bottom !== void 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xls-codec",
3
- "version": "4.12.5",
3
+ "version": "4.13.0",
4
4
  "description": "Hand-written reader for the legacy Excel Binary File Format (.xls, BIFF8) as specified by [MS-XLS], mapping a workbook's record stream onto the shared document-schema.js spreadsheet model - the .xls codec for the documents.js family.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -70,8 +70,8 @@
70
70
  },
71
71
  "packageManager": "pnpm@11.6.0",
72
72
  "dependencies": {
73
- "archive-codec": "1.10.6",
74
- "document-schema.js": "7.9.0",
73
+ "archive-codec": "1.10.8",
74
+ "document-schema.js": "7.10.0",
75
75
  "excel-number-format": "1.1.1"
76
76
  },
77
77
  "devDependencies": {