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