1
//! `gasp` — Grid-fitting And Scan-conversion Procedure Table
2
//!
3
//! <https://learn.microsoft.com/en-us/typography/opentype/spec/gasp>
4

            
5
use crate::binary::read::{ReadBinary, ReadCtxt};
6
use crate::error::ParseError;
7

            
8
/// Flags indicating grid-fitting and anti-aliasing behavior for a ppem range.
9
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
10
pub struct GaspBehavior {
11
    pub flags: u16,
12
}
13

            
14
impl GaspBehavior {
15
    /// Use gridfitting (bytecode hinting).
16
    pub const GRIDFIT: u16 = 0x0001;
17
    /// Use grayscale rendering (anti-aliasing).
18
    pub const DOGRAY: u16 = 0x0002;
19
    /// Use gridfitting with ClearType symmetric smoothing.
20
    pub const SYMMETRIC_GRIDFIT: u16 = 0x0004;
21
    /// Use smoothing along multiple axes with ClearType.
22
    pub const SYMMETRIC_SMOOTHING: u16 = 0x0008;
23

            
24
    pub fn should_gridfit(self) -> bool {
25
        self.flags & Self::GRIDFIT != 0
26
    }
27

            
28
    pub fn should_antialias(self) -> bool {
29
        self.flags & Self::DOGRAY != 0
30
    }
31

            
32
    pub fn should_symmetric_gridfit(self) -> bool {
33
        self.flags & Self::SYMMETRIC_GRIDFIT != 0
34
    }
35

            
36
    pub fn should_symmetric_smoothing(self) -> bool {
37
        self.flags & Self::SYMMETRIC_SMOOTHING != 0
38
    }
39
}
40

            
41
/// A single range entry in the `gasp` table.
42
#[derive(Copy, Clone, Debug)]
43
pub struct GaspRange {
44
    /// Upper ppem limit for this range (inclusive).
45
    pub range_max_ppem: u16,
46
    /// Behavior flags for ppem values in this range.
47
    pub behavior: GaspBehavior,
48
}
49

            
50
/// The `gasp` (Grid-fitting And Scan-conversion Procedure) table.
51
///
52
/// Maps ppem size ranges to rendering behavior flags that control whether
53
/// bytecode hinting and/or anti-aliasing should be applied.
54
#[derive(Clone, Debug)]
55
pub struct GaspTable {
56
    pub version: u16,
57
    pub ranges: Vec<GaspRange>,
58
}
59

            
60
impl GaspTable {
61
    /// Look up the rendering behavior flags for a given ppem size.
62
    ///
63
    /// Scans the ranges in order and returns the flags for the first range
64
    /// whose `range_max_ppem` is >= the given ppem. If no range matches,
65
    /// returns default flags (gridfit + dogray).
66
    pub fn rendering_flags(&self, ppem: u16) -> GaspBehavior {
67
        for range in &self.ranges {
68
            if ppem <= range.range_max_ppem {
69
                return range.behavior;
70
            }
71
        }
72
        // Default: enable everything
73
        GaspBehavior {
74
            flags: GaspBehavior::GRIDFIT | GaspBehavior::DOGRAY,
75
        }
76
    }
77
}
78

            
79
impl ReadBinary for GaspTable {
80
    type HostType<'a> = GaspTable;
81

            
82
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
83
        let version = ctxt.read_u16be()?;
84
        ctxt.check(version <= 1)?;
85
        let num_ranges = ctxt.read_u16be()? as usize;
86
        let mut ranges = Vec::with_capacity(num_ranges);
87
        for _ in 0..num_ranges {
88
            let range_max_ppem = ctxt.read_u16be()?;
89
            let flags = ctxt.read_u16be()?;
90
            ranges.push(GaspRange {
91
                range_max_ppem,
92
                behavior: GaspBehavior { flags },
93
            });
94
        }
95
        Ok(GaspTable { version, ranges })
96
    }
97
}