use std::hash::{Hash, Hasher};
use std::sync::Arc;
use pdf_writer::types::{LineCapStyle, LineJoinStyle};
use crate::color::SpecialColor;
use crate::geom::Transform;
use crate::graphics::color::{cmyk, luma, rgb, Color};
use crate::num::NormalizedF32;
use crate::stream::Stream;
#[derive(Debug, Clone, PartialEq)]
pub struct LinearGradient {
pub x1: f32,
pub y1: f32,
pub x2: f32,
pub y2: f32,
pub transform: Transform,
pub spread_method: SpreadMethod,
pub stops: Vec<Stop>,
pub anti_alias: bool,
}
impl Eq for LinearGradient {}
impl Hash for LinearGradient {
fn hash<H: Hasher>(&self, state: &mut H) {
self.x1.to_bits().hash(state);
self.y1.to_bits().hash(state);
self.x2.to_bits().hash(state);
self.y2.to_bits().hash(state);
self.transform.hash(state);
self.spread_method.hash(state);
self.stops.hash(state);
self.anti_alias.hash(state);
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct RadialGradient {
pub fx: f32,
pub fy: f32,
pub fr: f32,
pub cx: f32,
pub cy: f32,
pub cr: f32,
pub transform: Transform,
pub spread_method: SpreadMethod,
pub stops: Vec<Stop>,
pub anti_alias: bool,
}
impl Eq for RadialGradient {}
impl Hash for RadialGradient {
fn hash<H: Hasher>(&self, state: &mut H) {
self.fx.to_bits().hash(state);
self.fy.to_bits().hash(state);
self.fr.to_bits().hash(state);
self.cx.to_bits().hash(state);
self.cy.to_bits().hash(state);
self.cr.to_bits().hash(state);
self.transform.hash(state);
self.spread_method.hash(state);
self.stops.hash(state);
self.anti_alias.hash(state);
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SweepGradient {
pub cx: f32,
pub cy: f32,
pub start_angle: f32,
pub end_angle: f32,
pub transform: Transform,
pub spread_method: SpreadMethod,
pub stops: Vec<Stop>,
pub anti_alias: bool,
}
impl Eq for SweepGradient {}
impl Hash for SweepGradient {
fn hash<H: Hasher>(&self, state: &mut H) {
self.cx.to_bits().hash(state);
self.cy.to_bits().hash(state);
self.start_angle.to_bits().hash(state);
self.end_angle.to_bits().hash(state);
self.transform.hash(state);
self.spread_method.hash(state);
self.stops.hash(state);
self.anti_alias.hash(state);
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct Pattern {
pub stream: Stream,
pub transform: Transform,
pub width: f32,
pub height: f32,
}
impl Eq for Pattern {}
impl Hash for Pattern {
fn hash<H: Hasher>(&self, state: &mut H) {
self.stream.hash(state);
self.transform.hash(state);
self.width.to_bits().hash(state);
self.height.to_bits().hash(state);
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub(crate) enum InnerPaint {
Color(Color),
LinearGradient(LinearGradient),
RadialGradient(RadialGradient),
SweepGradient(SweepGradient),
Pattern(Arc<Pattern>),
}
#[derive(Debug, Hash, Eq, PartialEq, Clone)]
pub struct Paint(pub(crate) InnerPaint);
impl Paint {
pub(crate) fn as_rgb(&self) -> Option<rgb::Color> {
match &self.0 {
InnerPaint::Color(c) => match c {
Color::Regular(c) => c.as_rgb(),
Color::Special(SpecialColor::Separation(c)) => c.space.fallback.as_rgb(),
},
_ => None,
}
}
}
impl From<rgb::Color> for Paint {
fn from(value: rgb::Color) -> Self {
Paint(InnerPaint::Color(value.into()))
}
}
impl From<luma::Color> for Paint {
fn from(value: luma::Color) -> Self {
Paint(InnerPaint::Color(value.into()))
}
}
impl From<cmyk::Color> for Paint {
fn from(value: cmyk::Color) -> Self {
Paint(InnerPaint::Color(value.into()))
}
}
impl From<Color> for Paint {
fn from(value: Color) -> Self {
Paint(InnerPaint::Color(value))
}
}
impl From<LinearGradient> for Paint {
fn from(value: LinearGradient) -> Self {
Paint(InnerPaint::LinearGradient(value))
}
}
impl From<RadialGradient> for Paint {
fn from(value: RadialGradient) -> Self {
Paint(InnerPaint::RadialGradient(value))
}
}
impl From<SweepGradient> for Paint {
fn from(value: SweepGradient) -> Self {
Paint(InnerPaint::SweepGradient(value))
}
}
impl From<Pattern> for Paint {
fn from(value: Pattern) -> Self {
Paint(InnerPaint::Pattern(Arc::new(value)))
}
}
#[derive(Debug, Hash, Eq, PartialEq, Copy, Clone, Default)]
pub enum SpreadMethod {
#[default]
Pad,
Reflect,
Repeat,
}
#[derive(Debug, Hash, Eq, PartialEq, Clone)]
#[allow(private_bounds)]
pub struct Stop {
pub offset: NormalizedF32,
pub color: Color,
pub opacity: NormalizedF32,
}
#[derive(Eq, PartialEq, Debug, Clone, Copy, Default, Hash)]
pub enum LineCap {
#[default]
Butt,
Round,
Square,
}
impl LineCap {
pub(crate) fn to_pdf_line_cap(self) -> LineCapStyle {
match self {
LineCap::Butt => LineCapStyle::ButtCap,
LineCap::Round => LineCapStyle::RoundCap,
LineCap::Square => LineCapStyle::ProjectingSquareCap,
}
}
}
#[derive(PartialEq, Eq, Debug, Clone, Copy, Default, Hash)]
pub enum LineJoin {
#[default]
Miter,
Round,
Bevel,
}
impl LineJoin {
pub(crate) fn to_pdf_line_join(self) -> LineJoinStyle {
match self {
LineJoin::Miter => LineJoinStyle::MiterJoin,
LineJoin::Round => LineJoinStyle::RoundJoin,
LineJoin::Bevel => LineJoinStyle::BevelJoin,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct StrokeDash {
pub array: Vec<f32>,
pub offset: f32,
}
impl Eq for StrokeDash {}
impl Hash for StrokeDash {
fn hash<H: Hasher>(&self, state: &mut H) {
for el in &self.array {
el.to_bits().hash(state);
}
self.offset.to_bits().hash(state);
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Stroke {
pub paint: Paint,
pub width: f32,
pub miter_limit: f32,
pub line_cap: LineCap,
pub line_join: LineJoin,
pub opacity: NormalizedF32,
pub dash: Option<StrokeDash>,
}
impl Eq for Stroke {}
impl Hash for Stroke {
fn hash<H: Hasher>(&self, state: &mut H) {
self.paint.hash(state);
self.width.to_bits().hash(state);
self.miter_limit.to_bits().hash(state);
self.line_cap.hash(state);
self.line_join.hash(state);
self.opacity.hash(state);
self.dash.hash(state);
}
}
impl Default for Stroke {
fn default() -> Self {
Stroke {
paint: luma::Color::black().into(),
width: 1.0,
miter_limit: 10.0,
line_cap: LineCap::default(),
line_join: LineJoin::default(),
opacity: NormalizedF32::ONE,
dash: None,
}
}
}
impl Stroke {
pub(crate) fn into_tiny_skia(self) -> tiny_skia_path::Stroke {
let mut stroke = tiny_skia_path::Stroke {
width: self.width,
miter_limit: self.miter_limit,
line_cap: match self.line_cap {
LineCap::Butt => tiny_skia_path::LineCap::Butt,
LineCap::Round => tiny_skia_path::LineCap::Round,
LineCap::Square => tiny_skia_path::LineCap::Square,
},
line_join: match self.line_join {
LineJoin::Miter => tiny_skia_path::LineJoin::Miter,
LineJoin::Round => tiny_skia_path::LineJoin::Round,
LineJoin::Bevel => tiny_skia_path::LineJoin::Bevel,
},
dash: None,
};
if let Some(stroke_dash) = self.dash {
stroke.dash = tiny_skia_path::StrokeDash::new(stroke_dash.array, stroke_dash.offset);
}
stroke
}
}
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash, Default)]
pub enum FillRule {
#[default]
NonZero,
EvenOdd,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Fill {
pub paint: Paint,
pub opacity: NormalizedF32,
pub rule: FillRule,
}
impl Default for Fill {
fn default() -> Self {
Fill {
paint: luma::Color::black().into(),
opacity: NormalizedF32::ONE,
rule: FillRule::default(),
}
}
}