core_text/
font_collection.rs

1// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution.
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10use crate::font_descriptor;
11use crate::font_descriptor::{CTFontDescriptor, CTFontDescriptorCreateMatchingFontDescriptors};
12use crate::font_manager::{
13    CTFontManagerCopyAvailableFontFamilyNames, CTFontManagerCopyAvailablePostScriptNames,
14};
15
16use core_foundation::array::{CFArray, CFArrayRef};
17use core_foundation::base::{CFTypeID, TCFType};
18use core_foundation::dictionary::{CFDictionary, CFDictionaryRef};
19use core_foundation::number::CFNumber;
20use core_foundation::set::CFSet;
21use core_foundation::string::{CFString, CFStringRef};
22use core_foundation::{declare_TCFType, impl_CFTypeDescription, impl_TCFType};
23
24#[repr(C)]
25pub struct __CTFontCollection(core::ffi::c_void);
26
27pub type CTFontCollectionRef = *const __CTFontCollection;
28
29declare_TCFType! {
30    CTFontCollection, CTFontCollectionRef
31}
32impl_TCFType!(
33    CTFontCollection,
34    CTFontCollectionRef,
35    CTFontCollectionGetTypeID
36);
37impl_CFTypeDescription!(CTFontCollection);
38
39impl CTFontCollection {
40    pub fn get_descriptors(&self) -> Option<CFArray<CTFontDescriptor>> {
41        // surprise! this function follows the Get rule, despite being named *Create*.
42        // So we have to addRef it to avoid CTFontCollection from double freeing it later.
43        unsafe {
44            let font_descriptors = CTFontCollectionCreateMatchingFontDescriptors(self.0);
45            if font_descriptors.is_null() {
46                // This returns null if there are no matching font descriptors.
47                None
48            } else {
49                Some(CFArray::wrap_under_get_rule(font_descriptors))
50            }
51        }
52    }
53}
54
55pub fn new_from_descriptors(descs: &CFArray<CTFontDescriptor>) -> CTFontCollection {
56    unsafe {
57        let key = CFString::wrap_under_get_rule(kCTFontCollectionRemoveDuplicatesOption);
58        let value = CFNumber::from(1i64);
59        let options = CFDictionary::from_CFType_pairs(&[(key.as_CFType(), value.as_CFType())]);
60        let font_collection_ref = CTFontCollectionCreateWithFontDescriptors(
61            descs.as_concrete_TypeRef(),
62            options.as_concrete_TypeRef(),
63        );
64        CTFontCollection::wrap_under_create_rule(font_collection_ref)
65    }
66}
67
68pub fn create_for_all_families() -> CTFontCollection {
69    unsafe {
70        let key = CFString::wrap_under_get_rule(kCTFontCollectionRemoveDuplicatesOption);
71        let value = CFNumber::from(1i64);
72        let options = CFDictionary::from_CFType_pairs(&[(key.as_CFType(), value.as_CFType())]);
73        let font_collection_ref =
74            CTFontCollectionCreateFromAvailableFonts(options.as_concrete_TypeRef());
75        CTFontCollection::wrap_under_create_rule(font_collection_ref)
76    }
77}
78
79pub fn create_for_family(family: &str) -> Option<CTFontCollection> {
80    use crate::font_descriptor::kCTFontFamilyNameAttribute;
81
82    unsafe {
83        let family_attr = CFString::wrap_under_get_rule(kCTFontFamilyNameAttribute);
84        let family_name: CFString = family.parse().unwrap();
85        let specified_attrs =
86            CFDictionary::from_CFType_pairs(&[(family_attr.clone(), family_name.as_CFType())]);
87
88        let wildcard_desc: CTFontDescriptor =
89            font_descriptor::new_from_attributes(&specified_attrs);
90        let mandatory_attrs = CFSet::from_slice(&[family_attr.as_CFType()]);
91        let matched_descs = CTFontDescriptorCreateMatchingFontDescriptors(
92            wildcard_desc.as_concrete_TypeRef(),
93            mandatory_attrs.as_concrete_TypeRef(),
94        );
95        if matched_descs.is_null() {
96            return None;
97        }
98        let matched_descs = CFArray::wrap_under_create_rule(matched_descs);
99        // I suppose one doesn't even need the CTFontCollection object at this point.
100        // But we stick descriptors into and out of it just to provide a nice wrapper API.
101        Some(new_from_descriptors(&matched_descs))
102    }
103}
104
105pub fn get_family_names() -> CFArray<CFString> {
106    unsafe { CFArray::wrap_under_create_rule(CTFontManagerCopyAvailableFontFamilyNames()) }
107}
108
109pub fn get_postscript_names() -> CFArray<CFString> {
110    unsafe { CFArray::wrap_under_create_rule(CTFontManagerCopyAvailablePostScriptNames()) }
111}
112
113extern "C" {
114    /*
115     * CTFontCollection.h
116     */
117
118    static kCTFontCollectionRemoveDuplicatesOption: CFStringRef;
119
120    //fn CTFontCollectionCreateCopyWithFontDescriptors(original: CTFontCollectionRef,
121    //                                                 descriptors: CFArrayRef,
122    //                                                 options: CFDictionaryRef) -> CTFontCollectionRef;
123    fn CTFontCollectionCreateFromAvailableFonts(options: CFDictionaryRef) -> CTFontCollectionRef;
124    // this stupid function doesn't actually do any wildcard expansion;
125    // it just chooses the best match. Use
126    // CTFontDescriptorCreateMatchingDescriptors instead.
127    fn CTFontCollectionCreateMatchingFontDescriptors(collection: CTFontCollectionRef)
128        -> CFArrayRef;
129    fn CTFontCollectionCreateWithFontDescriptors(
130        descriptors: CFArrayRef,
131        options: CFDictionaryRef,
132    ) -> CTFontCollectionRef;
133    //fn CTFontCollectionCreateMatchingFontDescriptorsSortedWithCallback;
134    fn CTFontCollectionGetTypeID() -> CFTypeID;
135}