mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-24 10:09:32 -06:00
This commit is contained in:
+11
-7
@@ -263,8 +263,8 @@ fn parse_cmap(data: &[u8]) -> CMap {
|
|||||||
}
|
}
|
||||||
Obj::Array(list) => {
|
Obj::Array(list) => {
|
||||||
for (k, s) in list.iter().enumerate() {
|
for (k, s) in list.iter().enumerate() {
|
||||||
if let Obj::Str(s) = s {
|
if let (Obj::Str(s), Some(c)) = (s, lo.checked_add(k as u32)) {
|
||||||
m.unicode.insert(lo + k as u32, utf16(s));
|
m.unicode.insert(c, utf16(s));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -401,13 +401,15 @@ impl Font {
|
|||||||
match w.get(i + 1).map(|x| file.resolve(x)) {
|
match w.get(i + 1).map(|x| file.resolve(x)) {
|
||||||
Some(Obj::Array(list)) => {
|
Some(Obj::Array(list)) => {
|
||||||
for (k, v) in list.iter().enumerate() {
|
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;
|
i += 2;
|
||||||
}
|
}
|
||||||
Some(Obj::Num(last)) => {
|
Some(Obj::Num(last)) => {
|
||||||
let wv = w.get(i + 2).and_then(|x| file.resolve(x).num()).unwrap_or(0.0) / 1000.0;
|
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);
|
f.cid_widths.insert(c, wv);
|
||||||
}
|
}
|
||||||
i += 3;
|
i += 3;
|
||||||
@@ -432,13 +434,15 @@ impl Font {
|
|||||||
Some(Obj::Array(list)) => {
|
Some(Obj::Array(list)) => {
|
||||||
let v: Vec<f64> = list.iter().filter_map(|x| file.resolve(x).num()).collect();
|
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() {
|
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;
|
i += 2;
|
||||||
}
|
}
|
||||||
Some(Obj::Num(last)) => {
|
Some(Obj::Num(last)) => {
|
||||||
let (Some(w1), Some(vx), Some(vy)) = (num(i + 2), num(i + 3), num(i + 4)) else { break };
|
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));
|
f.cid_vmetrics.insert(c, (w1 / 1000.0, vx / 1000.0, vy / 1000.0));
|
||||||
}
|
}
|
||||||
i += 5;
|
i += 5;
|
||||||
@@ -609,7 +613,7 @@ impl Font {
|
|||||||
if self.cid_ranges.is_empty() {
|
if self.cid_ranges.is_empty() {
|
||||||
return code;
|
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
|
/// 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!(cropped.pixel_size(), (120, 80), "unknown bounds keep the full page");
|
||||||
assert_eq!(offset, [0.0, 0.0]);
|
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