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