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