Skip to content

fix(xlsx): honor icon-set cfvo thresholds and their types#407

Merged
developer0hye merged 2 commits into
mainfrom
fix/xlsx-icon-bands
Jul 22, 2026
Merged

fix(xlsx): honor icon-set cfvo thresholds and their types#407
developer0hye merged 2 commits into
mainfrom
fix/xlsx-icon-bands

Conversation

@developer0hye

Copy link
Copy Markdown
Owner

Summary

Icon-set conditional formatting assigned most cells the wrong band — values landed one band too low. On 10_kpi_tracker_en.xlsx the 3Arrows rule uses type="num" thresholds [0, 0.9, 1.0], but 107% rendered with the middle (yellow) icon and 93% with the low (red) icon instead of green-up / yellow-side (issue #406).

Two root causes:

  1. umya-spreadsheet drops the cfvo children. The <cfvo> elements are written as start/end tag pairs (openpyxl style); umya's IconSet reader only matches Event::Empty, so get_cfvo_collection() came back empty and office2pdf fell back to equal-thirds thresholds (the same reader gap fixed for DataBar in fix: read dataBar cfvo and color children written as start/end tags MathNya/umya-spreadsheet#347).
  2. office2pdf ignored the cfvo type. Every threshold was computed as min + range·(val/100), treating the value as a percentage even for type="num".

The iconSet cfvo (type, val) pairs are now parsed from the raw worksheet XML (where office2pdf already reads the icon-set type) and each threshold is resolved per its type: num literal, percent a fraction of the range, percentile interpolated over the values, min/max the extremes. umya's typeless cfvos remain a legacy fallback, and equal-thirds the last resort.

Verified on the KPI fixture: every cell's band now matches Excel (107% green-up, 93% yellow-side, 113% green-up, 84% red-down, 103%/130% green-up).

Related issue

Fixes #406

Testing

  • cargo test --workspace — green
  • Local bulk regression gate — PASS
  • New raw-hint tests worksheet_hints_collect_icon_set_cfvo_thresholds, worksheet_hints_do_not_confuse_databar_cfvo_with_icon_set
  • New integration test test_cond_fmt_icon_set_num_thresholds_from_start_end_cfvos (rewrites cfvos into start/end tags, asserts the per-band colors)
  • Converted 10_kpi_tracker_en.xlsx: bands match Excel

Visual impact

  • No rendered PDF change
  • Rendered PDF change or visual evidence added
  • Reason:

Visual audit

Visual comparison

GT Before After
GT Before After

Required inspection

  • Rendered all evidence at 150 DPI or higher
  • Stored progressive JPEG quality 86 assets with metadata stripped
  • Inspected matched region crops at full resolution
  • Ran the 5% fuzz pixel-difference sweep
  • Inventoried hairlines and border dash styles
  • Inventoried font weight, italic, and underline emphasis

Deviation audit

Check Result
Page count/order Matches GT
Element presence Matches GT
Position/size Matches GT
Rotation/flip No deviation observed
Fill Matches GT
Stroke/border Matches GT
Text content Matches GT
Font family/weight/style Matches GT
Text color Fixed (icon band color now matches Excel for every cell)
Alignment Matches GT
Line/paragraph spacing Matches GT
Clipping/overflow Matches GT
Icon glyph weight Remaining: #377 (thin arrows vs Excel's solid icons)

Checklist

  • Commits include a Signed-off-by line
  • PR scope contains one root cause
  • Remaining visual deviations each reference an open issue

🤖 Generated with Claude Code

developer0hye and others added 2 commits July 22, 2026 13:12
Icon-set conditional formatting assigned most cells the wrong band. Two
causes (issue #406):

1. umya-spreadsheet drops iconSet <cfvo> children written as start/end
   tag pairs (the Event::Start reader gap already fixed for dataBar), so
   the threshold collection came back empty and office2pdf fell back to
   equal-thirds bands.
2. office2pdf computed every threshold as min + range*(val/100),
   treating the cfvo value as a percentage even for type="num".

Parse the iconSet cfvo (type, val) pairs from the raw worksheet XML —
which office2pdf already reads for the icon-set type — and compute each
threshold per its cfvo type: num is literal, percent is a fraction of
the range, percentile interpolates the values, min/max are the extremes.
umya's typeless cfvos remain a legacy fallback.

Verified on the KPI fixture: bands now match Excel (107% green-up, 93%
yellow-side, 84% red-down). Remaining glyph weight is tracked in #377.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Yonghye Kwon <developer.0hye@gmail.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Yonghye Kwon <developer.0hye@gmail.com>
@developer0hye
developer0hye merged commit 049c5fc into main Jul 22, 2026
15 checks passed
@developer0hye
developer0hye deleted the fix/xlsx-icon-bands branch July 22, 2026 04:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

XLSX: icon set assigns wrong bands (cfvo thresholds dropped and type ignored)

1 participant