Skip to content

Commit

Permalink
added --color-object flag to specify colors and theire names
Browse files Browse the repository at this point in the history
  • Loading branch information
MCorange99 committed Feb 17, 2023
1 parent 4d48cc1 commit 46ed49c
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 56 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
target/
bootstrap-icons-1.10.3/
out/
out/
test-icons/
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "svg2colored-png"
version = "1.0.4"
version = "1.1.0"
edition = "2021"
description = "An SVG to PNG converter"
authors = ["MCorange <[email protected]>"]
Expand Down
8 changes: 6 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,13 @@ pub struct Args {
#[arg[long, short]]
output_folder: PathBuf,

/// Comma seperated colors that will be used in HEX
/// Comma seperated colors that will be used in HEX Eg. 000000,ffffff
#[arg[long, short, default_value_t = String::from("0d6efd,6c757d,198754,0dcaf0,ffc107,dc3545,f8f9fa,212529,ffffff,000000")]]
colors: String,

/// Comma seperated list of key value pairs of name:color Eg. black:000000,white:ffffff. This has priority over normal color list
#[arg[long, default_value_t = String::new()]]
colors_object: String,

/// Width of the generated PNG's
#[arg(long, default_value_t = 1024)]
Expand Down Expand Up @@ -70,7 +74,7 @@ fn main() -> Result<()> {
if path.is_dir() {
util::logger::info(&format!("Skipping folder '{}' since folder walking is not yet implemented", path.clone().display()));
} else {
match r.render(path.clone(), args.output_folder.clone()){
match r.render(path.clone(), args.clone()){
Ok(_) => util::logger::info(&format!("Successfully rendered all colors of '{}'", path.clone().display())),
Err(_) => util::logger::error(&format!("Failed to render '{}'", path.clone().display()))
};
Expand Down
156 changes: 105 additions & 51 deletions src/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,17 @@
*/
use std::path::PathBuf;
use usvg_text_layout::{fontdb::{self, Database}, TreeTextToPath};
use usvg::{self, Size};
use usvg;
use resvg;
use tiny_skia;
use color_eyre::Result;

use crate::util::{logger, self};
use crate::{util::{logger, self}, Args};

pub struct Renderer {
fontdb: Database,
colors: Vec<String>,
colors_obj: Vec<Vec<String>>,
size: (u32, u32)
}

Expand All @@ -35,82 +36,135 @@ impl Renderer {
let mut db = fontdb::Database::new();
db.load_system_fonts();


let colors = args.colors.split(",").map(|s| {
s.to_string()
})
.collect::<Vec<String>>();

let colors_obj = if args.colors_object.is_empty() {
Self::init_folders(colors.clone(), args.clone())?;
Vec::new()
} else {
let colors_obj = args.colors_object.split(",").map(|s| {
s.to_string()
})
.collect::<Vec<String>>();

for color in colors.clone() {
std::fs::create_dir_all(args.output_folder.join(color))?;
}
let colors_obj = colors_obj.iter().map(|p| {
let s: Vec<&str> = p.split(":").collect();
vec![ s[0].to_string(), s[1].to_string() ]
}).collect::<Vec<Vec<String>>>();

let folders = colors_obj.iter().map(|v| {
v[0].clone()
}).collect::<Vec<String>>();
Self::init_folders(folders, args.clone())?;
colors_obj
};

Ok(Self {
fontdb: db,
colors: colors,
colors_obj,
colors,
size: (args.width, args.height)
})
}

pub fn render(&self, path_in: PathBuf, out_dir: PathBuf) -> Result<(), ()> {
let ext = path_in.clone();
pub fn render(&self, fi: PathBuf, args: crate::Args) -> Result<(), ()> {
let ext = fi.clone();
let ext = match ext.extension() {
Some(e) => e,
None => return Err(util::logger::warning(format!("File '{}' is not of SVG type", path_in.clone().to_str().unwrap()))),
None => return Err(util::logger::warning(format!("File '{}' is not of SVG type", fi.clone().to_str().unwrap()))),
};

if ext.to_str().unwrap() != "svg" {
return Err(util::logger::warning(format!("File '{}' is not of SVG type", path_in.clone().to_str().unwrap())));
return Err(util::logger::warning(format!("File '{}' is not of SVG type", fi.clone().to_str().unwrap())));
}

let svg_data = match std::fs::read_to_string(path_in.clone()) {
Ok(d) => d,
Err(_) => return Err(logger::error(&format!("File {} does not exist", path_in.clone().display())))
};
if self.colors_obj.is_empty() {
for color in self.colors.clone() {
let fo = self.get_out_file(fi.clone(), color.clone(), args.clone());
self.render_one(fi.clone(), fo, color.clone())?;

}
} else {
for o in self.colors_obj.clone() {
let color = &o[1];
let name = &o[0];
let fo = self.get_out_file(fi.clone(), name.clone(), args.clone());
self.render_one(fi.clone(), fo, color.clone())?;

}
}

Ok(())
}


fn render_one(&self, fi: PathBuf, fo: PathBuf, color: String) -> Result<(), ()>{

if fo.exists() {
util::logger::warning(format!("File '{}' exists, skipping", fo.to_str().unwrap()));
return Ok(());
}

let svg = self.get_svg_data(fi.clone())?;
let svg = self.set_color(svg, color);

let mut opt = usvg::Options::default();
// Get file's absolute directory.
opt.resources_dir = std::fs::canonicalize(path_in.clone())
opt.resources_dir = std::fs::canonicalize(fi.clone())
.ok()
.and_then(|p| p.parent().map(|p| p.to_path_buf()));
opt.default_size = Size::new(1000.0, 1000.0).unwrap();
opt.dpi = 200.0;

for color in self.colors.clone() {
let f_n = path_in.clone().file_name().unwrap().to_string_lossy().replace(".svg", ".png");
let p = out_dir
.join(color.clone())
.join(
f_n
);
if p.exists() {
util::logger::warning(format!("File '{}' exists, skipping", p.to_str().unwrap()));
continue;
}

let mut tree = match usvg::Tree::from_data(svg.as_bytes(), &opt) {
Ok(v) => Ok(v),
Err(_) => Err(util::logger::error(format!("Failed to parse '{}'", fi.clone().display())))
}?;

tree.convert_text(&self.fontdb);

let mut pixmap = tiny_skia::Pixmap::new(self.size.0, self.size.1).unwrap();

util::logger::info(format!("Rendering '{}'", fo.display()));

let color = color.replace("#", "");
let svg_data_bytes = svg_data.replace("fill=\"currentColor\"", &format!("fill=\"#{}\"", color));
let svg_data_bytes = svg_data_bytes.as_bytes();
//fill="currentColor"
let mut tree = match usvg::Tree::from_data(&svg_data_bytes, &opt) {
Ok(v) => Ok(v),
Err(_) => Err(util::logger::error(format!("Failed to parse '{}'", path_in.clone().display())))
}?;
tree.convert_text(&self.fontdb);


let mut pixmap = tiny_skia::Pixmap::new(self.size.0, self.size.1).unwrap();
// let mut pixmap = tiny_skia::Pixmap::new(pixmap_size.width(), pixmap_size.height()).unwrap();
resvg::render(
&tree,
usvg::FitTo::Size(self.size.0, self.size.1),
tiny_skia::Transform::default(),
pixmap.as_mut(),
)
.unwrap();
util::logger::info(format!("Rendering '{}'", p.display()));
pixmap.save_png(p).unwrap();
//? 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).unwrap();

Ok(())
}

fn get_svg_data(&self, fi: PathBuf) -> Result<String, ()>{
match std::fs::read_to_string(fi.clone()) {
Ok(d) => Ok(d),
Err(_) => return Err(logger::error(&format!("File {} does not exist", fi.clone().display())))
}
}

fn set_color(&self, svg: String, color: String) -> String {
svg.replace("fill=\"currentColor\"", &format!("fill=\"#{}\"", color))
}

fn get_out_file(&self, fi: PathBuf, sub_folder: String, args: crate::Args) -> PathBuf {
let mut fo = args.output_folder.clone();
fo.push(sub_folder);
fo.push(fi.clone().file_name().unwrap());
fo.set_extension("png");
fo
}

fn init_folders(folders: Vec<String>, args: Args) -> Result<()>{
for folder in folders.clone() {
std::fs::create_dir_all(args.output_folder.join(folder))?;
}
Ok(())
}
}
Expand Down

0 comments on commit 46ed49c

Please sign in to comment.