From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- third_party/rust/core-text/src/font.rs | 1160 ++++++++++++++++++++ third_party/rust/core-text/src/font_collection.rs | 136 +++ third_party/rust/core-text/src/font_descriptor.rs | 441 ++++++++ third_party/rust/core-text/src/font_manager.rs | 76 ++ third_party/rust/core-text/src/frame.rs | 94 ++ third_party/rust/core-text/src/framesetter.rs | 94 ++ third_party/rust/core-text/src/lib.rs | 34 + third_party/rust/core-text/src/line.rs | 125 +++ third_party/rust/core-text/src/run.rs | 159 +++ .../rust/core-text/src/string_attributes.rs | 19 + 10 files changed, 2338 insertions(+) create mode 100644 third_party/rust/core-text/src/font.rs create mode 100644 third_party/rust/core-text/src/font_collection.rs create mode 100644 third_party/rust/core-text/src/font_descriptor.rs create mode 100644 third_party/rust/core-text/src/font_manager.rs create mode 100644 third_party/rust/core-text/src/frame.rs create mode 100644 third_party/rust/core-text/src/framesetter.rs create mode 100644 third_party/rust/core-text/src/lib.rs create mode 100644 third_party/rust/core-text/src/line.rs create mode 100644 third_party/rust/core-text/src/run.rs create mode 100644 third_party/rust/core-text/src/string_attributes.rs (limited to 'third_party/rust/core-text/src') diff --git a/third_party/rust/core-text/src/font.rs b/third_party/rust/core-text/src/font.rs new file mode 100644 index 0000000000..0d303a63fe --- /dev/null +++ b/third_party/rust/core-text/src/font.rs @@ -0,0 +1,1160 @@ +// 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 or the MIT license +// , 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 ::CType; +type CGFontRef = *mut ::CType; +type CGPathRef = *mut ::CType; + +pub type CTFontUIFontType = u32; +// kCTFontNoFontType: CTFontUIFontType = -1; +pub const kCTFontUserFontType: CTFontUIFontType = 0; +pub const kCTFontUserFixedPitchFontType: CTFontUIFontType = 1; +pub const kCTFontSystemFontType: CTFontUIFontType = 2; +pub const kCTFontEmphasizedSystemFontType: CTFontUIFontType = 3; +pub const kCTFontSmallSystemFontType: CTFontUIFontType = 4; +pub const kCTFontSmallEmphasizedSystemFontType: CTFontUIFontType = 5; +pub const kCTFontMiniSystemFontType: CTFontUIFontType = 6; +pub const kCTFontMiniEmphasizedSystemFontType: CTFontUIFontType = 7; +pub const kCTFontViewsFontType: CTFontUIFontType = 8; +pub const kCTFontApplicationFontType: CTFontUIFontType = 9; +pub const kCTFontLabelFontType: CTFontUIFontType = 10; +pub const kCTFontMenuTitleFontType: CTFontUIFontType = 11; +pub const kCTFontMenuItemFontType: CTFontUIFontType = 12; +pub const kCTFontMenuItemMarkFontType: CTFontUIFontType = 13; +pub const kCTFontMenuItemCmdKeyFontType: CTFontUIFontType = 14; +pub const kCTFontWindowTitleFontType: CTFontUIFontType = 15; +pub const kCTFontPushButtonFontType: CTFontUIFontType = 16; +pub const kCTFontUtilityWindowTitleFontType: CTFontUIFontType = 17; +pub const kCTFontAlertHeaderFontType: CTFontUIFontType = 18; +pub const kCTFontSystemDetailFontType: CTFontUIFontType = 19; +pub const kCTFontEmphasizedSystemDetailFontType: CTFontUIFontType = 20; +pub const kCTFontToolbarFontType: CTFontUIFontType = 21; +pub const kCTFontSmallToolbarFontType: CTFontUIFontType = 22; +pub const kCTFontMessageFontType: CTFontUIFontType = 23; +pub const kCTFontPaletteFontType: CTFontUIFontType = 24; +pub const kCTFontToolTipFontType: CTFontUIFontType = 25; +pub const kCTFontControlContentFontType: CTFontUIFontType = 26; + +pub type CTFontTableTag = u32; +// TODO: create bindings for enum with 'chars' values + +pub type CTFontTableOptions = u32; +pub const kCTFontTableOptionsNoOptions: CTFontTableOptions = 0; +pub const kCTFontTableOptionsExcludeSynthetic: CTFontTableOptions = 1 << 0; + +pub type CTFontOptions = CFOptionFlags; +pub const kCTFontOptionsDefault: CTFontOptions = 0; +pub const kCTFontOptionsPreventAutoActivation: CTFontOptions = 1 << 0; +pub const kCTFontOptionsPreferSystemFont: CTFontOptions = 1 << 2; + +pub enum CTFontNameSpecifier { + Copyright, + Family, + SubFamily, + Style, + Unique, + Full, + Version, + PostScript, + Trademark, + Manufacturer, + Designer, + Description, + VendorURL, + DesignerURL, + License, + LicenseURL, + SampleText, + PostScriptCID, +} + +impl From for CFStringRef { + fn from(val: CTFontNameSpecifier) -> Self { + unsafe { + match val { + CTFontNameSpecifier::Copyright => kCTFontCopyrightNameKey, + CTFontNameSpecifier::Family => kCTFontFamilyNameKey, + CTFontNameSpecifier::SubFamily => kCTFontSubFamilyNameKey, + CTFontNameSpecifier::Style => kCTFontStyleNameKey, + CTFontNameSpecifier::Unique => kCTFontUniqueNameKey, + CTFontNameSpecifier::Full => kCTFontFullNameKey, + CTFontNameSpecifier::Version => kCTFontVersionNameKey, + CTFontNameSpecifier::PostScript => kCTFontPostScriptNameKey, + CTFontNameSpecifier::Trademark => kCTFontTrademarkNameKey, + CTFontNameSpecifier::Manufacturer => kCTFontManufacturerNameKey, + CTFontNameSpecifier::Designer => kCTFontDesignerNameKey, + CTFontNameSpecifier::Description => kCTFontDescriptionNameKey, + CTFontNameSpecifier::VendorURL => kCTFontVendorURLNameKey, + CTFontNameSpecifier::DesignerURL => kCTFontDesignerURLNameKey, + CTFontNameSpecifier::License => kCTFontLicenseNameKey, + CTFontNameSpecifier::LicenseURL => kCTFontLicenseURLNameKey, + CTFontNameSpecifier::SampleText => kCTFontSampleTextNameKey, + CTFontNameSpecifier::PostScriptCID => kCTFontPostScriptCIDNameKey, + } + } + } +} + +#[repr(C)] +pub struct __CTFont(c_void); + +pub type CTFontRef = *const __CTFont; + +declare_TCFType! { + CTFont, CTFontRef +} +impl_TCFType!(CTFont, CTFontRef, CTFontGetTypeID); +impl_CFTypeDescription!(CTFont); + +unsafe impl Send for CTFont {} +unsafe impl Sync for CTFont {} + +pub fn new_from_CGFont(cgfont: &CGFont, pt_size: f64) -> CTFont { + unsafe { + let font_ref = CTFontCreateWithGraphicsFont( + cgfont.as_ptr() as *mut _, + pt_size as CGFloat, + ptr::null(), + ptr::null(), + ); + CTFont::wrap_under_create_rule(font_ref) + } +} + +pub fn new_from_CGFont_with_variations( + cgfont: &CGFont, + pt_size: f64, + variations: &CFDictionary, +) -> CTFont { + unsafe { + let font_desc = font_descriptor::new_from_variations(variations); + let font_ref = CTFontCreateWithGraphicsFont( + cgfont.as_ptr() as *mut _, + pt_size as CGFloat, + ptr::null(), + font_desc.as_concrete_TypeRef(), + ); + CTFont::wrap_under_create_rule(font_ref) + } +} + +pub fn new_from_descriptor(desc: &CTFontDescriptor, pt_size: f64) -> CTFont { + unsafe { + let font_ref = CTFontCreateWithFontDescriptor( + desc.as_concrete_TypeRef(), + pt_size as CGFloat, + ptr::null(), + ); + CTFont::wrap_under_create_rule(font_ref) + } +} + +pub fn new_from_descriptor_and_options( + desc: &CTFontDescriptor, + pt_size: f64, + options: CTFontOptions, +) -> CTFont { + unsafe { + let font_ref = CTFontCreateWithFontDescriptorAndOptions( + desc.as_concrete_TypeRef(), + pt_size as CGFloat, + ptr::null(), + options, + ); + CTFont::wrap_under_create_rule(font_ref) + } +} + +pub fn new_from_buffer(buffer: &[u8]) -> Result { + let ct_font_descriptor = create_font_descriptor(buffer)?; + Ok(new_from_descriptor(&ct_font_descriptor, 16.0)) +} + +pub fn new_from_name(name: &str, pt_size: f64) -> Result { + unsafe { + let name: CFString = name.parse().unwrap(); + let font_ref = + CTFontCreateWithName(name.as_concrete_TypeRef(), pt_size as CGFloat, ptr::null()); + if font_ref.is_null() { + Err(()) + } else { + Ok(CTFont::wrap_under_create_rule(font_ref)) + } + } +} + +pub fn new_from_name_and_options( + name: &str, + pt_size: f64, + options: CTFontOptions, +) -> Result { + unsafe { + let name: CFString = name.parse().unwrap(); + let font_ref = CTFontCreateWithNameAndOptions( + name.as_concrete_TypeRef(), + pt_size as CGFloat, + ptr::null(), + options, + ); + if font_ref.is_null() { + Err(()) + } else { + Ok(CTFont::wrap_under_create_rule(font_ref)) + } + } +} + +pub fn new_ui_font_for_language( + ui_type: CTFontUIFontType, + size: f64, + language: Option, +) -> 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) + } + } +} + +impl CTFont { + // Properties + pub fn symbolic_traits(&self) -> CTFontSymbolicTraits { + unsafe { CTFontGetSymbolicTraits(self.0) } + } +} + +impl CTFont { + // Creation methods + pub fn copy_to_CGFont(&self) -> CGFont { + unsafe { + let cgfont_ref = CTFontCopyGraphicsFont(self.0, ptr::null_mut()); + CGFont::from_ptr(cgfont_ref as *mut _) + } + } + + pub fn copy_descriptor(&self) -> CTFontDescriptor { + unsafe { + let desc = CTFontCopyFontDescriptor(self.0); + CTFontDescriptor::wrap_under_create_rule(desc) + } + } + + pub fn clone_with_font_size(&self, size: f64) -> CTFont { + unsafe { + let font_ref = + CTFontCreateCopyWithAttributes(self.0, size as CGFloat, ptr::null(), ptr::null()); + CTFont::wrap_under_create_rule(font_ref) + } + } + + pub fn clone_with_symbolic_traits( + &self, + trait_value: CTFontSymbolicTraits, + trait_mask: CTFontSymbolicTraits, + ) -> Option { + unsafe { + let font_ref = CTFontCreateCopyWithSymbolicTraits( + self.0, + 0.0, + ptr::null(), + trait_value, + trait_mask, + ); + if font_ref.is_null() { + None + } else { + Some(CTFont::wrap_under_create_rule(font_ref)) + } + } + } + + // Names + pub fn get_string_by_name_key(&self, name_key: CTFontNameSpecifier) -> Option { + 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()) + } + } + } + + pub fn family_name(&self) -> String { + let value = self.get_string_by_name_key(CTFontNameSpecifier::Family); + value.expect("Fonts should always have a family name.") + } + + pub fn face_name(&self) -> String { + let value = self.get_string_by_name_key(CTFontNameSpecifier::SubFamily); + value.expect("Fonts should always have a face name.") + } + + pub fn unique_name(&self) -> String { + let value = self.get_string_by_name_key(CTFontNameSpecifier::Unique); + value.expect("Fonts should always have a unique name.") + } + + pub fn postscript_name(&self) -> String { + let value = self.get_string_by_name_key(CTFontNameSpecifier::PostScript); + value.expect("Fonts should always have a PostScript name.") + } + + pub fn display_name(&self) -> String { + let value = self.get_string_by_name_key(CTFontNameSpecifier::Full); + value.expect("Fonts should always have a PostScript name.") + } + + pub fn style_name(&self) -> String { + let value = self.get_string_by_name_key(CTFontNameSpecifier::Style); + value.expect("Fonts should always have a style name.") + } + + pub fn all_traits(&self) -> CTFontTraits { + unsafe { CTFontTraits::wrap_under_create_rule(CTFontCopyTraits(self.0)) } + } + + // Font metrics + pub fn ascent(&self) -> CGFloat { + unsafe { CTFontGetAscent(self.0) } + } + + pub fn descent(&self) -> CGFloat { + unsafe { CTFontGetDescent(self.0) } + } + + pub fn underline_thickness(&self) -> CGFloat { + unsafe { CTFontGetUnderlineThickness(self.0) } + } + + pub fn underline_position(&self) -> CGFloat { + unsafe { CTFontGetUnderlinePosition(self.0) } + } + + pub fn slant_angle(&self) -> CGFloat { + unsafe { CTFontGetSlantAngle(self.0) } + } + + pub fn cap_height(&self) -> CGFloat { + unsafe { CTFontGetCapHeight(self.0) } + } + + pub fn bounding_box(&self) -> CGRect { + unsafe { CTFontGetBoundingBox(self.0) } + } + + pub fn leading(&self) -> CGFloat { + unsafe { CTFontGetLeading(self.0) } + } + + pub fn units_per_em(&self) -> libc::c_uint { + unsafe { CTFontGetUnitsPerEm(self.0) } + } + + pub fn x_height(&self) -> CGFloat { + unsafe { CTFontGetXHeight(self.0) } + } + + pub fn pt_size(&self) -> CGFloat { + unsafe { CTFontGetSize(self.0) } + } + + pub fn get_glyph_with_name(&self, glyph_name: &str) -> CGGlyph { + let glyph_name = CFString::new(glyph_name); + unsafe { CTFontGetGlyphWithName(self.0, glyph_name.as_concrete_TypeRef()) } + } + + pub unsafe fn get_glyphs_for_characters( + &self, + characters: *const UniChar, + glyphs: *mut CGGlyph, + count: CFIndex, + ) -> bool { + CTFontGetGlyphsForCharacters(self.0, characters, glyphs, count) + } + + pub unsafe fn get_advances_for_glyphs( + &self, + orientation: CTFontOrientation, + glyphs: *const CGGlyph, + advances: *mut CGSize, + count: CFIndex, + ) -> f64 { + CTFontGetAdvancesForGlyphs(self.0, orientation, glyphs, advances, count) + } + + pub unsafe fn get_vertical_translations_for_glyphs( + &self, + orientation: CTFontOrientation, + glyphs: *const CGGlyph, + translations: *mut CGSize, + count: CFIndex, + ) { + CTFontGetVerticalTranslationsForGlyphs(self.0, orientation, glyphs, translations, count) + } + + pub fn get_font_table(&self, tag: u32) -> Option { + unsafe { + let result = CTFontCopyTable( + self.0, + tag as CTFontTableTag, + kCTFontTableOptionsExcludeSynthetic, + ); + if result.is_null() { + None + } else { + Some(CFData::wrap_under_create_rule(result)) + } + } + } + + pub fn get_available_font_tables(&self) -> Option> { + unsafe { + let result = CTFontCopyAvailableTables(self.0, kCTFontTableOptionsExcludeSynthetic); + if result.is_null() { + None + } else { + Some(TCFType::wrap_under_create_rule(result)) + } + } + } + + pub fn get_bounding_rects_for_glyphs( + &self, + orientation: CTFontOrientation, + glyphs: &[CGGlyph], + ) -> CGRect { + unsafe { + CTFontGetBoundingRectsForGlyphs( + self.as_concrete_TypeRef(), + orientation, + glyphs.as_ptr(), + ptr::null_mut(), + glyphs.len() as CFIndex, + ) + } + } + + pub fn draw_glyphs(&self, glyphs: &[CGGlyph], positions: &[CGPoint], context: CGContext) { + assert_eq!(glyphs.len(), positions.len()); + unsafe { + CTFontDrawGlyphs( + self.as_concrete_TypeRef(), + glyphs.as_ptr(), + positions.as_ptr(), + glyphs.len() as size_t, + context.as_ptr(), + ) + } + } + + pub fn url(&self) -> Option { + unsafe { + let result = CTFontCopyAttribute(self.0, kCTFontURLAttribute); + if result.is_null() { + None + } else { + Some(CFURL::wrap_under_create_rule(result as CFURLRef)) + } + } + } + + pub fn get_variation_axes(&self) -> Option>> { + unsafe { + let axes = CTFontCopyVariationAxes(self.0); + if axes.is_null() { + return None; + } + Some(TCFType::wrap_under_create_rule(axes)) + } + } + + pub fn create_path_for_glyph( + &self, + glyph: CGGlyph, + matrix: &CGAffineTransform, + ) -> Result { + unsafe { + let path = CTFontCreatePathForGlyph(self.0, glyph, matrix); + if path.is_null() { + Err(()) + } else { + Ok(CGPath::from_ptr(path)) + } + } + } + + #[inline] + pub fn glyph_count(&self) -> CFIndex { + unsafe { CTFontGetGlyphCount(self.0) } + } +} + +// Helper methods +pub fn debug_font_names(font: &CTFont) { + fn get_key(font: &CTFont, key: CTFontNameSpecifier) -> String { + font.get_string_by_name_key(key).unwrap() + } + + println!( + "kCTFontFamilyNameKey: {}", + get_key(font, CTFontNameSpecifier::Family) + ); + println!( + "kCTFontSubFamilyNameKey: {}", + get_key(font, CTFontNameSpecifier::SubFamily) + ); + println!( + "kCTFontStyleNameKey: {}", + get_key(font, CTFontNameSpecifier::Style) + ); + println!( + "kCTFontUniqueNameKey: {}", + get_key(font, CTFontNameSpecifier::Unique) + ); + println!( + "kCTFontFullNameKey: {}", + get_key(font, CTFontNameSpecifier::Full) + ); + println!( + "kCTFontPostScriptNameKey: {}", + get_key(font, CTFontNameSpecifier::PostScript) + ); +} + +pub fn debug_font_traits(font: &CTFont) { + let sym = font.symbolic_traits(); + println!("kCTFontItalicTrait: {}", sym.is_italic()); + println!("kCTFontBoldTrait: {}", sym.is_bold()); + println!("kCTFontExpandedTrait: {}", sym.is_expanded()); + println!("kCTFontCondensedTrait: {}", sym.is_condensed()); + println!("kCTFontMonoSpaceTrait: {}", sym.is_monospace()); + + let traits = font.all_traits(); + println!("kCTFontWeightTrait: {}", traits.normalized_weight()); + println!("kCTFontWidthTrait: {}", traits.normalized_width()); + // println!("kCTFontSlantTrait: {}", traits.normalized_slant()); +} + +#[cfg(feature = "mountainlion")] +pub fn cascade_list_for_languages( + font: &CTFont, + language_pref_list: &CFArray, +) -> CFArray { + unsafe { + let font_collection_ref = CTFontCopyDefaultCascadeListForLanguages( + font.as_concrete_TypeRef(), + language_pref_list.as_concrete_TypeRef(), + ); + CFArray::wrap_under_create_rule(font_collection_ref) + } +} + +#[link(name = "CoreText", kind = "framework")] +extern "C" { + /* + * CTFont.h + */ + + /* Name Specifier Constants */ + static kCTFontCopyrightNameKey: CFStringRef; + static kCTFontFamilyNameKey: CFStringRef; + static kCTFontSubFamilyNameKey: CFStringRef; + static kCTFontStyleNameKey: CFStringRef; + static kCTFontUniqueNameKey: CFStringRef; + static kCTFontFullNameKey: CFStringRef; + static kCTFontVersionNameKey: CFStringRef; + static kCTFontPostScriptNameKey: CFStringRef; + static kCTFontTrademarkNameKey: CFStringRef; + static kCTFontManufacturerNameKey: CFStringRef; + static kCTFontDesignerNameKey: CFStringRef; + static kCTFontDescriptionNameKey: CFStringRef; + static kCTFontVendorURLNameKey: CFStringRef; + static kCTFontDesignerURLNameKey: CFStringRef; + static kCTFontLicenseNameKey: CFStringRef; + static kCTFontLicenseURLNameKey: CFStringRef; + static kCTFontSampleTextNameKey: CFStringRef; + static kCTFontPostScriptCIDNameKey: CFStringRef; + + #[cfg(test)] + static kCTFontVariationAxisIdentifierKey: CFStringRef; + //static kCTFontVariationAxisMinimumValueKey: CFStringRef; + #[cfg(test)] + static kCTFontVariationAxisMaximumValueKey: CFStringRef; + //static kCTFontVariationAxisDefaultValueKey: CFStringRef; + //static kCTFontVariationAxisNameKey: CFStringRef; + + //static kCTFontFeatureTypeIdentifierKey: CFStringRef; + //static kCTFontFeatureTypeNameKey: CFStringRef; + //static kCTFontFeatureTypeExclusiveKey: CFStringRef; + //static kCTFontFeatureTypeSelectorsKey: CFStringRef; + //static kCTFontFeatureSelectorIdentifierKey: CFStringRef; + //static kCTFontFeatureSelectorNameKey: CFStringRef; + //static kCTFontFeatureSelectorDefaultKey: CFStringRef; + //static kCTFontFeatureSelectorSettingKey: CFStringRef; + + static kCTFontURLAttribute: CFStringRef; + + // 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. + + /* Creating Fonts */ + fn CTFontCreateWithName( + name: CFStringRef, + size: CGFloat, + matrix: *const CGAffineTransform, + ) -> CTFontRef; + fn CTFontCreateWithNameAndOptions( + name: CFStringRef, + size: CGFloat, + matrix: *const CGAffineTransform, + options: CTFontOptions, + ) -> CTFontRef; + fn CTFontCreateWithFontDescriptor( + descriptor: CTFontDescriptorRef, + size: CGFloat, + matrix: *const CGAffineTransform, + ) -> CTFontRef; + fn CTFontCreateWithFontDescriptorAndOptions( + descriptor: CTFontDescriptorRef, + size: CGFloat, + matrix: *const CGAffineTransform, + options: CTFontOptions, + ) -> CTFontRef; + fn CTFontCreateUIFontForLanguage( + uiType: CTFontUIFontType, + size: CGFloat, + language: CFStringRef, + ) -> CTFontRef; + fn CTFontCreateCopyWithAttributes( + font: CTFontRef, + size: CGFloat, + matrix: *const CGAffineTransform, + attributes: CTFontDescriptorRef, + ) -> CTFontRef; + fn CTFontCreateCopyWithSymbolicTraits( + font: CTFontRef, + size: CGFloat, + matrix: *const CGAffineTransform, + symTraitValue: CTFontSymbolicTraits, + symTraitMask: CTFontSymbolicTraits, + ) -> CTFontRef; + //fn CTFontCreateCopyWithFamily + //fn CTFontCreateForString + + /* Getting Font Data */ + fn CTFontCopyFontDescriptor(font: CTFontRef) -> CTFontDescriptorRef; + fn CTFontCopyAttribute(font: CTFontRef, attribute: CFStringRef) -> CFTypeRef; + fn CTFontGetSize(font: CTFontRef) -> CGFloat; + //fn CTFontGetMatrix + fn CTFontGetSymbolicTraits(font: CTFontRef) -> CTFontSymbolicTraits; + fn CTFontCopyTraits(font: CTFontRef) -> CFDictionaryRef; + + /* Getting Font Names */ + //fn CTFontCopyPostScriptName(font: CTFontRef) -> CFStringRef; + //fn CTFontCopyFamilyName(font: CTFontRef) -> CFStringRef; + //fn CTFontCopyFullName(font: CTFontRef) -> CFStringRef; + //fn CTFontCopyDisplayName(font: CTFontRef) -> CFStringRef; + fn CTFontCopyName(font: CTFontRef, nameKey: CFStringRef) -> CFStringRef; + //fn CTFontCopyLocalizedName(font: CTFontRef, nameKey: CFStringRef, + // language: *CFStringRef) -> CFStringRef; + #[cfg(feature = "mountainlion")] + fn CTFontCopyDefaultCascadeListForLanguages( + font: CTFontRef, + languagePrefList: CFArrayRef, + ) -> CFArrayRef; + + /* Working With Encoding */ + //fn CTFontCopyCharacterSet + //fn CTFontGetStringEncoding + //fn CTFontCopySupportedLanguages + + /* Getting Font Metrics */ + fn CTFontGetAscent(font: CTFontRef) -> CGFloat; + fn CTFontGetDescent(font: CTFontRef) -> CGFloat; + fn CTFontGetLeading(font: CTFontRef) -> CGFloat; + fn CTFontGetUnitsPerEm(font: CTFontRef) -> libc::c_uint; + fn CTFontGetGlyphCount(font: CTFontRef) -> CFIndex; + fn CTFontGetBoundingBox(font: CTFontRef) -> CGRect; + fn CTFontGetUnderlinePosition(font: CTFontRef) -> CGFloat; + fn CTFontGetUnderlineThickness(font: CTFontRef) -> CGFloat; + fn CTFontGetSlantAngle(font: CTFontRef) -> CGFloat; + fn CTFontGetCapHeight(font: CTFontRef) -> CGFloat; + fn CTFontGetXHeight(font: CTFontRef) -> CGFloat; + + /* Getting Glyph Data */ + fn CTFontCreatePathForGlyph( + font: CTFontRef, + glyph: CGGlyph, + matrix: *const CGAffineTransform, + ) -> CGPathRef; + fn CTFontGetGlyphWithName(font: CTFontRef, glyphName: CFStringRef) -> CGGlyph; + fn CTFontGetBoundingRectsForGlyphs( + font: CTFontRef, + orientation: CTFontOrientation, + glyphs: *const CGGlyph, + boundingRects: *mut CGRect, + count: CFIndex, + ) -> CGRect; + fn CTFontGetAdvancesForGlyphs( + font: CTFontRef, + orientation: CTFontOrientation, + glyphs: *const CGGlyph, + advances: *mut CGSize, + count: CFIndex, + ) -> libc::c_double; + fn CTFontGetVerticalTranslationsForGlyphs( + font: CTFontRef, + orientation: CTFontOrientation, + glyphs: *const CGGlyph, + translations: *mut CGSize, + count: CFIndex, + ); + + /* Working With Font Variations */ + fn CTFontCopyVariationAxes(font: CTFontRef) -> CFArrayRef; + //fn CTFontCopyVariation + + /* Getting Font Features */ + //fn CTFontCopyFeatures + //fn CTFontCopyFeatureSettings + + /* Working with Glyphs */ + fn CTFontGetGlyphsForCharacters( + font: CTFontRef, + characters: *const UniChar, + glyphs: *mut CGGlyph, + count: CFIndex, + ) -> bool; + fn CTFontDrawGlyphs( + font: CTFontRef, + glyphs: *const CGGlyph, + positions: *const CGPoint, + count: size_t, + context: CGContextRef, + ); + //fn CTFontGetLigatureCaretPositions + + /* Converting Fonts */ + fn CTFontCopyGraphicsFont(font: CTFontRef, attributes: *mut CTFontDescriptorRef) -> CGFontRef; + fn CTFontCreateWithGraphicsFont( + graphicsFont: CGFontRef, + size: CGFloat, + matrix: *const CGAffineTransform, + attributes: CTFontDescriptorRef, + ) -> CTFontRef; + //fn CTFontGetPlatformFont + //fn CTFontCreateWithPlatformFont + //fn CTFontCreateWithQuickdrawInstance + + /* Getting Font Table Data */ + fn CTFontCopyAvailableTables(font: CTFontRef, options: CTFontTableOptions) -> CFArrayRef; + fn CTFontCopyTable( + font: CTFontRef, + table: CTFontTableTag, + options: CTFontTableOptions, + ) -> CFDataRef; + + fn CTFontGetTypeID() -> CFTypeID; +} + +#[test] +fn copy_font() { + use std::io::Read; + let mut f = std::fs::File::open("/System/Library/Fonts/ZapfDingbats.ttf").unwrap(); + let mut 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"; + let mut f = std::fs::File::open(file).unwrap(); + let mut 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 dict = unsafe { + std::mem::transmute::<_, CFDictionary>( + k.downcast::().unwrap(), + ) + }; + + let version = dict + .find(&CFString::new("ProductVersion").as_CFType()) + .as_ref() + .unwrap() + .downcast::() + .unwrap() + .to_string(); + + match version + .split('.') + .map(|x| x.parse().unwrap()) + .collect::>()[..] + { + [a, b, c] => (a, b, c), + [a, b] => (a, b, 0), + _ => panic!(), + } +} + +#[test] +fn copy_system_font() { + use crate::*; + + 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) }; + + println!("{:?}", cgfont.copy_descriptor()); + assert!(desc + .attributes() + .find(CFString::from_static_string("NSFontSizeAttribute")) + .is_some()); + + println!("{:?}", matching); + println!( + "{:?}", + matching + .attributes() + .find(CFString::from_static_string("NSFontSizeAttribute")) + ); + + assert!(matching + .attributes() + .find(CFString::from_static_string("NSFontSizeAttribute")) + .is_none()); + + assert_eq!(small.postscript_name(), cgfont.postscript_name()); +} + +// Tests what happens when variations have values not inbetween min and max +#[test] +fn out_of_range_variations() { + use crate::*; + + let small = new_ui_font_for_language(kCTFontSystemDetailFontType, 19., None); + + let axes = small.get_variation_axes(); + if macos_version() < (10, 12, 0) { + assert!(axes.is_none()); + return; + } + let axes = axes.unwrap(); + let mut vals = Vec::new(); + dbg!(&axes); + for axis in axes.iter() { + let tag = axis + .find(unsafe { kCTFontVariationAxisIdentifierKey }) + .unwrap() + .downcast::() + .unwrap() + .to_i64() + .unwrap(); + let max = axis + .find(unsafe { kCTFontVariationAxisMaximumValueKey }) + .unwrap() + .downcast::() + .unwrap() + .to_f64() + .unwrap(); + vals.push((CFNumber::from(tag), CFNumber::from(max + 1.))); + } + 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.clone(), vals_dict)]); + let ct_var_font_desc = small + .copy_descriptor() + .create_copy_with_attributes(attrs_dict.to_untyped()) + .unwrap(); + let variation_font = crate::font::new_from_descriptor(&ct_var_font_desc, 19.); + let var_desc = variation_font.copy_descriptor(); + let var_attrs = var_desc.attributes(); + dbg!(&var_attrs); + // attributes greater than max are dropped on macOS <= 11 + // on macOS 12 they seem to be preserved as is. + let var_attrs = var_attrs.find(variation_attribute); + if macos_version() >= (12, 0, 0) && macos_version() < (13, 0, 0) { + let var_attrs = var_attrs.unwrap().downcast::().unwrap(); + assert!(!var_attrs.is_empty()); + let var_attrs: CFDictionary = unsafe { std::mem::transmute(var_attrs) }; + // attributes greater than max remain + for axis in axes.iter() { + let tag = axis + .find(unsafe { kCTFontVariationAxisIdentifierKey }) + .unwrap(); + let max = axis + .find(unsafe { kCTFontVariationAxisMaximumValueKey }) + .unwrap() + .downcast::() + .unwrap() + .to_f64() + .unwrap(); + let val = var_attrs + .find(tag.clone()) + .unwrap() + .downcast::() + .unwrap() + .to_f64() + .unwrap(); + assert_eq!(val, max + 1.); + } + } else if macos_version() >= (10, 15, 0) { + assert!(var_attrs.is_none()); + } else { + let var_attrs = var_attrs.unwrap().downcast::().unwrap(); + assert!(var_attrs.is_empty()); + } +} + +#[test] +fn equal_descriptor_different_font() { + use crate::*; + + 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() + ); + } else if 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); + } + + if macos_version() >= (10, 13, 0) { + assert!(from_font_desc + .attributes() + .find(variation_attribute.clone()) + .is_some()); + if macos_version() >= (11, 0, 0) { + assert!(resized_font_desc + .attributes() + .find(variation_attribute) + .is_none()); + } else { + assert!(resized_font_desc + .attributes() + .find(variation_attribute) + .is_some()); + }; + } +} + +#[test] +fn system_font_variation() { + use crate::*; + + 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()); + } + + dbg!(ct_var_font_desc); +} + +#[test] +fn ui_font() { + // pass some garbagey inputs + new_ui_font_for_language( + kCTFontSystemDetailFontType, + 10000009., + Some(CFString::from("Gofld")), + ); +} diff --git a/third_party/rust/core-text/src/font_collection.rs b/third_party/rust/core-text/src/font_collection.rs new file mode 100644 index 0000000000..bf5822249a --- /dev/null +++ b/third_party/rust/core-text/src/font_collection.rs @@ -0,0 +1,136 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use font_descriptor; +use font_descriptor::{CTFontDescriptor, CTFontDescriptorCreateMatchingFontDescriptors}; +use font_manager::{ + CTFontManagerCopyAvailableFontFamilyNames, CTFontManagerCopyAvailablePostScriptNames, +}; + +use core_foundation::array::{CFArray, CFArrayRef}; +use core_foundation::base::{CFTypeID, TCFType}; +use core_foundation::dictionary::{CFDictionary, CFDictionaryRef}; +use core_foundation::number::CFNumber; +use core_foundation::set::CFSet; +use core_foundation::string::{CFString, CFStringRef}; + +use std::os::raw::c_void; + +#[repr(C)] +pub struct __CTFontCollection(c_void); + +pub type CTFontCollectionRef = *const __CTFontCollection; + +declare_TCFType! { + CTFontCollection, CTFontCollectionRef +} +impl_TCFType!( + CTFontCollection, + CTFontCollectionRef, + CTFontCollectionGetTypeID +); +impl_CFTypeDescription!(CTFontCollection); + +impl CTFontCollection { + pub fn get_descriptors(&self) -> Option> { + // surprise! this function follows the Get rule, despite being named *Create*. + // So we have to addRef it to avoid CTFontCollection from double freeing it later. + unsafe { + let font_descriptors = CTFontCollectionCreateMatchingFontDescriptors(self.0); + if font_descriptors.is_null() { + // This returns null if there are no matching font descriptors. + None + } else { + Some(CFArray::wrap_under_get_rule(font_descriptors)) + } + } + } +} + +pub fn new_from_descriptors(descs: &CFArray) -> CTFontCollection { + unsafe { + let key = CFString::wrap_under_get_rule(kCTFontCollectionRemoveDuplicatesOption); + let value = CFNumber::from(1i64); + let options = CFDictionary::from_CFType_pairs(&[(key.as_CFType(), value.as_CFType())]); + let font_collection_ref = CTFontCollectionCreateWithFontDescriptors( + descs.as_concrete_TypeRef(), + options.as_concrete_TypeRef(), + ); + CTFontCollection::wrap_under_create_rule(font_collection_ref) + } +} + +pub fn create_for_all_families() -> CTFontCollection { + unsafe { + let key = CFString::wrap_under_get_rule(kCTFontCollectionRemoveDuplicatesOption); + let value = CFNumber::from(1i64); + let options = CFDictionary::from_CFType_pairs(&[(key.as_CFType(), value.as_CFType())]); + let font_collection_ref = + CTFontCollectionCreateFromAvailableFonts(options.as_concrete_TypeRef()); + CTFontCollection::wrap_under_create_rule(font_collection_ref) + } +} + +pub fn create_for_family(family: &str) -> Option { + use font_descriptor::kCTFontFamilyNameAttribute; + + unsafe { + let family_attr = CFString::wrap_under_get_rule(kCTFontFamilyNameAttribute); + let family_name: CFString = family.parse().unwrap(); + let specified_attrs = + CFDictionary::from_CFType_pairs(&[(family_attr.clone(), family_name.as_CFType())]); + + let wildcard_desc: CTFontDescriptor = + font_descriptor::new_from_attributes(&specified_attrs); + let mandatory_attrs = CFSet::from_slice(&[family_attr.as_CFType()]); + let matched_descs = CTFontDescriptorCreateMatchingFontDescriptors( + wildcard_desc.as_concrete_TypeRef(), + mandatory_attrs.as_concrete_TypeRef(), + ); + if matched_descs.is_null() { + return None; + } + let matched_descs = CFArray::wrap_under_create_rule(matched_descs); + // I suppose one doesn't even need the CTFontCollection object at this point. + // But we stick descriptors into and out of it just to provide a nice wrapper API. + Some(new_from_descriptors(&matched_descs)) + } +} + +pub fn get_family_names() -> CFArray { + unsafe { CFArray::wrap_under_create_rule(CTFontManagerCopyAvailableFontFamilyNames()) } +} + +pub fn get_postscript_names() -> CFArray { + unsafe { CFArray::wrap_under_create_rule(CTFontManagerCopyAvailablePostScriptNames()) } +} + +extern "C" { + /* + * CTFontCollection.h + */ + + static kCTFontCollectionRemoveDuplicatesOption: CFStringRef; + + //fn CTFontCollectionCreateCopyWithFontDescriptors(original: CTFontCollectionRef, + // descriptors: CFArrayRef, + // options: CFDictionaryRef) -> CTFontCollectionRef; + fn CTFontCollectionCreateFromAvailableFonts(options: CFDictionaryRef) -> CTFontCollectionRef; + // this stupid function doesn't actually do any wildcard expansion; + // it just chooses the best match. Use + // CTFontDescriptorCreateMatchingDescriptors instead. + fn CTFontCollectionCreateMatchingFontDescriptors(collection: CTFontCollectionRef) + -> CFArrayRef; + fn CTFontCollectionCreateWithFontDescriptors( + descriptors: CFArrayRef, + options: CFDictionaryRef, + ) -> CTFontCollectionRef; + //fn CTFontCollectionCreateMatchingFontDescriptorsSortedWithCallback; + fn CTFontCollectionGetTypeID() -> CFTypeID; +} diff --git a/third_party/rust/core-text/src/font_descriptor.rs b/third_party/rust/core-text/src/font_descriptor.rs new file mode 100644 index 0000000000..e5506d2162 --- /dev/null +++ b/third_party/rust/core-text/src/font_descriptor.rs @@ -0,0 +1,441 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(non_upper_case_globals)] + +use core_foundation::array::CFArrayRef; +use core_foundation::base::{CFType, CFTypeID, CFTypeRef, TCFType}; +use core_foundation::dictionary::{CFDictionary, CFDictionaryRef}; +use core_foundation::number::{CFNumber, CFNumberRef}; +use core_foundation::set::CFSetRef; +use core_foundation::string::{CFString, CFStringRef}; +use core_foundation::url::{CFURLRef, CFURL}; +use core_graphics::base::CGFloat; + +use std::os::raw::c_void; +use std::path::PathBuf; + +/* +* CTFontTraits.h +*/ +// actually, these are extern enums +pub type CTFontFormat = u32; +pub const kCTFontFormatUnrecognized: CTFontFormat = 0; +pub const kCTFontFormatOpenTypePostScript: CTFontFormat = 1; +pub const kCTFontFormatOpenTypeTrueType: CTFontFormat = 2; +pub const kCTFontFormatTrueType: CTFontFormat = 3; +pub const kCTFontFormatPostScript: CTFontFormat = 4; +pub const kCTFontFormatBitmap: CTFontFormat = 5; + +pub const kCTFontClassMaskShift: u32 = 28; + +pub type CTFontSymbolicTraits = u32; +pub const kCTFontItalicTrait: CTFontSymbolicTraits = 1 << 0; +pub const kCTFontBoldTrait: CTFontSymbolicTraits = 1 << 1; +pub const kCTFontExpandedTrait: CTFontSymbolicTraits = 1 << 5; +pub const kCTFontCondensedTrait: CTFontSymbolicTraits = 1 << 6; +pub const kCTFontMonoSpaceTrait: CTFontSymbolicTraits = 1 << 10; +pub const kCTFontVerticalTrait: CTFontSymbolicTraits = 1 << 11; +pub const kCTFontUIOptimizedTrait: CTFontSymbolicTraits = 1 << 12; +pub const kCTFontColorGlyphsTrait: CTFontSymbolicTraits = 1 << 13; +pub const kCTFontClassMaskTrait: CTFontSymbolicTraits = 15 << kCTFontClassMaskShift; + +pub trait SymbolicTraitAccessors { + fn is_italic(&self) -> bool; + fn is_bold(&self) -> bool; + fn is_expanded(&self) -> bool; + fn is_condensed(&self) -> bool; + fn is_monospace(&self) -> bool; + fn is_vertical(&self) -> bool; +} + +impl SymbolicTraitAccessors for CTFontSymbolicTraits { + fn is_italic(&self) -> bool { + (*self & kCTFontItalicTrait) != 0 + } + fn is_bold(&self) -> bool { + (*self & kCTFontBoldTrait) != 0 + } + fn is_expanded(&self) -> bool { + (*self & kCTFontExpandedTrait) != 0 + } + fn is_condensed(&self) -> bool { + (*self & kCTFontCondensedTrait) != 0 + } + fn is_monospace(&self) -> bool { + (*self & kCTFontMonoSpaceTrait) != 0 + } + fn is_vertical(&self) -> bool { + (*self & kCTFontVerticalTrait) != 0 + } +} + +pub type CTFontStylisticClass = u32; +pub const kCTFontUnknownClass: CTFontStylisticClass = 0 << kCTFontClassMaskShift; +pub const kCTFontOldStyleSerifsClass: CTFontStylisticClass = 1 << kCTFontClassMaskShift; +pub const kCTFontTransitionalSerifsClass: CTFontStylisticClass = 2 << kCTFontClassMaskShift; +pub const kCTFontModernSerifsClass: CTFontStylisticClass = 3 << kCTFontClassMaskShift; +pub const kCTFontClarendonSerifsClass: CTFontStylisticClass = 4 << kCTFontClassMaskShift; +pub const kCTFontSlabSerifsClass: CTFontStylisticClass = 5 << kCTFontClassMaskShift; +pub const kCTFontFreeformSerifsClass: CTFontStylisticClass = 7 << kCTFontClassMaskShift; +pub const kCTFontSansSerifClass: CTFontStylisticClass = 8 << kCTFontClassMaskShift; +pub const kCTFontOrnamentalsClass: CTFontStylisticClass = 9 << kCTFontClassMaskShift; +pub const kCTFontScriptsClass: CTFontStylisticClass = 10 << kCTFontClassMaskShift; +pub const kCTFontSymbolicClass: CTFontStylisticClass = 12 << kCTFontClassMaskShift; + +pub trait StylisticClassAccessors { + fn is_serif(&self) -> bool; + fn is_sans_serif(&self) -> bool; + fn is_script(&self) -> bool; + fn is_fantasy(&self) -> bool; + fn is_symbols(&self) -> bool; +} + +impl StylisticClassAccessors for CTFontStylisticClass { + fn is_serif(&self) -> bool { + let any_serif_class = kCTFontOldStyleSerifsClass + | kCTFontTransitionalSerifsClass + | kCTFontModernSerifsClass + | kCTFontClarendonSerifsClass + | kCTFontSlabSerifsClass + | kCTFontFreeformSerifsClass; + + (*self & any_serif_class) != 0 + } + + fn is_sans_serif(&self) -> bool { + (*self & kCTFontSansSerifClass) != 0 + } + + fn is_script(&self) -> bool { + (*self & kCTFontScriptsClass) != 0 + } + + fn is_fantasy(&self) -> bool { + (*self & kCTFontOrnamentalsClass) != 0 + } + + fn is_symbols(&self) -> bool { + (*self & kCTFontSymbolicClass) != 0 + } +} + +pub type CTFontAttributes = CFDictionary; + +pub type CTFontTraits = CFDictionary; + +pub trait TraitAccessors { + fn symbolic_traits(&self) -> CTFontSymbolicTraits; + fn normalized_weight(&self) -> f64; + fn normalized_width(&self) -> f64; + fn normalized_slant(&self) -> f64; +} + +trait TraitAccessorPrivate { + fn extract_number_for_key(&self, key: CFStringRef) -> CFNumber; +} + +impl TraitAccessorPrivate for CTFontTraits { + fn extract_number_for_key(&self, key: CFStringRef) -> CFNumber { + let cftype = self.get(key); + cftype.downcast::().unwrap() + } +} + +impl TraitAccessors for CTFontTraits { + fn symbolic_traits(&self) -> CTFontSymbolicTraits { + unsafe { + let number = self.extract_number_for_key(kCTFontSymbolicTrait); + number.to_i64().unwrap() as u32 + } + } + + fn normalized_weight(&self) -> f64 { + unsafe { + let number = self.extract_number_for_key(kCTFontWeightTrait); + number.to_f64().unwrap() + } + } + + fn normalized_width(&self) -> f64 { + unsafe { + let number = self.extract_number_for_key(kCTFontWidthTrait); + number.to_f64().unwrap() + } + } + + fn normalized_slant(&self) -> f64 { + unsafe { + let number = self.extract_number_for_key(kCTFontSlantTrait); + number.to_f64().unwrap() + } + } +} + +/* +* CTFontDescriptor.h +*/ +pub type CTFontOrientation = u32; +pub const kCTFontDefaultOrientation: CTFontOrientation = 0; // deprecated since macOS 10.11, iOS 9 +pub const kCTFontOrientationDefault: CTFontOrientation = 0; // introduced in macOS 10.8, iOS 6 +pub const kCTFontHorizontalOrientation: CTFontOrientation = 1; // deprecated since macOS 10.11, iOS 9 +pub const kCTFontOrientationHorizontal: CTFontOrientation = 1; // introduced in macOS 10.8, iOS 6 +pub const kCTFontVerticalOrientation: CTFontOrientation = 2; // deprecated since macOS 10.11, iOS 9 +pub const kCTFontOrientationVertical: CTFontOrientation = 2; // introduced in macOS 10.8, iOS 6 + +pub type CTFontPriority = u32; +pub const kCTFontPrioritySystem: CTFontPriority = 10000; +pub const kCTFontPriorityNetwork: CTFontPriority = 20000; +pub const kCTFontPriorityComputer: CTFontPriority = 30000; +pub const kCTFontPriorityUser: CTFontPriority = 40000; +pub const kCTFontPriorityDynamic: CTFontPriority = 50000; +pub const kCTFontPriorityProcess: CTFontPriority = 60000; + +#[repr(C)] +pub struct __CTFontDescriptor(c_void); + +pub type CTFontDescriptorRef = *const __CTFontDescriptor; + +declare_TCFType! { + CTFontDescriptor, CTFontDescriptorRef +} +impl_TCFType!( + CTFontDescriptor, + CTFontDescriptorRef, + CTFontDescriptorGetTypeID +); +impl_CFTypeDescription!(CTFontDescriptor); + +// "Font objects (CTFont, CTFontDescriptor, and associated objects) can be used +// simultaneously by multiple operations, work queues, or threads." +unsafe impl Send for CTFontDescriptor {} +unsafe impl Sync for CTFontDescriptor {} + +impl CTFontDescriptor { + fn get_string_attribute(&self, attribute: CFStringRef) -> Option { + unsafe { + let value = CTFontDescriptorCopyAttribute(self.0, attribute); + if value.is_null() { + return None; + } + + let value = CFType::wrap_under_create_rule(value); + assert!(value.instance_of::()); + let s = CFString::wrap_under_get_rule(value.as_CFTypeRef() as CFStringRef); + Some(s.to_string()) + } + } +} + +impl CTFontDescriptor { + pub fn family_name(&self) -> String { + unsafe { + let value = self.get_string_attribute(kCTFontFamilyNameAttribute); + value.expect("A font must have a non-null family name.") + } + } + + pub fn font_name(&self) -> String { + unsafe { + let value = self.get_string_attribute(kCTFontNameAttribute); + value.expect("A font must have a non-null name.") + } + } + + pub fn style_name(&self) -> String { + unsafe { + let value = self.get_string_attribute(kCTFontStyleNameAttribute); + value.expect("A font must have a non-null style name.") + } + } + + pub fn display_name(&self) -> String { + unsafe { + let value = self.get_string_attribute(kCTFontDisplayNameAttribute); + value.expect("A font must have a non-null display name.") + } + } + + pub fn font_format(&self) -> Option { + unsafe { + let value = CTFontDescriptorCopyAttribute(self.0, kCTFontFormatAttribute); + if value.is_null() { + return None; + } + + let value = CFType::wrap_under_create_rule(value); + assert!(value.instance_of::()); + let format = CFNumber::wrap_under_get_rule(value.as_CFTypeRef() as CFNumberRef); + format.to_i32().map(|x| x as CTFontFormat) + } + } + + pub fn font_path(&self) -> Option { + unsafe { + let value = CTFontDescriptorCopyAttribute(self.0, kCTFontURLAttribute); + if value.is_null() { + return None; + } + + let value = CFType::wrap_under_create_rule(value); + assert!(value.instance_of::()); + let url = CFURL::wrap_under_get_rule(value.as_CFTypeRef() as CFURLRef); + url.to_path() + } + } + + pub fn traits(&self) -> CTFontTraits { + unsafe { + let value = CTFontDescriptorCopyAttribute(self.0, kCTFontTraitsAttribute); + assert!(!value.is_null()); + let value = CFType::wrap_under_create_rule(value); + assert!(value.instance_of::()); + CFDictionary::wrap_under_get_rule(value.as_CFTypeRef() as CFDictionaryRef) + } + } + + pub fn create_copy_with_attributes(&self, attr: CFDictionary) -> Result { + unsafe { + let desc = CTFontDescriptorCreateCopyWithAttributes( + self.as_concrete_TypeRef(), + attr.as_concrete_TypeRef(), + ); + if desc.is_null() { + return Err(()); + } + Ok(CTFontDescriptor::wrap_under_create_rule(desc)) + } + } + + pub fn attributes(&self) -> CFDictionary { + unsafe { + let attrs = CTFontDescriptorCopyAttributes(self.as_concrete_TypeRef()); + CFDictionary::wrap_under_create_rule(attrs) + } + } +} + +pub fn new_from_attributes(attributes: &CFDictionary) -> CTFontDescriptor { + unsafe { + let result: CTFontDescriptorRef = + CTFontDescriptorCreateWithAttributes(attributes.as_concrete_TypeRef()); + CTFontDescriptor::wrap_under_create_rule(result) + } +} + +pub fn new_from_variations(variations: &CFDictionary) -> CTFontDescriptor { + unsafe { + let var_key = CFString::wrap_under_get_rule(kCTFontVariationAttribute); + let var_val = CFType::wrap_under_get_rule(variations.as_CFTypeRef()); + let attributes = CFDictionary::from_CFType_pairs(&[(var_key, var_val)]); + new_from_attributes(&attributes) + } +} + +pub fn new_from_postscript_name(name: &CFString) -> CTFontDescriptor { + unsafe { + let result: CTFontDescriptorRef = + CTFontDescriptorCreateWithNameAndSize(name.as_concrete_TypeRef(), 0.0); + CTFontDescriptor::wrap_under_create_rule(result) + } +} + +pub fn debug_descriptor(desc: &CTFontDescriptor) { + println!("family: {}", desc.family_name()); + println!("name: {}", desc.font_name()); + println!("style: {}", desc.style_name()); + println!("display: {}", desc.display_name()); + println!("path: {:?}", desc.font_path()); + desc.show(); +} + +extern "C" { + /* + * CTFontTraits.h + */ + + // font trait constants + pub static kCTFontSymbolicTrait: CFStringRef; + pub static kCTFontWeightTrait: CFStringRef; + pub static kCTFontWidthTrait: CFStringRef; + pub static kCTFontSlantTrait: CFStringRef; + + /* + * CTFontDescriptor.h + */ + + // font attribute constants. Note that the name-related attributes + // here are somewhat flaky. Servo creates CTFont instances and + // then uses CTFontCopyName to get more fine-grained names. + pub static kCTFontURLAttribute: CFStringRef; // value: CFURLRef + pub static kCTFontNameAttribute: CFStringRef; // value: CFStringRef + pub static kCTFontDisplayNameAttribute: CFStringRef; // value: CFStringRef + pub static kCTFontFamilyNameAttribute: CFStringRef; // value: CFStringRef + pub static kCTFontStyleNameAttribute: CFStringRef; // value: CFStringRef + pub static kCTFontTraitsAttribute: CFStringRef; + pub static kCTFontVariationAttribute: CFStringRef; + pub static kCTFontSizeAttribute: CFStringRef; + pub static kCTFontMatrixAttribute: CFStringRef; + pub static kCTFontCascadeListAttribute: CFStringRef; + pub static kCTFontCharacterSetAttribute: CFStringRef; + pub static kCTFontLanguagesAttribute: CFStringRef; + pub static kCTFontBaselineAdjustAttribute: CFStringRef; + pub static kCTFontMacintoshEncodingsAttribute: CFStringRef; + pub static kCTFontFeaturesAttribute: CFStringRef; + pub static kCTFontFeatureSettingsAttribute: CFStringRef; + pub static kCTFontFixedAdvanceAttribute: CFStringRef; + pub static kCTFontOrientationAttribute: CFStringRef; + pub static kCTFontFormatAttribute: CFStringRef; + pub static kCTFontRegistrationScopeAttribute: CFStringRef; + pub static kCTFontPriorityAttribute: CFStringRef; + pub static kCTFontEnabledAttribute: CFStringRef; + + pub fn CTFontDescriptorCopyAttribute( + descriptor: CTFontDescriptorRef, + attribute: CFStringRef, + ) -> CFTypeRef; + pub fn CTFontDescriptorCopyAttributes(descriptor: CTFontDescriptorRef) -> CFDictionaryRef; + pub fn CTFontDescriptorCopyLocalizedAttribute( + descriptor: CTFontDescriptorRef, + attribute: CFStringRef, + language: *mut CFStringRef, + ) -> CFTypeRef; + pub fn CTFontDescriptorCreateCopyWithAttributes( + original: CTFontDescriptorRef, + attributes: CFDictionaryRef, + ) -> CTFontDescriptorRef; + pub fn CTFontDescriptorCreateCopyWithFeature( + original: CTFontDescriptorRef, + featureTypeIdentifier: CFNumberRef, + featureSelectorIdentifier: CFNumberRef, + ) -> CTFontDescriptorRef; + pub fn CTFontDescriptorCreateCopyWithVariation( + original: CTFontDescriptorRef, + variationIdentifier: CFNumberRef, + variationValue: CGFloat, + ) -> CTFontDescriptorRef; + pub fn CTFontDescriptorCreateMatchingFontDescriptor( + descriptor: CTFontDescriptorRef, + mandatoryAttributes: CFSetRef, + ) -> CTFontDescriptorRef; + pub fn CTFontDescriptorCreateWithAttributes(attributes: CFDictionaryRef) + -> CTFontDescriptorRef; + pub fn CTFontDescriptorCreateWithNameAndSize( + name: CFStringRef, + size: CGFloat, + ) -> CTFontDescriptorRef; + pub fn CTFontDescriptorGetTypeID() -> CFTypeID; +} + +extern "C" { + pub fn CTFontDescriptorCreateMatchingFontDescriptors( + descriptor: CTFontDescriptorRef, + mandatoryAttributes: CFSetRef, + ) -> CFArrayRef; +} diff --git a/third_party/rust/core-text/src/font_manager.rs b/third_party/rust/core-text/src/font_manager.rs new file mode 100644 index 0000000000..dff0f8d0fb --- /dev/null +++ b/third_party/rust/core-text/src/font_manager.rs @@ -0,0 +1,76 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::font_descriptor::{CTFontDescriptor, CTFontDescriptorRef}; +use core_foundation::array::{CFArray, CFArrayRef}; +use core_foundation::base::TCFType; +use core_foundation::data::{CFData, CFDataRef}; +use core_foundation::string::CFString; +use core_foundation::url::CFURLRef; + +pub fn copy_available_font_family_names() -> CFArray { + unsafe { TCFType::wrap_under_create_rule(CTFontManagerCopyAvailableFontFamilyNames()) } +} + +pub fn create_font_descriptor(buffer: &[u8]) -> Result { + let cf_data = CFData::from_buffer(buffer); + unsafe { + let ct_font_descriptor_ref = + CTFontManagerCreateFontDescriptorFromData(cf_data.as_concrete_TypeRef()); + if ct_font_descriptor_ref.is_null() { + return Err(()); + } + Ok(CTFontDescriptor::wrap_under_create_rule( + ct_font_descriptor_ref, + )) + } +} + +pub fn create_font_descriptor_with_data(data: CFData) -> Result { + unsafe { + let ct_font_descriptor_ref = + CTFontManagerCreateFontDescriptorFromData(data.as_concrete_TypeRef()); + if ct_font_descriptor_ref.is_null() { + return Err(()); + } + Ok(CTFontDescriptor::wrap_under_create_rule( + ct_font_descriptor_ref, + )) + } +} + +extern "C" { + /* + * CTFontManager.h + */ + + // Incomplete function bindings are mostly related to CoreText font matching, which + // we implement in a platform-independent manner using FontMatcher. + + //pub fn CTFontManagerCompareFontFamilyNames + pub fn CTFontManagerCopyAvailableFontURLs() -> CFArrayRef; + pub fn CTFontManagerCopyAvailableFontFamilyNames() -> CFArrayRef; + pub fn CTFontManagerCopyAvailablePostScriptNames() -> CFArrayRef; + pub fn CTFontManagerCreateFontDescriptorsFromURL(fileURL: CFURLRef) -> CFArrayRef; + pub fn CTFontManagerCreateFontDescriptorFromData(data: CFDataRef) -> CTFontDescriptorRef; + //pub fn CTFontManagerCreateFontRequestRunLoopSource + //pub fn CTFontManagerEnableFontDescriptors + //pub fn CTFontManagerGetAutoActivationSetting + //pub fn CTFontManagerGetScopeForURL + //pub fn CTFontManagerGetAutoActivationSetting + //pub fn CTFontManagerGetScopeForURL + pub fn CTFontManagerIsSupportedFont(fontURL: CFURLRef) -> bool; + //pub fn CTFontManagerRegisterFontsForURL + //pub fn CTFontManagerRegisterFontsForURLs + //pub fn CTFontManagerRegisterGraphicsFont + //pub fn CTFontManagerSetAutoActivationSetting + //pub fn CTFontManagerUnregisterFontsForURL + //pub fn CTFontManagerUnregisterFontsForURLs + //pub fn CTFontManagerUnregisterGraphicsFont +} diff --git a/third_party/rust/core-text/src/frame.rs b/third_party/rust/core-text/src/frame.rs new file mode 100644 index 0000000000..03fcee902a --- /dev/null +++ b/third_party/rust/core-text/src/frame.rs @@ -0,0 +1,94 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::line::CTLine; +use core_foundation::array::{CFArray, CFArrayRef}; +use core_foundation::base::{CFRange, CFTypeID, TCFType}; +use core_graphics::context::{CGContext, CGContextRef}; +use core_graphics::geometry::CGPoint; +use core_graphics::path::{CGPath, SysCGPathRef}; +use foreign_types::{ForeignType, ForeignTypeRef}; +use std::os::raw::c_void; + +#[repr(C)] +pub struct __CTFrame(c_void); + +pub type CTFrameRef = *const __CTFrame; + +declare_TCFType! { + CTFrame, CTFrameRef +} +impl_TCFType!(CTFrame, CTFrameRef, CTFrameGetTypeID); +impl_CFTypeDescription!(CTFrame); + +impl CTFrame { + /// The `CGPath` used to create this `CTFrame`. + pub fn get_path(&self) -> CGPath { + unsafe { CGPath::from_ptr(CTFrameGetPath(self.as_concrete_TypeRef())).clone() } + } + + /// Returns an owned copy of the underlying lines. + /// + /// Each line is retained, and will remain valid past the life of this `CTFrame`. + pub fn get_lines(&self) -> Vec { + unsafe { + let array_ref = CTFrameGetLines(self.as_concrete_TypeRef()); + let array: CFArray = CFArray::wrap_under_get_rule(array_ref); + array + .iter() + .map(|l| CTLine::wrap_under_get_rule(l.as_concrete_TypeRef())) + .collect() + } + } + + /// Return the origin of each line in a given range. + /// + /// If no range is provided, returns the origin of each line in the frame. + /// + /// If the length of the range is 0, returns the origin of all lines from + /// the range's start to the end. + /// + /// The origin is the position relative to the path used to create this `CTFFrame`; + /// to get the path use [`get_path`]. + /// + /// [`get_path`]: #method.get_path + pub fn get_line_origins(&self, range: impl Into>) -> Vec { + let range = range.into().unwrap_or_else(|| CFRange::init(0, 0)); + let len = match range.length { + // range length of 0 means 'all remaining lines' + 0 => unsafe { + let array_ref = CTFrameGetLines(self.as_concrete_TypeRef()); + let array: CFArray = CFArray::wrap_under_get_rule(array_ref); + array.len() - range.location + }, + n => n, + }; + let len = len.max(0) as usize; + let mut out = vec![CGPoint::new(0., 0.); len]; + unsafe { + CTFrameGetLineOrigins(self.as_concrete_TypeRef(), range, out.as_mut_ptr()); + } + out + } + + pub fn draw(&self, context: &CGContextRef) { + unsafe { + CTFrameDraw(self.as_concrete_TypeRef(), context.as_ptr()); + } + } +} + +#[link(name = "CoreText", kind = "framework")] +extern "C" { + fn CTFrameGetTypeID() -> CFTypeID; + fn CTFrameGetLines(frame: CTFrameRef) -> CFArrayRef; + fn CTFrameDraw(frame: CTFrameRef, context: *mut ::CType); + fn CTFrameGetLineOrigins(frame: CTFrameRef, range: CFRange, origins: *mut CGPoint); + fn CTFrameGetPath(frame: CTFrameRef) -> SysCGPathRef; +} diff --git a/third_party/rust/core-text/src/framesetter.rs b/third_party/rust/core-text/src/framesetter.rs new file mode 100644 index 0000000000..dacb540b85 --- /dev/null +++ b/third_party/rust/core-text/src/framesetter.rs @@ -0,0 +1,94 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::frame::{CTFrame, CTFrameRef}; +use core_foundation::attributed_string::CFAttributedStringRef; +use core_foundation::base::{CFRange, CFTypeID, TCFType}; +use core_foundation::dictionary::CFDictionaryRef; +use core_graphics::geometry::CGSize; +use core_graphics::path::{CGPath, CGPathRef}; +use foreign_types::{ForeignType, ForeignTypeRef}; +use std::os::raw::c_void; +use std::ptr::null; + +#[repr(C)] +pub struct __CTFramesetter(c_void); + +pub type CTFramesetterRef = *const __CTFramesetter; + +declare_TCFType! { + CTFramesetter, CTFramesetterRef +} +impl_TCFType!(CTFramesetter, CTFramesetterRef, CTFramesetterGetTypeID); +impl_CFTypeDescription!(CTFramesetter); + +impl CTFramesetter { + pub fn new_with_attributed_string(string: CFAttributedStringRef) -> Self { + unsafe { + let ptr = CTFramesetterCreateWithAttributedString(string); + CTFramesetter::wrap_under_create_rule(ptr) + } + } + + pub fn create_frame(&self, string_range: CFRange, path: &CGPathRef) -> CTFrame { + unsafe { + let ptr = CTFramesetterCreateFrame( + self.as_concrete_TypeRef(), + string_range, + path.as_ptr(), + null(), + ); + + CTFrame::wrap_under_create_rule(ptr) + } + } + + /// Suggest an appropriate frame size for displaying a text range. + /// + /// Returns a tuple containing an appropriate size (that should be smaller + /// than the provided constraints) as well as the range of text that fits in + /// this frame. + pub fn suggest_frame_size_with_constraints( + &self, + string_range: CFRange, + frame_attributes: CFDictionaryRef, + constraints: CGSize, + ) -> (CGSize, CFRange) { + unsafe { + let mut fit_range = CFRange::init(0, 0); + let size = CTFramesetterSuggestFrameSizeWithConstraints( + self.as_concrete_TypeRef(), + string_range, + frame_attributes, + constraints, + &mut fit_range, + ); + (size, fit_range) + } + } +} + +#[link(name = "CoreText", kind = "framework")] +extern "C" { + fn CTFramesetterGetTypeID() -> CFTypeID; + fn CTFramesetterCreateWithAttributedString(string: CFAttributedStringRef) -> CTFramesetterRef; + fn CTFramesetterCreateFrame( + framesetter: CTFramesetterRef, + string_range: CFRange, + path: *mut ::CType, + attributes: *const c_void, + ) -> CTFrameRef; + fn CTFramesetterSuggestFrameSizeWithConstraints( + framesetter: CTFramesetterRef, + string_range: CFRange, + frame_attributes: CFDictionaryRef, + constraints: CGSize, + fitRange: *mut CFRange, + ) -> CGSize; +} diff --git a/third_party/rust/core-text/src/lib.rs b/third_party/rust/core-text/src/lib.rs new file mode 100644 index 0000000000..be394b020d --- /dev/null +++ b/third_party/rust/core-text/src/lib.rs @@ -0,0 +1,34 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_name = "core_text"] +#![crate_type = "rlib"] +#![allow(non_snake_case)] + +/*! +Many of these functions will add objects to the autorelease pool. +If you don't have one this will cause leaks. +*/ + +extern crate foreign_types; +extern crate libc; + +#[macro_use] +extern crate core_foundation; +extern crate core_graphics; + +pub mod font; +pub mod font_collection; +pub mod font_descriptor; +pub mod font_manager; +pub mod frame; +pub mod framesetter; +pub mod line; +pub mod run; +pub mod string_attributes; diff --git a/third_party/rust/core-text/src/line.rs b/third_party/rust/core-text/src/line.rs new file mode 100644 index 0000000000..b2645aaf5c --- /dev/null +++ b/third_party/rust/core-text/src/line.rs @@ -0,0 +1,125 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core_foundation::array::{CFArray, CFArrayRef}; +use core_foundation::attributed_string::CFAttributedStringRef; +use core_foundation::base::{CFIndex, CFRange, CFTypeID, TCFType}; +use core_graphics::base::CGFloat; +use core_graphics::context::CGContext; +use core_graphics::geometry::{CGPoint, CGRect}; +use foreign_types::ForeignType; +use run::CTRun; +use std::os::raw::c_void; + +#[repr(C)] +pub struct __CTLine(c_void); + +pub type CTLineRef = *const __CTLine; + +declare_TCFType! { + CTLine, CTLineRef +} +impl_TCFType!(CTLine, CTLineRef, CTLineGetTypeID); +impl_CFTypeDescription!(CTLine); + +/// Metrics for a given line. +pub struct TypographicBounds { + pub width: CGFloat, + pub ascent: CGFloat, + pub descent: CGFloat, + pub leading: CGFloat, +} + +impl CTLine { + pub fn new_with_attributed_string(string: CFAttributedStringRef) -> Self { + unsafe { + let ptr = CTLineCreateWithAttributedString(string); + CTLine::wrap_under_create_rule(ptr) + } + } + + pub fn glyph_runs(&self) -> CFArray { + unsafe { TCFType::wrap_under_get_rule(CTLineGetGlyphRuns(self.0)) } + } + + pub fn get_string_range(&self) -> CFRange { + unsafe { CTLineGetStringRange(self.as_concrete_TypeRef()) } + } + + pub fn draw(&self, context: &CGContext) { + unsafe { CTLineDraw(self.as_concrete_TypeRef(), context.as_ptr()) } + } + + pub fn get_image_bounds(&self, context: &CGContext) -> CGRect { + unsafe { CTLineGetImageBounds(self.as_concrete_TypeRef(), context.as_ptr()) } + } + + pub fn get_typographic_bounds(&self) -> TypographicBounds { + let mut ascent = 0.0; + let mut descent = 0.0; + let mut leading = 0.0; + unsafe { + let width = CTLineGetTypographicBounds( + self.as_concrete_TypeRef(), + &mut ascent, + &mut descent, + &mut leading, + ); + TypographicBounds { + width, + ascent, + descent, + leading, + } + } + } + + pub fn get_string_index_for_position(&self, position: CGPoint) -> CFIndex { + unsafe { CTLineGetStringIndexForPosition(self.as_concrete_TypeRef(), position) } + } + + pub fn get_string_offset_for_string_index(&self, charIndex: CFIndex) -> CGFloat { + unsafe { + CTLineGetOffsetForStringIndex(self.as_concrete_TypeRef(), charIndex, std::ptr::null()) + } + } +} + +#[link(name = "CoreText", kind = "framework")] +extern "C" { + fn CTLineGetTypeID() -> CFTypeID; + fn CTLineGetGlyphRuns(line: CTLineRef) -> CFArrayRef; + fn CTLineGetStringRange(line: CTLineRef) -> CFRange; + + // Creating Lines + fn CTLineCreateWithAttributedString(string: CFAttributedStringRef) -> CTLineRef; + + // Drawing the Line + fn CTLineDraw(line: CTLineRef, context: *const core_graphics::sys::CGContext); + + // Measuring Lines + fn CTLineGetImageBounds( + line: CTLineRef, + context: *const core_graphics::sys::CGContext, + ) -> CGRect; + fn CTLineGetTypographicBounds( + line: CTLineRef, + ascent: *mut CGFloat, + descent: *mut CGFloat, + leading: *mut CGFloat, + ) -> CGFloat; + + // Getting Line Positioning + fn CTLineGetStringIndexForPosition(line: CTLineRef, position: CGPoint) -> CFIndex; + fn CTLineGetOffsetForStringIndex( + line: CTLineRef, + charIndex: CFIndex, + secondaryOffset: *const CGFloat, + ) -> CGFloat; +} diff --git a/third_party/rust/core-text/src/run.rs b/third_party/rust/core-text/src/run.rs new file mode 100644 index 0000000000..f84fb46cb7 --- /dev/null +++ b/third_party/rust/core-text/src/run.rs @@ -0,0 +1,159 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core_foundation::base::{CFIndex, CFRange, CFType, CFTypeID, TCFType}; +use core_foundation::dictionary::{CFDictionary, CFDictionaryRef}; +use core_foundation::string::CFString; +use core_graphics::font::CGGlyph; +use core_graphics::geometry::CGPoint; +use std::borrow::Cow; +use std::os::raw::c_void; +use std::slice; + +#[repr(C)] +pub struct __CTRun(c_void); + +pub type CTRunRef = *const __CTRun; + +declare_TCFType! { + CTRun, CTRunRef +} +impl_TCFType!(CTRun, CTRunRef, CTRunGetTypeID); +impl_CFTypeDescription!(CTRun); + +impl CTRun { + pub fn attributes(&self) -> Option> { + unsafe { + let attrs = CTRunGetAttributes(self.0); + if attrs.is_null() { + return None; + } + Some(TCFType::wrap_under_get_rule(attrs)) + } + } + pub fn glyph_count(&self) -> CFIndex { + unsafe { CTRunGetGlyphCount(self.0) } + } + + pub fn glyphs(&self) -> Cow<[CGGlyph]> { + unsafe { + // CTRunGetGlyphsPtr can return null under some not understood circumstances. + // If it does the Apple documentation tells us to allocate our own buffer and call + // CTRunGetGlyphs + let count = CTRunGetGlyphCount(self.0); + let glyphs_ptr = CTRunGetGlyphsPtr(self.0); + if !glyphs_ptr.is_null() { + Cow::from(slice::from_raw_parts(glyphs_ptr, count as usize)) + } else { + let mut vec = Vec::with_capacity(count as usize); + // "If the length of the range is set to 0, then the copy operation will continue + // from the start index of the range to the end of the run" + CTRunGetGlyphs(self.0, CFRange::init(0, 0), vec.as_mut_ptr()); + vec.set_len(count as usize); + Cow::from(vec) + } + } + } + + pub fn positions(&self) -> Cow<[CGPoint]> { + unsafe { + // CTRunGetPositionsPtr can return null under some not understood circumstances. + // If it does the Apple documentation tells us to allocate our own buffer and call + // CTRunGetPositions + let count = CTRunGetGlyphCount(self.0); + let positions_ptr = CTRunGetPositionsPtr(self.0); + if !positions_ptr.is_null() { + Cow::from(slice::from_raw_parts(positions_ptr, count as usize)) + } else { + let mut vec = Vec::with_capacity(count as usize); + // "If the length of the range is set to 0, then the copy operation will continue + // from the start index of the range to the end of the run" + CTRunGetPositions(self.0, CFRange::init(0, 0), vec.as_mut_ptr()); + vec.set_len(count as usize); + Cow::from(vec) + } + } + } + + pub fn string_indices(&self) -> Cow<[CFIndex]> { + unsafe { + // CTRunGetStringIndicesPtr can return null under some not understood circumstances. + // If it does the Apple documentation tells us to allocate our own buffer and call + // CTRunGetStringIndices + let count = CTRunGetGlyphCount(self.0); + let indices_ptr = CTRunGetStringIndicesPtr(self.0); + if !indices_ptr.is_null() { + Cow::from(slice::from_raw_parts(indices_ptr, count as usize)) + } else { + let mut vec = Vec::with_capacity(count as usize); + // "If the length of the range is set to 0, then the copy operation will continue + // from the start index of the range to the end of the run" + CTRunGetStringIndices(self.0, CFRange::init(0, 0), vec.as_mut_ptr()); + vec.set_len(count as usize); + Cow::from(vec) + } + } + } +} + +#[test] +fn create_runs() { + use core_foundation::attributed_string::CFMutableAttributedString; + use font; + use line::*; + use string_attributes::*; + let mut string = CFMutableAttributedString::new(); + string.replace_str(&CFString::new("Food"), CFRange::init(0, 0)); + let len = string.char_len(); + unsafe { + string.set_attribute( + CFRange::init(0, len), + kCTFontAttributeName, + &font::new_from_name("Helvetica", 16.).unwrap(), + ); + } + let line = CTLine::new_with_attributed_string(string.as_concrete_TypeRef()); + let runs = line.glyph_runs(); + assert_eq!(runs.len(), 1); + for run in runs.iter() { + assert_eq!(run.glyph_count(), 4); + let font = run + .attributes() + .unwrap() + .get(CFString::new("NSFont")) + .downcast::() + .unwrap(); + assert_eq!(font.pt_size(), 16.); + + let positions = run.positions(); + assert_eq!(positions.len(), 4); + assert!(positions[0].x < positions[1].x); + + let glyphs = run.glyphs(); + assert_eq!(glyphs.len(), 4); + assert_ne!(glyphs[0], glyphs[1]); + assert_eq!(glyphs[1], glyphs[2]); + + let indices = run.string_indices(); + assert_eq!(indices.as_ref(), &[0, 1, 2, 3]); + } +} + +#[link(name = "CoreText", kind = "framework")] +extern "C" { + fn CTRunGetTypeID() -> CFTypeID; + fn CTRunGetAttributes(run: CTRunRef) -> CFDictionaryRef; + fn CTRunGetGlyphCount(run: CTRunRef) -> CFIndex; + fn CTRunGetPositionsPtr(run: CTRunRef) -> *const CGPoint; + fn CTRunGetPositions(run: CTRunRef, range: CFRange, buffer: *const CGPoint); + fn CTRunGetStringIndicesPtr(run: CTRunRef) -> *const CFIndex; + fn CTRunGetStringIndices(run: CTRunRef, range: CFRange, buffer: *const CFIndex); + fn CTRunGetGlyphsPtr(run: CTRunRef) -> *const CGGlyph; + fn CTRunGetGlyphs(run: CTRunRef, range: CFRange, buffer: *const CGGlyph); +} diff --git a/third_party/rust/core-text/src/string_attributes.rs b/third_party/rust/core-text/src/string_attributes.rs new file mode 100644 index 0000000000..4f38ced4b9 --- /dev/null +++ b/third_party/rust/core-text/src/string_attributes.rs @@ -0,0 +1,19 @@ +use core_foundation::string::CFStringRef; + +extern "C" { + pub static kCTCharacterShapeAttributeName: CFStringRef; + pub static kCTFontAttributeName: CFStringRef; + pub static kCTKernAttributeName: CFStringRef; + pub static kCTLigatureAttributeName: CFStringRef; + pub static kCTForegroundColorAttributeName: CFStringRef; + pub static kCTForegroundColorFromContextAttributeName: CFStringRef; + pub static kCTParagraphStyleAttributeName: CFStringRef; + pub static kCTStrokeWidthAttributeName: CFStringRef; + pub static kCTStrokeColorAttributeName: CFStringRef; + pub static kCTSuperscriptAttributeName: CFStringRef; + pub static kCTUnderlineColorAttributeName: CFStringRef; + pub static kCTUnderlineStyleAttributeName: CFStringRef; + pub static kCTVerticalFormsAttributeName: CFStringRef; + pub static kCTGlyphInfoAttributeName: CFStringRef; + pub static kCTRunDelegateAttributeName: CFStringRef; +} -- cgit v1.2.3