Refactor pdf for strict Clippy checks

This commit is contained in:
Dylan M. Taylor
2026-10-28 06:09:21 +00:00
parent 388ebc10ac
commit 51b910657d
8 changed files with 386 additions and 256 deletions
+127 -107
View File
@@ -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<f64> = 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<f64> = 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<String>) {
// 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<String>) {
// The program.
let font_file = |key: &str| -> Option<(Dict, Vec<u8>)> {
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<Vec<Option<String>>> = match &f.program {
let builtin: Option<Vec<Option<String>>> = 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.
+253 -143
View File
@@ -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<V> = 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<V> = 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<bool, Flow> {
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<bool, Flow> {
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<bool, Flow> {
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<bool, Flow> {
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<bool, Flow> {
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)
}
}
+1 -1
View File
@@ -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).
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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.
+1 -1
View File
@@ -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> {
+1 -1
View File
@@ -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
+1 -1
View File
@@ -1,4 +1,4 @@
use super::*;
use super::{Affine, BezPath, Cap, HashMap, Join, Point, Rc, RefCell};
pub(crate) type DictRef = Rc<RefCell<HashMap<String, V>>>;