From e44f5a1b7767045658187715fab327fb22f30bcf Mon Sep 17 00:00:00 2001 From: JonRC Date: Tue, 3 Nov 2026 01:07:29 -0300 Subject: [PATCH 1/2] Fix #5312: EPS files written by Ghostscript's eps2write open `gcheck` and `scheck` answered false for every operand. The PLRM (3rd ed., `gcheck`) says they are true for a simple object, and false only for a composite one in local VM. The eps2write prolog copies its initial graphics state into global VM and leaves a value alone only when `gcheck` is true. So the integers in the `currenthalftone` dictionary took the copy path, left the stack one item off, and the next `put` failed with a typecheck in `cp2g`. Simple objects now answer true. Composites still answer false, since every composite here is in local VM. --- crates/eps/src/import/interp.rs | 7 +++++-- crates/eps/src/import/obj.rs | 5 +++++ crates/eps/src/import/tests_generators.rs | 15 +++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/crates/eps/src/import/interp.rs b/crates/eps/src/import/interp.rs index 12232624f..6126b4b32 100644 --- a/crates/eps/src/import/interp.rs +++ b/crates/eps/src/import/interp.rs @@ -1135,9 +1135,12 @@ impl<'a> Interp<'a> { PdfMark => { self.pop_to_mark()?; } + // True for a simple object, and for a composite one in global VM (PLRM 3rd ed., `gcheck`); + // every composite here is in local VM. Procsets that copy values into global VM leave + // alone those whose `gcheck` is true, such as a halftone's numbers. Gcheck | Scheck => { - self.pop()?; - self.push(Obj::Bool(false))?; + let simple = !self.pop()?.is_composite(); + self.push(Obj::Bool(simple))?; } CurrentShared => self.push(Obj::Bool(false))?, SetShared | SetVmThreshold | VmReclaim | Echo => { diff --git a/crates/eps/src/import/obj.rs b/crates/eps/src/import/obj.rs index b4dc34364..4f07678a7 100644 --- a/crates/eps/src/import/obj.rs +++ b/crates/eps/src/import/obj.rs @@ -286,6 +286,11 @@ impl Obj { Self::GState(_) => "gstatetype", } } + + /// A composite object, whose value lives in VM; the others are simple (PLRM 3rd ed., §3.3.1). + pub fn is_composite(&self) -> bool { + matches!(self, Self::Str(_) | Self::ExecStr(_) | Self::Array { .. } | Self::Dict(_) | Self::File { .. } | Self::Save(_) | Self::GState(_)) + } } /// The values of a string or an array: a run of storage that other strings or arrays may share diff --git a/crates/eps/src/import/tests_generators.rs b/crates/eps/src/import/tests_generators.rs index 2fe6ba324..4337b9474 100644 --- a/crates/eps/src/import/tests_generators.rs +++ b/crates/eps/src/import/tests_generators.rs @@ -592,6 +592,21 @@ showpage"##; assert!(near(bounds(&meshes[1]), Rect::new(150.0, 90.0, 190.0, 140.0)), "{:?}", bounds(&meshes[1])); } +/// Ghostscript 9's `eps2write` copies the initial graphics state (`currenthalftone`, +/// `currentcolortransfer`) into global VM before it draws, with a copier for each composite type. +/// It copies only what `gcheck` says is in local VM: `gcheck` is true for every simple object +/// (PLRM 3rd ed., `gcheck`), so the halftone's numbers stay as they are. Written from the PLRM. +#[test] +fn gcheck_is_true_for_simple_objects() { + super::tests::check("1 gcheck 2.5 gcheck and /n gcheck and true gcheck and null gcheck and /add load gcheck and mark gcheck and"); + super::tests::check("(s) gcheck not 1 dict gcheck not and {} gcheck not and 1 scheck and (s) scheck not and"); + super::tests::check( + "/copiers << /dicttype { << exch { cp2g } forall >> } /arraytype { [ exch { cp2g } forall ] } >> def \ + /cp2g { dup gcheck not { copiers 1 index type get exec } if } bind def \ + currenthalftone cp2g /Frequency get 60 eq [ currentcolortransfer ] cp2g length 4 eq and", + ); +} + /// A procset written as the PostScript Language Reference (3rd ed.) describes the Level 2 and 3 /// facilities programs probe before drawing: a resource category made from `Generic` and /// resources defined and found in it (§3.9), local and global VM (`currentglobal`, `setglobal`, From 5e3d35c0e02eb703caff71436fb068d3b1672801 Mon Sep 17 00:00:00 2001 From: JonRC Date: Tue, 3 Nov 2026 01:07:42 -0300 Subject: [PATCH 2/2] contributors: add my name for the About window --- contributors/people.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contributors/people.toml b/contributors/people.toml index f3c8629b7..db94e2a89 100644 --- a/contributors/people.toml +++ b/contributors/people.toml @@ -189,3 +189,7 @@ sync_github_name = false [people.WadahBadawi] display_name = "Wadah Badawi" sync_github_name = true + +[people.JonRC] +real_name = "Jonathan Cardoso" +display_name = "Jonathan Cardoso"