//! Live effects in SVG export: a fill or stroke's own raster effects become filters on that item's //! element, and geometry effects on type are baked through its outlines. // Integration tests: unwrapping and panicking on failure is fine here, unlike in shipped code (AGENTS.md › Robustness). #![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] use serde_json::json; use vectorcraft_color::{Color, Paint}; use vectorcraft_doc::{Appearance, CharStyle, Document, Effect, Node, NodeId, NodeKind, TextObject}; use vectorcraft_geom::{Point, Rect, shapes}; use vectorcraft_svg::{ExportOptions, export}; fn doc_with(mut n: Node) -> Document { let mut d = Document::new(200.0, 200.0); n.id = d.alloc_id(); let l = d.layers[0].id; d.insert(Some(l), 0, n).unwrap(); d } fn fx(id: &str) -> Effect { Effect { id: id.into(), params: json!({}), visible: true } } #[test] fn revolve_caps_export_as_additional_vector_faces_and_leave_source_editable() { let path = vectorcraft_geom::PathData::from_bezpath(&vectorcraft_geom::BezPath::from_svg("M100 30 L100 160").unwrap()); let mut n = Node::path(NodeId(0), path, Appearance::basic(Paint::solid(Color::rgb(0.8, 0.2, 0.1)), Paint::None, 0.0)); n.appearance.effects.push(Effect { params: json!({"offset":30,"rotationX":-35,"segments":16,"cap":false}), ..fx("threeD.revolve") }); let mut d = doc_with(n); let open = export(&d, &ExportOptions::default()); let id = d.layers[0].children().unwrap()[0].id; d.node_mut(id).unwrap().appearance.effects[0].params["cap"] = json!(true); let before = d.clone(); let capped = export(&d, &ExportOptions::default()); assert_eq!(capped.matches(" 50, "Revolve should export vector surfaces"); assert!(!svg.contains(" 50); assert!(svg.contains("gradientUnits=\"userSpaceOnUse\"")); assert!(svg.matches(" 100); assert!(svg.matches("stroke-linejoin=\"round\"").count() > 100, "seam strokes keep bounded joins in SVG"); assert!(!svg.contains("stroke-miterlimit")); assert!(!svg.contains("").map(|i| open + i).unwrap()]; assert!(inside.contains("stroke=\"#000000\"") && !inside.contains("fill=\"#ffffff\""), "{inside}"); assert!(svg[..open].contains("fill=\"#ffffff\""), "{svg}"); // A hidden effect exports nothing. let mut n = Node::path( NodeId(0), shapes::rectangle(Rect::new(20.0, 20.0, 80.0, 80.0)), Appearance::basic(Paint::solid(Color::WHITE), Paint::solid(Color::BLACK), 2.0), ); n.appearance.items[1].effects_mut().push(Effect { visible: false, ..fx("stylize.outerGlow") }); assert!(!export(&doc_with(n), &ExportOptions::default()).contains("