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