| Commit | Line | Data |
|---|---|---|
| 92d0a6a6 | 1 | // -*- C++ -*- |
| 4d3e9548 | 2 | /* Copyright (C) 1994, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009 |
| 92d0a6a6 JR |
3 | Free Software Foundation, Inc. |
| 4 | Written by Francisco Andrés Verdú <pandres@dragonet.es> with many ideas | |
| 5 | taken from the other groff drivers. | |
| 6 | ||
| 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 | |
| 4d3e9548 JL |
12 | Software Foundation, either version 3 of the License, or |
| 13 | (at your option) any later version. | |
| 92d0a6a6 JR |
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 | ||
| 4d3e9548 JL |
20 | You should have received a copy of the GNU General Public License |
| 21 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
| 92d0a6a6 JR |
22 | |
| 23 | /* | |
| 24 | TODO | |
| 25 | ||
| 26 | - Add X command to include bitmaps | |
| 27 | */ | |
| 28 | ||
| 29 | #include "driver.h" | |
| 30 | #include "lbp.h" | |
| 31 | #include "charset.h" | |
| 32 | #include "paper.h" | |
| 33 | ||
| 34 | #include "nonposix.h" | |
| 35 | ||
| 36 | extern "C" const char *Version_string; | |
| 37 | ||
| 38 | static int user_papersize = -1; // papersize | |
| 39 | static int orientation = -1; // orientation | |
| 40 | static double user_paperlength = 0; // Custom Paper size | |
| 41 | static double user_paperwidth = 0; | |
| 42 | static int ncopies = 1; // Number of copies | |
| 43 | ||
| 44 | #define DEFAULT_LINEWIDTH_FACTOR 40 // 0.04em | |
| 45 | static int linewidth_factor = DEFAULT_LINEWIDTH_FACTOR; | |
| 46 | ||
| 47 | static int set_papersize(const char *paperformat); | |
| 48 | ||
| 49 | class lbp_font : public font { | |
| 50 | public: | |
| 51 | ~lbp_font(); | |
| 52 | void handle_unknown_font_command(const char *command, const char *arg, | |
| 53 | const char *filename, int lineno); | |
| 54 | static lbp_font *load_lbp_font(const char *); | |
| 55 | char *lbpname; | |
| 56 | char is_scalable; | |
| 57 | private: | |
| 58 | lbp_font(const char *); | |
| 59 | }; | |
| 60 | ||
| 61 | class lbp_printer : public printer { | |
| 62 | public: | |
| 63 | lbp_printer(int, double, double); | |
| 64 | ~lbp_printer(); | |
| 4d3e9548 | 65 | void set_char(glyph *, font *, const environment *, int, const char *name); |
| 92d0a6a6 JR |
66 | void draw(int code, int *p, int np, const environment *env); |
| 67 | void begin_page(int); | |
| 68 | void end_page(int page_length); | |
| 69 | font *make_font(const char *); | |
| 70 | void end_of_line(); | |
| 71 | private: | |
| 72 | void set_line_thickness(int size,const environment *env); | |
| 73 | void vdmstart(); | |
| 74 | void vdmflush(); // the name vdmend was already used in lbp.h | |
| 75 | void setfillmode(int mode); | |
| 76 | void polygon( int hpos,int vpos,int np,int *p); | |
| 77 | char *font_name(const lbp_font *f, const int siz); | |
| 78 | ||
| 79 | int fill_pattern; | |
| 80 | int fill_mode; | |
| 81 | int cur_hpos; | |
| 82 | int cur_vpos; | |
| 83 | lbp_font *cur_font; | |
| 84 | int cur_size; | |
| 85 | unsigned short cur_symbol_set; | |
| 86 | int line_thickness; | |
| 87 | int req_linethickness; // requested line thickness | |
| 88 | int papersize; | |
| 89 | int paperlength; // custom paper size | |
| 90 | int paperwidth; | |
| 91 | }; | |
| 92 | ||
| 93 | lbp_font::lbp_font(const char *nm) | |
| 94 | : font(nm) | |
| 95 | { | |
| 96 | } | |
| 97 | ||
| 98 | lbp_font::~lbp_font() | |
| 99 | { | |
| 100 | } | |
| 101 | ||
| 102 | lbp_font *lbp_font::load_lbp_font(const char *s) | |
| 103 | { | |
| 104 | lbp_font *f = new lbp_font(s); | |
| 105 | f->lbpname = NULL; | |
| 106 | f->is_scalable = 1; // Default is that fonts are scalable | |
| 107 | if (!f->load()) { | |
| 108 | delete f; | |
| 109 | return 0; | |
| 110 | } | |
| 111 | return f; | |
| 112 | } | |
| 113 | ||
| 114 | ||
| 115 | void lbp_font::handle_unknown_font_command(const char *command, | |
| 116 | const char *arg, | |
| 117 | const char *filename, int lineno) | |
| 118 | { | |
| 119 | if (strcmp(command, "lbpname") == 0) { | |
| 120 | if (arg == 0) | |
| 121 | fatal_with_file_and_line(filename, lineno, | |
| 122 | "`%1' command requires an argument", | |
| 123 | command); | |
| 124 | this->lbpname = new char[strlen(arg) + 1]; | |
| 125 | strcpy(this->lbpname, arg); | |
| 126 | // we recognize bitmapped fonts by the first character of its name | |
| 127 | if (arg[0] == 'N') | |
| 128 | this->is_scalable = 0; | |
| 129 | // fprintf(stderr, "Loading font \"%s\" \n", arg); | |
| 130 | } | |
| 131 | // fprintf(stderr, "Loading font %s \"%s\" in %s at %d\n", | |
| 132 | // command, arg, filename, lineno); | |
| 133 | } | |
| 134 | ||
| 135 | static void wp54charset() | |
| 136 | { | |
| 137 | unsigned int i; | |
| 138 | lbpputs("\033[714;100;29;0;32;120.}"); | |
| 139 | for (i = 0; i < sizeof(symset); i++) | |
| 140 | lbpputc(symset[i]); | |
| 141 | lbpputs("\033[100;0 D"); | |
| 142 | return; | |
| 143 | } | |
| 144 | ||
| 145 | lbp_printer::lbp_printer(int ps, double pw, double pl) | |
| 146 | : fill_pattern(1), | |
| 147 | fill_mode(0), | |
| 148 | cur_hpos(-1), | |
| 149 | cur_font(0), | |
| 150 | cur_size(0), | |
| 151 | cur_symbol_set(0), | |
| 152 | req_linethickness(-1) | |
| 153 | { | |
| 154 | SET_BINARY(fileno(stdout)); | |
| 155 | lbpinit(stdout); | |
| 156 | lbpputs("\033c\033;\033[2&z\033[7 I\033[?32h\033[?33h\033[11h"); | |
| 157 | wp54charset(); // Define the new symbol set | |
| 158 | lbpputs("\033[7 I\033[?32h\033[?33h\033[11h"); | |
| 159 | // Paper size handling | |
| 160 | if (orientation < 0) | |
| 161 | orientation = 0; // Default orientation is portrait | |
| 162 | papersize = 14; // Default paper size is A4 | |
| 163 | if (font::papersize) { | |
| 164 | papersize = set_papersize(font::papersize); | |
| 165 | paperlength = font::paperlength; | |
| 166 | paperwidth = font::paperwidth; | |
| 167 | } | |
| 168 | if (ps >= 0) { | |
| 169 | papersize = ps; | |
| 170 | paperlength = int(pl * font::res + 0.5); | |
| 171 | paperwidth = int(pw * font::res + 0.5); | |
| 172 | } | |
| 173 | if (papersize < 80) // standard paper | |
| 174 | lbpprintf("\033[%dp", (papersize | orientation)); | |
| 175 | else // Custom paper | |
| 176 | lbpprintf("\033[%d;%d;%dp", (papersize | orientation), | |
| 177 | paperlength, paperwidth); | |
| 178 | // Number of copies | |
| 179 | lbpprintf("\033[%dv\n", ncopies); | |
| 180 | lbpputs("\033[0u\033[1u\033P1y Grolbp\033\\"); | |
| 181 | lbpmoveabs(0, 0); | |
| 182 | lbpputs("\033[0t\033[2t"); | |
| 183 | lbpputs("\033('$2\033)' 1"); // Primary symbol set IBML | |
| 184 | // Secondary symbol set IBMR1 | |
| 185 | cur_symbol_set = 0; | |
| 186 | } | |
| 187 | ||
| 188 | lbp_printer::~lbp_printer() | |
| 189 | { | |
| 190 | lbpputs("\033P1y\033\\"); | |
| 191 | lbpputs("\033c\033<"); | |
| 192 | } | |
| 193 | ||
| 194 | inline void lbp_printer::set_line_thickness(int size,const environment *env) | |
| 195 | { | |
| 196 | if (size == 0) | |
| 197 | line_thickness = 1; | |
| 198 | else { | |
| 199 | if (size < 0) | |
| 200 | // line_thickness = | |
| 201 | // (env->size * (font::res/72)) * (linewidth_factor/1000) | |
| 202 | // we ought to check for overflow | |
| 203 | line_thickness = | |
| 204 | env->size * linewidth_factor * font::res / 72000; | |
| 205 | else // size > 0 | |
| 206 | line_thickness = size; | |
| 207 | } // else from if (size == 0) | |
| 208 | if (line_thickness < 1) | |
| 209 | line_thickness = 1; | |
| 210 | if (vdminited()) | |
| 211 | vdmlinewidth(line_thickness); | |
| 212 | req_linethickness = size; // an size requested | |
| 213 | /* fprintf(stderr, "thickness: %d == %d, size %d, %d \n", | |
| 214 | size, line_thickness, env->size,req_linethickness); */ | |
| 215 | return; | |
| 465b256c | 216 | } // lbp_printer::set_line_thickness |
| 92d0a6a6 JR |
217 | |
| 218 | void lbp_printer::begin_page(int) | |
| 219 | { | |
| 220 | } | |
| 221 | ||
| 222 | void lbp_printer::end_page(int) | |
| 223 | { | |
| 224 | if (vdminited()) | |
| 225 | vdmflush(); | |
| 226 | lbpputc('\f'); | |
| 227 | cur_hpos = -1; | |
| 228 | } | |
| 229 | ||
| 230 | void lbp_printer::end_of_line() | |
| 231 | { | |
| 232 | cur_hpos = -1; // force absolute motion | |
| 233 | } | |
| 234 | ||
| 235 | char *lbp_printer::font_name(const lbp_font *f, const int siz) | |
| 236 | { | |
| 237 | static char bfont_name[255]; // The resulting font name | |
| 238 | char type, // Italic, Roman, Bold | |
| 239 | ori, // Normal or Rotated | |
| 240 | *nam; // The font name without other data. | |
| 241 | int cpi; // The font size in characters per inch | |
| 242 | // (bitmapped fonts are monospaced). | |
| 243 | /* Bitmap font selection is ugly in this printer, so don't expect | |
| 244 | this function to be elegant. */ | |
| 245 | bfont_name[0] = 0x00; | |
| 246 | if (orientation) // Landscape | |
| 247 | ori = 'R'; | |
| 248 | else // Portrait | |
| 249 | ori = 'N'; | |
| 250 | type = f->lbpname[strlen(f->lbpname) - 1]; | |
| 251 | nam = new char[strlen(f->lbpname) - 2]; | |
| 252 | strncpy(nam, &(f->lbpname[1]), strlen(f->lbpname) - 2); | |
| 253 | nam[strlen(f->lbpname) - 2] = 0x00; | |
| 254 | // fprintf(stderr, "Bitmap font '%s' %d %c %c \n", nam, siz, type, ori); | |
| 255 | /* Since these fonts are available only at certain sizes, | |
| 256 | 10 and 17 cpi for courier, 12 and 17 cpi for elite, | |
| 257 | we adjust the resulting size. */ | |
| 258 | cpi = 17; | |
| 259 | // Fortunately there are only two bitmapped fonts shipped with the printer. | |
| 260 | if (!strcasecmp(nam, "courier")) { | |
| 261 | // Courier font | |
| 262 | if (siz >= 12) | |
| 263 | cpi = 10; | |
| 264 | else cpi = 17; | |
| 265 | } | |
| 266 | if (!strcasecmp(nam, "elite")) { | |
| 267 | if (siz >= 10) | |
| 268 | cpi = 12; | |
| 269 | else cpi = 17; | |
| 270 | } | |
| 271 | // Now that we have all the data, let's generate the font name. | |
| 272 | if ((type != 'B') && (type != 'I')) // Roman font | |
| 273 | sprintf(bfont_name, "%c%s%d", ori, nam, cpi); | |
| 274 | else | |
| 275 | sprintf(bfont_name, "%c%s%d%c", ori, nam, cpi, type); | |
| 276 | return bfont_name; | |
| 277 | } | |
| 278 | ||
| 4d3e9548 | 279 | void lbp_printer::set_char(glyph *g, font *f, const environment *env, |
| 92d0a6a6 JR |
280 | int w, const char *) |
| 281 | { | |
| 4d3e9548 | 282 | int code = f->get_code(g); |
| 92d0a6a6 JR |
283 | unsigned char ch = code & 0xff; |
| 284 | unsigned short symbol_set = code >> 8; | |
| 285 | if (f != cur_font) { | |
| 286 | lbp_font *psf = (lbp_font *)f; | |
| 287 | // fprintf(stderr, "Loading font %s \"%d\" \n", psf->lbpname, env->size); | |
| 288 | if (psf->is_scalable) { | |
| 289 | // Scalable font selection is different from bitmaped | |
| 290 | lbpprintf("\033Pz%s.IBML\033\\\033[%d C", psf->lbpname, | |
| 291 | (int)((env->size * font::res) / 72)); | |
| 292 | } | |
| 293 | else | |
| 294 | // bitmapped font | |
| 295 | lbpprintf("\033Pz%s.IBML\033\\\n", font_name(psf, env->size)); | |
| 296 | lbpputs("\033)' 1"); // Select IBML and IBMR1 symbol set | |
| 297 | cur_font = psf; | |
| 298 | cur_symbol_set = 0; | |
| 299 | // Update the line thickness if needed | |
| 300 | if ((req_linethickness < 0 ) && (env->size != cur_size)) | |
| 301 | set_line_thickness(req_linethickness,env); | |
| 302 | cur_size = env->size; | |
| 303 | } | |
| 304 | if (symbol_set != cur_symbol_set) { | |
| 305 | if (cur_symbol_set == 3) | |
| 306 | // if current symbol set is Symbol we must restore the font | |
| 307 | lbpprintf("\033Pz%s.IBML\033\\\033[%d C", cur_font->lbpname, | |
| 308 | (int)((env->size * font::res) / 72)); | |
| 309 | switch (symbol_set) { | |
| 310 | case 0: | |
| 311 | lbpputs("\033('$2\033)' 1"); // Select IBML and IBMR1 symbol sets | |
| 312 | break; | |
| 313 | case 1: | |
| 314 | lbpputs("\033(d\033)' 1"); // Select wp54 symbol set | |
| 315 | break; | |
| 316 | case 2: | |
| 317 | lbpputs("\033('$2\033)'!0"); // Select IBMP symbol set | |
| 318 | break; | |
| 319 | case 3: | |
| 320 | lbpprintf("\033PzSymbol.SYML\033\\\033[%d C", | |
| 321 | (int)((env->size * font::res) / 72)); | |
| 322 | lbpputs("\033(\"!!0\033)\"!!1"); // Select symbol font | |
| 323 | break; | |
| 324 | case 4: | |
| 325 | lbpputs("\033)\"! 1\033(\"!$2"); // Select PS symbol set | |
| 326 | break; | |
| 327 | } | |
| 328 | cur_symbol_set = symbol_set; | |
| 329 | } | |
| 330 | if (env->size != cur_size) { | |
| 331 | if (!cur_font->is_scalable) | |
| 332 | lbpprintf("\033Pz%s.IBML\033\\\n", font_name(cur_font, env->size)); | |
| 333 | else | |
| 334 | lbpprintf("\033[%d C", (int)((env->size * font::res) / 72)); | |
| 335 | cur_size = env->size; | |
| 336 | // Update the line thickness if needed | |
| 337 | if (req_linethickness < 0 ) | |
| 338 | set_line_thickness(req_linethickness,env); | |
| 339 | } | |
| 340 | if ((env->hpos != cur_hpos) || (env->vpos != cur_vpos)) { | |
| 341 | // lbpmoveabs(env->hpos - ((5 * 300) / 16), env->vpos); | |
| 342 | lbpmoveabs(env->hpos - 64, env->vpos - 64); | |
| 343 | cur_vpos = env->vpos; | |
| 344 | cur_hpos = env->hpos; | |
| 345 | } | |
| 346 | if ((ch & 0x7F) < 32) | |
| 347 | lbpputs("\033[1.v"); | |
| 348 | lbpputc(ch); | |
| 349 | cur_hpos += w; | |
| 350 | } | |
| 351 | ||
| 352 | void lbp_printer::vdmstart() | |
| 353 | { | |
| 354 | FILE *f; | |
| 355 | static int changed_origin = 0; | |
| 356 | errno = 0; | |
| 357 | f = tmpfile(); | |
| 358 | // f = fopen("/tmp/gtmp","w+"); | |
| 359 | if (f == NULL) | |
| 360 | perror("Opening temporary file"); | |
| 361 | vdminit(f); | |
| 362 | if (!changed_origin) { // we should change the origin only one time | |
| 363 | changed_origin = 1; | |
| 364 | vdmorigin(-63, 0); | |
| 365 | } | |
| 366 | vdmlinewidth(line_thickness); | |
| 367 | } | |
| 368 | ||
| 369 | void | |
| 370 | lbp_printer::vdmflush() | |
| 371 | { | |
| 372 | char buffer[1024]; | |
| 373 | int bytes_read = 1; | |
| 374 | vdmend(); | |
| 375 | fflush(lbpoutput); | |
| 376 | /* let's copy the vdm code to the output */ | |
| 377 | rewind(vdmoutput); | |
| 378 | do { | |
| 379 | bytes_read = fread(buffer, 1, sizeof(buffer), vdmoutput); | |
| 380 | bytes_read = fwrite(buffer, 1, bytes_read, lbpoutput); | |
| 381 | } while (bytes_read == sizeof(buffer)); | |
| 382 | fclose(vdmoutput); // This will also delete the file, | |
| 383 | // since it is created by tmpfile() | |
| 384 | vdmoutput = NULL; | |
| 385 | } | |
| 386 | ||
| 387 | inline void lbp_printer::setfillmode(int mode) | |
| 388 | { | |
| 389 | if (mode != fill_mode) { | |
| 390 | if (mode != 1) | |
| 391 | vdmsetfillmode(mode, 1, 0); | |
| 392 | else | |
| 393 | vdmsetfillmode(mode, 1, 1); // To get black we must use white | |
| 394 | // inverted | |
| 395 | fill_mode = mode; | |
| 396 | } | |
| 397 | } | |
| 398 | ||
| 399 | inline void lbp_printer::polygon(int hpos, int vpos, int np, int *p) | |
| 400 | { | |
| 401 | int *points, i; | |
| 402 | points = new int[np + 2]; | |
| 403 | points[0] = hpos; | |
| 404 | points[1] = vpos; | |
| 405 | // fprintf(stderr, "Poligon (%d,%d) ", points[0], points[1]); | |
| 406 | for (i = 0; i < np; i++) | |
| 407 | points[i + 2] = p[i]; | |
| 408 | // for (i = 0; i < np; i++) fprintf(stderr, " %d ", p[i]); | |
| 409 | // fprintf(stderr, "\n"); | |
| 410 | vdmpolygon((np /2) + 1, points); | |
| 411 | } | |
| 412 | ||
| 413 | void lbp_printer::draw(int code, int *p, int np, const environment *env) | |
| 414 | { | |
| 415 | if ((req_linethickness < 0 ) && (env->size != cur_size)) | |
| 416 | set_line_thickness(req_linethickness,env); | |
| 417 | ||
| 418 | switch (code) { | |
| 419 | case 't': | |
| 420 | if (np == 0) | |
| 421 | line_thickness = 1; | |
| 422 | else { // troff gratuitously adds an extra 0 | |
| 423 | if (np != 1 && np != 2) { | |
| 424 | error("0 or 1 argument required for thickness"); | |
| 425 | break; | |
| 465b256c | 426 | } |
| 92d0a6a6 | 427 | set_line_thickness(p[0],env); |
| 465b256c | 428 | } |
| 92d0a6a6 JR |
429 | break; |
| 430 | case 'l': // Line | |
| 431 | if (np != 2) { | |
| 432 | error("2 arguments required for line"); | |
| 433 | break; | |
| 434 | } | |
| 435 | if (!vdminited()) | |
| 436 | vdmstart(); | |
| 437 | vdmline(env->hpos, env->vpos, p[0], p[1]); | |
| 438 | /* fprintf(stderr, "\nline: %d,%d - %d,%d thickness %d == %d\n", | |
| 439 | env->hpos - 64,env->vpos -64, env->hpos - 64 + p[0], | |
| 440 | env->vpos -64 + p[1], env->size, line_thickness);*/ | |
| 441 | break; | |
| 442 | case 'R': // Rule | |
| 443 | if (np != 2) { | |
| 444 | error("2 arguments required for Rule"); | |
| 445 | break; | |
| 446 | } | |
| 447 | if (vdminited()) { | |
| 448 | setfillmode(fill_pattern); // Solid Rule | |
| 449 | vdmrectangle(env->hpos, env->vpos, p[0], p[1]); | |
| 450 | } | |
| 451 | else { | |
| 452 | lbpruleabs(env->hpos - 64, env->vpos -64, p[0], p[1]); | |
| 453 | cur_vpos = p[1]; | |
| 454 | cur_hpos = p[0]; | |
| 455 | } | |
| 456 | // fprintf(stderr, "\nrule: thickness %d == %d\n", | |
| 457 | // env->size, line_thickness); | |
| 458 | break; | |
| 459 | case 'P': // Filled Polygon | |
| 460 | if (!vdminited()) | |
| 461 | vdmstart(); | |
| 462 | setfillmode(fill_pattern); | |
| 463 | polygon(env->hpos, env->vpos, np, p); | |
| 464 | break; | |
| 465 | case 'p': // Empty Polygon | |
| 466 | if (!vdminited()) | |
| 467 | vdmstart(); | |
| 468 | setfillmode(0); | |
| 469 | polygon(env->hpos, env->vpos, np, p); | |
| 470 | break; | |
| 471 | case 'C': // Filled Circle | |
| 472 | if (!vdminited()) | |
| 473 | vdmstart(); | |
| 474 | // fprintf(stderr, "Circle (%d,%d) Fill %d\n", | |
| 475 | // env->hpos, env->vpos, fill_pattern); | |
| 476 | setfillmode(fill_pattern); | |
| 477 | vdmcircle(env->hpos + (p[0]/2), env->vpos, p[0]/2); | |
| 478 | break; | |
| 479 | case 'c': // Empty Circle | |
| 480 | if (!vdminited()) | |
| 481 | vdmstart(); | |
| 482 | setfillmode(0); | |
| 483 | vdmcircle(env->hpos + (p[0]/2), env->vpos, p[0]/2); | |
| 484 | break; | |
| 485 | case 'E': // Filled Ellipse | |
| 486 | if (!vdminited()) | |
| 487 | vdmstart(); | |
| 488 | setfillmode(fill_pattern); | |
| 489 | vdmellipse(env->hpos + (p[0]/2), env->vpos, p[0]/2, p[1]/2, 0); | |
| 490 | break; | |
| 491 | case 'e': // Empty Ellipse | |
| 492 | if (!vdminited()) | |
| 493 | vdmstart(); | |
| 494 | setfillmode(0); | |
| 495 | vdmellipse(env->hpos + (p[0]/2), env->vpos, p[0]/2, p[1]/2, 0); | |
| 496 | break; | |
| 497 | case 'a': // Arc | |
| 498 | if (!vdminited()) | |
| 499 | vdmstart(); | |
| 500 | setfillmode(0); | |
| 501 | // VDM draws arcs clockwise and pic counterclockwise | |
| 502 | // We must compensate for that, exchanging the starting and | |
| 503 | // ending points | |
| 504 | vdmvarc(env->hpos + p[0], env->vpos+p[1], | |
| 505 | int(sqrt(double((p[0]*p[0]) + (p[1]*p[1])))), | |
| 506 | p[2], p[3], | |
| 507 | (-p[0]), (-p[1]), 1, 2); | |
| 508 | break; | |
| 509 | case '~': // Spline | |
| 510 | if (!vdminited()) | |
| 511 | vdmstart(); | |
| 512 | setfillmode(0); | |
| 513 | vdmspline(np/2, env->hpos, env->vpos, p); | |
| 514 | break; | |
| 515 | case 'f': | |
| 516 | if (np != 1 && np != 2) { | |
| 517 | error("1 argument required for fill"); | |
| 518 | break; | |
| 519 | } | |
| 520 | // fprintf(stderr, "Fill %d\n", p[0]); | |
| 521 | if ((p[0] == 1) || (p[0] >= 1000)) { // Black | |
| 522 | fill_pattern = 1; | |
| 523 | break; | |
| 524 | } | |
| 525 | if (p[0] == 0) { // White | |
| 526 | fill_pattern = 0; | |
| 527 | break; | |
| 528 | } | |
| 529 | if ((p[0] > 1) && (p[0] < 1000)) | |
| 530 | { | |
| 531 | if (p[0] >= 990) fill_pattern = -23; | |
| 532 | else if (p[0] >= 700) fill_pattern = -28; | |
| 533 | else if (p[0] >= 500) fill_pattern = -27; | |
| 534 | else if (p[0] >= 400) fill_pattern = -26; | |
| 535 | else if (p[0] >= 300) fill_pattern = -25; | |
| 536 | else if (p[0] >= 200) fill_pattern = -22; | |
| 537 | else if (p[0] >= 100) fill_pattern = -24; | |
| 538 | else fill_pattern = -21; | |
| 539 | } | |
| 540 | break; | |
| 541 | case 'F': | |
| 542 | // not implemented yet | |
| 543 | break; | |
| 544 | default: | |
| 545 | error("unrecognised drawing command `%1'", char(code)); | |
| 546 | break; | |
| 547 | } | |
| 548 | return; | |
| 549 | } | |
| 550 | ||
| 551 | font *lbp_printer::make_font(const char *nm) | |
| 552 | { | |
| 553 | return lbp_font::load_lbp_font(nm); | |
| 554 | } | |
| 555 | ||
| 556 | printer *make_printer() | |
| 557 | { | |
| 558 | return new lbp_printer(user_papersize, user_paperwidth, user_paperlength); | |
| 559 | } | |
| 560 | ||
| 561 | static struct { | |
| 562 | const char *name; | |
| 563 | int code; | |
| 564 | } lbp_papersizes[] = | |
| 565 | {{ "A4", 14 }, | |
| 566 | { "letter", 30 }, | |
| 567 | { "legal", 32 }, | |
| 568 | { "executive", 40 }, | |
| 569 | }; | |
| 570 | ||
| 571 | static int set_papersize(const char *paperformat) | |
| 572 | { | |
| 573 | unsigned int i; | |
| 574 | // First test for a standard (i.e. supported directly by the printer) | |
| 575 | // paper size | |
| 576 | for (i = 0 ; i < sizeof(lbp_papersizes) / sizeof(lbp_papersizes[0]); i++) | |
| 577 | { | |
| 578 | if (strcasecmp(lbp_papersizes[i].name,paperformat) == 0) | |
| 579 | return lbp_papersizes[i].code; | |
| 580 | } | |
| 581 | // Otherwise, we assume a custom paper size | |
| 582 | return 82; | |
| 583 | } | |
| 584 | ||
| 585 | static void handle_unknown_desc_command(const char *command, const char *arg, | |
| 586 | const char *filename, int lineno) | |
| 587 | { | |
| 588 | // orientation command | |
| 589 | if (strcasecmp(command, "orientation") == 0) { | |
| 590 | // We give priority to command line options | |
| 591 | if (orientation > 0) | |
| 592 | return; | |
| 593 | if (arg == 0) | |
| 594 | error_with_file_and_line(filename, lineno, | |
| 595 | "`orientation' command requires an argument"); | |
| 596 | else { | |
| 597 | if (strcasecmp(arg, "portrait") == 0) | |
| 598 | orientation = 0; | |
| 599 | else { | |
| 600 | if (strcasecmp(arg, "landscape") == 0) | |
| 601 | orientation = 1; | |
| 602 | else | |
| 603 | error_with_file_and_line(filename, lineno, | |
| 604 | "invalid argument to `orientation' command"); | |
| 605 | } | |
| 606 | } | |
| 607 | } | |
| 608 | } | |
| 609 | ||
| 610 | static struct option long_options[] = { | |
| 611 | { "orientation", required_argument, NULL, 'o' }, | |
| 612 | { "version", no_argument, NULL, 'v' }, | |
| 613 | { "copies", required_argument, NULL, 'c' }, | |
| 614 | { "landscape", no_argument, NULL, 'l' }, | |
| 615 | { "papersize", required_argument, NULL, 'p' }, | |
| 616 | { "linewidth", required_argument, NULL, 'w' }, | |
| 617 | { "fontdir", required_argument, NULL, 'F' }, | |
| 618 | { "help", no_argument, NULL, 'h' }, | |
| 619 | { NULL, 0, 0, 0 } | |
| 620 | }; | |
| 621 | ||
| 622 | static void usage(FILE *stream) | |
| 623 | { | |
| 624 | fprintf(stream, | |
| 625 | "usage: %s [-lvh] [-c n] [-p paper_size] [-F dir] [-o or]\n" | |
| 626 | " [-w width] [files ...]\n" | |
| 627 | "\n" | |
| 628 | " -o --orientation=[portrait|landscape]\n" | |
| 629 | " -v --version\n" | |
| 630 | " -c --copies=numcopies\n" | |
| 631 | " -l --landscape\n" | |
| 632 | " -p --papersize=paper_size\n" | |
| 633 | " -w --linewidth=width\n" | |
| 634 | " -F --fontdir=dir\n" | |
| 635 | " -h --help\n", | |
| 636 | program_name); | |
| 637 | } | |
| 638 | ||
| 639 | int main(int argc, char **argv) | |
| 640 | { | |
| 641 | if (program_name == NULL) | |
| 642 | program_name = strsave(argv[0]); | |
| 643 | font::set_unknown_desc_command_handler(handle_unknown_desc_command); | |
| 644 | // command line parsing | |
| 645 | int c = 0; | |
| 646 | int option_index = 0; | |
| 647 | while (c >= 0) { | |
| 648 | c = getopt_long (argc, argv, "c:F:hI:lo:p:vw:", | |
| 649 | long_options, &option_index); | |
| 650 | switch (c) { | |
| 651 | case 'F': | |
| 652 | font::command_line_font_dir(optarg); | |
| 653 | break; | |
| 654 | case 'I': | |
| 655 | // ignore include path arguments | |
| 656 | break; | |
| 657 | case 'p': | |
| 658 | { | |
| 659 | const char *s; | |
| 660 | if (!font::scan_papersize(optarg, &s, | |
| 661 | &user_paperlength, &user_paperwidth)) | |
| 662 | error("invalid paper size `%1' ignored", optarg); | |
| 663 | else | |
| 664 | user_papersize = set_papersize(s); | |
| 665 | break; | |
| 666 | } | |
| 667 | case 'l': | |
| 668 | orientation = 1; | |
| 669 | break; | |
| 670 | case 'v': | |
| 671 | printf("GNU grolbp (groff) version %s\n", Version_string); | |
| 672 | exit(0); | |
| 673 | break; | |
| 674 | case 'o': | |
| 675 | if (strcasecmp(optarg, "portrait") == 0) | |
| 676 | orientation = 0; | |
| 677 | else { | |
| 678 | if (strcasecmp(optarg, "landscape") == 0) | |
| 679 | orientation = 1; | |
| 680 | else | |
| 681 | error("unknown orientation '%1'", optarg); | |
| 465b256c | 682 | } |
| 92d0a6a6 JR |
683 | break; |
| 684 | case 'c': | |
| 685 | { | |
| 686 | char *ptr; | |
| 687 | long n = strtol(optarg, &ptr, 10); | |
| 688 | if ((n <= 0) && (ptr == optarg)) | |
| 689 | error("argument for -c must be a positive integer"); | |
| 690 | else if (n <= 0 || n > 32767) | |
| 691 | error("out of range argument for -c"); | |
| 692 | else | |
| 693 | ncopies = unsigned(n); | |
| 694 | break; | |
| 695 | } | |
| 696 | case 'w': | |
| 697 | { | |
| 698 | char *ptr; | |
| 699 | long n = strtol(optarg, &ptr, 10); | |
| 700 | if (n == 0 && ptr == optarg) | |
| 701 | error("argument for -w must be a non-negative integer"); | |
| 702 | else if (n < 0 || n > INT_MAX) | |
| 703 | error("out of range argument for -w"); | |
| 704 | else | |
| 705 | linewidth_factor = int(n); | |
| 706 | break; | |
| 707 | } | |
| 708 | case 'h': | |
| 709 | usage(stdout); | |
| 710 | exit(0); | |
| 711 | break; | |
| 712 | case '?': | |
| 713 | usage(stderr); | |
| 714 | exit(1); | |
| 715 | break; | |
| 716 | } | |
| 717 | } | |
| 718 | if (optind >= argc) | |
| 719 | do_file("-"); | |
| 720 | while (optind < argc) | |
| 721 | do_file(argv[optind++]); | |
| 722 | lbpputs("\033c\033<"); | |
| 723 | return 0; | |
| 724 | } |