2 * ps.c -- Postscript scanning and copying routines.
3 * Copyright (C) 1992, 1998 Timothy O. Theisen
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.
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.
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.
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
26 /* 18/3/98 Jake Hamby patch */
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/
37 /* GV by Johannes Plass
38 * Department of Physics
39 * Johannes Gutenberg University
42 * <plass@thep.physik.uni-mainz.de>
56 #include <X11/Xos.h> /* #includes the appropriate <string.h> */
58 #include "gsdefaults.h"
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] == '%')
70 /* list of standard paper sizes from Adobe's PPD. */
72 /*--------------------------------------------------*/
73 /* Declarations for ps_io_*() routines. */
75 typedef struct FileDataStruct_ *FileData;
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 */
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);
95 static char *skipped_line = "% ps_io_fgetchars: skipped line";
96 static char *empty_string = "";
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);
104 pages_new(struct page *pages, int current, int maxpages)
106 struct page *oldpages = pages;
108 pages = g_new0(struct page, maxpages);
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;
119 * psscan -- scan the PostScript file for document structuring comments.
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.)
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 and CreationDate are also retrieved to
137 * help identify the document.
139 * The following comments are examined:
142 * Must start with %!PS-Adobe-. Version numbers ignored.
143 * Also allowed to be just %!PS, many files seem to have that.
145 * %!PS-Adobe-* [EPSF-*]
146 * %%BoundingBox: <int> <int> <int> <int>|(atend)
147 * %%CreationDate: <textline>
148 * %%Orientation: Portrait|Landscape|(atend)
149 * %%Pages: <uint> [<int>]|(atend)
150 * %%PageOrder: Ascend|Descend|Special|(atend)
151 * %%Title: <textline>
152 * %%DocumentMedia: <text> <real> <real> <real> <text> <text>
153 * %%DocumentPaperSizes: <text>
156 * Note: Either the 3.0 or 2.0 syntax for %%Pages is accepted.
157 * Also either the 2.0 %%DocumentPaperSizes or the 3.0
158 * %%DocumentMedia comments are accepted as well.
160 * The header section ends either explicitly with %%EndComments or
161 * implicitly with any line that does not begin with %X where X is
162 * a not whitespace character.
164 * If the file is encapsulated PostScript the optional Preview section
170 * This section explicitly begins and ends with the above comments.
172 * Next the Defaults section for version 3 page defaults:
175 * %%PageBoundingBox: <int> <int> <int> <int>
176 * %%PageOrientation: Portrait|Landscape
177 * %%PageMedia: <text>
180 * This section explicitly begins and ends with the above comments.
182 * The prolog section either explicitly starts with %%BeginProlog or
183 * implicitly with any nonblank line.
188 * The Prolog should end with %%EndProlog, however the proglog implicitly
189 * ends when %%BeginSetup, %%Page, %%Trailer or %%EOF are encountered.
191 * The Setup section is where the version 2 page defaults are found.
192 * This section either explicitly begins with %%BeginSetup or implicitly
193 * with any nonblank line after the Prolog.
196 * %%PageBoundingBox: <int> <int> <int> <int>
197 * %%PageOrientation: Portrait|Landscape
198 * %%PaperSize: <text>
201 * The Setup should end with %%EndSetup, however the setup implicitly
202 * ends when %%Page, %%Trailer or %%EOF are encountered.
204 * Next each page starts explicitly with %%Page and ends implicitly with
205 * %%Page or %%Trailer or %%EOF. The following comments are recognized:
207 * %%Page: <text> <uint>
208 * %%PageBoundingBox: <int> <int> <int> <int>|(atend)
209 * %%PageOrientation: Portrait|Landscape
210 * %%PageMedia: <text>
211 * %%PaperSize: <text>
213 * The tralier section start explicitly with %%Trailer and end with %%EOF.
214 * The following comment are examined with the proper (atend) notation
215 * was used in the header:
218 * %%BoundingBox: <int> <int> <int> <int>|(atend)
219 * %%Orientation: Portrait|Landscape|(atend)
220 * %%Pages: <uint> [<int>]|(atend)
221 * %%PageOrder: Ascend|Descend|Special|(atend)
225 * + A DC-3 received severe damage to one of its wings. The wing was a total
226 * loss. There was no replacement readily available, so the mechanic
227 * installed a wing from a DC-2.
233 psscan(FILE * file, int respect_eof, const gchar * fname)
235 struct document *doc;
237 int pages_set = NONE;
238 int page_order_set = NONE;
239 int orientation_set = NONE;
240 int page_bb_set = NONE;
241 int page_size_set = NONE;
242 int preread; /* flag which tells the readline isn't needed */
244 unsigned int maxpages = 0;
245 unsigned int nextpage = 1; /* Next expected page */
246 unsigned int thispage;
247 int ignore = 0; /* whether to ignore page ordinals */
250 char text[PSLINELENGTH]; /* Temporary storage for text */
251 long position; /* Position of the current line */
252 long beginsection; /* Position of the beginning of the section */
253 unsigned int line_len; /* Length of the current line */
254 unsigned int section_len; /* Place to accumulate the section length */
255 char *next_char; /* 1st char after text returned by get_next_text() */
258 GtkGSPaperSize *papersizes = gtk_gs_defaults_get_paper_sizes();
266 fd = ps_io_init(file);
267 if (!readline(fd, &line, &position, &line_len)) {
268 fprintf(stderr, "Warning: empty file.\n");
273 /* HP printer job language data follows. Some printer drivers add pjl
274 * commands to switch a pjl printer to postscript mode. If no PS header
275 * follows, this seems to be a real pjl file. */
276 if(iscomment(line, "\033%-12345X@PJL")) {
277 /* read until first DSC comment */
278 while(readline(fd, &line, &position, &line_len)
279 && (line[0] != '%')) ;
281 g_print("psscan error: input files seems to be a PJL file.\n");
286 /* Header comments */
288 /* Header should start with "%!PS-Adobe-", but some programms omit
289 * parts of this or add a ^D at the beginning. */
290 if(iscomment(line, "%!PS") || iscomment(line, "\004%!PS")) {
291 doc = g_new0(struct document, 1);
292 doc->default_page_orientation = GTK_GS_ORIENTATION_NONE;
293 doc->orientation = GTK_GS_ORIENTATION_NONE;
295 /* ignore possible leading ^D */
296 if(*line == '\004') {
301 /* Jake Hamby patch 18/3/98 */
304 sscanf(line, "%*s %256s", text);
305 /*doc->epsf = iscomment(text, "EPSF-"); */
306 doc->epsf = iscomment(text, "EPSF"); /* Hamby - This line changed */
307 doc->beginheader = position;
308 section_len = line_len;
311 /* There are postscript documents that do not have
312 %PS at the beginning, usually unstructured. We should GS decide
313 For instance, the tech reports at this university:
315 http://svrc.it.uq.edu.au/Bibliography/svrc-tr.html?94-45
317 add ugly PostScript before the actual document.
320 able to display them correctly as unstructured PS.
322 In a way, this makes sense, a program PostScript does not need
323 the !PS at the beginning.
325 /* use a test command to determine if ghostscript can
326 understand this document! */
329 test_cmd = g_strdup_printf
330 ("%s -dNOPAUSE -dBATCH -sDEVICE=nullpage %s "
331 "1>/dev/null 2>/dev/null", gtk_gs_defaults_get_interpreter_cmd(), fname);
332 if(system(test_cmd) != 0) {
337 doc = g_new0(struct document, 1);
338 doc->default_page_orientation = GTK_GS_ORIENTATION_NONE;
339 doc->orientation = GTK_GS_ORIENTATION_NONE;
344 while(preread || readline(fd, &line, &position, &line_len)) {
346 section_len += line_len;
349 iscomment(line + 1, "%EndComments") ||
350 line[1] == ' ' || line[1] == '\t' || line[1] == '\n' ||
354 else if(line[1] != '%') {
357 else if(doc->title == NULL && iscomment(line + 2, "Title:")) {
358 doc->title = gettextline(line + length("%%Title:"));
360 else if(doc->date == NULL && iscomment(line + 2, "CreationDate:")) {
361 doc->date = gettextline(line + length("%%CreationDate:"));
363 else if(bb_set == NONE && iscomment(line + 2, "BoundingBox:")) {
364 sscanf(line + length("%%BoundingBox:"), "%256s", text);
365 if(strcmp(text, "(atend)") == 0) {
369 if(sscanf(line + length("%%BoundingBox:"), "%d %d %d %d",
370 &(doc->boundingbox[LLX]),
371 &(doc->boundingbox[LLY]),
372 &(doc->boundingbox[URX]), &(doc->boundingbox[URY])) == 4)
375 float fllx, flly, furx, fury;
376 if(sscanf(line + length("%%BoundingBox:"), "%f %f %f %f",
377 &fllx, &flly, &furx, &fury) == 4) {
379 doc->boundingbox[LLX] = fllx;
380 doc->boundingbox[LLY] = flly;
381 doc->boundingbox[URX] = furx;
382 doc->boundingbox[URY] = fury;
383 if(fllx < doc->boundingbox[LLX])
384 doc->boundingbox[LLX]--;
385 if(flly < doc->boundingbox[LLY])
386 doc->boundingbox[LLY]--;
387 if(furx > doc->boundingbox[URX])
388 doc->boundingbox[URX]++;
389 if(fury > doc->boundingbox[URY])
390 doc->boundingbox[URY]++;
395 else if(orientation_set == NONE && iscomment(line + 2, "Orientation:")) {
396 sscanf(line + length("%%Orientation:"), "%256s", text);
397 if(strcmp(text, "(atend)") == 0) {
398 orientation_set = ATEND;
400 else if(strcmp(text, "Portrait") == 0) {
401 doc->orientation = GTK_GS_ORIENTATION_PORTRAIT;
404 else if(strcmp(text, "Landscape") == 0) {
405 doc->orientation = GTK_GS_ORIENTATION_LANDSCAPE;
408 else if(strcmp(text, "Seascape") == 0) {
409 doc->orientation = GTK_GS_ORIENTATION_SEASCAPE;
413 else if(page_order_set == NONE && iscomment(line + 2, "PageOrder:")) {
414 sscanf(line + length("%%PageOrder:"), "%256s", text);
415 if(strcmp(text, "(atend)") == 0) {
416 page_order_set = ATEND;
418 else if(strcmp(text, "Ascend") == 0) {
419 doc->pageorder = ASCEND;
422 else if(strcmp(text, "Descend") == 0) {
423 doc->pageorder = DESCEND;
426 else if(strcmp(text, "Special") == 0) {
427 doc->pageorder = SPECIAL;
431 else if(pages_set == NONE && iscomment(line + 2, "Pages:")) {
432 sscanf(line + length("%%Pages:"), "%256s", text);
433 if(strcmp(text, "(atend)") == 0) {
437 switch (sscanf(line + length("%%Pages:"), "%d %d", &maxpages, &i)) {
439 if(page_order_set == NONE) {
441 doc->pageorder = DESCEND;
445 doc->pageorder = SPECIAL;
449 doc->pageorder = ASCEND;
455 doc->pages = pages_new(NULL, 0, maxpages);
459 else if(doc->numsizes == NONE && iscomment(line + 2, "DocumentMedia:")) {
461 doc->size = g_new0(GtkGSPaperSize, 1);
463 get_next_text(line + length("%%DocumentMedia:"), &next_char);
464 if(doc->size[0].name != NULL) {
465 if(sscanf(next_char, "%f %f", &w, &h) == 2) {
466 doc->size[0].width = w + 0.5;
467 doc->size[0].height = h + 0.5;
469 if(doc->size[0].width != 0 && doc->size[0].height != 0)
472 g_free(doc->size[0].name);
475 while(readline(fd, &line, &position, &line_len) &&
476 DSCcomment(line) && iscomment(line + 2, "+")) {
477 section_len += line_len;
478 doc->size = g_renew(GtkGSPaperSize, doc->size, doc->numsizes + 1);
479 doc->size[doc->numsizes].name =
480 get_next_text(line + length("%%+"), &next_char);
481 if(doc->size[doc->numsizes].name != NULL) {
482 if(sscanf(next_char, "%f %f", &w, &h) == 2) {
483 doc->size[doc->numsizes].width = w + 0.5;
484 doc->size[doc->numsizes].height = h + 0.5;
486 if(doc->size[doc->numsizes].width != 0 &&
487 doc->size[doc->numsizes].height != 0)
490 g_free(doc->size[doc->numsizes].name);
493 section_len += line_len;
494 if(doc->numsizes != 0)
495 doc->default_page_size = doc->size;
497 else if(doc->numsizes == NONE && iscomment(line + 2, "DocumentPaperSizes:")) {
499 doc->size = g_new0(GtkGSPaperSize, 1);
501 get_next_text(line + length("%%DocumentPaperSizes:"), &next_char);
502 if(doc->size[0].name != NULL) {
503 doc->size[0].width = 0;
504 doc->size[0].height = 0;
505 for(dmp = papersizes; dmp->name != NULL; dmp++) {
506 /* Note: Paper size comment uses down cased paper size
507 * name. Case insensitive compares are only used for
508 * PaperSize comments.
510 if(strcasecmp(doc->size[0].name, dmp->name) == 0) {
511 g_free(doc->size[0].name);
512 doc->size[0].name = g_strdup(dmp->name);
513 doc->size[0].width = dmp->width;
514 doc->size[0].height = dmp->height;
518 if(doc->size[0].width != 0 && doc->size[0].height != 0)
521 g_free(doc->size[0].name);
523 while((cp = get_next_text(next_char, &next_char))) {
524 doc->size = g_renew(GtkGSPaperSize, doc->size, doc->numsizes + 1);
525 doc->size[doc->numsizes].name = cp;
526 doc->size[doc->numsizes].width = 0;
527 doc->size[doc->numsizes].height = 0;
528 for(dmp = papersizes; dmp->name != NULL; dmp++) {
529 /* Note: Paper size comment uses down cased paper size
530 * name. Case insensitive compares are only used for
531 * PaperSize comments.
533 if(strcasecmp(doc->size[doc->numsizes].name, dmp->name) == 0) {
534 g_free(doc->size[doc->numsizes].name);
535 doc->size[doc->numsizes].name = g_strdup(dmp->name);
536 doc->size[doc->numsizes].name = dmp->name;
537 doc->size[doc->numsizes].width = dmp->width;
538 doc->size[doc->numsizes].height = dmp->height;
542 if(doc->size[doc->numsizes].width != 0 &&
543 doc->size[doc->numsizes].height != 0)
546 g_free(doc->size[doc->numsizes].name);
549 while(readline(fd, &line, &position, &line_len) &&
550 DSCcomment(line) && iscomment(line + 2, "+")) {
551 section_len += line_len;
552 next_char = line + length("%%+");
553 while((cp = get_next_text(next_char, &next_char))) {
554 doc->size = g_renew(GtkGSPaperSize, doc->size, doc->numsizes + 1);
555 doc->size[doc->numsizes].name = cp;
556 doc->size[doc->numsizes].width = 0;
557 doc->size[doc->numsizes].height = 0;
558 for(dmp = papersizes; dmp->name != NULL; dmp++) {
559 /* Note: Paper size comment uses down cased paper size
560 * name. Case insensitive compares are only used for
561 * PaperSize comments.
563 if(strcasecmp(doc->size[doc->numsizes].name, dmp->name) == 0) {
564 doc->size[doc->numsizes].width = dmp->width;
565 doc->size[doc->numsizes].height = dmp->height;
569 if(doc->size[doc->numsizes].width != 0 &&
570 doc->size[doc->numsizes].height != 0)
573 g_free(doc->size[doc->numsizes].name);
576 section_len += line_len;
577 if(doc->numsizes != 0)
578 doc->default_page_size = doc->size;
582 if(DSCcomment(line) && iscomment(line + 2, "EndComments")) {
583 readline(fd, &line, &position, &line_len);
584 section_len += line_len;
586 doc->endheader = position;
587 doc->lenheader = section_len - line_len;
589 /* Optional Preview comments for encapsulated PostScript files */
591 beginsection = position;
592 section_len = line_len;
593 while(blank(line) && readline(fd, &line, &position, &line_len)) {
594 section_len += line_len;
597 if(doc->epsf && DSCcomment(line) && iscomment(line + 2, "BeginPreview")) {
598 doc->beginpreview = beginsection;
600 while(readline(fd, &line, &position, &line_len) &&
601 !(DSCcomment(line) && iscomment(line + 2, "EndPreview"))) {
602 section_len += line_len;
604 section_len += line_len;
605 readline(fd, &line, &position, &line_len);
606 section_len += line_len;
607 doc->endpreview = position;
608 doc->lenpreview = section_len - line_len;
611 /* Page Defaults for Version 3.0 files */
613 if(beginsection == 0) {
614 beginsection = position;
615 section_len = line_len;
617 while(blank(line) && readline(fd, &line, &position, &line_len)) {
618 section_len += line_len;
621 if(DSCcomment(line) && iscomment(line + 2, "BeginDefaults")) {
622 doc->begindefaults = beginsection;
624 while(readline(fd, &line, &position, &line_len) &&
625 !(DSCcomment(line) && iscomment(line + 2, "EndDefaults"))) {
626 section_len += line_len;
627 if(!DSCcomment(line)) {
630 else if(doc->default_page_orientation == NONE &&
631 iscomment(line + 2, "PageOrientation:")) {
632 sscanf(line + length("%%PageOrientation:"), "%256s", text);
633 if(strcmp(text, "Portrait") == 0) {
634 doc->default_page_orientation = GTK_GS_ORIENTATION_PORTRAIT;
636 else if(strcmp(text, "Landscape") == 0) {
637 doc->default_page_orientation = GTK_GS_ORIENTATION_LANDSCAPE;
639 else if(strcmp(text, "Seascape") == 0) {
640 doc->default_page_orientation = GTK_GS_ORIENTATION_SEASCAPE;
643 else if(page_size_set == NONE && iscomment(line + 2, "PageMedia:")) {
644 cp = get_next_text(line + length("%%PageMedia:"), NULL);
645 for(dmp = doc->size, i = 0; i < doc->numsizes; i++, dmp++) {
646 if(strcmp(cp, dmp->name) == 0) {
647 doc->default_page_size = dmp;
654 else if(page_bb_set == NONE && iscomment(line + 2, "PageBoundingBox:")) {
655 if(sscanf(line + length("%%PageBoundingBox:"), "%d %d %d %d",
656 &(doc->default_page_boundingbox[LLX]),
657 &(doc->default_page_boundingbox[LLY]),
658 &(doc->default_page_boundingbox[URX]),
659 &(doc->default_page_boundingbox[URY])) == 4)
662 float fllx, flly, furx, fury;
664 (line + length("%%PageBoundingBox:"), "%f %f %f %f",
665 &fllx, &flly, &furx, &fury) == 4) {
667 doc->default_page_boundingbox[LLX] = fllx;
668 doc->default_page_boundingbox[LLY] = flly;
669 doc->default_page_boundingbox[URX] = furx;
670 doc->default_page_boundingbox[URY] = fury;
671 if(fllx < doc->default_page_boundingbox[LLX])
672 doc->default_page_boundingbox[LLX]--;
673 if(flly < doc->default_page_boundingbox[LLY])
674 doc->default_page_boundingbox[LLY]--;
675 if(furx > doc->default_page_boundingbox[URX])
676 doc->default_page_boundingbox[URX]++;
677 if(fury > doc->default_page_boundingbox[URY])
678 doc->default_page_boundingbox[URY]++;
683 section_len += line_len;
684 readline(fd, &line, &position, &line_len);
685 section_len += line_len;
686 doc->enddefaults = position;
687 doc->lendefaults = section_len - line_len;
690 /* Document Prolog */
692 if(beginsection == 0) {
693 beginsection = position;
694 section_len = line_len;
696 while(blank(line) && readline(fd, &line, &position, &line_len)) {
697 section_len += line_len;
700 if(!(DSCcomment(line) &&
701 (iscomment(line + 2, "BeginSetup") ||
702 iscomment(line + 2, "Page:") ||
703 iscomment(line + 2, "Trailer") || iscomment(line + 2, "EOF")))) {
704 doc->beginprolog = beginsection;
709 readline(fd, &line, &position, &line_len)) &&
710 !(DSCcomment(line) &&
711 (iscomment(line + 2, "EndProlog") ||
712 iscomment(line + 2, "BeginSetup") ||
713 iscomment(line + 2, "Page:") ||
714 iscomment(line + 2, "Trailer") || iscomment(line + 2, "EOF")))) {
716 section_len += line_len;
719 section_len += line_len;
720 if(DSCcomment(line) && iscomment(line + 2, "EndProlog")) {
721 readline(fd, &line, &position, &line_len);
722 section_len += line_len;
724 doc->endprolog = position;
725 doc->lenprolog = section_len - line_len;
728 /* Document Setup, Page Defaults found here for Version 2 files */
730 if(beginsection == 0) {
731 beginsection = position;
732 section_len = line_len;
734 while(blank(line) && readline(fd, &line, &position, &line_len)) {
735 section_len += line_len;
738 if(!(DSCcomment(line) &&
739 (iscomment(line + 2, "Page:") ||
740 iscomment(line + 2, "Trailer") ||
741 (respect_eof && iscomment(line + 2, "EOF"))))) {
742 doc->beginsetup = beginsection;
746 readline(fd, &line, &position, &line_len)) &&
747 !(DSCcomment(line) &&
748 (iscomment(line + 2, "EndSetup") ||
749 iscomment(line + 2, "Page:") ||
750 iscomment(line + 2, "Trailer") ||
751 (respect_eof && iscomment(line + 2, "EOF"))))) {
753 section_len += line_len;
755 if(!DSCcomment(line)) {
758 else if(doc->default_page_orientation == NONE &&
759 iscomment(line + 2, "PageOrientation:")) {
760 sscanf(line + length("%%PageOrientation:"), "%256s", text);
761 if(strcmp(text, "Portrait") == 0) {
762 doc->default_page_orientation = GTK_GS_ORIENTATION_PORTRAIT;
764 else if(strcmp(text, "Landscape") == 0) {
765 doc->default_page_orientation = GTK_GS_ORIENTATION_LANDSCAPE;
767 else if(strcmp(text, "Seascape") == 0) {
768 doc->default_page_orientation = GTK_GS_ORIENTATION_SEASCAPE;
771 else if(page_size_set == NONE && iscomment(line + 2, "PaperSize:")) {
772 cp = get_next_text(line + length("%%PaperSize:"), NULL);
773 for(dmp = doc->size, i = 0; i < doc->numsizes; i++, dmp++) {
774 /* Note: Paper size comment uses down cased paper size
775 * name. Case insensitive compares are only used for
776 * PaperSize comments.
778 if(strcasecmp(cp, dmp->name) == 0) {
779 doc->default_page_size = dmp;
786 else if(page_bb_set == NONE && iscomment(line + 2, "PageBoundingBox:")) {
787 if(sscanf(line + length("%%PageBoundingBox:"), "%d %d %d %d",
788 &(doc->default_page_boundingbox[LLX]),
789 &(doc->default_page_boundingbox[LLY]),
790 &(doc->default_page_boundingbox[URX]),
791 &(doc->default_page_boundingbox[URY])) == 4)
794 float fllx, flly, furx, fury;
796 (line + length("%%PageBoundingBox:"), "%f %f %f %f",
797 &fllx, &flly, &furx, &fury) == 4) {
799 doc->default_page_boundingbox[LLX] = fllx;
800 doc->default_page_boundingbox[LLY] = flly;
801 doc->default_page_boundingbox[URX] = furx;
802 doc->default_page_boundingbox[URY] = fury;
803 if(fllx < doc->default_page_boundingbox[LLX])
804 doc->default_page_boundingbox[LLX]--;
805 if(flly < doc->default_page_boundingbox[LLY])
806 doc->default_page_boundingbox[LLY]--;
807 if(furx > doc->default_page_boundingbox[URX])
808 doc->default_page_boundingbox[URX]++;
809 if(fury > doc->default_page_boundingbox[URY])
810 doc->default_page_boundingbox[URY]++;
815 section_len += line_len;
816 if(DSCcomment(line) && iscomment(line + 2, "EndSetup")) {
817 readline(fd, &line, &position, &line_len);
818 section_len += line_len;
820 doc->endsetup = position;
821 doc->lensetup = section_len - line_len;
824 /* Added this (Nov. 2, 1999) when I noticed that
825 a Postscript file would load in gv but not in ggv
827 dmg@csg.uwaterloo.ca */
829 /* BEGIN Windows NT fix ###jp###
830 Mark Pfeifer (pfeiferm%ppddev@comet.cmis.abbott.com) told me
831 about problems when viewing Windows NT 3.51 generated postscript
832 files with gv. He found that the relevant postscript files
833 show important postscript code after the '%%EndSetup' and before
834 the first page comment '%%Page: x y'.
836 if(doc->beginsetup) {
837 while(!(DSCcomment(line) &&
838 (iscomment(line + 2, "EndSetup") ||
839 (iscomment(line + 2, "Page:") ||
840 iscomment(line + 2, "Trailer") ||
841 (respect_eof && iscomment(line + 2, "EOF"))))) &&
842 (readline(fd, &line, &position, &line_len))) {
843 section_len += line_len;
844 doc->lensetup = section_len - line_len;
845 doc->endsetup = position;
848 /* END Windows NT fix ###jp## */
850 /* Individual Pages */
852 if(beginsection == 0) {
853 beginsection = position;
854 section_len = line_len;
856 while(blank(line) && readline(fd, &line, &position, &line_len)) {
857 section_len += line_len;
862 while(DSCcomment(line) && iscomment(line + 2, "Page:")) {
865 doc->pages = pages_new(NULL, 0, maxpages);
867 label = get_next_text(line + length("%%Page:"), &next_char);
868 if(sscanf(next_char, "%d", &thispage) != 1)
871 ignore = thispage != 1;
873 if(!ignore && thispage != nextpage) {
879 if(doc->numpages == maxpages) {
881 doc->pages = pages_new(doc->pages, maxpages - 1, maxpages);
884 doc->pages[doc->numpages].label = label;
886 doc->pages[doc->numpages].begin = beginsection;
890 doc->pages[doc->numpages].begin = position;
891 section_len = line_len;
894 while(readline(fd, &line, &position, &line_len) &&
895 !(DSCcomment(line) &&
896 (iscomment(line + 2, "Page:") ||
897 iscomment(line + 2, "Trailer") ||
898 (respect_eof && iscomment(line + 2, "EOF"))))) {
899 section_len += line_len;
900 if(!DSCcomment(line)) {
903 else if(doc->pages[doc->numpages].orientation == NONE &&
904 iscomment(line + 2, "PageOrientation:")) {
905 sscanf(line + length("%%PageOrientation:"), "%256s", text);
906 if(strcmp(text, "Portrait") == 0) {
907 doc->pages[doc->numpages].orientation = GTK_GS_ORIENTATION_PORTRAIT;
909 else if(strcmp(text, "Landscape") == 0) {
910 doc->pages[doc->numpages].orientation = GTK_GS_ORIENTATION_LANDSCAPE;
912 else if(strcmp(text, "Seascape") == 0) {
913 doc->pages[doc->numpages].orientation = GTK_GS_ORIENTATION_SEASCAPE;
916 else if(doc->pages[doc->numpages].size == NULL &&
917 iscomment(line + 2, "PageMedia:")) {
918 cp = get_next_text(line + length("%%PageMedia:"), NULL);
919 for(dmp = doc->size, i = 0; i < doc->numsizes; i++, dmp++) {
920 if(strcmp(cp, dmp->name) == 0) {
921 doc->pages[doc->numpages].size = dmp;
927 else if(doc->pages[doc->numpages].size == NULL &&
928 iscomment(line + 2, "PaperSize:")) {
929 cp = get_next_text(line + length("%%PaperSize:"), NULL);
930 for(dmp = doc->size, i = 0; i < doc->numsizes; i++, dmp++) {
931 /* Note: Paper size comment uses down cased paper size
932 * name. Case insensitive compares are only used for
933 * PaperSize comments.
935 if(strcasecmp(cp, dmp->name) == 0) {
936 doc->pages[doc->numpages].size = dmp;
942 else if((page_bb_set == NONE || page_bb_set == ATEND) &&
943 iscomment(line + 2, "PageBoundingBox:")) {
944 sscanf(line + length("%%PageBoundingBox:"), "%256s", text);
945 if(strcmp(text, "(atend)") == 0) {
950 (line + length("%%PageBoundingBox:"), "%d %d %d %d",
951 &(doc->pages[doc->numpages].boundingbox[LLX]),
952 &(doc->pages[doc->numpages].boundingbox[LLY]),
953 &(doc->pages[doc->numpages].boundingbox[URX]),
954 &(doc->pages[doc->numpages].boundingbox[URY])) == 4) {
955 if(page_bb_set == NONE)
959 float fllx, flly, furx, fury;
960 if(sscanf(line + length("%%PageBoundingBox:"),
961 "%f %f %f %f", &fllx, &flly, &furx, &fury) == 4) {
962 if(page_bb_set == NONE)
964 doc->pages[doc->numpages].boundingbox[LLX] = fllx;
965 doc->pages[doc->numpages].boundingbox[LLY] = flly;
966 doc->pages[doc->numpages].boundingbox[URX] = furx;
967 doc->pages[doc->numpages].boundingbox[URY] = fury;
968 if(fllx < doc->pages[doc->numpages].boundingbox[LLX])
969 doc->pages[doc->numpages].boundingbox[LLX]--;
970 if(flly < doc->pages[doc->numpages].boundingbox[LLY])
971 doc->pages[doc->numpages].boundingbox[LLY]--;
972 if(furx > doc->pages[doc->numpages].boundingbox[URX])
973 doc->pages[doc->numpages].boundingbox[URX]++;
974 if(fury > doc->pages[doc->numpages].boundingbox[URY])
975 doc->pages[doc->numpages].boundingbox[URY]++;
981 section_len += line_len;
982 doc->pages[doc->numpages].end = position;
983 doc->pages[doc->numpages].len = section_len - line_len;
987 /* Document Trailer */
990 doc->begintrailer = beginsection;
994 doc->begintrailer = position;
995 section_len = line_len;
1000 readline(fd, &line, &position, &line_len)) &&
1001 !(respect_eof && DSCcomment(line) && iscomment(line + 2, "EOF"))) {
1003 section_len += line_len;
1005 if(!DSCcomment(line)) {
1008 else if(iscomment(line + 2, "Page:")) {
1009 g_free(get_next_text(line + length("%%Page:"), &next_char));
1010 if(sscanf(next_char, "%d", &thispage) != 1)
1012 if(!ignore && thispage == nextpage) {
1013 if(doc->numpages > 0) {
1014 doc->pages[doc->numpages - 1].end = position;
1015 doc->pages[doc->numpages - 1].len += section_len - line_len;
1019 doc->endsetup = position;
1020 doc->endsetup += section_len - line_len;
1022 else if(doc->endprolog) {
1023 doc->endprolog = position;
1024 doc->endprolog += section_len - line_len;
1030 else if(!respect_eof && iscomment(line + 2, "Trailer")) {
1031 /* What we thought was the start of the trailer was really */
1032 /* the trailer of an EPS on the page. */
1033 /* Set the end of the page to this trailer and keep scanning. */
1034 if(doc->numpages > 0) {
1035 doc->pages[doc->numpages - 1].end = position;
1036 doc->pages[doc->numpages - 1].len += section_len - line_len;
1038 doc->begintrailer = position;
1039 section_len = line_len;
1041 else if(bb_set == ATEND && iscomment(line + 2, "BoundingBox:")) {
1042 if(sscanf(line + length("%%BoundingBox:"), "%d %d %d %d",
1043 &(doc->boundingbox[LLX]),
1044 &(doc->boundingbox[LLY]),
1045 &(doc->boundingbox[URX]), &(doc->boundingbox[URY])) != 4) {
1046 float fllx, flly, furx, fury;
1047 if(sscanf(line + length("%%BoundingBox:"), "%f %f %f %f",
1048 &fllx, &flly, &furx, &fury) == 4) {
1049 doc->boundingbox[LLX] = fllx;
1050 doc->boundingbox[LLY] = flly;
1051 doc->boundingbox[URX] = furx;
1052 doc->boundingbox[URY] = fury;
1053 if(fllx < doc->boundingbox[LLX])
1054 doc->boundingbox[LLX]--;
1055 if(flly < doc->boundingbox[LLY])
1056 doc->boundingbox[LLY]--;
1057 if(furx > doc->boundingbox[URX])
1058 doc->boundingbox[URX]++;
1059 if(fury > doc->boundingbox[URY])
1060 doc->boundingbox[URY]++;
1064 else if(orientation_set == ATEND && iscomment(line + 2, "Orientation:")) {
1065 sscanf(line + length("%%Orientation:"), "%256s", text);
1066 if(strcmp(text, "Portrait") == 0) {
1067 doc->orientation = GTK_GS_ORIENTATION_PORTRAIT;
1069 else if(strcmp(text, "Landscape") == 0) {
1070 doc->orientation = GTK_GS_ORIENTATION_LANDSCAPE;
1072 else if(strcmp(text, "Seascape") == 0) {
1073 doc->orientation = GTK_GS_ORIENTATION_SEASCAPE;
1076 else if(page_order_set == ATEND && iscomment(line + 2, "PageOrder:")) {
1077 sscanf(line + length("%%PageOrder:"), "%256s", text);
1078 if(strcmp(text, "Ascend") == 0) {
1079 doc->pageorder = ASCEND;
1081 else if(strcmp(text, "Descend") == 0) {
1082 doc->pageorder = DESCEND;
1084 else if(strcmp(text, "Special") == 0) {
1085 doc->pageorder = SPECIAL;
1088 else if(pages_set == ATEND && iscomment(line + 2, "Pages:")) {
1089 if(sscanf(line + length("%%Pages:"), "%*u %d", &i) == 1) {
1090 if(page_order_set == NONE) {
1092 doc->pageorder = DESCEND;
1094 doc->pageorder = SPECIAL;
1096 doc->pageorder = ASCEND;
1101 section_len += line_len;
1102 if(DSCcomment(line) && iscomment(line + 2, "EOF")) {
1103 readline(fd, &line, &position, &line_len);
1104 section_len += line_len;
1106 doc->endtrailer = position;
1107 doc->lentrailer = section_len - line_len;
1110 section_len = line_len;
1112 while(preread || readline(line, sizeof line, file, &position, &line_len)) {
1114 section_len += line_len;
1116 if(DSCcomment(line) && iscomment(line + 2, "Page:")) {
1117 g_free(get_next_text(line + length("%%Page:"), &next_char));
1118 if(sscanf(next_char, "%d", &thispage) != 1)
1120 if(!ignore && thispage == nextpage) {
1121 if(doc->numpages > 0) {
1122 doc->pages[doc->numpages - 1].end = position;
1123 doc->pages[doc->numpages - 1].len += doc->lentrailer +
1124 section_len - line_len;
1128 doc->endsetup = position;
1129 doc->endsetup += doc->lentrailer + section_len - line_len;
1131 else if(doc->endprolog) {
1132 doc->endprolog = position;
1133 doc->endprolog += doc->lentrailer + section_len - line_len;
1145 * psfree -- free dynamic storage associated with document structure.
1150 struct document *doc;
1156 printf("This document exists\n");
1158 for(i = 0; i < doc->numpages; i++) {
1159 if(doc->pages[i].label)
1160 g_free(doc->pages[i].label);
1162 for(i = 0; i < doc->numsizes; i++) {
1163 if(doc->size[i].name)
1164 g_free(doc->size[i].name);
1179 * gettextine -- skip over white space and return the rest of the line.
1180 * If the text begins with '(' return the text string
1181 * using get_next_text().
1185 gettextline(char *line)
1189 while(*line && (*line == ' ' || *line == '\t'))
1192 return get_next_text(line, NULL);
1195 if(strlen(line) == 0)
1198 cp = g_strdup(line);
1200 /* Remove end of line */
1201 if(cp[strlen(line) - 2] == '\r' && cp[strlen(line) - 1] == '\n')
1202 /* Handle DOS \r\n */
1203 cp[strlen(line) - 2] = '\0';
1204 else if(cp[strlen(line) - 1] == '\n' || cp[strlen(line) - 1] == '\r')
1205 /* Handle mac and unix */
1206 cp[strlen(line) - 1] = '\0';
1213 * get_next_text -- return the next text string on the line.
1214 * return NULL if nothing is present.
1218 get_next_text(line, next_char)
1222 char text[PSLINELENGTH]; /* Temporary storage for text */
1226 while(*line && (*line == ' ' || *line == '\t'))
1233 while(*line && !(*line == ')' && level == 0)) {
1235 if(*(line + 1) == 'n') {
1239 else if(*(line + 1) == 'r') {
1243 else if(*(line + 1) == 't') {
1247 else if(*(line + 1) == 'b') {
1251 else if(*(line + 1) == 'f') {
1255 else if(*(line + 1) == '\\') {
1259 else if(*(line + 1) == '(') {
1263 else if(*(line + 1) == ')') {
1267 else if(*(line + 1) >= '0' && *(line + 1) <= '9') {
1268 if(*(line + 2) >= '0' && *(line + 2) <= '9') {
1269 if(*(line + 3) >= '0' && *(line + 3) <= '9') {
1271 ((*(line + 1) - '0') * 8 + *(line + 2) -
1272 '0') * 8 + *(line + 3) - '0';
1276 *cp++ = (*(line + 1) - '0') * 8 + *(line + 2) - '0';
1281 *cp++ = *(line + 1) - '0';
1290 else if(*line == '(') {
1294 else if(*line == ')') {
1304 while(*line && !(*line == ' ' || *line == '\t' || *line == '\n'))
1310 if(!quoted && strlen(text) == 0)
1312 return g_strdup(text);
1316 * pscopy -- copy lines of Postscript from a section of one file
1318 * Automatically switch to binary copying whenever
1319 * %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
1320 * comments are encountered.
1324 pscopy(from, to, begin, end)
1327 long begin; /* set negative to avoid initial seek */
1330 char line[PSLINELENGTH]; /* 255 characters + 1 newline + 1 NULL */
1331 char text[PSLINELENGTH]; /* Temporary storage for text */
1337 fseek(from, begin, SEEK_SET);
1338 while(ftell(from) < end) {
1339 fgets(line, sizeof line, from);
1340 gtk_gs_doc_sink_write(to, line, strlen(line));
1342 if(!(DSCcomment(line) && iscomment(line + 2, "Begin"))) {
1345 else if(iscomment(line + 7, "Data:")) {
1347 if(sscanf(line + length("%%BeginData:"), "%d %*s %256s", &num, text) >= 1) {
1348 if(strcmp(text, "Lines") == 0) {
1349 for(i = 0; i < num; i++) {
1350 fgets(line, sizeof(line), from);
1351 gtk_gs_doc_sink_write(to, line, strlen(line));
1355 while(num > BUFSIZ) {
1356 fread(buf, sizeof(char), BUFSIZ, from);
1357 gtk_gs_doc_sink_write(to, buf, BUFSIZ);
1360 fread(buf, sizeof(char), num, from);
1361 gtk_gs_doc_sink_write(to, buf, num);
1365 else if(iscomment(line + 7, "Binary:")) {
1366 if(sscanf(line + length("%%BeginBinary:"), "%d", &num) == 1) {
1367 while(num > BUFSIZ) {
1368 fread(buf, sizeof(char), BUFSIZ, from);
1369 gtk_gs_doc_sink_write(to, buf, BUFSIZ);
1372 fread(buf, sizeof(char), num, from);
1373 gtk_gs_doc_sink_write(to, buf, num);
1380 * pscopyuntil -- copy lines of Postscript from a section of one file
1381 * to another file until a particular comment is reached.
1382 * Automatically switch to binary copying whenever
1383 * %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
1384 * comments are encountered.
1388 pscopyuntil(FILE * from, GtkGSDocSink * to, long begin, long end,
1389 const char *comment)
1391 char line[PSLINELENGTH]; /* 255 characters + 1 newline + 1 NULL */
1392 char text[PSLINELENGTH]; /* Temporary storage for text */
1399 comment_length = strlen(comment);
1403 fseek(from, begin, SEEK_SET);
1405 while(ftell(from) < end && !feof(from)) {
1406 fgets(line, sizeof line, from);
1408 /* iscomment cannot be used here,
1409 * because comment_length is not known at compile time. */
1410 if(comment != NULL && strncmp(line, comment, comment_length) == 0) {
1411 return g_strdup(line);
1413 gtk_gs_doc_sink_write(to, line, strlen(line));
1414 if(!(DSCcomment(line) && iscomment(line + 2, "Begin"))) {
1417 else if(iscomment(line + 7, "Data:")) {
1419 if(sscanf(line + length("%%BeginData:"), "%d %*s %256s", &num, text) >= 1) {
1420 if(strcmp(text, "Lines") == 0) {
1421 for(i = 0; i < num; i++) {
1422 fgets(line, sizeof line, from);
1423 gtk_gs_doc_sink_write(to, line, strlen(line));
1427 while(num > BUFSIZ) {
1428 fread(buf, sizeof(char), BUFSIZ, from);
1429 gtk_gs_doc_sink_write(to, buf, BUFSIZ);
1432 fread(buf, sizeof(char), num, from);
1433 gtk_gs_doc_sink_write(to, buf, num);
1437 else if(iscomment(line + 7, "Binary:")) {
1438 if(sscanf(line + length("%%BeginBinary:"), "%d", &num) == 1) {
1439 while(num > BUFSIZ) {
1440 fread(buf, sizeof(char), BUFSIZ, from);
1441 gtk_gs_doc_sink_write(to, buf, BUFSIZ);
1444 fread(buf, sizeof(char), num, from);
1445 gtk_gs_doc_sink_write(to, buf, num);
1453 * blank -- determine whether the line contains nothing but whitespace.
1461 while(*cp == ' ' || *cp == '\t')
1463 return *cp == '\n' || (*cp == '%' && (line[0] != '%' || line[1] != '%'));
1466 /*##########################################################*/
1468 /* Copy the headers, marked pages, and trailer to fp */
1469 /*##########################################################*/
1472 pscopydoc(GtkGSDocSink * dest,
1473 char *src_filename, struct document *d, gint * pagelist)
1476 char text[PSLINELENGTH];
1478 gboolean pages_written = FALSE;
1479 gboolean pages_atend = FALSE;
1485 src_file = fopen(src_filename, "r");
1488 for(i = 0; i < d->numpages; i++) {
1493 here = d->beginheader;
1495 while((comment = pscopyuntil(src_file, dest, here, d->endheader, "%%Pages:"))) {
1496 here = ftell(src_file);
1497 if(pages_written || pages_atend) {
1501 sscanf(comment + length("%%Pages:"), "%256s", text);
1502 if(strcmp(text, "(atend)") == 0) {
1503 gtk_gs_doc_sink_write(dest, comment, strlen(comment));
1507 switch (sscanf(comment + length("%%Pages:"), "%*d %d", &i)) {
1509 gtk_gs_doc_sink_printf(dest, "%%%%Pages: %d %d\n", pages, i);
1512 gtk_gs_doc_sink_printf(dest, "%%%%Pages: %d\n", pages);
1515 pages_written = TRUE;
1519 pscopyuntil(src_file, dest, d->beginpreview, d->endpreview, NULL);
1520 pscopyuntil(src_file, dest, d->begindefaults, d->enddefaults, NULL);
1521 pscopyuntil(src_file, dest, d->beginprolog, d->endprolog, NULL);
1522 pscopyuntil(src_file, dest, d->beginsetup, d->endsetup, NULL);
1524 for(i = 0; i < d->numpages; i++) {
1525 if(d->pageorder == DESCEND)
1526 j = (d->numpages - 1) - i;
1531 comment = pscopyuntil(src_file, dest,
1532 d->pages[i].begin, d->pages[i].end, "%%Page:");
1533 gtk_gs_doc_sink_printf(dest, "%%%%Page: %s %d\n",
1534 d->pages[i].label, page++);
1536 pscopyuntil(src_file, dest, -1, d->pages[i].end, NULL);
1540 here = d->begintrailer;
1541 while((comment = pscopyuntil(src_file, dest, here, d->endtrailer,
1543 here = ftell(src_file);
1548 switch (sscanf(comment + length("%%Pages:"), "%*d %d", &i)) {
1550 gtk_gs_doc_sink_printf(dest, "%%%%Pages: %d %d\n", pages, i);
1553 gtk_gs_doc_sink_printf(dest, "%%%%Pages: %d\n", pages);
1556 pages_written = TRUE;
1563 /*----------------------------------------------------------*/
1565 /*----------------------------------------------------------*/
1567 #define FD_FILE (fd->file)
1568 #define FD_FILE_DESC (fd->file_desc)
1569 #define FD_FILEPOS (fd->filepos)
1570 #define FD_LINE_BEGIN (fd->line_begin)
1571 #define FD_LINE_END (fd->line_end)
1572 #define FD_LINE_LEN (fd->line_len)
1573 #define FD_LINE_TERMCHAR (fd->line_termchar)
1574 #define FD_BUF (fd->buf)
1575 #define FD_BUF_END (fd->buf_end)
1576 #define FD_BUF_SIZE (fd->buf_size)
1577 #define FD_STATUS (fd->status)
1579 #define FD_STATUS_OKAY 0
1580 #define FD_STATUS_BUFTOOLARGE 1
1581 #define FD_STATUS_NOMORECHARS 2
1583 #define LINE_CHUNK_SIZE 4096
1584 #define MAX_PS_IO_FGETCHARS_BUF_SIZE 57344
1585 #define BREAK_PS_IO_FGETCHARS_BUF_SIZE 49152
1587 static FileData ps_io_init(file)
1591 size_t size = sizeof(FileDataStruct);
1593 fd = (FileData) g_malloc(size);
1594 memset((void*) fd ,0,(size_t)size);
1598 FD_FILE_DESC = fileno(file);
1599 FD_FILEPOS = ftell(file);
1600 FD_BUF_SIZE = (2*LINE_CHUNK_SIZE)+1;
1601 FD_BUF = g_malloc(FD_BUF_SIZE);
1606 /*----------------------------------------------------------*/
1608 /*----------------------------------------------------------*/
1618 /*----------------------------------------------------------*/
1620 /*----------------------------------------------------------*/
1623 ps_io_fseek(fd,offset)
1628 status=fseek(FD_FILE,(long)offset,SEEK_SET);
1629 FD_BUF_END = FD_LINE_BEGIN = FD_LINE_END = FD_LINE_LEN = 0;
1630 FD_FILEPOS = offset;
1631 FD_STATUS = FD_STATUS_OKAY;
1635 /*----------------------------------------------------------*/
1637 /*----------------------------------------------------------*/
1646 /*----------------------------------------------------------*/
1647 /* ps_io_fgetchars */
1648 /*----------------------------------------------------------*/
1650 #ifdef USE_MEMMOVE_CODE
1651 static void ps_memmove (d, s, l)
1656 if (s < d) for (s += l, d += l; l; --l) *--d = *--s;
1657 else if (s != d) for (; l; --l) *d++ = *s++;
1660 # define ps_memmove memmove
1663 static char * ps_io_fgetchars(fd,num)
1667 char *eol=NULL,*tmp;
1668 size_t size_of_char = sizeof(char);
1670 if (FD_STATUS != FD_STATUS_OKAY) {
1674 FD_BUF[FD_LINE_END] = FD_LINE_TERMCHAR; /* restoring char previously exchanged against '\0' */
1675 FD_LINE_BEGIN = FD_LINE_END;
1678 if (num<0) { /* reading whole line */
1679 if (FD_BUF_END-FD_LINE_END) {
1680 /* strpbrk is faster but fails on lines with embedded NULLs
1681 eol = strpbrk(FD_BUF+FD_LINE_END,"\n\r");
1683 tmp = FD_BUF + FD_BUF_END;
1684 eol = FD_BUF + FD_LINE_END;
1685 while (eol < tmp && *eol != '\n' && *eol != '\r') eol++;
1686 if (eol >= tmp) eol = NULL;
1688 if (*eol=='\r' && *(eol+1)=='\n') eol += 2;
1693 } else { /* reading specified num of chars */
1694 if (FD_BUF_END >= FD_LINE_BEGIN+num) {
1695 eol = FD_BUF+FD_LINE_BEGIN+num;
1700 if (FD_BUF_END - FD_LINE_BEGIN > BREAK_PS_IO_FGETCHARS_BUF_SIZE) {
1701 eol = FD_BUF + FD_BUF_END - 1;
1705 while (FD_BUF_SIZE < FD_BUF_END+LINE_CHUNK_SIZE+1) {
1706 if (FD_BUF_SIZE > MAX_PS_IO_FGETCHARS_BUF_SIZE) {
1707 /* we should never get here, since the line is broken
1708 artificially after BREAK_PS_IO_FGETCHARS_BUF_SIZE bytes. */
1709 fprintf(stderr, "gv: ps_io_fgetchars: Fatal Error: buffer became too large.\n");
1712 if (FD_LINE_BEGIN) {
1713 ps_memmove((void*)FD_BUF,(void*)(FD_BUF+FD_LINE_BEGIN),
1714 ((size_t)(FD_BUF_END-FD_LINE_BEGIN+1))*size_of_char);
1715 FD_BUF_END -= FD_LINE_BEGIN;
1718 FD_BUF_SIZE = FD_BUF_SIZE+LINE_CHUNK_SIZE+1;
1719 FD_BUF = g_realloc(FD_BUF,FD_BUF_SIZE);
1723 FD_LINE_END = FD_BUF_END;
1725 /* different existing VMS file formats require that we use read here ###jp###,10/12/96 */
1726 if (num<0) FD_BUF_END += read(FD_FILE_DESC,FD_BUF+FD_BUF_END,LINE_CHUNK_SIZE);
1727 else FD_BUF_END += fread(FD_BUF+FD_BUF_END,size_of_char,LINE_CHUNK_SIZE,FD_FILE);
1729 /* read() seems to fail sometimes (? ? ?) so we always use fread ###jp###,07/31/96*/
1730 FD_BUF_END += fread(FD_BUF+FD_BUF_END,size_of_char,LINE_CHUNK_SIZE,FD_FILE);
1733 FD_BUF[FD_BUF_END] = '\0';
1734 if (FD_BUF_END-FD_LINE_END == 0) {
1735 FD_STATUS = FD_STATUS_NOMORECHARS;
1741 FD_LINE_END = eol - FD_BUF;
1742 FD_LINE_LEN = FD_LINE_END - FD_LINE_BEGIN;
1743 FD_LINE_TERMCHAR = FD_BUF[FD_LINE_END];
1744 FD_BUF[FD_LINE_END] = '\0';
1745 #ifdef USE_FTELL_FOR_FILEPOS
1746 if (FD_LINE_END==FD_BUF_END) {
1748 For VMS we cannot assume that the record is FD_LINE_LEN bytes long
1749 on the disk. For stream_lf and stream_cr that is true, but not for
1750 other formats, since VAXC/DECC converts the formatting into a single \n.
1751 eg. variable format files have a 2-byte length and padding to an even
1752 number of characters. So, we use ftell for each record.
1753 This still will not work if we need to fseek to a \n or \r inside a
1754 variable record (ftell always returns the start of the record in this
1756 (Tim Adye, adye@v2.rl.ac.uk)
1758 FD_FILEPOS = ftell(FD_FILE);
1760 #endif /* USE_FTELL_FOR_FILEPOS */
1761 FD_FILEPOS += FD_LINE_LEN;
1763 return(FD_BUF+FD_LINE_BEGIN);
1766 /*----------------------------------------------------------*/
1769 Read the next line in the postscript file.
1770 Automatically skip over data (as indicated by
1771 %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
1773 Also, skip over included documents (as indicated by
1774 %%BeginDocument/%%EndDocument comments.)
1776 /*----------------------------------------------------------*/
1778 static char *readline (fd, lineP, positionP, line_lenP)
1782 unsigned int *line_lenP;
1784 unsigned int nbytes=0;
1788 if (positionP) *positionP = FD_FILEPOS;
1789 line = ps_io_fgetchars(fd,-1);
1792 *lineP = empty_string;
1796 *line_lenP = FD_LINE_LEN;
1798 #define IS_COMMENT(comment) \
1799 (DSCcomment(line) && iscomment(line+2,(comment)))
1800 #define IS_BEGIN(comment) \
1801 (iscomment(line+7,(comment)))
1802 #define SKIP_WHILE(cond) \
1803 while (readline(fd, &line, NULL, &nbytes) && (cond)) *line_lenP += nbytes;\
1805 #define SKIP_UNTIL_1(comment) { \
1806 SKIP_WHILE((!IS_COMMENT(comment))) \
1808 #define SKIP_UNTIL_2(comment1,comment2) { \
1809 SKIP_WHILE((!IS_COMMENT(comment1) && !IS_COMMENT(comment2)))\
1812 if (!IS_COMMENT("Begin")) {} /* Do nothing */
1813 else if IS_BEGIN("Document:") SKIP_UNTIL_1("EndDocument")
1814 else if IS_BEGIN("Feature:") SKIP_UNTIL_1("EndFeature")
1815 #ifdef USE_ACROREAD_WORKAROUND
1816 else if IS_BEGIN("File") SKIP_UNTIL_2("EndFile","EOF")
1818 else if IS_BEGIN("File") SKIP_UNTIL_1("EndFile")
1820 else if IS_BEGIN("Font") SKIP_UNTIL_1("EndFont")
1821 else if IS_BEGIN("ProcSet") SKIP_UNTIL_1("EndProcSet")
1822 else if IS_BEGIN("Resource") SKIP_UNTIL_1("EndResource")
1823 else if IS_BEGIN("Data:") {
1826 if (FD_LINE_LEN > 100) FD_BUF[100] = '\0';
1828 if (sscanf(line+length("%%BeginData:"), "%d %*s %s", &num, text) >= 1) {
1829 if (strcmp(text, "Lines") == 0) {
1831 line = ps_io_fgetchars(fd,-1);
1832 if (line) *line_lenP += FD_LINE_LEN;
1836 int read_chunk_size = LINE_CHUNK_SIZE;
1838 if (num <= LINE_CHUNK_SIZE) read_chunk_size=num;
1839 line = ps_io_fgetchars(fd,read_chunk_size);
1840 if (line) *line_lenP += FD_LINE_LEN;
1841 num -= read_chunk_size;
1845 SKIP_UNTIL_1("EndData")
1847 else if IS_BEGIN("Binary:") {
1849 if (sscanf(line+length("%%BeginBinary:"), "%d", &num) == 1) {
1850 int read_chunk_size = LINE_CHUNK_SIZE;
1852 if (num <= LINE_CHUNK_SIZE) read_chunk_size=num;
1853 line = ps_io_fgetchars(fd,read_chunk_size);
1854 if (line) *line_lenP += FD_LINE_LEN;
1855 num -= read_chunk_size;
1857 SKIP_UNTIL_1("EndBinary")
1862 *line_lenP += nbytes;
1863 *lineP = skipped_line;
1865 *lineP = FD_BUF+FD_LINE_BEGIN;
1868 return(FD_BUF+FD_LINE_BEGIN);