mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-13 08:29:14 +00:00
Save PDF: OK asks before replacing a picked file, and both routes write one title rule
File > Save as PDF... leaves the file to the save panel. When the panel picked the file the document was opened from, and opening it left things out, Save PDF's OK replaced it without asking. It now asks first, as it does for a given path. Export As PDF with Use Artboards off wrote DisplayDocTitle true for a document without a title of its own, titled after its file, while the same dialog with Use Artboards on, and document.exportPdf, wrote false. Every PDF Export As writes now shows the title in the viewer only when it is the document's own. docs/control-protocol.md: app.export continues in savePdf for any PDF, and ui.savePdfDialog's reply with useArtboards false is Export As's. The progress-doc line is its own line.
This commit is contained in:
@@ -158,6 +158,30 @@ fn a_page_of_the_arts_bounds_is_written_in_the_background() {
|
||||
assert_eq!(vectorcraft_pdf::import(&w[0].1).unwrap().artboards.len(), 1);
|
||||
}
|
||||
|
||||
/// A document without a title of its own, saved to a file, is titled after that file in the PDF,
|
||||
/// and viewers show the file name, with or without Use Artboards; one with its own title shows
|
||||
/// that title.
|
||||
#[test]
|
||||
fn a_page_of_the_arts_bounds_shows_the_title_only_when_it_is_the_documents_own() {
|
||||
let (mut app, written, _) = app();
|
||||
app.session.doc_mut().unwrap().path = Some("/docs/Poster.vectorcraft".into());
|
||||
for artboards in [true, false] {
|
||||
app.run("ui.savePdfDialog", json!({"path": "/tmp/t.pdf", "useArtboards": artboards, "compression": {"compressText": false}})).unwrap();
|
||||
super::super::confirm(&mut app).unwrap();
|
||||
}
|
||||
std::sync::Arc::make_mut(&mut app.session.doc_mut().unwrap().doc).title = "Spring Sale".into();
|
||||
app.run("ui.savePdfDialog", json!({"path": "/tmp/t.pdf", "useArtboards": false, "compression": {"compressText": false}})).unwrap();
|
||||
super::super::confirm(&mut app).unwrap();
|
||||
let w = written.borrow();
|
||||
assert_eq!(w.len(), 3);
|
||||
let texts: Vec<String> = w.iter().map(|(_, b)| String::from_utf8_lossy(b).into_owned()).collect();
|
||||
for (pdf, artboards) in texts[..2].iter().zip([true, false]) {
|
||||
assert!(pdf.contains("/Title(Poster)"), "useArtboards {artboards}");
|
||||
assert!(!pdf.contains("/DisplayDocTitle true"), "useArtboards {artboards}");
|
||||
}
|
||||
assert!(texts[2].contains("/Title(Spring Sale)") && texts[2].contains("/DisplayDocTitle true"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn warnings_reach_the_status_and_errors_keep_the_dialog_open() {
|
||||
let (mut app, written, opened) = app();
|
||||
|
||||
@@ -44,6 +44,9 @@ pub fn export(app: &mut VectorcraftApp, format: Option<&str>, path: Option<Strin
|
||||
std::borrow::Cow::Borrowed(_) => st.doc.clone(),
|
||||
std::borrow::Cow::Owned(d) => std::sync::Arc::new(d),
|
||||
};
|
||||
// A PDF shows its title in the viewer's title bar only when it is the document's own, not the
|
||||
// file name it was titled after, as `document.export` writes it.
|
||||
let own_title = fileio::has_own_title(&doc);
|
||||
let doc = fileio::titled_for_export(&doc, st.path.as_deref(), f.id).map_or(doc, std::sync::Arc::new);
|
||||
let uid = st.uid;
|
||||
let path = export_path(app, path, f)?;
|
||||
@@ -68,6 +71,9 @@ pub fn export(app: &mut VectorcraftApp, format: Option<&str>, path: Option<Strin
|
||||
let (mut params, target) = (params.clone(), path.clone());
|
||||
if let Some(o) = params.as_object_mut() {
|
||||
o.insert("path".into(), json!(path));
|
||||
if f.id == "pdf" {
|
||||
o.insert("_displayDocTitle".into(), json!(own_title));
|
||||
}
|
||||
}
|
||||
// The progress dialog's counts and Cancel: a cancelled export stops drawing and never starts
|
||||
// writing.
|
||||
@@ -199,15 +205,29 @@ pub(crate) fn run_to_file(app: &mut VectorcraftApp, id: &str, ext: &str, mut par
|
||||
/// to `path` (else a picked or suggested name) → `{path, bytes, warnings}`. With
|
||||
/// `viewAfterSaving`, the written file opens in the system viewer (not on the web, which
|
||||
/// downloads it). Writing over the file the document was opened from, when opening it left things
|
||||
/// out, asks first and runs `file.export.pdf` again on OK.
|
||||
pub fn export_pdf(app: &mut VectorcraftApp, params: Value) -> Result<Value, String> {
|
||||
/// out, asks first, for a given path and for one the save panel picked, and runs
|
||||
/// `file.export.pdf` again on OK.
|
||||
pub fn export_pdf(app: &mut VectorcraftApp, mut params: Value) -> Result<Value, String> {
|
||||
if let Some(path) = params.get("path").and_then(Value::as_str)
|
||||
&& let Some(r) = ask_before_losing(app, path, "file.export.pdf", ¶ms)
|
||||
{
|
||||
return Ok(r);
|
||||
}
|
||||
let view = params.get("viewAfterSaving").and_then(Value::as_bool).unwrap_or(false);
|
||||
let (path, mut v) = run_to_file(app, "document.exportPdf", "pdf", params)?;
|
||||
let given = params.as_object_mut().and_then(|o| o.remove("path")).and_then(|p| p.as_str().map(str::to_string));
|
||||
let mut v = app.session.execute("document.exportPdf", ¶ms).map_err(|e| e.to_string())?;
|
||||
let bytes = take_output(&mut v)?;
|
||||
let path = match given {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
let picked = target_path(app, None, "pdf")?;
|
||||
if let Some(r) = ask_before_losing(app, &picked, "file.export.pdf", ¶ms) {
|
||||
return Ok(r);
|
||||
}
|
||||
picked
|
||||
}
|
||||
};
|
||||
write_reporting(app, &path, &bytes)?;
|
||||
if view {
|
||||
view_file(app, &path);
|
||||
}
|
||||
|
||||
@@ -293,6 +293,23 @@ fn replacing_a_file_that_opening_left_things_out_of_asks_first() {
|
||||
app.ui.dialog = None;
|
||||
}
|
||||
assert_eq!(std::fs::read(&path).unwrap(), saved);
|
||||
// Also when File › Save as PDF… leaves the file to the save panel and the panel picks the PDF
|
||||
// the document was opened from.
|
||||
let pdf = d.join("a.pdf").to_string_lossy().to_string();
|
||||
std::fs::write(&pdf, b"%PDF-1.7 as opened").unwrap();
|
||||
app.session.doc_mut().unwrap().imported_from = Some(pdf.clone());
|
||||
let picked = pdf.clone();
|
||||
app.services.pick_save = Some(Box::new(move |_| Some(picked.clone())));
|
||||
for artboards in [true, false] {
|
||||
app.run("ui.savePdfDialog", json!({"useArtboards": artboards})).unwrap();
|
||||
dialogs::confirm(&mut app).unwrap();
|
||||
let asked = app.ui.dialog.as_ref().filter(|d| d.kind == dialogs::confirm::KIND);
|
||||
assert!(asked.is_some_and(|d| d.str("detail").contains("“hidden type”")), "picked, useArtboards {artboards}");
|
||||
app.ui.dialog = None;
|
||||
}
|
||||
assert_eq!(std::fs::read(&pdf).unwrap(), b"%PDF-1.7 as opened");
|
||||
app.services.pick_save = None;
|
||||
app.session.doc_mut().unwrap().imported_from = Some(path.clone());
|
||||
// Another name doesn't ask.
|
||||
let copy = d.join("b.svg").to_string_lossy().to_string();
|
||||
assert!(app.run("file.exportAs", json!({"path": copy, "format": "svg"})).unwrap().get("pending").is_none());
|
||||
|
||||
@@ -108,9 +108,9 @@ the two can still win that race.
|
||||
| `ui.render` | `{path?, scale?, artboard?, data?}` | render an artboard headlessly (PNG with the pixels of `app.export {format: "png", background: "white"}` at the same scale; `artboard`: its 0-based index, default the first): written to `path`, else (or with `data: true` as well) sent back as `pngBase64` |
|
||||
| `ui.resize` / `ui.focus` | | |
|
||||
| `app.open` / `app.save` / `app.export` / `app.quit` | `{path, importer?, params?}` / `{path?}` / `{path?, format?, artboard?, range?, scale?, …}` | `app.open` reads every format `document.open` reads (see `document.formats`) and returns its result (`{index, title, format, warnings, …}`; `warnings` say what didn't come in as it was, such as an EPS shown as its preview image and why its PostScript couldn't be read), or null when a dialog asks first or a library loads. The status bar (`ui.inspect` → `ui.status`) counts the `warnings` and names the first that says reading the file left something out (the notes that make Save ask before writing over it), else the first; with several, hovering it lists them in that order, as many as fit in the window (at most 30), then how many more. After `app.save`, and `app.export` with a `path`, the status bar counts the reply's `warnings` and names the first, and with several, hovering it lists them in the reply's order (a save's notes start with what the format loses). With `importer` (an importer plug-in's format id, `plugin.<id>`) it runs `file.openWith`: the file opens with that importer, also when a built-in format reads its extension, with the importer's parameters `params` (their defaults when omitted; no dialog asks for them). Without `importer`, `params` go to the importer that opens the file, and a built-in format does not read them. `app.export` encodes through the engine's `document.export` (same options; the document keeps its path) and writes `path` through the host; without `path` it returns `{dataBase64, format, bytes}`, as headless mode does. It refuses a key that no format takes, and an unknown key inside a PDF or EPS option object, before it writes anything (`docs/mcp.md`, Parameter keys). `app.save` refuses a key that no save format takes, beside the path or in `options`, the same way. `app.quit`, `file.close` and `file.closeAll` first open a `saveChanges` dialog for each modified document (they return `{"pending": "saveChanges"}`): `ui.dialog.confirm` saves, `ui.dialog.set {field: "discard", value: true}` then confirm discards, `ui.dialog.cancel` cancels the whole close or quit |
|
||||
| `app.export` with `useArtboards` | `{path, useArtboards: true, range? \| artboards?, …}` | Export As: PNG, JPEG and WebP write one file per chosen artboard (default all; SVG does so for a `range`), `<path stem>-<artboard>.<ext>`, and return `{path, files: [path…]}`; a PDF keeps them as pages of `path`. `useArtboards: false` covers the bounds of the visible art. The menu's Export As… is `file.exportAs`: the `exportAs` dialog (`format`, `useArtboards`, `all`, `range`), then a save dialog and the format's options: `pngOptions` / `jpgOptions` / `webpOptions`, `svgOptions`, or `savePdf` for a PDF with Use Artboards |
|
||||
| `app.export` with `useArtboards` | `{path, useArtboards: true, range? \| artboards?, …}` | Export As: PNG, JPEG and WebP write one file per chosen artboard (default all; SVG does so for a `range`), `<path stem>-<artboard>.<ext>`, and return `{path, files: [path…]}`; a PDF keeps them as pages of `path`. `useArtboards: false` covers the bounds of the visible art. The menu's Export As… is `file.exportAs`: the `exportAs` dialog (`format`, `useArtboards`, `all`, `range`), then a save dialog and the format's options: `pngOptions` / `jpgOptions` / `webpOptions`, `svgOptions`, or `savePdf` for a PDF |
|
||||
| `ui.dialog.*` on `gradientStop` | `{field: "color" \| "opacity" \| "location", value}` | double-clicking a stop on the Gradient tool's annotator opens its popover (fields `index`, `x`, `y`, `tab`); set `color` (hex), `opacity` or `location` (percentages) and `ui.dialog.confirm` to apply them to the selected stop. `gradient.selectStop {index}` picks the stop the annotator, the panels and Delete/←/→ (`ui.key`) act on |
|
||||
| `engine.execute` `ui.savePdfDialog` | `{path?, preset?, range?, …document.exportPdf options}` | opens the Save PDF dialog (`file.export.pdf` with no params opens it too). Its fields are the `document.exportPdf` options: set `compatibility` with `ui.dialog.set {field: "compatibility", value: "1.5"}`, a whole section with `{field: "compression", value: {…}}`, the artboards with `__allArtboards: false` and `range`, the visible section with `__section` (General, Compression, Marks and Bleeds, Output, Advanced, Security, Summary). `useArtboards: false` opens it for one page of the visible art's bounds, as Export As with Use Artboards off does: the dialog has no Artboards section and its `__artBounds` field is true. `ui.dialog.confirm` writes `path` (else asks for one), returns `{path, bytes, warnings}` and opens the file when `viewAfterSaving` is set |
|
||||
| `engine.execute` `ui.savePdfDialog` | `{path?, preset?, range?, …document.exportPdf options}` | opens the Save PDF dialog (`file.export.pdf` with no params opens it too). Its fields are the `document.exportPdf` options: set `compatibility` with `ui.dialog.set {field: "compatibility", value: "1.5"}`, a whole section with `{field: "compression", value: {…}}`, the artboards with `__allArtboards: false` and `range`, the visible section with `__section` (General, Compression, Marks and Bleeds, Output, Advanced, Security, Summary). `useArtboards: false` opens it for one page of the visible art's bounds, as Export As with Use Artboards off does: the dialog has no Artboards section and its `__artBounds` field is true. `ui.dialog.confirm` writes `path` (else asks for one), returns `{path, bytes, warnings}` and opens the file when `viewAfterSaving` is set. With `useArtboards: false` it writes the page as `app.export` does and returns `{path, warnings}`, or `{background: true, status}` while Background Export writes it |
|
||||
| `file.save` / `file.saveAs` / `file.saveCopy` / `file.saveAsTemplate` (via `engine.execute`) | `{path?, format?, options?, svg?}` | Save writes the document's own file in its own format (a document opened from or saved as SVG/PDF saves as that again; the result's `warnings` say what the format loses). With `path` the file is written at once (Save As or a Copy to an SVG path without SVG options first opens SVG Options, below). Without one (never saved, a converted older file, Save As, a Copy, a Template) the app shows a save panel with one file type per save format (`vectorcraft`, `template`, `pdf`, `svg`, `svgz`, `ai`; for a Template, `template` and `ait`), then the format's options: SVG opens the `svgOptions` dialog (below; returns `{"pending": "svgOptions", "path"}`), PDF the `savePdf` dialog (its `__save` field names the save command); `ui.dialog.confirm` finishes the save. A name typed in the panel without the file type's extension gets it, and a name the panel returns with the extension of a format the panel does not list has it replaced (in a native save, `card.png` and `card.ait` are saved as `card.vectorcraft`, and `notes.v2`, whose extension names no format, is saved as `notes.v2.vectorcraft`); when a file of that name exists, which the panel did not ask about, the save (and every export or other write through a save panel) answers `cancelled` and opens a `replaceFile` dialog (fields `message`, `detail`, `path`): `ui.dialog.confirm` writes that file, `ui.dialog.cancel` writes nothing. A save or export that writes other files beside that path (a file per artboard, linked images, image maps) checks them before it writes anything: when any of them exists, it writes nothing and opens a `replaceFiles` dialog (fields `message`, `detail` (each file's name when there are several), `files`, `__command`, `__params`; the reply is `{"pending": "replaceFiles"}`, or comes once a Background Save or Export ends): `ui.dialog.confirm` runs the save or export again and replaces them, `ui.dialog.cancel` writes nothing; a dialog open when a background job asks stays open, and the question follows it. A save does not ask about the files the document's last Save or Save As in this session wrote. On the web a `saveOptions` dialog (fields `path`, `format`) first names the file and picks the format. `file.formatOptions {format?}` reads the options a save would use. `file.revert` opens a `confirm` dialog (`{"pending": "confirm"}`); `ui.dialog.confirm` reloads the saved file in the same tab. Location refusals return `READ_ONLY_LOCATION: <reason>` without a fallback panel for automation; see [Save location errors](#save-location-errors). |
|
||||
| SVG Options (`engine.execute` of `file.export.svg`, `file.saveAs`, `file.saveCopy`) | `{}` / `{path?, svg?: {…}}` | `file.export.svg` without params, and Save As or Save a Copy to an `.svg`/`.svgz` path without SVG options, open the `svgOptions` dialog (`ui.dialog.set` its fields: `styling`, `outlineText`, `images`, `objectIds`, `decimals`, `minify`, `responsive`, `fewerTspans`, `metadata`, `preserveEditing`, `useArtboards`, `allArtboards`, `range`, `showCode`, `profile` (`svg11`/`tiny12`), `encoding` (`utf8`/`utf16`/`latin1`), `embedFonts`; `ui.dialog.confirm` exports or saves and remembers the choices). With `svg: {…}` (the `document.formats` SVG options) they run directly. `app.save {path?, svg?}` takes the same options; a document saved as SVG saves as SVG again with the same options |
|
||||
| `engine.execute` `file.place` | `{paths?}` / `{path \| name+dataBase64, …file.place params}` | with no file, picks files and opens the `place` dialog; with `paths`, opens it for them (fields `files`, `link`, `template`, `replace`; `__replace` says whether Replace applies, `__info` describes each file). `ui.dialog.confirm` places one file centered in the view (or in place of the selected object) and loads the place cursor with several (`file.place.queue`). In the desktop app the dialog reads its files off the UI thread: `__info` is empty until they are read, `ui.dialog.confirm` returns `{background: true, status}`, `ui.inspect` lists the files still being read in `placing`, and the file is placed (or the cursor loaded) once it is read. With a file, it runs `file.place` centered in the view, at once. Files dropped on the window with no document open open. On the web, files dropped on the canvas are placed at the drop point (linked; Shift embeds) and files dropped off it (the tab bar) open, as in Illustrator; desktop drops carry no pointer position, so documents (native, SVG, PDF, `.ai`, EPS, DXF, EMF/WMF) open as tabs of their own and pictures and text are placed in the middle of the view. Swatch libraries and color books (`.vcswatches`, `.gpl`, `.ase`, `.acb`), graphic style libraries (`.vcstyles`), Libraries panel files (`.vclibrary`), presets (`.vcflattener`, `.vcpdfpresets`, `.vcprintpresets`, `.vcperspective`, `.vcexportpresets`), plug-ins (`.wasm`) and shortcut sets (`.kys`, in Import Shortcut Set) open wherever they are dropped, as File › Open opens them. With the cursor loaded (`ui.inspect` → `tool: "place"`, `toolOptions: {count, current, name, names}`), `ui.pointer` clicks and drags place files (in the desktop app each file is read off the UI thread and placed once it is read, and `ui.inspect` lists it in `placing` until then) and `ui.key` ←/→/↑/↓ and Escape cycle and discard them |
|
||||
|
||||
@@ -1513,7 +1513,8 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
|
||||
In a `.ai` opened from its editing data, an envelope made with a warp, a mesh or a top object opens as a live envelope of its contents, with its warp style, bend and distortions, its mesh points and handles or its top object's outline, its Envelope Options, its Fidelity and whether its contents are being edited. When Distort Appearance is on and the size the file saves for the contents matches their bounds with strokes, the contents map from that stroked box, until an edit in Edit Contents changes how far the strokes reach; VectorCraft's new envelopes map from the geometric box, as do contents with no width or no height, such as a single straight line. An envelope that can't be read, or that VectorCraft draws differently from the file's drawn art, keeps that drawn art, with a note. An envelope of type opens live, its type laid out from the text document as its contents, where VectorCraft's own envelope of that type draws as the drawn art the file saves (compared as for the other envelopes, seen on white and on black, so white type counts, and only where the layers with it are compared with the page and draw as it, the envelope weighed as the art it draws); elsewhere, and where the type's font isn't available, a text or visibility variable binds the type, or the envelope is in a symbol's or a pattern's art, it keeps its drawn art, with its type hidden and a note that says the type isn't editable (#3179; `crates/eps/src/import/ai/tests.rs`, `crates/eps/src/import/tests_native.rs`). A warp of type whose own warp, VectorCraft's, doesn't draw as its drawn art is tried again as a mesh envelope on the grid the file saves for it: its type stays editable and keeps the file's shape, but the warp's settings (style, bend) are lost, as the document has no place for them on a mesh. Type with strokes in an envelope with Distort Appearance on is also tried mapped from its glyphs grown by as far as its strokes paint along a side (half the weight of a centred stroke), without what miter joins add at corners, where the size the file saves for it doesn't already give that box; the drawn-art check decides which one opens. An envelope's grid reads its way round from all its rows and columns, so a mesh bent far round reads the way it runs. While an envelope's contents are edited, its mesh is kept in its dictionary, and in some files that mesh lies away from the drawn art by a move, the contents with the drawn art (as if the mesh hadn't followed the envelope when it moved): a mesh or top object saved in Edit Contents whose mesh draws the contents as large as the drawn art but elsewhere is tried again, after every other way, with its mesh moved onto that art, and opens there where it then draws as it. The drawn art of these files strokes type in an envelope along its bent outlines at the stroke's own weight, Distort Appearance on or off, and VectorCraft draws it so (#6644): an envelope of stroked type whose grid squeezes or stretches it opens live where it draws as that art.
|
||||
In a `.ai` opened from its editing data, Convert to Shape on a whole path or type object opens as a live Rectangle, Rounded Rectangle or Ellipse effect with its Absolute or Relative size, its corner radius, its place among the object's effects and whether it is hidden. A relative size in the file is added to each side. A relative Ellipse, or a setting VectorCraft doesn't read, keeps the drawn art (`crates/eps/src/import/ai/style/tests.rs`, `crates/eps/src/import/ai/tests.rs`, `crates/eps/src/import/tests_native.rs`).
|
||||
In a `.ai` opened from its editing data, Bas Relief in an art style takes its Detail, Smoothness and Light direction from the style's settings block, and an object other than type with it opens as itself with the live effect. A settings block that VectorCraft can't read (malformed, another effect's, a value out of range or a key it doesn't know) keeps the object's drawn look (part of #637; [raster effects reference](https://helpx.adobe.com/illustrator/desktop/special-effects-styles/apply-filter-effects/raster-effects-reference.html), [Photoshop File Formats Specification](https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/), [scripting the filter gallery](https://community.adobe.com/questions-712/scripting-support-for-the-filter-gallery-1064646); `crates/eps/src/import/ai/style/tests.rs`, `crates/eps/src/import/ai/tests.rs`).
|
||||
PDF export and `.ai` saves write a page with a side longer than 14,400 pt with a `UserUnit` (PDF 1.6), its longer side divided by 14,400 and rounded up to the next thousandth, so that no side is longer than 14,400 units (ISO 32000-1, Annex C.2); a PDF 1.5 file with such a page is written as PDF 1.6 with PDF 1.5's encryption, PDF/X files and PDF 1.3 and 1.4 files write it in points (in PDF 1.3 and 1.4, a page past 32,767 pt that needs a real number past 32,767 is refused with an error naming it), a page whose short side is 3 pt or longer but would be under 3 units at that `UserUnit` gets a smaller one that keeps the short side at 3 units, and the export's warnings give each such page's size and how it is written (#6382). The Save PDF dialog's "Write pages longer than 200 in at full size in points" (`longPagesInPoints`, off by default) writes such pages in points without a `UserUnit` at every PDF version, in files without a PDF/X or PDF/A standard. Export As PDF with Use Artboards off also opens the Save PDF dialog, without its Artboards section, and writes one page of the visible art's bounds with the dialog's options (`document.exportPdf {useArtboards: false}`; `crates/ui-egui/src/dialogs/tests_export.rs`, `crates/engine/src/cmd/fileio/tests_pdf.rs`).
|
||||
PDF export and `.ai` saves write a page with a side longer than 14,400 pt with a `UserUnit` (PDF 1.6), its longer side divided by 14,400 and rounded up to the next thousandth, so that no side is longer than 14,400 units (ISO 32000-1, Annex C.2); a PDF 1.5 file with such a page is written as PDF 1.6 with PDF 1.5's encryption, PDF/X files and PDF 1.3 and 1.4 files write it in points (in PDF 1.3 and 1.4, a page past 32,767 pt that needs a real number past 32,767 is refused with an error naming it), a page whose short side is 3 pt or longer but would be under 3 units at that `UserUnit` gets a smaller one that keeps the short side at 3 units, and the export's warnings give each such page's size and how it is written (#6382). The Save PDF dialog's "Write pages longer than 200 in at full size in points" (`longPagesInPoints`, off by default) writes such pages in points without a `UserUnit` at every PDF version, in files without a PDF/X or PDF/A standard.
|
||||
Export As PDF with Use Artboards off also opens the Save PDF dialog, without its Artboards section, and writes one page of the visible art's bounds with the dialog's options (`document.exportPdf {useArtboards: false}`; `crates/ui-egui/src/dialogs/tests_export.rs`, `crates/engine/src/cmd/fileio/tests_pdf.rs`).
|
||||
PDF export warns when a PDF/X file has no bleed on a side where the document's bleed has one, and writes the file as asked.
|
||||
A PDF/A-2b export at PDF 1.4 with a page longer than 14,400 pt is written as PDF 1.6 with a `UserUnit` and a warning, whether or not `longPagesInPoints` is on, so its page boundaries stay within PDF/A-2's limit of 14,400 units (ISO 19005-2, 6.1.13) unless the page is too narrow for that (`crates/pdf/src/tests/marks.rs`).
|
||||
PDF/X-1a, PDF/X-3 and PDF 1.3 files keep the art under the hidden part of a picture, gradient, pattern, mesh or raster effect that a clipping mask or clipping layer crops: flattening rasterizes only the part of such art that its clipping path shows, on its own page and on the next one. Layers the file leaves out are left out of the flattening, and transparent art that a clipping path hides entirely is removed (`crates/engine/src/cmd/fileio/tests_pdfx.rs`).
|
||||
|
||||
Reference in New Issue
Block a user