xmllite: Fail to set input for external IXmlReaderInput.
[wine] / dlls / wineps.drv / ppd.c
1 /*      PostScript Printer Description (PPD) file parser
2  *
3  *      See  http://www.adobe.com/supportservice/devrelations/PDFS/TN/5003.PPD_Spec_v4.3.pdf
4  *
5  *      Copyright 1998  Huw D M Davies
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <string.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <ctype.h>
29 #include <locale.h>
30 #include <assert.h>
31 #include "windef.h"
32 #include "winbase.h"
33 #include "wine/debug.h"
34 #include "psdrv.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(psdrv);
37
38 typedef struct {
39 char    *key;
40 char    *option;
41 char    *opttrans;
42 char    *value;
43 char    *valtrans;
44 } PPDTuple;
45
46 struct map_context
47 {
48     const char *ptr, *pos, *end;
49 };
50
51 /* map of page names in ppd file to Windows paper constants */
52
53 static const struct {
54   const char *PSName;
55   WORD WinPage;
56 } PageTrans[] = {
57   {"10x11",                   DMPAPER_10X11},
58   {"10x14",                   DMPAPER_10X14},
59   {"11x17",                   DMPAPER_11X17},   /* not in Adobe PPD file spec */
60   {"12x11",                   DMPAPER_12X11},
61   {"15x11",                   DMPAPER_15X11},
62   {"9x11",                    DMPAPER_9X11},
63   {"A2",                      DMPAPER_A2},
64   {"A3",                      DMPAPER_A3},
65   {"A3.Transverse",           DMPAPER_A3_TRANSVERSE},
66   {"A3Extra",                 DMPAPER_A3_EXTRA},
67   {"A3Extra.Transverse",      DMPAPER_A3_EXTRA_TRANSVERSE},
68   {"A3Rotated",               DMPAPER_A3_ROTATED},
69   {"A4",                      DMPAPER_A4},
70   {"A4.Transverse",           DMPAPER_A4_TRANSVERSE},
71   {"A4Extra",                 DMPAPER_A4_EXTRA},
72   {"A4Plus",                  DMPAPER_A4_PLUS},
73   {"A4Rotated",               DMPAPER_A4_ROTATED},
74   {"A4Small",                 DMPAPER_A4SMALL},
75   {"A5",                      DMPAPER_A5},
76   {"A5.Transverse",           DMPAPER_A5_TRANSVERSE},
77   {"A5Extra",                 DMPAPER_A5_EXTRA},
78   {"A5Rotated",               DMPAPER_A5_ROTATED},
79   {"A6",                      DMPAPER_A6},
80   {"A6Rotated",               DMPAPER_A6_ROTATED},
81   {"ARCHC",                   DMPAPER_CSHEET},
82   {"ARCHD",                   DMPAPER_DSHEET},
83   {"ARCHE",                   DMPAPER_ESHEET},
84   {"B4",                      DMPAPER_B4},
85   {"B4Rotated",               DMPAPER_B4_JIS_ROTATED},
86   {"B5",                      DMPAPER_B5},
87   {"B5.Transverse",           DMPAPER_B5_TRANSVERSE},
88   {"B5Rotated",               DMPAPER_B5_JIS_ROTATED},
89   {"B6",                      DMPAPER_B6_JIS},
90   {"B6Rotated",               DMPAPER_B6_JIS_ROTATED},
91   {"C4",                      DMPAPER_ENV_C4},          /*  use EnvC4 */
92   {"C5",                      DMPAPER_ENV_C5},          /*  use EnvC5 */
93   {"C6",                      DMPAPER_ENV_C6},          /*  use EnvC6 */
94   {"Comm10",                  DMPAPER_ENV_10},          /*  use Env10 */
95   {"DL",                      DMPAPER_ENV_DL},          /*  use EnvDL */
96   {"DoublePostcard",          DMPAPER_DBL_JAPANESE_POSTCARD},
97   {"DoublePostcardRotated",   DMPAPER_DBL_JAPANESE_POSTCARD_ROTATED},
98   {"Env10",                   DMPAPER_ENV_10},
99   {"Env11",                   DMPAPER_ENV_11},
100   {"Env12",                   DMPAPER_ENV_12},
101   {"Env14",                   DMPAPER_ENV_14},
102   {"Env9",                    DMPAPER_ENV_9},
103   {"EnvC3",                   DMPAPER_ENV_C3},
104   {"EnvC4",                   DMPAPER_ENV_C4},
105   {"EnvC5",                   DMPAPER_ENV_C5},
106   {"EnvC6",                   DMPAPER_ENV_C6},
107   {"EnvC65",                  DMPAPER_ENV_C65},
108   {"EnvChou3",                DMPAPER_JENV_CHOU3},
109   {"EnvChou3Rotated",         DMPAPER_JENV_CHOU3_ROTATED},
110   {"EnvChou4",                DMPAPER_JENV_CHOU4},
111   {"EnvChou4Rotated",         DMPAPER_JENV_CHOU4_ROTATED},
112   {"EnvDL",                   DMPAPER_ENV_DL},
113   {"EnvISOB4",                DMPAPER_ENV_B4},
114   {"EnvISOB5",                DMPAPER_ENV_B5},
115   {"EnvISOB6",                DMPAPER_ENV_B6},
116   {"EnvInvite",               DMPAPER_ENV_INVITE},
117   {"EnvItalian",              DMPAPER_ENV_ITALY},
118   {"EnvKaku2",                DMPAPER_JENV_KAKU2},
119   {"EnvKaku2Rotated",         DMPAPER_JENV_KAKU2_ROTATED},
120   {"EnvKaku3",                DMPAPER_JENV_KAKU3},
121   {"EnvKaku3Rotated",         DMPAPER_JENV_KAKU3_ROTATED},
122   {"EnvMonarch",              DMPAPER_ENV_MONARCH},
123   {"EnvPRC1",                 DMPAPER_PENV_1},
124   {"EnvPRC10",                DMPAPER_PENV_10},
125   {"EnvPRC10Rotated",         DMPAPER_PENV_10_ROTATED},
126   {"EnvPRC1Rotated",          DMPAPER_PENV_1_ROTATED},
127   {"EnvPRC2",                 DMPAPER_PENV_2},
128   {"EnvPRC2Rotated",          DMPAPER_PENV_2_ROTATED},
129   {"EnvPRC3",                 DMPAPER_PENV_3},
130   {"EnvPRC3Rotated",          DMPAPER_PENV_3_ROTATED},
131   {"EnvPRC4",                 DMPAPER_PENV_4},
132   {"EnvPRC4Rotated",          DMPAPER_PENV_4_ROTATED},
133   {"EnvPRC5",                 DMPAPER_PENV_5},
134   {"EnvPRC5Rotated",          DMPAPER_PENV_5_ROTATED},
135   {"EnvPRC6",                 DMPAPER_PENV_6},
136   {"EnvPRC6Rotated",          DMPAPER_PENV_6_ROTATED},
137   {"EnvPRC7",                 DMPAPER_PENV_7},
138   {"EnvPRC7Rotated",          DMPAPER_PENV_7_ROTATED},
139   {"EnvPRC8",                 DMPAPER_PENV_8},
140   {"EnvPRC8Rotated",          DMPAPER_PENV_8_ROTATED},
141   {"EnvPRC9",                 DMPAPER_PENV_9},
142   {"EnvPRC9Rotated",          DMPAPER_PENV_9_ROTATED},
143   {"EnvPersonal",             DMPAPER_ENV_PERSONAL},
144   {"EnvYou4",                 DMPAPER_JENV_YOU4},
145   {"EnvYou4Rotated",          DMPAPER_JENV_YOU4_ROTATED},
146   {"Executive",               DMPAPER_EXECUTIVE},
147   {"FanFoldGerman",           DMPAPER_FANFOLD_STD_GERMAN},
148   {"FanFoldGermanLegal",      DMPAPER_FANFOLD_LGL_GERMAN},
149   {"FanFoldUS",               DMPAPER_FANFOLD_US},
150   {"Folio",                   DMPAPER_FOLIO},
151   {"ISOB4",                   DMPAPER_ISO_B4},
152   {"ISOB5Extra",              DMPAPER_B5_EXTRA},
153   {"Ledger",                  DMPAPER_LEDGER},
154   {"Legal",                   DMPAPER_LEGAL},
155   {"LegalExtra",              DMPAPER_LEGAL_EXTRA},
156   {"Letter",                  DMPAPER_LETTER},
157   {"Letter.Transverse",       DMPAPER_LETTER_TRANSVERSE},
158   {"LetterExtra",             DMPAPER_LETTER_EXTRA},
159   {"LetterExtra.Transverse",  DMPAPER_LETTER_EXTRA_TRANSVERSE},
160   {"LetterPlus",              DMPAPER_LETTER_PLUS},
161   {"LetterRotated",           DMPAPER_LETTER_ROTATED},
162   {"LetterSmall",             DMPAPER_LETTERSMALL},
163   {"Monarch",                 DMPAPER_ENV_MONARCH},     /* use EnvMonarch */
164   {"Note",                    DMPAPER_NOTE},
165   {"PRC16K",                  DMPAPER_P16K},
166   {"PRC16KRotated",           DMPAPER_P16K_ROTATED},
167   {"PRC32K",                  DMPAPER_P32K},
168   {"PRC32KBig",               DMPAPER_P32KBIG},
169   {"PRC32KBigRotated",        DMPAPER_P32KBIG_ROTATED},
170   {"PRC32KRotated",           DMPAPER_P32K_ROTATED},
171   {"Postcard",                DMPAPER_JAPANESE_POSTCARD},
172   {"PostcardRotated",         DMPAPER_JAPANESE_POSTCARD_ROTATED},
173   {"Quarto",                  DMPAPER_QUARTO},
174   {"Statement",               DMPAPER_STATEMENT},
175   {"SuperA",                  DMPAPER_A_PLUS},
176   {"SuperB",                  DMPAPER_B_PLUS},
177   {"Tabloid",                 DMPAPER_TABLOID},
178   {"TabloidExtra",            DMPAPER_TABLOID_EXTRA},
179   {NULL,                      0}
180 };
181
182 static WORD UserPageType = DMPAPER_USER;
183 static WORD UserBinType = DMBIN_USER;
184
185 /***********************************************************************
186  *
187  *              PSDRV_PPDDecodeHex
188  *
189  * Copies str into a newly allocated string from the process heap substituting
190  * hex strings enclosed in '<' and '>' for their byte codes.
191  *
192  */
193 static char *PSDRV_PPDDecodeHex(char *str)
194 {
195     char *buf, *in, *out;
196     BOOL inhex = FALSE;
197
198     buf = HeapAlloc(PSDRV_Heap, 0, strlen(str) + 1);
199     if(!buf)
200         return NULL;
201
202     for(in = str, out = buf; *in; in++) {
203         if(!inhex) {
204             if(*in != '<')
205                 *out++ = *in;
206             else
207                 inhex = TRUE;
208         } else {
209             if(*in == '>') {
210                 inhex = FALSE;
211                 continue;
212             }
213             else if(isspace(*in))
214                 continue;
215             else {
216                 int i;
217                 if(!isxdigit(*in) || !isxdigit(*(in + 1))) {
218                     ERR("Invalid hex char in hex string\n");
219                     HeapFree(PSDRV_Heap, 0, buf);
220                     return NULL;
221                 }
222                 *out = 0;
223                 for(i = 0; i < 2; i++) {
224                     if(isdigit(*(in + i)))
225                         *out |= (*(in + i) - '0') << ((1-i) * 4);
226                     else
227                         *out |= (toupper(*(in + i)) - 'A' + 10) << ((1-i) * 4);
228                 }
229                 out++;
230                 in++;
231             }
232         }
233     }
234     *out = '\0';
235     return buf;
236 }
237
238
239 /***********************************************************************
240  *
241  *              PSDRV_PPDGetTransValue
242  *
243  */
244 static BOOL PSDRV_PPDGetTransValue(const char *start, PPDTuple *tuple)
245 {
246     char *buf;
247     const char *end;
248
249     end = strpbrk(start, "\r\n");
250     if(end == start) return FALSE;
251     if(!end) end = start + strlen(start);
252     buf = HeapAlloc( PSDRV_Heap, 0, end - start + 1 );
253     memcpy(buf, start, end - start);
254     *(buf + (end - start)) = '\0';
255     tuple->valtrans = PSDRV_PPDDecodeHex(buf);
256     HeapFree( PSDRV_Heap, 0, buf );
257     return TRUE;
258 }
259
260 static BOOL get_line( char *buf, int size, struct map_context *ctx )
261 {
262     int i;
263     if (ctx->pos > ctx->end) return FALSE;
264
265     for (i = 0; i < size - 1; i++)
266     {
267         if (ctx->pos > ctx->end) break;
268         buf[i] = *ctx->pos++;
269
270         /* \r\n -> \n */
271         if (buf[i] == '\r' && ctx->pos <= ctx->end && *ctx->pos == '\n')
272         {
273             ctx->pos++;
274             buf[i] = '\n';
275         }
276
277         if (buf[i] == '\n' || buf[i] == '\r')
278         {
279             i++;
280             break;
281         }
282     }
283     buf[i] = '\0';
284     return TRUE;
285 }
286
287 /***********************************************************************
288  *
289  *              PSDRV_PPDGetInvocationValue
290  *
291  * Passed string that should be surrounded by `"'s, return string alloced
292  * from process heap.
293  */
294 static BOOL PSDRV_PPDGetInvocationValue(struct map_context *ctx, PPDTuple *tuple)
295 {
296     const char *start;
297     char *buf, line[257];
298
299     assert( *ctx->pos == '"' );
300
301     ctx->pos++;
302     for (start = ctx->pos; ctx->pos <= ctx->end; ctx->pos++)
303         if (*ctx->pos == '"') break;
304     if (ctx->pos > ctx->end) return FALSE;
305     ctx->pos++;
306
307     buf = HeapAlloc( PSDRV_Heap, 0, ctx->pos - start );
308     memcpy( buf, start, ctx->pos - start - 1 );
309     buf[ctx->pos - start  - 1] = '\0';
310     tuple->value = buf;
311
312     if (get_line( line, sizeof(line), ctx ))
313     {
314         start = strchr( line, '/' );
315         if (start) return PSDRV_PPDGetTransValue( start + 1, tuple );
316     }
317     return TRUE;
318 }
319
320
321 /***********************************************************************
322  *
323  *              PSDRV_PPDGetQuotedValue
324  *
325  * Passed string that should be surrounded by `"'s. Expand <xx> as hex
326  * return string alloced from process heap.
327  */
328 static BOOL PSDRV_PPDGetQuotedValue(struct map_context *ctx, PPDTuple *tuple)
329 {
330     char *buf;
331
332     if(!PSDRV_PPDGetInvocationValue(ctx, tuple))
333         return FALSE;
334     buf = PSDRV_PPDDecodeHex(tuple->value);
335     HeapFree(PSDRV_Heap, 0, tuple->value);
336     tuple->value = buf;
337     return TRUE;
338 }
339
340
341 /***********************************************************************
342  *
343  *              PSDRV_PPDGetStringValue
344  *
345  * Just strip leading white space.
346  */
347 static BOOL PSDRV_PPDGetStringValue(char *str, PPDTuple *tuple)
348 {
349     char *start = str, *end;
350
351     while(*start != '\0' && isspace(*start))
352         start++;
353
354     end = strpbrk(start, "/\r\n");
355     if(!end) end = start + strlen(start);
356     tuple->value = HeapAlloc( PSDRV_Heap, 0, (end - start) + 1 );
357     memcpy(tuple->value, start, end - start);
358     *(tuple->value + (end - start)) = '\0';
359     if(*end == '/')
360         PSDRV_PPDGetTransValue(end + 1, tuple);
361     return TRUE;
362 }
363
364
365 /***********************************************************************
366  *
367  *              PSDRV_PPDSymbolValue
368  *
369  * Not implemented yet.
370  */
371 static BOOL PSDRV_PPDGetSymbolValue(char *pos, PPDTuple *tuple)
372 {
373     FIXME("Stub\n");
374     return FALSE;
375 }
376
377
378 /*********************************************************************
379  *
380  *              PSDRV_PPDGetNextTuple
381  *
382  * Gets the next Keyword Option Value tuple from the file. Allocs space off
383  * the process heap which should be free()ed by the caller if not needed.
384  */
385 static BOOL PSDRV_PPDGetNextTuple(struct map_context *ctx, PPDTuple *tuple)
386 {
387     char line[257], *opt, *cp, *trans, *endkey;
388     BOOL gotoption;
389     struct map_context save;
390
391  start:
392
393     gotoption = TRUE;
394     opt = NULL;
395     memset(tuple, 0, sizeof(*tuple));
396
397     do {
398         save = *ctx;
399         if(!get_line(line, sizeof(line), ctx))
400             return FALSE;
401         if(line[0] == '*' && line[1] != '%' && strncmp(line, "*End", 4))
402             break;
403     } while(1);
404
405     cp = line + strlen(line) - 1;
406     if (*cp != '\n' && *cp != '\r')
407     {
408         ERR("Line too long.\n");
409         goto start;
410     }
411
412     for(cp = line; !isspace(*cp) && *cp != ':'; cp++)
413         ;
414
415     endkey = cp;
416     while (isspace(*cp)) cp++;
417     if (*cp == ':') /* <key>: */
418         gotoption = FALSE;
419     else /* <key> <option> */
420         opt = cp;
421
422     tuple->key = HeapAlloc( PSDRV_Heap, 0, endkey - line + 1 );
423     if(!tuple->key) return FALSE;
424
425     memcpy(tuple->key, line, endkey - line);
426     tuple->key[endkey - line] = '\0';
427
428     if(gotoption) { /* opt points to 1st non-space character of the option */
429         cp = strpbrk(opt, ":/");
430         if(!cp) {
431             ERR("Error in line '%s'?\n", line);
432             HeapFree(GetProcessHeap(), 0, tuple->key);
433             goto start;
434         }
435         tuple->option = HeapAlloc( PSDRV_Heap, 0, cp - opt + 1 );
436         if(!tuple->option) return FALSE;
437         memcpy(tuple->option, opt, cp - opt);
438         tuple->option[cp - opt] = '\0';
439         if(*cp == '/') {
440             char *buf;
441             trans = cp + 1;
442             cp = strchr(trans, ':');
443             if(!cp) {
444                 ERR("Error in line '%s'?\n", line);
445                 HeapFree(GetProcessHeap(), 0, tuple->option);
446                 HeapFree(GetProcessHeap(), 0, tuple->key);
447                 goto start;
448             }
449             buf = HeapAlloc( PSDRV_Heap, 0, cp - trans + 1 );
450             if(!buf) return FALSE;
451             memcpy(buf, trans, cp - trans);
452             buf[cp - trans] = '\0';
453             tuple->opttrans = PSDRV_PPDDecodeHex(buf);
454             HeapFree( PSDRV_Heap, 0, buf );
455         }
456     }
457
458     /* cp should point to a ':', so we increment past it */
459     cp++;
460
461     while(isspace(*cp))
462         cp++;
463
464     switch(*cp) {
465     case '"':
466         /* update the context pos so that it points to the opening quote */
467         ctx->pos = save.pos + (cp - line);
468         if( (!gotoption && strncmp(tuple->key, "*?", 2) ) ||
469              !strncmp(tuple->key, "*JCL", 4))
470             PSDRV_PPDGetQuotedValue(ctx, tuple);
471         else
472             PSDRV_PPDGetInvocationValue(ctx, tuple);
473         break;
474
475     case '^':
476         PSDRV_PPDGetSymbolValue(cp, tuple);
477         break;
478
479     default:
480         PSDRV_PPDGetStringValue(cp, tuple);
481     }
482     return TRUE;
483 }
484
485 /*********************************************************************
486  *                       get_pagesize
487  *
488  * Searches ppd PageSize list to return entry matching name or optionally creates new
489  * entry which is appended to the list if name is not found.
490  */
491 static PAGESIZE *get_pagesize( PPD *ppd, char *name, BOOL create )
492 {
493     PAGESIZE *page;
494
495     LIST_FOR_EACH_ENTRY(page, &ppd->PageSizes, PAGESIZE, entry)
496     {
497         if(!strcmp(page->Name, name))
498             return page;
499     }
500
501     if (!create) return NULL;
502
503     page = HeapAlloc( PSDRV_Heap,  HEAP_ZERO_MEMORY, sizeof(*page) );
504     list_add_tail(&ppd->PageSizes, &page->entry);
505     return page;
506 }
507
508 static DUPLEX *get_duplex( PPD *ppd, const char *name )
509 {
510     DUPLEX *duplex;
511
512     LIST_FOR_EACH_ENTRY( duplex, &ppd->Duplexes, DUPLEX, entry )
513     {
514         if (!strcmp( duplex->Name, name ))
515             return duplex;
516     }
517
518     return NULL;
519 }
520
521 /**********************************************************************
522  *
523  *              PSDRV_PPDGetWord
524  *
525  * Returns ptr alloced from heap to first word in str. Strips leading spaces.
526  * Puts ptr to next word in next
527  */
528 static char *PSDRV_PPDGetWord(char *str, char **next)
529 {
530     char *start, *end, *ret;
531
532     start = str;
533     while(start && *start && isspace(*start))
534         start++;
535     if(!start || !*start) return FALSE;
536
537     end = start;
538     while(*end && !isspace(*end))
539         end++;
540
541     ret = HeapAlloc( PSDRV_Heap, 0, end - start + 1 );
542     memcpy(ret, start, end - start );
543     *(ret + (end - start)) = '\0';
544
545     while(*end && isspace(*end))
546         end++;
547     if(*end)
548         *next = end;
549     else
550         *next = NULL;
551
552     return ret;
553 }
554
555 /************************************************************
556  *           parse_resolution
557  *
558  * Helper to extract x and y resolutions from a resolution entry.
559  * Returns TRUE on successful parsing, otherwise FALSE.
560  *
561  * The ppd spec says that entries can either be:
562  *    "nnndpi"
563  * or
564  *    "nnnxmmmdpi"
565  * in the former case return sz.cx == cx.cy == nnn.
566  *
567  * There are broken ppd files out there (notably from Samsung) that
568  * use the form "nnnmmmdpi", so we have to work harder to spot these.
569  * We use a transition from a zero to a non-zero digit as separator
570  * (and fail if we find more than one of these).  We also don't bother
571  * trying to parse "dpi" in case that's missing.
572  */
573 static BOOL parse_resolution(const char *str, SIZE *sz)
574 {
575     int tmp[2];
576     int *cur;
577     BOOL had_zero;
578     const char *c;
579
580     if(sscanf(str, "%dx%d", tmp, tmp + 1) == 2)
581     {
582         sz->cx = tmp[0];
583         sz->cy = tmp[1];
584         return TRUE;
585     }
586
587     tmp[0] = 0;
588     tmp[1] = -1;
589     cur = tmp;
590     had_zero = FALSE;
591     for(c = str; isdigit(*c); c++)
592     {
593         if(!had_zero || *c == '0')
594         {
595             *cur *= 10;
596             *cur += *c - '0';
597             if(*c == '0')
598                 had_zero = TRUE;
599         }
600         else if(cur != tmp)
601             return FALSE;
602         else
603         {
604             cur++;
605             *cur = *c - '0';
606             had_zero = FALSE;
607         }
608     }
609     if(tmp[0] == 0) return FALSE;
610
611     sz->cx = tmp[0];
612     if(tmp[1] != -1)
613         sz->cy = tmp[1];
614     else
615         sz->cy = sz->cx;
616     return TRUE;
617 }
618
619 /*******************************************************************************
620  *      PSDRV_AddSlot
621  *
622  */
623 static BOOL PSDRV_AddSlot(PPD *ppd, LPCSTR szName, LPCSTR szFullName,
624         LPSTR szInvocationString, WORD wWinBin)
625 {
626     INPUTSLOT *slot = HeapAlloc( PSDRV_Heap, 0, sizeof(INPUTSLOT) );
627     if (!slot) return FALSE;
628
629     slot->Name = szName;
630     slot->FullName = szFullName;
631     slot->InvocationString = szInvocationString;
632     slot->WinBin = wWinBin;
633
634     list_add_tail( &ppd->InputSlots, &slot->entry );
635     return TRUE;
636 }
637
638 static char *get_ppd_override( HANDLE printer, const char *value )
639 {
640     DWORD err, type, needed;
641     char *data;
642
643     err = GetPrinterDataExA( printer, "PPD Overrides", value, &type, NULL, 0, &needed );
644     if (err != ERROR_MORE_DATA || type != REG_SZ || needed == 0) return NULL;
645
646     data = HeapAlloc( PSDRV_Heap, 0, needed );
647     if (data)
648     {
649         GetPrinterDataExA( printer, "PPD Overrides", value, &type, (BYTE*)data, needed, &needed );
650         TRACE( "got override %s: %s\n", value, data );
651     }
652     return data;
653 }
654
655 static BOOL map_file( const WCHAR *filename, struct map_context *c )
656 {
657     HANDLE file, mapping;
658     LARGE_INTEGER size;
659
660     file = CreateFileW( filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
661     if (file == INVALID_HANDLE_VALUE) return FALSE;
662
663     if (!GetFileSizeEx( file, &size ) || size.u.HighPart)
664     {
665         CloseHandle( file );
666         return FALSE;
667     }
668
669     mapping = CreateFileMappingW( file, NULL, PAGE_READONLY, 0, 0, NULL );
670     CloseHandle( file );
671     if (!mapping) return FALSE;
672
673     c->pos = c->ptr = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, 0 );
674     c->end = c->ptr + size.u.LowPart - 1;
675     CloseHandle( mapping );
676     return TRUE;
677 }
678
679 static void unmap_file( struct map_context *c )
680 {
681     UnmapViewOfFile( c->ptr );
682 }
683
684 /***********************************************************************
685  *
686  *              PSDRV_ParsePPD
687  *
688  *
689  */
690 PPD *PSDRV_ParsePPD( const WCHAR *fname, HANDLE printer )
691 {
692     PPD *ppd;
693     PPDTuple tuple;
694     char *default_pagesize = NULL, *default_duplex = NULL;
695     char *def_pagesize_override = NULL, *def_duplex_override = NULL;
696     PAGESIZE *page, *page_cursor2;
697     struct map_context c;
698
699     TRACE("file %s\n", debugstr_w(fname));
700
701     if (!map_file( fname, &c ))
702     {
703         WARN("Couldn't open ppd file %s\n", debugstr_w(fname));
704         return NULL;
705     }
706
707     ppd = HeapAlloc( PSDRV_Heap, HEAP_ZERO_MEMORY, sizeof(*ppd));
708     if(!ppd) {
709         ERR("Unable to allocate memory for ppd\n");
710         unmap_file( &c );
711         return NULL;
712     }
713
714     ppd->ColorDevice = CD_NotSpecified;
715
716     list_init( &ppd->Resolutions );
717     list_init( &ppd->InstalledFonts );
718     list_init( &ppd->PageSizes );
719     list_init( &ppd->Constraints );
720     list_init( &ppd->InputSlots );
721     list_init( &ppd->Duplexes );
722
723     /* Some gimp-print ppd files don't contain a DefaultResolution line
724        so default to 300 */
725     ppd->DefaultResolution = 300;
726
727     /*
728      *  The Windows PostScript drivers create the following "virtual bin" for
729      *  every PostScript printer
730      */
731     if (!PSDRV_AddSlot( ppd, NULL, "Automatically Select", NULL, DMBIN_FORMSOURCE ))
732     {
733         HeapFree (PSDRV_Heap, 0, ppd);
734         unmap_file( &c );
735         return NULL;
736     }
737
738     while (PSDRV_PPDGetNextTuple( &c, &tuple ))
739     {
740         if(!strcmp("*NickName", tuple.key)) {
741             ppd->NickName = tuple.value;
742             tuple.value = NULL;
743             TRACE("NickName = '%s'\n", ppd->NickName);
744         }
745
746         else if(!strcmp("*LanguageLevel", tuple.key)) {
747             sscanf(tuple.value, "%d", &(ppd->LanguageLevel));
748             TRACE("LanguageLevel = %d\n", ppd->LanguageLevel);
749         }
750
751         else if(!strcmp("*ColorDevice", tuple.key)) {
752             if(!strcasecmp(tuple.value, "true"))
753                 ppd->ColorDevice = CD_True;
754             else
755                 ppd->ColorDevice = CD_False;
756             TRACE("ColorDevice = %s\n", ppd->ColorDevice == CD_True ? "True" : "False");
757         }
758
759         else if((!strcmp("*DefaultResolution", tuple.key)) ||
760                 (!strcmp("*DefaultJCLResolution", tuple.key))) {
761             SIZE sz;
762             if(parse_resolution(tuple.value, &sz))
763             {
764                 TRACE("DefaultResolution %dx%d\n", sz.cx, sz.cy);
765                 ppd->DefaultResolution = sz.cx;
766             }
767             else
768                 WARN("failed to parse DefaultResolution %s\n", debugstr_a(tuple.value));
769         }
770
771         else if(!strcmp("*Resolution", tuple.key))
772         {
773             SIZE sz;
774             if (parse_resolution(tuple.option, &sz))
775             {
776                 RESOLUTION *res;
777
778                 TRACE("Resolution %dx%d, invocation %s\n", sz.cx, sz.cy, tuple.value);
779
780                 res = HeapAlloc( GetProcessHeap(), 0, sizeof(*res) );
781                 res->resx = sz.cx;
782                 res->resy = sz.cy;
783                 res->InvocationString = tuple.value;
784                 tuple.value = NULL;
785                 list_add_tail( &ppd->Resolutions, &res->entry );
786             }
787             else
788                 WARN("failed to parse Resolution %s\n", debugstr_a(tuple.option));
789         }
790
791         else if(!strcmp("*Font", tuple.key))
792         {
793             FONTNAME *fn = HeapAlloc( PSDRV_Heap, 0, sizeof(*fn) );
794             fn->Name = tuple.option;
795             tuple.option = NULL;
796             list_add_tail( &ppd->InstalledFonts, &fn->entry );
797         }
798
799         else if(!strcmp("*DefaultFont", tuple.key)) {
800             ppd->DefaultFont = tuple.value;
801             tuple.value = NULL;
802         }
803
804         else if(!strcmp("*JCLBegin", tuple.key)) {
805             ppd->JCLBegin = tuple.value;
806             tuple.value = NULL;
807         }
808
809         else if(!strcmp("*JCLToPSInterpreter", tuple.key)) {
810             ppd->JCLToPSInterpreter = tuple.value;
811             tuple.value = NULL;
812         }
813
814         else if(!strcmp("*JCLEnd", tuple.key)) {
815             ppd->JCLEnd = tuple.value;
816             tuple.value = NULL;
817         }
818
819         else if(!strcmp("*PageSize", tuple.key)) {
820             page = get_pagesize( ppd, tuple.option, TRUE );
821
822             if(!page->Name) {
823                 int i;
824
825                 page->Name = tuple.option;
826                 tuple.option = NULL;
827
828                 for(i = 0; PageTrans[i].PSName; i++) {
829                     if(!strcmp(PageTrans[i].PSName, page->Name)) { /* case ? */
830                         page->WinPage = PageTrans[i].WinPage;
831                         break;
832                     }
833                 }
834                 if(!page->WinPage) {
835                     TRACE("Can't find Windows page type for '%s' - using %u\n",
836                           page->Name, UserPageType);
837                     page->WinPage = UserPageType++;
838                 }
839             }
840             if(!page->FullName) {
841                 if(tuple.opttrans) {
842                     page->FullName = tuple.opttrans;
843                     tuple.opttrans = NULL;
844                 } else
845                 {
846                     page->FullName = HeapAlloc( PSDRV_Heap, 0, strlen(page->Name)+1 );
847                     strcpy( page->FullName, page->Name );
848                 }
849             }
850             if(!page->InvocationString) {
851                 page->InvocationString = tuple.value;
852                 tuple.value = NULL;
853             }
854         }
855
856         else if(!strcmp("*DefaultPageSize", tuple.key))
857         {
858             if (!default_pagesize)
859             {
860                 default_pagesize = tuple.value;
861                 tuple.value = NULL;
862             }
863         }
864
865         else if(!strcmp("*ImageableArea", tuple.key)) {
866             page = get_pagesize( ppd, tuple.option, TRUE );
867
868             if(!page->Name) {
869                 page->Name = tuple.option;
870                 tuple.option = NULL;
871             }
872             if(!page->FullName) {
873                 page->FullName = tuple.opttrans;
874                 tuple.opttrans = NULL;
875             }
876
877 #define PIA page->ImageableArea
878             if(!PIA) {
879                 PIA = HeapAlloc( PSDRV_Heap, 0, sizeof(*PIA) );
880                 push_lc_numeric("C");
881                 sscanf(tuple.value, "%f%f%f%f", &PIA->llx, &PIA->lly,
882                                                 &PIA->urx, &PIA->ury);
883                 pop_lc_numeric();
884             }
885 #undef PIA
886         }
887
888         else if(!strcmp("*PaperDimension", tuple.key)) {
889             page = get_pagesize( ppd, tuple.option, TRUE );
890
891             if(!page->Name) {
892                 page->Name = tuple.option;
893                 tuple.option = NULL;
894             }
895             if(!page->FullName) {
896                 page->FullName = tuple.opttrans;
897                 tuple.opttrans = NULL;
898             }
899
900 #define PD page->PaperDimension
901             if(!PD) {
902                 PD = HeapAlloc( PSDRV_Heap, 0, sizeof(*PD) );
903                 push_lc_numeric("C");
904                 sscanf(tuple.value, "%f%f", &PD->x, &PD->y);
905                 pop_lc_numeric();
906             }
907 #undef PD
908         }
909
910         else if(!strcmp("*LandscapeOrientation", tuple.key)) {
911             if(!strcmp(tuple.value, "Plus90"))
912                 ppd->LandscapeOrientation = 90;
913             else if(!strcmp(tuple.value, "Minus90"))
914                 ppd->LandscapeOrientation = -90;
915
916             /* anything else, namely 'any', leaves value at 0 */
917
918             TRACE("LandscapeOrientation = %d\n",
919                   ppd->LandscapeOrientation);
920         }
921
922         else if(!strcmp("*UIConstraints", tuple.key))
923         {
924             char *start;
925             CONSTRAINT *con = HeapAlloc( PSDRV_Heap, 0, sizeof(*con) );
926
927             start = tuple.value;
928             con->Feature1 = PSDRV_PPDGetWord(start, &start);
929             con->Value1 = PSDRV_PPDGetWord(start, &start);
930             con->Feature2 = PSDRV_PPDGetWord(start, &start);
931             con->Value2 = PSDRV_PPDGetWord(start, &start);
932
933             list_add_tail( &ppd->Constraints, &con->entry );
934         }
935
936         else if (!strcmp("*InputSlot", tuple.key))
937         {
938
939             if (!tuple.opttrans)
940                 tuple.opttrans = tuple.option;
941
942             TRACE("Using Windows bin type %u for '%s'\n", UserBinType,
943                     tuple.option);
944
945             /* FIXME - should check for failure */
946             PSDRV_AddSlot(ppd, tuple.option, tuple.opttrans, tuple.value,
947                     UserBinType++);
948
949             tuple.option = tuple.opttrans = tuple.value = NULL;
950         }
951
952         /*
953          *  Windows treats "manual feed" as another paper source.  Most PPD
954          *  files, however, treat it as a separate option which is either on or
955          *  off.
956          */
957         else if (!strcmp("*ManualFeed", tuple.key) && tuple.option &&
958                 !strcmp ("True", tuple.option))
959         {
960             /* FIXME - should check for failure */
961             PSDRV_AddSlot(ppd, "Manual Feed", "Manual Feed", tuple.value, DMBIN_MANUAL);
962             tuple.value = NULL;
963         }
964
965         else if(!strcmp("*TTRasterizer", tuple.key)) {
966             if(!strcasecmp("None", tuple.value))
967                 ppd->TTRasterizer = RO_None;
968             else if(!strcasecmp("Accept68K", tuple.value))
969                 ppd->TTRasterizer = RO_Accept68K;
970             else if(!strcasecmp("Type42", tuple.value))
971                 ppd->TTRasterizer = RO_Type42;
972             else if(!strcasecmp("TrueImage", tuple.value))
973                 ppd->TTRasterizer = RO_TrueImage;
974             else {
975                 FIXME("Unknown option %s for *TTRasterizer\n",
976                       tuple.value);
977                 ppd->TTRasterizer = RO_None;
978             }
979             TRACE("*TTRasterizer = %d\n", ppd->TTRasterizer);
980         }
981
982         else if(!strcmp("*Duplex", tuple.key))
983         {
984             DUPLEX *duplex = HeapAlloc( GetProcessHeap(), 0, sizeof(*duplex) );
985             duplex->Name = tuple.option;
986             duplex->FullName = tuple.opttrans;
987             duplex->InvocationString = tuple.value;
988             if(!strcasecmp("None", tuple.option) || !strcasecmp("False", tuple.option)
989                || !strcasecmp("Simplex", tuple.option))
990                 duplex->WinDuplex = DMDUP_SIMPLEX;
991             else if(!strcasecmp("DuplexNoTumble", tuple.option))
992                 duplex->WinDuplex = DMDUP_VERTICAL;
993             else if(!strcasecmp("DuplexTumble", tuple.option))
994                 duplex->WinDuplex = DMDUP_HORIZONTAL;
995             else if(!strcasecmp("Notcapable", tuple.option))
996                 duplex->WinDuplex = 0;
997             else {
998                 FIXME("Unknown option %s for *Duplex defaulting to simplex\n", tuple.option);
999                 duplex->WinDuplex = DMDUP_SIMPLEX;
1000             }
1001             tuple.option = tuple.opttrans = tuple.value = NULL;
1002             list_add_tail( &ppd->Duplexes, &duplex->entry );
1003         }
1004
1005         else if (!strcmp("*DefaultDuplex", tuple.key))
1006         {
1007             if (!default_duplex)
1008             {
1009                 default_duplex = tuple.value;
1010                 tuple.value = NULL;
1011             }
1012         }
1013
1014         HeapFree(PSDRV_Heap, 0, tuple.key);
1015         HeapFree(PSDRV_Heap, 0, tuple.option);
1016         HeapFree(PSDRV_Heap, 0, tuple.value);
1017         HeapFree(PSDRV_Heap, 0, tuple.opttrans);
1018         HeapFree(PSDRV_Heap, 0, tuple.valtrans);
1019
1020     }
1021
1022     /* Remove any partial page size entries, that is any without a PageSize or a PaperDimension (we can
1023        cope with a missing ImageableArea). */
1024     LIST_FOR_EACH_ENTRY_SAFE(page, page_cursor2, &ppd->PageSizes, PAGESIZE, entry)
1025     {
1026         if(!page->InvocationString || !page->PaperDimension)
1027         {
1028             WARN("Removing page %s since it has a missing %s entry\n", debugstr_a(page->FullName),
1029                  page->InvocationString ? "PaperDimension" : "InvocationString");
1030             HeapFree(PSDRV_Heap, 0, page->Name);
1031             HeapFree(PSDRV_Heap, 0, page->FullName);
1032             HeapFree(PSDRV_Heap, 0, page->InvocationString);
1033             HeapFree(PSDRV_Heap, 0, page->ImageableArea);
1034             HeapFree(PSDRV_Heap, 0, page->PaperDimension);
1035             list_remove(&page->entry);
1036         }
1037     }
1038
1039     ppd->DefaultPageSize = NULL;
1040     def_pagesize_override = get_ppd_override( printer, "DefaultPageSize" );
1041     if (def_pagesize_override)
1042         ppd->DefaultPageSize = get_pagesize( ppd, def_pagesize_override, FALSE );
1043     if (!ppd->DefaultPageSize && default_pagesize)
1044         ppd->DefaultPageSize = get_pagesize( ppd, default_pagesize, FALSE );
1045
1046     if (!ppd->DefaultPageSize)
1047     {
1048         struct list *head = list_head( &ppd->PageSizes );
1049         if (head) ppd->DefaultPageSize = LIST_ENTRY( head, PAGESIZE, entry );
1050         TRACE("Setting DefaultPageSize to first in list\n");
1051     }
1052     TRACE( "DefaultPageSize: %s\n", ppd->DefaultPageSize ? ppd->DefaultPageSize->Name : "<not set>" );
1053
1054     HeapFree( PSDRV_Heap, 0, def_pagesize_override );
1055     HeapFree( PSDRV_Heap, 0, default_pagesize );
1056
1057     ppd->DefaultDuplex = NULL;
1058     def_duplex_override = get_ppd_override( printer, "DefaultDuplex" );
1059     if (def_duplex_override)
1060         ppd->DefaultDuplex = get_duplex( ppd, def_duplex_override );
1061     if (!ppd->DefaultDuplex && default_duplex)
1062         ppd->DefaultDuplex = get_duplex( ppd, default_duplex );
1063
1064     if (!ppd->DefaultDuplex)
1065     {
1066         struct list *head = list_head( &ppd->Duplexes );
1067         if (head) ppd->DefaultDuplex = LIST_ENTRY( head, DUPLEX, entry );
1068         TRACE("Setting DefaultDuplex to first in list\n");
1069     }
1070     TRACE( "DefaultDuplex: %s\n", ppd->DefaultDuplex ? ppd->DefaultDuplex->Name : "<not set>" );
1071
1072     HeapFree( PSDRV_Heap, 0, def_duplex_override );
1073     HeapFree( PSDRV_Heap, 0, default_duplex );
1074
1075
1076     {
1077         FONTNAME *fn;
1078         PAGESIZE *page;
1079         CONSTRAINT *con;
1080         INPUTSLOT *slot;
1081
1082         LIST_FOR_EACH_ENTRY( fn, &ppd->InstalledFonts, FONTNAME, entry )
1083             TRACE("'%s'\n", fn->Name);
1084
1085         LIST_FOR_EACH_ENTRY(page, &ppd->PageSizes, PAGESIZE, entry) {
1086             TRACE("'%s' aka '%s' (%d) invoked by '%s'\n", page->Name,
1087               page->FullName, page->WinPage, page->InvocationString);
1088             if(page->ImageableArea)
1089                 TRACE("Area = %.2f,%.2f - %.2f, %.2f\n",
1090                       page->ImageableArea->llx, page->ImageableArea->lly,
1091                       page->ImageableArea->urx, page->ImageableArea->ury);
1092             if(page->PaperDimension)
1093                 TRACE("Dimension = %.2f x %.2f\n",
1094                       page->PaperDimension->x, page->PaperDimension->y);
1095         }
1096
1097         LIST_FOR_EACH_ENTRY( con, &ppd->Constraints, CONSTRAINT, entry )
1098             TRACE("CONSTRAINTS@ %s %s %s %s\n", con->Feature1,
1099                   con->Value1, con->Feature2, con->Value2);
1100
1101         LIST_FOR_EACH_ENTRY( slot, &ppd->InputSlots, INPUTSLOT, entry )
1102             TRACE("INPUTSLOTS '%s' Name '%s' (%d) Invocation '%s'\n",
1103                   debugstr_a(slot->Name), slot->FullName, slot->WinBin,
1104                   debugstr_a(slot->InvocationString));
1105     }
1106
1107     unmap_file( &c );
1108     return ppd;
1109 }