Skip to content

Implement conditional formatting#628

Open
ddimaria wants to merge 2 commits into
tafia:masterfrom
ddimaria:implement-conditional-formatting
Open

Implement conditional formatting#628
ddimaria wants to merge 2 commits into
tafia:masterfrom
ddimaria:implement-conditional-formatting

Conversation

@ddimaria

@ddimaria ddimaria commented Mar 23, 2026

Copy link
Copy Markdown

Add conditional formatting support for XLSX

Adds read support for conditional formatting rules from XLSX worksheets. This parses <conditionalFormatting> elements and their child <cfRule> nodes, resolving differential format styles (dxf) into the existing Style type.

Branched from #653, so this should be reviewed after 653 is merged into main.

Supported rule types

Rule type Variant
Cell value comparison CellIs (all operators: equal, between, greaterThan, etc.)
Color scales ColorScale2, ColorScale3
Data bars DataBar (with fill and border colors)
Icon sets IconSet (all 20 built-in icon set types)
Top/Bottom N Top10 (rank, percent, bottom)
Above/below average AboveAverage (with std deviation support)
Text rules Text (contains, notContains, beginsWith, endsWith)
Time period TimePeriod (yesterday, today, last7Days, etc.)
Formula-based Expression
Blanks/errors ContainsBlanks, NotContainsBlanks, ContainsErrors, NotContainsErrors
Duplicates/uniques DuplicateValues, UniqueValues
Fallback Unknown (preserves raw type string)

API

Two new public methods on Xlsx<RS>:

  • worksheet_conditional_formatting(&mut self, name: &str) -- by sheet name
  • worksheet_conditional_formatting_at(&mut self, index: usize) -- by sheet index

Both return Vec<ConditionalFormatting>, where each block contains the target sqref range and a list of prioritized rules with resolved Style formats.

Changes

  • src/conditional_format.rs (new): Public data model -- ConditionalFormatting, ConditionalFormatRule, ConditionalFormatRuleType, and supporting enums (CfOperator, CfTextOperator, CfValueObject, IconSetType, TimePeriodType, etc.)
  • src/xlsx/mod.rs: Parses <dxfs> from styles.xml into differential format styles; adds parse_conditional_formattings and helpers (parse_cf_rules, parse_single_cf_rule, parse_color_scale, parse_data_bar, parse_icon_set, parse_cfvo) to walk the worksheet XML
  • src/lib.rs: Re-exports all new public types
  • tests/test.rs: 29 new tests covering every rule type variant, multi-rule blocks, stop_if_true, reversed icon sets, bottom-percent, std-dev below-average, unknown types, and dxf style resolution
  • tests/conditional_formatting.xlsx (new): Test fixture with 25 conditional formatting blocks

Example & Benchmarks

  • examples/conditional_formatting.rs: Demonstrates reading and printing all conditional formatting rules from a workbook
  • benches/conditional_formatting.rs: Criterion benchmarks for three scenarios:
    • cf_small_25_blocks -- real-world file with 25 CF blocks
    • cf_large_200_blocks_1000_rules -- synthetic file with 200 blocks / 1,000 rules
    • cf_large_with_dxf_access -- large file with dxf style resolution

Run with:

cargo bench --bench conditional_formatting

Test plan

  • All 29 new conditional formatting tests pass
  • Existing test suite unaffected
  • Example runs against test fixture: cargo run --example conditional_formatting
  • Benchmarks compile and run: cargo bench --bench conditional_formatting

@jmcnamara jmcnamara added awaiting user changes Awaiting changes to a PR to fix requested changes or CI issues. needs work for merge The PR needs some rework or clarification. No suitable for merge, yet. labels May 10, 2026
@jmcnamara

Copy link
Copy Markdown
Collaborator

PR #538 will have to be resolved before this can be reviewed/merged.

ddimaria and others added 2 commits May 18, 2026 17:16
…eet layout

Add style support for Calamine xlsx files.

Public API:
- `Xlsx::worksheet_style(sheet)` returns a row x col grid of cell styles
  using run-length encoding for memory efficiency on large workbooks.
- `Xlsx::worksheet_layout(sheet)` returns column widths and row heights.

Style types (in `src/style.rs`):
- `Style` with optional Font / Fill / Borders / Alignment / NumberFormat
  / Protection.
- `Color` with theme + tint resolution and indexed-color fallback.
- `RichText` / `TextRun` for cells with mixed inline formatting.
- `StyleRange` with RLE storage and a `cells()` iterator.

Parser in `src/xlsx/style_parser.rs` handles fonts (bold / italic /
underline / strikethrough / sz / color), fills, borders (with color and
style per side), number formats (built-in + custom format codes),
alignment (horizontal / vertical / wrap / indent / shrink / text
rotation incl. stacked), protection (locked default per OOXML), theme
colors with tint, and sysClr lastClr fallback.

Shared-string reader now decodes rich text runs and preserves their
formatting, while also handling plain text that precedes rich runs
(consistent with upstream PR tafia#637).

Includes benchmarks in `benches/style.rs` and test fixtures
(styles.xlsx, borders.xlsx, EMSI_JobChange_UK.xlsx,
problematic_formats.xlsx, styles_1M.xlsx) covering the various code
paths.

Co-authored-by: Cursor <cursoragent@cursor.com>
Add conditional formatting parsing for xlsx files. Builds on the
styles work added in the preceding commit.

Public API:

```rust
// vector of conditional formatting blocks for the named sheet
let cfs = xlsx.worksheet_conditional_formatting("Sheet1")?;
for cf in &cfs {
    println!("range {}: {} rule(s)", cf.sqref, cf.rules.len());
    for rule in &cf.rules {
        match &rule.rule_type {
            ConditionalFormatRuleType::CellIs { operator, formulas } => { /* ... */ }
            ConditionalFormatRuleType::ColorScale3 { /* ... */ } => { /* ... */ }
            /* ... */
        }
    }
}
```

Supported rule types (`src/conditional_format.rs`):

- `CellIs` (greaterThan, lessThan, between, equal, etc.)
- `ColorScale2` and `ColorScale3`
- `DataBar` (with fill and optional border color)
- `IconSet` (3TrafficLights, 5Arrows, ..., with `reversed` / `showValue`)
- `Top10` (top / bottom, percent / count)
- `AboveAverage` (above / below, equal-average, stdDev)
- `Text` (contains, notContains, beginsWith, endsWith)
- `ContainsBlanks` / `NotContainsBlanks`
- `ContainsErrors` / `NotContainsErrors`
- `DuplicateValues` / `UniqueValues`
- `Expression`
- `TimePeriod`
- `Unknown` (forwards the raw type for unrecognised rules)

Each rule carries `priority`, `stop_if_true`, the optional resolved
`Format` (looked up from the `<dxfs>` table), and any per-rule formula
strings.

Includes a criterion benchmark (`benches/conditional_formatting.rs`),
an example (`examples/conditional_formatting.rs`), and a fixture
covering all 25 supported rule variants (`tests/conditional_formatting.xlsx`).

Co-authored-by: Cursor <cursoragent@cursor.com>
@ddimaria ddimaria force-pushed the implement-conditional-formatting branch from 8ba7b7d to b2ac03f Compare May 18, 2026 23:18
@jmcnamara

Copy link
Copy Markdown
Collaborator

@ddimaria just to give you a heads up that I will get to this and merge it in the next release +1. Can you keep it up to date with the current main branch and resolve any conflicts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting user changes Awaiting changes to a PR to fix requested changes or CI issues. needs work for merge The PR needs some rework or clarification. No suitable for merge, yet.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants