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
138
139
140
141
142
143
144
145
/// Packed points within a Tuple Variation Store
use otspec::types::*;
use otspec::{
    DeserializationError, Deserialize, Deserializer, ReaderContext, SerializationError, Serialize,
    Serializer,
};

/// An array of packed points
///
/// If the option is None, then this represents all points within the glyph.
/// (Including phantom points.) This must be decoded with reference to the
/// glyph's contour and component information. If the option is Some, a vector
/// of the point numbers for which delta information is provided.
#[derive(Debug, PartialEq)]
pub struct PackedPoints {
    /// the array of points
    pub points: Option<Vec<uint16>>,
}

impl Deserialize for PackedPoints {
    fn from_bytes(c: &mut ReaderContext) -> Result<Self, DeserializationError> {
        let count1_u8: u8 = c.de()?;
        let mut count: u16 = count1_u8 as u16;
        if count > 127 {
            let count2: u8 = c.de()?;
            count = (count & 0xff) << 8 | count2 as u16;
        }
        if count == 0 {
            // All of them
            return Ok(PackedPoints { points: None });
        }
        let mut res = vec![];
        while res.len() < count as usize {
            let control_byte: u8 = c.de()?;
            let points_are_words = (control_byte & 0x80) > 0;
            // "The low 7 bits specify the number of elements in the run minus 1."
            // MINUS ONE.
            let run_count = (control_byte & 0x7f) + 1;
            let deltas: Vec<u16>;
            if points_are_words {
                deltas = c.de_counted(run_count.into())?;
            } else {
                let delta_bytes: Vec<u8> = c.de_counted(run_count.into())?;
                deltas = delta_bytes.iter().map(|x| *x as u16).collect();
            }
            res.extend(deltas);
        }
        let cumsum: Vec<u16> = res
            .iter()
            .scan(0, |acc, &x| {
                *acc += x;
                Some(*acc)
            })
            .collect();
        Ok(PackedPoints {
            points: Some(cumsum),
        })
    }
}

impl Serialize for PackedPoints {
    fn to_bytes(&self, data: &mut Vec<u8>) -> Result<(), SerializationError> {
        if self.points.is_none() {
            return data.put(0_u8);
        }
        let points = self.points.as_ref().unwrap();
        let num_points = points.len() as uint16;
        if num_points <= 0x80 {
            data.put(num_points as u8)?;
        } else {
            data.put(num_points | 0x8000)?;
        }

        let mut pos = 0;
        let mut last_value = 0;
        while pos < points.len() {
            let mut run: Vec<u8> = vec![0];
            let mut use_bytes: Option<bool> = None;
            while pos < points.len() && run.len() < 127 {
                let current = points[pos];
                let delta = current - last_value;
                if use_bytes.is_none() {
                    use_bytes = Some((0..=0xff).contains(&delta));
                }
                if use_bytes.unwrap() && !(0..=0xff).contains(&delta) {
                    break;
                }
                if use_bytes.unwrap() {
                    run.push(delta as u8);
                } else {
                    run.push((delta >> 8) as u8);
                    run.push((delta & 0xff) as u8);
                }
                last_value = current;
                pos += 1;
            }
            if use_bytes.unwrap() {
                run[0] = (run.len() as u8) - 2; // Don't count control byte
            } else {
                run[0] = (run.len() as u8 - 2) | 0x80;
            }
            data.put(run)?
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use crate::otvar::PackedPoints;

    #[test]
    fn test_packed_point_de() {
        let packed = vec![
            0x0b, 0x0a, 0x00, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x02, 0x02, 0x02,
        ];
        let expected = PackedPoints {
            points: Some(vec![0, 3, 4, 7, 8, 11, 12, 15, 17, 19, 21]),
        };
        let deserialized: PackedPoints = otspec::de::from_bytes(&packed).unwrap();
        assert_eq!(deserialized, expected);
    }

    #[test]
    fn test_packed_point_ser() {
        let expected = vec![
            0x0b, 0x0a, 0x00, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x02, 0x02, 0x02,
        ];
        let object = PackedPoints {
            points: Some(vec![0, 3, 4, 7, 8, 11, 12, 15, 17, 19, 21]),
        };
        let serialized = otspec::ser::to_bytes(&object).unwrap();
        assert_eq!(serialized, expected);
    }

    #[test]
    fn test_packed_point_zero_ser() {
        let expected = vec![0x01, 0x00, 0x00];
        let object = PackedPoints {
            points: Some(vec![0]),
        };
        let serialized = otspec::ser::to_bytes(&object).unwrap();
        assert_eq!(serialized, expected);
    }
}