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