PSDRV_StretchDIBits should use logical co-ords.
[wine] / graphics / psdrv / 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
8 #include <string.h>
9 #include <ctype.h>
10 #include "winnt.h" /* HEAP_ZERO_MEMORY */
11 #include "heap.h"
12 #include "debug.h"
13 #include "psdrv.h"
14 #include "winspool.h"
15
16 DEFAULT_DEBUG_CHANNEL(psdrv)
17
18 typedef struct {
19 char    *key;
20 char    *option;
21 char    *opttrans;
22 char    *value;
23 char    *valtrans;
24 } PPDTuple;
25
26
27 /* map of page names in ppd file to Windows paper constants */
28
29 static struct {
30   char *PSName;
31   WORD WinPage;
32 } PageTrans[] = {
33   {"A4",        DMPAPER_A4},
34   {"Letter",    DMPAPER_LETTER},
35   {"Legal",     DMPAPER_LEGAL},
36   {"Executive", DMPAPER_EXECUTIVE},
37   {"Comm10",    DMPAPER_ENV_10},
38   {"Monarch",   DMPAPER_ENV_MONARCH},
39   {"DL",        DMPAPER_ENV_DL},
40   {"C5",        DMPAPER_ENV_C5},
41   {"B5",        DMPAPER_ENV_B5},
42   {NULL,        0}
43 };
44
45 /* the same for bin names */
46
47 static struct {
48   char *PSName;
49   WORD WinBin;
50 } BinTrans[] = {
51   {"Lower",             DMBIN_LOWER},
52   {"Upper",             DMBIN_UPPER},
53   {"Envelope",          DMBIN_ENVELOPE},
54   {"LargeCapacity",     DMBIN_LARGECAPACITY},
55   {NULL,                0}
56 };
57
58 /***********************************************************************
59  *
60  *              PSDRV_PPDDecodeHex
61  *
62  * Copies str into a newly allocated string from the process heap subsituting
63  * hex strings enclosed in '<' and '>' for their byte codes.
64  *
65  */
66 static char *PSDRV_PPDDecodeHex(char *str)
67 {
68     char *buf, *in, *out;
69     BOOL inhex = FALSE;
70
71     buf = HeapAlloc(PSDRV_Heap, 0, strlen(str) + 1);
72     if(!buf)
73         return NULL;
74
75     for(in = str, out = buf; *in; in++) {
76         if(!inhex) {
77             if(*in != '<')
78                 *out++ = *in;
79             else
80                 inhex = TRUE;
81         } else {
82             if(*in == '>') {
83                 inhex = FALSE;
84                 continue;
85             }
86             else if(isspace(*in))
87                 continue;
88             else {
89                 int i;
90                 if(!isxdigit(*in) || !isxdigit(*(in + 1))) {
91                     ERR(psdrv, "Invalid hex char in hex string\n");
92                     HeapFree(PSDRV_Heap, 0, buf);
93                     return NULL;
94                 }
95                 *out = 0;
96                 for(i = 0; i < 2; i++) {
97                     if(isdigit(*(in + i)))
98                         *out |= (*(in + i) - '0') << ((1-i) * 4);
99                     else
100                         *out |= (toupper(*(in + i)) - 'A' + 10) << ((1-i) * 4);
101                 }
102                 out++;
103                 in++;
104             }
105         }
106     }
107     *out = '\0';
108     return buf;
109 }
110
111
112 /***********************************************************************
113  *
114  *              PSDRV_PPDGetTransValue
115  *
116  */
117 static BOOL PSDRV_PPDGetTransValue(char *start, PPDTuple *tuple)
118 {
119     char *buf, *end;
120
121     end = strpbrk(start, "\r\n");
122     if(end == start) return FALSE;
123     if(!end) end = start + strlen(start);
124     buf = HeapAlloc( PSDRV_Heap, 0, end - start + 1 );
125     memcpy(buf, start, end - start);
126     *(buf + (end - start)) = '\0';
127     tuple->valtrans = PSDRV_PPDDecodeHex(buf);
128     HeapFree( PSDRV_Heap, 0, buf );
129     return TRUE;
130 }
131
132
133 /***********************************************************************
134  *
135  *              PSDRV_PPDGetInvocationValue
136  *
137  * Passed string that should be surrounded by `"'s, return string alloced
138  * from process heap.
139  */
140 static BOOL PSDRV_PPDGetInvocationValue(FILE *fp, char *pos, PPDTuple *tuple)
141 {
142     char *start, *end, *buf;
143     char line[257];
144     int len;
145
146     start = pos + 1;
147     buf = HeapAlloc( PSDRV_Heap, 0, strlen(start) + 1 );
148     len = 0;
149     do {
150         end = strchr(start, '"');
151         if(end) {
152             buf = HeapReAlloc( PSDRV_Heap, 0, buf, 
153                                len + (end - start) + 1 );
154             memcpy(buf + len, start, end - start);
155             *(buf + len + (end - start)) = '\0';
156             tuple->value = buf;
157             start = strchr(end, '/');
158             if(start)
159                 return PSDRV_PPDGetTransValue(start + 1, tuple);
160             return TRUE;
161         } else {
162             int sl = strlen(start);
163             buf = HeapReAlloc( PSDRV_Heap, 0, buf, len + sl + 1 );
164             strcpy(buf + len, start);
165             len += sl;
166         }
167     } while( fgets((start = line), sizeof(line), fp) );
168
169     tuple->value = NULL;
170     HeapFree( PSDRV_Heap, 0, buf );
171     return FALSE;
172 }
173
174
175 /***********************************************************************
176  *
177  *              PSDRV_PPDGetQuotedValue
178  *
179  * Passed string that should be surrounded by `"'s. Expand <xx> as hex
180  * return string alloced from process heap.
181  */
182 static BOOL PSDRV_PPDGetQuotedValue(FILE *fp, char *pos, PPDTuple *tuple)
183 {
184     char *buf;
185
186     if(!PSDRV_PPDGetInvocationValue(fp, pos, tuple))
187         return FALSE;
188     buf = PSDRV_PPDDecodeHex(tuple->value);
189     HeapFree(PSDRV_Heap, 0, tuple->value);
190     tuple->value = buf;
191     return TRUE;
192 }
193
194
195 /***********************************************************************
196  *
197  *              PSDRV_PPDGetStringValue
198  *
199  * Just strip leading white space.
200  */
201 static BOOL PSDRV_PPDGetStringValue(char *str, PPDTuple *tuple)
202 {
203     char *start = str, *end;
204
205     while(*start != '\0' && isspace(*start))
206         start++;
207
208     end = strpbrk(start, "/\r\n");
209     if(!end) end = start + strlen(start);
210     tuple->value = HeapAlloc( PSDRV_Heap, 0, (end - start) + 1 );
211     memcpy(tuple->value, start, end - start);
212     *(tuple->value + (end - start)) = '\0';
213     if(*end == '/')
214         PSDRV_PPDGetTransValue(end + 1, tuple);
215     return TRUE;
216 }
217
218
219 /***********************************************************************
220  *
221  *              PSDRV_PPDSymbolValue
222  *
223  * Not implemented yet.
224  */
225 static BOOL PSDRV_PPDGetSymbolValue(char *pos, PPDTuple *tuple)
226 {
227     FIXME(psdrv, "Stub\n");
228     return FALSE;
229 }
230
231
232 /*********************************************************************
233  *
234  *              PSDRV_PPDGetNextTuple
235  *
236  * Gets the next Keyword Option Value tuple from the file. Allocs space off
237  * the process heap which should be free()ed by the caller if not needed.
238  */
239 static BOOL PSDRV_PPDGetNextTuple(FILE *fp, PPDTuple *tuple)
240 {
241     char line[257], *opt = NULL, *cp, *trans;
242     BOOL gotoption = TRUE;
243
244     memset(tuple, 0, sizeof(*tuple));
245
246     do {
247         if(!fgets(line, sizeof(line), fp))
248             return FALSE;
249         if(line[0] == '*' && line[1] != '%' && strncmp(line, "*End", 4))
250             break;
251     } while(1);
252
253     if(line[strlen(line)-1] != '\n') {
254         ERR(psdrv, "Line too long.\n");
255         return FALSE;
256     }
257
258     for(cp = line; !isspace(*cp); cp++)
259         ;
260
261     if(*(cp-1) == ':') {
262         cp--;
263         gotoption = FALSE;
264     } else {
265         opt = cp;
266     }
267
268     tuple->key = HeapAlloc( PSDRV_Heap, 0, cp - line + 1 );
269     if(!tuple->key) return FALSE;
270
271     memcpy(tuple->key, line, cp - line);
272     tuple->key[cp - line] = '\0';
273
274     if(gotoption) {
275         while(isspace(*opt))
276             opt++;
277         cp = strpbrk(opt, ":/");
278         if(!cp) {
279             ERR(psdrv, "Error in line '%s'?\n", line);
280             return FALSE;
281         }
282         tuple->option = HeapAlloc( PSDRV_Heap, 0, cp - opt + 1 );
283         if(!tuple->option) return FALSE;
284         memcpy(tuple->option, opt, cp - opt);
285         tuple->option[cp - opt] = '\0';
286         if(*cp == '/') {
287             char *buf;
288             trans = cp + 1;
289             cp = strchr(trans, ':');
290             if(!cp) {
291                 ERR(psdrv, "Error in line '%s'?\n", line);
292                 return FALSE;
293             }
294             buf = HeapAlloc( PSDRV_Heap, 0, cp - trans + 1 );
295             if(!buf) return FALSE;
296             memcpy(buf, trans, cp - trans);
297             buf[cp - trans] = '\0';
298             tuple->opttrans = PSDRV_PPDDecodeHex(buf);
299             HeapFree( PSDRV_Heap, 0, buf );
300         }
301     }
302     while(!isspace(*cp))
303         cp++;
304
305     while(isspace(*cp))
306         cp++;
307
308     switch(*cp) {
309     case '"':
310         if( (!gotoption && strncmp(tuple->key, "*?", 2) ) ||
311              !strncmp(tuple->key, "*JCL", 4))
312             PSDRV_PPDGetQuotedValue(fp, cp, tuple);
313         else
314             PSDRV_PPDGetInvocationValue(fp, cp, tuple);
315         break;
316
317     case '^':
318         PSDRV_PPDGetSymbolValue(cp, tuple);
319         break;
320
321     default:
322         PSDRV_PPDGetStringValue(cp, tuple);
323     }
324     return TRUE;
325 }
326
327 /*********************************************************************
328  *
329  *              PSDRV_PPDGetPageSizeInfo
330  *
331  * Searches ppd PageSize list to return entry matching name or creates new
332  * entry which is appended to the list if name is not found.
333  *
334  */
335 PAGESIZE *PSDRV_PPDGetPageSizeInfo(PPD *ppd, char *name)
336 {
337     PAGESIZE *page = ppd->PageSizes, *lastpage;
338     
339     if(!page) {
340        page = ppd->PageSizes = HeapAlloc( PSDRV_Heap,
341                                             HEAP_ZERO_MEMORY, sizeof(*page) );
342        return page;
343     } else {
344         for( ; page; page = page->next) {
345             if(!strcmp(page->Name, name))
346                  return page;
347             lastpage = page;
348         }
349
350         lastpage->next = HeapAlloc( PSDRV_Heap,
351                                            HEAP_ZERO_MEMORY, sizeof(*page) );
352         return lastpage->next;
353     }
354 }
355
356 /**********************************************************************
357  *
358  *              PSDRV_PPDGetWord
359  *
360  * Returns ptr alloced from heap to first word in str. Strips leading spaces.
361  * Puts ptr to next word in next
362  */
363 static char *PSDRV_PPDGetWord(char *str, char **next)
364 {
365     char *start, *end, *ret;
366
367     start = str;
368     while(start && *start && isspace(*start))
369         start++;
370     if(!start || !*start) return FALSE;
371
372     end = start;
373     while(*end && !isspace(*end))
374         end++;
375
376     ret = HeapAlloc( PSDRV_Heap, 0, end - start + 1 );
377     memcpy(ret, start, end - start );
378     *(ret + (end - start)) = '\0';
379
380     while(*end && isspace(*end))
381         end++;
382     if(*end)
383         *next = end;
384     else
385         *next = NULL;
386
387     return ret;
388 }
389
390 /***********************************************************************
391  *
392  *              PSDRV_ParsePPD
393  *
394  *
395  */
396 PPD *PSDRV_ParsePPD(char *fname)
397 {
398     FILE *fp;
399     PPD *ppd;
400     PPDTuple tuple;
401
402     TRACE(psdrv, "%s\n", fname);
403
404     if((fp = fopen(fname, "r")) == NULL) {
405         WARN(psdrv, "Couldn't open ppd file '%s'\n", fname);
406         return NULL;
407     }
408
409     ppd = HeapAlloc( PSDRV_Heap, HEAP_ZERO_MEMORY, sizeof(*ppd));
410     if(!ppd) {
411         ERR(psdrv, "Unable to allocate memory for ppd\n");
412         fclose(fp);
413         return NULL;
414     }
415
416     
417     while( PSDRV_PPDGetNextTuple(fp, &tuple)) {
418
419         if(!strcmp("*NickName", tuple.key)) {
420             ppd->NickName = tuple.value;
421             tuple.value = NULL;
422             TRACE(psdrv, "NickName = '%s'\n", ppd->NickName);
423         }
424
425         else if(!strcmp("*LanguageLevel", tuple.key)) {
426             sscanf(tuple.value, "%d", &(ppd->LanguageLevel));
427             TRACE(psdrv, "LanguageLevel = %d\n", ppd->LanguageLevel);
428         }
429
430         else if(!strcmp("*ColorDevice", tuple.key)) {
431             if(!strcasecmp(tuple.value, "true"))
432                 ppd->ColorDevice = TRUE;
433             TRACE(psdrv, "ColorDevice = %d\n", (int)ppd->ColorDevice);
434         }
435
436         else if((!strcmp("*DefaultResolution", tuple.key)) ||
437                 (!strcmp("*DefaultJCLResolution", tuple.key))) {
438             sscanf(tuple.value, "%d", &(ppd->DefaultResolution));
439             TRACE(psdrv, "DefaultResolution = %d\n", ppd->DefaultResolution);
440         }
441
442         else if(!strcmp("*Font", tuple.key)) {
443             FONTNAME *fn;
444
445             for(fn = ppd->InstalledFonts; fn && fn->next; fn = fn->next)
446                 ;
447             if(!fn) {
448                 ppd->InstalledFonts = HeapAlloc(PSDRV_Heap,
449                                                HEAP_ZERO_MEMORY, sizeof(*fn));
450                 fn = ppd->InstalledFonts;
451             } else {
452                fn->next = HeapAlloc(PSDRV_Heap,
453                                                HEAP_ZERO_MEMORY, sizeof(*fn));
454                fn = fn->next;
455             }
456             fn->Name = tuple.option;
457             tuple.option = NULL;
458         }
459
460         else if(!strcmp("*DefaultFont", tuple.key)) {
461             ppd->DefaultFont = tuple.value;
462             tuple.value = NULL;
463         }
464
465         else if(!strcmp("*JCLBegin", tuple.key)) {
466             ppd->JCLBegin = tuple.value;
467             tuple.value = NULL;
468         }
469
470         else if(!strcmp("*JCLToPSInterpreter", tuple.key)) {
471             ppd->JCLToPSInterpreter = tuple.value;
472             tuple.value = NULL;
473         }
474
475         else if(!strcmp("*JCLEnd", tuple.key)) {
476             ppd->JCLEnd = tuple.value;
477             tuple.value = NULL;
478         }
479
480         else if(!strcmp("*PageSize", tuple.key)) {
481             PAGESIZE *page;
482             page = PSDRV_PPDGetPageSizeInfo(ppd, tuple.option);
483
484             if(!page->Name) {
485                 int i;
486
487                 page->Name = tuple.option;
488                 tuple.option = NULL;
489                 
490                 for(i = 0; PageTrans[i].PSName; i++) {
491                     if(!strcmp(PageTrans[i].PSName, page->Name)) { /* case ? */
492                         page->WinPage = PageTrans[i].WinPage;
493                         break;
494                     }
495                 }
496                 if(!page->WinPage)
497                     FIXME(psdrv, "Can't find Windows page type for '%s'\n",
498                           page->Name);
499             }
500             if(!page->FullName) {
501                 page->FullName = tuple.opttrans;
502                 tuple.opttrans = NULL;
503             }
504             if(!page->InvocationString) {
505                 page->InvocationString = tuple.value;
506                 tuple.value = NULL;
507             }
508         }
509
510         else if(!strcmp("*ImageableArea", tuple.key)) {
511             PAGESIZE *page;
512             page = PSDRV_PPDGetPageSizeInfo(ppd, tuple.option);
513             
514             if(!page->Name) {
515                 page->Name = tuple.option;
516                 tuple.option = NULL;
517             }
518             if(!page->FullName) {
519                 page->FullName = tuple.opttrans;
520                 tuple.opttrans = NULL;
521             }
522
523 #define PIA page->ImageableArea
524             if(!PIA) {
525                 PIA = HeapAlloc( PSDRV_Heap, 0, sizeof(*PIA) );
526                 sscanf(tuple.value, "%f%f%f%f", &PIA->llx, &PIA->lly,
527                                                 &PIA->urx, &PIA->ury);
528             }
529 #undef PIA
530         }
531
532
533         else if(!strcmp("*PaperDimension", tuple.key)) {
534             PAGESIZE *page;
535             page = PSDRV_PPDGetPageSizeInfo(ppd, tuple.option);
536             
537             if(!page->Name) {
538                 page->Name = tuple.option;
539                 tuple.option = NULL;
540             }
541             if(!page->FullName) {
542                 page->FullName = tuple.opttrans;
543                 tuple.opttrans = NULL;
544             }
545
546 #define PD page->PaperDimension
547             if(!PD) {
548                 PD = HeapAlloc( PSDRV_Heap, 0, sizeof(*PD) );
549                 sscanf(tuple.value, "%f%f", &PD->x, &PD->y);
550             }
551 #undef PD
552         }
553
554         else if(!strcmp("*LandscapeOrientation", tuple.key)) {
555             if(!strcmp(tuple.value, "Plus90"))
556                 ppd->LandscapeOrientation = 90;
557             else if(!strcmp(tuple.value, "Minus90"))
558                 ppd->LandscapeOrientation = -90;
559
560             /* anything else, namely 'any', leaves value at 0 */
561
562             TRACE(psdrv, "LandscapeOrientation = %d\n", 
563                   ppd->LandscapeOrientation);
564         }
565         
566         else if(!strcmp("*UIConstraints", tuple.key)) {
567             char *start;
568             CONSTRAINT *con, **insert = &ppd->Constraints;
569
570             while(*insert)
571                 insert = &((*insert)->next);
572             
573             con = *insert = HeapAlloc( PSDRV_Heap, HEAP_ZERO_MEMORY,
574                                        sizeof(*con) );
575
576             start = tuple.value;
577             
578             con->Feature1 = PSDRV_PPDGetWord(start, &start);
579             con->Value1 = PSDRV_PPDGetWord(start, &start);
580             con->Feature2 = PSDRV_PPDGetWord(start, &start);
581             con->Value2 = PSDRV_PPDGetWord(start, &start);
582         }
583             
584         else if(!strcmp("*InputSlot", tuple.key)) {
585             INPUTSLOT *slot, **insert = &ppd->InputSlots;
586             int i;
587
588             while(*insert)
589                 insert = &((*insert)->next);
590
591             slot = *insert = HeapAlloc( PSDRV_Heap, HEAP_ZERO_MEMORY,
592                                     sizeof(*slot) );
593
594             slot->Name = tuple.option;
595             tuple.option = NULL;
596             if(tuple.opttrans) {
597                 slot->FullName = tuple.opttrans;
598                 tuple.opttrans = NULL;
599             }
600             if(tuple.value) {
601                 slot->InvocationString = tuple.value;
602                 tuple.value = NULL;
603             }
604
605             
606             for(i = 0; BinTrans[i].PSName; i++) {
607                 if(!strcmp(BinTrans[i].PSName, slot->Name)) { /* case ? */
608                     slot->WinBin = BinTrans[i].WinBin;
609                     break;
610                 }
611             }
612             if(!slot->WinBin)
613                 FIXME(psdrv, "Can't find Windows bin type for '%s'\n",
614                           slot->Name);
615
616         }   
617
618         if(tuple.key) HeapFree(PSDRV_Heap, 0, tuple.key);
619         if(tuple.option) HeapFree(PSDRV_Heap, 0, tuple.option);
620         if(tuple.value) HeapFree(PSDRV_Heap, 0, tuple.value);
621         if(tuple.opttrans) HeapFree(PSDRV_Heap, 0, tuple.opttrans);
622         if(tuple.valtrans) HeapFree(PSDRV_Heap, 0, tuple.valtrans);    
623         
624     }
625
626
627     {
628         FONTNAME *fn;
629         PAGESIZE *page;
630         CONSTRAINT *con;
631         INPUTSLOT *slot;
632         OPTION *option;
633         OPTIONENTRY *optionEntry;
634
635         for(fn = ppd->InstalledFonts; fn; fn = fn->next)
636             TRACE(psdrv, "'%s'\n", fn->Name);
637         
638         for(page = ppd->PageSizes; page; page = page->next) {
639             TRACE(psdrv, "'%s' aka '%s' (%d) invoked by '%s'\n", page->Name,
640               page->FullName, page->WinPage, page->InvocationString);
641             if(page->ImageableArea)
642                 TRACE(psdrv, "Area = %.2f,%.2f - %.2f, %.2f\n", 
643                       page->ImageableArea->llx, page->ImageableArea->lly,
644                       page->ImageableArea->urx, page->ImageableArea->ury);
645             if(page->PaperDimension)
646                 TRACE(psdrv, "Dimension = %.2f x %.2f\n", 
647                       page->PaperDimension->x, page->PaperDimension->y);
648         }
649
650         for(con = ppd->Constraints; con; con = con->next)
651             TRACE(psdrv, "CONSTRAINTS@ %s %s %s %s\n", con->Feature1,
652                   con->Value1, con->Feature2, con->Value2);
653
654         for(option = ppd->InstalledOptions; option; option = option->next) {
655             TRACE(psdrv, "OPTION: %s %s %s\n", option->OptionName,
656                   option->FullName, option->DefaultOption);
657             for(optionEntry = option->Options; optionEntry;
658                 optionEntry = optionEntry->next)
659                 TRACE(psdrv, "\tOPTIONENTRY: %s %s %s\n", optionEntry->Name,
660                       optionEntry->FullName, optionEntry->InvocationString);
661         }
662
663         for(slot = ppd->InputSlots; slot; slot = slot->next)
664             TRACE(psdrv, "INPUTSLOTS '%s' Name '%s' (%d) Invocation '%s'\n",
665                   slot->Name, slot->FullName, slot->WinBin, 
666                   slot->InvocationString);
667     }
668
669     return ppd;
670 }
671