~fgaz/blobfox

4a09d58d7ea072b426fe64b691ef2fa83fa13ae5 — Adrien Burgun 1 year, 6 months ago c763116
:sparkles: New output directory format and glob for snuggle emotes
5 files changed, 62 insertions(+), 43 deletions(-)

M .gitignore
M Cargo.toml
M src/bin/snuggle.rs
M src/export.rs
M src/main.rs
M .gitignore => .gitignore +1 -0
@@ 3,3 3,4 @@ output/
Cargo.lock
target/
blobfox-*.zip
species/custom/

M Cargo.toml => Cargo.toml +1 -0
@@ 21,3 21,4 @@ usvg = "0.23"
tiny-skia = "0.6"
png = "0.17"
css-color-parser = "0.1.2"
wax = "0.5.0"

M src/bin/snuggle.rs => src/bin/snuggle.rs +52 -37
@@ 5,6 5,7 @@ use std::path::PathBuf;
use std::collections::HashMap;
use serde::{Serialize, Deserialize};
use xmltree::{Element, XMLNode};
use wax::{Glob, Pattern};

use blobfox_template::{
    parse,


@@ 37,6 38,43 @@ struct Desc {
    right: HashMap<String, String>,
}

#[derive(Parser, Clone)]
#[clap(author, version, about, long_about = None)]
struct Args {
    /// Path to the description
    #[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,

    /// Dimension to export the images as; can be specified multiple times
    #[clap(long, value_parser)]
    dim: Vec<u32>,

    /// Input directory, containing the svgs to combine
    #[clap(short, long, value_parser)]
    input_dir: Option<PathBuf>,

    /// Output directory
    #[clap(short, long, value_parser)]
    output_dir: Option<PathBuf>,

    /// A glob to filter which emotes to output; supports wildcards, like `blobfox_snuggle*`
    #[clap(value_parser)]
    glob: Option<String>,
}

impl From<Args> for export::ExportArgs {
    fn from(args: Args) -> export::ExportArgs {
        export::ExportArgs {
            no_resize: args.no_resize,
            dim: args.dim,
        }
    }
}

fn main() {
    let args = Args::parse();
    let input_dir = args.input_dir.clone().unwrap_or(PathBuf::from("output/vector/"));


@@ 45,6 83,8 @@ fn main() {
    let files = std::fs::read_dir(&input_dir).unwrap_or_else(|err| {
        panic!("Couldn't read directory {}: {}", input_dir.display(), err);
    }).filter_map(|entry| {
        std::fs::read_dir(entry.ok()?.path()).ok()
    }).flatten().filter_map(|entry| {
        let entry = entry.ok()?;
        Some((entry.path().file_stem()?.to_str()?.to_string(), entry.path()))
    }).collect::<HashMap<_, _>>();


@@ 56,6 96,8 @@ fn main() {

    let export_args: export::ExportArgs = args.clone().into();

    let glob = args.glob.as_ref().map(|s| Glob::new(s).expect("Invalid parameter glob"));

    for (left_name, left_path) in desc.left.iter() {
        if let Some(left_path) = files.get(left_path) {
            let left = std::fs::read_to_string(left_path).unwrap_or_else(|err| {


@@ 64,6 106,13 @@ fn main() {

            for (right_name, right_path) in desc.right.iter() {
                if let Some(right_path) = files.get(right_path) {
                    let name = format!("{}_{}_{}", left_name, desc.name, right_name);
                    if let Some(ref glob) = &glob {
                        if !glob.is_match(&*name) {
                            continue // Skip this emote
                        }
                    }

                    let right = std::fs::read_to_string(&right_path).unwrap_or_else(|err| {
                        panic!("Couldn't open {}: {}", right_path.display(), err);
                    });


@@ 71,12 120,11 @@ fn main() {
                    let snuggle = generate_snuggle(&left, &right, &desc);
                    let snuggle = export::xml_to_str(&snuggle).unwrap();

                    let name = format!("{}_{}_{}", left_name, desc.name, right_name);

                    export::export(
                        snuggle,
                        &output_dir,
                        name,
                        &desc.name,
                        &name,
                        &export_args
                    ).unwrap();
                }


@@ 156,7 204,7 @@ fn bolden(amount: f64, xml: &mut Element) {
            *stroke_width = format!("{}", parsed + amount);
        }
    } else if xml.attributes.contains_key("fill") {
        xml.attributes.insert("stroke-width", amount.to_string());
        xml.attributes.insert("stroke-width".to_string(), amount.to_string());
    }

    if let Some(style) = xml.attributes.get_mut("style") {


@@ 188,36 236,3 @@ fn bolden(amount: f64, xml: &mut Element) {
        }
    }
}

#[derive(Parser, Clone)]
#[clap(author, version, about, long_about = None)]
struct Args {
    /// Path to the description
    #[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,

    /// Dimension to export the images as; can be specified multiple times
    #[clap(long, value_parser)]
    dim: Vec<u32>,

    /// Input directory, containing the svgs to combine
    #[clap(short, long, value_parser)]
    input_dir: Option<PathBuf>,

    /// Output directory
    #[clap(short, long, value_parser)]
    output_dir: Option<PathBuf>,
}

impl From<Args> for export::ExportArgs {
    fn from(args: Args) -> export::ExportArgs {
        export::ExportArgs {
            no_resize: args.no_resize,
            dim: args.dim,
        }
    }
}

M src/export.rs => src/export.rs +6 -5
@@ 185,7 185,8 @@ pub fn combine_defs(svg_str: String) -> Result<String, ExportError> {
pub fn export(
    mut svg_str: String,
    output_dir: &PathBuf,
    output_name: String,
    species_name: &str,
    output_name: &str,
    args: &ExportArgs,
) -> Result<(), ExportError> {
    if !args.no_resize {


@@ 196,15 197,15 @@ pub fn export(

    svg_str = combine_defs(svg_str)?;

    mkdirp::mkdirp(output_dir.join("vector")).unwrap();
    mkdirp::mkdirp(output_dir.join(format!("vector/{}", species_name))).unwrap();

    let output = output_dir.join(&format!("vector/{}.svg", output_name));
    let output = output_dir.join(&format!("vector/{}/{}.svg", species_name, output_name));
    std::fs::write(output.clone(), svg_str.clone()).map_err(|err| ExportError::Io(output, err))?;

    let svg_usvg = get_usvg(&svg_str)?;
    for resolution in args.dim.iter().copied().filter(|r| *r != 0).collect::<HashSet<_>>() {
        mkdirp::mkdirp(output_dir.join(&format!("{}", resolution))).unwrap();
        let output = output_dir.join(&format!("{}/{}.png", resolution, output_name));
        mkdirp::mkdirp(output_dir.join(&format!("{}/{}", resolution, species_name))).unwrap();
        let output = output_dir.join(&format!("{}/{}/{}.png", resolution, species_name, output_name));

        let mut image = tiny_skia::Pixmap::new(resolution, resolution).unwrap();


M src/main.rs => src/main.rs +2 -1
@@ 37,7 37,8 @@ fn generate_variant(context: &RenderingContext, name: &str, output_dir: &PathBuf
                match export(
                    svg,
                    output_dir,
                    format!("{}_{}", context.species().name, name),
                    &context.species().name,
                    &format!("{}_{}", context.species().name, name),
                    &args
                ) {
                    Ok(_) => {}