1use crate::{
2 fonts::{FontId, GlyphId},
3 geometry::{
4 rect::{RectF, RectI},
5 transform2d::Transform2F,
6 vector::{vec2f, vec2i, Vector2F},
7 },
8 platform,
9 text_layout::{Glyph, Line, Run},
10};
11use cocoa::appkit::{CGFloat, CGPoint};
12use core_foundation::{
13 attributed_string::CFMutableAttributedString,
14 base::{CFRange, TCFType},
15 number::CFNumber,
16 string::CFString,
17};
18use core_graphics::{
19 base::CGGlyph, color_space::CGColorSpace, context::CGContext, geometry::CGAffineTransform,
20};
21use core_text::{line::CTLine, string_attributes::kCTFontAttributeName};
22use font_kit::{
23 canvas::RasterizationOptions, hinting::HintingOptions, metrics::Metrics,
24 properties::Properties, source::SystemSource,
25};
26use parking_lot::RwLock;
27use std::{char, convert::TryFrom};
28
29#[allow(non_upper_case_globals)]
30const kCGImageAlphaOnly: u32 = 7;
31
32pub struct FontSystem(RwLock<FontSystemState>);
33
34struct FontSystemState {
35 source: SystemSource,
36 fonts: Vec<font_kit::font::Font>,
37}
38
39impl FontSystem {
40 pub fn new() -> Self {
41 Self(RwLock::new(FontSystemState {
42 source: SystemSource::new(),
43 fonts: Vec::new(),
44 }))
45 }
46}
47
48impl platform::FontSystem for FontSystem {
49 fn load_family(&self, name: &str) -> anyhow::Result<Vec<FontId>> {
50 self.0.write().load_family(name)
51 }
52
53 fn select_font(&self, font_ids: &[FontId], properties: &Properties) -> anyhow::Result<FontId> {
54 self.0.read().select_font(font_ids, properties)
55 }
56
57 fn font_metrics(&self, font_id: FontId) -> Metrics {
58 self.0.read().font_metrics(font_id)
59 }
60
61 fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> anyhow::Result<RectF> {
62 self.0.read().typographic_bounds(font_id, glyph_id)
63 }
64
65 fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId> {
66 self.0.read().glyph_for_char(font_id, ch)
67 }
68
69 fn rasterize_glyph(
70 &self,
71 font_id: FontId,
72 font_size: f32,
73 glyph_id: GlyphId,
74 subpixel_shift: Vector2F,
75 scale_factor: f32,
76 ) -> Option<(RectI, Vec<u8>)> {
77 self.0
78 .read()
79 .rasterize_glyph(font_id, font_size, glyph_id, subpixel_shift, scale_factor)
80 }
81
82 fn layout_str(
83 &self,
84 text: &str,
85 font_size: f32,
86 runs: &[(std::ops::Range<usize>, FontId)],
87 ) -> Line {
88 self.0.read().layout_str(text, font_size, runs)
89 }
90}
91
92impl FontSystemState {
93 fn load_family(&mut self, name: &str) -> anyhow::Result<Vec<FontId>> {
94 let mut font_ids = Vec::new();
95 for font in self.source.select_family_by_name(name)?.fonts() {
96 let font = font.load()?;
97 font_ids.push(FontId(self.fonts.len()));
98 self.fonts.push(font);
99 }
100 Ok(font_ids)
101 }
102
103 fn select_font(&self, font_ids: &[FontId], properties: &Properties) -> anyhow::Result<FontId> {
104 let candidates = font_ids
105 .iter()
106 .map(|font_id| self.fonts[font_id.0].properties())
107 .collect::<Vec<_>>();
108 let idx = font_kit::matching::find_best_match(&candidates, properties)?;
109 Ok(font_ids[idx])
110 }
111
112 fn font_metrics(&self, font_id: FontId) -> Metrics {
113 self.fonts[font_id.0].metrics()
114 }
115
116 fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> anyhow::Result<RectF> {
117 Ok(self.fonts[font_id.0].typographic_bounds(glyph_id)?)
118 }
119
120 fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId> {
121 self.fonts[font_id.0].glyph_for_char(ch)
122 }
123
124 fn rasterize_glyph(
125 &self,
126 font_id: FontId,
127 font_size: f32,
128 glyph_id: GlyphId,
129 subpixel_shift: Vector2F,
130 scale_factor: f32,
131 ) -> Option<(RectI, Vec<u8>)> {
132 let font = &self.fonts[font_id.0];
133 let scale = Transform2F::from_scale(scale_factor);
134 let bounds = font
135 .raster_bounds(
136 glyph_id,
137 font_size,
138 scale,
139 HintingOptions::None,
140 RasterizationOptions::GrayscaleAa,
141 )
142 .ok()?;
143
144 if bounds.width() == 0 || bounds.height() == 0 {
145 None
146 } else {
147 // Make room for subpixel variants.
148 let bounds = RectI::new(bounds.origin(), bounds.size() + vec2i(1, 1));
149 let mut pixels = vec![0; bounds.width() as usize * bounds.height() as usize];
150 let ctx = CGContext::create_bitmap_context(
151 Some(pixels.as_mut_ptr() as *mut _),
152 bounds.width() as usize,
153 bounds.height() as usize,
154 8,
155 bounds.width() as usize,
156 &CGColorSpace::create_device_gray(),
157 kCGImageAlphaOnly,
158 );
159
160 // Move the origin to bottom left and account for scaling, this
161 // makes drawing text consistent with the font-kit's raster_bounds.
162 ctx.translate(0.0, bounds.height() as CGFloat);
163 let transform = scale.translate(-bounds.origin().to_f32());
164 ctx.set_text_matrix(&CGAffineTransform {
165 a: transform.matrix.m11() as CGFloat,
166 b: -transform.matrix.m21() as CGFloat,
167 c: -transform.matrix.m12() as CGFloat,
168 d: transform.matrix.m22() as CGFloat,
169 tx: transform.vector.x() as CGFloat,
170 ty: -transform.vector.y() as CGFloat,
171 });
172
173 ctx.set_font(&font.native_font().copy_to_CGFont());
174 ctx.set_font_size(font_size as CGFloat);
175 ctx.show_glyphs_at_positions(
176 &[glyph_id as CGGlyph],
177 &[CGPoint::new(
178 (subpixel_shift.x() / scale_factor) as CGFloat,
179 (subpixel_shift.y() / scale_factor) as CGFloat,
180 )],
181 );
182
183 Some((bounds, pixels))
184 }
185 }
186
187 fn layout_str(
188 &self,
189 text: &str,
190 font_size: f32,
191 runs: &[(std::ops::Range<usize>, FontId)],
192 ) -> Line {
193 let font_id_attr_name = CFString::from_static_string("zed_font_id");
194
195 let mut string = CFMutableAttributedString::new();
196 string.replace_str(&CFString::new(text), CFRange::init(0, 0));
197
198 let mut utf16_lens = text.chars().map(|c| c.len_utf16());
199 let mut prev_char_ix = 0;
200 let mut prev_utf16_ix = 0;
201
202 for (range, font_id) in runs {
203 let utf16_start = prev_utf16_ix
204 + utf16_lens
205 .by_ref()
206 .take(range.start - prev_char_ix)
207 .sum::<usize>();
208 let utf16_end = utf16_start
209 + utf16_lens
210 .by_ref()
211 .take(range.end - range.start)
212 .sum::<usize>();
213 prev_char_ix = range.end;
214 prev_utf16_ix = utf16_end;
215
216 let cf_range = CFRange::init(utf16_start as isize, (utf16_end - utf16_start) as isize);
217 let font = &self.fonts[font_id.0];
218 unsafe {
219 string.set_attribute(
220 cf_range,
221 kCTFontAttributeName,
222 &font.native_font().clone_with_font_size(font_size as f64),
223 );
224 string.set_attribute(
225 cf_range,
226 font_id_attr_name.as_concrete_TypeRef(),
227 &CFNumber::from(font_id.0 as i64),
228 );
229 }
230 }
231
232 let line = CTLine::new_with_attributed_string(string.as_concrete_TypeRef());
233
234 let width = line.get_typographic_bounds().width as f32;
235
236 let mut utf16_chars = text.encode_utf16();
237 let mut char_ix = 0;
238 let mut prev_utf16_ix = 0;
239
240 let mut runs = Vec::new();
241 for run in line.glyph_runs().into_iter() {
242 let font_id = FontId(
243 run.attributes()
244 .unwrap()
245 .get(&font_id_attr_name)
246 .downcast::<CFNumber>()
247 .unwrap()
248 .to_i64()
249 .unwrap() as usize,
250 );
251
252 let mut glyphs = Vec::new();
253 for ((glyph_id, position), utf16_ix) in run
254 .glyphs()
255 .iter()
256 .zip(run.positions().iter())
257 .zip(run.string_indices().iter())
258 {
259 let utf16_ix = usize::try_from(*utf16_ix).unwrap();
260 char_ix +=
261 char::decode_utf16(utf16_chars.by_ref().take(utf16_ix - prev_utf16_ix)).count();
262 prev_utf16_ix = utf16_ix;
263
264 glyphs.push(Glyph {
265 id: *glyph_id as GlyphId,
266 position: vec2f(position.x as f32, position.y as f32),
267 index: char_ix,
268 });
269 }
270
271 runs.push(Run { font_id, glyphs })
272 }
273
274 Line {
275 width,
276 runs,
277 font_size,
278 len: char_ix + 1,
279 }
280 }
281}
282
283#[cfg(test)]
284mod tests {
285 use super::*;
286 use font_kit::properties::{Style, Weight};
287 use platform::FontSystem as _;
288
289 #[test]
290 fn test_layout_str() -> anyhow::Result<()> {
291 let fonts = FontSystem::new();
292 let menlo = fonts.load_family("Menlo")?;
293 let menlo_regular = fonts.select_font(&menlo, &Properties::new())?;
294 let menlo_italic = fonts.select_font(&menlo, &Properties::new().style(Style::Italic))?;
295 let menlo_bold = fonts.select_font(&menlo, &Properties::new().weight(Weight::BOLD))?;
296
297 let line = fonts.layout_str(
298 "hello world",
299 16.0,
300 &[
301 (0..2, menlo_bold),
302 (2..6, menlo_italic),
303 (6..11, menlo_regular),
304 ],
305 );
306 assert_eq!(line.runs.len(), 3);
307 assert_eq!(line.runs[0].font_id, menlo_bold);
308 assert_eq!(line.runs[0].glyphs.len(), 2);
309 assert_eq!(line.runs[1].font_id, menlo_italic);
310 assert_eq!(line.runs[1].glyphs.len(), 4);
311 assert_eq!(line.runs[2].font_id, menlo_regular);
312 assert_eq!(line.runs[2].glyphs.len(), 5);
313 Ok(())
314 }
315
316 #[test]
317 fn test_char_indices() -> anyhow::Result<()> {
318 let fonts = FontSystem::new();
319 let zapfino = fonts.load_family("Zapfino")?;
320 let zapfino_regular = fonts.select_font(&zapfino, &Properties::new())?;
321 let menlo = fonts.load_family("Menlo")?;
322 let menlo_regular = fonts.select_font(&menlo, &Properties::new())?;
323
324 let text = "This is, mπre πr less, Zapfino!π";
325 let line = fonts.layout_str(
326 text,
327 16.0,
328 &[
329 (0..9, zapfino_regular),
330 (9..22, menlo_regular),
331 (22..text.encode_utf16().count(), zapfino_regular),
332 ],
333 );
334 assert_eq!(
335 line.runs
336 .iter()
337 .flat_map(|r| r.glyphs.iter())
338 .map(|g| g.index)
339 .collect::<Vec<_>>(),
340 vec![
341 0, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 30, 31
342 ]
343 );
344 Ok(())
345 }
346
347 // #[test]
348 // fn test_render_glyph() {
349 // use std::{fs::File, io::BufWriter, path::Path};
350
351 // let fonts = FontSystem::new();
352 // let font_ids = fonts.load_family("Fira Code").unwrap();
353 // let font_id = fonts.select_font(&font_ids, &Default::default()).unwrap();
354 // let glyph_id = fonts.glyph_for_char(font_id, 'G').unwrap();
355
356 // for i in 0..2 {
357 // let variant = 0.5 * i as f32;
358 // let (bounds, bytes) = fonts
359 // .rasterize_glyph(font_id, 16.0, glyph_id, variant, 2.)
360 // .unwrap();
361
362 // let name = format!("/Users/as-cii/Desktop/twog-{}.png", i);
363 // let path = Path::new(&name);
364 // let file = File::create(path).unwrap();
365 // let ref mut w = BufWriter::new(file);
366
367 // let mut encoder = png::Encoder::new(w, bounds.width() as u32, bounds.height() as u32);
368 // encoder.set_color(png::ColorType::Grayscale);
369 // encoder.set_depth(png::BitDepth::Eight);
370 // let mut writer = encoder.write_header().unwrap();
371 // writer.write_image_data(&bytes).unwrap();
372 // }
373 // }
374}