mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-15 13:36:53 +00:00
Import PDF: Import PDF pages as links opens each page linked to its file
File › Open's Import PDF dialog gets Import PDF pages as links. With it on, each page picked opens as a placed document linked to that page of the file, on an artboard of its Crop To box and a layer named after the page, in place of editable art. The Links panel lists each page, and Embed makes a page editable. document.open takes the matching linkPages param. It is refused for a file that is not a PDF or a PDF-compatible .ai, for bytes without a path, with a password and with textAs: outlines. The pages share one copy of the file, and the document opens in CMYK when most of its pages do. Save asks for a name, as for other partly read PDFs.
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -677,15 +677,28 @@ impl ReadFile {
|
||||
/// ([`super::importer`]).
|
||||
pub fn read_for_open(name: &str, bytes: &[u8], path: Option<String>, p: &Value, update_links: bool, low_res_proxy_eps: bool) -> Result<ReadFile> {
|
||||
let opts = LoadOptions::from_params("document.open", p)?;
|
||||
let mut loaded = match super::importer::pick(name, bytes, p)? {
|
||||
Some(plugin) => super::importer::load(name, bytes, &plugin, p)?,
|
||||
None => load_with(name, bytes, &opts)?,
|
||||
let mut loaded = if opts.link_pages {
|
||||
linked_pages(name, bytes, path.as_deref(), &opts)?
|
||||
} else {
|
||||
match super::importer::pick(name, bytes, p)? {
|
||||
Some(plugin) => super::importer::load(name, bytes, &plugin, p)?,
|
||||
None => load_with(name, bytes, &opts)?,
|
||||
}
|
||||
};
|
||||
let links = crate::cmd::links::resolve(&mut loaded.doc, crate::cmd::links::LinkBase::file(path.as_deref()), update_links, low_res_proxy_eps);
|
||||
Session::refresh_text_bounds(&mut loaded.doc);
|
||||
Ok(ReadFile { loaded, opts, path, links })
|
||||
}
|
||||
|
||||
/// The PDF `name` (`bytes`, read from `path`) opened with `linkPages`: its pages as placed documents
|
||||
/// linked to them ([`super::pdfimport::linked_pages`]), named after the file.
|
||||
fn linked_pages(name: &str, bytes: &[u8], path: Option<&str>, opts: &LoadOptions) -> Result<Loaded> {
|
||||
let (mut doc, warnings) = super::pdfimport::linked_pages(name, bytes, path, opts)?;
|
||||
doc.title = file_name(name);
|
||||
let format = detect(name, bytes).ok_or_else(|| err(format!("can't open `{}`: not a PDF", file_name(name))))?;
|
||||
Ok(Loaded { doc, format, warnings, restored: false, converted: false, preview_only: false, editing_data: false, native_pdf_compatible: None })
|
||||
}
|
||||
|
||||
/// The second half of [`open_bytes_with`]: open a file [`read_for_open`] read as the new active
|
||||
/// document → `document.open`'s result.
|
||||
pub fn open_read(s: &mut Session, file: ReadFile) -> Result<Value> {
|
||||
|
||||
@@ -2,11 +2,15 @@
|
||||
//! and `file.place` read ([`LoadOptions`]), the pages they import and `document.pdfInfo` (pages,
|
||||
//! boxes, a thumbnail; one page for a PostScript file) for the Import PDF dialog.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use serde_json::{Value, json};
|
||||
use vectorcraft_doc::{ColorMode, Document};
|
||||
use vectorcraft_doc::{Artboard, ColorMode, Document, LayerColor, LinkInfo, Node, NodeKind};
|
||||
use vectorcraft_geom::{Affine, Rect};
|
||||
use vectorcraft_pdf::{Choice, CropTo, ImportOptions, PdfError, TextAs};
|
||||
|
||||
use super::super::colormgmt::Grays;
|
||||
use super::super::place::document;
|
||||
use super::super::{Result, Session, bad, check_numbers, number_or, parse_range, text_param};
|
||||
use super::{err, source};
|
||||
|
||||
@@ -47,6 +51,9 @@ pub struct LoadOptions {
|
||||
/// A PDF or `.ai` saved with Preserve Editing opens from the VectorCraft document it carries;
|
||||
/// off, it opens as another app would open it, from the editing copy of its art or its pages.
|
||||
pub native: bool,
|
||||
/// Each page of a PDF opens as a placed document linked to that page of its file, on an
|
||||
/// artboard of its own, instead of as editable art (`linkPages`; see [`linked_pages`]).
|
||||
pub link_pages: bool,
|
||||
}
|
||||
|
||||
impl Default for LoadOptions {
|
||||
@@ -65,6 +72,7 @@ impl Default for LoadOptions {
|
||||
trust_editing_data: false,
|
||||
legacy_names: vectorcraft_eps::NameEncoding::default(),
|
||||
native: true,
|
||||
link_pages: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,8 +82,8 @@ impl LoadOptions {
|
||||
/// `pages` isn't given), `cropTo` (or `crop`), `password`, `colorMode` (`rgb` | `cmyk`) with `grays`,
|
||||
/// `textAs` (`text` | `outlines`), `layers` (true | false), `hiddenLayers` (true | false),
|
||||
/// `editingData` (true | false | `always`),
|
||||
/// `legacyNameEncoding` (one of [`vectorcraft_eps::NameEncoding::LABELS`]) and `dxf` (the DXF
|
||||
/// import options).
|
||||
/// `legacyNameEncoding` (one of [`vectorcraft_eps::NameEncoding::LABELS`]), `linkPages` (true |
|
||||
/// false) and `dxf` (the DXF import options).
|
||||
pub fn from_params(cmd: &str, p: &Value) -> Result<Self> {
|
||||
let pages = match p.get("pages").filter(|v| !v.is_null()).or_else(|| p.get("page").filter(|v| !v.is_null())) {
|
||||
None => None,
|
||||
@@ -115,6 +123,10 @@ impl LoadOptions {
|
||||
.and_then(vectorcraft_eps::NameEncoding::from_label)
|
||||
.ok_or_else(|| bad(cmd, format!("legacyNameEncoding must be one of {}", vectorcraft_eps::NameEncoding::LABELS.join(", "))))?,
|
||||
};
|
||||
let link_pages = match p.get("linkPages").filter(|v| !v.is_null()) {
|
||||
None => false,
|
||||
Some(v) => v.as_bool().ok_or_else(|| bad(cmd, "linkPages must be true or false"))?,
|
||||
};
|
||||
let dxf = super::dxfimport::options(cmd, p.get("dxf"))?;
|
||||
Ok(Self {
|
||||
pages,
|
||||
@@ -130,14 +142,15 @@ impl LoadOptions {
|
||||
trust_editing_data,
|
||||
legacy_names,
|
||||
native: true,
|
||||
link_pages,
|
||||
})
|
||||
}
|
||||
|
||||
/// Does the document read only part of the file, or read it differently (a page range, another
|
||||
/// box, a password, without its hidden layers, without the VectorCraft document it carries)?
|
||||
/// Writing it back would lose the rest, so Save doesn't (it asks for a name).
|
||||
/// box, a password, without its hidden layers, without the VectorCraft document it carries, its
|
||||
/// pages linked)? Writing it back would lose the rest, so Save doesn't (it asks for a name).
|
||||
pub fn is_partial(&self) -> bool {
|
||||
self.pages.is_some() || self.crop != CropTo::default() || self.password.is_some() || !self.layers || !self.native
|
||||
self.pages.is_some() || self.crop != CropTo::default() || self.password.is_some() || !self.layers || !self.native || self.link_pages
|
||||
}
|
||||
|
||||
/// The PDF import options: `pages` resolved against the file's page count. A PDF-compatible
|
||||
@@ -201,6 +214,82 @@ fn page_options(page: usize, o: &LoadOptions) -> ImportOptions {
|
||||
ImportOptions { pages: Some(vec![page]), crop: o.crop, password: o.password.clone(), text_as: o.text_as, layers: false, ..Default::default() }
|
||||
}
|
||||
|
||||
/// `document.open` with `linkPages`: the pages `o` picks of the PDF (or PDF-compatible `.ai` or
|
||||
/// `.ait`) `name`, read from `path`, as a document holding one placed document per page, linked to
|
||||
/// that page of the file as File › Place with Link places one ([`document::source`]). Each page
|
||||
/// gets an artboard of its `o.crop` box (its art's bounds for `bounding`), in a row from the left
|
||||
/// with the gap a PDF's artboards open with, and a layer named after the page. The document opens
|
||||
/// in CMYK when most of the pages read in CMYK, unless `o.color_mode` names a mode. Refused without a
|
||||
/// path, for any other file, with a password (a linked page is read again without one), with
|
||||
/// `textAs: outlines` (a linked page shows the file's own type) and for a file over
|
||||
/// [`document::MAX_FILE`] → the document and the notes of reading its pages.
|
||||
pub(super) fn linked_pages(name: &str, bytes: &[u8], path: Option<&str>, o: &LoadOptions) -> Result<(Document, Vec<String>)> {
|
||||
const C: &str = "document.open";
|
||||
let file = super::file_name(name);
|
||||
if !document::is_pdf(name, bytes) {
|
||||
return Err(bad(C, format!("linkPages: `{file}` isn't a PDF or a PDF-compatible .ai file")));
|
||||
}
|
||||
let path = path.ok_or_else(|| bad(C, "linkPages needs the file's path: linked pages are read from it"))?;
|
||||
if o.password.is_some() {
|
||||
return Err(bad(C, "linkPages can't open a file with a password: linked pages are read again without it"));
|
||||
}
|
||||
if o.text_as != TextAs::Text {
|
||||
return Err(bad(C, "linkPages can't be used with textAs: outlines: linked pages show the file's own type"));
|
||||
}
|
||||
if bytes.len() > document::MAX_FILE {
|
||||
return Err(bad(C, format!("linkPages: `{file}` is over {} MB", document::MAX_FILE >> 20)));
|
||||
}
|
||||
let count = vectorcraft_pdf::open_pdf(bytes, None).map_err(err)?.page_count();
|
||||
let picked = match &o.pages {
|
||||
None => (0..count).collect(),
|
||||
Some(range) => parse_range(range, count).map_err(|e| err(format!("pages: {e}")))?,
|
||||
};
|
||||
let bounding = o.crop == CropTo::Bounding;
|
||||
let mut doc = Document::new(1.0, 1.0);
|
||||
let board = doc.artboards.first().cloned().ok_or_else(|| err("a new document has no artboard"))?;
|
||||
doc.artboards.clear();
|
||||
doc.layers.clear();
|
||||
let link = crate::cmd::links::link_info(path, bytes);
|
||||
let gap = ImportOptions::default().artboard_gap;
|
||||
// Every page's object keeps the same one copy of the file.
|
||||
let shared = Arc::new(bytes.to_vec());
|
||||
let (mut x, mut warnings, mut cmyk_pages) = (0.0, Vec::<String>::new(), 0);
|
||||
for (i, &index) in picked.iter().enumerate() {
|
||||
let page = index as u32 + 1;
|
||||
let s = document::shared_source(name, &shared, Some(path), page, bounding, document::page_box(o.crop), C)?;
|
||||
cmyk_pages += usize::from(s.color_mode == ColorMode::Cmyk);
|
||||
let mut node = document::node(&mut doc, &s, LinkInfo { page: Some(page), ..link.clone() }, bounding);
|
||||
if let NodeKind::PlacedDocument(p) = &mut node.kind {
|
||||
p.xf = Affine::translate((x, 0.0));
|
||||
}
|
||||
node.name = Some(file.clone());
|
||||
let mut layer = Node::layer(doc.alloc_id(), &format!("Page {page}"), LayerColor::Preset((i % 27) as u8));
|
||||
if let NodeKind::Layer { children, .. } = &mut layer.kind {
|
||||
children.push(Arc::new(node));
|
||||
}
|
||||
doc.layers.push(Arc::new(layer));
|
||||
let rect = Rect::new(x, 0.0, x + s.frame.width(), s.frame.height());
|
||||
doc.artboards.push(Artboard { id: i as u32 + 1, name: format!("Artboard {}", i + 1), rect, ..board.clone() });
|
||||
x = rect.x1 + gap;
|
||||
for w in s.warnings {
|
||||
if !warnings.contains(&w) {
|
||||
warnings.push(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Pages painted mostly in CMYK read as CMYK documents, as opening them as art does: the
|
||||
// document opens in CMYK (with the CMYK default swatches) when most of its pages do.
|
||||
if cmyk_pages * 2 > picked.len() {
|
||||
doc.color_mode = ColorMode::Cmyk;
|
||||
let (swatches, groups) = vectorcraft_color::default_swatches(ColorMode::Cmyk.model());
|
||||
(doc.swatches, doc.swatch_groups) = (swatches.into(), groups.into());
|
||||
}
|
||||
if let Some(mode) = o.color_mode.filter(|m| *m != doc.color_mode) {
|
||||
super::super::colormgmt::set_color_mode(&mut doc, mode, true, None, o.grays);
|
||||
}
|
||||
Ok((doc, warnings))
|
||||
}
|
||||
|
||||
const INFO: &str = "document.pdfInfo";
|
||||
|
||||
/// `document.pdfInfo`.
|
||||
|
||||
@@ -20,7 +20,7 @@ use std::sync::Arc;
|
||||
|
||||
use vectorcraft_doc::links::hash_bytes;
|
||||
use vectorcraft_doc::placed_document::{PREVIEW_MAX, PREVIEW_PX_PER_PT, key_for};
|
||||
use vectorcraft_doc::{Document, ImageBlob, LinkInfo, Node, NodeKind, PageBox, PlacedDocument, PlacementOptions};
|
||||
use vectorcraft_doc::{ColorMode, Document, ImageBlob, LinkInfo, Node, NodeKind, PageBox, PlacedDocument, PlacementOptions};
|
||||
use vectorcraft_geom::{Affine, Rect};
|
||||
use vectorcraft_pdf::CropTo;
|
||||
|
||||
@@ -221,22 +221,57 @@ pub(crate) struct Source {
|
||||
/// What is shown, in the file's coordinates: the artboard, the page's box or the art's bounds.
|
||||
pub frame: Rect,
|
||||
pub warnings: Vec<String>,
|
||||
/// The colour mode the file's art was read in (a PDF page painted mostly in CMYK reads in CMYK).
|
||||
pub color_mode: ColorMode,
|
||||
}
|
||||
|
||||
/// The VectorCraft document `name` (`bytes`, from `path` when there is one) read for showing
|
||||
/// artboard `page`, or the PDF or `.ai` file for showing page `page` clipped to its box
|
||||
/// `page_box` (its art's bounds when `bounding`).
|
||||
pub(crate) fn source(name: &str, bytes: &[u8], path: Option<&str>, page: u32, bounding: bool, page_box: PageBox, cmd: &str) -> Result<Source> {
|
||||
read_source(name, bytes, None, path, (page, bounding, page_box), cmd)
|
||||
}
|
||||
|
||||
/// [`source`] of the file whose bytes `bytes` holds, kept by the blob without a copy of its own:
|
||||
/// several pages of one file read this way share one copy of it.
|
||||
pub(crate) fn shared_source(
|
||||
name: &str,
|
||||
bytes: &Arc<Vec<u8>>,
|
||||
path: Option<&str>,
|
||||
page: u32,
|
||||
bounding: bool,
|
||||
page_box: PageBox,
|
||||
cmd: &str,
|
||||
) -> Result<Source> {
|
||||
read_source(name, bytes, Some(bytes), path, (page, bounding, page_box), cmd)
|
||||
}
|
||||
|
||||
/// [`source`], the blob keeping `shared` when given (else a copy of `bytes`); `shown` is the page
|
||||
/// (or artboard), whether the art's bounds are shown, and the page's box.
|
||||
fn read_source(
|
||||
name: &str,
|
||||
bytes: &[u8],
|
||||
shared: Option<&Arc<Vec<u8>>>,
|
||||
path: Option<&str>,
|
||||
shown: (u32, bool, PageBox),
|
||||
cmd: &str,
|
||||
) -> Result<Source> {
|
||||
let (page, bounding, page_box) = shown;
|
||||
// The same document placed the same way is read once (placing it again is free).
|
||||
let key = key_for(&hash_bytes(bytes), page, bounding, page_box);
|
||||
// Resolving its links depends on where the file is and on what the caller may read, so a hit
|
||||
// needs the same path, and nothing is shared with or from a confined (automation) caller.
|
||||
let confined = crate::file_access::current().is_some();
|
||||
if !confined && let Some((_, s)) = lock_memo().iter().find(|(at, s)| s.key == key && at.as_deref() == path).cloned() {
|
||||
if !confined && let Some((_, mut s)) = lock_memo().iter().find(|(at, s)| s.key == key && at.as_deref() == path).cloned() {
|
||||
// The same bytes: the copy given is kept instead, so the pages that share it go on sharing.
|
||||
if let Some(b) = shared {
|
||||
s.blob.bytes = b.clone();
|
||||
}
|
||||
return Ok(s);
|
||||
}
|
||||
let a = document_art(name, bytes, path, page, bounding, page_box, cmd)?;
|
||||
let mut blob = ImageBlob::new(mime(name, bytes), bytes.to_vec());
|
||||
let bytes = shared.cloned().unwrap_or_else(|| Arc::new(bytes.to_vec()));
|
||||
let mut blob = ImageBlob { mime: mime(name, &bytes).into(), bytes, proxy: None };
|
||||
let shown = PlacedDocument {
|
||||
link: LinkInfo { page: Some(page), ..LinkInfo::new(path.unwrap_or(name)) },
|
||||
key: key.clone(),
|
||||
@@ -249,9 +284,10 @@ pub(crate) fn source(name: &str, bytes: &[u8], path: Option<&str>, page: u32, bo
|
||||
};
|
||||
let art = group(a.nodes, a.clip);
|
||||
blob.proxy = preview(&a.doc, &art, a.frame).map(Arc::new);
|
||||
let color_mode = a.doc.color_mode;
|
||||
// Drawing it needn't read it again.
|
||||
vectorcraft_doc::placed_document::prime(&shown, blob.bytes.clone(), (a.doc, art, a.frame));
|
||||
let s = Source { blob, key, page_box, frame: a.frame, warnings: a.warnings };
|
||||
let s = Source { blob, key, page_box, frame: a.frame, warnings: a.warnings, color_mode };
|
||||
if !confined {
|
||||
let mut m = lock_memo();
|
||||
if m.len() >= MEMO_SIZE {
|
||||
|
||||
@@ -599,6 +599,100 @@ fn with_link_a_pdf_page_places_linked() {
|
||||
assert!(near(centre_colour(&s.doc().unwrap().doc), RED));
|
||||
}
|
||||
|
||||
/// The colour at the centre of `r` in `doc`.
|
||||
fn colour_at(doc: &Document, r: Rect) -> [u8; 3] {
|
||||
let img = vectorcraft_render::Renderer::new().render_region(doc, r, 1.0, true);
|
||||
let [red, g, b, _] = img.pixel((r.width() / 2.0) as u32, (r.height() / 2.0) as u32);
|
||||
[red, g, b]
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn link_pages_opens_each_picked_pdf_page_as_a_linked_placed_document() {
|
||||
let dir = Folder::new("open-pdf-link-pages");
|
||||
let src = dir.file("flyer.pdf");
|
||||
pdf_source(&src, "pdf", &[RED, BLUE, RED], 0);
|
||||
let mut s = Session::new();
|
||||
let r = s.execute("document.open", &json!({"path": src, "linkPages": true, "pages": "2-3"})).unwrap();
|
||||
assert_eq!((r["format"].as_str(), r["restored"].as_bool()), (Some("pdf"), Some(false)), "{r}");
|
||||
let st = s.doc().unwrap();
|
||||
assert_eq!(st.path, None, "Save asks for a name instead of writing over the PDF");
|
||||
let d = &st.doc;
|
||||
let boards: Vec<Rect> = d.artboards.iter().map(|a| a.rect).collect();
|
||||
assert_eq!(boards, [Rect::new(0.0, 0.0, 100.0, 50.0), Rect::new(136.0, 0.0, 236.0, 50.0)]);
|
||||
let layers: Vec<_> = d.layers.iter().map(|l| l.name.clone().unwrap_or_default()).collect();
|
||||
assert_eq!(layers, ["Page 2", "Page 3"]);
|
||||
let mut ids = vec![];
|
||||
for (layer, (page, board)) in d.layers.iter().zip([(2, boards[0]), (3, boards[1])]) {
|
||||
let kids = layer.children().unwrap();
|
||||
assert_eq!(kids.len(), 1);
|
||||
let p = placed(&s, kids[0].id);
|
||||
assert_eq!((p.link.path.as_str(), p.link.page, p.bounding), (src.as_str(), Some(page), false), "{p:?}");
|
||||
assert_eq!(bounds(&s, kids[0].id), board);
|
||||
ids.push(kids[0].id);
|
||||
}
|
||||
assert!(near(colour_at(d, boards[0]), BLUE));
|
||||
assert!(near(colour_at(d, boards[1]), RED));
|
||||
let links = s.execute("links.list", &json!({})).unwrap()["links"].clone();
|
||||
assert_eq!(links.as_array().map(Vec::len), Some(2), "{links}");
|
||||
assert!(links.as_array().unwrap().iter().all(|row| row["document"] == true && row["status"] == "ok"), "{links}");
|
||||
// Embed makes a page editable art.
|
||||
s.execute("links.embed", &json!({"ids": [ids[0].0]})).unwrap();
|
||||
let d = &s.doc().unwrap().doc;
|
||||
assert_eq!(d.node(ids[0]).unwrap().kind_label(), "Clip Group");
|
||||
assert_eq!(bounds(&s, ids[0]), boards[0]);
|
||||
assert!(near(colour_at(d, boards[0]), BLUE));
|
||||
// Every page, with the art's bounds as the artboard.
|
||||
s.execute("document.open", &json!({"path": src, "linkPages": true, "cropTo": "bounding"})).unwrap();
|
||||
let d = &s.doc().unwrap().doc;
|
||||
assert_eq!(d.artboards.len(), 3);
|
||||
assert!(d.layers.iter().flat_map(|l| l.children().unwrap()).all(|n| matches!(&n.kind, NodeKind::PlacedDocument(p) if p.bounding)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linked_pages_share_one_copy_of_the_file_and_open_in_the_pages_color_mode() {
|
||||
use vectorcraft_testkit::pdf::{PdfPage, pdf};
|
||||
let dir = Folder::new("open-pdf-link-pages-cmyk");
|
||||
let src = dir.file("cmyk.pdf");
|
||||
let page = || PdfPage::new(100.0, 50.0, "0 0.5 1 0 k 0 0 100 50 re f");
|
||||
write(&src, &pdf(&[page(), page(), page()], None));
|
||||
let mut s = Session::new();
|
||||
s.execute("document.open", &json!({"path": src, "linkPages": true})).unwrap();
|
||||
let d = &s.doc().unwrap().doc;
|
||||
assert_eq!(d.color_mode, vectorcraft_doc::ColorMode::Cmyk, "pages painted in CMYK open as a CMYK document");
|
||||
let blobs: Vec<_> = d.images.values().map(|b| b.bytes.clone()).collect();
|
||||
assert_eq!(blobs.len(), 3);
|
||||
assert!(blobs.iter().all(|b| std::sync::Arc::ptr_eq(b, &blobs[0])), "one copy of the file for every page");
|
||||
s.execute("document.open", &json!({"path": src, "linkPages": true, "colorMode": "rgb"})).unwrap();
|
||||
assert_eq!(s.doc().unwrap().doc.color_mode, vectorcraft_doc::ColorMode::Rgb, "colorMode wins");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn link_pages_is_refused_without_a_path_for_other_files_and_with_a_password_or_outlines() {
|
||||
let dir = Folder::new("open-pdf-link-pages-refused");
|
||||
let src = dir.file("flyer.pdf");
|
||||
pdf_source(&src, "pdf", &[RED, BLUE], 0);
|
||||
let bytes = std::fs::read(&src).unwrap();
|
||||
let logo = dir.file("logo.vectorcraft");
|
||||
source(&logo, 100.0, 50.0, RED, 0);
|
||||
let mut s = Session::new();
|
||||
let cases = [
|
||||
(json!({"name": "flyer.pdf", "dataBase64": vectorcraft_format::base64_encode(&bytes), "linkPages": true}), "needs the file's path"),
|
||||
(json!({"path": logo, "linkPages": true}), "isn't a PDF"),
|
||||
(json!({"path": src, "linkPages": true, "password": "secret"}), "password"),
|
||||
(json!({"path": src, "linkPages": true, "textAs": "outlines"}), "textAs: outlines"),
|
||||
(json!({"path": src, "linkPages": "yes"}), "linkPages must be true or false"),
|
||||
(json!({"path": src, "linkPages": true, "pages": "3"}), "pages:"),
|
||||
];
|
||||
for (p, says) in cases {
|
||||
let e = s.execute("document.open", &p).unwrap_err().to_string();
|
||||
assert!(e.contains(says), "{p}: {e}");
|
||||
}
|
||||
assert!(s.documents().is_empty(), "nothing opened");
|
||||
// Off, the pages open as editable art.
|
||||
s.execute("document.open", &json!({"path": src, "linkPages": false})).unwrap();
|
||||
assert!(!s.doc().unwrap().doc.has_placed());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_changed_pdf_is_modified_and_update_links_reads_it_again() {
|
||||
let dir = Folder::new("placed-pdf-update");
|
||||
|
||||
Reference in New Issue
Block a user