]> www.fi.muni.cz Git - evince.git/blob - ps/ps.c
Do not include ev-poppler.h when pdf is disabled.
[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 == GTK_GS_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 == GTK_GS_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   /* HACK: Mozilla 1.8 Workaround.
817   
818      It seems that Mozilla 1.8 generates important postscript code 
819      after the '%%EndProlog' and before the first page comment '%%Page: x y'.
820      See comment below also.
821    */
822    
823   if(doc->beginprolog && !doc->beginsetup) {
824       doc->lenprolog += section_len - line_len;
825       doc->endprolog = position;
826   }
827   
828   /* HACK: Windows NT Workaround
829
830      Mark Pfeifer (pfeiferm%ppddev@comet.cmis.abbott.com) noticed
831      about problems when viewing Windows NT 3.51 generated postscript
832      files with gv. He found that the relevant postscript files
833      show important postscript code after the '%%EndSetup' and before
834      the first page comment '%%Page: x y'.
835    */
836   if(doc->beginsetup) {
837     while(!(DSCcomment(line) &&
838             (iscomment(line + 2, "EndSetup") ||
839              (iscomment(line + 2, "Page:") ||
840               iscomment(line + 2, "Trailer") ||
841               (respect_eof && iscomment(line + 2, "EOF"))))) &&
842           (readline(fd, &line, &position, &line_len))) {
843       section_len += line_len;
844       doc->lensetup = section_len - line_len;
845       doc->endsetup = position;
846     }
847   }
848
849   /* Individual Pages */
850
851   if(beginsection == 0) {
852     beginsection = position;
853     section_len = line_len;
854   }
855   while(blank(line) && readline(fd, &line, &position, &line_len)) {
856     section_len += line_len;
857   }
858
859
860 newpage:
861   while(DSCcomment(line) && iscomment(line + 2, "Page:")) {
862     if(maxpages == 0) {
863       maxpages = 1;
864       doc->pages = pages_new(NULL, 0, maxpages);
865     }
866     label = get_next_text(line + length("%%Page:"), &next_char);
867     if(sscanf(next_char, "%d", &thispage) != 1)
868       thispage = 0;
869     if(nextpage == 1) {
870       ignore = thispage != 1;
871     }
872     if(!ignore && thispage != nextpage) {
873       g_free(label);
874       doc->numpages--;
875       goto continuepage;
876     }
877     nextpage++;
878     if(doc->numpages == maxpages) {
879       maxpages++;
880       doc->pages = pages_new(doc->pages, maxpages - 1, maxpages);
881     }
882     page_bb_set = NONE;
883     doc->pages[doc->numpages].label = label;
884     if(beginsection) {
885       doc->pages[doc->numpages].begin = beginsection;
886       beginsection = 0;
887     }
888     else {
889       doc->pages[doc->numpages].begin = position;
890       section_len = line_len;
891     }
892   continuepage:
893     while(readline(fd, &line, &position, &line_len) &&
894           !(DSCcomment(line) &&
895             (iscomment(line + 2, "Page:") ||
896              iscomment(line + 2, "Trailer") ||
897              (respect_eof && iscomment(line + 2, "EOF"))))) {
898       section_len += line_len;
899       if(!DSCcomment(line)) {
900         /* Do nothing */
901       }
902       else if(doc->pages[doc->numpages].orientation == NONE &&
903               iscomment(line + 2, "PageOrientation:")) {
904         sscanf(line + length("%%PageOrientation:"), "%256s", text);
905         if(strcmp(text, "Portrait") == 0) {
906           doc->pages[doc->numpages].orientation = GTK_GS_ORIENTATION_PORTRAIT;
907         }
908         else if(strcmp(text, "Landscape") == 0) {
909           doc->pages[doc->numpages].orientation = GTK_GS_ORIENTATION_LANDSCAPE;
910         }
911         else if(strcmp(text, "Seascape") == 0) {
912           doc->pages[doc->numpages].orientation = GTK_GS_ORIENTATION_SEASCAPE;
913         }
914       }
915       else if(doc->pages[doc->numpages].size == NULL &&
916               iscomment(line + 2, "PageMedia:")) {
917         cp = get_next_text(line + length("%%PageMedia:"), NULL);
918         for(dmp = doc->size, i = 0; i < doc->numsizes; i++, dmp++) {
919           if(strcmp(cp, dmp->name) == 0) {
920             doc->pages[doc->numpages].size = dmp;
921             break;
922           }
923         }
924         g_free(cp);
925       }
926       else if(doc->pages[doc->numpages].size == NULL &&
927               iscomment(line + 2, "PaperSize:")) {
928         cp = get_next_text(line + length("%%PaperSize:"), NULL);
929         for(dmp = doc->size, i = 0; i < doc->numsizes; i++, dmp++) {
930           /* Note: Paper size comment uses down cased paper size
931            * name.  Case insensitive compares are only used for
932            * PaperSize comments.
933            */
934           if(strcasecmp(cp, dmp->name) == 0) {
935             doc->pages[doc->numpages].size = dmp;
936             break;
937           }
938         }
939         g_free(cp);
940       }
941       else if((page_bb_set == NONE || page_bb_set == ATEND) &&
942               iscomment(line + 2, "PageBoundingBox:")) {
943         sscanf(line + length("%%PageBoundingBox:"), "%256s", text);
944         if(strcmp(text, "(atend)") == 0) {
945           page_bb_set = ATEND;
946         }
947         else {
948           if(sscanf
949              (line + length("%%PageBoundingBox:"), "%d %d %d %d",
950               &(doc->pages[doc->numpages].boundingbox[LLX]),
951               &(doc->pages[doc->numpages].boundingbox[LLY]),
952               &(doc->pages[doc->numpages].boundingbox[URX]),
953               &(doc->pages[doc->numpages].boundingbox[URY])) == 4) {
954             if(page_bb_set == NONE)
955               page_bb_set = 1;
956           }
957           else {
958             float fllx, flly, furx, fury;
959             if(sscanf(line + length("%%PageBoundingBox:"),
960                       "%f %f %f %f", &fllx, &flly, &furx, &fury) == 4) {
961               if(page_bb_set == NONE)
962                 page_bb_set = 1;
963               doc->pages[doc->numpages].boundingbox[LLX] = fllx;
964               doc->pages[doc->numpages].boundingbox[LLY] = flly;
965               doc->pages[doc->numpages].boundingbox[URX] = furx;
966               doc->pages[doc->numpages].boundingbox[URY] = fury;
967               if(fllx < doc->pages[doc->numpages].boundingbox[LLX])
968                 doc->pages[doc->numpages].boundingbox[LLX]--;
969               if(flly < doc->pages[doc->numpages].boundingbox[LLY])
970                 doc->pages[doc->numpages].boundingbox[LLY]--;
971               if(furx > doc->pages[doc->numpages].boundingbox[URX])
972                 doc->pages[doc->numpages].boundingbox[URX]++;
973               if(fury > doc->pages[doc->numpages].boundingbox[URY])
974                 doc->pages[doc->numpages].boundingbox[URY]++;
975             }
976           }
977         }
978       }
979     }
980     section_len += line_len;
981     doc->pages[doc->numpages].end = position;
982     doc->pages[doc->numpages].len = section_len - line_len;
983     doc->numpages++;
984   }
985
986   /* Document Trailer */
987
988   if(beginsection) {
989     doc->begintrailer = beginsection;
990     beginsection = 0;
991   }
992   else {
993     doc->begintrailer = position;
994     section_len = line_len;
995   }
996
997   preread = 1;
998   while((preread ||
999          readline(fd, &line, &position, &line_len)) &&
1000         !(respect_eof && DSCcomment(line) && iscomment(line + 2, "EOF"))) {
1001     if(!preread)
1002       section_len += line_len;
1003     preread = 0;
1004     if(!DSCcomment(line)) {
1005       /* Do nothing */
1006     }
1007     else if(iscomment(line + 2, "Page:")) {
1008       g_free(get_next_text(line + length("%%Page:"), &next_char));
1009       if(sscanf(next_char, "%d", &thispage) != 1)
1010         thispage = 0;
1011       if(!ignore && thispage == nextpage) {
1012         if(doc->numpages > 0) {
1013           doc->pages[doc->numpages - 1].end = position;
1014           doc->pages[doc->numpages - 1].len += section_len - line_len;
1015         }
1016         else {
1017           if(doc->endsetup) {
1018             doc->endsetup = position;
1019             doc->endsetup += section_len - line_len;
1020           }
1021           else if(doc->endprolog) {
1022             doc->endprolog = position;
1023             doc->endprolog += section_len - line_len;
1024           }
1025         }
1026         goto newpage;
1027       }
1028     }
1029     else if(!respect_eof && iscomment(line + 2, "Trailer")) {
1030       /* What we thought was the start of the trailer was really */
1031       /* the trailer of an EPS on the page. */
1032       /* Set the end of the page to this trailer and keep scanning. */
1033       if(doc->numpages > 0) {
1034         doc->pages[doc->numpages - 1].end = position;
1035         doc->pages[doc->numpages - 1].len += section_len - line_len;
1036       }
1037       doc->begintrailer = position;
1038       section_len = line_len;
1039     }
1040     else if(bb_set == ATEND && iscomment(line + 2, "BoundingBox:")) {
1041       if(sscanf(line + length("%%BoundingBox:"), "%d %d %d %d",
1042                 &(doc->boundingbox[LLX]),
1043                 &(doc->boundingbox[LLY]),
1044                 &(doc->boundingbox[URX]), &(doc->boundingbox[URY])) != 4) {
1045         float fllx, flly, furx, fury;
1046         if(sscanf(line + length("%%BoundingBox:"), "%f %f %f %f",
1047                   &fllx, &flly, &furx, &fury) == 4) {
1048           doc->boundingbox[LLX] = fllx;
1049           doc->boundingbox[LLY] = flly;
1050           doc->boundingbox[URX] = furx;
1051           doc->boundingbox[URY] = fury;
1052           if(fllx < doc->boundingbox[LLX])
1053             doc->boundingbox[LLX]--;
1054           if(flly < doc->boundingbox[LLY])
1055             doc->boundingbox[LLY]--;
1056           if(furx > doc->boundingbox[URX])
1057             doc->boundingbox[URX]++;
1058           if(fury > doc->boundingbox[URY])
1059             doc->boundingbox[URY]++;
1060         }
1061       }
1062     }
1063     else if(orientation_set == ATEND && iscomment(line + 2, "Orientation:")) {
1064       sscanf(line + length("%%Orientation:"), "%256s", text);
1065       if(strcmp(text, "Portrait") == 0) {
1066         doc->orientation = GTK_GS_ORIENTATION_PORTRAIT;
1067       }
1068       else if(strcmp(text, "Landscape") == 0) {
1069         doc->orientation = GTK_GS_ORIENTATION_LANDSCAPE;
1070       }
1071       else if(strcmp(text, "Seascape") == 0) {
1072         doc->orientation = GTK_GS_ORIENTATION_SEASCAPE;
1073       }
1074     }
1075     else if(page_order_set == ATEND && iscomment(line + 2, "PageOrder:")) {
1076       sscanf(line + length("%%PageOrder:"), "%256s", text);
1077       if(strcmp(text, "Ascend") == 0) {
1078         doc->pageorder = ASCEND;
1079       }
1080       else if(strcmp(text, "Descend") == 0) {
1081         doc->pageorder = DESCEND;
1082       }
1083       else if(strcmp(text, "Special") == 0) {
1084         doc->pageorder = SPECIAL;
1085       }
1086     }
1087     else if(pages_set == ATEND && iscomment(line + 2, "Pages:")) {
1088       if(sscanf(line + length("%%Pages:"), "%*u %d", &i) == 1) {
1089         if(page_order_set == NONE) {
1090           if(i == -1)
1091             doc->pageorder = DESCEND;
1092           else if(i == 0)
1093             doc->pageorder = SPECIAL;
1094           else if(i == 1)
1095             doc->pageorder = ASCEND;
1096         }
1097       }
1098     }
1099   }
1100   section_len += line_len;
1101   if(DSCcomment(line) && iscomment(line + 2, "EOF")) {
1102     readline(fd, &line, &position, &line_len);
1103     section_len += line_len;
1104   }
1105   doc->endtrailer = position;
1106   doc->lentrailer = section_len - line_len;
1107
1108 #if 0
1109   section_len = line_len;
1110   preread = 1;
1111   while(preread || readline(line, sizeof line, file, &position, &line_len)) {
1112     if(!preread)
1113       section_len += line_len;
1114     preread = 0;
1115     if(DSCcomment(line) && iscomment(line + 2, "Page:")) {
1116       g_free(get_next_text(line + length("%%Page:"), &next_char));
1117       if(sscanf(next_char, "%d", &thispage) != 1)
1118         thispage = 0;
1119       if(!ignore && thispage == nextpage) {
1120         if(doc->numpages > 0) {
1121           doc->pages[doc->numpages - 1].end = position;
1122           doc->pages[doc->numpages - 1].len += doc->lentrailer +
1123             section_len - line_len;
1124         }
1125         else {
1126           if(doc->endsetup) {
1127             doc->endsetup = position;
1128             doc->endsetup += doc->lentrailer + section_len - line_len;
1129           }
1130           else if(doc->endprolog) {
1131             doc->endprolog = position;
1132             doc->endprolog += doc->lentrailer + section_len - line_len;
1133           }
1134         }
1135         goto newpage;
1136       }
1137     }
1138   }
1139 #endif
1140   return doc;
1141 }
1142
1143 /*
1144  *      psfree -- free dynamic storage associated with document structure.
1145  */
1146
1147 void
1148 psfree(doc)
1149      struct document *doc;
1150 {
1151   int i;
1152
1153   if(doc) {
1154     /*
1155        printf("This document exists\n");
1156      */
1157     for(i = 0; i < doc->numpages; i++) {
1158       if(doc->pages[i].label)
1159         g_free(doc->pages[i].label);
1160     }
1161     for(i = 0; i < doc->numsizes; i++) {
1162       if(doc->size[i].name)
1163         g_free(doc->size[i].name);
1164     }
1165     if(doc->title)
1166       g_free(doc->title);
1167     if(doc->date)
1168       g_free(doc->date);
1169     if(doc->creator)
1170       g_free(doc->creator);
1171     if(doc->pages)
1172       g_free(doc->pages);
1173     if(doc->size)
1174       g_free(doc->size);
1175     g_free(doc);
1176   }
1177 }
1178
1179 /*
1180  * gettextine -- skip over white space and return the rest of the line.
1181  *               If the text begins with '(' return the text string
1182  *               using get_next_text().
1183  */
1184
1185 static char *
1186 gettextline(char *line)
1187 {
1188   char *cp;
1189
1190   while(*line && (*line == ' ' || *line == '\t'))
1191     line++;
1192   if(*line == '(') {
1193     return get_next_text(line, NULL);
1194   }
1195   else {
1196     if(strlen(line) == 0)
1197       return NULL;
1198
1199     cp = g_strdup(line);
1200
1201     /* Remove end of line */
1202     if(cp[strlen(line) - 2] == '\r' && cp[strlen(line) - 1] == '\n')
1203       /* Handle DOS \r\n */
1204       cp[strlen(line) - 2] = '\0';
1205     else if(cp[strlen(line) - 1] == '\n' || cp[strlen(line) - 1] == '\r')
1206       /* Handle mac and unix */
1207       cp[strlen(line) - 1] = '\0';
1208
1209     return cp;
1210   }
1211 }
1212
1213 /*
1214  *      get_next_text -- return the next text string on the line.
1215  *                 return NULL if nothing is present.
1216  */
1217
1218 static char *
1219 get_next_text(line, next_char)
1220      char *line;
1221      char **next_char;
1222 {
1223   char text[PSLINELENGTH];      /* Temporary storage for text */
1224   char *cp;
1225   int quoted = 0;
1226
1227   while(*line && (*line == ' ' || *line == '\t'))
1228     line++;
1229   cp = text;
1230   if(*line == '(') {
1231     int level = 0;
1232     quoted = 1;
1233     line++;
1234     while(*line && !(*line == ')' && level == 0)
1235           && (cp - text) < PSLINELENGTH - 1) {
1236       if(*line == '\\') {
1237         if(*(line + 1) == 'n') {
1238           *cp++ = '\n';
1239           line += 2;
1240         }
1241         else if(*(line + 1) == 'r') {
1242           *cp++ = '\r';
1243           line += 2;
1244         }
1245         else if(*(line + 1) == 't') {
1246           *cp++ = '\t';
1247           line += 2;
1248         }
1249         else if(*(line + 1) == 'b') {
1250           *cp++ = '\b';
1251           line += 2;
1252         }
1253         else if(*(line + 1) == 'f') {
1254           *cp++ = '\f';
1255           line += 2;
1256         }
1257         else if(*(line + 1) == '\\') {
1258           *cp++ = '\\';
1259           line += 2;
1260         }
1261         else if(*(line + 1) == '(') {
1262           *cp++ = '(';
1263           line += 2;
1264         }
1265         else if(*(line + 1) == ')') {
1266           *cp++ = ')';
1267           line += 2;
1268         }
1269         else if(*(line + 1) >= '0' && *(line + 1) <= '9') {
1270           if(*(line + 2) >= '0' && *(line + 2) <= '9') {
1271             if(*(line + 3) >= '0' && *(line + 3) <= '9') {
1272               *cp++ =
1273                 ((*(line + 1) - '0') * 8 + *(line + 2) -
1274                  '0') * 8 + *(line + 3) - '0';
1275               line += 4;
1276             }
1277             else {
1278               *cp++ = (*(line + 1) - '0') * 8 + *(line + 2) - '0';
1279               line += 3;
1280             }
1281           }
1282           else {
1283             *cp++ = *(line + 1) - '0';
1284             line += 2;
1285           }
1286         }
1287         else {
1288           line++;
1289           *cp++ = *line++;
1290         }
1291       }
1292       else if(*line == '(') {
1293         level++;
1294         *cp++ = *line++;
1295       }
1296       else if(*line == ')') {
1297         level--;
1298         *cp++ = *line++;
1299       }
1300       else {
1301         *cp++ = *line++;
1302       }
1303     }
1304   }
1305   else {
1306     while(*line && !(*line == ' ' || *line == '\t' || *line == '\n')
1307           && (cp - text) < PSLINELENGTH - 1)
1308       *cp++ = *line++;
1309   }
1310   *cp = '\0';
1311   if(next_char)
1312     *next_char = line;
1313   if(!quoted && strlen(text) == 0)
1314     return NULL;
1315   return g_strdup(text);
1316 }
1317
1318 /*
1319  *      pscopy -- copy lines of Postscript from a section of one file
1320  *                to another file.
1321  *                Automatically switch to binary copying whenever
1322  *                %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
1323  *                comments are encountered.
1324  */
1325
1326 void
1327 pscopy(from, to, begin, end)
1328      FILE *from;
1329      GtkGSDocSink *to;
1330      long begin;                /* set negative to avoid initial seek */
1331      long end;
1332 {
1333   char line[PSLINELENGTH];      /* 255 characters + 1 newline + 1 NULL */
1334   char text[PSLINELENGTH];      /* Temporary storage for text */
1335   unsigned int num;
1336   int i;
1337   char buf[BUFSIZ];
1338
1339   if(begin >= 0)
1340     fseek(from, begin, SEEK_SET);
1341   while(ftell(from) < end) {
1342     fgets(line, sizeof line, from);
1343     gtk_gs_doc_sink_write(to, line, strlen(line));
1344
1345     if(!(DSCcomment(line) && iscomment(line + 2, "Begin"))) {
1346       /* Do nothing */
1347     }
1348     else if(iscomment(line + 7, "Data:")) {
1349       text[0] = '\0';
1350       if(sscanf(line + length("%%BeginData:"), "%d %*s %256s", &num, text) >= 1) {
1351         if(strcmp(text, "Lines") == 0) {
1352           for(i = 0; i < num; i++) {
1353             fgets(line, sizeof(line), from);
1354             gtk_gs_doc_sink_write(to, line, strlen(line));
1355           }
1356         }
1357         else {
1358           while(num > BUFSIZ) {
1359             fread(buf, sizeof(char), BUFSIZ, from);
1360             gtk_gs_doc_sink_write(to, buf, BUFSIZ);
1361             num -= BUFSIZ;
1362           }
1363           fread(buf, sizeof(char), num, from);
1364           gtk_gs_doc_sink_write(to, buf, num);
1365         }
1366       }
1367     }
1368     else if(iscomment(line + 7, "Binary:")) {
1369       if(sscanf(line + length("%%BeginBinary:"), "%d", &num) == 1) {
1370         while(num > BUFSIZ) {
1371           fread(buf, sizeof(char), BUFSIZ, from);
1372           gtk_gs_doc_sink_write(to, buf, BUFSIZ);
1373           num -= BUFSIZ;
1374         }
1375         fread(buf, sizeof(char), num, from);
1376         gtk_gs_doc_sink_write(to, buf, num);
1377       }
1378     }
1379   }
1380 }
1381
1382 /*
1383  *      pscopyuntil -- copy lines of Postscript from a section of one file
1384  *                     to another file until a particular comment is reached.
1385  *                     Automatically switch to binary copying whenever
1386  *                     %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
1387  *                     comments are encountered.
1388  */
1389
1390 char *
1391 pscopyuntil(FILE * from, GtkGSDocSink * to, long begin, long end,
1392             const char *comment)
1393 {
1394   char line[PSLINELENGTH];      /* 255 characters + 1 newline + 1 NULL */
1395   char text[PSLINELENGTH];      /* Temporary storage for text */
1396   unsigned int num;
1397   int comment_length;
1398   int i;
1399   char buf[BUFSIZ];
1400
1401   if(comment != NULL)
1402     comment_length = strlen(comment);
1403   else
1404     comment_length = 0;
1405   if(begin >= 0)
1406     fseek(from, begin, SEEK_SET);
1407
1408   while(ftell(from) < end && !feof(from)) {
1409     fgets(line, sizeof line, from);
1410
1411     /* iscomment cannot be used here,
1412      * because comment_length is not known at compile time. */
1413     if(comment != NULL && strncmp(line, comment, comment_length) == 0) {
1414       return g_strdup(line);
1415     }
1416     gtk_gs_doc_sink_write(to, line, strlen(line));
1417     if(!(DSCcomment(line) && iscomment(line + 2, "Begin"))) {
1418       /* Do nothing */
1419     }
1420     else if(iscomment(line + 7, "Data:")) {
1421       text[0] = '\0';
1422       if(sscanf(line + length("%%BeginData:"), "%d %*s %256s", &num, text) >= 1) {
1423         if(strcmp(text, "Lines") == 0) {
1424           for(i = 0; i < num; i++) {
1425             fgets(line, sizeof line, from);
1426             gtk_gs_doc_sink_write(to, line, strlen(line));
1427           }
1428         }
1429         else {
1430           while(num > BUFSIZ) {
1431             fread(buf, sizeof(char), BUFSIZ, from);
1432             gtk_gs_doc_sink_write(to, buf, BUFSIZ);
1433             num -= BUFSIZ;
1434           }
1435           fread(buf, sizeof(char), num, from);
1436           gtk_gs_doc_sink_write(to, buf, num);
1437         }
1438       }
1439     }
1440     else if(iscomment(line + 7, "Binary:")) {
1441       if(sscanf(line + length("%%BeginBinary:"), "%d", &num) == 1) {
1442         while(num > BUFSIZ) {
1443           fread(buf, sizeof(char), BUFSIZ, from);
1444           gtk_gs_doc_sink_write(to, buf, BUFSIZ);
1445           num -= BUFSIZ;
1446         }
1447         fread(buf, sizeof(char), num, from);
1448         gtk_gs_doc_sink_write(to, buf, num);
1449       }
1450     }
1451   }
1452   return NULL;
1453 }
1454
1455 /*
1456  *      blank -- determine whether the line contains nothing but whitespace.
1457  */
1458
1459 static int
1460 blank(char *line)
1461 {
1462   char *cp = line;
1463
1464   while(*cp == ' ' || *cp == '\t')
1465     cp++;
1466   return *cp == '\n' || (*cp == '%' && (line[0] != '%' || line[1] != '%'));
1467 }
1468
1469 /*##########################################################*/
1470 /* pscopydoc */
1471 /* Copy the headers, marked pages, and trailer to fp */
1472 /*##########################################################*/
1473
1474 void
1475 pscopydoc(GtkGSDocSink * dest,
1476           char *src_filename, struct document *d, gint * pagelist)
1477 {
1478   FILE *src_file;
1479   char text[PSLINELENGTH];
1480   char *comment;
1481   gboolean pages_written = FALSE;
1482   gboolean pages_atend = FALSE;
1483   int pages;
1484   int page = 1;
1485   int i, j;
1486   int here;
1487
1488   src_file = fopen(src_filename, "r");
1489   i = 0;
1490   pages = 0;
1491   for(i = 0; i < d->numpages; i++) {
1492     if(pagelist[i])
1493       pages++;
1494   }
1495
1496   here = d->beginheader;
1497
1498   while((comment = pscopyuntil(src_file, dest, here, d->endheader, "%%Pages:"))) {
1499     here = ftell(src_file);
1500     if(pages_written || pages_atend) {
1501       g_free(comment);
1502       continue;
1503     }
1504     sscanf(comment + length("%%Pages:"), "%256s", text);
1505     if(strcmp(text, "(atend)") == 0) {
1506       gtk_gs_doc_sink_write(dest, comment, strlen(comment));
1507       pages_atend = TRUE;
1508     }
1509     else {
1510       switch (sscanf(comment + length("%%Pages:"), "%*d %d", &i)) {
1511       case 1:
1512         gtk_gs_doc_sink_printf(dest, "%%%%Pages: %d %d\n", pages, i);
1513         break;
1514       default:
1515         gtk_gs_doc_sink_printf(dest, "%%%%Pages: %d\n", pages);
1516         break;
1517       }
1518       pages_written = TRUE;
1519     }
1520     g_free(comment);
1521   }
1522   pscopyuntil(src_file, dest, d->beginpreview, d->endpreview, NULL);
1523   pscopyuntil(src_file, dest, d->begindefaults, d->enddefaults, NULL);
1524   pscopyuntil(src_file, dest, d->beginprolog, d->endprolog, NULL);
1525   pscopyuntil(src_file, dest, d->beginsetup, d->endsetup, NULL);
1526
1527   for(i = 0; i < d->numpages; i++) {
1528     if(d->pageorder == DESCEND)
1529       j = (d->numpages - 1) - i;
1530     else
1531       j = i;
1532     j = i;
1533     if(pagelist[j]) {
1534       comment = pscopyuntil(src_file, dest,
1535                             d->pages[i].begin, d->pages[i].end, "%%Page:");
1536       gtk_gs_doc_sink_printf(dest, "%%%%Page: %s %d\n",
1537                              d->pages[i].label, page++);
1538       g_free(comment);
1539       pscopyuntil(src_file, dest, -1, d->pages[i].end, NULL);
1540     }
1541   }
1542
1543   here = d->begintrailer;
1544   while((comment = pscopyuntil(src_file, dest, here, d->endtrailer,
1545                                "%%Pages:"))) {
1546     here = ftell(src_file);
1547     if(pages_written) {
1548       g_free(comment);
1549       continue;
1550     }
1551     switch (sscanf(comment + length("%%Pages:"), "%*d %d", &i)) {
1552     case 1:
1553       gtk_gs_doc_sink_printf(dest, "%%%%Pages: %d %d\n", pages, i);
1554       break;
1555     default:
1556       gtk_gs_doc_sink_printf(dest, "%%%%Pages: %d\n", pages);
1557       break;
1558     }
1559     pages_written = TRUE;
1560     g_free(comment);
1561   }
1562
1563   fclose(src_file);
1564 }
1565
1566 /*----------------------------------------------------------*/
1567 /* ps_io_init */
1568 /*----------------------------------------------------------*/
1569
1570 #define FD_FILE             (fd->file)
1571 #define FD_FILE_DESC        (fd->file_desc)
1572 #define FD_FILEPOS          (fd->filepos)
1573 #define FD_LINE_BEGIN       (fd->line_begin)
1574 #define FD_LINE_END         (fd->line_end)
1575 #define FD_LINE_LEN         (fd->line_len)
1576 #define FD_LINE_TERMCHAR    (fd->line_termchar)
1577 #define FD_BUF              (fd->buf)
1578 #define FD_BUF_END          (fd->buf_end)
1579 #define FD_BUF_SIZE         (fd->buf_size)
1580 #define FD_STATUS           (fd->status)
1581
1582 #define FD_STATUS_OKAY        0
1583 #define FD_STATUS_BUFTOOLARGE 1
1584 #define FD_STATUS_NOMORECHARS 2
1585
1586 #define LINE_CHUNK_SIZE     4096
1587 #define MAX_PS_IO_FGETCHARS_BUF_SIZE 57344
1588 #define BREAK_PS_IO_FGETCHARS_BUF_SIZE 49152
1589
1590 static FileData ps_io_init(file)
1591    FILE *file;
1592 {
1593    FileData fd;
1594    size_t size = sizeof(FileDataStruct);
1595
1596    fd = (FileData) g_malloc(size);
1597    memset((void*) fd ,0,(size_t)size);
1598
1599    rewind(file);
1600    FD_FILE      = file;
1601    FD_FILE_DESC = fileno(file);
1602    FD_FILEPOS   = ftell(file);
1603    FD_BUF_SIZE  = (2*LINE_CHUNK_SIZE)+1;
1604    FD_BUF       = g_malloc(FD_BUF_SIZE);
1605    FD_BUF[0]    = '\0';
1606    return(fd);
1607 }
1608
1609 /*----------------------------------------------------------*/
1610 /* ps_io_exit */
1611 /*----------------------------------------------------------*/
1612
1613 static void
1614 ps_io_exit(fd)
1615    FileData fd;
1616 {
1617    g_free(FD_BUF);
1618    g_free(fd);
1619 }
1620
1621 /*----------------------------------------------------------*/
1622 /* ps_io_fseek */
1623 /*----------------------------------------------------------*/
1624
1625 /*static int
1626 ps_io_fseek(fd,offset)
1627    FileData fd;
1628    int offset;
1629 {
1630    int status;
1631    status=fseek(FD_FILE,(long)offset,SEEK_SET);
1632    FD_BUF_END = FD_LINE_BEGIN = FD_LINE_END = FD_LINE_LEN = 0;
1633    FD_FILEPOS = offset;
1634    FD_STATUS  = FD_STATUS_OKAY;
1635    return(status);
1636 }*/
1637
1638 /*----------------------------------------------------------*/
1639 /* ps_io_ftell */
1640 /*----------------------------------------------------------*/
1641
1642 /*static int
1643 ps_io_ftell(fd)
1644    FileData fd;
1645 {
1646    return(FD_FILEPOS);
1647 }*/
1648
1649 /*----------------------------------------------------------*/
1650 /* ps_io_fgetchars */
1651 /*----------------------------------------------------------*/
1652
1653 #ifdef USE_MEMMOVE_CODE
1654 static void ps_memmove (d, s, l)
1655   char *d;
1656   const char *s;
1657   unsigned l;
1658 {
1659   if (s < d) for (s += l, d += l; l; --l) *--d = *--s;
1660   else if (s != d) for (; l; --l)         *d++ = *s++;
1661 }
1662 #else
1663 #   define ps_memmove memmove
1664 #endif
1665
1666 static char * ps_io_fgetchars(fd,num)
1667    FileData fd;
1668    int num;
1669 {
1670    char *eol=NULL,*tmp;
1671    size_t size_of_char = sizeof(char);
1672
1673    if (FD_STATUS != FD_STATUS_OKAY) {
1674       return(NULL);
1675    }
1676
1677    FD_BUF[FD_LINE_END] = FD_LINE_TERMCHAR; /* restoring char previously exchanged against '\0' */
1678    FD_LINE_BEGIN       = FD_LINE_END;
1679
1680    do {
1681       if (num<0) { /* reading whole line */
1682          if (FD_BUF_END-FD_LINE_END) {
1683             /* strpbrk is faster but fails on lines with embedded NULLs 
1684               eol = strpbrk(FD_BUF+FD_LINE_END,"\n\r");
1685             */
1686             tmp = FD_BUF + FD_BUF_END;
1687             eol = FD_BUF + FD_LINE_END;
1688             while (eol < tmp && *eol != '\n' && *eol != '\r') eol++;
1689             if (eol >= tmp) eol = NULL;
1690             if (eol) {
1691                if (*eol=='\r' && *(eol+1)=='\n') eol += 2;
1692                else eol++;
1693                break;
1694             }
1695          }
1696       } else { /* reading specified num of chars */
1697          if (FD_BUF_END >= FD_LINE_BEGIN+num) {
1698             eol = FD_BUF+FD_LINE_BEGIN+num;
1699             break;
1700          }
1701       }
1702
1703       if (FD_BUF_END - FD_LINE_BEGIN > BREAK_PS_IO_FGETCHARS_BUF_SIZE) {
1704         eol = FD_BUF + FD_BUF_END - 1;
1705         break;
1706       }
1707
1708       while (FD_BUF_SIZE < FD_BUF_END+LINE_CHUNK_SIZE+1) {
1709          if (FD_BUF_SIZE > MAX_PS_IO_FGETCHARS_BUF_SIZE) {
1710            /* we should never get here, since the line is broken
1711              artificially after BREAK_PS_IO_FGETCHARS_BUF_SIZE bytes. */
1712             fprintf(stderr, "gv: ps_io_fgetchars: Fatal Error: buffer became too large.\n");
1713             exit(-1);
1714          }
1715          if (FD_LINE_BEGIN) {
1716             ps_memmove((void*)FD_BUF,(void*)(FD_BUF+FD_LINE_BEGIN),
1717                     ((size_t)(FD_BUF_END-FD_LINE_BEGIN+1))*size_of_char);
1718             FD_BUF_END    -= FD_LINE_BEGIN; 
1719             FD_LINE_BEGIN  = 0;
1720          } else {
1721             FD_BUF_SIZE    = FD_BUF_SIZE+LINE_CHUNK_SIZE+1;
1722             FD_BUF         = g_realloc(FD_BUF,FD_BUF_SIZE);
1723          }
1724       }
1725
1726       FD_LINE_END = FD_BUF_END;
1727 #ifdef VMS
1728       /* different existing VMS file formats require that we use read here ###jp###,10/12/96 */ 
1729       if (num<0) FD_BUF_END += read(FD_FILE_DESC,FD_BUF+FD_BUF_END,LINE_CHUNK_SIZE);
1730       else       FD_BUF_END += fread(FD_BUF+FD_BUF_END,size_of_char,LINE_CHUNK_SIZE,FD_FILE);
1731 #else
1732       /* read() seems to fail sometimes (? ? ?) so we always use fread ###jp###,07/31/96*/
1733       FD_BUF_END += fread(FD_BUF+FD_BUF_END,size_of_char,LINE_CHUNK_SIZE,FD_FILE);
1734 #endif
1735
1736       FD_BUF[FD_BUF_END] = '\0';
1737       if (FD_BUF_END-FD_LINE_END == 0) {
1738          FD_STATUS = FD_STATUS_NOMORECHARS;
1739          return(NULL);
1740       }
1741    }
1742    while (1);
1743
1744    FD_LINE_END          = eol - FD_BUF;
1745    FD_LINE_LEN          = FD_LINE_END - FD_LINE_BEGIN;
1746    FD_LINE_TERMCHAR     = FD_BUF[FD_LINE_END];
1747    FD_BUF[FD_LINE_END]  = '\0';
1748 #ifdef USE_FTELL_FOR_FILEPOS
1749    if (FD_LINE_END==FD_BUF_END) {
1750       /*
1751       For VMS we cannot assume that the record is FD_LINE_LEN bytes long
1752       on the disk. For stream_lf and stream_cr that is true, but not for
1753       other formats, since VAXC/DECC converts the formatting into a single \n.
1754       eg. variable format files have a 2-byte length and padding to an even
1755       number of characters. So, we use ftell for each record.
1756       This still will not work if we need to fseek to a \n or \r inside a
1757       variable record (ftell always returns the start of the record in this
1758       case).
1759       (Tim Adye, adye@v2.rl.ac.uk)
1760       */
1761       FD_FILEPOS         = ftell(FD_FILE);
1762    } else
1763 #endif /* USE_FTELL_FOR_FILEPOS */
1764       FD_FILEPOS        += FD_LINE_LEN;
1765
1766    return(FD_BUF+FD_LINE_BEGIN);
1767 }
1768
1769 /*----------------------------------------------------------*/
1770 /*
1771    readline()
1772    Read the next line in the postscript file.
1773    Automatically skip over data (as indicated by
1774    %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
1775    comments.)
1776    Also, skip over included documents (as indicated by
1777    %%BeginDocument/%%EndDocument comments.)
1778 */
1779 /*----------------------------------------------------------*/
1780
1781 static char *readline (fd, lineP, positionP, line_lenP)
1782    FileData fd;
1783    char **lineP;
1784    long *positionP;
1785    unsigned int *line_lenP;
1786 {
1787    unsigned int nbytes=0;
1788    int skipped=0;
1789    char *line;
1790
1791    if (positionP) *positionP = FD_FILEPOS;
1792    line = ps_io_fgetchars(fd,-1);
1793    if (!line) {
1794       *line_lenP = 0;
1795       *lineP     = empty_string;
1796       return(NULL); 
1797    }
1798
1799    *line_lenP = FD_LINE_LEN;
1800
1801 #define IS_COMMENT(comment)                             \
1802            (DSCcomment(line) && iscomment(line+2,(comment)))
1803 #define IS_BEGIN(comment)                               \
1804            (iscomment(line+7,(comment)))
1805 #define SKIP_WHILE(cond)                                \
1806            while (readline(fd, &line, NULL, &nbytes) && (cond)) *line_lenP += nbytes;\
1807            skipped=1;
1808 #define SKIP_UNTIL_1(comment) {                         \
1809            SKIP_WHILE((!IS_COMMENT(comment)))           \
1810         }
1811 #define SKIP_UNTIL_2(comment1,comment2) {               \
1812            SKIP_WHILE((!IS_COMMENT(comment1) && !IS_COMMENT(comment2)))\
1813         }
1814
1815    if  (!IS_COMMENT("Begin"))     {} /* Do nothing */
1816    else if IS_BEGIN("Document:")  SKIP_UNTIL_1("EndDocument")
1817    else if IS_BEGIN("Feature:")   SKIP_UNTIL_1("EndFeature")
1818 #ifdef USE_ACROREAD_WORKAROUND
1819    else if IS_BEGIN("File")       SKIP_UNTIL_2("EndFile","EOF")
1820 #else
1821    else if IS_BEGIN("File")       SKIP_UNTIL_1("EndFile")
1822 #endif
1823    else if IS_BEGIN("Font")       SKIP_UNTIL_1("EndFont")
1824    else if IS_BEGIN("ProcSet")    SKIP_UNTIL_1("EndProcSet")
1825    else if IS_BEGIN("Resource")   SKIP_UNTIL_1("EndResource")
1826    else if IS_BEGIN("Data:")      {
1827       int  num;
1828       char text[101];
1829       if (FD_LINE_LEN > 100) FD_BUF[100] = '\0';
1830       text[0] = '\0';
1831       if (sscanf(line+length("%%BeginData:"), "%d %*s %s", &num, text) >= 1) {
1832          if (strcmp(text, "Lines") == 0) {
1833             while (num) {
1834                line = ps_io_fgetchars(fd,-1);
1835                if (line) *line_lenP += FD_LINE_LEN;
1836                num--;
1837             }
1838          } else {
1839             int read_chunk_size = LINE_CHUNK_SIZE;
1840             while (num>0) {
1841                if (num <= LINE_CHUNK_SIZE) read_chunk_size=num;
1842                line = ps_io_fgetchars(fd,read_chunk_size);
1843                if (line) *line_lenP += FD_LINE_LEN;
1844                num -= read_chunk_size;
1845             }
1846          }
1847       }
1848       SKIP_UNTIL_1("EndData")
1849    }
1850    else if IS_BEGIN("Binary:") {
1851       int  num;
1852       if (sscanf(line+length("%%BeginBinary:"), "%d", &num) == 1) {
1853          int read_chunk_size = LINE_CHUNK_SIZE;
1854          while (num>0) {
1855             if (num <= LINE_CHUNK_SIZE) read_chunk_size=num;
1856             line = ps_io_fgetchars(fd,read_chunk_size);
1857             if (line) *line_lenP += FD_LINE_LEN;
1858             num -= read_chunk_size;
1859          }
1860          SKIP_UNTIL_1("EndBinary")
1861       }
1862    }
1863
1864    if (skipped) {
1865       *line_lenP += nbytes;
1866       *lineP = skipped_line;      
1867    } else {
1868       *lineP = FD_BUF+FD_LINE_BEGIN;
1869    }
1870
1871    return(FD_BUF+FD_LINE_BEGIN);
1872 }