feat: a formula cell prints a currency and a date too
Both come from the document's language, read off Intl: the symbol's side and spacing, the short date's order and padding. Each minted data style names that language — without it LibreOffice prints its own locale's separators (probed: a German document's 1.234,00 € came out $1,234.00). A date is a serial day from LibreOffice's day 0, 1899-12-30. Word's formula dialog offers no date, so the DOCX leg uses the field's \@ picture. Also drops a document's name from three older comments.
This commit is contained in:
1 parent
9892377ee3
commit
9be1578b97
15 files changed
+239
-50
No files matched your search
@@ -90,7 +90,7 @@ formula in a Word document was silently dropped before this feature. `m:oMathPar
|
||||
|
||||
## A pasted formula is a picture, not a formula
|
||||
|
||||
Real documents mix both: a thesis will carry OMML formulas *and* equations its author pasted
|
||||
Real documents mix both: one will carry OMML formulas *and* equations its author pasted
|
||||
as raster images. Those import as `image` nodes and offer the image toolbar's wrap options on
|
||||
click — in Word and LibreOffice too. Nothing short of OCR changes that, so a click that offers
|
||||
wrap options instead of the formula dialog is the document's doing, not a broken import.
|
||||
|
||||
@@ -17,7 +17,8 @@ Covers the `table*.ts` extensions in `src/lib/editor/extensions/` and the table-
|
||||
- **`tableHeaderRow.ts`** (`TableHeaderRow`) — "header row" as a **styling preset** (not the structural ODF header that repeats per page, by design): `toggleHeaderRowStyle` sets/clears a **light-grey fill (`HEADER_SHADE` `#F2F2F2`)** on the first row's cells; `isHeaderStyled(state, axis)` drives the toolbar toggle's active state. **A table carrying a table style owns these two areas through its Table Style Options instead**: there `toggleHeaderStyle`/`isHeaderStyled` read and write `headerRow`/`firstColumn` in the table's `tableLook` (delegating to `setTableLook`), so the toolbar button and the gallery's checkbox are one state and can't disagree. Both also read their label from the same key (`styles.regions.*`), so they can't drift apart in wording either — the button used to say "header column" where the checkbox said "first column". The fill is the *marker* and **round-trips natively** as `fo:background-color`, so the toggle state survives reopen with no `table:table-header-rows` (⇒ Word/LibreOffice don't repeat it). **Bold is presentational, exactly like a heading**: `tableCellBackground.ts` tags a header-shaded cell with `class="cell-header"` and `editor.css` renders `.cell-header` bold — so *all* its text is bold (existing, typed, pasted, IME) without storing marks, and it stays **editable** via the `fontWeight:'normal'` override (the `Toolbar.svelte` Bold button treats `isInHeaderCell` like a heading: toggles `setFontWeight('normal')`/`unsetFontWeight`). Export (`export/odt.ts`) **bakes** bold onto header-shaded cells' runs (`applyRuns(..., forceBold)`, skipping `fontWeight:'normal'` runs) so Word/LibreOffice match; import (`import/odt.ts`) converts those cells' runs with `boldByDefault` (bold run → no mark, normal run → `fontWeight:'normal'`), mirroring headings. `TableToolbar.svelte` exposes the toggle button.
|
||||
|
||||
- **`tableFormula.ts`** (`TableFormula`) — a cell's **formula**, on the cell itself, which is where ODF puts it. The stored language is **Word's** (`SUM(ABOVE)`, `A1*2`, no leading `=`), the wider of the two; `utils/tableFormula.ts` holds the whole pure half — the tokenizer, the recursive-descent evaluator (`SUM PRODUCT AVERAGE MIN MAX COUNT ABS INT SIGN MOD ROUND`, ranges, Word's four directions, `+ - * / %` and parentheses) and the two translations. A **direction stops at the first cell holding no number**, a range does not. `evaluateTable` fills every formula cell in one pass, resolving a formula that reads another recursively behind a visited set, so a chain settles at once and a cycle yields 0 rather than looping. The `appendTransaction` recomputes on any doc change and writes the result as the cell's own text (keeping the first run's marks), **off the undo stack**; `editor.css` shades `td[data-formula]` the way both word processors shade a field, since typing over a result does not last. **ODF**: `applyCellFormulas` writes `table:formula="ooow:sum <A1:A3>"` (ABOVE resolved to the range it stands for — LibreOffice has no word for it) plus `office:value-type="float"`/`office:value`. The `ooow:` in that value is a **namespace prefix**: undeclared, LibreOffice reads it as part of the formula and prints "Expression is faulty", so `ensureOoowNamespace` declares it (probed both ways). **DOCX**: the cell's first paragraph becomes one `SimpleField` `=SUM(ABOVE)` with the result cached; import reads the instruction back off the cell. LibreOffice's own formula engine parses a cell's **text** in its locale regardless of number recognition (probed), which is why the editor formats a result in the document language.
|
||||
A formula cell can print its result in a **number format** of its own (`utils/cellFormat.ts`: the general one, whole numbers, two decimals, grouped, and the two percentages — LibreOffice's currency and date formats are not offered). ODF keeps it on the cell's **style**, as a `style:data-style-name` reference to a minted `<number:number-style>`/`<number:percentage-style>`; odf-kit shares one cell style across every cell, so `applyCellFormulas` mints a copy of it per format used. LibreOffice writes both back out unchanged (probed) — including from our `.docx`, which it does *not* read the format from: Word's `\#` field switch is where the DOCX leg keeps it, and LibreOffice's filter neither reads nor writes that switch.
|
||||
A formula cell can print its result in a **number format** of its own (`utils/cellFormat.ts`: the general one, whole numbers, two decimals, grouped, the two percentages, the currency and the short date). ODF keeps it on the cell's **style**, as a `style:data-style-name` reference to a minted `<number:number-style>`/`<number:percentage-style>`/`<number:currency-style>`/`<number:date-style>`; odf-kit shares one cell style across every cell, so `applyCellFormulas` mints a copy of it per format used. LibreOffice writes all four back out unchanged (probed) — including from our `.docx`, which it does *not* read the format from: Word's `\#` field switch is where the DOCX leg keeps it, and LibreOffice's filter neither reads nor writes that switch.
|
||||
The **currency and date are the document language's own**, read off `Intl` rather than a table of locales: `currencyParts` says which side of the number the symbol sits and what separates them, `datePattern` gives the short date's order, padding and separators. Each minted data style carries `number:language`/`number:country` — without them LibreOffice prints its *own* locale's separators and symbol instead of the editor's (probed, and the reason a German document's `1.234,00 €` came out `$1,234.00`). A date is a serial day count from **1899-12-30**, LibreOffice's own day 0 (probed: 45000 is 15 March 2023); a number no calendar reaches stays a number. Word's formula dialog offers no date format at all, so the DOCX leg writes the field's date picture switch (`\@ "dd.MM.yy"`) for us to read back — Word keeps showing the cached result. A **foreign** document's currency symbol and date order are read as "currency"/"date" and re-spelled in the document's language, which is all this closed set of formats can carry.
|
||||
- **`tableSort.ts`** (`TableSort`) — LibreOffice's Table ▸ Sort / Word's Layout ▸ Sort with the three keys both offer, each with its own direction and sort type; a later key decides only where the ones before it tie, and column `-1` is the key that is off. `sortedRows` is pure (rows in, rows out) and stable, so rows alike in every key keep the order they were typed in. The type is `auto` (numeric where both cells parse as numbers, else the collation), `number` — where a cell that is no number sorts last either way, as an empty cell does in a spreadsheet — or `text`, which is the **plain** collation: "10" before "2" is the whole point of choosing it, so `sortCollators` builds one collator with `numeric: true` and one without. `canSortTable` refuses a table with a vertically merged cell — such a cell belongs to more than one row, so there are no rows to reorder. The command rebuilds the table node in one transaction.
|
||||
- **Number recognition** (`storage/tableOptions.svelte.ts`) — LibreOffice's Table ▸ Number Recognition, **off** by default there (its `Writer/Table/Input/NumberRecognition` schema default) and so here. On, a cell whose text reads as a number is rewritten in the document language's general number format when the **cursor leaves it**, which is where LibreOffice applies it. It is an app option, not part of the file — LibreOffice keeps it in its own registry too — so only its effect travels. `setTableLanguage` is pushed from `App.svelte`'s language effect; the exporters take the same locale from the `language` argument instead, staying store-free.
|
||||
- **`tableCellAlign.ts`** (`TableCellAlign`) — `verticalAlign` (`middle`/`bottom`, null = top, the ODF and Word default) on `tableCell`/`tableHeader`, rendered as the cell's CSS `vertical-align`. A callout table's label sits mid-cell with it, at the row's top without. Round-trips to ODF `style:vertical-align` (import via `StyleResolver.cellVerticalAlign`, export via odf-kit's `CellOptions.verticalAlign` — it mints one cell style per property set, so a differing cell gets its own) and DOCX `w:vAlign` (Word's `center` is our `middle`; `top`/`both` stay the default). Set from `TableToolbar.svelte`'s three buttons via `setCellAttribute`, which covers a whole `CellSelection`.
|
||||
|
||||
Reference in new issue
Block a user