plotters_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, FF_ROMAN, FF_SWISS};
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                return self;
88            }
89           
90            match name {
91                "serif" => {
92                    self.config.lfPitchAndFamily |= FF_ROMAN as u8;
93                }
94                "serif-sans" => {
95                    self.config.lfPitchAndFamily |= FF_SWISS as u8;
96                }
97                "monospace" => {
98                    self.config.lfPitchAndFamily |= FIXED_PITCH as u8;
99                }
100                _ => {
101                    let name: &OsStr = name.as_ref();
102                    let buffer = name.encode_wide();
103                    let mut string: [u16; 32] = [0; 32]; // +1 Null terminator
104                    for (index, item) in buffer.enumerate() {
105                        string[index] = item;
106                    }
107                    self.config.lfFaceName = string;
108                }
109            }
110            self
111        }
112
113        pub fn build(self) -> FontProperty {
114            self.config
115        }
116    }
117
118    /// Get the binary data and index of a specific font
119    /// Note that only truetype fonts are supported
120    pub fn get(config: &FontProperty) -> Option<(Vec<u8>, c_int)> {
121        unsafe {
122            let hdc = wingdi::CreateCompatibleDC(ptr::null_mut());
123            let hfont = wingdi::CreateFontIndirectW(config as *const LOGFONTW);
124            wingdi::SelectObject(hdc, hfont as *mut c_void);
125            let size = wingdi::GetFontData(hdc, 0, 0, ptr::null_mut(), 0);
126            if size == 0xFFFFFFFF {
127                wingdi::DeleteDC(hdc);
128                None
129            } else if size > 0 {
130                let mut buffer: Vec<u8> = vec![0; size as usize];
131                let pointer = buffer.first_mut().unwrap() as *mut _ as PVOID;
132                let size = wingdi::GetFontData(hdc, 0, 0, pointer, size);
133                buffer.set_len(size as usize);
134                wingdi::DeleteDC(hdc);
135                Some((buffer, 0))
136            } else {
137                wingdi::DeleteDC(hdc);
138                None
139            }
140        }
141    }
142
143    pub fn get_native(config: &mut FontProperty) -> FontProperty {
144        let f: FONTENUMPROCW = Some(callback_native);
145        unsafe {
146            let mut logfont: LOGFONTW = mem::zeroed();
147            let pointer = &mut logfont as *mut _;
148            let hdc = wingdi::CreateCompatibleDC(ptr::null_mut());
149            wingdi::EnumFontFamiliesExW(hdc, config, f, pointer as LPARAM, 0);
150            wingdi::DeleteDC(hdc);
151            logfont
152        }
153    }
154
155    /// Query the names of all fonts installed in the system
156    /// Note that only truetype fonts are supported
157    pub fn query_all() -> Vec<String> {
158        let mut config = FontPropertyBuilder::new().build();
159        query_specific(&mut config)
160    }
161
162    /// Query the names of specifc fonts installed in the system
163    /// Note that only truetype fonts are supported
164    pub fn query_specific(property: &mut FontProperty) -> Vec<String> {
165
166        let mut fonts = Vec::new();
167        let mut f: FONTENUMPROCW = Some(callback_ttf);
168        unsafe {
169            let hdc = wingdi::CreateCompatibleDC(ptr::null_mut());
170
171            if (property.lfPitchAndFamily & FIXED_PITCH as u8) != 0 {
172                f = Some(callback_monospace);
173            }
174
175            let vec_pointer = &mut fonts as *mut Vec<String>;
176
177            wingdi::EnumFontFamiliesExW(hdc, property, f, vec_pointer as LPARAM, 0);
178            wingdi::DeleteDC(hdc);
179        }
180        fonts
181    }
182
183    #[allow(non_snake_case)]
184    unsafe extern "system" fn callback_ttf(lpelfe: *const LOGFONTW,
185                                           _: *const TEXTMETRICW,
186                                           fonttype: DWORD,
187                                           lparam: LPARAM)
188                                           -> c_int {
189
190        if fonttype != 4 {
191            return 1;
192        }
193
194        add_vec(lpelfe, lparam);
195
196        1
197    }
198
199    #[allow(non_snake_case)]
200    unsafe extern "system" fn callback_monospace(lpelfe: *const LOGFONTW,
201                                                 _: *const TEXTMETRICW,
202                                                 fonttype: DWORD,
203                                                 lparam: LPARAM)
204                                                 -> c_int {
205        if fonttype != 4 {
206            return 1;
207        }
208
209        if ((*lpelfe).lfPitchAndFamily & FIXED_PITCH as u8) == 0 {
210            return 1;
211        }
212        add_vec(lpelfe, lparam);
213
214        1
215    }
216
217    unsafe fn add_vec(lpelfe: *const LOGFONTW, lparam: LPARAM) {
218        let lpelfe = lpelfe as *const ENUMLOGFONTEXW;
219
220        let name_array = (*lpelfe).elfFullName;
221        let pos = name_array.iter().position(|c| *c == 0).unwrap();
222        let name_array = &name_array[0..pos];
223
224        let name = OsString::from_wide(name_array).into_string().unwrap();
225
226        if name.chars().next() != Some('@') {
227            let vec_pointer = lparam as *mut Vec<String>;
228            let ref mut fonts = *vec_pointer;
229            fonts.push(name);
230        }
231    }
232
233    #[allow(non_snake_case)]
234    unsafe extern "system" fn callback_native(lpelfe: *const LOGFONTW,
235                                              _: *const TEXTMETRICW,
236                                              fonttype: DWORD,
237                                              lparam: LPARAM)
238                                              -> c_int {
239
240        if fonttype != 4 {
241            return 1;
242        }
243
244        ptr::copy(lpelfe, lparam as *mut _, 1);
245
246        0
247    }
248
249}