2000-12-05 18:49:44 +00:00

773 lines
20 KiB
C++

// -*- C++ -*-
/* Copyright (C) 1994 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
/*
TODO
- Add X command to include bitmaps
*/
#define _GNU_SOURCE
#include "driver.h"
#include "lbp.h"
#include "charset.h"
#include "nonposix.h"
#ifdef HAVE_STRNCASECMP
#ifdef NEED_DECLARATION_STRNCASECMP
extern "C" {
// SunOS's string.h fails to declare this.
int strncasecmp(const char *, const char *, int);
}
#endif /* NEED_DECLARATION_STRNCASECMP */
#endif /* HAVE_STRNCASECMP */
static short int papersize = -1, // papersize
orientation = -1 , // orientation
paperlength = 0, // Custom Paper size
paperwidth = 0,
ncopies = 1; // Number of copies
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();
~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, int dot = 0);
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;
};
// Compatibility section.
//
// Here we define some functions not present in some of the targets
// platforms
#ifndef HAVE_STRSEP
// Solaris 8 doesn't have the strsep function
static char *strsep(char **pcadena, const char *delim)
{
char *p;
p = strtok(*pcadena,delim);
*pcadena = strtok(NULL,delim);
return p;
};
#endif
#ifndef HAVE_STRDUP
// Ditto with OS/390 and strdup
static char *strdup(const char *s)
{
char *result;
result = (char *)malloc(strlen(s)+1);
if (result != NULL) strcpy(result,s);
return result;
}; // strdup
#endif
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 Recongnize bitmaped fonts by the first character of it's name
if (arg[0] == 'N') this->is_scalable = 0;
// fprintf(stderr,"Loading font \"%s\" \n",arg);
}; // if (strcmp(command, "lbpname")
// fprintf(stderr,"Loading font %s \"%s\" in %s at %d\n",command,arg,filename,lineno);
};
static void wp54charset()
{
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()
: fill_pattern(1),
fill_mode(0),
cur_hpos(-1),
cur_font(0),
cur_size(0),
cur_symbol_set(0),
line_thickness(-1)
{
#ifdef SET_BINARY
SET_BINARY(fileno(stdout));
#endif
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
if (papersize < 0) papersize = 14; // Default paper size is A4
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<");
}
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.
// nam[strlen(f->lbpname)-2]; // The font name without other data.
int cpi; // The font size in characters per inch
// (Bitmaped 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 avaiable 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 were only two bitmaped fonts shiped with the printer.
if (!strcasecmp(nam,"courier"))
{ // Courier font
if (siz >= 12) cpi = 10;
else cpi = 17;
};
if (!strcasecmp(nam,"elite"))
{ // Elite font
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;
}; // lbp_printer::font_name
void lbp_printer::set_char(int index, font *f, const environment *env, int w, const char *name)
{
int code = f->get_code(index);
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*300)/72));
} else
{ // Bitmaped font
lbpprintf("\033Pz%s.IBML\033\\\n",font_name(psf,env->size));
};
lbpputs("\033)' 1"); // Select IBML and IBMR1 symbol set
cur_size = env->size;
cur_font = psf;
cur_symbol_set = 0;
}
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*300)/72));
}; // if ( cur_symbol_set == 3 )
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*300)/72));
lbpputs("\033(\"!!0\033)\"!!1"); // Select symbol font
break;
case 4: lbpputs("\033)\"! 1\033(\"!$2"); // Select PS symbol set
break;
}; // switch (symbol_set)
// if (symbol_set == 1) lbpputs("\033(d"); // Select wp54 symbol set
// else lbpputs("\033('$2\033)' 1"); // Select IBML and IBMR1 symbol sets
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*300)/72));
cur_size = env->size;
}
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("Openinig temp 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);
/* lets 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;
}; // lbp_printer::vdmflush
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;
};
}; // setfillmode
inline void
lbp_printer::polygon( int hpos,int vpos,int np,int *p)
{
//int points[np+2],i;
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)
{
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;
} // if (np != ...
if (p[0] == 0) line_thickness = 1;
if (p[0] < 0) // Default = 1 point
line_thickness = (int)(env->size*30/72);
line_thickness = (int)((abs(p[0])*env->size)/10);
if ((line_thickness > 16 ) && (!vdminited()))
{ /* for greater thickness we must use VDM */
vdmstart();
/* vdmlinewidth(line_thickness); already done in
* vdmstart() */
};
if (vdminited()) vdmlinewidth(line_thickness);
// fprintf(stderr,"\nthickness: %d == %d, size %d\n",
// p[0],line_thickness,env->size );
} // else
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] == 1)
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;
}; // if (p[0] >= 0 && p[0] <= 1000)
break;
default:
error("unrecognised drawing command `%1'", char(code));
break;
}; // switch (code)
return ;
};
font *lbp_printer::make_font(const char *nm)
{
return lbp_font::load_lbp_font(nm);
}
printer *make_printer()
{
return new lbp_printer;
}
static struct
{
const char *name;
int code;
} papersizes[] =
{{ "A4", 14 },
{ "letter", 30 },
{ "legal", 32 },
{ "executive", 40 },
};
static int set_papersize(const char *papersize)
{
int i;
// First test for a standard (i.e. supported directly by the printer)
// papersize
for (i = 0 ; i < sizeof(papersizes)/sizeof(papersizes[0]); i++)
{
if (strcasecmp(papersizes[i].name,papersize) == 0)
return papersizes[i].code;
};
// Now test for a custom papersize
if (strncasecmp("cust",papersize,4) == 0)
{
char *p ,
*p1,
*papsize;
p = papsize = strdup(&papersize[4]);
if (papsize == NULL) return -1;
p1 = strsep(&p,"x");
if (p == NULL)
{ // let's test for an uppercase x
p = papsize ;
p1 = strsep(&p,"X");
if (p == NULL) { free(papsize); return -1;};
}; // if (p1 == NULL)
paperlength = atoi(p1);
if (paperlength == 0) { free(papsize); return -1;};
paperwidth = atoi(p);
if (paperwidth == 0) { free(papsize); return -1;};
free(papsize);
return 82;
}; // if (strcnasecmp("cust",papersize,4) == 0)
return -1;
};
static int handle_papersize_command(const char *arg)
{
int n = set_papersize(arg);
if (n < 0)
{ // If is not a standard nor custom paper size
// let's see if it's a file (i.e /etc/papersize )
FILE *f = fopen(arg,"r");
if (f != NULL)
{ // the file exists and is readable
char psize[255],*p;
fgets(psize,254,f);
fclose(f);
// set_papersize doesn't like the trailing \n
p = psize; while (*p) p++;
if (*(--p) == '\n') *p = 0x00;
n = set_papersize(psize);
}; // if (f != NULL)
}; // if (n < 0)
return n;
}; // handle_papersize_command
static void handle_unknown_desc_command(const char *command, const char *arg,
const char *filename, int lineno)
{
// papersize command
if (strcasecmp(command, "papersize") == 0) {
// We give priority to command line options
if (papersize > 0) return;
if (arg == 0)
error_with_file_and_line(filename, lineno,
"`papersize' command requires an argument");
else
{
int n = handle_papersize_command(arg);
if (n < 0)
error_with_file_and_line(filename, lineno,
"unknown paper size `%1'", arg);
else
papersize = n;
}; // if (arg == 0) ... else ...
}; // if (strcasecmp(command, "papersize")
// 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,
"`papersize' 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,
"`orientation' command requires an argument");
};
}; // if (arg == 0) ... else ...
}; // if (strcasecmp(command, "orientation") == 0)
};
static struct option long_options[] = {
{"orientation",1,NULL,'o'},
{"version",0,NULL,'v'},
{"copies",1,NULL,'c'},
{"landscape",0,NULL,'l'},
{"papersize",1,NULL,'p'},
{"fontdir",1,NULL,'F'},
{"help",0,NULL,'h'},
{0, 0, 0, 0}
};
static void usage()
{
fprintf(stderr,
"usage: %s [-lvh] [-c n] [-p paper_size] [-F dir] [-o or] "\
" [files ...]\n"\
" -o --orientation=[portrait|landscape]\n"\
" -v --version\n"\
" -c --copies=numcopies\n"\
" -l --landscape\n"\
" -p --papersize=paper_size\n"\
" -F --fontdir=dir\n"\
" -h --help\n",
program_name);
exit(1);
}; // usage
int main(int argc, char **argv)
{
if (program_name == NULL) program_name = strdup(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, "F:p:lvo:c:h",\
long_options, &option_index);
switch (c) {
case 'F' : font::command_line_font_dir(optarg);
break;
case 'p' : {
int n = handle_papersize_command(optarg);
if (n < 0)
error("unknown paper size `%1'", optarg);
else
papersize = n;
break;
};
case 'l' : orientation = 1;
break;
case 'v' : {
extern const char *version_string;
fprintf(stderr, "grolbp version %s\n",\
version_string);
fflush(stderr);
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 'h' : usage();
break;
}; // switch (c)
}; // while (c > 0 )
if (optind >= argc)
do_file("-");
while (optind < argc) {
do_file(argv[optind++]);
};
lbpputs("\033c\033<");
return 0;
};