core_text/
font_descriptor.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
10#![allow(non_upper_case_globals)]
11
12use core_foundation::array::CFArrayRef;
13use core_foundation::base::{CFType, CFTypeID, CFTypeRef, TCFType};
14use core_foundation::dictionary::{CFDictionary, CFDictionaryRef};
15use core_foundation::number::{CFNumber, CFNumberRef};
16use core_foundation::set::CFSetRef;
17use core_foundation::string::{CFString, CFStringRef};
18use core_foundation::url::{CFURLRef, CFURL};
19use core_foundation::{declare_TCFType, impl_CFTypeDescription, impl_TCFType};
20use core_graphics::base::CGFloat;
21
22use core_foundation::boolean::CFBoolean;
23use std::path::PathBuf;
24
25/*
26* CTFontTraits.h
27*/
28// actually, these are extern enums
29pub type CTFontFormat = u32;
30pub const kCTFontFormatUnrecognized: CTFontFormat = 0;
31pub const kCTFontFormatOpenTypePostScript: CTFontFormat = 1;
32pub const kCTFontFormatOpenTypeTrueType: CTFontFormat = 2;
33pub const kCTFontFormatTrueType: CTFontFormat = 3;
34pub const kCTFontFormatPostScript: CTFontFormat = 4;
35pub const kCTFontFormatBitmap: CTFontFormat = 5;
36
37pub const kCTFontClassMaskShift: u32 = 28;
38
39pub type CTFontSymbolicTraits = u32;
40pub const kCTFontItalicTrait: CTFontSymbolicTraits = 1 << 0;
41pub const kCTFontBoldTrait: CTFontSymbolicTraits = 1 << 1;
42pub const kCTFontExpandedTrait: CTFontSymbolicTraits = 1 << 5;
43pub const kCTFontCondensedTrait: CTFontSymbolicTraits = 1 << 6;
44pub const kCTFontMonoSpaceTrait: CTFontSymbolicTraits = 1 << 10;
45pub const kCTFontVerticalTrait: CTFontSymbolicTraits = 1 << 11;
46pub const kCTFontUIOptimizedTrait: CTFontSymbolicTraits = 1 << 12;
47pub const kCTFontColorGlyphsTrait: CTFontSymbolicTraits = 1 << 13;
48pub const kCTFontClassMaskTrait: CTFontSymbolicTraits = 15 << kCTFontClassMaskShift;
49
50pub trait SymbolicTraitAccessors {
51    fn is_italic(&self) -> bool;
52    fn is_bold(&self) -> bool;
53    fn is_expanded(&self) -> bool;
54    fn is_condensed(&self) -> bool;
55    fn is_monospace(&self) -> bool;
56    fn is_vertical(&self) -> bool;
57}
58
59impl SymbolicTraitAccessors for CTFontSymbolicTraits {
60    fn is_italic(&self) -> bool {
61        (*self & kCTFontItalicTrait) != 0
62    }
63    fn is_bold(&self) -> bool {
64        (*self & kCTFontBoldTrait) != 0
65    }
66    fn is_expanded(&self) -> bool {
67        (*self & kCTFontExpandedTrait) != 0
68    }
69    fn is_condensed(&self) -> bool {
70        (*self & kCTFontCondensedTrait) != 0
71    }
72    fn is_monospace(&self) -> bool {
73        (*self & kCTFontMonoSpaceTrait) != 0
74    }
75    fn is_vertical(&self) -> bool {
76        (*self & kCTFontVerticalTrait) != 0
77    }
78}
79
80pub type CTFontStylisticClass = u32;
81pub const kCTFontUnknownClass: CTFontStylisticClass = 0 << kCTFontClassMaskShift;
82pub const kCTFontOldStyleSerifsClass: CTFontStylisticClass = 1 << kCTFontClassMaskShift;
83pub const kCTFontTransitionalSerifsClass: CTFontStylisticClass = 2 << kCTFontClassMaskShift;
84pub const kCTFontModernSerifsClass: CTFontStylisticClass = 3 << kCTFontClassMaskShift;
85pub const kCTFontClarendonSerifsClass: CTFontStylisticClass = 4 << kCTFontClassMaskShift;
86pub const kCTFontSlabSerifsClass: CTFontStylisticClass = 5 << kCTFontClassMaskShift;
87pub const kCTFontFreeformSerifsClass: CTFontStylisticClass = 7 << kCTFontClassMaskShift;
88pub const kCTFontSansSerifClass: CTFontStylisticClass = 8 << kCTFontClassMaskShift;
89pub const kCTFontOrnamentalsClass: CTFontStylisticClass = 9 << kCTFontClassMaskShift;
90pub const kCTFontScriptsClass: CTFontStylisticClass = 10 << kCTFontClassMaskShift;
91pub const kCTFontSymbolicClass: CTFontStylisticClass = 12 << kCTFontClassMaskShift;
92
93pub trait StylisticClassAccessors {
94    fn is_serif(&self) -> bool;
95    fn is_sans_serif(&self) -> bool;
96    fn is_script(&self) -> bool;
97    fn is_fantasy(&self) -> bool;
98    fn is_symbols(&self) -> bool;
99}
100
101impl StylisticClassAccessors for CTFontStylisticClass {
102    fn is_serif(&self) -> bool {
103        let any_serif_class = kCTFontOldStyleSerifsClass
104            | kCTFontTransitionalSerifsClass
105            | kCTFontModernSerifsClass
106            | kCTFontClarendonSerifsClass
107            | kCTFontSlabSerifsClass
108            | kCTFontFreeformSerifsClass;
109
110        (*self & any_serif_class) != 0
111    }
112
113    fn is_sans_serif(&self) -> bool {
114        (*self & kCTFontSansSerifClass) != 0
115    }
116
117    fn is_script(&self) -> bool {
118        (*self & kCTFontScriptsClass) != 0
119    }
120
121    fn is_fantasy(&self) -> bool {
122        (*self & kCTFontOrnamentalsClass) != 0
123    }
124
125    fn is_symbols(&self) -> bool {
126        (*self & kCTFontSymbolicClass) != 0
127    }
128}
129
130pub type CTFontAttributes = CFDictionary;
131
132pub type CTFontTraits = CFDictionary<CFString, CFType>;
133
134pub trait TraitAccessors {
135    fn symbolic_traits(&self) -> CTFontSymbolicTraits;
136    fn normalized_weight(&self) -> f64;
137    fn normalized_width(&self) -> f64;
138    fn normalized_slant(&self) -> f64;
139}
140
141trait TraitAccessorPrivate {
142    fn extract_number_for_key(&self, key: CFStringRef) -> CFNumber;
143}
144
145impl TraitAccessorPrivate for CTFontTraits {
146    fn extract_number_for_key(&self, key: CFStringRef) -> CFNumber {
147        let cftype = self.get(key);
148        let number = cftype.downcast::<CFNumber>();
149        match number {
150            Some(number) => number,
151            None => {
152                // The value was not able to be converted to a CFNumber, this violates the Core
153                // Foundation's docs (see https://developer.apple.com/documentation/coretext/kctfontsymbolictrait)
154                // but can occur in practice with certain fonts in MacOS 13 (Ventura). When this
155                // does occur in Ventura, the value returned is always a CFBoolean, so we attempt to
156                // convert into a boolean and create a number from there.
157                let value_as_bool = bool::from(
158                    cftype
159                        .downcast::<CFBoolean>()
160                        .expect("Should be able to convert value into CFBoolean"),
161                );
162                CFNumber::from(value_as_bool as i32)
163            }
164        }
165    }
166}
167
168impl TraitAccessors for CTFontTraits {
169    fn symbolic_traits(&self) -> CTFontSymbolicTraits {
170        unsafe {
171            let number = self.extract_number_for_key(kCTFontSymbolicTrait);
172            number.to_i64().unwrap() as u32
173        }
174    }
175
176    fn normalized_weight(&self) -> f64 {
177        unsafe {
178            let number = self.extract_number_for_key(kCTFontWeightTrait);
179            number.to_f64().unwrap()
180        }
181    }
182
183    fn normalized_width(&self) -> f64 {
184        unsafe {
185            let number = self.extract_number_for_key(kCTFontWidthTrait);
186            number.to_f64().unwrap()
187        }
188    }
189
190    fn normalized_slant(&self) -> f64 {
191        unsafe {
192            let number = self.extract_number_for_key(kCTFontSlantTrait);
193            number.to_f64().unwrap()
194        }
195    }
196}
197
198/*
199* CTFontDescriptor.h
200*/
201pub type CTFontOrientation = u32;
202pub const kCTFontDefaultOrientation: CTFontOrientation = 0; // deprecated since macOS 10.11, iOS 9
203pub const kCTFontOrientationDefault: CTFontOrientation = 0; // introduced in macOS 10.8, iOS 6
204pub const kCTFontHorizontalOrientation: CTFontOrientation = 1; // deprecated since macOS 10.11, iOS 9
205pub const kCTFontOrientationHorizontal: CTFontOrientation = 1; // introduced in macOS 10.8, iOS 6
206pub const kCTFontVerticalOrientation: CTFontOrientation = 2; // deprecated since macOS 10.11, iOS 9
207pub const kCTFontOrientationVertical: CTFontOrientation = 2; // introduced in macOS 10.8, iOS 6
208
209pub type CTFontPriority = u32;
210pub const kCTFontPrioritySystem: CTFontPriority = 10000;
211pub const kCTFontPriorityNetwork: CTFontPriority = 20000;
212pub const kCTFontPriorityComputer: CTFontPriority = 30000;
213pub const kCTFontPriorityUser: CTFontPriority = 40000;
214pub const kCTFontPriorityDynamic: CTFontPriority = 50000;
215pub const kCTFontPriorityProcess: CTFontPriority = 60000;
216
217#[repr(C)]
218pub struct __CTFontDescriptor(core::ffi::c_void);
219
220pub type CTFontDescriptorRef = *const __CTFontDescriptor;
221
222declare_TCFType! {
223    CTFontDescriptor, CTFontDescriptorRef
224}
225impl_TCFType!(
226    CTFontDescriptor,
227    CTFontDescriptorRef,
228    CTFontDescriptorGetTypeID
229);
230impl_CFTypeDescription!(CTFontDescriptor);
231
232// "Font objects (CTFont, CTFontDescriptor, and associated objects) can be used
233//  simultaneously by multiple operations, work queues, or threads."
234unsafe impl Send for CTFontDescriptor {}
235unsafe impl Sync for CTFontDescriptor {}
236
237impl CTFontDescriptor {
238    fn get_string_attribute(&self, attribute: CFStringRef) -> Option<String> {
239        unsafe {
240            let value = CTFontDescriptorCopyAttribute(self.0, attribute);
241            if value.is_null() {
242                return None;
243            }
244
245            let value = CFType::wrap_under_create_rule(value);
246            assert!(value.instance_of::<CFString>());
247            let s = CFString::wrap_under_get_rule(value.as_CFTypeRef() as CFStringRef);
248            Some(s.to_string())
249        }
250    }
251}
252
253impl CTFontDescriptor {
254    pub fn family_name(&self) -> String {
255        unsafe {
256            let value = self.get_string_attribute(kCTFontFamilyNameAttribute);
257            value.expect("A font must have a non-null family name.")
258        }
259    }
260
261    pub fn font_name(&self) -> String {
262        unsafe {
263            let value = self.get_string_attribute(kCTFontNameAttribute);
264            value.expect("A font must have a non-null name.")
265        }
266    }
267
268    pub fn style_name(&self) -> String {
269        unsafe {
270            let value = self.get_string_attribute(kCTFontStyleNameAttribute);
271            value.expect("A font must have a non-null style name.")
272        }
273    }
274
275    pub fn display_name(&self) -> String {
276        unsafe {
277            let value = self.get_string_attribute(kCTFontDisplayNameAttribute);
278            value.expect("A font must have a non-null display name.")
279        }
280    }
281
282    pub fn font_format(&self) -> Option<CTFontFormat> {
283        unsafe {
284            let value = CTFontDescriptorCopyAttribute(self.0, kCTFontFormatAttribute);
285            if value.is_null() {
286                return None;
287            }
288
289            let value = CFType::wrap_under_create_rule(value);
290            assert!(value.instance_of::<CFNumber>());
291            let format = CFNumber::wrap_under_get_rule(value.as_CFTypeRef() as CFNumberRef);
292            format.to_i32().map(|x| x as CTFontFormat)
293        }
294    }
295
296    pub fn font_path(&self) -> Option<PathBuf> {
297        unsafe {
298            let value = CTFontDescriptorCopyAttribute(self.0, kCTFontURLAttribute);
299            if value.is_null() {
300                return None;
301            }
302
303            let value = CFType::wrap_under_create_rule(value);
304            assert!(value.instance_of::<CFURL>());
305            let url = CFURL::wrap_under_get_rule(value.as_CFTypeRef() as CFURLRef);
306            url.to_path()
307        }
308    }
309
310    pub fn traits(&self) -> CTFontTraits {
311        unsafe {
312            let value = CTFontDescriptorCopyAttribute(self.0, kCTFontTraitsAttribute);
313            assert!(!value.is_null());
314            let value = CFType::wrap_under_create_rule(value);
315            assert!(value.instance_of::<CFDictionary>());
316            CFDictionary::wrap_under_get_rule(value.as_CFTypeRef() as CFDictionaryRef)
317        }
318    }
319
320    pub fn create_copy_with_attributes(&self, attr: CFDictionary) -> Result<CTFontDescriptor, ()> {
321        unsafe {
322            let desc = CTFontDescriptorCreateCopyWithAttributes(
323                self.as_concrete_TypeRef(),
324                attr.as_concrete_TypeRef(),
325            );
326            if desc.is_null() {
327                return Err(());
328            }
329            Ok(CTFontDescriptor::wrap_under_create_rule(desc))
330        }
331    }
332
333    pub fn attributes(&self) -> CFDictionary<CFString, CFType> {
334        unsafe {
335            let attrs = CTFontDescriptorCopyAttributes(self.as_concrete_TypeRef());
336            CFDictionary::wrap_under_create_rule(attrs)
337        }
338    }
339}
340
341pub fn new_from_attributes(attributes: &CFDictionary<CFString, CFType>) -> CTFontDescriptor {
342    unsafe {
343        let result: CTFontDescriptorRef =
344            CTFontDescriptorCreateWithAttributes(attributes.as_concrete_TypeRef());
345        CTFontDescriptor::wrap_under_create_rule(result)
346    }
347}
348
349pub fn new_from_variations(variations: &CFDictionary<CFString, CFNumber>) -> CTFontDescriptor {
350    unsafe {
351        let var_key = CFString::wrap_under_get_rule(kCTFontVariationAttribute);
352        let var_val = CFType::wrap_under_get_rule(variations.as_CFTypeRef());
353        let attributes = CFDictionary::from_CFType_pairs(&[(var_key, var_val)]);
354        new_from_attributes(&attributes)
355    }
356}
357
358pub fn new_from_postscript_name(name: &CFString) -> CTFontDescriptor {
359    unsafe {
360        let result: CTFontDescriptorRef =
361            CTFontDescriptorCreateWithNameAndSize(name.as_concrete_TypeRef(), 0.0);
362        CTFontDescriptor::wrap_under_create_rule(result)
363    }
364}
365
366pub fn debug_descriptor(desc: &CTFontDescriptor) {
367    println!("family: {}", desc.family_name());
368    println!("name: {}", desc.font_name());
369    println!("style: {}", desc.style_name());
370    println!("display: {}", desc.display_name());
371    println!("path: {:?}", desc.font_path());
372    desc.show();
373}
374
375extern "C" {
376    /*
377     * CTFontTraits.h
378     */
379
380    // font trait constants
381    pub static kCTFontSymbolicTrait: CFStringRef;
382    pub static kCTFontWeightTrait: CFStringRef;
383    pub static kCTFontWidthTrait: CFStringRef;
384    pub static kCTFontSlantTrait: CFStringRef;
385
386    /*
387     * CTFontDescriptor.h
388     */
389
390    // font attribute constants. Note that the name-related attributes
391    // here are somewhat flaky. Servo creates CTFont instances and
392    // then uses CTFontCopyName to get more fine-grained names.
393    pub static kCTFontURLAttribute: CFStringRef; // value: CFURLRef
394    pub static kCTFontNameAttribute: CFStringRef; // value: CFStringRef
395    pub static kCTFontDisplayNameAttribute: CFStringRef; // value: CFStringRef
396    pub static kCTFontFamilyNameAttribute: CFStringRef; // value: CFStringRef
397    pub static kCTFontStyleNameAttribute: CFStringRef; // value: CFStringRef
398    pub static kCTFontTraitsAttribute: CFStringRef;
399    pub static kCTFontVariationAttribute: CFStringRef;
400    pub static kCTFontSizeAttribute: CFStringRef;
401    pub static kCTFontMatrixAttribute: CFStringRef;
402    pub static kCTFontCascadeListAttribute: CFStringRef;
403    pub static kCTFontCharacterSetAttribute: CFStringRef;
404    pub static kCTFontLanguagesAttribute: CFStringRef;
405    pub static kCTFontBaselineAdjustAttribute: CFStringRef;
406    pub static kCTFontMacintoshEncodingsAttribute: CFStringRef;
407    pub static kCTFontFeaturesAttribute: CFStringRef;
408    pub static kCTFontFeatureSettingsAttribute: CFStringRef;
409    pub static kCTFontFixedAdvanceAttribute: CFStringRef;
410    pub static kCTFontOrientationAttribute: CFStringRef;
411    pub static kCTFontFormatAttribute: CFStringRef;
412    pub static kCTFontRegistrationScopeAttribute: CFStringRef;
413    pub static kCTFontPriorityAttribute: CFStringRef;
414    pub static kCTFontEnabledAttribute: CFStringRef;
415
416    pub fn CTFontDescriptorCopyAttribute(
417        descriptor: CTFontDescriptorRef,
418        attribute: CFStringRef,
419    ) -> CFTypeRef;
420    pub fn CTFontDescriptorCopyAttributes(descriptor: CTFontDescriptorRef) -> CFDictionaryRef;
421    pub fn CTFontDescriptorCopyLocalizedAttribute(
422        descriptor: CTFontDescriptorRef,
423        attribute: CFStringRef,
424        language: *mut CFStringRef,
425    ) -> CFTypeRef;
426    pub fn CTFontDescriptorCreateCopyWithAttributes(
427        original: CTFontDescriptorRef,
428        attributes: CFDictionaryRef,
429    ) -> CTFontDescriptorRef;
430    pub fn CTFontDescriptorCreateCopyWithFeature(
431        original: CTFontDescriptorRef,
432        featureTypeIdentifier: CFNumberRef,
433        featureSelectorIdentifier: CFNumberRef,
434    ) -> CTFontDescriptorRef;
435    pub fn CTFontDescriptorCreateCopyWithVariation(
436        original: CTFontDescriptorRef,
437        variationIdentifier: CFNumberRef,
438        variationValue: CGFloat,
439    ) -> CTFontDescriptorRef;
440    pub fn CTFontDescriptorCreateMatchingFontDescriptor(
441        descriptor: CTFontDescriptorRef,
442        mandatoryAttributes: CFSetRef,
443    ) -> CTFontDescriptorRef;
444    pub fn CTFontDescriptorCreateWithAttributes(attributes: CFDictionaryRef)
445        -> CTFontDescriptorRef;
446    pub fn CTFontDescriptorCreateWithNameAndSize(
447        name: CFStringRef,
448        size: CGFloat,
449    ) -> CTFontDescriptorRef;
450    pub fn CTFontDescriptorGetTypeID() -> CFTypeID;
451}
452
453extern "C" {
454    pub fn CTFontDescriptorCreateMatchingFontDescriptors(
455        descriptor: CTFontDescriptorRef,
456        mandatoryAttributes: CFSetRef,
457    ) -> CFArrayRef;
458}