fontconfig/
pattern.rs

1//!
2use std::borrow::{Borrow, BorrowMut};
3use std::ffi::{CStr, CString};
4
5use std::marker::PhantomData;
6use std::ops::{Deref, DerefMut};
7use std::os::raw::c_char;
8use std::ptr::{self, NonNull};
9use std::str::FromStr;
10
11///!
12use fontconfig_sys as sys;
13
14#[cfg(feature = "dlopen")]
15use sys::statics::LIB;
16#[cfg(not(feature = "dlopen"))]
17use sys::*;
18
19use sys::constants::*;
20use sys::{ffi_dispatch, FcPattern};
21
22use crate::charset::OwnedCharSet;
23use crate::{
24    CharSet, Error, FcFalse, FcStr, FcTrue, FontConfig, FontFormat, FontSet, LangSet, Matrix,
25    ObjectSet, Result, ToResult,
26};
27
28/// Representation of a borrowed fontconfig's [`sys::FcPattern`].
29///
30/// An `Pattern` is an opaque type that holds both patterns to match against the available fonts, as well as the information about each font.
31#[doc(alias = "FcPattern")]
32#[repr(transparent)]
33pub struct Pattern(FcPattern);
34
35/// A type representing an owned fontconfig's [`sys::FcPattern`].
36#[doc(alias = "FcPattern")]
37#[repr(transparent)]
38pub struct OwnedPattern {
39    /// Raw pointer to `FcPattern`
40    pub(crate) pat: NonNull<FcPattern>,
41}
42
43impl OwnedPattern {
44    /// Create a new empty [`OwnedPattern`].
45    pub fn new() -> OwnedPattern {
46        let pat = unsafe { ffi_dispatch!(LIB, FcPatternCreate,) };
47        assert!(!pat.is_null());
48
49        OwnedPattern {
50            pat: NonNull::new(pat).expect("out of memory"),
51        }
52    }
53
54    pub(crate) fn into_inner(self) -> *mut FcPattern {
55        let ptr = self.pat.as_ptr() as *mut FcPattern;
56        std::mem::forget(self);
57        ptr
58    }
59}
60
61impl Pattern {
62    /// Delete a property from a pattern
63    pub fn del(&mut self, name: &CStr) -> bool {
64        FcTrue == unsafe { ffi_dispatch!(LIB, FcPatternDel, self.as_mut_ptr(), name.as_ptr()) }
65    }
66
67    /// Print this pattern to stdout with all its values.
68    pub fn print(&self) {
69        unsafe {
70            ffi_dispatch!(LIB, FcPatternPrint, self.as_ptr());
71        }
72    }
73
74    /// Filter the objects of pattern
75    ///
76    /// Returns a new pattern that only has those objects from `self` that are in os.
77    /// If os is None, a duplicate of `self` is returned.
78    pub fn filter(&self, os: Option<&mut ObjectSet>) -> Option<OwnedPattern> {
79        let os = os.map(|o| o.as_mut_ptr()).unwrap_or(ptr::null_mut());
80        let pat = unsafe {
81            let pat = ffi_dispatch!(LIB, FcPatternFilter, self.as_ptr() as *mut FcPattern, os);
82            if pat.is_null() {
83                return None;
84            }
85            pat
86        };
87        NonNull::new(pat).map(|pat| OwnedPattern { pat })
88    }
89
90    /// Format a pattern into a string according to a format specifier
91    ///
92    /// See: [pattern-format](https://www.freedesktop.org/software/fontconfig/fontconfig-devel/fcpatternformat.html)
93    pub fn format(&self, fmt: &CStr) -> Option<FcStr> {
94        unsafe {
95            let s = ffi_dispatch!(
96                LIB,
97                FcPatternFormat,
98                self.as_ptr() as *mut FcPattern,
99                fmt.as_ptr() as *const u8
100            );
101            FcStr::from_ptr(s)
102        }
103    }
104
105    /// Perform default substitutions in a pattern
106    ///
107    /// Supplies default values for underspecified font patterns:
108    ///
109    /// * Patterns without a specified style or weight are set to Medium   
110    /// * Patterns without a specified style or slant are set to Roman   
111    /// * Patterns without a specified pixel size are given one computed from any specified point size (default 12), dpi (default 75) and scale (default 1).  
112    pub fn default_substitute(&mut self) {
113        unsafe {
114            ffi_dispatch!(LIB, FcDefaultSubstitute, self.as_mut_ptr());
115        }
116    }
117
118    /// Get the best available match for this pattern, returned as a new pattern.
119    ///
120    /// Finds the font in sets most closely matching pattern and returns the result of [`Pattern::render_prepare`] for that font and the provided pattern.   
121    /// This function should be called only after [`FontConfig::substitute`] and [`Pattern::default_substitute`] have been called for the pattern.    
122    /// otherwise the results will not be correct.
123    #[doc(alias = "FcFontMatch")]
124    pub fn font_match(&mut self, config: &mut FontConfig) -> OwnedPattern {
125        // self.default_substitute();
126        // config.substitute(self, MatchKind::Pattern);
127
128        unsafe {
129            let mut res = sys::FcResultNoMatch;
130            let pat = ffi_dispatch!(
131                LIB,
132                FcFontMatch,
133                config.as_mut_ptr(),
134                self.as_mut_ptr(),
135                &mut res
136            );
137            res.ok().unwrap();
138            OwnedPattern {
139                pat: NonNull::new(pat).unwrap(),
140            }
141        }
142    }
143
144    /// List fonts
145    ///
146    /// Selects fonts matching `self`,
147    /// creates patterns from those fonts containing only the objects in os and returns the set of unique such patterns.    
148    pub fn font_list(&self, config: &mut FontConfig, os: Option<&mut ObjectSet>) -> FontSet<'_> {
149        let os = os.map(|o| o.as_mut_ptr()).unwrap_or(ptr::null_mut());
150        let set = unsafe {
151            ffi_dispatch!(
152                LIB,
153                FcFontList,
154                config.as_mut_ptr(),
155                self.as_ptr() as *mut _,
156                os
157            )
158        };
159        // NOTE: Referenced by FontSet, should not drop it.
160        FontSet {
161            fcset: NonNull::new(set).unwrap(),
162            _marker: PhantomData,
163        }
164    }
165
166    /// Get the list of fonts sorted by closeness to self.
167    ///
168    /// If trim is `true`, elements in the list which don't include Unicode coverage not provided by earlier elements in the list are elided.    
169    /// This function should be called only after [`FontConfig::substitute`] and [`Pattern::default_substitute`] have been called for this pattern;    
170    /// otherwise the results will not be correct.
171    pub fn font_sort(&mut self, config: &mut FontConfig, trim: bool) -> Result<FontSet<'static>> {
172        unsafe {
173            // What is this result actually used for? Seems redundant with
174            // return type.
175            let mut res = sys::FcResultNoMatch;
176
177            let mut charsets: *mut _ = ptr::null_mut();
178
179            let fcset = ffi_dispatch!(
180                LIB,
181                FcFontSort,
182                config.as_mut_ptr(),
183                self.as_ptr() as *mut _,
184                if trim { FcTrue } else { FcFalse }, // Trim font list.
185                &mut charsets,
186                &mut res
187            );
188            res.ok()?;
189            if fcset.is_null() {
190                return Err(Error::OutOfMemory);
191            }
192            let fcset = NonNull::new_unchecked(fcset);
193            Ok(FontSet {
194                fcset,
195                _marker: PhantomData,
196            })
197        }
198    }
199
200    /// Get the list of fonts sorted by closeness to self.
201    ///
202    /// If trim is `true`, elements in the list which don't include Unicode coverage not provided by earlier elements in the list are elided.    
203    /// The union of Unicode coverage of all of the fonts is returned in [`CharSet`].   
204    /// This function should be called only after [`FontConfig::substitute`] and [`Pattern::default_substitute`] have been called for this pattern;    
205    /// otherwise the results will not be correct.
206    pub fn font_sort_with_charset(
207        &mut self,
208        config: &mut FontConfig,
209        trim: bool,
210    ) -> Option<(FontSet<'_>, OwnedCharSet)> {
211        // self.default_substitute();
212        // config.substitute(self, MatchKind::Pattern);
213        unsafe {
214            // What is this result actually used for? Seems redundant with
215            // return type.
216            let mut res = sys::FcResultNoMatch;
217
218            let mut charsets: *mut _ = ptr::null_mut();
219
220            let fcset = ffi_dispatch!(
221                LIB,
222                FcFontSort,
223                config.as_mut_ptr(),
224                self.as_mut_ptr(),
225                if trim { FcTrue } else { FcFalse }, // Trim font list.
226                &mut charsets,
227                &mut res
228            );
229            res.opt()?;
230            Some((
231                FontSet {
232                    fcset: NonNull::new(fcset).unwrap(),
233                    _marker: PhantomData,
234                },
235                OwnedCharSet {
236                    fcset: NonNull::new(charsets).unwrap(),
237                },
238            ))
239        }
240    }
241
242    /// Prepare pattern for loading font file.
243    ///
244    /// Creates a new pattern consisting of elements of font not appearing in pat,
245    /// elements of pat not appearing in font and the best matching value from pat for elements appearing in both.    
246    /// The result is passed to [`FontConfig::substitute_with_pat`] with kind [`crate::MatchKind::Font`] and then returned.
247    #[doc(alias = "FcFontRenderPrepare")]
248    pub fn render_prepare(&mut self, config: &mut FontConfig, font: &mut Self) -> OwnedPattern {
249        let pat = unsafe {
250            ffi_dispatch!(
251                LIB,
252                FcFontRenderPrepare,
253                config.as_mut_ptr(),
254                self.as_mut_ptr(),
255                font.as_mut_ptr()
256            )
257        };
258        OwnedPattern {
259            pat: NonNull::new(pat).unwrap(),
260        }
261    }
262
263    /// Get character map
264    #[doc(alias = "FcPatternGetCharSet")]
265    pub fn charset(&self) -> Option<&CharSet> {
266        unsafe {
267            let mut charsets = ffi_dispatch!(LIB, FcCharSetCreate,);
268            ffi_dispatch!(
269                LIB,
270                FcPatternGetCharSet,
271                self.as_ptr() as *mut _,
272                FC_CHARSET.as_ptr(),
273                0,
274                &mut charsets
275            );
276            if charsets.is_null() {
277                None
278            } else {
279                Some(&*(charsets as *const CharSet))
280            }
281        }
282    }
283
284    /// Get the "fullname" (human-readable name) of this pattern.
285    pub fn name(&self) -> Option<&str> {
286        self.get(&properties::FC_FULLNAME, 0)
287    }
288
289    /// Get the "file" (path on the filesystem) of this font pattern.
290    pub fn filename(&self) -> Option<&str> {
291        self.get(&properties::FC_FILE, 0)
292    }
293
294    /// Get the "index" (The index of the font within the file) of this pattern.
295    pub fn face_index(&self) -> Option<i32> {
296        self.get(&properties::FC_INDEX, 0)
297    }
298
299    /// Get the "slant" (Italic, oblique or roman) of this pattern.
300    pub fn slant(&self) -> Option<i32> {
301        self.get(&properties::FC_SLANT, 0)
302    }
303
304    /// Get the "weight" (Light, medium, demibold, bold or black) of this pattern.
305    pub fn weight(&self) -> Option<i32> {
306        self.get(&properties::FC_WEIGHT, 0)
307    }
308
309    /// Get the "width" (Condensed, normal or expanded) of this pattern.
310    pub fn width(&self) -> Option<i32> {
311        self.get(&properties::FC_WIDTH, 0)
312    }
313
314    /// Get the "fontformat" ("TrueType" "Type 1" "BDF" "PCF" "Type 42" "CID Type 1" "CFF" "PFR" "Windows FNT") of this pattern.
315    pub fn fontformat(&self) -> Result<FontFormat> {
316        self.get(&properties::FC_FONTFORMAT, 0)
317            .ok_or_else(|| Error::UnknownFontFormat(String::new()))
318            .and_then(|format| format.parse())
319    }
320
321    ///
322    pub fn hash(&self) -> u32 {
323        unsafe { ffi_dispatch!(LIB, FcPatternHash, self.as_ptr() as *mut _) }
324    }
325
326    /// Returns a raw pointer to underlying [`sys::FcPattern`].
327    pub(crate) fn as_ptr(&self) -> *const FcPattern {
328        self as *const _ as *const FcPattern
329    }
330
331    /// Returns an unsafe mutable pointer to the underlying [`sys::FcPattern`].
332    pub(crate) fn as_mut_ptr(&mut self) -> *mut FcPattern {
333        self as *mut _ as *mut FcPattern
334    }
335}
336
337impl ToOwned for Pattern {
338    type Owned = OwnedPattern;
339
340    fn to_owned(&self) -> OwnedPattern {
341        OwnedPattern {
342            pat: NonNull::new(unsafe { ffi_dispatch!(LIB, FcPatternDuplicate, self.as_ptr()) })
343                .unwrap(),
344        }
345    }
346}
347
348impl Borrow<Pattern> for OwnedPattern {
349    fn borrow(&self) -> &Pattern {
350        unsafe { &*(self.as_ptr() as *const Pattern) }
351    }
352}
353
354impl BorrowMut<Pattern> for OwnedPattern {
355    fn borrow_mut(&mut self) -> &mut Pattern {
356        unsafe { &mut *(self.as_mut_ptr() as *mut Pattern) }
357    }
358}
359
360impl FromStr for OwnedPattern {
361    type Err = Error;
362    /// Converts `name` from the standard text format described above into a pattern.
363    fn from_str(s: &str) -> Result<Self> {
364        let c_str = CString::new(s).unwrap();
365        unsafe {
366            let pat = ffi_dispatch!(LIB, FcNameParse, c_str.as_ptr().cast());
367            if let Some(pat) = NonNull::new(pat) {
368                Ok(OwnedPattern { pat })
369            } else {
370                Err(Error::OutOfMemory)
371            }
372        }
373    }
374}
375
376impl std::fmt::Debug for Pattern {
377    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
378        let fcstr = unsafe { ffi_dispatch!(LIB, FcNameUnparse, self.as_ptr() as *mut FcPattern) };
379        let fcstr = unsafe { CStr::from_ptr(fcstr as *const c_char) };
380        let result = write!(f, "{:?}", fcstr);
381        unsafe { ffi_dispatch!(LIB, FcStrFree, fcstr.as_ptr() as *mut u8) };
382        result
383    }
384}
385
386impl Clone for OwnedPattern {
387    fn clone(&self) -> Self {
388        let cloned = unsafe { ffi_dispatch!(LIB, FcPatternDuplicate, self.pat.as_ptr()) };
389        OwnedPattern {
390            pat: NonNull::new(cloned).unwrap(),
391        }
392    }
393}
394
395impl Drop for OwnedPattern {
396    fn drop(&mut self) {
397        unsafe {
398            ffi_dispatch!(LIB, FcPatternDestroy, self.pat.as_ptr());
399        }
400    }
401}
402
403impl Deref for OwnedPattern {
404    type Target = Pattern;
405
406    fn deref(&self) -> &Self::Target {
407        unsafe { &*(self.pat.as_ptr() as *const _) }
408    }
409}
410
411impl DerefMut for OwnedPattern {
412    fn deref_mut(&mut self) -> &mut Self::Target {
413        unsafe { &mut *(self.pat.as_ptr() as *mut _) }
414    }
415}
416
417impl AsRef<Pattern> for OwnedPattern {
418    fn as_ref(&self) -> &Pattern {
419        self
420    }
421}
422
423impl AsMut<Pattern> for OwnedPattern {
424    fn as_mut(&mut self) -> &mut Pattern {
425        self
426    }
427}
428
429impl Pattern {
430    /// Get the languages set of this pattern.
431    pub fn lang_set(&self) -> Option<LangSet> {
432        // let mut langset = LangSet::new();
433        let langset = unsafe {
434            let mut langset = ffi_dispatch!(LIB, FcLangSetCreate,);
435            ffi_dispatch!(
436                LIB,
437                FcPatternGetLangSet,
438                self.as_ptr() as *mut _,
439                FC_LANG.as_ptr(),
440                0,
441                &mut langset
442            )
443            .opt()?;
444            ffi_dispatch!(LIB, FcLangSetCopy, langset)
445        };
446        NonNull::new(langset).map(|langset| LangSet { langset })
447    }
448
449    /// Get the matrix from this pattern.
450    pub fn matrix(&mut self) -> Option<&Matrix> {
451        let mut matrix = ptr::null_mut();
452        unsafe {
453            ffi_dispatch!(
454                LIB,
455                FcPatternGetMatrix,
456                self.as_mut_ptr(),
457                FC_MATRIX.as_ptr(),
458                0,
459                &mut matrix
460            )
461            .opt()?;
462            if matrix.is_null() {
463                None
464            } else {
465                Some(&*(matrix as *mut crate::Matrix))
466            }
467        }
468    }
469
470    ///
471    pub fn get<'a, 'pat, V>(
472        &'pat self,
473        name: &'a properties::Property<'pat, V>,
474        index: usize,
475    ) -> Option<V::Returns>
476    where
477        V: properties::PropertyType<'pat>,
478    {
479        name.value_of(self, index)
480    }
481
482    ///
483    pub fn add<'a, 'pat, V>(
484        &'pat mut self,
485        name: &'a properties::Property<'pat, V>,
486        value: V,
487    ) -> bool
488    where
489        V: properties::PropertyType<'pat>,
490    {
491        name.value_for(self, value)
492    }
493}
494
495impl Default for OwnedPattern {
496    fn default() -> Self {
497        let mut pat = OwnedPattern::new();
498        pat.default_substitute();
499        pat
500    }
501}
502
503///
504pub mod properties {
505    use std::ffi::CStr;
506    use std::marker::PhantomData;
507    use std::ptr::NonNull;
508
509    use fontconfig_sys as sys;
510
511    #[cfg(feature = "dlopen")]
512    use sys::statics::LIB;
513    #[cfg(not(feature = "dlopen"))]
514    use sys::*;
515
516    use sys::ffi_dispatch;
517
518    use crate::{FcFalse, FcTrue, ToResult};
519
520    use super::Pattern;
521
522    ///
523    pub struct Property<'pat, V: PropertyType<'pat>> {
524        name: const_cstr::ConstCStr,
525        val: PhantomData<&'pat V>,
526    }
527
528    impl<'pat, V> Property<'pat, V>
529    where
530        V: PropertyType<'pat>,
531    {
532        pub(super) fn value_of<'a>(
533            &'a self,
534            pat: &'pat Pattern,
535            index: usize,
536        ) -> Option<V::Returns> {
537            V::value(pat, self, index)
538        }
539
540        pub(super) fn value_for<'a>(&'a self, pat: &'pat mut Pattern, value: V) -> bool {
541            value.set_to(pat, self)
542        }
543    }
544
545    mod private {
546        use crate::Pattern;
547
548        use super::{Property, PropertyType};
549
550        pub trait MaybeRef<'a> {
551            type Returns;
552        }
553
554        pub trait Sealed<'pat>: Sized + MaybeRef<'pat> {
555            fn value<'a>(
556                pat: &'pat Pattern,
557                property: &'a Property<'pat, Self>,
558                index: usize,
559            ) -> Option<<Self as MaybeRef<'pat>>::Returns>
560            where
561                Self: PropertyType<'pat>;
562            fn set_to<'a>(self, pat: &'pat mut Pattern, property: &'a Property<'pat, Self>) -> bool
563            where
564                Self: PropertyType<'pat>;
565        }
566    }
567
568    ///
569    pub trait PropertyType<'pat>: private::Sealed<'pat> {}
570
571    impl<'pat, T> PropertyType<'pat> for T where T: private::Sealed<'pat> {}
572
573    impl<'pat> private::MaybeRef<'pat> for String {
574        type Returns = &'pat str;
575    }
576
577    impl<'pat> private::Sealed<'pat> for String {
578        fn value<'a>(
579            pat: &'pat Pattern,
580            name: &'a Property<'pat, Self>,
581            index: usize,
582        ) -> Option<Self::Returns> {
583            let c_str = unsafe {
584                let mut ret: *mut sys::FcChar8 = std::ptr::null_mut();
585                ffi_dispatch!(
586                    LIB,
587                    FcPatternGetString,
588                    pat.as_ptr() as *mut _,
589                    name.name.as_ptr(),
590                    index as i32,
591                    &mut ret
592                )
593                .opt()?;
594                if ret.is_null() {
595                    return None;
596                }
597                CStr::from_ptr(ret as *const _)
598            };
599            c_str.to_str().ok()
600        }
601
602        fn set_to<'a>(mut self, pat: &'pat mut Pattern, name: &'a Property<'pat, Self>) -> bool {
603            self.push('\0');
604            let c_str = CStr::from_bytes_with_nul(self.as_bytes()).unwrap();
605            FcTrue
606                == unsafe {
607                    ffi_dispatch!(
608                        LIB,
609                        FcPatternAddString,
610                        pat.as_mut_ptr(),
611                        name.name.as_ptr(),
612                        c_str.as_ptr() as *mut _
613                    )
614                }
615        }
616    }
617
618    impl<'a> private::MaybeRef<'a> for i32 {
619        type Returns = i32;
620    }
621
622    impl<'a> private::Sealed<'a> for i32 {
623        fn value(pat: &Pattern, name: &Property<Self>, index: usize) -> Option<Self::Returns> {
624            let mut val: i32 = 0;
625            unsafe {
626                ffi_dispatch!(
627                    LIB,
628                    FcPatternGetInteger,
629                    pat.as_ptr() as *mut _,
630                    name.name.as_ptr(),
631                    index as i32,
632                    &mut val
633                )
634                .opt()?;
635            };
636            Some(val)
637        }
638
639        fn set_to(self, pat: &mut Pattern, property: &Property<Self>) -> bool {
640            FcTrue
641                == unsafe {
642                    ffi_dispatch!(
643                        LIB,
644                        FcPatternAddInteger,
645                        pat.as_mut_ptr(),
646                        property.name.as_ptr(),
647                        self
648                    )
649                }
650        }
651    }
652
653    impl<'a> private::MaybeRef<'a> for bool {
654        type Returns = bool;
655    }
656
657    impl<'a> private::Sealed<'a> for bool {
658        fn value(pat: &Pattern, name: &Property<Self>, index: usize) -> Option<Self::Returns> {
659            let mut val: i32 = 0;
660            unsafe {
661                ffi_dispatch!(
662                    LIB,
663                    FcPatternGetBool,
664                    pat.as_ptr() as *mut _,
665                    name.name.as_ptr(),
666                    index as i32,
667                    &mut val
668                )
669                .opt()?;
670            };
671            Some(val == FcTrue)
672        }
673
674        fn set_to(self, pat: &mut Pattern, property: &Property<Self>) -> bool {
675            FcTrue
676                == unsafe {
677                    ffi_dispatch!(
678                        LIB,
679                        FcPatternAddBool,
680                        pat.as_mut_ptr(),
681                        property.name.as_ptr(),
682                        if self { FcTrue } else { FcFalse }
683                    )
684                }
685        }
686    }
687
688    impl<'a> private::MaybeRef<'a> for f64 {
689        type Returns = f64;
690    }
691
692    impl<'a> private::Sealed<'a> for f64 {
693        fn value(pat: &Pattern, name: &Property<Self>, index: usize) -> Option<Self::Returns> {
694            let mut val: f64 = 0.;
695            unsafe {
696                ffi_dispatch!(
697                    LIB,
698                    FcPatternGetDouble,
699                    pat.as_ptr() as *mut _,
700                    name.name.as_ptr(),
701                    index as i32,
702                    &mut val
703                )
704                .opt()?;
705            };
706            Some(val)
707        }
708
709        fn set_to(self, pat: &mut Pattern, property: &Property<Self>) -> bool {
710            FcTrue
711                == unsafe {
712                    ffi_dispatch!(
713                        LIB,
714                        FcPatternAddDouble,
715                        pat.as_mut_ptr(),
716                        property.name.as_ptr(),
717                        self
718                    )
719                }
720        }
721    }
722
723    impl<'a> private::MaybeRef<'a> for crate::Matrix {
724        type Returns = &'a crate::Matrix;
725    }
726
727    impl<'pat> private::Sealed<'pat> for crate::Matrix {
728        fn value(pat: &'pat Pattern, name: &Property<Self>, index: usize) -> Option<Self::Returns> {
729            let val = unsafe {
730                let mut val = std::ptr::null_mut();
731                ffi_dispatch!(
732                    LIB,
733                    FcPatternGetMatrix,
734                    pat.as_ptr() as *mut _,
735                    name.name.as_ptr(),
736                    index as i32,
737                    &mut val
738                )
739                .opt()?;
740                if val.is_null() {
741                    return None;
742                }
743                &*(val as *mut crate::Matrix)
744            };
745            Some(val)
746        }
747
748        fn set_to(self, pat: &mut Pattern, property: &Property<Self>) -> bool {
749            // Safety: It copy the matrix, so it is safe to use it as a mutable pointer.
750            FcTrue
751                == unsafe {
752                    ffi_dispatch!(
753                        LIB,
754                        FcPatternAddMatrix,
755                        pat.as_mut_ptr(),
756                        property.name.as_ptr(),
757                        &self.matrix
758                    )
759                }
760        }
761    }
762
763    impl<'a> private::MaybeRef<'a> for crate::OwnedCharSet {
764        type Returns = &'a crate::CharSet;
765    }
766
767    impl<'a> private::Sealed<'a> for crate::OwnedCharSet {
768        fn value(pat: &Pattern, name: &Property<Self>, index: usize) -> Option<Self::Returns> {
769            unsafe {
770                let mut val = std::ptr::null_mut();
771                ffi_dispatch!(
772                    LIB,
773                    FcPatternGetCharSet,
774                    pat.as_ptr() as *mut _,
775                    name.name.as_ptr(),
776                    index as i32,
777                    &mut val
778                )
779                .opt()?;
780                if val.is_null() {
781                    return None;
782                }
783                Some(&*(val as *const crate::CharSet))
784            }
785        }
786
787        fn set_to(self, pat: &mut Pattern, property: &Property<Self>) -> bool {
788            // unimplemented!("set &'CharSet to pattern is unsound.");
789            FcTrue
790                == unsafe {
791                    ffi_dispatch!(
792                        LIB,
793                        FcPatternAddCharSet,
794                        pat.as_mut_ptr(),
795                        property.name.as_ptr(),
796                        self.fcset.as_ptr()
797                    )
798                }
799        }
800    }
801
802    impl<'a> private::MaybeRef<'a> for crate::LangSet {
803        type Returns = crate::LangSet;
804    }
805
806    impl<'a> private::Sealed<'a> for crate::LangSet {
807        fn value(pat: &Pattern, name: &Property<Self>, index: usize) -> Option<Self::Returns> {
808            let val = unsafe {
809                let mut val = std::ptr::null_mut();
810                ffi_dispatch!(
811                    LIB,
812                    FcPatternGetLangSet,
813                    pat.as_ptr() as *mut _,
814                    name.name.as_ptr(),
815                    index as i32,
816                    &mut val
817                )
818                .opt()?;
819                ffi_dispatch!(LIB, FcLangSetCopy, val)
820            };
821            NonNull::new(val).map(|langset| crate::LangSet { langset })
822        }
823
824        fn set_to(self, pat: &mut Pattern, property: &Property<Self>) -> bool {
825            FcTrue
826                == unsafe {
827                    ffi_dispatch!(
828                        LIB,
829                        FcPatternAddLangSet,
830                        pat.as_mut_ptr(),
831                        property.name.as_ptr(),
832                        self.langset.as_ptr()
833                    )
834                }
835        }
836    }
837
838    macro_rules! property_decl {
839        ($bytes:literal, $name:ident, $vtype:ty, $comment:literal) => {
840            /// $comment
841            pub const $name: Property<$vtype> = Property {
842                name: ::fontconfig_sys::constants::$name,
843                val: PhantomData,
844            };
845        };
846    }
847
848    property_decl!(b"family\0", FC_FAMILY, String, "Font family names");
849    property_decl!(
850        b"familylang\0",
851        FC_FAMILYLANG,
852        String,
853        "Language corresponding to each family name"
854    );
855    property_decl!(
856        b"style\0",
857        FC_STYLE,
858        String,
859        "Font style. Overrides weight and slant"
860    );
861    property_decl!(
862        b"stylelang\0",
863        FC_STYLELANG,
864        String,
865        "Language corresponding to each style name"
866    );
867    property_decl!(
868        b"fullname\0",
869        FC_FULLNAME,
870        String,
871        "Font face full name where different from family and family + style"
872    );
873    property_decl!(
874        b"fullnamelang\0",
875        FC_FULLNAMELANG,
876        String,
877        "Language corresponding to each fullname"
878    );
879    property_decl!(b"slant\0", FC_SLANT, i32, "Italic, oblique or roman");
880    property_decl!(
881        b"weight\0",
882        FC_WEIGHT,
883        i32,
884        "Light, medium, demibold, bold or black"
885    );
886    property_decl!(b"width\0", FC_WIDTH, i32, "Condensed, normal or expanded");
887    property_decl!(b"size\0", FC_SIZE, f64, "Point size");
888    property_decl!(
889        b"aspect\0",
890        FC_ASPECT,
891        f64,
892        "Stretches glyphs horizontally before hinting"
893    );
894    property_decl!(b"pixelsize\0", FC_PIXEL_SIZE, f64, "Pixel size");
895    property_decl!(
896        b"spacing\0",
897        FC_SPACING,
898        i32,
899        "Proportional, dual-width, monospace or charcell"
900    );
901    property_decl!(b"foundry\0", FC_FOUNDRY, String, "Font foundry name");
902    property_decl!(
903        b"antialias\0",
904        FC_ANTIALIAS,
905        bool,
906        "Whether glyphs can be antialiased"
907    );
908    property_decl!(
909        b"hintstyle\0",
910        FC_HINT_STYLE,
911        i32,
912        "Automatic hinting style"
913    );
914    property_decl!(
915        b"hinting\0",
916        FC_HINTING,
917        bool,
918        "Whether the rasterizer should use hinting"
919    );
920    property_decl!(
921        b"verticallayout\0",
922        FC_VERTICAL_LAYOUT,
923        bool,
924        "Use vertical layout"
925    );
926    property_decl!(
927        b"autohint\0",
928        FC_AUTOHINT,
929        bool,
930        "Use autohinter instead of normal hinter"
931    );
932    property_decl!(
933        b"globaladvance\0",
934        FC_GLOBAL_ADVANCE,
935        bool,
936        "Use font global advance data (deprecated)"
937    );
938    property_decl!(
939        b"file\0",
940        FC_FILE,
941        String,
942        "The filename holding the font relative to the config's sysroot"
943    );
944    property_decl!(
945        b"index\0",
946        FC_INDEX,
947        i32,
948        "The index of the font within the file"
949    );
950    // property_decl!(
951    //     b"ftface\0",
952    //     FC_FT_FACE,
953    //     FT_Face,
954    //     "Use the specified FreeType face object"
955    // );
956    property_decl!(
957        b"rasterizer\0",
958        FC_RASTERIZER,
959        String,
960        "Which rasterizer is in use (deprecated)"
961    );
962    property_decl!(
963        b"outline\0",
964        FC_OUTLINE,
965        bool,
966        "Whether the glyphs are outlines"
967    );
968    property_decl!(
969        b"scalable\0",
970        FC_SCALABLE,
971        bool,
972        "Whether glyphs can be scaled"
973    );
974    property_decl!(b"dpi\0", FC_DPI, f64, "Target dots per inch");
975    property_decl!(
976        b"rgba\0",
977        FC_RGBA,
978        i32,
979        "unknown, rgb, bgr, vrgb, vbgr, none - subpixel geometry"
980    );
981    property_decl!(
982        b"scale\0",
983        FC_SCALE,
984        f64,
985        "Scale factor for point->pixel conversions (deprecated)"
986    );
987    property_decl!(
988        b"minspace\0",
989        FC_MINSPACE,
990        bool,
991        "Eliminate leading from line spacing"
992    );
993    property_decl!(
994        b"charset\0",
995        FC_CHARSET,
996        crate::OwnedCharSet,
997        "Unicode chars encoded by the font"
998    );
999    property_decl!(
1000        b"lang\0",
1001        FC_LANG,
1002        crate::LangSet,
1003        "Set of RFC-3066-style languages this font supports"
1004    );
1005    property_decl!(
1006        b"fontversion\0",
1007        FC_FONTVERSION,
1008        i32,
1009        "Version number of the font"
1010    );
1011    property_decl!(
1012        b"capability\0",
1013        FC_CAPABILITY,
1014        String,
1015        "List of layout capabilities in the font"
1016    );
1017    property_decl!(
1018        b"fontformat\0",
1019        FC_FONTFORMAT,
1020        String,
1021        "String name of the font format"
1022    );
1023    property_decl!(
1024        b"embolden\0",
1025        FC_EMBOLDEN,
1026        bool,
1027        "Rasterizer should synthetically embolden the font"
1028    );
1029    property_decl!(
1030        b"embeddedbitmap\0",
1031        FC_EMBEDDED_BITMAP,
1032        bool,
1033        "Use the embedded bitmap instead of the outline"
1034    );
1035    property_decl!(
1036        b"decorative\0",
1037        FC_DECORATIVE,
1038        bool,
1039        "Whether the style is a decorative variant"
1040    );
1041    property_decl!(b"lcdfilter\0", FC_LCD_FILTER, i32, "Type of LCD filter");
1042    property_decl!(
1043        b"namelang\0",
1044        FC_NAMELANG,
1045        String,
1046        "Language name to be used for the default value of familylang, stylelang and fullnamelang"
1047    );
1048    property_decl!(
1049        b"fontfeatures\0",
1050        FC_FONT_FEATURES,
1051        String,
1052        "List of extra feature tags in OpenType to be enabled"
1053    );
1054    property_decl!(
1055        b"prgname\0",
1056        FC_PRGNAME,
1057        String,
1058        "Name of the running program"
1059    );
1060    property_decl!(
1061        b"hash\0",
1062        FC_HASH,
1063        String,
1064        "SHA256 hash value of the font data with \"sha256:\" prefix (deprecated)"
1065    );
1066    property_decl!(
1067        b"postscriptname\0",
1068        FC_POSTSCRIPT_NAME,
1069        String,
1070        "Font name in PostScript"
1071    );
1072    property_decl!(
1073        b"symbol\0",
1074        FC_SYMBOL,
1075        bool,
1076        "Whether font uses MS symbol-font encoding"
1077    );
1078    property_decl!(b"color\0", FC_COLOR, bool, "Whether any glyphs have color");
1079    property_decl!(
1080        b"fontvariations\0",
1081        FC_FONT_VARIATIONS,
1082        String,
1083        "comma-separated string of axes in variable font"
1084    );
1085    property_decl!(
1086        b"variable\0",
1087        FC_VARIABLE,
1088        bool,
1089        "Whether font is Variable Font"
1090    );
1091    property_decl!(
1092        b"fonthashint\0",
1093        FC_FONT_HAS_HINT,
1094        bool,
1095        "Whether font has hinting"
1096    );
1097    property_decl!(b"order\0", FC_ORDER, i32, "Order number of the font");
1098}
1099
1100#[cfg(test)]
1101mod tests {
1102    use crate::pattern::properties;
1103
1104    #[test]
1105    fn test_into_inner() {
1106        let mut pat = super::OwnedPattern::new();
1107        pat.add(&properties::FC_FAMILY, "nomospace".to_string());
1108        let pat = pat.into_inner();
1109        let pat = pat as *mut super::Pattern;
1110        assert_eq!(
1111            unsafe { &*pat }.get(&properties::FC_FAMILY, 0),
1112            Some("nomospace")
1113        );
1114    }
1115
1116    #[test]
1117    fn test_get_family() {
1118        let pat = super::OwnedPattern::new();
1119        assert!(pat.get(&properties::FC_FAMILY, 0).is_none());
1120    }
1121
1122    #[test]
1123    fn test_get_family_exists() {
1124        let mut pat = super::OwnedPattern::default();
1125        pat.add(&properties::FC_FAMILY, "nomospace".to_string());
1126        assert!(pat.get(&properties::FC_FAMILY, 0).is_some());
1127    }
1128
1129    #[test]
1130    fn test_get_filepath() {
1131        let mut cfg = crate::FontConfig::default();
1132        let mut pat = super::OwnedPattern::default();
1133        pat.add(&properties::FC_FAMILY, "nomospace".to_string());
1134        cfg.substitute(&mut pat, crate::MatchKind::Pattern);
1135        let pat = pat.font_match(&mut cfg);
1136        let file = pat.get(&properties::FC_FILE, 0);
1137        assert!(file.is_some());
1138        let dpi = pat.get(&properties::FC_DPI, 0);
1139        println!("{:?}", dpi);
1140        if let Some(file) = file {
1141            assert!(file.starts_with("/usr/share/fonts"));
1142            assert!(std::path::Path::new(&file).exists(), "{}", file);
1143        }
1144    }
1145}