1use byteorder::{BigEndian, ReadBytesExt};
14use core_graphics::base::{kCGImageAlphaPremultipliedLast, CGFloat};
15use core_graphics::color_space::CGColorSpace;
16use core_graphics::context::{CGContext, CGTextDrawingMode};
17use core_graphics::font::{CGFont, CGGlyph};
18use core_graphics::geometry::{CGAffineTransform, CGPoint, CGRect, CGSize};
19use core_graphics::geometry::{CG_AFFINE_TRANSFORM_IDENTITY, CG_ZERO_POINT, CG_ZERO_SIZE};
20use core_graphics::path::CGPathElementType;
21use core_text;
22use core_text::font::CTFont;
23use core_text::font_descriptor::kCTFontDefaultOrientation;
24use core_text::font_descriptor::{SymbolicTraitAccessors, TraitAccessors};
25use log::warn;
26use pathfinder_geometry::line_segment::LineSegment2F;
27use pathfinder_geometry::rect::{RectF, RectI};
28use pathfinder_geometry::transform2d::Transform2F;
29use pathfinder_geometry::vector::Vector2F;
30use pathfinder_simd::default::F32x4;
31use std::cmp::Ordering;
32use std::f32;
33use std::fmt::{self, Debug, Formatter};
34use std::fs::File;
35use std::io::{Seek, SeekFrom};
36use std::ops::Deref;
37use std::path::Path;
38use std::sync::Arc;
39
40use crate::canvas::{Canvas, Format, RasterizationOptions};
41use crate::error::{FontLoadingError, GlyphLoadingError};
42use crate::file_type::FileType;
43use crate::handle::Handle;
44use crate::hinting::HintingOptions;
45use crate::loader::{FallbackResult, Loader};
46use crate::metrics::Metrics;
47use crate::outline::OutlineSink;
48use crate::properties::{Properties, Stretch, Style, Weight};
49use crate::utils;
50
51const TTC_TAG: [u8; 4] = [b't', b't', b'c', b'f'];
52const OTTO_TAG: [u8; 4] = [b'O', b'T', b'T', b'O'];
53const OTTO_HEX: u32 = 0x4f54544f; const TRUE_HEX: u32 = 0x74727565; const TYP1_HEX: u32 = 0x74797031; const SFNT_HEX: u32 = 0x73666e74; #[allow(non_upper_case_globals)]
59const kCGImageAlphaOnly: u32 = 7;
60
61pub(crate) static FONT_WEIGHT_MAPPING: [f32; 9] = [-0.7, -0.5, -0.23, 0.0, 0.2, 0.3, 0.4, 0.6, 0.8];
62
63pub type NativeFont = CTFont;
65
66#[derive(Clone)]
68pub struct Font {
69 core_text_font: CTFont,
70 font_data: FontData,
71}
72
73impl Font {
74 pub fn from_bytes(
79 mut font_data: Arc<Vec<u8>>,
80 font_index: u32,
81 ) -> Result<Font, FontLoadingError> {
82 if !font_is_single_otf(&*font_data) && !font_is_collection(&*font_data) {
85 let mut new_font_data = (*font_data).clone();
86 unpack_data_fork_font(&mut new_font_data)?;
87 font_data = Arc::new(new_font_data);
88 } else if font_is_collection(&*font_data) {
89 let mut new_font_data = (*font_data).clone();
90 unpack_otc_font(&mut new_font_data, font_index)?;
91 font_data = Arc::new(new_font_data);
92 }
93
94 let core_text_font = match core_text::font::new_from_buffer(&*font_data) {
95 Ok(ct_font) => ct_font,
96 Err(_) => return Err(FontLoadingError::Parse),
97 };
98
99 Ok(Font {
100 core_text_font,
101 font_data: FontData::Memory(font_data),
102 })
103 }
104
105 pub fn from_file(file: &mut File, font_index: u32) -> Result<Font, FontLoadingError> {
110 file.seek(SeekFrom::Start(0))?;
111 let font_data = Arc::new(utils::slurp_file(file).map_err(FontLoadingError::Io)?);
112 Font::from_bytes(font_data, font_index)
113 }
114
115 #[inline]
120 pub fn from_path<P: AsRef<Path>>(path: P, font_index: u32) -> Result<Font, FontLoadingError> {
121 <Font as Loader>::from_path(path, font_index)
122 }
123
124 pub unsafe fn from_native_font(core_text_font: NativeFont) -> Font {
126 Font::from_core_text_font(core_text_font)
127 }
128
129 unsafe fn from_core_text_font(core_text_font: NativeFont) -> Font {
130 let mut font_data = FontData::Unavailable;
131 match core_text_font.url() {
132 None => warn!("No URL found for Core Text font!"),
133 Some(url) => match url.to_path() {
134 Some(path) => match File::open(path) {
135 Ok(ref mut file) => match utils::slurp_file(file) {
136 Ok(data) => font_data = FontData::Memory(Arc::new(data)),
137 Err(_) => warn!("Couldn't read file data for Core Text font!"),
138 },
139 Err(_) => warn!("Could not open file for Core Text font!"),
140 },
141 None => warn!("Could not convert URL from Core Text font to path!"),
142 },
143 }
144
145 Font {
146 core_text_font,
147 font_data,
148 }
149 }
150
151 pub fn from_core_graphics_font(core_graphics_font: CGFont) -> Font {
155 unsafe {
156 Font::from_core_text_font(core_text::font::new_from_CGFont(&core_graphics_font, 16.0))
157 }
158 }
159
160 #[inline]
162 pub fn from_handle(handle: &Handle) -> Result<Self, FontLoadingError> {
163 <Self as Loader>::from_handle(handle)
164 }
165
166 pub fn analyze_bytes(font_data: Arc<Vec<u8>>) -> Result<FileType, FontLoadingError> {
168 if let Ok(font_count) = read_number_of_fonts_from_otc_header(&font_data) {
169 return Ok(FileType::Collection(font_count));
170 }
171 match core_text::font::new_from_buffer(&*font_data) {
172 Ok(_) => Ok(FileType::Single),
173 Err(_) => Err(FontLoadingError::Parse),
174 }
175 }
176
177 pub fn analyze_file(file: &mut File) -> Result<FileType, FontLoadingError> {
179 file.seek(SeekFrom::Start(0))?;
180
181 let font_data = Arc::new(utils::slurp_file(file).map_err(FontLoadingError::Io)?);
182 if let Ok(font_count) = read_number_of_fonts_from_otc_header(&font_data) {
183 return Ok(FileType::Collection(font_count));
184 }
185
186 match core_text::font::new_from_buffer(&*font_data) {
187 Ok(_) => Ok(FileType::Single),
188 Err(_) => Err(FontLoadingError::Parse),
189 }
190 }
191
192 #[inline]
194 pub fn analyze_path<P: AsRef<Path>>(path: P) -> Result<FileType, FontLoadingError> {
195 <Self as Loader>::analyze_path(path)
196 }
197
198 #[inline]
200 pub fn native_font(&self) -> NativeFont {
201 self.core_text_font.clone()
202 }
203
204 #[inline]
206 pub fn postscript_name(&self) -> Option<String> {
207 Some(self.core_text_font.postscript_name())
208 }
209
210 #[inline]
212 pub fn full_name(&self) -> String {
213 self.core_text_font.display_name()
214 }
215
216 #[inline]
218 pub fn family_name(&self) -> String {
219 self.core_text_font.family_name()
220 }
221
222 #[inline]
226 pub fn style_name(&self) -> String {
227 self.core_text_font.style_name()
228 }
229
230 #[inline]
232 pub fn is_monospace(&self) -> bool {
233 self.core_text_font.symbolic_traits().is_monospace()
234 }
235
236 pub fn properties(&self) -> Properties {
238 let symbolic_traits = self.core_text_font.symbolic_traits();
239 let all_traits = self.core_text_font.all_traits();
240
241 let style = if symbolic_traits.is_italic() {
242 Style::Italic
243 } else if all_traits.normalized_slant() > 0.0 {
244 Style::Oblique
245 } else {
246 Style::Normal
247 };
248
249 let weight = core_text_to_css_font_weight(all_traits.normalized_weight() as f32);
250 let stretch = core_text_width_to_css_stretchiness(all_traits.normalized_width() as f32);
251
252 Properties {
253 style,
254 weight,
255 stretch,
256 }
257 }
258
259 pub fn glyph_count(&self) -> u32 {
263 self.core_text_font.glyph_count() as u32
264 }
265
266 pub fn glyph_for_char(&self, character: char) -> Option<u32> {
272 unsafe {
273 let (mut dest, mut src) = ([0, 0], [0, 0]);
274 let src = character.encode_utf16(&mut src);
275 self.core_text_font
276 .get_glyphs_for_characters(src.as_ptr(), dest.as_mut_ptr(), 2);
277
278 let id = dest[0] as u32;
279 if id != 0 {
280 Some(id)
281 } else {
282 None
283 }
284 }
285 }
286
287 #[inline]
289 pub fn glyph_by_name(&self, name: &str) -> Option<u32> {
290 let code = self.core_text_font.get_glyph_with_name(name);
291
292 Some(u32::from(code))
293 }
294
295 pub fn outline<S>(
302 &self,
303 glyph_id: u32,
304 _: HintingOptions,
305 sink: &mut S,
306 ) -> Result<(), GlyphLoadingError>
307 where
308 S: OutlineSink,
309 {
310 let path = match self
311 .core_text_font
312 .create_path_for_glyph(glyph_id as u16, &CG_AFFINE_TRANSFORM_IDENTITY)
313 {
314 Ok(path) => path,
315 Err(_) => {
316 drop(self.typographic_bounds(glyph_id)?);
319 return Ok(());
320 }
321 };
322
323 let units_per_point = self.units_per_point() as f32;
324 path.apply(&|element| {
325 let points = element.points();
326 match element.element_type {
327 CGPathElementType::MoveToPoint => {
328 sink.move_to(points[0].to_vector() * units_per_point)
329 }
330 CGPathElementType::AddLineToPoint => {
331 sink.line_to(points[0].to_vector() * units_per_point)
332 }
333 CGPathElementType::AddQuadCurveToPoint => sink.quadratic_curve_to(
334 points[0].to_vector() * units_per_point,
335 points[1].to_vector() * units_per_point,
336 ),
337 CGPathElementType::AddCurveToPoint => {
338 let ctrl = LineSegment2F::new(points[0].to_vector(), points[1].to_vector())
339 * units_per_point;
340 sink.cubic_curve_to(ctrl, points[2].to_vector() * units_per_point)
341 }
342 CGPathElementType::CloseSubpath => sink.close(),
343 }
344 });
345 Ok(())
346 }
347
348 pub fn typographic_bounds(&self, glyph_id: u32) -> Result<RectF, GlyphLoadingError> {
350 let rect = self
351 .core_text_font
352 .get_bounding_rects_for_glyphs(kCTFontDefaultOrientation, &[glyph_id as u16]);
353 let rect = RectF::new(
354 Vector2F::new(rect.origin.x as f32, rect.origin.y as f32),
355 Vector2F::new(rect.size.width as f32, rect.size.height as f32),
356 );
357 Ok(rect * self.units_per_point() as f32)
358 }
359
360 pub fn advance(&self, glyph_id: u32) -> Result<Vector2F, GlyphLoadingError> {
363 unsafe {
365 let (glyph_id, mut advance) = (glyph_id as u16, CG_ZERO_SIZE);
366 self.core_text_font.get_advances_for_glyphs(
367 kCTFontDefaultOrientation,
368 &glyph_id,
369 &mut advance,
370 1,
371 );
372 let advance = Vector2F::new(advance.width as f32, advance.height as f32);
373 Ok(advance * self.units_per_point() as f32)
374 }
375 }
376
377 pub fn origin(&self, glyph_id: u32) -> Result<Vector2F, GlyphLoadingError> {
379 unsafe {
380 let (glyph_id, mut translation) = (glyph_id as u16, CG_ZERO_SIZE);
382 self.core_text_font.get_vertical_translations_for_glyphs(
383 kCTFontDefaultOrientation,
384 &glyph_id,
385 &mut translation,
386 1,
387 );
388 let translation = Vector2F::new(translation.width as f32, translation.height as f32);
389 Ok(translation * self.units_per_point() as f32)
390 }
391 }
392
393 pub fn metrics(&self) -> Metrics {
395 let units_per_em = self.core_text_font.units_per_em();
396 let units_per_point = (units_per_em as f64) / self.core_text_font.pt_size();
397
398 let bounding_box = self.core_text_font.bounding_box();
399 let bounding_box = RectF::new(
400 Vector2F::new(bounding_box.origin.x as f32, bounding_box.origin.y as f32),
401 Vector2F::new(
402 bounding_box.size.width as f32,
403 bounding_box.size.height as f32,
404 ),
405 );
406 let bounding_box = bounding_box * units_per_point as f32;
407
408 Metrics {
409 units_per_em,
410 ascent: (self.core_text_font.ascent() * units_per_point) as f32,
411 descent: (-self.core_text_font.descent() * units_per_point) as f32,
412 line_gap: (self.core_text_font.leading() * units_per_point) as f32,
413 underline_position: (self.core_text_font.underline_position() * units_per_point) as f32,
414 underline_thickness: (self.core_text_font.underline_thickness() * units_per_point)
415 as f32,
416 cap_height: (self.core_text_font.cap_height() * units_per_point) as f32,
417 x_height: (self.core_text_font.x_height() * units_per_point) as f32,
418 bounding_box,
419 }
420 }
421
422 #[inline]
426 pub fn handle(&self) -> Option<Handle> {
427 <Self as Loader>::handle(self)
428 }
429
430 pub fn copy_font_data(&self) -> Option<Arc<Vec<u8>>> {
435 match self.font_data {
436 FontData::Unavailable => None,
437 FontData::Memory(ref memory) => Some((*memory).clone()),
438 }
439 }
440
441 #[inline]
444 pub fn raster_bounds(
445 &self,
446 glyph_id: u32,
447 point_size: f32,
448 transform: Transform2F,
449 hinting_options: HintingOptions,
450 rasterization_options: RasterizationOptions,
451 ) -> Result<RectI, GlyphLoadingError> {
452 <Self as Loader>::raster_bounds(
453 self,
454 glyph_id,
455 point_size,
456 transform,
457 hinting_options,
458 rasterization_options,
459 )
460 }
461
462 pub fn rasterize_glyph(
475 &self,
476 canvas: &mut Canvas,
477 glyph_id: u32,
478 point_size: f32,
479 transform: Transform2F,
480 hinting_options: HintingOptions,
481 rasterization_options: RasterizationOptions,
482 ) -> Result<(), GlyphLoadingError> {
483 if canvas.size.x() == 0 || canvas.size.y() == 0 {
484 return Ok(());
485 }
486
487 let (cg_color_space, cg_image_format) =
488 match format_to_cg_color_space_and_image_format(canvas.format) {
489 None => {
490 let mut temp_canvas = Canvas::new(canvas.size, Format::Rgba32);
496 self.rasterize_glyph(
497 &mut temp_canvas,
498 glyph_id,
499 point_size,
500 transform,
501 hinting_options,
502 rasterization_options,
503 )?;
504 canvas.blit_from_canvas(&temp_canvas);
505 return Ok(());
506 }
507 Some(cg_color_space_and_format) => cg_color_space_and_format,
508 };
509
510 let core_graphics_context = CGContext::create_bitmap_context(
511 Some(canvas.pixels.as_mut_ptr() as *mut _),
512 canvas.size.x() as usize,
513 canvas.size.y() as usize,
514 canvas.format.bits_per_component() as usize,
515 canvas.stride,
516 &cg_color_space,
517 cg_image_format,
518 );
519
520 match canvas.format {
521 Format::Rgba32 | Format::Rgb24 => {
522 core_graphics_context.set_rgb_fill_color(0.0, 0.0, 0.0, 0.0);
523 }
524 Format::A8 => core_graphics_context.set_gray_fill_color(0.0, 0.0),
525 }
526
527 let core_graphics_size = CGSize::new(canvas.size.x() as f64, canvas.size.y() as f64);
528 core_graphics_context.fill_rect(CGRect::new(&CG_ZERO_POINT, &core_graphics_size));
529
530 match rasterization_options {
531 RasterizationOptions::Bilevel => {
532 core_graphics_context.set_allows_font_smoothing(false);
533 core_graphics_context.set_should_smooth_fonts(false);
534 core_graphics_context.set_should_antialias(false);
535 }
536 RasterizationOptions::GrayscaleAa | RasterizationOptions::SubpixelAa => {
537 core_graphics_context.set_allows_font_smoothing(true);
539 core_graphics_context.set_should_smooth_fonts(true);
540 core_graphics_context.set_should_antialias(true);
541 }
542 }
543
544 match canvas.format {
545 Format::Rgba32 | Format::Rgb24 => {
546 core_graphics_context.set_rgb_fill_color(1.0, 1.0, 1.0, 1.0);
547 }
548 Format::A8 => core_graphics_context.set_gray_fill_color(1.0, 1.0),
549 }
550
551 core_graphics_context.translate(0.0, canvas.size.y() as CGFloat);
553 core_graphics_context.set_font(&self.core_text_font.copy_to_CGFont());
554 core_graphics_context.set_font_size(point_size as CGFloat);
555 core_graphics_context.set_text_drawing_mode(CGTextDrawingMode::CGTextFill);
556 let matrix = transform.matrix.0 * F32x4::new(1.0, -1.0, -1.0, 1.0);
557 core_graphics_context.set_text_matrix(&CGAffineTransform {
558 a: matrix.x() as CGFloat,
559 b: matrix.y() as CGFloat,
560 c: matrix.z() as CGFloat,
561 d: matrix.w() as CGFloat,
562 tx: transform.vector.x() as CGFloat,
563 ty: -transform.vector.y() as CGFloat,
564 });
565 let origin = CGPoint::new(0.0, 0.0);
566 core_graphics_context.show_glyphs_at_positions(&[glyph_id as CGGlyph], &[origin]);
567
568 Ok(())
569 }
570
571 #[inline]
578 pub fn supports_hinting_options(&self, hinting_options: HintingOptions, _: bool) -> bool {
579 match hinting_options {
580 HintingOptions::None => true,
581 HintingOptions::Vertical(..)
582 | HintingOptions::VerticalSubpixel(..)
583 | HintingOptions::Full(..) => false,
584 }
585 }
586
587 fn get_fallbacks(&self, text: &str, _locale: &str) -> FallbackResult<Font> {
592 warn!("unsupported");
593 FallbackResult {
594 fonts: Vec::new(),
595 valid_len: text.len(),
596 }
597 }
598
599 #[inline]
600 fn units_per_point(&self) -> f64 {
601 (self.core_text_font.units_per_em() as f64) / self.core_text_font.pt_size()
602 }
603
604 #[inline]
610 pub fn load_font_table(&self, table_tag: u32) -> Option<Box<[u8]>> {
611 self.core_text_font
612 .get_font_table(table_tag)
613 .map(|data| data.bytes().into())
614 }
615}
616
617impl Loader for Font {
618 type NativeFont = NativeFont;
619
620 #[inline]
621 fn from_bytes(font_data: Arc<Vec<u8>>, font_index: u32) -> Result<Self, FontLoadingError> {
622 Font::from_bytes(font_data, font_index)
623 }
624
625 #[inline]
626 fn from_file(file: &mut File, font_index: u32) -> Result<Font, FontLoadingError> {
627 Font::from_file(file, font_index)
628 }
629
630 #[inline]
631 unsafe fn from_native_font(native_font: Self::NativeFont) -> Self {
632 Font::from_native_font(native_font)
633 }
634
635 #[inline]
636 fn analyze_bytes(font_data: Arc<Vec<u8>>) -> Result<FileType, FontLoadingError> {
637 Font::analyze_bytes(font_data)
638 }
639
640 #[inline]
641 fn analyze_file(file: &mut File) -> Result<FileType, FontLoadingError> {
642 Font::analyze_file(file)
643 }
644
645 #[inline]
646 fn native_font(&self) -> Self::NativeFont {
647 self.native_font()
648 }
649
650 #[inline]
651 fn postscript_name(&self) -> Option<String> {
652 self.postscript_name()
653 }
654
655 #[inline]
656 fn full_name(&self) -> String {
657 self.full_name()
658 }
659
660 #[inline]
661 fn family_name(&self) -> String {
662 self.family_name()
663 }
664
665 #[inline]
666 fn is_monospace(&self) -> bool {
667 self.is_monospace()
668 }
669
670 #[inline]
671 fn properties(&self) -> Properties {
672 self.properties()
673 }
674
675 #[inline]
676 fn glyph_for_char(&self, character: char) -> Option<u32> {
677 self.glyph_for_char(character)
678 }
679
680 #[inline]
681 fn glyph_by_name(&self, name: &str) -> Option<u32> {
682 self.glyph_by_name(name)
683 }
684
685 #[inline]
686 fn glyph_count(&self) -> u32 {
687 self.glyph_count()
688 }
689
690 #[inline]
691 fn outline<S>(
692 &self,
693 glyph_id: u32,
694 hinting_mode: HintingOptions,
695 sink: &mut S,
696 ) -> Result<(), GlyphLoadingError>
697 where
698 S: OutlineSink,
699 {
700 self.outline(glyph_id, hinting_mode, sink)
701 }
702
703 #[inline]
704 fn typographic_bounds(&self, glyph_id: u32) -> Result<RectF, GlyphLoadingError> {
705 self.typographic_bounds(glyph_id)
706 }
707
708 #[inline]
709 fn advance(&self, glyph_id: u32) -> Result<Vector2F, GlyphLoadingError> {
710 self.advance(glyph_id)
711 }
712
713 #[inline]
714 fn origin(&self, glyph_id: u32) -> Result<Vector2F, GlyphLoadingError> {
715 self.origin(glyph_id)
716 }
717
718 #[inline]
719 fn metrics(&self) -> Metrics {
720 self.metrics()
721 }
722
723 #[inline]
724 fn copy_font_data(&self) -> Option<Arc<Vec<u8>>> {
725 self.copy_font_data()
726 }
727
728 #[inline]
729 fn supports_hinting_options(
730 &self,
731 hinting_options: HintingOptions,
732 for_rasterization: bool,
733 ) -> bool {
734 self.supports_hinting_options(hinting_options, for_rasterization)
735 }
736
737 #[inline]
738 fn rasterize_glyph(
739 &self,
740 canvas: &mut Canvas,
741 glyph_id: u32,
742 point_size: f32,
743 transform: Transform2F,
744 hinting_options: HintingOptions,
745 rasterization_options: RasterizationOptions,
746 ) -> Result<(), GlyphLoadingError> {
747 self.rasterize_glyph(
748 canvas,
749 glyph_id,
750 point_size,
751 transform,
752 hinting_options,
753 rasterization_options,
754 )
755 }
756
757 #[inline]
758 fn get_fallbacks(&self, text: &str, locale: &str) -> FallbackResult<Self> {
759 self.get_fallbacks(text, locale)
760 }
761
762 #[inline]
763 fn load_font_table(&self, table_tag: u32) -> Option<Box<[u8]>> {
764 self.load_font_table(table_tag)
765 }
766}
767
768impl Debug for Font {
769 fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
770 self.full_name().fmt(fmt)
771 }
772}
773
774#[derive(Clone)]
775enum FontData {
776 Unavailable,
777 Memory(Arc<Vec<u8>>),
778}
779
780impl Deref for FontData {
781 type Target = [u8];
782 fn deref(&self) -> &[u8] {
783 match *self {
784 FontData::Unavailable => panic!("Font data unavailable!"),
785 FontData::Memory(ref data) => &***data,
786 }
787 }
788}
789
790trait CGPointExt {
791 fn to_vector(&self) -> Vector2F;
792}
793
794impl CGPointExt for CGPoint {
795 #[inline]
796 fn to_vector(&self) -> Vector2F {
797 Vector2F::new(self.x as f32, self.y as f32)
798 }
799}
800
801fn core_text_to_css_font_weight(core_text_weight: f32) -> Weight {
802 let index = piecewise_linear_find_index(core_text_weight, &FONT_WEIGHT_MAPPING);
803
804 Weight(index * 100.0 + 100.0)
805}
806
807fn core_text_width_to_css_stretchiness(core_text_width: f32) -> Stretch {
808 Stretch(piecewise_linear_lookup(
809 (core_text_width + 1.0) * 4.0,
810 &Stretch::MAPPING,
811 ))
812}
813
814fn font_is_collection(header: &[u8]) -> bool {
815 header.len() >= 4 && header[0..4] == TTC_TAG
816}
817
818fn read_number_of_fonts_from_otc_header(header: &[u8]) -> Result<u32, FontLoadingError> {
819 if !font_is_collection(header) {
820 return Err(FontLoadingError::UnknownFormat);
821 }
822 Ok((&header[8..]).read_u32::<BigEndian>()?)
823}
824
825fn get_slice_from_start(slice: &[u8], start: usize) -> Result<&[u8], FontLoadingError> {
826 slice.get(start..).ok_or(FontLoadingError::Parse)
827}
828
829fn unpack_otc_font(data: &mut [u8], font_index: u32) -> Result<(), FontLoadingError> {
831 if font_index >= read_number_of_fonts_from_otc_header(data)? {
832 return Err(FontLoadingError::NoSuchFontInCollection);
833 }
834
835 let offset_table_pos_pos = 12 + 4 * font_index as usize;
836
837 let offset_table_pos =
838 get_slice_from_start(&data, offset_table_pos_pos)?.read_u32::<BigEndian>()? as usize;
839 debug_assert!(utils::SFNT_VERSIONS
840 .iter()
841 .any(|version| { data[offset_table_pos..(offset_table_pos + 4)] == *version }));
842 let num_tables = get_slice_from_start(&data, offset_table_pos + 4)?.read_u16::<BigEndian>()?;
843
844 let offset_table_and_table_record_size = 12 + (num_tables as usize) * 16;
846 for offset in 0..offset_table_and_table_record_size {
847 data[offset] = data[offset_table_pos + offset]
848 }
849
850 Ok(())
851}
852
853fn format_to_cg_color_space_and_image_format(format: Format) -> Option<(CGColorSpace, u32)> {
855 match format {
856 Format::Rgb24 => {
857 None
859 }
860 Format::Rgba32 => Some((
861 CGColorSpace::create_device_rgb(),
862 kCGImageAlphaPremultipliedLast,
863 )),
864 Format::A8 => Some((CGColorSpace::create_device_gray(), kCGImageAlphaOnly)),
865 }
866}
867
868fn font_is_single_otf(header: &[u8]) -> bool {
869 header.len() >= 4
870 && ((&header[..4]).read_u32::<BigEndian>().unwrap() == 0x00010000
871 || header[..4] == OTTO_TAG)
872}
873
874fn unpack_data_fork_font(data: &mut [u8]) -> Result<(), FontLoadingError> {
876 let data_offset = (&data[..]).read_u32::<BigEndian>()? as usize;
877 let map_offset = get_slice_from_start(&data, 4)?.read_u32::<BigEndian>()? as usize;
878 let num_types =
879 get_slice_from_start(&data, map_offset + 28)?.read_u16::<BigEndian>()? as usize + 1;
880
881 let mut font_data_offset = 0;
882 let mut font_data_len = 0;
883
884 let type_list_offset = get_slice_from_start(&data, map_offset + 24)?.read_u16::<BigEndian>()?
885 as usize
886 + map_offset;
887 for i in 0..num_types {
888 let res_type =
889 get_slice_from_start(&data, map_offset + 30 + i * 8)?.read_u32::<BigEndian>()?;
890
891 if res_type == SFNT_HEX {
892 let ref_list_offset = get_slice_from_start(&data, map_offset + 30 + i * 8 + 6)?
893 .read_u16::<BigEndian>()? as usize;
894 let res_data_offset =
895 get_slice_from_start(&data, type_list_offset + ref_list_offset + 5)?
896 .read_u24::<BigEndian>()? as usize;
897 font_data_len = get_slice_from_start(&data, data_offset + res_data_offset)?
898 .read_u32::<BigEndian>()? as usize;
899 font_data_offset = data_offset + res_data_offset + 4;
900 let sfnt_version =
901 get_slice_from_start(&data, font_data_offset)?.read_u32::<BigEndian>()?;
902
903 if sfnt_version == 0x00010000
905 || sfnt_version == OTTO_HEX
906 || sfnt_version == TRUE_HEX
907 || sfnt_version == TYP1_HEX
908 {
909 break;
910 }
911 }
912 }
913
914 if font_data_len == 0 {
915 return Err(FontLoadingError::Parse);
916 }
917
918 for offset in 0..font_data_len {
919 data[offset] = data[font_data_offset + offset];
920 }
921
922 Ok(())
923}
924
925#[cfg(test)]
926mod test {
927 use super::Font;
928 use crate::properties::{Stretch, Weight};
929
930 #[cfg(feature = "source")]
931 use crate::source::SystemSource;
932
933 static TEST_FONT_POSTSCRIPT_NAME: &'static str = "ArialMT";
934
935 #[cfg(feature = "source")]
936 #[test]
937 fn test_from_core_graphics_font() {
938 let font0 = SystemSource::new()
939 .select_by_postscript_name(TEST_FONT_POSTSCRIPT_NAME)
940 .unwrap()
941 .load()
942 .unwrap();
943 let core_text_font = font0.native_font();
944 let core_graphics_font = core_text_font.copy_to_CGFont();
945 let font1 = Font::from_core_graphics_font(core_graphics_font);
946 assert_eq!(font1.postscript_name().unwrap(), TEST_FONT_POSTSCRIPT_NAME);
947 }
948
949 #[test]
950 fn test_core_text_to_css_font_weight() {
951 assert_eq!(super::core_text_to_css_font_weight(-0.7), Weight(100.0));
953 assert_eq!(super::core_text_to_css_font_weight(0.0), Weight(400.0));
954 assert_eq!(super::core_text_to_css_font_weight(0.4), Weight(700.0));
955 assert_eq!(super::core_text_to_css_font_weight(0.8), Weight(900.0));
956
957 assert_eq!(super::core_text_to_css_font_weight(0.1), Weight(450.0));
959 }
960
961 #[test]
962 fn test_core_text_to_css_font_stretch() {
963 assert_eq!(
965 super::core_text_width_to_css_stretchiness(0.0),
966 Stretch(1.0)
967 );
968 assert_eq!(
969 super::core_text_width_to_css_stretchiness(-1.0),
970 Stretch(0.5)
971 );
972 assert_eq!(
973 super::core_text_width_to_css_stretchiness(1.0),
974 Stretch(2.0)
975 );
976
977 assert_eq!(
979 super::core_text_width_to_css_stretchiness(0.85),
980 Stretch(1.7)
981 );
982 }
983}
984
985pub(crate) fn piecewise_linear_lookup(index: f32, mapping: &[f32]) -> f32 {
986 let lower_value = mapping[f32::floor(index) as usize];
987 let upper_value = mapping[f32::ceil(index) as usize];
988 utils::lerp(lower_value, upper_value, f32::fract(index))
989}
990
991pub(crate) fn piecewise_linear_find_index(query_value: f32, mapping: &[f32]) -> f32 {
992 let upper_index = match mapping
993 .binary_search_by(|value| value.partial_cmp(&query_value).unwrap_or(Ordering::Less))
994 {
995 Ok(index) => return index as f32,
996 Err(upper_index) => upper_index,
997 };
998 if upper_index == 0 || upper_index >= mapping.len() {
999 return upper_index as f32;
1000 }
1001 let lower_index = upper_index - 1;
1002 let (upper_value, lower_value) = (mapping[upper_index], mapping[lower_index]);
1003 let t = (query_value - lower_value) / (upper_value - lower_value);
1004 lower_index as f32 + t
1005}