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