* M12.AR12: estimate Arabic text width from measured glyph widths in AutoFit
* M12.AR12: treat U+0897–089F as marks and U+08E2 as visible; size the honorific ligatures (review)
* M12.AR12: size the remaining honorific ligatures like their siblings (review)
U+FD4C-FD4F (Unicode 14) and U+FDC8-FDCF (FDCF Unicode 14, FDC8-FDCE
Unicode 17) are honorific word ligatures, not letters, but fell into
the 0.48 em letter arm. Give them the 1.6 em of the measured
U+FD40-FD4B honorifics and assert each range's endpoints.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M12.AR12: size Arabic-Indic digits like ASCII digits and the remaining honorific ligatures (review)
Arabic-Indic (U+0660-0669) and Extended Arabic-Indic (U+06F0-06F9)
digits measure 0.53-0.57 em in Noto Sans Arabic, Arial, Tahoma and
Times New Roman, the same as each font's ASCII digits, so they get
the ASCII digit estimate (0.56) instead of the letter estimate.
Arabic punctuation measures 0.17-0.50 em, so it keeps 0.48 rather
than falling back to 1 em.
U+FBC3-FBD2 and U+FD90-FD91 are honorific word ligatures like
U+FD40-FD4F and get the same 1.6 em.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
Co-authored-by: FredApps <FredApps@users.noreply.github.com>
* M12.AR7: sort Arabic text by its letters, not its vowel marks or digit script
* M12.AR7: order marked Arabic text the way Excel does
Checked compare_text against Excel 16.0 build 20527 (en-US) via COM:
Range.Sort, =SORT() and the < / = operators over ~300 strings. Fixes the
mismatches inside this change's scope:
- The tie-break compared raw code points, so an RLM-prefixed word sorted
after every vowelled form of it, "12" after "١٢", and a tatweel
form after a shadda form. Ignorable characters are now skipped at the
tie-break too.
- Marks are compared letter by letter (an unmarked letter first), so a
mark on an earlier letter outranks the digit or letter after it, as in
Excel ("ب١" < "بَ1", "بپ" < "بَپ").
- Marks rank in Excel's measured order (sukun before shadda), and the
Quranic marks Excel skips (U+0610–061A, U+06D8–06ED except 06DD/06DE/
06DF/06E0/06E5/06E6/06E9) are skipped by letter order.
- Excel also ignores bidi embeddings/overrides/isolates, BOM, soft hyphen,
CGJ and word joiner; they now are ignored too (ZWSP is not, as in Excel).
Equality is unchanged. Regression tests use the orders Excel produced.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M12.AR7: compare the mark level of Arabic text without allocating
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
* ft-121.7.9: apply Set precision as displayed in calculation (#1343)
With the option on, a formula result (and each element of a spill) is
rounded to its cell's number format before it is stored and before other
formulas read it. Numbers typed, pasted or reformatted while it is on are
rounded at once, and turning it on through formulas.calculationOptions
{precisionAsDisplayed} rounds every number constant, as Excel does.
The rounding is NumberFormat::round_as_displayed in the numfmt crate,
which shares the renderer's digit logic (fixed, scientific, fraction).
General, date/time, text, digitless and conditional formats keep the full
value, as observed in Excel through COM. Nothing changes when the flag is
off.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* ft-121.7.9: round conditional formats by the sign's section under precision as displayed (#1343)
Excel does round formats with a condition when "Set precision as
displayed" is on; the PR kept them at full value. Observed through COM
with synthetic values (and in gridcraft-corpus
excel/precision-displayed/pad-on-conditional-sections.xlsx): conditions
are ignored and the sign picks the section, the first for positives and
the second for negatives. `[<1]0.00;0` shows 2+1/3 as 2 but stores 2.33,
and stores -10/3 as -3.
Two more observed details: with a condition and a single number section,
negatives keep the full value (`[<1]0.00`), unless an `@` section follows
(`[<1]0.00;@` rounds them by the first section); and a completely empty
section passes to the next one (`0.0;;0.000` stores -2.333 as -2.333,
`;0.0` stores 2.333 as 2.3), for formats with or without a condition.
Display is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* engine: recalculate spill anchors when a spill cell is reformatted under precision as displayed
round_edited_as_displayed only looked at stored-cell changes, but a spill
destination lives in Sheet::spill, so reformatting it (or its row or column) left
the anchor out of the changed set and the element unrounded. Add each spill anchor
whose range holds a changed cell or a changed row/column style to the changed set.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* numfmt: drop a cast the fraction merge made redundant
* precision as displayed: keep locked constants on protected sheets, round new sheets, fix manual-mode comment
* precision as displayed: name the input sheet in the new-sheet histogram test
* precision as displayed: use six values in the new-sheet histogram test
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* UI: a dialog's reference boxes belong to the sheet it was opened on, recorded when it opens rather than at the first pick
* UI: a click on the cell already selected writes it into the reference box being picked into
* Solver: references split into areas only at commas outside quoted sheet names, through one splitter shared with the reference boxes
* Solver: a constraint splits at its relation only outside quoted sheet names, in constraint text and in a model loaded from cells
* Solver: Save Model counts a constraint's areas outside quoted sheet names, and reads a number on the right side as one value whatever its separators
* UI: a Ctrl+click that follows a link is no pick of the cell already selected
The press on the grid tells the reference box being picked into that a
cell was selected only in the branches that select: Shift+click, Ctrl+click
that adds a cell, and a plain click. A Ctrl+click on a link's text follows
the link and selects nothing, so a link back to the selected cell no longer
replaces the box's text.
* UI: while a reference box picks, a second quick click on a cell picks it
A click on the same cell within the double-click interval started editing
the cell and skipped the pick, also when the earlier click came before the
box was clicked into. While a reference box is the one grid selections
fill, the double-click handling is skipped and the click picks.
* Docs: reference boxes take a second quick click as a pick; a followed link is none
* UI: while a reference box picks, a click on a link or checkbox picks its cell
A link was followed (Go To moved the selection into the box, and a web
link's question replaced the dialog) and a checkbox was toggled. While a
reference box is the one grid selections fill, a click only picks the cell
clicked, with or without Ctrl. The test of the Ctrl+click on a link back to
the selected cell becomes part of a test of links on the same sheet, a web
link and a checkbox.
* Charts: draw every doughnut series as a ring (#1827)
* Charts: submit doughnut hole size and first slice angle when the drag ends (#1827)
---------
Co-authored-by: alosslessdev <alosslessdev@users.noreply.github.com>
Conflicts:
- crates/xlsx/src/read.rs: main's names.unique() kept with this branch's
(sheet, read_controls) result from read_sheet.
- crates/ui-egui/tests/it/keytips_generated.rs: regenerated with
cargo xtask keytips from the merged keytips.json (413 sequences).
- docs/file-format-parity.md, docs/ui-parity.md: header and revision
history keep main's entries and this branch's form-controls entries.
RaTeX counts macro expansions (1,000) but not how large they grow, so a
definition whose argument doubles on every call keeps the expander busy
without bound. User macros have no use in an equation, so check_caps now
refuses the definers (def, gdef, edef, xdef, let, futurelet, global, long,
newcommand, renewcommand, providecommand) before RaTeX sees the source. The
scan reads control words, verbatim text and comments the way RaTeX's lexer
does.
A matrix is laid out and written as rows times the widest row, so one long row
in a tall matrix multiplied the output. check_caps now counts the padded cells
of every environment from the source and refuses more than 2,500 in total;
to_omml checks the parsed grid as well.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
On a wide table the eye loses its place following a row from the active
cell. File > Options > General gets "Highlight the active cell's row", off
by default: a tint across the whole row of the active cell, over the fills
and under the text, in Normal and Page Layout views. It is a preference for
every sheet and workbook, so nothing goes into the file, the workbook does
not become dirty and there is no undo step.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A press on a header while a formula waits for a reference inserted C:C, but the
drag that followed did nothing, so dragging from column C to E left C:C. The drag
now follows the pointer, as it does over cells, in either direction and for rows
too, keeping the sheet qualifier, the earlier arguments of a Ctrl press and the
far end of a Shift press.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Addresses CodeRabbit review on #3250:
- "Clear parked keytip requests when their window becomes hidden": a
window hidden from another window (View › Hide while it isn't in
front) kept the request it had parked, and showing it again opened
the control. follow_active_window now drops the waiting request of
every parked window that is hidden.
- "Correct the no-badge description in the parity table": only Alt+I,
D, V and T (no ribbon tab) paint no badges.
Test: legacy_keytips::a_window_hidden_from_another_window_drops_its_parked_request.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
edit.find and edit.replace take within: selection, which searches only
the selected cells; Find Next then moves the active cell through them
and keeps the range selected, as Excel does. The Find and Replace
dialog uses it whenever more than one cell is selected (Excel searches
a selected range; one selected cell still means the whole sheet).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A small reader for the SYLK text format: cell values (numbers, text,
booleans, errors), formulas converted from R1C1 to A1 as Excel does
(relative/absolute references, ranges, whole rows and columns, shared
formulas) with their cached results, number formats (P / F;P) and
column widths (F;W). Import only; saving as .slk says so. The sheet is
named after the file, and damaged records are skipped with a warning.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cmd/Ctrl+V dropped egui's PasteImage event, and the ribbon/menu Paste only read
text, so screenshots and images copied in a browser reported "Nothing to paste".
Both now encode the pixels as PNG (bounded to 16384 px per side, 64 Mpx) and run
insert.picture as a floating picture anchored at the active cell, as Excel does.
Text on the clipboard still wins. New host service: Services::paste_image (arboard).
Fixes#699, #3018, #3182.
The bottom edge of the formula bar is a resize handle: dragging it sets
the height in whole lines (up to half the window; one line collapses
the bar). Expanding again (the chevron, Ctrl+Shift+U) returns to the
dragged height instead of a fixed five lines. The drag previews live
and commits once through view.expandFormulaBar {lines}, which is also
scriptable; the height is kept with the UI preferences.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds 43 legacy sequences to keytips.json (Insert, Edit, Format, Data,
View, Tools menus and File > Page Setup), each running the command or
dialog the modern key tip already uses. Alt+I, D, V and T are implicit
levels with no ribbon tab; badges are not painted under them, as in
Excel. The generator checks Options (a Backstage page) by its page.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Right-clicking a cell with a link only offered "Link…", whose dialog was
titled Insert Hyperlink, so changing a link wasn't obvious. As in Excel,
the cell menu of a linked cell now offers Edit Hyperlink… (the same
dialog, filled with the link and titled Edit Hyperlink), Open Hyperlink
(as clicking the link: a reference selects its cell, a web or e-mail
address asks first) and Remove Hyperlink (edit.clearHyperlinks). A cell
without a link keeps "Link…".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Paste Special only knew the letters F, V, T, C and E. Excel gives every
option one: A (All), N (Validation), X (All except borders), W (Column
widths), R (Formulas and number formats), U (Values and number formats);
O, D, S, M, I for the operations; B for Skip blanks; and L for the
Paste Link button, which pastes at once. One table now maps each letter
to what it does; Enter still pastes with the chosen options.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Excel can hide the sheet tabs, the horizontal scroll bar and the
vertical scroll bar of a workbook window (File › Options › Advanced ›
Display options for this workbook), saved with the workbook. GridCraft
already read and wrote these settings (#3057) but always showed all
three, and had no command or option for them.
New commands view.showSheetTabs, view.showHorizontalScrollBar and
view.showVerticalScrollBar {on?} (toggling without `on`, undoable,
saved with the workbook), and a "Display options for this workbook"
section in File › Options. The tab bar follows them: without the tabs
the scroll bar takes the bar, without the scroll bar the tabs do, and
with neither the bar isn't shown; without the vertical scroll bar the
grid takes its width.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Excel's per-workbook privacy setting (Trust Center › Privacy Options ›
Remove personal information from file properties on save, set by the
Document Inspector) is saved as <workbookPr filterPrivacy="1"/>, and
while it is on Excel leaves the author and company empty on save.
GridCraft dropped the attribute, so a cleaned workbook came back with
the setting off, and it wrote the author and company back.
Workbook.remove_personal_info now keeps the setting (read and written
as filterPrivacy), and while it is on the writer leaves dc:creator and
Company out. Other properties are written as before. Note authors and
lastModifiedBy are the rest of the issue (the latter overlaps #881).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The grid took a column's right edge to be where the next column starts,
`x(col + 1)`. At the freeze line the next column is in the scrolled pane
and moves with the scroll, so everything measured that way inside the
frozen columns shrank as the sheet scrolled right:
- text overflowing from a frozen cell was clipped at the scrolled
column's edge, until only a character or two showed;
- the frozen columns' gridlines stopped short, with a stray vertical
line drawn inside them;
- a merged range's borders under the last frozen column ended early;
- the freeze handle measured the last frozen column wrongly when
dropped while scrolled.
Geo gains `x_end`/`y_end` (a column's or row's own far edge) and
`same_col_pane`, and every such measurement uses them. Text overflows
only into empty cells of its own pane. In Page Layout view the
gridlines now stop at each page's last column instead of running into
the margin.
Fixes#2544
A workbook saved by Excel in R1C1 reference style says so in
<calcPr refMode="R1C1"/>. GridCraft didn't read the attribute, so
opening and saving such a file switched it back to A1 style for the
next person who opened it in Excel. The calculation settings now keep
it (CalcSettings.r1c1, serde default false) and the writer puts it back;
A1 stays the default and isn't written. GridCraft itself still shows
and takes A1 references; the R1C1 display and entry are the rest of
the issue.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copy wrapped any cell that held a double quote in quotes (and doubled
its quotes), so copying `say "hi"` into another app gave
`"say ""hi"""`. Excel quotes only text with a tab or line break; a cell
with just quotes goes on the clipboard as it is.
Pasting text read a field that starts with a quote as quoted even when
its closing quote doesn't end the field, so `"hi" there` (which Excel
copies as it is) pasted as `hi there`. A field is now quoted only when
its closing quote is followed by a tab, a line end or the end of the
text; otherwise it is the text as written, as Excel pastes it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
While dragging with a pen, the stroke under the pointer was always
painted blue at 2 pt, whatever pen or highlighter was chosen, so the
preview and the committed ink disagreed (a highlighter previewed as a
thin blue line). The preview now uses the colour and width draw.stroke
stores (pen blue and 2 pt by default, 0.5-40 pt, whole points) and the
translucency Ink shapes are painted with above 8 pt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The page margins are kept as f32 and were widened to f64 when written,
so a 0.3-inch margin was saved as left="0.30000001192092896" (and 1.1
as 1.100000023841858). Each margin now goes through its shortest
decimal form first, so the file says 0.3, as Excel writes it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Excel's Next Corner of Selection (Ctrl+., Control+. on a Mac) moves the
active cell clockwise to the next corner of the selected area (top-left,
top-right, bottom-right, bottom-left) and keeps the area selected; the
new active cell is also the anchor, so Shift+arrows then move the
opposite corner. GridCraft had no such command.
New command selection.nextCorner (Selection::next_corner) bound to
Ctrl+. on every platform. A one-row or one-column area goes end to end;
a single cell stays where it is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Web apps' "Export to Excel" often writes an HTML table and names it
.xls. GridCraft read such a file as CSV text, so it opened as lines of
markup in column A. Excel opens the table (after warning that the
format and the extension don't match).
Opening a file whose bytes are a web page with a table now reads the
first table as a sheet, through the clipboard HTML table reader: the
cells go through the CSV reader, so values read as they do in a CSV
(and stay text with csvAsText); <th> cells are bold and centred; the
sheet takes the page's <x:Name> or the file's name. A page that isn't
named .htm/.html opens with a warning saying what it is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GROUPBY's total_depth only ever added one Total line. In Excel a depth
of N adds a subtotal line for each group of the first N - 1 key columns
(after the group, or before it when the depth is negative) and labels
the last line "Grand Total"; a depth greater than the number of key
columns, or above 1 with field_relationship 1 (a flat table), is
#VALUE!. The subtotals aggregate the group's source rows, so AVERAGE and
COUNT subtotals are right too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dragging the selection's border onto cells that hold data replaced them
without asking, so dragging column B onto column A silently destroyed A.
Excel asks "There's already data here. Do you want to replace it?" first
(its "Alert before overwriting cells" option, on by default).
The drop now runs a new `range.move {range?, to, overwrite?}` command:
a cut and paste that returns a confirm question when the destination
holds data other than the moved cells; the grid shows it with OK and
Cancel, and OK runs it again with `overwrite: true`. A refused move
leaves the sheet and the clipboard as they were.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>