skrifa/outline/glyf/hint/
program.rs

1//! TrueType program management.
2
3use raw::tables::glyf::bytecode::Decoder;
4
5use super::{
6    call_stack::{CallRecord, CallStack},
7    definition::Definition,
8    error::HintErrorKind,
9};
10
11/// Describes the source for a piece of bytecode.
12#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
13#[repr(u8)]
14pub enum Program {
15    /// Program that initializes the function and instruction tables. Stored
16    /// in the `fpgm` table.
17    #[default]
18    Font = 0,
19    /// Program that initializes CVT and storage based on font size and other
20    /// parameters. Stored in the `prep` table.
21    ControlValue = 1,
22    /// Glyph specified program. Stored per-glyph in the `glyf` table.
23    Glyph = 2,
24}
25
26/// State for managing active programs and decoding instructions.
27pub struct ProgramState<'a> {
28    /// Bytecode for each of the three program types, indexed by `Program`.
29    pub bytecode: [&'a [u8]; 3],
30    /// The initial program when execution begins.
31    pub initial: Program,
32    /// The currently active program.
33    pub current: Program,
34    /// Instruction decoder for the currently active program.
35    pub decoder: Decoder<'a>,
36    /// Tracks nested function and instruction invocations.
37    pub call_stack: CallStack,
38}
39
40impl<'a> ProgramState<'a> {
41    pub fn new(
42        font_code: &'a [u8],
43        cv_code: &'a [u8],
44        glyph_code: &'a [u8],
45        initial_program: Program,
46    ) -> Self {
47        let bytecode = [font_code, cv_code, glyph_code];
48        Self {
49            bytecode,
50            initial: initial_program,
51            current: initial_program,
52            decoder: Decoder::new(bytecode[initial_program as usize], 0),
53            call_stack: CallStack::default(),
54        }
55    }
56
57    /// Resets the state for execution of the given program.
58    pub fn reset(&mut self, program: Program) {
59        self.initial = program;
60        self.current = program;
61        self.decoder = Decoder::new(self.bytecode[program as usize], 0);
62        self.call_stack.clear();
63    }
64
65    /// Jumps to the code in the given definition and sets it up for
66    /// execution `count` times.
67    pub fn enter(&mut self, definition: Definition, count: u32) -> Result<(), HintErrorKind> {
68        let program = definition.program();
69        let pc = definition.code_range().start;
70        let bytecode = self.bytecode[program as usize];
71        self.call_stack.push(CallRecord {
72            caller_program: self.current,
73            return_pc: self.decoder.pc,
74            current_count: count,
75            definition,
76        })?;
77        self.current = program;
78        self.decoder = Decoder::new(bytecode, pc);
79        Ok(())
80    }
81
82    /// Leaves the code from the definition on the top of the stack.
83    ///
84    /// If the top call record has a loop count greater than 1, restarts
85    /// execution from the beginning of the definition. Otherwise, resumes
86    /// execution at the previously active definition.
87    pub fn leave(&mut self) -> Result<(), HintErrorKind> {
88        let mut record = self.call_stack.pop()?;
89        if record.current_count > 1 {
90            // This is a loop call with some iterations remaining.
91            record.current_count -= 1;
92            self.decoder.pc = record.definition.code_range().start;
93            self.call_stack.push(record)?;
94        } else {
95            self.current = record.caller_program;
96            // Reset the decoder to the calling program and program counter.
97            self.decoder.bytecode = self.bytecode[record.caller_program as usize];
98            self.decoder.pc = record.return_pc;
99        }
100        Ok(())
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107
108    /// Test accounting of program, bytecode and program counter through
109    /// enter/leave cycles.
110    #[test]
111    fn accounting() {
112        let font_code = &[0][..];
113        let cv_code = &[1][..];
114        let glyph_code = &[2][..];
115        let mut state = ProgramState::new(font_code, cv_code, glyph_code, Program::Glyph);
116        // We start at glyph code
117        assert_eq!(state.active_state(), (Program::Glyph, glyph_code, 0));
118        let font_def = Definition::new(Program::Font, 10..20, 0);
119        let cv_def = Definition::new(Program::ControlValue, 33..111, 1);
120        // Now move to CV code
121        state.enter(cv_def, 1).unwrap();
122        assert_eq!(state.active_state(), (Program::ControlValue, cv_code, 33));
123        // Bump the program counter to test capture of return_pc
124        state.decoder.pc += 20;
125        // And to font code
126        state.enter(font_def, 1).unwrap();
127        assert_eq!(state.active_state(), (Program::Font, font_code, 10));
128        // Back to CV code
129        state.leave().unwrap();
130        assert_eq!(state.active_state(), (Program::ControlValue, cv_code, 53));
131        // And to the original glyph code
132        state.leave().unwrap();
133        assert_eq!(state.active_state(), (Program::Glyph, glyph_code, 0));
134    }
135
136    /// Ensure calls with a count of `n` require `n` leaves before returning
137    /// to previous frame. Also ensure program counter is reset to start of
138    /// definition at each leave.
139    #[test]
140    fn loop_call() {
141        let font_code = &[0][..];
142        let cv_code = &[1][..];
143        let glyph_code = &[2][..];
144        let mut state = ProgramState::new(font_code, cv_code, glyph_code, Program::Glyph);
145        let font_def = Definition::new(Program::Font, 10..20, 0);
146        // "Execute" font definition 3 times
147        state.enter(font_def, 3).unwrap();
148        for _ in 0..3 {
149            assert_eq!(state.active_state(), (Program::Font, font_code, 10));
150            // Modify program counter to ensure we reset on leave
151            state.decoder.pc += 22;
152            state.leave().unwrap();
153        }
154        // Should be back to glyph code
155        assert_eq!(state.active_state(), (Program::Glyph, glyph_code, 0));
156    }
157
158    impl<'a> ProgramState<'a> {
159        fn active_state(&self) -> (Program, &'a [u8], usize) {
160            (self.current, self.decoder.bytecode, self.decoder.pc)
161        }
162    }
163}