//! VectorCraft document model: artboards, layers and objects, appearance, swatches (pure data + serde). //! //! Documents are persistent trees: children are `Arc`, and edits go through //! [`Document::node_mut`], which clones only the nodes on the path from the root to the edited node. //! Keeping the previous `Document` value around is therefore a cheap undo snapshot. #![forbid(unsafe_code)] pub mod appearance; pub mod assets; pub mod blend; pub mod cancel; pub mod clipnest; pub mod cmyk; pub mod corners; pub mod depth; pub mod dimension; pub mod graph; pub mod hit; mod id_index; pub mod inks; mod inline; pub mod intertwine; pub mod shared; mod symbol_export_type; mod symbol_nine_slice; mod symbol_registration; pub use inline::{SYMBOL_HALF, SYMBOL_SIZES}; pub use shared::Shared; pub use symbol_export_type::{SYMBOL_EXPORT_TYPE, SymbolExportType}; pub use symbol_nine_slice::{SYMBOL_NINE_SLICE, SymbolNineSlice}; pub use symbol_registration::{SYMBOL_REGISTRATION, SymbolRegistration}; pub mod links; pub mod live; pub mod live_paint; pub mod marks; mod mem; pub mod metadata; pub mod node; pub mod objects_on_path; pub mod orient; pub mod overprint; pub mod pattern; pub mod perspective; mod pixels; pub use pixels::MAX_RASTER_ALLOC; pub mod placed_document; pub mod plugin_data; pub mod profiles; pub mod puppet; pub mod range; pub mod rastersettings; mod reach; pub mod recolor; pub mod selection; pub mod setup; pub mod shaper; pub mod slices; pub mod style_libs; pub mod swatches; mod symbols; pub use symbols::SymbolBoxes; pub mod text; pub mod trace; pub mod variables; pub mod xml_id; use std::collections::BTreeMap; use std::sync::Arc; /// `skip_serializing_if` predicates: fields equal to their serde default are not written. pub(crate) mod skip; pub use appearance::StrokeGradientMode; pub use appearance::{ Appearance, AppearanceItem, ArrowAlign, Arrowhead, Dash, Effect, FillLayer, LineCap, LineJoin, PressureProfile, ProfilePreset, SavedProfile, StrokeAlign, StrokeLayer, WidthProfile, }; pub use assets::ExportAsset; pub use corners::LiveCorners; pub use depth::{MAX_DEPTH, MAX_SYMBOL_NODES}; pub use graph::{ ColumnDesign, ColumnType, Fractions, GraphDesign, GraphKind, GraphSpec, LabelStyles, MAX_GRAPH_CATEGORIES, MAX_GRAPH_SERIES, PiePosition, PieSort, SeriesPaint, TickLength, ValueAxisSide, }; pub use hit::{Hit, HitKind}; pub use links::{LinkInfo, PlacementOptions}; pub use live::{BlendEase, BlendEaseType, BlendOrientation, BlendSpacing, BlendSpec, EnvelopeKind, GradientMesh, MeshPoint}; pub use live_paint::{GapOptions, GapSize}; pub use metadata::{CopyrightStatus, DocMetadata}; pub use node::Knockout; pub use node::Scaling; pub use node::{Extras, ExtrasMut, ImageObject, LAYER_COLORS, LayerColor, LiveShape, Node, NodeExtras, NodeId, NodeKind, OpacityMask}; pub use node::{ForeignAttribute, ImageMap, MAX_SVG_SCRIPTS, ObjectAttributes, SVG_EVENTS}; pub use orient::OrientedBox; pub use pattern::{Overlap, PatternDef, PatternEdit, RepeatKind, RepeatSpec, TileType}; pub use perspective::PerspectiveAttachment; pub use placed_document::{PageBox, PlacedDocument}; pub use profiles::ColorProfiles; pub use puppet::{PuppetPin, PuppetPins}; pub use rastersettings::{RasterColorModel, RasterEffectsSettings}; pub use selection::{AnchorRef, SegmentRef, Selection}; pub use setup::{Background, DocSetup, ExportText, GridSize, Quotes}; pub use slices::{CellAlign, CellVAlign, Slice, SliceArea, SliceKind, SliceOptions, SliceSource}; pub use style_libs::StyleLibrary; pub use text::{ AreaFit, AreaOptions, Burasagari, CharAlign, CharPosition, CharStyle, Composer, Digits, FirstBaseline, HyphenationOptions, InlineArt, JustificationOptions, Justify, Kinsoku, LeadingModel, Mojikumi, ParaDirection, ParaStyle, PathAlign, PathEffect, RubyAlign, RubyPosition, RubySpan, ScriptMetrics, SingleWordJustify, TabAlign, TabStop, TextKind, TextObject, TextRun, TextStyleDef, TextWrap, VerticalAlign, WrapShape, }; pub use trace::TraceView; pub use variables::{DataSet, DataValue, Variable, VariableKind, Variables}; pub use vectorcraft_color as color; pub use vectorcraft_geom as geom; use serde::{Deserialize, Serialize}; use vectorcraft_color::{Swatch, SwatchGroup}; use vectorcraft_geom::{Affine, Point, Rect, Vec2}; mod artboard; mod document; mod document_ext; mod document_guides; mod graphic_style; mod guides; mod limits; mod records; mod units; pub use artboard::{Artboard, LockedArtboards, RetiredArtboardId, VIDEO_PIXEL_ASPECT_RANGE, VIDEO_SAFE_INSETS}; #[cfg(test)] use document_ext::free_id; pub use graphic_style::{DEFAULT_GRAPHIC_STYLE, GraphicStyle}; pub use guides::{GridPrefs, Guide, RulerMode}; #[cfg(test)] use limits::MAX_NEXT_ID; pub use limits::{DocError, FORMAT_VERSION, MAX_COORD}; pub use records::{ImageBlob, MaskEdit, NewArtIds, SavedSelection, SavedView, Symbol}; pub use units::{ColorMode, Unit, parse_number}; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Document { pub version: u32, #[serde(default)] pub title: String, /// Saved with File → Save as Template: opening it starts a new untitled document. #[serde(default, skip_serializing_if = "std::ops::Not::not")] pub template: bool, #[serde(default)] pub units: Unit, /// The rulers' zero point in document coordinates. Moving it changes measurements, never art. #[serde(default, skip_serializing_if = "skip::is_default")] pub ruler_origin: Point, /// View › Rulers › Change to Global / Artboard Rulers: whether the rulers and position fields /// measure from [`Self::ruler_origin`] (global) or from the active artboard's own origin. /// Documents saved before the choice existed measured globally and open that way. #[serde(default = "RulerMode::legacy")] pub rulers: RulerMode, /// Artboard rulers' zero point of each artboard (by [`Artboard::id`]), as an offset from its /// top-left corner; an artboard not listed measures from that corner. #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] pub artboard_ruler_origins: BTreeMap, #[serde(default)] pub color_mode: ColorMode, pub artboards: Vec, /// Top-level layers, bottom first. pub layers: Vec>, // The document-wide resources below are [`Shared`]: an edit copies the document (copy-on-write) // and the undo history keeps every version, so what an edit doesn't change stays one copy. // Reads, writes and the saved form are those of the value itself. #[serde(default)] pub swatches: Shared>, #[serde(default)] pub swatch_groups: Shared>, #[serde(default)] pub graphic_styles: Shared>, /// Character styles (besides the built-in [Normal Character Style], which may be redefined here). #[serde(default, skip_serializing_if = "skip::is_empty_vec")] pub char_styles: Shared>, /// Paragraph styles (besides the built-in [Normal Paragraph Style]). #[serde(default, skip_serializing_if = "skip::is_empty_vec")] pub para_styles: Shared>, /// Threaded text: area-type frames one story flows through, in order. #[serde(default, skip_serializing_if = "Vec::is_empty")] pub text_threads: Vec>, #[serde(default)] pub symbols: Vec, /// Object › Graph › Design…: art graphs can draw as markers. #[serde(default, skip_serializing_if = "Vec::is_empty")] pub graph_designs: Vec, #[serde(default)] pub guides: Vec, /// View → New View… (at most [`SavedView::MAX`]); saved with the document. #[serde(default, skip_serializing_if = "Vec::is_empty")] pub views: Vec, /// Select → Save Selection… (at most [`SavedSelection::MAX`]); saved with the document. #[serde(default, skip_serializing_if = "skip::is_empty_vec")] pub saved_selections: Shared>, /// Variables (data merge): named values bound to objects, applied per dataset. #[serde(default, skip_serializing_if = "crate::skip::is_default")] pub variables: Shared, #[serde(default)] pub grid: GridPrefs, #[serde(default = "ppi72")] pub raster_effects_ppi: f64, #[serde(default)] pub images: BTreeMap, /// Pattern swatch definitions (Object → Pattern). #[serde(default, skip_serializing_if = "Vec::is_empty")] pub patterns: Vec, /// Pattern editing mode, while active. #[serde(default, skip_serializing_if = "Option::is_none")] pub pattern_edit: Option, /// Opacity-mask editing mode, while active. Never saved (see [`Document::without_edit_modes`]); /// still read so files saved mid-edit by older versions load without the editing layer. #[serde(default, skip_serializing)] pub mask_edit: Option, next_id: u64, /// Foreign data preserved on round-trip. #[serde(default, skip_serializing_if = "skip::is_empty_map")] pub unknown: Shared>, /// Transparency panel → Page Isolated Blending: the page is an isolated transparency group, /// so top-level blend modes don't blend with what lies under the page. #[serde(default, skip_serializing_if = "std::ops::Not::not")] pub page_isolate: bool, /// Transparency panel → Page Knockout Group: the page's elements (its layers; neutral layers /// pass their contents through) knock each other out. #[serde(default, skip_serializing_if = "std::ops::Not::not")] pub page_knockout: bool, /// Swatches panel → Spot Colors: spot colours defined in Lab show and separate from their Lab /// values (on, the default) or from their working-CMYK equivalents (off) /// ([`Document::linked_color`]). #[serde(default = "yes", skip_serializing_if = "skip::is_true")] pub spot_use_lab: bool, /// File → Document Setup (bleed, transparency grid, paper, type options). #[serde(default, skip_serializing_if = "skip::is_default")] pub setup: DocSetup, /// Layers panel → Paste Remembers Layers: pasted objects go back into the layers (by name) /// they were copied from instead of the current layer. #[serde(default, skip_serializing_if = "std::ops::Not::not")] pub paste_remembers_layers: bool, /// File → File Info (the title is [`Document::title`]). #[serde(default, skip_serializing_if = "skip::is_default")] pub metadata: DocMetadata, /// Effect → Document Raster Effects Settings besides the resolution /// ([`Document::raster_effects_ppi`]). #[serde(default, skip_serializing_if = "skip::is_default")] pub raster_effects: RasterEffectsSettings, /// The view (zoom, centre, rotation) the document was saved with; it reopens there. Written at /// save time only, so changing the view never marks the document modified. #[serde(default, skip_serializing_if = "Option::is_none")] pub last_view: Option, /// View → Show Transparency Grid when the document was saved. Written at save time only, /// like [`Document::last_view`]; older files open with the grid hidden. #[serde(default, skip_serializing_if = "std::ops::Not::not")] pub transparency_grid: bool, /// The layers, sublayers and groups open in the Layers panel when the document was saved; they /// reopen that way. `None`: the default, only the top-level layers open. Written at save time /// only, like [`Document::last_view`]. #[serde(default, skip_serializing_if = "Option::is_none")] pub layers_open: Option>, /// Edit → Assign Profile: the profiles the document is tagged with (files before format v3 /// kept them in `unknown`, see [`Document::migrate_color_profiles`]). #[serde(default, skip_serializing_if = "ColorProfiles::is_empty")] pub color_profiles: ColorProfiles, /// File → Export for Screens: the settings it last exported with (`document.exportForScreens` /// params), so the dialog reopens on them. #[serde(default, skip_serializing_if = "serde_json::Map::is_empty")] pub export_settings: serde_json::Map, /// Top-level keys this version doesn't know (written by a newer one), kept so saving doesn't /// lose them. Separate from [`Document::unknown`], which holds foreign data by design. #[serde(flatten)] pub extra: serde_json::Map, /// User slices (Slice tool, Object → Slice); object slices are [`Node::slice`]. #[serde(default, skip_serializing_if = "Vec::is_empty")] pub slices: Vec, /// Object → Slice → Clip to Artboard: slices are clipped to the artboards and auto slices fill /// them (on, the default); off, auto slices cover the art and the slices. #[serde(default = "yes", skip_serializing_if = "skip::is_true")] pub slices_clip_to_artboard: bool, /// File → Print: the print settings saved with the document (`vectorcraft_pdf::PrintSettings` /// as JSON: the print engine sits above this crate); `None` until they are set up. #[serde(default, skip_serializing_if = "Option::is_none")] pub print_setup: Option, /// Window → Asset Export: art collected for export ([`ExportAsset`]), in panel order. Their /// export settings are Export for Screens' ([`Document::export_settings`]). #[serde(default, skip_serializing_if = "Vec::is_empty")] pub assets: Vec, /// Window › SVG Interactivity › Link JavaScript Files: the JavaScript files (paths or URLs, as /// given) SVG export links with `