Files
vectorcraft/crates/ui-egui/src/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

50 lines
2.3 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Save PDF › Output and Advanced: the colour conversion, profiles, output intent and real text
//! chosen in the dialog reach the written file, with nothing reported as not applied.
use serde_json::json;
use vectorcraft_color::cms::GENERIC_CMYK;
use crate::tests_svg::app;
use crate::{dialogs, theme};
/// One headless frame of the dialog layer.
fn frame(app: &mut crate::VectorcraftApp) {
let ctx = egui::Context::default();
theme::install_fonts(&ctx);
let mut out = ctx.run_ui(egui::RawInput::default(), |ui| dialogs::show(app, ui.ctx()));
out.textures_delta.clear();
}
#[test]
fn output_and_advanced_choices_reach_the_pdf() {
let (mut app, written) = app("/tmp/unused.pdf");
app.run("text.create", json!({"x": 10, "y": 70, "text": "Proof"})).unwrap();
app.run("ui.savePdfDialog", json!({"path": "/tmp/press.pdf"})).unwrap();
let fields = &mut app.ui.dialog.as_mut().unwrap().fields;
fields.insert("__section".into(), json!("Output"));
fields.insert(
"output".into(),
json!({"conversion": "destination", "destination": GENERIC_CMYK, "profiles": "destination", "outputIntent": GENERIC_CMYK, "outputConditionId": "Proofing", "trapped": true}),
);
fields.insert("advanced".into(), json!({"outlineText": false}));
fields.insert("compression".into(), json!({"compressText": false}));
frame(&mut app);
// PDF/A greys the output intent out (it carries its own); a name that isn't a profile shows.
for (standard, intent) in [("pdfA2b", GENERIC_CMYK), ("none", "Registered Press")] {
let d = app.ui.dialog.as_mut().unwrap();
d.fields.insert("standard".into(), json!(standard));
d.fields["output"]["outputIntent"] = json!(intent);
frame(&mut app);
}
let d = app.ui.dialog.as_mut().unwrap();
d.fields.insert("standard".into(), json!("none"));
d.fields["output"]["outputIntent"] = json!(GENERIC_CMYK);
d.fields.insert("__section".into(), json!("Advanced"));
frame(&mut app);
dialogs::confirm(&mut app).unwrap();
assert!(app.ui.status.starts_with("Saved /tmp/press.pdf") && !app.ui.status.contains("note"), "{}", app.ui.status);
let pdf = String::from_utf8_lossy(&written.borrow()[0].1).into_owned();
assert!(pdf.contains("/S/GTS_PDFX") && pdf.contains("/Trapped/True") && pdf.contains("/ICCBased"));
assert!(pdf.contains("/ToUnicode"), "real text");
}