1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use std::io::Cursor;

use crate::compact1::index::{CharStrings, Dictionaries, Names, Strings, Subroutines};
use crate::compact1::{CharSet, Encoding, Header, Operand, Operations, Operator};
use crate::{Result, Tape, Value};

/// A font set.
#[derive(Clone, Debug)]
pub struct FontSet {
    pub header: Header,
    pub names: Vec<String>,
    pub strings: Strings,
    pub encodings: Vec<Encoding>,
    pub char_sets: Vec<CharSet>,
    pub char_strings: Vec<CharStrings>,
    pub global_dictionaries: Vec<Operations>,
    pub global_subroutines: Subroutines,
    pub local_dictionaries: Vec<Operations>,
    pub local_subroutines: Vec<Subroutines>,
}

macro_rules! is_i32(($value:ident) => ($value as i32 as Operand == $value));

macro_rules! get(
    (@single $operations:expr, $operator:ident) => (
        match $operations.get_single(Operator::$operator) {
            Some(value) if is_i32!(value) => value as i32,
            Some(_) => raise!(concat!("found a malformed operation with operator ", stringify!($operator))),
            _ => raise!(concat!("failed to find an operation with operator ", stringify!($operator))),
        }
    );
    (@double $operations:expr, $operator:ident) => (
        match $operations.get_double(Operator::$operator) {
            Some((value0, value1)) if is_i32!(value0) && is_i32!(value1) => {
                (value0 as i32, value1 as i32)
            },
            Some(_) => raise!(concat!("found a malformed operation with operator ", stringify!($operator))),
            _ => raise!(concat!("failed to find an operation with operator ", stringify!($operator))),
        }
    );
    (@try @double $operations:expr, $operator:ident) => (
        match $operations.get_double(Operator::$operator) {
            Some((value0, value1)) if is_i32!(value0) && is_i32!(value1) => {
                Some((value0 as i32, value1 as i32))
            },
            Some(_) => raise!(concat!("found a malformed operation with operator ", stringify!($operator))),
            _ => None,
        }
    );
);

impl Value for FontSet {
    fn read<T: Tape>(tape: &mut T) -> Result<Self> {
        let position = tape.position()?;
        let header = tape.take::<Header>()?;
        tape.jump(position + header.header_size as u64)?;
        let names = tape.take::<Names>()?.into()?;
        let global_dictionaries = tape.take::<Dictionaries>()?.into()?;
        let strings = tape.take::<Strings>()?;
        let global_subroutines = tape.take::<Subroutines>()?;
        let mut encodings = vec![];
        let mut char_sets = vec![];
        let mut char_strings = vec![];
        let mut local_dictionaries = vec![];
        let mut local_subroutines = vec![];
        for (i, dictionary) in global_dictionaries.iter().enumerate() {
            encodings.push(match get!(@single dictionary, Encoding) {
                0 => Encoding::Standard,
                1 => Encoding::Expert,
                _ => unimplemented!(),
            });
            char_strings.push({
                tape.jump(position + get!(@single dictionary, CharStrings) as u64)?;
                tape.take_given::<CharStrings>(get!(@single dictionary, CharStringType))?
            });
            char_sets.push(match get!(@single dictionary, CharSet) {
                0 => CharSet::ISOAdobe,
                1 => CharSet::Expert,
                2 => CharSet::ExpertSubset,
                offset => {
                    tape.jump(position + offset as u64)?;
                    tape.take_given(char_strings[i].len())?
                }
            });
            local_dictionaries.push({
                if cfg!(not(feature = "ignore-missing-operators")) {
                    let (size, offset) = get!(@double dictionary, Private);
                    tape.jump(position + offset as u64)?;
                    let chunk = tape.take_given::<Vec<u8>>(size as usize)?;
                    Cursor::new(chunk).take::<Operations>()?
                } else {
                    if let Some((size, offset)) = get!(@try @double dictionary, Private) {
                        tape.jump(position + offset as u64)?;
                        let chunk = tape.take_given::<Vec<u8>>(size as usize)?;
                        Cursor::new(chunk).take::<Operations>()?
                    } else {
                        Default::default()
                    }
                }
            });
            local_subroutines.push({
                if cfg!(not(feature = "ignore-missing-operators")) {
                    let (_, mut offset) = get!(@double dictionary, Private);
                    offset += get!(@single &local_dictionaries[i], Subrs);
                    tape.jump(position + offset as u64)?;
                    tape.take()?
                } else {
                    if let Some((_, mut offset)) = get!(@try @double dictionary, Private) {
                        offset += get!(@single &local_dictionaries[i], Subrs);
                        tape.jump(position + offset as u64)?;
                        tape.take()?
                    } else {
                        Default::default()
                    }
                }
            });
        }
        Ok(FontSet {
            header: header,
            names: names,
            strings: strings,
            encodings: encodings,
            char_sets: char_sets,
            char_strings: char_strings,
            global_dictionaries: global_dictionaries,
            global_subroutines: global_subroutines,
            local_dictionaries: local_dictionaries,
            local_subroutines: local_subroutines,
        })
    }
}