use super::ids::LayerColor; use super::live_shape::LiveShape; use super::{Affine, Arc, BlendSpec, Deserialize, EnvelopeKind, FillRule, GradientMesh, Node, PathData, RepeatSpec, Serialize, TextObject, yes}; /// Embedded or linked raster image. #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct ImageObject { /// Key into [`crate::Document::images`]. pub key: String, pub width: u32, pub height: u32, /// Maps pixel space (0..w, 0..h) into the document. pub xf: Affine, /// The file a linked image shows (File → Place with Link); `None`: embedded. Files saved /// before links had details hold just the path. #[serde(default, skip_serializing_if = "Option::is_none", deserialize_with = "crate::links::de_link")] pub link: Option, /// Links panel → Placement Options: how a file read again takes this image's place. #[serde(default, skip_serializing_if = "crate::skip::is_default")] pub placement: crate::links::PlacementOptions, /// The pixels of an image in art made outside a document (type's outlines, a glyph's art), /// which has no [`crate::Document::images`] to keep them in. A document takes them in /// ([`crate::Document::adopt_pixels`]): no image in one holds its own. #[serde(skip)] pub pixels: Option>, } /// Written with its `type` first, then its fields (an internally tagged enum); read by /// [`kind_de`], which takes the fields straight from the file when `type` comes first. #[derive(Clone, Debug, PartialEq, Serialize)] #[serde(tag = "type", rename_all = "lowercase")] pub enum NodeKind { /// A top-level layer or sublayer. Layer { color: LayerColor, #[serde(default)] template: bool, #[serde(default = "yes")] printable: bool, children: Vec>, /// Layer clipping mask: the first (bottom-most) child clips the others, as in a clip group. #[serde(default, skip_serializing_if = "crate::skip::is_default")] clip: bool, /// Layer Options → Preview: off, the layer's art is drawn (and clicked) in outline on screen /// (Ctrl-click its eye in the Layers panel). #[serde(default = "yes", skip_serializing_if = "crate::skip::is_true")] preview: bool, /// Layer Options → Dim Images to: on screen, images on the layer show faded to this /// percentage (0–100); `None`: not dimmed. #[serde(default, skip_serializing_if = "Option::is_none")] dim_images: Option, }, /// A group. With `clip`, the first (bottom-most) child is the clipping path. Group { children: Vec>, #[serde(default)] clip: bool, }, Path { path: PathData, #[serde(default, skip_serializing_if = "crate::skip::is_default")] rule: FillRule, /// Boxed: few paths are live shapes, and the shape is larger than the rest of the variant. #[serde(default, skip_serializing_if = "Option::is_none")] live: Option>, /// This path acts as a clipping path inside a clip group. #[serde(default, skip_serializing_if = "crate::skip::is_default")] clipping: bool, /// Guide (non-printing) path. #[serde(default, skip_serializing_if = "crate::skip::is_default")] guide: bool, }, /// Compound path: children are paths painted as one with the compound's appearance. Compound { children: Vec>, #[serde(default)] rule: FillRule, }, Text(Box), /// Boxed, as the other large kinds: a node is as large as its largest kind. Image(Box), SymbolInstance { symbol: String, xf: Affine, }, /// Live blend: the key objects (paint order) plus spacing/orientation/spine; the intermediate /// steps are evaluated on demand (`live::blend_expand`). Blend { children: Vec>, #[serde(default)] spec: Box, }, /// Live envelope distortion of `content`. Envelope { content: Vec>, kind: Box, /// Envelope Options → Fidelity (0–100). #[serde(default = "crate::live::default_fidelity")] fidelity: f64, /// Edit Contents mode (the content, not the envelope, is edited). #[serde(default)] editing: bool, /// Envelope Options besides Fidelity. #[serde(default, skip_serializing_if = "crate::skip::is_default")] options: crate::live::EnvelopeOptions, /// The envelope's own axes (→ the document): the transforms it took since it was made that /// the page axes can't stand for (rotation, shear, reflection). The content maps from its /// bounds in this frame, and a warp bends along it. #[serde(default, skip_serializing_if = "crate::skip::is_default")] frame: Box, }, /// Gradient mesh. Mesh(GradientMesh), /// Live Repeat (radial / grid / mirror) of source art. Repeat(RepeatSpec), /// A placed document: an artboard of another VectorCraft file, or a page of a PDF or `.ai` /// file, linked and locked (see [`crate::placed_document`]). PlacedDocument(Box), /// Object › Intertwine: the objects keep their own stacking order, except where one of them /// is drawn over the others: the area [`NodeExtras::intertwine`] of that member (see /// [`crate::intertwine`]). Intertwine { children: Vec>, }, } /// Reading a [`NodeKind`]. Derived, an internally tagged enum buffers each kind (with every /// object inside a group, again at each level of nesting) before reading it, since `type` may come /// anywhere among its fields. Files have it first (it is written first): then the kind is read /// straight from the file. Otherwise (hand-written files, values whose keys are sorted) the /// fields before `type` are buffered, and only those. mod kind_de { use std::fmt; use std::sync::Arc; use serde::de::value::{MapAccessDeserializer, StringDeserializer}; use serde::de::{self, DeserializeSeed, Deserializer, EnumAccess, IntoDeserializer, MapAccess, VariantAccess, Visitor}; use serde::{Deserialize, forward_to_deserialize_any}; use vectorcraft_geom::{Affine, FillRule, PathData}; use super::{LayerColor, LiveShape, Node, NodeKind, yes}; use crate::live::{BlendSpec, EnvelopeKind, GradientMesh}; use crate::pattern::RepeatSpec; use crate::text::TextObject; /// The kinds and their fields as [`NodeKind`] has them, with the defaults its field attributes /// note (the compiler checks that every field is here), read externally tagged: the kind from /// `type`, its fields from the rest of the object ([`Tagged`]). #[derive(Deserialize)] #[serde(remote = "NodeKind", rename_all = "lowercase")] enum KindDef { Layer { color: LayerColor, #[serde(default)] template: bool, #[serde(default = "yes")] printable: bool, children: Vec>, #[serde(default)] clip: bool, #[serde(default = "yes")] preview: bool, #[serde(default)] dim_images: Option, }, Group { children: Vec>, #[serde(default)] clip: bool, }, Path { path: PathData, #[serde(default)] rule: FillRule, #[serde(default)] live: Option>, #[serde(default)] clipping: bool, #[serde(default)] guide: bool, }, Compound { children: Vec>, #[serde(default)] rule: FillRule, }, Text(Box), Image(Box), SymbolInstance { symbol: String, xf: Affine, }, Blend { children: Vec>, #[serde(default)] spec: Box, }, Envelope { content: Vec>, kind: Box, #[serde(default = "crate::live::default_fidelity")] fidelity: f64, #[serde(default)] editing: bool, #[serde(default)] options: crate::live::EnvelopeOptions, #[serde(default)] frame: Box, }, Mesh(GradientMesh), Repeat(RepeatSpec), PlacedDocument(Box), Intertwine { children: Vec>, }, } impl<'de> Deserialize<'de> for NodeKind { fn deserialize>(d: D) -> Result { d.deserialize_map(KindVisitor) } } struct KindVisitor; impl<'de> Visitor<'de> for KindVisitor { type Value = NodeKind; fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("an object kind (a map with `type`)") } fn visit_map>(self, mut map: A) -> Result { let mut before = vec![]; while let Some(key) = map.next_key::()? { if key == "type" { let tag: String = map.next_value()?; return KindDef::deserialize(Tagged { tag, map: Buffered { before: before.into_iter(), value: None, map } }); } before.push((key, map.next_value::()?)); } Err(de::Error::missing_field("type")) } } /// The fields read before `type` (buffered), then the rest of the object. struct Buffered { before: std::vec::IntoIter<(String, serde_json::Value)>, value: Option, map: A, } impl<'de, A: MapAccess<'de>> MapAccess<'de> for Buffered { type Error = A::Error; fn next_key_seed>(&mut self, seed: K) -> Result, A::Error> { match self.before.next() { Some((key, value)) => { self.value = Some(value); seed.deserialize(StringDeserializer::::new(key)).map(Some) } None => self.map.next_key_seed(seed), } } fn next_value_seed>(&mut self, seed: V) -> Result { match self.value.take() { Some(value) => seed.deserialize(value).map_err(de::Error::custom), None => self.map.next_value_seed(seed), } } } /// An object whose `type` was read: the kind is `tag`, its fields are the rest of `map`. struct Tagged { tag: String, map: A, } impl<'de, A: MapAccess<'de>> Deserializer<'de> for Tagged { type Error = A::Error; fn deserialize_any>(self, visitor: V) -> Result { visitor.visit_enum(self) } forward_to_deserialize_any! { bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map struct enum identifier ignored_any } } impl<'de, A: MapAccess<'de>> EnumAccess<'de> for Tagged { type Error = A::Error; type Variant = Fields; fn variant_seed>(self, seed: V) -> Result<(V::Value, Fields), A::Error> { let kind = seed.deserialize(self.tag.into_deserializer())?; Ok((kind, Fields(self.map))) } } /// A kind's fields: the rest of the object. struct Fields(A); impl<'de, A: MapAccess<'de>> VariantAccess<'de> for Fields { type Error = A::Error; fn unit_variant(self) -> Result<(), A::Error> { Err(de::Error::invalid_type(de::Unexpected::UnitVariant, &"an object kind with fields")) } fn newtype_variant_seed>(self, seed: T) -> Result { seed.deserialize(MapAccessDeserializer::new(self.0)) } fn tuple_variant>(self, _: usize, _: V) -> Result { Err(de::Error::invalid_type(de::Unexpected::TupleVariant, &"an object kind with fields")) } fn struct_variant>(self, _: &'static [&'static str], visitor: V) -> Result { visitor.visit_map(self.0) } } }