diff --git a/crates/typst-pdf/src/krilla.rs b/crates/typst-pdf/src/krilla.rs index cb1dbb4b9..34e2b193c 100644 --- a/crates/typst-pdf/src/krilla.rs +++ b/crates/typst-pdf/src/krilla.rs @@ -6,7 +6,7 @@ use crate::page::PageLabelExt; use crate::shape::handle_shape; use crate::text::handle_text; use crate::util::{convert_path, display_font, AbsExt, TransformExt}; -use crate::{paint, PdfOptions}; +use crate::PdfOptions; use krilla::destination::{NamedDestination, XyzDestination}; use krilla::error::KrillaError; use krilla::page::PageLabel; @@ -24,19 +24,20 @@ use typst_library::layout::{ }; use typst_library::model::HeadingElem; use typst_library::text::{Font, Lang}; -use typst_library::visualize::{Geometry, Paint, Shape}; +use typst_library::visualize::{Geometry, Paint}; use typst_syntax::Span; +/// A state allowing us to keep track of transforms and container sizes, +/// which is mainly needed to resolve gradients and patterns correctly. #[derive(Debug, Clone)] pub(crate) struct State { + pub(crate) transform: Transform, /// The full transform chain transform_chain: Transform, - /// The transform of the current item. - pub(crate) transform: Transform, /// The transform of first hard frame in the hierarchy. container_transform_chain: Transform, /// The size of the first hard frame in the hierarchy. - size: Size, + container_size: Size, } impl State { @@ -50,29 +51,26 @@ impl State { transform_chain, transform: Transform::identity(), container_transform_chain, - size, + container_size: size, } } - pub(crate) fn size(&mut self, size: Size) { - self.size = size; + pub(crate) fn register_container(&mut self, size: Size) { + self.container_transform_chain = self.transform_chain; + self.container_size = size; } - pub(crate) fn transform(&mut self, transform: Transform) { + pub(crate) fn pre_concat(&mut self, transform: Transform) { self.transform = self.transform.pre_concat(transform); self.transform_chain = self.transform_chain.pre_concat(transform); } - fn set_container_transform(&mut self) { - self.container_transform_chain = self.transform_chain; - } - /// Creates the [`Transforms`] structure for the current item. pub(crate) fn transforms(&self, size: Size) -> Transforms { Transforms { transform_chain_: self.transform_chain, container_transform_chain: self.container_transform_chain, - container_size: self.size, + container_size: self.container_size, size, } } @@ -296,8 +294,7 @@ pub(crate) fn handle_frame( fc.push(); if frame.kind().is_hard() { - fc.state_mut().set_container_transform(); - fc.state_mut().size(frame.size()); + fc.state_mut().register_container(frame.size()); } if let Some(fill) = fill { @@ -307,7 +304,7 @@ pub(crate) fn handle_frame( for (point, item) in frame.items() { fc.push(); - fc.state_mut().transform(Transform::translate(point.x, point.y)); + fc.state_mut().pre_concat(Transform::translate(point.x, point.y)); match item { FrameItem::Group(g) => handle_group(fc, g, surface, gc)?, @@ -335,7 +332,7 @@ pub(crate) fn handle_group( context: &mut GlobalContext, ) -> SourceResult<()> { fc.push(); - fc.state_mut().transform(group.transform); + fc.state_mut().pre_concat(group.transform); let clip_path = group .clip_path diff --git a/crates/typst-pdf/src/paint.rs b/crates/typst-pdf/src/paint.rs index cc0cdf71a..41f6ba6ae 100644 --- a/crates/typst-pdf/src/paint.rs +++ b/crates/typst-pdf/src/paint.rs @@ -13,7 +13,7 @@ use typst_library::visualize::{ }; use typst_utils::Numeric; -pub(crate) fn fill( +pub(crate) fn convert_fill( gc: &mut GlobalContext, paint_: &Paint, fill_rule_: FillRule, @@ -21,7 +21,7 @@ pub(crate) fn fill( surface: &mut Surface, transforms: Transforms, ) -> SourceResult { - let (paint, opacity) = paint(gc, paint_, on_text, surface, transforms)?; + let (paint, opacity) = convert_paint(gc, paint_, on_text, surface, transforms)?; Ok(krilla::path::Fill { paint, @@ -30,14 +30,14 @@ pub(crate) fn fill( }) } -pub(crate) fn stroke( +pub(crate) fn convert_stroke( fc: &mut GlobalContext, stroke: &FixedStroke, on_text: bool, surface: &mut Surface, transforms: Transforms, ) -> SourceResult { - let (paint, opacity) = paint(fc, &stroke.paint, on_text, surface, transforms)?; + let (paint, opacity) = convert_paint(fc, &stroke.paint, on_text, surface, transforms)?; Ok(krilla::path::Stroke { paint, @@ -46,18 +46,18 @@ pub(crate) fn stroke( line_join: stroke.join.to_krilla(), line_cap: stroke.cap.to_krilla(), opacity: NormalizedF32::new(opacity as f32 / 255.0).unwrap(), - dash: stroke.dash.as_ref().map(|d| dash(d)), + dash: stroke.dash.as_ref().map(|d| convert_dash(d)), }) } -fn dash(dash: &DashPattern) -> krilla::path::StrokeDash { +fn convert_dash(dash: &DashPattern) -> krilla::path::StrokeDash { krilla::path::StrokeDash { array: dash.array.iter().map(|e| e.to_f32()).collect(), offset: dash.phase.to_f32(), } } -fn paint( +fn convert_paint( gc: &mut GlobalContext, paint: &Paint, on_text: bool, @@ -65,40 +65,41 @@ fn paint( transforms: Transforms, ) -> SourceResult<(krilla::paint::Paint, u8)> { match paint { - Paint::Solid(c) => { - let (p, alpha) = match c.space() { - ColorSpace::D65Gray => { - let components = c.to_vec4_u8(); - (krilla::color::luma::Color::new(components[0]).into(), components[3]) - } - ColorSpace::Cmyk => { - let components = c.to_vec4_u8(); - ( - krilla::color::cmyk::Color::new( - components[0], - components[1], - components[2], - components[3], - ) - .into(), - // Typst doesn't support alpha on CMYK colors. - 255, - ) - } - _ => { - let (c, a) = c.to_krilla_rgb(); - (c.into(), a) - } - }; - - Ok((p, alpha)) - } + Paint::Solid(c) => Ok(convert_solid(c)), Paint::Gradient(g) => Ok(convert_gradient(g, on_text, transforms)), Paint::Tiling(p) => convert_pattern(gc, p, on_text, surface, transforms), } } -pub(crate) fn convert_pattern( +fn convert_solid(color: &Color) -> (krilla::paint::Paint, u8) { + match color.space() { + ColorSpace::D65Gray => { + let components = color.to_vec4_u8(); + (krilla::color::luma::Color::new(components[0]).into(), components[3]) + } + ColorSpace::Cmyk => { + let components = color.to_vec4_u8(); + ( + krilla::color::cmyk::Color::new( + components[0], + components[1], + components[2], + components[3], + ) + .into(), + // Typst doesn't support alpha on CMYK colors. + 255, + ) + } + // Convert all remaining colors into RGB + _ => { + let (c, a) = color.to_krilla_rgb(); + (c.into(), a) + } + } +} + +fn convert_pattern( gc: &mut GlobalContext, pattern: &Tiling, on_text: bool, diff --git a/crates/typst-pdf/src/shape.rs b/crates/typst-pdf/src/shape.rs index 9c269f8d5..9a9c9878c 100644 --- a/crates/typst-pdf/src/shape.rs +++ b/crates/typst-pdf/src/shape.rs @@ -17,7 +17,7 @@ pub(crate) fn handle_shape( if let Some(path) = convert_geometry(&shape.geometry) { if let Some(paint) = &shape.fill { - let fill = paint::fill( + let fill = paint::convert_fill( gc, &paint, shape.fill_rule, @@ -38,7 +38,7 @@ pub(crate) fn handle_shape( }); if let Some(stroke) = &stroke { - let stroke = paint::stroke( + let stroke = paint::convert_stroke( gc, stroke, false, diff --git a/crates/typst-pdf/src/text.rs b/crates/typst-pdf/src/text.rs index 32096e761..98fa37d8d 100644 --- a/crates/typst-pdf/src/text.rs +++ b/crates/typst-pdf/src/text.rs @@ -21,7 +21,7 @@ pub(crate) fn handle_text( *gc.languages.entry(t.lang).or_insert(0) += t.glyphs.len(); let font = convert_font(gc, t.font.clone())?; - let fill = paint::fill( + let fill = paint::convert_fill( gc, &t.fill, FillRule::NonZero, @@ -48,7 +48,7 @@ pub(crate) fn handle_text( if let Some(stroke) = t .stroke .as_ref() - .map(|s| paint::stroke(gc, s, true, surface, fc.state().transforms(Size::zero()))) + .map(|s| paint::convert_stroke(gc, s, true, surface, fc.state().transforms(Size::zero()))) { let stroke = stroke?;