/*
 * ps.c -- Postscript scanning and copying routines.
 * Copyright (C) 1992  Timothy O. Theisen
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *   Author: Tim Theisen           Systems Programmer
 * Internet: tim@cs.wisc.edu       Department of Computer Sciences
 *     UUCP: uwvax!tim             University of Wisconsin-Madison
 *    Phone: (608)262-0438         1210 West Dayton Street
 *      FAX: (608)262-9777         Madison, WI   53706
 */

#include <stdio.h>
#include <stdlib.h>
#ifndef SEEK_SET
#define SEEK_SET 0
#endif
#ifndef BUFSIZ
#define BUFSIZ 1024
#endif
#include <ctype.h>
#include <string.h>
#include "ps.h"

#ifdef BSD4_2
#define memset(a,b,c) bzero(a,c)
#endif

/* length calculates string length at compile time */
/* can only be used with character constants */
#define length(a) (sizeof(a)-1)
#define iscomment(a, b)	(strncmp(a, b, length(b)) == 0)
#define DSCcomment(a) (a[0] == '%' && a[1] == '%')

	/* list of standard paper sizes from Adobe's PPD. */

struct documentmedia papersizes[] = {
    {"BBox",	         0,  0},
    {"Letter",		 612,  792,},
    {"Tabloid",		 792, 1224,},
    {"Ledger",		1224,  792,},
    {"Legal",		 612, 1008,},
    {"Statement",	 396,  612,},
    {"Executive",	 540,  720,},
    {"A3",		 842, 1190,},
    {"A4",		 595,  842,},
    {"A5",		 420,  595,},
    {"B4",		 729, 1032,},
    {"B5",		 516,  729,},
    {"Folio",		 612,  936,},
    {"Quarto",		 610,  780,},
    {"10x14",		 720, 1008,},
    {NULL,		   0,    0}
};


static char *readline();
static char *gettextline();
static char *gettext();
static int  blank();

/*
 *	psscan -- scan the PostScript file for document structuring comments.
 *
 *	This scanner is designed to retrieve the information necessary for
 *	the ghostview previewer.  It will scan files that conform to any
 *	version (1.0, 2.0, 2.1, or 3.0) of the document structuring conventions.
 *	It does not really care which version of comments the file contains.
 *	(The comments are largely upward compatible.)  It will scan a number
 *	of non-conforming documents.  (You could have part of the document
 *	conform to V2.0 and the rest conform to V3.0.  It would be similar
 *	to the DC-2 1/2+, it would look funny but it can still fly.)
 *
 *	This routine returns a pointer to the document structure.
 *	The structure contains the information relevant to previewing.
 *      These include EPSF flag (to tell if the file is a encapsulated figure),
 *      Page Media (for the Page Size), Bounding Box (to minimize backing
 *      pixmap size or determine window size for encapsulated PostScript), 
 *      Orientation of Paper (for default transformation matrix), and
 *      Page Order.  The title and CreationDate are also retrieved to
 *      help identify the document.
 *
 *      The following comments are examined:
 *
 *      Header section: 
 *      Must start with %!PS-Adobe-.  Version numbers ignored.
 *
 *      %!PS-Adobe-* [EPSF-*]
 *      %%BoundingBox: <int> <int> <int> <int>|(atend)
 *      %%CreationDate: <textline>
 *      %%Orientation: Portrait|Landscape|(atend)
 *      %%Pages: <uint> [<int>]|(atend)
 *      %%PageOrder: Ascend|Descend|Special|(atend)
 *      %%Title: <textline>
 *      %%DocumentMedia: <text> <real> <real> <real> <text> <text>
 *      %%DocumentPaperSizes: <text>
 *      %%EndComments
 *
 *      Note: Either the 3.0 or 2.0 syntax for %%Pages is accepted.
 *            Also either the 2.0 %%DocumentPaperSizes or the 3.0
 *            %%DocumentMedia comments are accepted as well.
 *
 *      The header section ends either explicitly with %%EndComments or
 *      implicitly with any line that does not begin with %X where X is
 *      a not whitespace character.
 *
 *      If the file is encapsulated PostScript the optional Preview section
 *      is next:
 *
 *      %%BeginPreview
 *      %%EndPreview
 *
 *      This section explicitly begins and ends with the above comments.
 *
 *      Next the Defaults section for version 3 page defaults:
 *
 *      %%BeginDefaults
 *      %%PageBoundingBox: <int> <int> <int> <int>
 *      %%PageOrientation: Portrait|Landscape
 *      %%PageMedia: <text>
 *      %%EndDefaults
 *
 *      This section explicitly begins and ends with the above comments.
 *
 *      The prolog section either explicitly starts with %%BeginProlog or
 *      implicitly with any nonblank line.
 *
 *      %%BeginProlog
 *      %%EndProlog
 *
 *      The Prolog should end with %%EndProlog, however the proglog implicitly
 *      ends when %%BeginSetup, %%Page, %%Trailer or %%EOF are encountered.
 *
 *      The Setup section is where the version 2 page defaults are found.
 *      This section either explicitly begins with %%BeginSetup or implicitly
 *      with any nonblank line after the Prolog.
 *
 *      %%BeginSetup
 *      %%PageBoundingBox: <int> <int> <int> <int>
 *      %%PageOrientation: Portrait|Landscape
 *      %%PaperSize: <text>
 *      %%EndSetup
 *
 *      The Setup should end with %%EndSetup, however the setup implicitly
 *      ends when %%Page, %%Trailer or %%EOF are encountered.
 *
 *      Next each page starts explicitly with %%Page and ends implicitly with
 *      %%Page or %%Trailer or %%EOF.  The following comments are recognized:
 *
 *      %%Page: <text> <uint>
 *      %%PageBoundingBox: <int> <int> <int> <int>|(atend)
 *      %%PageOrientation: Portrait|Landscape
 *      %%PageMedia: <text>
 *      %%PaperSize: <text>
 *
 *      The tralier section start explicitly with %%Trailer and end with %%EOF.
 *      The following comment are examined with the proper (atend) notation
 *      was used in the header:
 *
 *      %%Trailer
 *      %%BoundingBox: <int> <int> <int> <int>|(atend)
 *      %%Orientation: Portrait|Landscape|(atend)
 *      %%Pages: <uint> [<int>]|(atend)
 *      %%PageOrder: Ascend|Descend|Special|(atend)
 *      %%EOF
 *
 *
 *  + A DC-3 received severe damage to one of its wings.  The wing was a total
 *    loss.  There was no replacement readily available, so the mechanic
 *    installed a wing from a DC-2.
 */

struct document *psscan(FILE *file)
{
	struct document *doc;
	int bb_set = NONE;
	int pages_set = NONE;
	int page_order_set = NONE;
	int orientation_set = NONE;
	int page_bb_set = NONE;
	int page_media_set = NONE;
	int preread;		/* flag which tells the readline isn't needed */
	int i;
	unsigned int maxpages = 0;
	unsigned int nextpage = 1;	/* Next expected page */
	unsigned int thispage;
	int ignore = 0;		/* whether to ignore page ordinals */
	char *label;
	char line[PSLINELENGTH];	/* 255 characters + 1 newline + 1 NULL */
	char text[PSLINELENGTH];	/* Temporary storage for text */
	long position;		/* Position of the current line */
	long beginsection;		/* Position of the beginning of the section */
	unsigned int line_len; 	/* Length of the current line */
	unsigned int section_len;	/* Place to accumulate the section length */
	char *next_char;		/* 1st char after text returned by gettext() */
	char *cp;
	struct documentmedia *dmp;

	rewind(file);
	
	if (readline(line, sizeof line, file, &position, &line_len) == NULL) {
		fprintf(stderr, "Warning: empty file.\n");
		return(NULL);
	}

    /* Header comments */

	if (iscomment(line,"%!PS-Adobe-")) {
		doc = (struct document *) malloc(sizeof(struct document));
		if (doc == NULL) {
	    fprintf(stderr, "Fatal Error: Dynamic memory exhausted.\n");
	    exit(-1);
		}
		memset(doc, 0, sizeof(struct document));
		sscanf(line, "%*s %s", text);
		doc->epsf = iscomment(text, "EPSF-");
		doc->beginheader = position;
		section_len = line_len;
	} else {
		return(NULL);
	}

	preread = 0;
	
	while (preread || readline(line, sizeof line, file, &position, &line_len)) {
		if (!preread) {
			section_len += line_len;
		}
		preread = 0;
		
		if (line[0] != '%' || iscomment(line+1, "%EndComments") || line[1] == ' ' 
									|| line[1] == '\t' || line[1] == '\n' || !isprint(line[1])) {
	    break;
		} else if (line[1] != '%') {
	    /* Do nothing */
		} else if (doc->title == NULL && iscomment(line+2, "Title:")) {
	    doc->title = gettextline(line+length("%%Title:"));
		} else if (doc->date == NULL && iscomment(line+2, "CreationDate:")) {
	    doc->date = gettextline(line+length("%%CreationDate:"));
		} else if (bb_set == NONE && iscomment(line+2, "BoundingBox:")) {
	    sscanf(line+length("%%BoundingBox:"), "%s", text);
	    if (strcmp(text, "(atend)") == 0) {
				bb_set = ATEND;
	    } else {
				if (sscanf(line+length("%%BoundingBox:"), "%d %d %d %d",
			    					&(doc->boundingbox[LLX]),
			    					&(doc->boundingbox[LLY]),
			  	  				&(doc->boundingbox[URX]),
			    					&(doc->boundingbox[URY])) == 4) {
		    	bb_set = 1;
				} else {
		    	float fllx, flly, furx, fury;
		    	if (sscanf(line+length("%%BoundingBox:"), "%f %f %f %f",
			        											&fllx, &flly, &furx, &fury) == 4) {
						bb_set = 1;
						doc->boundingbox[LLX] = fllx;
						doc->boundingbox[LLY] = flly;
						doc->boundingbox[URX] = furx;
						doc->boundingbox[URY] = fury;
						if (fllx < doc->boundingbox[LLX]) {
			    		doc->boundingbox[LLX]--;
						}
						if (flly < doc->boundingbox[LLY]) {
			    		doc->boundingbox[LLY]--;
						}
						if (furx > doc->boundingbox[URX]) {
			    		doc->boundingbox[URX]++;
						}
						if (fury > doc->boundingbox[URY]) {
			    		doc->boundingbox[URY]++;
						}
		    	}
				}
	    }
		} else if (orientation_set == NONE && iscomment(line+2, "Orientation:")) {
	    sscanf(line+length("%%Orientation:"), "%s", text);
	    
			if (strcmp(text, "(atend)") == 0) {
				orientation_set = ATEND;
	    } else if (strcmp(text, "Portrait") == 0) {
				doc->orientation = PORTRAIT;
				orientation_set = 1;
	    } else if (strcmp(text, "Landscape") == 0) {
				doc->orientation = LANDSCAPE;
				orientation_set = 1;
	    }
		} else if (page_order_set == NONE && iscomment(line+2, "PageOrder:")) {
	    sscanf(line+length("%%PageOrder:"), "%s", text);
	    
			if (strcmp(text, "(atend)") == 0) {
				page_order_set = ATEND;
	    } else if (strcmp(text, "Ascend") == 0) {
				doc->pageorder = ASCEND;
				page_order_set = 1;
	    } else if (strcmp(text, "Descend") == 0) {
				doc->pageorder = DESCEND;
				page_order_set = 1;
	    } else if (strcmp(text, "Special") == 0) {
				doc->pageorder = SPECIAL;
				page_order_set = 1;
	    }
	
		} else if (pages_set == NONE && iscomment(line+2, "Pages:")) {
	  	sscanf(line+length("%%Pages:"), "%s", text);
	    
			if (strcmp(text, "(atend)") == 0) {
				pages_set = ATEND;
	    } else {
				switch (sscanf(line+length("%%Pages:"), "%d %d", &maxpages, &i)) {
		    	case 2:
						if (page_order_set == NONE) {
			    		if (i == -1) {
								doc->pageorder = DESCEND;
								page_order_set = 1;
			    		} else if (i == 0) {
								doc->pageorder = SPECIAL;
								page_order_set = 1;
			    		} else if (i == 1) {
								doc->pageorder = ASCEND;
								page_order_set = 1;
			    		}
						}
		    	case 1:
						if (maxpages > 0) {
			    		doc->pages = (struct page *) calloc(maxpages, sizeof(struct page));
			    		if (doc->pages == NULL) {
								fprintf(stderr, "Fatal Error: Dynamic memory exhausted.\n");
								exit(-1);
			    		}
						}
				}
	    }
		} else if (doc->nummedia == NONE && iscomment(line+2, "DocumentMedia:")) {
	    float w, h;
	    doc->media = (struct documentmedia *) malloc(sizeof (struct documentmedia));
	    if (doc->media == NULL) {
				fprintf(stderr, "Fatal Error: Dynamic memory exhausted.\n");
				exit(-1);
	    }
			
	    doc->media[0].name = gettext(line+length("%%DocumentMedia:"), &next_char);
	    if (doc->media[0].name != NULL) {
				if (sscanf(next_char, "%f %f", &w, &h) == 2) {
		    	doc->media[0].width = w + 0.5;
		    	doc->media[0].height = h + 0.5;
				}
				if (doc->media[0].width != 0 && doc->media[0].height != 0) {
		    	doc->nummedia = 1;
				} else {
		    	free(doc->media[0].name);
				}
	    }
			
	    preread=1;
	    while (readline(line, sizeof line, file, &position, &line_len) 
														&& DSCcomment(line) && iscomment(line+2, "+")) {
				section_len += line_len;
				doc->media = (struct documentmedia *)
			    realloc(doc->media, (doc->nummedia+1)* sizeof (struct documentmedia));
		
				if (doc->media == NULL) {
		    	fprintf(stderr, "Fatal Error: Dynamic memory exhausted.\n");
		    	exit(-1);
				}

				doc->media[doc->nummedia].name = gettext(line+length("%%+"), &next_char);

				if (doc->media[doc->nummedia].name != NULL) {
		    	if (sscanf(next_char, "%f %f", &w, &h) == 2) {
						doc->media[doc->nummedia].width = w + 0.5;
						doc->media[doc->nummedia].height = h + 0.5;
		    	}
		    	if (doc->media[doc->nummedia].width != 0 &&
									doc->media[doc->nummedia].height != 0) {
						doc->nummedia++;
		    	} else {
						free(doc->media[doc->nummedia].name);
					}
				}
	    }
			
	    section_len += line_len;
	    if (doc->nummedia != 0) {
				doc->default_page_media = doc->media;
			}
		} else if (doc->nummedia == NONE &&
		    						iscomment(line+2, "DocumentPaperSizes:")) {
	    doc->media = (struct documentmedia *) malloc(sizeof (struct documentmedia));
	    if (doc->media == NULL) {
				fprintf(stderr, "Fatal Error: Dynamic memory exhausted.\n");
				exit(-1);
	    }
			
	    doc->media[0].name = gettext(line+length("%%DocumentPaperSizes:"), &next_char);
	    if (doc->media[0].name != NULL) {
				doc->media[0].width = 0;
				doc->media[0].height = 0;
				
				for (dmp=papersizes; dmp->name != NULL; dmp++) {
		    /* Note: Paper size comment uses down cased paper size
		     * name.  Case insensitive compares are only used for
		     * PaperSize comments.
		     */
		    	if (strcasecmp(doc->media[0].name, dmp->name) == 0) {
						free(doc->media[0].name);
						doc->media[0].name = (char *)malloc(strlen(dmp->name)+1);
						if (doc->media[0].name == NULL) {
			    		fprintf(stderr, "Fatal Error: Dynamic memory exhausted.\n");
			    		exit(-1);
						}
						strcpy(doc->media[0].name, dmp->name);
						doc->media[0].width = dmp->width;
						doc->media[0].height = dmp->height;
						break;
		    	}
				}
				if (doc->media[0].width != 0 && doc->media[0].height != 0) {
		    	doc->nummedia = 1;
				} else {
		    	free(doc->media[0].name);
				}
	    }
			
	    while ((cp = gettext(next_char, &next_char))) {
				doc->media = (struct documentmedia *)
			      realloc(doc->media, (doc->nummedia+1)* sizeof (struct documentmedia));
				if (doc->media == NULL) {
		    	fprintf(stderr, "Fatal Error: Dynamic memory exhausted.\n");
		    	exit(-1);
				}
				
				doc->media[doc->nummedia].name = cp;
				doc->media[doc->nummedia].width = 0;
				doc->media[doc->nummedia].height = 0;
				for (dmp=papersizes; dmp->name != NULL; dmp++) {
		    	/* Note: Paper size comment uses down cased paper size
		      * name.  Case insensitive compares are only used for
		      * PaperSize comments.
		      */
		    	if (strcasecmp(doc->media[doc->nummedia].name, dmp->name) == 0) {
						free(doc->media[doc->nummedia].name);
						doc->media[doc->nummedia].name = (char *)malloc(strlen(dmp->name)+1);
						if (doc->media[doc->nummedia].name == NULL) {
			    		fprintf(stderr, "Fatal Error: Dynamic memory exhausted.\n");
			    		exit(-1);
						}
						strcpy(doc->media[doc->nummedia].name, dmp->name);
						doc->media[doc->nummedia].name = dmp->name;
						doc->media[doc->nummedia].width = dmp->width;
						doc->media[doc->nummedia].height = dmp->height;
						break;
		    	}
				}
				if (doc->media[doc->nummedia].width != 0 
									&& doc->media[doc->nummedia].height != 0) {
					doc->nummedia++;
				} else {
		    	free(doc->media[doc->nummedia].name);
	    	}
			}
			
	    preread=1;
	    while (readline(line, sizeof line, file, &position, &line_len) 
														&& DSCcomment(line) && iscomment(line+2, "+")) {
				section_len += line_len;
				next_char = line + length("%%+");
				
				while ((cp = gettext(next_char, &next_char))) {
		    	doc->media = (struct documentmedia *)
				  				realloc(doc->media, (doc->nummedia+1)*sizeof (struct documentmedia));
		    	if (doc->media == NULL) {
						fprintf(stderr,
						"Fatal Error: Dynamic memory exhausted.\n");
						exit(-1);
		    	}
		    	doc->media[doc->nummedia].name = cp;
		    	doc->media[doc->nummedia].width = 0;
		    	doc->media[doc->nummedia].height = 0;
		    	
					for (dmp=papersizes; dmp->name != NULL; dmp++) {
						/* Note: Paper size comment uses down cased paper size
			  		* name.  Case insensitive compares are only used for
			  		* PaperSize comments.
			  		*/
						if (strcasecmp(doc->media[doc->nummedia].name, dmp->name) == 0) {
			    		doc->media[doc->nummedia].width = dmp->width;
			    		doc->media[doc->nummedia].height = dmp->height;
			    		break;
						}
		    	}
		    
					if (doc->media[doc->nummedia].width != 0 &&
											doc->media[doc->nummedia].height != 0) {
						doc->nummedia++;
					} else {
						free(doc->media[doc->nummedia].name);
					}
				}
	    }
			
	    section_len += line_len;
	    if (doc->nummedia != 0) doc->default_page_media = doc->media;
		}
	}


// QUA



	if (DSCcomment(line) && iscomment(line+2, "EndComments")) {
		readline(line, sizeof line, file, &position, &line_len);
		section_len += line_len;
	}
	doc->endheader = position;
	doc->lenheader = section_len - line_len;

	/* Optional Preview comments for encapsulated PostScript files */ 

	beginsection = position;
	section_len = line_len;
	while (blank(line) && readline(line, sizeof line, file, &position, &line_len)) {
		section_len += line_len;
	}

	if (doc->epsf && DSCcomment(line) && iscomment(line+2, "BeginPreview")) {
		doc->beginpreview = beginsection;
		beginsection = 0;
		while (readline(line, sizeof line, file, &position, &line_len) &&
	        				!(DSCcomment(line) && iscomment(line+2, "EndPreview"))) {
			section_len += line_len;
		}
		section_len += line_len;
		readline(line, sizeof line, file, &position, &line_len);
		section_len += line_len;
		doc->endpreview = position;
		doc->lenpreview = section_len - line_len;
	}

    /* Page Defaults for Version 3.0 files */

	if (beginsection == 0) {
		beginsection = position;
		section_len = line_len;
	}
	
	while (blank(line) &&
	    			readline(line, sizeof line, file, &position, &line_len)) {
		section_len += line_len;
	}

	if (DSCcomment(line) && iscomment(line+2, "BeginDefaults")) {
		doc->begindefaults = beginsection;
		beginsection = 0;
		while (readline(line, sizeof line, file, &position, &line_len) &&
	        					!(DSCcomment(line) && iscomment(line+2, "EndDefaults"))) {
			section_len += line_len;
			if (!DSCcomment(line)) {
				/* Do nothing */
			} else if (doc->default_page_orientation == NONE &&
																	iscomment(line+2, "PageOrientation:")) {
				sscanf(line+length("%%PageOrientation:"), "%s", text);
				if (strcmp(text, "Portrait") == 0) {
		    	doc->default_page_orientation = PORTRAIT;
				} else if (strcmp(text, "Landscape") == 0) {
		    	doc->default_page_orientation = LANDSCAPE;
				}
			} else if (page_media_set == NONE && iscomment(line+2, "PageMedia:")) {
				cp = gettext(line+length("%%PageMedia:"), NULL);
				for (dmp = doc->media, i=0; i<doc->nummedia; i++, dmp++) {
		    	if (strcmp(cp, dmp->name) == 0) {
						doc->default_page_media = dmp;
						page_media_set = 1;
						break;
					}
				}
				free(cp);
			} else if (page_bb_set == NONE && iscomment(line+2, "PageBoundingBox:")) {
				if (sscanf(line+length("%%PageBoundingBox:"), "%d %d %d %d",
			    					&(doc->default_page_boundingbox[LLX]),
			    					&(doc->default_page_boundingbox[LLY]),
			    					&(doc->default_page_boundingbox[URX]),
			    					&(doc->default_page_boundingbox[URY])) == 4) {
					page_bb_set = 1;
				} else {
		    	float fllx, flly, furx, fury;

					if (sscanf(line+length("%%PageBoundingBox:"), "%f %f %f %f",
			        													&fllx, &flly, &furx, &fury) == 4) {
						page_bb_set = 1;
						doc->default_page_boundingbox[LLX] = fllx;
						doc->default_page_boundingbox[LLY] = flly;
						doc->default_page_boundingbox[URX] = furx;
						doc->default_page_boundingbox[URY] = fury;

						if (fllx < doc->default_page_boundingbox[LLX]) {
			    		doc->default_page_boundingbox[LLX]--;
						}
						if (flly < doc->default_page_boundingbox[LLY]) {
			    		doc->default_page_boundingbox[LLY]--;
						}
						if (furx > doc->default_page_boundingbox[URX]) {
			    		doc->default_page_boundingbox[URX]++;
						}
						if (fury > doc->default_page_boundingbox[URY]) {
			    		doc->default_page_boundingbox[URY]++;
						}
					}
				}
			}
		}
	
		section_len += line_len;
		readline(line, sizeof line, file, &position, &line_len);
		section_len += line_len;
		doc->enddefaults = position;
		doc->lendefaults = section_len - line_len;
	}

    /* Document Prolog */

	if (beginsection == 0) {
		beginsection = position;
		section_len = line_len;
	}
   
	 while (blank(line) && readline(line, sizeof line, file, &position, &line_len)) {
		section_len += line_len;
	}

	if (!(DSCcomment(line) &&
	  								(iscomment(line+2, "BeginSetup") ||
	    												iscomment(line+2, "Page:") ||
	    													iscomment(line+2, "Trailer") ||
	    															iscomment(line+2, "EOF")))) {
		doc->beginprolog = beginsection;
		beginsection = 0;
		preread = 1;

		while ((preread ||
				readline(line, sizeof line, file, &position, &line_len)) &&
	        !(DSCcomment(line) &&
	          	(iscomment(line+2, "EndProlog") ||
	          		iscomment(line+2, "BeginSetup") ||
	          		iscomment(line+2, "Page:") ||
	          		iscomment(line+2, "Trailer") ||
	          					iscomment(line+2, "EOF")))) {
			if (!preread) { 
				section_len += line_len;
			}
	    preread = 0;
		}
		section_len += line_len;
		if (DSCcomment(line) && iscomment(line+2, "EndProlog")) {
	    readline(line, sizeof line, file, &position, &line_len);
	    section_len += line_len;
		}
		doc->endprolog = position;
		doc->lenprolog = section_len - line_len;
	}

	/* Document Setup,  Page Defaults found here for Version 2 files */

    if (beginsection == 0) {
			beginsection = position;
			section_len = line_len;
    }
    while (blank(line) && readline(line, sizeof line, file, &position, &line_len)) {
			section_len += line_len;
    }

    if (!(DSCcomment(line) &&
	  			(iscomment(line+2, "Page:") ||
	    			iscomment(line+2, "Trailer") ||
	    							iscomment(line+2, "EOF")))) {
			doc->beginsetup = beginsection;
			beginsection = 0;
			preread = 1;
			while ((preread ||
						readline(line, sizeof line, file, &position, &line_len)) &&
	        		!(DSCcomment(line) &&
	          		(iscomment(line+2, "EndSetup") ||
	          			iscomment(line+2, "Page:") ||
	          			iscomment(line+2, "Trailer") ||
	          						iscomment(line+2, "EOF")))) {
	    	if (!preread) {
					section_len += line_len;
				}
	    	preread = 0;
	    	if (!DSCcomment(line)) {
				/* Do nothing */
	    	} else if (doc->default_page_orientation == NONE &&
																iscomment(line+2, "PageOrientation:")) {
					sscanf(line+length("%%PageOrientation:"), "%s", text);
					if (strcmp(text, "Portrait") == 0) {
		    		doc->default_page_orientation = PORTRAIT;
					} else if (strcmp(text, "Landscape") == 0) {
		    		doc->default_page_orientation = LANDSCAPE;
					}
	    	} else if (page_media_set == NONE &&
		        							iscomment(line+2, "PaperSize:")) {
					cp = gettext(line+length("%%PaperSize:"), NULL);
					for (dmp = doc->media, i=0; i<doc->nummedia; i++, dmp++) {
		    		/* Note: Paper size comment uses down cased paper size
		      	* name.  Case insensitive compares are only used for
		      	* PaperSize comments.
		      	*/
		    		if (strcasecmp(cp, dmp->name) == 0) {
							doc->default_page_media = dmp;
							page_media_set = 1;
							break;
		    		}
					}
					free(cp);
	    	} else if (page_bb_set == NONE &&
		        					iscomment(line+2, "PageBoundingBox:")) {
					if (sscanf(line+length("%%PageBoundingBox:"), "%d %d %d %d",
			    			&(doc->default_page_boundingbox[LLX]),
			    				&(doc->default_page_boundingbox[LLY]),
			    				&(doc->default_page_boundingbox[URX]),
			    					&(doc->default_page_boundingbox[URY])) == 4) {
		    		page_bb_set = 1;
					} else {
		    		float fllx, flly, furx, fury;
		    		if (sscanf(line+length("%%PageBoundingBox:"), "%f %f %f %f",
			        														&fllx, &flly, &furx, &fury) == 4) {
							page_bb_set = 1;
							doc->default_page_boundingbox[LLX] = fllx;
							doc->default_page_boundingbox[LLY] = flly;
							doc->default_page_boundingbox[URX] = furx;
							doc->default_page_boundingbox[URY] = fury;
							
							if (fllx < doc->default_page_boundingbox[LLX]) {
			    			doc->default_page_boundingbox[LLX]--;
							}
							if (flly < doc->default_page_boundingbox[LLY]) {
			    			doc->default_page_boundingbox[LLY]--;
							}
							if (furx > doc->default_page_boundingbox[URX]) {
			    			doc->default_page_boundingbox[URX]++;
							}
							if (fury > doc->default_page_boundingbox[URY]) {
			    			doc->default_page_boundingbox[URY]++;
							}
		    		}
					}
	    	}
			}
			
			section_len += line_len;
			if (DSCcomment(line) && iscomment(line+2, "EndSetup")) {
	    	readline(line, sizeof line, file, &position, &line_len);
	    	section_len += line_len;
			}
			doc->endsetup = position;
			doc->lensetup = section_len - line_len;
    }

    /* Individual Pages */

    if (beginsection == 0) {
			beginsection = position;
			section_len = line_len;
    }
    while (blank(line) && readline(line, sizeof line, file, &position, &line_len)) {
			section_len += line_len;
    }

newpage:
    while (DSCcomment(line) && iscomment(line+2, "Page:")) {
			if (maxpages == 0) {
	    	maxpages = 1;
	    	doc->pages = (struct page *) calloc(maxpages, sizeof(struct page));
	    	if (doc->pages == NULL) {
					fprintf(stderr, "Fatal Error: Dynamic memory exhausted.\n");
					exit(-1);
	    	}
			}
			label = gettext(line+length("%%Page:"), &next_char);
			if (sscanf(next_char, "%d", &thispage) != 1) {
				thispage = 0;
			}
			if (nextpage == 1) {
	    	ignore = thispage != 1;
			}
			if (!ignore && thispage != nextpage) {
	    	free(label);
	    	doc->numpages--;
	    	goto continuepage;
			}
			nextpage++;
			if (doc->numpages == maxpages) {
	    	maxpages++;
	    	doc->pages = (struct page *) realloc(doc->pages, maxpages*sizeof (struct page));
	    	if (doc->pages == NULL) {
					fprintf(stderr, "Fatal Error: Dynamic memory exhausted.\n");
					exit(-1);
	    	}
			}
			memset(&(doc->pages[doc->numpages]), 0, sizeof(struct page));
			page_bb_set = NONE;
			doc->pages[doc->numpages].label = label;
			if (beginsection) {
	    	doc->pages[doc->numpages].begin = beginsection;
	    	beginsection = 0;
			} else {
	    	doc->pages[doc->numpages].begin = position;
	    	section_len = line_len;
			}
			
continuepage:
			while (readline(line, sizeof line, file, &position, &line_len) &&
	        				!(DSCcomment(line) &&
	          				(iscomment(line+2, "Page:") ||
	          						iscomment(line+2, "Trailer") ||
	          									iscomment(line+2, "EOF")))) {
	    	section_len += line_len;
	    	if (!DSCcomment(line)) {
					/* Do nothing */
	    	} else if (doc->pages[doc->numpages].orientation == NONE &&
																	iscomment(line+2, "PageOrientation:")) {
					sscanf(line+length("%%PageOrientation:"), "%s", text);
					if (strcmp(text, "Portrait") == 0) {
		    		doc->pages[doc->numpages].orientation = PORTRAIT;
					} else if (strcmp(text, "Landscape") == 0) {
		    		doc->pages[doc->numpages].orientation = LANDSCAPE;
					}
	    	} else if (doc->pages[doc->numpages].media == NULL &&
																				iscomment(line+2, "PageMedia:")) {
					cp = gettext(line+length("%%PageMedia:"), NULL);
					for (dmp = doc->media, i=0; i<doc->nummedia; i++, dmp++) {
		    		if (strcmp(cp, dmp->name) == 0) {
							doc->pages[doc->numpages].media = dmp;
							break;
		    		}
					}
					free(cp);
	    	} else if (doc->pages[doc->numpages].media == NULL &&
		        													iscomment(line+2, "PaperSize:")) {
					cp = gettext(line+length("%%PaperSize:"), NULL);
					for (dmp = doc->media, i=0; i<doc->nummedia; i++, dmp++) {
		    		/* Note: Paper size comment uses down cased paper size
		      	* name.  Case insensitive compares are only used for
		      	* PaperSize comments.
		      	*/
		    		if (strcasecmp(cp, dmp->name) == 0) {
							doc->pages[doc->numpages].media = dmp;
						break;
		    		}
					}
					free(cp);
	    	} else if ((page_bb_set == NONE || page_bb_set == ATEND) &&
		        											iscomment(line+2, "PageBoundingBox:")) {
					sscanf(line+length("%%PageBoundingBox:"), "%s", text);
					if (strcmp(text, "(atend)") == 0) {
		    		page_bb_set = ATEND;
					} else {
		    		if (sscanf(line+length("%%PageBoundingBox:"), "%d %d %d %d",
			    					&(doc->pages[doc->numpages].boundingbox[LLX]),
			    					&(doc->pages[doc->numpages].boundingbox[LLY]),
			    					&(doc->pages[doc->numpages].boundingbox[URX]),
			    						&(doc->pages[doc->numpages].boundingbox[URY])) == 4) {
							if (page_bb_set == NONE) {
								page_bb_set = 1;
		    			} else {
								float fllx, flly, furx, fury;
								
								if (sscanf(line+length("%%PageBoundingBox:"), "%f %f %f %f",
				    															&fllx, &flly, &furx, &fury) == 4) {
			    				if (page_bb_set == NONE) {
										page_bb_set = 1;
									}
			    				doc->pages[doc->numpages].boundingbox[LLX] = fllx;
			    				doc->pages[doc->numpages].boundingbox[LLY] = flly;
			    				doc->pages[doc->numpages].boundingbox[URX] = furx;
			    				doc->pages[doc->numpages].boundingbox[URY] = fury;
			    
									if (fllx < doc->pages[doc->numpages].boundingbox[LLX]) {
										doc->pages[doc->numpages].boundingbox[LLX]--;
									}
			    				if (flly < doc->pages[doc->numpages].boundingbox[LLY]) {
										doc->pages[doc->numpages].boundingbox[LLY]--;
									}
			    				if (furx > doc->pages[doc->numpages].boundingbox[URX]) {
										doc->pages[doc->numpages].boundingbox[URX]++;
									}
			    				if (fury > doc->pages[doc->numpages].boundingbox[URY]) {
										doc->pages[doc->numpages].boundingbox[URY]++;
									}
								}
		    			}
						}
					}
	    	}
			}
			section_len += line_len;
			doc->pages[doc->numpages].end = position;
			doc->pages[doc->numpages].len = section_len - line_len;
			doc->numpages++;
    }

    /* Document Trailer */

    if (beginsection) {
			doc->begintrailer = beginsection;
			beginsection = 0;
    } else {
			doc->begintrailer = position;
			section_len = line_len;
    }

    preread = 1;
    while ((preread ||
	    			readline(line, sizeof line, file, &position, &line_len)) &&
	    										!(DSCcomment(line) && iscomment(line+2, "EOF"))) {
			if (!preread) {
				section_len += line_len;
			}
			preread = 0;
	
			if (!DSCcomment(line)) {
	    	/* Do nothing */
			} else if (iscomment(line+2, "Page:")) {
	    	free(gettext(line+length("%%Page:"), &next_char));
	    	if (sscanf(next_char, "%d", &thispage) != 1) {
					thispage = 0;
				}
	    	if (!ignore && thispage == nextpage) {
					if (doc->numpages > 0) {
		    		doc->pages[doc->numpages-1].end = position;
		    		doc->pages[doc->numpages-1].len += section_len - line_len;
					} else {
		    		if (doc->endsetup) {
							doc->endsetup = position;
							doc->endsetup += section_len - line_len;
		    		} else if (doc->endprolog) {
							doc->endprolog = position;
							doc->endprolog += section_len - line_len;
		    		}
					}
					goto newpage;
	    	}
			} else if (bb_set == ATEND && iscomment(line+2, "BoundingBox:")) {
	    	if (sscanf(line+length("%%BoundingBox:"), "%d %d %d %d",
		        						&(doc->boundingbox[LLX]),
		        								&(doc->boundingbox[LLY]),
		        										&(doc->boundingbox[URX]),
		        												&(doc->boundingbox[URY])) != 4) {
					float fllx, flly, furx, fury;
					if (sscanf(line+length("%%BoundingBox:"), "%f %f %f %f",
			    															&fllx, &flly, &furx, &fury) == 4) {
		    		doc->boundingbox[LLX] = fllx;
		    		doc->boundingbox[LLY] = flly;
		    		doc->boundingbox[URX] = furx;
		    		doc->boundingbox[URY] = fury;
		    
						if (fllx < doc->boundingbox[LLX]) {
							doc->boundingbox[LLX]--;
						}
		    		if (flly < doc->boundingbox[LLY]) {
							doc->boundingbox[LLY]--;
						}
		    		if (furx > doc->boundingbox[URX]) {
							doc->boundingbox[URX]++;
						}
		    		if (fury > doc->boundingbox[URY]) {
							doc->boundingbox[URY]++;
						}
					}
	    	}
			} else if (orientation_set == ATEND &&
		    												iscomment(line+2, "Orientation:")) {
	    	sscanf(line+length("%%Orientation:"), "%s", text);
	    	if (strcmp(text, "Portrait") == 0) {
					doc->orientation = PORTRAIT;
	    	} else if (strcmp(text, "Landscape") == 0) {
					doc->orientation = LANDSCAPE;
	    	}
			} else if (page_order_set == ATEND && iscomment(line+2, "PageOrder:")) {
	    	sscanf(line+length("%%PageOrder:"), "%s", text);
	    	if (strcmp(text, "Ascend") == 0) {
					doc->pageorder = ASCEND;
	    	} else if (strcmp(text, "Descend") == 0) {
					doc->pageorder = DESCEND;
	    	} else if (strcmp(text, "Special") == 0) {
					doc->pageorder = SPECIAL;
	    	}
			} else if (pages_set == ATEND && iscomment(line+2, "Pages:")) {
	    	if (sscanf(line+length("%%Pages:"), "%*u %d", &i) == 1) {
					if (page_order_set == NONE) {
		    		if (i == -1) {
							doc->pageorder = DESCEND;
		    		} else if (i == 0) {
							doc->pageorder = SPECIAL;
		    		} else if (i == 1) {
							doc->pageorder = ASCEND;
						}
					}
	    	}
			}
    }
    section_len += line_len;
    if (DSCcomment(line) && iscomment(line+2, "EOF")) {
			readline(line, sizeof line, file, &position, &line_len);
			section_len += line_len;
    }
    doc->endtrailer = position;
    doc->lentrailer = section_len - line_len;

#if 0
    section_len = line_len;
    preread = 1;
    while (preread || readline(line, sizeof line, file, &position, &line_len)) {
			if (!preread) {
				section_len += line_len;
			}	
			preread = 0;
			if (DSCcomment(line) && iscomment(line+2, "Page:")) {
	    	free(gettext(line+length("%%Page:"), &next_char));
	    	if (sscanf(next_char, "%d", &thispage) != 1) {
					thispage = 0;
				}
	    	if (!ignore && thispage == nextpage) {
					if (doc->numpages > 0) {
		    		doc->pages[doc->numpages-1].end = position;
		    		doc->pages[doc->numpages-1].len += doc->lentrailer + section_len - line_len;
					} else {
		    		if (doc->endsetup) {
							doc->endsetup = position;
							doc->endsetup += doc->lentrailer +
					  	section_len - line_len;
		    		} else if (doc->endprolog) {
							doc->endprolog = position;
							doc->endprolog += doc->lentrailer +
					  	section_len - line_len;
		    		}
					}
					goto newpage;
	    	}
			}
    }
#endif

	return doc;
}

/*
 *	psfree -- free dynamic storage associated with document structure.
 */

void psfree(struct document *doc)
{
	int i;

	if (doc) {
		for (i=0; i<doc->numpages; i++) {
	    if (doc->pages[i].label) {
				free(doc->pages[i].label);
			}
		}
		for (i=0; i<doc->nummedia; i++) {
	    if (doc->media[i].name) {
				free(doc->media[i].name);
			}
		}
		if (doc->title) {
			free(doc->title);
		}
		if (doc->date) {
			free(doc->date);
		}
		if (doc->pages) {
			free(doc->pages);
		}
		if (doc->media) {
			free(doc->media);
		}
		free(doc);
	}
}

/*
 * gettextine -- skip over white space and return the rest of the line.
 *               If the text begins with '(' return the text string
 *		 using gettext().
 */

static char *gettextline(char *line)
{
	char *cp;

	while (*line && (*line == ' ' || *line == '\t')) {
		line++;
	}
	
	if (*line == '(') {
		return gettext(line, NULL);
	} else {
		if (strlen(line) == 0) {
			return NULL;
		}
		cp = (char *) malloc(strlen(line));
		if (cp == NULL) {
	    fprintf(stderr, "Fatal Error: Dynamic memory exhausted.\n");
	    exit(-1);
		}
		strncpy(cp, line, strlen(line)-1);
		cp[strlen(line)-1] = '\0';
		return cp;
	}
}

/*
 *	gettext -- return the next text string on the line.
 *		   return NULL if nothing is present.
 */

static char *gettext(char *line, char **next_char)
{
	char text[PSLINELENGTH];	/* Temporary storage for text */
	char *cp;
	int quoted=0;

	while (*line && (*line == ' ' || *line == '\t')) {
		line++;
	}
	
	cp = text;
	
	if (*line == '(') {
		int level = 0;
		quoted=1;
		line++;
	
		while (*line && !(*line == ')' && level == 0 )) {
	    if (*line == '\\') {
				if (*(line+1) == 'n') {
		    	*cp++ = '\n';
		    	line += 2;
				} else if (*(line+1) == 'r') {
		    	*cp++ = '\r';
		    	line += 2;
				} else if (*(line+1) == 't') {
		    	*cp++ = '\t';
		    	line += 2;
				} else if (*(line+1) == 'b') {
		    	*cp++ = '\b';
		    	line += 2;
				} else if (*(line+1) == 'f') {
		    	*cp++ = '\f';
		    	line += 2;
				} else if (*(line+1) == '\\') {
		    	*cp++ = '\\';
		    	line += 2;
				} else if (*(line+1) == '(') {
		    	*cp++ = '(';
		    	line += 2;
				} else if (*(line+1) == ')') {
		    	*cp++ = ')';
		    	line += 2;
				} else if (*(line+1) >= '0' && *(line+1) <= '9') {
		    	if (*(line+2) >= '0' && *(line+2) <= '9') {
						if (*(line+3) >= '0' && *(line+3) <= '9') {
			    		*cp++ = ((*(line+1) - '0')*8 + *(line+2) - '0')*8 +
				    	*(line+3) - '0';
			    		line += 4;
						} else {
			    		*cp++ = (*(line+1) - '0')*8 + *(line+2) - '0';
			    		line += 3;
						}
		    	} else {
						*cp++ = *(line+1) - '0';
						line += 2;
		    	}
				} else {
		    	line++;
		    	*cp++ = *line++;
				}
			} else if (*line == '(') {
				level++;
				*cp++ = *line++;
	    } else if (*line == ')') {
				level--;
				*cp++ = *line++;
	    } else {
				*cp++ = *line++;
	    }
		}
	} else {
		while (*line && !(*line == ' ' || *line == '\t' || *line == '\n')) {
	    *cp++ = *line++;
		}
	}

	*cp = '\0';

	if (next_char) {
		*next_char = line;
	}
	
	if (!quoted && strlen(text) == 0) {
		return NULL;
	}
	
	cp = (char *) malloc(strlen(text)+1);

	if (cp == NULL) {
		fprintf(stderr, "Fatal Error: Dynamic memory exhausted.\n");
		exit(-1);
	}

	strcpy(cp, text);
	return cp;
}

/*
 *	readline -- Read the next line in the postscript file.
 *                  Automatically skip over data (as indicated by
 *                  %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
 *		    comments.)
 *		    Also, skip over included documents (as indicated by
 *		    %%BeginDocument/%%EndDocument comments.)
 */

static char *readline(char *line, int size, FILE *fp, long *position, unsigned *line_len)
{
	char text[PSLINELENGTH];	/* Temporary storage for text */
	char save[PSLINELENGTH];	/* Temporary storage for text */
	char *cp;
	unsigned int num;
	unsigned int nbytes;
	int i;
	char buf[BUFSIZ];

	if (position) {
		*position = ftell(fp);
	}
		
	cp = fgets(line, size, fp);
	
	if (cp == NULL) {
		line[0] = '\0';
	}
	
	*line_len = strlen(line);
	
	if (!(DSCcomment(line) && iscomment(line+2, "Begin"))) {
		/* Do nothing */
	} else if (iscomment(line+7, "Document:")) {
		strcpy(save, line+7);
	
		while (readline(line, size, fp, NULL, &nbytes) &&
	        		!(DSCcomment(line) && iscomment(line+2, "EndDocument"))) {
	    *line_len += nbytes;
		}
		
		*line_len += nbytes;
		strcpy(line, save);
		
	} else if (iscomment(line+7, "Feature:")) {
		strcpy(save, line+7);
		
		while (readline(line, size, fp, NULL, &nbytes) &&
	        				!(DSCcomment(line) && iscomment(line+2, "EndFeature"))) {
	    *line_len += nbytes;
		}
	
		*line_len += nbytes;
		strcpy(line, save);

	} else if (iscomment(line+7, "File:")) {
		strcpy(save, line+7);
	
		while (readline(line, size, fp, NULL, &nbytes) &&
	        	!(DSCcomment(line) && iscomment(line+2, "EndFile"))) {
	    *line_len += nbytes;
		}
	
		*line_len += nbytes;
		strcpy(line, save);
		
	} else if (iscomment(line+7, "Font:")) {
		strcpy(save, line+7);
	
		while (readline(line, size, fp, NULL, &nbytes) &&
	        		!(DSCcomment(line) && iscomment(line+2, "EndFont"))) {
	    *line_len += nbytes;
		}
		
		*line_len += nbytes;
		strcpy(line, save);
	
	} else if (iscomment(line+7, "ProcSet:")) {
		strcpy(save, line+7);
	
		while (readline(line, size, fp, NULL, &nbytes) &&
	        				!(DSCcomment(line) && iscomment(line+2, "EndProcSet"))) {
	    *line_len += nbytes;
		}
		
		*line_len += nbytes;
		strcpy(line, save);
		
	} else if (iscomment(line+7, "Resource:")) {
		strcpy(save, line+7);
		
		while (readline(line, size, fp, NULL, &nbytes) &&
	        		!(DSCcomment(line) && iscomment(line+2, "EndResource"))) {
	    *line_len += nbytes;
		}
		
		*line_len += nbytes;
		strcpy(line, save);
    
	} else if (iscomment(line+7, "Data:")) {
		text[0] = '\0';
		strcpy(save, line+7);
		
		if (sscanf(line+length("%%BeginData:"), "%d %*s %s", &num, text) >= 1) {
	    if (strcmp(text, "Lines") == 0) {
				for (i=0; i < num; i++) {
		    	cp = fgets(line, size, fp);
		    	*line_len += cp ? strlen(line) : 0;
				}
	    } else {
				while (num > BUFSIZ) {
		    	fread(buf, sizeof (char), BUFSIZ, fp);
		    	*line_len += BUFSIZ;
		    	num -= BUFSIZ;
				}
				fread(buf, sizeof (char), num, fp);
				*line_len += num;
			}
		}
	
		while (readline(line, size, fp, NULL, &nbytes) &&
	        		!(DSCcomment(line) && iscomment(line+2, "EndData"))) {
	    *line_len += nbytes;
		}
	
		*line_len += nbytes;
		strcpy(line, save);
	
	} else if (iscomment(line+7, "Binary:")) {
		strcpy(save, line+7);
		
		if(sscanf(line+length("%%BeginBinary:"), "%d", &num) == 1) {
	    while (num > BUFSIZ) {
				fread(buf, sizeof (char), BUFSIZ, fp);
				*line_len += BUFSIZ;
				num -= BUFSIZ;
	    }
	    fread(buf, sizeof (char), num, fp);
	    *line_len += num;
		}
		
		while (readline(line, size, fp, NULL, &nbytes) &&
	        				!(DSCcomment(line) && iscomment(line+2, "EndBinary"))) {
	    *line_len += nbytes;
		}
		*line_len += nbytes;
		strcpy(line, save);
	}
	
	return cp;
}

/*
 *	pscopy -- copy lines of Postscript from a section of one file
 *		  to another file.
 *                Automatically switch to binary copying whenever
 *                %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
 *		  comments are encountered.
 */

void pscopy(FILE *from, FILE *to, long begin, long end)
{
	char line[PSLINELENGTH];	/* 255 characters + 1 newline + 1 NULL */
	char text[PSLINELENGTH];	/* Temporary storage for text */
	unsigned int num;
	int i;
	char buf[BUFSIZ];

	if (begin >= 0) {
		fseek(from, begin, SEEK_SET);
	}
	
	while (ftell(from) < end) {
		fgets(line, sizeof line, from);
		fputs(line, to);

		if (!(DSCcomment(line) && iscomment(line+2, "Begin"))) {
	    /* Do nothing */
		} else if (iscomment(line+7, "Data:")) {
			text[0] = '\0';
	    
			if (sscanf(line+length("%%BeginData:"), "%d %*s %s", &num, text) >= 1) {
				if (strcmp(text, "Lines") == 0) {
		    	for (i=0; i < num; i++) {
						fgets(line, sizeof line, from);
						fputs(line, to);
		    	}
				} else {
		    	while (num > BUFSIZ) {
						fread(buf, sizeof (char), BUFSIZ, from);
						fwrite(buf, sizeof (char), BUFSIZ, to);
						num -= BUFSIZ;
		    	}
		    	fread(buf, sizeof (char), num, from);
		    	fwrite(buf, sizeof (char), num, to);
				}
	    }
		} else if (iscomment(line+7, "Binary:")) {
	  	if(sscanf(line+length("%%BeginBinary:"), "%d", &num) == 1) {
				while (num > BUFSIZ) {
		    	fread(buf, sizeof (char), BUFSIZ, from);
		    	fwrite(buf, sizeof (char), BUFSIZ, to);
		    	num -= BUFSIZ;
				}
				fread(buf, sizeof (char), num, from);
				fwrite(buf, sizeof (char), num, to);
	    }
		}
	}
}

/*
 *	pscopyuntil -- copy lines of Postscript from a section of one file
 *		       to another file until a particular comment is reached.
 *                     Automatically switch to binary copying whenever
 *                     %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
 *		       comments are encountered.
 */

char *pscopyuntil(FILE *from, FILE *to, long begin, long end, const char *comment)
{
	char line[PSLINELENGTH];	/* 255 characters + 1 newline + 1 NULL */
	char text[PSLINELENGTH];	/* Temporary storage for text */
	unsigned int num;
	int comment_length;
	int i;
	char buf[BUFSIZ];
	char *cp;

	comment_length = strlen(comment);
	
	if (begin >= 0) {
		fseek(from, begin, SEEK_SET);
	}
	
	while (ftell(from) < end) {
		fgets(line, sizeof line, from);

		/* iscomment cannot be used here,
	  * because comment_length is not known at compile time. */
		if (strncmp(line, comment, comment_length) == 0) {
			cp = (char *) malloc(strlen(line)+1);
	    
			if (cp == NULL) {
				fprintf(stderr, "Fatal Error: Dynamic memory exhausted.\n");
				exit(-1);
	    }
			
	  	strcpy(cp, line);
	  	return cp;
		}
		
		fputs(line, to);
		
		if (!(DSCcomment(line) && iscomment(line+2, "Begin"))) {
	    /* Do nothing */
		} else if (iscomment(line+7, "Data:")) {
	  	text[0] = '\0';
	    if (sscanf(line+length("%%BeginData:"), "%d %*s %s", &num, text) >= 1) {
				if (strcmp(text, "Lines") == 0) {
		    	for (i=0; i < num; i++) {
						fgets(line, sizeof line, from);
						fputs(line, to);
		    	}
				} else {
		    	while (num > BUFSIZ) {
						fread(buf, sizeof (char), BUFSIZ, from);
						fwrite(buf, sizeof (char), BUFSIZ, to);
						num -= BUFSIZ;
		    	}
		    	fread(buf, sizeof (char), num, from);
		    	fwrite(buf, sizeof (char), num, to);
				}
	    }
		} else if (iscomment(line+7, "Binary:")) {
	    if(sscanf(line+length("%%BeginBinary:"), "%d", &num) == 1) {
				while (num > BUFSIZ) {
		    	fread(buf, sizeof (char), BUFSIZ, from);
		    	fwrite(buf, sizeof (char), BUFSIZ, to);
		    	num -= BUFSIZ;
				}
				fread(buf, sizeof (char), num, from);
				fwrite(buf, sizeof (char), num, to);
	    }
		}
	}
	return NULL;
}

/*
 *	blank -- determine whether the line contains nothing but whitespace.
 */
static int blank(char *line)
{
	char *cp = line;

	while (*cp == ' ' || *cp == '\t') cp++;
	return *cp == '\n' || (*cp == '%' && (line[0] != '%' || line[1] != '%'));
}
