Commit Graph
345 Commits
Author SHA1 Message Date
Marco Arnulfo efc8171609 Keyboard: Ctrl+6 hides and shows objects, saved as workbookPr showObjects 2026-10-16 07:03:58 +02:00
Marco Arnulfo e8c6fcb8b0 XLSX: keep custom column widths that round to the default width (fixes #1607) 2026-10-16 05:11:07 +02:00
Marco Arnulfo 706127402a XLSX: keep header and footer pictures on save (part of #1257) 2026-10-16 02:48:25 +02:00
NetterSilver 47185eef1f XLSX export: a picture shared by several sheets is one media part (#985)
The writer gave every drawing its own xl/media/imageN part even when the pictures
had the same bytes, so a workbook with one logo on 16 sheets grew 16 copies of
it on a plain open and save. Out::image_part now returns the existing part when
an identical picture was already written (same extension and bytes), and every
drawing's relationship points to it, as Excel stores a copied picture.
2026-10-15 20:17:27 +00:00
Nihilist NarwhalandClaude Opus 5.5 82ee02aac4 Formula parser: Excel's 8,192-character limit and postfix nesting (never crash) (#1330)
A hostile file could abort any process that opens it: a cell formula of a
million `%` (`1%%%…`) or a 200,000-term sum parsed into trees deep enough to
overflow the stack when printed, saved, evaluated or dropped, even in release
builds. Postfix operators (`%`, invoking a result) built their nesting in a
loop that the 256-level limit did not see.

- `parse` refuses formula text longer than Excel's 8,192 characters
  (`MAX_FORMULA_LEN`, counted in characters, the leading `=` excluded). This
  also bounds the depth of the left-deep trees long operator chains build.
- Each postfix `%` and each call of a result counts towards the nesting limit.
- A file's formula that is refused keeps its text and cached value, as other
  unparsable formulas do.

Regression tests: formula (small-stack thread: long `%`, invoke and operator
chains are errors; the limit counts characters, not bytes) and xlsx
(`hostile_formula_text_does_not_overflow_the_stack`: open, save and drop a
workbook with such formulas and a defined name on a 1 MiB stack). Both abort
with a stack overflow without the fix.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 13:39:28 -06:00
midasmatterhorn 7deb382f3d xlsx: keep a table's filter criteria when the sheet AutoFilter coincides with it
The engine's live filter state is always sheet.autofilter — apply_filter,
filter_values, clear_filter, reapply and the slicer command all read and
write it, and a table's header dropdown installs one over the table's own
range — while Table::filter is only ever filled by the reader. Writing
suppressed the sheet <autoFilter> whenever any table merely intersected
it and wrote the table part from Table::filter, which was empty: a
filtered table saved with its rows still hidden and no criteria anywhere,
so on reopen there was no filter to clear and Excel showed an inactive
filter over mysteriously hidden rows. A slicer's selection was lost the
same way.

Fold the coinciding sheet filter's criteria into the table part (colId
offsets are relative to the range's first column, and both ranges start
at the same column, so they carry over verbatim); a filter that only
partially covers a table still has nowhere valid to go and keeps being
suppressed.

Regression test: a_table_filter_criteria_survive_a_round_trip — on main
the table part carries no criteria after a save; with the fold they round
trip, the hidden row stays, and the sheet keeps no duplicate autoFilter.
2026-10-15 17:22:31 +03:00
NetterSilverandClaude Haiku 5.5 67be35564c Keep leading empty columns and rows when saving CSV/text
Found while testing crates/xlsx/src/csv.rs:204 (write_csv_limited, starts at sheet.used_range().start instead of A1)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 16:01:47 +02:00
justinjohn0306 b930d60bc3 #1505: keep scalar INDEX MATCH formulas scalar 2026-10-15 18:32:00 +05:30
adrdsouza 7f9f73d6ba fix(numfmt): share legacy colors across grid and PDF rendering 2026-10-15 16:10:07 +05:30
derechopiano 9097170b08 XLSX: preserve carriage returns in saved text 2026-10-15 10:33:57 +01:00
Harrison BoothandClaude Opus 5.5 7ee11fe137 XLSX: keep dynamic-array cell metadata (cm) on save
A formula read with `cm` (Excel's XLDAPR dynamic-array record in
xl/metadata.xml) now carries `Formula::dynamic`, and the writer keeps the
`cm` and the metadata part for it even when the formula returns one value
and the export shape analysis would not need array evaluation. Before, such
formulas were saved as legacy formulas, so Excel recalculated some of them
with implicit intersection (SUMPRODUCT(IFERROR(range/range,0)) changed
value), and workbooks whose every dynamic formula was scalar lost
xl/metadata.xml altogether.

The mark survives moves, row and column insertion or deletion, sheet and
name renames and LAMBDA call-case fixes; retyping a formula drops it and
the writer decides from the formula's shape, as before. It is serialized
only when set, so saved documents without it are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 19:05:03 -05:00
midasmatterhorn d66ed11253 xlsx: keep every property of the default cell format when importing a file 2026-10-14 23:58:57 +03:00
Marco Arnulfo 736cc89b2b Opening an XLSX with a part over 512 MB says how to get the data in (fixes #1242) 2026-10-14 07:44:41 +02:00
Marco Arnulfo f519fd67f6 XLSX: keep a dxf's protection, font size and face, and the font parts it turns off (part of #1209) 2026-10-14 04:49:33 +02:00
vangel76andClaude Sonnet 5.5 48696f9bcf XLSX: [Content_Types].xml is the first zip entry, workbook parts precede document properties (#1090)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 21:44:20 +02:00
vangel76andClaude Sonnet 5.5 568dd58264 Charts: a huge trendline forecast no longer collapses the chart (#1148)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 21:13:05 +02:00
MarekSpodBiedry a091fd9b50 CSV export: check the size cap before each field, not after the row (#1023) 2026-10-13 18:05:34 +02:00
MarekSpodBiedry 8939e7817f Fix xlsx: combine escaped UTF-16 surrogate pairs when decoding strings (#1025) 2026-10-13 18:04:09 +02:00
MarekSpodBiedry 01032e9010 Cap CSV import fields per record at the worksheet column limit (#1037) 2026-10-13 18:03:54 +02:00
MarekSpodBiedry 29c38887de Keep chart cache point positions when a reference has blank edges (#1015) 2026-10-13 18:03:44 +02:00
MarekSpodBiedry 08902d925a xlsx: keep the first group's series kind when importing a combination chart (#1014) 2026-10-13 18:03:35 +02:00
Lexx143andClaude Fable 5.1 209d2447d9 Charts: multi-level category axes, read, drawn and written back
A category range two or more columns wide beside a column of values (year beside month, Excel's
multiLvlStrRef) was read as one flat list: 36 rows in two columns became 72 categories, the
series sat in the left half of the plot and the years and months shared one row of labels.
Saving wrote the range as a plain strRef, so Excel lost the two-level axis too.

- gridcraft_core::category_levels: the shape of a category range beside its values (levels are
  columns when the points run down the rows, rows when they run along the columns; up to three
  outer levels), shared by the chart and xlsx crates.
- ChartData.category_levels: the outer levels, nearest the labels first, one entry per category,
  non-empty where a group starts. resolve fills it; a single column is unchanged.
- render: the outer levels in rows under the labels, a group's name centred under its categories
  with a separator at each boundary; in bar charts in columns beside the labels.
- xlsx: <c:cat> written as multiLvlStrRef with the level caches (innermost first), so the axis
  survives a round trip.

Tests in core (shapes), chart (resolve and layout for columns, bars and a row of values) and
xlsx (written XML, read-back, stability, a single column stays strRef). chart-parity, gaps § 4
and the ROADMAP progress log updated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-13 17:30:01 +03:00
Brandon Thomas be003d44af Merge main into #863
# Conflicts:
#	crates/xlsx/src/tests/passthrough_tests.rs
2026-10-13 01:40:22 -04:00
Brandon ThomasandClaude Opus 5.5 8108dbccc6 Fix-up: close the passthrough tests the merges left open; rustfmt
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 01:21:57 -04:00
Brandon Thomas 0c488f79b3 Merge main into #809
# Conflicts:
#	ROADMAP.md
#	crates/numfmt/src/lib.rs
#	crates/numfmt/src/tests.rs
#	crates/xlsx/src/tests/malformed.rs
2026-10-13 01:04:29 -04:00
Brandon Thomas 9972a75f10 Merge main into #824
# Conflicts:
#	crates/engine/src/cmd/file.rs
#	crates/engine/src/tests.rs
#	crates/xlsx/src/tests/passthrough_tests.rs
2026-10-13 00:53:41 -04:00
Brandon Thomas 3c4556bb04 Merge main into #856
# Conflicts:
#	ROADMAP.md
#	crates/model/src/lib.rs
#	crates/xlsx/src/tests/passthrough_tests.rs
#	docs/file-format-parity.md
2026-10-13 00:52:57 -04:00
Brandon ThomasandClaude Opus 5.5 6405b00cf7 Fix-up: repair test files after merging the conflicting PRs
- restore closing braces the line-level merges of side-by-side tests dropped
- new ChartData literals carry the palette field
- the filter-menu shortcut passes the row with the column
- #688's tab tests account for #851's All Sheets button
- Serbian margins for the ruler-margin labels

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 00:34:01 -04:00
Marco Arnulfo c7cb91ea53 Merge remote-tracking branch 'origin/main' into fix/xlsx-xml-maps 2026-10-13 06:33:03 +02:00
Marco Arnulfo d231823e1c Merge remote-tracking branch 'origin/main' into fix/name-union-definition 2026-10-13 06:22:09 +02:00
Brandon Thomas 94ee7efdf9 Merge main into #853
# Conflicts:
#	ROADMAP.md
#	docs/file-format-parity.md
2026-10-13 00:08:58 -04:00
Brandon Thomas b2e2df3fc9 Merge main into #825
# Conflicts:
#	crates/xlsx/src/tests/xls_tests.rs
2026-10-13 00:08:49 -04:00
Brandon Thomas 62b7ba1e49 Merge main into #823
# Conflicts:
#	crates/xlsx/src/tests/roundtrip.rs
2026-10-13 00:08:44 -04:00
Brandon Thomas 6b28a97f63 Merge main into #747
# Conflicts:
#	crates/xlsx/src/tests/xls_tests.rs
#	docs/file-format-parity.md
2026-10-13 00:07:57 -04:00
Brandon Thomas 29775397ef Merge main into #712
# Conflicts:
#	ROADMAP.md
#	crates/xlsx/src/tests/mod.rs
#	docs/file-format-parity.md
#	docs/gaps.md
2026-10-13 00:07:50 -04:00
Brandon Thomas 6a3b462176 Merge main into #610
# Conflicts:
#	crates/xlsx/src/tests/roundtrip.rs
2026-10-13 00:07:26 -04:00
Brandon Thomas ad11eb3109 Merge main into #460
# Conflicts:
#	docs/file-format-parity.md
2026-10-13 00:07:10 -04:00
Brandon Thomas d14269a001 Merge pull request #869 from marcoarnulfo:fix/this-row-structured-refs 2026-10-12 23:50:09 -04:00
Brandon Thomas efed7caa01 Merge pull request #826 from marcoarnulfo:fix/xlsx-external-file-hyperlinks 2026-10-12 23:49:53 -04:00
Brandon Thomas a76759cba6 Merge pull request #822 from marcoarnulfo:fix/workbook-fileversion-calcfeatures 2026-10-12 23:49:47 -04:00
Brandon Thomas c9b36de54c Merge pull request #821 from marcoarnulfo:fix/xls-kept-value-string-result 2026-10-12 23:49:45 -04:00
Brandon Thomas d8561a9ed7 Merge pull request #755 from marcoarnulfo:fix/xludf-prefix 2026-10-12 23:49:23 -04:00
Brandon Thomas 83e9314835 Merge pull request #748 from marcoarnulfo:fix/cfb-short-last-sector 2026-10-12 23:49:16 -04:00
Brandon Thomas 0837cbc44d Merge pull request #746 from marcoarnulfo:fix/xls-format-short-string 2026-10-12 23:49:13 -04:00
Brandon Thomas 98c85c6de4 Merge pull request #718 from neilcawse:fix/pivot-values-on-rows 2026-10-12 23:49:00 -04:00
Brandon Thomas 1bba0ba35c Merge pull request #644 from MarekSpodBiedry:issue-511 2026-10-12 23:48:32 -04:00
Brandon Thomas 91138569fa Merge pull request #643 from MarekSpodBiedry:issue-512 2026-10-12 23:48:30 -04:00
Brandon Thomas 9c4314b070 Merge pull request #642 from MarekSpodBiedry:issue-513 2026-10-12 23:48:29 -04:00
Brandon Thomas e5071f382b Merge pull request #637 from MarekSpodBiedry:issue-518 2026-10-12 23:21:59 -04:00
Brandon Thomas 1fa38e583e Merge pull request #636 from MarekSpodBiedry:issue-519 2026-10-12 23:21:58 -04:00