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};
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::{
17 pen::NullPen, DrawSettings, Engine, HintingInstance, HintingOptions, OutlinePen,
18 SmoothMode, Target,
19 },
20 setting::VariationSetting,
21 string::{LocalizedStrings, StringId},
22 MetadataProvider, OutlineGlyphCollection, Tag,
23 };
24 use std::pin::Pin;
25
26 use crate::bitmap::{bitmap_glyph, bitmap_metrics, has_bitmap_glyph, png_data, BridgeBitmapGlyph};
27
28 use crate::ffi::{
29 AxisWrapper, BridgeFontStyle, BridgeLocalizedName, BridgeScalerMetrics, ClipBox,
30 ColorPainterWrapper, ColorStop, FfiPoint, PaletteOverride, SkiaDesignCoordinate,
31 };
32
33 const PATH_EXTRACTION_RESERVE: usize = 150;
34
make_mapping_index<'a>(font_ref: &'a BridgeFontRef) -> Box<BridgeMappingIndex>35 fn make_mapping_index<'a>(font_ref: &'a BridgeFontRef) -> Box<BridgeMappingIndex> {
36 font_ref
37 .with_font(|f| Some(Box::new(BridgeMappingIndex(MappingIndex::new(f)))))
38 .unwrap()
39 }
40
no_hinting_instance<'a>() -> Box<BridgeHintingInstance>41 unsafe fn no_hinting_instance<'a>() -> Box<BridgeHintingInstance> {
42 Box::new(BridgeHintingInstance(None))
43 }
44
make_hinting_instance<'a>( outlines: &BridgeOutlineCollection, size: f32, coords: &BridgeNormalizedCoords, do_light_hinting: bool, do_lcd_antialiasing: bool, lcd_orientation_vertical: bool, force_autohinting: bool, ) -> Box<BridgeHintingInstance>45 unsafe fn make_hinting_instance<'a>(
46 outlines: &BridgeOutlineCollection,
47 size: f32,
48 coords: &BridgeNormalizedCoords,
49 do_light_hinting: bool,
50 do_lcd_antialiasing: bool,
51 lcd_orientation_vertical: bool,
52 force_autohinting: bool,
53 ) -> Box<BridgeHintingInstance> {
54 let hinting_instance = match &outlines.0 {
55 Some(outlines) => {
56 let smooth_mode = match (
57 do_light_hinting,
58 do_lcd_antialiasing,
59 lcd_orientation_vertical,
60 ) {
61 (true, _, _) => SmoothMode::Light,
62 (false, true, false) => SmoothMode::Lcd,
63 (false, true, true) => SmoothMode::VerticalLcd,
64 _ => SmoothMode::Normal,
65 };
66
67 let hinting_target = Target::Smooth {
68 mode: smooth_mode,
69 // See https://docs.rs/skrifa/latest/skrifa/outline/enum.Target.html#variant.Smooth.field.mode
70 // Configure additional params to match FreeType.
71 symmetric_rendering: true,
72 preserve_linear_metrics: false,
73 };
74
75 let engine_type = if force_autohinting {
76 Engine::Auto(None)
77 } else {
78 Engine::AutoFallback
79 };
80
81 HintingInstance::new(
82 outlines,
83 Size::new(size),
84 &coords.normalized_coords,
85 HintingOptions {
86 engine: engine_type,
87 target: hinting_target,
88 },
89 )
90 .ok()
91 }
92 _ => None,
93 };
94 Box::new(BridgeHintingInstance(hinting_instance))
95 }
96
make_mono_hinting_instance<'a>( outlines: &BridgeOutlineCollection, size: f32, coords: &BridgeNormalizedCoords, ) -> Box<BridgeHintingInstance>97 unsafe fn make_mono_hinting_instance<'a>(
98 outlines: &BridgeOutlineCollection,
99 size: f32,
100 coords: &BridgeNormalizedCoords,
101 ) -> Box<BridgeHintingInstance> {
102 let hinting_instance = outlines.0.as_ref().and_then(|outlines| {
103 HintingInstance::new(
104 outlines,
105 Size::new(size),
106 &coords.normalized_coords,
107 skrifa::outline::HintingMode::Strong,
108 )
109 .ok()
110 });
111 Box::new(BridgeHintingInstance(hinting_instance))
112 }
113
lookup_glyph_or_zero(font_ref: &BridgeFontRef, map: &BridgeMappingIndex, codepoint: u32) -> u16114 fn lookup_glyph_or_zero(font_ref: &BridgeFontRef, map: &BridgeMappingIndex, codepoint: u32) -> u16 {
115 font_ref
116 .with_font(|f| {
117 let glyph_id = map.0.charmap(f).map(codepoint)?.to_u32();
118 // Remove conversion and change return type to u32 when
119 // implementing large glyph id support in Skia.
120 glyph_id.try_into().ok()
121 })
122 .unwrap_or_default()
123 }
124
num_glyphs(font_ref: &BridgeFontRef) -> u16125 fn num_glyphs(font_ref: &BridgeFontRef) -> u16 {
126 font_ref
127 .with_font(|f| Some(f.maxp().ok()?.num_glyphs()))
128 .unwrap_or_default()
129 }
130
fill_glyph_to_unicode_map(font_ref: &BridgeFontRef, map: &mut [u32])131 fn fill_glyph_to_unicode_map(font_ref: &BridgeFontRef, map: &mut [u32]) {
132 map.fill(0);
133 font_ref.with_font(|f| {
134 let mappings = f.charmap().mappings();
135 for item in mappings {
136 if map[item.1.to_u32() as usize] == 0 {
137 map[item.1.to_u32() as usize] = item.0;
138 }
139 }
140 Some(())
141 });
142 }
143
144 struct VerbsPointsPen<'a> {
145 verbs: &'a mut Vec<u8>,
146 points: &'a mut Vec<FfiPoint>,
147 started: bool,
148 current: FfiPoint,
149 }
150
151 impl FfiPoint {
new(x: f32, y: f32) -> Self152 fn new(x: f32, y: f32) -> Self {
153 Self { x, y }
154 }
155 }
156
157 // Values need to match SkPathVerb.
158 #[repr(u8)]
159 enum PathVerb {
160 MoveTo = 0,
161 LineTo = 1,
162 QuadTo = 2,
163 CubicTo = 4,
164 Close = 5,
165 }
166
167 impl<'a> VerbsPointsPen<'a> {
new(verbs: &'a mut Vec<u8>, points: &'a mut Vec<FfiPoint>) -> Self168 fn new(verbs: &'a mut Vec<u8>, points: &'a mut Vec<FfiPoint>) -> Self {
169 verbs.clear();
170 points.clear();
171 verbs.reserve(PATH_EXTRACTION_RESERVE);
172 points.reserve(PATH_EXTRACTION_RESERVE);
173 Self {
174 verbs,
175 points,
176 started: false,
177 current: FfiPoint::default(),
178 }
179 }
180
going_to(&mut self, point: &FfiPoint)181 fn going_to(&mut self, point: &FfiPoint) {
182 if !self.started {
183 self.started = true;
184 self.verbs.push(PathVerb::MoveTo as u8);
185 self.points.push(self.current);
186 }
187 self.current = *point;
188 }
189
current_is_not(&self, point: &FfiPoint) -> bool190 fn current_is_not(&self, point: &FfiPoint) -> bool {
191 self.current != *point
192 }
193 }
194
195 impl<'a> OutlinePen for VerbsPointsPen<'a> {
move_to(&mut self, x: f32, y: f32)196 fn move_to(&mut self, x: f32, y: f32) {
197 let pt0 = FfiPoint::new(x, -y);
198 if self.started {
199 self.close();
200 self.started = false;
201 }
202 self.current = pt0;
203 }
204
line_to(&mut self, x: f32, y: f32)205 fn line_to(&mut self, x: f32, y: f32) {
206 let pt0 = FfiPoint::new(x, -y);
207 if self.current_is_not(&pt0) {
208 self.going_to(&pt0);
209 self.verbs.push(PathVerb::LineTo as u8);
210 self.points.push(pt0);
211 }
212 }
213
quad_to(&mut self, cx0: f32, cy0: f32, x: f32, y: f32)214 fn quad_to(&mut self, cx0: f32, cy0: f32, x: f32, y: f32) {
215 let pt0 = FfiPoint::new(cx0, -cy0);
216 let pt1 = FfiPoint::new(x, -y);
217 if self.current_is_not(&pt0) || self.current_is_not(&pt1) {
218 self.going_to(&pt1);
219 self.verbs.push(PathVerb::QuadTo as u8);
220 self.points.push(pt0);
221 self.points.push(pt1);
222 }
223 }
224
curve_to(&mut self, cx0: f32, cy0: f32, cx1: f32, cy1: f32, x: f32, y: f32)225 fn curve_to(&mut self, cx0: f32, cy0: f32, cx1: f32, cy1: f32, x: f32, y: f32) {
226 let pt0 = FfiPoint::new(cx0, -cy0);
227 let pt1 = FfiPoint::new(cx1, -cy1);
228 let pt2 = FfiPoint::new(x, -y);
229 if self.current_is_not(&pt0) || self.current_is_not(&pt1) || self.current_is_not(&pt2) {
230 self.going_to(&pt2);
231 self.verbs.push(PathVerb::CubicTo as u8);
232 self.points.push(pt0);
233 self.points.push(pt1);
234 self.points.push(pt2);
235 }
236 }
237
close(&mut self)238 fn close(&mut self) {
239 if let Some(verb) = self.verbs.last().cloned() {
240 if verb == PathVerb::QuadTo as u8
241 || verb == PathVerb::CubicTo as u8
242 || verb == PathVerb::LineTo as u8
243 || verb == PathVerb::MoveTo as u8
244 {
245 self.verbs.push(PathVerb::Close as u8);
246 }
247 }
248 }
249 }
250
251 struct ColorPainterImpl<'a> {
252 color_painter_wrapper: Pin<&'a mut ffi::ColorPainterWrapper>,
253 clip_level: usize,
254 }
255
256 impl<'a> ColorPainter for ColorPainterImpl<'a> {
push_transform(&mut self, transform: Transform)257 fn push_transform(&mut self, transform: Transform) {
258 if self.clip_level > 0 {
259 return;
260 }
261 self.color_painter_wrapper
262 .as_mut()
263 .push_transform(&ffi::Transform {
264 xx: transform.xx,
265 xy: transform.xy,
266 yx: transform.yx,
267 yy: transform.yy,
268 dx: transform.dx,
269 dy: transform.dy,
270 });
271 }
272
pop_transform(&mut self)273 fn pop_transform(&mut self) {
274 if self.clip_level > 0 {
275 return;
276 }
277 self.color_painter_wrapper.as_mut().pop_transform();
278 }
279
push_clip_glyph(&mut self, glyph: GlyphId)280 fn push_clip_glyph(&mut self, glyph: GlyphId) {
281 if self.clip_level == 0 {
282 // TODO(drott): Handle large glyph ids in clip operation.
283 self.color_painter_wrapper
284 .as_mut()
285 .push_clip_glyph(glyph.to_u32().try_into().ok().unwrap_or_default());
286 }
287 if self.color_painter_wrapper.as_mut().is_bounds_mode() {
288 self.clip_level += 1;
289 }
290 }
291
push_clip_box(&mut self, clip_box: BoundingBox<f32>)292 fn push_clip_box(&mut self, clip_box: BoundingBox<f32>) {
293 if self.clip_level == 0 {
294 self.color_painter_wrapper.as_mut().push_clip_rectangle(
295 clip_box.x_min,
296 clip_box.y_min,
297 clip_box.x_max,
298 clip_box.y_max,
299 );
300 }
301 if self.color_painter_wrapper.as_mut().is_bounds_mode() {
302 self.clip_level += 1;
303 }
304 }
305
pop_clip(&mut self)306 fn pop_clip(&mut self) {
307 if self.color_painter_wrapper.as_mut().is_bounds_mode() {
308 self.clip_level -= 1;
309 }
310 if self.clip_level == 0 {
311 self.color_painter_wrapper.as_mut().pop_clip();
312 }
313 }
314
fill(&mut self, fill_type: Brush)315 fn fill(&mut self, fill_type: Brush) {
316 if self.clip_level > 0 {
317 return;
318 }
319 let color_painter = self.color_painter_wrapper.as_mut();
320 match fill_type {
321 Brush::Solid {
322 palette_index,
323 alpha,
324 } => {
325 color_painter.fill_solid(palette_index, alpha);
326 }
327
328 Brush::LinearGradient {
329 p0,
330 p1,
331 color_stops,
332 extend,
333 } => {
334 let mut bridge_color_stops = BridgeColorStops {
335 stops_iterator: Box::new(color_stops.iter()),
336 num_stops: color_stops.len(),
337 };
338 color_painter.fill_linear(
339 &FillLinearParams {
340 x0: p0.x,
341 y0: p0.y,
342 x1: p1.x,
343 y1: p1.y,
344 },
345 &mut bridge_color_stops,
346 extend as u8,
347 );
348 }
349 Brush::RadialGradient {
350 c0,
351 r0,
352 c1,
353 r1,
354 color_stops,
355 extend,
356 } => {
357 let mut bridge_color_stops = BridgeColorStops {
358 stops_iterator: Box::new(color_stops.iter()),
359 num_stops: color_stops.len(),
360 };
361 color_painter.fill_radial(
362 &FillRadialParams {
363 x0: c0.x,
364 y0: c0.y,
365 r0,
366 x1: c1.x,
367 y1: c1.y,
368 r1,
369 },
370 &mut bridge_color_stops,
371 extend as u8,
372 );
373 }
374 Brush::SweepGradient {
375 c0,
376 start_angle,
377 end_angle,
378 color_stops,
379 extend,
380 } => {
381 let mut bridge_color_stops = BridgeColorStops {
382 stops_iterator: Box::new(color_stops.iter()),
383 num_stops: color_stops.len(),
384 };
385 color_painter.fill_sweep(
386 &ffi::FillSweepParams {
387 x0: c0.x,
388 y0: c0.y,
389 start_angle,
390 end_angle,
391 },
392 &mut bridge_color_stops,
393 extend as u8,
394 );
395 }
396 }
397 }
398
fill_glyph(&mut self, glyph: GlyphId, brush_transform: Option<Transform>, brush: Brush)399 fn fill_glyph(&mut self, glyph: GlyphId, brush_transform: Option<Transform>, brush: Brush) {
400 if self.color_painter_wrapper.as_mut().is_bounds_mode() {
401 self.push_clip_glyph(glyph);
402 self.pop_clip();
403 return;
404 }
405
406 let color_painter = self.color_painter_wrapper.as_mut();
407 let brush_transform = brush_transform.unwrap_or_default();
408 match brush {
409 Brush::Solid {
410 palette_index,
411 alpha,
412 } => {
413 // TODO(drott): Handle large glyph ids in fill glyph operation.
414 color_painter.fill_glyph_solid(
415 glyph.to_u32().try_into().ok().unwrap_or_default(),
416 palette_index,
417 alpha,
418 );
419 }
420 Brush::LinearGradient {
421 p0,
422 p1,
423 color_stops,
424 extend,
425 } => {
426 let mut bridge_color_stops = BridgeColorStops {
427 stops_iterator: Box::new(color_stops.iter()),
428 num_stops: color_stops.len(),
429 };
430 color_painter.fill_glyph_linear(
431 // TODO(drott): Handle large glyph ids in fill glyph operation.
432 glyph.to_u32().try_into().ok().unwrap_or_default(),
433 &ffi::Transform {
434 xx: brush_transform.xx,
435 xy: brush_transform.xy,
436 yx: brush_transform.yx,
437 yy: brush_transform.yy,
438 dx: brush_transform.dx,
439 dy: brush_transform.dy,
440 },
441 &FillLinearParams {
442 x0: p0.x,
443 y0: p0.y,
444 x1: p1.x,
445 y1: p1.y,
446 },
447 &mut bridge_color_stops,
448 extend as u8,
449 );
450 }
451 Brush::RadialGradient {
452 c0,
453 r0,
454 c1,
455 r1,
456 color_stops,
457 extend,
458 } => {
459 let mut bridge_color_stops = BridgeColorStops {
460 stops_iterator: Box::new(color_stops.iter()),
461 num_stops: color_stops.len(),
462 };
463 color_painter.fill_glyph_radial(
464 // TODO(drott): Handle large glyph ids in fill glyph operation.
465 glyph.to_u32().try_into().ok().unwrap_or_default(),
466 &ffi::Transform {
467 xx: brush_transform.xx,
468 xy: brush_transform.xy,
469 yx: brush_transform.yx,
470 yy: brush_transform.yy,
471 dx: brush_transform.dx,
472 dy: brush_transform.dy,
473 },
474 &FillRadialParams {
475 x0: c0.x,
476 y0: c0.y,
477 r0,
478 x1: c1.x,
479 y1: c1.y,
480 r1,
481 },
482 &mut bridge_color_stops,
483 extend as u8,
484 );
485 }
486 Brush::SweepGradient {
487 c0,
488 start_angle,
489 end_angle,
490 color_stops,
491 extend,
492 } => {
493 let mut bridge_color_stops = BridgeColorStops {
494 stops_iterator: Box::new(color_stops.iter()),
495 num_stops: color_stops.len(),
496 };
497 color_painter.fill_glyph_sweep(
498 // TODO(drott): Handle large glyph ids in fill glyph operation.
499 glyph.to_u32().try_into().ok().unwrap_or_default(),
500 &ffi::Transform {
501 xx: brush_transform.xx,
502 xy: brush_transform.xy,
503 yx: brush_transform.yx,
504 yy: brush_transform.yy,
505 dx: brush_transform.dx,
506 dy: brush_transform.dy,
507 },
508 &ffi::FillSweepParams {
509 x0: c0.x,
510 y0: c0.y,
511 start_angle,
512 end_angle,
513 },
514 &mut bridge_color_stops,
515 extend as u8,
516 );
517 }
518 }
519 }
520
push_layer(&mut self, composite_mode: CompositeMode)521 fn push_layer(&mut self, composite_mode: CompositeMode) {
522 if self.clip_level > 0 {
523 return;
524 }
525 self.color_painter_wrapper
526 .as_mut()
527 .push_layer(composite_mode as u8);
528 }
pop_layer(&mut self)529 fn pop_layer(&mut self) {
530 if self.clip_level > 0 {
531 return;
532 }
533 self.color_painter_wrapper.as_mut().pop_layer();
534 }
535 }
536
get_path_verbs_points( outlines: &BridgeOutlineCollection, glyph_id: u16, size: f32, coords: &BridgeNormalizedCoords, hinting_instance: &BridgeHintingInstance, verbs: &mut Vec<u8>, points: &mut Vec<FfiPoint>, scaler_metrics: &mut BridgeScalerMetrics, ) -> bool537 fn get_path_verbs_points(
538 outlines: &BridgeOutlineCollection,
539 glyph_id: u16,
540 size: f32,
541 coords: &BridgeNormalizedCoords,
542 hinting_instance: &BridgeHintingInstance,
543 verbs: &mut Vec<u8>,
544 points: &mut Vec<FfiPoint>,
545 scaler_metrics: &mut BridgeScalerMetrics,
546 ) -> bool {
547 outlines
548 .0
549 .as_ref()
550 .and_then(|outlines| {
551 let glyph = outlines.get(GlyphId::from(glyph_id))?;
552
553 let draw_settings = match &hinting_instance.0 {
554 Some(instance) => DrawSettings::hinted(instance, false),
555 _ => DrawSettings::unhinted(Size::new(size), &coords.normalized_coords),
556 };
557
558 let mut verbs_points_pen = VerbsPointsPen::new(verbs, points);
559 match glyph.draw(draw_settings, &mut verbs_points_pen) {
560 Err(_) => None,
561 Ok(metrics) => {
562 scaler_metrics.has_overlaps = metrics.has_overlaps;
563 Some(())
564 }
565 }
566 })
567 .is_some()
568 }
569
shrink_verbs_points_if_needed(verbs: &mut Vec<u8>, points: &mut Vec<FfiPoint>)570 fn shrink_verbs_points_if_needed(verbs: &mut Vec<u8>, points: &mut Vec<FfiPoint>) {
571 verbs.shrink_to(PATH_EXTRACTION_RESERVE);
572 points.shrink_to(PATH_EXTRACTION_RESERVE);
573 }
574
unhinted_advance_width_or_zero( font_ref: &BridgeFontRef, size: f32, coords: &BridgeNormalizedCoords, glyph_id: u16, ) -> f32575 fn unhinted_advance_width_or_zero(
576 font_ref: &BridgeFontRef,
577 size: f32,
578 coords: &BridgeNormalizedCoords,
579 glyph_id: u16,
580 ) -> f32 {
581 font_ref
582 .with_font(|f| {
583 GlyphMetrics::new(f, Size::new(size), coords.normalized_coords.coords())
584 .advance_width(GlyphId::from(glyph_id))
585 })
586 .unwrap_or_default()
587 }
588
scaler_hinted_advance_width( outlines: &BridgeOutlineCollection, hinting_instance: &BridgeHintingInstance, glyph_id: u16, out_advance_width: &mut f32, ) -> bool589 fn scaler_hinted_advance_width(
590 outlines: &BridgeOutlineCollection,
591 hinting_instance: &BridgeHintingInstance,
592 glyph_id: u16,
593 out_advance_width: &mut f32,
594 ) -> bool {
595 hinting_instance
596 .0
597 .as_ref()
598 .and_then(|instance| {
599 let draw_settings = DrawSettings::hinted(instance, false);
600
601 let outlines = outlines.0.as_ref()?;
602 let glyph = outlines.get(GlyphId::from(glyph_id))?;
603 let mut null_pen = NullPen {};
604 let adjusted_metrics = glyph.draw(draw_settings, &mut null_pen).ok()?;
605 adjusted_metrics.advance_width.map(|adjusted_advance| {
606 *out_advance_width = adjusted_advance;
607 ()
608 })
609 })
610 .is_some()
611 }
612
units_per_em_or_zero(font_ref: &BridgeFontRef) -> u16613 fn units_per_em_or_zero(font_ref: &BridgeFontRef) -> u16 {
614 font_ref
615 .with_font(|f| Some(f.head().ok()?.units_per_em()))
616 .unwrap_or_default()
617 }
618
convert_metrics(skrifa_metrics: &Metrics) -> ffi::Metrics619 fn convert_metrics(skrifa_metrics: &Metrics) -> ffi::Metrics {
620 ffi::Metrics {
621 top: skrifa_metrics.bounds.map_or(0.0, |b| b.y_max),
622 bottom: skrifa_metrics.bounds.map_or(0.0, |b| b.y_min),
623 x_min: skrifa_metrics.bounds.map_or(0.0, |b| b.x_min),
624 x_max: skrifa_metrics.bounds.map_or(0.0, |b| b.x_max),
625 ascent: skrifa_metrics.ascent,
626 descent: skrifa_metrics.descent,
627 leading: skrifa_metrics.leading,
628 avg_char_width: skrifa_metrics.average_width.unwrap_or(0.0),
629 max_char_width: skrifa_metrics.max_width.unwrap_or(0.0),
630 x_height: -skrifa_metrics.x_height.unwrap_or(0.0),
631 cap_height: -skrifa_metrics.cap_height.unwrap_or(0.0),
632 underline_position: skrifa_metrics.underline.map_or(f32::NAN, |u| u.offset),
633 underline_thickness: skrifa_metrics.underline.map_or(f32::NAN, |u| u.thickness),
634 strikeout_position: skrifa_metrics.strikeout.map_or(f32::NAN, |s| s.offset),
635 strikeout_thickness: skrifa_metrics.strikeout.map_or(f32::NAN, |s| s.thickness),
636 }
637 }
638
get_skia_metrics( font_ref: &BridgeFontRef, size: f32, coords: &BridgeNormalizedCoords, ) -> ffi::Metrics639 fn get_skia_metrics(
640 font_ref: &BridgeFontRef,
641 size: f32,
642 coords: &BridgeNormalizedCoords,
643 ) -> ffi::Metrics {
644 font_ref
645 .with_font(|f| {
646 let fontations_metrics =
647 Metrics::new(f, Size::new(size), coords.normalized_coords.coords());
648 Some(convert_metrics(&fontations_metrics))
649 })
650 .unwrap_or_default()
651 }
652
get_unscaled_metrics(font_ref: &BridgeFontRef, coords: &BridgeNormalizedCoords) -> ffi::Metrics653 fn get_unscaled_metrics(font_ref: &BridgeFontRef, coords: &BridgeNormalizedCoords) -> ffi::Metrics {
654 font_ref
655 .with_font(|f| {
656 let fontations_metrics =
657 Metrics::new(f, Size::unscaled(), coords.normalized_coords.coords());
658 Some(convert_metrics(&fontations_metrics))
659 })
660 .unwrap_or_default()
661 }
662
get_localized_strings<'a>(font_ref: &'a BridgeFontRef<'a>) -> Box<BridgeLocalizedStrings<'a>>663 fn get_localized_strings<'a>(font_ref: &'a BridgeFontRef<'a>) -> Box<BridgeLocalizedStrings<'a>> {
664 Box::new(BridgeLocalizedStrings {
665 localized_strings: font_ref
666 .with_font(|f| Some(f.localized_strings(StringId::FAMILY_NAME)))
667 .unwrap_or_default(),
668 })
669 }
670
localized_name_next( bridge_localized_strings: &mut BridgeLocalizedStrings, out_localized_name: &mut BridgeLocalizedName, ) -> bool671 fn localized_name_next(
672 bridge_localized_strings: &mut BridgeLocalizedStrings,
673 out_localized_name: &mut BridgeLocalizedName,
674 ) -> bool {
675 match bridge_localized_strings.localized_strings.next() {
676 Some(localized_string) => {
677 out_localized_name.string = localized_string.to_string();
678 // TODO(b/307906051): Remove the suffix before shipping.
679 out_localized_name.string.push_str(" (Fontations)");
680 out_localized_name.language = localized_string
681 .language()
682 .map(|l| l.to_string())
683 .unwrap_or_default();
684 true
685 }
686 _ => false,
687 }
688 }
689
english_or_first_font_name(font_ref: &BridgeFontRef, name_id: StringId) -> Option<String>690 fn english_or_first_font_name(font_ref: &BridgeFontRef, name_id: StringId) -> Option<String> {
691 font_ref.with_font(|f| {
692 f.localized_strings(name_id)
693 .english_or_first()
694 .map(|localized_string| localized_string.to_string())
695 })
696 }
697
family_name(font_ref: &BridgeFontRef) -> String698 fn family_name(font_ref: &BridgeFontRef) -> String {
699 font_ref
700 .with_font(|f| {
701 // https://learn.microsoft.com/en-us/typography/opentype/spec/os2#fsselection
702 // Bit 8 of the `fsSelection' field in the `OS/2' table indicates a WWS-only font face.
703 // When this bit is set it means *do not* use the WWS strings.
704 let use_wws = !f
705 .os2()
706 .map(|t| t.fs_selection().contains(SelectionFlags::WWS))
707 .unwrap_or_default();
708 use_wws
709 .then(|| english_or_first_font_name(font_ref, StringId::WWS_FAMILY_NAME))
710 .flatten()
711 .or_else(|| english_or_first_font_name(font_ref, StringId::TYPOGRAPHIC_FAMILY_NAME))
712 .or_else(|| english_or_first_font_name(font_ref, StringId::FAMILY_NAME))
713 })
714 .unwrap_or_default()
715 }
716
postscript_name(font_ref: &BridgeFontRef, out_string: &mut String) -> bool717 fn postscript_name(font_ref: &BridgeFontRef, out_string: &mut String) -> bool {
718 let postscript_name = english_or_first_font_name(font_ref, StringId::POSTSCRIPT_NAME);
719 match postscript_name {
720 Some(name) => {
721 *out_string = name;
722 true
723 }
724 _ => false,
725 }
726 }
727
resolve_palette( font_ref: &BridgeFontRef, base_palette: u16, palette_overrides: &[PaletteOverride], ) -> Vec<u32>728 fn resolve_palette(
729 font_ref: &BridgeFontRef,
730 base_palette: u16,
731 palette_overrides: &[PaletteOverride],
732 ) -> Vec<u32> {
733 let cpal_to_vector = |cpal: &Cpal, palette_index| -> Option<Vec<u32>> {
734 let start_index: usize = cpal
735 .color_record_indices()
736 .get(usize::from(palette_index))?
737 .get()
738 .into();
739 let num_entries: usize = cpal.num_palette_entries().into();
740 let color_records = cpal.color_records_array()?.ok()?;
741 Some(
742 color_records
743 .get(start_index..start_index + num_entries)?
744 .iter()
745 .map(|record| {
746 u32::from_be_bytes([record.alpha, record.red, record.green, record.blue])
747 })
748 .collect(),
749 )
750 };
751
752 font_ref
753 .with_font(|f| {
754 let cpal = f.cpal().ok()?;
755
756 let mut palette = cpal_to_vector(&cpal, base_palette).or(cpal_to_vector(&cpal, 0))?;
757
758 for override_entry in palette_overrides {
759 let index = override_entry.index as usize;
760 if index < palette.len() {
761 palette[index] = override_entry.color_8888;
762 }
763 }
764 Some(palette)
765 })
766 .unwrap_or_default()
767 }
768
has_colr_glyph(font_ref: &BridgeFontRef, format: ColorGlyphFormat, glyph_id: u16) -> bool769 fn has_colr_glyph(font_ref: &BridgeFontRef, format: ColorGlyphFormat, glyph_id: u16) -> bool {
770 font_ref
771 .with_font(|f| {
772 let colrv1_paintable = f
773 .color_glyphs()
774 .get_with_format(GlyphId::from(glyph_id), format);
775 Some(colrv1_paintable.is_some())
776 })
777 .unwrap_or_default()
778 }
779
has_colrv1_glyph(font_ref: &BridgeFontRef, glyph_id: u16) -> bool780 fn has_colrv1_glyph(font_ref: &BridgeFontRef, glyph_id: u16) -> bool {
781 has_colr_glyph(font_ref, ColorGlyphFormat::ColrV1, glyph_id)
782 }
783
has_colrv0_glyph(font_ref: &BridgeFontRef, glyph_id: u16) -> bool784 fn has_colrv0_glyph(font_ref: &BridgeFontRef, glyph_id: u16) -> bool {
785 has_colr_glyph(font_ref, ColorGlyphFormat::ColrV0, glyph_id)
786 }
787
get_colrv1_clip_box( font_ref: &BridgeFontRef, coords: &BridgeNormalizedCoords, glyph_id: u16, size: f32, clip_box: &mut ClipBox, ) -> bool788 fn get_colrv1_clip_box(
789 font_ref: &BridgeFontRef,
790 coords: &BridgeNormalizedCoords,
791 glyph_id: u16,
792 size: f32,
793 clip_box: &mut ClipBox,
794 ) -> bool {
795 let size = match size {
796 x if x == 0.0 => {
797 return false;
798 }
799 _ => Size::new(size),
800 };
801 font_ref
802 .with_font(|f| {
803 match f
804 .color_glyphs()
805 .get_with_format(GlyphId::from(glyph_id), ColorGlyphFormat::ColrV1)?
806 .bounding_box(coords.normalized_coords.coords(), size)
807 {
808 Some(bounding_box) => {
809 *clip_box = ClipBox {
810 x_min: bounding_box.x_min,
811 y_min: bounding_box.y_min,
812 x_max: bounding_box.x_max,
813 y_max: bounding_box.y_max,
814 };
815 Some(true)
816 }
817 _ => None,
818 }
819 })
820 .unwrap_or_default()
821 }
822
823 /// Implements the behavior expected for `SkTypeface::getTableData`, compare
824 /// documentation for this method and the FreeType implementation in Skia.
825 /// * If the target data array is empty, do not copy any data into it, but
826 /// return the size of the table.
827 /// * If the target data buffer is shorted than from offset to the end of the
828 /// table, truncate the data.
829 /// * If offset is longer than the table's length, return 0.
table_data(font_ref: &BridgeFontRef, tag: u32, offset: usize, data: &mut [u8]) -> usize830 fn table_data(font_ref: &BridgeFontRef, tag: u32, offset: usize, data: &mut [u8]) -> usize {
831 let table_data = font_ref
832 .with_font(|f| f.table_data(Tag::from_be_bytes(tag.to_be_bytes())))
833 .unwrap_or_default();
834 let table_data = table_data.as_ref();
835 // Remaining table data size measured from offset to end, or 0 if offset is
836 // too large.
837 let mut to_copy_length = table_data.len().saturating_sub(offset);
838 match data.len() {
839 0 => to_copy_length,
840 _ => {
841 to_copy_length = to_copy_length.min(data.len());
842 let table_offset_data = table_data
843 .get(offset..offset + to_copy_length)
844 .unwrap_or_default();
845 data.get_mut(..table_offset_data.len())
846 .map_or(0, |data_slice| {
847 data_slice.copy_from_slice(table_offset_data);
848 data_slice.len()
849 })
850 }
851 }
852 }
853
table_tags(font_ref: &BridgeFontRef, tags: &mut [u32]) -> u16854 fn table_tags(font_ref: &BridgeFontRef, tags: &mut [u32]) -> u16 {
855 font_ref
856 .with_font(|f| {
857 let table_directory = &f.table_directory;
858 let table_tags_iter = table_directory
859 .table_records()
860 .iter()
861 .map(|table| u32::from_be_bytes(table.tag.get().into_bytes()));
862 tags.iter_mut()
863 .zip(table_tags_iter)
864 .for_each(|(out_tag, table_tag)| *out_tag = table_tag);
865 Some(table_directory.num_tables())
866 })
867 .unwrap_or_default()
868 }
869
variation_position( coords: &BridgeNormalizedCoords, coordinates: &mut [SkiaDesignCoordinate], ) -> isize870 fn variation_position(
871 coords: &BridgeNormalizedCoords,
872 coordinates: &mut [SkiaDesignCoordinate],
873 ) -> isize {
874 if !coordinates.is_empty() {
875 if coords.filtered_user_coords.len() > coordinates.len() {
876 return -1;
877 }
878 let skia_design_coordinates =
879 coords
880 .filtered_user_coords
881 .iter()
882 .map(|setting| SkiaDesignCoordinate {
883 axis: u32::from_be_bytes(setting.selector.into_bytes()),
884 value: setting.value,
885 });
886 for (i, coord) in skia_design_coordinates.enumerate() {
887 coordinates[i] = coord;
888 }
889 }
890 coords.filtered_user_coords.len().try_into().unwrap()
891 }
892
coordinates_for_shifted_named_instance_index( font_ref: &BridgeFontRef, shifted_index: u32, coords: &mut [SkiaDesignCoordinate], ) -> isize893 fn coordinates_for_shifted_named_instance_index(
894 font_ref: &BridgeFontRef,
895 shifted_index: u32,
896 coords: &mut [SkiaDesignCoordinate],
897 ) -> isize {
898 font_ref
899 .with_font(|f| {
900 let fvar = f.fvar().ok()?;
901 let instances = fvar.instances().ok()?;
902 let index: usize = ((shifted_index >> 16) - 1).try_into().unwrap();
903 let instance_coords = instances.get(index).ok()?.coordinates;
904
905 if coords.len() != 0 {
906 if coords.len() < instance_coords.len() {
907 return None;
908 }
909 let axis_coords = f.axes().iter().zip(instance_coords.iter()).enumerate();
910 for (i, axis_coord) in axis_coords {
911 coords[i] = SkiaDesignCoordinate {
912 axis: u32::from_be_bytes(axis_coord.0.tag().to_be_bytes()),
913 value: axis_coord.1.get().to_f32(),
914 };
915 }
916 }
917
918 Some(instance_coords.len() as isize)
919 })
920 .unwrap_or(-1)
921 }
922
num_axes(font_ref: &BridgeFontRef) -> usize923 fn num_axes(font_ref: &BridgeFontRef) -> usize {
924 font_ref
925 .with_font(|f| Some(f.axes().len()))
926 .unwrap_or_default()
927 }
928
populate_axes(font_ref: &BridgeFontRef, mut axis_wrapper: Pin<&mut AxisWrapper>) -> isize929 fn populate_axes(font_ref: &BridgeFontRef, mut axis_wrapper: Pin<&mut AxisWrapper>) -> isize {
930 font_ref
931 .with_font(|f| {
932 let axes = f.axes();
933 // Populate incoming allocated SkFontParameters::Variation::Axis[] only when a
934 // buffer is passed.
935 if axis_wrapper.as_ref().size() > 0 {
936 for (i, axis) in axes.iter().enumerate() {
937 if !axis_wrapper.as_mut().populate_axis(
938 i,
939 u32::from_be_bytes(axis.tag().into_bytes()),
940 axis.min_value(),
941 axis.default_value(),
942 axis.max_value(),
943 axis.is_hidden(),
944 ) {
945 return None;
946 }
947 }
948 }
949 isize::try_from(axes.len()).ok()
950 })
951 .unwrap_or(-1)
952 }
953
make_font_ref_internal<'a>(font_data: &'a [u8], index: u32) -> Result<FontRef<'a>, ReadError>954 fn make_font_ref_internal<'a>(font_data: &'a [u8], index: u32) -> Result<FontRef<'a>, ReadError> {
955 match FileRef::new(font_data) {
956 Ok(file_ref) => match file_ref {
957 FileRef::Font(font_ref) => {
958 // Indices with the higher bits set are meaningful here and do not result in an
959 // error, as they may refer to a named instance and are taken into account by the
960 // Fontations typeface implementation,
961 // compare `coordinates_for_shifted_named_instance_index()`.
962 if index & 0xFFFF > 0 {
963 Err(ReadError::InvalidCollectionIndex(index))
964 } else {
965 Ok(font_ref)
966 }
967 }
968 FileRef::Collection(collection) => collection.get(index),
969 },
970 Err(e) => Err(e),
971 }
972 }
973
make_font_ref<'a>(font_data: &'a [u8], index: u32) -> Box<BridgeFontRef<'a>>974 fn make_font_ref<'a>(font_data: &'a [u8], index: u32) -> Box<BridgeFontRef<'a>> {
975 let font = make_font_ref_internal(font_data, index).ok();
976 let has_any_color = font
977 .as_ref()
978 .map(|f| {
979 f.cbdt().is_ok() ||
980 f.sbix().is_ok() ||
981 // ColorGlyphCollection::get_with_format() first thing checks for presence of colr(),
982 // so we do the same:
983 f.colr().is_ok()
984 })
985 .unwrap_or_default();
986
987 Box::new(BridgeFontRef {
988 font,
989 has_any_color,
990 })
991 }
992
font_ref_is_valid(bridge_font_ref: &BridgeFontRef) -> bool993 fn font_ref_is_valid(bridge_font_ref: &BridgeFontRef) -> bool {
994 bridge_font_ref.font.is_some()
995 }
996
has_any_color_table(bridge_font_ref: &BridgeFontRef) -> bool997 fn has_any_color_table(bridge_font_ref: &BridgeFontRef) -> bool {
998 bridge_font_ref.has_any_color
999 }
1000
get_outline_collection<'a>(font_ref: &'a BridgeFontRef<'a>) -> Box<BridgeOutlineCollection<'a>>1001 fn get_outline_collection<'a>(font_ref: &'a BridgeFontRef<'a>) -> Box<BridgeOutlineCollection<'a>> {
1002 Box::new(
1003 font_ref
1004 .with_font(|f| Some(BridgeOutlineCollection(Some(f.outline_glyphs()))))
1005 .unwrap_or_default(),
1006 )
1007 }
1008
font_or_collection<'a>(font_data: &'a [u8], num_fonts: &mut u32) -> bool1009 fn font_or_collection<'a>(font_data: &'a [u8], num_fonts: &mut u32) -> bool {
1010 match FileRef::new(font_data) {
1011 Ok(FileRef::Collection(collection)) => {
1012 *num_fonts = collection.len();
1013 true
1014 }
1015 Ok(FileRef::Font(_)) => {
1016 *num_fonts = 0u32;
1017 true
1018 }
1019 _ => false,
1020 }
1021 }
1022
num_named_instances(font_ref: &BridgeFontRef) -> usize1023 fn num_named_instances(font_ref: &BridgeFontRef) -> usize {
1024 font_ref
1025 .with_font(|f| Some(f.named_instances().len()))
1026 .unwrap_or_default()
1027 }
1028
resolve_into_normalized_coords( font_ref: &BridgeFontRef, design_coords: &[SkiaDesignCoordinate], ) -> Box<BridgeNormalizedCoords>1029 fn resolve_into_normalized_coords(
1030 font_ref: &BridgeFontRef,
1031 design_coords: &[SkiaDesignCoordinate],
1032 ) -> Box<BridgeNormalizedCoords> {
1033 let variation_tuples = design_coords
1034 .iter()
1035 .map(|coord| (Tag::from_be_bytes(coord.axis.to_be_bytes()), coord.value));
1036 let bridge_normalized_coords = font_ref
1037 .with_font(|f| {
1038 let merged_defaults_with_user = f
1039 .axes()
1040 .iter()
1041 .map(|axis| (axis.tag(), axis.default_value()))
1042 .chain(design_coords.iter().map(|user_coord| {
1043 (
1044 Tag::from_be_bytes(user_coord.axis.to_be_bytes()),
1045 user_coord.value,
1046 )
1047 }));
1048 Some(BridgeNormalizedCoords {
1049 filtered_user_coords: f.axes().filter(merged_defaults_with_user).collect(),
1050 normalized_coords: f.axes().location(variation_tuples),
1051 })
1052 })
1053 .unwrap_or_default();
1054 Box::new(bridge_normalized_coords)
1055 }
1056
normalized_coords_equal(a: &BridgeNormalizedCoords, b: &BridgeNormalizedCoords) -> bool1057 fn normalized_coords_equal(a: &BridgeNormalizedCoords, b: &BridgeNormalizedCoords) -> bool {
1058 a.normalized_coords.coords() == b.normalized_coords.coords()
1059 }
1060
draw_colr_glyph( font_ref: &BridgeFontRef, coords: &BridgeNormalizedCoords, glyph_id: u16, color_painter: Pin<&mut ColorPainterWrapper>, ) -> bool1061 fn draw_colr_glyph(
1062 font_ref: &BridgeFontRef,
1063 coords: &BridgeNormalizedCoords,
1064 glyph_id: u16,
1065 color_painter: Pin<&mut ColorPainterWrapper>,
1066 ) -> bool {
1067 let mut color_painter_impl = ColorPainterImpl {
1068 color_painter_wrapper: color_painter,
1069 // In bounds mode, we do not need to track or forward to the client anything below the
1070 // first clip layer, as the bounds cannot grow after that.
1071 clip_level: 0,
1072 };
1073 font_ref
1074 .with_font(|f| {
1075 let paintable = f.color_glyphs().get(GlyphId::from(glyph_id))?;
1076 paintable
1077 .paint(coords.normalized_coords.coords(), &mut color_painter_impl)
1078 .ok()
1079 })
1080 .is_some()
1081 }
1082
next_color_stop(color_stops: &mut BridgeColorStops, out_stop: &mut ColorStop) -> bool1083 fn next_color_stop(color_stops: &mut BridgeColorStops, out_stop: &mut ColorStop) -> bool {
1084 if let Some(color_stop) = color_stops.stops_iterator.next() {
1085 out_stop.alpha = color_stop.alpha;
1086 out_stop.stop = color_stop.offset;
1087 out_stop.palette_index = color_stop.palette_index;
1088 true
1089 } else {
1090 false
1091 }
1092 }
1093
num_color_stops(color_stops: &BridgeColorStops) -> usize1094 fn num_color_stops(color_stops: &BridgeColorStops) -> usize {
1095 color_stops.num_stops
1096 }
1097
1098 #[allow(non_upper_case_globals)]
get_font_style( font_ref: &BridgeFontRef, coords: &BridgeNormalizedCoords, style: &mut BridgeFontStyle, ) -> bool1099 fn get_font_style(
1100 font_ref: &BridgeFontRef,
1101 coords: &BridgeNormalizedCoords,
1102 style: &mut BridgeFontStyle,
1103 ) -> bool {
1104 const SKIA_SLANT_UPRIGHT: i32 = 0; /* kUpright_Slant */
1105 const SKIA_SLANT_ITALIC: i32 = 1; /* kItalic_Slant */
1106 const SKIA_SLANT_OBLIQUE: i32 = 2; /* kOblique_Slant */
1107
1108 font_ref
1109 .with_font(|f| {
1110 let attrs = f.attributes();
1111 let mut skia_weight = attrs.weight.value().round() as i32;
1112 let mut skia_slant = match attrs.style {
1113 Style::Normal => SKIA_SLANT_UPRIGHT,
1114 Style::Italic => SKIA_SLANT_ITALIC,
1115 _ => SKIA_SLANT_OBLIQUE,
1116 };
1117 //0.5, 0.625, 0.75, 0.875, 1.0, 1.125, 1.25, 1.5, 2.0 map to 1-9
1118 let mut skia_width = match attrs.stretch.ratio() {
1119 x if x <= 0.5625 => 1,
1120 x if x <= 0.6875 => 2,
1121 x if x <= 0.8125 => 3,
1122 x if x <= 0.9375 => 4,
1123 x if x <= 1.0625 => 5,
1124 x if x <= 1.1875 => 6,
1125 x if x <= 1.3750 => 7,
1126 x if x <= 1.7500 => 8,
1127 _ => 9,
1128 };
1129
1130 const wght: Tag = Tag::new(b"wght");
1131 const wdth: Tag = Tag::new(b"wdth");
1132 const slnt: Tag = Tag::new(b"slnt");
1133
1134 for user_coord in coords.filtered_user_coords.iter() {
1135 match user_coord.selector {
1136 wght => skia_weight = user_coord.value.round() as i32,
1137 // 50, 62.5, 75, 87.5, 100, 112.5, 125, 150, 200 map to 1-9
1138 wdth => {
1139 skia_width = match user_coord.value {
1140 x if x <= 56.25 => 1,
1141 x if x <= 68.75 => 2,
1142 x if x <= 81.25 => 3,
1143 x if x <= 93.75 => 4,
1144 x if x <= 106.25 => 5,
1145 x if x <= 118.75 => 6,
1146 x if x <= 137.50 => 7,
1147 x if x <= 175.00 => 8,
1148 _ => 9,
1149 }
1150 }
1151 slnt => {
1152 if skia_slant != SKIA_SLANT_ITALIC {
1153 if user_coord.value == 0.0 {
1154 skia_slant = SKIA_SLANT_UPRIGHT;
1155 } else {
1156 skia_slant = SKIA_SLANT_OBLIQUE
1157 }
1158 }
1159 }
1160 _ => (),
1161 }
1162 }
1163
1164 *style = BridgeFontStyle {
1165 weight: skia_weight,
1166 slant: skia_slant,
1167 width: skia_width,
1168 };
1169 Some(true)
1170 })
1171 .unwrap_or_default()
1172 }
1173
is_embeddable(font_ref: &BridgeFontRef) -> bool1174 fn is_embeddable(font_ref: &BridgeFontRef) -> bool {
1175 font_ref
1176 .with_font(|f| {
1177 let fs_type = f.os2().ok()?.fs_type();
1178 // https://learn.microsoft.com/en-us/typography/opentype/spec/os2#fstype
1179 // Bit 2 and bit 9 must be cleared, "Restricted License embedding" and
1180 // "Bitmap embedding only" must both be unset.
1181 // Implemented to match SkTypeface_FreeType::onGetAdvancedMetrics.
1182 Some(fs_type & 0x202 == 0)
1183 })
1184 .unwrap_or(true)
1185 }
1186
is_subsettable(font_ref: &BridgeFontRef) -> bool1187 fn is_subsettable(font_ref: &BridgeFontRef) -> bool {
1188 font_ref
1189 .with_font(|f| {
1190 let fs_type = f.os2().ok()?.fs_type();
1191 // https://learn.microsoft.com/en-us/typography/opentype/spec/os2#fstype
1192 Some((fs_type & 0x100) == 0)
1193 })
1194 .unwrap_or(true)
1195 }
1196
is_fixed_pitch(font_ref: &BridgeFontRef) -> bool1197 fn is_fixed_pitch(font_ref: &BridgeFontRef) -> bool {
1198 font_ref
1199 .with_font(|f| {
1200 // Compare DWriteFontTypeface::onGetAdvancedMetrics().
1201 Some(
1202 f.post().ok()?.is_fixed_pitch() != 0
1203 || f.hhea().ok()?.number_of_long_metrics() == 1,
1204 )
1205 })
1206 .unwrap_or_default()
1207 }
1208
is_serif_style(font_ref: &BridgeFontRef) -> bool1209 fn is_serif_style(font_ref: &BridgeFontRef) -> bool {
1210 const FAMILY_TYPE_TEXT_AND_DISPLAY: u8 = 2;
1211 const SERIF_STYLE_COVE: u8 = 2;
1212 const SERIF_STYLE_TRIANGLE: u8 = 10;
1213 font_ref
1214 .with_font(|f| {
1215 // Compare DWriteFontTypeface::onGetAdvancedMetrics().
1216 let panose = f.os2().ok()?.panose_10();
1217 let family_type = panose[0];
1218
1219 match family_type {
1220 FAMILY_TYPE_TEXT_AND_DISPLAY => {
1221 let serif_style = panose[1];
1222 Some((SERIF_STYLE_COVE..=SERIF_STYLE_TRIANGLE).contains(&serif_style))
1223 }
1224 _ => None,
1225 }
1226 })
1227 .unwrap_or_default()
1228 }
1229
is_script_style(font_ref: &BridgeFontRef) -> bool1230 fn is_script_style(font_ref: &BridgeFontRef) -> bool {
1231 const FAMILY_TYPE_SCRIPT: u8 = 3;
1232 font_ref
1233 .with_font(|f| {
1234 // Compare DWriteFontTypeface::onGetAdvancedMetrics().
1235 let family_type = f.os2().ok()?.panose_10()[0];
1236 Some(family_type == FAMILY_TYPE_SCRIPT)
1237 })
1238 .unwrap_or_default()
1239 }
1240
italic_angle(font_ref: &BridgeFontRef) -> i321241 fn italic_angle(font_ref: &BridgeFontRef) -> i32 {
1242 font_ref
1243 .with_font(|f| Some(f.post().ok()?.italic_angle().to_i32()))
1244 .unwrap_or_default()
1245 }
1246
1247 pub struct BridgeFontRef<'a> {
1248 font: Option<FontRef<'a>>,
1249 has_any_color: bool,
1250 }
1251
1252 impl<'a> BridgeFontRef<'a> {
with_font<T>(&'a self, f: impl FnOnce(&'a FontRef) -> Option<T>) -> Option<T>1253 fn with_font<T>(&'a self, f: impl FnOnce(&'a FontRef) -> Option<T>) -> Option<T> {
1254 f(self.font.as_ref()?)
1255 }
1256 }
1257
1258 #[derive(Default)]
1259 struct BridgeOutlineCollection<'a>(Option<OutlineGlyphCollection<'a>>);
1260
1261 #[derive(Default)]
1262 struct BridgeNormalizedCoords {
1263 normalized_coords: Location,
1264 filtered_user_coords: Vec<VariationSetting>,
1265 }
1266
1267 struct BridgeLocalizedStrings<'a> {
1268 #[allow(dead_code)]
1269 localized_strings: LocalizedStrings<'a>,
1270 }
1271
1272 pub struct BridgeColorStops<'a> {
1273 pub stops_iterator: Box<dyn Iterator<Item = &'a skrifa::color::ColorStop> + 'a>,
1274 pub num_stops: usize,
1275 }
1276
1277 mod bitmap {
1278
1279 use read_fonts::{
1280 tables::{
1281 bitmap::{BitmapContent, BitmapData, BitmapDataFormat, BitmapMetrics, BitmapSize},
1282 sbix::{GlyphData, Strike},
1283 },
1284 FontRef, TableProvider,
1285 };
1286
1287 use font_types::{BoundingBox, GlyphId};
1288 use skrifa::{
1289 instance::{LocationRef, Size},
1290 metrics::GlyphMetrics,
1291 };
1292
1293 use crate::{ffi::BitmapMetrics as FfiBitmapMetrics, BridgeFontRef};
1294
1295 pub enum BitmapPixelData<'a> {
1296 PngData(&'a [u8]),
1297 }
1298
1299 struct CblcGlyph<'a> {
1300 bitmap_data: BitmapData<'a>,
1301 ppem_x: u8,
1302 ppem_y: u8,
1303 }
1304
1305 struct SbixGlyph<'a> {
1306 glyph_data: GlyphData<'a>,
1307 ppem: u16,
1308 }
1309
1310 #[derive(Default)]
1311 pub struct BridgeBitmapGlyph<'a> {
1312 pub data: Option<BitmapPixelData<'a>>,
1313 pub metrics: FfiBitmapMetrics,
1314 }
1315
1316 trait StrikeSizeRetrievable {
strike_size(&self) -> f321317 fn strike_size(&self) -> f32;
1318 }
1319
1320 impl StrikeSizeRetrievable for &BitmapSize {
strike_size(&self) -> f321321 fn strike_size(&self) -> f32 {
1322 self.ppem_y() as f32
1323 }
1324 }
1325
1326 impl StrikeSizeRetrievable for Strike<'_> {
strike_size(&self) -> f321327 fn strike_size(&self) -> f32 {
1328 self.ppem() as f32
1329 }
1330 }
1331
1332 // 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,1333 fn best_strike_size<T>(strikes: impl Iterator<Item = T>, font_size: f32) -> Option<T>
1334 where
1335 T: StrikeSizeRetrievable,
1336 {
1337 // After a bigger strike size is found, the order of strike sizes smaller
1338 // than the requested font size does not matter anymore. A new strike size
1339 // is only an improvement if it gets closer to the requested font size (and
1340 // is smaller than the current best, but bigger than font size). And vice
1341 // versa: As long as we have found only smaller ones so far, only any strike
1342 // size matters that is bigger than the current best.
1343 strikes.reduce(|best, entry| {
1344 let entry_size = entry.strike_size();
1345 if (entry_size >= font_size && entry_size < best.strike_size())
1346 || (best.strike_size() < font_size && entry_size > best.strike_size())
1347 {
1348 entry
1349 } else {
1350 best
1351 }
1352 })
1353 }
1354
sbix_glyph<'a>( font_ref: &'a FontRef, glyph_id: GlyphId, font_size: Option<f32>, ) -> Option<SbixGlyph<'a>>1355 fn sbix_glyph<'a>(
1356 font_ref: &'a FontRef,
1357 glyph_id: GlyphId,
1358 font_size: Option<f32>,
1359 ) -> Option<SbixGlyph<'a>> {
1360 let sbix = font_ref.sbix().ok()?;
1361 let mut strikes = sbix.strikes().iter().filter_map(|strike| strike.ok());
1362
1363 let best_strike = match font_size {
1364 Some(size) => best_strike_size(strikes, size),
1365 _ => strikes.next(),
1366 }?;
1367
1368 Some(SbixGlyph {
1369 ppem: best_strike.ppem(),
1370 glyph_data: best_strike.glyph_data(glyph_id).ok()??,
1371 })
1372 }
1373
cblc_glyph<'a>( font_ref: &'a FontRef, glyph_id: GlyphId, font_size: Option<f32>, ) -> Option<CblcGlyph<'a>>1374 fn cblc_glyph<'a>(
1375 font_ref: &'a FontRef,
1376 glyph_id: GlyphId,
1377 font_size: Option<f32>,
1378 ) -> Option<CblcGlyph<'a>> {
1379 let cblc = font_ref.cblc().ok()?;
1380 let cbdt = font_ref.cbdt().ok()?;
1381
1382 let strikes = &cblc.bitmap_sizes();
1383 let best_strike = font_size
1384 .and_then(|size| best_strike_size(strikes.iter(), size))
1385 .or(strikes.get(0))?;
1386
1387 let location = best_strike.location(cblc.offset_data(), glyph_id).ok()?;
1388
1389 Some(CblcGlyph {
1390 bitmap_data: cbdt.data(&location).ok()?,
1391 ppem_x: best_strike.ppem_x,
1392 ppem_y: best_strike.ppem_y,
1393 })
1394 }
1395
has_bitmap_glyph(font_ref: &BridgeFontRef, glyph_id: u16) -> bool1396 pub fn has_bitmap_glyph(font_ref: &BridgeFontRef, glyph_id: u16) -> bool {
1397 font_ref
1398 .with_font(|font| {
1399 let glyph_id = GlyphId::from(glyph_id);
1400 let has_sbix = sbix_glyph(font, glyph_id, None).is_some();
1401 let has_cblc = cblc_glyph(font, glyph_id, None).is_some();
1402 Some(has_sbix || has_cblc)
1403 })
1404 .unwrap_or_default()
1405 }
1406
glyf_bounds(font_ref: &FontRef, glyph_id: GlyphId) -> Option<BoundingBox<i16>>1407 fn glyf_bounds(font_ref: &FontRef, glyph_id: GlyphId) -> Option<BoundingBox<i16>> {
1408 let glyf_table = font_ref.glyf().ok()?;
1409 let glyph = font_ref
1410 .loca(None)
1411 .ok()?
1412 .get_glyf(glyph_id, &glyf_table)
1413 .ok()??;
1414 Some(BoundingBox {
1415 x_min: glyph.x_min(),
1416 y_min: glyph.y_min(),
1417 x_max: glyph.x_max(),
1418 y_max: glyph.y_max(),
1419 })
1420 }
1421
bitmap_glyph<'a>( font_ref: &'a BridgeFontRef, glyph_id: u16, font_size: f32, ) -> Box<BridgeBitmapGlyph<'a>>1422 pub unsafe fn bitmap_glyph<'a>(
1423 font_ref: &'a BridgeFontRef,
1424 glyph_id: u16,
1425 font_size: f32,
1426 ) -> Box<BridgeBitmapGlyph<'a>> {
1427 let glyph_id = GlyphId::from(glyph_id);
1428 font_ref
1429 .with_font(|font| {
1430 if let Some(sbix_glyph) = sbix_glyph(font, glyph_id, Some(font_size)) {
1431 // https://learn.microsoft.com/en-us/typography/opentype/spec/sbix
1432 // "If there is a glyph contour, the glyph design space
1433 // origin for the graphic is placed at the lower left corner
1434 // of the glyph bounding box (xMin, yMin)."
1435 let glyf_bb = glyf_bounds(font, glyph_id).unwrap_or_default();
1436 let glyf_left_side_bearing =
1437 GlyphMetrics::new(font, Size::unscaled(), LocationRef::default())
1438 .left_side_bearing(glyph_id)
1439 .unwrap_or_default();
1440
1441 return Some(Box::new(BridgeBitmapGlyph {
1442 data: Some(BitmapPixelData::PngData(sbix_glyph.glyph_data.data())),
1443 metrics: FfiBitmapMetrics {
1444 bearing_x: glyf_left_side_bearing,
1445 bearing_y: glyf_bb.y_min as f32,
1446 inner_bearing_x: sbix_glyph.glyph_data.origin_offset_x() as f32,
1447 inner_bearing_y: sbix_glyph.glyph_data.origin_offset_y() as f32,
1448 ppem_x: sbix_glyph.ppem as f32,
1449 ppem_y: sbix_glyph.ppem as f32,
1450 placement_origin_bottom_left: true,
1451 advance: f32::NAN,
1452 },
1453 }));
1454 } else if let Some(cblc_glyph) = cblc_glyph(font, glyph_id, Some(font_size)) {
1455 let (bearing_x, bearing_y, advance) = match cblc_glyph.bitmap_data.metrics {
1456 BitmapMetrics::Small(small_metrics) => (
1457 small_metrics.bearing_x() as f32,
1458 small_metrics.bearing_y() as f32,
1459 small_metrics.advance as f32,
1460 ),
1461 BitmapMetrics::Big(big_metrics) => (
1462 big_metrics.hori_bearing_x() as f32,
1463 big_metrics.hori_bearing_y() as f32,
1464 big_metrics.hori_advance as f32,
1465 ),
1466 };
1467 if let BitmapContent::Data(BitmapDataFormat::Png, png_buffer) =
1468 cblc_glyph.bitmap_data.content
1469 {
1470 return Some(Box::new(BridgeBitmapGlyph {
1471 data: Some(BitmapPixelData::PngData(png_buffer)),
1472 metrics: FfiBitmapMetrics {
1473 bearing_x: 0.0,
1474 bearing_y: 0.0,
1475 inner_bearing_x: bearing_x,
1476 inner_bearing_y: bearing_y,
1477 ppem_x: cblc_glyph.ppem_x as f32,
1478 ppem_y: cblc_glyph.ppem_y as f32,
1479 placement_origin_bottom_left: false,
1480 advance: advance,
1481 },
1482 }));
1483 }
1484 }
1485 None
1486 })
1487 .unwrap_or_default()
1488 }
1489
png_data<'a>(bitmap_glyph: &'a BridgeBitmapGlyph) -> &'a [u8]1490 pub unsafe fn png_data<'a>(bitmap_glyph: &'a BridgeBitmapGlyph) -> &'a [u8] {
1491 match bitmap_glyph.data {
1492 Some(BitmapPixelData::PngData(glyph_data)) => glyph_data,
1493 _ => &[],
1494 }
1495 }
1496
bitmap_metrics<'a>(bitmap_glyph: &'a BridgeBitmapGlyph) -> &'a FfiBitmapMetrics1497 pub unsafe fn bitmap_metrics<'a>(bitmap_glyph: &'a BridgeBitmapGlyph) -> &'a FfiBitmapMetrics {
1498 &bitmap_glyph.metrics
1499 }
1500 }
1501
1502 pub struct BridgeMappingIndex(MappingIndex);
1503 pub struct BridgeHintingInstance(Option<HintingInstance>);
1504
1505 #[cxx::bridge(namespace = "fontations_ffi")]
1506 mod ffi {
1507 struct ColorStop {
1508 stop: f32,
1509 palette_index: u16,
1510 alpha: f32,
1511 }
1512
1513 #[derive(Default)]
1514 struct Metrics {
1515 top: f32,
1516 ascent: f32,
1517 descent: f32,
1518 bottom: f32,
1519 leading: f32,
1520 avg_char_width: f32,
1521 max_char_width: f32,
1522 x_min: f32,
1523 x_max: f32,
1524 x_height: f32,
1525 cap_height: f32,
1526 underline_position: f32,
1527 underline_thickness: f32,
1528 strikeout_position: f32,
1529 strikeout_thickness: f32,
1530 }
1531
1532 #[derive(Clone, Copy, Default, PartialEq)]
1533 struct FfiPoint {
1534 x: f32,
1535 y: f32,
1536 }
1537
1538 struct BridgeLocalizedName {
1539 string: String,
1540 language: String,
1541 }
1542
1543 #[derive(PartialEq, Debug, Default)]
1544 struct SkiaDesignCoordinate {
1545 axis: u32,
1546 value: f32,
1547 }
1548
1549 struct BridgeScalerMetrics {
1550 has_overlaps: bool,
1551 }
1552
1553 struct PaletteOverride {
1554 index: u16,
1555 color_8888: u32,
1556 }
1557
1558 struct ClipBox {
1559 x_min: f32,
1560 y_min: f32,
1561 x_max: f32,
1562 y_max: f32,
1563 }
1564
1565 struct Transform {
1566 xx: f32,
1567 xy: f32,
1568 yx: f32,
1569 yy: f32,
1570 dx: f32,
1571 dy: f32,
1572 }
1573
1574 struct FillLinearParams {
1575 x0: f32,
1576 y0: f32,
1577 x1: f32,
1578 y1: f32,
1579 }
1580
1581 struct FillRadialParams {
1582 x0: f32,
1583 y0: f32,
1584 r0: f32,
1585 x1: f32,
1586 y1: f32,
1587 r1: f32,
1588 }
1589
1590 struct FillSweepParams {
1591 x0: f32,
1592 y0: f32,
1593 start_angle: f32,
1594 end_angle: f32,
1595 }
1596
1597 // This type is used to mirror SkFontStyle values for Weight, Slant and Width
1598 #[derive(Default)]
1599 pub struct BridgeFontStyle {
1600 pub weight: i32,
1601 pub slant: i32,
1602 pub width: i32,
1603 }
1604
1605 #[derive(Default)]
1606 struct BitmapMetrics {
1607 // Outer glyph bearings that affect the computed bounds. We distinguish
1608 // those here from `inner_bearing_*` to account for CoreText behavior in
1609 // SBIX placement. Where the sbix originOffsetX/Y are applied only
1610 // within the bounds. Specified in font units.
1611 // 0 for CBDT, CBLC.
1612 bearing_x: f32,
1613 bearing_y: f32,
1614 // Scale factors to scale image to 1em.
1615 ppem_x: f32,
1616 ppem_y: f32,
1617 // Account for the fact that Sbix and CBDT/CBLC have a different origin
1618 // definition.
1619 placement_origin_bottom_left: bool,
1620 // Specified as a pixel value, to be scaled by `ppem_*` as an
1621 // offset applied to placing the image within the bounds rectangle.
1622 inner_bearing_x: f32,
1623 inner_bearing_y: f32,
1624 // Some, but not all, bitmap glyphs have a special bitmap advance
1625 advance: f32,
1626 }
1627
1628 extern "Rust" {
1629 type BridgeFontRef<'a>;
make_font_ref<'a>(font_data: &'a [u8], index: u32) -> Box<BridgeFontRef<'a>>1630 unsafe fn make_font_ref<'a>(font_data: &'a [u8], index: u32) -> Box<BridgeFontRef<'a>>;
1631 // Returns whether BridgeFontRef is a valid font containing at
1632 // least a valid sfnt structure from which tables can be
1633 // accessed. This is what instantiation in make_font_ref checks
1634 // for. (see FontRef::new in read_fonts's lib.rs). Implemented
1635 // by returning whether the option is Some() and thus whether a
1636 // FontRef instantiation succeeded and a table directory was
1637 // accessible.
font_ref_is_valid(bridge_font_ref: &BridgeFontRef) -> bool1638 fn font_ref_is_valid(bridge_font_ref: &BridgeFontRef) -> bool;
1639
1640 // Optimization to quickly rule out that the font has any color tables.
has_any_color_table(bridge_font_ref: &BridgeFontRef) -> bool1641 fn has_any_color_table(bridge_font_ref: &BridgeFontRef) -> bool;
1642
1643 type BridgeOutlineCollection<'a>;
get_outline_collection<'a>( font_ref: &'a BridgeFontRef<'a>, ) -> Box<BridgeOutlineCollection<'a>>1644 unsafe fn get_outline_collection<'a>(
1645 font_ref: &'a BridgeFontRef<'a>,
1646 ) -> Box<BridgeOutlineCollection<'a>>;
1647
1648 /// Returns true on a font or collection, sets `num_fonts``
1649 /// to 0 if single font file, and to > 0 for a TrueType collection.
1650 /// 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) -> bool1651 unsafe fn font_or_collection<'a>(font_data: &'a [u8], num_fonts: &mut u32) -> bool;
1652
num_named_instances(font_ref: &BridgeFontRef) -> usize1653 unsafe fn num_named_instances(font_ref: &BridgeFontRef) -> usize;
1654
1655 type BridgeMappingIndex;
make_mapping_index<'a>(font_ref: &'a BridgeFontRef) -> Box<BridgeMappingIndex>1656 unsafe fn make_mapping_index<'a>(font_ref: &'a BridgeFontRef) -> Box<BridgeMappingIndex>;
1657
1658 type BridgeHintingInstance;
make_hinting_instance<'a>( outlines: &BridgeOutlineCollection, size: f32, coords: &BridgeNormalizedCoords, do_light_hinting: bool, do_lcd_antialiasing: bool, lcd_orientation_vertical: bool, force_autohinting: bool, ) -> Box<BridgeHintingInstance>1659 unsafe fn make_hinting_instance<'a>(
1660 outlines: &BridgeOutlineCollection,
1661 size: f32,
1662 coords: &BridgeNormalizedCoords,
1663 do_light_hinting: bool,
1664 do_lcd_antialiasing: bool,
1665 lcd_orientation_vertical: bool,
1666 force_autohinting: bool,
1667 ) -> Box<BridgeHintingInstance>;
make_mono_hinting_instance<'a>( outlines: &BridgeOutlineCollection, size: f32, coords: &BridgeNormalizedCoords, ) -> Box<BridgeHintingInstance>1668 unsafe fn make_mono_hinting_instance<'a>(
1669 outlines: &BridgeOutlineCollection,
1670 size: f32,
1671 coords: &BridgeNormalizedCoords,
1672 ) -> Box<BridgeHintingInstance>;
no_hinting_instance<'a>() -> Box<BridgeHintingInstance>1673 unsafe fn no_hinting_instance<'a>() -> Box<BridgeHintingInstance>;
1674
lookup_glyph_or_zero( font_ref: &BridgeFontRef, map: &BridgeMappingIndex, codepoint: u32, ) -> u161675 fn lookup_glyph_or_zero(
1676 font_ref: &BridgeFontRef,
1677 map: &BridgeMappingIndex,
1678 codepoint: u32,
1679 ) -> u16;
1680
get_path_verbs_points( outlines: &BridgeOutlineCollection, glyph_id: u16, size: f32, coords: &BridgeNormalizedCoords, hinting_instance: &BridgeHintingInstance, verbs: &mut Vec<u8>, points: &mut Vec<FfiPoint>, scaler_metrics: &mut BridgeScalerMetrics, ) -> bool1681 fn get_path_verbs_points(
1682 outlines: &BridgeOutlineCollection,
1683 glyph_id: u16,
1684 size: f32,
1685 coords: &BridgeNormalizedCoords,
1686 hinting_instance: &BridgeHintingInstance,
1687 verbs: &mut Vec<u8>,
1688 points: &mut Vec<FfiPoint>,
1689 scaler_metrics: &mut BridgeScalerMetrics,
1690 ) -> bool;
1691
shrink_verbs_points_if_needed(verbs: &mut Vec<u8>, points: &mut Vec<FfiPoint>)1692 fn shrink_verbs_points_if_needed(verbs: &mut Vec<u8>, points: &mut Vec<FfiPoint>);
1693
unhinted_advance_width_or_zero( font_ref: &BridgeFontRef, size: f32, coords: &BridgeNormalizedCoords, glyph_id: u16, ) -> f321694 fn unhinted_advance_width_or_zero(
1695 font_ref: &BridgeFontRef,
1696 size: f32,
1697 coords: &BridgeNormalizedCoords,
1698 glyph_id: u16,
1699 ) -> f32;
scaler_hinted_advance_width( outlines: &BridgeOutlineCollection, hinting_instance: &BridgeHintingInstance, glyph_id: u16, out_advance_width: &mut f32, ) -> bool1700 fn scaler_hinted_advance_width(
1701 outlines: &BridgeOutlineCollection,
1702 hinting_instance: &BridgeHintingInstance,
1703 glyph_id: u16,
1704 out_advance_width: &mut f32,
1705 ) -> bool;
units_per_em_or_zero(font_ref: &BridgeFontRef) -> u161706 fn units_per_em_or_zero(font_ref: &BridgeFontRef) -> u16;
get_skia_metrics( font_ref: &BridgeFontRef, size: f32, coords: &BridgeNormalizedCoords, ) -> Metrics1707 fn get_skia_metrics(
1708 font_ref: &BridgeFontRef,
1709 size: f32,
1710 coords: &BridgeNormalizedCoords,
1711 ) -> Metrics;
get_unscaled_metrics( font_ref: &BridgeFontRef, coords: &BridgeNormalizedCoords, ) -> Metrics1712 fn get_unscaled_metrics(
1713 font_ref: &BridgeFontRef,
1714 coords: &BridgeNormalizedCoords,
1715 ) -> Metrics;
num_glyphs(font_ref: &BridgeFontRef) -> u161716 fn num_glyphs(font_ref: &BridgeFontRef) -> u16;
fill_glyph_to_unicode_map(font_ref: &BridgeFontRef, map: &mut [u32])1717 fn fill_glyph_to_unicode_map(font_ref: &BridgeFontRef, map: &mut [u32]);
family_name(font_ref: &BridgeFontRef) -> String1718 fn family_name(font_ref: &BridgeFontRef) -> String;
postscript_name(font_ref: &BridgeFontRef, out_string: &mut String) -> bool1719 fn postscript_name(font_ref: &BridgeFontRef, out_string: &mut String) -> bool;
1720
1721 /// Receives a slice of palette overrides that will be merged
1722 /// with the specified base palette of the font. The result is a
1723 /// palette of RGBA, 8-bit per component, colors, consisting of
1724 /// palette entries merged with overrides.
resolve_palette( font_ref: &BridgeFontRef, base_palette: u16, palette_overrides: &[PaletteOverride], ) -> Vec<u32>1725 fn resolve_palette(
1726 font_ref: &BridgeFontRef,
1727 base_palette: u16,
1728 palette_overrides: &[PaletteOverride],
1729 ) -> Vec<u32>;
1730
has_colrv1_glyph(font_ref: &BridgeFontRef, glyph_id: u16) -> bool1731 fn has_colrv1_glyph(font_ref: &BridgeFontRef, glyph_id: u16) -> bool;
has_colrv0_glyph(font_ref: &BridgeFontRef, glyph_id: u16) -> bool1732 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, ) -> bool1733 fn get_colrv1_clip_box(
1734 font_ref: &BridgeFontRef,
1735 coords: &BridgeNormalizedCoords,
1736 glyph_id: u16,
1737 size: f32,
1738 clip_box: &mut ClipBox,
1739 ) -> bool;
1740
1741 type BridgeBitmapGlyph<'a>;
has_bitmap_glyph(font_ref: &BridgeFontRef, glyph_id: u16) -> bool1742 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>>1743 unsafe fn bitmap_glyph<'a>(
1744 font_ref: &'a BridgeFontRef,
1745 glyph_id: u16,
1746 font_size: f32,
1747 ) -> Box<BridgeBitmapGlyph<'a>>;
png_data<'a>(bitmap_glyph: &'a BridgeBitmapGlyph) -> &'a [u8]1748 unsafe fn png_data<'a>(bitmap_glyph: &'a BridgeBitmapGlyph) -> &'a [u8];
bitmap_metrics<'a>(bitmap_glyph: &'a BridgeBitmapGlyph) -> &'a BitmapMetrics1749 unsafe fn bitmap_metrics<'a>(bitmap_glyph: &'a BridgeBitmapGlyph) -> &'a BitmapMetrics;
1750
table_data(font_ref: &BridgeFontRef, tag: u32, offset: usize, data: &mut [u8]) -> usize1751 fn table_data(font_ref: &BridgeFontRef, tag: u32, offset: usize, data: &mut [u8]) -> usize;
table_tags(font_ref: &BridgeFontRef, tags: &mut [u32]) -> u161752 fn table_tags(font_ref: &BridgeFontRef, tags: &mut [u32]) -> u16;
variation_position( coords: &BridgeNormalizedCoords, coordinates: &mut [SkiaDesignCoordinate], ) -> isize1753 fn variation_position(
1754 coords: &BridgeNormalizedCoords,
1755 coordinates: &mut [SkiaDesignCoordinate],
1756 ) -> isize;
1757 // Fills the passed-in slice with the axis coordinates for a given
1758 // shifted named instance index. A shifted named instance index is a
1759 // 32bit value that contains the index to a named instance left-shifted
1760 // by 16bits and offset by 1. This mirrors FreeType behavior to smuggle
1761 // named instance identifiers through a TrueType collection index.
1762 // Returns the number of coordinates copied. If the slice length is 0,
1763 // performs no copy but only returns the number of axis coordinates for
1764 // the given shifted index. Returns -1 on error.
coordinates_for_shifted_named_instance_index( font_ref: &BridgeFontRef, shifted_index: u32, coords: &mut [SkiaDesignCoordinate], ) -> isize1765 fn coordinates_for_shifted_named_instance_index(
1766 font_ref: &BridgeFontRef,
1767 shifted_index: u32,
1768 coords: &mut [SkiaDesignCoordinate],
1769 ) -> isize;
1770
num_axes(font_ref: &BridgeFontRef) -> usize1771 fn num_axes(font_ref: &BridgeFontRef) -> usize;
1772
populate_axes(font_ref: &BridgeFontRef, axis_wrapper: Pin<&mut AxisWrapper>) -> isize1773 fn populate_axes(font_ref: &BridgeFontRef, axis_wrapper: Pin<&mut AxisWrapper>) -> isize;
1774
1775 type BridgeLocalizedStrings<'a>;
get_localized_strings<'a>( font_ref: &'a BridgeFontRef<'a>, ) -> Box<BridgeLocalizedStrings<'a>>1776 unsafe fn get_localized_strings<'a>(
1777 font_ref: &'a BridgeFontRef<'a>,
1778 ) -> Box<BridgeLocalizedStrings<'a>>;
localized_name_next( bridge_localized_strings: &mut BridgeLocalizedStrings, out_localized_name: &mut BridgeLocalizedName, ) -> bool1779 fn localized_name_next(
1780 bridge_localized_strings: &mut BridgeLocalizedStrings,
1781 out_localized_name: &mut BridgeLocalizedName,
1782 ) -> bool;
1783
1784 type BridgeNormalizedCoords;
resolve_into_normalized_coords( font_ref: &BridgeFontRef, design_coords: &[SkiaDesignCoordinate], ) -> Box<BridgeNormalizedCoords>1785 fn resolve_into_normalized_coords(
1786 font_ref: &BridgeFontRef,
1787 design_coords: &[SkiaDesignCoordinate],
1788 ) -> Box<BridgeNormalizedCoords>;
1789
normalized_coords_equal(a: &BridgeNormalizedCoords, b: &BridgeNormalizedCoords) -> bool1790 fn normalized_coords_equal(a: &BridgeNormalizedCoords, b: &BridgeNormalizedCoords) -> bool;
1791
draw_colr_glyph( font_ref: &BridgeFontRef, coords: &BridgeNormalizedCoords, glyph_id: u16, color_painter: Pin<&mut ColorPainterWrapper>, ) -> bool1792 fn draw_colr_glyph(
1793 font_ref: &BridgeFontRef,
1794 coords: &BridgeNormalizedCoords,
1795 glyph_id: u16,
1796 color_painter: Pin<&mut ColorPainterWrapper>,
1797 ) -> bool;
1798
1799 type BridgeColorStops<'a>;
next_color_stop(color_stops: &mut BridgeColorStops, stop: &mut ColorStop) -> bool1800 fn next_color_stop(color_stops: &mut BridgeColorStops, stop: &mut ColorStop) -> bool;
num_color_stops(color_stops: &BridgeColorStops) -> usize1801 fn num_color_stops(color_stops: &BridgeColorStops) -> usize;
1802
get_font_style( font_ref: &BridgeFontRef, coords: &BridgeNormalizedCoords, font_style: &mut BridgeFontStyle, ) -> bool1803 fn get_font_style(
1804 font_ref: &BridgeFontRef,
1805 coords: &BridgeNormalizedCoords,
1806 font_style: &mut BridgeFontStyle,
1807 ) -> bool;
1808
1809 // Additional low-level access functions needed for generateAdvancedMetrics().
is_embeddable(font_ref: &BridgeFontRef) -> bool1810 fn is_embeddable(font_ref: &BridgeFontRef) -> bool;
is_subsettable(font_ref: &BridgeFontRef) -> bool1811 fn is_subsettable(font_ref: &BridgeFontRef) -> bool;
is_fixed_pitch(font_ref: &BridgeFontRef) -> bool1812 fn is_fixed_pitch(font_ref: &BridgeFontRef) -> bool;
is_serif_style(font_ref: &BridgeFontRef) -> bool1813 fn is_serif_style(font_ref: &BridgeFontRef) -> bool;
is_script_style(font_ref: &BridgeFontRef) -> bool1814 fn is_script_style(font_ref: &BridgeFontRef) -> bool;
italic_angle(font_ref: &BridgeFontRef) -> i321815 fn italic_angle(font_ref: &BridgeFontRef) -> i32;
1816 }
1817
1818 unsafe extern "C++" {
1819
1820 include!("src/ports/fontations/src/skpath_bridge.h");
1821
1822 type AxisWrapper;
1823
populate_axis( self: Pin<&mut AxisWrapper>, i: usize, axis: u32, min: f32, def: f32, max: f32, hidden: bool, ) -> bool1824 fn populate_axis(
1825 self: Pin<&mut AxisWrapper>,
1826 i: usize,
1827 axis: u32,
1828 min: f32,
1829 def: f32,
1830 max: f32,
1831 hidden: bool,
1832 ) -> bool;
size(self: Pin<&AxisWrapper>) -> usize1833 fn size(self: Pin<&AxisWrapper>) -> usize;
1834
1835 type ColorPainterWrapper;
1836
is_bounds_mode(self: Pin<&mut ColorPainterWrapper>) -> bool1837 fn is_bounds_mode(self: Pin<&mut ColorPainterWrapper>) -> bool;
push_transform(self: Pin<&mut ColorPainterWrapper>, transform: &Transform)1838 fn push_transform(self: Pin<&mut ColorPainterWrapper>, transform: &Transform);
pop_transform(self: Pin<&mut ColorPainterWrapper>)1839 fn pop_transform(self: Pin<&mut ColorPainterWrapper>);
push_clip_glyph(self: Pin<&mut ColorPainterWrapper>, glyph_id: u16)1840 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, )1841 fn push_clip_rectangle(
1842 self: Pin<&mut ColorPainterWrapper>,
1843 x_min: f32,
1844 y_min: f32,
1845 x_max: f32,
1846 y_max: f32,
1847 );
pop_clip(self: Pin<&mut ColorPainterWrapper>)1848 fn pop_clip(self: Pin<&mut ColorPainterWrapper>);
1849
fill_solid(self: Pin<&mut ColorPainterWrapper>, palette_index: u16, alpha: f32)1850 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, )1851 fn fill_linear(
1852 self: Pin<&mut ColorPainterWrapper>,
1853 fill_linear_params: &FillLinearParams,
1854 color_stops: &mut BridgeColorStops,
1855 extend_mode: u8,
1856 );
fill_radial( self: Pin<&mut ColorPainterWrapper>, fill_radial_params: &FillRadialParams, color_stops: &mut BridgeColorStops, extend_mode: u8, )1857 fn fill_radial(
1858 self: Pin<&mut ColorPainterWrapper>,
1859 fill_radial_params: &FillRadialParams,
1860 color_stops: &mut BridgeColorStops,
1861 extend_mode: u8,
1862 );
fill_sweep( self: Pin<&mut ColorPainterWrapper>, fill_sweep_params: &FillSweepParams, color_stops: &mut BridgeColorStops, extend_mode: u8, )1863 fn fill_sweep(
1864 self: Pin<&mut ColorPainterWrapper>,
1865 fill_sweep_params: &FillSweepParams,
1866 color_stops: &mut BridgeColorStops,
1867 extend_mode: u8,
1868 );
1869
1870 // Optimized functions.
fill_glyph_solid( self: Pin<&mut ColorPainterWrapper>, glyph_id: u16, palette_index: u16, alpha: f32, )1871 fn fill_glyph_solid(
1872 self: Pin<&mut ColorPainterWrapper>,
1873 glyph_id: u16,
1874 palette_index: u16,
1875 alpha: f32,
1876 );
fill_glyph_linear( self: Pin<&mut ColorPainterWrapper>, glyph_id: u16, fill_transform: &Transform, fill_linear_params: &FillLinearParams, color_stops: &mut BridgeColorStops, extend_mode: u8, )1877 fn fill_glyph_linear(
1878 self: Pin<&mut ColorPainterWrapper>,
1879 glyph_id: u16,
1880 fill_transform: &Transform,
1881 fill_linear_params: &FillLinearParams,
1882 color_stops: &mut BridgeColorStops,
1883 extend_mode: u8,
1884 );
fill_glyph_radial( self: Pin<&mut ColorPainterWrapper>, glyph_id: u16, fill_transform: &Transform, fill_radial_params: &FillRadialParams, color_stops: &mut BridgeColorStops, extend_mode: u8, )1885 fn fill_glyph_radial(
1886 self: Pin<&mut ColorPainterWrapper>,
1887 glyph_id: u16,
1888 fill_transform: &Transform,
1889 fill_radial_params: &FillRadialParams,
1890 color_stops: &mut BridgeColorStops,
1891 extend_mode: u8,
1892 );
fill_glyph_sweep( self: Pin<&mut ColorPainterWrapper>, glyph_id: u16, fill_transform: &Transform, fill_sweep_params: &FillSweepParams, color_stops: &mut BridgeColorStops, extend_mode: u8, )1893 fn fill_glyph_sweep(
1894 self: Pin<&mut ColorPainterWrapper>,
1895 glyph_id: u16,
1896 fill_transform: &Transform,
1897 fill_sweep_params: &FillSweepParams,
1898 color_stops: &mut BridgeColorStops,
1899 extend_mode: u8,
1900 );
1901
push_layer(self: Pin<&mut ColorPainterWrapper>, colrv1_composite_mode: u8)1902 fn push_layer(self: Pin<&mut ColorPainterWrapper>, colrv1_composite_mode: u8);
pop_layer(self: Pin<&mut ColorPainterWrapper>)1903 fn pop_layer(self: Pin<&mut ColorPainterWrapper>);
1904
1905 }
1906 }
1907
1908 /// Tests to exercise COLR and CPAL parts of the Fontations FFI.
1909 /// Run using `$ bazel test --with_fontations //src/ports/fontations:test_ffi`
1910 #[cfg(test)]
1911 mod test {
1912 use crate::{
1913 coordinates_for_shifted_named_instance_index,
1914 ffi::{BridgeFontStyle, PaletteOverride, SkiaDesignCoordinate},
1915 font_or_collection, font_ref_is_valid, get_font_style, make_font_ref, num_axes,
1916 num_named_instances, resolve_into_normalized_coords, resolve_palette,
1917 };
1918 use std::fs;
1919
1920 const TEST_FONT_FILENAME: &str = "resources/fonts/test_glyphs-glyf_colr_1_variable.ttf";
1921 const TEST_COLLECTION_FILENAME: &str = "resources/fonts/test.ttc";
1922 const TEST_CONDENSED_BOLD_ITALIC: &str = "resources/fonts/cond-bold-italic.ttf";
1923 const TEST_VARIABLE: &str = "resources/fonts/Variable.ttf";
1924
1925 #[test]
test_palette_override()1926 fn test_palette_override() {
1927 let file_buffer =
1928 fs::read(TEST_FONT_FILENAME).expect("COLRv0/v1 test font could not be opened.");
1929 let font_ref = make_font_ref(&file_buffer, 0);
1930 assert!(font_ref_is_valid(&font_ref));
1931
1932 let override_color = 0xFFEEEEEE;
1933 let valid_overrides = [
1934 PaletteOverride {
1935 index: 9,
1936 color_8888: override_color,
1937 },
1938 PaletteOverride {
1939 index: 10,
1940 color_8888: override_color,
1941 },
1942 PaletteOverride {
1943 index: 11,
1944 color_8888: override_color,
1945 },
1946 ];
1947
1948 let palette = resolve_palette(&font_ref, 0, &valid_overrides);
1949
1950 assert_eq!(palette.len(), 14);
1951 assert_eq!(palette[9], override_color);
1952 assert_eq!(palette[10], override_color);
1953 assert_eq!(palette[11], override_color);
1954
1955 let out_of_bounds_overrides = [
1956 PaletteOverride {
1957 index: 15,
1958 color_8888: override_color,
1959 },
1960 PaletteOverride {
1961 index: 16,
1962 color_8888: override_color,
1963 },
1964 PaletteOverride {
1965 index: 17,
1966 color_8888: override_color,
1967 },
1968 ];
1969
1970 let palette = resolve_palette(&font_ref, 0, &out_of_bounds_overrides);
1971
1972 assert_eq!(palette.len(), 14);
1973 assert_eq!(
1974 (palette[11], palette[12], palette[13],),
1975 (0xff68c7e8, 0xffffdc01, 0xff808080)
1976 );
1977 }
1978
1979 #[test]
test_default_palette_for_invalid_index()1980 fn test_default_palette_for_invalid_index() {
1981 let file_buffer =
1982 fs::read(TEST_FONT_FILENAME).expect("COLRv0/v1 test font could not be opened.");
1983 let font_ref = make_font_ref(&file_buffer, 0);
1984 assert!(font_ref_is_valid(&font_ref));
1985 let palette = resolve_palette(&font_ref, 65535, &[]);
1986 assert_eq!(palette.len(), 14);
1987 assert_eq!(
1988 (palette[0], palette[6], palette[13],),
1989 (0xFFFF0000, 0xFFEE82EE, 0xFF808080)
1990 );
1991 }
1992
1993 #[test]
test_num_fonts_in_collection()1994 fn test_num_fonts_in_collection() {
1995 let collection_buffer = fs::read(TEST_COLLECTION_FILENAME)
1996 .expect("Unable to open TrueType collection test file.");
1997 let font_buffer =
1998 fs::read(TEST_FONT_FILENAME).expect("COLRv0/v1 test font could not be opened.");
1999 let garbage: [u8; 12] = [
2000 b'0', b'a', b'b', b'0', b'a', b'b', b'0', b'a', b'b', b'0', b'a', b'b',
2001 ];
2002
2003 let mut num_fonts = 0;
2004 let result_collection = font_or_collection(&collection_buffer, &mut num_fonts);
2005 assert!(result_collection && num_fonts == 2);
2006
2007 let result_font_file = font_or_collection(&font_buffer, &mut num_fonts);
2008 assert!(result_font_file);
2009 assert!(num_fonts == 0u32);
2010
2011 let result_garbage = font_or_collection(&garbage, &mut num_fonts);
2012 assert!(!result_garbage);
2013 }
2014
2015 #[test]
test_font_attributes()2016 fn test_font_attributes() {
2017 let file_buffer = fs::read(TEST_CONDENSED_BOLD_ITALIC)
2018 .expect("Font to test font styles could not be opened.");
2019 let font_ref = make_font_ref(&file_buffer, 0);
2020 let coords = resolve_into_normalized_coords(&font_ref, &[]);
2021 assert!(font_ref_is_valid(&font_ref));
2022
2023 let mut font_style = BridgeFontStyle::default();
2024
2025 if get_font_style(font_ref.as_ref(), &coords, &mut font_style) {
2026 assert_eq!(font_style.width, 5); // The font should have condenced width attribute but
2027 // it's condenced itself so we have the normal width
2028 assert_eq!(font_style.slant, 1); // Skia italic
2029 assert_eq!(font_style.weight, 700); // Skia bold
2030 } else {
2031 assert!(false);
2032 }
2033 }
2034
2035 #[test]
test_variable_font_attributes()2036 fn test_variable_font_attributes() {
2037 let file_buffer =
2038 fs::read(TEST_VARIABLE).expect("Font to test font styles could not be opened.");
2039 let font_ref = make_font_ref(&file_buffer, 0);
2040 let coords = resolve_into_normalized_coords(&font_ref, &[]);
2041 assert!(font_ref_is_valid(&font_ref));
2042
2043 let mut font_style = BridgeFontStyle::default();
2044
2045 assert!(get_font_style(font_ref.as_ref(), &coords, &mut font_style));
2046 assert_eq!(font_style.width, 5); // Skia normal
2047 assert_eq!(font_style.slant, 0); // Skia upright
2048 assert_eq!(font_style.weight, 400); // Skia normal
2049 }
2050
2051 #[test]
test_no_instances()2052 fn test_no_instances() {
2053 let font_buffer = fs::read(TEST_CONDENSED_BOLD_ITALIC)
2054 .expect("Font to test font styles could not be opened.");
2055 let font_ref = make_font_ref(&font_buffer, 0);
2056 let num_instances = num_named_instances(font_ref.as_ref());
2057 assert!(num_instances == 0);
2058 }
2059
2060 #[test]
test_no_axes()2061 fn test_no_axes() {
2062 let font_buffer = fs::read(TEST_CONDENSED_BOLD_ITALIC)
2063 .expect("Font to test font styles could not be opened.");
2064 let font_ref = make_font_ref(&font_buffer, 0);
2065 let size = num_axes(&font_ref);
2066 assert_eq!(0, size);
2067 }
2068
2069 #[test]
test_named_instances()2070 fn test_named_instances() {
2071 let font_buffer =
2072 fs::read(TEST_VARIABLE).expect("Font to test font styles could not be opened.");
2073
2074 let font_ref = make_font_ref(&font_buffer, 0);
2075 let num_instances = num_named_instances(font_ref.as_ref());
2076 assert!(num_instances == 5);
2077
2078 let mut index = 0;
2079 loop {
2080 if index >= num_instances {
2081 break;
2082 }
2083 let named_instance_index: u32 = ((index + 1) << 16) as u32;
2084 let num_coords = coordinates_for_shifted_named_instance_index(
2085 &font_ref,
2086 named_instance_index,
2087 &mut [],
2088 );
2089 assert_eq!(num_coords, 2);
2090
2091 let mut received_coords: [SkiaDesignCoordinate; 2] = Default::default();
2092 let num_coords = coordinates_for_shifted_named_instance_index(
2093 &font_ref,
2094 named_instance_index,
2095 &mut received_coords,
2096 );
2097 let size = num_axes(&font_ref) as isize;
2098 assert_eq!(num_coords, size);
2099 if (index + 1) == 5 {
2100 assert_eq!(num_coords, 2);
2101 assert_eq!(
2102 received_coords[0],
2103 SkiaDesignCoordinate {
2104 axis: u32::from_be_bytes([b'w', b'g', b'h', b't']),
2105 value: 400.0
2106 }
2107 );
2108 assert_eq!(
2109 received_coords[1],
2110 SkiaDesignCoordinate {
2111 axis: u32::from_be_bytes([b'w', b'd', b't', b'h']),
2112 value: 200.0
2113 }
2114 );
2115 };
2116 index += 1;
2117 }
2118 }
2119
2120 #[test]
test_shifted_named_instance_index()2121 fn test_shifted_named_instance_index() {
2122 let file_buffer =
2123 fs::read(TEST_VARIABLE).expect("Font to test named instances could not be opened.");
2124 let font_ref = make_font_ref(&file_buffer, 0);
2125 assert!(font_ref_is_valid(&font_ref));
2126 // Named instances are 1-indexed.
2127 const SHIFTED_NAMED_INSTANCE_INDEX: u32 = 5 << 16;
2128 const OUT_OF_BOUNDS_NAMED_INSTANCE_INDEX: u32 = 6 << 16;
2129
2130 let num_coords = coordinates_for_shifted_named_instance_index(
2131 &font_ref,
2132 SHIFTED_NAMED_INSTANCE_INDEX,
2133 &mut [],
2134 );
2135 assert_eq!(num_coords, 2);
2136
2137 let mut too_small: [SkiaDesignCoordinate; 1] = Default::default();
2138 let num_coords = coordinates_for_shifted_named_instance_index(
2139 &font_ref,
2140 SHIFTED_NAMED_INSTANCE_INDEX,
2141 &mut too_small,
2142 );
2143 assert_eq!(num_coords, -1);
2144
2145 let mut received_coords: [SkiaDesignCoordinate; 2] = Default::default();
2146 let num_coords = coordinates_for_shifted_named_instance_index(
2147 &font_ref,
2148 SHIFTED_NAMED_INSTANCE_INDEX,
2149 &mut received_coords,
2150 );
2151 assert_eq!(num_coords, 2);
2152 assert_eq!(
2153 received_coords[0],
2154 SkiaDesignCoordinate {
2155 axis: u32::from_be_bytes([b'w', b'g', b'h', b't']),
2156 value: 400.0
2157 }
2158 );
2159 assert_eq!(
2160 received_coords[1],
2161 SkiaDesignCoordinate {
2162 axis: u32::from_be_bytes([b'w', b'd', b't', b'h']),
2163 value: 200.0
2164 }
2165 );
2166
2167 let mut too_large: [SkiaDesignCoordinate; 5] = Default::default();
2168 let num_coords = coordinates_for_shifted_named_instance_index(
2169 &font_ref,
2170 SHIFTED_NAMED_INSTANCE_INDEX,
2171 &mut too_large,
2172 );
2173 assert_eq!(num_coords, 2);
2174
2175 // Index out of bounds:
2176 let num_coords = coordinates_for_shifted_named_instance_index(
2177 &font_ref,
2178 OUT_OF_BOUNDS_NAMED_INSTANCE_INDEX,
2179 &mut [],
2180 );
2181 assert_eq!(num_coords, -1);
2182 }
2183 }
2184