BSD 4_3_Net_2 release
[unix-history] / usr / src / usr.bin / groff / grops / ps.cc
CommitLineData
ca16ec82
C
1// -*- C++ -*-
2/* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.uucp)
4
5This file is part of groff.
6
7groff is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 1, or (at your option) any later
10version.
11
12groff is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15for more details.
16
17You should have received a copy of the GNU General Public License along
18with groff; see the file LICENSE. If not, write to the Free Software
19Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
20
21#include "driver.h"
22#include "stringclass.h"
23#include "cset.h"
24
25extern "C" {
26 // Sun's stdlib.h fails to declare this.
27 char *mktemp(char *);
28}
29
30static int landscape_flag = 0;
31static int ncopies = 1;
32static int linewidth = -1;
33
34#define DEFAULT_LINEWIDTH 40 /* in ems/1000 */
35#define FILL_MAX 1000
36
37// Maximum number of definitions in the prologue (used for sizing the
38// dictionary.)
39
40const int MAX_PROLOGUE_DEFS = 50;
41const char *const dict_name = "grops";
42const char *const defs_dict_name = "DEFS";
43const int DEFS_DICT_SPARE = 50;
44
45#define PROLOGUE "prologue"
46
47double degrees(double r)
48{
49 return r*180.0/M_PI;
50}
51
52double radians(double d)
53{
54 return d*M_PI/180.0;
55}
56
57inline double transform_fill(int fill)
58{
59 return 1 - fill/double(FILL_MAX);
60}
61
62class ps_output {
63public:
64 ps_output(FILE *, int max_line_length);
65 ps_output &put_string(const char *, int);
66 ps_output &put_number(int);
67 ps_output &put_fix_number(int);
68 ps_output &put_float(double);
69 ps_output &put_symbol(const char *);
70 ps_output &put_literal_symbol(const char *);
71 ps_output &set_fixed_point(int);
72 ps_output &simple_comment(const char *);
73 ps_output &begin_comment(const char *);
74 ps_output &comment_arg(const char *);
75 ps_output &end_comment();
76 ps_output &set_file(FILE *);
77 ps_output &include_file(FILE *);
78 ps_output &copy_file(FILE *);
79 ps_output &end_line();
80 ps_output &put_delimiter(char);
81 ps_output &special(const char *);
82private:
83 FILE *fp;
84 int col;
85 int max_line_length; // not including newline
86 int need_space;
87 int fixed_point;
88};
89
90ps_output::ps_output(FILE *f, int n)
91: fp(f), max_line_length(n), col(0), need_space(0), fixed_point(0)
92{
93}
94
95ps_output &ps_output::set_file(FILE *f)
96{
97 fp = f;
98 col = 0;
99 return *this;
100}
101
102ps_output &ps_output::include_file(FILE *infp)
103{
104 if (col != 0)
105 putc('\n', fp);
106 int c;
107#ifdef BROKEN_SPOOLER
108 // Strip the first line if it's a comment. I believe
109 // some spoolers get confused by %! in the middle of a file.
110 if ((c = getc(infp)) == '%') {
111 while ((c = getc(infp)) != '\n' && c != '\r' && c != EOF)
112 ;
113 }
114 else if (c != EOF)
115 ungetc(c, infp);
116#endif /* BROKEN_SPOOLER */
117 // We strip out lines beginning with STRIPENDUM.
118#ifdef BROKEN_SPOOLER
119#define STRIPENDUM "%%"
120#else
121#define STRIPENDUM "%%IncludeFont:"
122#endif
123 // Index into STRIPENDUM of next character to be matched.
124 int match = 0;
125 while ((c = getc(infp)) != EOF) {
126 if (match >= 0) {
127 if (c == STRIPENDUM[match]) {
128 if (++match == sizeof(STRIPENDUM) - 1) {
129 match = -1;
130 while ((c = getc(infp)) != EOF)
131 if (c == '\r' || c == '\n') {
132 match = 0;
133 break;
134 }
135 }
136 }
137 else {
138 for (int i = 0; i < match; i++)
139 putc(STRIPENDUM[i], fp);
140 putc(c, fp);
141 match = (c == '\n' || c == '\r' ? 0 : -1);
142 }
143 }
144 else {
145 putc(c, fp);
146 match = (c == '\n' || c == '\r' ? 0 : -1);
147 }
148 }
149 for (int i = 0; i < match; i++)
150 putc(STRIPENDUM[i], fp);
151 if (match != 0)
152 putc('\n', fp);
153 int lastc = '\n';
154 while ((c = getc(infp)) != EOF) {
155 putc(c, fp);
156 lastc = c;
157 }
158 if (lastc != '\n')
159 putc('\n', fp);
160 col = 0;
161 need_space = 0;
162 return *this;
163}
164
165ps_output &ps_output::copy_file(FILE *infp)
166{
167 int c;
168 while ((c = getc(infp)) != EOF)
169 putc(c, fp);
170 return *this;
171}
172
173ps_output &ps_output::end_line()
174{
175 if (col != 0) {
176 putc('\n', fp);
177 col = 0;
178 }
179 return *this;
180}
181
182ps_output &ps_output::special(const char *s)
183{
184 if (s == 0 || *s == '\0')
185 return *this;
186 if (col != 0) {
187 putc('\n', fp);
188 col = 0;
189 }
190 fputs(s, fp);
191 if (strchr(s, '\0')[-1] != '\n')
192 putc('\n', fp);
193 need_space = 0;
194 return *this;
195}
196
197ps_output &ps_output::simple_comment(const char *s)
198{
199 if (col != 0)
200 putc('\n', fp);
201 putc('%', fp);
202 putc('%', fp);
203 fputs(s, fp);
204 putc('\n', fp);
205 col = 0;
206 need_space = 0;
207 return *this;
208}
209
210ps_output &ps_output::begin_comment(const char *s)
211{
212 if (col != 0)
213 putc('\n', fp);
214 putc('%', fp);
215 putc('%', fp);
216 fputs(s, fp);
217 col = 2 + strlen(s);
218 return *this;
219}
220
221ps_output &ps_output::end_comment()
222{
223 if (col != 0) {
224 putc('\n', fp);
225 col = 0;
226 }
227 need_space = 0;
228 return *this;
229}
230
231ps_output &ps_output::comment_arg(const char *s)
232{
233 int len = strlen(s);
234 if (col + len + 1 > max_line_length) {
235 putc('\n', fp);
236 fputs("%%+", fp);
237 col = 3;
238 }
239 putc(' ', fp);
240 fputs(s, fp);
241 col += len + 1;
242 return *this;
243}
244
245ps_output &ps_output::set_fixed_point(int n)
246{
247 assert(n >= 0 && n <= 10);
248 fixed_point = n;
249 return *this;
250}
251
252ps_output &ps_output::put_delimiter(char c)
253{
254 if (col + 1 > max_line_length) {
255 putc('\n', fp);
256 col = 0;
257 }
258 putc(c, fp);
259 col++;
260 need_space = 0;
261 return *this;
262}
263
264ps_output &ps_output::put_string(const char *s, int n)
265{
266 int len = 0;
267 for (int i = 0; i < n; i++) {
268 char c = s[i];
269 if (isascii(c) && isprint(c)) {
270 if (c == '(' || c == ')' || c == '\\')
271 len += 2;
272 else
273 len += 1;
274 }
275 else
276 len += 4;
277 }
278 if (len > n*2) {
279 if (col + n*2 + 2 > max_line_length && n*2 + 2 <= max_line_length) {
280 putc('\n', fp);
281 col = 0;
282 }
283 if (col + 1 > max_line_length) {
284 putc('\n', fp);
285 col = 0;
286 }
287 putc('<', fp);
288 col++;
289 for (i = 0; i < n; i++) {
290 if (col + 2 > max_line_length) {
291 putc('\n', fp);
292 col = 0;
293 }
294 fprintf(fp, "%02x", s[i] & 0377);
295 col += 2;
296 }
297 putc('>', fp);
298 col++;
299 }
300 else {
301 if (col + len + 2 > max_line_length && len + 2 <= max_line_length) {
302 putc('\n', fp);
303 col = 0;
304 }
305 if (col + 2 > max_line_length) {
306 putc('\n', fp);
307 col = 0;
308 }
309 putc('(', fp);
310 col++;
311 for (i = 0; i < n; i++) {
312 char c = s[i];
313 if (isascii(c) && isprint(c)) {
314 if (c == '(' || c == ')' || c == '\\')
315 len = 2;
316 else
317 len = 1;
318 }
319 else
320 len = 4;
321 if (col + len + 1 > max_line_length) {
322 putc('\\', fp);
323 putc('\n', fp);
324 col = 0;
325 }
326 switch (len) {
327 case 1:
328 putc(c, fp);
329 break;
330 case 2:
331 putc('\\', fp);
332 putc(c, fp);
333 break;
334 case 4:
335 fprintf(fp, "\\%03o", c & 0377);
336 break;
337 default:
338 assert(0);
339 }
340 col += len;
341 }
342 putc(')', fp);
343 col++;
344 }
345 need_space = 0;
346 return *this;
347}
348
349ps_output &ps_output::put_number(int n)
350{
351 char buf[1 + INT_DIGITS + 1];
352 sprintf(buf, "%d", n);
353 int len = strlen(buf);
354 if (col > 0 && col + len + need_space > max_line_length) {
355 putc('\n', fp);
356 col = 0;
357 need_space = 0;
358 }
359 if (need_space) {
360 putc(' ', fp);
361 col++;
362 }
363 fputs(buf, fp);
364 col += len;
365 need_space = 1;
366 return *this;
367}
368
369
370ps_output &ps_output::put_fix_number(int i)
371{
372 const char *p = iftoa(i, fixed_point);
373 int len = strlen(p);
374 if (col > 0 && col + len + need_space > max_line_length) {
375 putc('\n', fp);
376 col = 0;
377 need_space = 0;
378 }
379 if (need_space) {
380 putc(' ', fp);
381 col++;
382 }
383 fputs(p, fp);
384 col += len;
385 need_space = 1;
386 return *this;
387}
388
389ps_output &ps_output::put_float(double d)
390{
391 char buf[128];
392 sprintf(buf, "%.4f", d);
393 int len = strlen(buf);
394 if (col > 0 && col + len + need_space > max_line_length) {
395 putc('\n', fp);
396 col = 0;
397 need_space = 0;
398 }
399 if (need_space) {
400 putc(' ', fp);
401 col++;
402 }
403 fputs(buf, fp);
404 col += len;
405 need_space = 1;
406 return *this;
407}
408
409ps_output &ps_output::put_symbol(const char *s)
410{
411 int len = strlen(s);
412 if (col > 0 && col + len + need_space > max_line_length) {
413 putc('\n', fp);
414 col = 0;
415 need_space = 0;
416 }
417 if (need_space) {
418 putc(' ', fp);
419 col++;
420 }
421 fputs(s, fp);
422 col += len;
423 need_space = 1;
424 return *this;
425}
426
427ps_output &ps_output::put_literal_symbol(const char *s)
428{
429 int len = strlen(s);
430 if (col > 0 && col + len + 1 > max_line_length) {
431 putc('\n', fp);
432 col = 0;
433 }
434 putc('/', fp);
435 fputs(s, fp);
436 col += len + 1;
437 need_space = 1;
438 return *this;
439}
440
441class ps_font : public font {
442 ps_font(const char *);
443public:
444 int encoding_index;
445 char *encoding;
446 char *reencoded_name;
447 ~ps_font();
448 void handle_unknown_font_command(int argc, const char **argv);
449 static ps_font *load_ps_font(const char *);
450};
451
452ps_font *ps_font::load_ps_font(const char *s)
453{
454 ps_font *f = new ps_font(s);
455 if (!f->load()) {
456 delete f;
457 return 0;
458 }
459 return f;
460}
461
462ps_font::ps_font(const char *nm)
463: font(nm), encoding(0), reencoded_name(0), encoding_index(-1)
464{
465}
466
467ps_font::~ps_font()
468{
469 delete encoding;
470 delete reencoded_name;
471}
472
473void ps_font::handle_unknown_font_command(int argc, const char **argv)
474{
475 if (strcmp(argv[0], "encoding") == 0) {
476 if (argc != 2)
477 error("`encoding' command requires exactly 1 argument");
478 else
479 encoding = strsave(argv[1]);
480 }
481}
482
483
484struct style {
485 font *f;
486 int point_size;
487 int height;
488 int slant;
489 style();
490 style(font *, int, int, int);
491 int operator==(const style &) const;
492 int operator!=(const style &) const;
493};
494
495style::style() : f(0)
496{
497}
498
499style::style(font *p, int sz, int h, int sl)
500: f(p), point_size(sz), height(h), slant(sl)
501{
502}
503
504int style::operator==(const style &s) const
505{
506 return (f == s.f && point_size == s.point_size
507 && height == s.height && slant == s.slant);
508}
509
510int style::operator!=(const style &s) const
511{
512 return !(*this == s);
513}
514
515struct depend_list;
516
517struct document_font {
518 enum { LISTED = 01, NEEDED = 02, SUPPLIED = 04 };
519 char *name;
520 char *filename;
521 int flags;
522 document_font *next;
523 depend_list *depends_on;
524 int mark;
525 document_font(const char *);
526 ~document_font();
527 void download(ps_output &);
528};
529
530struct depend_list {
531 document_font *p;
532 depend_list *next;
533};
534
535
536class ps_printer : public printer {
537 FILE *tempfp;
538 ps_output out;
539 int res;
540 int space_char_index;
541 int pages_output;
542 int paper_length;
543 int equalise_spaces;
544 enum { SBUF_SIZE = 256 };
545 char sbuf[SBUF_SIZE];
546 int sbuf_len;
547 int sbuf_start_hpos;
548 int sbuf_vpos;
549 int sbuf_end_hpos;
550 int sbuf_space_width;
551 int sbuf_space_count;
552 int sbuf_space_diff_count;
553 int sbuf_space_code;
554 int sbuf_kern;
555 style sbuf_style;
556 style output_style;
557 int output_hpos;
558 int output_vpos;
559 int output_draw_point_size;
560 int line_thickness;
561 int output_line_thickness;
562 int fill;
563 unsigned char output_space_code;
564 enum { MAX_DEFINED_STYLES = 50 };
565 style defined_styles[MAX_DEFINED_STYLES];
566 int ndefined_styles;
567 int next_encoding_index;
568 string defs;
569 int ndefs;
570 document_font *doc_fonts;
571
572 void flush_sbuf();
573 void set_style(const style &);
574 void set_space_code(unsigned char c);
575 int set_encoding_index(ps_font *);
576 void do_exec(char *, const environment *);
577 void do_import(char *, const environment *);
578 void do_def(char *, const environment *);
579 void do_mdef(char *, const environment *);
580 void do_file(char *, const environment *);
581 void set_line_thickness(const environment *);
582 void fill_path();
583 void merge_download_fonts();
584 void merge_import_fonts(FILE *);
585 void merge_ps_fonts();
586 void print_font_comment();
587 void print_supplied_font_comment();
588 void print_needed_font_comment();
589 void print_include_font_comments();
590 document_font *lookup_doc_font(const char *);
591 void encode_fonts();
592 void download_fonts();
593 void define_encoding(const char *, int);
594 void reencode_font(ps_font *);
595 void read_download_file();
596public:
597 ps_printer();
598 ~ps_printer();
599 void set_char(int i, font *f, const environment *env, int w);
600 void draw(int code, int *p, int np, const environment *env);
601 void begin_page(int);
602 void end_page();
603 void special(char *arg, const environment *env);
604 font *make_font(const char *);
605 void end_of_line();
606};
607
608ps_printer::ps_printer()
609: pages_output(0),
610 sbuf_len(0),
611 output_hpos(-1),
612 output_vpos(-1),
613 out(0, 79),
614 ndefined_styles(0),
615 next_encoding_index(0),
616 line_thickness(-1),
617 fill(FILL_MAX + 1),
618 ndefs(0),
619 doc_fonts(0)
620{
621 static char temp_filename[] = "/tmp/gropsXXXXXX";
622 mktemp(temp_filename);
623 tempfp = fopen(temp_filename, "w+");
624 if (tempfp == 0)
625 fatal("can't open temporary file `%1': %2",
626 temp_filename, strerror(errno));
627 if (unlink(temp_filename) < 0)
628 error("can't unlink `%1': %2", temp_filename, strerror(errno));
629 out.set_file(tempfp);
630 if (linewidth < 0)
631 linewidth = DEFAULT_LINEWIDTH;
632 if (font::hor != 1)
633 fatal("horizontal resolution must be 1");
634 if (font::vert != 1)
635 fatal("vertical resolution must be 1");
636 if (font::res % (font::sizescale*72) != 0)
637 fatal("res must be a multiple of 72*sizescale");
638 int r = font::res;
639 int point = 0;
640 while (r % 10 == 0) {
641 r /= 10;
642 point++;
643 }
644 res = r;
645 out.set_fixed_point(point);
646 space_char_index = font::name_to_index("space");
647 paper_length = font::paperlength;
648 if (paper_length == 0)
649 paper_length = 11*font::res;
650 equalise_spaces = font::res >= 72000;
651}
652
653int ps_printer::set_encoding_index(ps_font *f)
654{
655 if (f->encoding_index >= 0)
656 return f->encoding_index;
657 for (font_pointer_list *p = font_list; p; p = p->next)
658 if (p->p != f) {
659 char *encoding = ((ps_font *)p->p)->encoding;
660 int encoding_index = ((ps_font *)p->p)->encoding_index;
661 if (encoding != 0 && encoding_index >= 0
662 && strcmp(f->encoding, encoding) == 0) {
663 return f->encoding_index = encoding_index;
664 }
665 }
666 return f->encoding_index = next_encoding_index++;
667}
668
669void ps_printer::set_char(int i, font *f, const environment *env, int w)
670{
671 if (i == space_char_index)
672 return;
673 unsigned char code = f->get_code(i);
674 style sty(f, env->size, env->height, env->slant);
675 if (sty.slant != 0) {
676 if (sty.slant > 80 || sty.slant < -80) {
677 error("silly slant `%1' degrees", sty.slant);
678 sty.slant = 0;
679 }
680 }
681 if (sbuf_len > 0) {
682 if (sbuf_len < SBUF_SIZE
683 && sty == sbuf_style
684 && sbuf_vpos == env->vpos) {
685 if (sbuf_end_hpos == env->hpos) {
686 sbuf[sbuf_len++] = code;
687 sbuf_end_hpos += w + sbuf_kern;
688 return;
689 }
690 if (sbuf_len == 1 && sbuf_kern == 0) {
691 sbuf_kern = env->hpos - sbuf_end_hpos;
692 sbuf_end_hpos = env->hpos + sbuf_kern + w;
693 sbuf[sbuf_len++] = code;
694 return;
695 }
696 /* If sbuf_end_hpos - sbuf_kern == env->hpos, we are better off
697 starting a new string. */
698 if (sbuf_len < SBUF_SIZE - 1 && env->hpos >= sbuf_end_hpos
699 && (sbuf_kern == 0 || sbuf_end_hpos - sbuf_kern != env->hpos)) {
700 if (sbuf_space_code < 0) {
701 if (f->contains(space_char_index)) {
702 sbuf_space_code = f->get_code(space_char_index);
703 sbuf_space_width = env->hpos - sbuf_end_hpos;
704 sbuf_end_hpos = env->hpos + w + sbuf_kern;
705 sbuf[sbuf_len++] = sbuf_space_code;
706 sbuf[sbuf_len++] = code;
707 sbuf_space_count++;
708 return;
709 }
710 }
711 else {
712 int diff = env->hpos - sbuf_end_hpos - sbuf_space_width;
713 if (diff == 0 || (equalise_spaces && (diff == 1 || diff == -1))) {
714 sbuf_end_hpos = env->hpos + w + sbuf_kern;
715 sbuf[sbuf_len++] = sbuf_space_code;
716 sbuf[sbuf_len++] = code;
717 sbuf_space_count++;
718 if (diff == 1)
719 sbuf_space_diff_count++;
720 else if (diff == -1)
721 sbuf_space_diff_count--;
722 return;
723 }
724 }
725 }
726 }
727 flush_sbuf();
728 }
729 sbuf_len = 1;
730 sbuf[0] = code;
731 sbuf_end_hpos = env->hpos + w;
732 sbuf_start_hpos = env->hpos;
733 sbuf_vpos = env->vpos;
734 sbuf_style = sty;
735 sbuf_space_code = -1;
736 sbuf_space_width = 0;
737 sbuf_space_count = sbuf_space_diff_count = 0;
738 sbuf_kern = 0;
739}
740
741int is_small_h(int n)
742{
743 return n < (font::res*2)/72 && n > -(font::res*10)/72;
744}
745
746int is_small_v(int n)
747{
748 return n < (font::res*4)/72 && n > -(font::res*4)/72;
749}
750
751static char *make_encoding_name(int encoding_index)
752{
753 static char buf[3 + INT_DIGITS + 1];
754 sprintf(buf, "ENC%d", encoding_index);
755 return buf;
756}
757
758const char *const WS = " \t\n\r";
759
760void ps_printer::define_encoding(const char *encoding, int encoding_index)
761{
762 char *vec[256];
763 for (int i = 0; i < 256; i++)
764 vec[i] = 0;
765 char *path;
766 FILE *fp = font::open_file(encoding, &path);
767 if (fp == 0)
768 fatal("can't open encoding file `%1'", encoding);
769 int lineno = 1;
770 char buf[256];
771 while (fgets(buf, 512, fp) != 0) {
772 char *p = buf;
773 while (isascii(*p) && isspace(*p))
774 p++;
775 if (*p != '#' && *p != '\0' && (p = strtok(buf, WS)) != 0) {
776 char *q = strtok(0, WS);
777 int n;
778 if (q == 0 || sscanf(q, "%d", &n) != 1 || n < 0 || n >= 256)
779 fatal_with_file_and_line(path, lineno, "bad second field");
780 vec[n] = new char[strlen(p) + 1];
781 strcpy(vec[n], p);
782 }
783 lineno++;
784 }
785 delete path;
786 out.put_literal_symbol(make_encoding_name(encoding_index));
787 out.put_delimiter('[');
788 for (i = 0; i < 256; i++) {
789 if (vec[i] == 0)
790 out.put_literal_symbol(".notdef");
791 else
792 out.put_literal_symbol(vec[i]);
793 }
794 out.put_delimiter(']').put_symbol("def");
795}
796
797void ps_printer::reencode_font(ps_font *f)
798{
799 out.put_literal_symbol(f->reencoded_name)
800 .put_symbol(make_encoding_name(f->encoding_index))
801 .put_literal_symbol(f->get_internal_name())
802 .put_symbol("RE");
803}
804
805void ps_printer::encode_fonts()
806{
807 if (next_encoding_index == 0)
808 return;
809 char *done_encoding = new char[next_encoding_index];
810 for (int i = 0; i < next_encoding_index; i++)
811 done_encoding[i] = 0;
812 for (font_pointer_list *f = font_list; f; f = f->next) {
813 int encoding_index = ((ps_font *)f->p)->encoding_index;
814 if (encoding_index >= 0) {
815 assert(encoding_index < next_encoding_index);
816 if (!done_encoding[encoding_index]) {
817 done_encoding[encoding_index] = 1;
818 define_encoding(((ps_font *)f->p)->encoding, encoding_index);
819 }
820 reencode_font((ps_font *)f->p);
821 }
822 }
823 delete done_encoding;
824}
825
826void ps_printer::set_style(const style &sty)
827{
828 char buf[1 + INT_DIGITS + 1];
829 for (int i = 0; i < ndefined_styles; i++)
830 if (sty == defined_styles[i]) {
831 sprintf(buf, "F%d", i);
832 out.put_symbol(buf);
833 return;
834 }
835 if (ndefined_styles >= MAX_DEFINED_STYLES)
836 ndefined_styles = 0;
837 sprintf(buf, "F%d", ndefined_styles);
838 out.put_literal_symbol(buf);
839 const char *psname = sty.f->get_internal_name();
840 if (psname == 0)
841 fatal("no internalname specified for font `%1'", sty.f->get_name());
842 char *encoding = ((ps_font *)sty.f)->encoding;
843 if (encoding != 0) {
844 char *s = ((ps_font *)sty.f)->reencoded_name;
845 if (s == 0) {
846 int ei = set_encoding_index((ps_font *)sty.f);
847 char *tem = new char[strlen(psname) + 1 + INT_DIGITS + 1];
848 sprintf(tem, "%s@%d", psname, ei);
849 psname = tem;
850 ((ps_font *)sty.f)->reencoded_name = tem;
851 }
852 else
853 psname = s;
854 }
855 out.put_fix_number((font::res/(72*font::sizescale))*sty.point_size);
856 if (sty.height != 0 || sty.slant != 0) {
857 int h = sty.height == 0 ? sty.point_size : sty.height;
858 h *= font::res/(72*font::sizescale);
859 int c = int(h*tan(radians(sty.slant)) + .5);
860 out.put_fix_number(c).put_fix_number(h).put_literal_symbol(psname)
861 .put_symbol("MF");
862 }
863 else {
864 out.put_literal_symbol(psname).put_symbol("SF");
865 }
866 defined_styles[ndefined_styles++] = sty;
867}
868
869void ps_printer::set_space_code(unsigned char c)
870{
871 out.put_literal_symbol("SC").put_number(c).put_symbol("def");
872}
873
874void ps_printer::end_of_line()
875{
876 flush_sbuf();
877 // this ensures that we do an absolute motion to the beginning of a line
878 output_vpos = output_hpos = -1;
879}
880
881void ps_printer::flush_sbuf()
882{
883 enum {
884 NONE,
885 RELATIVE_H,
886 RELATIVE_V,
887 RELATIVE_HV,
888 ABSOLUTE
889 } motion = NONE;
890 int space_flag = 0;
891 if (sbuf_len == 0)
892 return;
893 if (output_style != sbuf_style) {
894 set_style(sbuf_style);
895 output_style = sbuf_style;
896 }
897 int extra_space = 0;
898 if (output_hpos < 0 || output_vpos < 0
899 || !is_small_h(output_hpos - sbuf_start_hpos)
900 || !is_small_v(output_vpos - sbuf_vpos))
901 motion = ABSOLUTE;
902 else {
903 if (output_hpos != sbuf_start_hpos)
904 motion = RELATIVE_H;
905 if (output_vpos != sbuf_vpos) {
906 if (motion != NONE)
907 motion = RELATIVE_HV;
908 else
909 motion = RELATIVE_V;
910 }
911 }
912 if (sbuf_space_code >= 0) {
913 int w = sbuf_style.f->get_width(space_char_index, sbuf_style.point_size);
914 if (w + sbuf_kern != sbuf_space_width) {
915 if (sbuf_space_code != output_space_code) {
916 set_space_code(sbuf_space_code);
917 output_space_code = sbuf_space_code;
918 }
919 space_flag = 1;
920 extra_space = sbuf_space_width - w - sbuf_kern;
921 if (sbuf_space_diff_count > sbuf_space_count/2)
922 extra_space++;
923 else if (sbuf_space_diff_count < -(sbuf_space_count/2))
924 extra_space--;
925 }
926 }
927 if (space_flag)
928 out.put_fix_number(extra_space);
929 if (sbuf_kern != 0)
930 out.put_fix_number(sbuf_kern);
931 out.put_string(sbuf, sbuf_len);
932 char sym[2];
933 sym[0] = 'A' + motion*4 + space_flag + 2*(sbuf_kern != 0);
934 sym[1] = '\0';
935 switch (motion) {
936 case NONE:
937 break;
938 case ABSOLUTE:
939 out.put_fix_number(sbuf_start_hpos)
940 .put_fix_number(sbuf_vpos);
941 break;
942 case RELATIVE_H:
943 out.put_fix_number(sbuf_start_hpos - output_hpos);
944 break;
945 case RELATIVE_V:
946 out.put_fix_number(sbuf_vpos - output_vpos);
947 break;
948 case RELATIVE_HV:
949 out.put_fix_number(sbuf_start_hpos - output_hpos)
950 .put_fix_number(sbuf_vpos - output_vpos);
951 break;
952 default:
953 assert(0);
954 }
955 out.put_symbol(sym);
956 output_hpos = sbuf_end_hpos;
957 output_vpos = sbuf_vpos;
958 sbuf_len = 0;
959}
960
961
962void ps_printer::set_line_thickness(const environment *env)
963{
964 if (line_thickness < 0) {
965 if (output_draw_point_size != env->size) {
966 // we ought to check for overflow here
967 int lw = ((font::res/(72*font::sizescale))*linewidth*env->size)/1000;
968 out.put_fix_number(lw).put_symbol("LW");
969 output_draw_point_size = env->size;
970 output_line_thickness = -1;
971 }
972 }
973 else {
974 if (output_line_thickness != line_thickness) {
975 out.put_fix_number(line_thickness).put_symbol("LW");
976 output_line_thickness = line_thickness;
977 output_draw_point_size = -1;
978 }
979 }
980}
981
982void ps_printer::fill_path()
983{
984 if (fill > FILL_MAX)
985 out.put_symbol("BL");
986 else
987 out.put_float(transform_fill(fill)).put_symbol("FL");
988}
989
990void ps_printer::draw(int code, int *p, int np, const environment *env)
991{
992 int fill_flag = 0;
993 switch (code) {
994 case 'C':
995 fill_flag = 1;
996 // fall through
997 case 'c':
998 // troff adds an extra argument to C
999 if (np != 1 && !(code == 'C' && np == 2)) {
1000 error("1 argument required for circle");
1001 break;
1002 }
1003 out.put_fix_number(env->hpos + p[0]/2)
1004 .put_fix_number(env->vpos)
1005 .put_fix_number(p[0]/2)
1006 .put_symbol("DC");
1007 if (fill_flag) {
1008 fill_path();
1009 }
1010 else {
1011 set_line_thickness(env);
1012 out.put_symbol("ST");
1013 }
1014 break;
1015 case 'l':
1016 if (np != 2) {
1017 error("2 arguments required for line");
1018 break;
1019 }
1020 set_line_thickness(env);
1021 out.put_fix_number(p[0] + env->hpos)
1022 .put_fix_number(p[1] + env->vpos)
1023 .put_fix_number(env->hpos)
1024 .put_fix_number(env->vpos)
1025 .put_symbol("DL");
1026 break;
1027 case 'E':
1028 fill_flag = 1;
1029 // fall through
1030 case 'e':
1031 if (np != 2) {
1032 error("2 arguments required for ellipse");
1033 break;
1034 }
1035 out.put_fix_number(p[0])
1036 .put_fix_number(p[1])
1037 .put_fix_number(env->hpos + p[0]/2)
1038 .put_fix_number(env->vpos)
1039 .put_symbol("DE");
1040 if (fill_flag) {
1041 fill_path();
1042 }
1043 else {
1044 set_line_thickness(env);
1045 out.put_symbol("ST");
1046 }
1047 break;
1048 case 'P':
1049 fill_flag = 1;
1050 // fall through
1051 case 'p':
1052 {
1053 if (np & 1) {
1054 error("even number of arguments required for polygon");
1055 break;
1056 }
1057 if (np == 0) {
1058 error("no arguments for polygon");
1059 break;
1060 }
1061 out.put_fix_number(env->hpos)
1062 .put_fix_number(env->vpos)
1063 .put_symbol("MT");
1064 for (int i = 0; i < np; i += 2)
1065 out.put_fix_number(p[i])
1066 .put_fix_number(p[i+1])
1067 .put_symbol("RL");
1068 out.put_symbol("CL");
1069 if (fill_flag) {
1070 fill_path();
1071 }
1072 else {
1073 set_line_thickness(env);
1074 out.put_symbol("ST");
1075 }
1076 break;
1077 }
1078 case '~':
1079 {
1080 if (np & 1) {
1081 error("even number of arguments required for spline");
1082 break;
1083 }
1084 if (np == 0) {
1085 error("no arguments for spline");
1086 break;
1087 }
1088 out.put_fix_number(env->hpos)
1089 .put_fix_number(env->vpos)
1090 .put_symbol("MT");
1091 out.put_fix_number(p[0]/2)
1092 .put_fix_number(p[1]/2)
1093 .put_symbol("RL");
1094 /* tnum/tden should be between 0 and 1; the closer it is to 1
1095 the tighter the curve will be to the guiding lines; 2/3
1096 is the standard value */
1097 const int tnum = 2;
1098 const int tden = 3;
1099 for (int i = 0; i < np - 2; i += 2) {
1100 out.put_fix_number((p[i]*tnum)/(2*tden))
1101 .put_fix_number((p[i + 1]*tnum)/(2*tden))
1102 .put_fix_number(p[i]/2 + (p[i + 2]*(tden - tnum))/(2*tden))
1103 .put_fix_number(p[i + 1]/2 + (p[i + 3]*(tden - tnum))/(2*tden))
1104 .put_fix_number((p[i] - p[i]/2) + p[i + 2]/2)
1105 .put_fix_number((p[i + 1] - p[i + 1]/2) + p[i + 3]/2)
1106 .put_symbol("RC");
1107 }
1108 out.put_fix_number(p[np - 2] - p[np - 2]/2)
1109 .put_fix_number(p[np - 1] - p[np - 1]/2)
1110 .put_symbol("RL");
1111 set_line_thickness(env);
1112 out.put_symbol("ST");
1113 }
1114 break;
1115 case 'a':
1116 {
1117 if (np != 4) {
1118 error("4 arguments required for arc");
1119 break;
1120 }
1121 set_line_thickness(env);
1122 int x = p[0] + p[2];
1123 int y = p[1] + p[3];
1124 double n = p[0]*double(x) + p[1]*double(y);
1125 if (n == 0)
1126 out.put_fix_number(x + env->hpos)
1127 .put_fix_number(y + env->vpos)
1128 .put_fix_number(env->hpos)
1129 .put_fix_number(env->vpos)
1130 .put_symbol("DL");
1131 else {
1132 double k = (double(x)*x + double(y)*y)/(2.0*n);
1133 double cx = k*p[0];
1134 double cy = k*p[1];
1135 out.put_fix_number(env->hpos + int(cx))
1136 .put_fix_number(env->vpos + int(cy))
1137 .put_fix_number(int(sqrt(cx*cx + cy*cy)))
1138 .put_float(degrees(atan2(-cy, -cx)))
1139 .put_float(degrees(atan2(y - cy, x - cx)))
1140 .put_symbol("DA");
1141 }
1142 }
1143 break;
1144 case 't':
1145 {
1146 if (np == 0) {
1147 line_thickness = -1;
1148 }
1149 else {
1150 // troff gratuitously adds an extra 0
1151 if (np != 1 && np != 2) {
1152 error("0 or 1 argument required for thickness");
1153 break;
1154 }
1155 line_thickness = p[0];
1156 }
1157 break;
1158 }
1159 case 'f':
1160 {
1161 if (np != 1 && np != 2) {
1162 error("1 argument required for fill");
1163 break;
1164 }
1165 fill = p[0];
1166 if (fill < 0 || fill > FILL_MAX) {
1167 // This means fill with the current color.
1168 fill = FILL_MAX + 1;
1169 }
1170 break;
1171 }
1172 default:
1173 error("unrecognised drawing command `%1'", char(code));
1174 break;
1175 }
1176
1177 output_hpos = output_vpos = -1;
1178}
1179
1180void ps_printer::begin_page(int n)
1181{
1182 out.begin_comment("Page:").comment_arg(itoa(n));
1183 out.comment_arg(itoa(++pages_output)).end_comment();
1184 output_style.f = 0;
1185 output_space_code = 32;
1186 output_draw_point_size = -1;
1187 output_line_thickness = -1;
1188 output_hpos = output_vpos = -1;
1189 ndefined_styles = 0;
1190 out.put_symbol("BP");
1191}
1192
1193void ps_printer::end_page()
1194{
1195 flush_sbuf();
1196 out.put_symbol("EP");
1197}
1198
1199font *ps_printer::make_font(const char *nm)
1200{
1201 return ps_font::load_ps_font(nm);
1202}
1203
1204ps_printer::~ps_printer()
1205{
1206 out.simple_comment("Trailer");
1207 out.put_symbol("end");
1208 out.end_line();
1209 if (fseek(tempfp, 0L, 0) < 0)
1210 fatal("fseek on temporary file failed");
1211 fputs("%!PS-Adobe-2.1\n", stdout);
1212 out.set_file(stdout);
1213 {
1214 extern const char *version_string;
1215 out.begin_comment("Creator:")
1216 .comment_arg("groff")
1217 .comment_arg("version")
1218 .comment_arg(version_string)
1219 .end_comment();
1220 }
1221 read_download_file();
1222 merge_ps_fonts();
1223 merge_download_fonts();
1224 print_font_comment();
1225 print_supplied_font_comment();
1226 print_needed_font_comment();
1227 out.begin_comment("Pages:").comment_arg(itoa(pages_output)).end_comment();
1228 out.simple_comment("EndComments");
1229 char *path;
1230 FILE *fp = font::open_file(PROLOGUE, &path);
1231 if (fp == 0)
1232 fatal("can't find `%1'", PROLOGUE);
1233 out.put_literal_symbol(dict_name);
1234 out.put_number(MAX_DEFINED_STYLES + MAX_PROLOGUE_DEFS).put_symbol("dict");
1235 out.put_symbol("def");
1236 out.put_symbol(dict_name).put_symbol("begin");
1237 out.include_file(fp);
1238 fclose(fp);
1239 if (ndefs > 0)
1240 ndefs += DEFS_DICT_SPARE;
1241 out.put_literal_symbol(defs_dict_name)
1242 .put_number(ndefs + 1)
1243 .put_symbol("dict")
1244 .put_symbol("def");
1245 out.put_symbol(defs_dict_name)
1246 .put_symbol("begin");
1247 out.put_literal_symbol("u")
1248 .put_delimiter('{')
1249 .put_fix_number(1)
1250 .put_symbol("mul")
1251 .put_delimiter('}')
1252 .put_symbol("bind")
1253 .put_symbol("def");
1254 defs += '\0';
1255 out.special(defs.contents());
1256 out.put_symbol("end");
1257 delete path;
1258 out.put_symbol("end");
1259#ifndef BROKEN_SPOOLER
1260 out.simple_comment("EndProlog");
1261#endif /* !BROKEN_SPOOLER */
1262 print_include_font_comments();
1263 download_fonts();
1264#ifndef BROKEN_SPOOLER
1265 out.simple_comment("BeginSetup");
1266#endif /* !BROKEN_SPOOLER */
1267 out.put_symbol(dict_name).put_symbol("begin");
1268 out.put_literal_symbol("#copies").put_number(ncopies).put_symbol("def");
1269 out.put_literal_symbol("RES").put_number(res).put_symbol("def");
1270 out.put_literal_symbol("PL").put_fix_number(paper_length).put_symbol("def");
1271 out.put_literal_symbol("LS")
1272 .put_symbol(landscape_flag ? "true" : "false")
1273 .put_symbol("def");
1274 encode_fonts();
1275#ifndef BROKEN_SPOOLER
1276 out.simple_comment("EndSetup");
1277#else /* !BROKEN_SPOOLER */
1278 out.simple_comment("EndProlog");
1279#endif /* BROKEN_SPOOLER */
1280 out.end_line();
1281 out.copy_file(tempfp);
1282 fclose(tempfp);
1283}
1284
1285void ps_printer::special(char *arg, const environment *env)
1286{
1287 typedef void (ps_printer::*SPECIAL_PROCP)(char *, const environment *);
1288 static struct {
1289 const char *name;
1290 SPECIAL_PROCP proc;
1291 } proc_table[] = {
1292 "exec", &ps_printer::do_exec,
1293 "def", &ps_printer::do_def,
1294 "mdef", &ps_printer::do_mdef,
1295 "import", &ps_printer::do_import,
1296 "file", &ps_printer::do_file,
1297 };
1298 for (char *p = arg; *p == ' ' || *p == '\n'; p++)
1299 ;
1300 char *tag = p;
1301 for (; *p != '\0' && *p != ':' && *p != ' ' && *p != '\n'; p++)
1302 ;
1303 if (*p == '\0' || strncmp(tag, "ps", p - tag) != 0) {
1304 error("X command without `ps:' tag ignored");
1305 return;
1306 }
1307 p++;
1308 for (; *p == ' ' || *p == '\n'; p++)
1309 ;
1310 char *command = p;
1311 for (; *p != '\0' && *p != ' ' && *p != '\n'; p++)
1312 ;
1313 if (*command == '\0') {
1314 error("X command without `ps:' tag ignored");
1315 return;
1316 }
1317 for (int i = 0; i < sizeof(proc_table)/sizeof(proc_table[0]); i++)
1318 if (strncmp(command, proc_table[i].name, p - command) == 0) {
1319 flush_sbuf();
1320 (this->*(proc_table[i].proc))(p, env);
1321 return;
1322 }
1323 error("X command `%1' not recognised", command);
1324}
1325
1326// A conforming PostScript document must not have lines longer
1327// than 255 characters (excluding line termination characters).
1328
1329static int check_line_lengths(const char *p)
1330{
1331 for (;;) {
1332 const char *end = strchr(p, '\n');
1333 if (end == 0)
1334 end = strchr(p, '\0');
1335 if (end - p > 255)
1336 return 0;
1337 if (*end == '\0')
1338 break;
1339 p = end + 1;
1340 }
1341 return 1;
1342}
1343
1344void ps_printer::do_exec(char *arg, const environment *env)
1345{
1346 while (csspace(*arg))
1347 arg++;
1348 if (*arg == '\0') {
1349 error("missing argument to X exec command");
1350 return;
1351 }
1352 if (!check_line_lengths(arg)) {
1353 error("lines in X exec command must not be more than 255 characters long");
1354 return;
1355 }
1356 out.put_fix_number(env->hpos)
1357 .put_fix_number(env->vpos)
1358 .put_symbol("EBEGIN")
1359 .special(arg)
1360 .put_symbol("EEND");
1361 output_hpos = output_vpos = -1;
1362 output_style.f = 0;
1363 output_draw_point_size = -1;
1364 output_line_thickness = -1;
1365 ndefined_styles = 0;
1366}
1367
1368void ps_printer::do_file(char *arg, const environment *env)
1369{
1370 while (csspace(*arg))
1371 arg++;
1372 if (*arg == '\0') {
1373 error("missing argument to X file command");
1374 return;
1375 }
1376 const char *filename = arg;
1377 do {
1378 ++arg;
1379 } while (*arg != '\0' && *arg != ' ' && *arg != '\n');
1380 FILE *fp = fopen(filename, "r");
1381 if (!fp) {
1382 error("can't open `%1': %2", filename, strerror(errno));
1383 return;
1384 }
1385 out.put_fix_number(env->hpos)
1386 .put_fix_number(env->vpos)
1387 .put_symbol("EBEGIN")
1388 .include_file(fp)
1389 .put_symbol("EEND");
1390 fclose(fp);
1391 output_hpos = output_vpos = -1;
1392 output_style.f = 0;
1393 output_draw_point_size = -1;
1394 output_line_thickness = -1;
1395 ndefined_styles = 0;
1396}
1397
1398void ps_printer::do_def(char *arg, const environment *)
1399{
1400 while (csspace(*arg))
1401 arg++;
1402 if (!check_line_lengths(arg)) {
1403 error("lines in X def command must not be more than 255 characters long");
1404 return;
1405 }
1406 defs += arg;
1407 if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n')
1408 defs += '\n';
1409 ndefs++;
1410}
1411
1412// Like def, but the first argument says how many definitions it contains.
1413
1414void ps_printer::do_mdef(char *arg, const environment *)
1415{
1416 char *p;
1417 int n = (int)strtol(arg, &p, 10);
1418 if (n == 0 && p == arg) {
1419 error("first argument to X mdef must be an integer");
1420 return;
1421 }
1422 if (n < 0) {
1423 error("out of range argument `%1' to X mdef command", int(n));
1424 return;
1425 }
1426 arg = p;
1427 while (csspace(*arg))
1428 arg++;
1429 if (!check_line_lengths(arg)) {
1430 error("lines in X mdef command must not be more than 255 characters long");
1431 return;
1432 }
1433 defs += arg;
1434 if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n')
1435 defs += '\n';
1436 ndefs += n;
1437}
1438
1439void ps_printer::do_import(char *arg, const environment *env)
1440{
1441 while (*arg == ' ' || *arg == '\n')
1442 arg++;
1443 for (char *p = arg; *p != '\0' && *p != ' ' && *p != '\n'; p++)
1444 ;
1445 if (*p != '\0')
1446 *p++ = '\0';
1447 int parms[6];
1448 int nparms = 0;
1449 while (nparms < 6) {
1450 char *end;
1451 long n = strtol(p, &end, 10);
1452 if (n == 0 && end == p)
1453 break;
1454 parms[nparms++] = int(n);
1455 p = end;
1456 }
1457 if (csalpha(*p) && (p[1] == '\0' || p[1] == ' ' || p[1] == '\n')) {
1458 error("scaling indicators not allowed in arguments for X import command");
1459 return;
1460 }
1461 while (*p == ' ' || *p == '\n')
1462 p++;
1463 if (nparms < 5) {
1464 if (*p == '\0')
1465 error("too few arguments for X import command");
1466 else
1467 error("invalid argument `%1' for X import command", p);
1468 return;
1469 }
1470 if (*p != '\0') {
1471 error("superflous argument `%1' for X import command", p);
1472 return;
1473 }
1474 int llx = parms[0];
1475 int lly = parms[1];
1476 int urx = parms[2];
1477 int ury = parms[3];
1478 int desired_width = parms[4];
1479 int desired_height = parms[5];
1480 if (desired_width <= 0) {
1481 error("bad width argument `%1' for X import command: must be > 0",
1482 desired_width);
1483 return;
1484 }
1485 if (nparms == 6 && desired_height <= 0) {
1486 error("bad height argument `%1' for X import command: must be > 0",
1487 desired_height);
1488 return;
1489 }
1490 if (llx == urx) {
1491 error("llx and urx arguments for X import command must not be equal");
1492 return;
1493 }
1494 if (lly == ury) {
1495 error("lly and ury arguments for X import command must not be equal");
1496 return;
1497 }
1498 if (nparms == 5) {
1499 int old_wid = urx - llx;
1500 int old_ht = ury - lly;
1501 if (old_wid < 0)
1502 old_wid = -old_wid;
1503 if (old_ht < 0)
1504 old_ht = -old_ht;
1505 desired_height = int(desired_width*(double(old_ht)/double(old_wid)) + .5);
1506 }
1507 if (env->vpos - desired_height < 0)
1508 warning("top of imported graphic is above the top of the page");
1509 FILE *fp = fopen(arg, "r");
1510 if (fp == 0) {
1511 error("can't open `%1': %2", arg, strerror(errno));
1512 return;
1513 }
1514 merge_import_fonts(fp);
1515 out.put_literal_symbol("level1")
1516 .put_symbol("save")
1517 .put_symbol("def");
1518 out.put_number(llx)
1519 .put_number(lly)
1520 .put_fix_number(desired_width)
1521 .put_number(urx - llx)
1522 .put_fix_number(-desired_height)
1523 .put_number(ury - lly)
1524 .put_fix_number(env->hpos)
1525 .put_fix_number(env->vpos)
1526 .put_symbol("PICTURE");
1527 // Put our dictionary off the dictionary stack so that it isn't
1528 // zapped by unfriendly applications.
1529 out.put_symbol("end");
1530 // Disable showpage.
1531 out.put_literal_symbol("showpage")
1532 .put_delimiter('{')
1533 .put_delimiter('}')
1534 .put_symbol("def");
1535 out.begin_comment("BeginDocument:")
1536 .comment_arg(arg)
1537 .end_comment();
1538 out.include_file(fp);
1539 fclose(fp);
1540 out.simple_comment("EndDocument");
1541 // Clear junk off operand stack, to lessen chance of an invalidrestore.
1542 out.put_symbol("clear");
1543 out.put_symbol(dict_name)
1544 .put_symbol("begin");
1545 out.put_symbol("level1")
1546 .put_symbol("restore");
1547}
1548
1549static void add_font(const char *name, int t, document_font **pp)
1550{
1551 for (; *pp; pp = &(*pp)->next)
1552 if (strcmp(name, (*pp)->name) == 0) {
1553 (*pp)->flags |= t;
1554 return;
1555 }
1556 *pp = new document_font(name);
1557 (*pp)->flags = t;
1558}
1559
1560// Skip the rest of the current line, including any line termination
1561// characters.
1562
1563static int skip_line(FILE *fp)
1564{
1565 // The spec says any combination of \n and \r is allowed as a line
1566 // termination. It seems wrong to allow multiple \n's or multiple
1567 // \r's since this would mean we were swallowing blank lines.
1568 for (;;) {
1569 int c = getc(fp);
1570 if (c == '\n') {
1571 c = getc(fp);
1572 if (c == EOF)
1573 return 0;
1574 if (c != '\r')
1575 ungetc(c, fp);
1576 return 1;
1577 }
1578 if (c == '\r') {
1579 c = getc(fp);
1580 if (c == EOF)
1581 return 0;
1582 if (c != '\n')
1583 ungetc(c, fp);
1584 return 1;
1585 }
1586 if (c == EOF)
1587 break;
1588 }
1589 return 0;
1590}
1591
1592static void parse_fonts_arg(FILE *fp, int type, document_font **font_listp,
1593 int *at_endp)
1594{
1595 // More than enough room for a single name.
1596 char buf[256];
1597 int c = getc(fp);
1598 for (;;) {
1599 while (c == ' ' || c == '\t')
1600 c = getc(fp);
1601 if (c == EOF || c == '\n' || c == '\r')
1602 break;
1603 int i = 0;
1604 do {
1605 if (i >= sizeof(buf) - 1)
1606 return;
1607 if (c == '\0')
1608 return;
1609 buf[i++] = c;
1610 c = getc(fp);
1611 } while (c != '\n' && c != '\r' && c != ' ' && c != '\t' && c != EOF);
1612 buf[i++] = '\0';
1613 if (strcmp(buf, "(atend)") == 0 && at_endp != 0)
1614 *at_endp |= type;
1615 else
1616 add_font(buf, type, font_listp);
1617 }
1618 if (c == '\n' || c == '\r')
1619 ungetc(c, fp);
1620}
1621
1622static document_font *read_document_fonts(FILE *fp)
1623{
1624 int last_comment = 0;
1625 int document_fonts_at_end = 0;
1626 // This says which comments we've seen in the header. The first of each
1627 // is the significant one.
1628 int header_comments = 0;
1629 char buf[sizeof("DocumentSuppliedFonts:")];
1630#define MAGIC "%!PS-Adobe-"
1631 if (fread(buf, 1, sizeof(MAGIC)-1, fp) != sizeof(MAGIC)-1
1632 || memcmp(buf, MAGIC, sizeof(MAGIC)-1) != 0)
1633 return 0;
1634 document_font *font_list = 0;
1635 enum { HEADER, BODY, TRAILER } state = HEADER;
1636 int level = 0;
1637 while (skip_line(fp)) {
1638 if (state == BODY && document_fonts_at_end == 0)
1639 break;
1640 int c = getc(fp);
1641 if (c == '%')
1642 c = getc(fp);
1643 if (c == EOF)
1644 break;
1645 if (c != '%') {
1646 if (state == HEADER)
1647 state = BODY;
1648 if (c == '\n' || c == '\r')
1649 ungetc(c, fp);
1650 continue;
1651 }
1652 c = getc(fp);
1653 if (c == EOF)
1654 break;
1655 if (c == '+') {
1656 if (last_comment)
1657 parse_fonts_arg(fp, last_comment, &font_list, 0);
1658 continue;
1659 }
1660 last_comment = 0;
1661 int i = 0;
1662 while (c != '\r' && c != '\n' && c != ' ' && c != '\t' && c != EOF) {
1663 if (i >= sizeof(buf) - 1) {
1664 i = 0;
1665 break;
1666 }
1667 buf[i++] = c;
1668 if (c == ':')
1669 break;
1670 c = getc(fp);
1671 }
1672 if (c == '\r' || c == '\n')
1673 ungetc(c, fp);
1674 buf[i++] = '\0';
1675 if (strcmp(buf, "BeginDocument:") == 0)
1676 level++;
1677 else if (strcmp(buf, "EndDocument") == 0) {
1678 if (level > 0)
1679 level--;
1680 }
1681 else if (level == 0) {
1682 if (strcmp(buf, "Trailer") == 0)
1683 state = TRAILER;
1684 else if (strcmp(buf, "EndComments") == 0) {
1685 if (state == HEADER)
1686 state = BODY;
1687 }
1688 else if (state == HEADER) {
1689 int comment_type = 0;
1690 if (strcmp(buf, "DocumentFonts:") == 0)
1691 comment_type = document_font::LISTED;
1692 else if (strcmp(buf, "DocumentNeededFonts:") == 0)
1693 comment_type = document_font::NEEDED;
1694 else if (strcmp(buf, "DocumentSuppliedFonts:") == 0)
1695 comment_type = document_font::SUPPLIED;
1696 if (comment_type != 0 && !(header_comments & comment_type)) {
1697 parse_fonts_arg(fp, comment_type, &font_list,
1698 (comment_type == document_font::LISTED
1699 ? &document_fonts_at_end
1700 : 0));
1701 last_comment = comment_type;
1702 header_comments |= comment_type;
1703 }
1704 }
1705 else if (state == TRAILER && strcmp(buf, "DocumentFonts:") == 0) {
1706 parse_fonts_arg(fp, document_font::LISTED, &font_list, 0);
1707 last_comment = document_font::LISTED;
1708 }
1709 }
1710 }
1711 return font_list;
1712}
1713
1714document_font *ps_printer::lookup_doc_font(const char *nm)
1715{
1716 for (document_font **p = &doc_fonts; *p; p = &(*p)->next)
1717 if (strcmp((*p)->name, nm) == 0)
1718 return *p;
1719 return *p = new document_font(nm);
1720}
1721
1722void ps_printer::merge_download_fonts()
1723{
1724 for (document_font *p = doc_fonts; p; p = p->next)
1725 if (p->filename && (p->flags & document_font::NEEDED)) {
1726 char *path = 0;
1727 FILE *fp = font::open_file(p->filename, &path);
1728 delete path;
1729 if (fp) {
1730 document_font *depends = read_document_fonts(fp);
1731 fclose(fp);
1732 while (depends) {
1733 document_font *tem = depends->next;
1734 if (strcmp(p->name, depends->name) != 0) {
1735 if ((depends->flags & document_font::LISTED)
1736 && !(depends->flags & document_font::SUPPLIED))
1737 depends->flags |= document_font::NEEDED;
1738 // We ignore the LISTED bit from now on.
1739 document_font *q = lookup_doc_font(depends->name);
1740 if (q->filename && (depends->flags & document_font::NEEDED)) {
1741 depend_list *dep = new depend_list;
1742 dep->next = p->depends_on;
1743 dep->p = q;
1744 p->depends_on = dep;
1745 if (!(q->flags & document_font::NEEDED)) {
1746 // Move q to the end of the list.
1747 for (document_font **pp = &doc_fonts;
1748 *pp != q;
1749 pp = &(*pp)->next)
1750 ;
1751 *pp = q->next;
1752 q->next = 0;
1753 for (; *pp; pp = &(*pp)->next)
1754 ;
1755 *pp = q;
1756 }
1757 }
1758 q->flags |= depends->flags;
1759 }
1760 delete depends;
1761 depends = tem;
1762 }
1763 }
1764 else {
1765 error("can't find font file `%1': %2", p->filename, strerror(errno));
1766 delete p->filename;
1767 p->filename = 0;
1768 }
1769 }
1770}
1771
1772void ps_printer::merge_import_fonts(FILE *fp)
1773{
1774 document_font *p = read_document_fonts(fp);
1775 rewind(fp);
1776 while (p) {
1777 if ((p->flags & document_font::LISTED)
1778 && !(p->flags & document_font::SUPPLIED))
1779 p->flags |= document_font::NEEDED;
1780 document_font *tem = p->next;
1781 document_font *q = lookup_doc_font(p->name);
1782 q->flags |= p->flags;
1783 delete p;
1784 p = tem;
1785 }
1786}
1787
1788void ps_printer::merge_ps_fonts()
1789{
1790 for (font_pointer_list *f = font_list; f; f = f->next) {
1791 ps_font *psf = (ps_font *)(f->p);
1792 document_font *p = lookup_doc_font(psf->get_internal_name());
1793 p->flags |= document_font::NEEDED;
1794 }
1795}
1796
1797void ps_printer::print_font_comment()
1798{
1799 out.begin_comment("DocumentFonts:");
1800 for (document_font *list = doc_fonts; list; list = list->next)
1801 if (list->flags)
1802 out.comment_arg(list->name);
1803 out.end_comment();
1804}
1805
1806void ps_printer::print_supplied_font_comment()
1807{
1808 out.begin_comment("DocumentSuppliedFonts:");
1809 for (document_font *list = doc_fonts; list; list = list->next)
1810 if (((list->flags & document_font::NEEDED) && list->filename)
1811 || ((list->flags & document_font::SUPPLIED)
1812#if 0
1813 && !(list->flags & document_font::NEEDED)
1814#endif
1815 ))
1816 out.comment_arg(list->name);
1817 out.end_comment();
1818}
1819
1820void ps_printer::print_needed_font_comment()
1821{
1822 out.begin_comment("DocumentNeededFonts:");
1823 for (document_font *list = doc_fonts; list; list = list->next)
1824 if ((list->flags & document_font::NEEDED) && !list->filename)
1825 out.comment_arg(list->name);
1826 out.end_comment();
1827}
1828
1829void ps_printer::print_include_font_comments()
1830{
1831 for (document_font *list = doc_fonts; list; list = list->next)
1832 if ((list->flags & document_font::NEEDED) && !list->filename)
1833 out.begin_comment("IncludeFont:").comment_arg(list->name).end_comment();
1834}
1835
1836void ps_printer::download_fonts()
1837{
1838 for (document_font *p = doc_fonts; p; p = p->next)
1839 p->download(out);
1840}
1841
1842void ps_printer::read_download_file()
1843{
1844 char *path = 0;
1845 FILE *fp = font::open_file("download", &path);
1846 if (!fp)
1847 fatal("can't find `download'");
1848 char buf[512];
1849 int lineno = 0;
1850 while (fgets(buf, sizeof(buf), fp)) {
1851 lineno++;
1852 char *p = strtok(buf, " \t\r\n");
1853 if (p == 0 || *p == '#')
1854 continue;
1855 char *q = strtok(0, " \t\r\n");
1856 if (!q)
1857 fatal_with_file_and_line(path, lineno, "missing filename");
1858 lookup_doc_font(p)->filename = strsave(q);
1859 }
1860 delete path;
1861 fclose(fp);
1862}
1863
1864document_font::document_font(const char *s)
1865: name(strsave(s)), filename(0), flags(0), next(0), depends_on(0), mark(0)
1866{
1867 assert(name != 0);
1868}
1869
1870document_font::~document_font()
1871{
1872 delete name;
1873 delete filename;
1874 while (depends_on) {
1875 depend_list *tem = depends_on->next;
1876 delete depends_on;
1877 depends_on = tem;
1878 }
1879}
1880
1881void document_font::download(ps_output &out)
1882{
1883 if (!filename)
1884 return;
1885 if (!(flags & NEEDED))
1886 return;
1887 // Do a reverse topological sort on the dependency graph.
1888 if (mark == 0) {
1889 mark = -1;
1890 for (depend_list *p = depends_on; p; p = p->next)
1891 p->p->download(out);
1892 char *path = 0;
1893 FILE *fp = font::open_file(filename, &path);
1894 // This shouldn't normally happen because we checked that the file
1895 // exists in merge_download_fonts.
1896 if (!fp)
1897 fatal("can't open `%1': %2", filename, strerror(errno));
1898 out.begin_comment("BeginFont:").comment_arg(name).end_comment();
1899 out.begin_comment("BeginDocument:").comment_arg(filename).end_comment();
1900 out.include_file(fp);
1901 out.simple_comment("EndDocument");
1902 out.simple_comment("EndFont");
1903 fclose(fp);
1904 delete path;
1905 mark = 1;
1906 }
1907 else if (mark < 0)
1908 error("loop detected in font dependencies for `%1'", name);
1909}
1910
1911printer *make_printer()
1912{
1913 return new ps_printer;
1914}
1915
1916static void usage();
1917
1918int main(int argc, char **argv)
1919{
1920 program_name = argv[0];
1921 static char stderr_buf[BUFSIZ];
1922 setbuf(stderr, stderr_buf);
1923 int c;
1924 while ((c = getopt(argc, argv, "F:lc:w:v")) != EOF)
1925 switch(c) {
1926 case 'v':
1927 {
1928 extern const char *version_string;
1929 fprintf(stderr, "grops version %s\n", version_string);
1930 fflush(stderr);
1931 break;
1932 }
1933 case 'c':
1934 if (sscanf(optarg, "%d", &ncopies) != 1 || ncopies <= 0) {
1935 error("bad number of copies `%s'", optarg);
1936 ncopies = 1;
1937 }
1938 break;
1939 case 'l':
1940 landscape_flag = 1;
1941 break;
1942 case 'F':
1943 font::command_line_font_dir(optarg);
1944 break;
1945 case 'w':
1946 if (sscanf(optarg, "%d", &linewidth) != 1 || linewidth < 0) {
1947 error("bad linewidth `%s'", optarg);
1948 linewidth = -1;
1949 }
1950 break;
1951 case '?':
1952 usage();
1953 break;
1954 default:
1955 assert(0);
1956 }
1957 if (optind >= argc)
1958 do_file("-");
1959 else {
1960 for (int i = optind; i < argc; i++)
1961 do_file(argv[i]);
1962 }
1963 delete pr;
1964 exit(0);
1965}
1966
1967static void usage()
1968{
1969 fprintf(stderr, "usage: %s [-l] [-c n] [-w n] [-F dir] [-v] [files ...]\n",
1970 program_name);
1971 exit(1);
1972}