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}