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