// this code is taken from https://github.com/MCorange99/svg2colored-png/tree/main // I was unable to figure out how to use usvg myself so younked it from here. use std::{ ffi::OsStr, path::{Path, PathBuf}, }; // use clap::builder::OsStr; use color_eyre::{eyre::bail, Result}; use usvg_text_layout::TreeTextToPath; #[derive(Debug, Clone)] pub struct Args { pub input: PathBuf, /// Output folder where the PNG's will be placed pub output: PathBuf, /// Comma seperated colors that will be used in HEX Eg. 000000,ffffff /// Can be like an object: black:000000,white:ffffff pub colors: String, /// Width of the generated PNG's pub width: u32, /// Height of the generated PNG's pub height: u32, } #[derive(Debug, Clone)] enum ColorType { Array(Vec), Object(Vec<(String, String)>), None, } #[derive(Debug, Clone)] pub struct Renderer { fontdb: usvg_text_layout::fontdb::Database, colors: ColorType, size: (u32, u32), pub count: u64, } impl Renderer { pub fn new(args: &Args) -> Result { let mut db = usvg_text_layout::fontdb::Database::new(); db.load_system_fonts(); let mut this = Self { fontdb: db, colors: ColorType::None, size: (args.width, args.height), count: 0, }; let colors = if args.colors.contains(':') { //? object let obj = args .colors .split(',') .map(|s| { let s = s.split(':').collect::>(); if s.len() < 2 { dbg!("Invalid color object, try checking help"); return None; } Some((s[0].to_string(), s[1].to_string())) }) .collect::>>(); let mut colors = Vec::new(); for c in obj.into_iter().flatten() { std::fs::create_dir_all(args.output.join(&c.0))?; colors.push(c); } ColorType::Object(colors) } else { //? list // let colors = args.colors.split(",").map(|s| { // s.to_string() // }) // .collect::>(); let mut colors = Vec::new(); for color in args.colors.split(',') { std::fs::create_dir_all(args.output.join(color))?; colors.push(color.to_string()) } ColorType::Array(colors) }; this.colors = colors; Ok(this) } pub fn render(&mut self, fi: &Path, args: &Args) -> Result<()> { match fi.extension() { Some(e) if e.to_str() == Some("svg") => {} Some(_) | None => { dbg!("Filer {:?} is not of type SVG", fi); // util::logger::warning(format!("File '{}' is not of SVG type", fi.clone().to_str().unwrap())); bail!("Failed to render"); } }; match self.colors.clone() { ColorType::Array(c) => { for color in c { // log::info!("Rendering the color {color:?}"); let fo = self.get_out_file(fi, &color, args); self.render_one(fi, &fo, &color)?; } } ColorType::Object(c) => { for o in c { // log::info!("Rendering the color {:?}", o); let fo = self.get_out_file(fi, &o.0, args); self.render_one(fi, &fo, &o.1)?; } } ColorType::None => unreachable!(), } Ok(()) } fn render_one(&mut self, fi: &Path, fo: &Path, color: &String) -> Result<()> { if fo.exists() { dbg!("File {fo:?} exists, skipping"); return Ok(()); } let svg = self.set_color(&self.get_svg_data(fi)?, color); let opt = usvg::Options { // Get file's absolute directory. resources_dir: std::fs::canonicalize(fi).ok().and_then(|p| p.parent().map(|p| p.to_path_buf())), ..Default::default() }; let mut tree = match usvg::Tree::from_data(svg.as_bytes(), &opt) { Ok(v) => v, Err(_) => { dbg!("Failed to parse {fi:?}"); bail!(""); } }; tree.convert_text(&self.fontdb); let mut pixmap = tiny_skia::Pixmap::new(self.size.0, self.size.1).unwrap(); // log::info!("Rendering {fo:?}"); //? maybe handle this and possibly throw error if its none let _ = resvg::render(&tree, usvg::FitTo::Size(self.size.0, self.size.1), tiny_skia::Transform::default(), pixmap.as_mut()); pixmap.save_png(fo)?; self.count += 1; Ok(()) } #[inline] fn get_out_file(&mut self, fi: &Path, _sub_folder: &str, args: &Args) -> PathBuf { let mut fo: std::path::PathBuf = args.output.clone(); // fo.push(sub_folder); fo.push(fi.file_name().unwrap_or(OsStr::new("default")).to_str().unwrap_or("default").replace(".svg", "")); fo.set_extension("png"); fo } fn set_color(&self, svg: &str, color: &String) -> String { svg.replace("fill=\"currentColor\"", &format!("fill=\"#{color}\"")) } fn get_svg_data(&self, fi: &Path) -> Result { match std::fs::read_to_string(fi) { Ok(d) => Ok(d), Err(_) => { dbg!("File {fi:?} does not exist"); bail!("File {fi:?} does not exist"); } } } }