core_text/
font.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 crate::font_descriptor;
13use crate::font_descriptor::{CTFontDescriptor, CTFontDescriptorRef, CTFontOrientation};
14use crate::font_descriptor::{
15    CTFontSymbolicTraits, CTFontTraits, SymbolicTraitAccessors, TraitAccessors,
16};
17use crate::font_manager::create_font_descriptor;
18
19use core_foundation::array::{CFArray, CFArrayRef};
20use core_foundation::base::{CFIndex, CFOptionFlags, CFType, CFTypeID, CFTypeRef, TCFType};
21use core_foundation::data::{CFData, CFDataRef};
22use core_foundation::dictionary::{CFDictionary, CFDictionaryRef};
23use core_foundation::number::CFNumber;
24use core_foundation::string::{CFString, CFStringRef, UniChar};
25use core_foundation::url::{CFURLRef, CFURL};
26use core_foundation::{declare_TCFType, impl_CFTypeDescription, impl_TCFType};
27use core_graphics::base::CGFloat;
28use core_graphics::context::CGContext;
29use core_graphics::font::{CGFont, CGGlyph};
30use core_graphics::geometry::{CGAffineTransform, CGPoint, CGRect, CGSize};
31use core_graphics::path::CGPath;
32
33use foreign_types::ForeignType;
34use libc::{self, size_t};
35use std::os::raw::c_void;
36use std::ptr;
37
38type CGContextRef = *mut <CGContext as ForeignType>::CType;
39type CGFontRef = *mut <CGFont as ForeignType>::CType;
40type CGPathRef = *mut <CGPath as ForeignType>::CType;
41
42pub type CTFontUIFontType = u32;
43// kCTFontNoFontType: CTFontUIFontType = -1;
44pub const kCTFontUserFontType: CTFontUIFontType = 0;
45pub const kCTFontUserFixedPitchFontType: CTFontUIFontType = 1;
46pub const kCTFontSystemFontType: CTFontUIFontType = 2;
47pub const kCTFontEmphasizedSystemFontType: CTFontUIFontType = 3;
48pub const kCTFontSmallSystemFontType: CTFontUIFontType = 4;
49pub const kCTFontSmallEmphasizedSystemFontType: CTFontUIFontType = 5;
50pub const kCTFontMiniSystemFontType: CTFontUIFontType = 6;
51pub const kCTFontMiniEmphasizedSystemFontType: CTFontUIFontType = 7;
52pub const kCTFontViewsFontType: CTFontUIFontType = 8;
53pub const kCTFontApplicationFontType: CTFontUIFontType = 9;
54pub const kCTFontLabelFontType: CTFontUIFontType = 10;
55pub const kCTFontMenuTitleFontType: CTFontUIFontType = 11;
56pub const kCTFontMenuItemFontType: CTFontUIFontType = 12;
57pub const kCTFontMenuItemMarkFontType: CTFontUIFontType = 13;
58pub const kCTFontMenuItemCmdKeyFontType: CTFontUIFontType = 14;
59pub const kCTFontWindowTitleFontType: CTFontUIFontType = 15;
60pub const kCTFontPushButtonFontType: CTFontUIFontType = 16;
61pub const kCTFontUtilityWindowTitleFontType: CTFontUIFontType = 17;
62pub const kCTFontAlertHeaderFontType: CTFontUIFontType = 18;
63pub const kCTFontSystemDetailFontType: CTFontUIFontType = 19;
64pub const kCTFontEmphasizedSystemDetailFontType: CTFontUIFontType = 20;
65pub const kCTFontToolbarFontType: CTFontUIFontType = 21;
66pub const kCTFontSmallToolbarFontType: CTFontUIFontType = 22;
67pub const kCTFontMessageFontType: CTFontUIFontType = 23;
68pub const kCTFontPaletteFontType: CTFontUIFontType = 24;
69pub const kCTFontToolTipFontType: CTFontUIFontType = 25;
70pub const kCTFontControlContentFontType: CTFontUIFontType = 26;
71
72pub type CTFontTableTag = u32;
73// TODO: create bindings for enum with 'chars' values
74
75pub type CTFontTableOptions = u32;
76pub const kCTFontTableOptionsNoOptions: CTFontTableOptions = 0;
77pub const kCTFontTableOptionsExcludeSynthetic: CTFontTableOptions = 1 << 0;
78
79pub type CTFontOptions = CFOptionFlags;
80pub const kCTFontOptionsDefault: CTFontOptions = 0;
81pub const kCTFontOptionsPreventAutoActivation: CTFontOptions = 1 << 0;
82pub const kCTFontOptionsPreferSystemFont: CTFontOptions = 1 << 2;
83
84pub enum CTFontNameSpecifier {
85    Copyright,
86    Family,
87    SubFamily,
88    Style,
89    Unique,
90    Full,
91    Version,
92    PostScript,
93    Trademark,
94    Manufacturer,
95    Designer,
96    Description,
97    VendorURL,
98    DesignerURL,
99    License,
100    LicenseURL,
101    SampleText,
102    PostScriptCID,
103}
104
105impl From<CTFontNameSpecifier> for CFStringRef {
106    fn from(val: CTFontNameSpecifier) -> Self {
107        unsafe {
108            match val {
109                CTFontNameSpecifier::Copyright => kCTFontCopyrightNameKey,
110                CTFontNameSpecifier::Family => kCTFontFamilyNameKey,
111                CTFontNameSpecifier::SubFamily => kCTFontSubFamilyNameKey,
112                CTFontNameSpecifier::Style => kCTFontStyleNameKey,
113                CTFontNameSpecifier::Unique => kCTFontUniqueNameKey,
114                CTFontNameSpecifier::Full => kCTFontFullNameKey,
115                CTFontNameSpecifier::Version => kCTFontVersionNameKey,
116                CTFontNameSpecifier::PostScript => kCTFontPostScriptNameKey,
117                CTFontNameSpecifier::Trademark => kCTFontTrademarkNameKey,
118                CTFontNameSpecifier::Manufacturer => kCTFontManufacturerNameKey,
119                CTFontNameSpecifier::Designer => kCTFontDesignerNameKey,
120                CTFontNameSpecifier::Description => kCTFontDescriptionNameKey,
121                CTFontNameSpecifier::VendorURL => kCTFontVendorURLNameKey,
122                CTFontNameSpecifier::DesignerURL => kCTFontDesignerURLNameKey,
123                CTFontNameSpecifier::License => kCTFontLicenseNameKey,
124                CTFontNameSpecifier::LicenseURL => kCTFontLicenseURLNameKey,
125                CTFontNameSpecifier::SampleText => kCTFontSampleTextNameKey,
126                CTFontNameSpecifier::PostScriptCID => kCTFontPostScriptCIDNameKey,
127            }
128        }
129    }
130}
131
132#[repr(C)]
133pub struct __CTFont(c_void);
134
135pub type CTFontRef = *const __CTFont;
136
137declare_TCFType! {
138    CTFont, CTFontRef
139}
140impl_TCFType!(CTFont, CTFontRef, CTFontGetTypeID);
141impl_CFTypeDescription!(CTFont);
142
143unsafe impl Send for CTFont {}
144unsafe impl Sync for CTFont {}
145
146pub fn new_from_CGFont(cgfont: &CGFont, pt_size: f64) -> CTFont {
147    unsafe {
148        let font_ref = CTFontCreateWithGraphicsFont(
149            cgfont.as_ptr() as *mut _,
150            pt_size as CGFloat,
151            ptr::null(),
152            ptr::null(),
153        );
154        CTFont::wrap_under_create_rule(font_ref)
155    }
156}
157
158pub fn new_from_CGFont_with_variations(
159    cgfont: &CGFont,
160    pt_size: f64,
161    variations: &CFDictionary<CFString, CFNumber>,
162) -> CTFont {
163    unsafe {
164        let font_desc = font_descriptor::new_from_variations(variations);
165        let font_ref = CTFontCreateWithGraphicsFont(
166            cgfont.as_ptr() as *mut _,
167            pt_size as CGFloat,
168            ptr::null(),
169            font_desc.as_concrete_TypeRef(),
170        );
171        CTFont::wrap_under_create_rule(font_ref)
172    }
173}
174
175pub fn new_from_descriptor(desc: &CTFontDescriptor, pt_size: f64) -> CTFont {
176    unsafe {
177        let font_ref = CTFontCreateWithFontDescriptor(
178            desc.as_concrete_TypeRef(),
179            pt_size as CGFloat,
180            ptr::null(),
181        );
182        CTFont::wrap_under_create_rule(font_ref)
183    }
184}
185
186pub fn new_from_descriptor_and_options(
187    desc: &CTFontDescriptor,
188    pt_size: f64,
189    options: CTFontOptions,
190) -> CTFont {
191    unsafe {
192        let font_ref = CTFontCreateWithFontDescriptorAndOptions(
193            desc.as_concrete_TypeRef(),
194            pt_size as CGFloat,
195            ptr::null(),
196            options,
197        );
198        CTFont::wrap_under_create_rule(font_ref)
199    }
200}
201
202pub fn new_from_buffer(buffer: &[u8]) -> Result<CTFont, ()> {
203    let ct_font_descriptor = create_font_descriptor(buffer)?;
204    Ok(new_from_descriptor(&ct_font_descriptor, 16.0))
205}
206
207pub fn new_from_name(name: &str, pt_size: f64) -> Result<CTFont, ()> {
208    unsafe {
209        let name: CFString = name.parse().unwrap();
210        let font_ref =
211            CTFontCreateWithName(name.as_concrete_TypeRef(), pt_size as CGFloat, ptr::null());
212        if font_ref.is_null() {
213            Err(())
214        } else {
215            Ok(CTFont::wrap_under_create_rule(font_ref))
216        }
217    }
218}
219
220pub fn new_from_name_and_options(
221    name: &str,
222    pt_size: f64,
223    options: CTFontOptions,
224) -> Result<CTFont, ()> {
225    unsafe {
226        let name: CFString = name.parse().unwrap();
227        let font_ref = CTFontCreateWithNameAndOptions(
228            name.as_concrete_TypeRef(),
229            pt_size as CGFloat,
230            ptr::null(),
231            options,
232        );
233        if font_ref.is_null() {
234            Err(())
235        } else {
236            Ok(CTFont::wrap_under_create_rule(font_ref))
237        }
238    }
239}
240
241pub fn new_ui_font_for_language(
242    ui_type: CTFontUIFontType,
243    size: f64,
244    language: Option<CFString>,
245) -> CTFont {
246    unsafe {
247        let font_ref = CTFontCreateUIFontForLanguage(
248            ui_type,
249            size,
250            language
251                .as_ref()
252                .map(|x| x.as_concrete_TypeRef())
253                .unwrap_or(std::ptr::null()),
254        );
255        if font_ref.is_null() {
256            // CTFontCreateUIFontForLanguage can fail, but is unlikely to do so during
257            // normal usage (if you pass a bad ui_type it will). To make things more
258            // convenient, just panic if it fails.
259            panic!();
260        } else {
261            CTFont::wrap_under_create_rule(font_ref)
262        }
263    }
264}
265
266impl CTFont {
267    // Properties
268    pub fn symbolic_traits(&self) -> CTFontSymbolicTraits {
269        unsafe { CTFontGetSymbolicTraits(self.0) }
270    }
271}
272
273impl CTFont {
274    // Creation methods
275    pub fn copy_to_CGFont(&self) -> CGFont {
276        unsafe {
277            let cgfont_ref = CTFontCopyGraphicsFont(self.0, ptr::null_mut());
278            CGFont::from_ptr(cgfont_ref as *mut _)
279        }
280    }
281
282    pub fn copy_descriptor(&self) -> CTFontDescriptor {
283        unsafe {
284            let desc = CTFontCopyFontDescriptor(self.0);
285            CTFontDescriptor::wrap_under_create_rule(desc)
286        }
287    }
288
289    pub fn clone_with_font_size(&self, size: f64) -> CTFont {
290        unsafe {
291            let font_ref =
292                CTFontCreateCopyWithAttributes(self.0, size as CGFloat, ptr::null(), ptr::null());
293            CTFont::wrap_under_create_rule(font_ref)
294        }
295    }
296
297    pub fn clone_with_symbolic_traits(
298        &self,
299        trait_value: CTFontSymbolicTraits,
300        trait_mask: CTFontSymbolicTraits,
301    ) -> Option<CTFont> {
302        unsafe {
303            let font_ref = CTFontCreateCopyWithSymbolicTraits(
304                self.0,
305                0.0,
306                ptr::null(),
307                trait_value,
308                trait_mask,
309            );
310            if font_ref.is_null() {
311                None
312            } else {
313                Some(CTFont::wrap_under_create_rule(font_ref))
314            }
315        }
316    }
317
318    // Names
319    pub fn get_string_by_name_key(&self, name_key: CTFontNameSpecifier) -> Option<String> {
320        unsafe {
321            let result = CTFontCopyName(self.as_concrete_TypeRef(), name_key.into());
322            if result.is_null() {
323                None
324            } else {
325                Some(CFString::wrap_under_create_rule(result).to_string())
326            }
327        }
328    }
329
330    pub fn family_name(&self) -> String {
331        let value = self.get_string_by_name_key(CTFontNameSpecifier::Family);
332        value.expect("Fonts should always have a family name.")
333    }
334
335    pub fn face_name(&self) -> String {
336        let value = self.get_string_by_name_key(CTFontNameSpecifier::SubFamily);
337        value.expect("Fonts should always have a face name.")
338    }
339
340    pub fn unique_name(&self) -> String {
341        let value = self.get_string_by_name_key(CTFontNameSpecifier::Unique);
342        value.expect("Fonts should always have a unique name.")
343    }
344
345    pub fn postscript_name(&self) -> String {
346        let value = self.get_string_by_name_key(CTFontNameSpecifier::PostScript);
347        value.expect("Fonts should always have a PostScript name.")
348    }
349
350    pub fn display_name(&self) -> String {
351        let value = self.get_string_by_name_key(CTFontNameSpecifier::Full);
352        value.expect("Fonts should always have a PostScript name.")
353    }
354
355    pub fn style_name(&self) -> String {
356        let value = self.get_string_by_name_key(CTFontNameSpecifier::Style);
357        value.expect("Fonts should always have a style name.")
358    }
359
360    pub fn all_traits(&self) -> CTFontTraits {
361        unsafe { CTFontTraits::wrap_under_create_rule(CTFontCopyTraits(self.0)) }
362    }
363
364    // Font metrics
365    pub fn ascent(&self) -> CGFloat {
366        unsafe { CTFontGetAscent(self.0) }
367    }
368
369    pub fn descent(&self) -> CGFloat {
370        unsafe { CTFontGetDescent(self.0) }
371    }
372
373    pub fn underline_thickness(&self) -> CGFloat {
374        unsafe { CTFontGetUnderlineThickness(self.0) }
375    }
376
377    pub fn underline_position(&self) -> CGFloat {
378        unsafe { CTFontGetUnderlinePosition(self.0) }
379    }
380
381    pub fn slant_angle(&self) -> CGFloat {
382        unsafe { CTFontGetSlantAngle(self.0) }
383    }
384
385    pub fn cap_height(&self) -> CGFloat {
386        unsafe { CTFontGetCapHeight(self.0) }
387    }
388
389    pub fn bounding_box(&self) -> CGRect {
390        unsafe { CTFontGetBoundingBox(self.0) }
391    }
392
393    pub fn leading(&self) -> CGFloat {
394        unsafe { CTFontGetLeading(self.0) }
395    }
396
397    pub fn units_per_em(&self) -> libc::c_uint {
398        unsafe { CTFontGetUnitsPerEm(self.0) }
399    }
400
401    pub fn x_height(&self) -> CGFloat {
402        unsafe { CTFontGetXHeight(self.0) }
403    }
404
405    pub fn pt_size(&self) -> CGFloat {
406        unsafe { CTFontGetSize(self.0) }
407    }
408
409    pub fn get_glyph_with_name(&self, glyph_name: &str) -> CGGlyph {
410        let glyph_name = CFString::new(glyph_name);
411        unsafe { CTFontGetGlyphWithName(self.0, glyph_name.as_concrete_TypeRef()) }
412    }
413
414    pub unsafe fn get_glyphs_for_characters(
415        &self,
416        characters: *const UniChar,
417        glyphs: *mut CGGlyph,
418        count: CFIndex,
419    ) -> bool {
420        CTFontGetGlyphsForCharacters(self.0, characters, glyphs, count)
421    }
422
423    pub unsafe fn get_advances_for_glyphs(
424        &self,
425        orientation: CTFontOrientation,
426        glyphs: *const CGGlyph,
427        advances: *mut CGSize,
428        count: CFIndex,
429    ) -> f64 {
430        CTFontGetAdvancesForGlyphs(self.0, orientation, glyphs, advances, count)
431    }
432
433    pub unsafe fn get_vertical_translations_for_glyphs(
434        &self,
435        orientation: CTFontOrientation,
436        glyphs: *const CGGlyph,
437        translations: *mut CGSize,
438        count: CFIndex,
439    ) {
440        CTFontGetVerticalTranslationsForGlyphs(self.0, orientation, glyphs, translations, count)
441    }
442
443    pub fn get_font_table(&self, tag: u32) -> Option<CFData> {
444        unsafe {
445            let result = CTFontCopyTable(
446                self.0,
447                tag as CTFontTableTag,
448                kCTFontTableOptionsExcludeSynthetic,
449            );
450            if result.is_null() {
451                None
452            } else {
453                Some(CFData::wrap_under_create_rule(result))
454            }
455        }
456    }
457
458    pub fn get_available_font_tables(&self) -> Option<CFArray<CTFontTableTag>> {
459        unsafe {
460            let result = CTFontCopyAvailableTables(self.0, kCTFontTableOptionsExcludeSynthetic);
461            if result.is_null() {
462                None
463            } else {
464                Some(TCFType::wrap_under_create_rule(result))
465            }
466        }
467    }
468
469    pub fn get_bounding_rects_for_glyphs(
470        &self,
471        orientation: CTFontOrientation,
472        glyphs: &[CGGlyph],
473    ) -> CGRect {
474        unsafe {
475            CTFontGetBoundingRectsForGlyphs(
476                self.as_concrete_TypeRef(),
477                orientation,
478                glyphs.as_ptr(),
479                ptr::null_mut(),
480                glyphs.len() as CFIndex,
481            )
482        }
483    }
484
485    pub fn draw_glyphs(&self, glyphs: &[CGGlyph], positions: &[CGPoint], context: CGContext) {
486        assert_eq!(glyphs.len(), positions.len());
487        unsafe {
488            CTFontDrawGlyphs(
489                self.as_concrete_TypeRef(),
490                glyphs.as_ptr(),
491                positions.as_ptr(),
492                glyphs.len() as size_t,
493                context.as_ptr(),
494            )
495        }
496    }
497
498    pub fn get_matrix(&self) -> CGAffineTransform {
499        unsafe { CTFontGetMatrix(self.as_concrete_TypeRef()) }
500    }
501
502    pub fn url(&self) -> Option<CFURL> {
503        unsafe {
504            let result = CTFontCopyAttribute(self.0, kCTFontURLAttribute);
505            if result.is_null() {
506                None
507            } else {
508                Some(CFURL::wrap_under_create_rule(result as CFURLRef))
509            }
510        }
511    }
512
513    pub fn get_variation_axes(&self) -> Option<CFArray<CFDictionary<CFString, CFType>>> {
514        unsafe {
515            let axes = CTFontCopyVariationAxes(self.0);
516            if axes.is_null() {
517                return None;
518            }
519            Some(TCFType::wrap_under_create_rule(axes))
520        }
521    }
522
523    pub fn create_path_for_glyph(
524        &self,
525        glyph: CGGlyph,
526        matrix: &CGAffineTransform,
527    ) -> Result<CGPath, ()> {
528        unsafe {
529            let path = CTFontCreatePathForGlyph(self.0, glyph, matrix);
530            if path.is_null() {
531                Err(())
532            } else {
533                Ok(CGPath::from_ptr(path))
534            }
535        }
536    }
537
538    #[inline]
539    pub fn glyph_count(&self) -> CFIndex {
540        unsafe { CTFontGetGlyphCount(self.0) }
541    }
542}
543
544// Helper methods
545pub fn debug_font_names(font: &CTFont) {
546    fn get_key(font: &CTFont, key: CTFontNameSpecifier) -> String {
547        font.get_string_by_name_key(key).unwrap()
548    }
549
550    println!(
551        "kCTFontFamilyNameKey: {}",
552        get_key(font, CTFontNameSpecifier::Family)
553    );
554    println!(
555        "kCTFontSubFamilyNameKey: {}",
556        get_key(font, CTFontNameSpecifier::SubFamily)
557    );
558    println!(
559        "kCTFontStyleNameKey: {}",
560        get_key(font, CTFontNameSpecifier::Style)
561    );
562    println!(
563        "kCTFontUniqueNameKey: {}",
564        get_key(font, CTFontNameSpecifier::Unique)
565    );
566    println!(
567        "kCTFontFullNameKey: {}",
568        get_key(font, CTFontNameSpecifier::Full)
569    );
570    println!(
571        "kCTFontPostScriptNameKey: {}",
572        get_key(font, CTFontNameSpecifier::PostScript)
573    );
574}
575
576pub fn debug_font_traits(font: &CTFont) {
577    let sym = font.symbolic_traits();
578    println!("kCTFontItalicTrait: {}", sym.is_italic());
579    println!("kCTFontBoldTrait: {}", sym.is_bold());
580    println!("kCTFontExpandedTrait: {}", sym.is_expanded());
581    println!("kCTFontCondensedTrait: {}", sym.is_condensed());
582    println!("kCTFontMonoSpaceTrait: {}", sym.is_monospace());
583
584    let traits = font.all_traits();
585    println!("kCTFontWeightTrait: {}", traits.normalized_weight());
586    println!("kCTFontWidthTrait: {}", traits.normalized_width());
587    //    println!("kCTFontSlantTrait: {}", traits.normalized_slant());
588}
589
590#[cfg(feature = "mountainlion")]
591pub fn cascade_list_for_languages(
592    font: &CTFont,
593    language_pref_list: &CFArray<CFString>,
594) -> CFArray<CTFontDescriptor> {
595    unsafe {
596        let font_collection_ref = CTFontCopyDefaultCascadeListForLanguages(
597            font.as_concrete_TypeRef(),
598            language_pref_list.as_concrete_TypeRef(),
599        );
600        CFArray::wrap_under_create_rule(font_collection_ref)
601    }
602}
603
604#[cfg_attr(feature = "link", link(name = "CoreText", kind = "framework"))]
605extern "C" {
606    /*
607     * CTFont.h
608     */
609
610    /* Name Specifier Constants */
611    static kCTFontCopyrightNameKey: CFStringRef;
612    static kCTFontFamilyNameKey: CFStringRef;
613    static kCTFontSubFamilyNameKey: CFStringRef;
614    static kCTFontStyleNameKey: CFStringRef;
615    static kCTFontUniqueNameKey: CFStringRef;
616    static kCTFontFullNameKey: CFStringRef;
617    static kCTFontVersionNameKey: CFStringRef;
618    static kCTFontPostScriptNameKey: CFStringRef;
619    static kCTFontTrademarkNameKey: CFStringRef;
620    static kCTFontManufacturerNameKey: CFStringRef;
621    static kCTFontDesignerNameKey: CFStringRef;
622    static kCTFontDescriptionNameKey: CFStringRef;
623    static kCTFontVendorURLNameKey: CFStringRef;
624    static kCTFontDesignerURLNameKey: CFStringRef;
625    static kCTFontLicenseNameKey: CFStringRef;
626    static kCTFontLicenseURLNameKey: CFStringRef;
627    static kCTFontSampleTextNameKey: CFStringRef;
628    static kCTFontPostScriptCIDNameKey: CFStringRef;
629
630    #[cfg(test)]
631    static kCTFontVariationAxisIdentifierKey: CFStringRef;
632    //static kCTFontVariationAxisMinimumValueKey: CFStringRef;
633    #[cfg(test)]
634    static kCTFontVariationAxisMaximumValueKey: CFStringRef;
635    //static kCTFontVariationAxisDefaultValueKey: CFStringRef;
636    //static kCTFontVariationAxisNameKey: CFStringRef;
637
638    //static kCTFontFeatureTypeIdentifierKey: CFStringRef;
639    //static kCTFontFeatureTypeNameKey: CFStringRef;
640    //static kCTFontFeatureTypeExclusiveKey: CFStringRef;
641    //static kCTFontFeatureTypeSelectorsKey: CFStringRef;
642    //static kCTFontFeatureSelectorIdentifierKey: CFStringRef;
643    //static kCTFontFeatureSelectorNameKey: CFStringRef;
644    //static kCTFontFeatureSelectorDefaultKey: CFStringRef;
645    //static kCTFontFeatureSelectorSettingKey: CFStringRef;
646
647    static kCTFontURLAttribute: CFStringRef;
648
649    // N.B. Unlike most Cocoa bindings, this extern block is organized according
650    // to the documentation's Functions By Task listing, because there so many functions.
651
652    /* Creating Fonts */
653    fn CTFontCreateWithName(
654        name: CFStringRef,
655        size: CGFloat,
656        matrix: *const CGAffineTransform,
657    ) -> CTFontRef;
658    fn CTFontCreateWithNameAndOptions(
659        name: CFStringRef,
660        size: CGFloat,
661        matrix: *const CGAffineTransform,
662        options: CTFontOptions,
663    ) -> CTFontRef;
664    fn CTFontCreateWithFontDescriptor(
665        descriptor: CTFontDescriptorRef,
666        size: CGFloat,
667        matrix: *const CGAffineTransform,
668    ) -> CTFontRef;
669    fn CTFontCreateWithFontDescriptorAndOptions(
670        descriptor: CTFontDescriptorRef,
671        size: CGFloat,
672        matrix: *const CGAffineTransform,
673        options: CTFontOptions,
674    ) -> CTFontRef;
675    fn CTFontCreateUIFontForLanguage(
676        uiType: CTFontUIFontType,
677        size: CGFloat,
678        language: CFStringRef,
679    ) -> CTFontRef;
680    fn CTFontCreateCopyWithAttributes(
681        font: CTFontRef,
682        size: CGFloat,
683        matrix: *const CGAffineTransform,
684        attributes: CTFontDescriptorRef,
685    ) -> CTFontRef;
686    fn CTFontCreateCopyWithSymbolicTraits(
687        font: CTFontRef,
688        size: CGFloat,
689        matrix: *const CGAffineTransform,
690        symTraitValue: CTFontSymbolicTraits,
691        symTraitMask: CTFontSymbolicTraits,
692    ) -> CTFontRef;
693    //fn CTFontCreateCopyWithFamily
694    //fn CTFontCreateForString
695
696    /* Getting Font Data */
697    fn CTFontCopyFontDescriptor(font: CTFontRef) -> CTFontDescriptorRef;
698    fn CTFontCopyAttribute(font: CTFontRef, attribute: CFStringRef) -> CFTypeRef;
699    fn CTFontGetSize(font: CTFontRef) -> CGFloat;
700    fn CTFontGetMatrix(font: CTFontRef) -> CGAffineTransform;
701    fn CTFontGetSymbolicTraits(font: CTFontRef) -> CTFontSymbolicTraits;
702    fn CTFontCopyTraits(font: CTFontRef) -> CFDictionaryRef;
703
704    /* Getting Font Names */
705    //fn CTFontCopyPostScriptName(font: CTFontRef) -> CFStringRef;
706    //fn CTFontCopyFamilyName(font: CTFontRef) -> CFStringRef;
707    //fn CTFontCopyFullName(font: CTFontRef) -> CFStringRef;
708    //fn CTFontCopyDisplayName(font: CTFontRef) -> CFStringRef;
709    fn CTFontCopyName(font: CTFontRef, nameKey: CFStringRef) -> CFStringRef;
710    //fn CTFontCopyLocalizedName(font: CTFontRef, nameKey: CFStringRef,
711    //                           language: *CFStringRef) -> CFStringRef;
712    #[cfg(feature = "mountainlion")]
713    fn CTFontCopyDefaultCascadeListForLanguages(
714        font: CTFontRef,
715        languagePrefList: CFArrayRef,
716    ) -> CFArrayRef;
717
718    /* Working With Encoding */
719    //fn CTFontCopyCharacterSet
720    //fn CTFontGetStringEncoding
721    //fn CTFontCopySupportedLanguages
722
723    /* Getting Font Metrics */
724    fn CTFontGetAscent(font: CTFontRef) -> CGFloat;
725    fn CTFontGetDescent(font: CTFontRef) -> CGFloat;
726    fn CTFontGetLeading(font: CTFontRef) -> CGFloat;
727    fn CTFontGetUnitsPerEm(font: CTFontRef) -> libc::c_uint;
728    fn CTFontGetGlyphCount(font: CTFontRef) -> CFIndex;
729    fn CTFontGetBoundingBox(font: CTFontRef) -> CGRect;
730    fn CTFontGetUnderlinePosition(font: CTFontRef) -> CGFloat;
731    fn CTFontGetUnderlineThickness(font: CTFontRef) -> CGFloat;
732    fn CTFontGetSlantAngle(font: CTFontRef) -> CGFloat;
733    fn CTFontGetCapHeight(font: CTFontRef) -> CGFloat;
734    fn CTFontGetXHeight(font: CTFontRef) -> CGFloat;
735
736    /* Getting Glyph Data */
737    fn CTFontCreatePathForGlyph(
738        font: CTFontRef,
739        glyph: CGGlyph,
740        matrix: *const CGAffineTransform,
741    ) -> CGPathRef;
742    fn CTFontGetGlyphWithName(font: CTFontRef, glyphName: CFStringRef) -> CGGlyph;
743    fn CTFontGetBoundingRectsForGlyphs(
744        font: CTFontRef,
745        orientation: CTFontOrientation,
746        glyphs: *const CGGlyph,
747        boundingRects: *mut CGRect,
748        count: CFIndex,
749    ) -> CGRect;
750    fn CTFontGetAdvancesForGlyphs(
751        font: CTFontRef,
752        orientation: CTFontOrientation,
753        glyphs: *const CGGlyph,
754        advances: *mut CGSize,
755        count: CFIndex,
756    ) -> libc::c_double;
757    fn CTFontGetVerticalTranslationsForGlyphs(
758        font: CTFontRef,
759        orientation: CTFontOrientation,
760        glyphs: *const CGGlyph,
761        translations: *mut CGSize,
762        count: CFIndex,
763    );
764
765    /* Working With Font Variations */
766    fn CTFontCopyVariationAxes(font: CTFontRef) -> CFArrayRef;
767    //fn CTFontCopyVariation
768
769    /* Getting Font Features */
770    //fn CTFontCopyFeatures
771    //fn CTFontCopyFeatureSettings
772
773    /* Working with Glyphs */
774    fn CTFontGetGlyphsForCharacters(
775        font: CTFontRef,
776        characters: *const UniChar,
777        glyphs: *mut CGGlyph,
778        count: CFIndex,
779    ) -> bool;
780    fn CTFontDrawGlyphs(
781        font: CTFontRef,
782        glyphs: *const CGGlyph,
783        positions: *const CGPoint,
784        count: size_t,
785        context: CGContextRef,
786    );
787    //fn CTFontGetLigatureCaretPositions
788
789    /* Converting Fonts */
790    fn CTFontCopyGraphicsFont(font: CTFontRef, attributes: *mut CTFontDescriptorRef) -> CGFontRef;
791    fn CTFontCreateWithGraphicsFont(
792        graphicsFont: CGFontRef,
793        size: CGFloat,
794        matrix: *const CGAffineTransform,
795        attributes: CTFontDescriptorRef,
796    ) -> CTFontRef;
797    //fn CTFontGetPlatformFont
798    //fn CTFontCreateWithPlatformFont
799    //fn CTFontCreateWithQuickdrawInstance
800
801    /* Getting Font Table Data */
802    fn CTFontCopyAvailableTables(font: CTFontRef, options: CTFontTableOptions) -> CFArrayRef;
803    fn CTFontCopyTable(
804        font: CTFontRef,
805        table: CTFontTableTag,
806        options: CTFontTableOptions,
807    ) -> CFDataRef;
808
809    fn CTFontGetTypeID() -> CFTypeID;
810}
811
812#[test]
813fn copy_font() {
814    use std::io::Read;
815    let mut f = std::fs::File::open("/System/Library/Fonts/ZapfDingbats.ttf").unwrap();
816    let mut font_data = Vec::new();
817    f.read_to_end(&mut font_data).unwrap();
818    let desc = crate::font_manager::create_font_descriptor(&font_data).unwrap();
819    let font = new_from_descriptor(&desc, 12.);
820    drop(desc);
821    let desc = font.copy_descriptor();
822    drop(font);
823    let font = new_from_descriptor(&desc, 14.);
824    assert_eq!(font.family_name(), "Zapf Dingbats");
825}
826
827#[cfg(test)]
828fn macos_version() -> (i32, i32, i32) {
829    use std::io::Read;
830
831    // This is the same approach that Firefox uses for detecting versions
832    let file = "/System/Library/CoreServices/SystemVersion.plist";
833    let mut f = std::fs::File::open(file).unwrap();
834    let mut system_version_data = Vec::new();
835    f.read_to_end(&mut system_version_data).unwrap();
836
837    use core_foundation::propertylist;
838    let (list, _) = propertylist::create_with_data(
839        core_foundation::data::CFData::from_buffer(&system_version_data),
840        propertylist::kCFPropertyListImmutable,
841    )
842    .unwrap();
843    let k = unsafe { propertylist::CFPropertyList::wrap_under_create_rule(list) };
844
845    let dict = unsafe {
846        std::mem::transmute::<_, CFDictionary<CFType, CFType>>(
847            k.downcast::<CFDictionary>().unwrap(),
848        )
849    };
850
851    let version = dict
852        .find(&CFString::new("ProductVersion").as_CFType())
853        .as_ref()
854        .unwrap()
855        .downcast::<CFString>()
856        .unwrap()
857        .to_string();
858
859    match version
860        .split('.')
861        .map(|x| x.parse().unwrap())
862        .collect::<Vec<_>>()[..]
863    {
864        [a, b, c] => (a, b, c),
865        [a, b] => (a, b, 0),
866        _ => panic!(),
867    }
868}
869
870#[test]
871fn copy_system_font() {
872    use crate::*;
873
874    let small = new_ui_font_for_language(kCTFontSystemDetailFontType, 19., None);
875    let big = small.clone_with_font_size(20.);
876
877    // ensure that we end up with different fonts for the different sizes before 10.15
878    if macos_version() < (10, 15, 0) {
879        assert_ne!(big.url(), small.url());
880    } else {
881        assert_eq!(big.url(), small.url());
882    }
883
884    let ps = small.postscript_name();
885    let desc = small.copy_descriptor();
886
887    // check that we can construct a new version by descriptor
888    let ctfont = new_from_descriptor(&desc, 20.);
889    assert_eq!(big.postscript_name(), ctfont.postscript_name());
890
891    // check that we can construct a new version by attributes
892    let attr = desc.attributes();
893    let desc_from_attr = font_descriptor::new_from_attributes(&attr);
894    let font_from_attr = new_from_descriptor(&desc_from_attr, 19.);
895    assert_eq!(font_from_attr.postscript_name(), small.postscript_name());
896
897    // on newer versions of macos we can't construct by name anymore
898    if macos_version() < (10, 13, 0) {
899        let ui_font_by_name = new_from_name(&small.postscript_name(), 19.).unwrap();
900        assert_eq!(ui_font_by_name.postscript_name(), small.postscript_name());
901
902        let ui_font_by_name = new_from_name(&small.postscript_name(), 20.).unwrap();
903        assert_eq!(ui_font_by_name.postscript_name(), small.postscript_name());
904
905        let ui_font_by_name = new_from_name(&big.postscript_name(), 20.).unwrap();
906        assert_eq!(ui_font_by_name.postscript_name(), big.postscript_name());
907    }
908
909    // but we can still construct the CGFont by name
910    let cgfont = CGFont::from_name(&CFString::new(&ps)).unwrap();
911    let cgfont = new_from_CGFont(&cgfont, 0.);
912    println!("{:?}", cgfont);
913    let desc = cgfont.copy_descriptor();
914    let matching = unsafe {
915        crate::font_descriptor::CTFontDescriptorCreateMatchingFontDescriptor(
916            desc.as_concrete_TypeRef(),
917            std::ptr::null(),
918        )
919    };
920    let matching = unsafe { CTFontDescriptor::wrap_under_create_rule(matching) };
921
922    println!("{:?}", cgfont.copy_descriptor());
923    assert!(desc
924        .attributes()
925        .find(CFString::from_static_string("NSFontSizeAttribute"))
926        .is_some());
927
928    println!("{:?}", matching);
929    println!(
930        "{:?}",
931        matching
932            .attributes()
933            .find(CFString::from_static_string("NSFontSizeAttribute"))
934    );
935
936    assert!(matching
937        .attributes()
938        .find(CFString::from_static_string("NSFontSizeAttribute"))
939        .is_none());
940
941    assert_eq!(small.postscript_name(), cgfont.postscript_name());
942}
943
944// Tests what happens when variations have values not inbetween min and max
945#[test]
946fn out_of_range_variations() {
947    use crate::*;
948    use core_foundation::base::ItemRef;
949
950    let small = new_ui_font_for_language(kCTFontSystemDetailFontType, 19., None);
951
952    let axes = small.get_variation_axes();
953    let version = dbg!(macos_version());
954    if version < (10, 12, 0) {
955        assert!(axes.is_none());
956        return;
957    }
958    let axes = axes.unwrap();
959    let mut vals = Vec::new();
960    dbg!(&axes);
961    for axis in axes.iter() {
962        let tag = axis
963            .find(unsafe { kCTFontVariationAxisIdentifierKey })
964            .unwrap()
965            .downcast::<CFNumber>()
966            .unwrap()
967            .to_i64()
968            .unwrap();
969        let max = axis
970            .find(unsafe { kCTFontVariationAxisMaximumValueKey })
971            .unwrap()
972            .downcast::<CFNumber>()
973            .unwrap()
974            .to_f64()
975            .unwrap();
976        vals.push((CFNumber::from(tag), CFNumber::from(max + 1.)));
977    }
978    let vals_dict = CFDictionary::from_CFType_pairs(&vals);
979    let variation_attribute =
980        unsafe { CFString::wrap_under_get_rule(font_descriptor::kCTFontVariationAttribute) };
981    let attrs_dict = CFDictionary::from_CFType_pairs(&[(variation_attribute.clone(), vals_dict)]);
982    let ct_var_font_desc = small
983        .copy_descriptor()
984        .create_copy_with_attributes(attrs_dict.to_untyped())
985        .unwrap();
986    let variation_font = crate::font::new_from_descriptor(&ct_var_font_desc, 19.);
987    let var_desc = variation_font.copy_descriptor();
988    let var_attrs = var_desc.attributes();
989    dbg!(&var_attrs);
990
991    // Handling of attributes greater than max changed between versions
992    let var_attrs = var_attrs.find(variation_attribute);
993    if version >= (14, 0, 0) {
994        check_attrs(0., var_attrs, axes);
995    } else if version >= (12, 0, 0) && version < (13, 0, 0) {
996        check_attrs(1., var_attrs, axes);
997    } else if version >= (10, 15, 0) {
998        assert!(var_attrs.is_none());
999    } else {
1000        let var_attrs = var_attrs.unwrap().downcast::<CFDictionary>().unwrap();
1001        assert!(var_attrs.is_empty());
1002    }
1003
1004    fn check_attrs(
1005        clamp_diff: f64,
1006        var_attrs: Option<ItemRef<'_, CFType>>,
1007        axes: CFArray<CFDictionary<CFString, CFType>>,
1008    ) {
1009        let var_attrs = var_attrs.unwrap().downcast::<CFDictionary>().unwrap();
1010        assert!(!var_attrs.is_empty());
1011        let var_attrs: CFDictionary<CFType, CFType> = unsafe { std::mem::transmute(var_attrs) };
1012        // attributes greater than max remain
1013        for axis in axes.iter() {
1014            let tag = axis
1015                .find(unsafe { kCTFontVariationAxisIdentifierKey })
1016                .unwrap();
1017            let max = axis
1018                .find(unsafe { kCTFontVariationAxisMaximumValueKey })
1019                .unwrap()
1020                .downcast::<CFNumber>()
1021                .unwrap()
1022                .to_f64()
1023                .unwrap();
1024            let val = var_attrs
1025                .find(tag.clone())
1026                .unwrap()
1027                .downcast::<CFNumber>()
1028                .unwrap()
1029                .to_f64()
1030                .unwrap();
1031
1032            let expected = max + clamp_diff;
1033            assert_eq!(
1034                val, expected,
1035                "axis {:?} = {:?} (expected {:?})",
1036                tag, val, expected
1037            );
1038        }
1039    }
1040}
1041
1042#[test]
1043fn equal_descriptor_different_font() {
1044    use crate::*;
1045
1046    let variation_attribute =
1047        unsafe { CFString::wrap_under_get_rule(font_descriptor::kCTFontVariationAttribute) };
1048    let size_attribute =
1049        unsafe { CFString::wrap_under_get_rule(font_descriptor::kCTFontSizeAttribute) };
1050
1051    let sys_font = new_ui_font_for_language(kCTFontSystemDetailFontType, 19., None);
1052
1053    // but we can still construct the CGFont by name
1054    let create_vars = |desc| {
1055        let vals: Vec<(CFNumber, CFNumber)> =
1056            vec![(CFNumber::from(0x6f70737a), CFNumber::from(17.))];
1057        let vals_dict = CFDictionary::from_CFType_pairs(&vals);
1058        let attrs_dict =
1059            CFDictionary::from_CFType_pairs(&[(variation_attribute.clone(), vals_dict)]);
1060        let size_attrs_dict =
1061            CFDictionary::from_CFType_pairs(&[(size_attribute.clone(), CFNumber::from(120.))]);
1062        dbg!(&desc);
1063        let from_font_desc = new_from_descriptor(&desc, 120.).copy_descriptor();
1064        let resized_font_desc = desc
1065            .create_copy_with_attributes(size_attrs_dict.to_untyped())
1066            .unwrap();
1067        if macos_version() >= (11, 0, 0) {
1068            assert_eq!(from_font_desc, resized_font_desc);
1069        } else {
1070            // we won't have a name if we're using system font desc
1071            if from_font_desc
1072                .attributes()
1073                .find(unsafe { font_descriptor::kCTFontNameAttribute })
1074                .is_none()
1075            {
1076                // it's surprising that desc's are the not equal but the attributes are
1077                assert_ne!(from_font_desc, resized_font_desc);
1078                assert_eq!(
1079                    from_font_desc.attributes().to_untyped(),
1080                    resized_font_desc.attributes().to_untyped()
1081                );
1082            } else if macos_version() >= (10, 13, 0) {
1083                // this is unsurprising
1084                assert_ne!(from_font_desc, resized_font_desc);
1085                assert_ne!(
1086                    from_font_desc.attributes().to_untyped(),
1087                    resized_font_desc.attributes().to_untyped()
1088                );
1089            } else {
1090                assert_ne!(from_font_desc, resized_font_desc);
1091                assert_eq!(
1092                    from_font_desc.attributes().to_untyped(),
1093                    resized_font_desc.attributes().to_untyped()
1094                );
1095            }
1096        }
1097
1098        let from_font_desc = from_font_desc
1099            .create_copy_with_attributes(attrs_dict.to_untyped())
1100            .unwrap();
1101        let resized_font_desc = resized_font_desc
1102            .create_copy_with_attributes(attrs_dict.to_untyped())
1103            .unwrap();
1104        (from_font_desc, resized_font_desc)
1105    };
1106
1107    // setting the variation works properly if we use system font desc
1108    let (from_font_desc, resized_font_desc) = create_vars(sys_font.copy_descriptor());
1109    assert_eq!(from_font_desc, resized_font_desc);
1110    assert!(resized_font_desc
1111        .attributes()
1112        .find(variation_attribute.clone())
1113        .is_some());
1114
1115    // but doesn't if we refer to it by name
1116    let ps = sys_font.postscript_name();
1117    let cgfont = CGFont::from_name(&CFString::new(&ps)).unwrap();
1118    let ctfont = new_from_CGFont(&cgfont, 0.);
1119
1120    let (from_font_desc, resized_font_desc) = create_vars(ctfont.copy_descriptor());
1121    if macos_version() >= (10, 15, 0) {
1122        assert_ne!(from_font_desc, resized_font_desc);
1123    }
1124
1125    if macos_version() >= (10, 13, 0) {
1126        assert!(from_font_desc
1127            .attributes()
1128            .find(variation_attribute.clone())
1129            .is_some());
1130        if macos_version() >= (11, 0, 0) {
1131            assert!(resized_font_desc
1132                .attributes()
1133                .find(variation_attribute)
1134                .is_none());
1135        } else {
1136            assert!(resized_font_desc
1137                .attributes()
1138                .find(variation_attribute)
1139                .is_some());
1140        };
1141    }
1142}
1143
1144#[test]
1145fn system_font_variation() {
1146    use crate::*;
1147
1148    let small = new_ui_font_for_language(kCTFontSystemDetailFontType, 19., None);
1149
1150    // but we can still construct the CGFont by name
1151    let ps = small.postscript_name();
1152    let cgfont = CGFont::from_name(&CFString::new(&ps)).unwrap();
1153    let cgfont = new_from_CGFont(&cgfont, 0.);
1154    let desc = cgfont.copy_descriptor();
1155
1156    let vals: Vec<(CFNumber, CFNumber)> =
1157        vec![(CFNumber::from(0x6f70737a /* opsz */), CFNumber::from(17.))];
1158    let vals_dict = CFDictionary::from_CFType_pairs(&vals);
1159    let variation_attribute =
1160        unsafe { CFString::wrap_under_get_rule(font_descriptor::kCTFontVariationAttribute) };
1161    let attrs_dict = CFDictionary::from_CFType_pairs(&[(variation_attribute, vals_dict)]);
1162    let ct_var_font_desc = desc
1163        .create_copy_with_attributes(attrs_dict.to_untyped())
1164        .unwrap();
1165    let attrs = ct_var_font_desc.attributes();
1166    let var_attr = attrs.find(CFString::from_static_string("NSCTFontVariationAttribute"));
1167    if macos_version() >= (11, 0, 0) {
1168        // the variation goes away
1169        assert!(var_attr.is_none());
1170    } else {
1171        assert!(var_attr.is_some());
1172    }
1173
1174    dbg!(ct_var_font_desc);
1175}
1176
1177#[test]
1178fn ui_font() {
1179    // pass some garbagey inputs
1180    new_ui_font_for_language(
1181        kCTFontSystemDetailFontType,
1182        10000009.,
1183        Some(CFString::from("Gofld")),
1184    );
1185}