diff --git a/crates/eps/src/import/interp.rs b/crates/eps/src/import/interp.rs index cf7c5495a..3908bd41b 100644 --- a/crates/eps/src/import/interp.rs +++ b/crates/eps/src/import/interp.rs @@ -1050,10 +1050,10 @@ impl<'a> Interp<'a> { self.insert(&d, k, Some(v))?; } Known => { - let k = self.pop()?.key().ok_or(PsError::Ps("typecheck", String::new()))?; + let k = self.pop()?; let d = self.pop_dict()?; - let known = d.borrow().contains_key(&k); - self.push(Obj::Bool(known))?; + let found = known(&d, &k)?; + self.push(Obj::Bool(found))?; } Where => { let k = self.pop()?.key().ok_or(PsError::Ps("typecheck", String::new()))?; @@ -1418,6 +1418,16 @@ fn index(k: &Obj) -> Res { } } +/// `known`: a key may be any object but null (PLRM 3rd ed.), and one that can't be a key here (an +/// array, a dictionary, a real with a fraction) is in no dictionary, since `put` refuses it. +fn known(d: &DictRef, k: &Obj) -> Res { + match k.key() { + Some(key) => Ok(d.borrow().contains_key(&key)), + None if matches!(k, Obj::Null) => ps_err("typecheck", "known"), + None => Ok(false), + } +} + /// `eq`: numbers by value, strings and names by text, composites by identity. fn equal(a: &Obj, b: &Obj) -> bool { match (a, b) { diff --git a/crates/eps/src/import/tests.rs b/crates/eps/src/import/tests.rs index f249ebf31..7435fc8b2 100644 --- a/crates/eps/src/import/tests.rs +++ b/crates/eps/src/import/tests.rs @@ -996,6 +996,17 @@ fn the_standard_dictionaries_are_dictionaries_in_systemdict() { check("SharedFontDirectory type /dicttype eq"); } +/// A key may be any object but null (PLRM 3rd ed.), so `known` answers for every other key. +/// Ghostscript's `eps2write` looks an inline image's filter up in a dictionary of abbreviations, +/// and the filter may be an array of them. +#[test] +fn known_answers_false_for_keys_no_dictionary_holds() { + check("<< /A85 /ASCII85Decode >> [ /A85 /LZW ] known not"); + check("1 dict 1 dict known not 1 dict 1.5 known not and 1 dict mark known not and"); + check("<< /A85 /ASCII85Decode >> /A85 known << 1 2 >> 1.0 known and"); + check("{ 1 dict null known } stopped"); +} + #[test] fn dictstack_and_internaldict_answer() { check("10 array dictstack length countdictstack eq");