mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-26 02:37:01 -06:00
With Convert Text to Outlines off (advanced.outlineText: false), type is written as selectable, searchable text: each glyph in its font, embedded as a subset with a ToUnicode map, where its outline would be (rotated, scaled, skewed and on-path type too; gradients keep their place). Fonts whose licence forbids embedding, or allows only whole fonts, fall back to outlines with a warning; a subset threshold below 100% is accepted with a warning. Laid-out glyphs now carry their glyph id and transform. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
41 lines
2.2 KiB
Rust
41 lines
2.2 KiB
Rust
//! `document.exportPdf` output and advanced options reach the writer: colours converted and tagged
|
|
//! with profiles, the output intent written, and type as real text.
|
|
|
|
use serde_json::{Value, json};
|
|
|
|
use super::*;
|
|
|
|
fn pdf(s: &mut Session, p: Value) -> (String, Value) {
|
|
let v = s.execute("document.exportPdf", &p).unwrap();
|
|
let bytes = vectorcraft_format::base64_decode(v["dataBase64"].as_str().unwrap()).unwrap();
|
|
(String::from_utf8_lossy(&bytes).into_owned(), v["warnings"].clone())
|
|
}
|
|
|
|
#[test]
|
|
fn export_pdf_converts_colours_writes_the_output_intent_and_real_text() {
|
|
let mut s = Session::new();
|
|
s.execute("file.new", &json!({"width": 200, "height": 100})).unwrap();
|
|
s.execute("shape.rectangle", &json!({"x": 10, "y": 10, "width": 40, "height": 40})).unwrap();
|
|
s.execute("paint.setFill", &json!({"color": {"model": "rgb", "r": 1.0, "g": 0.0, "b": 0.0}})).unwrap();
|
|
s.execute("text.create", &json!({"x": 80, "y": 60, "text": "Press ready"})).unwrap();
|
|
let generic = vectorcraft_color::cms::GENERIC_CMYK;
|
|
let p = json!({
|
|
"preset": "VectorCraft Default",
|
|
"compression": {"compressText": false},
|
|
"output": {"conversion": "destination", "destination": generic, "profiles": "destination", "outputIntent": generic, "outputConditionId": "Generic press", "trapped": true},
|
|
"advanced": {"outlineText": false},
|
|
});
|
|
let (text, warnings) = pdf(&mut s, p.clone());
|
|
assert_eq!(warnings, json!([]), "everything asked for is applied");
|
|
assert!(text.contains("/ICCBased") && text.contains("/N 4"), "CMYK colours tagged with the destination");
|
|
assert!(text.contains("/S/GTS_PDFX") && text.contains("/Trapped/True"));
|
|
assert!(text.contains("/ToUnicode") && (text.contains("/FontFile2") || text.contains("/FontFile3")), "real text");
|
|
// The Summary has nothing to report either.
|
|
let mut q = p;
|
|
q["includeDocument"] = json!(true);
|
|
assert_eq!(s.execute("document.pdfSettings", &q).unwrap()["warnings"], json!([]));
|
|
// Converting to a profile that isn't there is a bad parameter.
|
|
let bad = s.execute("document.exportPdf", &json!({"output": {"conversion": "destination", "destination": "Missing"}}));
|
|
assert!(matches!(bad, Err(crate::EngineError::BadParams { .. })), "{bad:?}");
|
|
}
|