Skip to main content

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