The DirectWrite font lister (#594) was installed in the desktop app only:
`vectorcraft-cli` (convert, run, MCP) didn't see fonts that font services
such as Adobe Fonts load on Windows, so its exports set such type in a
fallback font.
- The CLI shares the app's `system_fonts.rs` and installs it first thing
on Windows (not the win7 build). The text crate stays free of unsafe
code; the CLI denies unsafe code instead of forbidding it, so the
shared module's own allowance applies.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Preferences › Devices › Enable Touch Gestures (on by default) was stored
but nothing read it: no touch gesture was implemented.
- `touch::Taps` follows the fingers egui reports (winit reads them from
the system: WM_POINTER on Windows): fingers down together and lifted
within half a second, none moved more than 16 points, is a tap. Two
fingers undo, three redo, with the preference on.
- egui makes the first finger the pointer too, so it has pressed on the
canvas by the time a second lands: that press is dropped then (its tool
interaction rolled back), and the rest of the gesture presses nothing.
The tap so undoes what came before it, not the first finger's mark.
- A pen is one contact, so pen strokes and taps are unaffected; pinch zoom
is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
While the Free Transform tool (E) is active, a small box at the canvas's
top left holds Constrain over the tool's three modes: Free Transform,
Perspective Distort and Free Distort. Before, the modes were reachable
only by modifier keys or `tool.setOption`.
- The buttons set the tool's `constrain` and `mode` options through
`tool.setOption`, as agents do. Constrain acts as Shift held and is
greyed in the distort modes, where it does nothing. The modifier keys
keep working while dragging.
- Theme tokens; Constrain shows the link icon, Free Transform the tool's
own icon, the distort modes are drawn in code. Tooltips translated
(cs, es, it, ja, pt-br, ru, zh-hans, zh-hant).
- `ui.inspect` reports where it shows (`freeTransformWidget`).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A browser's Copy Image puts the picture on the clipboard with the image's
address as text (and HTML). Paste takes text before bitmaps, as word
processors offer a picture of copied text, so it pasted the address.
- `clipboard::is_address`: text that is only an address (http, https,
file, ftp, data or blob).
- Desktop paste (Windows, macOS, Linux): such text gives way to the
clipboard's bitmap when it has one. An address alone pastes as text, as
before; nothing is fetched.
- Web: a paste whose text is only an address takes its picture file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
macOS's Geeza Pro is an AAT font: its morx shaping takes no context from
outside the run, so different_style_arabic_boundary_receives_joining_context
failed on Macs (reported in #610). The tests now use the first candidate
with a GSUB table and skip when there is none.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An anchor was picked within the selection tolerance (3 px), the same reach
as its segments: a press a little off the anchor's 5 px square took the
curve instead.
- `ToolContext::point_tol`: anchors and direction handle ends are picked
within the selection tolerance, but at least 2 px past the drawn point
(4.5 px at the default Size 3, more with a larger Size). Anchors are
tested before segments, so where both are within reach the anchor wins.
- Direct Selection (presses, Highlight anchors on mouse over, blend spine
points and handles), the Anchor Point, Delete Anchor Point and Scissors
tools, and the Pen (its Alt conversions; its close/continue reach stays
5 px unless the points are drawn larger) use it. Segments, objects and
frame edges keep the selection tolerance.
- Of the topmost path with anchors in reach, the nearest anchor is picked.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each paragraph has its own alignment, indents, spacing, hyphenation, tab
stops and paragraph style (TextObject.paras; para stays paragraph 0's, so
old files and readers work). Layout, editing (split/merge), threads, the
Paragraph/Tabs/Paragraph Styles panels, ranged text.setStyle/setFormat/
tabs/paraStyle.apply, SVG export/import and the fuzzers follow.
#606 adds a second area type (Shrink Text to Fit) to the rich fixture: the
vertical-alignment test now picks the centred area type by its text and
strips verticalAlign from every area for its old-file case, and the
composer test checks the Single-line text and the rest at the default.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
VectorCraft asks for a path where it needs one and goes on with it at
once, so its file dialogs ran on the UI thread. Windows and macOS dialogs
run the window's events meanwhile; on Linux nothing answered the Wayland
compositor's pings while a dialog was open, and Hyprland offered to kill
the window as not responding.
- ui-egui `picks`: every file dialog (open, save, folder, place) goes
through one place. With the host's `start_pick` (desktop Linux) the
dialog shows on a thread of its own; what asked (the command, the
dialog's OK, a panel's or dialog's button) gives up for now and runs
again with the picked path when the dialog answers. Another dialog
waits for the open one.
- The desktop app passes its window as the dialogs' parent on Linux (the
portal can then associate its window, "Unhandled parent window type"
before) and Windows; macOS shows them as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Area Type Options gain a fit: none, autoHeight (Illustrator's Auto Size:
the frame's height follows the text after every edit, resolved in
refresh_bounds inside the edit so it shares the edit's undo step) and
shrinkText {minPercent} (beyond Illustrator: overflowing text scales its
sizes, leading and baseline shifts down at layout time by the largest
factor, in 0.1 pt steps of the first run, that fits). The Auto Size New
Area Type preference now applies. text.areaOptions and document.inspect
report overflow and the shrink factor; the Area Type Options dialog
offers fit and first baseline as dropdowns.
A `.ai` file's PDF part is what apps that don't read VectorCraft's native
document open. It had no PDF layers: hidden layers and sublayers were left
out of it and everything else was flat page content.
- `.ai` files (Save As, exports, the CLI) write PDF layers unless
`createLayers` is false (a PDF standard still decides for itself).
- createLayers writes sublayers too, as optional content groups listed
under their layer's in `/Order`, hidden ones off with their art.
- A layer's form gets a box round the whole page instead of its art's
bounds, which readers made a clipping group of, gathering a layer's art
and its sublayers' in one group.
- PDF import: art inside a group, clip or form that dissolves into its
parent keeps the layer it was drawn in, so sublayers nested in their
layer's content import as sublayers.
- Save PDF: "Create PDF layers from layers and sublayers" (ja, es, it,
pt-br, ru, zh-hans, zh-hant).
Hidden objects (not layers) still aren't in the PDF part.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Without craft-fonts the CJK text falls back to a system font with its own
em box (Windows: 34.0 instead of 35.2 at 40 pt); the relative alignment
checks hold either way.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#587 and #600 landed together: the fixture's new area type (Every-line,
the default) is a second text object the composer test now sees.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Area Type Options > Align: Top / Center / Bottom / Justify (AreaOptions.verticalAlign,
LayoutOptions.vertical_align). Each row/column cell is aligned on its own; rectangular
cells shift their lines, other frames and text wrap reflow until settled without
losing text. Justify shares the leftover space equally between line gaps. Area Type Options
shows Align and First Baseline as dropdowns (the parameter dialog's choices table).
Font services load their fonts with GDI at run time, from files in their
own folders, without installing them: neither the font folders nor the
registry list them, so the font scan never saw them. DirectWrite's system
font collection holds them since Windows 10 1607.
- vectorcraft-text: `set_platform_font_files` lets the app name font files
the platform knows; each scan and each check for installed fonts asks
again and reads those outside the font folders. A file named by itself
is read whatever its extension (font services keep fonts in files
without one).
- The desktop app lists the local files of DirectWrite's system font
collection on Windows (`system_fonts.rs`, through the windows crate
already in the build), read in place.
- The check for fonts installed while the app was in the background runs
on a thread: DirectWrite's check for updates takes about 50 ms.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Illustrator's Every-line Composer balances the breaks of the whole
paragraph for left/centre/right aligned text too, not only justified
text. VectorCraft only composed justified area text; ragged text was
always broken greedily.
- Model: ParaStyle.composer (vectorcraft_doc::Composer, re-exported by
vectorcraft-text), Every-line by default like Illustrator, saved only
when Single-line. layout() reads it from the object;
LayoutOptions.composer is now an Option override for tests.
- Composer: the justified gate is gone. Ragged lines never shrink (a
line that doesn't fit ends the node), badness is 100*r^3 with r = the
line's leftover space over a rag zone of 1/6 of the width (finite for
lines without spaces); the last line is free unless shorter than 1/3
of the width (measured in zones of 1/3), so a lone short word on the
last line pulls a word down. Hyphens cost 25 in ragged text (50 in
justified). Knuth-Plass fitness classes with adjacency demerits
(justified 10000, ragged 1000). Lines past the last width change
share one node key, active nodes are capped (4096) and paragraphs
over 200k glyphs break single-line, so work stays bounded; ragged
tries a tolerance of one rag zone, then any fitting breaks, then
falls back to greedy.
- text.setFormat {composer: "singleLine"|"everyLine"}; the Paragraph
panel menu's composer items work with a check mark (Reset Panel goes
back to Every-line).
- Tests: ragged Every-line moves "or" down in "Destroy all creatures
with toughness X or less." where greedy leaves "less." alone; the rag
of long paragraphs is no worse than greedy (sum of squared leftover
and of neighbour differences); Single-line is greedy; the 10k-char
layout budget covers ragged text; command test with undo; native
format round trip (rich_session's text is now Single-line).
No existing test needed changing: the justified tests and
hyphenated_layout_adds_hyphens (ragged, now Every-line) still pass.
Auto alignment (start of the paragraph direction) is ragged; a wrapped
line's Mojikumi flush opening bracket gives its half em back to the
composer, as in the line loop; tests cover Auto in both directions.
Layout of 10k chars, release: justified Every-line 5.6 ms, ragged
Every-line 5.8 ms, ragged Single-line 4.8 ms.