fontconfig/
fontset.rs

1//!
2use std::ptr::NonNull;
3
4use fontconfig_sys as sys;
5
6use sys::ffi_dispatch;
7
8#[cfg(feature = "dlopen")]
9use sys::statics::LIB;
10#[cfg(not(feature = "dlopen"))]
11use sys::*;
12
13#[allow(deprecated)]
14use crate::Blanks;
15
16use crate::{FcTrue, OwnedPattern, Pattern};
17
18/// Wrapper around `FcFontSet`.
19#[doc(alias = "FcFontSet")]
20#[repr(transparent)]
21pub struct FontSet<'pat> {
22    pub(crate) fcset: NonNull<sys::FcFontSet>,
23    pub(crate) _marker: ::std::marker::PhantomData<&'pat mut Pattern>,
24}
25
26impl<'pat> FontSet<'pat> {
27    /// Create an empty [`FontSet`].
28    #[doc(alias = "FcFontSetCreate")]
29    pub fn new() -> FontSet<'pat> {
30        let fcset = unsafe { ffi_dispatch!(LIB, FcFontSetCreate,) };
31        FontSet {
32            fcset: NonNull::new(fcset).unwrap(),
33            _marker: ::std::marker::PhantomData,
34        }
35    }
36
37    /// Add a [`Pattern`] to this [`FontSet`].
38    #[doc(alias = "FcFontSetAdd")]
39    pub fn push(&mut self, pat: OwnedPattern) {
40        unsafe {
41            assert_eq!(
42                ffi_dispatch!(LIB, FcFontSetAdd, self.as_mut_ptr(), pat.into_inner()),
43                FcTrue,
44            );
45        }
46    }
47
48    /// How many fonts are in this [`FontSet`]
49    #[doc(alias = "FcFontSet->nfont")]
50    pub fn len(&self) -> usize {
51        unsafe { (*self.as_ptr()).nfont as _ }
52    }
53
54    /// If there are no fonts in this [`FontSet`], return `true`.
55    pub fn is_empty(&self) -> bool {
56        self.len() == 0
57    }
58
59    /// Print this [`FontSet`] to stdout.
60    #[doc(alias = "FcFontSetPrint")]
61    pub fn print(&mut self) {
62        unsafe { ffi_dispatch!(LIB, FcFontSetPrint, self.as_mut_ptr()) };
63    }
64
65    /// Iterate the fonts (as `Patterns`) in this [`FontSet`].
66    pub fn iter<'fs>(&'fs self) -> Iter<'fs, 'pat> {
67        Iter {
68            fcset: self,
69            index: 0,
70        }
71    }
72
73    /// Iterate the fonts (as `Patterns`) in this [`FontSet`].
74    pub fn iter_mut<'fs>(&'fs mut self) -> IterMut<'fs, 'pat> {
75        IterMut {
76            fcset: self,
77            index: 0,
78        }
79    }
80
81    /// Compute all patterns from font file (and index)
82    ///
83    /// Constructs patterns found in 'file'.
84    /// If id is -1, then all patterns found in 'file' are added to 'set'.
85    /// Otherwise, this function works exactly like FcFreeTypeQuery().
86    /// The number of faces in 'file' is returned in 'count'.
87    /// The number of patterns added to 'set' is returned.
88    /// [`Blanks`] is deprecated, blanks is ignored and accepted only for compatibility with older code.
89    #[doc(alias = "FcFreeTypeQuery")]
90    #[allow(deprecated)]
91    pub fn freetype_query_all(
92        &mut self,
93        _file: &std::path::Path,
94        _index: isize,
95        _blanks: Option<&mut Blanks>,
96        _count: Option<&mut usize>,
97    ) -> usize {
98        unimplemented!()
99        // unsafe {
100        //     let blanks = blanks.map(|s| s.as_mut_ptr()).unwrap_or(std::ptr::null());
101        //     assert_eq!(
102        //         ffi_dispatch!(
103        //             LIB,
104        //             FcFreeTypeQueryAll,
105        //             file.as_ptr(),
106        //             index,
107        //             blanks,
108        //             ptr::null_mut(),
109        //             self.as_mut_ptr()
110        //         ),
111        //         FcTrue,
112        //     );
113        // }
114    }
115
116    fn as_mut_ptr(&mut self) -> *mut sys::FcFontSet {
117        self.fcset.as_ptr()
118    }
119
120    fn as_ptr(&self) -> *const sys::FcFontSet {
121        self.fcset.as_ptr()
122    }
123}
124
125impl<'a> Default for FontSet<'a> {
126    fn default() -> FontSet<'a> {
127        FontSet::new()
128    }
129}
130
131impl Drop for FontSet<'_> {
132    fn drop(&mut self) {
133        unsafe { ffi_dispatch!(LIB, FcFontSetDestroy, self.as_mut_ptr()) }
134    }
135}
136
137/// Iterator over the fonts in a [`FontSet`].
138pub struct Iter<'fs, 'pat> {
139    fcset: &'fs FontSet<'pat>,
140    index: usize,
141}
142
143impl<'fs, 'pat> Iterator for Iter<'fs, 'pat> {
144    type Item = &'pat Pattern;
145
146    fn next(&mut self) -> Option<Self::Item> {
147        let fcset = self.fcset.as_ptr();
148        let index = self.index;
149        self.index += 1;
150        if index >= unsafe { (*fcset).nfont } as usize {
151            return None;
152        }
153        let pat = unsafe {
154            let font = (*fcset).fonts.add(index);
155            if font.is_null() {
156                return None;
157            }
158            *font
159        };
160        if pat.is_null() {
161            return None;
162        }
163        Some(unsafe { &*(pat as *const sys::FcPattern as *const Pattern) })
164    }
165}
166
167/// Iterator over the fonts in a [`FontSet`].
168pub struct IterMut<'fs, 'pat> {
169    fcset: &'fs mut FontSet<'pat>,
170    index: usize,
171}
172
173impl<'fs, 'pat> Iterator for IterMut<'fs, 'pat> {
174    type Item = &'pat mut Pattern;
175
176    fn next(&mut self) -> Option<Self::Item> {
177        let index = self.index;
178        let fcset = self.fcset.as_ptr();
179        self.index += 1;
180        if index >= unsafe { (*fcset).nfont } as usize {
181            return None;
182        }
183        let pat = unsafe {
184            let font = (*fcset).fonts.add(index);
185            if font.is_null() {
186                return None;
187            }
188            *font
189        };
190        if pat.is_null() {
191            return None;
192        }
193        Some(unsafe { &mut *(pat as *mut sys::FcPattern as *mut Pattern) })
194    }
195}
196
197#[cfg(test)]
198mod tests {
199    use super::*;
200    use crate::pattern::{properties, OwnedPattern};
201
202    #[test]
203    fn fontset_new() {
204        let fontset = FontSet::new();
205        assert_eq!(fontset.len(), 0);
206    }
207
208    #[test]
209    fn fontset_iter() {
210        let mut fontset = FontSet::new();
211        let mut pat = OwnedPattern::default();
212        pat.add(&properties::FC_FAMILY, "sans-serif".to_string());
213        fontset.push(pat);
214        assert_eq!(fontset.len(), 1);
215        let mut c = 0;
216        for pat in fontset.iter() {
217            c += 1;
218            // pat.add(&properties::FC_DPI, 10.);  // this should be failed.
219            assert_eq!(pat.get(&properties::FC_FAMILY, 0), Some("sans-serif"));
220        }
221
222        assert_eq!(c, fontset.len());
223    }
224
225    #[test]
226    fn fontset_iter_mut() {
227        let mut fontset = FontSet::new();
228        let mut pat = OwnedPattern::new();
229        pat.add(&properties::FC_FAMILY, "sans-serif".to_string());
230        fontset.push(pat);
231        assert_eq!(fontset.len(), 1);
232        let mut c = 0;
233        for pat in fontset.iter_mut() {
234            c += 1;
235            assert_eq!(pat.get(&properties::FC_DPI, 0), None);
236            assert!(pat.add(&properties::FC_DPI, 20.));
237            assert_eq!(pat.get(&properties::FC_FAMILY, 0), Some("sans-serif"));
238            assert_eq!(pat.get(&properties::FC_DPI, 0), Some(20.));
239        }
240
241        assert_eq!(c, fontset.len());
242    }
243}