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