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