]> www.fi.muni.cz Git - evince.git/blob - ps/ps.c
693cc375cefc4506cf5624111692495d15b3d970
[evince.git] / ps / ps.c
1 /*
2  * ps.c -- Postscript scanning and copying routines.
3  * Copyright (C) 1992, 1998  Timothy O. Theisen
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  *
19  *   Author: Tim Theisen           Systems Programmer
20  * Internet: tim@cs.wisc.edu       Department of Computer Sciences
21  *     UUCP: uwvax!tim             University of Wisconsin-Madison
22  *    Phone: (608)262-0438         1210 West Dayton Street
23  *      FAX: (608)262-9777         Madison, WI   53706
24  */
25
26 /* 18/3/98 Jake Hamby patch */
27
28 /*
29  * 98/03/17: Jake Hamby (jehamby@lightside.com):
30  * Added support for compressed/gzipped Postscript and PDF files.
31  * Compressed files are gunzipped to a temporary file, and PDF files are
32  * scanned by calling Ghostscript to generate a fake DSC file.
33  * This is based on code from GV 3.5.8, which is available at:
34  *    http://wwwthep.physik.uni-mainz.de/~plass/gv/
35  */
36
37 /* GV by        Johannes Plass
38  *                      Department of Physics
39  *                      Johannes Gutenberg University
40  *                      Mainz, Germany
41  *              
42  *                      <plass@thep.physik.uni-mainz.de>
43  */
44
45 /* end of patch */
46
47 #include <stdlib.h>
48 #include <stdio.h>
49 #ifndef SEEK_SET
50 #   define SEEK_SET 0
51 #endif
52 #ifndef BUFSIZ
53 #   define BUFSIZ 1024
54 #endif
55 #include <ctype.h>
56 #include <X11/Xos.h>            /* #includes the appropriate <string.h> */
57 #include "gstypes.h"
58 #include "gsdefaults.h"
59 #include "ps.h"
60 #include "gsio.h"
61
62 #include <glib.h>
63
64 /* length calculates string length at compile time */
65 /* can only be used with character constants */
66 #define length(a) (sizeof(a)-1)
67 #define iscomment(a, b) (strncmp(a, b, length(b)) == 0)
68 #define DSCcomment(a) (a[0] == '%' && a[1] == '%')
69
70     /* list of standard paper sizes from Adobe's PPD. */
71
72 /*--------------------------------------------------*/
73 /* Declarations for ps_io_*() routines. */
74
75 typedef struct FileDataStruct_ *FileData;
76
77 typedef struct FileDataStruct_ {
78    FILE *file;           /* file */
79    int   file_desc;      /* file descriptor corresponding to file */
80    int   filepos;        /* file position corresponding to the start of the line */
81    char *buf;            /* buffer */
82    int   buf_size;       /* size of buffer */
83    int   buf_end;        /* last char in buffer given as offset to buf */
84    int   line_begin;     /* start of the line given as offset to buf */
85    int   line_end;       /* end of the line given as offset to buf */
86    int   line_len;       /* length of line, i.e. (line_end-line_begin) */
87    char  line_termchar;  /* char exchanged for a '\0' at end of line */
88    int   status;         /* 0 = okay, 1 = failed */
89 } FileDataStruct;
90
91 static FileData ps_io_init (FILE *file);
92 static void     ps_io_exit (FileData data);
93 static char    *ps_io_fgetchars (FileData data, int offset);
94
95 static char    *skipped_line = "% ps_io_fgetchars: skipped line";
96 static char    *empty_string = "";
97
98 static char *readline (FileData fd, char **lineP, long *positionP, unsigned int *line_lenP);
99 static char *gettextline(char *line);
100 static char *get_next_text(char *line, char **next_char);
101 static int blank(char *line);
102
103 static struct page *
104 pages_new(struct page *pages, int current, int maxpages)
105 {
106   struct page *oldpages = pages;
107   if(!oldpages)
108     pages = g_new0(struct page, maxpages);
109   else
110     pages = g_renew(struct page, oldpages, maxpages);
111   for(; current < maxpages; current++) {
112     memset(&(pages[current]), 0x00, sizeof(struct page));
113     pages[current].orientation = GTK_GS_ORIENTATION_NONE;
114   }
115   return pages;
116 }
117
118 /*
119  *      psscan -- scan the PostScript file for document structuring comments.
120  *
121  *      This scanner is designed to retrieve the information necessary for
122  *      the ghostview previewer.  It will scan files that conform to any
123  *      version (1.0, 2.0, 2.1, or 3.0) of the document structuring conventions.
124  *      It does not really care which version of comments the file contains.
125  *      (The comments are largely upward compatible.)  It will scan a number
126  *      of non-conforming documents.  (You could have part of the document
127  *      conform to V2.0 and the rest conform to V3.0.  It would be similar
128  *      to the DC-2 1/2+, it would look funny but it can still fly.)
129  *
130  *      This routine returns a pointer to the document structure.
131  *      The structure contains the information relevant to previewing.
132  *      These include EPSF flag (to tell if the file is a encapsulated figure),
133  *      Page Size (for the Page Size), Bounding Box (to minimize backing
134  *      pixmap size or determine window size for encapsulated PostScript), 
135  *      Orientation of Paper (for default transformation matrix), and
136  *      Page Order.  The title and CreationDate are also retrieved to
137  *      help identify the document.
138  *
139  *      The following comments are examined:
140  *
141  *      Header section: 
142  *      Must start with %!PS-Adobe-.  Version numbers ignored.
143  *      Also allowed to be just %!PS, many files seem to have that.
144  *
145  *      %!PS-Adobe-* [EPSF-*]
146  *      %%BoundingBox: <int> <int> <int> <int>|(atend)
147  *      %%CreationDate: <textline>
148  *      %%Orientation: Portrait|Landscape|(atend)
149  *      %%Pages: <uint> [<int>]|(atend)
150  *      %%PageOrder: Ascend|Descend|Special|(atend)
151  *      %%Title: <textline>
152  *      %%DocumentMedia: <text> <real> <real> <real> <text> <text>
153  *      %%DocumentPaperSizes: <text>
154  *      %%EndComments
155  *
156  *      Note: Either the 3.0 or 2.0 syntax for %%Pages is accepted.
157  *            Also either the 2.0 %%DocumentPaperSizes or the 3.0
158  *            %%DocumentMedia comments are accepted as well.
159  *
160  *      The header section ends either explicitly with %%EndComments or
161  *      implicitly with any line that does not begin with %X where X is
162  *      a not whitespace character.
163  *
164  *      If the file is encapsulated PostScript the optional Preview section
165  *      is next:
166  *
167  *      %%BeginPreview
168  *      %%EndPreview
169  *
170  *      This section explicitly begins and ends with the above comments.
171  *
172  *      Next the Defaults section for version 3 page defaults:
173  *
174  *      %%BeginDefaults
175  *      %%PageBoundingBox: <int> <int> <int> <int>
176  *      %%PageOrientation: Portrait|Landscape
177  *      %%PageMedia: <text>
178  *      %%EndDefaults
179  *
180  *      This section explicitly begins and ends with the above comments.
181  *
182  *      The prolog section either explicitly starts with %%BeginProlog or
183  *      implicitly with any nonblank line.
184  *
185  *      %%BeginProlog
186  *      %%EndProlog
187  *
188  *      The Prolog should end with %%EndProlog, however the proglog implicitly
189  *      ends when %%BeginSetup, %%Page, %%Trailer or %%EOF are encountered.
190  *
191  *      The Setup section is where the version 2 page defaults are found.
192  *      This section either explicitly begins with %%BeginSetup or implicitly
193  *      with any nonblank line after the Prolog.
194  *
195  *      %%BeginSetup
196  *      %%PageBoundingBox: <int> <int> <int> <int>
197  *      %%PageOrientation: Portrait|Landscape
198  *      %%PaperSize: <text>
199  *      %%EndSetup
200  *
201  *      The Setup should end with %%EndSetup, however the setup implicitly
202  *      ends when %%Page, %%Trailer or %%EOF are encountered.
203  *
204  *      Next each page starts explicitly with %%Page and ends implicitly with
205  *      %%Page or %%Trailer or %%EOF.  The following comments are recognized:
206  *
207  *      %%Page: <text> <uint>
208  *      %%PageBoundingBox: <int> <int> <int> <int>|(atend)
209  *      %%PageOrientation: Portrait|Landscape
210  *      %%PageMedia: <text>
211  *      %%PaperSize: <text>
212  *
213  *      The tralier section start explicitly with %%Trailer and end with %%EOF.
214  *      The following comment are examined with the proper (atend) notation
215  *      was used in the header:
216  *
217  *      %%Trailer
218  *      %%BoundingBox: <int> <int> <int> <int>|(atend)
219  *      %%Orientation: Portrait|Landscape|(atend)
220  *      %%Pages: <uint> [<int>]|(atend)
221  *      %%PageOrder: Ascend|Descend|Special|(atend)
222  *      %%EOF
223  *
224  *
225  *  + A DC-3 received severe damage to one of its wings.  The wing was a total
226  *    loss.  There was no replacement readily available, so the mechanic
227  *    installed a wing from a DC-2.
228  */
229
230 #include <glib.h>
231
232 struct document *
233 psscan(FILE * file, int respect_eof, const gchar * fname)
234 {
235   struct document *doc;
236   int bb_set = NONE;
237   int pages_set = NONE;
238   int page_order_set = NONE;
239   int orientation_set = NONE;
240   int page_bb_set = NONE;
241   int page_size_set = NONE;
242   int preread;                  /* flag which tells the readline isn't needed */
243   int i;
244   unsigned int maxpages = 0;
245   unsigned int nextpage = 1;    /* Next expected page */
246   unsigned int thispage;
247   int ignore = 0;               /* whether to ignore page ordinals */
248   char *label;
249   char *line;
250   char text[PSLINELENGTH];      /* Temporary storage for text */
251   long position;                /* Position of the current line */
252   long beginsection;            /* Position of the beginning of the section */
253   unsigned int line_len;        /* Length of the current line */
254   unsigned int section_len;     /* Place to accumulate the section length */
255   char *next_char;              /* 1st char after text returned by get_next_text() */
256   char *cp;
257   GtkGSPaperSize *dmp;
258   GtkGSPaperSize *papersizes = gtk_gs_defaults_get_paper_sizes();
259   FileData fd;
260
261   if(!file)
262     return NULL;
263
264   rewind(file);
265
266   fd =  ps_io_init(file);
267   if (!readline(fd, &line, &position, &line_len)) {
268     fprintf(stderr, "Warning: empty file.\n");
269     ps_io_exit(fd);
270     return(NULL);
271   }
272
273   /* HP printer job language data follows. Some printer drivers add pjl
274    * commands to switch a pjl printer to postscript mode. If no PS header
275    * follows, this seems to be a real pjl file. */
276   if(iscomment(line, "\033%-12345X@PJL")) {
277     /* read until first DSC comment */
278     while(readline(fd, &line, &position, &line_len)
279           && (line[0] != '%')) ;
280     if(line[0] != '%') {
281       g_print("psscan error: input files seems to be a PJL file.\n");
282       return (NULL);
283     }
284   }
285
286   /* Header comments */
287
288   /* Header should start with "%!PS-Adobe-", but some programms omit
289    * parts of this or add a ^D at the beginning. */
290   if(iscomment(line, "%!PS") || iscomment(line, "\004%!PS")) {
291     doc = g_new0(struct document, 1);
292     doc->default_page_orientation = GTK_GS_ORIENTATION_NONE;
293     doc->orientation = GTK_GS_ORIENTATION_NONE;
294
295     /* ignore possible leading ^D */
296     if(*line == '\004') {
297       position++;
298       line_len--;
299     }
300
301 /* Jake Hamby patch 18/3/98 */
302
303     text[0] = '\0';
304     sscanf(line, "%*s %256s", text);
305     /*doc->epsf = iscomment(text, "EPSF-"); */
306     doc->epsf = iscomment(text, "EPSF");    /* Hamby - This line changed */
307     doc->beginheader = position;
308     section_len = line_len;
309   }
310   else {
311     /* There are postscript documents that do not have
312        %PS at the beginning, usually unstructured. We should GS decide
313        For instance, the tech reports at this university:
314
315        http://svrc.it.uq.edu.au/Bibliography/svrc-tr.html?94-45
316
317        add ugly PostScript before the actual document. 
318
319        GS and gv is
320        able to display them correctly as unstructured PS.
321
322        In a way, this makes sense, a program PostScript does not need
323        the !PS at the beginning.
324      */
325     /* use a test command to determine if ghostscript can
326        understand this document! */
327     gchar *test_cmd;
328
329     test_cmd = g_strdup_printf
330       ("%s -dNOPAUSE -dBATCH -sDEVICE=nullpage %s "
331        "1>/dev/null 2>/dev/null", gtk_gs_defaults_get_interpreter_cmd(), fname);
332     if(system(test_cmd) != 0) {
333       g_free(test_cmd);
334       return NULL;
335     }
336     g_free(test_cmd);
337     doc = g_new0(struct document, 1);
338     doc->default_page_orientation = GTK_GS_ORIENTATION_NONE;
339     doc->orientation = GTK_GS_ORIENTATION_NONE;
340     return (doc);
341   }
342
343   preread = 0;
344   while(preread || readline(fd, &line, &position, &line_len)) {
345     if(!preread)
346       section_len += line_len;
347     preread = 0;
348     if(line[0] != '%' ||
349        iscomment(line + 1, "%EndComments") ||
350        line[1] == ' ' || line[1] == '\t' || line[1] == '\n' ||
351        !isprint(line[1])) {
352       break;
353     }
354     else if(line[1] != '%') {
355       /* Do nothing */
356     }
357     else if(doc->title == NULL && iscomment(line + 2, "Title:")) {
358       doc->title = gettextline(line + length("%%Title:"));
359     }
360     else if(doc->date == NULL && iscomment(line + 2, "CreationDate:")) {
361       doc->date = gettextline(line + length("%%CreationDate:"));
362     }
363     else if(bb_set == NONE && iscomment(line + 2, "BoundingBox:")) {
364       sscanf(line + length("%%BoundingBox:"), "%256s", text);
365       if(strcmp(text, "(atend)") == 0) {
366         bb_set = ATEND;
367       }
368       else {
369         if(sscanf(line + length("%%BoundingBox:"), "%d %d %d %d",
370                   &(doc->boundingbox[LLX]),
371                   &(doc->boundingbox[LLY]),
372                   &(doc->boundingbox[URX]), &(doc->boundingbox[URY])) == 4)
373           bb_set = 1;
374         else {
375           float fllx, flly, furx, fury;
376           if(sscanf(line + length("%%BoundingBox:"), "%f %f %f %f",
377                     &fllx, &flly, &furx, &fury) == 4) {
378             bb_set = 1;
379             doc->boundingbox[LLX] = fllx;
380             doc->boundingbox[LLY] = flly;
381             doc->boundingbox[URX] = furx;
382             doc->boundingbox[URY] = fury;
383             if(fllx < doc->boundingbox[LLX])
384               doc->boundingbox[LLX]--;
385             if(flly < doc->boundingbox[LLY])
386               doc->boundingbox[LLY]--;
387             if(furx > doc->boundingbox[URX])
388               doc->boundingbox[URX]++;
389             if(fury > doc->boundingbox[URY])
390               doc->boundingbox[URY]++;
391           }
392         }
393       }
394     }
395     else if(orientation_set == NONE && iscomment(line + 2, "Orientation:")) {
396       sscanf(line + length("%%Orientation:"), "%256s", text);
397       if(strcmp(text, "(atend)") == 0) {
398         orientation_set = ATEND;
399       }
400       else if(strcmp(text, "Portrait") == 0) {
401         doc->orientation = GTK_GS_ORIENTATION_PORTRAIT;
402         orientation_set = 1;
403       }
404       else if(strcmp(text, "Landscape") == 0) {
405         doc->orientation = GTK_GS_ORIENTATION_LANDSCAPE;
406         orientation_set = 1;
407       }
408       else if(strcmp(text, "Seascape") == 0) {
409         doc->orientation = GTK_GS_ORIENTATION_SEASCAPE;
410         orientation_set = 1;
411       }
412     }
413     else if(page_order_set == NONE && iscomment(line + 2, "PageOrder:")) {
414       sscanf(line + length("%%PageOrder:"), "%256s", text);
415       if(strcmp(text, "(atend)") == 0) {
416         page_order_set = ATEND;
417       }
418       else if(strcmp(text, "Ascend") == 0) {
419         doc->pageorder = ASCEND;
420         page_order_set = 1;
421       }
422       else if(strcmp(text, "Descend") == 0) {
423         doc->pageorder = DESCEND;
424         page_order_set = 1;
425       }
426       else if(strcmp(text, "Special") == 0) {
427         doc->pageorder = SPECIAL;
428         page_order_set = 1;
429       }
430     }
431     else if(pages_set == NONE && iscomment(line + 2, "Pages:")) {
432       sscanf(line + length("%%Pages:"), "%256s", text);
433       if(strcmp(text, "(atend)") == 0) {
434         pages_set = ATEND;
435       }
436       else {
437         switch (sscanf(line + length("%%Pages:"), "%d %d", &maxpages, &i)) {
438         case 2:
439           if(page_order_set == NONE) {
440             if(i == -1) {
441               doc->pageorder = DESCEND;
442               page_order_set = 1;
443             }
444             else if(i == 0) {
445               doc->pageorder = SPECIAL;
446               page_order_set = 1;
447             }
448             else if(i == 1) {
449               doc->pageorder = ASCEND;
450               page_order_set = 1;
451             }
452           }
453         case 1:
454           if(maxpages > 0)
455             doc->pages = pages_new(NULL, 0, maxpages);
456         }
457       }
458     }
459     else if(doc->numsizes == NONE && iscomment(line + 2, "DocumentMedia:")) {
460       float w, h;
461       doc->size = g_new0(GtkGSPaperSize, 1);
462       doc->size[0].name =
463         get_next_text(line + length("%%DocumentMedia:"), &next_char);
464       if(doc->size[0].name != NULL) {
465         if(sscanf(next_char, "%f %f", &w, &h) == 2) {
466           doc->size[0].width = w + 0.5;
467           doc->size[0].height = h + 0.5;
468         }
469         if(doc->size[0].width != 0 && doc->size[0].height != 0)
470           doc->numsizes = 1;
471         else
472           g_free(doc->size[0].name);
473       }
474       preread = 1;
475       while(readline(fd, &line, &position, &line_len) &&
476             DSCcomment(line) && iscomment(line + 2, "+")) {
477         section_len += line_len;
478         doc->size = g_renew(GtkGSPaperSize, doc->size, doc->numsizes + 1);
479         doc->size[doc->numsizes].name =
480           get_next_text(line + length("%%+"), &next_char);
481         if(doc->size[doc->numsizes].name != NULL) {
482           if(sscanf(next_char, "%f %f", &w, &h) == 2) {
483             doc->size[doc->numsizes].width = w + 0.5;
484             doc->size[doc->numsizes].height = h + 0.5;
485           }
486           if(doc->size[doc->numsizes].width != 0 &&
487              doc->size[doc->numsizes].height != 0)
488             doc->numsizes++;
489           else
490             g_free(doc->size[doc->numsizes].name);
491         }
492       }
493       section_len += line_len;
494       if(doc->numsizes != 0)
495         doc->default_page_size = doc->size;
496     }
497     else if(doc->numsizes == NONE && iscomment(line + 2, "DocumentPaperSizes:")) {
498
499       doc->size = g_new0(GtkGSPaperSize, 1);
500       doc->size[0].name =
501         get_next_text(line + length("%%DocumentPaperSizes:"), &next_char);
502       if(doc->size[0].name != NULL) {
503         doc->size[0].width = 0;
504         doc->size[0].height = 0;
505         for(dmp = papersizes; dmp->name != NULL; dmp++) {
506           /* Note: Paper size comment uses down cased paper size
507            * name.  Case insensitive compares are only used for
508            * PaperSize comments.
509            */
510           if(strcasecmp(doc->size[0].name, dmp->name) == 0) {
511             g_free(doc->size[0].name);
512             doc->size[0].name = g_strdup(dmp->name);
513             doc->size[0].width = dmp->width;
514             doc->size[0].height = dmp->height;
515             break;
516           }
517         }
518         if(doc->size[0].width != 0 && doc->size[0].height != 0)
519           doc->numsizes = 1;
520         else
521           g_free(doc->size[0].name);
522       }
523       while((cp = get_next_text(next_char, &next_char))) {
524         doc->size = g_renew(GtkGSPaperSize, doc->size, doc->numsizes + 1);
525         doc->size[doc->numsizes].name = cp;
526         doc->size[doc->numsizes].width = 0;
527         doc->size[doc->numsizes].height = 0;
528         for(dmp = papersizes; dmp->name != NULL; dmp++) {
529           /* Note: Paper size comment uses down cased paper size
530            * name.  Case insensitive compares are only used for
531            * PaperSize comments.
532            */
533           if(strcasecmp(doc->size[doc->numsizes].name, dmp->name) == 0) {
534             g_free(doc->size[doc->numsizes].name);
535             doc->size[doc->numsizes].name = g_strdup(dmp->name);
536             doc->size[doc->numsizes].name = dmp->name;
537             doc->size[doc->numsizes].width = dmp->width;
538             doc->size[doc->numsizes].height = dmp->height;
539             break;
540           }
541         }
542         if(doc->size[doc->numsizes].width != 0 &&
543            doc->size[doc->numsizes].height != 0)
544           doc->numsizes++;
545         else
546           g_free(doc->size[doc->numsizes].name);
547       }
548       preread = 1;
549       while(readline(fd, &line, &position, &line_len) &&
550             DSCcomment(line) && iscomment(line + 2, "+")) {
551         section_len += line_len;
552         next_char = line + length("%%+");
553         while((cp = get_next_text(next_char, &next_char))) {
554           doc->size = g_renew(GtkGSPaperSize, doc->size, doc->numsizes + 1);
555           doc->size[doc->numsizes].name = cp;
556           doc->size[doc->numsizes].width = 0;
557           doc->size[doc->numsizes].height = 0;
558           for(dmp = papersizes; dmp->name != NULL; dmp++) {
559             /* Note: Paper size comment uses down cased paper size
560              * name.  Case insensitive compares are only used for
561              * PaperSize comments.
562              */
563             if(strcasecmp(doc->size[doc->numsizes].name, dmp->name) == 0) {
564               doc->size[doc->numsizes].width = dmp->width;
565               doc->size[doc->numsizes].height = dmp->height;
566               break;
567             }
568           }
569           if(doc->size[doc->numsizes].width != 0 &&
570              doc->size[doc->numsizes].height != 0)
571             doc->numsizes++;
572           else
573             g_free(doc->size[doc->numsizes].name);
574         }
575       }
576       section_len += line_len;
577       if(doc->numsizes != 0)
578         doc->default_page_size = doc->size;
579     }
580   }
581
582   if(DSCcomment(line) && iscomment(line + 2, "EndComments")) {
583     readline(fd, &line, &position, &line_len);
584     section_len += line_len;
585   }
586   doc->endheader = position;
587   doc->lenheader = section_len - line_len;
588
589   /* Optional Preview comments for encapsulated PostScript files */
590
591   beginsection = position;
592   section_len = line_len;
593   while(blank(line) && readline(fd, &line, &position, &line_len)) {
594     section_len += line_len;
595   }
596
597   if(doc->epsf && DSCcomment(line) && iscomment(line + 2, "BeginPreview")) {
598     doc->beginpreview = beginsection;
599     beginsection = 0;
600     while(readline(fd, &line, &position, &line_len) &&
601           !(DSCcomment(line) && iscomment(line + 2, "EndPreview"))) {
602       section_len += line_len;
603     }
604     section_len += line_len;
605     readline(fd, &line, &position, &line_len);
606     section_len += line_len;
607     doc->endpreview = position;
608     doc->lenpreview = section_len - line_len;
609   }
610
611   /* Page Defaults for Version 3.0 files */
612
613   if(beginsection == 0) {
614     beginsection = position;
615     section_len = line_len;
616   }
617   while(blank(line) && readline(fd, &line, &position, &line_len)) {
618     section_len += line_len;
619   }
620
621   if(DSCcomment(line) && iscomment(line + 2, "BeginDefaults")) {
622     doc->begindefaults = beginsection;
623     beginsection = 0;
624     while(readline(fd, &line, &position, &line_len) &&
625           !(DSCcomment(line) && iscomment(line + 2, "EndDefaults"))) {
626       section_len += line_len;
627       if(!DSCcomment(line)) {
628         /* Do nothing */
629       }
630       else if(doc->default_page_orientation == NONE &&
631               iscomment(line + 2, "PageOrientation:")) {
632         sscanf(line + length("%%PageOrientation:"), "%256s", text);
633         if(strcmp(text, "Portrait") == 0) {
634           doc->default_page_orientation = GTK_GS_ORIENTATION_PORTRAIT;
635         }
636         else if(strcmp(text, "Landscape") == 0) {
637           doc->default_page_orientation = GTK_GS_ORIENTATION_LANDSCAPE;
638         }
639         else if(strcmp(text, "Seascape") == 0) {
640           doc->default_page_orientation = GTK_GS_ORIENTATION_SEASCAPE;
641         }
642       }
643       else if(page_size_set == NONE && iscomment(line + 2, "PageMedia:")) {
644         cp = get_next_text(line + length("%%PageMedia:"), NULL);
645         for(dmp = doc->size, i = 0; i < doc->numsizes; i++, dmp++) {
646           if(strcmp(cp, dmp->name) == 0) {
647             doc->default_page_size = dmp;
648             page_size_set = 1;
649             break;
650           }
651         }
652         g_free(cp);
653       }
654       else if(page_bb_set == NONE && iscomment(line + 2, "PageBoundingBox:")) {
655         if(sscanf(line + length("%%PageBoundingBox:"), "%d %d %d %d",
656                   &(doc->default_page_boundingbox[LLX]),
657                   &(doc->default_page_boundingbox[LLY]),
658                   &(doc->default_page_boundingbox[URX]),
659                   &(doc->default_page_boundingbox[URY])) == 4)
660           page_bb_set = 1;
661         else {
662           float fllx, flly, furx, fury;
663           if(sscanf
664              (line + length("%%PageBoundingBox:"), "%f %f %f %f",
665               &fllx, &flly, &furx, &fury) == 4) {
666             page_bb_set = 1;
667             doc->default_page_boundingbox[LLX] = fllx;
668             doc->default_page_boundingbox[LLY] = flly;
669             doc->default_page_boundingbox[URX] = furx;
670             doc->default_page_boundingbox[URY] = fury;
671             if(fllx < doc->default_page_boundingbox[LLX])
672               doc->default_page_boundingbox[LLX]--;
673             if(flly < doc->default_page_boundingbox[LLY])
674               doc->default_page_boundingbox[LLY]--;
675             if(furx > doc->default_page_boundingbox[URX])
676               doc->default_page_boundingbox[URX]++;
677             if(fury > doc->default_page_boundingbox[URY])
678               doc->default_page_boundingbox[URY]++;
679           }
680         }
681       }
682     }
683     section_len += line_len;
684     readline(fd, &line, &position, &line_len);
685     section_len += line_len;
686     doc->enddefaults = position;
687     doc->lendefaults = section_len - line_len;
688   }
689
690   /* Document Prolog */
691
692   if(beginsection == 0) {
693     beginsection = position;
694     section_len = line_len;
695   }
696   while(blank(line) && readline(fd, &line, &position, &line_len)) {
697     section_len += line_len;
698   }
699
700   if(!(DSCcomment(line) &&
701        (iscomment(line + 2, "BeginSetup") ||
702         iscomment(line + 2, "Page:") ||
703         iscomment(line + 2, "Trailer") || iscomment(line + 2, "EOF")))) {
704     doc->beginprolog = beginsection;
705     beginsection = 0;
706     preread = 1;
707
708     while((preread ||
709            readline(fd, &line, &position, &line_len)) &&
710           !(DSCcomment(line) &&
711             (iscomment(line + 2, "EndProlog") ||
712              iscomment(line + 2, "BeginSetup") ||
713              iscomment(line + 2, "Page:") ||
714              iscomment(line + 2, "Trailer") || iscomment(line + 2, "EOF")))) {
715       if(!preread)
716         section_len += line_len;
717       preread = 0;
718     }
719     section_len += line_len;
720     if(DSCcomment(line) && iscomment(line + 2, "EndProlog")) {
721       readline(fd, &line, &position, &line_len);
722       section_len += line_len;
723     }
724     doc->endprolog = position;
725     doc->lenprolog = section_len - line_len;
726   }
727
728   /* Document Setup,  Page Defaults found here for Version 2 files */
729
730   if(beginsection == 0) {
731     beginsection = position;
732     section_len = line_len;
733   }
734   while(blank(line) && readline(fd, &line, &position, &line_len)) {
735     section_len += line_len;
736   }
737
738   if(!(DSCcomment(line) &&
739        (iscomment(line + 2, "Page:") ||
740         iscomment(line + 2, "Trailer") ||
741         (respect_eof && iscomment(line + 2, "EOF"))))) {
742     doc->beginsetup = beginsection;
743     beginsection = 0;
744     preread = 1;
745     while((preread ||
746            readline(fd, &line, &position, &line_len)) &&
747           !(DSCcomment(line) &&
748             (iscomment(line + 2, "EndSetup") ||
749              iscomment(line + 2, "Page:") ||
750              iscomment(line + 2, "Trailer") ||
751              (respect_eof && iscomment(line + 2, "EOF"))))) {
752       if(!preread)
753         section_len += line_len;
754       preread = 0;
755       if(!DSCcomment(line)) {
756         /* Do nothing */
757       }
758       else if(doc->default_page_orientation == NONE &&
759               iscomment(line + 2, "PageOrientation:")) {
760         sscanf(line + length("%%PageOrientation:"), "%256s", text);
761         if(strcmp(text, "Portrait") == 0) {
762           doc->default_page_orientation = GTK_GS_ORIENTATION_PORTRAIT;
763         }
764         else if(strcmp(text, "Landscape") == 0) {
765           doc->default_page_orientation = GTK_GS_ORIENTATION_LANDSCAPE;
766         }
767         else if(strcmp(text, "Seascape") == 0) {
768           doc->default_page_orientation = GTK_GS_ORIENTATION_SEASCAPE;
769         }
770       }
771       else if(page_size_set == NONE && iscomment(line + 2, "PaperSize:")) {
772         cp = get_next_text(line + length("%%PaperSize:"), NULL);
773         for(dmp = doc->size, i = 0; i < doc->numsizes; i++, dmp++) {
774           /* Note: Paper size comment uses down cased paper size
775            * name.  Case insensitive compares are only used for
776            * PaperSize comments.
777            */
778           if(strcasecmp(cp, dmp->name) == 0) {
779             doc->default_page_size = dmp;
780             page_size_set = 1;
781             break;
782           }
783         }
784         g_free(cp);
785       }
786       else if(page_bb_set == NONE && iscomment(line + 2, "PageBoundingBox:")) {
787         if(sscanf(line + length("%%PageBoundingBox:"), "%d %d %d %d",
788                   &(doc->default_page_boundingbox[LLX]),
789                   &(doc->default_page_boundingbox[LLY]),
790                   &(doc->default_page_boundingbox[URX]),
791                   &(doc->default_page_boundingbox[URY])) == 4)
792           page_bb_set = 1;
793         else {
794           float fllx, flly, furx, fury;
795           if(sscanf
796              (line + length("%%PageBoundingBox:"), "%f %f %f %f",
797               &fllx, &flly, &furx, &fury) == 4) {
798             page_bb_set = 1;
799             doc->default_page_boundingbox[LLX] = fllx;
800             doc->default_page_boundingbox[LLY] = flly;
801             doc->default_page_boundingbox[URX] = furx;
802             doc->default_page_boundingbox[URY] = fury;
803             if(fllx < doc->default_page_boundingbox[LLX])
804               doc->default_page_boundingbox[LLX]--;
805             if(flly < doc->default_page_boundingbox[LLY])
806               doc->default_page_boundingbox[LLY]--;
807             if(furx > doc->default_page_boundingbox[URX])
808               doc->default_page_boundingbox[URX]++;
809             if(fury > doc->default_page_boundingbox[URY])
810               doc->default_page_boundingbox[URY]++;
811           }
812         }
813       }
814     }
815     section_len += line_len;
816     if(DSCcomment(line) && iscomment(line + 2, "EndSetup")) {
817       readline(fd, &line, &position, &line_len);
818       section_len += line_len;
819     }
820     doc->endsetup = position;
821     doc->lensetup = section_len - line_len;
822   }
823
824   /* Added this (Nov. 2, 1999) when I noticed that
825      a Postscript file would load in gv but not in ggv
826
827      dmg@csg.uwaterloo.ca */
828
829   /* BEGIN Windows NT fix ###jp###
830      Mark Pfeifer (pfeiferm%ppddev@comet.cmis.abbott.com) told me
831      about problems when viewing Windows NT 3.51 generated postscript
832      files with gv. He found that the relevant postscript files
833      show important postscript code after the '%%EndSetup' and before
834      the first page comment '%%Page: x y'.
835    */
836   if(doc->beginsetup) {
837     while(!(DSCcomment(line) &&
838             (iscomment(line + 2, "EndSetup") ||
839              (iscomment(line + 2, "Page:") ||
840               iscomment(line + 2, "Trailer") ||
841               (respect_eof && iscomment(line + 2, "EOF"))))) &&
842           (readline(fd, &line, &position, &line_len))) {
843       section_len += line_len;
844       doc->lensetup = section_len - line_len;
845       doc->endsetup = position;
846     }
847   }
848   /* END Windows NT fix ###jp## */
849
850   /* Individual Pages */
851
852   if(beginsection == 0) {
853     beginsection = position;
854     section_len = line_len;
855   }
856   while(blank(line) && readline(fd, &line, &position, &line_len)) {
857     section_len += line_len;
858   }
859
860
861 newpage:
862   while(DSCcomment(line) && iscomment(line + 2, "Page:")) {
863     if(maxpages == 0) {
864       maxpages = 1;
865       doc->pages = pages_new(NULL, 0, maxpages);
866     }
867     label = get_next_text(line + length("%%Page:"), &next_char);
868     if(sscanf(next_char, "%d", &thispage) != 1)
869       thispage = 0;
870     if(nextpage == 1) {
871       ignore = thispage != 1;
872     }
873     if(!ignore && thispage != nextpage) {
874       g_free(label);
875       doc->numpages--;
876       goto continuepage;
877     }
878     nextpage++;
879     if(doc->numpages == maxpages) {
880       maxpages++;
881       doc->pages = pages_new(doc->pages, maxpages - 1, maxpages);
882     }
883     page_bb_set = NONE;
884     doc->pages[doc->numpages].label = label;
885     if(beginsection) {
886       doc->pages[doc->numpages].begin = beginsection;
887       beginsection = 0;
888     }
889     else {
890       doc->pages[doc->numpages].begin = position;
891       section_len = line_len;
892     }
893   continuepage:
894     while(readline(fd, &line, &position, &line_len) &&
895           !(DSCcomment(line) &&
896             (iscomment(line + 2, "Page:") ||
897              iscomment(line + 2, "Trailer") ||
898              (respect_eof && iscomment(line + 2, "EOF"))))) {
899       section_len += line_len;
900       if(!DSCcomment(line)) {
901         /* Do nothing */
902       }
903       else if(doc->pages[doc->numpages].orientation == NONE &&
904               iscomment(line + 2, "PageOrientation:")) {
905         sscanf(line + length("%%PageOrientation:"), "%256s", text);
906         if(strcmp(text, "Portrait") == 0) {
907           doc->pages[doc->numpages].orientation = GTK_GS_ORIENTATION_PORTRAIT;
908         }
909         else if(strcmp(text, "Landscape") == 0) {
910           doc->pages[doc->numpages].orientation = GTK_GS_ORIENTATION_LANDSCAPE;
911         }
912         else if(strcmp(text, "Seascape") == 0) {
913           doc->pages[doc->numpages].orientation = GTK_GS_ORIENTATION_SEASCAPE;
914         }
915       }
916       else if(doc->pages[doc->numpages].size == NULL &&
917               iscomment(line + 2, "PageMedia:")) {
918         cp = get_next_text(line + length("%%PageMedia:"), NULL);
919         for(dmp = doc->size, i = 0; i < doc->numsizes; i++, dmp++) {
920           if(strcmp(cp, dmp->name) == 0) {
921             doc->pages[doc->numpages].size = dmp;
922             break;
923           }
924         }
925         g_free(cp);
926       }
927       else if(doc->pages[doc->numpages].size == NULL &&
928               iscomment(line + 2, "PaperSize:")) {
929         cp = get_next_text(line + length("%%PaperSize:"), NULL);
930         for(dmp = doc->size, i = 0; i < doc->numsizes; i++, dmp++) {
931           /* Note: Paper size comment uses down cased paper size
932            * name.  Case insensitive compares are only used for
933            * PaperSize comments.
934            */
935           if(strcasecmp(cp, dmp->name) == 0) {
936             doc->pages[doc->numpages].size = dmp;
937             break;
938           }
939         }
940         g_free(cp);
941       }
942       else if((page_bb_set == NONE || page_bb_set == ATEND) &&
943               iscomment(line + 2, "PageBoundingBox:")) {
944         sscanf(line + length("%%PageBoundingBox:"), "%256s", text);
945         if(strcmp(text, "(atend)") == 0) {
946           page_bb_set = ATEND;
947         }
948         else {
949           if(sscanf
950              (line + length("%%PageBoundingBox:"), "%d %d %d %d",
951               &(doc->pages[doc->numpages].boundingbox[LLX]),
952               &(doc->pages[doc->numpages].boundingbox[LLY]),
953               &(doc->pages[doc->numpages].boundingbox[URX]),
954               &(doc->pages[doc->numpages].boundingbox[URY])) == 4) {
955             if(page_bb_set == NONE)
956               page_bb_set = 1;
957           }
958           else {
959             float fllx, flly, furx, fury;
960             if(sscanf(line + length("%%PageBoundingBox:"),
961                       "%f %f %f %f", &fllx, &flly, &furx, &fury) == 4) {
962               if(page_bb_set == NONE)
963                 page_bb_set = 1;
964               doc->pages[doc->numpages].boundingbox[LLX] = fllx;
965               doc->pages[doc->numpages].boundingbox[LLY] = flly;
966               doc->pages[doc->numpages].boundingbox[URX] = furx;
967               doc->pages[doc->numpages].boundingbox[URY] = fury;
968               if(fllx < doc->pages[doc->numpages].boundingbox[LLX])
969                 doc->pages[doc->numpages].boundingbox[LLX]--;
970               if(flly < doc->pages[doc->numpages].boundingbox[LLY])
971                 doc->pages[doc->numpages].boundingbox[LLY]--;
972               if(furx > doc->pages[doc->numpages].boundingbox[URX])
973                 doc->pages[doc->numpages].boundingbox[URX]++;
974               if(fury > doc->pages[doc->numpages].boundingbox[URY])
975                 doc->pages[doc->numpages].boundingbox[URY]++;
976             }
977           }
978         }
979       }
980     }
981     section_len += line_len;
982     doc->pages[doc->numpages].end = position;
983     doc->pages[doc->numpages].len = section_len - line_len;
984     doc->numpages++;
985   }
986
987   /* Document Trailer */
988
989   if(beginsection) {
990     doc->begintrailer = beginsection;
991     beginsection = 0;
992   }
993   else {
994     doc->begintrailer = position;
995     section_len = line_len;
996   }
997
998   preread = 1;
999   while((preread ||
1000          readline(fd, &line, &position, &line_len)) &&
1001         !(respect_eof && DSCcomment(line) && iscomment(line + 2, "EOF"))) {
1002     if(!preread)
1003       section_len += line_len;
1004     preread = 0;
1005     if(!DSCcomment(line)) {
1006       /* Do nothing */
1007     }
1008     else if(iscomment(line + 2, "Page:")) {
1009       g_free(get_next_text(line + length("%%Page:"), &next_char));
1010       if(sscanf(next_char, "%d", &thispage) != 1)
1011         thispage = 0;
1012       if(!ignore && thispage == nextpage) {
1013         if(doc->numpages > 0) {
1014           doc->pages[doc->numpages - 1].end = position;
1015           doc->pages[doc->numpages - 1].len += section_len - line_len;
1016         }
1017         else {
1018           if(doc->endsetup) {
1019             doc->endsetup = position;
1020             doc->endsetup += section_len - line_len;
1021           }
1022           else if(doc->endprolog) {
1023             doc->endprolog = position;
1024             doc->endprolog += section_len - line_len;
1025           }
1026         }
1027         goto newpage;
1028       }
1029     }
1030     else if(!respect_eof && iscomment(line + 2, "Trailer")) {
1031       /* What we thought was the start of the trailer was really */
1032       /* the trailer of an EPS on the page. */
1033       /* Set the end of the page to this trailer and keep scanning. */
1034       if(doc->numpages > 0) {
1035         doc->pages[doc->numpages - 1].end = position;
1036         doc->pages[doc->numpages - 1].len += section_len - line_len;
1037       }
1038       doc->begintrailer = position;
1039       section_len = line_len;
1040     }
1041     else if(bb_set == ATEND && iscomment(line + 2, "BoundingBox:")) {
1042       if(sscanf(line + length("%%BoundingBox:"), "%d %d %d %d",
1043                 &(doc->boundingbox[LLX]),
1044                 &(doc->boundingbox[LLY]),
1045                 &(doc->boundingbox[URX]), &(doc->boundingbox[URY])) != 4) {
1046         float fllx, flly, furx, fury;
1047         if(sscanf(line + length("%%BoundingBox:"), "%f %f %f %f",
1048                   &fllx, &flly, &furx, &fury) == 4) {
1049           doc->boundingbox[LLX] = fllx;
1050           doc->boundingbox[LLY] = flly;
1051           doc->boundingbox[URX] = furx;
1052           doc->boundingbox[URY] = fury;
1053           if(fllx < doc->boundingbox[LLX])
1054             doc->boundingbox[LLX]--;
1055           if(flly < doc->boundingbox[LLY])
1056             doc->boundingbox[LLY]--;
1057           if(furx > doc->boundingbox[URX])
1058             doc->boundingbox[URX]++;
1059           if(fury > doc->boundingbox[URY])
1060             doc->boundingbox[URY]++;
1061         }
1062       }
1063     }
1064     else if(orientation_set == ATEND && iscomment(line + 2, "Orientation:")) {
1065       sscanf(line + length("%%Orientation:"), "%256s", text);
1066       if(strcmp(text, "Portrait") == 0) {
1067         doc->orientation = GTK_GS_ORIENTATION_PORTRAIT;
1068       }
1069       else if(strcmp(text, "Landscape") == 0) {
1070         doc->orientation = GTK_GS_ORIENTATION_LANDSCAPE;
1071       }
1072       else if(strcmp(text, "Seascape") == 0) {
1073         doc->orientation = GTK_GS_ORIENTATION_SEASCAPE;
1074       }
1075     }
1076     else if(page_order_set == ATEND && iscomment(line + 2, "PageOrder:")) {
1077       sscanf(line + length("%%PageOrder:"), "%256s", text);
1078       if(strcmp(text, "Ascend") == 0) {
1079         doc->pageorder = ASCEND;
1080       }
1081       else if(strcmp(text, "Descend") == 0) {
1082         doc->pageorder = DESCEND;
1083       }
1084       else if(strcmp(text, "Special") == 0) {
1085         doc->pageorder = SPECIAL;
1086       }
1087     }
1088     else if(pages_set == ATEND && iscomment(line + 2, "Pages:")) {
1089       if(sscanf(line + length("%%Pages:"), "%*u %d", &i) == 1) {
1090         if(page_order_set == NONE) {
1091           if(i == -1)
1092             doc->pageorder = DESCEND;
1093           else if(i == 0)
1094             doc->pageorder = SPECIAL;
1095           else if(i == 1)
1096             doc->pageorder = ASCEND;
1097         }
1098       }
1099     }
1100   }
1101   section_len += line_len;
1102   if(DSCcomment(line) && iscomment(line + 2, "EOF")) {
1103     readline(fd, &line, &position, &line_len);
1104     section_len += line_len;
1105   }
1106   doc->endtrailer = position;
1107   doc->lentrailer = section_len - line_len;
1108
1109 #if 0
1110   section_len = line_len;
1111   preread = 1;
1112   while(preread || readline(line, sizeof line, file, &position, &line_len)) {
1113     if(!preread)
1114       section_len += line_len;
1115     preread = 0;
1116     if(DSCcomment(line) && iscomment(line + 2, "Page:")) {
1117       g_free(get_next_text(line + length("%%Page:"), &next_char));
1118       if(sscanf(next_char, "%d", &thispage) != 1)
1119         thispage = 0;
1120       if(!ignore && thispage == nextpage) {
1121         if(doc->numpages > 0) {
1122           doc->pages[doc->numpages - 1].end = position;
1123           doc->pages[doc->numpages - 1].len += doc->lentrailer +
1124             section_len - line_len;
1125         }
1126         else {
1127           if(doc->endsetup) {
1128             doc->endsetup = position;
1129             doc->endsetup += doc->lentrailer + section_len - line_len;
1130           }
1131           else if(doc->endprolog) {
1132             doc->endprolog = position;
1133             doc->endprolog += doc->lentrailer + section_len - line_len;
1134           }
1135         }
1136         goto newpage;
1137       }
1138     }
1139   }
1140 #endif
1141   return doc;
1142 }
1143
1144 /*
1145  *      psfree -- free dynamic storage associated with document structure.
1146  */
1147
1148 void
1149 psfree(doc)
1150      struct document *doc;
1151 {
1152   int i;
1153
1154   if(doc) {
1155     /*
1156        printf("This document exists\n");
1157      */
1158     for(i = 0; i < doc->numpages; i++) {
1159       if(doc->pages[i].label)
1160         g_free(doc->pages[i].label);
1161     }
1162     for(i = 0; i < doc->numsizes; i++) {
1163       if(doc->size[i].name)
1164         g_free(doc->size[i].name);
1165     }
1166     if(doc->title)
1167       g_free(doc->title);
1168     if(doc->date)
1169       g_free(doc->date);
1170     if(doc->pages)
1171       g_free(doc->pages);
1172     if(doc->size)
1173       g_free(doc->size);
1174     g_free(doc);
1175   }
1176 }
1177
1178 /*
1179  * gettextine -- skip over white space and return the rest of the line.
1180  *               If the text begins with '(' return the text string
1181  *               using get_next_text().
1182  */
1183
1184 static char *
1185 gettextline(char *line)
1186 {
1187   char *cp;
1188
1189   while(*line && (*line == ' ' || *line == '\t'))
1190     line++;
1191   if(*line == '(') {
1192     return get_next_text(line, NULL);
1193   }
1194   else {
1195     if(strlen(line) == 0)
1196       return NULL;
1197
1198     cp = g_strdup(line);
1199
1200     /* Remove end of line */
1201     if(cp[strlen(line) - 2] == '\r' && cp[strlen(line) - 1] == '\n')
1202       /* Handle DOS \r\n */
1203       cp[strlen(line) - 2] = '\0';
1204     else if(cp[strlen(line) - 1] == '\n' || cp[strlen(line) - 1] == '\r')
1205       /* Handle mac and unix */
1206       cp[strlen(line) - 1] = '\0';
1207
1208     return cp;
1209   }
1210 }
1211
1212 /*
1213  *      get_next_text -- return the next text string on the line.
1214  *                 return NULL if nothing is present.
1215  */
1216
1217 static char *
1218 get_next_text(line, next_char)
1219      char *line;
1220      char **next_char;
1221 {
1222   char text[PSLINELENGTH];      /* Temporary storage for text */
1223   char *cp;
1224   int quoted = 0;
1225
1226   while(*line && (*line == ' ' || *line == '\t'))
1227     line++;
1228   cp = text;
1229   if(*line == '(') {
1230     int level = 0;
1231     quoted = 1;
1232     line++;
1233     while(*line && !(*line == ')' && level == 0)) {
1234       if(*line == '\\') {
1235         if(*(line + 1) == 'n') {
1236           *cp++ = '\n';
1237           line += 2;
1238         }
1239         else if(*(line + 1) == 'r') {
1240           *cp++ = '\r';
1241           line += 2;
1242         }
1243         else if(*(line + 1) == 't') {
1244           *cp++ = '\t';
1245           line += 2;
1246         }
1247         else if(*(line + 1) == 'b') {
1248           *cp++ = '\b';
1249           line += 2;
1250         }
1251         else if(*(line + 1) == 'f') {
1252           *cp++ = '\f';
1253           line += 2;
1254         }
1255         else if(*(line + 1) == '\\') {
1256           *cp++ = '\\';
1257           line += 2;
1258         }
1259         else if(*(line + 1) == '(') {
1260           *cp++ = '(';
1261           line += 2;
1262         }
1263         else if(*(line + 1) == ')') {
1264           *cp++ = ')';
1265           line += 2;
1266         }
1267         else if(*(line + 1) >= '0' && *(line + 1) <= '9') {
1268           if(*(line + 2) >= '0' && *(line + 2) <= '9') {
1269             if(*(line + 3) >= '0' && *(line + 3) <= '9') {
1270               *cp++ =
1271                 ((*(line + 1) - '0') * 8 + *(line + 2) -
1272                  '0') * 8 + *(line + 3) - '0';
1273               line += 4;
1274             }
1275             else {
1276               *cp++ = (*(line + 1) - '0') * 8 + *(line + 2) - '0';
1277               line += 3;
1278             }
1279           }
1280           else {
1281             *cp++ = *(line + 1) - '0';
1282             line += 2;
1283           }
1284         }
1285         else {
1286           line++;
1287           *cp++ = *line++;
1288         }
1289       }
1290       else if(*line == '(') {
1291         level++;
1292         *cp++ = *line++;
1293       }
1294       else if(*line == ')') {
1295         level--;
1296         *cp++ = *line++;
1297       }
1298       else {
1299         *cp++ = *line++;
1300       }
1301     }
1302   }
1303   else {
1304     while(*line && !(*line == ' ' || *line == '\t' || *line == '\n'))
1305       *cp++ = *line++;
1306   }
1307   *cp = '\0';
1308   if(next_char)
1309     *next_char = line;
1310   if(!quoted && strlen(text) == 0)
1311     return NULL;
1312   return g_strdup(text);
1313 }
1314
1315 /*
1316  *      pscopy -- copy lines of Postscript from a section of one file
1317  *                to another file.
1318  *                Automatically switch to binary copying whenever
1319  *                %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
1320  *                comments are encountered.
1321  */
1322
1323 void
1324 pscopy(from, to, begin, end)
1325      FILE *from;
1326      GtkGSDocSink *to;
1327      long begin;                /* set negative to avoid initial seek */
1328      long end;
1329 {
1330   char line[PSLINELENGTH];      /* 255 characters + 1 newline + 1 NULL */
1331   char text[PSLINELENGTH];      /* Temporary storage for text */
1332   unsigned int num;
1333   int i;
1334   char buf[BUFSIZ];
1335
1336   if(begin >= 0)
1337     fseek(from, begin, SEEK_SET);
1338   while(ftell(from) < end) {
1339     fgets(line, sizeof line, from);
1340     gtk_gs_doc_sink_write(to, line, strlen(line));
1341
1342     if(!(DSCcomment(line) && iscomment(line + 2, "Begin"))) {
1343       /* Do nothing */
1344     }
1345     else if(iscomment(line + 7, "Data:")) {
1346       text[0] = '\0';
1347       if(sscanf(line + length("%%BeginData:"), "%d %*s %256s", &num, text) >= 1) {
1348         if(strcmp(text, "Lines") == 0) {
1349           for(i = 0; i < num; i++) {
1350             fgets(line, sizeof(line), from);
1351             gtk_gs_doc_sink_write(to, line, strlen(line));
1352           }
1353         }
1354         else {
1355           while(num > BUFSIZ) {
1356             fread(buf, sizeof(char), BUFSIZ, from);
1357             gtk_gs_doc_sink_write(to, buf, BUFSIZ);
1358             num -= BUFSIZ;
1359           }
1360           fread(buf, sizeof(char), num, from);
1361           gtk_gs_doc_sink_write(to, buf, num);
1362         }
1363       }
1364     }
1365     else if(iscomment(line + 7, "Binary:")) {
1366       if(sscanf(line + length("%%BeginBinary:"), "%d", &num) == 1) {
1367         while(num > BUFSIZ) {
1368           fread(buf, sizeof(char), BUFSIZ, from);
1369           gtk_gs_doc_sink_write(to, buf, BUFSIZ);
1370           num -= BUFSIZ;
1371         }
1372         fread(buf, sizeof(char), num, from);
1373         gtk_gs_doc_sink_write(to, buf, num);
1374       }
1375     }
1376   }
1377 }
1378
1379 /*
1380  *      pscopyuntil -- copy lines of Postscript from a section of one file
1381  *                     to another file until a particular comment is reached.
1382  *                     Automatically switch to binary copying whenever
1383  *                     %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
1384  *                     comments are encountered.
1385  */
1386
1387 char *
1388 pscopyuntil(FILE * from, GtkGSDocSink * to, long begin, long end,
1389             const char *comment)
1390 {
1391   char line[PSLINELENGTH];      /* 255 characters + 1 newline + 1 NULL */
1392   char text[PSLINELENGTH];      /* Temporary storage for text */
1393   unsigned int num;
1394   int comment_length;
1395   int i;
1396   char buf[BUFSIZ];
1397
1398   if(comment != NULL)
1399     comment_length = strlen(comment);
1400   else
1401     comment_length = 0;
1402   if(begin >= 0)
1403     fseek(from, begin, SEEK_SET);
1404
1405   while(ftell(from) < end && !feof(from)) {
1406     fgets(line, sizeof line, from);
1407
1408     /* iscomment cannot be used here,
1409      * because comment_length is not known at compile time. */
1410     if(comment != NULL && strncmp(line, comment, comment_length) == 0) {
1411       return g_strdup(line);
1412     }
1413     gtk_gs_doc_sink_write(to, line, strlen(line));
1414     if(!(DSCcomment(line) && iscomment(line + 2, "Begin"))) {
1415       /* Do nothing */
1416     }
1417     else if(iscomment(line + 7, "Data:")) {
1418       text[0] = '\0';
1419       if(sscanf(line + length("%%BeginData:"), "%d %*s %256s", &num, text) >= 1) {
1420         if(strcmp(text, "Lines") == 0) {
1421           for(i = 0; i < num; i++) {
1422             fgets(line, sizeof line, from);
1423             gtk_gs_doc_sink_write(to, line, strlen(line));
1424           }
1425         }
1426         else {
1427           while(num > BUFSIZ) {
1428             fread(buf, sizeof(char), BUFSIZ, from);
1429             gtk_gs_doc_sink_write(to, buf, BUFSIZ);
1430             num -= BUFSIZ;
1431           }
1432           fread(buf, sizeof(char), num, from);
1433           gtk_gs_doc_sink_write(to, buf, num);
1434         }
1435       }
1436     }
1437     else if(iscomment(line + 7, "Binary:")) {
1438       if(sscanf(line + length("%%BeginBinary:"), "%d", &num) == 1) {
1439         while(num > BUFSIZ) {
1440           fread(buf, sizeof(char), BUFSIZ, from);
1441           gtk_gs_doc_sink_write(to, buf, BUFSIZ);
1442           num -= BUFSIZ;
1443         }
1444         fread(buf, sizeof(char), num, from);
1445         gtk_gs_doc_sink_write(to, buf, num);
1446       }
1447     }
1448   }
1449   return NULL;
1450 }
1451
1452 /*
1453  *      blank -- determine whether the line contains nothing but whitespace.
1454  */
1455
1456 static int
1457 blank(char *line)
1458 {
1459   char *cp = line;
1460
1461   while(*cp == ' ' || *cp == '\t')
1462     cp++;
1463   return *cp == '\n' || (*cp == '%' && (line[0] != '%' || line[1] != '%'));
1464 }
1465
1466 /*##########################################################*/
1467 /* pscopydoc */
1468 /* Copy the headers, marked pages, and trailer to fp */
1469 /*##########################################################*/
1470
1471 void
1472 pscopydoc(GtkGSDocSink * dest,
1473           char *src_filename, struct document *d, gint * pagelist)
1474 {
1475   FILE *src_file;
1476   char text[PSLINELENGTH];
1477   char *comment;
1478   gboolean pages_written = FALSE;
1479   gboolean pages_atend = FALSE;
1480   int pages;
1481   int page = 1;
1482   int i, j;
1483   int here;
1484
1485   src_file = fopen(src_filename, "r");
1486   i = 0;
1487   pages = 0;
1488   for(i = 0; i < d->numpages; i++) {
1489     if(pagelist[i])
1490       pages++;
1491   }
1492
1493   here = d->beginheader;
1494
1495   while((comment = pscopyuntil(src_file, dest, here, d->endheader, "%%Pages:"))) {
1496     here = ftell(src_file);
1497     if(pages_written || pages_atend) {
1498       g_free(comment);
1499       continue;
1500     }
1501     sscanf(comment + length("%%Pages:"), "%256s", text);
1502     if(strcmp(text, "(atend)") == 0) {
1503       gtk_gs_doc_sink_write(dest, comment, strlen(comment));
1504       pages_atend = TRUE;
1505     }
1506     else {
1507       switch (sscanf(comment + length("%%Pages:"), "%*d %d", &i)) {
1508       case 1:
1509         gtk_gs_doc_sink_printf(dest, "%%%%Pages: %d %d\n", pages, i);
1510         break;
1511       default:
1512         gtk_gs_doc_sink_printf(dest, "%%%%Pages: %d\n", pages);
1513         break;
1514       }
1515       pages_written = TRUE;
1516     }
1517     g_free(comment);
1518   }
1519   pscopyuntil(src_file, dest, d->beginpreview, d->endpreview, NULL);
1520   pscopyuntil(src_file, dest, d->begindefaults, d->enddefaults, NULL);
1521   pscopyuntil(src_file, dest, d->beginprolog, d->endprolog, NULL);
1522   pscopyuntil(src_file, dest, d->beginsetup, d->endsetup, NULL);
1523
1524   for(i = 0; i < d->numpages; i++) {
1525     if(d->pageorder == DESCEND)
1526       j = (d->numpages - 1) - i;
1527     else
1528       j = i;
1529     j = i;
1530     if(pagelist[j]) {
1531       comment = pscopyuntil(src_file, dest,
1532                             d->pages[i].begin, d->pages[i].end, "%%Page:");
1533       gtk_gs_doc_sink_printf(dest, "%%%%Page: %s %d\n",
1534                              d->pages[i].label, page++);
1535       g_free(comment);
1536       pscopyuntil(src_file, dest, -1, d->pages[i].end, NULL);
1537     }
1538   }
1539
1540   here = d->begintrailer;
1541   while((comment = pscopyuntil(src_file, dest, here, d->endtrailer,
1542                                "%%Pages:"))) {
1543     here = ftell(src_file);
1544     if(pages_written) {
1545       g_free(comment);
1546       continue;
1547     }
1548     switch (sscanf(comment + length("%%Pages:"), "%*d %d", &i)) {
1549     case 1:
1550       gtk_gs_doc_sink_printf(dest, "%%%%Pages: %d %d\n", pages, i);
1551       break;
1552     default:
1553       gtk_gs_doc_sink_printf(dest, "%%%%Pages: %d\n", pages);
1554       break;
1555     }
1556     pages_written = TRUE;
1557     g_free(comment);
1558   }
1559
1560   fclose(src_file);
1561 }
1562
1563 /*----------------------------------------------------------*/
1564 /* ps_io_init */
1565 /*----------------------------------------------------------*/
1566
1567 #define FD_FILE             (fd->file)
1568 #define FD_FILE_DESC        (fd->file_desc)
1569 #define FD_FILEPOS          (fd->filepos)
1570 #define FD_LINE_BEGIN       (fd->line_begin)
1571 #define FD_LINE_END         (fd->line_end)
1572 #define FD_LINE_LEN         (fd->line_len)
1573 #define FD_LINE_TERMCHAR    (fd->line_termchar)
1574 #define FD_BUF              (fd->buf)
1575 #define FD_BUF_END          (fd->buf_end)
1576 #define FD_BUF_SIZE         (fd->buf_size)
1577 #define FD_STATUS           (fd->status)
1578
1579 #define FD_STATUS_OKAY        0
1580 #define FD_STATUS_BUFTOOLARGE 1
1581 #define FD_STATUS_NOMORECHARS 2
1582
1583 #define LINE_CHUNK_SIZE     4096
1584 #define MAX_PS_IO_FGETCHARS_BUF_SIZE 57344
1585 #define BREAK_PS_IO_FGETCHARS_BUF_SIZE 49152
1586
1587 static FileData ps_io_init(file)
1588    FILE *file;
1589 {
1590    FileData fd;
1591    size_t size = sizeof(FileDataStruct);
1592
1593    fd = (FileData) g_malloc(size);
1594    memset((void*) fd ,0,(size_t)size);
1595
1596    rewind(file);
1597    FD_FILE      = file;
1598    FD_FILE_DESC = fileno(file);
1599    FD_FILEPOS   = ftell(file);
1600    FD_BUF_SIZE  = (2*LINE_CHUNK_SIZE)+1;
1601    FD_BUF       = g_malloc(FD_BUF_SIZE);
1602    FD_BUF[0]    = '\0';
1603    return(fd);
1604 }
1605
1606 /*----------------------------------------------------------*/
1607 /* ps_io_exit */
1608 /*----------------------------------------------------------*/
1609
1610 static void
1611 ps_io_exit(fd)
1612    FileData fd;
1613 {
1614    g_free(FD_BUF);
1615    g_free(fd);
1616 }
1617
1618 /*----------------------------------------------------------*/
1619 /* ps_io_fseek */
1620 /*----------------------------------------------------------*/
1621
1622 /*static int
1623 ps_io_fseek(fd,offset)
1624    FileData fd;
1625    int offset;
1626 {
1627    int status;
1628    status=fseek(FD_FILE,(long)offset,SEEK_SET);
1629    FD_BUF_END = FD_LINE_BEGIN = FD_LINE_END = FD_LINE_LEN = 0;
1630    FD_FILEPOS = offset;
1631    FD_STATUS  = FD_STATUS_OKAY;
1632    return(status);
1633 }*/
1634
1635 /*----------------------------------------------------------*/
1636 /* ps_io_ftell */
1637 /*----------------------------------------------------------*/
1638
1639 /*static int
1640 ps_io_ftell(fd)
1641    FileData fd;
1642 {
1643    return(FD_FILEPOS);
1644 }*/
1645
1646 /*----------------------------------------------------------*/
1647 /* ps_io_fgetchars */
1648 /*----------------------------------------------------------*/
1649
1650 #ifdef USE_MEMMOVE_CODE
1651 static void ps_memmove (d, s, l)
1652   char *d;
1653   const char *s;
1654   unsigned l;
1655 {
1656   if (s < d) for (s += l, d += l; l; --l) *--d = *--s;
1657   else if (s != d) for (; l; --l)         *d++ = *s++;
1658 }
1659 #else
1660 #   define ps_memmove memmove
1661 #endif
1662
1663 static char * ps_io_fgetchars(fd,num)
1664    FileData fd;
1665    int num;
1666 {
1667    char *eol=NULL,*tmp;
1668    size_t size_of_char = sizeof(char);
1669
1670    if (FD_STATUS != FD_STATUS_OKAY) {
1671       return(NULL);
1672    }
1673
1674    FD_BUF[FD_LINE_END] = FD_LINE_TERMCHAR; /* restoring char previously exchanged against '\0' */
1675    FD_LINE_BEGIN       = FD_LINE_END;
1676
1677    do {
1678       if (num<0) { /* reading whole line */
1679          if (FD_BUF_END-FD_LINE_END) {
1680             /* strpbrk is faster but fails on lines with embedded NULLs 
1681               eol = strpbrk(FD_BUF+FD_LINE_END,"\n\r");
1682             */
1683             tmp = FD_BUF + FD_BUF_END;
1684             eol = FD_BUF + FD_LINE_END;
1685             while (eol < tmp && *eol != '\n' && *eol != '\r') eol++;
1686             if (eol >= tmp) eol = NULL;
1687             if (eol) {
1688                if (*eol=='\r' && *(eol+1)=='\n') eol += 2;
1689                else eol++;
1690                break;
1691             }
1692          }
1693       } else { /* reading specified num of chars */
1694          if (FD_BUF_END >= FD_LINE_BEGIN+num) {
1695             eol = FD_BUF+FD_LINE_BEGIN+num;
1696             break;
1697          }
1698       }
1699
1700       if (FD_BUF_END - FD_LINE_BEGIN > BREAK_PS_IO_FGETCHARS_BUF_SIZE) {
1701         eol = FD_BUF + FD_BUF_END - 1;
1702         break;
1703       }
1704
1705       while (FD_BUF_SIZE < FD_BUF_END+LINE_CHUNK_SIZE+1) {
1706          if (FD_BUF_SIZE > MAX_PS_IO_FGETCHARS_BUF_SIZE) {
1707            /* we should never get here, since the line is broken
1708              artificially after BREAK_PS_IO_FGETCHARS_BUF_SIZE bytes. */
1709             fprintf(stderr, "gv: ps_io_fgetchars: Fatal Error: buffer became too large.\n");
1710             exit(-1);
1711          }
1712          if (FD_LINE_BEGIN) {
1713             ps_memmove((void*)FD_BUF,(void*)(FD_BUF+FD_LINE_BEGIN),
1714                     ((size_t)(FD_BUF_END-FD_LINE_BEGIN+1))*size_of_char);
1715             FD_BUF_END    -= FD_LINE_BEGIN; 
1716             FD_LINE_BEGIN  = 0;
1717          } else {
1718             FD_BUF_SIZE    = FD_BUF_SIZE+LINE_CHUNK_SIZE+1;
1719             FD_BUF         = g_realloc(FD_BUF,FD_BUF_SIZE);
1720          }
1721       }
1722
1723       FD_LINE_END = FD_BUF_END;
1724 #ifdef VMS
1725       /* different existing VMS file formats require that we use read here ###jp###,10/12/96 */ 
1726       if (num<0) FD_BUF_END += read(FD_FILE_DESC,FD_BUF+FD_BUF_END,LINE_CHUNK_SIZE);
1727       else       FD_BUF_END += fread(FD_BUF+FD_BUF_END,size_of_char,LINE_CHUNK_SIZE,FD_FILE);
1728 #else
1729       /* read() seems to fail sometimes (? ? ?) so we always use fread ###jp###,07/31/96*/
1730       FD_BUF_END += fread(FD_BUF+FD_BUF_END,size_of_char,LINE_CHUNK_SIZE,FD_FILE);
1731 #endif
1732
1733       FD_BUF[FD_BUF_END] = '\0';
1734       if (FD_BUF_END-FD_LINE_END == 0) {
1735          FD_STATUS = FD_STATUS_NOMORECHARS;
1736          return(NULL);
1737       }
1738    }
1739    while (1);
1740
1741    FD_LINE_END          = eol - FD_BUF;
1742    FD_LINE_LEN          = FD_LINE_END - FD_LINE_BEGIN;
1743    FD_LINE_TERMCHAR     = FD_BUF[FD_LINE_END];
1744    FD_BUF[FD_LINE_END]  = '\0';
1745 #ifdef USE_FTELL_FOR_FILEPOS
1746    if (FD_LINE_END==FD_BUF_END) {
1747       /*
1748       For VMS we cannot assume that the record is FD_LINE_LEN bytes long
1749       on the disk. For stream_lf and stream_cr that is true, but not for
1750       other formats, since VAXC/DECC converts the formatting into a single \n.
1751       eg. variable format files have a 2-byte length and padding to an even
1752       number of characters. So, we use ftell for each record.
1753       This still will not work if we need to fseek to a \n or \r inside a
1754       variable record (ftell always returns the start of the record in this
1755       case).
1756       (Tim Adye, adye@v2.rl.ac.uk)
1757       */
1758       FD_FILEPOS         = ftell(FD_FILE);
1759    } else
1760 #endif /* USE_FTELL_FOR_FILEPOS */
1761       FD_FILEPOS        += FD_LINE_LEN;
1762
1763    return(FD_BUF+FD_LINE_BEGIN);
1764 }
1765
1766 /*----------------------------------------------------------*/
1767 /*
1768    readline()
1769    Read the next line in the postscript file.
1770    Automatically skip over data (as indicated by
1771    %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
1772    comments.)
1773    Also, skip over included documents (as indicated by
1774    %%BeginDocument/%%EndDocument comments.)
1775 */
1776 /*----------------------------------------------------------*/
1777
1778 static char *readline (fd, lineP, positionP, line_lenP)
1779    FileData fd;
1780    char **lineP;
1781    long *positionP;
1782    unsigned int *line_lenP;
1783 {
1784    unsigned int nbytes=0;
1785    int skipped=0;
1786    char *line;
1787
1788    if (positionP) *positionP = FD_FILEPOS;
1789    line = ps_io_fgetchars(fd,-1);
1790    if (!line) {
1791       *line_lenP = 0;
1792       *lineP     = empty_string;
1793       return(NULL); 
1794    }
1795
1796    *line_lenP = FD_LINE_LEN;
1797
1798 #define IS_COMMENT(comment)                             \
1799            (DSCcomment(line) && iscomment(line+2,(comment)))
1800 #define IS_BEGIN(comment)                               \
1801            (iscomment(line+7,(comment)))
1802 #define SKIP_WHILE(cond)                                \
1803            while (readline(fd, &line, NULL, &nbytes) && (cond)) *line_lenP += nbytes;\
1804            skipped=1;
1805 #define SKIP_UNTIL_1(comment) {                         \
1806            SKIP_WHILE((!IS_COMMENT(comment)))           \
1807         }
1808 #define SKIP_UNTIL_2(comment1,comment2) {               \
1809            SKIP_WHILE((!IS_COMMENT(comment1) && !IS_COMMENT(comment2)))\
1810         }
1811
1812    if  (!IS_COMMENT("Begin"))     {} /* Do nothing */
1813    else if IS_BEGIN("Document:")  SKIP_UNTIL_1("EndDocument")
1814    else if IS_BEGIN("Feature:")   SKIP_UNTIL_1("EndFeature")
1815 #ifdef USE_ACROREAD_WORKAROUND
1816    else if IS_BEGIN("File")       SKIP_UNTIL_2("EndFile","EOF")
1817 #else
1818    else if IS_BEGIN("File")       SKIP_UNTIL_1("EndFile")
1819 #endif
1820    else if IS_BEGIN("Font")       SKIP_UNTIL_1("EndFont")
1821    else if IS_BEGIN("ProcSet")    SKIP_UNTIL_1("EndProcSet")
1822    else if IS_BEGIN("Resource")   SKIP_UNTIL_1("EndResource")
1823    else if IS_BEGIN("Data:")      {
1824       int  num;
1825       char text[101];
1826       if (FD_LINE_LEN > 100) FD_BUF[100] = '\0';
1827       text[0] = '\0';
1828       if (sscanf(line+length("%%BeginData:"), "%d %*s %s", &num, text) >= 1) {
1829          if (strcmp(text, "Lines") == 0) {
1830             while (num) {
1831                line = ps_io_fgetchars(fd,-1);
1832                if (line) *line_lenP += FD_LINE_LEN;
1833                num--;
1834             }
1835          } else {
1836             int read_chunk_size = LINE_CHUNK_SIZE;
1837             while (num>0) {
1838                if (num <= LINE_CHUNK_SIZE) read_chunk_size=num;
1839                line = ps_io_fgetchars(fd,read_chunk_size);
1840                if (line) *line_lenP += FD_LINE_LEN;
1841                num -= read_chunk_size;
1842             }
1843          }
1844       }
1845       SKIP_UNTIL_1("EndData")
1846    }
1847    else if IS_BEGIN("Binary:") {
1848       int  num;
1849       if (sscanf(line+length("%%BeginBinary:"), "%d", &num) == 1) {
1850          int read_chunk_size = LINE_CHUNK_SIZE;
1851          while (num>0) {
1852             if (num <= LINE_CHUNK_SIZE) read_chunk_size=num;
1853             line = ps_io_fgetchars(fd,read_chunk_size);
1854             if (line) *line_lenP += FD_LINE_LEN;
1855             num -= read_chunk_size;
1856          }
1857          SKIP_UNTIL_1("EndBinary")
1858       }
1859    }
1860
1861    if (skipped) {
1862       *line_lenP += nbytes;
1863       *lineP = skipped_line;      
1864    } else {
1865       *lineP = FD_BUF+FD_LINE_BEGIN;
1866    }
1867
1868    return(FD_BUF+FD_LINE_BEGIN);
1869 }