2 * Convert ASCII to PostScript.
3 * Copyright (c) 1995-2002 Markku Rossi.
5 * Author: Markku Rossi <mtr@iki.fi>
9 * This file is part of GNU Enscript.
11 * Enscript is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
16 * Enscript is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with Enscript. If not, see <http://www.gnu.org/licenses/>.
30 * Types and definitions.
33 /* Values for token flags. */
36 #define F_EPSF_CENTER 0x01
37 #define F_EPSF_RIGHT 0x02
38 #define M_EPSF_JUSTIFICATION 0x03
40 #define F_EPSF_NO_CPOINT_UPDATE_X 0x04
41 #define F_EPSF_NO_CPOINT_UPDATE_Y 0x08
43 #define F_EPSF_ABSOLUTE_X 0x10
44 #define F_EPSF_ABSOLUTE_Y 0x20
46 #define F_EPSF_SCALE_X 0x40
47 #define F_EPSF_SCALE_Y 0x80
50 /* Predicate to check if we are at the correct slice. */
51 #define CORRECT_SLICE() (slicing == 0 || current_slice == slice)
53 /* Predicates for the current body font. */
55 /* Is character <ch> printable. */
56 #define ISPRINT(ch) (font_ctype[(unsigned char) (ch)] != ' ')
58 /* Does character <ch> exist in current body font? */
59 #define EXISTS(ch) (font_ctype[(unsigned char) (ch)] == '*')
62 #define RESOURCE_LINE_WIDTH 75
86 /* Special escape tokens. */
105 /* Token structure. */
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. */
120 double x; /* x-offset */
121 double y; /* y-offset */
122 double w; /* width */
123 double h; /* height */
126 int llx, lly, urx, ury; /* Bounding box. */
127 char filename[PATH_MAX];
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? */
140 InputEncoding encoding;
142 char filename[PATH_MAX];
146 typedef struct gs_token_st Token;
150 * Prototypes for static functions.
153 static void get_next_token ___P ((InputStream *is, double linestart,
154 double linepos, unsigned int col,
155 double linew, Token *token));
157 static void dump_ps_page_header ___P ((char *fname, int empty));
159 static void dump_ps_page_trailer ();
161 static void dump_empty_page ();
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
169 static int recognize_eps_file ___P ((Token *token));
172 * Insert EPS file described by <token> to the output stream.
174 static void paste_epsf ___P ((Token *token));
177 * Check if InputStream <is> contains a file which can be passed
178 * through without any modifications. Returns 1 if file was passed or
181 static int do_pass_through ___P ((char *fname, InputStream *is));
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).
189 static double read_float ___P ((InputStream *is, int units, int horizontal));
192 * Print linenumber <linenum> to the beginning of the current line.
193 * Current line start is specified by point (x, y).
195 static void print_line_number ___P ((double x, double y, double space,
196 double margin, unsigned int linenum));
198 /* Send PostScript to the output file. */
199 #define OUTPUT(body) \
207 /* Divert output to tmp file so the total page count can be counted. */
208 static void divert ();
210 /* Paste diverted data to the output and patch the total page counts. */
211 static void undivert ();
214 * Handle two-side printing related binding options. This function is
215 * called once for each even-numbered page.
217 static void handle_two_side_options ();
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. */
235 /* Have we dumped PS header? */
236 static int ps_header_dumped = 0;
239 static FILE *divertfp = NULL;
241 /* Current output() file. */
242 static FILE *cofp = NULL;
244 /* To print or not to print, that's a question. */
245 static int do_print = 1;
247 /* Is ^@font{}-defined font active? */
248 static int user_fontp = 0;
250 /* The user ^@font{}-defined font. */
251 static char user_font_name[PATH_MAX];
252 static FontPoint user_font_pt;
253 static InputEncoding user_font_encoding;
255 /* Is ^@color{}-defined color active? */
256 static int user_colorp = 0;
258 /* The user ^@color{}-defined color. */
259 static Color user_color;
261 /* Is ^@bgcolor{}-defined color active? */
262 static int user_bgcolorp = 0;
264 /* The user ^@bgcolor{}-defined color. */
265 static Color user_bgcolor;
267 /* The last linenumber printed by print_line_number(). */
268 static unsigned int print_line_number_last;
270 /* Registers to store X-coordinates with the ^@savex{} escape.
271 Initially these are uninitialized. */
272 static double xstore[256];
284 /* Dump PS header only once. */
285 if (ps_header_dumped)
287 ps_header_dumped = 1;
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"));
308 if (count_key_value_set (pagedevice) > 0)
309 OUTPUT ((cofp, "%%%%LanguageLevel: 2\n"));
311 OUTPUT ((cofp, "%%%%EndComments\n"));
315 * Procedure Definitions.
318 OUTPUT ((cofp, "%%%%BeginProlog\n"));
321 OUTPUT ((cofp, "%%%%BeginResource: procset Enscript-Prolog %s\n",
323 if (!paste_file ("enscript", ".pro"))
324 FATAL ((stderr, _("couldn't find prolog \"%s\": %s\n"), "enscript.pro",
326 OUTPUT ((cofp, "%%%%EndResource\n"));
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"));
336 OUTPUT ((cofp, "%%%%EndProlog\n"));
343 OUTPUT ((cofp, "%%%%BeginSetup\n"));
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))
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));
355 OUTPUT ((cofp, "/HFpt_w %g def\n", HFpt.w));
356 OUTPUT ((cofp, "/HFpt_h %g def\n", HFpt.h));
359 /* Select our fonts. */
361 /* Header font HF. */
362 OUTPUT ((cofp, "/%s /HF-gs-font MF\n", HFname));
364 "/HF /HF-gs-font findfont [HFpt_w 0 0 HFpt_h 0 0] makefont def\n"));
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));
371 if (underlay != NULL)
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"));
386 /* Number of copies. */
387 OUTPUT ((cofp, "/#copies %d def\n", num_copies));
391 OUTPUT ((cofp, "true page_prefeed\n"));
393 /* Statusdict definitions. */
394 if (count_key_value_set (statusdict) > 0)
396 OUTPUT ((cofp, "%% Statustdict definitions:\nstatusdict begin\n "));
398 for (got = strhash_get_first (statusdict, &cp, &j, (void **) &cp2); got;
399 got = strhash_get_next (statusdict, &cp, &j, (void **) &cp2))
401 j = strlen (cp) + 1 + strlen (cp2) + 1;
402 if (i + j > RESOURCE_LINE_WIDTH)
404 OUTPUT ((cofp, "\n "));
407 OUTPUT ((cofp, "%s %s ", cp2, cp));
410 OUTPUT ((cofp, "\nend\n"));
413 /* Page device definitions. */
415 (count_key_value_set (pagedevice) > 0 || generate_PageSize))
417 OUTPUT ((cofp, "%% Pagedevice definitions:\n"));
418 OUTPUT ((cofp, "gs_languagelevel 1 gt {\n <<\n "));
421 for (got = strhash_get_first (pagedevice, &cp, &j, (void **) &cp2); got;
422 got = strhash_get_next (pagedevice, &cp, &j, (void **) &cp2))
424 j = strlen (cp2) + 1 + strlen (cp) + 2;
425 if (i + j > RESOURCE_LINE_WIDTH)
427 OUTPUT ((cofp, "\n "));
430 OUTPUT ((cofp, "/%s %s ", cp, cp2));
434 if (generate_PageSize)
436 if (i + 21 > RESOURCE_LINE_WIDTH)
438 OUTPUT ((cofp, "\n "));
441 OUTPUT ((cofp, "/PageSize [%d %d] ", media->w, media->h));
445 OUTPUT ((cofp, "\n >> setpagedevice\n} if\n"));
449 * Dump header procset. Header must come after all font inclusions
450 * and enscript's dynamic state definition.
452 if (header != HDR_NONE)
455 if (header == HDR_SIMPLE)
458 hdr = fancy_header_name;
460 OUTPUT ((cofp, "%%%%BeginResource: procset Enscript-Header-%s %s\n",
461 hdr, ps_version_string));
462 if (!paste_file (hdr, ".hdr"))
464 _("couldn't find header definition file \"%s.hdr\": %s\n"),
465 hdr, strerror (errno)));
466 OUTPUT ((cofp, "%%%%EndResource\n"));
470 * Count output width and height here; we can't do it earlier because
471 * header might have just allocated some extra space.
473 d_output_w = d_page_w;
474 d_output_h = d_page_h - d_header_h - d_footer_h;
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));
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));
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));
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));
494 OUTPUT ((cofp, "%%%%EndSetup\n"));
504 unsigned int nup_subpage;
506 if (!ps_header_dumped)
507 /* No header, let's be consistent and forget trailer also. */
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"));
518 OUTPUT ((cofp, "%%%%Trailer\n"));
521 OUTPUT ((cofp, "false page_prefeed\n"));
523 OUTPUT ((cofp, "%%%%Pages: %d\n", total_pages));
525 /* Document needed resources. */
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))
533 if (i + strlen (cp) + 1 > RESOURCE_LINE_WIDTH)
535 OUTPUT ((cofp, "\n%%%%+ font "));
536 i = 9; /* length of the previous string. */
538 OUTPUT ((cofp, "%s ", cp));
539 i += strlen (cp) + 1;
541 OUTPUT ((cofp, "\n%%%%EOF\n"));
546 process_file (char *fname_arg, InputStream *is, int is_toc)
551 double linewidth; /* Line width in points. */
555 unsigned int line_column;
556 unsigned int current_linenum;
557 double linenumber_space = 0;
558 double linenumber_margin = 0;
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;
569 fname = xstrdup (fname_arg);
571 /* Init page number and line counters. */
572 if (!continuous_page_numbers)
574 total_pages_in_file = 0;
575 current_file_linenum = start_line_number;
578 * Count possible line number spaces. This should be enought for 99999
581 linenumber_space = CHAR_WIDTH ('0') * 5 + 1.0;
582 linenumber_margin = CHAR_WIDTH (':') + CHAR_WIDTH ('m');
584 /* We got a new input file. */
587 /* We haven't printed any line numbers yet. */
588 print_line_number_last = (unsigned int) -1;
590 if (pass_through || output_language_pass_through)
591 if (do_pass_through (fname, is))
595 /* We have work to do, let's give header a chance to dump itself. */
599 * Align files to the file_align boundary, this is handy for two-side
602 while ((total_pages % file_align) != 0)
608 MESSAGE (1, (stderr, _("processing file \"%s\"...\n"), fname));
610 linewidth = d_output_w / num_columns - 2 * d_output_x_margin
613 /* Save the current running page number for possible toc usage. */
614 first_pagenum_for_file = total_pages + 1;
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
623 /* Process this input file. */
626 /* Start a new page. */
629 for (col = 0; !done && col < num_columns; col++)
631 /* Move to the beginning of the column <col>. */
632 lx = x = col * d_output_w / (float) num_columns + d_output_x_margin
634 lineend = lx + linewidth;
636 ly = y = d_footer_h + d_output_h - d_output_y_margin - LINESKIP;
642 if (line_numbers && line_column == 0
643 && (current_file_linenum != last_spaced_file_linenum))
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;
651 if (!reuse_last_token)
652 get_next_token (is, lx, x, line_column, lineend, &token);
653 reuse_last_token = 0;
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 ;)
663 if (token.type == tEOF)
670 * Now we know that we are going to make marks to this page
671 * => print page header.
679 total_pages_in_file++;
681 /* Check page ranges. */
682 if (page_ranges == NULL)
687 for (pr = page_ranges; pr; pr = pr->next)
689 if (pr->odd || pr->even)
691 if ((pr->odd && (current_pagenum % 2) == 1)
692 || (pr->even && (current_pagenum % 2) == 0))
700 if (pr->start <= current_pagenum
701 && current_pagenum <= pr->end)
715 save_current_pagenum = current_pagenum;
717 current_pagenum = toc_pagenum;
720 dump_ps_page_header (fname, 0);
724 current_pagenum = save_current_pagenum;
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));
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);
740 /* Check rest of tokens. */
744 switch (formfeed_type)
746 case FORMFEED_COLUMN:
754 case FORMFEED_HCOLUMN:
756 * Advance y-coordinate to the next even
757 * `horizontal_column_height' position.
762 current_row = (ly - y) / horizontal_column_height;
763 y = ly - (current_row + 1) * horizontal_column_height;
765 /* Check the end of the page. */
766 if (y < d_footer_h + d_output_y_margin)
774 if (CORRECT_SLICE ())
778 OUTPUT ((cofp, "%g %g %g %g %g (%s) bgs\n", x, y,
779 Fpt.h + baselineskip,
781 - (font_bbox_lly * Fpt.h / UNITS_PER_POINT),
785 else if (user_bgcolorp)
787 OUTPUT ((cofp, "%g %g %g %g %g %g %g (%s) bgcs\n",
788 x, y, Fpt.h + baselineskip,
790 - (font_bbox_lly * Fpt.h / UNITS_PER_POINT),
798 OUTPUT ((cofp, "%g %g M\n(%s) s\n", x, y,
803 line_column = token.new_col;
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;
814 case tWRAPPED_NEWLINE:
815 if (token.type == tNEWLINE)
817 current_file_linenum++;
826 /* Mark wrapped line marks. */
827 switch (mark_wrapped_lines_style)
834 OUTPUT ((cofp, "%g %g M (+) s\n", x, y));
838 /* Print some fancy graphics. */
840 "%g %g %g %g %d wrapped_line_mark\n",
842 mark_wrapped_lines_style));
847 * For wrapped newlines, decrement y only if
848 * we are not slicing the input.
853 /* Count the wrapped lines here. */
854 if (!slicing || current_slice > slice)
855 if (current_file_linenum != last_wrapped_line)
858 num_truncated_lines++;
859 last_wrapped_line = current_file_linenum;
864 if (current_linenum >= lines_per_page
865 || y < d_footer_h + d_output_y_margin)
868 x = col * d_output_w / (float) num_columns
869 + d_output_x_margin + line_indent;
874 /* Count current point movement. */
876 if (token.flags & F_EPSF_ABSOLUTE_Y)
880 token.new_y += token.u.epsf.y - token.u.epsf.h;
882 if (token.flags & F_EPSF_ABSOLUTE_X)
886 token.new_x += token.u.epsf.x;
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);
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)
900 if (current_linenum == 0)
903 * At the beginning of the column, warn user
906 MESSAGE (0, (stderr, _("EPS file \"%s\" is too \
908 token.u.epsf.filename));
912 /* Must start a new column. */
913 reuse_last_token = 1;
919 if (CORRECT_SLICE ())
922 /* Update current point? */
923 if (!(token.flags & F_EPSF_NO_CPOINT_UPDATE_Y))
925 if (!(token.flags & F_EPSF_NO_CPOINT_UPDATE_X))
926 x = token.new_x + token.u.epsf.w;
928 if (y < d_footer_h + d_output_y_margin)
933 /* Select a new current font. */
934 if (line_column == 0)
938 /* Check for possible line skip change. */
939 if (token.u.font.name[0] == '\0')
940 newh = default_Fpt.h;
942 newh = token.u.font.size.h;
946 /* We need a different line skip value. */
950 * We must check for page overflow after we have
955 MESSAGE (2, (stderr, "^@font="));
956 if (token.u.font.name[0] == '\0')
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));
968 strhash_put (res_fonts, token.u.font.name,
969 strlen (token.u.font.name) + 1,
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));
979 _("user font encoding can be only the system's default or `ps'")));
981 memset (user_font_name, 0, sizeof(user_font_name));
982 strncpy (user_font_name, token.u.font.name, sizeof(user_font_name) - 1);
983 user_font_pt.w = token.u.font.size.w;
984 user_font_pt.h = token.u.font.size.h;
985 user_font_encoding = token.u.font.encoding;
988 Fpt.w = user_font_pt.w;
989 Fpt.h = user_font_pt.h;
990 Fname = user_font_name;
991 encoding = user_font_encoding;
993 MESSAGE (2, (stderr, "%s %g/%gpt\n", Fname, Fpt.w, Fpt.h));
997 * Check for page overflow in that case that we were
998 * at the first column and font were changed to a bigger
1001 if (y < d_footer_h + d_output_y_margin)
1006 /* Select a new color. */
1007 MESSAGE (2, (stderr, "^@color{%f %f %f}\n",
1011 if (token.u.color.r == token.u.color.g
1012 && token.u.color.g == token.u.color.b
1013 && token.u.color.b == 0.0)
1015 /* Select the default color (black). */
1016 OUTPUT ((cofp, "0 setgray\n"));
1021 OUTPUT ((cofp, "%g %g %g setrgbcolor\n",
1026 user_color.r = token.u.color.r;
1027 user_color.g = token.u.color.g;
1028 user_color.b = token.u.color.b;
1034 /* Select a new background color. */
1035 MESSAGE (2, (stderr, "^@bgcolor{%f %f %f}\n",
1040 if (token.u.color.r == token.u.color.g
1041 && token.u.color.g == token.u.color.b
1042 && token.u.color.b == 1.0)
1044 /* Select the default bgcolor (white). */
1049 user_bgcolor.r = token.u.color.r;
1050 user_bgcolor.g = token.u.color.g;
1051 user_bgcolor.b = token.u.color.b;
1058 fname = xstrdup (token.u.filename);
1061 case tSETPAGENUMBER:
1062 current_pagenum = token.u.i - 1;
1066 if (current_linenum >= token.u.i)
1071 xstore[(unsigned char) token.u.i] = x;
1075 x = xstore[(unsigned char) token.u.i];
1079 OUTPUT ((cofp, "%g %g M\n%s\n", x, y, token.u.str));
1080 xfree (token.u.str);
1085 FATAL ((stderr, "process_file(): got illegal token %d",
1091 ; /* ULTRIX's cc needs this line. */
1096 dump_ps_page_trailer ();
1100 * Reset print flag to true so all the required document trailers
1101 * etc. get printed properly.
1105 /* Undivert our output from the temp file to our output stream. */
1108 /* Table of contents? */
1112 int save_total_pages = total_pages;
1114 /* use first pagenum in file for toc */
1115 total_pages = first_pagenum_for_file;
1117 cp = format_user_string ("TOC", toc_fmt_string);
1118 fprintf (toc_fp, "%s\n", cp);
1121 total_pages = save_total_pages;
1132 /* Check if character <ch> fits to current line. */
1133 #define FITS_ON_LINE(ch) ((linepos + CHAR_WIDTH (ch) < linew) || col == 0)
1135 /* Is line buffer empty? */
1136 #define BUFFER_EMPTY() (bufpos == 0)
1138 /* Unconditionally append character <ch> to the line buffer. */
1139 #define APPEND_CHAR(ch) \
1141 if (bufpos >= buflen) \
1144 buffer = xrealloc (buffer, buflen); \
1146 buffer[bufpos++] = ch; \
1150 * Copy character <ch> (it fits to this line) to output buffer and
1151 * update current point counters.
1156 linepos += CHAR_WIDTH (ch); \
1160 #define UNEMIT(ch) \
1162 linepos -= CHAR_WIDTH (ch); \
1166 #define ISSPACE(ch) ((ch) == ' ' || (ch) == '\t')
1167 #define ISOCTAL(ch) ('0' <= (ch) && (ch) <= '7')
1169 /* Read one special escape from input <fp>. */
1174 SpecialEscape escape;
1177 {"comment", ESC_COMMENT},
1180 {"color", ESC_COLOR},
1181 {"bgcolor", ESC_BGCOLOR},
1182 {"newpage", ESC_NEWPAGE},
1184 {"setfilename", ESC_SETFILENAME},
1185 {"setpagenumber", ESC_SETPAGENUMBER},
1186 {"shade", ESC_SHADE},
1187 {"bggray", ESC_BGGRAY},
1188 {"escape", ESC_ESCAPE},
1189 {"savex", ESC_SAVEX},
1190 {"loadx", ESC_LOADX},
1196 read_special_escape (InputStream *is, Token *token)
1203 /* Get escape name. */
1204 for (i = 0; i < sizeof (escname) - 1 && (ch = is_getc (is)) != EOF; i++)
1216 /* Lookup escape. */
1217 for (e = 0; escapes[e].name; e++)
1218 if (strcmp (escname, escapes[e].name) == 0)
1220 if (escapes[e].name == NULL)
1221 FATAL ((stderr, _("unknown special escape: %s"), escname));
1224 * The epsf escape takes optional arguments so it must be handled
1227 if (escapes[e].escape == ESC_EPSF)
1234 token->u.epsf.x = 0.0;
1235 token->u.epsf.y = 0.0;
1236 token->u.epsf.h = 0.0;
1237 token->u.epsf.pipe = 0;
1243 while ((ch = is_getc (is)) != EOF && ch != ']')
1247 case 'c': /* center justification */
1248 token->flags &= ~M_EPSF_JUSTIFICATION;
1249 token->flags |= F_EPSF_CENTER;
1252 case 'n': /* no current point update */
1253 /* Check the next character. */
1258 token->flags |= F_EPSF_NO_CPOINT_UPDATE_X;
1262 token->flags |= F_EPSF_NO_CPOINT_UPDATE_Y;
1267 token->flags |= F_EPSF_NO_CPOINT_UPDATE_X;
1268 token->flags |= F_EPSF_NO_CPOINT_UPDATE_Y;
1273 case 'r': /* right justification */
1274 token->flags &= ~M_EPSF_JUSTIFICATION;
1275 token->flags |= F_EPSF_RIGHT;
1279 case 's': /* scale */
1280 /* Check the next character. */
1285 token->flags |= F_EPSF_SCALE_X;
1286 token->u.epsf.xscale = read_float (is, 0, 1);
1290 token->flags |= F_EPSF_SCALE_Y;
1291 token->u.epsf.yscale = read_float (is, 0, 0);
1296 token->flags |= F_EPSF_SCALE_X;
1297 token->flags |= F_EPSF_SCALE_Y;
1298 token->u.epsf.xscale = token->u.epsf.yscale
1299 = read_float (is, 0, 1);
1304 case 'x': /* x-position */
1305 token->u.epsf.x = read_float (is, 1, 1);
1307 /* Check the next character. */
1312 token->flags |= F_EPSF_ABSOLUTE_X;
1321 case 'y': /* y-position */
1322 token->u.epsf.y = - read_float (is, 1, 0);
1324 /* Check the next character. */
1329 token->flags |= F_EPSF_ABSOLUTE_Y;
1338 case 'h': /* height */
1339 token->u.epsf.h = read_float (is, 1, 0);
1347 FATAL ((stderr, _("illegal option %c for ^@epsf escape"),
1353 _("malformed ^@epsf escape: no ']' after options")));
1359 /* Read filename. */
1360 for (i = 0; (ch = is_getc (is)) != EOF && ch != '}'; i++)
1362 token->u.epsf.filename[i] = ch;
1363 if (i + 1 >= sizeof (token->u.epsf.filename))
1365 _("too long file name for ^@epsf escape:\n%.*s"),
1366 i, token->u.epsf.filename));
1369 FATAL ((stderr, _("unexpected EOF while scanning ^@epsf escape")));
1371 token->u.epsf.filename[i] = '\0';
1372 token->type = tEPSF;
1375 FATAL ((stderr, _("malformed ^@epsf escape: no '{' found")));
1378 * Now we have a valid epsf-token in <token>. Let's read BoundingBox
1379 * and do some calculations.
1381 if (!recognize_eps_file (token))
1382 /* Recognize eps has already printed error message so we are done. */
1383 token->type = tNONE;
1386 /* Some fixups for x and y dimensions. */
1387 token->u.epsf.y += LINESKIP - 1;
1388 if (token->u.epsf.h != 0.0)
1389 token->u.epsf.h -= 1.0;
1391 /* Count picture's width and height. */
1393 pw = token->u.epsf.urx - token->u.epsf.llx;
1394 ph = token->u.epsf.ury - token->u.epsf.lly;
1396 /* The default scale. */
1397 if (token->u.epsf.h == 0.0)
1400 scale = token->u.epsf.h / ph;
1402 if ((token->flags & F_EPSF_SCALE_X) == 0)
1403 token->u.epsf.xscale = scale;
1404 if ((token->flags & F_EPSF_SCALE_Y) == 0)
1405 token->u.epsf.yscale = scale;
1407 pw *= token->u.epsf.xscale;
1408 ph *= token->u.epsf.yscale;
1410 token->u.epsf.w = pw;
1411 token->u.epsf.h = ph;
1414 else if (escapes[e].escape == ESC_COMMENT)
1416 /* Comment the rest of this line. */
1417 while ((ch = is_getc (is)) != EOF && ch != nl)
1419 token->type = tNONE;
1427 * Handle the rest of the escapes.
1430 /* Read argument. */
1433 FATAL ((stderr, _("malformed %s escape: no '{' found"),
1438 (ch = is_getc (is)) != EOF && (parenlevel > 0 || ch != '}'); i++)
1446 if (i + 1 >= sizeof (buf))
1447 FATAL ((stderr, _("too long argument for %s escape:\n%.*s"),
1448 escapes[e].name, i, buf));
1452 /* And now handle the escape. */
1453 switch (escapes[e].escape)
1456 memset (token->u.font.name, 0, sizeof(token->u.font.name));
1457 strncpy (token->u.font.name, buf, sizeof(token->u.font.name) - 1);
1459 /* Check for the default font. */
1460 if (strcmp (token->u.font.name, "default") == 0)
1461 token->u.font.name[0] = '\0';
1464 if (!parse_font_spec (token->u.font.name, &cp,
1465 &token->u.font.size,
1466 &token->u.font.encoding))
1467 FATAL ((stderr, _("malformed font spec for ^@font escape: %s"),
1468 token->u.font.name));
1470 memset (token->u.font.name, 0, sizeof(token->u.font.name));
1471 strncpy (token->u.font.name, cp, sizeof(token->u.font.name) - 1);
1474 token->type = tFONT;
1479 /* Check for the default color. */
1480 if (strcmp (buf, "default") == 0)
1484 if (escapes[e].escape == ESC_BGCOLOR)
1487 token->u.color.r = val;
1488 token->u.color.g = val;
1489 token->u.color.b = val;
1495 got = sscanf (buf, "%g %g %g",
1504 _("malformed color spec for ^@%s escape: %s"),
1505 escapes[e].escape == ESC_COLOR
1506 ? "color" : "bgcolor",
1511 token->u.color.g = token->u.color.b = token->u.color.r;
1515 /* Got all three components. */
1519 if (escapes[e].escape == ESC_COLOR)
1520 token->type = tCOLOR;
1522 token->type = tBGCOLOR;
1526 line_highlight_gray = atof (buf);
1527 if (line_highlight_gray < 0.0 || line_highlight_gray > 1.0)
1528 FATAL ((stderr, _("invalid value for ^@shade escape: %s"), buf));
1530 token->type = tNONE;
1534 bggray = atof (buf);
1535 if (bggray < 0.0 || bggray > 1.0)
1536 FATAL ((stderr, _("invalid value for ^@bggray escape: %s"), buf));
1538 token->type = tNONE;
1542 if (strcmp (buf, "default") == 0)
1543 escape_char = default_escape_char;
1545 escape_char = atoi (buf);
1546 token->type = tNONE;
1549 case ESC_SETFILENAME:
1550 memset (token->u.filename, 0, sizeof(token->u.filename));
1551 strncpy (token->u.filename, buf, sizeof(token->u.filename) - 1);
1552 token->type = tSETFILENAME;
1555 case ESC_SETPAGENUMBER:
1556 token->u.i = atoi (buf);
1557 token->type = tSETPAGENUMBER;
1562 token->u.i = 1; /* The default is the first line. */
1564 token->u.i = atoi (buf);
1565 token->type = tNEWPAGE;
1569 token->type = tSAVEX;
1570 token->u.i = atoi (buf);
1574 token->type = tLOADX;
1575 token->u.i = atoi (buf);
1579 token->u.str = xstrdup (buf);
1592 /* Get next token from input file <fp>. */
1594 get_next_token (InputStream *is, double linestart, double linepos,
1595 unsigned int col, double linew, Token *token)
1597 static unsigned char *buffer = NULL; /* output buffer */
1598 static unsigned int buflen = 0; /* output buffer's length */
1599 unsigned int bufpos = 0; /* current position in output buffer */
1603 static int pending_token = tNONE;
1604 unsigned int original_col = col;
1606 if (pending_token != tNONE)
1608 token->type = pending_token;
1609 pending_token = tNONE;
1622 if (BUFFER_EMPTY ())
1634 * One of these is the newline character and the other one
1635 * is carriage return.
1639 /* The newline character. */
1640 if (BUFFER_EMPTY ())
1642 token->type = tNEWLINE;
1653 /* The carriage return character. */
1654 if (BUFFER_EMPTY ())
1656 token->type = tCARRIAGE_RETURN;
1670 i = tabsize - (col % tabsize);
1673 if (FITS_ON_LINE (' '))
1684 /* Proportional font. */
1686 double grid = tabsize * CHAR_WIDTH (' ');
1689 /* Move linepos to the next multiple of <grid>. */
1690 linepos = (((int) ((linepos - linestart) / grid) + 1) * grid
1692 if (linepos >= linew)
1700 if (BUFFER_EMPTY ())
1702 if (interpret_formfeed)
1703 token->type = tFORMFEED;
1705 token->type = tNEWLINE;
1716 /* Handle special escapes. */
1717 if (special_escapes && ch == escape_char)
1719 if (BUFFER_EMPTY ())
1721 /* Interpret special escapes. */
1722 read_special_escape (is, token);
1723 if (token->type != tNONE)
1727 * Got tNONE special escape => read_special_escape()
1728 * has already done what was needed. Just read more.
1740 /* Handle backspace character. */
1743 if (BUFFER_EMPTY () || !EXISTS (buffer[bufpos - 1]))
1744 linepos -= CHAR_WIDTH ('m');
1746 linepos -= CHAR_WIDTH (buffer[bufpos - 1]);
1752 /* Check normal characters. */
1755 if (FITS_ON_LINE (ch))
1758 * Print control characters (and optionally
1759 * characters greater than 127) in the escaped form
1760 * so PostScript interpreter will not hang on them.
1762 if (ch < 040 || (clean_7bit && ch >= 0200))
1766 sprintf (buf, "\\%03o", ch);
1767 for (i = 0; buf[i]; i++)
1768 APPEND_CHAR (buf[i]);
1770 /* Update current point counters manually. */
1771 linepos += CHAR_WIDTH (ch);
1774 else if (ch == '(' || ch == ')' || ch == '\\')
1776 /* These must be quoted in PostScript strings. */
1789 else if (ISPRINT (ch))
1791 /* Printable, but do not exists in this font. */
1792 if (FITS_ON_LINE ('?'))
1795 if (missing_chars[ch]++ == 0)
1796 num_missing_chars++;
1810 * Non-printable and does not exist in current font, print
1811 * it in the format specified by non_printable_format.
1814 if (non_printable_chars[ch]++ == 0)
1815 num_non_printable_chars++;
1817 switch (non_printable_format)
1823 case NPF_QUESTIONMARK:
1838 sprintf (buf, "\\%03o", ch);
1843 for (i = 0; buf[i]; i++)
1844 len += CHAR_WIDTH (buf[i]);
1846 if (linepos + len < linew || col == 0)
1849 for (i = 0; buf[i]; i++)
1852 APPEND_CHAR ('\\'); /* Escape '\\' characters. */
1868 /* Check for wrapped line. */
1869 if (done == DONE_WRAP)
1871 /* This line is too long. */
1873 if (line_end == LE_TRUNCATE)
1875 /* Truncate this line. */
1876 while ((ch = is_getc (is)) != EOF && ch != nl)
1879 else if (!BUFFER_EMPTY () && line_end == LE_WORD_WRAP)
1883 if (ISSPACE (buffer[bufpos - 1]))
1885 /* Skip all whitespace from the end of the wrapped line. */
1886 while ((w = is_getc (is)) != EOF && ISSPACE (w))
1892 /* Find the previous word boundary for the wrap. */
1893 for (w = bufpos - 1; w >= 0 && !ISSPACE (buffer[w]); w--)
1896 if (w > 0 || original_col > 0)
1899 * Ok, we found a word boundary. Now we must unemit
1900 * characters from the buffer to the intput stream.
1903 * - bufpos is unsigned integer variable
1904 * - some characters are escaped with '\\'
1905 * - some characters are printed in octal notation
1911 /* Check for '(', ')' and '\\'. */
1913 && (buffer[bufpos] == '('
1914 || buffer[bufpos] == ')'
1915 || buffer[bufpos] == '\\')
1916 && buffer[bufpos - 1] == '\\')
1918 is_ungetc (buffer[bufpos], is);
1919 UNEMIT (buffer[bufpos]);
1922 /* Check the octal notations "\\%03o". */
1923 else if (bufpos - 2 > w
1924 && ISOCTAL (buffer[bufpos])
1925 && ISOCTAL (buffer[bufpos - 1])
1926 && ISOCTAL (buffer[bufpos - 2])
1927 && buffer[bufpos - 3] == '\\')
1932 * It is a potential octal character. Now we
1933 * must process the buffer from the beginning
1934 * and see if `bufpos - 3' really starts a character.
1936 for (ti = w; ti < bufpos - 3; ti++)
1938 if (buffer[ti] == '\\')
1940 if (ISOCTAL (buffer[ti + 1]))
1945 tti < 3 && ISOCTAL (buffer[ti + 1]);
1950 /* Simple escape. */
1956 * If <ti> is equal to <bufpos - 3>, we found
1957 * an octal character, otherwise the leading
1958 * backslash at <bufpos - 3> belongs to the
1959 * previous character.
1961 if (ti == bufpos - 3)
1965 tch = (((buffer[bufpos - 2] - '0') << 6)
1966 + ((buffer[bufpos - 1] - '0') << 3)
1967 + (buffer[bufpos] - '0'));
1968 is_ungetc (tch, is);
1973 /* Normal character. */
1978 /* Normal character, just unget it. */
1980 is_ungetc (buffer[bufpos], is);
1981 UNEMIT (buffer[bufpos]);
1991 if (line_end == LE_TRUNCATE)
1994 num_truncated_lines++;
1995 pending_token = tNEWLINE;
1998 pending_token = tWRAPPED_NEWLINE;
2001 pending_token = tEOF;
2005 token->type = tSTRING;
2006 token->u.str = (char *) buffer;
2007 token->new_x = linepos;
2008 token->new_col = col;
2013 dump_ps_page_header (char *fname, int empty)
2015 char *dirc, *basec, *fdir, *ftail;
2019 unsigned int nup_subpage;
2021 /* The N-up printing sub-page. */
2022 nup_subpage = (total_pages - 1) % nup;
2024 /* Split fname into fdir and ftail. */
2025 dirc = strdup(fname);
2026 basec = strdup(fname);
2027 fdir = dirname(dirc);
2028 ftail = basename(basec);
2032 /* N-up printing is active. */
2035 if (nup_subpage == 0)
2037 /* This is a real page start. */
2042 OUTPUT ((cofp, "%%%%Page: (%d-%d) %d\n", current_pagenum,
2043 current_pagenum + nup - 1, total_pages / nup + 1));
2047 OUTPUT ((cofp, "%%%%Page: (%s:%3d-%3d) %d\n", ftail,
2048 current_pagenum, current_pagenum + nup - 1,
2049 total_pages / nup + 1));
2054 OUTPUT ((cofp, "%%%%BeginPageSetup\n_S\n"));
2056 if ((total_pages / nup + 1) % 2 == 0)
2057 /* Two-side binding options for the even pages. */
2058 handle_two_side_options ();
2060 #define PRINT_BOUNDING_BOXES 0
2062 #if PRINT_BOUNDING_BOXES
2064 "%d %d moveto %d %d lineto %d %d lineto %d %d lineto closepath stroke\n",
2065 media->llx, media->lly, media->llx, media->ury,
2066 media->urx, media->ury, media->urx, media->lly));
2072 OUTPUT ((cofp, "90 rotate\n%d %d translate\n",
2073 media->lly, -media->urx));
2075 OUTPUT ((cofp, "%d %d translate\n", media->llx, media->lly));
2080 OUTPUT ((cofp, "90 rotate\n%d %d translate\n",
2081 media->lly, -media->llx));
2083 OUTPUT ((cofp, "%d %d translate\n", media->llx, media->ury));
2088 /* Page start comment. */
2092 OUTPUT ((cofp, "%sPage: (%d) %d\n", cstr, current_pagenum, total_pages));
2096 OUTPUT ((cofp, "%sPage: (%s:%3d) %d\n", cstr, ftail, current_pagenum,
2105 OUTPUT ((cofp, "%sBeginPageSetup\n_S\n", cstr));
2111 OUTPUT ((cofp, "%% N-up sub-page %d/%d\n", nup_subpage + 1, nup));
2116 xm = nup_subpage % nup_columns;
2117 ym = nup_subpage / nup_columns;
2121 xm = nup_subpage / nup_rows;
2122 ym = nup_subpage % nup_rows;
2125 OUTPUT ((cofp, "%d %d translate\n",
2126 xm * (nup_width + nup_xpad),
2127 ym * (nup_height + nup_ypad)));
2133 xm = nup_subpage / nup_rows;
2134 ym = nup_subpage % nup_rows;
2138 xm = nup_subpage % nup_columns;
2139 ym = nup_subpage / nup_columns;
2142 OUTPUT ((cofp, "%d %d translate\n",
2143 xm * (nup_width + nup_xpad),
2144 -((int) (ym * (nup_height + nup_ypad) + nup_height))));
2146 OUTPUT ((cofp, "%g dup scale\n", nup_scale));
2148 /* And finally, the real page setup. */
2150 OUTPUT ((cofp, "90 rotate\n%d %d translate\n", 0, -d_page_h));
2154 /* No N-up printing. */
2156 if (total_pages % 2 == 0)
2157 /* Two-side binding options for the even pages. */
2158 handle_two_side_options ();
2161 OUTPUT ((cofp, "90 rotate\n%d %d translate\n",
2162 media->lly, -media->urx));
2164 OUTPUT ((cofp, "%d %d translate\n", media->llx, media->lly));
2167 /* Some constants etc. */
2168 OUTPUT ((cofp, "/pagenum %d def\n", current_pagenum));
2170 cp = escape_string (fname);
2171 OUTPUT ((cofp, "/fname (%s) def\n", cp));
2174 cp = escape_string (fdir);
2175 OUTPUT ((cofp, "/fdir (%s) def\n", cp));
2179 cp = escape_string (ftail);
2180 OUTPUT ((cofp, "/ftail (%s) def\n", cp));
2184 /* Do we have a pending ^@font{} font? */
2187 if (encoding == default_Fencoding)
2188 OUTPUT ((cofp, "/%s %g %g SUF\n", Fname, Fpt.w, Fpt.h));
2190 /* This must be the case. */
2191 OUTPUT ((cofp, "/%s %g %g SUF_PS\n", Fname, Fpt.w, Fpt.h));
2194 /* Dump user defined strings. */
2195 if (count_key_value_set (user_strings) > 0)
2197 OUTPUT ((cofp, "%% User defined strings:\n"));
2198 for (got = strhash_get_first (user_strings, &cp, &i, (void **) &cp2);
2200 got = strhash_get_next (user_strings, &cp, &i, (void **) &cp2))
2202 cp2 = format_user_string ("%Format", cp2);
2203 OUTPUT ((cofp, "/%s (%s) def\n", cp, cp2));
2208 /* User supplied header? */
2213 char *h_right = NULL;
2215 h_left = format_user_string ("page header", page_header);
2216 h_center = strchr (h_left, '|');
2222 h_right = strchr (h_center, '|');
2230 OUTPUT ((cofp, "/user_header_p true def\n"));
2231 OUTPUT ((cofp, "/user_header_left_str (%s) def\n", h_left));
2232 OUTPUT ((cofp, "/user_header_center_str (%s) def\n",
2233 h_center ? h_center : ""));
2234 OUTPUT ((cofp, "/user_header_right_str (%s) def\n",
2235 h_right ? h_right : ""));
2239 OUTPUT ((cofp, "/user_header_p false def\n"));
2241 /* User supplied footer? */
2246 char *f_right = NULL;
2248 f_left = format_user_string ("page footer", page_footer);
2249 f_center = strchr (f_left, '|');
2255 f_right = strchr (f_center, '|');
2263 OUTPUT ((cofp, "/user_footer_p true def\n"));
2264 OUTPUT ((cofp, "/user_footer_left_str (%s) def\n", f_left));
2265 OUTPUT ((cofp, "/user_footer_center_str (%s) def\n",
2266 f_center ? f_center : ""));
2267 OUTPUT ((cofp, "/user_footer_right_str (%s) def\n",
2268 f_right ? f_right : ""));
2272 OUTPUT ((cofp, "/user_footer_p false def\n"));
2274 OUTPUT ((cofp, "%%%%EndPageSetup\n"));
2277 * Mark standard page decorations.
2282 /* Highlight bars. */
2284 OUTPUT ((cofp, "%d %f %d %f highlight_bars\n", highlight_bars,
2285 LINESKIP, d_output_y_margin, highlight_bar_gray));
2288 if (underlay != NULL)
2290 if (ul_position_p || ul_angle_p)
2291 OUTPUT ((cofp, "user_underlay\n"));
2293 OUTPUT ((cofp, "underlay\n"));
2297 if (num_columns > 1 && (header == HDR_FANCY || borders))
2298 OUTPUT ((cofp, "column_lines\n"));
2300 /* Borders around columns. */
2302 OUTPUT ((cofp, "column_borders\n"));
2312 OUTPUT ((cofp, "do_header\n"));
2317 /* Do we have a pending ^@color{} color? */
2319 OUTPUT ((cofp, "%g %g %g setrgbcolor\n", user_color.r, user_color.g,
2325 dump_ps_page_trailer ()
2327 unsigned int nup_subpage = (total_pages - 1) % nup;
2329 OUTPUT ((cofp, "_R\n"));
2333 if (nup_subpage + 1 == nup)
2334 /* Real end of page. */
2335 OUTPUT ((cofp, "_R\nS\n"));
2338 OUTPUT ((cofp, "S\n"));
2347 unsigned int nup_subpage = (total_pages - 1) % nup;
2349 if (nup_subpage == 0)
2351 /* Real start of the page, must do it the harder way. */
2352 dump_ps_page_header ("", 1);
2353 OUTPUT ((cofp, "_R\n"));
2356 OUTPUT ((cofp, "%%Page: (-) %d\n", total_pages));
2358 if (nup_subpage + 1 == nup)
2359 /* This is the last page on this sheet, dump us. */
2360 OUTPUT ((cofp, "_R\nS\n"));
2363 OUTPUT ((cofp, "%%%%Page: (-) %d\nS\n", total_pages));
2368 recognize_eps_file (Token *token)
2375 float llx, lly, urx, ury;
2377 MESSAGE (2, (stderr, "^@epsf=\"%s\"\n", token->u.epsf.filename));
2379 i = strlen (token->u.epsf.filename);
2381 /* Read EPS data from file. */
2382 filename = tilde_subst (token->u.epsf.filename);
2384 token->u.epsf.fp = fopen (filename, "rb");
2387 if (token->u.epsf.fp == NULL)
2389 if (token->u.epsf.filename[0] != '/')
2391 /* Name is not absolute, let's lookup path. */
2394 ctx.name = token->u.epsf.filename;
2396 ctx.fullname = buffer_alloc ();
2398 if (pathwalk (libpath, file_lookup, &ctx))
2399 token->u.epsf.fp = fopen (buffer_ptr (ctx.fullname), "rb");
2401 buffer_free (ctx.fullname);
2403 if (token->u.epsf.fp == NULL)
2405 MESSAGE (0, (stderr, _("couldn't open EPS file \"%s\": %s\n"),
2406 token->u.epsf.filename, strerror (errno)));
2411 /* Find BoundingBox DSC comment. */
2415 token->u.epsf.skipbuf = NULL;
2416 token->u.epsf.skipbuf_len = 0;
2417 token->u.epsf.skipbuf_pos = 0;
2419 while (fgets (buf, sizeof (buf), token->u.epsf.fp))
2423 /* Append data to the skip buffer. */
2425 if (i + token->u.epsf.skipbuf_pos >= token->u.epsf.skipbuf_len)
2427 token->u.epsf.skipbuf_len += 8192;
2428 token->u.epsf.skipbuf = xrealloc (token->u.epsf.skipbuf,
2429 token->u.epsf.skipbuf_len);
2431 memcpy (token->u.epsf.skipbuf + token->u.epsf.skipbuf_pos, buf, i);
2432 token->u.epsf.skipbuf_pos += i;
2434 /* Check the "%!" magic cookie. */
2437 if (buf[0] != '%' || buf[1] != '!')
2441 _("EPS file \"%s\" does not start with \"%%!\" magic\n"),
2442 token->u.epsf.filename));
2447 #define BB_DSC "%%BoundingBox:"
2449 if (strncmp (buf, BB_DSC, strlen (BB_DSC)) == 0)
2451 i = sscanf (buf + strlen (BB_DSC), "%f %f %f %f",
2452 &llx, &lly, &urx, &ury);
2457 /* Skip possible whitespace. */
2458 for (i = strlen (BB_DSC);
2459 buf[i] && (buf[i] == ' ' || buf[i] == '\t');
2462 #define BB_DSC_ATEND "(atend)"
2463 if (strncmp (buf + i, BB_DSC_ATEND, strlen (BB_DSC_ATEND)) != 0)
2465 /* No, this BoundingBox comment is corrupted. */
2466 MESSAGE (0, (stderr, _("EPS file \"%s\" contains malformed \
2467 %%%%BoundingBox row:\n\"%.*s\"\n"),
2468 token->u.epsf.filename, strlen (buf) - 1, buf));
2474 /* It was a valid EPS file. */
2476 /* We store bounding box in int format. */
2477 token->u.epsf.llx = llx;
2478 token->u.epsf.lly = lly;
2479 token->u.epsf.urx = urx;
2480 token->u.epsf.ury = ury;
2488 /* Check that we found the BoundingBox comment. */
2491 MESSAGE (0, (stderr, _("EPS file \"%s\" is not a valid EPS file\n"),
2492 token->u.epsf.filename));
2493 if (token->u.epsf.pipe)
2494 pclose (token->u.epsf.fp);
2496 fclose (token->u.epsf.fp);
2497 xfree (token->u.epsf.skipbuf);
2501 MESSAGE (2, (stderr, "BoundingBox: %d %d %d %d\n",
2502 token->u.epsf.llx, token->u.epsf.lly,
2503 token->u.epsf.urx, token->u.epsf.ury));
2510 paste_epsf (Token *token)
2515 /* EPSF import header. */
2516 OUTPUT ((cofp, "BeginEPSF\n"));
2517 OUTPUT ((cofp, "%g %g translate\n", token->new_x, token->new_y));
2518 OUTPUT ((cofp, "%g %g scale\n", token->u.epsf.xscale, token->u.epsf.yscale));
2519 OUTPUT ((cofp, "%d %d translate\n", -token->u.epsf.llx,
2520 -token->u.epsf.lly));
2521 OUTPUT ((cofp, "%d %d %d %d Box clip newpath\n",
2522 token->u.epsf.llx - 1,
2523 token->u.epsf.lly - 1,
2524 token->u.epsf.urx - token->u.epsf.llx + 2,
2525 token->u.epsf.ury - token->u.epsf.lly + 2));
2526 OUTPUT ((cofp, "%%%%BeginDocument: %s%s\n", token->u.epsf.filename,
2527 token->u.epsf.pipe ? "|" : ""));
2531 /* Dump skip buffer. */
2532 fwrite (token->u.epsf.skipbuf, 1, token->u.epsf.skipbuf_pos, cofp);
2535 while ((i = fread (buf, 1, sizeof (buf), token->u.epsf.fp)) != 0)
2536 fwrite (buf, 1, i, cofp);
2539 /* Add a newline to keep comments correct */
2540 OUTPUT ((cofp, "\n"));
2542 /* EPSF import trailer. */
2543 OUTPUT ((cofp, "%%%%EndDocument\nEndEPSF\n"));
2546 if (token->u.epsf.pipe)
2547 pclose (token->u.epsf.fp);
2549 fclose (token->u.epsf.fp);
2550 xfree (token->u.epsf.skipbuf);
2555 read_float (InputStream *is, int units, int horizontal)
2561 for (i = 0; (i < sizeof (buf) - 1
2562 && (ch = is_getc (is)) != EOF
2563 && ISNUMBERDIGIT (ch));
2578 case 'c': /* centimeters */
2582 case 'p': /* PostScript points */
2585 case 'i': /* inches */
2593 case 'l': /* lines or characters */
2595 val *= CHAR_WIDTH ('m');
2606 /* Magics used to recognize different pass-through files. */
2610 unsigned int magiclen;
2613 } pass_through_magics[] =
2615 {"%!", 2, "PostScript", -2},
2616 {"\004%!", 3, "PostScript", -2},
2617 {"\033E", 2, "PCL", -2},
2618 {"\033%", 2, "PCL", -2},
2624 do_pass_through (char *fname, InputStream *is)
2627 unsigned long saved_pos = is->bufpos;
2630 if (output_language_pass_through)
2633 _("passing through all input files for output language `%s'\n"),
2638 * Try to recognize pass-through files.
2641 for (i = 0; pass_through_magics[i].magic; i++)
2643 for (j = 0; j < pass_through_magics[i].magiclen; j++)
2647 || ch != (unsigned char) pass_through_magics[i].magic[j])
2651 if (j >= pass_through_magics[i].magiclen)
2652 /* The <i>th one matched. */
2656 * Try the next one, but first, seek the input stream to its
2659 is->bufpos = saved_pos;
2662 /* Did we find any? */
2663 if (pass_through_magics[i].magic == NULL)
2667 /* Yes, it really is a pass-through file. Now do the pass through. */
2669 is->bufpos += pass_through_magics[i].revert_delta;
2671 if (ps_header_dumped)
2673 /* A pass-through file between normal ASCII files, obey DSC. */
2676 * XXX I don't know how to handle PCL files... Let's hope none
2677 * mixes them with the normal ASCII files.
2681 "%%%%Page: (%s) -1\n_S\n%%%%BeginDocument: %s\n",
2685 MESSAGE (1, (stderr, _("passing through %s file \"%s\"\n"),
2686 pass_through_magics[i].name, fname));
2689 /* And now, do the actual pass-through. */
2692 /* Note: this will be written directly to the <ofp>. */
2693 fwrite (is->buf + is->bufpos, 1, is->data_in_buf - is->bufpos, ofp);
2694 is->bufpos = is->data_in_buf;
2696 /* Read more data to the input buffer. */
2702 if (!output_language_pass_through)
2704 if (ps_header_dumped)
2706 * XXX How to end a PCL file mixed between ASCII files?
2708 OUTPUT ((cofp, "%%%%EndDocument\n_R\n"));
2716 print_line_number (double x, double y, double space, double margin,
2717 unsigned int linenum)
2722 char *saved_Fname = "";
2723 FontPoint saved_Fpt;
2724 InputEncoding saved_Fencoding;
2729 /* Do not print linenumbers for wrapped lines. */
2730 if (linenum == print_line_number_last)
2732 print_line_number_last = linenum;
2736 /* Re-select our default typing font. */
2737 saved_Fname = Fname;
2738 saved_Fpt.w = Fpt.w;
2739 saved_Fpt.h = Fpt.h;
2740 saved_Fencoding = encoding;
2742 Fname = default_Fname;
2743 Fpt.w = default_Fpt.w;
2744 Fpt.h = default_Fpt.h;
2745 encoding = default_Fencoding;
2747 OUTPUT ((cofp, "/F-gs-font %g %g SF\n", Fpt.w, Fpt.h));
2751 /* Count linenumber string length. */
2752 sprintf (buf, "%d", linenum);
2753 for (i = 0; buf[i]; i++)
2754 len += CHAR_WIDTH (buf[i]);
2756 /* Print line numbers. */
2757 OUTPUT ((cofp, "%g %g M (%s:) s\n", x + space - len, y, buf));
2761 /* Switch back to the user font. */
2762 Fname = saved_Fname;
2763 Fpt.w = saved_Fpt.w;
2764 Fpt.h = saved_Fpt.h;
2765 encoding = saved_Fencoding;
2767 OUTPUT ((cofp, "/%s %g %g SUF\n", Fname, Fpt.w, Fpt.h));
2774 * The name of the divert file, shared between divert() and undivert()
2777 static char divertfname[512];
2782 assert (divertfp == NULL);
2784 /* Open divert file. */
2786 divertfp = tmpfile ();
2787 if (divertfp == NULL)
2788 FATAL ((stderr, _("couldn't create temporary divert file: %s"),
2802 assert (divertfp != NULL);
2804 if (fseek (divertfp, 0, SEEK_SET) != 0)
2805 FATAL ((stderr, _("couldn't rewind divert file: %s"), strerror (errno)));
2807 while (fgets (buf, sizeof (buf), divertfp))
2809 if (strncmp (buf, "%%BeginDocument", 15) == 0)
2811 else if (strncmp (buf, "%%EndDocument", 13) == 0)
2816 if (strncmp (buf, "% User defined strings", 22) == 0)
2819 while (fgets (buf, sizeof (buf), divertfp))
2821 if (strncmp (buf, "%%EndPageSetup", 14) == 0)
2824 /* Patch total pages to the user defined strings. */
2825 cp = strchr (buf, '\001');
2830 fprintf (ofp, "%d", total_pages_in_file);
2831 fputs (cp + 1, ofp);
2850 handle_two_side_options ()
2852 if (rotate_even_pages)
2853 /* Rotate page 180 degrees. */
2854 OUTPUT ((cofp, "180 rotate\n%d %d translate\n",
2855 -media->w, -media->h));
2857 if (swap_even_page_margins)
2858 OUTPUT ((cofp, "%d 0 translate\n",
2859 -(media->llx - (media->w - media->urx))));