summaryrefslogtreecommitdiffstats
path: root/src/devices/grolj4
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/grolj4')
-rw-r--r--src/devices/grolj4/grolj4.1.man896
-rw-r--r--src/devices/grolj4/grolj4.am32
-rw-r--r--src/devices/grolj4/lj4.cpp715
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: