fix(xlsx): keep currency labels from number formats - #2538
Islam El-Nashar (aslamalkarywk7) wants to merge 4 commits into
Conversation
Sylvester Kaczmarek (sylvesterkaczmarek)
left a comment
There was a problem hiding this comment.
Both helpers always inspect the first semicolon section, then apply that currency to every numeric cell. Negative values use the second Excel format section (zero can use the third), so a format like "$"#,##0;"€"#,##0 will render a negative value with $. The selected section needs to depend on the cell value, or section-specific currencies should be left alone.
|
@microsoft-github-policy-service agree |
Negative values use the second Excel format section (zero can use the third), so inspect the section matching the cell sign instead of always using the first section.
|
Thanks for pointing this out. I’ve fixed the issue by selecting the Excel number-format section based on the cell value:
This ensures that section-specific currencies are preserved correctly, for example: "$"#,##0;"€"#,##0 will render positive values with |
Sylvester Kaczmarek (sylvesterkaczmarek)
left a comment
There was a problem hiding this comment.
Rechecked da00881. Currency extraction and prefix placement now use the number-format section selected from the cell value, so positive, negative and zero values no longer all inherit the first section's currency. The regression covers the exact $/€ split I raised plus a third zero section. My concern is resolved.
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Currency detection has parsing gaps, one assertion is ineffective, and Black formatting will fail CI.
Get a fresh assessment by requesting another Copilot review.
Review effort: Balanced
Findings: 4
Open (4)
What changed in this PR
Adds currency labels from XLSX number formats to converted Markdown.
Changes:
- Detects currency symbols and their placement.
- Overlays labels onto pandas cell values.
- Adds currency conversion tests.
| File | Description |
|---|---|
_xlsx_converter.py |
Adds currency-format parsing and label overlay. |
test_xlsx_currency.py |
Tests prefix, suffix, plain, and sectioned formats. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| # Currency symbols that may appear in Excel number formats, either as quoted | ||
| # literals (e.g. '"$"#,##0.00') or locale blocks (e.g. '[$€-x-euro2]'). | ||
| # See https://github.com/microsoft/markitdown/issues/53. | ||
| _CURRENCY_SYMBOL_RE = re.compile(r'[$€£¥₹₽₩₪₺₴₸₫₦¤]') |
| Excel uses: 1 section = all numbers, 2 sections = positive+zero / negative, | ||
| 3 sections = positive / negative / zero (4th section is text and ignored). | ||
| """ | ||
| parts = number_format.split(";") |
| def _overlay_currency_labels( | ||
| sheets: dict[str, Any], workbook_stream: BinaryIO | ||
| ) -> None: | ||
| """Rewrite currency-formatted numeric cells with their display label. |
| markdown = _convert(stream.getvalue()) | ||
| assert "$10" in markdown | ||
| assert "€" in markdown | ||
| assert "$-5" not in markdown.replace("$-5", "") or "€" in markdown |
|
Thanks for working on this. It seems there is some additional complexity to this problem that I need to think about. I had Astra review this PR and it suggested the following, which I am now confirming by hand:
However, there are several cases where the implementation introduces incorrect currency information. I’m requesting changes before merging.
There are also several smaller correctness and test-coverage gaps:
|

Closes #53
pandas.read_excel returns raw values and drops Excel number formats, so currency-formatted cells lost their label (1199 instead of ). XlsxConverter now overlays the currency symbol from each cell's number_format (quoted literals and locale blocks like [], prefix or suffix by format order) onto the values before rendering, without touching the numbers themselves.
Verification: new tests/test_xlsx_currency.py (3 tests: $ prefix, untouched plain cells, EUR suffix) pass; existing xlsx image tests unaffected (22 pass; 1 pre-existing env failure for missing xlrd also fails on main); full module-vectors failure count identical before/after (41 pre-existing env failures from missing optional deps).