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 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;
43pub 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;
73pub 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 panic!();
260 } else {
261 CTFont::wrap_under_create_rule(font_ref)
262 }
263 }
264}
265
266impl CTFont {
267 pub fn symbolic_traits(&self) -> CTFontSymbolicTraits {
269 unsafe { CTFontGetSymbolicTraits(self.0) }
270 }
271}
272
273impl CTFont {
274 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 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 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
544pub 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 }
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 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 #[cfg(test)]
634 static kCTFontVariationAxisMaximumValueKey: CFStringRef;
635 static kCTFontURLAttribute: CFStringRef;
648
649 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 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 fn CTFontCopyName(font: CTFontRef, nameKey: CFStringRef) -> CFStringRef;
710 #[cfg(feature = "mountainlion")]
713 fn CTFontCopyDefaultCascadeListForLanguages(
714 font: CTFontRef,
715 languagePrefList: CFArrayRef,
716 ) -> CFArrayRef;
717
718 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 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 fn CTFontCopyVariationAxes(font: CTFontRef) -> CFArrayRef;
767 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 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 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 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 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 let ctfont = new_from_descriptor(&desc, 20.);
889 assert_eq!(big.postscript_name(), ctfont.postscript_name());
890
891 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 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 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#[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 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 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 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 if from_font_desc
1072 .attributes()
1073 .find(unsafe { font_descriptor::kCTFontNameAttribute })
1074 .is_none()
1075 {
1076 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 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 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 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 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 ), 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 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 new_ui_font_for_language(
1181 kCTFontSystemDetailFontType,
1182 10000009.,
1183 Some(CFString::from("Gofld")),
1184 );
1185}