skrifa/outline/autohint/hint/
mod.rs

1//! Entry point to hinting algorithm.
2
3mod edges;
4mod outline;
5
6use super::{
7    metrics::{scale_style_metrics, Scale, UnscaledStyleMetrics},
8    outline::Outline,
9    style::{GlyphStyle, ScriptGroup},
10    topo::{self, Axis},
11};
12
13/// Captures adjusted horizontal scale and outer edge positions to be used
14/// for horizontal metrics adjustments.
15#[derive(Copy, Clone, PartialEq, Default, Debug)]
16pub(crate) struct EdgeMetrics {
17    pub left_opos: i32,
18    pub left_pos: i32,
19    pub right_opos: i32,
20    pub right_pos: i32,
21}
22
23#[derive(Copy, Clone, PartialEq, Default, Debug)]
24pub(crate) struct HintedMetrics {
25    pub x_scale: i32,
26    /// This will be `None` when we've identified fewer than two horizontal
27    /// edges in the outline. This will occur for empty outlines and outlines
28    /// that are degenerate (all x coordinates have the same value, within
29    /// a threshold). In these cases, horizontal metrics will not be adjusted.
30    pub edge_metrics: Option<EdgeMetrics>,
31}
32
33/// Applies the complete hinting process to a latin outline.
34///
35/// See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/autofit/aflatin.c#L3554>
36pub(crate) fn hint_outline(
37    outline: &mut Outline,
38    metrics: &UnscaledStyleMetrics,
39    scale: &Scale,
40    glyph_style: Option<GlyphStyle>,
41) -> HintedMetrics {
42    let scaled_metrics = scale_style_metrics(metrics, *scale);
43    let scale = &scaled_metrics.scale;
44    let mut axis = Axis::default();
45    let hint_top_to_bottom = metrics.style_class().script.hint_top_to_bottom;
46    outline.scale(&scaled_metrics.scale);
47    let mut hinted_metrics = HintedMetrics {
48        x_scale: scale.x_scale,
49        ..Default::default()
50    };
51    let group = metrics.style_class().script.group;
52    // For default script group, we don't proceed with hinting if we're
53    // missing alignment zones. FreeType swaps in a "dummy" hinter here
54    // See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/autofit/afglobal.c#L475>
55    if group == ScriptGroup::Default && scaled_metrics.axes[1].blues.is_empty() {
56        return hinted_metrics;
57    }
58    for dim in 0..2 {
59        if (dim == Axis::HORIZONTAL && scale.flags & Scale::NO_HORIZONTAL != 0)
60            || (dim == Axis::VERTICAL && scale.flags & Scale::NO_VERTICAL != 0)
61        {
62            continue;
63        }
64        axis.reset(dim, outline.orientation);
65        topo::compute_segments(outline, &mut axis, group);
66        topo::link_segments(
67            outline,
68            &mut axis,
69            scaled_metrics.axes[dim].scale,
70            group,
71            metrics.axes[dim].max_width(),
72        );
73        topo::compute_edges(
74            &mut axis,
75            &scaled_metrics.axes[dim],
76            hint_top_to_bottom,
77            scaled_metrics.scale.y_scale,
78            group,
79        );
80        if dim == Axis::VERTICAL {
81            if group != ScriptGroup::Default
82                || glyph_style
83                    .map(|style| !style.is_non_base())
84                    .unwrap_or(true)
85            {
86                topo::compute_blue_edges(
87                    &mut axis,
88                    scale,
89                    &metrics.axes[dim].blues,
90                    &scaled_metrics.axes[dim].blues,
91                    group,
92                );
93            }
94        } else {
95            hinted_metrics.x_scale = scaled_metrics.axes[0].scale;
96        }
97        edges::hint_edges(
98            &mut axis,
99            &scaled_metrics.axes[dim],
100            group,
101            scale,
102            hint_top_to_bottom,
103        );
104        outline::align_edge_points(outline, &axis, group, scale);
105        outline::align_strong_points(outline, &mut axis);
106        outline::align_weak_points(outline, dim);
107        if dim == 0 && axis.edges.len() > 1 {
108            let left = axis.edges.first().unwrap();
109            let right = axis.edges.last().unwrap();
110            hinted_metrics.edge_metrics = Some(EdgeMetrics {
111                left_pos: left.pos,
112                left_opos: left.opos,
113                right_pos: right.pos,
114                right_opos: right.opos,
115            });
116        }
117    }
118    hinted_metrics
119}