summaryrefslogtreecommitdiff
path: root/crates/typst-render/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/typst-render/src/lib.rs')
-rw-r--r--crates/typst-render/src/lib.rs78
1 files changed, 58 insertions, 20 deletions
diff --git a/crates/typst-render/src/lib.rs b/crates/typst-render/src/lib.rs
index 393f86cf..e8633f55 100644
--- a/crates/typst-render/src/lib.rs
+++ b/crates/typst-render/src/lib.rs
@@ -15,8 +15,8 @@ use typst::layout::{
};
use typst::text::{Font, TextItem};
use typst::visualize::{
- Color, FixedStroke, Geometry, Gradient, Image, ImageKind, LineCap, LineJoin, Paint,
- Path, PathItem, Pattern, RasterFormat, RelativeTo, Shape,
+ Color, DashPattern, FixedStroke, Geometry, Gradient, Image, ImageKind, LineCap,
+ LineJoin, Paint, Path, PathItem, Pattern, RasterFormat, RelativeTo, Shape,
};
use usvg::{NodeExt, TreeParsing};
@@ -377,7 +377,12 @@ fn render_outline_glyph(
// Render a glyph directly as a path. This only happens when the fast glyph
// rasterization can't be used due to very large text size or weird
// scale/skewing transforms.
- if ppem > 100.0 || ts.kx != 0.0 || ts.ky != 0.0 || ts.sx != ts.sy {
+ if ppem > 100.0
+ || ts.kx != 0.0
+ || ts.ky != 0.0
+ || ts.sx != ts.sy
+ || text.stroke.is_some()
+ {
let path = {
let mut builder = WrappedPathBuilder(sk::PathBuilder::new());
text.font.ttf().outline_glyph(id, &mut builder)?;
@@ -387,22 +392,56 @@ fn render_outline_glyph(
let scale = text.size.to_f32() / text.font.units_per_em() as f32;
let mut pixmap = None;
+
+ let rule = sk::FillRule::default();
+
+ // Flip vertically because font design coordinate
+ // system is Y-up.
+ let ts = ts.pre_scale(scale, -scale);
+ let state_ts = state.pre_concat(sk::Transform::from_scale(scale, -scale));
let paint = to_sk_paint(
&text.fill,
- state.pre_concat(sk::Transform::from_scale(scale, -scale)),
+ state_ts,
Size::zero(),
true,
None,
&mut pixmap,
None,
);
+ canvas.fill_path(&path, &paint, rule, ts, state.mask);
- let rule = sk::FillRule::default();
+ if let Some(FixedStroke {
+ paint,
+ thickness,
+ line_cap,
+ line_join,
+ dash_pattern,
+ miter_limit,
+ }) = &text.stroke
+ {
+ if thickness.to_f32() > 0.0 {
+ let dash = dash_pattern.as_ref().and_then(to_sk_dash_pattern);
+
+ let paint = to_sk_paint(
+ paint,
+ state_ts,
+ Size::zero(),
+ true,
+ None,
+ &mut pixmap,
+ None,
+ );
+ let stroke = sk::Stroke {
+ width: thickness.to_f32() / scale, // When we scale the path, we need to scale the stroke width, too.
+ line_cap: to_sk_line_cap(*line_cap),
+ line_join: to_sk_line_join(*line_join),
+ dash,
+ miter_limit: miter_limit.get() as f32,
+ };
- // Flip vertically because font design coordinate
- // system is Y-up.
- let ts = ts.pre_scale(scale, -scale);
- canvas.fill_path(&path, &paint, rule, ts, state.mask);
+ canvas.stroke_path(&path, &paint, &stroke, ts, state.mask);
+ }
+ }
return Some(());
}
@@ -581,17 +620,7 @@ fn render_shape(canvas: &mut sk::Pixmap, state: State, shape: &Shape) -> Option<
// Don't draw zero-pt stroke.
if width > 0.0 {
- let dash = dash_pattern.as_ref().and_then(|pattern| {
- // tiny-skia only allows dash patterns with an even number of elements,
- // while pdf allows any number.
- let pattern_len = pattern.array.len();
- let len =
- if pattern_len % 2 == 1 { 2 * pattern_len } else { pattern_len };
- let dash_array =
- pattern.array.iter().map(|l| l.to_f32()).cycle().take(len).collect();
-
- sk::StrokeDash::new(dash_array, pattern.phase.to_f32())
- });
+ let dash = dash_pattern.as_ref().and_then(to_sk_dash_pattern);
let bbox = shape.geometry.bbox_size();
let offset_bbox = (!matches!(shape.geometry, Geometry::Line(..)))
@@ -1045,6 +1074,15 @@ fn to_sk_transform(transform: &Transform) -> sk::Transform {
)
}
+fn to_sk_dash_pattern(pattern: &DashPattern<Abs, Abs>) -> Option<sk::StrokeDash> {
+ // tiny-skia only allows dash patterns with an even number of elements,
+ // while pdf allows any number.
+ let pattern_len = pattern.array.len();
+ let len = if pattern_len % 2 == 1 { 2 * pattern_len } else { pattern_len };
+ let dash_array = pattern.array.iter().map(|l| l.to_f32()).cycle().take(len).collect();
+ sk::StrokeDash::new(dash_array, pattern.phase.to_f32())
+}
+
/// Allows to build tiny-skia paths from glyph outlines.
struct WrappedPathBuilder(sk::PathBuilder);