SonarCloud's analysis of main at b4da59f0c lists 23 closures such as `|v| v.as_slice()` that hand their argument straight to one method. This passes the method itself: `Vec::as_slice`, `str::to_ascii_lowercase`, `f64::total_cmp` and the like, all forms the repo already uses elsewhere. Behaviour is unchanged.
Five stay. The four in crates/proof/src/es/mod.rs sit in code that #4191 replaces, and its replacement already uses `PoisonError::into_inner`. In crates/engine/src/io/safe_save.rs the path would be `<dyn std::error::Error + Send + Sync>::is::<Guided>`, which reads worse than `|inner| inner.is::<Guided>()`.
* chore: resolve SonarCloud maintainability issues
* fix: restore compiling refactors, revert behaviour changes, run cargo fmt
- fontdb::norm: keep the closure for filter (char::is_alphanumeric takes
self, not &self, so it does not fit Iterator::filter).
- story.rs: write_mark_rpr needs &mut self (rev_attrs, rpr_change).
- Revert the viewport meta change and the return 0 added to
workspace_version/sha256, which changed behaviour or masked failures.
- cargo fmt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Import Document in odt page_parts after dropping glob re-export
* fix: apply_kind_properties binds ContentControl; cell_ref keeps char closures
* fix(engine): pass case helpers by value (clippy needless borrows)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Dylan Taylor <dylan@craftmaker.dev>
Pin craft-fonts a71c20e3 in ci.yml, freebsd.yml and release.yml; web-craft-fonts so the web
build keeps the Geor face; the interface font definitions add it; the Thai run test
accepts the embedded Thai face that pin brings (#3561).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
ROADMAP.md: status line once (this change first, then main's); progress-log and history rows union in date order
docs/layout-parity.md: status line once (this change first, then main's); history rows union in date order
Word for Mac spaces Symbol lines at 1.222 em (measured at 11 and 20 pt)
even with Apple's Symbol installed, whose own line is one em. Symbol's
metrics now apply when the resolved face is one em tall as well as when
the font is substituted (fonts::word::symbol_line).
fallback_for_run cached its answer under the face count read after the
search. A face loaded in between by another thread (its own search, or a
family a document names) made a miss look current: later lookups at the
new count got the stale miss (review on #3078). The search now returns
the count of the loaded faces its last choice was made from, and the
answer is cached under that; anything loaded since makes the next lookup
search again. No lock is added, so cached lookups from parallel layout
stay contention-free.
A missing Khmer OS font drew in whichever loaded Khmer font covered the text, usually a
wider Khmer OS face, so lines wrapped and fixed-height text boxes hid their last line.
Word uses the system Khmer font; so do we now.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Two holes in the whole-run fallback search (review on #3078):
- The coverage index keeps one coverage per file, so a font collection
whose faces only together map a run matched it; the search loaded that
family and stopped, returning no face even when a later font covered
the run. It now goes on to the next file that maps the run.
- A single-character fallback may have loaded one file of a family (its
Italic, say). The run search then treated the family as loaded and
added only the file it reached first, so a regular run could be drawn
in Bold although the Regular file was installed. It now loads whatever
of the family isn't loaded yet.
Files loaded by family name are now recorded in the index too, so no
search loads a file a second time.
Tests: a split-coverage collection before a covering font; a family
whose Italic a character fallback loaded, reached at its Bold file first.
Both fail without the change (no face; Bold).
On Linux the run fallback loaded the script's preferred family (Noto Sans
Thai, which has no Latin letters) and stopped: every character had a face,
so an installed font mapping the whole run (IBM Plex Sans Thai) was never
tried, and a Thai + Latin run was split across two faces. Three Thai tests
failed on machines with Noto Sans Thai installed.
When no loaded face covers the run, the search now looks through the
coverage index for the first installed file mapping the whole run. It
loads that file's whole family, since path order can reach the Bold
file before the Regular. It also looks at the loaded faces again even
when it loaded nothing itself: a concurrent search may just have
loaded the font and taken its files off the index, and the miss would
otherwise be cached.
Regression test: a_run_no_preferred_face_covers_whole_loads_an_installed_font_that_does
(synthetic fonts, independent of what is installed).
* #2240: fonts Word embeds as subsets lay out the text set in them
Embedded fonts were registered under the family inside the font file. Word
embeds a subset ("embed only the characters used") under a made-up family
(___WRD_EMBED_SUB_46) and the text asks for it by the font table's name
(OBRBWW+Century Schoolbook), so it still fell back to a substitute. Each
embedded file now comes with its font table name, and its faces go by that
name when they don't already.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #2240: the black-circle fallback test passes its font's table name
main's new `circle_checks_face_coverage` test (#2582) called `add_document_fonts` with bare font
bytes; with this branch each embedded font comes with the name the document's font table gives
it, so the test passes "Times New Roman" alongside the face it renames to it.
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>
Old releases of heavy faces (the core-fonts Arial Black) claim weight 400 in
OS/2, so bold text in them was drawn with synthetic bold. Current releases say
900 and Word draws bold Arial Black unchanged. A static face whose file says less
than semibold now takes the weight its family's last word names: Black/Heavy 900,
ExtraBold/UltraBold 800. Files that already declare a heavy weight keep it.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
The system font scan (file_face_names) also catalogs faces with none of
the tables skrifa reads glyph outlines from (glyf, CFF, CFF2, VARC) and
none of the color bitmap tables the renderer draws (sbix, CBDT). On
macOS 27.0.1 the scan of /System/Library/Fonts catalogs "GB18030 Bitmap"
(Supplemental/NISC18030.ttf), and the font list shows it. The face
stores its glyphs as bdat bitmaps and maps 27,544 characters.
FontDb::outline returns an empty path for each of them, and the face has
no color glyph tables. The renderer draws none of them.
file_face_names now leaves out a face whose table directory has none of
those six tables. It reuses OUTLINE_TABLES from the interface CJK
fallback (#1250). VectorCraft's font scan checks the four outline tables
(storytold/vectorcraft#781). WordCraft's renderer also draws sbix and
CBDT bitmaps (#1504). Noto Color Emoji has CBDT and none of the outline
tables, and the scan keeps listing it.
A paragraph in a Thai font that isn't installed (TH Sarabun New, Angsana
New, Cordia New, the UPC families…) drew its Thai letters in one fallback
face and the Latin letters, digits and baht sign in a Latin substitute at
a visibly different size. Word substitutes one font (Cordia New) for the
whole run.
Thai families now have their own substitute lists (Cordia New and
Leelawadee UI on Windows, Thonburi on macOS, Noto Sans Thai and the TLWG
fonts on Linux; serif families prefer Angsana New, Sathu or Noto Serif
Thai), and the first listed face with Latin letters and digits as well
as Thai wins. When the substitute still lacks some of the run's Thai or
Latin text, the run moves to one face covering it through
FontDb::fallback_for_run, the generalised East Asian run fallback. Only
Thai fonts get this: a missing Latin font keeps Latin text in its own
substitute, a run with its own complex-script font keeps it for the Thai
letters, and a missing roman-classed Thai font is no longer drawn in
Cambria.
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Word sizes a line by the fonts in it, and a bullet in Symbol (Word's
default, U+F0B7) has a taller ascent than Calibri or Times New Roman.
Without Symbol (Linux, macOS without Office, the web build) WordCraft
draws the bullet from another face and used that face's height, so every
bulleted line was shorter than Word's: 14.5 pt against 15.1 pt in
Calibri 11 pt at 1.08 spacing, 14.9 against 15.7 in Times New Roman 12 pt.
symbol_font_metrics gives Symbol's and Wingdings' ascent and descent as
Word's PDFs state them, and a run in one of them that isn't installed
takes them. The label's ascent now comes from its style, so the existing
rule (the label's extra ascent added unscaled) sees them.
Measured in Word 2016: Calibri 11 / Times 12 bullets 15.08 / 15.72 pt,
WordCraft now 15.07 / 15.75 pt. On 251 real documents against Word PDFs,
page breaks at the same word 45.4% -> 50.1%.
#1613 draws a missing font in Cambria when the font table classes it as
roman, as Word does. Times New Roman is roman in every font table, so
where it isn't installed (Linux, the web build) every Times document came
out in Caladea: lines 2.1 pt taller at 12 pt and different line breaks.
Word always has Times New Roman, so it never applies that rule to it.
The rule now leaves alone a font whose substitutes are chosen by its name
(a metric-compatible clone or the same design under another name: Times,
Arial, Courier New, Palatino, the symbol, Hebrew and Persian fonts).
Fonts with no clone of their own (Cambria, Georgia, brand fonts) still
follow it.
Checked on 251 real documents against PDFs from Word: exact page counts
201 -> 229, page breaks in the same place 45.4% -> 51.9%.
Inside a Flatpak, /usr/share/fonts and /usr/local/share/fonts are the runtime's and the home font
folders aren't exposed, so WordCraft listed only the runtime's fonts. Flatpak mounts the host's
system, local and user fonts at /run/host/fonts, /run/host/local-fonts and /run/host/user-fonts
for every app, and its main branch (after 1.19.2) also mounts ~/.fonts at
/run/host/user-fonts-legacy when the XDG user font folder exists too; scan those as well.
Folders that don't exist are skipped, as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fallback put Segoe UI Symbol before Segoe UI Emoji, and Symbol has
monochrome glyphs for some emoji (✅), so they drew in black and white
and tests_color_glyphs failed on Windows. Characters with default emoji
presentation now rank the colour emoji fonts first, both when loading a
system fallback and when choosing among loaded faces.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(web): add option to load system installed fonts in dropdown
Add explicit 'Load System Installed Fonts…' button in font dropdowns for web (Wasm).
- Dispatches font.loadSystem via transient user activation on click
- Calls window.queryLocalFonts() to enumerate and load installed fonts into FontDb
- Invalidates font family cache in FontPreviews upon new font installation
- Complete localization across all 18 languages
- Credit GhostMitra in contributors/people.toml
* fix(ui, engine): canvas focus after chrome click and checked file access in sniff
* Revert "fix(ui, engine): canvas focus after chrome click and checked file access in sniff"
This reverts commit b0c925bf79.
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>