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