egui counts a press a few pixels outside the canvas as hovering it (its
interaction radius), but the canvas only takes pointer positions inside
its rect, so such a press fell back to the canvas's centre: a click
just below the document tabs or beside the panels selected, typed or
drew in the middle of the view. Presses, and middle-button pans, now
start only on the canvas itself.
Fixes#232.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts with #214 (fonts by PostScript name): this branch's name index (FaceKeys,
aliases) stays, and #214's `by_postscript_name` (used by PDF import) reads a PostScript
map filled from it, keyed by the same normalized names.
Removed: scanning ~/Library/Application Support/Adobe (CoreSync/plugins/livetype and
Fonts), and the preference that switched it (Type › Include Fonts of Other Design Apps'
Folders). Fonts activated through another vendor's subscription are licensed for use
through that vendor's own sync; reading their files from VectorCraft would go around that
licence, and the project's clean-room rules keep VectorCraft out of other vendors' files.
Fonts the user has installed in the system or user font folders are still found by every
name, as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
macOS keeps faces for its own interface under family names that start with
"." (".SF NS", ".LastResort", ".Aqua Kana"…); Mac apps don't list them. Since
the font menus list every installed family (#36), they showed 74 of them on a
stock macOS, at the top of every list.
- FontDb::menu_family_list: family_list without names that start with ".",
cached like it. Only a leading dot hides a family.
- Type › Font, the family dropdowns (Character, Properties), text.fontList and
Refresh Font List's count use it.
- Lookups by name still see every family: documents and fallbacks that name
a hidden family still find it (face, has_family, find_family, families,
text.fontList {family}, Fonts in Document's missing check).
- Test: tests_sysfonts::font_lists_leave_out_hidden_system_families_which_still_resolve
(a temporary font folder); the engine and UI font-list tests compare with
the listed families.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Right-clicking the canvas now opens a context menu, as the reference app
does. The object under the pointer is selected first unless it already is.
The menu then lists what applies to the selection: Undo and Redo, Cut, Copy,
Paste, Isolate Selected Group or Exit Isolation Mode, Group and Ungroup,
Join and Average, Make or Release Clipping Mask and Compound Path, Make
Guides, the Transform, Arrange and Select submenus, and Export Selection.
On empty canvas it lists Paste, the zoom commands, the ruler, grid and guide
toggles and Select All. Commands that can't run now are left out (Undo and
Redo stay, greyed out), and a press on the canvas only closes the menu. Agents list the same menu with the
`ui.contextMenu.list` control method.
Root cause: the canvas ignored the secondary button except in the toolbar's
tool flyouts; there was no context menu at all. The menu reuses the menu
bar's items, rendering and dispatch; Object > Transform and Arrange are now
shared lists.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Choosing an Effect > Pathfinder item (Add, Intersect, Exclude, Subtract...)
with several loose objects selected now groups them and applies the live
effect to the group, in one undo step, so the result shows at once and stays
editable. A selection that already holds a group, or a single object, works
as before, and agents get `grouped: true` back when it happens.
Root cause: Pathfinder effects combine the contents of a group or layer.
Applied to loose objects, each one got an effect with nothing to combine, so
nothing visibly changed, while the Pathfinder panel's commands (found by the
command search) act on the selection directly.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pressing with Shift on an object that was already selected toggled it
out of the selection at once and went idle, so the drag that followed
moved nothing. The toggle now waits for the release: a Shift-click
still takes the object out of the selection, while a Shift-drag moves
the selection constrained, as it already did for unselected objects
and when Shift was pressed mid-drag.
Fixes#224.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U9TrG4w49oSRYbP3cqxA3D
DXF text justified Fit now keeps its height and stretches or squeezes
horizontally to run exactly from its first point to its second; Aligned
text scales as a whole, height included, to do the same, its width factor
kept. Attributes justified the same way follow suit. Without a second point,
or with both points the same, the text keeps its size as before.
Root cause: the importer took the angle of the two points and anchored the
text at the first, but kept the text's height and width factor, ignoring the
distance between the points. The text now records that span and, when it
becomes a text object, is measured in the font it is set in, then given the
horizontal scale (Fit) or size (Aligned) that makes it end at the second
point, kept between 1% and 10,000% of its natural length. The import's
bounds estimate uses the same scale.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Live Paint Bucket now fills areas enclosed by any paths. Crossing
lines enclose faces it can fill, a line across a shape splits that shape's
face in two, and an area enclosed by paths becomes a face even when nothing
fills it (it starts unfilled). Edges are the paths cut wherever another path
meets them, keeping their stroke, so Shift-click paints each piece. Closed
shapes give the same faces as before.
Root cause: Live Paint built its faces from the Pathfinder regions, which
divide the filled areas of closed shapes. An open path counted only as the
area it would fill if closed, which for a straight line is nothing, so lines
divided nothing and the bucket found no face to fill. A new planar map in
vectorcraft-pathops (live_paint) sweeps every path as an edge: an open path
goes in traced there and back, with crossing counts that never cancel; faces
are walked on the resulting graph (holes included), and their fills and the
edges' sources are found geometrically. The sweep runs in an exactly sheared
frame, because it mishandles a horizontal line traced there and back.
Make, Merge, the bucket and Live Paint Selection all use it;
interior_point moves to vectorcraft-pathops with it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since 546a65f every release job checks storytold/craft-fonts out at its pinned commit and
builds with CRAFT_FONTS_DIR and CRAFT_FONTS_REQUIRED=1. The FreeBSD jobs do the same inside
the VM, so FreeBSD packages carry the Japanese fonts and their OFL texts. The package check
now requires each expected file (the font licences included) instead of any one of them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Shippori Mincho (assets/fonts, from #145) moves to storytold/craft-fonts. crates/text/build.rs
embeds the fonts of a craft-fonts checkout when CRAFT_FONTS_DIR is set (web: BIZ UDPGothic
Regular only) as vectorcraft_text::CRAFT_FONTS; unset, it is empty and Japanese falls back to the
installed fonts. Document text gets the Japanese faces after the bundled fonts (Mincho first); the
UI adds them at the end of every egui family (BIZ UDPGothic first), before ui_fonts' system-font
discovery. Release and FreeBSD CI builds check craft-fonts out at a pinned commit; packages carry
the fonts' OFL texts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Release builds add an arm64 MSI and portable zip, cross-compiled on the x64
runner (signing and WiX as for x64). package.ps1 checks each binary's PE
header before packaging: Machine must match -Arch, the app must be a GUI
program and the CLI a console program. It skips the CLI smoke run when an
ARM64 build can't run on the host. A new Windows ARM64 workflow packages the
ARM64 MSI, installs it on a Windows 11 ARM64 runner, checks both installed
exes are ARM64, runs the CLI, and uninstalls. Ported from printcraft#66.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Builds on the vertical type from #145 (same data, commands and tools).
- Kinsoku (the strict set, horizontal Japanese too): no line starts with a
closing bracket, 、。, a middle dot, colon, !?, the long vowel mark,
iteration marks or small kana; no line ends with an opening bracket.
Hyphenation breaks obey it too.
- Tate-chu-yoko: a run of two or three half-width digits (10月, 第100回)
stands upright across the column as one block one em long, squeezed to
the em when wider; longer numbers stay on their side.
- Upright or on its side by Unicode's Vertical_Orientation: CJK and
symbols upright, marks that need a vertical form on their side when the
font has none; upright glyphs advance at least one em, centred, and turn
about the ideographic em box centre so they sit on the column's centre
line. Point type keeps its anchor on the first column's centre line.
- `vrt2` is no longer requested: its glyphs are already on their side, so
with the layout turning Latin and digits too they came out upside down
(and tate-chu-yoko blocks broke) in fonts that have it, e.g. Hiragino.
- The line→text mapping is one affine (`TextLayout::line_xf`, an exact
quarter turn plus the point-type offset); `logical_point` and
`physical_point` use it. Pixel snapping skips vertical type.
- Tests: kinsoku in both directions, tate-chu-yoko blocks, upright vs
sideways, anchor on the centre line, arrows along columns.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Documents made elsewhere often name a font differently from the family
name VectorCraft lists: a localized family name (ヒラギノ角ゴシック), a
legacy family/style pair (Hiragino Sans + W3) or a PostScript name
(HiraginoSans-W3). FontDb now indexes every name in a face's name table
and resolves a request to the right face, telling the caller whether it
matched exactly, by another name, by substitution or not at all
(FontMatch).
- text: FaceKeys/aliases, FontDb::resolve, canonical, find_postscript;
fix a race where a face loaded by another thread was reported missing.
- engine: text.fonts reports status, the resolved face and missing
glyphs; Replace Font resolves the same way.
- render: substituted-font highlighting uses resolve, so a font found
by another name is no longer shown as missing.
- Find Font notes how each font was found.
- Preferences › Type › Include Fonts of Other Design Apps' Folders
(macOS, on by default).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Typing with an input method (the macOS Japanese IME and the like) used to
insert the raw keys (romaji came in as ASCII): egui's Ime events weren't
handled.
- Tools gain `ime_preedit` / `composing` / `ime_caret`; the Type tool lays
the marked text out in place inside its typing session, underlined (the
clause being converted thicker), and commits it through `text_input`.
Character ranges from the IME convert to byte ranges in one place. A
cancelled composition leaves no empty undo step; an empty clear with
nothing marked never deletes the selection.
- The UI sends Text and Ime events to the tool in order, sends IMEOutput
every frame while text is edited (what lets egui-winit enable the IME
and place its candidate window at the caret, or the converting clause),
and holds back keys, Enter/Escape, clipboard, single-key shortcuts and
menu commands (Undo, Cut, Select All… also from the native menu) while
composing. A click, a tool switch or a style change ends the
composition with the marked text kept as typed; on macOS the IME is
told to discard it (NSTextInputContext.discardMarkedText) so it doesn't
come back in the next input.
- Undo during a typing session also ends the Type tool's session, so its
caret and marked text follow the document.
- Tests: tools (char → byte ranges, underlines and candidate position),
engine (compose → commit is one step, cancel leaves no step, a click
commits, Undo mid-composition is safe), ui-egui (synthetic Ime events:
compose, commit, ⌘Z held back, IME output, click and app switch).
Checked by hand on macOS 27 with the Japanese IME (romaji input, convert,
commit, cancel, Undo/Redo after commit, click away, Latin mode, app
switch). Not checked: Windows and Linux IMEs, the web build.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
VectorCraft ▸ Language gains Čeština (`app.language {lang: "cs"}`,
persisted as "cs"). `Language::tr` now picks a table per language.
The Czech table (crates/ui-egui/src/i18n/czech.rs) translates every
menu title, submenu, item and header in the menu tree, plus the labels
toggling items switch to (Show/Hide Guides, Lock/Unlock Grid, Outline/
Preview, Edit Contents/Edit Envelope…). It was written clean-room from the
meaning of the English labels: commands in the infinitive, menus as nouns.
Left as-is on purpose: the product name, language names, OpenType, the
built-in workspace names and the perspective grid preset names.
Tests:
- every Czech (and Japanese) entry is a menu label, toggled labels included;
- Czech covers every menu label except fonts, sizes, plug-in names and
an explicit KEEP_AS_IS list;
- ellipses match their source label, entries are unique;
- the Language menu lists every language; `cs` validates and persists;
- each UI font family's own first font has the Czech letters and
punctuation (no system fonts needed).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Fixes the Windows installer: the Start Menu shortcut and Add/Remove Programs icon were blank. Release builds no longer open a console window behind the app.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The MSI Icon id had no .ico/.exe extension, which Windows Installer needs for
advertised shortcuts and ARPPRODUCTICON, so both showed a blank icon. Release
builds of the desktop app are now GUI-subsystem (no console window; the CLI stays
a console program), the shortcut/summary description names what VectorCraft does, and
packaging-lint rejects an Icon id without an extension. Found in printcraft#57.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The appended Interface localization section moves into the roadmap's structure: a
Shipped so far entry, the Type advanced row (45%, CJK composition left), the gap list,
the inventories (16 stubbed menu items; Touch Type the only missing tool) and totals
recomputed from the parity table (75.3%, 211–332 h to feature parity); README Status
follows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
47 of the 83 entries were labels from other apps ("New blank PDF", "Comments panel",
"Read mode"…) that never appear here. A test now walks the menu tree and fails on any
entry that isn't one of VectorCraft's labels.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A document names its fonts by PostScript name, which the importer split at the
first hyphen into family and style. A family with hyphens in its name was never
found: "Rounded-X-Mplus-1c-black" became family "rounded", style
"x-mplus-1c-black", and the text fell back to another font. Common Japanese
families have hyphens in their names.
The system font scan now also indexes each installed face's PostScript name
(read from the same name table it already reads). The importer asks for the face
with that exact name first (`FontDb::by_postscript_name`) and splits the name only
when no installed face has it.
Test: installed faces are found by PostScript name, in any case.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>