font_kit/loaders/
freetype.rs

1// font-kit/src/loaders/freetype.rs
2//
3// Copyright © 2018 The Pathfinder Project Developers.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
11//! A cross-platform loader that uses the FreeType library to load and rasterize fonts.
12//!
13//! On macOS and Windows, the Cargo feature `loader-freetype-default` can be used to opt into this
14//! loader by default.
15
16use byteorder::{BigEndian, ReadBytesExt};
17use freetype_sys::{
18    ft_sfnt_os2, FT_Byte, FT_Done_Face, FT_Done_FreeType, FT_Error, FT_Face, FT_Fixed,
19    FT_Get_Char_Index, FT_Get_Name_Index, FT_Get_Postscript_Name, FT_Get_Sfnt_Name,
20    FT_Get_Sfnt_Name_Count, FT_Get_Sfnt_Table, FT_Init_FreeType, FT_Library,
21    FT_Library_SetLcdFilter, FT_Load_Glyph, FT_Long, FT_Matrix, FT_New_Memory_Face, FT_Pos,
22    FT_Reference_Face, FT_Set_Char_Size, FT_Set_Transform, FT_UInt, FT_ULong, FT_Vector,
23    FT_FACE_FLAG_FIXED_WIDTH, FT_LCD_FILTER_DEFAULT, FT_LOAD_DEFAULT, FT_LOAD_MONOCHROME,
24    FT_LOAD_NO_HINTING, FT_LOAD_RENDER, FT_LOAD_TARGET_LCD, FT_LOAD_TARGET_LIGHT,
25    FT_LOAD_TARGET_MONO, FT_LOAD_TARGET_NORMAL, FT_PIXEL_MODE_GRAY, FT_PIXEL_MODE_LCD,
26    FT_PIXEL_MODE_LCD_V, FT_PIXEL_MODE_MONO, FT_STYLE_FLAG_ITALIC, TT_OS2,
27};
28use log::warn;
29use pathfinder_geometry::line_segment::LineSegment2F;
30use pathfinder_geometry::rect::{RectF, RectI};
31use pathfinder_geometry::transform2d::Transform2F;
32use pathfinder_geometry::vector::{Vector2F, Vector2I};
33use pathfinder_simd::default::F32x4;
34use std::f32;
35use std::ffi::{CStr, CString};
36use std::fmt::{self, Debug, Formatter};
37use std::io::{Seek, SeekFrom};
38use std::iter;
39use std::mem;
40use std::os::raw::{c_char, c_void};
41use std::ptr;
42use std::slice;
43use std::sync::Arc;
44
45use crate::canvas::{Canvas, Format, RasterizationOptions};
46use crate::error::{FontLoadingError, GlyphLoadingError};
47use crate::file_type::FileType;
48use crate::handle::Handle;
49use crate::hinting::HintingOptions;
50use crate::loader::{FallbackResult, Loader};
51use crate::metrics::Metrics;
52use crate::outline::OutlineSink;
53use crate::properties::{Properties, Stretch, Style, Weight};
54use crate::utils;
55
56#[cfg(not(target_arch = "wasm32"))]
57use std::fs::File;
58#[cfg(not(target_arch = "wasm32"))]
59use std::path::Path;
60
61const PS_DICT_FULL_NAME: u32 = 38;
62const TT_NAME_ID_FULL_NAME: u16 = 4;
63
64const TT_PLATFORM_APPLE_UNICODE: u16 = 0;
65
66const FT_POINT_TAG_ON_CURVE: c_char = 0x01;
67const FT_POINT_TAG_CUBIC_CONTROL: c_char = 0x02;
68
69const OS2_FS_SELECTION_OBLIQUE: u16 = 1 << 9;
70
71// Not in our FreeType bindings, so we define these ourselves.
72#[allow(dead_code)]
73const BDF_PROPERTY_TYPE_NONE: BDF_PropertyType = 0;
74#[allow(dead_code)]
75const BDF_PROPERTY_TYPE_ATOM: BDF_PropertyType = 1;
76#[allow(dead_code)]
77const BDF_PROPERTY_TYPE_INTEGER: BDF_PropertyType = 2;
78#[allow(dead_code)]
79const BDF_PROPERTY_TYPE_CARDINAL: BDF_PropertyType = 3;
80
81thread_local! {
82    static FREETYPE_LIBRARY: FtLibrary = {
83        unsafe {
84            let mut library = ptr::null_mut();
85            assert_eq!(FT_Init_FreeType(&mut library), 0);
86            FT_Library_SetLcdFilter(library, FT_LCD_FILTER_DEFAULT);
87            FtLibrary(library)
88        }
89    };
90}
91
92#[repr(transparent)]
93struct FtLibrary(FT_Library);
94
95impl Drop for FtLibrary {
96    fn drop(&mut self) {
97        unsafe {
98            let mut library = ptr::null_mut();
99            mem::swap(&mut library, &mut self.0);
100            FT_Done_FreeType(library);
101        }
102    }
103}
104
105/// The handle that the FreeType API natively uses to represent a font.
106pub type NativeFont = FT_Face;
107
108// Not in our FreeType bindings, so we define this ourselves.
109#[allow(non_camel_case_types)]
110type BDF_PropertyType = i32;
111
112// Not in our FreeType bindings, so we define this ourselves.
113#[repr(C)]
114struct BDF_PropertyRec {
115    property_type: BDF_PropertyType,
116    value: *const c_char,
117}
118
119/// A cross-platform loader that uses the FreeType library to load and rasterize fonts.
120///
121///
122/// On macOS and Windows, the Cargo feature `loader-freetype-default` can be used to opt into this
123/// loader by default.
124pub struct Font {
125    freetype_face: FT_Face,
126    font_data: Arc<Vec<u8>>,
127}
128
129impl Font {
130    /// Loads a font from raw font data (the contents of a `.ttf`/`.otf`/etc. file).
131    ///
132    /// If the data represents a collection (`.ttc`/`.otc`/etc.), `font_index` specifies the index
133    /// of the font to load from it. If the data represents a single font, pass 0 for `font_index`.
134    pub fn from_bytes(font_data: Arc<Vec<u8>>, font_index: u32) -> Result<Font, FontLoadingError> {
135        FREETYPE_LIBRARY.with(|freetype_library| unsafe {
136            let mut freetype_face = ptr::null_mut();
137            if FT_New_Memory_Face(
138                freetype_library.0,
139                (*font_data).as_ptr(),
140                font_data.len() as FT_Long,
141                font_index as FT_Long,
142                &mut freetype_face,
143            ) != 0
144            {
145                return Err(FontLoadingError::Parse);
146            }
147
148            setup_freetype_face(freetype_face);
149
150            Ok(Font {
151                freetype_face,
152                font_data,
153            })
154        })
155    }
156
157    /// Loads a font from a `.ttf`/`.otf`/etc. file.
158    ///
159    /// If the file is a collection (`.ttc`/`.otc`/etc.), `font_index` specifies the index of the
160    /// font to load from it. If the file represents a single font, pass 0 for `font_index`.
161    #[cfg(not(target_arch = "wasm32"))]
162    pub fn from_file(file: &mut File, font_index: u32) -> Result<Font, FontLoadingError> {
163        file.seek(SeekFrom::Start(0))?;
164        let font_data = Arc::new(utils::slurp_file(file).map_err(FontLoadingError::Io)?);
165        Font::from_bytes(font_data, font_index)
166    }
167
168    /// Loads a font from the path to a `.ttf`/`.otf`/etc. file.
169    ///
170    /// If the file is a collection (`.ttc`/`.otc`/etc.), `font_index` specifies the index of the
171    /// font to load from it. If the file represents a single font, pass 0 for `font_index`.
172    #[inline]
173    #[cfg(not(target_arch = "wasm32"))]
174    pub fn from_path<P>(path: P, font_index: u32) -> Result<Font, FontLoadingError>
175    where
176        P: AsRef<Path>,
177    {
178        // TODO(pcwalton): Perhaps use the native FreeType support for opening paths?
179        <Font as Loader>::from_path(path, font_index)
180    }
181
182    /// Creates a font from a native API handle.
183    pub unsafe fn from_native_font(freetype_face: NativeFont) -> Font {
184        // We make an in-memory copy of the underlying font data. This is because the native font
185        // does not necessarily hold a strong reference to the memory backing it.
186        const CHUNK_SIZE: usize = 4096;
187        let mut font_data = vec![];
188        loop {
189            font_data.extend(iter::repeat(0).take(CHUNK_SIZE));
190            let freetype_stream = (*freetype_face).stream;
191            let n_read = ((*freetype_stream).read)(
192                freetype_stream,
193                font_data.len() as FT_ULong,
194                font_data.as_mut_ptr(),
195                CHUNK_SIZE as FT_ULong,
196            );
197            if n_read < CHUNK_SIZE as FT_ULong {
198                break;
199            }
200        }
201
202        Font::from_bytes(Arc::new(font_data), (*freetype_face).face_index as u32).unwrap()
203    }
204
205    /// Loads the font pointed to by a handle.
206    #[inline]
207    pub fn from_handle(handle: &Handle) -> Result<Self, FontLoadingError> {
208        <Self as Loader>::from_handle(handle)
209    }
210
211    /// Determines whether a blob of raw font data represents a supported font, and, if so, what
212    /// type of font it is.
213    pub fn analyze_bytes(font_data: Arc<Vec<u8>>) -> Result<FileType, FontLoadingError> {
214        FREETYPE_LIBRARY.with(|freetype_library| unsafe {
215            let mut freetype_face = ptr::null_mut();
216            if FT_New_Memory_Face(
217                freetype_library.0,
218                (*font_data).as_ptr(),
219                font_data.len() as FT_Long,
220                0,
221                &mut freetype_face,
222            ) != 0
223            {
224                return Err(FontLoadingError::Parse);
225            }
226
227            let font_type = match (*freetype_face).num_faces {
228                1 => FileType::Single,
229                num_faces => FileType::Collection(num_faces as u32),
230            };
231            FT_Done_Face(freetype_face);
232            Ok(font_type)
233        })
234    }
235
236    /// Determines whether a file represents a supported font, and, if so, what type of font it is.
237    #[cfg(not(target_arch = "wasm32"))]
238    pub fn analyze_file(file: &mut File) -> Result<FileType, FontLoadingError> {
239        FREETYPE_LIBRARY.with(|freetype_library| unsafe {
240            file.seek(SeekFrom::Start(0))?;
241            let font_data = Arc::new(utils::slurp_file(file).map_err(FontLoadingError::Io)?);
242
243            let mut freetype_face = ptr::null_mut();
244            if FT_New_Memory_Face(
245                freetype_library.0,
246                (*font_data).as_ptr(),
247                font_data.len() as FT_Long,
248                0,
249                &mut freetype_face,
250            ) != 0
251            {
252                return Err(FontLoadingError::Parse);
253            }
254
255            let font_type = match (*freetype_face).num_faces {
256                1 => FileType::Single,
257                num_faces => FileType::Collection(num_faces as u32),
258            };
259            FT_Done_Face(freetype_face);
260            Ok(font_type)
261        })
262    }
263
264    /// Determines whether a path points to a supported font, and, if so, what type of font it is.
265    #[inline]
266    #[cfg(not(target_arch = "wasm32"))]
267    pub fn analyze_path<P>(path: P) -> Result<FileType, FontLoadingError>
268    where
269        P: AsRef<Path>,
270    {
271        <Self as Loader>::analyze_path(path)
272    }
273
274    /// Returns the wrapped native font handle.
275    ///
276    /// This function increments the reference count of the FreeType face before returning it.
277    /// Therefore, it is the caller's responsibility to free it with `FT_Done_Face`.
278    pub fn native_font(&self) -> NativeFont {
279        unsafe {
280            assert_eq!(FT_Reference_Face(self.freetype_face), 0);
281            self.freetype_face
282        }
283    }
284
285    /// Returns the PostScript name of the font. This should be globally unique.
286    pub fn postscript_name(&self) -> Option<String> {
287        unsafe {
288            let postscript_name = FT_Get_Postscript_Name(self.freetype_face);
289            if !postscript_name.is_null() {
290                return Some(CStr::from_ptr(postscript_name).to_str().unwrap().to_owned());
291            }
292
293            let font_format = FT_Get_Font_Format(self.freetype_face);
294            assert!(!font_format.is_null());
295            let font_format = CStr::from_ptr(font_format).to_str().unwrap();
296            if font_format != "BDF" && font_format != "PCF" {
297                return None;
298            }
299
300            let mut property = mem::zeroed();
301            if FT_Get_BDF_Property(
302                self.freetype_face,
303                "_DEC_DEVICE_FONTNAMES\0".as_ptr() as *const c_char,
304                &mut property,
305            ) != 0
306            {
307                return None;
308            }
309            if property.property_type != BDF_PROPERTY_TYPE_ATOM {
310                return None;
311            }
312            let dec_device_fontnames = CStr::from_ptr(property.value).to_str().unwrap();
313            if !dec_device_fontnames.starts_with("PS=") {
314                return None;
315            }
316            Some(dec_device_fontnames[3..].to_string())
317        }
318    }
319
320    /// Returns the full name of the font (also known as "display name" on macOS).
321    pub fn full_name(&self) -> String {
322        self.get_type_1_or_sfnt_name(PS_DICT_FULL_NAME, TT_NAME_ID_FULL_NAME)
323            .unwrap_or_else(|| self.family_name())
324    }
325
326    /// Returns the name of the font family.
327    pub fn family_name(&self) -> String {
328        unsafe {
329            let ptr = (*self.freetype_face).family_name;
330            // FreeType doesn't guarantee a non-null family name (see issue #5).
331            if ptr.is_null() {
332                String::new()
333            } else {
334                CStr::from_ptr(ptr).to_str().unwrap().to_owned()
335            }
336        }
337    }
338
339    /// Returns true if and only if the font is monospace (fixed-width).
340    pub fn is_monospace(&self) -> bool {
341        unsafe { (*self.freetype_face).face_flags & (FT_FACE_FLAG_FIXED_WIDTH as FT_Long) != 0 }
342    }
343
344    /// Returns the values of various font properties, corresponding to those defined in CSS.
345    pub fn properties(&self) -> Properties {
346        unsafe {
347            let os2_table = self.get_os2_table();
348            let style = match os2_table {
349                Some(os2_table) if ((*os2_table).fsSelection & OS2_FS_SELECTION_OBLIQUE) != 0 => {
350                    Style::Oblique
351                }
352                _ if ((*self.freetype_face).style_flags & (FT_STYLE_FLAG_ITALIC) as FT_Long)
353                    != 0 =>
354                {
355                    Style::Italic
356                }
357                _ => Style::Normal,
358            };
359            let stretch = match os2_table {
360                Some(os2_table) if (1..=9).contains(&(*os2_table).usWidthClass) => {
361                    Stretch(Stretch::MAPPING[((*os2_table).usWidthClass as usize) - 1])
362                }
363                _ => Stretch::NORMAL,
364            };
365            let weight = match os2_table {
366                None => Weight::NORMAL,
367                Some(os2_table) => Weight((*os2_table).usWeightClass as f32),
368            };
369            Properties {
370                style,
371                stretch,
372                weight,
373            }
374        }
375    }
376
377    /// Returns the usual glyph ID for a Unicode character.
378    ///
379    /// Be careful with this function; typographically correct character-to-glyph mapping must be
380    /// done using a *shaper* such as HarfBuzz. This function is only useful for best-effort simple
381    /// use cases like "what does character X look like on its own".
382    #[inline]
383    pub fn glyph_for_char(&self, character: char) -> Option<u32> {
384        unsafe {
385            let res = FT_Get_Char_Index(self.freetype_face, character as FT_ULong);
386            match res {
387                0 => None,
388                _ => Some(res),
389            }
390        }
391    }
392
393    /// Returns the glyph ID for the specified glyph name.
394    #[inline]
395    pub fn glyph_by_name(&self, name: &str) -> Option<u32> {
396        if let Ok(ffi_name) = CString::new(name) {
397            let code =
398                unsafe { FT_Get_Name_Index(self.freetype_face, ffi_name.as_ptr() as *mut c_char) };
399
400            if code > 0 {
401                return Some(code);
402            }
403        }
404        None
405    }
406
407    /// Returns the number of glyphs in the font.
408    ///
409    /// Glyph IDs range from 0 inclusive to this value exclusive.
410    #[inline]
411    pub fn glyph_count(&self) -> u32 {
412        unsafe { (*self.freetype_face).num_glyphs as u32 }
413    }
414
415    /// Sends the vector path for a glyph to a path builder.
416    ///
417    /// If `hinting_mode` is not None, this function performs grid-fitting as requested before
418    /// sending the hinding outlines to the builder.
419    ///
420    /// TODO(pcwalton): What should we do for bitmap glyphs?
421    pub fn outline<S>(
422        &self,
423        glyph_id: u32,
424        hinting: HintingOptions,
425        sink: &mut S,
426    ) -> Result<(), GlyphLoadingError>
427    where
428        S: OutlineSink,
429    {
430        unsafe {
431            let rasterization_options = RasterizationOptions::GrayscaleAa;
432            let load_flags = self
433                .hinting_and_rasterization_options_to_load_flags(hinting, rasterization_options);
434
435            let units_per_em = (*self.freetype_face).units_per_EM;
436            let grid_fitting_size = hinting.grid_fitting_size();
437            if let Some(size) = grid_fitting_size {
438                assert_eq!(
439                    FT_Set_Char_Size(self.freetype_face, size.f32_to_ft_fixed_26_6(), 0, 0, 0),
440                    0
441                );
442            }
443
444            if FT_Load_Glyph(self.freetype_face, glyph_id, load_flags) != 0 {
445                return Err(GlyphLoadingError::NoSuchGlyph);
446            }
447
448            let outline = &(*(*self.freetype_face).glyph).outline;
449            if outline.n_contours == 0 {
450                return Ok(());
451            }
452            let contours = slice::from_raw_parts(outline.contours, outline.n_contours as usize);
453            let point_positions = slice::from_raw_parts(outline.points, outline.n_points as usize);
454            let point_tags = slice::from_raw_parts(outline.tags, outline.n_points as usize);
455
456            let mut current_point_index = 0;
457            for &last_point_index_in_contour in contours {
458                let last_point_index_in_contour = last_point_index_in_contour as usize;
459                let (mut first_point, first_tag) = get_point(
460                    &mut current_point_index,
461                    point_positions,
462                    point_tags,
463                    last_point_index_in_contour,
464                    grid_fitting_size,
465                    units_per_em,
466                );
467                if (first_tag & FT_POINT_TAG_ON_CURVE) == 0 {
468                    // Rare, but can happen; e.g. with Inconsolata (see pathfinder#84).
469                    //
470                    // FIXME(pcwalton): I'm not sure this is right.
471                    let mut temp_point_index = last_point_index_in_contour;
472                    let (last_point, last_tag) = get_point(
473                        &mut temp_point_index,
474                        point_positions,
475                        point_tags,
476                        last_point_index_in_contour,
477                        grid_fitting_size,
478                        units_per_em,
479                    );
480                    if (last_tag & FT_POINT_TAG_ON_CURVE) != 0 {
481                        first_point = last_point
482                    } else {
483                        first_point = last_point.lerp(first_point, 0.5)
484                    }
485                    // Back up so we properly process the first point as a control point.
486                    current_point_index -= 1;
487                }
488                sink.move_to(first_point);
489
490                while current_point_index <= last_point_index_in_contour {
491                    let (mut point0, tag0) = get_point(
492                        &mut current_point_index,
493                        point_positions,
494                        point_tags,
495                        last_point_index_in_contour,
496                        grid_fitting_size,
497                        units_per_em,
498                    );
499                    if (tag0 & FT_POINT_TAG_ON_CURVE) != 0 {
500                        sink.line_to(point0);
501                        continue;
502                    }
503
504                    loop {
505                        if current_point_index > last_point_index_in_contour {
506                            // The *last* point in the contour is off the curve. So we just need to
507                            // close the contour with a quadratic Bézier curve.
508                            sink.quadratic_curve_to(point0, first_point);
509                            break;
510                        }
511
512                        let (point1, tag1) = get_point(
513                            &mut current_point_index,
514                            point_positions,
515                            point_tags,
516                            last_point_index_in_contour,
517                            grid_fitting_size,
518                            units_per_em,
519                        );
520
521                        if (tag0 & FT_POINT_TAG_CUBIC_CONTROL) != 0 {
522                            let ctrl = LineSegment2F::new(point0, point1);
523                            if current_point_index <= last_point_index_in_contour {
524                                // FIXME(pcwalton): Can we have implied on-curve points for cubic
525                                // control points too?
526                                let (point2, _) = get_point(
527                                    &mut current_point_index,
528                                    point_positions,
529                                    point_tags,
530                                    last_point_index_in_contour,
531                                    grid_fitting_size,
532                                    units_per_em,
533                                );
534                                sink.cubic_curve_to(ctrl, point2);
535                            } else {
536                                // Last point on the contour. Use first_point as point2.
537                                sink.cubic_curve_to(ctrl, first_point);
538                            }
539                            break;
540                        }
541
542                        if (tag1 & FT_POINT_TAG_ON_CURVE) != 0 {
543                            sink.quadratic_curve_to(point0, point1);
544                            break;
545                        }
546
547                        // We have an implied on-curve point midway between the two consecutive
548                        // off-curve points.
549                        let point_half = point0.lerp(point1, 0.5);
550                        sink.quadratic_curve_to(point0, point_half);
551                        point0 = point1;
552                    }
553                }
554                sink.close();
555            }
556
557            if hinting.grid_fitting_size().is_some() {
558                reset_freetype_face_char_size(self.freetype_face)
559            }
560        }
561
562        return Ok(());
563
564        fn get_point(
565            current_point_index: &mut usize,
566            point_positions: &[FT_Vector],
567            point_tags: &[c_char],
568            last_point_index_in_contour: usize,
569            grid_fitting_size: Option<f32>,
570            units_per_em: u16,
571        ) -> (Vector2F, c_char) {
572            assert!(*current_point_index <= last_point_index_in_contour);
573            let point_position = point_positions[*current_point_index];
574            let point_tag = point_tags[*current_point_index];
575            *current_point_index += 1;
576
577            let point_position = Vector2I::new(point_position.x as i32, point_position.y as i32);
578            let mut point_position = point_position.ft_fixed_26_6_to_f32();
579            if let Some(grid_fitting_size) = grid_fitting_size {
580                point_position = point_position * (units_per_em as f32) / grid_fitting_size;
581            }
582
583            (point_position, point_tag)
584        }
585    }
586
587    /// Returns the boundaries of a glyph in font units.
588    pub fn typographic_bounds(&self, glyph_id: u32) -> Result<RectF, GlyphLoadingError> {
589        unsafe {
590            if FT_Load_Glyph(
591                self.freetype_face,
592                glyph_id,
593                FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING,
594            ) != 0
595            {
596                return Err(GlyphLoadingError::NoSuchGlyph);
597            }
598
599            let metrics = &(*(*self.freetype_face).glyph).metrics;
600            let rect = RectI::new(
601                Vector2I::new(
602                    metrics.horiBearingX as i32,
603                    (metrics.horiBearingY - metrics.height) as i32,
604                ),
605                Vector2I::new(metrics.width as i32, metrics.height as i32),
606            );
607            Ok(rect.ft_fixed_26_6_to_f32())
608        }
609    }
610
611    /// Returns the distance from the origin of the glyph with the given ID to the next, in font
612    /// units.
613    pub fn advance(&self, glyph_id: u32) -> Result<Vector2F, GlyphLoadingError> {
614        unsafe {
615            if FT_Load_Glyph(
616                self.freetype_face,
617                glyph_id,
618                FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING,
619            ) != 0
620            {
621                return Err(GlyphLoadingError::NoSuchGlyph);
622            }
623
624            let advance = (*(*self.freetype_face).glyph).advance;
625            Ok(Vector2I::new(advance.x as i32, advance.y as i32).ft_fixed_26_6_to_f32())
626        }
627    }
628
629    /// Returns the amount that the given glyph should be displaced from the origin.
630    ///
631    /// FIXME(pcwalton): This always returns zero on FreeType.
632    pub fn origin(&self, _: u32) -> Result<Vector2F, GlyphLoadingError> {
633        warn!("unimplemented");
634        Ok(Vector2F::default())
635    }
636
637    /// Retrieves various metrics that apply to the entire font.
638    pub fn metrics(&self) -> Metrics {
639        let os2_table = self.get_os2_table();
640        unsafe {
641            let ascender = (*self.freetype_face).ascender;
642            let descender = (*self.freetype_face).descender;
643            let underline_position = (*self.freetype_face).underline_position;
644            let underline_thickness = (*self.freetype_face).underline_thickness;
645
646            let bbox = (*self.freetype_face).bbox;
647            let bounding_box_origin = Vector2I::new(bbox.xMin as i32, bbox.yMin as i32);
648            let bounding_box_lower_right = Vector2I::new(bbox.xMax as i32, bbox.yMax as i32);
649            let bounding_box = RectI::from_points(bounding_box_origin, bounding_box_lower_right);
650
651            Metrics {
652                units_per_em: (*self.freetype_face).units_per_EM as u32,
653                ascent: ascender as f32,
654                descent: descender as f32,
655                line_gap: ((*self.freetype_face).height + descender - ascender) as f32,
656                underline_position: (underline_position + underline_thickness / 2) as f32,
657                underline_thickness: underline_thickness as f32,
658                cap_height: os2_table
659                    .map(|table| (*table).sCapHeight as f32)
660                    .unwrap_or(0.0),
661                x_height: os2_table
662                    .map(|table| (*table).sxHeight as f32)
663                    .unwrap_or(0.0),
664                bounding_box: bounding_box.to_f32(),
665            }
666        }
667    }
668
669    /// Returns true if and only if the font loader can perform hinting in the requested way.
670    ///
671    /// Some APIs support only rasterizing glyphs with hinting, not retrieving hinted outlines. If
672    /// `for_rasterization` is false, this function returns true if and only if the loader supports
673    /// retrieval of hinted *outlines*. If `for_rasterization` is true, this function returns true
674    /// if and only if the loader supports *rasterizing* hinted glyphs.
675    #[inline]
676    pub fn supports_hinting_options(
677        &self,
678        hinting_options: HintingOptions,
679        for_rasterization: bool,
680    ) -> bool {
681        match (hinting_options, for_rasterization) {
682            (HintingOptions::None, _)
683            | (HintingOptions::Vertical(_), true)
684            | (HintingOptions::VerticalSubpixel(_), true)
685            | (HintingOptions::Full(_), true) => true,
686            (HintingOptions::Vertical(_), false)
687            | (HintingOptions::VerticalSubpixel(_), false)
688            | (HintingOptions::Full(_), false) => false,
689        }
690    }
691
692    fn get_type_1_or_sfnt_name(&self, type_1_id: u32, sfnt_id: u16) -> Option<String> {
693        unsafe {
694            let ps_value_size =
695                FT_Get_PS_Font_Value(self.freetype_face, type_1_id, 0, ptr::null_mut(), 0);
696            if ps_value_size > 0 {
697                let mut buffer = vec![0; ps_value_size as usize];
698                if FT_Get_PS_Font_Value(
699                    self.freetype_face,
700                    type_1_id,
701                    0,
702                    buffer.as_mut_ptr() as *mut c_void,
703                    buffer.len() as FT_Long,
704                ) == 0
705                {
706                    return String::from_utf8(buffer).ok();
707                }
708            }
709
710            let sfnt_name_count = FT_Get_Sfnt_Name_Count(self.freetype_face);
711            let mut sfnt_name = mem::zeroed();
712            for sfnt_name_index in 0..sfnt_name_count {
713                assert_eq!(
714                    FT_Get_Sfnt_Name(self.freetype_face, sfnt_name_index, &mut sfnt_name),
715                    0
716                );
717                if sfnt_name.name_id != sfnt_id {
718                    continue;
719                }
720
721                match (sfnt_name.platform_id, sfnt_name.encoding_id) {
722                    (TT_PLATFORM_APPLE_UNICODE, _) => {
723                        let mut sfnt_name_bytes =
724                            slice::from_raw_parts(sfnt_name.string, sfnt_name.string_len as usize);
725                        let mut sfnt_name_string = Vec::with_capacity(sfnt_name_bytes.len() / 2);
726                        while !sfnt_name_bytes.is_empty() {
727                            sfnt_name_string.push(sfnt_name_bytes.read_u16::<BigEndian>().unwrap())
728                        }
729                        if let Ok(result) = String::from_utf16(&sfnt_name_string) {
730                            return Some(result);
731                        }
732                    }
733                    (platform_id, _) => {
734                        warn!(
735                            "get_type_1_or_sfnt_name(): found invalid platform ID {}",
736                            platform_id
737                        );
738                        // TODO(pcwalton)
739                    }
740                }
741            }
742
743            None
744        }
745    }
746
747    fn get_os2_table(&self) -> Option<*const TT_OS2> {
748        unsafe {
749            let table = FT_Get_Sfnt_Table(self.freetype_face, ft_sfnt_os2);
750            if table.is_null() {
751                None
752            } else {
753                Some(table as *const TT_OS2)
754            }
755        }
756    }
757
758    /// Returns the pixel boundaries that the glyph will take up when rendered using this loader's
759    /// rasterizer at the given size and origin.
760    #[inline]
761    pub fn raster_bounds(
762        &self,
763        glyph_id: u32,
764        point_size: f32,
765        transform: Transform2F,
766        hinting_options: HintingOptions,
767        rasterization_options: RasterizationOptions,
768    ) -> Result<RectI, GlyphLoadingError> {
769        <Self as Loader>::raster_bounds(
770            self,
771            glyph_id,
772            point_size,
773            transform,
774            hinting_options,
775            rasterization_options,
776        )
777    }
778
779    /// Rasterizes a glyph to a canvas with the given size and origin.
780    ///
781    /// Format conversion will be performed if the canvas format does not match the rasterization
782    /// options. For example, if bilevel (black and white) rendering is requested to an RGBA
783    /// surface, this function will automatically convert the 1-bit raster image to the 32-bit
784    /// format of the canvas. Note that this may result in a performance penalty, depending on the
785    /// loader.
786    ///
787    /// If `hinting_options` is not None, the requested grid fitting is performed.
788    pub fn rasterize_glyph(
789        &self,
790        canvas: &mut Canvas,
791        glyph_id: u32,
792        point_size: f32,
793        transform: Transform2F,
794        hinting_options: HintingOptions,
795        rasterization_options: RasterizationOptions,
796    ) -> Result<(), GlyphLoadingError> {
797        // TODO(pcwalton): This is woefully incomplete. See WebRender's code for a more complete
798        // implementation.
799        unsafe {
800            let matrix = transform.matrix.0 * F32x4::new(65536.0, -65536.0, -65536.0, 65536.0);
801            let matrix = matrix.to_i32x4();
802            let vector = transform.vector.f32_to_ft_fixed_26_6();
803
804            let mut delta = FT_Vector {
805                x: vector.x() as FT_Pos,
806                y: -vector.y() as FT_Pos,
807            };
808            let mut ft_shape = FT_Matrix {
809                xx: matrix.x() as FT_Fixed,
810                xy: matrix.y() as FT_Fixed,
811                yx: matrix.z() as FT_Fixed,
812                yy: matrix.w() as FT_Fixed,
813            };
814            FT_Set_Transform(self.freetype_face, &mut ft_shape, &mut delta);
815
816            assert_eq!(
817                FT_Set_Char_Size(
818                    self.freetype_face,
819                    point_size.f32_to_ft_fixed_26_6(),
820                    0,
821                    0,
822                    0
823                ),
824                0
825            );
826
827            let mut load_flags = FT_LOAD_DEFAULT | FT_LOAD_RENDER;
828            load_flags |= self.hinting_and_rasterization_options_to_load_flags(
829                hinting_options,
830                rasterization_options,
831            );
832            if FT_Load_Glyph(self.freetype_face, glyph_id, load_flags) != 0 {
833                return Err(GlyphLoadingError::NoSuchGlyph);
834            }
835
836            // TODO(pcwalton): Use the FreeType "direct" API to save a copy here. Note that we will
837            // need to keep this around for bilevel rendering, as the direct API doesn't work with
838            // that mode.
839            let bitmap = &(*(*self.freetype_face).glyph).bitmap;
840            let bitmap_stride = bitmap.pitch as usize;
841            let bitmap_width = bitmap.width;
842            let bitmap_height = bitmap.rows;
843            let bitmap_size = Vector2I::new(bitmap_width, bitmap_height);
844            let bitmap_buffer = bitmap.buffer as *const i8 as *const u8;
845            let bitmap_length = bitmap_stride * bitmap_height as usize;
846            if bitmap_buffer.is_null() {
847                assert_eq!(
848                    bitmap_length, 0,
849                    "bitmap length should be 0 when bitmap_buffer is nullptr"
850                );
851            } else {
852                let buffer = slice::from_raw_parts(bitmap_buffer, bitmap_length);
853                let dst_point = Vector2I::new(
854                    (*(*self.freetype_face).glyph).bitmap_left,
855                    -(*(*self.freetype_face).glyph).bitmap_top,
856                );
857
858                // FIXME(pcwalton): This function should return a Result instead.
859                match bitmap.pixel_mode as u32 {
860                    FT_PIXEL_MODE_GRAY => {
861                        canvas.blit_from(dst_point, buffer, bitmap_size, bitmap_stride, Format::A8);
862                    }
863                    FT_PIXEL_MODE_LCD | FT_PIXEL_MODE_LCD_V => {
864                        canvas.blit_from(
865                            dst_point,
866                            buffer,
867                            bitmap_size,
868                            bitmap_stride,
869                            Format::Rgb24,
870                        );
871                    }
872                    FT_PIXEL_MODE_MONO => {
873                        canvas.blit_from_bitmap_1bpp(dst_point, buffer, bitmap_size, bitmap_stride);
874                    }
875                    _ => panic!("Unexpected FreeType pixel mode!"),
876                }
877            }
878
879            FT_Set_Transform(self.freetype_face, ptr::null_mut(), ptr::null_mut());
880            reset_freetype_face_char_size(self.freetype_face);
881            Ok(())
882        }
883    }
884
885    fn hinting_and_rasterization_options_to_load_flags(
886        &self,
887        hinting: HintingOptions,
888        rasterization: RasterizationOptions,
889    ) -> i32 {
890        let mut options = match (hinting, rasterization) {
891            (HintingOptions::VerticalSubpixel(_), _) | (_, RasterizationOptions::SubpixelAa) => {
892                FT_LOAD_TARGET_LCD
893            }
894            (HintingOptions::None, _) => FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING,
895            (HintingOptions::Vertical(_), RasterizationOptions::Bilevel)
896            | (HintingOptions::Full(_), RasterizationOptions::Bilevel) => FT_LOAD_TARGET_MONO,
897            (HintingOptions::Vertical(_), _) => FT_LOAD_TARGET_LIGHT,
898            (HintingOptions::Full(_), _) => FT_LOAD_TARGET_NORMAL,
899        };
900        if rasterization == RasterizationOptions::Bilevel {
901            options |= FT_LOAD_MONOCHROME
902        }
903        options
904    }
905
906    /// Returns a handle to this font, if possible.
907    ///
908    /// This is useful if you want to open the font with a different loader.
909    #[inline]
910    pub fn handle(&self) -> Option<Handle> {
911        <Self as Loader>::handle(self)
912    }
913
914    /// Attempts to return the raw font data (contents of the font file).
915    ///
916    /// If this font is a member of a collection, this function returns the data for the entire
917    /// collection.
918    pub fn copy_font_data(&self) -> Option<Arc<Vec<u8>>> {
919        Some(self.font_data.clone())
920    }
921
922    /// Get font fallback results for the given text and locale.
923    ///
924    /// Note: this is currently just a stub implementation, a proper implementation
925    /// would likely use FontConfig, at least on Linux. It's not clear what a
926    /// FreeType loader with a non-FreeType source should do.
927    fn get_fallbacks(&self, text: &str, _locale: &str) -> FallbackResult<Font> {
928        warn!("unsupported");
929        FallbackResult {
930            fonts: Vec::new(),
931            valid_len: text.len(),
932        }
933    }
934
935    /// Returns the raw contents of the OpenType table with the given tag.
936    ///
937    /// Tags are four-character codes. A list of tags can be found in the [OpenType specification].
938    ///
939    /// [OpenType specification]: https://docs.microsoft.com/en-us/typography/opentype/spec/
940    pub fn load_font_table(&self, table_tag: u32) -> Option<Box<[u8]>> {
941        unsafe {
942            let mut len = 0;
943
944            if 0 != FT_Load_Sfnt_Table(
945                self.freetype_face,
946                table_tag as FT_ULong,
947                0,
948                ptr::null_mut(),
949                &mut len,
950            ) {
951                return None;
952            }
953
954            let mut buf = Box::<[u8]>::from(vec![0; len as usize]);
955            if 0 != FT_Load_Sfnt_Table(
956                self.freetype_face,
957                table_tag as FT_ULong,
958                0,
959                buf.as_mut_ptr() as *mut FT_Byte,
960                &mut len,
961            ) {
962                return None;
963            }
964
965            Some(buf)
966        }
967    }
968}
969
970impl Clone for Font {
971    fn clone(&self) -> Font {
972        unsafe {
973            assert_eq!(FT_Reference_Face(self.freetype_face), 0);
974            Font {
975                freetype_face: self.freetype_face,
976                font_data: self.font_data.clone(),
977            }
978        }
979    }
980}
981
982impl Drop for Font {
983    fn drop(&mut self) {
984        // The AccessError can be ignored, as it means FREETYPE_LIBRARY has already been
985        // destroyed, and it already destroys all FreeType resources.
986        // https://freetype.org/freetype2/docs/reference/ft2-module_management.html#ft_done_library
987        let _ = FREETYPE_LIBRARY.try_with(|freetype_library| unsafe {
988            if !freetype_library.0.is_null() && !self.freetype_face.is_null() {
989                assert_eq!(FT_Done_Face(self.freetype_face), 0);
990            }
991        });
992    }
993}
994
995impl Debug for Font {
996    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
997        self.family_name().fmt(fmt)
998    }
999}
1000
1001impl Loader for Font {
1002    type NativeFont = NativeFont;
1003
1004    #[inline]
1005    fn from_bytes(font_data: Arc<Vec<u8>>, font_index: u32) -> Result<Self, FontLoadingError> {
1006        Font::from_bytes(font_data, font_index)
1007    }
1008
1009    #[inline]
1010    #[cfg(not(target_arch = "wasm32"))]
1011    fn from_file(file: &mut File, font_index: u32) -> Result<Font, FontLoadingError> {
1012        Font::from_file(file, font_index)
1013    }
1014
1015    #[inline]
1016    fn analyze_bytes(font_data: Arc<Vec<u8>>) -> Result<FileType, FontLoadingError> {
1017        Font::analyze_bytes(font_data)
1018    }
1019
1020    #[cfg(not(target_arch = "wasm32"))]
1021    fn analyze_file(file: &mut File) -> Result<FileType, FontLoadingError> {
1022        Font::analyze_file(file)
1023    }
1024
1025    #[inline]
1026    fn native_font(&self) -> Self::NativeFont {
1027        self.native_font()
1028    }
1029
1030    #[inline]
1031    unsafe fn from_native_font(native_font: Self::NativeFont) -> Self {
1032        Font::from_native_font(native_font)
1033    }
1034
1035    #[inline]
1036    fn postscript_name(&self) -> Option<String> {
1037        self.postscript_name()
1038    }
1039
1040    #[inline]
1041    fn full_name(&self) -> String {
1042        self.full_name()
1043    }
1044
1045    #[inline]
1046    fn family_name(&self) -> String {
1047        self.family_name()
1048    }
1049
1050    #[inline]
1051    fn is_monospace(&self) -> bool {
1052        self.is_monospace()
1053    }
1054
1055    #[inline]
1056    fn properties(&self) -> Properties {
1057        self.properties()
1058    }
1059
1060    #[inline]
1061    fn glyph_for_char(&self, character: char) -> Option<u32> {
1062        self.glyph_for_char(character)
1063    }
1064
1065    #[inline]
1066    fn glyph_by_name(&self, name: &str) -> Option<u32> {
1067        self.glyph_by_name(name)
1068    }
1069
1070    #[inline]
1071    fn glyph_count(&self) -> u32 {
1072        self.glyph_count()
1073    }
1074
1075    #[inline]
1076    fn outline<S>(
1077        &self,
1078        glyph_id: u32,
1079        hinting_mode: HintingOptions,
1080        sink: &mut S,
1081    ) -> Result<(), GlyphLoadingError>
1082    where
1083        S: OutlineSink,
1084    {
1085        self.outline(glyph_id, hinting_mode, sink)
1086    }
1087
1088    #[inline]
1089    fn typographic_bounds(&self, glyph_id: u32) -> Result<RectF, GlyphLoadingError> {
1090        self.typographic_bounds(glyph_id)
1091    }
1092
1093    #[inline]
1094    fn advance(&self, glyph_id: u32) -> Result<Vector2F, GlyphLoadingError> {
1095        self.advance(glyph_id)
1096    }
1097
1098    #[inline]
1099    fn origin(&self, origin: u32) -> Result<Vector2F, GlyphLoadingError> {
1100        self.origin(origin)
1101    }
1102
1103    #[inline]
1104    fn metrics(&self) -> Metrics {
1105        self.metrics()
1106    }
1107
1108    #[inline]
1109    fn copy_font_data(&self) -> Option<Arc<Vec<u8>>> {
1110        self.copy_font_data()
1111    }
1112
1113    #[inline]
1114    fn supports_hinting_options(
1115        &self,
1116        hinting_options: HintingOptions,
1117        for_rasterization: bool,
1118    ) -> bool {
1119        self.supports_hinting_options(hinting_options, for_rasterization)
1120    }
1121
1122    #[inline]
1123    fn rasterize_glyph(
1124        &self,
1125        canvas: &mut Canvas,
1126        glyph_id: u32,
1127        point_size: f32,
1128        transform: Transform2F,
1129        hinting_options: HintingOptions,
1130        rasterization_options: RasterizationOptions,
1131    ) -> Result<(), GlyphLoadingError> {
1132        self.rasterize_glyph(
1133            canvas,
1134            glyph_id,
1135            point_size,
1136            transform,
1137            hinting_options,
1138            rasterization_options,
1139        )
1140    }
1141
1142    #[inline]
1143    fn get_fallbacks(&self, text: &str, locale: &str) -> FallbackResult<Self> {
1144        self.get_fallbacks(text, locale)
1145    }
1146
1147    #[inline]
1148    fn load_font_table(&self, table_tag: u32) -> Option<Box<[u8]>> {
1149        self.load_font_table(table_tag)
1150    }
1151}
1152
1153unsafe fn setup_freetype_face(face: FT_Face) {
1154    reset_freetype_face_char_size(face);
1155}
1156
1157unsafe fn reset_freetype_face_char_size(face: FT_Face) {
1158    // Apple Color Emoji has 0 units per em. Whee!
1159    let units_per_em = (*face).units_per_EM as i64;
1160    if units_per_em > 0 {
1161        assert_eq!(
1162            FT_Set_Char_Size(face, ((*face).units_per_EM as FT_Long) << 6, 0, 0, 0),
1163            0
1164        );
1165    }
1166}
1167
1168trait F32ToFtFixed {
1169    type Output;
1170    fn f32_to_ft_fixed_26_6(self) -> Self::Output;
1171}
1172
1173trait FtFixedToF32 {
1174    type Output;
1175    fn ft_fixed_26_6_to_f32(self) -> Self::Output;
1176}
1177
1178impl F32ToFtFixed for Vector2F {
1179    type Output = Vector2I;
1180    #[inline]
1181    fn f32_to_ft_fixed_26_6(self) -> Vector2I {
1182        (self * 64.0).to_i32()
1183    }
1184}
1185
1186impl F32ToFtFixed for f32 {
1187    type Output = FT_Fixed;
1188    #[inline]
1189    fn f32_to_ft_fixed_26_6(self) -> FT_Fixed {
1190        (self * 64.0) as FT_Fixed
1191    }
1192}
1193
1194impl FtFixedToF32 for Vector2I {
1195    type Output = Vector2F;
1196    #[inline]
1197    fn ft_fixed_26_6_to_f32(self) -> Vector2F {
1198        (self.to_f32() * (1.0 / 64.0)).round()
1199    }
1200}
1201
1202impl FtFixedToF32 for RectI {
1203    type Output = RectF;
1204    #[inline]
1205    fn ft_fixed_26_6_to_f32(self) -> RectF {
1206        self.to_f32() * (1.0 / 64.0)
1207    }
1208}
1209
1210extern "C" {
1211    fn FT_Get_Font_Format(face: FT_Face) -> *const c_char;
1212    fn FT_Get_BDF_Property(
1213        face: FT_Face,
1214        prop_name: *const c_char,
1215        aproperty: *mut BDF_PropertyRec,
1216    ) -> FT_Error;
1217    fn FT_Get_PS_Font_Value(
1218        face: FT_Face,
1219        key: u32,
1220        idx: FT_UInt,
1221        value: *mut c_void,
1222        value_len: FT_Long,
1223    ) -> FT_Long;
1224    fn FT_Load_Sfnt_Table(
1225        face: FT_Face,
1226        tag: FT_ULong,
1227        offset: FT_Long,
1228        buffer: *mut FT_Byte,
1229        length: *mut FT_ULong,
1230    ) -> FT_Error;
1231}
1232
1233#[cfg(test)]
1234mod test {
1235    use crate::loaders::freetype::Font;
1236
1237    static PCF_FONT_PATH: &str = "resources/tests/times-roman-pcf/timR12.pcf";
1238    static PCF_FONT_POSTSCRIPT_NAME: &str = "Times-Roman";
1239
1240    #[test]
1241    fn get_pcf_postscript_name() {
1242        let font = Font::from_path(PCF_FONT_PATH, 0).unwrap();
1243        assert_eq!(font.postscript_name().unwrap(), PCF_FONT_POSTSCRIPT_NAME);
1244    }
1245}