summaryrefslogtreecommitdiff
path: root/crates/typst-pdf/src/shape.rs
diff options
context:
space:
mode:
authorLaurenz Stampfl <47084093+LaurenzV@users.noreply.github.com>2025-04-01 16:42:52 +0200
committerGitHub <noreply@github.com>2025-04-01 14:42:52 +0000
commit96dd67e011bb317cf78683bcf1edfdfca5e7b6b3 (patch)
tree900a0c4e7723af4289685af35d788041055ad4a2 /crates/typst-pdf/src/shape.rs
parent012e14d40cb44997630cf6469a446f217f2e9057 (diff)
Switch PDF backend to `krilla` (#5420)
Co-authored-by: Laurenz <laurmaedje@gmail.com>
Diffstat (limited to 'crates/typst-pdf/src/shape.rs')
-rw-r--r--crates/typst-pdf/src/shape.rs106
1 files changed, 106 insertions, 0 deletions
diff --git a/crates/typst-pdf/src/shape.rs b/crates/typst-pdf/src/shape.rs
new file mode 100644
index 00000000..5b9232db
--- /dev/null
+++ b/crates/typst-pdf/src/shape.rs
@@ -0,0 +1,106 @@
+use krilla::geom::{Path, PathBuilder, Rect};
+use krilla::surface::Surface;
+use typst_library::diag::SourceResult;
+use typst_library::visualize::{Geometry, Shape};
+use typst_syntax::Span;
+
+use crate::convert::{FrameContext, GlobalContext};
+use crate::paint;
+use crate::util::{convert_path, AbsExt, TransformExt};
+
+#[typst_macros::time(name = "handle shape")]
+pub(crate) fn handle_shape(
+ fc: &mut FrameContext,
+ shape: &Shape,
+ surface: &mut Surface,
+ gc: &mut GlobalContext,
+ span: Span,
+) -> SourceResult<()> {
+ surface.set_location(span.into_raw().get());
+ surface.push_transform(&fc.state().transform().to_krilla());
+
+ if let Some(path) = convert_geometry(&shape.geometry) {
+ let fill = if let Some(paint) = &shape.fill {
+ Some(paint::convert_fill(
+ gc,
+ paint,
+ shape.fill_rule,
+ false,
+ surface,
+ fc.state(),
+ shape.geometry.bbox_size(),
+ )?)
+ } else {
+ None
+ };
+
+ let stroke = shape.stroke.as_ref().and_then(|stroke| {
+ if stroke.thickness.to_f32() > 0.0 {
+ Some(stroke)
+ } else {
+ None
+ }
+ });
+
+ let stroke = if let Some(stroke) = &stroke {
+ let stroke = paint::convert_stroke(
+ gc,
+ stroke,
+ false,
+ surface,
+ fc.state(),
+ shape.geometry.bbox_size(),
+ )?;
+
+ Some(stroke)
+ } else {
+ None
+ };
+
+ // Otherwise, krilla will by default fill with a black paint.
+ if fill.is_some() || stroke.is_some() {
+ surface.set_fill(fill);
+ surface.set_stroke(stroke);
+ surface.draw_path(&path);
+ }
+ }
+
+ surface.pop();
+ surface.reset_location();
+
+ Ok(())
+}
+
+fn convert_geometry(geometry: &Geometry) -> Option<Path> {
+ let mut path_builder = PathBuilder::new();
+
+ match geometry {
+ Geometry::Line(l) => {
+ path_builder.move_to(0.0, 0.0);
+ path_builder.line_to(l.x.to_f32(), l.y.to_f32());
+ }
+ Geometry::Rect(size) => {
+ let w = size.x.to_f32();
+ let h = size.y.to_f32();
+ let rect = if w < 0.0 || h < 0.0 {
+ // krilla doesn't normally allow for negative dimensions, but
+ // Typst supports them, so we apply a transform if needed.
+ let transform =
+ krilla::geom::Transform::from_scale(w.signum(), h.signum());
+ Rect::from_xywh(0.0, 0.0, w.abs(), h.abs())
+ .and_then(|rect| rect.transform(transform))
+ } else {
+ Rect::from_xywh(0.0, 0.0, w, h)
+ };
+
+ if let Some(rect) = rect {
+ path_builder.push_rect(rect);
+ }
+ }
+ Geometry::Curve(c) => {
+ convert_path(c, &mut path_builder);
+ }
+ }
+
+ path_builder.finish()
+}