text_width sums in u64; an all-zero or non-finite dash array is written as
solid ([] 0 d); stroke widths are clamped to >= 0 (NaN -> 0); drawing calls
with non-finite or non-positive geometry return before storing anything.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Info strings were WinAnsi; PDF text strings are PDFDocEncoding or UTF-16BE.
Printable ASCII stays a literal string; anything else is a <FEFF...> hex
string. Info strings are capped at 1024 characters.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
home.rowHeight, home.autofitRowHeight and data.ungroup change row hidden
state without marking the edit structural, so SUBTOTAL(101-111) kept a stale
value. Found by the command-sequence fuzz (seed 3).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
With values on rows and no column field, the header cell above the data
column reads "Total", as Excel saves it (tabular checked against a real
file).
Σ Values in Columns always comes after the last column field: the column
layout doesn't place it anywhere else, so `pivot.moveField` no longer
stores a position there, the reader ignores where `-2` sits in colFields
and the writer puts it last.
`pivot.changeSource` removes dropped row and column fields one by one so
Σ Values keeps its place ahead of the field it preceded.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Format Cells dialog drew English literals in every language. Its
tabs, labels, options and protection note now go through Language::tr,
as do dialog titles and the shared OK/Cancel buttons. The Fill tab is
keyed Format Cells|Fill because the Alignment tab's Fill is a different
word in some languages. d.tab keeps the English name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Excel rejects a text criterion longer than 255 characters in COUNTIF(S),
SUMIF(S), AVERAGEIF(S), MAXIFS and MINIFS with #VALUE!; GridCraft matched
it anyway. Found by the calc oracle on an Excel-saved report where a
268-character concatenated criterion caches #VALUE!.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The exponent sign goes right before the exponent digits, after any literal that follows
the E; ? pads exponent digits with spaces; % and commas never scale a scientific section;
zero shows 0 in every integer placeholder. Checked against the 248 TEXT() rows of Apache
POI's NumberFormatApproxTests.xlsx, copied as data into a test.
home.deleteRows/deleteColumns used only the last selected area. They now
take every area (or a comma list like "3:3,7:8"), merge them and delete
from the last to the first inside one edit, so it is one undo step.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A formula under a criteria label that names no field is evaluated for each record: its relative references move from the first record to that record, and the database labels name the record's fields, as in Excel. The calc evaluator passes the criteria reference to the database functions through Ctx::computed_criterion.
Ctrl+Shift+Arrow (and Shift+Arrow, Shift+PageUp/PageDown, Alt+Shift+PageUp/PageDown,
Ctrl+Shift+Home/End, Shift+Home) extend the selection by moving its far corner while the
active cell stays on the anchor. After the move the grid set `ensure_visible`, but the
scroll-into-view step in `grid::show` always targeted `selection.active`, which is the
anchor and is already on screen. So the view never followed the extension past the last
visible row or column.
The keyboard extension paths now also set `GridState::reveal_moving_end`, and the
ensure-visible step then scrolls to `Selection::moving_end()`, the corner opposite the
anchor. `selection.move` now uses that same helper (it had the logic inline) to find where
an extension starts. Other ensure-visible callers (Go To, Name Box, plain moves) still
reveal the active cell.
Regression test: crates/ui-egui/tests/selection_scroll.rs drives Ctrl+Shift+Up/Down/
Left/Right, Shift+Down/Right and Ctrl+Shift+Home/End through real egui key events and
checks that the moving end ends up on screen while the anchor and active cell stay put.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The parser stripped _xludf. like _xlfn., but the two mean opposite things:
_xludf. marks a name Excel must not treat as a built-in. Keeping it in the
call name makes the call an unknown function (#NAME?, as in Excel) and the
writer emits it unchanged, so _xludf.EOMONTH() no longer saves as an
EOMONTH() call Excel repairs away.
CalcMode repeats in every worksheet substream like the iteration records and sets wb.calc.mode from the first worksheet; CalcPrecision sits in the globals and sets precision_as_displayed, matching calcMode and fullPrecision on the .xlsx side.
CalcIter, CalcCount and CalcDelta repeat in every worksheet substream and were skipped, so a workbook saved with iterative calculation on opened with it off and its circular references showed #CIRC!. They now set wb.calc from the first worksheet, as <calcPr iterate iterateCount iterateDelta> does for .xlsx.
Excel's EDATE and EOMONTH read serial 0 as 0 January 1900 and end
February 1900 on the 28th (serial 59), although the sheet itself keeps
the Lotus 29 February 1900 (serial 60). GridCraft clamped day 0 up to
day 1 and used the sheet's 29-day February, so EDATE(0,0) was 1,
EDATE(0,1) 32, EDATE(31,1) and EOMONTH(59,0) 60.
EDATE now keeps day 0 and both functions take the real calendar's month
lengths. Serial 60 still reads as 29 February, so EDATE(60,1) stays
29 March. DATE, DAY and the parser keep the leap day.
Tests: the Excel 365 table from the issue; the #124 cases that expected
60 at the end of February 1900 now expect 59.
Brazilian Portuguese had 377 catalog entries, so most dialogs, menus and
tooltips showed in English with the interface set to Português (Brasil).
This adds every key the other catalogs translate (counting the open
Spanish-dialog #669, Russian #676, Czech #652, Polish #650, Turkish #663
and Italian #713 PRs), bringing pt-BR to 923 entries.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`input` is documented as "text, number or =formula", but only a string
was read; a JSON number was dropped and the command failed (or, on 0.5.0,
silently cleared the cell). The undocumented `value` alias already went
through `json_to_input`. Both keys now share that converter, so numbers
and booleans are entered as typed. The MCP `set_cell` schema types
`input` as string, number or boolean to match its description.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With two or more value fields the Σ Values pseudo-field can sit in Rows
as well as Columns, at any position among the row fields. The model
records it (`PivotTable.values_on_rows`, `values_position`) and the
engine lays it out like Excel in compact, outline and tabular form: one
row item per value field at the Σ Values level, per-value subtotals
("East Sum of Sales") and grand totals ("Total Sum of Sales").
XLSX reads `dataOnRows` and the `-2` field's place in rowFields or
colFields and writes them back; the "values on rows are shown on
columns" warning is gone. `pivot.moveField` moves "Σ Values" between
Rows and Columns or within one, axis field moves keep it in place, and
the PivotTable Fields pane lists it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>