1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use crate::*;
use riddle_common::Color;
use riddle_image::Image;
use futures::{AsyncRead, AsyncReadExt};
use std::io::Read;
pub struct TTFont {
font: rusttype::Font<'static>,
}
impl TTFont {
pub fn load<R: Read>(mut r: R) -> Result<Self> {
let mut data = vec![0u8; 0];
r.read_to_end(&mut data)?;
let font = rusttype::Font::try_from_vec(data).ok_or(FontError::FontParseFailed)?;
Ok(TTFont { font })
}
pub async fn load_async<R: AsyncRead + Unpin>(mut r: R) -> Result<Self> {
let mut data = vec![0u8; 0];
r.read_to_end(&mut data).await?;
let font = rusttype::Font::try_from_vec(data).ok_or(FontError::FontParseFailed)?;
Ok(TTFont { font })
}
pub fn render_simple(&self, text: &str, pixel_height: u32) -> Result<Image> {
let scale = rusttype::Scale::uniform(pixel_height as f32);
let layout: Vec<rusttype::PositionedGlyph> = self
.font
.layout(text, scale, rusttype::Point { x: 0.0, y: 0.0 })
.collect();
if layout.is_empty() {
Ok(Image::new(0, 0))
} else {
let base_line = self.font.v_metrics(scale).ascent as i32;
let max_x = layout
.iter()
.map(|glyph| glyph.pixel_bounding_box().unwrap_or_default().max.x)
.max()
.unwrap();
let mut img = Image::new(max_x as u32, pixel_height);
for glyph in layout {
let bb = glyph.pixel_bounding_box().unwrap_or_default();
glyph.draw(|x, y, v| {
let b = (255.0 * v) as u8;
img.set_pixel(
(bb.min.x + x as i32) as u32,
(base_line + bb.min.y + (y as i32)) as u32,
Color::rgba(255, 255, 255, b),
);
})
}
Ok(img)
}
}
}
impl rusttype_ext::RustTypeTTFontExt for TTFont {
fn rustype_font(&self) -> &rusttype::Font<'static> {
&self.font
}
}