]> www.fi.muni.cz Git - evince.git/blob - ps/ps.c
Try to render even if psscan fails.
[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     doc = g_new0(struct document, 1);
326     doc->default_page_orientation = GTK_GS_ORIENTATION_NONE;
327     doc->orientation = GTK_GS_ORIENTATION_NONE;
328     return (doc);
329   }
330
331   preread = 0;
332   while(preread || readline(fd, &line, &position, &line_len)) {
333     if(!preread)
334       section_len += line_len;
335     preread = 0;
336     if(line[0] != '%' ||
337        iscomment(line + 1, "%EndComments") ||
338        line[1] == ' ' || line[1] == '\t' || line[1] == '\n' ||
339        !isprint(line[1])) {
340       break;
341     }
342     else if(line[1] != '%') {
343       /* Do nothing */
344     }
345     else if(doc->title == NULL && iscomment(line + 2, "Title:")) {
346       doc->title = gettextline(line + length("%%Title:"));
347     }
348     else if(doc->date == NULL && iscomment(line + 2, "CreationDate:")) {
349       doc->date = gettextline(line + length("%%CreationDate:"));
350     }
351     else if(bb_set == NONE && iscomment(line + 2, "BoundingBox:")) {
352       sscanf(line + length("%%BoundingBox:"), "%256s", text);
353       if(strcmp(text, "(atend)") == 0) {
354         bb_set = ATEND;
355       }
356       else {
357         if(sscanf(line + length("%%BoundingBox:"), "%d %d %d %d",
358                   &(doc->boundingbox[LLX]),
359                   &(doc->boundingbox[LLY]),
360                   &(doc->boundingbox[URX]), &(doc->boundingbox[URY])) == 4)
361           bb_set = 1;
362         else {
363           float fllx, flly, furx, fury;
364           if(sscanf(line + length("%%BoundingBox:"), "%f %f %f %f",
365                     &fllx, &flly, &furx, &fury) == 4) {
366             bb_set = 1;
367             doc->boundingbox[LLX] = fllx;
368             doc->boundingbox[LLY] = flly;
369             doc->boundingbox[URX] = furx;
370             doc->boundingbox[URY] = fury;
371             if(fllx < doc->boundingbox[LLX])
372               doc->boundingbox[LLX]--;
373             if(flly < doc->boundingbox[LLY])
374               doc->boundingbox[LLY]--;
375             if(furx > doc->boundingbox[URX])
376               doc->boundingbox[URX]++;
377             if(fury > doc->boundingbox[URY])
378               doc->boundingbox[URY]++;
379           }
380         }
381       }
382     }
383     else if(orientation_set == NONE && iscomment(line + 2, "Orientation:")) {
384       sscanf(line + length("%%Orientation:"), "%256s", text);
385       if(strcmp(text, "(atend)") == 0) {
386         orientation_set = ATEND;
387       }
388       else if(strcmp(text, "Portrait") == 0) {
389         doc->orientation = GTK_GS_ORIENTATION_PORTRAIT;
390         orientation_set = 1;
391       }
392       else if(strcmp(text, "Landscape") == 0) {
393         doc->orientation = GTK_GS_ORIENTATION_LANDSCAPE;
394         orientation_set = 1;
395       }
396       else if(strcmp(text, "Seascape") == 0) {
397         doc->orientation = GTK_GS_ORIENTATION_SEASCAPE;
398         orientation_set = 1;
399       }
400     }
401     else if(page_order_set == NONE && iscomment(line + 2, "PageOrder:")) {
402       sscanf(line + length("%%PageOrder:"), "%256s", text);
403       if(strcmp(text, "(atend)") == 0) {
404         page_order_set = ATEND;
405       }
406       else if(strcmp(text, "Ascend") == 0) {
407         doc->pageorder = ASCEND;
408         page_order_set = 1;
409       }
410       else if(strcmp(text, "Descend") == 0) {
411         doc->pageorder = DESCEND;
412         page_order_set = 1;
413       }
414       else if(strcmp(text, "Special") == 0) {
415         doc->pageorder = SPECIAL;
416         page_order_set = 1;
417       }
418     }
419     else if(pages_set == NONE && iscomment(line + 2, "Pages:")) {
420       sscanf(line + length("%%Pages:"), "%256s", text);
421       if(strcmp(text, "(atend)") == 0) {
422         pages_set = ATEND;
423       }
424       else {
425         switch (sscanf(line + length("%%Pages:"), "%d %d", &maxpages, &i)) {
426         case 2:
427           if(page_order_set == NONE) {
428             if(i == -1) {
429               doc->pageorder = DESCEND;
430               page_order_set = 1;
431             }
432             else if(i == 0) {
433               doc->pageorder = SPECIAL;
434               page_order_set = 1;
435             }
436             else if(i == 1) {
437               doc->pageorder = ASCEND;
438               page_order_set = 1;
439             }
440           }
441         case 1:
442           if(maxpages > 0)
443             doc->pages = pages_new(NULL, 0, maxpages);
444         }
445       }
446     }
447     else if(doc->numsizes == NONE && iscomment(line + 2, "DocumentMedia:")) {
448       float w, h;
449       doc->size = g_new0(GtkGSPaperSize, 1);
450       doc->size[0].name =
451         get_next_text(line + length("%%DocumentMedia:"), &next_char);
452       if(doc->size[0].name != NULL) {
453         if(sscanf(next_char, "%f %f", &w, &h) == 2) {
454           doc->size[0].width = w + 0.5;
455           doc->size[0].height = h + 0.5;
456         }
457         if(doc->size[0].width != 0 && doc->size[0].height != 0)
458           doc->numsizes = 1;
459         else
460           g_free(doc->size[0].name);
461       }
462       preread = 1;
463       while(readline(fd, &line, &position, &line_len) &&
464             DSCcomment(line) && iscomment(line + 2, "+")) {
465         section_len += line_len;
466         doc->size = g_renew(GtkGSPaperSize, doc->size, doc->numsizes + 1);
467         doc->size[doc->numsizes].name =
468           get_next_text(line + length("%%+"), &next_char);
469         if(doc->size[doc->numsizes].name != NULL) {
470           if(sscanf(next_char, "%f %f", &w, &h) == 2) {
471             doc->size[doc->numsizes].width = w + 0.5;
472             doc->size[doc->numsizes].height = h + 0.5;
473           }
474           if(doc->size[doc->numsizes].width != 0 &&
475              doc->size[doc->numsizes].height != 0)
476             doc->numsizes++;
477           else
478             g_free(doc->size[doc->numsizes].name);
479         }
480       }
481       section_len += line_len;
482       if(doc->numsizes != 0)
483         doc->default_page_size = doc->size;
484     }
485     else if(doc->numsizes == NONE && iscomment(line + 2, "DocumentPaperSizes:")) {
486
487       doc->size = g_new0(GtkGSPaperSize, 1);
488       doc->size[0].name =
489         get_next_text(line + length("%%DocumentPaperSizes:"), &next_char);
490       if(doc->size[0].name != NULL) {
491         doc->size[0].width = 0;
492         doc->size[0].height = 0;
493         for(dmp = papersizes; dmp->name != NULL; dmp++) {
494           /* Note: Paper size comment uses down cased paper size
495            * name.  Case insensitive compares are only used for
496            * PaperSize comments.
497            */
498           if(strcasecmp(doc->size[0].name, dmp->name) == 0) {
499             g_free(doc->size[0].name);
500             doc->size[0].name = g_strdup(dmp->name);
501             doc->size[0].width = dmp->width;
502             doc->size[0].height = dmp->height;
503             break;
504           }
505         }
506         if(doc->size[0].width != 0 && doc->size[0].height != 0)
507           doc->numsizes = 1;
508         else
509           g_free(doc->size[0].name);
510       }
511       while((cp = get_next_text(next_char, &next_char))) {
512         doc->size = g_renew(GtkGSPaperSize, doc->size, doc->numsizes + 1);
513         doc->size[doc->numsizes].name = cp;
514         doc->size[doc->numsizes].width = 0;
515         doc->size[doc->numsizes].height = 0;
516         for(dmp = papersizes; dmp->name != NULL; dmp++) {
517           /* Note: Paper size comment uses down cased paper size
518            * name.  Case insensitive compares are only used for
519            * PaperSize comments.
520            */
521           if(strcasecmp(doc->size[doc->numsizes].name, dmp->name) == 0) {
522             g_free(doc->size[doc->numsizes].name);
523             doc->size[doc->numsizes].name = g_strdup(dmp->name);
524             doc->size[doc->numsizes].name = dmp->name;
525             doc->size[doc->numsizes].width = dmp->width;
526             doc->size[doc->numsizes].height = dmp->height;
527             break;
528           }
529         }
530         if(doc->size[doc->numsizes].width != 0 &&
531            doc->size[doc->numsizes].height != 0)
532           doc->numsizes++;
533         else
534           g_free(doc->size[doc->numsizes].name);
535       }
536       preread = 1;
537       while(readline(fd, &line, &position, &line_len) &&
538             DSCcomment(line) && iscomment(line + 2, "+")) {
539         section_len += line_len;
540         next_char = line + length("%%+");
541         while((cp = get_next_text(next_char, &next_char))) {
542           doc->size = g_renew(GtkGSPaperSize, doc->size, doc->numsizes + 1);
543           doc->size[doc->numsizes].name = cp;
544           doc->size[doc->numsizes].width = 0;
545           doc->size[doc->numsizes].height = 0;
546           for(dmp = papersizes; dmp->name != NULL; dmp++) {
547             /* Note: Paper size comment uses down cased paper size
548              * name.  Case insensitive compares are only used for
549              * PaperSize comments.
550              */
551             if(strcasecmp(doc->size[doc->numsizes].name, dmp->name) == 0) {
552               doc->size[doc->numsizes].width = dmp->width;
553               doc->size[doc->numsizes].height = dmp->height;
554               break;
555             }
556           }
557           if(doc->size[doc->numsizes].width != 0 &&
558              doc->size[doc->numsizes].height != 0)
559             doc->numsizes++;
560           else
561             g_free(doc->size[doc->numsizes].name);
562         }
563       }
564       section_len += line_len;
565       if(doc->numsizes != 0)
566         doc->default_page_size = doc->size;
567     }
568   }
569
570   if(DSCcomment(line) && iscomment(line + 2, "EndComments")) {
571     readline(fd, &line, &position, &line_len);
572     section_len += line_len;
573   }
574   doc->endheader = position;
575   doc->lenheader = section_len - line_len;
576
577   /* Optional Preview comments for encapsulated PostScript files */
578
579   beginsection = position;
580   section_len = line_len;
581   while(blank(line) && readline(fd, &line, &position, &line_len)) {
582     section_len += line_len;
583   }
584
585   if(doc->epsf && DSCcomment(line) && iscomment(line + 2, "BeginPreview")) {
586     doc->beginpreview = beginsection;
587     beginsection = 0;
588     while(readline(fd, &line, &position, &line_len) &&
589           !(DSCcomment(line) && iscomment(line + 2, "EndPreview"))) {
590       section_len += line_len;
591     }
592     section_len += line_len;
593     readline(fd, &line, &position, &line_len);
594     section_len += line_len;
595     doc->endpreview = position;
596     doc->lenpreview = section_len - line_len;
597   }
598
599   /* Page Defaults for Version 3.0 files */
600
601   if(beginsection == 0) {
602     beginsection = position;
603     section_len = line_len;
604   }
605   while(blank(line) && readline(fd, &line, &position, &line_len)) {
606     section_len += line_len;
607   }
608
609   if(DSCcomment(line) && iscomment(line + 2, "BeginDefaults")) {
610     doc->begindefaults = beginsection;
611     beginsection = 0;
612     while(readline(fd, &line, &position, &line_len) &&
613           !(DSCcomment(line) && iscomment(line + 2, "EndDefaults"))) {
614       section_len += line_len;
615       if(!DSCcomment(line)) {
616         /* Do nothing */
617       }
618       else if(doc->default_page_orientation == NONE &&
619               iscomment(line + 2, "PageOrientation:")) {
620         sscanf(line + length("%%PageOrientation:"), "%256s", text);
621         if(strcmp(text, "Portrait") == 0) {
622           doc->default_page_orientation = GTK_GS_ORIENTATION_PORTRAIT;
623         }
624         else if(strcmp(text, "Landscape") == 0) {
625           doc->default_page_orientation = GTK_GS_ORIENTATION_LANDSCAPE;
626         }
627         else if(strcmp(text, "Seascape") == 0) {
628           doc->default_page_orientation = GTK_GS_ORIENTATION_SEASCAPE;
629         }
630       }
631       else if(page_size_set == NONE && iscomment(line + 2, "PageMedia:")) {
632         cp = get_next_text(line + length("%%PageMedia:"), NULL);
633         for(dmp = doc->size, i = 0; i < doc->numsizes; i++, dmp++) {
634           if(strcmp(cp, dmp->name) == 0) {
635             doc->default_page_size = dmp;
636             page_size_set = 1;
637             break;
638           }
639         }
640         g_free(cp);
641       }
642       else if(page_bb_set == NONE && iscomment(line + 2, "PageBoundingBox:")) {
643         if(sscanf(line + length("%%PageBoundingBox:"), "%d %d %d %d",
644                   &(doc->default_page_boundingbox[LLX]),
645                   &(doc->default_page_boundingbox[LLY]),
646                   &(doc->default_page_boundingbox[URX]),
647                   &(doc->default_page_boundingbox[URY])) == 4)
648           page_bb_set = 1;
649         else {
650           float fllx, flly, furx, fury;
651           if(sscanf
652              (line + length("%%PageBoundingBox:"), "%f %f %f %f",
653               &fllx, &flly, &furx, &fury) == 4) {
654             page_bb_set = 1;
655             doc->default_page_boundingbox[LLX] = fllx;
656             doc->default_page_boundingbox[LLY] = flly;
657             doc->default_page_boundingbox[URX] = furx;
658             doc->default_page_boundingbox[URY] = fury;
659             if(fllx < doc->default_page_boundingbox[LLX])
660               doc->default_page_boundingbox[LLX]--;
661             if(flly < doc->default_page_boundingbox[LLY])
662               doc->default_page_boundingbox[LLY]--;
663             if(furx > doc->default_page_boundingbox[URX])
664               doc->default_page_boundingbox[URX]++;
665             if(fury > doc->default_page_boundingbox[URY])
666               doc->default_page_boundingbox[URY]++;
667           }
668         }
669       }
670     }
671     section_len += line_len;
672     readline(fd, &line, &position, &line_len);
673     section_len += line_len;
674     doc->enddefaults = position;
675     doc->lendefaults = section_len - line_len;
676   }
677
678   /* Document Prolog */
679
680   if(beginsection == 0) {
681     beginsection = position;
682     section_len = line_len;
683   }
684   while(blank(line) && readline(fd, &line, &position, &line_len)) {
685     section_len += line_len;
686   }
687
688   if(!(DSCcomment(line) &&
689        (iscomment(line + 2, "BeginSetup") ||
690         iscomment(line + 2, "Page:") ||
691         iscomment(line + 2, "Trailer") || iscomment(line + 2, "EOF")))) {
692     doc->beginprolog = beginsection;
693     beginsection = 0;
694     preread = 1;
695
696     while((preread ||
697            readline(fd, &line, &position, &line_len)) &&
698           !(DSCcomment(line) &&
699             (iscomment(line + 2, "EndProlog") ||
700              iscomment(line + 2, "BeginSetup") ||
701              iscomment(line + 2, "Page:") ||
702              iscomment(line + 2, "Trailer") || iscomment(line + 2, "EOF")))) {
703       if(!preread)
704         section_len += line_len;
705       preread = 0;
706     }
707     section_len += line_len;
708     if(DSCcomment(line) && iscomment(line + 2, "EndProlog")) {
709       readline(fd, &line, &position, &line_len);
710       section_len += line_len;
711     }
712     doc->endprolog = position;
713     doc->lenprolog = section_len - line_len;
714   }
715
716   /* Document Setup,  Page Defaults found here for Version 2 files */
717
718   if(beginsection == 0) {
719     beginsection = position;
720     section_len = line_len;
721   }
722   while(blank(line) && readline(fd, &line, &position, &line_len)) {
723     section_len += line_len;
724   }
725
726   if(!(DSCcomment(line) &&
727        (iscomment(line + 2, "Page:") ||
728         iscomment(line + 2, "Trailer") ||
729         (respect_eof && iscomment(line + 2, "EOF"))))) {
730     doc->beginsetup = beginsection;
731     beginsection = 0;
732     preread = 1;
733     while((preread ||
734            readline(fd, &line, &position, &line_len)) &&
735           !(DSCcomment(line) &&
736             (iscomment(line + 2, "EndSetup") ||
737              iscomment(line + 2, "Page:") ||
738              iscomment(line + 2, "Trailer") ||
739              (respect_eof && iscomment(line + 2, "EOF"))))) {
740       if(!preread)
741         section_len += line_len;
742       preread = 0;
743       if(!DSCcomment(line)) {
744         /* Do nothing */
745       }
746       else if(doc->default_page_orientation == NONE &&
747               iscomment(line + 2, "PageOrientation:")) {
748         sscanf(line + length("%%PageOrientation:"), "%256s", text);
749         if(strcmp(text, "Portrait") == 0) {
750           doc->default_page_orientation = GTK_GS_ORIENTATION_PORTRAIT;
751         }
752         else if(strcmp(text, "Landscape") == 0) {
753           doc->default_page_orientation = GTK_GS_ORIENTATION_LANDSCAPE;
754         }
755         else if(strcmp(text, "Seascape") == 0) {
756           doc->default_page_orientation = GTK_GS_ORIENTATION_SEASCAPE;
757         }
758       }
759       else if(page_size_set == NONE && iscomment(line + 2, "PaperSize:")) {
760         cp = get_next_text(line + length("%%PaperSize:"), NULL);
761         for(dmp = doc->size, i = 0; i < doc->numsizes; i++, dmp++) {
762           /* Note: Paper size comment uses down cased paper size
763            * name.  Case insensitive compares are only used for
764            * PaperSize comments.
765            */
766           if(strcasecmp(cp, dmp->name) == 0) {
767             doc->default_page_size = dmp;
768             page_size_set = 1;
769             break;
770           }
771         }
772         g_free(cp);
773       }
774       else if(page_bb_set == NONE && iscomment(line + 2, "PageBoundingBox:")) {
775         if(sscanf(line + length("%%PageBoundingBox:"), "%d %d %d %d",
776                   &(doc->default_page_boundingbox[LLX]),
777                   &(doc->default_page_boundingbox[LLY]),
778                   &(doc->default_page_boundingbox[URX]),
779                   &(doc->default_page_boundingbox[URY])) == 4)
780           page_bb_set = 1;
781         else {
782           float fllx, flly, furx, fury;
783           if(sscanf
784              (line + length("%%PageBoundingBox:"), "%f %f %f %f",
785               &fllx, &flly, &furx, &fury) == 4) {
786             page_bb_set = 1;
787             doc->default_page_boundingbox[LLX] = fllx;
788             doc->default_page_boundingbox[LLY] = flly;
789             doc->default_page_boundingbox[URX] = furx;
790             doc->default_page_boundingbox[URY] = fury;
791             if(fllx < doc->default_page_boundingbox[LLX])
792               doc->default_page_boundingbox[LLX]--;
793             if(flly < doc->default_page_boundingbox[LLY])
794               doc->default_page_boundingbox[LLY]--;
795             if(furx > doc->default_page_boundingbox[URX])
796               doc->default_page_boundingbox[URX]++;
797             if(fury > doc->default_page_boundingbox[URY])
798               doc->default_page_boundingbox[URY]++;
799           }
800         }
801       }
802     }
803     section_len += line_len;
804     if(DSCcomment(line) && iscomment(line + 2, "EndSetup")) {
805       readline(fd, &line, &position, &line_len);
806       section_len += line_len;
807     }
808     doc->endsetup = position;
809     doc->lensetup = section_len - line_len;
810   }
811
812   /* Added this (Nov. 2, 1999) when I noticed that
813      a Postscript file would load in gv but not in ggv
814
815      dmg@csg.uwaterloo.ca */
816
817   /* BEGIN Windows NT fix ###jp###
818      Mark Pfeifer (pfeiferm%ppddev@comet.cmis.abbott.com) told me
819      about problems when viewing Windows NT 3.51 generated postscript
820      files with gv. He found that the relevant postscript files
821      show important postscript code after the '%%EndSetup' and before
822      the first page comment '%%Page: x y'.
823    */
824   if(doc->beginsetup) {
825     while(!(DSCcomment(line) &&
826             (iscomment(line + 2, "EndSetup") ||
827              (iscomment(line + 2, "Page:") ||
828               iscomment(line + 2, "Trailer") ||
829               (respect_eof && iscomment(line + 2, "EOF"))))) &&
830           (readline(fd, &line, &position, &line_len))) {
831       section_len += line_len;
832       doc->lensetup = section_len - line_len;
833       doc->endsetup = position;
834     }
835   }
836   /* END Windows NT fix ###jp## */
837
838   /* Individual Pages */
839
840   if(beginsection == 0) {
841     beginsection = position;
842     section_len = line_len;
843   }
844   while(blank(line) && readline(fd, &line, &position, &line_len)) {
845     section_len += line_len;
846   }
847
848
849 newpage:
850   while(DSCcomment(line) && iscomment(line + 2, "Page:")) {
851     if(maxpages == 0) {
852       maxpages = 1;
853       doc->pages = pages_new(NULL, 0, maxpages);
854     }
855     label = get_next_text(line + length("%%Page:"), &next_char);
856     if(sscanf(next_char, "%d", &thispage) != 1)
857       thispage = 0;
858     if(nextpage == 1) {
859       ignore = thispage != 1;
860     }
861     if(!ignore && thispage != nextpage) {
862       g_free(label);
863       doc->numpages--;
864       goto continuepage;
865     }
866     nextpage++;
867     if(doc->numpages == maxpages) {
868       maxpages++;
869       doc->pages = pages_new(doc->pages, maxpages - 1, maxpages);
870     }
871     page_bb_set = NONE;
872     doc->pages[doc->numpages].label = label;
873     if(beginsection) {
874       doc->pages[doc->numpages].begin = beginsection;
875       beginsection = 0;
876     }
877     else {
878       doc->pages[doc->numpages].begin = position;
879       section_len = line_len;
880     }
881   continuepage:
882     while(readline(fd, &line, &position, &line_len) &&
883           !(DSCcomment(line) &&
884             (iscomment(line + 2, "Page:") ||
885              iscomment(line + 2, "Trailer") ||
886              (respect_eof && iscomment(line + 2, "EOF"))))) {
887       section_len += line_len;
888       if(!DSCcomment(line)) {
889         /* Do nothing */
890       }
891       else if(doc->pages[doc->numpages].orientation == NONE &&
892               iscomment(line + 2, "PageOrientation:")) {
893         sscanf(line + length("%%PageOrientation:"), "%256s", text);
894         if(strcmp(text, "Portrait") == 0) {
895           doc->pages[doc->numpages].orientation = GTK_GS_ORIENTATION_PORTRAIT;
896         }
897         else if(strcmp(text, "Landscape") == 0) {
898           doc->pages[doc->numpages].orientation = GTK_GS_ORIENTATION_LANDSCAPE;
899         }
900         else if(strcmp(text, "Seascape") == 0) {
901           doc->pages[doc->numpages].orientation = GTK_GS_ORIENTATION_SEASCAPE;
902         }
903       }
904       else if(doc->pages[doc->numpages].size == NULL &&
905               iscomment(line + 2, "PageMedia:")) {
906         cp = get_next_text(line + length("%%PageMedia:"), NULL);
907         for(dmp = doc->size, i = 0; i < doc->numsizes; i++, dmp++) {
908           if(strcmp(cp, dmp->name) == 0) {
909             doc->pages[doc->numpages].size = dmp;
910             break;
911           }
912         }
913         g_free(cp);
914       }
915       else if(doc->pages[doc->numpages].size == NULL &&
916               iscomment(line + 2, "PaperSize:")) {
917         cp = get_next_text(line + length("%%PaperSize:"), NULL);
918         for(dmp = doc->size, i = 0; i < doc->numsizes; i++, dmp++) {
919           /* Note: Paper size comment uses down cased paper size
920            * name.  Case insensitive compares are only used for
921            * PaperSize comments.
922            */
923           if(strcasecmp(cp, dmp->name) == 0) {
924             doc->pages[doc->numpages].size = dmp;
925             break;
926           }
927         }
928         g_free(cp);
929       }
930       else if((page_bb_set == NONE || page_bb_set == ATEND) &&
931               iscomment(line + 2, "PageBoundingBox:")) {
932         sscanf(line + length("%%PageBoundingBox:"), "%256s", text);
933         if(strcmp(text, "(atend)") == 0) {
934           page_bb_set = ATEND;
935         }
936         else {
937           if(sscanf
938              (line + length("%%PageBoundingBox:"), "%d %d %d %d",
939               &(doc->pages[doc->numpages].boundingbox[LLX]),
940               &(doc->pages[doc->numpages].boundingbox[LLY]),
941               &(doc->pages[doc->numpages].boundingbox[URX]),
942               &(doc->pages[doc->numpages].boundingbox[URY])) == 4) {
943             if(page_bb_set == NONE)
944               page_bb_set = 1;
945           }
946           else {
947             float fllx, flly, furx, fury;
948             if(sscanf(line + length("%%PageBoundingBox:"),
949                       "%f %f %f %f", &fllx, &flly, &furx, &fury) == 4) {
950               if(page_bb_set == NONE)
951                 page_bb_set = 1;
952               doc->pages[doc->numpages].boundingbox[LLX] = fllx;
953               doc->pages[doc->numpages].boundingbox[LLY] = flly;
954               doc->pages[doc->numpages].boundingbox[URX] = furx;
955               doc->pages[doc->numpages].boundingbox[URY] = fury;
956               if(fllx < doc->pages[doc->numpages].boundingbox[LLX])
957                 doc->pages[doc->numpages].boundingbox[LLX]--;
958               if(flly < doc->pages[doc->numpages].boundingbox[LLY])
959                 doc->pages[doc->numpages].boundingbox[LLY]--;
960               if(furx > doc->pages[doc->numpages].boundingbox[URX])
961                 doc->pages[doc->numpages].boundingbox[URX]++;
962               if(fury > doc->pages[doc->numpages].boundingbox[URY])
963                 doc->pages[doc->numpages].boundingbox[URY]++;
964             }
965           }
966         }
967       }
968     }
969     section_len += line_len;
970     doc->pages[doc->numpages].end = position;
971     doc->pages[doc->numpages].len = section_len - line_len;
972     doc->numpages++;
973   }
974
975   /* Document Trailer */
976
977   if(beginsection) {
978     doc->begintrailer = beginsection;
979     beginsection = 0;
980   }
981   else {
982     doc->begintrailer = position;
983     section_len = line_len;
984   }
985
986   preread = 1;
987   while((preread ||
988          readline(fd, &line, &position, &line_len)) &&
989         !(respect_eof && DSCcomment(line) && iscomment(line + 2, "EOF"))) {
990     if(!preread)
991       section_len += line_len;
992     preread = 0;
993     if(!DSCcomment(line)) {
994       /* Do nothing */
995     }
996     else if(iscomment(line + 2, "Page:")) {
997       g_free(get_next_text(line + length("%%Page:"), &next_char));
998       if(sscanf(next_char, "%d", &thispage) != 1)
999         thispage = 0;
1000       if(!ignore && thispage == nextpage) {
1001         if(doc->numpages > 0) {
1002           doc->pages[doc->numpages - 1].end = position;
1003           doc->pages[doc->numpages - 1].len += section_len - line_len;
1004         }
1005         else {
1006           if(doc->endsetup) {
1007             doc->endsetup = position;
1008             doc->endsetup += section_len - line_len;
1009           }
1010           else if(doc->endprolog) {
1011             doc->endprolog = position;
1012             doc->endprolog += section_len - line_len;
1013           }
1014         }
1015         goto newpage;
1016       }
1017     }
1018     else if(!respect_eof && iscomment(line + 2, "Trailer")) {
1019       /* What we thought was the start of the trailer was really */
1020       /* the trailer of an EPS on the page. */
1021       /* Set the end of the page to this trailer and keep scanning. */
1022       if(doc->numpages > 0) {
1023         doc->pages[doc->numpages - 1].end = position;
1024         doc->pages[doc->numpages - 1].len += section_len - line_len;
1025       }
1026       doc->begintrailer = position;
1027       section_len = line_len;
1028     }
1029     else if(bb_set == ATEND && iscomment(line + 2, "BoundingBox:")) {
1030       if(sscanf(line + length("%%BoundingBox:"), "%d %d %d %d",
1031                 &(doc->boundingbox[LLX]),
1032                 &(doc->boundingbox[LLY]),
1033                 &(doc->boundingbox[URX]), &(doc->boundingbox[URY])) != 4) {
1034         float fllx, flly, furx, fury;
1035         if(sscanf(line + length("%%BoundingBox:"), "%f %f %f %f",
1036                   &fllx, &flly, &furx, &fury) == 4) {
1037           doc->boundingbox[LLX] = fllx;
1038           doc->boundingbox[LLY] = flly;
1039           doc->boundingbox[URX] = furx;
1040           doc->boundingbox[URY] = fury;
1041           if(fllx < doc->boundingbox[LLX])
1042             doc->boundingbox[LLX]--;
1043           if(flly < doc->boundingbox[LLY])
1044             doc->boundingbox[LLY]--;
1045           if(furx > doc->boundingbox[URX])
1046             doc->boundingbox[URX]++;
1047           if(fury > doc->boundingbox[URY])
1048             doc->boundingbox[URY]++;
1049         }
1050       }
1051     }
1052     else if(orientation_set == ATEND && iscomment(line + 2, "Orientation:")) {
1053       sscanf(line + length("%%Orientation:"), "%256s", text);
1054       if(strcmp(text, "Portrait") == 0) {
1055         doc->orientation = GTK_GS_ORIENTATION_PORTRAIT;
1056       }
1057       else if(strcmp(text, "Landscape") == 0) {
1058         doc->orientation = GTK_GS_ORIENTATION_LANDSCAPE;
1059       }
1060       else if(strcmp(text, "Seascape") == 0) {
1061         doc->orientation = GTK_GS_ORIENTATION_SEASCAPE;
1062       }
1063     }
1064     else if(page_order_set == ATEND && iscomment(line + 2, "PageOrder:")) {
1065       sscanf(line + length("%%PageOrder:"), "%256s", text);
1066       if(strcmp(text, "Ascend") == 0) {
1067         doc->pageorder = ASCEND;
1068       }
1069       else if(strcmp(text, "Descend") == 0) {
1070         doc->pageorder = DESCEND;
1071       }
1072       else if(strcmp(text, "Special") == 0) {
1073         doc->pageorder = SPECIAL;
1074       }
1075     }
1076     else if(pages_set == ATEND && iscomment(line + 2, "Pages:")) {
1077       if(sscanf(line + length("%%Pages:"), "%*u %d", &i) == 1) {
1078         if(page_order_set == NONE) {
1079           if(i == -1)
1080             doc->pageorder = DESCEND;
1081           else if(i == 0)
1082             doc->pageorder = SPECIAL;
1083           else if(i == 1)
1084             doc->pageorder = ASCEND;
1085         }
1086       }
1087     }
1088   }
1089   section_len += line_len;
1090   if(DSCcomment(line) && iscomment(line + 2, "EOF")) {
1091     readline(fd, &line, &position, &line_len);
1092     section_len += line_len;
1093   }
1094   doc->endtrailer = position;
1095   doc->lentrailer = section_len - line_len;
1096
1097 #if 0
1098   section_len = line_len;
1099   preread = 1;
1100   while(preread || readline(line, sizeof line, file, &position, &line_len)) {
1101     if(!preread)
1102       section_len += line_len;
1103     preread = 0;
1104     if(DSCcomment(line) && iscomment(line + 2, "Page:")) {
1105       g_free(get_next_text(line + length("%%Page:"), &next_char));
1106       if(sscanf(next_char, "%d", &thispage) != 1)
1107         thispage = 0;
1108       if(!ignore && thispage == nextpage) {
1109         if(doc->numpages > 0) {
1110           doc->pages[doc->numpages - 1].end = position;
1111           doc->pages[doc->numpages - 1].len += doc->lentrailer +
1112             section_len - line_len;
1113         }
1114         else {
1115           if(doc->endsetup) {
1116             doc->endsetup = position;
1117             doc->endsetup += doc->lentrailer + section_len - line_len;
1118           }
1119           else if(doc->endprolog) {
1120             doc->endprolog = position;
1121             doc->endprolog += doc->lentrailer + section_len - line_len;
1122           }
1123         }
1124         goto newpage;
1125       }
1126     }
1127   }
1128 #endif
1129   return doc;
1130 }
1131
1132 /*
1133  *      psfree -- free dynamic storage associated with document structure.
1134  */
1135
1136 void
1137 psfree(doc)
1138      struct document *doc;
1139 {
1140   int i;
1141
1142   if(doc) {
1143     /*
1144        printf("This document exists\n");
1145      */
1146     for(i = 0; i < doc->numpages; i++) {
1147       if(doc->pages[i].label)
1148         g_free(doc->pages[i].label);
1149     }
1150     for(i = 0; i < doc->numsizes; i++) {
1151       if(doc->size[i].name)
1152         g_free(doc->size[i].name);
1153     }
1154     if(doc->title)
1155       g_free(doc->title);
1156     if(doc->date)
1157       g_free(doc->date);
1158     if(doc->pages)
1159       g_free(doc->pages);
1160     if(doc->size)
1161       g_free(doc->size);
1162     g_free(doc);
1163   }
1164 }
1165
1166 /*
1167  * gettextine -- skip over white space and return the rest of the line.
1168  *               If the text begins with '(' return the text string
1169  *               using get_next_text().
1170  */
1171
1172 static char *
1173 gettextline(char *line)
1174 {
1175   char *cp;
1176
1177   while(*line && (*line == ' ' || *line == '\t'))
1178     line++;
1179   if(*line == '(') {
1180     return get_next_text(line, NULL);
1181   }
1182   else {
1183     if(strlen(line) == 0)
1184       return NULL;
1185
1186     cp = g_strdup(line);
1187
1188     /* Remove end of line */
1189     if(cp[strlen(line) - 2] == '\r' && cp[strlen(line) - 1] == '\n')
1190       /* Handle DOS \r\n */
1191       cp[strlen(line) - 2] = '\0';
1192     else if(cp[strlen(line) - 1] == '\n' || cp[strlen(line) - 1] == '\r')
1193       /* Handle mac and unix */
1194       cp[strlen(line) - 1] = '\0';
1195
1196     return cp;
1197   }
1198 }
1199
1200 /*
1201  *      get_next_text -- return the next text string on the line.
1202  *                 return NULL if nothing is present.
1203  */
1204
1205 static char *
1206 get_next_text(line, next_char)
1207      char *line;
1208      char **next_char;
1209 {
1210   char text[PSLINELENGTH];      /* Temporary storage for text */
1211   char *cp;
1212   int quoted = 0;
1213
1214   while(*line && (*line == ' ' || *line == '\t'))
1215     line++;
1216   cp = text;
1217   if(*line == '(') {
1218     int level = 0;
1219     quoted = 1;
1220     line++;
1221     while(*line && !(*line == ')' && level == 0)) {
1222       if(*line == '\\') {
1223         if(*(line + 1) == 'n') {
1224           *cp++ = '\n';
1225           line += 2;
1226         }
1227         else if(*(line + 1) == 'r') {
1228           *cp++ = '\r';
1229           line += 2;
1230         }
1231         else if(*(line + 1) == 't') {
1232           *cp++ = '\t';
1233           line += 2;
1234         }
1235         else if(*(line + 1) == 'b') {
1236           *cp++ = '\b';
1237           line += 2;
1238         }
1239         else if(*(line + 1) == 'f') {
1240           *cp++ = '\f';
1241           line += 2;
1242         }
1243         else if(*(line + 1) == '\\') {
1244           *cp++ = '\\';
1245           line += 2;
1246         }
1247         else if(*(line + 1) == '(') {
1248           *cp++ = '(';
1249           line += 2;
1250         }
1251         else if(*(line + 1) == ')') {
1252           *cp++ = ')';
1253           line += 2;
1254         }
1255         else if(*(line + 1) >= '0' && *(line + 1) <= '9') {
1256           if(*(line + 2) >= '0' && *(line + 2) <= '9') {
1257             if(*(line + 3) >= '0' && *(line + 3) <= '9') {
1258               *cp++ =
1259                 ((*(line + 1) - '0') * 8 + *(line + 2) -
1260                  '0') * 8 + *(line + 3) - '0';
1261               line += 4;
1262             }
1263             else {
1264               *cp++ = (*(line + 1) - '0') * 8 + *(line + 2) - '0';
1265               line += 3;
1266             }
1267           }
1268           else {
1269             *cp++ = *(line + 1) - '0';
1270             line += 2;
1271           }
1272         }
1273         else {
1274           line++;
1275           *cp++ = *line++;
1276         }
1277       }
1278       else if(*line == '(') {
1279         level++;
1280         *cp++ = *line++;
1281       }
1282       else if(*line == ')') {
1283         level--;
1284         *cp++ = *line++;
1285       }
1286       else {
1287         *cp++ = *line++;
1288       }
1289     }
1290   }
1291   else {
1292     while(*line && !(*line == ' ' || *line == '\t' || *line == '\n'))
1293       *cp++ = *line++;
1294   }
1295   *cp = '\0';
1296   if(next_char)
1297     *next_char = line;
1298   if(!quoted && strlen(text) == 0)
1299     return NULL;
1300   return g_strdup(text);
1301 }
1302
1303 /*
1304  *      pscopy -- copy lines of Postscript from a section of one file
1305  *                to another file.
1306  *                Automatically switch to binary copying whenever
1307  *                %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
1308  *                comments are encountered.
1309  */
1310
1311 void
1312 pscopy(from, to, begin, end)
1313      FILE *from;
1314      GtkGSDocSink *to;
1315      long begin;                /* set negative to avoid initial seek */
1316      long end;
1317 {
1318   char line[PSLINELENGTH];      /* 255 characters + 1 newline + 1 NULL */
1319   char text[PSLINELENGTH];      /* Temporary storage for text */
1320   unsigned int num;
1321   int i;
1322   char buf[BUFSIZ];
1323
1324   if(begin >= 0)
1325     fseek(from, begin, SEEK_SET);
1326   while(ftell(from) < end) {
1327     fgets(line, sizeof line, from);
1328     gtk_gs_doc_sink_write(to, line, strlen(line));
1329
1330     if(!(DSCcomment(line) && iscomment(line + 2, "Begin"))) {
1331       /* Do nothing */
1332     }
1333     else if(iscomment(line + 7, "Data:")) {
1334       text[0] = '\0';
1335       if(sscanf(line + length("%%BeginData:"), "%d %*s %256s", &num, text) >= 1) {
1336         if(strcmp(text, "Lines") == 0) {
1337           for(i = 0; i < num; i++) {
1338             fgets(line, sizeof(line), from);
1339             gtk_gs_doc_sink_write(to, line, strlen(line));
1340           }
1341         }
1342         else {
1343           while(num > BUFSIZ) {
1344             fread(buf, sizeof(char), BUFSIZ, from);
1345             gtk_gs_doc_sink_write(to, buf, BUFSIZ);
1346             num -= BUFSIZ;
1347           }
1348           fread(buf, sizeof(char), num, from);
1349           gtk_gs_doc_sink_write(to, buf, num);
1350         }
1351       }
1352     }
1353     else if(iscomment(line + 7, "Binary:")) {
1354       if(sscanf(line + length("%%BeginBinary:"), "%d", &num) == 1) {
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 }
1366
1367 /*
1368  *      pscopyuntil -- copy lines of Postscript from a section of one file
1369  *                     to another file until a particular comment is reached.
1370  *                     Automatically switch to binary copying whenever
1371  *                     %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
1372  *                     comments are encountered.
1373  */
1374
1375 char *
1376 pscopyuntil(FILE * from, GtkGSDocSink * to, long begin, long end,
1377             const char *comment)
1378 {
1379   char line[PSLINELENGTH];      /* 255 characters + 1 newline + 1 NULL */
1380   char text[PSLINELENGTH];      /* Temporary storage for text */
1381   unsigned int num;
1382   int comment_length;
1383   int i;
1384   char buf[BUFSIZ];
1385
1386   if(comment != NULL)
1387     comment_length = strlen(comment);
1388   else
1389     comment_length = 0;
1390   if(begin >= 0)
1391     fseek(from, begin, SEEK_SET);
1392
1393   while(ftell(from) < end && !feof(from)) {
1394     fgets(line, sizeof line, from);
1395
1396     /* iscomment cannot be used here,
1397      * because comment_length is not known at compile time. */
1398     if(comment != NULL && strncmp(line, comment, comment_length) == 0) {
1399       return g_strdup(line);
1400     }
1401     gtk_gs_doc_sink_write(to, line, strlen(line));
1402     if(!(DSCcomment(line) && iscomment(line + 2, "Begin"))) {
1403       /* Do nothing */
1404     }
1405     else if(iscomment(line + 7, "Data:")) {
1406       text[0] = '\0';
1407       if(sscanf(line + length("%%BeginData:"), "%d %*s %256s", &num, text) >= 1) {
1408         if(strcmp(text, "Lines") == 0) {
1409           for(i = 0; i < num; i++) {
1410             fgets(line, sizeof line, from);
1411             gtk_gs_doc_sink_write(to, line, strlen(line));
1412           }
1413         }
1414         else {
1415           while(num > BUFSIZ) {
1416             fread(buf, sizeof(char), BUFSIZ, from);
1417             gtk_gs_doc_sink_write(to, buf, BUFSIZ);
1418             num -= BUFSIZ;
1419           }
1420           fread(buf, sizeof(char), num, from);
1421           gtk_gs_doc_sink_write(to, buf, num);
1422         }
1423       }
1424     }
1425     else if(iscomment(line + 7, "Binary:")) {
1426       if(sscanf(line + length("%%BeginBinary:"), "%d", &num) == 1) {
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   return NULL;
1438 }
1439
1440 /*
1441  *      blank -- determine whether the line contains nothing but whitespace.
1442  */
1443
1444 static int
1445 blank(char *line)
1446 {
1447   char *cp = line;
1448
1449   while(*cp == ' ' || *cp == '\t')
1450     cp++;
1451   return *cp == '\n' || (*cp == '%' && (line[0] != '%' || line[1] != '%'));
1452 }
1453
1454 /*##########################################################*/
1455 /* pscopydoc */
1456 /* Copy the headers, marked pages, and trailer to fp */
1457 /*##########################################################*/
1458
1459 void
1460 pscopydoc(GtkGSDocSink * dest,
1461           char *src_filename, struct document *d, gint * pagelist)
1462 {
1463   FILE *src_file;
1464   char text[PSLINELENGTH];
1465   char *comment;
1466   gboolean pages_written = FALSE;
1467   gboolean pages_atend = FALSE;
1468   int pages;
1469   int page = 1;
1470   int i, j;
1471   int here;
1472
1473   src_file = fopen(src_filename, "r");
1474   i = 0;
1475   pages = 0;
1476   for(i = 0; i < d->numpages; i++) {
1477     if(pagelist[i])
1478       pages++;
1479   }
1480
1481   here = d->beginheader;
1482
1483   while((comment = pscopyuntil(src_file, dest, here, d->endheader, "%%Pages:"))) {
1484     here = ftell(src_file);
1485     if(pages_written || pages_atend) {
1486       g_free(comment);
1487       continue;
1488     }
1489     sscanf(comment + length("%%Pages:"), "%256s", text);
1490     if(strcmp(text, "(atend)") == 0) {
1491       gtk_gs_doc_sink_write(dest, comment, strlen(comment));
1492       pages_atend = TRUE;
1493     }
1494     else {
1495       switch (sscanf(comment + length("%%Pages:"), "%*d %d", &i)) {
1496       case 1:
1497         gtk_gs_doc_sink_printf(dest, "%%%%Pages: %d %d\n", pages, i);
1498         break;
1499       default:
1500         gtk_gs_doc_sink_printf(dest, "%%%%Pages: %d\n", pages);
1501         break;
1502       }
1503       pages_written = TRUE;
1504     }
1505     g_free(comment);
1506   }
1507   pscopyuntil(src_file, dest, d->beginpreview, d->endpreview, NULL);
1508   pscopyuntil(src_file, dest, d->begindefaults, d->enddefaults, NULL);
1509   pscopyuntil(src_file, dest, d->beginprolog, d->endprolog, NULL);
1510   pscopyuntil(src_file, dest, d->beginsetup, d->endsetup, NULL);
1511
1512   for(i = 0; i < d->numpages; i++) {
1513     if(d->pageorder == DESCEND)
1514       j = (d->numpages - 1) - i;
1515     else
1516       j = i;
1517     j = i;
1518     if(pagelist[j]) {
1519       comment = pscopyuntil(src_file, dest,
1520                             d->pages[i].begin, d->pages[i].end, "%%Page:");
1521       gtk_gs_doc_sink_printf(dest, "%%%%Page: %s %d\n",
1522                              d->pages[i].label, page++);
1523       g_free(comment);
1524       pscopyuntil(src_file, dest, -1, d->pages[i].end, NULL);
1525     }
1526   }
1527
1528   here = d->begintrailer;
1529   while((comment = pscopyuntil(src_file, dest, here, d->endtrailer,
1530                                "%%Pages:"))) {
1531     here = ftell(src_file);
1532     if(pages_written) {
1533       g_free(comment);
1534       continue;
1535     }
1536     switch (sscanf(comment + length("%%Pages:"), "%*d %d", &i)) {
1537     case 1:
1538       gtk_gs_doc_sink_printf(dest, "%%%%Pages: %d %d\n", pages, i);
1539       break;
1540     default:
1541       gtk_gs_doc_sink_printf(dest, "%%%%Pages: %d\n", pages);
1542       break;
1543     }
1544     pages_written = TRUE;
1545     g_free(comment);
1546   }
1547
1548   fclose(src_file);
1549 }
1550
1551 /*----------------------------------------------------------*/
1552 /* ps_io_init */
1553 /*----------------------------------------------------------*/
1554
1555 #define FD_FILE             (fd->file)
1556 #define FD_FILE_DESC        (fd->file_desc)
1557 #define FD_FILEPOS          (fd->filepos)
1558 #define FD_LINE_BEGIN       (fd->line_begin)
1559 #define FD_LINE_END         (fd->line_end)
1560 #define FD_LINE_LEN         (fd->line_len)
1561 #define FD_LINE_TERMCHAR    (fd->line_termchar)
1562 #define FD_BUF              (fd->buf)
1563 #define FD_BUF_END          (fd->buf_end)
1564 #define FD_BUF_SIZE         (fd->buf_size)
1565 #define FD_STATUS           (fd->status)
1566
1567 #define FD_STATUS_OKAY        0
1568 #define FD_STATUS_BUFTOOLARGE 1
1569 #define FD_STATUS_NOMORECHARS 2
1570
1571 #define LINE_CHUNK_SIZE     4096
1572 #define MAX_PS_IO_FGETCHARS_BUF_SIZE 57344
1573 #define BREAK_PS_IO_FGETCHARS_BUF_SIZE 49152
1574
1575 static FileData ps_io_init(file)
1576    FILE *file;
1577 {
1578    FileData fd;
1579    size_t size = sizeof(FileDataStruct);
1580
1581    fd = (FileData) g_malloc(size);
1582    memset((void*) fd ,0,(size_t)size);
1583
1584    rewind(file);
1585    FD_FILE      = file;
1586    FD_FILE_DESC = fileno(file);
1587    FD_FILEPOS   = ftell(file);
1588    FD_BUF_SIZE  = (2*LINE_CHUNK_SIZE)+1;
1589    FD_BUF       = g_malloc(FD_BUF_SIZE);
1590    FD_BUF[0]    = '\0';
1591    return(fd);
1592 }
1593
1594 /*----------------------------------------------------------*/
1595 /* ps_io_exit */
1596 /*----------------------------------------------------------*/
1597
1598 static void
1599 ps_io_exit(fd)
1600    FileData fd;
1601 {
1602    g_free(FD_BUF);
1603    g_free(fd);
1604 }
1605
1606 /*----------------------------------------------------------*/
1607 /* ps_io_fseek */
1608 /*----------------------------------------------------------*/
1609
1610 /*static int
1611 ps_io_fseek(fd,offset)
1612    FileData fd;
1613    int offset;
1614 {
1615    int status;
1616    status=fseek(FD_FILE,(long)offset,SEEK_SET);
1617    FD_BUF_END = FD_LINE_BEGIN = FD_LINE_END = FD_LINE_LEN = 0;
1618    FD_FILEPOS = offset;
1619    FD_STATUS  = FD_STATUS_OKAY;
1620    return(status);
1621 }*/
1622
1623 /*----------------------------------------------------------*/
1624 /* ps_io_ftell */
1625 /*----------------------------------------------------------*/
1626
1627 /*static int
1628 ps_io_ftell(fd)
1629    FileData fd;
1630 {
1631    return(FD_FILEPOS);
1632 }*/
1633
1634 /*----------------------------------------------------------*/
1635 /* ps_io_fgetchars */
1636 /*----------------------------------------------------------*/
1637
1638 #ifdef USE_MEMMOVE_CODE
1639 static void ps_memmove (d, s, l)
1640   char *d;
1641   const char *s;
1642   unsigned l;
1643 {
1644   if (s < d) for (s += l, d += l; l; --l) *--d = *--s;
1645   else if (s != d) for (; l; --l)         *d++ = *s++;
1646 }
1647 #else
1648 #   define ps_memmove memmove
1649 #endif
1650
1651 static char * ps_io_fgetchars(fd,num)
1652    FileData fd;
1653    int num;
1654 {
1655    char *eol=NULL,*tmp;
1656    size_t size_of_char = sizeof(char);
1657
1658    if (FD_STATUS != FD_STATUS_OKAY) {
1659       return(NULL);
1660    }
1661
1662    FD_BUF[FD_LINE_END] = FD_LINE_TERMCHAR; /* restoring char previously exchanged against '\0' */
1663    FD_LINE_BEGIN       = FD_LINE_END;
1664
1665    do {
1666       if (num<0) { /* reading whole line */
1667          if (FD_BUF_END-FD_LINE_END) {
1668             /* strpbrk is faster but fails on lines with embedded NULLs 
1669               eol = strpbrk(FD_BUF+FD_LINE_END,"\n\r");
1670             */
1671             tmp = FD_BUF + FD_BUF_END;
1672             eol = FD_BUF + FD_LINE_END;
1673             while (eol < tmp && *eol != '\n' && *eol != '\r') eol++;
1674             if (eol >= tmp) eol = NULL;
1675             if (eol) {
1676                if (*eol=='\r' && *(eol+1)=='\n') eol += 2;
1677                else eol++;
1678                break;
1679             }
1680          }
1681       } else { /* reading specified num of chars */
1682          if (FD_BUF_END >= FD_LINE_BEGIN+num) {
1683             eol = FD_BUF+FD_LINE_BEGIN+num;
1684             break;
1685          }
1686       }
1687
1688       if (FD_BUF_END - FD_LINE_BEGIN > BREAK_PS_IO_FGETCHARS_BUF_SIZE) {
1689         eol = FD_BUF + FD_BUF_END - 1;
1690         break;
1691       }
1692
1693       while (FD_BUF_SIZE < FD_BUF_END+LINE_CHUNK_SIZE+1) {
1694          if (FD_BUF_SIZE > MAX_PS_IO_FGETCHARS_BUF_SIZE) {
1695            /* we should never get here, since the line is broken
1696              artificially after BREAK_PS_IO_FGETCHARS_BUF_SIZE bytes. */
1697             fprintf(stderr, "gv: ps_io_fgetchars: Fatal Error: buffer became too large.\n");
1698             exit(-1);
1699          }
1700          if (FD_LINE_BEGIN) {
1701             ps_memmove((void*)FD_BUF,(void*)(FD_BUF+FD_LINE_BEGIN),
1702                     ((size_t)(FD_BUF_END-FD_LINE_BEGIN+1))*size_of_char);
1703             FD_BUF_END    -= FD_LINE_BEGIN; 
1704             FD_LINE_BEGIN  = 0;
1705          } else {
1706             FD_BUF_SIZE    = FD_BUF_SIZE+LINE_CHUNK_SIZE+1;
1707             FD_BUF         = g_realloc(FD_BUF,FD_BUF_SIZE);
1708          }
1709       }
1710
1711       FD_LINE_END = FD_BUF_END;
1712 #ifdef VMS
1713       /* different existing VMS file formats require that we use read here ###jp###,10/12/96 */ 
1714       if (num<0) FD_BUF_END += read(FD_FILE_DESC,FD_BUF+FD_BUF_END,LINE_CHUNK_SIZE);
1715       else       FD_BUF_END += fread(FD_BUF+FD_BUF_END,size_of_char,LINE_CHUNK_SIZE,FD_FILE);
1716 #else
1717       /* read() seems to fail sometimes (? ? ?) so we always use fread ###jp###,07/31/96*/
1718       FD_BUF_END += fread(FD_BUF+FD_BUF_END,size_of_char,LINE_CHUNK_SIZE,FD_FILE);
1719 #endif
1720
1721       FD_BUF[FD_BUF_END] = '\0';
1722       if (FD_BUF_END-FD_LINE_END == 0) {
1723          FD_STATUS = FD_STATUS_NOMORECHARS;
1724          return(NULL);
1725       }
1726    }
1727    while (1);
1728
1729    FD_LINE_END          = eol - FD_BUF;
1730    FD_LINE_LEN          = FD_LINE_END - FD_LINE_BEGIN;
1731    FD_LINE_TERMCHAR     = FD_BUF[FD_LINE_END];
1732    FD_BUF[FD_LINE_END]  = '\0';
1733 #ifdef USE_FTELL_FOR_FILEPOS
1734    if (FD_LINE_END==FD_BUF_END) {
1735       /*
1736       For VMS we cannot assume that the record is FD_LINE_LEN bytes long
1737       on the disk. For stream_lf and stream_cr that is true, but not for
1738       other formats, since VAXC/DECC converts the formatting into a single \n.
1739       eg. variable format files have a 2-byte length and padding to an even
1740       number of characters. So, we use ftell for each record.
1741       This still will not work if we need to fseek to a \n or \r inside a
1742       variable record (ftell always returns the start of the record in this
1743       case).
1744       (Tim Adye, adye@v2.rl.ac.uk)
1745       */
1746       FD_FILEPOS         = ftell(FD_FILE);
1747    } else
1748 #endif /* USE_FTELL_FOR_FILEPOS */
1749       FD_FILEPOS        += FD_LINE_LEN;
1750
1751    return(FD_BUF+FD_LINE_BEGIN);
1752 }
1753
1754 /*----------------------------------------------------------*/
1755 /*
1756    readline()
1757    Read the next line in the postscript file.
1758    Automatically skip over data (as indicated by
1759    %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
1760    comments.)
1761    Also, skip over included documents (as indicated by
1762    %%BeginDocument/%%EndDocument comments.)
1763 */
1764 /*----------------------------------------------------------*/
1765
1766 static char *readline (fd, lineP, positionP, line_lenP)
1767    FileData fd;
1768    char **lineP;
1769    long *positionP;
1770    unsigned int *line_lenP;
1771 {
1772    unsigned int nbytes=0;
1773    int skipped=0;
1774    char *line;
1775
1776    if (positionP) *positionP = FD_FILEPOS;
1777    line = ps_io_fgetchars(fd,-1);
1778    if (!line) {
1779       *line_lenP = 0;
1780       *lineP     = empty_string;
1781       return(NULL); 
1782    }
1783
1784    *line_lenP = FD_LINE_LEN;
1785
1786 #define IS_COMMENT(comment)                             \
1787            (DSCcomment(line) && iscomment(line+2,(comment)))
1788 #define IS_BEGIN(comment)                               \
1789            (iscomment(line+7,(comment)))
1790 #define SKIP_WHILE(cond)                                \
1791            while (readline(fd, &line, NULL, &nbytes) && (cond)) *line_lenP += nbytes;\
1792            skipped=1;
1793 #define SKIP_UNTIL_1(comment) {                         \
1794            SKIP_WHILE((!IS_COMMENT(comment)))           \
1795         }
1796 #define SKIP_UNTIL_2(comment1,comment2) {               \
1797            SKIP_WHILE((!IS_COMMENT(comment1) && !IS_COMMENT(comment2)))\
1798         }
1799
1800    if  (!IS_COMMENT("Begin"))     {} /* Do nothing */
1801    else if IS_BEGIN("Document:")  SKIP_UNTIL_1("EndDocument")
1802    else if IS_BEGIN("Feature:")   SKIP_UNTIL_1("EndFeature")
1803 #ifdef USE_ACROREAD_WORKAROUND
1804    else if IS_BEGIN("File")       SKIP_UNTIL_2("EndFile","EOF")
1805 #else
1806    else if IS_BEGIN("File")       SKIP_UNTIL_1("EndFile")
1807 #endif
1808    else if IS_BEGIN("Font")       SKIP_UNTIL_1("EndFont")
1809    else if IS_BEGIN("ProcSet")    SKIP_UNTIL_1("EndProcSet")
1810    else if IS_BEGIN("Resource")   SKIP_UNTIL_1("EndResource")
1811    else if IS_BEGIN("Data:")      {
1812       int  num;
1813       char text[101];
1814       if (FD_LINE_LEN > 100) FD_BUF[100] = '\0';
1815       text[0] = '\0';
1816       if (sscanf(line+length("%%BeginData:"), "%d %*s %s", &num, text) >= 1) {
1817          if (strcmp(text, "Lines") == 0) {
1818             while (num) {
1819                line = ps_io_fgetchars(fd,-1);
1820                if (line) *line_lenP += FD_LINE_LEN;
1821                num--;
1822             }
1823          } else {
1824             int read_chunk_size = LINE_CHUNK_SIZE;
1825             while (num>0) {
1826                if (num <= LINE_CHUNK_SIZE) read_chunk_size=num;
1827                line = ps_io_fgetchars(fd,read_chunk_size);
1828                if (line) *line_lenP += FD_LINE_LEN;
1829                num -= read_chunk_size;
1830             }
1831          }
1832       }
1833       SKIP_UNTIL_1("EndData")
1834    }
1835    else if IS_BEGIN("Binary:") {
1836       int  num;
1837       if (sscanf(line+length("%%BeginBinary:"), "%d", &num) == 1) {
1838          int read_chunk_size = LINE_CHUNK_SIZE;
1839          while (num>0) {
1840             if (num <= LINE_CHUNK_SIZE) read_chunk_size=num;
1841             line = ps_io_fgetchars(fd,read_chunk_size);
1842             if (line) *line_lenP += FD_LINE_LEN;
1843             num -= read_chunk_size;
1844          }
1845          SKIP_UNTIL_1("EndBinary")
1846       }
1847    }
1848
1849    if (skipped) {
1850       *line_lenP += nbytes;
1851       *lineP = skipped_line;      
1852    } else {
1853       *lineP = FD_BUF+FD_LINE_BEGIN;
1854    }
1855
1856    return(FD_BUF+FD_LINE_BEGIN);
1857 }