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