1use super::super::{
10 metrics::{
11 fixed_div, fixed_mul, fixed_mul_div, pix_round, BlueZones, Scale, ScaledAxisMetrics,
12 ScaledBlue, ScaledStyleMetrics, ScaledWidth, UnscaledAxisMetrics, UnscaledBlue,
13 UnscaledStyleMetrics, WidthMetrics,
14 },
15 shape::{Shaper, ShaperMode},
16 style::{ScriptGroup, StyleClass},
17 topo::Dimension,
18 QuirksMode,
19};
20use crate::{instance::NormalizedCoord, prelude::Size, FontRef, MetadataProvider};
21use raw::types::F2Dot14;
22
23impl UnscaledStyleMetrics {
24 pub fn new(font: &FontRef, coords: &[NormalizedCoord], style: &StyleClass) -> Self {
27 let shaper_mode = if cfg!(feature = "autohint_shaping") {
28 ShaperMode::BestEffort
29 } else {
30 ShaperMode::Nominal
31 };
32 let shaper = Shaper::new(font, shaper_mode);
33 compute_unscaled_style_metrics(&shaper, coords, style, QuirksMode::Aot)
34 }
35
36 pub fn scale(&self, scale: Scale) -> ScaledStyleMetrics {
38 scale_style_metrics(self, scale, QuirksMode::Aot)
39 }
40}
41
42pub(crate) fn compute_unscaled_style_metrics(
46 shaper: &Shaper,
47 coords: &[F2Dot14],
48 style: &StyleClass,
49 quirks: QuirksMode,
50) -> UnscaledStyleMetrics {
51 let charmap = shaper.charmap();
52 if charmap.is_symbol() {
56 return UnscaledStyleMetrics {
57 class_ix: style.index as u16,
58 axes: [
59 UnscaledAxisMetrics {
60 dim: Dimension::Horizontal,
61 ..Default::default()
62 },
63 UnscaledAxisMetrics {
64 dim: Dimension::Vertical,
65 ..Default::default()
66 },
67 ],
68 ..Default::default()
69 };
70 }
71 let [hwidths, vwidths] = super::widths::compute_widths(shaper, coords, style, quirks);
72 let [hblues, vblues] = super::blues::compute_unscaled_blues(shaper, coords, style);
73 let glyph_metrics = shaper.font().glyph_metrics(Size::unscaled(), coords);
74 let mut digit_advance = None;
75 let mut digits_have_same_width = true;
76 for ch in '0'..='9' {
77 if let Some(advance) = charmap
78 .map(ch)
79 .and_then(|gid| glyph_metrics.advance_width(gid))
80 {
81 if digit_advance.is_some() && digit_advance != Some(advance) {
82 digits_have_same_width = false;
83 break;
84 }
85 digit_advance = Some(advance);
86 }
87 }
88 UnscaledStyleMetrics {
89 class_ix: style.index as u16,
90 digits_have_same_width,
91 axes: [
92 UnscaledAxisMetrics {
93 dim: Dimension::Horizontal,
94 blues: hblues,
95 width_metrics: hwidths.0,
96 widths: hwidths.1,
97 },
98 UnscaledAxisMetrics {
99 dim: Dimension::Vertical,
100 blues: vblues,
101 width_metrics: vwidths.0,
102 widths: vwidths.1,
103 },
104 ],
105 }
106}
107
108pub(crate) fn scale_style_metrics(
112 unscaled_metrics: &UnscaledStyleMetrics,
113 mut scale: Scale,
114 quirks: QuirksMode,
115) -> ScaledStyleMetrics {
116 let scale_axis_fn = if unscaled_metrics.style_class().script.group == ScriptGroup::Default {
117 scale_default_axis_metrics
118 } else {
119 scale_cjk_axis_metrics
120 };
121 let mut scale_axis = |axis: &UnscaledAxisMetrics| {
122 scale_axis_fn(
123 axis.dim,
124 &axis.widths,
125 axis.width_metrics,
126 &axis.blues,
127 &mut scale,
128 quirks,
129 )
130 };
131 let axes = [
132 scale_axis(&unscaled_metrics.axes[0]),
133 scale_axis(&unscaled_metrics.axes[1]),
134 ];
135 ScaledStyleMetrics { scale, axes }
136}
137
138fn scale_default_axis_metrics(
142 dim: Dimension,
143 widths: &[i32],
144 width_metrics: WidthMetrics,
145 blues: &[UnscaledBlue],
146 scale: &mut Scale,
147 quirks: QuirksMode,
148) -> ScaledAxisMetrics {
149 let mut axis = ScaledAxisMetrics {
150 dim,
151 ..Default::default()
152 };
153 if dim == Dimension::Horizontal {
154 axis.scale = scale.x_scale;
155 axis.delta = scale.x_delta;
156 } else {
157 axis.scale = scale.y_scale;
158 axis.delta = scale.y_delta;
159 };
160 if let Some(blue_ix) = blues
162 .iter()
163 .position(|blue| blue.zones.contains(BlueZones::ADJUSTMENT))
164 {
165 let unscaled_blue = &blues[blue_ix];
166 let scaled = fixed_mul(axis.scale, unscaled_blue.overshoot);
167 let fitted = (scaled + 40) & !63;
168 if scaled != fitted && dim == Dimension::Vertical {
169 let new_scale = fixed_mul_div(axis.scale, fitted, scaled);
170 let mut max_height = scale.units_per_em;
172 for blue in blues {
173 max_height = max_height.max(blue.ascender).max(-blue.descender);
174 }
175 let mut dist = fixed_mul(max_height, new_scale - axis.scale).abs();
176 dist &= !127;
177 if dist == 0 {
178 axis.scale = new_scale;
179 scale.y_scale = new_scale;
180 }
181 }
182 }
183 axis.width_metrics = width_metrics;
186 if widths.is_empty() && quirks == QuirksMode::Aot {
187 let scaled = fixed_mul(axis.scale, axis.width_metrics.standard_width);
188 axis.widths.push(ScaledWidth {
189 scaled,
190 fitted: scaled,
191 });
192 } else {
193 for unscaled_width in widths {
194 let scaled = fixed_mul(axis.scale, *unscaled_width);
195 axis.widths.push(ScaledWidth {
196 scaled,
197 fitted: scaled,
198 });
199 }
200 }
201 axis.width_metrics.is_extra_light =
204 fixed_mul(axis.width_metrics.standard_width, axis.scale) < (32 + 8);
205 if dim == Dimension::Vertical {
206 for unscaled_blue in blues {
208 let scaled_position = fixed_mul(axis.scale, unscaled_blue.position) + axis.delta;
209 let scaled_overshoot = fixed_mul(axis.scale, unscaled_blue.overshoot) + axis.delta;
210 let mut blue = ScaledBlue {
211 position: ScaledWidth {
212 scaled: scaled_position,
213 fitted: scaled_position,
214 },
215 overshoot: ScaledWidth {
216 scaled: scaled_overshoot,
217 fitted: scaled_overshoot,
218 },
219 zones: unscaled_blue.zones,
220 is_active: false,
221 };
222 let dist = fixed_mul(unscaled_blue.position - unscaled_blue.overshoot, axis.scale);
224 if (-48..=48).contains(&dist) {
225 let mut delta = dist.abs();
226 if delta < 32 {
227 delta = 0;
228 } else if delta < 48 {
229 delta = 32;
230 } else {
231 delta = 64;
232 }
233 if dist < 0 {
234 delta = -delta;
235 }
236 blue.position.fitted = pix_round(blue.position.scaled);
237 blue.overshoot.fitted = blue.position.fitted - delta;
238 blue.is_active = true;
239 }
240 axis.blues.push(blue);
241 }
242 for blue_ix in 0..axis.blues.len() {
245 let blue = axis.blues[blue_ix];
246 if !blue.zones.is_sub_top() || !blue.is_active {
247 continue;
248 }
249 for blue2 in &axis.blues {
250 if blue2.zones.is_sub_top() || !blue2.is_active {
251 continue;
252 }
253 if blue2.position.fitted <= blue.overshoot.fitted
254 && blue2.overshoot.fitted >= blue.position.fitted
255 {
256 axis.blues[blue_ix].is_active = false;
257 break;
258 }
259 }
260 }
261 }
262 axis
263}
264
265fn scale_cjk_axis_metrics(
269 dim: Dimension,
270 widths: &[i32],
271 width_metrics: WidthMetrics,
272 blues: &[UnscaledBlue],
273 scale: &mut Scale,
274 _quirks: QuirksMode,
275) -> ScaledAxisMetrics {
276 let mut axis = ScaledAxisMetrics {
277 dim,
278 ..Default::default()
279 };
280 axis.dim = dim;
281 if dim == Dimension::Horizontal {
282 axis.scale = scale.x_scale;
283 axis.delta = scale.x_delta;
284 } else {
285 axis.scale = scale.y_scale;
286 axis.delta = scale.y_delta;
287 };
288 let scale = axis.scale;
289 for unscaled_blue in blues {
291 let position = fixed_mul(unscaled_blue.position, scale) + axis.delta;
292 let overshoot = fixed_mul(unscaled_blue.overshoot, scale) + axis.delta;
293 let mut blue = ScaledBlue {
294 position: ScaledWidth {
295 scaled: position,
296 fitted: position,
297 },
298 overshoot: ScaledWidth {
299 scaled: overshoot,
300 fitted: overshoot,
301 },
302 zones: unscaled_blue.zones,
303 is_active: false,
304 };
305 let dist = fixed_mul(unscaled_blue.position - unscaled_blue.overshoot, scale);
307 if (-48..=48).contains(&dist) {
308 blue.position.fitted = pix_round(blue.position.scaled);
309 let delta1 = fixed_div(blue.position.fitted, scale) - unscaled_blue.overshoot;
311 let mut delta2 = fixed_mul(delta1.abs(), scale);
312 if delta2 < 32 {
313 delta2 = 0;
314 } else {
315 delta2 = pix_round(delta2);
316 }
317 if delta1 < 0 {
318 delta2 = -delta2;
319 }
320 blue.overshoot.fitted = blue.position.fitted - delta2;
321 blue.is_active = true;
322 }
323 axis.blues.push(blue);
324 }
325 for _ in 0..widths.len() {
329 axis.widths.push(ScaledWidth::default());
330 }
331 axis.width_metrics = width_metrics;
332 axis
333}
334
335#[cfg(test)]
336mod tests {
337 use super::{
338 super::super::{shape::ShaperMode, style},
339 *,
340 };
341 use crate::attribute::Style;
342 use raw::{FontRef, TableProvider};
343
344 #[test]
345 fn scaled_metrics_default() {
346 let scaled_metrics = make_scaled_metrics(
349 font_test_data::NOTOSERIFHEBREW_AUTOHINT_METRICS,
350 StyleClass::HEBR,
351 );
352 assert_eq!(scaled_metrics.scale.x_scale, 67109);
354 assert_eq!(scaled_metrics.scale.y_scale, 67109);
355 assert_eq!(scaled_metrics.scale.x_delta, 0);
356 assert_eq!(scaled_metrics.scale.y_delta, 0);
357 let h_axis = &scaled_metrics.axes[0];
359 let expected_h_widths = [55];
360 check_axis(h_axis, &expected_h_widths, &[]);
362 assert!(!h_axis.width_metrics.is_extra_light);
364 let v_axis = &scaled_metrics.axes[1];
366 let expected_v_widths = [22, 112];
367 #[rustfmt::skip]
369 let expected_v_blues = [
370 ScaledBlue::from(((606, 576), (606, 576), BlueZones::TOP, true)),
372 ScaledBlue::from(((0, 0), (-9, 0), BlueZones::default(), true)),
373 ScaledBlue::from(((-246, -256), (-246, -256), BlueZones::default(), true)),
374 ];
375 check_axis(v_axis, &expected_v_widths, &expected_v_blues);
376 assert!(v_axis.width_metrics.is_extra_light);
378 }
379
380 #[test]
381 fn cjk_scaled_metrics() {
382 let scaled_metrics = make_scaled_metrics(
385 font_test_data::NOTOSERIFTC_AUTOHINT_METRICS,
386 StyleClass::HANI,
387 );
388 assert_eq!(scaled_metrics.scale.x_scale, 67109);
390 assert_eq!(scaled_metrics.scale.y_scale, 67109);
391 assert_eq!(scaled_metrics.scale.x_delta, 0);
392 assert_eq!(scaled_metrics.scale.y_delta, 0);
393 let h_axis = &scaled_metrics.axes[0];
395 let expected_h_widths = [0];
396 check_axis(h_axis, &expected_h_widths, &[]);
397 assert!(!h_axis.width_metrics.is_extra_light);
399 let v_axis = &scaled_metrics.axes[1];
401 let expected_v_widths = [0];
402 #[rustfmt::skip]
404 let expected_v_blues = [
405 ScaledBlue::from(((857, 832), (844, 832), BlueZones::TOP, true)),
407 ScaledBlue::from(((-80, -64), (-68, -64), BlueZones::default(), true)),
408 ];
409 check_axis(v_axis, &expected_v_widths, &expected_v_blues);
411 assert!(!v_axis.width_metrics.is_extra_light);
413 }
414
415 fn make_scaled_metrics(font_data: &[u8], style_class: usize) -> ScaledStyleMetrics {
416 let font = FontRef::new(font_data).unwrap();
417 let class = &style::STYLE_CLASSES[style_class];
418 let shaper = Shaper::new(&font, ShaperMode::Nominal);
419 let unscaled_metrics = compute_unscaled_style_metrics(
420 &shaper,
421 Default::default(),
422 class,
423 QuirksMode::default(),
424 );
425 let scale = Scale::new(
426 16.0,
427 font.head().unwrap().units_per_em() as i32,
428 Style::Normal,
429 Default::default(),
430 class.script.group,
431 );
432 scale_style_metrics(&unscaled_metrics, scale, QuirksMode::default())
433 }
434
435 fn check_axis(
436 axis: &ScaledAxisMetrics,
437 expected_widths: &[i32],
438 expected_blues: &[ScaledBlue],
439 ) {
440 let widths = axis
441 .widths
442 .iter()
443 .map(|width| width.scaled)
444 .collect::<Vec<_>>();
445 assert_eq!(widths, expected_widths);
446 assert_eq!(axis.blues.as_slice(), expected_blues);
447 }
448
449 impl From<(i32, i32)> for ScaledWidth {
450 fn from(value: (i32, i32)) -> Self {
451 Self {
452 scaled: value.0,
453 fitted: value.1,
454 }
455 }
456 }
457
458 impl From<((i32, i32), (i32, i32), BlueZones, bool)> for ScaledBlue {
459 fn from(value: ((i32, i32), (i32, i32), BlueZones, bool)) -> Self {
460 Self {
461 position: value.0.into(),
462 overshoot: value.1.into(),
463 zones: value.2,
464 is_active: value.3,
465 }
466 }
467 }
468}