|
| 1 | +// Copyright 2013 The Servo Project Developers. See the COPYRIGHT |
| 2 | +// file at the top-level directory of this distribution. |
| 3 | +// |
| 4 | +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| 5 | +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| 6 | +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| 7 | +// option. This file may not be copied, modified, or distributed |
| 8 | +// except according to those terms. |
| 9 | + |
| 10 | +use std::borrow::Cow; |
| 11 | +use std::os::raw::c_void; |
| 12 | +use std::slice; |
| 13 | +use std::ptr; |
| 14 | +use core_foundation::base::{CFIndex, CFTypeID, TCFType, CFType, CFRange}; |
| 15 | +use core_foundation::dictionary::{CFDictionary, CFDictionaryRef}; |
| 16 | +use core_foundation::string::CFString; |
| 17 | +use core_graphics::font::CGGlyph; |
| 18 | +use core_graphics::geometry::CGPoint; |
| 19 | + |
| 20 | +#[repr(C)] |
| 21 | +pub struct __CTRun(c_void); |
| 22 | + |
| 23 | +pub type CTRunRef = *const __CTRun; |
| 24 | + |
| 25 | +declare_TCFType! { |
| 26 | + CTRun, CTRunRef |
| 27 | +} |
| 28 | +impl_TCFType!(CTRun, CTRunRef, CTRunGetTypeID); |
| 29 | +impl_CFTypeDescription!(CTRun); |
| 30 | + |
| 31 | +impl CTRun { |
| 32 | + pub fn attributes(&self) -> Option<CFDictionary<CFString, CFType>> { |
| 33 | + unsafe { |
| 34 | + let attrs = CTRunGetAttributes(self.0); |
| 35 | + if attrs.is_null() { |
| 36 | + return None; |
| 37 | + } |
| 38 | + Some(TCFType::wrap_under_get_rule(attrs)) |
| 39 | + } |
| 40 | + } |
| 41 | + pub fn glyph_count(&self) -> CFIndex { |
| 42 | + unsafe { |
| 43 | + CTRunGetGlyphCount(self.0) |
| 44 | + } |
| 45 | + } |
| 46 | + |
| 47 | + pub fn glyphs(&self) -> Cow<[CGGlyph]> { |
| 48 | + unsafe { |
| 49 | + // CTRunGetGlyphsPtr can return null under some not understood circumstances. |
| 50 | + // If it does the Apple documentation tells us to allocate our own buffer and call |
| 51 | + // CTRunGetGlyphs |
| 52 | + let count = CTRunGetGlyphCount(self.0); |
| 53 | + let glyphs_ptr = CTRunGetGlyphsPtr(self.0); |
| 54 | + if glyphs_ptr != ptr::null() { |
| 55 | + Cow::from(slice::from_raw_parts(glyphs_ptr, count as usize)) |
| 56 | + } else { |
| 57 | + let mut vec = Vec::with_capacity(count as usize); |
| 58 | + // "If the length of the range is set to 0, then the copy operation will continue |
| 59 | + // from the start index of the range to the end of the run" |
| 60 | + CTRunGetGlyphs(self.0, CFRange::init(0, 0), vec.as_mut_ptr()); |
| 61 | + vec.set_len(count as usize); |
| 62 | + Cow::from(vec) |
| 63 | + } |
| 64 | + } |
| 65 | + } |
| 66 | + |
| 67 | + pub fn positions(&self) -> Cow<[CGPoint]> { |
| 68 | + unsafe { |
| 69 | + // CTRunGetPositionsPtr can return null under some not understood circumstances. |
| 70 | + // If it does the Apple documentation tells us to allocate our own buffer and call |
| 71 | + // CTRunGetPositions |
| 72 | + let count = CTRunGetGlyphCount(self.0); |
| 73 | + let positions_ptr = CTRunGetPositionsPtr(self.0); |
| 74 | + if positions_ptr != ptr::null() { |
| 75 | + Cow::from(slice::from_raw_parts(positions_ptr, count as usize)) |
| 76 | + } else { |
| 77 | + let mut vec = Vec::with_capacity(count as usize); |
| 78 | + // "If the length of the range is set to 0, then the copy operation will continue |
| 79 | + // from the start index of the range to the end of the run" |
| 80 | + CTRunGetPositions(self.0, CFRange::init(0, 0), vec.as_mut_ptr()); |
| 81 | + vec.set_len(count as usize); |
| 82 | + Cow::from(vec) |
| 83 | + } |
| 84 | + } |
| 85 | + } |
| 86 | + |
| 87 | +} |
| 88 | + |
| 89 | +#[test] |
| 90 | +fn create_runs() { |
| 91 | + use core_foundation::attributed_string::CFMutableAttributedString; |
| 92 | + use string_attributes::*; |
| 93 | + use line::*; |
| 94 | + use font; |
| 95 | + let mut string = CFMutableAttributedString::new(); |
| 96 | + string.replace_str(&CFString::new("Food"), CFRange::init(0, 0)); |
| 97 | + let len = string.char_len(); |
| 98 | + unsafe { |
| 99 | + string.set_attribute(CFRange::init(0, len), kCTFontAttributeName, font::new_from_name("Helvetica", 16.).unwrap()); |
| 100 | + } |
| 101 | + let line = CTLine::new_with_attributed_string(string.as_concrete_TypeRef()); |
| 102 | + let runs = line.glyph_runs(); |
| 103 | + assert_eq!(runs.len(), 1); |
| 104 | + for run in runs.iter() { |
| 105 | + assert_eq!(run.glyph_count(), 4); |
| 106 | + let font = run.attributes().unwrap().get(CFString::new("NSFont")).downcast::<font::CTFont>().unwrap(); |
| 107 | + assert_eq!(font.pt_size(), 16.); |
| 108 | + |
| 109 | + let positions = run.positions(); |
| 110 | + assert_eq!(positions.len(), 4); |
| 111 | + assert!(positions[0].x < positions[1].x); |
| 112 | + |
| 113 | + let glyphs = run.glyphs(); |
| 114 | + assert_eq!(glyphs.len(), 4); |
| 115 | + assert_ne!(glyphs[0], glyphs[1]); |
| 116 | + assert_eq!(glyphs[1], glyphs[2]); |
| 117 | + } |
| 118 | +} |
| 119 | + |
| 120 | +#[link(name = "CoreText", kind = "framework")] |
| 121 | +extern { |
| 122 | + fn CTRunGetTypeID() -> CFTypeID; |
| 123 | + fn CTRunGetAttributes(run: CTRunRef) -> CFDictionaryRef; |
| 124 | + fn CTRunGetGlyphCount(run: CTRunRef) -> CFIndex; |
| 125 | + fn CTRunGetPositionsPtr(run: CTRunRef) -> *const CGPoint; |
| 126 | + fn CTRunGetPositions(run: CTRunRef, range: CFRange, buffer: *const CGPoint); |
| 127 | + fn CTRunGetGlyphsPtr(run: CTRunRef) -> *const CGGlyph; |
| 128 | + fn CTRunGetGlyphs(run: CTRunRef, range: CFRange, buffer: *const CGGlyph); |
| 129 | +} |
0 commit comments