Print: load Windows.Data.Pdf.dll before using it; fix the error hint (#5884)

Review follow-up on #6395:
- Check that the DLL actually loads (winsafe `LoadLibrary`, no unsafe) rather than that the
  file exists: a DLL that is present but cannot load still aborts at the delay-load stub.
- The hint no longer says to clear Print Settings; that doesn't take a page off the PDF path.
  It now points to Save as PDF, or printing an RGB image without page text or labels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Higgs32584
2026-10-17 16:53:11 -04:00
parent 5f8d92f9f0
commit 86f6199b01
4 changed files with 48 additions and 19 deletions
Generated
+1
View File
@@ -4810,6 +4810,7 @@ dependencies = [
"tempfile",
"thiserror 2.0.21",
"wat",
"winsafe",
]
[[package]]
+5
View File
@@ -43,6 +43,11 @@ roxmltree = "0.20"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tempfile = "3"
# `file.print` loads Windows.Data.Pdf.dll before calling into it (#5884): the safe binding
# ui-egui and craft-print already use, pinned to the same version.
[target.'cfg(target_os = "windows")'.dependencies]
winsafe = { version = "=0.0.29", default-features = false, features = ["kernel"] }
[dev-dependencies]
photocraft-affinity = { workspace = true, features = ["synth"] }
# Synthetic camera raw files for the open tests.
+21 -14
View File
@@ -437,24 +437,31 @@ fn render_and_spool(doc: &Document, p: &Value, cmd: &str) -> Result<Value> {
}
/// Windows prints a PDF through Windows.Data.Pdf.dll, which the apps delay-load (see their
/// `build.rs`) so a Windows image without it still starts. Calling into it when it is missing
/// would abort the process, so check that it is installed first (#5884).
/// `build.rs`) so a Windows image without it still starts. Calling into it when it cannot load
/// would abort the process, so check that it loads first (#5884).
fn require_windows_pdf() -> Result<()> {
if !cfg!(windows) {
return Ok(());
}
let root = std::env::var_os("SystemRoot").unwrap_or_else(|| std::ffi::OsString::from(r"C:\Windows"));
if windows_pdf_installed(std::path::Path::new(&root)) {
Ok(())
} else {
Err(other(
"printing this page needs the Windows PDF component (Windows.Data.Pdf.dll), which is not installed. Save as PDF and print it from another app, or clear Print Settings to print directly.",
))
#[cfg(target_os = "windows")]
{
let root = std::env::var_os("SystemRoot").unwrap_or_else(|| std::ffi::OsString::from(r"C:\Windows"));
if !windows_pdf_loads(&windows_pdf_dll(std::path::Path::new(&root))) {
return Err(other(
"printing this page needs the Windows PDF component (Windows.Data.Pdf.dll), which is missing or cannot load. Save as PDF and print it from another app, or print an RGB image without page text or labels to print directly.",
));
}
}
Ok(())
}
fn windows_pdf_installed(system_root: &std::path::Path) -> bool {
system_root.join("System32").join("Windows.Data.Pdf.dll").is_file()
#[cfg(target_os = "windows")]
fn windows_pdf_dll(system_root: &std::path::Path) -> std::path::PathBuf {
system_root.join("System32").join("Windows.Data.Pdf.dll")
}
/// Loads the DLL rather than checking the file exists: one that is present but cannot load (a
/// dependency stripped too) would still abort at the delay-load stub.
#[cfg(target_os = "windows")]
fn windows_pdf_loads(dll: &std::path::Path) -> bool {
dll.to_str().is_some_and(|p| winsafe::HINSTANCE::LoadLibrary(p).is_ok())
}
/// Keep CMYK output profiles and page text on the existing PDF composition path. GDI currently
+21 -5
View File
@@ -435,12 +435,28 @@ fn printers_lists_the_spooler_destinations_without_a_document() {
}
}
#[cfg(target_os = "windows")]
#[test]
fn windows_pdf_component_is_detected_by_its_dll() {
// #5884: a Windows image without Windows.Data.Pdf.dll reports an error instead of aborting.
fn windows_pdf_component_is_detected_by_loading_its_dll() {
// #5884: a Windows image without a loadable Windows.Data.Pdf.dll reports an error instead of
// aborting at the delay-load stub.
let dir = tempfile::tempdir().unwrap();
assert!(!windows_pdf_installed(dir.path()));
let dll = windows_pdf_dll(dir.path());
assert!(!windows_pdf_loads(&dll));
// Present but not loadable counts as missing.
std::fs::create_dir(dir.path().join("System32")).unwrap();
std::fs::write(dir.path().join("System32").join("Windows.Data.Pdf.dll"), b"MZ").unwrap();
assert!(windows_pdf_installed(dir.path()));
std::fs::write(&dll, b"MZ").unwrap();
assert!(!windows_pdf_loads(&dll));
let root = std::env::var_os("SystemRoot").unwrap();
let system = windows_pdf_dll(std::path::Path::new(&root));
if system.is_file() {
assert!(windows_pdf_loads(&system));
assert!(require_windows_pdf().is_ok());
}
}
#[cfg(not(target_os = "windows"))]
#[test]
fn the_windows_pdf_check_is_a_no_op_elsewhere() {
assert!(require_windows_pdf().is_ok());
}