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