diff options
author | pst <pst@FreeBSD.org> | 1996-09-07 16:18:32 +0000 |
---|---|---|
committer | pst <pst@FreeBSD.org> | 1996-09-07 16:18:32 +0000 |
commit | 7fdf49473c970aa96ee1bae16928d1db23643228 (patch) | |
tree | 0abcbad9804fcf7a7157983937cadcf61449b840 /contrib/groff/grops | |
download | FreeBSD-src-7fdf49473c970aa96ee1bae16928d1db23643228.zip FreeBSD-src-7fdf49473c970aa96ee1bae16928d1db23643228.tar.gz |
Virgin import of FSF groff v1.10
Diffstat (limited to 'contrib/groff/grops')
-rw-r--r-- | contrib/groff/grops/Makefile.dep | 6 | ||||
-rw-r--r-- | contrib/groff/grops/Makefile.sub | 7 | ||||
-rw-r--r-- | contrib/groff/grops/TODO | 29 | ||||
-rw-r--r-- | contrib/groff/grops/grops.man | 837 | ||||
-rw-r--r-- | contrib/groff/grops/ps.cc | 1532 | ||||
-rw-r--r-- | contrib/groff/grops/ps.h | 122 | ||||
-rw-r--r-- | contrib/groff/grops/psfig.diff | 106 | ||||
-rw-r--r-- | contrib/groff/grops/psrm.cc | 1099 |
8 files changed, 3738 insertions, 0 deletions
diff --git a/contrib/groff/grops/Makefile.dep b/contrib/groff/grops/Makefile.dep new file mode 100644 index 0000000..94e3434 --- /dev/null +++ b/contrib/groff/grops/Makefile.dep @@ -0,0 +1,6 @@ +ps.o: ps.cc ../include/driver.h ../include/errarg.h ../include/error.h \ + ../include/font.h ../include/printer.h ../include/lib.h \ + ../include/stringclass.h ../include/cset.h ps.h +psrm.o: psrm.cc ../include/driver.h ../include/errarg.h \ + ../include/error.h ../include/font.h ../include/printer.h \ + ../include/lib.h ../include/stringclass.h ../include/cset.h ps.h diff --git a/contrib/groff/grops/Makefile.sub b/contrib/groff/grops/Makefile.sub new file mode 100644 index 0000000..891472b --- /dev/null +++ b/contrib/groff/grops/Makefile.sub @@ -0,0 +1,7 @@ +PROG=grops +MAN1=grops.n +XLIBS=$(LIBDRIVER) $(LIBGROFF) +MLIB=$(LIBM) +OBJS=ps.o psrm.o +CCSRCS=ps.cc psrm.cc +HDRS=ps.h diff --git a/contrib/groff/grops/TODO b/contrib/groff/grops/TODO new file mode 100644 index 0000000..da67973 --- /dev/null +++ b/contrib/groff/grops/TODO @@ -0,0 +1,29 @@ +Read PFB files directly. + +Generate %%DocumentMedia comment. + +Generate %%For comment. + +Generate %%Title comment. + +For efficiency it might be better to have the printer interface have +support for the t and u commands. + +Angles in arc command: don't generate more digits after the decimal +point than are necessary. + +Possibly generate BoundingBox comment. + +Per font composite character mechanism (sufficient for fractions). + +Consider whether we ought to do rounding of graphical objects other +than lines. What's the point? + +Error messages should refer to output page number. + +Search for downloadable fonts using their PostScript names if not +found in download file. + +Separate path for searching for downloadable font files. + +Clip to the BoundingBox when importing documents. diff --git a/contrib/groff/grops/grops.man b/contrib/groff/grops/grops.man new file mode 100644 index 0000000..6ac082f --- /dev/null +++ b/contrib/groff/grops/grops.man @@ -0,0 +1,837 @@ +.ig \"-*- nroff -*- +Copyright (C) 1989-1995 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. +.. +.\" Like TP, but if specified indent is more than half +.\" the current line-length - indent, use the default indent. +.de Tp +.ie \\n(.$=0:((0\\$1)*2u>(\\n(.lu-\\n(.iu)) .TP +.el .TP "\\$1" +.. +.TH GROPS @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@" +.SH NAME +grops \- PostScript driver for groff +.SH SYNOPSIS +.B grops +[ +.B \-glmv +] [ +.BI \-b n +] [ +.BI \-c n +] [ +.BI \-w n +] [ +.BI \-F dir +] [ +.IR files \|.\|.\|. +] +.SH DESCRIPTION +.B grops +translates the output of GNU +.B troff +to PostScript. +Normally +.B grops +should be invoked by using the groff command +with a +.B \-Tps +option. +.if '@DEVICE@'ps' (Actually, this is the default for groff.) +If no files are given, +.B grops +will read the standard input. +A filename of +.B \- +will also cause +.B grops +to read the standard input. +PostScript output is written to the standard output. +When +.B grops +is run by +.B groff +options can be passed to +.B grops +using the +.B groff +.B \-P +option. +.SH OPTIONS +.TP +.BI \-b n +Workaround broken spoolers and previewers. +Normally +.B grops +produces output that conforms +the Document Structuring Conventions version 3.0. +Unfortunately some spoolers and previewers can't handle such output. +The value of +.I n +controls what +.B grops +does to its output acceptable to such programs. +A value of 0 will cause grops not to employ any workarounds. +Add 1 if no +.B %%BeginDocumentSetup +and +.B %%EndDocumentSetup +comments should be generated; +this is needed for early versions of TranScript that get confused by +anything between the +.B %%EndProlog +comment and the first +.B %%Page +comment. +Add 2 if lines in included files beginning with +.B %! +should be stripped out; this is needed for Sun's pageview previewer. +Add 4 if +.BR %%Page , +.BR %%Trailer +and +.B %%EndProlog +comments should be +stripped out of included files; this is needed for spoolers that +don't understand the +.B %%BeginDocument +and +.B %%EndDocument +comments. +Add 8 if the first line of the PostScript output should be +.B %!PS-Adobe-2.0 +rather than +.BR %!PS-Adobe-3.0 ; +this is needed when using Sun's Newsprint with a printer that requires +page reversal. +The default value can be specified by a +.RS +.IP +.BI broken\ n +.LP +command in the DESC file. +Otherwise the default value is 0. +.RE +.TP +.BI \-c n +Print +.I n +copies of each page. +.TP +.BI \-g +Guess the page length. +This generates PostScript code that guesses the page length. +The guess will be correct only if the imageable area is vertically +centered on the page. +This option allows you to generate documents that can be printed +both on letter (8.5\(mu11) paper and on A4 paper without change. +.TP +.B \-l +Print the document in landscape format. +.TP +.B \-m +Turn manual feed on for the document. +.TP +.BI \-F dir +Search the directory +.IB dir /dev name +for font and device description files; +.I name +is the name of the device, usually +.BR ps . +.TP +.BI \-w n +Lines should be drawn using a thickness of +.I n +thousandths of an em. +.TP +.B \-v +Print the version number. +.SH USAGE +There are styles called +.BR R , +.BR I , +.BR B , +and +.B BI +mounted at font positions 1 to 4. +The fonts are grouped into families +.BR A , +.BR BM , +.BR C , +.BR H , +.BR HN , +.BR N , +.B P +and +.B T +having members in each of these styles: +.de FT +.if '\\*(.T'ps' .ft \\$1 +.. +.TP +.B AR +.FT AR +AvantGarde-Book +.FT +.TP +.B AI +.FT AI +AvantGarde-BookOblique +.FT +.TP +.B AB +.FT AB +AvantGarde-Demi +.FT +.TP +.B ABI +.FT ABI +AvantGarde-DemiOblique +.FT +.TP +.B BMR +.FT BMR +Bookman-Light +.FT +.TP +.B BMI +.FT BMI +Bookman-LightItalic +.FT +.TP +.B BMB +.FT BMB +Bookman-Demi +.FT +.TP +.B BMBI +.FT BMBI +Bookman-DemiItalic +.FT +.TP +.B CR +.FT CR +Courier +.FT +.TP +.B CI +.FT CI +Courier-Oblique +.FT +.TP +.B CB +.FT CB +Courier-Bold +.FT +.TP +.B CBI +.FT CBI +Courier-BoldOblique +.FT +.TP +.B HR +.FT HR +Helvetica +.FT +.TP +.B HI +.FT HI +Helvetica-Oblique +.FT +.TP +.B HB +.FT HB +Helvetica-Bold +.FT +.TP +.B HBI +.FT HBI +Helvetica-BoldOblique +.FT +.TP +.B HNR +.FT HNR +Helvetica-Narrow +.FT +.TP +.B HNI +.FT HNI +Helvetica-Narrow-Oblique +.FT +.TP +.B HNB +.FT HNB +Helvetica-Narrow-Bold +.FT +.TP +.B HNBI +.FT HNBI +Helvetica-Narrow-BoldOblique +.FT +.TP +.B NR +.FT NR +NewCenturySchlbk-Roman +.FT +.TP +.B NI +.FT NI +NewCenturySchlbk-Italic +.FT +.TP +.B NB +.FT NB +NewCenturySchlbk-Bold +.FT +.TP +.B NBI +.FT NBI +NewCenturySchlbk-BoldItalic +.FT +.TP +.B PR +.FT PR +Palatino-Roman +.FT +.TP +.B PI +.FT PI +Palatino-Italic +.FT +.TP +.B PB +.FT PB +Palatino-Bold +.FT +.TP +.B PBI +.FT PBI +Palatino-BoldItalic +.FT +.TP +.B TR +.FT TR +Times-Roman +.FT +.TP +.B TI +.FT TI +Times-Italic +.FT +.TP +.B TB +.FT TB +Times-Bold +.FT +.TP +.B TBI +.FT TBI +Times-BoldItalic +.FT +.LP +There is also the following font which is not a member of a family: +.TP +.B ZCMI +.FT ZCMI +ZapfChancery-MediumItalic +.FT +.LP +There are also some special fonts called +.B SS +and +.BR S . +Zapf Dingbats is available as +.BR ZD +and a reversed version of ZapfDingbats (with symbols pointing in the opposite +direction) is available as +.BR ZDR ; +most characters in these fonts are unnamed and must be accessed using +.BR \eN . +.LP +.B grops +understands various X commands produced using the +.B \eX +escape sequence; +.B grops +will only interpret commands that begin with a +.B ps: +tag. +.TP +.BI \eX'ps:\ exec\ code ' +This executes the arbitrary PostScript commands in +.IR code . +The PostScript currentpoint will be set to the position of the +.B \eX +command before executing +.IR code . +The origin will be at the top left corner of the page, +and y coordinates will increase down the page. +A procedure +.B u +will be defined that converts groff units +to the coordinate system in effect. +For example, +.RS +.IP +.B +\&.nr x 1i +.br +.B +\eX'ps: exec \enx u 0 rlineto stroke' +.br +.RE +.IP +will draw a horizontal line one inch long. +.I code +may make changes to the graphics state, +but any changes will persist only to the +end of the page. +A dictionary containing the definitions specified by the +.B def +and +.B mdef +will be on top of the dictionary stack. +If your code adds definitions to this dictionary, +you should allocate space for them using +.BI \eX'ps\ mdef \ n '\fR. +Any definitions will persist only until the end of the page. +If you use the +.B \eY +escape sequence with an argument that names a macro, +.I code +can extend over multiple lines. +For example, +.RS +.IP +.nf +.ft B +\&.nr x 1i +\&.de y +\&ps: exec +\&\enx u 0 rlineto +\&stroke +\&.. +\&\eYy +.fi +.ft R +.LP +is another way to draw a horizontal line one inch long. +.RE +.TP +.BI \eX'ps:\ file\ name ' +This is the same as the +.B exec +command except that the PostScript code is read from file +.IR name . +.TP +.BI \eX'ps:\ def\ code ' +Place a PostScript definition contained in +.I code +in the prologue. +There should be at most one definition per +.B \eX +command. +Long definitions can be split over several +.B \eX +commands; +all the +.I code +arguments are simply joined together separated by newlines. +The definitions are placed in a dictionary which is automatically +pushed on the dictionary stack when an +.B exec +command is executed. +If you use the +.B \eY +escape sequence with an argument that names a macro, +.I code +can extend over multiple lines. +.TP +.BI \eX'ps:\ mdef\ n\ code ' +Like +.BR def , +except that +.I code +may contain up to +.I n +definitions. +.B grops +needs to know how many definitions +.I code +contains +so that it can create an appropriately sized PostScript dictionary +to contain them. +.TP +.BI \eX'ps:\ import\ file\ llx\ lly\ urx\ ury\ width\ \fR[\fP\ height\ \fR]\fP ' +Import a PostScript graphic from +.IR file . +The arguments +.IR llx , +.IR lly , +.IR urx , +and +.I ury +give the bounding box of the graphic in the default PostScript +coordinate system; they should all be integers; +.I llx +and +.I lly +are the x and y coordinates of the lower left +corner of the graphic; +.I urx +and +.I ury +are the x and y coordinates of the upper right corner of the graphic; +.I width +and +.I height +are integers that give the desired width and height in groff +units of the graphic. +The graphic will be scaled so that it has this width and height +and translated so that the lower left corner of the graphic is +located at the position associated with +.B \eX +command. +If the height argument is omitted it will be scaled uniformly in the +x and y directions so that it has the specified width. +Note that the contents of the +.B \eX +command are not interpreted by +.BR troff ; +so vertical space for the graphic is not automatically added, +and the +.I width +and +.I height +arguments are not allowed to have attached scaling indicators. +If the PostScript file complies with the Adobe Document Structuring +Conventions and contains a +.B %%BoundingBox +comment, then the bounding box can be automatically +extracted from within groff by using the +.B sy +request to run the +.B psbb +command. +.RS +.LP +The +.B \-mps +macros (which are automatically loaded when +.B grops +is run by the groff command) include a +.B PSPIC +macro which allows a picture to be easily imported. +This has the format +.IP +\&\fB.PSPIC\fP [ \fB\-L\fP | \fB-R\fP | \fB\-I\fP \fIn\fP ]\ \" +\fI\|file\fP [ \fIwidth\fP [ \fIheight\fP ]] +.LP +.I file +is the name of the file containing the illustration; +.I width +and +.I height +give the desired width and height of the graphic. +The +.I width +and +.I height +arguments may have scaling indicators attached; +the default scaling indicator is +.BR i . +This macro will scale the graphic uniformly +in the x and y directions so that it is no more than +.I width +wide +and +.I height +high. +By default, the graphic will be horizontally centered. +The +.BI \-L +and +.BI \-R +cause the graphic to be left-aligned and right-aligned +respectively. +The +.B \-I +option causes the graphic to be indented by +.IR n . +.RE +.TP +.B \eX'ps:\ invis' +.br +.ns +.TP +.B \eX'ps:\ endinvis' +No output will be generated for text and drawing commands +that are bracketed with these +.B \eX +commands. +These commands are intended for use when output from +.B troff +will be previewed before being processed with +.BR grops ; +if the previewer is unable to display certain characters +or other constructs, then other substitute characters or constructs +can be used for previewing by bracketing them with these +.B \eX +commands. +.RS +.LP +For example, +.B gxditview +is not able to display a proper +.B \e(em +character because the standard X11 fonts do not provide it; +this problem can be overcome by executing the following +request +.IP +.ft B +.nf +\&.char \e(em \eX'ps: invis'\e +\eZ'\ev'-.25m'\eh'.05m'\eD'l .9m 0'\eh'.05m''\e +\eX'ps: endinvis'\e(em +.ft +.fi +.LP +In this case, +.B gxditview +will be unable to display the +.B \e(em +character and will draw the line, +whereas +.B grops +will print the +.B \e(em +character +and ignore the line. +.RE +.LP +The input to +.B grops +must be in the format output by +.BR @g@troff (@MAN1EXT@). +This is described in +.BR groff_out (@MAN1EXT@). +In addition the device and font description files for the device used +must meet certain requirements. +The device and font description files supplied for +.B ps +device meet all these requirements. +.BR afmtodit (@MAN1EXT@) +can be used to create font files from AFM files. +The resolution must be an integer multiple of 72 times the +.BR sizescale . +The +.B ps +device uses a resolution of 72000 and a sizescale of 1000. +The device description file should contain a command +.IP +.BI paperlength\ n +.LP +which says that output should be generated which is suitable for +printing on a page whose length is +.I n +machine units. +Each font description file must contain a command +.IP +.BI internalname\ psname +.LP +which says that the PostScript name of the font is +.IR psname . +It may also contain a command +.IP +.BI encoding\ enc_file +.LP +which says that +the PostScript font should be reencoded using the encoding described in +.IR enc_file ; +this file should consist of a sequence of lines of the form: +.IP +.I +pschar code +.LP +where +.I pschar +is the PostScript name of the character, +and +.I code +is its position in the encoding expressed as a decimal integer. +The code for each character given in the font file must correspond +to the code for the character in encoding file, or to the code in the default +encoding for the font if the PostScript font is not to be reencoded. +This code can be used with the +.B \eN +escape sequence in +.B troff +to select the character, +even if the character does not have a groff name. +Every character in the font file must exist in the PostScript font, and +the widths given in the font file must match the widths used +in the PostScript font. +.B grops +will assume that a character with a groff name of +.B space +is blank (makes no marks on the page); +it can make use of such a character to generate more efficient and +compact PostScript output. +.LP +.B grops +can automatically include the downloadable fonts necessary +to print the document. +Any downloadable fonts which should, when required, be included by +.B grops +must be listed in the file +.BR @FONTDIR@/devps/download ; +this should consist of lines of the form +.IP +.I +font filename +.LP +where +.I font +is the PostScript name of the font, +and +.I filename +is the name of the file containing the font; +lines beginning with +.B # +and blank lines are ignored; +fields may be separated by tabs or spaces; +.I filename +will be searched for using the same mechanism that is used +for groff font metric files. +The +.B download +file itself will also be searched for using this mechanism. +.LP +If the file containing a downloadable font or imported document +conforms to the Adobe Document Structuring Conventions, +then +.B grops +will interpret any comments in the files sufficiently to ensure that its +own output is conforming. +It will also supply any needed font resources that are listed in the +.B download +file +as well as any needed file resources. +It is also able to handle inter-resource dependencies. +For example, suppose that you have a downloadable font called Garamond, +and also a downloadable font called Garamond-Outline +which depends on Garamond +(typically it would be defined to copy Garamond's font dictionary, +and change the PaintType), +then it is necessary for Garamond to be appear before Garamond-Outline +in the PostScript document. +.B grops +will handle this automatically +provided that the downloadable font file for Garamond-Outline +indicates its dependence on Garamond by means of +the Document Structuring Conventions, +for example by beginning with the following lines +.IP +.B +%!PS-Adobe-3.0 Resource-Font +.br +.B +%%DocumentNeededResources: font Garamond +.br +.B +%%EndComments +.br +.B +%%IncludeResource: font Garamond +.LP +In this case both Garamond and Garamond-Outline would need to be listed +in the +.B download +file. +A downloadable font should not include its own name in a +.B %%DocumentSuppliedResources +comment. +.LP +.B grops +will not interpret +.B %%DocumentFonts +comments. +The +.BR %%DocumentNeededResources , +.BR %%DocumentSuppliedResources , +.BR %%IncludeResource , +.BR %%BeginResource +and +.BR %%EndResource +comments +(or possibly the old +.BR %%DocumentNeededFonts , +.BR %%DocumentSuppliedFonts , +.BR %%IncludeFont , +.BR %%BeginFont +and +.BR %%EndFont +comments) +should be used. +.SH FILES +.Tp \w'\fB@FONTDIR@/devps/download'u+2n +.B @FONTDIR@/devps/DESC +Device description file. +.TP +.BI @FONTDIR@/devps/ F +Font description file for font +.IR F . +.TP +.B @FONTDIR@/devps/download +List of downloadable fonts. +.TP +.B @FONTDIR@/devps/text.enc +Encoding used for text fonts. +.TP +.B @MACRODIR@/tmac.ps +Macros for use with +.BR grops ; +automatically loaded by +.BR troffrc +.TP +.B @MACRODIR@/tmac.pspic +Definition of +.B PSPIC +macro, +automatically loaded by +.BR tmac.ps . +.TP +.B @MACRODIR@/tmac.psold +Macros to disable use of characters not present in older +PostScript printers; automatically loaded by +.BR tmac.ps . +.TP +.B @MACRODIR@/tmac.psnew +Macros to undo the effect of +.BR tmac.psold . +.TP +.BI /tmp/grops XXXXXX +Temporary file. +.SH "SEE ALSO" +.BR afmtodit (@MAN1EXT@), +.BR groff (@MAN1EXT@), +.BR @g@troff (@MAN1EXT@), +.BR psbb (@MAN1EXT@), +.BR groff_out (@MAN5EXT@), +.BR groff_font (@MAN5EXT@), +.BR groff_char (@MAN7EXT@) diff --git a/contrib/groff/grops/ps.cc b/contrib/groff/grops/ps.cc new file mode 100644 index 0000000..49ed9d4 --- /dev/null +++ b/contrib/groff/grops/ps.cc @@ -0,0 +1,1532 @@ +// -*- C++ -*- +/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc. + Written by James Clark (jjc@jclark.com) + +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with groff; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "driver.h" +#include "stringclass.h" +#include "cset.h" + +#include "ps.h" +#include <time.h> + +static int landscape_flag = 0; +static int manual_feed_flag = 0; +static int ncopies = 1; +static int linewidth = -1; +// Non-zero means generate PostScript code that guesses the paper +// length using the imageable area. +static int guess_flag = 0; + +// Non-zero if -b was specified on the command line. +static int bflag = 0; +unsigned broken_flags = 0; + +#define DEFAULT_LINEWIDTH 40 /* in ems/1000 */ +#define MAX_LINE_LENGTH 72 +#define FILL_MAX 1000 + +const char *const dict_name = "grops"; +const char *const defs_dict_name = "DEFS"; +const int DEFS_DICT_SPARE = 50; + +double degrees(double r) +{ + return r*180.0/PI; +} + +double radians(double d) +{ + return d*PI/180.0; +} + +inline double transform_fill(int fill) +{ + return 1 - fill/double(FILL_MAX); +} + +// This is used for testing whether a character should be output in the +// PostScript file using \nnn, so we really want the character to be +// less than 0200. + +inline int is_ascii(char c) +{ + return (unsigned char)c < 0200; +} + +ps_output::ps_output(FILE *f, int n) +: fp(f), max_line_length(n), col(0), need_space(0), fixed_point(0) +{ +} + +ps_output &ps_output::set_file(FILE *f) +{ + fp = f; + col = 0; + return *this; +} + +ps_output &ps_output::copy_file(FILE *infp) +{ + int c; + while ((c = getc(infp)) != EOF) + putc(c, fp); + return *this; +} + +ps_output &ps_output::end_line() +{ + if (col != 0) { + putc('\n', fp); + col = 0; + need_space = 0; + } + return *this; +} + +ps_output &ps_output::special(const char *s) +{ + if (s == 0 || *s == '\0') + return *this; + if (col != 0) { + putc('\n', fp); + col = 0; + } + fputs(s, fp); + if (strchr(s, '\0')[-1] != '\n') + putc('\n', fp); + need_space = 0; + return *this; +} + +ps_output &ps_output::simple_comment(const char *s) +{ + if (col != 0) + putc('\n', fp); + putc('%', fp); + putc('%', fp); + fputs(s, fp); + putc('\n', fp); + col = 0; + need_space = 0; + return *this; +} + +ps_output &ps_output::begin_comment(const char *s) +{ + if (col != 0) + putc('\n', fp); + putc('%', fp); + putc('%', fp); + fputs(s, fp); + col = 2 + strlen(s); + return *this; +} + +ps_output &ps_output::end_comment() +{ + if (col != 0) { + putc('\n', fp); + col = 0; + } + need_space = 0; + return *this; +} + +ps_output &ps_output::comment_arg(const char *s) +{ + int len = strlen(s); + if (col + len + 1 > max_line_length) { + putc('\n', fp); + fputs("%%+", fp); + col = 3; + } + putc(' ', fp); + fputs(s, fp); + col += len + 1; + return *this; +} + +ps_output &ps_output::set_fixed_point(int n) +{ + assert(n >= 0 && n <= 10); + fixed_point = n; + return *this; +} + +ps_output &ps_output::put_delimiter(char c) +{ + if (col + 1 > max_line_length) { + putc('\n', fp); + col = 0; + } + putc(c, fp); + col++; + need_space = 0; + return *this; +} + +ps_output &ps_output::put_string(const char *s, int n) +{ + int len = 0; + int i; + for (i = 0; i < n; i++) { + char c = s[i]; + if (is_ascii(c) && csprint(c)) { + if (c == '(' || c == ')' || c == '\\') + len += 2; + else + len += 1; + } + else + len += 4; + } + if (len > n*2) { + if (col + n*2 + 2 > max_line_length && n*2 + 2 <= max_line_length) { + putc('\n', fp); + col = 0; + } + if (col + 1 > max_line_length) { + putc('\n', fp); + col = 0; + } + putc('<', fp); + col++; + for (i = 0; i < n; i++) { + if (col + 2 > max_line_length) { + putc('\n', fp); + col = 0; + } + fprintf(fp, "%02x", s[i] & 0377); + col += 2; + } + putc('>', fp); + col++; + } + else { + if (col + len + 2 > max_line_length && len + 2 <= max_line_length) { + putc('\n', fp); + col = 0; + } + if (col + 2 > max_line_length) { + putc('\n', fp); + col = 0; + } + putc('(', fp); + col++; + for (i = 0; i < n; i++) { + char c = s[i]; + if (is_ascii(c) && csprint(c)) { + if (c == '(' || c == ')' || c == '\\') + len = 2; + else + len = 1; + } + else + len = 4; + if (col + len + 1 > max_line_length) { + putc('\\', fp); + putc('\n', fp); + col = 0; + } + switch (len) { + case 1: + putc(c, fp); + break; + case 2: + putc('\\', fp); + putc(c, fp); + break; + case 4: + fprintf(fp, "\\%03o", c & 0377); + break; + default: + assert(0); + } + col += len; + } + putc(')', fp); + col++; + } + need_space = 0; + return *this; +} + +ps_output &ps_output::put_number(int n) +{ + char buf[1 + INT_DIGITS + 1]; + sprintf(buf, "%d", n); + int len = strlen(buf); + if (col > 0 && col + len + need_space > max_line_length) { + putc('\n', fp); + col = 0; + need_space = 0; + } + if (need_space) { + putc(' ', fp); + col++; + } + fputs(buf, fp); + col += len; + need_space = 1; + return *this; +} + +ps_output &ps_output::put_fix_number(int i) +{ + const char *p = iftoa(i, fixed_point); + int len = strlen(p); + if (col > 0 && col + len + need_space > max_line_length) { + putc('\n', fp); + col = 0; + need_space = 0; + } + if (need_space) { + putc(' ', fp); + col++; + } + fputs(p, fp); + col += len; + need_space = 1; + return *this; +} + +ps_output &ps_output::put_float(double d) +{ + char buf[128]; + sprintf(buf, "%.4f", d); + int len = strlen(buf); + if (col > 0 && col + len + need_space > max_line_length) { + putc('\n', fp); + col = 0; + need_space = 0; + } + if (need_space) { + putc(' ', fp); + col++; + } + fputs(buf, fp); + col += len; + need_space = 1; + return *this; +} + +ps_output &ps_output::put_symbol(const char *s) +{ + int len = strlen(s); + if (col > 0 && col + len + need_space > max_line_length) { + putc('\n', fp); + col = 0; + need_space = 0; + } + if (need_space) { + putc(' ', fp); + col++; + } + fputs(s, fp); + col += len; + need_space = 1; + return *this; +} + +ps_output &ps_output::put_literal_symbol(const char *s) +{ + int len = strlen(s); + if (col > 0 && col + len + 1 > max_line_length) { + putc('\n', fp); + col = 0; + } + putc('/', fp); + fputs(s, fp); + col += len + 1; + need_space = 1; + return *this; +} + +class ps_font : public font { + ps_font(const char *); +public: + int encoding_index; + char *encoding; + char *reencoded_name; + ~ps_font(); + void handle_unknown_font_command(const char *command, const char *arg, + const char *filename, int lineno); + static ps_font *load_ps_font(const char *); +}; + +ps_font *ps_font::load_ps_font(const char *s) +{ + ps_font *f = new ps_font(s); + if (!f->load()) { + delete f; + return 0; + } + return f; +} + +ps_font::ps_font(const char *nm) +: font(nm), encoding(0), reencoded_name(0), encoding_index(-1) +{ +} + +ps_font::~ps_font() +{ + a_delete encoding; + a_delete reencoded_name; +} + +void ps_font::handle_unknown_font_command(const char *command, const char *arg, + const char *filename, int lineno) +{ + if (strcmp(command, "encoding") == 0) { + if (arg == 0) + error_with_file_and_line(filename, lineno, + "`encoding' command requires an argument"); + else + encoding = strsave(arg); + } +} + +static void handle_unknown_desc_command(const char *command, const char *arg, + const char *filename, int lineno) +{ + if (strcmp(command, "broken") == 0) { + if (arg == 0) + error_with_file_and_line(filename, lineno, + "`broken' command requires an argument"); + else if (!bflag) + broken_flags = atoi(arg); + } +} + +struct style { + font *f; + int point_size; + int height; + int slant; + style(); + style(font *, int, int, int); + int operator==(const style &) const; + int operator!=(const style &) const; +}; + +style::style() : f(0) +{ +} + +style::style(font *p, int sz, int h, int sl) +: f(p), point_size(sz), height(h), slant(sl) +{ +} + +int style::operator==(const style &s) const +{ + return (f == s.f && point_size == s.point_size + && height == s.height && slant == s.slant); +} + +int style::operator!=(const style &s) const +{ + return !(*this == s); +} + +class ps_printer : public printer { + FILE *tempfp; + ps_output out; + int res; + int space_char_index; + int pages_output; + int paper_length; + int equalise_spaces; + enum { SBUF_SIZE = 256 }; + char sbuf[SBUF_SIZE]; + int sbuf_len; + int sbuf_start_hpos; + int sbuf_vpos; + int sbuf_end_hpos; + int sbuf_space_width; + int sbuf_space_count; + int sbuf_space_diff_count; + int sbuf_space_code; + int sbuf_kern; + style sbuf_style; + style output_style; + int output_hpos; + int output_vpos; + int output_draw_point_size; + int line_thickness; + int output_line_thickness; + int fill; + unsigned char output_space_code; + enum { MAX_DEFINED_STYLES = 50 }; + style defined_styles[MAX_DEFINED_STYLES]; + int ndefined_styles; + int next_encoding_index; + string defs; + int ndefs; + resource_manager rm; + int invis_count; + + void flush_sbuf(); + void set_style(const style &); + void set_space_code(unsigned char c); + int set_encoding_index(ps_font *); + void do_exec(char *, const environment *); + void do_import(char *, const environment *); + void do_def(char *, const environment *); + void do_mdef(char *, const environment *); + void do_file(char *, const environment *); + void do_invis(char *, const environment *); + void do_endinvis(char *, const environment *); + void set_line_thickness(const environment *); + void fill_path(); + void encode_fonts(); + void define_encoding(const char *, int); + void reencode_font(ps_font *); +public: + ps_printer(); + ~ps_printer(); + void set_char(int i, font *f, const environment *env, int w); + void draw(int code, int *p, int np, const environment *env); + void begin_page(int); + void end_page(int); + void special(char *arg, const environment *env); + font *make_font(const char *); + void end_of_line(); +}; + +ps_printer::ps_printer() +: pages_output(0), + sbuf_len(0), + output_hpos(-1), + output_vpos(-1), + out(0, MAX_LINE_LENGTH), + ndefined_styles(0), + next_encoding_index(0), + line_thickness(-1), + fill(FILL_MAX + 1), + ndefs(0), + invis_count(0) +{ + tempfp = xtmpfile(); + out.set_file(tempfp); + if (linewidth < 0) + linewidth = DEFAULT_LINEWIDTH; + if (font::hor != 1) + fatal("horizontal resolution must be 1"); + if (font::vert != 1) + fatal("vertical resolution must be 1"); + if (font::res % (font::sizescale*72) != 0) + fatal("res must be a multiple of 72*sizescale"); + int r = font::res; + int point = 0; + while (r % 10 == 0) { + r /= 10; + point++; + } + res = r; + out.set_fixed_point(point); + space_char_index = font::name_to_index("space"); + paper_length = font::paperlength; + if (paper_length == 0) + paper_length = 11*font::res; + equalise_spaces = font::res >= 72000; +} + +int ps_printer::set_encoding_index(ps_font *f) +{ + if (f->encoding_index >= 0) + return f->encoding_index; + for (font_pointer_list *p = font_list; p; p = p->next) + if (p->p != f) { + char *encoding = ((ps_font *)p->p)->encoding; + int encoding_index = ((ps_font *)p->p)->encoding_index; + if (encoding != 0 && encoding_index >= 0 + && strcmp(f->encoding, encoding) == 0) { + return f->encoding_index = encoding_index; + } + } + return f->encoding_index = next_encoding_index++; +} + +void ps_printer::set_char(int i, font *f, const environment *env, int w) +{ + if (i == space_char_index || invis_count > 0) + return; + unsigned char code = f->get_code(i); + style sty(f, env->size, env->height, env->slant); + if (sty.slant != 0) { + if (sty.slant > 80 || sty.slant < -80) { + error("silly slant `%1' degrees", sty.slant); + sty.slant = 0; + } + } + if (sbuf_len > 0) { + if (sbuf_len < SBUF_SIZE + && sty == sbuf_style + && sbuf_vpos == env->vpos) { + if (sbuf_end_hpos == env->hpos) { + sbuf[sbuf_len++] = code; + sbuf_end_hpos += w + sbuf_kern; + return; + } + if (sbuf_len == 1 && sbuf_kern == 0) { + sbuf_kern = env->hpos - sbuf_end_hpos; + sbuf_end_hpos = env->hpos + sbuf_kern + w; + sbuf[sbuf_len++] = code; + return; + } + /* If sbuf_end_hpos - sbuf_kern == env->hpos, we are better off + starting a new string. */ + if (sbuf_len < SBUF_SIZE - 1 && env->hpos >= sbuf_end_hpos + && (sbuf_kern == 0 || sbuf_end_hpos - sbuf_kern != env->hpos)) { + if (sbuf_space_code < 0) { + if (f->contains(space_char_index)) { + sbuf_space_code = f->get_code(space_char_index); + sbuf_space_width = env->hpos - sbuf_end_hpos; + sbuf_end_hpos = env->hpos + w + sbuf_kern; + sbuf[sbuf_len++] = sbuf_space_code; + sbuf[sbuf_len++] = code; + sbuf_space_count++; + return; + } + } + else { + int diff = env->hpos - sbuf_end_hpos - sbuf_space_width; + if (diff == 0 || (equalise_spaces && (diff == 1 || diff == -1))) { + sbuf_end_hpos = env->hpos + w + sbuf_kern; + sbuf[sbuf_len++] = sbuf_space_code; + sbuf[sbuf_len++] = code; + sbuf_space_count++; + if (diff == 1) + sbuf_space_diff_count++; + else if (diff == -1) + sbuf_space_diff_count--; + return; + } + } + } + } + flush_sbuf(); + } + sbuf_len = 1; + sbuf[0] = code; + sbuf_end_hpos = env->hpos + w; + sbuf_start_hpos = env->hpos; + sbuf_vpos = env->vpos; + sbuf_style = sty; + sbuf_space_code = -1; + sbuf_space_width = 0; + sbuf_space_count = sbuf_space_diff_count = 0; + sbuf_kern = 0; +} + +static char *make_encoding_name(int encoding_index) +{ + static char buf[3 + INT_DIGITS + 1]; + sprintf(buf, "ENC%d", encoding_index); + return buf; +} + +const char *const WS = " \t\n\r"; + +void ps_printer::define_encoding(const char *encoding, int encoding_index) +{ + char *vec[256]; + int i; + for (i = 0; i < 256; i++) + vec[i] = 0; + char *path; + FILE *fp = font::open_file(encoding, &path); + if (fp == 0) + fatal("can't open encoding file `%1'", encoding); + int lineno = 1; + char buf[256]; + while (fgets(buf, 512, fp) != 0) { + char *p = buf; + while (csspace(*p)) + p++; + if (*p != '#' && *p != '\0' && (p = strtok(buf, WS)) != 0) { + char *q = strtok(0, WS); + int n; + if (q == 0 || sscanf(q, "%d", &n) != 1 || n < 0 || n >= 256) + fatal_with_file_and_line(path, lineno, "bad second field"); + vec[n] = new char[strlen(p) + 1]; + strcpy(vec[n], p); + } + lineno++; + } + a_delete path; + out.put_literal_symbol(make_encoding_name(encoding_index)); + out.put_delimiter('['); + for (i = 0; i < 256; i++) { + if (vec[i] == 0) + out.put_literal_symbol(".notdef"); + else { + out.put_literal_symbol(vec[i]); + a_delete vec[i]; + } + } + out.put_delimiter(']').put_symbol("def"); +} + +void ps_printer::reencode_font(ps_font *f) +{ + out.put_literal_symbol(f->reencoded_name) + .put_symbol(make_encoding_name(f->encoding_index)) + .put_literal_symbol(f->get_internal_name()) + .put_symbol("RE"); +} + +void ps_printer::encode_fonts() +{ + if (next_encoding_index == 0) + return; + char *done_encoding = new char[next_encoding_index]; + for (int i = 0; i < next_encoding_index; i++) + done_encoding[i] = 0; + for (font_pointer_list *f = font_list; f; f = f->next) { + int encoding_index = ((ps_font *)f->p)->encoding_index; + if (encoding_index >= 0) { + assert(encoding_index < next_encoding_index); + if (!done_encoding[encoding_index]) { + done_encoding[encoding_index] = 1; + define_encoding(((ps_font *)f->p)->encoding, encoding_index); + } + reencode_font((ps_font *)f->p); + } + } + a_delete done_encoding; +} + +void ps_printer::set_style(const style &sty) +{ + char buf[1 + INT_DIGITS + 1]; + for (int i = 0; i < ndefined_styles; i++) + if (sty == defined_styles[i]) { + sprintf(buf, "F%d", i); + out.put_symbol(buf); + return; + } + if (ndefined_styles >= MAX_DEFINED_STYLES) + ndefined_styles = 0; + sprintf(buf, "F%d", ndefined_styles); + out.put_literal_symbol(buf); + const char *psname = sty.f->get_internal_name(); + if (psname == 0) + fatal("no internalname specified for font `%1'", sty.f->get_name()); + char *encoding = ((ps_font *)sty.f)->encoding; + if (encoding != 0) { + char *s = ((ps_font *)sty.f)->reencoded_name; + if (s == 0) { + int ei = set_encoding_index((ps_font *)sty.f); + char *tem = new char[strlen(psname) + 1 + INT_DIGITS + 1]; + sprintf(tem, "%s@%d", psname, ei); + psname = tem; + ((ps_font *)sty.f)->reencoded_name = tem; + } + else + psname = s; + } + out.put_fix_number((font::res/(72*font::sizescale))*sty.point_size); + if (sty.height != 0 || sty.slant != 0) { + int h = sty.height == 0 ? sty.point_size : sty.height; + h *= font::res/(72*font::sizescale); + int c = int(h*tan(radians(sty.slant)) + .5); + out.put_fix_number(c).put_fix_number(h).put_literal_symbol(psname) + .put_symbol("MF"); + } + else { + out.put_literal_symbol(psname).put_symbol("SF"); + } + defined_styles[ndefined_styles++] = sty; +} + +void ps_printer::set_space_code(unsigned char c) +{ + out.put_literal_symbol("SC").put_number(c).put_symbol("def"); +} + +void ps_printer::end_of_line() +{ + flush_sbuf(); + // this ensures that we do an absolute motion to the beginning of a line + output_vpos = output_hpos = -1; +} + +void ps_printer::flush_sbuf() +{ + enum { + NONE, + RELATIVE_H, + RELATIVE_V, + RELATIVE_HV, + ABSOLUTE + } motion = NONE; + int space_flag = 0; + if (sbuf_len == 0) + return; + if (output_style != sbuf_style) { + set_style(sbuf_style); + output_style = sbuf_style; + } + int extra_space = 0; + if (output_hpos < 0 || output_vpos < 0) + motion = ABSOLUTE; + else { + if (output_hpos != sbuf_start_hpos) + motion = RELATIVE_H; + if (output_vpos != sbuf_vpos) { + if (motion != NONE) + motion = RELATIVE_HV; + else + motion = RELATIVE_V; + } + } + if (sbuf_space_code >= 0) { + int w = sbuf_style.f->get_width(space_char_index, sbuf_style.point_size); + if (w + sbuf_kern != sbuf_space_width) { + if (sbuf_space_code != output_space_code) { + set_space_code(sbuf_space_code); + output_space_code = sbuf_space_code; + } + space_flag = 1; + extra_space = sbuf_space_width - w - sbuf_kern; + if (sbuf_space_diff_count > sbuf_space_count/2) + extra_space++; + else if (sbuf_space_diff_count < -(sbuf_space_count/2)) + extra_space--; + } + } + if (space_flag) + out.put_fix_number(extra_space); + if (sbuf_kern != 0) + out.put_fix_number(sbuf_kern); + out.put_string(sbuf, sbuf_len); + char sym[2]; + sym[0] = 'A' + motion*4 + space_flag + 2*(sbuf_kern != 0); + sym[1] = '\0'; + switch (motion) { + case NONE: + break; + case ABSOLUTE: + out.put_fix_number(sbuf_start_hpos) + .put_fix_number(sbuf_vpos); + break; + case RELATIVE_H: + out.put_fix_number(sbuf_start_hpos - output_hpos); + break; + case RELATIVE_V: + out.put_fix_number(sbuf_vpos - output_vpos); + break; + case RELATIVE_HV: + out.put_fix_number(sbuf_start_hpos - output_hpos) + .put_fix_number(sbuf_vpos - output_vpos); + break; + default: + assert(0); + } + out.put_symbol(sym); + output_hpos = sbuf_end_hpos; + output_vpos = sbuf_vpos; + sbuf_len = 0; +} + + +void ps_printer::set_line_thickness(const environment *env) +{ + if (line_thickness < 0) { + if (output_draw_point_size != env->size) { + // we ought to check for overflow here + int lw = ((font::res/(72*font::sizescale))*linewidth*env->size)/1000; + out.put_fix_number(lw).put_symbol("LW"); + output_draw_point_size = env->size; + output_line_thickness = -1; + } + } + else { + if (output_line_thickness != line_thickness) { + out.put_fix_number(line_thickness).put_symbol("LW"); + output_line_thickness = line_thickness; + output_draw_point_size = -1; + } + } +} + +void ps_printer::fill_path() +{ + if (fill > FILL_MAX) + out.put_symbol("BL"); + else + out.put_float(transform_fill(fill)).put_symbol("FL"); +} + +void ps_printer::draw(int code, int *p, int np, const environment *env) +{ + if (invis_count > 0) + return; + int fill_flag = 0; + switch (code) { + case 'C': + fill_flag = 1; + // fall through + case 'c': + // troff adds an extra argument to C + if (np != 1 && !(code == 'C' && np == 2)) { + error("1 argument required for circle"); + break; + } + out.put_fix_number(env->hpos + p[0]/2) + .put_fix_number(env->vpos) + .put_fix_number(p[0]/2) + .put_symbol("DC"); + if (fill_flag) { + fill_path(); + } + else { + set_line_thickness(env); + out.put_symbol("ST"); + } + break; + case 'l': + if (np != 2) { + error("2 arguments required for line"); + break; + } + set_line_thickness(env); + out.put_fix_number(p[0] + env->hpos) + .put_fix_number(p[1] + env->vpos) + .put_fix_number(env->hpos) + .put_fix_number(env->vpos) + .put_symbol("DL"); + break; + case 'E': + fill_flag = 1; + // fall through + case 'e': + if (np != 2) { + error("2 arguments required for ellipse"); + break; + } + out.put_fix_number(p[0]) + .put_fix_number(p[1]) + .put_fix_number(env->hpos + p[0]/2) + .put_fix_number(env->vpos) + .put_symbol("DE"); + if (fill_flag) { + fill_path(); + } + else { + set_line_thickness(env); + out.put_symbol("ST"); + } + break; + case 'P': + fill_flag = 1; + // fall through + case 'p': + { + if (np & 1) { + error("even number of arguments required for polygon"); + break; + } + if (np == 0) { + error("no arguments for polygon"); + break; + } + out.put_fix_number(env->hpos) + .put_fix_number(env->vpos) + .put_symbol("MT"); + for (int i = 0; i < np; i += 2) + out.put_fix_number(p[i]) + .put_fix_number(p[i+1]) + .put_symbol("RL"); + out.put_symbol("CL"); + if (fill_flag) { + fill_path(); + } + else { + set_line_thickness(env); + out.put_symbol("ST"); + } + break; + } + case '~': + { + if (np & 1) { + error("even number of arguments required for spline"); + break; + } + if (np == 0) { + error("no arguments for spline"); + break; + } + out.put_fix_number(env->hpos) + .put_fix_number(env->vpos) + .put_symbol("MT"); + out.put_fix_number(p[0]/2) + .put_fix_number(p[1]/2) + .put_symbol("RL"); + /* tnum/tden should be between 0 and 1; the closer it is to 1 + the tighter the curve will be to the guiding lines; 2/3 + is the standard value */ + const int tnum = 2; + const int tden = 3; + for (int i = 0; i < np - 2; i += 2) { + out.put_fix_number((p[i]*tnum)/(2*tden)) + .put_fix_number((p[i + 1]*tnum)/(2*tden)) + .put_fix_number(p[i]/2 + (p[i + 2]*(tden - tnum))/(2*tden)) + .put_fix_number(p[i + 1]/2 + (p[i + 3]*(tden - tnum))/(2*tden)) + .put_fix_number((p[i] - p[i]/2) + p[i + 2]/2) + .put_fix_number((p[i + 1] - p[i + 1]/2) + p[i + 3]/2) + .put_symbol("RC"); + } + out.put_fix_number(p[np - 2] - p[np - 2]/2) + .put_fix_number(p[np - 1] - p[np - 1]/2) + .put_symbol("RL"); + set_line_thickness(env); + out.put_symbol("ST"); + } + break; + case 'a': + { + if (np != 4) { + error("4 arguments required for arc"); + break; + } + set_line_thickness(env); + double c[2]; + if (adjust_arc_center(p, c)) + out.put_fix_number(env->hpos + int(c[0])) + .put_fix_number(env->vpos + int(c[1])) + .put_fix_number(int(sqrt(c[0]*c[0] + c[1]*c[1]))) + .put_float(degrees(atan2(-c[1], -c[0]))) + .put_float(degrees(atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0]))) + .put_symbol("DA"); + else + out.put_fix_number(p[0] + p[2] + env->hpos) + .put_fix_number(p[1] + p[3] + env->vpos) + .put_fix_number(env->hpos) + .put_fix_number(env->vpos) + .put_symbol("DL"); + } + break; + case 't': + { + if (np == 0) { + line_thickness = -1; + } + else { + // troff gratuitously adds an extra 0 + if (np != 1 && np != 2) { + error("0 or 1 argument required for thickness"); + break; + } + line_thickness = p[0]; + } + break; + } + case 'f': + { + if (np != 1 && np != 2) { + error("1 argument required for fill"); + break; + } + fill = p[0]; + if (fill < 0 || fill > FILL_MAX) { + // This means fill with the current color. + fill = FILL_MAX + 1; + } + break; + } + default: + error("unrecognised drawing command `%1'", char(code)); + break; + } + + output_hpos = output_vpos = -1; +} + + +void ps_printer::begin_page(int n) +{ + out.begin_comment("Page:").comment_arg(itoa(n)); + out.comment_arg(itoa(++pages_output)).end_comment(); + output_style.f = 0; + output_space_code = 32; + output_draw_point_size = -1; + output_line_thickness = -1; + output_hpos = output_vpos = -1; + ndefined_styles = 0; + out.simple_comment("BeginPageSetup"); + out.put_symbol("BP"); + out.simple_comment("EndPageSetup"); +} + +void ps_printer::end_page(int) +{ + flush_sbuf(); + out.put_symbol("EP"); + if (invis_count != 0) { + error("missing `endinvis' command"); + invis_count = 0; + } +} + +font *ps_printer::make_font(const char *nm) +{ + return ps_font::load_ps_font(nm); +} + +ps_printer::~ps_printer() +{ + out.simple_comment("Trailer"); + out.put_symbol("end"); + out.simple_comment("EOF"); + if (fseek(tempfp, 0L, 0) < 0) + fatal("fseek on temporary file failed"); + fputs("%!PS-Adobe-", stdout); + fputs((broken_flags & USE_PS_ADOBE_2_0) ? "2.0" : "3.0", stdout); + putchar('\n'); + out.set_file(stdout); + { + extern const char *version_string; + out.begin_comment("Creator:") + .comment_arg("groff") + .comment_arg("version") + .comment_arg(version_string) + .end_comment(); + } + { + fputs("%%CreationDate: ", out.get_file()); +#ifdef LONG_FOR_TIME_T + long +#else + time_t +#endif + t = time(0); + fputs(ctime(&t), out.get_file()); + } + for (font_pointer_list *f = font_list; f; f = f->next) { + ps_font *psf = (ps_font *)(f->p); + rm.need_font(psf->get_internal_name()); + } + rm.print_header_comments(out); + out.begin_comment("Pages:").comment_arg(itoa(pages_output)).end_comment(); + out.begin_comment("PageOrder:").comment_arg("Ascend").end_comment(); +#if 0 + fprintf(out.get_file(), "%%%%DocumentMedia: () %g %g 0 () ()\n", + font::paperwidth*72.0/font::res, + paper_length*72.0/font::res); +#endif + out.begin_comment("Orientation:") + .comment_arg(landscape_flag ? "Landscape" : "Portrait") + .end_comment(); + if (ncopies != 1) { + out.end_line(); + fprintf(out.get_file(), "%%%%Requirements: numcopies(%d)\n", ncopies); + } + out.simple_comment("EndComments"); + out.simple_comment("BeginProlog"); + rm.output_prolog(out); + if (!(broken_flags & NO_SETUP_SECTION)) { + out.simple_comment("EndProlog"); + out.simple_comment("BeginSetup"); + } + rm.document_setup(out); + out.put_symbol(dict_name).put_symbol("begin"); + if (ndefs > 0) + ndefs += DEFS_DICT_SPARE; + out.put_literal_symbol(defs_dict_name) + .put_number(ndefs + 1) + .put_symbol("dict") + .put_symbol("def"); + out.put_symbol(defs_dict_name) + .put_symbol("begin"); + out.put_literal_symbol("u") + .put_delimiter('{') + .put_fix_number(1) + .put_symbol("mul") + .put_delimiter('}') + .put_symbol("bind") + .put_symbol("def"); + defs += '\0'; + out.special(defs.contents()); + out.put_symbol("end"); + if (ncopies != 1) + out.put_literal_symbol("#copies").put_number(ncopies).put_symbol("def"); + out.put_literal_symbol("RES").put_number(res).put_symbol("def"); + out.put_literal_symbol("PL"); + if (guess_flag) + out.put_symbol("PLG"); + else + out.put_fix_number(paper_length); + out.put_symbol("def"); + out.put_literal_symbol("LS") + .put_symbol(landscape_flag ? "true" : "false") + .put_symbol("def"); + if (manual_feed_flag) { + out.begin_comment("BeginFeature:") + .comment_arg("*ManualFeed") + .comment_arg("True") + .end_comment() + .put_symbol("MANUAL") + .simple_comment("EndFeature"); + } + encode_fonts(); + out.simple_comment((broken_flags & NO_SETUP_SECTION) + ? "EndProlog" + : "EndSetup"); + out.end_line(); + out.copy_file(tempfp); + fclose(tempfp); +} + +void ps_printer::special(char *arg, const environment *env) +{ + typedef void (ps_printer::*SPECIAL_PROCP)(char *, const environment *); + static struct { + const char *name; + SPECIAL_PROCP proc; + } proc_table[] = { + { "exec", &ps_printer::do_exec }, + { "def", &ps_printer::do_def }, + { "mdef", &ps_printer::do_mdef }, + { "import", &ps_printer::do_import }, + { "file", &ps_printer::do_file }, + { "invis", &ps_printer::do_invis }, + { "endinvis", &ps_printer::do_endinvis }, + }; + char *p; + for (p = arg; *p == ' ' || *p == '\n'; p++) + ; + char *tag = p; + for (; *p != '\0' && *p != ':' && *p != ' ' && *p != '\n'; p++) + ; + if (*p == '\0' || strncmp(tag, "ps", p - tag) != 0) { + error("X command without `ps:' tag ignored"); + return; + } + p++; + for (; *p == ' ' || *p == '\n'; p++) + ; + char *command = p; + for (; *p != '\0' && *p != ' ' && *p != '\n'; p++) + ; + if (*command == '\0') { + error("X command without `ps:' tag ignored"); + return; + } + for (int i = 0; i < sizeof(proc_table)/sizeof(proc_table[0]); i++) + if (strncmp(command, proc_table[i].name, p - command) == 0) { + (this->*(proc_table[i].proc))(p, env); + return; + } + error("X command `%1' not recognised", command); +} + +// A conforming PostScript document must not have lines longer +// than 255 characters (excluding line termination characters). + +static int check_line_lengths(const char *p) +{ + for (;;) { + const char *end = strchr(p, '\n'); + if (end == 0) + end = strchr(p, '\0'); + if (end - p > 255) + return 0; + if (*end == '\0') + break; + p = end + 1; + } + return 1; +} + +void ps_printer::do_exec(char *arg, const environment *env) +{ + flush_sbuf(); + while (csspace(*arg)) + arg++; + if (*arg == '\0') { + error("missing argument to X exec command"); + return; + } + if (!check_line_lengths(arg)) { + error("lines in X exec command must not be more than 255 characters long"); + return; + } + out.put_fix_number(env->hpos) + .put_fix_number(env->vpos) + .put_symbol("EBEGIN") + .special(arg) + .put_symbol("EEND"); + output_hpos = output_vpos = -1; + output_style.f = 0; + output_draw_point_size = -1; + output_line_thickness = -1; + ndefined_styles = 0; + if (!ndefs) + ndefs = 1; +} + +void ps_printer::do_file(char *arg, const environment *env) +{ + flush_sbuf(); + while (csspace(*arg)) + arg++; + if (*arg == '\0') { + error("missing argument to X file command"); + return; + } + const char *filename = arg; + do { + ++arg; + } while (*arg != '\0' && *arg != ' ' && *arg != '\n'); + out.put_fix_number(env->hpos) + .put_fix_number(env->vpos) + .put_symbol("EBEGIN"); + rm.import_file(filename, out); + out.put_symbol("EEND"); + output_hpos = output_vpos = -1; + output_style.f = 0; + output_draw_point_size = -1; + output_line_thickness = -1; + ndefined_styles = 0; + if (!ndefs) + ndefs = 1; +} + +void ps_printer::do_def(char *arg, const environment *) +{ + flush_sbuf(); + while (csspace(*arg)) + arg++; + if (!check_line_lengths(arg)) { + error("lines in X def command must not be more than 255 characters long"); + return; + } + defs += arg; + if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n') + defs += '\n'; + ndefs++; +} + +// Like def, but the first argument says how many definitions it contains. + +void ps_printer::do_mdef(char *arg, const environment *) +{ + flush_sbuf(); + char *p; + int n = (int)strtol(arg, &p, 10); + if (n == 0 && p == arg) { + error("first argument to X mdef must be an integer"); + return; + } + if (n < 0) { + error("out of range argument `%1' to X mdef command", int(n)); + return; + } + arg = p; + while (csspace(*arg)) + arg++; + if (!check_line_lengths(arg)) { + error("lines in X mdef command must not be more than 255 characters long"); + return; + } + defs += arg; + if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n') + defs += '\n'; + ndefs += n; +} + +void ps_printer::do_import(char *arg, const environment *env) +{ + flush_sbuf(); + while (*arg == ' ' || *arg == '\n') + arg++; + char *p; + for (p = arg; *p != '\0' && *p != ' ' && *p != '\n'; p++) + ; + if (*p != '\0') + *p++ = '\0'; + int parms[6]; + int nparms = 0; + while (nparms < 6) { + char *end; + long n = strtol(p, &end, 10); + if (n == 0 && end == p) + break; + parms[nparms++] = int(n); + p = end; + } + if (csalpha(*p) && (p[1] == '\0' || p[1] == ' ' || p[1] == '\n')) { + error("scaling indicators not allowed in arguments for X import command"); + return; + } + while (*p == ' ' || *p == '\n') + p++; + if (nparms < 5) { + if (*p == '\0') + error("too few arguments for X import command"); + else + error("invalid argument `%1' for X import command", p); + return; + } + if (*p != '\0') { + error("superflous argument `%1' for X import command", p); + return; + } + int llx = parms[0]; + int lly = parms[1]; + int urx = parms[2]; + int ury = parms[3]; + int desired_width = parms[4]; + int desired_height = parms[5]; + if (desired_width <= 0) { + error("bad width argument `%1' for X import command: must be > 0", + desired_width); + return; + } + if (nparms == 6 && desired_height <= 0) { + error("bad height argument `%1' for X import command: must be > 0", + desired_height); + return; + } + if (llx == urx) { + error("llx and urx arguments for X import command must not be equal"); + return; + } + if (lly == ury) { + error("lly and ury arguments for X import command must not be equal"); + return; + } + if (nparms == 5) { + int old_wid = urx - llx; + int old_ht = ury - lly; + if (old_wid < 0) + old_wid = -old_wid; + if (old_ht < 0) + old_ht = -old_ht; + desired_height = int(desired_width*(double(old_ht)/double(old_wid)) + .5); + } + if (env->vpos - desired_height < 0) + warning("top of imported graphic is above the top of the page"); + out.put_number(llx) + .put_number(lly) + .put_fix_number(desired_width) + .put_number(urx - llx) + .put_fix_number(-desired_height) + .put_number(ury - lly) + .put_fix_number(env->hpos) + .put_fix_number(env->vpos) + .put_symbol("PBEGIN"); + rm.import_file(arg, out); + // do this here just in case application defines PEND + out.put_symbol("end"); + out.put_symbol("PEND"); +} + +void ps_printer::do_invis(char *, const environment *) +{ + invis_count++; +} + +void ps_printer::do_endinvis(char *, const environment *) +{ + if (invis_count == 0) + error("unbalanced `endinvis' command"); + else + --invis_count; +} + +printer *make_printer() +{ + return new ps_printer; +} + +static void usage(); + +int main(int argc, char **argv) +{ + program_name = argv[0]; + static char stderr_buf[BUFSIZ]; + setbuf(stderr, stderr_buf); + int c; + while ((c = getopt(argc, argv, "F:glmc:w:vb:")) != EOF) + switch(c) { + case 'v': + { + extern const char *version_string; + fprintf(stderr, "grops version %s\n", version_string); + fflush(stderr); + break; + } + case 'c': + if (sscanf(optarg, "%d", &ncopies) != 1 || ncopies <= 0) { + error("bad number of copies `%s'", optarg); + ncopies = 1; + } + break; + case 'g': + guess_flag = 1; + break; + case 'l': + landscape_flag = 1; + break; + case 'm': + manual_feed_flag = 1; + break; + case 'F': + font::command_line_font_dir(optarg); + break; + case 'w': + if (sscanf(optarg, "%d", &linewidth) != 1 || linewidth < 0) { + error("bad linewidth `%1'", optarg); + linewidth = -1; + } + break; + case 'b': + // XXX check this + broken_flags = atoi(optarg); + bflag = 1; + break; + case '?': + usage(); + break; + default: + assert(0); + } + font::set_unknown_desc_command_handler(handle_unknown_desc_command); + if (optind >= argc) + do_file("-"); + else { + for (int i = optind; i < argc; i++) + do_file(argv[i]); + } + delete pr; + return 0; +} + +static void usage() +{ + fprintf(stderr, "usage: %s [-glmv] [-b n] [-c n] [-w n] [-F dir] [files ...]\n", + program_name); + exit(1); +} diff --git a/contrib/groff/grops/ps.h b/contrib/groff/grops/ps.h new file mode 100644 index 0000000..6e78597 --- /dev/null +++ b/contrib/groff/grops/ps.h @@ -0,0 +1,122 @@ +// -*- C++ -*- +/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc. + Written by James Clark (jjc@jclark.com) + +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with groff; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +class ps_output { +public: + ps_output(FILE *, int max_line_length); + ps_output &put_string(const char *, int); + ps_output &put_number(int); + ps_output &put_fix_number(int); + ps_output &put_float(double); + ps_output &put_symbol(const char *); + ps_output &put_literal_symbol(const char *); + ps_output &set_fixed_point(int); + ps_output &simple_comment(const char *); + ps_output &begin_comment(const char *); + ps_output &comment_arg(const char *); + ps_output &end_comment(); + ps_output &set_file(FILE *); + ps_output &include_file(FILE *); + ps_output ©_file(FILE *); + ps_output &end_line(); + ps_output &put_delimiter(char); + ps_output &special(const char *); + FILE *get_file(); +private: + FILE *fp; + int col; + int max_line_length; // not including newline + int need_space; + int fixed_point; +}; + +inline FILE *ps_output::get_file() +{ + return fp; +} + +enum resource_type { + RESOURCE_FONT, + RESOURCE_PROCSET, + RESOURCE_FILE, + RESOURCE_ENCODING, + RESOURCE_FORM, + RESOURCE_PATTERN + }; + +struct resource; + +extern string an_empty_string; + +class resource_manager { +public: + resource_manager(); + ~resource_manager(); + void import_file(const char *filename, ps_output &); + void need_font(const char *name); + void print_header_comments(ps_output &); + void document_setup(ps_output &); + void output_prolog(ps_output &); +private: + unsigned extensions; + unsigned language_level; + resource *procset_resource; + resource *resource_list; + resource *lookup_resource(resource_type type, string &name, + string &version = an_empty_string, + unsigned revision = 0); + resource *lookup_font(const char *name); + void read_download_file(); + void supply_resource(resource *r, int rank, FILE *outfp, + int is_document = 0); + void process_file(int rank, FILE *fp, const char *filename, FILE *outfp); + resource *read_file_arg(const char **); + resource *read_procset_arg(const char **); + resource *read_font_arg(const char **); + resource *read_resource_arg(const char **); + void print_resources_comment(unsigned flag, FILE *outfp); + void print_extensions_comment(FILE *outfp); + void print_language_level_comment(FILE *outfp); + int do_begin_resource(const char *ptr, int rank, FILE *fp, FILE *outfp); + int do_include_resource(const char *ptr, int rank, FILE *fp, FILE *outfp); + int do_begin_document(const char *ptr, int rank, FILE *fp, FILE *outfp); + int do_include_document(const char *ptr, int rank, FILE *fp, FILE *outfp); + int do_begin_procset(const char *ptr, int rank, FILE *fp, FILE *outfp); + int do_include_procset(const char *ptr, int rank, FILE *fp, FILE *outfp); + int do_begin_font(const char *ptr, int rank, FILE *fp, FILE *outfp); + int do_include_font(const char *ptr, int rank, FILE *fp, FILE *outfp); + int do_begin_file(const char *ptr, int rank, FILE *fp, FILE *outfp); + int do_include_file(const char *ptr, int rank, FILE *fp, FILE *outfp); + int change_to_end_resource(const char *ptr, int rank, FILE *fp, FILE *outfp); + int do_begin_preview(const char *ptr, int rank, FILE *fp, FILE *outfp); + int do_begin_data(const char *ptr, int rank, FILE *fp, FILE *outfp); + int do_begin_binary(const char *ptr, int rank, FILE *fp, FILE *outfp); +}; + +extern unsigned broken_flags; + +// broken_flags is ored from these + +enum { + NO_SETUP_SECTION = 01, + STRIP_PERCENT_BANG = 02, + STRIP_STRUCTURE_COMMENTS = 04, + USE_PS_ADOBE_2_0 = 010 +}; diff --git a/contrib/groff/grops/psfig.diff b/contrib/groff/grops/psfig.diff new file mode 100644 index 0000000..5be080d --- /dev/null +++ b/contrib/groff/grops/psfig.diff @@ -0,0 +1,106 @@ +These are patches to makes psfig work with groff. They apply to the +version of psfig in comp.sources.unix/Volume11. After applying them, +psfig should be recompiled with -DGROFF. The resulting psfig will +work only with groff, so you might want to install it under a +different name. The output of this psfig must be processed using the +macros in the file ../tmac/tmac.psfig. These will automatically add +the necessary PostScript code to the prologue output by grops. Use of +the `global' feature in psfig will result in non-conformant PostScript +which will fail if processed by a page reversal program. Note that +psfig is unsupported by me (I'm not interested in hearing about psfig +problems.) For new documents, I recommend using the PostScript +inclusion features provided by grops. + +James Clark +jjc@jclark.com + +*** cmds.c.~1~ Thu Feb 14 16:09:45 1991 +--- cmds.c Mon Mar 4 12:49:26 1991 +*************** +*** 245,253 **** +--- 245,261 ---- + (void) sprintf(x, "%.2fp", fx); + (void) sprintf(y, "%.2fp", fy); + } else if (!*x) { ++ #ifndef GROFF + (void) sprintf(x,"(%.2fp*%s/%.2fp)", fx, y, fy); ++ #else /* GROFF */ ++ (void) sprintf(x,"(%.0fu*%s/%.0fu)", fx, y, fy); ++ #endif /* GROFF */ + } else if (!*y) { ++ #ifndef GROFF + (void) sprintf(y,"(%.2fp*%s/%.2fp)", fy, x, fx); ++ #else /* GROFF */ ++ (void) sprintf(y,"(%.0fu*%s/%.0fu)", fy, x, fx); ++ #endif /* GROFF */ + } + + /* +*** troff.c.~1~ Thu Feb 14 16:09:48 1991 +--- troff.c Mon Mar 4 12:48:46 1991 +*************** +*** 26,32 **** +--- 26,36 ---- + } + + ++ #ifndef GROFF + char incl_file_s[] = "\\X'f%s'"; ++ #else /* GROFF */ ++ char incl_file_s[] = "\\X'ps: file %s'"; ++ #endif /* GROFF */ + includeFile(filenm) + char *filenm; { + printf(incl_file_s, filenm); +*************** +*** 40,52 **** +--- 44,64 ---- + error("buffer overflow"); + } + ++ #ifndef GROFF + char endfig_s[] = "\\X'pendFig'"; ++ #else /* GROFF */ ++ char endfig_s[] = "\\X'ps: exec psfigend'"; ++ #endif /* GROFF */ + endfig() { + printf(endfig_s); + } + + char startfig_s[] = ++ #ifndef GROFF + "\\X'p\\w@\\h@%s@@'\\X'p\\w@\\h@%s@@'\\X'p%.2f'\\X'p%.2f'\\X'p%.2f'\\X'p%.2f'\\X'pstartFig'"; ++ #else /* GROFF */ ++ "\\X'ps: exec \\w@\\h@%s@@ \\w@\\h@%s@@ %.2f %.2f %.2f %.2f psfigstart'"; ++ #endif /* GROFF */ + + startfig(x, y, llx, lly, urx, ury) + char *x, *y; +*************** +*** 57,63 **** +--- 69,79 ---- + } + + emitDoClip() { ++ #ifndef GROFF + printf("\\X'pdoclip'"); ++ #else /* GROFF */ ++ printf("\\X'ps: exec psfigclip'"); ++ #endif /* GROFF */ + } + + flushX() +*************** +*** 116,122 **** +--- 132,142 ---- + + #define isWhite(ch) ((ch) == ' ' || (ch) == '\t' || (ch) == '\n') + ++ #ifndef GROFF + char literal_s[] = "\\X'p%s'"; ++ #else /* GROFF */ ++ char literal_s[] = "\\X'ps: exec %s'"; ++ #endif /* GROFF */ + emitLiteral(text) + char *text; { + static char litbuf[BUFSZ]; diff --git a/contrib/groff/grops/psrm.cc b/contrib/groff/grops/psrm.cc new file mode 100644 index 0000000..3030aef --- /dev/null +++ b/contrib/groff/grops/psrm.cc @@ -0,0 +1,1099 @@ +// -*- C++ -*- +/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc. + Written by James Clark (jjc@jclark.com) + +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with groff; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "driver.h" +#include "stringclass.h" +#include "cset.h" + +#include "ps.h" + +#define PROLOGUE "prologue" + +static void print_ps_string(const string &s, FILE *outfp); + +cset white_space("\n\r \t"); +string an_empty_string; + +const char *extension_table[] = { + "DPS", + "CMYK", + "Composite", + "FileSystem", +}; + +const int NEXTENSIONS = sizeof(extension_table)/sizeof(extension_table[0]); + +const char *resource_table[] = { + "font", + "procset", + "file", + "encoding", + "form", + "pattern", +}; + +const int NRESOURCES = sizeof(resource_table)/sizeof(resource_table[0]); + +struct resource { + resource *next; + resource_type type; + string name; + enum { NEEDED = 01, SUPPLIED = 02, FONT_NEEDED = 04, BUSY = 010 }; + unsigned flags; + string version; + unsigned revision; + char *filename; + int rank; + resource(resource_type, string &, string & = an_empty_string, unsigned = 0); + ~resource(); + void print_type_and_name(FILE *outfp); +}; + +resource::resource(resource_type t, string &n, string &v, unsigned r) +: type(t), revision(r), flags (0), filename(0), rank(-1), next(0) +{ + name.move(n); + version.move(v); + if (type == RESOURCE_FILE) { + if (name.search('\0') >= 0) + error("filename contains a character with code 0"); + filename = name.extract(); + } +} + +resource::~resource() +{ + a_delete filename; +} + +void resource::print_type_and_name(FILE *outfp) +{ + fputs(resource_table[type], outfp); + putc(' ', outfp); + print_ps_string(name, outfp); + if (type == RESOURCE_PROCSET) { + putc(' ', outfp); + print_ps_string(version, outfp); + fprintf(outfp, " %u", revision); + } +} + +resource_manager::resource_manager() +: resource_list(0), extensions(0), language_level(0) +{ + read_download_file(); + string procset_name("grops"); + extern const char *version_string; + string procset_version(version_string); + procset_resource = lookup_resource(RESOURCE_PROCSET, procset_name, + procset_version, 0); + procset_resource->flags |= resource::SUPPLIED; +} + +resource_manager::~resource_manager() +{ + while (resource_list) { + resource *tem = resource_list; + resource_list = resource_list->next; + delete tem; + } +} + +resource *resource_manager::lookup_resource(resource_type type, + string &name, + string &version, + unsigned revision) +{ + resource *r; + for (r = resource_list; r; r = r->next) + if (r->type == type + && r->name == name + && r->version == version + && r->revision == revision) + return r; + r = new resource(type, name, version, revision); + r->next = resource_list; + resource_list = r; + return r; +} + +// Just a specialized version of lookup_resource(). + +resource *resource_manager::lookup_font(const char *name) +{ + resource *r; + for (r = resource_list; r; r = r->next) + if (r->type == RESOURCE_FONT + && strlen(name) == r->name.length() + && memcmp(name, r->name.contents(), r->name.length()) == 0) + return r; + string s(name); + r = new resource(RESOURCE_FONT, s); + r->next = resource_list; + resource_list = r; + return r; +} + +void resource_manager::need_font(const char *name) +{ + lookup_font(name)->flags |= resource::FONT_NEEDED; +} + +typedef resource *Presource; // Work around g++ bug. + +void resource_manager::document_setup(ps_output &out) +{ + int nranks = 0; + resource *r; + for (r = resource_list; r; r = r->next) + if (r->rank >= nranks) + nranks = r->rank + 1; + if (nranks > 0) { + // Sort resource_list in reverse order of rank. + Presource *head = new Presource[nranks + 1]; + Presource **tail = new Presource *[nranks + 1]; + int i; + for (i = 0; i < nranks + 1; i++) { + head[i] = 0; + tail[i] = &head[i]; + } + for (r = resource_list; r; r = r->next) { + i = r->rank < 0 ? 0 : r->rank + 1; + *tail[i] = r; + tail[i] = &(*tail[i])->next; + } + resource_list = 0; + for (i = 0; i < nranks + 1; i++) + if (head[i]) { + *tail[i] = resource_list; + resource_list = head[i]; + } + a_delete head; + a_delete tail; + // check it + for (r = resource_list; r; r = r->next) + if (r->next) + assert(r->rank >= r->next->rank); + for (r = resource_list; r; r = r->next) + if (r->type == RESOURCE_FONT && r->rank >= 0) + supply_resource(r, -1, out.get_file()); + } +} + +void resource_manager::print_resources_comment(unsigned flag, FILE *outfp) +{ + int continued = 0; + for (resource *r = resource_list; r; r = r->next) + if (r->flags & flag) { + if (continued) + fputs("%%+ ", outfp); + else { + fputs(flag == resource::NEEDED + ? "%%DocumentNeededResources: " + : "%%DocumentSuppliedResources: ", + outfp); + continued = 1; + } + r->print_type_and_name(outfp); + putc('\n', outfp); + } +} + +void resource_manager::print_header_comments(ps_output &out) +{ + for (resource *r = resource_list; r; r = r->next) + if (r->type == RESOURCE_FONT && (r->flags & resource::FONT_NEEDED)) + supply_resource(r, 0, 0); + print_resources_comment(resource::NEEDED, out.get_file()); + print_resources_comment(resource::SUPPLIED, out.get_file()); + print_language_level_comment(out.get_file()); + print_extensions_comment(out.get_file()); +} + +void resource_manager::output_prolog(ps_output &out) +{ + FILE *outfp = out.get_file(); + out.end_line(); + char *path; + FILE *fp = font::open_file(PROLOGUE, &path); + if (!fp) + fatal("can't find `%1'", PROLOGUE); + fputs("%%BeginResource: ", outfp); + procset_resource->print_type_and_name(outfp); + putc('\n', outfp); + process_file(-1, fp, path, outfp); + fclose(fp); + a_delete path; + fputs("%%EndResource\n", outfp); +} + +void resource_manager::import_file(const char *filename, ps_output &out) +{ + out.end_line(); + string name(filename); + resource *r = lookup_resource(RESOURCE_FILE, name); + supply_resource(r, -1, out.get_file(), 1); +} + +void resource_manager::supply_resource(resource *r, int rank, FILE *outfp, + int is_document) +{ + if (r->flags & resource::BUSY) { + r->name += '\0'; + fatal("loop detected in dependency graph for %1 `%2'", + resource_table[r->type], + r->name.contents()); + } + r->flags |= resource::BUSY; + if (rank > r->rank) + r->rank = rank; + char *path; + FILE *fp = 0; + if (r->filename != 0) { + if (r->type == RESOURCE_FONT) { + fp = font::open_file(r->filename, &path); + if (!fp) { + error("can't find `%1'", r->filename); + a_delete r->filename; + r->filename = 0; + } + } + else { + errno = 0; + fp = fopen(r->filename, "r"); + if (!fp) { + error("can't open `%1': %2", r->filename, strerror(errno)); + a_delete r->filename; + r->filename = 0; + } + else + path = r->filename; + } + } + if (fp) { + if (outfp) { + if (r->type == RESOURCE_FILE && is_document) { + fputs("%%BeginDocument: ", outfp); + print_ps_string(r->name, outfp); + putc('\n', outfp); + } + else { + fputs("%%BeginResource: ", outfp); + r->print_type_and_name(outfp); + putc('\n', outfp); + } + } + process_file(rank, fp, path, outfp); + fclose(fp); + if (r->type == RESOURCE_FONT) + a_delete path; + if (outfp) { + if (r->type == RESOURCE_FILE && is_document) + fputs("%%EndDocument\n", outfp); + else + fputs("%%EndResource\n", outfp); + } + r->flags |= resource::SUPPLIED; + } + else { + if (outfp) { + if (r->type == RESOURCE_FILE && is_document) { + fputs("%%IncludeDocument: ", outfp); + print_ps_string(r->name, outfp); + putc('\n', outfp); + } + else { + fputs("%%IncludeResource: ", outfp); + r->print_type_and_name(outfp); + putc('\n', outfp); + } + } + r->flags |= resource::NEEDED; + } + r->flags &= ~resource::BUSY; +} + + +#define PS_LINE_MAX 255 +#define PS_MAGIC "%!PS-Adobe-" + +static int ps_get_line(char *buf, FILE *fp) +{ + int c = getc(fp); + if (c == EOF) { + buf[0] = '\0'; + return 0; + } + current_lineno++; + int i = 0; + int err = 0; + while (c != '\r' && c != '\n' && c != EOF) { + if ((c < 0x1b && !white_space(c)) || c == 0x7f) + error("illegal input character code %1", int(c)); + else if (i < PS_LINE_MAX) + buf[i++] = c; + else if (!err) { + err = 1; + error("PostScript file non-conforming " + "because length of line exceeds 255"); + } + c = getc(fp); + } + buf[i++] = '\n'; + buf[i] = '\0'; + if (c == '\r') { + c = getc(fp); + if (c != EOF && c != '\n') + ungetc(c, fp); + } + return 1; +} + +static int read_text_arg(const char **pp, string &res) +{ + res.clear(); + while (white_space(**pp)) + *pp += 1; + if (**pp == '\0') { + error("missing argument"); + return 0; + } + if (**pp != '(') { + for (; **pp != '\0' && !white_space(**pp); *pp += 1) + res += **pp; + return 1; + } + *pp += 1; + res.clear(); + int level = 0; + for (;;) { + if (**pp == '\0' || **pp == '\r' || **pp == '\n') { + error("missing ')'"); + return 0; + } + if (**pp == ')') { + if (level == 0) { + *pp += 1; + break; + } + res += **pp; + level--; + } + else if (**pp == '(') { + level++; + res += **pp; + } + else if (**pp == '\\') { + *pp += 1; + switch (**pp) { + case 'n': + res += '\n'; + break; + case 'r': + res += '\n'; + break; + case 't': + res += '\t'; + break; + case 'b': + res += '\b'; + break; + case 'f': + res += '\f'; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + int val = **pp - '0'; + if ((*pp)[1] >= '0' && (*pp)[1] <= '7') { + *pp += 1; + val = val*8 + (**pp - '0'); + if ((*pp)[1] >= '0' && (*pp)[1] <= '7') { + *pp += 1; + val = val*8 + (**pp - '0'); + } + } + } + break; + default: + res += **pp; + break; + } + } + else + res += **pp; + *pp += 1; + } + return 1; +} + +static int read_uint_arg(const char **pp, unsigned *res) +{ + while (white_space(**pp)) + *pp += 1; + if (**pp == '\0') { + error("missing argument"); + return 0; + } + const char *start = *pp; + // XXX use strtoul + long n = strtol(start, (char **)pp, 10); + if (n == 0 && *pp == start) { + error("not an integer"); + return 0; + } + if (n < 0) { + error("argument must not be negative"); + return 0; + } + *res = unsigned(n); + return 1; +} + +resource *resource_manager::read_file_arg(const char **ptr) +{ + string arg; + if (!read_text_arg(ptr, arg)) + return 0; + return lookup_resource(RESOURCE_FILE, arg); +} + +resource *resource_manager::read_font_arg(const char **ptr) +{ + string arg; + if (!read_text_arg(ptr, arg)) + return 0; + return lookup_resource(RESOURCE_FONT, arg); +} + +resource *resource_manager::read_procset_arg(const char **ptr) +{ + string arg; + if (!read_text_arg(ptr, arg)) + return 0; + string version; + if (!read_text_arg(ptr, version)) + return 0; + unsigned revision; + if (!read_uint_arg(ptr, &revision)) + return 0; + return lookup_resource(RESOURCE_PROCSET, arg, version, revision); +} + +resource *resource_manager::read_resource_arg(const char **ptr) +{ + while (white_space(**ptr)) + *ptr += 1; + const char *name = *ptr; + while (**ptr != '\0' && !white_space(**ptr)) + *ptr += 1; + if (name == *ptr) { + error("missing resource type"); + return 0; + } + int ri; + for (ri = 0; ri < NRESOURCES; ri++) + if (strlen(resource_table[ri]) == *ptr - name + && memcmp(resource_table[ri], name, *ptr - name) == 0) + break; + if (ri >= NRESOURCES) { + error("unknown resource type"); + return 0; + } + if (ri == RESOURCE_PROCSET) + return read_procset_arg(ptr); + string arg; + if (!read_text_arg(ptr, arg)) + return 0; + return lookup_resource(resource_type(ri), arg); +} + +static const char *matches_comment(const char *buf, const char *comment) +{ + if (buf[0] != '%' || buf[1] != '%') + return 0; + for (buf += 2; *comment; comment++, buf++) + if (*buf != *comment) + return 0; + if (comment[-1] == ':') + return buf; + if (*buf == '\0' || white_space(*buf)) + return buf; + return 0; +} + +// Return 1 if the line should be copied out. + +int resource_manager::do_begin_resource(const char *ptr, int, FILE *, + FILE *) +{ + resource *r = read_resource_arg(&ptr); + if (r) + r->flags |= resource::SUPPLIED; + return 1; +} + +int resource_manager::do_include_resource(const char *ptr, int rank, FILE *, + FILE *outfp) +{ + resource *r = read_resource_arg(&ptr); + if (r) { + if (r->type == RESOURCE_FONT) { + if (rank >= 0) + supply_resource(r, rank + 1, outfp); + else + r->flags |= resource::FONT_NEEDED; + } + else + supply_resource(r, rank, outfp); + } + return 0; +} + +int resource_manager::do_begin_document(const char *ptr, int, FILE *, + FILE *) +{ + resource *r = read_file_arg(&ptr); + if (r) + r->flags |= resource::SUPPLIED; + return 1; +} + +int resource_manager::do_include_document(const char *ptr, int rank, FILE *, + FILE *outfp) +{ + resource *r = read_file_arg(&ptr); + if (r) + supply_resource(r, rank, outfp, 1); + return 0; +} + +int resource_manager::do_begin_procset(const char *ptr, int, FILE *, + FILE *outfp) +{ + resource *r = read_procset_arg(&ptr); + if (r) { + r->flags |= resource::SUPPLIED; + if (outfp) { + fputs("%%BeginResource: ", outfp); + r->print_type_and_name(outfp); + putc('\n', outfp); + } + } + return 0; +} + +int resource_manager::do_include_procset(const char *ptr, int rank, FILE *, + FILE *outfp) +{ + resource *r = read_procset_arg(&ptr); + if (r) + supply_resource(r, rank, outfp); + return 0; +} + +int resource_manager::do_begin_file(const char *ptr, int, FILE *, + FILE *outfp) +{ + resource *r = read_file_arg(&ptr); + if (r) { + r->flags |= resource::SUPPLIED; + if (outfp) { + fputs("%%BeginResource: ", outfp); + r->print_type_and_name(outfp); + putc('\n', outfp); + } + } + return 0; +} + +int resource_manager::do_include_file(const char *ptr, int rank, FILE *, + FILE *outfp) +{ + resource *r = read_file_arg(&ptr); + if (r) + supply_resource(r, rank, outfp); + return 0; +} + +int resource_manager::do_begin_font(const char *ptr, int, FILE *, + FILE *outfp) +{ + resource *r = read_font_arg(&ptr); + if (r) { + r->flags |= resource::SUPPLIED; + if (outfp) { + fputs("%%BeginResource: ", outfp); + r->print_type_and_name(outfp); + putc('\n', outfp); + } + } + return 0; +} + +int resource_manager::do_include_font(const char *ptr, int rank, FILE *, + FILE *outfp) +{ + resource *r = read_font_arg(&ptr); + if (r) { + if (rank >= 0) + supply_resource(r, rank + 1, outfp); + else + r->flags |= resource::FONT_NEEDED; + } + return 0; +} + +int resource_manager::change_to_end_resource(const char *, int, FILE *, + FILE *outfp) +{ + if (outfp) + fputs("%%EndResource\n", outfp); + return 0; +} + +int resource_manager::do_begin_preview(const char *, int, FILE *fp, FILE *) +{ + char buf[PS_LINE_MAX + 2]; + do { + if (!ps_get_line(buf, fp)) { + error("end of file in preview section"); + break; + } + } while (!matches_comment(buf, "EndPreview")); + return 0; +} + +int read_one_of(const char **ptr, const char **s, int n) +{ + while (white_space(**ptr)) + *ptr += 1; + if (**ptr == '\0') + return -1; + const char *start = *ptr; + do { + ++ptr; + } while (**ptr != '\0' && !white_space(**ptr)); + for (int i = 0; i < n; i++) + if (strlen(s[i]) == *ptr - start + && memcmp(s[i], start, *ptr - start) == 0) + return i; + return -1; +} + +int resource_manager::do_begin_data(const char *ptr, int, FILE *fp, + FILE *outfp) +{ + while (white_space(*ptr)) + ptr++; + const char *start = ptr; + unsigned numberof; + if (!read_uint_arg(&ptr, &numberof)) + return 0; + static const char *types[] = { "Binary", "Hex", "ASCII" }; + const int Binary = 0; + int type = 0; + static const char *units[] = { "Bytes", "Lines" }; + const int Bytes = 0; + int unit = Bytes; + while (white_space(*ptr)) + ptr++; + if (*ptr != '\0') { + type = read_one_of(&ptr, types, 3); + if (type < 0) { + error("bad data type"); + return 0; + } + while (white_space(*ptr)) + ptr++; + if (*ptr != '\0') { + unit = read_one_of(&ptr, units, 2); + if (unit < 0) { + error("expected `Bytes' or `Lines'"); + return 0; + } + } + } + if (type != Binary) + return 1; + if (outfp) { + fputs("%%BeginData: ", outfp); + fputs(start, outfp); + } + if (numberof > 0) { + unsigned bytecount = 0; + unsigned linecount = 0; + do { + int c = getc(fp); + if (c == EOF) { + error("end of file within data section"); + return 0; + } + if (outfp) + putc(c, outfp); + bytecount++; + if (c == '\r') { + int cc = getc(fp); + if (cc != '\n') { + linecount++; + current_lineno++; + } + if (cc != EOF) + ungetc(c, fp); + } + else if (c == '\n') { + linecount++; + current_lineno++; + } + } while ((unit == Bytes ? bytecount : linecount) < numberof); + } + char buf[PS_LINE_MAX + 2]; + if (!ps_get_line(buf, fp)) { + error("missing %%%%EndData line"); + return 0; + } + if (!matches_comment(buf, "EndData")) + error("bad %%%%EndData line"); + if (outfp) + fputs(buf, outfp); + return 0; +} + +int resource_manager::do_begin_binary(const char *ptr, int, FILE *fp, + FILE *outfp) +{ + if (!outfp) + return 0; + unsigned count; + if (!read_uint_arg(&ptr, &count)) + return 0; + if (outfp) + fprintf(outfp, "%%%%BeginData: %u Binary Bytes\n", count); + while (count != 0) { + int c = getc(fp); + if (c == EOF) { + error("end of file within binary section"); + return 0; + } + if (outfp) + putc(c, outfp); + --count; + if (c == '\r') { + int cc = getc(fp); + if (cc != '\n') + current_lineno++; + if (cc != EOF) + ungetc(c, fp); + } + else if (c == '\n') + current_lineno++; + } + char buf[PS_LINE_MAX + 2]; + if (!ps_get_line(buf, fp)) { + error("missing %%%%EndBinary line"); + return 0; + } + if (!matches_comment(buf, "EndBinary")) { + error("bad %%%%EndBinary line"); + if (outfp) + fputs(buf, outfp); + } + else if (outfp) + fputs("%%EndData\n", outfp); + return 0; +} + +static unsigned parse_extensions(const char *ptr) +{ + unsigned flags = 0; + for (;;) { + while (white_space(*ptr)) + ptr++; + if (*ptr == '\0') + break; + const char *name = ptr; + do { + ++ptr; + } while (*ptr != '\0' && !white_space(*ptr)); + int i; + for (i = 0; i < NEXTENSIONS; i++) + if (strlen(extension_table[i]) == ptr - name + && memcmp(extension_table[i], name, ptr - name) == 0) { + flags |= (1 << i); + break; + } + if (i >= NEXTENSIONS) { + string s(name, ptr - name); + s += '\0'; + error("unknown extension `%1'", s.contents()); + } + } + return flags; +} + +// XXX if it has not been surrounded with {Begin,End}Document need to strip +// out Page: Trailer {Begin,End}Prolog {Begin,End}Setup sections. + +// XXX Perhaps the decision whether to use BeginDocument or +// BeginResource: file should be postponed till we have seen +// the first line of the file. + +void resource_manager::process_file(int rank, FILE *fp, const char *filename, + FILE *outfp) +{ + // If none of these comments appear in the header section, and we are + // just analyzing the file (ie outfp is 0), then we can return immediately. + static const char *header_comment_table[] = { + "DocumentNeededResources:", + "DocumentSuppliedResources:", + "DocumentNeededFonts:", + "DocumentSuppliedFonts:", + "DocumentNeededProcSets:", + "DocumentSuppliedProcSets:", + "DocumentNeededFiles:", + "DocumentSuppliedFiles:", + }; + + const int NHEADER_COMMENTS = (sizeof(header_comment_table) + / sizeof(header_comment_table[0])); + struct comment_info { + const char *name; + int (resource_manager::*proc)(const char *, int, FILE *, FILE *); + }; + + static comment_info comment_table[] = { + { "BeginResource:", &resource_manager::do_begin_resource }, + { "IncludeResource:", &resource_manager::do_include_resource }, + { "BeginDocument:", &resource_manager::do_begin_document }, + { "IncludeDocument:", &resource_manager::do_include_document }, + { "BeginProcSet:", &resource_manager::do_begin_procset }, + { "IncludeProcSet:", &resource_manager::do_include_procset }, + { "BeginFont:", &resource_manager::do_begin_font }, + { "IncludeFont:", &resource_manager::do_include_font }, + { "BeginFile:", &resource_manager::do_begin_file }, + { "IncludeFile:", &resource_manager::do_include_file }, + { "EndProcSet", &resource_manager::change_to_end_resource }, + { "EndFont", &resource_manager::change_to_end_resource }, + { "EndFile", &resource_manager::change_to_end_resource }, + { "BeginPreview:", &resource_manager::do_begin_preview }, + { "BeginData:", &resource_manager::do_begin_data }, + { "BeginBinary:", &resource_manager::do_begin_binary }, + }; + + const int NCOMMENTS = sizeof(comment_table)/sizeof(comment_table[0]); + char buf[PS_LINE_MAX + 2]; + int saved_lineno = current_lineno; + const char *saved_filename = current_filename; + current_filename = filename; + current_lineno = 0; + if (!ps_get_line(buf, fp)) { + current_filename = saved_filename; + current_lineno = saved_lineno; + return; + } + if (strlen(buf) < sizeof(PS_MAGIC) - 1 + || memcmp(buf, PS_MAGIC, sizeof(PS_MAGIC) - 1) != 0) { + if (outfp) { + do { + if (!(broken_flags & STRIP_PERCENT_BANG) + || buf[0] != '%' || buf[1] != '!') + fputs(buf, outfp); + } while (ps_get_line(buf, fp)); + } + } + else { + if (!(broken_flags & STRIP_PERCENT_BANG) && outfp) + fputs(buf, outfp); + int in_header = 1; + int interesting = 0; + int had_extensions_comment = 0; + int had_language_level_comment = 0; + for (;;) { + if (!ps_get_line(buf, fp)) + break; + int copy_this_line = 1; + if (buf[0] == '%') { + if (buf[1] == '%') { + const char *ptr; + int i; + for (i = 0; i < NCOMMENTS; i++) + if (ptr = matches_comment(buf, comment_table[i].name)) { + copy_this_line + = (this->*(comment_table[i].proc))(ptr, rank, fp, outfp); + break; + } + if (i >= NCOMMENTS && in_header) { + if (ptr = matches_comment(buf, "EndComments")) + in_header = 0; + else if (!had_extensions_comment + && (ptr = matches_comment(buf, "Extensions:"))) { + extensions |= parse_extensions(ptr); + // XXX handle possibility that next line is %%+ + had_extensions_comment = 1; + } + else if (!had_language_level_comment + && (ptr = matches_comment(buf, "LanguageLevel:"))) { + unsigned ll; + if (read_uint_arg(&ptr, &ll) && ll > language_level) + language_level = ll; + had_language_level_comment = 1; + } + else { + for (int i = 0; i < NHEADER_COMMENTS; i++) + if (matches_comment(buf, header_comment_table[i])) { + interesting = 1; + break; + } + } + } + if ((broken_flags & STRIP_STRUCTURE_COMMENTS) + && (matches_comment(buf, "EndProlog") + || matches_comment(buf, "Page:") + || matches_comment(buf, "Trailer"))) + copy_this_line = 0; + } + else if (buf[1] == '!') { + if (broken_flags & STRIP_PERCENT_BANG) + copy_this_line = 0; + } + } + else + in_header = 0; + if (!outfp && !in_header && !interesting) + break; + if (copy_this_line && outfp) + fputs(buf, outfp); + } + } + current_filename = saved_filename; + current_lineno = saved_lineno; +} + +void resource_manager::read_download_file() +{ + char *path = 0; + FILE *fp = font::open_file("download", &path); + if (!fp) + fatal("can't find `download'"); + char buf[512]; + int lineno = 0; + while (fgets(buf, sizeof(buf), fp)) { + lineno++; + char *p = strtok(buf, " \t\r\n"); + if (p == 0 || *p == '#') + continue; + char *q = strtok(0, " \t\r\n"); + if (!q) + fatal_with_file_and_line(path, lineno, "missing filename"); + lookup_font(p)->filename = strsave(q); + } + a_delete path; + fclose(fp); +} + +// XXX Can we share some code with ps_output::put_string()? + +static void print_ps_string(const string &s, FILE *outfp) +{ + int len = s.length(); + const char *str = s.contents(); + int funny = 0; + if (str[0] == '(') + funny = 1; + else { + for (int i = 0; i < len; i++) + if (str[i] <= 040 || str[i] > 0176) { + funny = 1; + break; + } + } + if (!funny) { + put_string(s, outfp); + return; + } + int level = 0; + int i; + for (i = 0; i < len; i++) + if (str[i] == '(') + level++; + else if (str[i] == ')' && --level < 0) + break; + putc('(', outfp); + for (i = 0; i < len; i++) + switch (str[i]) { + case '(': + case ')': + if (level != 0) + putc('\\', outfp); + putc(str[i], outfp); + break; + case '\\': + fputs("\\\\", outfp); + break; + case '\n': + fputs("\\n", outfp); + break; + case '\r': + fputs("\\r", outfp); + break; + case '\t': + fputs("\\t", outfp); + break; + case '\b': + fputs("\\b", outfp); + break; + case '\f': + fputs("\\f", outfp); + break; + default: + if (str[i] < 040 || str[i] > 0176) + fprintf(outfp, "\\%03o", str[i] & 0377); + else + putc(str[i], outfp); + break; + } + putc(')', outfp); +} + +void resource_manager::print_extensions_comment(FILE *outfp) +{ + if (extensions) { + fputs("%%Extensions:", outfp); + for (int i = 0; i < NEXTENSIONS; i++) + if (extensions & (1 << i)) { + putc(' ', outfp); + fputs(extension_table[i], outfp); + } + putc('\n', outfp); + } +} + +void resource_manager::print_language_level_comment(FILE *outfp) +{ + if (language_level) + fprintf(outfp, "%%%%LanguageLevel: %u\n", language_level); +} + |