1mod font_features;
2mod line;
3mod line_wrapper;
4mod text_layout_cache;
5
6use anyhow::anyhow;
7pub use font_features::*;
8pub use line::*;
9use line_wrapper::*;
10use smallvec::SmallVec;
11pub use text_layout_cache::*;
12
13use crate::{
14 px, Bounds, DevicePixels, Hsla, Pixels, PlatformTextSystem, Point, Result, SharedString, Size,
15 UnderlineStyle,
16};
17use collections::HashMap;
18use core::fmt;
19use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
20use std::{
21 fmt::{Debug, Display, Formatter},
22 hash::{Hash, Hasher},
23 ops::{Deref, DerefMut},
24 sync::Arc,
25};
26
27#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)]
28#[repr(C)]
29pub struct FontId(pub usize);
30
31#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)]
32pub struct FontFamilyId(pub usize);
33
34pub const SUBPIXEL_VARIANTS: u8 = 4;
35
36pub struct TextSystem {
37 text_layout_cache: Arc<TextLayoutCache>,
38 platform_text_system: Arc<dyn PlatformTextSystem>,
39 font_ids_by_font: RwLock<HashMap<Font, FontId>>,
40 font_metrics: RwLock<HashMap<FontId, FontMetrics>>,
41 wrapper_pool: Mutex<HashMap<FontIdWithSize, Vec<LineWrapper>>>,
42 font_runs_pool: Mutex<Vec<Vec<(usize, FontId)>>>,
43}
44
45impl TextSystem {
46 pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self {
47 TextSystem {
48 text_layout_cache: Arc::new(TextLayoutCache::new(platform_text_system.clone())),
49 platform_text_system,
50 font_metrics: RwLock::new(HashMap::default()),
51 font_ids_by_font: RwLock::new(HashMap::default()),
52 wrapper_pool: Mutex::new(HashMap::default()),
53 font_runs_pool: Default::default(),
54 }
55 }
56
57 pub fn font_id(&self, font: &Font) -> Result<FontId> {
58 let font_id = self.font_ids_by_font.read().get(font).copied();
59 if let Some(font_id) = font_id {
60 Ok(font_id)
61 } else {
62 let font_id = self.platform_text_system.font_id(font)?;
63 self.font_ids_by_font.write().insert(font.clone(), font_id);
64 Ok(font_id)
65 }
66 }
67
68 pub fn bounding_box(&self, font_id: FontId, font_size: Pixels) -> Result<Bounds<Pixels>> {
69 self.read_metrics(font_id, |metrics| metrics.bounding_box(font_size))
70 }
71
72 pub fn typographic_bounds(
73 &self,
74 font_id: FontId,
75 font_size: Pixels,
76 character: char,
77 ) -> Result<Bounds<Pixels>> {
78 let glyph_id = self
79 .platform_text_system
80 .glyph_for_char(font_id, character)
81 .ok_or_else(|| anyhow!("glyph not found for character '{}'", character))?;
82 let bounds = self
83 .platform_text_system
84 .typographic_bounds(font_id, glyph_id)?;
85 self.read_metrics(font_id, |metrics| {
86 (bounds / metrics.units_per_em as f32 * font_size.0).map(px)
87 })
88 }
89
90 pub fn advance(&self, font_id: FontId, font_size: Pixels, ch: char) -> Result<Size<Pixels>> {
91 let glyph_id = self
92 .platform_text_system
93 .glyph_for_char(font_id, ch)
94 .ok_or_else(|| anyhow!("glyph not found for character '{}'", ch))?;
95 let result = self.platform_text_system.advance(font_id, glyph_id)?
96 / self.units_per_em(font_id)? as f32;
97
98 Ok(result * font_size)
99 }
100
101 pub fn units_per_em(&self, font_id: FontId) -> Result<u32> {
102 self.read_metrics(font_id, |metrics| metrics.units_per_em as u32)
103 }
104
105 pub fn cap_height(&self, font_id: FontId, font_size: Pixels) -> Result<Pixels> {
106 self.read_metrics(font_id, |metrics| metrics.cap_height(font_size))
107 }
108
109 pub fn x_height(&self, font_id: FontId, font_size: Pixels) -> Result<Pixels> {
110 self.read_metrics(font_id, |metrics| metrics.x_height(font_size))
111 }
112
113 pub fn ascent(&self, font_id: FontId, font_size: Pixels) -> Result<Pixels> {
114 self.read_metrics(font_id, |metrics| metrics.ascent(font_size))
115 }
116
117 pub fn descent(&self, font_id: FontId, font_size: Pixels) -> Result<Pixels> {
118 self.read_metrics(font_id, |metrics| metrics.descent(font_size))
119 }
120
121 pub fn baseline_offset(
122 &self,
123 font_id: FontId,
124 font_size: Pixels,
125 line_height: Pixels,
126 ) -> Result<Pixels> {
127 let ascent = self.ascent(font_id, font_size)?;
128 let descent = self.descent(font_id, font_size)?;
129 let padding_top = (line_height - ascent - descent) / 2.;
130 Ok(padding_top + ascent)
131 }
132
133 fn read_metrics<T>(&self, font_id: FontId, read: impl FnOnce(&FontMetrics) -> T) -> Result<T> {
134 let lock = self.font_metrics.upgradable_read();
135
136 if let Some(metrics) = lock.get(&font_id) {
137 Ok(read(metrics))
138 } else {
139 let mut lock = RwLockUpgradableReadGuard::upgrade(lock);
140 let metrics = lock
141 .entry(font_id)
142 .or_insert_with(|| self.platform_text_system.font_metrics(font_id));
143 Ok(read(metrics))
144 }
145 }
146
147 pub fn layout_text(
148 &self,
149 text: &str,
150 font_size: Pixels,
151 runs: &[(usize, RunStyle)],
152 wrap_width: Option<Pixels>,
153 ) -> Result<SmallVec<[Line; 1]>> {
154 let mut font_runs: Vec<(usize, FontId)> =
155 self.font_runs_pool.lock().pop().unwrap_or_default();
156
157 let mut last_font: Option<&Font> = None;
158 for (len, style) in runs {
159 if let Some(last_font) = last_font.as_ref() {
160 if **last_font == style.font {
161 font_runs.last_mut().unwrap().0 += len;
162 continue;
163 }
164 }
165 last_font = Some(&style.font);
166 font_runs.push((*len, self.font_id(&style.font)?));
167 }
168
169 let mut layouts = SmallVec::new();
170 let mut start = 0;
171 let mut run_start = 0;
172 for line in text.lines() {
173 let end = start + line.len();
174 let mut run_end = run_start;
175 let mut line_length = 0;
176 for (len, _) in font_runs[run_start..].iter() {
177 line_length += len;
178 if *len >= line_length {
179 break;
180 }
181 run_end += 1;
182 }
183 // If a run lands in the middle of a line, create an additional run for the remaining characters.
184 if line_length < end - start {
185 // Create a run for the part that fits this line.
186 let partial_run = font_runs[run_end];
187 partial_run.0 = line_length;
188 layouts.push(self.text_layout_cache.layout_line(
189 &text[start..start + line_length],
190 font_size,
191 &font_runs[run_start..=run_end],
192 ));
193 // Update the original run to only include the part that does not fit this line.
194 font_runs[run_end].0 -= line_length;
195 } else {
196 layouts.push(self.text_layout_cache.layout_line(
197 &text[start..end],
198 font_size,
199 &font_runs[run_start..run_end],
200 ));
201 run_start = run_end;
202 }
203 start = end + 1;
204 }
205
206 font_runs.clear();
207 self.font_runs_pool.lock().push(font_runs);
208
209 Ok(layouts)
210 }
211
212 pub fn end_frame(&self) {
213 self.text_layout_cache.end_frame()
214 }
215
216 pub fn line_wrapper(
217 self: &Arc<Self>,
218 font: Font,
219 font_size: Pixels,
220 ) -> Result<LineWrapperHandle> {
221 let lock = &mut self.wrapper_pool.lock();
222 let font_id = self.font_id(&font)?;
223 let wrappers = lock
224 .entry(FontIdWithSize { font_id, font_size })
225 .or_default();
226 let wrapper = wrappers.pop().map(anyhow::Ok).unwrap_or_else(|| {
227 Ok(LineWrapper::new(
228 font_id,
229 font_size,
230 self.platform_text_system.clone(),
231 ))
232 })?;
233
234 Ok(LineWrapperHandle {
235 wrapper: Some(wrapper),
236 text_system: self.clone(),
237 })
238 }
239
240 pub fn raster_bounds(&self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>> {
241 self.platform_text_system.glyph_raster_bounds(params)
242 }
243
244 pub fn rasterize_glyph(
245 &self,
246 glyph_id: &RenderGlyphParams,
247 ) -> Result<(Size<DevicePixels>, Vec<u8>)> {
248 self.platform_text_system.rasterize_glyph(glyph_id)
249 }
250}
251
252#[derive(Hash, Eq, PartialEq)]
253struct FontIdWithSize {
254 font_id: FontId,
255 font_size: Pixels,
256}
257
258pub struct LineWrapperHandle {
259 wrapper: Option<LineWrapper>,
260 text_system: Arc<TextSystem>,
261}
262
263impl Drop for LineWrapperHandle {
264 fn drop(&mut self) {
265 let mut state = self.text_system.wrapper_pool.lock();
266 let wrapper = self.wrapper.take().unwrap();
267 state
268 .get_mut(&FontIdWithSize {
269 font_id: wrapper.font_id.clone(),
270 font_size: wrapper.font_size,
271 })
272 .unwrap()
273 .push(wrapper);
274 }
275}
276
277impl Deref for LineWrapperHandle {
278 type Target = LineWrapper;
279
280 fn deref(&self) -> &Self::Target {
281 self.wrapper.as_ref().unwrap()
282 }
283}
284
285impl DerefMut for LineWrapperHandle {
286 fn deref_mut(&mut self) -> &mut Self::Target {
287 self.wrapper.as_mut().unwrap()
288 }
289}
290
291/// The degree of blackness or stroke thickness of a font. This value ranges from 100.0 to 900.0,
292/// with 400.0 as normal.
293#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
294pub struct FontWeight(pub f32);
295
296impl Default for FontWeight {
297 #[inline]
298 fn default() -> FontWeight {
299 FontWeight::NORMAL
300 }
301}
302
303impl Hash for FontWeight {
304 fn hash<H: Hasher>(&self, state: &mut H) {
305 state.write_u32(u32::from_be_bytes(self.0.to_be_bytes()));
306 }
307}
308
309impl Eq for FontWeight {}
310
311impl FontWeight {
312 /// Thin weight (100), the thinnest value.
313 pub const THIN: FontWeight = FontWeight(100.0);
314 /// Extra light weight (200).
315 pub const EXTRA_LIGHT: FontWeight = FontWeight(200.0);
316 /// Light weight (300).
317 pub const LIGHT: FontWeight = FontWeight(300.0);
318 /// Normal (400).
319 pub const NORMAL: FontWeight = FontWeight(400.0);
320 /// Medium weight (500, higher than normal).
321 pub const MEDIUM: FontWeight = FontWeight(500.0);
322 /// Semibold weight (600).
323 pub const SEMIBOLD: FontWeight = FontWeight(600.0);
324 /// Bold weight (700).
325 pub const BOLD: FontWeight = FontWeight(700.0);
326 /// Extra-bold weight (800).
327 pub const EXTRA_BOLD: FontWeight = FontWeight(800.0);
328 /// Black weight (900), the thickest value.
329 pub const BLACK: FontWeight = FontWeight(900.0);
330}
331
332/// Allows italic or oblique faces to be selected.
333#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)]
334pub enum FontStyle {
335 /// A face that is neither italic not obliqued.
336 Normal,
337 /// A form that is generally cursive in nature.
338 Italic,
339 /// A typically-sloped version of the regular face.
340 Oblique,
341}
342
343impl Default for FontStyle {
344 fn default() -> FontStyle {
345 FontStyle::Normal
346 }
347}
348
349impl Display for FontStyle {
350 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
351 Debug::fmt(self, f)
352 }
353}
354
355#[derive(Clone, Debug, PartialEq, Eq)]
356pub struct RunStyle {
357 pub font: Font,
358 pub color: Hsla,
359 pub underline: Option<UnderlineStyle>,
360}
361
362#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
363#[repr(C)]
364pub struct GlyphId(u32);
365
366impl From<GlyphId> for u32 {
367 fn from(value: GlyphId) -> Self {
368 value.0
369 }
370}
371
372impl From<u16> for GlyphId {
373 fn from(num: u16) -> Self {
374 GlyphId(num as u32)
375 }
376}
377
378impl From<u32> for GlyphId {
379 fn from(num: u32) -> Self {
380 GlyphId(num)
381 }
382}
383
384#[derive(Default, Debug)]
385pub struct LineLayout {
386 pub font_size: Pixels,
387 pub width: Pixels,
388 pub ascent: Pixels,
389 pub descent: Pixels,
390 pub runs: Vec<ShapedRun>,
391 pub len: usize,
392}
393
394#[derive(Debug)]
395pub struct ShapedRun {
396 pub font_id: FontId,
397 pub glyphs: SmallVec<[ShapedGlyph; 8]>,
398}
399
400#[derive(Clone, Debug)]
401pub struct ShapedGlyph {
402 pub id: GlyphId,
403 pub position: Point<Pixels>,
404 pub index: usize,
405 pub is_emoji: bool,
406}
407
408#[derive(Clone, Debug, PartialEq)]
409pub struct RenderGlyphParams {
410 pub(crate) font_id: FontId,
411 pub(crate) glyph_id: GlyphId,
412 pub(crate) font_size: Pixels,
413 pub(crate) subpixel_variant: Point<u8>,
414 pub(crate) scale_factor: f32,
415 pub(crate) is_emoji: bool,
416}
417
418impl Eq for RenderGlyphParams {}
419
420impl Hash for RenderGlyphParams {
421 fn hash<H: Hasher>(&self, state: &mut H) {
422 self.font_id.0.hash(state);
423 self.glyph_id.0.hash(state);
424 self.font_size.0.to_bits().hash(state);
425 self.subpixel_variant.hash(state);
426 self.scale_factor.to_bits().hash(state);
427 }
428}
429
430#[derive(Clone, Debug, PartialEq)]
431pub struct RenderEmojiParams {
432 pub(crate) font_id: FontId,
433 pub(crate) glyph_id: GlyphId,
434 pub(crate) font_size: Pixels,
435 pub(crate) scale_factor: f32,
436}
437
438impl Eq for RenderEmojiParams {}
439
440impl Hash for RenderEmojiParams {
441 fn hash<H: Hasher>(&self, state: &mut H) {
442 self.font_id.0.hash(state);
443 self.glyph_id.0.hash(state);
444 self.font_size.0.to_bits().hash(state);
445 self.scale_factor.to_bits().hash(state);
446 }
447}
448
449#[derive(Clone, Debug, Eq, PartialEq, Hash)]
450pub struct Font {
451 pub family: SharedString,
452 pub features: FontFeatures,
453 pub weight: FontWeight,
454 pub style: FontStyle,
455}
456
457pub fn font(family: impl Into<SharedString>) -> Font {
458 Font {
459 family: family.into(),
460 features: FontFeatures::default(),
461 weight: FontWeight::default(),
462 style: FontStyle::default(),
463 }
464}
465
466impl Font {
467 pub fn bold(mut self) -> Self {
468 self.weight = FontWeight::BOLD;
469 self
470 }
471}
472
473/// A struct for storing font metrics.
474/// It is used to define the measurements of a typeface.
475#[derive(Clone, Copy, Debug)]
476pub struct FontMetrics {
477 /// The number of font units that make up the "em square",
478 /// a scalable grid for determining the size of a typeface.
479 pub(crate) units_per_em: u32,
480
481 /// The vertical distance from the baseline of the font to the top of the glyph covers.
482 pub(crate) ascent: f32,
483
484 /// The vertical distance from the baseline of the font to the bottom of the glyph covers.
485 pub(crate) descent: f32,
486
487 /// The recommended additional space to add between lines of type.
488 pub(crate) line_gap: f32,
489
490 /// The suggested position of the underline.
491 pub(crate) underline_position: f32,
492
493 /// The suggested thickness of the underline.
494 pub(crate) underline_thickness: f32,
495
496 /// The height of a capital letter measured from the baseline of the font.
497 pub(crate) cap_height: f32,
498
499 /// The height of a lowercase x.
500 pub(crate) x_height: f32,
501
502 /// The outer limits of the area that the font covers.
503 pub(crate) bounding_box: Bounds<f32>,
504}
505
506impl FontMetrics {
507 /// Returns the vertical distance from the baseline of the font to the top of the glyph covers in pixels.
508 pub fn ascent(&self, font_size: Pixels) -> Pixels {
509 Pixels((self.ascent / self.units_per_em as f32) * font_size.0)
510 }
511
512 /// Returns the vertical distance from the baseline of the font to the bottom of the glyph covers in pixels.
513 pub fn descent(&self, font_size: Pixels) -> Pixels {
514 Pixels((self.descent / self.units_per_em as f32) * font_size.0)
515 }
516
517 /// Returns the recommended additional space to add between lines of type in pixels.
518 pub fn line_gap(&self, font_size: Pixels) -> Pixels {
519 Pixels((self.line_gap / self.units_per_em as f32) * font_size.0)
520 }
521
522 /// Returns the suggested position of the underline in pixels.
523 pub fn underline_position(&self, font_size: Pixels) -> Pixels {
524 Pixels((self.underline_position / self.units_per_em as f32) * font_size.0)
525 }
526
527 /// Returns the suggested thickness of the underline in pixels.
528 pub fn underline_thickness(&self, font_size: Pixels) -> Pixels {
529 Pixels((self.underline_thickness / self.units_per_em as f32) * font_size.0)
530 }
531
532 /// Returns the height of a capital letter measured from the baseline of the font in pixels.
533 pub fn cap_height(&self, font_size: Pixels) -> Pixels {
534 Pixels((self.cap_height / self.units_per_em as f32) * font_size.0)
535 }
536
537 /// Returns the height of a lowercase x in pixels.
538 pub fn x_height(&self, font_size: Pixels) -> Pixels {
539 Pixels((self.x_height / self.units_per_em as f32) * font_size.0)
540 }
541
542 /// Returns the outer limits of the area that the font covers in pixels.
543 pub fn bounding_box(&self, font_size: Pixels) -> Bounds<Pixels> {
544 (self.bounding_box / self.units_per_em as f32 * font_size.0).map(px)
545 }
546}