Remove code to read EPS data from a pipe.
[enscript.git] / src / psgen.c
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 "gsint.h"
26
27 /*
28  * Types and definitions.
29  */
30
31 /* Values for token flags. */
32
33 /* EPSF. */
34 #define F_EPSF_CENTER                   0x01
35 #define F_EPSF_RIGHT                    0x02
36 #define M_EPSF_JUSTIFICATION            0x03
37
38 #define F_EPSF_NO_CPOINT_UPDATE_X       0x04
39 #define F_EPSF_NO_CPOINT_UPDATE_Y       0x08
40
41 #define F_EPSF_ABSOLUTE_X               0x10
42 #define F_EPSF_ABSOLUTE_Y               0x20
43
44 #define F_EPSF_SCALE_X                  0x40
45 #define F_EPSF_SCALE_Y                  0x80
46
47
48 /* Predicate to check if we are at the correct slice. */
49 #define CORRECT_SLICE() (slicing == 0 || current_slice == slice)
50
51 /* Predicates for the current body font. */
52
53 /* Is character <ch> printable. */
54 #define ISPRINT(ch) (font_ctype[(unsigned char) (ch)] != ' ')
55
56 /* Does character <ch> exist in current body font? */
57 #define EXISTS(ch) (font_ctype[(unsigned char) (ch)] == '*')
58
59
60 #define RESOURCE_LINE_WIDTH 75
61
62 /* Token types. */
63 typedef enum
64 {
65   tNONE,
66   tEOF,
67   tSTRING,
68   tFORMFEED,
69   tNEWLINE,
70   tCARRIAGE_RETURN,
71   tWRAPPED_NEWLINE,
72   tEPSF,
73   tSETFILENAME,
74   tSETPAGENUMBER,
75   tNEWPAGE,
76   tFONT,
77   tCOLOR,
78   tBGCOLOR,
79   tSAVEX,
80   tLOADX,
81   tPS
82 } TokenType;
83
84 /* Special escape tokens. */
85 typedef enum
86 {
87   ESC_COMMENT,
88   ESC_EPSF,
89   ESC_FONT,
90   ESC_COLOR,
91   ESC_BGCOLOR,
92   ESC_NEWPAGE,
93   ESC_SETFILENAME,
94   ESC_SETPAGENUMBER,
95   ESC_SHADE,
96   ESC_BGGRAY,
97   ESC_ESCAPE,
98   ESC_SAVEX,
99   ESC_LOADX,
100   ESC_PS
101 } SpecialEscape;
102
103 /* Token structure. */
104 struct gs_token_st
105 {
106   TokenType type;
107   unsigned int flags;
108   double new_x;                 /* Current point x after this token. */
109   double new_y;                 /* Current point y after this token. */
110   int new_col;                  /* Line column after this token. */
111
112   union
113     {
114       int i;
115       char *str;
116       struct
117         {
118           double x;             /* x-offset */
119           double y;             /* y-offset */
120           double w;             /* width */
121           double h;             /* height */
122           double xscale;
123           double yscale;
124           int llx, lly, urx, ury; /* Bounding box. */
125           char filename[512];
126           char *skipbuf;
127           unsigned int skipbuf_len;
128           unsigned int skipbuf_pos;
129           FILE *fp;             /* File from which eps image is read. */
130           int pipe;             /* Is <fp> opened to pipe?  */
131         } epsf;
132       Color color;
133       Color bgcolor;
134       struct
135         {
136           char name[512];
137           FontPoint size;
138           InputEncoding encoding;
139         } font;
140       char filename[512];
141     } u;
142 };
143
144 typedef struct gs_token_st Token;
145
146
147 /*
148  * Prototypes for static functions.
149  */
150
151 static void get_next_token ___P ((InputStream *is, double linestart,
152                                   double linepos, unsigned int col,
153                                   double linew, Token *token));
154
155 static void dump_ps_page_header ___P ((char *fname, int empty));
156
157 static void dump_ps_page_trailer ();
158
159 static void dump_empty_page ();
160
161 /*
162  * Recognize a EPS file described by <token>.  Returns 1 if file was a
163  * valid EPS file or 0 otherwise.  File is accepted if it starts with
164  * the PostScript magic `%!' and it has a valid `%%BoundingBox' DSC
165  * comment.
166  */
167 static int recognize_eps_file ___P ((Token *token));
168
169 /*
170  * Insert EPS file described by <token> to the output stream.
171  */
172 static void paste_epsf ___P ((Token *token));
173
174 /*
175  * Check if InputStream <is> contains a file which can be passed
176  * through without any modifications.  Returns 1 if file was passed or
177  * 0 otherwise.
178  */
179 static int do_pass_through ___P ((char *fname, InputStream *is));
180
181 /*
182  * Read one float dimension from InputStream <is>.  If <units> is
183  * true, number can be followed by an optional unit specifier.  If
184  * <horizontal> is true, dimension is horizontal, otherwise it is
185  * vertical (this is used to find out how big `line' units are).
186  */
187 static double read_float ___P ((InputStream *is, int units, int horizontal));
188
189 /*
190  * Print linenumber <linenum> to the beginning of the current line.
191  * Current line start is specified by point (x, y).
192  */
193 static void print_line_number ___P ((double x, double y, double space,
194                                      double margin, unsigned int linenum));
195
196 /* Send PostScript to the output file. */
197 #define OUTPUT(body)    \
198   do {                  \
199     if (cofp == NULL)   \
200       cofp = ofp;       \
201     if (do_print)       \
202       fprintf body;     \
203   } while (0)
204
205 /* Divert output to tmp file so the total page count can be counted. */
206 static void divert ();
207
208 /* Paste diverted data to the output and patch the total page counts. */
209 static void undivert ();
210
211 /*
212  * Handle two-side printing related binding options.  This function is
213  * called once for each even-numbered page.
214  */
215 static void handle_two_side_options ();
216
217 /*
218  * Global variables.
219  */
220
221 unsigned int current_pagenum = 0; /* The number of the current page. */
222 unsigned int total_pages_in_file;
223 unsigned int input_filenum = 0;
224 unsigned int current_file_linenum;
225 int first_pagenum_for_file;
226 char *fname = NULL;             /* The name of the current input file. */
227
228
229 /*
230  * Static variables
231  */
232
233 /* Have we dumped PS header? */
234 static int ps_header_dumped = 0;
235
236 /* Divert file. */
237 static FILE *divertfp = NULL;
238
239 /* Current output() file. */
240 static FILE *cofp = NULL;
241
242 /* To print or not to print, that's a question. */
243 static int do_print = 1;
244
245 /* Is ^@font{}-defined font active? */
246 static int user_fontp = 0;
247
248 /* The user ^@font{}-defined font. */
249 static char user_font_name[256];
250 static FontPoint user_font_pt;
251 static InputEncoding user_font_encoding;
252
253 /* Is ^@color{}-defined color active? */
254 static int user_colorp = 0;
255
256 /* The user ^@color{}-defined color. */
257 static Color user_color;
258
259 /* Is ^@bgcolor{}-defined color active? */
260 static int user_bgcolorp = 0;
261
262 /* The user ^@bgcolor{}-defined color. */
263 static Color user_bgcolor;
264
265 /* The last linenumber printed by print_line_number(). */
266 static unsigned int print_line_number_last;
267
268 /* Registers to store X-coordinates with the ^@savex{} escape.
269    Initially these are uninitialized. */
270 static double xstore[256];
271
272 /*
273  * Global functions.
274  */
275
276 void
277 dump_ps_header ()
278 {
279   char *cp, *cp2;
280   int i, j, got;
281
282   /* Dump PS header only once. */
283   if (ps_header_dumped)
284     return;
285   ps_header_dumped = 1;
286
287   /*
288    * Header.
289    */
290
291   OUTPUT ((cofp, "%s\n", output_first_line));
292   OUTPUT ((cofp, "%%%%BoundingBox: %d %d %d %d\n", media->llx, media->lly,
293            media->urx, media->ury));
294   OUTPUT ((cofp, "%%%%Title: %s\n", title));
295   OUTPUT ((cofp, "%%%%For: %s\n", passwd->pw_gecos));
296   OUTPUT ((cofp, "%%%%Creator: %s\n", version_string));
297   OUTPUT ((cofp, "%%%%CreationDate: %s\n", date_string));
298   OUTPUT ((cofp, "%%%%Orientation: %s\n",
299            ((nup > 1) && nup_landscape)
300            || ((nup == 1) && landscape) ? "Landscape" : "Portrait"));
301   OUTPUT ((cofp, "%%%%Pages: (atend)\n"));
302   OUTPUT ((cofp, "%%%%DocumentMedia: %s %d %d 0 () ()\n",
303            media->name, media->w, media->h));
304   OUTPUT ((cofp, "%%%%DocumentNeededResources: (atend)\n"));
305
306   if (count_key_value_set (pagedevice) > 0)
307     OUTPUT ((cofp, "%%%%LanguageLevel: 2\n"));
308
309   OUTPUT ((cofp, "%%%%EndComments\n"));
310
311
312   /*
313    * Procedure Definitions.
314    */
315
316   OUTPUT ((cofp, "%%%%BeginProlog\n"));
317
318   /* Prolog. */
319   OUTPUT ((cofp, "%%%%BeginResource: procset Enscript-Prolog %s\n",
320            ps_version_string));
321   if (!paste_file ("enscript", ".pro"))
322     FATAL ((stderr, _("couldn't find prolog \"%s\": %s\n"), "enscript.pro",
323             strerror (errno)));
324   OUTPUT ((cofp, "%%%%EndResource\n"));
325
326   /* Encoding vector. */
327   OUTPUT ((cofp, "%%%%BeginResource: procset Enscript-Encoding-%s %s\n",
328            encoding_name, ps_version_string));
329   if (!paste_file (encoding_name, ".enc"))
330     FATAL ((stderr, _("couldn't find encoding file \"%s.enc\": %s\n"),
331             encoding_name, strerror (errno)));
332   OUTPUT ((cofp, "%%%%EndResource\n"));
333
334   OUTPUT ((cofp, "%%%%EndProlog\n"));
335
336
337   /*
338    * Document Setup.
339    */
340
341   OUTPUT ((cofp, "%%%%BeginSetup\n"));
342
343   /* Download fonts. */
344   for (got = strhash_get_first (download_fonts, &cp, &j, (void **) &cp2); got;
345        got = strhash_get_next (download_fonts, &cp, &j, (void **) &cp2))
346     download_font (cp);
347
348   /* For each required font, emit %%IncludeResouce comment. */
349   for (got = strhash_get_first (res_fonts, &cp, &j, (void **) &cp2); got;
350        got = strhash_get_next (res_fonts, &cp, &j, (void **) &cp2))
351     OUTPUT ((cofp, "%%%%IncludeResource: font %s\n", cp));
352
353   OUTPUT ((cofp, "/HFpt_w %g def\n", HFpt.w));
354   OUTPUT ((cofp, "/HFpt_h %g def\n", HFpt.h));
355
356
357   /* Select our fonts. */
358
359   /* Header font HF. */
360   OUTPUT ((cofp, "/%s /HF-gs-font MF\n", HFname));
361   OUTPUT ((cofp,
362            "/HF /HF-gs-font findfont [HFpt_w 0 0 HFpt_h 0 0] makefont def\n"));
363
364   /* Our default typing font F. */
365   OUTPUT ((cofp, "/%s /F-gs-font MF\n", Fname));
366   OUTPUT ((cofp, "/F-gs-font %g %g SF\n", Fpt.w, Fpt.h));
367
368   /* Underlay. */
369   if (underlay != NULL)
370     {
371       OUTPUT ((cofp, "/ul_str (%s) def\n", underlay));
372       OUTPUT ((cofp, "/ul_w_ptsize %g def\n", ul_ptsize.w));
373       OUTPUT ((cofp, "/ul_h_ptsize %g def\n", ul_ptsize.h));
374       OUTPUT ((cofp, "/ul_gray %g def\n", ul_gray));
375       OUTPUT ((cofp, "/ul_x %g def\n", ul_x));
376       OUTPUT ((cofp, "/ul_y %g def\n", ul_y));
377       OUTPUT ((cofp, "/ul_angle %g def\n", ul_angle));
378       OUTPUT ((cofp, "/ul_style %d def\n", ul_style));
379       OUTPUT ((cofp, "/%s /F-ul-font MF\n", ul_font));
380       OUTPUT ((cofp, "/ul_font /F-ul-font findfont \
381 [ul_w_ptsize 0 0 ul_h_ptsize 0 0] makefont def\n"));
382     }
383
384   /* Number of copies. */
385   OUTPUT ((cofp, "/#copies %d def\n", num_copies));
386
387   /* Page prefeed. */
388   if (page_prefeed)
389     OUTPUT ((cofp, "true page_prefeed\n"));
390
391   /* Statusdict definitions. */
392   if (count_key_value_set (statusdict) > 0)
393     {
394       OUTPUT ((cofp, "%% Statustdict definitions:\nstatusdict begin\n  "));
395       i = 2;
396       for (got = strhash_get_first (statusdict, &cp, &j, (void **) &cp2); got;
397            got = strhash_get_next (statusdict, &cp, &j, (void **) &cp2))
398         {
399           j = strlen (cp) + 1 + strlen (cp2) + 1;
400           if (i + j > RESOURCE_LINE_WIDTH)
401             {
402               OUTPUT ((cofp, "\n  "));
403               i = 2;
404             }
405           OUTPUT ((cofp, "%s %s ", cp2, cp));
406           i += j;
407         }
408       OUTPUT ((cofp, "\nend\n"));
409     }
410
411   /* Page device definitions. */
412   if (pslevel >= 2 &&
413       (count_key_value_set (pagedevice) > 0 || generate_PageSize))
414     {
415       OUTPUT ((cofp, "%% Pagedevice definitions:\n"));
416       OUTPUT ((cofp, "gs_languagelevel 1 gt {\n  <<\n    "));
417
418       i = 4;
419       for (got = strhash_get_first (pagedevice, &cp, &j, (void **) &cp2); got;
420            got = strhash_get_next (pagedevice, &cp, &j, (void **) &cp2))
421         {
422           j = strlen (cp2) + 1 + strlen (cp) + 2;
423           if (i + j > RESOURCE_LINE_WIDTH)
424             {
425               OUTPUT ((cofp, "\n    "));
426               i = 4;
427             }
428           OUTPUT ((cofp, "/%s %s ", cp, cp2));
429           i += j;
430         }
431
432       if (generate_PageSize)
433         {
434           if (i + 21 > RESOURCE_LINE_WIDTH)
435             {
436               OUTPUT ((cofp, "\n    "));
437               i = 4;
438             }
439           OUTPUT ((cofp, "/PageSize [%d %d] ", media->w, media->h));
440           i += 21;
441         }
442
443       OUTPUT ((cofp, "\n  >> setpagedevice\n} if\n"));
444     }
445
446   /*
447    * Dump header procset.  Header must come after all font inclusions
448    * and enscript's dynamic state definition.
449    */
450   if (header != HDR_NONE)
451     {
452       char *hdr;
453       if (header == HDR_SIMPLE)
454         hdr = "simple";
455       else
456         hdr = fancy_header_name;
457
458       OUTPUT ((cofp, "%%%%BeginResource: procset Enscript-Header-%s %s\n",
459                hdr, ps_version_string));
460       if (!paste_file (hdr, ".hdr"))
461         FATAL ((stderr,
462                 _("couldn't find header definition file \"%s.hdr\": %s\n"),
463                 hdr, strerror (errno)));
464       OUTPUT ((cofp, "%%%%EndResource\n"));
465     }
466
467   /*
468    * Count output width and height here; we can't do it earlier because
469    * header might have just allocated some extra space.
470    */
471   d_output_w = d_page_w;
472   d_output_h = d_page_h - d_header_h - d_footer_h;
473
474   /* Dump our current dynamic state. */
475   OUTPUT ((cofp, "/d_page_w %d def\n", d_page_w));
476   OUTPUT ((cofp, "/d_page_h %d def\n", d_page_h));
477
478   OUTPUT ((cofp, "/d_header_x %d def\n", 0));
479   OUTPUT ((cofp, "/d_header_y %d def\n", d_output_h + d_footer_h));
480   OUTPUT ((cofp, "/d_header_w %d def\n", d_header_w));
481   OUTPUT ((cofp, "/d_header_h %d def\n", d_header_h));
482
483   OUTPUT ((cofp, "/d_footer_x %d def\n", 0));
484   OUTPUT ((cofp, "/d_footer_y %d def\n", 0));
485   OUTPUT ((cofp, "/d_footer_w %d def\n", d_header_w));
486   OUTPUT ((cofp, "/d_footer_h %d def\n", d_footer_h));
487
488   OUTPUT ((cofp, "/d_output_w %d def\n", d_output_w));
489   OUTPUT ((cofp, "/d_output_h %d def\n", d_output_h));
490   OUTPUT ((cofp, "/cols %d def\n", num_columns));
491
492   OUTPUT ((cofp, "%%%%EndSetup\n"));
493 }
494
495
496 void
497 dump_ps_trailer ()
498 {
499   int i, j, got;
500   char *cp;
501   void *value;
502   unsigned int nup_subpage;
503
504   if (!ps_header_dumped)
505     /* No header, let's be consistent and forget trailer also. */
506     return;
507
508   /* The possible pending N-up showpage. */
509   nup_subpage = (total_pages - 1) % nup;
510   if (nup > 1 && nup_subpage + 1 != nup)
511     /* N-up showpage missing. */
512     OUTPUT ((cofp, "_R\nS\n"));
513
514   /* Trailer. */
515
516   OUTPUT ((cofp, "%%%%Trailer\n"));
517
518   if (page_prefeed)
519     OUTPUT ((cofp, "false page_prefeed\n"));
520
521   OUTPUT ((cofp, "%%%%Pages: %d\n", total_pages));
522
523   /* Document needed resources. */
524
525   /* fonts. */
526   OUTPUT ((cofp, "%%%%DocumentNeededResources: font "));
527   i = 32;                       /* length of the previous string. */
528   for (got = strhash_get_first (res_fonts, &cp, &j, &value); got;
529        got = strhash_get_next (res_fonts, &cp, &j, &value))
530     {
531       if (i + strlen (cp) + 1 > RESOURCE_LINE_WIDTH)
532         {
533           OUTPUT ((cofp, "\n%%%%+ font "));
534           i = 9;                /* length of the previous string. */
535         }
536       OUTPUT ((cofp, "%s ", cp));
537       i += strlen (cp) + 1;
538     }
539   OUTPUT ((cofp, "\n%%%%EOF\n"));
540 }
541
542
543 void
544 process_file (char *fname_arg, InputStream *is, int is_toc)
545 {
546   int col;
547   double x, y;
548   double lx, ly;
549   double linewidth;             /* Line width in points. */
550   double lineend;
551   int done = 0;
552   int page_clear = 1;
553   unsigned int line_column;
554   unsigned int current_linenum;
555   double linenumber_space = 0;
556   double linenumber_margin = 0;
557   Token token;
558   int reuse_last_token = 0;
559   unsigned int current_slice = 1;
560   int last_wrapped_line = -1;
561   int last_spaced_file_linenum = -1;
562   int save_current_pagenum;
563   int toc_pagenum = 0;
564
565   /* Save filename. */
566   xfree (fname);
567   fname = xstrdup (fname_arg);
568
569   /* Init page number and line counters. */
570   if (!continuous_page_numbers)
571     current_pagenum = 0;
572   total_pages_in_file = 0;
573   current_file_linenum = start_line_number;
574
575   /*
576    * Count possible line number spaces.  This should be enought for 99999
577    * lines
578    */
579   linenumber_space = CHAR_WIDTH ('0') * 5 + 1.0;
580   linenumber_margin = CHAR_WIDTH (':') + CHAR_WIDTH ('m');
581
582   /* We got a new input file. */
583   input_filenum++;
584
585   /* We haven't printed any line numbers yet. */
586   print_line_number_last = (unsigned int) -1;
587
588   if (pass_through || output_language_pass_through)
589     if (do_pass_through (fname, is))
590       /* All done. */
591       return;
592
593   /* We have work to do, let's give header a chance to dump itself. */
594   dump_ps_header ();
595
596   /*
597    * Align files to the file_align boundary, this is handy for two-side
598    * printing.
599    */
600   while ((total_pages % file_align) != 0)
601     {
602       total_pages++;
603       dump_empty_page ();
604     }
605
606   MESSAGE (1, (stderr, _("processing file \"%s\"...\n"), fname));
607
608   linewidth = d_output_w / num_columns - 2 * d_output_x_margin
609     - line_indent;
610
611   /* Save the current running page number for possible toc usage. */
612   first_pagenum_for_file = total_pages + 1;
613
614   /*
615    * Divert our output to a temp file.  We will re-process it
616    * afterwards to patch, for example, the number of pages in the
617    * document.
618    */
619   divert ();
620
621   /* Process this input file. */
622   while (!done)
623     {
624       /* Start a new page. */
625       page_clear = 1;
626
627       for (col = 0; !done && col < num_columns; col++)
628         {
629           /* Move to the beginning of the column <col>. */
630           lx = x = col * d_output_w / (float) num_columns + d_output_x_margin
631             + line_indent;
632           lineend = lx + linewidth;
633
634           ly = y = d_footer_h + d_output_h - d_output_y_margin - LINESKIP;
635           current_linenum = 0;
636           line_column = 0;
637
638           while (1)
639             {
640               if (line_numbers && line_column == 0
641                   && (current_file_linenum != last_spaced_file_linenum))
642                 {
643                   /* Forward x by the amount needed by our line numbers. */
644                   x += linenumber_space + linenumber_margin;
645                   last_spaced_file_linenum = current_file_linenum;
646                 }
647
648               /* Get token. */
649               if (!reuse_last_token)
650                 get_next_token (is, lx, x, line_column, lineend, &token);
651               reuse_last_token = 0;
652
653               /*
654                * Page header printing is delayed to this point because
655                * we want to handle files ending with a newline character
656                * with care.  If the last newline would cause a pagebreak,
657                * otherwise we would print page header to the non-existent
658                * next page and that would be ugly ;)
659                */
660
661               if (token.type == tEOF)
662                 {
663                   done = 1;
664                   goto end_of_page;
665                 }
666
667               /*
668                * Now we know that we are going to make marks to this page
669                * => print page header.
670                */
671
672               if (page_clear)
673                 {
674                   PageRange *pr;
675
676                   current_pagenum++;
677                   total_pages_in_file++;
678
679                   /* Check page ranges. */
680                   if (page_ranges == NULL)
681                     do_print = 1;
682                   else
683                     {
684                       do_print = 0;
685                       for (pr = page_ranges; pr; pr = pr->next)
686                         {
687                           if (pr->odd || pr->even)
688                             {
689                               if ((pr->odd && (current_pagenum % 2) == 1)
690                                   || (pr->even && (current_pagenum % 2) == 0))
691                                 {
692                                   do_print = 1;
693                                   break;
694                                 }
695                             }
696                           else
697                             {
698                               if (pr->start <= current_pagenum
699                                   && current_pagenum <= pr->end)
700                                 {
701                                   do_print = 1;
702                                   break;
703                                 }
704                             }
705                         }
706                     }
707
708                   if (do_print)
709                     total_pages++;
710
711                   if (is_toc)
712                     {
713                       save_current_pagenum = current_pagenum;
714                       toc_pagenum--;
715                       current_pagenum = toc_pagenum;
716                     }
717
718                   dump_ps_page_header (fname, 0);
719                   page_clear = 0;
720
721                   if (is_toc)
722                     current_pagenum = save_current_pagenum;
723                 }
724
725               /* Print line highlight. */
726               if (line_column == 0 && line_highlight_gray < 1.0)
727                 OUTPUT ((cofp, "%g %g %g %g %g line_highlight\n",
728                          lx, (y - baselineskip
729                               + (font_bbox_lly * Fpt.h / UNITS_PER_POINT)),
730                          linewidth, Fpt.h + baselineskip,
731                          line_highlight_gray));
732
733               /* Print line numbers if needed. */
734               if (line_numbers && line_column == 0 && token.type != tFORMFEED)
735                 print_line_number (lx, y, linenumber_space, linenumber_margin,
736                                    current_file_linenum);
737
738               /* Check rest of tokens. */
739               switch (token.type)
740                 {
741                 case tFORMFEED:
742                   switch (formfeed_type)
743                     {
744                     case FORMFEED_COLUMN:
745                       goto end_of_column;
746                       break;
747
748                     case FORMFEED_PAGE:
749                       goto end_of_page;
750                       break;
751
752                     case FORMFEED_HCOLUMN:
753                       /*
754                        * Advance y-coordinate to the next even
755                        * `horizontal_column_height' position.
756                        */
757                       {
758                         int current_row;
759
760                         current_row = (ly - y) / horizontal_column_height;
761                         y = ly - (current_row + 1) * horizontal_column_height;
762
763                         /* Check the end of the page. */
764                         if (y < d_footer_h + d_output_y_margin)
765                           goto end_of_column;
766                       }
767                       break;
768                     }
769                   break;
770
771                 case tSTRING:
772                   if (CORRECT_SLICE ())
773                     {
774                       if (bggray < 1.0)
775                         {
776                           OUTPUT ((cofp, "%g %g %g %g %g (%s) bgs\n", x, y,
777                                    Fpt.h + baselineskip,
778                                    baselineskip
779                                    - (font_bbox_lly * Fpt.h / UNITS_PER_POINT),
780                                    bggray,
781                                    token.u.str));
782                         }
783                       else if (user_bgcolorp)
784                         {
785                           OUTPUT ((cofp, "%g %g %g %g %g %g %g (%s) bgcs\n",
786                                    x, y, Fpt.h + baselineskip,
787                                    baselineskip
788                                    - (font_bbox_lly * Fpt.h / UNITS_PER_POINT),
789                                    user_bgcolor.r,
790                                    user_bgcolor.g,
791                                    user_bgcolor.b,
792                                    token.u.str));
793                         }
794                       else
795                         {
796                           OUTPUT ((cofp, "%g %g M\n(%s) s\n", x, y,
797                                    token.u.str));
798                         }
799                     }
800                   x = token.new_x;
801                   line_column = token.new_col;
802                   break;
803
804                 case tCARRIAGE_RETURN:
805                   /* Just reset the x-coordinate. */
806                   x = col * d_output_w / (float) num_columns
807                     + d_output_x_margin + line_indent;
808                   line_column = 0;
809                   break;
810
811                 case tNEWLINE:
812                 case tWRAPPED_NEWLINE:
813                   if (token.type == tNEWLINE)
814                     {
815                       current_file_linenum++;
816                       current_slice = 1;
817                       y -= LINESKIP;
818                     }
819                   else
820                     {
821                       current_slice++;
822                       if (!slicing)
823                         {
824                           /* Mark wrapped line marks. */
825                           switch (mark_wrapped_lines_style)
826                             {
827                             case MWLS_NONE:
828                               /* nothing */
829                               break;
830
831                             case MWLS_PLUS:
832                               OUTPUT ((cofp, "%g %g M (+) s\n", x, y));
833                               break;
834
835                             default:
836                               /* Print some fancy graphics. */
837                               OUTPUT ((cofp,
838                                        "%g %g %g %g %d wrapped_line_mark\n",
839                                        x, y, Fpt.w, Fpt.h,
840                                        mark_wrapped_lines_style));
841                               break;
842                             }
843
844                           /*
845                            * For wrapped newlines, decrement y only if
846                            * we are not slicing the input.
847                            */
848                           y -= LINESKIP;
849                         }
850
851                       /* Count the wrapped lines here. */
852                       if (!slicing || current_slice > slice)
853                         if (current_file_linenum != last_wrapped_line)
854                           {
855                             if (do_print)
856                               num_truncated_lines++;
857                             last_wrapped_line = current_file_linenum;
858                           }
859                     }
860
861                   current_linenum++;
862                   if (current_linenum >= lines_per_page
863                       || y < d_footer_h + d_output_y_margin)
864                     goto end_of_column;
865
866                   x = col * d_output_w / (float) num_columns
867                     + d_output_x_margin + line_indent;
868                   line_column = 0;
869                   break;
870
871                 case tEPSF:
872                   /* Count current point movement. */
873
874                   if (token.flags & F_EPSF_ABSOLUTE_Y)
875                     token.new_y = ly;
876                   else
877                     token.new_y = y;
878                   token.new_y += token.u.epsf.y - token.u.epsf.h;
879
880                   if (token.flags & F_EPSF_ABSOLUTE_X)
881                     token.new_x = lx;
882                   else
883                     token.new_x = x;
884                   token.new_x += token.u.epsf.x;
885
886                   /* Check flags. */
887
888                   /* Justification flags overwrite <x_ofs>. */
889                   if (token.flags & F_EPSF_CENTER)
890                     token.new_x = lx + (linewidth - token.u.epsf.w) / 2;
891                   if (token.flags & F_EPSF_RIGHT)
892                     token.new_x = lx + (linewidth - token.u.epsf.w);
893
894                   /* Check if eps file does not fit to this column. */
895                   if ((token.flags & F_EPSF_NO_CPOINT_UPDATE_Y) == 0
896                       && token.new_y < d_footer_h + d_output_y_margin)
897                     {
898                       if (current_linenum == 0)
899                         {
900                           /*
901                            * At the beginning of the column, warn user
902                            * and print image.
903                            */
904                           MESSAGE (0, (stderr, _("EPS file \"%s\" is too \
905 large for page\n"),
906                                        token.u.epsf.filename));
907                         }
908                       else
909                         {
910                           /* Must start a new column. */
911                           reuse_last_token = 1;
912                           goto end_of_column;
913                         }
914                     }
915
916                   /* Do paste. */
917                   if (CORRECT_SLICE ())
918                     paste_epsf (&token);
919
920                   /* Update current point? */
921                   if (!(token.flags & F_EPSF_NO_CPOINT_UPDATE_Y))
922                     y = token.new_y;
923                   if (!(token.flags & F_EPSF_NO_CPOINT_UPDATE_X))
924                     x = token.new_x + token.u.epsf.w;
925
926                   if (y < d_footer_h + d_output_y_margin)
927                     goto end_of_column;
928                   break;
929
930                 case tFONT:
931                   /* Select a new current font. */
932                   if (line_column == 0)
933                     {
934                       double newh;
935
936                       /* Check for possible line skip change. */
937                       if (token.u.font.name[0] == '\0')
938                         newh = default_Fpt.h;
939                       else
940                         newh = token.u.font.size.h;
941
942                       if (newh != Fpt.h)
943                         {
944                           /* We need a different line skip value. */
945                           y -= (newh - Fpt.h);
946                         }
947                       /*
948                        * We must check for page overflow after we have
949                        * set the new font.
950                        */
951                     }
952
953                   MESSAGE (2, (stderr, "^@font="));
954                   if (token.u.font.name[0] == '\0')
955                     {
956                       /* Select the default font. */
957                       Fpt.w = default_Fpt.w;
958                       Fpt.h = default_Fpt.h;
959                       Fname = default_Fname;
960                       encoding = default_Fencoding;
961                       OUTPUT ((cofp, "/F-gs-font %g %g SF\n", Fpt.w, Fpt.h));
962                       user_fontp = 0;
963                     }
964                   else
965                     {
966                       strhash_put (res_fonts, token.u.font.name,
967                                    strlen (token.u.font.name) + 1,
968                                    NULL, NULL);
969                       if (token.u.font.encoding == default_Fencoding)
970                         OUTPUT ((cofp, "/%s %g %g SUF\n", token.u.font.name,
971                                  token.u.font.size.w, token.u.font.size.h));
972                       else if (token.u.font.encoding == ENC_PS)
973                         OUTPUT ((cofp, "/%s %g %g SUF_PS\n", token.u.font.name,
974                                  token.u.font.size.w, token.u.font.size.h));
975                       else
976                         FATAL ((stderr,
977                                 _("user font encoding can be only the system's default or `ps'")));
978
979                       strcpy (user_font_name, token.u.font.name);
980                       user_font_pt.w = token.u.font.size.w;
981                       user_font_pt.h = token.u.font.size.h;
982                       user_font_encoding = token.u.font.encoding;
983                       user_fontp = 1;
984
985                       Fpt.w = user_font_pt.w;
986                       Fpt.h = user_font_pt.h;
987                       Fname = user_font_name;
988                       encoding = user_font_encoding;
989                     }
990                   MESSAGE (2, (stderr, "%s %g/%gpt\n", Fname, Fpt.w, Fpt.h));
991                   read_font_info ();
992
993                   /*
994                    * Check for page overflow in that case that we were
995                    * at the first column and font were changed to a bigger
996                    * one.
997                    */
998                   if (y < d_footer_h + d_output_y_margin)
999                     goto end_of_column;
1000                   break;
1001
1002                 case tCOLOR:
1003                   /* Select a new color. */
1004                   MESSAGE (2, (stderr, "^@color{%f %f %f}\n",
1005                                token.u.color.r,
1006                                token.u.color.g,
1007                                token.u.color.b));
1008                   if (token.u.color.r == token.u.color.g
1009                       && token.u.color.g == token.u.color.b
1010                       && token.u.color.b == 0.0)
1011                     {
1012                       /* Select the default color (black). */
1013                       OUTPUT ((cofp, "0 setgray\n"));
1014                       user_colorp = 0;
1015                     }
1016                   else
1017                     {
1018                       OUTPUT ((cofp, "%g %g %g setrgbcolor\n",
1019                                token.u.color.r,
1020                                token.u.color.g,
1021                                token.u.color.b));
1022
1023                       user_color.r = token.u.color.r;
1024                       user_color.g = token.u.color.g;
1025                       user_color.b = token.u.color.b;
1026                       user_colorp = 1;
1027                     }
1028                   break;
1029
1030                 case tBGCOLOR:
1031                   /* Select a new background color. */
1032                   MESSAGE (2, (stderr, "^@bgcolor{%f %f %f}\n",
1033                                token.u.color.r,
1034                                token.u.color.g,
1035                                token.u.color.b));
1036
1037                   if (token.u.color.r == token.u.color.g
1038                       && token.u.color.g == token.u.color.b
1039                       && token.u.color.b == 1.0)
1040                     {
1041                       /* Select the default bgcolor (white). */
1042                       user_bgcolorp = 0;
1043                     }
1044                   else
1045                     {
1046                       user_bgcolor.r = token.u.color.r;
1047                       user_bgcolor.g = token.u.color.g;
1048                       user_bgcolor.b = token.u.color.b;
1049                       user_bgcolorp = 1;
1050                     }
1051                   break;
1052
1053                 case tSETFILENAME:
1054                   xfree (fname);
1055                   fname = xstrdup (token.u.filename);
1056                   break;
1057
1058                 case tSETPAGENUMBER:
1059                   current_pagenum = token.u.i - 1;
1060                   break;
1061
1062                 case tNEWPAGE:
1063                   if (current_linenum >= token.u.i)
1064                     goto end_of_page;
1065                   break;
1066
1067                 case tSAVEX:
1068                   xstore[(unsigned char) token.u.i] = x;
1069                   break;
1070
1071                 case tLOADX:
1072                   x = xstore[(unsigned char) token.u.i];
1073                   break;
1074
1075                 case tPS:
1076                   OUTPUT ((cofp, "%g %g M\n%s\n", x, y, token.u.str));
1077                   xfree (token.u.str);
1078                   break;
1079
1080                 case tNONE:
1081                 default:
1082                   FATAL ((stderr, "process_file(): got illegal token %d",
1083                           token.type));
1084                   break;
1085                 }
1086             }
1087         end_of_column:
1088           ;                     /* ULTRIX's cc needs this line. */
1089         }
1090
1091     end_of_page:
1092       if (!page_clear)
1093         dump_ps_page_trailer ();
1094     }
1095
1096   /*
1097    * Reset print flag to true so all the required document trailers
1098    * etc. get printed properly.
1099    */
1100   do_print = 1;
1101
1102   /* Undivert our output from the temp file to our output stream. */
1103   undivert ();
1104
1105   /* Table of contents? */
1106   if (toc)
1107     {
1108       char *cp;
1109       int save_total_pages = total_pages;
1110
1111       /* use first pagenum in file for toc */
1112       total_pages = first_pagenum_for_file;
1113
1114       cp = format_user_string ("TOC", toc_fmt_string);
1115       fprintf (toc_fp, "%s\n", cp);
1116       xfree (cp);
1117
1118       total_pages = save_total_pages;
1119     }
1120 }
1121
1122 \f
1123 /*
1124  * Static functions.
1125  */
1126
1127 /* Help macros. */
1128
1129 /* Check if character <ch> fits to current line. */
1130 #define FITS_ON_LINE(ch) ((linepos + CHAR_WIDTH (ch) < linew) || col == 0)
1131
1132 /* Is line buffer empty? */
1133 #define BUFFER_EMPTY() (bufpos == 0)
1134
1135 /* Unconditionally append character <ch> to the line buffer. */
1136 #define APPEND_CHAR(ch)                                 \
1137   do {                                                  \
1138     if (bufpos >= buflen)                               \
1139       {                                                 \
1140         buflen += 4096;                                 \
1141         buffer = xrealloc (buffer, buflen);             \
1142       }                                                 \
1143     buffer[bufpos++] = ch;                              \
1144   } while (0)
1145
1146 /*
1147  * Copy character <ch> (it fits to this line) to output buffer and
1148  * update current point counters.
1149  */
1150 #define EMIT(ch)                \
1151   do {                          \
1152     APPEND_CHAR (ch);           \
1153     linepos += CHAR_WIDTH (ch); \
1154     col++;                      \
1155   } while (0)
1156
1157 #define UNEMIT(ch)              \
1158   do {                          \
1159     linepos -= CHAR_WIDTH (ch); \
1160     col--;                      \
1161   } while (0)
1162
1163 #define ISSPACE(ch) ((ch) == ' ' || (ch) == '\t')
1164 #define ISOCTAL(ch) ('0' <= (ch) && (ch) <= '7')
1165
1166 /* Read one special escape from input <fp>. */
1167
1168 static struct
1169 {
1170   char *name;
1171   SpecialEscape escape;
1172 } escapes[] =
1173   {
1174     {"comment",         ESC_COMMENT},
1175     {"epsf",            ESC_EPSF},
1176     {"font",            ESC_FONT},
1177     {"color",           ESC_COLOR},
1178     {"bgcolor",         ESC_BGCOLOR},
1179     {"newpage",         ESC_NEWPAGE},
1180     {"ps",              ESC_PS},
1181     {"setfilename",     ESC_SETFILENAME},
1182     {"setpagenumber",   ESC_SETPAGENUMBER},
1183     {"shade",           ESC_SHADE},
1184     {"bggray",          ESC_BGGRAY},
1185     {"escape",          ESC_ESCAPE},
1186     {"savex",           ESC_SAVEX},
1187     {"loadx",           ESC_LOADX},
1188     {NULL, 0},
1189   };
1190
1191
1192 static void
1193 read_special_escape (InputStream *is, Token *token)
1194 {
1195   char escname[256];
1196   char buf[4096];
1197   int i, e;
1198   int ch;
1199
1200   /* Get escape name. */
1201   for (i = 0; i < sizeof (escname) - 1 && (ch = is_getc (is)) != EOF; i++)
1202     {
1203       if (!isalnum (ch))
1204         {
1205           is_ungetc (ch, is);
1206           break;
1207         }
1208       else
1209         escname[i] = ch;
1210     }
1211   escname[i] = '\0';
1212
1213   /* Lookup escape. */
1214   for (e = 0; escapes[e].name; e++)
1215     if (strcmp (escname, escapes[e].name) == 0)
1216       break;
1217   if (escapes[e].name == NULL)
1218     FATAL ((stderr, _("unknown special escape: %s"), escname));
1219
1220   /*
1221    * The epsf escape takes optional arguments so it must be handled
1222    * differently.
1223    */
1224   if (escapes[e].escape == ESC_EPSF)
1225     {
1226       int i;
1227       int pw, ph;
1228       double scale;
1229
1230       token->flags = 0;
1231       token->u.epsf.x = 0.0;
1232       token->u.epsf.y = 0.0;
1233       token->u.epsf.h = 0.0;
1234       token->u.epsf.pipe = 0;
1235
1236       ch = is_getc (is);
1237       if (ch == '[')
1238         {
1239           /* Read options. */
1240           while ((ch = is_getc (is)) != EOF && ch != ']')
1241             {
1242               switch (ch)
1243                 {
1244                 case 'c':       /* center justification */
1245                   token->flags &= ~M_EPSF_JUSTIFICATION;
1246                   token->flags |= F_EPSF_CENTER;
1247                   break;
1248
1249                 case 'n':       /* no current point update */
1250                   /* Check the next character. */
1251                   ch = is_getc (is);
1252                   switch (ch)
1253                     {
1254                     case 'x':
1255                       token->flags |= F_EPSF_NO_CPOINT_UPDATE_X;
1256                       break;
1257
1258                     case 'y':
1259                       token->flags |= F_EPSF_NO_CPOINT_UPDATE_Y;
1260                       break;
1261
1262                     default:
1263                       is_ungetc (ch, is);
1264                       token->flags |= F_EPSF_NO_CPOINT_UPDATE_X;
1265                       token->flags |= F_EPSF_NO_CPOINT_UPDATE_Y;
1266                       break;
1267                     }
1268                   break;
1269
1270                 case 'r':       /* right justification */
1271                   token->flags &= ~M_EPSF_JUSTIFICATION;
1272                   token->flags |= F_EPSF_RIGHT;
1273                   break;
1274
1275
1276                 case 's':       /* scale */
1277                   /* Check the next character. */
1278                   ch = is_getc (is);
1279                   switch (ch)
1280                     {
1281                     case 'x':
1282                       token->flags |= F_EPSF_SCALE_X;
1283                       token->u.epsf.xscale = read_float (is, 0, 1);
1284                       break;
1285
1286                     case 'y':
1287                       token->flags |= F_EPSF_SCALE_Y;
1288                       token->u.epsf.yscale = read_float (is, 0, 0);
1289                       break;
1290
1291                     default:
1292                       is_ungetc (ch, is);
1293                       token->flags |= F_EPSF_SCALE_X;
1294                       token->flags |= F_EPSF_SCALE_Y;
1295                       token->u.epsf.xscale = token->u.epsf.yscale
1296                         = read_float (is, 0, 1);
1297                       break;
1298                     }
1299                   break;
1300
1301                 case 'x':       /* x-position */
1302                   token->u.epsf.x = read_float (is, 1, 1);
1303
1304                   /* Check the next character. */
1305                   ch = is_getc (is);
1306                   switch (ch)
1307                     {
1308                     case 'a':
1309                       token->flags |= F_EPSF_ABSOLUTE_X;
1310                       break;
1311
1312                     default:
1313                       is_ungetc (ch, is);
1314                       break;
1315                     }
1316                   break;
1317
1318                 case 'y':       /* y-position */
1319                   token->u.epsf.y = - read_float (is, 1, 0);
1320
1321                   /* Check the next character. */
1322                   ch = is_getc (is);
1323                   switch (ch)
1324                     {
1325                     case 'a':
1326                       token->flags |= F_EPSF_ABSOLUTE_Y;
1327                       break;
1328
1329                     default:
1330                       is_ungetc (ch, is);
1331                       break;
1332                     }
1333                   break;
1334
1335                 case 'h':       /* height */
1336                   token->u.epsf.h = read_float (is, 1, 0);
1337                   break;
1338
1339                 case ' ':
1340                 case '\t':
1341                   break;
1342
1343                 default:
1344                   FATAL ((stderr, _("illegal option %c for ^@epsf escape"),
1345                           ch));
1346                 }
1347             }
1348           if (ch != ']')
1349             FATAL ((stderr,
1350                     _("malformed ^@epsf escape: no ']' after options")));
1351
1352           ch = is_getc (is);
1353         }
1354       if (ch == '{')
1355         {
1356           /* Read filename. */
1357           for (i = 0; (ch = is_getc (is)) != EOF && ch != '}'; i++)
1358             {
1359               token->u.epsf.filename[i] = ch;
1360               if (i + 1 >= sizeof (token->u.epsf.filename))
1361                 FATAL ((stderr,
1362                         _("too long file name for ^@epsf escape:\n%.*s"),
1363                         i, token->u.epsf.filename));
1364             }
1365           if (ch == EOF)
1366             FATAL ((stderr, _("unexpected EOF while scanning ^@epsf escape")));
1367
1368           token->u.epsf.filename[i] = '\0';
1369           token->type = tEPSF;
1370         }
1371       else
1372         FATAL ((stderr, _("malformed ^@epsf escape: no '{' found")));
1373
1374       /*
1375        * Now we have a valid epsf-token in <token>.  Let's read BoundingBox
1376        * and do some calculations.
1377        */
1378       if (!recognize_eps_file (token))
1379         /* Recognize eps has already printed error message so we are done. */
1380         token->type = tNONE;
1381       else
1382         {
1383           /* Some fixups for x and y dimensions. */
1384           token->u.epsf.y += LINESKIP - 1;
1385           if (token->u.epsf.h != 0.0)
1386             token->u.epsf.h -= 1.0;
1387
1388           /* Count picture's width and height. */
1389
1390           pw = token->u.epsf.urx - token->u.epsf.llx;
1391           ph = token->u.epsf.ury - token->u.epsf.lly;
1392
1393           /* The default scale. */
1394           if (token->u.epsf.h == 0.0)
1395             scale = 1.0;
1396           else
1397             scale = token->u.epsf.h / ph;
1398
1399           if ((token->flags & F_EPSF_SCALE_X) == 0)
1400             token->u.epsf.xscale = scale;
1401           if ((token->flags & F_EPSF_SCALE_Y) == 0)
1402             token->u.epsf.yscale = scale;
1403
1404           pw *= token->u.epsf.xscale;
1405           ph *= token->u.epsf.yscale;
1406
1407           token->u.epsf.w = pw;
1408           token->u.epsf.h = ph;
1409         }
1410     }
1411   else if (escapes[e].escape == ESC_COMMENT)
1412     {
1413       /* Comment the rest of this line. */
1414       while ((ch = is_getc (is)) != EOF && ch != nl)
1415         ;
1416       token->type = tNONE;
1417     }
1418   else
1419     {
1420       char *cp;
1421       int parenlevel;
1422
1423       /*
1424        * Handle the rest of the escapes.
1425        */
1426
1427       /* Read argument. */
1428       ch = is_getc (is);
1429       if (ch != '{')
1430         FATAL ((stderr, _("malformed %s escape: no '{' found"),
1431                 escapes[e].name));
1432
1433       parenlevel = 0;
1434       for (i = 0;
1435            (ch = is_getc (is)) != EOF && (parenlevel > 0 || ch != '}'); i++)
1436         {
1437           if (ch == '{')
1438             parenlevel++;
1439           else if (ch == '}')
1440             parenlevel--;
1441
1442           buf[i] = ch;
1443           if (i + 1 >= sizeof (buf))
1444             FATAL ((stderr, _("too long argument for %s escape:\n%.*s"),
1445                     escapes[i].name, i, buf));
1446         }
1447       buf[i] = '\0';
1448
1449       /* And now handle the escape. */
1450       switch (escapes[e].escape)
1451         {
1452         case ESC_FONT:
1453           strcpy (token->u.font.name, buf);
1454
1455           /* Check for the default font. */
1456           if (strcmp (token->u.font.name, "default") == 0)
1457             token->u.font.name[0] = '\0';
1458           else
1459             {
1460               if (!parse_font_spec (token->u.font.name, &cp,
1461                                     &token->u.font.size,
1462                                     &token->u.font.encoding))
1463                 FATAL ((stderr, _("malformed font spec for ^@font escape: %s"),
1464                         token->u.font.name));
1465
1466               strcpy (token->u.font.name, cp);
1467               xfree (cp);
1468             }
1469           token->type = tFONT;
1470           break;
1471
1472         case ESC_COLOR:
1473         case ESC_BGCOLOR:
1474           /* Check for the default color. */
1475           if (strcmp (buf, "default") == 0)
1476             {
1477               double val = 0;
1478
1479               if (escapes[e].escape == ESC_BGCOLOR)
1480                 val = 1;
1481
1482               token->u.color.r = val;
1483               token->u.color.g = val;
1484               token->u.color.b = val;
1485             }
1486           else
1487             {
1488               int got;
1489
1490               got = sscanf (buf, "%g %g %g",
1491                             &token->u.color.r,
1492                             &token->u.color.g,
1493                             &token->u.color.b);
1494               switch (got)
1495                 {
1496                 case 0:
1497                 case 2:
1498                   FATAL ((stderr,
1499                           _("malformed color spec for ^@%s escape: %s"),
1500                           escapes[e].escape == ESC_COLOR
1501                           ? "color" : "bgcolor",
1502                           buf));
1503                   break;
1504
1505                 case 1:
1506                   token->u.color.g = token->u.color.b = token->u.color.r;
1507                   break;
1508
1509                 default:
1510                   /* Got all three components. */
1511                   break;
1512                 }
1513             }
1514           if (escapes[e].escape == ESC_COLOR)
1515             token->type = tCOLOR;
1516           else
1517             token->type = tBGCOLOR;
1518           break;
1519
1520         case ESC_SHADE:
1521           line_highlight_gray = atof (buf);
1522           if (line_highlight_gray < 0.0 || line_highlight_gray > 1.0)
1523             FATAL ((stderr, _("invalid value for ^@shade escape: %s"), buf));
1524
1525           token->type = tNONE;
1526           break;
1527
1528         case ESC_BGGRAY:
1529           bggray = atof (buf);
1530           if (bggray < 0.0 || bggray > 1.0)
1531             FATAL ((stderr, _("invalid value for ^@bggray escape: %s"), buf));
1532
1533           token->type = tNONE;
1534           break;
1535
1536         case ESC_ESCAPE:
1537           if (strcmp (buf, "default") == 0)
1538             escape_char = default_escape_char;
1539           else
1540             escape_char = atoi (buf);
1541           token->type = tNONE;
1542           break;
1543
1544         case ESC_SETFILENAME:
1545           strcpy (token->u.filename, buf);
1546           token->type = tSETFILENAME;
1547           break;
1548
1549         case ESC_SETPAGENUMBER:
1550           token->u.i = atoi (buf);
1551           token->type = tSETPAGENUMBER;
1552           break;
1553
1554         case ESC_NEWPAGE:
1555           if (i == 0)
1556             token->u.i = 1;     /* The default is the first line. */
1557           else
1558             token->u.i = atoi (buf);
1559           token->type = tNEWPAGE;
1560           break;
1561
1562         case ESC_SAVEX:
1563           token->type = tSAVEX;
1564           token->u.i = atoi (buf);
1565           break;
1566
1567         case ESC_LOADX:
1568           token->type = tLOADX;
1569           token->u.i = atoi (buf);
1570           break;
1571
1572         case ESC_PS:
1573           token->u.str = xstrdup (buf);
1574           token->type = tPS;
1575           break;
1576
1577         default:
1578           /* NOTREACHED */
1579           abort ();
1580           break;
1581         }
1582     }
1583 }
1584
1585
1586 /* Get next token from input file <fp>. */
1587 static void
1588 get_next_token (InputStream *is, double linestart, double linepos,
1589                 unsigned int col, double linew, Token *token)
1590 {
1591   static unsigned char *buffer = NULL; /* output buffer */
1592   static unsigned int buflen = 0; /* output buffer's length */
1593   unsigned int bufpos = 0;      /* current position in output buffer */
1594   int ch = 0;
1595   int done = 0;
1596   int i;
1597   static int pending_token = tNONE;
1598   unsigned int original_col = col;
1599
1600   if (pending_token != tNONE)
1601     {
1602       token->type = pending_token;
1603       pending_token = tNONE;
1604       return;
1605     }
1606
1607 #define DONE_DONE 1
1608 #define DONE_WRAP 2
1609
1610   while (!done)
1611     {
1612       ch = is_getc (is);
1613       switch (ch)
1614         {
1615         case EOF:
1616           if (BUFFER_EMPTY ())
1617             {
1618               token->type = tEOF;
1619               return;
1620             }
1621
1622           done = DONE_DONE;
1623           break;
1624
1625         case '\r':
1626         case '\n':
1627           /*
1628            * One of these is the newline character and the other one
1629            * is carriage return.
1630            */
1631           if (ch == nl)
1632             {
1633               /* The newline character. */
1634               if (BUFFER_EMPTY ())
1635                 {
1636                   token->type = tNEWLINE;
1637                   return;
1638                 }
1639               else
1640                 {
1641                   is_ungetc (ch, is);
1642                   done = DONE_DONE;
1643                 }
1644             }
1645           else
1646             {
1647               /* The carriage return character. */
1648               if (BUFFER_EMPTY ())
1649                 {
1650                   token->type = tCARRIAGE_RETURN;
1651                   return;
1652                 }
1653               else
1654                 {
1655                   is_ungetc (ch, is);
1656                   done = DONE_DONE;
1657                 }
1658             }
1659           break;
1660
1661         case '\t':
1662           if (font_is_fixed)
1663             {
1664               i = tabsize - (col % tabsize);
1665               for (; i > 0; i--)
1666                 {
1667                   if (FITS_ON_LINE (' '))
1668                     EMIT (' ');
1669                   else
1670                     {
1671                       done = DONE_WRAP;
1672                       break;
1673                     }
1674                 }
1675             }
1676           else
1677             {
1678               /* Proportional font. */
1679
1680               double grid = tabsize * CHAR_WIDTH (' ');
1681               col++;
1682
1683               /* Move linepos to the next multiple of <grid>. */
1684               linepos = (((int) ((linepos - linestart) / grid) + 1) * grid
1685                          + linestart);
1686               if (linepos >= linew)
1687                 done = DONE_WRAP;
1688               else
1689                 done = DONE_DONE;
1690             }
1691           break;
1692
1693         case '\f':
1694           if (BUFFER_EMPTY ())
1695             {
1696               if (interpret_formfeed)
1697                 token->type = tFORMFEED;
1698               else
1699                 token->type = tNEWLINE;
1700               return;
1701             }
1702           else
1703             {
1704               is_ungetc (ch, is);
1705               done = DONE_DONE;
1706             }
1707           break;
1708
1709         default:
1710           /* Handle special escapes. */
1711           if (special_escapes && ch == escape_char)
1712             {
1713               if (BUFFER_EMPTY ())
1714                 {
1715                   /* Interpret special escapes. */
1716                   read_special_escape (is, token);
1717                   if (token->type != tNONE)
1718                     return;
1719
1720                   /*
1721                    * Got tNONE special escape => read_special_escape()
1722                    * has already done what was needed.  Just read more.
1723                    */
1724                   break;
1725                 }
1726               else
1727                 {
1728                   is_ungetc (ch, is);
1729                   done = DONE_DONE;
1730                   break;
1731                 }
1732             }
1733
1734           /* Handle backspace character. */
1735           if (ch == bs)
1736             {
1737               if (BUFFER_EMPTY () || !EXISTS (buffer[bufpos - 1]))
1738                 linepos -= CHAR_WIDTH ('m');
1739               else
1740                 linepos -= CHAR_WIDTH (buffer[bufpos - 1]);
1741
1742               done = DONE_DONE;
1743               break;
1744             }
1745
1746           /* Check normal characters. */
1747           if (EXISTS (ch))
1748             {
1749               if (FITS_ON_LINE (ch))
1750                 {
1751                   /*
1752                    * Print control characters (and optionally
1753                    * characters greater than 127) in the escaped form
1754                    * so PostScript interpreter will not hang on them.
1755                    */
1756                   if (ch < 040 || (clean_7bit && ch >= 0200))
1757                     {
1758                       char buf[10];
1759
1760                       sprintf (buf, "\\%03o", ch);
1761                       for (i = 0; buf[i]; i++)
1762                         APPEND_CHAR (buf[i]);
1763
1764                       /* Update current point counters manually. */
1765                       linepos += CHAR_WIDTH (ch);
1766                       col++;
1767                     }
1768                   else if (ch == '(' || ch == ')' || ch == '\\')
1769                     {
1770                       /* These must be quoted in PostScript strings. */
1771                       APPEND_CHAR ('\\');
1772                       EMIT (ch);
1773                     }
1774                   else
1775                     EMIT (ch);
1776                 }
1777               else
1778                 {
1779                   is_ungetc (ch, is);
1780                   done = DONE_WRAP;
1781                 }
1782             }
1783           else if (ISPRINT (ch))
1784             {
1785               /* Printable, but do not exists in this font. */
1786               if (FITS_ON_LINE ('?'))
1787                 {
1788                   EMIT ('?');
1789                   if (missing_chars[ch]++ == 0)
1790                     num_missing_chars++;
1791                 }
1792               else
1793                 {
1794                   is_ungetc (ch, is);
1795                   done = DONE_WRAP;
1796                 }
1797             }
1798           else
1799             {
1800               char buf[20];
1801               double len = 0.0;
1802
1803               /*
1804                * Non-printable and does not exist in current font, print
1805                * it in the format specified by non_printable_format.
1806                */
1807
1808               if (non_printable_chars[ch]++ == 0)
1809                 num_non_printable_chars++;
1810
1811               switch (non_printable_format)
1812                 {
1813                 case NPF_SPACE:
1814                   strcpy (buf, " ");
1815                   break;
1816
1817                 case NPF_QUESTIONMARK:
1818                   strcpy (buf, "?");
1819                   break;
1820
1821                 case NPF_CARET:
1822                   if (ch < 0x20)
1823                     {
1824                       buf[0] = '^';
1825                       buf[1] = '@' + ch;
1826                       buf[2] = '\0';
1827                       break;
1828                     }
1829                   /* FALLTHROUGH */
1830
1831                 case NPF_OCTAL:
1832                   sprintf (buf, "\\%03o", ch);
1833                   break;
1834                 }
1835
1836               /* Count length. */
1837               for (i = 0; buf[i]; i++)
1838                 len += CHAR_WIDTH (buf[i]);
1839
1840               if (linepos + len < linew || col == 0)
1841                 {
1842                   /* Print it. */
1843                   for (i = 0; buf[i]; i++)
1844                     {
1845                       if (buf[i] == '\\')
1846                         APPEND_CHAR ('\\'); /* Escape '\\' characters. */
1847                       EMIT (buf[i]);
1848                     }
1849                 }
1850               else
1851                 {
1852                   is_ungetc (ch, is);
1853                   done = DONE_WRAP;
1854                 }
1855             }
1856           break;
1857         }
1858     }
1859
1860   /* Got a string. */
1861
1862   /* Check for wrapped line. */
1863   if (done == DONE_WRAP)
1864     {
1865       /* This line is too long. */
1866       ch = nl;
1867       if (line_end == LE_TRUNCATE)
1868         {
1869           /* Truncate this line. */
1870           while ((ch = is_getc (is)) != EOF && ch != nl)
1871             ;
1872         }
1873       else if (!BUFFER_EMPTY () && line_end == LE_WORD_WRAP)
1874         {
1875           int w;
1876
1877           if (ISSPACE (buffer[bufpos - 1]))
1878             {
1879               /* Skip all whitespace from the end of the wrapped line. */
1880               while ((w = is_getc (is)) != EOF && ISSPACE (w))
1881                 ;
1882               is_ungetc (w, is);
1883             }
1884           else
1885             {
1886               /* Find the previous word boundary for the wrap. */
1887               for (w = bufpos - 1; w >= 0 && !ISSPACE (buffer[w]); w--)
1888                 ;
1889               w++;
1890               if (w > 0 || original_col > 0)
1891                 {
1892                   /*
1893                    * Ok, we found a word boundary.  Now we must unemit
1894                    * characters from the buffer to the intput stream.
1895                    *
1896                    * Note:
1897                    *  - bufpos is unsigned integer variable
1898                    *  - some characters are escaped with '\\'
1899                    *  - some characters are printed in octal notation
1900                    */
1901                   do
1902                     {
1903                       bufpos--;
1904
1905                       /* Check for '(', ')' and '\\'. */
1906                       if (bufpos > w
1907                           && (buffer[bufpos] == '('
1908                               || buffer[bufpos] ==  ')'
1909                               || buffer[bufpos] == '\\')
1910                           && buffer[bufpos - 1] == '\\')
1911                         {
1912                           is_ungetc (buffer[bufpos], is);
1913                           UNEMIT (buffer[bufpos]);
1914                           bufpos--;
1915                         }
1916                       /* Check the octal notations "\\%03o". */
1917                       else if (bufpos - 2 > w
1918                                && ISOCTAL (buffer[bufpos])
1919                                && ISOCTAL (buffer[bufpos - 1])
1920                                && ISOCTAL (buffer[bufpos - 2])
1921                                && buffer[bufpos - 3] == '\\')
1922                         {
1923                           unsigned int ti;
1924
1925                           /*
1926                            * It is a potential octal character.  Now we
1927                            * must process the buffer from the beginning
1928                            * and see if `bufpos - 3' really starts a character.
1929                            */
1930                           for (ti = w; ti < bufpos - 3; ti++)
1931                             {
1932                               if (buffer[ti] == '\\')
1933                                 {
1934                                   if (ISOCTAL (buffer[ti + 1]))
1935                                     {
1936                                       unsigned int tti;
1937
1938                                       for (tti = 0;
1939                                            tti < 3 && ISOCTAL (buffer[ti + 1]);
1940                                            tti++, ti++)
1941                                         ;
1942                                     }
1943                                   else
1944                                     /* Simple escape. */
1945                                     ti++;
1946                                 }
1947                             }
1948
1949                           /*
1950                            * If <ti> is equal to <bufpos - 3>, we found
1951                            * an octal character, otherwise the leading
1952                            * backslash at <bufpos - 3> belongs to the
1953                            * previous character.
1954                            */
1955                           if (ti == bufpos - 3)
1956                             {
1957                               int tch;
1958
1959                               tch = (((buffer[bufpos - 2] - '0') << 6)
1960                                      + ((buffer[bufpos - 1] - '0') << 3)
1961                                      + (buffer[bufpos] - '0'));
1962                               is_ungetc (tch, is);
1963                               UNEMIT (tch);
1964                               bufpos -= 3;
1965                             }
1966                           else
1967                             /* Normal character. */
1968                             goto unemit_normal;
1969                         }
1970                       else
1971                         {
1972                           /* Normal character, just unget it. */
1973                         unemit_normal:
1974                           is_ungetc (buffer[bufpos], is);
1975                           UNEMIT (buffer[bufpos]);
1976                         }
1977                     }
1978                   while (bufpos > w);
1979                 }
1980             }
1981         }
1982
1983       if (ch == nl)
1984         {
1985           if (line_end == LE_TRUNCATE)
1986             {
1987               if (do_print)
1988                 num_truncated_lines++;
1989               pending_token = tNEWLINE;
1990             }
1991           else
1992             pending_token = tWRAPPED_NEWLINE;
1993         }
1994       else
1995         pending_token = tEOF;
1996     }
1997
1998   APPEND_CHAR ('\0');
1999   token->type = tSTRING;
2000   token->u.str = (char *) buffer;
2001   token->new_x = linepos;
2002   token->new_col = col;
2003 }
2004
2005
2006 static void
2007 dump_ps_page_header (char *fname, int empty)
2008 {
2009   char buf[512];
2010   char *ftail;
2011   int got, i;
2012   char *cp, *cp2;
2013   char *cstr = "%%";
2014   unsigned int nup_subpage;
2015
2016   /* The N-up printing sub-page. */
2017   nup_subpage = (total_pages - 1) % nup;
2018
2019   /* Create fdir and ftail. */
2020   ftail = strrchr (fname, '/');
2021
2022 #if defined(WIN32)
2023   if (ftail == NULL)
2024     ftail = strrchr (fname, '\\');
2025 #endif /* WIN32 */
2026
2027   if (ftail == NULL)
2028     {
2029       buf[0] = '\0';
2030       ftail = fname;
2031     }
2032   else
2033     {
2034       ftail++;
2035       strncpy (buf, fname, ftail - fname);
2036       buf[ftail - fname] = '\0';
2037     }
2038
2039   if (nup > 1)
2040     {
2041       /* N-up printing is active. */
2042       cstr = "%";
2043
2044       if (nup_subpage == 0)
2045         {
2046           /* This is a real page start. */
2047
2048           switch (page_label)
2049             {
2050             case LABEL_SHORT:
2051               OUTPUT ((cofp, "%%%%Page: (%d-%d) %d\n", current_pagenum,
2052                        current_pagenum + nup - 1, total_pages / nup + 1));
2053               break;
2054
2055             case LABEL_LONG:
2056               OUTPUT ((cofp, "%%%%Page: (%s:%3d-%3d) %d\n", ftail,
2057                        current_pagenum, current_pagenum + nup - 1,
2058                        total_pages / nup + 1));
2059               break;
2060             }
2061
2062           /* Page setup. */
2063           OUTPUT ((cofp, "%%%%BeginPageSetup\n_S\n"));
2064
2065           if ((total_pages / nup + 1) % 2 == 0)
2066             /* Two-side binding options for the even pages. */
2067             handle_two_side_options ();
2068
2069 #define PRINT_BOUNDING_BOXES 0
2070
2071 #if PRINT_BOUNDING_BOXES
2072           OUTPUT ((cofp,
2073                    "%d %d moveto %d %d lineto %d %d lineto %d %d lineto closepath stroke\n",
2074                    media->llx, media->lly, media->llx, media->ury,
2075                    media->urx, media->ury, media->urx, media->lly));
2076 #endif
2077
2078           if (landscape)
2079             {
2080               if (nup_landscape)
2081                 OUTPUT ((cofp, "90 rotate\n%d %d translate\n",
2082                          media->lly, -media->urx));
2083               else
2084                 OUTPUT ((cofp, "%d %d translate\n", media->llx, media->lly));
2085             }
2086           else
2087             {
2088               if (nup_landscape)
2089                 OUTPUT ((cofp, "90 rotate\n%d %d translate\n",
2090                          media->lly, -media->llx));
2091               else
2092                 OUTPUT ((cofp, "%d %d translate\n", media->llx, media->ury));
2093             }
2094         }
2095     }
2096
2097   /* Page start comment. */
2098   switch (page_label)
2099     {
2100     case LABEL_SHORT:
2101       OUTPUT ((cofp, "%sPage: (%d) %d\n", cstr, current_pagenum, total_pages));
2102       break;
2103
2104     case LABEL_LONG:
2105       OUTPUT ((cofp, "%sPage: (%s:%3d) %d\n", cstr, ftail, current_pagenum,
2106                total_pages));
2107       break;
2108     }
2109
2110   /*
2111    * Page Setup.
2112    */
2113
2114   OUTPUT ((cofp, "%sBeginPageSetup\n_S\n", cstr));
2115
2116   if (nup > 1)
2117     {
2118       int xm, ym;
2119
2120       OUTPUT ((cofp, "%% N-up sub-page %d/%d\n", nup_subpage + 1, nup));
2121       if (landscape)
2122         {
2123           if (nup_columnwise)
2124             {
2125               xm = nup_subpage % nup_columns;
2126               ym = nup_subpage / nup_columns;
2127             }
2128           else
2129             {
2130               xm = nup_subpage / nup_rows;
2131               ym = nup_subpage % nup_rows;
2132             }
2133
2134           OUTPUT ((cofp, "%d %d translate\n",
2135                    xm * (nup_width + nup_xpad),
2136                    ym * (nup_height + nup_ypad)));
2137         }
2138       else
2139         {
2140           if (nup_columnwise)
2141             {
2142               xm = nup_subpage / nup_rows;
2143               ym = nup_subpage % nup_rows;
2144             }
2145           else
2146             {
2147               xm = nup_subpage % nup_columns;
2148               ym = nup_subpage / nup_columns;
2149             }
2150
2151           OUTPUT ((cofp, "%d %d translate\n",
2152                    xm * (nup_width + nup_xpad),
2153                    -((int) (ym * (nup_height + nup_ypad) + nup_height))));
2154         }
2155       OUTPUT ((cofp, "%g dup scale\n", nup_scale));
2156
2157       /* And finally, the real page setup. */
2158       if (landscape)
2159         OUTPUT ((cofp, "90 rotate\n%d %d translate\n", 0, -d_page_h));
2160     }
2161   else
2162     {
2163       /* No N-up printing. */
2164
2165       if (total_pages % 2 == 0)
2166         /* Two-side binding options for the even pages. */
2167         handle_two_side_options ();
2168
2169       if (landscape)
2170         OUTPUT ((cofp, "90 rotate\n%d %d translate\n",
2171                  media->lly, -media->urx));
2172       else
2173         OUTPUT ((cofp, "%d %d translate\n", media->llx, media->lly));
2174     }
2175
2176   /* Some constants etc. */
2177   OUTPUT ((cofp, "/pagenum %d def\n", current_pagenum));
2178
2179   cp = escape_string (fname);
2180   OUTPUT ((cofp, "/fname (%s) def\n", cp));
2181   xfree (cp);
2182
2183   cp = escape_string (buf);
2184   OUTPUT ((cofp, "/fdir (%s) def\n", cp));
2185   xfree (cp);
2186
2187   cp = escape_string (ftail);
2188   OUTPUT ((cofp, "/ftail (%s) def\n", cp));
2189   xfree (cp);
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, 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 *= 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;
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 += 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 \f
2780 /*
2781  * The name of the divert file, shared between divert() and undivert()
2782  * functions.
2783  */
2784 static char divertfname[512];
2785
2786 static void
2787 divert ()
2788 {
2789   assert (divertfp == NULL);
2790
2791   /* Open divert file. */
2792
2793   divertfp = tmpfile ();
2794   if (divertfp == NULL)
2795     FATAL ((stderr, _("couldn't create temporary divert file: %s"),
2796             strerror (errno)));
2797
2798   cofp = divertfp;
2799 }
2800
2801
2802 static void
2803 undivert ()
2804 {
2805   char buf[1024];
2806   int doc_level = 0;
2807   char *cp;
2808
2809   assert (divertfp != NULL);
2810
2811   if (fseek (divertfp, 0, SEEK_SET) != 0)
2812     FATAL ((stderr, _("couldn't rewind divert file: %s"), strerror (errno)));
2813
2814   while (fgets (buf, sizeof (buf), divertfp))
2815     {
2816       if (strncmp (buf, "%%BeginDocument", 15) == 0)
2817         doc_level++;
2818       else if (strncmp (buf, "%%EndDocument", 13) == 0)
2819         doc_level--;
2820
2821       if (doc_level == 0)
2822         {
2823           if (strncmp (buf, "% User defined strings", 22) == 0)
2824             {
2825               fputs (buf, ofp);
2826               while (fgets (buf, sizeof (buf), divertfp))
2827                 {
2828                   if (strncmp (buf, "%%EndPageSetup", 14) == 0)
2829                     break;
2830
2831                   /* Patch total pages to the user defined strings. */
2832                   cp = strchr (buf, '\001');
2833                   if (cp)
2834                     {
2835                       *cp = '\0';
2836                       fputs (buf, ofp);
2837                       fprintf (ofp, "%d", total_pages_in_file);
2838                       fputs (cp + 1, ofp);
2839                     }
2840                   else
2841                     fputs (buf, ofp);
2842                 }
2843             }
2844         }
2845
2846       fputs (buf, ofp);
2847     }
2848
2849   fclose (divertfp);
2850   divertfp = NULL;
2851
2852   cofp = ofp;
2853 }
2854
2855
2856 static void
2857 handle_two_side_options ()
2858 {
2859   if (rotate_even_pages)
2860     /* Rotate page 180 degrees. */
2861     OUTPUT ((cofp, "180 rotate\n%d %d translate\n",
2862              -media->w, -media->h));
2863
2864   if (swap_even_page_margins)
2865     OUTPUT ((cofp, "%d 0 translate\n",
2866              -(media->llx - (media->w - media->urx))));
2867 }