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