An AppKit local event monitor (apps/vectorcraft/src/mac_tablet.rs) reads each mouse and tablet event's pressure and the pen's end before winit's view handles it and writes them into the canvas's StylusFeed. The feed keeps the pressure of each pointer move, and the canvas pairs them with the frame's moves, so a fast stroke keeps its curve and each point its own pressure; mouse event coalescing is off. The eraser end selects the Eraser tool, as on Linux. Ported from PhotoCraft's crates/tablet (pressure and eraser end only).
* Audit: text layout, shaping, fonts and embedding bounds
* Audit: text bitmap chunk walk without overflow
* Test: PNG metadata chunk walk
* Security: keep the word minimum in wrap regions; allow uncompressed iTXt text
A wrap shape can narrow the first baseline of a rectangular region:
place_at_least then relaxed the minimum to 0 (the region still counted as
"all lines equally wide") and picked the narrow span, breaking a word on
that baseline even though a lower one had room for it whole. Treat the
width as constant only when the region has no wrap shapes.
Also, PNG iTXt chunks may be stored uncompressed: only genuinely
compressed text (flag 1, conservatively anything that does not parse)
counts as compressed metadata now.
* Audit: private temp paths for the signing certificate and testkit output; confine web font licences to the checkout
* testkit: refuse a pre-made private temp directory this user cannot write to
* CI: line tables only for workspace crates, no debug info for dependencies
* CI: one integration-test binary per crate
Each crate's tests/*.rs files become modules of tests/it/main.rs. Files that change
process-wide state (Color Settings, the font database, installed plug-ins, the working
directory), the fuzz sweeps and the test targets the Affinity corpus workflow names stay
their own binaries.
* Docs: name the integration tests' new paths
* CI: xtask test with nextest and doctests, one wasm check, clippy --keep-going
cargo xtask ci runs the tests with cargo nextest (cargo test when it is not installed),
builds them with --lib --bins --tests, runs the doctests as their own step, and checks
the wasm crates in one cargo check --keep-going call.
Where two passes of a path (an open line's way there and back) cross another
path, the sweep can cut them a rounding error apart and join the two cuts
with a stub a little over its tolerance. Upstream's twin-piece joining
already merges the ends of the two passes, but not such stubs, so dense
hatches at exact binary steps (lines every 0.5 pt across a 45° line) still
came back with a speck of a piece per crossing. The planar map now contracts
such stubs into one vertex before the edges are built (within 4x eps up and
down the sweep's frame and 1/16 of the 0.01 pt precision along it), bounded
by each cluster's extent so a run of tiny, nearly level segments (a finely
flattened or traced path) is not collapsed transitively.
Ported from the fork's d5985b14 and 616a5c4a with their tests:
a_dense_hatch_at_exact_binary_steps_cuts_each_line_once_per_crossing (fails
without the fix), a_run_of_tiny_level_segments_is_not_contracted_as_stubs
(fails without the extent bound) and
a_line_crossed_where_its_two_passes_part_is_cut_once (already passing on
upstream, kept as a regression test).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014gCpjL3yUhaH3jRp7ksufh
ci.yml is new: it calls the shared workflow (cached apt packages, nextest, main-only cache saves, Cargo.lock check). FreeBSD and Windows ARM64 run nightly and on pushes to release instead of on pull requests. Depends on storytold/craft-common-actions#69.
An installed family was added to the font database one file at a time, so a
lookup on another thread could find it with only some of its styles: text
falling back to Arial took Arial Bold Italic or Bold (its files sort first)
for upright Regular text, and the same text laid out a moment later took
Arial Regular. One Arabic word could even mix the two. Two threads loading
the same family could also add a face twice.
A family's files are now read and parsed first and their faces added under
one lock, skipping faces another thread added meanwhile. Loading a family
reports it loaded when another thread loaded it meanwhile, so the fallback
search doesn't go on to other families.
This made tests_bidi::a_change_of_paint_in_a_mixed_direction_line_moves_no_glyph
fail intermittently when another test loaded Arial at the same time.
Each `stream` keyword walked back up to 64 KiB through every dictionary before it, so 1 MiB of `<</A <</B 1>> >>stream` took minutes. A walk back now stops where the previous keyword's walk began, and a stream without a Length shares the search for the next `endstream`.
The check for images of SVG documents looked at href and xlink:href alike, so an image whose href is a PNG was cut for an SVG xlink:href; it now reads href, else xlink:href, as the importer does (SVG 2).
The importer expanded an image whose data is an SVG document into art; the SVG table asks for it to be ignored, and a glyph draws no documents inside it.
- copied_file: check the read policy on the real path when it is
valid UTF-8; skip non-UTF-8 paths while automation roots are
active instead of checking a lossy name (path traversal via
symlink, CodeRabbit #3872 thread 1).
- full_bytes_cached: collect matching cache candidates under the
FULL lock, then run the read checks after releasing it, so
filesystem walks no longer stall other FULL users (thread 2).
With objects and ruler guides selected together, object.nudge moved the
guides by the art's delta turned through General > Constrain Angle, so at
90 degrees a Right nudge moved a horizontal guide vertically. The guides
in that edit now move by the page-axis delta, as guides selected alone
already did (CodeRabbit finding on #3745).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014gCpjL3yUhaH3jRp7ksufh
* Links: use resolve_absolute for stored paths
* CI: release only with git dependencies from storytold
* Release: check git sources from the parsed Cargo.lock and cargo config
The grep let any character stand for the dot in github.com, passed a missing or stale
Cargo.lock, and missed source replacement in .cargo/config.