1#[derive(Debug, Default, Clone, Copy, PartialEq)]
5pub struct BBox {
6 pub x_min: f32,
8 pub y_min: f32,
10 pub x_max: f32,
12 pub y_max: f32,
14}
15
16impl BBox {
17 #[inline]
19 pub fn width(&self) -> f32 {
20 self.x_max - self.x_min
21 }
22
23 #[inline]
25 pub fn height(&self) -> f32 {
26 self.y_max - self.y_min
27 }
28
29 #[inline]
31 pub fn extend_by(&mut self, x: f32, y: f32) {
32 self.x_min = self.x_min.min(x);
33 self.y_min = self.y_min.min(y);
34 self.x_max = self.x_max.max(x);
35 self.y_max = self.y_max.max(y);
36 }
37}
38
39#[derive(Debug, Clone)]
41pub struct Outline {
42 bbox: std::cell::Cell<Option<BBox>>,
43 cff: bool,
44 contours: Vec<Contour>,
45}
46
47impl Outline {
48 pub fn new(face: &ttf_parser::Face, glyph_id: ttf_parser::GlyphId) -> Option<Self> {
50 let mut outline = Outline {
51 bbox: std::cell::Cell::new(None),
52 cff: face.has_table(ttf_parser::TableName::CompactFontFormat)
53 || face.has_table(ttf_parser::TableName::CompactFontFormat2),
54 contours: Vec::new(),
55 };
56 let mut outline_builder = OutlineBuilder::new(&mut outline);
57 let _ = face.outline_glyph(glyph_id, &mut outline_builder)?;
58 Some(outline)
59 }
60
61 pub fn bbox(&self) -> BBox {
63 if let Some(bbox) = self.bbox.get() {
64 bbox
65 } else {
66 let mut bbox = BBox::default();
67 for (i, p) in self.contours.iter().flat_map(|c| &c.points).enumerate() {
68 if i == 0 {
69 bbox.x_min = p.x;
70 bbox.y_min = p.y;
71 bbox.x_max = p.x;
72 bbox.y_max = p.y;
73 } else {
74 bbox.extend_by(p.x, p.y);
75 }
76 }
77
78 self.bbox.set(Some(bbox));
79 bbox
80 }
81 }
82
83 pub fn embolden(&mut self, strength: f32) {
85 self.bbox.set(None);
86 for c in &mut self.contours {
87 let num_points = c.points.len();
88 if num_points == 0 {
89 continue;
90 }
91
92 let closed = num_points > 1 && c.points.last() == c.points.first();
93 let last = if closed {
94 num_points - 2
95 } else {
96 num_points - 1
97 };
98
99 let mut in_pt = Point::default();
100 let mut in_len = 0f32;
101
102 let mut anchor_pt = Point::default();
103 let mut anchor_len = 0f32;
104
105 let mut i = last;
106 let mut j = 0;
107 let mut k: Option<usize> = None;
108 while i != j && Some(i) != k {
109 let (out_pt, out_len) = if Some(j) != k {
110 let x = c.points[j].x - c.points[i].x;
111 let y = c.points[j].y - c.points[i].y;
112 let len = (x * x + y * y).sqrt();
113 if len != 0.0 {
114 (Point::new(x / len, y / len), len)
115 } else {
116 j = if j < last { j + 1 } else { 0 };
117 continue;
118 }
119 } else {
120 (anchor_pt, anchor_len)
121 };
122
123 if in_len != 0.0 {
124 if k.is_none() {
125 k = Some(i);
126 anchor_pt = in_pt;
127 anchor_len = in_len;
128 }
129
130 let d = (in_pt.x * out_pt.x) + (in_pt.y * out_pt.y);
131 let shift_pt = if d > -0.9375 {
132 let d = d + 1.0;
133 let mut q = out_pt.x * in_pt.y - out_pt.y * in_pt.x;
134 if !self.cff {
135 q = -q;
136 }
137
138 let len = in_len.min(out_len);
139 let (x, y) = if self.cff {
140 (in_pt.y + out_pt.y, -(in_pt.x + out_pt.x))
141 } else {
142 (-(in_pt.y + out_pt.y), in_pt.x + out_pt.x)
143 };
144 if (strength * q) <= (len * d) {
145 Point::new(x * strength / d, y * strength / d)
146 } else {
147 Point::new(x * len / q, y * len / q)
148 }
149 } else {
150 Point::default()
151 };
152
153 while i != j {
154 let pt = &mut c.points[i];
155 pt.x += strength + shift_pt.x;
156 pt.y += strength + shift_pt.y;
157 i = if i < last { i + 1 } else { 0 };
158 }
159 } else {
160 i = j;
161 }
162
163 in_pt = out_pt;
164 in_len = out_len;
165 j = if j < last { j + 1 } else { 0 };
166 }
167
168 if closed {
169 let first = &c.points[0];
170 c.points[num_points - 1] = *first;
171 }
172 }
173 }
174
175 pub fn oblique(&mut self, x_skew: f32) {
177 self.bbox.set(None);
178 for c in &mut self.contours {
179 for p in &mut c.points {
180 if p.y != 0.0 {
181 p.x += p.y * x_skew;
182 }
183 }
184 }
185 }
186
187 pub fn emit(&self, builder: &mut dyn ttf_parser::OutlineBuilder) {
189 let mut points = self.contours.iter().flat_map(|c| &c.points);
190 for v in self.contours.iter().flat_map(|c| &c.verbs) {
191 match v {
192 PathVerb::MoveTo => {
193 let p = points.next().unwrap();
194 builder.move_to(p.x, p.y);
195 }
196 PathVerb::LineTo => {
197 let p = points.next().unwrap();
198 builder.line_to(p.x, p.y);
199 }
200 PathVerb::QuadTo => {
201 let p1 = points.next().unwrap();
202 let p = points.next().unwrap();
203 builder.quad_to(p1.x, p1.y, p.x, p.y);
204 }
205 PathVerb::CurveTo => {
206 let p1 = points.next().unwrap();
207 let p2 = points.next().unwrap();
208 let p = points.next().unwrap();
209 builder.curve_to(p1.x, p1.y, p2.x, p2.y, p.x, p.y);
210 }
211 PathVerb::Close => {
212 builder.close();
213 }
214 }
215 }
216 }
217}
218
219#[derive(Debug, Default, Clone)]
220struct Contour {
221 verbs: Vec<PathVerb>,
222 points: Vec<Point>,
223}
224
225#[derive(Debug, Clone, Copy)]
226enum PathVerb {
227 MoveTo,
228 LineTo,
229 QuadTo,
230 CurveTo,
231 Close,
232}
233
234#[derive(Debug, Default, Clone, Copy, PartialEq)]
235struct Point {
236 x: f32,
237 y: f32,
238}
239
240impl Point {
241 #[inline]
242 fn new(x: f32, y: f32) -> Self {
243 Self { x, y }
244 }
245}
246
247struct OutlineBuilder<'a> {
248 outline: &'a mut Outline,
249 current_contour: usize,
250}
251
252impl<'a> OutlineBuilder<'a> {
253 #[inline]
254 fn new(outline: &'a mut Outline) -> Self {
255 Self {
256 outline,
257 current_contour: 1,
258 }
259 }
260
261 #[inline]
262 fn current_contour(&mut self) -> &mut Contour {
263 if self.current_contour > self.outline.contours.len() {
264 self.outline.contours.push(Contour::default());
265 }
266
267 &mut self.outline.contours[self.current_contour - 1]
268 }
269}
270
271impl<'a> ttf_parser::OutlineBuilder for OutlineBuilder<'a> {
272 fn move_to(&mut self, x: f32, y: f32) {
273 let c = self.current_contour();
274 c.verbs.push(PathVerb::MoveTo);
275 c.points.push(Point::new(x, y));
276 }
277
278 fn line_to(&mut self, x: f32, y: f32) {
279 let c = self.current_contour();
280 c.verbs.push(PathVerb::LineTo);
281 c.points.push(Point::new(x, y));
282 }
283
284 fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
285 let c = self.current_contour();
286 c.verbs.push(PathVerb::QuadTo);
287 c.points.push(Point::new(x1, y1));
288 c.points.push(Point::new(x, y));
289 }
290
291 fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
292 let c = self.current_contour();
293 c.verbs.push(PathVerb::CurveTo);
294 c.points.push(Point::new(x1, y1));
295 c.points.push(Point::new(x2, y2));
296 c.points.push(Point::new(x, y));
297 }
298
299 fn close(&mut self) {
300 self.current_contour().verbs.push(PathVerb::Close);
301 self.current_contour += 1;
302 }
303}