1use core_foundation::base::{CFIndex, CFRange, CFType, CFTypeID, TCFType};
11use core_foundation::dictionary::{CFDictionary, CFDictionaryRef};
12use core_foundation::string::CFString;
13use core_foundation::{declare_TCFType, impl_CFTypeDescription, impl_TCFType};
14use core_graphics::base::CGFloat;
15use core_graphics::font::CGGlyph;
16use core_graphics::geometry::CGPoint;
17use std::borrow::Cow;
18use std::os::raw::c_void;
19use std::slice;
20
21use crate::line::TypographicBounds;
22
23#[repr(C)]
24pub struct __CTRun(c_void);
25
26pub type CTRunRef = *const __CTRun;
27
28declare_TCFType! {
29 CTRun, CTRunRef
30}
31impl_TCFType!(CTRun, CTRunRef, CTRunGetTypeID);
32impl_CFTypeDescription!(CTRun);
33
34impl CTRun {
35 pub fn attributes(&self) -> Option<CFDictionary<CFString, CFType>> {
36 unsafe {
37 let attrs = CTRunGetAttributes(self.0);
38 if attrs.is_null() {
39 return None;
40 }
41 Some(TCFType::wrap_under_get_rule(attrs))
42 }
43 }
44 pub fn glyph_count(&self) -> CFIndex {
45 unsafe { CTRunGetGlyphCount(self.0) }
46 }
47
48 pub fn glyphs(&self) -> Cow<[CGGlyph]> {
49 unsafe {
50 let count = CTRunGetGlyphCount(self.0);
54 let glyphs_ptr = CTRunGetGlyphsPtr(self.0);
55 if !glyphs_ptr.is_null() {
56 Cow::from(slice::from_raw_parts(glyphs_ptr, count as usize))
57 } else {
58 let mut vec = Vec::with_capacity(count as usize);
59 CTRunGetGlyphs(self.0, CFRange::init(0, 0), vec.as_mut_ptr());
62 vec.set_len(count as usize);
63 Cow::from(vec)
64 }
65 }
66 }
67
68 pub fn positions(&self) -> Cow<[CGPoint]> {
69 unsafe {
70 let count = CTRunGetGlyphCount(self.0);
74 let positions_ptr = CTRunGetPositionsPtr(self.0);
75 if !positions_ptr.is_null() {
76 Cow::from(slice::from_raw_parts(positions_ptr, count as usize))
77 } else {
78 let mut vec = Vec::with_capacity(count as usize);
79 CTRunGetPositions(self.0, CFRange::init(0, 0), vec.as_mut_ptr());
82 vec.set_len(count as usize);
83 Cow::from(vec)
84 }
85 }
86 }
87
88 pub fn get_typographic_bounds(&self) -> TypographicBounds {
89 let mut ascent = 0.0;
90 let mut descent = 0.0;
91 let mut leading = 0.0;
92 unsafe {
93 let range = CFRange {
96 location: 0,
97 length: 0,
98 };
99
100 let width = CTRunGetTypographicBounds(
101 self.as_concrete_TypeRef(),
102 range,
103 &mut ascent,
104 &mut descent,
105 &mut leading,
106 );
107 TypographicBounds {
108 width,
109 ascent,
110 descent,
111 leading,
112 }
113 }
114 }
115
116 pub fn string_indices(&self) -> Cow<[CFIndex]> {
117 unsafe {
118 let count = CTRunGetGlyphCount(self.0);
122 let indices_ptr = CTRunGetStringIndicesPtr(self.0);
123 if !indices_ptr.is_null() {
124 Cow::from(slice::from_raw_parts(indices_ptr, count as usize))
125 } else {
126 let mut vec = Vec::with_capacity(count as usize);
127 CTRunGetStringIndices(self.0, CFRange::init(0, 0), vec.as_mut_ptr());
130 vec.set_len(count as usize);
131 Cow::from(vec)
132 }
133 }
134 }
135}
136
137#[test]
138fn create_runs() {
139 use crate::font;
140 use crate::line::*;
141 use crate::string_attributes::*;
142 use core_foundation::attributed_string::CFMutableAttributedString;
143 let mut string = CFMutableAttributedString::new();
144 string.replace_str(&CFString::new("Food"), CFRange::init(0, 0));
145 let len = string.char_len();
146 unsafe {
147 string.set_attribute(
148 CFRange::init(0, len),
149 kCTFontAttributeName,
150 &font::new_from_name("Helvetica", 16.).unwrap(),
151 );
152 }
153 let line = CTLine::new_with_attributed_string(string.as_concrete_TypeRef());
154 let runs = line.glyph_runs();
155 assert_eq!(runs.len(), 1);
156 for run in runs.iter() {
157 assert_eq!(run.glyph_count(), 4);
158 let font = run
159 .attributes()
160 .unwrap()
161 .get(CFString::new("NSFont"))
162 .downcast::<font::CTFont>()
163 .unwrap();
164 assert_eq!(font.pt_size(), 16.);
165
166 let positions = run.positions();
167 assert_eq!(positions.len(), 4);
168 assert!(positions[0].x < positions[1].x);
169
170 let glyphs = run.glyphs();
171 assert_eq!(glyphs.len(), 4);
172 assert_ne!(glyphs[0], glyphs[1]);
173 assert_eq!(glyphs[1], glyphs[2]);
174
175 let indices = run.string_indices();
176 assert_eq!(indices.as_ref(), &[0, 1, 2, 3]);
177 }
178}
179
180#[cfg_attr(feature = "link", link(name = "CoreText", kind = "framework"))]
181extern "C" {
182 fn CTRunGetTypeID() -> CFTypeID;
183 fn CTRunGetAttributes(run: CTRunRef) -> CFDictionaryRef;
184 fn CTRunGetGlyphCount(run: CTRunRef) -> CFIndex;
185 fn CTRunGetPositionsPtr(run: CTRunRef) -> *const CGPoint;
186 fn CTRunGetPositions(run: CTRunRef, range: CFRange, buffer: *const CGPoint);
187 fn CTRunGetStringIndicesPtr(run: CTRunRef) -> *const CFIndex;
188 fn CTRunGetStringIndices(run: CTRunRef, range: CFRange, buffer: *const CFIndex);
189 fn CTRunGetGlyphsPtr(run: CTRunRef) -> *const CGGlyph;
190 fn CTRunGetGlyphs(run: CTRunRef, range: CFRange, buffer: *const CGGlyph);
191 fn CTRunGetTypographicBounds(
192 line: CTRunRef,
193 range: CFRange,
194 ascent: *mut CGFloat,
195 descent: *mut CGFloat,
196 leading: *mut CGFloat,
197 ) -> CGFloat;
198}