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