pub extern crate image;
pub extern crate resvg;
use crate::usvg::Tree;
use image::{DynamicImage, GenericImageView, ImageError};
pub use resvg::{
raqote,
usvg::{self, XmlIndent, XmlOptions},
};
use std::{
convert::From,
error,
fmt::{self, Debug, Display, Formatter},
fs::File,
io::{self, Write},
path::Path,
};
pub mod favicon;
pub mod icns;
pub mod ico;
pub mod resample;
pub mod encode;
#[cfg(test)]
mod test;
const STD_CAPACITY: usize = 7;
const MISMATCHED_DIM_ERR: &str =
"a resampling filter returned an image of dimensions other than the ones specified by it's arguments";
pub trait Icon
where
Self: Sized,
{
type Key: AsSize + Send + Sync;
fn new() -> Self {
Self::with_capacity(STD_CAPACITY)
}
fn with_capacity(capacity: usize) -> Self;
fn len(&self) -> usize;
fn add_entry<F: FnMut(&DynamicImage, u32) -> io::Result<DynamicImage>>(
&mut self,
filter: F,
source: &Image,
key: Self::Key,
) -> Result<(), IconError<Self::Key>>;
fn add_entries<F: FnMut(&DynamicImage, u32) -> io::Result<DynamicImage>, I: IntoIterator<Item = Self::Key>>(
&mut self,
mut filter: F,
source: &Image,
keys: I,
) -> Result<(), IconError<Self::Key>> {
for key in keys {
self.add_entry(|src, size| filter(src, size), source, key)?;
}
Ok(())
}
fn write<W: Write>(&mut self, w: &mut W) -> io::Result<()>;
fn save<P: AsRef<Path>>(&mut self, path: &P) -> io::Result<()> {
let mut file = File::create(path.as_ref())?;
self.write(&mut file)
}
}
pub trait AsSize {
fn as_size(&self) -> u32;
}
#[derive(Clone)]
pub enum Image {
Raster(DynamicImage),
Svg(Tree),
}
pub enum IconError<K: AsSize + Send + Sync> {
AlreadyIncluded(K),
Resample(ResampleError),
}
#[derive(Debug)]
pub enum ResampleError {
Io(io::Error),
MismatchedDimensions(u32, (u32, u32)),
}
impl Image {
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, io::Error> {
match image::open(&path) {
Ok(img) => Ok(Image::from(img)),
Err(ImageError::InsufficientMemory) => Err(io::Error::from(io::ErrorKind::Other)),
Err(ImageError::IoError(err)) => Err(err),
Err(ImageError::UnsupportedError(_)) => {
match Tree::from_file(path, &usvg::Options::default()) {
Ok(img) => Ok(Image::from(img)),
Err(usvg::Error::InvalidFileSuffix) => {
Err(io::Error::from(io::ErrorKind::InvalidInput))
}
Err(usvg::Error::FileOpenFailed) => Err(io::Error::from(io::ErrorKind::Other)),
_ => Err(io::Error::from(io::ErrorKind::InvalidData)),
}
}
_ => Err(io::Error::from(io::ErrorKind::InvalidData)),
}
}
pub fn rasterize<F: FnMut(&DynamicImage, u32) -> io::Result<DynamicImage>>(
&self,
filter: F,
size: u32,
) -> Result<DynamicImage, ResampleError> {
match self {
Self::Raster(ras) => resample::apply(filter, ras, size),
Self::Svg(svg) => resample::svg(svg, size),
}
}
pub fn width(&self) -> f64 {
match self {
Image::Raster(ras) => ras.width() as f64,
Image::Svg(svg) => svg.svg_node().view_box.rect.width(),
}
}
pub fn height(&self) -> f64 {
match self {
Image::Raster(ras) => ras.height() as f64,
Image::Svg(svg) => svg.svg_node().view_box.rect.height(),
}
}
pub fn dimensions(&self) -> (f64, f64) {
(self.width(), self.height())
}
}
impl From<Tree> for Image {
fn from(svg: Tree) -> Self {
Image::Svg(svg)
}
}
impl From<DynamicImage> for Image {
fn from(bit: DynamicImage) -> Self {
Image::Raster(bit)
}
}
unsafe impl Send for Image {}
unsafe impl Sync for Image {}
impl<K: AsSize + Send + Sync> IconError<K> {
pub fn map<T: AsSize + Send + Sync, F: FnOnce(K) -> T>(
self,
f: F
) -> IconError<T> {
match self {
Self::AlreadyIncluded(e) => IconError::AlreadyIncluded(f(e)),
Self::Resample(err) => IconError::Resample(err),
}
}
}
impl<K: AsSize + Send + Sync> Display for IconError<K> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::AlreadyIncluded(_) => write!(
f,
"the icon already contains an entry associated with this key"
),
Self::Resample(err) => <ResampleError as Display>::fmt(&err, f),
}
}
}
impl<K: AsSize + Send + Sync + Debug> Debug for IconError<K> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::AlreadyIncluded(e) => write!(f, "Error::AlreadyIncluded({:?})", e),
Self::Resample(err) => <ResampleError as Debug>::fmt(&err, f),
}
}
}
impl<K: AsSize + Send + Sync + Debug> error::Error for IconError<K> {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
if let Self::Resample(ref err) = self {
err.source()
} else {
None
}
}
}
impl<K: AsSize + Send + Sync> From<ResampleError> for IconError<K> {
fn from(err: ResampleError) -> Self {
Self::Resample(err)
}
}
impl<K: AsSize + Send + Sync> From<io::Error> for IconError<K> {
fn from(err: io::Error) -> Self {
Self::from(ResampleError::from(err))
}
}
impl<K: AsSize + Send + Sync> Into<io::Error> for IconError<K> {
fn into(self) -> io::Error {
if let Self::Resample(err) = self {
err.into()
} else {
io::Error::from(io::ErrorKind::InvalidInput)
}
}
}
impl From<io::Error> for ResampleError {
fn from(err: io::Error) -> Self {
Self::Io(err)
}
}
impl Display for ResampleError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Io(err) => write!(f, "{}", err),
Self::MismatchedDimensions(s, (w, h)) => write!(
f,
"{0}: expected {1}x{1}, got {2}x{3}",
MISMATCHED_DIM_ERR, s, w, h
),
}
}
}
impl error::Error for ResampleError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
if let Self::Io(ref err) = self {
Some(err)
} else {
None
}
}
}
impl Into<io::Error> for ResampleError {
fn into(self) -> io::Error {
match self {
Self::Io(err) => err,
Self::MismatchedDimensions(_, _) => io::Error::from(io::ErrorKind::InvalidData),
}
}
}