Skip to main content

skrifa/scaler/
hinting.rs

1//! Hinting configuration for a [`ScalerFont`](super::ScalerFont).
2
3use crate::outline::{self, SmoothMode};
4
5/// The rasterization target for a hinted outline.
6#[derive(Copy, Clone, PartialEq, Eq, Hash, Default, Debug)]
7pub enum RasterTarget {
8    /// One bit per pixel with no antialiasing. Hinting is strong and rounds
9    /// in both directions.
10    Mono,
11    /// Grayscale antialiasing.
12    #[default]
13    Normal,
14    /// Grayscale antialiasing with lighter hinting. There is less adjustment
15    /// in the horizontal direction, and the automatic hinter moves points
16    /// only vertically.
17    Light,
18    /// Subpixel rendering on a display whose subpixels run horizontally.
19    Lcd,
20    /// Subpixel rendering on a display whose subpixels run vertically.
21    VerticalLcd,
22}
23
24/// The hinting engine a scaler runs.
25///
26/// [`HintOptions`] asks for an engine, and the font decides which one runs:
27/// [`HintOptions::auto_fallback`] runs the font's own hints where they are
28/// worth running and the automatic hinter otherwise, and a font that needs
29/// its instructions for correct contours always runs them.
30#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
31pub enum HintEngine {
32    /// The font's TrueType instructions.
33    TrueType,
34    /// The font's PostScript hints.
35    PostScript,
36    /// The automatic hinter.
37    Auto,
38}
39
40/// The hinting engine requested by a configuration.
41#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
42pub(super) enum EngineRequest {
43    /// The font's own instructions.
44    Interpreter,
45    /// The interpreter if the font has instructions, and the automatic hinter
46    /// otherwise.
47    AutoFallback,
48    /// The automatic hinter.
49    Auto,
50}
51
52/// Hinting configuration for a [`ScalerFont`](super::ScalerFont).
53///
54/// Create one with a constructor and adjust it with the `with_*` methods:
55///
56/// ```
57/// use skrifa::{HintOptions, RasterTarget};
58///
59/// let hinting = HintOptions::interpreter()
60///     .with_target(RasterTarget::Lcd)
61///     .with_classic_cleartype(true);
62/// assert_eq!(hinting.target(), RasterTarget::Lcd);
63/// ```
64#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
65pub struct HintOptions {
66    engine: EngineRequest,
67    target: RasterTarget,
68    classic_cleartype: bool,
69    symmetric_rendering: bool,
70    preserve_linear_metrics: bool,
71    pedantic: bool,
72}
73
74impl Default for HintOptions {
75    fn default() -> Self {
76        Self::auto_fallback()
77    }
78}
79
80impl HintOptions {
81    /// Creates a configuration that chooses the engine based on the font, with
82    /// a grayscale target.
83    ///
84    /// Uses the font's own instructions when it has any, and the automatic
85    /// hinter otherwise. A TrueType font with no glyph, font or control value
86    /// program is autohinted.
87    pub fn auto_fallback() -> Self {
88        Self::with_engine(EngineRequest::AutoFallback)
89    }
90
91    /// Creates a configuration that uses the instructions embedded in the
92    /// font.
93    ///
94    /// This runs the TrueType interpreter or the PostScript hinter, depending
95    /// on which kind of instructions the font contains.
96    pub fn interpreter() -> Self {
97        Self::with_engine(EngineRequest::Interpreter)
98    }
99
100    /// Creates a configuration that uses the automatic hinter, which ignores
101    /// the font's instructions.
102    ///
103    /// A TrueType font that needs its instructions to draw correct contours
104    /// always runs the interpreter.
105    ///
106    /// The data the hinter derives from a font is stored with the font. All
107    /// scalers for that font share it at every size.
108    pub fn auto() -> Self {
109        Self::with_engine(EngineRequest::Auto)
110    }
111
112    /// Creates the defaults for a grayscale target that uses `engine`.
113    fn with_engine(engine: EngineRequest) -> Self {
114        Self {
115            engine,
116            target: RasterTarget::Normal,
117            classic_cleartype: false,
118            symmetric_rendering: true,
119            preserve_linear_metrics: false,
120            pedantic: false,
121        }
122    }
123
124    /// Sets the rasterization target.
125    pub fn with_target(self, target: RasterTarget) -> Self {
126        Self { target, ..self }
127    }
128
129    /// Sets whether the TrueType interpreter uses the classic ClearType
130    /// behavior.
131    ///
132    /// By default, a version 1 `gasp` table selects symmetric smoothing and
133    /// grid fitting per size, and glyphs that opt out of backward
134    /// compatibility with `INSTCTRL` selector 3 keep fractional horizontal
135    /// positions. With the classic behavior, every glyph is grid fitted, the
136    /// symmetric rendering bit of `GETINFO` follows
137    /// [`symmetric_rendering`](Self::symmetric_rendering) alone, and
138    /// horizontal moves round to whole pixels. This is the behavior of
139    /// [`HintingInstance`](crate::outline::HintingInstance). Defaults to off.
140    ///
141    /// See <https://github.com/googlefonts/fontations/issues/2112>.
142    pub fn with_classic_cleartype(self, classic_cleartype: bool) -> Self {
143        Self {
144            classic_cleartype,
145            ..self
146        }
147    }
148
149    /// Sets whether TrueType bytecode may assume vertical supersampling
150    /// during rasterization.
151    ///
152    /// This is the symmetric rendering bit of the `GETINFO` instruction.
153    /// When it is set, ClearType fonts often generate wider horizontal stems
154    /// that look blurry with an analytic area rasterizer. It is set by
155    /// default. Unless [classic ClearType](Self::with_classic_cleartype) is
156    /// on, the font's `gasp` table can also clear it per size. Only the TrueType interpreter reads it.
157    /// See <https://github.com/googlefonts/fontations/issues/1080>.
158    pub fn with_symmetric_rendering(self, symmetric_rendering: bool) -> Self {
159        Self {
160            symmetric_rendering,
161            ..self
162        }
163    }
164
165    /// Sets whether hinting leaves the outline unchanged horizontally.
166    ///
167    /// Hinting then moves points only vertically, so a glyph keeps its
168    /// unhinted horizontal shape and spacing. The advance a draw reports is
169    /// still rounded to a whole pixel, as for any hinted draw. The TrueType
170    /// interpreter runs in backward compatibility mode, and the automatic
171    /// hinter works as it does for a [`RasterTarget::Light`] target. Defaults
172    /// to off.
173    pub fn with_preserve_linear_metrics(self, preserve_linear_metrics: bool) -> Self {
174        Self {
175            preserve_linear_metrics,
176            ..self
177        }
178    }
179
180    /// Sets whether an error in a glyph's instructions fails the draw.
181    ///
182    /// When off, the draw keeps the outline as the instructions left it.
183    /// Defaults to off.
184    pub fn with_pedantic(self, pedantic: bool) -> Self {
185        Self { pedantic, ..self }
186    }
187
188    /// Returns the hinting engine.
189    pub(super) fn engine(&self) -> EngineRequest {
190        self.engine
191    }
192
193    /// Returns the rasterization target.
194    pub fn target(&self) -> RasterTarget {
195        self.target
196    }
197
198    /// Returns whether the TrueType interpreter uses the classic ClearType
199    /// behavior.
200    pub fn classic_cleartype(&self) -> bool {
201        self.classic_cleartype
202    }
203
204    /// Returns whether the `GETINFO` symmetric rendering bit may be set.
205    pub fn symmetric_rendering(&self) -> bool {
206        self.symmetric_rendering
207    }
208
209    /// Returns whether hinting preserves the font's advance widths.
210    pub fn preserve_linear_metrics(&self) -> bool {
211        self.preserve_linear_metrics
212    }
213
214    /// Returns whether an instruction error fails the draw.
215    pub fn pedantic(&self) -> bool {
216        self.pedantic
217    }
218
219    /// Returns the target in the form the hinting engines use.
220    pub(super) fn outline_target(&self) -> outline::Target {
221        let mode = match self.target {
222            RasterTarget::Mono => return outline::Target::Mono,
223            RasterTarget::Normal => SmoothMode::Normal,
224            RasterTarget::Light => SmoothMode::Light,
225            RasterTarget::Lcd => SmoothMode::Lcd,
226            RasterTarget::VerticalLcd => SmoothMode::VerticalLcd,
227        };
228        outline::Target::Smooth {
229            mode,
230            symmetric_rendering: self.symmetric_rendering,
231            preserve_linear_metrics: self.preserve_linear_metrics,
232        }
233    }
234}