• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use ffi::{FillLinearParams, FillRadialParams};
2 // Copyright 2023 Google LLC
3 // Use of this source code is governed by a BSD-style license that can be found
4 // in the LICENSE file.
5 use font_types::{BoundingBox, GlyphId, Pen};
6 use read_fonts::{
7     tables::{colr::CompositeMode, cpal::Cpal, os2::SelectionFlags},
8     FileRef, FontRef, ReadError, TableProvider,
9 };
10 use skrifa::{
11     attribute::Style,
12     charmap::MappingIndex,
13     color::{Brush, ColorGlyphFormat, ColorPainter, Transform},
14     instance::{Location, Size},
15     metrics::{GlyphMetrics, Metrics},
16     outline::{DrawSettings, HintingInstance, LcdLayout},
17     setting::VariationSetting,
18     string::{LocalizedStrings, StringId},
19     MetadataProvider, OutlineGlyphCollection, Tag,
20 };
21 use std::pin::Pin;
22 
23 use crate::bitmap::{bitmap_glyph, bitmap_metrics, has_bitmap_glyph, png_data, BridgeBitmapGlyph};
24 
25 use crate::ffi::{
26     AxisWrapper, BridgeFontStyle, BridgeLocalizedName, BridgeScalerMetrics, ClipBox,
27     ColorPainterWrapper, ColorStop, PaletteOverride, PathWrapper, SkiaDesignCoordinate,
28 };
29 
make_mapping_index<'a>(font_ref: &'a BridgeFontRef) -> Box<BridgeMappingIndex>30 fn make_mapping_index<'a>(font_ref: &'a BridgeFontRef) -> Box<BridgeMappingIndex> {
31     font_ref
32         .with_font(|f| Some(Box::new(BridgeMappingIndex(MappingIndex::new(f)))))
33         .unwrap()
34 }
35 
no_hinting_instance<'a>() -> Box<BridgeHintingInstance>36 unsafe fn no_hinting_instance<'a>() -> Box<BridgeHintingInstance> {
37     Box::new(BridgeHintingInstance(None))
38 }
39 
make_hinting_instance<'a>( outlines: &BridgeOutlineCollection, size: f32, coords: &BridgeNormalizedCoords, do_lcd_antialiasing: bool, lcd_orientation_vertical: bool, preserve_linear_metrics: bool, ) -> Box<BridgeHintingInstance>40 unsafe fn make_hinting_instance<'a>(
41     outlines: &BridgeOutlineCollection,
42     size: f32,
43     coords: &BridgeNormalizedCoords,
44     do_lcd_antialiasing: bool,
45     lcd_orientation_vertical: bool,
46     preserve_linear_metrics: bool,
47 ) -> Box<BridgeHintingInstance> {
48     let hinting_instance = match &outlines.0 {
49         Some(outlines) => {
50             let lcd_subpixel = match (do_lcd_antialiasing, lcd_orientation_vertical) {
51                 (true, false) => Some(LcdLayout::Horizontal),
52                 (true, true) => Some(LcdLayout::Vertical),
53                 _ => None,
54             };
55             HintingInstance::new(
56                 outlines,
57                 Size::new(size),
58                 &coords.normalized_coords,
59                 skrifa::outline::HintingMode::Smooth {
60                     lcd_subpixel,
61                     preserve_linear_metrics,
62                 },
63             )
64             .ok()
65         }
66         _ => None,
67     };
68     Box::new(BridgeHintingInstance(hinting_instance))
69 }
70 
make_mono_hinting_instance<'a>( outlines: &BridgeOutlineCollection, size: f32, coords: &BridgeNormalizedCoords, ) -> Box<BridgeHintingInstance>71 unsafe fn make_mono_hinting_instance<'a>(
72     outlines: &BridgeOutlineCollection,
73     size: f32,
74     coords: &BridgeNormalizedCoords,
75 ) -> Box<BridgeHintingInstance> {
76     let hinting_instance = outlines.0.as_ref().and_then(|outlines| {
77         HintingInstance::new(
78             outlines,
79             Size::new(size),
80             &coords.normalized_coords,
81             skrifa::outline::HintingMode::Strong,
82         )
83         .ok()
84     });
85     Box::new(BridgeHintingInstance(hinting_instance))
86 }
87 
lookup_glyph_or_zero(font_ref: &BridgeFontRef, map: &BridgeMappingIndex, codepoint: u32) -> u1688 fn lookup_glyph_or_zero(font_ref: &BridgeFontRef, map: &BridgeMappingIndex, codepoint: u32) -> u16 {
89     font_ref
90         .with_font(|f| Some(map.0.charmap(f).map(codepoint)?.to_u16()))
91         .unwrap_or_default()
92 }
93 
num_glyphs(font_ref: &BridgeFontRef) -> u1694 fn num_glyphs(font_ref: &BridgeFontRef) -> u16 {
95     font_ref
96         .with_font(|f| Some(f.maxp().ok()?.num_glyphs()))
97         .unwrap_or_default()
98 }
99 
fill_glyph_to_unicode_map(font_ref: &BridgeFontRef, map: &mut [u32])100 fn fill_glyph_to_unicode_map(font_ref: &BridgeFontRef, map: &mut [u32]) {
101     map.fill(0);
102     font_ref.with_font(|f| {
103         let mappings = f.charmap().mappings();
104         for item in mappings {
105             if map[item.1.to_u16() as usize] == 0 {
106                 map[item.1.to_u16() as usize] = item.0;
107             }
108         }
109         Some(())
110     });
111 }
112 
113 struct PathWrapperPen<'a> {
114     path_wrapper: Pin<&'a mut ffi::PathWrapper>,
115 }
116 
117 // We need to wrap ffi::PathWrapper in PathWrapperPen and forward the path
118 // recording calls to the path wrapper as we can't define trait implementations
119 // inside the cxx::bridge section.
120 impl<'a> Pen for PathWrapperPen<'a> {
move_to(&mut self, x: f32, y: f32)121     fn move_to(&mut self, x: f32, y: f32) {
122         self.path_wrapper.as_mut().move_to(x, -y);
123     }
124 
line_to(&mut self, x: f32, y: f32)125     fn line_to(&mut self, x: f32, y: f32) {
126         self.path_wrapper.as_mut().line_to(x, -y);
127     }
128 
quad_to(&mut self, cx0: f32, cy0: f32, x: f32, y: f32)129     fn quad_to(&mut self, cx0: f32, cy0: f32, x: f32, y: f32) {
130         self.path_wrapper.as_mut().quad_to(cx0, -cy0, x, -y);
131     }
132 
curve_to(&mut self, cx0: f32, cy0: f32, cx1: f32, cy1: f32, x: f32, y: f32)133     fn curve_to(&mut self, cx0: f32, cy0: f32, cx1: f32, cy1: f32, x: f32, y: f32) {
134         self.path_wrapper
135             .as_mut()
136             .curve_to(cx0, -cy0, cx1, -cy1, x, -y);
137     }
138 
close(&mut self)139     fn close(&mut self) {
140         self.path_wrapper.as_mut().close();
141     }
142 }
143 
144 struct NoOpPen {}
145 
146 impl<'a> Pen for NoOpPen {
move_to(&mut self, _x: f32, _y: f32)147     fn move_to(&mut self, _x: f32, _y: f32) {}
148 
line_to(&mut self, _x: f32, _y: f32)149     fn line_to(&mut self, _x: f32, _y: f32) {}
150 
quad_to(&mut self, _cx0: f32, _cy0: f32, _x: f32, _y: f32)151     fn quad_to(&mut self, _cx0: f32, _cy0: f32, _x: f32, _y: f32) {}
152 
curve_to(&mut self, _cx0: f32, _cy0: f32, _cx1: f32, _cy1: f32, _x: f32, _y: f32)153     fn curve_to(&mut self, _cx0: f32, _cy0: f32, _cx1: f32, _cy1: f32, _x: f32, _y: f32) {}
154 
close(&mut self)155     fn close(&mut self) {}
156 }
157 
158 struct ColorPainterImpl<'a> {
159     color_painter_wrapper: Pin<&'a mut ffi::ColorPainterWrapper>,
160 }
161 
162 impl<'a> ColorPainter for ColorPainterImpl<'a> {
push_transform(&mut self, transform: Transform)163     fn push_transform(&mut self, transform: Transform) {
164         self.color_painter_wrapper
165             .as_mut()
166             .push_transform(&ffi::Transform {
167                 xx: transform.xx,
168                 xy: transform.xy,
169                 yx: transform.yx,
170                 yy: transform.yy,
171                 dx: transform.dx,
172                 dy: transform.dy,
173             });
174     }
175 
pop_transform(&mut self)176     fn pop_transform(&mut self) {
177         self.color_painter_wrapper.as_mut().pop_transform();
178     }
179 
push_clip_glyph(&mut self, glyph: GlyphId)180     fn push_clip_glyph(&mut self, glyph: GlyphId) {
181         self.color_painter_wrapper
182             .as_mut()
183             .push_clip_glyph(glyph.to_u16());
184     }
185 
push_clip_box(&mut self, clip_box: BoundingBox<f32>)186     fn push_clip_box(&mut self, clip_box: BoundingBox<f32>) {
187         self.color_painter_wrapper.as_mut().push_clip_rectangle(
188             clip_box.x_min,
189             clip_box.y_min,
190             clip_box.x_max,
191             clip_box.y_max,
192         );
193     }
194 
pop_clip(&mut self)195     fn pop_clip(&mut self) {
196         self.color_painter_wrapper.as_mut().pop_clip();
197     }
198 
fill(&mut self, fill_type: Brush)199     fn fill(&mut self, fill_type: Brush) {
200         let color_painter = self.color_painter_wrapper.as_mut();
201         match fill_type {
202             Brush::Solid {
203                 palette_index,
204                 alpha,
205             } => {
206                 color_painter.fill_solid(palette_index, alpha);
207             }
208 
209             Brush::LinearGradient {
210                 p0,
211                 p1,
212                 color_stops,
213                 extend,
214             } => {
215                 let mut bridge_color_stops = BridgeColorStops {
216                     stops_iterator: Box::new(color_stops.iter()),
217                     num_stops: color_stops.len(),
218                 };
219                 color_painter.fill_linear(
220                     &FillLinearParams {
221                         x0: p0.x,
222                         y0: p0.y,
223                         x1: p1.x,
224                         y1: p1.y,
225                     },
226                     &mut bridge_color_stops,
227                     extend as u8,
228                 );
229             }
230             Brush::RadialGradient {
231                 c0,
232                 r0,
233                 c1,
234                 r1,
235                 color_stops,
236                 extend,
237             } => {
238                 let mut bridge_color_stops = BridgeColorStops {
239                     stops_iterator: Box::new(color_stops.iter()),
240                     num_stops: color_stops.len(),
241                 };
242                 color_painter.fill_radial(
243                     &FillRadialParams {
244                         x0: c0.x,
245                         y0: c0.y,
246                         r0,
247                         x1: c1.x,
248                         y1: c1.y,
249                         r1,
250                     },
251                     &mut bridge_color_stops,
252                     extend as u8,
253                 );
254             }
255             Brush::SweepGradient {
256                 c0,
257                 start_angle,
258                 end_angle,
259                 color_stops,
260                 extend,
261             } => {
262                 let mut bridge_color_stops = BridgeColorStops {
263                     stops_iterator: Box::new(color_stops.iter()),
264                     num_stops: color_stops.len(),
265                 };
266                 color_painter.fill_sweep(
267                     &ffi::FillSweepParams {
268                         x0: c0.x,
269                         y0: c0.y,
270                         start_angle,
271                         end_angle,
272                     },
273                     &mut bridge_color_stops,
274                     extend as u8,
275                 );
276             }
277         }
278     }
279 
fill_glyph(&mut self, glyph: GlyphId, brush_transform: Option<Transform>, brush: Brush)280     fn fill_glyph(&mut self, glyph: GlyphId, brush_transform: Option<Transform>, brush: Brush) {
281         let color_painter = self.color_painter_wrapper.as_mut();
282         let brush_transform = brush_transform.unwrap_or_default();
283         match brush {
284             Brush::Solid {
285                 palette_index,
286                 alpha,
287             } => {
288                 color_painter.fill_glyph_solid(glyph.to_u16(), palette_index, alpha);
289             }
290             Brush::LinearGradient {
291                 p0,
292                 p1,
293                 color_stops,
294                 extend,
295             } => {
296                 let mut bridge_color_stops = BridgeColorStops {
297                     stops_iterator: Box::new(color_stops.iter()),
298                     num_stops: color_stops.len(),
299                 };
300                 color_painter.fill_glyph_linear(
301                     glyph.to_u16(),
302                     &ffi::Transform {
303                         xx: brush_transform.xx,
304                         xy: brush_transform.xy,
305                         yx: brush_transform.yx,
306                         yy: brush_transform.yy,
307                         dx: brush_transform.dx,
308                         dy: brush_transform.dy,
309                     },
310                     &FillLinearParams {
311                         x0: p0.x,
312                         y0: p0.y,
313                         x1: p1.x,
314                         y1: p1.y,
315                     },
316                     &mut bridge_color_stops,
317                     extend as u8,
318                 );
319             }
320             Brush::RadialGradient {
321                 c0,
322                 r0,
323                 c1,
324                 r1,
325                 color_stops,
326                 extend,
327             } => {
328                 let mut bridge_color_stops = BridgeColorStops {
329                     stops_iterator: Box::new(color_stops.iter()),
330                     num_stops: color_stops.len(),
331                 };
332                 color_painter.fill_glyph_radial(
333                     glyph.to_u16(),
334                     &ffi::Transform {
335                         xx: brush_transform.xx,
336                         xy: brush_transform.xy,
337                         yx: brush_transform.yx,
338                         yy: brush_transform.yy,
339                         dx: brush_transform.dx,
340                         dy: brush_transform.dy,
341                     },
342                     &FillRadialParams {
343                         x0: c0.x,
344                         y0: c0.y,
345                         r0,
346                         x1: c1.x,
347                         y1: c1.y,
348                         r1,
349                     },
350                     &mut bridge_color_stops,
351                     extend as u8,
352                 );
353             }
354             Brush::SweepGradient {
355                 c0,
356                 start_angle,
357                 end_angle,
358                 color_stops,
359                 extend,
360             } => {
361                 let mut bridge_color_stops = BridgeColorStops {
362                     stops_iterator: Box::new(color_stops.iter()),
363                     num_stops: color_stops.len(),
364                 };
365                 color_painter.fill_glyph_sweep(
366                     glyph.to_u16(),
367                     &ffi::Transform {
368                         xx: brush_transform.xx,
369                         xy: brush_transform.xy,
370                         yx: brush_transform.yx,
371                         yy: brush_transform.yy,
372                         dx: brush_transform.dx,
373                         dy: brush_transform.dy,
374                     },
375                     &ffi::FillSweepParams {
376                         x0: c0.x,
377                         y0: c0.y,
378                         start_angle,
379                         end_angle,
380                     },
381                     &mut bridge_color_stops,
382                     extend as u8,
383                 );
384             }
385         }
386     }
387 
push_layer(&mut self, composite_mode: CompositeMode)388     fn push_layer(&mut self, composite_mode: CompositeMode) {
389         self.color_painter_wrapper
390             .as_mut()
391             .push_layer(composite_mode as u8);
392     }
pop_layer(&mut self)393     fn pop_layer(&mut self) {
394         self.color_painter_wrapper.as_mut().pop_layer();
395     }
396 }
397 
get_path( outlines: &BridgeOutlineCollection, glyph_id: u16, size: f32, coords: &BridgeNormalizedCoords, hinting_instance: &BridgeHintingInstance, path_wrapper: Pin<&mut PathWrapper>, scaler_metrics: &mut BridgeScalerMetrics, ) -> bool398 fn get_path(
399     outlines: &BridgeOutlineCollection,
400     glyph_id: u16,
401     size: f32,
402     coords: &BridgeNormalizedCoords,
403     hinting_instance: &BridgeHintingInstance,
404     path_wrapper: Pin<&mut PathWrapper>,
405     scaler_metrics: &mut BridgeScalerMetrics,
406 ) -> bool {
407     outlines
408         .0
409         .as_ref()
410         .and_then(|outlines| {
411             let glyph = outlines.get(GlyphId::new(glyph_id))?;
412 
413             let draw_settings = match &hinting_instance.0 {
414                 Some(instance) => DrawSettings::hinted(instance, false),
415                 _ => DrawSettings::unhinted(Size::new(size), &coords.normalized_coords),
416             };
417 
418             let mut pen_dump = PathWrapperPen { path_wrapper };
419             match glyph.draw(draw_settings, &mut pen_dump) {
420                 Err(_) => None,
421                 Ok(metrics) => {
422                     scaler_metrics.has_overlaps = metrics.has_overlaps;
423                     Some(())
424                 }
425             }
426         })
427         .is_some()
428 }
429 
unhinted_advance_width_or_zero( font_ref: &BridgeFontRef, size: f32, coords: &BridgeNormalizedCoords, glyph_id: u16, ) -> f32430 fn unhinted_advance_width_or_zero(
431     font_ref: &BridgeFontRef,
432     size: f32,
433     coords: &BridgeNormalizedCoords,
434     glyph_id: u16,
435 ) -> f32 {
436     font_ref
437         .with_font(|f| {
438             GlyphMetrics::new(f, Size::new(size), coords.normalized_coords.coords())
439                 .advance_width(GlyphId::new(glyph_id))
440         })
441         .unwrap_or_default()
442 }
443 
scaler_hinted_advance_width( outlines: &BridgeOutlineCollection, hinting_instance: &BridgeHintingInstance, glyph_id: u16, out_advance_width: &mut f32, ) -> bool444 fn scaler_hinted_advance_width(
445     outlines: &BridgeOutlineCollection,
446     hinting_instance: &BridgeHintingInstance,
447     glyph_id: u16,
448     out_advance_width: &mut f32,
449 ) -> bool {
450     hinting_instance
451         .0
452         .as_ref()
453         .and_then(|instance| {
454             let draw_settings = DrawSettings::hinted(instance, false);
455 
456             let outlines = outlines.0.as_ref()?;
457             let glyph = outlines.get(GlyphId::new(glyph_id))?;
458             let mut pen_dump = NoOpPen {};
459             let adjusted_metrics = glyph.draw(draw_settings, &mut pen_dump).ok()?;
460             adjusted_metrics.advance_width.map(|adjusted_advance| {
461                 *out_advance_width = adjusted_advance;
462                 ()
463             })
464         })
465         .is_some()
466 }
467 
units_per_em_or_zero(font_ref: &BridgeFontRef) -> u16468 fn units_per_em_or_zero(font_ref: &BridgeFontRef) -> u16 {
469     font_ref
470         .with_font(|f| Some(f.head().ok()?.units_per_em()))
471         .unwrap_or_default()
472 }
473 
convert_metrics(skrifa_metrics: &Metrics) -> ffi::Metrics474 fn convert_metrics(skrifa_metrics: &Metrics) -> ffi::Metrics {
475     ffi::Metrics {
476         top: skrifa_metrics.bounds.map_or(0.0, |b| b.y_max),
477         bottom: skrifa_metrics.bounds.map_or(0.0, |b| b.y_min),
478         x_min: skrifa_metrics.bounds.map_or(0.0, |b| b.x_min),
479         x_max: skrifa_metrics.bounds.map_or(0.0, |b| b.x_max),
480         ascent: skrifa_metrics.ascent,
481         descent: skrifa_metrics.descent,
482         leading: skrifa_metrics.leading,
483         avg_char_width: skrifa_metrics.average_width.unwrap_or(0.0),
484         max_char_width: skrifa_metrics.max_width.unwrap_or(0.0),
485         x_height: -skrifa_metrics.x_height.unwrap_or(0.0),
486         cap_height: -skrifa_metrics.cap_height.unwrap_or(0.0),
487         underline_position: skrifa_metrics.underline.map_or(f32::NAN, |u| u.offset),
488         underline_thickness: skrifa_metrics.underline.map_or(f32::NAN, |u| u.thickness),
489         strikeout_position: skrifa_metrics.strikeout.map_or(f32::NAN, |s| s.offset),
490         strikeout_thickness: skrifa_metrics.strikeout.map_or(f32::NAN, |s| s.thickness),
491     }
492 }
493 
get_skia_metrics( font_ref: &BridgeFontRef, size: f32, coords: &BridgeNormalizedCoords, ) -> ffi::Metrics494 fn get_skia_metrics(
495     font_ref: &BridgeFontRef,
496     size: f32,
497     coords: &BridgeNormalizedCoords,
498 ) -> ffi::Metrics {
499     font_ref
500         .with_font(|f| {
501             let fontations_metrics =
502                 Metrics::new(f, Size::new(size), coords.normalized_coords.coords());
503             Some(convert_metrics(&fontations_metrics))
504         })
505         .unwrap_or_default()
506 }
507 
get_unscaled_metrics(font_ref: &BridgeFontRef, coords: &BridgeNormalizedCoords) -> ffi::Metrics508 fn get_unscaled_metrics(font_ref: &BridgeFontRef, coords: &BridgeNormalizedCoords) -> ffi::Metrics {
509     font_ref
510         .with_font(|f| {
511             let fontations_metrics =
512                 Metrics::new(f, Size::unscaled(), coords.normalized_coords.coords());
513             Some(convert_metrics(&fontations_metrics))
514         })
515         .unwrap_or_default()
516 }
517 
get_localized_strings<'a>(font_ref: &'a BridgeFontRef<'a>) -> Box<BridgeLocalizedStrings<'a>>518 fn get_localized_strings<'a>(font_ref: &'a BridgeFontRef<'a>) -> Box<BridgeLocalizedStrings<'a>> {
519     Box::new(BridgeLocalizedStrings {
520         localized_strings: font_ref
521             .with_font(|f| Some(f.localized_strings(StringId::FAMILY_NAME)))
522             .unwrap_or_default(),
523     })
524 }
525 
localized_name_next( bridge_localized_strings: &mut BridgeLocalizedStrings, out_localized_name: &mut BridgeLocalizedName, ) -> bool526 fn localized_name_next(
527     bridge_localized_strings: &mut BridgeLocalizedStrings,
528     out_localized_name: &mut BridgeLocalizedName,
529 ) -> bool {
530     match bridge_localized_strings.localized_strings.next() {
531         Some(localized_string) => {
532             out_localized_name.string = localized_string.to_string();
533             // TODO(b/307906051): Remove the suffix before shipping.
534             out_localized_name.string.push_str(" (Fontations)");
535             out_localized_name.language = localized_string
536                 .language()
537                 .map(|l| l.to_string())
538                 .unwrap_or_default();
539             true
540         }
541         _ => false,
542     }
543 }
544 
english_or_first_font_name(font_ref: &BridgeFontRef, name_id: StringId) -> Option<String>545 fn english_or_first_font_name(font_ref: &BridgeFontRef, name_id: StringId) -> Option<String> {
546     font_ref.with_font(|f| {
547         f.localized_strings(name_id)
548             .english_or_first()
549             .map(|localized_string| localized_string.to_string())
550     })
551 }
552 
family_name(font_ref: &BridgeFontRef) -> String553 fn family_name(font_ref: &BridgeFontRef) -> String {
554     font_ref.with_font(|f| {
555         // https://learn.microsoft.com/en-us/typography/opentype/spec/os2#fsselection
556         // Bit 8 of the `fsSelection' field in the `OS/2' table indicates a WWS-only font face.
557         // When this bit is set it means *do not* use the WWS strings.
558         let use_wws = !f.os2().map_or(false, |t| t.fs_selection().contains(SelectionFlags::WWS));
559         if use_wws { english_or_first_font_name(font_ref, StringId::WWS_FAMILY_NAME) } else { None }
560          .or_else(|| english_or_first_font_name(font_ref, StringId::TYPOGRAPHIC_FAMILY_NAME))
561          .or_else(|| english_or_first_font_name(font_ref, StringId::FAMILY_NAME))
562     }).unwrap_or_default()
563 }
564 
postscript_name(font_ref: &BridgeFontRef, out_string: &mut String) -> bool565 fn postscript_name(font_ref: &BridgeFontRef, out_string: &mut String) -> bool {
566     let postscript_name = english_or_first_font_name(font_ref, StringId::POSTSCRIPT_NAME);
567     match postscript_name {
568         Some(name) => {
569             *out_string = name;
570             true
571         }
572         _ => false,
573     }
574 }
575 
resolve_palette( font_ref: &BridgeFontRef, base_palette: u16, palette_overrides: &[PaletteOverride], ) -> Vec<u32>576 fn resolve_palette(
577     font_ref: &BridgeFontRef,
578     base_palette: u16,
579     palette_overrides: &[PaletteOverride],
580 ) -> Vec<u32> {
581     let cpal_to_vector = |cpal: &Cpal, palette_index| -> Option<Vec<u32>> {
582         let start_index: usize = cpal
583             .color_record_indices()
584             .get(usize::from(palette_index))?
585             .get()
586             .into();
587         let num_entries: usize = cpal.num_palette_entries().into();
588         let color_records = cpal.color_records_array()?.ok()?;
589         Some(
590             color_records
591                 .get(start_index..start_index + num_entries)?
592                 .iter()
593                 .map(|record| {
594                     u32::from_be_bytes([record.alpha, record.red, record.green, record.blue])
595                 })
596                 .collect(),
597         )
598     };
599 
600     font_ref
601         .with_font(|f| {
602             let cpal = f.cpal().ok()?;
603 
604             let mut palette = cpal_to_vector(&cpal, base_palette).or(cpal_to_vector(&cpal, 0))?;
605 
606             for override_entry in palette_overrides {
607                 let index = override_entry.index as usize;
608                 if index < palette.len() {
609                     palette[index] = override_entry.color_8888;
610                 }
611             }
612             Some(palette)
613         })
614         .unwrap_or_default()
615 }
616 
has_colr_glyph(font_ref: &BridgeFontRef, format: ColorGlyphFormat, glyph_id: u16) -> bool617 fn has_colr_glyph(font_ref: &BridgeFontRef, format: ColorGlyphFormat, glyph_id: u16) -> bool {
618     font_ref
619         .with_font(|f| {
620             let colrv1_paintable = f
621                 .color_glyphs()
622                 .get_with_format(GlyphId::new(glyph_id), format);
623             Some(colrv1_paintable.is_some())
624         })
625         .unwrap_or_default()
626 }
627 
has_colrv1_glyph(font_ref: &BridgeFontRef, glyph_id: u16) -> bool628 fn has_colrv1_glyph(font_ref: &BridgeFontRef, glyph_id: u16) -> bool {
629     has_colr_glyph(font_ref, ColorGlyphFormat::ColrV1, glyph_id)
630 }
631 
has_colrv0_glyph(font_ref: &BridgeFontRef, glyph_id: u16) -> bool632 fn has_colrv0_glyph(font_ref: &BridgeFontRef, glyph_id: u16) -> bool {
633     has_colr_glyph(font_ref, ColorGlyphFormat::ColrV0, glyph_id)
634 }
635 
get_colrv1_clip_box( font_ref: &BridgeFontRef, coords: &BridgeNormalizedCoords, glyph_id: u16, size: f32, clip_box: &mut ClipBox, ) -> bool636 fn get_colrv1_clip_box(
637     font_ref: &BridgeFontRef,
638     coords: &BridgeNormalizedCoords,
639     glyph_id: u16,
640     size: f32,
641     clip_box: &mut ClipBox,
642 ) -> bool {
643     let size = match size {
644         x if x == 0.0 => {
645             return false;
646         }
647         _ => Size::new(size),
648     };
649     font_ref
650         .with_font(|f| {
651             match f
652                 .color_glyphs()
653                 .get_with_format(GlyphId::new(glyph_id), ColorGlyphFormat::ColrV1)?
654                 .bounding_box(coords.normalized_coords.coords(), size)
655             {
656                 Some(bounding_box) => {
657                     *clip_box = ClipBox {
658                         x_min: bounding_box.x_min,
659                         y_min: bounding_box.y_min,
660                         x_max: bounding_box.x_max,
661                         y_max: bounding_box.y_max,
662                     };
663                     Some(true)
664                 }
665                 _ => None,
666             }
667         })
668         .unwrap_or_default()
669 }
670 
671 /// Implements the behavior expected for `SkTypeface::getTableData`, compare
672 /// documentation for this method and the FreeType implementation in Skia.
673 /// * If the target data array is empty, do not copy any data into it, but
674 ///   return the size of the table.
675 /// * If the target data buffer is shorted than from offset to the end of the
676 ///   table, truncate the data.
677 /// * If offset is longer than the table's length, return 0.
table_data(font_ref: &BridgeFontRef, tag: u32, offset: usize, data: &mut [u8]) -> usize678 fn table_data(font_ref: &BridgeFontRef, tag: u32, offset: usize, data: &mut [u8]) -> usize {
679     let table_data = font_ref
680         .with_font(|f| f.table_data(Tag::from_be_bytes(tag.to_be_bytes())))
681         .unwrap_or_default();
682     let table_data = table_data.as_ref();
683     // Remaining table data size measured from offset to end, or 0 if offset is
684     // too large.
685     let mut to_copy_length = table_data.len().saturating_sub(offset);
686     match data.len() {
687         0 => to_copy_length,
688         _ => {
689             to_copy_length = to_copy_length.min(data.len());
690             let table_offset_data = table_data
691                 .get(offset..offset + to_copy_length)
692                 .unwrap_or_default();
693             data.get_mut(..table_offset_data.len())
694                 .map_or(0, |data_slice| {
695                     data_slice.copy_from_slice(table_offset_data);
696                     data_slice.len()
697                 })
698         }
699     }
700 }
701 
table_tags(font_ref: &BridgeFontRef, tags: &mut [u32]) -> u16702 fn table_tags(font_ref: &BridgeFontRef, tags: &mut [u32]) -> u16 {
703     font_ref
704         .with_font(|f| {
705             let table_directory = &f.table_directory;
706             let table_tags_iter = table_directory
707                 .table_records()
708                 .iter()
709                 .map(|table| u32::from_be_bytes(table.tag.get().into_bytes()));
710             tags.iter_mut()
711                 .zip(table_tags_iter)
712                 .for_each(|(out_tag, table_tag)| *out_tag = table_tag);
713             Some(table_directory.num_tables())
714         })
715         .unwrap_or_default()
716 }
717 
variation_position( coords: &BridgeNormalizedCoords, coordinates: &mut [SkiaDesignCoordinate], ) -> isize718 fn variation_position(
719     coords: &BridgeNormalizedCoords,
720     coordinates: &mut [SkiaDesignCoordinate],
721 ) -> isize {
722     if !coordinates.is_empty() {
723         if coords.filtered_user_coords.len() > coordinates.len() {
724             return -1;
725         }
726         let skia_design_coordinates =
727             coords
728                 .filtered_user_coords
729                 .iter()
730                 .map(|setting| SkiaDesignCoordinate {
731                     axis: u32::from_be_bytes(setting.selector.into_bytes()),
732                     value: setting.value,
733                 });
734         for (i, coord) in skia_design_coordinates.enumerate() {
735             coordinates[i] = coord;
736         }
737     }
738     coords.filtered_user_coords.len().try_into().unwrap()
739 }
740 
coordinates_for_shifted_named_instance_index( font_ref: &BridgeFontRef, shifted_index: u32, coords: &mut [SkiaDesignCoordinate], ) -> isize741 fn coordinates_for_shifted_named_instance_index(
742     font_ref: &BridgeFontRef,
743     shifted_index: u32,
744     coords: &mut [SkiaDesignCoordinate],
745 ) -> isize {
746     font_ref
747         .with_font(|f| {
748             let fvar = f.fvar().ok()?;
749             let instances = fvar.instances().ok()?;
750             let index: usize = ((shifted_index >> 16) - 1).try_into().unwrap();
751             let instance_coords = instances.get(index).ok()?.coordinates;
752 
753             if coords.len() != 0 {
754                 if coords.len() < instance_coords.len() {
755                     return None;
756                 }
757                 let axis_coords = f.axes().iter().zip(instance_coords.iter()).enumerate();
758                 for (i, axis_coord) in axis_coords {
759                     coords[i] = SkiaDesignCoordinate {
760                         axis: u32::from_be_bytes(axis_coord.0.tag().to_be_bytes()),
761                         value: axis_coord.1.get().to_f32(),
762                     };
763                 }
764             }
765 
766             Some(instance_coords.len() as isize)
767         })
768         .unwrap_or(-1)
769 }
770 
populate_axes(font_ref: &BridgeFontRef, mut axis_wrapper: Pin<&mut AxisWrapper>) -> isize771 fn populate_axes(font_ref: &BridgeFontRef, mut axis_wrapper: Pin<&mut AxisWrapper>) -> isize {
772     font_ref
773         .with_font(|f| {
774             let axes = f.axes();
775             // Populate incoming allocated SkFontParameters::Variation::Axis[] only when a
776             // buffer is passed.
777             if axis_wrapper.as_ref().size() > 0 {
778                 for (i, axis) in axes.iter().enumerate() {
779                     if !axis_wrapper.as_mut().populate_axis(
780                         i,
781                         u32::from_be_bytes(axis.tag().into_bytes()),
782                         axis.min_value(),
783                         axis.default_value(),
784                         axis.max_value(),
785                         axis.is_hidden(),
786                     ) {
787                         return None;
788                     }
789                 }
790             }
791             isize::try_from(axes.len()).ok()
792         })
793         .unwrap_or(-1)
794 }
795 
make_font_ref_internal<'a>(font_data: &'a [u8], index: u32) -> Result<FontRef<'a>, ReadError>796 fn make_font_ref_internal<'a>(font_data: &'a [u8], index: u32) -> Result<FontRef<'a>, ReadError> {
797     match FileRef::new(font_data) {
798         Ok(file_ref) => match file_ref {
799             FileRef::Font(font_ref) => {
800                 // Indices with the higher bits set are meaningful here and do not result in an
801                 // error, as they may refer to a named instance and are taken into account by the
802                 // Fontations typeface implementation,
803                 // compare `coordinates_for_shifted_named_instance_index()`.
804                 if index & 0xFFFF > 0 {
805                     Err(ReadError::InvalidCollectionIndex(index))
806                 } else {
807                     Ok(font_ref)
808                 }
809             }
810             FileRef::Collection(collection) => collection.get(index),
811         },
812         Err(e) => Err(e),
813     }
814 }
815 
make_font_ref<'a>(font_data: &'a [u8], index: u32) -> Box<BridgeFontRef<'a>>816 fn make_font_ref<'a>(font_data: &'a [u8], index: u32) -> Box<BridgeFontRef<'a>> {
817     Box::new(BridgeFontRef(make_font_ref_internal(font_data, index).ok()))
818 }
819 
font_ref_is_valid(bridge_font_ref: &BridgeFontRef) -> bool820 fn font_ref_is_valid(bridge_font_ref: &BridgeFontRef) -> bool {
821     bridge_font_ref.0.is_some()
822 }
823 
get_outline_collection<'a>(font_ref: &'a BridgeFontRef<'a>) -> Box<BridgeOutlineCollection<'a>>824 fn get_outline_collection<'a>(font_ref: &'a BridgeFontRef<'a>) -> Box<BridgeOutlineCollection<'a>> {
825     Box::new(
826         font_ref
827             .with_font(|f| Some(BridgeOutlineCollection(Some(f.outline_glyphs()))))
828             .unwrap_or_default(),
829     )
830 }
831 
font_or_collection<'a>(font_data: &'a [u8], num_fonts: &mut u32) -> bool832 fn font_or_collection<'a>(font_data: &'a [u8], num_fonts: &mut u32) -> bool {
833     match FileRef::new(font_data) {
834         Ok(FileRef::Collection(collection)) => {
835             *num_fonts = collection.len();
836             true
837         }
838         Ok(FileRef::Font(_)) => {
839             *num_fonts = 0u32;
840             true
841         }
842         _ => false,
843     }
844 }
845 
resolve_into_normalized_coords( font_ref: &BridgeFontRef, design_coords: &[SkiaDesignCoordinate], ) -> Box<BridgeNormalizedCoords>846 fn resolve_into_normalized_coords(
847     font_ref: &BridgeFontRef,
848     design_coords: &[SkiaDesignCoordinate],
849 ) -> Box<BridgeNormalizedCoords> {
850     let variation_tuples = design_coords
851         .iter()
852         .map(|coord| (Tag::from_be_bytes(coord.axis.to_be_bytes()), coord.value));
853     let bridge_normalized_coords = font_ref
854         .with_font(|f| {
855             let merged_defaults_with_user = f
856                 .axes()
857                 .iter()
858                 .map(|axis| (axis.tag(), axis.default_value()))
859                 .chain(design_coords.iter().map(|user_coord| {
860                     (
861                         Tag::from_be_bytes(user_coord.axis.to_be_bytes()),
862                         user_coord.value,
863                     )
864                 }));
865             Some(BridgeNormalizedCoords {
866                 filtered_user_coords: f.axes().filter(merged_defaults_with_user).collect(),
867                 normalized_coords: f.axes().location(variation_tuples),
868             })
869         })
870         .unwrap_or_default();
871     Box::new(bridge_normalized_coords)
872 }
873 
normalized_coords_equal(a: &BridgeNormalizedCoords, b: &BridgeNormalizedCoords) -> bool874 fn normalized_coords_equal(a: &BridgeNormalizedCoords, b: &BridgeNormalizedCoords) -> bool {
875     a.normalized_coords.coords() == b.normalized_coords.coords()
876 }
877 
draw_colr_glyph( font_ref: &BridgeFontRef, coords: &BridgeNormalizedCoords, glyph_id: u16, color_painter: Pin<&mut ColorPainterWrapper>, ) -> bool878 fn draw_colr_glyph(
879     font_ref: &BridgeFontRef,
880     coords: &BridgeNormalizedCoords,
881     glyph_id: u16,
882     color_painter: Pin<&mut ColorPainterWrapper>,
883 ) -> bool {
884     let mut color_painter_impl = ColorPainterImpl {
885         color_painter_wrapper: color_painter,
886     };
887     font_ref
888         .with_font(|f| {
889             let paintable = f.color_glyphs().get(GlyphId::new(glyph_id))?;
890             paintable
891                 .paint(coords.normalized_coords.coords(), &mut color_painter_impl)
892                 .ok()
893         })
894         .is_some()
895 }
896 
next_color_stop(color_stops: &mut BridgeColorStops, out_stop: &mut ColorStop) -> bool897 fn next_color_stop(color_stops: &mut BridgeColorStops, out_stop: &mut ColorStop) -> bool {
898     if let Some(color_stop) = color_stops.stops_iterator.next() {
899         out_stop.alpha = color_stop.alpha;
900         out_stop.stop = color_stop.offset;
901         out_stop.palette_index = color_stop.palette_index;
902         true
903     } else {
904         false
905     }
906 }
907 
num_color_stops(color_stops: &BridgeColorStops) -> usize908 fn num_color_stops(color_stops: &BridgeColorStops) -> usize {
909     color_stops.num_stops
910 }
911 
912 #[allow(non_upper_case_globals)]
get_font_style( font_ref: &BridgeFontRef, coords: &BridgeNormalizedCoords, style: &mut BridgeFontStyle, ) -> bool913 fn get_font_style(
914     font_ref: &BridgeFontRef,
915     coords: &BridgeNormalizedCoords,
916     style: &mut BridgeFontStyle,
917 ) -> bool {
918     font_ref
919         .with_font(|f| {
920             let attrs = f.attributes();
921             let mut skia_weight = attrs.weight.value().round() as i32;
922             let mut skia_slant = match attrs.style {
923                 Style::Normal => 0,
924                 Style::Italic => 1,
925                 _ => 2, /* kOblique_Slant */
926             };
927             //0.5, 0.625, 0.75, 0.875, 1.0, 1.125, 1.25, 1.5, 2.0 map to 1-9
928             let mut skia_width = match attrs.stretch.ratio() {
929                 x if x <= 0.5625 => 1,
930                 x if x <= 0.6875 => 2,
931                 x if x <= 0.8125 => 3,
932                 x if x <= 0.9375 => 4,
933                 x if x <= 1.0625 => 5,
934                 x if x <= 1.1875 => 6,
935                 x if x <= 1.3750 => 7,
936                 x if x <= 1.7500 => 8,
937                 _ => 9,
938             };
939 
940             const wght: Tag = Tag::new(b"wght");
941             const wdth: Tag = Tag::new(b"wdth");
942             const slnt: Tag = Tag::new(b"slnt");
943 
944             for user_coord in coords.filtered_user_coords.iter() {
945                 match user_coord.selector {
946                     wght => skia_weight = user_coord.value.round() as i32,
947                     // 50, 62.5, 75, 87.5, 100, 112.5, 125, 150, 200 map to 1-9
948                     wdth => skia_width = match user_coord.value {
949                         x if x <=  56.25 => 1,
950                         x if x <=  68.75 => 2,
951                         x if x <=  81.25 => 3,
952                         x if x <=  93.75 => 4,
953                         x if x <= 106.25 => 5,
954                         x if x <= 118.75 => 6,
955                         x if x <= 137.50 => 7,
956                         x if x <= 175.00 => 8,
957                         _ => 9,
958                     },
959                     slnt => skia_slant = if skia_slant == 1 /* kItalic_Slant */ {
960                                              skia_slant
961                                          } else if user_coord.value == 0.0 {
962                                              0 /* kUpright_Slant */
963                                          } else {
964                                              2 /* kOblique_Slant */
965                                          },
966                     _ => (),
967                 }
968             }
969 
970             *style = BridgeFontStyle {
971                 weight: skia_weight,
972                 slant: skia_slant,
973                 width: skia_width,
974             };
975             Some(true)
976         })
977         .unwrap_or_default()
978 }
979 
is_embeddable(font_ref: &BridgeFontRef) -> bool980 fn is_embeddable(font_ref: &BridgeFontRef) -> bool {
981     font_ref
982         .with_font(|f| {
983             let fs_type = f.os2().ok()?.fs_type();
984             // https://learn.microsoft.com/en-us/typography/opentype/spec/os2#fstype
985             // Bit 2 and bit 9 must be cleared, "Restricted License embedding" and
986             // "Bitmap embedding only" must both be unset.
987             // Implemented to match SkTypeface_FreeType::onGetAdvancedMetrics.
988             Some(fs_type & 0x202 == 0)
989         })
990         .unwrap_or(true)
991 }
992 
is_subsettable(font_ref: &BridgeFontRef) -> bool993 fn is_subsettable(font_ref: &BridgeFontRef) -> bool {
994     font_ref
995         .with_font(|f| {
996             let fs_type = f.os2().ok()?.fs_type();
997             // https://learn.microsoft.com/en-us/typography/opentype/spec/os2#fstype
998             Some((fs_type & 0x100) == 0)
999         })
1000         .unwrap_or(true)
1001 }
1002 
is_fixed_pitch(font_ref: &BridgeFontRef) -> bool1003 fn is_fixed_pitch(font_ref: &BridgeFontRef) -> bool {
1004     font_ref
1005         .with_font(|f| {
1006             // Compare DWriteFontTypeface::onGetAdvancedMetrics().
1007             Some(
1008                 f.post().ok()?.is_fixed_pitch() != 0
1009                     || f.hhea().ok()?.number_of_long_metrics() == 1,
1010             )
1011         })
1012         .unwrap_or_default()
1013 }
1014 
is_serif_style(font_ref: &BridgeFontRef) -> bool1015 fn is_serif_style(font_ref: &BridgeFontRef) -> bool {
1016     const FAMILY_TYPE_TEXT_AND_DISPLAY: u8 = 2;
1017     const SERIF_STYLE_COVE: u8 = 2;
1018     const SERIF_STYLE_TRIANGLE: u8 = 10;
1019     font_ref
1020         .with_font(|f| {
1021             // Compare DWriteFontTypeface::onGetAdvancedMetrics().
1022             let panose = f.os2().ok()?.panose_10();
1023             let family_type = panose[0];
1024 
1025             match family_type {
1026                 FAMILY_TYPE_TEXT_AND_DISPLAY => {
1027                     let serif_style = panose[1];
1028                     Some((SERIF_STYLE_COVE..=SERIF_STYLE_TRIANGLE).contains(&serif_style))
1029                 }
1030                 _ => None,
1031             }
1032         })
1033         .unwrap_or_default()
1034 }
1035 
is_script_style(font_ref: &BridgeFontRef) -> bool1036 fn is_script_style(font_ref: &BridgeFontRef) -> bool {
1037     const FAMILY_TYPE_SCRIPT: u8 = 3;
1038     font_ref
1039         .with_font(|f| {
1040             // Compare DWriteFontTypeface::onGetAdvancedMetrics().
1041             let family_type = f.os2().ok()?.panose_10()[0];
1042             Some(family_type == FAMILY_TYPE_SCRIPT)
1043         })
1044         .unwrap_or_default()
1045 }
1046 
italic_angle(font_ref: &BridgeFontRef) -> i321047 fn italic_angle(font_ref: &BridgeFontRef) -> i32 {
1048     font_ref
1049         .with_font(|f| Some(f.post().ok()?.italic_angle().to_i32()))
1050         .unwrap_or_default()
1051 }
1052 
1053 pub struct BridgeFontRef<'a>(Option<FontRef<'a>>);
1054 
1055 impl<'a> BridgeFontRef<'a> {
with_font<T>(&'a self, f: impl FnOnce(&'a FontRef) -> Option<T>) -> Option<T>1056     fn with_font<T>(&'a self, f: impl FnOnce(&'a FontRef) -> Option<T>) -> Option<T> {
1057         f(self.0.as_ref()?)
1058     }
1059 }
1060 
1061 #[derive(Default)]
1062 struct BridgeOutlineCollection<'a>(Option<OutlineGlyphCollection<'a>>);
1063 
1064 #[derive(Default)]
1065 struct BridgeNormalizedCoords {
1066     normalized_coords: Location,
1067     filtered_user_coords: Vec<VariationSetting>,
1068 }
1069 
1070 struct BridgeLocalizedStrings<'a> {
1071     #[allow(dead_code)]
1072     localized_strings: LocalizedStrings<'a>,
1073 }
1074 
1075 pub struct BridgeColorStops<'a> {
1076     pub stops_iterator: Box<dyn Iterator<Item = &'a skrifa::color::ColorStop> + 'a>,
1077     pub num_stops: usize,
1078 }
1079 
1080 mod bitmap {
1081 
1082     use read_fonts::{
1083         tables::{
1084             bitmap::{BitmapContent, BitmapData, BitmapDataFormat, BitmapMetrics, BitmapSize},
1085             sbix::{GlyphData, Strike},
1086         },
1087         FontRef, TableProvider,
1088     };
1089 
1090     use font_types::{BoundingBox, GlyphId};
1091     use skrifa::{
1092         instance::{LocationRef, Size},
1093         metrics::GlyphMetrics,
1094     };
1095 
1096     use crate::{ffi::BitmapMetrics as FfiBitmapMetrics, BridgeFontRef};
1097 
1098     pub enum BitmapPixelData<'a> {
1099         PngData(&'a [u8]),
1100     }
1101 
1102     struct CblcGlyph<'a> {
1103         bitmap_data: BitmapData<'a>,
1104         ppem_x: u8,
1105         ppem_y: u8,
1106     }
1107 
1108     struct SbixGlyph<'a> {
1109         glyph_data: GlyphData<'a>,
1110         ppem: u16,
1111     }
1112 
1113     #[derive(Default)]
1114     pub struct BridgeBitmapGlyph<'a> {
1115         pub data: Option<BitmapPixelData<'a>>,
1116         pub metrics: FfiBitmapMetrics,
1117     }
1118 
1119     trait StrikeSizeRetrievable {
strike_size(&self) -> f321120         fn strike_size(&self) -> f32;
1121     }
1122 
1123     impl StrikeSizeRetrievable for &BitmapSize {
strike_size(&self) -> f321124         fn strike_size(&self) -> f32 {
1125             self.ppem_y() as f32
1126         }
1127     }
1128 
1129     impl StrikeSizeRetrievable for Strike<'_> {
strike_size(&self) -> f321130         fn strike_size(&self) -> f32 {
1131             self.ppem() as f32
1132         }
1133     }
1134 
1135     // Find the nearest larger strike size, or if no larger one is available, the nearest smaller.
best_strike_size<T>(strikes: impl Iterator<Item = T>, font_size: f32) -> Option<T> where T: StrikeSizeRetrievable,1136     fn best_strike_size<T>(strikes: impl Iterator<Item = T>, font_size: f32) -> Option<T>
1137     where
1138         T: StrikeSizeRetrievable,
1139     {
1140         // After a bigger strike size is found, the order of strike sizes smaller
1141         // than the requested font size does not matter anymore. A new strike size
1142         // is only an improvement if it gets closer to the requested font size (and
1143         // is smaller than the current best, but bigger than font size). And vice
1144         // versa: As long as we have found only smaller ones so far, only any strike
1145         // size matters that is bigger than the current best.
1146         strikes.reduce(|best, entry| {
1147             let entry_size = entry.strike_size();
1148             if (entry_size >= font_size && entry_size < best.strike_size())
1149                 || (best.strike_size() < font_size && entry_size > best.strike_size())
1150             {
1151                 entry
1152             } else {
1153                 best
1154             }
1155         })
1156     }
1157 
sbix_glyph<'a>( font_ref: &'a FontRef, glyph_id: GlyphId, font_size: Option<f32>, ) -> Option<SbixGlyph<'a>>1158     fn sbix_glyph<'a>(
1159         font_ref: &'a FontRef,
1160         glyph_id: GlyphId,
1161         font_size: Option<f32>,
1162     ) -> Option<SbixGlyph<'a>> {
1163         let sbix = font_ref.sbix().ok()?;
1164         let mut strikes = sbix.strikes().iter().filter_map(|strike| strike.ok());
1165 
1166         let best_strike = match font_size {
1167             Some(size) => best_strike_size(strikes, size),
1168             _ => strikes.next(),
1169         }?;
1170 
1171         Some(SbixGlyph {
1172             ppem: best_strike.ppem(),
1173             glyph_data: best_strike.glyph_data(glyph_id).ok()??,
1174         })
1175     }
1176 
cblc_glyph<'a>( font_ref: &'a FontRef, glyph_id: GlyphId, font_size: Option<f32>, ) -> Option<CblcGlyph<'a>>1177     fn cblc_glyph<'a>(
1178         font_ref: &'a FontRef,
1179         glyph_id: GlyphId,
1180         font_size: Option<f32>,
1181     ) -> Option<CblcGlyph<'a>> {
1182         let cblc = font_ref.cblc().ok()?;
1183         let cbdt = font_ref.cbdt().ok()?;
1184 
1185         let strikes = &cblc.bitmap_sizes();
1186         let best_strike = font_size
1187             .and_then(|size| best_strike_size(strikes.iter(), size))
1188             .or(strikes.get(0))?;
1189 
1190         let location = best_strike.location(cblc.offset_data(), glyph_id).ok()?;
1191 
1192         Some(CblcGlyph {
1193             bitmap_data: cbdt.data(&location).ok()?,
1194             ppem_x: best_strike.ppem_x,
1195             ppem_y: best_strike.ppem_y,
1196         })
1197     }
1198 
has_bitmap_glyph(font_ref: &BridgeFontRef, glyph_id: u16) -> bool1199     pub fn has_bitmap_glyph(font_ref: &BridgeFontRef, glyph_id: u16) -> bool {
1200         let glyph_id = GlyphId::new(glyph_id);
1201         font_ref
1202             .with_font(|font| {
1203                 let has_sbix = sbix_glyph(font, glyph_id, None).is_some();
1204                 let has_cblc = cblc_glyph(font, glyph_id, None).is_some();
1205                 Some(has_sbix || has_cblc)
1206             })
1207             .unwrap_or_default()
1208     }
1209 
glyf_bounds(font_ref: &FontRef, glyph_id: GlyphId) -> Option<BoundingBox<i16>>1210     fn glyf_bounds(font_ref: &FontRef, glyph_id: GlyphId) -> Option<BoundingBox<i16>> {
1211         let glyf_table = font_ref.glyf().ok()?;
1212         let glyph = font_ref
1213             .loca(None)
1214             .ok()?
1215             .get_glyf(glyph_id, &glyf_table)
1216             .ok()??;
1217         Some(BoundingBox {
1218             x_min: glyph.x_min(),
1219             y_min: glyph.y_min(),
1220             x_max: glyph.x_max(),
1221             y_max: glyph.y_max(),
1222         })
1223     }
1224 
bitmap_glyph<'a>( font_ref: &'a BridgeFontRef, glyph_id: u16, font_size: f32, ) -> Box<BridgeBitmapGlyph<'a>>1225     pub unsafe fn bitmap_glyph<'a>(
1226         font_ref: &'a BridgeFontRef,
1227         glyph_id: u16,
1228         font_size: f32,
1229     ) -> Box<BridgeBitmapGlyph<'a>> {
1230         let glyph_id = GlyphId::new(glyph_id);
1231         font_ref
1232             .with_font(|font| {
1233                 if let Some(sbix_glyph) = sbix_glyph(font, glyph_id, Some(font_size)) {
1234                     // https://learn.microsoft.com/en-us/typography/opentype/spec/sbix
1235                     // "If there is a glyph contour, the glyph design space
1236                     // origin for the graphic is placed at the lower left corner
1237                     // of the glyph bounding box (xMin, yMin)."
1238                     let glyf_bb = glyf_bounds(font, glyph_id).unwrap_or_default();
1239                     let glyf_left_side_bearing =
1240                         GlyphMetrics::new(font, Size::unscaled(), LocationRef::default())
1241                             .left_side_bearing(glyph_id)
1242                             .unwrap_or_default();
1243 
1244                     return Some(Box::new(BridgeBitmapGlyph {
1245                         data: Some(BitmapPixelData::PngData(sbix_glyph.glyph_data.data())),
1246                         metrics: FfiBitmapMetrics {
1247                             bearing_x: glyf_left_side_bearing,
1248                             bearing_y: glyf_bb.y_min as f32,
1249                             inner_bearing_x: sbix_glyph.glyph_data.origin_offset_x() as f32,
1250                             inner_bearing_y: sbix_glyph.glyph_data.origin_offset_y() as f32,
1251                             ppem_x: sbix_glyph.ppem as f32,
1252                             ppem_y: sbix_glyph.ppem as f32,
1253                             placement_origin_bottom_left: true,
1254                             advance: f32::NAN,
1255                         },
1256                     }));
1257                 } else if let Some(cblc_glyph) = cblc_glyph(font, glyph_id, Some(font_size)) {
1258                     let (bearing_x, bearing_y, advance) = match cblc_glyph.bitmap_data.metrics {
1259                         BitmapMetrics::Small(small_metrics) => (
1260                             small_metrics.bearing_x() as f32,
1261                             small_metrics.bearing_y() as f32,
1262                             small_metrics.advance as f32,
1263                         ),
1264                         BitmapMetrics::Big(big_metrics) => (
1265                             big_metrics.hori_bearing_x() as f32,
1266                             big_metrics.hori_bearing_y() as f32,
1267                             big_metrics.hori_advance as f32,
1268                         ),
1269                     };
1270                     if let BitmapContent::Data(BitmapDataFormat::Png, png_buffer) =
1271                         cblc_glyph.bitmap_data.content
1272                     {
1273                         return Some(Box::new(BridgeBitmapGlyph {
1274                             data: Some(BitmapPixelData::PngData(png_buffer)),
1275                             metrics: FfiBitmapMetrics {
1276                                 bearing_x: 0.0,
1277                                 bearing_y: 0.0,
1278                                 inner_bearing_x: bearing_x,
1279                                 inner_bearing_y: bearing_y,
1280                                 ppem_x: cblc_glyph.ppem_x as f32,
1281                                 ppem_y: cblc_glyph.ppem_y as f32,
1282                                 placement_origin_bottom_left: false,
1283                                 advance: advance,
1284                             },
1285                         }));
1286                     }
1287                 }
1288                 None
1289             })
1290             .unwrap_or_default()
1291     }
1292 
png_data<'a>(bitmap_glyph: &'a BridgeBitmapGlyph) -> &'a [u8]1293     pub unsafe fn png_data<'a>(bitmap_glyph: &'a BridgeBitmapGlyph) -> &'a [u8] {
1294         match bitmap_glyph.data {
1295             Some(BitmapPixelData::PngData(glyph_data)) => glyph_data,
1296             _ => &[],
1297         }
1298     }
1299 
bitmap_metrics<'a>(bitmap_glyph: &'a BridgeBitmapGlyph) -> &'a FfiBitmapMetrics1300     pub unsafe fn bitmap_metrics<'a>(bitmap_glyph: &'a BridgeBitmapGlyph) -> &'a FfiBitmapMetrics {
1301         &bitmap_glyph.metrics
1302     }
1303 }
1304 
1305 pub struct BridgeMappingIndex(MappingIndex);
1306 pub struct BridgeHintingInstance(Option<HintingInstance>);
1307 
1308 #[cxx::bridge(namespace = "fontations_ffi")]
1309 mod ffi {
1310     struct ColorStop {
1311         stop: f32,
1312         palette_index: u16,
1313         alpha: f32,
1314     }
1315 
1316     #[derive(Default)]
1317     struct Metrics {
1318         top: f32,
1319         ascent: f32,
1320         descent: f32,
1321         bottom: f32,
1322         leading: f32,
1323         avg_char_width: f32,
1324         max_char_width: f32,
1325         x_min: f32,
1326         x_max: f32,
1327         x_height: f32,
1328         cap_height: f32,
1329         underline_position: f32,
1330         underline_thickness: f32,
1331         strikeout_position: f32,
1332         strikeout_thickness: f32,
1333     }
1334 
1335     struct BridgeLocalizedName {
1336         string: String,
1337         language: String,
1338     }
1339 
1340     #[derive(PartialEq, Debug, Default)]
1341     struct SkiaDesignCoordinate {
1342         axis: u32,
1343         value: f32,
1344     }
1345 
1346     struct BridgeScalerMetrics {
1347         has_overlaps: bool,
1348     }
1349 
1350     struct PaletteOverride {
1351         index: u16,
1352         color_8888: u32,
1353     }
1354 
1355     struct ClipBox {
1356         x_min: f32,
1357         y_min: f32,
1358         x_max: f32,
1359         y_max: f32,
1360     }
1361 
1362     struct Transform {
1363         xx: f32,
1364         xy: f32,
1365         yx: f32,
1366         yy: f32,
1367         dx: f32,
1368         dy: f32,
1369     }
1370 
1371     struct FillLinearParams {
1372         x0: f32,
1373         y0: f32,
1374         x1: f32,
1375         y1: f32,
1376     }
1377 
1378     struct FillRadialParams {
1379         x0: f32,
1380         y0: f32,
1381         r0: f32,
1382         x1: f32,
1383         y1: f32,
1384         r1: f32,
1385     }
1386 
1387     struct FillSweepParams {
1388         x0: f32,
1389         y0: f32,
1390         start_angle: f32,
1391         end_angle: f32,
1392     }
1393 
1394     // This type is used to mirror SkFontStyle values for Weight, Slant and Width
1395     #[derive(Default)]
1396     pub struct BridgeFontStyle {
1397         pub weight: i32,
1398         pub slant: i32,
1399         pub width: i32,
1400     }
1401 
1402     #[derive(Default)]
1403     struct BitmapMetrics {
1404         // Outer glyph bearings that affect the computed bounds. We distinguish
1405         // those here from `inner_bearing_*` to account for CoreText behavior in
1406         // SBIX placement. Where the sbix originOffsetX/Y are applied only
1407         // within the bounds. Specified in font units.
1408         // 0 for CBDT, CBLC.
1409         bearing_x: f32,
1410         bearing_y: f32,
1411         // Scale factors to scale image to 1em.
1412         ppem_x: f32,
1413         ppem_y: f32,
1414         // Account for the fact that Sbix and CBDT/CBLC have a different origin
1415         // definition.
1416         placement_origin_bottom_left: bool,
1417         // Specified as a pixel value, to be scaled by `ppem_*` as an
1418         // offset applied to placing the image within the bounds rectangle.
1419         inner_bearing_x: f32,
1420         inner_bearing_y: f32,
1421         // Some, but not all, bitmap glyphs have a special bitmap advance
1422         advance: f32,
1423     }
1424 
1425     extern "Rust" {
1426         type BridgeFontRef<'a>;
make_font_ref<'a>(font_data: &'a [u8], index: u32) -> Box<BridgeFontRef<'a>>1427         unsafe fn make_font_ref<'a>(font_data: &'a [u8], index: u32) -> Box<BridgeFontRef<'a>>;
1428         // Returns whether BridgeFontRef is a valid font containing at
1429         // least a valid sfnt structure from which tables can be
1430         // accessed. This is what instantiation in make_font_ref checks
1431         // for. (see FontRef::new in read_fonts's lib.rs). Implemented
1432         // by returning whether the option is Some() and thus whether a
1433         // FontRef instantiation succeeded and a table directory was
1434         // accessible.
font_ref_is_valid(bridge_font_ref: &BridgeFontRef) -> bool1435         fn font_ref_is_valid(bridge_font_ref: &BridgeFontRef) -> bool;
1436 
1437         type BridgeOutlineCollection<'a>;
get_outline_collection<'a>( font_ref: &'a BridgeFontRef<'a>, ) -> Box<BridgeOutlineCollection<'a>>1438         unsafe fn get_outline_collection<'a>(
1439             font_ref: &'a BridgeFontRef<'a>,
1440         ) -> Box<BridgeOutlineCollection<'a>>;
1441 
1442         /// Returns true on a font or collection, sets `num_fonts``
1443         /// to 0 if single font file, and to > 0 for a TrueType collection.
1444         /// Returns false if the data cannot be interpreted as a font or collection.
font_or_collection<'a>(font_data: &'a [u8], num_fonts: &mut u32) -> bool1445         unsafe fn font_or_collection<'a>(font_data: &'a [u8], num_fonts: &mut u32) -> bool;
1446 
1447         type BridgeMappingIndex;
make_mapping_index<'a>(font_ref: &'a BridgeFontRef) -> Box<BridgeMappingIndex>1448         unsafe fn make_mapping_index<'a>(font_ref: &'a BridgeFontRef) -> Box<BridgeMappingIndex>;
1449 
1450         type BridgeHintingInstance;
make_hinting_instance<'a>( outlines: &BridgeOutlineCollection, size: f32, coords: &BridgeNormalizedCoords, do_lcd_antialiasing: bool, lcd_orientation_vertical: bool, preserve_linear_metrics: bool, ) -> Box<BridgeHintingInstance>1451         unsafe fn make_hinting_instance<'a>(
1452             outlines: &BridgeOutlineCollection,
1453             size: f32,
1454             coords: &BridgeNormalizedCoords,
1455             do_lcd_antialiasing: bool,
1456             lcd_orientation_vertical: bool,
1457             preserve_linear_metrics: bool,
1458         ) -> Box<BridgeHintingInstance>;
make_mono_hinting_instance<'a>( outlines: &BridgeOutlineCollection, size: f32, coords: &BridgeNormalizedCoords, ) -> Box<BridgeHintingInstance>1459         unsafe fn make_mono_hinting_instance<'a>(
1460             outlines: &BridgeOutlineCollection,
1461             size: f32,
1462             coords: &BridgeNormalizedCoords,
1463         ) -> Box<BridgeHintingInstance>;
no_hinting_instance<'a>() -> Box<BridgeHintingInstance>1464         unsafe fn no_hinting_instance<'a>() -> Box<BridgeHintingInstance>;
1465 
lookup_glyph_or_zero( font_ref: &BridgeFontRef, map: &BridgeMappingIndex, codepoint: u32, ) -> u161466         fn lookup_glyph_or_zero(
1467             font_ref: &BridgeFontRef,
1468             map: &BridgeMappingIndex,
1469             codepoint: u32,
1470         ) -> u16;
1471 
get_path( outlines: &BridgeOutlineCollection, glyph_id: u16, size: f32, coords: &BridgeNormalizedCoords, hinting_instance: &BridgeHintingInstance, path_wrapper: Pin<&mut PathWrapper>, scaler_metrics: &mut BridgeScalerMetrics, ) -> bool1472         fn get_path(
1473             outlines: &BridgeOutlineCollection,
1474             glyph_id: u16,
1475             size: f32,
1476             coords: &BridgeNormalizedCoords,
1477             hinting_instance: &BridgeHintingInstance,
1478             path_wrapper: Pin<&mut PathWrapper>,
1479             scaler_metrics: &mut BridgeScalerMetrics,
1480         ) -> bool;
unhinted_advance_width_or_zero( font_ref: &BridgeFontRef, size: f32, coords: &BridgeNormalizedCoords, glyph_id: u16, ) -> f321481         fn unhinted_advance_width_or_zero(
1482             font_ref: &BridgeFontRef,
1483             size: f32,
1484             coords: &BridgeNormalizedCoords,
1485             glyph_id: u16,
1486         ) -> f32;
scaler_hinted_advance_width( outlines: &BridgeOutlineCollection, hinting_instance: &BridgeHintingInstance, glyph_id: u16, out_advance_width: &mut f32, ) -> bool1487         fn scaler_hinted_advance_width(
1488             outlines: &BridgeOutlineCollection,
1489             hinting_instance: &BridgeHintingInstance,
1490             glyph_id: u16,
1491             out_advance_width: &mut f32,
1492         ) -> bool;
units_per_em_or_zero(font_ref: &BridgeFontRef) -> u161493         fn units_per_em_or_zero(font_ref: &BridgeFontRef) -> u16;
get_skia_metrics( font_ref: &BridgeFontRef, size: f32, coords: &BridgeNormalizedCoords, ) -> Metrics1494         fn get_skia_metrics(
1495             font_ref: &BridgeFontRef,
1496             size: f32,
1497             coords: &BridgeNormalizedCoords,
1498         ) -> Metrics;
get_unscaled_metrics( font_ref: &BridgeFontRef, coords: &BridgeNormalizedCoords, ) -> Metrics1499         fn get_unscaled_metrics(
1500             font_ref: &BridgeFontRef,
1501             coords: &BridgeNormalizedCoords,
1502         ) -> Metrics;
num_glyphs(font_ref: &BridgeFontRef) -> u161503         fn num_glyphs(font_ref: &BridgeFontRef) -> u16;
fill_glyph_to_unicode_map(font_ref: &BridgeFontRef, map: &mut [u32])1504         fn fill_glyph_to_unicode_map(font_ref: &BridgeFontRef, map: &mut [u32]);
family_name(font_ref: &BridgeFontRef) -> String1505         fn family_name(font_ref: &BridgeFontRef) -> String;
postscript_name(font_ref: &BridgeFontRef, out_string: &mut String) -> bool1506         fn postscript_name(font_ref: &BridgeFontRef, out_string: &mut String) -> bool;
1507 
1508         /// Receives a slice of palette overrides that will be merged
1509         /// with the specified base palette of the font. The result is a
1510         /// palette of RGBA, 8-bit per component, colors, consisting of
1511         /// palette entries merged with overrides.
resolve_palette( font_ref: &BridgeFontRef, base_palette: u16, palette_overrides: &[PaletteOverride], ) -> Vec<u32>1512         fn resolve_palette(
1513             font_ref: &BridgeFontRef,
1514             base_palette: u16,
1515             palette_overrides: &[PaletteOverride],
1516         ) -> Vec<u32>;
1517 
has_colrv1_glyph(font_ref: &BridgeFontRef, glyph_id: u16) -> bool1518         fn has_colrv1_glyph(font_ref: &BridgeFontRef, glyph_id: u16) -> bool;
has_colrv0_glyph(font_ref: &BridgeFontRef, glyph_id: u16) -> bool1519         fn has_colrv0_glyph(font_ref: &BridgeFontRef, glyph_id: u16) -> bool;
get_colrv1_clip_box( font_ref: &BridgeFontRef, coords: &BridgeNormalizedCoords, glyph_id: u16, size: f32, clip_box: &mut ClipBox, ) -> bool1520         fn get_colrv1_clip_box(
1521             font_ref: &BridgeFontRef,
1522             coords: &BridgeNormalizedCoords,
1523             glyph_id: u16,
1524             size: f32,
1525             clip_box: &mut ClipBox,
1526         ) -> bool;
1527 
1528         type BridgeBitmapGlyph<'a>;
has_bitmap_glyph(font_ref: &BridgeFontRef, glyph_id: u16) -> bool1529         fn has_bitmap_glyph(font_ref: &BridgeFontRef, glyph_id: u16) -> bool;
bitmap_glyph<'a>( font_ref: &'a BridgeFontRef, glyph_id: u16, font_size: f32, ) -> Box<BridgeBitmapGlyph<'a>>1530         unsafe fn bitmap_glyph<'a>(
1531             font_ref: &'a BridgeFontRef,
1532             glyph_id: u16,
1533             font_size: f32,
1534         ) -> Box<BridgeBitmapGlyph<'a>>;
png_data<'a>(bitmap_glyph: &'a BridgeBitmapGlyph) -> &'a [u8]1535         unsafe fn png_data<'a>(bitmap_glyph: &'a BridgeBitmapGlyph) -> &'a [u8];
bitmap_metrics<'a>(bitmap_glyph: &'a BridgeBitmapGlyph) -> &'a BitmapMetrics1536         unsafe fn bitmap_metrics<'a>(bitmap_glyph: &'a BridgeBitmapGlyph) -> &'a BitmapMetrics;
1537 
table_data(font_ref: &BridgeFontRef, tag: u32, offset: usize, data: &mut [u8]) -> usize1538         fn table_data(font_ref: &BridgeFontRef, tag: u32, offset: usize, data: &mut [u8]) -> usize;
table_tags(font_ref: &BridgeFontRef, tags: &mut [u32]) -> u161539         fn table_tags(font_ref: &BridgeFontRef, tags: &mut [u32]) -> u16;
variation_position( coords: &BridgeNormalizedCoords, coordinates: &mut [SkiaDesignCoordinate], ) -> isize1540         fn variation_position(
1541             coords: &BridgeNormalizedCoords,
1542             coordinates: &mut [SkiaDesignCoordinate],
1543         ) -> isize;
1544         // Fills the passed-in slice with the axis coordinates for a given
1545         // shifted named instance index. A shifted named instance index is a
1546         // 32bit value that contains the index to a named instance left-shifted
1547         // by 16bits and offset by 1. This mirrors FreeType behavior to smuggle
1548         // named instance identifiers through a TrueType collection index.
1549         // Returns the number of coordinates copied. If the slice length is 0,
1550         // performs no copy but only returns the number of axis coordinates for
1551         // the given shifted index. Returns -1 on error.
coordinates_for_shifted_named_instance_index( font_ref: &BridgeFontRef, shifted_index: u32, coords: &mut [SkiaDesignCoordinate], ) -> isize1552         fn coordinates_for_shifted_named_instance_index(
1553             font_ref: &BridgeFontRef,
1554             shifted_index: u32,
1555             coords: &mut [SkiaDesignCoordinate],
1556         ) -> isize;
1557 
populate_axes(font_ref: &BridgeFontRef, axis_wrapper: Pin<&mut AxisWrapper>) -> isize1558         fn populate_axes(font_ref: &BridgeFontRef, axis_wrapper: Pin<&mut AxisWrapper>) -> isize;
1559 
1560         type BridgeLocalizedStrings<'a>;
get_localized_strings<'a>( font_ref: &'a BridgeFontRef<'a>, ) -> Box<BridgeLocalizedStrings<'a>>1561         unsafe fn get_localized_strings<'a>(
1562             font_ref: &'a BridgeFontRef<'a>,
1563         ) -> Box<BridgeLocalizedStrings<'a>>;
localized_name_next( bridge_localized_strings: &mut BridgeLocalizedStrings, out_localized_name: &mut BridgeLocalizedName, ) -> bool1564         fn localized_name_next(
1565             bridge_localized_strings: &mut BridgeLocalizedStrings,
1566             out_localized_name: &mut BridgeLocalizedName,
1567         ) -> bool;
1568 
1569         type BridgeNormalizedCoords;
resolve_into_normalized_coords( font_ref: &BridgeFontRef, design_coords: &[SkiaDesignCoordinate], ) -> Box<BridgeNormalizedCoords>1570         fn resolve_into_normalized_coords(
1571             font_ref: &BridgeFontRef,
1572             design_coords: &[SkiaDesignCoordinate],
1573         ) -> Box<BridgeNormalizedCoords>;
1574 
normalized_coords_equal(a: &BridgeNormalizedCoords, b: &BridgeNormalizedCoords) -> bool1575         fn normalized_coords_equal(a: &BridgeNormalizedCoords, b: &BridgeNormalizedCoords) -> bool;
1576 
draw_colr_glyph( font_ref: &BridgeFontRef, coords: &BridgeNormalizedCoords, glyph_id: u16, color_painter: Pin<&mut ColorPainterWrapper>, ) -> bool1577         fn draw_colr_glyph(
1578             font_ref: &BridgeFontRef,
1579             coords: &BridgeNormalizedCoords,
1580             glyph_id: u16,
1581             color_painter: Pin<&mut ColorPainterWrapper>,
1582         ) -> bool;
1583 
1584         type BridgeColorStops<'a>;
next_color_stop(color_stops: &mut BridgeColorStops, stop: &mut ColorStop) -> bool1585         fn next_color_stop(color_stops: &mut BridgeColorStops, stop: &mut ColorStop) -> bool;
num_color_stops(color_stops: &BridgeColorStops) -> usize1586         fn num_color_stops(color_stops: &BridgeColorStops) -> usize;
1587 
get_font_style( font_ref: &BridgeFontRef, coords: &BridgeNormalizedCoords, font_style: &mut BridgeFontStyle, ) -> bool1588         fn get_font_style(
1589             font_ref: &BridgeFontRef,
1590             coords: &BridgeNormalizedCoords,
1591             font_style: &mut BridgeFontStyle,
1592         ) -> bool;
1593 
1594         // Additional low-level access functions needed for generateAdvancedMetrics().
is_embeddable(font_ref: &BridgeFontRef) -> bool1595         fn is_embeddable(font_ref: &BridgeFontRef) -> bool;
is_subsettable(font_ref: &BridgeFontRef) -> bool1596         fn is_subsettable(font_ref: &BridgeFontRef) -> bool;
is_fixed_pitch(font_ref: &BridgeFontRef) -> bool1597         fn is_fixed_pitch(font_ref: &BridgeFontRef) -> bool;
is_serif_style(font_ref: &BridgeFontRef) -> bool1598         fn is_serif_style(font_ref: &BridgeFontRef) -> bool;
is_script_style(font_ref: &BridgeFontRef) -> bool1599         fn is_script_style(font_ref: &BridgeFontRef) -> bool;
italic_angle(font_ref: &BridgeFontRef) -> i321600         fn italic_angle(font_ref: &BridgeFontRef) -> i32;
1601     }
1602 
1603     unsafe extern "C++" {
1604 
1605         include!("src/ports/fontations/src/skpath_bridge.h");
1606 
1607         type PathWrapper;
1608 
1609         #[allow(dead_code)]
move_to(self: Pin<&mut PathWrapper>, x: f32, y: f32)1610         fn move_to(self: Pin<&mut PathWrapper>, x: f32, y: f32);
1611         #[allow(dead_code)]
line_to(self: Pin<&mut PathWrapper>, x: f32, y: f32)1612         fn line_to(self: Pin<&mut PathWrapper>, x: f32, y: f32);
1613         #[allow(dead_code)]
quad_to(self: Pin<&mut PathWrapper>, cx0: f32, cy0: f32, x: f32, y: f32)1614         fn quad_to(self: Pin<&mut PathWrapper>, cx0: f32, cy0: f32, x: f32, y: f32);
1615         #[allow(dead_code)]
curve_to( self: Pin<&mut PathWrapper>, cx0: f32, cy0: f32, cx1: f32, cy1: f32, x: f32, y: f32, )1616         fn curve_to(
1617             self: Pin<&mut PathWrapper>,
1618             cx0: f32,
1619             cy0: f32,
1620             cx1: f32,
1621             cy1: f32,
1622             x: f32,
1623             y: f32,
1624         );
1625         #[allow(dead_code)]
close(self: Pin<&mut PathWrapper>)1626         fn close(self: Pin<&mut PathWrapper>);
1627 
1628         type AxisWrapper;
1629 
populate_axis( self: Pin<&mut AxisWrapper>, i: usize, axis: u32, min: f32, def: f32, max: f32, hidden: bool, ) -> bool1630         fn populate_axis(
1631             self: Pin<&mut AxisWrapper>,
1632             i: usize,
1633             axis: u32,
1634             min: f32,
1635             def: f32,
1636             max: f32,
1637             hidden: bool,
1638         ) -> bool;
size(self: Pin<&AxisWrapper>) -> usize1639         fn size(self: Pin<&AxisWrapper>) -> usize;
1640 
1641         type ColorPainterWrapper;
1642 
push_transform(self: Pin<&mut ColorPainterWrapper>, transform: &Transform)1643         fn push_transform(self: Pin<&mut ColorPainterWrapper>, transform: &Transform);
pop_transform(self: Pin<&mut ColorPainterWrapper>)1644         fn pop_transform(self: Pin<&mut ColorPainterWrapper>);
push_clip_glyph(self: Pin<&mut ColorPainterWrapper>, glyph_id: u16)1645         fn push_clip_glyph(self: Pin<&mut ColorPainterWrapper>, glyph_id: u16);
push_clip_rectangle( self: Pin<&mut ColorPainterWrapper>, x_min: f32, y_min: f32, x_max: f32, y_max: f32, )1646         fn push_clip_rectangle(
1647             self: Pin<&mut ColorPainterWrapper>,
1648             x_min: f32,
1649             y_min: f32,
1650             x_max: f32,
1651             y_max: f32,
1652         );
pop_clip(self: Pin<&mut ColorPainterWrapper>)1653         fn pop_clip(self: Pin<&mut ColorPainterWrapper>);
1654 
fill_solid(self: Pin<&mut ColorPainterWrapper>, palette_index: u16, alpha: f32)1655         fn fill_solid(self: Pin<&mut ColorPainterWrapper>, palette_index: u16, alpha: f32);
fill_linear( self: Pin<&mut ColorPainterWrapper>, fill_linear_params: &FillLinearParams, color_stops: &mut BridgeColorStops, extend_mode: u8, )1656         fn fill_linear(
1657             self: Pin<&mut ColorPainterWrapper>,
1658             fill_linear_params: &FillLinearParams,
1659             color_stops: &mut BridgeColorStops,
1660             extend_mode: u8,
1661         );
fill_radial( self: Pin<&mut ColorPainterWrapper>, fill_radial_params: &FillRadialParams, color_stops: &mut BridgeColorStops, extend_mode: u8, )1662         fn fill_radial(
1663             self: Pin<&mut ColorPainterWrapper>,
1664             fill_radial_params: &FillRadialParams,
1665             color_stops: &mut BridgeColorStops,
1666             extend_mode: u8,
1667         );
fill_sweep( self: Pin<&mut ColorPainterWrapper>, fill_sweep_params: &FillSweepParams, color_stops: &mut BridgeColorStops, extend_mode: u8, )1668         fn fill_sweep(
1669             self: Pin<&mut ColorPainterWrapper>,
1670             fill_sweep_params: &FillSweepParams,
1671             color_stops: &mut BridgeColorStops,
1672             extend_mode: u8,
1673         );
1674 
1675         // Optimized functions.
fill_glyph_solid( self: Pin<&mut ColorPainterWrapper>, glyph_id: u16, palette_index: u16, alpha: f32, )1676         fn fill_glyph_solid(
1677             self: Pin<&mut ColorPainterWrapper>,
1678             glyph_id: u16,
1679             palette_index: u16,
1680             alpha: f32,
1681         );
fill_glyph_linear( self: Pin<&mut ColorPainterWrapper>, glyph_id: u16, fill_transform: &Transform, fill_linear_params: &FillLinearParams, color_stops: &mut BridgeColorStops, extend_mode: u8, )1682         fn fill_glyph_linear(
1683             self: Pin<&mut ColorPainterWrapper>,
1684             glyph_id: u16,
1685             fill_transform: &Transform,
1686             fill_linear_params: &FillLinearParams,
1687             color_stops: &mut BridgeColorStops,
1688             extend_mode: u8,
1689         );
fill_glyph_radial( self: Pin<&mut ColorPainterWrapper>, glyph_id: u16, fill_transform: &Transform, fill_radial_params: &FillRadialParams, color_stops: &mut BridgeColorStops, extend_mode: u8, )1690         fn fill_glyph_radial(
1691             self: Pin<&mut ColorPainterWrapper>,
1692             glyph_id: u16,
1693             fill_transform: &Transform,
1694             fill_radial_params: &FillRadialParams,
1695             color_stops: &mut BridgeColorStops,
1696             extend_mode: u8,
1697         );
fill_glyph_sweep( self: Pin<&mut ColorPainterWrapper>, glyph_id: u16, fill_transform: &Transform, fill_sweep_params: &FillSweepParams, color_stops: &mut BridgeColorStops, extend_mode: u8, )1698         fn fill_glyph_sweep(
1699             self: Pin<&mut ColorPainterWrapper>,
1700             glyph_id: u16,
1701             fill_transform: &Transform,
1702             fill_sweep_params: &FillSweepParams,
1703             color_stops: &mut BridgeColorStops,
1704             extend_mode: u8,
1705         );
1706 
push_layer(self: Pin<&mut ColorPainterWrapper>, colrv1_composite_mode: u8)1707         fn push_layer(self: Pin<&mut ColorPainterWrapper>, colrv1_composite_mode: u8);
pop_layer(self: Pin<&mut ColorPainterWrapper>)1708         fn pop_layer(self: Pin<&mut ColorPainterWrapper>);
1709 
1710     }
1711 }
1712 
1713 /// Tests to exercise COLR and CPAL parts of the Fontations FFI.
1714 /// Run using `$ bazel test --with_fontations //src/ports/fontations:test_ffi`
1715 #[cfg(test)]
1716 mod test {
1717     use crate::{
1718         coordinates_for_shifted_named_instance_index,
1719         ffi::{BridgeFontStyle, PaletteOverride, SkiaDesignCoordinate},
1720         font_or_collection, font_ref_is_valid, get_font_style, make_font_ref,
1721         resolve_into_normalized_coords, resolve_palette,
1722     };
1723     use std::fs;
1724 
1725     const TEST_FONT_FILENAME: &str = "resources/fonts/test_glyphs-glyf_colr_1_variable.ttf";
1726     const TEST_COLLECTION_FILENAME: &str = "resources/fonts/test.ttc";
1727     const TEST_CONDENSED_BOLD_ITALIC: &str = "resources/fonts/cond-bold-italic.ttf";
1728     const TEST_VARIABLE: &str = "resources/fonts/Variable.ttf";
1729 
1730     #[test]
test_palette_override()1731     fn test_palette_override() {
1732         let file_buffer =
1733             fs::read(TEST_FONT_FILENAME).expect("COLRv0/v1 test font could not be opened.");
1734         let font_ref = make_font_ref(&file_buffer, 0);
1735         assert!(font_ref_is_valid(&font_ref));
1736 
1737         let override_color = 0xFFEEEEEE;
1738         let valid_overrides = [
1739             PaletteOverride {
1740                 index: 9,
1741                 color_8888: override_color,
1742             },
1743             PaletteOverride {
1744                 index: 10,
1745                 color_8888: override_color,
1746             },
1747             PaletteOverride {
1748                 index: 11,
1749                 color_8888: override_color,
1750             },
1751         ];
1752 
1753         let palette = resolve_palette(&font_ref, 0, &valid_overrides);
1754 
1755         assert_eq!(palette.len(), 14);
1756         assert_eq!(palette[9], override_color);
1757         assert_eq!(palette[10], override_color);
1758         assert_eq!(palette[11], override_color);
1759 
1760         let out_of_bounds_overrides = [
1761             PaletteOverride {
1762                 index: 15,
1763                 color_8888: override_color,
1764             },
1765             PaletteOverride {
1766                 index: 16,
1767                 color_8888: override_color,
1768             },
1769             PaletteOverride {
1770                 index: 17,
1771                 color_8888: override_color,
1772             },
1773         ];
1774 
1775         let palette = resolve_palette(&font_ref, 0, &out_of_bounds_overrides);
1776 
1777         assert_eq!(palette.len(), 14);
1778         assert_eq!(
1779             (palette[11], palette[12], palette[13],),
1780             (0xff68c7e8, 0xffffdc01, 0xff808080)
1781         );
1782     }
1783 
1784     #[test]
test_default_palette_for_invalid_index()1785     fn test_default_palette_for_invalid_index() {
1786         let file_buffer =
1787             fs::read(TEST_FONT_FILENAME).expect("COLRv0/v1 test font could not be opened.");
1788         let font_ref = make_font_ref(&file_buffer, 0);
1789         assert!(font_ref_is_valid(&font_ref));
1790         let palette = resolve_palette(&font_ref, 65535, &[]);
1791         assert_eq!(palette.len(), 14);
1792         assert_eq!(
1793             (palette[0], palette[6], palette[13],),
1794             (0xFFFF0000, 0xFFEE82EE, 0xFF808080)
1795         );
1796     }
1797 
1798     #[test]
test_num_fonts_in_collection()1799     fn test_num_fonts_in_collection() {
1800         let collection_buffer = fs::read(TEST_COLLECTION_FILENAME)
1801             .expect("Unable to open TrueType collection test file.");
1802         let font_buffer =
1803             fs::read(TEST_FONT_FILENAME).expect("COLRv0/v1 test font could not be opened.");
1804         let garbage: [u8; 12] = [
1805             b'0', b'a', b'b', b'0', b'a', b'b', b'0', b'a', b'b', b'0', b'a', b'b',
1806         ];
1807 
1808         let mut num_fonts = 0;
1809         let result_collection = font_or_collection(&collection_buffer, &mut num_fonts);
1810         assert!(result_collection && num_fonts == 2);
1811 
1812         let result_font_file = font_or_collection(&font_buffer, &mut num_fonts);
1813         assert!(result_font_file);
1814         assert!(num_fonts == 0u32);
1815 
1816         let result_garbage = font_or_collection(&garbage, &mut num_fonts);
1817         assert!(!result_garbage);
1818     }
1819 
1820     #[test]
test_font_attributes()1821     fn test_font_attributes() {
1822         let file_buffer = fs::read(TEST_CONDENSED_BOLD_ITALIC)
1823             .expect("Font to test font styles could not be opened.");
1824         let font_ref = make_font_ref(&file_buffer, 0);
1825         let coords = resolve_into_normalized_coords(&font_ref, &[]);
1826         assert!(font_ref_is_valid(&font_ref));
1827 
1828         let mut font_style = BridgeFontStyle::default();
1829 
1830         if get_font_style(font_ref.as_ref(), &coords, &mut font_style) {
1831             assert_eq!(font_style.width, 5); // The font should have condenced width attribute but
1832                                              // it's condenced itself so we have the normal width
1833             assert_eq!(font_style.slant, 1); // Skia italic
1834             assert_eq!(font_style.weight, 700); // Skia bold
1835         } else {
1836             assert!(false);
1837         }
1838     }
1839 
1840     #[test]
test_variable_font_attributes()1841     fn test_variable_font_attributes() {
1842         let file_buffer =
1843             fs::read(TEST_VARIABLE).expect("Font to test font styles could not be opened.");
1844         let font_ref = make_font_ref(&file_buffer, 0);
1845         let coords = resolve_into_normalized_coords(&font_ref, &[]);
1846         assert!(font_ref_is_valid(&font_ref));
1847 
1848         let mut font_style = BridgeFontStyle::default();
1849 
1850         assert!(get_font_style(font_ref.as_ref(), &coords, &mut font_style));
1851         assert_eq!(font_style.width, 5); // Skia normal
1852         assert_eq!(font_style.slant, 0); // Skia upright
1853         assert_eq!(font_style.weight, 400); // Skia normal
1854     }
1855 
1856     #[test]
test_shifted_named_instance_index()1857     fn test_shifted_named_instance_index() {
1858         let file_buffer =
1859             fs::read(TEST_VARIABLE).expect("Font to test named instances could not be opened.");
1860         let font_ref = make_font_ref(&file_buffer, 0);
1861         assert!(font_ref_is_valid(&font_ref));
1862         // Named instances are 1-indexed.
1863         const SHIFTED_NAMED_INSTANCE_INDEX: u32 = 5 << 16;
1864         const OUT_OF_BOUNDS_NAMED_INSTANCE_INDEX: u32 = 6 << 16;
1865 
1866         let num_coords = coordinates_for_shifted_named_instance_index(
1867             &font_ref,
1868             SHIFTED_NAMED_INSTANCE_INDEX,
1869             &mut [],
1870         );
1871         assert_eq!(num_coords, 2);
1872 
1873         let mut too_small: [SkiaDesignCoordinate; 1] = Default::default();
1874         let num_coords = coordinates_for_shifted_named_instance_index(
1875             &font_ref,
1876             SHIFTED_NAMED_INSTANCE_INDEX,
1877             &mut too_small,
1878         );
1879         assert_eq!(num_coords, -1);
1880 
1881         let mut received_coords: [SkiaDesignCoordinate; 2] = Default::default();
1882         let num_coords = coordinates_for_shifted_named_instance_index(
1883             &font_ref,
1884             SHIFTED_NAMED_INSTANCE_INDEX,
1885             &mut received_coords,
1886         );
1887         assert_eq!(num_coords, 2);
1888         assert_eq!(
1889             received_coords[0],
1890             SkiaDesignCoordinate {
1891                 axis: u32::from_be_bytes([b'w', b'g', b'h', b't']),
1892                 value: 400.0
1893             }
1894         );
1895         assert_eq!(
1896             received_coords[1],
1897             SkiaDesignCoordinate {
1898                 axis: u32::from_be_bytes([b'w', b'd', b't', b'h']),
1899                 value: 200.0
1900             }
1901         );
1902 
1903         let mut too_large: [SkiaDesignCoordinate; 5] = Default::default();
1904         let num_coords = coordinates_for_shifted_named_instance_index(
1905             &font_ref,
1906             SHIFTED_NAMED_INSTANCE_INDEX,
1907             &mut too_large,
1908         );
1909         assert_eq!(num_coords, 2);
1910 
1911         // Index out of bounds:
1912         let num_coords = coordinates_for_shifted_named_instance_index(
1913             &font_ref,
1914             OUT_OF_BOUNDS_NAMED_INSTANCE_INDEX,
1915             &mut [],
1916         );
1917         assert_eq!(num_coords, -1);
1918     }
1919 }
1920