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