Commit Graph
932 Commits
Author SHA1 Message Date
bflatasticandClaude Opus 5.5 2c1a1526df Fix clippy on main: Create Outlines passes the text, not a reference to it
needless_borrow from #850, which a stale lint result let through.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 06:27:19 +07:00
BFlat 5b27fdc321 Merge pull request #845 from dotCooCoo/feat/copy-to-user-defined
Swatches: swatch.library.copyToUser copies a loaded library into User Defined
2026-10-10 06:21:36 +07:00
BFlat 338ed529e4 Merge pull request #850 from storytold/fix/847-underline-strikethrough
Fix #847: draw Underline and Strikethrough
2026-10-10 06:17:48 +07:00
bflatasticandClaude Opus 5.5 72c9cec0e0 Fix #847: draw Underline and Strikethrough
The styles were stored on the text and written to SVG's text-decoration,
but the type was drawn without the lines. vectorcraft_text::decorations
now gives each underlined or struck-through stretch of a run on a line
its bar, placed and sized from the font's own underline and strikeout
metrics (with fallbacks for fonts that have none). The canvas, Create
Outlines, PDF export (outlined and real text) and SVG's outlined text
draw them in the run's paint.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 06:03:04 +07:00
bflatasticandClaude Opus 5.5 4181755db6 Hyphenation exceptions: unit tests read only their thread's list
hyphenation_exceptions_override_the_pattern failed when the round-trip
test set the process-wide list at the same moment: words missing from the
thread's override fell through to the shared store.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 06:02:48 +07:00
BFlat c93d4ad995 Merge pull request #810 from Messhias/feat/394-hyphenation-exceptions
Part of #394: Hyphenation › Exceptions reach the hyphenator
2026-10-10 05:46:03 +07:00
dotCooCoo 975d4975ee Swatches: swatch.library.copyToUser copies a loaded library into User Defined
swatch.library.copyToUser {library} copies a library loaded from
another folder into the user library folder of the desktop app. A
.vcswatches, .gpl or .ase file is copied as it is; the swatches of a
document and a library loaded from the data or dataBase64 parameter are
written as a .vcswatches file. The copy gets a name that no file in the
folder has, and when a file with the same extension and bytes is
already there, that file is used. The library is then listed as User
Defined only. Until the app quits, commands given its loaded id use the
copy, unless another library is loaded under that id. Without a user
library folder (the web and headless sessions) the command is disabled.

A file with the same extension and bytes as a User Defined library
opens as that User Defined library. The unchanged original of a copied
.ase file is one such file. Graphic style libraries open the same way.
2026-10-09 13:32:59 -07:00
bflatasticandClaude Opus 5.5 78f0297127 Part of #812: an ellipse's Pie Start and End Angle and Invert Pie
An ellipse kept pie angles in its model, but nothing drew or set them. Now
the Transform and Properties panels show Ellipse Properties with Pie Start
Angle, Pie End Angle and Invert Pie, as in Illustrator, and
object.setLiveShape takes {pieStart, pieEnd, invertPie}. The pie runs
counterclockwise on screen from the start angle (0 at 3 o'clock) to the
end angle and closes through the centre; 0 to 360 is the whole ellipse,
an end of 0 means 360, and Invert Pie swaps the angles to show the other
part. One undo step each; a bad angle is an error. The labels are
translated in every complete language. The on-canvas pie widget is still
to come.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 03:00:15 +07:00
BFlat d857d2c0b7 Merge pull request #814 from jmverges/font-versions
Fix #813: type can name an installed version of its font
2026-10-10 02:44:09 +07:00
BFlat 9df597a46d Merge pull request #816 from storytold/fix/807-all-tools-drawer
Fix #807: All Tools shows every tool with its icon, under headings
2026-10-10 02:33:18 +07:00
bflatasticandClaude Opus 5.5 a262ee5c9e Fix #807: All Tools shows every tool with its icon, under headings
The toolbar's All Tools drawer listed the tools' names as plain buttons.
It now shows each tool with its icon and name in a grid, under the
toolbar's group headings (Select, Shapes, Draw, Modify, Type, Navigate,
Color) and headings for the tools only the Advanced toolbar shows
(Perspective, Symbols, Graph, Artboards, Slices). The active tool is
highlighted, a tool's tooltip gives its name and shortcut, and picking one
selects it and closes the drawer. The headings use labels translated
already.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 02:32:52 +07:00
bflatasticandClaude Opus 5.5 986b83280f Fix #808: the toolbar's scroll bar stays a slim line
The toolbar and the dock's icon column scroll when the window is too
short for them. Their scroll bar was egui's floating one, which widens to
10 pt while scrolled or dragged and covered the group labels and the
icons' right edge. It now stays at most 4 pt wide (2 pt at rest), at the
column's edge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 02:32:27 +07:00
Fabio William ConceiçãoandCursor 445bceb681 Part of #394: look up hyphenation exceptions under the lock; drop ~ syntax.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-09 20:26:56 +01:00
Fabio William ConceiçãoandCursor 4f9b292213 Part of #394: headless tests for Hyphenation › Exceptions UI and layout.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-09 20:26:56 +01:00
Fabio William Conceição c9ba5db4fb Part of #394: Preferences › Hyphenation › Exceptions reach the hyphenator.
Comma-separated words never break, or break only at marked hyphens; prefs.set and restore push the list so type reflows.
2026-10-09 20:26:56 +01:00
Juan Manuel Verges SolanasandClaude Opus 5.5 562e09707a Fix #813: type can name an installed version of its font
A font library can hold two versions of one family and style (a new
Montserrat-Bold.ttf beside an old montserrat.ttf). The font database kept
only the first one loaded, so type set in the other couldn't be matched
when a PDF or .ai was opened: it came in as outlines. Now every version
loads (the first stays the one family and style resolve to), PDF import
tries the other versions when the glyphs differ, and type set in a version
the family and style alone wouldn't pick names it in CharStyle::font_version
(left out when None, so documents without it are unchanged). Layout,
shaping, the substituted-glyph highlight, Find Font, Package and Document
Info follow it; changing the font clears it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 19:26:51 +00:00
bflatastic 8c1be41e08 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	ROADMAP.md
2026-10-10 01:56:46 +07:00
BFlat cbacb47fdb Merge pull request #770 from dotCooCoo/feat/swatch-libraries
Swatches: Save Swatch Library writes swatch exchange (.ase) files
2026-10-10 01:53:32 +07:00
BFlat 116573ba86 Merge pull request #782 from dotCooCoo/feat/package-font-resolution
Package: fonts found by any of their names are copied, and skipped fonts are named
2026-10-10 01:43:01 +07:00
BFlat 783540a34a Merge pull request #781 from dotCooCoo/feat/macos-registered-fonts
Part of #579: macOS fonts registered from other folders are listed too
2026-10-10 01:42:54 +07:00
BFlat 35a6015d88 Merge pull request #803 from haranishi/i18n-japanese-messages
i18n: Japanese translations for error and status-bar messages
2026-10-10 01:15:02 +07:00
BFlat 71ea5c4215 Merge pull request #802 from dmatviichuk/i18n/ukrainian-dictionary-review
i18n: correct Ukrainian wording and spelling
2026-10-10 01:14:55 +07:00
BFlat 3e1d9b7bbb Merge pull request #795 from DWAA1660/fix/selection-validation-and-clear
Validate selection IDs and clear exact compound members
2026-10-10 01:05:58 +07:00
Ikki NishiharaandClaude Sonnet 5.5 cee9feea2e i18n: Japanese translations for error and status-bar messages
Japanese now translates the status-bar and error messages (`@msg`
templates) like Spanish, French, Italian, Russian and Ukrainian do:
556 rows at the end of ja.tsv, the 498 message literals the test scan
finds plus the 58 that the other catalogs also carry (web start-up,
relinking, export notes, PostScript read errors).

`ja` joins COMPLETE_MESSAGES, so complete_languages_translate_every_message
keeps it complete. The registry comment, docs/development.md and the
ROADMAP line now list Japanese with the other five. The messages
themselves stay English for agents and MCP.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-10 02:53:14 +09:00
Dmytro MatviichukandCodex 01f329aa99 i18n: correct Ukrainian wording and spelling (#747)
Co-authored-by: Codex <noreply@openai.com>
2026-10-09 19:51:43 +02:00
bflatasticandClaude Opus 5.5 f300321e28 Part of #793: Artboard Options lists Name, Width, Height; one-key shortcuts follow the key on any layout
- Artboard Options listed its fields by key (Height, Name, Width); it now
  lists Name, then Width and Height.
- One-key tool and command shortcuts (V, P, X, D…) matched the character
  typed, so with a layout without Latin letters (Persian, Arabic, Russian,
  Greek…) they did nothing. When the text typed holds no ASCII character,
  the key's position stands in: the V key selects the Selection tool
  whatever it types. Latin layouts (AZERTY…) keep the letter typed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 00:51:18 +07:00
bflatasticandClaude Opus 5.5 0928cafab2 Native menu test: judge idle frames while the global generations hold still
key_equivalents_run_through_the_normal_shortcut_path_once failed about one
full ui-egui run in four ("the menus weren't read again", 5 vs 4). Each
app mirrors its shortcut overrides into a process-wide store every frame,
bumping shortcut_editor::GENERATION, and the plug-in registry's revision is
process-wide too. Tests running alongside with other shortcuts flip them,
which rightly makes the menus read again. The test now checks two idle
frames during which those generations held still (trying up to 50 times),
so it still fails if idle frames re-read the menus on their own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 00:36:14 +07:00
DWAA1660 636d9b24c8 Fix #786: clear exact compound members and explicit targets 2026-10-09 11:33:23 -06:00
DWAA1660 04439bb01d Fix #784: validate selection IDs and report the resulting selection 2026-10-09 11:19:08 -06:00
bflatasticandClaude Opus 5.5 bd8fa795ed Fix #776: the Pen continues any open path and joins two paths
As in Illustrator, the Pen shows its continue cursor over an end of any
open path, selected or not, and a click there selects the path and draws
on from that end (reversing it from its first end). While drawing, it
shows a join cursor over an end of another open path, and a click joins
the two into one path, in one undo step, which finishes the path.

path.join takes {ids: [a, b], ends: ["first"|"last", "first"|"last"]}
to join the ends asked for (the Pen's join); without them it joins the
selected paths' nearest ends as before. Joining a path with itself, or
anything that isn't a path, is an error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 23:59:50 +07:00
bflatasticandClaude Opus 5.5 4b44265965 Fix #734: Crop Image shows a crop box on the image
Object › Crop Image and the image's Crop Image buttons (Control bar,
Properties) now show a crop box on the selected image, as Illustrator
does, instead of cropping straight to the artboard (which did nothing for
an image inside its artboard). The box starts on the image's part over its
artboard; its handles and its inside drag it, always within the image, and
the image outside it is dimmed. The Control bar shows its X/Y/W/H with
Apply and Cancel; Enter applies and Escape cancels, both back to the
Selection tool. Apply runs object.cropImage {rect}, one undo step.

- tools: the Crop Image tool (cropimage.rs), with crop_box shared by the
  tool and the Control bar.
- ui: ui.cropImage (enabled with an image selected), the Control bar's
  crop fields and buttons, "Apply" translated (es, fr, it, ru, uk, ja,
  zh-hans, zh-hant).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 23:40:12 +07:00
bflatasticandClaude Opus 5.5 8124cf7b43 Clippy 1.99: as_chunks in the Affinity raster tests
#763 landed beside #771 with five chunks_exact(4) calls in
crates/affinity/tests/raster.rs, which clippy 1.99's chunks_exact_to_as_chunks
rejects, so cargo xtask ci failed at clippy on main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 23:15:36 +07:00
dotCooCoo 75f2145b92 Package: fonts found by any of their names are copied, and skipped fonts are named
File › Package and Document Info › Font Details looked fonts up with
FontDb::face and compared the face's family with the name the document
stores. A font a document named by another of its names (a PostScript
or localized name, as SVG and PDF files do) was reported as not
available and left out of the package, although the canvas drew it and
exports embedded it.

- Package and Font Details resolve fonts with FontDb::resolve, as
  text.fonts and the renderer do. A style the family lacks is still
  copied as its closest style; the report now names the style it
  copies, and Font Details lists the font as substituted, with that
  style's file and permission.
- FontFace::embeddable reads fsType as exports do (Embedding), so fonts
  that allow bitmap embedding only (bit 9), or that set the restricted
  bit together with reserved bit 0 (0x0003), are no longer copied.
  Document Info's permission labels follow the same rule: when a font
  sets more than one usage bit, the label names the most permissive
  one.
- The Package dialog and its status line name the fonts it didn't copy.
2026-10-09 08:35:16 -07:00
BFlat d168d4f843 Merge pull request #771 from DisasterAreaDesigns/affinity-as-chunks
Clippy 1.99: use as_chunks for constant-size chunks
2026-10-09 22:32:02 +07:00
BFlat 9ff0277306 Merge pull request #763 from callmeBalloch/feat/affinity-af-validation
AF.4: harden current .af artboards, text and images with a pinned corpus
2026-10-09 22:21:20 +07:00
bflatasticandClaude Opus 5.5 573f27e6c9 AF.4 docs: leave out shop listings and sandbox notes
The source audit linked commercial template listings and other pages it
didn't use, and described the environment it ran in. Keep the two sources
the corpus uses, say in a sentence what kinds of sources were left out and
why, and state plainly that reopening our output in Affinity is still to do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 22:20:39 +07:00
dotCooCoo 4b00c2d968 Part of #579: macOS fonts registered from other folders are listed too
The font scan read only the macOS font folders and the Additional
Fonts Folder, so fonts that apps and font managers register with
CoreText from their own folders were missing from the font menus, and
exports set such type in the fallback font. CoreText's font manager
lists them with the installed fonts.

- `mac_fonts.rs` lists the files of the fonts the font manager has
  available (`CTFontManagerCopyAvailableFontURLs`, through
  objc2-core-text), keeping only file URLs as `open_documents.rs` does
  (#433). The desktop app and `vectorcraft-cli` install it as they
  install the DirectWrite lister on Windows, and the scan reads the
  files outside the font folders in place.
- The scan leaves out faces with no outlines the font engine draws (no
  glyf, CFF, CFF2 or VARC table), on every platform. CoreText lists
  Apple's PingFang UI interface font (macOS 27), whose outlines are in
  an hvgl table; if the scan cataloged it, PingFang SC would count as
  installed, and Chinese characters in fallback text, Japanese kanji
  included, would draw blank.
- Tests: the list holds the installed fonts by full path, a font
  registered from another folder is listed while it is registered, and
  a face without outlines the engine draws isn't cataloged.
2026-10-09 08:16:33 -07:00
dotCooCoo a61d058f1b Swatches: Save Swatch Library writes swatch exchange (.ase) files
The Save Swatch Library dialog lists Swatch Exchange (.ase) as a
format, and swatch.library.save takes format: "ase" or a path ending
in .ase. Each solid color keeps its model (RGB, CMYK, Lab or Gray) and
its type (spot, global or process), and color groups stay groups.
Gradients and patterns are left out, and a tint swatch is written as
the color it shows. Without a path or user: true, the command returns
the file as dataBase64.

A swatch exchange library opens under the name of its file. When Save
Swatch Library or Save Graphic Style Library saves to a file, the save
panel suggests the Name from the dialog as the file name, as a save to
User Defined does, and the web app downloads the file under that name.
A name made only of dots and spaces, such as "...", gives the file name
Library (Library.vcswatches) in place of a hidden file.

palette_io::write returns the file's bytes. A property test writes
generated libraries and reads them back, and an import fuzz test loads
mutated copies of a file the writer wrote.
2026-10-09 08:15:09 -07:00
BFlat 0a91fd9b44 Merge pull request #762 from DisasterAreaDesigns/suitcase-fonts
Fonts: find suitcase fonts, whose font data is in the resource fork
2026-10-09 21:49:07 +07:00
Matthew FarrowandClaude Sonnet 5.5 75151bd79a Clippy 1.99: use as_chunks for constant-size chunks, and say the gate targets the latest stable
Clippy 1.99.0 added `chunks_exact_to_as_chunks`, which fails `cargo xtask ci`
at its clippy step. Eight uses of `chunks_exact(_mut)` with a constant size now
use `as_chunks(_mut)` (stable since 1.88, within the 1.95 minimum): four in
crates/affinity, three in crates/effects and crates/engine tests, and two in
xtask's sha256. docs/development.md says the gate targets the current stable.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-09 10:43:23 -04:00
bflatasticandClaude Opus 5.5 be9a879e51 Fonts: cap the total a suitcase fork can make the scan copy; no fork look-up off macOS
References can share data, so a damaged fork could make the scan copy one
large resource up to 256 times: cap the total copied like the fork itself.
Off macOS no file has a fork, so the scan doesn't look at each font file twice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 21:40:42 +07:00
bflatasticandClaude Opus 5.5 7dd69aaf81 Fix #740: moved art snaps to the grid
With View › Snap to Grid on, a selection moved with the Selection, Group
Selection or Direct Selection tool lands on the grid: on each axis,
whichever of its two edges and its centre is nearest a gridline (every
subdivision, 9 pt by default) moves onto it. Snap to Grid takes over from
Smart Guides and Snap to Point while it is on, as it does for drawn points.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 21:36:42 +07:00
Matthew FarrowandClaude Sonnet 5.5 d35db792dc PDF tests: the system-fonts test checks the first installed family that stays live text
The test took the first installed family that isn't bundled and expected its
PDF text to stay live. The PDF doesn't embed the font, so the page draws a
stand-in and text stays live only where the installed font's glyphs match it.
With suitcase fonts now found, the first family on a Mac is a condensed
display face whose glyphs don't, so the importer rightly kept outlines and the
test failed. Which family comes first depends on the machine's fonts: the test
now tries up to 200 families and checks the first that stays live.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-09 10:29:11 -04:00
Ahmed Moussa 806ba127e7 Merge branch 'main' into feature/support-arabic-language 2026-10-09 17:19:05 +03:00
Ahmed Moussa 3e20c2cf29 build(web): copy web fonts with cargo xtask, not sh
Replace the Trunk post_build hook packaging/web/copy-fonts.sh with `cargo xtask web-fonts` (xtask/src/web_fonts.rs), so the web build only needs trunk, the wasm32 target and cargo, with no POSIX shell, and works the same on Windows.

The new task reuses crates/text/build/web_fonts.rs for list parsing and URLs, so build.rs and the copier can't disagree on fonts/<sha16>/<file>. It still does nothing without CRAFT_FONTS_DIR, checks each file's SHA-256 against the manifest, and copies the licence next to each face. A listed face that an older craft-fonts checkout lacks is now skipped with a warning, as build.rs does, instead of failing the build. A relative CRAFT_FONTS_DIR is resolved from the workspace root.

Update the references in Trunk.toml, fonts.rs, build.rs, craft_fonts.rs and web-fonts.txt, and document the web build's requirements in docs/development.md. Add tests for the copy, a hash mismatch and the no-op case.

Signed-off-by: Ahmed Moussa <moussa.ahmed95@gmail.com>
2026-10-09 17:18:54 +03:00
Matthew FarrowandClaude Sonnet 5.5 e1d35131d3 Fonts: find suitcase fonts, whose font data is in the resource fork
macOS lists fonts such as the ones Microsoft Office installs in
/Library/Fonts/Microsoft (Bookman Old Style, Impact, Georgia, Tahoma, ...) but
their files have nothing in the data fork and no font extension: the fonts are
`sfnt` resources in the resource fork. The font scan only read data forks of
.ttf/.otf/.ttc/.otc files, so those families were never cataloged and a file
using one (an EPS naming BookmanOldStyle-Bold) showed the font as missing.

The scan now catalogs a file with an empty data fork and a resource fork by
the fonts in that fork, and loading reads them the same way. The fork reader
(crates/text/src/suitcase.rs) follows Apple's published resource file format,
caps what a damaged fork can make it read and never panics. macOS only.

Tests: the fork reader on a synthetic fork built around a bundled font,
including truncated and garbage forks; a macOS scan of a suitcase file.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-09 10:05:18 -04:00
Riccardo Balloch 79c5d673ff AF.4: validate and harden Affinity 3 artboards, text and images 2026-10-09 13:45:55 +00:00
Juan Manuel Verges SolanasandClaude Opus 5.5 1c692e5191 Part of #758: .ai import draws the page once when weighing the layers against it
Each comparison of the layers' art with the page made a new renderer and
drew both again, so the page was drawn and every image decoded once per
text object weighed, plus once more for the final check. The page is now
drawn once, and the layers keep one renderer (with its decoded images)
across their draws. The page's images keep a renderer of their own, as
their keys can match the layers' without their pixels doing so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 13:34:06 +00:00
bflatasticandClaude Opus 5.5 7255ac394d Fix #745: a working Libraries panel of graphics, colours and text styles
The Libraries panel was a placeholder: it invited dragging art onto it,
but nothing happened. It now keeps local libraries (no account, no
cloud), as Illustrator's Libraries panel does:

- drag art off the canvas (or a swatch) onto the panel to add it, or use
  + to add the selection's graphic, fill or stroke colour, character
  style or paragraph style;
- drag a graphic onto the canvas (or double-click it) to place a copy,
  click a colour to paint the selection through the active Fill/Stroke
  proxy, click a style to apply it to the selected type;
- the library picker and the panel menu create, rename and delete
  libraries; each is a .vclibrary file in a Libraries folder next to the
  preferences (session-only in the web app, headless sessions and runs
  without preferences).

Engine: library.list/get/setCurrent/create/rename/delete/add/use/
removeItem. A graphic is stored as a native document with a thumbnail:
adding copies as Copy does and placing pastes as Paste does, resources
and swatch-name conflicts included. Using a style adds it to the
document and applies it in one undo step.

Clipboard::to_document gave its layer the id u64::MAX, which made a
saved copy overflow Document::fix_next_id when read back; the layer now
takes an id above the objects', and fix_next_id saturates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 20:12:59 +07:00
Ahmed Moussa 3c85f3f9e3 feat(text): load craft-fonts' Arabic families, fetch them on web
Desktop builds made with CRAFT_FONTS_DIR now load craft-fonts' 12 Arabic families into the document font database. Noto Sans Arabic comes first and is the fallback for Arabic text. Cairo, Tajawal, Almarai, IBM Plex Sans Arabic, Noto Kufi Arabic, Amiri, Alexandria, Fustat, Reem Kufi, Aref Ruqaa and Gulzar can be picked by name from the font menus.

The web build doesn't embed these fonts because they would make the wasm too large. It fetches them from beside the wasm instead:
- crates/text/web-fonts.txt lists each face as startup or background.
- build.rs turns that list into vectorcraft_text::WEB_FONTS, with each face's URL (fonts/<sha16>/<file>) and SRI hash taken from the craft-fonts manifest.
- packaging/web/copy-fonts.sh, a Trunk post_build hook, copies the font files and their OFL licences there.
- apps/vectorcraft-web/src/fonts.rs fetches Noto Sans Arabic before the app starts, so Arabic text never shows as boxes. It loads the other faces in the background and logs `web font …` for any that fail.

Because fonts can now arrive after startup, the Type tool's layout cache also checks the font database's generation, so text lays out again when new fonts arrive.

_headers and .htaccess serve fonts/* as font/ttf and cache it as immutable. packaging/web/README.md and docs/development.md explain the fonts folder, and ROADMAP.md lists the Arabic typefaces under Type. Also adds Ahmed Moussa to contributors/people.toml.

Signed-off-by: Ahmed Moussa <moussa.ahmed95@gmail.com>
2026-10-09 16:09:34 +03:00