Another project
1use bone_text::{
2 FontFace, FontWeight, ShapeRequest, ShapedText, Shaper, TessellatedOutline, append_outline,
3 tessellate_path,
4};
5use lyon_tessellation::{FillTessellator, path::Path as LyonPath};
6use swash::{
7 FontRef,
8 scale::{ScaleContext, outline::Outline},
9 zeno::Point as ZenoPoint,
10};
11
12pub(crate) fn shape_line(
13 text: &str,
14 size_px: f32,
15 face: FontFace,
16 shaper: &mut Shaper,
17) -> ShapedText {
18 shaper.shape(
19 text,
20 ShapeRequest {
21 face,
22 size_px,
23 weight: FontWeight::Regular,
24 line_height_px: 0.0,
25 letter_spacing_px: 0.0,
26 max_width: None,
27 },
28 )
29}
30
31pub(crate) fn tessellate_at(
32 layout: &ShapedText,
33 font: &FontRef<'_>,
34 scale_ctx: &mut ScaleContext,
35 fill: &mut FillTessellator,
36 origin: ZenoPoint,
37) -> TessellatedOutline {
38 let path = build_path_at(layout, scale_ctx, font, origin);
39 tessellate_path(&path, fill).unwrap_or_else(|e| {
40 tracing::warn!(error = %e, "text tessellation failed");
41 TessellatedOutline::default()
42 })
43}
44
45fn build_path_at(
46 layout: &ShapedText,
47 scale_ctx: &mut ScaleContext,
48 font: &FontRef<'_>,
49 origin: ZenoPoint,
50) -> LyonPath {
51 let mut scaler = scale_ctx.builder(*font).size(layout.font_size_px).build();
52 layout
53 .lines
54 .iter()
55 .flat_map(|line| line.runs.iter())
56 .flat_map(|run| {
57 let run_origin = run.origin_x_px;
58 run.glyphs.iter().map(move |g| (run_origin + g.x_px, g.id))
59 })
60 .fold(LyonPath::svg_builder(), |mut builder, (x, id)| {
61 let Ok(glyph_id_u16) = u16::try_from(id.raw()) else {
62 return builder;
63 };
64 let mut outline = Outline::new();
65 if scaler.scale_outline_into(glyph_id_u16, &mut outline) {
66 append_outline(
67 &mut builder,
68 &outline,
69 ZenoPoint::new(origin.x + x, origin.y),
70 );
71 }
72 builder
73 })
74 .build()
75}