Laurenz Stampfl 96dd67e011
Switch PDF backend to krilla (#5420)
Co-authored-by: Laurenz <laurmaedje@gmail.com>
2025-04-01 14:42:52 +00:00

107 lines
3.0 KiB
Rust

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()
}