From 42889636bff5fb9bc862526735e8f02fa50e39f2 Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Fri, 2 Nov 2018 14:53:00 -0400 Subject: [PATCH] Add some minimal bindings for CTLine and CTRun --- core-text/src/lib.rs | 3 + core-text/src/line.rs | 47 +++++++++++ core-text/src/run.rs | 129 +++++++++++++++++++++++++++++ core-text/src/string_attributes.rs | 19 +++++ 4 files changed, 198 insertions(+) create mode 100644 core-text/src/line.rs create mode 100644 core-text/src/run.rs create mode 100644 core-text/src/string_attributes.rs diff --git a/core-text/src/lib.rs b/core-text/src/lib.rs index fbd0be1..876e84a 100644 --- a/core-text/src/lib.rs +++ b/core-text/src/lib.rs @@ -25,3 +25,6 @@ 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/core-text/src/line.rs b/core-text/src/line.rs new file mode 100644 index 0000000..57b9749 --- /dev/null +++ b/core-text/src/line.rs @@ -0,0 +1,47 @@ +// 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 std::os::raw::c_void; +use core_foundation::attributed_string::CFAttributedStringRef; +use core_foundation::array::{CFArray, CFArrayRef}; +use core_foundation::base::{CFTypeID, TCFType}; +use run::CTRun; + +#[repr(C)] +pub struct __CTLine(c_void); + +pub type CTLineRef = *const __CTLine; + +declare_TCFType! { + CTLine, CTLineRef +} +impl_TCFType!(CTLine, CTLineRef, CTLineGetTypeID); +impl_CFTypeDescription!(CTLine); + +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)) + } + } +} + +#[link(name = "CoreText", kind = "framework")] +extern { + fn CTLineGetTypeID() -> CFTypeID; + fn CTLineGetGlyphRuns(line: CTLineRef) -> CFArrayRef; + fn CTLineCreateWithAttributedString(string: CFAttributedStringRef) -> CTLineRef; +} diff --git a/core-text/src/run.rs b/core-text/src/run.rs new file mode 100644 index 0000000..f675dd0 --- /dev/null +++ b/core-text/src/run.rs @@ -0,0 +1,129 @@ +// 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 std::borrow::Cow; +use std::os::raw::c_void; +use std::slice; +use std::ptr; +use core_foundation::base::{CFIndex, CFTypeID, TCFType, CFType, CFRange}; +use core_foundation::dictionary::{CFDictionary, CFDictionaryRef}; +use core_foundation::string::CFString; +use core_graphics::font::CGGlyph; +use core_graphics::geometry::CGPoint; + +#[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 != ptr::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 != ptr::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) + } + } + } + +} + +#[test] +fn create_runs() { + use core_foundation::attributed_string::CFMutableAttributedString; + use string_attributes::*; + use line::*; + use font; + 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]); + } +} + +#[link(name = "CoreText", kind = "framework")] +extern { + 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 CTRunGetGlyphsPtr(run: CTRunRef) -> *const CGGlyph; + fn CTRunGetGlyphs(run: CTRunRef, range: CFRange, buffer: *const CGGlyph); +} diff --git a/core-text/src/string_attributes.rs b/core-text/src/string_attributes.rs new file mode 100644 index 0000000..dde1936 --- /dev/null +++ b/core-text/src/string_attributes.rs @@ -0,0 +1,19 @@ +use core_foundation::string::CFStringRef; + +extern { + 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; +}