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