Skip to content

Commit 006f621

Browse files
committed
Support GIFs, bump deps
1 parent 1625d05 commit 006f621

2 files changed

Lines changed: 97 additions & 75 deletions

File tree

Cargo.toml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,19 @@ categories = ["encoding", "graphics", "multimedia"]
1111
keywords = ["svg", "pdf", "vector-graphics", "conversion"]
1212

1313
[features]
14-
default = ["png", "jpeg"]
14+
default = ["png", "jpeg", "gif"]
1515
png = ["image/png"]
1616
jpeg = ["image/jpeg"]
17+
gif = ["image/gif"]
1718
cli = ["clap", "termcolor", "usvg/text", "fontdb"]
1819

1920
[dependencies]
2021
miniz_oxide = "0.4"
2122
pdf-writer = "0.4.1"
22-
usvg = { version = "0.20", default-features = false }
23+
usvg = { version = "0.22", default-features = false }
2324
clap = { version = "3", features = ["derive"], optional = true }
24-
fontdb = { version = "0.7", optional = true }
25-
image = { version = "0.23", default-features = false, optional = true }
25+
fontdb = { version = "0.9", optional = true }
26+
image = { version = "0.24", default-features = false, optional = true }
2627
termcolor = { version = "1", optional = true }
2728

2829
[[bin]]

src/render.rs

Lines changed: 92 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use usvg::{
1515
#[cfg(any(feature = "png", feature = "jpeg"))]
1616
use {
1717
image::io::Reader as ImageReader,
18-
image::{DynamicImage, GenericImageView, ImageFormat, Rgb, Rgba, Luma},
18+
image::{DynamicImage, ImageFormat, Luma, Rgb, Rgba},
1919
pdf_writer::writers::ImageXObject,
2020
};
2121

@@ -553,7 +553,7 @@ impl Render for usvg::Image {
553553

554554
let image_ref = ctx.alloc_ref();
555555

556-
#[cfg(any(feature = "png", feature = "jpeg"))]
556+
#[cfg(any(feature = "png", feature = "jpeg", feature = "gif"))]
557557
let set_image_props = |
558558
image: &mut ImageXObject,
559559
raster_size: &mut Option<(u32, u32)>,
@@ -576,14 +576,75 @@ impl Render for usvg::Image {
576576
}
577577
};
578578

579-
#[cfg(any(feature = "png", feature = "jpeg"))]
579+
#[cfg(any(feature = "png", feature = "jpeg", feature = "gif"))]
580580
let mut raster_size: Option<(u32, u32)> = None;
581581
let rect = self.view_box.rect;
582582

583+
#[cfg(any(feature = "png", feature = "gif"))]
584+
let mut apply_transparent = |decoded: DynamicImage| {
585+
let color = decoded.color();
586+
587+
let bits = color.bits_per_pixel();
588+
let channels = color.channel_count() as u16;
589+
let image_bytes: Vec<u8> = match (channels, bits / channels > 8) {
590+
(1, false) => {
591+
decoded.to_luma8().pixels().flat_map(|&Luma(c)| c).collect()
592+
}
593+
(1, true) => decoded
594+
.to_luma16()
595+
.pixels()
596+
.flat_map(|&Luma(x)| x)
597+
.flat_map(|x| x.to_be_bytes())
598+
.collect(),
599+
(3 | 4, false) => {
600+
decoded.to_rgb8().pixels().flat_map(|&Rgb(c)| c).collect()
601+
}
602+
(3 | 4, true) => decoded
603+
.to_rgb16()
604+
.pixels()
605+
.flat_map(|&Rgb(c)| c)
606+
.flat_map(|x| x.to_be_bytes())
607+
.collect(),
608+
_ => panic!("unknown number of channels={channels}"),
609+
};
610+
let compressed = compress_to_vec_zlib(&image_bytes, 8);
611+
612+
let mut image = writer.image_xobject(image_ref, &compressed);
613+
set_image_props(&mut image, &mut raster_size, &decoded, false);
614+
image.filter(Filter::FlateDecode);
615+
616+
// The alpha channel has to be written separately, as a Soft
617+
// Mask.
618+
if color.has_alpha() {
619+
let mask_id = ctx.alloc_ref();
620+
image.pair(Name(b"SMask"), mask_id);
621+
image.finish();
622+
623+
let bits = color.bits_per_pixel();
624+
let channels = color.channel_count() as u16;
625+
let alpha_bytes: Vec<u8> = if bits / channels > 8 {
626+
decoded
627+
.to_rgba16()
628+
.pixels()
629+
.flat_map(|&Rgba([.., a])| a.to_be_bytes())
630+
.collect()
631+
} else {
632+
decoded.to_rgba8().pixels().map(|&Rgba([.., a])| a).collect()
633+
};
634+
635+
let compressed = compress_to_vec_zlib(&alpha_bytes, 8);
636+
let mut mask = writer.image_xobject(mask_id, &compressed);
637+
let mut void = None;
638+
639+
set_image_props(&mut mask, &mut void, &decoded, true);
640+
mask.filter(Filter::FlateDecode);
641+
}
642+
};
643+
583644
match &self.kind {
584645
#[cfg(feature = "jpeg")]
585646
ImageKind::JPEG(buf) => {
586-
let cursor = std::io::Cursor::new(buf);
647+
let cursor = std::io::Cursor::new(buf.as_ref());
587648
let decoded = if let Ok(decoded) =
588649
ImageReader::with_format(cursor, ImageFormat::Jpeg).decode()
589650
{
@@ -598,72 +659,29 @@ impl Render for usvg::Image {
598659
}
599660
#[cfg(feature = "png")]
600661
ImageKind::PNG(buf) => {
601-
let cursor = std::io::Cursor::new(buf);
602-
let decoded = if let Ok(decoded) =
603-
ImageReader::with_format(cursor, ImageFormat::Png).decode()
604-
{
605-
decoded
606-
} else {
607-
return;
608-
};
609-
610-
let color = decoded.color();
611-
612-
let bits = color.bits_per_pixel();
613-
let channels = color.channel_count() as u16;
614-
let image_bytes: Vec<u8> = match (channels, bits / channels > 8) {
615-
(1, false) => {
616-
decoded.to_luma8().pixels().flat_map(|&Luma(c)| c).collect()
617-
}
618-
(1, true) => decoded
619-
.to_luma16()
620-
.pixels()
621-
.flat_map(|&Luma(x)| x)
622-
.flat_map(|x| x.to_be_bytes())
623-
.collect(),
624-
(3 | 4, false) => {
625-
decoded.to_rgb8().pixels().flat_map(|&Rgb(c)| c).collect()
626-
}
627-
(3 | 4, true) => decoded
628-
.to_rgb16()
629-
.pixels()
630-
.flat_map(|&Rgb(c)| c)
631-
.flat_map(|x| x.to_be_bytes())
632-
.collect(),
633-
_ => panic!("unknown number of channels={channels}"),
634-
};
635-
let compressed = compress_to_vec_zlib(&image_bytes, 8);
636-
637-
let mut image = writer.image_xobject(image_ref, &compressed);
638-
set_image_props(&mut image, &mut raster_size, &decoded, false);
639-
image.filter(Filter::FlateDecode);
640-
641-
// The alpha channel has to be written separately, as a Soft
642-
// Mask.
643-
if color.has_alpha() {
644-
let mask_id = ctx.alloc_ref();
645-
image.pair(Name(b"SMask"), mask_id);
646-
image.finish();
647-
648-
let bits = color.bits_per_pixel();
649-
let channels = color.channel_count() as u16;
650-
let alpha_bytes: Vec<u8> = if bits / channels > 8 {
662+
let cursor = std::io::Cursor::new(buf.as_ref());
663+
apply_transparent(
664+
if let Ok(decoded) =
665+
ImageReader::with_format(cursor, ImageFormat::Png).decode()
666+
{
651667
decoded
652-
.to_rgba16()
653-
.pixels()
654-
.flat_map(|&Rgba([.., a])| a.to_be_bytes())
655-
.collect()
656668
} else {
657-
decoded.to_rgba8().pixels().map(|&Rgba([.., a])| a).collect()
658-
};
659-
660-
let compressed = compress_to_vec_zlib(&alpha_bytes, 8);
661-
let mut mask = writer.image_xobject(mask_id, &compressed);
662-
let mut void = None;
663-
664-
set_image_props(&mut mask, &mut void, &decoded, true);
665-
mask.filter(Filter::FlateDecode);
666-
}
669+
return;
670+
},
671+
);
672+
}
673+
#[cfg(feature = "gif")]
674+
ImageKind::GIF(buf) => {
675+
let cursor = std::io::Cursor::new(buf.as_ref());
676+
apply_transparent(
677+
if let Ok(decoded) =
678+
ImageReader::with_format(cursor, ImageFormat::Gif).decode()
679+
{
680+
decoded
681+
} else {
682+
return;
683+
},
684+
);
667685
}
668686
ImageKind::SVG(tree) => {
669687
// An SVG image means that the file gets embedded in a
@@ -677,13 +695,16 @@ impl Render for usvg::Image {
677695

678696
ctx.next_id = convert_tree_into(tree, opt, writer, image_ref).get();
679697
}
680-
681-
#[cfg(any(not(feature = "jpeg"), not(feature = "png")))]
698+
#[cfg(any(
699+
not(feature = "jpeg"),
700+
not(feature = "png"),
701+
not(feature = "gif")
702+
))]
682703
_ => {}
683704
}
684705

685706
// Common operations for raster image formats.
686-
#[cfg(any(feature = "png", feature = "jpeg"))]
707+
#[cfg(any(feature = "png", feature = "jpeg", feature = "gif"))]
687708
let image_ref = if let Some((width, height)) = raster_size {
688709
let mut content = Content::new();
689710
let xobj_name = Name(b"EmbRaster");

0 commit comments

Comments
 (0)