2 /* Copyright (C) 1994-2018 Free Software Foundation, Inc.
3 Written by Francisco Andrés Verdú <pandres@dragonet.es> with many ideas
4 taken from the other groff drivers.
6 This file is part of groff.
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>. */
24 - Add X command to include bitmaps
34 extern "C" const char *Version_string;
36 static int user_papersize = -1; // papersize
37 static int orientation = -1; // orientation
38 static double user_paperlength = 0; // Custom Paper size
39 static double user_paperwidth = 0;
40 static int ncopies = 1; // Number of copies
42 #define DEFAULT_LINEWIDTH_FACTOR 40 // 0.04em
43 static int linewidth_factor = DEFAULT_LINEWIDTH_FACTOR;
45 static int set_papersize(const char *paperformat);
47 class lbp_font : public font {
50 void handle_unknown_font_command(const char *command, const char *arg,
51 const char *filename, int lineno);
52 static lbp_font *load_lbp_font(const char *);
56 lbp_font(const char *);
59 class lbp_printer : public printer {
61 lbp_printer(int, double, double);
63 void set_char(glyph *, font *, const environment *, int, const char *name);
64 void draw(int code, int *p, int np, const environment *env);
66 void end_page(int page_length);
67 font *make_font(const char *);
70 void set_line_thickness(int size,const environment *env);
72 void vdmflush(); // the name vdmend was already used in lbp.h
73 void setfillmode(int mode);
74 void polygon( int hpos,int vpos,int np,int *p);
75 char *font_name(const lbp_font *f, const int siz);
83 unsigned short cur_symbol_set;
85 int req_linethickness; // requested line thickness
87 int paperlength; // custom paper size
91 lbp_font::lbp_font(const char *nm)
100 lbp_font *lbp_font::load_lbp_font(const char *s)
102 lbp_font *f = new lbp_font(s);
104 f->is_scalable = 1; // Default is that fonts are scalable
113 void lbp_font::handle_unknown_font_command(const char *command,
115 const char *filename, int lineno)
117 if (strcmp(command, "lbpname") == 0) {
119 fatal_with_file_and_line(filename, lineno,
120 "'%1' command requires an argument",
122 this->lbpname = new char[strlen(arg) + 1];
123 strcpy(this->lbpname, arg);
124 // we recognize bitmapped fonts by the first character of its name
126 this->is_scalable = 0;
127 // fprintf(stderr, "Loading font \"%s\" \n", arg);
129 // fprintf(stderr, "Loading font %s \"%s\" in %s at %d\n",
130 // command, arg, filename, lineno);
133 static void wp54charset()
136 lbpputs("\033[714;100;29;0;32;120.}");
137 for (i = 0; i < sizeof(symset); i++)
139 lbpputs("\033[100;0 D");
143 lbp_printer::lbp_printer(int ps, double pw, double pl)
150 req_linethickness(-1)
152 SET_BINARY(fileno(stdout));
154 lbpputs("\033c\033;\033[2&z\033[7 I\033[?32h\033[?33h\033[11h");
155 wp54charset(); // Define the new symbol set
156 lbpputs("\033[7 I\033[?32h\033[?33h\033[11h");
157 // Paper size handling
159 orientation = 0; // Default orientation is portrait
160 papersize = 14; // Default paper size is A4
161 if (font::papersize) {
162 papersize = set_papersize(font::papersize);
163 paperlength = font::paperlength;
164 paperwidth = font::paperwidth;
168 paperlength = int(pl * font::res + 0.5);
169 paperwidth = int(pw * font::res + 0.5);
171 if (papersize < 80) // standard paper
172 lbpprintf("\033[%dp", (papersize | orientation));
174 lbpprintf("\033[%d;%d;%dp", (papersize | orientation),
175 paperlength, paperwidth);
177 lbpprintf("\033[%dv\n", ncopies);
178 lbpputs("\033[0u\033[1u\033P1y Grolbp\033\\");
180 lbpputs("\033[0t\033[2t");
181 lbpputs("\033('$2\033)' 1"); // Primary symbol set IBML
182 // Secondary symbol set IBMR1
186 lbp_printer::~lbp_printer()
188 lbpputs("\033P1y\033\\");
189 lbpputs("\033c\033<");
192 inline void lbp_printer::set_line_thickness(int size,const environment *env)
199 // (env->size * (font::res/72)) * (linewidth_factor/1000)
200 // we ought to check for overflow
202 env->size * linewidth_factor * font::res / 72000;
204 line_thickness = size;
205 } // else from if (size == 0)
206 if (line_thickness < 1)
209 vdmlinewidth(line_thickness);
210 req_linethickness = size; // an size requested
211 /* fprintf(stderr, "thickness: %d == %d, size %d, %d \n",
212 size, line_thickness, env->size,req_linethickness); */
214 } // lbp_printer::set_line_thickness
216 void lbp_printer::begin_page(int)
220 void lbp_printer::end_page(int)
228 void lbp_printer::end_of_line()
230 cur_hpos = -1; // force absolute motion
233 char *lbp_printer::font_name(const lbp_font *f, const int siz)
235 static char bfont_name[255]; // The resulting font name
236 char type, // Italic, Roman, Bold
237 ori, // Normal or Rotated
238 *nam; // The font name without other data.
239 int cpi; // The font size in characters per inch
240 // (bitmapped fonts are monospaced).
241 /* Bitmap font selection is ugly in this printer, so don't expect
242 this function to be elegant. */
243 bfont_name[0] = 0x00;
244 if (orientation) // Landscape
248 type = f->lbpname[strlen(f->lbpname) - 1];
249 nam = new char[strlen(f->lbpname) - 2];
250 strncpy(nam, &(f->lbpname[1]), strlen(f->lbpname) - 2);
251 nam[strlen(f->lbpname) - 2] = 0x00;
252 // fprintf(stderr, "Bitmap font '%s' %d %c %c \n", nam, siz, type, ori);
253 /* Since these fonts are available only at certain sizes,
254 10 and 17 cpi for courier, 12 and 17 cpi for elite,
255 we adjust the resulting size. */
257 // Fortunately there are only two bitmapped fonts shipped with the printer.
258 if (!strcasecmp(nam, "courier")) {
264 if (!strcasecmp(nam, "elite")) {
269 // Now that we have all the data, let's generate the font name.
270 if ((type != 'B') && (type != 'I')) // Roman font
271 sprintf(bfont_name, "%c%s%d", ori, nam, cpi);
273 sprintf(bfont_name, "%c%s%d%c", ori, nam, cpi, type);
277 void lbp_printer::set_char(glyph *g, font *f, const environment *env,
280 int code = f->get_code(g);
281 unsigned char ch = code & 0xff;
282 unsigned short symbol_set = code >> 8;
284 lbp_font *psf = (lbp_font *)f;
285 // fprintf(stderr, "Loading font %s \"%d\" \n", psf->lbpname, env->size);
286 if (psf->is_scalable) {
287 // Scalable font selection is different from bitmaped
288 lbpprintf("\033Pz%s.IBML\033\\\033[%d C", psf->lbpname,
289 (int)((env->size * font::res) / 72));
293 lbpprintf("\033Pz%s.IBML\033\\\n", font_name(psf, env->size));
294 lbpputs("\033)' 1"); // Select IBML and IBMR1 symbol set
297 // Update the line thickness if needed
298 if ((req_linethickness < 0 ) && (env->size != cur_size))
299 set_line_thickness(req_linethickness,env);
300 cur_size = env->size;
302 if (symbol_set != cur_symbol_set) {
303 if (cur_symbol_set == 3)
304 // if current symbol set is Symbol we must restore the font
305 lbpprintf("\033Pz%s.IBML\033\\\033[%d C", cur_font->lbpname,
306 (int)((env->size * font::res) / 72));
307 switch (symbol_set) {
309 lbpputs("\033('$2\033)' 1"); // Select IBML and IBMR1 symbol sets
312 lbpputs("\033(d\033)' 1"); // Select wp54 symbol set
315 lbpputs("\033('$2\033)'!0"); // Select IBMP symbol set
318 lbpprintf("\033PzSymbol.SYML\033\\\033[%d C",
319 (int)((env->size * font::res) / 72));
320 lbpputs("\033(\"!!0\033)\"!!1"); // Select symbol font
323 lbpputs("\033)\"! 1\033(\"!$2"); // Select PS symbol set
326 cur_symbol_set = symbol_set;
328 if (env->size != cur_size) {
329 if (!cur_font->is_scalable)
330 lbpprintf("\033Pz%s.IBML\033\\\n", font_name(cur_font, env->size));
332 lbpprintf("\033[%d C", (int)((env->size * font::res) / 72));
333 cur_size = env->size;
334 // Update the line thickness if needed
335 if (req_linethickness < 0 )
336 set_line_thickness(req_linethickness,env);
338 if ((env->hpos != cur_hpos) || (env->vpos != cur_vpos)) {
339 // lbpmoveabs(env->hpos - ((5 * 300) / 16), env->vpos);
340 lbpmoveabs(env->hpos - 64, env->vpos - 64);
341 cur_vpos = env->vpos;
342 cur_hpos = env->hpos;
344 if ((ch & 0x7F) < 32)
350 void lbp_printer::vdmstart()
353 static int changed_origin = 0;
356 // f = fopen("/tmp/gtmp","w+");
358 perror("Opening temporary file");
360 if (!changed_origin) { // we should change the origin only one time
364 vdmlinewidth(line_thickness);
368 lbp_printer::vdmflush()
374 /* let's copy the vdm code to the output */
377 bytes_read = fread(buffer, 1, sizeof(buffer), vdmoutput);
378 bytes_read = fwrite(buffer, 1, bytes_read, lbpoutput);
379 } while (bytes_read == sizeof(buffer));
380 fclose(vdmoutput); // This will also delete the file,
381 // since it is created by tmpfile()
385 inline void lbp_printer::setfillmode(int mode)
387 if (mode != fill_mode) {
389 vdmsetfillmode(mode, 1, 0);
391 // To get black, we must use white inverted.
392 vdmsetfillmode(mode, 1, 1);
398 inline void lbp_printer::polygon(int hpos, int vpos, int np, int *p)
401 points = new int[np + 2];
404 // fprintf(stderr, "Polygon (%d,%d) ", points[0], points[1]);
405 for (i = 0; i < np; i++)
406 points[i + 2] = p[i];
407 // for (i = 0; i < np; i++) fprintf(stderr, " %d ", p[i]);
408 // fprintf(stderr, "\n");
409 vdmpolygon((np /2) + 1, points);
412 void lbp_printer::draw(int code, int *p, int np, const environment *env)
414 if ((req_linethickness < 0 ) && (env->size != cur_size))
415 set_line_thickness(req_linethickness,env);
421 else { // troff gratuitously adds an extra 0
422 if (np != 1 && np != 2) {
423 error("0 or 1 argument required for thickness");
426 set_line_thickness(p[0],env);
431 error("2 arguments required for line");
436 vdmline(env->hpos, env->vpos, p[0], p[1]);
437 /* fprintf(stderr, "\nline: %d,%d - %d,%d thickness %d == %d\n",
438 env->hpos - 64,env->vpos -64, env->hpos - 64 + p[0],
439 env->vpos -64 + p[1], env->size, line_thickness);*/
443 error("2 arguments required for Rule");
447 setfillmode(fill_pattern); // Solid Rule
448 vdmrectangle(env->hpos, env->vpos, p[0], p[1]);
451 lbpruleabs(env->hpos - 64, env->vpos -64, p[0], p[1]);
455 // fprintf(stderr, "\nrule: thickness %d == %d\n",
456 // env->size, line_thickness);
458 case 'P': // Filled Polygon
461 setfillmode(fill_pattern);
462 polygon(env->hpos, env->vpos, np, p);
464 case 'p': // Empty Polygon
468 polygon(env->hpos, env->vpos, np, p);
470 case 'C': // Filled Circle
473 // fprintf(stderr, "Circle (%d,%d) Fill %d\n",
474 // env->hpos, env->vpos, fill_pattern);
475 setfillmode(fill_pattern);
476 vdmcircle(env->hpos + (p[0]/2), env->vpos, p[0]/2);
478 case 'c': // Empty Circle
482 vdmcircle(env->hpos + (p[0]/2), env->vpos, p[0]/2);
484 case 'E': // Filled Ellipse
487 setfillmode(fill_pattern);
488 vdmellipse(env->hpos + (p[0]/2), env->vpos, p[0]/2, p[1]/2, 0);
490 case 'e': // Empty Ellipse
494 vdmellipse(env->hpos + (p[0]/2), env->vpos, p[0]/2, p[1]/2, 0);
500 // VDM draws arcs clockwise and pic counterclockwise
501 // We must compensate for that, exchanging the starting and
503 vdmvarc(env->hpos + p[0], env->vpos+p[1],
504 int(sqrt(double((p[0]*p[0]) + (p[1]*p[1])))),
506 (-p[0]), (-p[1]), 1, 2);
512 vdmspline(np/2, env->hpos, env->vpos, p);
515 if (np != 1 && np != 2) {
516 error("1 argument required for fill");
519 // fprintf(stderr, "Fill %d\n", p[0]);
520 if ((p[0] == 1) || (p[0] >= 1000)) { // Black
524 if (p[0] == 0) { // White
528 if ((p[0] > 1) && (p[0] < 1000))
530 if (p[0] >= 990) fill_pattern = -23;
531 else if (p[0] >= 700) fill_pattern = -28;
532 else if (p[0] >= 500) fill_pattern = -27;
533 else if (p[0] >= 400) fill_pattern = -26;
534 else if (p[0] >= 300) fill_pattern = -25;
535 else if (p[0] >= 200) fill_pattern = -22;
536 else if (p[0] >= 100) fill_pattern = -24;
537 else fill_pattern = -21;
541 // not implemented yet
544 error("unrecognised drawing command '%1'", char(code));
550 font *lbp_printer::make_font(const char *nm)
552 return lbp_font::load_lbp_font(nm);
555 printer *make_printer()
557 return new lbp_printer(user_papersize, user_paperwidth, user_paperlength);
570 static int set_papersize(const char *paperformat)
573 // First test for a standard (i.e. supported directly by the printer)
575 for (i = 0 ; i < sizeof(lbp_papersizes) / sizeof(lbp_papersizes[0]); i++)
577 if (strcasecmp(lbp_papersizes[i].name,paperformat) == 0)
578 return lbp_papersizes[i].code;
580 // Otherwise, we assume a custom paper size
584 static void handle_unknown_desc_command(const char *command, const char *arg,
585 const char *filename, int lineno)
587 // orientation command
588 if (strcasecmp(command, "orientation") == 0) {
589 // We give priority to command-line options
593 error_with_file_and_line(filename, lineno,
594 "'orientation' command requires an argument");
596 if (strcasecmp(arg, "portrait") == 0)
599 if (strcasecmp(arg, "landscape") == 0)
602 error_with_file_and_line(filename, lineno,
603 "invalid argument to 'orientation' command");
609 static struct option long_options[] = {
610 { "orientation", required_argument, NULL, 'o' },
611 { "version", no_argument, NULL, 'v' },
612 { "copies", required_argument, NULL, 'c' },
613 { "landscape", no_argument, NULL, 'l' },
614 { "papersize", required_argument, NULL, 'p' },
615 { "linewidth", required_argument, NULL, 'w' },
616 { "fontdir", required_argument, NULL, 'F' },
617 { "help", no_argument, NULL, 'h' },
621 static void usage(FILE *stream)
624 "usage: %s [-lvh] [-c n] [-p paper_size] [-F dir] [-o or]\n"
625 " [-w width] [files ...]\n"
627 " -o --orientation=[portrait|landscape]\n"
629 " -c --copies=numcopies\n"
631 " -p --papersize=paper_size\n"
632 " -w --linewidth=width\n"
633 " -F --fontdir=dir\n"
638 int main(int argc, char **argv)
640 if (program_name == NULL)
641 program_name = strsave(argv[0]);
642 font::set_unknown_desc_command_handler(handle_unknown_desc_command);
643 // command line parsing
645 int option_index = 0;
647 c = getopt_long (argc, argv, "c:F:hI:lo:p:vw:",
648 long_options, &option_index);
651 font::command_line_font_dir(optarg);
654 // ignore include path arguments
659 if (!font::scan_papersize(optarg, &s,
660 &user_paperlength, &user_paperwidth))
661 error("invalid paper size '%1' ignored", optarg);
663 user_papersize = set_papersize(s);
670 printf("GNU grolbp (groff) version %s\n", Version_string);
674 if (strcasecmp(optarg, "portrait") == 0)
677 if (strcasecmp(optarg, "landscape") == 0)
680 error("unknown orientation '%1'", optarg);
686 long n = strtol(optarg, &ptr, 10);
687 if ((n <= 0) && (ptr == optarg))
688 error("argument for -c must be a positive integer");
689 else if (n <= 0 || n > 32767)
690 error("out of range argument for -c");
692 ncopies = unsigned(n);
698 long n = strtol(optarg, &ptr, 10);
699 if (n == 0 && ptr == optarg)
700 error("argument for -w must be a non-negative integer");
701 else if (n < 0 || n > INT_MAX)
702 error("out of range argument for -w");
704 linewidth_factor = int(n);
719 while (optind < argc)
720 do_file(argv[optind++]);
722 lbpputs("\033c\033<");