UPDATE_APP (native builds; craft-update is a native-only dependency) carries our 128 px icon and accent (#3f5fe0, its colour in ArtCraft Launcher). The updater, the self-update
helper and craft-update's startup prompt all get them through crate::UPDATE_APP; the
prompt draws the icon, the accent and "by ArtCraft" (craft-update's default developer).
craft-update is pinned to storytold/craft-libs main at 1065653e7 (#434).
* #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>
Chart and diagram text was shaped in one face, the document's body font,
so Chinese legends and axis labels showed boxes while body text was fine.
Characters the face lacks now go to a fallback face, one face per run
when one covers it all, as in body text; a bold fallback face without a
bold style is emboldened.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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>
* #4411: captions in a style based on Caption are cross-reference targets
Cross-reference and Table of Figures found captions only by the exact
Caption style, so a caption restyled with a style based on Caption (a
label's own "Caption Table") was left out: of five tableX captions,
only the two still in Caption were offered. A caption is now a
paragraph whose style is Caption or based on it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #4411: test captions two styles from Caption and the Table type
Covers a style based on a style based on Caption, a Table caption in
such a style offered under the built-in Table type, and a caption
label used only in a style not based on Caption staying out of the
cross-reference types.
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>
* #4086: highlighting at a caret leaves the word alone
* #4086: the highlight toggle reads pending caret formatting; word formatting updates it
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Backspace after a floating shape's anchor joins paragraphs and keeps the shape (#4592)
* wc-upkeep-6: protect Backspace joins and markers
---------
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>
* #3674: PDF export writes a glyph id above 65535 as .notdef instead of failing
Shaping gives 32-bit glyph ids (a format-12 cmap can map a character to
any of them), and the PDF exporter passed them to krilla unchecked.
krilla's CID font takes 16-bit ids and unwraps the conversion
(vendor/krilla/src/text/cid.rs:161 at craft-libs b926b5c0), so one such
glyph failed the whole export natively and aborted the tab on wasm.
The three places that build a KrillaGlyph (body text, metafile text and
the text watermark) now go through glyph_id(), which writes an id that
doesn't fit as glyph 0. The glyph keeps its place and its text range, so
the rest of the run is unchanged.
Regression test: glyph_id_above_u16_exports lays out a paragraph, sets
one glyph's id to 70000 and exports the layout. It failed before with
"PDF export failed internally: ... TryFromIntError(PosOverflow)".
* #3674: the replaced glyph keeps its place, not its text
krilla writes no ToUnicode entry for glyph 0, so a glyph written as
.notdef doesn't extract: "Wide glyph" with the W's id set to 70000
extracts as "\u{FFFD}ide glyph". The rest of the run extracts as
before. The comment on glyph_id() said the glyph kept its text.
* Adapt glyph_id imports to main's exporter text module
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* #4696: built-in heading styles have their outline level without w:outlineLvl
Some producers write "heading 1"…"heading 9" in styles.xml without w:outlineLvl. Word
gives those built-in styles their outline level anyway (checked: Word for Windows reports
OutlineLevel 1 and 2, whatever the style id), so their headings show in the Navigation
pane. WordCraft read no level, and the Headings tab stayed empty.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #4696: review — w:customStyle="on" marks a custom style too
A custom style named "heading 4" with customStyle="on" (ST_OnOff) was taken for Word's
built-in heading and got an outline level.
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>
* #4548: comment ranges inside footnotes and endnotes are kept on read
read_opened read footnotes.xml and endnotes.xml before comments.xml, so
a comment range inside a note looked its id up in an empty comment map
and was dropped. Comments are now read first. read_comments uses nothing
the notes set up.
Regression test: tests/it/note_comments.rs (synthetic markup).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: file-format-parity row for comment ranges in notes (#4548)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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>
Found while testing crates/formats/src/markdown/export.rs:167-182 (inlines_md_impl, emphasis marker emission)
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>
Found while testing crates/formats/src/latex/export.rs:402-444 (escape_text, `prev` resets per call) and :230-237 (inlines, runs concatenated)
Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Found while testing crates/formats/src/html/builder.rs:338-344 (Builder::text)
Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* fix(formats): saturate HTML <font size> offsets so extreme values cannot overflow
* test(formats): assert the saturated HTML font sizes, not only that the text survives
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Found while testing crates/formats/src/model/color.rs:45-47 (parse_color, 8-digit hex branch)
Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Found while testing crates/formats/src/sheet/date.rs:136-152 (iso_time, PT duration branch)
Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
The Linux same-user check read the uid of the listener's side of the
connection from /proc/net/tcp. Until the listener accepts it, that row has
no socket file (inode 0) and reads uid 0 whoever listens, so a Zotero that
hadn't accepted yet was refused as "another user (uid 0)". The ui-egui
Zotero tests failed about one run in three on a busy machine because of it.
Rows without a socket file (unaccepted connections, TIME_WAIT) are now
skipped, and the listening row on the listener's address stands in for the
connection's own; two users listening there is refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Ctrl+V pastes a clipboard picture again with egui 0.37
Since the egui 0.37 migration, egui-winit keeps both the Mod+V press and
its release, so the canvas never sees the V release it pasted a clipboard
picture on. Outside Wayland, egui-winit instead sends Event::PasteImage
when the clipboard holds a picture and no text, which the canvas ignored.
Ctrl+V pasted nothing while the ribbon and context-menu Paste still worked.
Answer PasteImage with edit.paste, which reads the picture through the host
like the ribbon's Paste. The web build sends no PasteImage, so nothing
pastes twice there.
Part of #4782
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Mark the clipboard picture as pasted on PasteImage so a later V release cannot paste twice
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
File › Options › Proofing › AutoCorrect Options did nothing on the Start
screen: tools.autocorrect wasn't in works_without_document, so the click
was dropped. Its options are user preferences (session.prefs), like the
other File › Options settings, so it now works without a document.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Shading buttons show the last picked colour as their bar (#4412)
The paragraph and table Shading buttons always drew the icon's yellow bar,
whatever colour a click applied. They now draw the remembered colour as the
bar, as Font Color and Text Highlight Color do (#4087), on an icon without a
bar of its own.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* docs: ui-parity row for the Shading button colour (#4412)
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
With the painter armed, only dragging across the text applied the copied
formatting: a double-click (a word) or triple-click (a paragraph) selected
the text and returned before the painter was used. Both paths now go through
one helper, so the click paints what it selected and the painter goes off.
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Options: a blank User Name stays blank after a restart (#4368)
* docs: ui-parity row for the blank User Name (#4368)
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Inserting WordArt selects its placeholder text, so typing should replace it. From the Insert
gallery the first keystrokes went nowhere: the gallery's cells (and the Text Fill, Text Outline,
Glow and Transform galleries beside them) ran their command with `app.run` instead of
`run_from_ribbon`, so the canvas never asked for focus back and the page ignored the keys.
They now go through `run_from_ribbon` like every menu item.
Regression test renders the style gallery, picks a style and checks the page asks for the
keyboard; it fails without the change.
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Adds the key that Apache POI's Excel-written test file
protected_passtika.xlsx (password "tika") derives, computed from
[MS-OFFCRYPTO] §2.3.4.7 independently of the implementation, so real
Word 2007 files stay pinned.
Also adds the contributor's people.toml entry.
Co-authored-by: Bra1nZ <bra1nz@astraoffice.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* M14: validate window fallback geometry and avoid duplicate symbol names
Verified regression cases and full cargo xtask ci before publication.
* M14: document review helpers and regression contracts
Documentation-only follow-up; full cargo xtask ci passed.
---------
Co-authored-by: kalmarr <680152+kalmarr@users.noreply.github.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Ported onto current main (title_stem now lives in app/frame.rs; the window
title is built in windows.rs).
Co-authored-by: Lucenx9 <185146821+Lucenx9@users.noreply.github.com>
Count table opens in a single linear pass instead of rescanning
earlier events for every lone table-open marker (O(events) instead of
O(lone * events)).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* i18n: Traditional Chinese interface draws hanzi with an installed TC font
The zh-hant interface shared the Simplified Chinese font order, so its first
CJK face was the embedded Noto Sans CJK SC and traditional characters were
drawn with mainland glyph shapes (說, 資, 這, 為, 戶).
On the desktop, a Traditional Chinese interface now puts an installed
Traditional Chinese font before every CJK face: PingFang TC on macOS,
Microsoft JhengHei on Windows, Noto Sans CJK TC on Linux. Noto Sans CJK SC
stays after it as the fallback. Without an installed TC font, and on wasm,
the order is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Credits: add TobyWu666 to contributors/people.toml
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* i18n: find Traditional Chinese fonts at the root of a font folder
Linux user font folders (~/.fonts, ~/.local/share/fonts) often hold font
files directly; the Traditional Chinese candidates only named files in
distribution subfolders, so those fonts were missed (CodeRabbit review).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* i18n: skip the Traditional Chinese font test without embedded catalogs
Since the catalogs come from craft-i18n (#4072), a build without
CRAFT_I18N_DIR is English only, so the test now skips like the other
catalog tests. Also rustfmt hant_font_chars.
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>
* #1103: ignore macOS launch arguments and name the real cause when a file won't open
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* #1103: filter launch args on macOS only; leave Backstage after a failed Finder open
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* #1103: leave Backstage after a failed drag-and-drop open
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* #1103: name the file when an open fails after the Save changes prompt
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* #1103: report failed opens from every deferred path in execute_user
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* #1103: keep the named failed-open message after a browser save
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* #1103: only opens held back by a prompt or save leave Backstage on failure
A direct open from Recent keeps File open and reports there (#2938).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* #1103: re-apply open-failure reporting on main's split ui-egui modules
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Qualify i18n paths in report_open_failure after merging main
---------
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* #1744: WIP
* #1744: Select Table copies and cuts the whole table
Copy of a whole-table selection used to keep only the cells' paragraphs
(copy_blocks flattened a cross-cell range), and Cut only cleared the text.
Now Copy puts the table on the clipboard and Cut removes the table.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #1744: selected table rows and cells copy as a table; Cut clears their text
Copy of whole rows, cells of a row or a block of cells puts a table of just
those cells on the clipboard (Table::slice). Cut of rows or cells clears their
text and keeps the rows and cells, as in Word (Word check A).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #1744: review fixes
Table::slice widens every row to a merged cell partly selected (no ragged
copy); an empty selected table or cell copies and cuts; a text selection over
exactly a nested table copies it; one selected_table per Cut; tests for
slice merges/widths/controls, tracked and locked cuts, and the text route.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #1744: Cut clears every cell Copy takes; Spike stays off for empty cell selections
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* #1744: a copied part of a percentage-width table keeps a point width
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* ROADMAP: tidy merged header
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* ROADMAP: keep only the #1744 rows over main's version
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* ROADMAP: remove conflict markers; keep only the #1744 rows over main's version
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* #1744: rename the range Cut helper clear_range_cells (main added clear_cells)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Remove conflict markers from ROADMAP.md; leave main's header line alone
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* cargo fmt
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Import Path in table structure after merging main
---------
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>
* Tables: share span minima by preferred widths (#1883)
* 1222: Match nested preferred span weights
* docs: word the #1883 layout-parity entries
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>