1 /* Copyright (C) 2001-2019 Artifex Software, Inc.
2    All Rights Reserved.
3 
4    This software is provided AS-IS with no warranty, either express or
5    implied.
6 
7    This software is distributed under license and may not be copied,
8    modified or distributed except as expressly authorized under the terms
9    of the license contained in the file LICENSE in this distribution.
10 
11    Refer to licensing information at http://www.artifex.com or contact
12    Artifex Software, Inc.,  1305 Grant Avenue - Suite 200, Novato,
13    CA 94945, U.S.A., +1(415)492-9861, for further information.
14 */
15 
16 #include <stdio.h>
17 #include <windows.h>
18 #include <XpsObjectModel.h>
19 #include <XpsPrint.h>
20 #include <prntvpt.h>
21 
22 #include "string_.h"
23 #include "gx.h"
24 #include "gserrors.h"
25 
26 #undef eprintf1
27 #define eprintf1(str, arg) {}
28 #undef eprintf
29 #define eprintf(str) {}
30 #undef gs_note_error
31 #define gs_note_error(error) return(error)
32 
33 typedef HRESULT (CALLBACK* StartXpsPrintJobType)(
34     /* [string][in] */ __RPC__in_string LPCWSTR printerName,
35     /* [string][in] */ __RPC__in_string LPCWSTR jobName,
36     /* [string][in] */ __RPC__in_string LPCWSTR outputFileName,
37     /* [in] */ __RPC__in HANDLE progressEvent,
38     /* [in] */ __RPC__in HANDLE completionEvent,
39     /* [size_is][in] */ __RPC__in_ecount_full(printablePagesOnCount) UINT8 *printablePagesOn,
40     /* [in] */ UINT32 printablePagesOnCount,
41     /* [out] */ __RPC__deref_out_opt IXpsPrintJob **xpsPrintJob,
42     /* [out] */ __RPC__deref_out_opt IXpsPrintJobStream **documentStream,
43     /* [out] */ __RPC__deref_out_opt IXpsPrintJobStream **printTicketStream
44 );
45 
gp_xpsprint(char * filename,char * printername,int * result)46 extern "C" int gp_xpsprint(char *filename, char *printername, int *result)
47 {
48     HRESULT hr = S_OK;
49     HANDLE completionEvent = NULL;
50     IXpsPrintJob* job = NULL;
51     IXpsPrintJobStream* jobStream = NULL;
52     IXpsOMObjectFactory* xpsFactory = NULL;
53     IOpcPartUri* partUri = NULL;
54     IXpsOMPackage* package = NULL;
55     IXpsOMPackageWriter* packageWriter = NULL;
56     IXpsOMPage* xpsPage = NULL;
57     IXpsOMFontResource* fontResource = NULL;
58     XPS_JOB_STATUS jobStatus = {};
59     HPTPROVIDER pProvider;
60     HGLOBAL hGlobal = NULL;
61     IStream *pCapabilities;
62     BSTR *pbstrErrorMessage = NULL;
63     int code = 0;
64     StartXpsPrintJobType StartXpsPrintJobPtr = NULL;
65 
66     HINSTANCE dllHandle = NULL;
67 
68     /*
69       Loading the DLL at run-time makes life easier with the build
70       system (no need to link in the ".lib" file), and allows us to
71       use the same executable on old Windows systems (XP and earlier)
72       without XpsPrint.dll, and those with it (Vista and later).
73      */
74     if (!(dllHandle = LoadLibrary(TEXT("XpsPrint.dll"))))
75     {
76         *result = 0x80029C4A;
77         return -15;
78     }
79 
80     StartXpsPrintJobPtr = (StartXpsPrintJobType)GetProcAddress(dllHandle, "StartXpsPrintJob");
81     if (!StartXpsPrintJobPtr)
82     {
83         *result = 0x80029C4A;
84         return -16;
85     }
86 
87     if (FAILED(hr = CoInitializeEx(0, COINIT_MULTITHREADED)))
88     {
89         *result = hr;
90         code = -1;
91     }
92 
93     if (SUCCEEDED(hr))
94     {
95         completionEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
96         if (!completionEvent)
97         {
98             *result = hr;
99             code = -2;
100         }
101     }
102 
103     /* Need code for PrintTIcket here */
104 
105     if (SUCCEEDED(hr))
106     {
107         WCHAR MBStr[64];
108 
109         code = MultiByteToWideChar(CP_ACP, 0, printername, -1, MBStr, 64);
110         if (code != 0) {
111             if (FAILED(hr = StartXpsPrintJobPtr(
112                         (LPCWSTR)MBStr,
113                         NULL,
114                         NULL,
115                         NULL,
116                         completionEvent,
117                         NULL,
118                         0,
119                         &job,
120                         &jobStream,
121                         NULL
122                         )))
123             {
124                 *result = hr;
125                 code = -4;
126             }
127         } else {
128             *result = 0;
129             code = -3;
130             hr = -1;
131         }
132     }
133 
134     if (SUCCEEDED(hr))
135     {
136         if (FAILED(hr = CoCreateInstance(
137                     __uuidof(XpsOMObjectFactory),
138                     NULL,
139                     CLSCTX_INPROC_SERVER,
140                     __uuidof(IXpsOMObjectFactory),
141                     reinterpret_cast<void**>(&xpsFactory)
142                     )
143                 )
144             )
145         {
146             *result = hr;
147             code = -5;
148         }
149     }
150 
151     if (SUCCEEDED(hr))
152     {
153         WCHAR MBStr[MAX_PATH];
154 
155         code = MultiByteToWideChar(CP_ACP, 0, filename, -1, MBStr, MAX_PATH);
156         if (code != 0) {
157             if (FAILED(hr = xpsFactory->CreatePackageFromFile((LPCWSTR)MBStr, false, &package))){
158                 *result = hr;
159                 code = -7;
160             }
161         } else {
162             hr = -1;
163             *result = 0;
164             code = -6;
165         }
166     }
167     if (SUCCEEDED(hr))
168     {
169         if (FAILED(hr = package->WriteToStream(jobStream, FALSE))) {
170             *result = hr;
171             code = -8;
172         }
173     }
174 
175     if (SUCCEEDED(hr))
176     {
177         if (FAILED(hr = jobStream->Close()))
178         {
179             *result = hr;
180             code = -9;
181         }
182     }
183     else
184     {
185         // Only cancel the job if we succeeded in creating one in the first place.
186         if (job)
187         {
188             // Tell the XPS Print API that we're giving up.  Don't overwrite hr with the return from
189             // this function.
190             job->Cancel();
191         }
192     }
193 
194 
195     if (SUCCEEDED(hr))
196     {
197         if (WaitForSingleObject(completionEvent, INFINITE) != WAIT_OBJECT_0)
198         {
199             *result = hr = HRESULT_FROM_WIN32(GetLastError());
200             code = -10;
201         }
202     }
203 
204     if (SUCCEEDED(hr))
205     {
206         if (FAILED(hr = job->GetJobStatus(&jobStatus)))
207         {
208             *result = hr;
209             code = -11;
210         }
211     }
212 
213     if (SUCCEEDED(hr))
214     {
215         switch (jobStatus.completion)
216         {
217             case XPS_JOB_COMPLETED:
218                 break;
219             case XPS_JOB_CANCELLED:
220                 hr = E_FAIL;
221                 code = -12;
222                 break;
223             case XPS_JOB_FAILED:
224                 hr = E_FAIL;
225                 *result = jobStatus.jobStatus;
226                 code = -13;
227                 break;
228             default:
229                 hr = E_UNEXPECTED;
230                 code = -14;
231                 break;
232         }
233     }
234 
235     if (fontResource)
236     {
237         fontResource->Release();
238         fontResource = NULL;
239     }
240 
241     if (xpsPage)
242     {
243         xpsPage->Release();
244         xpsPage = NULL;
245     }
246 
247     if (packageWriter)
248     {
249         packageWriter->Release();
250         packageWriter = NULL;
251     }
252 
253     if (partUri)
254     {
255         partUri->Release();
256         partUri = NULL;
257     }
258 
259     if (xpsFactory)
260     {
261         xpsFactory->Release();
262         xpsFactory = NULL;
263     }
264 
265     if (jobStream)
266     {
267         jobStream->Release();
268         jobStream = NULL;
269     }
270 
271     if (job)
272     {
273         job->Release();
274         job = NULL;
275     }
276 
277     if (completionEvent)
278     {
279         CloseHandle(completionEvent);
280         completionEvent = NULL;
281     }
282 
283     (void)FreeLibrary(dllHandle);
284     CoUninitialize();
285 
286     return code;
287 }
288