* Use chrono for UTC/calendar conversion instead of hand-rolled copies
Replaces the hand-rolled Howard Hinnant civil/days-from-civil copies and the
leap-year/days-in-month tables with chrono as a workspace dependency:
- apps/vectorcraft/src/logging.rs: timestamp (log line time)
- crates/doc/src/metadata.rs: civil (Unix seconds -> [y,m,d,h,mi,s]) and iso8601
- crates/pdf/src/file_info.rs: unix (date -> seconds) and in_month, used by the
PDF/XMP date parsers (pdf_date, xmp_date)
Output formats and parsing semantics are unchanged and pinned by the existing
tests (PDF D:... and XMP dates, offsets, leap-year rejection, year clamp 0-9999,
pre-epoch clamp). chrono uses only std, so wasm stays safe. AGENTS.md/CLAUDE.md
now say time goes through chrono.
Follow-up to storytold/cadcraft#1785
* Never panic on a clock outside chrono's range; keep 5-digit years unprefixed
chrono's From<SystemTime> unwraps, so a clock past year 262143 panicked
inside the logger. timestamp now uses the fallible DateTime::from_timestamp
and reads such a clock (or a pre-epoch one) as the epoch instead. Years past
9999 are written with {:04} like before, because %Y would add a '+'. Tests
cover the out-of-range and 5-digit-year cases.
* Don't build an unrepresentable SystemTime in the far-future test
UNIX_EPOCH + Duration::from_secs(u64::MAX / 2) overflows Windows'
SystemTime and panicked before timestamp() ran, failing the Windows
test job. Build it with checked_add and only assert where it exists.
* time: preserve old out-of-range date conversion (no epoch fallback)
`DateTime::from_timestamp(...).unwrap_or_default()` silently reset any instant
past chrono's year range to 1970-01-01. Restore the old conversion domain with
a cycle-based helper that routes the calendar through chrono (the calendar
repeats every 400 years = 146_097 days, so month/day/time come from chrono and
only the year is carried in i128, keeping every i64 exact).
- apps/vectorcraft/src/logging.rs: `timestamp` via `utc_parts`; keep the
pre-1970 epoch clamp; u64 secs past i64::MAX saturate, not epoch.
- crates/doc/src/metadata.rs: `civil` cycle-based, keeping its clamp EXACTLY
as the old code had it: the input is clamped to 0000-01-01..9999-12-31 first
and the year is clamped to 0..=9999 afterwards. (With the input clamp the old
`unwrap_or_default()` was already unreachable; this drops it and makes the
conversion explicit, with identical output.)
Pinned old outputs in tests:
logging u64::MAX/2 s -> 292277026596-12-04T15:30:07.000Z (was epoch),
10_000_000_000_000 s -> 318857-05-20T17:46:40.000Z
metadata civil(i64::MIN)[0]=0, civil(i64::MAX)[0]=9999;
iso8601(i64::MIN)=0000-01-01T00:00:00Z,
iso8601(i64::MAX)=9999-12-31T23:59:59Z (year clamp, as before)
* Don't build a year-318857 SystemTime unchecked in the logging test
Windows' SystemTime only reaches about year 30828, so
UNIX_EPOCH + Duration::from_secs(10_000_000_000_000) panicked before
timestamp() ran and failed the Windows test job. Build it with
checked_add and assert where the platform can hold it. The year-10000
case fits everywhere and stays unconditional.
* Name the 400-year range extension as the one exception to the time rule
The rule said never to hand-roll calendar math, but the range fix
(shifting by whole 400-year Gregorian cycles so dates past chrono's year
range keep their old output) is cycle arithmetic, so the rule and the code
disagreed. The rule now names that exception: the library still computes
month, day and time, and only whole cycles are carried in the year.
* Say the library computes month and day; time-of-day is plain arithmetic
Narrows the range-extension wording: the date library computes the
calendar fields, and splitting seconds into hours, minutes and seconds
is ordinary arithmetic, which is what the code does.
#2027 gave vectorcraft-text and vectorcraft-engine a `bundled-fonts`
feature for an embedder that supplies every document font at run
time, but vectorcraft-ui-egui (and vectorcraft-mcp) named the engine
with its default features, so an embedder that hosts the panels got
the fonts whatever it asked for. Each now has a `bundled-fonts`
feature of its own, on by default, that turns the engine's on, so a
downstream user keeps the fonts unless it asks otherwise. For a
member to leave the engine's default features out, the workspace has
to declare the engine without them, as it already declares
vectorcraft-text; the crates that used the engine's default now ask
for the fonts: the three apps and vectorcraft-testkit. Off,
vectorcraft-text's copy of the fonts (3.4 MB) is left out; theme.rs's
own copies of five of them, the panels' interface faces, stay either
way.
Nothing the workspace builds changes: for every workspace member and
for the whole workspace, with and without dev-dependencies, for all
targets, wasm32-unknown-unknown, aarch64-apple-darwin and
x86_64-pc-windows-msvc, `cargo tree -e normal[,dev] -f '{p} [{f}]'`
resolves the same features for every package as on main, apart from
the new feature names (the engine's `default` no longer listed;
ui-egui's and mcp's `bundled-fonts` and `default`).
A test of the panels can't see their configuration without the fonts
(their dev-dependencies turn them on), so `cargo xtask ci`'s
`no-bundled-fonts` step now also reads that tree for
vectorcraft-ui-egui and vectorcraft-mcp: with their default features
it must show the fonts, and without them, for any target (a wasm-only
dependency too), it must not. Documented in docs/development.md
(Fonts › Leaving out the bundled fonts).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* feat(window): adopt shared craft-window for Linux decorations
Decide the window frame on Linux/BSD via the shared craft-window crate
(storytold/craft-libs) instead of VectorCraft's unconditional
`CUSTOM_TITLEBAR = !macos`, so the app matches the rest of the Craft
suite per desktop.
- Probe `craft_window::Choice::probe(Prefix::new("VECTORCRAFT"))` once at
startup; custom titlebar is on when the frame is `Frame::App(_)` and the
user has not opted into the system title bar.
- Preserve the `systemTitleBar` preference: opting in forces the native
(desktop) frame back on, as before.
- Keep macOS and Windows behavior exactly as today (decided by
`CUSTOM_TITLEBAR`, not the probe): macOS integrated title strip,
Windows app-drawn caption. craft-window returns `Frame::Desktop` for
both; VectorCraft's overdraw is its own choice.
- The only behavioral change: KDE/Wayland now keeps KWin's native
decorations (Frame::Desktop) instead of always drawing a custom
titlebar; GNOME still draws its own (Frame::App).
- `VECTORCRAFT_TITLEBAR` override and the `systemTitleBar` pref both still
work (the env var is craft-window's override for the `VECTORCRAFT` prefix).
NOT MERGEABLE YET: craft-window is open PR storytold/craft-libs#217; the
dep points at the PR branch and must be repointed to a merged `rev`
before this can merge.
* fix(window): native frame when X11 is forced; pin craft-window to merged rev
- When Preferences > Linux Display Server > X11 forces the window onto
XWayland, treat the frame as the desktop's (native X11 decorations)
rather than letting a GNOME/Wayland probe pick Frame::App and draw our
own title bar on what is really an X11 window. An explicit
VECTORCRAFT_TITLEBAR override still wins (checked via Choice::overridden).
- Repoint craft-window from the (now-merged) feature branch to the squash
commit on storytold/craft-libs (craft-libs#217); regenerate Cargo.lock.
- The WAYLAND_SOCKET concern raised in review is handled in the merged
craft-window crate itself (the probe skips when WAYLAND_SOCKET is set).
Verified: cargo build + test -p vectorcraft (131 tests) pass against the
merged dep; windows7 (--no-default-features) still compiles; rustfmt clean.
* fix(window): only force native frame when X11 libs are actually present
Follow-up to review: the XWayland title-bar override now also checks
xwayland::x11_libraries_found(), matching the launch path's own condition.
If X11 is requested but the libraries are missing, startup stays on
Wayland, so the frame decision must keep the probe's app-drawn result
rather than flipping to native. Verified: tests pass, rustfmt clean.
0.13.0's macOS release job failed twice at `stapler validate` right after a successful notarization
and staple: the CloudKit ticket lookup lost its connection (NSURLErrorDomain -1005, Error 68). Retry
the validate up to four times with a growing pause; a missing or bad ticket still fails.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
craft-libs#169 is merged, so the fork pin is no longer needed. Pinned to
storytold/craft-libs 82a24b006eefa036b43c09a67fb9a3fbd17ffe63 (main after the merge) rather than the older 12281d07,
which predates resolve_absolute and the Windows colon refusal.
The PostScript interpreter that reads EPS and PostScript files had no
CCITTFaxDecode filter. Reading a file whose image data went through it
stopped at an error naming the filter, and the file opened from a
fallback, such as its preview or the art drawn before the image, or
did not open.
CCITTFaxDecode now decodes CCITT Group 4 (ITU-T T.6) and Group 3
one-dimensional and mixed (ITU-T T.4) data to rows of 1-bit samples.
It reads K, Columns, Rows, EndOfBlock, EndOfLine, EncodedByteAlign and
BlackIs1 (PLRM 3rd ed., §3.13; ISO 32000-2, §7.4.6). With EndOfBlock
the data ends at its EOFB or RTC regardless of Rows, and the RTC of
mixed Group 3 data, six EOLs each with a tag bit, is found by the
filter. Damaged data keeps the rows decoded before it, and damage in
the first row is an ioerror. Columns outside 1 to 65,535 and Rows
below 0 are a rangecheck, and data that would decode to more than
256 MiB is refused. hayro-ccitt 0.3.0, already in Cargo.lock through
hayro-syntax, becomes a dependency of vectorcraft-eps.
The tests encode their images in the test, with the fax crate's
Group 4 encoder and T.4 run codes (a dev-dependency, already in
Cargo.lock through tiff), and fail without the change. The import fuzz
tests read a Group 4 and a Group 3 image coded by hand from the T.4
and T.6 tables, read the same file with random damage, and add the
filter and its parameters to the hostile programs.
Docs: docs/file-format-parity.md, docs/target-app-parity.md.
Paths stored with linked images and placed documents, and the hrefs of
SVG images, go through fileio::document_path (craft-docpath): a relative
path is taken from the document's folder, and only local files or files
on the document's own network share are looked at. Opening a document,
Link Info, Package, the linked-file watch and SVG import all use it; the
watch stats files through fileio, so automation roots apply to it too.
A path that is not followed counts as missing; document.open,
links.check and links.info say why in `refused`.
What's new for users since v0.11.0:
- A restyled interface: Medium Dark with a coral accent by default, a choice
of accent colours, and a Classic colour scheme (User Interface › Color
Scheme) with the neutral greys of earlier versions.
- New tools and commands: Object › Intertwine and the Intertwine tool, the
Dimension tool, Export for Screens name patterns, folders and presets,
File › Automate › Batch, Sample Color from Screen, Release Guides into
paths, Update Selection, Rotate View to Selection and Rotate View presets,
Outline Object, the Halftone Pattern effect, canvas scroll bars, the
Hyphenation dialog, Legacy .ai export and portable mode on Windows.
- Canvas editing: the ellipse pie widget, the Shaper Group arrow, E for an
enclosed marquee, Unlock from a right-click on locked objects, Blob Brush
Keep Selected and Merge Only with Selection, Simplify's angle threshold and
straight lines, Distribute with a spacing from a key object, and Ctrl/Cmd+
Enter for proportional W and H.
- Type: font size steps of five, reset horizontal scale, reset tracking and
kerning, Align Stroke on live type, and Find and Replace that keeps
paragraph styles.
- Variables: image and graph variables, data set CSV import and export.
- Import: many .ai, EPS and PDF fidelity fixes (images, gradients, meshes,
patterns, layers, File Info), damaged PDFs no longer crash the importer,
and .ai, .ait and .svgz files copied in a file manager paste as art.
- Image Trace keeps tiny parts, thin outlines, shared corners and junctions,
and Noise no longer splits merged regions.
- MCP: draw_path and draw_shape as batch steps with dashes, caps and effects,
and command listings filtered by query and menu.
- Languages: Swedish, Finnish, Greek and Bulgarian, and Brazilian Portuguese
completed.
- Windows: a graphics driver crash leaves that adapter out at the next start,
and a maximized window stays clear of the taskbar.
The nine OFL faces in `assets/fonts` are compiled into every build of
vectorcraft-text: 3.4 MB of the binary, about 1.6 MB of a gzipped wasm.
An embedder that loads every font at run time with `FontDb::add_font`
(a web app serving its own font catalogue, say) ships them twice and
can't turn them off.
- vectorcraft-text: `bundled-fonts`, a default feature, holds the
`include_bytes!` list. Off, a database starts without them (craft-fonts
and system fonts are unaffected) and text has no glyphs until a font
is added, as when the bundled fonts fail to parse.
- vectorcraft-engine: a `bundled-fonts` default feature forwards to it,
so the apps and any embedder using the engine keep the fonts unless
they build it with `default-features = false`.
- The workspace declares vectorcraft-text without default features, so
no library crate turns the fonts back on. The tests of cad, metafile
and tools, which lay type out, ask for the feature in their
dev-dependencies; the other crates' tests get it through testkit.
`cargo test --workspace` builds with the fonts, as before. A new
integration test, `without_bundled_fonts`, covers the configuration
without them (`cargo test -p vectorcraft-text --no-default-features
--test without_bundled_fonts`): the database starts without them, text
in no font lays out with no glyphs, and a font added later is used and
is the fallback for families the database lacks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The default `text` features of vello_cpu (glyph runs, through glifo) and
usvg (fontdb, harfrust 0.12, skrifa 0.44 and its read-fonts 0.41) build
another copy of the font stack into the engine, next to the workspace's
skrifa 0.47 and harfrust 0.13. Neither is used: the renderer draws the
glyph outlines vectorcraft-text gives it, and SVG import reads <text>
from the XML itself and never gives usvg a font (usvg leaves out text it
has no font for).
- vello_cpu: its default features but `text` (std, png, u8_pipeline),
plus f32_pipeline as before.
- usvg: `svgz` only, for gzipped SVG images inside an SVG; `text`,
`system-fonts`, `memmap-fonts` and `writer` are off. text_layout.rs uses
usvg as a reference viewer with a font, so crates/svg turns `text` on as
a dev-dependency feature, as crates/pdf does for krilla's `simple-text`.
- A test pins `svgz`: SVGZ images, linked or as a data URL, import as
vector art.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Preserve the writer and reader changes while integrating the current main.
The combined candidate passes all eight local CI gates and the 2,048-case
reader panic verification. Hosted checks remain required before landing.
Refs #1893, #1894.
krilla's default `simple-text` feature is the only thing that brings in rustybuzz and
ttf-parser, which RustSec lists as unmaintained. The PDF writer places glyphs itself, so the
workspace takes krilla with `raster-images` only; the one test that writes files with
`draw_text` turns `simple-text` on as a dev-dependency feature.
0.9.1 enforces the decompressed size of each block, which 0.9.0 didn't
(memory exhaustion on crafted .ai / Affinity zstd data). The manifests now ask
for at least 0.9.1.
VectorCraft does not run scripts written for the reference app, and
File › Scripts held only the disabled Other Script…. File › Scripts ›
Check Script… picks a .jsx, .jsxinc or .jsxbin file, reads it without
running any of it, and shows a report: what the script needs (the
script language's own objects, the document model, files, menu
commands, dialogs, or features only the reference app has), what it
would do outside the document, the menu-command strings it passes, the
document-model member chains and type names it uses, its directives,
the files it includes, and the problems that would stop it loading.
Copy Report copies the report as JSON. File › Open and drag-and-drop
show the report for those files too.
- vectorcraft-scriptcheck, a new Standalone crate with no
dependencies, holds the checker: a lexer for ECMAScript 3 with E4X
literals and the #name and //@name directive lines, and an analysis
of the tokens that follows variables, function scopes and the files
a script includes. It never recurses: open brackets, the levels of
an XML literal and the function bodies being read are kept on stacks
capped at 4,096. It reads at most 8 MiB of a file and 32 MiB with the
included files, follows 64 included files 16 deep, looks up at most
1,024 paths for them, keeps 1,000 entries in a list, tracks 1,000
variables holding member chains and 1,000 holding File objects, and
cuts echoed text to 120 characters. A compiled script is reported as
compiled and isn't read.
- script.check (engine, a query) takes path, data or dataBase64. With a
path and includes: true, it reads the files the #include lines name
from inside the script's folder, through the automation read root,
and when the script or one of those files assigns a member chain or a
File object to a variable, the check treats the variable as holding
it in every file. It doesn't follow absolute names, names, folders or
links that lead out of the folder, names or folders longer than 4,096
bytes, or JSINCLUDE, and it reads only regular files.
- ui.checkScript shows the report (dialog scriptCheck). File › Open
lists Scripts (.jsx, .jsxinc, .jsxbin) and sends a script's path to
script.check, which reads at most 8 MiB of it.
The language rules come from ECMA-262 3rd edition and ECMA-357 2nd
edition. The directive, platform and document-model root names come
from the public JavaScript Tools Guide and scripting guide pages that
catalog.rs cites by URL, and executeMenuCommand, doScript, loadAction,
unloadAction and selectTool come from the public forum posts cited
there; only names are used. Every script in the tests is written from
scratch with invented names.
Docs: docs/mcp.md, docs/target-app-parity.md.
- Version 0.8.0 -> 0.9.0 (cargo xtask version set; Cargo.lock refreshed).
- release.yml: new linux-riscv64 job, ported from GridCraft. Cross-compiles
riscv64gc-unknown-linux-gnu on ubuntu-24.04 against riscv64 -dev packages
from ports.ubuntu.com (the same set the linux job installs), packages a
tar.gz, and runs vectorcraft-cli --version under qemu-riscv64. The draft
release job now needs it, so its tarball is uploaded and in SHA256SUMS.
- packaging/linux/package.sh: CROSS_ARCH / CROSS_TARGET / CROSS_COMPILE /
EMULATOR support, as in GridCraft.
- docs/releasing.md: list the riscv64 artifact.
Checked locally: cargo check -p vectorcraft -p vectorcraft-cli --target
riscv64gc-unknown-linux-gnu, cargo fmt --check, actionlint, shellcheck.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The sweep under Offset Path (linesweeper 0.4) never finished on some
miter offsets: on the reported glyph and on a synthetic outline it kept
allocating until the app ran out of memory. linesweeper 0.5 fixes that
infinite loop ("Fixed an infinite loop", 0.5.0 changelog), and builds
without changes.
0.5 can hand back a curve piece that doesn't follow the input curve it
was cut from. In the Shape Builder rosette test, a contour runs out along
one petal and back by such a piece, which became a 59 x 4 pt hole in the
merged shape. Tidy::repair rebuilds a piece that strays from the one input
curve through both its ends as that curve's exact sub-curve (input curves
are indexed by their bounds' left edge, so it stays cheap). The there-and-
back then encloses nothing and is dropped as a sliver. Pieces on their
curve, and pieces whose ends two input curves pass through (a real lens),
are left alone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The renderer composited in 8-bit integers, so where a partly transparent
anti-aliased edge crossed opaque art (or a background colour) the result
came out at alpha 254 on some pixels, and PNG, TIFF and WebP files were
written with an alpha channel. Raster exports (and Save for Web) now
composite in floating point (vello_cpu's f32 pipeline, enabled as a
feature), offscreen groups, masks and patterns included; the canvas keeps
the faster 8-bit path.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An Illustrator EPS is a flattened page plus, after %%EOF, an editing copy of
the document; a PDF-compatible .ai carries the same in its private data.
Importing only the page lost layer names, order, visibility, hidden layers
and hidden objects and hidden text.
- eps: read the editing copy (Zstandard %AI24_DataStream, older zlib
%AI9_DataStream) into layers, sublayers, groups, compound paths, clip
groups and hidden objects, run by the existing PostScript interpreter.
Falls back to the page, with a warning, when a shown layer uses an
operator that isn't read or the art differs from the page by over 5%.
- eps: read the text document (ate.rs) to place type on hidden layers and
objects; the page's type is matched to the file's text objects by
position (pieces of a line included) and kept with the nearest one when
none claims it. Area type and type on a path are left out with a note.
- pdf/engine: .ai files go through the same reader.
- engine/cli: a file whose import left things out isn't overwritten unless
asked (acknowledgeLoss, --replace-lossy).
- eps: font names re-encoded with a *<n> suffix resolve to their family.
- i18n: es/it/ru for the new import messages; ROADMAP updated.
Container and text-document formats were inferred from user-owned files; the
Adobe file format specification was not read.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
For users, since 0.6.0:
- Linux: no more startup crash on desktops whose compositor runs on another
GPU (#502, #551); the window renders on the desktop's GPU and retries
the next one instead of crashing.
- Windows: rendering through DirectX 12 first, fixing the black flicker on
Intel GPUs (#545).
- Italian interface (#514).
- macOS: the menus live in the system menu bar.
- Panels, panel groups and the Tools panel undock into floating panels (#495).
- Live Corners on any path's corners: stars, polygons, pen paths (#511).
- Drawing tools snap to Smart Guides as the Selection tool does (#506).
- Pen: Cmd/Ctrl edits the path being drawn, Alt reshapes segments, drags
out of the first anchor always close (#494, #501); Convert buttons for
whole paths (#504).
- Right-click a ruler to change the document's units; the Control bar's
Transform link opens the Transform panel.
- Object > Image Trace > Make traces with the Default preset (#543).
- Edit menu Select All, Cut, Copy and Paste act on a focused field (#516).
- Japanese UI: new type starts top-to-top with em box centre alignment (#432).
- PDF/.ai import keeps type strokes, wrapped lines and layer order (#508);
EPS import fixes (#498, #505).
- macOS 27: only file URLs are opened (#433); Linux: the Paste menu no
longer freezes on an unresponsive clipboard owner (#510).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Read only the indexed PNG thumbnail observed in version-12 Affinity containers. Bound and validate the PNG before decoding, warn about omitted native content, clear the source save target, and reject auxiliary imports that cannot display that warning. Add malformed-input, lifecycle, property and optional native-oracle tests plus a fuzz target. Editable native layers and Affinity export remain unsupported.
A PDF's DeviceCMYK image opened as RGB (the interpreter's pixels as a PNG),
so every export wrote it as DeviceRGB or ICC RGB, silently. Images now keep
their CMYK samples, as CMYK colours already did:
- Model: CMYK images are blobs holding CMYK samples, a four-component JPEG
(Adobe's inverted samples) or a Deflate CMYK TIFF. vectorcraft-doc's cmyk
module reads their ink amounts (ImageBlob::is_cmyk, ::cmyk, capped at 64
megapixels) and writes the TIFF (ImageBlob::cmyk_tiff).
- PDF import: DeviceCMYK, CalCMYK and four-component ICC images keep their
samples. A JPEG with Decode [1 0 1 0 1 0 1 0] is kept byte for byte; any
other is decoded (8 or 16 bits, its Decode array applied) and stored as a
CMYK TIFF. Masked CMYK images and 1/2/4-bit ones open in RGB with a warning.
Placing a CMYK TIFF keeps it CMYK too.
- PDF export: CMYK images are written in CMYK (DeviceCMYK, or ICC-based with
the CMYK profile when colours are tagged: PDF/X-3, PDF/X-4, PDF/A). A CMYK
JPEG neither resampled nor recompressed passes through unchanged; others are
resampled in CMYK and compressed as ZIP or as a CMYK JPEG (jpeg-encoder,
which also replaces the ZIP fallback and its warning for images converted to
CMYK). Output conversion treats them like CMYK colours: Preserve Numbers and
PDF/X-1a keep their numbers, Convert to Destination converts them to another
CMYK profile or to RGB through a 17^4 lookup table (cms::CmykLut, now also
behind the renderer's ink table). A CMYK image that can't be decoded is
reported in the warnings.
- Canvas: CMYK images show through the working CMYK profile, as CMYK fills
do, and paint their own inks in ink planes (CMYK raster exports, CMYK
blending). SVG export writes TIFF images as PNG.
Illustrator's Output › No Conversion keeps colour data as it is
(https://helpx.adobe.com/illustrator/using/pdf-options.html).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bump the workspace version 0.3.1 -> 0.4.0 (cargo xtask version set 0.4.0).
Minor release: 50 pull requests since v0.3.1 (#128-#289).
What changes for users:
- Interface languages: VectorCraft > Language picks Automatic or a
language; Traditional Chinese covers the whole interface, Czech and
Japanese every menu.
- Vertical type: kinsoku, tate-chu-yoko and upright CJK glyphs, each
character in its own cell down the column.
- Area type resizes its frame with the Selection tool's handles and is
reshaped with Direct Selection; the text reflows at its size.
- Canvas and tools: right-click context menu, guides dragged out of the
rulers, Cmd+Space / Cmd+Alt+Space zoom, Cmd drags with the selection
tool used last, double-clicking type places the caret, tool
double-clicks (Hand, Zoom, Rotate, Scale, Reflect, Shear), Free
Transform distort and shear by the keys held during a drag, the status
bar's artboard navigator, Symbols panel drag and drop, Shift+punctuation
shortcuts.
- Live Paint treats every path as an edge; Effect > Pathfinder works on
several objects.
- Files: EPS files from other apps open; PDF/.ai import finds fonts by
PostScript name, brings type on a path back as type on a path and keeps
each artboard's art on its own page; DXF Fit/Aligned text.
- MCP: prompts, resource templates, completions and logging; tool
descriptions and replies match the engine.
- Platforms: FreeBSD and Windows on ARM64 release packages; Japanese
fonts come from craft-fonts at build time.
Co-Authored-By: Claude Opus 5.5 <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>
VectorCraft had an Additional Plug-ins Folder preference but nothing loaded from it. Port
PhotoCraft's wasmi plug-in host (same sandbox, manifest and parameter schema) as the new
vectorcraft-plugins crate (L2), extended for vector documents:
- Object filters read the selected paths and compound paths as JSON (native PathData and
Paint forms: geometry, fills, strokes, gradients, opacity) and return the objects to keep,
change, add or delete; the output is validated in full and applied as one undo step
(plugin.run). A failing plug-in leaves the document untouched.
- Live-effect plug-ins are geometry effects in the appearance stack (effect id
plugin.<id>), re-evaluated (and cached) when the object or parameters change, editable,
exported and baked by Expand Appearance; a missing plug-in leaves the geometry as is.
- plugin.list / info / run / install / remove / reload; the preference folder installs
every *.wasm at startup and when it changes; File > Open of a .wasm installs it (web).
- Object > Plug-ins and Effect > Plug-ins menus with dialogs generated from the parameter
schema (live preview), the Appearance panel's fx menu, docs/plugins.md, an original
example plug-in (Desaturate, a Rust crate outside the workspace) and fuzzing of the
plug-in boundary (engine/tests/plugin_fuzz.rs).
Tool plug-ins and paint-changing live effects are left for later (docs/plugins.md).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
egui 0.36 needs rustc 1.95 (hayro and krilla 1.92), so the declared 1.90 let
cargo fail on dependency versions instead of naming the toolchain. Clippy 1.95
also flagged four collapsible matches that newer clippy no longer does; each is
rewritten without changing behaviour.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New crates/metafile (L3, pure Rust): hand-written EMF and WMF writers over
one shared document walk. EMF keeps Bezier paths, solid brushes, geometric
pens (width, caps, joins, miter limit, user-style dashes), clipping masks as
path clips, images (StretchDIBits, or AlphaBlend with transparency) placed by
a world transform, type as outlines; gradients become images clipped to their
shape and pattern fills their tiles clipped to it. The header's frame is the
artboard in 0.01 mm on a 100 px/mm reference device. WMF flattens curves into
polygons in 16-bit units behind a placeable header (with its checksum),
numbers objects as players do, and reports what it leaves out (clipping,
transparency, gradients, rotated images).
document.export {format: emf|wmf} (and serialize, export) writes one artboard,
or one file per artboard with useArtboards (false: the art's bounds); raster
effects are rendered to images first, as for PDF. Export As lists EMF and WMF.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New crates/eps (L3): a hand-written EPS writer. A DSC header with
%%BoundingBox and %%HiResBoundingBox (y up from the origin), %%LanguageLevel
and spot colours; paths filled and stroked with caps, joins, miter limits
and dashes (strokes a plain line can't draw as filled outlines, brushes as
their art), clipping groups, linear and radial gradients as smooth
shadings at Level 3 and stepped fills at Level 2 or with compatible
gradient printing, patterns as tiles, freeform gradients and placed images
as images (Flate with masked transparent pixels at Level 3, run-length at
Level 2), type as glyph outlines, spot colours as Separation spaces and
overprints when preserved; RGB written as CMYK on request. A TIFF preview
(1-bit or colour, transparent or opaque) behind the binary header, a PNG
thumbnail and the native document in comments (eps::native and
eps::thumbnail read them back for a later import).
document.export {format: eps} and document.exportEps: transparency is
flattened first with a flattener preset (built-in or saved) and options;
the visible art's bounds, or with useArtboards one file per artboard named
{stem}_{artboard}.eps. Export As lists EPS, then EPS Options
(ui.epsOptionsDialog for agents, remembered between exports).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>