psgen.c - enscript - GNU Enscript
 (HTM) git clone git://thinkerwim.org/enscript.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       psgen.c (66072B)
       ---
            1 /*
            2  * Convert ASCII to PostScript.
            3  * Copyright (c) 1995-2002 Markku Rossi.
            4  *
            5  * Author: Markku Rossi <mtr@iki.fi>
            6  */
            7 
            8 /*
            9  * This file is part of GNU Enscript.
           10  *
           11  * Enscript is free software: you can redistribute it and/or modify
           12  * it under the terms of the GNU General Public License as published by
           13  * the Free Software Foundation, either version 3 of the License, or
           14  * (at your option) any later version.
           15  *
           16  * Enscript is distributed in the hope that it will be useful,
           17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
           18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
           19  * GNU General Public License for more details.
           20  *
           21  * You should have received a copy of the GNU General Public License
           22  * along with Enscript.  If not, see <http://www.gnu.org/licenses/>.
           23  */
           24 
           25 #include <limits.h>
           26 #include "gsint.h"
           27 #include <libgen.h>
           28 
           29 /*
           30  * Types and definitions.
           31  */
           32 
           33 /* Values for token flags. */
           34 
           35 /* EPSF. */
           36 #define F_EPSF_CENTER                        0x01
           37 #define F_EPSF_RIGHT                        0x02
           38 #define M_EPSF_JUSTIFICATION                0x03
           39 
           40 #define F_EPSF_NO_CPOINT_UPDATE_X        0x04
           41 #define F_EPSF_NO_CPOINT_UPDATE_Y        0x08
           42 
           43 #define F_EPSF_ABSOLUTE_X                0x10
           44 #define F_EPSF_ABSOLUTE_Y                0x20
           45 
           46 #define F_EPSF_SCALE_X                        0x40
           47 #define F_EPSF_SCALE_Y                        0x80
           48 
           49 
           50 /* Predicate to check if we are at the correct slice. */
           51 #define CORRECT_SLICE() (slicing == 0 || current_slice == slice)
           52 
           53 /* Predicates for the current body font. */
           54 
           55 /* Is character <ch> printable. */
           56 #define ISPRINT(ch) (font_ctype[(unsigned char) (ch)] != ' ')
           57 
           58 /* Does character <ch> exist in current body font? */
           59 #define EXISTS(ch) (font_ctype[(unsigned char) (ch)] == '*')
           60 
           61 
           62 #define RESOURCE_LINE_WIDTH 75
           63 
           64 /* Token types. */
           65 typedef enum
           66 {
           67   tNONE,
           68   tEOF,
           69   tSTRING,
           70   tFORMFEED,
           71   tNEWLINE,
           72   tCARRIAGE_RETURN,
           73   tWRAPPED_NEWLINE,
           74   tEPSF,
           75   tSETFILENAME,
           76   tSETPAGENUMBER,
           77   tNEWPAGE,
           78   tFONT,
           79   tCOLOR,
           80   tBGCOLOR,
           81   tSAVEX,
           82   tLOADX,
           83   tPS
           84 } TokenType;
           85 
           86 /* Special escape tokens. */
           87 typedef enum
           88 {
           89   ESC_COMMENT,
           90   ESC_EPSF,
           91   ESC_FONT,
           92   ESC_COLOR,
           93   ESC_BGCOLOR,
           94   ESC_NEWPAGE,
           95   ESC_SETFILENAME,
           96   ESC_SETPAGENUMBER,
           97   ESC_SHADE,
           98   ESC_BGGRAY,
           99   ESC_ESCAPE,
          100   ESC_SAVEX,
          101   ESC_LOADX,
          102   ESC_PS
          103 } SpecialEscape;
          104 
          105 /* Token structure. */
          106 struct gs_token_st
          107 {
          108   TokenType type;
          109   unsigned int flags;
          110   double new_x;                        /* Current point x after this token. */
          111   double new_y;                        /* Current point y after this token. */
          112   int new_col;                        /* Line column after this token. */
          113 
          114   union
          115     {
          116       int i;
          117       char *str;
          118       struct
          119         {
          120           double x;                /* x-offset */
          121           double y;                /* y-offset */
          122           double w;                /* width */
          123           double h;                /* height */
          124           double xscale;
          125           double yscale;
          126           int llx, lly, urx, ury; /* Bounding box. */
          127           char filename[PATH_MAX];
          128           char *skipbuf;
          129           unsigned int skipbuf_len;
          130           unsigned int skipbuf_pos;
          131           FILE *fp;                /* File from which eps image is read. */
          132           int pipe;                /* Is <fp> opened to pipe?  */
          133         } epsf;
          134       Color color;
          135       Color bgcolor;
          136       struct
          137         {
          138           char name[PATH_MAX];
          139           FontPoint size;
          140           InputEncoding encoding;
          141         } font;
          142       char filename[PATH_MAX];
          143     } u;
          144 };
          145 
          146 typedef struct gs_token_st Token;
          147 
          148 
          149 /*
          150  * Prototypes for static functions.
          151  */
          152 
          153 static void get_next_token ___P ((InputStream *is, double linestart,
          154                                   double linepos, unsigned int col,
          155                                   double linew, Token *token));
          156 
          157 static void dump_ps_page_header ___P ((char *fname, int empty));
          158 
          159 static void dump_ps_page_trailer ();
          160 
          161 static void dump_empty_page ();
          162 
          163 /*
          164  * Recognize a EPS file described by <token>.  Returns 1 if file was a
          165  * valid EPS file or 0 otherwise.  File is accepted if it starts with
          166  * the PostScript magic `%!' and it has a valid `%%BoundingBox' DSC
          167  * comment.
          168  */
          169 static int recognize_eps_file ___P ((Token *token));
          170 
          171 /*
          172  * Insert EPS file described by <token> to the output stream.
          173  */
          174 static void paste_epsf ___P ((Token *token));
          175 
          176 /*
          177  * Check if InputStream <is> contains a file which can be passed
          178  * through without any modifications.  Returns 1 if file was passed or
          179  * 0 otherwise.
          180  */
          181 static int do_pass_through ___P ((char *fname, InputStream *is));
          182 
          183 /*
          184  * Read one float dimension from InputStream <is>.  If <units> is
          185  * true, number can be followed by an optional unit specifier.  If
          186  * <horizontal> is true, dimension is horizontal, otherwise it is
          187  * vertical (this is used to find out how big `line' units are).
          188  */
          189 static double read_float ___P ((InputStream *is, int units, int horizontal));
          190 
          191 /*
          192  * Print linenumber <linenum> to the beginning of the current line.
          193  * Current line start is specified by point (x, y).
          194  */
          195 static void print_line_number ___P ((double x, double y, double space,
          196                                      double margin, unsigned int linenum));
          197 
          198 /* Send PostScript to the output file. */
          199 #define OUTPUT(body)        \
          200   do {                        \
          201     if (cofp == NULL)        \
          202       cofp = ofp;        \
          203     if (do_print)        \
          204       fprintf body;        \
          205   } while (0)
          206 
          207 /* Divert output to tmp file so the total page count can be counted. */
          208 static void divert ();
          209 
          210 /* Paste diverted data to the output and patch the total page counts. */
          211 static void undivert ();
          212 
          213 /*
          214  * Handle two-side printing related binding options.  This function is
          215  * called once for each even-numbered page.
          216  */
          217 static void handle_two_side_options ();
          218 
          219 /*
          220  * Global variables.
          221  */
          222 
          223 unsigned int current_pagenum = 0; /* The number of the current page. */
          224 unsigned int total_pages_in_file;
          225 unsigned int input_filenum = 0;
          226 unsigned int current_file_linenum;
          227 int first_pagenum_for_file;
          228 char *fname = NULL;                /* The name of the current input file. */
          229 
          230 
          231 /*
          232  * Static variables
          233  */
          234 
          235 /* Have we dumped PS header? */
          236 static int ps_header_dumped = 0;
          237 
          238 /* Divert file. */
          239 static FILE *divertfp = NULL;
          240 
          241 /* Current output() file. */
          242 static FILE *cofp = NULL;
          243 
          244 /* To print or not to print, that's a question. */
          245 static int do_print = 1;
          246 
          247 /* Is ^@font{}-defined font active? */
          248 static int user_fontp = 0;
          249 
          250 /* The user ^@font{}-defined font. */
          251 static char user_font_name[PATH_MAX];
          252 static FontPoint user_font_pt;
          253 static InputEncoding user_font_encoding;
          254 
          255 /* Is ^@color{}-defined color active? */
          256 static int user_colorp = 0;
          257 
          258 /* The user ^@color{}-defined color. */
          259 static Color user_color;
          260 
          261 /* Is ^@bgcolor{}-defined color active? */
          262 static int user_bgcolorp = 0;
          263 
          264 /* The user ^@bgcolor{}-defined color. */
          265 static Color user_bgcolor;
          266 
          267 /* The last linenumber printed by print_line_number(). */
          268 static unsigned int print_line_number_last;
          269 
          270 /* Registers to store X-coordinates with the ^@savex{} escape.
          271    Initially these are uninitialized. */
          272 static double xstore[256];
          273 
          274 /*
          275  * Global functions.
          276  */
          277 
          278 void
          279 dump_ps_header ()
          280 {
          281   char *cp, *cp2;
          282   int i, j, got;
          283   char *ps_version_string;        /* Version string for PS procsets. */
          284 
          285 
          286   /* Dump PS header only once. */
          287   if (ps_header_dumped)
          288     return;
          289   ps_header_dumped = 1;
          290 
          291   /* Create version string. */
          292   ps_version_string = xstrdup (VERSION);
          293   cp = strrchr (ps_version_string, '.');
          294   *cp = ' ';
          295 
          296   /*
          297    * Header.
          298    */
          299 
          300   OUTPUT ((cofp, "%s\n", output_first_line));
          301   OUTPUT ((cofp, "%%%%BoundingBox: %d %d %d %d\n", media->llx, media->lly,
          302            media->urx, media->ury));
          303   OUTPUT ((cofp, "%%%%Title: %s\n", title));
          304   OUTPUT ((cofp, "%%%%For: %s\n", passwd->pw_gecos));
          305   OUTPUT ((cofp, "%%%%Creator: %s\n", PACKAGE_STRING));
          306   OUTPUT ((cofp, "%%%%CreationDate: %s\n", date_string));
          307   OUTPUT ((cofp, "%%%%Orientation: %s\n",
          308            ((nup > 1) && nup_landscape)
          309            || ((nup == 1) && landscape) ? "Landscape" : "Portrait"));
          310   OUTPUT ((cofp, "%%%%Pages: (atend)\n"));
          311   OUTPUT ((cofp, "%%%%DocumentMedia: %s %d %d 0 () ()\n",
          312            media->name, media->w, media->h));
          313   OUTPUT ((cofp, "%%%%DocumentNeededResources: (atend)\n"));
          314 
          315   if (count_key_value_set (pagedevice) > 0)
          316     OUTPUT ((cofp, "%%%%LanguageLevel: 2\n"));
          317 
          318   OUTPUT ((cofp, "%%%%EndComments\n"));
          319 
          320 
          321   /*
          322    * Procedure Definitions.
          323    */
          324 
          325   OUTPUT ((cofp, "%%%%BeginProlog\n"));
          326 
          327   /* Prolog. */
          328   OUTPUT ((cofp, "%%%%BeginResource: procset Enscript-Prolog %s\n",
          329            ps_version_string));
          330   if (!paste_file ("enscript", ".pro"))
          331     FATAL ((stderr, _("couldn't find prolog \"%s\": %s\n"), "enscript.pro",
          332             strerror (errno)));
          333   OUTPUT ((cofp, "%%%%EndResource\n"));
          334 
          335   /* Encoding vector. */
          336   OUTPUT ((cofp, "%%%%BeginResource: procset Enscript-Encoding-%s %s\n",
          337            encoding_name, ps_version_string));
          338   if (!paste_file (encoding_name, ".enc"))
          339     FATAL ((stderr, _("couldn't find encoding file \"%s.enc\": %s\n"),
          340             encoding_name, strerror (errno)));
          341   OUTPUT ((cofp, "%%%%EndResource\n"));
          342 
          343   OUTPUT ((cofp, "%%%%EndProlog\n"));
          344 
          345 
          346   /*
          347    * Document Setup.
          348    */
          349 
          350   OUTPUT ((cofp, "%%%%BeginSetup\n"));
          351 
          352   /* Download fonts. */
          353   for (got = strhash_get_first (download_fonts, &cp, &j, (void **) &cp2); got;
          354        got = strhash_get_next (download_fonts, &cp, &j, (void **) &cp2))
          355     download_font (cp);
          356 
          357   /* For each required font, emit %%IncludeResouce comment. */
          358   for (got = strhash_get_first (res_fonts, &cp, &j, (void **) &cp2); got;
          359        got = strhash_get_next (res_fonts, &cp, &j, (void **) &cp2))
          360     OUTPUT ((cofp, "%%%%IncludeResource: font %s\n", cp));
          361 
          362   OUTPUT ((cofp, "/HFpt_w %g def\n", HFpt.w));
          363   OUTPUT ((cofp, "/HFpt_h %g def\n", HFpt.h));
          364 
          365 
          366   /* Select our fonts. */
          367 
          368   /* Header font HF. */
          369   OUTPUT ((cofp, "/%s /HF-gs-font MF\n", HFname));
          370   OUTPUT ((cofp,
          371            "/HF /HF-gs-font findfont [HFpt_w 0 0 HFpt_h 0 0] makefont def\n"));
          372 
          373   /* Our default typing font F. */
          374   OUTPUT ((cofp, "/%s /F-gs-font MF\n", Fname));
          375   OUTPUT ((cofp, "/F-gs-font %g %g SF\n", Fpt.w, Fpt.h));
          376 
          377   /* Underlay. */
          378   if (underlay != NULL)
          379     {
          380       OUTPUT ((cofp, "/ul_str (%s) def\n", underlay));
          381       OUTPUT ((cofp, "/ul_w_ptsize %g def\n", ul_ptsize.w));
          382       OUTPUT ((cofp, "/ul_h_ptsize %g def\n", ul_ptsize.h));
          383       OUTPUT ((cofp, "/ul_gray %g def\n", ul_gray));
          384       OUTPUT ((cofp, "/ul_x %g def\n", ul_x));
          385       OUTPUT ((cofp, "/ul_y %g def\n", ul_y));
          386       OUTPUT ((cofp, "/ul_angle %g def\n", ul_angle));
          387       OUTPUT ((cofp, "/ul_style %d def\n", ul_style));
          388       OUTPUT ((cofp, "/%s /F-ul-font MF\n", ul_font));
          389       OUTPUT ((cofp, "/ul_font /F-ul-font findfont \
          390 [ul_w_ptsize 0 0 ul_h_ptsize 0 0] makefont def\n"));
          391     }
          392 
          393   /* Number of copies. */
          394   OUTPUT ((cofp, "/#copies %d def\n", num_copies));
          395 
          396   /* Page prefeed. */
          397   if (page_prefeed)
          398     OUTPUT ((cofp, "true page_prefeed\n"));
          399 
          400   /* Statusdict definitions. */
          401   if (count_key_value_set (statusdict) > 0)
          402     {
          403       OUTPUT ((cofp, "%% Statustdict definitions:\nstatusdict begin\n  "));
          404       i = 2;
          405       for (got = strhash_get_first (statusdict, &cp, &j, (void **) &cp2); got;
          406            got = strhash_get_next (statusdict, &cp, &j, (void **) &cp2))
          407         {
          408           j = strlen (cp) + 1 + strlen (cp2) + 1;
          409           if (i + j > RESOURCE_LINE_WIDTH)
          410             {
          411               OUTPUT ((cofp, "\n  "));
          412               i = 2;
          413             }
          414           OUTPUT ((cofp, "%s %s ", cp2, cp));
          415           i += j;
          416         }
          417       OUTPUT ((cofp, "\nend\n"));
          418     }
          419 
          420   /* Page device definitions. */
          421   if (pslevel >= 2 &&
          422       (count_key_value_set (pagedevice) > 0 || generate_PageSize))
          423     {
          424       OUTPUT ((cofp, "%% Pagedevice definitions:\n"));
          425       OUTPUT ((cofp, "gs_languagelevel 1 gt {\n  <<\n    "));
          426 
          427       i = 4;
          428       for (got = strhash_get_first (pagedevice, &cp, &j, (void **) &cp2); got;
          429            got = strhash_get_next (pagedevice, &cp, &j, (void **) &cp2))
          430         {
          431           j = strlen (cp2) + 1 + strlen (cp) + 2;
          432           if (i + j > RESOURCE_LINE_WIDTH)
          433             {
          434               OUTPUT ((cofp, "\n    "));
          435               i = 4;
          436             }
          437           OUTPUT ((cofp, "/%s %s ", cp, cp2));
          438           i += j;
          439         }
          440 
          441       if (generate_PageSize)
          442         {
          443           if (i + 21 > RESOURCE_LINE_WIDTH)
          444             {
          445               OUTPUT ((cofp, "\n    "));
          446               i = 4;
          447             }
          448           OUTPUT ((cofp, "/PageSize [%d %d] ", media->w, media->h));
          449           i += 21;
          450         }
          451 
          452       OUTPUT ((cofp, "\n  >> setpagedevice\n} if\n"));
          453     }
          454 
          455   /*
          456    * Dump header procset.  Header must come after all font inclusions
          457    * and enscript's dynamic state definition.
          458    */
          459   if (header != HDR_NONE)
          460     {
          461       char *hdr;
          462       if (header == HDR_SIMPLE)
          463         hdr = "simple";
          464       else
          465         hdr = fancy_header_name;
          466 
          467       OUTPUT ((cofp, "%%%%BeginResource: procset Enscript-Header-%s %s\n",
          468                hdr, ps_version_string));
          469       if (!paste_file (hdr, ".hdr"))
          470         FATAL ((stderr,
          471                 _("couldn't find header definition file \"%s.hdr\": %s\n"),
          472                 hdr, strerror (errno)));
          473       OUTPUT ((cofp, "%%%%EndResource\n"));
          474     }
          475 
          476   /*
          477    * Count output width and height here; we can't do it earlier because
          478    * header might have just allocated some extra space.
          479    */
          480   d_output_w = d_page_w;
          481   d_output_h = d_page_h - d_header_h - d_footer_h;
          482 
          483   /* Dump our current dynamic state. */
          484   OUTPUT ((cofp, "/d_page_w %d def\n", d_page_w));
          485   OUTPUT ((cofp, "/d_page_h %d def\n", d_page_h));
          486 
          487   OUTPUT ((cofp, "/d_header_x %d def\n", 0));
          488   OUTPUT ((cofp, "/d_header_y %d def\n", d_output_h + d_footer_h));
          489   OUTPUT ((cofp, "/d_header_w %d def\n", d_header_w));
          490   OUTPUT ((cofp, "/d_header_h %d def\n", d_header_h));
          491 
          492   OUTPUT ((cofp, "/d_footer_x %d def\n", 0));
          493   OUTPUT ((cofp, "/d_footer_y %d def\n", 0));
          494   OUTPUT ((cofp, "/d_footer_w %d def\n", d_header_w));
          495   OUTPUT ((cofp, "/d_footer_h %d def\n", d_footer_h));
          496 
          497   OUTPUT ((cofp, "/d_output_w %d def\n", d_output_w));
          498   OUTPUT ((cofp, "/d_output_h %d def\n", d_output_h));
          499   OUTPUT ((cofp, "/cols %d def\n", num_columns));
          500 
          501   OUTPUT ((cofp, "%%%%EndSetup\n"));
          502 }
          503 
          504 
          505 void
          506 dump_ps_trailer ()
          507 {
          508   int i, j, got;
          509   char *cp;
          510   void *value;
          511   unsigned int nup_subpage;
          512 
          513   if (!ps_header_dumped)
          514     /* No header, let's be consistent and forget trailer also. */
          515     return;
          516 
          517   /* The possible pending N-up showpage. */
          518   nup_subpage = (total_pages - 1) % nup;
          519   if (nup > 1 && nup_subpage + 1 != nup)
          520     /* N-up showpage missing. */
          521     OUTPUT ((cofp, "_R\nS\n"));
          522 
          523   /* Trailer. */
          524 
          525   OUTPUT ((cofp, "%%%%Trailer\n"));
          526 
          527   if (page_prefeed)
          528     OUTPUT ((cofp, "false page_prefeed\n"));
          529 
          530   OUTPUT ((cofp, "%%%%Pages: %d\n", total_pages));
          531 
          532   /* Document needed resources. */
          533 
          534   /* fonts. */
          535   OUTPUT ((cofp, "%%%%DocumentNeededResources: font "));
          536   i = 32;                        /* length of the previous string. */
          537   for (got = strhash_get_first (res_fonts, &cp, &j, &value); got;
          538        got = strhash_get_next (res_fonts, &cp, &j, &value))
          539     {
          540       if (i + strlen (cp) + 1 > RESOURCE_LINE_WIDTH)
          541         {
          542           OUTPUT ((cofp, "\n%%%%+ font "));
          543           i = 9;                /* length of the previous string. */
          544         }
          545       OUTPUT ((cofp, "%s ", cp));
          546       i += strlen (cp) + 1;
          547     }
          548   OUTPUT ((cofp, "\n%%%%EOF\n"));
          549 }
          550 
          551 
          552 void
          553 process_file (char *fname_arg, InputStream *is, int is_toc)
          554 {
          555   int col;
          556   double x, y;
          557   double lx, ly;
          558   double linewidth;                /* Line width in points. */
          559   double lineend;
          560   int done = 0;
          561   int page_clear = 1;
          562   unsigned int line_column;
          563   unsigned int current_linenum;
          564   double linenumber_space = 0;
          565   double linenumber_margin = 0;
          566   Token token;
          567   int reuse_last_token = 0;
          568   unsigned int current_slice = 1;
          569   int last_wrapped_line = -1;
          570   int last_spaced_file_linenum = -1;
          571   int save_current_pagenum;
          572   int toc_pagenum = 0;
          573 
          574   /* Save filename. */
          575   xfree (fname);
          576   fname = xstrdup (fname_arg);
          577 
          578   /* Init page number and line counters. */
          579   if (!continuous_page_numbers)
          580     current_pagenum = 0;
          581   total_pages_in_file = 0;
          582   current_file_linenum = start_line_number;
          583 
          584   /*
          585    * Count possible line number spaces.  This should be enought for 99999
          586    * lines
          587    */
          588   linenumber_space = FNT_CHAR_WIDTH ('0') * 5 + 1.0;
          589   linenumber_margin = FNT_CHAR_WIDTH (':') + FNT_CHAR_WIDTH ('m');
          590 
          591   /* We got a new input file. */
          592   input_filenum++;
          593 
          594   /* We haven't printed any line numbers yet. */
          595   print_line_number_last = (unsigned int) -1;
          596 
          597   if (pass_through || output_language_pass_through)
          598     if (do_pass_through (fname, is))
          599       /* All done. */
          600       return;
          601 
          602   /* We have work to do, let's give header a chance to dump itself. */
          603   dump_ps_header ();
          604 
          605   /*
          606    * Align files to the file_align boundary, this is handy for two-side
          607    * printing.
          608    */
          609   while ((total_pages % file_align) != 0)
          610     {
          611       total_pages++;
          612       dump_empty_page ();
          613     }
          614 
          615   MESSAGE (1, (stderr, _("processing file \"%s\"...\n"), fname));
          616 
          617   linewidth = d_output_w / num_columns - 2 * d_output_x_margin
          618     - line_indent;
          619 
          620   /* Save the current running page number for possible toc usage. */
          621   first_pagenum_for_file = total_pages + 1;
          622 
          623   /*
          624    * Divert our output to a temp file.  We will re-process it
          625    * afterwards to patch, for example, the number of pages in the
          626    * document.
          627    */
          628   divert ();
          629 
          630   /* Process this input file. */
          631   while (!done)
          632     {
          633       /* Start a new page. */
          634       page_clear = 1;
          635 
          636       for (col = 0; !done && col < num_columns; col++)
          637         {
          638           /* Move to the beginning of the column <col>. */
          639           lx = x = col * d_output_w / (float) num_columns + d_output_x_margin
          640             + line_indent;
          641           lineend = lx + linewidth;
          642 
          643           ly = y = d_footer_h + d_output_h - d_output_y_margin - LINESKIP;
          644           current_linenum = 0;
          645           line_column = 0;
          646 
          647           while (1)
          648             {
          649               if (line_numbers && line_column == 0
          650                   && (current_file_linenum != last_spaced_file_linenum))
          651                 {
          652                   /* Forward x by the amount needed by our line numbers. */
          653                   x += linenumber_space + linenumber_margin;
          654                   last_spaced_file_linenum = current_file_linenum;
          655                 }
          656 
          657               /* Get token. */
          658               if (!reuse_last_token)
          659                 get_next_token (is, lx, x, line_column, lineend, &token);
          660               reuse_last_token = 0;
          661 
          662               /*
          663                * Page header printing is delayed to this point because
          664                * we want to handle files ending with a newline character
          665                * with care.  If the last newline would cause a pagebreak,
          666                * otherwise we would print page header to the non-existent
          667                * next page and that would be ugly ;)
          668                */
          669 
          670               if (token.type == tEOF)
          671                 {
          672                   done = 1;
          673                   goto end_of_page;
          674                 }
          675 
          676               /*
          677                * Now we know that we are going to make marks to this page
          678                * => print page header.
          679                */
          680 
          681               if (page_clear)
          682                 {
          683                   PageRange *pr;
          684 
          685                   current_pagenum++;
          686                   total_pages_in_file++;
          687 
          688                   /* Check page ranges. */
          689                   if (page_ranges == NULL)
          690                     do_print = 1;
          691                   else
          692                     {
          693                       do_print = 0;
          694                       for (pr = page_ranges; pr; pr = pr->next)
          695                         {
          696                           if (pr->odd || pr->even)
          697                             {
          698                               if ((pr->odd && (current_pagenum % 2) == 1)
          699                                   || (pr->even && (current_pagenum % 2) == 0))
          700                                 {
          701                                   do_print = 1;
          702                                   break;
          703                                 }
          704                             }
          705                           else
          706                             {
          707                               if (pr->start <= current_pagenum
          708                                   && current_pagenum <= pr->end)
          709                                 {
          710                                   do_print = 1;
          711                                   break;
          712                                 }
          713                             }
          714                         }
          715                     }
          716 
          717                   if (do_print)
          718                     total_pages++;
          719 
          720                   if (is_toc)
          721                     {
          722                       save_current_pagenum = current_pagenum;
          723                       toc_pagenum--;
          724                       current_pagenum = toc_pagenum;
          725                     }
          726 
          727                   dump_ps_page_header (fname, 0);
          728                   page_clear = 0;
          729 
          730                   if (is_toc)
          731                     current_pagenum = save_current_pagenum;
          732                 }
          733 
          734               /* Print line highlight. */
          735               if (line_column == 0 && line_highlight_gray < 1.0)
          736                 OUTPUT ((cofp, "%g %g %g %g %g line_highlight\n",
          737                          lx, (y - baselineskip
          738                               + (font_bbox_lly * Fpt.h / UNITS_PER_POINT)),
          739                          linewidth, Fpt.h + baselineskip,
          740                          line_highlight_gray));
          741 
          742               /* Print line numbers if needed. */
          743               if (line_numbers && line_column == 0 && token.type != tFORMFEED)
          744                 print_line_number (lx, y, linenumber_space, linenumber_margin,
          745                                    current_file_linenum);
          746 
          747               /* Check rest of tokens. */
          748               switch (token.type)
          749                 {
          750                 case tFORMFEED:
          751                   switch (formfeed_type)
          752                     {
          753                     case FORMFEED_COLUMN:
          754                       goto end_of_column;
          755                       break;
          756 
          757                     case FORMFEED_PAGE:
          758                       goto end_of_page;
          759                       break;
          760 
          761                     case FORMFEED_HCOLUMN:
          762                       /*
          763                        * Advance y-coordinate to the next even
          764                        * `horizontal_column_height' position.
          765                        */
          766                       {
          767                         int current_row;
          768 
          769                         current_row = (ly - y) / horizontal_column_height;
          770                         y = ly - (current_row + 1) * horizontal_column_height;
          771 
          772                         /* Check the end of the page. */
          773                         if (y < d_footer_h + d_output_y_margin)
          774                           goto end_of_column;
          775                       }
          776                       break;
          777                     }
          778                   break;
          779 
          780                 case tSTRING:
          781                   if (CORRECT_SLICE ())
          782                     {
          783                       if (bggray < 1.0)
          784                         {
          785                           OUTPUT ((cofp, "%g %g %g %g %g (%s) bgs\n", x, y,
          786                                    Fpt.h + baselineskip,
          787                                    baselineskip
          788                                    - (font_bbox_lly * Fpt.h / UNITS_PER_POINT),
          789                                    bggray,
          790                                    token.u.str));
          791                         }
          792                       else if (user_bgcolorp)
          793                         {
          794                           OUTPUT ((cofp, "%g %g %g %g %g %g %g (%s) bgcs\n",
          795                                    x, y, Fpt.h + baselineskip,
          796                                    baselineskip
          797                                    - (font_bbox_lly * Fpt.h / UNITS_PER_POINT),
          798                                    user_bgcolor.r,
          799                                    user_bgcolor.g,
          800                                    user_bgcolor.b,
          801                                    token.u.str));
          802                         }
          803                       else
          804                         {
          805                           OUTPUT ((cofp, "%g %g M\n(%s) s\n", x, y,
          806                                    token.u.str));
          807                         }
          808                     }
          809                   x = token.new_x;
          810                   line_column = token.new_col;
          811                   break;
          812 
          813                 case tCARRIAGE_RETURN:
          814                   /* Just reset the x-coordinate. */
          815                   x = col * d_output_w / (float) num_columns
          816                     + d_output_x_margin + line_indent;
          817                   line_column = 0;
          818                   break;
          819 
          820                 case tNEWLINE:
          821                 case tWRAPPED_NEWLINE:
          822                   if (token.type == tNEWLINE)
          823                     {
          824                       current_file_linenum++;
          825                       current_slice = 1;
          826                       y -= LINESKIP;
          827                     }
          828                   else
          829                     {
          830                       current_slice++;
          831                       if (!slicing)
          832                         {
          833                           /* Mark wrapped line marks. */
          834                           switch (mark_wrapped_lines_style)
          835                             {
          836                             case MWLS_NONE:
          837                               /* nothing */
          838                               break;
          839 
          840                             case MWLS_PLUS:
          841                               OUTPUT ((cofp, "%g %g M (+) s\n", x, y));
          842                               break;
          843 
          844                             default:
          845                               /* Print some fancy graphics. */
          846                               OUTPUT ((cofp,
          847                                        "%g %g %g %g %d wrapped_line_mark\n",
          848                                        x, y, Fpt.w, Fpt.h,
          849                                        mark_wrapped_lines_style));
          850                               break;
          851                             }
          852 
          853                           /*
          854                            * For wrapped newlines, decrement y only if
          855                            * we are not slicing the input.
          856                            */
          857                           y -= LINESKIP;
          858                         }
          859 
          860                       /* Count the wrapped lines here. */
          861                       if (!slicing || current_slice > slice)
          862                         if (current_file_linenum != last_wrapped_line)
          863                           {
          864                             if (do_print)
          865                               num_truncated_lines++;
          866                             last_wrapped_line = current_file_linenum;
          867                           }
          868                     }
          869 
          870                   current_linenum++;
          871                   if (current_linenum >= lines_per_page
          872                       || y < d_footer_h + d_output_y_margin)
          873                     goto end_of_column;
          874 
          875                   x = col * d_output_w / (float) num_columns
          876                     + d_output_x_margin + line_indent;
          877                   line_column = 0;
          878                   break;
          879 
          880                 case tEPSF:
          881                   /* Count current point movement. */
          882 
          883                   if (token.flags & F_EPSF_ABSOLUTE_Y)
          884                     token.new_y = ly;
          885                   else
          886                     token.new_y = y;
          887                   token.new_y += token.u.epsf.y - token.u.epsf.h;
          888 
          889                   if (token.flags & F_EPSF_ABSOLUTE_X)
          890                     token.new_x = lx;
          891                   else
          892                     token.new_x = x;
          893                   token.new_x += token.u.epsf.x;
          894 
          895                   /* Check flags. */
          896 
          897                   /* Justification flags overwrite <x_ofs>. */
          898                   if (token.flags & F_EPSF_CENTER)
          899                     token.new_x = lx + (linewidth - token.u.epsf.w) / 2;
          900                   if (token.flags & F_EPSF_RIGHT)
          901                     token.new_x = lx + (linewidth - token.u.epsf.w);
          902 
          903                   /* Check if eps file does not fit to this column. */
          904                   if ((token.flags & F_EPSF_NO_CPOINT_UPDATE_Y) == 0
          905                       && token.new_y < d_footer_h + d_output_y_margin)
          906                     {
          907                       if (current_linenum == 0)
          908                         {
          909                           /*
          910                            * At the beginning of the column, warn user
          911                            * and print image.
          912                            */
          913                           MESSAGE (0, (stderr, _("EPS file \"%s\" is too \
          914 large for page\n"),
          915                                        token.u.epsf.filename));
          916                         }
          917                       else
          918                         {
          919                           /* Must start a new column. */
          920                           reuse_last_token = 1;
          921                           goto end_of_column;
          922                         }
          923                     }
          924 
          925                   /* Do paste. */
          926                   if (CORRECT_SLICE ())
          927                     paste_epsf (&token);
          928 
          929                   /* Update current point? */
          930                   if (!(token.flags & F_EPSF_NO_CPOINT_UPDATE_Y))
          931                     y = token.new_y;
          932                   if (!(token.flags & F_EPSF_NO_CPOINT_UPDATE_X))
          933                     x = token.new_x + token.u.epsf.w;
          934 
          935                   if (y < d_footer_h + d_output_y_margin)
          936                     goto end_of_column;
          937                   break;
          938 
          939                 case tFONT:
          940                   /* Select a new current font. */
          941                   if (line_column == 0)
          942                     {
          943                       double newh;
          944 
          945                       /* Check for possible line skip change. */
          946                       if (token.u.font.name[0] == '\0')
          947                         newh = default_Fpt.h;
          948                       else
          949                         newh = token.u.font.size.h;
          950 
          951                       if (newh != Fpt.h)
          952                         {
          953                           /* We need a different line skip value. */
          954                           y -= (newh - Fpt.h);
          955                         }
          956                       /*
          957                        * We must check for page overflow after we have
          958                        * set the new font.
          959                        */
          960                     }
          961 
          962                   MESSAGE (2, (stderr, "^@font="));
          963                   if (token.u.font.name[0] == '\0')
          964                     {
          965                       /* Select the default font. */
          966                       Fpt.w = default_Fpt.w;
          967                       Fpt.h = default_Fpt.h;
          968                       Fname = default_Fname;
          969                       encoding = default_Fencoding;
          970                       OUTPUT ((cofp, "/F-gs-font %g %g SF\n", Fpt.w, Fpt.h));
          971                       user_fontp = 0;
          972                     }
          973                   else
          974                     {
          975                       strhash_put (res_fonts, token.u.font.name,
          976                                    strlen (token.u.font.name) + 1,
          977                                    NULL, NULL);
          978                       if (token.u.font.encoding == default_Fencoding)
          979                         OUTPUT ((cofp, "/%s %g %g SUF\n", token.u.font.name,
          980                                  token.u.font.size.w, token.u.font.size.h));
          981                       else if (token.u.font.encoding == ENC_PS)
          982                         OUTPUT ((cofp, "/%s %g %g SUF_PS\n", token.u.font.name,
          983                                  token.u.font.size.w, token.u.font.size.h));
          984                       else
          985                         FATAL ((stderr,
          986                                 _("user font encoding can be only the system's default or `ps'")));
          987 
          988                       memset  (user_font_name, 0, sizeof(user_font_name));
          989                       strncpy (user_font_name, token.u.font.name, sizeof(user_font_name) - 1);
          990                       user_font_pt.w = token.u.font.size.w;
          991                       user_font_pt.h = token.u.font.size.h;
          992                       user_font_encoding = token.u.font.encoding;
          993                       user_fontp = 1;
          994 
          995                       Fpt.w = user_font_pt.w;
          996                       Fpt.h = user_font_pt.h;
          997                       Fname = user_font_name;
          998                       encoding = user_font_encoding;
          999                     }
         1000                   MESSAGE (2, (stderr, "%s %g/%gpt\n", Fname, Fpt.w, Fpt.h));
         1001                   read_font_info ();
         1002 
         1003                   /*
         1004                    * Check for page overflow in that case that we were
         1005                    * at the first column and font were changed to a bigger
         1006                    * one.
         1007                    */
         1008                   if (y < d_footer_h + d_output_y_margin)
         1009                     goto end_of_column;
         1010                   break;
         1011 
         1012                 case tCOLOR:
         1013                   /* Select a new color. */
         1014                   MESSAGE (2, (stderr, "^@color{%f %f %f}\n",
         1015                                token.u.color.r,
         1016                                token.u.color.g,
         1017                                token.u.color.b));
         1018                   if (token.u.color.r == token.u.color.g
         1019                       && token.u.color.g == token.u.color.b
         1020                       && token.u.color.b == 0.0)
         1021                     {
         1022                       /* Select the default color (black). */
         1023                       OUTPUT ((cofp, "0 setgray\n"));
         1024                       user_colorp = 0;
         1025                     }
         1026                   else
         1027                     {
         1028                       OUTPUT ((cofp, "%g %g %g setrgbcolor\n",
         1029                                token.u.color.r,
         1030                                token.u.color.g,
         1031                                token.u.color.b));
         1032 
         1033                       user_color.r = token.u.color.r;
         1034                       user_color.g = token.u.color.g;
         1035                       user_color.b = token.u.color.b;
         1036                       user_colorp = 1;
         1037                     }
         1038                   break;
         1039 
         1040                 case tBGCOLOR:
         1041                   /* Select a new background color. */
         1042                   MESSAGE (2, (stderr, "^@bgcolor{%f %f %f}\n",
         1043                                token.u.color.r,
         1044                                token.u.color.g,
         1045                                token.u.color.b));
         1046 
         1047                   if (token.u.color.r == token.u.color.g
         1048                       && token.u.color.g == token.u.color.b
         1049                       && token.u.color.b == 1.0)
         1050                     {
         1051                       /* Select the default bgcolor (white). */
         1052                       user_bgcolorp = 0;
         1053                     }
         1054                   else
         1055                     {
         1056                       user_bgcolor.r = token.u.color.r;
         1057                       user_bgcolor.g = token.u.color.g;
         1058                       user_bgcolor.b = token.u.color.b;
         1059                       user_bgcolorp = 1;
         1060                     }
         1061                   break;
         1062 
         1063                 case tSETFILENAME:
         1064                   xfree (fname);
         1065                   fname = xstrdup (token.u.filename);
         1066                   break;
         1067 
         1068                 case tSETPAGENUMBER:
         1069                   current_pagenum = token.u.i - 1;
         1070                   break;
         1071 
         1072                 case tNEWPAGE:
         1073                   if (current_linenum >= token.u.i)
         1074                     goto end_of_page;
         1075                   break;
         1076 
         1077                 case tSAVEX:
         1078                   xstore[(unsigned char) token.u.i] = x;
         1079                   break;
         1080 
         1081                 case tLOADX:
         1082                   x = xstore[(unsigned char) token.u.i];
         1083                   break;
         1084 
         1085                 case tPS:
         1086                   OUTPUT ((cofp, "%g %g M\n%s\n", x, y, token.u.str));
         1087                   xfree (token.u.str);
         1088                   break;
         1089 
         1090                 case tNONE:
         1091                 default:
         1092                   FATAL ((stderr, "process_file(): got illegal token %d",
         1093                           token.type));
         1094                   break;
         1095                 }
         1096             }
         1097         end_of_column:
         1098           ;                        /* ULTRIX's cc needs this line. */
         1099         }
         1100 
         1101     end_of_page:
         1102       if (!page_clear)
         1103         dump_ps_page_trailer ();
         1104     }
         1105 
         1106   /*
         1107    * Reset print flag to true so all the required document trailers
         1108    * etc. get printed properly.
         1109    */
         1110   do_print = 1;
         1111 
         1112   /* Undivert our output from the temp file to our output stream. */
         1113   undivert ();
         1114 
         1115   /* Table of contents? */
         1116   if (toc)
         1117     {
         1118       char *cp;
         1119       int save_total_pages = total_pages;
         1120 
         1121       /* use first pagenum in file for toc */
         1122       total_pages = first_pagenum_for_file;
         1123 
         1124       cp = format_user_string ("TOC", toc_fmt_string);
         1125       fprintf (toc_fp, "%s\n", cp);
         1126       xfree (cp);
         1127 
         1128       total_pages = save_total_pages;
         1129     }
         1130 }
         1131 
         1132 
         1133 /*
         1134  * Static functions.
         1135  */
         1136 
         1137 /* Help macros. */
         1138 
         1139 /* Check if character <ch> fits to current line. */
         1140 #define FITS_ON_LINE(ch) ((linepos + FNT_CHAR_WIDTH (ch) < linew) || col == 0)
         1141 
         1142 /* Is line buffer empty? */
         1143 #define BUFFER_EMPTY() (bufpos == 0)
         1144 
         1145 /* Unconditionally append character <ch> to the line buffer. */
         1146 #define APPEND_CHAR(ch)                                 \
         1147   do {                                                        \
         1148     if (bufpos >= buflen)                                \
         1149       {                                                        \
         1150         buflen += 4096;                                        \
         1151         buffer = xrealloc (buffer, buflen);                \
         1152       }                                                        \
         1153     buffer[bufpos++] = ch;                                \
         1154   } while (0)
         1155 
         1156 /*
         1157  * Copy character <ch> (it fits to this line) to output buffer and
         1158  * update current point counters.
         1159  */
         1160 #define EMIT(ch)                 \
         1161   do {                                \
         1162     APPEND_CHAR (ch);                \
         1163     linepos += FNT_CHAR_WIDTH (ch);        \
         1164     col++;                        \
         1165   } while (0)
         1166 
         1167 #define UNEMIT(ch)                \
         1168   do {                                \
         1169     linepos -= FNT_CHAR_WIDTH (ch); \
         1170     col--;                        \
         1171   } while (0)
         1172 
         1173 #define ISSPACE(ch) ((ch) == ' ' || (ch) == '\t')
         1174 #define ISOCTAL(ch) ('0' <= (ch) && (ch) <= '7')
         1175 
         1176 /* Read one special escape from input <fp>. */
         1177 
         1178 static struct
         1179 {
         1180   char *name;
         1181   SpecialEscape escape;
         1182 } escapes[] =
         1183   {
         1184     {"comment",                ESC_COMMENT},
         1185     {"epsf",                 ESC_EPSF},
         1186     {"font",                 ESC_FONT},
         1187     {"color",                ESC_COLOR},
         1188     {"bgcolor",                ESC_BGCOLOR},
         1189     {"newpage",                ESC_NEWPAGE},
         1190     {"ps",                ESC_PS},
         1191     {"setfilename",        ESC_SETFILENAME},
         1192     {"setpagenumber",        ESC_SETPAGENUMBER},
         1193     {"shade",                ESC_SHADE},
         1194     {"bggray",                ESC_BGGRAY},
         1195     {"escape",                ESC_ESCAPE},
         1196     {"savex",                ESC_SAVEX},
         1197     {"loadx",                ESC_LOADX},
         1198     {NULL, 0},
         1199   };
         1200 
         1201 
         1202 static void
         1203 read_special_escape (InputStream *is, Token *token)
         1204 {
         1205   char escname[256];
         1206   char buf[4096];
         1207   int i, e;
         1208   int ch;
         1209 
         1210   /* Get escape name. */
         1211   for (i = 0; i < sizeof (escname) - 1 && (ch = is_getc (is)) != EOF; i++)
         1212     {
         1213       if (!isalnum (ch))
         1214         {
         1215           is_ungetc (ch, is);
         1216           break;
         1217         }
         1218       else
         1219         escname[i] = ch;
         1220     }
         1221   escname[i] = '\0';
         1222 
         1223   /* Lookup escape. */
         1224   for (e = 0; escapes[e].name; e++)
         1225     if (strcmp (escname, escapes[e].name) == 0)
         1226       break;
         1227   if (escapes[e].name == NULL)
         1228     FATAL ((stderr, _("unknown special escape: %s"), escname));
         1229 
         1230   /*
         1231    * The epsf escape takes optional arguments so it must be handled
         1232    * differently.
         1233    */
         1234   if (escapes[e].escape == ESC_EPSF)
         1235     {
         1236       int i;
         1237       int pw, ph;
         1238       double scale;
         1239 
         1240       token->flags = 0;
         1241       token->u.epsf.x = 0.0;
         1242       token->u.epsf.y = 0.0;
         1243       token->u.epsf.h = 0.0;
         1244       token->u.epsf.pipe = 0;
         1245 
         1246       ch = is_getc (is);
         1247       if (ch == '[')
         1248         {
         1249           /* Read options. */
         1250           while ((ch = is_getc (is)) != EOF && ch != ']')
         1251             {
         1252               switch (ch)
         1253                 {
         1254                 case 'c':        /* center justification */
         1255                   token->flags &= ~M_EPSF_JUSTIFICATION;
         1256                   token->flags |= F_EPSF_CENTER;
         1257                   break;
         1258 
         1259                 case 'n':        /* no current point update */
         1260                   /* Check the next character. */
         1261                   ch = is_getc (is);
         1262                   switch (ch)
         1263                     {
         1264                     case 'x':
         1265                       token->flags |= F_EPSF_NO_CPOINT_UPDATE_X;
         1266                       break;
         1267 
         1268                     case 'y':
         1269                       token->flags |= F_EPSF_NO_CPOINT_UPDATE_Y;
         1270                       break;
         1271 
         1272                     default:
         1273                       is_ungetc (ch, is);
         1274                       token->flags |= F_EPSF_NO_CPOINT_UPDATE_X;
         1275                       token->flags |= F_EPSF_NO_CPOINT_UPDATE_Y;
         1276                       break;
         1277                     }
         1278                   break;
         1279 
         1280                 case 'r':        /* right justification */
         1281                   token->flags &= ~M_EPSF_JUSTIFICATION;
         1282                   token->flags |= F_EPSF_RIGHT;
         1283                   break;
         1284 
         1285 
         1286                 case 's':        /* scale */
         1287                   /* Check the next character. */
         1288                   ch = is_getc (is);
         1289                   switch (ch)
         1290                     {
         1291                     case 'x':
         1292                       token->flags |= F_EPSF_SCALE_X;
         1293                       token->u.epsf.xscale = read_float (is, 0, 1);
         1294                       break;
         1295 
         1296                     case 'y':
         1297                       token->flags |= F_EPSF_SCALE_Y;
         1298                       token->u.epsf.yscale = read_float (is, 0, 0);
         1299                       break;
         1300 
         1301                     default:
         1302                       is_ungetc (ch, is);
         1303                       token->flags |= F_EPSF_SCALE_X;
         1304                       token->flags |= F_EPSF_SCALE_Y;
         1305                       token->u.epsf.xscale = token->u.epsf.yscale
         1306                         = read_float (is, 0, 1);
         1307                       break;
         1308                     }
         1309                   break;
         1310 
         1311                 case 'x':        /* x-position */
         1312                   token->u.epsf.x = read_float (is, 1, 1);
         1313 
         1314                   /* Check the next character. */
         1315                   ch = is_getc (is);
         1316                   switch (ch)
         1317                     {
         1318                     case 'a':
         1319                       token->flags |= F_EPSF_ABSOLUTE_X;
         1320                       break;
         1321 
         1322                     default:
         1323                       is_ungetc (ch, is);
         1324                       break;
         1325                     }
         1326                   break;
         1327 
         1328                 case 'y':        /* y-position */
         1329                   token->u.epsf.y = - read_float (is, 1, 0);
         1330 
         1331                   /* Check the next character. */
         1332                   ch = is_getc (is);
         1333                   switch (ch)
         1334                     {
         1335                     case 'a':
         1336                       token->flags |= F_EPSF_ABSOLUTE_Y;
         1337                       break;
         1338 
         1339                     default:
         1340                       is_ungetc (ch, is);
         1341                       break;
         1342                     }
         1343                   break;
         1344 
         1345                 case 'h':        /* height */
         1346                   token->u.epsf.h = read_float (is, 1, 0);
         1347                   break;
         1348 
         1349                 case ' ':
         1350                 case '\t':
         1351                   break;
         1352 
         1353                 default:
         1354                   FATAL ((stderr, _("illegal option %c for ^@epsf escape"),
         1355                           ch));
         1356                 }
         1357             }
         1358           if (ch != ']')
         1359             FATAL ((stderr,
         1360                     _("malformed ^@epsf escape: no ']' after options")));
         1361 
         1362           ch = is_getc (is);
         1363         }
         1364       if (ch == '{')
         1365         {
         1366           /* Read filename. */
         1367           for (i = 0; (ch = is_getc (is)) != EOF && ch != '}'; i++)
         1368             {
         1369               token->u.epsf.filename[i] = ch;
         1370               if (i + 1 >= sizeof (token->u.epsf.filename))
         1371                 FATAL ((stderr,
         1372                         _("too long file name for ^@epsf escape:\n%.*s"),
         1373                         i, token->u.epsf.filename));
         1374             }
         1375           if (ch == EOF)
         1376             FATAL ((stderr, _("unexpected EOF while scanning ^@epsf escape")));
         1377 
         1378           token->u.epsf.filename[i] = '\0';
         1379           token->type = tEPSF;
         1380         }
         1381       else
         1382         FATAL ((stderr, _("malformed ^@epsf escape: no '{' found")));
         1383 
         1384       /*
         1385        * Now we have a valid epsf-token in <token>.  Let's read BoundingBox
         1386        * and do some calculations.
         1387        */
         1388       if (!recognize_eps_file (token))
         1389         /* Recognize eps has already printed error message so we are done. */
         1390         token->type = tNONE;
         1391       else
         1392         {
         1393           /* Some fixups for x and y dimensions. */
         1394           token->u.epsf.y += LINESKIP - 1;
         1395           if (token->u.epsf.h != 0.0)
         1396             token->u.epsf.h -= 1.0;
         1397 
         1398           /* Count picture's width and height. */
         1399 
         1400           pw = token->u.epsf.urx - token->u.epsf.llx;
         1401           ph = token->u.epsf.ury - token->u.epsf.lly;
         1402 
         1403           /* The default scale. */
         1404           if (token->u.epsf.h == 0.0)
         1405             scale = 1.0;
         1406           else
         1407             scale = token->u.epsf.h / ph;
         1408 
         1409           if ((token->flags & F_EPSF_SCALE_X) == 0)
         1410             token->u.epsf.xscale = scale;
         1411           if ((token->flags & F_EPSF_SCALE_Y) == 0)
         1412             token->u.epsf.yscale = scale;
         1413 
         1414           pw *= token->u.epsf.xscale;
         1415           ph *= token->u.epsf.yscale;
         1416 
         1417           token->u.epsf.w = pw;
         1418           token->u.epsf.h = ph;
         1419         }
         1420     }
         1421   else if (escapes[e].escape == ESC_COMMENT)
         1422     {
         1423       /* Comment the rest of this line. */
         1424       while ((ch = is_getc (is)) != EOF && ch != nl)
         1425         ;
         1426       token->type = tNONE;
         1427     }
         1428   else
         1429     {
         1430       char *cp;
         1431       int parenlevel;
         1432 
         1433       /*
         1434        * Handle the rest of the escapes.
         1435        */
         1436 
         1437       /* Read argument. */
         1438       ch = is_getc (is);
         1439       if (ch != '{')
         1440         FATAL ((stderr, _("malformed %s escape: no '{' found"),
         1441                 escapes[e].name));
         1442 
         1443       parenlevel = 0;
         1444       for (i = 0;
         1445            (ch = is_getc (is)) != EOF && (parenlevel > 0 || ch != '}'); i++)
         1446         {
         1447           if (ch == '{')
         1448             parenlevel++;
         1449           else if (ch == '}')
         1450             parenlevel--;
         1451 
         1452           buf[i] = ch;
         1453           if (i + 1 >= sizeof (buf))
         1454             FATAL ((stderr, _("too long argument for %s escape:\n%.*s"),
         1455                     escapes[e].name, i, buf));
         1456         }
         1457       buf[i] = '\0';
         1458 
         1459       /* And now handle the escape. */
         1460       switch (escapes[e].escape)
         1461         {
         1462         case ESC_FONT:
         1463           memset  (token->u.font.name, 0, sizeof(token->u.font.name));
         1464           strncpy (token->u.font.name, buf, sizeof(token->u.font.name) - 1);
         1465 
         1466           /* Check for the default font. */
         1467           if (strcmp (token->u.font.name, "default") == 0)
         1468             token->u.font.name[0] = '\0';
         1469           else
         1470             {
         1471               if (!parse_font_spec (token->u.font.name, &cp,
         1472                                     &token->u.font.size,
         1473                                     &token->u.font.encoding))
         1474                 FATAL ((stderr, _("malformed font spec for ^@font escape: %s"),
         1475                         token->u.font.name));
         1476 
         1477               memset  (token->u.font.name, 0, sizeof(token->u.font.name));
         1478               strncpy (token->u.font.name, cp, sizeof(token->u.font.name) - 1);
         1479               xfree (cp);
         1480             }
         1481           token->type = tFONT;
         1482           break;
         1483 
         1484         case ESC_COLOR:
         1485         case ESC_BGCOLOR:
         1486           /* Check for the default color. */
         1487           if (strcmp (buf, "default") == 0)
         1488             {
         1489               double val = 0;
         1490 
         1491               if (escapes[e].escape == ESC_BGCOLOR)
         1492                 val = 1;
         1493 
         1494               token->u.color.r = val;
         1495               token->u.color.g = val;
         1496               token->u.color.b = val;
         1497             }
         1498           else
         1499             {
         1500               int got;
         1501 
         1502               got = sscanf (buf, "%g %g %g",
         1503                             &token->u.color.r,
         1504                             &token->u.color.g,
         1505                             &token->u.color.b);
         1506               switch (got)
         1507                 {
         1508                 case 0:
         1509                 case 2:
         1510                   FATAL ((stderr,
         1511                           _("malformed color spec for ^@%s escape: %s"),
         1512                           escapes[e].escape == ESC_COLOR
         1513                           ? "color" : "bgcolor",
         1514                           buf));
         1515                   break;
         1516 
         1517                 case 1:
         1518                   token->u.color.g = token->u.color.b = token->u.color.r;
         1519                   break;
         1520 
         1521                 default:
         1522                   /* Got all three components. */
         1523                   break;
         1524                 }
         1525             }
         1526           if (escapes[e].escape == ESC_COLOR)
         1527             token->type = tCOLOR;
         1528           else
         1529             token->type = tBGCOLOR;
         1530           break;
         1531 
         1532         case ESC_SHADE:
         1533           line_highlight_gray = atof (buf);
         1534           if (line_highlight_gray < 0.0 || line_highlight_gray > 1.0)
         1535             FATAL ((stderr, _("invalid value for ^@shade escape: %s"), buf));
         1536 
         1537           token->type = tNONE;
         1538           break;
         1539 
         1540         case ESC_BGGRAY:
         1541           bggray = atof (buf);
         1542           if (bggray < 0.0 || bggray > 1.0)
         1543             FATAL ((stderr, _("invalid value for ^@bggray escape: %s"), buf));
         1544 
         1545           token->type = tNONE;
         1546           break;
         1547 
         1548         case ESC_ESCAPE:
         1549           if (strcmp (buf, "default") == 0)
         1550             escape_char = default_escape_char;
         1551           else
         1552             escape_char = atoi (buf);
         1553           token->type = tNONE;
         1554           break;
         1555 
         1556         case ESC_SETFILENAME:
         1557           memset  (token->u.filename, 0, sizeof(token->u.filename));
         1558           strncpy (token->u.filename, buf, sizeof(token->u.filename) - 1);
         1559           token->type = tSETFILENAME;
         1560           break;
         1561 
         1562         case ESC_SETPAGENUMBER:
         1563           token->u.i = atoi (buf);
         1564           token->type = tSETPAGENUMBER;
         1565           break;
         1566 
         1567         case ESC_NEWPAGE:
         1568           if (i == 0)
         1569             token->u.i = 1;        /* The default is the first line. */
         1570           else
         1571             token->u.i = atoi (buf);
         1572           token->type = tNEWPAGE;
         1573           break;
         1574 
         1575         case ESC_SAVEX:
         1576           token->type = tSAVEX;
         1577           token->u.i = atoi (buf);
         1578           break;
         1579 
         1580         case ESC_LOADX:
         1581           token->type = tLOADX;
         1582           token->u.i = atoi (buf);
         1583           break;
         1584 
         1585         case ESC_PS:
         1586           token->u.str = xstrdup (buf);
         1587           token->type = tPS;
         1588           break;
         1589 
         1590         default:
         1591           /* NOTREACHED */
         1592           abort ();
         1593           break;
         1594         }
         1595     }
         1596 }
         1597 
         1598 
         1599 /* Get next token from input file <fp>. */
         1600 static void
         1601 get_next_token (InputStream *is, double linestart, double linepos,
         1602                 unsigned int col, double linew, Token *token)
         1603 {
         1604   static unsigned char *buffer = NULL; /* output buffer */
         1605   static unsigned int buflen = 0; /* output buffer's length */
         1606   unsigned int bufpos = 0;        /* current position in output buffer */
         1607   int ch = 0;
         1608   int done = 0;
         1609   int i;
         1610   static int pending_token = tNONE;
         1611   unsigned int original_col = col;
         1612 
         1613   if (pending_token != tNONE)
         1614     {
         1615       token->type = pending_token;
         1616       pending_token = tNONE;
         1617       return;
         1618     }
         1619 
         1620 #define DONE_DONE 1
         1621 #define DONE_WRAP 2
         1622 
         1623   while (!done)
         1624     {
         1625       ch = is_getc (is);
         1626       switch (ch)
         1627         {
         1628         case EOF:
         1629           if (BUFFER_EMPTY ())
         1630             {
         1631               token->type = tEOF;
         1632               return;
         1633             }
         1634 
         1635           done = DONE_DONE;
         1636           break;
         1637 
         1638         case '\r':
         1639         case '\n':
         1640           /*
         1641            * One of these is the newline character and the other one
         1642            * is carriage return.
         1643            */
         1644           if (ch == nl)
         1645             {
         1646               /* The newline character. */
         1647               if (BUFFER_EMPTY ())
         1648                 {
         1649                   token->type = tNEWLINE;
         1650                   return;
         1651                 }
         1652               else
         1653                 {
         1654                   is_ungetc (ch, is);
         1655                   done = DONE_DONE;
         1656                 }
         1657             }
         1658           else
         1659             {
         1660               /* The carriage return character. */
         1661               if (BUFFER_EMPTY ())
         1662                 {
         1663                   token->type = tCARRIAGE_RETURN;
         1664                   return;
         1665                 }
         1666               else
         1667                 {
         1668                   is_ungetc (ch, is);
         1669                   done = DONE_DONE;
         1670                 }
         1671             }
         1672           break;
         1673 
         1674         case '\t':
         1675           if (font_is_fixed)
         1676             {
         1677               i = tabsize - (col % tabsize);
         1678               for (; i > 0; i--)
         1679                 {
         1680                   if (FITS_ON_LINE (' '))
         1681                     EMIT (' ');
         1682                   else
         1683                     {
         1684                       done = DONE_WRAP;
         1685                       break;
         1686                     }
         1687                 }
         1688             }
         1689           else
         1690             {
         1691               /* Proportional font. */
         1692 
         1693               double grid = tabsize * FNT_CHAR_WIDTH (' ');
         1694               col++;
         1695 
         1696               /* Move linepos to the next multiple of <grid>. */
         1697               linepos = (((int) ((linepos - linestart) / grid) + 1) * grid
         1698                          + linestart);
         1699               if (linepos >= linew)
         1700                 done = DONE_WRAP;
         1701               else
         1702                 done = DONE_DONE;
         1703             }
         1704           break;
         1705 
         1706         case '\f':
         1707           if (BUFFER_EMPTY ())
         1708             {
         1709               if (interpret_formfeed)
         1710                 token->type = tFORMFEED;
         1711               else
         1712                 token->type = tNEWLINE;
         1713               return;
         1714             }
         1715           else
         1716             {
         1717               is_ungetc (ch, is);
         1718               done = DONE_DONE;
         1719             }
         1720           break;
         1721 
         1722         default:
         1723           /* Handle special escapes. */
         1724           if (special_escapes && ch == escape_char)
         1725             {
         1726               if (BUFFER_EMPTY ())
         1727                 {
         1728                   /* Interpret special escapes. */
         1729                   read_special_escape (is, token);
         1730                   if (token->type != tNONE)
         1731                     return;
         1732 
         1733                   /*
         1734                    * Got tNONE special escape => read_special_escape()
         1735                    * has already done what was needed.  Just read more.
         1736                    */
         1737                   break;
         1738                 }
         1739               else
         1740                 {
         1741                   is_ungetc (ch, is);
         1742                   done = DONE_DONE;
         1743                   break;
         1744                 }
         1745             }
         1746 
         1747           /* Handle backspace character. */
         1748           if (ch == bs)
         1749             {
         1750               if (BUFFER_EMPTY () || !EXISTS (buffer[bufpos - 1]))
         1751                 linepos -= FNT_CHAR_WIDTH ('m');
         1752               else
         1753                 linepos -= FNT_CHAR_WIDTH (buffer[bufpos - 1]);
         1754 
         1755               done = DONE_DONE;
         1756               break;
         1757             }
         1758 
         1759           /* Check normal characters. */
         1760           if (EXISTS (ch))
         1761             {
         1762               if (FITS_ON_LINE (ch))
         1763                 {
         1764                   /*
         1765                    * Print control characters (and optionally
         1766                    * characters greater than 127) in the escaped form
         1767                    * so PostScript interpreter will not hang on them.
         1768                    */
         1769                   if (ch < 040 || (clean_7bit && ch >= 0200))
         1770                     {
         1771                       char buf[10];
         1772 
         1773                       sprintf (buf, "\\%03o", ch);
         1774                       for (i = 0; buf[i]; i++)
         1775                         APPEND_CHAR (buf[i]);
         1776 
         1777                       /* Update current point counters manually. */
         1778                       linepos += FNT_CHAR_WIDTH (ch);
         1779                       col++;
         1780                     }
         1781                   else if (ch == '(' || ch == ')' || ch == '\\')
         1782                     {
         1783                       /* These must be quoted in PostScript strings. */
         1784                       APPEND_CHAR ('\\');
         1785                       EMIT (ch);
         1786                     }
         1787                   else
         1788                     EMIT (ch);
         1789                 }
         1790               else
         1791                 {
         1792                   is_ungetc (ch, is);
         1793                   done = DONE_WRAP;
         1794                 }
         1795             }
         1796           else if (ISPRINT (ch))
         1797             {
         1798               /* Printable, but do not exists in this font. */
         1799               if (FITS_ON_LINE ('?'))
         1800                 {
         1801                   EMIT ('?');
         1802                   if (missing_chars[ch]++ == 0)
         1803                     num_missing_chars++;
         1804                 }
         1805               else
         1806                 {
         1807                   is_ungetc (ch, is);
         1808                   done = DONE_WRAP;
         1809                 }
         1810             }
         1811           else
         1812             {
         1813               char buf[20];
         1814               double len = 0.0;
         1815 
         1816               /*
         1817                * Non-printable and does not exist in current font, print
         1818                * it in the format specified by non_printable_format.
         1819                */
         1820 
         1821               if (non_printable_chars[ch]++ == 0)
         1822                 num_non_printable_chars++;
         1823 
         1824               switch (non_printable_format)
         1825                 {
         1826                 case NPF_SPACE:
         1827                   strcpy (buf, " ");
         1828                   break;
         1829 
         1830                 case NPF_QUESTIONMARK:
         1831                   strcpy (buf, "?");
         1832                   break;
         1833 
         1834                 case NPF_CARET:
         1835                   if (ch < 0x20)
         1836                     {
         1837                       buf[0] = '^';
         1838                       buf[1] = '@' + ch;
         1839                       buf[2] = '\0';
         1840                       break;
         1841                     }
         1842                   /* FALLTHROUGH */
         1843 
         1844                 case NPF_OCTAL:
         1845                   sprintf (buf, "\\%03o", ch);
         1846                   break;
         1847                 }
         1848 
         1849               /* Count length. */
         1850               for (i = 0; buf[i]; i++)
         1851                 len += FNT_CHAR_WIDTH (buf[i]);
         1852 
         1853               if (linepos + len < linew || col == 0)
         1854                 {
         1855                   /* Print it. */
         1856                   for (i = 0; buf[i]; i++)
         1857                     {
         1858                       if (buf[i] == '\\')
         1859                         APPEND_CHAR ('\\'); /* Escape '\\' characters. */
         1860                       EMIT (buf[i]);
         1861                     }
         1862                 }
         1863               else
         1864                 {
         1865                   is_ungetc (ch, is);
         1866                   done = DONE_WRAP;
         1867                 }
         1868             }
         1869           break;
         1870         }
         1871     }
         1872 
         1873   /* Got a string. */
         1874 
         1875   /* Check for wrapped line. */
         1876   if (done == DONE_WRAP)
         1877     {
         1878       /* This line is too long. */
         1879       ch = nl;
         1880       if (line_end == LE_TRUNCATE)
         1881         {
         1882           /* Truncate this line. */
         1883           while ((ch = is_getc (is)) != EOF && ch != nl)
         1884             ;
         1885         }
         1886       else if (!BUFFER_EMPTY () && line_end == LE_WORD_WRAP)
         1887         {
         1888           int w;
         1889 
         1890           if (ISSPACE (buffer[bufpos - 1]))
         1891             {
         1892               /* Skip all whitespace from the end of the wrapped line. */
         1893               while ((w = is_getc (is)) != EOF && ISSPACE (w))
         1894                 ;
         1895               is_ungetc (w, is);
         1896             }
         1897           else
         1898             {
         1899               /* Find the previous word boundary for the wrap. */
         1900               for (w = bufpos - 1; w >= 0 && !ISSPACE (buffer[w]); w--)
         1901                 ;
         1902               w++;
         1903               if (w > 0 || original_col > 0)
         1904                 {
         1905                   /*
         1906                    * Ok, we found a word boundary.  Now we must unemit
         1907                    * characters from the buffer to the intput stream.
         1908                    *
         1909                    * Note:
         1910                    *  - bufpos is unsigned integer variable
         1911                    *  - some characters are escaped with '\\'
         1912                    *  - some characters are printed in octal notation
         1913                    */
         1914                   do
         1915                     {
         1916                       bufpos--;
         1917 
         1918                       /* Check for '(', ')' and '\\'. */
         1919                       if (bufpos > w
         1920                           && (buffer[bufpos] == '('
         1921                               || buffer[bufpos] ==  ')'
         1922                               || buffer[bufpos] == '\\')
         1923                           && buffer[bufpos - 1] == '\\')
         1924                         {
         1925                           is_ungetc (buffer[bufpos], is);
         1926                           UNEMIT (buffer[bufpos]);
         1927                           bufpos--;
         1928                         }
         1929                       /* Check the octal notations "\\%03o". */
         1930                       else if (bufpos - 2 > w
         1931                                && ISOCTAL (buffer[bufpos])
         1932                                && ISOCTAL (buffer[bufpos - 1])
         1933                                && ISOCTAL (buffer[bufpos - 2])
         1934                                && buffer[bufpos - 3] == '\\')
         1935                         {
         1936                           unsigned int ti;
         1937 
         1938                           /*
         1939                            * It is a potential octal character.  Now we
         1940                            * must process the buffer from the beginning
         1941                            * and see if `bufpos - 3' really starts a character.
         1942                            */
         1943                           for (ti = w; ti < bufpos - 3; ti++)
         1944                             {
         1945                               if (buffer[ti] == '\\')
         1946                                 {
         1947                                   if (ISOCTAL (buffer[ti + 1]))
         1948                                     {
         1949                                       unsigned int tti;
         1950 
         1951                                       for (tti = 0;
         1952                                            tti < 3 && ISOCTAL (buffer[ti + 1]);
         1953                                            tti++, ti++)
         1954                                         ;
         1955                                     }
         1956                                   else
         1957                                     /* Simple escape. */
         1958                                     ti++;
         1959                                 }
         1960                             }
         1961 
         1962                           /*
         1963                            * If <ti> is equal to <bufpos - 3>, we found
         1964                            * an octal character, otherwise the leading
         1965                            * backslash at <bufpos - 3> belongs to the
         1966                            * previous character.
         1967                            */
         1968                           if (ti == bufpos - 3)
         1969                             {
         1970                               int tch;
         1971 
         1972                               tch = (((buffer[bufpos - 2] - '0') << 6)
         1973                                      + ((buffer[bufpos - 1] - '0') << 3)
         1974                                      + (buffer[bufpos] - '0'));
         1975                               is_ungetc (tch, is);
         1976                               UNEMIT (tch);
         1977                               bufpos -= 3;
         1978                             }
         1979                           else
         1980                             /* Normal character. */
         1981                             goto unemit_normal;
         1982                         }
         1983                       else
         1984                         {
         1985                           /* Normal character, just unget it. */
         1986                         unemit_normal:
         1987                           is_ungetc (buffer[bufpos], is);
         1988                           UNEMIT (buffer[bufpos]);
         1989                         }
         1990                     }
         1991                   while (bufpos > w);
         1992                 }
         1993             }
         1994         }
         1995 
         1996       if (ch == nl)
         1997         {
         1998           if (line_end == LE_TRUNCATE)
         1999             {
         2000               if (do_print)
         2001                 num_truncated_lines++;
         2002               pending_token = tNEWLINE;
         2003             }
         2004           else
         2005             pending_token = tWRAPPED_NEWLINE;
         2006         }
         2007       else
         2008         pending_token = tEOF;
         2009     }
         2010 
         2011   APPEND_CHAR ('\0');
         2012   token->type = tSTRING;
         2013   token->u.str = (char *) buffer;
         2014   token->new_x = linepos;
         2015   token->new_col = col;
         2016 }
         2017 
         2018 
         2019 static void
         2020 dump_ps_page_header (char *fname, int empty)
         2021 {
         2022   char *dirc, *basec, *fdir, *ftail;
         2023   int got, i;
         2024   char *cp, *cp2;
         2025   char *cstr = "%%";
         2026   unsigned int nup_subpage;
         2027 
         2028   /* The N-up printing sub-page. */
         2029   nup_subpage = (total_pages - 1) % nup;
         2030 
         2031   /* Split fname into fdir and ftail. */
         2032   dirc = strdup(fname);
         2033   basec = strdup(fname);
         2034   fdir = dirname(dirc);
         2035   ftail = basename(basec);
         2036 
         2037   if (nup > 1)
         2038     {
         2039       /* N-up printing is active. */
         2040       cstr = "%";
         2041 
         2042       if (nup_subpage == 0)
         2043         {
         2044           /* This is a real page start. */
         2045 
         2046           switch (page_label)
         2047             {
         2048             case LABEL_SHORT:
         2049               OUTPUT ((cofp, "%%%%Page: (%d-%d) %d\n", current_pagenum,
         2050                        current_pagenum + nup - 1, total_pages / nup + 1));
         2051               break;
         2052 
         2053             case LABEL_LONG:
         2054               OUTPUT ((cofp, "%%%%Page: (%s:%3d-%3d) %d\n", ftail,
         2055                        current_pagenum, current_pagenum + nup - 1,
         2056                        total_pages / nup + 1));
         2057               break;
         2058             }
         2059 
         2060           /* Page setup. */
         2061           OUTPUT ((cofp, "%%%%BeginPageSetup\n_S\n"));
         2062 
         2063           if ((total_pages / nup + 1) % 2 == 0)
         2064             /* Two-side binding options for the even pages. */
         2065             handle_two_side_options ();
         2066 
         2067 #define PRINT_BOUNDING_BOXES 0
         2068 
         2069 #if PRINT_BOUNDING_BOXES
         2070           OUTPUT ((cofp,
         2071                    "%d %d moveto %d %d lineto %d %d lineto %d %d lineto closepath stroke\n",
         2072                    media->llx, media->lly, media->llx, media->ury,
         2073                    media->urx, media->ury, media->urx, media->lly));
         2074 #endif
         2075 
         2076           if (landscape)
         2077             {
         2078               if (nup_landscape)
         2079                 OUTPUT ((cofp, "90 rotate\n%d %d translate\n",
         2080                          media->lly, -media->urx));
         2081               else
         2082                 OUTPUT ((cofp, "%d %d translate\n", media->llx, media->lly));
         2083             }
         2084           else
         2085             {
         2086               if (nup_landscape)
         2087                 OUTPUT ((cofp, "90 rotate\n%d %d translate\n",
         2088                          media->lly, -media->llx));
         2089               else
         2090                 OUTPUT ((cofp, "%d %d translate\n", media->llx, media->ury));
         2091             }
         2092         }
         2093     }
         2094 
         2095   /* Page start comment. */
         2096   switch (page_label)
         2097     {
         2098     case LABEL_SHORT:
         2099       OUTPUT ((cofp, "%sPage: (%d) %d\n", cstr, current_pagenum, total_pages));
         2100       break;
         2101 
         2102     case LABEL_LONG:
         2103       OUTPUT ((cofp, "%sPage: (%s:%3d) %d\n", cstr, ftail, current_pagenum,
         2104                total_pages));
         2105       break;
         2106     }
         2107 
         2108   /*
         2109    * Page Setup.
         2110    */
         2111 
         2112   OUTPUT ((cofp, "%sBeginPageSetup\n_S\n", cstr));
         2113 
         2114   if (nup > 1)
         2115     {
         2116       int xm, ym;
         2117 
         2118       OUTPUT ((cofp, "%% N-up sub-page %d/%d\n", nup_subpage + 1, nup));
         2119       if (landscape)
         2120         {
         2121           if (nup_columnwise)
         2122             {
         2123               xm = nup_subpage % nup_columns;
         2124               ym = nup_subpage / nup_columns;
         2125             }
         2126           else
         2127             {
         2128               xm = nup_subpage / nup_rows;
         2129               ym = nup_subpage % nup_rows;
         2130             }
         2131 
         2132           OUTPUT ((cofp, "%d %d translate\n",
         2133                    xm * (nup_width + nup_xpad),
         2134                    ym * (nup_height + nup_ypad)));
         2135         }
         2136       else
         2137         {
         2138           if (nup_columnwise)
         2139             {
         2140               xm = nup_subpage / nup_rows;
         2141               ym = nup_subpage % nup_rows;
         2142             }
         2143           else
         2144             {
         2145               xm = nup_subpage % nup_columns;
         2146               ym = nup_subpage / nup_columns;
         2147             }
         2148 
         2149           OUTPUT ((cofp, "%d %d translate\n",
         2150                    xm * (nup_width + nup_xpad),
         2151                    -((int) (ym * (nup_height + nup_ypad) + nup_height))));
         2152         }
         2153       OUTPUT ((cofp, "%g dup scale\n", nup_scale));
         2154 
         2155       /* And finally, the real page setup. */
         2156       if (landscape)
         2157         OUTPUT ((cofp, "90 rotate\n%d %d translate\n", 0, -d_page_h));
         2158     }
         2159   else
         2160     {
         2161       /* No N-up printing. */
         2162 
         2163       if (total_pages % 2 == 0)
         2164         /* Two-side binding options for the even pages. */
         2165         handle_two_side_options ();
         2166 
         2167       if (landscape)
         2168         OUTPUT ((cofp, "90 rotate\n%d %d translate\n",
         2169                  media->lly, -media->urx));
         2170       else
         2171         OUTPUT ((cofp, "%d %d translate\n", media->llx, media->lly));
         2172     }
         2173 
         2174   /* Some constants etc. */
         2175   OUTPUT ((cofp, "/pagenum %d def\n", current_pagenum));
         2176 
         2177   cp = escape_string (fname);
         2178   OUTPUT ((cofp, "/fname (%s) def\n", cp));
         2179   xfree (cp);
         2180 
         2181   cp = escape_string (fdir);
         2182   OUTPUT ((cofp, "/fdir (%s) def\n", cp));
         2183   xfree (cp);
         2184   xfree (dirc);
         2185 
         2186   cp = escape_string (ftail);
         2187   OUTPUT ((cofp, "/ftail (%s) def\n", cp));
         2188   xfree (cp);
         2189   xfree (basec);
         2190 
         2191   /* Do we have a pending ^@font{} font? */
         2192   if (user_fontp)
         2193     {
         2194       if (encoding == default_Fencoding)
         2195         OUTPUT ((cofp, "/%s %g %g SUF\n", Fname, Fpt.w, Fpt.h));
         2196       else
         2197         /* This must be the case. */
         2198         OUTPUT ((cofp, "/%s %g %g SUF_PS\n", Fname, Fpt.w, Fpt.h));
         2199     }
         2200 
         2201   /* Dump user defined strings. */
         2202   if (count_key_value_set (user_strings) > 0)
         2203     {
         2204       OUTPUT ((cofp, "%% User defined strings:\n"));
         2205       for (got = strhash_get_first (user_strings, &cp, &i, (void **) &cp2);
         2206            got;
         2207            got = strhash_get_next (user_strings, &cp, &i, (void **) &cp2))
         2208         {
         2209           cp2 = format_user_string ("%Format", cp2);
         2210           OUTPUT ((cofp, "/%s (%s) def\n", cp, cp2));
         2211           xfree (cp2);
         2212         }
         2213     }
         2214 
         2215   /* User supplied header? */
         2216   if (page_header)
         2217     {
         2218       char *h_left;
         2219       char *h_center;
         2220       char *h_right = NULL;
         2221 
         2222       h_left = format_user_string ("page header", page_header);
         2223       h_center = strchr (h_left, '|');
         2224       if (h_center)
         2225         {
         2226           *h_center = '\0';
         2227           h_center++;
         2228 
         2229           h_right = strchr (h_center, '|');
         2230           if (h_right)
         2231             {
         2232               *h_right = '\0';
         2233               h_right++;
         2234             }
         2235         }
         2236 
         2237       OUTPUT ((cofp, "/user_header_p true def\n"));
         2238       OUTPUT ((cofp, "/user_header_left_str (%s) def\n", h_left));
         2239       OUTPUT ((cofp, "/user_header_center_str (%s) def\n",
         2240                h_center ? h_center : ""));
         2241       OUTPUT ((cofp, "/user_header_right_str (%s) def\n",
         2242                h_right ? h_right : ""));
         2243       xfree (h_left);
         2244     }
         2245   else
         2246     OUTPUT ((cofp, "/user_header_p false def\n"));
         2247 
         2248   /* User supplied footer? */
         2249   if (page_footer)
         2250     {
         2251       char *f_left;
         2252       char *f_center;
         2253       char *f_right = NULL;
         2254 
         2255       f_left = format_user_string ("page footer", page_footer);
         2256       f_center = strchr (f_left, '|');
         2257       if (f_center)
         2258         {
         2259           *f_center = '\0';
         2260           f_center++;
         2261 
         2262           f_right = strchr (f_center, '|');
         2263           if (f_right)
         2264             {
         2265               *f_right = '\0';
         2266               f_right++;
         2267             }
         2268         }
         2269 
         2270       OUTPUT ((cofp, "/user_footer_p true def\n"));
         2271       OUTPUT ((cofp, "/user_footer_left_str (%s) def\n", f_left));
         2272       OUTPUT ((cofp, "/user_footer_center_str (%s) def\n",
         2273                f_center ? f_center : ""));
         2274       OUTPUT ((cofp, "/user_footer_right_str (%s) def\n",
         2275                f_right ? f_right : ""));
         2276       xfree (f_left);
         2277     }
         2278   else
         2279     OUTPUT ((cofp, "/user_footer_p false def\n"));
         2280 
         2281   OUTPUT ((cofp, "%%%%EndPageSetup\n"));
         2282 
         2283   /*
         2284    * Mark standard page decorations.
         2285    */
         2286 
         2287   if (!empty)
         2288     {
         2289       /* Highlight bars. */
         2290       if (highlight_bars)
         2291         OUTPUT ((cofp, "%d %f %d %f highlight_bars\n", highlight_bars,
         2292                  LINESKIP, d_output_y_margin, highlight_bar_gray));
         2293 
         2294       /* Underlay. */
         2295       if (underlay != NULL)
         2296         {
         2297           if (ul_position_p || ul_angle_p)
         2298             OUTPUT ((cofp, "user_underlay\n"));
         2299           else
         2300             OUTPUT ((cofp, "underlay\n"));
         2301         }
         2302 
         2303       /* Column lines. */
         2304       if (num_columns > 1 && (header == HDR_FANCY || borders))
         2305         OUTPUT ((cofp, "column_lines\n"));
         2306 
         2307       /* Borders around columns. */
         2308       if (borders)
         2309         OUTPUT ((cofp, "column_borders\n"));
         2310 
         2311       /* Header. */
         2312       switch (header)
         2313         {
         2314         case HDR_NONE:
         2315           break;
         2316 
         2317         case HDR_SIMPLE:
         2318         case HDR_FANCY:
         2319           OUTPUT ((cofp, "do_header\n"));
         2320           break;
         2321         }
         2322     }
         2323 
         2324   /* Do we have a pending ^@color{} color? */
         2325   if (user_colorp)
         2326     OUTPUT ((cofp, "%g %g %g setrgbcolor\n", user_color.r, user_color.g,
         2327              user_color.b));
         2328 }
         2329 
         2330 
         2331 static void
         2332 dump_ps_page_trailer ()
         2333 {
         2334   unsigned int nup_subpage = (total_pages - 1) % nup;
         2335 
         2336   OUTPUT ((cofp, "_R\n"));
         2337 
         2338   if (nup > 1)
         2339     {
         2340       if (nup_subpage + 1 == nup)
         2341         /* Real end of page. */
         2342         OUTPUT ((cofp, "_R\nS\n"));
         2343     }
         2344   else
         2345     OUTPUT ((cofp, "S\n"));
         2346 }
         2347 
         2348 
         2349 static void
         2350 dump_empty_page ()
         2351 {
         2352   if (nup > 1)
         2353     {
         2354       unsigned int nup_subpage = (total_pages - 1) % nup;
         2355 
         2356       if (nup_subpage == 0)
         2357         {
         2358           /* Real start of the page, must do it the harder way. */
         2359           dump_ps_page_header ("", 1);
         2360           OUTPUT ((cofp, "_R\n"));
         2361         }
         2362       else
         2363         OUTPUT ((cofp, "%%Page: (-) %d\n", total_pages));
         2364 
         2365       if (nup_subpage + 1 == nup)
         2366         /* This is the last page on this sheet, dump us. */
         2367         OUTPUT ((cofp, "_R\nS\n"));
         2368     }
         2369   else
         2370     OUTPUT ((cofp, "%%%%Page: (-) %d\nS\n", total_pages));
         2371 }
         2372 
         2373 
         2374 static int
         2375 recognize_eps_file (Token *token)
         2376 {
         2377   int i;
         2378   char buf[4096];
         2379   char *filename;
         2380   int line;
         2381   int valid_epsf;
         2382   float llx, lly, urx, ury;
         2383 
         2384   MESSAGE (2, (stderr, "^@epsf=\"%s\"\n", token->u.epsf.filename));
         2385 
         2386   i = strlen (token->u.epsf.filename);
         2387 
         2388   /* Read EPS data from file. */
         2389   filename = tilde_subst (token->u.epsf.filename);
         2390 
         2391   token->u.epsf.fp = fopen (filename, "rb");
         2392   xfree (filename);
         2393 
         2394   if (token->u.epsf.fp == NULL)
         2395     {
         2396       if (token->u.epsf.filename[0] != '/')
         2397         {
         2398           /* Name is not absolute, let's lookup path. */
         2399           FileLookupCtx ctx;
         2400 
         2401           ctx.name = token->u.epsf.filename;
         2402           ctx.suffix = "";
         2403           ctx.fullname = buffer_alloc ();
         2404 
         2405           if (pathwalk (libpath, file_lookup, &ctx))
         2406             token->u.epsf.fp = fopen (buffer_ptr (ctx.fullname), "rb");
         2407 
         2408           buffer_free (ctx.fullname);
         2409         }
         2410       if (token->u.epsf.fp == NULL)
         2411         {
         2412           MESSAGE (0, (stderr, _("couldn't open EPS file \"%s\": %s\n"),
         2413                        token->u.epsf.filename, strerror (errno)));
         2414           return 0;
         2415         }
         2416     }
         2417 
         2418   /* Find BoundingBox DSC comment. */
         2419 
         2420   line = 0;
         2421   valid_epsf = 0;
         2422   token->u.epsf.skipbuf = NULL;
         2423   token->u.epsf.skipbuf_len = 0;
         2424   token->u.epsf.skipbuf_pos = 0;
         2425 
         2426   while (fgets (buf, sizeof (buf), token->u.epsf.fp))
         2427     {
         2428       line++;
         2429 
         2430       /* Append data to the skip buffer. */
         2431       i = strlen (buf);
         2432       if (i + token->u.epsf.skipbuf_pos >= token->u.epsf.skipbuf_len)
         2433         {
         2434           token->u.epsf.skipbuf_len += 8192;
         2435           token->u.epsf.skipbuf = xrealloc (token->u.epsf.skipbuf,
         2436                                             token->u.epsf.skipbuf_len);
         2437         }
         2438       memcpy (token->u.epsf.skipbuf + token->u.epsf.skipbuf_pos, buf, i);
         2439       token->u.epsf.skipbuf_pos += i;
         2440 
         2441       /* Check the "%!" magic cookie. */
         2442       if (line == 1)
         2443         {
         2444           if (buf[0] != '%' || buf[1] != '!')
         2445             {
         2446               MESSAGE (0,
         2447                        (stderr,
         2448                         _("EPS file \"%s\" does not start with \"%%!\" magic\n"),
         2449                         token->u.epsf.filename));
         2450               break;
         2451             }
         2452         }
         2453 
         2454 #define BB_DSC "%%BoundingBox:"
         2455 
         2456       if (strncmp (buf, BB_DSC, strlen (BB_DSC)) == 0)
         2457         {
         2458           i = sscanf (buf + strlen (BB_DSC), "%f %f %f %f",
         2459                       &llx, &lly, &urx, &ury);
         2460           if (i != 4)
         2461             {
         2462               /* (atend) ? */
         2463 
         2464               /* Skip possible whitespace. */
         2465               for (i = strlen (BB_DSC);
         2466                    buf[i] && (buf[i] == ' ' || buf[i] == '\t');
         2467                    i++)
         2468                 ;
         2469 #define BB_DSC_ATEND "(atend)"
         2470               if (strncmp (buf + i, BB_DSC_ATEND, strlen (BB_DSC_ATEND)) != 0)
         2471                 {
         2472                   /* No, this BoundingBox comment is corrupted. */
         2473                   MESSAGE (0, (stderr, _("EPS file \"%s\" contains malformed \
         2474 %%%%BoundingBox row:\n\"%.*s\"\n"),
         2475                                token->u.epsf.filename, (int)(strlen (buf) - 1), buf));
         2476                   break;
         2477                 }
         2478             }
         2479           else
         2480             {
         2481               /* It was a valid EPS file. */
         2482 
         2483               /* We store bounding box in int format. */
         2484               token->u.epsf.llx = llx;
         2485               token->u.epsf.lly = lly;
         2486               token->u.epsf.urx = urx;
         2487               token->u.epsf.ury = ury;
         2488 
         2489               valid_epsf = 1;
         2490               break;
         2491             }
         2492         }
         2493     }
         2494 
         2495   /* Check that we found the BoundingBox comment. */
         2496   if (!valid_epsf)
         2497     {
         2498       MESSAGE (0, (stderr, _("EPS file \"%s\" is not a valid EPS file\n"),
         2499                    token->u.epsf.filename));
         2500       if (token->u.epsf.pipe)
         2501         pclose (token->u.epsf.fp);
         2502       else
         2503         fclose (token->u.epsf.fp);
         2504       xfree (token->u.epsf.skipbuf);
         2505       return 0;
         2506     }
         2507 
         2508   MESSAGE (2, (stderr, "BoundingBox: %d %d %d %d\n",
         2509                token->u.epsf.llx, token->u.epsf.lly,
         2510                token->u.epsf.urx, token->u.epsf.ury));
         2511 
         2512   return 1;
         2513 }
         2514 
         2515 
         2516 static void
         2517 paste_epsf (Token *token)
         2518 {
         2519   char buf[4096];
         2520   int i;
         2521 
         2522   /* EPSF import header. */
         2523   OUTPUT ((cofp, "BeginEPSF\n"));
         2524   OUTPUT ((cofp, "%g %g translate\n", token->new_x, token->new_y));
         2525   OUTPUT ((cofp, "%g %g scale\n", token->u.epsf.xscale, token->u.epsf.yscale));
         2526   OUTPUT ((cofp, "%d %d translate\n", -token->u.epsf.llx,
         2527            -token->u.epsf.lly));
         2528   OUTPUT ((cofp, "%d %d %d %d Box clip newpath\n",
         2529            token->u.epsf.llx - 1,
         2530            token->u.epsf.lly - 1,
         2531            token->u.epsf.urx - token->u.epsf.llx + 2,
         2532            token->u.epsf.ury - token->u.epsf.lly + 2));
         2533   OUTPUT ((cofp, "%%%%BeginDocument: %s%s\n", token->u.epsf.filename,
         2534            token->u.epsf.pipe ? "|" : ""));
         2535 
         2536   if (do_print)
         2537     {
         2538       /* Dump skip buffer. */
         2539       fwrite (token->u.epsf.skipbuf, 1, token->u.epsf.skipbuf_pos, cofp);
         2540 
         2541       /* Dump file. */
         2542       while ((i = fread (buf, 1, sizeof (buf), token->u.epsf.fp)) != 0)
         2543         fwrite (buf, 1, i, cofp);
         2544     }
         2545 
         2546   /* Add a newline to keep comments correct */
         2547   OUTPUT ((cofp, "\n"));
         2548 
         2549   /* EPSF import trailer. */
         2550   OUTPUT ((cofp, "%%%%EndDocument\nEndEPSF\n"));
         2551 
         2552   /* Cleanup. */
         2553   if (token->u.epsf.pipe)
         2554     pclose (token->u.epsf.fp);
         2555   else
         2556     fclose (token->u.epsf.fp);
         2557   xfree (token->u.epsf.skipbuf);
         2558 }
         2559 
         2560 
         2561 static double
         2562 read_float (InputStream *is, int units, int horizontal)
         2563 {
         2564   char buf[256];
         2565   int i, ch;
         2566   double val;
         2567 
         2568   for (i = 0; (i < sizeof (buf) - 1
         2569                && (ch = is_getc (is)) != EOF
         2570                && ISNUMBERDIGIT (ch));
         2571        i++)
         2572     buf[i] = ch;
         2573   buf[i] = '\0';
         2574   if (ch != EOF)
         2575     is_ungetc (ch, is);
         2576 
         2577   val = atof (buf);
         2578 
         2579   if (units)
         2580     {
         2581       /* Get unit. */
         2582       ch = is_getc (is);
         2583       switch (ch)
         2584         {
         2585         case 'c':                /* centimeters */
         2586           val *= 72 / 2.54;
         2587           break;
         2588 
         2589         case 'p':                /* PostScript points */
         2590           break;
         2591 
         2592         case 'i':                /* inches */
         2593           val *= 72;
         2594           break;
         2595 
         2596         default:
         2597           is_ungetc (ch, is);
         2598           /* FALLTHROUGH */
         2599 
         2600         case 'l':                /* lines or characters */
         2601           if (horizontal)
         2602             val *= FNT_CHAR_WIDTH ('m');
         2603           else
         2604             val *= LINESKIP;
         2605           break;
         2606         }
         2607     }
         2608 
         2609   return val;
         2610 }
         2611 
         2612 
         2613 /* Magics used to recognize different pass-through files. */
         2614 static struct
         2615 {
         2616   char *magic;
         2617   unsigned int magiclen;
         2618   char *name;
         2619   int revert_delta;
         2620 } pass_through_magics[] =
         2621   {
         2622     {"%!",         2, "PostScript",         -2},
         2623     {"\004%!",        3, "PostScript",         -2},
         2624     {"\033E",        2, "PCL",                -2},
         2625     {"\033%",        2, "PCL",                -2},
         2626     {NULL, 0, NULL, 0},
         2627   };
         2628 
         2629 
         2630 static int
         2631 do_pass_through (char *fname, InputStream *is)
         2632 {
         2633   int ch;
         2634   unsigned long saved_pos = is->bufpos;
         2635   int i, j;
         2636 
         2637   if (output_language_pass_through)
         2638     MESSAGE (1,
         2639              (stderr,
         2640               _("passing through all input files for output language `%s'\n"),
         2641               output_language));
         2642   else
         2643     {
         2644       /*
         2645        * Try to recognize pass-through files.
         2646        */
         2647 
         2648       for (i = 0; pass_through_magics[i].magic; i++)
         2649         {
         2650           for (j = 0; j < pass_through_magics[i].magiclen; j++)
         2651             {
         2652               ch = is_getc (is);
         2653               if (ch == EOF
         2654                   || ch != (unsigned char) pass_through_magics[i].magic[j])
         2655                 break;
         2656             }
         2657 
         2658           if (j >= pass_through_magics[i].magiclen)
         2659             /* The <i>th one matched. */
         2660             break;
         2661 
         2662           /*
         2663            * Try the next one, but first, seek the input stream to its
         2664            * start.
         2665            */
         2666           is->bufpos = saved_pos;
         2667         }
         2668 
         2669       /* Did we find any? */
         2670       if (pass_through_magics[i].magic == NULL)
         2671         /* No we didn't. */
         2672         return 0;
         2673 
         2674       /* Yes, it really is a pass-through file.  Now do the pass through. */
         2675 
         2676       is->bufpos += pass_through_magics[i].revert_delta;
         2677 
         2678       if (ps_header_dumped)
         2679         {
         2680           /* A pass-through file between normal ASCII files, obey DSC. */
         2681 
         2682           /*
         2683            * XXX I don't know how to handle PCL files... Let's hope none
         2684            * mixes them with the normal ASCII files.
         2685            */
         2686 
         2687           OUTPUT ((cofp,
         2688                    "%%%%Page: (%s) -1\n_S\n%%%%BeginDocument: %s\n",
         2689                    fname, fname));
         2690         }
         2691 
         2692       MESSAGE (1, (stderr, _("passing through %s file \"%s\"\n"),
         2693                    pass_through_magics[i].name, fname));
         2694     }
         2695 
         2696   /* And now, do the actual pass-through. */
         2697   do
         2698     {
         2699       /* Note: this will be written directly to the <ofp>. */
         2700       fwrite (is->buf + is->bufpos, 1, is->data_in_buf - is->bufpos, ofp);
         2701       is->bufpos = is->data_in_buf;
         2702 
         2703       /* Read more data to the input buffer. */
         2704       ch = is_getc (is);
         2705       is->bufpos = 0;
         2706     }
         2707   while (ch != EOF);
         2708 
         2709   if (!output_language_pass_through)
         2710     {
         2711       if (ps_header_dumped)
         2712         /*
         2713          * XXX How to end a PCL file mixed between ASCII files?
         2714          */
         2715         OUTPUT ((cofp, "%%%%EndDocument\n_R\n"));
         2716     }
         2717 
         2718   return 1;
         2719 }
         2720 
         2721 
         2722 static void
         2723 print_line_number (double x, double y, double space, double margin,
         2724                    unsigned int linenum)
         2725 {
         2726   double len = 0.0;
         2727   char buf[20];
         2728   int i;
         2729   char *saved_Fname = "";
         2730   FontPoint saved_Fpt;
         2731   InputEncoding saved_Fencoding = 0;
         2732 
         2733   saved_Fpt.w = 0.0;
         2734   saved_Fpt.h = 0.0;
         2735 
         2736   /* Do not print linenumbers for wrapped lines. */
         2737   if (linenum == print_line_number_last)
         2738     return;
         2739   print_line_number_last = linenum;
         2740 
         2741   if (user_fontp)
         2742     {
         2743       /* Re-select our default typing font. */
         2744       saved_Fname = Fname;
         2745       saved_Fpt.w = Fpt.w;
         2746       saved_Fpt.h = Fpt.h;
         2747       saved_Fencoding = encoding;
         2748 
         2749       Fname = default_Fname;
         2750       Fpt.w = default_Fpt.w;
         2751       Fpt.h = default_Fpt.h;
         2752       encoding = default_Fencoding;
         2753 
         2754       OUTPUT ((cofp, "/F-gs-font %g %g SF\n", Fpt.w, Fpt.h));
         2755       read_font_info ();
         2756     }
         2757 
         2758   /* Count linenumber string length. */
         2759   sprintf (buf, "%d", linenum);
         2760   for (i = 0; buf[i]; i++)
         2761     len += FNT_CHAR_WIDTH (buf[i]);
         2762 
         2763   /* Print line numbers. */
         2764   OUTPUT ((cofp, "%g %g M (%s:) s\n", x + space - len, y, buf));
         2765 
         2766   if (user_fontp)
         2767     {
         2768       /* Switch back to the user font. */
         2769       Fname = saved_Fname;
         2770       Fpt.w = saved_Fpt.w;
         2771       Fpt.h = saved_Fpt.h;
         2772       encoding = saved_Fencoding;
         2773 
         2774       OUTPUT ((cofp, "/%s %g %g SUF\n", Fname, Fpt.w, Fpt.h));
         2775       read_font_info ();
         2776     }
         2777 }
         2778 
         2779 
         2780 /*
         2781  * The name of the divert file, shared between divert() and undivert()
         2782  * functions.
         2783  */
         2784 static void
         2785 divert ()
         2786 {
         2787   assert (divertfp == NULL);
         2788 
         2789   /* Open divert file. */
         2790 
         2791   divertfp = tmpfile ();
         2792   if (divertfp == NULL)
         2793     FATAL ((stderr, _("couldn't create temporary divert file: %s"),
         2794             strerror (errno)));
         2795 
         2796   cofp = divertfp;
         2797 }
         2798 
         2799 
         2800 static void
         2801 undivert ()
         2802 {
         2803   char buf[1024];
         2804   int doc_level = 0;
         2805   char *cp;
         2806 
         2807   assert (divertfp != NULL);
         2808 
         2809   if (fseek (divertfp, 0, SEEK_SET) != 0)
         2810     FATAL ((stderr, _("couldn't rewind divert file: %s"), strerror (errno)));
         2811 
         2812   while (fgets (buf, sizeof (buf), divertfp))
         2813     {
         2814       if (strncmp (buf, "%%BeginDocument", 15) == 0)
         2815         doc_level++;
         2816       else if (strncmp (buf, "%%EndDocument", 13) == 0)
         2817         doc_level--;
         2818 
         2819       if (doc_level == 0)
         2820         {
         2821           if (strncmp (buf, "% User defined strings", 22) == 0)
         2822             {
         2823               fputs (buf, ofp);
         2824               while (fgets (buf, sizeof (buf), divertfp))
         2825                 {
         2826                   if (strncmp (buf, "%%EndPageSetup", 14) == 0)
         2827                     break;
         2828 
         2829                   /* Patch total pages to the user defined strings. */
         2830                   cp = strchr (buf, '\001');
         2831                   if (cp)
         2832                     {
         2833                       *cp = '\0';
         2834                       fputs (buf, ofp);
         2835                       fprintf (ofp, "%d", total_pages_in_file);
         2836                       fputs (cp + 1, ofp);
         2837                     }
         2838                   else
         2839                     fputs (buf, ofp);
         2840                 }
         2841             }
         2842         }
         2843 
         2844       fputs (buf, ofp);
         2845     }
         2846 
         2847   fclose (divertfp);
         2848   divertfp = NULL;
         2849 
         2850   cofp = ofp;
         2851 }
         2852 
         2853 
         2854 static void
         2855 handle_two_side_options ()
         2856 {
         2857   if (rotate_even_pages)
         2858     /* Rotate page 180 degrees. */
         2859     OUTPUT ((cofp, "180 rotate\n%d %d translate\n",
         2860              -media->w, -media->h));
         2861 
         2862   if (swap_even_page_margins)
         2863     OUTPUT ((cofp, "%d 0 translate\n",
         2864              -(media->llx - (media->w - media->urx))));
         2865 }