file.place of a .txt file takes `text: {characterSet: unicode|ansi, platform: windows|mac,
removeLineReturns, removeParagraphReturns, replaceSpaces}`: Unicode reads UTF-8 or UTF-16 by its
byte-order mark (other bytes as the platform's 8-bit set), ANSI reads Windows-1252 or Mac Roman;
CR LF, CR and LF all break lines; joining lines makes each block one paragraph, blank lines can
go, and runs of spaces can become tabs. The text becomes area type filling `rect`, the replaced
object's bounds, or the artboard less a 36 pt margin (type reflows rather than scaling), as one
undo step. The place cursor and file.place.info take text files too, and File → Place lists them.
In the app, placing a text file (the Place dialog, a drop, file.place without options) opens the
Text Import Options dialog. Its dropdown rows share a form helper with Document Setup.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DeviceCMYK and CMYK ICC colours import as CMYK and a file painted mostly in
CMYK opens as a CMYK document (with CMYK default swatches); DeviceGray
imports as Gray; Separation and DeviceN inks become global spot swatches
(their 100 % colour from the alternate space: CMYK, RGB, Gray or Lab) that
the art links to at its tint, /All links to Registration and process
colorants make CMYK. Shading stops keep their model and ink links the same
way. Inks mixed in one DeviceN colour, or spaces that can't be told apart,
import as RGB (with a warning for the mix).
document.open takes colorMode (rgb | cmyk) to open any file in that mode,
converted as file.documentColorMode does (its conversion is now shared).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Opening or placing a PDF or .ai with several pages or a password shows the
Import PDF / Place PDF dialog: a page preview with page navigation, Pages
(All or a range) when opening, Crop To (Bounding Box, Art, Crop, Trim, Bleed,
Media) and a password prompt for encrypted files.
- vectorcraft-pdf: ImportOptions.pages / crop / password, CropTo, info()
(pages, sizes, rotation, boxes); NeedsPassword / WrongPassword / BadPage
errors; images keyed by content so placed pages merge safely.
- document.open takes pages, page, cropTo (or crop) and password;
document.pdfInfo (query) lists pages and boxes, reports needsPassword and
renders a page thumbnail.
- file.place and file.place.info read only the page placed and take every
PDF box as crop (art, trim, bleed, media besides crop and bounding) and a
password; the UI's File > Place asks with the dialog when no page is given,
keeping Link, Template and the other place params.
- testkit::pdf writes hand-made PDFs with page boxes and RC4 encryption.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The editing data now sits in CDATA inside <metadata> with a hash of the
markup around that element (whitespace between tags and line endings
don't count). document.open restores the native document when the hash
matches (data written without a hash is trusted as before); when another
app changed the SVG, or the data can't be read, it imports the SVG and
says why in its warnings. vectorcraft_svg::editing reports the data and
whether it is intact; editing_data stays. Live effects, symbols and
patterns come back exactly; the import fuzz test now mutates saved SVGs
with editing data and hidden layers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
file.place {path | name+dataBase64, link, template, replace, at, rect, page, crop} places a
file as one undo step without touching the clipboard. Raster images come in at 100% of the
resolution their file declares (PNG pHYs, JFIF/EXIF, TIFF, BMP; 72 ppi otherwise), linked to
their path by default; SVG becomes one group with its embedded images; a PDF/.ai page or a
native document's artboard becomes one clipped group. The images, symbols, patterns and
swatches the art uses join the document (renamed on conflict). Replace keeps the replaced
object's stacking place and transform; Template puts the art on a new locked template layer
below the current one. file.place.info and image.info report size, ppi and colour mode.
File > Place picks a file and opens the Place dialog (Link, Template, Replace); the web picks
through the inbox. The Control bar shows Linked File/Embedded, the file name, colour mode and
ppi of the selected image.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PdfSettings (serde, camelCase) is the Save PDF model: Standard, Compatibility
PDF 1.4-2.0, General (Preserve editing, Thumbnails, Fast web view, View after
saving, Create layers), Compression, Marks and Bleeds, Output, Advanced and
Security. PdfOptions = settings + pages, title and date.
document.exportPdf {path?, preset?, artboard?|artboards?|range?, standard?,
compatibility?, ...sections} -> {path|dataBase64, bytes, warnings} applies the
options over the preset (null keeps the preset's value); PDF/X, passwords,
PDF/A-2b at 2.0 and out-of-range values are refused; options accepted but not
applied yet come back as warnings. document.export {format: pdf} and
document.formats take and list the same options.
The Save PDF dialog (file.export.pdf, ui.savePdfDialog) has Preset,
Standard and Compatibility on top and a section list; it runs the export
before asking for a path, so bad options never open a save dialog.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each <text> is swapped for a placeholder group before usvg runs, so the text keeps its z-order,
parent group, clip, mask, opacity, link and <use> instances, and usvg resolves its gradient and
pattern paints (in text space). <textPath> becomes type on a path (startOffset, side, anchor),
vertical writing-mode type on a vertical path. Per-character x/y/dx/dy/rotate lists become line
breaks (bounded), kerning (absolute x measured against the layout), baseline shift and rotation;
baseline-shift, numeric and relative weights (style names), letter/word spacing, kerning and the
character stroke's cap, join, miter and dashes are kept. The style cascade supports type, #id,
multiple-class, attribute, descendant and child selectors with !important and specificity.
<pattern> paints become pattern swatches. The import fuzz covers the new attributes and CSS.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
import_fuzz now also feeds garbage and mutated `.vcswatches`, `.gpl`,
`.vcstyles` and `.vcflattener` files through `swatch.library.load` /
`graphicStyle.loadLibrary` / `flattener.presets.import`, uses what loads
(adds and applies the swatches and styles, previews and flattens with the
presets) and renders and exports the result. The SVG generator gains `<a>`,
Lab colours, mask options (`mask-type`, `data-vectorcraft-mask`) and paint
attributes. `PROPTEST_CASES` now raises the case count as the file's docs
say (an explicit `cases: 64` used to override it).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Shipped code no longer unwraps, expects or panics: every one of the 124
unwrap()/expect()/panic!/unreachable! sites outside tests now handles the
failure (Result/Option, ?, let-else, defaults) and reports errors to the user.
- Lints: workspace clippy denies unwrap_used, expect_used, panic, todo,
unimplemented and unreachable; clippy.toml allows them in tests, and
integration tests, examples and testkit opt out explicitly.
- Safety net (vectorcraft_engine::guard): a panic in a top-level command rolls
the document back and returns EngineError::Internal; a panicking tool is
reset; the MCP server answers with an internal error and keeps serving; the
UI frame and each control request are guarded.
- APIs that hid failures now return them: Rendered::to_png/to_jpeg/to_webp
return Result (JPEG/WebP errors used to write empty files),
FontDb::face returns Option, Region::top returns Option.
- Fuzzing: engine/tests/import_fuzz.rs feeds garbage, hostile and mutated SVG
and PDF through import → render → export (30k cases locally, no panics).
- Docs: AGENTS.md/CLAUDE.md rule, docs/development.md "Robustness" section,
README pointer and a PR template checklist.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The engine unit tests share one process, and two colour-management tests
switched the process-wide Color Settings (the working RGB space to the wide
gamut profile, Black Point Compensation off) for a moment. Any test that
showed or rendered a colour at that moment saw it through the wrong profile:
clicking_an_image_samples_its_pixel read #008181 instead of #008080.
- Move the two tests that change the Color Settings to their own test
binary (crates/engine/tests/color_settings.rs), so no unit test can see
non-default settings.
- The image-sampling test compares the stored colour, not its display hex.
- every_command_survives_empty_params toggles the process-wide proof view
(Proof Colors, Overprint Preview, Separations), so it now takes the
colour-management test lock like the tests that read that view.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- New original DrawCraft mark (gradient + pen nib) replaces the two-letter badge
- render: thumbnails and pattern tiles use a single-threaded context (vello's default is
multithreaded and panics on filters); per-item raster effects detected
- Type tool state no longer leaks across documents
- ui.set {status} for clean screenshots
- README: new hero, feature shots, Made in DrawCraft gallery; examples/neon-drive, ribbons,
feature-sheet; ASSETS.md updated; heading renamed to Crafting Apps
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>