[Printing] Fix ups and Implementations.
[reactos.git] / win32ss / printing / base / winspool / devmode.c
1 /*
2 * PROJECT: ReactOS Spooler API
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Functions giving information about DEVMODE structures
5 * COPYRIGHT: Copyright 2016-2017 Colin Finck (colin@reactos.org)
6 */
7
8 #include "precomp.h"
9
10 typedef struct _MINIMUM_SIZE_TABLE
11 {
12 DWORD dwField;
13 WORD wSize;
14 }
15 MINIMUM_SIZE_TABLE, *PMINIMUM_SIZE_TABLE;
16
17 /**
18 * Minimum required DEVMODEA structure size based on the used fields. Must be in descending order!
19 */
20 static MINIMUM_SIZE_TABLE MinimumSizeA[] = {
21 { DM_PANNINGHEIGHT, FIELD_OFFSET(DEVMODEA, dmPanningHeight) + RTL_FIELD_SIZE(DEVMODEA, dmPanningHeight) },
22 { DM_PANNINGWIDTH, FIELD_OFFSET(DEVMODEA, dmPanningWidth) + RTL_FIELD_SIZE(DEVMODEA, dmPanningWidth) },
23 { DM_DITHERTYPE, FIELD_OFFSET(DEVMODEA, dmDitherType) + RTL_FIELD_SIZE(DEVMODEA, dmDitherType) },
24 { DM_MEDIATYPE, FIELD_OFFSET(DEVMODEA, dmMediaType) + RTL_FIELD_SIZE(DEVMODEA, dmMediaType) },
25 { DM_ICMINTENT, FIELD_OFFSET(DEVMODEA, dmICMIntent) + RTL_FIELD_SIZE(DEVMODEA, dmICMIntent) },
26 { DM_ICMMETHOD, FIELD_OFFSET(DEVMODEA, dmICMMethod) + RTL_FIELD_SIZE(DEVMODEA, dmICMMethod) },
27 { DM_DISPLAYFREQUENCY, FIELD_OFFSET(DEVMODEA, dmDisplayFrequency) + RTL_FIELD_SIZE(DEVMODEA, dmDisplayFrequency) },
28 { DM_NUP, FIELD_OFFSET(DEVMODEA, dmNup) + RTL_FIELD_SIZE(DEVMODEA, dmNup) },
29 { DM_DISPLAYFLAGS, FIELD_OFFSET(DEVMODEA, dmDisplayFlags) + RTL_FIELD_SIZE(DEVMODEA, dmDisplayFlags) },
30 { DM_PELSHEIGHT, FIELD_OFFSET(DEVMODEA, dmPelsHeight) + RTL_FIELD_SIZE(DEVMODEA, dmPelsHeight) },
31 { DM_PELSWIDTH, FIELD_OFFSET(DEVMODEA, dmPelsWidth) + RTL_FIELD_SIZE(DEVMODEA, dmPelsWidth) },
32 { DM_BITSPERPEL, FIELD_OFFSET(DEVMODEA, dmBitsPerPel) + RTL_FIELD_SIZE(DEVMODEA, dmBitsPerPel) },
33 { DM_LOGPIXELS, FIELD_OFFSET(DEVMODEA, dmLogPixels) + RTL_FIELD_SIZE(DEVMODEA, dmLogPixels) },
34 { DM_FORMNAME, FIELD_OFFSET(DEVMODEA, dmFormName) + RTL_FIELD_SIZE(DEVMODEA, dmFormName) },
35 { DM_COLLATE, FIELD_OFFSET(DEVMODEA, dmCollate) + RTL_FIELD_SIZE(DEVMODEA, dmCollate) },
36 { DM_TTOPTION, FIELD_OFFSET(DEVMODEA, dmTTOption) + RTL_FIELD_SIZE(DEVMODEA, dmTTOption) },
37 { DM_YRESOLUTION, FIELD_OFFSET(DEVMODEA, dmYResolution) + RTL_FIELD_SIZE(DEVMODEA, dmYResolution) },
38 { DM_DUPLEX, FIELD_OFFSET(DEVMODEA, dmDuplex) + RTL_FIELD_SIZE(DEVMODEA, dmDuplex) },
39 { DM_COLOR, FIELD_OFFSET(DEVMODEA, dmColor) + RTL_FIELD_SIZE(DEVMODEA, dmColor) },
40 { DM_DISPLAYFIXEDOUTPUT, FIELD_OFFSET(DEVMODEA, dmDisplayFixedOutput) + RTL_FIELD_SIZE(DEVMODEA, dmDisplayFixedOutput) },
41 { DM_DISPLAYORIENTATION, FIELD_OFFSET(DEVMODEA, dmDisplayOrientation) + RTL_FIELD_SIZE(DEVMODEA, dmDisplayOrientation) },
42 { DM_POSITION, FIELD_OFFSET(DEVMODEA, dmPosition) + RTL_FIELD_SIZE(DEVMODEA, dmPosition) },
43 { DM_PRINTQUALITY, FIELD_OFFSET(DEVMODEA, dmPrintQuality) + RTL_FIELD_SIZE(DEVMODEA, dmPrintQuality) },
44 { DM_DEFAULTSOURCE, FIELD_OFFSET(DEVMODEA, dmDefaultSource) + RTL_FIELD_SIZE(DEVMODEA, dmDefaultSource) },
45 { DM_COPIES, FIELD_OFFSET(DEVMODEA, dmCopies) + RTL_FIELD_SIZE(DEVMODEA, dmCopies) },
46 { DM_SCALE, FIELD_OFFSET(DEVMODEA, dmScale) + RTL_FIELD_SIZE(DEVMODEA, dmScale) },
47 { DM_PAPERWIDTH, FIELD_OFFSET(DEVMODEA, dmPaperWidth) + RTL_FIELD_SIZE(DEVMODEA, dmPaperWidth) },
48 { DM_PAPERLENGTH, FIELD_OFFSET(DEVMODEA, dmPaperLength) + RTL_FIELD_SIZE(DEVMODEA, dmPaperLength) },
49 { DM_PAPERSIZE, FIELD_OFFSET(DEVMODEA, dmPaperSize) + RTL_FIELD_SIZE(DEVMODEA, dmPaperSize) },
50 { DM_ORIENTATION, FIELD_OFFSET(DEVMODEA, dmOrientation) + RTL_FIELD_SIZE(DEVMODEA, dmOrientation) },
51 { 0, 0 }
52 };
53
54 /**
55 * Minimum required DEVMODEW structure size based on the used fields. Must be in descending order!
56 */
57 static MINIMUM_SIZE_TABLE MinimumSizeW[] = {
58 { DM_PANNINGHEIGHT, FIELD_OFFSET(DEVMODEW, dmPanningHeight) + RTL_FIELD_SIZE(DEVMODEW, dmPanningHeight) },
59 { DM_PANNINGWIDTH, FIELD_OFFSET(DEVMODEW, dmPanningWidth) + RTL_FIELD_SIZE(DEVMODEW, dmPanningWidth) },
60 { DM_DITHERTYPE, FIELD_OFFSET(DEVMODEW, dmDitherType) + RTL_FIELD_SIZE(DEVMODEW, dmDitherType) },
61 { DM_MEDIATYPE, FIELD_OFFSET(DEVMODEW, dmMediaType) + RTL_FIELD_SIZE(DEVMODEW, dmMediaType) },
62 { DM_ICMINTENT, FIELD_OFFSET(DEVMODEW, dmICMIntent) + RTL_FIELD_SIZE(DEVMODEW, dmICMIntent) },
63 { DM_ICMMETHOD, FIELD_OFFSET(DEVMODEW, dmICMMethod) + RTL_FIELD_SIZE(DEVMODEW, dmICMMethod) },
64 { DM_DISPLAYFREQUENCY, FIELD_OFFSET(DEVMODEW, dmDisplayFrequency) + RTL_FIELD_SIZE(DEVMODEW, dmDisplayFrequency) },
65 { DM_NUP, FIELD_OFFSET(DEVMODEW, dmNup) + RTL_FIELD_SIZE(DEVMODEW, dmNup) },
66 { DM_DISPLAYFLAGS, FIELD_OFFSET(DEVMODEW, dmDisplayFlags) + RTL_FIELD_SIZE(DEVMODEW, dmDisplayFlags) },
67 { DM_PELSHEIGHT, FIELD_OFFSET(DEVMODEW, dmPelsHeight) + RTL_FIELD_SIZE(DEVMODEW, dmPelsHeight) },
68 { DM_PELSWIDTH, FIELD_OFFSET(DEVMODEW, dmPelsWidth) + RTL_FIELD_SIZE(DEVMODEW, dmPelsWidth) },
69 { DM_BITSPERPEL, FIELD_OFFSET(DEVMODEW, dmBitsPerPel) + RTL_FIELD_SIZE(DEVMODEW, dmBitsPerPel) },
70 { DM_LOGPIXELS, FIELD_OFFSET(DEVMODEW, dmLogPixels) + RTL_FIELD_SIZE(DEVMODEW, dmLogPixels) },
71 { DM_FORMNAME, FIELD_OFFSET(DEVMODEW, dmFormName) + RTL_FIELD_SIZE(DEVMODEW, dmFormName) },
72 { DM_COLLATE, FIELD_OFFSET(DEVMODEW, dmCollate) + RTL_FIELD_SIZE(DEVMODEW, dmCollate) },
73 { DM_TTOPTION, FIELD_OFFSET(DEVMODEW, dmTTOption) + RTL_FIELD_SIZE(DEVMODEW, dmTTOption) },
74 { DM_YRESOLUTION, FIELD_OFFSET(DEVMODEW, dmYResolution) + RTL_FIELD_SIZE(DEVMODEW, dmYResolution) },
75 { DM_DUPLEX, FIELD_OFFSET(DEVMODEW, dmDuplex) + RTL_FIELD_SIZE(DEVMODEW, dmDuplex) },
76 { DM_COLOR, FIELD_OFFSET(DEVMODEW, dmColor) + RTL_FIELD_SIZE(DEVMODEW, dmColor) },
77 { DM_DISPLAYFIXEDOUTPUT, FIELD_OFFSET(DEVMODEW, dmDisplayFixedOutput) + RTL_FIELD_SIZE(DEVMODEW, dmDisplayFixedOutput) },
78 { DM_DISPLAYORIENTATION, FIELD_OFFSET(DEVMODEW, dmDisplayOrientation) + RTL_FIELD_SIZE(DEVMODEW, dmDisplayOrientation) },
79 { DM_POSITION, FIELD_OFFSET(DEVMODEW, dmPosition) + RTL_FIELD_SIZE(DEVMODEW, dmPosition) },
80 { DM_PRINTQUALITY, FIELD_OFFSET(DEVMODEW, dmPrintQuality) + RTL_FIELD_SIZE(DEVMODEW, dmPrintQuality) },
81 { DM_DEFAULTSOURCE, FIELD_OFFSET(DEVMODEW, dmDefaultSource) + RTL_FIELD_SIZE(DEVMODEW, dmDefaultSource) },
82 { DM_COPIES, FIELD_OFFSET(DEVMODEW, dmCopies) + RTL_FIELD_SIZE(DEVMODEW, dmCopies) },
83 { DM_SCALE, FIELD_OFFSET(DEVMODEW, dmScale) + RTL_FIELD_SIZE(DEVMODEW, dmScale) },
84 { DM_PAPERWIDTH, FIELD_OFFSET(DEVMODEW, dmPaperWidth) + RTL_FIELD_SIZE(DEVMODEW, dmPaperWidth) },
85 { DM_PAPERLENGTH, FIELD_OFFSET(DEVMODEW, dmPaperLength) + RTL_FIELD_SIZE(DEVMODEW, dmPaperLength) },
86 { DM_PAPERSIZE, FIELD_OFFSET(DEVMODEW, dmPaperSize) + RTL_FIELD_SIZE(DEVMODEW, dmPaperSize) },
87 { DM_ORIENTATION, FIELD_OFFSET(DEVMODEW, dmOrientation) + RTL_FIELD_SIZE(DEVMODEW, dmOrientation) },
88 { 0, 0 }
89 };
90
91 /**
92 * Replace the last character by a null terminator if the given ANSI string is not null-terminated.
93 */
94 static __inline void
95 _FixStringA(PBYTE String, DWORD cbString)
96 {
97 const PBYTE pLastCharacter = &String[cbString / sizeof(BYTE) - 1];
98 PBYTE p = String;
99
100 while (*p)
101 {
102 if (p == pLastCharacter)
103 {
104 *p = 0;
105 break;
106 }
107
108 p++;
109 }
110 }
111
112 /**
113 * Replace the last character by a null terminator if the given Unicode string is not null-terminated.
114 */
115 static __inline void
116 _FixStringW(PWSTR String, DWORD cbString)
117 {
118 const PWSTR pLastCharacter = &String[cbString / sizeof(WCHAR) - 1];
119 PWSTR p = String;
120
121 while (*p)
122 {
123 if (p == pLastCharacter)
124 {
125 *p = 0;
126 break;
127 }
128
129 p++;
130 }
131 }
132
133 BOOL WINAPI
134 IsValidDevmodeA(PDEVMODEA pDevmode, size_t DevmodeSize)
135 {
136 PMINIMUM_SIZE_TABLE pTable = MinimumSizeA;
137 WORD wRequiredSize;
138
139 TRACE("IsValidDevmodeA(%p, %lu)\n", pDevmode, DevmodeSize);
140
141 // Check if a Devmode was given at all.
142 if (!pDevmode)
143 goto Failure;
144
145 // Verify that DevmodeSize is large enough to hold the public and private members of the structure.
146 if (DevmodeSize < pDevmode->dmSize + pDevmode->dmDriverExtra)
147 goto Failure;
148
149 // If the structure has private members, the public structure must be 32-bit packed.
150 if (pDevmode->dmDriverExtra && pDevmode->dmSize % 4)
151 goto Failure;
152
153 // Now determine the minimum possible dmSize based on the given fields in dmFields.
154 wRequiredSize = FIELD_OFFSET(DEVMODEA, dmFields) + RTL_FIELD_SIZE(DEVMODEA, dmFields);
155
156 while (pTable->dwField)
157 {
158 if (pDevmode->dmFields & pTable->dwField)
159 {
160 wRequiredSize = pTable->wSize;
161 break;
162 }
163
164 pTable++;
165 }
166
167 // Verify that the value in dmSize is big enough for the used fields.
168 if (pDevmode->dmSize < wRequiredSize)
169 goto Failure;
170
171 // Check if dmDeviceName and (if used) dmFormName are null-terminated.
172 // Fix this if they aren't.
173 _FixStringA(pDevmode->dmDeviceName, sizeof(pDevmode->dmDeviceName));
174 if (pDevmode->dmFields & DM_FORMNAME)
175 _FixStringA(pDevmode->dmFormName, sizeof(pDevmode->dmFormName));
176
177 // Return success without setting the error code.
178 return TRUE;
179
180 Failure:
181 SetLastError(ERROR_INVALID_DATA);
182 return FALSE;
183 }
184
185 BOOL WINAPI
186 IsValidDevmodeW(PDEVMODEW pDevmode, size_t DevmodeSize)
187 {
188 PMINIMUM_SIZE_TABLE pTable = MinimumSizeW;
189 WORD wRequiredSize;
190
191 TRACE("IsValidDevmodeW(%p, %lu)\n", pDevmode, DevmodeSize);
192
193 // Check if a Devmode was given at all.
194 if (!pDevmode)
195 goto Failure;
196
197 // Verify that DevmodeSize is large enough to hold the public and private members of the structure.
198 if (DevmodeSize < pDevmode->dmSize + pDevmode->dmDriverExtra)
199 goto Failure;
200
201 // If the structure has private members, the public structure must be 32-bit packed.
202 if (pDevmode->dmDriverExtra && pDevmode->dmSize % 4)
203 goto Failure;
204
205 // Now determine the minimum possible dmSize based on the given fields in dmFields.
206 wRequiredSize = FIELD_OFFSET(DEVMODEW, dmFields) + RTL_FIELD_SIZE(DEVMODEW, dmFields);
207
208 while (pTable->dwField)
209 {
210 if (pDevmode->dmFields & pTable->dwField)
211 {
212 wRequiredSize = pTable->wSize;
213 break;
214 }
215
216 pTable++;
217 }
218
219 // Verify that the value in dmSize is big enough for the used fields.
220 if (pDevmode->dmSize < wRequiredSize)
221 goto Failure;
222
223 // Check if dmDeviceName and (if used) dmFormName are null-terminated.
224 // Fix this if they aren't.
225 _FixStringW(pDevmode->dmDeviceName, sizeof(pDevmode->dmDeviceName));
226 if (pDevmode->dmFields & DM_FORMNAME)
227 _FixStringW(pDevmode->dmFormName, sizeof(pDevmode->dmFormName));
228
229 // Return success without setting the error code.
230 return TRUE;
231
232 Failure:
233 SetLastError(ERROR_INVALID_DATA);
234 return FALSE;
235 }
236
237 BOOL WINAPI
238 IsValidDevmodeNoSizeW(PDEVMODEW pDevmode)
239 {
240 PMINIMUM_SIZE_TABLE pTable = MinimumSizeW;
241 WORD wRequiredSize;
242
243 TRACE("IsValidDevmodeNoSizeW(%p)\n", pDevmode);
244
245 // Check if a Devmode was given at all.
246 if (!pDevmode)
247 goto Failure;
248
249 // If the structure has private members, the public structure must be 32-bit packed.
250 if (pDevmode->dmDriverExtra && pDevmode->dmSize % 4)
251 goto Failure;
252
253 // Now determine the minimum possible dmSize based on the given fields in dmFields.
254 wRequiredSize = FIELD_OFFSET(DEVMODEW, dmFields) + RTL_FIELD_SIZE(DEVMODEW, dmFields);
255
256 while (pTable->dwField)
257 {
258 if (pDevmode->dmFields & pTable->dwField)
259 {
260 wRequiredSize = pTable->wSize;
261 break;
262 }
263
264 pTable++;
265 }
266
267 // Verify that the value in dmSize is big enough for the used fields.
268 if (pDevmode->dmSize < wRequiredSize)
269 goto Failure;
270
271 // Check if dmDeviceName and (if used) dmFormName are null-terminated.
272 // Fix this if they aren't.
273 _FixStringW(pDevmode->dmDeviceName, sizeof(pDevmode->dmDeviceName));
274 if (pDevmode->dmFields & DM_FORMNAME)
275 _FixStringW(pDevmode->dmFormName, sizeof(pDevmode->dmFormName));
276
277 // Return success without setting the error code.
278 return TRUE;
279
280 Failure:
281 SetLastError(ERROR_INVALID_DATA);
282 return FALSE;
283 }
284
285 void RosConvertAnsiDevModeToUnicodeDevmode(PDEVMODEA pDevModeInput, PDEVMODEW *pDevModeOutput)
286 {
287 // FIXME: This function should become ConvertAnsiDevModeToUnicodeDevmode when its parameters are known!
288
289 // Check if a pDevModeInput and pDevModeOutput are both not NULL.
290 if (!pDevModeInput || !pDevModeOutput)
291 return;
292
293 *pDevModeOutput = GdiConvertToDevmodeW(pDevModeInput);
294 }
295
296 // Internal counterpart to GdiConvertToDevmodeW from gdi32
297 static __inline DEVMODEA*
298 _ConvertToDevmodeA(const DEVMODEW *dmW)
299 {
300 DEVMODEA *dmA;
301 WORD dmA_size, dmW_size;
302 size_t BytesToCopy;
303
304 dmW_size = dmW->dmSize;
305
306 /* this is the minimal dmSize that XP accepts */
307 if (dmW_size < FIELD_OFFSET(DEVMODEW, dmFields))
308 return NULL;
309
310 // Guard against callers that set dmSize incorrectly.
311 if (dmW_size > sizeof(DEVMODEW))
312 dmW_size = sizeof(DEVMODEW);
313
314 // dmA_size must become dmW_size without the additional 1 byte per character for each Unicode string (dmDeviceName and dmFormName).
315 dmA_size = dmW_size - CCHDEVICENAME;
316 if (dmW_size >= FIELD_OFFSET(DEVMODEW, dmFormName) + CCHFORMNAME * sizeof(WCHAR))
317 dmA_size -= CCHFORMNAME;
318
319 // Allocate the required bytes, that is dmSize for the ANSI DEVMODEA structure plus any extra bytes requested through dmDriverExtra.
320 dmA = HeapAlloc(GetProcessHeap(), 0, dmA_size + dmW->dmDriverExtra);
321 if (!dmA) return NULL;
322
323 // Every valid DEVMODEW has a dmDeviceName, which we convert to ANSI here.
324 WideCharToMultiByte(CP_ACP, 0, dmW->dmDeviceName, -1, (LPSTR)dmA->dmDeviceName, CCHDEVICENAME, NULL, NULL);
325
326 // Copy everything up to dmFormName or the remaining dmW_size, whatever is smaller.
327 BytesToCopy = min(FIELD_OFFSET(DEVMODEW, dmFormName) - FIELD_OFFSET(DEVMODEW, dmSpecVersion), dmW_size - CCHDEVICENAME * sizeof(WCHAR));
328 memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion, BytesToCopy);
329
330 // Handle dmFormName if the input DEVMODEW is large enough to contain one.
331 if (dmW_size >= FIELD_OFFSET(DEVMODEW, dmFormName) + CCHFORMNAME * sizeof(WCHAR))
332 {
333 if (dmW->dmFields & DM_FORMNAME)
334 WideCharToMultiByte(CP_ACP, 0, dmW->dmFormName, -1, (LPSTR)dmA->dmFormName, CCHFORMNAME, NULL, NULL);
335 else
336 dmA->dmFormName[0] = 0;
337
338 // Copy the remaining fields.
339 if (dmW_size > FIELD_OFFSET(DEVMODEW, dmLogPixels))
340 memcpy(&dmA->dmLogPixels, &dmW->dmLogPixels, dmW_size - FIELD_OFFSET(DEVMODEW, dmLogPixels));
341 }
342
343 // Append dmDriverExtra if required.
344 if (dmW->dmDriverExtra)
345 memcpy((char *)dmA + dmA_size, (const char *)dmW + dmW_size, dmW->dmDriverExtra);
346
347 // Set the corrected dmSize and we are done.
348 dmA->dmSize = dmA_size;
349
350 return dmA;
351 }
352
353 void RosConvertUnicodeDevModeToAnsiDevmode(PDEVMODEW pDevModeInput, PDEVMODEA pDevModeOutput)
354 {
355 PDEVMODEA pTmp;
356
357 // FIXME: This function should become ConvertUnicodeDevModeToAnsiDevmode when its parameters are known!
358
359 // Check if a pDevModeInput and pDevModeOutput are both not NULL.
360 if (!pDevModeInput || !pDevModeOutput)
361 return;
362
363 pTmp = _ConvertToDevmodeA(pDevModeInput);
364 memcpy( pDevModeOutput, pTmp, pTmp->dmSize + pTmp->dmDriverExtra); // Copy into a Wide char (Larger) buffer.
365 HeapFree(hProcessHeap, 0, pTmp);
366 }