1 use std::fmt;
2 use std::ptr;
3 
4 use foreign_types::{ForeignType, ForeignTypeRef};
5 
6 use fontconfig::fontconfig as ffi;
7 
8 use ffi::FcInitBringUptoDate;
9 use ffi::FcResultNoMatch;
10 use ffi::{FcFontList, FcFontMatch, FcFontSort};
11 use ffi::{FcMatchFont, FcMatchPattern, FcMatchScan};
12 use ffi::{FcSetApplication, FcSetSystem};
13 use ffi::{FC_SLANT_ITALIC, FC_SLANT_OBLIQUE, FC_SLANT_ROMAN};
14 use ffi::{FC_WEIGHT_BLACK, FC_WEIGHT_BOLD, FC_WEIGHT_EXTRABLACK, FC_WEIGHT_EXTRABOLD};
15 use ffi::{FC_WEIGHT_BOOK, FC_WEIGHT_MEDIUM, FC_WEIGHT_REGULAR, FC_WEIGHT_SEMIBOLD};
16 use ffi::{FC_WEIGHT_EXTRALIGHT, FC_WEIGHT_LIGHT, FC_WEIGHT_THIN};
17 
18 pub mod config;
19 pub use config::{Config, ConfigRef};
20 
21 pub mod font_set;
22 pub use font_set::{FontSet, FontSetRef};
23 
24 pub mod object_set;
25 pub use object_set::{ObjectSet, ObjectSetRef};
26 
27 pub mod char_set;
28 pub use char_set::{CharSet, CharSetRef};
29 
30 pub mod pattern;
31 pub use pattern::{FtFaceLocation, Pattern, PatternHash, PatternRef};
32 
33 /// Find the font closest matching the provided pattern.
34 ///
35 /// The returned pattern is the result of Pattern::render_prepare.
font_match(config: &ConfigRef, pattern: &PatternRef) -> Option<Pattern>36 pub fn font_match(config: &ConfigRef, pattern: &PatternRef) -> Option<Pattern> {
37     unsafe {
38         // What is this result actually used for? Seems redundant with
39         // return type.
40         let mut result = FcResultNoMatch;
41         let ptr = FcFontMatch(config.as_ptr(), pattern.as_ptr(), &mut result);
42 
43         if ptr.is_null() {
44             None
45         } else {
46             Some(Pattern::from_ptr(ptr))
47         }
48     }
49 }
50 
51 /// Reloads the Fontconfig configuration files.
update_config()52 pub fn update_config() {
53     unsafe {
54         let _ = FcInitBringUptoDate();
55     }
56 }
57 
58 /// List fonts by closeness to the pattern.
font_sort(config: &ConfigRef, pattern: &PatternRef) -> Option<FontSet>59 pub fn font_sort(config: &ConfigRef, pattern: &PatternRef) -> Option<FontSet> {
60     unsafe {
61         // What is this result actually used for? Seems redundant with
62         // return type.
63         let mut result = FcResultNoMatch;
64 
65         let mut charsets: *mut _ = ptr::null_mut();
66         let ptr = FcFontSort(
67             config.as_ptr(),
68             pattern.as_ptr(),
69             1, // Trim font list.
70             &mut charsets,
71             &mut result,
72         );
73 
74         if ptr.is_null() {
75             None
76         } else {
77             Some(FontSet::from_ptr(ptr))
78         }
79     }
80 }
81 
82 /// List fonts matching pattern.
font_list( config: &ConfigRef, pattern: &PatternRef, objects: &ObjectSetRef, ) -> Option<FontSet>83 pub fn font_list(
84     config: &ConfigRef,
85     pattern: &PatternRef,
86     objects: &ObjectSetRef,
87 ) -> Option<FontSet> {
88     unsafe {
89         let ptr = FcFontList(config.as_ptr(), pattern.as_ptr(), objects.as_ptr());
90 
91         if ptr.is_null() {
92             None
93         } else {
94             Some(FontSet::from_ptr(ptr))
95         }
96     }
97 }
98 
99 /// Available font sets.
100 #[derive(Debug, Copy, Clone)]
101 pub enum SetName {
102     System = FcSetSystem as isize,
103     Application = FcSetApplication as isize,
104 }
105 
106 /// When matching, how to match.
107 #[derive(Debug, Copy, Clone)]
108 pub enum MatchKind {
109     Font = FcMatchFont as isize,
110     Pattern = FcMatchPattern as isize,
111     Scan = FcMatchScan as isize,
112 }
113 
114 #[derive(Debug, Copy, Clone)]
115 pub enum Slant {
116     Italic = FC_SLANT_ITALIC as isize,
117     Oblique = FC_SLANT_OBLIQUE as isize,
118     Roman = FC_SLANT_ROMAN as isize,
119 }
120 
121 #[derive(Debug, Copy, Clone)]
122 pub enum Weight {
123     Thin = FC_WEIGHT_THIN as isize,
124     Extralight = FC_WEIGHT_EXTRALIGHT as isize,
125     Light = FC_WEIGHT_LIGHT as isize,
126     Book = FC_WEIGHT_BOOK as isize,
127     Regular = FC_WEIGHT_REGULAR as isize,
128     Medium = FC_WEIGHT_MEDIUM as isize,
129     Semibold = FC_WEIGHT_SEMIBOLD as isize,
130     Bold = FC_WEIGHT_BOLD as isize,
131     Extrabold = FC_WEIGHT_EXTRABOLD as isize,
132     Black = FC_WEIGHT_BLACK as isize,
133     Extrablack = FC_WEIGHT_EXTRABLACK as isize,
134 }
135 
136 #[derive(Debug, Copy, Clone)]
137 pub enum Width {
138     Ultracondensed,
139     Extracondensed,
140     Condensed,
141     Semicondensed,
142     Normal,
143     Semiexpanded,
144     Expanded,
145     Extraexpanded,
146     Ultraexpanded,
147     Other(i32),
148 }
149 
150 impl Width {
to_isize(self) -> isize151     fn to_isize(self) -> isize {
152         match self {
153             Width::Ultracondensed => 50,
154             Width::Extracondensed => 63,
155             Width::Condensed => 75,
156             Width::Semicondensed => 87,
157             Width::Normal => 100,
158             Width::Semiexpanded => 113,
159             Width::Expanded => 125,
160             Width::Extraexpanded => 150,
161             Width::Ultraexpanded => 200,
162             Width::Other(value) => value as isize,
163         }
164     }
165 }
166 
167 impl From<isize> for Width {
from(value: isize) -> Self168     fn from(value: isize) -> Self {
169         match value {
170             50 => Width::Ultracondensed,
171             63 => Width::Extracondensed,
172             75 => Width::Condensed,
173             87 => Width::Semicondensed,
174             100 => Width::Normal,
175             113 => Width::Semiexpanded,
176             125 => Width::Expanded,
177             150 => Width::Extraexpanded,
178             200 => Width::Ultraexpanded,
179             _ => Width::Other(value as _),
180         }
181     }
182 }
183 
184 /// Subpixel geometry.
185 #[derive(Debug)]
186 pub enum Rgba {
187     Unknown,
188     Rgb,
189     Bgr,
190     Vrgb,
191     Vbgr,
192     None,
193 }
194 
195 impl Rgba {
to_isize(&self) -> isize196     fn to_isize(&self) -> isize {
197         match *self {
198             Rgba::Unknown => 0,
199             Rgba::Rgb => 1,
200             Rgba::Bgr => 2,
201             Rgba::Vrgb => 3,
202             Rgba::Vbgr => 4,
203             Rgba::None => 5,
204         }
205     }
206 }
207 
208 impl fmt::Display for Rgba {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result209     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
210         f.write_str(match *self {
211             Rgba::Unknown => "unknown",
212             Rgba::Rgb => "rgb",
213             Rgba::Bgr => "bgr",
214             Rgba::Vrgb => "vrgb",
215             Rgba::Vbgr => "vbgr",
216             Rgba::None => "none",
217         })
218     }
219 }
220 
221 impl From<isize> for Rgba {
from(val: isize) -> Rgba222     fn from(val: isize) -> Rgba {
223         match val {
224             1 => Rgba::Rgb,
225             2 => Rgba::Bgr,
226             3 => Rgba::Vrgb,
227             4 => Rgba::Vbgr,
228             5 => Rgba::None,
229             _ => Rgba::Unknown,
230         }
231     }
232 }
233 
234 /// Hinting Style.
235 #[derive(Debug, Copy, Clone)]
236 pub enum HintStyle {
237     None,
238     Slight,
239     Medium,
240     Full,
241 }
242 
243 impl fmt::Display for HintStyle {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result244     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
245         f.write_str(match *self {
246             HintStyle::None => "none",
247             HintStyle::Slight => "slight",
248             HintStyle::Medium => "medium",
249             HintStyle::Full => "full",
250         })
251     }
252 }
253 
254 /// Lcd filter, used to reduce color fringing with subpixel rendering.
255 pub enum LcdFilter {
256     None,
257     Default,
258     Light,
259     Legacy,
260 }
261 
262 impl fmt::Display for LcdFilter {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result263     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
264         f.write_str(match *self {
265             LcdFilter::None => "none",
266             LcdFilter::Default => "default",
267             LcdFilter::Light => "light",
268             LcdFilter::Legacy => "legacy",
269         })
270     }
271 }
272 
273 #[cfg(test)]
274 mod tests {
275     use super::*;
276 
277     #[test]
font_match()278     fn font_match() {
279         let mut pattern = Pattern::new();
280         pattern.add_family("monospace");
281         pattern.add_style("regular");
282 
283         let config = Config::get_current();
284         pattern.config_substitute(config, MatchKind::Pattern);
285         pattern.default_substitute();
286         let font = super::font_match(config, &pattern).expect("match font monospace");
287 
288         print!("index={:?}; ", font.index());
289         print!("family={:?}; ", font.family());
290         print!("style={:?}; ", font.style());
291         print!("antialias={:?}; ", font.antialias());
292         print!("autohint={:?}; ", font.autohint());
293         print!("hinting={:?}; ", font.hinting());
294         print!("rgba={:?}; ", font.rgba());
295         print!("embeddedbitmap={:?}; ", font.embeddedbitmap());
296         print!("lcdfilter={:?}; ", font.lcdfilter());
297         print!("hintstyle={:?}", font.hintstyle());
298         println!();
299     }
300 
301     #[test]
font_sort()302     fn font_sort() {
303         let mut pattern = Pattern::new();
304         pattern.add_family("monospace");
305         pattern.set_slant(Slant::Italic);
306 
307         let config = Config::get_current();
308         pattern.config_substitute(config, MatchKind::Pattern);
309         pattern.default_substitute();
310         let fonts = super::font_sort(config, &pattern).expect("sort font monospace");
311 
312         for font in fonts.into_iter().take(10) {
313             let font = pattern.render_prepare(&config, &font);
314             print!("index={:?}; ", font.index());
315             print!("family={:?}; ", font.family());
316             print!("style={:?}; ", font.style());
317             print!("rgba={:?}", font.rgba());
318             print!("rgba={:?}", font.rgba());
319             println!();
320         }
321     }
322 
323     #[test]
font_sort_with_glyph()324     fn font_sort_with_glyph() {
325         let mut charset = CharSet::new();
326         charset.add('��');
327         let mut pattern = Pattern::new();
328         pattern.add_charset(&charset);
329         drop(charset);
330 
331         let config = Config::get_current();
332         pattern.config_substitute(config, MatchKind::Pattern);
333         pattern.default_substitute();
334         let fonts = super::font_sort(config, &pattern).expect("font_sort");
335 
336         for font in fonts.into_iter().take(10) {
337             let font = pattern.render_prepare(&config, &font);
338             print!("index={:?}; ", font.index());
339             print!("family={:?}; ", font.family());
340             print!("style={:?}; ", font.style());
341             print!("rgba={:?}", font.rgba());
342             println!();
343         }
344     }
345 }
346