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
//! 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, Tape, Value, Walue};

/// 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 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>()?;
        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![];
        let mut records = vec![];
        for (i, operations) in operations.iter().enumerate() {
            character_strings.push({
                tape.jump(position + get!(@single operations, CharStrings) as u64)?;
                tape.take_given::<CharacterStrings>(get!(@single operations, CharStringType))?
            });
            character_sets.push(match get!(@single operations, CharSet) {
                0 => CharacterSet::ISOAdobe,
                1 => CharacterSet::Expert,
                2 => CharacterSet::ExpertSubset,
                offset => {
                    tape.jump(position + offset as u64)?;
                    tape.take_given(character_strings[i].count as usize)?
                }
            });
            encodings.push(match get!(@single operations, Encoding) {
                0 => Encoding::Standard,
                1 => Encoding::Expert,
                offset => {
                    tape.jump(position + offset as u64)?;
                    tape.take()?
                }
            });
            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> Walue<'l> for Record {
    type Parameter = (u64, &'l Operations, &'l CharacterStrings);

    fn read<T: Tape>(
        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))?,
            ))
        }
    }
}