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