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::freetype::{FT_Byte, FT_Done_Face, FT_Error, FT_Face, FT_FACE_FLAG_FIXED_WIDTH};
18use freetype::freetype::{
19 FT_Fixed, FT_Get_Char_Index, FT_Get_Name_Index, FT_Get_Postscript_Name, FT_Pos,
20};
21use freetype::freetype::{FT_Get_Sfnt_Table, FT_Init_FreeType, FT_LcdFilter, FT_Library};
22use freetype::freetype::{FT_Library_SetLcdFilter, FT_Load_Glyph, FT_LOAD_DEFAULT};
23use freetype::freetype::{FT_Load_Sfnt_Table, FT_Long, FT_Matrix, FT_New_Memory_Face};
24use freetype::freetype::{FT_Reference_Face, FT_Set_Char_Size, FT_Set_Transform, FT_Sfnt_Tag};
25use freetype::freetype::{FT_UInt, FT_ULong, FT_UShort, FT_Vector, FT_STYLE_FLAG_ITALIC};
26use freetype::freetype::{FT_LOAD_MONOCHROME, FT_LOAD_NO_HINTING, FT_LOAD_RENDER};
27use freetype::tt_os2::TT_OS2;
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 FT_RENDER_MODE_NORMAL: u32 = 0;
70const FT_RENDER_MODE_LIGHT: u32 = 1;
71const FT_RENDER_MODE_MONO: u32 = 2;
72const FT_RENDER_MODE_LCD: u32 = 3;
73
74const FT_LOAD_TARGET_LIGHT: u32 = (FT_RENDER_MODE_LIGHT & 15) << 16;
75const FT_LOAD_TARGET_LCD: u32 = (FT_RENDER_MODE_LCD & 15) << 16;
76const FT_LOAD_TARGET_MONO: u32 = (FT_RENDER_MODE_MONO & 15) << 16;
77const FT_LOAD_TARGET_NORMAL: u32 = (FT_RENDER_MODE_NORMAL & 15) << 16;
78
79const FT_PIXEL_MODE_MONO: u8 = 1;
80const FT_PIXEL_MODE_GRAY: u8 = 2;
81const FT_PIXEL_MODE_LCD: u8 = 5;
82const FT_PIXEL_MODE_LCD_V: u8 = 6;
83
84const OS2_FS_SELECTION_OBLIQUE: u16 = 1 << 9;
85
86// Not in our FreeType bindings, so we define these ourselves.
87#[allow(dead_code)]
88const BDF_PROPERTY_TYPE_NONE: BDF_PropertyType = 0;
89#[allow(dead_code)]
90const BDF_PROPERTY_TYPE_ATOM: BDF_PropertyType = 1;
91#[allow(dead_code)]
92const BDF_PROPERTY_TYPE_INTEGER: BDF_PropertyType = 2;
93#[allow(dead_code)]
94const BDF_PROPERTY_TYPE_CARDINAL: BDF_PropertyType = 3;
95
96thread_local! {
97 static FREETYPE_LIBRARY: FT_Library = {
98 unsafe {
99 let mut library = ptr::null_mut();
100 assert_eq!(FT_Init_FreeType(&mut library), 0);
101 FT_Library_SetLcdFilter(library, FT_LcdFilter::FT_LCD_FILTER_DEFAULT);
102 library
103 }
104 };
105}
106
107/// The handle that the FreeType API natively uses to represent a font.
108pub type NativeFont = FT_Face;
109
110// Not in our FreeType bindings, so we define this ourselves.
111#[allow(non_camel_case_types)]
112type BDF_PropertyType = i32;
113
114// Not in our FreeType bindings, so we define this ourselves.
115#[repr(C)]
116struct BDF_PropertyRec {
117 property_type: BDF_PropertyType,
118 value: *const c_char,
119}
120
121/// A cross-platform loader that uses the FreeType library to load and rasterize fonts.
122///
123///
124/// On macOS and Windows, the Cargo feature `loader-freetype-default` can be used to opt into this
125/// loader by default.
126pub struct Font {
127 freetype_face: FT_Face,
128 font_data: Arc<Vec<u8>>,
129}
130
131impl Font {
132 /// Loads a font from raw font data (the contents of a `.ttf`/`.otf`/etc. file).
133 ///
134 /// If the data represents a collection (`.ttc`/`.otc`/etc.), `font_index` specifies the index
135 /// of the font to load from it. If the data represents a single font, pass 0 for `font_index`.
136 pub fn from_bytes(font_data: Arc<Vec<u8>>, font_index: u32) -> Result<Font, FontLoadingError> {
137 FREETYPE_LIBRARY.with(|freetype_library| unsafe {
138 let mut freetype_face = ptr::null_mut();
139 if FT_New_Memory_Face(
140 *freetype_library,
141 (*font_data).as_ptr(),
142 font_data.len() as FT_Long,
143 font_index as FT_Long,
144 &mut freetype_face,
145 ) != 0
146 {
147 return Err(FontLoadingError::Parse);
148 }
149
150 setup_freetype_face(freetype_face);
151
152 Ok(Font {
153 freetype_face,
154 font_data,
155 })
156 })
157 }
158
159 /// Loads a font from 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 #[cfg(not(target_arch = "wasm32"))]
164 pub fn from_file(file: &mut File, font_index: u32) -> Result<Font, FontLoadingError> {
165 file.seek(SeekFrom::Start(0))?;
166 let font_data = Arc::new(utils::slurp_file(file).map_err(FontLoadingError::Io)?);
167 Font::from_bytes(font_data, font_index)
168 }
169
170 /// Loads a font from the path to a `.ttf`/`.otf`/etc. file.
171 ///
172 /// If the file is a collection (`.ttc`/`.otc`/etc.), `font_index` specifies the index of the
173 /// font to load from it. If the file represents a single font, pass 0 for `font_index`.
174 #[inline]
175 #[cfg(not(target_arch = "wasm32"))]
176 pub fn from_path<P>(path: P, font_index: u32) -> Result<Font, FontLoadingError>
177 where
178 P: AsRef<Path>,
179 {
180 // TODO(pcwalton): Perhaps use the native FreeType support for opening paths?
181 <Font as Loader>::from_path(path, font_index)
182 }
183
184 /// Creates a font from a native API handle.
185 pub unsafe fn from_native_font(freetype_face: NativeFont) -> Font {
186 // We make an in-memory copy of the underlying font data. This is because the native font
187 // does not necessarily hold a strong reference to the memory backing it.
188 const CHUNK_SIZE: usize = 4096;
189 let mut font_data = vec![];
190 loop {
191 font_data.extend(iter::repeat(0).take(CHUNK_SIZE));
192 let freetype_stream = (*freetype_face).stream;
193 let n_read = ((*freetype_stream).read.unwrap())(
194 freetype_stream,
195 font_data.len() as FT_ULong,
196 font_data.as_mut_ptr(),
197 CHUNK_SIZE as FT_ULong,
198 );
199 if n_read < CHUNK_SIZE as FT_ULong {
200 break;
201 }
202 }
203
204 Font::from_bytes(Arc::new(font_data), (*freetype_face).face_index as u32).unwrap()
205 }
206
207 /// Loads the font pointed to by a handle.
208 #[inline]
209 pub fn from_handle(handle: &Handle) -> Result<Self, FontLoadingError> {
210 <Self as Loader>::from_handle(handle)
211 }
212
213 /// Determines whether a blob of raw font data represents a supported font, and, if so, what
214 /// type of font it is.
215 pub fn analyze_bytes(font_data: Arc<Vec<u8>>) -> Result<FileType, FontLoadingError> {
216 FREETYPE_LIBRARY.with(|freetype_library| unsafe {
217 let mut freetype_face = ptr::null_mut();
218 if FT_New_Memory_Face(
219 *freetype_library,
220 (*font_data).as_ptr(),
221 font_data.len() as FT_Long,
222 0,
223 &mut freetype_face,
224 ) != 0
225 {
226 return Err(FontLoadingError::Parse);
227 }
228
229 let font_type = match (*freetype_face).num_faces {
230 1 => FileType::Single,
231 num_faces => FileType::Collection(num_faces as u32),
232 };
233 FT_Done_Face(freetype_face);
234 Ok(font_type)
235 })
236 }
237
238 /// Determines whether a file represents a supported font, and, if so, what type of font it is.
239 #[cfg(not(target_arch = "wasm32"))]
240 pub fn analyze_file(file: &mut File) -> Result<FileType, FontLoadingError> {
241 FREETYPE_LIBRARY.with(|freetype_library| unsafe {
242 file.seek(SeekFrom::Start(0))?;
243 let font_data = Arc::new(utils::slurp_file(file).map_err(FontLoadingError::Io)?);
244
245 let mut freetype_face = ptr::null_mut();
246 if FT_New_Memory_Face(
247 *freetype_library,
248 (*font_data).as_ptr(),
249 font_data.len() as FT_Long,
250 0,
251 &mut freetype_face,
252 ) != 0
253 {
254 return Err(FontLoadingError::Parse);
255 }
256
257 let font_type = match (*freetype_face).num_faces {
258 1 => FileType::Single,
259 num_faces => FileType::Collection(num_faces as u32),
260 };
261 FT_Done_Face(freetype_face);
262 Ok(font_type)
263 })
264 }
265
266 /// Determines whether a path points to a supported font, and, if so, what type of font it is.
267 #[inline]
268 #[cfg(not(target_arch = "wasm32"))]
269 pub fn analyze_path<P>(path: P) -> Result<FileType, FontLoadingError>
270 where
271 P: AsRef<Path>,
272 {
273 <Self as Loader>::analyze_path(path)
274 }
275
276 /// Returns the wrapped native font handle.
277 ///
278 /// This function increments the reference count of the FreeType face before returning it.
279 /// Therefore, it is the caller's responsibility to free it with `FT_Done_Face`.
280 pub fn native_font(&self) -> NativeFont {
281 unsafe {
282 assert_eq!(FT_Reference_Face(self.freetype_face), 0);
283 self.freetype_face
284 }
285 }
286
287 /// Returns the PostScript name of the font. This should be globally unique.
288 pub fn postscript_name(&self) -> Option<String> {
289 unsafe {
290 let postscript_name = FT_Get_Postscript_Name(self.freetype_face);
291 if !postscript_name.is_null() {
292 return Some(CStr::from_ptr(postscript_name).to_str().unwrap().to_owned());
293 }
294
295 let font_format = FT_Get_Font_Format(self.freetype_face);
296 assert!(!font_format.is_null());
297 let font_format = CStr::from_ptr(font_format).to_str().unwrap();
298 if font_format != "BDF" && font_format != "PCF" {
299 return None;
300 }
301
302 let mut property = mem::zeroed();
303 if FT_Get_BDF_Property(
304 self.freetype_face,
305 "_DEC_DEVICE_FONTNAMES\0".as_ptr() as *const c_char,
306 &mut property,
307 ) != 0
308 {
309 return None;
310 }
311 if property.property_type != BDF_PROPERTY_TYPE_ATOM {
312 return None;
313 }
314 let dec_device_fontnames = CStr::from_ptr(property.value).to_str().unwrap();
315 if !dec_device_fontnames.starts_with("PS=") {
316 return None;
317 }
318 Some(dec_device_fontnames[3..].to_string())
319 }
320 }
321
322 /// Returns the full name of the font (also known as "display name" on macOS).
323 pub fn full_name(&self) -> String {
324 self.get_type_1_or_sfnt_name(PS_DICT_FULL_NAME, TT_NAME_ID_FULL_NAME)
325 .unwrap_or_else(|| self.family_name())
326 }
327
328 /// Returns the name of the font family.
329 pub fn family_name(&self) -> String {
330 unsafe {
331 let ptr = (*self.freetype_face).family_name;
332 // FreeType doesn't guarantee a non-null family name (see issue #5).
333 if ptr.is_null() {
334 String::new()
335 } else {
336 CStr::from_ptr(ptr).to_str().unwrap().to_owned()
337 }
338 }
339 }
340
341 /// Returns true if and only if the font is monospace (fixed-width).
342 pub fn is_monospace(&self) -> bool {
343 unsafe { (*self.freetype_face).face_flags & (FT_FACE_FLAG_FIXED_WIDTH as FT_Long) != 0 }
344 }
345
346 /// Returns the values of various font properties, corresponding to those defined in CSS.
347 pub fn properties(&self) -> Properties {
348 unsafe {
349 let os2_table = self.get_os2_table();
350 let style = match os2_table {
351 Some(os2_table) if ((*os2_table).fsSelection & OS2_FS_SELECTION_OBLIQUE) != 0 => {
352 Style::Oblique
353 }
354 _ if ((*self.freetype_face).style_flags & (FT_STYLE_FLAG_ITALIC) as FT_Long)
355 != 0 =>
356 {
357 Style::Italic
358 }
359 _ => Style::Normal,
360 };
361 let stretch = match os2_table {
362 Some(os2_table) if (1..=9).contains(&(*os2_table).usWidthClass) => {
363 Stretch(Stretch::MAPPING[((*os2_table).usWidthClass as usize) - 1])
364 }
365 _ => Stretch::NORMAL,
366 };
367 let weight = match os2_table {
368 None => Weight::NORMAL,
369 Some(os2_table) => Weight((*os2_table).usWeightClass as f32),
370 };
371 Properties {
372 style,
373 stretch,
374 weight,
375 }
376 }
377 }
378
379 /// Returns the usual glyph ID for a Unicode character.
380 ///
381 /// Be careful with this function; typographically correct character-to-glyph mapping must be
382 /// done using a *shaper* such as HarfBuzz. This function is only useful for best-effort simple
383 /// use cases like "what does character X look like on its own".
384 #[inline]
385 pub fn glyph_for_char(&self, character: char) -> Option<u32> {
386 unsafe {
387 let res = FT_Get_Char_Index(self.freetype_face, character as FT_ULong);
388 match res {
389 0 => None,
390 _ => Some(res),
391 }
392 }
393 }
394
395 /// Returns the glyph ID for the specified glyph name.
396 #[inline]
397 pub fn glyph_by_name(&self, name: &str) -> Option<u32> {
398 if let Ok(ffi_name) = CString::new(name) {
399 let code =
400 unsafe { FT_Get_Name_Index(self.freetype_face, ffi_name.as_ptr() as *mut c_char) };
401
402 if code > 0 {
403 return Some(u32::from(code));
404 }
405 }
406 None
407 }
408
409 /// Returns the number of glyphs in the font.
410 ///
411 /// Glyph IDs range from 0 inclusive to this value exclusive.
412 #[inline]
413 pub fn glyph_count(&self) -> u32 {
414 unsafe { (*self.freetype_face).num_glyphs as u32 }
415 }
416
417 /// Sends the vector path for a glyph to a path builder.
418 ///
419 /// If `hinting_mode` is not None, this function performs grid-fitting as requested before
420 /// sending the hinding outlines to the builder.
421 ///
422 /// TODO(pcwalton): What should we do for bitmap glyphs?
423 pub fn outline<S>(
424 &self,
425 glyph_id: u32,
426 hinting: HintingOptions,
427 sink: &mut S,
428 ) -> Result<(), GlyphLoadingError>
429 where
430 S: OutlineSink,
431 {
432 unsafe {
433 let rasterization_options = RasterizationOptions::GrayscaleAa;
434 let load_flags = self
435 .hinting_and_rasterization_options_to_load_flags(hinting, rasterization_options);
436
437 let units_per_em = (*self.freetype_face).units_per_EM;
438 let grid_fitting_size = hinting.grid_fitting_size();
439 if let Some(size) = grid_fitting_size {
440 assert_eq!(
441 FT_Set_Char_Size(self.freetype_face, size.f32_to_ft_fixed_26_6(), 0, 0, 0),
442 0
443 );
444 }
445
446 if FT_Load_Glyph(self.freetype_face, glyph_id, load_flags as i32) != 0 {
447 return Err(GlyphLoadingError::NoSuchGlyph);
448 }
449
450 let outline = &(*(*self.freetype_face).glyph).outline;
451 let contours =
452 slice::from_raw_parts((*outline).contours, (*outline).n_contours as usize);
453 let point_positions =
454 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) as i32,
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) as i32,
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 retriving 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_Tag::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 as i32) != 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 as i32;
843 let bitmap_height = (*bitmap).rows as i32;
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 let buffer = slice::from_raw_parts(bitmap_buffer, bitmap_length);
848 let dst_point = Vector2I::new(
849 (*(*self.freetype_face).glyph).bitmap_left,
850 -(*(*self.freetype_face).glyph).bitmap_top,
851 );
852
853 // FIXME(pcwalton): This function should return a Result instead.
854 match (*bitmap).pixel_mode {
855 FT_PIXEL_MODE_GRAY => {
856 canvas.blit_from(dst_point, buffer, bitmap_size, bitmap_stride, Format::A8);
857 }
858 FT_PIXEL_MODE_LCD | FT_PIXEL_MODE_LCD_V => {
859 canvas.blit_from(dst_point, buffer, bitmap_size, bitmap_stride, Format::Rgb24);
860 }
861 FT_PIXEL_MODE_MONO => {
862 canvas.blit_from_bitmap_1bpp(dst_point, buffer, bitmap_size, bitmap_stride);
863 }
864 _ => panic!("Unexpected FreeType pixel mode!"),
865 }
866
867 FT_Set_Transform(self.freetype_face, ptr::null_mut(), ptr::null_mut());
868 reset_freetype_face_char_size(self.freetype_face);
869 Ok(())
870 }
871 }
872
873 fn hinting_and_rasterization_options_to_load_flags(
874 &self,
875 hinting: HintingOptions,
876 rasterization: RasterizationOptions,
877 ) -> u32 {
878 let mut options = match (hinting, rasterization) {
879 (HintingOptions::VerticalSubpixel(_), _) | (_, RasterizationOptions::SubpixelAa) => {
880 FT_LOAD_TARGET_LCD
881 }
882 (HintingOptions::None, _) => FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING,
883 (HintingOptions::Vertical(_), RasterizationOptions::Bilevel)
884 | (HintingOptions::Full(_), RasterizationOptions::Bilevel) => FT_LOAD_TARGET_MONO,
885 (HintingOptions::Vertical(_), _) => FT_LOAD_TARGET_LIGHT,
886 (HintingOptions::Full(_), _) => FT_LOAD_TARGET_NORMAL,
887 };
888 if rasterization == RasterizationOptions::Bilevel {
889 options |= FT_LOAD_MONOCHROME
890 }
891 options
892 }
893
894 /// Returns a handle to this font, if possible.
895 ///
896 /// This is useful if you want to open the font with a different loader.
897 #[inline]
898 pub fn handle(&self) -> Option<Handle> {
899 <Self as Loader>::handle(self)
900 }
901
902 /// Attempts to return the raw font data (contents of the font file).
903 ///
904 /// If this font is a member of a collection, this function returns the data for the entire
905 /// collection.
906 pub fn copy_font_data(&self) -> Option<Arc<Vec<u8>>> {
907 Some(self.font_data.clone())
908 }
909
910 /// Get font fallback results for the given text and locale.
911 ///
912 /// Note: this is currently just a stub implementation, a proper implementation
913 /// would likely use FontConfig, at least on Linux. It's not clear what a
914 /// FreeType loader with a non-FreeType source should do.
915 fn get_fallbacks(&self, text: &str, _locale: &str) -> FallbackResult<Font> {
916 warn!("unsupported");
917 FallbackResult {
918 fonts: Vec::new(),
919 valid_len: text.len(),
920 }
921 }
922
923 /// Returns the raw contents of the OpenType table with the given tag.
924 ///
925 /// Tags are four-character codes. A list of tags can be found in the [OpenType specification].
926 ///
927 /// [OpenType specification]: https://docs.microsoft.com/en-us/typography/opentype/spec/
928 pub fn load_font_table(&self, table_tag: u32) -> Option<Box<[u8]>> {
929 unsafe {
930 let mut len = 0;
931
932 if 0 != FT_Load_Sfnt_Table(
933 self.freetype_face,
934 table_tag as FT_ULong,
935 0,
936 ptr::null_mut(),
937 &mut len,
938 ) {
939 return None;
940 }
941
942 let mut buf = Box::<[u8]>::from(vec![0; len as usize]);
943 if 0 != FT_Load_Sfnt_Table(
944 self.freetype_face,
945 table_tag as FT_ULong,
946 0,
947 buf.as_mut_ptr() as *mut FT_Byte,
948 &mut len,
949 ) {
950 return None;
951 }
952
953 Some(buf)
954 }
955 }
956}
957
958impl Clone for Font {
959 fn clone(&self) -> Font {
960 unsafe {
961 assert_eq!(FT_Reference_Face(self.freetype_face), 0);
962 Font {
963 freetype_face: self.freetype_face,
964 font_data: self.font_data.clone(),
965 }
966 }
967 }
968}
969
970impl Drop for Font {
971 fn drop(&mut self) {
972 unsafe {
973 if !self.freetype_face.is_null() {
974 assert_eq!(FT_Done_Face(self.freetype_face), 0);
975 }
976 }
977 }
978}
979
980impl Debug for Font {
981 fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
982 self.family_name().fmt(fmt)
983 }
984}
985
986impl Loader for Font {
987 type NativeFont = NativeFont;
988
989 #[inline]
990 fn from_bytes(font_data: Arc<Vec<u8>>, font_index: u32) -> Result<Self, FontLoadingError> {
991 Font::from_bytes(font_data, font_index)
992 }
993
994 #[inline]
995 #[cfg(not(target_arch = "wasm32"))]
996 fn from_file(file: &mut File, font_index: u32) -> Result<Font, FontLoadingError> {
997 Font::from_file(file, font_index)
998 }
999
1000 #[inline]
1001 fn analyze_bytes(font_data: Arc<Vec<u8>>) -> Result<FileType, FontLoadingError> {
1002 Font::analyze_bytes(font_data)
1003 }
1004
1005 #[cfg(not(target_arch = "wasm32"))]
1006 fn analyze_file(file: &mut File) -> Result<FileType, FontLoadingError> {
1007 Font::analyze_file(file)
1008 }
1009
1010 #[inline]
1011 fn native_font(&self) -> Self::NativeFont {
1012 self.native_font()
1013 }
1014
1015 #[inline]
1016 unsafe fn from_native_font(native_font: Self::NativeFont) -> Self {
1017 Font::from_native_font(native_font)
1018 }
1019
1020 #[inline]
1021 fn postscript_name(&self) -> Option<String> {
1022 self.postscript_name()
1023 }
1024
1025 #[inline]
1026 fn full_name(&self) -> String {
1027 self.full_name()
1028 }
1029
1030 #[inline]
1031 fn family_name(&self) -> String {
1032 self.family_name()
1033 }
1034
1035 #[inline]
1036 fn is_monospace(&self) -> bool {
1037 self.is_monospace()
1038 }
1039
1040 #[inline]
1041 fn properties(&self) -> Properties {
1042 self.properties()
1043 }
1044
1045 #[inline]
1046 fn glyph_for_char(&self, character: char) -> Option<u32> {
1047 self.glyph_for_char(character)
1048 }
1049
1050 #[inline]
1051 fn glyph_by_name(&self, name: &str) -> Option<u32> {
1052 self.glyph_by_name(name)
1053 }
1054
1055 #[inline]
1056 fn glyph_count(&self) -> u32 {
1057 self.glyph_count()
1058 }
1059
1060 #[inline]
1061 fn outline<S>(
1062 &self,
1063 glyph_id: u32,
1064 hinting_mode: HintingOptions,
1065 sink: &mut S,
1066 ) -> Result<(), GlyphLoadingError>
1067 where
1068 S: OutlineSink,
1069 {
1070 self.outline(glyph_id, hinting_mode, sink)
1071 }
1072
1073 #[inline]
1074 fn typographic_bounds(&self, glyph_id: u32) -> Result<RectF, GlyphLoadingError> {
1075 self.typographic_bounds(glyph_id)
1076 }
1077
1078 #[inline]
1079 fn advance(&self, glyph_id: u32) -> Result<Vector2F, GlyphLoadingError> {
1080 self.advance(glyph_id)
1081 }
1082
1083 #[inline]
1084 fn origin(&self, origin: u32) -> Result<Vector2F, GlyphLoadingError> {
1085 self.origin(origin)
1086 }
1087
1088 #[inline]
1089 fn metrics(&self) -> Metrics {
1090 self.metrics()
1091 }
1092
1093 #[inline]
1094 fn copy_font_data(&self) -> Option<Arc<Vec<u8>>> {
1095 self.copy_font_data()
1096 }
1097
1098 #[inline]
1099 fn supports_hinting_options(
1100 &self,
1101 hinting_options: HintingOptions,
1102 for_rasterization: bool,
1103 ) -> bool {
1104 self.supports_hinting_options(hinting_options, for_rasterization)
1105 }
1106
1107 #[inline]
1108 fn rasterize_glyph(
1109 &self,
1110 canvas: &mut Canvas,
1111 glyph_id: u32,
1112 point_size: f32,
1113 transform: Transform2F,
1114 hinting_options: HintingOptions,
1115 rasterization_options: RasterizationOptions,
1116 ) -> Result<(), GlyphLoadingError> {
1117 self.rasterize_glyph(
1118 canvas,
1119 glyph_id,
1120 point_size,
1121 transform,
1122 hinting_options,
1123 rasterization_options,
1124 )
1125 }
1126
1127 #[inline]
1128 fn get_fallbacks(&self, text: &str, locale: &str) -> FallbackResult<Self> {
1129 self.get_fallbacks(text, locale)
1130 }
1131
1132 #[inline]
1133 fn load_font_table(&self, table_tag: u32) -> Option<Box<[u8]>> {
1134 self.load_font_table(table_tag)
1135 }
1136}
1137
1138unsafe fn setup_freetype_face(face: FT_Face) {
1139 reset_freetype_face_char_size(face);
1140}
1141
1142unsafe fn reset_freetype_face_char_size(face: FT_Face) {
1143 // Apple Color Emoji has 0 units per em. Whee!
1144 let units_per_em: i64 = (*face).units_per_EM as i64;
1145 if units_per_em > 0 {
1146 assert_eq!(
1147 FT_Set_Char_Size(face, ((*face).units_per_EM as FT_Long) << 6, 0, 0, 0),
1148 0
1149 );
1150 }
1151}
1152
1153#[repr(C)]
1154struct FT_SfntName {
1155 platform_id: FT_UShort,
1156 encoding_id: FT_UShort,
1157 language_id: FT_UShort,
1158 name_id: FT_UShort,
1159 string: *mut FT_Byte,
1160 string_len: FT_UInt,
1161}
1162
1163trait F32ToFtFixed {
1164 type Output;
1165 fn f32_to_ft_fixed_26_6(self) -> Self::Output;
1166}
1167
1168trait FtFixedToF32 {
1169 type Output;
1170 fn ft_fixed_26_6_to_f32(self) -> Self::Output;
1171}
1172
1173impl F32ToFtFixed for Vector2F {
1174 type Output = Vector2I;
1175 #[inline]
1176 fn f32_to_ft_fixed_26_6(self) -> Vector2I {
1177 (self * 64.0).to_i32()
1178 }
1179}
1180
1181impl F32ToFtFixed for f32 {
1182 type Output = FT_Fixed;
1183 #[inline]
1184 fn f32_to_ft_fixed_26_6(self) -> FT_Fixed {
1185 (self * 64.0) as FT_Fixed
1186 }
1187}
1188
1189impl FtFixedToF32 for Vector2I {
1190 type Output = Vector2F;
1191 #[inline]
1192 fn ft_fixed_26_6_to_f32(self) -> Vector2F {
1193 (self.to_f32() * (1.0 / 64.0)).round()
1194 }
1195}
1196
1197impl FtFixedToF32 for RectI {
1198 type Output = RectF;
1199 #[inline]
1200 fn ft_fixed_26_6_to_f32(self) -> RectF {
1201 self.to_f32() * (1.0 / 64.0)
1202 }
1203}
1204
1205extern "C" {
1206 fn FT_Get_Font_Format(face: FT_Face) -> *const c_char;
1207 fn FT_Get_BDF_Property(
1208 face: FT_Face,
1209 prop_name: *const c_char,
1210 aproperty: *mut BDF_PropertyRec,
1211 ) -> FT_Error;
1212 fn FT_Get_PS_Font_Value(
1213 face: FT_Face,
1214 key: u32,
1215 idx: FT_UInt,
1216 value: *mut c_void,
1217 value_len: FT_Long,
1218 ) -> FT_Long;
1219 fn FT_Get_Sfnt_Name(face: FT_Face, idx: FT_UInt, aname: *mut FT_SfntName) -> FT_Error;
1220 fn FT_Get_Sfnt_Name_Count(face: FT_Face) -> FT_UInt;
1221}
1222
1223#[cfg(test)]
1224mod test {
1225 use crate::loaders::freetype::Font;
1226
1227 static PCF_FONT_PATH: &'static str = "resources/tests/times-roman-pcf/timR12.pcf";
1228 static PCF_FONT_POSTSCRIPT_NAME: &'static str = "Times-Roman";
1229
1230 #[test]
1231 fn get_pcf_postscript_name() {
1232 let font = Font::from_path(PCF_FONT_PATH, 0).unwrap();
1233 assert_eq!(font.postscript_name().unwrap(), PCF_FONT_POSTSCRIPT_NAME);
1234 }
1235}
1236