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