xls-codec 4.5.0 → 4.6.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 CHANGED
@@ -55,8 +55,9 @@ Each deliberate rather than overlooked:
55
55
  - **Cell comments, resolved for real (ExaDev/documents.js#949).** A legacy BIFF8 comment is split across three record kinds a single self-contained xlsx `<comment>` element never needs: a `Note` record ([MS-XLS] 2.4.179, wrapping a `NoteSh` structure) anchors the comment to a cell and names its author, but carries no text of its own; the text lives in a `Txo` record ([MS-XLS] 2.4.329), its characters carried across trailing `Continue` records; the two are joined through an `Obj` record ([MS-XLS] 2.4.181) whose `FtCmo` names an object id and type, the `Note`'s own `idObj` field naming that same id, and a `Txo` always immediately following the `Obj` record whose shape it belongs to (`src/workbook/comments.ts`). Read in one independent pass over the whole worksheet substream rather than assuming stream order between a `Note` and the `Obj`+`Txo` pair it names -- both are resolved regardless of which comes first. Legacy BIFF8 has no threading and no per-comment timestamp at all (unlike xlsx's own `[MS-XLSX]` threaded-comments extension), so `ContentSheetCellComment.replies` and `.createdAt` are never populated from an `.xls` source; rich per-character formatting within a comment (`TxORuns`) is skipped rather than modelled, the same scope limit this reader already applies to the shared string table's own rich runs, since `ContentSheetCellComment.text` is a plain string with nowhere to carry them.
56
56
  - **Data validation, resolved for real (ExaDev/documents.js#1098).** `Dv` ([MS-XLS] 2.4.95) is a fixed, published binary structure -- a bit-packed flags `DWORD` (`valType`/`errStyle`/`fAllowBlank`/`fShowInputMsg`/`fShowErrorMsg`/`typOperator`), four `XLUnicodeString`s (prompt/error titles and messages), one or two `DVParsedFormula` structures, and a trailing `SqRefU` range list -- read directly against that spec (`src/workbook/data-validation.ts`), unlike `calcext:condition`'s own LibreOffice-source-transcribed mini-language odf.js's equivalent reader needed. A `DVParsedFormula`'s own `rgce` is the identical `Ptg` token grammar a cell's `Formula` record carries, so its formula text is recovered with the same `parseFormulaText` this reader already uses for ordinary cell formulas. `DVal` ([MS-XLS] 2.4.96), the record that precedes a sheet's own `Dv` collection, carries only input-window UI state (position, drop-down `Obj` reference) with no counterpart in `ContentSheetDataValidationSchema` and is not read for its own fields.
57
57
  - **Conditional formatting, base rules resolved for real (ExaDev/documents.js#1102).** A `CondFmt` record ([MS-XLS] 2.4.56) marks the start of one to three `CF` records ([MS-XLS] 2.4.42) sharing one cell-range list, read together as a group (`src/workbook/conditional-format.ts`) since a `CF`'s own ranges live on its parent `CondFmt`, not on itself. Base BIFF8 (Excel 97) conditional formatting has exactly two rule shapes: a comparison ("Cell Value Is") condition, promoted to `ContentSheetConditionalFormat`'s `cellIs` variant with its formula(s) recovered through the same `parseFormulaText` this reader already uses for cell formulas and `Dv`; and a formula condition, left unpromoted for the same "no closed-form structure without a general formula engine" reason `data-validation.ts` and `ooxml.js`'s own xlsx `cfRule` reading already draw. A rule's resulting style -- font colour and fill background -- comes from its `CF`'s own `DXFN` structure, resolved through the identical `icv`/`Palette` colour machinery a regular cell's fill and border already use ([Cell decoration](#cell-decoration)); a `DXFN` naming a user-defined number format (`DXFNumUsr`) degrades that one rule's style to absent rather than risk misreading the font/fill fields that follow it, since [MS-XLS]'s own prose for `DXFNumUsr`'s length field does not settle whether it counts itself. Every richer rule type Excel 2007+ added has no representation in the base `CF` record at all; it rides a `CF12` record instead, linked to its own `CondFmt` through a `CFEx` extension record.
58
- - **Conditional formatting, CF12's colour scale/data bar/icon set rules resolved for real (ExaDev/documents.js#1104).** `CF12`'s own `ct` field picks one of six rule shapes ([MS-XLS] 2.4.43); this reader promotes the three that share a genuine array-of-thresholds building block already modelled in `document-schema.js` -- colour scale, data bar, and icon set (`src/workbook/conditional-format-12.ts`) -- each reading an array of `CFVO` threshold objects (the identical `num`/`percent`/`max`/`min`/`formula`/`percentile` vocabulary `ContentSheetConditionalFormatValueSchema` already carries for `ooxml.js`'s xlsx reader and `odf.js`'s ods reader) paired with `CFColor` values. A `CFColor` naming an indexed colour resolves through the same `icv`/`Palette` machinery as base `CF`'s own `DXFN`; one naming a plain RGB triple resolves directly; one naming an automatic or theme colour degrades the whole rule to absent, since this package has no BIFF8 `Theme` reader to resolve a theme reference against. Icon set's own `iIconSet` byte maps to the identical ECMA-376 `ST_IconSetType` vocabulary (`3Arrows`, `3TrafficLights1`, `5Quarters`, and the rest of the seventeen built-in sets) `ooxml.js`'s xlsx `cfRule` reading already carries as a plain string. `CF12`'s remaining rule shapes -- comparison/formula rules re-expressed in this newer record shape, and the filter-dispatched template family (top10, aboveAverage, duplicate/unique values, text/date conditions, dispatched through `icfTemplate` + a fixed `CFExTemplateParams` block) -- remain unread (ExaDev/documents.js#1100), along with `CFEx`'s own extension of a legacy `CF` record.
59
- - **Not read at all:** charts, drawings and images, and `CF12`'s filter-dispatched template rules plus `CFEx`'s own `CF`-record extension (ExaDev/documents.js#1100). Defined names (`Lbl`) are read only for the two built-in ones a sheet's print range and repeated header bands live in ([Print settings](#print-settings)); a user-defined name has nowhere to land in `document-schema.js`'s spreadsheet model, so it is skipped.
58
+ - **Conditional formatting, CF12's colour scale/data bar/icon set rules resolved for real (ExaDev/documents.js#1104).** `CF12`'s own `ct` field picks one of six rule shapes ([MS-XLS] 2.4.43); this reader promotes the three that share a genuine array-of-thresholds building block already modelled in `document-schema.js` -- colour scale, data bar, and icon set (`src/workbook/conditional-format-12.ts`) -- each reading an array of `CFVO` threshold objects (the identical `num`/`percent`/`max`/`min`/`formula`/`percentile` vocabulary `ContentSheetConditionalFormatValueSchema` already carries for `ooxml.js`'s xlsx reader and `odf.js`'s ods reader) paired with `CFColor` values. A `CFColor` naming an indexed colour resolves through the same `icv`/`Palette` machinery as base `CF`'s own `DXFN`, then Excel's own TintAndShade model applies the colour's own tint/shade Xnum (`xf-colors.ts`'s `applyTint`); one naming a plain RGB triple resolves directly, tint included; one naming an automatic or theme colour degrades the whole rule to absent, since this package has no BIFF8 `Theme` reader to resolve a theme reference against. Icon set's own `iIconSet` byte maps to the identical ECMA-376 `ST_IconSetType` vocabulary (`3Arrows`, `3TrafficLights1`, `5Quarters`, and the rest of the seventeen built-in sets) `ooxml.js`'s xlsx `cfRule` reading already carries as a plain string. A record longer than the 8224-byte single-record ceiling continues onto `ContinueFrt12` rather than the plain `Continue` every other record in this package joins against; `groupRecords` (`src/biff/substreams.ts`) strips its own restated `FrtRefHeader` before joining it.
59
+ - **Conditional formatting, CF12's filter-dispatched template rules resolved for real (ExaDev/documents.js#1106).** `ct` 0x05 ("filter") dispatches further through `icfTemplate`, alongside a 16-byte `CFExTemplateParams` block always present regardless of `ct`. Of that block's five variants, only two need real parsing -- `CFExFilterParams` (top10's own `fTop`/`fPercent`/`iParam`) and `CFExAveragesTemplateParams` (the aboveAverage family's own standard-deviation count) -- since `CFExDefaultTemplateParams` (duplicateValues/uniqueValues/the four blank/error conditions) is 16 reserved bytes and `CFExDateTemplateParams`'s own `dateOp` field is a fixed restatement of `icfTemplate` for all ten date/time periods, so both dispatch directly off `icfTemplate` with no further byte reading. Unlike colour scale/data bar/icon set, [MS-XLS] does not force a `ct` 0x05 rule's own `cbDxf` to zero, so its `DXFN12` can carry a genuine font/fill override; that structure resolves through the identical `parseDxfStyle` base `CF`'s own `DXFN` already uses. A malformed `iParam` of zero degrades a top10 rule to absent rather than promoting a `rank` `ContentSheetConditionalFormatSchema` itself requires to be positive. Comparison/formula rules re-expressed in this newer record shape, `containsText`'s own four sub-types (`CFExTextTemplateParams` names which one a rule is but not its actual search string, and CF12's own formula fields are spec'd to be zero for every `ct` 0x05 rule -- where the literal text operand lives is a genuine open question), and `CFEx`'s own extension of a legacy `CF` record all remain unread (ExaDev/documents.js#1100).
60
+ - **Not read at all:** charts, drawings and images, `CF12`'s `containsText` filter rules, and `CFEx`'s own `CF`-record extension (ExaDev/documents.js#1100). Defined names (`Lbl`) are read only for the two built-in ones a sheet's print range and repeated header bands live in ([Print settings](#print-settings)); a user-defined name has nowhere to land in `document-schema.js`'s spreadsheet model, so it is skipped.
60
61
  - **Encrypted workbooks** are refused rather than mis-read: a `FilePass` record means every record after it is ciphertext.
61
62
 
62
63
  This package is wired into `documents.js`'s conversion registry (`xlsToPdf`/`pdfToXls`, `convertDocument("xls", ...)`, and every same-variant spreadsheet bridge) — see that package's own README Fidelity table for exactly which pairs route and which don't. Remaining read+write scope gaps (formula writing and per-cell font) are tracked on [#815](https://github.com/ExaDev/documents.js/issues/815).
@@ -1,6 +1,7 @@
1
1
  import { r as FormulaSheetContext } from "./ptg-CCBbJLZJ.cjs";
2
2
  import { t as RecordGroup } from "./substreams-Cpy5Fi3d.cjs";
3
- import { Color, ContentSheetConditionalFormatValue, ContentSheetRange } from "document-schema.js";
3
+ import { i as RawConditionalFormatStyle } from "./conditional-format-S9NaPjhr.cjs";
4
+ import { Color, ContentSheetConditionalFormat, ContentSheetConditionalFormatValue, ContentSheetRange } from "document-schema.js";
4
5
  //#region src/workbook/conditional-format-12.d.ts
5
6
  type RawCfColor = {
6
7
  readonly kind: "icv";
@@ -38,7 +39,33 @@ interface RawIconSetFormat extends RawConditionalFormat12Common {
38
39
  readonly reverse: boolean;
39
40
  readonly showValue: boolean;
40
41
  }
41
- type RawConditionalFormat12 = RawColorScaleFormat | RawDataBarFormat | RawIconSetFormat;
42
+ type RawTimePeriod = Extract<ContentSheetConditionalFormat, {
43
+ readonly type: "timePeriod";
44
+ }>["timePeriod"];
45
+ interface RawFilterFormatCommon extends RawConditionalFormat12Common {
46
+ readonly style: RawConditionalFormatStyle | undefined;
47
+ }
48
+ interface RawTop10Format extends RawFilterFormatCommon {
49
+ readonly kind: "top10";
50
+ readonly rank: number;
51
+ readonly percent: boolean;
52
+ readonly bottom: boolean;
53
+ }
54
+ interface RawAboveAverageFormat extends RawFilterFormatCommon {
55
+ readonly kind: "aboveAverage";
56
+ readonly aboveAverage: boolean;
57
+ readonly equalAverage: boolean;
58
+ readonly stdDev: number | undefined;
59
+ }
60
+ interface RawTimePeriodFormat extends RawFilterFormatCommon {
61
+ readonly kind: "timePeriod";
62
+ readonly timePeriod: RawTimePeriod;
63
+ }
64
+ /** duplicateValues/uniqueValues and the four blank/error conditions carry no data beyond which one a rule is -- CFExDefaultTemplateParams is 16 reserved bytes ([MS-XLS] 2.4, https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-xls/c8f156b6-10ec-4594-adb1-c734fbb1fc11). */
65
+ interface RawSimpleFilterFormat extends RawFilterFormatCommon {
66
+ readonly kind: "containsBlanks" | "notContainsBlanks" | "containsErrors" | "notContainsErrors" | "uniqueValues" | "duplicateValues";
67
+ }
68
+ type RawConditionalFormat12 = RawColorScaleFormat | RawDataBarFormat | RawIconSetFormat | RawTop10Format | RawAboveAverageFormat | RawTimePeriodFormat | RawSimpleFilterFormat;
42
69
  interface CondFmt12GroupResult {
43
70
  readonly formats: RawConditionalFormat12[];
44
71
  /** How many records (the CondFmt12 itself plus every CF12 it claimed) the caller should advance past, regardless of how many rules actually promoted. */
@@ -46,4 +73,4 @@ interface CondFmt12GroupResult {
46
73
  }
47
74
  declare function readCondFmt12Group(records: readonly RecordGroup[], startIndex: number, formulaSheets: FormulaSheetContext): CondFmt12GroupResult;
48
75
  //#endregion
49
- export { RawDataBarFormat as a, RawConditionalFormat12 as i, RawCfColor as n, RawIconSetFormat as o, RawColorScaleFormat as r, readCondFmt12Group as s, CondFmt12GroupResult as t };
76
+ export { RawConditionalFormat12 as a, RawSimpleFilterFormat as c, RawTop10Format as d, readCondFmt12Group as f, RawColorScaleFormat as i, RawTimePeriod as l, RawAboveAverageFormat as n, RawDataBarFormat as o, RawCfColor as r, RawIconSetFormat as s, CondFmt12GroupResult as t, RawTimePeriodFormat as u };
@@ -1,6 +1,7 @@
1
1
  import { r as FormulaSheetContext } from "./ptg-CCBbJLZJ.js";
2
2
  import { t as RecordGroup } from "./substreams-CmyZMtuM.js";
3
- import { Color, ContentSheetConditionalFormatValue, ContentSheetRange } from "document-schema.js";
3
+ import { i as RawConditionalFormatStyle } from "./conditional-format-CWlz0Ty6.js";
4
+ import { Color, ContentSheetConditionalFormat, ContentSheetConditionalFormatValue, ContentSheetRange } from "document-schema.js";
4
5
  //#region src/workbook/conditional-format-12.d.ts
5
6
  type RawCfColor = {
6
7
  readonly kind: "icv";
@@ -38,7 +39,33 @@ interface RawIconSetFormat extends RawConditionalFormat12Common {
38
39
  readonly reverse: boolean;
39
40
  readonly showValue: boolean;
40
41
  }
41
- type RawConditionalFormat12 = RawColorScaleFormat | RawDataBarFormat | RawIconSetFormat;
42
+ type RawTimePeriod = Extract<ContentSheetConditionalFormat, {
43
+ readonly type: "timePeriod";
44
+ }>["timePeriod"];
45
+ interface RawFilterFormatCommon extends RawConditionalFormat12Common {
46
+ readonly style: RawConditionalFormatStyle | undefined;
47
+ }
48
+ interface RawTop10Format extends RawFilterFormatCommon {
49
+ readonly kind: "top10";
50
+ readonly rank: number;
51
+ readonly percent: boolean;
52
+ readonly bottom: boolean;
53
+ }
54
+ interface RawAboveAverageFormat extends RawFilterFormatCommon {
55
+ readonly kind: "aboveAverage";
56
+ readonly aboveAverage: boolean;
57
+ readonly equalAverage: boolean;
58
+ readonly stdDev: number | undefined;
59
+ }
60
+ interface RawTimePeriodFormat extends RawFilterFormatCommon {
61
+ readonly kind: "timePeriod";
62
+ readonly timePeriod: RawTimePeriod;
63
+ }
64
+ /** duplicateValues/uniqueValues and the four blank/error conditions carry no data beyond which one a rule is -- CFExDefaultTemplateParams is 16 reserved bytes ([MS-XLS] 2.4, https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-xls/c8f156b6-10ec-4594-adb1-c734fbb1fc11). */
65
+ interface RawSimpleFilterFormat extends RawFilterFormatCommon {
66
+ readonly kind: "containsBlanks" | "notContainsBlanks" | "containsErrors" | "notContainsErrors" | "uniqueValues" | "duplicateValues";
67
+ }
68
+ type RawConditionalFormat12 = RawColorScaleFormat | RawDataBarFormat | RawIconSetFormat | RawTop10Format | RawAboveAverageFormat | RawTimePeriodFormat | RawSimpleFilterFormat;
42
69
  interface CondFmt12GroupResult {
43
70
  readonly formats: RawConditionalFormat12[];
44
71
  /** How many records (the CondFmt12 itself plus every CF12 it claimed) the caller should advance past, regardless of how many rules actually promoted. */
@@ -46,4 +73,4 @@ interface CondFmt12GroupResult {
46
73
  }
47
74
  declare function readCondFmt12Group(records: readonly RecordGroup[], startIndex: number, formulaSheets: FormulaSheetContext): CondFmt12GroupResult;
48
75
  //#endregion
49
- export { RawDataBarFormat as a, RawConditionalFormat12 as i, RawCfColor as n, RawIconSetFormat as o, RawColorScaleFormat as r, readCondFmt12Group as s, CondFmt12GroupResult as t };
76
+ export { RawConditionalFormat12 as a, RawSimpleFilterFormat as c, RawTop10Format as d, readCondFmt12Group as f, RawColorScaleFormat as i, RawTimePeriod as l, RawAboveAverageFormat as n, RawDataBarFormat as o, RawCfColor as r, RawIconSetFormat as s, CondFmt12GroupResult as t, RawTimePeriodFormat as u };
@@ -18,6 +18,7 @@ interface RawConditionalFormat {
18
18
  readonly style: RawConditionalFormatStyle | undefined;
19
19
  readonly ranges: ContentSheetRange[];
20
20
  }
21
+ declare function parseDxfStyle(dxfBytes: Uint8Array<ArrayBuffer>): RawConditionalFormatStyle | undefined;
21
22
  interface CondFmtGroupResult {
22
23
  readonly formats: RawConditionalFormat[];
23
24
  /** How many records (the CondFmt itself plus every CF it claimed) the caller should advance past, regardless of how many rules actually promoted. */
@@ -25,4 +26,4 @@ interface CondFmtGroupResult {
25
26
  }
26
27
  declare function readCondFmtGroup(records: readonly RecordGroup[], startIndex: number, formulaSheets: FormulaSheetContext): CondFmtGroupResult;
27
28
  //#endregion
28
- export { readCondFmtGroup as a, RawConditionalFormatStyle as i, RawConditionalFormat as n, RawConditionalFormatFill as r, CondFmtGroupResult as t };
29
+ export { parseDxfStyle as a, RawConditionalFormatStyle as i, RawConditionalFormat as n, readCondFmtGroup as o, RawConditionalFormatFill as r, CondFmtGroupResult as t };
@@ -18,6 +18,7 @@ interface RawConditionalFormat {
18
18
  readonly style: RawConditionalFormatStyle | undefined;
19
19
  readonly ranges: ContentSheetRange[];
20
20
  }
21
+ declare function parseDxfStyle(dxfBytes: Uint8Array<ArrayBuffer>): RawConditionalFormatStyle | undefined;
21
22
  interface CondFmtGroupResult {
22
23
  readonly formats: RawConditionalFormat[];
23
24
  /** How many records (the CondFmt itself plus every CF it claimed) the caller should advance past, regardless of how many rules actually promoted. */
@@ -25,4 +26,4 @@ interface CondFmtGroupResult {
25
26
  }
26
27
  declare function readCondFmtGroup(records: readonly RecordGroup[], startIndex: number, formulaSheets: FormulaSheetContext): CondFmtGroupResult;
27
28
  //#endregion
28
- export { readCondFmtGroup as a, RawConditionalFormatStyle as i, RawConditionalFormat as n, RawConditionalFormatFill as r, CondFmtGroupResult as t };
29
+ export { parseDxfStyle as a, RawConditionalFormatStyle as i, RawConditionalFormat as n, readCondFmtGroup as o, RawConditionalFormatFill as r, CondFmtGroupResult as t };
package/dist/content.cjs CHANGED
@@ -186,12 +186,55 @@ function mapConditionalFormats12(raw, palette) {
186
186
  });
187
187
  continue;
188
188
  }
189
+ if (format.kind === "iconSet") {
190
+ results.push({
191
+ type: "iconSet",
192
+ iconSetType: format.iconSetType,
193
+ thresholds: [...format.thresholds],
194
+ ...format.reverse ? { reverse: true } : {},
195
+ ...format.showValue ? {} : { showValue: false },
196
+ ...common
197
+ });
198
+ continue;
199
+ }
200
+ if (format.kind === "top10") {
201
+ const style = mapConditionalFormatStyle(format.style, palette);
202
+ results.push({
203
+ type: "top10",
204
+ rank: format.rank,
205
+ ...format.percent ? { percent: true } : {},
206
+ ...format.bottom ? { bottom: true } : {},
207
+ ...style !== void 0 ? { style } : {},
208
+ ...common
209
+ });
210
+ continue;
211
+ }
212
+ if (format.kind === "aboveAverage") {
213
+ const style = mapConditionalFormatStyle(format.style, palette);
214
+ results.push({
215
+ type: "aboveAverage",
216
+ ...format.aboveAverage ? {} : { aboveAverage: false },
217
+ ...format.equalAverage ? { equalAverage: true } : {},
218
+ ...format.stdDev !== void 0 ? { stdDev: format.stdDev } : {},
219
+ ...style !== void 0 ? { style } : {},
220
+ ...common
221
+ });
222
+ continue;
223
+ }
224
+ if (format.kind === "timePeriod") {
225
+ const style = mapConditionalFormatStyle(format.style, palette);
226
+ results.push({
227
+ type: "timePeriod",
228
+ timePeriod: format.timePeriod,
229
+ ...style !== void 0 ? { style } : {},
230
+ ...common
231
+ });
232
+ continue;
233
+ }
234
+ const style = mapConditionalFormatStyle(format.style, palette);
189
235
  results.push({
190
- type: "iconSet",
191
- iconSetType: format.iconSetType,
192
- thresholds: [...format.thresholds],
193
- ...format.reverse ? { reverse: true } : {},
194
- ...format.showValue ? {} : { showValue: false },
236
+ type: format.kind,
237
+ ...style !== void 0 ? { style } : {},
195
238
  ...common
196
239
  });
197
240
  }
package/dist/content.js CHANGED
@@ -185,12 +185,55 @@ function mapConditionalFormats12(raw, palette) {
185
185
  });
186
186
  continue;
187
187
  }
188
+ if (format.kind === "iconSet") {
189
+ results.push({
190
+ type: "iconSet",
191
+ iconSetType: format.iconSetType,
192
+ thresholds: [...format.thresholds],
193
+ ...format.reverse ? { reverse: true } : {},
194
+ ...format.showValue ? {} : { showValue: false },
195
+ ...common
196
+ });
197
+ continue;
198
+ }
199
+ if (format.kind === "top10") {
200
+ const style = mapConditionalFormatStyle(format.style, palette);
201
+ results.push({
202
+ type: "top10",
203
+ rank: format.rank,
204
+ ...format.percent ? { percent: true } : {},
205
+ ...format.bottom ? { bottom: true } : {},
206
+ ...style !== void 0 ? { style } : {},
207
+ ...common
208
+ });
209
+ continue;
210
+ }
211
+ if (format.kind === "aboveAverage") {
212
+ const style = mapConditionalFormatStyle(format.style, palette);
213
+ results.push({
214
+ type: "aboveAverage",
215
+ ...format.aboveAverage ? {} : { aboveAverage: false },
216
+ ...format.equalAverage ? { equalAverage: true } : {},
217
+ ...format.stdDev !== void 0 ? { stdDev: format.stdDev } : {},
218
+ ...style !== void 0 ? { style } : {},
219
+ ...common
220
+ });
221
+ continue;
222
+ }
223
+ if (format.kind === "timePeriod") {
224
+ const style = mapConditionalFormatStyle(format.style, palette);
225
+ results.push({
226
+ type: "timePeriod",
227
+ timePeriod: format.timePeriod,
228
+ ...style !== void 0 ? { style } : {},
229
+ ...common
230
+ });
231
+ continue;
232
+ }
233
+ const style = mapConditionalFormatStyle(format.style, palette);
188
234
  results.push({
189
- type: "iconSet",
190
- iconSetType: format.iconSetType,
191
- thresholds: [...format.thresholds],
192
- ...format.reverse ? { reverse: true } : {},
193
- ...format.showValue ? {} : { showValue: false },
235
+ type: format.kind,
236
+ ...style !== void 0 ? { style } : {},
194
237
  ...common
195
238
  });
196
239
  }
@@ -3,6 +3,7 @@ require("../biff/record-types.cjs");
3
3
  const require_biff_records = require("../biff/records.cjs");
4
4
  const require_biff_cursor = require("../biff/cursor.cjs");
5
5
  const require_biff_ptg = require("../biff/ptg.cjs");
6
+ const require_workbook_conditional_format = require("./conditional-format.cjs");
6
7
  //#region src/workbook/conditional-format-12.ts
7
8
  const CFVO_TYPE_TO_VALUE_TYPE = /* @__PURE__ */ new Map([
8
9
  [1, "num"],
@@ -149,6 +150,69 @@ function readCfMultistate(cursor, formulaSheets) {
149
150
  showValue
150
151
  };
151
152
  }
153
+ const ICF_TEMPLATE_TOP10 = 5;
154
+ const ICF_TEMPLATE_UNIQUE_VALUES = 7;
155
+ const ICF_TEMPLATE_CONTAINS_BLANKS = 9;
156
+ const ICF_TEMPLATE_CONTAINS_NO_BLANKS = 10;
157
+ const ICF_TEMPLATE_CONTAINS_ERRORS = 11;
158
+ const ICF_TEMPLATE_CONTAINS_NO_ERRORS = 12;
159
+ const ICF_TEMPLATE_ABOVE_AVERAGE = 25;
160
+ const ICF_TEMPLATE_BELOW_AVERAGE = 26;
161
+ const ICF_TEMPLATE_DUPLICATE_VALUES = 27;
162
+ const ICF_TEMPLATE_ABOVE_OR_EQUAL_AVERAGE = 29;
163
+ const ICF_TEMPLATE_BELOW_OR_EQUAL_AVERAGE = 30;
164
+ const ICF_TEMPLATE_TO_TIME_PERIOD = /* @__PURE__ */ new Map([
165
+ [15, "today"],
166
+ [16, "tomorrow"],
167
+ [17, "yesterday"],
168
+ [18, "last7Days"],
169
+ [19, "lastMonth"],
170
+ [20, "nextMonth"],
171
+ [21, "thisWeek"],
172
+ [22, "nextWeek"],
173
+ [23, "lastWeek"],
174
+ [24, "thisMonth"]
175
+ ]);
176
+ const SIMPLE_ICF_TEMPLATE_KIND = /* @__PURE__ */ new Map([
177
+ [ICF_TEMPLATE_UNIQUE_VALUES, "uniqueValues"],
178
+ [ICF_TEMPLATE_CONTAINS_BLANKS, "containsBlanks"],
179
+ [ICF_TEMPLATE_CONTAINS_NO_BLANKS, "notContainsBlanks"],
180
+ [ICF_TEMPLATE_CONTAINS_ERRORS, "containsErrors"],
181
+ [ICF_TEMPLATE_CONTAINS_NO_ERRORS, "notContainsErrors"],
182
+ [ICF_TEMPLATE_DUPLICATE_VALUES, "duplicateValues"]
183
+ ]);
184
+ function readCfFilterRule(icfTemplate, templateParams) {
185
+ if (icfTemplate === ICF_TEMPLATE_TOP10) {
186
+ const cursor = new require_biff_cursor.BlockCursor([templateParams]);
187
+ const flags = cursor.u8();
188
+ const top = (flags & 1) !== 0;
189
+ const percent = (flags >>> 1 & 1) !== 0;
190
+ const rank = cursor.u16();
191
+ if (rank <= 0) return;
192
+ return {
193
+ kind: "top10",
194
+ rank,
195
+ percent,
196
+ bottom: !top
197
+ };
198
+ }
199
+ if (icfTemplate === ICF_TEMPLATE_ABOVE_AVERAGE || icfTemplate === ICF_TEMPLATE_BELOW_AVERAGE || icfTemplate === ICF_TEMPLATE_ABOVE_OR_EQUAL_AVERAGE || icfTemplate === ICF_TEMPLATE_BELOW_OR_EQUAL_AVERAGE) {
200
+ const iParam = new require_biff_cursor.BlockCursor([templateParams]).u16();
201
+ return {
202
+ kind: "aboveAverage",
203
+ aboveAverage: icfTemplate === ICF_TEMPLATE_ABOVE_AVERAGE || icfTemplate === ICF_TEMPLATE_ABOVE_OR_EQUAL_AVERAGE,
204
+ equalAverage: icfTemplate === ICF_TEMPLATE_ABOVE_OR_EQUAL_AVERAGE || icfTemplate === ICF_TEMPLATE_BELOW_OR_EQUAL_AVERAGE,
205
+ stdDev: iParam > 0 ? iParam : void 0
206
+ };
207
+ }
208
+ const timePeriod = ICF_TEMPLATE_TO_TIME_PERIOD.get(icfTemplate);
209
+ if (timePeriod !== void 0) return {
210
+ kind: "timePeriod",
211
+ timePeriod
212
+ };
213
+ const simpleKind = SIMPLE_ICF_TEMPLATE_KIND.get(icfTemplate);
214
+ if (simpleKind !== void 0) return { kind: simpleKind };
215
+ }
152
216
  function readCf12(record, ranges, formulaSheets) {
153
217
  try {
154
218
  const cursor = new require_biff_cursor.BlockCursor(record.blocks);
@@ -158,16 +222,16 @@ function readCf12(record, ranges, formulaSheets) {
158
222
  const cce1 = cursor.u16();
159
223
  const cce2 = cursor.u16();
160
224
  const cbDxf = cursor.u32();
161
- cursor.skip(cbDxf);
225
+ const dxfBytes = cursor.take(cbDxf);
162
226
  cursor.skip(cce1);
163
227
  cursor.skip(cce2);
164
228
  const cceActive = cursor.u16();
165
229
  cursor.skip(cceActive);
166
230
  const stopIfTrue = (cursor.u8() & 2) !== 0;
167
231
  const priority = cursor.u16();
168
- cursor.skip(2);
232
+ const icfTemplate = cursor.u16();
169
233
  cursor.skip(1);
170
- cursor.skip(16);
234
+ const templateParams = cursor.take(16);
171
235
  const common = {
172
236
  priority,
173
237
  stopIfTrue,
@@ -197,6 +261,18 @@ function readCf12(record, ranges, formulaSheets) {
197
261
  ...common
198
262
  };
199
263
  }
264
+ if (ct === 5) {
265
+ const rule = readCfFilterRule(icfTemplate, templateParams);
266
+ const cbFilter = cursor.u16();
267
+ cursor.skip(cbFilter);
268
+ if (rule === void 0) return;
269
+ const style = require_workbook_conditional_format.parseDxfStyle(dxfBytes);
270
+ return {
271
+ ...rule,
272
+ ...common,
273
+ style
274
+ };
275
+ }
200
276
  return;
201
277
  } catch (err) {
202
278
  if (!(err instanceof require_biff_records.BiffFormatError)) throw err;
@@ -1,2 +1,2 @@
1
- import { a as RawDataBarFormat, i as RawConditionalFormat12, n as RawCfColor, o as RawIconSetFormat, r as RawColorScaleFormat, s as readCondFmt12Group, t as CondFmt12GroupResult } from "../conditional-format-12-DdEL5DL0.cjs";
2
- export { CondFmt12GroupResult, RawCfColor, RawColorScaleFormat, RawConditionalFormat12, RawDataBarFormat, RawIconSetFormat, readCondFmt12Group };
1
+ import { a as RawConditionalFormat12, c as RawSimpleFilterFormat, d as RawTop10Format, f as readCondFmt12Group, i as RawColorScaleFormat, l as RawTimePeriod, n as RawAboveAverageFormat, o as RawDataBarFormat, r as RawCfColor, s as RawIconSetFormat, t as CondFmt12GroupResult, u as RawTimePeriodFormat } from "../conditional-format-12-DPNiYLVp.cjs";
2
+ export { CondFmt12GroupResult, RawAboveAverageFormat, RawCfColor, RawColorScaleFormat, RawConditionalFormat12, RawDataBarFormat, RawIconSetFormat, RawSimpleFilterFormat, RawTimePeriod, RawTimePeriodFormat, RawTop10Format, readCondFmt12Group };
@@ -1,2 +1,2 @@
1
- import { a as RawDataBarFormat, i as RawConditionalFormat12, n as RawCfColor, o as RawIconSetFormat, r as RawColorScaleFormat, s as readCondFmt12Group, t as CondFmt12GroupResult } from "../conditional-format-12-Dw82ECRh.js";
2
- export { CondFmt12GroupResult, RawCfColor, RawColorScaleFormat, RawConditionalFormat12, RawDataBarFormat, RawIconSetFormat, readCondFmt12Group };
1
+ import { a as RawConditionalFormat12, c as RawSimpleFilterFormat, d as RawTop10Format, f as readCondFmt12Group, i as RawColorScaleFormat, l as RawTimePeriod, n as RawAboveAverageFormat, o as RawDataBarFormat, r as RawCfColor, s as RawIconSetFormat, t as CondFmt12GroupResult, u as RawTimePeriodFormat } from "../conditional-format-12-DyqonR1G.js";
2
+ export { CondFmt12GroupResult, RawAboveAverageFormat, RawCfColor, RawColorScaleFormat, RawConditionalFormat12, RawDataBarFormat, RawIconSetFormat, RawSimpleFilterFormat, RawTimePeriod, RawTimePeriodFormat, RawTop10Format, readCondFmt12Group };
@@ -2,6 +2,7 @@ import "../biff/record-types.js";
2
2
  import { BiffFormatError } from "../biff/records.js";
3
3
  import { BlockCursor } from "../biff/cursor.js";
4
4
  import { parseFormulaText } from "../biff/ptg.js";
5
+ import { parseDxfStyle } from "./conditional-format.js";
5
6
  //#region src/workbook/conditional-format-12.ts
6
7
  const CFVO_TYPE_TO_VALUE_TYPE = /* @__PURE__ */ new Map([
7
8
  [1, "num"],
@@ -148,6 +149,69 @@ function readCfMultistate(cursor, formulaSheets) {
148
149
  showValue
149
150
  };
150
151
  }
152
+ const ICF_TEMPLATE_TOP10 = 5;
153
+ const ICF_TEMPLATE_UNIQUE_VALUES = 7;
154
+ const ICF_TEMPLATE_CONTAINS_BLANKS = 9;
155
+ const ICF_TEMPLATE_CONTAINS_NO_BLANKS = 10;
156
+ const ICF_TEMPLATE_CONTAINS_ERRORS = 11;
157
+ const ICF_TEMPLATE_CONTAINS_NO_ERRORS = 12;
158
+ const ICF_TEMPLATE_ABOVE_AVERAGE = 25;
159
+ const ICF_TEMPLATE_BELOW_AVERAGE = 26;
160
+ const ICF_TEMPLATE_DUPLICATE_VALUES = 27;
161
+ const ICF_TEMPLATE_ABOVE_OR_EQUAL_AVERAGE = 29;
162
+ const ICF_TEMPLATE_BELOW_OR_EQUAL_AVERAGE = 30;
163
+ const ICF_TEMPLATE_TO_TIME_PERIOD = /* @__PURE__ */ new Map([
164
+ [15, "today"],
165
+ [16, "tomorrow"],
166
+ [17, "yesterday"],
167
+ [18, "last7Days"],
168
+ [19, "lastMonth"],
169
+ [20, "nextMonth"],
170
+ [21, "thisWeek"],
171
+ [22, "nextWeek"],
172
+ [23, "lastWeek"],
173
+ [24, "thisMonth"]
174
+ ]);
175
+ const SIMPLE_ICF_TEMPLATE_KIND = /* @__PURE__ */ new Map([
176
+ [ICF_TEMPLATE_UNIQUE_VALUES, "uniqueValues"],
177
+ [ICF_TEMPLATE_CONTAINS_BLANKS, "containsBlanks"],
178
+ [ICF_TEMPLATE_CONTAINS_NO_BLANKS, "notContainsBlanks"],
179
+ [ICF_TEMPLATE_CONTAINS_ERRORS, "containsErrors"],
180
+ [ICF_TEMPLATE_CONTAINS_NO_ERRORS, "notContainsErrors"],
181
+ [ICF_TEMPLATE_DUPLICATE_VALUES, "duplicateValues"]
182
+ ]);
183
+ function readCfFilterRule(icfTemplate, templateParams) {
184
+ if (icfTemplate === ICF_TEMPLATE_TOP10) {
185
+ const cursor = new BlockCursor([templateParams]);
186
+ const flags = cursor.u8();
187
+ const top = (flags & 1) !== 0;
188
+ const percent = (flags >>> 1 & 1) !== 0;
189
+ const rank = cursor.u16();
190
+ if (rank <= 0) return;
191
+ return {
192
+ kind: "top10",
193
+ rank,
194
+ percent,
195
+ bottom: !top
196
+ };
197
+ }
198
+ if (icfTemplate === ICF_TEMPLATE_ABOVE_AVERAGE || icfTemplate === ICF_TEMPLATE_BELOW_AVERAGE || icfTemplate === ICF_TEMPLATE_ABOVE_OR_EQUAL_AVERAGE || icfTemplate === ICF_TEMPLATE_BELOW_OR_EQUAL_AVERAGE) {
199
+ const iParam = new BlockCursor([templateParams]).u16();
200
+ return {
201
+ kind: "aboveAverage",
202
+ aboveAverage: icfTemplate === ICF_TEMPLATE_ABOVE_AVERAGE || icfTemplate === ICF_TEMPLATE_ABOVE_OR_EQUAL_AVERAGE,
203
+ equalAverage: icfTemplate === ICF_TEMPLATE_ABOVE_OR_EQUAL_AVERAGE || icfTemplate === ICF_TEMPLATE_BELOW_OR_EQUAL_AVERAGE,
204
+ stdDev: iParam > 0 ? iParam : void 0
205
+ };
206
+ }
207
+ const timePeriod = ICF_TEMPLATE_TO_TIME_PERIOD.get(icfTemplate);
208
+ if (timePeriod !== void 0) return {
209
+ kind: "timePeriod",
210
+ timePeriod
211
+ };
212
+ const simpleKind = SIMPLE_ICF_TEMPLATE_KIND.get(icfTemplate);
213
+ if (simpleKind !== void 0) return { kind: simpleKind };
214
+ }
151
215
  function readCf12(record, ranges, formulaSheets) {
152
216
  try {
153
217
  const cursor = new BlockCursor(record.blocks);
@@ -157,16 +221,16 @@ function readCf12(record, ranges, formulaSheets) {
157
221
  const cce1 = cursor.u16();
158
222
  const cce2 = cursor.u16();
159
223
  const cbDxf = cursor.u32();
160
- cursor.skip(cbDxf);
224
+ const dxfBytes = cursor.take(cbDxf);
161
225
  cursor.skip(cce1);
162
226
  cursor.skip(cce2);
163
227
  const cceActive = cursor.u16();
164
228
  cursor.skip(cceActive);
165
229
  const stopIfTrue = (cursor.u8() & 2) !== 0;
166
230
  const priority = cursor.u16();
167
- cursor.skip(2);
231
+ const icfTemplate = cursor.u16();
168
232
  cursor.skip(1);
169
- cursor.skip(16);
233
+ const templateParams = cursor.take(16);
170
234
  const common = {
171
235
  priority,
172
236
  stopIfTrue,
@@ -196,6 +260,18 @@ function readCf12(record, ranges, formulaSheets) {
196
260
  ...common
197
261
  };
198
262
  }
263
+ if (ct === 5) {
264
+ const rule = readCfFilterRule(icfTemplate, templateParams);
265
+ const cbFilter = cursor.u16();
266
+ cursor.skip(cbFilter);
267
+ if (rule === void 0) return;
268
+ const style = parseDxfStyle(dxfBytes);
269
+ return {
270
+ ...rule,
271
+ ...common,
272
+ style
273
+ };
274
+ }
199
275
  return;
200
276
  } catch (err) {
201
277
  if (!(err instanceof BiffFormatError)) throw err;
@@ -138,4 +138,5 @@ function readCondFmtGroup(records, startIndex, formulaSheets) {
138
138
  }
139
139
  }
140
140
  //#endregion
141
+ exports.parseDxfStyle = parseDxfStyle;
141
142
  exports.readCondFmtGroup = readCondFmtGroup;
@@ -1,2 +1,2 @@
1
- import { a as readCondFmtGroup, i as RawConditionalFormatStyle, n as RawConditionalFormat, r as RawConditionalFormatFill, t as CondFmtGroupResult } from "../conditional-format-DUQuOrNW.cjs";
2
- export { CondFmtGroupResult, RawConditionalFormat, RawConditionalFormatFill, RawConditionalFormatStyle, readCondFmtGroup };
1
+ import { a as parseDxfStyle, i as RawConditionalFormatStyle, n as RawConditionalFormat, o as readCondFmtGroup, r as RawConditionalFormatFill, t as CondFmtGroupResult } from "../conditional-format-S9NaPjhr.cjs";
2
+ export { CondFmtGroupResult, RawConditionalFormat, RawConditionalFormatFill, RawConditionalFormatStyle, parseDxfStyle, readCondFmtGroup };
@@ -1,2 +1,2 @@
1
- import { a as readCondFmtGroup, i as RawConditionalFormatStyle, n as RawConditionalFormat, r as RawConditionalFormatFill, t as CondFmtGroupResult } from "../conditional-format-BV2313jL.js";
2
- export { CondFmtGroupResult, RawConditionalFormat, RawConditionalFormatFill, RawConditionalFormatStyle, readCondFmtGroup };
1
+ import { a as parseDxfStyle, i as RawConditionalFormatStyle, n as RawConditionalFormat, o as readCondFmtGroup, r as RawConditionalFormatFill, t as CondFmtGroupResult } from "../conditional-format-CWlz0Ty6.js";
2
+ export { CondFmtGroupResult, RawConditionalFormat, RawConditionalFormatFill, RawConditionalFormatStyle, parseDxfStyle, readCondFmtGroup };
@@ -137,4 +137,4 @@ function readCondFmtGroup(records, startIndex, formulaSheets) {
137
137
  }
138
138
  }
139
139
  //#endregion
140
- export { readCondFmtGroup };
140
+ export { parseDxfStyle, readCondFmtGroup };
@@ -1,8 +1,8 @@
1
1
  import { n as SetupFields } from "../print-setup-B_ihDvm5.cjs";
2
2
  import { r as FormulaSheetContext } from "../ptg-CCBbJLZJ.cjs";
3
3
  import { t as RecordGroup } from "../substreams-Cpy5Fi3d.cjs";
4
- import { n as RawConditionalFormat } from "../conditional-format-DUQuOrNW.cjs";
5
- import { i as RawConditionalFormat12 } from "../conditional-format-12-DdEL5DL0.cjs";
4
+ import { n as RawConditionalFormat } from "../conditional-format-S9NaPjhr.cjs";
5
+ import { a as RawConditionalFormat12 } from "../conditional-format-12-DPNiYLVp.cjs";
6
6
  import { t as RawDataValidation } from "../data-validation-DZPzAW-8.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. */
@@ -1,8 +1,8 @@
1
1
  import { n as SetupFields } from "../print-setup-B_ihDvm5.js";
2
2
  import { r as FormulaSheetContext } from "../ptg-CCBbJLZJ.js";
3
3
  import { t as RecordGroup } from "../substreams-CmyZMtuM.js";
4
- import { n as RawConditionalFormat } from "../conditional-format-BV2313jL.js";
5
- import { i as RawConditionalFormat12 } from "../conditional-format-12-Dw82ECRh.js";
4
+ import { n as RawConditionalFormat } from "../conditional-format-CWlz0Ty6.js";
5
+ import { a as RawConditionalFormat12 } from "../conditional-format-12-DyqonR1G.js";
6
6
  import { t as RawDataValidation } from "../data-validation-DsVG0Mms.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. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xls-codec",
3
- "version": "4.5.0",
3
+ "version": "4.6.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": {