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