Unlocked rectangle corners now each use their own radius and type (top left,
top right, bottom right, bottom left) instead of all following the first one,
and straight corners are cut across instead of rounded. patchy-shapes.af now
matches its embedded thumbnail (mean difference 1.91 -> 0.24) with no warning.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#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>
Open and Place read Affinity documents natively (.af from Affinity 3,
.afdesign/.afphoto/.afpub from Affinity 1 and 2): layers and sublayers,
groups, artboards with their names and backgrounds, Publisher pages side
by side, curves with live corners, parametric shapes, compound shapes,
clipping by nesting, vector and pixel masks, solid and gradient fills,
strokes, fill layers, artistic and frame text, placed images (their
original JPEG/PNG), pixel layers, embedded documents as their cached
picture, opacity, visibility, locking and blend modes, at the document's
resolution in points. Unsupported content (effects, adjustments, brushes,
master pages, special shapes, Affinity-only blend modes...) is listed in
the import warning, one line per kind. A file whose native data can't be
read opens as its embedded preview with the reason, and that preview
cannot be placed or used as a template or library. Save never writes back
over the Affinity file and there is no Affinity export.
The document semantics were fitted to the thumbnails Affinity embeds in
176 public documents. `cargo xtask corpus --affinity` fetches 22 public
CC0/MIT/Apache-2.0 documents at pinned commits, sha256-verified; the new
engine test renders each and compares it with Affinity's own thumbnail
(0-4.4 of 255 mean difference, per-file ceilings). import_fuzz mutates a
synthetic native document's stream and container through open, render
and export; the fuzz crate gains a stream target.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GQPh8UDgMyARR5Hat9UchC
Read the native container beyond its preview: the chain of allocation
tables (#FAT, #FT2, #FT3, #FT4) with newest-revision selection, CRC-checked
extraction of stored, zlib and zstd entries with their byte predictors,
and the self-describing tagged object stream in doc.dat as a generic tree
of shared, inline and positional objects.
Every declared size is checked against per-entry and per-import budgets
before allocation; the zstd window is capped at 64 MiB; table chains are
cycle-checked; arrays are checked against the remaining bytes; object
nesting stops at 384 levels.
The header now accepts container versions 8 to 12 and the flag values 4
and 8 that real Affinity 3 files carry (the previous check rejected the
public CC0 Affinity 3 documents); the thumbnail record is the same in all
those versions. Checked against 176 public documents saved by Affinity 1.x
to 3.x: every doc.dat decompresses with a matching CRC and parses with no
bytes left over.
A test-only `synth` feature builds synthetic containers for unit tests
and fuzz seeds; it is not an Affinity exporter.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GQPh8UDgMyARR5Hat9UchC