sfnt 0.7.0

A zero-allocation SFNT parser.
Documentation
// Copyright 2018 Kyle Mayes
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate sfnt;

use std::fs::{File};
use std::io::{Read};
use std::path::{Path};

use sfnt::{Fixed16_16, Sfnt, checksum};

macro_rules! assert_record_eq {
    ($record:expr, $tag:expr, $checksum:expr, $offset:expr, $length:expr) => {
        let record = $record;
        assert_eq!(record.tag, $tag);
        assert_eq!(record.checksum, $checksum);
        assert_eq!(record.offset, $offset);
        assert_eq!(record.length, $length);
    };
}

macro_rules! assert_find_eq {
    ($sfnt:expr, $tag:expr, $checksum:expr, $offset:expr, $length:expr) => {
        let (record, bytes) = $sfnt.find($tag).unwrap();
        assert_record_eq!(record, $tag, $checksum, $offset, $length);
        let start = record.offset as usize;
        let end = start + record.length as usize;
        assert_eq!(bytes, &$sfnt.bytes[start..end])
    };
}

fn with_bytes<P: AsRef<Path>, F: Fn(&[u8])>(path: P, f: F) {
    let mut file = File::open(path).unwrap();
    let mut bytes = vec![];
    file.read_to_end(&mut bytes).unwrap();
    f(&bytes);
}

#[test]
fn test_parse_sfnt() {
    with_bytes("tests/resources/OpenSans-Italic.ttf", |bytes| {
        let sfnt = Sfnt::parse(bytes).unwrap();

        assert_eq!(sfnt.bytes, bytes);

        assert_eq!(sfnt.header.version, Fixed16_16(65536));
        assert_eq!(sfnt.header.num_tables, 19);
        assert_eq!(sfnt.header.search_range, 256);
        assert_eq!(sfnt.header.entry_selector, 4);
        assert_eq!(sfnt.header.range_shift, 48);

        let records = sfnt.records.iter().collect::<Vec<_>>();
        assert_eq!(records.len(), sfnt.header.num_tables as usize);
        assert_record_eq!(records[0], "DSIG", 0x5A77FD40, 207404, 5492);
        assert_record_eq!(records[1], "GDEF", 0x002603AF, 206348, 30);
        assert_record_eq!(records[2], "GPOS", 0x0B370F37, 206380, 56);
        assert_record_eq!(records[3], "GSUB", 0x0E2B3DB7, 206436, 966);
        assert_record_eq!(records[4], "OS/2", 0xA14D963D, 440, 96);
        assert_record_eq!(records[5], "cmap", 0x29AB2F68, 4276, 1050);
        assert_record_eq!(records[6], "cvt ", 0x0EC01719, 7568, 160);
        assert_record_eq!(records[7], "fpgm", 0x7E61B611, 5328, 1972);
        assert_record_eq!(records[8], "gasp", 0x00150023, 206332, 16);
        assert_record_eq!(records[9], "glyf", 0x5058F254, 9608, 73246);
        assert_record_eq!(records[10], "head", 0xF847F963, 316, 54);
        assert_record_eq!(records[11], "hhea", 0x102010E0, 372, 36);
        assert_record_eq!(records[12], "hmtx", 0xD907CDE3, 536, 3738);
        assert_record_eq!(records[13], "kern", 0x542B097E, 82856, 112182);
        assert_record_eq!(records[14], "loca", 0x4D8505A9, 7728, 1878);
        assert_record_eq!(records[15], "maxp", 0x054301A0, 408, 32);
        assert_record_eq!(records[16], "name", 0x537422CE, 195040, 1518);
        assert_record_eq!(records[17], "post", 0x0237EF6C, 196560, 9771);
        assert_record_eq!(records[18], "prep", 0x54819693, 7300, 265);
    });
}

#[test]
fn test_find() {
    with_bytes("tests/resources/OpenSans-Italic.ttf", |bytes| {
        let sfnt = Sfnt::parse(bytes).unwrap();

        assert_eq!(sfnt.find("AAAA"), None);
        assert_find_eq!(sfnt, "DSIG", 0x5A77FD40, 207404, 5492);
        assert_find_eq!(sfnt, "GDEF", 0x002603AF, 206348, 30);
        assert_find_eq!(sfnt, "GPOS", 0x0B370F37, 206380, 56);
        assert_find_eq!(sfnt, "GSUB", 0x0E2B3DB7, 206436, 966);
        assert_find_eq!(sfnt, "OS/2", 0xA14D963D, 440, 96);
        assert_find_eq!(sfnt, "cmap", 0x29AB2F68, 4276, 1050);
        assert_find_eq!(sfnt, "cvt ", 0x0EC01719, 7568, 160);
        assert_find_eq!(sfnt, "fpgm", 0x7E61B611, 5328, 1972);
        assert_find_eq!(sfnt, "gasp", 0x00150023, 206332, 16);
        assert_find_eq!(sfnt, "glyf", 0x5058F254, 9608, 73246);
        assert_find_eq!(sfnt, "head", 0xF847F963, 316, 54);
        assert_find_eq!(sfnt, "hhea", 0x102010E0, 372, 36);
        assert_find_eq!(sfnt, "hmtx", 0xD907CDE3, 536, 3738);
        assert_find_eq!(sfnt, "kern", 0x542B097E, 82856, 112182);
        assert_find_eq!(sfnt, "loca", 0x4D8505A9, 7728, 1878);
        assert_find_eq!(sfnt, "maxp", 0x054301A0, 408, 32);
        assert_find_eq!(sfnt, "name", 0x537422CE, 195040, 1518);
        assert_find_eq!(sfnt, "post", 0x0237EF6C, 196560, 9771);
        assert_find_eq!(sfnt, "prep", 0x54819693, 7300, 265);
        assert_eq!(sfnt.find("ZZZZ"), None);
    });
}

#[test]
fn test_checksum() {
    with_bytes("tests/resources/OpenSans-Italic.ttf", |bytes| {
        let sfnt = Sfnt::parse(bytes).unwrap();

        for (record, bytes) in sfnt.iter() {
            if record.tag != "head" {
                assert_eq!(checksum(bytes), record.checksum);
            }
        }
    });
}