#![allow(clippy::cast_possible_truncation)]
use super::*;
use crate::{name, sname};
use CharCode::*;
use either::{Left, Right};
use std::vec;
use test_log::test;
use tinyvec::array_vec;
fn one(v: u8) -> CharCode {
One([v])
}
fn two(v: u16) -> CharCode {
Two([(v >> 8) as u8, v as u8])
}
fn three(v: u32) -> CharCode {
Three([(v >> 16) as u8, (v >> 8) as u8, v as u8])
}
fn four(v: u32) -> CharCode {
Four([(v >> 24) as u8, (v >> 16) as u8, (v >> 8) as u8, v as u8])
}
#[test]
fn char_code_as_byte_slice() {
assert_eq!(&[0x20][..], one(0x20).as_ref());
assert_eq!(&[0x81, 0x40][..], two(0x8140).as_ref());
assert_eq!(&[0x00, 0x81, 0x40][..], three(0x8140).as_ref());
assert_eq!(&[0xD8, 0x00, 0xDC, 0x00][..], four(0xD800DC00).as_ref());
}
#[test]
fn test_parse_cid_from_str_buf() {
assert_eq!(parse_cid_from_str_buf(&[0x0u8]), CID(0));
assert_eq!(parse_cid_from_str_buf(&[0x1u8]), CID(1));
assert_eq!(parse_cid_from_str_buf(&[0x10u8]), CID(16));
assert_eq!(parse_cid_from_str_buf(&[0x81, 0x40]), CID(0x8140));
}
#[test]
fn code_range_parse() {
assert_eq!(
CodeRange::from_str_buf(&[0x20], &[0x7e]).unwrap(),
CodeRange(array_vec!([ByteRange; 4] => ByteRange::new(0x20, 0x7e)))
);
assert_eq!(
CodeRange::from_str_buf(&[0x81, 0x40], &[0x81, 0x7e]).unwrap(),
CodeRange(array_vec!([ByteRange; 4] =>
ByteRange::new(0x81, 0x81),
ByteRange::new(0x40, 0x7e)
))
);
assert_eq!(
CodeRange::from_str_buf(&[0xD8, 0x00, 0xDC, 0x00], &[0xDB, 0xFF, 0xDF, 0xFF]).unwrap(),
CodeRange(array_vec!([ByteRange; 4] =>
ByteRange::new(0xD8, 0xDB),
ByteRange::new(0x00, 0xFF),
ByteRange::new(0xDC, 0xDF),
ByteRange::new(0x00, 0xFF),
)),
);
}
#[test]
fn code_range_in_range() {
let r = CodeRange::from_str_buf(&[0x20], &[0x7e]).unwrap();
assert!(r.in_range(one(0x20)));
assert!(r.in_range(one(0x7e)));
assert!(r.in_range(one(0x21)));
assert!(!r.in_range(one(0x1f)));
assert!(!r.in_range(one(0x7f)));
assert!(!r.in_range(two(0x7f)));
let r = CodeRange::from_str_buf(&[0x81, 0x40], &[0x81, 0x7e]).unwrap();
assert!(r.in_range(two(0x8140)));
assert!(r.in_range(two(0x817e)));
assert!(r.in_range(two(0x8141)));
assert!(!r.in_range(two(0x8040)));
assert!(!r.in_range(one(0x81)));
assert!(!r.in_range(three(0x8140)));
}
#[test]
fn code_range_next_code() {
let r = CodeRange::from_str_buf(&[0x20], &[0x7e]).unwrap();
assert_eq!(CodeSpaceResult::Matched(one(0x20)), r.next_code(&[0x20, 0]));
assert_eq!(CodeSpaceResult::Matched(one(0x7e)), r.next_code(&[0x7e]));
assert_eq!(CodeSpaceResult::Matched(one(0x21)), r.next_code(&[0x21]));
assert_eq!(CodeSpaceResult::NotMatched, r.next_code(&[0x1f]));
assert_eq!(CodeSpaceResult::NotMatched, r.next_code(&[0x7f]));
let r = CodeRange::from_str_buf(&[0x81, 0x40], &[0x81, 0x7e]).unwrap();
assert_eq!(
CodeSpaceResult::Matched(two(0x8140)),
r.next_code(&[0x81, 0x40])
);
assert_eq!(
CodeSpaceResult::Matched(two(0x817e)),
r.next_code(&[0x81, 0x7e])
);
assert_eq!(
CodeSpaceResult::Matched(two(0x8141)),
r.next_code(&[0x81, 0x41])
);
assert_eq!(CodeSpaceResult::NotMatched, r.next_code(&[0x80, 0x40]));
assert_eq!(
CodeSpaceResult::Partial(two(0x817f)),
r.next_code(&[0x81, 0x7f])
);
assert_eq!(CodeSpaceResult::Partial(one(0x81)), r.next_code(&[0x81]));
}
#[test]
fn code_space() {
let code_space = CodeSpace::new(vec![
CodeRange::from_str_buf(&[0x81, 0x40], &[0x81, 0x7e]).unwrap(),
CodeRange::from_str_buf(&[0xD8, 0x00, 0xDC, 0x00], &[0xDB, 0xFF, 0xDF, 0xFF]).unwrap(),
CodeRange::from_str_buf(&[0xE0, 0x00], &[0xFF, 0xFF]).unwrap(),
]);
assert_eq!(
(&[][..], Right(two(0x8141))),
code_space.next_code(&[0x81, 0x41]).unwrap()
);
assert_eq!(
(&[][..], Left(two(0x1f01))),
code_space.next_code(&[0x1f, 1]).unwrap()
);
assert_eq!(
(&[][..], Left(two(0x1f00))),
code_space.next_code(&[0x1f]).unwrap()
);
assert_eq!(
(&[][..], Right(four(0xD800DC00))),
code_space.next_code(&[0xD8, 0x00, 0xDC, 0x00]).unwrap()
);
assert_eq!(
(&[][..], Left(two(0x817f))),
code_space.next_code(&[0x81, 0x7f]).unwrap(),
);
}
#[test]
fn single_code_map() {
let m = SingleCodeMap::new(one(0x20), CID(0x1234));
assert_eq!(Some(CID(0x1234)), m.map(one(0x20)));
assert_eq!(None, m.map(one(0x21)));
}
#[test]
fn range_map_to_one() {
let m = RangeMapToOne {
range: CodeRange::from_str_buf(&[0x20], &[0x7e]).unwrap(),
cid: CID(0x1234),
};
assert_eq!(Some(CID(0x1234)), m.map(one(0x20)));
assert_eq!(Some(CID(0x1234)), m.map(one(0x7e)));
assert_eq!(None, m.map(one(0x12)));
assert_eq!(None, m.map(two(0x21)));
}
#[test]
fn inc_range_map() {
let m = IncRangeMap {
range: CodeRange::from_str_buf(&[0x20], &[0x7e]).unwrap(),
start_cid: CID(1234),
};
assert_eq!(Some(CID(1234)), m.map(one(0x20)));
assert_eq!(Some(CID(1235)), m.map(one(0x21)));
assert_eq!(Some(CID(1236)), m.map(one(0x22)));
assert_eq!(None, m.map(one(0x1f)));
assert_eq!(None, m.map(one(0x7f)));
let m = IncRangeMap {
range: CodeRange::from_str_buf(&[0x81, 0x00], &[0x82, 0x7f]).unwrap(),
start_cid: CID(1000),
};
assert_eq!(Some(CID(1000)), m.map(two(0x8100)));
assert_eq!(Some(CID(1001)), m.map(two(0x8101)));
assert_eq!(Some(CID(1128)), m.map(two(0x8200)));
assert_eq!(Some(CID(1255)), m.map(two(0x827f)));
}
#[test]
fn mapper() {
let mapper = Mapper {
ranges: vec![
IncRangeMap {
range: CodeRange::from_str_buf(&[0x20], &[0x7e]).unwrap(),
start_cid: CID(1234),
},
IncRangeMap {
range: CodeRange::from_str_buf(&[0x81, 0x00], &[0x82, 0x7f]).unwrap(),
start_cid: CID(1000),
},
]
.into(),
chars: vec![
SingleCodeMap::new(one(0x7f), CID(0x0000)),
SingleCodeMap::new(one(0x20), CID(0x1234)),
]
.into(),
};
assert_eq!(None, mapper.map(one(0x1f)));
assert_eq!(Some(CID(0x0000)), mapper.map(one(0x7f)));
assert_eq!(Some(CID(0x1234)), mapper.map(one(0x20)));
assert_eq!(Some(CID(1235)), mapper.map(one(0x21)));
assert_eq!(Some(CID(1002)), mapper.map(two(0x8102)));
}
#[test]
fn cmap() {
let code_space = CodeSpace::new(vec![
CodeRange::from_str_buf(&[0x20], &[0x7e]).unwrap(),
CodeRange::from_str_buf(&[0x81, 0x40], &[0x81, 0x7e]).unwrap(),
CodeRange::from_str_buf(&[0xD8, 0x00, 0xDC, 0x00], &[0xDB, 0xFF, 0xDF, 0xFF]).unwrap(),
CodeRange::from_str_buf(&[0xE0, 0x00], &[0xFF, 0xFF]).unwrap(),
]);
let cid_map = Mapper {
ranges: vec![].into(),
chars: vec![SingleCodeMap::new(two(0x8144), CID(0x1234))].into(),
};
let notdef_map = Mapper {
ranges: vec![].into(),
chars: vec![
SingleCodeMap::new(one(0x7f), CID(1)),
SingleCodeMap::new(one(0x0), CID(2)),
]
.into(),
};
let cmap = CMap {
cid_system_info: Default::default(),
w_mode: Default::default(),
name: sname("foo"),
code_space,
cid_map,
notdef_map,
use_map: None,
};
assert_eq!(
vec![
CID(0),
CID(2),
CID(0x1234),
CID(1),
CID(0),
],
cmap.map(&[1u8, 0, 0x81, 0x44, 0x7f, 0x81, 0x50]).unwrap()
);
}
#[test]
fn use_map() {
let base_code_space = CodeSpace::new(vec![CodeRange::from_str_buf(&[0x20], &[0x30]).unwrap()]);
let code_space = CodeSpace::new(vec![CodeRange::from_str_buf(&[0x25], &[0x40]).unwrap()]);
let base_cid_map = Mapper {
ranges: vec![].into(),
chars: vec![SingleCodeMap::new(one(0x30), CID(0x500))].into(),
};
let cid_map = Mapper {
ranges: vec![].into(),
chars: vec![SingleCodeMap::new(one(0x35), CID(0x501))].into(),
};
let base_notdef_map = Mapper {
ranges: vec![].into(),
chars: vec![SingleCodeMap::new(one(0x20), CID(1))].into(),
};
let notdef_map = Mapper {
ranges: vec![].into(),
chars: vec![
SingleCodeMap::new(one(0x30), CID(2)),
SingleCodeMap::new(one(0x36), CID(20)),
]
.into(),
};
let use_map = CMap {
cid_system_info: Default::default(),
w_mode: Default::default(),
name: sname("base"),
code_space: base_code_space,
cid_map: base_cid_map,
notdef_map: base_notdef_map,
use_map: None,
};
let cmap = CMap {
cid_system_info: Default::default(),
w_mode: Default::default(),
name: sname("foo"),
code_space,
cid_map,
notdef_map,
use_map: Some(Rc::new(use_map)),
};
assert_eq!(
vec![
CID(0x501),
CID(0x500),
CID(20),
CID(1),
CID(0),
],
cmap.map(&[0x35u8, 0x30, 0x36, 0x20, 0x7f]).unwrap()
);
}
#[test]
fn parse_cmap_file() {
let mut reg = CMapRegistry::new();
let cmap_data = include_bytes!("test-cmap.ps");
let cmap = reg.add_cmap_file(cmap_data).unwrap();
assert_eq!("Test-H", cmap.name.as_str());
assert_eq!(
CIDSystemInfo {
registry: "Testing".to_owned(),
ordering: "Test".to_owned(),
supplement: 3,
},
cmap.cid_system_info
);
assert_eq!(WriteMode::Horizontal, cmap.w_mode);
assert_eq!(
&CodeSpace::new(vec![
CodeRange::from_str_buf(&[0x00], &[0x80]).unwrap(),
CodeRange::from_str_buf(&[0x87, 0x40], &[0xfe, 0xfe]).unwrap(),
]),
&cmap.code_space,
);
assert_eq!(544, cmap.cid_map.chars.len());
assert_eq!(
SingleCodeMap::new(two(0x8943), CID(17718)),
cmap.cid_map.chars[0]
);
assert_eq!(
SingleCodeMap::new(two(0xfedd), CID(7080)),
cmap.cid_map.chars[543]
);
assert_eq!(665, cmap.cid_map.ranges.len());
assert_eq!(
IncRangeMap {
range: CodeRange::from_str_buf(&[0x20], &[0x7e]).unwrap(),
start_cid: CID(1),
},
cmap.cid_map.ranges[0]
);
assert_eq!(
IncRangeMap {
range: CodeRange::from_str_buf(&[0xfe, 0xef], &[0xfe, 0xfe]).unwrap(),
start_cid: CID(17144),
},
cmap.cid_map.ranges[664]
);
assert_eq!(2, cmap.notdef_map.ranges.len());
assert_eq!(
RangeMapToOne {
range: CodeRange::from_str_buf(&[0x00], &[0x0f]).unwrap(),
cid: CID(1),
},
cmap.notdef_map.ranges[0]
);
assert_eq!(
RangeMapToOne {
range: CodeRange::from_str_buf(&[0x10], &[0x1f]).unwrap(),
cid: CID(2),
},
cmap.notdef_map.ranges[1]
);
assert_eq!(2, cmap.notdef_map.chars.len());
assert_eq!(
SingleCodeMap::new(two(0x8940), CID(7717)),
cmap.notdef_map.chars[0]
);
assert_eq!(
SingleCodeMap::new(one(0x45), CID(17717)),
cmap.notdef_map.chars[1]
);
}
#[test]
fn parse_cmap_file_with_default_wmode() {
let mut reg = CMapRegistry::new();
let cmap_data = include_bytes!("default-wmode-cmap.ps");
let cmap = reg.add_cmap_file(cmap_data).unwrap();
assert_eq!(WriteMode::Horizontal, cmap.w_mode);
}
#[test]
fn parse_cmap_file_with_use() {
let mut reg = CMapRegistry::new();
let base_data = include_bytes!("test-cmap.ps");
let use_cmap_data = include_bytes!("use-cmap.ps");
let base = reg.add_cmap_file(base_data).unwrap();
let use_cmap = reg.add_cmap_file(use_cmap_data).unwrap();
assert_eq!(base, use_cmap.use_map.as_ref().unwrap().clone());
}
fn create_test_cmap_data(inner: &str) -> Vec<u8> {
format!(
r#"
/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CIDSystemInfo 3 dict dup begin
/Registry (Testing) def
/Ordering (Test) def
/Supplement 3 def
end def
/CMapName /Test-H def
/CMapVersion 2.009 def
/CMapType 1 def
/XUID [1 10 25582] def
/WMode 0 def
2 begincodespacerange
<00> <80>
<8740> <FEFE>
endcodespacerange
{inner}
endcmap
CMapName currentdict /CMap defineresource pop
end
end
"#
)
.into_bytes()
}
#[test]
fn parse_bf_char() {
let mut reg = CMapRegistry::new();
let cmap_data = create_test_cmap_data(
r#"
2 beginbfchar
<03> <00>
<04> <01>
endbfchar
"#,
);
let cmap = reg.add_cmap_file(&cmap_data).unwrap();
assert_eq!(2, cmap.cid_map.chars.len());
assert_eq!(SingleCodeMap::new(one(0x03), CID(0)), cmap.cid_map.chars[0]);
assert_eq!(SingleCodeMap::new(one(0x04), CID(1)), cmap.cid_map.chars[1]);
}
#[test]
fn parse_bf_range() {
let mut reg = CMapRegistry::new();
let cmap_data = create_test_cmap_data(
r#"
2 beginbfrange
<03> <05> <00>
<06> <08> <01>
endbfrange
"#,
);
let cmap = reg.add_cmap_file(&cmap_data).unwrap();
assert_eq!(2, cmap.cid_map.ranges.len());
assert_eq!(
IncRangeMap {
range: CodeRange::from_str_buf(&[0x03], &[0x05]).unwrap(),
start_cid: CID(0),
},
cmap.cid_map.ranges[0]
);
assert_eq!(
IncRangeMap {
range: CodeRange::from_str_buf(&[0x06], &[0x08]).unwrap(),
start_cid: CID(1),
},
cmap.cid_map.ranges[1]
);
}
#[test]
fn get_builtin_cmap() {
let reg = CMapRegistry::new();
let cmap = reg.get(&sname("ETen-B5-V")).unwrap().unwrap();
assert_eq!("ETen-B5-V", cmap.name.as_str());
assert_eq!(
"ETen-B5-H",
cmap.as_ref().use_map.as_ref().unwrap().name.as_str()
);
for &n in PREDEFINED_CMAPS.keys() {
let cmap = reg.get(&name(n)).unwrap().unwrap();
assert_eq!(n, cmap.name.as_str());
}
}
#[test]
fn identity_h_map() {
let reg = CMapRegistry::new();
let cmap = reg.get(&sname("Identity-H")).unwrap().unwrap();
assert_eq!(vec![CID(0x04)], cmap.map(&[0x0, 0x04u8]).unwrap());
}