font_loader/
fontconfig.rs

1// The MIT License (MIT)
2// Copyright (c) 2016 font-loader Developers
3//
4// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
5// associated documentation files (the "Software"), to deal in the Software without restriction,
6// including without limitation the rights to use, copy, modify, merge, publish, distribute,
7// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all copies or
11// substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
14// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
16// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18
19/// Font loading utilities for installed system fonts
20pub mod system_fonts {
21    use servo_fontconfig::fontconfig::{FcConfig, FcInitLoadConfigAndFonts, FcNameParse};
22    use servo_fontconfig::fontconfig::{FcPattern, FcPatternCreate, FcPatternDestroy, FcFontMatch};
23    use servo_fontconfig::fontconfig::{FcFontList, FcObjectSetBuild, FcChar8, FcDefaultSubstitute};
24    use servo_fontconfig::fontconfig::{FcPatternGetString, FcPatternAddInteger, FcPatternGetInteger};
25    use servo_fontconfig::fontconfig::{FcResultMatch, FcMatchPattern, FcResultNoMatch, FcConfigSubstitute};
26    use servo_fontconfig::fontconfig::FcPatternAddString;
27
28    use libc::{c_int, c_char};
29
30    use std::ptr;
31    use std::slice;
32    use std::ffi::{CStr, CString};
33    use std::io::prelude::*;
34    use std::fs::File;
35
36    use std::sync::{Once, ONCE_INIT};
37
38    static FC_FAMILY: &'static [u8] = b"family\0";
39    static FC_FILE: &'static [u8] = b"file\0";
40    static FC_WEIGHT: &'static [u8] = b"weight\0";
41    static FC_INDEX: &'static [u8] = b"index\0";
42    static FC_SLANT: &'static [u8] = b"slant\0";
43    static FC_SPACING: &'static [u8] = b"spacing\0";
44    //  static FC_FONTFORMAT: &'static [u8] = b"fontformat\0";
45    // 	static FC_STYLE: &'static [u8] = b"style\0";
46    // 	static FC_FAMILYLANG: &'static[u8] = b"familylang\0";
47    // 	static FC_CHARSET: &'static [u8] = b"charset\0";
48    // 	static FC_LANG: &'static [u8] = b"lang\0";
49    // 	static FC_STYLELANG: &'static [u8] = b"stylelang\0";
50
51    // 	static FC_WEIGHT_THIN: c_int 		= 0;
52    // 	static FC_WEIGHT_EXTRALIGHT: c_int 	= 40;
53    // 	static FC_WEIGHT_LIGHT: c_int 		= 50;
54    // 	static FC_WEIGHT_DEMILIGHT: c_int 	= 55;
55    // 	static FC_WEIGHT_BOOK: c_int 		= 75;
56    static FC_WEIGHT_REGULAR: c_int = 80;
57    // 	static FC_WEIGHT_MEDIUM: c_int 		= 100;
58    // 	static FC_WEIGHT_DEMIBOLD: c_int 	= 180;
59    static FC_WEIGHT_BOLD: c_int = 200;
60    // 	static FC_WEIGHT_EXTRABOLD: c_int 	= 205;
61    // 	static FC_WEIGHT_BLACK: c_int 		= 210;
62    // 	static FC_WEIGHT_EXTRA_BLACK: c_int = 215;
63
64    static FC_SLANT_ROMAN: c_int = 0;
65    static FC_SLANT_ITALIC: c_int = 100;
66    static FC_SLANT_OBLIQUE: c_int = 110;
67
68    //    static FC_PROPORTIONAL: c_int = 0;
69    // 	static FC_DUAL: c_int = 90;
70    static FC_MONO: c_int = 100;
71    // 	static FC_CHARCELL: c_int = 110;
72
73    static INIT_FONTCONFIG: Once = ONCE_INIT;
74    static mut CONFIG: *mut FcConfig = 0 as *mut FcConfig;
75
76    fn init() -> *mut FcConfig {
77        unsafe {
78            INIT_FONTCONFIG.call_once(|| {
79                CONFIG = FcInitLoadConfigAndFonts();
80            });
81            CONFIG
82        }
83    }
84
85    /// The platform specific font properties
86    pub struct FontProperty {
87        slant: c_int,
88        weight: c_int,
89        family: String,
90        spacing: Option<c_int>,
91    }
92
93    /// Builder for FontProperty
94    pub struct FontPropertyBuilder {
95        property: FontProperty,
96    }
97
98    impl FontPropertyBuilder {
99        pub fn new() -> FontPropertyBuilder {
100            let property = FontProperty {
101                slant: FC_SLANT_ROMAN,
102                weight: FC_WEIGHT_REGULAR,
103                family: String::new(),
104                spacing: None,
105            };
106            FontPropertyBuilder { property: property }
107        }
108
109        pub fn italic(mut self) -> FontPropertyBuilder {
110            self.property.slant = FC_SLANT_ITALIC;
111            self
112        }
113
114        pub fn oblique(mut self) -> FontPropertyBuilder {
115            self.property.slant = FC_SLANT_OBLIQUE;
116            self
117        }
118
119        pub fn bold(mut self) -> FontPropertyBuilder {
120            self.property.weight = FC_WEIGHT_BOLD;
121            self
122        }
123
124        pub fn monospace(mut self) -> FontPropertyBuilder {
125            self.property.spacing = Some(FC_MONO);
126            self
127        }
128
129        pub fn family(mut self, name: &str) -> FontPropertyBuilder {
130            self.property.family.clear();
131            self.property.family.push_str(name);
132            self
133        }
134
135        pub fn build(self) -> FontProperty {
136            self.property
137        }
138    }
139
140    /// Get the binary data and index of a specific font
141    /// Note that only truetype fonts are supported
142    pub fn get(property: &FontProperty) -> Option<(Vec<u8>, c_int)> {
143        let config = init();
144        let family: &str = &property.family;
145
146        unsafe {
147            let name = CString::new(family).unwrap();
148            let pat = FcNameParse(name.as_ptr() as *const FcChar8);
149            add_int(pat, FC_SLANT, property.slant);
150            add_int(pat, FC_WEIGHT, property.weight);
151            FcConfigSubstitute(config, pat, FcMatchPattern);
152            FcDefaultSubstitute(pat);
153
154            let mut result = FcResultNoMatch;
155            let font_pat = FcFontMatch(config, pat, &mut result);
156
157            if font_pat.is_null() {
158                None
159            } else {
160                let file = get_string(font_pat, FC_FILE).unwrap();
161                let index = get_int(font_pat, FC_INDEX).unwrap();
162                FcPatternDestroy(font_pat);
163                let mut file = File::open(file).unwrap();
164                let mut buf: Vec<u8> = Vec::new();
165                let _ = file.read_to_end(&mut buf);
166                Some((buf, index))
167            }
168        }
169    }
170
171    /// Query the names of all fonts installed in the system
172    /// Note that only truetype fonts are supported
173    pub fn query_all() -> Vec<String> {
174        let mut property = FontPropertyBuilder::new().build();
175        query_specific(&mut property)
176    }
177
178    /// Query the names of specifc fonts installed in the system
179    /// Note that only truetype fonts are supported
180    pub fn query_specific(property: &mut FontProperty) -> Vec<String> {
181        let mut fonts: Vec<String> = Vec::new();
182        unsafe {
183            let config = init();
184
185            let pattern = FcPatternCreate();
186            if !property.family.is_empty() {
187                add_string(pattern, FC_FAMILY, &property.family);
188            }
189            property.spacing.map(|spacing| add_int(pattern, FC_SPACING, spacing));
190            add_int(pattern, FC_WEIGHT, property.weight);
191            add_int(pattern, FC_SLANT, property.slant);
192
193            let null_ptr: *const c_char = ptr::null();
194            let o1 = FC_FAMILY.as_ptr() as *mut c_char;
195            let os = FcObjectSetBuild(o1, null_ptr);
196            let fs = FcFontList(config, pattern, os);
197
198            let patterns = slice::from_raw_parts((*fs).fonts, (*fs).nfont as usize);
199            for pat in patterns {
200                let family_name = get_string(*pat, FC_FAMILY).unwrap();
201                fonts.push(family_name);
202            }
203        }
204
205        fonts.sort();
206        fonts.dedup();
207        fonts
208    }
209
210    fn add_int(pat: *mut FcPattern, object_name: &[u8], value: c_int) {
211        let object = object_name.as_ptr() as *const c_char;
212        unsafe {
213            FcPatternAddInteger(pat, object, value);
214        }
215    }
216
217    fn get_int(pat: *mut FcPattern, object_name: &[u8]) -> Result<c_int, &str> {
218        let object = object_name.as_ptr() as *const c_char;
219        unsafe {
220            let mut int: c_int = 0;
221            if FcPatternGetInteger(pat, object, 0, &mut int) == FcResultMatch {
222                Ok(int)
223            } else {
224                Err("Type didn't match")
225            }
226        }
227    }
228
229    fn add_string(pat: *mut FcPattern, object_name: &[u8], value: &str) {
230        let value = CString::new(value).unwrap();
231        let value_ptr = value.as_ptr() as *const FcChar8;
232        let object = object_name.as_ptr() as *const c_char;
233        unsafe {
234            FcPatternAddString(pat, object, value_ptr);
235        }
236    }
237
238    fn get_string(pat: *mut FcPattern, object_name: &[u8]) -> Result<String, &str> {
239        unsafe {
240            let mut string: *mut FcChar8 = ptr::null_mut();
241            let object = object_name.as_ptr() as *const c_char;
242            if FcPatternGetString(pat, object, 0, &mut string) == FcResultMatch {
243                let cstr = CStr::from_ptr(string as *mut c_char);
244                let string = cstr.to_string_lossy().into_owned();
245                Ok(string)
246            } else {
247                Err("Type didn't match")
248            }
249        }
250    }
251}