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