From aafe102649f1f46064e05d3f11e3c9a20983b33d Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Sat, 17 Oct 2026 12:29:55 -0700 Subject: [PATCH] .ai import: a file whose editing data can't be decompressed says why its layers weren't read A PDF-compatible .ai, or a PDF saved with the editing data, whose editing data can't be decompressed (damaged zlib or Zstandard data, or data that decompresses to more than 256 MB) opened from its PDF part with no note about its layers. layered_ai returned on the decompression error without a note. Data that decompresses to layers that can't be read gets one, and so does an EPS. layered_ai now gives the same note with the decompression error as its reason: "the file's layers weren't read from its editing data (its zlib compression is damaged): they come from its PDF part". The note counts as a loss, so document.export or Save As over a PDF saved with the editing data asks first (acknowledgeLoss), as it already did for a .ai. The three reasons have rows in the 17 catalogs that translate every message. Docs: docs/mcp.md, docs/target-app-parity.md. --- .../engine/src/cmd/fileio/tests_aiimport.rs | 34 ++++++++++++++++--- crates/eps/src/import/native.rs | 23 ++++++++----- crates/eps/src/import/tests_native.rs | 17 ++++++++++ crates/ui-egui/src/i18n/cs.tsv | 3 ++ crates/ui-egui/src/i18n/de.tsv | 3 ++ crates/ui-egui/src/i18n/el.tsv | 3 ++ crates/ui-egui/src/i18n/es.tsv | 3 ++ crates/ui-egui/src/i18n/fi.tsv | 3 ++ crates/ui-egui/src/i18n/fr.tsv | 3 ++ crates/ui-egui/src/i18n/hu.tsv | 3 ++ crates/ui-egui/src/i18n/it.tsv | 3 ++ crates/ui-egui/src/i18n/ja.tsv | 3 ++ crates/ui-egui/src/i18n/ko.tsv | 3 ++ crates/ui-egui/src/i18n/pl.tsv | 3 ++ crates/ui-egui/src/i18n/pt-br.tsv | 3 ++ crates/ui-egui/src/i18n/ru.tsv | 3 ++ crates/ui-egui/src/i18n/sv.tsv | 3 ++ crates/ui-egui/src/i18n/tests.rs | 12 ++++++- crates/ui-egui/src/i18n/uk.tsv | 3 ++ crates/ui-egui/src/i18n/vi.tsv | 3 ++ crates/ui-egui/src/i18n/zh-hans.tsv | 3 ++ docs/mcp.md | 2 +- docs/target-app-parity.md | 1 + 23 files changed, 126 insertions(+), 14 deletions(-) diff --git a/crates/engine/src/cmd/fileio/tests_aiimport.rs b/crates/engine/src/cmd/fileio/tests_aiimport.rs index e24bdf10f..f2b6802bb 100644 --- a/crates/engine/src/cmd/fileio/tests_aiimport.rs +++ b/crates/engine/src/cmd/fileio/tests_aiimport.rs @@ -135,16 +135,42 @@ fn a_pdfs_art_past_the_page_opens_whole_from_its_editing_data() { assert!(s.doc().unwrap().doc.art_bounds().is_some_and(|b| b.x1 < 200.5)); } -/// Editing data that can't be read leaves the file as its PDF part reads, with that part's notes: -/// a `.ai`'s art outside its artboards is missing, and a PDF's art past the page is clipped. +/// A `.ai` or a PDF whose editing data can't be decompressed opens from its PDF part with a note +/// that its layers weren't read, and an export to its own path is refused unless it is given +/// `acknowledgeLoss`. +#[test] +fn a_file_whose_editing_data_cant_be_decompressed_is_not_written_over_unasked() { + let d = std::env::temp_dir().join(format!("vc-aiundecoded-{}", std::process::id())); + std::fs::create_dir_all(&d).unwrap(); + let damaged = b"%AI12_CompressedDataxx"; + for (name, file) in [("art.ai", ai::ai(damaged, ai::page_pdf())), ("art.pdf", ai::pdf(damaged, ai::page_pdf()))] { + let path = d.join(name); + std::fs::write(&path, &file).unwrap(); + let path = path.to_string_lossy().to_string(); + let mut s = Session::new(); + let r = s.execute("document.open", &json!({"path": path})).unwrap(); + assert!(r["warnings"].to_string().contains("layers weren't read from its editing data (its zlib compression is damaged)"), "{name}: {r}"); + let e = s.execute("document.export", &json!({"path": path})).unwrap_err().to_string(); + assert!(e.contains("left things out") && e.contains("layers weren't read"), "{name}: {e}"); + assert_eq!(std::fs::read(&path).unwrap(), file, "{name}"); + s.execute("document.export", &json!({"path": path, "acknowledgeLoss": true})).unwrap(); + assert_ne!(std::fs::read(&path).unwrap(), file, "{name}"); + } + std::fs::remove_dir_all(d).ok(); +} + +/// Editing data that can't be read leaves the file as its PDF part reads, with that part's notes +/// and then a note saying why its layers weren't read: a `.ai`'s art outside its artboards is +/// missing, and a PDF's art past the page is clipped. #[test] fn editing_data_that_cant_be_read_keeps_the_pdf_parts_notes() { let damaged = b"%AI12_CompressedData not zlib"; + let unread = "the file's layers weren't read from its editing data (its zlib compression is damaged): they come from its PDF part"; let mut s = Session::new(); let r = open(&mut s, "damaged.ai", &ai::ai(damaged, ai::page_pdf()), json!({})).unwrap(); - assert_eq!(r["warnings"], json!([vectorcraft_pdf::OFF_ARTBOARD_NOTE]), "{r}"); + assert_eq!(r["warnings"], json!([vectorcraft_pdf::OFF_ARTBOARD_NOTE, unread]), "{r}"); let r = open(&mut s, "damaged.pdf", &ai::pdf(damaged, "0 g 150 20 100 60 re f"), json!({})).unwrap(); - assert_eq!(r["warnings"], json!([vectorcraft_pdf::PAST_PAGE_NOTE]), "{r}"); + assert_eq!(r["warnings"], json!([vectorcraft_pdf::PAST_PAGE_NOTE, unread]), "{r}"); } #[test] diff --git a/crates/eps/src/import/native.rs b/crates/eps/src/import/native.rs index 9d281c213..a7d64e428 100644 --- a/crates/eps/src/import/native.rs +++ b/crates/eps/src/import/native.rs @@ -1308,17 +1308,26 @@ pub(super) fn layered(ps: &[u8], visible: Imported) -> Imported { /// its editing copy (`private`, the joined `AIPrivateData` or `AIPDFPrivateData` streams) instead, /// as [`layered`] reads an EPS: with the layers and objects of the file, its artboards (with the /// PDF part's bleed), and the art that lies outside them, which its PDF part doesn't have. -/// `warnings` are the notes of reading the PDF part; the notes returned are those, and the layers'. -/// The flag returned is `true` when the document is the one read from the layers; else the document -/// is `visible`, its pages moved by `place_pages` when the layers can't be read. `outlined`: the -/// PDF part's type was read as outlines (a file whose type shows then comes in as its PDF part). +/// `warnings` are the notes of reading the PDF part; the notes returned are those, then the layers' +/// notes, or a note saying why the layers weren't read when the editing data can't be decompressed +/// or its layers can't be read. The flag returned is `true` when the document is the one read from +/// the layers; else the document is `visible`, its pages moved by `place_pages` when the editing +/// data was decompressed but its layers can't be read. `outlined`: the PDF part's type was read as +/// outlines (a file whose type shows then comes in as its PDF part). /// `trust`: layers that draw differently from the PDF part are used all the same, with a warning. /// `unit`: the size in points of a unit of the PDF part's user space, its first page's `UserUnit`, /// by which its art came in scaled. Editing data whose artboard matches the page only in those /// units comes in `unit` times as large, and so does the artboard layout `place_pages` takes from /// it. pub fn layered_ai(private: &[u8], visible: Document, warnings: Vec, outlined: bool, trust: bool, unit: f64) -> (Document, Vec, bool) { - let Ok(data) = ai::decode_private(private) else { return (visible, warnings, false) }; + let unread = |mut all: Vec, why: &str| { + all.push(format!("{LAYERS_UNREAD} from its editing data ({why}): they come from its PDF part")); + all + }; + let data = match ai::decode_private(private) { + Ok(data) => data, + Err(why) => return (visible, unread(warnings, &why), false), + }; if !has_layers(&data) { return (visible, warnings, false); } @@ -1330,11 +1339,9 @@ pub fn layered_ai(private: &[u8], visible: Document, warnings: Vec, outl (done, all, true) } Err(why) => { - let mut all = warnings; - all.push(format!("{LAYERS_UNREAD} from its editing data ({why}): they come from its PDF part")); let mut visible = visible; place_pages(&ai::artboards(&data), &mut visible, unit); - (visible, all, false) + (visible, unread(warnings, &why), false) } } } diff --git a/crates/eps/src/import/tests_native.rs b/crates/eps/src/import/tests_native.rs index 1344fba28..ea61613a7 100644 --- a/crates/eps/src/import/tests_native.rs +++ b/crates/eps/src/import/tests_native.rs @@ -293,6 +293,23 @@ fn an_ai_file_that_cant_be_read_through_its_editing_data_keeps_its_pdf_part() { assert!(w.iter().any(|w| w.contains("different page")), "{w:?}"); } +/// Editing data that can't be decompressed leaves the `.ai` as its PDF part, after its notes, with a +/// note saying why its layers weren't read, which counts as a loss. +#[test] +fn an_ai_file_whose_editing_data_cant_be_decompressed_says_so() { + let pdf = pdf_part(10.0, 86.0); + for (private, why) in [ + (&b"%AI12_CompressedDataxx"[..], "its zlib compression is damaged"), + (b"%AI24_ZStandard_Data nothing", "its Zstandard compression is damaged"), + ] { + let (d, w, read) = crate::layered_ai(private, pdf.clone(), vec!["a note".into()], false, false, 1.0); + assert!(!read); + assert_eq!(names(&d), names(&pdf)); + assert_eq!(w, ["a note".to_string(), format!("the file's layers weren't read from its editing data ({why}): they come from its PDF part")]); + assert!(crate::is_loss(&w[1]), "{w:?}"); + } +} + /// #2031: a Large Canvas editing copy (`%AI24_LargeCanvasScale: 10`) writes its art and boxes at /// 1/10 scale; the import scales them back, so its layers are read instead of being rejected as /// another page's — with the art at the page's size. Art that doesn't match the page at that diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index 218158270..565191645 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -3995,6 +3995,9 @@ imageTrace Outline Obrys @msg its editing data doesn't say where its artboard is z jeho dat pro úpravy není zřejmé, kde je pracovní plocha @msg its editing data has no layers jeho data pro úpravy nemají žádné vrstvy @msg its editing data is damaged jeho data pro úpravy jsou poškozená +@msg its zlib compression is damaged jejich komprese zlib je poškozená +@msg its Zstandard compression is damaged jejich komprese Zstandard je poškozená +@msg it decompresses to more than {_1} MB po rozbalení mají více než {_1} MB @msg its editing data is for a different page jeho data pro úpravy patří k jiné stránce @msg its layers use {_1}, which aren't read yet jeho vrstvy používají {_1}, což zatím nelze načíst @msg its page has text, and its layers have no text objects to put it in jeho stránka obsahuje text, ale vrstvy nemají žádné textové objekty, kam by ho bylo možné umístit diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index 7e2aed047..4acdb18f6 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -3629,6 +3629,9 @@ theme Appearance Erscheinungsbild @msg its editing data doesn't say where its artboard is seine Bearbeitungsdaten geben nicht an, wo seine Zeichenfläche liegt @msg its editing data has no layers seine Bearbeitungsdaten enthalten keine Ebenen @msg its editing data is damaged seine Bearbeitungsdaten sind beschädigt +@msg its zlib compression is damaged ihre zlib-Komprimierung ist beschädigt +@msg its Zstandard compression is damaged ihre Zstandard-Komprimierung ist beschädigt +@msg it decompresses to more than {_1} MB sie entpacken sich auf mehr als {_1} MB @msg its editing data is for a different page seine Bearbeitungsdaten gehören zu einer anderen Seite @msg its layers use {_1}, which aren't read yet seine Ebenen verwenden {_1}, was noch nicht gelesen wird @msg its page has text, and its layers have no text objects to put it in seine Seite enthält Text, und seine Ebenen haben keine Textobjekte, die ihn aufnehmen könnten diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index b9e99cd6f..60753ec65 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -1991,6 +1991,9 @@ axis Both Και τα δύο @msg its editing data doesn't say where its artboard is Τα δεδομένα επεξεργασίας δεν προσδιορίζουν τη θέση του πίνακα σχεδίασης @msg its editing data has no layers Τα δεδομένα επεξεργασίας δεν περιλαμβάνουν επίπεδα @msg its editing data is damaged Τα δεδομένα επεξεργασίας είναι κατεστραμμένα +@msg its zlib compression is damaged η συμπίεση zlib τους είναι κατεστραμμένη +@msg its Zstandard compression is damaged η συμπίεση Zstandard τους είναι κατεστραμμένη +@msg it decompresses to more than {_1} MB αποσυμπιεσμένα υπερβαίνουν τα {_1} MB @msg its editing data is for a different page Τα δεδομένα επεξεργασίας αφορούν άλλη σελίδα @msg its layers use {_1}, which aren't read yet Τα επίπεδά του χρησιμοποιούν {_1}, που δεν υποστηρίζονται ακόμη στην ανάγνωση @msg its page has text, and its layers have no text objects to put it in Η σελίδα περιέχει κείμενο, αλλά τα επίπεδα δεν διαθέτουν αντικείμενα κειμένου για την τοποθέτησή του diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index f0e711730..a0346d906 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -3692,6 +3692,9 @@ axis Both Ambos @msg its editing data doesn't say where its artboard is sus datos de edición no indican dónde está su mesa de trabajo @msg its editing data has no layers sus datos de edición no tienen capas @msg its editing data is damaged sus datos de edición están dañados +@msg its zlib compression is damaged su compresión zlib está dañada +@msg its Zstandard compression is damaged su compresión Zstandard está dañada +@msg it decompresses to more than {_1} MB ocupan más de {_1} MB al descomprimirse @msg its editing data is for a different page sus datos de edición son de otra página @msg its layers use {_1}, which aren't read yet sus capas usan {_1}, que aún no se leen @msg its page has text, and its layers have no text objects to put it in su página tiene texto y sus capas no tienen objetos de texto donde ponerlo diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index ccfd2475c..bc17def6e 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -3924,6 +3924,9 @@ imageTrace Outline Ääriviiva @msg its editing data doesn't say where its artboard is sen muokkaustiedoista ei selviä piirtoalustan sijainti @msg its editing data has no layers sen muokkaustiedoissa ei ole tasoja @msg its editing data is damaged sen muokkaustiedot ovat vaurioituneet +@msg its zlib compression is damaged niiden zlib-pakkaus on vaurioitunut +@msg its Zstandard compression is damaged niiden Zstandard-pakkaus on vaurioitunut +@msg it decompresses to more than {_1} MB ne purkautuvat yli {_1} megatavuksi @msg its editing data is for a different page sen muokkaustiedot koskevat eri sivua @msg its layers use {_1}, which aren't read yet sen tasot käyttävät kohteita {_1}, joita ei vielä lueta @msg its page has text, and its layers have no text objects to put it in sen sivulla on tekstiä, mutta sen tasoilla ei ole tekstiobjekteja, joihin sen voisi sijoittaa diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index bd5aad78d..f298ca89f 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -3689,6 +3689,9 @@ axis Both Les deux @msg its editing data doesn't say where its artboard is ses données de modification n’indiquent pas où se trouve son plan de travail @msg its editing data has no layers ses données de modification n’ont pas de calques @msg its editing data is damaged ses données de modification sont endommagées +@msg its zlib compression is damaged leur compression zlib est endommagée +@msg its Zstandard compression is damaged leur compression Zstandard est endommagée +@msg it decompresses to more than {_1} MB elles dépassent {_1} Mo une fois décompressées @msg its editing data is for a different page ses données de modification concernent une autre page @msg its layers use {_1}, which aren't read yet ses calques utilisent {_1}, qui ne sont pas encore lus @msg its page has text, and its layers have no text objects to put it in sa page contient du texte, et ses calques n’ont aucun objet texte où le mettre diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index 9c74ce8d4..04e475de1 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -4537,6 +4537,9 @@ imageTrace Outline Körvonal @msg its editing data doesn't say where its artboard is a szerkesztési adataiból nem derül ki, hol van a rajztáblája @msg its editing data has no layers a szerkesztési adataiban nincsenek rétegek @msg its editing data is damaged a szerkesztési adatai sérültek +@msg its zlib compression is damaged a zlib-tömörítésük sérült +@msg its Zstandard compression is damaged a Zstandard-tömörítésük sérült +@msg it decompresses to more than {_1} MB kicsomagolva nagyobbak, mint {_1} MB @msg its editing data is for a different page a szerkesztési adatai egy másik oldalhoz tartoznak @msg its layers use {_1}, which aren't read yet a rétegei olyat használnak, ami még nem olvasható: {_1} @msg its objects are nested too deeply az objektumai túl mélyen egymásba ágyazottak diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index b993af7f8..5e9654052 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -3731,6 +3731,9 @@ axis Both Entrambi @msg its editing data doesn't say where its artboard is i suoi dati di modifica non indicano dove si trova la tavola da disegno @msg its editing data has no layers i suoi dati di modifica non hanno livelli @msg its editing data is damaged i suoi dati di modifica sono danneggiati +@msg its zlib compression is damaged la loro compressione zlib è danneggiata +@msg its Zstandard compression is damaged la loro compressione Zstandard è danneggiata +@msg it decompresses to more than {_1} MB una volta decompressi superano {_1} MB @msg its editing data is for a different page i suoi dati di modifica sono di un'altra pagina @msg its layers use {_1}, which aren't read yet i suoi livelli usano {_1}, che non vengono ancora letti @msg its page has text, and its layers have no text objects to put it in la sua pagina ha del testo e i suoi livelli non hanno oggetti di testo in cui metterlo diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index 02ad6f5ea..3c696bcb1 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -3790,6 +3790,9 @@ swatch Bright Violet ビビッドバイオレット @msg it has more than one artboard アートボードが複数あります @msg its editing data has no layers 編集データにレイヤーがありません @msg its editing data is damaged 編集データが破損しています +@msg its zlib compression is damaged zlib 圧縮が破損しています +@msg its Zstandard compression is damaged Zstandard 圧縮が破損しています +@msg it decompresses to more than {_1} MB 展開すると {_1} MB を超えます @msg its type is to come in as outlines, which only its page has 文字をアウトラインとして読み込む設定ですが、アウトラインはページにしかありません @msg {_1} files {_1} 個のファイル @msg A browser with WebGPU or WebGL2 is required. WebGPU または WebGL2 に対応したブラウザーが必要です。 diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index db95c3c61..219a0346b 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -3722,6 +3722,9 @@ theme Appearance 외관 @msg it has more than one artboard 아트보드가 둘 이상입니다 @msg its editing data has no layers 편집 데이터에 레이어가 없습니다 @msg its editing data is damaged 편집 데이터가 손상되었습니다 +@msg its zlib compression is damaged zlib 압축이 손상되었습니다 +@msg its Zstandard compression is damaged Zstandard 압축이 손상되었습니다 +@msg it decompresses to more than {_1} MB 압축을 풀면 {_1}MB를 넘습니다 @msg its type is to come in as outlines, which only its page has 문자를 윤곽선으로 가져오도록 되어 있으나, 이는 해당 페이지에만 있습니다 @msg {_1} files {_1} 파일 @msg A browser with WebGPU or WebGL2 is required. WebGPU 또는 WebGL2를 지원하는 브라우저가 필요합니다. diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index de3b18a52..c442450cb 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -3817,6 +3817,9 @@ imageTrace Outline Kontur @msg its editing data doesn't say where its artboard is jego dane edycyjne nie określają położenia obszaru kompozycji @msg its editing data has no layers jego dane edycyjne nie zawierają warstw @msg its editing data is damaged jego dane edycyjne są uszkodzone +@msg its zlib compression is damaged ich kompresja zlib jest uszkodzona +@msg its Zstandard compression is damaged ich kompresja Zstandard jest uszkodzona +@msg it decompresses to more than {_1} MB po rozpakowaniu przekraczają {_1} MB @msg its editing data is for a different page jego dane edycyjne dotyczą innej strony @msg its layers use {_1}, which aren't read yet jego warstwy używają {_1}, które nie są jeszcze odczytywane @msg its page has text, and its layers have no text objects to put it in jego strona zawiera tekst, a warstwy nie mają obiektów tekstowych, w których można go umieścić diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index 4802794d7..4584338ca 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -3283,6 +3283,9 @@ axis Both Ambos @msg its editing data doesn't say where its artboard is seus dados de edição não indicam onde fica a prancheta @msg its editing data has no layers seus dados de edição não têm camadas @msg its editing data is damaged seus dados de edição estão danificados +@msg its zlib compression is damaged sua compactação zlib está danificada +@msg its Zstandard compression is damaged sua compactação Zstandard está danificada +@msg it decompresses to more than {_1} MB eles se expandem para mais de {_1} MB @msg its editing data is for a different page seus dados de edição são de outra página @msg its layers use {_1}, which aren't read yet suas camadas usam {_1}, que ainda não são lidos @msg its page has text, and its layers have no text objects to put it in sua página tem texto, e suas camadas não têm objetos de texto para recebê-lo diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index 4cfe2fa1b..e23cd0497 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -3727,6 +3727,9 @@ axis Both Оба @msg its editing data doesn't say where its artboard is его данные для редактирования не указывают, где находится монтажная область @msg its editing data has no layers в его данных для редактирования нет слоёв @msg its editing data is damaged его данные для редактирования повреждены +@msg its zlib compression is damaged их сжатие zlib повреждено +@msg its Zstandard compression is damaged их сжатие Zstandard повреждено +@msg it decompresses to more than {_1} MB после распаковки они занимают более {_1} МБ @msg its editing data is for a different page его данные для редактирования относятся к другой странице @msg its layers use {_1}, which aren't read yet его слои используют {_1}, что пока не читается @msg its page has text, and its layers have no text objects to put it in на его странице есть текст, а на слоях нет текстовых объектов, куда его поместить diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index e40d70787..c1da49d51 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -3623,6 +3623,9 @@ axis Both Båda @msg its editing data doesn't say where its artboard is dess redigeringsdata anger inte var dess rityta finns @msg its editing data has no layers dess redigeringsdata har inga lager @msg its editing data is damaged dess redigeringsdata är skadade +@msg its zlib compression is damaged dess zlib-komprimering är skadad +@msg its Zstandard compression is damaged dess Zstandard-komprimering är skadad +@msg it decompresses to more than {_1} MB de packas upp till mer än {_1} MB @msg its editing data is for a different page dess redigeringsdata gäller en annan sida @msg its layers use {_1}, which aren't read yet dess lager använder {_1}, som inte kan läsas än @msg its page has text, and its layers have no text objects to put it in dess sida har text, och dess lager saknar textobjekt att placera den i diff --git a/crates/ui-egui/src/i18n/tests.rs b/crates/ui-egui/src/i18n/tests.rs index 2ea3a7444..fc4a563e9 100644 --- a/crates/ui-egui/src/i18n/tests.rs +++ b/crates/ui-egui/src/i18n/tests.rs @@ -1658,11 +1658,21 @@ fn unread_layers_notes_are_translated() { for note in [ "the file's layers weren't read (its editing data is for a different page): it comes in as its page, in one layer", "the file's layers weren't read from its editing data (its editing data is damaged): they come from its PDF part", + "the file's layers weren't read from its editing data (its zlib compression is damaged): they come from its PDF part", + "the file's layers weren't read from its editing data (its Zstandard compression is damaged): they come from its PDF part", + "the file's layers weren't read from its editing data (it decompresses to more than 256 MB): they come from its PDF part", ] { let status = format!("Opened with 1 note(s): {note}"); for code in COMPLETE_MESSAGES.iter().chain(&["pt-br"]) { let shown = message(Lang::from_code(code).unwrap(), &status); - assert!(!shown.contains("weren't read") && !shown.contains("its editing data") && !shown.contains("Opened with"), "{code}: {shown}"); + assert!( + !shown.contains("weren't read") + && !shown.contains("its editing data") + && !shown.contains("is damaged") + && !shown.contains("decompresses") + && !shown.contains("Opened with"), + "{code}: {shown}" + ); } } } diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index 3a482ddf5..f321407f7 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -3715,6 +3715,9 @@ axis Both Обидві @msg its editing data doesn't say where its artboard is його дані для редагування не вказують, де знаходиться монтажна область @msg its editing data has no layers у його даних для редагування немає шарів @msg its editing data is damaged його дані для редагування пошкоджено +@msg its zlib compression is damaged їхнє стиснення zlib пошкоджено +@msg its Zstandard compression is damaged їхнє стиснення Zstandard пошкоджено +@msg it decompresses to more than {_1} MB після розпакування вони займають понад {_1} МБ @msg its editing data is for a different page його дані для редагування відносяться до іншої сторінки @msg its layers use {_1}, which aren't read yet його шари використовують {_1}, що поки не читається @msg its page has text, and its layers have no text objects to put it in на його сторінці є текст, а на шарах немає текстових об’єктів, куди його помістити diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index 0e8be780b..7b724c957 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -4182,6 +4182,9 @@ theme Appearance Giao diện @msg its editing data doesn't say where its artboard is dữ liệu chỉnh sửa không cho biết vị trí bảng vẽ @msg its editing data has no layers dữ liệu chỉnh sửa không có lớp @msg its editing data is damaged dữ liệu chỉnh sửa bị hỏng +@msg its zlib compression is damaged phần nén zlib của chúng bị hỏng +@msg its Zstandard compression is damaged phần nén Zstandard của chúng bị hỏng +@msg it decompresses to more than {_1} MB giải nén ra hơn {_1} MB @msg its editing data is for a different page dữ liệu chỉnh sửa dành cho trang khác @msg its layers use {_1}, which aren't read yet các lớp của nó dùng {_1}, chưa đọc được @msg its objects are nested too deeply các đối tượng của nó lồng nhau quá sâu diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index 16774fc4f..64d7f364d 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -3548,6 +3548,9 @@ imageTrace Outline 轮廓 @msg it has more than one artboard 它有多个画板 @msg its editing data has no layers 其编辑数据中没有图层 @msg its editing data is damaged 其编辑数据已损坏 +@msg its zlib compression is damaged 其 zlib 压缩已损坏 +@msg its Zstandard compression is damaged 其 Zstandard 压缩已损坏 +@msg it decompresses to more than {_1} MB 解压后超过 {_1} MB @msg its type is to come in as outlines, which only its page has 其文字需要以轮廓导入,而只有其页面中包含这些轮廓 @msg {_1} files {_1} 文件 @msg A browser with WebGPU or WebGL2 is required. 需要支持 WebGPU 或 WebGL2 的浏览器。 diff --git a/docs/mcp.md b/docs/mcp.md index f2e68d4f0..433b03f31 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -606,7 +606,7 @@ document, whole and editable: point type, area type (a story in several frames a with its fonts, size, leading, tracking, scaling, baseline shift, fill and stroke (grey, RGB or CMYK), opacity, blend mode, alignment, indents and paragraph spacing. Where the text document can't say, type that shows is the page's, in its text object's place. A text object on a layer that shows and doesn't print keeps the type the text document makes for it, on that -layer, even where the page doesn't draw it. A file whose editing data has symbols, +layer, even where the page doesn't draw it. A file whose editing data can't be decompressed or has symbols, pattern fills, placed files or anything else the reader doesn't read on a layer that shows, or whose layers look different from its page, opens as before: an EPS as its printed page, a `.ai` file from its PDF content (where plain groups open ungrouped), with a warning saying why. Layers look different when they draw an object the page doesn't (or diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 73553937e..2f1cd89f8 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -692,6 +692,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur A PDF saved with Illustrator's editing data opens through it as a PDF-compatible `.ai` does, with its layers and groups and with art reaching past its page unclipped; such a PDF holds the data in `AIPDFPrivateData` streams (part of #868). A shape a PDF or `.ai` paints with a gradient (a clip path round a shading) opens as one path filled with the gradient, not a clip group round a rectangle, so Shape Builder, Live Paint and Pathfinder work on its outline (#1304). In a `.ai` opened from its editing data, a text object on a layer that shows and doesn't print stays on that layer, made from the file's text document, even where the page doesn't draw it (`crates/eps/src/import/tests_native.rs`). Such a `.ai` opens without the PDF part's warnings that text was kept as outlines when no outlined glyphs come in, and a warning counts the text objects that hold the page's type next to glyphs the page has only as outlines, which don't come in (`crates/engine/src/cmd/fileio/tests_aiimport.rs`). Where the art of a `.ai` or EPS opened from its editing data differs a little from the file's page, the warning gives the share of the page that differs away from type. When that share is 0.2% or less, the warning names the type and gives the share of the page that differs where the type is (`crates/eps/src/import/tests_native.rs`). A PDF page whose `UserUnit` (PDF 1.6) is above 1 opens at its size in points, its numbers times the `UserUnit` (one above 75,000 counts as 75,000), and `document.pdfInfo` gives its size and boxes in points too. A `.ai` whose editing data has its artboards in the units of such a page opens from that data at the page's size, with its layers, and when the layers can't be read its pages still take the artboard layout of the data, scaled the same way (part of #1482). + A `.ai`, or a PDF saved with Illustrator's editing data, whose editing data can't be decompressed opens from its PDF part with a warning that its layers weren't read and why, and an export or Save As to its own path asks first (`acknowledgeLoss`), as for a file whose layers the reader can't read (`crates/eps/src/import/tests_native.rs`, `crates/engine/src/cmd/fileio/tests_aiimport.rs`). PDF export and `.ai` saves write a page with a side longer than 14,400 pt with a `UserUnit` (PDF 1.6), its longer side divided by 14,400 and rounded up to the next thousandth, so that no side is longer than 14,400 units (ISO 32000-1, Annex C.2); in PDF 1.3 to 1.5 files and PDF/X files such a page is written in points. Placed (linked) files in a `.ai`'s editing data come in as linked images in their place, each showing the low-resolution preview the file keeps of it until its file is found or relinked in the Links panel (a file found next to the document or at its path is read as the document opens). A file with them no longer falls back to its PDF part, so its guides, sublayers and hidden objects open (#1264; `crates/eps/src/import/ai/tests.rs`, `crates/engine/src/tests_links.rs`). In a file opened that way, a gradient with stops in the middle runs its whole ramp across its shape and a gradient object no longer passes its fill to the next path (#1390); text objects with an appearance keep their own text and frame, type of hidden layers stays hidden, and type on later artboards lands on its text objects (#1391); area type whose frame is too short for a line of the fallback font shows its first line, the frame made taller with a warning in the open result (#1392). - EPS and DXF in and out, EMF/WMF in and out; PNG (with PNG-8), JPEG, WebP, GIF, TIFF, BMP, Targa and layered PSD export; grayscale TIFFs with an alpha channel or other extra channels place, relink and open, the alpha as the image's transparency (#1466 in part; CCITT Group 3 and planar files with extra channels are not read); PSD and PSB files open and place, linked or embedded, as their merged image in every colour mode and depth (#918). EPS import runs a PostScript Level 3 interpreter written from the PostScript Language Reference: resource categories and `resourceforall`, local and global VM (`currentglobal`/`setglobal`/`gcheck`), `clipsave`/`cliprestore`, subarrays and substrings that share storage, the standard dictionaries in `systemdict`, `dictstack`, `internaldict`, executable filters and strings, `load` by any key, `//name` immediate names, `token`, `execform`, user paths, `cshow`, `rootfont`, `cachestatus`, colour rendering and halftone queries, file `status` and data read with `flushfile`, and `pdfmark` marks dropped (Discord feedback, #466, #469, #474). Masked images (`ImageType 3` in its three interleavings) and reusable streams are read, and an image's several data sources are read in turn, as the reference defines (procedures that read one file get their own rows: such images came in banded, #498). `restore` puts dictionaries back as they were at the `save`, so programs that count their saves in a dictionary read to their end (part of #505). Tiling patterns come in as pattern swatches, uncoloured ones too; Type 3 fonts' glyphs as their outlines, spaced by `ashow`/`widthshow`/`xshow`/`kshow`; mesh and function-based shadings (types 1, 4–7) as gradient meshes; sampled functions (type 0) and Flate/LZW predictors are read. It is tested with programs written from the specification in the ways common generators use these features; Inkscape/cairo EPS files open as vectors. When a file's PostScript still can't be read, it opens as its TIFF preview, its Windows metafile preview (as vectors) or its EPSI preview, and the warning (in the status bar, `document.open`/`app.open`/MCP `open_file` replies and `vectorcraft-cli info`) names the error, the operator that raised it and the procedures it ran in. Legacy PostScript `.ai` files (Illustrator 3–8, written with the prolog that defines their operators) keep their groups and nested groups (`u` … `U`), between and inside clipping groups; EPS and EMF/WMF import nest clips (and groups) at most 128 deep, so a hostile file can't exhaust the stack. DXF TEXT justified Fit or Aligned spans its two points (stretched, or scaled as a whole). PDF export writes type as real, searchable text in embedded subset fonts by default, as Illustrator's PDF presets do (Convert Text to Outlines is an option; printer's marks stay shapes), so text-heavy PDFs stay small (#733). DXF export writes each fill as its outlines followed by its hatch, so laser and cutter software, which skips hatches, gets filled art and type too (#546).