mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-28 11:47:21 -06:00
M4.47: PostScript .ai and placeholder-only .ai files say why they can't open
A PostScript file (a .ai saved in an older format or without PDF compatibility, or an .eps, plain or with a binary header) is refused by any name with a clear message instead of a parse error (there is no PostScript importer yet). An .ai whose pages are only placeholder text over the editor's private data (PieceInfo AIPrivateData) is detected and refused with advice to save it with PDF compatibility, instead of opening the placeholder. vectorcraft-pdf: is_postscript(), PdfError::PostScript / PlaceholderOnly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
8765d335ec
commit
e0895fd4ba
@@ -80,6 +80,11 @@ pub fn load(name: &str, bytes: &[u8]) -> Result<Loaded> {
|
||||
|
||||
/// [`load`] with `document.open` options (the PDF pages, box and password).
|
||||
pub fn load_with(name: &str, bytes: &[u8], opts: &LoadOptions) -> Result<Loaded> {
|
||||
// PostScript (.ai saved in an older format or without PDF compatibility, .eps) says so, by
|
||||
// any name.
|
||||
if vectorcraft_pdf::is_postscript(bytes) {
|
||||
return Err(err(format!("can't open `{}`: {}", file_name(name), vectorcraft_pdf::PdfError::PostScript)));
|
||||
}
|
||||
let format = detect(name, bytes).ok_or_else(|| err(format!("can't open `{name}`: not a format VectorCraft reads (see document.formats)")))?;
|
||||
let title = file_name(name);
|
||||
let mut warnings = vec![];
|
||||
|
||||
@@ -146,3 +146,44 @@ fn load_options_read_pages_crop_and_password() {
|
||||
assert_eq!((o.pages.as_deref(), o.password), (Some("1-2"), None));
|
||||
assert_eq!(LoadOptions::from_params("x", &Value::Null).unwrap(), LoadOptions::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_postscript_ai_file_says_it_cannot_be_opened_yet() {
|
||||
let ps = b"%!PS-3.0\n%%BoundingBox: 0 0 100 100\n%%EndComments\n0 0 moveto 100 100 lineto stroke\nshowpage\n%%EOF\n";
|
||||
let mut s = Session::new();
|
||||
for name in ["legacy.ai", "art.eps", "named.pdf", "template.ait"] {
|
||||
let e = s.execute("document.open", &json!({"name": name, "dataBase64": b64(ps)})).unwrap_err().to_string();
|
||||
assert!(e.contains(name) && e.contains("PostScript") && e.contains("save it as PDF"), "{name}: {e}");
|
||||
}
|
||||
// An EPS with a binary header too, and the PDF info of either.
|
||||
let mut binary = vec![0xC5, 0xD0, 0xD3, 0xC6];
|
||||
binary.extend_from_slice(&[0; 28]);
|
||||
binary.extend_from_slice(ps);
|
||||
assert!(s.execute("document.open", &json!({"name": "x.eps", "dataBase64": b64(&binary)})).unwrap_err().to_string().contains("PostScript"));
|
||||
assert!(s.execute("document.pdfInfo", &json!({"dataBase64": b64(ps)})).unwrap_err().to_string().contains("PostScript"));
|
||||
assert!(s.active().is_none());
|
||||
}
|
||||
|
||||
/// A page showing only text, with an editor's private data when `private`.
|
||||
fn text_page(private: bool, also: &str) -> PdfPage {
|
||||
PdfPage {
|
||||
resources: "/Font << /F1 << /Type /Font /Subtype /Type1 /BaseFont /Helvetica >> >>".into(),
|
||||
entries: if private { "/PieceInfo << /Editor << /Private << /AIPrivateData1 7 /RoundTripVersion 24 >> >> >>".into() } else { String::new() },
|
||||
..PdfPage::new(300.0, 100.0, &format!("BT /F1 12 Tf 10 50 Td (Saved without PDF content) Tj ET {also}"))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_placeholder_only_pdf_part_is_detected() {
|
||||
let mut s = Session::new();
|
||||
let e = open(&mut s, &pdf(&[text_page(true, "")], None), json!({})).unwrap_err().to_string();
|
||||
assert!(e.contains("placeholder page") && e.contains("PDF compatibility"), "{e}");
|
||||
assert!(s.active().is_none());
|
||||
// Text without private data, and private data with art, are ordinary files.
|
||||
open(&mut s, &pdf(&[text_page(false, "")], None), json!({})).unwrap();
|
||||
open(&mut s, &pdf(&[text_page(true, "0 g 0 0 10 10 re f")], None), json!({})).unwrap();
|
||||
// One real page among the pages picked is enough.
|
||||
let two = pdf(&[text_page(true, ""), text_page(true, "0 g 0 0 10 10 re f")], None);
|
||||
assert!(open(&mut s, &two, json!({"pages": "1"})).is_err());
|
||||
open(&mut s, &two, json!({})).unwrap();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user