1use std::collections::HashMap;
2use std::fs::File;
3use std::io::{BufRead, BufReader};
4
5use crate::canvas::Canvas;
6use crate::image::Color;
7
8#[derive(Copy, Clone)]
9struct BoundingBox {
10 width: i32,
11 height: i32,
12 x: i32,
13 y: i32,
14}
15
16pub struct Glyph {
17 dwidth: i32,
18 bbx: BoundingBox,
19 bitmap: Vec<u32>,
20}
21
22pub struct Font {
23 font_bounding_box: BoundingBox,
24 glyphs: HashMap<i32, Glyph>,
25}
26
27pub type SharedFont = shared_type!(Font);
28
29impl Font {
30 pub fn new(filename: &str) -> SharedFont {
31 let mut font_bounding_box = BoundingBox {
32 width: 0,
33 height: 0,
34 x: 0,
35 y: 0,
36 };
37 let mut glyphs = HashMap::new();
38 let mut code = None;
39 let mut bitmap = None;
40 let mut dwidth = 0;
41 let mut bbx = BoundingBox {
42 width: 0,
43 height: 0,
44 x: 0,
45 y: 0,
46 };
47
48 let file = File::open(filename).unwrap();
49 for line in BufReader::new(file).lines().map_while(Result::ok) {
50 if line.starts_with("FONTBOUNDINGBOX") {
51 let values: Vec<i32> = line
52 .split_whitespace()
53 .skip(1)
54 .map(|v| v.parse().unwrap())
55 .collect();
56 font_bounding_box = BoundingBox {
57 width: values[0],
58 height: values[1],
59 x: values[2],
60 y: values[3],
61 };
62 } else if line.starts_with("ENCODING") {
63 code = Some(
64 line.split_whitespace()
65 .nth(1)
66 .unwrap()
67 .parse::<i32>()
68 .unwrap(),
69 );
70 } else if line.starts_with("DWIDTH") {
71 dwidth = line
72 .split_whitespace()
73 .nth(1)
74 .map(|v| v.parse().unwrap())
75 .unwrap();
76 } else if line.starts_with("BBX") {
77 let values: Vec<i32> = line
78 .split_whitespace()
79 .skip(1)
80 .map(|v| v.parse().unwrap())
81 .collect();
82 bbx = BoundingBox {
83 width: values[0],
84 height: values[1],
85 x: values[2],
86 y: values[3],
87 };
88 } else if line.starts_with("BITMAP") {
89 bitmap = Some(Vec::new());
90 } else if line.starts_with("ENDCHAR") {
91 if let (Some(code), Some(bitmap)) = (code, bitmap) {
92 glyphs.insert(
93 code,
94 Glyph {
95 dwidth,
96 bbx,
97 bitmap,
98 },
99 );
100 }
101 bitmap = None;
102 } else if let Some(ref mut bitmap) = bitmap {
103 let hex_string = line.trim();
104 let bin_string = u32::from_str_radix(hex_string, 16).unwrap();
105 bitmap.push(bin_string.reverse_bits() >> (32 - hex_string.len() * 4));
106 }
107 }
108
109 new_shared_type!(Font {
110 font_bounding_box,
111 glyphs,
112 })
113 }
114
115 pub fn text_width(&self, s: &str) -> i32 {
116 s.chars()
117 .map(|c| self.glyphs.get(&(c as i32)).map_or(0, |glyph| glyph.dwidth))
118 .sum()
119 }
120
121 pub(crate) fn draw(
122 &self,
123 canvas: &mut Canvas<Color>,
124 x: i32,
125 y: i32,
126 text: &str,
127 color: Color,
128 ) {
129 let mut x = x;
130 for c in text.chars() {
131 if let Some(glyph) = self.glyphs.get(&(c as i32)) {
132 self.draw_glyph(canvas, x, y, glyph, color);
133 x += glyph.dwidth;
134 }
135 }
136 }
137
138 fn draw_glyph(&self, canvas: &mut Canvas<Color>, x: i32, y: i32, glyph: &Glyph, color: Color) {
139 let x = x + self.font_bounding_box.x + glyph.bbx.x;
140 let y = y + self.font_bounding_box.y + self.font_bounding_box.height
141 - glyph.bbx.y
142 - glyph.bbx.height;
143
144 for (i, &row) in glyph.bitmap.iter().enumerate() {
145 let value_y = y + i as i32;
146 for j in 0..glyph.bbx.width {
147 let value_x = x + j;
148 if canvas.clip_rect.contains(value_x, value_y) && (row >> j) & 1 == 1 {
149 canvas.write_data(value_x as usize, value_y as usize, color);
150 }
151 }
152 }
153 }
154}