font_loader/
win32.rs

1// The MIT License (MIT)
2// Copyright (c) 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 winapi::um::wingdi;
22    use winapi::um::wingdi::TEXTMETRICW;
23    use winapi::ctypes::{c_int, c_void};
24    use winapi::um::winnt::{PVOID};
25    use winapi::um::wingdi::FIXED_PITCH;
26    use winapi::um::wingdi::{ENUMLOGFONTEXW, LOGFONTW, OUT_TT_ONLY_PRECIS};
27    use winapi::um::wingdi::FONTENUMPROCW;
28    use winapi::shared::minwindef::{DWORD, LPARAM};
29
30    use std::ptr;
31    use std::mem;
32    use std::ffi::{OsStr, OsString};
33    use std::os::windows::ffi::{OsStrExt, OsStringExt};
34
35    /// The platform specific font properties
36    pub type FontProperty = LOGFONTW;
37
38    /// Builder for FontProperty
39    pub struct FontPropertyBuilder {
40        config: FontProperty,
41    }
42
43    impl FontPropertyBuilder {
44        pub fn new() -> FontPropertyBuilder {
45            let string: [u16; 32] = [0; 32];
46            FontPropertyBuilder {
47                config: FontProperty {
48                    lfHeight: 0,
49                    lfWidth: 0,
50                    lfEscapement: 0,
51                    lfOrientation: 0,
52                    lfWeight: 0,
53                    lfItalic: 0,
54                    lfUnderline: 0,
55                    lfStrikeOut: 0,
56                    lfCharSet: 0,
57                    lfOutPrecision: OUT_TT_ONLY_PRECIS as u8,
58                    lfClipPrecision: 0,
59                    lfQuality: 0,
60                    lfPitchAndFamily: 0,
61                    lfFaceName: string,
62                },
63            }
64        }
65
66        pub fn italic(mut self) -> FontPropertyBuilder {
67            self.config.lfItalic = true as u8;
68            self
69        }
70
71        pub fn oblique(self) -> FontPropertyBuilder {
72            self.italic()
73        }
74
75        pub fn monospace(mut self) -> FontPropertyBuilder {
76            self.config.lfPitchAndFamily |= FIXED_PITCH as u8;
77            self
78        }
79
80        pub fn bold(mut self) -> FontPropertyBuilder {
81            self.config.lfWeight = 700;
82            self
83        }
84
85        pub fn family(mut self, name: &str) -> FontPropertyBuilder {
86            if name.len() > 31 {
87                panic!("Font length must me smaller than 31");
88            }
89            let name: &OsStr = name.as_ref();
90            let buffer = name.encode_wide();
91            let mut string: [u16; 32] = [0; 32]; // +1 Null terminator
92            for (index, item) in buffer.enumerate() {
93                string[index] = item;
94            }
95            self.config.lfFaceName = string;
96            self
97        }
98
99        pub fn build(self) -> FontProperty {
100            self.config
101        }
102    }
103
104    /// Get the binary data and index of a specific font
105    /// Note that only truetype fonts are supported
106    pub fn get(config: &FontProperty) -> Option<(Vec<u8>, c_int)> {
107        unsafe {
108            let hdc = wingdi::CreateCompatibleDC(ptr::null_mut());
109            let hfont = wingdi::CreateFontIndirectW(config as *const LOGFONTW);
110            wingdi::SelectObject(hdc, hfont as *mut c_void);
111            let size = wingdi::GetFontData(hdc, 0, 0, ptr::null_mut(), 0);
112            if size == 0xFFFFFFFF {
113                wingdi::DeleteDC(hdc);
114                None
115            } else if size > 0 {
116                let mut buffer: Vec<u8> = vec![0; size as usize];
117                let pointer = buffer.first_mut().unwrap() as *mut _ as PVOID;
118                let size = wingdi::GetFontData(hdc, 0, 0, pointer, size);
119                buffer.set_len(size as usize);
120                wingdi::DeleteDC(hdc);
121                Some((buffer, 0))
122            } else {
123                wingdi::DeleteDC(hdc);
124                None
125            }
126        }
127    }
128
129    pub fn get_native(config: &mut FontProperty) -> FontProperty {
130        let f: FONTENUMPROCW = Some(callback_native);
131        unsafe {
132            let mut logfont: LOGFONTW = mem::zeroed();
133            let pointer = &mut logfont as *mut _;
134            let hdc = wingdi::CreateCompatibleDC(ptr::null_mut());
135            wingdi::EnumFontFamiliesExW(hdc, config, f, pointer as LPARAM, 0);
136            wingdi::DeleteDC(hdc);
137            logfont
138        }
139    }
140
141    /// Query the names of all fonts installed in the system
142    /// Note that only truetype fonts are supported
143    pub fn query_all() -> Vec<String> {
144        let mut config = FontPropertyBuilder::new().build();
145        query_specific(&mut config)
146    }
147
148    /// Query the names of specifc fonts installed in the system
149    /// Note that only truetype fonts are supported
150    pub fn query_specific(property: &mut FontProperty) -> Vec<String> {
151
152        let mut fonts = Vec::new();
153        let mut f: FONTENUMPROCW = Some(callback_ttf);
154        unsafe {
155            let hdc = wingdi::CreateCompatibleDC(ptr::null_mut());
156
157            if (property.lfPitchAndFamily & FIXED_PITCH as u8) != 0 {
158                f = Some(callback_monospace);
159            }
160
161            let vec_pointer = &mut fonts as *mut Vec<String>;
162
163            wingdi::EnumFontFamiliesExW(hdc, property, f, vec_pointer as LPARAM, 0);
164            wingdi::DeleteDC(hdc);
165        }
166        fonts
167    }
168
169    #[allow(non_snake_case)]
170    unsafe extern "system" fn callback_ttf(lpelfe: *const LOGFONTW,
171                                           _: *const TEXTMETRICW,
172                                           fonttype: DWORD,
173                                           lparam: LPARAM)
174                                           -> c_int {
175
176        if fonttype != 4 {
177            return 1;
178        }
179
180        add_vec(lpelfe, lparam);
181
182        1
183    }
184
185    #[allow(non_snake_case)]
186    unsafe extern "system" fn callback_monospace(lpelfe: *const LOGFONTW,
187                                                 _: *const TEXTMETRICW,
188                                                 fonttype: DWORD,
189                                                 lparam: LPARAM)
190                                                 -> c_int {
191        if fonttype != 4 {
192            return 1;
193        }
194
195        if ((*lpelfe).lfPitchAndFamily & FIXED_PITCH as u8) == 0 {
196            return 1;
197        }
198        add_vec(lpelfe, lparam);
199
200        1
201    }
202
203    unsafe fn add_vec(lpelfe: *const LOGFONTW, lparam: LPARAM) {
204        let lpelfe = lpelfe as *const ENUMLOGFONTEXW;
205
206        let name_array = (*lpelfe).elfFullName;
207        let pos = name_array.iter().position(|c| *c == 0).unwrap();
208        let name_array = &name_array[0..pos];
209
210        let name = OsString::from_wide(name_array).into_string().unwrap();
211
212        if name.chars().next() != Some('@') {
213            let vec_pointer = lparam as *mut Vec<String>;
214            let ref mut fonts = *vec_pointer;
215            fonts.push(name);
216        }
217    }
218
219    #[allow(non_snake_case)]
220    unsafe extern "system" fn callback_native(lpelfe: *const LOGFONTW,
221                                              _: *const TEXTMETRICW,
222                                              fonttype: DWORD,
223                                              lparam: LPARAM)
224                                              -> c_int {
225
226        if fonttype != 4 {
227            return 1;
228        }
229
230        ptr::copy(lpelfe, lparam as *mut _, 1);
231
232        0
233    }
234
235}