1use 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
11use 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#[doc(alias = "FcPattern")]
32#[repr(transparent)]
33pub struct Pattern(FcPattern);
34
35#[doc(alias = "FcPattern")]
37#[repr(transparent)]
38pub struct OwnedPattern {
39 pub(crate) pat: NonNull<FcPattern>,
41}
42
43impl OwnedPattern {
44 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 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 pub fn print(&self) {
69 unsafe {
70 ffi_dispatch!(LIB, FcPatternPrint, self.as_ptr());
71 }
72 }
73
74 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 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 pub fn default_substitute(&mut self) {
113 unsafe {
114 ffi_dispatch!(LIB, FcDefaultSubstitute, self.as_mut_ptr());
115 }
116 }
117
118 #[doc(alias = "FcFontMatch")]
124 pub fn font_match(&mut self, config: &mut FontConfig) -> OwnedPattern {
125 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 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 FontSet {
161 fcset: NonNull::new(set).unwrap(),
162 _marker: PhantomData,
163 }
164 }
165
166 pub fn font_sort(&mut self, config: &mut FontConfig, trim: bool) -> Result<FontSet<'static>> {
172 unsafe {
173 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 }, &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 pub fn font_sort_with_charset(
207 &mut self,
208 config: &mut FontConfig,
209 trim: bool,
210 ) -> Option<(FontSet<'_>, OwnedCharSet)> {
211 unsafe {
214 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 }, &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 #[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 #[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 pub fn name(&self) -> Option<&str> {
286 self.get(&properties::FC_FULLNAME, 0)
287 }
288
289 pub fn filename(&self) -> Option<&str> {
291 self.get(&properties::FC_FILE, 0)
292 }
293
294 pub fn face_index(&self) -> Option<i32> {
296 self.get(&properties::FC_INDEX, 0)
297 }
298
299 pub fn slant(&self) -> Option<i32> {
301 self.get(&properties::FC_SLANT, 0)
302 }
303
304 pub fn weight(&self) -> Option<i32> {
306 self.get(&properties::FC_WEIGHT, 0)
307 }
308
309 pub fn width(&self) -> Option<i32> {
311 self.get(&properties::FC_WIDTH, 0)
312 }
313
314 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 pub fn hash(&self) -> u32 {
323 unsafe { ffi_dispatch!(LIB, FcPatternHash, self.as_ptr() as *mut _) }
324 }
325
326 pub(crate) fn as_ptr(&self) -> *const FcPattern {
328 self as *const _ as *const FcPattern
329 }
330
331 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 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 pub fn lang_set(&self) -> Option<LangSet> {
432 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 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 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 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
503pub 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 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 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 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 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 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!(
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}