diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 19:44:05 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 19:44:05 +0000 |
commit | d318611dd6f23fcfedd50e9b9e24620b102ba96a (patch) | |
tree | 8b9eef82ca40fdd5a8deeabf07572074c236095d /src/preproc/grn | |
parent | Initial commit. (diff) | |
download | groff-d318611dd6f23fcfedd50e9b9e24620b102ba96a.tar.xz groff-d318611dd6f23fcfedd50e9b9e24620b102ba96a.zip |
Adding upstream version 1.23.0.upstream/1.23.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/preproc/grn')
-rw-r--r-- | src/preproc/grn/README | 68 | ||||
-rw-r--r-- | src/preproc/grn/gprint.h | 90 | ||||
-rw-r--r-- | src/preproc/grn/grn.1.man | 978 | ||||
-rw-r--r-- | src/preproc/grn/grn.am | 35 | ||||
-rw-r--r-- | src/preproc/grn/hdb.cpp | 441 | ||||
-rw-r--r-- | src/preproc/grn/hgraph.cpp | 1060 | ||||
-rw-r--r-- | src/preproc/grn/hpoint.cpp | 59 | ||||
-rw-r--r-- | src/preproc/grn/main.cpp | 977 |
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: |