| Commit | Line | Data |
|---|---|---|
| 92d0a6a6 | 1 | // -*- C++ -*- |
| 4d3e9548 | 2 | /* Copyright (C) 1994, 2000, 2001, 2002, 2003, 2004, 2006, 2009 |
| 92d0a6a6 JR |
3 | Free Software Foundation, Inc. |
| 4 | Written by James Clark (jjc@jclark.com) | |
| 5 | ||
| 6 | This file is part of groff. | |
| 7 | ||
| 8 | groff is free software; you can redistribute it and/or modify it under | |
| 9 | the terms of the GNU General Public License as published by the Free | |
| 4d3e9548 JL |
10 | Software Foundation, either version 3 of the License, or |
| 11 | (at your option) any later version. | |
| 92d0a6a6 JR |
12 | |
| 13 | groff is distributed in the hope that it will be useful, but WITHOUT ANY | |
| 14 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 15 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 16 | for more details. | |
| 17 | ||
| 4d3e9548 JL |
18 | You should have received a copy of the GNU General Public License |
| 19 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
| 92d0a6a6 JR |
20 | |
| 21 | /* | |
| 22 | TODO | |
| 23 | ||
| 24 | option to use beziers for circle/ellipse/arc | |
| 25 | option to use lines for spline (for LJ3) | |
| 26 | left/top offset registration | |
| 27 | output bin selection option | |
| 28 | paper source option | |
| 29 | output non-integer parameters using fixed point numbers | |
| 30 | X command to insert contents of file | |
| 31 | X command to specify inline escape sequence (how to specify unprintable chars?) | |
| 32 | X command to include bitmap graphics | |
| 33 | */ | |
| 34 | ||
| 35 | #include "driver.h" | |
| 36 | #include "nonposix.h" | |
| 37 | ||
| 38 | extern "C" const char *Version_string; | |
| 39 | ||
| 40 | static struct { | |
| 41 | const char *name; | |
| 42 | int code; | |
| 43 | // at 300dpi | |
| 44 | int x_offset_portrait; | |
| 45 | int x_offset_landscape; | |
| 46 | } paper_table[] = { | |
| 47 | { "letter", 2, 75, 60 }, | |
| 48 | { "legal", 3, 75, 60 }, | |
| 49 | { "executive", 1, 75, 60 }, | |
| 50 | { "a4", 26, 71, 59 }, | |
| 51 | { "com10", 81, 75, 60 }, | |
| 52 | { "monarch", 80, 75, 60 }, | |
| 53 | { "c5", 91, 71, 59 }, | |
| 54 | { "b5", 100, 71, 59 }, | |
| 55 | { "dl", 90, 71, 59 }, | |
| 56 | }; | |
| 57 | ||
| 58 | static int user_paper_size = -1; | |
| 59 | static int landscape_flag = 0; | |
| 60 | static int duplex_flag = 0; | |
| 61 | ||
| 62 | // An upper limit on the paper size in centipoints, | |
| 63 | // used for setting HPGL picture frame. | |
| 64 | #define MAX_PAPER_WIDTH (12*720) | |
| 65 | #define MAX_PAPER_HEIGHT (17*720) | |
| 66 | ||
| 67 | // Dotted lines that are thinner than this don't work right. | |
| 68 | #define MIN_DOT_PEN_WIDTH .351 | |
| 69 | ||
| 70 | #ifndef DEFAULT_LINE_WIDTH_FACTOR | |
| 71 | // in ems/1000 | |
| 72 | #define DEFAULT_LINE_WIDTH_FACTOR 40 | |
| 73 | #endif | |
| 74 | ||
| 75 | const int DEFAULT_HPGL_UNITS = 1016; | |
| 76 | int line_width_factor = DEFAULT_LINE_WIDTH_FACTOR; | |
| 77 | unsigned ncopies = 0; // 0 means don't send ncopies command | |
| 78 | ||
| 79 | static int lookup_paper_size(const char *); | |
| 80 | ||
| 81 | class lj4_font : public font { | |
| 82 | public: | |
| 83 | ~lj4_font(); | |
| 84 | void handle_unknown_font_command(const char *command, const char *arg, | |
| 85 | const char *filename, int lineno); | |
| 86 | static lj4_font *load_lj4_font(const char *); | |
| 87 | int weight; | |
| 88 | int style; | |
| 89 | int proportional; | |
| 90 | int typeface; | |
| 91 | private: | |
| 92 | lj4_font(const char *); | |
| 93 | }; | |
| 94 | ||
| 95 | lj4_font::lj4_font(const char *nm) | |
| 96 | : font(nm), weight(0), style(0), proportional(0), typeface(0) | |
| 97 | { | |
| 98 | } | |
| 99 | ||
| 100 | lj4_font::~lj4_font() | |
| 101 | { | |
| 102 | } | |
| 103 | ||
| 104 | lj4_font *lj4_font::load_lj4_font(const char *s) | |
| 105 | { | |
| 106 | lj4_font *f = new lj4_font(s); | |
| 107 | if (!f->load()) { | |
| 108 | delete f; | |
| 109 | return 0; | |
| 110 | } | |
| 111 | return f; | |
| 112 | } | |
| 113 | ||
| 114 | static struct { | |
| 115 | const char *s; | |
| 116 | int lj4_font::*ptr; | |
| 117 | int min; | |
| 118 | int max; | |
| 119 | } command_table[] = { | |
| 120 | { "pclweight", &lj4_font::weight, -7, 7 }, | |
| 121 | { "pclstyle", &lj4_font::style, 0, 32767 }, | |
| 122 | { "pclproportional", &lj4_font::proportional, 0, 1 }, | |
| 123 | { "pcltypeface", &lj4_font::typeface, 0, 65535 }, | |
| 124 | }; | |
| 125 | ||
| 126 | void lj4_font::handle_unknown_font_command(const char *command, | |
| 127 | const char *arg, | |
| 128 | const char *filename, int lineno) | |
| 129 | { | |
| 130 | for (unsigned int i = 0; | |
| 131 | i < sizeof(command_table)/sizeof(command_table[0]); i++) { | |
| 132 | if (strcmp(command, command_table[i].s) == 0) { | |
| 133 | if (arg == 0) | |
| 134 | fatal_with_file_and_line(filename, lineno, | |
| 135 | "`%1' command requires an argument", | |
| 136 | command); | |
| 137 | char *ptr; | |
| 138 | long n = strtol(arg, &ptr, 10); | |
| 139 | if (n == 0 && ptr == arg) | |
| 140 | fatal_with_file_and_line(filename, lineno, | |
| 141 | "`%1' command requires numeric argument", | |
| 142 | command); | |
| 143 | if (n < command_table[i].min) { | |
| 144 | error_with_file_and_line(filename, lineno, | |
| 145 | "argument for `%1' command must not be less than %2", | |
| 146 | command, command_table[i].min); | |
| 147 | n = command_table[i].min; | |
| 148 | } | |
| 149 | else if (n > command_table[i].max) { | |
| 150 | error_with_file_and_line(filename, lineno, | |
| 151 | "argument for `%1' command must not be greater than %2", | |
| 152 | command, command_table[i].max); | |
| 153 | n = command_table[i].max; | |
| 154 | } | |
| 155 | this->*command_table[i].ptr = int(n); | |
| 156 | break; | |
| 157 | } | |
| 158 | } | |
| 159 | } | |
| 160 | ||
| 161 | class lj4_printer : public printer { | |
| 162 | public: | |
| 163 | lj4_printer(int); | |
| 164 | ~lj4_printer(); | |
| 4d3e9548 | 165 | void set_char(glyph *, font *, const environment *, int, const char *name); |
| 92d0a6a6 JR |
166 | void draw(int code, int *p, int np, const environment *env); |
| 167 | void begin_page(int); | |
| 168 | void end_page(int page_length); | |
| 169 | font *make_font(const char *); | |
| 170 | void end_of_line(); | |
| 171 | private: | |
| 172 | void set_line_thickness(int size, int dot = 0); | |
| 173 | void hpgl_init(); | |
| 174 | void hpgl_start(); | |
| 175 | void hpgl_end(); | |
| 176 | int moveto(int hpos, int vpos); | |
| 177 | int moveto1(int hpos, int vpos); | |
| 178 | ||
| 179 | int cur_hpos; | |
| 180 | int cur_vpos; | |
| 181 | lj4_font *cur_font; | |
| 182 | int cur_size; | |
| 183 | unsigned short cur_symbol_set; | |
| 184 | int x_offset; | |
| 185 | int line_thickness; | |
| 186 | double pen_width; | |
| 187 | double hpgl_scale; | |
| 188 | int hpgl_inited; | |
| 189 | int paper_size; | |
| 190 | }; | |
| 191 | ||
| 192 | inline | |
| 193 | int lj4_printer::moveto(int hpos, int vpos) | |
| 194 | { | |
| 195 | if (cur_hpos != hpos || cur_vpos != vpos || cur_hpos < 0) | |
| 196 | return moveto1(hpos, vpos); | |
| 197 | else | |
| 198 | return 1; | |
| 199 | } | |
| 200 | ||
| 201 | inline | |
| 202 | void lj4_printer::hpgl_start() | |
| 203 | { | |
| 204 | fputs("\033%1B", stdout); | |
| 205 | } | |
| 206 | ||
| 207 | inline | |
| 208 | void lj4_printer::hpgl_end() | |
| 209 | { | |
| 210 | fputs(";\033%0A", stdout); | |
| 211 | } | |
| 212 | ||
| 213 | lj4_printer::lj4_printer(int ps) | |
| 214 | : cur_hpos(-1), | |
| 215 | cur_font(0), | |
| 216 | cur_size(0), | |
| 217 | cur_symbol_set(0), | |
| 218 | line_thickness(-1), | |
| 219 | pen_width(-1.0), | |
| 220 | hpgl_inited(0) | |
| 221 | { | |
| 222 | if (7200 % font::res != 0) | |
| 223 | fatal("invalid resolution %1: resolution must be a factor of 7200", | |
| 224 | font::res); | |
| 225 | fputs("\033E", stdout); // reset | |
| 226 | if (font::res != 300) | |
| 227 | printf("\033&u%dD", font::res); // unit of measure | |
| 228 | if (ncopies > 0) | |
| 229 | printf("\033&l%uX", ncopies); | |
| 230 | paper_size = 0; // default to letter | |
| 231 | if (font::papersize) { | |
| 232 | int n = lookup_paper_size(font::papersize); | |
| 233 | if (n < 0) | |
| 234 | error("unknown paper size `%1'", font::papersize); | |
| 235 | else | |
| 236 | paper_size = n; | |
| 237 | } | |
| 238 | if (ps >= 0) | |
| 239 | paper_size = ps; | |
| 240 | printf("\033&l%dA" // paper size | |
| 241 | "\033&l%dO" // orientation | |
| 242 | "\033&l0E", // no top margin | |
| 243 | paper_table[paper_size].code, | |
| 244 | landscape_flag != 0); | |
| 245 | if (landscape_flag) | |
| 246 | x_offset = paper_table[paper_size].x_offset_landscape; | |
| 247 | else | |
| 248 | x_offset = paper_table[paper_size].x_offset_portrait; | |
| 249 | x_offset = (x_offset * font::res) / 300; | |
| 250 | if (duplex_flag) | |
| 251 | printf("\033&l%dS", duplex_flag); | |
| 252 | } | |
| 253 | ||
| 254 | lj4_printer::~lj4_printer() | |
| 255 | { | |
| 256 | fputs("\033E", stdout); | |
| 257 | } | |
| 258 | ||
| 259 | void lj4_printer::begin_page(int) | |
| 260 | { | |
| 261 | } | |
| 262 | ||
| 263 | void lj4_printer::end_page(int) | |
| 264 | { | |
| 265 | putchar('\f'); | |
| 266 | cur_hpos = -1; | |
| 267 | } | |
| 268 | ||
| 269 | void lj4_printer::end_of_line() | |
| 270 | { | |
| 271 | cur_hpos = -1; // force absolute motion | |
| 272 | } | |
| 273 | ||
| 274 | inline | |
| 275 | int is_unprintable(unsigned char c) | |
| 276 | { | |
| 277 | return c < 32 && (c == 0 || (7 <= c && c <= 15) || c == 27); | |
| 278 | } | |
| 279 | ||
| 4d3e9548 | 280 | void lj4_printer::set_char(glyph *g, font *f, const environment *env, |
| 92d0a6a6 JR |
281 | int w, const char *) |
| 282 | { | |
| 4d3e9548 | 283 | int code = f->get_code(g); |
| 92d0a6a6 JR |
284 | |
| 285 | unsigned char ch = code & 0xff; | |
| 286 | unsigned short symbol_set = code >> 8; | |
| 287 | if (symbol_set != cur_symbol_set) { | |
| 288 | printf("\033(%d%c", symbol_set/32, (symbol_set & 31) + 64); | |
| 289 | cur_symbol_set = symbol_set; | |
| 290 | } | |
| 291 | if (f != cur_font) { | |
| 292 | lj4_font *psf = (lj4_font *)f; | |
| 293 | // FIXME only output those that are needed | |
| 294 | printf("\033(s%dp%ds%db%dT", | |
| 295 | psf->proportional, | |
| 296 | psf->style, | |
| 297 | psf->weight, | |
| 298 | psf->typeface); | |
| 299 | if (!psf->proportional || !cur_font || !cur_font->proportional) | |
| 300 | cur_size = 0; | |
| 301 | cur_font = psf; | |
| 302 | } | |
| 303 | if (env->size != cur_size) { | |
| 304 | if (cur_font->proportional) { | |
| 305 | static const char *quarters[] = { "", ".25", ".5", ".75" }; | |
| 306 | printf("\033(s%d%sV", env->size/4, quarters[env->size & 3]); | |
| 307 | } | |
| 308 | else { | |
| 309 | double pitch = double(font::res)/w; | |
| 310 | // PCL uses the next largest pitch, so round it down. | |
| 311 | pitch = floor(pitch*100.0)/100.0; | |
| 312 | printf("\033(s%.2fH", pitch); | |
| 313 | } | |
| 314 | cur_size = env->size; | |
| 315 | } | |
| 316 | if (!moveto(env->hpos, env->vpos)) | |
| 317 | return; | |
| 318 | if (is_unprintable(ch)) | |
| 319 | fputs("\033&p1X", stdout); | |
| 320 | putchar(ch); | |
| 321 | cur_hpos += w; | |
| 322 | } | |
| 323 | ||
| 324 | int lj4_printer::moveto1(int hpos, int vpos) | |
| 325 | { | |
| 326 | if (hpos < x_offset || vpos < 0) | |
| 327 | return 0; | |
| 328 | fputs("\033*p", stdout); | |
| 329 | if (cur_hpos < 0) | |
| 330 | printf("%dx%dY", hpos - x_offset, vpos); | |
| 331 | else { | |
| 332 | if (cur_hpos != hpos) | |
| 333 | printf("%s%d%c", hpos > cur_hpos ? "+" : "", | |
| 334 | hpos - cur_hpos, vpos == cur_vpos ? 'X' : 'x'); | |
| 335 | if (cur_vpos != vpos) | |
| 336 | printf("%s%dY", vpos > cur_vpos ? "+" : "", vpos - cur_vpos); | |
| 337 | } | |
| 338 | cur_hpos = hpos; | |
| 339 | cur_vpos = vpos; | |
| 340 | return 1; | |
| 341 | } | |
| 342 | ||
| 343 | void lj4_printer::draw(int code, int *p, int np, const environment *env) | |
| 344 | { | |
| 345 | switch (code) { | |
| 346 | case 'R': | |
| 347 | { | |
| 348 | if (np != 2) { | |
| 349 | error("2 arguments required for rule"); | |
| 350 | break; | |
| 351 | } | |
| 352 | int hpos = env->hpos; | |
| 353 | int vpos = env->vpos; | |
| 354 | int hsize = p[0]; | |
| 355 | int vsize = p[1]; | |
| 356 | if (hsize < 0) { | |
| 357 | hpos += hsize; | |
| 358 | hsize = -hsize; | |
| 359 | } | |
| 360 | if (vsize < 0) { | |
| 361 | vpos += vsize; | |
| 362 | vsize = -vsize; | |
| 363 | } | |
| 364 | if (!moveto(hpos, vpos)) | |
| 365 | return; | |
| 366 | printf("\033*c%da%db0P", hsize, vsize); | |
| 367 | break; | |
| 368 | } | |
| 369 | case 'l': | |
| 370 | if (np != 2) { | |
| 371 | error("2 arguments required for line"); | |
| 372 | break; | |
| 373 | } | |
| 374 | hpgl_init(); | |
| 375 | if (!moveto(env->hpos, env->vpos)) | |
| 376 | return; | |
| 377 | hpgl_start(); | |
| 378 | set_line_thickness(env->size, p[0] == 0 && p[1] == 0); | |
| 379 | printf("PD%d,%d", p[0], p[1]); | |
| 380 | hpgl_end(); | |
| 381 | break; | |
| 382 | case 'p': | |
| 383 | case 'P': | |
| 384 | { | |
| 385 | if (np & 1) { | |
| 386 | error("even number of arguments required for polygon"); | |
| 387 | break; | |
| 388 | } | |
| 389 | if (np == 0) { | |
| 390 | error("no arguments for polygon"); | |
| 391 | break; | |
| 392 | } | |
| 393 | hpgl_init(); | |
| 394 | if (!moveto(env->hpos, env->vpos)) | |
| 395 | return; | |
| 396 | hpgl_start(); | |
| 397 | if (code == 'p') | |
| 398 | set_line_thickness(env->size); | |
| 399 | printf("PMPD%d", p[0]); | |
| 400 | for (int i = 1; i < np; i++) | |
| 401 | printf(",%d", p[i]); | |
| 402 | printf("PM2%cP", code == 'p' ? 'E' : 'F'); | |
| 403 | hpgl_end(); | |
| 404 | break; | |
| 405 | } | |
| 406 | case '~': | |
| 407 | { | |
| 408 | if (np & 1) { | |
| 409 | error("even number of arguments required for spline"); | |
| 410 | break; | |
| 411 | } | |
| 412 | if (np == 0) { | |
| 413 | error("no arguments for spline"); | |
| 414 | break; | |
| 415 | } | |
| 416 | hpgl_init(); | |
| 417 | if (!moveto(env->hpos, env->vpos)) | |
| 418 | return; | |
| 419 | hpgl_start(); | |
| 420 | set_line_thickness(env->size); | |
| 421 | printf("PD%d,%d", p[0]/2, p[1]/2); | |
| 422 | const int tnum = 2; | |
| 423 | const int tden = 3; | |
| 424 | if (np > 2) { | |
| 425 | fputs("BR", stdout); | |
| 426 | for (int i = 0; i < np - 2; i += 2) { | |
| 427 | if (i != 0) | |
| 428 | putchar(','); | |
| 429 | printf("%d,%d,%d,%d,%d,%d", | |
| 430 | (p[i]*tnum)/(2*tden), | |
| 431 | (p[i + 1]*tnum)/(2*tden), | |
| 432 | p[i]/2 + (p[i + 2]*(tden - tnum))/(2*tden), | |
| 433 | p[i + 1]/2 + (p[i + 3]*(tden - tnum))/(2*tden), | |
| 434 | (p[i] - p[i]/2) + p[i + 2]/2, | |
| 435 | (p[i + 1] - p[i + 1]/2) + p[i + 3]/2); | |
| 436 | } | |
| 437 | } | |
| 438 | printf("PR%d,%d", p[np - 2] - p[np - 2]/2, p[np - 1] - p[np - 1]/2); | |
| 439 | hpgl_end(); | |
| 440 | break; | |
| 441 | } | |
| 442 | case 'c': | |
| 443 | case 'C': | |
| 444 | // troff adds an extra argument to C | |
| 445 | if (np != 1 && !(code == 'C' && np == 2)) { | |
| 446 | error("1 argument required for circle"); | |
| 447 | break; | |
| 448 | } | |
| 449 | hpgl_init(); | |
| 450 | if (!moveto(env->hpos + p[0]/2, env->vpos)) | |
| 451 | return; | |
| 452 | hpgl_start(); | |
| 453 | if (code == 'c') { | |
| 454 | set_line_thickness(env->size); | |
| 455 | printf("CI%d", p[0]/2); | |
| 456 | } | |
| 457 | else | |
| 458 | printf("WG%d,0,360", p[0]/2); | |
| 459 | hpgl_end(); | |
| 460 | break; | |
| 461 | case 'e': | |
| 462 | case 'E': | |
| 463 | if (np != 2) { | |
| 464 | error("2 arguments required for ellipse"); | |
| 465 | break; | |
| 466 | } | |
| 467 | hpgl_init(); | |
| 468 | if (!moveto(env->hpos + p[0]/2, env->vpos)) | |
| 469 | return; | |
| 470 | hpgl_start(); | |
| 471 | printf("SC0,%.4f,0,-%.4f,2", hpgl_scale * double(p[0])/p[1], hpgl_scale); | |
| 472 | if (code == 'e') { | |
| 473 | set_line_thickness(env->size); | |
| 474 | printf("CI%d", p[1]/2); | |
| 475 | } | |
| 476 | else | |
| 477 | printf("WG%d,0,360", p[1]/2); | |
| 478 | printf("SC0,%.4f,0,-%.4f,2", hpgl_scale, hpgl_scale); | |
| 479 | hpgl_end(); | |
| 480 | break; | |
| 481 | case 'a': | |
| 482 | { | |
| 483 | if (np != 4) { | |
| 484 | error("4 arguments required for arc"); | |
| 485 | break; | |
| 486 | } | |
| 487 | hpgl_init(); | |
| 488 | if (!moveto(env->hpos, env->vpos)) | |
| 489 | return; | |
| 490 | hpgl_start(); | |
| 491 | set_line_thickness(env->size); | |
| 492 | double c[2]; | |
| 493 | if (adjust_arc_center(p, c)) { | |
| 494 | double sweep = ((atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0]) | |
| 495 | - atan2(-c[1], -c[0])) | |
| 496 | * 180.0/PI); | |
| 497 | if (sweep > 0.0) | |
| 498 | sweep -= 360.0; | |
| 499 | printf("PDAR%d,%d,%f", int(c[0]), int(c[1]), sweep); | |
| 500 | } | |
| 501 | else | |
| 502 | printf("PD%d,%d", p[0] + p[2], p[1] + p[3]); | |
| 503 | hpgl_end(); | |
| 504 | } | |
| 505 | break; | |
| 506 | case 'f': | |
| 507 | if (np != 1 && np != 2) { | |
| 508 | error("1 argument required for fill"); | |
| 509 | break; | |
| 510 | } | |
| 511 | hpgl_init(); | |
| 512 | hpgl_start(); | |
| 513 | if (p[0] >= 0 && p[0] <= 1000) | |
| 514 | printf("FT10,%d", p[0]/10); | |
| 515 | hpgl_end(); | |
| 516 | break; | |
| 517 | case 'F': | |
| 518 | // not implemented yet | |
| 519 | break; | |
| 520 | case 't': | |
| 521 | { | |
| 522 | if (np == 0) { | |
| 523 | line_thickness = -1; | |
| 524 | } | |
| 525 | else { | |
| 526 | // troff gratuitously adds an extra 0 | |
| 527 | if (np != 1 && np != 2) { | |
| 528 | error("0 or 1 argument required for thickness"); | |
| 529 | break; | |
| 530 | } | |
| 531 | line_thickness = p[0]; | |
| 532 | } | |
| 533 | break; | |
| 534 | } | |
| 535 | default: | |
| 536 | error("unrecognised drawing command `%1'", char(code)); | |
| 537 | break; | |
| 538 | } | |
| 539 | } | |
| 540 | ||
| 541 | void lj4_printer::hpgl_init() | |
| 542 | { | |
| 543 | if (hpgl_inited) | |
| 544 | return; | |
| 545 | hpgl_inited = 1; | |
| 546 | hpgl_scale = double(DEFAULT_HPGL_UNITS)/font::res; | |
| 547 | printf("\033&f0S" // push position | |
| 548 | "\033*p0x0Y" // move to 0,0 | |
| 549 | "\033*c%dx%dy0T" // establish picture frame | |
| 550 | "\033%%1B" // switch to HPGL | |
| 551 | "SP1SC0,%.4f,0,-%.4f,2IR0,100,0,100" // set up scaling | |
| 552 | "LA1,4,2,4" // round line ends and joins | |
| 553 | "PR" // relative plotting | |
| 554 | "TR0" // opaque | |
| 555 | ";\033%%1A" // back to PCL | |
| 556 | "\033&f1S", // pop position | |
| 557 | MAX_PAPER_WIDTH, MAX_PAPER_HEIGHT, | |
| 558 | hpgl_scale, hpgl_scale); | |
| 559 | } | |
| 560 | ||
| 561 | void lj4_printer::set_line_thickness(int size, int dot) | |
| 562 | { | |
| 563 | double pw; | |
| 564 | if (line_thickness < 0) | |
| 565 | pw = (size * (line_width_factor * 25.4))/(font::sizescale * 72000.0); | |
| 566 | else | |
| 567 | pw = line_thickness*25.4/font::res; | |
| 568 | if (dot && pw < MIN_DOT_PEN_WIDTH) | |
| 569 | pw = MIN_DOT_PEN_WIDTH; | |
| 570 | if (pw != pen_width) { | |
| 571 | printf("PW%f", pw); | |
| 572 | pen_width = pw; | |
| 573 | } | |
| 574 | } | |
| 575 | ||
| 576 | font *lj4_printer::make_font(const char *nm) | |
| 577 | { | |
| 578 | return lj4_font::load_lj4_font(nm); | |
| 579 | } | |
| 580 | ||
| 581 | printer *make_printer() | |
| 582 | { | |
| 583 | return new lj4_printer(user_paper_size); | |
| 584 | } | |
| 585 | ||
| 586 | static | |
| 587 | int lookup_paper_size(const char *s) | |
| 588 | { | |
| 589 | for (unsigned int i = 0; | |
| 590 | i < sizeof(paper_table)/sizeof(paper_table[0]); i++) { | |
| 591 | // FIXME Perhaps allow unique prefix. | |
| 592 | if (strcasecmp(s, paper_table[i].name) == 0) | |
| 593 | return i; | |
| 594 | } | |
| 595 | return -1; | |
| 596 | } | |
| 597 | ||
| 598 | static void usage(FILE *stream); | |
| 599 | ||
| 600 | extern "C" int optopt, optind; | |
| 601 | ||
| 602 | int main(int argc, char **argv) | |
| 603 | { | |
| 604 | setlocale(LC_NUMERIC, "C"); | |
| 605 | program_name = argv[0]; | |
| 606 | static char stderr_buf[BUFSIZ]; | |
| 607 | setbuf(stderr, stderr_buf); | |
| 608 | int c; | |
| 609 | static const struct option long_options[] = { | |
| 610 | { "help", no_argument, 0, CHAR_MAX + 1 }, | |
| 611 | { "version", no_argument, 0, 'v' }, | |
| 612 | { NULL, 0, 0, 0 } | |
| 613 | }; | |
| 614 | while ((c = getopt_long(argc, argv, "c:d:F:I:lp:vw:", long_options, NULL)) | |
| 615 | != EOF) | |
| 616 | switch(c) { | |
| 617 | case 'l': | |
| 618 | landscape_flag = 1; | |
| 619 | break; | |
| 620 | case 'I': | |
| 621 | // ignore include search path | |
| 622 | break; | |
| 623 | case ':': | |
| 624 | if (optopt == 'd') { | |
| 625 | fprintf(stderr, "duplex assumed to be long-side\n"); | |
| 626 | duplex_flag = 1; | |
| 627 | } else | |
| 628 | fprintf(stderr, "option -%c requires an argument\n", optopt); | |
| 629 | fflush(stderr); | |
| 630 | break; | |
| 631 | case 'd': | |
| 632 | if (!isdigit(*optarg)) // this ugly hack prevents -d without | |
| 633 | optind--; // args from messing up the arg list | |
| 634 | duplex_flag = atoi(optarg); | |
| 635 | if (duplex_flag != 1 && duplex_flag != 2) { | |
| 636 | fprintf(stderr, "odd value for duplex; assumed to be long-side\n"); | |
| 637 | duplex_flag = 1; | |
| 638 | } | |
| 639 | break; | |
| 640 | case 'p': | |
| 641 | { | |
| 642 | int n = lookup_paper_size(optarg); | |
| 643 | if (n < 0) | |
| 644 | error("unknown paper size `%1'", optarg); | |
| 645 | else | |
| 646 | user_paper_size = n; | |
| 647 | break; | |
| 648 | } | |
| 649 | case 'v': | |
| 650 | printf("GNU grolj4 (groff) version %s\n", Version_string); | |
| 651 | exit(0); | |
| 652 | break; | |
| 653 | case 'F': | |
| 654 | font::command_line_font_dir(optarg); | |
| 655 | break; | |
| 656 | case 'c': | |
| 657 | { | |
| 658 | char *ptr; | |
| 659 | long n = strtol(optarg, &ptr, 10); | |
| 660 | if (n == 0 && ptr == optarg) | |
| 661 | error("argument for -c must be a positive integer"); | |
| 662 | else if (n <= 0 || n > 32767) | |
| 663 | error("out of range argument for -c"); | |
| 664 | else | |
| 665 | ncopies = unsigned(n); | |
| 666 | break; | |
| 667 | } | |
| 668 | case 'w': | |
| 669 | { | |
| 670 | char *ptr; | |
| 671 | long n = strtol(optarg, &ptr, 10); | |
| 672 | if (n == 0 && ptr == optarg) | |
| 673 | error("argument for -w must be a non-negative integer"); | |
| 674 | else if (n < 0 || n > INT_MAX) | |
| 675 | error("out of range argument for -w"); | |
| 676 | else | |
| 677 | line_width_factor = int(n); | |
| 678 | break; | |
| 679 | } | |
| 680 | case CHAR_MAX + 1: // --help | |
| 681 | usage(stdout); | |
| 682 | exit(0); | |
| 683 | break; | |
| 684 | case '?': | |
| 685 | usage(stderr); | |
| 686 | exit(1); | |
| 687 | break; | |
| 688 | default: | |
| 689 | assert(0); | |
| 690 | } | |
| 691 | SET_BINARY(fileno(stdout)); | |
| 692 | if (optind >= argc) | |
| 693 | do_file("-"); | |
| 694 | else { | |
| 695 | for (int i = optind; i < argc; i++) | |
| 696 | do_file(argv[i]); | |
| 697 | } | |
| 698 | return 0; | |
| 699 | } | |
| 700 | ||
| 701 | static void usage(FILE *stream) | |
| 702 | { | |
| 703 | fprintf(stream, | |
| 704 | "usage: %s [-lv] [-d [n]] [-c n] [-p paper_size]\n" | |
| 705 | " [-w n] [-F dir] [files ...]\n", | |
| 706 | program_name); | |
| 707 | } |