winprint_ext/ticket/
print_ticket.rs1use super::{
2 document::{writer::XmlDocumentRoot, PrintTicketDocument},
3 DEFAULT_PRINT_TICKET_XML,
4};
5use crate::{printer::PrinterDevice, utils::wchar};
6use scopeguard::defer;
7use std::ptr;
8use thiserror::Error;
9use windows::{
10 core::{BSTR, PCWSTR},
11 Win32::{
12 Graphics::{
13 Gdi::{DM_IN_BUFFER, DM_OUT_BUFFER},
14 Printing::{
15 ClosePrinter, DocumentPropertiesW, OpenPrinterW,
16 PrintTicket::{
17 kPTJobScope, kUserDefaultDevmode, PTCloseProvider,
18 PTConvertPrintTicketToDevMode, PTOpenProvider, PTReleaseMemory,
19 },
20 PRINTER_HANDLE,
21 },
22 },
23 UI::{Shell::SHCreateMemStream, WindowsAndMessaging::IDOK},
24 },
25};
26
27#[derive(Clone, Debug)]
28pub struct PrintTicket {
30 pub(crate) xml: Vec<u8>,
31}
32
33impl Default for PrintTicket {
34 fn default() -> Self {
35 Self {
36 xml: DEFAULT_PRINT_TICKET_XML.into(),
37 }
38 }
39}
40
41#[derive(Error, Debug)]
42pub enum ToDevModeError {
44 #[error("Failed to open print ticket provider")]
46 OpenProviderFailed(#[source] windows::core::Error),
47 #[error("Stream not allocated")]
49 StreamNotAllocated,
50 #[error("Failed to convert print ticket to dev mode")]
52 ConvertPrintTicketToDevModeFailed(String, #[source] windows::core::Error),
53 #[error("Failed to open printer")]
55 FailedToOpenPrinter(#[source] windows::core::Error),
56 #[error("Failed to correct dev mode via DocumentProperties")]
60 FailedToCorrectDevMode,
61}
62
63impl PrintTicket {
64 pub fn from_xml(xml: impl Into<Vec<u8>>) -> Self {
66 Self { xml: xml.into() }
67 }
68
69 pub fn into_xml(self) -> Vec<u8> {
71 self.xml
72 }
73
74 pub fn get_xml(&self) -> &[u8] {
76 &self.xml
77 }
78
79 pub fn to_dev_mode(&self, device: &PrinterDevice) -> Result<Vec<u8>, ToDevModeError> {
83 unsafe {
84 let provider =
85 PTOpenProvider(PCWSTR(wchar::to_wide_chars(device.os_name()).as_ptr()), 1)
86 .map_err(ToDevModeError::OpenProviderFailed)?;
87 defer! {
88 let _ = PTCloseProvider(provider);
89 }
90
91 let stream = SHCreateMemStream(Some(self.get_xml()))
92 .ok_or(ToDevModeError::StreamNotAllocated)?;
93
94 let mut dev_mode_size = 0;
95 let mut dev_mode_data = std::ptr::null_mut();
96 let mut error_message = BSTR::default();
97 PTConvertPrintTicketToDevMode(
98 provider,
99 &stream,
100 kUserDefaultDevmode,
101 kPTJobScope,
102 ptr::addr_of_mut!(dev_mode_size),
103 ptr::addr_of_mut!(dev_mode_data),
104 Some(&mut error_message),
105 )
106 .map_err(|win32_error| {
107 ToDevModeError::ConvertPrintTicketToDevModeFailed(
108 error_message.to_string(),
109 win32_error,
110 )
111 })?;
112 defer! {
113 let _ = PTReleaseMemory(dev_mode_data as *mut _);
114 }
115
116 let printer_name = wchar::to_wide_chars(device.os_name());
117 let printer_handle = {
118 let mut printer_handle = PRINTER_HANDLE::default();
119 OpenPrinterW(
120 PCWSTR(printer_name.as_ptr()),
121 ptr::addr_of_mut!(printer_handle),
122 None,
123 )
124 .map_err(ToDevModeError::FailedToOpenPrinter)?;
125 printer_handle
126 };
127 defer! {
128 let _ = ClosePrinter(printer_handle);
129 }
130
131 let mut buffer_size = DocumentPropertiesW(
132 None,
133 printer_handle,
134 PCWSTR(printer_name.as_ptr()),
135 None,
136 None,
137 0,
138 );
139
140 if buffer_size <= 0 {
141 return Err(ToDevModeError::FailedToCorrectDevMode);
142 }
143
144 buffer_size = buffer_size * 2 + 8192;
147
148 let mut buffer = vec![0u8; buffer_size as usize];
149 if DocumentPropertiesW(
150 None,
151 printer_handle,
152 PCWSTR(printer_name.as_ptr()),
153 Some(buffer.as_mut_ptr() as *mut _),
154 Some(dev_mode_data as *mut _),
155 (DM_IN_BUFFER | DM_OUT_BUFFER).0,
156 ) != IDOK.0
157 {
158 return Err(ToDevModeError::FailedToCorrectDevMode);
159 }
160
161 Ok(buffer)
162 }
163 }
164}
165
166impl From<PrintTicketDocument> for PrintTicket {
167 fn from(value: PrintTicketDocument) -> Self {
168 Self {
169 xml: value.to_xml(),
170 }
171 }
172}