Node ids are per document, so a rename left open while another document
came to the front renamed that document's node with the same id. The
panel drops an open rename when the document it shows changes.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01U9TrG4w49oSRYbP3cqxA3D
The name field asked for focus every frame, which took it back from
Enter, Escape or a click elsewhere, so lost_focus never fired and the
rename was never committed. It now asks only while opening, as the
Artboards panel's name field does.
Fixes#165
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01U9TrG4w49oSRYbP3cqxA3D
Layer and object names, document tabs and any other UI text in scripts the
UI fonts lack now draw in an installed font that covers them, as the canvas
already did.
Root cause: egui only had the bundled UI fonts (Source Sans 3, JetBrains
Mono) and its defaults, none with CJK glyphs. After each frame the UI now
looks at the text it painted; the first time a character the UI fonts lack
appears, a thread finds an installed font covering it through the font
database (the canvas's fallback search, so a font the canvas loaded is
reused, its bytes shared rather than copied) and adds it to every egui
family as the last fallback. Nothing is bundled, Latin-only UIs load no
extra font, and on the web, without system fonts, the boxes remain.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Type > Font, the Character, Properties and Glyphs panels and Find Font list
every installed font by default, as the reference app does, instead of the
four bundled families. The family dropdowns get a search field that filters
the list as you type (Enter picks the first match) and open on the current
font. Show System Fonts, which no longer means anything, becomes Refresh Font
List (text.rescanFonts): it picks up fonts installed since the app started and
redraws type set in them without editing the document. text.fontList lets MCP
agents list the families and a family's styles.
Root cause: the font lists came from FontDb::families(), which only held the
bundled fonts until something scanned the system fonts (fixed with #130). The
Type > Font menu, rebuilt every frame, now builds its items once per font
change, and renderers lay type out again when the fonts change.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Opening, placing, pasting or importing a file, the font menus and MCP now find
the installed system fonts (Arial, Times New Roman...) in a fresh session, so a
file renders the same whatever happened earlier, and PDF import keeps the real
family names (Microsoft YaHei, not "Microsoft Ya Hei").
Root cause: FontDb only consulted the system font catalog, which stayed empty
until a CJK fallback or Character panel > Show System Fonts happened to scan
the font folders. Every lookup by family name (face, families, styles,
has_family) now waits for one cataloging scan per session: the app starts it
in the background, other processes run it on first use. The scan reads only
each file's table directory and name table (about 70 ms for 780 faces here,
instead of reading every font file whole), the catalog is indexed by family,
the family list is cached, and an installed style of a loaded family (a
bundled family also installed with more weights) loads when asked for.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The issue asked for a vector halftone that turns art or an image into
vector dots, lines or shapes by tone, with angle, frequency, shape, mono
or CMYK separations and clipping to the object. Nothing like it existed.
object.vectorHalftone renders the selection offscreen, samples its tone
once per cell of a grid turned by the screen angle and places a shape
whose area follows the tone (circle, ellipse, square, diamond or line).
Mono screens follow lightness in one colour; CMYK screens follow the ink
amounts the CMYK output separates, one screen per ink at the classic
angles, multiplied over each other. Dots are clipped to the art's
outline as real geometry (only the dots crossing it go through a
boolean). It is a command rather than a live effect: the live pipeline
can't sample rendered tone when exporting. Its dialog previews live and
OK keeps it as one undo step.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The issue asked for non-destructive colour adjustments in the Effect menu
and the Appearance panel that work on vector paints, live type and
embedded images. Only the destructive Edit Colors commands existed, and
the live-effect pipeline only knew geometry and raster effects.
Effect > Color Adjustments adds six effects (adjust.*). They recolour,
in stack order, everything an object paints with: fills and strokes
(gradient stops too), type, gradient meshes, embedded images and a
group's members, each colour keeping its model. The Edit Colors walk over
an object's colours moved to vectorcraft_doc::recolor so the commands
and the live effects share it. The renderer draws adjusted art through
its object-effect path (recoloured images cached within a memory
budget), exports bake the adjusted colours (images as recoloured
copies), and Expand Appearance makes them permanent. The effect dialog
shows sliders, a channel menu and a draggable Curves graph, all previewed
live as one undo step.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The issue asked for one-step symmetric shapes, splitting all selected art
along a line and cropping art to a rectangle as real geometry. Only the
freehand Knife and the Scissors existed, and neither works on compound
paths as one shape.
New commands path.mirrorCut, path.lineCut and path.rectCut cut the
selected paths and compound paths (inside groups too) with pathops
booleans against a half-plane or rectangle, and split open paths with
the Knife/Path Eraser helpers. Mirror & Cut reflects the kept half and
unites it with itself (open halves are joined on the axis). The draw2
piece helpers now rebuild compound paths too. Three tools (Eraser group,
original icons) preview the cut live while dragging and commit it as one
undo step; Mirror & Cut's axis and kept side are tool options shown in
the Control bar.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
edit.undo cancelled an open interaction (rolling back the typing session
or drag preview without recording it) and then undid the step before it:
one Undo removed two steps, and the typing could not be redone. Undo now
commits the interaction first, so it is the step taken back and Redo can
restore it; commit_interaction does nothing when nothing is open or
nothing changed.
Fixes#159
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01U9TrG4w49oSRYbP3cqxA3D
The Save / Don't Save / Cancel prompt (and every dialog in the shared
dialog frame) is as tall as its text and buttons at every UI Scaling value,
including when the scale changes while it is open, and never wider than the
window: at 2x in a small window the question wraps instead of running off
the left edge.
Root cause: the tall, mostly empty prompt in the report was the shared
frame's button row taking all the window's height, fixed on main by
d3df1bce (M14.1). What remained was the width: the frame's minimum and
maximum widths are in points, and at a large UI scale a small window has
fewer points than the dialog's 466 pt, so it overflowed the window. The
frame now caps both widths to the window.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A right-click on a toolbar button with sub-tools (the small triangle) opens
the flyout and it stays open until a tool in it is chosen or the user clicks
elsewhere. A long press opens it too, and releasing that press no longer
closes it or picks the button's tool.
Root cause: the right-click did open the flyout, but the flyout closes on a
click outside itself, and the release of that same right-click (on the tool
button, outside the flyout) closed it in the same frame. The release of a
long press shorter than egui's click time did the same, and also selected
the button's tool. Clicks on the flyout's own tool button now leave it to
that button.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
In a window too short for them, the toolbar (tools, Fill/Stroke, colour
chips, drawing modes, Screen Mode, Edit Toolbar) and the dock's panel icon
column scroll with the mouse wheel, or with the scroll bar that shows while
the pointer is over them, instead of being cut off at the hint bar.
Root cause: both were drawn straight into their side panels with no scroll
area, and the toolbar's only fallback (two columns) runs out below about
760 pt. They now draw inside widgets::strip_scroll, a vertical ScrollArea
next to the menus' widgets::menu_scroll.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The first window opens at 1440 x 900 when the screen has room for it around a
taskbar or Dock, and maximized (with a size that fits once un-maximized)
when it doesn't, as on 1600 x 900, 1366 x 768 or 150% scaled 1080p screens.
The window's size, position and maximized state are saved with the UI
preferences (UiState::window) and restored at the next launch, moved fully
onto a monitor that is still connected and maximized if it no longer fits.
Root cause: main.rs created the window at a fixed 1440 x 900 at the system's
default position, never checked it against the monitor and never remembered
it. The new apps/vectorcraft/src/window.rs places the still-hidden window
from the monitors winit reports (CreationContext::winit_window) and tracks
its geometry every frame.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
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 <[email protected]>
Save, Save As and Save a Copy already added the save format's extension to a
name typed in the save panel, but every other write through it (Export,
Export Selection, Save as PDF, text and preset exports, Document Info) wrote
the name as typed, so "art" became an extensionless file.
The save panel's answer now goes through one rule (io::with_extension): kept
when it ends in the suggested name's extension or names a known format, else
that extension is added. The web already downloads under the suggested name
or adds the format's extension in its Save As dialog.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The Transform panel had the link, but kept it in panel-local egui memory;
the Properties panel's Transform section and the Control bar had none, so
size changes there were never proportional.
The link is now the constrainProportions preference (prefs.get/prefs.set),
one toggle shared by the three places (transform::constrain_link), and every
W/H field passes it to object.setBounds as `proportional`.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A `*` or `/` operand had to be a bare number, so `1080/2 px` (the number
replaced by an expression, the field's suffix still there) read as nothing.
The unit after a factor or divisor now measures the whole expression, in
every unit (`Unit::parse`, shared by every unit field). Numeric fields
(num_field, opt_field, plain/mixed fields) select their whole text, unit
included, when they gain focus or are double-clicked, so typing replaces it;
unitless fields gain the same arithmetic (`parse_number`, `45*2°`).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Clicking with the Type tool runs text.create at once, and ending the edit
(Escape, another tool, a click elsewhere) only stopped editing, so every stray
click left an invisible empty text object and a "Type" history step.
The Type tool now remembers the point type its click placed and, when editing
ends with it still empty, runs the new text.discardEmpty command: it removes
the text and, when nothing else changed since the step that created it, drops
those steps so neither the document nor the history keeps a trace. A command
from outside the tool that takes the edited text out of the selection
(Deselect, a Layers panel pick) or removes it now ends the edit too
(Tool::after_command). Dragged area-type frames and type in or on a path keep
their frame.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Rotating an object baked the rotation into its geometry and kept nothing
else, so the bounding box, the handles and the Transform/Properties
panels fell back to the page-aligned box (the Rotate field went back to 0).
Root cause: nodes carried no record of their own axes. Each node now
keeps `bbox_angle` (append-only, written only when non-zero), which every
transform turns with the object (mirroring transforms keep the nearer of
the two possible boxes). The geometry is still baked. The selection's box
(`Document::oriented_bounds`) stands at the angle the selected objects
share. The Selection tool hit-tests and scales its handles in that frame,
and the canvas draws it rotated. `object.setBounds` measures W/H and the
reference point along it. `object.rotate {absolute}` turns the selection
to an angle, which the panels' Rotate field now shows and sets. Object >
Transform > Reset Bounding Box (previously a no-op) clears the angle in
one undo step.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Paste in Place is Cmd+Shift+V, but egui reports every Cmd+V chord as a
Paste event (or, for what isn't text, only the key's release), never as
the key, and the handler ran Paste for every Paste event. A paste now runs
the paste command whose V chord is held (Paste in Place, Paste on All
Artboards, Paste without Formatting; user overrides included), Paste for
the Paste key and Shift+Insert. Envelope Distort > Edit Contents also had
Cmd+Shift+V; it loses the duplicate, and a test checks that no two
commands, tools or panels share a default shortcut.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The control channel attached its modifiers only to the synthetic key and
button events, while most handlers read egui's held modifiers
(i.modifiers), which only the keyboard set. The raw input hook now sets
egui's modifiers (a ModifiersChanged event) for the frames synthetic input
spans: a key's press, text and release, a click, and a drag from press
through its moves to release; the keyboard's modifiers, tracked from the
host's events, come back after. ui.key's text now comes with the press.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The hint bar's segments were hard-coded strings with macOS names (Option,
Cmd) next to one Alt. The hints now write keys as shortcut strings
("Alt+Click") and show them through the menus' pretty_shortcut, and the
fallback hint shows the effective Search Commands shortcut (or the menu
item when it has none).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The shortcut parser mapped "=" to Key::Equals and the matcher only took
that exact key, while egui reports the numpad plus, and + on layouts that
give it its own key (and often Shift+=), as Key::Plus. "+" and "=" now
parse to one chord key, a recorded + is stored as =, and the matcher lets
a = chord take Key::Plus too (also next to the native macOS menu, which
handles only the chord as written).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
font_props wrote `bold` for any style whose name contained bold, black
or heavy and nothing for the others unless fonts were embedded, so
Semibold came back Bold and Light or Medium came back Regular. It now
writes the style's numeric weight (font_descriptor, from style_weight)
for every writer: 600 for Semibold, 300 for Light, 900 for Black, `bold`
for 700 and nothing for 400. The SVG import already reads these numbers
back to the matching face.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The text import reads <text> from the XML and never looked at
paint-order, so character strokes always painted over the fill. A text
whose characters all share one stroke painted first now gets that
stroke as the object's own stroke below the Characters row (as the
canvas, SVG and PDF export already draw it), with its gradient or
pattern resolved in the document; characters with differing strokes
keep them and the import warns.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
usvg knows vector-effect but drops it from the tree it returns, so every
stroke was scaled by its transforms. The importer now records the shapes
that ask for vector-effect="non-scaling-stroke" in its XML prepass (by id)
and gives their strokes and dashes the width they have on screen; type
does the same on its character strokes. Where the width can't be kept
(art in patterns, markers or <use> copies, stretched or skewed type) the
import warns.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
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 <[email protected]>
The engine's file.new also claimed Cmd+N and engine commands win shortcut
lookups, so the shortcut made a blank document. file.newDialog now owns it,
as the docs already said; file.new stays the headless command.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
It used to open the New Document dialog. Now it runs the new app.home UI command:
the Home screen replaces the canvas while the documents stay open as tabs;
choosing a tab, or a document opening, closing or becoming active, returns.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Menu bar menus, their submenus, panel menus, the effects menu groups and library
submenus are as tall as their items down to the window's bottom edge, and scroll
only past that (widgets::menu_scroll), instead of running off screen.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
compatibility "1.3" writes a %PDF-1.3 file (its XMP metadata says 1.3
too) without transparency: a copy of the document is flattened first
with flattenerPreset (stored with PDF presets; empty is High Resolution)
and the flattener options over it, see-through image pixels counting as
transparency and rasterized areas clipped to their regions so no soft
mask is needed. Page groups are left out and the writer refuses a file
that would still have transparency. PDF/X-1a and PDF/X-3 share this
path (their version is now PDF 1.3; 1.4 is still accepted and written
as 1.3), and with it the preset, the header and metadata code and the
transparency check. Passwords at PDF 1.3 encrypt with 40-bit RC4
(revision 2). The Save PDF dialog lists PDF 1.3 and picks the flattener
preset under Advanced.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Embed Page Thumbnails gives every page a /Thumb image: the page drawn on
white, 106 px on its long side, without the layers the page leaves out
(the engine draws them; vectorcraft_pdf::page_areas says what each page
shows; PDF/X files get grey ones). Optimize for Fast Web View writes a
linearised file: the objects are put in linearised order and numbering,
encrypted when a password asks (the encryption dictionary goes with the
catalog, PDF layers' groups too), then laid out with the linearization
dictionary, both cross-reference tables and the page offset and shared
object hint tables (the hint stream encrypted like any other). Their
"not applied yet" warnings are gone. Reading the objects back uses the
lexer of encrypt.rs (syntax.rs adds what rewriting needs).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Advanced > Overprint: Preserve (the default; document.exportPdf
advanced.overprint) writes each overprinting fill and stroke (an object's
own, a character's, a type object's) with a graphics state of /OP true
/op true /OPM 1 (/OPM 0 in PDF/A); Discard writes none. White ones knock out
when Document Setup's Discard White Overprint is on, as in print. Mask art
never overprints. File > Print's composite PDF honours Print > Advanced >
Overprints: Preserve the same way (separations keep multiplying plates).
Each overprinting fill or stroke is drawn as a form marked to overprint
(crates/pdf/src/forms.rs); after writing, its content starts by setting the
overprinting graphics state, and the PDF/X self-check runs on the result.
The engine's "overprint is not written to PDF yet" warning and print's
"overprints print as knockouts in composite output" warning are gone.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Create PDF layers from top-level layers (document.exportPdf createLayers, the
Save PDF dialog's General option) now writes an optional content group per
top-level layer (template layers are left out): named as the layer, off when
the layer is hidden, /PrintState /OFF when its Print option is off, locked
when it is locked, listed top first, with a print auto-state (not under a
standard). The file reopens with those layers.
The PDF writer has no optional content, so each layer is drawn as a form of
its own (no transparency group, so it draws as in place) starting with a mark;
after writing, the uses of the layers' forms are wrapped in /OC ... BDC/EMC
and the catalog gets /OCProperties (crates/pdf/src/forms.rs, through the
shared patch writer and the PDF syntax reader of encrypt.rs). PDF 1.4 and a
knockout page group write plain page content with a warning; the dialog greys
the option at 1.4. The "not written yet" warning is gone.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The pdf crate's PrintSettings gains advanced {printAsBitmap,
overprints: preserve|discard|simulate, flattenerPreset} and
color.profile, all defaulting so saved print_setup values still load.
Composite output discards overprints (no /OP) or simulates them as
Overprint Preview draws them; separations keep them. A printer profile
converts composite colours with the chosen intent (a CMYK one writes
device CMYK) and separations separate with a CMYK one. The engine
flattens transparency with the preset, or renders each printed region
as one image for Print as Bitmap, before PDF or PostScript layout.
The Print dialog's Advanced and Color Management sections show them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
View > Show Print Tiling (view.printTiling) draws the pages of the
document's print settings on the canvas, dashed and numbered, from
print.preview's own layout (print.tiling), so the canvas and the
printed job agree. The Print Tiling tool replaces the no-op tool: a
drag places the first page's imageable corner (print.tiling.set,
saved as print_setup.tileOrigin, one undo step); a double click on
the canvas or the tool button resets it. Tiles start at the origin
and extend to cover the art; the Print dialog shows the tool's
placement with a Reset button.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
document.exportPdf {standard: pdfX1a|pdfX3|pdfX4} and the PDF/X presets now
write conforming files instead of being refused:
- every PDF/X file gets a /GTS_PDFX output intent (by default the CMYK
profile in effect, embedded), TrimBox, Trapped, GTS_PDFXVersion (and
GTS_PDFXConformance for X-1a), a title and dates; editing data,
passwords and, in X-1a/X-3, PDF layers are refused
- PDF/X-1a converts every colour and image to a CMYK destination,
untagged, with CMYK spot alternates; X-3 and X-4 tag colours with ICC
profiles
- X-1a and X-3 are written as PDF 1.3 and their transparency is flattened
first (High Resolution preset; images with see-through pixels count,
rasters stay opaque); X-4 keeps transparency, is PDF 1.6 at most and
names the standard in its XMP metadata
- the written file is checked against its standard (soft masks,
transparency groups, opacity, blend modes, RGB in X-1a, fonts not
embedded) and refused rather than mislabelled
- choosing a standard sets the latest compatibility it allows and turns
off what it forbids (engine and Save PDF dialog)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
file.print {format: "postscript"} (the default for a .ps path) writes the
print job as Level 3 or 2 (level) PostScript instead of a PDF: DSC comments
with %%Pages the page count, one page per sheet with its paper size,
printer's marks in Registration, separations naming their ink (%%PlateColor)
with its halftone screen, a fixed flatness and film negatives. Transparency
is flattened first with flattenerPreset. The answer says which format was
written. In the app, a PostScript job is always saved, never sent to the
printer (the Print dialog's own options are unchanged).
The PDF print's layout is exposed as vectorcraft_pdf::plan (the PDF writer
uses it too); vectorcraft_eps::print draws its sheets with the EPS writer's
document walk, each sheet once however many copies print.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
document.open and file.place read .eps files and PostScript by any name
(PostScript .ai opens as artwork, .ait as a new untitled document). An EPS
file VectorCraft wrote restores the document it carries (restored: true);
other files run through a small clean-room PostScript interpreter (paths,
colours and spot inks, gsave/grestore, concat, line state, bind def,
procedures, loops and dictionaries, clips, shadings, images through the
decoding filters, type as point type), and fall back to their TIFF preview
with a warning when the program can't be read. Operation, nesting, memory
and path budgets keep hostile files from hanging or exhausting memory.
The clipping-group nesting the metafile reader used moves to
vectorcraft_doc::clipnest, shared by both readers.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The Print dialog's Print Preset list loads [Default] or a saved preset
(reading [Custom] once the settings differ) and Save Preset... names the
settings as a preset. Edit > Print Presets... lists [Default] (generated,
protected) and the saved presets with how they differ from it; New... and
Edit... open the preset editor (the Print dialog's sections), Delete,
Import... and Export... use .vcprintpresets files, which File > Open also
imports. Commands print.presets.list/save/delete/import/export; the presets
live in Prefs.print_presets and persist with the preferences.
The open-dialog filter count test counts the new Print presets filter.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The Print dialog (Ctrl/Cmd+P) edits the document's print settings in the
sections General, Marks and Bleed, Output, Graphics, Color Management,
Advanced and Summary, with a preview of each printed page under the section
list (drag the art to move its placement). It drives print.setup,
print.preview and file.print; the preview pages now carry their transform,
printed area and trim box. Print sends the job's PDF to the chosen printer
through the new PrintService (lp on macOS/Linux, the shell print verb on
Windows, a hidden frame and the browser's print dialog on the web); without
one, or with PDF File, it saves the PDF. Done only keeps the settings.
UI commands file.print, print.printers and print.printerSetup.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
With Convert Text to Outlines off (advanced.outlineText: false), type is
written as selectable, searchable text: each glyph in its font, embedded
as a subset with a ToUnicode map, where its outline would be (rotated,
scaled, skewed and on-path type too; gradients keep their place). Fonts
whose licence forbids embedding, or allows only whole fonts, fall back to
outlines with a warning; a subset threshold below 100% is accepted with a
warning. Laid-out glyphs now carry their glyph id and transform.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Save PDF > Output now applies: colours convert to a destination profile
(destination, or preserveNumbers keeping the numbers of colours already in
its model; grey stays grey; images and freeform gradient images convert
too; printer's marks don't). Profile inclusion writes ICC-based colour
spaces (CMYK with the destination's or the document's profile, RGB as sRGB,
grey), and transparency in CMYK output blends in the CMYK profile's space.
The output intent profile is embedded as the catalog's /GTS_PDFX output
intent with its condition, identifier and registry, and Trapped goes in
the document information (PDF/A keeps its own). A shared patch module
edits written PDFs (Lab spot alternates use it too).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
document.export {format: "psd"} writes layered bitmaps (8 bits per channel),
written from the format's public specification: RGB, CMYK (ink amounts) or
grayscale, the resolution and an embedded profile, PackBits channels, and the
merged image (the flat render). layers (default true) draws each top-level
layer alone as a pixel layer, cropped to what it paints, with its name,
opacity and blend mode; a background colour becomes a Background layer.
maxEditability turns layers and sublayers into groups and every object into a
layer named as the Layers panel names it (text by its text); clipping, masked,
knockout or appearance-carrying layers stay one layer. hiddenLayers writes
hidden layers hidden instead of leaving them out; layers: false writes a flat
image on white. At most 30000 pixels a side.
Export As offers PSD with the psdOptions dialog (the PNG Options rows, then
Color Model, Flat Image / Write Layers with Maximum Editability and Include
Hidden Layers, Embed ICC Profile). Tests read the files back with the psd
crate (MIT OR Apache-2.0, a dev-dependency).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The svg crate's new CSS writer (css_rules) gives the CSS web pages style objects
with: rectangles and ellipses as background-color / linear-/radial-gradient(),
border and border-radius; type as font, color, spacing and alignment; shadows,
glows and blur as box-shadow, text-shadow and filter. It shares SVG export's
declarations (transparency, fonts, type spacing, colours, gradient stops), which
moved into svg/src/css.rs. Options: units, absolute position, width and height,
unnamed objects, rasterize unsupported art (as a PNG background-image).
Commands css.selection, css.generate and css.export; UI commands css.copy and
css.exportFile; the CSS Properties panel with a highlighted code view.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Export Selection… (file.exportSelection, a UI command) now collects the
selected objects as assets, one per object (multiple: false makes one),
reusing assets of art already collected, and opens Export for Screens on
its Assets tab with just those checked. document.exportSelection stays the
one-shot export for agents and scripts.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Window > Asset Export lists the document's assets as thumbnails: art
dragged in off the canvas (or added with +) becomes one asset per object,
or one of them all with Alt; Object > Collect for Export does the same.
Assets name their objects, so they follow edits (moving the art moves the
crop) and deleting the art removes it from its asset. Click selects,
double-click renames, the bin removes. The panel's export settings are
Export for Screens' own format rows on the document's shared settings, and
Export writes each asset alone, cropped to its art, into a picked folder
(the web downloads a zip). Export for Screens' Assets tab lists the assets
with checkboxes and exports the checked ones.
Commands: assets.add, assets.remove, assets.rename, assets.list,
assets.export, assets.settings.set; document.exportForScreens takes
assets. Model: Document.assets (ExportAsset {id, name, nodes}).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
File > Export > Save for Web (Legacy) (Ctrl/Cmd+Alt+Shift+S) opens a dialog with Original,
Optimized, 2-Up and 4-Up views showing each file's size and download time, and settings for
colour reduction, colours, dither, transparency and matte, interlacing, web snap, lossy GIF,
JPEG quality, the colour table (lock, map to transparent, web shift, sort), image size and
anti-aliasing, Clip to Artboard, sRGB, metadata, slices (all or selected), HTML output and
presets. Preview in Browser opens the HTML page.
Engine: document.exportForWeb, document.exportForWeb.preview (its byte count is the size of
the file Save writes), webExport.settings and webExport.presets.list/save/delete. Saved presets
and the remembered settings are stored in Prefs. Render: quantize_locked, and encode::web for
web snap, colour table edits, lossy GIF and GIF/JPEG comments. PNG files get an sRGB chunk.
Co-Authored-By: Claude Opus 5.5 <[email protected]>