From 1cd8665df7b56c7b79fa8657002f2dff9c63916d Mon Sep 17 00:00:00 2001 From: Adrien Burgun Date: Sat, 6 Aug 2022 13:08:59 +0200 Subject: [PATCH] :fire: Snuggle generator [WIP] --- .gitignore | 1 + Cargo.toml | 1 + README.md | 12 ++ snuggle.toml | 4 + species/blobfox/assets/snuggle_right.svg | 49 ++++++ species/blobfox/species.toml | 5 + .../blobfox/templates/body-snuggle.mustache | 15 ++ species/blobfox/templates/body.mustache | 3 + species/blobfox/templates/eyes.mustache | 4 + species/blobfox/templates/hands.mustache | 16 +- species/blobfox/templates/nose.mustache | 3 + species/blobfox/variants/heart_ace.mustache | 6 +- .../variants/heart_demisexual.mustache | 6 +- species/blobfox/variants/heart_enby.mustache | 6 +- species/blobfox/variants/heart_pan.mustache | 6 +- .../blobfox/variants/heart_progress.mustache | 6 +- .../blobfox/variants/snuggle_right.mustache | 9 ++ src/bin/snuggle.rs | 148 ++++++++++++++++++ src/export.rs | 70 +++++++-- src/lib.rs | 3 + src/main.rs | 28 ++-- src/template.rs | 125 +++++++++------ vector/blobfox_snuggle_right.svg | 125 +++++++++++++++ 23 files changed, 572 insertions(+), 79 deletions(-) create mode 100644 snuggle.toml create mode 100644 species/blobfox/assets/snuggle_right.svg create mode 100644 species/blobfox/templates/body-snuggle.mustache create mode 100644 species/blobfox/variants/snuggle_right.mustache create mode 100644 src/bin/snuggle.rs create mode 100644 src/lib.rs create mode 100644 vector/blobfox_snuggle_right.svg diff --git a/.gitignore b/.gitignore index 9d605e3..8bc691d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ original/ output/ Cargo.lock target/ +blobfox-*.zip diff --git a/Cargo.toml b/Cargo.toml index 6b1fdcd..b0f2f12 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,3 +20,4 @@ resvg = "0.23" usvg = "0.23" tiny-skia = "0.6" png = "0.17" +css-color-parser = "0.1.2" diff --git a/README.md b/README.md index 0c3f86d..3cc3721 100644 --- a/README.md +++ b/README.md @@ -41,9 +41,21 @@ If you'd like to help, there are a few things that need attention outside of imp - german shepherd - collie - sheep + - bird - etc. - clean up the SVG for the existing emotes (the `clean` binary in `feat/template` is meant to do the heavy-lifting) +### TODO + +- set_stroke! +- blobfox_ohmy +- blobfox_trumpet +- blobfox_highfive +- googly eyes? +- tea/coffee +- sad +- uwu + ## License All the code, images and assets of this repository are made available under the Apache 2.0 license. diff --git a/snuggle.toml b/snuggle.toml new file mode 100644 index 0000000..e4009fa --- /dev/null +++ b/snuggle.toml @@ -0,0 +1,4 @@ +name = "snuggle" +dx = -60 +dy = -40 +transform = "scale(1.02 1.02) translate(-1.5 -1)" diff --git a/species/blobfox/assets/snuggle_right.svg b/species/blobfox/assets/snuggle_right.svg new file mode 100644 index 0000000..1941986 --- /dev/null +++ b/species/blobfox/assets/snuggle_right.svg @@ -0,0 +1,49 @@ + + + blobfox + + + + + + + + + + + + + + + + + + + + + + + + + + + + Blobfox team (https://git.shadamethyst.xyz/adri326/blobfox), licensed under the Apache 2.0 License + + + blobfox + + + Feuerfuchs + + + https://git.shadamethyst.xyz/adri326/blobfox + + + Shad Amethyst + + + + + + diff --git a/species/blobfox/species.toml b/species/blobfox/species.toml index aec1dad..abf0481 100644 --- a/species/blobfox/species.toml +++ b/species/blobfox/species.toml @@ -6,6 +6,7 @@ body_color = "#ff8702" ear_color = "#313131" ear_fluff_color = "#ebdccc" hand_color = "#ff8702" +hand_stroke_color = "#313131" tail_color = "#ff8702" [variants] @@ -51,3 +52,7 @@ heart_enby = ["body-basic", "eyes-basic", "left-hand", "right-hand", "holding", heart_ace = ["body-basic", "eyes-basic", "left-hand", "right-hand", "holding", "big-object"] heart_demisexual = ["body-basic", "eyes-basic", "left-hand", "right-hand", "holding", "big-object"] heart_pan = ["body-basic", "eyes-basic", "left-hand", "right-hand", "holding", "big-object"] + +# Snuggle +snuggle_right = ["body-snuggle", "eyes-snuggle", "mouth-w"] +snuggle_right_shadow = ["body-snuggle", "eyes-snuggle", "mouth-w"] diff --git a/species/blobfox/templates/body-snuggle.mustache b/species/blobfox/templates/body-snuggle.mustache new file mode 100644 index 0000000..db2c8f7 --- /dev/null +++ b/species/blobfox/templates/body-snuggle.mustache @@ -0,0 +1,15 @@ +{{! Left ear }} +{{#set-fill}} {{vars.ear_color}} | {{#snuggle_right}}#left-ear{{/snuggle_right}} {{/set-fill}} + +{{! Body }} + + + {{#snuggle_right}}#body{{/snuggle_right}} + + +{{#set-fill}} {{vars.body_color}} | {{#snuggle_right}}#body{{/snuggle_right}} {{/set-fill}} +{{#set-fill}} {{vars.body_color}} | {{#snuggle_right}}#hair{{/snuggle_right}} {{/set-fill}} + +{{! Right ear }} +{{#set-fill}} {{vars.ear_color}} | {{#snuggle_right}}#right-ear{{/snuggle_right}} {{/set-fill}} +{{#set-fill}} {{vars.ear_fluff_color}} | {{#snuggle_right}}#right-ear-fluff{{/snuggle_right}} {{/set-fill}} diff --git a/species/blobfox/templates/body.mustache b/species/blobfox/templates/body.mustache index d4239d8..ca18fc8 100644 --- a/species/blobfox/templates/body.mustache +++ b/species/blobfox/templates/body.mustache @@ -8,4 +8,7 @@ {{#tags.body-comfy}} {{>body-comfy}} {{/tags.body-comfy}} + {{#tags.body-snuggle}} + {{>body-snuggle}} + {{/tags.body-snuggle}} diff --git a/species/blobfox/templates/eyes.mustache b/species/blobfox/templates/eyes.mustache index 9658a4e..214917b 100644 --- a/species/blobfox/templates/eyes.mustache +++ b/species/blobfox/templates/eyes.mustache @@ -23,4 +23,8 @@ {{#blush}}#left-eye{{/blush}} {{#blush}}#right-eye{{/blush}} {{/tags.eyes-closed}} + {{#tags.eyes-snuggle}} + {{#snuggle_right}}#left-eye{{/snuggle_right}} + {{#snuggle_right}}#right-eye{{/snuggle_right}} + {{/tags.eyes-snuggle}} diff --git a/species/blobfox/templates/hands.mustache b/species/blobfox/templates/hands.mustache index e268cb6..57317b2 100644 --- a/species/blobfox/templates/hands.mustache +++ b/species/blobfox/templates/hands.mustache @@ -1,7 +1,19 @@ {{#tags.hands-reach}} - {{#set-fill}} {{vars.hand_color}} | {{#reach_aww}}#left-hand{{/reach_aww}} {{/set-fill}} - {{#set-fill}} {{vars.hand_color}} | {{#reach_aww}}#right-hand{{/reach_aww}} {{/set-fill}} + {{#set-stroke}} + {{vars.hand_stroke_color}} | + {{#set-fill}} + {{vars.hand_color}} + | {{#reach_aww}}#left-hand{{/reach_aww}} + {{/set-fill}} + {{/set-stroke}} + {{#set-stroke}} + {{vars.hand_stroke_color}} | + {{#set-fill}} + {{vars.hand_color}} + | {{#reach_aww}}#right-hand{{/reach_aww}} + {{/set-fill}} + {{/set-stroke}} {{/tags.hands-reach}} {{#tags.hand-3c}} {{#tags.holding}} diff --git a/species/blobfox/templates/nose.mustache b/species/blobfox/templates/nose.mustache index edbdedf..7bb93d6 100644 --- a/species/blobfox/templates/nose.mustache +++ b/species/blobfox/templates/nose.mustache @@ -14,6 +14,9 @@ {{#tags.eyes-closed}} {{#blush}}#nose-outline{{/blush}} {{/tags.eyes-closed}} + {{#tags.eyes-snuggle}} + {{#snuggle_right}}#nose-outline{{/snuggle_right}} + {{/tags.eyes-snuggle}} {{#base}}#nose{{/base}} diff --git a/species/blobfox/variants/heart_ace.mustache b/species/blobfox/variants/heart_ace.mustache index 90e5eeb..abf3d49 100644 --- a/species/blobfox/variants/heart_ace.mustache +++ b/species/blobfox/variants/heart_ace.mustache @@ -9,7 +9,11 @@ - + + {{#heart}}#heart{{/heart}} + + + {{#flag_ace}}{{/flag_ace}} diff --git a/species/blobfox/variants/heart_demisexual.mustache b/species/blobfox/variants/heart_demisexual.mustache index c508c39..fff4d22 100644 --- a/species/blobfox/variants/heart_demisexual.mustache +++ b/species/blobfox/variants/heart_demisexual.mustache @@ -9,7 +9,11 @@ - + + {{#heart}}#heart{{/heart}} + + + {{#flag_demisexual}}{{/flag_demisexual}} diff --git a/species/blobfox/variants/heart_enby.mustache b/species/blobfox/variants/heart_enby.mustache index 71ccdff..05c2eb4 100644 --- a/species/blobfox/variants/heart_enby.mustache +++ b/species/blobfox/variants/heart_enby.mustache @@ -9,7 +9,11 @@ - + + {{#heart}}#heart{{/heart}} + + + {{#flag_enby}}{{/flag_enby}} diff --git a/species/blobfox/variants/heart_pan.mustache b/species/blobfox/variants/heart_pan.mustache index ec04719..db05a5e 100644 --- a/species/blobfox/variants/heart_pan.mustache +++ b/species/blobfox/variants/heart_pan.mustache @@ -9,7 +9,11 @@ - + + {{#heart}}#heart{{/heart}} + + + {{#flag_pan}}{{/flag_pan}} diff --git a/species/blobfox/variants/heart_progress.mustache b/species/blobfox/variants/heart_progress.mustache index d515e0f..3ffc9f5 100644 --- a/species/blobfox/variants/heart_progress.mustache +++ b/species/blobfox/variants/heart_progress.mustache @@ -9,7 +9,11 @@ - + + {{#heart}}#heart{{/heart}} + + + {{#flag_progress}}{{/flag_progress}} diff --git a/species/blobfox/variants/snuggle_right.mustache b/species/blobfox/variants/snuggle_right.mustache new file mode 100644 index 0000000..6a0a83d --- /dev/null +++ b/species/blobfox/variants/snuggle_right.mustache @@ -0,0 +1,9 @@ +{{>header}} + {{>body}} + + + {{>eyes}} + {{>nose}} + {{>mouth}} + +{{>footer}} diff --git a/src/bin/snuggle.rs b/src/bin/snuggle.rs new file mode 100644 index 0000000..d4cb8b0 --- /dev/null +++ b/src/bin/snuggle.rs @@ -0,0 +1,148 @@ +//! Very crude tool for generating snuggle emotes +use clap::Parser; +use std::fmt::Write; +use std::path::PathBuf; +use serde::{Serialize, Deserialize}; +use xmltree::{Element, XMLNode}; + +use blobfox_template::{ + parse, + template, + export, +}; + +#[derive(Serialize, Deserialize, Debug)] +struct Desc { + dx: f64, + dy: f64, + + scale: Option, + + #[serde(default)] + transform: String, +} + +fn main() { + let args = Args::parse(); + + let left = std::fs::read_to_string(&args.input_left).unwrap_or_else(|err| { + panic!("Couldn't open {}: {}", args.input_right.display(), err); + }); + let right = std::fs::read_to_string(&args.input_right).unwrap_or_else(|err| { + panic!("Couldn't open {}: {}", args.input_right.display(), err); + }); + + let desc = std::fs::read_to_string(&args.desc).unwrap_or_else(|err| { + panic!("Couldn't open {}: {}", args.desc.display(), err); + }); + let desc: Desc = toml::from_str(&desc).unwrap(); + + let snuggle = generate_snuggle(left, right, desc); + let snuggle = export::xml_to_str(&snuggle).unwrap(); + + let output_dir = args.output_dir.clone().unwrap_or(PathBuf::from("output/")); + + export::export( + snuggle, + &output_dir, + args.name.clone(), + &args.clone().into() + ).unwrap(); +} + +fn generate_snuggle(left: String, right: String, desc: Desc) -> Element { + let left_usvg = export::get_usvg(&left).unwrap(); + let left_bbox = left_usvg.svg_node().view_box.rect; + + // + let mut mask = Element::new("mask"); + mask.attributes.insert("id".to_string(), "snuggle-mask".to_string()); + + let mut rect = Element::new("rect"); + rect.attributes.insert("fill".to_string(), "white".to_string()); + // TODO: use scale? + rect.attributes.insert("x".to_string(), (desc.dx + left_bbox.x()).to_string()); + rect.attributes.insert("y".to_string(), (desc.dy + left_bbox.y()).to_string()); + rect.attributes.insert("width".to_string(), left_bbox.width().to_string()); + rect.attributes.insert("height".to_string(), left_bbox.height().to_string()); + + mask.children.push(XMLNode::Element(rect)); + + let mut right_mask = Element::new("g"); + right_mask.attributes.insert("transform".to_string(), desc.transform); + + let mut right_xml = Element::parse(right.as_bytes()).unwrap(); + template::set_fill("#000000", &mut right_xml); + template::set_stroke("#000000", &mut right_xml); + + for child in right_xml.children { + if let XMLNode::Element(child) = child { + right_mask.children.push(XMLNode::Element(child)); + } + } + + mask.children.push(XMLNode::Element(right_mask)); + + let mut right_xml = Element::parse(right.as_bytes()).unwrap(); + let left_xml = Element::parse(left.as_bytes()).unwrap(); + + let mut left_group = Element::new("g"); + left_group.attributes.insert("transform".to_string(), format!( + "translate({} {})", + desc.dx, + desc.dy + )); + left_group.children = left_xml.children; + + let mut left_group2 = Element::new("g"); + left_group2.attributes.insert("mask".to_string(), "url(#snuggle-mask)".to_string()); + left_group2.children.push(XMLNode::Element(left_group)); + + let mut res = Element::new("svg"); + res.attributes.insert("xmlns".to_string(), "http://www.w3.org/2000/svg".to_string()); + res.attributes.insert("version".to_string(), "1.1".to_string()); + res.attributes.insert("width".to_string(), "128".to_string()); + res.attributes.insert("height".to_string(), "128".to_string()); + res.children.push(XMLNode::Element(mask)); + res.children.append(&mut right_xml.children); + res.children.push(XMLNode::Element(left_group2)); + + res +} + +#[derive(Parser, Clone)] +#[clap(author, version, about, long_about = None)] +struct Args { + #[clap(value_parser)] + input_left: PathBuf, + + #[clap(value_parser)] + input_right: PathBuf, + + #[clap(short, long, value_parser)] + desc: PathBuf, + + /// Disable automatically resizing the SVG's viewBox, defaults to false + #[clap(short, long, value_parser, default_value = "false")] + no_resize: bool, + + #[clap(long, value_parser)] + name: String, + + /// Dimension to export the images as; can be specified multiple times + #[clap(long, value_parser)] + dim: Vec, + + /// Output directory + #[clap(short, long, value_parser)] + output_dir: Option, +} + +impl From for export::ExportArgs { + fn from(args: Args) -> export::ExportArgs { + export::ExportArgs { + no_resize: args.no_resize, + dim: args.dim, + } + } +} diff --git a/src/export.rs b/src/export.rs index 27c86db..8f730b8 100644 --- a/src/export.rs +++ b/src/export.rs @@ -48,6 +48,28 @@ impl From for ExportError { } } +pub struct ExportArgs { + pub no_resize: bool, + pub dim: Vec, +} + +pub fn get_usvg(svg_str: &str) -> Result { + let usvg_options = Options::default(); + Tree::from_str(svg_str, &usvg_options.to_ref()) +} + +pub fn get_xml(svg_str: &str) -> Result { + Element::parse(svg_str.as_bytes()) +} + +pub fn xml_to_str(svg_xml: &Element) -> Result { + let mut s: Vec = Vec::new(); + + svg_xml.write(&mut s)?; + + Ok(String::from_utf8(s)?) +} + fn get_new_bbox(svg: &Tree) -> Option<(f64, f64, f64, f64)> { let bbox = svg.root().calculate_bbox()?; @@ -70,25 +92,45 @@ fn get_new_bbox(svg: &Tree) -> Option<(f64, f64, f64, f64)> { } } -fn get_usvg(svg_str: &str) -> Result { - let usvg_options = Options::default(); - Tree::from_str(svg_str, &usvg_options.to_ref()) -} +/// Removes all elements marked with `blobfox-ignore-size="true"` -fn get_xml(svg_str: &str) -> Result { - Element::parse(svg_str.as_bytes()) -} +macro_rules! strip { + ( $name:tt, $attribute:expr ) => { + fn $name(svg_str: &str) -> Result { + let mut xml = get_xml(svg_str)?; -fn xml_to_str(svg_xml: &Element) -> Result { - let mut s: Vec = Vec::new(); + fn rec(element: &mut Element) { + // TODO: replace with Vec::drain_filter once https://github.com/rust-lang/rust/issues/43244 gets merged + for child in std::mem::take(&mut element.children) { + match child { + XMLNode::Element(mut child) => { + if let Some("true") = child.attributes.get($attribute).map(|s| s.as_str()) { + continue + } - svg_xml.write(&mut s)?; + rec(&mut child); - Ok(String::from_utf8(s)?) + element.children.push(XMLNode::Element(child)); + } + child => element.children.push(child), + } + } + } + + rec(&mut xml); + + xml_to_str(&xml) + } + } } +strip!(strip_ignore_size, "blobfox-ignore-size"); +strip!(strip_only_size, "blobfox-only-size"); + pub fn resize(svg_str: String) -> Result { - if let Some(new_bbox) = get_new_bbox(&get_usvg(&svg_str)?) { + let stripped = strip_ignore_size(&svg_str)?; + + if let Some(new_bbox) = get_new_bbox(&get_usvg(&stripped)?) { let mut svg_xml = get_xml(&svg_str)?; svg_xml.attributes.insert( "viewBox".to_string(), @@ -144,12 +186,14 @@ pub fn export( mut svg_str: String, output_dir: &PathBuf, output_name: String, - args: &super::Args, + args: &ExportArgs, ) -> Result<(), ExportError> { if !args.no_resize { svg_str = resize(svg_str)?; } + svg_str = strip_only_size(&svg_str)?; + svg_str = combine_defs(svg_str)?; mkdirp::mkdirp(output_dir.join("vector")).unwrap(); diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..6a4f2b2 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,3 @@ +pub mod parse; +pub mod template; +pub mod export; diff --git a/src/main.rs b/src/main.rs index b25f515..f063643 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,11 @@ use clap::Parser; use std::path::PathBuf; -pub mod parse; -use parse::*; - -pub mod template; -use template::*; - -pub mod export; -use export::*; +use blobfox_template::{ + parse::*, + template::*, + export::*, +}; fn main() { let args = Args::parse(); @@ -30,6 +27,8 @@ fn main() { } fn generate_variant(context: &RenderingContext, name: &str, output_dir: &PathBuf, args: &Args) { + let args: ExportArgs = args.clone().into(); + if let Some(path) = context.species().variant_paths.get(name) { match context.compile(path).and_then(|template| { template.render_data_to_string(&context.get_data(name)) @@ -39,7 +38,7 @@ fn generate_variant(context: &RenderingContext, name: &str, output_dir: &PathBuf svg, output_dir, format!("{}_{}", context.species().name, name), - args + &args ) { Ok(_) => {} Err(err) => { @@ -56,7 +55,7 @@ fn generate_variant(context: &RenderingContext, name: &str, output_dir: &PathBuf } } -#[derive(Parser, Debug)] +#[derive(Parser, Debug, Clone)] #[clap(author, version, about, long_about = None)] pub struct Args { /// A folder containing the declaration from which the emotes should be generated @@ -79,3 +78,12 @@ pub struct Args { #[clap(short, long, value_parser)] output_dir: Option, } + +impl From for ExportArgs { + fn from(args: Args) -> ExportArgs { + ExportArgs { + no_resize: args.no_resize, + dim: args.dim, + } + } +} diff --git a/src/template.rs b/src/template.rs index 293e10b..d71876f 100644 --- a/src/template.rs +++ b/src/template.rs @@ -1,9 +1,10 @@ -use super::*; +use crate::parse::SpeciesDecl; use mustache::{Context, Data, MapBuilder, PartialLoader, Template}; use std::collections::HashMap; use std::path::Path; use std::sync::{Arc, Mutex}; use xmltree::{Element, XMLNode}; +use css_color_parser::Color as CssColor; #[derive(Debug, Clone)] pub struct RenderingContext { @@ -90,39 +91,45 @@ impl RenderingContext { }); } - let this = self.clone(); - let variant_name_owned = variant_name.to_string(); - builder = builder.insert_fn("set-fill", move |input| { - // Parse `color|xml` - if let [color, xml] = input.splitn(2, '|').collect::>()[..] { - // Render `color` and `xml` - if let (Ok(color), Ok(xml)) = ( - this.render_to_string(&color, &variant_name_owned), - this.render_to_string(&xml, &variant_name_owned), - ) { - // Convert `xml` to XML - match Element::parse(xml.as_bytes()) { - Ok(mut xml) => { - set_fill(&color.trim(), &mut xml); - - // Render XML to string - if let Some(res) = xml_to_string(xml) { - res - } else { - String::from("") + for (cb, name) in [ + (set_fill as fn(&str, &mut Element), "set-fill"), + (set_stroke, "set-stroke") + ] { + let this = self.clone(); + let variant_name_owned = variant_name.to_string(); + + builder = builder.insert_fn(name, move |input| { + // Parse `color|xml` + if let [color, xml] = input.splitn(2, '|').collect::>()[..] { + // Render `color` and `xml` + if let (Ok(color), Ok(xml)) = ( + this.render_to_string(&color, &variant_name_owned), + this.render_to_string(&xml, &variant_name_owned), + ) { + // Convert `xml` to XML + match Element::parse(xml.as_bytes()) { + Ok(mut xml) => { + cb(&color.trim(), &mut xml); + + // Render XML to string + if let Some(res) = xml_to_string(xml) { + res + } else { + String::from("") + } + } + Err(err) => { + format!("", err) } } - Err(err) => { - format!("", err) - } + } else { + String::from("") } } else { - String::from("") + String::from("") } - } else { - String::from("") - } - }); + }); + } builder = builder.insert("vars", &self.species.vars).unwrap(); @@ -236,34 +243,54 @@ impl PartialLoader for RenderingContext { } } -fn set_fill(color: &str, xml: &mut Element) { - // Substitute the fill color; TODO: handle transparency for SVG 1.1 - if let Some(style) = xml.attributes.get_mut("style") { - let mut new_style = Vec::new(); +macro_rules! set_color { + ( $fn_name:tt, $color_name:expr, $opacity_name:expr ) => { + pub fn $fn_name(color: &str, xml: &mut Element) { + let (color, opacity) = if let Ok(parsed) = color.parse::() { + (format!("#{:02x}{:02x}{:02x}", parsed.r, parsed.g, parsed.b), parsed.a) + } else { + (color.to_string(), 1.0) + }; - for rule in style.split(';') { - if let [name, value] = rule.splitn(2, ':').collect::>()[..] { - if name.trim() != "fill" && name.trim() != "fill-opacity" { - new_style.push(format!("{}:{}", name, value)); - } - } - } + fn rec(color: &str, opacity: f32, xml: &mut Element) { + if let Some(style) = xml.attributes.get_mut("style") { + let mut new_style = Vec::new(); + + for rule in style.split(';') { + if let [name, value] = rule.splitn(2, ':').collect::>()[..] { + if name.trim() != $color_name && name.trim() != $opacity_name { + new_style.push(format!("{}:{}", name, value)); + } + } + } - new_style.push(format!("fill: {};", color)); + new_style.push(format!(concat!($color_name, ": {};"), color)); + new_style.push(format!(concat!($opacity_name, ": {};"), opacity)); - *style = new_style.join(";"); - } - if let Some(_fill) = xml.attributes.get("fill") { - xml.attributes.insert("fill".to_string(), color.to_string()); - } + *style = new_style.join(";"); + } + if let Some(_fill) = xml.attributes.get($color_name) { + xml.attributes.insert($color_name.to_string(), color.to_string()); + } + if let Some(_fill) = xml.attributes.get($opacity_name) { + xml.attributes.insert($opacity_name.to_string(), opacity.to_string()); + } + + for child in xml.children.iter_mut() { + if let XMLNode::Element(ref mut child) = child { + rec(color, opacity, child); + } + } + } - for child in xml.children.iter_mut() { - if let XMLNode::Element(ref mut child) = child { - set_fill(color, child); + rec(&color, opacity, xml) } } } +set_color!(set_fill, "fill", "fill-opacity"); +set_color!(set_stroke, "stroke", "stroke-opacity"); + pub fn query_selector(svg: Element, pattern: &str) -> Option { if pattern == "" { // NOTE: it looks like having a nested svg makes resvg unhappy diff --git a/vector/blobfox_snuggle_right.svg b/vector/blobfox_snuggle_right.svg new file mode 100644 index 0000000..f7a2382 --- /dev/null +++ b/vector/blobfox_snuggle_right.svg @@ -0,0 +1,125 @@ + +blobfoxBlobfox team (https://git.shadamethyst.xyz/adri326/blobfox), licensed under the Apache 2.0 LicenseblobfoxFeuerfuchshttps://git.shadamethyst.xyz/adri326/blobfoxShad Amethyst