use pdf_writer::types::{BlendMode, LineCapStyle, LineJoinStyle, MaskType};
use pdf_writer::{Content, Name, Rect};
use usvg::{LineCap, LineJoin, NonZeroRect, Transform};
use crate::render::gradient::Stop;
use crate::{ConversionError, Result};
pub trait ColorExt {
fn to_pdf_color(&self) -> [f32; 3];
}
impl ColorExt for usvg::Color {
fn to_pdf_color(&self) -> [f32; 3] {
[self.red as f32 / 255.0, self.green as f32 / 255.0, self.blue as f32 / 255.0]
}
}
pub trait TransformExt {
fn to_pdf_transform(&self) -> [f32; 6];
}
impl TransformExt for Transform {
fn to_pdf_transform(&self) -> [f32; 6] {
[self.sx, self.ky, self.kx, self.sy, self.tx, self.ty]
}
}
pub trait NameExt {
fn to_pdf_name(&self) -> Name;
}
impl NameExt for String {
fn to_pdf_name(&self) -> Name {
Name(self.as_bytes())
}
}
pub trait RectExt {
fn to_pdf_rect(&self) -> Rect;
}
impl RectExt for NonZeroRect {
fn to_pdf_rect(&self) -> Rect {
Rect::new(self.x(), self.y(), self.x() + self.width(), self.y() + self.height())
}
}
pub trait ContentExt {
fn save_state_checked(&mut self) -> Result<()>;
}
impl ContentExt for Content {
fn save_state_checked(&mut self) -> Result<()> {
self.save_state();
if self.state_nesting_depth() > 28 {
return Err(ConversionError::TooMuchNesting);
}
Ok(())
}
}
pub trait BlendModeExt {
fn to_pdf_blend_mode(&self) -> BlendMode;
}
impl BlendModeExt for usvg::BlendMode {
fn to_pdf_blend_mode(&self) -> BlendMode {
match self {
usvg::BlendMode::Normal => BlendMode::Normal,
usvg::BlendMode::Multiply => BlendMode::Multiply,
usvg::BlendMode::Screen => BlendMode::Screen,
usvg::BlendMode::Overlay => BlendMode::Overlay,
usvg::BlendMode::Darken => BlendMode::Darken,
usvg::BlendMode::Lighten => BlendMode::Lighten,
usvg::BlendMode::ColorDodge => BlendMode::ColorDodge,
usvg::BlendMode::ColorBurn => BlendMode::ColorBurn,
usvg::BlendMode::HardLight => BlendMode::HardLight,
usvg::BlendMode::SoftLight => BlendMode::SoftLight,
usvg::BlendMode::Difference => BlendMode::Difference,
usvg::BlendMode::Exclusion => BlendMode::Exclusion,
usvg::BlendMode::Hue => BlendMode::Hue,
usvg::BlendMode::Saturation => BlendMode::Saturation,
usvg::BlendMode::Color => BlendMode::Color,
usvg::BlendMode::Luminosity => BlendMode::Luminosity,
}
}
}
pub trait MaskTypeExt {
fn to_pdf_mask_type(&self) -> MaskType;
}
impl MaskTypeExt for usvg::MaskType {
fn to_pdf_mask_type(&self) -> MaskType {
match self {
usvg::MaskType::Alpha => MaskType::Alpha,
usvg::MaskType::Luminance => MaskType::Luminosity,
}
}
}
pub trait LineCapExt {
fn to_pdf_line_cap(&self) -> LineCapStyle;
}
impl LineCapExt for LineCap {
fn to_pdf_line_cap(&self) -> LineCapStyle {
match self {
LineCap::Butt => LineCapStyle::ButtCap,
LineCap::Round => LineCapStyle::RoundCap,
LineCap::Square => LineCapStyle::ProjectingSquareCap,
}
}
}
pub trait LineJoinExt {
fn to_pdf_line_join(&self) -> LineJoinStyle;
}
impl LineJoinExt for LineJoin {
fn to_pdf_line_join(&self) -> LineJoinStyle {
match self {
LineJoin::Miter => LineJoinStyle::MiterJoin,
LineJoin::MiterClip => LineJoinStyle::MiterJoin,
LineJoin::Round => LineJoinStyle::RoundJoin,
LineJoin::Bevel => LineJoinStyle::BevelJoin,
}
}
}
pub trait StopExt {
fn opacity_stops(&self) -> Stop<1>;
fn color_stops(&self) -> Stop<3>;
}
impl StopExt for usvg::Stop {
fn opacity_stops(&self) -> Stop<1> {
Stop {
color: [self.opacity().get()],
offset: self.offset().get(),
}
}
fn color_stops(&self) -> Stop<3> {
Stop {
color: self.color().to_pdf_color(),
offset: self.offset().get(),
}
}
}
pub trait GroupExt {
fn is_isolated(&self) -> bool;
}
impl GroupExt for usvg::Group {
fn is_isolated(&self) -> bool {
self.isolate()
|| self.mask().is_some()
|| self.blend_mode() != usvg::BlendMode::Normal
|| !self.filters().is_empty()
|| self.opacity().get() != 1.0
}
}
pub fn bbox_to_non_zero_rect(rect: Option<usvg::Rect>) -> NonZeroRect {
rect.and_then(|bb| bb.to_non_zero_rect())
.unwrap_or(NonZeroRect::from_xywh(0.0, 0.0, 1.0, 1.0).unwrap())
}
pub fn deflate(data: &[u8]) -> Vec<u8> {
const COMPRESSION_LEVEL: u8 = 6;
miniz_oxide::deflate::compress_to_vec_zlib(data, COMPRESSION_LEVEL)
}
pub fn clip_to_rect(rect: NonZeroRect, content: &mut Content) {
content.rect(rect.x(), rect.y(), rect.width(), rect.height());
content.close_path();
content.clip_nonzero();
content.end_path();
}