feat: code borrowed from https://github.com/MCorange99/svg2colored-png/tree/main in order to convert from svg to png

This commit is contained in:
silver 2025-06-07 01:05:08 +01:00
parent 725bfa41cc
commit 0034bd34d6
Signed by: silver
GPG key ID: 36F93D61BAD3FD7D
5 changed files with 867 additions and 221 deletions

823
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -49,4 +49,13 @@ toml = "0.8.23"
serde = "1.0"
gdk-pixbuf = "0.20.10"
resvg = "=0.6.1"
clap = { version = "4.1.4", features = ["derive"] }
eyre = "0.6.8"
color-eyre = "0.6.2"
usvg-text-layout = "0.29.0"
usvg = "0.29.0"
resvg = "0.29.0"
tiny-skia = "0.8.3"
log = "0.4.20"
simple_logger = "4.2.0"

View file

@ -7,16 +7,19 @@ use serenity::{
use skynet_discord_bot::common::database::{db_init, DataBase};
use skynet_discord_bot::{get_config, Config};
use std::{fs, process, sync::Arc};
use std::ffi::OsStr;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use std::process::Command;
use chrono::{Datelike, Utc};
use gdk_pixbuf::PixbufLoader;
use gdk_pixbuf::{Pixbuf, PixbufFormat, PixbufLoader};
use gdk_pixbuf::prelude::PixbufLoaderExt;
use resvg::usvg;
use serde::Deserialize;
use serenity::all::GuildId;
use tokio::sync::RwLock;
use skynet_discord_bot::common::renderer::{Args, Renderer};
#[tokio::main]
async fn main() {
@ -163,7 +166,7 @@ fn clone_repo(config: &Config, config_toml: &ConfigToml){
.expect("failed to execute process");
}
fn convert_svg_to_png(original: &str, out: &str){
fn convert_svg_to_png(original: &PathBuf, out: &PathBuf){
let mut f = File::open(original).unwrap();
let mut buffer = Vec::new();
// read the whole file
@ -183,11 +186,41 @@ fn convert_svg_to_png(original: &str, out: &str){
fn get_logos(config: &Config, config_toml: &ConfigToml){
let folder = format!("{}/open-governance/{}", &config.home, &config_toml.source.directory);
let folder_path = PathBuf::from(&folder);
let paths = fs::read_dir(folder).unwrap();
let args = Args{
input: folder_path.clone(),
output: folder_path.clone(),
colors: String::from(""),
width: 1024,
height: 1024,
};
let mut r = Renderer::new(&args).unwrap();
for path in paths {
let tmp = path.unwrap();
let mut path_local = tmp.path().to_owned();
match tmp.path().extension() {
None => {}
Some(ext) => {
if ext == "svg" {
let mut path_new = path_local.clone();
path_new.set_extension("png");
// check if exists
match r.render(&path_local, &args) {
Ok(_) => log::info!("Successfully rendered all colors of {path_local:?}"),
Err(e) => {
log::error!("Failed to render {path_local:?}: {}", e)
}
}
path_local = path_new;
}
}
};
println!("Name: {}", &tmp.path().display());
}
}

View file

@ -2,3 +2,5 @@ pub mod database;
pub mod minecraft;
pub mod set_roles;
pub mod wolves;
pub mod renderer;

213
src/common/renderer.rs Normal file
View file

@ -0,0 +1,213 @@
use std::path::{Path, PathBuf};
use clap::builder::OsStr;
use clap::Parser;
use color_eyre::{Result, eyre::bail};
use usvg_text_layout::TreeTextToPath;
#[derive(Parser, Debug, Clone)]
#[command(name = "svg2colored-png")]
#[command(author = "MCorange <mcorangecodes@gmail.com>")]
#[command(version = env!("CARGO_PKG_VERSION"))]
#[command(about = "Converts svgs to multiple png's that differ in color", long_about = "Made by MCorange <mcorangecodes@gmail.com>")]
pub struct Args {
/// Input folder with the SVG's
#[arg(long, short)]
pub input: PathBuf,
/// Output folder where the PNG's will be placed
#[arg[long, short]]
pub output: PathBuf,
/// Comma seperated colors that will be used in HEX Eg. 000000,ffffff
/// Can be like an object: black:000000,white:ffffff
#[arg[long, short, default_value_t = String::from("0d6efd,6c757d,198754,0dcaf0,ffc107,dc3545,f8f9fa,212529,ffffff,000000")]]
pub colors: String,
/// Width of the generated PNG's
#[arg(long, default_value_t = 1024)]
pub width: u32,
/// Height of the generated PNG's
#[arg(long, default_value_t = 1024)]
pub height: u32
}
#[derive(Debug, Clone)]
enum ColorType {
Array(Vec<String>),
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<Self> {
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::<Vec<&str>>();
if s.len() < 2 {
log::error!("Invalid color object, try checking help");
return None;
}
Some((s[0].to_string(), s[1].to_string()))
}).collect::<Vec<Option<(String, String)>>>();
let mut colors = Vec::new();
for c in obj {
if let Some(c) = c {
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::<Vec<String>>();
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 => {
log::warn!("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() {
log::warn!("File {fo:?} exists, skipping");
return Ok(());
}
let svg = self.set_color(&self.get_svg_data(fi)?, &color);
let mut opt = usvg::Options::default();
// Get file's absolute directory.
opt.resources_dir = std::fs::canonicalize(fi.clone())
.ok()
.and_then(|p| p.parent().map(|p| p.to_path_buf()));
let mut tree = match usvg::Tree::from_data(svg.as_bytes(), &opt) {
Ok(v) => v,
Err(_) => {
log::error!("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: &String, args: &Args) -> PathBuf {
let mut fo: std::path::PathBuf = args.output.clone();
// fo.push(sub_folder);
fo.push(fi.file_name().unwrap_or(&OsStr::from("default")).to_str().unwrap_or("default").replace(".svg", ""));
fo.set_extension("png");
fo
}
fn set_color(&self, svg: &String, color: &String) -> String {
svg.replace("fill=\"currentColor\"", &format!("fill=\"#{}\"", color))
}
fn get_svg_data(&self, fi: &Path) -> Result<String>{
match std::fs::read_to_string(fi) {
Ok(d) => Ok(d),
Err(_) => {
log::error!("File {fi:?} does not exist");
bail!("File {fi:?} does not exist");
}
}
}
}