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
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
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. */
27 - Add X command to include bitmaps
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
{
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 *);
59 lbp_font(const char *);
62 class lbp_printer
: public printer
{
64 lbp_printer(int, double, double);
66 void set_char(int, font
*, const environment
*, int, const char *name
);
67 void draw(int code
, int *p
, int np
, const environment
*env
);
69 void end_page(int page_length
);
70 font
*make_font(const char *);
73 void set_line_thickness(int size
,const environment
*env
);
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
);
86 unsigned short cur_symbol_set
;
88 int req_linethickness
; // requested line thickness
90 int paperlength
; // custom paper size
94 lbp_font::lbp_font(const char *nm
)
103 lbp_font
*lbp_font::load_lbp_font(const char *s
)
105 lbp_font
*f
= new lbp_font(s
);
107 f
->is_scalable
= 1; // Default is that fonts are scalable
116 void lbp_font::handle_unknown_font_command(const char *command
,
118 const char *filename
, int lineno
)
120 if (strcmp(command
, "lbpname") == 0) {
122 fatal_with_file_and_line(filename
, lineno
,
123 "`%1' command requires an argument",
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
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()
139 lbpputs("\033[714;100;29;0;32;120.}");
140 for (i
= 0; i
< sizeof(symset
); i
++)
142 lbpputs("\033[100;0 D");
146 lbp_printer::lbp_printer(int ps
, double pw
, double pl
)
153 req_linethickness(-1)
155 SET_BINARY(fileno(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
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
;
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
));
177 lbpprintf("\033[%d;%d;%dp", (papersize
| orientation
),
178 paperlength
, paperwidth
);
180 lbpprintf("\033[%dv\n", ncopies
);
181 lbpputs("\033[0u\033[1u\033P1y Grolbp\033\\");
183 lbpputs("\033[0t\033[2t");
184 lbpputs("\033('$2\033)' 1"); // Primary symbol set IBML
185 // Secondary symbol set IBMR1
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
)
202 // (env->size * (font::res/72)) * (linewidth_factor/1000)
203 // we ought to check for overflow
205 env
->size
* linewidth_factor
* font::res
/ 72000;
207 line_thickness
= size
;
208 } // else from if (size == 0)
209 if (line_thickness
< 1)
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); */
217 }; // lbp_printer::set_line_thickness
219 void lbp_printer::begin_page(int)
223 void lbp_printer::end_page(int)
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
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. */
260 // Fortunately there are only two bitmapped fonts shipped with the printer.
261 if (!strcasecmp(nam
, "courier")) {
267 if (!strcasecmp(nam
, "elite")) {
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
);
276 sprintf(bfont_name
, "%c%s%d%c", ori
, nam
, cpi
, type
);
280 void lbp_printer::set_char(int idx
, font
*f
, const environment
*env
,
283 int code
= f
->get_code(idx
);
284 unsigned char ch
= code
& 0xff;
285 unsigned short symbol_set
= code
>> 8;
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));
296 lbpprintf("\033Pz%s.IBML\033\\\n", font_name(psf
, env
->size
));
297 lbpputs("\033)' 1"); // Select IBML and IBMR1 symbol set
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
) {
312 lbpputs("\033('$2\033)' 1"); // Select IBML and IBMR1 symbol sets
315 lbpputs("\033(d\033)' 1"); // Select wp54 symbol set
318 lbpputs("\033('$2\033)'!0"); // Select IBMP symbol set
321 lbpprintf("\033PzSymbol.SYML\033\\\033[%d C",
322 (int)((env
->size
* font::res
) / 72));
323 lbpputs("\033(\"!!0\033)\"!!1"); // Select symbol font
326 lbpputs("\033)\"! 1\033(\"!$2"); // Select PS symbol set
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
));
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)
353 void lbp_printer::vdmstart()
356 static int changed_origin
= 0;
359 // f = fopen("/tmp/gtmp","w+");
361 perror("Opening temporary file");
363 if (!changed_origin
) { // we should change the origin only one time
367 vdmlinewidth(line_thickness
);
371 lbp_printer::vdmflush()
377 /* let's copy the vdm code to the output */
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()
388 inline void lbp_printer::setfillmode(int mode
)
390 if (mode
!= fill_mode
) {
392 vdmsetfillmode(mode
, 1, 0);
394 vdmsetfillmode(mode
, 1, 1); // To get black we must use white
400 inline void lbp_printer::polygon(int hpos
, int vpos
, int np
, int *p
)
403 points
= new int[np
+ 2];
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
);
423 else { // troff gratuitously adds an extra 0
424 if (np
!= 1 && np
!= 2) {
425 error("0 or 1 argument required for thickness");
428 set_line_thickness(p
[0],env
);
433 error("2 arguments required for line");
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);*/
445 error("2 arguments required for Rule");
449 setfillmode(fill_pattern
); // Solid Rule
450 vdmrectangle(env
->hpos
, env
->vpos
, p
[0], p
[1]);
453 lbpruleabs(env
->hpos
- 64, env
->vpos
-64, p
[0], p
[1]);
457 // fprintf(stderr, "\nrule: thickness %d == %d\n",
458 // env->size, line_thickness);
460 case 'P': // Filled Polygon
463 setfillmode(fill_pattern
);
464 polygon(env
->hpos
, env
->vpos
, np
, p
);
466 case 'p': // Empty Polygon
470 polygon(env
->hpos
, env
->vpos
, np
, p
);
472 case 'C': // Filled Circle
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);
480 case 'c': // Empty Circle
484 vdmcircle(env
->hpos
+ (p
[0]/2), env
->vpos
, p
[0]/2);
486 case 'E': // Filled Ellipse
489 setfillmode(fill_pattern
);
490 vdmellipse(env
->hpos
+ (p
[0]/2), env
->vpos
, p
[0]/2, p
[1]/2, 0);
492 case 'e': // Empty Ellipse
496 vdmellipse(env
->hpos
+ (p
[0]/2), env
->vpos
, p
[0]/2, p
[1]/2, 0);
502 // VDM draws arcs clockwise and pic counterclockwise
503 // We must compensate for that, exchanging the starting and
505 vdmvarc(env
->hpos
+ p
[0], env
->vpos
+p
[1],
506 int(sqrt(double((p
[0]*p
[0]) + (p
[1]*p
[1])))),
508 (-p
[0]), (-p
[1]), 1, 2);
514 vdmspline(np
/2, env
->hpos
, env
->vpos
, p
);
517 if (np
!= 1 && np
!= 2) {
518 error("1 argument required for fill");
521 // fprintf(stderr, "Fill %d\n", p[0]);
522 if ((p
[0] == 1) || (p
[0] >= 1000)) { // Black
526 if (p
[0] == 0) { // White
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;
543 // not implemented yet
546 error("unrecognised drawing command `%1'", char(code
));
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
);
572 static int set_papersize(const char *paperformat
)
575 // First test for a standard (i.e. supported directly by the printer)
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
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
595 error_with_file_and_line(filename
, lineno
,
596 "`orientation' command requires an argument");
598 if (strcasecmp(arg
, "portrait") == 0)
601 if (strcasecmp(arg
, "landscape") == 0)
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' },
623 static void usage(FILE *stream
)
626 "usage: %s [-lvh] [-c n] [-p paper_size] [-F dir] [-o or]\n"
627 " [-w width] [files ...]\n"
629 " -o --orientation=[portrait|landscape]\n"
631 " -c --copies=numcopies\n"
633 " -p --papersize=paper_size\n"
634 " -w --linewidth=width\n"
635 " -F --fontdir=dir\n"
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
647 int option_index
= 0;
649 c
= getopt_long (argc
, argv
, "c:F:hI:lo:p:vw:",
650 long_options
, &option_index
);
653 font::command_line_font_dir(optarg
);
656 // ignore include path arguments
661 if (!font::scan_papersize(optarg
, &s
,
662 &user_paperlength
, &user_paperwidth
))
663 error("invalid paper size `%1' ignored", optarg
);
665 user_papersize
= set_papersize(s
);
672 printf("GNU grolbp (groff) version %s\n", Version_string
);
676 if (strcasecmp(optarg
, "portrait") == 0)
679 if (strcasecmp(optarg
, "landscape") == 0)
682 error("unknown orientation '%1'", optarg
);
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");
694 ncopies
= unsigned(n
);
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");
706 linewidth_factor
= int(n
);
721 while (optind
< argc
)
722 do_file(argv
[optind
++]);
723 lbpputs("\033c\033<");