* configure.in: Added test for strdup.
[s-roff.git] / src / devices / grolbp / lbp.cc
blob379846ab5df12eb43132c0d3a57a5823feb6ab2a
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
30 #include <string.h>
31 #include "driver.h"
32 #include "lbp.h"
33 #include "charset.h"
36 static short int papersize = -1, // papersize
37 orientation = -1 , // orientation
38 paperlength = 0, // Custom Paper size
39 paperwidth = 0,
40 ncopies = 1; // Number of copies
42 class lbp_font : public font {
43 public:
44 ~lbp_font();
45 void handle_unknown_font_command(const char *command, const char *arg,
46 const char *filename, int lineno);
47 static lbp_font *load_lbp_font(const char *);
48 char *lbpname;
49 char is_scalable;
50 private:
51 lbp_font(const char *);
55 class lbp_printer : public printer {
56 public:
57 lbp_printer();
58 ~lbp_printer();
59 void set_char(int, font *, const environment *, int, const char *name);
60 void draw(int code, int *p, int np, const environment *env);
61 void begin_page(int);
62 void end_page(int page_length);
63 font *make_font(const char *);
64 void end_of_line();
65 private:
66 void set_line_thickness(int size, int dot = 0);
67 void vdmstart();
68 void vdmflush(); // the name vdmend was already used in lbp.h
69 void setfillmode(int mode);
70 void lbp_printer::polygon( int hpos,int vpos,int np,int *p);
71 char *lbp_printer::font_name(const lbp_font *f, const int siz);
73 int fill_pattern;
74 int fill_mode;
75 int cur_hpos;
76 int cur_vpos;
77 lbp_font *cur_font;
78 int cur_size;
79 unsigned short cur_symbol_set;
80 int line_thickness;
83 // Compatibility section.
85 // Here we define some functions not present in some of the targets
86 // platforms
87 #ifndef HAVE_STRSEP
88 // Solaris 8 doesn't have the strsep function
89 static char *strsep(char **pcadena, const char *delim)
91 char *p;
93 p = strtok(*pcadena,delim);
94 *pcadena = strtok(NULL,delim);
95 return p;
98 #endif
100 #ifndef HAVE_STRDUP
101 // Ditto with OS/390 and strdup
102 static char *strdup(const char *s)
104 char *result;
106 result = (char *)malloc(strlen(s)+1);
107 if (result != NULL) strcpy(result,s);
108 return result;
110 }; // strdup
112 #endif
113 lbp_font::lbp_font(const char *nm)
114 : font(nm)
118 lbp_font::~lbp_font()
122 lbp_font *lbp_font::load_lbp_font(const char *s)
124 lbp_font *f = new lbp_font(s);
125 f->lbpname = NULL;
126 f->is_scalable = 1; // Default is that fonts are scalable
127 if (!f->load()) {
128 delete f;
129 return 0;
131 return f;
135 void lbp_font::handle_unknown_font_command(const char *command,
136 const char *arg,
137 const char *filename, int lineno)
139 if (strcmp(command, "lbpname") == 0) {
140 if (arg == 0)
141 fatal_with_file_and_line(filename, lineno,
142 "`%1' command requires an argument",
143 command);
144 this->lbpname = new char[strlen(arg)+1];
145 strcpy(this->lbpname,arg);
146 // We Recongnize bitmaped fonts by the first character of it's name
147 if (arg[0] == 'N') this->is_scalable = 0;
148 // fprintf(stderr,"Loading font \"%s\" \n",arg);
149 }; // if (strcmp(command, "lbpname")
150 // fprintf(stderr,"Loading font %s \"%s\" in %s at %d\n",command,arg,filename,lineno);
153 static void wp54charset()
155 int i;
157 lbpputs("\033[714;100;29;0;32;120.}");
158 for (i = 0; i < sizeof(symset) ; i++) lbpputc(symset[i]);
159 lbpputs("\033[100;0 D");
160 return ;
163 lbp_printer::lbp_printer()
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 line_thickness(-1)
172 lbpinit(stdout);
173 lbpputs("\033c\033;\033[2&z\033[7 I\033[?32h\033[?33h\033[11h");
174 wp54charset(); // Define the new symbol set
175 lbpputs("\033[7 I\033[?32h\033[?33h\033[11h");
176 // Paper size handling
177 if (orientation < 0) orientation = 0;// Default orientation is portrait
178 if (papersize < 0) papersize = 14; // Default paper size is A4
179 if (papersize < 80) // standard paper
180 lbpprintf("\033[%dp",(papersize | orientation));
181 else // Custom paper
182 lbpprintf("\033[%d;%d;%dp",(papersize | orientation),\
183 paperlength,paperwidth);
185 // Number of copies
186 lbpprintf("\033[%dv\n",ncopies);
188 lbpputs("\033[0u\033[1u\033P1y Grolbp\033\\");
189 lbpmoveabs(0,0);
190 lbpputs("\033[0t\033[2t");
191 lbpputs("\033('$2\033)' 1"); // Primary symbol set IBML
192 // Secondary symbol set IBMR1
193 cur_symbol_set = 0;
196 lbp_printer::~lbp_printer()
198 lbpputs("\033P1y\033\\");
199 lbpputs("\033c\033<");
202 void lbp_printer::begin_page(int)
206 void lbp_printer::end_page(int)
208 if (vdminited()) vdmflush();
209 lbpputc('\f');
210 cur_hpos = -1;
213 void lbp_printer::end_of_line()
215 cur_hpos = -1; // force absolute motion
218 char *lbp_printer::font_name(const lbp_font *f, const int siz)
220 static char bfont_name[255] ; // The resulting font name
221 char type, // Italic, Roman, Bold
222 ori, // Normal or Rotated
223 *nam; // The font name without other data.
224 // nam[strlen(f->lbpname)-2]; // The font name without other data.
225 int cpi; // The font size in characters per inch
226 // (Bitmaped fonts are monospaced).
229 /* Bitmap font selection is ugly in this printer, so don't expect
230 this function to be elegant. */
232 bfont_name[0] = 0x00;
233 if (orientation) // Landscape
234 ori = 'R';
235 else // Portrait
236 ori = 'N';
237 type = f->lbpname[strlen(f->lbpname)-1];
238 nam = new char[strlen(f->lbpname)-2];
239 strncpy(nam,&(f->lbpname[1]),strlen(f->lbpname)-2);
240 nam[strlen(f->lbpname)-2] = 0x00;
241 // fprintf(stderr,"Bitmap font '%s' %d %c %c \n",nam,siz,type,ori);
242 /* Since these fonts are avaiable only at certain sizes,
243 10 and 17 cpi for courier, 12 and 17 cpi for elite,
244 we adjust the resulting size. */
245 cpi = 17;
246 // Fortunately there were only two bitmaped fonts shiped with the printer.
247 if (!strcasecmp(nam,"courier"))
248 { // Courier font
249 if (siz >= 12) cpi = 10;
250 else cpi = 17;
252 if (!strcasecmp(nam,"elite"))
253 { // Elite font
254 if (siz >= 10) cpi = 12;
255 else cpi = 17;
258 // Now that we have all the data, let's generate the font name.
259 if ((type != 'B') && (type != 'I')) // Roman font
260 sprintf(bfont_name,"%c%s%d",ori,nam,cpi);
261 else
262 sprintf(bfont_name,"%c%s%d%c",ori,nam,cpi,type);
264 return bfont_name;
266 }; // lbp_printer::font_name
268 void lbp_printer::set_char(int index, font *f, const environment *env, int w, const char *name)
270 int code = f->get_code(index);
272 unsigned char ch = code & 0xff;
273 unsigned short symbol_set = code >> 8;
274 if (f != cur_font) {
275 lbp_font *psf = (lbp_font *)f;
276 // fprintf(stderr,"Loading font %s \"%d\" \n",psf->lbpname,env->size);
277 if (psf->is_scalable)
278 { // Scalable font selection is different from bitmaped
279 lbpprintf("\033Pz%s.IBML\033\\\033[%d C",psf->lbpname,\
280 (int)((env->size*300)/72));
281 } else
282 { // Bitmaped font
283 lbpprintf("\033Pz%s.IBML\033\\\n",font_name(psf,env->size));
285 lbpputs("\033)' 1"); // Select IBML and IBMR1 symbol set
286 cur_size = env->size;
287 cur_font = psf;
288 cur_symbol_set = 0;
290 if (symbol_set != cur_symbol_set) {
291 if ( cur_symbol_set == 3 ) {
292 // if current symbol set is Symbol we must restore the font
293 lbpprintf("\033Pz%s.IBML\033\\\033[%d C",cur_font->lbpname,\
294 (int)((env->size*300)/72));
295 }; // if ( cur_symbol_set == 3 )
296 switch (symbol_set) {
297 case 0: lbpputs("\033('$2\033)' 1"); // Select IBML and IBMR1 symbol sets
298 break;
299 case 1: lbpputs("\033(d\033)' 1"); // Select wp54 symbol set
300 break;
301 case 2: lbpputs("\033('$2\033)'!0"); // Select IBMP symbol set
302 break;
303 case 3: lbpprintf("\033PzSymbol.SYML\033\\\033[%d C",\
304 (int)((env->size*300)/72));
305 lbpputs("\033(\"!!0\033)\"!!1"); // Select symbol font
306 break;
307 case 4: lbpputs("\033)\"! 1\033(\"!$2"); // Select PS symbol set
308 break;
309 }; // switch (symbol_set)
311 // if (symbol_set == 1) lbpputs("\033(d"); // Select wp54 symbol set
312 // else lbpputs("\033('$2\033)' 1"); // Select IBML and IBMR1 symbol sets
313 cur_symbol_set = symbol_set;
315 if (env->size != cur_size) {
317 if (!cur_font->is_scalable)
318 lbpprintf("\033Pz%s.IBML\033\\\n",font_name(cur_font,env->size));
319 else
320 lbpprintf("\033[%d C",(int)((env->size*300)/72));
321 cur_size = env->size;
323 if ((env->hpos != cur_hpos) || (env->vpos != cur_vpos))
325 // lbpmoveabs(env->hpos - ((5*300)/16),env->vpos );
326 lbpmoveabs(env->hpos - 64,env->vpos - 64 );
327 cur_vpos = env->vpos;
328 cur_hpos = env->hpos;
330 if ((ch & 0x7F) < 32) lbpputs("\033[1.v");
331 lbpputc(ch);
332 cur_hpos += w;
335 void
336 lbp_printer::vdmstart()
338 FILE *f;
339 static int changed_origin = 0;
341 errno = 0;
342 f = tmpfile();
343 // f = fopen("/tmp/gtmp","w+");
344 if (f == NULL) perror("Openinig temp file");
345 vdminit(f);
346 if (!changed_origin) { // we should change the origin only one time
347 changed_origin = 1;
348 vdmorigin(-63,0);
350 vdmlinewidth(line_thickness);
354 void
355 lbp_printer::vdmflush()
357 char buffer[1024];
358 int bytes_read = 1;
360 vdmend();
361 fflush(lbpoutput);
362 /* lets copy the vdm code to the output */
363 rewind(vdmoutput);
366 bytes_read = fread(buffer,1,sizeof(buffer),vdmoutput);
367 bytes_read = fwrite(buffer,1,bytes_read,lbpoutput);
368 } while ( bytes_read == sizeof(buffer));
370 fclose(vdmoutput); // This will also delete the file,
371 // since it is created by tmpfile()
372 vdmoutput = NULL;
374 }; // lbp_printer::vdmflush
376 inline void
377 lbp_printer::setfillmode(int mode)
379 if (mode != fill_mode) {
380 if (mode != 1) vdmsetfillmode(mode,1,0);
381 else vdmsetfillmode(mode,1,1); // To get black we must use white
382 // inverted
383 fill_mode = mode;
385 }; // setfillmode
387 inline void
388 lbp_printer::polygon( int hpos,int vpos,int np,int *p)
390 //int points[np+2],i;
391 int *points,i;
393 points = new int[np+2];
394 points[0] = hpos;
395 points[1] = vpos;
396 /* fprintf(stderr,"Poligon (%d,%d) ", points[0],points[1]);*/
397 for (i = 0; i < np; i++) points[i+2] = p[i];
398 /* for (i = 0; i < np; i++) fprintf(stderr," %d ",p[i]);
399 fprintf(stderr,"\n"); */
400 vdmpolygon((np /2) + 1,points);
403 void lbp_printer::draw(int code, int *p, int np, const environment *env)
405 switch (code) {
406 case 't':
407 if (np == 0) line_thickness = 1;
408 else { // troff gratuitously adds an extra 0
409 if (np != 1 && np != 2) {
410 error("0 or 1 argument required for thickness");
411 break;
412 } // if (np != ...
413 if (p[0] == 0) line_thickness = 1;
414 if (p[0] < 0) // Default = 1 point
415 line_thickness = (int)(env->size*30/72);
416 line_thickness = (int)((abs(p[0])*env->size)/10);
417 if ((line_thickness > 16 ) && (!vdminited()))
418 { /* for greater thickness we must use VDM */
419 vdmstart();
420 /* vdmlinewidth(line_thickness); already done in
421 * vdmstart() */
423 if (vdminited()) vdmlinewidth(line_thickness);
424 // fprintf(stderr,"\nthickness: %d == %d, size %d\n",\
425 // p[0],line_thickness,env->size );
426 break;
427 } // else
429 case 'l': // Line
430 if (np != 2) {
431 error("2 arguments required for line");
432 break;
434 if (!vdminited()) vdmstart();
435 vdmline(env->hpos,env->vpos,p[0],p[1]);
436 /*fprintf(stderr,"\nline: %d,%d - %d,%d thickness %d == %d\n",\
437 env->hpos - 64,env->vpos -64, env->hpos - 64 + p[0],\
438 env->vpos -64 + p[1],env->size, line_thickness);*/
439 break;
440 case 'R': // Rule
441 if (np != 2) {
442 error("2 arguments required for Rule");
443 break;
445 if (vdminited()) {
446 setfillmode(fill_pattern); // Solid Rule
447 vdmrectangle(env->hpos,env->vpos,p[0],p[1]);
449 else {
450 lbpruleabs(env->hpos - 64,env->vpos -64 , p[0], p[1]);
451 cur_vpos = p[1];
452 cur_hpos = p[0];
454 fprintf(stderr,"\nrule: thickness %d == %d\n", env->size, line_thickness);
455 break;
456 case 'P': // Filled Polygon
457 if (!vdminited()) vdmstart();
458 setfillmode(fill_pattern);
459 polygon(env->hpos,env->vpos,np,p);
460 break;
461 case 'p': // Empty Polygon
462 if (!vdminited()) vdmstart();
463 setfillmode(0);
464 polygon(env->hpos,env->vpos,np,p);
465 break;
466 case 'C': // Filled Circle
467 if (!vdminited()) vdmstart();
468 // fprintf(stderr,"Circle (%d,%d) Fill %d\n",env->hpos,env->vpos,fill_pattern);
469 setfillmode(fill_pattern);
470 vdmcircle(env->hpos + (p[0]/2),env->vpos,p[0]/2);
471 break;
472 case 'c': // Empty Circle
473 if (!vdminited()) vdmstart();
474 setfillmode(0);
475 vdmcircle(env->hpos + (p[0]/2),env->vpos,p[0]/2);
476 break;
477 case 'E': // Filled Ellipse
478 if (!vdminited()) vdmstart();
479 setfillmode(fill_pattern);
480 vdmellipse(env->hpos + (p[0]/2),env->vpos,p[0]/2,p[1]/2,0);
481 break;
482 case 'e': // Empty Ellipse
483 if (!vdminited()) vdmstart();
484 setfillmode(0);
485 vdmellipse(env->hpos + (p[0]/2),env->vpos,p[0]/2,p[1]/2,0);
486 break;
487 case 'a': // Arc
488 if (!vdminited()) vdmstart();
489 setfillmode(0);
490 // VDM draws arcs clockwise and pic counterclockwise
491 // We must compensate for that, exchanging the starting and
492 // ending points
493 vdmvarc(env->hpos + p[0],env->vpos+p[1],\
494 int(sqrt( (p[0]*p[0])+(p[1]*p[1]))),\
495 p[2],p[3],\
496 (-p[0]),(-p[1]),1,2);
497 break;
498 case '~': // Spline
499 if (!vdminited()) vdmstart();
500 setfillmode(0);
501 vdmspline(np/2,env->hpos,env->vpos,p);
502 break;
503 case 'f':
504 if (np != 1 && np != 2) {
505 error("1 argument required for fill");
506 break;
508 // fprintf(stderr,"Fill %d\n",p[0]);
509 if ((p[0] == 1) || (p[0] >= 1000)) { // Black
510 fill_pattern = 1;
511 break;
512 }; // if (p[0] == 1)
513 if (p[0] == 0) { // White
514 fill_pattern = 0;
515 break;
517 if ((p[0] > 1) && (p[0] < 1000))
519 if (p[0] >= 990) fill_pattern = -23;
520 else if (p[0] >= 700) fill_pattern = -28;
521 else if (p[0] >= 500) fill_pattern = -27;
522 else if (p[0] >= 400) fill_pattern = -26;
523 else if (p[0] >= 300) fill_pattern = -25;
524 else if (p[0] >= 200) fill_pattern = -22;
525 else if (p[0] >= 100) fill_pattern = -24;
526 else fill_pattern = -21;
527 }; // if (p[0] >= 0 && p[0] <= 1000)
528 break;
529 default:
530 error("unrecognised drawing command `%1'", char(code));
531 break;
532 }; // switch (code)
533 return ;
536 font *lbp_printer::make_font(const char *nm)
538 return lbp_font::load_lbp_font(nm);
543 printer *make_printer()
545 return new lbp_printer;
548 static struct
550 const char *name;
551 int code;
552 } papersizes[] =
553 {{ "A4", 14 },
554 { "letter", 30 },
555 { "legal", 32 },
556 { "executive", 40 },
560 static int set_papersize(const char *papersize)
562 int i;
564 // First test for a standard (i.e. supported directly by the printer)
565 // papersize
566 for (i = 0 ; i < sizeof(papersizes)/sizeof(papersizes[0]); i++)
568 if (strcasecmp(papersizes[i].name,papersize) == 0)
569 return papersizes[i].code;
572 // Now test for a custom papersize
573 if (strncasecmp("cust",papersize,4) == 0)
575 char *p ,
576 *p1,
577 *papsize;
579 p = papsize = strdup(&papersize[4]);
580 if (papsize == NULL) return -1;
581 p1 = strsep(&p,"x");
582 if (p == NULL)
583 { // let's test for an uppercase x
584 p = papsize ;
585 p1 = strsep(&p,"X");
586 if (p == NULL) { free(papsize); return -1;};
587 }; // if (p1 == NULL)
588 paperlength = atoi(p1);
589 if (paperlength == 0) { free(papsize); return -1;};
590 paperwidth = atoi(p);
591 if (paperwidth == 0) { free(papsize); return -1;};
592 free(papsize);
593 return 82;
594 }; // if (strcnasecmp("cust",papersize,4) == 0)
596 return -1;
599 static int handle_papersize_command(const char *arg)
601 int n = set_papersize(arg);
603 if (n < 0)
604 { // If is not a standard nor custom paper size
605 // let's see if it's a file (i.e /etc/papersize )
606 FILE *f = fopen(arg,"r");
607 if (f != NULL)
608 { // the file exists and is readable
609 char psize[255],*p;
610 fgets(psize,254,f);
611 fclose(f);
612 // set_papersize doesn't like the trailing \n
613 p = psize; while (*p) p++;
614 if (*(--p) == '\n') *p = 0x00;
616 n = set_papersize(psize);
617 }; // if (f != NULL)
618 }; // if (n < 0)
620 return n;
621 }; // handle_papersize_command
624 static void handle_unknown_desc_command(const char *command, const char *arg,
625 const char *filename, int lineno)
627 // papersize command
628 if (strcasecmp(command, "papersize") == 0) {
629 // We give priority to command line options
630 if (papersize > 0) return;
631 if (arg == 0)
632 error_with_file_and_line(filename, lineno,
633 "`papersize' command requires an argument");
634 else
636 int n = handle_papersize_command(arg);
637 if (n < 0)
638 error_with_file_and_line(filename, lineno,
639 "unknown paper size `%1'", arg);
640 else
641 papersize = n;
643 }; // if (arg == 0) ... else ...
644 }; // if (strcasecmp(command, "papersize")
646 // orientation command
647 if (strcasecmp(command, "orientation") == 0) {
648 // We give priority to command line options
649 if (orientation > 0) return;
650 if (arg == 0)
651 error_with_file_and_line(filename, lineno,
652 "`papersize' command requires an argument");
653 else {
654 if (strcasecmp(arg,"portrait") == 0) orientation = 0;
655 else { if (strcasecmp(arg,"landscape") == 0) orientation = 1;
656 else error_with_file_and_line(filename, lineno,
657 "`orientation' command requires an argument");
659 }; // if (arg == 0) ... else ...
660 }; // if (strcasecmp(command, "orientation") == 0)
663 static struct option long_options[] = {
664 {"orientation",1,NULL,'o'},
665 {"version",0,NULL,'v'},
666 {"copies",1,NULL,'c'},
667 {"landscape",0,NULL,'l'},
668 {"papersize",1,NULL,'p'},
669 {"fontdir",1,NULL,'F'},
670 {"help",0,NULL,'h'},
671 {0, 0, 0, 0}
674 static void usage()
676 fprintf(stderr,
677 "usage: %s [-lvh] [-c n] [-p paper_size] [-F dir] [-o or] "\
678 " [files ...]\n"\
679 " -o --orientation=[portrait|landscape]\n"\
680 " -v --version\n"\
681 " -c --copies=numcopies\n"\
682 " -l --landscape\n"\
683 " -p --papersize=paper_size\n"\
684 " -F --fontdir=dir\n"\
685 " -h --help\n",
686 program_name);
687 exit(1);
688 }; // usage
690 int main(int argc, char **argv)
692 if (program_name == NULL) program_name = strdup(argv[0]);
694 font::set_unknown_desc_command_handler(handle_unknown_desc_command);
695 // command line parsing
696 int c = 0;
697 int option_index = 0;
699 while (c >= 0 )
701 c = getopt_long (argc, argv, "F:p:lvo:c:h",\
702 long_options, &option_index);
703 switch (c) {
704 case 'F' : font::command_line_font_dir(optarg);
705 break;
706 case 'p' : {
707 int n = handle_papersize_command(optarg);
708 if (n < 0)
709 error("unknown paper size `%1'", optarg);
710 else
711 papersize = n;
712 break;
714 case 'l' : orientation = 1;
715 break;
716 case 'v' : {
717 extern const char *version_string;
718 fprintf(stderr, "grolbp version %s\n",\
719 version_string);
720 fflush(stderr);
721 break;
723 case 'o' : {
724 if (strcasecmp(optarg,"portrait") == 0)
725 orientation = 0;
726 else {
727 if (strcasecmp(optarg,"landscape") == 0)
728 orientation = 1;
729 else
730 error("unknown orientation '%1'", optarg);
732 break;
734 case 'c' : {
735 char *ptr;
736 long n = strtol(optarg, &ptr, 10);
737 if ((n <= 0) && (ptr == optarg))
738 error("argument for -c must be a positive integer");
739 else if (n <= 0 || n > 32767)
740 error("out of range argument for -c");
741 else
742 ncopies = unsigned(n);
743 break;
745 case 'h' : usage();
746 break;
749 }; // switch (c)
750 }; // while (c > 0 )
752 if (optind >= argc)
753 do_file("-");
755 while (optind < argc) {
756 do_file(argv[optind++]);
759 lbpputs("\033c\033<");
760 return 0;