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