From 51b910657d0acf12e0ccb825d6d5989ddefa21f0 Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Wed, 28 Oct 2026 06:09:21 +0000 Subject: [PATCH] Refactor pdf for strict Clippy checks --- crates/pdf/src/font.rs | 234 +++++++++++---------- crates/pdf/src/ps/builtin.rs | 396 ++++++++++++++++++++++------------- crates/pdf/src/ps/eps.rs | 2 +- crates/pdf/src/ps/exec.rs | 2 +- crates/pdf/src/ps/paths.rs | 2 +- crates/pdf/src/ps/scanner.rs | 2 +- crates/pdf/src/ps/text.rs | 2 +- crates/pdf/src/ps/value.rs | 2 +- 8 files changed, 386 insertions(+), 256 deletions(-) diff --git a/crates/pdf/src/font.rs b/crates/pdf/src/font.rs index 2c27838a2..64b0a1c3a 100644 --- a/crates/pdf/src/font.rs +++ b/crates/pdf/src/font.rs @@ -89,7 +89,7 @@ impl OutlinePen for Pen { /// A bundled stand-in for a font that is not embedded, by its PostScript name. fn fallback(base: &str) -> &'static [u8] { - use effectcraft_text::fonts::*; + use effectcraft_text::fonts::{INTER_BOLD, INTER_ITALIC, INTER_REGULAR, INTER_SEMIBOLD, JETBRAINS_MONO_REGULAR, NOTO_SERIF_REGULAR}; let b = base.to_ascii_lowercase(); if b.contains("courier") || b.contains("mono") || b.contains("consol") { return JETBRAINS_MONO_REGULAR; @@ -393,89 +393,10 @@ impl Font { } // Widths. if f.composite { - f.default_width = file.get_num(&desc_dict, "DW").unwrap_or(1000.0) / 1000.0; - if let Some(w) = file.get(&desc_dict, "W").and_then(Obj::array) { - let mut i = 0; - while i < w.len() { - let first = file.resolve(&w[i]).num().unwrap_or(0.0) as u32; - match w.get(i + 1).map(|x| file.resolve(x)) { - Some(Obj::Array(list)) => { - for (k, v) in list.iter().enumerate() { - 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.saturating_add(65535)) { - f.cid_widths.insert(c, wv); - } - i += 3; - } - _ => break, - } - } - } - // Vertical metrics (§9.7.4.3): /DW2 [vy w1y] and /W2 entries - // `c [w1y vx vy …]` or `cfirst clast w1y vx vy`. - let dw2 = file.get(&desc_dict, "DW2").map(|x| file.nums(x)).unwrap_or_default(); - if dw2.len() == 2 { - f.dw2 = (dw2[0] / 1000.0, dw2[1] / 1000.0); - } - if let Some(w) = file.get(&desc_dict, "W2").and_then(Obj::array) { - let num = |i: usize| w.get(i).and_then(|x| file.resolve(x).num()); - let mut i = 0; - while i < w.len() { - let Some(first) = num(i) else { break }; - let first = first.max(0.0) as u32; - match w.get(i + 1).map(|x| file.resolve(x)) { - Some(Obj::Array(list)) => { - let v: Vec = list.iter().filter_map(|x| file.resolve(x).num()).collect(); - for (k, t) in v.as_chunks::<3>().0.iter().enumerate() { - 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.saturating_add(65535)) { - f.cid_vmetrics.insert(c, (w1 / 1000.0, vx / 1000.0, vy / 1000.0)); - } - i += 5; - } - _ => break, - } - } - } + f.load_cid_widths(file, &desc_dict); + f.load_vertical_metrics(file, &desc_dict); f.has_widths = true; - // Encoding CMap. - match file.get(d, "Encoding") { - Some(Obj::Name(n)) => { - f.vertical = n.ends_with("-V"); - if !n.starts_with("Identity") { - skipped.push(format!("CMap {n}")); - } - } - Some(Obj::Stream(sd, raw)) => { - if let Some(data) = decode_stream(file, sd, raw) { - let cm = parse_cmap(&data); - f.vertical = cm.wmode || file.get_num(sd, "WMode") == Some(1.0); - f.codespace = cm.codespace; - f.cid_ranges = cm.cids; - } - } - _ => {} - } - match file.get(&desc_dict, "CIDToGIDMap") { - Some(Obj::Stream(sd, raw)) => { - f.cid_to_gid = decode_stream(file, sd, raw).map(|b| b.as_chunks::<2>().0.iter().map(|c| u16::from_be_bytes([c[0], c[1]])).collect()); - } - _ => f.cid_to_gid = None, - } + f.load_cid_encoding(file, d, &desc_dict, skipped); } else { f.first_char = file.get_num(d, "FirstChar").unwrap_or(0.0) as u32; if let Some(w) = file.get(d, "Widths") { @@ -486,16 +407,113 @@ impl Font { f.default_width = file.get_num(fd, "MissingWidth").unwrap_or(0.0); } } + f.load_program(file, d, fd.as_ref(), &subtype, skipped); + f.load_simple_encoding(file, d); + f + } + + fn load_cid_widths(&mut self, file: &File, desc_dict: &Dict) { + self.default_width = file.get_num(desc_dict, "DW").unwrap_or(1000.0) / 1000.0; + if let Some(w) = file.get(desc_dict, "W").and_then(Obj::array) { + let mut i = 0; + while i < w.len() { + let first = file.resolve(&w[i]).num().unwrap_or(0.0) as u32; + match w.get(i + 1).map(|x| file.resolve(x)) { + Some(Obj::Array(list)) => { + for (k, v) in list.iter().enumerate() { + if let Some(cid) = first.checked_add(k as u32) { + self.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.saturating_add(65535)) { + self.cid_widths.insert(c, wv); + } + i += 3; + } + _ => break, + } + } + } + } + + fn load_vertical_metrics(&mut self, file: &File, desc_dict: &Dict) { + // Vertical metrics (§9.7.4.3): /DW2 [vy w1y] and /W2 entries + // `c [w1y vx vy …]` or `cfirst clast w1y vx vy`. + let dw2 = file.get(desc_dict, "DW2").map(|x| file.nums(x)).unwrap_or_default(); + if dw2.len() == 2 { + self.dw2 = (dw2[0] / 1000.0, dw2[1] / 1000.0); + } + if let Some(w) = file.get(desc_dict, "W2").and_then(Obj::array) { + let num = |i: usize| w.get(i).and_then(|x| file.resolve(x).num()); + let mut i = 0; + while i < w.len() { + let Some(first) = num(i) else { break }; + let first = first.max(0.0) as u32; + match w.get(i + 1).map(|x| file.resolve(x)) { + Some(Obj::Array(list)) => { + let v: Vec = list.iter().filter_map(|x| file.resolve(x).num()).collect(); + for (k, t) in v.as_chunks::<3>().0.iter().enumerate() { + if let Some(cid) = first.checked_add(k as u32) { + self.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.saturating_add(65535)) { + self.cid_vmetrics.insert(c, (w1 / 1000.0, vx / 1000.0, vy / 1000.0)); + } + i += 5; + } + _ => break, + } + } + } + } + + fn load_cid_encoding(&mut self, file: &File, d: &Dict, desc_dict: &Dict, skipped: &mut Vec) { + // Encoding CMap. + match file.get(d, "Encoding") { + Some(Obj::Name(n)) => { + self.vertical = n.ends_with("-V"); + if !n.starts_with("Identity") { + skipped.push(format!("CMap {n}")); + } + } + Some(Obj::Stream(sd, raw)) => { + if let Some(data) = decode_stream(file, sd, raw) { + let cm = parse_cmap(&data); + self.vertical = cm.wmode || file.get_num(sd, "WMode") == Some(1.0); + self.codespace = cm.codespace; + self.cid_ranges = cm.cids; + } + } + _ => {} + } + match file.get(desc_dict, "CIDToGIDMap") { + Some(Obj::Stream(sd, raw)) => { + self.cid_to_gid = decode_stream(file, sd, raw).map(|b| b.as_chunks::<2>().0.iter().map(|c| u16::from_be_bytes([c[0], c[1]])).collect()); + } + _ => self.cid_to_gid = None, + } + } + + fn load_program(&mut self, file: &File, d: &Dict, fd: Option<&Dict>, subtype: &str, skipped: &mut Vec) { // The program. let font_file = |key: &str| -> Option<(Dict, Vec)> { - let fd = fd.as_ref()?; + let fd = fd?; match file.get(fd, key) { Some(Obj::Stream(sd, raw)) => Some((sd.clone(), decode_stream(file, sd, raw)?)), _ => None, } }; if subtype == "Type3" { - f.matrix = file + self.matrix = file .get(d, "FontMatrix") .map(|m| file.nums(m)) .filter(|m| m.len() == 6) @@ -511,76 +529,78 @@ impl Font { } } } - f.program = Program::Type3 { procs, resources: file.get_dict(d, "Resources").cloned() }; + self.program = Program::Type3 { procs, resources: file.get_dict(d, "Resources").cloned() }; // Type 3 widths are in glyph space. - let m = f.matrix.as_coeffs()[0]; - for w in &mut f.widths { + let m = self.matrix.as_coeffs()[0]; + for w in &mut self.widths { *w *= m * 1000.0; } } else if let Some((_, data)) = font_file("FontFile2") { - f.program = Program::TrueType(Arc::new(data)); + self.program = Program::TrueType(Arc::new(data)); } else if let Some((sd, data)) = font_file("FontFile3") { match file.get(&sd, "Subtype").and_then(Obj::name) { Some("OpenType") => { // An OpenType wrapper: TrueType outlines through skrifa, CFF through ours. let cff = FontRef::new(&data).ok().and_then(|fr| fr.table_data(skrifa::Tag::new(b"CFF ")).map(|t| t.as_bytes().to_vec())); match cff.and_then(|c| Cff::parse(&c)) { - Some(c) => f.program = Program::Cff(Box::new(c)), - None => f.program = Program::TrueType(Arc::new(data)), + Some(c) => self.program = Program::Cff(Box::new(c)), + None => self.program = Program::TrueType(Arc::new(data)), } } _ => match Cff::parse(&data) { - Some(c) => f.program = Program::Cff(Box::new(c)), + Some(c) => self.program = Program::Cff(Box::new(c)), None => skipped.push("font program (CFF)".into()), }, } } else if let Some((_, data)) = font_file("FontFile") { match Type1::parse(&data) { - Some(t) => f.program = Program::Type1(Box::new(t)), + Some(t) => self.program = Program::Type1(Box::new(t)), None => skipped.push("font program (Type 1)".into()), } } else { - f.program = Program::Fallback(fallback(&base_name)); + self.program = Program::Fallback(fallback(&self.base_name)); } - if let Program::Cff(c) = &f.program { + if let Program::Cff(c) = &self.program { let m = c.font_matrix; - f.matrix = Affine::new(m); + self.matrix = Affine::new(m); } - if let Program::Type1(t) = &f.program + if let Program::Type1(t) = &self.program && let Some(m) = t.font_matrix { - f.matrix = Affine::new(m); + self.matrix = Affine::new(m); } + } + + fn load_simple_encoding(&mut self, file: &File, d: &Dict) { // Simple-font encoding: base encoding, built-in encoding, Differences. - if !f.composite { + if !self.composite { let enc = file.get(d, "Encoding").map(|e| file.resolve(e)); let base = match enc { Some(Obj::Name(n)) => Base::from_name(n), Some(Obj::Dict(e)) => file.get(e, "BaseEncoding").and_then(Obj::name).and_then(Base::from_name), _ => None, }; - let builtin: Option>> = match &f.program { + let builtin: Option>> = match &self.program { Program::Type1(t) if !t.encoding.is_empty() => Some((0..256).map(|c| t.encoding.get(&(c as u8)).cloned()).collect()), _ => None, }; - let fallback_base = if f.symbolic { None } else { Some(Base::Standard) }; + let fallback_base = if self.symbolic { None } else { Some(Base::Standard) }; for c in 0..256usize { - f.names[c] = match (base, &builtin) { + self.names[c] = match (base, &builtin) { (Some(b), _) => b.name(c as u8), (None, Some(bi)) => bi[c].clone(), - (None, None) => match &f.program { + (None, None) => match &self.program { // TrueType symbolic fonts map codes directly; others use Standard. - Program::TrueType(_) if f.symbolic => None, - Program::Fallback(_) if base_name.contains("Symbol") || base_name.contains("Dingbats") => None, + Program::TrueType(_) if self.symbolic => None, + Program::Fallback(_) if self.base_name.contains("Symbol") || self.base_name.contains("Dingbats") => None, _ => fallback_base.or(Some(Base::Standard)).and_then(|b| b.name(c as u8)), }, }; } if let Some(Obj::Dict(e)) = enc { - differences(file, e, &mut f.names); + differences(file, e, &mut self.names); } } - f } /// Split a string into (code, byte length) pairs. diff --git a/crates/pdf/src/ps/builtin.rs b/crates/pdf/src/ps/builtin.rs index 3d998d620..dcafa9417 100644 --- a/crates/pdf/src/ps/builtin.rs +++ b/crates/pdf/src/ps/builtin.rs @@ -2,10 +2,166 @@ use super::eps::find_from; use super::scanner::Src; use super::text::encoding_array; use super::value::{Flow, V, array, matrix_of, matrix_val, new_dict}; -use super::*; +use super::{Affine, BezPath, Cap, FillRule, Interp, Join, PathEl, Point, Rc, RefCell, parse_num}; impl Interp { pub(super) fn builtin(&mut self, n: &str, src: &mut Src) -> Result<(), Flow> { + // Every handler leaves the stacks untouched when the operator is outside its family. + if self.try_stack_op(n) + || self.try_arithmetic_op(n) + || self.try_logic_op(n) + || self.try_control_op(n, src)? + || self.try_dictionary_op(n) + || self.try_collection_op(n) + || self.try_environment_op(n, src)? + || self.try_graphics_op(n)? + || self.try_coordinate_op(n) + || self.try_path_op(n)? + || self.try_paint_op(n)? + { + return Ok(()); + } + match n { + // Text (see `text_op`). + "scalefont" | "makefont" | "selectfont" | "setfont" | "show" | "charpath" | "glyphshow" | "rootfont" | "currentfont" | "setcachedevice" + | "setcharwidth" | "ashow" | "widthshow" | "awidthshow" | "xshow" | "yshow" | "xyshow" | "kshow" | "cshow" | "stringwidth" | "definefont" + | "StandardEncoding" | "ISOLatin1Encoding" | "eexec" | "StartData" | "undefinefont" => self.text_op(n, src)?, + // Images: operands popped, inline data skipped. + "image" | "imagemask" | "colorimage" => { + let mut reads_file = false; + let check = |v: &V, rf: &mut bool| { + if matches!(v, V::File) || matches!(v, V::Proc(p) if p.iter().any(|x| matches!(x, V::Name(n) if &**n == "currentfile"))) { + *rf = true; + } + }; + if n == "colorimage" { + let ncomp = self.popn().max(1.0) as usize; + let multi = matches!(self.pop(), V::Bool(true)); + for _ in 0..if multi { ncomp.min(4) } else { 1 } { + let v = self.pop(); + check(&v, &mut reads_file); + } + for _ in 0..4 { + self.pop(); + } + } else { + match self.pop() { + V::Dict(d) => { + if let Some(ds) = d.borrow().get("DataSource") { + check(ds, &mut reads_file); + } + } + top => { + check(&top, &mut reads_file); + for _ in 0..4 { + self.pop(); + } + } + } + } + if reads_file { + src.skip_image_data(); + } + self.b.skip("image"); + } + _ => { + if self.unknown.len() < 32 && !self.unknown.iter().any(|u| u == n) { + self.unknown.push(n.to_string()); + } + } + } + Ok(()) + } + + fn repeat_proc(&mut self, src: &mut Src) -> Result<(), Flow> { + let p = self.pop(); + let k = self.popn().clamp(0.0, 1e6) as usize; + for _ in 0..k { + self.spend(1)?; + match self.exec_obj(p.clone(), src) { + Err(Flow::Exit) => break, + r => r?, + } + } + + Ok(()) + } + + fn for_proc(&mut self, src: &mut Src) -> Result<(), Flow> { + let p = self.pop(); + let lim = self.popn(); + let inc = self.popn(); + let mut i = self.popn(); + let mut guard = 0; + while inc != 0.0 && ((inc > 0.0 && i <= lim) || (inc < 0.0 && i >= lim)) && guard < 1_000_000 { + self.spend(1)?; + self.push(V::Num(i)); + match self.exec_obj(p.clone(), src) { + Err(Flow::Exit) => break, + r => r?, + } + i += inc; + guard += 1; + } + + Ok(()) + } + + fn loop_proc(&mut self, src: &mut Src) -> Result<(), Flow> { + let p = self.pop(); + for _ in 0..1_000_000 { + self.spend(1)?; + match self.exec_obj(p.clone(), src) { + Err(Flow::Exit) => break, + r => r?, + } + } + + Ok(()) + } + + fn forall_proc(&mut self, src: &mut Src) -> Result<(), Flow> { + let p = self.pop(); + match self.pop() { + V::Dict(d) => { + let items: Vec<(String, V)> = d.borrow().iter().map(|(k, v)| (k.clone(), v.clone())).collect(); + for (k, v) in items { + self.spend(1)?; + self.push(V::Lit(k.into())); + self.push(v); + match self.exec_obj(p.clone(), src) { + Err(Flow::Exit) => break, + r => r?, + } + } + } + V::Str(s) => { + let bytes = s.borrow().clone(); + for c in bytes { + self.spend(1)?; + self.push(V::Num(c as f64)); + match self.exec_obj(p.clone(), src) { + Err(Flow::Exit) => break, + r => r?, + } + } + } + a => { + for v in a.items().unwrap_or_default() { + self.spend(1)?; + self.push(v); + match self.exec_obj(p.clone(), src) { + Err(Flow::Exit) => break, + r => r?, + } + } + } + } + + Ok(()) + } + + fn try_stack_op(&mut self, n: &str) -> bool { match n { // Stack. "pop" => { @@ -21,25 +177,27 @@ impl Interp { let a = self.st.last().cloned().unwrap_or(V::Null); self.push(a); } - "copy" => match self.pop() { - V::Num(k) => { - let k = (k.max(0.0) as usize).min(self.st.len()); - let tail: Vec = self.st[self.st.len() - k..].to_vec(); - self.st.extend(tail); - } - V::Array(dst) => { - let s = self.pop().items().unwrap_or_default(); - let mut d = dst.borrow_mut(); - for (i, v) in s.into_iter().enumerate() { - if i < d.len() { - d[i] = v; - } + "copy" => { + match self.pop() { + V::Num(k) => { + let k = (k.max(0.0) as usize).min(self.st.len()); + let tail: Vec = self.st[self.st.len() - k..].to_vec(); + self.st.extend(tail); } - drop(d); - self.push(V::Array(dst)); - } - other => self.push(other), - }, + V::Array(dst) => { + let s = self.pop().items().unwrap_or_default(); + let mut d = dst.borrow_mut(); + for (i, v) in s.into_iter().enumerate() { + if i < d.len() { + d[i] = v; + } + } + drop(d); + self.push(V::Array(dst)); + } + other => self.push(other), + }; + } "index" => { let k = self.popn().max(0.0) as usize; let v = if k < self.st.len() { self.st[self.st.len() - 1 - k].clone() } else { V::Null }; @@ -84,6 +242,13 @@ impl Interp { } self.push(V::Dict(d)); } + _ => return false, + } + true + } + + fn try_arithmetic_op(&mut self, n: &str) -> bool { + match n { // Arithmetic. "add" | "sub" | "mul" | "div" | "idiv" | "mod" | "atan" | "exp" | "max" | "min" => { let b = self.popn(); @@ -131,6 +296,13 @@ impl Interp { }; self.push(V::Num(if v.is_finite() { v } else { 0.0 })); } + _ => return false, + } + true + } + + fn try_logic_op(&mut self, n: &str) -> bool { + match n { // Comparison and logic. "eq" | "ne" => { let b = self.pop(); @@ -181,6 +353,13 @@ impl Interp { "true" => self.push(V::Bool(true)), "false" => self.push(V::Bool(false)), "null" => self.push(V::Null), + _ => return false, + } + true + } + + fn try_control_op(&mut self, n: &str, src: &mut Src) -> Result { + match n { // Control. "exec" => { let v = self.pop(); @@ -199,82 +378,10 @@ impl Interp { self.exec_obj(if c { t } else { f }, src)?; } // Each pass of a loop counts against the budget. - "repeat" => { - let p = self.pop(); - let k = self.popn().clamp(0.0, 1e6) as usize; - for _ in 0..k { - self.spend(1)?; - match self.exec_obj(p.clone(), src) { - Err(Flow::Exit) => break, - r => r?, - } - } - } - "for" => { - let p = self.pop(); - let lim = self.popn(); - let inc = self.popn(); - let mut i = self.popn(); - let mut guard = 0; - while inc != 0.0 && ((inc > 0.0 && i <= lim) || (inc < 0.0 && i >= lim)) && guard < 1_000_000 { - self.spend(1)?; - self.push(V::Num(i)); - match self.exec_obj(p.clone(), src) { - Err(Flow::Exit) => break, - r => r?, - } - i += inc; - guard += 1; - } - } - "loop" => { - let p = self.pop(); - for _ in 0..1_000_000 { - self.spend(1)?; - match self.exec_obj(p.clone(), src) { - Err(Flow::Exit) => break, - r => r?, - } - } - } - "forall" => { - let p = self.pop(); - match self.pop() { - V::Dict(d) => { - let items: Vec<(String, V)> = d.borrow().iter().map(|(k, v)| (k.clone(), v.clone())).collect(); - for (k, v) in items { - self.spend(1)?; - self.push(V::Lit(k.into())); - self.push(v); - match self.exec_obj(p.clone(), src) { - Err(Flow::Exit) => break, - r => r?, - } - } - } - V::Str(s) => { - let bytes = s.borrow().clone(); - for c in bytes { - self.spend(1)?; - self.push(V::Num(c as f64)); - match self.exec_obj(p.clone(), src) { - Err(Flow::Exit) => break, - r => r?, - } - } - } - a => { - for v in a.items().unwrap_or_default() { - self.spend(1)?; - self.push(v); - match self.exec_obj(p.clone(), src) { - Err(Flow::Exit) => break, - r => r?, - } - } - } - } - } + "repeat" => self.repeat_proc(src)?, + "for" => self.for_proc(src)?, + "loop" => self.loop_proc(src)?, + "forall" => self.forall_proc(src)?, "exit" => return Err(Flow::Exit), "stop" => return Err(Flow::Stop), "stopped" => { @@ -288,6 +395,13 @@ impl Interp { } "quit" => return Err(Flow::Quit), "showpage" | "copypage" => {} + _ => return Ok(false), + } + Ok(true) + } + + fn try_dictionary_op(&mut self, n: &str) -> bool { + match n { // Dictionaries. "def" => { let v = self.pop(); @@ -385,6 +499,13 @@ impl Interp { self.pop(); self.push(V::Bool(false)); } + _ => return false, + } + true + } + + fn try_collection_op(&mut self, n: &str) -> bool { + match n { // Arrays and strings. "array" => { let k = self.popn().clamp(0.0, 1e6) as usize; @@ -521,6 +642,13 @@ impl Interp { self.push(V::Name(t.into())); } "bind" | "readonly" | "executeonly" | "noaccess" => {} + _ => return false, + } + true + } + + fn try_environment_op(&mut self, n: &str, src: &mut Src) -> Result { + match n { "languagelevel" => self.push(V::Num(3.0)), "version" | "product" => self.push(V::Str(Rc::new(RefCell::new(b"3010".to_vec())))), "save" => { @@ -644,6 +772,13 @@ impl Interp { self.push(V::Num(0.0)); } } + _ => return Ok(false), + } + Ok(true) + } + + fn try_graphics_op(&mut self, n: &str) -> Result { + match n { // Graphics state. "gsave" => { self.spend(self.gs.path.elements().len())?; @@ -733,6 +868,13 @@ impl Interp { self.push(V::Num(c)); } } + _ => return Ok(false), + } + Ok(true) + } + + fn try_coordinate_op(&mut self, n: &str) -> bool { + match n { // Coordinates. "matrix" => self.push(matrix_val(Affine::IDENTITY)), "identmatrix" | "defaultmatrix" => { @@ -805,6 +947,13 @@ impl Interp { self.push(V::Num(p.x)); self.push(V::Num(p.y)); } + _ => return false, + } + true + } + + fn try_path_op(&mut self, n: &str) -> Result { + match n { // Paths. "newpath" => { self.gs.path = BezPath::new(); @@ -869,6 +1018,13 @@ impl Interp { let p = self.page; self.gs.path = kurbo::Shape::to_path(&kurbo::Rect::new(p[0], p[1], p[2], p[3]), 0.1); } + _ => return Ok(false), + } + Ok(true) + } + + fn try_paint_op(&mut self, n: &str) -> Result { + match n { // Painting. "fill" => self.paint(Some(FillRule::NonZero), false), "eofill" => self.paint(Some(FillRule::EvenOdd), false), @@ -903,54 +1059,8 @@ impl Interp { self.pop(); self.b.skip("smooth shading (shfill)"); } - // Text (see `text_op`). - "scalefont" | "makefont" | "selectfont" | "setfont" | "show" | "charpath" | "glyphshow" | "rootfont" | "currentfont" | "setcachedevice" - | "setcharwidth" | "ashow" | "widthshow" | "awidthshow" | "xshow" | "yshow" | "xyshow" | "kshow" | "cshow" | "stringwidth" | "definefont" - | "StandardEncoding" | "ISOLatin1Encoding" | "eexec" | "StartData" | "undefinefont" => self.text_op(n, src)?, - // Images: operands popped, inline data skipped. - "image" | "imagemask" | "colorimage" => { - let mut reads_file = false; - let check = |v: &V, rf: &mut bool| { - if matches!(v, V::File) || matches!(v, V::Proc(p) if p.iter().any(|x| matches!(x, V::Name(n) if &**n == "currentfile"))) { - *rf = true; - } - }; - if n == "colorimage" { - let ncomp = self.popn().max(1.0) as usize; - let multi = matches!(self.pop(), V::Bool(true)); - for _ in 0..if multi { ncomp.min(4) } else { 1 } { - let v = self.pop(); - check(&v, &mut reads_file); - } - for _ in 0..4 { - self.pop(); - } - } else { - match self.pop() { - V::Dict(d) => { - if let Some(ds) = d.borrow().get("DataSource") { - check(ds, &mut reads_file); - } - } - top => { - check(&top, &mut reads_file); - for _ in 0..4 { - self.pop(); - } - } - } - } - if reads_file { - src.skip_image_data(); - } - self.b.skip("image"); - } - _ => { - if self.unknown.len() < 32 && !self.unknown.iter().any(|u| u == n) { - self.unknown.push(n.to_string()); - } - } + _ => return Ok(false), } - Ok(()) + Ok(true) } } diff --git a/crates/pdf/src/ps/eps.rs b/crates/pdf/src/ps/eps.rs index 22391567c..90d60cfbe 100644 --- a/crates/pdf/src/ps/eps.rs +++ b/crates/pdf/src/ps/eps.rs @@ -1,4 +1,4 @@ -use super::*; +use super::{HashMap, Rc, is_white}; /// The PostScript section of an EPS file (DOS EPS binary header handled) and its bounding box /// (`%%HiResBoundingBox` preferred). diff --git a/crates/pdf/src/ps/exec.rs b/crates/pdf/src/ps/exec.rs index dd7f6a579..27697527c 100644 --- a/crates/pdf/src/ps/exec.rs +++ b/crates/pdf/src/ps/exec.rs @@ -1,7 +1,7 @@ use super::eps::embedded_fonts; use super::scanner::Src; use super::value::{Flow, GState, V, new_dict}; -use super::*; +use super::{Affine, BezPath, Builder, Cap, HashMap, Interp, Join}; /// The deepest procedure nesting run (deeper recursion stops the program). const MAX_CALL_DEPTH: usize = 256; diff --git a/crates/pdf/src/ps/paths.rs b/crates/pdf/src/ps/paths.rs index 20af48b9d..b1fe07092 100644 --- a/crates/pdf/src/ps/paths.rs +++ b/crates/pdf/src/ps/paths.rs @@ -1,5 +1,5 @@ use super::value::{Flow, V}; -use super::*; +use super::{Affine, BezPath, FillRule, Interp, MAX_PATH, Paint, PathEl, Point, Stroke}; /// The angle from `a` counterclockwise to `b` (degrees, 0 to 360), worked out from what is left /// of each after whole turns. diff --git a/crates/pdf/src/ps/scanner.rs b/crates/pdf/src/ps/scanner.rs index e407fba55..59933e214 100644 --- a/crates/pdf/src/ps/scanner.rs +++ b/crates/pdf/src/ps/scanner.rs @@ -1,5 +1,5 @@ use super::value::V; -use super::*; +use super::{Rc, RefCell, ascii85, is_white, parse_num}; /// Source tokenizer (the data source for `currentfile`). pub(super) struct Src<'a> { diff --git a/crates/pdf/src/ps/text.rs b/crates/pdf/src/ps/text.rs index 06fdddb4e..b138965bb 100644 --- a/crates/pdf/src/ps/text.rs +++ b/crates/pdf/src/ps/text.rs @@ -1,6 +1,6 @@ use super::scanner::Src; use super::value::{DictRef, Flow, V, array, matrix_of, matrix_val, new_dict}; -use super::*; +use super::{Affine, BezPath, FillRule, Font, HashMap, Interp, Paint, Point, Rc, RefCell}; // ------------------------------------------------------------------ text diff --git a/crates/pdf/src/ps/value.rs b/crates/pdf/src/ps/value.rs index 31754a223..3cfafed6e 100644 --- a/crates/pdf/src/ps/value.rs +++ b/crates/pdf/src/ps/value.rs @@ -1,4 +1,4 @@ -use super::*; +use super::{Affine, BezPath, Cap, HashMap, Join, Point, Rc, RefCell}; pub(crate) type DictRef = Rc>>;