groff: update vendor branch to v1.20.1
[dragonfly.git] / contrib / groff / src / devices / grops / ps.cpp
CommitLineData
92d0a6a6 1// -*- C++ -*-
4d3e9548
JL
2/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005,
3 2006, 2007, 2009
92d0a6a6
JR
4 Free Software Foundation, Inc.
5 Written by James Clark (jjc@jclark.com)
6
7This file is part of groff.
8
9groff is free software; you can redistribute it and/or modify it under
10the terms of the GNU General Public License as published by the Free
4d3e9548
JL
11Software Foundation, either version 3 of the License, or
12(at your option) any later version.
92d0a6a6
JR
13
14groff is distributed in the hope that it will be useful, but WITHOUT ANY
15WARRANTY; without even the implied warranty of MERCHANTABILITY or
16FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17for more details.
18
4d3e9548
JL
19You should have received a copy of the GNU General Public License
20along with this program. If not, see <http://www.gnu.org/licenses/>. */
92d0a6a6
JR
21
22/*
23 * PostScript documentation:
24 * http://www.adobe.com/products/postscript/pdfs/PLRM.pdf
4d3e9548 25 * http://partners.adobe.com/public/developer/en/ps/5001.DSC_Spec.pdf
92d0a6a6
JR
26 */
27
28#include "driver.h"
29#include "stringclass.h"
30#include "cset.h"
31#include "nonposix.h"
32#include "paper.h"
33
34#include "ps.h"
35#include <time.h>
36
37#ifdef NEED_DECLARATION_PUTENV
38extern "C" {
39 int putenv(const char *);
40}
41#endif /* NEED_DECLARATION_PUTENV */
42
43extern "C" const char *Version_string;
44
45// search path defaults to the current directory
46search_path include_search_path(0, 0, 0, 1);
47
48static int landscape_flag = 0;
49static int manual_feed_flag = 0;
50static int ncopies = 1;
51static int linewidth = -1;
52// Non-zero means generate PostScript code that guesses the paper
53// length using the imageable area.
54static int guess_flag = 0;
55static double user_paper_length = 0;
56static double user_paper_width = 0;
57
58// Non-zero if -b was specified on the command line.
59static int bflag = 0;
60unsigned broken_flags = 0;
61
62// Non-zero means we need the CMYK extension for PostScript Level 1
63static int cmyk_flag = 0;
64
65#define DEFAULT_LINEWIDTH 40 /* in ems/1000 */
66#define MAX_LINE_LENGTH 72
67#define FILL_MAX 1000
68
69const char *const dict_name = "grops";
70const char *const defs_dict_name = "DEFS";
71const int DEFS_DICT_SPARE = 50;
72
73double degrees(double r)
74{
75 return r*180.0/PI;
76}
77
78double radians(double d)
79{
80 return d*PI/180.0;
81}
82
83// This is used for testing whether a character should be output in the
84// PostScript file using \nnn, so we really want the character to be
85// less than 0200.
86
87inline int is_ascii(char c)
88{
89 return (unsigned char)c < 0200;
90}
91
92ps_output::ps_output(FILE *f, int n)
93: fp(f), col(0), max_line_length(n), need_space(0), fixed_point(0)
94{
95}
96
97ps_output &ps_output::set_file(FILE *f)
98{
99 fp = f;
100 col = 0;
101 return *this;
102}
103
104ps_output &ps_output::copy_file(FILE *infp)
105{
106 int c;
107 while ((c = getc(infp)) != EOF)
108 putc(c, fp);
109 return *this;
110}
111
112ps_output &ps_output::end_line()
113{
114 if (col != 0) {
115 putc('\n', fp);
116 col = 0;
117 need_space = 0;
118 }
119 return *this;
120}
121
122ps_output &ps_output::special(const char *s)
123{
124 if (s == 0 || *s == '\0')
125 return *this;
126 if (col != 0) {
127 putc('\n', fp);
128 col = 0;
129 }
130 fputs(s, fp);
131 if (strchr(s, '\0')[-1] != '\n')
132 putc('\n', fp);
133 need_space = 0;
134 return *this;
135}
136
137ps_output &ps_output::simple_comment(const char *s)
138{
139 if (col != 0)
140 putc('\n', fp);
141 putc('%', fp);
142 putc('%', fp);
143 fputs(s, fp);
144 putc('\n', fp);
145 col = 0;
146 need_space = 0;
147 return *this;
148}
149
150ps_output &ps_output::begin_comment(const char *s)
151{
152 if (col != 0)
153 putc('\n', fp);
154 putc('%', fp);
155 putc('%', fp);
156 fputs(s, fp);
157 col = 2 + strlen(s);
158 return *this;
159}
160
161ps_output &ps_output::end_comment()
162{
163 if (col != 0) {
164 putc('\n', fp);
165 col = 0;
166 }
167 need_space = 0;
168 return *this;
169}
170
171ps_output &ps_output::comment_arg(const char *s)
172{
173 int len = strlen(s);
174 if (col + len + 1 > max_line_length) {
175 putc('\n', fp);
176 fputs("%%+", fp);
177 col = 3;
178 }
179 putc(' ', fp);
180 fputs(s, fp);
181 col += len + 1;
182 return *this;
183}
184
185ps_output &ps_output::set_fixed_point(int n)
186{
187 assert(n >= 0 && n <= 10);
188 fixed_point = n;
189 return *this;
190}
191
192ps_output &ps_output::put_delimiter(char c)
193{
194 if (col + 1 > max_line_length) {
195 putc('\n', fp);
196 col = 0;
197 }
198 putc(c, fp);
199 col++;
200 need_space = 0;
201 return *this;
202}
203
204ps_output &ps_output::put_string(const char *s, int n)
205{
206 int len = 0;
207 int i;
208 for (i = 0; i < n; i++) {
209 char c = s[i];
210 if (is_ascii(c) && csprint(c)) {
211 if (c == '(' || c == ')' || c == '\\')
212 len += 2;
213 else
214 len += 1;
215 }
216 else
217 len += 4;
218 }
219 if (len > n*2) {
220 if (col + n*2 + 2 > max_line_length && n*2 + 2 <= max_line_length) {
221 putc('\n', fp);
222 col = 0;
223 }
224 if (col + 1 > max_line_length) {
225 putc('\n', fp);
226 col = 0;
227 }
228 putc('<', fp);
229 col++;
230 for (i = 0; i < n; i++) {
231 if (col + 2 > max_line_length) {
232 putc('\n', fp);
233 col = 0;
234 }
235 fprintf(fp, "%02x", s[i] & 0377);
236 col += 2;
237 }
238 putc('>', fp);
239 col++;
240 }
241 else {
242 if (col + len + 2 > max_line_length && len + 2 <= max_line_length) {
243 putc('\n', fp);
244 col = 0;
245 }
246 if (col + 2 > max_line_length) {
247 putc('\n', fp);
248 col = 0;
249 }
250 putc('(', fp);
251 col++;
252 for (i = 0; i < n; i++) {
253 char c = s[i];
254 if (is_ascii(c) && csprint(c)) {
255 if (c == '(' || c == ')' || c == '\\')
256 len = 2;
257 else
258 len = 1;
259 }
260 else
261 len = 4;
262 if (col + len + 1 > max_line_length) {
263 putc('\\', fp);
264 putc('\n', fp);
265 col = 0;
266 }
267 switch (len) {
268 case 1:
269 putc(c, fp);
270 break;
271 case 2:
272 putc('\\', fp);
273 putc(c, fp);
274 break;
275 case 4:
276 fprintf(fp, "\\%03o", c & 0377);
277 break;
278 default:
279 assert(0);
280 }
281 col += len;
282 }
283 putc(')', fp);
284 col++;
285 }
286 need_space = 0;
287 return *this;
288}
289
290ps_output &ps_output::put_number(int n)
291{
292 char buf[1 + INT_DIGITS + 1];
293 sprintf(buf, "%d", n);
294 int len = strlen(buf);
295 if (col > 0 && col + len + need_space > max_line_length) {
296 putc('\n', fp);
297 col = 0;
298 need_space = 0;
299 }
300 if (need_space) {
301 putc(' ', fp);
302 col++;
303 }
304 fputs(buf, fp);
305 col += len;
306 need_space = 1;
307 return *this;
308}
309
310ps_output &ps_output::put_fix_number(int i)
311{
312 const char *p = if_to_a(i, fixed_point);
313 int len = strlen(p);
314 if (col > 0 && col + len + need_space > max_line_length) {
315 putc('\n', fp);
316 col = 0;
317 need_space = 0;
318 }
319 if (need_space) {
320 putc(' ', fp);
321 col++;
322 }
323 fputs(p, fp);
324 col += len;
325 need_space = 1;
326 return *this;
327}
328
329ps_output &ps_output::put_float(double d)
330{
331 char buf[128];
332 sprintf(buf, "%.4f", d);
333 int last = strlen(buf) - 1;
334 while (buf[last] == '0')
335 last--;
336 if (buf[last] == '.')
337 last--;
338 buf[++last] = '\0';
339 if (col > 0 && col + last + need_space > max_line_length) {
340 putc('\n', fp);
341 col = 0;
342 need_space = 0;
343 }
344 if (need_space) {
345 putc(' ', fp);
346 col++;
347 }
348 fputs(buf, fp);
349 col += last;
350 need_space = 1;
351 return *this;
352}
353
354ps_output &ps_output::put_symbol(const char *s)
355{
356 int len = strlen(s);
357 if (col > 0 && col + len + need_space > max_line_length) {
358 putc('\n', fp);
359 col = 0;
360 need_space = 0;
361 }
362 if (need_space) {
363 putc(' ', fp);
364 col++;
365 }
366 fputs(s, fp);
367 col += len;
368 need_space = 1;
369 return *this;
370}
371
372ps_output &ps_output::put_color(unsigned int c)
373{
374 char buf[128];
375 sprintf(buf, "%.3g", double(c) / color::MAX_COLOR_VAL);
376 int len = strlen(buf);
377 if (col > 0 && col + len + need_space > max_line_length) {
378 putc('\n', fp);
379 col = 0;
380 need_space = 0;
381 }
382 if (need_space) {
383 putc(' ', fp);
384 col++;
385 }
386 fputs(buf, fp);
387 col += len;
388 need_space = 1;
389 return *this;
390}
391
392ps_output &ps_output::put_literal_symbol(const char *s)
393{
394 int len = strlen(s);
395 if (col > 0 && col + len + 1 > max_line_length) {
396 putc('\n', fp);
397 col = 0;
398 }
399 putc('/', fp);
400 fputs(s, fp);
401 col += len + 1;
402 need_space = 1;
403 return *this;
404}
405
406class ps_font : public font {
407 ps_font(const char *);
408public:
409 int encoding_index;
410 char *encoding;
411 char *reencoded_name;
412 ~ps_font();
413 void handle_unknown_font_command(const char *command, const char *arg,
414 const char *filename, int lineno);
415 static ps_font *load_ps_font(const char *);
416};
417
418ps_font *ps_font::load_ps_font(const char *s)
419{
420 ps_font *f = new ps_font(s);
421 if (!f->load()) {
422 delete f;
423 return 0;
424 }
425 return f;
426}
427
428ps_font::ps_font(const char *nm)
429: font(nm), encoding_index(-1), encoding(0), reencoded_name(0)
430{
431}
432
433ps_font::~ps_font()
434{
435 a_delete encoding;
436 a_delete reencoded_name;
437}
438
439void ps_font::handle_unknown_font_command(const char *command, const char *arg,
440 const char *filename, int lineno)
441{
442 if (strcmp(command, "encoding") == 0) {
443 if (arg == 0)
444 error_with_file_and_line(filename, lineno,
445 "`encoding' command requires an argument");
446 else
447 encoding = strsave(arg);
448 }
449}
450
451static void handle_unknown_desc_command(const char *command, const char *arg,
452 const char *filename, int lineno)
453{
454 if (strcmp(command, "broken") == 0) {
455 if (arg == 0)
456 error_with_file_and_line(filename, lineno,
457 "`broken' command requires an argument");
458 else if (!bflag)
459 broken_flags = atoi(arg);
460 }
461}
462
463struct subencoding {
464 font *p;
465 unsigned int num;
466 int idx;
467 char *subfont;
468 const char *glyphs[256];
469 subencoding *next;
470
471 subencoding(font *, unsigned int, int, subencoding *);
472 ~subencoding();
473};
474
475subencoding::subencoding(font *f, unsigned int n, int ix, subencoding *s)
476: p(f), num(n), idx(ix), subfont(0), next(s)
477{
478 for (int i = 0; i < 256; i++)
479 glyphs[i] = 0;
480}
481
482subencoding::~subencoding()
483{
484 a_delete subfont;
485}
486
487struct style {
488 font *f;
489 subencoding *sub;
490 int point_size;
491 int height;
492 int slant;
493 style();
494 style(font *, subencoding *, int, int, int);
495 int operator==(const style &) const;
496 int operator!=(const style &) const;
497};
498
499style::style() : f(0)
500{
501}
502
503style::style(font *p, subencoding *s, int sz, int h, int sl)
504: f(p), sub(s), point_size(sz), height(h), slant(sl)
505{
506}
507
508int style::operator==(const style &s) const
509{
510 return (f == s.f
511 && sub == s.sub
512 && point_size == s.point_size
513 && height == s.height
514 && slant == s.slant);
515}
516
517int style::operator!=(const style &s) const
518{
519 return !(*this == s);
520}
521
522class ps_printer : public printer {
523 FILE *tempfp;
524 ps_output out;
525 int res;
4d3e9548 526 glyph *space_glyph;
92d0a6a6
JR
527 int pages_output;
528 int paper_length;
529 int equalise_spaces;
530 enum { SBUF_SIZE = 256 };
531 char sbuf[SBUF_SIZE];
532 int sbuf_len;
533 int sbuf_start_hpos;
534 int sbuf_vpos;
535 int sbuf_end_hpos;
536 int sbuf_space_width;
537 int sbuf_space_count;
538 int sbuf_space_diff_count;
539 int sbuf_space_code;
540 int sbuf_kern;
541 style sbuf_style;
542 color sbuf_color; // the current PS color
543 style output_style;
544 subencoding *subencodings;
545 int output_hpos;
546 int output_vpos;
547 int output_draw_point_size;
548 int line_thickness;
549 int output_line_thickness;
550 unsigned char output_space_code;
551 enum { MAX_DEFINED_STYLES = 50 };
552 style defined_styles[MAX_DEFINED_STYLES];
553 int ndefined_styles;
554 int next_encoding_index;
555 int next_subencoding_index;
556 string defs;
557 int ndefs;
558 resource_manager rm;
559 int invis_count;
560
561 void flush_sbuf();
562 void set_style(const style &);
4d3e9548 563 void set_space_code(unsigned char);
92d0a6a6 564 int set_encoding_index(ps_font *);
4d3e9548 565 subencoding *set_subencoding(font *, glyph *, unsigned char *);
92d0a6a6
JR
566 char *get_subfont(subencoding *, const char *);
567 void do_exec(char *, const environment *);
568 void do_import(char *, const environment *);
569 void do_def(char *, const environment *);
570 void do_mdef(char *, const environment *);
571 void do_file(char *, const environment *);
572 void do_invis(char *, const environment *);
573 void do_endinvis(char *, const environment *);
574 void set_line_thickness_and_color(const environment *);
575 void fill_path(const environment *);
576 void encode_fonts();
577 void encode_subfont(subencoding *);
578 void define_encoding(const char *, int);
579 void reencode_font(ps_font *);
4d3e9548 580 void set_color(color *, int = 0);
92d0a6a6
JR
581
582 const char *media_name();
583 int media_width();
584 int media_height();
585 void media_set();
586
587public:
588 ps_printer(double);
589 ~ps_printer();
4d3e9548
JL
590 void set_char(glyph *, font *, const environment *, int, const char *);
591 void draw(int, int *, int, const environment *);
92d0a6a6
JR
592 void begin_page(int);
593 void end_page(int);
4d3e9548 594 void special(char *, const environment *, char);
92d0a6a6
JR
595 font *make_font(const char *);
596 void end_of_line();
597};
598
599// `pl' is in inches
600ps_printer::ps_printer(double pl)
601: out(0, MAX_LINE_LENGTH),
602 pages_output(0),
603 sbuf_len(0),
604 subencodings(0),
605 output_hpos(-1),
606 output_vpos(-1),
607 line_thickness(-1),
608 ndefined_styles(0),
609 next_encoding_index(0),
610 next_subencoding_index(0),
611 ndefs(0),
612 invis_count(0)
613{
614 tempfp = xtmpfile();
615 out.set_file(tempfp);
616 if (linewidth < 0)
617 linewidth = DEFAULT_LINEWIDTH;
618 if (font::hor != 1)
619 fatal("horizontal resolution must be 1");
620 if (font::vert != 1)
621 fatal("vertical resolution must be 1");
622 if (font::res % (font::sizescale*72) != 0)
623 fatal("res must be a multiple of 72*sizescale");
624 int r = font::res;
625 int point = 0;
626 while (r % 10 == 0) {
627 r /= 10;
628 point++;
629 }
630 res = r;
631 out.set_fixed_point(point);
4d3e9548 632 space_glyph = name_to_glyph("space");
92d0a6a6
JR
633 if (pl == 0)
634 paper_length = font::paperlength;
635 else
636 paper_length = int(pl * font::res + 0.5);
637 if (paper_length == 0)
638 paper_length = 11 * font::res;
639 equalise_spaces = font::res >= 72000;
640}
641
642int ps_printer::set_encoding_index(ps_font *f)
643{
644 if (f->encoding_index >= 0)
645 return f->encoding_index;
646 for (font_pointer_list *p = font_list; p; p = p->next)
647 if (p->p != f) {
648 char *encoding = ((ps_font *)p->p)->encoding;
649 int encoding_index = ((ps_font *)p->p)->encoding_index;
650 if (encoding != 0 && encoding_index >= 0
651 && strcmp(f->encoding, encoding) == 0) {
652 return f->encoding_index = encoding_index;
653 }
654 }
655 return f->encoding_index = next_encoding_index++;
656}
657
4d3e9548
JL
658subencoding *ps_printer::set_subencoding(font *f, glyph *g,
659 unsigned char *codep)
92d0a6a6 660{
4d3e9548 661 unsigned int idx = f->get_code(g);
92d0a6a6
JR
662 *codep = idx % 256;
663 unsigned int num = idx >> 8;
664 if (num == 0)
665 return 0;
666 subencoding *p = 0;
667 for (p = subencodings; p; p = p->next)
668 if (p->p == f && p->num == num)
669 break;
670 if (p == 0)
671 p = subencodings = new subencoding(f, num, next_subencoding_index++,
672 subencodings);
4d3e9548 673 p->glyphs[*codep] = f->get_special_device_encoding(g);
92d0a6a6
JR
674 return p;
675}
676
677char *ps_printer::get_subfont(subencoding *sub, const char *stem)
678{
679 assert(sub != 0);
680 if (!sub->subfont) {
681 char *tem = new char[strlen(stem) + 2 + INT_DIGITS + 1];
4d3e9548 682 sprintf(tem, "%s@@%d", stem, sub->idx);
92d0a6a6
JR
683 sub->subfont = tem;
684 }
685 return sub->subfont;
686}
687
4d3e9548 688void ps_printer::set_char(glyph *g, font *f, const environment *env, int w,
92d0a6a6
JR
689 const char *)
690{
4d3e9548 691 if (g == space_glyph || invis_count > 0)
92d0a6a6
JR
692 return;
693 unsigned char code;
4d3e9548 694 subencoding *sub = set_subencoding(f, g, &code);
92d0a6a6
JR
695 style sty(f, sub, env->size, env->height, env->slant);
696 if (sty.slant != 0) {
697 if (sty.slant > 80 || sty.slant < -80) {
698 error("silly slant `%1' degrees", sty.slant);
699 sty.slant = 0;
700 }
701 }
702 if (sbuf_len > 0) {
703 if (sbuf_len < SBUF_SIZE
704 && sty == sbuf_style
705 && sbuf_vpos == env->vpos
706 && sbuf_color == *env->col) {
707 if (sbuf_end_hpos == env->hpos) {
708 sbuf[sbuf_len++] = code;
709 sbuf_end_hpos += w + sbuf_kern;
710 return;
711 }
712 if (sbuf_len == 1 && sbuf_kern == 0) {
713 sbuf_kern = env->hpos - sbuf_end_hpos;
714 sbuf_end_hpos = env->hpos + sbuf_kern + w;
715 sbuf[sbuf_len++] = code;
716 return;
717 }
718 /* If sbuf_end_hpos - sbuf_kern == env->hpos, we are better off
719 starting a new string. */
720 if (sbuf_len < SBUF_SIZE - 1 && env->hpos >= sbuf_end_hpos
721 && (sbuf_kern == 0 || sbuf_end_hpos - sbuf_kern != env->hpos)) {
722 if (sbuf_space_code < 0) {
4d3e9548
JL
723 if (f->contains(space_glyph) && !sub) {
724 sbuf_space_code = f->get_code(space_glyph);
92d0a6a6
JR
725 sbuf_space_width = env->hpos - sbuf_end_hpos;
726 sbuf_end_hpos = env->hpos + w + sbuf_kern;
727 sbuf[sbuf_len++] = sbuf_space_code;
728 sbuf[sbuf_len++] = code;
729 sbuf_space_count++;
730 return;
731 }
732 }
733 else {
734 int diff = env->hpos - sbuf_end_hpos - sbuf_space_width;
735 if (diff == 0 || (equalise_spaces && (diff == 1 || diff == -1))) {
736 sbuf_end_hpos = env->hpos + w + sbuf_kern;
737 sbuf[sbuf_len++] = sbuf_space_code;
738 sbuf[sbuf_len++] = code;
739 sbuf_space_count++;
740 if (diff == 1)
741 sbuf_space_diff_count++;
742 else if (diff == -1)
743 sbuf_space_diff_count--;
744 return;
745 }
746 }
747 }
748 }
749 flush_sbuf();
750 }
751 sbuf_len = 1;
752 sbuf[0] = code;
753 sbuf_end_hpos = env->hpos + w;
754 sbuf_start_hpos = env->hpos;
755 sbuf_vpos = env->vpos;
756 sbuf_style = sty;
757 sbuf_space_code = -1;
758 sbuf_space_width = 0;
759 sbuf_space_count = sbuf_space_diff_count = 0;
760 sbuf_kern = 0;
761 if (sbuf_color != *env->col)
762 set_color(env->col);
763}
764
765static char *make_encoding_name(int encoding_index)
766{
767 static char buf[3 + INT_DIGITS + 1];
768 sprintf(buf, "ENC%d", encoding_index);
769 return buf;
770}
771
772static char *make_subencoding_name(int subencoding_index)
773{
774 static char buf[6 + INT_DIGITS + 1];
775 sprintf(buf, "SUBENC%d", subencoding_index);
776 return buf;
777}
778
779const char *const WS = " \t\n\r";
780
781void ps_printer::define_encoding(const char *encoding, int encoding_index)
782{
783 char *vec[256];
784 int i;
785 for (i = 0; i < 256; i++)
786 vec[i] = 0;
787 char *path;
788 FILE *fp = font::open_file(encoding, &path);
789 if (fp == 0)
790 fatal("can't open encoding file `%1'", encoding);
791 int lineno = 1;
792 const int BUFFER_SIZE = 512;
793 char buf[BUFFER_SIZE];
794 while (fgets(buf, BUFFER_SIZE, fp) != 0) {
795 char *p = buf;
796 while (csspace(*p))
797 p++;
798 if (*p != '#' && *p != '\0' && (p = strtok(buf, WS)) != 0) {
799 char *q = strtok(0, WS);
800 int n = 0; // pacify compiler
801 if (q == 0 || sscanf(q, "%d", &n) != 1 || n < 0 || n >= 256)
802 fatal_with_file_and_line(path, lineno, "bad second field");
803 vec[n] = new char[strlen(p) + 1];
804 strcpy(vec[n], p);
805 }
806 lineno++;
807 }
808 a_delete path;
809 out.put_literal_symbol(make_encoding_name(encoding_index))
810 .put_delimiter('[');
811 for (i = 0; i < 256; i++) {
812 if (vec[i] == 0)
813 out.put_literal_symbol(".notdef");
814 else {
815 out.put_literal_symbol(vec[i]);
816 a_delete vec[i];
817 }
818 }
819 out.put_delimiter(']')
820 .put_symbol("def");
821 fclose(fp);
822}
823
824void ps_printer::reencode_font(ps_font *f)
825{
826 out.put_literal_symbol(f->reencoded_name)
827 .put_symbol(make_encoding_name(f->encoding_index))
828 .put_literal_symbol(f->get_internal_name())
829 .put_symbol("RE");
830}
831
832void ps_printer::encode_fonts()
833{
834 if (next_encoding_index == 0)
835 return;
836 char *done_encoding = new char[next_encoding_index];
837 for (int i = 0; i < next_encoding_index; i++)
838 done_encoding[i] = 0;
839 for (font_pointer_list *f = font_list; f; f = f->next) {
840 int encoding_index = ((ps_font *)f->p)->encoding_index;
841 if (encoding_index >= 0) {
842 assert(encoding_index < next_encoding_index);
843 if (!done_encoding[encoding_index]) {
844 done_encoding[encoding_index] = 1;
845 define_encoding(((ps_font *)f->p)->encoding, encoding_index);
846 }
847 reencode_font((ps_font *)f->p);
848 }
849 }
850 a_delete done_encoding;
851}
852
853void ps_printer::encode_subfont(subencoding *sub)
854{
855 assert(sub->glyphs != 0);
856 out.put_literal_symbol(make_subencoding_name(sub->idx))
857 .put_delimiter('[');
858 for (int i = 0; i < 256; i++)
859 {
860 if (sub->glyphs[i])
861 out.put_literal_symbol(sub->glyphs[i]);
862 else
863 out.put_literal_symbol(".notdef");
864 }
865 out.put_delimiter(']')
866 .put_symbol("def");
867}
868
869void ps_printer::set_style(const style &sty)
870{
871 char buf[1 + INT_DIGITS + 1];
872 for (int i = 0; i < ndefined_styles; i++)
873 if (sty == defined_styles[i]) {
874 sprintf(buf, "F%d", i);
875 out.put_symbol(buf);
876 return;
877 }
878 if (ndefined_styles >= MAX_DEFINED_STYLES)
879 ndefined_styles = 0;
880 sprintf(buf, "F%d", ndefined_styles);
881 out.put_literal_symbol(buf);
882 const char *psname = sty.f->get_internal_name();
883 if (psname == 0)
884 fatal("no internalname specified for font `%1'", sty.f->get_name());
885 char *encoding = ((ps_font *)sty.f)->encoding;
886 if (sty.sub == 0) {
887 if (encoding != 0) {
888 char *s = ((ps_font *)sty.f)->reencoded_name;
889 if (s == 0) {
890 int ei = set_encoding_index((ps_font *)sty.f);
891 char *tem = new char[strlen(psname) + 1 + INT_DIGITS + 1];
892 sprintf(tem, "%s@%d", psname, ei);
893 psname = tem;
894 ((ps_font *)sty.f)->reencoded_name = tem;
895 }
896 else
897 psname = s;
898 }
899 }
900 else
901 psname = get_subfont(sty.sub, psname);
902 out.put_fix_number((font::res/(72*font::sizescale))*sty.point_size);
903 if (sty.height != 0 || sty.slant != 0) {
904 int h = sty.height == 0 ? sty.point_size : sty.height;
905 h *= font::res/(72*font::sizescale);
906 int c = int(h*tan(radians(sty.slant)) + .5);
907 out.put_fix_number(c)
908 .put_fix_number(h)
909 .put_literal_symbol(psname)
910 .put_symbol("MF");
911 }
912 else {
913 out.put_literal_symbol(psname)
914 .put_symbol("SF");
915 }
916 defined_styles[ndefined_styles++] = sty;
917}
918
919void ps_printer::set_color(color *col, int fill)
920{
921 sbuf_color = *col;
922 unsigned int components[4];
923 char s[3];
924 color_scheme cs = col->get_components(components);
925 s[0] = fill ? 'F' : 'C';
926 s[2] = 0;
927 switch (cs) {
928 case DEFAULT: // black
929 out.put_symbol("0");
930 s[1] = 'g';
931 break;
932 case RGB:
933 out.put_color(Red)
934 .put_color(Green)
935 .put_color(Blue);
936 s[1] = 'r';
937 break;
938 case CMY:
939 col->get_cmyk(&Cyan, &Magenta, &Yellow, &Black);
940 // fall through
941 case CMYK:
942 out.put_color(Cyan)
943 .put_color(Magenta)
944 .put_color(Yellow)
945 .put_color(Black);
946 s[1] = 'k';
947 cmyk_flag = 1;
948 break;
949 case GRAY:
950 out.put_color(Gray);
951 s[1] = 'g';
952 break;
953 }
954 out.put_symbol(s);
955}
956
957void ps_printer::set_space_code(unsigned char c)
958{
959 out.put_literal_symbol("SC")
960 .put_number(c)
961 .put_symbol("def");
962}
963
964void ps_printer::end_of_line()
965{
966 flush_sbuf();
967 // this ensures that we do an absolute motion to the beginning of a line
968 output_vpos = output_hpos = -1;
969}
970
971void ps_printer::flush_sbuf()
972{
973 enum {
974 NONE,
975 RELATIVE_H,
976 RELATIVE_V,
977 RELATIVE_HV,
978 ABSOLUTE
979 } motion = NONE;
980 int space_flag = 0;
981 if (sbuf_len == 0)
982 return;
983 if (output_style != sbuf_style) {
984 set_style(sbuf_style);
985 output_style = sbuf_style;
986 }
987 int extra_space = 0;
988 if (output_hpos < 0 || output_vpos < 0)
989 motion = ABSOLUTE;
990 else {
991 if (output_hpos != sbuf_start_hpos)
992 motion = RELATIVE_H;
993 if (output_vpos != sbuf_vpos) {
994 if (motion != NONE)
995 motion = RELATIVE_HV;
996 else
997 motion = RELATIVE_V;
998 }
999 }
1000 if (sbuf_space_code >= 0) {
4d3e9548 1001 int w = sbuf_style.f->get_width(space_glyph, sbuf_style.point_size);
92d0a6a6
JR
1002 if (w + sbuf_kern != sbuf_space_width) {
1003 if (sbuf_space_code != output_space_code) {
1004 set_space_code(sbuf_space_code);
1005 output_space_code = sbuf_space_code;
1006 }
1007 space_flag = 1;
1008 extra_space = sbuf_space_width - w - sbuf_kern;
1009 if (sbuf_space_diff_count > sbuf_space_count/2)
1010 extra_space++;
1011 else if (sbuf_space_diff_count < -(sbuf_space_count/2))
1012 extra_space--;
1013 }
1014 }
1015 if (space_flag)
1016 out.put_fix_number(extra_space);
1017 if (sbuf_kern != 0)
1018 out.put_fix_number(sbuf_kern);
1019 out.put_string(sbuf, sbuf_len);
1020 char command_array[] = {'A', 'B', 'C', 'D',
1021 'E', 'F', 'G', 'H',
1022 'I', 'J', 'K', 'L',
1023 'M', 'N', 'O', 'P',
1024 'Q', 'R', 'S', 'T'};
1025 char sym[2];
1026 sym[0] = command_array[motion*4 + space_flag + 2*(sbuf_kern != 0)];
1027 sym[1] = '\0';
1028 switch (motion) {
1029 case NONE:
1030 break;
1031 case ABSOLUTE:
1032 out.put_fix_number(sbuf_start_hpos)
1033 .put_fix_number(sbuf_vpos);
1034 break;
1035 case RELATIVE_H:
1036 out.put_fix_number(sbuf_start_hpos - output_hpos);
1037 break;
1038 case RELATIVE_V:
1039 out.put_fix_number(sbuf_vpos - output_vpos);
1040 break;
1041 case RELATIVE_HV:
1042 out.put_fix_number(sbuf_start_hpos - output_hpos)
1043 .put_fix_number(sbuf_vpos - output_vpos);
1044 break;
1045 default:
1046 assert(0);
1047 }
1048 out.put_symbol(sym);
1049 output_hpos = sbuf_end_hpos;
1050 output_vpos = sbuf_vpos;
1051 sbuf_len = 0;
1052}
1053
1054void ps_printer::set_line_thickness_and_color(const environment *env)
1055{
1056 if (line_thickness < 0) {
1057 if (output_draw_point_size != env->size) {
1058 // we ought to check for overflow here
1059 int lw = ((font::res/(72*font::sizescale))*linewidth*env->size)/1000;
1060 out.put_fix_number(lw)
1061 .put_symbol("LW");
1062 output_draw_point_size = env->size;
1063 output_line_thickness = -1;
1064 }
1065 }
1066 else {
1067 if (output_line_thickness != line_thickness) {
1068 out.put_fix_number(line_thickness)
1069 .put_symbol("LW");
1070 output_line_thickness = line_thickness;
1071 output_draw_point_size = -1;
1072 }
1073 }
1074 if (sbuf_color != *env->col)
1075 set_color(env->col);
1076}
1077
1078void ps_printer::fill_path(const environment *env)
1079{
1080 if (sbuf_color == *env->fill)
1081 out.put_symbol("FL");
1082 else
1083 set_color(env->fill, 1);
1084}
1085
1086void ps_printer::draw(int code, int *p, int np, const environment *env)
1087{
1088 if (invis_count > 0)
1089 return;
1090 flush_sbuf();
1091 int fill_flag = 0;
1092 switch (code) {
1093 case 'C':
1094 fill_flag = 1;
1095 // fall through
1096 case 'c':
1097 // troff adds an extra argument to C
1098 if (np != 1 && !(code == 'C' && np == 2)) {
1099 error("1 argument required for circle");
1100 break;
1101 }
1102 out.put_fix_number(env->hpos + p[0]/2)
1103 .put_fix_number(env->vpos)
1104 .put_fix_number(p[0]/2)
1105 .put_symbol("DC");
1106 if (fill_flag)
1107 fill_path(env);
1108 else {
1109 set_line_thickness_and_color(env);
1110 out.put_symbol("ST");
1111 }
1112 break;
1113 case 'l':
1114 if (np != 2) {
1115 error("2 arguments required for line");
1116 break;
1117 }
1118 set_line_thickness_and_color(env);
1119 out.put_fix_number(p[0] + env->hpos)
1120 .put_fix_number(p[1] + env->vpos)
1121 .put_fix_number(env->hpos)
1122 .put_fix_number(env->vpos)
1123 .put_symbol("DL");
1124 break;
1125 case 'E':
1126 fill_flag = 1;
1127 // fall through
1128 case 'e':
1129 if (np != 2) {
1130 error("2 arguments required for ellipse");
1131 break;
1132 }
1133 out.put_fix_number(p[0])
1134 .put_fix_number(p[1])
1135 .put_fix_number(env->hpos + p[0]/2)
1136 .put_fix_number(env->vpos)
1137 .put_symbol("DE");
1138 if (fill_flag)
1139 fill_path(env);
1140 else {
1141 set_line_thickness_and_color(env);
1142 out.put_symbol("ST");
1143 }
1144 break;
1145 case 'P':
1146 fill_flag = 1;
1147 // fall through
1148 case 'p':
1149 {
1150 if (np & 1) {
1151 error("even number of arguments required for polygon");
1152 break;
1153 }
1154 if (np == 0) {
1155 error("no arguments for polygon");
1156 break;
1157 }
1158 out.put_fix_number(env->hpos)
1159 .put_fix_number(env->vpos)
1160 .put_symbol("MT");
1161 for (int i = 0; i < np; i += 2)
1162 out.put_fix_number(p[i])
1163 .put_fix_number(p[i+1])
1164 .put_symbol("RL");
1165 out.put_symbol("CL");
1166 if (fill_flag)
1167 fill_path(env);
1168 else {
1169 set_line_thickness_and_color(env);
1170 out.put_symbol("ST");
1171 }
1172 break;
1173 }
1174 case '~':
1175 {
1176 if (np & 1) {
1177 error("even number of arguments required for spline");
1178 break;
1179 }
1180 if (np == 0) {
1181 error("no arguments for spline");
1182 break;
1183 }
1184 out.put_fix_number(env->hpos)
1185 .put_fix_number(env->vpos)
1186 .put_symbol("MT");
1187 out.put_fix_number(p[0]/2)
1188 .put_fix_number(p[1]/2)
1189 .put_symbol("RL");
1190 /* tnum/tden should be between 0 and 1; the closer it is to 1
1191 the tighter the curve will be to the guiding lines; 2/3
1192 is the standard value */
1193 const int tnum = 2;
1194 const int tden = 3;
1195 for (int i = 0; i < np - 2; i += 2) {
1196 out.put_fix_number((p[i]*tnum)/(2*tden))
1197 .put_fix_number((p[i + 1]*tnum)/(2*tden))
1198 .put_fix_number(p[i]/2 + (p[i + 2]*(tden - tnum))/(2*tden))
1199 .put_fix_number(p[i + 1]/2 + (p[i + 3]*(tden - tnum))/(2*tden))
1200 .put_fix_number((p[i] - p[i]/2) + p[i + 2]/2)
1201 .put_fix_number((p[i + 1] - p[i + 1]/2) + p[i + 3]/2)
1202 .put_symbol("RC");
1203 }
1204 out.put_fix_number(p[np - 2] - p[np - 2]/2)
1205 .put_fix_number(p[np - 1] - p[np - 1]/2)
1206 .put_symbol("RL");
1207 set_line_thickness_and_color(env);
1208 out.put_symbol("ST");
1209 }
1210 break;
1211 case 'a':
1212 {
1213 if (np != 4) {
1214 error("4 arguments required for arc");
1215 break;
1216 }
1217 set_line_thickness_and_color(env);
1218 double c[2];
1219 if (adjust_arc_center(p, c))
1220 out.put_fix_number(env->hpos + int(c[0]))
1221 .put_fix_number(env->vpos + int(c[1]))
1222 .put_fix_number(int(sqrt(c[0]*c[0] + c[1]*c[1])))
1223 .put_float(degrees(atan2(-c[1], -c[0])))
1224 .put_float(degrees(atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0])))
1225 .put_symbol("DA");
1226 else
1227 out.put_fix_number(p[0] + p[2] + env->hpos)
1228 .put_fix_number(p[1] + p[3] + env->vpos)
1229 .put_fix_number(env->hpos)
1230 .put_fix_number(env->vpos)
1231 .put_symbol("DL");
1232 }
1233 break;
1234 case 't':
1235 if (np == 0)
1236 line_thickness = -1;
1237 else {
1238 // troff gratuitously adds an extra 0
1239 if (np != 1 && np != 2) {
1240 error("0 or 1 argument required for thickness");
1241 break;
1242 }
1243 line_thickness = p[0];
1244 }
1245 break;
1246 default:
1247 error("unrecognised drawing command `%1'", char(code));
1248 break;
1249 }
1250 output_hpos = output_vpos = -1;
1251}
1252
1253const char *ps_printer::media_name()
1254{
1255 return "Default";
1256}
1257
1258int ps_printer::media_width()
1259{
1260 /*
1261 * NOTE:
1262 * Although paper size is defined as real numbers, it seems to be
1263 * a common convention to round to the nearest postscript unit.
1264 * For example, a4 is really 595.276 by 841.89 but we use 595 by 842.
1265 *
1266 * This is probably a good compromise, especially since the
1267 * Postscript definition specifies that media
1268 * matching should be done within a tolerance of 5 units.
1269 */
1270 return int(user_paper_width ? user_paper_width*72.0 + 0.5
1271 : font::paperwidth*72.0/font::res + 0.5);
1272}
1273
1274int ps_printer::media_height()
1275{
1276 return int(user_paper_length ? user_paper_length*72.0 + 0.5
1277 : paper_length*72.0/font::res + 0.5);
1278}
1279
1280void ps_printer::media_set()
1281{
1282 /*
1283 * The setpagedevice implies an erasepage and initgraphics, and
1284 * must thus precede any descriptions for a particular page.
1285 *
1286 * NOTE:
1287 * This does not work with ps2pdf when there are included eps
1288 * segments that contain PageSize/setpagedevice.
1289 * This might be a bug in ghostscript -- must be investigated.
1290 * Using setpagedevice in an .eps is really the wrong concept, anyway.
1291 *
1292 * NOTE:
1293 * For the future, this is really the place to insert other
1294 * media selection features, like:
1295 * MediaColor
1296 * MediaPosition
1297 * MediaType
1298 * MediaWeight
1299 * MediaClass
1300 * TraySwitch
1301 * ManualFeed
1302 * InsertSheet
1303 * Duplex
1304 * Collate
1305 * ProcessColorModel
1306 * etc.
1307 */
1308 if (!(broken_flags & (USE_PS_ADOBE_2_0|NO_PAPERSIZE))) {
1309 out.begin_comment("BeginFeature:")
1310 .comment_arg("*PageSize")
1311 .comment_arg(media_name())
1312 .end_comment();
1313 int w = media_width();
1314 int h = media_height();
1315 if (w > 0 && h > 0)
1316 // warning to user is done elsewhere
1317 fprintf(out.get_file(),
1318 "<< /PageSize [ %d %d ] /ImagingBBox null >> setpagedevice\n",
1319 w, h);
1320 out.simple_comment("EndFeature");
1321 }
1322}
1323
1324void ps_printer::begin_page(int n)
1325{
1326 out.begin_comment("Page:")
1327 .comment_arg(i_to_a(n));
1328 out.comment_arg(i_to_a(++pages_output))
1329 .end_comment();
1330 output_style.f = 0;
1331 output_space_code = 32;
1332 output_draw_point_size = -1;
1333 output_line_thickness = -1;
1334 output_hpos = output_vpos = -1;
1335 ndefined_styles = 0;
1336 out.simple_comment("BeginPageSetup");
1337
1338#if 0
1339 /*
1340 * NOTE:
1341 * may decide to do this once per page
1342 */
1343 media_set();
1344#endif
1345
1346 out.put_symbol("BP")
1347 .simple_comment("EndPageSetup");
1348 if (sbuf_color != default_color)
1349 set_color(&sbuf_color);
1350}
1351
1352void ps_printer::end_page(int)
1353{
1354 flush_sbuf();
1355 set_color(&default_color);
1356 out.put_symbol("EP");
1357 if (invis_count != 0) {
1358 error("missing `endinvis' command");
1359 invis_count = 0;
1360 }
1361}
1362
1363font *ps_printer::make_font(const char *nm)
1364{
1365 return ps_font::load_ps_font(nm);
1366}
1367
1368ps_printer::~ps_printer()
1369{
1370 out.simple_comment("Trailer")
1371 .put_symbol("end")
1372 .simple_comment("EOF");
1373 if (fseek(tempfp, 0L, 0) < 0)
1374 fatal("fseek on temporary file failed");
1375 fputs("%!PS-Adobe-", stdout);
1376 fputs((broken_flags & USE_PS_ADOBE_2_0) ? "2.0" : "3.0", stdout);
1377 putchar('\n');
1378 out.set_file(stdout);
1379 if (cmyk_flag)
1380 out.begin_comment("Extensions:")
1381 .comment_arg("CMYK")
1382 .end_comment();
1383 out.begin_comment("Creator:")
1384 .comment_arg("groff")
1385 .comment_arg("version")
1386 .comment_arg(Version_string)
1387 .end_comment();
1388 {
1389 fputs("%%CreationDate: ", out.get_file());
1390#ifdef LONG_FOR_TIME_T
1391 long
1392#else
1393 time_t
1394#endif
1395 t = time(0);
1396 fputs(ctime(&t), out.get_file());
1397 }
1398 for (font_pointer_list *f = font_list; f; f = f->next) {
1399 ps_font *psf = (ps_font *)(f->p);
1400 rm.need_font(psf->get_internal_name());
1401 }
1402 rm.print_header_comments(out);
1403 out.begin_comment("Pages:")
1404 .comment_arg(i_to_a(pages_output))
1405 .end_comment();
1406 out.begin_comment("PageOrder:")
1407 .comment_arg("Ascend")
1408 .end_comment();
1409 if (!(broken_flags & NO_PAPERSIZE)) {
1410 int w = media_width();
1411 int h = media_height();
1412 if (w > 0 && h > 0)
1413 fprintf(out.get_file(),
1414 "%%%%DocumentMedia: %s %d %d %d %s %s\n",
1415 media_name(), // tag name of media
1416 w, // media width
1417 h, // media height
1418 0, // weight in g/m2
1419 "()", // paper color, e.g. white
1420 "()" // preprinted form type
1421 );
1422 else {
1423 if (h <= 0)
1424 // see ps_printer::ps_printer
1425 warning("bad paper height, defaulting to 11i");
1426 if (w <= 0)
1427 warning("bad paper width");
1428 }
1429 }
1430 out.begin_comment("Orientation:")
1431 .comment_arg(landscape_flag ? "Landscape" : "Portrait")
1432 .end_comment();
1433 if (ncopies != 1) {
1434 out.end_line();
1435 fprintf(out.get_file(), "%%%%Requirements: numcopies(%d)\n", ncopies);
1436 }
1437 out.simple_comment("EndComments");
1438 if (!(broken_flags & NO_PAPERSIZE)) {
1439 /* gv works fine without this one, but it really should be there. */
1440 out.simple_comment("BeginDefaults");
1441 fprintf(out.get_file(), "%%%%PageMedia: %s\n", media_name());
1442 out.simple_comment("EndDefaults");
1443 }
1444 out.simple_comment("BeginProlog");
1445 rm.output_prolog(out);
1446 if (!(broken_flags & NO_SETUP_SECTION)) {
1447 out.simple_comment("EndProlog");
1448 out.simple_comment("BeginSetup");
1449 }
1450#if 1
1451 /*
1452 * Define paper (i.e., media) size for entire document here.
1453 * This allows ps2pdf to correctly determine page size, for instance.
1454 */
1455 media_set();
1456#endif
1457 rm.document_setup(out);
1458 out.put_symbol(dict_name)
1459 .put_symbol("begin");
1460 if (ndefs > 0)
1461 ndefs += DEFS_DICT_SPARE;
1462 out.put_literal_symbol(defs_dict_name)
1463 .put_number(ndefs + 1)
1464 .put_symbol("dict")
1465 .put_symbol("def");
1466 out.put_symbol(defs_dict_name)
1467 .put_symbol("begin");
1468 out.put_literal_symbol("u")
1469 .put_delimiter('{')
1470 .put_fix_number(1)
1471 .put_symbol("mul")
1472 .put_delimiter('}')
1473 .put_symbol("bind")
1474 .put_symbol("def");
1475 defs += '\0';
1476 out.special(defs.contents());
1477 out.put_symbol("end");
1478 if (ncopies != 1)
1479 out.put_literal_symbol("#copies")
1480 .put_number(ncopies)
1481 .put_symbol("def");
1482 out.put_literal_symbol("RES")
1483 .put_number(res)
1484 .put_symbol("def");
1485 out.put_literal_symbol("PL");
1486 if (guess_flag)
1487 out.put_symbol("PLG");
1488 else
1489 out.put_fix_number(paper_length);
1490 out.put_symbol("def");
1491 out.put_literal_symbol("LS")
1492 .put_symbol(landscape_flag ? "true" : "false")
1493 .put_symbol("def");
1494 if (manual_feed_flag) {
1495 out.begin_comment("BeginFeature:")
1496 .comment_arg("*ManualFeed")
1497 .comment_arg("True")
1498 .end_comment()
1499 .put_symbol("MANUAL")
1500 .simple_comment("EndFeature");
1501 }
1502 encode_fonts();
1503 while (subencodings) {
1504 subencoding *tem = subencodings;
1505 subencodings = subencodings->next;
1506 encode_subfont(tem);
1507 out.put_literal_symbol(tem->subfont)
1508 .put_symbol(make_subencoding_name(tem->idx))
1509 .put_literal_symbol(tem->p->get_internal_name())
1510 .put_symbol("RE");
1511 delete tem;
1512 }
1513 out.simple_comment((broken_flags & NO_SETUP_SECTION)
1514 ? "EndProlog"
1515 : "EndSetup");
1516 out.end_line();
1517 out.copy_file(tempfp);
1518 fclose(tempfp);
1519}
1520
1521void ps_printer::special(char *arg, const environment *env, char type)
1522{
1523 if (type != 'p')
1524 return;
1525 typedef void (ps_printer::*SPECIAL_PROCP)(char *, const environment *);
1526 static struct {
1527 const char *name;
1528 SPECIAL_PROCP proc;
1529 } proc_table[] = {
1530 { "exec", &ps_printer::do_exec },
1531 { "def", &ps_printer::do_def },
1532 { "mdef", &ps_printer::do_mdef },
1533 { "import", &ps_printer::do_import },
1534 { "file", &ps_printer::do_file },
1535 { "invis", &ps_printer::do_invis },
1536 { "endinvis", &ps_printer::do_endinvis },
1537 };
1538 char *p;
1539 for (p = arg; *p == ' ' || *p == '\n'; p++)
1540 ;
1541 char *tag = p;
1542 for (; *p != '\0' && *p != ':' && *p != ' ' && *p != '\n'; p++)
1543 ;
1544 if (*p == '\0' || strncmp(tag, "ps", p - tag) != 0) {
1545 error("X command without `ps:' tag ignored");
1546 return;
1547 }
1548 p++;
1549 for (; *p == ' ' || *p == '\n'; p++)
1550 ;
1551 char *command = p;
1552 for (; *p != '\0' && *p != ' ' && *p != '\n'; p++)
1553 ;
1554 if (*command == '\0') {
1555 error("empty X command ignored");
1556 return;
1557 }
1558 for (unsigned int i = 0; i < sizeof(proc_table)/sizeof(proc_table[0]); i++)
1559 if (strncmp(command, proc_table[i].name, p - command) == 0) {
1560 (this->*(proc_table[i].proc))(p, env);
1561 return;
1562 }
1563 error("X command `%1' not recognised", command);
1564}
1565
1566// A conforming PostScript document must not have lines longer
1567// than 255 characters (excluding line termination characters).
1568
1569static int check_line_lengths(const char *p)
1570{
1571 for (;;) {
1572 const char *end = strchr(p, '\n');
1573 if (end == 0)
1574 end = strchr(p, '\0');
1575 if (end - p > 255)
1576 return 0;
1577 if (*end == '\0')
1578 break;
1579 p = end + 1;
1580 }
1581 return 1;
1582}
1583
1584void ps_printer::do_exec(char *arg, const environment *env)
1585{
1586 flush_sbuf();
1587 while (csspace(*arg))
1588 arg++;
1589 if (*arg == '\0') {
1590 error("missing argument to X exec command");
1591 return;
1592 }
4d3e9548
JL
1593 if (!check_line_lengths(arg))
1594 warning("lines in X exec command should"
1595 " not be more than 255 characters long");
92d0a6a6
JR
1596 out.put_fix_number(env->hpos)
1597 .put_fix_number(env->vpos)
1598 .put_symbol("EBEGIN")
1599 .special(arg)
1600 .put_symbol("EEND");
1601 output_hpos = output_vpos = -1;
1602 output_style.f = 0;
1603 output_draw_point_size = -1;
1604 output_line_thickness = -1;
1605 ndefined_styles = 0;
1606 if (!ndefs)
1607 ndefs = 1;
1608}
1609
1610void ps_printer::do_file(char *arg, const environment *env)
1611{
1612 flush_sbuf();
1613 while (csspace(*arg))
1614 arg++;
1615 if (*arg == '\0') {
1616 error("missing argument to X file command");
1617 return;
1618 }
1619 const char *filename = arg;
1620 do {
1621 ++arg;
1622 } while (*arg != '\0' && *arg != ' ' && *arg != '\n');
1623 out.put_fix_number(env->hpos)
1624 .put_fix_number(env->vpos)
1625 .put_symbol("EBEGIN");
1626 rm.import_file(filename, out);
1627 out.put_symbol("EEND");
1628 output_hpos = output_vpos = -1;
1629 output_style.f = 0;
1630 output_draw_point_size = -1;
1631 output_line_thickness = -1;
1632 ndefined_styles = 0;
1633 if (!ndefs)
1634 ndefs = 1;
1635}
1636
1637void ps_printer::do_def(char *arg, const environment *)
1638{
1639 flush_sbuf();
1640 while (csspace(*arg))
1641 arg++;
4d3e9548
JL
1642 if (!check_line_lengths(arg))
1643 warning("lines in X def command should"
1644 " not be more than 255 characters long");
92d0a6a6
JR
1645 defs += arg;
1646 if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n')
1647 defs += '\n';
1648 ndefs++;
1649}
1650
1651// Like def, but the first argument says how many definitions it contains.
1652
1653void ps_printer::do_mdef(char *arg, const environment *)
1654{
1655 flush_sbuf();
1656 char *p;
1657 int n = (int)strtol(arg, &p, 10);
1658 if (n == 0 && p == arg) {
1659 error("first argument to X mdef must be an integer");
1660 return;
1661 }
1662 if (n < 0) {
1663 error("out of range argument `%1' to X mdef command", int(n));
1664 return;
1665 }
1666 arg = p;
1667 while (csspace(*arg))
1668 arg++;
4d3e9548
JL
1669 if (!check_line_lengths(arg))
1670 warning("lines in X mdef command should"
1671 " not be more than 255 characters long");
92d0a6a6
JR
1672 defs += arg;
1673 if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n')
1674 defs += '\n';
1675 ndefs += n;
1676}
1677
1678void ps_printer::do_import(char *arg, const environment *env)
1679{
1680 flush_sbuf();
1681 while (*arg == ' ' || *arg == '\n')
1682 arg++;
1683 char *p;
1684 for (p = arg; *p != '\0' && *p != ' ' && *p != '\n'; p++)
1685 ;
1686 if (*p != '\0')
1687 *p++ = '\0';
1688 int parms[6];
1689 int nparms = 0;
1690 while (nparms < 6) {
1691 char *end;
1692 long n = strtol(p, &end, 10);
1693 if (n == 0 && end == p)
1694 break;
1695 parms[nparms++] = int(n);
1696 p = end;
1697 }
1698 if (csalpha(*p) && (p[1] == '\0' || p[1] == ' ' || p[1] == '\n')) {
1699 error("scaling indicators not allowed in arguments for X import command");
1700 return;
1701 }
1702 while (*p == ' ' || *p == '\n')
1703 p++;
1704 if (nparms < 5) {
1705 if (*p == '\0')
1706 error("too few arguments for X import command");
1707 else
1708 error("invalid argument `%1' for X import command", p);
1709 return;
1710 }
1711 if (*p != '\0') {
1712 error("superfluous argument `%1' for X import command", p);
1713 return;
1714 }
1715 int llx = parms[0];
1716 int lly = parms[1];
1717 int urx = parms[2];
1718 int ury = parms[3];
1719 int desired_width = parms[4];
1720 int desired_height = parms[5];
1721 if (desired_width <= 0) {
1722 error("bad width argument `%1' for X import command: must be > 0",
1723 desired_width);
1724 return;
1725 }
1726 if (nparms == 6 && desired_height <= 0) {
1727 error("bad height argument `%1' for X import command: must be > 0",
1728 desired_height);
1729 return;
1730 }
1731 if (llx == urx) {
1732 error("llx and urx arguments for X import command must not be equal");
1733 return;
1734 }
1735 if (lly == ury) {
1736 error("lly and ury arguments for X import command must not be equal");
1737 return;
1738 }
1739 if (nparms == 5) {
1740 int old_wid = urx - llx;
1741 int old_ht = ury - lly;
1742 if (old_wid < 0)
1743 old_wid = -old_wid;
1744 if (old_ht < 0)
1745 old_ht = -old_ht;
1746 desired_height = int(desired_width*(double(old_ht)/double(old_wid)) + .5);
1747 }
1748 if (env->vpos - desired_height < 0)
1749 warning("top of imported graphic is above the top of the page");
1750 out.put_number(llx)
1751 .put_number(lly)
1752 .put_fix_number(desired_width)
1753 .put_number(urx - llx)
1754 .put_fix_number(-desired_height)
1755 .put_number(ury - lly)
1756 .put_fix_number(env->hpos)
1757 .put_fix_number(env->vpos)
1758 .put_symbol("PBEGIN");
1759 rm.import_file(arg, out);
1760 // do this here just in case application defines PEND
1761 out.put_symbol("end")
1762 .put_symbol("PEND");
1763}
1764
1765void ps_printer::do_invis(char *, const environment *)
1766{
1767 invis_count++;
1768}
1769
1770void ps_printer::do_endinvis(char *, const environment *)
1771{
1772 if (invis_count == 0)
1773 error("unbalanced `endinvis' command");
1774 else
1775 --invis_count;
1776}
1777
1778printer *make_printer()
1779{
1780 return new ps_printer(user_paper_length);
1781}
1782
1783static void usage(FILE *stream);
1784
1785int main(int argc, char **argv)
1786{
1787 setlocale(LC_NUMERIC, "C");
1788 program_name = argv[0];
1789 string env;
1790 static char stderr_buf[BUFSIZ];
1791 setbuf(stderr, stderr_buf);
1792 int c;
1793 static const struct option long_options[] = {
1794 { "help", no_argument, 0, CHAR_MAX + 1 },
1795 { "version", no_argument, 0, 'v' },
1796 { NULL, 0, 0, 0 }
1797 };
1798 while ((c = getopt_long(argc, argv, "b:c:F:gI:lmp:P:vw:", long_options, NULL))
1799 != EOF)
1800 switch(c) {
1801 case 'b':
1802 // XXX check this
1803 broken_flags = atoi(optarg);
1804 bflag = 1;
1805 break;
1806 case 'c':
1807 if (sscanf(optarg, "%d", &ncopies) != 1 || ncopies <= 0) {
1808 error("bad number of copies `%s'", optarg);
1809 ncopies = 1;
1810 }
1811 break;
1812 case 'F':
1813 font::command_line_font_dir(optarg);
1814 break;
1815 case 'g':
1816 guess_flag = 1;
1817 break;
1818 case 'I':
1819 include_search_path.command_line_dir(optarg);
1820 break;
1821 case 'l':
1822 landscape_flag = 1;
1823 break;
1824 case 'm':
1825 manual_feed_flag = 1;
1826 break;
1827 case 'p':
1828 if (!font::scan_papersize(optarg, 0,
1829 &user_paper_length, &user_paper_width))
1830 error("invalid custom paper size `%1' ignored", optarg);
1831 break;
1832 case 'P':
1833 env = "GROPS_PROLOGUE";
1834 env += '=';
1835 env += optarg;
1836 env += '\0';
1837 if (putenv(strsave(env.contents())))
1838 fatal("putenv failed");
1839 break;
1840 case 'v':
1841 printf("GNU grops (groff) version %s\n", Version_string);
1842 exit(0);
1843 break;
1844 case 'w':
1845 if (sscanf(optarg, "%d", &linewidth) != 1 || linewidth < 0) {
1846 error("bad linewidth `%1'", optarg);
1847 linewidth = -1;
1848 }
1849 break;
1850 case CHAR_MAX + 1: // --help
1851 usage(stdout);
1852 exit(0);
1853 break;
1854 case '?':
1855 usage(stderr);
1856 exit(1);
1857 break;
1858 default:
1859 assert(0);
1860 }
1861 font::set_unknown_desc_command_handler(handle_unknown_desc_command);
1862 SET_BINARY(fileno(stdout));
1863 if (optind >= argc)
1864 do_file("-");
1865 else {
1866 for (int i = optind; i < argc; i++)
1867 do_file(argv[i]);
1868 }
1869 return 0;
1870}
1871
1872static void usage(FILE *stream)
1873{
1874 fprintf(stream,
1875"usage: %s [-glmv] [-b n] [-c n] [-w n] [-I dir] [-P prologue]\n"
1876" [-F dir] [files ...]\n",
1877 program_name);
1878}