Files
vectorcraft/crates/engine/src/cmd/fileio/tests_pdfoutput.rs
T
bflatasticandClaude Opus 5.5 a5367b0846 M4.74: real text with embedded subset fonts in PDF
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>
2026-10-05 16:25:21 +07:00

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:?}");
}