Skip to main content

skrifa/outline/autohint/
mod.rs

1//! Runtime autohinting support.
2
3mod hint;
4mod instance;
5mod metrics;
6mod outline;
7mod recorder;
8mod shape;
9mod style;
10mod topo;
11
12pub use instance::GlyphStyles;
13pub(crate) use instance::Instance;
14pub use metrics::{
15    BlueZones, Scale, ScaleFlags, ScaledAxisMetrics, ScaledBlue, ScaledStyleMetrics, ScaledWidth,
16    UnscaledAxisMetrics, UnscaledBlue, UnscaledStyleMetrics, WidthMetrics,
17};
18pub use outline::Direction;
19pub use recorder::{EdgeAction, EdgeHint, HintAction, PointAction, PointHint};
20pub use style::{GlyphStyle, ScriptClass, ScriptGroup, StyleClass};
21pub use style::{SCRIPT_CLASSES, STYLE_CLASSES};
22pub use topo::{Axis, BlueProvenance, Dimension, Edge, Segment, TopoFlags};
23
24use crate::outline::{DrawError, Target};
25use crate::{FontRef, MetadataProvider};
26use alloc::vec::Vec;
27use raw::types::{F2Dot14, GlyphId};
28
29/// Controls quirks for the different autohinters.
30#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
31enum QuirksMode {
32    /// Just in time hinter; matches FreeType.
33    #[default]
34    Jit,
35    /// Ahead of time hinter; matches ttfautohint.
36    Aot,
37}
38
39/// Plan for hinting an outline.
40#[derive(Clone, Debug, Default)]
41pub struct HintPlan {
42    hints: Vec<HintAction>,
43    axes: [Option<Axis>; 2],
44}
45
46impl HintPlan {
47    /// Creates a new hint plan.
48    pub fn new(
49        font: &FontRef,
50        coords: &[F2Dot14],
51        ppem: f32,
52        target: Target,
53        glyph_id: GlyphId,
54        glyph_style: GlyphStyle,
55    ) -> Result<Self, DrawError> {
56        let style_class = glyph_style
57            .style_class()
58            .ok_or(DrawError::GlyphNotFound(glyph_id))?;
59        let outline_glyph = font
60            .outline_glyphs()
61            .get(glyph_id)
62            .ok_or(DrawError::GlyphNotFound(glyph_id))?;
63
64        let shaper_mode = if cfg!(feature = "autohint_shaping") {
65            shape::ShaperMode::BestEffort
66        } else {
67            shape::ShaperMode::Nominal
68        };
69        let shaper = shape::Shaper::new(font, shaper_mode);
70        let metrics =
71            metrics::compute_unscaled_style_metrics(&shaper, coords, style_class, QuirksMode::Aot);
72
73        let mut outline = outline::Outline::default();
74        outline.fill(&outline_glyph, coords, QuirksMode::Aot)?;
75
76        let scale = metrics::Scale::new(
77            ppem,
78            outline_glyph.units_per_em() as i32,
79            font.attributes().style,
80            target,
81            metrics.style_class().script.group,
82        );
83
84        let mut recorder = recorder::HintsRecorder::default();
85        let hinted_plan = hint::hint_outline_with_recorder(
86            &mut outline,
87            &metrics,
88            &scale,
89            Some(glyph_style),
90            &mut recorder,
91        );
92
93        Ok(Self {
94            hints: recorder.actions,
95            axes: hinted_plan.axes,
96        })
97    }
98
99    /// Returns the collection of hinting actions.
100    pub fn actions(&self) -> &[HintAction] {
101        &self.hints
102    }
103
104    /// Returns the topological analysis of the horizontal axis.
105    pub fn horizontal_axis(&self) -> Option<&Axis> {
106        self.axes[Dimension::Horizontal].as_ref()
107    }
108
109    /// Returns the topological analysis of the vertical axis.
110    pub fn vertical_axis(&self) -> Option<&Axis> {
111        self.axes[Dimension::Vertical].as_ref()
112    }
113}
114
115/// All constants are defined based on a UPEM of 2048.
116///
117/// See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/autofit/aflatin.h#L34>
118fn derived_constant(units_per_em: i32, value: i32) -> i32 {
119    value * units_per_em / 2048
120}