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