mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-02 14:16:56 -06:00
M4.44: PDF presets: built-ins in code, saved presets, Edit > PDF Presets
Built-in presets are generated in code (VectorCraft Default, High Quality Print, Press Quality, Smallest File Size and PDF/X-1a, X-3, X-4, the latter listed as not supported yet) and read-only. Saved presets live with the preferences (Prefs.pdf_presets) and work as `preset` wherever PDF options are taken: document.exportPdf, pdfSettings, export/serialize as PDF and .ai saves. pdf.preset.list / save / delete / export / import manage them (.vcpdfpresets files; opening one imports it). The Save PDF dialog's Save Preset… saves its settings under a name; Edit > PDF Presets… (ui.pdfPresetsDialog) lists the presets with each one's description and differences from the default, with New…, Edit… (the Save PDF dialog as a preset editor, ui.pdfPresetDialog), Delete, Import… and Export…. document.pdfSettings now lists what differs from the chosen preset. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
0ffaa427d1
commit
9aa29aa0fd
@@ -166,6 +166,8 @@ mod tests_import_color;
|
||||
#[cfg(test)]
|
||||
mod tests_import_options;
|
||||
#[cfg(test)]
|
||||
mod tests_presets;
|
||||
#[cfg(test)]
|
||||
mod tests_settings;
|
||||
#[cfg(test)]
|
||||
mod tests_stroke;
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
//! PDF presets: named [`PdfSettings`]. The built-in ones are generated here (the app default
|
||||
//! keeps the document editable).
|
||||
//! PDF presets: named [`PdfSettings`]. The built-in ones are generated here (the app default,
|
||||
//! which keeps the document editable, plus presets for print, press, small files and the PDF/X
|
||||
//! standards); the user's own live in the app's preferences.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::PdfSettings;
|
||||
use crate::{
|
||||
BleedSettings, Compatibility, CompressionSettings, Downsample, ImageCodec, ImageCompression, JpegQuality, MonoCodec, MonoCompression,
|
||||
PdfSettings, Standard,
|
||||
};
|
||||
|
||||
/// The built-in preset every PDF export starts from.
|
||||
pub const DEFAULT_PRESET: &str = "VectorCraft Default";
|
||||
@@ -20,13 +24,75 @@ pub struct PdfPreset {
|
||||
pub settings: PdfSettings,
|
||||
}
|
||||
|
||||
/// Colour and greyscale images resampled (bicubic) to `ppi` above `above` ppi, compressed with
|
||||
/// `codec`.
|
||||
fn images(ppi: f64, above: f64, codec: ImageCodec, quality: JpegQuality) -> ImageCompression {
|
||||
ImageCompression { downsample: Downsample::Bicubic, ppi, above_ppi: above, compression: codec, quality }
|
||||
}
|
||||
|
||||
/// Compression with the same settings for colour and greyscale images, and CCITT Group 4
|
||||
/// monochrome images resampled to `mono_ppi` above 1.5 × that.
|
||||
fn compression(img: ImageCompression, mono_ppi: f64) -> CompressionSettings {
|
||||
let mono = MonoCompression { downsample: Downsample::Bicubic, ppi: mono_ppi, above_ppi: mono_ppi * 1.5, compression: MonoCodec::CcittG4 };
|
||||
CompressionSettings { color: img.clone(), gray: img, mono, compress_text: true }
|
||||
}
|
||||
|
||||
/// Settings for print: PDF 1.6, images kept at 300 ppi, the document's bleed when `bleed`.
|
||||
fn print(codec: ImageCodec, bleed: bool) -> PdfSettings {
|
||||
PdfSettings {
|
||||
compatibility: Compatibility::Pdf16,
|
||||
compression: compression(images(300.0, 450.0, codec, JpegQuality::Maximum), 1200.0),
|
||||
bleed: BleedSettings { use_document: bleed, ..Default::default() },
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// A PDF/X preset: print settings for `standard` at PDF version `compatibility`.
|
||||
fn pdf_x(standard: Standard, compatibility: Compatibility) -> PdfSettings {
|
||||
PdfSettings { standard, compatibility, ..print(ImageCodec::Auto, true) }
|
||||
}
|
||||
|
||||
/// Every built-in preset, the app default first.
|
||||
pub fn builtin_presets() -> Vec<PdfPreset> {
|
||||
vec![PdfPreset {
|
||||
name: DEFAULT_PRESET.into(),
|
||||
description: "Keeps the document editable: VectorCraft reopens the PDF with nothing lost. For files you'll edit again.".into(),
|
||||
settings: PdfSettings { preserve_editing: true, ..Default::default() },
|
||||
}]
|
||||
let preset = |name: &str, description: &str, settings| PdfPreset { name: name.into(), description: description.into(), settings };
|
||||
vec![
|
||||
preset(
|
||||
DEFAULT_PRESET,
|
||||
"Keeps the document editable: VectorCraft reopens the PDF with nothing lost. For files you'll edit again.",
|
||||
PdfSettings { preserve_editing: true, ..Default::default() },
|
||||
),
|
||||
preset("High Quality Print", "For desktop printers and proofs: images stay at print resolution.", print(ImageCodec::Auto, false)),
|
||||
preset(
|
||||
"Press Quality",
|
||||
"For commercial printing: high-resolution images compressed losslessly, the document's bleed.",
|
||||
print(ImageCodec::Zip, true),
|
||||
),
|
||||
preset(
|
||||
"Smallest File Size",
|
||||
"For screens, email and the web: images resampled to 100 ppi and compressed.",
|
||||
PdfSettings {
|
||||
compatibility: Compatibility::Pdf16,
|
||||
fast_web_view: true,
|
||||
compression: compression(images(100.0, 150.0, ImageCodec::Jpeg, JpegQuality::Medium), 300.0),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
preset(
|
||||
"PDF/X-1a:2001",
|
||||
"Print exchange with CMYK and spot colours only (PDF/X output is not supported yet).",
|
||||
pdf_x(Standard::PdfX1a, Compatibility::Pdf14),
|
||||
),
|
||||
preset(
|
||||
"PDF/X-3:2002",
|
||||
"Print exchange allowing colour-managed colours (PDF/X output is not supported yet).",
|
||||
pdf_x(Standard::PdfX3, Compatibility::Pdf14),
|
||||
),
|
||||
preset(
|
||||
"PDF/X-4:2010",
|
||||
"Print exchange keeping live transparency and layers (PDF/X output is not supported yet).",
|
||||
pdf_x(Standard::PdfX4, Compatibility::Pdf16),
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
/// The built-in preset `name` names (any case; `default` is the app default).
|
||||
|
||||
@@ -424,6 +424,12 @@ impl PdfSettings {
|
||||
within("advanced.fontSubsetPercent", self.advanced.font_subset_percent, 0.0, 100.0, "%")
|
||||
}
|
||||
|
||||
/// Forget the passwords (presets never store them).
|
||||
pub fn clear_passwords(&mut self) {
|
||||
self.security.open_password.clear();
|
||||
self.security.permissions_password.clear();
|
||||
}
|
||||
|
||||
/// Options that are accepted but not applied by the writer yet, one warning each.
|
||||
pub fn warnings(&self) -> Vec<String> {
|
||||
let d = Self::default();
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
//! The built-in PDF presets: generated in code, valid, described, and round-tripping as JSON.
|
||||
|
||||
use crate::*;
|
||||
|
||||
#[test]
|
||||
fn built_in_presets() {
|
||||
let all = builtin_presets();
|
||||
assert_eq!(all[0].name, DEFAULT_PRESET);
|
||||
assert!(all[0].settings.preserve_editing, "the app default keeps the document editable");
|
||||
assert_eq!(all.len(), 7);
|
||||
for p in &all {
|
||||
assert_eq!(p.settings.check_values(), Ok(()), "{}", p.name);
|
||||
assert!(!p.description.is_empty());
|
||||
let back: PdfPreset = serde_json::from_value(serde_json::to_value(p).unwrap()).unwrap();
|
||||
assert_eq!(&back, p, "round-trips");
|
||||
}
|
||||
// PDF/X presets name standards the writer doesn't produce yet.
|
||||
let x4 = builtin_preset("pdf/x-4:2010").unwrap();
|
||||
assert!(matches!(x4.settings.check(), Err(PdfError::Unsupported(_))));
|
||||
assert_eq!(builtin_preset("default").unwrap().name, DEFAULT_PRESET);
|
||||
assert!(builtin_preset("Nope").is_none());
|
||||
}
|
||||
Reference in New Issue
Block a user