//! VectorCraft SVG import and export. //! //! * [`export`] / [`export_full`] write a [`Document`] as SVG 1.1 with our own writer, as set by //! [`ExportOptions`] (presentation attributes, inline styles, style entities or internal CSS //! classes; gradients in `` with `userSpaceOnUse`; clip groups as ``; images //! embedded as `data:` URIs or linked; text as ``/`` or outlines; symbols as one //! `` each with a `` per instance; hidden layers left out, or kept not displayed), //! in UTF-8, UTF-16 or ISO 8859-1 ([`Output::bytes`]), as SVG 1.1 or a simplified SVG Tiny 1.2 //! ([`Profile`]), optionally with the fonts type uses embedded as `@font-face` subsets. //! * [`import`] / [`import_with_report`] parse SVG with `usvg` and convert its normalized tree into //! document nodes with all transforms baked into the geometry. //! * [`css_rules`] writes the CSS web pages style objects with (the CSS Properties panel): shapes' //! paints as backgrounds and borders, type as fonts, shadows as `box-shadow`, each property //! written as SVG export writes it in its style sheets. //! //! ## Export approximations //! //! * **Stroke alignment** is not expressible in SVG 1.1. An *inside* stroke of a closed path is //! written as a stroke of double width clipped to the path's own shape (``); an //! *outside* stroke as a stroke of double width masked by a `` that hides the path's //! interior. Both are visually exact but re-import as a group (clip group / masked stroke) //! rather than an aligned stroke. Open paths have no inside: their strokes are written centred, //! as the canvas draws them. //! * **Arrowheads, width profiles, dashes fitted to corners and dotted dashes** are written as the //! filled outlines the canvas paints (the line and each arrowhead, grouped under the stroke's //! opacity); **brushed strokes** as their brush art. Both re-import as filled paths. //! * **Multiple fills/strokes** (or per-fill/stroke blend modes) are written as a `` holding one //! `` per appearance item, in paint order. //! * Live geometry effects are written as their result and raster effects (shadows, glows, blurs) //! as SVG filters. //! * **Freeform gradients**, which SVG can't express, fill or stroke objects as an `` of //! their colour field (sampled as the canvas samples it) clipped to the shape or the stroke's //! outline; on characters they are written as linear gradients. Gradients along or across //! strokes are written as slices of linear gradients clipped to the stroke. //! * **Symbol instances** the shared `` would paint differently from the canvas (stained, //! scaled with strokes, turned or scaled with effects, brushes or live objects, or holding //! pattern paints or unlinked masks, which stay on the page) are written as their own art. //! //! ## Import approximations //! //! * One CSS pixel (user unit) is one point, as on export, and absolute lengths (`in`, `cm`, `mm`, //! `pt`, `pc`) keep their physical size (72 pt per inch): `font-size="12pt"` is 12 pt. A root //! `width`/`height` in absolute units keeps its physical size, so a 210 mm SVG opens on a 210 mm //! artboard, its user units being CSS pixels of it (96 per inch), the other side's too when only //! one is absolute. The document's units follow the unit of the root `width` (pixels when it has //! none). //! * `` imports as a luminance opacity mask; a mask we exported keeps its options and art //! (its `data-vectorcraft-mask="noclip invert"` lists the options that differ from clipping and //! not inverted). Nested clip paths (``) are clip groups in clip groups. //! * Filters that are a Gaussian blur, a drop shadow (`feDropShadow` or the usual chains of offset, //! blur, flood or colour matrix, composite and merge), a glow or a feather (as we export them) //! become those live effects; other filters are ignored (reported as warnings). //! * `` becomes a pattern swatch (its content clipped to the tile) painted with the //! pattern's placement. //! * `` with `` becomes a symbol and its instances. A `` that shows the symbol //! differently (inherited paint, a viewport that cuts it) or that the canvas would paint //! differently (see the export's symbol rules) becomes plain art, as do ``s of other //! elements. //! * A `` layer (top-level, or inside one) that isn't displayed (`display: none`) comes back //! as a hidden layer; other undisplayed objects as hidden objects, unless a `` refers to //! them. //! * `spreadMethod` reflect and repeat are expanded into stops over the painted area (at most //! 64 periods). //! * `` files are read through [`ImportOptions`]: rasters stay linked, SVG files (and SVG //! `data:` URLs) become art (without their text), missing ones a placeholder keeping the link. //! * Text lines after the first start at the first line's x; text in a clip path is ignored; //! absolute positions inside type on a path are ignored; vertical text sets every glyph sideways. //! //! ## Import text //! //! usvg only keeps `` when fonts are loaded; we don't load a font database (too expensive and //! unavailable on wasm), so `` elements are read from the XML as live [`TextObject`]s. Each //! one is swapped for a placeholder before usvg runs, so the text keeps its z-order, parent group, //! clip, mask, opacity, link, `` instances and gradient or pattern paints. The style cascade //! supports type, `#id`, `.class` (several), attribute, descendant and child selectors. //! `` becomes type on a path (`startOffset`, `side`), vertical `writing-mode` type on a //! vertical path; per-character `x`/`y`/`dx`/`dy`/`rotate`, `baseline-shift`, numeric weights, //! letter and word spacing become character attributes (line breaks, kerning, baseline shift, //! rotation and style names). #![forbid(unsafe_code)] mod css; mod export; mod import; pub use css::{CssOptions, CssRule, CssSheet, CssUnits, css_rules}; use serde::{Deserialize, Serialize}; pub use vectorcraft_doc::Document; pub use vectorcraft_doc::TextObject; /// How style properties are written (SVG Options → Styling). #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum Styling { /// `fill="#ff0000"` attributes. #[default] #[serde(rename = "presentation")] PresentationAttributes, /// `style="fill:#ff0000"`. #[serde(rename = "style")] InlineStyle, /// `style="&st1;"`, each distinct declaration block an XML entity declared in the DOCTYPE. #[serde(rename = "entities")] StyleEntities, /// `class="cls-1"` with a `