Commit Graph
345 Commits
Author SHA1 Message Date
Brandon Thomas 0f5227ddde Merge pull request #2513 from Fubeo:fub/keep-sheet-id 2026-10-25 09:36:35 -04:00
Brandon Thomas 2c81adf94f Merge pull request #2557 from Silvertree2010:fix/2517-a-new-font-derived-from-the-them 2026-10-25 09:36:33 -04:00
NetterSilverandClaude Opus 5.5 85b6f564f9 Keep <scheme val="minor"/> on every body-font font on save
Fixes #2517

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 13:20:17 +01:00
NetterSilverandClaude Haiku 5.5 d07639c7c4 Warn for each skipped drawing object and chart on XLSX open
Fixes #2407

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 12:25:18 +01:00
aiongg 6194f8241d Merge storytold/gridcraft#2522: Charts: 100% Stacked Area draws each value as a share of its category's total
# Conflicts:
#	ROADMAP.md
2026-10-25 02:54:23 +00:00
aiongg b58846a549 Merge storytold/gridcraft#2482: Outline: Settings puts summary rows above and summary columns left of their detail
# Conflicts:
#	docs/ui-parity.md
2026-10-25 02:54:23 +00:00
dotCooCoo 0eb2f1f6f0 Charts: 100% Stacked Area draws each value as a share of its category's total
Insert > Line or Area and the All Charts list offer 100% Stacked Area,
and insert.chart and chart.set take it as type area with subtype
stacked100. Each series' area stacks on the ones before it as its
share of the category's total. When every series on the value axis is
100% stacked and no value is negative, the axis runs from 0% to 100%.
A blank cell counts as zero, and a category whose total is zero stays
on the baseline.

An XLSX area chart with percentStacked grouping now opens as 100%
stacked and is saved back as percentStacked. It used to open as a
stacked area and was saved as stacked.
2026-10-24 16:42:59 -07:00
Fubeo 85e8f7909e fix(xlsx): keep each sheet's sheetId across a rewrite
The writer numbers sheetIds from 1 in sheet order, so a rewritten workbook
changes the id of any sheet that is not part of a gapless series from 1.
Anything that recognises a sheet by its id (parts carried through as they
are, external tools) loses it. The model keeps the sheetId it read
(Sheet::file_id, PreservedSheet::sheet_id, not serialised to JSON) and the
writer writes it back; a new sheet, or a copy with a taken id, gets the first
free one.
2026-10-25 01:31:19 +02:00
Harrison Booth 9ed1001035 Merge remote-tracking branch 'origin/main' into gc-xls-cell-styles
# Conflicts:
#	ROADMAP.md
2026-10-24 15:32:17 -05:00
dotCooCoo 118cd15d93 Outline: Settings puts summary rows above and summary columns left of their detail
Data > Outline > Settings now has Summary rows below detail and Summary
columns to right of detail. With one cleared, each group's - / + button
sits on the line before the group, its bracket runs down to the last
detail line, and Hide Detail, Show Detail, the level buttons and
Ungroup mark or clear the collapsed flag on that line. Changing a
direction moves the flags of collapsed groups to their new summary
lines.

- model: Sheet::summary_below and summary_right (on by default, also
  for older JSON).
- xlsx: read and written as sheetPr/outlinePr summaryBelow and
  summaryRight; files that set them no longer lose them on save.
- engine: data.outlineSettings {sheet?, summaryBelow?, summaryRight?},
  undoable; outline::groups_with places the summary line.
- ui: a Settings button in the Data tab's Outline group opens the
  dialog; the outline band follows the setting.
2026-10-24 11:34:35 -07:00
dotCooCoo 30126a709a PivotTables: Enable show details turns Show Details off for a PivotTable
A PivotTable has an Enable show details option, set with the new
pivot.options command ({pivot?, enableShowDetails?: bool}). While it is
off, pivot.showDetails and double-clicking a value cell list no records:
the double-click shows a message and does not open the cell for editing.
Labels and headers still edit. Changing the option is one undo step and
does not refresh the report. pivot.fields reports the setting.

XLSX files keep it: an off option is written as enableDrill="0" on the
pivotTableDefinition, and enableDrill is read on open (on when absent).
2026-10-24 07:46:29 -07:00
Dylan M. Taylor 3099df934e M7: verify Agile encryption's dataIntegrity before returning plaintext (#2425)
Agile decryption checked only the password verifier, which authenticates
the key — not the package. An attacker who could replace EncryptedPackage
ciphertext could change workbook content undetected ([MS-OFFCRYPTO]
2.3.4.14, the descriptor's dataIntegrity element).

The reader now requires the dataIntegrity pair, decrypts it with the
recovered package key, and checks HMAC(hash, hmacKey) over the whole
EncryptedPackage stream (StreamSize field included) against the stored
value before any plaintext is returned. Excel always writes the element
("SHOULD" in the spec); descriptors without it are refused as damaged
rather than accepted unauthenticated.

- crypto: RFC 2104/4231 HMAC test vectors, RFC-test cases for a key
  longer than the block size
- fixtures write the pair like Excel; new tests: missing/empty/ragged
  integrity fields, tampered ciphertext and descriptor, a changed
  StreamSize field
2026-10-24 07:58:13 -04:00
Dylan M. TaylorandCodebuff 6fac430f55 M2.9: cap xlsx import work and gate hostile datetime field lengths (#2413)
Security-sweep findings from the scan at revision ac8da7d4 (storytold-
gridcraft-agent-findings.json), no longer exploitable on main at 72c1d57d.
Each change caps work or storage per sheet/part so a hostile file cannot buy
unbounded work with ordinary bytes; each has a regression test:

- finding 2 (package.rs): a part rejected by the MAX_PART size gate is
  charged its declared compressed size up front, so a loop of oversized
  parts still ends the allowance instead of re-entering the package.
- finding 4 (xml.rs): elements past the kept-node cap are consumed and
  dropped instead of kept — parse cost stays linear, memory stops growing
  with the same input (a small compressed workbook previously asked for
  gigabytes of declared-sized metadata tree).
- finding 10 (records.rs): .remaining() caches the suffix of a shared
  string's Continue parts, so crossing N Continue records is O(N) instead
  of O(N^2) in records scanned per character.
- finding 9 (xls shared formulas): the SHRFMLA master template is parsed
  once and re-emitted per dependent cell instead of decoded and reprinted
  for every referencing cell.
- findings 3, 6, 8 (sheet_read.rs): hyperlink-cell expansion, conditional-
  format kept range cells and dynamic-array (deleted-spill) checks are
  capped with one import warning each instead of the compact-workbook
  multi-gigabyte allocation or the scanned-per-cell cost.
- finding 31 (core/parse.rs): datetime recognition no longer reparses a
  prefix at every space with a time-shaped tail; an all-digit field length
  is gated before the derivation machinery runs, staying linear in the
  text.

🤖 Generated with Codebuff

Co-authored-by: Codebuff <noreply@codebuff.com>
2026-10-24 05:46:28 -04:00
Helen Kwok b389d9067a XLSX: write accounting formats 42 and 44 with their code
builtin_id already wrote the currency formats 5-8 as custom formats
because readers resolve those ids by locale. The accounting formats
with a currency, 42 and 44, are resolved the same way but were written
as bare ids, so LibreOffice showed a 44 cell holding -0.14 as "- 0.14"
where US Excel shows "$ (0.14)". Treat them like 5-8.
2026-10-24 10:17:32 +08:00
Harrison Booth ad2e3ac9de Check duplicate .xls style names in linear time 2026-10-23 16:37:19 -05:00
Harrison Booth 2c052d03d5 Read cell formatting from .xls files
.xls (Excel 97-2003) import used only each cell's number format. It now
also reads the BIFF8 formatting records (Font, XF, Palette and Style),
so opened sheets keep their fonts, solid and pattern fills, borders
(including diagonals), alignment (rotation, indent, wrap, shrink),
locked and hidden protection, the workbook's custom colour palette, its
Normal font as the default, and its user-defined cell styles.

The decoding lives in a new xls/styles.rs; xls/mod.rs gains the record
arms and the build call. Short or malformed formatting records keep the
fields present and add one warning instead of refusing the workbook.
Tests build the records in code, including truncation and byte mutation.

Also merges origin/main, keeping its dialog-sheet, WINDOW2 and XF
number-format handling; the XF arm now goes through the new styles
module, which covers the number format too.
2026-10-23 16:32:15 -05:00
Brandon Thomas bcf55f0f00 Merge PR #2274
# Conflicts:
#	docs/ui-parity.md
2026-10-23 10:11:33 -04:00
aiongg 2ed3df2bd6 G4: two windows on one workbook without copying it; Arrange All
- The workbook's active sheet is an atomic set in place (Workbook::active_sheet /
  set_active_sheet), so a window swap or sheet switch never clones the
  Arc<Workbook> or marks it dirty. Selections already live in DocState and the
  window views. Save records the sheet of the most recently active window.
- A dialog taken out of its window while it runs goes back to that window when
  a command run from it switched windows (dialogs::show, ui.dialog.confirm).
- view.arrangeAll {arrange?: tiled|horizontal|vertical|cascade, activeWorkbook?}
  returns the visible windows, active first; the desktop moves and sizes the
  native windows over the main window's monitor. New Window and Arrange All
  buttons on the View ribbon; Arrange Windows dialog.
- Docs: ui-parity, hardware-parity, gaps #20, target-app-parity, ROADMAP.
2026-10-22 16:50:10 +00:00
Dominik StrebelandClaude Opus 5.5 840ba5c3a5 view: Show Outline Symbols (Ctrl+8), read and written
A sheet can hide its outline's level buttons and brackets while keeping
the outline (Excel's Ctrl+8, saved as showOutlineSymbols="0"). It wasn't
modelled, so such sheets showed a wide outline band Excel doesn't.

- model: Sheet::show_outline_symbols (default on, also for older JSON).
- xlsx: read and written on the sheet view.
- engine: view.showOutlineSymbols {on?} (Ctrl+8), undoable.
- ui: no outline bands while it's off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 15:54:06 +02:00
NetterSilverandClaude Opus 5.5 c39d2ad197 Keep '!' in structured-reference columns intact on XLSX save
Found while testing crates/xlsx/src/fmla.rs:101 (with crates/formula/src/printer.rs:181)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 09:47:21 +02:00
aiongg c7016fb724 Integration: fix struct literals and call signatures after combining tracks (Series.quartiles, Comment.note_box, Image data Arc, signature language) 2026-10-22 03:11:35 +00:00
aiongg d5cdd2ea2b Integration: track t15
# Conflicts:
#	contributors/people.toml
#	crates/engine/src/tests.rs
#	docs/file-format-parity.md
#	docs/localization-parity.md
2026-10-22 03:05:39 +00:00
aiongg 561382cc6d Integration: track t14
# Conflicts:
#	ROADMAP.md
#	contributors/people.toml
#	crates/formula/src/tests.rs
#	docs/file-format-parity.md
#	docs/localization-parity.md
#	docs/ui-parity.md
2026-10-22 03:05:30 +00:00
aiongg ca1cdd4229 Integration: track t13
# Conflicts:
#	ROADMAP.md
#	contributors/people.toml
#	crates/engine/src/cmd/data.rs
#	crates/xlsx/src/tests/mod.rs
#	docs/chart-parity.md
#	docs/file-format-parity.md
#	docs/function-parity.md
#	docs/gaps.md
#	docs/localization-parity.md
#	docs/target-app-parity.md
2026-10-22 03:05:18 +00:00
aiongg c7fd66d807 Integration: track t12
# Conflicts:
#	ROADMAP.md
#	crates/calc/src/tests.rs
#	crates/chart/src/tests.rs
#	crates/engine/src/tests.rs
#	crates/xlsx/src/tests/mod.rs
#	docs/chart-parity.md
2026-10-22 03:05:03 +00:00
aiongg 4c5c345c31 Integration: track t10
# Conflicts:
#	ROADMAP.md
#	crates/ui-egui/tests/sheet_tabs.rs
#	crates/xlsx/src/tests/roundtrip.rs
#	docs/gaps.md
#	docs/ui-parity.md
2026-10-22 03:04:30 +00:00
aiongg d3201e7cea Integration: track t08
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/tests.rs
#	docs/file-format-parity.md
#	docs/localization-parity.md
2026-10-22 03:03:55 +00:00
aiongg 512b2aa9fd Integration: track t04
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/cmd/edit.rs
#	crates/engine/src/cmd/formulas.rs
#	crates/engine/src/tests.rs
#	crates/formula/src/lib.rs
#	crates/formula/src/parser.rs
#	crates/formula/src/tests.rs
#	crates/ui-egui/src/control.rs
#	crates/ui-egui/src/formula_bar.rs
#	crates/ui-egui/src/grid.rs
#	crates/ui-egui/src/i18n.rs
#	crates/ui-egui/src/lib.rs
#	crates/ui-egui/src/ribbon.rs
#	crates/ui-egui/tests/selection.rs
#	docs/function-parity.md
#	docs/ui-parity.md
2026-10-22 03:01:31 +00:00
aiongg b0b6409112 Integration: track t03
# Conflicts:
#	ROADMAP.md
#	crates/ui-egui/tests/keyboard_shortcuts.rs
#	docs/file-format-parity.md
#	docs/localization-parity.md
#	docs/ui-parity.md
2026-10-22 03:00:13 +00:00
aiongg 54d0f7cfc0 Integration: track t02
# Conflicts:
#	ROADMAP.md
#	crates/core/src/lib.rs
#	crates/ui-egui/src/editor.rs
#	crates/ui-egui/src/grid.rs
#	crates/ui-egui/src/tabs.rs
#	docs/chart-parity.md
#	docs/localization-parity.md
#	docs/ui-parity.md
2026-10-22 02:59:55 +00:00
aiongg b35b5a8b9d Integration: cargo fmt 2026-10-22 02:46:29 +00:00
aiongg 7b47c5a846 Integration: #995 test builds Image.data as Arc<Vec<u8>> (after #1629)
shared_media.rs predates the Arc change; literals and comparisons use .into() and to_vec().
2026-10-22 02:46:21 +00:00
aiongg 4ac3f2fa64 Integration: cargo fmt 2026-10-22 02:45:35 +00:00
aiongg 1316d39865 Merge PR #1151: Charts: a huge trendline forecast no longer collapses the chart (#1148)
ROADMAP.md: all rows kept.
crates/chart/src/tests.rs: both tests kept.
2026-10-22 02:37:37 +00:00
aiongg 37bf37d181 Merge PR #1103: Keep chart cache point positions when a reference has blank edges (#1015)
crates/xlsx/src/chart.rs: 1103's used-range clipping applied in ref_values after 1088's ref_target call.
2026-10-22 02:37:08 +00:00
aiongg f4f18bb368 Merge PR #1701: XLSX: new workbooks get <fileVersion appName="gridcraft"/>
Conflicts:
- crates/xlsx/src/write.rs: keeps #1701's fileVersion block followed by #1626's workbookPr attributes (showObjects, date1904)
2026-10-22 02:37:05 +00:00
aiongg 857bdae023 Merge PR #1692: XLSX: keep Japanese phonetic runs on open and save
Conflicts:
- crates/model/src/lib.rs, crates/xlsx/src/read.rs, crates/xlsx/src/write.rs: use lists unioned (ShowObjects and the phonetic types)
- docs/file-format-parity.md: main's header and rows kept (#1692 collapsed the three duplicate .xlsx rows; they stay), phonetic-runs text added to each; Last updated 2026-10-20
2026-10-22 02:36:58 +00:00
aiongg e738049157 Merge PR #1088: Charts: multi-level category axes (year beside month) read, drawn and written back
ROADMAP.md: all rows kept.
2026-10-22 02:36:49 +00:00
aiongg 6370fd2254 Merge PR #1693: Charts: series that refer to defined names ([0]!Name) plot their data, and are saved qualified
crates/xlsx/src/tests/mod.rs: kept both mod lines (cellmetadata, chart_book_names)
docs/file-format-parity.md: kept HEAD header line, both change rows
2026-10-22 02:36:34 +00:00
aiongg d97f61748e Merge PR #1946: XLSX: keep cell fonts' charset (Arabic 178) and family on save
crates/xlsx/src/styles.rs: family/charset block ported into the dxf.is_none() branch of font_xml (PR #1225 made it take Option<&DxfStated>)
docs/file-format-parity.md: header Change line keeps both sides' clauses
2026-10-22 02:36:23 +00:00
aiongg 8e2974cc65 Merge PR #1629: Memory: undo steps share picture and macro bytes instead of copying them
Conflicts:
- ROADMAP.md: both progress-log and revision rows kept
- crates/xlsx/src/sheet_write.rs: keeps #995's image_part call; drops #1629's to_vec line (image_part copies from &[u8])
2026-10-22 02:36:22 +00:00
aiongg 7fd1553a8b Merge PR #1626: Keyboard: Ctrl+6 hides and shows objects, saved as workbookPr showObjects
Conflicts:
- docs/file-format-parity.md: header and the three .xlsx rows keep main's text plus the show-objects entry; both log rows kept
- docs/ui-parity.md: header keeps main's text plus Ctrl+6; both log rows kept
2026-10-22 02:36:15 +00:00
aiongg 7221e11f56 Merge PR #1945: XLSX: keep the theme's per-script fonts (<a:font script="Arab">) on save (stacked on #1740, included)
Conflicts:
- docs/file-format-parity.md: kept main's three .xlsx rows (the PR's docs hunk replaced them with one) and added the theme-font wording to each; header keeps main's change list plus this PR's phrase (updated 2026-10-20)
2026-10-22 02:36:12 +00:00
aiongg d67193f403 Merge PR #1685: #1447: a PivotChart's link names the workbook
docs/file-format-parity.md: header Change line names both pivot fixes; both change log rows kept
2026-10-22 02:36:11 +00:00
aiongg 635056bd23 Merge PR #2131: Print: JIS B4/B5 and Japanese paper sizes (はがき・封筒・B6)
ROADMAP.md: one status line naming both changes; both feature-table and progress-log rows, newest first.
2026-10-22 02:36:10 +00:00
aiongg c03d4a881b Merge PR #1570: XLSX: keep dynamic-array cell metadata (cm) on save
ROADMAP.md: kept both progress-log rows
2026-10-22 02:36:04 +00:00
aiongg 508d230f7f Merge PR #1683: #1447: two value fields without a column field hide the Values row 2026-10-22 02:36:01 +00:00
aiongg 44320b393e Merge PR #1648: fix(xlsx): bound repeated column expansion
Conflicts:
- docs/file-format-parity.md: kept main's header and log rows, added this PR's change phrase and row once
2026-10-22 02:35:42 +00:00
aiongg 7a016c2de9 Integration: #1531 fold the sheet filter into a table only when both start at the same column
A sheet AutoFilter that starts right of the table's first column is not folded
(its colId offsets would hit the wrong column); the sheet autoFilter stays
suppressed for any table it intersects, as before. Test added.
2026-10-22 02:35:41 +00:00
aiongg d83dc11ff2 Merge PR #1695: Charts: box & whisker charts honor their quartile method
crates/engine/src/tests.rs: kept both new tests (csv_as_text_keeps_identifier_columns_as_written and box_whisker_quartile_methods_survive_xlsx).
2026-10-22 02:35:39 +00:00