* gap 19: RTF import compares heading and code run formatting with the file's stylesheet
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* gap 19: review round 1: a style chain with formatting words is complete; unnumbered entries are Normal
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Found while testing crates/formats/src/markdown.rs:160-161,463 and crates/formats/src/model.rs:111 (ListInfo)
Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
Found while testing crates/formats/src/html.rs (export, around line 1744; paragraph text writer)
Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
* #778: .xls and legacy-encoding recipient lists; filter and sort recipients
- Recipient lists: legacy Excel 97-2003 .xls (BIFF8 reader on the cfb crate already in the
tree); Excel 5.0/95 and password-protected .xls refused with a clear message; a CSV named
.xls reads as text, an HTML one gets an error. .xls added to the picker filters (desktop,
web, built-in browser) and drop handling.
- Text lists (and plain-text import) detect legacy encodings when not UTF-8/UTF-16: GBK/GB18030,
Big5, Shift_JIS, EUC-KR, Windows code pages (encoding_rs + chardetng, MIT/Apache); an
`encoding` param overrides.
- mailings.filterRecipients / mailings.sortRecipients: field, comparison (equal, notEqual,
less, greater, lessOrEqual, greaterOrEqual, blank, notBlank, contains, notContains), value,
And/Or joins; sort keys. Preview, Finish & Merge and labels use only the matching records
in sort order. Edit Recipient List edits every record and keeps the filter.
- Recipient List dialog: Filter and Sort section and a merge-order column; catalog rows;
strings in every catalog; docs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* i18n: Hungarian and French strings for this change
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Review fixes for #885: total order for recipient sort, Bulgarian strings
- compare_cells now ranks numbers (compared numerically) before text (compared
case-insensitively), so it is a total order; a column mixing 9, 10 and 1a
could make slice::sort_by panic. Regression test sorts mixed columns.
- Bulgarian catalog: the 24 Filter and Sort strings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #1287: placeholder (work in progress)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #1287: keep the filter on a failed Edit Recipients; cap .xls/.xlsx cell text; dialog drops rules on unknown fields and caches the merge order
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #1287: rustfmt
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Found while testing crates/formats/src/rtf.rs:269-272 (Writer::para)
Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Keep the ODT paragraph 'Keep with next' option on import and export (#2813)
* file-format-parity: keep with next on the ODT row
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Found while testing crates/formats/src/odt.rs:1480 (finish_para calls p.trim()); crates/formats/src/model.rs:188 (Para::trim)
Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* fix(formats): support quoted and non-latin charset declarations in HTML decode
* html: clippy (question_mark, collapsible_if) after merging main
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* fix(formats): support relative font size values in HTML font tags
* Drop the duplicate divyanshu2074 entry in people.toml (already on main)
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* gap: RTF formatting revisions WIP
* gap 19: RTF formatting and paragraph-mark revisions
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* gap 19: review round 1: list kept on Reject; old formatting normalised; section marks; hostile borders
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* gap 19: review round 2: \oldcprops holds only the changed properties, as Word writes it
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* gap 19: old formatting in styled paragraphs uses the style's size and font
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* gap 19: RTF import keeps main's heading and code normalisation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* gap 19: file-format-parity status line keeps main's history
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* gap 19: a paragraph that was a list item gets its old list back on Reject
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* gap 19: a numbered item made bulleted keeps its numbered list for Reject
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* gap 19: format matrix names the tracked section mark
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* rtf test: Writer has the section fields main added; rustfmt
---------
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* rtf-sect-export: WIP
* Gap 19: RTF export writes sections
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Gap 19: RTF export writes sections — review fixes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* odt-sect-export: WIP
* Gap 19: ODT export writes sections
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Gap 19: ODT export writes sections — review fixes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* gaps log: RTF and ODT section export both done
* Gap 19: ODT continued lists share the earlier list's style; one RTF and ODT row in the parity table
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* odt-sect-export: WIP
* Gap 19: ODT export writes sections
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Gap 19: ODT export writes sections — review fixes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* ODT export: a list cut by a section continues with text:continue-list; drop duplicate parity rows
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Found while testing crates/formats/src/odt.rs:347 (writer, `plain_inlines`) and crates/formats/src/odt.rs:1550 (reader, `text`)
Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
* fix(formats): support none and dual scripts in mmultiscripts, mpadded phantoms, menclose borders, and mtable column align
* cargo fmt
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Collapse the nested if (clippy collapsible_if)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
LibreOffice puts a paragraph indented on both sides on the clipboard as
<blockquote>, its margins in the page's stylesheet. Paste mapped it to the
centred, italic Quote style. On paste only, it now becomes a Normal
paragraph indented by the blockquote's stylesheet margins (else 0.5 in each
side). Opening .html files still maps blockquote to Quote.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
A copy or cut of one equation on its own now also puts its MathML in the
platform's MathML clipboard formats, beside the HTML (with MathML, #816) and
the linear text: "MathML" and "MathML Presentation" (UTF-16LE) on Windows,
public.mathml.presentation and public.mathml on macOS. These are the names
W3C MathML 3 section 6.2.4 registers, and Microsoft's "MathML Support in
Microsoft 365" doc says Office checks them first when it pastes math. No
public clipboard format carries OMML. A copy with text around the equation
doesn't offer them, so Word keeps the text.
Not yet verified in Word: the parity docs say MathML clipboard formats, not
an editable paste.
Claude-Session: https://claude.ai/code/session_01P8veLveuUsJJm6UEcQPEuW
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Found while testing crates/formats/src/markdown.rs:253 (join_lines); also the trim_start() calls at lines 229, 311, 426 and 432, against escape_line_start at line 1077
Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
* perf: lowercase HTML tag names during collection to avoid double allocation
Inline .map(|c| c.to_ascii_lowercase()) into the tag name iterator chain
for both open and close tags, then drop the subsequent .to_ascii_lowercase()
call. Eliminates a second String allocation and copy per tag in the
HTML tokenizer.
* rustfmt
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* RTF import: Mac OS Roman text (`\mac`, `\fcharset77`) (#1653)
A Macintosh RTF document (`{\rtf1\mac …}`) had its `\'xx` bytes read as
Windows-1252: LibreOffice's fdo85889-mac.rtf showed "ñòó" instead of
"ÒÚÛ". Mac OS Roman (code page 10000, with the euro sign at 0xDB) joins
the single-byte code pages, and the reader uses it for `\mac` documents,
fonts with the Mac charset (`\fcharset77`) and `\cpg` / `\ansicpg` 10000.
Tests: the code page table (accents, euro, quotes) and two RTF cases, the
issue's file and a Mac-charset font in a Windows document.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* RTF import: DOS code pages (`\pc`, `\pca`, OEM `\cpg`) (#1653)
With the OEM tables from #1146 now on main, the RTF reader decodes DOS
text too: `\pc` documents as code page 437, `\pca` as 850, a font's
`\cpg` 437/737/850/852/857/866 and `\fcharset254` (PC 437).
`encoding::code_page_byte` decodes a byte of document text in any code
page RTF names (Windows, OEM or Mac OS Roman; ASCII below 0x80 stays
ASCII), and `is_byte_code_page` says which those are.
`SINGLE_BYTE_CODE_PAGES` stays Windows-only, so `text.typeCode`'s `ansi`
pages are unchanged.
A `\cpg` / `\ansicpg` number out of the u16 range is now ignored; before,
`pv as u16` truncated it to another code page (65973 read as 437).
Tests: document text in DOS, Mac and Windows pages; `\pc`, `\pca` with a
`\cpg437` font, and an out-of-range `\ansicpg`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
A shape anchored in a text:p (draw:custom-shape, draw:rect, ...) had its
own text:p read as a body paragraph. That closed the paragraph the shape
sits in, and the text after the shape was dropped. LibreOffice's
MadeByLO7.odt lost three of its seven body paragraphs.
Drawing shapes are now skipped with their text, as text boxes already
are. Groups are still walked, so the pictures inside them are read.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* #1336: Markdown looks up the backtick run closing a code span instead of rescanning the text
Code spans and link brackets searched the rest of the text for the run
of backticks closing a code span. Every run nothing closes (one per
length) cost a scan to the end, and the bracket scan repeated those
searches for each `[` over them, outside its look-ahead budget: 2 MB of
such runs took 2-18 s. The runs are now indexed by length once per text
and found with a binary search; what reads as a code span or a link is
unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #1452: Markdown brackets no longer turn later links in the paragraph into plain text
The link and image bracket scans share a look-ahead budget per text, so
hostile input stays linear, but it was charged more than they read: a
`[` nothing closes took the whole budget, and every bracket pair and
every link took the rest of the text, as if a destination had been read
to the end. An interval like `[0, 1)`, a dozen references in a long
paragraph or ten links were enough to make every later link plain text.
A scan now pays for the bytes it reads and records the pairs it passes,
and the brackets still open at the end of the text, so later brackets
are looked up. A destination is read, and paid for, only when `(`
follows the `]`; one that parses costs its own length.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The mime_of helper is missing SVG (most common web vector image) and
ICO formats, causing SVG data URIs and HTML img references to be marked
as generic application/octet-stream. Add image/svg+xml and image/x-icon.
Replace from_utf8(h).unwrap_or("zz") with .ok() and let_chaining.
The fallback was unreachable because h is always valid UTF-8 (it comes
from slicing a valid UTF-8 input string). The magic string "zz" made
the intent unclear to readers.
* Harden against hostile-document DoS and non-web link export
A security pass found several small files that make the app allocate or
compute without bound when opened (GUI, wordcraft-cli, or the MCP server),
and link targets leaving on export without the scheme check the HTML and
Markdown writers already apply. None lets a document run code or read files;
all are denial-of-service or credential-leak-on-click. Each fix bounds an
attacker-controlled size and lands with a regression test, per the
never-crash standard.
- layout: a table repeats its header rows on every page it spills onto, so
many tall header rows paginate to O(headers x pages) placed items. Cap the
total header-row repeats per table (lib.rs). 2 KB docx: 1.1 GB -> 124 MB.
- formats/model, html: an HTML table of many <tr> with large rowspan expanded
to rows x 64 covered cells, and `model::table` pre-allocated a full
rows x cols grid only to clear it. Cap rows at MAX_ROWS before
insert_covered, bound its total work, and build the grid template with one
row. 640 KB html: >1.5 GB -> 214 MB (a plain large table is faster too).
- docx/read/freeform: a custom-geometry Bezier segment took any number of
points, making de Casteljau flattening quadratic. Cap points per segment.
3 KB docx: 12 s -> 0.15 s.
- docbin: footnote/header/text-box story tables and the piece table can name
the same backing text many times. Hold the total characters materialised to
twice the WordDocument stream size (a ceiling no real file reaches, since
each char costs >=1 stream byte). 45 KB doc: >1.2 GB -> 14 MB.
- render, pdf, formats/rtf: document pictures were decoded with no dimension
limit, so a tiny file declaring a huge image could allocate before the size
check. Decode through image::Limits with a side cap first.
- doc/link, formats/rtf, odt, latex: route exported hyperlink targets through
is_safe_href (as HTML/Markdown already do), and extend it to reject
protocol-relative (//host) and UNC (\host) references. Keeps file:,
javascript:, ms-msdt: and UNC targets from being written into exported RTF,
ODT, LaTeX and PDF where a reader would follow them (UNC leaks NTLM on
Windows).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Align picture-decode limits with the bounds already in force
Review of the previous commit: `image::load_from_memory` and
`load_from_memory_with_format` already applied `Limits::default()`, whose
512 MB allocation cap bounded every picture decode. The PDF change had set
`max_alloc = None`, loosening that; restore the default. The render and RTF
side caps (16 384 px) newly rejected wide-but-short pictures that fit in
memory, so render now uses the pixmap's own `u16` side limit (what
`premul_pixmap` enforced after decoding, applied before it instead) and the
RTF re-encode keeps only the explicit allocation cap. Net effect: rejection
moves to header time with no change to the sizes accepted. Also fix a
comment: the header-row repeat cap is per table, not per document.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* #778: .xls and legacy-encoding recipient lists; filter and sort recipients
- Recipient lists: legacy Excel 97-2003 .xls (BIFF8 reader on the cfb crate already in the
tree); Excel 5.0/95 and password-protected .xls refused with a clear message; a CSV named
.xls reads as text, an HTML one gets an error. .xls added to the picker filters (desktop,
web, built-in browser) and drop handling.
- Text lists (and plain-text import) detect legacy encodings when not UTF-8/UTF-16: GBK/GB18030,
Big5, Shift_JIS, EUC-KR, Windows code pages (encoding_rs + chardetng, MIT/Apache); an
`encoding` param overrides.
- mailings.filterRecipients / mailings.sortRecipients: field, comparison (equal, notEqual,
less, greater, lessOrEqual, greaterOrEqual, blank, notBlank, contains, notContains), value,
And/Or joins; sort keys. Preview, Finish & Merge and labels use only the matching records
in sort order. Edit Recipient List edits every record and keeps the filter.
- Recipient List dialog: Filter and Sort section and a merge-order column; catalog rows;
strings in every catalog; docs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* i18n: Hungarian and French strings for this change
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Review fixes for #885: total order for recipient sort, Bulgarian strings
- compare_cells now ranks numbers (compared numerically) before text (compared
case-insensitively), so it is a total order; a column mixing 9, 10 and 1a
could make slice::sort_by panic. Regression test sorts mixed columns.
- Bulgarian catalog: the 24 Filter and Sort strings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* vi: mail merge Filter and Sort strings
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* DOCX: merge settings.xml into the original instead of regenerating it (#1034)
A save regenerated word/settings.xml from the model, so every setting the
model doesn't hold was dropped: compatibility options (lost in 18 of 18
real-world Word files), rsids, document variables, proofing state and the
colour scheme mapping.
The reader now keeps the original part when it holds such settings, and
the writer merges into it: the original root and every unowned child stay
byte for byte; owned children follow the model in CT_Settings order; in
w:compat only compatibilityMode is replaced. The privacy options are the
model's since #1062, so they are owned too. Kept children that point at a
relationship (a mail-merge data source) are dropped. Anything unexpected
falls back to the generated part. Files WordCraft wrote itself keep
nothing extra.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* DOCX: carry unmodelled parts and keep comment ids, style defaults, latent styles, list ids and app props (#1034)
Builds on the settings.xml merge.
- Parts the package or main document relate to that WordCraft neither
models nor writes (webSettings, stylesWithEffects, people, add-in task
panes, the thumbnail, future parts) are carried verbatim with their
relationships and content types; relationship types the writer
produces, and any the body refers to by id, are not (HANDLED_RELS).
- commentsIds / commentsExtensible: durable ids and UTC dates are read
into the model and regenerated against the written paragraph ids.
- w:default on localized default styles, w:latentStyles, lists'
nsid/tmpl/multiLevelType, and app.xml Template/Company/Manager/
HyperlinkBase round-trip; counts are not carried, and Company/Manager
are dropped when the file asks to remove personal information.
- Comment reference marks use the document's "annotation reference"
style id instead of a hard-coded CommentReference.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: przemek-arch <263159043+przemek-arch@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
ODT: every frame inflated its picture entry again, and the byte budget
was checked only after inflating, so frames pointing at an entry that
didn't fit (or at any entry once the budget was used up) inflated it
again each time. Each entry is now inflated at most once, and only when
the size it declares fits in what is left; the bytes inflated and every
later copy are charged, so an entry that inflates past its declared
size uses the budget up and nothing is inflated after it.
.doc: inline pictures remembered only successes, so a picture that
couldn't be read or didn't fit was located, decoded and copied again
for every anchor on it, and an inline shape's BLIP was decoded against
the whole media cap rather than what was left. Failures are remembered
too, bytes are copied and decoded only within what is left of the cap,
and nothing is read once it is used up.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* ODT: read and write tracked changes (gap 19)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
# ROADMAP.md
# crates/engine/src/tests.rs
# crates/formats/src/model.rs
# crates/formats/src/odt.rs
# docs/file-format-parity.md
# docs/gaps.md
* ODT tracked changes: deleted paragraph marks don't take the body's insertion; one region per deletion
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* #1381: WIP
* #1381: HTML import: bold <th>, cell text-align, region paper for HTML/MD/TXT
Header cells are bold unless their own CSS says otherwise. text-align on a
td/th, or inherited from its tr/row group/table, aligns the cell's
paragraphs; a paragraph's own alignment wins, and a table's align attribute
(which places the table) no longer counts. Files that give no page size
(HTML, Markdown, plain text) open on the region's paper, as a new blank
document does: file.open uses the session's region, wordcraft-cli reads the
system region the way the app does (Region::from_locale_vars, shared with
ui-egui). Tables size to that page.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #1381: the CLI uses the system region everywhere; help mentions WORDCRAFT_REGION
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* #816: copied equations reach other programs as MathML
A copy or cut whose selection holds an equation now also puts HTML on the
system clipboard, with each equation as Presentation MathML (wrapped in
<semantics> with the linear format as an application/x-unicodemath
annotation), so Word, LibreOffice, Google Docs and browsers paste a
formatted equation instead of `y=2x_s`.
- formats: new mathml module, a writer from the equation tree (runs,
fractions, scripts and pre-scripts, radicals, n-ary operators, delimiters,
functions, limits, accents, bars, border boxes, group characters, equation
arrays, matrices, phantoms) and a bounded reader (our annotation, else the
presentation elements, else a TeX annotation). HTML export writes
equations as MathML; HTML import (open and paste) reads <math> back.
- engine: edit.copy/edit.cut (and column copy) return `html` and keep
Session::clipboard_html when the fragment holds an equation.
- ui-egui: Services::set_clipboard_html; copy sites put HTML with the text
through it, falling back to egui's text copy.
- wordcraft: arboard set().html() with the text alternative; on Wayland
without data-control the text-only path is kept.
OMML on the clipboard (Word's "Office Open XML") needs custom clipboard
formats arboard can't set; left as a follow-up.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #816: honest clipboard docs; MathML reader fixes (token cap, function names, column vectors)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Text boxes: VML text boxes keep their internal margins, fit to their text and alignment
A VML text box's inset ("l,t,r,b", ECMA-376 Part 4 §19.1.2.22) was ignored,
so its text sat Word's default 7.2 pt in and 3.6 pt down even where the file
says 0,0,0,0, which is how letterheads and form headers place address blocks
exactly. It's now read like DrawingML's lIns/tIns/rIns/bIns: each value left
out keeps the default, a bare number is in EMUs, o:insetmode="auto" leaves
the defaults. mso-fit-shape-to-text (VML's "Resize shape to fit text") is read
as auto-fit, like a:spAutoFit, and the shape's v-text-anchor as the text's
vertical alignment. In a group, a member's margins go with that member's
text.
The reader feeds layout, so the editor, PDF and page images all change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Drawings: hidden shapes and pictures are kept but not shown
A drawing marked hidden (VML visibility:hidden, floating or inline;
DrawingML wp:docPr/@hidden, or a group member's cNvPr/@hidden) was drawn,
took room and wrapped text. Word for Windows shows it in no view; it's now
kept and saved hidden, but left out of layout: not placed, no wrap area, no
room in its line, nothing drawn, its text box's notes not placed. Exports to
other formats and copies leave it out, the accessibility checker no longer
asks for its alt text, and the selection pane says it's hidden.
Word for Mac draws hidden VML shapes; Windows follows the markup.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Naxvog <4963183+naxvog@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>