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