├── .travis.yml ├── examples ├── assets │ ├── FiraSans-Regular.ttf │ └── LICENSE ├── single_glyph.rs └── glyph_outline.rs ├── .gitignore ├── src ├── render_mode.rs ├── charmap.rs ├── bitmap_glyph.rs ├── tt_postscript.rs ├── stroker.rs ├── lib.rs ├── tt_os2.rs ├── bitmap.rs ├── outline.rs ├── library.rs ├── glyph.rs ├── glyph_slot.rs ├── face.rs └── error.rs ├── Cargo.toml ├── LICENSE └── README.md /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | -------------------------------------------------------------------------------- /examples/assets/FiraSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PistonDevelopers/freetype-rs/HEAD/examples/assets/FiraSans-Regular.ttf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *~ 3 | *# 4 | *.o 5 | *.so 6 | *.swp 7 | *.old 8 | *.bak 9 | *.kate-swp 10 | *.dylib 11 | *.dSYM 12 | *.dll 13 | *.rlib 14 | *.dummy 15 | *.exe 16 | *-test 17 | /bin/main 18 | /bin/test-internal 19 | /bin/test-external 20 | /doc/ 21 | /target/ 22 | /build/ 23 | /.rust/ 24 | rusti.sh 25 | watch.sh 26 | /examples/**/target 27 | .vscode 28 | 29 | Cargo.lock 30 | -------------------------------------------------------------------------------- /src/render_mode.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi; 2 | 3 | #[repr(u32)] 4 | #[derive(Copy, Clone)] 5 | pub enum RenderMode { 6 | Normal = ffi::FT_RENDER_MODE_NORMAL, 7 | Light = ffi::FT_RENDER_MODE_LIGHT, 8 | Mono = ffi::FT_RENDER_MODE_MONO, 9 | Lcd = ffi::FT_RENDER_MODE_LCD, 10 | LcdV = ffi::FT_RENDER_MODE_LCD_V, 11 | Sdf = ffi::FT_RENDER_MODE_SDF, 12 | Max = ffi::FT_RENDER_MODE_MAX, 13 | } 14 | -------------------------------------------------------------------------------- /src/charmap.rs: -------------------------------------------------------------------------------- 1 | use freetype_sys::FT_CharMap; 2 | 3 | pub struct CharMap { 4 | raw: FT_CharMap, 5 | } 6 | 7 | impl CharMap { 8 | pub fn new(raw: FT_CharMap) -> Self { 9 | CharMap { raw } 10 | } 11 | 12 | pub fn platform_id(&self) -> u16 { 13 | unsafe { (*self.raw).platform_id } 14 | } 15 | 16 | pub fn encoding_id(&self) -> u16 { 17 | unsafe { (*self.raw).encoding_id } 18 | } 19 | 20 | pub fn encoding(&self) -> u32 { 21 | unsafe { (*self.raw).encoding } 22 | } 23 | 24 | pub fn raw(&self) -> FT_CharMap { 25 | self.raw 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "freetype-rs" 3 | version = "0.38.0" 4 | authors = ["Coeuvre "] 5 | keywords = ["freetype", "font", "glyph"] 6 | description = "Bindings for FreeType font library" 7 | license = "MIT" 8 | readme = "README.md" 9 | repository = "https://github.com/PistonDevelopers/freetype-rs.git" 10 | homepage = "https://github.com/PistonDevelopers/freetype-rs" 11 | edition = "2021" 12 | 13 | [lib] 14 | name = "freetype" 15 | 16 | [features] 17 | bundled = ["freetype-sys/bundled"] 18 | 19 | [dependencies] 20 | bitflags = "2.4.0" 21 | libc = "0.2.1" 22 | freetype-sys = "0.23.0" 23 | 24 | [dev-dependencies] 25 | unicode-normalization = "0.1.0" 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 PistonDevelopers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # freetype-rs [![Build Status](https://travis-ci.org/PistonDevelopers/freetype-rs.svg?branch=master)](https://travis-ci.org/PistonDevelopers/freetype-rs) 2 | 3 | Rust bindings for FreeType library 4 | 5 | ## Requirements 6 | 7 | * *Cargo*: We use Cargo to compile the project. 8 | * *FreeType2 development libraries*: For installation instructions see 9 | [freetype-sys](https://github.com/PistonDevelopers/freetype-sys). 10 | 11 | If the building fails, then it is likely that pkg-config does not find a FreeType library it can bind to. Use the feature "bundled" to build a static version of the library (requires a C compiler): 12 | 13 | ```text 14 | [dependencies] 15 | freetype-rs = { version = "*", features = ["bundled"] } 16 | ``` 17 | 18 | ## Build 19 | 20 | Clone this repo then run 21 | ``` 22 | cd freetype-rs 23 | cargo build 24 | ``` 25 | 26 | ## Examples 27 | 28 | To build examples, use `cargo test`. They are all built in `./target/debug/examples/*`. 29 | 30 | To run examples, use `cargo run --example name`, for example: 31 | ``` 32 | cargo run --example single_glyph examples/assets/FiraSans-Regular.ttf A 33 | ``` 34 | 35 | [How to contribute](https://github.com/PistonDevelopers/piston/blob/master/CONTRIBUTING.md) 36 | -------------------------------------------------------------------------------- /src/bitmap_glyph.rs: -------------------------------------------------------------------------------- 1 | use crate::{ffi, Bitmap}; 2 | use std::ptr::null_mut; 3 | 4 | pub struct BitmapGlyph { 5 | library_raw: ffi::FT_Library, 6 | raw: ffi::FT_BitmapGlyph, 7 | } 8 | 9 | impl BitmapGlyph { 10 | pub unsafe fn from_raw(library_raw: ffi::FT_Library, raw: ffi::FT_BitmapGlyph) -> Self { 11 | ffi::FT_Reference_Library(library_raw); 12 | BitmapGlyph { library_raw, raw } 13 | } 14 | 15 | #[inline(always)] 16 | pub fn left(&self) -> i32 { 17 | unsafe { (*self.raw).left } 18 | } 19 | 20 | #[inline(always)] 21 | pub fn top(&self) -> i32 { 22 | unsafe { (*self.raw).top } 23 | } 24 | 25 | #[inline(always)] 26 | pub fn bitmap(&self) -> Bitmap { 27 | unsafe { Bitmap::from_raw(&(*self.raw).bitmap) } 28 | } 29 | 30 | #[inline(always)] 31 | pub fn raw(&self) -> &ffi::FT_BitmapGlyphRec { 32 | unsafe { &*self.raw } 33 | } 34 | } 35 | 36 | impl Clone for BitmapGlyph { 37 | fn clone(&self) -> Self { 38 | let mut target = null_mut(); 39 | 40 | let err = unsafe { ffi::FT_Glyph_Copy(self.raw as ffi::FT_Glyph, &mut target) }; 41 | if err == ffi::FT_Err_Ok { 42 | unsafe { BitmapGlyph::from_raw(self.library_raw, target as ffi::FT_BitmapGlyph) } 43 | } else { 44 | panic!("Failed to copy bitmap glyph") 45 | } 46 | } 47 | } 48 | 49 | impl Drop for BitmapGlyph { 50 | fn drop(&mut self) { 51 | let err = unsafe { 52 | ffi::FT_Done_Glyph(self.raw as ffi::FT_Glyph); 53 | ffi::FT_Done_Library(self.library_raw) 54 | }; 55 | if err != ffi::FT_Err_Ok { 56 | panic!("Failed to drop library") 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /examples/single_glyph.rs: -------------------------------------------------------------------------------- 1 | use freetype as ft; 2 | 3 | use unicode_normalization::UnicodeNormalization; 4 | 5 | const WIDTH: i32 = 32; 6 | const HEIGHT: i32 = 24; 7 | 8 | type Figure = [[u8; WIDTH as usize]; HEIGHT as usize]; 9 | 10 | fn draw_bitmap(bitmap: ft::Bitmap, x: i32, y: i32) -> Figure { 11 | let mut figure = [[0; WIDTH as usize]; HEIGHT as usize]; 12 | let w = bitmap.width() as usize; 13 | let x_max = x + w as i32; 14 | let y_max = y + bitmap.rows(); 15 | 16 | for (p, i) in (x..x_max).enumerate() { 17 | for (q, j) in (y..y_max).enumerate() { 18 | if i < 0 || j < 0 || i >= WIDTH || j >= HEIGHT { 19 | continue; 20 | } 21 | figure[j as usize][i as usize] |= bitmap.buffer()[q * w + p]; 22 | } 23 | } 24 | figure 25 | } 26 | 27 | fn main() { 28 | let mut args = std::env::args(); 29 | if args.len() != 3 { 30 | let exe = args.next().unwrap(); 31 | println!("Usage: {} font character", exe); 32 | return; 33 | } 34 | 35 | let font = args.nth(1).unwrap(); 36 | let character = args.next().and_then(|s| s.nfc().next()).unwrap() as usize; 37 | let library = ft::Library::init().unwrap(); 38 | let face = library.new_face(font, 0).unwrap(); 39 | 40 | face.set_char_size(40 * 64, 0, 50, 0).unwrap(); 41 | face.load_char(character, ft::face::LoadFlag::RENDER) 42 | .unwrap(); 43 | 44 | let glyph = face.glyph(); 45 | let x = glyph.bitmap_left(); 46 | let y = HEIGHT - glyph.bitmap_top(); 47 | let figure = draw_bitmap(glyph.bitmap(), x, y); 48 | for row in figure { 49 | for v in row { 50 | let c = match v { 51 | 0 => " ", 52 | 1..=127 => "*", 53 | _ => "+", 54 | }; 55 | print!("{}", c) 56 | } 57 | println!(" "); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/tt_postscript.rs: -------------------------------------------------------------------------------- 1 | use crate::face::Face; 2 | use crate::ffi; 3 | 4 | #[derive(Copy, Clone)] 5 | pub struct TrueTypePostscriptTable { 6 | raw: ffi::TT_Postscript_Internal, 7 | } 8 | 9 | impl TrueTypePostscriptTable { 10 | pub fn from_face(face: &mut Face) -> Option { 11 | unsafe { 12 | let post = 13 | ffi::FT_Get_Sfnt_Table(face.raw_mut() as *mut ffi::FT_FaceRec, ffi::ft_sfnt_post) 14 | as ffi::TT_Postscript_Internal; 15 | if !post.is_null() && (*post).formatType != 0 { 16 | Some(TrueTypePostscriptTable { raw: post }) 17 | } else { 18 | None 19 | } 20 | } 21 | } 22 | 23 | #[inline(always)] 24 | pub fn format_type(&self) -> ffi::FT_Fixed { 25 | unsafe { (*self.raw).formatType } 26 | } 27 | 28 | #[inline(always)] 29 | pub fn italic_angle(&self) -> ffi::FT_Fixed { 30 | unsafe { (*self.raw).italicAngle } 31 | } 32 | 33 | #[inline(always)] 34 | pub fn underline_position(&self) -> ffi::FT_Short { 35 | unsafe { (*self.raw).underlinePosition } 36 | } 37 | 38 | #[inline(always)] 39 | pub fn underline_thickness(&self) -> ffi::FT_Short { 40 | unsafe { (*self.raw).underlineThickness } 41 | } 42 | 43 | #[inline(always)] 44 | pub fn is_fixed_pitch(&self) -> ffi::FT_ULong { 45 | unsafe { (*self.raw).isFixedPitch } 46 | } 47 | 48 | #[inline(always)] 49 | pub fn min_mem_type_42(&self) -> ffi::FT_ULong { 50 | unsafe { (*self.raw).minMemType42 } 51 | } 52 | 53 | #[inline(always)] 54 | pub fn max_mem_type_42(&self) -> ffi::FT_ULong { 55 | unsafe { (*self.raw).maxMemType42 } 56 | } 57 | 58 | #[inline(always)] 59 | pub fn min_mem_type_1(&self) -> ffi::FT_ULong { 60 | unsafe { (*self.raw).minMemType1 } 61 | } 62 | 63 | #[inline(always)] 64 | pub fn max_mem_type_1(&self) -> ffi::FT_ULong { 65 | unsafe { (*self.raw).maxMemType1 } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/stroker.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi; 2 | 3 | #[repr(u32)] 4 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 5 | pub enum StrokerLineCap { 6 | Butt = ffi::FT_STROKER_LINECAP_BUTT, 7 | Round = ffi::FT_STROKER_LINECAP_ROUND, 8 | Square = ffi::FT_STROKER_LINECAP_SQUARE, 9 | } 10 | 11 | #[repr(u32)] 12 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 13 | pub enum StrokerLineJoin { 14 | Round = ffi::FT_STROKER_LINEJOIN_ROUND, 15 | Bevel = ffi::FT_STROKER_LINEJOIN_BEVEL, 16 | MiterVariable = ffi::FT_STROKER_LINEJOIN_MITER_VARIABLE, 17 | MiterFixed = ffi::FT_STROKER_LINEJOIN_MITER_FIXED, 18 | } 19 | 20 | pub struct Stroker { 21 | library_raw: ffi::FT_Library, 22 | raw: ffi::FT_Stroker, 23 | } 24 | 25 | impl Stroker { 26 | pub unsafe fn from_raw(library_raw: ffi::FT_Library, raw: ffi::FT_Stroker) -> Self { 27 | ffi::FT_Reference_Library(library_raw); 28 | Stroker { library_raw, raw } 29 | } 30 | 31 | pub fn set( 32 | &self, 33 | radius: ffi::FT_Fixed, 34 | line_cap: StrokerLineCap, 35 | line_join: StrokerLineJoin, 36 | miter_limit: ffi::FT_Fixed, 37 | ) { 38 | unsafe { 39 | ffi::FT_Stroker_Set( 40 | self.raw, 41 | radius, 42 | line_cap as u32, 43 | line_join as u32, 44 | miter_limit, 45 | ); 46 | } 47 | } 48 | 49 | pub fn raw(&self) -> &ffi::FT_StrokerRec { 50 | unsafe { &*self.raw } 51 | } 52 | 53 | pub fn raw_mut(&mut self) -> &mut ffi::FT_StrokerRec { 54 | unsafe { &mut *self.raw } 55 | } 56 | 57 | pub(crate) fn raw_stroker(&self) -> ffi::FT_Stroker { 58 | self.raw 59 | } 60 | } 61 | 62 | impl Drop for Stroker { 63 | fn drop(&mut self) { 64 | let err = unsafe { 65 | ffi::FT_Stroker_Done(self.raw); 66 | ffi::FT_Done_Library(self.library_raw) 67 | }; 68 | if err != ffi::FT_Err_Ok { 69 | panic!("Failed to drop library"); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /examples/glyph_outline.rs: -------------------------------------------------------------------------------- 1 | use freetype as ft; 2 | 3 | fn draw_curve(curve: ft::outline::Curve) { 4 | match curve { 5 | ft::outline::Curve::Line(pt) => println!("L {} {}", pt.x, -pt.y), 6 | ft::outline::Curve::Bezier2(pt1, pt2) => { 7 | println!("Q {} {} {} {}", pt1.x, -pt1.y, pt2.x, -pt2.y) 8 | } 9 | ft::outline::Curve::Bezier3(pt1, pt2, pt3) => println!( 10 | "C {} {} {} {} {} {}", 11 | pt1.x, -pt1.y, pt2.x, -pt2.y, pt3.x, -pt3.y 12 | ), 13 | } 14 | } 15 | 16 | fn main() { 17 | let mut args = std::env::args(); 18 | 19 | if args.len() != 3 { 20 | let exe = args.next().unwrap(); 21 | println!("Usage: {} font character", exe); 22 | return; 23 | } 24 | 25 | let font = args.nth(1).unwrap(); 26 | let character = args.next().and_then(|s| s.chars().next()).unwrap() as usize; 27 | let library = ft::Library::init().unwrap(); 28 | let face = library.new_face(font, 0).unwrap(); 29 | 30 | face.set_char_size(40 * 64, 0, 50, 0).unwrap(); 31 | face.load_char(character, ft::face::LoadFlag::NO_SCALE) 32 | .unwrap(); 33 | 34 | let glyph = face.glyph(); 35 | let metrics = glyph.metrics(); 36 | let xmin = metrics.horiBearingX - 5; 37 | let width = metrics.width + 10; 38 | let ymin = -metrics.horiBearingY - 5; 39 | let height = metrics.height + 10; 40 | let outline = glyph.outline().unwrap(); 41 | 42 | println!(""); 43 | println!(""); 45 | println!( 46 | "", 47 | xmin, ymin, width, height 48 | ); 49 | 50 | for contour in outline.contours_iter() { 51 | let start = contour.start(); 52 | println!( 53 | ""); 60 | } 61 | println!(""); 62 | } 63 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Rust wrapper around freetype 2 library 3 | //! 4 | //! # Initialization 5 | //! 6 | //! To create a new freetype context, instantiate the Library struct as below. 7 | //! The Library (along with other objects) obeys RAII and is dropped when the struct goes out of 8 | //! scope. 9 | //! 10 | //! # Example 11 | //! 12 | //! ```no_run 13 | //! fn main() { 14 | //! use freetype::Library; 15 | //! use freetype::face::LoadFlag; 16 | //! 17 | //! // Init the library 18 | //! let lib = Library::init().unwrap(); 19 | //! // Load a font face 20 | //! let face = lib.new_face("/path/to/a/font/file.ttf", 0).unwrap(); 21 | //! // Set the font size 22 | //! face.set_char_size(40 * 64, 0, 50, 0).unwrap(); 23 | //! // Load a character 24 | //! face.load_char('A' as usize, LoadFlag::RENDER).unwrap(); 25 | //! // Get the glyph instance 26 | //! let glyph = face.glyph(); 27 | //! do_something_with_bitmap(glyph.bitmap()); 28 | //! } 29 | //! # fn do_something_with_bitmap(_bitmap: freetype::Bitmap) {} 30 | //! ``` 31 | //! 32 | //! See in the `examples/` folder for more examples. 33 | //! 34 | //! # External links 35 | //! - See [freetype docs](http://www.freetype.org/freetype2/docs/reference/ft2-index.html) 36 | //! for more information 37 | 38 | #![allow(clippy::missing_safety_doc)] 39 | #![deny(missing_copy_implementations)] 40 | 41 | pub use freetype_sys; 42 | 43 | pub use crate::bitmap::Bitmap; 44 | pub use crate::bitmap_glyph::BitmapGlyph; 45 | pub use crate::error::{Error, FtResult}; 46 | pub use crate::face::Face; 47 | pub use crate::glyph::Glyph; 48 | pub use crate::glyph_slot::GlyphSlot; 49 | pub use crate::library::{LcdFilter, Library}; 50 | pub use crate::outline::Outline; 51 | pub use crate::render_mode::RenderMode; 52 | pub use crate::stroker::{Stroker, StrokerLineCap, StrokerLineJoin}; 53 | pub use freetype_sys as ffi; 54 | 55 | pub mod bitmap; 56 | pub mod bitmap_glyph; 57 | pub mod charmap; 58 | pub mod error; 59 | pub mod face; 60 | pub mod glyph; 61 | pub mod glyph_slot; 62 | pub mod library; 63 | pub mod outline; 64 | pub mod render_mode; 65 | pub mod stroker; 66 | pub mod tt_os2; 67 | pub mod tt_postscript; 68 | 69 | pub type BBox = ffi::FT_BBox; 70 | pub type GlyphMetrics = ffi::FT_Glyph_Metrics; 71 | pub type Matrix = ffi::FT_Matrix; 72 | pub type Vector = ffi::FT_Vector; 73 | -------------------------------------------------------------------------------- /src/tt_os2.rs: -------------------------------------------------------------------------------- 1 | use crate::face::Face; 2 | use crate::ffi; 3 | 4 | #[derive(Copy, Clone)] 5 | pub struct TrueTypeOS2Table { 6 | raw: ffi::TT_OS2_Internal, 7 | } 8 | 9 | impl TrueTypeOS2Table { 10 | pub fn from_face(face: &mut Face) -> Option { 11 | unsafe { 12 | let os2 = 13 | ffi::FT_Get_Sfnt_Table(face.raw_mut() as *mut ffi::FT_FaceRec, ffi::ft_sfnt_os2) 14 | as ffi::TT_OS2_Internal; 15 | if !os2.is_null() && (*os2).version != 0xffff { 16 | Some(TrueTypeOS2Table { raw: os2 }) 17 | } else { 18 | None 19 | } 20 | } 21 | } 22 | 23 | #[inline(always)] 24 | pub fn version(&self) -> ffi::FT_UShort { 25 | unsafe { (*self.raw).version } 26 | } 27 | 28 | #[inline(always)] 29 | pub fn avg_char_width(&self) -> ffi::FT_Short { 30 | unsafe { (*self.raw).xAvgCharWidth } 31 | } 32 | 33 | #[inline(always)] 34 | pub fn us_weight_class(&self) -> ffi::FT_UShort { 35 | unsafe { (*self.raw).usWeightClass } 36 | } 37 | 38 | #[inline(always)] 39 | pub fn us_width_class(&self) -> ffi::FT_UShort { 40 | unsafe { (*self.raw).usWidthClass } 41 | } 42 | 43 | #[inline(always)] 44 | pub fn fs_type(&self) -> ffi::FT_UShort { 45 | unsafe { (*self.raw).fsType } 46 | } 47 | 48 | #[inline(always)] 49 | pub fn y_subscript_x_size(&self) -> ffi::FT_Short { 50 | unsafe { (*self.raw).ySubscriptXSize } 51 | } 52 | 53 | #[inline(always)] 54 | pub fn y_subscript_y_size(&self) -> ffi::FT_Short { 55 | unsafe { (*self.raw).ySubscriptYSize } 56 | } 57 | 58 | #[inline(always)] 59 | pub fn y_subscript_x_offset(&self) -> ffi::FT_Short { 60 | unsafe { (*self.raw).ySubscriptXOffset } 61 | } 62 | 63 | #[inline(always)] 64 | pub fn y_subscript_y_offset(&self) -> ffi::FT_Short { 65 | unsafe { (*self.raw).ySubscriptYOffset } 66 | } 67 | 68 | #[inline(always)] 69 | pub fn y_superscript_x_size(&self) -> ffi::FT_Short { 70 | unsafe { (*self.raw).ySuperscriptXSize } 71 | } 72 | 73 | #[inline(always)] 74 | pub fn y_superscript_y_size(&self) -> ffi::FT_Short { 75 | unsafe { (*self.raw).ySuperscriptYSize } 76 | } 77 | 78 | #[inline(always)] 79 | pub fn y_superscript_x_offset(&self) -> ffi::FT_Short { 80 | unsafe { (*self.raw).ySuperscriptXOffset } 81 | } 82 | 83 | #[inline(always)] 84 | pub fn y_superscript_y_offset(&self) -> ffi::FT_Short { 85 | unsafe { (*self.raw).ySuperscriptYOffset } 86 | } 87 | 88 | #[inline(always)] 89 | pub fn y_strikeout_size(&self) -> ffi::FT_Short { 90 | unsafe { (*self.raw).yStrikeoutSize } 91 | } 92 | 93 | #[inline(always)] 94 | pub fn y_strikeout_position(&self) -> ffi::FT_Short { 95 | unsafe { (*self.raw).yStrikeoutPosition } 96 | } 97 | 98 | #[inline(always)] 99 | pub fn s_family_class(&self) -> ffi::FT_Short { 100 | unsafe { (*self.raw).sFamilyClass } 101 | } 102 | 103 | #[inline(always)] 104 | pub fn fs_selection(&self) -> ffi::FT_UShort { 105 | unsafe { (*self.raw).fsSelection } 106 | } 107 | 108 | #[inline(always)] 109 | pub fn s_typo_ascender(&self) -> ffi::FT_Short { 110 | unsafe { (*self.raw).sTypoAscender } 111 | } 112 | 113 | #[inline(always)] 114 | pub fn s_typo_descender(&self) -> ffi::FT_Short { 115 | unsafe { (*self.raw).sTypoDescender } 116 | } 117 | 118 | #[inline(always)] 119 | pub fn s_typo_line_gap(&self) -> ffi::FT_Short { 120 | unsafe { (*self.raw).sTypoLineGap } 121 | } 122 | 123 | #[inline(always)] 124 | pub fn x_height(&self) -> ffi::FT_Short { 125 | unsafe { (*self.raw).sxHeight } 126 | } 127 | } 128 | 129 | #[cfg(test)] 130 | mod tests { 131 | use std::path::PathBuf; 132 | 133 | use crate::library::Library; 134 | 135 | use super::*; 136 | 137 | /// Sanity-check reading basic line metrics from the OS/2 table. 138 | #[test] 139 | fn line_metrics() { 140 | let mut fira_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 141 | fira_path.push("examples/assets/FiraSans-Regular.ttf"); 142 | 143 | let library = Library::init().unwrap(); 144 | let mut face = library.new_face(fira_path, 0).unwrap(); 145 | let os2 = TrueTypeOS2Table::from_face(&mut face).unwrap(); 146 | 147 | assert_eq!(os2.s_typo_ascender(), 785); 148 | assert_eq!(os2.s_typo_descender(), -215); 149 | assert_eq!(os2.s_typo_line_gap(), 400); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /examples/assets/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Mozilla Foundation https://mozilla.org/ 2 | with Reserved Font Name Fira Sans. 3 | 4 | Copyright (c) 2014, Mozilla Foundation https://mozilla.org/ 5 | with Reserved Font Name Fira Mono. 6 | 7 | Copyright (c) 2014, Telefonica S.A. 8 | 9 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 10 | This license is copied below, and is also available with a FAQ at: 11 | http://scripts.sil.org/OFL 12 | 13 | 14 | ----------------------------------------------------------- 15 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 16 | ----------------------------------------------------------- 17 | 18 | PREAMBLE 19 | The goals of the Open Font License (OFL) are to stimulate worldwide 20 | development of collaborative font projects, to support the font creation 21 | efforts of academic and linguistic communities, and to provide a free and 22 | open framework in which fonts may be shared and improved in partnership 23 | with others. 24 | 25 | The OFL allows the licensed fonts to be used, studied, modified and 26 | redistributed freely as long as they are not sold by themselves. The 27 | fonts, including any derivative works, can be bundled, embedded, 28 | redistributed and/or sold with any software provided that any reserved 29 | names are not used by derivative works. The fonts and derivatives, 30 | however, cannot be released under any other type of license. The 31 | requirement for fonts to remain under this license does not apply 32 | to any document created using the fonts or their derivatives. 33 | 34 | DEFINITIONS 35 | "Font Software" refers to the set of files released by the Copyright 36 | Holder(s) under this license and clearly marked as such. This may 37 | include source files, build scripts and documentation. 38 | 39 | "Reserved Font Name" refers to any names specified as such after the 40 | copyright statement(s). 41 | 42 | "Original Version" refers to the collection of Font Software components as 43 | distributed by the Copyright Holder(s). 44 | 45 | "Modified Version" refers to any derivative made by adding to, deleting, 46 | or substituting -- in part or in whole -- any of the components of the 47 | Original Version, by changing formats or by porting the Font Software to a 48 | new environment. 49 | 50 | "Author" refers to any designer, engineer, programmer, technical 51 | writer or other person who contributed to the Font Software. 52 | 53 | PERMISSION & CONDITIONS 54 | Permission is hereby granted, free of charge, to any person obtaining 55 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 56 | redistribute, and sell modified and unmodified copies of the Font 57 | Software, subject to the following conditions: 58 | 59 | 1) Neither the Font Software nor any of its individual components, 60 | in Original or Modified Versions, may be sold by itself. 61 | 62 | 2) Original or Modified Versions of the Font Software may be bundled, 63 | redistributed and/or sold with any software, provided that each copy 64 | contains the above copyright notice and this license. These can be 65 | included either as stand-alone text files, human-readable headers or 66 | in the appropriate machine-readable metadata fields within text or 67 | binary files as long as those fields can be easily viewed by the user. 68 | 69 | 3) No Modified Version of the Font Software may use the Reserved Font 70 | Name(s) unless explicit written permission is granted by the corresponding 71 | Copyright Holder. This restriction only applies to the primary font name as 72 | presented to the users. 73 | 74 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 75 | Software shall not be used to promote, endorse or advertise any 76 | Modified Version, except to acknowledge the contribution(s) of the 77 | Copyright Holder(s) and the Author(s) or with their explicit written 78 | permission. 79 | 80 | 5) The Font Software, modified or unmodified, in part or in whole, 81 | must be distributed entirely under this license, and must not be 82 | distributed under any other license. The requirement for fonts to 83 | remain under this license does not apply to any document created 84 | using the Font Software. 85 | 86 | TERMINATION 87 | This license becomes null and void if any of the above conditions are 88 | not met. 89 | 90 | DISCLAIMER 91 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 92 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 93 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 94 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 95 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 96 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 97 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 98 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 99 | OTHER DEALINGS IN THE FONT SOFTWARE. 100 | -------------------------------------------------------------------------------- /src/bitmap.rs: -------------------------------------------------------------------------------- 1 | use crate::{ffi, Error, FtResult}; 2 | use std::slice; 3 | 4 | /// An enumeration type used to describe the format of pixels in a given bitmap. Note that 5 | /// additional formats may be added in the future. 6 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 7 | pub enum PixelMode { 8 | /// This value is reserved. 9 | None, 10 | 11 | /// A monochrome bitmap, using 1 bit per pixel. Note that pixels are 12 | /// stored in most-significant order (MSB), which means that the left-most 13 | /// pixel in a byte has value 128. 14 | Mono, 15 | 16 | /// An 8-bit bitmap, generally used to represent anti-aliased glyph images. 17 | /// Each pixel is stored in one byte. Note that the number of `gray` 18 | /// levels is stored in the `num_grays` field of the FT_Bitmap structure 19 | /// (it generally is 256). 20 | Gray, 21 | 22 | /// A 2-bit per pixel bitmap, used to represent embedded anti-aliased 23 | /// bitmaps in font files according to the OpenType specification. We 24 | /// haven't found a single font using this format, however. 25 | Gray2, 26 | 27 | /// A 4-bit per pixel bitmap, representing embedded anti-aliased bitmaps in 28 | /// font files according to the OpenType specification. We haven't found a 29 | /// single font using this format, however. 30 | Gray4, 31 | 32 | /// An 8-bit bitmap, representing RGB or BGR decimated glyph images used 33 | /// for display on LCD displays; the bitmap is three times wider than the 34 | /// original glyph image. See also FT_RENDER_MODE_LCD. 35 | Lcd, 36 | 37 | /// An 8-bit bitmap, representing RGB or BGR decimated glyph images used for 38 | /// display on rotated LCD displays; the bitmap is three times taller than 39 | /// the original glyph image. See also FT_RENDER_MODE_LCD_V. 40 | LcdV, 41 | 42 | /// An image with four 8-bit channels per pixel, representing a color image 43 | /// (such as emoticons) with alpha channel. For each pixel, the format is 44 | /// BGRA, which means, the blue channel comes first in memory. The color 45 | /// channels are pre-multiplied and in the sRGB colorspace. For example, 46 | /// full red at half-translucent opacity will be represented as 47 | /// `00,00,80,80`, not `00,00,FF,80`. See also FT_LOAD_COLOR. 48 | Bgra, 49 | } 50 | 51 | #[allow(missing_copy_implementations)] 52 | pub struct Bitmap { 53 | raw: *const ffi::FT_Bitmap, 54 | } 55 | 56 | impl Bitmap { 57 | pub unsafe fn from_raw(raw: *const ffi::FT_Bitmap) -> Self { 58 | Bitmap { raw } 59 | } 60 | 61 | /// A typeless pointer to the bitmap buffer. This value should be aligned 62 | /// on 32-bit boundaries in most cases. 63 | pub fn buffer(&self) -> &[u8] { 64 | let buffer_size = (self.pitch().abs() * self.rows()) as usize; 65 | if buffer_size > 0 { 66 | unsafe { slice::from_raw_parts((*self.raw).buffer, buffer_size) } 67 | } else { 68 | // When buffer_size is 0, the buffer pointer will be null. 69 | &[] 70 | } 71 | } 72 | 73 | /// The number of pixels in bitmap row. 74 | pub fn width(&self) -> i32 { 75 | unsafe { (*self.raw).width } 76 | } 77 | 78 | /// The number of bitmap rows. 79 | pub fn rows(&self) -> i32 { 80 | unsafe { (*self.raw).rows } 81 | } 82 | 83 | pub fn raw(&self) -> &ffi::FT_Bitmap { 84 | unsafe { &*self.raw } 85 | } 86 | 87 | /// The pixel mode, i.e., how pixel bits are stored. See `PixelMode` for 88 | /// possible values. 89 | pub fn pixel_mode(&self) -> FtResult { 90 | let pixel_mode = unsafe { (*self.raw).pixel_mode } as u32; 91 | 92 | Ok(match pixel_mode { 93 | ffi::FT_PIXEL_MODE_NONE => PixelMode::None, 94 | ffi::FT_PIXEL_MODE_MONO => PixelMode::Mono, 95 | ffi::FT_PIXEL_MODE_GRAY => PixelMode::Gray, 96 | ffi::FT_PIXEL_MODE_GRAY2 => PixelMode::Gray2, 97 | ffi::FT_PIXEL_MODE_GRAY4 => PixelMode::Gray4, 98 | ffi::FT_PIXEL_MODE_LCD => PixelMode::Lcd, 99 | ffi::FT_PIXEL_MODE_LCD_V => PixelMode::LcdV, 100 | ffi::FT_PIXEL_MODE_BGRA => PixelMode::Bgra, 101 | _ => return Err(Error::UnexpectedPixelMode), 102 | }) 103 | } 104 | 105 | /// The pitch's absolute value is the number of bytes taken by one bitmap row, including 106 | /// padding. However, the pitch is positive when the bitmap has a ‘down’ flow, and negative 107 | /// when it has an ‘up’ flow. In all cases, the pitch is an offset to add to a bitmap pointer 108 | /// in order to go down one row. 109 | /// 110 | /// Note that ‘padding’ means the alignment of a bitmap to a byte border, and FreeType 111 | /// functions normally align to the smallest possible integer value. 112 | /// For the B/W rasterizer, ‘pitch’ is always an even number. 113 | /// 114 | /// To change the pitch of a bitmap (say, to make it a multiple of 4), use FT_Bitmap_Convert. 115 | /// Alternatively, you might use callback functions to directly render to the application's 116 | /// surface; see the file ‘example2.cpp’ in the tutorial for a demonstration. 117 | pub fn pitch(&self) -> i32 { 118 | unsafe { (*self.raw).pitch } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/outline.rs: -------------------------------------------------------------------------------- 1 | use crate::{ffi, Vector}; 2 | use libc::{c_char, c_short}; 3 | use std::marker::PhantomData; 4 | use std::slice; 5 | 6 | #[derive(Copy, Clone)] 7 | pub enum Curve { 8 | Line(Vector), 9 | Bezier2(Vector, Vector), 10 | Bezier3(Vector, Vector, Vector), 11 | } 12 | 13 | pub struct Outline<'a> { 14 | raw: &'a ffi::FT_Outline, 15 | } 16 | 17 | impl<'a> Outline<'a> { 18 | pub unsafe fn from_raw(raw: &'a ffi::FT_Outline) -> Self { 19 | Outline { raw } 20 | } 21 | 22 | pub fn points(&self) -> &'a [Vector] { 23 | unsafe { slice::from_raw_parts(self.raw.points, self.raw.n_points as usize) } 24 | } 25 | 26 | pub fn tags(&self) -> &'a [c_char] { 27 | unsafe { slice::from_raw_parts(self.raw.tags, self.raw.n_points as usize) } 28 | } 29 | 30 | pub fn contours(&self) -> &'a [c_short] { 31 | unsafe { slice::from_raw_parts(self.raw.contours, self.raw.n_contours as usize) } 32 | } 33 | 34 | pub fn contours_iter(&self) -> ContourIterator<'a> { 35 | unsafe { ContourIterator::from_raw(self.raw) } 36 | } 37 | 38 | pub fn flags(&self) -> i32 { 39 | self.raw.flags 40 | } 41 | } 42 | 43 | const TAG_ONCURVE: c_char = 0x01; 44 | const TAG_BEZIER3: c_char = 0x02; 45 | 46 | pub struct CurveIterator<'a> { 47 | start_point: *const Vector, 48 | start_tag: *const c_char, 49 | idx: isize, 50 | length: isize, 51 | marker: PhantomData<&'a ()>, 52 | } 53 | 54 | impl<'a> CurveIterator<'a> { 55 | pub unsafe fn from_raw(outline: &'a ffi::FT_Outline, start_idx: isize, end_idx: isize) -> Self { 56 | CurveIterator { 57 | start_point: outline.points.offset(start_idx), 58 | start_tag: outline.tags.offset(start_idx), 59 | idx: 0, 60 | length: end_idx - start_idx + 1, 61 | marker: PhantomData, 62 | } 63 | } 64 | 65 | pub fn start(&self) -> &'a Vector { 66 | unsafe { &*self.start_point } 67 | } 68 | 69 | // Retrieves the point at offset i from the current point. Note that contours implicitly repeat their 70 | // first point at the end. 71 | unsafe fn pt(&self, i: isize) -> Vector { 72 | if self.idx + i < self.length { 73 | *self.start_point.offset(self.idx + i) 74 | } else { 75 | *self.start_point 76 | } 77 | } 78 | 79 | unsafe fn tg(&self, i: isize) -> c_char { 80 | if self.idx + i < self.length { 81 | *self.start_tag.offset(self.idx + i) 82 | } else { 83 | *self.start_tag 84 | } 85 | } 86 | } 87 | 88 | impl Iterator for CurveIterator<'_> { 89 | type Item = Curve; 90 | 91 | fn next(&mut self) -> Option { 92 | if self.idx >= self.length { 93 | None 94 | } else { 95 | unsafe { 96 | let tag1 = self.tg(1); 97 | 98 | let (shift, curve) = if (tag1 & TAG_ONCURVE) == TAG_ONCURVE { 99 | (1, Curve::Line(self.pt(1))) 100 | } else if (tag1 & TAG_BEZIER3) == TAG_BEZIER3 { 101 | (3, Curve::Bezier3(self.pt(1), self.pt(2), self.pt(3))) 102 | } else { 103 | // We are some kind of quadratic Bezier. 104 | // Quadratic Bezier curves have a special treatment in TTF outlines: 105 | // as an optimization, curves are often constructed from sequences 106 | // of off-curve control points. In this case, there are implied on-curve 107 | // points in between each pair of off-curve points. 108 | if (self.tg(2) & TAG_ONCURVE) == TAG_ONCURVE { 109 | (2, Curve::Bezier2(self.pt(1), self.pt(2))) 110 | } else { 111 | let pt = ffi::FT_Vector { 112 | x: (self.pt(1).x + self.pt(2).x) / 2, 113 | y: (self.pt(1).y + self.pt(2).y) / 2, 114 | }; 115 | 116 | (1, Curve::Bezier2(self.pt(1), pt)) 117 | } 118 | }; 119 | 120 | self.idx += shift; 121 | Some(curve) 122 | } 123 | } 124 | } 125 | } 126 | 127 | pub struct ContourIterator<'a> { 128 | outline: &'a ffi::FT_Outline, 129 | contour_start: c_short, 130 | contour_end_idx: *const c_short, 131 | last_end_idx: *const c_short, 132 | } 133 | 134 | impl<'a> ContourIterator<'a> { 135 | pub unsafe fn from_raw(outline: &'a ffi::FT_Outline) -> Self { 136 | ContourIterator { 137 | outline, 138 | contour_start: 0, 139 | contour_end_idx: outline.contours, 140 | last_end_idx: outline.contours.offset(outline.n_contours as isize - 1), 141 | } 142 | } 143 | } 144 | 145 | impl<'a> Iterator for ContourIterator<'a> { 146 | type Item = CurveIterator<'a>; 147 | 148 | fn next(&mut self) -> Option { 149 | if self.contour_end_idx.is_null() || self.contour_end_idx > self.last_end_idx { 150 | None 151 | } else { 152 | unsafe { 153 | let contour_end = *self.contour_end_idx; 154 | let curves = CurveIterator::from_raw( 155 | self.outline, 156 | self.contour_start as isize, 157 | contour_end as isize, 158 | ); 159 | self.contour_start = contour_end + 1; 160 | self.contour_end_idx = self.contour_end_idx.offset(1); 161 | 162 | Some(curves) 163 | } 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/library.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi; 2 | use crate::{Error, Face, FtResult, Stroker}; 3 | use libc::{self, c_long, c_void, size_t}; 4 | use std::borrow::Borrow; 5 | use std::ffi::{CString, OsStr}; 6 | use std::ptr::null_mut; 7 | use std::rc::Rc; 8 | 9 | extern "C" fn alloc_library(_memory: ffi::FT_Memory, size: c_long) -> *mut c_void { 10 | unsafe { libc::malloc(size as size_t) } 11 | } 12 | 13 | extern "C" fn free_library(_memory: ffi::FT_Memory, block: *mut c_void) { 14 | unsafe { libc::free(block) } 15 | } 16 | 17 | extern "C" fn realloc_library( 18 | _memory: ffi::FT_Memory, 19 | _cur_size: c_long, 20 | new_size: c_long, 21 | block: *mut c_void, 22 | ) -> *mut c_void { 23 | unsafe { libc::realloc(block, new_size as size_t) } 24 | } 25 | 26 | #[repr(u32)] 27 | #[derive(Copy, Clone)] 28 | pub enum LcdFilter { 29 | LcdFilterNone = ffi::FT_LCD_FILTER_NONE, 30 | LcdFilterDefault = ffi::FT_LCD_FILTER_DEFAULT, 31 | LcdFilterLight = ffi::FT_LCD_FILTER_LIGHT, 32 | LcdFilterLegacy = ffi::FT_LCD_FILTER_LEGACY, 33 | } 34 | 35 | static mut MEMORY: ffi::FT_MemoryRec = ffi::FT_MemoryRec { 36 | user: 0 as *mut c_void, 37 | alloc: alloc_library, 38 | free: free_library, 39 | realloc: realloc_library, 40 | }; 41 | 42 | pub struct Library { 43 | raw: ffi::FT_Library, 44 | } 45 | 46 | impl Library { 47 | /// This function is used to create a new FreeType library instance and add the default 48 | /// modules. It returns a struct encapsulating the freetype library. The library is correctly 49 | /// discarded when the struct is dropped. 50 | pub fn init() -> FtResult { 51 | let mut raw = null_mut(); 52 | 53 | let err = unsafe { ffi::FT_New_Library(&mut MEMORY, &mut raw) }; 54 | if err == ffi::FT_Err_Ok { 55 | unsafe { 56 | ffi::FT_Add_Default_Modules(raw); 57 | } 58 | Ok(Library { raw }) 59 | } else { 60 | Err(err.into()) 61 | } 62 | } 63 | 64 | /// Open a font file using its pathname. `face_index` should be 0 if there is only 1 font 65 | /// in the file. 66 | pub fn new_face

(&self, path: P, face_index: isize) -> FtResult 67 | where 68 | P: AsRef, 69 | { 70 | let mut face = null_mut(); 71 | 72 | let path = path 73 | .as_ref() 74 | .to_str() 75 | .and_then(|s| CString::new(s).ok()) 76 | .ok_or(Error::InvalidPath)?; 77 | let err = unsafe { 78 | ffi::FT_New_Face( 79 | self.raw, 80 | path.as_ptr() as *const _, 81 | face_index as ffi::FT_Long, 82 | &mut face, 83 | ) 84 | }; 85 | if err == ffi::FT_Err_Ok { 86 | Ok(unsafe { Face::from_raw(self.raw, face, None) }) 87 | } else { 88 | Err(err.into()) 89 | } 90 | } 91 | 92 | pub fn new_stroker(&self) -> FtResult { 93 | let mut stroker = null_mut(); 94 | 95 | let err = unsafe { ffi::FT_Stroker_New(self.raw, &mut stroker) }; 96 | 97 | if err == ffi::FT_Err_Ok { 98 | Ok(unsafe { Stroker::from_raw(self.raw, stroker) }) 99 | } else { 100 | Err(err.into()) 101 | } 102 | } 103 | 104 | /// Similar to `new_face`, but loads file data from a byte array in memory 105 | pub fn new_memory_face(&self, buffer: T, face_index: isize) -> FtResult 106 | where 107 | T: Into>>, 108 | { 109 | let mut face = null_mut(); 110 | let buffer = buffer.into(); 111 | 112 | let err = unsafe { 113 | ffi::FT_New_Memory_Face( 114 | self.raw, 115 | buffer.as_ptr(), 116 | buffer.len() as ffi::FT_Long, 117 | face_index as ffi::FT_Long, 118 | &mut face, 119 | ) 120 | }; 121 | if err == ffi::FT_Err_Ok { 122 | Ok(unsafe { Face::from_raw(self.raw, face, Some(buffer)) }) 123 | } else { 124 | Err(err.into()) 125 | } 126 | } 127 | 128 | /// Similar to `new_face`, but loads file data from a byte array in memory 129 | pub fn new_memory_face2(&self, buffer: T, face_index: isize) -> FtResult> 130 | where 131 | T: Borrow<[u8]>, 132 | { 133 | let mut face = null_mut(); 134 | let buf = buffer.borrow(); 135 | 136 | let err = unsafe { 137 | ffi::FT_New_Memory_Face( 138 | self.raw, 139 | buf.as_ptr(), 140 | buf.len() as ffi::FT_Long, 141 | face_index as ffi::FT_Long, 142 | &mut face, 143 | ) 144 | }; 145 | if err == ffi::FT_Err_Ok { 146 | Ok(unsafe { Face::from_raw(self.raw, face, Some(buffer)) }) 147 | } else { 148 | Err(err.into()) 149 | } 150 | } 151 | 152 | pub fn set_lcd_filter(&self, lcd_filter: LcdFilter) -> FtResult<()> { 153 | let err = unsafe { ffi::FT_Library_SetLcdFilter(self.raw, lcd_filter as u32) }; 154 | if err == ffi::FT_Err_Ok { 155 | Ok(()) 156 | } else { 157 | Err(err.into()) 158 | } 159 | } 160 | 161 | /// Get the underlying library object 162 | pub fn raw(&self) -> ffi::FT_Library { 163 | self.raw 164 | } 165 | } 166 | 167 | unsafe impl Send for Library {} 168 | unsafe impl Sync for Library {} 169 | 170 | impl Drop for Library { 171 | fn drop(&mut self) { 172 | let err = unsafe { ffi::FT_Done_Library(self.raw) }; 173 | if err != ffi::FT_Err_Ok { 174 | panic!("Failed to drop library") 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/glyph.rs: -------------------------------------------------------------------------------- 1 | use crate::{ffi, BBox, BitmapGlyph, FtResult, Matrix, RenderMode, Stroker, Vector}; 2 | use std::ptr::null_mut; 3 | 4 | /// Represents a retrieved glyph from the library 5 | /// 6 | /// Note that when this glyph is dropped, so is the library 7 | pub struct Glyph { 8 | library_raw: ffi::FT_Library, 9 | raw: ffi::FT_Glyph, 10 | } 11 | 12 | impl Glyph { 13 | /// Create a freetype-rs glyph object from c constituent parts 14 | pub unsafe fn from_raw(library_raw: ffi::FT_Library, raw: ffi::FT_Glyph) -> Self { 15 | ffi::FT_Reference_Library(library_raw); 16 | Glyph { library_raw, raw } 17 | } 18 | 19 | /// Transform a glyph image if its format is scalable. 20 | pub fn transform(&self, mut matrix: Option, mut delta: Option) -> FtResult<()> { 21 | let mut p_matrix = null_mut(); 22 | let mut p_delta = null_mut(); 23 | 24 | if let Some(ref mut m) = matrix { 25 | p_matrix = m as *mut Matrix; 26 | } 27 | if let Some(ref mut d) = delta { 28 | p_delta = d as *mut Vector; 29 | } 30 | let err = unsafe { ffi::FT_Glyph_Transform(self.raw, p_matrix, p_delta) }; 31 | if err == ffi::FT_Err_Ok { 32 | Ok(()) 33 | } else { 34 | Err(err.into()) 35 | } 36 | } 37 | 38 | /// Return a glyph's ‘control box’. The control box encloses all the outline's points, 39 | /// including Bézier control points. Though it coincides with the exact bounding box for most 40 | /// glyphs, it can be slightly larger in some situations (like when rotating an outline that 41 | /// contains Bézier outside arcs). 42 | /// 43 | /// Computing the control box is very fast, while getting the bounding box can take much more 44 | /// time as it needs to walk over all segments and arcs in the outline. To get the latter, you 45 | /// can use the ‘ftbbox’ component, which is dedicated to this single task. 46 | pub fn get_cbox(&self, bbox_mode: ffi::FT_Glyph_BBox_Mode) -> BBox { 47 | let mut acbox = ffi::FT_BBox { 48 | xMin: 0, 49 | yMin: 0, 50 | xMax: 0, 51 | yMax: 0, 52 | }; 53 | unsafe { ffi::FT_Glyph_Get_CBox(self.raw, bbox_mode, &mut acbox) }; 54 | acbox 55 | } 56 | 57 | /// Convert a given glyph object to a bitmap glyph object. 58 | pub fn to_bitmap( 59 | &self, 60 | render_mode: RenderMode, 61 | mut origin: Option, 62 | ) -> FtResult { 63 | let mut the_glyph = self.raw; 64 | let mut p_origin = null_mut(); 65 | 66 | if let Some(ref mut o) = origin { 67 | p_origin = o as *mut Vector; 68 | } 69 | let err = 70 | unsafe { ffi::FT_Glyph_To_Bitmap(&mut the_glyph, render_mode as u32, p_origin, 0) }; 71 | if err == ffi::FT_Err_Ok { 72 | Ok( 73 | unsafe { 74 | BitmapGlyph::from_raw(self.library_raw, the_glyph as ffi::FT_BitmapGlyph) 75 | }, 76 | ) 77 | } else { 78 | Err(err.into()) 79 | } 80 | } 81 | 82 | pub fn stroke(&self, stroker: &Stroker) -> FtResult { 83 | let mut the_glyph = self.raw; 84 | 85 | let err = unsafe { 86 | ffi::FT_Glyph_Stroke(&mut the_glyph, stroker.raw_stroker(), false as ffi::FT_Bool) 87 | }; 88 | 89 | if err == ffi::FT_Err_Ok { 90 | Ok(unsafe { Glyph::from_raw(self.library_raw, the_glyph) }) 91 | } else { 92 | Err(err.into()) 93 | } 94 | } 95 | 96 | pub fn stroke_border(&self, stroker: &Stroker, inside: bool) -> FtResult { 97 | let mut the_glyph = self.raw; 98 | 99 | let err = unsafe { 100 | ffi::FT_Glyph_StrokeBorder( 101 | &mut the_glyph, 102 | stroker.raw_stroker(), 103 | inside as ffi::FT_Bool, 104 | false as ffi::FT_Bool, 105 | ) 106 | }; 107 | 108 | if err == ffi::FT_Err_Ok { 109 | Ok(unsafe { Glyph::from_raw(self.library_raw, the_glyph) }) 110 | } else { 111 | Err(err.into()) 112 | } 113 | } 114 | 115 | pub fn advance_x(&self) -> isize { 116 | unsafe { (*self.raw).advance.x as isize } 117 | } 118 | 119 | pub fn advance_y(&self) -> isize { 120 | unsafe { (*self.raw).advance.y as isize } 121 | } 122 | 123 | /// An enumeration type used to describe the format of a given glyph image. Note that this 124 | /// version of FreeType only supports two image formats, even though future font drivers will 125 | /// be able to register their own format. 126 | #[inline(always)] 127 | pub fn format(&self) -> ffi::FT_Glyph_Format { 128 | unsafe { (*self.raw).format } 129 | } 130 | 131 | /// Get the underlying c glyph struct (The system actually calls this a GlyphRec because it can 132 | /// be a different struct in different circumstances) 133 | #[inline(always)] 134 | pub fn raw(&self) -> &ffi::FT_GlyphRec { 135 | unsafe { &*self.raw } 136 | } 137 | } 138 | 139 | impl Clone for Glyph { 140 | fn clone(&self) -> Self { 141 | let mut target = null_mut(); 142 | 143 | let err = unsafe { ffi::FT_Glyph_Copy(self.raw, &mut target) }; 144 | if err == ffi::FT_Err_Ok { 145 | unsafe { Glyph::from_raw(self.library_raw, target) } 146 | } else { 147 | panic!("Failed to copy glyph") 148 | } 149 | } 150 | } 151 | 152 | impl Drop for Glyph { 153 | fn drop(&mut self) { 154 | let err = unsafe { 155 | ffi::FT_Done_Glyph(self.raw); 156 | ffi::FT_Done_Library(self.library_raw) 157 | }; 158 | if err != ffi::FT_Err_Ok { 159 | panic!("Failed to drop library") 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/glyph_slot.rs: -------------------------------------------------------------------------------- 1 | use crate::{ffi, Bitmap, FtResult, Glyph, GlyphMetrics, Outline, RenderMode, Vector}; 2 | use std::ptr::null_mut; 3 | 4 | /// A description of a given subglyph returned by `GlyphSlot::get_subglyph_info` 5 | /// function. 6 | #[derive(Copy, Clone)] 7 | pub struct SubGlyphInfo { 8 | /// The glyph index of the subglyph. 9 | pub index: i32, 10 | /// The subglyph flags, see FT_SUBGLYPH_FLAG_XXX. 11 | pub flags: u32, 12 | /// The subglyph's first argument (if any). 13 | pub arg1: i32, 14 | /// The subglyph's second argument (if any). 15 | pub arg2: i32, 16 | /// The subglyph transformation (if any). 17 | pub transform: ffi::FT_Matrix, 18 | } 19 | 20 | impl Default for SubGlyphInfo { 21 | fn default() -> Self { 22 | SubGlyphInfo { 23 | index: 0, 24 | flags: 0, 25 | arg1: 0, 26 | arg2: 0, 27 | transform: ffi::FT_Matrix { 28 | xx: 0, 29 | xy: 0, 30 | yx: 0, 31 | yy: 0, 32 | }, 33 | } 34 | } 35 | } 36 | 37 | /// A struct encapsulating the space for a glyph within a `Library` 38 | #[derive(Copy, Clone, Eq, PartialEq, Hash)] 39 | pub struct GlyphSlot { 40 | library_raw: ffi::FT_Library, 41 | raw: ffi::FT_GlyphSlot, 42 | } 43 | 44 | impl GlyphSlot { 45 | /// Create a `GlyphSlot` from its constituent C parts 46 | pub unsafe fn from_raw(library_raw: ffi::FT_Library, raw: ffi::FT_GlyphSlot) -> Self { 47 | GlyphSlot { library_raw, raw } 48 | } 49 | 50 | /// Convert a given glyph image to a bitmap. It does so by inspecting the glyph image format, 51 | /// finding the relevant renderer, and invoking it. 52 | pub fn render_glyph(&self, render_mode: RenderMode) -> FtResult<()> { 53 | let err = unsafe { ffi::FT_Render_Glyph(self.raw, render_mode as u32) }; 54 | if err == ffi::FT_Err_Ok { 55 | Ok(()) 56 | } else { 57 | Err(err.into()) 58 | } 59 | } 60 | 61 | /// Retrieve a description of a given subglyph. Only use it if the glyph's format is 62 | /// FT_GLYPH_FORMAT_COMPOSITE; an error is returned otherwise. 63 | pub fn get_subglyph_info(&self, sub_index: u32) -> FtResult { 64 | let mut info = SubGlyphInfo::default(); 65 | let err = unsafe { 66 | ffi::FT_Get_SubGlyph_Info( 67 | self.raw, 68 | sub_index, 69 | &mut info.index, 70 | &mut info.flags, 71 | &mut info.arg1, 72 | &mut info.arg2, 73 | &mut info.transform, 74 | ) 75 | }; 76 | if err == ffi::FT_Err_Ok { 77 | Ok(info) 78 | } else { 79 | Err(err.into()) 80 | } 81 | } 82 | 83 | /// Returns a glyph object, that is similar to a `GlyphSlot` but managed outside of the library 84 | pub fn get_glyph(&self) -> FtResult { 85 | let mut aglyph = null_mut(); 86 | 87 | let err = unsafe { ffi::FT_Get_Glyph(self.raw, &mut aglyph) }; 88 | if err == ffi::FT_Err_Ok { 89 | Ok(unsafe { Glyph::from_raw(self.library_raw, aglyph) }) 90 | } else { 91 | Err(err.into()) 92 | } 93 | } 94 | 95 | /// In freetype, the `Outline` object is a scalable glyph. This method unpacks a glyph into 96 | /// this object, or returns `None` if the glyph has no `outline` 97 | pub fn outline(&self) -> Option { 98 | let outline = unsafe { &(*self.raw).outline }; 99 | let format = unsafe { (*self.raw).format }; 100 | 101 | if format == ffi::FT_GLYPH_FORMAT_OUTLINE { 102 | let outline = unsafe { Outline::from_raw(outline) }; 103 | Some(outline) 104 | } else { 105 | None 106 | } 107 | } 108 | 109 | /// This field is used as a bitmap descriptor when the slot format is FT_GLYPH_FORMAT_BITMAP. 110 | /// Note that the address and content of the bitmap buffer can change between calls of 111 | /// FT_Load_Glyph and a few other functions. 112 | #[inline(always)] 113 | pub fn bitmap(&self) -> Bitmap { 114 | unsafe { Bitmap::from_raw(&(*self.raw).bitmap) } 115 | } 116 | 117 | /// The bitmap's left bearing expressed in integer pixels. Only valid if the format is 118 | /// FT_GLYPH_FORMAT_BITMAP, this is, if the glyph slot contains a bitmap. 119 | #[inline(always)] 120 | pub fn bitmap_left(&self) -> i32 { 121 | unsafe { (*self.raw).bitmap_left } 122 | } 123 | 124 | /// The bitmap's top bearing expressed in integer pixels. Remember that this is the distance 125 | /// from the baseline to the top-most glyph scanline, upwards y coordinates being positive. 126 | #[inline(always)] 127 | pub fn bitmap_top(&self) -> i32 { 128 | unsafe { (*self.raw).bitmap_top } 129 | } 130 | 131 | /// This shorthand is, depending on FT_LOAD_IGNORE_TRANSFORM, the transformed (hinted) advance 132 | /// width for the glyph, in 26.6 fractional pixel format. As specified with 133 | /// FT_LOAD_VERTICAL_LAYOUT, it uses either the ‘horiAdvance’ or the ‘vertAdvance’ value of 134 | /// ‘metrics’ field. 135 | #[inline(always)] 136 | pub fn advance(&self) -> Vector { 137 | unsafe { (*self.raw).advance } 138 | } 139 | 140 | /// The advance width of the unhinted glyph. Its value is expressed in 16.16 fractional pixels, 141 | /// unless FT_LOAD_LINEAR_DESIGN is set when loading the glyph. This field can be important to 142 | /// perform correct WYSIWYG layout. Only relevant for outline glyphs. 143 | #[inline(always)] 144 | pub fn linear_hori_advance(&self) -> ffi::FT_Fixed { 145 | unsafe { (*self.raw).linearHoriAdvance } 146 | } 147 | 148 | /// The advance height of the unhinted glyph. Its value is expressed in 16.16 fractional 149 | /// pixels, unless FT_LOAD_LINEAR_DESIGN is set when loading the glyph. This field can be 150 | /// important to perform correct WYSIWYG layout. Only relevant for outline glyphs. 151 | #[inline(always)] 152 | pub fn linear_vert_advance(&self) -> ffi::FT_Fixed { 153 | unsafe { (*self.raw).linearVertAdvance } 154 | } 155 | 156 | /// The metrics of the last loaded glyph in the slot. The returned values depend on the last 157 | /// load flags (see the FT_Load_Glyph API function) and can be expressed either in 26.6 158 | /// fractional pixels or font units. 159 | #[inline(always)] 160 | pub fn metrics(&self) -> GlyphMetrics { 161 | unsafe { (*self.raw).metrics } 162 | } 163 | 164 | /// Get a pointer to the underlying c struct 165 | #[inline(always)] 166 | pub fn raw(&self) -> &ffi::FT_GlyphSlotRec { 167 | unsafe { &*self.raw } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/face.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{CStr, CString}; 2 | use std::fmt; 3 | use std::num::NonZeroU32; 4 | use std::rc::Rc; 5 | 6 | use crate::charmap::CharMap; 7 | use crate::{ffi, FtResult, GlyphSlot, Matrix, Vector}; 8 | 9 | #[repr(u32)] 10 | #[derive(Copy, Clone)] 11 | pub enum KerningMode { 12 | KerningDefault = ffi::FT_KERNING_DEFAULT, 13 | KerningUnfitted = ffi::FT_KERNING_UNFITTED, 14 | KerningUnscaled = ffi::FT_KERNING_UNSCALED, 15 | } 16 | 17 | bitflags::bitflags! { 18 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 19 | pub struct LoadFlag: i32 { 20 | const DEFAULT = crate::ffi::FT_LOAD_DEFAULT; 21 | const NO_SCALE = crate::ffi::FT_LOAD_NO_SCALE; 22 | const NO_HINTING = crate::ffi::FT_LOAD_NO_HINTING; 23 | const RENDER = crate::ffi::FT_LOAD_RENDER; 24 | const NO_BITMAP = crate::ffi::FT_LOAD_NO_BITMAP; 25 | const VERTICAL_LAYOUT = crate::ffi::FT_LOAD_VERTICAL_LAYOUT; 26 | const FORCE_AUTOHINT = crate::ffi::FT_LOAD_FORCE_AUTOHINT; 27 | const CROP_BITMAP = crate::ffi::FT_LOAD_CROP_BITMAP; 28 | const PEDANTIC = crate::ffi::FT_LOAD_PEDANTIC; 29 | const IGNORE_GLOBAL_ADVANCE_WITH = crate::ffi::FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH; 30 | const NO_RECURSE = crate::ffi::FT_LOAD_NO_RECURSE; 31 | const IGNORE_TRANSFORM = crate::ffi::FT_LOAD_IGNORE_TRANSFORM; 32 | const MONOCHROME = crate::ffi::FT_LOAD_MONOCHROME; 33 | const LINEAR_DESIGN = crate::ffi::FT_LOAD_LINEAR_DESIGN; 34 | const NO_AUTOHINT = crate::ffi::FT_LOAD_NO_AUTOHINT; 35 | const TARGET_NORMAL = crate::ffi::FT_LOAD_TARGET_NORMAL; 36 | const TARGET_LIGHT = crate::ffi::FT_LOAD_TARGET_LIGHT; 37 | const TARGET_MONO = crate::ffi::FT_LOAD_TARGET_MONO; 38 | const TARGET_LCD = crate::ffi::FT_LOAD_TARGET_LCD; 39 | const TARGET_LCD_V = crate::ffi::FT_LOAD_TARGET_LCD_V; 40 | const COLOR = crate::ffi::FT_LOAD_COLOR; 41 | } 42 | } 43 | 44 | bitflags::bitflags! { 45 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 46 | pub struct StyleFlag: ffi::FT_Long { 47 | const BOLD = crate::ffi::FT_STYLE_FLAG_BOLD; 48 | const ITALIC = crate::ffi::FT_STYLE_FLAG_ITALIC; 49 | } 50 | } 51 | 52 | pub struct CharIterator<'a, BYTES> { 53 | started: bool, 54 | face: &'a Face, 55 | gindex: ffi::FT_UInt, 56 | charcode: ffi::FT_ULong, 57 | } 58 | 59 | impl<'a, BYTES> CharIterator<'a, BYTES> { 60 | fn new(face: &'a Face) -> Self { 61 | CharIterator { 62 | started: false, 63 | face, 64 | gindex: 0, 65 | charcode: 0, 66 | } 67 | } 68 | } 69 | 70 | impl Iterator for CharIterator<'_, BYTES> { 71 | type Item = (usize, NonZeroU32); 72 | 73 | fn next(&mut self) -> Option { 74 | // Implementing per https://freetype.org/freetype2/docs/reference/ft2-character_mapping.html#ft_get_first_char 75 | // FT_UInt gindex; 76 | // FT_ULong charcode = FT_Get_First_Char( face, &gindex ); 77 | // while ( gindex != 0 ) { 78 | // ... do something with (charcode,gindex) pair ... 79 | // charcode = FT_Get_Next_Char( face, charcode, &gindex ); 80 | // } 81 | 82 | if self.started { 83 | self.charcode = 84 | unsafe { ffi::FT_Get_Next_Char(self.face.raw, self.charcode, &mut self.gindex) }; 85 | } else { 86 | self.started = true; 87 | self.charcode = unsafe { ffi::FT_Get_First_Char(self.face.raw, &mut self.gindex) }; 88 | } 89 | 90 | if self.gindex == 0 { 91 | None 92 | } else { 93 | NonZeroU32::new(self.gindex).map(|gindex| (self.charcode as usize, gindex)) 94 | } 95 | } 96 | 97 | // TODO: implement size_hint 98 | } 99 | 100 | #[derive(Eq, PartialEq, Hash)] 101 | pub struct Face>> { 102 | library_raw: ffi::FT_Library, 103 | raw: ffi::FT_Face, 104 | glyph: GlyphSlot, 105 | bytes: Option, 106 | } 107 | 108 | impl Clone for Face { 109 | fn clone(&self) -> Self { 110 | let err = unsafe { ffi::FT_Reference_Library(self.library_raw) }; 111 | if err != ffi::FT_Err_Ok { 112 | panic!("Failed to reference library"); 113 | } 114 | let err = unsafe { ffi::FT_Reference_Face(self.raw) }; 115 | if err != ffi::FT_Err_Ok { 116 | panic!("Failed to reference face"); 117 | } 118 | Face { 119 | library_raw: self.library_raw, 120 | raw: self.raw, 121 | glyph: self.glyph, 122 | bytes: self.bytes.clone(), 123 | } 124 | } 125 | } 126 | 127 | impl Face { 128 | pub unsafe fn from_raw( 129 | library_raw: ffi::FT_Library, 130 | raw: ffi::FT_Face, 131 | bytes: Option, 132 | ) -> Self { 133 | ffi::FT_Reference_Library(library_raw); 134 | Face { 135 | library_raw, 136 | raw, 137 | glyph: GlyphSlot::from_raw(library_raw, (*raw).glyph), 138 | bytes, 139 | } 140 | } 141 | 142 | pub fn attach_file(&self, filepathname: &str) -> FtResult<()> { 143 | let err = unsafe { ffi::FT_Attach_File(self.raw, filepathname.as_ptr() as *const _) }; 144 | if err == ffi::FT_Err_Ok { 145 | Ok(()) 146 | } else { 147 | Err(err.into()) 148 | } 149 | } 150 | 151 | pub fn reference(&self) -> FtResult<()> { 152 | let err = unsafe { ffi::FT_Reference_Face(self.raw) }; 153 | if err == ffi::FT_Err_Ok { 154 | Ok(()) 155 | } else { 156 | Err(err.into()) 157 | } 158 | } 159 | 160 | pub fn set_char_size( 161 | &self, 162 | char_width: isize, 163 | char_height: isize, 164 | horz_resolution: u32, 165 | vert_resolution: u32, 166 | ) -> FtResult<()> { 167 | let err = unsafe { 168 | ffi::FT_Set_Char_Size( 169 | self.raw, 170 | char_width as ffi::FT_F26Dot6, 171 | char_height as ffi::FT_F26Dot6, 172 | horz_resolution, 173 | vert_resolution, 174 | ) 175 | }; 176 | if err == ffi::FT_Err_Ok { 177 | Ok(()) 178 | } else { 179 | Err(err.into()) 180 | } 181 | } 182 | 183 | pub fn select_size(&self, strike_index: i32) -> FtResult<()> { 184 | let err = unsafe { ffi::FT_Select_Size(self.raw, strike_index) }; 185 | if err == ffi::FT_Err_Ok { 186 | Ok(()) 187 | } else { 188 | Err(err.into()) 189 | } 190 | } 191 | 192 | pub fn set_pixel_sizes(&self, pixel_width: u32, pixel_height: u32) -> FtResult<()> { 193 | let err = unsafe { ffi::FT_Set_Pixel_Sizes(self.raw, pixel_width, pixel_height) }; 194 | if err == ffi::FT_Err_Ok { 195 | Ok(()) 196 | } else { 197 | Err(err.into()) 198 | } 199 | } 200 | 201 | pub fn load_glyph(&self, glyph_index: u32, load_flags: LoadFlag) -> FtResult<()> { 202 | let err = unsafe { ffi::FT_Load_Glyph(self.raw, glyph_index, load_flags.bits()) }; 203 | if err == ffi::FT_Err_Ok { 204 | Ok(()) 205 | } else { 206 | Err(err.into()) 207 | } 208 | } 209 | 210 | pub fn load_char(&self, char_code: usize, load_flags: LoadFlag) -> FtResult<()> { 211 | let err = 212 | unsafe { ffi::FT_Load_Char(self.raw, char_code as ffi::FT_ULong, load_flags.bits()) }; 213 | if err == ffi::FT_Err_Ok { 214 | Ok(()) 215 | } else { 216 | Err(err.into()) 217 | } 218 | } 219 | 220 | pub fn set_transform(&self, matrix: &mut Matrix, delta: &mut Vector) { 221 | unsafe { 222 | ffi::FT_Set_Transform(self.raw, matrix, delta); 223 | } 224 | } 225 | 226 | pub fn get_char_index(&self, charcode: usize) -> Option { 227 | let res = unsafe { ffi::FT_Get_Char_Index(self.raw, charcode as ffi::FT_ULong) }; 228 | if res == 0 { 229 | None 230 | } else { 231 | Some(res) 232 | } 233 | } 234 | 235 | pub fn get_name_index(&self, glyph_name: &str) -> Option { 236 | if !unsafe { ffi::FT_HAS_GLYPH_NAMES(self.raw) } { 237 | return None; 238 | } 239 | 240 | match CString::new(glyph_name) { 241 | Ok(name) => { 242 | Some(unsafe { ffi::FT_Get_Name_Index(self.raw, name.as_ptr() as *const _) }) 243 | } 244 | Err(_) => None, 245 | } 246 | } 247 | 248 | pub fn chars(&self) -> CharIterator<'_, BYTES> { 249 | CharIterator::new(self) 250 | } 251 | 252 | pub fn get_kerning( 253 | &self, 254 | left_char_index: u32, 255 | right_char_index: u32, 256 | kern_mode: KerningMode, 257 | ) -> FtResult { 258 | let mut vec = Vector { x: 0, y: 0 }; 259 | 260 | let err = unsafe { 261 | ffi::FT_Get_Kerning( 262 | self.raw, 263 | left_char_index, 264 | right_char_index, 265 | kern_mode as u32, 266 | &mut vec, 267 | ) 268 | }; 269 | if err == ffi::FT_Err_Ok { 270 | Ok(vec) 271 | } else { 272 | Err(err.into()) 273 | } 274 | } 275 | 276 | pub fn get_charmap(&self, charmap_index: isize) -> CharMap { 277 | let charmap = unsafe { *self.raw().charmaps.offset(charmap_index) }; 278 | CharMap::new(charmap) 279 | } 280 | 281 | pub fn set_charmap(&self, charmap: &CharMap) -> FtResult<()> { 282 | let err = unsafe { ffi::FT_Set_Charmap(self.raw, charmap.raw()) }; 283 | if err == ffi::FT_Err_Ok { 284 | Ok(()) 285 | } else { 286 | Err(err.into()) 287 | } 288 | } 289 | 290 | // According to FreeType doc, each time you load a new glyph image, 291 | // the previous one is erased from the glyph slot. 292 | #[inline(always)] 293 | pub fn glyph(&self) -> &GlyphSlot { 294 | &self.glyph 295 | } 296 | 297 | #[inline(always)] 298 | pub fn has_horizontal(&self) -> bool { 299 | unsafe { ffi::FT_HAS_HORIZONTAL(self.raw) } 300 | } 301 | 302 | #[inline(always)] 303 | pub fn has_vertical(&self) -> bool { 304 | unsafe { ffi::FT_HAS_VERTICAL(self.raw) } 305 | } 306 | 307 | #[inline(always)] 308 | pub fn has_kerning(&self) -> bool { 309 | unsafe { ffi::FT_HAS_KERNING(self.raw) } 310 | } 311 | 312 | #[inline(always)] 313 | pub fn is_scalable(&self) -> bool { 314 | unsafe { ffi::FT_IS_SCALABLE(self.raw) } 315 | } 316 | 317 | #[inline(always)] 318 | pub fn is_sfnt(&self) -> bool { 319 | unsafe { ffi::FT_IS_SFNT(self.raw) } 320 | } 321 | 322 | #[inline(always)] 323 | pub fn is_fixed_width(&self) -> bool { 324 | unsafe { ffi::FT_IS_FIXED_WIDTH(self.raw) } 325 | } 326 | 327 | #[inline(always)] 328 | pub fn has_fixed_sizes(&self) -> bool { 329 | unsafe { ffi::FT_HAS_FIXED_SIZES(self.raw) } 330 | } 331 | 332 | #[inline(always)] 333 | pub fn has_glyph_names(&self) -> bool { 334 | unsafe { ffi::FT_HAS_GLYPH_NAMES(self.raw) } 335 | } 336 | 337 | #[inline(always)] 338 | pub fn is_cid_keyed(&self) -> bool { 339 | unsafe { ffi::FT_IS_CID_KEYED(self.raw) } 340 | } 341 | 342 | #[inline(always)] 343 | pub fn is_tricky(&self) -> bool { 344 | unsafe { ffi::FT_IS_TRICKY(self.raw) } 345 | } 346 | 347 | #[inline(always)] 348 | pub fn has_color(&self) -> bool { 349 | unsafe { ffi::FT_HAS_COLOR(self.raw) } 350 | } 351 | 352 | #[inline(always)] 353 | pub fn raw(&self) -> &ffi::FT_FaceRec { 354 | unsafe { &*self.raw } 355 | } 356 | 357 | #[inline(always)] 358 | pub fn raw_mut(&mut self) -> &mut ffi::FT_FaceRec { 359 | unsafe { &mut *self.raw } 360 | } 361 | 362 | #[inline(always)] 363 | pub fn ascender(&self) -> ffi::FT_Short { 364 | unsafe { (*self.raw).ascender } 365 | } 366 | 367 | #[inline(always)] 368 | pub fn num_charmaps(&self) -> i32 { 369 | unsafe { (*self.raw).num_charmaps } 370 | } 371 | 372 | #[inline(always)] 373 | pub fn descender(&self) -> ffi::FT_Short { 374 | unsafe { (*self.raw).descender } 375 | } 376 | 377 | #[inline(always)] 378 | pub fn em_size(&self) -> ffi::FT_Short { 379 | unsafe { (*self.raw).units_per_EM as i16 } 380 | } 381 | 382 | #[inline(always)] 383 | pub fn height(&self) -> ffi::FT_Short { 384 | unsafe { (*self.raw).height } 385 | } 386 | 387 | #[inline(always)] 388 | pub fn max_advance_width(&self) -> ffi::FT_Short { 389 | unsafe { (*self.raw).max_advance_width } 390 | } 391 | 392 | #[inline(always)] 393 | pub fn max_advance_height(&self) -> ffi::FT_Short { 394 | unsafe { (*self.raw).max_advance_height } 395 | } 396 | 397 | #[inline(always)] 398 | pub fn underline_position(&self) -> ffi::FT_Short { 399 | unsafe { (*self.raw).underline_position } 400 | } 401 | 402 | #[inline(always)] 403 | pub fn underline_thickness(&self) -> ffi::FT_Short { 404 | unsafe { (*self.raw).underline_thickness } 405 | } 406 | 407 | #[inline(always)] 408 | pub fn num_faces(&self) -> ffi::FT_Short { 409 | unsafe { (*self.raw).num_faces as i16 } 410 | } 411 | 412 | #[inline(always)] 413 | pub fn num_glyphs(&self) -> ffi::FT_Long { 414 | unsafe { (*self.raw).num_glyphs } 415 | } 416 | 417 | pub fn family_name(&self) -> Option { 418 | let family_name = unsafe { (*self.raw).family_name }; 419 | 420 | if family_name.is_null() { 421 | None 422 | } else { 423 | let family_name = 424 | unsafe { CStr::from_ptr(family_name as *const _).to_bytes().to_vec() }; 425 | String::from_utf8(family_name).ok() 426 | } 427 | } 428 | 429 | pub fn style_name(&self) -> Option { 430 | let style_name = unsafe { (*self.raw).style_name }; 431 | 432 | if style_name.is_null() { 433 | None 434 | } else { 435 | let style_name = unsafe { CStr::from_ptr(style_name as *const _).to_bytes().to_vec() }; 436 | String::from_utf8(style_name).ok() 437 | } 438 | } 439 | 440 | pub fn style_flags(&self) -> StyleFlag { 441 | let style_flags = unsafe { (*self.raw).style_flags }; 442 | StyleFlag::from_bits_truncate(style_flags) 443 | } 444 | 445 | pub fn size_metrics(&self) -> Option { 446 | if self.raw.is_null() { 447 | None 448 | } else { 449 | let size = unsafe { (*self.raw).size }; 450 | if size.is_null() { 451 | None 452 | } else { 453 | Some(unsafe { (*size).metrics }) 454 | } 455 | } 456 | } 457 | 458 | pub fn postscript_name(&self) -> Option { 459 | let face_name = unsafe { ffi::FT_Get_Postscript_Name(self.raw) }; 460 | if face_name.is_null() { 461 | None 462 | } else { 463 | let face_name = unsafe { CStr::from_ptr(face_name as *const _).to_bytes().to_vec() }; 464 | String::from_utf8(face_name).ok() 465 | } 466 | } 467 | } 468 | 469 | impl fmt::Debug for Face { 470 | fn fmt(&self, form: &mut fmt::Formatter) -> fmt::Result { 471 | let name = self.style_name().unwrap_or("[unknown name]".to_owned()); 472 | form.write_str("Font Face: ")?; 473 | form.write_str(&name[..]) 474 | } 475 | } 476 | 477 | impl Drop for Face { 478 | fn drop(&mut self) { 479 | let err = unsafe { ffi::FT_Done_Face(self.raw) }; 480 | if err != ffi::FT_Err_Ok { 481 | panic!("Failed to drop face"); 482 | } 483 | let err = unsafe { ffi::FT_Done_Library(self.library_raw) }; 484 | if err != ffi::FT_Err_Ok { 485 | panic!("Failed to drop library") 486 | } 487 | self.bytes = None; 488 | } 489 | } 490 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi; 2 | use std::error; 3 | use std::fmt; 4 | 5 | pub type FtResult = Result; 6 | 7 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] 8 | #[repr(i32)] 9 | pub enum Error { 10 | Ok = ffi::FT_Err_Ok, 11 | CannotOpenResource = ffi::FT_Err_Cannot_Open_Resource, 12 | UnknownFileFormat = ffi::FT_Err_Unknown_File_Format, 13 | InvalidFileFormat = ffi::FT_Err_Invalid_File_Format, 14 | InvalidVersion = ffi::FT_Err_Invalid_Version, 15 | LowerModuleVersion = ffi::FT_Err_Lower_Module_Version, 16 | InvalidArgument = ffi::FT_Err_Invalid_Argument, 17 | UnimplementedFeature = ffi::FT_Err_Unimplemented_Feature, 18 | InvalidTable = ffi::FT_Err_Invalid_Table, 19 | InvalidOffset = ffi::FT_Err_Invalid_Offset, 20 | ArrayTooLarge = ffi::FT_Err_Array_Too_Large, 21 | MissingModule = ffi::FT_Err_Missing_Module, 22 | MissingProperty = ffi::FT_Err_Missing_Property, 23 | InvalidGlyphIndex = ffi::FT_Err_Invalid_Glyph_Index, 24 | InvalidCharacterCode = ffi::FT_Err_Invalid_Character_Code, 25 | InvalidGlyphFormat = ffi::FT_Err_Invalid_Glyph_Format, 26 | CannotRenderGlyph = ffi::FT_Err_Cannot_Render_Glyph, 27 | InvalidOutline = ffi::FT_Err_Invalid_Outline, 28 | InvalidComposite = ffi::FT_Err_Invalid_Composite, 29 | TooManyHints = ffi::FT_Err_Too_Many_Hints, 30 | InvalidPixelSize = ffi::FT_Err_Invalid_Pixel_Size, 31 | InvalidHandle = ffi::FT_Err_Invalid_Handle, 32 | InvalidLibraryHandle = ffi::FT_Err_Invalid_Library_Handle, 33 | InvalidDriverHandle = ffi::FT_Err_Invalid_Driver_Handle, 34 | InvalidFaceHandle = ffi::FT_Err_Invalid_Face_Handle, 35 | InvalidSizeHandle = ffi::FT_Err_Invalid_Size_Handle, 36 | InvalidSlotHandle = ffi::FT_Err_Invalid_Slot_Handle, 37 | InvalidCharMapHandle = ffi::FT_Err_Invalid_CharMap_Handle, 38 | InvalidCacheHandle = ffi::FT_Err_Invalid_Cache_Handle, 39 | InvalidStreamHandle = ffi::FT_Err_Invalid_Stream_Handle, 40 | TooManyDrivers = ffi::FT_Err_Too_Many_Drivers, 41 | TooManyExtensions = ffi::FT_Err_Too_Many_Extensions, 42 | OutOfMemory = ffi::FT_Err_Out_Of_Memory, 43 | UnlistedObject = ffi::FT_Err_Unlisted_Object, 44 | CannotOpenStream = ffi::FT_Err_Cannot_Open_Stream, 45 | InvalidStreamSeek = ffi::FT_Err_Invalid_Stream_Seek, 46 | InvalidStreamSkip = ffi::FT_Err_Invalid_Stream_Skip, 47 | InvalidStreamRead = ffi::FT_Err_Invalid_Stream_Read, 48 | InvalidStreamOperation = ffi::FT_Err_Invalid_Stream_Operation, 49 | InvalidFrameOperation = ffi::FT_Err_Invalid_Frame_Operation, 50 | NestedFrameAccess = ffi::FT_Err_Nested_Frame_Access, 51 | InvalidFrameRead = ffi::FT_Err_Invalid_Frame_Read, 52 | RasterUninitialized = ffi::FT_Err_Raster_Uninitialized, 53 | RasterCorrupted = ffi::FT_Err_Raster_Corrupted, 54 | RasterOverflow = ffi::FT_Err_Raster_Overflow, 55 | RasterNegativeHeight = ffi::FT_Err_Raster_Negative_Height, 56 | TooManyCaches = ffi::FT_Err_Too_Many_Caches, 57 | InvalidOpcode = ffi::FT_Err_Invalid_Opcode, 58 | TooFewArguments = ffi::FT_Err_Too_Few_Arguments, 59 | StackOverflow = ffi::FT_Err_Stack_Overflow, 60 | CodeOverflow = ffi::FT_Err_Code_Overflow, 61 | BadArgument = ffi::FT_Err_Bad_Argument, 62 | DivideByZero = ffi::FT_Err_Divide_By_Zero, 63 | InvalidReference = ffi::FT_Err_Invalid_Reference, 64 | DebugOpCode = ffi::FT_Err_Debug_OpCode, 65 | ENDFInExecStream = ffi::FT_Err_ENDF_In_Exec_Stream, 66 | NestedDEFS = ffi::FT_Err_Nested_DEFS, 67 | InvalidCodeRange = ffi::FT_Err_Invalid_CodeRange, 68 | ExecutionTooLong = ffi::FT_Err_Execution_Too_Long, 69 | TooManyFunctionDefs = ffi::FT_Err_Too_Many_Function_Defs, 70 | TooManyInstructionDefs = ffi::FT_Err_Too_Many_Instruction_Defs, 71 | TableMissing = ffi::FT_Err_Table_Missing, 72 | HorizHeaderMissing = ffi::FT_Err_Horiz_Header_Missing, 73 | LocationsMissing = ffi::FT_Err_Locations_Missing, 74 | NameTableMissing = ffi::FT_Err_Name_Table_Missing, 75 | CMapTableMissing = ffi::FT_Err_CMap_Table_Missing, 76 | HmtxTableMissing = ffi::FT_Err_Hmtx_Table_Missing, 77 | PostTableMissing = ffi::FT_Err_Post_Table_Missing, 78 | InvalidHorizMetrics = ffi::FT_Err_Invalid_Horiz_Metrics, 79 | InvalidCharMapFormat = ffi::FT_Err_Invalid_CharMap_Format, 80 | InvalidPPem = ffi::FT_Err_Invalid_PPem, 81 | InvalidVertMetrics = ffi::FT_Err_Invalid_Vert_Metrics, 82 | CouldNotFindContext = ffi::FT_Err_Could_Not_Find_Context, 83 | InvalidPostTableFormat = ffi::FT_Err_Invalid_Post_Table_Format, 84 | InvalidPostTable = ffi::FT_Err_Invalid_Post_Table, 85 | Syntax = ffi::FT_Err_Syntax_Error, 86 | StackUnderflow = ffi::FT_Err_Stack_Underflow, 87 | Ignore = ffi::FT_Err_Ignore, 88 | NoUnicodeGlyphName = ffi::FT_Err_No_Unicode_Glyph_Name, 89 | MissingStartfontField = ffi::FT_Err_Missing_Startfont_Field, 90 | MissingFontField = ffi::FT_Err_Missing_Font_Field, 91 | MissingSizeField = ffi::FT_Err_Missing_Size_Field, 92 | MissingFontboundingboxField = ffi::FT_Err_Missing_Fontboundingbox_Field, 93 | MissingCharsField = ffi::FT_Err_Missing_Chars_Field, 94 | MissingStartcharField = ffi::FT_Err_Missing_Startchar_Field, 95 | MissingEncodingField = ffi::FT_Err_Missing_Encoding_Field, 96 | MissingBbxField = ffi::FT_Err_Missing_Bbx_Field, 97 | BbxTooBig = ffi::FT_Err_Bbx_Too_Big, 98 | CorruptedFontHeader = ffi::FT_Err_Corrupted_Font_Header, 99 | CorruptedFontGlyphs = ffi::FT_Err_Corrupted_Font_Glyphs, 100 | Max = ffi::FT_Err_Max, 101 | UnexpectedPixelMode, 102 | InvalidPath, 103 | Unknown, 104 | } 105 | 106 | impl From for Error { 107 | fn from(err: i32) -> Self { 108 | match err { 109 | ffi::FT_Err_Ok => Error::Ok, 110 | ffi::FT_Err_Cannot_Open_Resource => Error::CannotOpenResource, 111 | ffi::FT_Err_Unknown_File_Format => Error::UnknownFileFormat, 112 | ffi::FT_Err_Invalid_File_Format => Error::InvalidFileFormat, 113 | ffi::FT_Err_Invalid_Version => Error::InvalidVersion, 114 | ffi::FT_Err_Lower_Module_Version => Error::LowerModuleVersion, 115 | ffi::FT_Err_Invalid_Argument => Error::InvalidArgument, 116 | ffi::FT_Err_Unimplemented_Feature => Error::UnimplementedFeature, 117 | ffi::FT_Err_Invalid_Table => Error::InvalidTable, 118 | ffi::FT_Err_Invalid_Offset => Error::InvalidOffset, 119 | ffi::FT_Err_Array_Too_Large => Error::ArrayTooLarge, 120 | ffi::FT_Err_Missing_Module => Error::MissingModule, 121 | ffi::FT_Err_Missing_Property => Error::MissingProperty, 122 | ffi::FT_Err_Invalid_Glyph_Index => Error::InvalidGlyphIndex, 123 | ffi::FT_Err_Invalid_Character_Code => Error::InvalidCharacterCode, 124 | ffi::FT_Err_Invalid_Glyph_Format => Error::InvalidGlyphFormat, 125 | ffi::FT_Err_Cannot_Render_Glyph => Error::CannotRenderGlyph, 126 | ffi::FT_Err_Invalid_Outline => Error::InvalidOutline, 127 | ffi::FT_Err_Invalid_Composite => Error::InvalidComposite, 128 | ffi::FT_Err_Too_Many_Hints => Error::TooManyHints, 129 | ffi::FT_Err_Invalid_Pixel_Size => Error::InvalidPixelSize, 130 | ffi::FT_Err_Invalid_Handle => Error::InvalidHandle, 131 | ffi::FT_Err_Invalid_Library_Handle => Error::InvalidLibraryHandle, 132 | ffi::FT_Err_Invalid_Driver_Handle => Error::InvalidDriverHandle, 133 | ffi::FT_Err_Invalid_Face_Handle => Error::InvalidFaceHandle, 134 | ffi::FT_Err_Invalid_Size_Handle => Error::InvalidSizeHandle, 135 | ffi::FT_Err_Invalid_Slot_Handle => Error::InvalidSlotHandle, 136 | ffi::FT_Err_Invalid_CharMap_Handle => Error::InvalidCharMapHandle, 137 | ffi::FT_Err_Invalid_Cache_Handle => Error::InvalidCacheHandle, 138 | ffi::FT_Err_Invalid_Stream_Handle => Error::InvalidStreamHandle, 139 | ffi::FT_Err_Too_Many_Drivers => Error::TooManyDrivers, 140 | ffi::FT_Err_Too_Many_Extensions => Error::TooManyExtensions, 141 | ffi::FT_Err_Out_Of_Memory => Error::OutOfMemory, 142 | ffi::FT_Err_Unlisted_Object => Error::UnlistedObject, 143 | ffi::FT_Err_Cannot_Open_Stream => Error::CannotOpenStream, 144 | ffi::FT_Err_Invalid_Stream_Seek => Error::InvalidStreamSeek, 145 | ffi::FT_Err_Invalid_Stream_Skip => Error::InvalidStreamSkip, 146 | ffi::FT_Err_Invalid_Stream_Read => Error::InvalidStreamRead, 147 | ffi::FT_Err_Invalid_Stream_Operation => Error::InvalidStreamOperation, 148 | ffi::FT_Err_Invalid_Frame_Operation => Error::InvalidFrameOperation, 149 | ffi::FT_Err_Nested_Frame_Access => Error::NestedFrameAccess, 150 | ffi::FT_Err_Invalid_Frame_Read => Error::InvalidFrameRead, 151 | ffi::FT_Err_Raster_Uninitialized => Error::RasterUninitialized, 152 | ffi::FT_Err_Raster_Corrupted => Error::RasterCorrupted, 153 | ffi::FT_Err_Raster_Overflow => Error::RasterOverflow, 154 | ffi::FT_Err_Raster_Negative_Height => Error::RasterNegativeHeight, 155 | ffi::FT_Err_Too_Many_Caches => Error::TooManyCaches, 156 | ffi::FT_Err_Invalid_Opcode => Error::InvalidOpcode, 157 | ffi::FT_Err_Too_Few_Arguments => Error::TooFewArguments, 158 | ffi::FT_Err_Stack_Overflow => Error::StackOverflow, 159 | ffi::FT_Err_Code_Overflow => Error::CodeOverflow, 160 | ffi::FT_Err_Bad_Argument => Error::BadArgument, 161 | ffi::FT_Err_Divide_By_Zero => Error::DivideByZero, 162 | ffi::FT_Err_Invalid_Reference => Error::InvalidReference, 163 | ffi::FT_Err_Debug_OpCode => Error::DebugOpCode, 164 | ffi::FT_Err_ENDF_In_Exec_Stream => Error::ENDFInExecStream, 165 | ffi::FT_Err_Nested_DEFS => Error::NestedDEFS, 166 | ffi::FT_Err_Invalid_CodeRange => Error::InvalidCodeRange, 167 | ffi::FT_Err_Execution_Too_Long => Error::ExecutionTooLong, 168 | ffi::FT_Err_Too_Many_Function_Defs => Error::TooManyFunctionDefs, 169 | ffi::FT_Err_Too_Many_Instruction_Defs => Error::TooManyInstructionDefs, 170 | ffi::FT_Err_Table_Missing => Error::TableMissing, 171 | ffi::FT_Err_Horiz_Header_Missing => Error::HorizHeaderMissing, 172 | ffi::FT_Err_Locations_Missing => Error::LocationsMissing, 173 | ffi::FT_Err_Name_Table_Missing => Error::NameTableMissing, 174 | ffi::FT_Err_CMap_Table_Missing => Error::CMapTableMissing, 175 | ffi::FT_Err_Hmtx_Table_Missing => Error::HmtxTableMissing, 176 | ffi::FT_Err_Post_Table_Missing => Error::PostTableMissing, 177 | ffi::FT_Err_Invalid_Horiz_Metrics => Error::InvalidHorizMetrics, 178 | ffi::FT_Err_Invalid_CharMap_Format => Error::InvalidCharMapFormat, 179 | ffi::FT_Err_Invalid_PPem => Error::InvalidPPem, 180 | ffi::FT_Err_Invalid_Vert_Metrics => Error::InvalidVertMetrics, 181 | ffi::FT_Err_Could_Not_Find_Context => Error::CouldNotFindContext, 182 | ffi::FT_Err_Invalid_Post_Table_Format => Error::InvalidPostTableFormat, 183 | ffi::FT_Err_Invalid_Post_Table => Error::InvalidPostTable, 184 | ffi::FT_Err_Syntax_Error => Error::Syntax, 185 | ffi::FT_Err_Stack_Underflow => Error::StackUnderflow, 186 | ffi::FT_Err_Ignore => Error::Ignore, 187 | ffi::FT_Err_No_Unicode_Glyph_Name => Error::NoUnicodeGlyphName, 188 | ffi::FT_Err_Missing_Startfont_Field => Error::MissingStartfontField, 189 | ffi::FT_Err_Missing_Font_Field => Error::MissingFontField, 190 | ffi::FT_Err_Missing_Size_Field => Error::MissingSizeField, 191 | ffi::FT_Err_Missing_Fontboundingbox_Field => Error::MissingFontboundingboxField, 192 | ffi::FT_Err_Missing_Chars_Field => Error::MissingCharsField, 193 | ffi::FT_Err_Missing_Startchar_Field => Error::MissingStartcharField, 194 | ffi::FT_Err_Missing_Encoding_Field => Error::MissingEncodingField, 195 | ffi::FT_Err_Missing_Bbx_Field => Error::MissingBbxField, 196 | ffi::FT_Err_Bbx_Too_Big => Error::BbxTooBig, 197 | ffi::FT_Err_Corrupted_Font_Header => Error::CorruptedFontHeader, 198 | ffi::FT_Err_Corrupted_Font_Glyphs => Error::CorruptedFontGlyphs, 199 | ffi::FT_Err_Max => Error::Max, 200 | _ => Error::Unknown, 201 | } 202 | } 203 | } 204 | 205 | impl fmt::Display for Error { 206 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 207 | use self::Error::*; 208 | f.write_str(match *self { 209 | Ok => "Ok", 210 | CannotOpenResource => "Cannot open resource", 211 | UnknownFileFormat => "Unknown file format", 212 | InvalidFileFormat => "Invalid file format", 213 | InvalidVersion => "Invalid version", 214 | LowerModuleVersion => "Lower module version", 215 | InvalidArgument => "Invalid argument", 216 | UnimplementedFeature => "Unimplemented feature", 217 | InvalidTable => "Invalid table", 218 | InvalidOffset => "Invalid offset", 219 | ArrayTooLarge => "Array too large", 220 | MissingModule => "Missing module", 221 | MissingProperty => "Missing property", 222 | InvalidGlyphIndex => "Invalid glyph index", 223 | InvalidCharacterCode => "Invalid character code", 224 | InvalidGlyphFormat => "Invalid glyph format", 225 | CannotRenderGlyph => "Cannot render glyph", 226 | InvalidOutline => "Invalid outline", 227 | InvalidComposite => "Invalid composite", 228 | TooManyHints => "Too many hints", 229 | InvalidPixelSize => "Invalid pixel size", 230 | InvalidHandle => "Invalid handle", 231 | InvalidLibraryHandle => "Invalid library handle", 232 | InvalidDriverHandle => "Invalid driver handle", 233 | InvalidFaceHandle => "Invalid face handle", 234 | InvalidSizeHandle => "Invalid size handle", 235 | InvalidSlotHandle => "Invalid slot handle", 236 | InvalidCharMapHandle => "Invalid char map handle", 237 | InvalidCacheHandle => "Invalid cache handle", 238 | InvalidStreamHandle => "Invalid stream handle", 239 | TooManyDrivers => "Too many drivers", 240 | TooManyExtensions => "Too many extensions", 241 | OutOfMemory => "Out of memory", 242 | UnlistedObject => "Unlisted object", 243 | CannotOpenStream => "Cannot open stream", 244 | InvalidStreamSeek => "Invalid stream seek", 245 | InvalidStreamSkip => "Invalid stream skip", 246 | InvalidStreamRead => "Invalid stream read", 247 | InvalidStreamOperation => "Invalid stream operation", 248 | InvalidFrameOperation => "Invalid frame operation", 249 | NestedFrameAccess => "Nested frame access", 250 | InvalidFrameRead => "Invalid frame read", 251 | RasterUninitialized => "Raster uninitialized", 252 | RasterCorrupted => "Raster corrupted", 253 | RasterOverflow => "Raster overflow", 254 | RasterNegativeHeight => "Raster negative height", 255 | TooManyCaches => "Too many caches", 256 | InvalidOpcode => "Invalid opcode", 257 | TooFewArguments => "Too few arguments", 258 | StackOverflow => "Stack overflow", 259 | CodeOverflow => "Code overflow", 260 | BadArgument => "Bad argument", 261 | DivideByZero => "Divide by zero", 262 | InvalidReference => "Invalid reference", 263 | DebugOpCode => "Debug op code", 264 | ENDFInExecStream => "ENDF in exec stream", 265 | NestedDEFS => "Nested DEFS", 266 | InvalidCodeRange => "Invalid code range", 267 | ExecutionTooLong => "Execution too long", 268 | TooManyFunctionDefs => "Too many function defs", 269 | TooManyInstructionDefs => "Too many instruction defs", 270 | TableMissing => "Table missing", 271 | HorizHeaderMissing => "Horiz header missing", 272 | LocationsMissing => "Locations missing", 273 | NameTableMissing => "Name table missing", 274 | CMapTableMissing => "C map table missing", 275 | HmtxTableMissing => "Hmtx table missing", 276 | PostTableMissing => "Post table missing", 277 | InvalidHorizMetrics => "Invalid horiz metrics", 278 | InvalidCharMapFormat => "Invalid char map format", 279 | InvalidPPem => "Invalid p pem", 280 | InvalidVertMetrics => "Invalid vert metrics", 281 | CouldNotFindContext => "Could not find context", 282 | InvalidPostTableFormat => "Invalid post table format", 283 | InvalidPostTable => "Invalid post table", 284 | Syntax => "Syntax", 285 | StackUnderflow => "Stack underflow", 286 | Ignore => "Ignore", 287 | NoUnicodeGlyphName => "No unicode glyph name", 288 | MissingStartfontField => "Missing startfont field", 289 | MissingFontField => "Missing font field", 290 | MissingSizeField => "Missing size field", 291 | MissingFontboundingboxField => "Missing fontboundingbox field", 292 | MissingCharsField => "Missing chars field", 293 | MissingStartcharField => "Missing startchar field", 294 | MissingEncodingField => "Missing encoding field", 295 | MissingBbxField => "Missing bbx field", 296 | BbxTooBig => "Bbx too big", 297 | CorruptedFontHeader => "Corrupted font header", 298 | CorruptedFontGlyphs => "Corrupted font glyphs", 299 | Max => "Max", 300 | UnexpectedPixelMode => "Unexpected pixel mode", 301 | InvalidPath => "Invalid path", 302 | Unknown => "Unknown", 303 | }) 304 | } 305 | } 306 | 307 | impl error::Error for Error {} 308 | 309 | #[cfg(test)] 310 | mod tests { 311 | use super::*; 312 | 313 | #[test] 314 | fn print_should_halt() { 315 | use std::error::Error as _; 316 | 317 | Error::Ok.to_string(); 318 | #[allow(deprecated)] 319 | Error::Ok.description(); 320 | } 321 | } 322 | --------------------------------------------------------------------------------