summaryrefslogtreecommitdiffstats
path: root/src/preproc/grn
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 19:44:05 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 19:44:05 +0000
commitd318611dd6f23fcfedd50e9b9e24620b102ba96a (patch)
tree8b9eef82ca40fdd5a8deeabf07572074c236095d /src/preproc/grn
parentInitial commit. (diff)
downloadgroff-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/preproc/grn')
-rw-r--r--src/preproc/grn/README68
-rw-r--r--src/preproc/grn/gprint.h90
-rw-r--r--src/preproc/grn/grn.1.man978
-rw-r--r--src/preproc/grn/grn.am35
-rw-r--r--src/preproc/grn/hdb.cpp441
-rw-r--r--src/preproc/grn/hgraph.cpp1060
-rw-r--r--src/preproc/grn/hpoint.cpp59
-rw-r--r--src/preproc/grn/main.cpp977
8 files changed, 3708 insertions, 0 deletions
diff --git a/src/preproc/grn/README b/src/preproc/grn/README
new file mode 100644
index 0000000..73273f7
--- /dev/null
+++ b/src/preproc/grn/README
@@ -0,0 +1,68 @@
+This is grn from the Berkeley ditroff distribution. It has no
+AT&T code and is therefore freely distributable.
+
+Tim Theisen <tim@cs.wisc.edu>
+
+=====================================================================
+
+This is the modified code for the groff. It uses the different
+devxxx format that is ascii rather than binary as in the
+Berkeley distribution. Since groff does not have the \Ds option
+for line drawing (dotted, dashed, etc.), this version includes
+the routines for drawing curves and arcs, so it does not use the
+\D~, \Da nor \Dc. Although also included in here is a routine
+for drawing the optional gremlin style curves, it is not used
+because the gremlin editor uses the conventional spline
+algorithm. The Berkeley grn has the choice of different
+stipples. Here, only different shades of gray will be painted
+depending on the gremlin file. It is possible to upgrade this at
+a later time. (Daniel Senderowicz <daniel@synchrods.com> 12/28/99)
+
+=====================================================================
+
+Gremlin produces three types of curves: B-Splines, interpolated
+curves and Bezier. As the original Berkeley grn, now groff grn
+will honor B-Splines and interpolated curves. Bezier curves will
+be printed as B-Splines. (Daniel Senderowicz <daniel@synchrods.com>
+10/04/02)
+
+=====================================================================
+
+It has been further modified by Werner Lemberg <wl@gnu.org> to fit
+better into the groff package.
+
+ . Replaced Makefile with Makefile.sub.
+
+ . Removed dev.h since it is unused.
+
+ . Renamed grn.1 to grn.man; this man page has been extensively
+ revised.
+
+ . Used error() and fatal() from libgroff for all source files.
+
+ . Renamed *.c to *.cpp; updates as needed for C++ (prototypes, proper
+ casts, standard header files etc). Heavy formatting.
+
+ . main.cpp:
+
+ Using groff's default values instead of DEVDIR, DEFAULTDEV,
+ PRINTER, TYPESETTER, and GREMLIB.
+
+ 'res' is now an integer.
+
+ Added '-C' command flag (for compatibility mode) as with other
+ preprocessors.
+
+ Added '-F' and '-v' option (similar to troff).
+
+ Renamed '-L' option to '-M' for consistence.
+
+ Removed '-P' option.
+
+ Using font::load_desc() for scanning DESC files.
+
+ Removed SYSV-specific code.
+
+ Using macro_path.open_file() for getting gremlin graphic files.
+
+ Added usage().
diff --git a/src/preproc/grn/gprint.h b/src/preproc/grn/gprint.h
new file mode 100644
index 0000000..772d79a
--- /dev/null
+++ b/src/preproc/grn/gprint.h
@@ -0,0 +1,90 @@
+/* Last non-groff version: gprint.h 1.1 84/10/08
+ *
+ * This file contains standard definitions used by the gprint program.
+ */
+
+#include <stdio.h>
+#include <math.h>
+
+
+#define xorn(x,y) (x)
+ /* was 512 */
+#define yorn(x,y) (511 - (y)) /* switch direction for */
+ /* y-coordinates */
+
+#define STYLES 6
+#define SIZES 4
+#define FONTS 4
+#define SOLID -1
+#define DOTTED 004 /* 014 */
+#define DASHED 020 /* 034 */
+#define DOTDASHED 024 /* 054 */
+#define LONGDASHED 074
+
+#define DEFTHICK -1 /* default thickness */
+#define DEFSTYLE SOLID /* default line style */
+
+#define TRUE 1
+#define FALSE 0
+
+#define nullelt -1
+#define nullpt -1
+#define nullun NULL
+
+#define BOTLEFT 0
+#define BOTRIGHT 1
+#define CENTCENT 2
+#define VECTOR 3
+#define ARC 4
+#define CURVE 5
+#define POLYGON 6
+#define BSPLINE 7
+#define BEZIER 8
+#define TOPLEFT 10
+#define TOPCENT 11
+#define TOPRIGHT 12
+#define CENTLEFT 13
+#define CENTRIGHT 14
+#define BOTCENT 15
+#define TEXT(t) ( (t <= CENTCENT) || (t >= TOPLEFT) )
+
+/* WARNING * WARNING * WARNING * WARNING * WARNING * WARNING * WARNING
+ * The above (TEXT) test is dependent on the relative values of the
+ * constants and will have to change if these values change or if new
+ * commands are added with value greater than BOTCENT
+ */
+
+#define NUSER 4
+#define NFONTS 4
+#define NBRUSHES 6
+#define NSIZES 4
+#define NJUSTS 9
+#define NSTIPPLES 16
+
+#define ADD 1
+#define DELETE 2
+#define MOD 3
+
+typedef struct point {
+ double x, y;
+ struct point *nextpt;
+} POINT;
+
+typedef struct elmt {
+ int type, brushf, size, textlength;
+ char *textpt;
+ POINT *ptlist;
+ struct elmt *nextelt, *setnext;
+} ELT;
+
+#define DBNextElt(elt) (elt->nextelt)
+#define DBNextofSet(elt) (elt->setnext)
+#define DBNullelt(elt) (elt == NULL)
+#define Nullpoint(pt) ((pt) == (POINT *) NULL)
+#define PTNextPoint(pt) (pt->nextpt)
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/preproc/grn/grn.1.man b/src/preproc/grn/grn.1.man
new file mode 100644
index 0000000..cbc15ae
--- /dev/null
+++ b/src/preproc/grn/grn.1.man
@@ -0,0 +1,978 @@
+'\" t
+.TH @g@grn @MAN1EXT@ "@MDATE@" "groff @VERSION@"
+.SH Name
+@g@grn \- embed Gremlin images in
+.I groff
+documents
+.
+.
+.\" ====================================================================
+.\" Legal Terms
+.\" ====================================================================
+.\"
+.\" Copyright (C) 2000-2020 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_grn_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
+.
+.
+.\" ====================================================================
+.SH Synopsis
+.\" ====================================================================
+.
+.SY @g@grn
+.RB [ \-C ]
+.RB [ \-T\~\c
+.IR dev ]
+.RB [ \-M\~\c
+.IR dir ]
+.RB [ \-F\~\c
+.IR dir ]
+.RI [ file\~ .\|.\|.]
+.YS
+.
+.
+.SY @g@grn
+.B \-?
+.
+.SY @g@grn
+.B \-\-help
+.YS
+.
+.
+.SY @g@grn
+.B \-v
+.
+.SY @g@grn
+.B \-\-version
+.YS
+.
+.
+.\" ====================================================================
+.SH Description
+.\" ====================================================================
+.
+.I @g@grn
+is a preprocessor for including
+.I gremlin
+pictures in
+.MR @g@troff @MAN1EXT@
+input.
+.
+.I @g@grn
+writes to standard output,
+processing only input lines between two that start with
+.B .GS
+and
+.BR .GE .
+.
+Those lines must contain
+.I @g@grn
+commands
+(see below).
+.
+These macros request a
+.I gremlin
+file;
+the picture in that file is converted and placed in the
+.I @g@troff
+input stream.
+.
+.B .GS
+may be called with a
+.BR C ,
+.BR L ,
+or
+.B R
+argument to center,
+left-,
+or right-justify the whole
+.I gremlin
+picture
+(the default is to center).
+.
+If no
+.I file
+is mentioned,
+the standard input is read.
+.
+At the end of the picture,
+the position on the page is the bottom of the
+.I gremlin
+picture.
+.
+If the
+.I @g@grn
+entry is ended with
+.B .GF
+instead of
+.BR .GE ,
+the position is left at the top of the picture.
+.
+.
+.PP
+Currently only the
+.I me
+macro package has support for
+.BR .GS ,
+.BR .GE ,
+and
+.BR .GF .
+.
+.
+.PP
+.I @g@grn
+produces drawing escape sequences that use
+.IR groff 's
+color scheme extension
+.RB ( \[rs]D\[aq]F \~.\|.\|.\& \[aq] ),
+and thus may not work with other
+.IR troff s.
+.
+.
+.\" ====================================================================
+.SS "\f[I]grn\f[] commands"
+.\" ====================================================================
+.
+Each input line between
+.B .GS
+and
+.B .GE
+may have one
+.I @g@grn
+command.
+.
+Commands consist of one or two strings separated by white space,
+the first string being the command and the second its operand.
+.
+Commands may be upper- or lowercase and abbreviated down to one
+character.
+.
+.
+.PP
+Commands that affect a picture's environment
+(those listed before
+.RB \%\[lq] default \[rq],
+see below)
+are only in effect for the current picture:
+the environment is reinitialized to the defaults at the start of the
+next picture.
+.
+The commands are as follows.
+.
+.
+.TP
+.BI 1\~ N
+.TQ
+.BI 2\~ N
+.TQ
+.BI 3\~ N
+.TQ
+.BI 4\~ N
+.
+Set
+.IR gremlin 's
+text size number 1
+(2,
+3,
+or 4)
+to
+.I N
+points.
+.
+The default is 12
+(16,
+24,
+and 36,
+respectively).
+.
+.
+.TP
+.BI roman\~ f
+.TQ
+.BI italics\~ f
+.TQ
+.BI bold\~ f
+.TQ
+.BI special\~ f
+Set the roman
+(italics,
+bold,
+or special)
+font to
+.IR @g@troff 's
+font
+.I f
+(either a name or number).
+.
+The default is R
+(I,
+B,
+and S,
+respectively).
+.
+.
+.TP
+.BI l\~ f
+.TQ
+.BI stipple\~ f
+Set the stipple font to
+.IR @g@troff 's
+stipple font
+.I f
+(name or number).
+.
+The command
+.B \%stipple
+may be abbreviated down as far as
+.RB \[lq] st \[rq]
+(to avoid confusion with
+.RB \%\[lq] special \[rq]).
+.
+There is
+.I no
+default for stipples
+(unless one is set by the
+.RB \%\[lq] default \[rq]
+command),
+and it is invalid to include a
+.I gremlin
+picture with polygons without specifying a stipple font.
+.
+.
+.TP
+.BI x\~ N
+.TQ
+.BI scale\~ N
+Magnify the picture
+(in addition to any default magnification)
+by
+.IR N ,
+a floating-point number larger than zero.
+.
+The command
+.B scale
+may be abbreviated down to
+.RB \[lq] sc \[rq].
+.
+.
+.TP
+.BI narrow\~ N
+.TQ
+.BI medium\~ N
+.TQ
+.BI thick\~ N
+.
+Set the thickness of
+.IR gremlin 's
+narrow
+(medium and thick,
+respectively)
+lines to
+.I N
+times 0.15pt
+(this value can be changed at compile time).
+.
+The default is 1.0
+(3.0 and 5.0,
+respectively),
+which corresponds to 0.15pt
+(0.45pt and 0.75pt,
+respectively).
+.
+A thickness value of zero selects the smallest available line thickness.
+.
+Negative values cause the line thickness to be proportional to the
+current point size.
+.
+.
+.TP
+.BR pointscale\~ [ off | on ]
+Scale text to match the picture.
+.
+Gremlin text is usually printed in the point size specified with the
+commands
+.BR 1 ,
+.BR 2 ,
+.BR 3 ,
+.RB or\~ 4 ,
+regardless of any scaling factors in the picture.
+.
+Setting
+.B pointscale
+will cause the point sizes to scale with the picture
+(within
+.IR @g@troff 's
+limitations,
+of course).
+.
+An operand of anything but
+.B off
+will turn text scaling on.
+.
+.
+.TP
+.B default
+Reset the picture environment defaults to the settings in the current
+picture.
+.
+This is meant to be used as a global parameter setting mechanism at
+the beginning of the
+.I @g@troff
+input file,
+but can be used at any time to reset the default settings.
+.
+.
+.TP
+.BI width\~ N
+Force the picture to be
+.I N
+inches wide.
+.
+This overrides any scaling factors present in the same picture.
+.
+.RB \[lq] "width 0" \[rq]
+is ignored.
+.
+.
+.TP
+.BI height\~ N
+Force the picture to be
+.I N
+inches high,
+overriding other scaling factors.
+.
+If both
+.B width
+and
+.B height
+are specified,
+the tighter constraint will determine the scale of the picture.
+.
+.B height
+and
+.B width
+commands are not saved with a
+.RB \%\[lq] default \[rq]
+command.
+.
+They will,
+however,
+affect point size scaling if that option is set.
+.
+.
+.TP
+.BI file\~ name
+Get picture from
+.I gremlin
+file
+.I name
+located the current directory
+(or in the library directory;
+see the
+.B \-M
+option above).
+.
+If multiple
+.B file
+commands are given,
+the last one controls.
+.
+If
+.I name
+doesn't exist,
+an error message is reported and processing continues from the
+.B .GE
+line.
+.
+.
+.\" ====================================================================
+.SS "Usage with \f[I]groff\f[]"
+.\" ====================================================================
+.
+Since
+.I @g@grn
+is a preprocessor,
+it has no access to elements of formatter state,
+such as
+indentation,
+line length,
+type size,
+or
+register values.
+.
+Consequently,
+no
+.I @g@troff
+input can be placed between the
+.B .GS
+and
+.B .GE
+macros.
+.
+However,
+.I gremlin
+text elements are subsequently processed by
+.IR @g@troff ,
+so anything valid in a single line of
+.I @g@troff
+input is valid in a line of
+.I gremlin
+text
+(barring the dot control character \[lq].\[rq] at the beginning of a
+line).
+.
+Thus,
+it is possible to have equations within a
+.I gremlin
+figure by including in the
+.I gremlin
+file
+.I eqn \" language
+expressions enclosed by previously defined delimiters
+(e.g.,
+\[lq]$$\[rq]).
+.
+.
+.PP
+When using
+.I @g@grn
+along with other preprocessors,
+it is best to run
+.MR @g@tbl @MAN1EXT@
+before
+.IR @g@grn ,
+.MR @g@pic @MAN1EXT@ ,
+and/or
+.I ideal \" no GNU version yet
+to avoid overworking
+.IR @g@tbl .
+.
+.MR @g@eqn @MAN1EXT@
+should always be run last.
+.
+.MR groff @MAN1EXT@
+will automatically run preprocessors in the correct order.
+.
+.
+.PP
+A picture is considered an entity,
+but that doesn't stop
+.I @g@troff
+from trying to break it up if it falls off the end of a page.
+.
+Placing the picture between \[lq]keeps\[rq] in the
+.I me
+macros will ensure proper placement.
+.
+.
+.PP
+.I @g@grn
+uses
+.IR @g@troff 's
+registers
+.B g1
+through
+.B g9
+and sets registers
+.B g1
+and
+.B g2
+to the width and height of the
+.I gremlin
+figure
+(in device units)
+before entering the
+.B .GS
+macro
+(this is for those who want to rewrite these macros).
+.
+.
+.\" ====================================================================
+.SS "Gremlin file format"
+.\" ====================================================================
+.
+There exist two distinct
+.I gremlin
+file formats:
+the original format for AED graphic terminals,
+and the Sun or X11 version.
+.
+An extension used by the Sun/X11 version allowing reference points with
+negative coordinates is
+.I not
+compatible with the AED version.
+.
+As long as a
+.I gremlin
+file does not contain negative coordinates,
+either format will be read correctly by either version of
+.I gremlin
+or
+.IR @g@grn .
+.
+The other difference in
+Sun/X11 format is the use of names for picture objects
+(e.g.,
+.BR POLYGON ,
+.BR CURVE )
+instead of numbers.
+.
+Files representing the same picture are shown below.
+.
+.
+.PP
+.ie t .ne 18v
+.el .ne 19v
+.TS
+center, tab(@);
+l lw(0.1i) l.
+sungremlinfile@@gremlinfile
+0 240.00 128.00@@0 240.00 128.00
+CENTCENT@@2
+240.00 128.00@@240.00 128.00
+185.00 120.00@@185.00 120.00
+240.00 120.00@@240.00 120.00
+296.00 120.00@@296.00 120.00
+*@@\-1.00 \-1.00
+2 3@@2 3
+10 A Triangle@@10 A Triangle
+POLYGON@@6
+224.00 416.00@@224.00 416.00
+96.00 160.00@@96.00 160.00
+384.00 160.00@@384.00 160.00
+*@@\-1.00 \-1.00
+5 1@@5 1
+0@@0
+\-1@@\-1
+.TE
+.
+.
+.IP \[bu] 2n
+The first line of each
+.I gremlin
+file contains either the string
+.RB \[lq] gremlinfile \[rq]
+(AED)
+or
+.RB \[lq] sungremlinfile \[rq]
+(Sun/X11).
+.
+.
+.IP \[bu]
+The second line of the file contains an orientation and
+.I x
+and
+.I y
+values for a positioning point,
+separated by spaces.
+.
+The orientation,
+either
+.B 0
+or
+.BR 1 ,
+is ignored by the Sun/X11 version.
+.
+.B 0
+means that
+.I gremlin
+will display things in horizontal format
+(a drawing area wider than it is tall,
+with a menu across the top).
+.
+.B 1
+means that
+.I gremlin
+will display things in vertical format
+(a drawing area taller than it is wide,
+with a menu on the left side).
+.
+.I x
+and
+.I y
+are floating-point values giving a positioning point to be used when
+this file is read into another file.
+.
+The stuff on this line really isn't all that important;
+a value of
+.RB \[lq] "1 0.00 0.00" \[rq]
+is suggested.
+.
+.
+.IP \[bu]
+The rest of the file consists of zero or more element specifications.
+.
+After the last element specification is a line containing the string
+.RB \[lq] \-1 \[rq].
+.
+.
+.IP \[bu]
+Lines longer than 127 characters are truncated to that length.
+.
+.
+.\" ====================================================================
+.SS "Element specifications"
+.\" ====================================================================
+.
+.IP \[bu] 2n
+The first line of each element contains a single decimal number giving
+the type of the element (AED) or its name (Sun/X11).
+.
+.
+.IP
+.ie t .ne 18v
+.el .ne 19v
+.TS
+center, tab(@);
+css
+ccc
+nBlBl.
+\f[I]gremlin\f[] File Format: Object Type Specification
+_
+AED Number@Sun/X11 Name@Description
+0@BOTLEFT@bottom-left-justified text
+1@BOTRIGHT@bottom-right-justified text
+2@CENTCENT@center-justified text
+3@VECTOR@vector
+4@ARC@arc
+5@CURVE@curve
+6@POLYGON@polygon
+7@BSPLINE@b-spline
+8@BEZIER@B\['e]zier
+10@TOPLEFT@top-left-justified text
+11@TOPCENT@top-center-justified text
+12@TOPRIGHT@top-right-justified text
+13@CENTLEFT@left-center-justified text
+14@CENTRIGHT@right-center-justified text
+15@BOTCENT@bottom-center-justified text
+.TE
+.
+.
+.IP \[bu]
+After the object type comes a variable number of lines,
+each specifying a point used to display the element.
+.
+Each line contains an x-coordinate and a y-coordinate in floating-point
+format,
+separated by spaces.
+.
+The list of points is terminated by a line containing the string
+.RB \[lq] "\-1.0 \-1.0" \[rq]
+(AED) or a single asterisk,
+.RB \[lq] * \[rq]
+(Sun/X11).
+.
+.
+.IP \[bu]
+After the points comes a line containing two decimal values,
+giving the brush and size for the element.
+.
+The brush determines the style in which things are drawn.
+.
+For vectors,
+arcs,
+and curves there are six valid brush values.
+.
+.
+.IP
+.TS
+center, tab(@);
+nB l.
+1@thin dotted lines
+2@thin dot-dashed lines
+3@thick solid lines
+4@thin dashed lines
+5@thin solid lines
+6@medium solid lines
+.TE
+.
+.
+.IP
+For polygons,
+one more value,
+0,
+is valid.
+.
+It specifies a polygon with an invisible border.
+.
+For text,
+the brush selects a font as follows.
+.
+.
+.IP
+.TS
+center, tab(@);
+nB l.
+1@roman (R font in \f[I]@g@troff\f[])
+2@italics (I font in \f[I]@g@troff\f[])
+3@bold (B font in \f[I]@g@troff\f[])
+4@special (S font in \f[I]@g@troff\f[])
+.TE
+.
+.
+.IP
+If you're using
+.I @g@grn
+to run your pictures through
+.IR groff ,
+the font is really just a starting font.
+.
+The text string can contain formatting sequences like
+\[lq]\[rs]fI\[rq]
+or
+\[lq]\[rs]d\[rq]
+which may change the font
+(as well as do many other things).
+.
+For text,
+the size field is a decimal value between 1 and 4.
+.
+It selects the size of the font in which the text will be drawn.
+.
+For polygons,
+this size field is interpreted as a stipple number to fill the polygon
+with.
+.
+The number is used to index into a stipple font at print time.
+.
+.
+.IP \[bu]
+The last line of each element contains a decimal number and a string of
+characters,
+separated by a single space.
+.
+The number is a count of the number of characters in the string.
+.
+This information is used only for text elements,
+and contains the text string.
+.
+There can be spaces inside the text.
+.
+For arcs,
+curves,
+and vectors,
+the character count is zero
+.RB ( 0 ),
+followed by exactly one space before the newline.
+.
+.
+.\" ====================================================================
+.SS Coordinates
+.\" ====================================================================
+.
+.I gremlin
+was designed for AED terminals,
+and its coordinates reflect the AED coordinate space.
+.
+For vertical pictures,
+.IR x \~values
+range 116 to 511,
+and
+.IR y \~values
+from 0 to 483.
+.
+For horizontal pictures,
+.IR x \~values
+range from 0 to 511,
+and
+.IR y \~values
+from 0 to 367.
+.
+Although you needn't absolutely stick to this range,
+you'll get better results if you at least stay in this vicinity.
+.
+Also,
+point lists are terminated by a point of
+(\-1,
+\-1),
+so you shouldn't ever use negative coordinates.
+.
+.I gremlin
+writes out coordinates using the
+.MR printf 3
+format \[lq]%f1.2\[rq];
+it's probably a good idea to use the same format if you want to modify
+the
+.I @g@grn
+code.
+.
+.
+.\" ====================================================================
+.SS "Sun/X11 coordinates"
+.\" ====================================================================
+.
+There is no restriction on the range of coordinates used to create
+objects in the Sun/X11 version of
+.IR gremlin .
+.
+However,
+files with negative coordinates
+.I will
+cause problems if displayed on the AED.
+.
+.
+.\" ====================================================================
+.SH Options
+.\" ====================================================================
+.
+.B \-?\&
+and
+.B \-\-help
+display a usage message,
+while
+.B \-v
+and
+.B \-\-version
+show version information;
+all exit afterward.
+.
+.
+.TP
+.B \-C
+Recognize
+.B .GS
+and
+.B .GE
+(and
+.BR .GF )
+even when followed by a character other than space or newline.
+.
+.
+.TP
+.BI \-F\~ dir
+Search
+.I dir
+for subdirectories
+.IR dev name
+.RI ( name
+is the name of the output driver)
+for the
+.I DESC
+file before the default font directories
+.IR @LOCALFONTDIR@ ,
+.IR @FONTDIR@ ,
+and
+.IR @LEGACYFONTDIR@ .
+.
+.
+.TP
+.BI \-M\~ dir
+Prepend
+.I dir
+to the search path for
+.I gremlin
+files.
+.
+The default search path is the current directory,
+the home directory,
+.if !'@COMPATIBILITY_WRAPPERS@'no' .IR @SYSTEMMACRODIR@ ,
+.IR @LOCALMACRODIR@ ,
+and
+.IR @MACRODIR@ ,
+in that order.
+.\".
+.\".
+.\".TP
+.\".B \-s
+.\"This switch causes the picture to be traversed twice:
+.\"The first time,
+.\"only the interiors of filled polygons
+.\"(as borderless polygons)
+.\"are printed.
+.\".
+.\"The second time,
+.\"the outline is printed as a series of line segments.
+.\".
+.\"This way,
+.\"postprocessors that overwrite rather than merge picture elements
+.\"(such as PostScript)
+.\"can still have text and graphics on a shaded background.
+.
+.
+.TP
+.BI \-T\~ dev
+Prepare device output using output driver
+.IR dev .
+.
+The default is
+.BR @DEVICE@ .
+.
+See
+.MR groff @MAN1EXT@
+for a list of valid devices.
+.
+.
+.\" ====================================================================
+.SH Files
+.\" ====================================================================
+.
+.TP
+.IR @FONTDIR@/\:\%dev name /\:DESC
+describes the output device
+.IR name .
+.
+.
+.\" ====================================================================
+.SH Authors
+.\" ====================================================================
+.
+David Slattengren and Barry Roitblat wrote the original Berkeley
+.IR grn .
+.
+Daniel Senderowicz and Werner Lemberg modified it for
+.IR groff .
+.
+.
+.\" ====================================================================
+.SH "See also"
+.\" ====================================================================
+.
+.MR gremlin 1 ,
+.MR groff @MAN1EXT@ ,
+.MR @g@pic @MAN1EXT@ ,
+.MR ideal 1
+.
+.
+.\" Restore compatibility mode (for, e.g., Solaris 10/11).
+.cp \n[*groff_grn_1_man_C]
+.do rr *groff_grn_1_man_C
+.
+.
+.\" Local Variables:
+.\" fill-column: 72
+.\" mode: nroff
+.\" End:
+.\" vim: set filetype=groff textwidth=72:
diff --git a/src/preproc/grn/grn.am b/src/preproc/grn/grn.am
new file mode 100644
index 0000000..f45fa24
--- /dev/null
+++ b/src/preproc/grn/grn.am
@@ -0,0 +1,35 @@
+# 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/>.
+
+prefixexecbin_PROGRAMS += grn
+grn_SOURCES = \
+ src/preproc/grn/hdb.cpp \
+ src/preproc/grn/hpoint.cpp \
+ src/preproc/grn/hgraph.cpp \
+ src/preproc/grn/main.cpp \
+ src/preproc/grn/gprint.h
+src/preproc/grn/main.$(OBJEXT): defs.h
+grn_LDADD = libgroff.a lib/libgnu.a $(LIBM)
+PREFIXMAN1 += src/preproc/grn/grn.1
+EXTRA_DIST += src/preproc/grn/README src/preproc/grn/grn.1.man
+
+
+# Local Variables:
+# fill-column: 72
+# mode: makefile-automake
+# End:
+# vim: set autoindent filetype=automake textwidth=72:
diff --git a/src/preproc/grn/hdb.cpp b/src/preproc/grn/hdb.cpp
new file mode 100644
index 0000000..9ba3eaa
--- /dev/null
+++ b/src/preproc/grn/hdb.cpp
@@ -0,0 +1,441 @@
+ /* Last non-groff version: hdb.c 1.8 (Berkeley) 84/10/20
+ *
+ * Copyright -C- 1982 Barry S. Roitblat
+ *
+ * This file contains database routines for the hard copy programs of
+ * the gremlin picture editor.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include "gprint.h"
+#include <string.h>
+#include <ctype.h>
+
+#include "errarg.h"
+#include "error.h"
+
+#define MAXSTRING 128
+#define MAXSTRING_S "127"
+
+/* imports from main.cpp */
+
+extern char gremlinfile[]; /* name of file currently reading */
+extern int SUNFILE; /* TRUE if SUN gremlin file */
+extern int compatibility_flag; /* TRUE if in compatibility mode */
+extern void *grnmalloc(size_t size, const char *what);
+extern void savebounds(double x, double y);
+
+/* imports from hpoint.cpp */
+
+extern POINT *PTInit();
+extern POINT *PTMakePoint(double x, double y, POINT ** pplist);
+
+
+int DBGetType(char *s);
+
+static long lineno = 0; /* line number of gremlin file */
+
+/*
+ * This routine returns a pointer to an initialized database element
+ * which would be the only element in an empty list.
+ */
+ELT *
+DBInit()
+{
+ return ((ELT *) NULL);
+} /* end DBInit */
+
+
+/*
+ * This routine creates a new element with the specified attributes and
+ * links it into database.
+ */
+ELT *
+DBCreateElt(int type,
+ POINT * pointlist,
+ int brush,
+ int size,
+ char *text,
+ ELT **db)
+{
+ ELT *temp = 0;
+
+ temp = (ELT *) grnmalloc(sizeof(ELT), "picture element");
+ temp->nextelt = *db;
+ temp->type = type;
+ temp->ptlist = pointlist;
+ temp->brushf = brush;
+ temp->size = size;
+ temp->textpt = text;
+ *db = temp;
+ return (temp);
+} /* end CreateElt */
+
+
+/*
+ * This routine reads the specified file into a database and returns a
+ * pointer to that database.
+ */
+ELT *
+DBRead(FILE *file)
+{
+ int i;
+ int done; /* flag for input exhausted */
+ double nx; /* x holder so x is not set before orienting */
+ int type; /* element type */
+ ELT *elist; /* pointer to the file's elements */
+ POINT *plist; /* pointer for reading in points */
+ char string[MAXSTRING], *txt;
+ double x, y; /* x and y are read in point coords */
+ int len, brush, size;
+ int lastpoint;
+
+ SUNFILE = FALSE;
+ elist = DBInit();
+ int nitems = fscanf(file, "%" MAXSTRING_S "s%*[^\n]\n", string);
+ if (nitems != 1) {
+ error_with_file_and_line(gremlinfile, lineno,
+ "malformed input; giving up on this"
+ " picture");
+ return (elist);
+ }
+ lineno++;
+ if (strcmp(string, "gremlinfile")) {
+ if (strcmp(string, "sungremlinfile")) {
+ error_with_file_and_line(gremlinfile, lineno,
+ "not a gremlin file; giving up on this"
+ " picture");
+ return (elist);
+ }
+ SUNFILE = TRUE;
+ }
+
+ nitems = fscanf(file, "%d%lf%lf\n", &size, &x, &y);
+ if (nitems != 3) {
+ error_with_file_and_line(gremlinfile, lineno,
+ "malformed input; giving up on this"
+ " picture");
+ return (elist);
+ }
+ lineno++;
+ /* ignore orientation and file positioning point */
+
+ done = FALSE;
+ while (!done) {
+ /* if (fscanf(file,"%" MAXSTRING_S "s\n", string) == EOF) */
+ /* I changed the scanf format because the element */
+ /* can have two words (e.g. CURVE SPLINE) */
+ if (fscanf(file, "\n%"
+ MAXSTRING_S
+ "[^\n]%*[^\n]\n", string) == EOF) {
+ lineno++;
+ error_with_file_and_line(gremlinfile, lineno, "error in format;"
+ " giving up on this picture");
+ return (elist);
+ }
+ lineno++;
+
+ type = DBGetType(string); /* interpret element type */
+ if (type < 0) { /* no more data */
+ done = TRUE;
+ } else {
+ /* always one point */
+#ifdef UW_FASTSCAN
+ (void) xscanf(file, &x, &y);
+#else
+ nitems = fscanf(file, "%lf%lf\n", &x, &y);
+ if (nitems != 2) {
+ error_with_file_and_line(gremlinfile, lineno,
+ "malformed input; giving up on this"
+ " picture");
+ return (elist);
+ }
+ lineno++;
+#endif /* UW_FASTSCAN */
+ plist = PTInit(); /* NULL point list */
+
+ /*
+ * Files created on the SUN have point lists terminated by a line
+ * containing only an asterisk ('*'). Files created on the AED
+ * have point lists terminated by the coordinate pair (-1.00
+ * -1.00).
+ */
+ if (TEXT(type)) { /* read only first point for TEXT elements */
+ nx = xorn(x, y);
+ y = yorn(x, y);
+ (void) PTMakePoint(nx, y, &plist);
+ savebounds(nx, y);
+
+#ifdef UW_FASTSCAN
+ while (xscanf(file, &x, &y));
+#else
+ lastpoint = FALSE;
+ do {
+ char *cp = fgets(string, MAXSTRING, file);
+ if (0 /* nullptr */ == cp) {
+ error_with_file_and_line(gremlinfile, lineno,
+ "premature end-of-file or error"
+ " reading input; giving up on this"
+ " picture");
+ return(elist);
+ }
+ lineno++;
+ if (string[0] == '*') { /* SUN gremlin file */
+ lastpoint = TRUE;
+ } else {
+ if (!sscanf(string, "%lf%lf", &x, &y)) {
+ error_with_file_and_line(gremlinfile, lineno,
+ "expected coordinate pair, got"
+ " '%1'; giving up on this"
+ " picture", string);
+ return(elist);
+ }
+ if ((x == -1.00 && y == -1.00) && (!SUNFILE))
+ lastpoint = TRUE;
+ else {
+ if (compatibility_flag)
+ savebounds(xorn(x, y), yorn(x, y));
+ }
+ }
+ } while (!lastpoint);
+#endif /* UW_FASTSCAN */
+ } else { /* not TEXT element */
+#ifdef UW_FASTSCAN
+ do {
+ nx = xorn(x, y);
+ y = yorn(x, y);
+ (void) PTMakePoint(nx, y, &plist);
+ savebounds(nx, y);
+ } while (xscanf(file, &x, &y));
+#else
+ lastpoint = FALSE;
+ while (!lastpoint) {
+ nx = xorn(x, y);
+ y = yorn(x, y);
+ (void) PTMakePoint(nx, y, &plist);
+ savebounds(nx, y);
+
+ char *cp = fgets(string, MAXSTRING, file);
+ if (0 /* nullptr */ == cp) {
+ error_with_file_and_line(gremlinfile, lineno,
+ "premature end-of-file or error"
+ " reading input; giving up on this"
+ " picture");
+ return(elist);
+ }
+ lineno++;
+ if (string[0] == '*') { /* SUN gremlin file */
+ lastpoint = TRUE;
+ } else {
+ (void) sscanf(string, "%lf%lf", &x, &y);
+ if ((x == -1.00 && y == -1.00) && (!SUNFILE))
+ lastpoint = TRUE;
+ }
+ }
+#endif /* UW_FASTSCAN */
+ }
+ nitems = fscanf(file, "%d%d\n", &brush, &size);
+ if (nitems != 2) {
+ error_with_file_and_line(gremlinfile, lineno,
+ "malformed input; giving up on this"
+ " picture");
+ return (elist);
+ }
+ lineno++;
+ nitems = fscanf(file, "%d", &len); /* text length */
+ if (nitems != 1) {
+ error_with_file_and_line(gremlinfile, lineno,
+ "malformed input; giving up on this"
+ " picture");
+ return (elist);
+ }
+ (void) getc(file); /* eat blank */
+ lineno++; /* advance line counter early */
+ if (len < 0) {
+ error_with_file_and_line(gremlinfile, lineno,
+ "length claimed for text is nonsense:"
+ " '%1'; giving up on this picture",
+ len);
+ return (elist);
+ }
+ txt = (char *) grnmalloc((unsigned) len + 1, "element text");
+ for (i = 0; i < len; ++i) { /* read text */
+ int c = getc(file);
+ if (c == EOF)
+ break;
+ txt[i] = c;
+ }
+ if (feof(file)) {
+ error_with_file_and_line(gremlinfile, lineno,
+ "end of file while reading text of"
+ " length %1; giving up on this"
+ " picture", len);
+ return (elist);
+ }
+ txt[len] = '\0';
+ (void) DBCreateElt(type, plist, brush, size, txt, &elist);
+ } /* end else */
+ } /* end while not done */ ;
+ return (elist);
+} /* end DBRead */
+
+
+/*
+ * Interpret element type in string s.
+ * Old file format consisted of integer element types.
+ * New file format has literal names for element types.
+ */
+int
+DBGetType(char *s)
+{
+ if (isdigit(s[0]) || (s[0] == '-')) /* old element format or EOF */
+ return (atoi(s));
+
+ switch (s[0]) {
+ case 'P':
+ return (POLYGON);
+ case 'V':
+ return (VECTOR);
+ case 'A':
+ return (ARC);
+ case 'C':
+ if (s[1] == 'U') {
+ if (s[5] == '\n')
+ return (CURVE);
+ switch (s[7]) {
+ case 'S':
+ return(BSPLINE);
+ case 'E':
+ warning_with_file_and_line(gremlinfile, lineno,
+ "using B-spline for Bezier curve");
+ return(BSPLINE);
+ default:
+ return(CURVE);
+ }
+ }
+ switch (s[4]) {
+ case 'L':
+ return (CENTLEFT);
+ case 'C':
+ return (CENTCENT);
+ case 'R':
+ return (CENTRIGHT);
+ default:
+ error_with_file_and_line(gremlinfile, lineno,
+ "unknown element type '%1'", s);
+ return -1;
+ }
+ case 'B':
+ switch (s[3]) {
+ case 'L':
+ return (BOTLEFT);
+ case 'C':
+ return (BOTCENT);
+ case 'R':
+ return (BOTRIGHT);
+ default:
+ error_with_file_and_line(gremlinfile, lineno,
+ "unknown element type '%1'", s);
+ return -1;
+ }
+ case 'T':
+ switch (s[3]) {
+ case 'L':
+ return (TOPLEFT);
+ case 'C':
+ return (TOPCENT);
+ case 'R':
+ return (TOPRIGHT);
+ default:
+ error_with_file_and_line(gremlinfile, lineno,
+ "unknown element type '%1'", s);
+ return -1;
+ }
+ default:
+ error_with_file_and_line(gremlinfile, lineno,
+ "unknown element type '%1'", s);
+ return -1;
+ }
+}
+
+#ifdef UW_FASTSCAN
+/*
+ * Optimization hack added by solomon@crys.wisc.edu, 12/2/86.
+ * A huge fraction of the time was spent reading floating point numbers
+ * from the input file, but the numbers always have the format 'ddd.dd'.
+ * Thus the following special-purpose version of fscanf.
+ *
+ * xscanf(f,xp,yp) does roughly what fscanf(f,"%f%f",xp,yp) does except:
+ * -the next piece of input must be of the form
+ * <space>* <digit>*'.'<digit>* <space>* <digit>*'.'<digit>*
+ * -xscanf eats the character following the second number
+ * -xscanf returns 0 for "end-of-data" indication, 1 otherwise, where
+ * end-of-data is signalled by a '*' [in which case the rest of the
+ * line is gobbled], or by '-1.00 -1.00' [but only if !SUNFILE].
+ */
+int
+xscanf(FILE *f,
+ double *xp,
+ double *yp)
+{
+ int c, i, j, m, frac;
+ int iscale = 1, jscale = 1; /* x = i/scale, y=j/jscale */
+
+ while ((c = getc(f)) == ' ');
+ if (c == '*') {
+ while ((c = getc(f)) != '\n');
+ return 0;
+ }
+ i = m = frac = 0;
+ while (isdigit(c) || c == '.' || c == '-') {
+ if (c == '-') {
+ m++;
+ c = getc(f);
+ continue;
+ }
+ if (c == '.')
+ frac = 1;
+ else {
+ if (frac)
+ iscale *= 10;
+ i = 10 * i + c - '0';
+ }
+ c = getc(f);
+ }
+ if (m)
+ i = -i;
+ *xp = (double) i / (double) iscale;
+
+ while ((c = getc(f)) == ' ');
+ j = m = frac = 0;
+ while (isdigit(c) || c == '.' || c == '-') {
+ if (c == '-') {
+ m++;
+ c = getc(f);
+ continue;
+ }
+ if (c == '.')
+ frac = 1;
+ else {
+ if (frac)
+ jscale *= 10;
+ j = 10 * j + c - '0';
+ }
+ c = getc(f);
+ }
+ if (m)
+ j = -j;
+ *yp = (double) j / (double) jscale;
+ return (SUNFILE || i != -iscale || j != -jscale);
+}
+#endif /* UW_FASTSCAN */
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/preproc/grn/hgraph.cpp b/src/preproc/grn/hgraph.cpp
new file mode 100644
index 0000000..9ed81a4
--- /dev/null
+++ b/src/preproc/grn/hgraph.cpp
@@ -0,0 +1,1060 @@
+/* Last non-groff version: hgraph.c 1.14 (Berkeley) 84/11/27
+ *
+ * This file contains the graphics routines for converting gremlin
+ * pictures to troff input.
+ */
+
+#include "lib.h"
+
+#include "gprint.h"
+
+#define MAXVECT 40
+#define MAXPOINTS 200
+#define LINELENGTH 1
+#define PointsPerInterval 64
+#define pi 3.14159265358979324
+#define twopi (2.0 * pi)
+#define len(a, b) groff_hypot((double)(b.x-a.x), \
+ (double)(b.y-a.y))
+
+
+extern int dotshifter; /* for the length of dotted curves */
+
+extern int style[]; /* line and character styles */
+extern double thick[];
+extern char *tfont[];
+extern int tsize[];
+extern int stipple_index[]; /* stipple font idx for stipples 0-16 */
+extern char *stipple; /* stipple type (cf or ug) */
+
+
+extern double troffscale; /* imports from main.c */
+extern double linethickness;
+extern int linmod;
+extern int lastx;
+extern int lasty;
+extern int lastyline;
+extern int ytop;
+extern int ybottom;
+extern int xleft;
+extern int xright;
+extern enum E {
+ OUTLINE, FILL, BOTH
+} polyfill;
+
+extern double adj1;
+extern double adj2;
+extern double adj3;
+extern double adj4;
+extern int res;
+
+void HGSetFont(int font, int size);
+void HGPutText(int justify, POINT pnt, char *string);
+void HGSetBrush(int mode);
+void tmove2(int px, int py);
+void doarc(POINT cp, POINT sp, int angle);
+void tmove(POINT * ptr);
+void cr();
+void drawwig(POINT * ptr, int type);
+void HGtline(int x1, int y1);
+void deltax(double x);
+void deltay(double y);
+void HGArc(int cx, int cy, int px, int py, int angle);
+void picurve(int *x, int *y, int npts);
+void HGCurve(int *x, int *y, int numpoints);
+void Parameterize(int x[], int y[], double h[], int n);
+void PeriodicSpline(double h[], int z[],
+ double dz[], double d2z[], double d3z[],
+ int npoints);
+void NaturalEndSpline(double h[], int z[],
+ double dz[], double d2z[], double d3z[],
+ int npoints);
+
+
+
+/*--------------------------------------------------------------------*
+ | Routine: HGPrintElt (element_pointer, baseline)
+ |
+ | Results: Examines a picture element and calls the appropriate
+ | routine(s) to print them according to their type. After
+ | the picture is drawn, current position is (lastx,lasty).
+ *--------------------------------------------------------------------*/
+
+void
+HGPrintElt(ELT *element,
+ int /* baseline */)
+{
+ POINT *p1;
+ POINT *p2;
+ int length;
+ int graylevel;
+
+ if (!DBNullelt(element) && !Nullpoint((p1 = element->ptlist))) {
+ /* p1 always has first point */
+ if (TEXT(element->type)) {
+ HGSetFont(element->brushf, element->size);
+ switch (element->size) {
+ case 1:
+ p1->y += adj1;
+ break;
+ case 2:
+ p1->y += adj2;
+ break;
+ case 3:
+ p1->y += adj3;
+ break;
+ case 4:
+ p1->y += adj4;
+ break;
+ default:
+ break;
+ }
+ HGPutText(element->type, *p1, element->textpt);
+ } else {
+ if (element->brushf) /* if there is a brush, the */
+ HGSetBrush(element->brushf); /* graphics need it set */
+
+ switch (element->type) {
+
+ case ARC:
+ p2 = PTNextPoint(p1);
+ tmove(p2);
+ doarc(*p1, *p2, element->size);
+ cr();
+ break;
+
+ case CURVE:
+ length = 0; /* keep track of line length */
+ drawwig(p1, CURVE);
+ cr();
+ break;
+
+ case BSPLINE:
+ length = 0; /* keep track of line length */
+ drawwig(p1, BSPLINE);
+ cr();
+ break;
+
+ case VECTOR:
+ length = 0; /* keep track of line length so */
+ tmove(p1); /* single lines don't get long */
+ while (!Nullpoint((p1 = PTNextPoint(p1)))) {
+ HGtline((int) (p1->x * troffscale),
+ (int) (p1->y * troffscale));
+ if (length++ > LINELENGTH) {
+ length = 0;
+ printf("\\\n");
+ }
+ } /* end while */
+ cr();
+ break;
+
+ case POLYGON:
+ {
+ /* brushf = style of outline; size = color of fill:
+ * on first pass (polyfill=FILL), do the interior using 'P'
+ * unless size=0
+ * on second pass (polyfill=OUTLINE), do the outline using a
+ * series of vectors. It might make more sense to use \D'p
+ * ...', but there is no uniform way to specify a 'fill
+ * character' that prints as 'no fill' on all output
+ * devices (and stipple fonts).
+ * If polyfill=BOTH, just use the \D'p ...' command.
+ */
+ double firstx = p1->x;
+ double firsty = p1->y;
+
+ length = 0; /* keep track of line length so */
+ /* single lines don't get long */
+
+ if (polyfill == FILL || polyfill == BOTH) {
+ /* do the interior */
+ char command = (polyfill == BOTH && element->brushf)
+ ? 'p' : 'P';
+
+ /* include outline, if there is one and */
+ /* the -p flag was set */
+
+ /* switch based on what gremlin gives */
+ switch (element->size) {
+ case 1:
+ graylevel = 1;
+ break;
+ case 3:
+ graylevel = 2;
+ break;
+ case 12:
+ graylevel = 3;
+ break;
+ case 14:
+ graylevel = 4;
+ break;
+ case 16:
+ graylevel = 5;
+ break;
+ case 19:
+ graylevel = 6;
+ break;
+ case 21:
+ graylevel = 7;
+ break;
+ case 23:
+ graylevel = 8;
+ break;
+ default: /* who's giving something else? */
+ graylevel = NSTIPPLES;
+ break;
+ }
+ /* int graylevel = element->size; */
+
+ if (graylevel < 0)
+ break;
+ if (graylevel > NSTIPPLES)
+ graylevel = NSTIPPLES;
+ printf("\\D'Fg %.3f'",
+ double(1000 - stipple_index[graylevel]) / 1000.0);
+ cr();
+ tmove(p1);
+ printf("\\D'%c", command);
+
+ while (!Nullpoint((PTNextPoint(p1)))) {
+ p1 = PTNextPoint(p1);
+ deltax((double) p1->x);
+ deltay((double) p1->y);
+ if (length++ > LINELENGTH) {
+ length = 0;
+ printf("\\\n");
+ }
+ } /* end while */
+
+ /* close polygon if not done so by user */
+ if ((firstx != p1->x) || (firsty != p1->y)) {
+ deltax((double) firstx);
+ deltay((double) firsty);
+ }
+ putchar('\'');
+ cr();
+ break;
+ }
+ /* else polyfill == OUTLINE; only draw the outline */
+ if (!(element->brushf))
+ break;
+ length = 0; /* keep track of line length */
+ tmove(p1);
+
+ while (!Nullpoint((PTNextPoint(p1)))) {
+ p1 = PTNextPoint(p1);
+ HGtline((int) (p1->x * troffscale),
+ (int) (p1->y * troffscale));
+ if (length++ > LINELENGTH) {
+ length = 0;
+ printf("\\\n");
+ }
+ } /* end while */
+
+ /* close polygon if not done so by user */
+ if ((firstx != p1->x) || (firsty != p1->y)) {
+ HGtline((int) (firstx * troffscale),
+ (int) (firsty * troffscale));
+ }
+ cr();
+ break;
+ } /* end case POLYGON */
+ } /* end switch */
+ } /* end else Text */
+ } /* end if */
+} /* end PrintElt */
+
+
+/*---------------------------------------------------------------------*
+ | Routine: HGPutText (justification, position_point, string)
+ |
+ | Results: Given the justification, a point to position with, and a
+ | string to put, HGPutText first sends the string into a
+ | diversion, moves to the positioning point, then outputs
+ | local vertical and horizontal motions as needed to
+ | justify the text. After all motions are done, the
+ | diversion is printed out.
+ *--------------------------------------------------------------------*/
+
+void
+HGPutText(int justify,
+ POINT pnt,
+ char *string)
+{
+ int savelasty = lasty; /* vertical motion for text is to be */
+ /* ignored. Save current y here */
+
+ printf(".nr g8 \\n(.d\n"); /* save current vertical position. */
+ printf(".ds g9 \""); /* define string containing the text. */
+ while (*string) { /* put out the string */
+ if (*string == '\\' &&
+ *(string + 1) == '\\') { /* one character at a */
+ printf("\\\\\\"); /* time replacing // */
+ string++; /* by //// to prevent */
+ } /* interpretation at */
+ printf("%c", *(string++)); /* printout time */
+ }
+ printf("\n");
+
+ tmove(&pnt); /* move to positioning point */
+
+ switch (justify) {
+ /* local vertical motions--the numbers here are used to be
+ somewhat compatible with gprint */
+ case CENTLEFT:
+ case CENTCENT:
+ case CENTRIGHT:
+ printf("\\v'0.85n'"); /* down half */
+ break;
+
+ case TOPLEFT:
+ case TOPCENT:
+ case TOPRIGHT:
+ printf("\\v'1.7n'"); /* down whole */
+ }
+
+ switch (justify) {
+ /* local horizontal motions */
+ case BOTCENT:
+ case CENTCENT:
+ case TOPCENT:
+ printf("\\h'-\\w'\\*(g9'u/2u'"); /* back half */
+ break;
+
+ case BOTRIGHT:
+ case CENTRIGHT:
+ case TOPRIGHT:
+ printf("\\h'-\\w'\\*(g9'u'"); /* back whole */
+ }
+
+ printf("\\&\\*(g9\n"); /* now print the text. */
+ printf(".sp |\\n(g8u\n"); /* restore vertical position */
+ lasty = savelasty; /* vertical position restored to */
+ lastx = xleft; /* where it was before text, also */
+ /* horizontal is at left */
+} /* end HGPutText */
+
+
+/*--------------------------------------------------------------------*
+ | Routine: doarc (center_point, start_point, angle)
+ |
+ | Results: Produces either drawarc command or a drawcircle command
+ | depending on the angle needed to draw through.
+ *--------------------------------------------------------------------*/
+
+void
+doarc(POINT cp,
+ POINT sp,
+ int angle)
+{
+ if (angle) /* arc with angle */
+ HGArc((int) (cp.x * troffscale), (int) (cp.y * troffscale),
+ (int) (sp.x * troffscale), (int) (sp.y * troffscale), angle);
+ else /* a full circle (angle == 0) */
+ HGArc((int) (cp.x * troffscale), (int) (cp.y * troffscale),
+ (int) (sp.x * troffscale), (int) (sp.y * troffscale), 0);
+}
+
+
+/*--------------------------------------------------------------------*
+ | Routine: HGSetFont (font_number, Point_size)
+ |
+ | Results: ALWAYS outputs a .ft and .ps directive to troff. This
+ | is done because someone may change stuff inside a text
+ | string. Changes thickness back to default thickness.
+ | Default thickness depends on font and point size.
+ *--------------------------------------------------------------------*/
+
+void
+HGSetFont(int font,
+ int size)
+{
+ printf(".ft %s\n"
+ ".ps %d\n", tfont[font - 1], tsize[size - 1]);
+ linethickness = DEFTHICK;
+}
+
+
+/*--------------------------------------------------------------------*
+ | Routine: HGSetBrush (line_mode)
+ |
+ | Results: Generates the troff commands to set up the line width
+ | and style of subsequent lines. Does nothing if no
+ | change is needed.
+ |
+ | Side Efct: Sets 'linmode' and 'linethickness'.
+ *--------------------------------------------------------------------*/
+
+void
+HGSetBrush(int mode)
+{
+ int printed = 0;
+
+ if (linmod != style[--mode]) {
+ /* Groff doesn't understand \Ds, so we take it out */
+ /* printf ("\\D's %du'", linmod = style[mode]); */
+ linmod = style[mode];
+ printed = 1;
+ }
+ if (linethickness != thick[mode]) {
+ linethickness = thick[mode];
+ printf("\\h'-%.2fp'\\D't %.2fp'", linethickness, linethickness);
+ printed = 1;
+ }
+ if (printed)
+ cr();
+}
+
+
+/*--------------------------------------------------------------------*
+ | Routine: deltax (x_destination)
+ |
+ | Results: Scales and outputs a number for delta x (with a leading
+ | space) given 'lastx' and x_destination.
+ |
+ | Side Efct: Resets 'lastx' to x_destination.
+ *--------------------------------------------------------------------*/
+
+void
+deltax(double x)
+{
+ int ix = (int) (x * troffscale);
+
+ printf(" %du", ix - lastx);
+ lastx = ix;
+}
+
+
+/*--------------------------------------------------------------------*
+ | Routine: deltay (y_destination)
+ |
+ | Results: Scales and outputs a number for delta y (with a leading
+ | space) given 'lastyline' and y_destination.
+ |
+ | Side Efct: Resets 'lastyline' to y_destination. Since 'line'
+ | vertical motions don't affect 'page' ones, 'lasty' isn't
+ | updated.
+ *--------------------------------------------------------------------*/
+
+void
+deltay(double y)
+{
+ int iy = (int) (y * troffscale);
+
+ printf(" %du", iy - lastyline);
+ lastyline = iy;
+}
+
+
+/*--------------------------------------------------------------------*
+ | Routine: tmove2 (px, py)
+ |
+ | Results: Produces horizontal and vertical moves for troff given
+ | the pair of points to move to and knowing the current
+ | position. Also puts out a horizontal move to start the
+ | line. This is a variation without the .sp command.
+ *--------------------------------------------------------------------*/
+
+void
+tmove2(int px,
+ int py)
+{
+ int dx;
+ int dy;
+
+ if ((dy = py - lasty)) {
+ printf("\\v'%du'", dy);
+ }
+ lastyline = lasty = py; /* lasty is always set to current */
+ if ((dx = px - lastx)) {
+ printf("\\h'%du'", dx);
+ lastx = px;
+ }
+}
+
+
+/*--------------------------------------------------------------------*
+ | Routine: tmove (point_pointer)
+ |
+ | Results: Produces horizontal and vertical moves for troff given
+ | the pointer of a point to move to and knowing the
+ | current position. Also puts out a horizontal move to
+ | start the line.
+ *--------------------------------------------------------------------*/
+
+void
+tmove(POINT * ptr)
+{
+ int ix = (int) (ptr->x * troffscale);
+ int iy = (int) (ptr->y * troffscale);
+ int dx;
+ int dy;
+
+ if ((dy = iy - lasty)) {
+ printf(".sp %du\n", dy);
+ }
+ lastyline = lasty = iy; /* lasty is always set to current */
+ if ((dx = ix - lastx)) {
+ printf("\\h'%du'", dx);
+ lastx = ix;
+ }
+}
+
+
+/*--------------------------------------------------------------------*
+ | Routine: cr ( )
+ |
+ | Results: Ends off an input line. '.sp -1' is also added to
+ | counteract the vertical move done at the end of text
+ | lines.
+ |
+ | Side Efct: Sets 'lastx' to 'xleft' for troff's return to left
+ | margin.
+ *--------------------------------------------------------------------*/
+
+void
+cr()
+{
+ printf("\n.sp -1\n");
+ lastx = xleft;
+}
+
+
+/*--------------------------------------------------------------------*
+ | Routine: line ( )
+ |
+ | Results: Draws a single solid line to (x,y).
+ *--------------------------------------------------------------------*/
+
+void
+line(int px,
+ int py)
+{
+ printf("\\D'l");
+ printf(" %du", px - lastx);
+ printf(" %du'", py - lastyline);
+ lastx = px;
+ lastyline = lasty = py;
+}
+
+
+/*--------------------------------------------------------------------*
+ | Routine: drawwig (ptr, type)
+ |
+ | Results: The point sequence found in the structure pointed by ptr
+ | is placed in integer arrays for further manipulation by
+ | the existing routing. With the corresponding type
+ | parameter, either picurve or HGCurve are called.
+ *--------------------------------------------------------------------*/
+
+void
+drawwig(POINT * ptr,
+ int type)
+{
+ int npts; /* point list index */
+ int x[MAXPOINTS], y[MAXPOINTS]; /* point list */
+
+ for (npts = 1; !Nullpoint(ptr); ptr = PTNextPoint(ptr), npts++) {
+ x[npts] = (int) (ptr->x * troffscale);
+ y[npts] = (int) (ptr->y * troffscale);
+ }
+ if (--npts) {
+ if (type == CURVE) /* Use the 2 different types of curves */
+ HGCurve(&x[0], &y[0], npts);
+ else
+ picurve(&x[0], &y[0], npts);
+ }
+}
+
+
+/*--------------------------------------------------------------------*
+ | Routine: HGArc (xcenter, ycenter, xstart, ystart, angle)
+ |
+ | Results: This routine plots an arc centered about (cx, cy)
+ | counter-clockwise starting from the point (px, py)
+ | through 'angle' degrees. If angle is 0, a full circle
+ | is drawn. It does so by creating a draw-path around the
+ | arc whose density of points depends on the size of the
+ | arc.
+ *--------------------------------------------------------------------*/
+
+void
+HGArc(int cx,
+ int cy,
+ int px,
+ int py,
+ int angle)
+{
+ double xs, ys, resolution, fullcircle;
+ int m;
+ int mask;
+ int extent;
+ int nx;
+ int ny;
+ int length;
+ double epsilon;
+
+ xs = px - cx;
+ ys = py - cy;
+
+ length = 0;
+
+ resolution = (1.0 + groff_hypot(xs, ys) / res) * PointsPerInterval;
+ /* mask = (1 << (int) log10(resolution + 1.0)) - 1; */
+ (void) frexp(resolution, &m); /* more elegant than log10 */
+ for (mask = 1; mask < m; mask = mask << 1);
+ mask -= 1;
+
+ epsilon = 1.0 / resolution;
+ fullcircle = (2.0 * pi) * resolution;
+ if (angle == 0)
+ extent = (int) fullcircle;
+ else
+ extent = (int) (angle * fullcircle / 360.0);
+
+ HGtline(px, py);
+ while (--extent >= 0) {
+ xs += epsilon * ys;
+ nx = cx + (int) (xs + 0.5);
+ ys -= epsilon * xs;
+ ny = cy + (int) (ys + 0.5);
+ if (!(extent & mask)) {
+ HGtline(nx, ny); /* put out a point on circle */
+ if (length++ > LINELENGTH) {
+ length = 0;
+ printf("\\\n");
+ }
+ }
+ } /* end for */
+} /* end HGArc */
+
+
+/*--------------------------------------------------------------------*
+ | Routine: picurve (xpoints, ypoints, num_of_points)
+ |
+ | Results: Draws a curve delimited by (not through) the line
+ | segments traced by (xpoints, ypoints) point list. This
+ | is the 'Pic'-style curve.
+ *--------------------------------------------------------------------*/
+
+void
+picurve(int *x,
+ int *y,
+ int npts)
+{
+ int nseg; /* effective resolution for each curve */
+ int xp; /* current point (and temporary) */
+ int yp;
+ int pxp, pyp; /* previous point (to make lines from) */
+ int i; /* inner curve segment traverser */
+ int length = 0;
+ double w; /* position factor */
+ double t1, t2, t3; /* calculation temps */
+
+ if (x[1] == x[npts] && y[1] == y[npts]) {
+ x[0] = x[npts - 1]; /* if the lines' ends meet, make */
+ y[0] = y[npts - 1]; /* sure the curve meets */
+ x[npts + 1] = x[2];
+ y[npts + 1] = y[2];
+ } else { /* otherwise, make the ends of the */
+ x[0] = x[1]; /* curve touch the ending points of */
+ y[0] = y[1]; /* the line segments */
+ x[npts + 1] = x[npts];
+ y[npts + 1] = y[npts];
+ }
+
+ pxp = (x[0] + x[1]) / 2; /* make the last point pointers */
+ pyp = (y[0] + y[1]) / 2; /* point to the start of the 1st line */
+ tmove2(pxp, pyp);
+
+ for (; npts--; x++, y++) { /* traverse the line segments */
+ xp = x[0] - x[1];
+ yp = y[0] - y[1];
+ nseg = (int) groff_hypot((double) xp, (double) yp);
+ xp = x[1] - x[2];
+ yp = y[1] - y[2];
+ /* 'nseg' is the number of line */
+ /* segments that will be drawn for */
+ /* each curve segment. */
+ nseg = (int) ((double) (nseg + (int) groff_hypot((double) xp,
+ (double) yp)) /
+ res * PointsPerInterval);
+
+ for (i = 1; i < nseg; i++) {
+ w = (double) i / (double) nseg;
+ t1 = w * w;
+ t3 = t1 + 1.0 - (w + w);
+ t2 = 2.0 - (t3 + t1);
+ xp = (((int) (t1 * x[2] + t2 * x[1] + t3 * x[0])) + 1) / 2;
+ yp = (((int) (t1 * y[2] + t2 * y[1] + t3 * y[0])) + 1) / 2;
+
+ HGtline(xp, yp);
+ if (length++ > LINELENGTH) {
+ length = 0;
+ printf("\\\n");
+ }
+ }
+ }
+}
+
+
+/*--------------------------------------------------------------------*
+ | Routine: HGCurve(xpoints, ypoints, num_points)
+ |
+ | Results: This routine generates a smooth curve through a set of
+ | points. The method used is the parametric spline curve
+ | on unit knot mesh described in 'Spline Curve Techniques'
+ | by Patrick Baudelaire, Robert Flegal, and Robert Sproull
+ | -- Xerox Parc.
+ *--------------------------------------------------------------------*/
+
+void
+HGCurve(int *x,
+ int *y,
+ int numpoints)
+{
+ double h[MAXPOINTS], dx[MAXPOINTS], dy[MAXPOINTS];
+ double d2x[MAXPOINTS], d2y[MAXPOINTS], d3x[MAXPOINTS], d3y[MAXPOINTS];
+ double t, t2, t3;
+ int j;
+ int k;
+ int nx;
+ int ny;
+ int lx, ly;
+ int length = 0;
+
+ lx = x[1];
+ ly = y[1];
+ tmove2(lx, ly);
+
+ /*
+ * Solve for derivatives of the curve at each point separately for x
+ * and y (parametric).
+ */
+ Parameterize(x, y, h, numpoints);
+
+ /* closed curve */
+ if ((x[1] == x[numpoints]) && (y[1] == y[numpoints])) {
+ PeriodicSpline(h, x, dx, d2x, d3x, numpoints);
+ PeriodicSpline(h, y, dy, d2y, d3y, numpoints);
+ } else {
+ NaturalEndSpline(h, x, dx, d2x, d3x, numpoints);
+ NaturalEndSpline(h, y, dy, d2y, d3y, numpoints);
+ }
+
+ /*
+ * Generate the curve using the above information and
+ * PointsPerInterval vectors between each specified knot.
+ */
+
+ for (j = 1; j < numpoints; ++j) {
+ if ((x[j] == x[j + 1]) && (y[j] == y[j + 1]))
+ continue;
+ for (k = 0; k <= PointsPerInterval; ++k) {
+ t = (double) k *h[j] / (double) PointsPerInterval;
+ t2 = t * t;
+ t3 = t * t * t;
+ nx = x[j] + (int) (t * dx[j] + t2 * d2x[j] / 2 + t3 * d3x[j] / 6);
+ ny = y[j] + (int) (t * dy[j] + t2 * d2y[j] / 2 + t3 * d3y[j] / 6);
+ HGtline(nx, ny);
+ if (length++ > LINELENGTH) {
+ length = 0;
+ printf("\\\n");
+ }
+ } /* end for k */
+ } /* end for j */
+} /* end HGCurve */
+
+
+/*--------------------------------------------------------------------*
+ | Routine: Parameterize (xpoints, ypoints, hparams, num_points)
+ |
+ | Results: This routine calculates parametric values for use in
+ | calculating curves. The parametric values are returned
+ | in the array h. The values are an approximation of
+ | cumulative arc lengths of the curve (uses cord length).
+ | For additional information, see paper cited below.
+ *--------------------------------------------------------------------*/
+
+void
+Parameterize(int x[],
+ int y[],
+ double h[],
+ int n)
+{
+ int dx;
+ int dy;
+ int i;
+ int j;
+ double u[MAXPOINTS];
+
+ for (i = 1; i <= n; ++i) {
+ u[i] = 0;
+ for (j = 1; j < i; j++) {
+ dx = x[j + 1] - x[j];
+ dy = y[j + 1] - y[j];
+ /* Here was overflowing, so I changed it. */
+ /* u[i] += sqrt ((double) (dx * dx + dy * dy)); */
+ u[i] += groff_hypot((double) dx, (double) dy);
+ }
+ }
+ for (i = 1; i < n; ++i)
+ h[i] = u[i + 1] - u[i];
+} /* end Parameterize */
+
+
+/*--------------------------------------------------------------------*
+ | Routine: PeriodicSpline (h, z, dz, d2z, d3z, npoints)
+ |
+ | Results: This routine solves for the cubic polynomial to fit a
+ | spline curve to the points specified by the list of
+ | values. The curve generated is periodic. The
+ | algorithms for this curve are from the 'Spline Curve
+ | Techniques' paper cited above.
+ *--------------------------------------------------------------------*/
+
+void
+PeriodicSpline(double h[], /* parameterization */
+ int z[], /* point list */
+ double dz[], /* to return the 1st derivative */
+ double d2z[], /* 2nd derivative */
+ double d3z[], /* 3rd derivative */
+ int npoints) /* number of valid points */
+{
+ double d[MAXPOINTS];
+ double deltaz[MAXPOINTS], a[MAXPOINTS], b[MAXPOINTS];
+ double c[MAXPOINTS], r[MAXPOINTS], s[MAXPOINTS];
+ int i;
+
+ /* step 1 */
+ for (i = 1; i < npoints; ++i) {
+ deltaz[i] = h[i] ? ((double) (z[i + 1] - z[i])) / h[i] : 0;
+ }
+ h[0] = h[npoints - 1];
+ deltaz[0] = deltaz[npoints - 1];
+
+ /* step 2 */
+ for (i = 1; i < npoints - 1; ++i) {
+ d[i] = deltaz[i + 1] - deltaz[i];
+ }
+ d[0] = deltaz[1] - deltaz[0];
+
+ /* step 3a */
+ a[1] = 2 * (h[0] + h[1]);
+ b[1] = d[0];
+ c[1] = h[0];
+ for (i = 2; i < npoints - 1; ++i) {
+ a[i] = 2 * (h[i - 1] + h[i]) -
+ pow((double) h[i - 1], (double) 2.0) / a[i - 1];
+ b[i] = d[i - 1] - h[i - 1] * b[i - 1] / a[i - 1];
+ c[i] = -h[i - 1] * c[i - 1] / a[i - 1];
+ }
+
+ /* step 3b */
+ r[npoints - 1] = 1;
+ s[npoints - 1] = 0;
+ for (i = npoints - 2; i > 0; --i) {
+ r[i] = -(h[i] * r[i + 1] + c[i]) / a[i];
+ s[i] = (6 * b[i] - h[i] * s[i + 1]) / a[i];
+ }
+
+ /* step 4 */
+ d2z[npoints - 1] = (6 * d[npoints - 2] - h[0] * s[1]
+ - h[npoints - 1] * s[npoints - 2])
+ / (h[0] * r[1] + h[npoints - 1] * r[npoints - 2]
+ + 2 * (h[npoints - 2] + h[0]));
+ for (i = 1; i < npoints - 1; ++i) {
+ d2z[i] = r[i] * d2z[npoints - 1] + s[i];
+ }
+ d2z[npoints] = d2z[1];
+
+ /* step 5 */
+ for (i = 1; i < npoints; ++i) {
+ dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i + 1]) / 6;
+ d3z[i] = h[i] ? (d2z[i + 1] - d2z[i]) / h[i] : 0;
+ }
+} /* end PeriodicSpline */
+
+
+/*--------------------------------------------------------------------
+ | Routine: NaturalEndSpline (h, z, dz, d2z, d3z, npoints)
+ |
+ | Results: This routine solves for the cubic polynomial to fit a
+ | spline curve the points specified by the list of values.
+ | The algorithms for this curve are from the 'Spline Curve
+ | Techniques' paper cited above.
+ *--------------------------------------------------------------------*/
+
+void
+NaturalEndSpline(double h[], /* parameterization */
+ int z[], /* Point list */
+ double dz[], /* to return the 1st derivative */
+ double d2z[], /* 2nd derivative */
+ double d3z[], /* 3rd derivative */
+ int npoints) /* number of valid points */
+{
+ double d[MAXPOINTS];
+ double deltaz[MAXPOINTS], a[MAXPOINTS], b[MAXPOINTS];
+ int i;
+
+ /* step 1 */
+ for (i = 1; i < npoints; ++i) {
+ deltaz[i] = h[i] ? ((double) (z[i + 1] - z[i])) / h[i] : 0;
+ }
+ deltaz[0] = deltaz[npoints - 1];
+
+ /* step 2 */
+ for (i = 1; i < npoints - 1; ++i) {
+ d[i] = deltaz[i + 1] - deltaz[i];
+ }
+ d[0] = deltaz[1] - deltaz[0];
+
+ /* step 3 */
+ a[0] = 2 * (h[2] + h[1]);
+ b[0] = d[1];
+ for (i = 1; i < npoints - 2; ++i) {
+ a[i] = 2 * (h[i + 1] + h[i + 2]) -
+ pow((double) h[i + 1], (double) 2.0) / a[i - 1];
+ b[i] = d[i + 1] - h[i + 1] * b[i - 1] / a[i - 1];
+ }
+
+ /* step 4 */
+ d2z[npoints] = d2z[1] = 0;
+ for (i = npoints - 1; i > 1; --i) {
+ d2z[i] = (6 * b[i - 2] - h[i] * d2z[i + 1]) / a[i - 2];
+ }
+
+ /* step 5 */
+ for (i = 1; i < npoints; ++i) {
+ dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i + 1]) / 6;
+ d3z[i] = h[i] ? (d2z[i + 1] - d2z[i]) / h[i] : 0;
+ }
+} /* end NaturalEndSpline */
+
+
+/*--------------------------------------------------------------------*
+ | Routine: change (x_position, y_position, visible_flag)
+ |
+ | Results: As HGtline passes from the invisible to visible (or vice
+ | versa) portion of a line, change is called to either
+ | draw the line, or initialize the beginning of the next
+ | one. Change calls line to draw segments if visible_flag
+ | is set (which means we're leaving a visible area).
+ *--------------------------------------------------------------------*/
+
+void
+change(int x,
+ int y,
+ int vis)
+{
+ static int length = 0;
+
+ if (vis) { /* leaving a visible area, draw it. */
+ line(x, y);
+ if (length++ > LINELENGTH) {
+ length = 0;
+ printf("\\\n");
+ }
+ } else { /* otherwise entering one; remember */
+ /* beginning */
+ tmove2(x, y);
+ }
+}
+
+
+/*--------------------------------------------------------------------*
+ | Routine: HGtline (xstart, ystart, xend, yend)
+ |
+ | Results: Draws a line from current position to (x1,y1) using
+ | line(x1, y1) to place individual segments of dotted or
+ | dashed lines.
+ *--------------------------------------------------------------------*/
+
+void
+HGtline(int x_1,
+ int y_1)
+{
+ int x_0 = lastx;
+ int y_0 = lasty;
+ int dx;
+ int dy;
+ int oldcoord;
+ int res1;
+ int visible;
+ int res2;
+ int xinc;
+ int yinc;
+ int dotcounter;
+
+ if (linmod == SOLID) {
+ line(x_1, y_1);
+ return;
+ }
+
+ /* for handling different resolutions */
+ dotcounter = linmod << dotshifter;
+
+ xinc = 1;
+ yinc = 1;
+ if ((dx = x_1 - x_0) < 0) {
+ xinc = -xinc;
+ dx = -dx;
+ }
+ if ((dy = y_1 - y_0) < 0) {
+ yinc = -yinc;
+ dy = -dy;
+ }
+ res1 = 0;
+ res2 = 0;
+ visible = 0;
+ if (dx >= dy) {
+ oldcoord = y_0;
+ while (x_0 != x_1) {
+ if ((x_0 & dotcounter) && !visible) {
+ change(x_0, y_0, 0);
+ visible = 1;
+ } else if (visible && !(x_0 & dotcounter)) {
+ change(x_0 - xinc, oldcoord, 1);
+ visible = 0;
+ }
+ if (res1 > res2) {
+ oldcoord = y_0;
+ res2 += dx - res1;
+ res1 = 0;
+ y_0 += yinc;
+ }
+ res1 += dy;
+ x_0 += xinc;
+ }
+ } else {
+ oldcoord = x_0;
+ while (y_0 != y_1) {
+ if ((y_0 & dotcounter) && !visible) {
+ change(x_0, y_0, 0);
+ visible = 1;
+ } else if (visible && !(y_0 & dotcounter)) {
+ change(oldcoord, y_0 - yinc, 1);
+ visible = 0;
+ }
+ if (res1 > res2) {
+ oldcoord = x_0;
+ res2 += dy - res1;
+ res1 = 0;
+ x_0 += xinc;
+ }
+ res1 += dx;
+ y_0 += yinc;
+ }
+ }
+ if (visible)
+ change(x_1, y_1, 1);
+ else
+ change(x_1, y_1, 0);
+}
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/preproc/grn/hpoint.cpp b/src/preproc/grn/hpoint.cpp
new file mode 100644
index 0000000..5ef0c0a
--- /dev/null
+++ b/src/preproc/grn/hpoint.cpp
@@ -0,0 +1,59 @@
+/* Last non-groff version: hpoint.c 1.1 84/10/08 */
+
+/*
+ * This file contains routines for manipulating the point data
+ * structures for the gremlin picture editor.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include "gprint.h"
+
+/* imports from main.cpp */
+extern void *grnmalloc(size_t size, const char *what);
+
+/*
+ * Return pointer to empty point list.
+ */
+POINT *
+PTInit()
+{
+ return ((POINT *) NULL);
+}
+
+
+/*
+ * This routine creates a new point with coordinates x and y and links
+ * it into the point list.
+ */
+POINT *
+PTMakePoint(double x,
+ double y,
+ POINT **pplist)
+{
+ POINT *pt;
+
+ if (Nullpoint(pt = *pplist)) { /* empty list */
+ *pplist = (POINT *) grnmalloc(sizeof(POINT), "initial point");
+ pt = *pplist;
+ } else {
+ while (!Nullpoint(pt->nextpt))
+ pt = pt->nextpt;
+ pt->nextpt = (POINT *) grnmalloc(sizeof(POINT), "subsequent point");
+ pt = pt->nextpt;
+ }
+
+ pt->x = x;
+ pt->y = y;
+ pt->nextpt = PTInit();
+ return (pt);
+} /* end PTMakePoint */
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/preproc/grn/main.cpp b/src/preproc/grn/main.cpp
new file mode 100644
index 0000000..6d6d586
--- /dev/null
+++ b/src/preproc/grn/main.cpp
@@ -0,0 +1,977 @@
+/* Last non-groff version: main.c 1.23 (Berkeley) 85/08/05
+ *
+ * Adapted to GNU troff by Daniel Senderowicz 99/12/29.
+ *
+ * Further refinements by Werner Lemberg 00/02/20.
+ *
+ *
+ * This file contains the main and file system dependent routines for
+ * processing gremlin files into troff input. The program watches input
+ * go by to standard output, only interpreting things between .GS and
+ * .GE lines. Default values (font, size, scale, thickness) may be
+ * overridden with a 'default' command and are further overridden by
+ * commands in the input.
+ *
+ * Inside the GS and GE, commands are accepted to reconfigure the
+ * picture. At most one command may reside on each line, and each
+ * command is followed by a parameter separated by white space. The
+ * commands are as follows, and may be abbreviated down to one character
+ * (with exception of 'scale' and 'stipple' down to "sc" and "st") and
+ * may be upper or lower case.
+ *
+ * default - Make all settings in the current
+ * .GS/.GE the global defaults.
+ * Height, width and file are NOT
+ * saved.
+ * 1, 2, 3, 4 - Set size 1, 2, 3, or 4 (followed
+ * by an integer point size).
+ * roman, italics, bold, special - Set gremlin's fonts to any other
+ * troff font (1 or 2 characters).
+ * stipple, l - Use a stipple font for polygons.
+ * Arg is troff font name. No
+ * default. Can use only one stipple
+ * font per picture. (See below for
+ * stipple font index.)
+ * scale, x - Scale is IN ADDITION to the global
+ * scale factor from the default.
+ * pointscale - Turn on scaling point sizes to
+ * match 'scale' commands. (Optional
+ * operand 'off' to turn it off.)
+ * narrow, medium, thick - Set widths of lines.
+ * file - Set the file name to read the
+ * gremlin picture from. If the file
+ * isn't in the current directory,
+ * the gremlin library is tried.
+ * width, height - These two commands override any
+ * scaling factor that is in effect,
+ * and forces the picture to fit into
+ * either the height or width
+ * specified, whichever makes the
+ * picture smaller. The operand for
+ * these two commands is a
+ * floating-point number in units of
+ * inches.
+ * l<nn> (integer <nn>) - Set association between stipple
+ * <nn> and a stipple 'character'.
+ * <nn> must be in the range 0 to
+ * NSTIPPLES (16) inclusive. The
+ * integer operand is an index in the
+ * stipple font selected. Valid cf
+ * (cifplot) indices are 1-32
+ * (although 24 is not defined),
+ * valid ug (unigrafix) indices are
+ * 1-14, and valid gs (gray scale)
+ * indices are 0-16. Nonetheless,
+ * any number between 0 and 255 is
+ * accepted since new stipple fonts
+ * may be added. An integer operand
+ * is required.
+ *
+ * Troff number registers used: g1 through g9. g1 is the width of the
+ * picture, and g2 is the height. g3, and g4, save information, g8 and
+ * g9 are used for text processing and g5-g7 are reserved.
+ */
+
+
+#include "lib.h"
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h> // errno
+#include "gprint.h"
+
+#include "device.h"
+#include "font.h"
+#include "searchpath.h"
+#include "macropath.h"
+
+#include "errarg.h"
+#include "error.h"
+#include "defs.h"
+
+extern "C" const char *Version_string;
+
+/* database imports */
+
+extern void HGPrintElt(ELT *element, int baseline);
+extern ELT *DBInit();
+extern ELT *DBRead(FILE *file);
+extern POINT *PTInit();
+extern POINT *PTMakePoint(double x, double y, POINT **pplist);
+
+#define INIT_FILE_SIZE 50 /* Initial sz of file array from cmd line. */
+#define FILE_SIZE_INCR 50 /* Amount to increase array of files by. */
+
+#define SUN_SCALEFACTOR 0.70
+
+/* #define DEFSTIPPLE "gs" */
+#define DEFSTIPPLE "cf"
+/*
+ * This grn implementation emits '.st' requests to control stipple
+ * effects, but groff does not (currently) support any such request.
+ *
+ * This hack disables the emission of such requests, without destroying
+ * the infrastructure necessary to support the feature in the future; to
+ * enable the emission of '.st' requests, at a future date when groff
+ * can support them, simply rewrite the following #define as:
+ *
+ * #define USE_ST_REQUEST stipple
+ *
+ * with accompanying comment: "emit '.st' requests as required".
+ */
+#define USE_ST_REQUEST 0 /* never emit '.st' requests */
+
+#define MAXINLINE 100 /* input line length */
+
+#define SCREENtoINCH 0.02 /* scaling factor, screen to inches */
+
+#define BIG 999999999999.0 /* unwieldy large floating number */
+
+
+/* static char sccsid[] = "@(#) (Berkeley) 8/5/85, 12/28/99"; */
+
+int res; /* the printer's resolution goes here */
+
+int dotshifter; /* for the length of dotted curves */
+
+double linethickness; /* brush styles */
+int linmod;
+int lastx; /* point registers for printing */
+int lasty; /* elements */
+int lastyline; /* A line's vertical position is NOT */
+ /* the same after that line is over, */
+ /* so for a line of drawing commands, */
+ /* vertical spacing is kept in */
+ /* lastyline. */
+
+/* These are the default fonts, sizes, line styles, */
+/* and thicknesses. They can be modified from a */
+/* 'default' command and are reset each time the */
+/* start of a picture (.GS) is found. */
+
+const char *deffont[] =
+{"R", "I", "B", "S"};
+int defsize[] =
+{10, 16, 24, 36};
+/* #define BASE_THICKNESS 1.0 */
+#define BASE_THICKNESS 0.15
+double defthick[STYLES] =
+{1 * BASE_THICKNESS,
+ 1 * BASE_THICKNESS,
+ 5 * BASE_THICKNESS,
+ 1 * BASE_THICKNESS,
+ 1 * BASE_THICKNESS,
+ 3 * BASE_THICKNESS};
+
+/* int cf_stipple_index[NSTIPPLES + 1] = */
+/* {0, 1, 3, 12, 14, 16, 19, 21, 23}; */
+/* a logarithmic scale looks better than a linear one for gray shades */
+/* */
+/* int other_stipple_index[NSTIPPLES + 1] = */
+/* {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; */
+
+int cf_stipple_index[NSTIPPLES + 1] =
+{0, 18, 32, 56, 100, 178, 316, 562, 1000}; /* only 1-8 used */
+int other_stipple_index[NSTIPPLES + 1] =
+{0, 62, 125, 187, 250, 312, 375, 437, 500,
+ 562, 625, 687, 750, 812, 875, 937, 1000};
+
+/* int *defstipple_index = other_stipple_index; */
+int *defstipple_index = cf_stipple_index;
+
+int style[STYLES] =
+{DOTTED, DOTDASHED, SOLID, DASHED, SOLID, SOLID};
+double scale = 1.0; /* no scaling, default */
+int defpoint = 0; /* flag for point size scaling */
+char *defstipple = (char *) 0;
+enum E {
+ OUTLINE, FILL, BOTH
+} polyfill;
+
+/* flag to control filling of polygons */
+
+double adj1 = 0.0;
+double adj2 = 0.0;
+double adj3 = 0.0;
+double adj4 = 0.0;
+
+double thick[STYLES]; /* thicknesses set by defaults, then */
+ /* by commands */
+char *tfont[FONTS]; /* fonts originally set to deffont */
+ /* values, then optionally changed by */
+int tsize[SIZES]; /* commands inside grn */
+int stipple_index[NSTIPPLES + 1]; /* stipple font file indices */
+char *stipple;
+
+double xscale; /* scaling factor from individual pictures */
+double troffscale; /* scaling factor at output time */
+
+double width; /* user-request maximum width for picture */
+ /* (in inches) */
+double height; /* user-request height */
+int pointscale; /* flag for point size scaling */
+int setdefault; /* flag for a .GS/.GE to remember all */
+ /* settings */
+int sflag; /* -s flag: sort order (do polyfill first) */
+
+double toppoint; /* remember the picture */
+double bottompoint; /* bounds in these variables */
+double leftpoint;
+double rightpoint;
+
+int ytop; /* these are integer versions of the above */
+int ybottom; /* so not to convert each time they're used */
+int xleft;
+int xright;
+
+static int linenum = 0; /* line number of troff input file */
+char inputline[MAXINLINE]; /* spot to filter through the file */
+char *c1 = inputline; /* c1, c2, and c3 will be used to */
+char *c2 = inputline + 1; /* hunt for lines that begin with */
+char *c3 = inputline + 2; /* '.GS' by looking individually */
+char *c4 = inputline + 3; /* needed for compatibility mode */
+char GScommand[MAXINLINE]; /* put user's '.GS' command line here */
+char gremlinfile[MAXINLINE]; /* filename to use for a picture */
+int SUNFILE = FALSE; /* TRUE if SUN gremlin file */
+int compatibility_flag = FALSE; /* TRUE if in compatibility mode */
+
+
+void getres();
+int doinput(FILE *fp);
+void conv(FILE *fp, int baseline);
+void savestate();
+int has_polygon(ELT *elist);
+void interpret(char *line);
+
+void *
+grnmalloc(size_t size,
+ const char *what)
+{
+ void *ptr = 0;
+ ptr = malloc(size);
+ if (!ptr) {
+ fatal("memory allocation failed for %1: %2", what, strerror(errno));
+ }
+ return ptr;
+}
+
+void
+usage(FILE *stream)
+{
+ fprintf(stream,
+ "usage: %s [-Cs] [-M dir] [-F dir] [-T dev] [file ...]\n"
+ "usage: %s {-v | --version}\n"
+ "usage: %s --help\n",
+ program_name, program_name, program_name);
+}
+
+
+/* Add a new file entry in the array, expanding array if needs be. */
+
+char **
+add_file(char **file,
+ char *new_file,
+ int *count,
+ int *cur_size)
+{
+ if (*count >= *cur_size) {
+ *cur_size += FILE_SIZE_INCR;
+ file = (char **) realloc(file, *cur_size * sizeof(char *));
+ if (file == NULL) {
+ fatal("unable to extend file array");
+ }
+ }
+ file[*count] = new_file;
+ *count += 1;
+
+ return file;
+}
+
+
+/*--------------------------------------------------------------------*
+ | Routine: main (argument_count, argument_pointer)
+ |
+ | Results: Parses the command line, accumulating input file names,
+ | then reads the inputs, passing it directly to output
+ | until a '.GS' line is read. Main then passes control to
+ | 'conv' to do the gremlin file conversions.
+ *--------------------------------------------------------------------*/
+
+int
+main(int argc,
+ char **argv)
+{
+ setlocale(LC_NUMERIC, "C");
+ program_name = argv[0];
+ FILE *fp;
+ int k;
+ char c;
+ int gfil = 0;
+ char **file = NULL;
+ int file_cur_size = INIT_FILE_SIZE;
+ char *operand(int *argcp, char ***argvp);
+
+ file = (char **) grnmalloc(file_cur_size * sizeof(char *),
+ "file array");
+ while (--argc) {
+ if (**++argv != '-')
+ file = add_file(file, *argv, &gfil, &file_cur_size);
+ else
+ switch (c = (*argv)[1]) {
+
+ case 0:
+ file = add_file(file, NULL, &gfil, &file_cur_size);
+ break;
+
+ case 'C': /* compatibility mode */
+ compatibility_flag = TRUE;
+ break;
+
+ case 'F': /* font path to find DESC */
+ font::command_line_font_dir(operand(&argc, &argv));
+ break;
+
+ case 'T': /* final output typesetter name */
+ device = operand(&argc, &argv);
+ break;
+
+ case 'M': /* set library directory */
+ macro_path.command_line_dir(operand(&argc, &argv));
+ break;
+
+ case 's': /* preserve order of elements */
+ sflag = 1;
+ break;
+
+ case '-':
+ if (strcmp(*argv,"--version")==0) {
+ case 'v':
+ printf("GNU grn (groff) version %s\n", Version_string);
+ exit(0);
+ break;
+ }
+ if (strcmp(*argv,"--help")==0) {
+ case '?':
+ usage(stdout);
+ exit(0);
+ break;
+ }
+ // fallthrough
+ default:
+ error("unknown switch: %1", c);
+ usage(stderr);
+ exit(1);
+ }
+ }
+
+ getres(); /* set the resolution for an output device */
+
+ if (gfil == 0) { /* no filename, use standard input */
+ file[0] = NULL;
+ gfil++;
+ }
+
+ for (k = 0; k < gfil; k++) {
+ if (file[k] != NULL) {
+ if ((fp = fopen(file[k], "r")) == NULL)
+ fatal("can't open %1", file[k]);
+ } else
+ fp = stdin;
+
+ while (doinput(fp)) {
+ if (*c1 == '.' && *c2 == 'G' && *c3 == 'S') {
+ if (compatibility_flag ||
+ *c4 == '\n' || *c4 == ' ' || *c4 == '\0')
+ conv(fp, linenum);
+ else
+ fputs(inputline, stdout);
+ } else
+ fputs(inputline, stdout);
+ }
+ }
+
+ return 0;
+}
+
+
+/*--------------------------------------------------------------------*
+ | Routine: char * operand (& argc, & argv)
+ |
+ | Results: Returns address of the operand given with a command-line
+ | option. It uses either '-Xoperand' or '-X operand',
+ | whichever is present. The program is terminated if no
+ | option is present.
+ |
+ | Side Efct: argc and argv are updated as necessary.
+ *--------------------------------------------------------------------*/
+
+char *
+operand(int *argcp,
+ char ***argvp)
+{
+ if ((**argvp)[2])
+ return (**argvp + 2); /* operand immediately follows */
+ if ((--*argcp) <= 0) { /* no operand */
+ error("command-line option operand missing.");
+ exit(8);
+ }
+ return (*(++(*argvp))); /* operand is next word */
+}
+
+
+/*--------------------------------------------------------------------*
+ | Routine: getres ()
+ |
+ | Results: Sets 'res' to the resolution of the output device.
+ *--------------------------------------------------------------------*/
+
+void
+getres()
+{
+ int linepiece;
+
+ if (0 /* nullptr */ == font::load_desc())
+ fatal("cannot load 'DESC' description file for device '%1'",
+ device);
+
+ res = font::res;
+
+ /* Correct the brush thicknesses based on res */
+ /* if (res >= 256) {
+ defthick[0] = res >> 8;
+ defthick[1] = res >> 8;
+ defthick[2] = res >> 4;
+ defthick[3] = res >> 8;
+ defthick[4] = res >> 8;
+ defthick[5] = res >> 6;
+ } */
+
+ linepiece = res >> 9;
+ for (dotshifter = 0; linepiece; dotshifter++)
+ linepiece = linepiece >> 1;
+}
+
+
+/*--------------------------------------------------------------------*
+ | Routine: int doinput (file_pointer)
+ |
+ | Results: A line of input is read into 'inputline'.
+ |
+ | Side Efct: "linenum" is incremented.
+ |
+ | Bugs: Lines longer than MAXINLINE are NOT checked, except for
+ | updating 'linenum'.
+ *--------------------------------------------------------------------*/
+
+int
+doinput(FILE *fp)
+{
+ if (fgets(inputline, MAXINLINE, fp) == NULL)
+ return 0;
+ if (strchr(inputline, '\n')) /* ++ only if it's a complete line */
+ linenum++;
+ return 1;
+}
+
+
+/*--------------------------------------------------------------------*
+ | Routine: initpic ( )
+ |
+ | Results: Sets all parameters to the normal defaults, possibly
+ | overridden by a setdefault command. Initialize the
+ | picture variables, and output the startup commands to
+ | troff to begin the picture.
+ *--------------------------------------------------------------------*/
+
+void
+initpic()
+{
+ int i;
+
+ for (i = 0; i < STYLES; i++) { /* line thickness defaults */
+ thick[i] = defthick[i];
+ }
+ for (i = 0; i < FONTS; i++) { /* font name defaults */
+ tfont[i] = (char *)deffont[i];
+ }
+ for (i = 0; i < SIZES; i++) { /* font size defaults */
+ tsize[i] = defsize[i];
+ }
+ for (i = 0; i <= NSTIPPLES; i++) { /* stipple font file default */
+ /* indices */
+ stipple_index[i] = defstipple_index[i];
+ }
+ stipple = defstipple;
+
+ gremlinfile[0] = 0; /* filename is 'null' */
+ setdefault = 0; /* not the default settings (yet) */
+
+ toppoint = BIG; /* set the picture bounds out */
+ bottompoint = -BIG; /* of range so they'll be set */
+ leftpoint = BIG; /* by 'savebounds' on input */
+ rightpoint = -BIG;
+
+ pointscale = defpoint;/* flag for scaling point sizes default */
+ xscale = scale; /* default scale of individual pictures */
+ width = 0.0; /* size specifications input by user */
+ height = 0.0;
+
+ linethickness = DEFTHICK; /* brush styles */
+ linmod = DEFSTYLE;
+}
+
+
+/*--------------------------------------------------------------------*
+ | Routine: conv (file_pointer, starting_line)
+ |
+ | Results: At this point, we just passed a '.GS' line in the input
+ | file. conv reads the input and calls 'interpret' to
+ | process commands, gathering up information until a '.GE'
+ | line is found. It then calls 'HGPrint' to do the
+ | translation of the gremlin file to troff commands.
+ *--------------------------------------------------------------------*/
+
+void
+conv(FILE *fp,
+ int baseline)
+{
+ FILE *gfp = NULL; /* input file pointer */
+ int done = 0; /* flag to remember if finished */
+ ELT *e; /* current element pointer */
+ ELT *PICTURE; /* whole picture data base pointer */
+ double temp; /* temporary calculating area */
+ /* POINT ptr; */ /* coordinates of a point to pass to 'mov' */
+ /* routine */
+ int flyback; /* flag 'want to end up at the top of the */
+ /* picture?' */
+ int compat; /* test character after .GE or .GF */
+
+
+ initpic(); /* set defaults, ranges, etc. */
+ strcpy(GScommand, inputline); /* save '.GS' line for later */
+
+ do {
+ done = !doinput(fp); /* test for EOF */
+ flyback = (*c3 == 'F'); /* and .GE or .GF */
+ compat = (compatibility_flag ||
+ *c4 == '\n' || *c4 == ' ' || *c4 == '\0');
+ done |= (*c1 == '.' && *c2 == 'G' && (*c3 == 'E' || flyback) &&
+ compat);
+
+ if (done) {
+ if (setdefault)
+ savestate();
+
+ if (!gremlinfile[0]) {
+ if (!setdefault)
+ error("no picture file name at line %1", baseline);
+ return;
+ }
+ char *path;
+ gfp = macro_path.open_file(gremlinfile, &path);
+ if (0 /* nullptr */ == gfp) {
+ error("cannot open picture file '%1'", gremlinfile);
+ return;
+ }
+ PICTURE = DBRead(gfp); /* read picture file */
+ fclose(gfp);
+ free(path);
+ if (DBNullelt(PICTURE))
+ return; /* If a request is made to make the */
+ /* picture fit into a specific area, */
+ /* set the scale to do that. */
+
+ if (stipple == (char *) NULL) /* if user forgot stipple */
+ if (has_polygon(PICTURE)) /* and picture has a polygon */
+ stipple = (char *)DEFSTIPPLE; /* then set the default */
+
+ if ((temp = bottompoint - toppoint) < 0.1)
+ temp = 0.1;
+ temp = (height != 0.0) ? height / (temp * SCREENtoINCH) : BIG;
+ if ((troffscale = rightpoint - leftpoint) < 0.1)
+ troffscale = 0.1;
+ troffscale = (width != 0.0) ?
+ width / (troffscale * SCREENtoINCH) : BIG;
+ if (temp == BIG && troffscale == BIG)
+ troffscale = xscale;
+ else {
+ if (temp < troffscale)
+ troffscale = temp;
+ } /* here, troffscale is the */
+ /* picture's scaling factor */
+ if (pointscale) {
+ int i; /* do point scaling here, when */
+ /* scale is known, before output */
+ for (i = 0; i < SIZES; i++)
+ tsize[i] = (int) (troffscale * (double) tsize[i] + 0.5);
+ }
+
+ /* change to device units */
+ troffscale *= SCREENtoINCH * res; /* from screen units */
+
+ /* Calculate integer versions of the picture limits. */
+ ytop = (int) (toppoint * troffscale);
+ ybottom = (int) (bottompoint * troffscale);
+ xleft = (int) (leftpoint * troffscale);
+ xright = (int) (rightpoint * troffscale);
+
+ /* save stuff in number registers, */
+ /* register g1 = picture width and */
+ /* register g2 = picture height, */
+ /* set vertical spacing, no fill, */
+ /* and break (to make sure picture */
+ /* starts on left), and put out the */
+ /* user's '.GS' line. */
+ printf(".br\n"
+ ".nr g1 %du\n"
+ ".nr g2 %du\n"
+ "%s"
+ ".nr g3 \\n(.f\n"
+ ".nr g4 \\n(.s\n"
+ "\\0\n"
+ ".sp -1\n",
+ xright - xleft, ybottom - ytop, GScommand);
+
+ if (USE_ST_REQUEST) /* stipple requested for this picture */
+ printf(".st %s\n", stipple);
+ lastx = xleft; /* note where we are (upper left */
+ lastyline = lasty = ytop; /* corner of the picture) */
+
+ /* Just dump everything in the order it appears.
+ *
+ * If -s command-line option, traverse picture twice: First time,
+ * print only the interiors of filled polygons (as borderless
+ * polygons). Second time, print the outline as series of line
+ * segments. This way, postprocessors that overwrite rather than
+ * merge picture elements (such as Postscript) can still have text
+ * and graphics on a shaded background.
+ */
+ /* if (sflag) */
+ if (!sflag) { /* changing the default for filled polygons */
+ e = PICTURE;
+ polyfill = FILL;
+ while (!DBNullelt(e)) {
+ printf(".mk\n");
+ if (e->type == POLYGON)
+ HGPrintElt(e, baseline);
+ printf(".rt\n");
+ lastx = xleft;
+ lastyline = lasty = ytop;
+ e = DBNextElt(e);
+ }
+ }
+ e = PICTURE;
+
+ /* polyfill = !sflag ? BOTH : OUTLINE; */
+ polyfill = sflag ? BOTH : OUTLINE; /* changing default */
+ while (!DBNullelt(e)) {
+ printf(".mk\n");
+ HGPrintElt(e, baseline);
+ printf(".rt\n");
+ lastx = xleft;
+ lastyline = lasty = ytop;
+ e = DBNextElt(e);
+ }
+
+ /* decide where to end picture */
+
+ /* I [Senderowicz?] changed everything here. I always use the */
+ /* combination .mk and .rt, so once finished I just space down */
+ /* height of the picture that is \n(g2u. */
+ if (flyback) { /* end picture at upper left */
+ /* ptr.x = leftpoint;
+ ptr.y = toppoint; */
+ } else { /* end picture at lower left */
+ /* ptr.x = leftpoint;
+ ptr.y = bottompoint; */
+ printf(".sp \\n(g2u\n");
+ }
+
+ /* tmove(&ptr); */ /* restore default line parameters */
+
+ /* Restore everything to the way it was before the .GS, then */
+ /* put out the '.GE' line from user */
+
+ /* printf("\\D't %du'\\D's %du'\n", DEFTHICK, DEFSTYLE); */
+ /* groff doesn't understand the \Ds command */
+
+ printf("\\D't %du'\n", DEFTHICK);
+ if (flyback) /* make sure we end up at top of */
+ printf(".sp -1\n"); /* picture if 'flying back' */
+ if (USE_ST_REQUEST) /* restore stipple to previous */
+ printf(".st\n");
+ printf(".br\n"
+ ".ft \\n(g3\n"
+ ".ps \\n(g4\n"
+ "%s", inputline);
+ } else
+ interpret(inputline); /* take commands from the input file */
+ } while (!done);
+}
+
+
+/*--------------------------------------------------------------------*
+ | Routine: savestate ( )
+ |
+ | Results: All the current scaling/font size/font name/thickness/
+ | pointscale settings are made the defaults. Scaled
+ | point sizes are NOT saved. The scaling is done each
+ | time a new picture is started.
+ |
+ | Side Efct: scale, and def* are modified.
+ *--------------------------------------------------------------------*/
+
+void
+savestate()
+{
+ int i;
+
+ for (i = 0; i < STYLES; i++) /* line thickness defaults */
+ defthick[i] = thick[i];
+ for (i = 0; i < FONTS; i++) /* font name defaults */
+ deffont[i] = tfont[i];
+ for (i = 0; i < SIZES; i++) /* font size defaults */
+ defsize[i] = tsize[i];
+ /* stipple font file default indices */
+ for (i = 0; i <= NSTIPPLES; i++)
+ defstipple_index[i] = stipple_index[i];
+
+ defstipple = stipple; /* if stipple has been set, it's remembered */
+ scale *= xscale; /* default scale of individual pictures */
+ defpoint = pointscale;/* flag to scale point sizes from x factors */
+}
+
+
+/*--------------------------------------------------------------------*
+ | Routine: savebounds (x_coordinate, y_coordinate)
+ |
+ | Results: Keeps track of the maximum and minimum extent of a
+ | picture in the global variables: left-, right-, top- and
+ | bottompoint. 'savebounds' assumes that the points have
+ | been oriented to the correct direction. No scaling has
+ | taken place, though.
+ *--------------------------------------------------------------------*/
+
+void
+savebounds(double x,
+ double y)
+{
+ if (x < leftpoint)
+ leftpoint = x;
+ if (x > rightpoint)
+ rightpoint = x;
+ if (y < toppoint)
+ toppoint = y;
+ if (y > bottompoint)
+ bottompoint = y;
+}
+
+
+/*--------------------------------------------------------------------*
+ | Routine: interpret (character_string)
+ |
+ | Results: Commands are taken from the input string and performed.
+ | Commands are separated by newlines, and are of the
+ | format:
+ | string1 string2
+ | where string1 is the command, string2 the argument.
+ |
+ | Side Efct: Font and size strings, plus the gremlin file name and
+ | the width and height variables are set by this routine.
+ *--------------------------------------------------------------------*/
+
+void
+interpret(char *line)
+{
+ char str1[MAXINLINE];
+ char str2[MAXINLINE];
+ char *chr;
+ int i;
+ double par;
+
+ str2[0] = '\0';
+ sscanf(line, "%80s%80s", &str1[0], &str2[0]);
+ for (chr = &str1[0]; *chr; chr++) /* convert command to */
+ if (isupper(*chr))
+ *chr = tolower(*chr); /* lower case */
+
+ switch (str1[0]) {
+
+ case '1':
+ case '2': /* font sizes */
+ case '3':
+ case '4':
+ i = atoi(str2);
+ if (i > 0 && i < 1000)
+ tsize[str1[0] - '1'] = i;
+ else
+ error("bad font size value at line %1", linenum);
+ break;
+
+ case 'r': /* roman */
+ if (str2[0] < '0')
+ goto nofont;
+ tfont[0] = (char *) grnmalloc(strlen(str2) + 1, "roman command");
+ strcpy(tfont[0], str2);
+ break;
+
+ case 'i': /* italics */
+ if (str2[0] < '0')
+ goto nofont;
+ tfont[1] = (char *) grnmalloc(strlen(str2) + 1, "italics command");
+ strcpy(tfont[1], str2);
+ break;
+
+ case 'b': /* bold */
+ if (str2[0] < '0')
+ goto nofont;
+ tfont[2] = (char *) grnmalloc(strlen(str2) + 1, "bold command");
+ strcpy(tfont[2], str2);
+ break;
+
+ case 's': /* special */
+ if (str1[1] == 'c')
+ goto scalecommand; /* or scale */
+
+ if (str2[0] < '0') {
+ nofont:
+ error("no font name specified in line %1", linenum);
+ break;
+ }
+ if (str1[1] == 't')
+ goto stipplecommand; /* or stipple */
+
+ tfont[3] = (char *) grnmalloc(strlen(str2) + 1, "special command");
+ strcpy(tfont[3], str2);
+ break;
+
+ case 'l': /* l */
+ if (isdigit(str1[1])) { /* set stipple index */
+ int idx = atoi(str1 + 1), val;
+
+ if (idx < 0 || idx > NSTIPPLES) {
+ error("bad stipple number %1 at line %2", idx, linenum);
+ break;
+ }
+ if (!defstipple_index)
+ defstipple_index = other_stipple_index;
+ val = atoi(str2);
+ if (val >= 0 && val < 256)
+ stipple_index[idx] = val;
+ else
+ error("bad stipple index value at line %1", linenum);
+ break;
+ }
+
+ stipplecommand: /* set stipple name */
+ stipple = (char *) grnmalloc(strlen(str2) + 1, "stipple command");
+ strcpy(stipple, str2);
+ /* if it's a 'known' font (currently only 'cf'), set indices */
+ if (strcmp(stipple, "cf") == 0)
+ defstipple_index = cf_stipple_index;
+ else
+ defstipple_index = other_stipple_index;
+ for (i = 0; i <= NSTIPPLES; i++)
+ stipple_index[i] = defstipple_index[i];
+ break;
+
+ case 'a': /* text adjust */
+ par = atof(str2);
+ switch (str1[1]) {
+ case '1':
+ adj1 = par;
+ break;
+ case '2':
+ adj2 = par;
+ break;
+ case '3':
+ adj3 = par;
+ break;
+ case '4':
+ adj4 = par;
+ break;
+ default:
+ error("bad adjust command at line %1", linenum);
+ break;
+ }
+ break;
+
+ case 't': /* thick */
+ thick[2] = defthick[0] * atof(str2);
+ break;
+
+ case 'm': /* medium */
+ thick[5] = defthick[0] * atof(str2);
+ break;
+
+ case 'n': /* narrow */
+ thick[0] = thick[1] = thick[3] = thick[4] =
+ defthick[0] * atof(str2);
+ break;
+
+ case 'x': /* x */
+ scalecommand: /* scale */
+ par = atof(str2);
+ if (par > 0.0)
+ xscale *= par;
+ else
+ error("invalid scale value on line %1", linenum);
+ break;
+
+ case 'f': /* file */
+ strcpy(gremlinfile, str2);
+ break;
+
+ case 'w': /* width */
+ width = atof(str2);
+ if (width < 0.0)
+ width = -width;
+ break;
+
+ case 'h': /* height */
+ height = atof(str2);
+ if (height < 0.0)
+ height = -height;
+ break;
+
+ case 'd': /* defaults */
+ setdefault = 1;
+ break;
+
+ case 'p': /* pointscale */
+ if (strcmp("off", str2))
+ pointscale = 1;
+ else
+ pointscale = 0;
+ break;
+
+ default:
+ error("unknown command '%1' on line %2", str1, linenum);
+ exit(8);
+ break;
+ };
+}
+
+
+/*
+ * return TRUE if picture contains a polygon
+ * otherwise FALSE
+ */
+
+int
+has_polygon(ELT *elist)
+{
+ while (!DBNullelt(elist)) {
+ if (elist->type == POLYGON)
+ return (1);
+ elist = DBNextElt(elist);
+ }
+
+ return (0);
+}
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72: