diff --git a/.gitignore b/.gitignore index be576c475..0b1dc8775 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ Cargo.lock target/ +.idea diff --git a/core-text/src/font.rs b/core-text/src/font.rs index ebe6d580f..64142fac5 100644 --- a/core-text/src/font.rs +++ b/core-text/src/font.rs @@ -395,6 +395,12 @@ impl CTFont { } } + pub fn get_matrix(&self) -> CGAffineTransform { + unsafe { + CTFontGetMatrix(self.as_concrete_TypeRef()) + } + } + pub fn url(&self) -> Option { unsafe { let result = CTFontCopyAttribute(self.0, kCTFontURLAttribute); @@ -556,7 +562,7 @@ extern { fn CTFontCopyFontDescriptor(font: CTFontRef) -> CTFontDescriptorRef; fn CTFontCopyAttribute(font: CTFontRef, attribute: CFStringRef) -> CFTypeRef; fn CTFontGetSize(font: CTFontRef) -> CGFloat; - //fn CTFontGetMatrix + fn CTFontGetMatrix(font: CTFontRef) -> CGAffineTransform; fn CTFontGetSymbolicTraits(font: CTFontRef) -> CTFontSymbolicTraits; fn CTFontCopyTraits(font: CTFontRef) -> CFDictionaryRef; diff --git a/core-text/src/font_descriptor.rs b/core-text/src/font_descriptor.rs index c70495c82..4dd70717a 100644 --- a/core-text/src/font_descriptor.rs +++ b/core-text/src/font_descriptor.rs @@ -20,6 +20,7 @@ use core_graphics::base::CGFloat; use std::os::raw::c_void; use std::path::PathBuf; +use core_foundation::boolean::CFBoolean; /* * CTFontTraits.h @@ -132,7 +133,19 @@ trait TraitAccessorPrivate { impl TraitAccessorPrivate for CTFontTraits { fn extract_number_for_key(&self, key: CFStringRef) -> CFNumber { let cftype = self.get(key); - cftype.downcast::().unwrap() + let number = cftype.downcast::(); + match number { + Some(number) => number, + None => { + // The value was not able to be converted to a CFNumber, this violates the Core + // Foundation's docs (see https://developer.apple.com/documentation/coretext/kctfontsymbolictrait) + // but can occur in practice with certain fonts in MacOS 13 (Ventura). When this + // does occur in Ventura, the value returned is always a CFBoolean, so we attempt to + // convert into a boolean and create a number from there. + let value_as_bool = bool::from(cftype.downcast::().expect("Should be able to convert value into CFBoolean")); + CFNumber::from(value_as_bool as i32) + } + } } } diff --git a/core-text/src/run.rs b/core-text/src/run.rs index b7cb37616..c523a4aee 100644 --- a/core-text/src/run.rs +++ b/core-text/src/run.rs @@ -7,14 +7,17 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::borrow::Cow; -use std::os::raw::c_void; -use std::slice; -use core_foundation::base::{CFIndex, CFTypeID, TCFType, CFType, CFRange}; +use core_foundation::base::{CFIndex, CFRange, CFType, CFTypeID, TCFType}; use core_foundation::dictionary::{CFDictionary, CFDictionaryRef}; use core_foundation::string::CFString; +use core_graphics::base::CGFloat; use core_graphics::font::CGGlyph; use core_graphics::geometry::CGPoint; +use std::borrow::Cow; +use std::os::raw::c_void; +use std::slice; + +use crate::line::TypographicBounds; #[repr(C)] pub struct __CTRun(c_void); @@ -38,9 +41,7 @@ impl CTRun { } } pub fn glyph_count(&self) -> CFIndex { - unsafe { - CTRunGetGlyphCount(self.0) - } + unsafe { CTRunGetGlyphCount(self.0) } } pub fn glyphs(&self) -> Cow<[CGGlyph]> { @@ -83,6 +84,34 @@ impl CTRun { } } + pub fn get_typographic_bounds(&self) -> TypographicBounds { + let mut ascent = 0.0; + let mut descent = 0.0; + let mut leading = 0.0; + unsafe { + // The portion of the run to calculate the typographic bounds for. By setting this to 0, + // CoreText will measure the bounds from start to end, see https://developer.apple.com/documentation/coretext/1510569-ctrungettypographicbounds?language=objc. + let range = CFRange { + location: 0, + length: 0, + }; + + let width = CTRunGetTypographicBounds( + self.as_concrete_TypeRef(), + range, + &mut ascent, + &mut descent, + &mut leading, + ); + TypographicBounds { + width, + ascent, + descent, + leading, + } + } + } + pub fn string_indices(&self) -> Cow<[CFIndex]> { unsafe { // CTRunGetStringIndicesPtr can return null under some not understood circumstances. @@ -107,21 +136,30 @@ impl CTRun { #[test] fn create_runs() { use core_foundation::attributed_string::CFMutableAttributedString; - use string_attributes::*; - use line::*; 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()); + 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(); + let font = run + .attributes() + .unwrap() + .get(CFString::new("NSFont")) + .downcast::() + .unwrap(); assert_eq!(font.pt_size(), 16.); let positions = run.positions(); @@ -139,7 +177,7 @@ fn create_runs() { } #[link(name = "CoreText", kind = "framework")] -extern { +extern "C" { fn CTRunGetTypeID() -> CFTypeID; fn CTRunGetAttributes(run: CTRunRef) -> CFDictionaryRef; fn CTRunGetGlyphCount(run: CTRunRef) -> CFIndex; @@ -149,4 +187,12 @@ extern { fn CTRunGetStringIndices(run: CTRunRef, range: CFRange, buffer: *const CFIndex); fn CTRunGetGlyphsPtr(run: CTRunRef) -> *const CGGlyph; fn CTRunGetGlyphs(run: CTRunRef, range: CFRange, buffer: *const CGGlyph); + + fn CTRunGetTypographicBounds( + line: CTRunRef, + range: CFRange, + ascent: *mut CGFloat, + descent: *mut CGFloat, + leading: *mut CGFloat, + ) -> CGFloat; }