diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 19:44:05 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 19:44:05 +0000 |
commit | d318611dd6f23fcfedd50e9b9e24620b102ba96a (patch) | |
tree | 8b9eef82ca40fdd5a8deeabf07572074c236095d /src/devices/grolj4 | |
parent | Initial commit. (diff) | |
download | groff-d318611dd6f23fcfedd50e9b9e24620b102ba96a.tar.xz groff-d318611dd6f23fcfedd50e9b9e24620b102ba96a.zip |
Adding upstream version 1.23.0.upstream/1.23.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/devices/grolj4')
-rw-r--r-- | src/devices/grolj4/grolj4.1.man | 896 | ||||
-rw-r--r-- | src/devices/grolj4/grolj4.am | 32 | ||||
-rw-r--r-- | src/devices/grolj4/lj4.cpp | 715 |
3 files changed, 1643 insertions, 0 deletions
diff --git a/src/devices/grolj4/grolj4.1.man b/src/devices/grolj4/grolj4.1.man new file mode 100644 index 0000000..6227779 --- /dev/null +++ b/src/devices/grolj4/grolj4.1.man @@ -0,0 +1,896 @@ +.TH grolj4 @MAN1EXT@ "@MDATE@" "groff @VERSION@" +.SH Name +grolj4 \- +.I groff +output driver for HP LaserJet 4 and compatible printers +. +. +.\" ==================================================================== +.\" Legal Terms +.\" ==================================================================== +.\" +.\" Copyright (C) 1994-2020, 2022 Free Software Foundation, Inc. +.\" +.\" Permission is granted to make and distribute verbatim copies of this +.\" manual provided the copyright notice and this permission notice are +.\" preserved on all copies. +.\" +.\" Permission is granted to copy and distribute modified versions of +.\" this manual under the conditions for verbatim copying, provided that +.\" the entire resulting derived work is distributed under the terms of +.\" a permission notice identical to this one. +.\" +.\" Permission is granted to copy and distribute translations of this +.\" manual into another language, under the above conditions for +.\" modified versions, except that this permission notice may be +.\" included in translations approved by the Free Software Foundation +.\" instead of in the original English. +. +. +.\" Save and disable compatibility mode (for, e.g., Solaris 10/11). +.do nr *groff_grolj4_1_man_C \n[.cp] +.cp 0 +. +.\" Define fallback for groff 1.23's MR macro if the system lacks it. +.nr do-fallback 0 +.if !\n(.f .nr do-fallback 1 \" mandoc +.if \n(.g .if !d MR .nr do-fallback 1 \" older groff +.if !\n(.g .nr do-fallback 1 \" non-groff *roff +.if \n[do-fallback] \{\ +. de MR +. ie \\n(.$=1 \ +. I \%\\$1 +. el \ +. IR \%\\$1 (\\$2)\\$3 +. . +.\} +.rr do-fallback +. +.\" This macro definition is poor style from a portability standpoint, +.\" but it's a good test and demonstration of the standard font +.\" repertoire for the devices where it has any effect at all, and so +.\" should be retained. +.de FT +. if '\\*(.T'lj4' .ft \\$1 +.. +. +. +.\" ==================================================================== +.SH Synopsis +.\" ==================================================================== +. +.SY grolj4 +.RB [ \-l ] +.RB [ \-c\~\c +.IR num-copies ] +.RB [ \-d +.RI [ n ]] +.RB [ \-F\~\c +.IR font-directory ] +.RB [ \-p\~\c +.IR paper-format ] +.RB [ \-w\~\c +.IR line-width ] +.RI [ file\~ .\|.\|.] +.YS +. +. +.SY grolj4 +.B \-\-help +.YS +. +. +.SY grolj4 +.B \-v +. +.SY grolj4 +.B \-\-version +.YS +. +. +.\" ==================================================================== +.SH Description +.\" ==================================================================== +. +This GNU +.I roff +output driver translates the output of +.MR @g@troff @MAN1EXT@ +into a PCL5 format suitable for an HP LaserJet 4 printer. +. +Normally, +.I grolj4 +is invoked by +.MR groff @MAN1EXT@ +when the latter is given the +.RB \[lq] \-T\~lj4 \[rq] +option. +. +(In this installation, +.B @DEVICE@ +is the default output device.) +. +Use +.IR groff 's +.B \-P +option to pass any options shown above to +.IR grolj4 . +. +If no +.I file +arguments are given, +or if +.I file +is \[lq]\-\[rq], +.I grolj4 +reads the standard input stream. +. +Output is written to the standard output stream. +. +. +.\" ==================================================================== +.SS Typefaces +.\" ==================================================================== +. +.I grolj4 +supports the standard four styles: +.B R +(roman), +.B I +.RI ( italic ), +.B B +.RB ( bold ), +and +.B BI +(\f[BI]bold-italic\f[]). +. +Fonts are grouped into families +.BR A , +.BR C , +.BR G , +.BR O , +.BR T , +.BR TN , +.BR U , +and +.B UC +having members in each style. +. +. +.RS +.TP 14n +.B AB +.FT AB +Arial Bold +.FT +. +.TQ +.B ABI +.FT ABI +Arial Bold Italic +.FT +. +.TQ +.B AI +.FT AI +Arial Italic +.FT +. +.TQ +.B AR +.FT AR +Arial Roman +.FT +. +.TQ +.B CB +.FT CB +Courier Bold +.FT +. +.TQ +.B CBI +.FT CBI +Courier Bold Italic +.FT +. +.TQ +.B CI +.FT CI +Courier Italic +.FT +. +.TQ +.B CR +.FT CR +Courier Roman +.FT +. +.TQ +.B GB +.FT GB +Garamond Halbfett +.FT +. +.TQ +.B GBI +.FT GBI +Garamond Kursiv Halbfett +.FT +. +.TQ +.B GI +.FT GI +Garamond Kursiv +.FT +. +.TQ +.B GR +.FT GR +Garamond Antiqua +.FT +. +.TQ +.B OB +.FT OB +CG Omega Bold +.FT +. +.TQ +.B OBI +.FT OBI +CG Omega Bold Italic +.FT +. +.TQ +.B OI +.FT OI +CG Omega Italic +.FT +. +.TQ +.B OR +.FT OR +CG Omega Roman +. +.TQ +.B OB +.FT OB +CG Omega Bold +.FT +. +.TQ +.B OBI +.FT OBI +CG Omega Bold Italic +.FT +. +.TQ +.B OI +.FT OI +CG Omega Italic +.FT +. +.TQ +.B OR +.FT OR +CG Omega Roman +.FT +. +.TQ +.B TB +.FT TB +CG Times Bold +.FT +. +.TQ +.B TBI +.FT TBI +CG Times Bold Italic +.FT +. +.TQ +.B TI +.FT TI +CG Times Italic +.FT +. +.TQ +.B TR +.FT TR +CG Times Roman +.FT +. +.TQ +.B TNRB +.FT TNRB +M Times Bold +.FT +. +.TQ +.B TNRBI +.FT TNRBI +M Times Bold Italic +.FT +. +.TQ +.B TNRI +.FT TNRI +M Times Italic +.FT +. +.TQ +.B TNRR +.FT TNRR +M Times Roman +.FT +. +.TQ +.B UB +.FT UB +Univers Bold +.FT +. +.TQ +.B UBI +.FT UBI +Univers Bold Italic +.FT +. +.TQ +.B UI +.FT UI +Univers Medium Italic +.FT +. +.TQ +.B UR +.FT UR +Univers Medium +.FT +. +.TQ +.B UCB +.FT UCB +Univers Condensed Bold +.FT +. +.TQ +.B UCBI +.FT UCBI +Univers Condensed Bold Italic +.FT +. +.TQ +.B UCI +.FT UCI +Univers Condensed Medium Italic +.FT +. +.TQ +.B UCR +.FT UCR +Univers Condensed Medium +.FT +.RE +. +. +.P +The following fonts are not members of a family. +. +. +.RS +.TP 14n +.B ALBB +.FT ALBB +Albertus Extra Bold +.FT +. +.TQ +.B ALBR +.FT ALBR +Albertus Medium +.FT +. +.TQ +.B AOB +.FT AOB +Antique Olive Bold +. +.TQ +.B AOI +.FT AOI +Antique Olive Italic +. +.TQ +.B AOR +.FT AOR +Antique Olive Roman +. +.TQ +.B CLARENDON +.FT CLARENDON +Clarendon +. +.TQ +.B CORONET +.FT CORONET +Coronet +. +.TQ +.B LGB +.FT LGB +Letter Gothic Bold +. +.TQ +.B LGI +.FT LGI +Letter Gothic Italic +. +.TQ +.B LGR +.FT LGR +Letter Gothic Roman +. +.TQ +.B MARIGOLD +.FT MARIGOLD +Marigold +.RE +. +. +.P +The special font is +.B S +(PostScript Symbol); +.B SYMBOL +(M Symbol), +and +.B WINGDINGS +(Wingdings) +are also available but not mounted by default. +. +. +.\" ==================================================================== +.SS "Paper format and device description file" +.\" ==================================================================== +. +.I grolj4 +supports paper formats +.RB \[lq] A4 \[rq], +.RB \[lq] B5 \[rq], +.RB \[lq] C5 \[rq], +.RB \[lq] com10 \[rq], +.RB \[lq] DL \[rq], +.RB \%\[lq] executive \[rq], +.RB \%\[lq] legal \[rq], +.RB \%\[lq] letter \[rq], +and +.RB \[lq] monarch \[rq]. +. +These are matched case-insensitively. +. +The +.B \-p +option overrides any setting in the device description file +.IR DESC . +. +If neither specifies a paper format, +\[lq]letter\[rq] is assumed. +. +. +.\" ==================================================================== +.SS "Font description files" +.\" ==================================================================== +. +.I grolj4 +recognizes four font description file directives in addition to those +documented in +.MR groff_font @MAN5EXT@ . +. +. +.TP +.BI pclweight\~ n +Set the stroke weight to +.IR n , +an integer in the range \-7 to +7; +the default is\~0. +. +. +.TP +.BI pclstyle\~ n +Set the style to +.IR n , +an integer in the range 0 to 32767; +the default is\~0. +. +. +.TP +.BI pclproportional\~ n +Set the proportional spacing Boolean flag to +.IR n , +which can be either 0 or\~1; +the default is\~0. +. +. +.TP +.BI pcltypeface\~ n +Set the typeface family to +.IR n , +an integer in the range 0 to 65535; +the default is\~0. +. +. +.\" ==================================================================== +.SS "Drawing commands" +.\" ==================================================================== +. +An additional drawing command is recognized as an extension to those +documented in +.MR groff @MAN7EXT@ . +. +. +.TP +.BI \[rs]D\[aq]R\~ "dh dv" \[aq] +Draw a rule +(solid black rectangle) +with one corner at the drawing position, +and the diagonally opposite corner at the drawing position +.RI +( dh , dv ), +at which the drawing position will be afterward. +. +This generates a PCL fill rectangle command, +and so will work on printers that do not support HP-GL/2, +unlike the other +.B \[rs]D +commands. +. +. +.\" ==================================================================== +.SS Fonts +.\" ==================================================================== +. +Nominally, +all Hewlett-Packard LaserJet\~\%4-series and newer printers have the +same internal fonts: +45 scalable fonts and one bitmapped Lineprinter font. +. +The scalable fonts are available in sizes between 0.25 points and 999.75 +points, +in 0.25-point increments; +the Lineprinter font is available only in 8.5-point size. +. +. +.P +The LaserJet font files included with +.I groff +assume that all printers since the LaserJet\~4 are identical. +. +There are some differences between fonts in the earlier and more recent +printers, +however. +. +The LaserJet\~4 printer used Agfa Intellifont technology for 35 of the +internal scalable fonts; +the remaining 10 scalable fonts were TrueType. +. +Beginning with the LaserJet\~\%4000-series printers introduced in 1997, +all scalable internal fonts have been TrueType. +. +The number of printable glyphs differs slightly between Intellifont and +TrueType fonts +(generally, +the TrueType fonts include more glyphs), +and +there are some minor differences in glyph metrics. +. +Differences among printer models are described in the +.I "PCL\~5 Comparison Guide" +and the +.I "PCL\~5 Comparison Guide Addendum" +(for printers introduced since approximately 2001). +. +. +.P +LaserJet printers reference a glyph by a combination of a 256-glyph +symbol set and an index within that symbol set. +. +Many glyphs appear in more than one symbol set; +all combinations of symbol set and index that reference the same glyph +are equivalent. +. +For each glyph, +.MR hpftodit @MAN1EXT@ +searches a list of symbol sets, +and selects the first set that contains the glyph. +. +The printing code generated by +.I hpftodit +is an integer that encodes a numerical value for the symbol set in the +high byte(s), +and the index in the low byte. +. +See +.MR groff_font @MAN5EXT@ +for a complete description of the font file format; +symbol sets are described in greater detail in the +.IR "PCL\~5 Printer Language Technical Reference Manual" . +. +. +.P +Two of the scalable fonts, +Symbol and Wingdings, +are bound to 256-glyph symbol sets; +the remaining scalable fonts, +as well as the Lineprinter font, +support numerous symbol sets, +sufficient to enable printing of more than 600 glyphs. +. +. +.P +The metrics generated by +.I hpftodit +assume that the +.I DESC +file contains values of 1200 for +.I res +and 6350 for +.IR unitwidth , +or any combination +(e.g., +2400 and 3175) +for which +.IR res \~\[tmu]\~ unitwidth \~=\~7\|620\|000. +. +Although HP PCL\~5 LaserJet printers support an internal resolution of +7200 units per inch, +they use a 16-bit signed integer for positioning; +if +.B devlj4 +is to support U.S.\& ledger paper (11\~in\~\[mu]\~17\~in; +in = inch), +the maximum usable resolution is 32\|767\~\[di]\~17, +or 1927 units per inch, +which rounds down to 1200 units per inch. +. +If the largest required paper dimension is less +(e.g., +8.5\~in\~\[mu]\~11\~in, +or A5), +a greater +.I res +(and lesser +.IR unitwidth ) +can be specified. +. +. +.P +Font metrics for Intellifont fonts were provided by Tagged Font Metric +(TFM) files originally developed by Agfa/Compugraphic. +. +The TFM files provided for these fonts supported 600+ glyphs and +contained extensive lists of kerning pairs. +. +. +.P +To accommodate developers who had become accustomed to TFM files, +HP also provided TFM files for the 10 TrueType fonts included in the +LaserJet\~4. +. +The TFM files for TrueType fonts generally included less information +than the Intellifont TFMs, +supporting fewer glyphs, +and in most cases, +providing no kerning information. +. +By the time the LaserJet\~4000 printer was introduced, +most developers had migrated to other means of obtaining font metrics, +and support for new TFM files was very limited. +. +The TFM files provided for the TrueType fonts in the LaserJet\~4000 +support only the Latin 2 (ISO 8859-2) symbol set, +and include no kerning information; +consequently, +they are of little value for any but the most rudimentary documents. +. +. +.P +Because the Intellifont TFM files contain considerably more information, +they generally are preferable to the TrueType TFM files even for use +with the TrueType fonts in the newer printers. +. +The metrics for the TrueType fonts are very close, +though not identical, +to those for the earlier Intellifont fonts of the same names. +. +Although most output using the Intellifont metrics with the newer +printers is quite acceptable, +a few glyphs may fail to print as expected. +. +The differences in glyph metrics may be particularly noticeable with +composite parentheses, +brackets, +and braces used by +.MR eqn @MAN1EXT@ . +. +A script, +located in +.IR @FONTDIR@/\:\%devlj4/\:generate , +can be used to adjust the metrics for these glyphs in the special font +\[lq]S\[rq] for use with printers that have all TrueType fonts. +. +. +.P +At the time HP last supported TFM files, +only version 1.0 of the Unicode standard was available. +. +Consequently, +many glyphs lacking assigned code points were assigned by HP to the +Private Use Area (PUA). +. +Later versions of the Unicode standard included code points outside the +PUA for many of these glyphs. +. +The HP-supplied TrueType TFM files use the PUA assignments; +TFM files generated from more recent TrueType font files require the +later Unicode values to access the same glyphs. +. +Consequently, +two different mapping files may be required: +one for the HP-supplied TFM files, +and one for more recent TFM files. +. +. +.\" ==================================================================== +.SH Options +.\" ==================================================================== +. +.B \-\-help +displays a usage message, +while +.B \-v +and +.B \-\-version +show version information; +all exit afterward. +. +. +.TP +.BI \-c\~ num-copies +Format +.I num-copies +copies of each page. +. +. +.TP +.BR \-d \~[\c +.IR n ] +Use duplex mode +.IR n : +1\~is long-side binding (default), +and 2\~is short-side binding. +. +. +.TP +.BI \-F " font-directory" +Prepend directory +.IR font-directory /dev name +to the search path for font and device description files; +.I name +is the name of the device, +usually +.BR lj4 . +. +. +.TP +.B \-l +Format the document in landscape orientation. +. +. +.TP +.BI \-p " paper-format" +Set the paper format to +.IR paper-format , +which must be a valid paper format as described above. +. +. +.TP +.BI \-w " line-width" +Set the default line thickness to +.I line-width +thousandths of an em; +the default is +.B 40 +(0.04\~em). +. +. +.br +.ne 4v \" Keep section heading and paragraph together. +.\" ==================================================================== +.SH Environment +.\" ==================================================================== +. +.TP +.I GROFF_FONT_PATH +lists directories in which to seek the selected output device's +directory of device and font description files. +. +See +.MR @g@troff @MAN1EXT@ +and +.MR groff_font @MAN5EXT@ . +. +. +.\" ==================================================================== +.SH Files +.\" ==================================================================== +. +.TP +.I @FONTDIR@/\:\%devlj4/\:DESC +describes the +.B lj4 +output device. +. +. +.TP +.IR @FONTDIR@/\:\%devlj4/ F +describes the font known +.RI as\~ F +on device +.BR lj4 . +. +. +.TP +.I @MACRODIR@/\:lj4\:.tmac +defines macros for use with the +.B lj4 +output device. +. +It is automatically loaded by +.I troffrc +when the +.B lj4 +output device is selected. +. +. +.\" ==================================================================== +.SH Bugs +.\" ==================================================================== +. +.\" XXX: What does this mean? The period/full stop glyph? Flyspecks? +Small dots. +. +. +.\" ==================================================================== +.SH "See also" +.\" ==================================================================== +. +.UR http://\:www\:.hp\:.com/\:ctg/\:Manual/\:bpl13210\:.pdf +.I HP PCL/PJL Reference: +.I PCL\~5 Printer Language Technical Reference Manual, +.I Part I +.UE +. +. +.P +.MR hpftodit @MAN1EXT@ , +.MR groff @MAN1EXT@ , +.MR @g@troff @MAN1EXT@ , +.MR groff_out @MAN5EXT@ , +.MR groff_font @MAN5EXT@ , +.MR groff_char @MAN7EXT@ +. +. +.\" Clean up. +.rm FT +. +.\" Restore compatibility mode (for, e.g., Solaris 10/11). +.cp \n[*groff_grolj4_1_man_C] +.do rr *groff_grolj4_1_man_C +. +. +.\" Local Variables: +.\" fill-column: 72 +.\" mode: nroff +.\" End: +.\" vim: set filetype=groff textwidth=72: diff --git a/src/devices/grolj4/grolj4.am b/src/devices/grolj4/grolj4.am new file mode 100644 index 0000000..37138b2 --- /dev/null +++ b/src/devices/grolj4/grolj4.am @@ -0,0 +1,32 @@ +# Copyright (C) 2014-2020 Free Software Foundation, Inc. +# +# 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/>. + +bin_PROGRAMS += grolj4 +grolj4_SOURCES = src/devices/grolj4/lj4.cpp +grolj4_LDADD = $(LIBM) \ + libdriver.a \ + libgroff.a \ + lib/libgnu.a +man1_MANS += src/devices/grolj4/grolj4.1 +EXTRA_DIST += \ + src/devices/grolj4/grolj4.1.man + +# Local Variables: +# fill-column: 72 +# mode: makefile-automake +# End: +# vim: set autoindent filetype=automake textwidth=72: diff --git a/src/devices/grolj4/lj4.cpp b/src/devices/grolj4/lj4.cpp new file mode 100644 index 0000000..a429a7d --- /dev/null +++ b/src/devices/grolj4/lj4.cpp @@ -0,0 +1,715 @@ +/* Copyright (C) 1994-2020 Free Software Foundation, Inc. + Written by James Clark (jjc@jclark.com) + +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 + +option to use beziers for circle/ellipse/arc +option to use lines for spline (for LJ3) +left/top offset registration +output bin selection option +paper source option +output non-integer parameters using fixed point numbers +X command to insert contents of file +X command to specify inline escape sequence (how to specify unprintable chars?) +X command to include bitmap graphics +*/ + +#include <assert.h> + +#include "driver.h" +#include "nonposix.h" + +extern "C" const char *Version_string; + +static struct { + const char *name; + int code; + // at 300dpi + int x_offset_portrait; + int x_offset_landscape; +} paper_table[] = { + { "letter", 2, 75, 60 }, + { "legal", 3, 75, 60 }, + { "executive", 1, 75, 60 }, + { "a4", 26, 71, 59 }, + { "com10", 81, 75, 60 }, + { "monarch", 80, 75, 60 }, + { "c5", 91, 71, 59 }, + { "b5", 100, 71, 59 }, + { "dl", 90, 71, 59 }, +}; + +static int user_paper_size = -1; +static int landscape_flag = 0; +static int duplex_flag = 0; + +// An upper limit on the paper dimensions in centipoints, +// used for setting HPGL picture frame. +#define MAX_PAPER_WIDTH (12*720) +#define MAX_PAPER_HEIGHT (17*720) + +// Dotted lines that are thinner than this don't work right. +#define MIN_DOT_PEN_WIDTH .351 + +#ifndef DEFAULT_LINE_WIDTH_FACTOR +// in ems/1000 +#define DEFAULT_LINE_WIDTH_FACTOR 40 +#endif + +const int DEFAULT_HPGL_UNITS = 1016; +int line_width_factor = DEFAULT_LINE_WIDTH_FACTOR; +unsigned ncopies = 0; // 0 means don't send ncopies command + +static int lookup_paper_size(const char *); + +class lj4_font : public font { +public: + ~lj4_font(); + void handle_unknown_font_command(const char *command, const char *arg, + const char *filename, int lineno); + static lj4_font *load_lj4_font(const char *); + int weight; + int style; + int proportional; + int typeface; +private: + lj4_font(const char *); +}; + +lj4_font::lj4_font(const char *nm) +: font(nm), weight(0), style(0), proportional(0), typeface(0) +{ +} + +lj4_font::~lj4_font() +{ +} + +lj4_font *lj4_font::load_lj4_font(const char *s) +{ + lj4_font *f = new lj4_font(s); + if (!f->load()) { + delete f; + return 0; + } + return f; +} + +static struct { + const char *s; + int lj4_font::*ptr; + int min; + int max; +} command_table[] = { + { "pclweight", &lj4_font::weight, -7, 7 }, + { "pclstyle", &lj4_font::style, 0, 32767 }, + { "pclproportional", &lj4_font::proportional, 0, 1 }, + { "pcltypeface", &lj4_font::typeface, 0, 65535 }, +}; + +void lj4_font::handle_unknown_font_command(const char *command, + const char *arg, + const char *filename, int lineno) +{ + for (unsigned int i = 0; + i < sizeof(command_table)/sizeof(command_table[0]); i++) { + if (strcmp(command, command_table[i].s) == 0) { + if (arg == 0) + fatal_with_file_and_line(filename, lineno, + "'%1' command requires an argument", + command); + char *ptr; + long n = strtol(arg, &ptr, 10); + if (n == 0 && ptr == arg) + fatal_with_file_and_line(filename, lineno, + "'%1' command requires numeric argument", + command); + if (n < command_table[i].min) { + error_with_file_and_line(filename, lineno, + "argument for '%1' command must not be less than %2", + command, command_table[i].min); + n = command_table[i].min; + } + else if (n > command_table[i].max) { + error_with_file_and_line(filename, lineno, + "argument for '%1' command must not be greater than %2", + command, command_table[i].max); + n = command_table[i].max; + } + this->*command_table[i].ptr = int(n); + break; + } + } +} + +class lj4_printer : public printer { +public: + lj4_printer(int); + ~lj4_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, int dot = 0); + void hpgl_init(); + void hpgl_start(); + void hpgl_end(); + int moveto(int hpos, int vpos); + int moveto1(int hpos, int vpos); + + int cur_hpos; + int cur_vpos; + lj4_font *cur_font; + int cur_size; + unsigned short cur_symbol_set; + int x_offset; + int line_thickness; + double pen_width; + double hpgl_scale; + int hpgl_inited; + int paper_size; +}; + +inline +int lj4_printer::moveto(int hpos, int vpos) +{ + if (cur_hpos != hpos || cur_vpos != vpos || cur_hpos < 0) + return moveto1(hpos, vpos); + else + return 1; +} + +inline +void lj4_printer::hpgl_start() +{ + fputs("\033%1B", stdout); +} + +inline +void lj4_printer::hpgl_end() +{ + fputs(";\033%0A", stdout); +} + +lj4_printer::lj4_printer(int ps) +: cur_hpos(-1), + cur_font(0), + cur_size(0), + cur_symbol_set(0), + line_thickness(-1), + pen_width(-1.0), + hpgl_inited(0) +{ + if (7200 % font::res != 0) + fatal("invalid resolution %1: resolution must be a factor of 7200", + font::res); + fputs("\033E", stdout); // reset + if (font::res != 300) + printf("\033&u%dD", font::res); // unit of measure + if (ncopies > 0) + printf("\033&l%uX", ncopies); + paper_size = 0; // default to letter + if (font::papersize) { + int n = lookup_paper_size(font::papersize); + if (n < 0) + error("ignoring invalid paper format '%1'", font::papersize); + else + paper_size = n; + } + if (ps >= 0) + paper_size = ps; + printf("\033&l%dA" // paper format + "\033&l%dO" // orientation + "\033&l0E", // no top margin + paper_table[paper_size].code, + landscape_flag != 0); + if (landscape_flag) + x_offset = paper_table[paper_size].x_offset_landscape; + else + x_offset = paper_table[paper_size].x_offset_portrait; + x_offset = (x_offset * font::res) / 300; + if (duplex_flag) + printf("\033&l%dS", duplex_flag); +} + +lj4_printer::~lj4_printer() +{ + fputs("\033E", stdout); +} + +void lj4_printer::begin_page(int) +{ +} + +void lj4_printer::end_page(int) +{ + putchar('\f'); + cur_hpos = -1; +} + +void lj4_printer::end_of_line() +{ + cur_hpos = -1; // force absolute motion +} + +inline +int is_unprintable(unsigned char c) +{ + return c < 32 && (c == 0 || (7 <= c && c <= 15) || c == 27); +} + +void lj4_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 (symbol_set != cur_symbol_set) { + printf("\033(%d%c", symbol_set/32, (symbol_set & 31) + 64); + cur_symbol_set = symbol_set; + } + if (f != cur_font) { + lj4_font *psf = (lj4_font *)f; + // FIXME only output those that are needed + printf("\033(s%dp%ds%db%dT", + psf->proportional, + psf->style, + psf->weight, + psf->typeface); + if (!psf->proportional || !cur_font || !cur_font->proportional) + cur_size = 0; + cur_font = psf; + } + if (env->size != cur_size) { + if (cur_font->proportional) { + static const char *quarters[] = { "", ".25", ".5", ".75" }; + printf("\033(s%d%sV", env->size/4, quarters[env->size & 3]); + } + else { + double pitch = double(font::res)/w; + // PCL uses the next largest pitch, so round it down. + pitch = floor(pitch*100.0)/100.0; + printf("\033(s%.2fH", pitch); + } + cur_size = env->size; + } + if (!moveto(env->hpos, env->vpos)) + return; + if (is_unprintable(ch)) + fputs("\033&p1X", stdout); + putchar(ch); + cur_hpos += w; +} + +int lj4_printer::moveto1(int hpos, int vpos) +{ + if (hpos < x_offset || vpos < 0) + return 0; + fputs("\033*p", stdout); + if (cur_hpos < 0) + printf("%dx%dY", hpos - x_offset, vpos); + else { + if (cur_hpos != hpos) + printf("%s%d%c", hpos > cur_hpos ? "+" : "", + hpos - cur_hpos, vpos == cur_vpos ? 'X' : 'x'); + if (cur_vpos != vpos) + printf("%s%dY", vpos > cur_vpos ? "+" : "", vpos - cur_vpos); + } + cur_hpos = hpos; + cur_vpos = vpos; + return 1; +} + +void lj4_printer::draw(int code, int *p, int np, const environment *env) +{ + switch (code) { + case 'R': + { + if (np != 2) { + error("2 arguments required for rule"); + break; + } + int hpos = env->hpos; + int vpos = env->vpos; + int hsize = p[0]; + int vsize = p[1]; + if (hsize < 0) { + hpos += hsize; + hsize = -hsize; + } + if (vsize < 0) { + vpos += vsize; + vsize = -vsize; + } + if (!moveto(hpos, vpos)) + return; + printf("\033*c%da%db0P", hsize, vsize); + break; + } + case 'l': + if (np != 2) { + error("2 arguments required for line"); + break; + } + hpgl_init(); + if (!moveto(env->hpos, env->vpos)) + return; + hpgl_start(); + set_line_thickness(env->size, p[0] == 0 && p[1] == 0); + printf("PD%d,%d", p[0], p[1]); + hpgl_end(); + break; + case 'p': + case 'P': + { + if (np & 1) { + error("even number of arguments required for polygon"); + break; + } + if (np == 0) { + error("no arguments for polygon"); + break; + } + hpgl_init(); + if (!moveto(env->hpos, env->vpos)) + return; + hpgl_start(); + if (code == 'p') + set_line_thickness(env->size); + printf("PMPD%d", p[0]); + for (int i = 1; i < np; i++) + printf(",%d", p[i]); + printf("PM2%cP", code == 'p' ? 'E' : 'F'); + hpgl_end(); + break; + } + case '~': + { + if (np & 1) { + error("even number of arguments required for spline"); + break; + } + if (np == 0) { + error("no arguments for spline"); + break; + } + hpgl_init(); + if (!moveto(env->hpos, env->vpos)) + return; + hpgl_start(); + set_line_thickness(env->size); + printf("PD%d,%d", p[0]/2, p[1]/2); + const int tnum = 2; + const int tden = 3; + if (np > 2) { + fputs("BR", stdout); + for (int i = 0; i < np - 2; i += 2) { + if (i != 0) + putchar(','); + printf("%d,%d,%d,%d,%d,%d", + (p[i]*tnum)/(2*tden), + (p[i + 1]*tnum)/(2*tden), + p[i]/2 + (p[i + 2]*(tden - tnum))/(2*tden), + p[i + 1]/2 + (p[i + 3]*(tden - tnum))/(2*tden), + (p[i] - p[i]/2) + p[i + 2]/2, + (p[i + 1] - p[i + 1]/2) + p[i + 3]/2); + } + } + printf("PR%d,%d", p[np - 2] - p[np - 2]/2, p[np - 1] - p[np - 1]/2); + hpgl_end(); + break; + } + case 'c': + case 'C': + // troff adds an extra argument to C + if (np != 1 && !(code == 'C' && np == 2)) { + error("1 argument required for circle"); + break; + } + hpgl_init(); + if (!moveto(env->hpos + p[0]/2, env->vpos)) + return; + hpgl_start(); + if (code == 'c') { + set_line_thickness(env->size); + printf("CI%d", p[0]/2); + } + else + printf("WG%d,0,360", p[0]/2); + hpgl_end(); + break; + case 'e': + case 'E': + if (np != 2) { + error("2 arguments required for ellipse"); + break; + } + hpgl_init(); + if (!moveto(env->hpos + p[0]/2, env->vpos)) + return; + hpgl_start(); + printf("SC0,%.4f,0,-%.4f,2", hpgl_scale * double(p[0])/p[1], hpgl_scale); + if (code == 'e') { + set_line_thickness(env->size); + printf("CI%d", p[1]/2); + } + else + printf("WG%d,0,360", p[1]/2); + printf("SC0,%.4f,0,-%.4f,2", hpgl_scale, hpgl_scale); + hpgl_end(); + break; + case 'a': + { + if (np != 4) { + error("4 arguments required for arc"); + break; + } + hpgl_init(); + if (!moveto(env->hpos, env->vpos)) + return; + hpgl_start(); + set_line_thickness(env->size); + double c[2]; + if (adjust_arc_center(p, c)) { + double sweep = ((atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0]) + - atan2(-c[1], -c[0])) + * 180.0/PI); + if (sweep > 0.0) + sweep -= 360.0; + printf("PDAR%d,%d,%f", int(c[0]), int(c[1]), sweep); + } + else + printf("PD%d,%d", p[0] + p[2], p[1] + p[3]); + hpgl_end(); + } + break; + case 'f': + if (np != 1 && np != 2) { + error("1 argument required for fill"); + break; + } + hpgl_init(); + hpgl_start(); + if (p[0] >= 0 && p[0] <= 1000) + printf("FT10,%d", p[0]/10); + hpgl_end(); + break; + case 'F': + // not implemented yet + break; + 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; + } + line_thickness = p[0]; + } + break; + } + default: + error("unrecognised drawing command '%1'", char(code)); + break; + } +} + +void lj4_printer::hpgl_init() +{ + if (hpgl_inited) + return; + hpgl_inited = 1; + hpgl_scale = double(DEFAULT_HPGL_UNITS)/font::res; + printf("\033&f0S" // push position + "\033*p0x0Y" // move to 0,0 + "\033*c%dx%dy0T" // establish picture frame + "\033%%1B" // switch to HPGL + "SP1SC0,%.4f,0,-%.4f,2IR0,100,0,100" // set up scaling + "LA1,4,2,4" // round line ends and joins + "PR" // relative plotting + "TR0" // opaque + ";\033%%1A" // back to PCL + "\033&f1S", // pop position + MAX_PAPER_WIDTH, MAX_PAPER_HEIGHT, + hpgl_scale, hpgl_scale); +} + +void lj4_printer::set_line_thickness(int size, int dot) +{ + double pw; + if (line_thickness < 0) + pw = (size * (line_width_factor * 25.4))/(font::sizescale * 72000.0); + else + pw = line_thickness*25.4/font::res; + if (dot && pw < MIN_DOT_PEN_WIDTH) + pw = MIN_DOT_PEN_WIDTH; + if (pw != pen_width) { + printf("PW%f", pw); + pen_width = pw; + } +} + +font *lj4_printer::make_font(const char *nm) +{ + return lj4_font::load_lj4_font(nm); +} + +printer *make_printer() +{ + return new lj4_printer(user_paper_size); +} + +static +int lookup_paper_size(const char *s) +{ + for (unsigned int i = 0; + i < sizeof(paper_table)/sizeof(paper_table[0]); i++) { + // FIXME Perhaps allow unique prefix. + if (strcasecmp(s, paper_table[i].name) == 0) + return i; + } + return -1; +} + +static void usage(FILE *stream); + +extern "C" int optopt, optind; + +int main(int argc, char **argv) +{ + setlocale(LC_NUMERIC, "C"); + program_name = argv[0]; + static char stderr_buf[BUFSIZ]; + setbuf(stderr, stderr_buf); + int c; + static const struct option long_options[] = { + { "help", no_argument, 0, CHAR_MAX + 1 }, + { "version", no_argument, 0, 'v' }, + { NULL, 0, 0, 0 } + }; + while ((c = getopt_long(argc, argv, "c:d:F:I:lp:vw:", long_options, NULL)) + != EOF) + switch(c) { + case 'l': + landscape_flag = 1; + break; + case 'I': + // ignore include search path + break; + case ':': + if (optopt == 'd') { + fprintf(stderr, "duplex assumed to be long-side\n"); + duplex_flag = 1; + } else + fprintf(stderr, "option -%c requires an argument\n", optopt); + fflush(stderr); + break; + case 'd': + if (!isdigit(*optarg)) // this ugly hack prevents -d without + optind--; // args from messing up the arg list + duplex_flag = atoi(optarg); + if (duplex_flag != 1 && duplex_flag != 2) { + fprintf(stderr, "odd value for duplex; assumed to be long-side\n"); + duplex_flag = 1; + } + break; + case 'p': + { + int n = lookup_paper_size(optarg); + if (n < 0) + error("ignoring invalid paper format '%1'", font::papersize); + else + user_paper_size = n; + break; + } + case 'v': + printf("GNU grolj4 (groff) version %s\n", Version_string); + exit(0); + break; + case 'F': + font::command_line_font_dir(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 + line_width_factor = int(n); + break; + } + case CHAR_MAX + 1: // --help + usage(stdout); + exit(0); + break; + case '?': + usage(stderr); + exit(1); + break; + default: + assert(0); + } + SET_BINARY(fileno(stdout)); + if (optind >= argc) + do_file("-"); + else { + for (int i = optind; i < argc; i++) + do_file(argv[i]); + } + return 0; +} + +static void usage(FILE *stream) +{ + fprintf(stream, + "usage: %s [-l] [-c n] [-d [n]] [-F dir] [-p paper-format]" + " [-w n] [file ...]\n" + "usage: %s {-v | --version}\n" + "usage: %s --help\n", + program_name, program_name, program_name); +} + +// Local Variables: +// fill-column: 72 +// mode: C++ +// End: +// vim: set cindent noexpandtab shiftwidth=2 textwidth=72: |