mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 09:46:59 -06:00
Part of #868: legacy .ai defines annotatepage in a prolog of its own
The page trailer the version 7 specification gives (section 3, Script Setup) is `gsave annotatepage grestore showpage`, and annotatepage is a procedure of the app's own prolog, which the file didn't have. The file now carries a procedure set of the writer's own (VectorCraft_Legacy 1 0, listed in %%DocumentSuppliedResources) in a dictionary the setup begins and the trailer ends, which defines annotatepage as marking nothing.
This commit is contained in:
@@ -51,8 +51,10 @@
|
||||
//! Windows-1252, vertical type, inline graphics, gradient or pattern fills) is written as its
|
||||
//! outlines, and all type with [`LegacyAiOptions::outline_text`].
|
||||
//!
|
||||
//! No prolog is written: the file names none, so PostScript printers can't print it, as the `.ai`
|
||||
//! files other apps write in this format.
|
||||
//! The prolog defines the one procedure the script calls that isn't an operator of the format,
|
||||
//! the page trailer's `annotatepage` ([`PROLOG`]); the format's operators are left to the app that
|
||||
//! reads the file, so PostScript printers can't print it, as the `.ai` files other apps write in
|
||||
//! this format.
|
||||
|
||||
use std::fmt::Write;
|
||||
use std::sync::Arc;
|
||||
@@ -196,7 +198,11 @@ pub fn export_legacy_ai(doc: &Document, o: &LegacyAiOptions) -> Result<LegacyAiO
|
||||
let _ = writeln!(s, "%AI5_ArtSize: {} {}", ps::num(width), ps::num(height));
|
||||
s.push_str("%AI5_RulerUnits: 2\n");
|
||||
let _ = writeln!(s, "%AI5_NumLayers: {layers}");
|
||||
s.push_str("%%EndComments\n%%BeginProlog\n%%EndProlog\n%%BeginSetup\n");
|
||||
s.push_str("%%DocumentSuppliedResources: procset VectorCraft_Legacy 1 0\n");
|
||||
s.push_str("%%EndComments\n%%BeginProlog\n");
|
||||
s.push_str(PROLOG);
|
||||
s.push_str("%%EndProlog\n%%BeginSetup\n");
|
||||
s.push_str("VCLegacy begin\n");
|
||||
fonts(&mut s, &w.fonts);
|
||||
if !w.gradients.is_empty() {
|
||||
let _ = writeln!(s, "{} Bn", w.gradients.len());
|
||||
@@ -206,7 +212,9 @@ pub fn export_legacy_ai(doc: &Document, o: &LegacyAiOptions) -> Result<LegacyAiO
|
||||
}
|
||||
s.push_str("%%EndSetup\n");
|
||||
s.push_str(&w.body);
|
||||
s.push_str("%%PageTrailer\ngsave annotatepage grestore showpage\n%%Trailer\n%%EOF\n");
|
||||
// The page trailer as the specification's script has it (§3); the document trailer takes the
|
||||
// prolog's dictionary off the dictionary stack (its procedure set termination, §3).
|
||||
s.push_str("%%PageTrailer\ngsave annotatepage grestore showpage\n%%Trailer\nend\n%%EOF\n");
|
||||
Ok(LegacyAiOutput { bytes: s.into_bytes(), warnings: w.warnings })
|
||||
}
|
||||
|
||||
@@ -234,6 +242,19 @@ fn fonts(s: &mut String, fonts: &[(String, bool)]) {
|
||||
}
|
||||
}
|
||||
|
||||
/// The prolog: a procedure set of this writer's own (`VectorCraft_Legacy 1 0`), in a dictionary
|
||||
/// (`VCLegacy`) the setup begins and the trailer ends (§1.2: the prolog holds definitions only,
|
||||
/// the setup activates them, the trailer deactivates them). The page trailer of the
|
||||
/// specification's script (§3, Script Setup: `gsave annotatepage grestore showpage`) calls
|
||||
/// `annotatepage`, which marks nothing here.
|
||||
const PROLOG: &str = "%%BeginResource: procset VectorCraft_Legacy 1 0
|
||||
/VCLegacy 8 dict def
|
||||
VCLegacy begin
|
||||
/annotatepage {} bind def
|
||||
end
|
||||
%%EndResource
|
||||
";
|
||||
|
||||
/// The first line of a PostScript file (the DSC version comment).
|
||||
const PS_HEADER: &str = "%!PS-Adobe-3.0"; // brand-ok: the PostScript header comment
|
||||
|
||||
|
||||
@@ -103,10 +103,14 @@ fn writes_the_documented_operators() {
|
||||
"\nq\n",
|
||||
"\n1 A\n",
|
||||
"XI\n",
|
||||
"%%EOF\n",
|
||||
"%%PageTrailer\ngsave annotatepage grestore showpage\n%%Trailer\nend\n%%EOF\n",
|
||||
] {
|
||||
assert!(text.contains(needle), "missing {needle:?} in\n{text}");
|
||||
}
|
||||
// The prolog defines the page trailer's `annotatepage` (§3), in the dictionary the setup begins.
|
||||
let prolog = text.split("%%BeginProlog\n").nth(1).and_then(|t| t.split("%%EndProlog\n").next()).unwrap();
|
||||
assert!(prolog.contains("/VCLegacy 8 dict def\nVCLegacy begin\n/annotatepage {} bind def\nend\n"), "{prolog}");
|
||||
assert!(text.contains("%%DocumentSuppliedResources: procset VectorCraft_Legacy 1 0\n"), "{text}");
|
||||
// The hidden layer is written hidden: visible 0.
|
||||
assert!(text.contains("%AI5_BeginLayer\n0 1 1 1 0 0 1 "), "{text}");
|
||||
// Art space is y up from the artboard's bottom-left: the red square (10..40 down from the top)
|
||||
@@ -119,7 +123,7 @@ fn writes_the_documented_operators() {
|
||||
// The clipping path: closed by `h`, then `W n` (§5.6.2).
|
||||
assert!(text.contains(" L\nh\nW\nn\n"), "{text}");
|
||||
// Gradients: counted (`Bn`), each stop's operands before `%_Bs` (§3.5).
|
||||
assert!(text.contains("%%BeginSetup\n1 Bn\n%AI5_BeginGradient: (Gradient)\n(Gradient) 0 2 Bd\n[\n"), "{text}");
|
||||
assert!(text.contains("%%BeginSetup\nVCLegacy begin\n1 Bn\n%AI5_BeginGradient: (Gradient)\n(Gradient) 0 2 Bd\n[\n"), "{text}");
|
||||
assert!(text.contains(" 2 50 100 %_Bs\n") && !text.contains("Np\n"), "{text}");
|
||||
assert!(text.contains("1 0 0 1 0 0 Bg\nf\n0 BB\n"), "{text}");
|
||||
// The image: operands and `XI` on a line, its samples on comment lines (§8).
|
||||
|
||||
Reference in New Issue
Block a user