1use gfx::path::Path;
11
12use crate::outline::Outline;
13
14pub const FAST_CP: u32 = 384;
15pub const SGF1_MAGIC: u32 = 0x3146_4753;
16
17pub const SGH_ICON: u16 = 1;
19pub const SGH_KERN: u16 = 2;
20pub const SGH_RUNTIME: u16 = 0x100;
21pub const SGH_COLR: u16 = 0x200; pub const SGH_CBDT: u16 = 0x400; pub const FS_SYN_BOLD: u32 = 1;
26pub const FS_SYN_ITALIC: u32 = 2;
27pub const FS_SHARP: u32 = 4;
28pub const FS_CLASSIC: u32 = 8;
30pub const FS_MASK: u32 = 15;
31
32pub const TD_UNDERLINE: u32 = 1;
34pub const TD_STRIKEOUT: u32 = 2;
35
36pub const FN_FAMILY: u32 = 1;
38pub const FN_STYLE: u32 = 2;
39pub const FN_FULL: u32 = 4;
40pub const FN_TYPO_FAMILY: u32 = 16;
41pub const FN_TYPO_STYLE: u32 = 17;
42
43pub const FM_UPEM: u32 = 0;
45pub const FM_ASC: u32 = 4;
46pub const FM_DESC: u32 = 8;
47pub const FM_GAP: u32 = 12;
48pub const FM_TYPO_ASC: u32 = 16;
49pub const FM_TYPO_DESC: u32 = 20;
50pub const FM_TYPO_GAP: u32 = 24;
51pub const FM_WIN_ASC: u32 = 28;
52pub const FM_WIN_DESC: u32 = 32;
53pub const FM_XH: u32 = 36;
54pub const FM_CAPH: u32 = 40;
55pub const FM_UL_POS: u32 = 44;
56pub const FM_UL_THICK: u32 = 48;
57pub const FM_ST_POS: u32 = 52;
58pub const FM_ST_THICK: u32 = 56;
59pub const FM_WEIGHT: u32 = 60;
60pub const FM_FLAGS: u32 = 64;
61pub const FM_NGLYPHS: u32 = 68;
62pub const FM_ADVMAX: u32 = 72;
63pub const FM_WIDTH: u32 = 76;
64pub const FM_SIZE: u32 = 80;
65
66pub const FMF_ITALIC: i32 = 1;
68pub const FMF_BOLD: i32 = 2;
69pub const FMF_OBLIQUE: i32 = 4;
70pub const FMF_COLOR: i32 = 8;
71pub const FMF_CFF: i32 = 16;
72pub const FMF_VAR: i32 = 32;
73pub const FMF_SYMBOL: i32 = 64;
74pub const FMF_MONO: i32 = 128;
75pub const FMF_TYPO: i32 = 256;
76pub const FMF_EMBED: i32 = 512;
77pub const FMF_SYN: i32 = 1024;
78pub const FMF_BITMAP: i32 = 2048;
79pub const FMF_KERN: i32 = 4096;
80
81#[derive(Clone, Copy, Default)]
84pub struct Hdr {
85 pub flags: u16,
86 pub upem: i32,
87 pub asc: i32,
88 pub desc: i32,
89 pub gap: i32,
90 pub caph: i32,
91 pub xh: i32,
92 pub ulpos: i32,
93 pub ulthick: i32,
94 pub nglyphs: u32,
95 pub baseid: u32, }
97
98#[derive(Clone, Copy, Default)]
100pub struct Metrics {
101 pub upem: i32,
102 pub asc: i32,
103 pub desc: i32,
104 pub gap: i32,
105 pub typo_asc: i32,
106 pub typo_desc: i32,
107 pub typo_gap: i32,
108 pub win_asc: i32,
109 pub win_desc: i32,
110 pub xh: i32,
111 pub caph: i32,
112 pub ul_pos: i32,
113 pub ul_thick: i32,
114 pub st_pos: i32,
115 pub st_thick: i32,
116 pub weight: i32,
117 pub flags: i32,
118 pub nglyphs: i32,
119 pub advmax: i32,
120 pub width: i32,
121}
122
123impl Metrics {
124 pub fn field(&self, off: u32) -> i32 {
126 if off > FM_SIZE - 4 || off & 3 != 0 {
127 return 0;
128 }
129 self.as_array()[(off / 4) as usize]
130 }
131
132 pub fn as_array(&self) -> [i32; 20] {
133 [
134 self.upem, self.asc, self.desc, self.gap, self.typo_asc, self.typo_desc, self.typo_gap,
135 self.win_asc, self.win_desc, self.xh, self.caph, self.ul_pos, self.ul_thick, self.st_pos,
136 self.st_thick, self.weight, self.flags, self.nglyphs, self.advmax, self.width,
137 ]
138 }
139}
140
141#[derive(Clone, Copy, Default)]
144pub struct Gp {
145 pub x: f32,
146 pub y: f32,
147 pub sx: f32,
148 pub sy: f32,
149 pub dx: i32,
150 pub dy: i32,
151}
152
153pub struct Font {
154 data: &'static [u8],
155 pub flags: u16,
156 pub upem: i32,
157 pub asc: i32,
158 pub desc: i32,
159 pub gap: i32,
160 pub caph: i32,
161 pub xh: i32,
162 pub ulpos: i32,
163 pub ulthick: i32,
164 pub nglyphs: u32,
165 nranges: u32,
166 kern_r: u32,
167 off_ranges: usize,
168 off_glyphs: usize,
169 off_outlines: usize,
170 off_kern: usize,
171 pub fallback: u32,
172 fmap: [u16; FAST_CP as usize],
173}
174
175fn u16_at(d: &[u8], o: usize) -> u16 {
176 u16::from_le_bytes([d[o], d[o + 1]])
177}
178
179fn i16_at(d: &[u8], o: usize) -> i32 {
180 i16::from_le_bytes([d[o], d[o + 1]]) as i32
181}
182
183fn u32_at(d: &[u8], o: usize) -> u32 {
184 u32::from_le_bytes([d[o], d[o + 1], d[o + 2], d[o + 3]])
185}
186
187impl Font {
188 pub fn parse(data: &'static [u8]) -> Option<Font> {
190 if data.len() < 64 || u32_at(data, 0) != SGF1_MAGIC {
191 return None;
192 }
193 let mut f = Font {
194 data,
195 flags: u16_at(data, 6),
196 upem: u16_at(data, 8) as i32,
197 asc: i16_at(data, 10),
198 desc: i16_at(data, 12),
199 gap: i16_at(data, 14),
200 caph: i16_at(data, 16),
201 xh: i16_at(data, 18),
202 ulpos: i16_at(data, 20),
203 ulthick: i16_at(data, 22),
204 nglyphs: u16_at(data, 24) as u32,
205 nranges: u16_at(data, 26) as u32,
206 kern_r: u16_at(data, 30) as u32,
207 off_ranges: u32_at(data, 32) as usize,
208 off_glyphs: u32_at(data, 36) as usize,
209 off_outlines: u32_at(data, 40) as usize,
210 off_kern: u32_at(data, 44) as usize,
211 fallback: u16_at(data, 48) as u32,
212 fmap: [0; FAST_CP as usize],
213 };
214 for cp in 0..FAST_CP {
215 f.fmap[cp as usize] = f.glyph_slow(cp) as u16;
216 }
217 Some(f)
218 }
219
220 pub fn new(data: &'static [u8]) -> Font {
221 Font::parse(data).expect("not an SGF1 font")
222 }
223
224 pub fn hdr(&self) -> Hdr {
225 Hdr {
226 flags: self.flags,
227 upem: self.upem,
228 asc: self.asc,
229 desc: self.desc,
230 gap: self.gap,
231 caph: self.caph,
232 xh: self.xh,
233 ulpos: self.ulpos,
234 ulthick: self.ulthick,
235 nglyphs: self.nglyphs,
236 baseid: 0,
237 }
238 }
239
240 pub fn lookup(&self, cp: u32) -> Option<u32> {
242 let (mut lo, mut hi) = (0u32, self.nranges);
243 while lo < hi {
244 let mid = (lo + hi) >> 1;
245 let o = self.off_ranges + mid as usize * 8;
246 let first = u32_at(self.data, o);
247 if cp < first {
248 hi = mid;
249 continue;
250 }
251 let count = u16_at(self.data, o + 4) as u32;
252 if cp >= first.wrapping_add(count) {
253 lo = mid + 1;
254 continue;
255 }
256 return Some((cp - first).wrapping_add(u16_at(self.data, o + 6) as u32));
257 }
258 None
259 }
260
261 fn glyph_slow(&self, cp: u32) -> u32 {
262 self.lookup(cp).unwrap_or(self.fallback)
263 }
264
265 pub fn glyph(&self, cp: u32) -> u32 {
266 if cp < FAST_CP { self.fmap[cp as usize] as u32 } else { self.glyph_slow(cp) }
267 }
268
269 fn rec(&self, g: u32) -> Option<usize> {
270 if g >= self.nglyphs { None } else { Some(self.off_glyphs + g as usize * 16) }
271 }
272
273 pub fn adv(&self, g: u32) -> i32 {
274 self.rec(g).map_or(0, |r| u16_at(self.data, r) as i32)
275 }
276
277 pub fn kern(&self, g1: u32, g2: u32) -> i32 {
279 if self.flags & SGH_KERN == 0 || g1 >= self.nglyphs || g2 >= self.nglyphs {
280 return 0;
281 }
282 let n = self.nglyphs as usize;
283 let k = self.off_kern;
284 let lc = self.data[k + g1 as usize] as u32;
285 let rc = self.data[k + n + g2 as usize] as u32;
286 if lc == 0 || rc == 0 {
287 return 0;
288 }
289 let m = (lc * self.kern_r + rc) as usize;
290 (self.data[k + 2 * n + m] as i8 as i32) << 2
291 }
292
293 pub fn glyph_box(&self, g: u32) -> Option<[i32; 4]> {
294 let r = self.rec(g)?;
295 if u16_at(self.data, r + 10) == 0 {
296 return None;
297 }
298 Some([
299 i16_at(self.data, r + 2),
300 i16_at(self.data, r + 4),
301 i16_at(self.data, r + 6),
302 i16_at(self.data, r + 8),
303 ])
304 }
305
306 fn point(gp: &Gp, fx: i32, fy: i32) -> (f32, f32) {
308 let x = (fx.wrapping_add(gp.dx)) as f32 * gp.sx + gp.x;
309 let y = gp.y - (fy.wrapping_add(gp.dy)) as f32 * gp.sy;
310 (x, y)
311 }
312
313 pub fn path(&self, g: u32, gp: &mut Gp, path: &mut Path, depth: u32) {
316 let Some(r) = self.rec(g) else { return };
317 let n = u16_at(self.data, r + 10) as usize;
318 if n == 0 || depth > 64 {
319 return;
320 }
321 let ol = self.off_outlines + u32_at(self.data, r + 12) as usize;
322 if n & 0x8000 != 0 {
323 for c in 0..n & 0x7FFF {
324 let o = ol + c * 8;
325 let (sdx, sdy) = (gp.dx, gp.dy);
326 gp.dx = gp.dx.wrapping_add(i16_at(self.data, o + 2));
327 gp.dy = gp.dy.wrapping_add(i16_at(self.data, o + 4));
328 self.path(u16_at(self.data, o) as u32, gp, path, depth + 1);
329 gp.dx = sdx;
330 gp.dy = sdy;
331 }
332 return;
333 }
334 let pts = ol + ((n + n + 3) & !3);
335 let on = |i: usize| u16_at(self.data, pts + i * 4) & 1 != 0;
336 let load = |i: usize| {
337 let x = i16_at(self.data, pts + i * 4) >> 1;
338 let y = i16_at(self.data, pts + i * 4 + 2);
339 Self::point(gp, x, y)
340 };
341 let mut first = 0usize;
342 for c in 0..n {
343 let last = u16_at(self.data, ol + c * 2) as usize;
344 if last < first {
345 first = last + 1;
346 continue;
347 }
348 let count = last - first + 1;
349 let (start, mut next, mut left);
351 if on(first) {
352 start = load(first);
353 next = first + 1;
354 left = count - 1;
355 } else if on(last) {
356 start = load(last);
357 next = first;
358 left = count - 1;
359 } else {
360 let a = load(last);
361 let b = load(first);
362 start = ((b.0 + a.0) * 0.5, (b.1 + a.1) * 0.5);
363 next = first;
364 left = count;
365 }
366 path.move_f(start.0, start.1);
367 let mut pending: Option<(f32, f32)> = None;
368 while left > 0 {
369 left -= 1;
370 let i = next;
371 next += 1;
372 let p = load(i);
373 if on(i) {
374 match pending.take() {
375 Some(c) => path.quad_f(c.0, c.1, p.0, p.1),
376 None => path.line_f(p.0, p.1),
377 }
378 } else {
379 if let Some(c) = pending {
380 path.quad_f(c.0, c.1, (p.0 + c.0) * 0.5, (p.1 + c.1) * 0.5);
382 }
383 pending = Some(p);
384 }
385 }
386 if let Some(c) = pending {
387 path.quad_f(c.0, c.1, start.0, start.1);
388 }
389 path.close();
390 first = last + 1;
391 }
392 }
393
394 pub fn outline(&self, g: u32, dx: i32, dy: i32, depth: u32, ol: &mut Outline) {
397 if depth > 16 {
398 return;
399 }
400 let Some(r) = self.rec(g) else { return };
401 let n = u16_at(self.data, r + 10) as usize;
402 if n == 0 {
403 return;
404 }
405 let o = self.off_outlines + u32_at(self.data, r + 12) as usize;
406 if n & 0x8000 != 0 {
407 for c in 0..n & 0x7FFF {
408 let e = o + c * 8;
409 let cdx = i16_at(self.data, e + 2).wrapping_add(dx);
410 let cdy = i16_at(self.data, e + 4).wrapping_add(dy);
411 self.outline(u16_at(self.data, e) as u32, cdx, cdy, depth + 1, ol);
412 }
413 return;
414 }
415 let pts = o + ((n + n + 3) & !3);
416 let mut pt = 0usize;
417 for c in 0..n {
418 let last = u16_at(self.data, o + c * 2) as usize;
419 while pt <= last {
420 let v = i16_at(self.data, pts + pt * 4);
421 let tag = (v & 1) as u8;
422 let x = (v >> 1).wrapping_add(dx) as f32;
423 let y = i16_at(self.data, pts + pt * 4 + 2).wrapping_add(dy) as f32;
424 ol.point(x, y, tag);
425 pt += 1;
426 }
427 ol.close();
428 }
429 }
430}