mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-10 01:50:22 -06:00
.ai import: a file whose PDF part can't be read opens from its editing data
Squashed from #5051 onto main.
This commit is contained in:
@@ -200,6 +200,20 @@ fn from_editing_data_alone(bytes: &[u8], opts: &LoadOptions) -> Option<Result<(D
|
||||
)
|
||||
}
|
||||
|
||||
/// A `.ai`, or a PDF saved with the editing data, whose PDF part can't be read (`e`, the import's
|
||||
/// error) from the editor's own copy of its art alone, as a file saved without PDF compatibility
|
||||
/// is read, its first note saying why: `None` when pages are picked, layers or the editing data
|
||||
/// are off, or the file has no editing data that reads. Its type is outlines when `opts` asks.
|
||||
fn without_pdf_part(bytes: &[u8], opts: &LoadOptions, e: &EngineError) -> Option<(Document, Vec<String>)> {
|
||||
if opts.pages.is_some() || !opts.layers || !opts.editing_data {
|
||||
return None;
|
||||
}
|
||||
let private = vectorcraft_pdf::illustrator_data_without_pdf_part(bytes, opts.password.as_deref())?;
|
||||
let (doc, warnings) = vectorcraft_eps::ai_alone_on_page(&private.data, private.trim, private.user_unit).ok()?;
|
||||
let notes = std::iter::once(format!("the PDF part couldn't be read ({e}): the file opened from its editing data")).chain(warnings).collect();
|
||||
Some(outlined_if_asked(doc, notes, opts))
|
||||
}
|
||||
|
||||
/// Is `w` a note on the PDF part as a page, which says nothing was skipped: that the art off the
|
||||
/// artboards is in the editing data alone, or that art past the page was clipped to it?
|
||||
fn is_page_note(w: &str) -> bool {
|
||||
@@ -255,22 +269,29 @@ fn every_page(bytes: &[u8], opts: &LoadOptions) -> bool {
|
||||
vectorcraft_pdf::info(bytes, opts.password.as_deref()).is_ok_and(|i| parse_range(range, i.pages.len()).is_ok_and(|p| p.len() == i.pages.len()))
|
||||
}
|
||||
|
||||
/// A page read from a PDF or `.ai` (`read`, as [`super::page_document`] reads it) → (document,
|
||||
/// notes, false); for a `.ai` saved without PDF compatibility (its page only a placeholder), the
|
||||
/// file `name` read as `document.open` reads it with `password`, from its editing copy alone →
|
||||
/// (document, notes, true).
|
||||
/// Page `page_index` (0-based) read from a PDF or `.ai` (`read`, as [`super::page_document`] reads
|
||||
/// it) → (document, notes, false); for a `.ai` saved without PDF compatibility (its page only a
|
||||
/// placeholder), the file `name` read as `document.open` reads it with `opts`' password, from its
|
||||
/// editing copy alone → (document, notes, true). So too, read with `opts` but every page, for the
|
||||
/// first page of a `.ai` or PDF whose PDF part can't be read and whose editing copy reads; the
|
||||
/// error of any other page stays.
|
||||
pub(crate) fn page_or_editing_data(
|
||||
name: &str,
|
||||
bytes: &[u8],
|
||||
password: Option<&str>,
|
||||
opts: &LoadOptions,
|
||||
page_index: usize,
|
||||
read: Result<(Document, Vec<String>)>,
|
||||
) -> Result<(Document, Vec<String>, bool)> {
|
||||
match read {
|
||||
Ok((doc, notes)) => Ok((doc, notes, false)),
|
||||
Err(e) if is_placeholder(&e) => {
|
||||
let l = load_with(name, bytes, &LoadOptions { password: password.map(str::to_string), ..Default::default() })?;
|
||||
let l = load_with(name, bytes, &LoadOptions { password: opts.password.clone(), ..Default::default() })?;
|
||||
Ok((l.doc, l.warnings, true))
|
||||
}
|
||||
Err(e) if page_index == 0 => match load_with(name, bytes, &LoadOptions { pages: None, ..opts.clone() }) {
|
||||
Ok(l) if l.editing_data => Ok((l.doc, l.warnings, true)),
|
||||
_ => Err(e),
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
@@ -345,7 +366,9 @@ pub fn load_with(name: &str, bytes: &[u8], opts: &LoadOptions) -> Result<Loaded>
|
||||
.map(|(doc, warnings)| outlined_if_asked(doc, warnings, opts))
|
||||
.inspect(|_| editing_data = true)
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
// A PDF part that can't be read (a stream the import refuses): the editing copy
|
||||
// alone where it reads, else the error.
|
||||
Err(e) => without_pdf_part(bytes, opts, &e).inspect(|_| editing_data = true).ok_or(e),
|
||||
})?
|
||||
}
|
||||
"dxf" => {
|
||||
|
||||
@@ -348,6 +348,167 @@ fn editing_data_that_cant_be_read_keeps_the_pdf_parts_notes() {
|
||||
assert_eq!(r["warnings"], json!([vectorcraft_pdf::PAST_PAGE_NOTE, unread]), "{r}");
|
||||
}
|
||||
|
||||
/// Decode parameters the import refuses, for the page's content stream (#4366).
|
||||
const REFUSED_CONTENT: &str = "/DecodeParms << /Predictor 12 /Columns 0 >>";
|
||||
|
||||
/// A `.ai` and a PDF saved with the editing data, carrying `private`, whose page's content stream
|
||||
/// the import refuses.
|
||||
fn refused_files(private: &[u8], page: &str) -> [(&'static str, Vec<u8>); 2] {
|
||||
[("art.ai", ai::ai_with_content(private, page, REFUSED_CONTENT)), ("art.pdf", ai::pdf_with_content(private, page, REFUSED_CONTENT))]
|
||||
}
|
||||
|
||||
/// The note of a file opened from its editing data because its PDF part fails with `e`.
|
||||
fn unread_pdf_part(e: &str) -> String {
|
||||
format!("the PDF part couldn't be read ({e}): the file opened from its editing data")
|
||||
}
|
||||
|
||||
/// #4366: a `.ai`, or a PDF saved with the editing data, whose PDF part can't be read opens from
|
||||
/// its editing data, with a first note saying why. Pages picked, layers off or the editing data
|
||||
/// off keep the error, and so does editing data that can't be read.
|
||||
#[test]
|
||||
fn a_file_whose_pdf_part_cant_be_read_opens_from_its_editing_data() {
|
||||
let mut s = Session::new();
|
||||
for (name, file) in refused_files(&compressed(&ai::sample_data()), ai::page_pdf()) {
|
||||
let e = open(&mut s, name, &file, json!({"editingData": false})).unwrap_err().to_string();
|
||||
assert!(e.contains("a page's content in it (object 4) has decode parameters that can't be applied"), "{name}: {e}");
|
||||
let r = open(&mut s, name, &file, json!({})).unwrap();
|
||||
assert_eq!(r["format"], json!(&name[4..]), "{name}: {r}");
|
||||
assert_eq!(r["warnings"], json!([unread_pdf_part(&e)]), "{name}: {r}");
|
||||
check_sample(&s);
|
||||
assert!(load(name, &file).unwrap().editing_data, "{name}");
|
||||
for p in [json!({"pages": "1"}), json!({"layers": false})] {
|
||||
assert_eq!(open(&mut s, name, &file, p.clone()).unwrap_err().to_string(), e, "{name}: {p}");
|
||||
}
|
||||
}
|
||||
for (name, file) in refused_files(b"%AI12_CompressedData not zlib", ai::page_pdf()) {
|
||||
let plain = open(&mut s, name, &file, json!({"editingData": false})).unwrap_err().to_string();
|
||||
assert_eq!(open(&mut s, name, &file, json!({})).unwrap_err().to_string(), plain, "{name}");
|
||||
}
|
||||
}
|
||||
|
||||
/// A file saved in units of 10 points (`/UserUnit 10`, as in
|
||||
/// `an_ai_file_with_a_user_unit_opens_at_its_size_in_points`) whose PDF part can't be read opens
|
||||
/// from its editing data at its size in points.
|
||||
#[test]
|
||||
fn a_file_whose_pdf_part_cant_be_read_opens_at_its_size_in_points() {
|
||||
let data = ai::editing_data(200.0, 100.0, &ai::layer("Art", &format!("0 1 1 0 1 0 0 Xa\n{}f\n", ai::rect(20.0, 20.0, 60.0, 50.0))));
|
||||
let file = ai::ai_with_entries_and_content(&compressed(data.as_bytes()), "1 0 0 rg 20 20 40 30 re f", "/UserUnit 10", REFUSED_CONTENT);
|
||||
let near = |r: vectorcraft_geom::Rect, want: [f64; 4]| [r.x0, r.y0, r.x1, r.y1].iter().zip(want).all(|(a, b)| (a - b).abs() < 0.01);
|
||||
let mut s = Session::new();
|
||||
let r = open(&mut s, "large.ai", &file, json!({})).unwrap();
|
||||
assert!(r["warnings"][0].as_str().unwrap().starts_with("the PDF part couldn't be read ("), "{r}");
|
||||
let doc = &s.doc().unwrap().doc;
|
||||
assert!(near(doc.artboards[0].rect, [0.0, 0.0, 2000.0, 1000.0]), "{:?}", doc.artboards[0].rect);
|
||||
let art = doc.art_bounds().unwrap();
|
||||
assert!(near(art, [200.0, 500.0, 600.0, 800.0]), "{art:?}");
|
||||
// On a page turned a quarter, the same.
|
||||
let file = ai::ai_with_entries_and_content(&compressed(data.as_bytes()), "", "/UserUnit 10 /Rotate 90", REFUSED_CONTENT);
|
||||
open(&mut s, "turned.ai", &file, json!({})).unwrap();
|
||||
assert!(near(s.doc().unwrap().doc.artboards[0].rect, [0.0, 0.0, 2000.0, 1000.0]), "{:?}", s.doc().unwrap().doc.artboards[0].rect);
|
||||
// A page cropped to the art: its box has the art's size in those units.
|
||||
let art = format!("0 1 1 0 1 0 0 Xa\n{}f\n", ai::rect(20.0, 20.0, 60.0, 50.0));
|
||||
let cropped = ai::editing_data_in([20.0, 20.0, 60.0, 50.0], [0.0, 0.0, 200.0, 100.0], &ai::layer("Art", &art));
|
||||
let file = ai::ai_with_entries_and_content(&compressed(cropped.as_bytes()), "", "/UserUnit 10 /TrimBox [20 20 60 50]", REFUSED_CONTENT);
|
||||
open(&mut s, "cropped.ai", &file, json!({})).unwrap();
|
||||
let doc = &s.doc().unwrap().doc;
|
||||
assert!(near(doc.artboards[0].rect, [0.0, 0.0, 2000.0, 1000.0]), "{:?}", doc.artboards[0].rect);
|
||||
assert!(near(doc.art_bounds().unwrap(), [200.0, 500.0, 600.0, 800.0]), "{:?}", doc.art_bounds());
|
||||
// Saved in points, it opens as it is.
|
||||
let file = ai::ai_with_entries_and_content(&compressed(data.as_bytes()), "", "", REFUSED_CONTENT);
|
||||
open(&mut s, "points.ai", &file, json!({})).unwrap();
|
||||
assert!(near(s.doc().unwrap().doc.artboards[0].rect, [0.0, 0.0, 200.0, 100.0]));
|
||||
}
|
||||
|
||||
/// Type asked for as outlines in a file whose PDF part can't be read: the editing data's type,
|
||||
/// outlined, as for a file saved without its PDF part.
|
||||
#[test]
|
||||
fn a_file_whose_pdf_part_cant_be_read_outlines_its_type_when_asked() {
|
||||
let [(name, file), _] = refused_files(&typed_data(), "");
|
||||
let mut s = Session::new();
|
||||
let r = open(&mut s, name, &file, json!({})).unwrap();
|
||||
assert_eq!(texts_and_paths(&s).0, 1, "by default the editing data's type is type: {r}");
|
||||
let r = open(&mut s, name, &file, json!({"textAs": "outlines"})).unwrap();
|
||||
let (texts, paths) = texts_and_paths(&s);
|
||||
assert_eq!(texts, 0, "no type left: {r}");
|
||||
assert!(paths > 0, "the outlines are there: {r}");
|
||||
}
|
||||
|
||||
/// An image too large to read from the editing data of a file whose PDF part can't be read is
|
||||
/// left out, and both notes say so.
|
||||
#[test]
|
||||
fn a_file_whose_pdf_part_cant_be_read_notes_an_image_left_out() {
|
||||
let data = String::from_utf8_lossy(&ai::sample_data()).replace("] 0 0 2 1 2 1 8 3", "] 0 0 6000 6000 6000 6000 8 3");
|
||||
let [(name, file), _] = refused_files(&compressed(data.as_bytes()), ai::page_pdf());
|
||||
let mut s = Session::new();
|
||||
let e = open(&mut s, name, &file, json!({"editingData": false})).unwrap_err().to_string();
|
||||
let r = open(&mut s, name, &file, json!({})).unwrap();
|
||||
assert_eq!(r["warnings"], json!([unread_pdf_part(&e), "an image too large to read from the editing data was left out"]), "{r}");
|
||||
}
|
||||
|
||||
/// A copied file whose PDF part can't be read pastes the objects of its editing data when asked
|
||||
/// for them, with the note; without `editingData` its page can't be read.
|
||||
#[test]
|
||||
fn a_copied_file_whose_pdf_part_cant_be_read_pastes_from_its_editing_data() {
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", &json!({"width": 400, "height": 300})).unwrap();
|
||||
for (name, file) in refused_files(&compressed(&ai::sample_data()), ai::page_pdf()) {
|
||||
let (r, pasted) = paste(&mut s, &file, true);
|
||||
assert_eq!(r["count"], 7, "{name}: {r}");
|
||||
let pasted: Vec<String> = pasted.iter().map(|n| n.name.clone().unwrap_or_default()).collect();
|
||||
assert_eq!(pasted, ["Pair", "Ring", "Window", "Shaded", "", "Sub", ""], "{name}");
|
||||
assert!(r["warnings"][0].as_str().unwrap().starts_with("the PDF part couldn't be read (cannot read PDF: "), "{name}: {r}");
|
||||
let p = json!({"dataBase64": vectorcraft_format::base64_encode(&file)});
|
||||
let e = s.execute("clipboard.importPdf", &p).unwrap_err().to_string();
|
||||
assert!(e.contains("decode parameters"), "{name}: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
/// A file whose PDF part can't be read places its first artboard from its editing data, with the
|
||||
/// note; any other page keeps the error.
|
||||
#[test]
|
||||
fn a_file_whose_pdf_part_cant_be_read_places_from_its_editing_data() {
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", &json!({})).unwrap();
|
||||
for (name, file) in refused_files(&compressed(&ai::sample_data()), ai::page_pdf()) {
|
||||
let data = vectorcraft_format::base64_encode(&file);
|
||||
let r = s.execute("file.place", &json!({"name": name, "dataBase64": data, "link": false})).unwrap();
|
||||
assert_eq!((r["width"].as_f64(), r["height"].as_f64()), (Some(200.0), Some(100.0)), "{name}: {r}");
|
||||
assert!(r["warnings"][0].as_str().unwrap().starts_with("the PDF part couldn't be read ("), "{name}: {r}");
|
||||
let placed = s.doc().unwrap().doc.layers[0].children().unwrap().last().unwrap().clone();
|
||||
assert_eq!(names(&placed.children().unwrap()[1..]), ["Pair", "Ring", "Window"], "{name}: after the clipping path");
|
||||
for off in [json!({"editingData": false}), json!({"layers": false})] {
|
||||
let mut p = json!({"name": name, "dataBase64": data, "link": false});
|
||||
p.as_object_mut().unwrap().extend(off.as_object().unwrap().clone());
|
||||
let e = s.execute("file.place", &p).unwrap_err().to_string();
|
||||
assert!(e.contains("decode parameters"), "{name}: {off}: {e}");
|
||||
}
|
||||
}
|
||||
let [(name, file), _] = refused_files(&typed_data(), "");
|
||||
let data = vectorcraft_format::base64_encode(&file);
|
||||
let r = s.execute("file.place", &json!({"name": name, "dataBase64": data, "link": false, "textAs": "outlines", "crop": "bounding"})).unwrap();
|
||||
assert!(r["warnings"][0].as_str().unwrap().starts_with("the PDF part couldn't be read ("), "{r}");
|
||||
let placed = s.doc().unwrap().doc.layers[0].children().unwrap().last().unwrap().clone();
|
||||
let mut texts = 0;
|
||||
placed.walk(&mut |n| texts += usize::from(matches!(n.kind, NodeKind::Text(_))));
|
||||
assert_eq!(texts, 0, "placed with its type outlined: {r}");
|
||||
// Two artboards: the second can't be placed.
|
||||
let boards = "%_/Document :\n%_/Dictionary :\n%_/Array :\n\
|
||||
%_/Dictionary :\n%_0 100 /RealPointRelToROrigin\n%_ (PositionPoint1) ,\n%_100 0 /RealPointRelToROrigin\n%_ (PositionPoint2) ,\n%_; ,\n\
|
||||
%_/Dictionary :\n%_0 -50 /RealPointRelToROrigin\n%_ (PositionPoint1) ,\n%_100 -150 /RealPointRelToROrigin\n%_ (PositionPoint2) ,\n%_; ,\n\
|
||||
%_; (ArtboardArray) ,\n%_; /NotRecorded ,\n%_;\n";
|
||||
let art = format!("0 g\n{}f\n{}f\n", ai::rect(10.0, 10.0, 50.0, 50.0), ai::rect(10.0, -140.0, 70.0, -100.0));
|
||||
let paged = ai::editing_data(100.0, 100.0, &(boards.to_string() + &ai::layer("Art", &art)));
|
||||
let [(name, file), _] = refused_files(&compressed(paged.as_bytes()), ai::page_pdf());
|
||||
let data = vectorcraft_format::base64_encode(&file);
|
||||
let size = |s: &mut Session, page: u64| {
|
||||
let r = s.execute("file.place.info", &json!({"name": name, "dataBase64": data, "page": page, "crop": "bounding"}));
|
||||
r.map(|r| (r["width"].as_f64(), r["height"].as_f64())).map_err(|e| e.to_string())
|
||||
};
|
||||
assert_eq!(size(&mut s, 1), Ok((Some(40.0), Some(40.0))));
|
||||
let second = size(&mut s, 2);
|
||||
assert!(second.as_ref().is_err_and(|e| e.contains("decode parameters")), "{second:?}");
|
||||
}
|
||||
|
||||
/// A copied `.ai` whose editing data can't be read, or isn't there, pastes its page as a PDF
|
||||
/// does: the same objects as without `editingData`.
|
||||
#[test]
|
||||
|
||||
@@ -284,7 +284,8 @@ fn load(p: &Value, cmd: &str, board: Option<Rect>, link_pdf: bool) -> Result<Loa
|
||||
/// one) as art to place: clipped to the box `opts.crop` names, or (`bounding`) the bounds of its
|
||||
/// art; read with `opts.password`. Only the page placed is read. A .ai saved without PDF
|
||||
/// compatibility is read from its editing data, as it opens, and placed as an EPS is: the art on
|
||||
/// artboard `page`, its linked images found from its folder.
|
||||
/// artboard `page`, its linked images found from its folder. So is page 1 of a .ai or PDF whose
|
||||
/// PDF part can't be read.
|
||||
pub(crate) fn pdf_art(
|
||||
src: &str,
|
||||
bytes: &[u8],
|
||||
@@ -306,8 +307,7 @@ pub(crate) fn pdf_art(
|
||||
{
|
||||
read = Err(e);
|
||||
}
|
||||
let (mut doc, warnings, alone) =
|
||||
fileio::page_or_editing_data(src, bytes, o.password.as_deref(), read).map_err(|e| bad(cmd, format!("{name}: {e}")))?;
|
||||
let (mut doc, warnings, alone) = fileio::page_or_editing_data(src, bytes, &o, index, read).map_err(|e| bad(cmd, format!("{name}: {e}")))?;
|
||||
if alone {
|
||||
// Linked images show their files, found from its folder.
|
||||
super::links::resolve(&mut doc, path, false, false);
|
||||
|
||||
@@ -44,15 +44,17 @@ pub(crate) fn is_pdf(name: &str, bytes: &[u8]) -> bool {
|
||||
fileio::detect(name, bytes).is_some_and(|f| matches!(f.id, "pdf" | "ai" | "ait")) && !vectorcraft_pdf::is_postscript(bytes)
|
||||
}
|
||||
|
||||
/// The editing data of the `.ai` file `name` (`bytes`) when its PDF part is only a placeholder:
|
||||
/// The editing data of the `.ai` file `name` (`bytes`) when its PDF part is only a placeholder, or
|
||||
/// can't be read:
|
||||
/// the document its pages are read from ([`super::pdf_art`]), with the files it links. `None` for
|
||||
/// other files.
|
||||
pub(crate) fn editing_data(name: &str, bytes: &[u8]) -> Option<Document> {
|
||||
if !is_pdf(name, bytes) {
|
||||
return None;
|
||||
}
|
||||
let read = fileio::page_document(bytes, 0, &fileio::LoadOptions::default());
|
||||
match fileio::page_or_editing_data(name, bytes, None, read) {
|
||||
let opts = fileio::LoadOptions::default();
|
||||
let read = fileio::page_document(bytes, 0, &opts);
|
||||
match fileio::page_or_editing_data(name, bytes, &opts, 0, read) {
|
||||
Ok((doc, _, true)) => Some(doc),
|
||||
_ => None,
|
||||
}
|
||||
|
||||
@@ -2678,6 +2678,39 @@ fn zstd(text: &str) -> Vec<u8> {
|
||||
ruzstd::encoding::compress_to_vec(text.as_bytes(), ruzstd::encoding::CompressionLevel::Fastest)
|
||||
}
|
||||
|
||||
/// A one-page `.ai` whose page's content stream has decode parameters the import refuses (#4366),
|
||||
/// carrying `private` in one stream with the entries `filter`, under the key `{key}1` of its
|
||||
/// `PieceInfo`; with an object stream of entries `object_stream` when they aren't empty.
|
||||
fn refused_ai(private: &[u8], key: &str, filter: &str, object_stream: &str) -> Vec<u8> {
|
||||
let stream = |dict: &str, data: &[u8]| [format!("<< {dict} /Length {} >>\nstream\n", data.len()).as_bytes(), data, b"\nendstream"].concat();
|
||||
let mut objects: Vec<Vec<u8>> = vec![
|
||||
b"<< /Type /Catalog /Pages 2 0 R >>".to_vec(),
|
||||
b"<< /Type /Pages /Kids [3 0 R] /Count 1 >>".to_vec(),
|
||||
format!("<< /Type /Page /Parent 2 0 R /MediaBox [0 0 100 100] /Contents 4 0 R /PieceInfo << /Illustrator << /Private << /{key}1 5 0 R /NumBlock 1 >> >> >> >>")
|
||||
.into_bytes(),
|
||||
stream("/DecodeParms << /Predictor 12 /Columns 0 >>", b"0 g 10 10 20 20 re f"),
|
||||
stream(filter, private),
|
||||
];
|
||||
if !object_stream.is_empty() {
|
||||
objects.push(stream(object_stream, b"8 0 << >>"));
|
||||
}
|
||||
let mut out = b"%PDF-1.5\n".to_vec();
|
||||
let mut offsets = vec![];
|
||||
for (i, o) in objects.iter().enumerate() {
|
||||
offsets.push(out.len());
|
||||
out.extend(format!("{} 0 obj\n", i + 1).bytes());
|
||||
out.extend_from_slice(o);
|
||||
out.extend_from_slice(b"\nendobj\n");
|
||||
}
|
||||
let xref = out.len();
|
||||
out.extend(format!("xref\n0 {}\n0000000000 65535 f \n", objects.len() + 1).bytes());
|
||||
for off in offsets {
|
||||
out.extend(format!("{off:010} 00000 n \n").bytes());
|
||||
}
|
||||
out.extend(format!("trailer\n<< /Size {} /Root 1 0 R >>\nstartxref\n{xref}\n%%EOF\n", objects.len() + 1).bytes());
|
||||
out
|
||||
}
|
||||
|
||||
/// An EPS drawing a square whose private data holds `editing`.
|
||||
fn editing_eps(editing: &str) -> Vec<u8> {
|
||||
let lines: Vec<String> = ascii85(&zstd(editing)).as_bytes().chunks(60).map(|c| format!("%{}", String::from_utf8_lossy(c))).collect();
|
||||
@@ -3004,6 +3037,55 @@ proptest! {
|
||||
survive("mutated .ai editing data", || Some(vectorcraft_eps::layered_ai(&private, Document::new(100.0, 100.0), vec![], false, false, 1.0).0))?;
|
||||
}
|
||||
|
||||
/// A `.ai` whose PDF part can't be read (#4366), its private data damaged or hostile, under
|
||||
/// any key and filter: it opens from its editing data, or with the error of its PDF part.
|
||||
#[test]
|
||||
fn ai_whose_pdf_part_cant_be_read_never_panics(
|
||||
cut in 0usize..3_000,
|
||||
edits in prop::collection::vec((0usize..3_000, prop::sample::select(vec!['0', '9', '-', ' ', '\n', '(', ')', '/', '%', 'u', 'U', 'L'])), 0..8),
|
||||
junk in prop::collection::vec(any::<u8>(), 0..64),
|
||||
key in prop::sample::select(vec!["AIPrivateData", "AIPDFPrivateData", "AIPrivateData2", "Other"]),
|
||||
filter in prop::sample::select(vec!["", "/Filter /FlateDecode", "/Filter /Bogus", "/Filter [/FlateDecode /FlateDecode]", "/DP << /Predictor 2 >>", "/Filter 7"]),
|
||||
) {
|
||||
let text = mutate_text(&editing_text(), cut, &edits);
|
||||
let private = [b"%AI24_ZStandard_Data".as_slice(), &zstd(&text), &junk].concat();
|
||||
let bytes = refused_ai(&private, key, filter, "");
|
||||
let plain = vectorcraft_engine::cmd::fileio::LoadOptions { editing_data: false, ..Default::default() };
|
||||
let page_error = vectorcraft_engine::cmd::fileio::load_with("x.ai", &bytes, &plain).err().map(|e| e.to_string());
|
||||
prop_assert!(page_error.is_some());
|
||||
let _ = catch_quiet(|| vectorcraft_pdf::illustrator_data_without_pdf_part(&bytes, None)).expect("the private data read panicked");
|
||||
let mut opened = None;
|
||||
survive("a .ai whose PDF part can't be read", || {
|
||||
let l = vectorcraft_engine::cmd::fileio::load("x.ai", &bytes);
|
||||
opened = Some(l.as_ref().map(|l| l.editing_data).map_err(|e| e.to_string()));
|
||||
l.ok().map(|l| l.doc)
|
||||
})?;
|
||||
match opened {
|
||||
Some(Ok(editing)) => prop_assert!(editing),
|
||||
Some(Err(e)) => prop_assert_eq!(Some(e), page_error),
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// The same file with a damaged table of objects and an object stream the reader would fail
|
||||
/// on: refused, as before.
|
||||
#[test]
|
||||
fn ai_with_a_hostile_object_stream_is_refused(damage in prop::collection::vec((0usize..200, any::<u8>()), 0..6)) {
|
||||
let private = [&b"%AI12_CompressedData"[..], &deflated(&vectorcraft_testkit::ai::sample_data())].concat();
|
||||
let mut bytes = refused_ai(&private, "AIPrivateData", "/Filter /FlateDecode", "/Type /ObjStm /N 1 /First 4 /DecodeParms << /Predictor 12 /Columns 0 >>");
|
||||
let table = bytes.windows(5).rposition(|w| w == b"xref\n").unwrap_or(0);
|
||||
for (at, b) in damage {
|
||||
let n = bytes.len() - table;
|
||||
if let Some(x) = bytes.get_mut(table + at % n) {
|
||||
*x = b;
|
||||
}
|
||||
}
|
||||
let read = catch_quiet(|| vectorcraft_pdf::illustrator_data_without_pdf_part(&bytes, None)).expect("the private data read panicked");
|
||||
prop_assert!(read.is_none());
|
||||
let opened = catch_quiet(|| vectorcraft_engine::cmd::fileio::load("x.ai", &bytes).is_ok()).expect("the open panicked");
|
||||
prop_assert!(!opened);
|
||||
}
|
||||
|
||||
/// A `.ai` whose variable library is damaged or hostile: the library is counted for the warning
|
||||
/// whether the layers are read or not (an inserted `Z` can make an operator the reader doesn't
|
||||
/// handle, and then the layers aren't read).
|
||||
|
||||
@@ -69,7 +69,7 @@ use graphics::{GState, Out};
|
||||
use interp::{Fault, Interp};
|
||||
use obj::PsError;
|
||||
|
||||
pub use native::{ai_alone, is_loss, layered_ai};
|
||||
pub use native::{ai_alone, ai_alone_on_page, is_loss, layered_ai};
|
||||
pub use text::family_style;
|
||||
|
||||
/// The page of a PostScript file without a bounding box (US Letter).
|
||||
|
||||
@@ -25,7 +25,7 @@ pub(super) const MAX_PIXELS: u64 = 1 << 25;
|
||||
pub(super) const MAX_PLACED_ELSEWHERE: usize = 4096;
|
||||
|
||||
/// The note for an image past [`MAX_PIXELS`]: the file then opens from its PDF part, which reads
|
||||
/// larger images (#1479).
|
||||
/// larger images (#1479), unless that part can't be read either; then the image is left out.
|
||||
pub const IMAGE_TOO_LARGE: &str = "an image too large to read from the editing data was left out";
|
||||
|
||||
/// Most hex digits of an image's samples read from comment lines (CMYK and alpha at the largest size).
|
||||
|
||||
@@ -51,7 +51,7 @@ use build::{Frame, MADE, prune_unseen};
|
||||
use compare::{
|
||||
MAX_DIFFERENCE, MAX_SOLID_DIFFERENCE, PageView, Shares, compare, compared, comparison_render, comparison_samples, differ, differ_in_colour,
|
||||
};
|
||||
pub use layers::{ai_alone, layered_ai};
|
||||
pub use layers::{ai_alone, ai_alone_on_page, layered_ai};
|
||||
pub(super) use layers::{layered, program_alone};
|
||||
pub use slots::is_loss;
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -53,6 +53,27 @@ pub(super) fn build(
|
||||
build_from(ai::read(data, matches!(page, Page::Art))?, data, visible, page, outlined, trust, unit)
|
||||
}
|
||||
|
||||
/// [`build`] without a page, for a `.ai` whose PDF part can't be read, its first page `page`
|
||||
/// (width and height, either way round) points large in units of `unit` points: the document is
|
||||
/// drawn `unit` times as large when its first artboard, or its art's box, has the page's size in
|
||||
/// those units and not in points, as [`build_from`] decides with a page.
|
||||
pub(super) fn build_on_page(data: &[u8], page: (f64, f64), unit: f64) -> Result<(Document, Vec<String>), String> {
|
||||
let structure = ai::read(data, false)?;
|
||||
let board = structure.artboard.map(|a| structure.to_doc.transform_rect_bbox(a));
|
||||
let art = structure.bbox.map(|[a, b, c, d]| structure.to_doc.transform_rect_bbox(Rect::new(a, b, c, d)));
|
||||
let (w, h) = page;
|
||||
let fits = |r: Option<Rect>, k: f64| {
|
||||
r.is_some_and(|r| [(w, h), (h, w)].iter().any(|(a, b)| (r.width() - a / k).abs() <= 0.5 && (r.height() - b / k).abs() <= 0.5))
|
||||
};
|
||||
let in_units = |r: Option<Rect>| unit.is_finite() && unit > 1.0 && fits(r, unit) && !fits(r, 1.0);
|
||||
let scale = in_units(board) || (!fits(board, 1.0) && in_units(art));
|
||||
let (mut doc, notes) = build_from(structure, data, None, Page::Artboard, false, false, 1.0)?;
|
||||
if scale {
|
||||
scale_document(&mut doc, unit);
|
||||
}
|
||||
Ok((doc, notes))
|
||||
}
|
||||
|
||||
/// [`build`] from `structure`, the editing copy `data` already read.
|
||||
pub(super) fn build_from(
|
||||
structure: Structure,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::build::{PAGE_MOVE, Page, build, build_from};
|
||||
use super::build::{PAGE_MOVE, Page, build, build_from, build_on_page};
|
||||
use super::slots::LAYERS_UNREAD;
|
||||
use super::{Affine, Arc, Cow, Document, Imported, Node, NodeKind, PathData, Rect, Vec2, ai};
|
||||
|
||||
@@ -216,6 +216,16 @@ pub fn ai_alone(private: &[u8]) -> Result<(Document, Vec<String>), String> {
|
||||
build(&data, None, Page::Artboard, false, false, 1.0)
|
||||
}
|
||||
|
||||
/// [`ai_alone`] for a `.ai` whose PDF part can't be read, its first page `page` (width and height)
|
||||
/// points large in units of `unit` points (its `UserUnit`): editing data whose first artboard, or
|
||||
/// whose art's box, has the page's size in those units, and not in points, comes in `unit` times as
|
||||
/// large, as [`layered_ai`] reads it. A page turned a quarter (`/Rotate 90`) is measured either
|
||||
/// way round.
|
||||
pub fn ai_alone_on_page(private: &[u8], page: (f64, f64), unit: f64) -> Result<(Document, Vec<String>), String> {
|
||||
let data = ai::decode_private(private)?;
|
||||
build_on_page(&data, page, unit)
|
||||
}
|
||||
|
||||
/// Does the editing data (or a program in the legacy format) have layers?
|
||||
fn has_layers(data: &[u8]) -> bool {
|
||||
data.windows(15).any(|w| w == b"%AI5_BeginLayer")
|
||||
|
||||
@@ -37,7 +37,9 @@ mod ps;
|
||||
mod scene;
|
||||
mod tiff;
|
||||
|
||||
pub use import::{Imported, POSTSCRIPT_ERRORS, ai_alone, family_style, import, import_trusting, import_with, is_loss, layered_ai, page};
|
||||
pub use import::{
|
||||
Imported, POSTSCRIPT_ERRORS, ai_alone, ai_alone_on_page, family_style, import, import_trusting, import_with, is_loss, layered_ai, page,
|
||||
};
|
||||
pub use legacy_ai::{HiddenObjects, LegacyAiOptions, LegacyAiOutput, export_legacy_ai};
|
||||
pub use print::{PrintJob, PrintPage, print};
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ pub use import::{
|
||||
INVISIBLE_TEXT_NOTE, OFF_ARTBOARD_NOTE, OUTLINED_TEXT_NOTE, PAST_PAGE_NOTE, UNMATCHED_GLYPHS_NOTE, UNNAMED_CHARACTERS_NOTE, holds_outlines,
|
||||
import, import_with_report, is_glyph_outlines, is_outlines_note,
|
||||
};
|
||||
pub use pages::{PageInfo, PdfInfo, has_illustrator_data, illustrator_data, info, is_postscript};
|
||||
pub use pages::{IllustratorData, PageInfo, PdfInfo, has_illustrator_data, illustrator_data, illustrator_data_without_pdf_part, info, is_postscript};
|
||||
pub use post::{THUMBNAIL_SIZE, Thumbnail};
|
||||
pub use presets::*;
|
||||
pub use print::*;
|
||||
|
||||
+67
-13
@@ -241,6 +241,18 @@ pub(crate) fn open_options() -> craft_pdf::OpenOptions {
|
||||
}
|
||||
|
||||
pub(crate) fn open_with_options(bytes: &[u8], password: Option<&str>, options: &craft_pdf::OpenOptions) -> Result<Pdf, PdfError> {
|
||||
// Twice: looking at every object makes the reader repair a damaged table of objects (once
|
||||
// only), and the objects are other ones after that.
|
||||
open_structural_with_options(bytes, password, options).and_then(|pdf| match (0..2).find_map(|_| hostile(&pdf)) {
|
||||
Some(why) => Err(PdfError::Parse(why)),
|
||||
None => Ok(pdf),
|
||||
})
|
||||
}
|
||||
|
||||
/// [`open_with_options`] without looking at the file's objects ([`hostile`]): the reader builds its
|
||||
/// table of objects and its page tree, and nothing on a page is read. For reading one object that
|
||||
/// is checked on its own ([`illustrator_data_without_pdf_part`]), never for importing a page.
|
||||
fn open_structural_with_options(bytes: &[u8], password: Option<&str>, options: &craft_pdf::OpenOptions) -> Result<Pdf, PdfError> {
|
||||
if is_postscript(bytes) {
|
||||
return Err(PdfError::PostScript);
|
||||
}
|
||||
@@ -265,12 +277,6 @@ pub(crate) fn open_with_options(bytes: &[u8], password: Option<&str>, options: &
|
||||
craft_pdf::OpenError::Load(LoadPdfError::Decryption(DecryptionError::PasswordProtected)) => PdfError::WrongPassword,
|
||||
e => PdfError::Parse(e.to_string()),
|
||||
})
|
||||
// Twice: looking at every object makes the reader repair a damaged table of objects (once
|
||||
// only), and the objects are other ones after that.
|
||||
.and_then(|pdf| match (0..2).find_map(|_| hostile(&pdf)) {
|
||||
Some(why) => Err(PdfError::Parse(why)),
|
||||
None => Ok(pdf),
|
||||
})
|
||||
}
|
||||
|
||||
/// The pages and their boxes. An encrypted PDF needs its `password`
|
||||
@@ -367,7 +373,37 @@ const MAX_PRIVATE: usize = 256 << 20;
|
||||
/// `AIPrivateData1`. `None` for a PDF without either (or with a stream that can't be read).
|
||||
/// What it holds is the editor's own copy of the art (see `vectorcraft_eps::layered_ai`).
|
||||
pub fn illustrator_data(bytes: &[u8], password: Option<&str>) -> Option<Vec<u8>> {
|
||||
let pdf = open(bytes, password).ok()?;
|
||||
private_data(&open(bytes, password).ok()?)
|
||||
}
|
||||
|
||||
/// The private data of a file whose PDF part can't be read ([`illustrator_data_without_pdf_part`]),
|
||||
/// with the size of its first page.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct IllustratorData {
|
||||
/// The private data, as [`illustrator_data`] reads it.
|
||||
pub data: Vec<u8>,
|
||||
/// The size in points of a unit of the first page's user space (its `UserUnit`).
|
||||
pub user_unit: f64,
|
||||
/// The first page's trim box (its crop box when it has none) in points, rotated as the page
|
||||
/// shows: width and height.
|
||||
pub trim: (f64, f64),
|
||||
}
|
||||
|
||||
/// [`illustrator_data`] for a file whose PDF part can't be read (a stream the import refuses, or
|
||||
/// objects built on one another without end): the file is opened without looking at its other
|
||||
/// objects, and only the private data streams are read, with the same limits. `None` when the
|
||||
/// file itself can't be opened, or as for [`illustrator_data`].
|
||||
pub fn illustrator_data_without_pdf_part(bytes: &[u8], password: Option<&str>) -> Option<IllustratorData> {
|
||||
let pdf = open_structural_with_options(bytes, password, &open_options()).ok()?;
|
||||
let data = private_data(&pdf)?;
|
||||
let page = pdf.pages().first()?;
|
||||
// (The page transform is in points: it applies the `UserUnit`.)
|
||||
let trim = frame(page, CropTo::Trim).1;
|
||||
Some(IllustratorData { data, user_unit: f64::from(page.user_unit()), trim: (trim.width(), trim.height()) })
|
||||
}
|
||||
|
||||
/// The private data of `pdf`'s first page, as [`illustrator_data`] reads it.
|
||||
fn private_data(pdf: &Pdf) -> Option<Vec<u8>> {
|
||||
let page = pdf.pages().first()?;
|
||||
let private = page.raw().get::<Dict<'_>>(b"PieceInfo")?.get::<Dict<'_>>(PRIVATE_DATA_OWNER)?.get::<Dict<'_>>(b"Private")?;
|
||||
let key = if private.contains_key(b"AIPrivateData1") { "AIPrivateData" } else { "AIPDFPrivateData" };
|
||||
@@ -388,19 +424,37 @@ pub fn has_illustrator_data(bytes: &[u8], password: Option<&str>) -> bool {
|
||||
|
||||
/// `stream` decoded, up to `most` bytes: the decoder is run here rather than by the PDF reader's
|
||||
/// `decoded()`, which has no limit, so a stream of zeros can't grow to the size of the memory. Only a
|
||||
/// stream that is plain or only deflated is read (the private data is no other); else, or when it
|
||||
/// is larger once decoded, `None`.
|
||||
/// stream that is plain or only deflated is read (the private data is no other), without decode
|
||||
/// parameters; else, or when it is larger once decoded, `None`. A filter the reader doesn't know
|
||||
/// counts as another filter.
|
||||
fn inflated(stream: &Stream<'_>, most: usize) -> Option<Vec<u8>> {
|
||||
use std::io::Read;
|
||||
if stream.dict().get::<Dict<'_>>(b"DecodeParms").is_some() {
|
||||
let dict = stream.dict();
|
||||
let named = |key: &[u8]| match dict.get::<Object<'_>>(key) {
|
||||
None | Some(Object::Null(_)) => Some(0),
|
||||
Some(Object::Name(_)) => Some(1),
|
||||
Some(Object::Array(a)) => Some(items(&a).count()),
|
||||
Some(_) => None,
|
||||
};
|
||||
let params = |key: &[u8]| match dict.get::<Object<'_>>(key) {
|
||||
None | Some(Object::Null(_)) => false,
|
||||
Some(Object::Array(a)) => items(&a).any(|m| !matches!(m, MaybeRef::NotRef(Object::Null(_)))),
|
||||
Some(_) => true,
|
||||
};
|
||||
if params(b"DecodeParms") || params(b"DP") {
|
||||
return None;
|
||||
}
|
||||
let filters = stream.filters();
|
||||
// (The reader leaves out the filters it doesn't know.)
|
||||
if named(b"Filter")?.max(named(b"F")?) != filters.len() {
|
||||
return None;
|
||||
}
|
||||
let raw = stream.raw_data();
|
||||
let mut out = vec![];
|
||||
let limit = u64::try_from(most).ok()?.checked_add(1)?;
|
||||
match stream.filters().len() {
|
||||
0 => out.extend_from_slice(&raw),
|
||||
1 => {
|
||||
match filters.as_slice() {
|
||||
[] if raw.len() <= most => out.extend_from_slice(&raw),
|
||||
[craft_pdf::syntax::Filter::FlateDecode] => {
|
||||
flate2::read::ZlibDecoder::new(&*raw).take(limit).read_to_end(&mut out).ok()?;
|
||||
}
|
||||
_ => return None,
|
||||
|
||||
@@ -416,3 +416,35 @@ fn a_pdfs_private_data_is_its_pdf_streams_joined() {
|
||||
let both = format!("/AIPDFPrivateData1 {a} 0 R /AIPDFPrivateData2 {b} 0 R /AIPrivateData1 {c} 0 R");
|
||||
assert_eq!(illustrator_data(&pdf_with_catalog(&[page(both)], &[&one, &two, &other], "", None), None).unwrap(), b"%AI24_ZStandard_Data other");
|
||||
}
|
||||
|
||||
/// The private data of a file whose other objects the import refuses (#4366) is read without its
|
||||
/// PDF part, and only then; a private data stream under a filter the reader doesn't know, or with
|
||||
/// decode parameters, is read neither way.
|
||||
#[test]
|
||||
fn private_data_reads_without_a_pdf_part_that_cant_be_read() {
|
||||
let (a, b) = (first_extra(1), first_extra(1) + 1);
|
||||
let page = PdfPage {
|
||||
entries: format!("/PieceInfo << /Illustrator << /Private << /AIPrivateData1 {a} 0 R /AIPrivateData2 {b} 0 R /NumBlock 2 >> >> >>"),
|
||||
..PdfPage::new(100.0, 100.0, "1 0 0 rg 10 10 30 30 re f")
|
||||
};
|
||||
let (one, two) = (stream("", "%AI24_ZStandard_Data first "), stream("", "second"));
|
||||
let refused = stream("/DecodeParms << /Predictor 12 /Columns 0 >>", "x");
|
||||
let pdf = pdf_with_catalog(std::slice::from_ref(&page), &[&one, &two, &refused], "", None);
|
||||
assert!(illustrator_data(&pdf, None).is_none());
|
||||
let read = illustrator_data_without_pdf_part(&pdf, None).unwrap();
|
||||
assert_eq!((read.data.as_slice(), read.user_unit, read.trim), (&b"%AI24_ZStandard_Data first second"[..], 1.0, (100.0, 100.0)));
|
||||
let whole = pdf_with_catalog(std::slice::from_ref(&page), &[&one, &two], "", None);
|
||||
assert_eq!(illustrator_data_without_pdf_part(&whole, None).map(|p| p.data), illustrator_data(&whole, None));
|
||||
// The trim box in points, as the page shows it.
|
||||
let large = PdfPage { entries: format!("{} /UserUnit 10 /TrimBox [10 10 60 30] /Rotate 90", page.entries), ..page.clone() };
|
||||
let read = illustrator_data_without_pdf_part(&pdf_with_catalog(&[large], &[&one, &two, &refused], "", None), None).unwrap();
|
||||
assert_eq!((read.user_unit, read.trim), (10.0, (200.0, 500.0)));
|
||||
assert!(illustrator_data_without_pdf_part(b"not a pdf", None).is_none());
|
||||
for odd in ["/Filter /Bogus", "/Filter [/Bogus]", "/DP << /Predictor 2 >>", "/Filter /FlateDecode /DecodeParms [<< /Predictor 2 >>]", "/Filter 5"]
|
||||
{
|
||||
let first = stream(odd, "%AI24_ZStandard_Data first ");
|
||||
let pdf = pdf_with_catalog(std::slice::from_ref(&page), &[&first, &two], "", None);
|
||||
assert!(illustrator_data(&pdf, None).is_none(), "{odd}");
|
||||
assert!(illustrator_data_without_pdf_part(&pdf, None).is_none(), "{odd}");
|
||||
}
|
||||
}
|
||||
|
||||
+27
-10
@@ -190,7 +190,19 @@ pub fn ai(private: &[u8], page: &str) -> Vec<u8> {
|
||||
|
||||
/// [`ai`] with more entries in the page dictionary (`/ArtBox [0 0 200 100]`).
|
||||
pub fn ai_with(private: &[u8], page: &str, entries: &str) -> Vec<u8> {
|
||||
with_private(private, page, "AIPrivateData", entries)
|
||||
with_private(private, page, "AIPrivateData", entries, "")
|
||||
}
|
||||
|
||||
/// [`ai`] with more entries in the dictionary of the page's content stream
|
||||
/// (`/DecodeParms << /Predictor 12 /Columns 0 >>`).
|
||||
pub fn ai_with_content(private: &[u8], page: &str, content: &str) -> Vec<u8> {
|
||||
with_private(private, page, "AIPrivateData", "", content)
|
||||
}
|
||||
|
||||
/// [`ai`] with more `entries` in the page dictionary, as [`ai_with`], and more in its content
|
||||
/// stream's dictionary, as [`ai_with_content`].
|
||||
pub fn ai_with_entries_and_content(private: &[u8], page: &str, entries: &str, content: &str) -> Vec<u8> {
|
||||
with_private(private, page, "AIPrivateData", entries, content)
|
||||
}
|
||||
|
||||
/// A PDF saved with Illustrator's editing data: [`ai`]'s file with the two streams under
|
||||
@@ -198,15 +210,20 @@ pub fn ai_with(private: &[u8], page: &str, entries: &str) -> Vec<u8> {
|
||||
/// #868, and nothing else of that file is used; ExifTool's PDF tag table lists them too
|
||||
/// (<https://exiftool.org/TagNames/PDF.html>).
|
||||
pub fn pdf(private: &[u8], page: &str) -> Vec<u8> {
|
||||
with_private(private, page, "AIPDFPrivateData", "")
|
||||
with_private(private, page, "AIPDFPrivateData", "", "")
|
||||
}
|
||||
|
||||
/// The file of [`ai`] and [`pdf`], with `private` in the streams `{key}1` and `{key}2` and
|
||||
/// `entries` more in the page dictionary.
|
||||
fn with_private(private: &[u8], page: &str, key: &str, entries: &str) -> Vec<u8> {
|
||||
/// [`pdf`] with more entries in the dictionary of the page's content stream, as [`ai_with_content`].
|
||||
pub fn pdf_with_content(private: &[u8], page: &str, content: &str) -> Vec<u8> {
|
||||
with_private(private, page, "AIPDFPrivateData", "", content)
|
||||
}
|
||||
|
||||
/// The file of [`ai`] and [`pdf`], with `private` in the streams `{key}1` and `{key}2`, `entries`
|
||||
/// more in the page dictionary and `content` more in its content stream's dictionary.
|
||||
fn with_private(private: &[u8], page: &str, key: &str, entries: &str, content: &str) -> Vec<u8> {
|
||||
let (first, second) = private.split_at(private.len() / 2);
|
||||
let stream = |part: &[u8]| {
|
||||
let mut s = format!("<< /Length {} >>\nstream\n", part.len()).into_bytes();
|
||||
let stream = |part: &[u8], more: &str| {
|
||||
let mut s = format!("<< /Length {} {more}>>\nstream\n", part.len()).into_bytes();
|
||||
s.extend_from_slice(part);
|
||||
s.extend_from_slice(b"\nendstream");
|
||||
s
|
||||
@@ -220,9 +237,9 @@ fn with_private(private: &[u8], page: &str, key: &str, entries: &str) -> Vec<u8>
|
||||
/PieceInfo << /{PIECE} << /Private << /{key}1 5 0 R /{key}2 6 0 R /NumBlock 2 >> >> >> >>"
|
||||
)
|
||||
.into_bytes(),
|
||||
stream(page.as_bytes()),
|
||||
stream(first),
|
||||
stream(second),
|
||||
stream(page.as_bytes(), content),
|
||||
stream(first, ""),
|
||||
stream(second, ""),
|
||||
];
|
||||
let mut out = b"%PDF-1.5\n".to_vec();
|
||||
let mut offsets = vec![];
|
||||
|
||||
@@ -4439,6 +4439,7 @@ axis Both И двете
|
||||
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise текстовите рамки за област в редове, твърде къси за шрифта, в който се показват, получиха по-високи рамки ({_1}): текстът им иначе нямаше да се види
|
||||
@msg its editing data doesn't say where its art is данните му за редактиране не посочват къде се намира неговата графика
|
||||
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) този файл е записан без съвместимост с PDF и данните му за редактиране не могат да бъдат прочетени ({why})
|
||||
@msg the PDF part couldn't be read ({_1}): the file opened from its editing data PDF частта не можа да бъде прочетена ({_1}): файлът беше отворен от данните си за редактиране
|
||||
@msg No fonts are missing in this document В този документ няма липсващи шрифтове
|
||||
@msg VectorCraft doesn't search other apps' or the system's folders: {path} VectorCraft не търси в папките на други приложения или на системата: {path}
|
||||
@msg VectorCraft doesn't search the Shared or Public folder in Users: {path} VectorCraft не търси в папките Shared или Public в Users: {path}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4841,6 +4841,7 @@ imageTrace Outline Obris
|
||||
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise tekst u području u okvirima prekratkim za red fonta u kojem se prikazuje dobio je više okvire ({_1}): inače se tekst ne bi prikazao
|
||||
@msg its editing data doesn't say where its art is njeni podaci za uređivanje ne govore gdje joj je crtež
|
||||
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) ova datoteka je sačuvana bez PDF kompatibilnosti, a njeni podaci za uređivanje se ne mogu pročitati ({why})
|
||||
@msg the PDF part couldn't be read ({_1}): the file opened from its editing data PDF dio se nije mogao pročitati ({_1}): datoteka je otvorena iz svojih podataka za uređivanje
|
||||
@msg No fonts are missing in this document U ovom dokumentu ne nedostaje nijedan font
|
||||
@msg VectorCraft doesn't search other apps' or the system's folders: {path} VectorCraft ne pretražuje foldere drugih aplikacija ni sistemske foldere: {path}
|
||||
@msg VectorCraft doesn't search the Shared or Public folder in Users: {path} VectorCraft ne pretražuje folder Shared ili Public u Users: {path}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5531,6 +5531,7 @@ digits Malayalam Malaiàlam
|
||||
@msg there are no printer settings to open here aquí no hi ha cap configuració d’impressora per obrir
|
||||
@msg thickness must be from 0.01 to 5000 pt el gruix ha de ser d’entre 0,01 i 5000 pt
|
||||
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) aquest fitxer s’ha desat sense compatibilitat amb PDF i les seves dades d’edició no es poden llegir ({why})
|
||||
@msg the PDF part couldn't be read ({_1}): the file opened from its editing data no s’ha pogut llegir la part PDF ({_1}): el fitxer s’ha obert a partir de les seves dades d’edició
|
||||
@msg this file's PostScript couldn't be read ({why}): {what} no s’ha pogut llegir el PostScript d’aquest fitxer ({why}): {what}
|
||||
@msg this format holds one artboard: export several as PDF or with document.exportForScreens aquest format admet un sol tauler: exporta’n diversos com a PDF o amb document.exportForScreens
|
||||
@msg this is a DWG drawing, which can't be read: save it from the CAD app as an ASCII DXF and open that aquest és un dibuix DWG, que no es pot llegir: desa’l des de l’aplicació CAD com a DXF ASCII i obre’l
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4885,6 +4885,7 @@ imageTrace Outline Obrys
|
||||
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise text v oblasti v rámcích příliš nízkých pro řádek písma, ve kterém se zde zobrazuje, dostal vyšší rámce ({_1}): jinak by nebyl vidět
|
||||
@msg its editing data doesn't say where its art is z jeho dat pro úpravy není zřejmé, kde je kresba
|
||||
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) tento soubor byl uložen bez kompatibility s PDF a jeho data pro úpravy nelze přečíst ({why})
|
||||
@msg the PDF part couldn't be read ({_1}): the file opened from its editing data část PDF nelze přečíst ({_1}): soubor byl otevřen z jeho dat pro úpravy
|
||||
@msg No fonts are missing in this document V tomto dokumentu nechybí žádná písma
|
||||
@msg VectorCraft doesn't search other apps' or the system's folders: {path} VectorCraft neprohledává složky jiných aplikací ani systému: {path}
|
||||
@msg VectorCraft doesn't search the Shared or Public folder in Users: {path} VectorCraft neprohledává složku Shared ani Public ve složce Users: {path}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4530,6 +4530,7 @@ imageTrace Outline Kontur
|
||||
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise rammetekst i rammer, der er for lave til en tekstlinje i den skrifttype, den viser i, fik højere rammer ({_1}): teksten ville ellers ikke blive vist
|
||||
@msg its editing data doesn't say where its art is dens redigeringsdata siger ikke, hvor dens grafik er
|
||||
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) denne fil blev gemt uden PDF-kompatibilitet, og dens redigeringsdata kan ikke læses ({why})
|
||||
@msg the PDF part couldn't be read ({_1}): the file opened from its editing data PDF-delen kunne ikke læses ({_1}): filen blev åbnet fra dens redigeringsdata
|
||||
@msg No fonts are missing in this document Ingen skrifttyper mangler i dette dokument
|
||||
@msg VectorCraft doesn't search other apps' or the system's folders: {path} VectorCraft gennemsøger ikke andre programmers eller systemets mapper: {path}
|
||||
@msg VectorCraft doesn't search the Shared or Public folder in Users: {path} VectorCraft gennemsøger ikke mappen Shared eller Public i Users: {path}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4494,6 +4494,7 @@ theme Appearance Erscheinungsbild
|
||||
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise Flächentext in Rahmen, die für eine Zeile der Schrift, in der er hier erscheint, zu niedrig sind, hat höhere Rahmen bekommen ({_1}): Sonst wäre der Text nicht zu sehen
|
||||
@msg its editing data doesn't say where its art is seine Bearbeitungsdaten geben nicht an, wo seine Grafik liegt
|
||||
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) diese Datei wurde ohne PDF-Kompatibilität gespeichert, und ihre Bearbeitungsdaten können nicht gelesen werden ({why})
|
||||
@msg the PDF part couldn't be read ({_1}): the file opened from its editing data der PDF-Teil konnte nicht gelesen werden ({_1}): die Datei wurde aus ihren Bearbeitungsdaten geöffnet
|
||||
@msg No fonts are missing in this document In diesem Dokument fehlen keine Schriften
|
||||
@msg VectorCraft doesn't search other apps' or the system's folders: {path} VectorCraft durchsucht keine Ordner anderer Apps oder des Systems: {path}
|
||||
@msg VectorCraft doesn't search the Shared or Public folder in Users: {path} VectorCraft durchsucht nicht den Ordner „Shared“ oder „Public“ unter „Users“: {path}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4527,6 +4527,7 @@ digits Malayalam Μαλαγιάλαμ
|
||||
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise οι περιοχές κειμένου με πλαίσια πολύ μικρά για μία γραμμή της γραμματοσειράς τους απέκτησαν μεγαλύτερο ύψος ({_1}), διαφορετικά το κείμενο δεν θα εμφανιζόταν
|
||||
@msg its editing data doesn't say where its art is τα δεδομένα επεξεργασίας του δεν προσδιορίζουν τη θέση του σχεδίου
|
||||
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) το αρχείο αποθηκεύτηκε χωρίς συμβατότητα PDF και τα δεδομένα επεξεργασίας του δεν μπορούν να διαβαστούν ({why})
|
||||
@msg the PDF part couldn't be read ({_1}): the file opened from its editing data το τμήμα PDF δεν μπόρεσε να διαβαστεί ({_1}): το αρχείο άνοιξε από τα δεδομένα επεξεργασίας του
|
||||
@msg No fonts are missing in this document Δεν λείπουν γραμματοσειρές από αυτό το έγγραφο
|
||||
@msg VectorCraft doesn't search other apps' or the system's folders: {path} Το VectorCraft δεν αναζητά στους φακέλους άλλων εφαρμογών ή του συστήματος: {path}
|
||||
@msg VectorCraft doesn't search the Shared or Public folder in Users: {path} Το VectorCraft δεν αναζητά στους φακέλους Shared ή Public των Χρηστών: {path}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 164 and column 5.
|
@@ -4579,6 +4579,7 @@ axis Both Ambos
|
||||
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise el texto de área en marcos demasiado bajos para una línea de la fuente en que se muestra recibió marcos más altos ({_1}): de otro modo no se vería
|
||||
@msg its editing data doesn't say where its art is sus datos de edición no dicen dónde está su ilustración
|
||||
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) este archivo se guardó sin compatibilidad con PDF y sus datos de edición no se pueden leer ({why})
|
||||
@msg the PDF part couldn't be read ({_1}): the file opened from its editing data no se pudo leer la parte PDF ({_1}): el archivo se abrió desde sus datos de edición
|
||||
@msg No fonts are missing in this document No faltan fuentes en este documento
|
||||
@msg VectorCraft doesn't search other apps' or the system's folders: {path} VectorCraft no busca en carpetas de otras aplicaciones ni del sistema: {path}
|
||||
@msg VectorCraft doesn't search the Shared or Public folder in Users: {path} VectorCraft no busca en las carpetas Shared ni Public de Users: {path}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4787,6 +4787,7 @@ imageTrace Outline Ääriviiva
|
||||
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise aluetekstin kehykset, jotka olivat liian matalia yhdelle riville niiden fontilla, korotettiin ({_1}): muuten niiden teksti ei näkyisi
|
||||
@msg its editing data doesn't say where its art is sen muokkaustiedoista ei selviä kuvituksen sijainti
|
||||
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) tämä tiedosto tallennettiin ilman PDF-yhteensopivuutta, eikä sen muokkaustietoja voi lukea ({why})
|
||||
@msg the PDF part couldn't be read ({_1}): the file opened from its editing data PDF-osaa ei voitu lukea ({_1}): tiedosto avattiin sen muokkaustiedoista
|
||||
@msg No fonts are missing in this document Tästä asiakirjasta ei puutu fontteja
|
||||
@msg VectorCraft doesn't search other apps' or the system's folders: {path} VectorCraft ei hae muiden sovellusten tai järjestelmän kansioista: {path}
|
||||
@msg VectorCraft doesn't search the Shared or Public folder in Users: {path} VectorCraft ei hae Users-kansion Shared- tai Public-kansiosta: {path}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4569,6 +4569,7 @@ axis Both Les deux
|
||||
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise le texte captif dans des cadres trop bas pour une ligne de la police dans laquelle il s’affiche a reçu des cadres plus hauts ({_1}) : il ne s’afficherait pas autrement
|
||||
@msg its editing data doesn't say where its art is ses données d’édition ne disent pas où se trouve son illustration
|
||||
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) ce fichier a été enregistré sans compatibilité PDF et ses données d’édition sont illisibles ({why})
|
||||
@msg the PDF part couldn't be read ({_1}): the file opened from its editing data la partie PDF n’a pas pu être lue ({_1}) : le fichier a été ouvert depuis ses données de modification
|
||||
@msg No fonts are missing in this document Aucune police ne manque dans ce document
|
||||
@msg VectorCraft doesn't search other apps' or the system's folders: {path} VectorCraft ne recherche pas dans les dossiers d’autres apps ni du système : {path}
|
||||
@msg VectorCraft doesn't search the Shared or Public folder in Users: {path} VectorCraft ne recherche pas dans les dossiers Shared et Public de Users : {path}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5199,6 +5199,7 @@ imageTrace Outline Körvonal
|
||||
@msg there are no objects to release nincs felszabadítható objektum
|
||||
@msg there are no printer settings to open here itt nincsenek megnyitható nyomtatóbeállítások
|
||||
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) ezt a fájlt PDF-kompatibilitás nélkül mentették, a szerkesztési adatai nem olvashatók ({why})
|
||||
@msg the PDF part couldn't be read ({_1}): the file opened from its editing data a PDF-rész nem olvasható ({_1}): a fájl a szerkesztési adataiból nyílt meg
|
||||
@msg this file's PostScript couldn't be read ({why}): {what} a fájl PostScript-tartalma nem olvasható ({why}): {what}
|
||||
@msg this format holds one artboard: export several as PDF or with document.exportForScreens ez a formátum egy rajztáblát tárol: többet PDF-ként vagy a document.exportForScreens paranccsal exportálj
|
||||
@msg this is a DWG drawing, which can't be read: save it from the CAD app as an ASCII DXF and open that ez DWG-rajz, amely nem olvasható: mentsd ASCII DXF-ként a CAD-alkalmazásból, és azt nyisd meg
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4530,6 +4530,7 @@ imageTrace Outline Kontur
|
||||
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise teks area dalam bingkai yang terlalu pendek untuk satu baris font yang ditampilkannya mendapat bingkai yang lebih tinggi ({_1}): tanpa itu teksnya tidak akan tampil
|
||||
@msg its editing data doesn't say where its art is data pengeditannya tidak menyatakan di mana karya seninya berada
|
||||
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) berkas ini disimpan tanpa kompatibilitas PDF, dan data pengeditannya tidak dapat dibaca ({why})
|
||||
@msg the PDF part couldn't be read ({_1}): the file opened from its editing data bagian PDF tidak dapat dibaca ({_1}): file dibuka dari data pengeditannya
|
||||
@msg No fonts are missing in this document Tidak ada font yang hilang dalam dokumen ini
|
||||
@msg VectorCraft doesn't search other apps' or the system's folders: {path} VectorCraft tidak mencari di folder aplikasi lain atau folder sistem: {path}
|
||||
@msg VectorCraft doesn't search the Shared or Public folder in Users: {path} VectorCraft tidak mencari di folder Shared atau Public di Users: {path}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2190 and column 5.
|
@@ -4620,6 +4620,7 @@ axis Both Entrambi
|
||||
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise il testo in area in cornici troppo basse per una riga del font in cui viene mostrato ha ricevuto cornici più alte ({_1}): altrimenti non sarebbe visibile
|
||||
@msg its editing data doesn't say where its art is i suoi dati di modifica non dicono dove si trova la grafica
|
||||
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) questo file è stato salvato senza compatibilità PDF e i suoi dati di modifica non si possono leggere ({why})
|
||||
@msg the PDF part couldn't be read ({_1}): the file opened from its editing data non è stato possibile leggere la parte PDF ({_1}): il file è stato aperto dai suoi dati di modifica
|
||||
@msg No fonts are missing in this document In questo documento non mancano font
|
||||
@msg VectorCraft doesn't search other apps' or the system's folders: {path} VectorCraft non cerca nelle cartelle di altre app né di sistema: {path}
|
||||
@msg VectorCraft doesn't search the Shared or Public folder in Users: {path} VectorCraft non cerca nelle cartelle Shared e Public di Users: {path}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4102,6 +4102,7 @@ swatch Bright Violet ビビッドバイオレット
|
||||
@msg the document has no artboard ドキュメントにアートボードがありません
|
||||
@msg this format holds one artboard: export several as PDF or with document.exportForScreens この形式で書き出せるアートボードは 1 つだけです: 複数のアートボードは PDF として書き出すか、document.exportForScreens を使用してください
|
||||
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) このファイルは PDF 互換性なしで保存されており、編集データを読み込めません ({why})
|
||||
@msg the PDF part couldn't be read ({_1}): the file opened from its editing data PDF 部分を読み込めませんでした ({_1})。編集データからファイルを開きました
|
||||
@msg the export wrote no file 書き出しでファイルが作成されませんでした
|
||||
@msg {_1} files can be opened but not written (see document.formats) {_1} ファイルは開けますが、保存や書き出しはできません (document.formats を参照してください)
|
||||
@msg {_1} files are written by file.saveAsTemplate {_1} ファイルは file.saveAsTemplate で保存します
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4317,6 +4317,7 @@ font-technology Variable ცვლადი
|
||||
@msg an image whose data is cut short was left out გამოსახულება, რომლის მონაცემებიც შეკვეცილია, გამოტოვებულია
|
||||
@msg its editing data doesn't say where its art is მისი რედაქტირების მონაცემები არ მიუთითებს, სად არის მისი ნახატი
|
||||
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) ეს ფაილი PDF-თავსებადობის გარეშეა შენახული და მისი რედაქტირების მონაცემების წაკითხვა ვერ ხერხდება ({why})
|
||||
@msg the PDF part couldn't be read ({_1}): the file opened from its editing data PDF ნაწილის წაკითხვა ვერ მოხერხდა ({_1}): ფაილი რედაქტირების მონაცემებიდან გაიხსნა
|
||||
@msg No fonts are missing in this document ამ დოკუმენტში შრიფტები არ აკლია
|
||||
@msg VectorCraft doesn't search other apps' or the system's folders: {path} VectorCraft არ ეძებს სხვა აპების ან სისტემის საქაღალდეებში: {path}
|
||||
@msg VectorCraft doesn't search the Shared or Public folder in Users: {path} VectorCraft არ ეძებს Users-ის Shared ან Public საქაღალდეში: {path}
|
||||
|
||||
|
Can't render this file because it is too large.
|
@@ -4029,6 +4029,7 @@ theme Appearance 외관
|
||||
@msg the document has no artboard 문서에 아트보드가 없습니다
|
||||
@msg this format holds one artboard: export several as PDF or with document.exportForScreens 이 형식은 아트보드를 하나만 담을 수 있습니다. 여러 개는 PDF로 내보내거나 document.exportForScreens를 사용하세요
|
||||
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) 이 파일은 PDF 호환성 없이 저장되어 편집 데이터를 읽을 수 없습니다({why})
|
||||
@msg the PDF part couldn't be read ({_1}): the file opened from its editing data PDF 부분을 읽지 못했습니다({_1}). 편집 데이터에서 파일을 열었습니다
|
||||
@msg the export wrote no file 내보내기에서 파일이 만들어지지 않았습니다
|
||||
@msg {_1} files can be opened but not written (see document.formats) {_1} 파일은 열 수 있지만 쓸 수 없습니다(document.formats 참조)
|
||||
@msg {_1} files are written by file.saveAsTemplate {_1} 파일은 file.saveAsTemplate로 기록합니다
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4542,6 +4542,7 @@ imageTrace Outline Contour
|
||||
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise vlaktekst in kaders die te kort zijn voor één regel van het lettertype waarin de tekst wordt weergegeven, heeft hogere kaders gekregen ({_1}): anders zou de tekst niet zichtbaar zijn
|
||||
@msg its editing data doesn't say where its art is zijn bewerkingsgegevens zeggen niet waar de illustratie zich bevindt
|
||||
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) dit bestand is opgeslagen zonder PDF-compatibiliteit en de bewerkingsgegevens kunnen niet worden gelezen ({why})
|
||||
@msg the PDF part couldn't be read ({_1}): the file opened from its editing data het PDF-gedeelte kon niet worden gelezen ({_1}): het bestand is geopend vanuit de bewerkingsgegevens
|
||||
@msg No fonts are missing in this document Er ontbreken geen lettertypen in dit document
|
||||
@msg VectorCraft doesn't search other apps' or the system's folders: {path} VectorCraft doorzoekt geen mappen van andere apps of van het systeem: {path}
|
||||
@msg VectorCraft doesn't search the Shared or Public folder in Users: {path} VectorCraft doorzoekt de map Gedeeld of Openbaar onder Gebruikers niet: {path}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4711,6 +4711,7 @@ imageTrace Outline Kontur
|
||||
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise tekst akapitowy w ramkach niższych niż wiersz wyświetlanej czcionki otrzymał wyższe ramki ({_1}): inaczej tekst nie byłby widoczny
|
||||
@msg its editing data doesn't say where its art is jego dane edycyjne nie określają położenia kompozycji
|
||||
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) ten plik zapisano bez zgodności z PDF, a jego danych edycyjnych nie można odczytać ({why})
|
||||
@msg the PDF part couldn't be read ({_1}): the file opened from its editing data nie udało się odczytać części PDF ({_1}): plik otwarto z jego danych edycyjnych
|
||||
@msg No fonts are missing in this document W tym dokumencie nie brakuje czcionek
|
||||
@msg VectorCraft doesn't search other apps' or the system's folders: {path} VectorCraft nie przeszukuje folderów innych aplikacji ani systemowych: {path}
|
||||
@msg VectorCraft doesn't search the Shared or Public folder in Users: {path} VectorCraft nie przeszukuje folderu Shared ani Public w Users: {path}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4163,6 +4163,7 @@ axis Both Ambos
|
||||
@msg an image whose data is cut short was left out uma imagem com dados incompletos foi omitida
|
||||
@msg its editing data doesn't say where its art is seus dados de edição não indicam onde está a arte
|
||||
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) este arquivo foi salvo sem compatibilidade com PDF e seus dados de edição não podem ser lidos ({why})
|
||||
@msg the PDF part couldn't be read ({_1}): the file opened from its editing data não foi possível ler a parte PDF ({_1}): o arquivo foi aberto a partir dos seus dados de edição
|
||||
@msg No fonts are missing in this document Nenhuma fonte está ausente neste documento
|
||||
@msg VectorCraft doesn't search other apps' or the system's folders: {path} O VectorCraft não pesquisa pastas de outros aplicativos nem do sistema: {path}
|
||||
@msg VectorCraft doesn't search the Shared or Public folder in Users: {path} O VectorCraft não pesquisa a pasta Compartilhado ou Público em Usuários: {path}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -4615,6 +4615,7 @@ axis Both Оба
|
||||
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise текст в области в рамках, слишком низких для строки шрифта, которым он отображается, получил более высокие рамки ({_1}): иначе он не был бы виден
|
||||
@msg its editing data doesn't say where its art is в его данных редактирования не указано, где находится графика
|
||||
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) этот файл сохранён без совместимости с PDF, и его данные редактирования не удаётся прочитать ({why})
|
||||
@msg the PDF part couldn't be read ({_1}): the file opened from its editing data не удалось прочитать PDF-часть ({_1}): файл открыт из его данных для редактирования
|
||||
@msg No fonts are missing in this document В этом документе нет отсутствующих шрифтов
|
||||
@msg VectorCraft doesn't search other apps' or the system's folders: {path} VectorCraft не ищет в папках других приложений и системы: {path}
|
||||
@msg VectorCraft doesn't search the Shared or Public folder in Users: {path} VectorCraft не ищет в папках Shared и Public внутри Users: {path}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4478,6 +4478,7 @@ axis Both Båda
|
||||
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise områdestext i ramar som var för låga för en rad i det typsnitt den visas med fick högre ramar ({_1}): annars skulle texten inte synas
|
||||
@msg its editing data doesn't say where its art is dess redigeringsdata anger inte var dess grafik finns
|
||||
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) den här filen sparades utan PDF-kompatibilitet och dess redigeringsdata kan inte läsas ({why})
|
||||
@msg the PDF part couldn't be read ({_1}): the file opened from its editing data PDF-delen kunde inte läsas ({_1}): filen öppnades från dess redigeringsdata
|
||||
@msg No fonts are missing in this document Inga typsnitt saknas i det här dokumentet
|
||||
@msg VectorCraft doesn't search other apps' or the system's folders: {path} VectorCraft söker inte i andra appars eller systemets mappar: {path}
|
||||
@msg VectorCraft doesn't search the Shared or Public folder in Users: {path} VectorCraft söker inte i mappen Shared eller Public under Users: {path}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 5 and column 25.
|
@@ -4316,6 +4316,7 @@ axis Both Her İkisi
|
||||
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise çerçevelerdeki alan türü, daha uzun çerçevelerde gösterdiği yazı tipinin bir satırı için çok kısa ({_1}): bunların türü aksi halde gösterilmez
|
||||
@msg its editing data doesn't say where its art is düzenleme verileri sanatın nerede olduğunu söylemiyor
|
||||
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) bu dosya PDF uyumluluğu olmadan kaydedildi ve düzenleme verileri okunamıyor ({why})
|
||||
@msg the PDF part couldn't be read ({_1}): the file opened from its editing data PDF kısmı okunamadı ({_1}): dosya düzenleme verilerinden açıldı
|
||||
@msg No fonts are missing in this document Bu belgede eksik yazı tipi yok
|
||||
@msg VectorCraft doesn't search other apps' or the system's folders: {path} VectorCraft diğer uygulamaların veya sistemin klasörlerini aramaz: {path}
|
||||
@msg VectorCraft doesn't search the Shared or Public folder in Users: {path} VectorCraft, Kullanıcılar'daki Paylaşılan veya Genel klasörü aramıyor: {path}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 5 and column 25.
|
@@ -4604,6 +4604,7 @@ axis Both Обидві
|
||||
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise текст в області в рамках, занизьких для рядка шрифту, яким він показується, отримав вищі рамки ({_1}): інакше його не було б видно
|
||||
@msg its editing data doesn't say where its art is у даних редагування не зазначено, де знаходиться графіка
|
||||
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) цей файл збережений без сумісності з PDF, і його дані редагування не вдається прочитати ({why})
|
||||
@msg the PDF part couldn't be read ({_1}): the file opened from its editing data не вдалося прочитати PDF-частину ({_1}): файл відкрито з його даних для редагування
|
||||
@msg No fonts are missing in this document У цьому документі немає відсутніх шрифтів
|
||||
@msg VectorCraft doesn't search other apps' or the system's folders: {path} VectorCraft не шукає в папках інших програм і системи: {path}
|
||||
@msg VectorCraft doesn't search the Shared or Public folder in Users: {path} VectorCraft не шукає в папках Shared і Public у Users: {path}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 178 and column 5.
|
@@ -4859,6 +4859,7 @@ theme Appearance Giao diện
|
||||
@msg there are no objects to release không có đối tượng nào để giải phóng
|
||||
@msg there are no printer settings to open here không có cài đặt máy in để mở ở đây
|
||||
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) tệp này được lưu không có tương thích PDF, và dữ liệu chỉnh sửa không thể đọc được ({why})
|
||||
@msg the PDF part couldn't be read ({_1}): the file opened from its editing data không đọc được phần PDF ({_1}): tệp được mở từ dữ liệu chỉnh sửa
|
||||
@msg this file's PostScript couldn't be read ({why}): {what} PostScript của tệp này không thể đọc được ({why}): {what}
|
||||
@msg this format holds one artboard: export several as PDF or with document.exportForScreens định dạng này chứa một bảng vẽ: xuất nhiều bảng vẽ dưới dạng PDF hoặc với document.exportForScreens
|
||||
@msg this is a DWG drawing, which can't be read: save it from the CAD app as an ASCII DXF and open that đây là bản vẽ DWG, không thể đọc: lưu từ ứng dụng CAD dưới dạng ASCII DXF và mở tệp đó
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 200 and column 5.
|
@@ -3833,6 +3833,7 @@ imageTrace Outline 轮廓
|
||||
@msg the document has no artboard 文档没有画板
|
||||
@msg this format holds one artboard: export several as PDF or with document.exportForScreens 此格式只能包含一个画板:要导出多个画板,请导出为 PDF 或使用 document.exportForScreens
|
||||
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) 此文件保存时未启用 PDF 兼容性,且无法读取其编辑数据 ({why})
|
||||
@msg the PDF part couldn't be read ({_1}): the file opened from its editing data 无法读取 PDF 部分 ({_1}):已从其编辑数据打开文件
|
||||
@msg the export wrote no file 导出未写入任何文件
|
||||
@msg {_1} files can be opened but not written (see document.formats) {_1} 文件可以打开,但不能写入 (请参阅 document.formats)
|
||||
@msg {_1} files are written by file.saveAsTemplate {_1} 文件由 file.saveAsTemplate 写入
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1761,6 +1761,7 @@ file-filter Templates 範本
|
||||
@msg the document has no artboard 文件沒有工作區域
|
||||
@msg this format holds one artboard: export several as PDF or with document.exportForScreens 此格式只能容納一個工作區域:若要匯出多個,請匯出為 PDF 或使用 document.exportForScreens
|
||||
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) 此檔案儲存時未啟用 PDF 相容性,且無法讀取其編輯資料 ({why})
|
||||
@msg the PDF part couldn't be read ({_1}): the file opened from its editing data 無法讀取 PDF 部分 ({_1}):已從其編輯資料開啟檔案
|
||||
@msg the export wrote no file 匯出未寫入任何檔案
|
||||
@msg {_1} files can be opened but not written (see document.formats) {_1} 檔案可以開啟,但無法寫入 (請參閱 document.formats)
|
||||
@msg {_1} files are written by file.saveAsTemplate {_1} 檔案由 file.saveAsTemplate 寫入
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
Reference in New Issue
Block a user