winprint_ext/printer/
dxgi.rs1use crate::printer::PrinterDevice;
2use crate::ticket::PrintTicket;
3use crate::ticket::ToDevModeError;
4use crate::utils::com::ComInitializer;
5use crate::utils::print_completion_source::PrintCompletionSource;
6use crate::utils::print_completion_source::PrintCompletionWaiter;
7use crate::utils::wchar;
8use std::cmp::max;
9use std::ffi::OsStr;
10use std::ptr;
11use thiserror::Error;
12use windows::core::Interface;
13use windows::core::PCWSTR;
14use windows::Win32::Foundation::E_UNEXPECTED;
15use windows::Win32::Foundation::HMODULE;
16use windows::Win32::Graphics::Direct2D::D2D1CreateFactory;
17use windows::Win32::Graphics::Direct2D::ID2D1Device;
18use windows::Win32::Graphics::Direct2D::ID2D1DeviceContext;
19use windows::Win32::Graphics::Direct2D::ID2D1Factory1;
20use windows::Win32::Graphics::Direct2D::ID2D1PrintControl;
21use windows::Win32::Graphics::Direct2D::D2D1_DEVICE_CONTEXT_OPTIONS_NONE;
22use windows::Win32::Graphics::Direct2D::D2D1_FACTORY_OPTIONS;
23use windows::Win32::Graphics::Direct2D::D2D1_FACTORY_TYPE_SINGLE_THREADED;
24use windows::Win32::Graphics::Direct2D::D2D1_PRINT_CONTROL_PROPERTIES;
25use windows::Win32::Graphics::Direct3D::D3D_DRIVER_TYPE_HARDWARE;
26use windows::Win32::Graphics::Direct3D11::D3D11CreateDevice;
27use windows::Win32::Graphics::Direct3D11::ID3D11Device;
28use windows::Win32::Graphics::Direct3D11::ID3D11DeviceContext;
29use windows::Win32::Graphics::Direct3D11::D3D11_CREATE_DEVICE_BGRA_SUPPORT;
30use windows::Win32::Graphics::Direct3D11::D3D11_SDK_VERSION;
31use windows::Win32::Graphics::Dxgi::IDXGIDevice;
32use windows::Win32::Graphics::Gdi::DEVMODEW;
33use windows::Win32::Graphics::Imaging::CLSID_WICImagingFactory2;
34use windows::Win32::Graphics::Imaging::D2D::IWICImagingFactory2;
35use windows::Win32::Storage::Xps::Printing::IPrintDocumentPackageStatusEvent;
36use windows::Win32::Storage::Xps::Printing::IPrintDocumentPackageTarget;
37use windows::Win32::Storage::Xps::Printing::IPrintDocumentPackageTargetFactory;
38use windows::Win32::Storage::Xps::Printing::PrintDocumentPackageTargetFactory;
39use windows::Win32::System::Com::CoCreateInstance;
40use windows::Win32::System::Com::IConnectionPointContainer;
41use windows::Win32::System::Com::CLSCTX_ALL;
42use windows::Win32::UI::Shell::SHCreateMemStream;
43
44#[derive(Error, Debug)]
45pub enum DxgiPrintContextError {
47 #[error("Print Ticker Error")]
49 PrintTicketError(#[source] ToDevModeError),
50 #[error("Failed to create event")]
52 FailedToCreateEvent(#[source] windows::core::Error),
53 #[error("Failed to initialize DirectX")]
55 InitializeDirectX(#[source] windows::core::Error),
56 #[error("Failed to start job")]
58 FailedToStartJob(#[source] windows::core::Error),
59 #[error("Stream not allocated")]
61 StreamNotAllocated,
62 #[error("Failed to close print control")]
64 FailedToClosePrintControl(#[source] windows::core::Error),
65}
66
67const DEFAULT_DPI: i16 = 300;
68
69#[allow(missing_docs)]
71pub struct DxgiPrintContext {
72 pub d3d_device: ID3D11Device,
73 pub d3d_context: ID3D11DeviceContext,
74 pub dxgi_device: IDXGIDevice,
75 pub d2d_factory: ID2D1Factory1,
76 pub d2d_device: ID2D1Device,
77 pub d2d_context: ID2D1DeviceContext,
78 pub wic_factory: IWICImagingFactory2,
79 pub document_target_factory: IPrintDocumentPackageTargetFactory,
80 pub document_target: IPrintDocumentPackageTarget,
81 pub print_control: ID2D1PrintControl,
82 pub completion_waiter: PrintCompletionWaiter,
83 _com_initializer: ComInitializer,
85}
86
87impl DxgiPrintContext {
88 pub fn new(
90 device: &PrinterDevice,
91 options: &PrintTicket,
92 job_name: &OsStr,
93 ) -> Result<Self, DxgiPrintContextError> {
94 let _com_initializer = ComInitializer::new();
96 unsafe {
97 let dev_mode = options
98 .to_dev_mode(device)
99 .map_err(DxgiPrintContextError::PrintTicketError)?;
100 let raster_dpi = {
101 let dev_mode = &*(dev_mode.as_ptr() as *const DEVMODEW);
102 let dpi_x = if (dev_mode.dmFields & windows::Win32::Graphics::Gdi::DM_PRINTQUALITY)
103 .0
104 != 0
105 {
106 Some(dev_mode.Anonymous1.Anonymous1.dmPrintQuality)
107 } else {
108 None
109 };
110 let dpi_y =
111 if (dev_mode.dmFields & windows::Win32::Graphics::Gdi::DM_YRESOLUTION).0 != 0 {
112 Some(dev_mode.dmYResolution)
113 } else {
114 None
115 };
116 match (dpi_x, dpi_y) {
117 (Some(x), Some(y)) => max(x, y),
118 (Some(x), None) => x,
119 (None, Some(y)) => y,
120 (None, None) => DEFAULT_DPI,
121 }
122 };
123 let mut d3d_device: Option<ID3D11Device> = None;
124 let mut d3d_context: Option<ID3D11DeviceContext> = None;
125 D3D11CreateDevice(
126 None,
127 D3D_DRIVER_TYPE_HARDWARE,
128 HMODULE::default(),
129 D3D11_CREATE_DEVICE_BGRA_SUPPORT,
130 None,
131 D3D11_SDK_VERSION,
132 Some(ptr::addr_of_mut!(d3d_device)),
133 None,
134 Some(ptr::addr_of_mut!(d3d_context)),
135 )
136 .map_err(DxgiPrintContextError::InitializeDirectX)?;
137 let d3d_device = d3d_device.ok_or(DxgiPrintContextError::InitializeDirectX(
138 windows::core::Error::from_hresult(E_UNEXPECTED),
139 ))?;
140 let d3d_context = d3d_context.ok_or(DxgiPrintContextError::InitializeDirectX(
141 windows::core::Error::from_hresult(E_UNEXPECTED),
142 ))?;
143 let dxgi_device = d3d_device
144 .cast::<IDXGIDevice>()
145 .map_err(DxgiPrintContextError::InitializeDirectX)?;
146
147 let d2d_factory_options = D2D1_FACTORY_OPTIONS::default();
148 let d2d_factory: ID2D1Factory1 = D2D1CreateFactory(
149 D2D1_FACTORY_TYPE_SINGLE_THREADED,
150 Some(&d2d_factory_options),
151 )
152 .map_err(DxgiPrintContextError::InitializeDirectX)?;
153
154 let d2d_device = d2d_factory
155 .CreateDevice(&dxgi_device)
156 .map_err(DxgiPrintContextError::InitializeDirectX)?;
157 let d2d_context = d2d_device
158 .CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE)
159 .map_err(DxgiPrintContextError::InitializeDirectX)?;
160
161 let wic_factory: IWICImagingFactory2 =
162 CoCreateInstance(&CLSID_WICImagingFactory2, None, CLSCTX_ALL)
163 .map_err(DxgiPrintContextError::InitializeDirectX)?;
164
165 let document_target_factory: IPrintDocumentPackageTargetFactory =
166 CoCreateInstance(&PrintDocumentPackageTargetFactory, None, CLSCTX_ALL)
167 .map_err(DxgiPrintContextError::FailedToStartJob)?;
168 let print_ticket_stream = SHCreateMemStream(Some(options.get_xml()))
169 .ok_or(DxgiPrintContextError::StreamNotAllocated)?;
170 let document_target = document_target_factory
171 .CreateDocumentPackageTargetForPrintJob(
172 PCWSTR(wchar::to_wide_chars(device.os_name()).as_ptr()),
173 PCWSTR(wchar::to_wide_chars(job_name).as_ptr()),
174 None,
175 &print_ticket_stream,
176 )
177 .map_err(DxgiPrintContextError::FailedToStartJob)?;
178 let print_control_options = D2D1_PRINT_CONTROL_PROPERTIES {
179 rasterDPI: raster_dpi as f32,
180 ..Default::default()
181 };
182 let print_control = d2d_device
183 .CreatePrintControl(
184 &wic_factory,
185 &document_target,
186 Some(ptr::addr_of!(print_control_options)),
187 )
188 .map_err(DxgiPrintContextError::FailedToStartJob)?;
189
190 let completion_source =
191 PrintCompletionSource::new().map_err(DxgiPrintContextError::FailedToCreateEvent)?;
192 let completion_waiter = completion_source.waiter();
193 let completion_source: IPrintDocumentPackageStatusEvent = completion_source.into();
194 let cpc: IConnectionPointContainer = document_target
195 .cast()
196 .map_err(DxgiPrintContextError::FailedToCreateEvent)?;
197 cpc.FindConnectionPoint(&IPrintDocumentPackageStatusEvent::IID)
198 .map_err(DxgiPrintContextError::FailedToCreateEvent)?
199 .Advise(&completion_source)
200 .map_err(DxgiPrintContextError::FailedToCreateEvent)?;
201
202 Ok(Self {
203 d3d_device,
204 d3d_context,
205 dxgi_device,
206 d2d_factory,
207 d2d_device,
208 d2d_context,
209 wic_factory,
210 document_target_factory,
211 document_target,
212 print_control,
213 completion_waiter,
214 _com_initializer,
215 })
216 }
217 }
218
219 pub fn close_and_wait(self) -> Result<(), DxgiPrintContextError> {
221 unsafe {
222 self.print_control
223 .Close()
224 .map(|_| ())
225 .map_err(DxgiPrintContextError::FailedToClosePrintControl)?;
226 }
227 self.completion_waiter.wait();
228 Ok(())
229 }
230}