grolbp output device
[s-roff.git] / src / devices / grolbp / lbp.cc
blob361a8fa41ada180dc91ac27a0fb6aa8781330238
1 // -*- C++ -*-
2 /* Copyright (C) 1994 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 #define _GNU_SOURCE
29 #include <getopt.h>
31 #include <string.h>
32 #include "driver.h"
33 #include "lbp.h"
34 #include "charset.h"
37 static short int papersize = -1, // papersize
38 orientation = -1 , // orientation
39 paperlength = 0, // Custom Paper size
40 paperwidth = 0,
41 ncopies = 1; // Number of copies
43 class lbp_font : public font {
44 public:
45 ~lbp_font();
46 void handle_unknown_font_command(const char *command, const char *arg,
47 const char *filename, int lineno);
48 static lbp_font *load_lbp_font(const char *);
49 char *lbpname;
50 char is_scalable;
51 private:
52 lbp_font(const char *);
56 class lbp_printer : public printer {
57 public:
58 lbp_printer();
59 ~lbp_printer();
60 void set_char(int, font *, const environment *, int, const char *name);
61 void draw(int code, int *p, int np, const environment *env);
62 void begin_page(int);
63 void end_page(int page_length);
64 font *make_font(const char *);
65 void end_of_line();
66 private:
67 void set_line_thickness(int size, int dot = 0);
68 void vdmstart();
69 void vdmflush(); // the name vdmend was already used in lbp.h
70 void setfillmode(int mode);
71 void lbp_printer::polygon( int hpos,int vpos,int np,int *p);
72 char *lbp_printer::font_name(const lbp_font *f, const int siz);
74 int fill_pattern;
75 int fill_mode;
76 int cur_hpos;
77 int cur_vpos;
78 lbp_font *cur_font;
79 int cur_size;
80 unsigned short cur_symbol_set;
81 int line_thickness;
84 lbp_font::lbp_font(const char *nm)
85 : font(nm)
89 lbp_font::~lbp_font()
93 lbp_font *lbp_font::load_lbp_font(const char *s)
95 lbp_font *f = new lbp_font(s);
96 f->lbpname = NULL;
97 f->is_scalable = 1; // Default is that fonts are scalable
98 if (!f->load()) {
99 delete f;
100 return 0;
102 return f;
106 void lbp_font::handle_unknown_font_command(const char *command,
107 const char *arg,
108 const char *filename, int lineno)
110 if (strcmp(command, "lbpname") == 0) {
111 if (arg == 0)
112 fatal_with_file_and_line(filename, lineno,
113 "`%1' command requires an argument",
114 command);
115 this->lbpname = new char[strlen(arg)+1];
116 strcpy(this->lbpname,arg);
117 // We Recongnize bitmaped fonts by the first character of it's name
118 if (arg[0] == 'N') this->is_scalable = 0;
119 // fprintf(stderr,"Loading font \"%s\" \n",arg);
120 }; // if (strcmp(command, "lbpname")
121 // fprintf(stderr,"Loading font %s \"%s\" in %s at %d\n",command,arg,filename,lineno);
124 static void wp54charset()
126 int i;
128 lbpputs("\033[714;100;29;0;32;120.}");
129 for (i = 0; i < sizeof(symset) ; i++) lbpputc(symset[i]);
130 lbpputs("\033[100;0 D");
131 return ;
134 lbp_printer::lbp_printer()
135 : fill_pattern(1),
136 fill_mode(0),
137 cur_hpos(-1),
138 cur_font(0),
139 cur_size(0),
140 cur_symbol_set(0),
141 line_thickness(-1)
143 lbpinit(stdout);
144 lbpputs("\033c\033;\033[2&z\033[7 I\033[?32h\033[?33h\033[11h");
145 wp54charset(); // Define the new symbol set
146 lbpputs("\033[7 I\033[?32h\033[?33h\033[11h");
147 // Paper size handling
148 if (orientation < 0) orientation = 0;// Default orientation is portrait
149 if (papersize < 0) papersize = 14; // Default paper size is A4
150 if (papersize < 80) // standard paper
151 lbpprintf("\033[%dp",(papersize | orientation));
152 else // Custom paper
153 lbpprintf("\033[%d;%d;%dp",(papersize | orientation),\
154 paperlength,paperwidth);
156 // Number of copies
157 lbpprintf("\033[%dv\n",ncopies);
159 lbpputs("\033[0u\033[1u\033P1y Grolbp\033\\");
160 lbpmoveabs(0,0);
161 lbpputs("\033[0t\033[2t");
162 lbpputs("\033('$2\033)' 1"); // Primary symbol set IBML
163 // Secondary symbol set IBMR1
164 cur_symbol_set = 0;
167 lbp_printer::~lbp_printer()
169 lbpputs("\033P1y\033\\");
170 lbpputs("\033c\033<");
173 void lbp_printer::begin_page(int)
177 void lbp_printer::end_page(int)
179 if (vdminited()) vdmflush();
180 lbpputc('\f');
181 cur_hpos = -1;
184 void lbp_printer::end_of_line()
186 cur_hpos = -1; // force absolute motion
189 char *lbp_printer::font_name(const lbp_font *f, const int siz)
191 static char bfont_name[255] ; // The resulting font name
192 char type, // Italic, Roman, Bold
193 ori, // Normal or Rotated
194 nam[strlen(f->lbpname)-2]; // The font name without other data.
195 int cpi; // The font size in characters per inch
196 // (Bitmaped fonts are monospaced).
199 /* Bitmap font selection is ugly in this printer, so don't expect
200 this function to be elegant. */
202 bfont_name[0] = 0x00;
203 if (orientation) // Landscape
204 ori = 'R';
205 else // Portrait
206 ori = 'N';
207 type = f->lbpname[strlen(f->lbpname)-1];
208 strncpy(nam,&(f->lbpname[1]),strlen(f->lbpname)-2);
209 nam[strlen(f->lbpname)-2] = 0x00;
210 // fprintf(stderr,"Bitmap font '%s' %d %c %c \n",nam,siz,type,ori);
211 /* Since these fonts are avaiable only at certain sizes,
212 10 and 17 cpi for courier, 12 and 17 cpi for elite,
213 we adjust the resulting size. */
214 // Fortunately there were only two bitmaped fonts shiped with the printer.
215 if (!strcasecmp(nam,"courier"))
216 { // Courier font
217 if (siz >= 12) cpi = 10;
218 else cpi = 17;
220 if (!strcasecmp(nam,"elite"))
221 { // Elite font
222 if (siz >= 10) cpi = 12;
223 else cpi = 17;
226 // Now that we have all the data, let's generate the font name.
227 if ((type != 'B') && (type != 'I')) // Roman font
228 sprintf(bfont_name,"%c%s%d",ori,nam,cpi);
229 else
230 sprintf(bfont_name,"%c%s%d%c",ori,nam,cpi,type);
232 return bfont_name;
234 }; // lbp_printer::font_name
236 void lbp_printer::set_char(int index, font *f, const environment *env, int w, const char *name)
238 int code = f->get_code(index);
240 unsigned char ch = code & 0xff;
241 unsigned short symbol_set = code >> 8;
242 if (f != cur_font) {
243 lbp_font *psf = (lbp_font *)f;
244 // fprintf(stderr,"Loading font %s \"%d\" \n",psf->lbpname,env->size);
245 if (psf->is_scalable)
246 { // Scalable font selection is different from bitmaped
247 lbpprintf("\033Pz%s.IBML\033\\\033[%d C",psf->lbpname,\
248 (int)((env->size*300)/72));
249 } else
250 { // Bitmaped font
251 lbpprintf("\033Pz%s.IBML\033\\\n",font_name(psf,env->size));
253 lbpputs("\033)' 1"); // Select IBML and IBMR1 symbol set
254 cur_size = env->size;
255 cur_font = psf;
256 cur_symbol_set = 0;
258 if (symbol_set != cur_symbol_set) {
259 if ( cur_symbol_set == 3 ) {
260 // if current symbol set is Symbol we must restore the font
261 lbpprintf("\033Pz%s.IBML\033\\\033[%d C",cur_font->lbpname,\
262 (int)((env->size*300)/72));
263 }; // if ( cur_symbol_set == 3 )
264 switch (symbol_set) {
265 case 0: lbpputs("\033('$2\033)' 1"); // Select IBML and IBMR1 symbol sets
266 break;
267 case 1: lbpputs("\033(d\033)' 1"); // Select wp54 symbol set
268 break;
269 case 2: lbpputs("\033('$2\033)'!0"); // Select IBMP symbol set
270 break;
271 case 3: lbpprintf("\033PzSymbol.SYML\033\\\033[%d C",\
272 (int)((env->size*300)/72));
273 lbpputs("\033(\"!!0\033)\"!!1"); // Select symbol font
274 break;
275 case 4: lbpputs("\033)\"! 1\033(\"!$2"); // Select PS symbol set
276 break;
277 }; // switch (symbol_set)
279 // if (symbol_set == 1) lbpputs("\033(d"); // Select wp54 symbol set
280 // else lbpputs("\033('$2\033)' 1"); // Select IBML and IBMR1 symbol sets
281 cur_symbol_set = symbol_set;
283 if (env->size != cur_size) {
285 if (!cur_font->is_scalable)
286 lbpprintf("\033Pz%s.IBML\033\\\n",font_name(cur_font,env->size));
287 else
288 lbpprintf("\033[%d C",(int)((env->size*300)/72));
289 cur_size = env->size;
291 if ((env->hpos != cur_hpos) || (env->vpos != cur_vpos))
293 // lbpmoveabs(env->hpos - ((5*300)/16),env->vpos );
294 lbpmoveabs(env->hpos - 64,env->vpos - 64 );
295 cur_vpos = env->vpos;
296 cur_hpos = env->hpos;
298 if ((ch & 0x7F) < 32) lbpputs("\033[1.v");
299 lbpputc(ch);
300 cur_hpos += w;
303 void
304 lbp_printer::vdmstart()
306 FILE *f;
307 static int changed_origin = 0;
309 errno = 0;
310 f = tmpfile();
311 // f = fopen("/tmp/gtmp","w+");
312 if (f == NULL) perror("Openinig temp file");
313 vdminit(f);
314 if (!changed_origin) { // we should change the origin only one time
315 changed_origin = 1;
316 vdmorigin(-63,0);
318 vdmlinewidth(line_thickness);
322 void
323 lbp_printer::vdmflush()
325 char buffer[1024];
326 int bytes_read = 1;
328 vdmend();
329 fflush(lbpoutput);
330 /* lets copy the vdm code to the output */
331 rewind(vdmoutput);
334 bytes_read = fread(buffer,1,sizeof(buffer),vdmoutput);
335 bytes_read = fwrite(buffer,1,bytes_read,lbpoutput);
336 } while ( bytes_read == sizeof(buffer));
338 fclose(vdmoutput); // This will also delete the file,
339 // since it is created by tmpfile()
340 vdmoutput = NULL;
342 }; // lbp_printer::vdmflush
344 inline void
345 lbp_printer::setfillmode(int mode)
347 if (mode != fill_mode) {
348 if (mode != 1) vdmsetfillmode(mode,1,0);
349 else vdmsetfillmode(mode,1,1); // To get black we must use white
350 // inverted
351 fill_mode = mode;
353 }; // setfillmode
355 inline void
356 lbp_printer::polygon( int hpos,int vpos,int np,int *p)
358 int points[np+2],i;
360 points[0] = hpos;
361 points[1] = vpos;
362 /* fprintf(stderr,"Poligon (%d,%d) ", points[0],points[1]);*/
363 for (i = 0; i < np; i++) points[i+2] = p[i];
364 /* for (i = 0; i < np; i++) fprintf(stderr," %d ",p[i]);
365 fprintf(stderr,"\n"); */
366 vdmpolygon((np /2) + 1,points);
369 void lbp_printer::draw(int code, int *p, int np, const environment *env)
371 switch (code) {
372 case 't':
373 if (np == 0) line_thickness = 1;
374 else { // troff gratuitously adds an extra 0
375 if (np != 1 && np != 2) {
376 error("0 or 1 argument required for thickness");
377 break;
378 } // if (np != ...
379 if (p[0] == 0) line_thickness = 1;
380 if (p[0] < 0) // Default = 1 point
381 line_thickness = (int)(env->size*30/72);
382 line_thickness = (int)((abs(p[0])*env->size)/10);
383 if ((line_thickness > 16 ) && (!vdminited()))
384 { /* for greater thickness we must use VDM */
385 vdmstart();
386 /* vdmlinewidth(line_thickness); already done in
387 * vdmstart() */
389 if (vdminited()) vdmlinewidth(line_thickness);
390 // fprintf(stderr,"\nthickness: %d == %d, size %d\n",\
391 // p[0],line_thickness,env->size );
392 break;
393 } // else
395 case 'l': // Line
396 if (np != 2) {
397 error("2 arguments required for line");
398 break;
400 if (!vdminited()) vdmstart();
401 vdmline(env->hpos,env->vpos,p[0],p[1]);
402 /*fprintf(stderr,"\nline: %d,%d - %d,%d thickness %d == %d\n",\
403 env->hpos - 64,env->vpos -64, env->hpos - 64 + p[0],\
404 env->vpos -64 + p[1],env->size, line_thickness);*/
405 break;
406 case 'R': // Rule
407 if (np != 2) {
408 error("2 arguments required for Rule");
409 break;
411 if (vdminited()) {
412 setfillmode(fill_pattern); // Solid Rule
413 vdmrectangle(env->hpos,env->vpos,p[0],p[1]);
415 else {
416 lbpruleabs(env->hpos - 64,env->vpos -64 , p[0], p[1]);
417 cur_vpos = p[1];
418 cur_hpos = p[0];
420 fprintf(stderr,"\nrule: thickness %d == %d\n", env->size, line_thickness);
421 break;
422 case 'P': // Filled Polygon
423 if (!vdminited()) vdmstart();
424 setfillmode(fill_pattern);
425 polygon(env->hpos,env->vpos,np,p);
426 break;
427 case 'p': // Empty Polygon
428 if (!vdminited()) vdmstart();
429 setfillmode(0);
430 polygon(env->hpos,env->vpos,np,p);
431 break;
432 case 'C': // Filled Circle
433 if (!vdminited()) vdmstart();
434 // fprintf(stderr,"Circle (%d,%d) Fill %d\n",env->hpos,env->vpos,fill_pattern);
435 setfillmode(fill_pattern);
436 vdmcircle(env->hpos + (p[0]/2),env->vpos,p[0]/2);
437 break;
438 case 'c': // Empty Circle
439 if (!vdminited()) vdmstart();
440 setfillmode(0);
441 vdmcircle(env->hpos + (p[0]/2),env->vpos,p[0]/2);
442 break;
443 case 'E': // Filled Ellipse
444 if (!vdminited()) vdmstart();
445 setfillmode(fill_pattern);
446 vdmellipse(env->hpos + (p[0]/2),env->vpos,p[0]/2,p[1]/2,0);
447 break;
448 case 'e': // Empty Ellipse
449 if (!vdminited()) vdmstart();
450 setfillmode(0);
451 vdmellipse(env->hpos + (p[0]/2),env->vpos,p[0]/2,p[1]/2,0);
452 break;
453 case 'a': // Arc
454 if (!vdminited()) vdmstart();
455 setfillmode(0);
456 // VDM draws arcs clockwise and pic counterclockwise
457 // We must compensate for that, exchanging the starting and
458 // ending points
459 vdmvarc(env->hpos + p[0],env->vpos+p[1],\
460 int(sqrt( (p[0]*p[0])+(p[1]*p[1]))),\
461 p[2],p[3],\
462 (-p[0]),(-p[1]),1,2);
463 break;
464 case '~': // Spline
465 if (!vdminited()) vdmstart();
466 setfillmode(0);
467 vdmspline(np/2,env->hpos,env->vpos,p);
468 break;
469 case 'f':
470 if (np != 1 && np != 2) {
471 error("1 argument required for fill");
472 break;
474 // fprintf(stderr,"Fill %d\n",p[0]);
475 if ((p[0] == 1) || (p[0] >= 1000)) { // Black
476 fill_pattern = 1;
477 break;
478 }; // if (p[0] == 1)
479 if (p[0] == 0) { // White
480 fill_pattern = 0;
481 break;
483 if ((p[0] > 1) && (p[0] < 1000))
485 if (p[0] >= 990) fill_pattern = -23;
486 else if (p[0] >= 700) fill_pattern = -28;
487 else if (p[0] >= 500) fill_pattern = -27;
488 else if (p[0] >= 400) fill_pattern = -26;
489 else if (p[0] >= 300) fill_pattern = -25;
490 else if (p[0] >= 200) fill_pattern = -22;
491 else if (p[0] >= 100) fill_pattern = -24;
492 else fill_pattern = -21;
493 }; // if (p[0] >= 0 && p[0] <= 1000)
494 break;
495 default:
496 error("unrecognised drawing command `%1'", char(code));
497 break;
498 }; // switch (code)
499 return ;
502 font *lbp_printer::make_font(const char *nm)
504 return lbp_font::load_lbp_font(nm);
509 printer *make_printer()
511 return new lbp_printer;
514 static struct
516 const char *name;
517 int code;
518 } papersizes[] =
519 {{ "A4", 14 },
520 { "letter", 30 },
521 { "legal", 32 },
522 { "executive", 40 },
525 static int set_papersize(const char *papersize)
527 int i;
529 // First test for a standard (i.e. supported directly by the printer)
530 // papersize
531 for (i = 0 ; i < sizeof(papersizes)/sizeof(papersizes[0]); i++)
533 if (strcasecmp(papersizes[i].name,papersize) == 0)
534 return papersizes[i].code;
537 // Now test for a custom papersize
538 if (strncasecmp("cust",papersize,4) == 0)
540 char *p ,
541 *p1,
542 *papsize;
544 p = papsize = strdup(&papersize[4]);
545 if (papsize == NULL) return -1;
546 p1 = strsep(&p,"x");
547 if (p == NULL)
548 { // let's test for an uppercase x
549 p = papsize ;
550 p1 = strsep(&p,"X");
551 if (p == NULL) { free(papsize); return -1;};
552 }; // if (p1 == NULL)
553 paperlength = atoi(p1);
554 if (paperlength == 0) { free(papsize); return -1;};
555 paperwidth = atoi(p);
556 if (paperwidth == 0) { free(papsize); return -1;};
557 free(papsize);
558 return 82;
559 }; // if (strcnasecmp("cust",papersize,4) == 0)
561 return -1;
564 static int handle_papersize_command(const char *arg)
566 int n = set_papersize(arg);
568 if (n < 0)
569 { // If is not a standard nor custom paper size
570 // let's see if it's a file (i.e /etc/papersize )
571 FILE *f = fopen(arg,"r");
572 if (f != NULL)
573 { // the file exists and is readable
574 char psize[255],*p;
575 fgets(psize,254,f);
576 fclose(f);
577 // set_papersize doesn't like the trailing \n
578 p = psize; while (*p) p++;
579 if (*(--p) == '\n') *p = 0x00;
581 n = set_papersize(psize);
582 }; // if (f != NULL)
583 }; // if (n < 0)
585 return n;
586 }; // handle_papersize_command
589 static void handle_unknown_desc_command(const char *command, const char *arg,
590 const char *filename, int lineno)
592 // papersize command
593 if (strcasecmp(command, "papersize") == 0) {
594 // We give priority to command line options
595 if (papersize > 0) return;
596 if (arg == 0)
597 error_with_file_and_line(filename, lineno,
598 "`papersize' command requires an argument");
599 else
601 int n = handle_papersize_command(arg);
602 if (n < 0)
603 error_with_file_and_line(filename, lineno,
604 "unknown paper size `%1'", arg);
605 else
606 papersize = n;
608 }; // if (arg == 0) ... else ...
609 }; // if (strcasecmp(command, "papersize")
611 // orientation command
612 if (strcasecmp(command, "orientation") == 0) {
613 // We give priority to command line options
614 if (orientation > 0) return;
615 if (arg == 0)
616 error_with_file_and_line(filename, lineno,
617 "`papersize' command requires an argument");
618 else {
619 if (strcasecmp(arg,"portrait") == 0) orientation = 0;
620 else { if (strcasecmp(arg,"landscape") == 0) orientation = 1;
621 else error_with_file_and_line(filename, lineno,
622 "`orientation' command requires an argument");
624 }; // if (arg == 0) ... else ...
625 }; // if (strcasecmp(command, "orientation") == 0)
628 static struct option long_options[] = {
629 {"orientation",1,NULL,'o'},
630 {"version",0,NULL,'v'},
631 {"copies",1,NULL,'c'},
632 {"landscape",0,NULL,'l'},
633 {"papersize",1,NULL,'p'},
634 {"fontdir",1,NULL,'F'},
635 {"help",0,NULL,'h'},
636 {0, 0, 0, 0}
639 static void usage()
641 fprintf(stderr,
642 "usage: %s [-lvh] [-c n] [-p paper_size] [-F dir] [-o or] "\
643 " [files ...]\n"\
644 " -o --orientation=[portrait|landscape]\n"\
645 " -v --version\n"\
646 " -c --copies=numcopies\n"\
647 " -l --landscape\n"\
648 " -p --papersize=paper_size\n"\
649 " -F --fontdir=dir\n"\
650 " -h --help\n",
651 program_name);
652 exit(1);
653 }; // usage
655 int main(int argc, char **argv)
657 if (program_name == NULL) program_name = strdup(argv[0]);
659 font::set_unknown_desc_command_handler(handle_unknown_desc_command);
660 // command line parsing
661 int c = 0;
662 int digit_optind = 0, option_index = 0;
664 while (c >= 0 )
666 c = getopt_long (argc, argv, "F:p:lvo:c:h",\
667 long_options, &option_index);
668 switch (c) {
669 case 'F' : font::command_line_font_dir(optarg);
670 break;
671 case 'p' : {
672 int n = handle_papersize_command(optarg);
673 if (n < 0)
674 error("unknown paper size `%1'", optarg);
675 else
676 papersize = n;
677 break;
679 case 'l' : orientation = 1;
680 break;
681 case 'v' : {
682 extern const char *version_string;
683 fprintf(stderr, "grolbp version %s\n",\
684 version_string);
685 fflush(stderr);
686 break;
688 case 'o' : {
689 if (strcasecmp(optarg,"portrait") == 0)
690 orientation = 0;
691 else {
692 if (strcasecmp(optarg,"landscape") == 0)
693 orientation = 1;
694 else
695 error("unknown orientation '%1'", optarg);
698 break;
699 case 'c' : {
700 char *ptr;
701 long n = strtol(optarg, &ptr, 10);
702 if ((n <= 0) && (ptr == optarg))
703 error("argument for -c must be a positive integer");
704 else if (n <= 0 || n > 32767)
705 error("out of range argument for -c");
706 else
707 ncopies = unsigned(n);
708 break;
710 case 'h' : usage();
711 break;
714 }; // switch (c)
715 }; // while (c > 0 )
717 if (optind >= argc)
718 do_file("-");
720 while (optind < argc) {
721 do_file(argv[optind++]);
724 lbpputs("\033c\033<");
725 return 0;