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