home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Tools / ghostview-1.5 / ps.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-03  |  43.7 KB  |  1,432 lines

  1. /*
  2.  * ps.c -- Postscript scanning and copying routines.
  3.  * Copyright (C) 1992  Timothy O. Theisen
  4.  *
  5.  * This program is free software; you can redistribute it and/or modify
  6.  * it under the terms of the GNU General Public License as published by
  7.  * the Free Software Foundation; either version 2 of the License, or
  8.  * (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program; if not, write to the Free Software
  17.  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  *
  19.  *   Author: Tim Theisen           Systems Programmer
  20.  * Internet: tim@cs.wisc.edu       Department of Computer Sciences
  21.  *     UUCP: uwvax!tim             University of Wisconsin-Madison
  22.  *    Phone: (608)262-0438         1210 West Dayton Street
  23.  *      FAX: (608)262-9777         Madison, WI   53706
  24.  */
  25.  
  26. #include <stdio.h>
  27. #ifndef SEEK_SET
  28. #define SEEK_SET 0
  29. #endif
  30. #ifndef BUFSIZ
  31. #define BUFSIZ 1024
  32. #endif
  33. #include <ctype.h>
  34. #include <X11/Xos.h>        /* #includes the appropriate <string.h> */
  35. #include "ps.h"
  36.  
  37. #ifdef BSD4_2
  38. #define memset(a,b,c) bzero(a,c)
  39. #endif
  40.  
  41. /* length calculates string length at compile time */
  42. /* can only be used with character constants */
  43. #define length(a) (sizeof(a)-1)
  44. #define iscomment(a, b)    (strncmp(a, b, length(b)) == 0)
  45. #define DSCcomment(a) (a[0] == '%' && a[1] == '%')
  46.  
  47.     /* list of standard paper sizes from Adobe's PPD. */
  48.  
  49. struct documentmedia papersizes[] = {
  50.     "Letter",         612,  792,
  51.     "LetterSmall",     612,  792,
  52.     "Tabloid",         792, 1224,
  53.     "Ledger",        1224,  792,
  54.     "Legal",         612, 1008,
  55.     "Statement",     396,  612,
  56.     "Executive",     540,  720,
  57.     "A3",         842, 1190,
  58.     "A4",         595,  842,
  59.     "A4Small",         595,  842,
  60.     "A5",         420,  595,
  61.     "B4",         729, 1032,
  62.     "B5",         516,  729,
  63.     "Folio",         612,  936,
  64.     "Quarto",         610,  780,
  65.     "10x14",         720, 1008,
  66.     NULL,           0,    0
  67. };
  68.  
  69.  
  70. static char *readline();
  71. static char *gettextline();
  72. static char *gettext();
  73. static int  blank();
  74.  
  75. /*
  76.  *    psscan -- scan the PostScript file for document structuring comments.
  77.  *
  78.  *    This scanner is designed to retrieve the information necessary for
  79.  *    the ghostview previewer.  It will scan files that conform to any
  80.  *    version (1.0, 2.0, 2.1, or 3.0) of the document structuring conventions.
  81.  *    It does not really care which version of comments the file contains.
  82.  *    (The comments are largely upward compatible.)  It will scan a number
  83.  *    of non-conforming documents.  (You could have part of the document
  84.  *    conform to V2.0 and the rest conform to V3.0.  It would be similar
  85.  *    to the DC-2 1/2+, it would look funny but it can still fly.)
  86.  *
  87.  *    This routine returns a pointer to the document structure.
  88.  *    The structure contains the information relevant to previewing.
  89.  *      These include EPSF flag (to tell if the file is a encapsulated figure),
  90.  *      Page Media (for the Page Size), Bounding Box (to minimize backing
  91.  *      pixmap size or determine window size for encapsulated PostScript), 
  92.  *      Orientation of Paper (for default transformation matrix), and
  93.  *      Page Order.  The title and CreationDate are also retrieved to
  94.  *      help identify the document.
  95.  *
  96.  *      The following comments are examined:
  97.  *
  98.  *      Header section: 
  99.  *      Must start with %!PS-Adobe-.  Version numbers ignored.
  100.  *
  101.  *      %!PS-Adobe-* [EPSF-*]
  102.  *      %%BoundingBox: <int> <int> <int> <int>|(atend)
  103.  *      %%CreationDate: <textline>
  104.  *      %%Orientation: Portrait|Landscape|(atend)
  105.  *      %%Pages: <uint> [<int>]|(atend)
  106.  *      %%PageOrder: Ascend|Descend|Special|(atend)
  107.  *      %%Title: <textline>
  108.  *      %%DocumentMedia: <text> <real> <real> <real> <text> <text>
  109.  *      %%DocumentPaperSizes: <text>
  110.  *      %%EndComments
  111.  *
  112.  *      Note: Either the 3.0 or 2.0 syntax for %%Pages is accepted.
  113.  *            Also either the 2.0 %%DocumentPaperSizes or the 3.0
  114.  *            %%DocumentMedia comments are accepted as well.
  115.  *
  116.  *      The header section ends either explicitly with %%EndComments or
  117.  *      implicitly with any line that does not begin with %X where X is
  118.  *      a not whitespace character.
  119.  *
  120.  *      If the file is encapsulated PostScript the optional Preview section
  121.  *      is next:
  122.  *
  123.  *      %%BeginPreview
  124.  *      %%EndPreview
  125.  *
  126.  *      This section explicitly begins and ends with the above comments.
  127.  *
  128.  *      Next the Defaults section for version 3 page defaults:
  129.  *
  130.  *      %%BeginDefaults
  131.  *      %%PageBoundingBox: <int> <int> <int> <int>
  132.  *      %%PageOrientation: Portrait|Landscape
  133.  *      %%PageMedia: <text>
  134.  *      %%EndDefaults
  135.  *
  136.  *      This section explicitly begins and ends with the above comments.
  137.  *
  138.  *      The prolog section either explicitly starts with %%BeginProlog or
  139.  *      implicitly with any nonblank line.
  140.  *
  141.  *      %%BeginProlog
  142.  *      %%EndProlog
  143.  *
  144.  *      The Prolog should end with %%EndProlog, however the proglog implicitly
  145.  *      ends when %%BeginSetup, %%Page, %%Trailer or %%EOF are encountered.
  146.  *
  147.  *      The Setup section is where the version 2 page defaults are found.
  148.  *      This section either explicitly begins with %%BeginSetup or implicitly
  149.  *      with any nonblank line after the Prolog.
  150.  *
  151.  *      %%BeginSetup
  152.  *      %%PageBoundingBox: <int> <int> <int> <int>
  153.  *      %%PageOrientation: Portrait|Landscape
  154.  *      %%PaperSize: <text>
  155.  *      %%EndSetup
  156.  *
  157.  *      The Setup should end with %%EndSetup, however the setup implicitly
  158.  *      ends when %%Page, %%Trailer or %%EOF are encountered.
  159.  *
  160.  *      Next each page starts explicitly with %%Page and ends implicitly with
  161.  *      %%Page or %%Trailer or %%EOF.  The following comments are recognized:
  162.  *
  163.  *      %%Page: <text> <uint>
  164.  *      %%PageBoundingBox: <int> <int> <int> <int>|(atend)
  165.  *      %%PageOrientation: Portrait|Landscape
  166.  *      %%PageMedia: <text>
  167.  *      %%PaperSize: <text>
  168.  *
  169.  *      The tralier section start explicitly with %%Trailer and end with %%EOF.
  170.  *      The following comment are examined with the proper (atend) notation
  171.  *      was used in the header:
  172.  *
  173.  *      %%Trailer
  174.  *      %%BoundingBox: <int> <int> <int> <int>|(atend)
  175.  *      %%Orientation: Portrait|Landscape|(atend)
  176.  *      %%Pages: <uint> [<int>]|(atend)
  177.  *      %%PageOrder: Ascend|Descend|Special|(atend)
  178.  *      %%EOF
  179.  *
  180.  *
  181.  *  + A DC-3 received severe damage to one of its wings.  The wing was a total
  182.  *    loss.  There was no replacement readily available, so the mechanic
  183.  *    installed a wing from a DC-2.
  184.  */
  185.  
  186. struct document *
  187. psscan(file)
  188.     FILE *file;
  189. {
  190.     struct document *doc;
  191.     int bb_set = NONE;
  192.     int pages_set = NONE;
  193.     int page_order_set = NONE;
  194.     int orientation_set = NONE;
  195.     int page_bb_set = NONE;
  196.     int page_media_set = NONE;
  197.     int preread;        /* flag which tells the readline isn't needed */
  198.     int i;
  199.     unsigned int maxpages = 0;
  200.     unsigned int nextpage = 1;    /* Next expected page */
  201.     unsigned int thispage;
  202.     int ignore = 0;        /* whether to ignore page ordinals */
  203.     char *label;
  204.     char line[PSLINELENGTH];    /* 255 characters + 1 newline + 1 NULL */
  205.     char text[PSLINELENGTH];    /* Temporary storage for text */
  206.     long position;        /* Position of the current line */
  207.     long beginsection;        /* Position of the beginning of the section */
  208.     unsigned int line_len;     /* Length of the current line */
  209.     unsigned int section_len;    /* Place to accumulate the section length */
  210.     char *next_char;        /* 1st char after text returned by gettext() */
  211.     char *cp;
  212.     struct documentmedia *dmp;
  213.  
  214.     rewind(file);
  215.     if (readline(line, sizeof line, file, &position, &line_len) == NULL) {
  216.     fprintf(stderr, "Warning: empty file.\n");
  217.     return(NULL);
  218.     }
  219.  
  220.     /* Header comments */
  221.  
  222.     if (iscomment(line,"%!PS-Adobe-")) {
  223.     doc = (struct document *) malloc(sizeof(struct document));
  224.     if (doc == NULL) {
  225.         fprintf(stderr, "Fatal Error: Dynamic memory exhausted.\n");
  226.         exit(-1);
  227.     }
  228.     memset(doc, 0, sizeof(struct document));
  229.     sscanf(line, "%*s %s", text);
  230.     doc->epsf = iscomment(text, "EPSF-");
  231.     doc->beginheader = position;
  232.     section_len = line_len;
  233.     } else {
  234.     return(NULL);
  235.     }
  236.  
  237.     preread = 0;
  238.     while (preread || readline(line, sizeof line, file, &position, &line_len)) {
  239.     if (!preread) section_len += line_len;
  240.     preread = 0;
  241.     if (line[0] != '%' ||
  242.         iscomment(line+1, "%EndComments") ||
  243.         line[1] == ' ' || line[1] == '\t' || line[1] == '\n' ||
  244.         !isprint(line[1])) {
  245.         break;
  246.     } else if (line[1] != '%') {
  247.         /* Do nothing */
  248.     } else if (doc->title == NULL && iscomment(line+2, "Title:")) {
  249.         doc->title = gettextline(line+length("%%Title:"));
  250.     } else if (doc->date == NULL && iscomment(line+2, "CreationDate:")) {
  251.         doc->date = gettextline(line+length("%%CreationDate:"));
  252.     } else if (bb_set == NONE && iscomment(line+2, "BoundingBox:")) {
  253.         sscanf(line+length("%%BoundingBox:"), "%s", text);
  254.         if (strcmp(text, "(atend)") == 0) {
  255.         bb_set = ATEND;
  256.         } else {
  257.         if (sscanf(line+length("%%BoundingBox:"), "%d %d %d %d",
  258.                &(doc->boundingbox[LLX]),
  259.                &(doc->boundingbox[LLY]),
  260.                &(doc->boundingbox[URX]),
  261.                &(doc->boundingbox[URY])) == 4)
  262.             bb_set = 1;
  263.         else {
  264.             float fllx, flly, furx, fury;
  265.             if (sscanf(line+length("%%BoundingBox:"), "%f %f %f %f",
  266.                    &fllx, &flly, &furx, &fury) == 4) {
  267.             bb_set = 1;
  268.             doc->boundingbox[LLX] = fllx;
  269.             doc->boundingbox[LLY] = flly;
  270.             doc->boundingbox[URX] = furx;
  271.             doc->boundingbox[URY] = fury;
  272.             if (fllx < doc->boundingbox[LLX])
  273.                 doc->boundingbox[LLX]--;
  274.             if (flly < doc->boundingbox[LLY])
  275.                 doc->boundingbox[LLY]--;
  276.             if (furx > doc->boundingbox[URX])
  277.                 doc->boundingbox[URX]++;
  278.             if (fury > doc->boundingbox[URY])
  279.                 doc->boundingbox[URY]++;
  280.             }
  281.         }
  282.         }
  283.     } else if (orientation_set == NONE &&
  284.            iscomment(line+2, "Orientation:")) {
  285.         sscanf(line+length("%%Orientation:"), "%s", text);
  286.         if (strcmp(text, "(atend)") == 0) {
  287.         orientation_set = ATEND;
  288.         } else if (strcmp(text, "Portrait") == 0) {
  289.         doc->orientation = PORTRAIT;
  290.         orientation_set = 1;
  291.         } else if (strcmp(text, "Landscape") == 0) {
  292.         doc->orientation = LANDSCAPE;
  293.         orientation_set = 1;
  294.         }
  295.     } else if (page_order_set == NONE && iscomment(line+2, "PageOrder:")) {
  296.         sscanf(line+length("%%PageOrder:"), "%s", text);
  297.         if (strcmp(text, "(atend)") == 0) {
  298.         page_order_set = ATEND;
  299.         } else if (strcmp(text, "Ascend") == 0) {
  300.         doc->pageorder = ASCEND;
  301.         page_order_set = 1;
  302.         } else if (strcmp(text, "Descend") == 0) {
  303.         doc->pageorder = DESCEND;
  304.         page_order_set = 1;
  305.         } else if (strcmp(text, "Special") == 0) {
  306.         doc->pageorder = SPECIAL;
  307.         page_order_set = 1;
  308.         }
  309.     } else if (pages_set == NONE && iscomment(line+2, "Pages:")) {
  310.         sscanf(line+length("%%Pages:"), "%s", text);
  311.         if (strcmp(text, "(atend)") == 0) {
  312.         pages_set = ATEND;
  313.         } else {
  314.         switch (sscanf(line+length("%%Pages:"), "%d %d",
  315.                    &maxpages, &i)) {
  316.             case 2:
  317.             if (page_order_set == NONE) {
  318.                 if (i == -1) {
  319.                 doc->pageorder = DESCEND;
  320.                 page_order_set = 1;
  321.                 } else if (i == 0) {
  322.                 doc->pageorder = SPECIAL;
  323.                 page_order_set = 1;
  324.                 } else if (i == 1) {
  325.                 doc->pageorder = ASCEND;
  326.                 page_order_set = 1;
  327.                 }
  328.             }
  329.             case 1:
  330.             if (maxpages > 0) {
  331.                 doc->pages = (struct page *) calloc(maxpages,
  332.                                sizeof(struct page));
  333.                 if (doc->pages == NULL) {
  334.                 fprintf(stderr,
  335.                     "Fatal Error: Dynamic memory exhausted.\n");
  336.                 exit(-1);
  337.                 }
  338.             }
  339.         }
  340.         }
  341.     } else if (doc->nummedia == NONE &&
  342.            iscomment(line+2, "DocumentMedia:")) {
  343.         float w, h;
  344.         doc->media = (struct documentmedia *)
  345.              malloc(sizeof (struct documentmedia));
  346.         if (doc->media == NULL) {
  347.         fprintf(stderr, "Fatal Error: Dynamic memory exhausted.\n");
  348.         exit(-1);
  349.         }
  350.         doc->media[0].name = gettext(line+length("%%DocumentMedia:"),
  351.                      &next_char);
  352.         if (doc->media[0].name != NULL) {
  353.         if (sscanf(next_char, "%f %f", &w, &h) == 2) {
  354.             doc->media[0].width = w + 0.5;
  355.             doc->media[0].height = h + 0.5;
  356.         }
  357.         if (doc->media[0].width != 0 && doc->media[0].height != 0)
  358.             doc->nummedia = 1;
  359.         else
  360.             free(doc->media[0].name);
  361.         }
  362.         preread=1;
  363.         while (readline(line, sizeof line, file, &position, &line_len) &&
  364.            DSCcomment(line) && iscomment(line+2, "+")) {
  365.         section_len += line_len;
  366.         doc->media = (struct documentmedia *)
  367.                  realloc(doc->media,
  368.                      (doc->nummedia+1)*
  369.                      sizeof (struct documentmedia));
  370.         if (doc->media == NULL) {
  371.             fprintf(stderr, "Fatal Error: Dynamic memory exhausted.\n");
  372.             exit(-1);
  373.         }
  374.         doc->media[doc->nummedia].name = gettext(line+length("%%+"),
  375.                              &next_char);
  376.         if (doc->media[doc->nummedia].name != NULL) {
  377.             if (sscanf(next_char, "%f %f", &w, &h) == 2) {
  378.             doc->media[doc->nummedia].width = w + 0.5;
  379.             doc->media[doc->nummedia].height = h + 0.5;
  380.             }
  381.             if (doc->media[doc->nummedia].width != 0 &&
  382.             doc->media[doc->nummedia].height != 0) doc->nummedia++;
  383.             else
  384.             free(doc->media[doc->nummedia].name);
  385.         }
  386.         }
  387.         section_len += line_len;
  388.         if (doc->nummedia != 0) doc->default_page_media = doc->media;
  389.     } else if (doc->nummedia == NONE &&
  390.            iscomment(line+2, "DocumentPaperSizes:")) {
  391.  
  392.         doc->media = (struct documentmedia *)
  393.              malloc(sizeof (struct documentmedia));
  394.         if (doc->media == NULL) {
  395.         fprintf(stderr, "Fatal Error: Dynamic memory exhausted.\n");
  396.         exit(-1);
  397.         }
  398.         doc->media[0].name = gettext(line+length("%%DocumentPaperSizes:"),
  399.                      &next_char);
  400.         if (doc->media[0].name != NULL) {
  401.         doc->media[0].width = 0;
  402.         doc->media[0].height = 0;
  403.         for (dmp=papersizes; dmp->name != NULL; dmp++) {
  404.             /* Note: Paper size comment uses down cased paper size
  405.              * name.  Case insensitive compares are only used for
  406.              * PaperSize comments.
  407.              */
  408.             if (strcasecmp(doc->media[0].name, dmp->name) == 0) {
  409.             free(doc->media[0].name);
  410.             doc->media[0].name =
  411.                 (char *)malloc(strlen(dmp->name)+1);
  412.             if (doc->media[0].name == NULL) {
  413.                 fprintf(stderr,
  414.                     "Fatal Error: Dynamic memory exhausted.\n");
  415.                 exit(-1);
  416.             }
  417.             strcpy(doc->media[0].name, dmp->name);
  418.             doc->media[0].width = dmp->width;
  419.             doc->media[0].height = dmp->height;
  420.             break;
  421.             }
  422.         }
  423.         if (doc->media[0].width != 0 && doc->media[0].height != 0)
  424.             doc->nummedia = 1;
  425.         else
  426.             free(doc->media[0].name);
  427.         }
  428.         while (cp = gettext(next_char, &next_char)) {
  429.         doc->media = (struct documentmedia *)
  430.                  realloc(doc->media,
  431.                      (doc->nummedia+1)*
  432.                      sizeof (struct documentmedia));
  433.         if (doc->media == NULL) {
  434.             fprintf(stderr, "Fatal Error: Dynamic memory exhausted.\n");
  435.             exit(-1);
  436.         }
  437.         doc->media[doc->nummedia].name = cp;
  438.         doc->media[doc->nummedia].width = 0;
  439.         doc->media[doc->nummedia].height = 0;
  440.         for (dmp=papersizes; dmp->name != NULL; dmp++) {
  441.             /* Note: Paper size comment uses down cased paper size
  442.              * name.  Case insensitive compares are only used for
  443.              * PaperSize comments.
  444.              */
  445.             if (strcasecmp(doc->media[doc->nummedia].name,
  446.                    dmp->name) == 0) {
  447.             free(doc->media[doc->nummedia].name);
  448.             doc->media[doc->nummedia].name =
  449.                 (char *)malloc(strlen(dmp->name)+1);
  450.             if (doc->media[doc->nummedia].name == NULL) {
  451.                 fprintf(stderr,
  452.                     "Fatal Error: Dynamic memory exhausted.\n");
  453.                 exit(-1);
  454.             }
  455.             strcpy(doc->media[doc->nummedia].name, dmp->name);
  456.             doc->media[doc->nummedia].name = dmp->name;
  457.             doc->media[doc->nummedia].width = dmp->width;
  458.             doc->media[doc->nummedia].height = dmp->height;
  459.             break;
  460.             }
  461.         }
  462.         if (doc->media[doc->nummedia].width != 0 &&
  463.             doc->media[doc->nummedia].height != 0) doc->nummedia++;
  464.         else
  465.             free(doc->media[doc->nummedia].name);
  466.         }
  467.         preread=1;
  468.         while (readline(line, sizeof line, file, &position, &line_len) &&
  469.            DSCcomment(line) && iscomment(line+2, "+")) {
  470.         section_len += line_len;
  471.         next_char = line + length("%%+");
  472.         while (cp = gettext(next_char, &next_char)) {
  473.             doc->media = (struct documentmedia *)
  474.                  realloc(doc->media,
  475.                      (doc->nummedia+1)*
  476.                      sizeof (struct documentmedia));
  477.             if (doc->media == NULL) {
  478.             fprintf(stderr,
  479.                 "Fatal Error: Dynamic memory exhausted.\n");
  480.             exit(-1);
  481.             }
  482.             doc->media[doc->nummedia].name = cp;
  483.             doc->media[doc->nummedia].width = 0;
  484.             doc->media[doc->nummedia].height = 0;
  485.             for (dmp=papersizes; dmp->name != NULL; dmp++) {
  486.             /* Note: Paper size comment uses down cased paper size
  487.              * name.  Case insensitive compares are only used for
  488.              * PaperSize comments.
  489.              */
  490.             if (strcasecmp(doc->media[doc->nummedia].name,
  491.                    dmp->name) == 0) {
  492.                 doc->media[doc->nummedia].width = dmp->width;
  493.                 doc->media[doc->nummedia].height = dmp->height;
  494.                 break;
  495.             }
  496.             }
  497.             if (doc->media[doc->nummedia].width != 0 &&
  498.             doc->media[doc->nummedia].height != 0) doc->nummedia++;
  499.             else
  500.             free(doc->media[doc->nummedia].name);
  501.         }
  502.         }
  503.         section_len += line_len;
  504.         if (doc->nummedia != 0) doc->default_page_media = doc->media;
  505.     }
  506.     }
  507.  
  508.     if (DSCcomment(line) && iscomment(line+2, "EndComments")) {
  509.     readline(line, sizeof line, file, &position, &line_len);
  510.     section_len += line_len;
  511.     }
  512.     doc->endheader = position;
  513.     doc->lenheader = section_len - line_len;
  514.  
  515.     /* Optional Preview comments for encapsulated PostScript files */ 
  516.  
  517.     beginsection = position;
  518.     section_len = line_len;
  519.     while (blank(line) &&
  520.        readline(line, sizeof line, file, &position, &line_len)) {
  521.     section_len += line_len;
  522.     }
  523.  
  524.     if (doc->epsf && DSCcomment(line) && iscomment(line+2, "BeginPreview")) {
  525.     doc->beginpreview = beginsection;
  526.     beginsection = 0;
  527.     while (readline(line, sizeof line, file, &position, &line_len) &&
  528.            !(DSCcomment(line) && iscomment(line+2, "EndPreview"))) {
  529.         section_len += line_len;
  530.     }
  531.     section_len += line_len;
  532.     readline(line, sizeof line, file, &position, &line_len);
  533.     section_len += line_len;
  534.     doc->endpreview = position;
  535.     doc->lenpreview = section_len - line_len;
  536.     }
  537.  
  538.     /* Page Defaults for Version 3.0 files */
  539.  
  540.     if (beginsection == 0) {
  541.     beginsection = position;
  542.     section_len = line_len;
  543.     }
  544.     while (blank(line) &&
  545.        readline(line, sizeof line, file, &position, &line_len)) {
  546.     section_len += line_len;
  547.     }
  548.  
  549.     if (DSCcomment(line) && iscomment(line+2, "BeginDefaults")) {
  550.     doc->begindefaults = beginsection;
  551.     beginsection = 0;
  552.     while (readline(line, sizeof line, file, &position, &line_len) &&
  553.            !(DSCcomment(line) && iscomment(line+2, "EndDefaults"))) {
  554.         section_len += line_len;
  555.         if (!DSCcomment(line)) {
  556.         /* Do nothing */
  557.         } else if (doc->default_page_orientation == NONE &&
  558.         iscomment(line+2, "PageOrientation:")) {
  559.         sscanf(line+length("%%PageOrientation:"), "%s", text);
  560.         if (strcmp(text, "Portrait") == 0) {
  561.             doc->default_page_orientation = PORTRAIT;
  562.         } else if (strcmp(text, "Landscape") == 0) {
  563.             doc->default_page_orientation = LANDSCAPE;
  564.         }
  565.         } else if (page_media_set == NONE &&
  566.                iscomment(line+2, "PageMedia:")) {
  567.         cp = gettext(line+length("%%PageMedia:"), NULL);
  568.         for (dmp = doc->media, i=0; i<doc->nummedia; i++, dmp++) {
  569.             if (strcmp(cp, dmp->name) == 0) {
  570.             doc->default_page_media = dmp;
  571.             page_media_set = 1;
  572.             break;
  573.             }
  574.         }
  575.         free(cp);
  576.         } else if (page_bb_set == NONE &&
  577.                iscomment(line+2, "PageBoundingBox:")) {
  578.         if (sscanf(line+length("%%PageBoundingBox:"), "%d %d %d %d",
  579.                &(doc->default_page_boundingbox[LLX]),
  580.                &(doc->default_page_boundingbox[LLY]),
  581.                &(doc->default_page_boundingbox[URX]),
  582.                &(doc->default_page_boundingbox[URY])) == 4)
  583.             page_bb_set = 1;
  584.         else {
  585.             float fllx, flly, furx, fury;
  586.             if (sscanf(line+length("%%PageBoundingBox:"), "%f %f %f %f",
  587.                    &fllx, &flly, &furx, &fury) == 4) {
  588.             page_bb_set = 1;
  589.             doc->default_page_boundingbox[LLX] = fllx;
  590.             doc->default_page_boundingbox[LLY] = flly;
  591.             doc->default_page_boundingbox[URX] = furx;
  592.             doc->default_page_boundingbox[URY] = fury;
  593.             if (fllx < doc->default_page_boundingbox[LLX])
  594.                 doc->default_page_boundingbox[LLX]--;
  595.             if (flly < doc->default_page_boundingbox[LLY])
  596.                 doc->default_page_boundingbox[LLY]--;
  597.             if (furx > doc->default_page_boundingbox[URX])
  598.                 doc->default_page_boundingbox[URX]++;
  599.             if (fury > doc->default_page_boundingbox[URY])
  600.                 doc->default_page_boundingbox[URY]++;
  601.             }
  602.         }
  603.         }
  604.     }
  605.     section_len += line_len;
  606.     readline(line, sizeof line, file, &position, &line_len);
  607.     section_len += line_len;
  608.     doc->enddefaults = position;
  609.     doc->lendefaults = section_len - line_len;
  610.     }
  611.  
  612.     /* Document Prolog */
  613.  
  614.     if (beginsection == 0) {
  615.     beginsection = position;
  616.     section_len = line_len;
  617.     }
  618.     while (blank(line) &&
  619.        readline(line, sizeof line, file, &position, &line_len)) {
  620.     section_len += line_len;
  621.     }
  622.  
  623.     if (!(DSCcomment(line) &&
  624.       (iscomment(line+2, "BeginSetup") ||
  625.        iscomment(line+2, "Page:") ||
  626.        iscomment(line+2, "Trailer") ||
  627.        iscomment(line+2, "EOF")))) {
  628.     doc->beginprolog = beginsection;
  629.     beginsection = 0;
  630.     preread = 1;
  631.  
  632.     while ((preread ||
  633.         readline(line, sizeof line, file, &position, &line_len)) &&
  634.            !(DSCcomment(line) &&
  635.              (iscomment(line+2, "EndProlog") ||
  636.               iscomment(line+2, "BeginSetup") ||
  637.               iscomment(line+2, "Page:") ||
  638.               iscomment(line+2, "Trailer") ||
  639.               iscomment(line+2, "EOF")))) {
  640.         if (!preread) section_len += line_len;
  641.         preread = 0;
  642.     }
  643.     section_len += line_len;
  644.     if (DSCcomment(line) && iscomment(line+2, "EndProlog")) {
  645.         readline(line, sizeof line, file, &position, &line_len);
  646.         section_len += line_len;
  647.     }
  648.     doc->endprolog = position;
  649.     doc->lenprolog = section_len - line_len;
  650.     }
  651.  
  652.     /* Document Setup,  Page Defaults found here for Version 2 files */
  653.  
  654.     if (beginsection == 0) {
  655.     beginsection = position;
  656.     section_len = line_len;
  657.     }
  658.     while (blank(line) &&
  659.        readline(line, sizeof line, file, &position, &line_len)) {
  660.     section_len += line_len;
  661.     }
  662.  
  663.     if (!(DSCcomment(line) &&
  664.       (iscomment(line+2, "Page:") ||
  665.        iscomment(line+2, "Trailer") ||
  666.        iscomment(line+2, "EOF")))) {
  667.     doc->beginsetup = beginsection;
  668.     beginsection = 0;
  669.     preread = 1;
  670.     while ((preread ||
  671.         readline(line, sizeof line, file, &position, &line_len)) &&
  672.            !(DSCcomment(line) &&
  673.              (iscomment(line+2, "EndSetup") ||
  674.               iscomment(line+2, "Page:") ||
  675.               iscomment(line+2, "Trailer") ||
  676.               iscomment(line+2, "EOF")))) {
  677.         if (!preread) section_len += line_len;
  678.         preread = 0;
  679.         if (!DSCcomment(line)) {
  680.         /* Do nothing */
  681.         } else if (doc->default_page_orientation == NONE &&
  682.         iscomment(line+2, "PageOrientation:")) {
  683.         sscanf(line+length("%%PageOrientation:"), "%s", text);
  684.         if (strcmp(text, "Portrait") == 0) {
  685.             doc->default_page_orientation = PORTRAIT;
  686.         } else if (strcmp(text, "Landscape") == 0) {
  687.             doc->default_page_orientation = LANDSCAPE;
  688.         }
  689.         } else if (page_media_set == NONE &&
  690.                iscomment(line+2, "PaperSize:")) {
  691.         cp = gettext(line+length("%%PaperSize:"), NULL);
  692.         for (dmp = doc->media, i=0; i<doc->nummedia; i++, dmp++) {
  693.             /* Note: Paper size comment uses down cased paper size
  694.              * name.  Case insensitive compares are only used for
  695.              * PaperSize comments.
  696.              */
  697.             if (strcasecmp(cp, dmp->name) == 0) {
  698.             doc->default_page_media = dmp;
  699.             page_media_set = 1;
  700.             break;
  701.             }
  702.         }
  703.         free(cp);
  704.         } else if (page_bb_set == NONE &&
  705.                iscomment(line+2, "PageBoundingBox:")) {
  706.         if (sscanf(line+length("%%PageBoundingBox:"), "%d %d %d %d",
  707.                &(doc->default_page_boundingbox[LLX]),
  708.                &(doc->default_page_boundingbox[LLY]),
  709.                &(doc->default_page_boundingbox[URX]),
  710.                &(doc->default_page_boundingbox[URY])) == 4)
  711.             page_bb_set = 1;
  712.         else {
  713.             float fllx, flly, furx, fury;
  714.             if (sscanf(line+length("%%PageBoundingBox:"), "%f %f %f %f",
  715.                    &fllx, &flly, &furx, &fury) == 4) {
  716.             page_bb_set = 1;
  717.             doc->default_page_boundingbox[LLX] = fllx;
  718.             doc->default_page_boundingbox[LLY] = flly;
  719.             doc->default_page_boundingbox[URX] = furx;
  720.             doc->default_page_boundingbox[URY] = fury;
  721.             if (fllx < doc->default_page_boundingbox[LLX])
  722.                 doc->default_page_boundingbox[LLX]--;
  723.             if (flly < doc->default_page_boundingbox[LLY])
  724.                 doc->default_page_boundingbox[LLY]--;
  725.             if (furx > doc->default_page_boundingbox[URX])
  726.                 doc->default_page_boundingbox[URX]++;
  727.             if (fury > doc->default_page_boundingbox[URY])
  728.                 doc->default_page_boundingbox[URY]++;
  729.             }
  730.         }
  731.         }
  732.     }
  733.     section_len += line_len;
  734.     if (DSCcomment(line) && iscomment(line+2, "EndSetup")) {
  735.         readline(line, sizeof line, file, &position, &line_len);
  736.         section_len += line_len;
  737.     }
  738.     doc->endsetup = position;
  739.     doc->lensetup = section_len - line_len;
  740.     }
  741.  
  742.     /* Individual Pages */
  743.  
  744.     if (beginsection == 0) {
  745.     beginsection = position;
  746.     section_len = line_len;
  747.     }
  748.     while (blank(line) &&
  749.        readline(line, sizeof line, file, &position, &line_len)) {
  750.     section_len += line_len;
  751.     }
  752.  
  753. newpage:
  754.     while (DSCcomment(line) && iscomment(line+2, "Page:")) {
  755.     if (maxpages == 0) {
  756.         maxpages = 1;
  757.         doc->pages = (struct page *) calloc(maxpages, sizeof(struct page));
  758.         if (doc->pages == NULL) {
  759.         fprintf(stderr,
  760.             "Fatal Error: Dynamic memory exhausted.\n");
  761.         exit(-1);
  762.         }
  763.     }
  764.     label = gettext(line+length("%%Page:"), &next_char);
  765.     if (sscanf(next_char, "%d", &thispage) != 1) thispage = 0;
  766.     if (nextpage == 1) {
  767.         ignore = thispage != 1;
  768.     }
  769.     if (!ignore && thispage != nextpage) {
  770.         free(label);
  771.         doc->numpages--;
  772.         goto continuepage;
  773.     }
  774.     nextpage++;
  775.     if (doc->numpages == maxpages) {
  776.         maxpages++;
  777.         doc->pages = (struct page *)
  778.              realloc(doc->pages, maxpages*sizeof (struct page));
  779.         if (doc->pages == NULL) {
  780.         fprintf(stderr, "Fatal Error: Dynamic memory exhausted.\n");
  781.         exit(-1);
  782.         }
  783.     }
  784.     memset(&(doc->pages[doc->numpages]), 0, sizeof(struct page));
  785.     page_bb_set = NONE;
  786.     doc->pages[doc->numpages].label = label;
  787.     if (beginsection) {
  788.         doc->pages[doc->numpages].begin = beginsection;
  789.         beginsection = 0;
  790.     } else {
  791.         doc->pages[doc->numpages].begin = position;
  792.         section_len = line_len;
  793.     }
  794. continuepage:
  795.     while (readline(line, sizeof line, file, &position, &line_len) &&
  796.            !(DSCcomment(line) &&
  797.              (iscomment(line+2, "Page:") ||
  798.               iscomment(line+2, "Trailer") ||
  799.               iscomment(line+2, "EOF")))) {
  800.         section_len += line_len;
  801.         if (!DSCcomment(line)) {
  802.         /* Do nothing */
  803.         } else if (doc->pages[doc->numpages].orientation == NONE &&
  804.         iscomment(line+2, "PageOrientation:")) {
  805.         sscanf(line+length("%%PageOrientation:"), "%s", text);
  806.         if (strcmp(text, "Portrait") == 0) {
  807.             doc->pages[doc->numpages].orientation = PORTRAIT;
  808.         } else if (strcmp(text, "Landscape") == 0) {
  809.             doc->pages[doc->numpages].orientation = LANDSCAPE;
  810.         }
  811.         } else if (doc->pages[doc->numpages].media == NULL &&
  812.                iscomment(line+2, "PageMedia:")) {
  813.         cp = gettext(line+length("%%PageMedia:"), NULL);
  814.         for (dmp = doc->media, i=0; i<doc->nummedia; i++, dmp++) {
  815.             if (strcmp(cp, dmp->name) == 0) {
  816.             doc->pages[doc->numpages].media = dmp;
  817.             break;
  818.             }
  819.         }
  820.         free(cp);
  821.         } else if (doc->pages[doc->numpages].media == NULL &&
  822.                iscomment(line+2, "PaperSize:")) {
  823.         cp = gettext(line+length("%%PaperSize:"), NULL);
  824.         for (dmp = doc->media, i=0; i<doc->nummedia; i++, dmp++) {
  825.             /* Note: Paper size comment uses down cased paper size
  826.              * name.  Case insensitive compares are only used for
  827.              * PaperSize comments.
  828.              */
  829.             if (strcasecmp(cp, dmp->name) == 0) {
  830.             doc->pages[doc->numpages].media = dmp;
  831.             break;
  832.             }
  833.         }
  834.         free(cp);
  835.         } else if ((page_bb_set == NONE || page_bb_set == ATEND) &&
  836.                iscomment(line+2, "PageBoundingBox:")) {
  837.         sscanf(line+length("%%PageBoundingBox:"), "%s", text);
  838.         if (strcmp(text, "(atend)") == 0) {
  839.             page_bb_set = ATEND;
  840.         } else {
  841.             if (sscanf(line+length("%%PageBoundingBox:"), "%d %d %d %d",
  842.                 &(doc->pages[doc->numpages].boundingbox[LLX]),
  843.                 &(doc->pages[doc->numpages].boundingbox[LLY]),
  844.                 &(doc->pages[doc->numpages].boundingbox[URX]),
  845.                 &(doc->pages[doc->numpages].boundingbox[URY])) == 4)
  846.             if (page_bb_set == NONE) page_bb_set = 1;
  847.             else {
  848.             float fllx, flly, furx, fury;
  849.             if (sscanf(line+length("%%PageBoundingBox:"),
  850.                    "%f %f %f %f",
  851.                    &fllx, &flly, &furx, &fury) == 4) {
  852.                 if (page_bb_set == NONE) page_bb_set = 1;
  853.                 doc->pages[doc->numpages].boundingbox[LLX] = fllx;
  854.                 doc->pages[doc->numpages].boundingbox[LLY] = flly;
  855.                 doc->pages[doc->numpages].boundingbox[URX] = furx;
  856.                 doc->pages[doc->numpages].boundingbox[URY] = fury;
  857.                 if (fllx <
  858.                     doc->pages[doc->numpages].boundingbox[LLX])
  859.                 doc->pages[doc->numpages].boundingbox[LLX]--;
  860.                 if (flly <
  861.                     doc->pages[doc->numpages].boundingbox[LLY])
  862.                 doc->pages[doc->numpages].boundingbox[LLY]--;
  863.                 if (furx >
  864.                     doc->pages[doc->numpages].boundingbox[URX])
  865.                 doc->pages[doc->numpages].boundingbox[URX]++;
  866.                 if (fury >
  867.                     doc->pages[doc->numpages].boundingbox[URY])
  868.                 doc->pages[doc->numpages].boundingbox[URY]++;
  869.             }
  870.             }
  871.         }
  872.         }
  873.     }
  874.     section_len += line_len;
  875.     doc->pages[doc->numpages].end = position;
  876.     doc->pages[doc->numpages].len = section_len - line_len;
  877.     doc->numpages++;
  878.     }
  879.  
  880.     /* Document Trailer */
  881.  
  882.     if (beginsection) {
  883.     doc->begintrailer = beginsection;
  884.     beginsection = 0;
  885.     } else {
  886.     doc->begintrailer = position;
  887.     section_len = line_len;
  888.     }
  889.  
  890.     preread = 1;
  891.     while ((preread ||
  892.         readline(line, sizeof line, file, &position, &line_len)) &&
  893.        !(DSCcomment(line) && iscomment(line+2, "EOF"))) {
  894.     if (!preread) section_len += line_len;
  895.     preread = 0;
  896.     if (!DSCcomment(line)) {
  897.         /* Do nothing */
  898.     } else if (iscomment(line+2, "Page:")) {
  899.         free(gettext(line+length("%%Page:"), &next_char));
  900.         if (sscanf(next_char, "%d", &thispage) != 1) thispage = 0;
  901.         if (!ignore && thispage == nextpage) {
  902.         if (doc->numpages > 0) {
  903.             doc->pages[doc->numpages-1].end = position;
  904.             doc->pages[doc->numpages-1].len += section_len - line_len;
  905.         } else {
  906.             if (doc->endsetup) {
  907.             doc->endsetup = position;
  908.             doc->endsetup += section_len - line_len;
  909.             } else if (doc->endprolog) {
  910.             doc->endprolog = position;
  911.             doc->endprolog += section_len - line_len;
  912.             }
  913.         }
  914.         goto newpage;
  915.         }
  916.     } else if (bb_set == ATEND && iscomment(line+2, "BoundingBox:")) {
  917.         if (sscanf(line+length("%%BoundingBox:"), "%d %d %d %d",
  918.                &(doc->boundingbox[LLX]),
  919.                &(doc->boundingbox[LLY]),
  920.                &(doc->boundingbox[URX]),
  921.                &(doc->boundingbox[URY])) != 4) {
  922.         float fllx, flly, furx, fury;
  923.         if (sscanf(line+length("%%BoundingBox:"), "%f %f %f %f",
  924.                &fllx, &flly, &furx, &fury) == 4) {
  925.             doc->boundingbox[LLX] = fllx;
  926.             doc->boundingbox[LLY] = flly;
  927.             doc->boundingbox[URX] = furx;
  928.             doc->boundingbox[URY] = fury;
  929.             if (fllx < doc->boundingbox[LLX])
  930.             doc->boundingbox[LLX]--;
  931.             if (flly < doc->boundingbox[LLY])
  932.             doc->boundingbox[LLY]--;
  933.             if (furx > doc->boundingbox[URX])
  934.             doc->boundingbox[URX]++;
  935.             if (fury > doc->boundingbox[URY])
  936.             doc->boundingbox[URY]++;
  937.         }
  938.         }
  939.     } else if (orientation_set == ATEND &&
  940.            iscomment(line+2, "Orientation:")) {
  941.         sscanf(line+length("%%Orientation:"), "%s", text);
  942.         if (strcmp(text, "Portrait") == 0) {
  943.         doc->orientation = PORTRAIT;
  944.         } else if (strcmp(text, "Landscape") == 0) {
  945.         doc->orientation = LANDSCAPE;
  946.         }
  947.     } else if (page_order_set == ATEND && iscomment(line+2, "PageOrder:")) {
  948.         sscanf(line+length("%%PageOrder:"), "%s", text);
  949.         if (strcmp(text, "Ascend") == 0) {
  950.         doc->pageorder = ASCEND;
  951.         } else if (strcmp(text, "Descend") == 0) {
  952.         doc->pageorder = DESCEND;
  953.         } else if (strcmp(text, "Special") == 0) {
  954.         doc->pageorder = SPECIAL;
  955.         }
  956.     } else if (pages_set == ATEND && iscomment(line+2, "Pages:")) {
  957.         if (sscanf(line+length("%%Pages:"), "%*u %d", &i) == 1) {
  958.         if (page_order_set == NONE) {
  959.             if (i == -1) doc->pageorder = DESCEND;
  960.             else if (i == 0) doc->pageorder = SPECIAL;
  961.             else if (i == 1) doc->pageorder = ASCEND;
  962.         }
  963.         }
  964.     }
  965.     }
  966.     section_len += line_len;
  967.     if (DSCcomment(line) && iscomment(line+2, "EOF")) {
  968.     readline(line, sizeof line, file, &position, &line_len);
  969.     section_len += line_len;
  970.     }
  971.     doc->endtrailer = position;
  972.     doc->lentrailer = section_len - line_len;
  973.  
  974. #if 0
  975.     section_len = line_len;
  976.     preread = 1;
  977.     while (preread ||
  978.        readline(line, sizeof line, file, &position, &line_len)) {
  979.     if (!preread) section_len += line_len;
  980.     preread = 0;
  981.     if (DSCcomment(line) && iscomment(line+2, "Page:")) {
  982.         free(gettext(line+length("%%Page:"), &next_char));
  983.         if (sscanf(next_char, "%d", &thispage) != 1) thispage = 0;
  984.         if (!ignore && thispage == nextpage) {
  985.         if (doc->numpages > 0) {
  986.             doc->pages[doc->numpages-1].end = position;
  987.             doc->pages[doc->numpages-1].len += doc->lentrailer +
  988.                                section_len - line_len;
  989.         } else {
  990.             if (doc->endsetup) {
  991.             doc->endsetup = position;
  992.             doc->endsetup += doc->lentrailer +
  993.                      section_len - line_len;
  994.             } else if (doc->endprolog) {
  995.             doc->endprolog = position;
  996.             doc->endprolog += doc->lentrailer +
  997.                       section_len - line_len;
  998.             }
  999.         }
  1000.         goto newpage;
  1001.         }
  1002.     }
  1003.     }
  1004. #endif
  1005.     return doc;
  1006. }
  1007.  
  1008. /*
  1009.  *    psfree -- free dynamic storage associated with document structure.
  1010.  */
  1011.  
  1012. void
  1013. psfree(doc)
  1014.     struct document *doc;
  1015. {
  1016.     int i;
  1017.  
  1018.     if (doc) {
  1019.     for (i=0; i<doc->numpages; i++) {
  1020.         if (doc->pages[i].label) free(doc->pages[i].label);
  1021.     }
  1022.     for (i=0; i<doc->nummedia; i++) {
  1023.         if (doc->media[i].name) free(doc->media[i].name);
  1024.     }
  1025.     if (doc->title) free(doc->title);
  1026.     if (doc->date) free(doc->date);
  1027.     if (doc->pages) free(doc->pages);
  1028.     if (doc->media) free(doc->media);
  1029.     free(doc);
  1030.     }
  1031. }
  1032.  
  1033. /*
  1034.  * gettextine -- skip over white space and return the rest of the line.
  1035.  *               If the text begins with '(' return the text string
  1036.  *         using gettext().
  1037.  */
  1038.  
  1039. static char *
  1040. gettextline(line)
  1041.     char *line;
  1042. {
  1043.     char *cp;
  1044.  
  1045.     while (*line && (*line == ' ' || *line == '\t')) line++;
  1046.     if (*line == '(') {
  1047.     return gettext(line, NULL);
  1048.     } else {
  1049.     if (strlen(line) == 0) return NULL;
  1050.     cp = (char *) malloc(strlen(line));
  1051.     if (cp == NULL) {
  1052.         fprintf(stderr, "Fatal Error: Dynamic memory exhausted.\n");
  1053.         exit(-1);
  1054.     }
  1055.     strncpy(cp, line, strlen(line)-1);
  1056.     cp[strlen(line)-1] = '\0';
  1057.     return cp;
  1058.     }
  1059. }
  1060.  
  1061. /*
  1062.  *    gettext -- return the next text string on the line.
  1063.  *           return NULL if nothing is present.
  1064.  */
  1065.  
  1066. static char *
  1067. gettext(line, next_char)
  1068.     char *line;
  1069.     char **next_char;
  1070. {
  1071.     char text[PSLINELENGTH];    /* Temporary storage for text */
  1072.     char *cp;
  1073.     int quoted=0;
  1074.  
  1075.     while (*line && (*line == ' ' || *line == '\t')) line++;
  1076.     cp = text;
  1077.     if (*line == '(') {
  1078.     int level = 0;
  1079.     quoted=1;
  1080.     line++;
  1081.     while (*line && !(*line == ')' && level == 0 )) {
  1082.         if (*line == '\\') {
  1083.         if (*(line+1) == 'n') {
  1084.             *cp++ = '\n';
  1085.             line += 2;
  1086.         } else if (*(line+1) == 'r') {
  1087.             *cp++ = '\r';
  1088.             line += 2;
  1089.         } else if (*(line+1) == 't') {
  1090.             *cp++ = '\t';
  1091.             line += 2;
  1092.         } else if (*(line+1) == 'b') {
  1093.             *cp++ = '\b';
  1094.             line += 2;
  1095.         } else if (*(line+1) == 'f') {
  1096.             *cp++ = '\f';
  1097.             line += 2;
  1098.         } else if (*(line+1) == '\\') {
  1099.             *cp++ = '\\';
  1100.             line += 2;
  1101.         } else if (*(line+1) == '(') {
  1102.             *cp++ = '(';
  1103.             line += 2;
  1104.         } else if (*(line+1) == ')') {
  1105.             *cp++ = ')';
  1106.             line += 2;
  1107.         } else if (*(line+1) >= '0' && *(line+1) <= '9') {
  1108.             if (*(line+2) >= '0' && *(line+2) <= '9') {
  1109.             if (*(line+3) >= '0' && *(line+3) <= '9') {
  1110.                 *cp++ = ((*(line+1) - '0')*8 + *(line+2) - '0')*8 +
  1111.                     *(line+3) - '0';
  1112.                 line += 4;
  1113.             } else {
  1114.                 *cp++ = (*(line+1) - '0')*8 + *(line+2) - '0';
  1115.                 line += 3;
  1116.             }
  1117.             } else {
  1118.             *cp++ = *(line+1) - '0';
  1119.             line += 2;
  1120.             }
  1121.         } else {
  1122.             line++;
  1123.             *cp++ = *line++;
  1124.         }
  1125.         } else if (*line == '(') {
  1126.         level++;
  1127.         *cp++ = *line++;
  1128.         } else if (*line == ')') {
  1129.         level--;
  1130.         *cp++ = *line++;
  1131.         } else {
  1132.         *cp++ = *line++;
  1133.         }
  1134.     }
  1135.     } else {
  1136.     while (*line && !(*line == ' ' || *line == '\t' || *line == '\n'))
  1137.         *cp++ = *line++;
  1138.     }
  1139.     *cp = '\0';
  1140.     if (next_char) *next_char = line;
  1141.     if (!quoted && strlen(text) == 0) return NULL;
  1142.     cp = (char *) malloc(strlen(text)+1);
  1143.     if (cp == NULL) {
  1144.     fprintf(stderr, "Fatal Error: Dynamic memory exhausted.\n");
  1145.     exit(-1);
  1146.     }
  1147.     strcpy(cp, text);
  1148.     return cp;
  1149. }
  1150.  
  1151. /*
  1152.  *    readline -- Read the next line in the postscript file.
  1153.  *                  Automatically skip over data (as indicated by
  1154.  *                  %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
  1155.  *            comments.)
  1156.  *            Also, skip over included documents (as indicated by
  1157.  *            %%BeginDocument/%%EndDocument comments.)
  1158.  */
  1159.  
  1160. static char *
  1161. readline(line, size, fp, position, line_len)
  1162.     char *line;
  1163.     int size;
  1164.     FILE *fp;
  1165.     long *position;
  1166.     unsigned int *line_len;
  1167. {
  1168.     char text[PSLINELENGTH];    /* Temporary storage for text */
  1169.     char save[PSLINELENGTH];    /* Temporary storage for text */
  1170.     char *cp;
  1171.     unsigned int num;
  1172.     unsigned int nbytes;
  1173.     int i;
  1174.     char buf[BUFSIZ];
  1175.  
  1176.     if (position) *position = ftell(fp);
  1177.     cp = fgets(line, size, fp);
  1178.     if (cp == NULL) line[0] = '\0';
  1179.     *line_len = strlen(line);
  1180.     if (!(DSCcomment(line) && iscomment(line+2, "Begin"))) {
  1181.     /* Do nothing */
  1182.     } else if (iscomment(line+7, "Document:")) {
  1183.     strcpy(save, line+7);
  1184.     while (readline(line, size, fp, NULL, &nbytes) &&
  1185.            !(DSCcomment(line) && iscomment(line+2, "EndDocument"))) {
  1186.         *line_len += nbytes;
  1187.     }
  1188.     *line_len += nbytes;
  1189.     strcpy(line, save);
  1190.     } else if (iscomment(line+7, "Feature:")) {
  1191.     strcpy(save, line+7);
  1192.     while (readline(line, size, fp, NULL, &nbytes) &&
  1193.            !(DSCcomment(line) && iscomment(line+2, "EndFeature"))) {
  1194.         *line_len += nbytes;
  1195.     }
  1196.     *line_len += nbytes;
  1197.     strcpy(line, save);
  1198.     } else if (iscomment(line+7, "File:")) {
  1199.     strcpy(save, line+7);
  1200.     while (readline(line, size, fp, NULL, &nbytes) &&
  1201.            !(DSCcomment(line) && iscomment(line+2, "EndFile"))) {
  1202.         *line_len += nbytes;
  1203.     }
  1204.     *line_len += nbytes;
  1205.     strcpy(line, save);
  1206.     } else if (iscomment(line+7, "Font:")) {
  1207.     strcpy(save, line+7);
  1208.     while (readline(line, size, fp, NULL, &nbytes) &&
  1209.            !(DSCcomment(line) && iscomment(line+2, "EndFont"))) {
  1210.         *line_len += nbytes;
  1211.     }
  1212.     *line_len += nbytes;
  1213.     strcpy(line, save);
  1214.     } else if (iscomment(line+7, "ProcSet:")) {
  1215.     strcpy(save, line+7);
  1216.     while (readline(line, size, fp, NULL, &nbytes) &&
  1217.            !(DSCcomment(line) && iscomment(line+2, "EndProcSet"))) {
  1218.         *line_len += nbytes;
  1219.     }
  1220.     *line_len += nbytes;
  1221.     strcpy(line, save);
  1222.     } else if (iscomment(line+7, "Resource:")) {
  1223.     strcpy(save, line+7);
  1224.     while (readline(line, size, fp, NULL, &nbytes) &&
  1225.            !(DSCcomment(line) && iscomment(line+2, "EndResource"))) {
  1226.         *line_len += nbytes;
  1227.     }
  1228.     *line_len += nbytes;
  1229.     strcpy(line, save);
  1230.     } else if (iscomment(line+7, "Data:")) {
  1231.     text[0] = '\0';
  1232.     strcpy(save, line+7);
  1233.     if (sscanf(line+length("%%BeginData:"), "%d %*s %s", &num, text) >= 1) {
  1234.         if (strcmp(text, "Lines") == 0) {
  1235.         for (i=0; i < num; i++) {
  1236.             cp = fgets(line, size, fp);
  1237.             *line_len += cp ? strlen(line) : 0;
  1238.         }
  1239.         } else {
  1240.         while (num > BUFSIZ) {
  1241.             fread(buf, sizeof (char), BUFSIZ, fp);
  1242.             *line_len += BUFSIZ;
  1243.             num -= BUFSIZ;
  1244.         }
  1245.         fread(buf, sizeof (char), num, fp);
  1246.         *line_len += num;
  1247.         }
  1248.     }
  1249.     while (readline(line, size, fp, NULL, &nbytes) &&
  1250.            !(DSCcomment(line) && iscomment(line+2, "EndData"))) {
  1251.         *line_len += nbytes;
  1252.     }
  1253.     *line_len += nbytes;
  1254.     strcpy(line, save);
  1255.     } else if (iscomment(line+7, "Binary:")) {
  1256.     strcpy(save, line+7);
  1257.     if(sscanf(line+length("%%BeginBinary:"), "%d", &num) == 1) {
  1258.         while (num > BUFSIZ) {
  1259.         fread(buf, sizeof (char), BUFSIZ, fp);
  1260.         *line_len += BUFSIZ;
  1261.         num -= BUFSIZ;
  1262.         }
  1263.         fread(buf, sizeof (char), num, fp);
  1264.         *line_len += num;
  1265.     }
  1266.     while (readline(line, size, fp, NULL, &nbytes) &&
  1267.            !(DSCcomment(line) && iscomment(line+2, "EndBinary"))) {
  1268.         *line_len += nbytes;
  1269.     }
  1270.     *line_len += nbytes;
  1271.     strcpy(line, save);
  1272.     }
  1273.     return cp;
  1274. }
  1275.  
  1276. /*
  1277.  *    pscopy -- copy lines of Postscript from a section of one file
  1278.  *          to another file.
  1279.  *                Automatically switch to binary copying whenever
  1280.  *                %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
  1281.  *          comments are encountered.
  1282.  */
  1283.  
  1284. void
  1285. pscopy(from, to, begin, end)
  1286.     FILE *from;
  1287.     FILE *to;
  1288.     long begin;            /* set negative to avoid initial seek */
  1289.     long end;
  1290. {
  1291.     char line[PSLINELENGTH];    /* 255 characters + 1 newline + 1 NULL */
  1292.     char text[PSLINELENGTH];    /* Temporary storage for text */
  1293.     unsigned int num;
  1294.     int i;
  1295.     char buf[BUFSIZ];
  1296.  
  1297.     if (begin >= 0) fseek(from, begin, SEEK_SET);
  1298.     while (ftell(from) < end) {
  1299.  
  1300.     fgets(line, sizeof line, from);
  1301.     fputs(line, to);
  1302.  
  1303.     if (!(DSCcomment(line) && iscomment(line+2, "Begin"))) {
  1304.         /* Do nothing */
  1305.     } else if (iscomment(line+7, "Data:")) {
  1306.         text[0] = '\0';
  1307.         if (sscanf(line+length("%%BeginData:"),
  1308.                "%d %*s %s", &num, text) >= 1) {
  1309.         if (strcmp(text, "Lines") == 0) {
  1310.             for (i=0; i < num; i++) {
  1311.             fgets(line, sizeof line, from);
  1312.             fputs(line, to);
  1313.             }
  1314.         } else {
  1315.             while (num > BUFSIZ) {
  1316.             fread(buf, sizeof (char), BUFSIZ, from);
  1317.             fwrite(buf, sizeof (char), BUFSIZ, to);
  1318.             num -= BUFSIZ;
  1319.             }
  1320.             fread(buf, sizeof (char), num, from);
  1321.             fwrite(buf, sizeof (char), num, to);
  1322.         }
  1323.         }
  1324.     } else if (iscomment(line+7, "Binary:")) {
  1325.         if(sscanf(line+length("%%BeginBinary:"), "%d", &num) == 1) {
  1326.         while (num > BUFSIZ) {
  1327.             fread(buf, sizeof (char), BUFSIZ, from);
  1328.             fwrite(buf, sizeof (char), BUFSIZ, to);
  1329.             num -= BUFSIZ;
  1330.         }
  1331.         fread(buf, sizeof (char), num, from);
  1332.         fwrite(buf, sizeof (char), num, to);
  1333.         }
  1334.     }
  1335.     }
  1336. }
  1337.  
  1338. /*
  1339.  *    pscopyuntil -- copy lines of Postscript from a section of one file
  1340.  *               to another file until a particular comment is reached.
  1341.  *                     Automatically switch to binary copying whenever
  1342.  *                     %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
  1343.  *               comments are encountered.
  1344.  */
  1345.  
  1346. char *
  1347. pscopyuntil(from, to, begin, end, comment)
  1348.     FILE *from;
  1349.     FILE *to;
  1350.     long begin;            /* set negative to avoid initial seek */
  1351.     long end;
  1352. #if NeedFunctionPrototypes
  1353.     const
  1354. #endif
  1355.     char *comment;
  1356. {
  1357.     char line[PSLINELENGTH];    /* 255 characters + 1 newline + 1 NULL */
  1358.     char text[PSLINELENGTH];    /* Temporary storage for text */
  1359.     unsigned int num;
  1360.     int comment_length;
  1361.     int i;
  1362.     char buf[BUFSIZ];
  1363.     char *cp;
  1364.  
  1365.     comment_length = strlen(comment);
  1366.     if (begin >= 0) fseek(from, begin, SEEK_SET);
  1367.     while (ftell(from) < end) {
  1368.  
  1369.     fgets(line, sizeof line, from);
  1370.  
  1371.     /* iscomment cannot be used here,
  1372.      * because comment_length is not known at compile time. */
  1373.     if (strncmp(line, comment, comment_length) == 0) {
  1374.         cp = (char *) malloc(strlen(line)+1);
  1375.         if (cp == NULL) {
  1376.         fprintf(stderr, "Fatal Error: Dynamic memory exhausted.\n");
  1377.         exit(-1);
  1378.         }
  1379.         strcpy(cp, line);
  1380.         return cp;
  1381.     }
  1382.     fputs(line, to);
  1383.     if (!(DSCcomment(line) && iscomment(line+2, "Begin"))) {
  1384.         /* Do nothing */
  1385.     } else if (iscomment(line+7, "Data:")) {
  1386.         text[0] = '\0';
  1387.         if (sscanf(line+length("%%BeginData:"),
  1388.                "%d %*s %s", &num, text) >= 1) {
  1389.         if (strcmp(text, "Lines") == 0) {
  1390.             for (i=0; i < num; i++) {
  1391.             fgets(line, sizeof line, from);
  1392.             fputs(line, to);
  1393.             }
  1394.         } else {
  1395.             while (num > BUFSIZ) {
  1396.             fread(buf, sizeof (char), BUFSIZ, from);
  1397.             fwrite(buf, sizeof (char), BUFSIZ, to);
  1398.             num -= BUFSIZ;
  1399.             }
  1400.             fread(buf, sizeof (char), num, from);
  1401.             fwrite(buf, sizeof (char), num, to);
  1402.         }
  1403.         }
  1404.     } else if (iscomment(line+7, "Binary:")) {
  1405.         if(sscanf(line+length("%%BeginBinary:"), "%d", &num) == 1) {
  1406.         while (num > BUFSIZ) {
  1407.             fread(buf, sizeof (char), BUFSIZ, from);
  1408.             fwrite(buf, sizeof (char), BUFSIZ, to);
  1409.             num -= BUFSIZ;
  1410.         }
  1411.         fread(buf, sizeof (char), num, from);
  1412.         fwrite(buf, sizeof (char), num, to);
  1413.         }
  1414.     }
  1415.     }
  1416.     return NULL;
  1417. }
  1418.  
  1419. /*
  1420.  *    blank -- determine whether the line contains nothing but whitespace.
  1421.  */
  1422.  
  1423. static int
  1424. blank(line)
  1425.     char *line;
  1426. {
  1427.     char *cp = line;
  1428.  
  1429.     while (*cp == ' ' || *cp == '\t') cp++;
  1430.     return *cp == '\n' || (*cp == '%' && (line[0] != '%' || line[1] != '%'));
  1431. }
  1432.