wineps.drv: Assign to structs instead of using memcpy.
[wine] / dlls / wineps.drv / escape.c
1 /*
2  *      PostScript driver Escape function
3  *
4  *      Copyright 1998  Huw D M Davies
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 #include <stdarg.h>
21
22 #include "windef.h"
23 #include "winbase.h"
24 #include "wingdi.h"
25 #include "wine/wingdi16.h"
26 #include "wownt32.h"
27 #include "psdrv.h"
28 #include "wine/debug.h"
29 #include "winspool.h"
30
31 WINE_DEFAULT_DEBUG_CHANNEL(psdrv);
32
33 static const char psbegindocument[] =
34 "%%BeginDocument: Wine passthrough\n";
35
36 /**********************************************************************
37  *           ExtEscape  (WINEPS.@)
38  */
39 INT PSDRV_ExtEscape( PSDRV_PDEVICE *physDev, INT nEscape, INT cbInput, LPCVOID in_data,
40                      INT cbOutput, LPVOID out_data )
41 {
42     switch(nEscape)
43     {
44     case QUERYESCSUPPORT:
45         if(cbInput < sizeof(INT))
46         {
47             WARN("cbInput < sizeof(INT) (=%d) for QUERYESCSUPPORT\n", cbInput);
48             return 0;
49         } else {
50             UINT num = *(const UINT *)in_data;
51             TRACE("QUERYESCSUPPORT for %d\n", num);
52
53             switch(num) {
54             case NEXTBAND:
55             /*case BANDINFO:*/
56             case SETCOPYCOUNT:
57             case GETTECHNOLOGY:
58             case SETLINECAP:
59             case SETLINEJOIN:
60             case SETMITERLIMIT:
61             case SETCHARSET:
62             case EXT_DEVICE_CAPS:
63             case SET_BOUNDS:
64             case EPSPRINTING:
65             case POSTSCRIPT_DATA:
66             case PASSTHROUGH:
67             case POSTSCRIPT_PASSTHROUGH:
68             case POSTSCRIPT_IGNORE:
69             case BEGIN_PATH:
70             case CLIP_TO_PATH:
71             case END_PATH:
72             /*case DRAWPATTERNRECT:*/
73                 return TRUE;
74
75             default:
76                 FIXME("QUERYESCSUPPORT(%d) - not supported.\n", num);
77                 return FALSE;
78             }
79         }
80
81     case MFCOMMENT:
82     {
83         FIXME("MFCOMMENT(%p, %d)\n", in_data, cbInput);
84         return 1;
85     }
86     case DRAWPATTERNRECT:
87     {
88         DRAWPATRECT     *dpr = (DRAWPATRECT*)in_data;
89
90         FIXME("DRAWPATTERNRECT(pos (%d,%d), size %dx%d, style %d, pattern %x), stub!\n",
91                 dpr->ptPosition.x, dpr->ptPosition.y,
92                 dpr->ptSize.x, dpr->ptSize.y,
93                 dpr->wStyle, dpr->wPattern
94         );
95         return 1;
96     }
97     case BANDINFO:
98     {
99         BANDINFOSTRUCT  *ibi = (BANDINFOSTRUCT*)in_data;
100         BANDINFOSTRUCT  *obi = (BANDINFOSTRUCT*)out_data;
101
102         FIXME("BANDINFO(graphics %d, text %d, rect [%dx%d-%dx%d]), stub!\n",
103                 ibi->GraphicsFlag,
104                 ibi->TextFlag,
105                 ibi->GraphicsRect.top,
106                 ibi->GraphicsRect.bottom,
107                 ibi->GraphicsRect.left,
108                 ibi->GraphicsRect.right
109         );
110         *obi = *ibi;
111         return 1;
112     }
113     case NEXTBAND:
114     {
115         RECT *r = out_data;
116         if(!physDev->job.banding) {
117             physDev->job.banding = TRUE;
118             r->left   = 0;
119             r->top    = 0;
120             r->right  = physDev->horzRes;
121             r->bottom = physDev->vertRes;
122             TRACE("NEXTBAND returning %d,%d - %d,%d\n", r->left, r->top, r->right, r->bottom );
123             return 1;
124         }
125         r->left   = 0;
126         r->top    = 0;
127         r->right  = 0;
128         r->bottom = 0;
129         TRACE("NEXTBAND rect to 0,0 - 0,0\n" );
130         physDev->job.banding = FALSE;
131         return EndPage( physDev->hdc );
132     }
133
134     case SETCOPYCOUNT:
135         {
136             const INT *NumCopies = in_data;
137             INT *ActualCopies = out_data;
138             if(cbInput != sizeof(INT)) {
139                 WARN("cbInput != sizeof(INT) (=%d) for SETCOPYCOUNT\n", cbInput);
140                 return 0;
141             }
142             TRACE("SETCOPYCOUNT %d\n", *NumCopies);
143             *ActualCopies = 1;
144             return 1;
145         }
146
147     case GETTECHNOLOGY:
148         {
149             LPSTR p = out_data;
150             strcpy(p, "PostScript");
151             *(p + strlen(p) + 1) = '\0'; /* 2 '\0's at end of string */
152             return 1;
153         }
154
155     case SETLINECAP:
156         {
157             INT newCap = *(const INT *)in_data;
158             if(cbInput != sizeof(INT)) {
159                 WARN("cbInput != sizeof(INT) (=%d) for SETLINECAP\n", cbInput);
160                 return 0;
161             }
162             TRACE("SETLINECAP %d\n", newCap);
163             return 0;
164         }
165
166     case SETLINEJOIN:
167         {
168             INT newJoin = *(const INT *)in_data;
169             if(cbInput != sizeof(INT)) {
170                 WARN("cbInput != sizeof(INT) (=%d) for SETLINEJOIN\n", cbInput);
171                 return 0;
172             }
173             TRACE("SETLINEJOIN %d\n", newJoin);
174             return 0;
175         }
176
177     case SETMITERLIMIT:
178         {
179             INT newLimit = *(const INT *)in_data;
180             if(cbInput != sizeof(INT)) {
181                 WARN("cbInput != sizeof(INT) (=%d) for SETMITERLIMIT\n", cbInput);
182                 return 0;
183             }
184             TRACE("SETMITERLIMIT %d\n", newLimit);
185             return 0;
186         }
187
188     case SETCHARSET:
189       /* Undocumented escape used by winword6.
190          Switches between ANSI and a special charset.
191          If *lpInData == 1 we require that
192          0x91 is quoteleft
193          0x92 is quoteright
194          0x93 is quotedblleft
195          0x94 is quotedblright
196          0x95 is bullet
197          0x96 is endash
198          0x97 is emdash
199          0xa0 is non break space - yeah right.
200
201          If *lpInData == 0 we get ANSI.
202          Since there's nothing else there, let's just make these the default
203          anyway and see what happens...
204       */
205         return 1;
206
207     case EXT_DEVICE_CAPS:
208         {
209             UINT cap = *(const UINT *)in_data;
210             if(cbInput != sizeof(UINT)) {
211                 WARN("cbInput != sizeof(UINT) (=%d) for EXT_DEVICE_CAPS\n", cbInput);
212                 return 0;
213             }
214             TRACE("EXT_DEVICE_CAPS %d\n", cap);
215             return 0;
216         }
217
218     case SET_BOUNDS:
219         {
220             const RECT *r = in_data;
221             if(cbInput != sizeof(RECT)) {
222                 WARN("cbInput != sizeof(RECT) (=%d) for SET_BOUNDS\n", cbInput);
223                 return 0;
224             }
225             TRACE("SET_BOUNDS (%d,%d) - (%d,%d)\n", r->left, r->top,
226                   r->right, r->bottom);
227             return 0;
228         }
229
230     case EPSPRINTING:
231         {
232             UINT epsprint = *(const UINT*)in_data;
233             /* FIXME: In this mode we do not need to send page intros and page
234              * ends according to the doc. But I just ignore that detail
235              * for now.
236              */
237             TRACE("EPS Printing support %sable.\n",epsprint?"en":"dis");
238             return 1;
239         }
240
241     case POSTSCRIPT_DATA:
242     case PASSTHROUGH:
243     case POSTSCRIPT_PASSTHROUGH:
244         {
245             /* Write directly to spool file, bypassing normal PS driver
246              * processing that is done along with writing PostScript code
247              * to the spool.
248              * We have a WORD before the data counting the size, but
249              * cbInput is just this +2.
250              * However Photoshop 7 has a bug that sets cbInput to 2 less than the
251              * length of the string, rather than 2 more.  So we'll use the WORD at
252              * in_data[0] instead.
253              */
254             if(!physDev->job.in_passthrough) {
255                 WriteSpool16(physDev->job.hJob, (LPSTR)psbegindocument, sizeof(psbegindocument)-1);
256                 physDev->job.in_passthrough = TRUE;
257             }
258             return WriteSpool16(physDev->job.hJob,((char*)in_data)+2,*(const WORD*)in_data);
259         }
260
261     case POSTSCRIPT_IGNORE:
262       {
263         BOOL ret = physDev->job.quiet;
264         TRACE("POSTSCRIPT_IGNORE %d\n", *(const short*)in_data);
265         physDev->job.quiet = *(const short*)in_data;
266         return ret;
267       }
268
269     case GETSETPRINTORIENT:
270         {
271             /* If lpInData is present, it is a 20 byte structure, first 32
272              * bit LONG value is the orientation. if lpInData is NULL, it
273              * returns the current orientation.
274              */
275             FIXME("GETSETPRINTORIENT not implemented (data %p)!\n",in_data);
276             return 1;
277         }
278     case BEGIN_PATH:
279         TRACE("BEGIN_PATH\n");
280         if(physDev->pathdepth)
281             FIXME("Nested paths not yet handled\n");
282         return ++physDev->pathdepth;
283
284     case END_PATH:
285       {
286         const struct PATH_INFO *info = (const struct PATH_INFO*)in_data;
287
288         TRACE("END_PATH\n");
289         if(!physDev->pathdepth) {
290             ERR("END_PATH called without a BEIGN_PATH\n");
291             return -1;
292         }
293         TRACE("RenderMode = %d, FillMode = %d, BkMode = %d\n",
294               info->RenderMode, info->FillMode, info->BkMode);
295         switch(info->RenderMode) {
296         case RENDERMODE_NO_DISPLAY:
297             PSDRV_WriteClosePath(physDev); /* not sure if this is necessary, but it can't hurt */
298             break;
299         case RENDERMODE_OPEN:
300         case RENDERMODE_CLOSED:
301         default:
302             FIXME("END_PATH: RenderMode %d, not yet supported\n", info->RenderMode);
303             break;
304         }
305         return --physDev->pathdepth;
306       }
307
308     case CLIP_TO_PATH:
309       {
310         WORD mode = *(const WORD*)in_data;
311
312         switch(mode) {
313         case CLIP_SAVE:
314             TRACE("CLIP_TO_PATH: CLIP_SAVE\n");
315             PSDRV_WriteGSave(physDev);
316             return 1;
317         case CLIP_RESTORE:
318             TRACE("CLIP_TO_PATH: CLIP_RESTORE\n");
319             PSDRV_WriteGRestore(physDev);
320             return 1;
321         case CLIP_INCLUSIVE:
322             TRACE("CLIP_TO_PATH: CLIP_INCLUSIVE\n");
323             /* FIXME to clip or eoclip ? (see PATH_INFO.FillMode) */
324             PSDRV_WriteClip(physDev);
325             PSDRV_WriteNewPath(physDev);
326             return 1;
327         case CLIP_EXCLUSIVE:
328             FIXME("CLIP_EXCLUSIVE: not implemented\n");
329             return 0;
330         default:
331             FIXME("Unknown CLIP_TO_PATH mode %d\n", mode);
332             return 0;
333         }
334       }
335     default:
336         FIXME("Unimplemented code 0x%x\n", nEscape);
337         return 0;
338     }
339 }
340
341 /************************************************************************
342  *           PSDRV_StartPage
343  */
344 INT PSDRV_StartPage( PSDRV_PDEVICE *physDev )
345 {
346     if(!physDev->job.OutOfPage) {
347         FIXME("Already started a page?\n");
348         return 1;
349     }
350
351     if(physDev->job.PageNo++ == 0) {
352         if(!PSDRV_WriteHeader( physDev, physDev->job.DocName ))
353             return 0;
354     }
355
356     if(!PSDRV_WriteNewPage( physDev ))
357         return 0;
358     physDev->job.OutOfPage = FALSE;
359     return 1;
360 }
361
362
363 /************************************************************************
364  *           PSDRV_EndPage
365  */
366 INT PSDRV_EndPage( PSDRV_PDEVICE *physDev )
367 {
368     if(physDev->job.OutOfPage) {
369         FIXME("Already ended a page?\n");
370         return 1;
371     }
372     if(!PSDRV_WriteEndPage( physDev ))
373         return 0;
374     PSDRV_EmptyDownloadList(physDev, FALSE);
375     physDev->job.OutOfPage = TRUE;
376     return 1;
377 }
378
379
380 /************************************************************************
381  *           PSDRV_StartDocA
382  */
383 static INT PSDRV_StartDocA( PSDRV_PDEVICE *physDev, const DOCINFOA *doc )
384 {
385     LPCSTR output = "LPT1:";
386     BYTE buf[300];
387     HANDLE hprn = INVALID_HANDLE_VALUE;
388     PRINTER_INFO_5A *pi5 = (PRINTER_INFO_5A*)buf;
389     DWORD needed;
390
391     if(physDev->job.hJob) {
392         FIXME("hJob != 0. Now what?\n");
393         return 0;
394     }
395
396     if(doc->lpszOutput)
397         output = doc->lpszOutput;
398     else if(physDev->job.output)
399         output = physDev->job.output;
400     else {
401         if(OpenPrinterA(physDev->pi->FriendlyName, &hprn, NULL) &&
402            GetPrinterA(hprn, 5, buf, sizeof(buf), &needed)) {
403             output = pi5->pPortName;
404         }
405         if(hprn != INVALID_HANDLE_VALUE)
406             ClosePrinter(hprn);
407     }
408
409     physDev->job.hJob = OpenJob16(output,  doc->lpszDocName, HDC_16(physDev->hdc) );
410     if(!physDev->job.hJob) {
411         WARN("OpenJob failed\n");
412         return 0;
413     }
414     physDev->job.banding = FALSE;
415     physDev->job.OutOfPage = TRUE;
416     physDev->job.PageNo = 0;
417     physDev->job.quiet = FALSE;
418     physDev->job.in_passthrough = FALSE;
419     physDev->job.had_passthrough_rect = FALSE;
420     if(doc->lpszDocName) {
421         physDev->job.DocName = HeapAlloc(GetProcessHeap(), 0, strlen(doc->lpszDocName)+1);
422         strcpy(physDev->job.DocName, doc->lpszDocName);
423     } else
424         physDev->job.DocName = NULL;
425
426     return physDev->job.hJob;
427 }
428
429 /************************************************************************
430  *           PSDRV_StartDoc
431  */
432 INT PSDRV_StartDoc( PSDRV_PDEVICE *physDev, const DOCINFOW *doc )
433 {
434     DOCINFOA docA;
435     INT ret, len;
436     LPSTR docname = NULL, output = NULL, datatype = NULL;
437
438     docA.cbSize = doc->cbSize;
439     if (doc->lpszDocName)
440     {
441         len = WideCharToMultiByte( CP_ACP, 0, doc->lpszDocName, -1, NULL, 0, NULL, NULL );
442         if ((docname = HeapAlloc( GetProcessHeap(), 0, len )))
443             WideCharToMultiByte( CP_ACP, 0, doc->lpszDocName, -1, docname, len, NULL, NULL );
444     }
445     if (doc->lpszOutput)
446     {
447         len = WideCharToMultiByte( CP_ACP, 0, doc->lpszOutput, -1, NULL, 0, NULL, NULL );
448         if ((output = HeapAlloc( GetProcessHeap(), 0, len )))
449             WideCharToMultiByte( CP_ACP, 0, doc->lpszOutput, -1, output, len, NULL, NULL );
450     }
451     if (doc->lpszDatatype)
452     {
453         len = WideCharToMultiByte( CP_ACP, 0, doc->lpszDatatype, -1, NULL, 0, NULL, NULL );
454         if ((datatype = HeapAlloc( GetProcessHeap(), 0, len )))
455             WideCharToMultiByte( CP_ACP, 0, doc->lpszDatatype, -1, datatype, len, NULL, NULL );
456     }
457     docA.lpszDocName = docname;
458     docA.lpszOutput = output;
459     docA.lpszDatatype = datatype;
460     docA.fwType = doc->fwType;
461
462     ret = PSDRV_StartDocA(physDev, &docA);
463
464     HeapFree( GetProcessHeap(), 0, docname );
465     HeapFree( GetProcessHeap(), 0, output );
466     HeapFree( GetProcessHeap(), 0, datatype );
467
468     return ret;
469 }
470
471 /************************************************************************
472  *           PSDRV_EndDoc
473  */
474 INT PSDRV_EndDoc( PSDRV_PDEVICE *physDev )
475 {
476     INT ret = 1;
477     if(!physDev->job.hJob) {
478         FIXME("hJob == 0. Now what?\n");
479         return 0;
480     }
481
482     if(!physDev->job.OutOfPage) {
483         WARN("Somebody forgot an EndPage\n");
484         PSDRV_EndPage( physDev );
485     }
486     PSDRV_WriteFooter( physDev );
487
488     if( CloseJob16( physDev->job.hJob ) == SP_ERROR ) {
489         WARN("CloseJob error\n");
490         ret = 0;
491     }
492     physDev->job.hJob = 0;
493     HeapFree(GetProcessHeap(), 0, physDev->job.DocName);
494     physDev->job.DocName = NULL;
495
496     return ret;
497 }