diff --git a/.changes/custom-icon.md b/.changes/custom-icon.md new file mode 100644 index 000000000..b41a58565 --- /dev/null +++ b/.changes/custom-icon.md @@ -0,0 +1,6 @@ +--- +"cli.rs": minor +"cli.js": minor +--- + +Add `--png` option for the `icon` command to generate custom icon sizes. diff --git a/tooling/cli/src/icon.rs b/tooling/cli/src/icon.rs index 25a6262bb..6ea1cfd16 100644 --- a/tooling/cli/src/icon.rs +++ b/tooling/cli/src/icon.rs @@ -30,6 +30,20 @@ struct IcnsEntry { ostype: String, } +struct PngTarget { + size: u32, + file_name: String, +} + +impl PngTarget { + fn new(size: u32, file_name: impl Into) -> Self { + Self { + size, + file_name: file_name.into(), + } + } +} + #[derive(Debug, Parser)] #[clap(about = "Generates various icons for all major platforms")] pub struct Options { @@ -41,15 +55,18 @@ pub struct Options { /// Default: 'icons' directory next to the tauri.conf.json file. #[clap(short, long)] output: Option, + + /// Custom PNG icon sizes to generate. When set, the default icons are not generated. + #[clap(short, long, use_value_delimiter = true)] + png: Option>, } pub fn command(options: Options) -> Result<()> { let input = options.input; let out_dir = options.output.unwrap_or_else(|| tauri_dir().join("icons")); + let png_icon_sizes = options.png.unwrap_or_default(); create_dir_all(&out_dir).context("Can't create output directory")?; - // Try to read the image as a DynamicImage, convert it to rgba8 and turn it into a DynamicImage again. - // Both things should be catched by the explicit conversions to rgba8 anyway. let source = open(input) .context("Can't read and decode source image")? .into_rgba8(); @@ -60,13 +77,32 @@ pub fn command(options: Options) -> Result<()> { panic!("Source image must be square"); } - appx(&source, &out_dir).context("Failed to generate appx icons")?; + if png_icon_sizes.is_empty() { + appx(&source, &out_dir).context("Failed to generate appx icons")?; + icns(&source, &out_dir).context("Failed to generate .icns file")?; + ico(&source, &out_dir).context("Failed to generate .ico file")?; - icns(&source, &out_dir).context("Failed to generate .icns file")?; - - ico(&source, &out_dir).context("Failed to generate .ico file")?; - - png(&source, &out_dir).context("Failed to generate png icons")?; + let mut png_targets = vec![ + PngTarget::new(256, "128x128@2x.png"), + PngTarget::new(512, "icon.png"), + ]; + png_targets.extend( + [32, 128] + .into_iter() + .map(|size| PngTarget::new(size, format!("{}x{}.png", size, size))) + .collect::>(), + ); + png(&source, &out_dir, png_targets).context("Failed to generate png icons")?; + } else { + png( + &source, + &out_dir, + png_icon_sizes + .into_iter() + .map(|size| PngTarget::new(size, format!("{}x{}.png", size, size))) + .collect(), + )?; + } Ok(()) } @@ -154,16 +190,10 @@ fn ico(source: &DynamicImage, out_dir: &Path) -> Result<()> { // Generate .png files in 32x32, 128x128, 256x256, 512x512 (icon.png) // Main target: Linux -fn png(source: &DynamicImage, out_dir: &Path) -> Result<()> { - for size in [32, 128, 256, 512] { - let file_name = match size { - 256 => "128x128@2x.png".to_string(), - 512 => "icon.png".to_string(), - _ => format!("{size}x{size}.png"), - }; - - log::info!(action = "PNG"; "Creating {}", file_name); - resize_and_save_png(source, size, &out_dir.join(&file_name))?; +fn png(source: &DynamicImage, out_dir: &Path, targets: Vec) -> Result<()> { + for target in targets { + log::info!(action = "PNG"; "Creating {}", target.file_name); + resize_and_save_png(source, target.size, &out_dir.join(&target.file_name))?; } Ok(())