diff options
Diffstat (limited to 'src/devices/grolbp/lbp.cpp')
-rw-r--r-- | src/devices/grolbp/lbp.cpp | 738 |
1 files changed, 738 insertions, 0 deletions
diff --git a/src/devices/grolbp/lbp.cpp b/src/devices/grolbp/lbp.cpp new file mode 100644 index 0000000..35cd54e --- /dev/null +++ b/src/devices/grolbp/lbp.cpp @@ -0,0 +1,738 @@ +/* Copyright (C) 1994-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* +TODO + + - Add X command to include bitmaps +*/ + +#include <assert.h> + +#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; +static int orientation = -1; + +// custom paper format +static double user_paperlength = 0; +static double user_paperwidth = 0; + +static int ncopies = 1; + +#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(glyph *, 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 + // custom paper format + int papersize; + int paperlength; + 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 format handling + if (orientation < 0) + orientation = 0; // Default orientation is portrait + papersize = 14; // Default paper format 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(glyph *g, font *f, const environment *env, + int w, const char *) +{ + int code = f->get_code(g); + 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 + // To get black, we must use white inverted. + vdmsetfillmode(mode, 1, 1); + + 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, "Polygon (%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 format. + 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 format. + return 82; // XXX: magic number +} + +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 [-l] [-c num-copies] [-F font-directory] [-o orientation]" +" [-p paper-format] [-w width] [file ...]\n" +"usage: %s {-v | --version}\n" +"usage: %s {-h | --help}\n", + program_name, program_name, program_name); + if (stdout == stream) { + fputs( +"\n" +"Translate the output of troff(1) into a CaPSL and VDM format suitable" +"\n" +"for Canon LBP-4 and LBP-8 printers. See the grolbp(1) manual page.\n", + stream); + exit(EXIT_SUCCESS); + } +} + +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; + int option_index = 0; + while ((c = getopt_long(argc, argv, "c:F:hI:lo:p:vw:", long_options, + &option_index)) + != EOF) { + 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("ignoring invalid paper format '%1'", 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(EXIT_SUCCESS); + 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); + break; + case '?': + usage(stderr); + exit(EXIT_FAILURE); + break; + default: + assert(0 == "unhandled getopt_long return value"); + } + } + if (optind >= argc) + do_file("-"); + while (optind < argc) + do_file(argv[optind++]); + if (lbpoutput) + lbpputs("\033c\033<"); + return 0; +} + +// Local Variables: +// fill-column: 72 +// mode: C++ +// End: +// vim: set cindent noexpandtab shiftwidth=2 textwidth=72: |