Skip to main content

ttf_rs/tables/
glyf.rs

1use crate::error::Result;
2use crate::stream::FontReader;
3use super::loca::LocaTable;
4
5/// 2D Point for glyph coordinates
6#[derive(Debug, Clone, Copy, PartialEq)]
7pub struct Point {
8    pub x: f32,
9    pub y: f32,
10}
11
12impl Point {
13    pub fn new(x: f32, y: f32) -> Self {
14        Self { x, y }
15    }
16
17    /// Apply transformation matrix to the point
18    pub fn transform(&self, transform: &Transform) -> Point {
19        Point {
20            x: transform.xx * self.x + transform.xy * self.y + transform.dx,
21            y: transform.yx * self.x + transform.yy * self.y + transform.dy,
22        }
23    }
24}
25
26/// Bounding box for glyphs
27#[derive(Debug, Clone, Copy, PartialEq)]
28pub struct BoundingBox {
29    pub x_min: f32,
30    pub y_min: f32,
31    pub x_max: f32,
32    pub y_max: f32,
33}
34
35impl BoundingBox {
36    pub fn new(x_min: f32, y_min: f32, x_max: f32, y_max: f32) -> Self {
37        Self {
38            x_min,
39            y_min,
40            x_max,
41            y_max,
42        }
43    }
44
45    /// Create from existing glyph bounding box values
46    pub fn from_glyph(x_min: i16, y_min: i16, x_max: i16, y_max: i16) -> Self {
47        Self {
48            x_min: x_min as f32,
49            y_min: y_min as f32,
50            x_max: x_max as f32,
51            y_max: y_max as f32,
52        }
53    }
54
55    /// Calculate width of bounding box
56    pub fn width(&self) -> f32 {
57        self.x_max - self.x_min
58    }
59
60    /// Calculate height of bounding box
61    pub fn height(&self) -> f32 {
62        self.y_max - self.y_min
63    }
64
65    /// Merge two bounding boxes
66    pub fn merge(&self, other: &BoundingBox) -> BoundingBox {
67        BoundingBox {
68            x_min: self.x_min.min(other.x_min),
69            y_min: self.y_min.min(other.y_min),
70            x_max: self.x_max.max(other.x_max),
71            y_max: self.y_max.max(other.y_max),
72        }
73    }
74}
75
76/// GLYF table - Glyph data
77#[derive(Debug, Clone)]
78pub struct GlyfTable {
79    pub glyphs: Vec<Glyph>,
80}
81
82#[derive(Debug, Clone)]
83pub struct Glyph {
84    pub number_of_contours: i16,
85    pub x_min: i16,
86    pub y_min: i16,
87    pub x_max: i16,
88    pub y_max: i16,
89    pub data: GlyphData,
90}
91
92#[derive(Debug, Clone)]
93pub enum GlyphData {
94    Simple(SimpleGlyph),
95    Composite(CompositeGlyph),
96    Empty,
97}
98
99#[derive(Debug, Clone)]
100pub struct SimpleGlyph {
101    pub end_pts_of_contours: Vec<u16>,
102    pub instruction_length: u16,
103    pub instructions: Vec<u8>,
104    pub flags: Vec<u8>,
105    pub x_coordinates: Vec<i16>,
106    pub y_coordinates: Vec<i16>,
107}
108
109#[derive(Debug, Clone)]
110pub struct CompositeGlyph {
111    pub components: Vec<GlyphComponent>,
112}
113
114#[derive(Debug, Clone)]
115pub struct GlyphComponent {
116    pub flags: u16,
117    pub glyph_index: u16,
118    pub arg1: i16,
119    pub arg2: i16,
120    pub transform: Transform,
121}
122
123#[derive(Debug, Clone)]
124pub struct Transform {
125    pub xx: f32,
126    pub xy: f32,
127    pub yx: f32,
128    pub yy: f32,
129    pub dx: f32,
130    pub dy: f32,
131}
132
133impl Glyph {
134    pub fn is_simple(&self) -> bool {
135        matches!(self.data, GlyphData::Simple(_))
136    }
137
138    pub fn is_composite(&self) -> bool {
139        matches!(self.data, GlyphData::Composite(_))
140    }
141
142    pub fn is_empty(&self) -> bool {
143        matches!(self.data, GlyphData::Empty)
144    }
145
146    /// Calculate the bounding box for this glyph
147    pub fn calculate_bounding_box(&self) -> Option<BoundingBox> {
148        if self.is_empty() {
149            return None;
150        }
151
152        // For simple glyphs, calculate from points
153        if let GlyphData::Simple(simple) = &self.data {
154            if simple.x_coordinates.is_empty() {
155                return Some(BoundingBox::from_glyph(
156                    self.x_min, self.y_min, self.x_max, self.y_max
157                ));
158            }
159
160            let mut x_min = f32::MAX;
161            let mut y_min = f32::MAX;
162            let mut x_max = f32::MIN;
163            let mut y_max = f32::MIN;
164
165            for i in 0..simple.x_coordinates.len() {
166                let x = simple.x_coordinates[i] as f32;
167                let y = simple.y_coordinates[i] as f32;
168                x_min = x_min.min(x);
169                y_min = y_min.min(y);
170                x_max = x_max.max(x);
171                y_max = y_max.max(y);
172            }
173
174            Some(BoundingBox::new(x_min, y_min, x_max, y_max))
175        } else if let GlyphData::Composite(composite) = &self.data {
176            // For composite glyphs, return the stored bounding box
177            Some(BoundingBox::from_glyph(
178                self.x_min, self.y_min, self.x_max, self.y_max
179            ))
180        } else {
181            None
182        }
183    }
184
185    /// Transform the glyph by applying a transformation matrix
186    pub fn transform(&mut self, transform: &Transform) -> Result<()> {
187        match &mut self.data {
188            GlyphData::Simple(simple) => {
189                // Transform all coordinates
190                for (x, y) in simple.x_coordinates.iter_mut().zip(simple.y_coordinates.iter_mut()) {
191                    let old_x = *x as f32;
192                    let old_y = *y as f32;
193                    let new_x = transform.xx * old_x + transform.xy * old_y + transform.dx;
194                    let new_y = transform.yx * old_x + transform.yy * old_y + transform.dy;
195                    *x = new_x.round() as i16;
196                    *y = new_y.round() as i16;
197                }
198
199                // Update bounding box
200                if let Some(bbox) = self.calculate_bounding_box() {
201                    self.x_min = bbox.x_min.round() as i16;
202                    self.y_min = bbox.y_min.round() as i16;
203                    self.x_max = bbox.x_max.round() as i16;
204                    self.y_max = bbox.y_max.round() as i16;
205                }
206            }
207            GlyphData::Composite(composite) => {
208                // Update the transform for each component
209                for component in &mut composite.components {
210                    component.transform = Self::combine_transforms(&component.transform, transform);
211                }
212
213                // Update bounding box
214                if let Some(bbox) = self.calculate_bounding_box() {
215                    self.x_min = bbox.x_min.round() as i16;
216                    self.y_min = bbox.y_min.round() as i16;
217                    self.x_max = bbox.x_max.round() as i16;
218                    self.y_max = bbox.y_max.round() as i16;
219                }
220            }
221            GlyphData::Empty => {}
222        }
223        Ok(())
224    }
225
226    /// Scale the glyph by the given factors
227    pub fn scale(&mut self, scale_x: f32, scale_y: f32) -> Result<()> {
228        let transform = Transform {
229            xx: scale_x,
230            xy: 0.0,
231            yx: 0.0,
232            yy: scale_y,
233            dx: 0.0,
234            dy: 0.0,
235        };
236        self.transform(&transform)
237    }
238
239    /// Rotate the glyph by the given angle (in radians)
240    pub fn rotate(&mut self, angle: f32) -> Result<()> {
241        let cos_a = angle.cos();
242        let sin_a = angle.sin();
243        let transform = Transform {
244            xx: cos_a,
245            xy: -sin_a,
246            yx: sin_a,
247            yy: cos_a,
248            dx: 0.0,
249            dy: 0.0,
250        };
251        self.transform(&transform)
252    }
253
254    /// Translate the glyph by the given offsets
255    pub fn translate(&mut self, dx: f32, dy: f32) -> Result<()> {
256        let transform = Transform {
257            xx: 1.0,
258            xy: 0.0,
259            yx: 0.0,
260            yy: 1.0,
261            dx,
262            dy,
263        };
264        self.transform(&transform)
265    }
266
267    /// Combine two transforms (applied right to left: second then first)
268    fn combine_transforms(first: &Transform, second: &Transform) -> Transform {
269        Transform {
270            xx: first.xx * second.xx + first.xy * second.yx,
271            xy: first.xx * second.xy + first.xy * second.yy,
272            yx: first.yx * second.xx + first.yy * second.yx,
273            yy: first.yx * second.xy + first.yy * second.yy,
274            dx: first.xx * second.dx + first.xy * second.dy + first.dx,
275            dy: first.yx * second.dx + first.yy * second.dy + first.dy,
276        }
277    }
278
279    /// Simplify the glyph outline by removing redundant points
280    /// This is a basic implementation that removes collinear points
281    pub fn simplify(&mut self, tolerance: f32) -> Result<()> {
282        if let GlyphData::Simple(simple) = &mut self.data {
283            if simple.x_coordinates.len() < 3 {
284                return Ok(());
285            }
286
287            let mut to_remove = vec![false; simple.x_coordinates.len()];
288
289            // Check each point (except endpoints of contours)
290            let mut contour_start = 0;
291            for &end_pt in &simple.end_pts_of_contours {
292                let end = end_pt as usize;
293
294                // Check interior points of this contour
295                for i in (contour_start + 1)..end {
296                    let prev = if i > contour_start { i - 1 } else { end };
297                    let next = if i < end { i + 1 } else { contour_start };
298
299                    let x1 = simple.x_coordinates[prev] as f32;
300                    let y1 = simple.y_coordinates[prev] as f32;
301                    let x2 = simple.x_coordinates[i] as f32;
302                    let y2 = simple.y_coordinates[i] as f32;
303                    let x3 = simple.x_coordinates[next] as f32;
304                    let y3 = simple.y_coordinates[next] as f32;
305
306                    // Check if point i is collinear with prev and next
307                    let area = ((x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1)).abs();
308
309                    if area < tolerance {
310                        // Also check distance to ensure point isn't adding precision
311                        let dist = ((x2 - x1).powi(2) + (y2 - y1).powi(2)).sqrt();
312                        if dist < tolerance || ((x3 - x2).powi(2) + (y3 - y2).powi(2)).sqrt() < tolerance {
313                            to_remove[i] = true;
314                        }
315                    }
316                }
317
318                contour_start = end + 1;
319            }
320
321            // Remove marked points (in reverse order to preserve indices)
322            for i in (0..to_remove.len()).rev() {
323                if to_remove[i] {
324                    simple.x_coordinates.remove(i);
325                    simple.y_coordinates.remove(i);
326                    simple.flags.remove(i);
327
328                    // Update contour end indices
329                    for end_pt in &mut simple.end_pts_of_contours {
330                        if *end_pt as usize > i {
331                            *end_pt = (*end_pt as usize - 1) as u16;
332                        }
333                    }
334                }
335            }
336        }
337        Ok(())
338    }
339}
340
341impl Default for Transform {
342    fn default() -> Self {
343        Self {
344            xx: 1.0,
345            xy: 0.0,
346            yx: 0.0,
347            yy: 1.0,
348            dx: 0.0,
349            dy: 0.0,
350        }
351    }
352}
353
354impl GlyfTable {
355    pub fn from_reader(reader: &mut FontReader, _length: u32, loca: &LocaTable, num_glyphs: u16) -> Result<Self> {
356        let mut glyphs = Vec::with_capacity(num_glyphs as usize);
357
358        for i in 0..num_glyphs {
359            let offset = loca.get_offset(i as usize)?;
360            let next_offset = loca.get_offset(i as usize + 1)?;
361
362            if offset == next_offset {
363                // Empty glyph
364                glyphs.push(Glyph {
365                    number_of_contours: 0,
366                    x_min: 0,
367                    y_min: 0,
368                    x_max: 0,
369                    y_max: 0,
370                    data: GlyphData::Empty,
371                });
372                continue;
373            }
374
375            reader.set_position(offset as usize)?;
376
377            let number_of_contours = reader.read_i16()?;
378            let x_min = reader.read_i16()?;
379            let y_min = reader.read_i16()?;
380            let x_max = reader.read_i16()?;
381            let y_max = reader.read_i16()?;
382
383            let data = if number_of_contours > 0 {
384                // Simple glyph
385                GlyphData::Simple(SimpleGlyph::read(reader, number_of_contours as usize)?)
386            } else if number_of_contours < 0 {
387                // Composite glyph
388                GlyphData::Composite(CompositeGlyph::read(reader)?)
389            } else {
390                GlyphData::Empty
391            };
392
393            glyphs.push(Glyph {
394                number_of_contours,
395                x_min,
396                y_min,
397                x_max,
398                y_max,
399                data,
400            });
401        }
402
403        Ok(GlyfTable { glyphs })
404    }
405
406    pub fn get_glyph(&self, index: usize) -> Option<&Glyph> {
407        self.glyphs.get(index)
408    }
409
410    pub fn get_glyph_mut(&mut self, index: usize) -> Option<&mut Glyph> {
411        self.glyphs.get_mut(index)
412    }
413
414    /// Resolve a composite glyph by flattening it into a simple glyph
415    /// This resolves all component references and transforms
416    pub fn resolve_composite(&self, glyph_index: usize) -> Result<Option<Glyph>> {
417        let glyph = self.get_glyph(glyph_index);
418        let glyph = match glyph {
419            Some(g) => g,
420            None => return Ok(None),
421        };
422
423        match &glyph.data {
424            GlyphData::Simple(_) | GlyphData::Empty => {
425                // Already simple or empty, return a clone
426                Ok(Some(glyph.clone()))
427            }
428            GlyphData::Composite(composite) => {
429                // Resolve all components
430                let mut all_points: Vec<(i16, i16)> = Vec::new();
431                let mut all_flags: Vec<u8> = Vec::new();
432                let mut all_contours: Vec<u16> = Vec::new();
433                let mut total_contours = 0;
434
435                for component in &composite.components {
436                    let component_glyph = self.get_glyph(component.glyph_index as usize);
437                    let component_glyph = match component_glyph {
438                        Some(g) => g,
439                        None => continue,
440                    };
441
442                    if let GlyphData::Simple(simple) = &component_glyph.data {
443                        let transform = &component.transform;
444
445                        // Transform and collect all points from this component
446                        let point_count = simple.x_coordinates.len();
447                        for i in 0..point_count {
448                            let x = simple.x_coordinates[i] as f32;
449                            let y = simple.y_coordinates[i] as f32;
450
451                            let new_x = transform.xx * x + transform.xy * y + transform.dx;
452                            let new_y = transform.yx * x + transform.yy * y + transform.dy;
453
454                            all_points.push((new_x.round() as i16, new_y.round() as i16));
455                            all_flags.push(simple.flags.get(i).copied().unwrap_or(0));
456                        }
457
458                        // Update contour endpoints
459                        for end_pt in &simple.end_pts_of_contours {
460                            all_contours.push((*end_pt as usize + total_contours) as u16);
461                            total_contours += *end_pt as usize + 1;
462                        }
463                    }
464                }
465
466                // Build a new simple glyph from resolved components
467                if !all_points.is_empty() {
468                    let mut x_coordinates = Vec::new();
469                    let mut y_coordinates = Vec::new();
470                    for (x, y) in &all_points {
471                        x_coordinates.push(*x);
472                        y_coordinates.push(*y);
473                    }
474
475                    // Calculate bounding box
476                    let x_min = x_coordinates.iter().min().unwrap();
477                    let y_min = y_coordinates.iter().min().unwrap();
478                    let x_max = x_coordinates.iter().max().unwrap();
479                    let y_max = y_coordinates.iter().max().unwrap();
480
481                    Ok(Some(Glyph {
482                        number_of_contours: all_contours.len() as i16,
483                        x_min: *x_min,
484                        y_min: *y_min,
485                        x_max: *x_max,
486                        y_max: *y_max,
487                        data: GlyphData::Simple(SimpleGlyph {
488                            end_pts_of_contours: all_contours,
489                            instruction_length: 0,
490                            instructions: Vec::new(),
491                            flags: all_flags,
492                            x_coordinates,
493                            y_coordinates,
494                        }),
495                    }))
496                } else {
497                    Ok(None)
498                }
499            }
500        }
501    }
502}
503
504impl SimpleGlyph {
505    fn read(reader: &mut FontReader, num_contours: usize) -> Result<Self> {
506        let mut end_pts_of_contours = Vec::with_capacity(num_contours);
507        for _ in 0..num_contours {
508            end_pts_of_contours.push(reader.read_u16()?);
509        }
510
511        let instruction_length = reader.read_u16()?;
512        let mut instructions = Vec::with_capacity(instruction_length as usize);
513        for _ in 0..instruction_length {
514            instructions.push(reader.read_u8()?);
515        }
516
517        let num_points = if let Some(&last) = end_pts_of_contours.last() {
518            last as usize + 1
519        } else {
520            0
521        };
522
523        let mut flags = Vec::with_capacity(num_points);
524        let mut i = 0;
525        while i < num_points {
526            let flag = reader.read_u8()?;
527            flags.push(flag);
528            i += 1;
529
530            // Check for repeat flag
531            if flag & 0x8 != 0 {
532                let repeat_count = reader.read_u8()? as usize;
533                for _ in 0..repeat_count {
534                    flags.push(flag);
535                    i += 1;
536                }
537            }
538        }
539
540        let mut x_coordinates = Vec::with_capacity(num_points);
541        let mut x = 0i16;
542        for &flag in &flags {
543            if flag & 0x2 != 0 {
544                // 1-byte signed
545                let val = reader.read_i8()?;
546                x += if flag & 0x10 != 0 { val as i16 } else { -(val as i16) };
547            } else if flag & 0x10 == 0 {
548                // 2-byte signed
549                x += reader.read_i16()?;
550            }
551            // else: x is unchanged (duplicate)
552            x_coordinates.push(x);
553        }
554
555        let mut y_coordinates = Vec::with_capacity(num_points);
556        let mut y = 0i16;
557        for &flag in &flags {
558            if flag & 0x4 != 0 {
559                // 1-byte signed
560                let val = reader.read_i8()?;
561                y += if flag & 0x20 != 0 { val as i16 } else { -(val as i16) };
562            } else if flag & 0x20 == 0 {
563                // 2-byte signed
564                y += reader.read_i16()?;
565            }
566            // else: y is unchanged (duplicate)
567            y_coordinates.push(y);
568        }
569
570        Ok(SimpleGlyph {
571            end_pts_of_contours,
572            instruction_length,
573            instructions,
574            flags,
575            x_coordinates,
576            y_coordinates,
577        })
578    }
579}
580
581impl CompositeGlyph {
582    fn read(reader: &mut FontReader) -> Result<Self> {
583        let mut components = Vec::new();
584
585        loop {
586            let flags = reader.read_u16()?;
587            let glyph_index = reader.read_u16()?;
588
589            let arg1 = if flags & 0x1 != 0 {
590                reader.read_i16()?
591            } else {
592                reader.read_i8()? as i16
593            };
594
595            let arg2 = if flags & 0x1 != 0 {
596                reader.read_i16()?
597            } else {
598                reader.read_i8()? as i16
599            };
600
601            let mut transform = Transform::default();
602
603            // Read transform based on flags
604            transform.xx = if flags & 0x80 != 0 {
605                reader.read_f2dot14()?
606            } else {
607                1.0
608            };
609
610            transform.xy = if flags & 0x40 != 0 {
611                reader.read_f2dot14()?
612            } else {
613                0.0
614            };
615
616            transform.yx = if flags & 0x20 != 0 {
617                reader.read_f2dot14()?
618            } else {
619                0.0
620            };
621
622            transform.yy = if flags & 0x10 != 0 {
623                reader.read_f2dot14()?
624            } else {
625                1.0
626            };
627
628            if flags & 0x8 != 0 {
629                transform.dx = reader.read_i16()? as f32;
630                transform.dy = reader.read_i16()? as f32;
631            } else {
632                // Use arg1/arg2 as offsets
633                transform.dx = arg1 as f32;
634                transform.dy = arg2 as f32;
635            }
636
637            components.push(GlyphComponent {
638                flags,
639                glyph_index,
640                arg1,
641                arg2,
642                transform,
643            });
644
645            if flags & 0x20 == 0 {
646                // More components flag
647                break;
648            }
649        }
650
651        Ok(CompositeGlyph { components })
652    }
653}