1use super::{
4 Decoration, GlyphExtents, GlyphMetrics, LineBox, LineExtents, Metrics, ScriptMetrics,
5 StyleMetrics,
6};
7use crate::model::glyph::GlyphSourceOrder;
8use types::{BoundingBox, F26Dot6, F48Dot16, Fixed, GlyphId};
9
10pub trait Scale {
24 type Value: Copy;
26
27 fn add(a: Self::Value, b: Self::Value) -> Self::Value;
33
34 fn sub(a: Self::Value, b: Self::Value) -> Self::Value;
36
37 fn half(value: Self::Value) -> Self::Value;
39
40 fn scale_x(&self, value: F48Dot16) -> Self::Value;
42
43 fn scale_y(&self, value: F48Dot16) -> Self::Value;
49
50 fn scale_glyph_extents(&self, extents: GlyphExtents<F48Dot16>) -> GlyphExtents<Self::Value>;
56
57 fn scale_rect(&self, bounds: BoundingBox<F48Dot16>) -> BoundingBox<Self::Value>;
63
64 fn ppem(&self) -> Option<f32> {
69 None
70 }
71}
72
73#[derive(Copy, Clone, Debug)]
80pub struct Scale26Dot6 {
81 x: Fixed,
82 y: Fixed,
83 ppem: f32,
84}
85
86impl Scale26Dot6 {
87 pub fn new(x_ppem: f32, y_ppem: f32, units_per_em: u16) -> Self {
89 Self {
90 x: Self::factor(x_ppem, units_per_em),
91 y: Self::factor(y_ppem, units_per_em),
92 ppem: x_ppem.max(y_ppem),
93 }
94 }
95
96 pub fn from_ppem(ppem: f32, units_per_em: u16) -> Self {
98 Self::new(ppem, ppem, units_per_em)
99 }
100
101 fn factor(ppem: f32, units_per_em: u16) -> Fixed {
106 Fixed::from_bits((ppem * 64.0) as i32) / Fixed::from_bits(units_per_em.max(1) as i32)
107 }
108
109 fn scale(value: F48Dot16, factor: Fixed) -> F26Dot6 {
110 F26Dot6::from_bits((Fixed::from_bits(value.to_i32()) * factor).to_bits())
111 }
112}
113
114impl Scale for Scale26Dot6 {
115 type Value = F26Dot6;
116
117 fn add(a: F26Dot6, b: F26Dot6) -> F26Dot6 {
118 a.saturating_add(b)
119 }
120
121 fn sub(a: F26Dot6, b: F26Dot6) -> F26Dot6 {
122 a.saturating_sub(b)
123 }
124
125 fn half(value: F26Dot6) -> F26Dot6 {
126 F26Dot6::from_bits(value.to_bits() >> 1)
127 }
128
129 fn scale_x(&self, value: F48Dot16) -> F26Dot6 {
130 Self::scale(value, self.x)
131 }
132
133 fn scale_y(&self, value: F48Dot16) -> F26Dot6 {
134 Self::scale(value, self.y)
135 }
136
137 fn scale_glyph_extents(&self, e: GlyphExtents<F48Dot16>) -> GlyphExtents<F26Dot6> {
138 if e.width <= F48Dot16::ZERO || e.height <= F48Dot16::ZERO {
139 return GlyphExtents::default();
140 }
141 let round = |value: F48Dot16| {
143 let bits = value.to_bits();
144 F48Dot16::from_bits(bits.wrapping_add(0x8000 - i64::from(bits < 0)) & !0xffff)
145 };
146 let left = round(e.x_bearing);
147 let top = round(e.y_bearing);
148 let right = round(e.x_bearing + e.width);
149 let bottom = round(e.y_bearing - e.height);
150 GlyphExtents {
151 x_bearing: self.scale_x(left),
152 y_bearing: self.scale_y(top),
153 width: self.scale_x(right - left),
154 height: self.scale_y(top - bottom),
155 }
156 }
157
158 fn scale_rect(&self, b: BoundingBox<F48Dot16>) -> BoundingBox<F26Dot6> {
159 BoundingBox {
160 x_min: self.scale_x(b.x_min),
161 y_min: self.scale_y(b.y_min),
162 x_max: self.scale_x(b.x_max),
163 y_max: self.scale_y(b.y_max),
164 }
165 }
166
167 fn ppem(&self) -> Option<f32> {
168 Some(self.ppem)
169 }
170}
171
172#[derive(Copy, Clone, Debug)]
177pub struct ScaleF32 {
178 x: f32,
179 y: f32,
180 ppem: f32,
181}
182
183impl ScaleF32 {
184 pub fn new(x_ppem: f32, y_ppem: f32, units_per_em: u16) -> Self {
186 let per_unit = |ppem: f32| ppem / units_per_em.max(1) as f32;
187 Self {
188 x: per_unit(x_ppem),
189 y: per_unit(y_ppem),
190 ppem: x_ppem.max(y_ppem),
191 }
192 }
193
194 pub fn from_ppem(ppem: f32, units_per_em: u16) -> Self {
196 Self::new(ppem, ppem, units_per_em)
197 }
198}
199
200impl Scale for ScaleF32 {
201 type Value = f32;
202
203 fn add(a: f32, b: f32) -> f32 {
204 a + b
205 }
206
207 fn sub(a: f32, b: f32) -> f32 {
208 a - b
209 }
210
211 fn half(value: f32) -> f32 {
212 value * 0.5
213 }
214
215 fn scale_x(&self, value: F48Dot16) -> f32 {
216 value.to_f32() * self.x
217 }
218
219 fn scale_y(&self, value: F48Dot16) -> f32 {
220 value.to_f32() * self.y
221 }
222
223 fn scale_glyph_extents(&self, e: GlyphExtents<F48Dot16>) -> GlyphExtents<f32> {
224 GlyphExtents {
225 x_bearing: self.scale_x(e.x_bearing),
226 y_bearing: self.scale_y(e.y_bearing),
227 width: self.scale_x(e.width),
228 height: self.scale_y(e.height),
229 }
230 }
231
232 fn scale_rect(&self, b: BoundingBox<F48Dot16>) -> BoundingBox<f32> {
233 BoundingBox {
234 x_min: self.scale_x(b.x_min),
235 y_min: self.scale_y(b.y_min),
236 x_max: self.scale_x(b.x_max),
237 y_max: self.scale_y(b.y_max),
238 }
239 }
240
241 fn ppem(&self) -> Option<f32> {
242 Some(self.ppem)
243 }
244}
245
246#[derive(Clone, Copy)]
252pub struct ScaledMetrics<'a, S: Scale> {
253 metrics: &'a Metrics,
254 scale: S,
255}
256
257impl<'a, S: Scale> ScaledMetrics<'a, S> {
258 pub(crate) fn new(metrics: &'a Metrics, scale: S) -> Self {
259 Self { metrics, scale }
260 }
261
262 #[inline]
264 pub fn num_glyphs(&self) -> u32 {
265 self.metrics.num_glyphs
266 }
267
268 #[inline]
270 pub fn units_per_em(&self) -> u16 {
271 self.metrics.units_per_em
272 }
273
274 #[inline]
276 pub fn bounds(&self) -> BoundingBox<S::Value> {
277 self.scale.scale_rect(self.metrics.bounds)
278 }
279
280 #[inline]
286 pub fn h_line(&self) -> Option<LineBox<S::Value>> {
287 self.metrics.h_line().map(|line| self.scale_line_y(line))
288 }
289
290 #[inline]
292 pub fn hhea_line(&self) -> Option<LineBox<S::Value>> {
293 self.metrics.hhea_line.map(|line| self.scale_line_y(line))
294 }
295
296 #[inline]
298 pub fn typo_line(&self) -> Option<LineBox<S::Value>> {
299 self.metrics.typo_line.map(|line| self.scale_line_y(line))
300 }
301
302 #[inline]
304 pub fn use_typo_metrics(&self) -> bool {
305 self.metrics.use_typo_metrics
306 }
307
308 #[inline]
313 pub fn vhea_line(&self) -> Option<LineBox<S::Value>> {
314 self.metrics.vhea_line.map(|line| LineBox {
315 ascender: self.scale.scale_x(line.ascender),
316 descender: self.scale.scale_x(line.descender),
317 line_gap: self.scale.scale_x(line.line_gap),
318 })
319 }
320
321 #[inline]
323 pub fn x_height(&self) -> Option<S::Value> {
324 self.scaled_y(self.metrics.x_height)
325 }
326
327 #[inline]
329 pub fn cap_height(&self) -> Option<S::Value> {
330 self.scaled_y(self.metrics.cap_height)
331 }
332
333 #[inline]
335 pub fn max_h_advance(&self) -> Option<S::Value> {
336 self.metrics
337 .max_h_advance
338 .map(|value| self.scale.scale_x(value))
339 }
340
341 #[inline]
343 pub fn max_v_advance(&self) -> Option<S::Value> {
344 self.scaled_y(self.metrics.max_v_advance)
345 }
346
347 #[inline]
349 pub fn average_char_width(&self) -> Option<S::Value> {
350 self.metrics
351 .average_char_width
352 .map(|value| self.scale.scale_x(value))
353 }
354
355 #[inline]
357 pub fn win_line(&self) -> Option<LineExtents<S::Value>> {
358 self.metrics.win_line.map(|line| self.scale_line(line))
359 }
360
361 #[inline]
362 fn scale_line_y(&self, line: LineBox<F48Dot16>) -> LineBox<S::Value> {
363 LineBox {
364 ascender: self.scale.scale_y(line.ascender),
365 descender: self.scale.scale_y(line.descender),
366 line_gap: self.scale.scale_y(line.line_gap),
367 }
368 }
369
370 #[inline]
371 fn scale_line(&self, line: LineExtents<F48Dot16>) -> LineExtents<S::Value> {
372 LineExtents {
373 ascender: self.scale.scale_y(line.ascender),
374 descender: self.scale.scale_y(line.descender),
375 }
376 }
377
378 #[inline]
379 fn scaled_y(&self, value: Option<F48Dot16>) -> Option<S::Value> {
380 value.map(|value| self.scale.scale_y(value))
381 }
382}
383
384impl Metrics {
385 #[inline]
387 pub fn scaled<S: Scale>(&self, scale: S) -> ScaledMetrics<'_, S> {
388 ScaledMetrics::new(self, scale)
389 }
390
391 #[inline]
393 pub fn scaled_f32(&self, ppem: f32) -> ScaledMetrics<'_, ScaleF32> {
394 self.scaled(ScaleF32::from_ppem(ppem, self.units_per_em))
395 }
396
397 #[inline]
399 pub fn scaled_26dot6(&self, ppem: f32) -> ScaledMetrics<'_, Scale26Dot6> {
400 self.scaled(Scale26Dot6::from_ppem(ppem, self.units_per_em))
401 }
402}
403
404#[derive(Clone, Copy)]
409pub struct ScaledStyleMetrics<'a, S: Scale> {
410 metrics: &'a StyleMetrics,
411 scale: S,
412}
413
414impl<'a, S: Scale> ScaledStyleMetrics<'a, S> {
415 pub(crate) fn new(metrics: &'a StyleMetrics, scale: S) -> Self {
416 Self { metrics, scale }
417 }
418
419 pub fn subscript(&self) -> Option<ScriptMetrics<S::Value>> {
421 self.metrics
422 .subscript
423 .map(|script| self.scale_script(script))
424 }
425
426 pub fn superscript(&self) -> Option<ScriptMetrics<S::Value>> {
428 self.metrics
429 .superscript
430 .map(|script| self.scale_script(script))
431 }
432
433 pub fn underline(&self) -> Option<Decoration<S::Value>> {
435 self.metrics
436 .underline
437 .map(|decoration| self.scale_decoration(decoration))
438 }
439
440 pub fn strike_through(&self) -> Option<Decoration<S::Value>> {
442 self.metrics
443 .strike_through
444 .map(|decoration| self.scale_decoration(decoration))
445 }
446
447 pub fn italic_angle(&self) -> Option<Fixed> {
449 self.metrics.italic_angle
450 }
451
452 pub fn is_fixed_pitch(&self) -> Option<bool> {
454 self.metrics.is_fixed_pitch
455 }
456
457 fn scale_script(&self, script: ScriptMetrics) -> ScriptMetrics<S::Value> {
458 ScriptMetrics {
459 x_size: self.scale.scale_x(script.x_size),
460 y_size: self.scale.scale_y(script.y_size),
461 x_offset: self.scale.scale_x(script.x_offset),
462 y_offset: self.scale.scale_y(script.y_offset),
463 }
464 }
465
466 fn scale_decoration(&self, decoration: Decoration) -> Decoration<S::Value> {
467 Decoration {
468 position: self.scale.scale_y(decoration.position),
469 thickness: self.scale.scale_y(decoration.thickness),
470 }
471 }
472}
473
474impl StyleMetrics {
475 pub fn scaled<S: Scale>(&self, scale: S) -> ScaledStyleMetrics<'_, S> {
477 ScaledStyleMetrics::new(self, scale)
478 }
479
480 #[inline]
482 pub fn scaled_f32(&self, ppem: f32) -> ScaledStyleMetrics<'_, ScaleF32> {
483 self.scaled(ScaleF32::from_ppem(ppem, self.units_per_em))
484 }
485
486 #[inline]
489 pub fn scaled_26dot6(&self, ppem: f32) -> ScaledStyleMetrics<'_, Scale26Dot6> {
490 self.scaled(Scale26Dot6::from_ppem(ppem, self.units_per_em))
491 }
492}
493
494#[derive(Clone, Copy)]
500pub struct ScaledGlyphMetrics<'a, 'extents, S: Scale> {
501 metrics: GlyphMetrics<'a>,
502 scale: S,
503 line: Option<LineExtents<S::Value>>,
504 extents: Option<&'extents dyn Fn(GlyphId) -> Option<GlyphExtents<S::Value>>>,
505}
506
507impl<'a, 'extents, S: Scale> ScaledGlyphMetrics<'a, 'extents, S> {
508 pub(crate) fn new(metrics: GlyphMetrics<'a>, scale: S) -> Self {
509 Self {
510 metrics: metrics.with_bitmap_ppem(scale.ppem()),
511 scale,
512 line: None,
513 extents: None,
514 }
515 }
516
517 pub fn with_line_extents(self, line: Option<LineExtents<S::Value>>) -> Self {
527 Self { line, ..self }
528 }
529
530 pub fn with_extents_source_order(self, order: GlyphSourceOrder) -> Self {
534 Self {
535 metrics: self.metrics.with_extents_source_order(order),
536 ..self
537 }
538 }
539
540 pub fn with_glyph_extents<'new>(
554 self,
555 extents: Option<&'new dyn Fn(GlyphId) -> Option<GlyphExtents<S::Value>>>,
556 ) -> ScaledGlyphMetrics<'a, 'new, S> {
557 ScaledGlyphMetrics {
558 metrics: self.metrics,
559 scale: self.scale,
560 line: self.line,
561 extents,
562 }
563 }
564
565 #[inline]
567 pub fn h_advance(&self, glyph: GlyphId) -> S::Value {
568 self.scale.scale_x(self.metrics.h_advance_exact(glyph))
569 }
570
571 #[inline]
573 pub fn h_advance_batched<'o, V: 'o>(
574 &self,
575 convert: impl Fn(S::Value) -> V,
576 glyphs: impl Iterator<Item = (GlyphId, &'o mut V)>,
577 ) {
578 self.metrics
579 .h_advance_batched(|value| convert(self.scale.scale_x(value)), glyphs);
580 }
581
582 #[inline]
588 pub fn v_advance(&self, glyph: GlyphId) -> S::Value {
589 match self.line_height_fallback() {
592 Some(height) => height,
593 None => self.scale.scale_y(self.metrics.v_advance_exact(glyph)),
594 }
595 }
596
597 #[inline]
599 pub fn v_advance_batched<'o, V: 'o>(
600 &self,
601 convert: impl Fn(S::Value) -> V,
602 glyphs: impl Iterator<Item = (GlyphId, &'o mut V)>,
603 ) {
604 if let Some(height) = self.line_height_fallback() {
609 for (_, out) in glyphs {
610 *out = convert(height);
611 }
612 return;
613 }
614 self.metrics
615 .v_advance_batched(|value| convert(self.scale.scale_y(value)), glyphs);
616 }
617
618 pub fn v_origin_y(&self, glyph: GlyphId) -> S::Value {
628 let mut origin = self.scale.scale_y(F48Dot16::ZERO);
629 self.v_origin_y_batched(|value| value, core::iter::once((glyph, &mut origin)));
630 origin
631 }
632
633 pub fn v_origin_y_batched<'o, V: 'o>(
638 &self,
639 convert: impl Fn(S::Value) -> V,
640 glyphs: impl Iterator<Item = (GlyphId, &'o mut V)>,
641 ) {
642 let mut line = None;
643 self.metrics.v_origin_y_batched_with(
644 |glyph, origin| {
645 let value = match origin {
646 Some(origin) => self.scale.scale_y(origin),
647 None => self
648 .origin_against_ink(glyph, *line.get_or_insert_with(|| self.origin_line())),
649 };
650 convert(value)
651 },
652 |glyphs| {
653 let line = self.origin_line();
654 self.extents_batched(
655 |extents| convert(self.origin_from_extents(extents, line)),
656 glyphs,
657 );
658 },
659 glyphs,
660 );
661 }
662
663 #[inline]
669 pub fn extents(&self, glyph: GlyphId) -> Option<GlyphExtents<S::Value>> {
670 match self.extents {
671 Some(extents) => extents(glyph),
672 None => {
673 let mut extents = None;
674 self.metrics.extents_batched(
675 |value| value.map(|extents| self.scale.scale_glyph_extents(extents)),
676 core::iter::once((glyph, &mut extents)),
677 );
678 extents
679 }
680 }
681 }
682
683 #[inline]
685 pub fn extents_batched<'o, V: 'o>(
686 &self,
687 convert: impl Fn(Option<GlyphExtents<S::Value>>) -> V,
688 glyphs: impl Iterator<Item = (GlyphId, &'o mut V)>,
689 ) {
690 if let Some(extents) = self.extents {
693 for (glyph, out) in glyphs {
694 *out = convert(extents(glyph));
695 }
696 return;
697 }
698 self.metrics.extents_batched(
699 |extents| convert(extents.map(|extents| self.scale.scale_glyph_extents(extents))),
700 glyphs,
701 );
702 }
703
704 #[inline]
716 fn line_height_fallback(&self) -> Option<S::Value> {
717 if self.metrics.states_vertical_advances() {
718 return None;
719 }
720 let line = self.origin_line();
721 Some(S::sub(line.ascender, line.descender))
722 }
723
724 fn origin_line(&self) -> LineExtents<S::Value> {
729 if let Some(line) = self.line {
732 return line;
733 }
734 let line = self.metrics.origin_line();
735 LineExtents {
738 ascender: self.scale.scale_y(line.ascender),
739 descender: self.scale.scale_y(line.descender),
740 }
741 }
742
743 fn origin_against_ink(&self, glyph: GlyphId, line: LineExtents<S::Value>) -> S::Value {
746 self.origin_from_extents(self.extents(glyph), line)
747 }
748
749 fn origin_from_extents(
750 &self,
751 extents: Option<GlyphExtents<S::Value>>,
752 line: LineExtents<S::Value>,
753 ) -> S::Value {
754 let Some(extents) = extents else {
755 return line.ascender;
756 };
757 let line_height = S::sub(line.ascender, line.descender);
758 S::add(
759 extents.y_bearing,
760 S::half(S::sub(line_height, extents.height)),
761 )
762 }
763}
764
765#[cfg(test)]
766mod tests {
767 use super::*;
768 use crate::model::Blob;
769 use crate::model::Font;
770 use alloc::sync::Arc;
771 use types::{GlyphId, Tag};
772
773 #[derive(Clone, Copy)]
775 struct Ppem {
776 ppem: f32,
777 upem: u16,
778 }
779
780 impl Scale for Ppem {
781 type Value = f32;
782
783 fn scale_x(&self, value: F48Dot16) -> f32 {
784 value.to_f32() * self.ppem / self.upem as f32
785 }
786
787 fn scale_y(&self, value: F48Dot16) -> f32 {
788 self.scale_x(value)
789 }
790
791 fn scale_glyph_extents(&self, extents: GlyphExtents<F48Dot16>) -> GlyphExtents<f32> {
792 GlyphExtents {
793 x_bearing: self.scale_x(extents.x_bearing),
794 y_bearing: self.scale_y(extents.y_bearing),
795 width: self.scale_x(extents.width),
796 height: self.scale_y(extents.height),
797 }
798 }
799
800 fn scale_rect(&self, b: BoundingBox<F48Dot16>) -> BoundingBox<f32> {
801 BoundingBox {
802 x_min: self.scale_x(b.x_min),
803 y_min: self.scale_y(b.y_min),
804 x_max: self.scale_x(b.x_max),
805 y_max: self.scale_y(b.y_max),
806 }
807 }
808
809 fn add(a: f32, b: f32) -> f32 {
810 a + b
811 }
812
813 fn sub(a: f32, b: f32) -> f32 {
814 a - b
815 }
816
817 fn half(value: f32) -> f32 {
818 value / 2.0
819 }
820 }
821
822 const STATIC: &[u8] = font_test_data::TINOS_SUBSET;
823 const VERT: &[u8] = font_test_data::MPLUS1CODE_VERTICAL_SUBSET;
824 const VORG: &[u8] = font_test_data::VORG;
826
827 fn scaled(data: &[u8], ppem: f32) -> (Font, Ppem) {
828 let font = Font::new(data.to_vec(), 0).unwrap();
829 let upem = font.metrics().units_per_em;
830 (font, Ppem { ppem, upem })
831 }
832
833 fn line(ascender: f32, descender: f32) -> LineExtents<f32> {
834 LineExtents {
835 ascender,
836 descender,
837 }
838 }
839
840 #[derive(Clone, Copy)]
842 struct Rounding(f32, u16);
843
844 impl Scale for Rounding {
845 type Value = f32;
846 fn scale_x(&self, value: F48Dot16) -> f32 {
847 (value.to_f32() * self.0 / self.1 as f32).round()
848 }
849 fn scale_y(&self, value: F48Dot16) -> f32 {
850 self.scale_x(value)
851 }
852 fn scale_glyph_extents(&self, e: GlyphExtents<F48Dot16>) -> GlyphExtents<f32> {
853 GlyphExtents {
854 x_bearing: self.scale_x(e.x_bearing),
855 y_bearing: self.scale_y(e.y_bearing),
856 width: self.scale_x(e.width),
857 height: self.scale_y(e.height),
858 }
859 }
860
861 fn scale_rect(&self, b: BoundingBox<F48Dot16>) -> BoundingBox<f32> {
862 BoundingBox {
863 x_min: self.scale_x(b.x_min),
864 y_min: self.scale_y(b.y_min),
865 x_max: self.scale_x(b.x_max),
866 y_max: self.scale_y(b.y_max),
867 }
868 }
869 fn add(a: f32, b: f32) -> f32 {
870 a + b
871 }
872 fn sub(a: f32, b: f32) -> f32 {
873 a - b
874 }
875 fn half(value: f32) -> f32 {
876 value / 2.0
877 }
878 }
879
880 #[test]
881 fn a_rounding_scale_rounds_the_ends_of_the_line_not_the_height() {
882 let font = Font::new(STATIC.to_vec(), 0).unwrap();
888 let upem = font.metrics().units_per_em;
889 let from_font = font.metrics().h_line().unwrap().extents();
890 let mut rounded_apart = 0;
891 for ppem in [11.0f32, 12.0, 13.0, 14.0, 16.0, 19.0, 24.0] {
892 let scale = Rounding(ppem, upem);
893 let metrics = font.glyph_metrics().scaled(scale);
894 let ends = LineExtents {
895 ascender: scale.scale_y(from_font.ascender),
896 descender: scale.scale_y(from_font.descender),
897 };
898 let from_ends = Rounding::sub(ends.ascender, ends.descender);
899 assert_eq!(
900 metrics.v_advance(GlyphId::new(1)),
901 from_ends,
902 "at {ppem}ppem"
903 );
904 assert_eq!(
906 metrics
907 .with_line_extents(Some(ends))
908 .v_advance(GlyphId::new(1)),
909 from_ends,
910 "at {ppem}ppem, supplied"
911 );
912 if scale.scale_y(from_font.ascender - from_font.descender) != from_ends {
914 rounded_apart += 1;
915 }
916 }
917 assert!(
918 rounded_apart > 0,
919 "no size here rounds the two ways apart, so this proves nothing"
920 );
921 }
922
923 #[test]
924 fn the_scaled_line_is_the_one_the_glyph_metrics_stack_by() {
925 let font = Font::new(STATIC.to_vec(), 0).unwrap();
930 let upem = font.metrics().units_per_em;
931 for ppem in [11.0f32, 12.0, 13.0, 16.0, 19.0, 24.0] {
932 let scale = Rounding(ppem, upem);
933 let line = font.metrics().scaled(scale).h_line().unwrap();
934 let height = Rounding::sub(line.ascender, line.descender);
935 let glyphs = font.glyph_metrics().scaled(scale);
936 for gid in (0..font.num_glyphs()).map(GlyphId::new) {
937 assert_eq!(glyphs.v_advance(gid), height, "glyph {gid} at {ppem}ppem");
938 }
939 }
940 }
941
942 #[test]
943 fn every_measurement_is_the_unscaled_one_through_the_scale() {
944 let font = Font::new(STATIC.to_vec(), 0).unwrap();
947 let global = font.metrics();
948 let scale = Ppem {
949 ppem: 16.0,
950 upem: global.units_per_em,
951 };
952 let scaled = global.scaled(scale);
953 let y = |line: LineBox<F48Dot16>| LineBox {
954 ascender: scale.scale_y(line.ascender),
955 descender: scale.scale_y(line.descender),
956 line_gap: scale.scale_y(line.line_gap),
957 };
958 let x = |line: LineBox<F48Dot16>| LineBox {
959 ascender: scale.scale_x(line.ascender),
960 descender: scale.scale_x(line.descender),
961 line_gap: scale.scale_x(line.line_gap),
962 };
963 assert_eq!(scaled.bounds(), scale.scale_rect(global.bounds));
964 assert_eq!(scaled.hhea_line(), global.hhea_line.map(y));
965 assert_eq!(scaled.typo_line(), global.typo_line.map(y));
966 assert_eq!(scaled.vhea_line(), global.vhea_line.map(x));
967 assert_eq!(scaled.h_line(), global.h_line().map(y));
968 assert_eq!(
969 scaled.win_line(),
970 global.win_line.map(|l| LineExtents {
971 ascender: scale.scale_y(l.ascender),
972 descender: scale.scale_y(l.descender),
973 })
974 );
975 assert_eq!(scaled.x_height(), global.x_height.map(|v| scale.scale_y(v)));
976 assert_eq!(
977 scaled.cap_height(),
978 global.cap_height.map(|v| scale.scale_y(v))
979 );
980 assert_eq!(
981 scaled.max_h_advance(),
982 global.max_h_advance.map(|v| scale.scale_x(v))
983 );
984 assert_eq!(
985 scaled.max_v_advance(),
986 global.max_v_advance.map(|v| scale.scale_y(v))
987 );
988 assert_eq!(
989 scaled.average_char_width(),
990 global.average_char_width.map(|v| scale.scale_x(v))
991 );
992 assert_eq!(scaled.units_per_em(), global.units_per_em);
994 assert_eq!(scaled.num_glyphs(), global.num_glyphs);
995 assert_eq!(scaled.use_typo_metrics(), global.use_typo_metrics);
996 assert!(scaled.hhea_line().is_some() && scaled.typo_line().is_some());
998 }
999
1000 #[test]
1001 fn the_clipping_line_reports_its_descent_as_a_position() {
1002 let font = Font::new(STATIC.to_vec(), 0).unwrap();
1005 let global = font.metrics();
1006 let win = global
1007 .win_line
1008 .expect("this font provides clipping metrics");
1009 assert!(win.ascender > F48Dot16::ZERO);
1010 assert!(win.descender < F48Dot16::ZERO);
1011 let scale = Ppem {
1012 ppem: 16.0,
1013 upem: global.units_per_em,
1014 };
1015 let scaled = global.scaled(scale).win_line().unwrap();
1016 assert_eq!(scaled.ascender, scale.scale_y(win.ascender));
1017 assert_eq!(scaled.descender, scale.scale_y(win.descender));
1018 }
1019
1020 #[test]
1021 fn the_two_scales_agree_within_what_each_can_hold() {
1022 let font = Font::new(STATIC.to_vec(), 0).unwrap();
1026 let upem = font.metrics().units_per_em;
1027 let coarse = font
1028 .glyph_metrics()
1029 .scaled(Scale26Dot6::from_ppem(16.0, upem));
1030 let fine = font.glyph_metrics().scaled(ScaleF32::from_ppem(16.0, upem));
1031 for gid in (0..font.num_glyphs()).map(GlyphId::new) {
1032 let apart = (coarse.h_advance(gid).to_f32() - fine.h_advance(gid)).abs();
1034 assert!(apart < 1.0 / 32.0, "glyph {gid} is {apart} apart");
1035 }
1036 }
1037
1038 #[test]
1039 fn a_scale_with_a_size_per_axis_uses_each_in_its_own_direction() {
1040 let font = Font::new(VERT.to_vec(), 0).unwrap();
1043 let upem = font.metrics().units_per_em;
1044 let em = upem as f32;
1045 let wide = ScaleF32::new(em * 2.0, em, upem);
1046 let tall = ScaleF32::new(em, em * 2.0, upem);
1047 let plain = ScaleF32::new(em, em, upem);
1048 let gid = GlyphId::new(1);
1049 let (w, t, p) = (
1050 font.glyph_metrics().scaled(wide),
1051 font.glyph_metrics().scaled(tall),
1052 font.glyph_metrics().scaled(plain),
1053 );
1054 assert_eq!(w.h_advance(gid), p.h_advance(gid) * 2.0);
1055 assert_eq!(w.v_advance(gid), p.v_advance(gid));
1056 assert_eq!(t.v_advance(gid), p.v_advance(gid) * 2.0);
1057 assert_eq!(t.h_advance(gid), p.h_advance(gid));
1058 }
1059
1060 #[test]
1061 fn freetype_scales_whole_design_units() {
1062 let upem = 1000;
1066 let fraction = F48Dot16::from_f64(10.5);
1067 assert_eq!(
1068 Scale26Dot6::from_ppem(upem as f32, upem).scale_x(fraction),
1069 F26Dot6::from_f64(11.0)
1070 );
1071 assert_eq!(
1072 ScaleF32::from_ppem(upem as f32, upem).scale_x(fraction),
1073 10.5
1074 );
1075 }
1076
1077 #[test]
1078 fn a_rounding_scale_rounds_extents_corners_before_size() {
1079 let extents = GlyphExtents {
1080 x_bearing: F48Dot16::from_f64(10.6),
1081 y_bearing: F48Dot16::from_f64(-0.5),
1082 width: F48Dot16::from_f64(9.8),
1083 height: F48Dot16::from_f64(20.0),
1084 };
1085 let scale = Scale26Dot6::from_ppem(1000.0, 1000);
1086 assert_eq!(
1087 scale.scale_glyph_extents(extents),
1088 GlyphExtents {
1089 x_bearing: F26Dot6::from_f64(11.0),
1090 y_bearing: F26Dot6::from_f64(-1.0),
1091 width: F26Dot6::from_f64(9.0),
1092 height: F26Dot6::from_f64(20.0),
1093 }
1094 );
1095
1096 let below_half = F48Dot16::from_bits((100_000 << 16) + 0x7fff);
1097 let extents = GlyphExtents {
1098 x_bearing: below_half,
1099 y_bearing: F48Dot16::from_i32(10),
1100 width: F48Dot16::from_i32(2),
1101 height: F48Dot16::from_i32(2),
1102 };
1103 assert_eq!(
1104 scale.scale_glyph_extents(extents).x_bearing,
1105 F26Dot6::from_i32(100_000)
1106 );
1107 }
1108
1109 #[test]
1110 fn halving_rounds_toward_negative_infinity_in_26_6() {
1111 assert_eq!(
1114 Scale26Dot6::half(F26Dot6::from_bits(-3)),
1115 F26Dot6::from_bits(-2)
1116 );
1117 assert_eq!(ScaleF32::half(-3.0), -1.5);
1118 }
1119
1120 #[test]
1121 fn a_scaled_advance_is_the_design_unit_one_through_the_scale() {
1122 let (font, scale) = scaled(STATIC, 16.0);
1123 let metrics = font.glyph_metrics();
1124 let scaled = metrics.scaled(scale);
1125 for gid in (0..font.num_glyphs()).map(GlyphId::new) {
1126 assert_eq!(
1127 scaled.h_advance(gid),
1128 scale.scale_x(metrics.h_advance_exact(gid)),
1129 "glyph {gid}"
1130 );
1131 }
1132 }
1133
1134 #[test]
1135 fn a_stated_line_measures_a_font_that_states_none() {
1136 let (font, scale) = scaled(STATIC, 16.0);
1138 let metrics = font.glyph_metrics().scaled(scale);
1139 let supplied = metrics.with_line_extents(Some(line(12.0, -4.0)));
1140 let gid = GlyphId::new(1);
1141 assert_eq!(supplied.v_advance(gid), 16.0);
1142 assert!(metrics.v_advance(gid) != 16.0);
1143 }
1144
1145 #[test]
1146 fn a_font_that_states_its_own_ignores_the_line() {
1147 let (font, scale) = scaled(VERT, 16.0);
1150 let metrics = font.glyph_metrics().scaled(scale);
1151 let supplied = metrics.with_line_extents(Some(line(99.0, -99.0)));
1152 for gid in (0..font.num_glyphs()).map(GlyphId::new) {
1153 assert_eq!(
1154 metrics.v_advance(gid),
1155 supplied.v_advance(gid),
1156 "glyph {gid}"
1157 );
1158 }
1159 }
1160
1161 #[test]
1162 fn stating_nothing_is_stating_nothing() {
1163 let (font, scale) = scaled(STATIC, 16.0);
1164 let metrics = font.glyph_metrics().scaled(scale);
1165 let gid = GlyphId::new(1);
1166 assert_eq!(
1167 metrics.with_line_extents(None).v_advance(gid),
1168 metrics.v_advance(gid)
1169 );
1170 }
1171
1172 #[test]
1173 fn a_batch_agrees_with_one_at_a_time() {
1174 for (data, supplied) in [
1175 (STATIC, None),
1176 (STATIC, Some(line(12.0, -4.0))),
1177 (VERT, None),
1178 (VERT, Some(line(12.0, -4.0))),
1179 ] {
1180 let (font, scale) = scaled(data, 16.0);
1181 let metrics = font
1182 .glyph_metrics()
1183 .scaled(scale)
1184 .with_line_extents(supplied);
1185 let gids: Vec<_> = (0..font.num_glyphs()).map(GlyphId::new).collect();
1186 let mut h = vec![0.0f32; gids.len()];
1187 let mut v = vec![0.0f32; gids.len()];
1188 metrics.h_advance_batched(|value| value, gids.iter().copied().zip(h.iter_mut()));
1189 metrics.v_advance_batched(|value| value, gids.iter().copied().zip(v.iter_mut()));
1190 for (i, gid) in gids.iter().enumerate() {
1191 assert_eq!(metrics.h_advance(*gid), h[i], "h, glyph {gid}");
1192 assert_eq!(metrics.v_advance(*gid), v[i], "v, glyph {gid}");
1193 }
1194 }
1195 }
1196
1197 #[test]
1198 fn a_scaled_box_is_the_font_box_through_the_scale() {
1199 let (font, scale) = scaled(STATIC, 16.0);
1200 let metrics = font.glyph_metrics();
1201 let scaled = metrics.scaled(scale);
1202 let mut measured = 0;
1203 for gid in (0..font.num_glyphs()).map(GlyphId::new) {
1204 let expected = metrics
1205 .extents_exact(gid)
1206 .map(|extents| scale.scale_glyph_extents(extents));
1207 assert_eq!(scaled.extents(gid), expected, "glyph {gid}");
1208 measured += expected.is_some() as u32;
1209 }
1210 assert!(measured > 0);
1211 }
1212
1213 #[test]
1214 fn a_supplied_measurement_replaces_the_font_one() {
1215 let (font, scale) = scaled(STATIC, 16.0);
1218 let ink = GlyphExtents {
1219 x_bearing: 1.0,
1220 y_bearing: 2.0,
1221 width: 3.0,
1222 height: 4.0,
1223 };
1224 let supplied = |glyph: GlyphId| (glyph.to_u32() % 2 == 0).then_some(ink);
1225 let metrics = font.glyph_metrics().scaled(scale);
1226 let overridden = metrics.with_glyph_extents(Some(&supplied));
1227 let mut replaced = 0;
1228 for gid in (0..font.num_glyphs()).map(GlyphId::new) {
1229 assert_eq!(overridden.extents(gid), supplied(gid), "glyph {gid}");
1230 replaced += (metrics.extents(gid) != supplied(gid)) as u32;
1231 }
1232 assert!(replaced > 0, "the font already agreed everywhere");
1233 }
1234
1235 #[test]
1236 fn a_supplied_measurement_answers_a_whole_run() {
1237 let (font, scale) = scaled(STATIC, 16.0);
1238 let ink = GlyphExtents {
1239 x_bearing: 5.0,
1240 y_bearing: 6.0,
1241 width: 7.0,
1242 height: 8.0,
1243 };
1244 let supplied = |glyph: GlyphId| (glyph.to_u32() % 3 != 0).then_some(ink);
1245 let metrics = font
1246 .glyph_metrics()
1247 .scaled(scale)
1248 .with_glyph_extents(Some(&supplied));
1249 let glyphs: Vec<_> = (0..font.num_glyphs()).map(GlyphId::new).collect();
1250 let mut batched = vec![None; glyphs.len()];
1251 metrics.extents_batched(
1252 |extents| extents,
1253 glyphs.iter().copied().zip(batched.iter_mut()),
1254 );
1255 for (gid, batched) in glyphs.iter().copied().zip(batched) {
1256 assert_eq!(batched, supplied(gid), "glyph {gid}");
1257 }
1258 }
1259
1260 #[test]
1261 fn no_supplied_measurement_leaves_the_font_in_place() {
1262 let (font, scale) = scaled(STATIC, 16.0);
1263 let metrics = font.glyph_metrics().scaled(scale);
1264 for gid in (0..font.num_glyphs()).map(GlyphId::new) {
1265 assert_eq!(
1266 metrics.with_glyph_extents(None).extents(gid),
1267 metrics.extents(gid),
1268 "glyph {gid}"
1269 );
1270 }
1271 }
1272
1273 #[test]
1274 fn a_supplied_measurement_can_borrow_for_one_query() {
1275 let (font, scale) = scaled(STATIC, 16.0);
1276 let metrics = font.glyph_metrics().scaled(scale);
1277 let glyph = GlyphId::new(1);
1278 let original = metrics.extents(glyph);
1279 let supplied = {
1280 let ink = GlyphExtents {
1281 x_bearing: 1.0,
1282 y_bearing: 2.0,
1283 width: 3.0,
1284 height: 4.0,
1285 };
1286 let callback = |_: GlyphId| Some(ink);
1287 metrics.with_glyph_extents(Some(&callback)).extents(glyph)
1288 };
1289 assert_ne!(supplied, original);
1290 assert_eq!(metrics.extents(glyph), original);
1291 }
1292
1293 #[test]
1294 fn vorg_answers_ahead_of_everything_else() {
1295 let (font, scale) = scaled(VORG, 16.0);
1298 let metrics = font.glyph_metrics().scaled(scale);
1299 let direct = crate::FontRef::new(VORG).unwrap();
1300 let vorg = crate::TableProvider::vorg(&direct).unwrap();
1301 for gid in (0..4).map(GlyphId::new) {
1302 assert_eq!(
1303 metrics.v_origin_y(gid),
1304 scale.scale_y(F48Dot16::from_i32(vorg.vertical_origin_y(gid) as i32)),
1305 "glyph {gid}"
1306 );
1307 }
1308 let ink = GlyphExtents {
1310 x_bearing: 0.0,
1311 y_bearing: 999.0,
1312 width: 1.0,
1313 height: 1.0,
1314 };
1315 let supplied = |_: GlyphId| Some(ink);
1316 let overridden = metrics.with_glyph_extents(Some(&supplied));
1317 for gid in (0..4).map(GlyphId::new) {
1318 assert_eq!(overridden.v_origin_y(gid), metrics.v_origin_y(gid));
1319 }
1320 }
1321
1322 #[test]
1323 fn vmtx_uses_the_glyf_top_phantom_point() {
1324 let (font, scale) = scaled(VERT, 16.0);
1326 let glyph_metrics = font.glyph_metrics();
1327 let metrics = glyph_metrics.scaled(scale);
1328 assert!(glyph_metrics.stated_v_origin(GlyphId::new(1)).is_none());
1329 let mut checked = 0;
1330 for gid in (0..font.num_glyphs()).map(GlyphId::new) {
1331 let (Some(extents), Some(bearing)) = (
1332 glyph_metrics.extents_exact(gid),
1333 glyph_metrics.stated_v_bearing(gid),
1334 ) else {
1335 continue;
1336 };
1337 assert_eq!(
1338 metrics.v_origin_y(gid),
1339 scale.scale_y(glyph_metrics.glyf_v_origin(gid).unwrap()),
1340 "glyph {gid}"
1341 );
1342 assert_eq!(
1343 glyph_metrics.glyf_v_origin(gid).unwrap(),
1344 extents.y_bearing + bearing,
1345 "glyph {gid}"
1346 );
1347 checked += 1;
1348 }
1349 assert!(checked > 0, "no glyph stated both");
1350
1351 let supplied = |_: GlyphId| {
1352 Some(GlyphExtents {
1353 x_bearing: 0.0,
1354 y_bearing: 999.0,
1355 width: 1.0,
1356 height: 1.0,
1357 })
1358 };
1359 let overridden = metrics.with_glyph_extents(Some(&supplied));
1360 assert_eq!(
1361 overridden.v_origin_y(GlyphId::new(1)),
1362 metrics.v_origin_y(GlyphId::new(1))
1363 );
1364 }
1365
1366 #[test]
1367 fn without_vmtx_the_ink_is_centered_in_the_line() {
1368 let (font, scale) = scaled(STATIC, 16.0);
1370 let glyph_metrics = font.glyph_metrics();
1371 let metrics = glyph_metrics.scaled(scale);
1372 assert!(glyph_metrics.stated_v_bearing(GlyphId::new(1)).is_none());
1373 let line = glyph_metrics.line_extents().unwrap();
1374 let height = scale.scale_y(line.ascender) - scale.scale_y(line.descender);
1375 let mut checked = 0;
1376 for gid in (0..font.num_glyphs()).map(GlyphId::new) {
1377 let Some(extents) = glyph_metrics.extents_exact(gid) else {
1378 continue;
1379 };
1380 let extents = scale.scale_glyph_extents(extents);
1381 assert_eq!(
1382 metrics.v_origin_y(gid),
1383 extents.y_bearing + (height - extents.height) / 2.0,
1384 "glyph {gid}"
1385 );
1386 checked += 1;
1387 }
1388 assert!(checked > 0);
1389 }
1390
1391 #[test]
1392 fn an_empty_glyph_is_centered_but_unavailable_extents_use_the_ascender() {
1393 let (font, scale) = scaled(STATIC, 16.0);
1394 let glyph_metrics = font.glyph_metrics();
1395 let metrics = glyph_metrics.scaled(scale);
1396 let line = metrics.origin_line();
1397 let absent = |_: GlyphId| None;
1398 let overridden = metrics.with_glyph_extents(Some(&absent));
1399 let mut checked = 0;
1400 for gid in (0..font.num_glyphs()).map(GlyphId::new) {
1401 if glyph_metrics.extents(gid) != Some(GlyphExtents::default()) {
1402 continue;
1403 }
1404 assert_eq!(
1405 metrics.v_origin_y(gid),
1406 (line.ascender - line.descender) / 2.0,
1407 "glyph {gid}"
1408 );
1409 assert_eq!(overridden.v_origin_y(gid), line.ascender);
1410 checked += 1;
1411 }
1412 assert!(checked > 0, "no glyph was empty");
1413 }
1414
1415 #[test]
1416 fn a_supplied_measurement_moves_the_origin_with_it() {
1417 let (font, scale) = scaled(STATIC, 16.0);
1420 let metrics = font.glyph_metrics().scaled(scale);
1421 let ink = GlyphExtents {
1422 x_bearing: 0.0,
1423 y_bearing: 100.0,
1424 width: 10.0,
1425 height: 20.0,
1426 };
1427 let supplied = |_: GlyphId| Some(ink);
1428 let overridden = metrics.with_glyph_extents(Some(&supplied));
1429 let line = metrics.origin_line();
1430 let height = line.ascender - line.descender;
1431 let expected = ink.y_bearing + (height - ink.height) / 2.0;
1432 let mut moved = 0;
1433 for gid in (0..font.num_glyphs()).map(GlyphId::new) {
1434 assert_eq!(overridden.v_origin_y(gid), expected, "glyph {gid}");
1435 moved += (metrics.v_origin_y(gid) != expected) as u32;
1436 }
1437 assert!(moved > 0, "the font already agreed everywhere");
1438
1439 let gids: Vec<_> = (0..font.num_glyphs()).map(GlyphId::new).collect();
1440 let mut origins = vec![0.0; gids.len()];
1441 overridden.v_origin_y_batched(
1442 |origin| origin,
1443 gids.iter().copied().zip(origins.iter_mut()),
1444 );
1445 assert!(origins.iter().all(|origin| *origin == expected));
1446 }
1447
1448 #[test]
1449 fn a_supplied_line_moves_the_origin_with_it() {
1450 let (font, scale) = scaled(STATIC, 16.0);
1453 let metrics = font.glyph_metrics().scaled(scale);
1454 let taller = metrics.with_line_extents(Some(line(100.0, -20.0)));
1455 let gid = (0..font.num_glyphs())
1456 .map(GlyphId::new)
1457 .find(|gid| font.glyph_metrics().extents(*gid).is_some())
1458 .unwrap();
1459 assert_ne!(taller.v_origin_y(gid), metrics.v_origin_y(gid));
1460 }
1461
1462 #[test]
1463 fn a_run_of_origins_agrees_with_one_at_a_time() {
1464 for data in [VORG, VERT, STATIC] {
1465 let (font, scale) = scaled(data, 16.0);
1466 let metrics = font.glyph_metrics().scaled(scale);
1467 let glyphs: Vec<_> = (0..font.num_glyphs()).map(GlyphId::new).collect();
1468 let mut batched = vec![0.0; glyphs.len()];
1469 metrics.v_origin_y_batched(
1470 |origin| origin,
1471 glyphs.iter().copied().zip(batched.iter_mut()),
1472 );
1473 for (gid, batched) in glyphs.iter().copied().zip(batched) {
1474 assert_eq!(metrics.v_origin_y(gid), batched, "glyph {gid}");
1475 }
1476 }
1477 }
1478
1479 #[test]
1480 fn a_font_stating_no_line_is_given_one_proportioned_to_its_em() {
1481 let font = font_without(&[b"hhea", b"OS/2"]);
1484 let glyph_metrics = font.glyph_metrics();
1485 assert!(glyph_metrics.line_extents().is_none());
1486 let em = font.units_per_em();
1487 assert!(
1488 em > 0,
1489 "the em itself has to survive for this to mean anything"
1490 );
1491
1492 let scale = Ppem {
1493 ppem: 16.0,
1494 upem: em,
1495 };
1496 let metrics = glyph_metrics.scaled(scale);
1497 let ascender = F48Dot16::from_f64(em as f64 * 0.8);
1498 let empty = empty_glyph(&font);
1499 assert_eq!(
1500 metrics.v_origin_y(empty),
1501 scale.scale_y(F48Dot16::from_i32(em as i32)) / 2.0
1502 );
1503 let absent = |_: GlyphId| None;
1504 assert_eq!(
1505 metrics.with_glyph_extents(Some(&absent)).v_origin_y(empty),
1506 scale.scale_y(ascender)
1507 );
1508 assert_eq!(
1511 metrics.v_advance(empty),
1512 scale.scale_y(ascender) - scale.scale_y(ascender - F48Dot16::from_i32(em as i32))
1513 );
1514 }
1515
1516 fn font_without(hide: &'static [&[u8; 4]]) -> Font {
1518 let source: Arc<dyn Fn(Tag) -> Option<Blob> + Send + Sync> = Arc::new(move |tag: Tag| {
1519 if hide.iter().any(|hidden| Tag::new(hidden) == tag) {
1520 return None;
1521 }
1522 let font = crate::FontRef::new(STATIC).ok()?;
1523 Some(Blob::from(font.table_data(tag)?.as_bytes().to_vec()))
1524 });
1525 Font::new(source, 0).unwrap()
1526 }
1527
1528 #[test]
1529 fn the_ends_of_the_line_are_signed_whatever_the_font_says() {
1530 let stated = font_with_hhea_ends(1600, -400);
1534 let scale = Ppem {
1535 ppem: 16.0,
1536 upem: stated.units_per_em(),
1537 };
1538 let empty = empty_glyph(&stated);
1539 let ink = drawn_glyph(&stated);
1540 let expected = stated.glyph_metrics().scaled(scale);
1541 for (ascender, descender) in [(1600, 400), (-1600, 400), (-1600, -400)] {
1542 let font = font_with_hhea_ends(ascender, descender);
1543 let actual = font.glyph_metrics().scaled(scale);
1544 assert_eq!(
1546 actual.v_origin_y(empty),
1547 expected.v_origin_y(empty),
1548 "ascender {ascender}, descender {descender}"
1549 );
1550 assert_eq!(
1551 actual.v_origin_y(ink),
1552 expected.v_origin_y(ink),
1553 "ascender {ascender}, descender {descender}"
1554 );
1555 assert_eq!(
1557 actual.v_advance(ink),
1558 expected.v_advance(ink),
1559 "ascender {ascender}, descender {descender}"
1560 );
1561 }
1562 assert_eq!(
1565 expected.v_origin_y(empty),
1566 scale.scale_y(F48Dot16::from_i32(1000))
1567 );
1568 }
1569
1570 #[test]
1573 fn the_scale_chooses_the_bitmap_strike() {
1574 let font = Font::new(font_test_data::CBDT, 0).unwrap();
1575 let glyph = GlyphId::new(2);
1576 let upem = font.units_per_em();
1577 let metrics = font.glyph_metrics();
1578 for scale in [
1579 ScaleF32::from_ppem(12.0, upem),
1580 ScaleF32::new(12.0, 10.0, upem),
1581 ] {
1582 assert_eq!(scale.ppem(), Some(12.0));
1583 let expected = metrics
1584 .with_bitmap_ppem(Some(12.0))
1585 .extents_exact(glyph)
1586 .map(|extents| scale.scale_glyph_extents(extents));
1587 assert_eq!(metrics.scaled(scale).extents(glyph), expected);
1588 }
1589 assert_eq!(Scale26Dot6::new(10.0, 14.0, upem).ppem(), Some(14.0));
1590 assert_ne!(
1592 metrics.with_bitmap_ppem(Some(12.0)).extents_exact(glyph),
1593 metrics.with_bitmap_ppem(None).extents_exact(glyph)
1594 );
1595 struct NoSize(ScaleF32);
1597 impl Scale for NoSize {
1598 type Value = f32;
1599 fn add(a: f32, b: f32) -> f32 {
1600 ScaleF32::add(a, b)
1601 }
1602 fn sub(a: f32, b: f32) -> f32 {
1603 ScaleF32::sub(a, b)
1604 }
1605 fn half(value: f32) -> f32 {
1606 ScaleF32::half(value)
1607 }
1608 fn scale_x(&self, value: F48Dot16) -> f32 {
1609 self.0.scale_x(value)
1610 }
1611 fn scale_y(&self, value: F48Dot16) -> f32 {
1612 self.0.scale_y(value)
1613 }
1614 fn scale_glyph_extents(&self, extents: GlyphExtents<F48Dot16>) -> GlyphExtents<f32> {
1615 self.0.scale_glyph_extents(extents)
1616 }
1617 fn scale_rect(&self, bounds: BoundingBox<F48Dot16>) -> BoundingBox<f32> {
1618 self.0.scale_rect(bounds)
1619 }
1620 }
1621 let no_size = NoSize(ScaleF32::from_ppem(12.0, upem));
1622 let largest = metrics
1623 .with_bitmap_ppem(None)
1624 .extents_exact(glyph)
1625 .map(|extents| no_size.scale_glyph_extents(extents));
1626 assert_eq!(metrics.scaled(no_size).extents(glyph), largest);
1627 }
1628
1629 #[test]
1630 fn a_supplied_line_is_taken_as_it_was_given() {
1631 let (font, scale) = scaled(STATIC, 16.0);
1635 let metrics = font
1636 .glyph_metrics()
1637 .scaled(scale)
1638 .with_line_extents(Some(line(-100.0, 20.0)));
1639 let absent = |_: GlyphId| None;
1640 assert_eq!(
1641 metrics
1642 .with_glyph_extents(Some(&absent))
1643 .v_origin_y(empty_glyph(&font)),
1644 -100.0
1645 );
1646 }
1647
1648 fn font_with_hhea_ends(ascender: i16, descender: i16) -> Font {
1651 let source: Arc<dyn Fn(Tag) -> Option<Blob> + Send + Sync> = Arc::new(move |tag: Tag| {
1652 if tag == Tag::new(b"OS/2") {
1653 return None;
1654 }
1655 let font = crate::FontRef::new(STATIC).ok()?;
1656 let mut data = font.table_data(tag)?.as_bytes().to_vec();
1657 if tag == Tag::new(b"hhea") {
1658 data.get_mut(4..6)?.copy_from_slice(&ascender.to_be_bytes());
1660 data.get_mut(6..8)?
1661 .copy_from_slice(&descender.to_be_bytes());
1662 }
1663 Some(Blob::from(data))
1664 });
1665 Font::new(source, 0).unwrap()
1666 }
1667
1668 fn empty_glyph(font: &Font) -> GlyphId {
1670 let metrics = font.glyph_metrics();
1671 (0..font.num_glyphs())
1672 .map(GlyphId::new)
1673 .find(|gid| metrics.extents(*gid) == Some(GlyphExtents::default()))
1674 .expect("no glyph has a zero box")
1675 }
1676
1677 fn drawn_glyph(font: &Font) -> GlyphId {
1679 let metrics = font.glyph_metrics();
1680 (0..font.num_glyphs())
1681 .map(GlyphId::new)
1682 .find(|gid| metrics.extents(*gid).is_some())
1683 .expect("no glyph drew anything")
1684 }
1685}