winprint_ext/ticket/
print_capabilities.rs1use super::{
2 document::{
3 reader::{ParsableXmlDocument, ParsePrintSchemaError},
4 ParameterInit, PrintCapabilitiesDocument, PrintFeatureOption, WithProperties, NS_PSF,
5 NS_PSK,
6 },
7 Copies, FeatureOptionPack, JobDuplex, PageMediaSize, PageOrientation, PageOutputColor,
8 PageResolution, PrintTicket,
9};
10use crate::{
11 printer::PrinterDevice,
12 utils::{stream::read_com_stream, wchar},
13};
14use scopeguard::defer;
15use std::fmt::Debug;
16use thiserror::Error;
17use windows::{
18 core::{BSTR, PCWSTR},
19 Win32::{
20 Graphics::Printing::PrintTicket::{
21 PTCloseProvider, PTGetPrintCapabilities, PTOpenProvider,
22 },
23 UI::Shell::SHCreateMemStream,
24 },
25};
26use xml::name::OwnedName;
27
28#[derive(Error, Debug)]
29pub enum FetchPrintCapabilitiesError {
31 #[error("Failed to open print ticket provider")]
33 OpenProviderFailed(#[source] windows::core::Error),
34 #[error("Stream not allocated")]
36 StreamNotAllocated,
37 #[error("Cannot get print capabilities")]
39 CannotGetPrintCapabilities(String, #[source] windows::core::Error),
40 #[error("Failed to read stream")]
42 ReadStreamFailed(#[source] windows::core::Error),
43 #[error("Failed to parse print capabilities")]
45 ParseError(#[source] ParsePrintSchemaError),
46}
47
48#[derive(Clone, Debug)]
49pub struct PrintCapabilities {
51 pub document: PrintCapabilitiesDocument,
53}
54
55impl PrintCapabilities {
56 pub fn fetch_xml(device: &PrinterDevice) -> Result<Vec<u8>, FetchPrintCapabilitiesError> {
58 Self::fetch_xml_for_ticket(device, None)
59 }
60
61 pub fn fetch_xml_for_ticket(
63 device: &PrinterDevice,
64 ticket: Option<&PrintTicket>,
65 ) -> Result<Vec<u8>, FetchPrintCapabilitiesError> {
66 unsafe {
67 let provider =
68 PTOpenProvider(PCWSTR(wchar::to_wide_chars(device.os_name()).as_ptr()), 1)
69 .map_err(FetchPrintCapabilitiesError::OpenProviderFailed)?;
70 defer! {
71 let _ = PTCloseProvider(provider);
72 }
73 let stream =
74 SHCreateMemStream(None).ok_or(FetchPrintCapabilitiesError::StreamNotAllocated)?;
75 let ticket_stream = ticket
76 .map(|x| {
77 SHCreateMemStream(Some(x.get_xml()))
78 .ok_or(FetchPrintCapabilitiesError::StreamNotAllocated)
79 })
80 .transpose()?;
81
82 let mut error_message = BSTR::default();
83 PTGetPrintCapabilities(
84 provider,
85 ticket_stream.as_ref(),
86 &stream,
87 Some(&mut error_message),
88 )
89 .map_err(|win32_error| {
90 FetchPrintCapabilitiesError::CannotGetPrintCapabilities(
91 error_message.to_string(),
92 win32_error,
93 )
94 })?;
95
96 let data =
97 read_com_stream(&stream).map_err(FetchPrintCapabilitiesError::ReadStreamFailed)?;
98
99 Ok(data)
100 }
101 }
102
103 pub fn fetch(device: &PrinterDevice) -> Result<PrintCapabilities, FetchPrintCapabilitiesError> {
105 let xml = Self::fetch_xml(device)?;
106 let document = PrintCapabilitiesDocument::parse_from_bytes(xml)
107 .map_err(FetchPrintCapabilitiesError::ParseError)?;
108 Ok(PrintCapabilities { document })
109 }
110
111 pub fn default_parameters(&self) -> impl Iterator<Item = ParameterInit> + '_ {
113 self.document.parameter_defs.iter().filter_map(|param_def| {
114 param_def
115 .default_value()
116 .map(|default_value| ParameterInit {
117 name: param_def.name.clone(),
118 value: default_value.clone(),
119 })
120 })
121 }
122
123 pub fn default_parameters_for<'a>(
125 &'a self,
126 filters: &'a [OwnedName],
127 ) -> impl Iterator<Item = ParameterInit> + 'a {
128 let mut filters = filters
129 .iter()
130 .map(|x| (&x.namespace, &x.local_name))
131 .collect::<Vec<_>>();
132 filters.sort_unstable();
133 self.document
134 .parameter_defs
135 .iter()
136 .filter(move |param_def| {
137 filters
138 .binary_search(&(¶m_def.name.namespace, ¶m_def.name.local_name))
139 .is_ok()
140 })
141 .filter_map(move |param_def| {
142 param_def
143 .default_value()
144 .map(|default_value| ParameterInit {
145 name: param_def.name.clone(),
146 value: default_value.clone(),
147 })
148 })
149 }
150
151 pub fn options_for_feature(
153 &self,
154 feature_name: OwnedName,
155 ) -> impl Iterator<Item = &PrintFeatureOption> + '_ {
156 self.document
157 .features
158 .iter()
159 .filter(move |x| {
160 x.name.local_name == feature_name.local_name
161 && x.name.namespace == feature_name.namespace
162 })
163 .flat_map(|x| x.options.iter())
164 }
165
166 pub fn page_media_sizes(&self) -> impl Iterator<Item = PageMediaSize> + '_ {
168 PageMediaSize::list(self)
169 }
170
171 pub fn page_orientations(&self) -> impl Iterator<Item = PageOrientation> + '_ {
173 PageOrientation::list(self)
174 }
175
176 pub fn duplexes(&self) -> impl Iterator<Item = JobDuplex> + '_ {
181 JobDuplex::list(self)
182 }
183
184 pub fn page_output_colors(&self) -> impl Iterator<Item = PageOutputColor> + '_ {
186 PageOutputColor::list(self)
187 }
188
189 pub fn page_resolutions(&self) -> impl Iterator<Item = PageResolution> + '_ {
191 PageResolution::list(self)
192 }
193
194 pub fn max_copies(&self) -> Option<Copies> {
199 self.document
200 .parameter_defs
201 .iter()
202 .find(|x| {
203 x.name.local_name == "JobCopiesAllDocuments"
204 && x.name.namespace_ref() == Some(NS_PSK)
205 })
206 .and_then(|x| x.get_property("MaxValue", Some(NS_PSF)))
207 .and_then(|x| x.value.as_ref())
208 .and_then(|x| x.integer())
209 .and_then(|x| u16::try_from(x).ok())
210 .map(Copies)
211 }
212}
213
214#[cfg(test)]
215mod tests {
216 use super::PrintCapabilities;
217 use crate::test_utils::null_device;
218 #[test]
219 fn test_fetch_xml() {
220 let device = null_device::thread_local();
221 PrintCapabilities::fetch_xml(&device).unwrap();
222 }
223
224 #[test]
225 fn test_fetch_xml_and_parse() {
226 let device = null_device::thread_local();
227 PrintCapabilities::fetch(&device).unwrap();
228 }
229}