mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-24 01:57:53 -06:00
This commit is contained in:
+11
-7
@@ -263,8 +263,8 @@ fn parse_cmap(data: &[u8]) -> CMap {
|
||||
}
|
||||
Obj::Array(list) => {
|
||||
for (k, s) in list.iter().enumerate() {
|
||||
if let Obj::Str(s) = s {
|
||||
m.unicode.insert(lo + k as u32, utf16(s));
|
||||
if let (Obj::Str(s), Some(c)) = (s, lo.checked_add(k as u32)) {
|
||||
m.unicode.insert(c, utf16(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -401,13 +401,15 @@ impl Font {
|
||||
match w.get(i + 1).map(|x| file.resolve(x)) {
|
||||
Some(Obj::Array(list)) => {
|
||||
for (k, v) in list.iter().enumerate() {
|
||||
f.cid_widths.insert(first + k as u32, file.resolve(v).num().unwrap_or(0.0) / 1000.0);
|
||||
if let Some(cid) = first.checked_add(k as u32) {
|
||||
f.cid_widths.insert(cid, file.resolve(v).num().unwrap_or(0.0) / 1000.0);
|
||||
}
|
||||
}
|
||||
i += 2;
|
||||
}
|
||||
Some(Obj::Num(last)) => {
|
||||
let wv = w.get(i + 2).and_then(|x| file.resolve(x).num()).unwrap_or(0.0) / 1000.0;
|
||||
for c in first..=(*last as u32).min(first + 65535) {
|
||||
for c in first..=(*last as u32).min(first.saturating_add(65535)) {
|
||||
f.cid_widths.insert(c, wv);
|
||||
}
|
||||
i += 3;
|
||||
@@ -432,13 +434,15 @@ impl Font {
|
||||
Some(Obj::Array(list)) => {
|
||||
let v: Vec<f64> = list.iter().filter_map(|x| file.resolve(x).num()).collect();
|
||||
for (k, t) in v.as_chunks::<3>().0.iter().enumerate() {
|
||||
f.cid_vmetrics.insert(first + k as u32, (t[0] / 1000.0, t[1] / 1000.0, t[2] / 1000.0));
|
||||
if let Some(cid) = first.checked_add(k as u32) {
|
||||
f.cid_vmetrics.insert(cid, (t[0] / 1000.0, t[1] / 1000.0, t[2] / 1000.0));
|
||||
}
|
||||
}
|
||||
i += 2;
|
||||
}
|
||||
Some(Obj::Num(last)) => {
|
||||
let (Some(w1), Some(vx), Some(vy)) = (num(i + 2), num(i + 3), num(i + 4)) else { break };
|
||||
for c in first..=(last.max(0.0) as u32).min(first + 65535) {
|
||||
for c in first..=(last.max(0.0) as u32).min(first.saturating_add(65535)) {
|
||||
f.cid_vmetrics.insert(c, (w1 / 1000.0, vx / 1000.0, vy / 1000.0));
|
||||
}
|
||||
i += 5;
|
||||
@@ -609,7 +613,7 @@ impl Font {
|
||||
if self.cid_ranges.is_empty() {
|
||||
return code;
|
||||
}
|
||||
self.cid_ranges.iter().find(|(lo, hi, _)| code >= *lo && code <= *hi).map(|(lo, _, c)| c + (code - lo)).unwrap_or(0)
|
||||
self.cid_ranges.iter().find(|(lo, hi, _)| code >= *lo && code <= *hi).and_then(|(lo, _, c)| c.checked_add(code - lo)).unwrap_or(0)
|
||||
}
|
||||
|
||||
/// Vertical metrics of a code in text space (before the font size): the vertical
|
||||
|
||||
@@ -1090,3 +1090,47 @@ fn retained_layers_preserve_artwork_beyond_the_bounds_depth_limit() {
|
||||
assert_eq!(cropped.pixel_size(), (120, 80), "unknown bounds keep the full page");
|
||||
assert_eq!(offset, [0.0, 0.0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cid_width_entries_at_the_top_of_the_cid_range_do_not_overflow() {
|
||||
// A first CID of u32::MAX (larger values saturate to it) used to overflow `first + k` and `first + 65535`:
|
||||
// a panic in debug builds; in release builds the list wrapped round to CID 0 and the range was dropped.
|
||||
let load = |entry: &str| {
|
||||
let type0 = "<< /Type /Font /Subtype /Type0 /BaseFont /X /Encoding /Identity-H /DescendantFonts [13 0 R] >>";
|
||||
let desc = format!("<< /Type /Font /Subtype /CIDFontType2 {entry} >>");
|
||||
let file = object::File::parse(&page_pdf("", "", vec![(10, type0.into(), None), (13, desc, None)])).unwrap();
|
||||
let d = file.objects.get(&(10, 0)).and_then(object::Obj::dict).unwrap();
|
||||
font::Font::load(&file, d, &mut vec![])
|
||||
};
|
||||
let max = u32::MAX;
|
||||
// c [w1 w2 ...]
|
||||
let f = load("/W [4294967295 [500 600 700]]");
|
||||
assert_eq!((f.width(max), f.width(0)), (0.5, 1.0));
|
||||
// cfirst clast w
|
||||
assert_eq!(load("/W [4294967295 4294967295 400]").width(max), 0.4);
|
||||
// c [w1y vx vy ...]
|
||||
let f = load("/W2 [4294967295 [1 2 3 4 5 6]]");
|
||||
assert_eq!((f.vmetrics(max), f.vmetrics(0)), ((0.001, 0.002, 0.003), (-1.0, 0.5, 0.88)));
|
||||
// cfirst clast w1y vx vy
|
||||
assert_eq!(load("/W2 [4294967295 4294967295 1 2 3]").vmetrics(max), (0.001, 0.002, 0.003));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cmap_entries_at_the_top_of_the_code_range_do_not_overflow() {
|
||||
// A bfrange array starting at code 0xFFFFFFFF and a cidrange mapping onto CID u32::MAX used to overflow
|
||||
// `lo + k` and `c + (code - lo)`: a panic in debug builds, a wrap round to code / CID 0 in release builds.
|
||||
let to_unicode = "beginbfrange <FFFFFFFF> <FFFFFFFF> [<0041> <0042>] endbfrange";
|
||||
let encoding = "begincodespacerange <0000> <FFFF> endcodespacerange begincidrange <0000> <FFFF> 4294967295 endcidrange";
|
||||
let objs: Objs = vec![
|
||||
(10, "<< /Type /Font /Subtype /Type0 /Encoding 15 0 R /ToUnicode 14 0 R /DescendantFonts [13 0 R] >>".into(), None),
|
||||
(13, "<< /Type /Font /Subtype /CIDFontType2 /W [0 [700 800] 4294967295 [500]] >>".into(), None),
|
||||
(14, "<< >>".into(), Some(to_unicode.as_bytes().to_vec())),
|
||||
(15, "<< /Type /CMap >>".into(), Some(encoding.as_bytes().to_vec())),
|
||||
];
|
||||
let file = object::File::parse(&page_pdf("", "", objs)).unwrap();
|
||||
let d = file.objects.get(&(10, 0)).and_then(object::Obj::dict).unwrap();
|
||||
let f = font::Font::load(&file, d, &mut vec![]);
|
||||
assert_eq!((f.unicode(0xFFFF_FFFF), f.unicode(0)), ("A".to_string(), String::new()));
|
||||
// Code 0 is CID u32::MAX; code 2 would be past it, so it falls back to CID 0 like an unmapped code.
|
||||
assert_eq!((f.width(0), f.width(2)), (0.5, 0.7));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user