source: trunk/third/enscript/src/psgen.c @ 17631

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