556 lines
19 KiB
Rust
556 lines
19 KiB
Rust
mod format0 {
|
|
use ttf_parser::{cmap, GlyphId};
|
|
use crate::{convert, Unit::*};
|
|
|
|
#[test]
|
|
fn maps_not_all_256_codepoints() {
|
|
let mut data = convert(&[
|
|
UInt16(0), // format
|
|
UInt16(262), // subtable size
|
|
UInt16(0), // language ID
|
|
]);
|
|
|
|
// Map (only) codepoint 0x40 to 100.
|
|
data.extend(std::iter::repeat(0).take(256));
|
|
data[6 + 0x40] = 100;
|
|
|
|
let subtable = cmap::Subtable0::parse(&data).unwrap();
|
|
|
|
assert_eq!(subtable.glyph_index(0), None);
|
|
assert_eq!(subtable.glyph_index(0x40), Some(GlyphId(100)));
|
|
assert_eq!(subtable.glyph_index(100), None);
|
|
|
|
let mut vec = vec![];
|
|
subtable.codepoints(|c| vec.push(c));
|
|
assert_eq!(vec, [0x40]);
|
|
}
|
|
}
|
|
|
|
mod format2 {
|
|
use ttf_parser::{cmap, GlyphId};
|
|
use crate::{convert, Unit::*};
|
|
|
|
const U16_SIZE: usize = std::mem::size_of::<u16>();
|
|
|
|
#[test]
|
|
fn collect_codepoints() {
|
|
let mut data = convert(&[
|
|
UInt16(2), // format
|
|
UInt16(534), // subtable size
|
|
UInt16(0), // language ID
|
|
]);
|
|
|
|
// Make only high byte 0x28 multi-byte.
|
|
data.extend(std::iter::repeat(0x00).take(256 * U16_SIZE));
|
|
data[6 + 0x28 * U16_SIZE + 1] = 0x08;
|
|
|
|
data.extend(convert(&[
|
|
// First sub header (for single byte mapping)
|
|
UInt16(254), // first code
|
|
UInt16(2), // entry count
|
|
UInt16(0), // id delta: uninteresting
|
|
UInt16(0), // id range offset: uninteresting
|
|
// Second sub header (for high byte 0x28)
|
|
UInt16(16), // first code: (0x28 << 8) + 0x10 = 10256
|
|
UInt16(3), // entry count
|
|
UInt16(0), // id delta: uninteresting
|
|
UInt16(0), // id range offset: uninteresting
|
|
]));
|
|
|
|
// Now only glyph ID's would follow. Not interesting for codepoints.
|
|
|
|
let subtable = cmap::Subtable2::parse(&data).unwrap();
|
|
|
|
let mut vec = vec![];
|
|
subtable.codepoints(|c| vec.push(c));
|
|
assert_eq!(vec, [10256, 10257, 10258, 254, 255]);
|
|
}
|
|
|
|
#[test]
|
|
fn codepoint_at_range_end() {
|
|
let mut data = convert(&[
|
|
UInt16(2), // format
|
|
UInt16(532), // subtable size
|
|
UInt16(0), // language ID
|
|
]);
|
|
|
|
// Only single bytes.
|
|
data.extend(std::iter::repeat(0x00).take(256 * U16_SIZE));
|
|
data.extend(convert(&[
|
|
// First sub header (for single byte mapping)
|
|
UInt16(40), // first code
|
|
UInt16(2), // entry count
|
|
UInt16(0), // id delta
|
|
UInt16(2), // id range offset
|
|
// Glyph index
|
|
UInt16(100), // glyph ID [0]
|
|
UInt16(1000), // glyph ID [1]
|
|
UInt16(10000), // glyph ID [2] (unused)
|
|
]));
|
|
|
|
let subtable = cmap::Subtable2::parse(&data).unwrap();
|
|
assert_eq!(subtable.glyph_index(39), None);
|
|
assert_eq!(subtable.glyph_index(40), Some(GlyphId(100)));
|
|
assert_eq!(subtable.glyph_index(41), Some(GlyphId(1000)));
|
|
assert_eq!(subtable.glyph_index(42), None);
|
|
}
|
|
}
|
|
|
|
mod format4 {
|
|
use ttf_parser::{cmap, GlyphId};
|
|
use crate::{convert, Unit::*};
|
|
|
|
#[test]
|
|
fn single_glyph() {
|
|
let data = convert(&[
|
|
UInt16(4), // format
|
|
UInt16(32), // subtable size
|
|
UInt16(0), // language ID
|
|
UInt16(4), // 2 x segCount
|
|
UInt16(2), // search range
|
|
UInt16(0), // entry selector
|
|
UInt16(2), // range shift
|
|
// End character codes
|
|
UInt16(65), // char code [0]
|
|
UInt16(65535), // char code [1]
|
|
UInt16(0), // reserved
|
|
// Start character codes
|
|
UInt16(65), // char code [0]
|
|
UInt16(65535), // char code [1]
|
|
// Deltas
|
|
Int16(-64), // delta [0]
|
|
Int16(1), // delta [1]
|
|
// Offsets into Glyph index array
|
|
UInt16(0), // offset [0]
|
|
UInt16(0), // offset [1]
|
|
]);
|
|
|
|
let subtable = cmap::Subtable4::parse(&data).unwrap();
|
|
assert_eq!(subtable.glyph_index(0x41), Some(GlyphId(1)));
|
|
assert_eq!(subtable.glyph_index(0x42), None);
|
|
}
|
|
|
|
#[test]
|
|
fn continuous_range() {
|
|
let data = convert(&[
|
|
UInt16(4), // format
|
|
UInt16(32), // subtable size
|
|
UInt16(0), // language ID
|
|
UInt16(4), // 2 x segCount
|
|
UInt16(2), // search range
|
|
UInt16(0), // entry selector
|
|
UInt16(2), // range shift
|
|
// End character codes
|
|
UInt16(73), // char code [0]
|
|
UInt16(65535), // char code [1]
|
|
UInt16(0), // reserved
|
|
// Start character codes
|
|
UInt16(65), // char code [0]
|
|
UInt16(65535), // char code [1]
|
|
// Deltas
|
|
Int16(-64), // delta [0]
|
|
Int16(1), // delta [1]
|
|
// Offsets into Glyph index array
|
|
UInt16(0), // offset [0]
|
|
UInt16(0), // offset [1]
|
|
]);
|
|
|
|
let subtable = cmap::Subtable4::parse(&data).unwrap();
|
|
assert_eq!(subtable.glyph_index(0x40), None);
|
|
assert_eq!(subtable.glyph_index(0x41), Some(GlyphId(1)));
|
|
assert_eq!(subtable.glyph_index(0x42), Some(GlyphId(2)));
|
|
assert_eq!(subtable.glyph_index(0x43), Some(GlyphId(3)));
|
|
assert_eq!(subtable.glyph_index(0x44), Some(GlyphId(4)));
|
|
assert_eq!(subtable.glyph_index(0x45), Some(GlyphId(5)));
|
|
assert_eq!(subtable.glyph_index(0x46), Some(GlyphId(6)));
|
|
assert_eq!(subtable.glyph_index(0x47), Some(GlyphId(7)));
|
|
assert_eq!(subtable.glyph_index(0x48), Some(GlyphId(8)));
|
|
assert_eq!(subtable.glyph_index(0x49), Some(GlyphId(9)));
|
|
assert_eq!(subtable.glyph_index(0x4A), None);
|
|
}
|
|
|
|
#[test]
|
|
fn multiple_ranges() {
|
|
let data = convert(&[
|
|
UInt16(4), // format
|
|
UInt16(48), // subtable size
|
|
UInt16(0), // language ID
|
|
UInt16(8), // 2 x segCount
|
|
UInt16(4), // search range
|
|
UInt16(1), // entry selector
|
|
UInt16(4), // range shift
|
|
// End character codes
|
|
UInt16(65), // char code [0]
|
|
UInt16(69), // char code [1]
|
|
UInt16(73), // char code [2]
|
|
UInt16(65535), // char code [3]
|
|
UInt16(0), // reserved
|
|
// Start character codes
|
|
UInt16(65), // char code [0]
|
|
UInt16(67), // char code [1]
|
|
UInt16(71), // char code [2]
|
|
UInt16(65535), // char code [3]
|
|
// Deltas
|
|
Int16(-64), // delta [0]
|
|
Int16(-65), // delta [1]
|
|
Int16(-66), // delta [2]
|
|
Int16(1), // delta [3]
|
|
// Offsets into Glyph index array
|
|
UInt16(0), // offset [0]
|
|
UInt16(0), // offset [1]
|
|
UInt16(0), // offset [2]
|
|
UInt16(0), // offset [3]
|
|
]);
|
|
|
|
let subtable = cmap::Subtable4::parse(&data).unwrap();
|
|
assert_eq!(subtable.glyph_index(0x40), None);
|
|
assert_eq!(subtable.glyph_index(0x41), Some(GlyphId(1)));
|
|
assert_eq!(subtable.glyph_index(0x42), None);
|
|
assert_eq!(subtable.glyph_index(0x43), Some(GlyphId(2)));
|
|
assert_eq!(subtable.glyph_index(0x44), Some(GlyphId(3)));
|
|
assert_eq!(subtable.glyph_index(0x45), Some(GlyphId(4)));
|
|
assert_eq!(subtable.glyph_index(0x46), None);
|
|
assert_eq!(subtable.glyph_index(0x47), Some(GlyphId(5)));
|
|
assert_eq!(subtable.glyph_index(0x48), Some(GlyphId(6)));
|
|
assert_eq!(subtable.glyph_index(0x49), Some(GlyphId(7)));
|
|
assert_eq!(subtable.glyph_index(0x4A), None);
|
|
}
|
|
|
|
#[test]
|
|
fn unordered_ids() {
|
|
let data = convert(&[
|
|
UInt16(4), // format
|
|
UInt16(42), // subtable size
|
|
UInt16(0), // language ID
|
|
UInt16(4), // 2 x segCount
|
|
UInt16(2), // search range
|
|
UInt16(0), // entry selector
|
|
UInt16(2), // range shift
|
|
// End character codes
|
|
UInt16(69), // char code [0]
|
|
UInt16(65535), // char code [1]
|
|
UInt16(0), // reserved
|
|
// Start character codes
|
|
UInt16(65), // char code [0]
|
|
UInt16(65535), // char code [1]
|
|
// Deltas
|
|
Int16(0), // delta [0]
|
|
Int16(1), // delta [1]
|
|
// Offsets into Glyph index array
|
|
UInt16(4), // offset [0]
|
|
UInt16(0), // offset [1]
|
|
// Glyph index array
|
|
UInt16(1), // glyph ID [0]
|
|
UInt16(10), // glyph ID [1]
|
|
UInt16(100), // glyph ID [2]
|
|
UInt16(1000), // glyph ID [3]
|
|
UInt16(10000), // glyph ID [4]
|
|
]);
|
|
|
|
let subtable = cmap::Subtable4::parse(&data).unwrap();
|
|
assert_eq!(subtable.glyph_index(0x40), None);
|
|
assert_eq!(subtable.glyph_index(0x41), Some(GlyphId(1)));
|
|
assert_eq!(subtable.glyph_index(0x42), Some(GlyphId(10)));
|
|
assert_eq!(subtable.glyph_index(0x43), Some(GlyphId(100)));
|
|
assert_eq!(subtable.glyph_index(0x44), Some(GlyphId(1000)));
|
|
assert_eq!(subtable.glyph_index(0x45), Some(GlyphId(10000)));
|
|
assert_eq!(subtable.glyph_index(0x46), None);
|
|
}
|
|
|
|
#[test]
|
|
fn unordered_chars_and_ids() {
|
|
let data = convert(&[
|
|
UInt16(4), // format
|
|
UInt16(64), // subtable size
|
|
UInt16(0), // language ID
|
|
UInt16(12), // 2 x segCount
|
|
UInt16(8), // search range
|
|
UInt16(2), // entry selector
|
|
UInt16(4), // range shift
|
|
// End character codes
|
|
UInt16(80), // char code [0]
|
|
UInt16(256), // char code [1]
|
|
UInt16(336), // char code [2]
|
|
UInt16(512), // char code [3]
|
|
UInt16(592), // char code [4]
|
|
UInt16(65535), // char code [5]
|
|
UInt16(0), // reserved
|
|
// Start character codes
|
|
UInt16(80), // char code [0]
|
|
UInt16(256), // char code [1]
|
|
UInt16(336), // char code [2]
|
|
UInt16(512), // char code [3]
|
|
UInt16(592), // char code [4]
|
|
UInt16(65535), // char code [5]
|
|
// Deltas
|
|
Int16(-79), // delta [0]
|
|
Int16(-246), // delta [1]
|
|
Int16(-236), // delta [2]
|
|
Int16(488), // delta [3]
|
|
Int16(9408), // delta [4]
|
|
Int16(1), // delta [5]
|
|
// Offsets into Glyph index array
|
|
UInt16(0), // offset [0]
|
|
UInt16(0), // offset [1]
|
|
UInt16(0), // offset [2]
|
|
UInt16(0), // offset [3]
|
|
UInt16(0), // offset [4]
|
|
UInt16(0), // offset [5]
|
|
]);
|
|
|
|
let subtable = cmap::Subtable4::parse(&data).unwrap();
|
|
assert_eq!(subtable.glyph_index(0x40), None);
|
|
assert_eq!(subtable.glyph_index(0x50), Some(GlyphId(1)));
|
|
assert_eq!(subtable.glyph_index(0x100), Some(GlyphId(10)));
|
|
assert_eq!(subtable.glyph_index(0x150), Some(GlyphId(100)));
|
|
assert_eq!(subtable.glyph_index(0x200), Some(GlyphId(1000)));
|
|
assert_eq!(subtable.glyph_index(0x250), Some(GlyphId(10000)));
|
|
assert_eq!(subtable.glyph_index(0x300), None);
|
|
}
|
|
|
|
#[test]
|
|
fn no_end_codes() {
|
|
let data = convert(&[
|
|
UInt16(4), // format
|
|
UInt16(28), // subtable size
|
|
UInt16(0), // language ID
|
|
UInt16(4), // 2 x segCount
|
|
UInt16(2), // search range
|
|
UInt16(0), // entry selector
|
|
UInt16(2), // range shift
|
|
// End character codes
|
|
UInt16(73), // char code [0]
|
|
// 0xFF, 0xFF, // char code [1] <-- removed
|
|
UInt16(0), // reserved
|
|
// Start character codes
|
|
UInt16(65), // char code [0]
|
|
// 0xFF, 0xFF, // char code [1] <-- removed
|
|
// Deltas
|
|
Int16(-64), // delta [0]
|
|
Int16(1), // delta [1]
|
|
// Offsets into Glyph index array
|
|
UInt16(0), // offset [0]
|
|
UInt16(0), // offset [1]
|
|
]);
|
|
|
|
assert!(cmap::Subtable4::parse(&data).is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn invalid_segment_count() {
|
|
let data = convert(&[
|
|
UInt16(4), // format
|
|
UInt16(32), // subtable size
|
|
UInt16(0), // language ID
|
|
UInt16(1), // 2 x segCount <-- must be more than 1
|
|
UInt16(2), // search range
|
|
UInt16(0), // entry selector
|
|
UInt16(2), // range shift
|
|
// End character codes
|
|
UInt16(65), // char code [0]
|
|
UInt16(65535), // char code [1]
|
|
UInt16(0), // reserved
|
|
// Start character codes
|
|
UInt16(65), // char code [0]
|
|
UInt16(65535), // char code [1]
|
|
// Deltas
|
|
Int16(-64), // delta [0]
|
|
Int16(1), // delta [1]
|
|
// Offsets into Glyph index array
|
|
UInt16(0), // offset [0]
|
|
UInt16(0), // offset [1]
|
|
]);
|
|
|
|
assert!(cmap::Subtable4::parse(&data).is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn only_end_segments() {
|
|
let data = convert(&[
|
|
UInt16(4), // format
|
|
UInt16(32), // subtable size
|
|
UInt16(0), // language ID
|
|
UInt16(2), // 2 x segCount
|
|
UInt16(2), // search range
|
|
UInt16(0), // entry selector
|
|
UInt16(2), // range shift
|
|
// End character codes
|
|
UInt16(65535), // char code [1]
|
|
UInt16(0), // reserved
|
|
// Start character codes
|
|
UInt16(65535), // char code [1]
|
|
// Deltas
|
|
Int16(-64), // delta [0]
|
|
Int16(1), // delta [1]
|
|
// Offsets into Glyph index array
|
|
UInt16(0), // offset [0]
|
|
UInt16(0), // offset [1]
|
|
]);
|
|
|
|
let subtable = cmap::Subtable4::parse(&data).unwrap();
|
|
// Should not loop forever.
|
|
assert_eq!(subtable.glyph_index(0x41), None);
|
|
}
|
|
|
|
#[test]
|
|
fn invalid_length() {
|
|
let data = convert(&[
|
|
UInt16(4), // format
|
|
UInt16(16), // subtable size <-- the size should be 32, but we don't check it anyway
|
|
UInt16(0), // language ID
|
|
UInt16(4), // 2 x segCount
|
|
UInt16(2), // search range
|
|
UInt16(0), // entry selector
|
|
UInt16(2), // range shift
|
|
// End character codes
|
|
UInt16(65), // char code [0]
|
|
UInt16(65535), // char code [1]
|
|
UInt16(0), // reserved
|
|
// Start character codes
|
|
UInt16(65), // char code [0]
|
|
UInt16(65535), // char code [1]
|
|
// Deltas
|
|
Int16(-64), // delta [0]
|
|
Int16(1), // delta [1]
|
|
// Offsets into Glyph index array
|
|
UInt16(0), // offset [0]
|
|
UInt16(0), // offset [1]
|
|
]);
|
|
|
|
let subtable = cmap::Subtable4::parse(&data).unwrap();
|
|
assert_eq!(subtable.glyph_index(0x41), Some(GlyphId(1)));
|
|
assert_eq!(subtable.glyph_index(0x42), None);
|
|
}
|
|
|
|
#[test]
|
|
fn codepoint_out_of_range() {
|
|
let data = convert(&[
|
|
UInt16(4), // format
|
|
UInt16(32), // subtable size
|
|
UInt16(0), // language ID
|
|
UInt16(4), // 2 x segCount
|
|
UInt16(2), // search range
|
|
UInt16(0), // entry selector
|
|
UInt16(2), // range shift
|
|
// End character codes
|
|
UInt16(65), // char code [0]
|
|
UInt16(65535), // char code [1]
|
|
UInt16(0), // reserved
|
|
// Start character codes
|
|
UInt16(65), // char code [0]
|
|
UInt16(65535), // char code [1]
|
|
// Deltas
|
|
Int16(-64), // delta [0]
|
|
Int16(1), // delta [1]
|
|
// Offsets into Glyph index array
|
|
UInt16(0), // offset [0]
|
|
UInt16(0), // offset [1]
|
|
]);
|
|
|
|
let subtable = cmap::Subtable4::parse(&data).unwrap();
|
|
// Format 4 support only u16 codepoints, so we have to bail immediately otherwise.
|
|
assert_eq!(subtable.glyph_index(0x1FFFF), None);
|
|
}
|
|
|
|
#[test]
|
|
fn zero() {
|
|
let data = convert(&[
|
|
UInt16(4), // format
|
|
UInt16(42), // subtable size
|
|
UInt16(0), // language ID
|
|
UInt16(4), // 2 x segCount
|
|
UInt16(2), // search range
|
|
UInt16(0), // entry selector
|
|
UInt16(2), // range shift
|
|
// End character codes
|
|
UInt16(69), // char code [0]
|
|
UInt16(65535), // char code [1]
|
|
UInt16(0), // reserved
|
|
// Start character codes
|
|
UInt16(65), // char code [0]
|
|
UInt16(65535), // char code [1]
|
|
// Deltas
|
|
Int16(0), // delta [0]
|
|
Int16(1), // delta [1]
|
|
// Offsets into Glyph index array
|
|
UInt16(4), // offset [0]
|
|
UInt16(0), // offset [1]
|
|
// Glyph index array
|
|
UInt16(0), // glyph ID [0] <-- indicates missing glyph
|
|
UInt16(10), // glyph ID [1]
|
|
UInt16(100), // glyph ID [2]
|
|
UInt16(1000), // glyph ID [3]
|
|
UInt16(10000), // glyph ID [4]
|
|
]);
|
|
|
|
let subtable = cmap::Subtable4::parse(&data).unwrap();
|
|
assert_eq!(subtable.glyph_index(0x41), None);
|
|
}
|
|
|
|
#[test]
|
|
fn invalid_offset() {
|
|
let data = convert(&[
|
|
UInt16(4), // format
|
|
UInt16(42), // subtable size
|
|
UInt16(0), // language ID
|
|
UInt16(4), // 2 x segCount
|
|
UInt16(2), // search range
|
|
UInt16(0), // entry selector
|
|
UInt16(2), // range shift
|
|
// End character codes
|
|
UInt16(69), // char code [0]
|
|
UInt16(65535), // char code [1]
|
|
UInt16(0), // reserved
|
|
// Start character codes
|
|
UInt16(65), // char code [0]
|
|
UInt16(65535), // char code [1]
|
|
// Deltas
|
|
Int16(0), // delta [0]
|
|
Int16(1), // delta [1]
|
|
// Offsets into Glyph index array
|
|
UInt16(4), // offset [0]
|
|
UInt16(65535), // offset [1]
|
|
// Glyph index array
|
|
UInt16(1), // glyph ID [0]
|
|
]);
|
|
|
|
let subtable = cmap::Subtable4::parse(&data).unwrap();
|
|
assert_eq!(subtable.glyph_index(65535), None);
|
|
}
|
|
|
|
#[test]
|
|
fn collect_codepoints() {
|
|
let data = convert(&[
|
|
UInt16(4), // format
|
|
UInt16(24), // subtable size
|
|
UInt16(0), // language ID
|
|
UInt16(4), // 2 x segCount
|
|
UInt16(2), // search range
|
|
UInt16(0), // entry selector
|
|
UInt16(2), // range shift
|
|
// End character codes
|
|
UInt16(34), // char code [0]
|
|
UInt16(65535), // char code [1]
|
|
UInt16(0), // reserved
|
|
// Start character codes
|
|
UInt16(27), // char code [0]
|
|
UInt16(65533), // char code [1]
|
|
// Deltas
|
|
Int16(0), // delta [0]
|
|
Int16(1), // delta [1]
|
|
// Offsets into Glyph index array
|
|
UInt16(4), // offset [0]
|
|
UInt16(0), // offset [1]
|
|
// Glyph index array
|
|
UInt16(0), // glyph ID [0]
|
|
UInt16(10), // glyph ID [1]
|
|
]);
|
|
|
|
let subtable = cmap::Subtable4::parse(&data).unwrap();
|
|
|
|
let mut vec = vec![];
|
|
subtable.codepoints(|c| vec.push(c));
|
|
assert_eq!(vec, [27, 28, 29, 30, 31, 32, 33, 34, 65533, 65534, 65535]);
|
|
}
|
|
}
|