fonttools 0.1.0

A library for reading, manipulating and writing OpenType font files
Documentation
use otspec::types::*;
use otspec::{deserialize_visitor, read_field};
use otspec_macros::tables;
use serde::de::SeqAccess;
use serde::de::Visitor;
use serde::ser::SerializeSeq;
use serde::Deserializer;
use serde::Serializer;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;

tables!(
    ClassDefFormat1 {
        uint16  startGlyphID
        Counted(uint16) classValueArray
    }
    ClassDefFormat2 {
        Counted(ClassRangeRecord) classRangeRecords
    }
    ClassRangeRecord {
        uint16  startGlyphID
        uint16  endGlyphID
        uint16  class
    }
);

#[derive(Debug, PartialEq, Clone)]
/// A class definition table.
///
/// Class definitions, used to define glyph contexts in the GSUB and GPOS tables,
/// map a glyph ID to a glyph class integer.
pub struct ClassDef {
    /// Stores the mapping between glyph IDs and classes.
    pub classes: BTreeMap<uint16, uint16>,
}

deserialize_visitor!(
    ClassDef,
    ClassDefVisitor,
    fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
        let format = read_field!(seq, uint16, "a class definition table format field");
        let mut classes = BTreeMap::new();
        if format == 1 {
            let cdf1 = read_field!(seq, ClassDefFormat1, "a class definition table format 1");
            for (ix, class) in cdf1.classValueArray.iter().enumerate() {
                classes.insert(cdf1.startGlyphID + ix as uint16, *class);
            }
        } else {
            let cdf2 = read_field!(seq, ClassDefFormat2, "a class definition table format 2");
            for rr in cdf2.classRangeRecords {
                for g in rr.startGlyphID..(rr.endGlyphID + 1) {
                    classes.insert(g, rr.class);
                }
            }
        }
        Ok(ClassDef { classes })
    }
);

fn consecutive_slices(data: &[(uint16, uint16)]) -> Vec<&[(uint16, uint16)]> {
    let mut slice_start = 0;
    let mut result = Vec::new();
    for i in 1..data.len() {
        if data[i - 1].0 + 1 != data[i].0 || data[i - 1].1 != data[i].1 {
            result.push(&data[slice_start..i]);
            slice_start = i;
        }
    }
    if !data.is_empty() {
        result.push(&data[slice_start..]);
    }
    result
}

impl Serialize for ClassDef {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut seq = serializer.serialize_seq(None)?;
        let pairs: Vec<(u16, u16)> = self.classes.iter().map(|(k, v)| (*k, *v)).collect();
        let as_consecutive = consecutive_slices(&pairs);
        if self.classes.is_empty() {
            seq.serialize_element::<uint16>(&1)?;
            seq.serialize_element(&ClassDefFormat1 {
                startGlyphID: 0,
                classValueArray: vec![],
            })?;
            return seq.end();
        }
        let first_gid = pairs[0].0;
        let last_gid = pairs.last().unwrap().0;
        if as_consecutive.len() as u16 * 3 > (2 + last_gid - first_gid) {
            seq.serialize_element::<uint16>(&1)?;
            seq.serialize_element(&ClassDefFormat1 {
                startGlyphID: first_gid,
                classValueArray: (first_gid..last_gid + 1)
                    .map(|gid| self.classes.get(&gid).map_or(0, |class| *class))
                    .collect(),
            })?;
        } else {
            seq.serialize_element::<uint16>(&2)?;
            seq.serialize_element(&(as_consecutive.len() as uint16))?;
            for slice in as_consecutive {
                seq.serialize_element(&ClassRangeRecord {
                    startGlyphID: slice.first().unwrap().0,
                    endGlyphID: slice.last().unwrap().0,
                    class: slice.first().unwrap().1,
                })?;
            }
        }
        seq.end()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::iter::FromIterator;

    macro_rules! btreemap {
            ($($k:expr => $v:expr),* $(,)?) => {
                std::collections::BTreeMap::<_, _>::from_iter(std::array::IntoIter::new([$(($k, $v),)*]))
            };
        }

    #[test]
    fn test_format2_deser() {
        let expected = ClassDef {
            classes: btreemap!(
            24 => 1, 25 => 1, 26 => 1, 27 => 1, 28 => 1, 29 => 2, 30 => 1,
            31 => 5, 32 => 5, 33 => 1, 34 => 3, 35 => 1, 36 => 1, 70 => 1,
            71 => 2, 72 => 2, 73 => 1, 74 => 1, 75 => 1, 76 => 2, 77 => 5,
            78 => 3, 79 => 3, 80 => 1, 81 => 1, 82 => 2, 83 => 1, 84 => 2),
        };
        let binary_classdef = vec![
            0x00, 0x02, 0x00, 0x11, 0x00, 0x18, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x1d, 0x00, 0x1d,
            0x00, 0x02, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x01, 0x00, 0x1f, 0x00, 0x20, 0x00, 0x05,
            0x00, 0x21, 0x00, 0x21, 0x00, 0x01, 0x00, 0x22, 0x00, 0x22, 0x00, 0x03, 0x00, 0x23,
            0x00, 0x24, 0x00, 0x01, 0x00, 0x46, 0x00, 0x46, 0x00, 0x01, 0x00, 0x47, 0x00, 0x48,
            0x00, 0x02, 0x00, 0x49, 0x00, 0x4b, 0x00, 0x01, 0x00, 0x4c, 0x00, 0x4c, 0x00, 0x02,
            0x00, 0x4d, 0x00, 0x4d, 0x00, 0x05, 0x00, 0x4e, 0x00, 0x4f, 0x00, 0x03, 0x00, 0x50,
            0x00, 0x51, 0x00, 0x01, 0x00, 0x52, 0x00, 0x52, 0x00, 0x02, 0x00, 0x53, 0x00, 0x53,
            0x00, 0x01, 0x00, 0x54, 0x00, 0x54, 0x00, 0x02,
        ];
        let deserialized: ClassDef = otspec::de::from_bytes(&binary_classdef).unwrap();
        assert_eq!(deserialized, expected);
        let serialized = otspec::ser::to_bytes(&deserialized).unwrap();
        assert_eq!(serialized, binary_classdef);
    }

    #[test]
    fn test_format1_deser() {
        let expected = ClassDef {
            classes: btreemap!(
 1 => 1,
 2 => 2,
 3 => 0,
 4 => 1,
 5 => 2,
 6 => 0,
 7 => 1,
 8 => 2,
 9 => 0,
 10 => 1,
 11 => 2,
 12 => 0,
 13 => 1,
 14 => 2),
        };
        let binary_classdef = vec![
            0x00, 0x01, 0x00, 0x01, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01,
            0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02,
            0x00, 0x00, 0x00, 0x01, 0x00, 0x02,
        ];
        let deserialized: ClassDef = otspec::de::from_bytes(&binary_classdef).unwrap();
        assert_eq!(deserialized, expected);
        let serialized = otspec::ser::to_bytes(&deserialized).unwrap();
        assert_eq!(serialized, binary_classdef);
    }
}