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