/contrib/groff/src/devices/grolbp/lbp.cpp
https://bitbucket.org/freebsd/freebsd-head/ · C++ · 725 lines · 605 code · 38 blank · 82 comment · 151 complexity · 5fb0c2c3b0ef69179d639d14d2345209 MD5 · raw file
- // -*- C++ -*-
- /* Copyright (C) 1994, 2000, 2001, 2002, 2003, 2004, 2005
- Free Software Foundation, Inc.
- Written by Francisco Andrés Verdú <pandres@dragonet.es> with many ideas
- taken from the other groff drivers.
- This file is part of groff.
- groff is free software; you can redistribute it and/or modify it under
- the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 2, or (at your option) any later
- version.
- groff is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- for more details.
- You should have received a copy of the GNU General Public License along
- with groff; see the file COPYING. If not, write to the Free Software
- Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
- /*
- TODO
- - Add X command to include bitmaps
- */
- #include "driver.h"
- #include "lbp.h"
- #include "charset.h"
- #include "paper.h"
- #include "nonposix.h"
- extern "C" const char *Version_string;
- static int user_papersize = -1; // papersize
- static int orientation = -1; // orientation
- static double user_paperlength = 0; // Custom Paper size
- static double user_paperwidth = 0;
- static int ncopies = 1; // Number of copies
- #define DEFAULT_LINEWIDTH_FACTOR 40 // 0.04em
- static int linewidth_factor = DEFAULT_LINEWIDTH_FACTOR;
- static int set_papersize(const char *paperformat);
- class lbp_font : public font {
- public:
- ~lbp_font();
- void handle_unknown_font_command(const char *command, const char *arg,
- const char *filename, int lineno);
- static lbp_font *load_lbp_font(const char *);
- char *lbpname;
- char is_scalable;
- private:
- lbp_font(const char *);
- };
- class lbp_printer : public printer {
- public:
- lbp_printer(int, double, double);
- ~lbp_printer();
- void set_char(int, font *, const environment *, int, const char *name);
- void draw(int code, int *p, int np, const environment *env);
- void begin_page(int);
- void end_page(int page_length);
- font *make_font(const char *);
- void end_of_line();
- private:
- void set_line_thickness(int size,const environment *env);
- void vdmstart();
- void vdmflush(); // the name vdmend was already used in lbp.h
- void setfillmode(int mode);
- void polygon( int hpos,int vpos,int np,int *p);
- char *font_name(const lbp_font *f, const int siz);
- int fill_pattern;
- int fill_mode;
- int cur_hpos;
- int cur_vpos;
- lbp_font *cur_font;
- int cur_size;
- unsigned short cur_symbol_set;
- int line_thickness;
- int req_linethickness; // requested line thickness
- int papersize;
- int paperlength; // custom paper size
- int paperwidth;
- };
- lbp_font::lbp_font(const char *nm)
- : font(nm)
- {
- }
- lbp_font::~lbp_font()
- {
- }
- lbp_font *lbp_font::load_lbp_font(const char *s)
- {
- lbp_font *f = new lbp_font(s);
- f->lbpname = NULL;
- f->is_scalable = 1; // Default is that fonts are scalable
- if (!f->load()) {
- delete f;
- return 0;
- }
- return f;
- }
- void lbp_font::handle_unknown_font_command(const char *command,
- const char *arg,
- const char *filename, int lineno)
- {
- if (strcmp(command, "lbpname") == 0) {
- if (arg == 0)
- fatal_with_file_and_line(filename, lineno,
- "`%1' command requires an argument",
- command);
- this->lbpname = new char[strlen(arg) + 1];
- strcpy(this->lbpname, arg);
- // we recognize bitmapped fonts by the first character of its name
- if (arg[0] == 'N')
- this->is_scalable = 0;
- // fprintf(stderr, "Loading font \"%s\" \n", arg);
- }
- // fprintf(stderr, "Loading font %s \"%s\" in %s at %d\n",
- // command, arg, filename, lineno);
- }
- static void wp54charset()
- {
- unsigned int i;
- lbpputs("\033[714;100;29;0;32;120.}");
- for (i = 0; i < sizeof(symset); i++)
- lbpputc(symset[i]);
- lbpputs("\033[100;0 D");
- return;
- }
- lbp_printer::lbp_printer(int ps, double pw, double pl)
- : fill_pattern(1),
- fill_mode(0),
- cur_hpos(-1),
- cur_font(0),
- cur_size(0),
- cur_symbol_set(0),
- req_linethickness(-1)
- {
- SET_BINARY(fileno(stdout));
- lbpinit(stdout);
- lbpputs("\033c\033;\033[2&z\033[7 I\033[?32h\033[?33h\033[11h");
- wp54charset(); // Define the new symbol set
- lbpputs("\033[7 I\033[?32h\033[?33h\033[11h");
- // Paper size handling
- if (orientation < 0)
- orientation = 0; // Default orientation is portrait
- papersize = 14; // Default paper size is A4
- if (font::papersize) {
- papersize = set_papersize(font::papersize);
- paperlength = font::paperlength;
- paperwidth = font::paperwidth;
- }
- if (ps >= 0) {
- papersize = ps;
- paperlength = int(pl * font::res + 0.5);
- paperwidth = int(pw * font::res + 0.5);
- }
- if (papersize < 80) // standard paper
- lbpprintf("\033[%dp", (papersize | orientation));
- else // Custom paper
- lbpprintf("\033[%d;%d;%dp", (papersize | orientation),
- paperlength, paperwidth);
- // Number of copies
- lbpprintf("\033[%dv\n", ncopies);
- lbpputs("\033[0u\033[1u\033P1y Grolbp\033\\");
- lbpmoveabs(0, 0);
- lbpputs("\033[0t\033[2t");
- lbpputs("\033('$2\033)' 1"); // Primary symbol set IBML
- // Secondary symbol set IBMR1
- cur_symbol_set = 0;
- }
- lbp_printer::~lbp_printer()
- {
- lbpputs("\033P1y\033\\");
- lbpputs("\033c\033<");
- }
- inline void lbp_printer::set_line_thickness(int size,const environment *env)
- {
- if (size == 0)
- line_thickness = 1;
- else {
- if (size < 0)
- // line_thickness =
- // (env->size * (font::res/72)) * (linewidth_factor/1000)
- // we ought to check for overflow
- line_thickness =
- env->size * linewidth_factor * font::res / 72000;
- else // size > 0
- line_thickness = size;
- } // else from if (size == 0)
- if (line_thickness < 1)
- line_thickness = 1;
- if (vdminited())
- vdmlinewidth(line_thickness);
- req_linethickness = size; // an size requested
- /* fprintf(stderr, "thickness: %d == %d, size %d, %d \n",
- size, line_thickness, env->size,req_linethickness); */
- return;
- } // lbp_printer::set_line_thickness
- void lbp_printer::begin_page(int)
- {
- }
- void lbp_printer::end_page(int)
- {
- if (vdminited())
- vdmflush();
- lbpputc('\f');
- cur_hpos = -1;
- }
- void lbp_printer::end_of_line()
- {
- cur_hpos = -1; // force absolute motion
- }
- char *lbp_printer::font_name(const lbp_font *f, const int siz)
- {
- static char bfont_name[255]; // The resulting font name
- char type, // Italic, Roman, Bold
- ori, // Normal or Rotated
- *nam; // The font name without other data.
- int cpi; // The font size in characters per inch
- // (bitmapped fonts are monospaced).
- /* Bitmap font selection is ugly in this printer, so don't expect
- this function to be elegant. */
- bfont_name[0] = 0x00;
- if (orientation) // Landscape
- ori = 'R';
- else // Portrait
- ori = 'N';
- type = f->lbpname[strlen(f->lbpname) - 1];
- nam = new char[strlen(f->lbpname) - 2];
- strncpy(nam, &(f->lbpname[1]), strlen(f->lbpname) - 2);
- nam[strlen(f->lbpname) - 2] = 0x00;
- // fprintf(stderr, "Bitmap font '%s' %d %c %c \n", nam, siz, type, ori);
- /* Since these fonts are available only at certain sizes,
- 10 and 17 cpi for courier, 12 and 17 cpi for elite,
- we adjust the resulting size. */
- cpi = 17;
- // Fortunately there are only two bitmapped fonts shipped with the printer.
- if (!strcasecmp(nam, "courier")) {
- // Courier font
- if (siz >= 12)
- cpi = 10;
- else cpi = 17;
- }
- if (!strcasecmp(nam, "elite")) {
- if (siz >= 10)
- cpi = 12;
- else cpi = 17;
- }
- // Now that we have all the data, let's generate the font name.
- if ((type != 'B') && (type != 'I')) // Roman font
- sprintf(bfont_name, "%c%s%d", ori, nam, cpi);
- else
- sprintf(bfont_name, "%c%s%d%c", ori, nam, cpi, type);
- return bfont_name;
- }
- void lbp_printer::set_char(int idx, font *f, const environment *env,
- int w, const char *)
- {
- int code = f->get_code(idx);
- unsigned char ch = code & 0xff;
- unsigned short symbol_set = code >> 8;
- if (f != cur_font) {
- lbp_font *psf = (lbp_font *)f;
- // fprintf(stderr, "Loading font %s \"%d\" \n", psf->lbpname, env->size);
- if (psf->is_scalable) {
- // Scalable font selection is different from bitmaped
- lbpprintf("\033Pz%s.IBML\033\\\033[%d C", psf->lbpname,
- (int)((env->size * font::res) / 72));
- }
- else
- // bitmapped font
- lbpprintf("\033Pz%s.IBML\033\\\n", font_name(psf, env->size));
- lbpputs("\033)' 1"); // Select IBML and IBMR1 symbol set
- cur_font = psf;
- cur_symbol_set = 0;
- // Update the line thickness if needed
- if ((req_linethickness < 0 ) && (env->size != cur_size))
- set_line_thickness(req_linethickness,env);
- cur_size = env->size;
- }
- if (symbol_set != cur_symbol_set) {
- if (cur_symbol_set == 3)
- // if current symbol set is Symbol we must restore the font
- lbpprintf("\033Pz%s.IBML\033\\\033[%d C", cur_font->lbpname,
- (int)((env->size * font::res) / 72));
- switch (symbol_set) {
- case 0:
- lbpputs("\033('$2\033)' 1"); // Select IBML and IBMR1 symbol sets
- break;
- case 1:
- lbpputs("\033(d\033)' 1"); // Select wp54 symbol set
- break;
- case 2:
- lbpputs("\033('$2\033)'!0"); // Select IBMP symbol set
- break;
- case 3:
- lbpprintf("\033PzSymbol.SYML\033\\\033[%d C",
- (int)((env->size * font::res) / 72));
- lbpputs("\033(\"!!0\033)\"!!1"); // Select symbol font
- break;
- case 4:
- lbpputs("\033)\"! 1\033(\"!$2"); // Select PS symbol set
- break;
- }
- cur_symbol_set = symbol_set;
- }
- if (env->size != cur_size) {
- if (!cur_font->is_scalable)
- lbpprintf("\033Pz%s.IBML\033\\\n", font_name(cur_font, env->size));
- else
- lbpprintf("\033[%d C", (int)((env->size * font::res) / 72));
- cur_size = env->size;
- // Update the line thickness if needed
- if (req_linethickness < 0 )
- set_line_thickness(req_linethickness,env);
- }
- if ((env->hpos != cur_hpos) || (env->vpos != cur_vpos)) {
- // lbpmoveabs(env->hpos - ((5 * 300) / 16), env->vpos);
- lbpmoveabs(env->hpos - 64, env->vpos - 64);
- cur_vpos = env->vpos;
- cur_hpos = env->hpos;
- }
- if ((ch & 0x7F) < 32)
- lbpputs("\033[1.v");
- lbpputc(ch);
- cur_hpos += w;
- }
- void lbp_printer::vdmstart()
- {
- FILE *f;
- static int changed_origin = 0;
- errno = 0;
- f = tmpfile();
- // f = fopen("/tmp/gtmp","w+");
- if (f == NULL)
- perror("Opening temporary file");
- vdminit(f);
- if (!changed_origin) { // we should change the origin only one time
- changed_origin = 1;
- vdmorigin(-63, 0);
- }
- vdmlinewidth(line_thickness);
- }
- void
- lbp_printer::vdmflush()
- {
- char buffer[1024];
- int bytes_read = 1;
- vdmend();
- fflush(lbpoutput);
- /* let's copy the vdm code to the output */
- rewind(vdmoutput);
- do {
- bytes_read = fread(buffer, 1, sizeof(buffer), vdmoutput);
- bytes_read = fwrite(buffer, 1, bytes_read, lbpoutput);
- } while (bytes_read == sizeof(buffer));
- fclose(vdmoutput); // This will also delete the file,
- // since it is created by tmpfile()
- vdmoutput = NULL;
- }
- inline void lbp_printer::setfillmode(int mode)
- {
- if (mode != fill_mode) {
- if (mode != 1)
- vdmsetfillmode(mode, 1, 0);
- else
- vdmsetfillmode(mode, 1, 1); // To get black we must use white
- // inverted
- fill_mode = mode;
- }
- }
- inline void lbp_printer::polygon(int hpos, int vpos, int np, int *p)
- {
- int *points, i;
- points = new int[np + 2];
- points[0] = hpos;
- points[1] = vpos;
- // fprintf(stderr, "Poligon (%d,%d) ", points[0], points[1]);
- for (i = 0; i < np; i++)
- points[i + 2] = p[i];
- // for (i = 0; i < np; i++) fprintf(stderr, " %d ", p[i]);
- // fprintf(stderr, "\n");
- vdmpolygon((np /2) + 1, points);
- }
- void lbp_printer::draw(int code, int *p, int np, const environment *env)
- {
- if ((req_linethickness < 0 ) && (env->size != cur_size))
- set_line_thickness(req_linethickness,env);
- switch (code) {
- case 't':
- if (np == 0)
- line_thickness = 1;
- else { // troff gratuitously adds an extra 0
- if (np != 1 && np != 2) {
- error("0 or 1 argument required for thickness");
- break;
- }
- set_line_thickness(p[0],env);
- }
- break;
- case 'l': // Line
- if (np != 2) {
- error("2 arguments required for line");
- break;
- }
- if (!vdminited())
- vdmstart();
- vdmline(env->hpos, env->vpos, p[0], p[1]);
- /* fprintf(stderr, "\nline: %d,%d - %d,%d thickness %d == %d\n",
- env->hpos - 64,env->vpos -64, env->hpos - 64 + p[0],
- env->vpos -64 + p[1], env->size, line_thickness);*/
- break;
- case 'R': // Rule
- if (np != 2) {
- error("2 arguments required for Rule");
- break;
- }
- if (vdminited()) {
- setfillmode(fill_pattern); // Solid Rule
- vdmrectangle(env->hpos, env->vpos, p[0], p[1]);
- }
- else {
- lbpruleabs(env->hpos - 64, env->vpos -64, p[0], p[1]);
- cur_vpos = p[1];
- cur_hpos = p[0];
- }
- // fprintf(stderr, "\nrule: thickness %d == %d\n",
- // env->size, line_thickness);
- break;
- case 'P': // Filled Polygon
- if (!vdminited())
- vdmstart();
- setfillmode(fill_pattern);
- polygon(env->hpos, env->vpos, np, p);
- break;
- case 'p': // Empty Polygon
- if (!vdminited())
- vdmstart();
- setfillmode(0);
- polygon(env->hpos, env->vpos, np, p);
- break;
- case 'C': // Filled Circle
- if (!vdminited())
- vdmstart();
- // fprintf(stderr, "Circle (%d,%d) Fill %d\n",
- // env->hpos, env->vpos, fill_pattern);
- setfillmode(fill_pattern);
- vdmcircle(env->hpos + (p[0]/2), env->vpos, p[0]/2);
- break;
- case 'c': // Empty Circle
- if (!vdminited())
- vdmstart();
- setfillmode(0);
- vdmcircle(env->hpos + (p[0]/2), env->vpos, p[0]/2);
- break;
- case 'E': // Filled Ellipse
- if (!vdminited())
- vdmstart();
- setfillmode(fill_pattern);
- vdmellipse(env->hpos + (p[0]/2), env->vpos, p[0]/2, p[1]/2, 0);
- break;
- case 'e': // Empty Ellipse
- if (!vdminited())
- vdmstart();
- setfillmode(0);
- vdmellipse(env->hpos + (p[0]/2), env->vpos, p[0]/2, p[1]/2, 0);
- break;
- case 'a': // Arc
- if (!vdminited())
- vdmstart();
- setfillmode(0);
- // VDM draws arcs clockwise and pic counterclockwise
- // We must compensate for that, exchanging the starting and
- // ending points
- vdmvarc(env->hpos + p[0], env->vpos+p[1],
- int(sqrt(double((p[0]*p[0]) + (p[1]*p[1])))),
- p[2], p[3],
- (-p[0]), (-p[1]), 1, 2);
- break;
- case '~': // Spline
- if (!vdminited())
- vdmstart();
- setfillmode(0);
- vdmspline(np/2, env->hpos, env->vpos, p);
- break;
- case 'f':
- if (np != 1 && np != 2) {
- error("1 argument required for fill");
- break;
- }
- // fprintf(stderr, "Fill %d\n", p[0]);
- if ((p[0] == 1) || (p[0] >= 1000)) { // Black
- fill_pattern = 1;
- break;
- }
- if (p[0] == 0) { // White
- fill_pattern = 0;
- break;
- }
- if ((p[0] > 1) && (p[0] < 1000))
- {
- if (p[0] >= 990) fill_pattern = -23;
- else if (p[0] >= 700) fill_pattern = -28;
- else if (p[0] >= 500) fill_pattern = -27;
- else if (p[0] >= 400) fill_pattern = -26;
- else if (p[0] >= 300) fill_pattern = -25;
- else if (p[0] >= 200) fill_pattern = -22;
- else if (p[0] >= 100) fill_pattern = -24;
- else fill_pattern = -21;
- }
- break;
- case 'F':
- // not implemented yet
- break;
- default:
- error("unrecognised drawing command `%1'", char(code));
- break;
- }
- return;
- }
- font *lbp_printer::make_font(const char *nm)
- {
- return lbp_font::load_lbp_font(nm);
- }
- printer *make_printer()
- {
- return new lbp_printer(user_papersize, user_paperwidth, user_paperlength);
- }
- static struct {
- const char *name;
- int code;
- } lbp_papersizes[] =
- {{ "A4", 14 },
- { "letter", 30 },
- { "legal", 32 },
- { "executive", 40 },
- };
- static int set_papersize(const char *paperformat)
- {
- unsigned int i;
- // First test for a standard (i.e. supported directly by the printer)
- // paper size
- for (i = 0 ; i < sizeof(lbp_papersizes) / sizeof(lbp_papersizes[0]); i++)
- {
- if (strcasecmp(lbp_papersizes[i].name,paperformat) == 0)
- return lbp_papersizes[i].code;
- }
- // Otherwise, we assume a custom paper size
- return 82;
- }
- static void handle_unknown_desc_command(const char *command, const char *arg,
- const char *filename, int lineno)
- {
- // orientation command
- if (strcasecmp(command, "orientation") == 0) {
- // We give priority to command line options
- if (orientation > 0)
- return;
- if (arg == 0)
- error_with_file_and_line(filename, lineno,
- "`orientation' command requires an argument");
- else {
- if (strcasecmp(arg, "portrait") == 0)
- orientation = 0;
- else {
- if (strcasecmp(arg, "landscape") == 0)
- orientation = 1;
- else
- error_with_file_and_line(filename, lineno,
- "invalid argument to `orientation' command");
- }
- }
- }
- }
- static struct option long_options[] = {
- { "orientation", required_argument, NULL, 'o' },
- { "version", no_argument, NULL, 'v' },
- { "copies", required_argument, NULL, 'c' },
- { "landscape", no_argument, NULL, 'l' },
- { "papersize", required_argument, NULL, 'p' },
- { "linewidth", required_argument, NULL, 'w' },
- { "fontdir", required_argument, NULL, 'F' },
- { "help", no_argument, NULL, 'h' },
- { NULL, 0, 0, 0 }
- };
- static void usage(FILE *stream)
- {
- fprintf(stream,
- "usage: %s [-lvh] [-c n] [-p paper_size] [-F dir] [-o or]\n"
- " [-w width] [files ...]\n"
- "\n"
- " -o --orientation=[portrait|landscape]\n"
- " -v --version\n"
- " -c --copies=numcopies\n"
- " -l --landscape\n"
- " -p --papersize=paper_size\n"
- " -w --linewidth=width\n"
- " -F --fontdir=dir\n"
- " -h --help\n",
- program_name);
- }
- int main(int argc, char **argv)
- {
- if (program_name == NULL)
- program_name = strsave(argv[0]);
- font::set_unknown_desc_command_handler(handle_unknown_desc_command);
- // command line parsing
- int c = 0;
- int option_index = 0;
- while (c >= 0) {
- c = getopt_long (argc, argv, "c:F:hI:lo:p:vw:",
- long_options, &option_index);
- switch (c) {
- case 'F':
- font::command_line_font_dir(optarg);
- break;
- case 'I':
- // ignore include path arguments
- break;
- case 'p':
- {
- const char *s;
- if (!font::scan_papersize(optarg, &s,
- &user_paperlength, &user_paperwidth))
- error("invalid paper size `%1' ignored", optarg);
- else
- user_papersize = set_papersize(s);
- break;
- }
- case 'l':
- orientation = 1;
- break;
- case 'v':
- printf("GNU grolbp (groff) version %s\n", Version_string);
- exit(0);
- break;
- case 'o':
- if (strcasecmp(optarg, "portrait") == 0)
- orientation = 0;
- else {
- if (strcasecmp(optarg, "landscape") == 0)
- orientation = 1;
- else
- error("unknown orientation '%1'", optarg);
- }
- break;
- case 'c':
- {
- char *ptr;
- long n = strtol(optarg, &ptr, 10);
- if ((n <= 0) && (ptr == optarg))
- error("argument for -c must be a positive integer");
- else if (n <= 0 || n > 32767)
- error("out of range argument for -c");
- else
- ncopies = unsigned(n);
- break;
- }
- case 'w':
- {
- char *ptr;
- long n = strtol(optarg, &ptr, 10);
- if (n == 0 && ptr == optarg)
- error("argument for -w must be a non-negative integer");
- else if (n < 0 || n > INT_MAX)
- error("out of range argument for -w");
- else
- linewidth_factor = int(n);
- break;
- }
- case 'h':
- usage(stdout);
- exit(0);
- break;
- case '?':
- usage(stderr);
- exit(1);
- break;
- }
- }
- if (optind >= argc)
- do_file("-");
- while (optind < argc)
- do_file(argv[optind++]);
- lbpputs("\033c\033<");
- return 0;
- }