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