`style:writing-mode` `rl-tb` (or `rl`; ODF 1.3 §20.404) on paragraph
properties opens as a bidirectional paragraph, from automatic and custom
paragraph styles alike, and is written back on the paragraph's or the
custom style's properties. Right-to-left paragraphs opened left to right
and left-aligned.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBVQLyDnrG8Tn1yDQcUabC
`text:outline-style` (ODF 1.3 §16.34) numbers the headings of its
levels. On open its levels (format, prefix, suffix, levels shown, start)
become one list, each level linked to its heading style, and Heading
1-6 number their paragraphs through it, as Word's numbered headings do:
LibreOffice's "1. / 1.1." headings keep their numbers (they were lost).
Levels without a number stay unnumbered.
Export writes a `text:outline-style` from the numbering of the heading
styles; levels without one show no number.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBVQLyDnrG8Tn1yDQcUabC
Cell borders (`fo:border*`: width, style, colour; ODF 1.3 §20.183) and
padding (`fo:padding*`) are read from table-cell styles. LibreOffice
gives an edge two cells share to one of them only (the left cell's
right side `none`, the right cell's left side drawing it); on open the
line goes to both cells, as WordCraft's cells each draw their own sides.
A table whose cells all say "no line" opens without a grid.
Export writes each cell's padding (its own, its table's or Word's
default) and its borders with each shared edge once, as LibreOffice
does (left and bottom, the first row's top, the last column's right), so
lines no longer double; cells used a fixed 4 pt padding and a grid even
in tables without lines.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBVQLyDnrG8Tn1yDQcUabC
On open, the file's own Heading 1-6, Title, Quotations and Preformatted
Text styles (named or displayed so, their whole chain but Standard)
give WordCraft's matching styles their font, size, bold, italic,
colour, spacing, indents, alignment and keep with next: a LibreOffice
heading is bold Liberation Sans, no longer WordCraft's blue heading.
`fo:font-size` percentages are read as a share of the inherited size.
On save, those styles are written from the document's own definitions
in full, instead of fixed ones, so a WordCraft heading looks the same in
LibreOffice. The styles test document now round-trips pixel-identically.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBVQLyDnrG8Tn1yDQcUabC
Tab stops (`style:tab-stops`, ODF 1.3 §17.7-17.8) open with their
alignment (left, centre, right, decimal) and leader (dot, hyphen,
underline, middle dot), and are written back on the paragraph's style;
the default tab interval (`style:tab-stop-distance`) round-trips.
LibreOffice measures tabs from the paragraph's indent when its
`TabsRelativeToIndent` setting is on (its default); opening follows the
setting, and saved files carry a settings.xml that turns it off, so
positions run from the margin as in Word and WordCraft.
The flow model carries the tabs in `Para::extra` only for flows that
ask (FlowOpts::paras).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBVQLyDnrG8Tn1yDQcUabC
`style:text-properties` beyond bold, italic and colour now open and
save: underline styles (double, thick, dotted, dashes, wave, words only)
and colour, double strike, caps, small caps, hidden text (kept, hidden),
letter spacing, scale, text raised or lowered at full size (a position,
not superscript), outline, shadow, emboss, engrave and language.
A monospaced font keeps its own name; it was read as "code" and written
as Courier New. The default style's pair kerning (`style:letter-kerning`)
is read into the document defaults and written back, so lines break
where LibreOffice breaks them (round-trip renders of the test documents
are now pixel-identical in LibreOffice).
The flow model carries this in `Fmt::extra` only for flows that ask
(FlowOpts::chars); the other formats are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBVQLyDnrG8Tn1yDQcUabC
Each level of a list style (ODF 1.3 §16.30-16.33) is read with its
number format (1, i, I, a, A, none), prefix and suffix, levels shown,
start value and bullet character, and written back. Each `text:list` is
a list of its own unless it continues the one before it
(`text:continue-numbering`) or the one it names (`text:continue-list`);
an item's `text:start-value` restarts from there. A list inside a note
or table cell leaves the list around it alone.
Before, consecutive lists merged into one: three LibreOffice lists
numbered 1., i. and A. opened as one decimal list 1-7.
The flow model's ListInfo carries a list number and a compact label
(prefix and suffix up to 12 bytes, so "Chapter %1" fits) only for flows
that ask for them (FlowOpts::lists); the other formats are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBVQLyDnrG8Tn1yDQcUabC
`office:annotation` (ODF 1.3 §14.1) opens as a document comment with its
author, date, initials and text; `office:name` and
`office:annotation-end` (§14.2) give its range, across paragraphs too.
LibreOffice's resolved state (`loext:resolved`) and reply threads
(`loext:parent-name`) are kept: a reply is a comment of its parent with
no range of its own. An end with no comment is dropped, and a comment
whose end never comes covers nothing. LibreOffice's all-zero date is no
date. Before, comments were dropped on open.
Export writes each comment named, with its end where its range ends,
and its replies after it naming it; LibreOffice keeps the threads when
it saves the file again. Truncated XML no longer loses the paragraph a
note or comment sits in.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBVQLyDnrG8Tn1yDQcUabC
`text:note` (ODF 1.3 §6.3) opens as a note story built as Word's: its
paragraphs in Footnote Text or Endnote Text, the first starting with the
note's own mark, the reference in the text in the reference style, and a
`text:label` custom mark kept. The paragraph a note sits in (with its
spans, list and table cell) is put aside while the note's content is
read, and goes on after it. Notes inside notes, in deleted text or in
frames are left out. Before, notes were dropped on open.
Export writes each note with a citation numbered as LibreOffice shows
it (footnotes 1, 2, 3; endnotes i, ii, iii) or its custom mark, its
paragraphs in Footnote or Endnote styles defined in styles.xml. An
insertion open around a note ends before it and goes on after it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBVQLyDnrG8Tn1yDQcUabC
Export writes each section's headers and footers into its master page
(style:header, -left for even pages, -first for a different first page;
an empty element where Word shows nothing), with their automatic styles
in styles.xml under names of their own. Word's page-edge header and
footer distances become ODF's page margin plus header/footer band
height, and import converts back, so headers sit where LibreOffice
draws them.
Text fields (ODF 1.3 §7: page number and count, date, time, document
information, bookmark references) open as WordCraft fields with their
shown text as the result, and are written back as ODF fields. Fixed
dates and fields without a Word equivalent stay text; Word fields ODF
lacks are written as their result. The flow model carries fields only
when asked (FlowOpts), so the other formats are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBVQLyDnrG8Tn1yDQcUabC
* #4734: the print job's PDF leaves out link annotations
craft-print refuses to fit a page with annotations to the paper ("flatten page annotations
before using shrink-to-fit"), and the desktop app always prints with ShrinkToFit, so any
document with a hyperlink or a TOC (its entries are links) failed to print. Links print
nothing; `PdfOptions::links` (default on) lets the print job leave them out. Exported PDFs
keep them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #4734: review — the print note is in every desktop column; gaps and progress log
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #4734: doc wording: the print PDF omits link annotations; hardware still to validate
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #4734: only the printer's PDF drops links; Save these pages as PDF keeps them
Session::print_job_pdf is what ui.printSubmit sends to the spooler, without
link annotations; Session::print_pdf (file.printPdf) keeps them again.
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>
* #4764: copy selected objects along an axis with Ctrl+Shift
* #4775: preserve aligned copies through scrolling and secondary selection
* docs: keep the gaps.md note in the status line and revision table
* docs: drop a stray blank line
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* #3631: index laid-out objects during connector rerouting
* docs: keep the gaps.md note in the status line and revision table
* docs: drop a stray blank line
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* #4848: corporate authors in bibliographies
A Word source whose author is an organisation (Create Source › Corporate Author)
stores it as `b:Corporate`, which the reader ignored: the source opened with no
author, citations fell back to the title and bibliography entries started with
the year. A bare name typed into Author was split into "Organization, W. H.".
- Source gains `corporate`, read from and written as `b:Corporate` (in place of a
name list: a role holds one or the other, ECMA-376 CT_NameOrCorporateType).
- Citations and bibliography entries write it as it is in every style; it gives
the source's tag and its place in an author-sorted list, and untagged sources
are matched by it.
- APA puts its full stop after a group author ("World Health Organization.
(2021).") instead of relying on the initials for it.
- Create Source has a Corporate Author box under Author; Edit Source opens with
it checked when the source has one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LvG6DxS4tKmWSLCiDPvb4G
* Match untagged sources by corporate author before personal author
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* M12: preserve custom-property bookmark links (#4767)
* M12: preserve bookmark links across property renames (#4767)
* M12: align custom-property preservation status dates (#4767)
* docs: keep the gaps.md note in the status line and revision table
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Row, column and cell settings applied to the caret's row, column and cell only, so
selecting the whole table and unticking "Allow row to break across pages" changed
one row and the others still split across pages.
- table.properties applies row height and rule, allow break and header row to every
selected row, the column width to every selected column, and the cell width and
vertical alignment to every selected cell (the caret's with nothing selected), in
one undo step.
- The dialog shows "Allow row to break" and "Repeat as header row" as mixed when the
selected rows differ; left alone they change nothing, clicked they set every row.
- The row-height rule box is named by "Specify height" for screen readers.
Claude-Session: https://claude.ai/code/session_01LvG6DxS4tKmWSLCiDPvb4G
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* M2 #4903: step each selected run from its own font size
* docs: keep the gaps.md note in the status line and revision table
* docs: drop a stray blank line
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* 4757: keep Arabic base letters when removing marks
* docs: keep the gaps.md note in the status line and revision table
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Fix#4750: walk note references and formatting runs together
* docs: keep the gaps.md note in the status line and revision table
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
The RTF reader only recognised \pngblip and \jpegblip, so a \pict
holding \wmetafile8 (how Word writes pasted and floating pictures) or
\emfblip was dropped and the document opened without its pictures.
Keep them as wmf/emf media when the bytes are a metafile (the extension
comes from the bytes; raster bytes go through the usual image path,
anything else is dropped), sized from \picwgoal/\pichgoal and the scale,
else from \picw/\pich (0.01 mm, or pixels under \wmetafile1; zero or
negative falls back to the default). The metafile renderer already
draws them and the DOCX writer saves them as .wmf/.emf.
RTF export dropped WMF/EMF pictures as well (it re-encoded every other
format through the raster decoder): it now writes \emfblip with the raw
bytes and \wmetafile8 without the placeable header, \picw/\pich in
0.01 mm.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* M1 #4658: bound Markdown reference-label work
* #4658: align Markdown progress status dates
* docs: keep ROADMAP's second status line as on main
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Formats: preserve HTML input newlines (#4810)
* docs: fold the #4810 notes into the existing status lines
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Match format_range for whole-cell segments and ordinary targets, including empty
paragraphs. Partial text columns leave their unselected marks out of the check.
Keep the large-run fixture prefix mark blue so it cannot mask the tail regression.
Before the fix, whole-cell and ordinary-mark regressions failed while the other5
highlight controls passed. All7 now pass, including inherited/empty cells,
unselected neighbours, empty-only ranges, partial columns and Undo/Redo.
Full cargo xtask ci passed all8 gates with serial tests. Previous head's CodeRabbit
docstring measurement95.24% exceeds80%; all3 new tests have documentation.
Native Word comparison remains pending. No new interface strings.
Merge upstream9d1067fc, preserving both progress-document histories.
Copy the session highlighter choice into command previews, and verify that the
preview toggles the chosen colour without changing the document or undo history.
Document the shared-registry constructor as well as the public constructor.
Validation: complete integrated cargo xtask ci passed all8 gates, including all4
highlight regressions; RUST_TEST_THREADS=1. Local named-function doc audit16/16.
Native Word comparison pending. No new interface strings in this fix.
Resolve highlight runs one at a time without the formatting-state query cap.
Pin mixed and inherited tails at 10000/10001/10002 runs, including Undo/Redo.
Document the changed command/session/helpers and explicit palette setter behavior.
Validation: red regression at10001 runs; all3 highlight tests pass after fix.
Full cargo xtask ci passed all8 gates:3963 passed,0 failed,4 existing ignored.
RUST_TEST_THREADS=1. Local named-function doc audit:13/13 documented; CodeRabbit
uses its own analyzer. Native Word comparison remains pending.
Remember the session highlighter choice and toggle it when no explicit colour is given. Keep explicit palette/automation setters and No Color semantics, and preserve undoable formatting.
Two command/key regressions and four headless UI screenshots passed. Full cargo xtask ci passed all 8 steps with 3962 passed, 0 failed, 4 existing ignored using RUST_TEST_THREADS=1. Initial parallel run hit an unchanged Russian suggestion timing assertion; isolated unchanged binary passed at13ms. Native Word comparison pending.
Use the pre-spacing origin only for paragraph-relative box floats. Expand
regression coverage for line anchors and frameless page/margin fallbacks,
and document the cap conversion helpers and synthetic regression contracts.
Merge upstream main through 478fd445 and reconcile the roadmap progress log.
Native CI stages passed; the WASM gate passed on a targeted rerun after a
local sandbox linker adjustment. Optional catalog/font inputs were absent.
Co-Authored-By: GPT-6 <codex@openai.com>