// Copyright 2013 The Servo Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms.
#![allow(non_upper_case_globals)]
use font_descriptor; use font_descriptor::{CTFontDescriptor, CTFontDescriptorRef, CTFontOrientation}; use font_descriptor::{CTFontSymbolicTraits, CTFontTraits, SymbolicTraitAccessors, TraitAccessors}; use font_manager::create_font_descriptor;
use core_foundation::array::{CFArray, CFArrayRef}; use core_foundation::base::{CFIndex, CFOptionFlags, CFType, CFTypeID, CFTypeRef, TCFType}; use core_foundation::data::{CFData, CFDataRef}; use core_foundation::dictionary::{CFDictionary, CFDictionaryRef}; use core_foundation::number::CFNumber; use core_foundation::string::{CFString, CFStringRef, UniChar}; use core_foundation::url::{CFURLRef, CFURL}; use core_graphics::base::CGFloat; use core_graphics::context::CGContext; use core_graphics::font::{CGFont, CGGlyph}; use core_graphics::geometry::{CGAffineTransform, CGPoint, CGRect, CGSize}; use core_graphics::path::CGPath;
use foreign_types::ForeignType; use libc::{self, size_t}; use std::os::raw::c_void; use std::ptr;
type CGContextRef = *mut <CGContext as ForeignType>::CType; type CGFontRef = *mut <CGFont as ForeignType>::CType; type CGPathRef = *mut <CGPath as ForeignType>::CType;
pubfn new_ui_font_for_language(
ui_type: CTFontUIFontType,
size: f64,
language: Option<CFString>,
) -> CTFont { unsafe { let font_ref = CTFontCreateUIFontForLanguage(
ui_type,
size,
language
.as_ref()
.map(|x| x.as_concrete_TypeRef())
.unwrap_or(std::ptr::null()),
); if font_ref.is_null() { // CTFontCreateUIFontForLanguage can fail, but is unlikely to do so during // normal usage (if you pass a bad ui_type it will). To make things more // convenient, just panic if it fails.
panic!();
} else {
CTFont::wrap_under_create_rule(font_ref)
}
}
}
// Names pubfn get_string_by_name_key(&self, name_key: CTFontNameSpecifier) -> Option<String> { unsafe { let result = CTFontCopyName(self.as_concrete_TypeRef(), name_key.into()); if result.is_null() {
None
} else {
Some(CFString::wrap_under_create_rule(result).to_string())
}
}
}
pubfn family_name(&self) -> String { let value = self.get_string_by_name_key(CTFontNameSpecifier::Family);
value.expect("Fonts should always have a family name.")
}
pubfn face_name(&self) -> String { let value = self.get_string_by_name_key(CTFontNameSpecifier::SubFamily);
value.expect("Fonts should always have a face name.")
}
pubfn unique_name(&self) -> String { let value = self.get_string_by_name_key(CTFontNameSpecifier::Unique);
value.expect("Fonts should always have a unique name.")
}
pubfn postscript_name(&self) -> String { let value = self.get_string_by_name_key(CTFontNameSpecifier::PostScript);
value.expect("Fonts should always have a PostScript name.")
}
pubfn display_name(&self) -> String { let value = self.get_string_by_name_key(CTFontNameSpecifier::Full);
value.expect("Fonts should always have a PostScript name.")
}
pubfn style_name(&self) -> String { let value = self.get_string_by_name_key(CTFontNameSpecifier::Style);
value.expect("Fonts should always have a style name.")
}
// N.B. Unlike most Cocoa bindings, this extern block is organized according // to the documentation's Functions By Task listing, because there so many functions.
#[test] fn copy_font() { use std::io::Read; letmut f = std::fs::File::open("/System/Library/Fonts/ZapfDingbats.ttf").unwrap(); letmut font_data = Vec::new();
f.read_to_end(&mut font_data).unwrap(); let desc = crate::font_manager::create_font_descriptor(&font_data).unwrap(); let font = new_from_descriptor(&desc, 12.);
drop(desc); let desc = font.copy_descriptor();
drop(font); let font = new_from_descriptor(&desc, 14.);
assert_eq!(font.family_name(), "Zapf Dingbats");
}
#[cfg(test)] fn macos_version() -> (i32, i32, i32) { use std::io::Read;
// This is the same approach that Firefox uses for detecting versions let file = "/System/Library/CoreServices/SystemVersion.plist"; letmut f = std::fs::File::open(file).unwrap(); letmut system_version_data = Vec::new();
f.read_to_end(&mut system_version_data).unwrap();
use core_foundation::propertylist; let (list, _) = propertylist::create_with_data(
core_foundation::data::CFData::from_buffer(&system_version_data),
propertylist::kCFPropertyListImmutable,
)
.unwrap(); let k = unsafe { propertylist::CFPropertyList::wrap_under_create_rule(list) };
let small = new_ui_font_for_language(kCTFontSystemDetailFontType, 19., None); let big = small.clone_with_font_size(20.);
// ensure that we end up with different fonts for the different sizes before 10.15 if macos_version() < (10, 15, 0) {
assert_ne!(big.url(), small.url());
} else {
assert_eq!(big.url(), small.url());
}
let ps = small.postscript_name(); let desc = small.copy_descriptor();
// check that we can construct a new vesion by descriptor let ctfont = new_from_descriptor(&desc, 20.);
assert_eq!(big.postscript_name(), ctfont.postscript_name());
// check that we can construct a new version by attributes let attr = desc.attributes(); let desc_from_attr = font_descriptor::new_from_attributes(&attr); let font_from_attr = new_from_descriptor(&desc_from_attr, 19.);
assert_eq!(font_from_attr.postscript_name(), small.postscript_name());
// on newer versions of macos we can't construct by name anymore if macos_version() < (10, 13, 0) { let ui_font_by_name = new_from_name(&small.postscript_name(), 19.).unwrap();
assert_eq!(ui_font_by_name.postscript_name(), small.postscript_name());
let ui_font_by_name = new_from_name(&small.postscript_name(), 20.).unwrap();
assert_eq!(ui_font_by_name.postscript_name(), small.postscript_name());
let ui_font_by_name = new_from_name(&big.postscript_name(), 20.).unwrap();
assert_eq!(ui_font_by_name.postscript_name(), big.postscript_name());
}
// but we can still construct the CGFont by name let cgfont = CGFont::from_name(&CFString::new(&ps)).unwrap(); let cgfont = new_from_CGFont(&cgfont, 0.);
println!("{:?}", cgfont); let desc = cgfont.copy_descriptor(); let matching = unsafe { crate::font_descriptor::CTFontDescriptorCreateMatchingFontDescriptor(
desc.as_concrete_TypeRef(),
std::ptr::null(),
)
}; let matching = unsafe { CTFontDescriptor::wrap_under_create_rule(matching) };
let variation_attribute = unsafe { CFString::wrap_under_get_rule(font_descriptor::kCTFontVariationAttribute) }; let size_attribute = unsafe { CFString::wrap_under_get_rule(font_descriptor::kCTFontSizeAttribute) };
let sys_font = new_ui_font_for_language(kCTFontSystemDetailFontType, 19., None);
// but we can still construct the CGFont by name let create_vars = |desc| { let vals: Vec<(CFNumber, CFNumber)> =
vec![(CFNumber::from(0x6f70737a), CFNumber::from(17.))]; let vals_dict = CFDictionary::from_CFType_pairs(&vals); let attrs_dict =
CFDictionary::from_CFType_pairs(&[(variation_attribute.clone(), vals_dict)]); let size_attrs_dict =
CFDictionary::from_CFType_pairs(&[(size_attribute.clone(), CFNumber::from(120.))]);
dbg!(&desc); let from_font_desc = new_from_descriptor(&desc, 120.).copy_descriptor(); let resized_font_desc = desc
.create_copy_with_attributes(size_attrs_dict.to_untyped())
.unwrap(); if macos_version() >= (11, 0, 0) {
assert_eq!(from_font_desc, resized_font_desc);
} else { // we won't have a name if we're using system font desc if from_font_desc
.attributes()
.find(unsafe { font_descriptor::kCTFontNameAttribute })
.is_none()
{ // it's surprising that desc's are the not equal but the attributes are
assert_ne!(from_font_desc, resized_font_desc);
assert_eq!(
from_font_desc.attributes().to_untyped(),
resized_font_desc.attributes().to_untyped()
);
} elseif macos_version() >= (10, 13, 0) { // this is unsurprising
assert_ne!(from_font_desc, resized_font_desc);
assert_ne!(
from_font_desc.attributes().to_untyped(),
resized_font_desc.attributes().to_untyped()
);
} else {
assert_ne!(from_font_desc, resized_font_desc);
assert_eq!(
from_font_desc.attributes().to_untyped(),
resized_font_desc.attributes().to_untyped()
);
}
}
let from_font_desc = from_font_desc
.create_copy_with_attributes(attrs_dict.to_untyped())
.unwrap(); let resized_font_desc = resized_font_desc
.create_copy_with_attributes(attrs_dict.to_untyped())
.unwrap();
(from_font_desc, resized_font_desc)
};
// setting the variation works properly if we use system font desc let (from_font_desc, resized_font_desc) = create_vars(sys_font.copy_descriptor());
assert_eq!(from_font_desc, resized_font_desc);
assert!(resized_font_desc
.attributes()
.find(variation_attribute.clone())
.is_some());
// but doesn't if we refer to it by name let ps = sys_font.postscript_name(); let cgfont = CGFont::from_name(&CFString::new(&ps)).unwrap(); let ctfont = new_from_CGFont(&cgfont, 0.);
let (from_font_desc, resized_font_desc) = create_vars(ctfont.copy_descriptor()); if macos_version() >= (10, 15, 0) {
assert_ne!(from_font_desc, resized_font_desc);
}
let small = new_ui_font_for_language(kCTFontSystemDetailFontType, 19., None);
// but we can still construct the CGFont by name let ps = small.postscript_name(); let cgfont = CGFont::from_name(&CFString::new(&ps)).unwrap(); let cgfont = new_from_CGFont(&cgfont, 0.); let desc = cgfont.copy_descriptor();
let vals: Vec<(CFNumber, CFNumber)> =
vec![(CFNumber::from(0x6f70737a /* opsz */), CFNumber::from(17.))]; let vals_dict = CFDictionary::from_CFType_pairs(&vals); let variation_attribute = unsafe { CFString::wrap_under_get_rule(font_descriptor::kCTFontVariationAttribute) }; let attrs_dict = CFDictionary::from_CFType_pairs(&[(variation_attribute, vals_dict)]); let ct_var_font_desc = desc
.create_copy_with_attributes(attrs_dict.to_untyped())
.unwrap(); let attrs = ct_var_font_desc.attributes(); let var_attr = attrs.find(CFString::from_static_string("NSCTFontVariationAttribute")); if macos_version() >= (11, 0, 0) { // the variation goes away
assert!(var_attr.is_none());
} else {
assert!(var_attr.is_some());
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.