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
132
133
134
135
136
137
//! The font sets.

macro_rules! get(
    (@single $operations:expr, $operator:ident) => (
        match $operations.get_single(crate::compact1::Operator::$operator) {
            Some(crate::compact1::Number::Integer(value)) => value,
            Some(_) => raise!(concat!("found a malformed operation with operator ", stringify!($operator))),
            _ => raise!(concat!("found no operation with operator ", stringify!($operator))),
        }
    );
    (@try @single $operations:expr, $operator:ident) => (
        match $operations.get_single(crate::compact1::Operator::$operator) {
            Some(crate::compact1::Number::Integer(value)) => Some(value),
            Some(_) => raise!(concat!("found a malformed operation with operator ", stringify!($operator))),
            _ => None,
        }
    );
    (@double $operations:expr, $operator:ident) => (
        match $operations.get_double(crate::compact1::Operator::$operator) {
            Some((crate::compact1::Number::Integer(value0), crate::compact1::Number::Integer(value1))) => (value0, value1),
            Some(_) => raise!(concat!("found a malformed operation with operator ", stringify!($operator))),
            _ => raise!(concat!("found no operation with operator ", stringify!($operator))),
        }
    );
);

pub mod character_id_keyed;
pub mod character_name_keyed;

use crate::compact1::index::{CharacterStrings, Dictionaries, Names, Strings, Subroutines};
use crate::compact1::{CharacterSet, Encoding, Header, Operations, Operator};
use crate::Result;

/// A font set.
#[derive(Clone, Debug)]
pub struct FontSet {
    pub header: Header,
    pub names: Names,
    pub operations: Vec<Operations>,
    pub strings: Strings,
    pub subroutines: Subroutines,
    pub encodings: Vec<Encoding>,
    pub character_strings: Vec<CharacterStrings>,
    pub character_sets: Vec<CharacterSet>,
    pub records: Vec<Record>,
}

/// A record in a font set.
#[derive(Clone, Debug)]
pub enum Record {
    CharacterIDKeyed(character_id_keyed::Record),
    CharacterNameKeyed(character_name_keyed::Record),
}

impl FontSet {
    /// Count the number of records.
    pub fn count<T: crate::tape::Read>(tape: &mut T) -> Result<usize> {
        let position = tape.position()?;
        let header = tape.take::<Header>()?;
        let count: u16 = jump_take!(@unwrap tape, position, header.header_size);
        Ok(count as usize)
    }
}

impl crate::value::Read for FontSet {
    fn read<T: crate::tape::Read>(tape: &mut T) -> Result<Self> {
        let position = tape.position()?;
        let header = tape.take::<Header>()?;
        let names: Names = jump_take!(@unwrap tape, position, header.header_size);
        let operations: Vec<_> = (&tape.take::<Dictionaries>()?).try_into()?;
        let strings = tape.take::<Strings>()?;
        let subroutines = tape.take::<Subroutines>()?;
        let mut encodings = vec![];
        let mut character_sets = vec![];
        let mut character_strings: Vec<CharacterStrings> = vec![];
        let mut records = vec![];
        for (i, operations) in operations.iter().enumerate() {
            character_strings.push(jump_take_given!(
                @unwrap
                tape,
                position,
                get!(@single operations, CharStrings),
                get!(@single operations, CharStringType)
            ));
            character_sets.push(match get!(@single operations, CharSet) {
                0 => CharacterSet::ISOAdobe,
                1 => CharacterSet::Expert,
                2 => CharacterSet::ExpertSubset,
                offset => jump_take_given!(
                    @unwrap
                    tape,
                    position,
                    offset,
                    character_strings[i].count as usize
                ),
            });
            encodings.push(match get!(@single operations, Encoding) {
                0 => Encoding::Standard,
                1 => Encoding::Expert,
                offset => jump_take!(@unwrap tape, position, offset),
            });
            records.push(tape.take_given((position, operations, &character_strings[i]))?);
        }
        Ok(Self {
            header,
            names,
            operations,
            strings,
            subroutines,
            encodings,
            character_strings,
            character_sets,
            records,
        })
    }
}

impl<'l> crate::walue::Read<'l> for Record {
    type Parameter = (u64, &'l Operations, &'l CharacterStrings);

    fn read<T: crate::tape::Read>(
        tape: &mut T,
        (position, operations, character_strings): Self::Parameter,
    ) -> Result<Self> {
        if operations.contains_key(&Operator::ROS) {
            Ok(Record::CharacterIDKeyed(tape.take_given((
                position,
                operations,
                character_strings,
            ))?))
        } else {
            Ok(Record::CharacterNameKeyed(
                tape.take_given((position, operations))?,
            ))
        }
    }
}