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