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