mirror of
https://github.com/tauri-apps/tauri.git
synced 2024-12-01 03:02:28 +03:00
fix(bundler): dmg volume icon (#1730)
This commit is contained in:
parent
f1aa12075f
commit
e37e187d4a
5
.changes/fix-dmg-volume-icon.md
Normal file
5
.changes/fix-dmg-volume-icon.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"tauri-bundler": patch
|
||||
---
|
||||
|
||||
Fixes DMG volume icon.
|
@ -23,18 +23,16 @@
|
||||
|
||||
use super::{
|
||||
super::common,
|
||||
icon::create_icns_file,
|
||||
sign::{notarize, notarize_auth_args, setup_keychain_if_needed, sign},
|
||||
};
|
||||
use crate::Settings;
|
||||
|
||||
use anyhow::Context;
|
||||
use image::{self, GenericImageView};
|
||||
|
||||
use std::{
|
||||
cmp::min,
|
||||
ffi::OsStr,
|
||||
fs::{self, File},
|
||||
io::{self, prelude::*, BufWriter},
|
||||
fs,
|
||||
io::prelude::*,
|
||||
path::{Path, PathBuf},
|
||||
process::{Command, Stdio},
|
||||
};
|
||||
@ -362,102 +360,3 @@ fn copy_frameworks_to_bundle(bundle_directory: &Path, settings: &Settings) -> cr
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Given a list of icon files, try to produce an ICNS file in the resources
|
||||
// directory and return the path to it. Returns `Ok(None)` if no usable icons
|
||||
// were provided.
|
||||
fn create_icns_file(resources_dir: &Path, settings: &Settings) -> crate::Result<Option<PathBuf>> {
|
||||
if settings.icon_files().count() == 0 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// If one of the icon files is already an ICNS file, just use that.
|
||||
for icon_path in settings.icon_files() {
|
||||
let icon_path = icon_path?;
|
||||
if icon_path.extension() == Some(OsStr::new("icns")) {
|
||||
let mut dest_path = resources_dir.to_path_buf();
|
||||
dest_path.push(icon_path.file_name().expect("Could not get icon filename"));
|
||||
common::copy_file(&icon_path, &dest_path)?;
|
||||
return Ok(Some(dest_path));
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, read available images and pack them into a new ICNS file.
|
||||
let mut family = icns::IconFamily::new();
|
||||
|
||||
fn add_icon_to_family(
|
||||
icon: image::DynamicImage,
|
||||
density: u32,
|
||||
family: &mut icns::IconFamily,
|
||||
) -> io::Result<()> {
|
||||
// Try to add this image to the icon family. Ignore images whose sizes
|
||||
// don't map to any ICNS icon type; print warnings and skip images that
|
||||
// fail to encode.
|
||||
match icns::IconType::from_pixel_size_and_density(icon.width(), icon.height(), density) {
|
||||
Some(icon_type) => {
|
||||
if !family.has_icon_with_type(icon_type) {
|
||||
let icon = make_icns_image(icon)?;
|
||||
family.add_icon_with_type(&icon, icon_type)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
None => Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"No matching IconType",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
let mut images_to_resize: Vec<(image::DynamicImage, u32, u32)> = vec![];
|
||||
for icon_path in settings.icon_files() {
|
||||
let icon_path = icon_path?;
|
||||
let icon = image::open(&icon_path)?;
|
||||
let density = if common::is_retina(&icon_path) { 2 } else { 1 };
|
||||
let (w, h) = icon.dimensions();
|
||||
let orig_size = min(w, h);
|
||||
let next_size_down = 2f32.powf((orig_size as f32).log2().floor()) as u32;
|
||||
if orig_size > next_size_down {
|
||||
images_to_resize.push((icon, next_size_down, density));
|
||||
} else {
|
||||
add_icon_to_family(icon, density, &mut family)?;
|
||||
}
|
||||
}
|
||||
|
||||
for (icon, next_size_down, density) in images_to_resize {
|
||||
let icon = icon.resize_exact(
|
||||
next_size_down,
|
||||
next_size_down,
|
||||
image::imageops::FilterType::Lanczos3,
|
||||
);
|
||||
add_icon_to_family(icon, density, &mut family)?;
|
||||
}
|
||||
|
||||
if !family.is_empty() {
|
||||
fs::create_dir_all(resources_dir)?;
|
||||
let mut dest_path = resources_dir.to_path_buf();
|
||||
dest_path.push(settings.product_name());
|
||||
dest_path.set_extension("icns");
|
||||
let icns_file = BufWriter::new(File::create(&dest_path)?);
|
||||
family.write(icns_file)?;
|
||||
Ok(Some(dest_path))
|
||||
} else {
|
||||
Err(crate::Error::GenericError(
|
||||
"No usable Icon files found".to_owned(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// Converts an image::DynamicImage into an icns::Image.
|
||||
fn make_icns_image(img: image::DynamicImage) -> io::Result<icns::Image> {
|
||||
let pixel_format = match img.color() {
|
||||
image::ColorType::Rgba8 => icns::PixelFormat::RGBA,
|
||||
image::ColorType::Rgb8 => icns::PixelFormat::RGB,
|
||||
image::ColorType::La8 => icns::PixelFormat::GrayAlpha,
|
||||
image::ColorType::L8 => icns::PixelFormat::Gray,
|
||||
_ => {
|
||||
let msg = format!("unsupported ColorType: {:?}", img.color());
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidData, msg));
|
||||
}
|
||||
};
|
||||
icns::Image::from_data(pixel_format, img.width(), img.height(), img.to_bytes())
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use super::{super::common, app};
|
||||
use super::{super::common, app, icon::create_icns_file};
|
||||
use crate::{bundle::Bundle, PackageType::MacOsBundle, Settings};
|
||||
|
||||
use anyhow::Context;
|
||||
@ -41,7 +41,8 @@ pub fn bundle_project(settings: &Settings, bundles: &[Bundle]) -> crate::Result<
|
||||
let dmg_name = format!("{}.dmg", &package_base_name);
|
||||
let dmg_path = output_path.join(&dmg_name);
|
||||
|
||||
let product_name = &format!("{}.app", settings.main_binary_name());
|
||||
let product_name = settings.main_binary_name();
|
||||
let bundle_file_name = format!("{}.app", product_name);
|
||||
let bundle_dir = settings.project_out_directory().join("bundle/macos");
|
||||
|
||||
let support_directory_path = output_path.join("support");
|
||||
@ -79,7 +80,7 @@ pub fn bundle_project(settings: &Settings, bundles: &[Bundle]) -> crate::Result<
|
||||
Command::new("chmod")
|
||||
.arg("777")
|
||||
.arg(&bundle_script_path)
|
||||
.current_dir(output_path)
|
||||
.current_dir(&output_path)
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.output()
|
||||
@ -87,12 +88,7 @@ pub fn bundle_project(settings: &Settings, bundles: &[Bundle]) -> crate::Result<
|
||||
|
||||
let mut args = vec![
|
||||
"--volname",
|
||||
&package_base_name,
|
||||
// todo: volume icon
|
||||
// make sure this is a valid path?
|
||||
|
||||
//"--volicon",
|
||||
//"../../../../icons/icon.icns",
|
||||
&product_name,
|
||||
"--icon",
|
||||
&product_name,
|
||||
"180",
|
||||
@ -104,9 +100,16 @@ pub fn bundle_project(settings: &Settings, bundles: &[Bundle]) -> crate::Result<
|
||||
"660",
|
||||
"400",
|
||||
"--hide-extension",
|
||||
&product_name,
|
||||
&bundle_file_name,
|
||||
];
|
||||
|
||||
let icns_icon_path =
|
||||
create_icns_file(&output_path, &settings)?.map(|path| path.to_string_lossy().to_string());
|
||||
if let Some(icon) = &icns_icon_path {
|
||||
args.push("--volicon");
|
||||
args.push(icon);
|
||||
}
|
||||
|
||||
#[allow(unused_assignments)]
|
||||
let mut license_path_ref = "".to_string();
|
||||
if let Some(license_path) = &settings.macos().license {
|
||||
@ -131,7 +134,7 @@ pub fn bundle_project(settings: &Settings, bundles: &[Bundle]) -> crate::Result<
|
||||
cmd
|
||||
.current_dir(bundle_dir.clone())
|
||||
.args(args)
|
||||
.args(vec![dmg_name.as_str(), product_name.as_str()]);
|
||||
.args(vec![dmg_name.as_str(), bundle_file_name.as_str()]);
|
||||
|
||||
common::print_info("running bundle_dmg.sh")?;
|
||||
common::execute_with_verbosity(&mut cmd, &settings).map_err(|_| {
|
||||
|
113
tooling/bundler/src/bundle/macos/icon.rs
Normal file
113
tooling/bundler/src/bundle/macos/icon.rs
Normal file
@ -0,0 +1,113 @@
|
||||
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use crate::bundle::{common, Settings};
|
||||
use std::{
|
||||
cmp::min,
|
||||
ffi::OsStr,
|
||||
fs::{self, File},
|
||||
io::{self, BufWriter},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use image::GenericImageView;
|
||||
|
||||
// Given a list of icon files, try to produce an ICNS file in the out_dir
|
||||
// nd return the path to it. Returns `Ok(None)` if no usable icons
|
||||
// were provided.
|
||||
pub fn create_icns_file(out_dir: &Path, settings: &Settings) -> crate::Result<Option<PathBuf>> {
|
||||
if settings.icon_files().count() == 0 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// If one of the icon files is already an ICNS file, just use that.
|
||||
for icon_path in settings.icon_files() {
|
||||
let icon_path = icon_path?;
|
||||
if icon_path.extension() == Some(OsStr::new("icns")) {
|
||||
let mut dest_path = out_dir.to_path_buf();
|
||||
dest_path.push(icon_path.file_name().expect("Could not get icon filename"));
|
||||
common::copy_file(&icon_path, &dest_path)?;
|
||||
return Ok(Some(dest_path));
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, read available images and pack them into a new ICNS file.
|
||||
let mut family = icns::IconFamily::new();
|
||||
|
||||
fn add_icon_to_family(
|
||||
icon: image::DynamicImage,
|
||||
density: u32,
|
||||
family: &mut icns::IconFamily,
|
||||
) -> io::Result<()> {
|
||||
// Try to add this image to the icon family. Ignore images whose sizes
|
||||
// don't map to any ICNS icon type; print warnings and skip images that
|
||||
// fail to encode.
|
||||
match icns::IconType::from_pixel_size_and_density(icon.width(), icon.height(), density) {
|
||||
Some(icon_type) => {
|
||||
if !family.has_icon_with_type(icon_type) {
|
||||
let icon = make_icns_image(icon)?;
|
||||
family.add_icon_with_type(&icon, icon_type)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
None => Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"No matching IconType",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
let mut images_to_resize: Vec<(image::DynamicImage, u32, u32)> = vec![];
|
||||
for icon_path in settings.icon_files() {
|
||||
let icon_path = icon_path?;
|
||||
let icon = image::open(&icon_path)?;
|
||||
let density = if common::is_retina(&icon_path) { 2 } else { 1 };
|
||||
let (w, h) = icon.dimensions();
|
||||
let orig_size = min(w, h);
|
||||
let next_size_down = 2f32.powf((orig_size as f32).log2().floor()) as u32;
|
||||
if orig_size > next_size_down {
|
||||
images_to_resize.push((icon, next_size_down, density));
|
||||
} else {
|
||||
add_icon_to_family(icon, density, &mut family)?;
|
||||
}
|
||||
}
|
||||
|
||||
for (icon, next_size_down, density) in images_to_resize {
|
||||
let icon = icon.resize_exact(
|
||||
next_size_down,
|
||||
next_size_down,
|
||||
image::imageops::FilterType::Lanczos3,
|
||||
);
|
||||
add_icon_to_family(icon, density, &mut family)?;
|
||||
}
|
||||
|
||||
if !family.is_empty() {
|
||||
fs::create_dir_all(out_dir)?;
|
||||
let mut dest_path = out_dir.to_path_buf();
|
||||
dest_path.push(settings.product_name());
|
||||
dest_path.set_extension("icns");
|
||||
let icns_file = BufWriter::new(File::create(&dest_path)?);
|
||||
family.write(icns_file)?;
|
||||
Ok(Some(dest_path))
|
||||
} else {
|
||||
Err(crate::Error::GenericError(
|
||||
"No usable Icon files found".to_owned(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// Converts an image::DynamicImage into an icns::Image.
|
||||
fn make_icns_image(img: image::DynamicImage) -> io::Result<icns::Image> {
|
||||
let pixel_format = match img.color() {
|
||||
image::ColorType::Rgba8 => icns::PixelFormat::RGBA,
|
||||
image::ColorType::Rgb8 => icns::PixelFormat::RGB,
|
||||
image::ColorType::La8 => icns::PixelFormat::GrayAlpha,
|
||||
image::ColorType::L8 => icns::PixelFormat::Gray,
|
||||
_ => {
|
||||
let msg = format!("unsupported ColorType: {:?}", img.color());
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidData, msg));
|
||||
}
|
||||
};
|
||||
icns::Image::from_data(pixel_format, img.width(), img.height(), img.to_bytes())
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
pub mod app;
|
||||
pub mod dmg;
|
||||
pub mod icon;
|
||||
pub mod ios;
|
||||
pub mod sign;
|
||||
|
Loading…
Reference in New Issue
Block a user