| Commit | Line | Data |
|---|---|---|
| 92d0a6a6 | 1 | // -*- C++ -*- |
| 4d3e9548 JL |
2 | /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005, |
| 3 | 2006, 2007, 2009 | |
| 92d0a6a6 JR |
4 | Free Software Foundation, Inc. |
| 5 | Written by James Clark (jjc@jclark.com) | |
| 6 | ||
| 7 | This file is part of groff. | |
| 8 | ||
| 9 | groff is free software; you can redistribute it and/or modify it under | |
| 10 | the terms of the GNU General Public License as published by the Free | |
| 4d3e9548 JL |
11 | Software Foundation, either version 3 of the License, or |
| 12 | (at your option) any later version. | |
| 92d0a6a6 JR |
13 | |
| 14 | groff is distributed in the hope that it will be useful, but WITHOUT ANY | |
| 15 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 16 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 17 | for more details. | |
| 18 | ||
| 4d3e9548 JL |
19 | You should have received a copy of the GNU General Public License |
| 20 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
| 92d0a6a6 JR |
21 | |
| 22 | /* | |
| 23 | * PostScript documentation: | |
| 24 | * http://www.adobe.com/products/postscript/pdfs/PLRM.pdf | |
| 4d3e9548 | 25 | * http://partners.adobe.com/public/developer/en/ps/5001.DSC_Spec.pdf |
| 92d0a6a6 JR |
26 | */ |
| 27 | ||
| 28 | #include "driver.h" | |
| 29 | #include "stringclass.h" | |
| 30 | #include "cset.h" | |
| 31 | #include "nonposix.h" | |
| 32 | #include "paper.h" | |
| 33 | ||
| 34 | #include "ps.h" | |
| 35 | #include <time.h> | |
| 36 | ||
| 37 | #ifdef NEED_DECLARATION_PUTENV | |
| 38 | extern "C" { | |
| 39 | int putenv(const char *); | |
| 40 | } | |
| 41 | #endif /* NEED_DECLARATION_PUTENV */ | |
| 42 | ||
| 43 | extern "C" const char *Version_string; | |
| 44 | ||
| 45 | // search path defaults to the current directory | |
| 46 | search_path include_search_path(0, 0, 0, 1); | |
| 47 | ||
| 48 | static int landscape_flag = 0; | |
| 49 | static int manual_feed_flag = 0; | |
| 50 | static int ncopies = 1; | |
| 51 | static int linewidth = -1; | |
| 52 | // Non-zero means generate PostScript code that guesses the paper | |
| 53 | // length using the imageable area. | |
| 54 | static int guess_flag = 0; | |
| 55 | static double user_paper_length = 0; | |
| 56 | static double user_paper_width = 0; | |
| 57 | ||
| 58 | // Non-zero if -b was specified on the command line. | |
| 59 | static int bflag = 0; | |
| 60 | unsigned broken_flags = 0; | |
| 61 | ||
| 62 | // Non-zero means we need the CMYK extension for PostScript Level 1 | |
| 63 | static 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 | ||
| 69 | const char *const dict_name = "grops"; | |
| 70 | const char *const defs_dict_name = "DEFS"; | |
| 71 | const int DEFS_DICT_SPARE = 50; | |
| 72 | ||
| 73 | double degrees(double r) | |
| 74 | { | |
| 75 | return r*180.0/PI; | |
| 76 | } | |
| 77 | ||
| 78 | double 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 | ||
| 87 | inline int is_ascii(char c) | |
| 88 | { | |
| 89 | return (unsigned char)c < 0200; | |
| 90 | } | |
| 91 | ||
| 92 | ps_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 | ||
| 97 | ps_output &ps_output::set_file(FILE *f) | |
| 98 | { | |
| 99 | fp = f; | |
| 100 | col = 0; | |
| 101 | return *this; | |
| 102 | } | |
| 103 | ||
| 104 | ps_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 | ||
| 112 | ps_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 | ||
| 122 | ps_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 | ||
| 137 | ps_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 | ||
| 150 | ps_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 | ||
| 161 | ps_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 | ||
| 171 | ps_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 | ||
| 185 | ps_output &ps_output::set_fixed_point(int n) | |
| 186 | { | |
| 187 | assert(n >= 0 && n <= 10); | |
| 188 | fixed_point = n; | |
| 189 | return *this; | |
| 190 | } | |
| 191 | ||
| 192 | ps_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 | ||
| 204 | ps_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 | ||
| 290 | ps_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 | ||
| 310 | ps_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 | ||
| 329 | ps_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 | ||
| 354 | ps_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 | ||
| 372 | ps_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 | ||
| 392 | ps_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 | ||
| 406 | class ps_font : public font { | |
| 407 | ps_font(const char *); | |
| 408 | public: | |
| 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 | ||
| 418 | ps_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 | ||
| 428 | ps_font::ps_font(const char *nm) | |
| 429 | : font(nm), encoding_index(-1), encoding(0), reencoded_name(0) | |
| 430 | { | |
| 431 | } | |
| 432 | ||
| 433 | ps_font::~ps_font() | |
| 434 | { | |
| 435 | a_delete encoding; | |
| 436 | a_delete reencoded_name; | |
| 437 | } | |
| 438 | ||
| 439 | void 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 | ||
| 451 | static 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 | ||
| 463 | struct 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 | ||
| 475 | subencoding::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 | ||
| 482 | subencoding::~subencoding() | |
| 483 | { | |
| 484 | a_delete subfont; | |
| 485 | } | |
| 486 | ||
| 487 | struct 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 | ||
| 499 | style::style() : f(0) | |
| 500 | { | |
| 501 | } | |
| 502 | ||
| 503 | style::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 | ||
| 508 | int 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 | ||
| 517 | int style::operator!=(const style &s) const | |
| 518 | { | |
| 519 | return !(*this == s); | |
| 520 | } | |
| 521 | ||
| 522 | class ps_printer : public printer { | |
| 523 | FILE *tempfp; | |
| 524 | ps_output out; | |
| 525 | int res; | |
| 4d3e9548 | 526 | glyph *space_glyph; |
| 92d0a6a6 JR |
527 | int pages_output; |
| 528 | int paper_length; | |
| 529 | int equalise_spaces; | |
| 530 | enum { SBUF_SIZE = 256 }; | |
| 531 | char sbuf[SBUF_SIZE]; | |
| 532 | int sbuf_len; | |
| 533 | int sbuf_start_hpos; | |
| 534 | int sbuf_vpos; | |
| 535 | int sbuf_end_hpos; | |
| 536 | int sbuf_space_width; | |
| 537 | int sbuf_space_count; | |
| 538 | int sbuf_space_diff_count; | |
| 539 | int sbuf_space_code; | |
| 540 | int sbuf_kern; | |
| 541 | style sbuf_style; | |
| 542 | color sbuf_color; // the current PS color | |
| 543 | style output_style; | |
| 544 | subencoding *subencodings; | |
| 545 | int output_hpos; | |
| 546 | int output_vpos; | |
| 547 | int output_draw_point_size; | |
| 548 | int line_thickness; | |
| 549 | int output_line_thickness; | |
| 550 | unsigned char output_space_code; | |
| 551 | enum { MAX_DEFINED_STYLES = 50 }; | |
| 552 | style defined_styles[MAX_DEFINED_STYLES]; | |
| 553 | int ndefined_styles; | |
| 554 | int next_encoding_index; | |
| 555 | int next_subencoding_index; | |
| 556 | string defs; | |
| 557 | int ndefs; | |
| 558 | resource_manager rm; | |
| 559 | int invis_count; | |
| 560 | ||
| 561 | void flush_sbuf(); | |
| 562 | void set_style(const style &); | |
| 4d3e9548 | 563 | void set_space_code(unsigned char); |
| 92d0a6a6 | 564 | int set_encoding_index(ps_font *); |
| 4d3e9548 | 565 | subencoding *set_subencoding(font *, glyph *, unsigned char *); |
| 92d0a6a6 JR |
566 | char *get_subfont(subencoding *, const char *); |
| 567 | void do_exec(char *, const environment *); | |
| 568 | void do_import(char *, const environment *); | |
| 569 | void do_def(char *, const environment *); | |
| 570 | void do_mdef(char *, const environment *); | |
| 571 | void do_file(char *, const environment *); | |
| 572 | void do_invis(char *, const environment *); | |
| 573 | void do_endinvis(char *, const environment *); | |
| 574 | void set_line_thickness_and_color(const environment *); | |
| 575 | void fill_path(const environment *); | |
| 576 | void encode_fonts(); | |
| 577 | void encode_subfont(subencoding *); | |
| 578 | void define_encoding(const char *, int); | |
| 579 | void reencode_font(ps_font *); | |
| 4d3e9548 | 580 | void set_color(color *, int = 0); |
| 92d0a6a6 JR |
581 | |
| 582 | const char *media_name(); | |
| 583 | int media_width(); | |
| 584 | int media_height(); | |
| 585 | void media_set(); | |
| 586 | ||
| 587 | public: | |
| 588 | ps_printer(double); | |
| 589 | ~ps_printer(); | |
| 4d3e9548 JL |
590 | void set_char(glyph *, font *, const environment *, int, const char *); |
| 591 | void draw(int, int *, int, const environment *); | |
| 92d0a6a6 JR |
592 | void begin_page(int); |
| 593 | void end_page(int); | |
| 4d3e9548 | 594 | void special(char *, const environment *, char); |
| 92d0a6a6 JR |
595 | font *make_font(const char *); |
| 596 | void end_of_line(); | |
| 597 | }; | |
| 598 | ||
| 599 | // `pl' is in inches | |
| 600 | ps_printer::ps_printer(double pl) | |
| 601 | : out(0, MAX_LINE_LENGTH), | |
| 602 | pages_output(0), | |
| 603 | sbuf_len(0), | |
| 604 | subencodings(0), | |
| 605 | output_hpos(-1), | |
| 606 | output_vpos(-1), | |
| 607 | line_thickness(-1), | |
| 608 | ndefined_styles(0), | |
| 609 | next_encoding_index(0), | |
| 610 | next_subencoding_index(0), | |
| 611 | ndefs(0), | |
| 612 | invis_count(0) | |
| 613 | { | |
| 614 | tempfp = xtmpfile(); | |
| 615 | out.set_file(tempfp); | |
| 616 | if (linewidth < 0) | |
| 617 | linewidth = DEFAULT_LINEWIDTH; | |
| 618 | if (font::hor != 1) | |
| 619 | fatal("horizontal resolution must be 1"); | |
| 620 | if (font::vert != 1) | |
| 621 | fatal("vertical resolution must be 1"); | |
| 622 | if (font::res % (font::sizescale*72) != 0) | |
| 623 | fatal("res must be a multiple of 72*sizescale"); | |
| 624 | int r = font::res; | |
| 625 | int point = 0; | |
| 626 | while (r % 10 == 0) { | |
| 627 | r /= 10; | |
| 628 | point++; | |
| 629 | } | |
| 630 | res = r; | |
| 631 | out.set_fixed_point(point); | |
| 4d3e9548 | 632 | space_glyph = name_to_glyph("space"); |
| 92d0a6a6 JR |
633 | if (pl == 0) |
| 634 | paper_length = font::paperlength; | |
| 635 | else | |
| 636 | paper_length = int(pl * font::res + 0.5); | |
| 637 | if (paper_length == 0) | |
| 638 | paper_length = 11 * font::res; | |
| 639 | equalise_spaces = font::res >= 72000; | |
| 640 | } | |
| 641 | ||
| 642 | int ps_printer::set_encoding_index(ps_font *f) | |
| 643 | { | |
| 644 | if (f->encoding_index >= 0) | |
| 645 | return f->encoding_index; | |
| 646 | for (font_pointer_list *p = font_list; p; p = p->next) | |
| 647 | if (p->p != f) { | |
| 648 | char *encoding = ((ps_font *)p->p)->encoding; | |
| 649 | int encoding_index = ((ps_font *)p->p)->encoding_index; | |
| 650 | if (encoding != 0 && encoding_index >= 0 | |
| 651 | && strcmp(f->encoding, encoding) == 0) { | |
| 652 | return f->encoding_index = encoding_index; | |
| 653 | } | |
| 654 | } | |
| 655 | return f->encoding_index = next_encoding_index++; | |
| 656 | } | |
| 657 | ||
| 4d3e9548 JL |
658 | subencoding *ps_printer::set_subencoding(font *f, glyph *g, |
| 659 | unsigned char *codep) | |
| 92d0a6a6 | 660 | { |
| 4d3e9548 | 661 | unsigned int idx = f->get_code(g); |
| 92d0a6a6 JR |
662 | *codep = idx % 256; |
| 663 | unsigned int num = idx >> 8; | |
| 664 | if (num == 0) | |
| 665 | return 0; | |
| 666 | subencoding *p = 0; | |
| 667 | for (p = subencodings; p; p = p->next) | |
| 668 | if (p->p == f && p->num == num) | |
| 669 | break; | |
| 670 | if (p == 0) | |
| 671 | p = subencodings = new subencoding(f, num, next_subencoding_index++, | |
| 672 | subencodings); | |
| 4d3e9548 | 673 | p->glyphs[*codep] = f->get_special_device_encoding(g); |
| 92d0a6a6 JR |
674 | return p; |
| 675 | } | |
| 676 | ||
| 677 | char *ps_printer::get_subfont(subencoding *sub, const char *stem) | |
| 678 | { | |
| 679 | assert(sub != 0); | |
| 680 | if (!sub->subfont) { | |
| 681 | char *tem = new char[strlen(stem) + 2 + INT_DIGITS + 1]; | |
| 4d3e9548 | 682 | sprintf(tem, "%s@@%d", stem, sub->idx); |
| 92d0a6a6 JR |
683 | sub->subfont = tem; |
| 684 | } | |
| 685 | return sub->subfont; | |
| 686 | } | |
| 687 | ||
| 4d3e9548 | 688 | void ps_printer::set_char(glyph *g, font *f, const environment *env, int w, |
| 92d0a6a6 JR |
689 | const char *) |
| 690 | { | |
| 4d3e9548 | 691 | if (g == space_glyph || invis_count > 0) |
| 92d0a6a6 JR |
692 | return; |
| 693 | unsigned char code; | |
| 4d3e9548 | 694 | subencoding *sub = set_subencoding(f, g, &code); |
| 92d0a6a6 JR |
695 | style sty(f, sub, env->size, env->height, env->slant); |
| 696 | if (sty.slant != 0) { | |
| 697 | if (sty.slant > 80 || sty.slant < -80) { | |
| 698 | error("silly slant `%1' degrees", sty.slant); | |
| 699 | sty.slant = 0; | |
| 700 | } | |
| 701 | } | |
| 702 | if (sbuf_len > 0) { | |
| 703 | if (sbuf_len < SBUF_SIZE | |
| 704 | && sty == sbuf_style | |
| 705 | && sbuf_vpos == env->vpos | |
| 706 | && sbuf_color == *env->col) { | |
| 707 | if (sbuf_end_hpos == env->hpos) { | |
| 708 | sbuf[sbuf_len++] = code; | |
| 709 | sbuf_end_hpos += w + sbuf_kern; | |
| 710 | return; | |
| 711 | } | |
| 712 | if (sbuf_len == 1 && sbuf_kern == 0) { | |
| 713 | sbuf_kern = env->hpos - sbuf_end_hpos; | |
| 714 | sbuf_end_hpos = env->hpos + sbuf_kern + w; | |
| 715 | sbuf[sbuf_len++] = code; | |
| 716 | return; | |
| 717 | } | |
| 718 | /* If sbuf_end_hpos - sbuf_kern == env->hpos, we are better off | |
| 719 | starting a new string. */ | |
| 720 | if (sbuf_len < SBUF_SIZE - 1 && env->hpos >= sbuf_end_hpos | |
| 721 | && (sbuf_kern == 0 || sbuf_end_hpos - sbuf_kern != env->hpos)) { | |
| 722 | if (sbuf_space_code < 0) { | |
| 4d3e9548 JL |
723 | if (f->contains(space_glyph) && !sub) { |
| 724 | sbuf_space_code = f->get_code(space_glyph); | |
| 92d0a6a6 JR |
725 | sbuf_space_width = env->hpos - sbuf_end_hpos; |
| 726 | sbuf_end_hpos = env->hpos + w + sbuf_kern; | |
| 727 | sbuf[sbuf_len++] = sbuf_space_code; | |
| 728 | sbuf[sbuf_len++] = code; | |
| 729 | sbuf_space_count++; | |
| 730 | return; | |
| 731 | } | |
| 732 | } | |
| 733 | else { | |
| 734 | int diff = env->hpos - sbuf_end_hpos - sbuf_space_width; | |
| 735 | if (diff == 0 || (equalise_spaces && (diff == 1 || diff == -1))) { | |
| 736 | sbuf_end_hpos = env->hpos + w + sbuf_kern; | |
| 737 | sbuf[sbuf_len++] = sbuf_space_code; | |
| 738 | sbuf[sbuf_len++] = code; | |
| 739 | sbuf_space_count++; | |
| 740 | if (diff == 1) | |
| 741 | sbuf_space_diff_count++; | |
| 742 | else if (diff == -1) | |
| 743 | sbuf_space_diff_count--; | |
| 744 | return; | |
| 745 | } | |
| 746 | } | |
| 747 | } | |
| 748 | } | |
| 749 | flush_sbuf(); | |
| 750 | } | |
| 751 | sbuf_len = 1; | |
| 752 | sbuf[0] = code; | |
| 753 | sbuf_end_hpos = env->hpos + w; | |
| 754 | sbuf_start_hpos = env->hpos; | |
| 755 | sbuf_vpos = env->vpos; | |
| 756 | sbuf_style = sty; | |
| 757 | sbuf_space_code = -1; | |
| 758 | sbuf_space_width = 0; | |
| 759 | sbuf_space_count = sbuf_space_diff_count = 0; | |
| 760 | sbuf_kern = 0; | |
| 761 | if (sbuf_color != *env->col) | |
| 762 | set_color(env->col); | |
| 763 | } | |
| 764 | ||
| 765 | static 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 | ||
| 772 | static 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 | ||
| 779 | const char *const WS = " \t\n\r"; | |
| 780 | ||
| 781 | void 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 | ||
| 824 | void 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 | ||
| 832 | void 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 | ||
| 853 | void 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 | ||
| 869 | void 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 | ||
| 919 | void 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 | ||
| 957 | void 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 | ||
| 964 | void 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 | ||
| 971 | void ps_printer::flush_sbuf() | |
| 972 | { | |
| 973 | enum { | |
| 974 | NONE, | |
| 975 | RELATIVE_H, | |
| 976 | RELATIVE_V, | |
| 977 | RELATIVE_HV, | |
| 978 | ABSOLUTE | |
| 979 | } motion = NONE; | |
| 980 | int space_flag = 0; | |
| 981 | if (sbuf_len == 0) | |
| 982 | return; | |
| 983 | if (output_style != sbuf_style) { | |
| 984 | set_style(sbuf_style); | |
| 985 | output_style = sbuf_style; | |
| 986 | } | |
| 987 | int extra_space = 0; | |
| 988 | if (output_hpos < 0 || output_vpos < 0) | |
| 989 | motion = ABSOLUTE; | |
| 990 | else { | |
| 991 | if (output_hpos != sbuf_start_hpos) | |
| 992 | motion = RELATIVE_H; | |
| 993 | if (output_vpos != sbuf_vpos) { | |
| 994 | if (motion != NONE) | |
| 995 | motion = RELATIVE_HV; | |
| 996 | else | |
| 997 | motion = RELATIVE_V; | |
| 998 | } | |
| 999 | } | |
| 1000 | if (sbuf_space_code >= 0) { | |
| 4d3e9548 | 1001 | int w = sbuf_style.f->get_width(space_glyph, sbuf_style.point_size); |
| 92d0a6a6 JR |
1002 | if (w + sbuf_kern != sbuf_space_width) { |
| 1003 | if (sbuf_space_code != output_space_code) { | |
| 1004 | set_space_code(sbuf_space_code); | |
| 1005 | output_space_code = sbuf_space_code; | |
| 1006 | } | |
| 1007 | space_flag = 1; | |
| 1008 | extra_space = sbuf_space_width - w - sbuf_kern; | |
| 1009 | if (sbuf_space_diff_count > sbuf_space_count/2) | |
| 1010 | extra_space++; | |
| 1011 | else if (sbuf_space_diff_count < -(sbuf_space_count/2)) | |
| 1012 | extra_space--; | |
| 1013 | } | |
| 1014 | } | |
| 1015 | if (space_flag) | |
| 1016 | out.put_fix_number(extra_space); | |
| 1017 | if (sbuf_kern != 0) | |
| 1018 | out.put_fix_number(sbuf_kern); | |
| 1019 | out.put_string(sbuf, sbuf_len); | |
| 1020 | char command_array[] = {'A', 'B', 'C', 'D', | |
| 1021 | 'E', 'F', 'G', 'H', | |
| 1022 | 'I', 'J', 'K', 'L', | |
| 1023 | 'M', 'N', 'O', 'P', | |
| 1024 | 'Q', 'R', 'S', 'T'}; | |
| 1025 | char sym[2]; | |
| 1026 | sym[0] = command_array[motion*4 + space_flag + 2*(sbuf_kern != 0)]; | |
| 1027 | sym[1] = '\0'; | |
| 1028 | switch (motion) { | |
| 1029 | case NONE: | |
| 1030 | break; | |
| 1031 | case ABSOLUTE: | |
| 1032 | out.put_fix_number(sbuf_start_hpos) | |
| 1033 | .put_fix_number(sbuf_vpos); | |
| 1034 | break; | |
| 1035 | case RELATIVE_H: | |
| 1036 | out.put_fix_number(sbuf_start_hpos - output_hpos); | |
| 1037 | break; | |
| 1038 | case RELATIVE_V: | |
| 1039 | out.put_fix_number(sbuf_vpos - output_vpos); | |
| 1040 | break; | |
| 1041 | case RELATIVE_HV: | |
| 1042 | out.put_fix_number(sbuf_start_hpos - output_hpos) | |
| 1043 | .put_fix_number(sbuf_vpos - output_vpos); | |
| 1044 | break; | |
| 1045 | default: | |
| 1046 | assert(0); | |
| 1047 | } | |
| 1048 | out.put_symbol(sym); | |
| 1049 | output_hpos = sbuf_end_hpos; | |
| 1050 | output_vpos = sbuf_vpos; | |
| 1051 | sbuf_len = 0; | |
| 1052 | } | |
| 1053 | ||
| 1054 | void 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 | ||
| 1078 | void 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 | ||
| 1086 | void 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 | ||
| 1253 | const char *ps_printer::media_name() | |
| 1254 | { | |
| 1255 | return "Default"; | |
| 1256 | } | |
| 1257 | ||
| 1258 | int 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 | ||
| 1274 | int 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 | ||
| 1280 | void 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 | ||
| 1324 | void 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 | ||
| 1352 | void 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 | ||
| 1363 | font *ps_printer::make_font(const char *nm) | |
| 1364 | { | |
| 1365 | return ps_font::load_ps_font(nm); | |
| 1366 | } | |
| 1367 | ||
| 1368 | ps_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 | ||
| 1521 | void 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 | ||
| 1569 | static 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 | ||
| 1584 | void ps_printer::do_exec(char *arg, const environment *env) | |
| 1585 | { | |
| 1586 | flush_sbuf(); | |
| 1587 | while (csspace(*arg)) | |
| 1588 | arg++; | |
| 1589 | if (*arg == '\0') { | |
| 1590 | error("missing argument to X exec command"); | |
| 1591 | return; | |
| 1592 | } | |
| 4d3e9548 JL |
1593 | if (!check_line_lengths(arg)) |
| 1594 | warning("lines in X exec command should" | |
| 1595 | " not be more than 255 characters long"); | |
| 92d0a6a6 JR |
1596 | out.put_fix_number(env->hpos) |
| 1597 | .put_fix_number(env->vpos) | |
| 1598 | .put_symbol("EBEGIN") | |
| 1599 | .special(arg) | |
| 1600 | .put_symbol("EEND"); | |
| 1601 | output_hpos = output_vpos = -1; | |
| 1602 | output_style.f = 0; | |
| 1603 | output_draw_point_size = -1; | |
| 1604 | output_line_thickness = -1; | |
| 1605 | ndefined_styles = 0; | |
| 1606 | if (!ndefs) | |
| 1607 | ndefs = 1; | |
| 1608 | } | |
| 1609 | ||
| 1610 | void ps_printer::do_file(char *arg, const environment *env) | |
| 1611 | { | |
| 1612 | flush_sbuf(); | |
| 1613 | while (csspace(*arg)) | |
| 1614 | arg++; | |
| 1615 | if (*arg == '\0') { | |
| 1616 | error("missing argument to X file command"); | |
| 1617 | return; | |
| 1618 | } | |
| 1619 | const char *filename = arg; | |
| 1620 | do { | |
| 1621 | ++arg; | |
| 1622 | } while (*arg != '\0' && *arg != ' ' && *arg != '\n'); | |
| 1623 | out.put_fix_number(env->hpos) | |
| 1624 | .put_fix_number(env->vpos) | |
| 1625 | .put_symbol("EBEGIN"); | |
| 1626 | rm.import_file(filename, out); | |
| 1627 | out.put_symbol("EEND"); | |
| 1628 | output_hpos = output_vpos = -1; | |
| 1629 | output_style.f = 0; | |
| 1630 | output_draw_point_size = -1; | |
| 1631 | output_line_thickness = -1; | |
| 1632 | ndefined_styles = 0; | |
| 1633 | if (!ndefs) | |
| 1634 | ndefs = 1; | |
| 1635 | } | |
| 1636 | ||
| 1637 | void ps_printer::do_def(char *arg, const environment *) | |
| 1638 | { | |
| 1639 | flush_sbuf(); | |
| 1640 | while (csspace(*arg)) | |
| 1641 | arg++; | |
| 4d3e9548 JL |
1642 | if (!check_line_lengths(arg)) |
| 1643 | warning("lines in X def command should" | |
| 1644 | " not be more than 255 characters long"); | |
| 92d0a6a6 JR |
1645 | defs += arg; |
| 1646 | if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n') | |
| 1647 | defs += '\n'; | |
| 1648 | ndefs++; | |
| 1649 | } | |
| 1650 | ||
| 1651 | // Like def, but the first argument says how many definitions it contains. | |
| 1652 | ||
| 1653 | void ps_printer::do_mdef(char *arg, const environment *) | |
| 1654 | { | |
| 1655 | flush_sbuf(); | |
| 1656 | char *p; | |
| 1657 | int n = (int)strtol(arg, &p, 10); | |
| 1658 | if (n == 0 && p == arg) { | |
| 1659 | error("first argument to X mdef must be an integer"); | |
| 1660 | return; | |
| 1661 | } | |
| 1662 | if (n < 0) { | |
| 1663 | error("out of range argument `%1' to X mdef command", int(n)); | |
| 1664 | return; | |
| 1665 | } | |
| 1666 | arg = p; | |
| 1667 | while (csspace(*arg)) | |
| 1668 | arg++; | |
| 4d3e9548 JL |
1669 | if (!check_line_lengths(arg)) |
| 1670 | warning("lines in X mdef command should" | |
| 1671 | " not be more than 255 characters long"); | |
| 92d0a6a6 JR |
1672 | defs += arg; |
| 1673 | if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n') | |
| 1674 | defs += '\n'; | |
| 1675 | ndefs += n; | |
| 1676 | } | |
| 1677 | ||
| 1678 | void ps_printer::do_import(char *arg, const environment *env) | |
| 1679 | { | |
| 1680 | flush_sbuf(); | |
| 1681 | while (*arg == ' ' || *arg == '\n') | |
| 1682 | arg++; | |
| 1683 | char *p; | |
| 1684 | for (p = arg; *p != '\0' && *p != ' ' && *p != '\n'; p++) | |
| 1685 | ; | |
| 1686 | if (*p != '\0') | |
| 1687 | *p++ = '\0'; | |
| 1688 | int parms[6]; | |
| 1689 | int nparms = 0; | |
| 1690 | while (nparms < 6) { | |
| 1691 | char *end; | |
| 1692 | long n = strtol(p, &end, 10); | |
| 1693 | if (n == 0 && end == p) | |
| 1694 | break; | |
| 1695 | parms[nparms++] = int(n); | |
| 1696 | p = end; | |
| 1697 | } | |
| 1698 | if (csalpha(*p) && (p[1] == '\0' || p[1] == ' ' || p[1] == '\n')) { | |
| 1699 | error("scaling indicators not allowed in arguments for X import command"); | |
| 1700 | return; | |
| 1701 | } | |
| 1702 | while (*p == ' ' || *p == '\n') | |
| 1703 | p++; | |
| 1704 | if (nparms < 5) { | |
| 1705 | if (*p == '\0') | |
| 1706 | error("too few arguments for X import command"); | |
| 1707 | else | |
| 1708 | error("invalid argument `%1' for X import command", p); | |
| 1709 | return; | |
| 1710 | } | |
| 1711 | if (*p != '\0') { | |
| 1712 | error("superfluous argument `%1' for X import command", p); | |
| 1713 | return; | |
| 1714 | } | |
| 1715 | int llx = parms[0]; | |
| 1716 | int lly = parms[1]; | |
| 1717 | int urx = parms[2]; | |
| 1718 | int ury = parms[3]; | |
| 1719 | int desired_width = parms[4]; | |
| 1720 | int desired_height = parms[5]; | |
| 1721 | if (desired_width <= 0) { | |
| 1722 | error("bad width argument `%1' for X import command: must be > 0", | |
| 1723 | desired_width); | |
| 1724 | return; | |
| 1725 | } | |
| 1726 | if (nparms == 6 && desired_height <= 0) { | |
| 1727 | error("bad height argument `%1' for X import command: must be > 0", | |
| 1728 | desired_height); | |
| 1729 | return; | |
| 1730 | } | |
| 1731 | if (llx == urx) { | |
| 1732 | error("llx and urx arguments for X import command must not be equal"); | |
| 1733 | return; | |
| 1734 | } | |
| 1735 | if (lly == ury) { | |
| 1736 | error("lly and ury arguments for X import command must not be equal"); | |
| 1737 | return; | |
| 1738 | } | |
| 1739 | if (nparms == 5) { | |
| 1740 | int old_wid = urx - llx; | |
| 1741 | int old_ht = ury - lly; | |
| 1742 | if (old_wid < 0) | |
| 1743 | old_wid = -old_wid; | |
| 1744 | if (old_ht < 0) | |
| 1745 | old_ht = -old_ht; | |
| 1746 | desired_height = int(desired_width*(double(old_ht)/double(old_wid)) + .5); | |
| 1747 | } | |
| 1748 | if (env->vpos - desired_height < 0) | |
| 1749 | warning("top of imported graphic is above the top of the page"); | |
| 1750 | out.put_number(llx) | |
| 1751 | .put_number(lly) | |
| 1752 | .put_fix_number(desired_width) | |
| 1753 | .put_number(urx - llx) | |
| 1754 | .put_fix_number(-desired_height) | |
| 1755 | .put_number(ury - lly) | |
| 1756 | .put_fix_number(env->hpos) | |
| 1757 | .put_fix_number(env->vpos) | |
| 1758 | .put_symbol("PBEGIN"); | |
| 1759 | rm.import_file(arg, out); | |
| 1760 | // do this here just in case application defines PEND | |
| 1761 | out.put_symbol("end") | |
| 1762 | .put_symbol("PEND"); | |
| 1763 | } | |
| 1764 | ||
| 1765 | void ps_printer::do_invis(char *, const environment *) | |
| 1766 | { | |
| 1767 | invis_count++; | |
| 1768 | } | |
| 1769 | ||
| 1770 | void ps_printer::do_endinvis(char *, const environment *) | |
| 1771 | { | |
| 1772 | if (invis_count == 0) | |
| 1773 | error("unbalanced `endinvis' command"); | |
| 1774 | else | |
| 1775 | --invis_count; | |
| 1776 | } | |
| 1777 | ||
| 1778 | printer *make_printer() | |
| 1779 | { | |
| 1780 | return new ps_printer(user_paper_length); | |
| 1781 | } | |
| 1782 | ||
| 1783 | static void usage(FILE *stream); | |
| 1784 | ||
| 1785 | int main(int argc, char **argv) | |
| 1786 | { | |
| 1787 | setlocale(LC_NUMERIC, "C"); | |
| 1788 | program_name = argv[0]; | |
| 1789 | string env; | |
| 1790 | static char stderr_buf[BUFSIZ]; | |
| 1791 | setbuf(stderr, stderr_buf); | |
| 1792 | int c; | |
| 1793 | static const struct option long_options[] = { | |
| 1794 | { "help", no_argument, 0, CHAR_MAX + 1 }, | |
| 1795 | { "version", no_argument, 0, 'v' }, | |
| 1796 | { NULL, 0, 0, 0 } | |
| 1797 | }; | |
| 1798 | while ((c = getopt_long(argc, argv, "b:c:F:gI:lmp:P:vw:", long_options, NULL)) | |
| 1799 | != EOF) | |
| 1800 | switch(c) { | |
| 1801 | case 'b': | |
| 1802 | // XXX check this | |
| 1803 | broken_flags = atoi(optarg); | |
| 1804 | bflag = 1; | |
| 1805 | break; | |
| 1806 | case 'c': | |
| 1807 | if (sscanf(optarg, "%d", &ncopies) != 1 || ncopies <= 0) { | |
| 1808 | error("bad number of copies `%s'", optarg); | |
| 1809 | ncopies = 1; | |
| 1810 | } | |
| 1811 | break; | |
| 1812 | case 'F': | |
| 1813 | font::command_line_font_dir(optarg); | |
| 1814 | break; | |
| 1815 | case 'g': | |
| 1816 | guess_flag = 1; | |
| 1817 | break; | |
| 1818 | case 'I': | |
| 1819 | include_search_path.command_line_dir(optarg); | |
| 1820 | break; | |
| 1821 | case 'l': | |
| 1822 | landscape_flag = 1; | |
| 1823 | break; | |
| 1824 | case 'm': | |
| 1825 | manual_feed_flag = 1; | |
| 1826 | break; | |
| 1827 | case 'p': | |
| 1828 | if (!font::scan_papersize(optarg, 0, | |
| 1829 | &user_paper_length, &user_paper_width)) | |
| 1830 | error("invalid custom paper size `%1' ignored", optarg); | |
| 1831 | break; | |
| 1832 | case 'P': | |
| 1833 | env = "GROPS_PROLOGUE"; | |
| 1834 | env += '='; | |
| 1835 | env += optarg; | |
| 1836 | env += '\0'; | |
| 1837 | if (putenv(strsave(env.contents()))) | |
| 1838 | fatal("putenv failed"); | |
| 1839 | break; | |
| 1840 | case 'v': | |
| 1841 | printf("GNU grops (groff) version %s\n", Version_string); | |
| 1842 | exit(0); | |
| 1843 | break; | |
| 1844 | case 'w': | |
| 1845 | if (sscanf(optarg, "%d", &linewidth) != 1 || linewidth < 0) { | |
| 1846 | error("bad linewidth `%1'", optarg); | |
| 1847 | linewidth = -1; | |
| 1848 | } | |
| 1849 | break; | |
| 1850 | case CHAR_MAX + 1: // --help | |
| 1851 | usage(stdout); | |
| 1852 | exit(0); | |
| 1853 | break; | |
| 1854 | case '?': | |
| 1855 | usage(stderr); | |
| 1856 | exit(1); | |
| 1857 | break; | |
| 1858 | default: | |
| 1859 | assert(0); | |
| 1860 | } | |
| 1861 | font::set_unknown_desc_command_handler(handle_unknown_desc_command); | |
| 1862 | SET_BINARY(fileno(stdout)); | |
| 1863 | if (optind >= argc) | |
| 1864 | do_file("-"); | |
| 1865 | else { | |
| 1866 | for (int i = optind; i < argc; i++) | |
| 1867 | do_file(argv[i]); | |
| 1868 | } | |
| 1869 | return 0; | |
| 1870 | } | |
| 1871 | ||
| 1872 | static void usage(FILE *stream) | |
| 1873 | { | |
| 1874 | fprintf(stream, | |
| 1875 | "usage: %s [-glmv] [-b n] [-c n] [-w n] [-I dir] [-P prologue]\n" | |
| 1876 | " [-F dir] [files ...]\n", | |
| 1877 | program_name); | |
| 1878 | } |