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