aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/postscript
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2004-05-15 23:55:53 +0000
committerrsc <devnull@localhost>2004-05-15 23:55:53 +0000
commit61f5c35c9465f0702739b41249a664d409f0482c (patch)
tree17546b7dcc76abd9ee74dc7543cc77121acfe39a /src/cmd/postscript
parent173302913ebce353eadcbb12d71c3759cbe79e34 (diff)
downloadplan9port-61f5c35c9465f0702739b41249a664d409f0482c.tar.gz
plan9port-61f5c35c9465f0702739b41249a664d409f0482c.tar.bz2
plan9port-61f5c35c9465f0702739b41249a664d409f0482c.zip
more files
Diffstat (limited to 'src/cmd/postscript')
-rw-r--r--src/cmd/postscript/common/bbox.c257
-rw-r--r--src/cmd/postscript/common/comments.h127
-rw-r--r--src/cmd/postscript/common/common.c264
-rw-r--r--src/cmd/postscript/common/common.h43
-rw-r--r--src/cmd/postscript/common/ext.h40
-rw-r--r--src/cmd/postscript/common/gen.h65
-rw-r--r--src/cmd/postscript/common/getopt.c56
-rw-r--r--src/cmd/postscript/common/glob.c29
-rw-r--r--src/cmd/postscript/common/misc.c230
-rw-r--r--src/cmd/postscript/common/mkfile23
-rw-r--r--src/cmd/postscript/common/path.h32
-rw-r--r--src/cmd/postscript/common/request.c119
-rw-r--r--src/cmd/postscript/common/request.h22
-rw-r--r--src/cmd/postscript/common/rune.c142
-rw-r--r--src/cmd/postscript/common/rune.h19
-rw-r--r--src/cmd/postscript/common/tempnam.c27
-rw-r--r--src/cmd/postscript/download/README11
-rw-r--r--src/cmd/postscript/download/download.c545
-rw-r--r--src/cmd/postscript/download/download.h14
-rw-r--r--src/cmd/postscript/download/mkfile25
-rw-r--r--src/cmd/postscript/mkfile25
-rw-r--r--src/cmd/postscript/tr2post/Bgetfield.c156
-rw-r--r--src/cmd/postscript/tr2post/chartab.c458
-rw-r--r--src/cmd/postscript/tr2post/conv.c100
-rw-r--r--src/cmd/postscript/tr2post/devcntl.c178
-rw-r--r--src/cmd/postscript/tr2post/draw.c342
-rw-r--r--src/cmd/postscript/tr2post/mkfile36
-rw-r--r--src/cmd/postscript/tr2post/pictures.c295
-rw-r--r--src/cmd/postscript/tr2post/ps_include.c191
-rw-r--r--src/cmd/postscript/tr2post/ps_include.h66
-rw-r--r--src/cmd/postscript/tr2post/readDESC.c139
-rw-r--r--src/cmd/postscript/tr2post/shell.lib1238
-rw-r--r--src/cmd/postscript/tr2post/tr2post.c218
-rw-r--r--src/cmd/postscript/tr2post/tr2post.h103
-rw-r--r--src/cmd/postscript/tr2post/utfmap47
-rw-r--r--src/cmd/postscript/tr2post/utils.c264
36 files changed, 5946 insertions, 0 deletions
diff --git a/src/cmd/postscript/common/bbox.c b/src/cmd/postscript/common/bbox.c
new file mode 100644
index 00000000..7e1f14a5
--- /dev/null
+++ b/src/cmd/postscript/common/bbox.c
@@ -0,0 +1,257 @@
+/*
+ *
+ * Boundingbox code for PostScript translators. The boundingbox for each page
+ * is accumulated in bbox - the one for the whole document goes in docbbox. A
+ * call to writebbox() puts out an appropriate comment, updates docbbox, and
+ * resets bbox for the next page. The assumption made at the end of writebbox()
+ * is that we're really printing the current page only if output is now going
+ * to stdout - a valid assumption for all supplied translators. Needs the math
+ * library.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <math.h>
+
+#include "comments.h" /* PostScript file structuring comments */
+#include "gen.h" /* a few general purpose definitions */
+#include "ext.h" /* external variable declarations */
+
+typedef struct bbox {
+ int set;
+ double llx, lly;
+ double urx, ury;
+} Bbox;
+
+Bbox bbox = {FALSE, 0.0, 0.0, 0.0, 0.0};
+Bbox docbbox = {FALSE, 0.0, 0.0, 0.0, 0.0};
+
+double ctm[6] = {1.0, 0.0, 0.0, 1.0, 0.0, 0.0};
+double matrix1[6], matrix2[6];
+
+/*****************************************************************************/
+
+cover(x, y)
+
+ double x, y;
+
+{
+
+/*
+ *
+ * Adds point (x, y) to bbox. Coordinates are in user space - the transformation
+ * to default coordinates happens in writebbox().
+ *
+ */
+
+ if ( bbox.set == FALSE ) {
+ bbox.llx = bbox.urx = x;
+ bbox.lly = bbox.ury = y;
+ bbox.set = TRUE;
+ } else {
+ if ( x < bbox.llx )
+ bbox.llx = x;
+ if ( y < bbox.lly )
+ bbox.lly = y;
+ if ( x > bbox.urx )
+ bbox.urx = x;
+ if ( y > bbox.ury )
+ bbox.ury = y;
+ } /* End else */
+
+} /* End of cover */
+
+/*****************************************************************************/
+
+writebbox(fp, keyword, slop)
+
+ FILE *fp; /* the comment is written here */
+ char *keyword; /* the boundingbox comment string */
+ int slop; /* expand (or contract?) the box a bit */
+
+{
+
+ Bbox ubbox; /* user space bounding box */
+ double x, y;
+
+/*
+ *
+ * Transforms the numbers in the bbox[] using ctm[], adjusts the corners a bit
+ * (depending on slop) and then writes comment. If *keyword is BoundingBox use
+ * whatever's been saved in docbbox, otherwise assume the comment is just for
+ * the current page.
+ *
+ */
+
+ if ( strcmp(keyword, BOUNDINGBOX) == 0 )
+ bbox = docbbox;
+
+ if ( bbox.set == TRUE ) {
+ ubbox = bbox;
+ bbox.set = FALSE; /* so cover() works properly */
+ x = ctm[0] * ubbox.llx + ctm[2] * ubbox.lly + ctm[4];
+ y = ctm[1] * ubbox.llx + ctm[3] * ubbox.lly + ctm[5];
+ cover(x, y);
+ x = ctm[0] * ubbox.llx + ctm[2] * ubbox.ury + ctm[4];
+ y = ctm[1] * ubbox.llx + ctm[3] * ubbox.ury + ctm[5];
+ cover(x, y);
+ x = ctm[0] * ubbox.urx + ctm[2] * ubbox.ury + ctm[4];
+ y = ctm[1] * ubbox.urx + ctm[3] * ubbox.ury + ctm[5];
+ cover(x, y);
+ x = ctm[0] * ubbox.urx + ctm[2] * ubbox.lly + ctm[4];
+ y = ctm[1] * ubbox.urx + ctm[3] * ubbox.lly + ctm[5];
+ cover(x, y);
+ bbox.llx -= slop + 0.5;
+ bbox.lly -= slop + 0.5;
+ bbox.urx += slop + 0.5;
+ bbox.ury += slop + 0.5;
+ fprintf(fp, "%s %d %d %d %d\n", keyword, (int)bbox.llx, (int)bbox.lly,(int)bbox.urx, (int)bbox.ury);
+ bbox = ubbox;
+ } /* End if */
+
+ resetbbox((fp == stdout) ? TRUE : FALSE);
+
+} /* End of writebbox */
+
+/*****************************************************************************/
+
+resetbbox(output)
+
+ int output;
+
+{
+
+/*
+ *
+ * Adds bbox to docbbox and resets bbox for the next page. Only update docbbox
+ * if we really did output on the last page.
+ *
+ */
+
+ if ( docbbox.set == TRUE ) {
+ cover(docbbox.llx, docbbox.lly);
+ cover(docbbox.urx, docbbox.ury);
+ } /* End if */
+
+ if ( output == TRUE ) {
+ docbbox = bbox;
+ docbbox.set = TRUE;
+ } /* End if */
+
+ bbox.set = FALSE;
+
+} /* End of resetbbox */
+
+/*****************************************************************************/
+
+scale(sx, sy)
+
+ double sx, sy;
+
+{
+
+/*
+ *
+ * Scales the default matrix.
+ *
+ */
+
+ matrix1[0] = sx;
+ matrix1[1] = 0;
+ matrix1[2] = 0;
+ matrix1[3] = sy;
+ matrix1[4] = 0;
+ matrix1[5] = 0;
+
+ concat(matrix1);
+
+} /* End of scale */
+
+/*****************************************************************************/
+
+translate(tx, ty)
+
+ double tx, ty;
+
+{
+
+/*
+ *
+ * Translates the default matrix.
+ *
+ */
+
+ matrix1[0] = 1.0;
+ matrix1[1] = 0.0;
+ matrix1[2] = 0.0;
+ matrix1[3] = 1.0;
+ matrix1[4] = tx;
+ matrix1[5] = ty;
+
+ concat(matrix1);
+
+} /* End of translate */
+
+/*****************************************************************************/
+
+rotate(angle)
+
+ double angle;
+
+{
+
+/*
+ *
+ * Rotates by angle degrees.
+ *
+ */
+
+ angle *= 3.1416 / 180;
+
+ matrix1[0] = matrix1[3] = cos(angle);
+ matrix1[1] = sin(angle);
+ matrix1[2] = -matrix1[1];
+ matrix1[4] = 0.0;
+ matrix1[5] = 0.0;
+
+ concat(matrix1);
+
+} /* End of rotate */
+
+/*****************************************************************************/
+
+concat(m1)
+
+ double m1[];
+
+{
+
+ double m2[6];
+
+/*
+ *
+ * Replaces the ctm[] by the result of the matrix multiplication m1[] x ctm[].
+ *
+ */
+
+ m2[0] = ctm[0];
+ m2[1] = ctm[1];
+ m2[2] = ctm[2];
+ m2[3] = ctm[3];
+ m2[4] = ctm[4];
+ m2[5] = ctm[5];
+
+ ctm[0] = m1[0] * m2[0] + m1[1] * m2[2];
+ ctm[1] = m1[0] * m2[1] + m1[1] * m2[3];
+ ctm[2] = m1[2] * m2[0] + m1[3] * m2[2];
+ ctm[3] = m1[2] * m2[1] + m1[3] * m2[3];
+ ctm[4] = m1[4] * m2[0] + m1[5] * m2[2] + m2[4];
+ ctm[5] = m1[4] * m2[1] + m1[5] * m2[3] + m2[5];
+
+} /* End of concat */
+
+/*****************************************************************************/
+
diff --git a/src/cmd/postscript/common/comments.h b/src/cmd/postscript/common/comments.h
new file mode 100644
index 00000000..6b409cad
--- /dev/null
+++ b/src/cmd/postscript/common/comments.h
@@ -0,0 +1,127 @@
+/*
+ *
+ * Currently defined file structuring comments from Adobe - plus a few others.
+ * Ones that end with a colon expect arguments, while those ending with a newline
+ * stand on their own. Truly overkill on Adobe's part and mine for including them
+ * all!
+ *
+ * All PostScript files should begin with a header that starts with one of the
+ * following comments.
+ *
+ */
+
+#define NONCONFORMING "%!PS\n"
+#define MINCONFORMING "%!PS-Adobe-\n"
+#define OLDCONFORMING "%!PS-Adobe-1.0\n"
+
+#define CONFORMING "%!PS-Adobe-2.0\n"
+#define CONFORMINGEPS "%!PS-Adobe-2.0 EPS\n"
+#define CONFORMINGQUERY "%!PS-Adobe-2.0 Query\n"
+#define CONFORMINGEXITSERVER "%!PS-Adobe-2.0 ExitServer\n"
+
+/*
+ *
+ * Header comments - immediately follow the appropriate document classification
+ * comment.
+ *
+ */
+
+#define TITLE "%%Title:"
+#define CREATOR "%%Creator:"
+#define CREATIONDATE "%%CreationDate:"
+#define FOR "%%For:"
+#define ROUTING "%%Routing:"
+#define BOUNDINGBOX "%%BoundingBox:"
+#define PAGES "%%Pages:"
+#define REQUIREMENTS "%%Requirements:"
+
+#define DOCUMENTFONTS "%%DocumentFonts:"
+#define DOCUMENTNEEDEDFONTS "%%DocumentNeededFonts:"
+#define DOCUMENTSUPPLIEDFONTS "%%DocumentSuppliedFonts:"
+#define DOCUMENTNEEDEDPROCSETS "%%DocumentNeededProcSets:"
+#define DOCUMENTSUPPLIEDPROCSETS "%%DocumentSuppliedProcSets:"
+#define DOCUMENTNEEDEDFILES "%%DocumentNeededFiles:"
+#define DOCUMENTSUPPLIEDFILES "%%DocumentSuppliedFiles:"
+#define DOCUMENTPAPERSIZES "%%DocumentPaperSizes:"
+#define DOCUMENTPAPERFORMS "%%DocumentPaperForms:"
+#define DOCUMENTPAPERCOLORS "%%DocumentPaperColors:"
+#define DOCUMENTPAPERWEIGHTS "%%DocumentPaperWeights:"
+#define DOCUMENTPRINTERREQUIRED "%%DocumentPrinterREquired:"
+#define ENDCOMMENTS "%%EndComments\n"
+#define ENDPROLOG "%%EndProlog\n"
+
+/*
+ *
+ * Body comments - can appear anywhere in a document.
+ *
+ */
+
+#define BEGINSETUP "%%BeginSetup\n"
+#define ENDSETUP "%%EndSetup\n"
+#define BEGINDOCUMENT "%%BeginDocument:"
+#define ENDDOCUMENT "%%EndDocument\n"
+#define BEGINFILE "%%BeginFile:"
+#define ENDFILE "%%EndFile\n"
+#define BEGINPROCSET "%%BeginProcSet:"
+#define ENDPROCSET "%%EndProcSet\n"
+#define BEGINBINARY "%%BeginBinary:"
+#define ENDBINARY "%%EndBinary\n"
+#define BEGINPAPERSIZE "%%BeginePaperSize:"
+#define ENDPAPERSIZE "%%EndPaperSize\n"
+#define BEGINFEATURE "%%BeginFeature:"
+#define ENDFEATURE "%%EndFeature\n"
+#define BEGINEXITSERVER "%%BeginExitServer:"
+#define ENDEXITSERVER "%%EndExitServer\n"
+#define TRAILER "%%Trailer\n"
+
+/*
+ *
+ * Page level comments - usually will occur once per page.
+ *
+ */
+
+#define PAGE "%%Page:"
+#define PAGEFONTS "%%PageFonts:"
+#define PAGEFILES "%%PageFiles:"
+#define PAGEBOUNDINGBOX "%%PageBoundingBox:"
+#define BEGINPAGESETUP "%%BeginPageSetup\n"
+#define BEGINOBJECT "%%BeginObject:"
+#define ENDOBJECT "%%EndObject\n"
+
+/*
+ *
+ * Resource requirements - again can appear anywhere in a document.
+ *
+ */
+
+#define INCLUDEFONT "%%IncludeFont:"
+#define INCLUDEPROCSET "%%IncludeProcSet:"
+#define INCLUDEFILE "%%IncludeFile:"
+#define EXECUTEFILE "%%ExecuteFile:"
+#define CHANGEFONT "%%ChangeFont:"
+#define PAPERFORM "%%PaparForm:"
+#define PAPERCOLOR "%%PaperColor:"
+#define PAPERWEIGHT "%%PaperWeight:"
+#define PAPERSIZE "%%PaperSize:"
+#define FEATURE "%%Feature:"
+#define ENDOFFILE "%%EOF\n"
+
+#define CONTINUECOMMENT "%%+"
+#define ATEND "(atend)"
+
+/*
+ *
+ * Some non-standard document comments. Global definitions are occasionally used
+ * in dpost and are marked by BEGINGLOBAL and ENDGLOBAL. The resulting document
+ * violates page independence, but can easily be converted to a conforming file
+ * using a utililty program.
+ *
+ */
+
+#define BEGINSCRIPT "%%BeginScript\n"
+#define BEGINGLOBAL "%%BeginGlobal\n"
+#define ENDGLOBAL "%%EndGlobal\n"
+#define ENDPAGE "%%EndPage:"
+#define FORMSPERPAGE "%%FormsPerPage:"
+#define VERSION "%%Version:"
+
diff --git a/src/cmd/postscript/common/common.c b/src/cmd/postscript/common/common.c
new file mode 100644
index 00000000..945ef6a0
--- /dev/null
+++ b/src/cmd/postscript/common/common.c
@@ -0,0 +1,264 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+#include "common.h"
+#include "comments.h"
+#include "path.h"
+
+struct strtab charcode[FONTSIZE] = {
+ {4, "\\000"}, {4, "\\001"}, {4, "\\002"}, {4, "\\003"},
+ {4, "\\004"}, {4, "\\005"}, {4, "\\006"}, {4, "\\007"},
+ {4, "\\010"}, {4, "\\011"}, {4, "\\012"}, {4, "\\013"},
+ {4, "\\014"}, {4, "\\015"}, {4, "\\016"}, {4, "\\017"},
+ {4, "\\020"}, {4, "\\021"}, {4, "\\022"}, {4, "\\023"},
+ {4, "\\024"}, {4, "\\025"}, {4, "\\026"}, {4, "\\027"},
+ {4, "\\030"}, {4, "\\031"}, {4, "\\032"}, {4, "\\033"},
+ {4, "\\034"}, {4, "\\035"}, {4, "\\036"}, {4, "\\037"},
+ {1, " "}, {1, "!"}, {1, "\""}, {1, "#"},
+ {1, "$"}, {1, "%"}, {1, "&"}, {1, "'"},
+ {2, "\\("}, {2, "\\)"}, {1, "*"}, {1, "+"},
+ {1, ","}, {1, "-"}, {1, "."}, {1, "/"},
+ {1, "0"}, {1, "1"}, {1, "2"}, {1, "3"},
+ {1, "4"}, {1, "5"}, {1, "6"}, {1, "7"},
+ {1, "8"}, {1, "9"}, {1, ":"}, {1, ";"},
+ {1, "<"}, {1, "="}, {1, ">"}, {1, "?"},
+ {1, "@"}, {1, "A"}, {1, "B"}, {1, "C"},
+ {1, "D"}, {1, "E"}, {1, "F"}, {1, "G"},
+ {1, "H"}, {1, "I"}, {1, "J"}, {1, "K"},
+ {1, "L"}, {1, "M"}, {1, "N"}, {1, "O"},
+ {1, "P"}, {1, "Q"}, {1, "R"}, {1, "S"},
+ {1, "T"}, {1, "U"}, {1, "V"}, {1, "W"},
+ {1, "X"}, {1, "Y"}, {1, "Z"}, {1, "["},
+ {2, "\\\\"}, {1, "]"}, {1, "^"}, {1, "_"},
+ {1, "`"}, {1, "a"}, {1, "b"}, {1, "c"},
+ {1, "d"}, {1, "e"}, {1, "f"}, {1, "g"},
+ {1, "h"}, {1, "i"}, {1, "j"}, {1, "k"},
+ {1, "l"}, {1, "m"}, {1, "n"}, {1, "o"},
+ {1, "p"}, {1, "q"}, {1, "r"}, {1, "s"},
+ {1, "t"}, {1, "u"}, {1, "v"}, {1, "w"},
+ {1, "x"}, {1, "y"}, {1, "z"}, {1, "{"},
+ {1, "|"}, {1, "}"}, {1, "~"}, {4, "\\177"},
+ {4, "\\200"}, {4, "\\201"}, {4, "\\202"}, {4, "\\203"},
+ {4, "\\204"}, {4, "\\205"}, {4, "\\206"}, {4, "\\207"},
+ {4, "\\210"}, {4, "\\211"}, {4, "\\212"}, {4, "\\213"},
+ {4, "\\214"}, {4, "\\215"}, {4, "\\216"}, {4, "\\217"},
+ {4, "\\220"}, {4, "\\221"}, {4, "\\222"}, {4, "\\223"},
+ {4, "\\224"}, {4, "\\225"}, {4, "\\226"}, {4, "\\227"},
+ {4, "\\230"}, {4, "\\231"}, {4, "\\232"}, {4, "\\233"},
+ {4, "\\234"}, {4, "\\235"}, {4, "\\236"}, {4, "\\237"},
+ {4, "\\240"}, {4, "\\241"}, {4, "\\242"}, {4, "\\243"},
+ {4, "\\244"}, {4, "\\245"}, {4, "\\246"}, {4, "\\247"},
+ {4, "\\250"}, {4, "\\251"}, {4, "\\252"}, {4, "\\253"},
+ {4, "\\254"}, {4, "\\255"}, {4, "\\256"}, {4, "\\257"},
+ {4, "\\260"}, {4, "\\261"}, {4, "\\262"}, {4, "\\263"},
+ {4, "\\264"}, {4, "\\265"}, {4, "\\266"}, {4, "\\267"},
+ {4, "\\270"}, {4, "\\271"}, {4, "\\272"}, {4, "\\273"},
+ {4, "\\274"}, {4, "\\275"}, {4, "\\276"}, {4, "\\277"},
+ {4, "\\300"}, {4, "\\301"}, {4, "\\302"}, {4, "\\303"},
+ {4, "\\304"}, {4, "\\305"}, {4, "\\306"}, {4, "\\307"},
+ {4, "\\310"}, {4, "\\311"}, {4, "\\312"}, {4, "\\313"},
+ {4, "\\314"}, {4, "\\315"}, {4, "\\316"}, {4, "\\317"},
+ {4, "\\320"}, {4, "\\321"}, {4, "\\322"}, {4, "\\323"},
+ {4, "\\324"}, {4, "\\325"}, {4, "\\326"}, {4, "\\327"},
+ {4, "\\330"}, {4, "\\331"}, {4, "\\332"}, {4, "\\333"},
+ {4, "\\334"}, {4, "\\335"}, {4, "\\336"}, {4, "\\337"},
+ {4, "\\340"}, {4, "\\341"}, {4, "\\342"}, {4, "\\343"},
+ {4, "\\344"}, {4, "\\345"}, {4, "\\346"}, {4, "\\347"},
+ {4, "\\350"}, {4, "\\351"}, {4, "\\352"}, {4, "\\353"},
+ {4, "\\354"}, {4, "\\355"}, {4, "\\356"}, {4, "\\357"},
+ {4, "\\360"}, {4, "\\361"}, {4, "\\362"}, {4, "\\363"},
+ {4, "\\364"}, {4, "\\365"}, {4, "\\366"}, {4, "\\367"},
+ {4, "\\370"}, {4, "\\371"}, {4, "\\372"}, {4, "\\373"},
+ {4, "\\374"}, {4, "\\375"}, {4, "\\376"}, {4, "\\377"}
+};
+
+static BOOLEAN in_string = FALSE;
+int char_no = 0;
+int line_no = 0;
+int page_no = 0; /* page number in a document */
+int pages_printed = 0;
+static int pplistmaxsize=0;
+
+static unsigned char *pplist=0; /* bitmap list for storing pages to print */
+
+void
+pagelist(char *list) {
+ char c;
+ int n, m;
+ int state, start;
+
+ if (list == 0) return;
+ state = 1;
+ start = 0;
+ while ((c=*list) != '\0') {
+ n = 0;
+ while (isdigit(c)) {
+ n = n * 10 + c - '0';
+ c = *++list;
+ }
+ switch (state) {
+ case 1:
+ start = n;
+ case 2:
+ if (n/8+1 > pplistmaxsize) {
+ pplistmaxsize = n/8+1;
+ pplist = galloc(pplist, n/8+1, "page list");
+ }
+ for (m=start; m<=n; m++)
+ pplist[m/8] |= 1<<(m%8);
+ break;
+ }
+ switch (c) {
+ case '-':
+ state = 2;
+ list++;
+ break;
+ case ',':
+ state = 1;
+ list++;
+ break;
+ case '\0':
+ break;
+ }
+ }
+}
+
+BOOLEAN
+pageon(void) {
+ extern BOOLEAN debug;
+ static BOOLEAN privdebug = FALSE;
+
+ if (pplist == 0 && page_no != 0) {
+ if (privdebug && !debug) {
+ privdebug = FALSE;
+ debug = TRUE;
+ }
+ return(TRUE); /* no page list, print all pages */
+ }
+ if (page_no/8 < pplistmaxsize && (pplist[page_no/8] & 1<<(page_no%8))) {
+ if (privdebug && !debug) {
+ privdebug = FALSE;
+ debug = TRUE;
+ }
+ return(TRUE);
+ } else {
+ if (!privdebug && debug) {
+ privdebug = TRUE;
+ debug = FALSE;
+ }
+ return(FALSE);
+ }
+}
+
+static int stringhpos, stringvpos;
+
+void
+startstring(void) {
+ if (!in_string) {
+ stringhpos = hpos;
+ stringvpos = vpos;
+ if (pageon()) Bprint(Bstdout, "(");
+ in_string = 1;
+ }
+}
+
+void
+endstring(void) {
+ if (in_string) {
+ if (pageon()) Bprint(Bstdout, ") %d %d w\n", stringhpos, stringvpos);
+ in_string = 0;
+ }
+}
+
+BOOLEAN
+isinstring(void) {
+ return(in_string);
+}
+
+void
+startpage(void) {
+ ++char_no;
+ ++line_no;
+ ++page_no;
+ if (pageon()) {
+ ++pages_printed;
+ Bprint(Bstdout, "%s %d %d\n", PAGE, page_no, pages_printed);
+ Bprint(Bstdout, "/saveobj save def\n");
+ Bprint(Bstdout, "mark\n");
+ Bprint(Bstdout, "%d pagesetup\n", pages_printed);
+ }
+}
+
+void
+endpage(void) {
+ endstring();
+ curpostfontid = -1;
+ line_no = 0;
+ char_no = 0;
+ if (pageon()) {
+ Bprint(Bstdout, "cleartomark\n");
+ Bprint(Bstdout, "showpage\n");
+ Bprint(Bstdout, "saveobj restore\n");
+ Bprint(Bstdout, "%s %d %d\n", ENDPAGE, page_no, pages_printed);
+ }
+}
+
+/* This was taken from postprint */
+
+int
+cat(char *filename) {
+ Biobuf *bfile;
+ Biobuf *Bfile;
+ int n;
+ static char buf[Bsize];
+
+ if ((bfile = Bopen(unsharp(filename), OREAD)) == 0) {
+ return(1);
+ }
+ Bfile = bfile;
+ while ((n=Bread(Bfile, buf, Bsize)) > 0) {
+ if (Bwrite(Bstdout, buf, n) != n)
+ break;
+ }
+ Bterm(Bfile);
+ if (n != 0) {
+ return(1);
+ }
+ return(0);
+}
+extern int debug;
+void *
+galloc(void *ptr, int size, char *perstr) {
+ void *x;
+
+ if ((x=realloc(ptr, size)) == 0) {
+ perror(perstr);
+ exits("malloc");
+ }
+ return(x);
+}
+
+static char *errorstrings[] = {
+ {""}, /* NONE */
+ {"WARNING"},
+ {"FATAL"}
+};
+
+char *programname;
+char *inputfilename = "<stdin>";
+int inputlineno;
+
+void
+error(int errtype, char *fmt, ...) {
+ va_list arg;
+
+ Bflush(Bstdout);
+ Bflush(Bstderr);
+ fprint(2, "%s: %s:%d :%s: ", programname, inputfilename, inputlineno, errorstrings[errtype]);
+ va_start(arg, fmt);
+ vfprint(2, fmt, arg);
+ va_end(arg);
+ if (errtype == FATAL)
+ exits("fatal error");
+}
diff --git a/src/cmd/postscript/common/common.h b/src/cmd/postscript/common/common.h
new file mode 100644
index 00000000..62eba08e
--- /dev/null
+++ b/src/cmd/postscript/common/common.h
@@ -0,0 +1,43 @@
+#define NONE 0
+#define WARNING 1
+#define FATAL 2
+
+#define RUNEGETGROUP(a) ((a>>8)&0xff)
+#define RUNEGETCHAR(a) (a&0xff)
+
+typedef int BOOLEAN;
+
+#define TRUE 1
+#define FALSE 0
+
+#define NUMOFONTS 0x100
+#define FONTSIZE 0x100
+
+extern char *programname;
+extern char *inputfilename;
+extern int inputlineno;
+
+extern int page_no;
+extern int pages_printed;
+extern int curpostfontid;
+extern int hpos, vpos;
+
+extern Biobuf *Bstdout, *Bstderr;
+
+struct strtab {
+ int size;
+ char *str;
+ int used;
+};
+
+extern struct strtab charcode[];
+BOOLEAN pageon(void);
+void startstring(void);
+void endstring(void);
+BOOLEAN isinstring(void);
+void startpage(void);
+void endpage(void);
+int cat(char *);
+int Bgetfield(Biobuf *, int, void *, int);
+void *galloc(void *, int, char *);
+void pagelist(char *);
diff --git a/src/cmd/postscript/common/ext.h b/src/cmd/postscript/common/ext.h
new file mode 100644
index 00000000..e260cee1
--- /dev/null
+++ b/src/cmd/postscript/common/ext.h
@@ -0,0 +1,40 @@
+/*
+ *
+ * External varibles - most are in glob.c.
+ *
+ */
+
+extern char **argv; /* global so everyone can use them */
+extern int argc;
+
+extern int x_stat; /* program exit status */
+extern int debug; /* debug flag */
+extern int ignore; /* what we do with FATAL errors */
+
+extern long lineno; /* line number */
+extern long position; /* byte position */
+extern char *prog_name; /* and program name - for errors */
+extern char *temp_file; /* temporary file - for some programs */
+extern char *fontencoding; /* text font encoding scheme */
+
+extern int dobbox; /* enable BoundingBox stuff if TRUE */
+extern double pageheight; /* only for BoundingBox calculations! */
+extern double pagewidth;
+
+extern int reading; /* input */
+extern int writing; /* and output encoding */
+
+extern char *optarg; /* for getopt() */
+extern int optind;
+
+extern void interrupt();
+//extern char *tempnam(char*,char*);
+/*
+ * extern char *malloc();
+ * extern char *calloc();
+ * extern char *strtok();
+ * extern long ftell();
+ * extern double atof();
+ * extern double sqrt();
+ * extern double atan2();
+ */
diff --git a/src/cmd/postscript/common/gen.h b/src/cmd/postscript/common/gen.h
new file mode 100644
index 00000000..ba8cbbac
--- /dev/null
+++ b/src/cmd/postscript/common/gen.h
@@ -0,0 +1,65 @@
+/*
+ *
+ * A few definitions that shouldn't have to change. Used by most programs in
+ * this package.
+ *
+ */
+
+#define PROGRAMVERSION "3.3.2"
+
+#define NON_FATAL 0
+#define FATAL 1
+#define USER_FATAL 2
+
+#define OFF 0
+#define ON 1
+
+#define FALSE 0
+#define TRUE 1
+
+#define BYTE 8
+#define BMASK 0377
+
+#define POINTS 72.3
+
+#ifndef PI
+#define PI 3.141592654
+#endif
+
+#define ONEBYTE 0
+#define UTFENCODING 1
+
+#define READING ONEBYTE
+#define WRITING ONEBYTE
+
+/*
+ *
+ * DOROUND controls whether some translators include file ROUNDPAGE (path.h)
+ * after the prologue. Used to round page dimensions obtained from the clippath
+ * to know paper sizes. Enabled by setting DOROUND to TRUE (or 1).
+ *
+ */
+
+#define DOROUND TRUE
+
+/*
+ *
+ * Default resolution and the height and width of a page (in case we need to get
+ * to upper left corner) - only used in BoundingBox calculations!!
+ *
+ */
+
+#define DEFAULT_RES 72
+#define PAGEHEIGHT 11.0 * DEFAULT_RES
+#define PAGEWIDTH 8.5 * DEFAULT_RES
+
+/*
+ *
+ * Simple macros.
+ *
+ */
+
+#define ABS(A) ((A) >= 0 ? (A) : -(A))
+#define MIN(A, B) ((A) < (B) ? (A) : (B))
+#define MAX(A, B) ((A) > (B) ? (A) : (B))
+
diff --git a/src/cmd/postscript/common/getopt.c b/src/cmd/postscript/common/getopt.c
new file mode 100644
index 00000000..cf6619f7
--- /dev/null
+++ b/src/cmd/postscript/common/getopt.c
@@ -0,0 +1,56 @@
+#ifndef _POSIX_SOURCE
+#include <u.h>
+#include <libc.h>
+#endif
+#include <stdio.h>
+#define ERR(str, chr) if(opterr){fprintf(stderr, "%s%s%c\n", argv[0], str, chr);}
+int opterr = 1;
+int optind = 1;
+int optopt;
+char *optarg;
+char *strchr();
+
+int
+getopt (argc, argv, opts)
+char **argv, *opts;
+{
+ static int sp = 1;
+ register c;
+ register char *cp;
+
+ if (sp == 1)
+ if (optind >= argc ||
+ argv[optind][0] != '-' || argv[optind][1] == '\0')
+ return EOF;
+ else if (strcmp(argv[optind], "--") == NULL) {
+ optind++;
+ return EOF;
+ }
+ optopt = c = argv[optind][sp];
+ if (c == ':' || (cp=strchr(opts, c)) == NULL) {
+ ERR (": illegal option -- ", c);
+ if (argv[optind][++sp] == '\0') {
+ optind++;
+ sp = 1;
+ }
+ return '?';
+ }
+ if (*++cp == ':') {
+ if (argv[optind][sp+1] != '\0')
+ optarg = &argv[optind++][sp+1];
+ else if (++optind >= argc) {
+ ERR (": option requires an argument -- ", c);
+ sp = 1;
+ return '?';
+ } else
+ optarg = argv[optind++];
+ sp = 1;
+ } else {
+ if (argv[optind][++sp] == '\0') {
+ sp = 1;
+ optind++;
+ }
+ optarg = NULL;
+ }
+ return c;
+}
diff --git a/src/cmd/postscript/common/glob.c b/src/cmd/postscript/common/glob.c
new file mode 100644
index 00000000..2826f4e5
--- /dev/null
+++ b/src/cmd/postscript/common/glob.c
@@ -0,0 +1,29 @@
+/*
+ *
+ * Global varibles - for PostScript translators.
+ *
+ */
+
+#include <stdio.h>
+#include "gen.h"
+
+char **argv; /* global so everyone can use them */
+int argc;
+
+int x_stat = 0; /* program exit status */
+int debug = OFF; /* debug flag */
+int ignore = OFF; /* what we do with FATAL errors */
+
+long lineno = 0; /* line number */
+long position = 0; /* byte position */
+char *prog_name = ""; /* and program name - for errors */
+char *temp_file = NULL; /* temporary file - for some programs */
+char *fontencoding = NULL; /* text font encoding scheme */
+
+int dobbox = FALSE; /* enable BoundingBox stuff if TRUE */
+double pageheight = PAGEHEIGHT; /* only for BoundingBox calculations! */
+double pagewidth = PAGEWIDTH;
+
+int reading = UTFENCODING; /* input */
+int writing = WRITING; /* and output encoding */
+
diff --git a/src/cmd/postscript/common/misc.c b/src/cmd/postscript/common/misc.c
new file mode 100644
index 00000000..25bd37aa
--- /dev/null
+++ b/src/cmd/postscript/common/misc.c
@@ -0,0 +1,230 @@
+/*
+ *
+ * General purpose routines.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include "gen.h"
+#include "ext.h"
+#include "path.h"
+
+int nolist = 0; /* number of specified ranges */
+int olist[50]; /* processing range pairs */
+
+/*****************************************************************************/
+
+out_list(str)
+
+ char *str;
+
+{
+
+ int start, stop;
+
+/*
+ *
+ * Grab page ranges from str, save them in olist[], and update the nolist
+ * count. Range syntax matches nroff/troff syntax.
+ *
+ */
+
+ while ( *str && nolist < sizeof(olist) - 2 ) {
+ start = stop = str_convert(&str, 0);
+
+ if ( *str == '-' && *str++ )
+ stop = str_convert(&str, 9999);
+
+ if ( start > stop )
+ error(FATAL, "illegal range %d-%d", start, stop);
+
+ olist[nolist++] = start;
+ olist[nolist++] = stop;
+
+ if ( *str != '\0' ) str++;
+ } /* End while */
+
+ olist[nolist] = 0;
+
+} /* End of out_list */
+
+/*****************************************************************************/
+
+in_olist(num)
+
+ int num;
+
+{
+
+ int i;
+
+/*
+ *
+ * Return ON if num is in the current page range list. Print everything if
+ * there's no list.
+ *
+ */
+ if ( nolist == 0 )
+ return(ON);
+
+ for ( i = 0; i < nolist; i += 2 )
+ if ( num >= olist[i] && num <= olist[i+1] )
+ return(ON);
+
+ return(OFF);
+
+} /* End of in_olist */
+
+/*****************************************************************************/
+
+setencoding(name)
+
+ char *name;
+
+{
+
+ char path[150];
+
+/*
+ *
+ * Include the font encoding file selected by name. It's a full pathname if
+ * it begins with /, otherwise append suffix ".enc" and look for the file in
+ * ENCODINGDIR. Missing files are silently ignored.
+ *
+ */
+
+ if ( name == NULL )
+ name = "Default";
+
+ if ( *name == '/' )
+ strcpy(path, name);
+ else sprintf(path, "%s/%s.enc", ENCODINGDIR, name);
+
+ if ( cat(path) == TRUE )
+ writing = strncmp(name, "UTF", 3) == 0;
+
+} /* End of setencoding */
+
+/*****************************************************************************/
+
+cat(file)
+
+ char *file;
+
+{
+
+ int fd_in;
+ int fd_out;
+ char buf[512];
+ int count;
+
+/*
+ *
+ * Copy *file to stdout. Return FALSE is there was a problem.
+ *
+ */
+
+ fflush(stdout);
+
+ if ( (fd_in = open(file, O_RDONLY)) == -1 )
+ return(FALSE);
+
+ fd_out = fileno(stdout);
+ while ( (count = read(fd_in, buf, sizeof(buf))) > 0 )
+ write(fd_out, buf, count);
+
+ close(fd_in);
+
+ return(TRUE);
+
+} /* End of cat */
+
+/*****************************************************************************/
+
+str_convert(str, err)
+
+ char **str;
+ int err;
+
+{
+
+ int i;
+
+/*
+ *
+ * Grab the next integer from **str and return its value or err if *str
+ * isn't an integer. *str is modified after each digit is read.
+ *
+ */
+
+ if ( ! isdigit(**str) )
+ return(err);
+
+ for ( i = 0; isdigit(**str); *str += 1 )
+ i = 10 * i + **str - '0';
+
+ return(i);
+
+} /* End of str_convert */
+
+/*****************************************************************************/
+
+error(kind, mesg, a1, a2, a3)
+
+ int kind;
+ char *mesg;
+ unsigned a1, a2, a3;
+
+{
+
+/*
+ *
+ * Print an error message and quit if kind is FATAL.
+ *
+ */
+
+ if ( mesg != NULL && *mesg != '\0' ) {
+ fprintf(stderr, "%s: ", prog_name);
+ fprintf(stderr, mesg, a1, a2, a3);
+ if ( lineno > 0 )
+ fprintf(stderr, " (line %d)", lineno);
+ if ( position > 0 )
+ fprintf(stderr, " (near byte %d)", position);
+ putc('\n', stderr);
+ } /* End if */
+
+ if ( kind == FATAL && ignore == OFF ) {
+ if ( temp_file != NULL )
+ unlink(temp_file);
+ exit(x_stat | 01);
+ } /* End if */
+
+} /* End of error */
+
+/*****************************************************************************/
+
+void interrupt(sig)
+
+ int sig;
+
+{
+
+/*
+ *
+ * Signal handler for translators.
+ *
+ */
+
+ if ( temp_file != NULL )
+ unlink(temp_file);
+
+ exit(1);
+
+} /* End of interrupt */
+
+/*****************************************************************************/
+
diff --git a/src/cmd/postscript/common/mkfile b/src/cmd/postscript/common/mkfile
new file mode 100644
index 00000000..09d43035
--- /dev/null
+++ b/src/cmd/postscript/common/mkfile
@@ -0,0 +1,23 @@
+<$PLAN9/src/mkhdr
+
+<../config
+
+LIB=com.a
+OFILES=bbox.$O\
+ glob.$O\
+ misc.$O\
+ request.$O\
+ rune.$O\
+ tempnam.$O\
+ getopt.$O\
+
+HFILES=comments.h\
+ gen.h\
+ ext.h\
+ request.h\
+ path.h\
+ rune.h\
+
+<$PLAN9/src/mklib
+
+CFLAGS=-c -D$SYSTEM -D_POSIX_SOURCE
diff --git a/src/cmd/postscript/common/path.h b/src/cmd/postscript/common/path.h
new file mode 100644
index 00000000..a3692139
--- /dev/null
+++ b/src/cmd/postscript/common/path.h
@@ -0,0 +1,32 @@
+/*
+ *
+ * pathname definitions for important files and directories.
+ *
+ */
+
+#define DPOST "#9/sys/lib/postscript/prologues/dpost.ps"
+#define POSTBGI "#9/sys/lib/postscript/prologues/postbgi.ps"
+#define POSTDAISY "#9/sys/lib/postscript/prologues/postdaisy.ps"
+#define POSTDMD "#9/sys/lib/postscript/prologues/postdmd.ps"
+#define POSTMD "#9/sys/lib/postscript/prologues/postmd.ps"
+#define POSTPLOT "#9/sys/lib/postscript/prologues/postplot.ps"
+#define POSTPRINT "#9/sys/lib/postscript/prologues/postprint.ps"
+#define POSTNPRINT "#9/sys/lib/postscript/prologues/postnprint.ps"
+#define POSTTEK "#9/sys/lib/postscript/prologues/posttek.ps"
+#define POSTGIF "#9/sys/lib/postscript/prologues/postgif.ps"
+
+#define BASELINE "#9/sys/lib/postscript/prologues/baseline.ps"
+#define COLOR "#9/sys/lib/postscript/prologues/color.ps"
+#define DRAW "#9/sys/lib/postscript/prologues/draw.ps"
+#define FORMFILE "#9/sys/lib/postscript/prologues/forms.ps"
+#define SHADEFILE "#9/sys/lib/postscript/prologues/shade.ps"
+#define KERNING "#9/sys/lib/postscript/prologues/kerning.ps"
+#define REQUESTFILE "#9/sys/lib/postscript/prologues/ps.requests"
+#define ROUNDPAGE "#9/sys/lib/postscript/prologues/roundpage.ps"
+
+#define ENCODINGDIR "#9/sys/lib/postscript/prologues"
+#define HOSTDIR "#9/sys/lib/postscript/font"
+#define FONTDIR "#9/sys/lib/troff/font"
+#define POSTLIBDIR "#9/sys/lib/postscript/prologues"
+#define TEMPDIR "/tmp"
+
diff --git a/src/cmd/postscript/common/request.c b/src/cmd/postscript/common/request.c
new file mode 100644
index 00000000..d8d7dd13
--- /dev/null
+++ b/src/cmd/postscript/common/request.c
@@ -0,0 +1,119 @@
+/*
+ *
+ * Things used to handle special requests (eg. manual feed) globally or on a per
+ * page basis. Requests are passed through to the translator using the -R option.
+ * The argument to -R can be "request", "request:page", or "request:page:file".
+ * If page is omitted (as in the first form) or set to 0 request will be applied
+ * to the global environment. In all other cases it applies only to the selected
+ * page. If a file is given, page must be supplied, and the lookup is in that file
+ * rather than *requestfile.
+ *
+ */
+
+#include <stdio.h>
+
+#include "gen.h" /* general purpose definitions */
+#include "request.h" /* a few special definitions */
+#include "path.h" /* for the default request file */
+
+Request request[MAXREQUEST]; /* next page or global request */
+int nextreq = 0; /* goes in request[nextreq] */
+char *requestfile = REQUESTFILE; /* default lookup file */
+
+/*****************************************************************************/
+
+saverequest(want)
+
+ char *want; /* grab code for this stuff */
+
+{
+
+ char *page; /* and save it for this page */
+ char *strtok();
+
+/*
+ *
+ * Save the request until we get to appropriate page - don't even bother with
+ * the lookup right now. Format of *want string is "request", "request:page", or
+ * "request:page:file", and we assume we can change the string here as needed.
+ * If page is omitted or given as 0 the request will be done globally. If *want
+ * includes a file, request and page must also be given, and in that case *file
+ * will be used for the lookup.
+ *
+ */
+
+ if ( nextreq < MAXREQUEST ) {
+ request[nextreq].want = strtok(want, ": ");
+ if ( (page = strtok(NULL, ": ")) == NULL )
+ request[nextreq].page = 0;
+ else request[nextreq].page = atoi(page);
+ if ( (request[nextreq].file = strtok(NULL, ": ")) == NULL )
+ request[nextreq].file = requestfile;
+ nextreq++;
+ } else error(NON_FATAL, "too many requests - ignoring %s", want);
+
+} /* End of saverequest */
+
+/*****************************************************************************/
+
+writerequest(page, fp_out)
+
+ int page; /* write everything for this page */
+ FILE *fp_out; /* to this file */
+
+{
+
+ int i; /* loop index */
+
+/*
+ *
+ * Writes out all the requests that have been saved for page. Page 0 refers to
+ * the global environment and is done during initial setup.
+ *
+ */
+
+ for ( i = 0; i < nextreq; i++ )
+ if ( request[i].page == page )
+ dumprequest(request[i].want, request[i].file, fp_out);
+
+} /* End of writerequest */
+
+/*****************************************************************************/
+
+dumprequest(want, file, fp_out)
+
+ char *want; /* look for this string */
+ char *file; /* in this file */
+ FILE *fp_out; /* and write the value out here */
+
+{
+
+ char buf[100]; /* line buffer for reading *file */
+ FILE *fp_in;
+
+/*
+ *
+ * Looks for *want in the request file and if it's found the associated value
+ * is copied to the output file. Keywords (ie. the *want strings) begin an @ in
+ * the first column of file, while the values (ie. the stuff that's copied to
+ * the output file) starts on the next line and extends to the next keyword or
+ * to the end of file.
+ *
+ */
+
+ if ( (fp_in = fopen(file, "r")) != NULL ) {
+ while ( fgets(buf, sizeof(buf), fp_in) != NULL )
+ if ( buf[0] == '@' && strncmp(want, &buf[1], strlen(want)) == 0 )
+ while ( fgets(buf, sizeof(buf), fp_in) != NULL )
+ if ( buf[0] == '#' || buf[0] == '%' )
+ continue;
+ else if ( buf[0] != '@' )
+ fprintf(fp_out, "%s", buf);
+ else break;
+ fclose(fp_in);
+ } /* End if */
+
+} /* End of dumprequest */
+
+/*****************************************************************************/
+
diff --git a/src/cmd/postscript/common/request.h b/src/cmd/postscript/common/request.h
new file mode 100644
index 00000000..25d94d01
--- /dev/null
+++ b/src/cmd/postscript/common/request.h
@@ -0,0 +1,22 @@
+/*
+ *
+ * Things used to handle special PostScript requests (like manual feed) globally
+ * or on a per page basis. All the translators I've supplied accept the -R option
+ * that can be used to insert special PostScript code before the global setup is
+ * done, or at the start of named pages. The argument to the -R option is a string
+ * that can be "request", "request:page", or "request:page:file". If page isn't
+ * given (as in the first form) or if it's 0 in the last two, the request applies
+ * to the global environment, otherwise request holds only for the named page.
+ * If a file name is given a page number must be supplied, and in that case the
+ * request will be looked up in that file.
+ *
+ */
+
+#define MAXREQUEST 30
+
+typedef struct {
+ char *want;
+ int page;
+ char *file;
+} Request;
+
diff --git a/src/cmd/postscript/common/rune.c b/src/cmd/postscript/common/rune.c
new file mode 100644
index 00000000..01ee6ba8
--- /dev/null
+++ b/src/cmd/postscript/common/rune.c
@@ -0,0 +1,142 @@
+#include "rune.h"
+
+enum
+{
+ Bit1 = 7,
+ Bitx = 6,
+ Bit2 = 5,
+ Bit3 = 4,
+ Bit4 = 3,
+
+ T1 = ((1<<(Bit1+1))-1) ^ 0xFF, /* 0000 0000 */
+ Tx = ((1<<(Bitx+1))-1) ^ 0xFF, /* 1000 0000 */
+ T2 = ((1<<(Bit2+1))-1) ^ 0xFF, /* 1100 0000 */
+ T3 = ((1<<(Bit3+1))-1) ^ 0xFF, /* 1110 0000 */
+ T4 = ((1<<(Bit4+1))-1) ^ 0xFF, /* 1111 0000 */
+
+ Rune1 = (1<<(Bit1+0*Bitx))-1, /* 0000 0000 0111 1111 */
+ Rune2 = (1<<(Bit2+1*Bitx))-1, /* 0000 0111 1111 1111 */
+ Rune3 = (1<<(Bit3+2*Bitx))-1, /* 1111 1111 1111 1111 */
+
+ Maskx = (1<<Bitx)-1, /* 0011 1111 */
+ Testx = Maskx ^ 0xFF, /* 1100 0000 */
+
+ Bad = Runeerror,
+};
+
+int
+chartorune(Rune *rune, char *str)
+{
+ int c, c1, c2;
+ long l;
+
+ /*
+ * one character sequence
+ * 00000-0007F => T1
+ */
+ c = *(unsigned char*)str;
+ if(c < Tx) {
+ *rune = c;
+ return 1;
+ }
+
+ /*
+ * two character sequence
+ * 0080-07FF => T2 Tx
+ */
+ c1 = *(unsigned char*)(str+1) ^ Tx;
+ if(c1 & Testx)
+ goto bad;
+ if(c < T3) {
+ if(c < T2)
+ goto bad;
+ l = ((c << Bitx) | c1) & Rune2;
+ if(l <= Rune1)
+ goto bad;
+ *rune = l;
+ return 2;
+ }
+
+ /*
+ * three character sequence
+ * 0800-FFFF => T3 Tx Tx
+ */
+ c2 = *(unsigned char*)(str+2) ^ Tx;
+ if(c2 & Testx)
+ goto bad;
+ if(c < T4) {
+ l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3;
+ if(l <= Rune2)
+ goto bad;
+ *rune = l;
+ return 3;
+ }
+
+ /*
+ * bad decoding
+ */
+bad:
+ *rune = Bad;
+ return 1;
+}
+
+int
+runetochar(char *str, Rune *rune)
+{
+ long c;
+
+ /*
+ * one character sequence
+ * 00000-0007F => 00-7F
+ */
+ c = *rune;
+ if(c <= Rune1) {
+ str[0] = c;
+ return 1;
+ }
+
+ /*
+ * two character sequence
+ * 0080-07FF => T2 Tx
+ */
+ if(c <= Rune2) {
+ str[0] = T2 | (c >> 1*Bitx);
+ str[1] = Tx | (c & Maskx);
+ return 2;
+ }
+
+ /*
+ * three character sequence
+ * 0800-FFFF => T3 Tx Tx
+ */
+ str[0] = T3 | (c >> 2*Bitx);
+ str[1] = Tx | ((c >> 1*Bitx) & Maskx);
+ str[2] = Tx | (c & Maskx);
+ return 3;
+}
+
+int
+runelen(long c)
+{
+ Rune rune;
+ char str[10];
+
+ rune = c;
+ return runetochar(str, &rune);
+}
+
+int
+fullrune(char *str, int n)
+{
+ int c;
+
+ if(n > 0) {
+ c = *(unsigned char*)str;
+ if(c < Tx)
+ return 1;
+ if(n > 1)
+ if(c < T3 || n > 2)
+ return 1;
+ }
+ return 0;
+}
diff --git a/src/cmd/postscript/common/rune.h b/src/cmd/postscript/common/rune.h
new file mode 100644
index 00000000..2df5f767
--- /dev/null
+++ b/src/cmd/postscript/common/rune.h
@@ -0,0 +1,19 @@
+/*
+ *
+ * Rune declarations - for supporting UTF encoding.
+ *
+ */
+
+#define RUNELIB 1
+
+#ifdef RUNELIB
+typedef unsigned short Rune;
+
+enum
+{
+ UTFmax = 3, /* maximum bytes per rune */
+ Runesync = 0x80, /* cannot represent part of a utf sequence (<) */
+ Runeself = 0x80, /* rune and utf sequences are the same (<) */
+ Runeerror = 0x80, /* decoding error in utf */
+};
+#endif
diff --git a/src/cmd/postscript/common/tempnam.c b/src/cmd/postscript/common/tempnam.c
new file mode 100644
index 00000000..529025ed
--- /dev/null
+++ b/src/cmd/postscript/common/tempnam.c
@@ -0,0 +1,27 @@
+#include <stdio.h>
+#include <errno.h>
+
+#if defined(V9) || defined(BSD4_2) || defined(plan9)
+char *tempnam(char *dir, char *pfx) {
+ int pid;
+ unsigned int len;
+ char *tnm, *malloc();
+ static int seq = 0;
+
+ pid = getpid();
+ len = strlen(dir) + strlen(pfx) + 10;
+ if ((tnm = malloc(len)) != NULL) {
+ sprintf(tnm, "%s", dir);
+ if (access(tnm, 7) == -1)
+ return(NULL);
+ do {
+ sprintf(tnm, "%s/%s%d%d", dir, pfx, pid, seq++);
+ errno = 0;
+ if (access(tnm, 7) == -1)
+ if (errno == ENOENT)
+ return(tnm);
+ } while (1);
+ }
+ return(tnm);
+}
+#endif
diff --git a/src/cmd/postscript/download/README b/src/cmd/postscript/download/README
new file mode 100644
index 00000000..9bfce12a
--- /dev/null
+++ b/src/cmd/postscript/download/README
@@ -0,0 +1,11 @@
+
+A simple program that scans PostScript files for %%DocumentFonts:
+comments and prepends requested host resident font files to the
+input. Written for Unix 4.0 lp.
+
+Downloaded fonts are the ones named in the %%DocumentFonts: comment
+and listed in a special map file (which can be selected using the
+-m option). See example.map and comments in download.c for examples
+of map files. By default map files and font files are in *hostfontdir.
+It's initialized using HOSTDIR (file ../common/path.h).
+
diff --git a/src/cmd/postscript/download/download.c b/src/cmd/postscript/download/download.c
new file mode 100644
index 00000000..ab6bdd52
--- /dev/null
+++ b/src/cmd/postscript/download/download.c
@@ -0,0 +1,545 @@
+/*
+ *
+ * download - host resident font downloader
+ *
+ * Prepends host resident fonts to PostScript input files. The program assumes
+ * the input files are part of a single PostScript job and that requested fonts
+ * can be downloaded at the start of each input file. Downloaded fonts are the
+ * ones named in a %%DocumentFonts: comment and listed in a special map table.
+ * Map table pathnames (supplied using the -m option) that begin with a / are
+ * taken as is. Otherwise the final pathname is built using *hostfontdir (-H
+ * option), *mapname (-m option), and *suffix.
+ *
+ * The map table consists of fontname-filename pairs, separated by white space.
+ * Comments are introduced by % (as in PostScript) and extend to the end of the
+ * current line. The only fonts that can be downloaded are the ones listed in
+ * the active map table that point the program to a readable Unix file. A request
+ * for an unlisted font or inaccessible file is ignored. All font requests are
+ * ignored if the map table can't be read. In that case the program simply copies
+ * the input files to stdout.
+ *
+ * An example (but not one to follow) of what can be in a map table is,
+ *
+ * %
+ * % Map requests for Bookman-Light to file *hostfontdir/KR
+ * %
+ *
+ * Bookman-Light KR % Keeping everything (including the map
+ * % table) in *hostfontdir seems like the
+ * % cleanest approach.
+ *
+ * %
+ * % Map Palatino-Roman to file *hostfontdir/palatino/Roman
+ * %
+ * Palatino-Roman palatino/Roman
+ *
+ * % Map ZapfDingbats to file /usr/lib/host/dingbats
+ *
+ * ZapfDingbats /usr/lib/host/dingbats
+ *
+ * Once again, file names that begin with a / are taken as is. All others have
+ * *hostfontdir/ prepended to the file string associated with a particular font.
+ *
+ * Map table can be associated with a printer model (e.g. a LaserWriter), a
+ * printer destination, or whatever - the choice is up to an administrator.
+ * By destination may be best if your spooler is running several private
+ * printers. Host resident fonts are usually purchased under a license that
+ * restricts their use to a limited number of printers. A font licensed for
+ * a single printer should only be used on that printer.
+ *
+ * Was written quickly, so there's much room for improvement. Undoubtedly should
+ * be a more general program (e.g. scan for other comments).
+ *
+ */
+
+#include <stdio.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <string.h>
+
+#include "comments.h" /* PostScript file structuring comments */
+#include "gen.h" /* general purpose definitions */
+#include "path.h" /* for temporary directory */
+#include "ext.h" /* external variable declarations */
+#include "download.h" /* a few special definitions */
+
+char *temp_dir = TEMPDIR; /* temp directory - for copying stdin */
+char *hostfontdir = HOSTDIR; /* host resident directory */
+char *mapname = "map"; /* map table - usually in *hostfontdir */
+char *suffix = ""; /* appended to the map table pathname */
+Map *map = NULL; /* device font map table */
+char *stringspace = NULL; /* for storing font and file strings */
+int next = 0; /* next free slot in map[] */
+
+char *residentfonts = NULL; /* list of printer resident fonts */
+char *printer = NULL; /* printer name - only for Unix 4.0 lp */
+
+char buf[2048]; /* input file line buffer */
+char *comment = DOCUMENTFONTS; /* look for this comment */
+int atend = FALSE; /* TRUE only if a comment says so */
+
+FILE *fp_in; /* next input file */
+FILE *fp_temp = NULL; /* for copying stdin */
+
+/*****************************************************************************/
+
+main(agc, agv)
+
+ int agc;
+ char *agv[];
+
+{
+
+/*
+ *
+ * Host resident font downloader. The input files are assumed to be part of a
+ * single PostScript job.
+ *
+ */
+
+ fp_in = stdin;
+
+ argc = agc; /* other routines may want them */
+ argv = agv;
+
+ prog_name = argv[0]; /* just for error messages */
+
+ init_signals(); /* sets up interrupt handling */
+ options(); /* first get command line options */
+ readmap(); /* read the font map table */
+ readresident(); /* and the optional resident font list */
+ arguments(); /* then process non-option arguments */
+ done(); /* and clean things up */
+ exit(x_stat); /* not much could be wrong */
+
+} /* End of main */
+
+/*****************************************************************************/
+
+init_signals()
+
+{
+
+/*
+ *
+ * Makes sure we handle interrupts properly.
+ *
+ */
+
+ if ( signal(SIGINT, interrupt) == SIG_IGN ) {
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ } else {
+ signal(SIGHUP, interrupt);
+ signal(SIGQUIT, interrupt);
+ } /* End else */
+
+ signal(SIGTERM, interrupt);
+
+} /* End of init_signals */
+
+/*****************************************************************************/
+
+options()
+
+{
+
+ int ch; /* return value from getopt() */
+ char *optnames = "c:fm:p:r:H:T:DI";
+
+ extern char *optarg; /* used by getopt() */
+ extern int optind;
+
+/*
+ *
+ * Reads and processes the command line options.
+ *
+ */
+
+ while ( (ch = getopt(argc, argv, optnames)) != EOF ) {
+ switch ( ch ) {
+ case 'c': /* look for this comment */
+ comment = optarg;
+ break;
+
+ case 'f': /* force a complete input file scan */
+ atend = TRUE;
+ break;
+
+ case 'm': /* printer map table name */
+ mapname = optarg;
+ break;
+
+ case 'p': /* printer name - for Unix 4.0 lp */
+ printer = optarg;
+ break;
+
+ case 'r': /* resident font list */
+ residentfonts = optarg;
+ break;
+
+ case 'H': /* host resident font directory */
+ hostfontdir = optarg;
+ break;
+
+ case 'T': /* temporary file directory */
+ temp_dir = optarg;
+ break;
+
+ case 'D': /* debug flag */
+ debug = ON;
+ break;
+
+ case 'I': /* ignore FATAL errors */
+ ignore = ON;
+ break;
+
+ case '?': /* don't understand the option */
+ error(FATAL, "");
+ break;
+
+ default: /* don't know what to do for ch */
+ error(FATAL, "missing case for option %c\n", ch);
+ break;
+ } /* End switch */
+ } /* End while */
+
+ argc -= optind; /* get ready for non-option args */
+ argv += optind;
+
+} /* End of options */
+
+/*****************************************************************************/
+
+readmap()
+
+{
+
+ char *path;
+ char *ptr;
+ int fd;
+ struct stat sbuf;
+
+/*
+ *
+ * Initializes the map table by reading an ASCII mapping file. If mapname begins
+ * with a / it's the map table. Otherwise hostfontdir, mapname, and suffix are
+ * combined to build the final pathname. If we can open the file we read it all
+ * into memory, erase comments, and separate the font and file name pairs. When
+ * we leave next points to the next free slot in the map[] array. If it's zero
+ * nothing was in the file or we couldn't open it.
+ *
+ */
+
+ if ( hostfontdir == NULL || mapname == NULL )
+ return;
+
+ if ( *mapname != '/' ) {
+ if ( (path = (char *)malloc(strlen(hostfontdir) + strlen(mapname) +
+ strlen(suffix) + 2)) == NULL )
+ error(FATAL, "no memory");
+ sprintf(path, "%s/%s%s", hostfontdir, mapname, suffix);
+ } else path = mapname;
+
+ if ( (fd = open(unsharp(path), 0)) != -1 ) {
+ if ( fstat(fd, &sbuf) == -1 )
+ error(FATAL, "can't fstat %s", path);
+ if ( (stringspace = (char *)malloc(sbuf.st_size + 2)) == NULL )
+ error(FATAL, "no memory");
+ if ( read(fd, stringspace, sbuf.st_size) == -1 )
+ error(FATAL, "can't read %s", path);
+ close(fd);
+
+ stringspace[sbuf.st_size] = '\n'; /* just to be safe */
+ stringspace[sbuf.st_size+1] = '\0';
+ for ( ptr = stringspace; *ptr != '\0'; ptr++ ) /* erase comments */
+ if ( *ptr == '%' )
+ for ( ; *ptr != '\n' ; ptr++ )
+ *ptr = ' ';
+
+ for ( ptr = stringspace; ; next++ ) {
+ if ( (next % 50) == 0 )
+ map = allocate(map, next+50);
+ map[next].downloaded = FALSE;
+ map[next].font = strtok(ptr, " \t\n");
+ map[next].file = strtok(ptr = NULL, " \t\n");
+ if ( map[next].font == NULL )
+ break;
+ if ( map[next].file == NULL )
+ error(FATAL, "map table format error - check %s", path);
+ } /* End for */
+ } /* End if */
+
+} /* End of readmap */
+
+/*****************************************************************************/
+
+readresident()
+
+{
+
+ FILE *fp;
+ char *path;
+ int ch;
+ int n;
+
+/*
+ *
+ * Reads a file that lists the resident fonts for a particular printer and marks
+ * each font as already downloaded. Nothing's done if the file can't be read or
+ * there's no mapping file. Comments, as in the map file, begin with a % and
+ * extend to the end of the line. Added for Unix 4.0 lp.
+ *
+ */
+
+ if ( next == 0 || (printer == NULL && residentfonts == NULL) )
+ return;
+
+ if ( printer != NULL ) { /* use Unix 4.0 lp pathnames */
+ sprintf(buf, "%s/printers/%s", HOSTDIR, printer);
+ path = buf;
+ } else path = residentfonts;
+
+ if ( (fp = fopen(unsharp(path), "r")) != NULL ) {
+ while ( fscanf(fp, "%s", buf) != EOF )
+ if ( buf[0] == '%' )
+ while ( (ch = getc(fp)) != EOF && ch != '\n' ) ;
+ else if ( (n = lookup(buf)) < next )
+ map[n].downloaded = TRUE;
+ fclose(fp);
+ } /* End if */
+
+} /* End of readresident */
+
+/*****************************************************************************/
+
+arguments()
+
+{
+
+/*
+ *
+ * Makes sure all the non-option command line arguments are processed. If we get
+ * here and there aren't any arguments left, or if '-' is one of the input files
+ * we'll translate stdin. Assumes input files are part of a single PostScript
+ * job and fonts can be downloaded at the start of each file.
+ *
+ */
+
+ if ( argc < 1 )
+ download();
+ else {
+ while ( argc > 0 ) {
+ fp_temp = NULL;
+ if ( strcmp(*argv, "-") == 0 )
+ fp_in = stdin;
+ else if ( (fp_in = fopen(unsharp(*argv), "r")) == NULL )
+ error(FATAL, "can't open %s", *argv);
+ download();
+ if ( fp_in != stdin )
+ fclose(fp_in);
+ if ( fp_temp != NULL )
+ fclose(fp_temp);
+ argc--;
+ argv++;
+ } /* End while */
+ } /* End else */
+
+} /* End of arguments */
+
+/*****************************************************************************/
+
+done()
+
+{
+
+/*
+ *
+ * Clean things up before we quit.
+ *
+ */
+
+ if ( temp_file != NULL )
+ unlink(temp_file);
+
+} /* End of done */
+
+/*****************************************************************************/
+
+download()
+
+{
+
+ int infontlist = FALSE;
+
+/*
+ *
+ * If next is zero the map table is empty and all we do is copy the input file
+ * to stdout. Otherwise we read the input file looking for %%DocumentFonts: or
+ * continuation comments, add any accessible fonts to the output file, and then
+ * append the input file. When reading stdin we append lines to fp_temp and
+ * recover them when we're ready to copy the input file. fp_temp will often
+ * only contain part of stdin - if there's no %%DocumentFonts: (atend) comment
+ * we stop reading fp_in after the header.
+ *
+ */
+
+ if ( next > 0 ) {
+ if ( fp_in == stdin ) {
+ if ( (temp_file = tempnam(temp_dir, "post")) == NULL )
+ error(FATAL, "can't generate temp file name");
+ if ( (fp_temp = fopen(temp_file, "w+r")) == NULL )
+ error(FATAL, "can't open %s", temp_file);
+ unlink(temp_file);
+ } /* End if */
+
+ while ( fgets(buf, sizeof(buf), fp_in) != NULL ) {
+ if ( fp_temp != NULL )
+ fprintf(fp_temp, "%s", buf);
+ if ( buf[0] != '%' || buf[1] != '%' ) {
+ if ( (buf[0] != '%' || buf[1] != '!') && atend == FALSE )
+ break;
+ infontlist = FALSE;
+ } else if ( strncmp(buf, comment, strlen(comment)) == 0 ) {
+ copyfonts(buf);
+ infontlist = TRUE;
+ } else if ( buf[2] == '+' && infontlist == TRUE )
+ copyfonts(buf);
+ else infontlist = FALSE;
+ } /* End while */
+ } /* End if */
+
+ copyinput();
+
+} /* End of download */
+
+/*****************************************************************************/
+
+copyfonts(list)
+
+ char *list;
+
+{
+
+ char *font;
+ char *path;
+ int n;
+
+/*
+ *
+ * list points to a %%DocumentFonts: or continuation comment. What follows the
+ * the keyword will be a list of fonts separated by white space (or (atend)).
+ * Look for each font in the map table and if it's found copy the font file to
+ * stdout (once only).
+ *
+ */
+
+ strtok(list, " \n"); /* skip to the font list */
+
+ while ( (font = strtok(NULL, " \t\n")) != NULL ) {
+ if ( strcmp(font, ATEND) == 0 ) {
+ atend = TRUE;
+ break;
+ } /* End if */
+ if ( (n = lookup(font)) < next ) {
+ if ( *map[n].file != '/' ) {
+ if ( (path = (char *)malloc(strlen(hostfontdir)+strlen(map[n].file)+2)) == NULL )
+ error(FATAL, "no memory");
+ sprintf(path, "%s/%s", hostfontdir, map[n].file);
+ cat(unsharp(path));
+ free(path);
+ } else cat(unsharp(map[n].file));
+ map[n].downloaded = TRUE;
+ } /* End if */
+ } /* End while */
+
+} /* End of copyfonts */
+
+/*****************************************************************************/
+
+copyinput()
+
+{
+
+/*
+ *
+ * Copies the input file to stdout. If fp_temp isn't NULL seek to the start and
+ * add it to the output file - it's a partial (or complete) copy of stdin made
+ * by download(). Then copy fp_in, but only seek to the start if it's not stdin.
+ *
+ */
+
+ if ( fp_temp != NULL ) {
+ fseek(fp_temp, 0L, 0);
+ while ( fgets(buf, sizeof(buf), fp_temp) != NULL )
+ printf("%s", buf);
+ } /* End if */
+
+ if ( fp_in != stdin )
+ fseek(fp_in, 0L, 0);
+
+ while ( fgets(buf, sizeof(buf), fp_in) != NULL )
+ printf("%s", buf);
+
+} /* End of copyinput */
+
+/*****************************************************************************/
+
+lookup(font)
+
+ char *font;
+
+{
+
+ int i;
+
+/*
+ *
+ * Looks for *font in the map table. Return the map table index if found and
+ * not yet downloaded - otherwise return next.
+ *
+ */
+
+ for ( i = 0; i < next; i++ )
+ if ( strcmp(font, map[i].font) == 0 ) {
+ if ( map[i].downloaded == TRUE )
+ i = next;
+ break;
+ } /* End if */
+
+ return(i);
+
+} /* End of lookup */
+
+/*****************************************************************************/
+
+Map *allocate(ptr, num)
+
+ Map *ptr;
+ int num;
+
+{
+
+/*
+ *
+ * Allocates space for num Map elements. Calls malloc() if ptr is NULL and
+ * realloc() otherwise.
+ *
+ */
+
+ if ( ptr == NULL )
+ ptr = (Map *)malloc(num * sizeof(Map));
+ else ptr = (Map *)realloc(ptr, num * sizeof(Map));
+
+ if ( ptr == NULL )
+ error(FATAL, "no map memory");
+
+ return(ptr);
+
+} /* End of allocate */
+
+/*****************************************************************************/
+
diff --git a/src/cmd/postscript/download/download.h b/src/cmd/postscript/download/download.h
new file mode 100644
index 00000000..f88cc57a
--- /dev/null
+++ b/src/cmd/postscript/download/download.h
@@ -0,0 +1,14 @@
+/*
+ *
+ * The font data for a printer is saved in an array of the following type.
+ *
+ */
+
+typedef struct map {
+ char *font; /* a request for this PostScript font */
+ char *file; /* means copy this unix file */
+ int downloaded; /* TRUE after *file is downloaded */
+} Map;
+
+Map *allocate();
+
diff --git a/src/cmd/postscript/download/mkfile b/src/cmd/postscript/download/mkfile
new file mode 100644
index 00000000..d2321e65
--- /dev/null
+++ b/src/cmd/postscript/download/mkfile
@@ -0,0 +1,25 @@
+<$PLAN9/src/mkhdr
+
+<../config
+TARG=psdownload
+
+OFILES=download.$O
+
+COMMONDIR=../common
+
+HFILES=download.h\
+ $COMMONDIR/comments.h\
+ $COMMONDIR/gen.h\
+ $COMMONDIR/path.h\
+ $COMMONDIR/ext.h\
+
+LIB=$COMMONDIR/com.a
+BIN=$POSTBIN
+
+<$PLAN9/src/mkone
+CFLAGS=-c -D$SYSTEM -D_POSIX_SOURCE -I$COMMONDIR
+
+$LIB:
+ cd $COMMONDIR
+ mk install
+ mk clean
diff --git a/src/cmd/postscript/mkfile b/src/cmd/postscript/mkfile
new file mode 100644
index 00000000..158be819
--- /dev/null
+++ b/src/cmd/postscript/mkfile
@@ -0,0 +1,25 @@
+<$PLAN9/src/mkhdr
+
+<config
+
+DIRS=\
+ common\
+ tr2post\
+ download\
+# cropmarks\
+# grabit\
+# hardcopy\
+# mpictures\
+# postgif\
+# postprint\
+# postreverse\
+# posttek\
+# printfont\
+# psencoding\
+# psfiles\
+# g3p9bit\
+# p9bitpost\
+# tcpostio\
+# text2post\
+
+<$PLAN9/src/mkdirs
diff --git a/src/cmd/postscript/tr2post/Bgetfield.c b/src/cmd/postscript/tr2post/Bgetfield.c
new file mode 100644
index 00000000..8922139d
--- /dev/null
+++ b/src/cmd/postscript/tr2post/Bgetfield.c
@@ -0,0 +1,156 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "../common/common.h"
+#include "tr2post.h"
+
+#undef isspace
+#define isspace bisspace
+
+int
+isspace(Rune r)
+{
+ return(r==' ' || r=='\t' || r=='\n' || r == '\r' || r=='\f');
+}
+
+int
+Bskipws(Biobuf *bp) {
+ int r;
+ char c[UTFmax];
+ int sindex = 0;
+
+ /* skip over initial white space */
+ do {
+ r = Bgetrune(bp);
+ if (r == '\n') inputlineno++;
+ sindex++;
+ } while (r>=0 && isspace(r));
+ if (r<0) {
+ return(-1);
+ } else if (!isspace(r)) {
+ Bungetrune(bp);
+ --sindex;
+ }
+ return(sindex);
+}
+
+int
+asc2dig(char c, int base) {
+ if (c >= '0' && c <= '9')
+ if (base == 8 && c > '7') return(-1);
+ else return(c - '0');
+
+ if (base == 16)
+ if (c >= 'a' && c <= 'f') return(10 + c - 'a');
+ else if (c >= 'A' && c <= 'F') return(10 + c - 'A');
+
+ return(-1);
+}
+
+/* get a string of type: "d" for decimal integer, "u" for unsigned,
+ * "s" for string", "c" for char,
+ * return the number of characters gotten for the field. If nothing
+ * was gotten and the end of file was reached, a negative value
+ * from the Bgetrune is returned.
+ */
+
+int
+Bgetfield(Biobuf *bp, int type, void *thing, int size) {
+ int r;
+ Rune R;
+ char c[UTFmax];
+ int sindex = 0, i, j, n = 0;
+ int negate = 0;
+ int base = 10;
+ BOOLEAN bailout = FALSE;
+ int dig;
+ unsigned int u = 0;
+
+ /* skip over initial white space */
+ if (Bskipws(bp) < 0)
+ return(-1);
+
+ switch (type) {
+ case 'd':
+ while (!bailout && (r = Bgetrune(bp))>=0) {
+ switch (sindex++) {
+ case 0:
+ switch (r) {
+ case '-':
+ negate = 1;
+ continue;
+ case '+':
+ continue;
+ case '0':
+ base = 8;
+ continue;
+ default:
+ break;
+ }
+ break;
+ case 1:
+ if ((r == 'x' || r == 'X') && base == 8) {
+ base = 16;
+ continue;
+ }
+ }
+ if ((dig = asc2dig(r, base)) == -1) bailout = TRUE;
+ else n = dig + (n * base);
+ }
+ if (r < 0) return(-1);
+ *(int *)thing = (negate)?-n:n;
+ Bungetrune(bp);
+ break;
+ case 'u':
+ while (!bailout && (r = Bgetrune(bp))>=0) {
+ switch (sindex++) {
+ case 0:
+ if (*c == '0') {
+ base = 8;
+ continue;
+ }
+ break;
+ case 1:
+ if ((r == 'x' || r == 'X') && base == 8) {
+ base = 16;
+ continue;
+ }
+ }
+ if ((dig = asc2dig(r, base)) == -1) bailout = TRUE;
+ else u = dig + (n * base);
+ }
+ *(int *)thing = u;
+ if (r < 0) return(-1);
+ Bungetrune(bp);
+ break;
+ case 's':
+ j = 0;
+ while ((size>j+UTFmax) && (r = Bgetrune(bp))>=0 && !isspace(r)) {
+ R = r;
+ i = runetochar(&(((char *)thing)[j]), &R);
+ j += i;
+ sindex++;
+ }
+ ((char *)thing)[j++] = '\0';
+ if (r < 0) return(-1);
+ Bungetrune(bp);
+ break;
+ case 'r':
+ if ((r = Bgetrune(bp))>=0) {
+ *(Rune *)thing = r;
+ sindex++;
+ return(sindex);
+ }
+ if (r <= 0) return(-1);
+ Bungetrune(bp);
+ break;
+ default:
+ return(-2);
+ }
+ if (r < 0 && sindex == 0)
+ return(r);
+ else if (bailout && sindex == 1) {
+ return(0);
+ } else
+ return(sindex);
+}
diff --git a/src/cmd/postscript/tr2post/chartab.c b/src/cmd/postscript/tr2post/chartab.c
new file mode 100644
index 00000000..87ab556b
--- /dev/null
+++ b/src/cmd/postscript/tr2post/chartab.c
@@ -0,0 +1,458 @@
+/* Unicode | PostScript
+ * start end | offset font name
+ * 0x0000 0x00ff 0x00 LucidaSansUnicode00
+ */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "common.h"
+#include "tr2post.h"
+#include "comments.h"
+#include "path.h"
+
+/* Postscript font names, e.g., `LucidaSansUnicode00'
+ * names may only be added because reference to the
+ * names is made by indexing into this table.
+ */
+static struct pfnament *pfnafontmtab = 0;
+static int pfnamcnt = 0;
+int curpostfontid = -1;
+int curfontsize = -1;
+int curtrofffontid = -1;
+static int curfontpos = -1;
+static int fontheight = 0;
+static int fontslant = 0;
+
+/* This is troffs mounted font table. It is an anachronism resulting
+ * from the design of the APS typesetter. fontmnt is the
+ * number of positions available. fontmnt is really 11, but
+ * should not be limited.
+ */
+int fontmnt = 0;
+char **fontmtab;
+
+struct troffont *troffontab = 0;
+
+int troffontcnt = 0;
+
+void
+mountfont(int pos, char *fontname) {
+ int i;
+
+ if (debug) Bprint(Bstderr, "mountfont(%d, %s)\n", pos, fontname);
+ if (pos < 0 || pos >= fontmnt)
+ error(FATAL, "cannot mount a font at position %d,\n can only mount into postions 0-%d\n",
+ pos, fontmnt-1);
+
+ i = strlen(fontname);
+ fontmtab[pos] = galloc(fontmtab[pos], i+1, "mountfont():fontmtab");
+ strcpy(fontmtab[pos], fontname);
+ if (curfontpos == pos) curfontpos = -1;
+}
+
+void
+settrfont(void) {
+ if (curfontpos == fontpos) return;
+
+ if (fontmtab[fontpos] == 0)
+ error(FATAL, "Font at position %d was not initialized, botch!\n", fontpos);
+
+ curtrofffontid = findtfn(fontmtab[fontpos], 1);
+ if (debug) Bprint(Bstderr, "settrfont()-> curtrofffontid=%d\n", curtrofffontid);
+ curfontpos = fontpos;
+ if (curtrofffontid < 0) {
+ int i;
+
+ error(WARNING, "fontpos=%d\n", fontpos);
+ for (i=0; i<fontmnt; i++)
+ if (fontmtab[i] == 0)
+ error(WARNING, "fontmtab[%d]=0x0\n", i);
+ else
+ error(WARNING, "fontmtab[%d]=%s\n", i, fontmtab[i]);
+ exits("settrfont()");
+ }
+}
+
+void
+setpsfont(int psftid, int fontsize) {
+ if (psftid == curpostfontid && fontsize == curfontsize) return;
+ if (psftid >= pfnamcnt)
+ error(FATAL, "Postscript font index=%d used but not defined, there are only %d fonts\n",
+ psftid, pfnamcnt);
+
+ endstring();
+ if (pageon()) {
+ Bprint(Bstdout, "%d /%s f\n", fontsize, pfnafontmtab[psftid].str);
+ if ( fontheight != 0 || fontslant != 0 )
+ Bprint(Bstdout, "%d %d changefont\n", fontslant, (fontheight != 0) ? fontheight : fontsize);
+ pfnafontmtab[psftid].used = 1;
+ curpostfontid = psftid;
+ curfontsize = fontsize;
+ }
+}
+
+/* find index of PostScript font name in table
+ * returns -1 if name is not in table
+ * If insflg is not zero
+ * and the name is not found in the table, insert it.
+ */
+int
+findpfn(char *fontname, int insflg) {
+ char *tp;
+ int i;
+
+ for (i=0; i<pfnamcnt; i++) {
+ if (strcmp(pfnafontmtab[i].str, fontname) == 0)
+ return(i);
+ }
+ if (insflg) {
+ tp = galloc(pfnafontmtab, sizeof(struct pfnament)*(pfnamcnt+1), "findpfn():pfnafontmtab");
+ if (tp == 0)
+ return(-2);
+ pfnafontmtab = (struct pfnament *)tp;
+ i = strlen(fontname);
+ pfnafontmtab[pfnamcnt].str = galloc(0, i+1, "findpfn():pfnafontmtab[].str");
+ strncpy(pfnafontmtab[pfnamcnt].str, fontname, i);
+ pfnafontmtab[pfnamcnt].str[i] = '\0';
+ pfnafontmtab[pfnamcnt].used = 0;
+ return(pfnamcnt++);
+ }
+ return(-1);
+}
+
+char postroffdirname[] = "#9/sys/lib/postscript/troff"; /* "/sys/lib/postscript/troff/"; */
+char troffmetricdirname[] = "#9/sys/lib/troff/font"; /* "/sys/lib/troff/font/devutf/"; */
+
+int
+readpsfontdesc(char *fontname, int trindex) {
+ static char *filename = 0;
+ Biobuf *bfd;
+ Biobuf *Bfd;
+ int warn = 0, errorflg = 0, line =1, rv;
+ int start, end, offset;
+ int startfont, endfont, startchar, endchar, i, pfid;
+ char psfontnam[128];
+ struct troffont *tp;
+
+ if (debug) Bprint(Bstderr, "readpsfontdesc(%s,%d)\n", fontname, trindex);
+ filename=galloc(filename, strlen(postroffdirname)+1+strlen(fontname)+1, "readpsfontdesc: cannot allocate memory\n");
+ sprint(filename, "%s/%s", postroffdirname, fontname);
+
+ bfd = Bopen(unsharp(filename), OREAD);
+ if (bfd == 0) {
+ error(WARNING, "cannot open file %s\n", filename);
+ return(0);
+ }
+ Bfd = bfd;
+
+ do {
+ offset = 0;
+ if ((rv=Bgetfield(Bfd, 'd', &start, 0)) == 0) {
+ errorflg = 1;
+ error(WARNING, "file %s:%d illegal start value\n", filename, line);
+ } else if (rv < 0) break;
+ if ((rv=Bgetfield(Bfd, 'd', &end, 0)) == 0) {
+ errorflg = 1;
+ error(WARNING, "file %s:%d illegal end value\n", filename, line);
+ } else if (rv < 0) break;
+ if ((rv=Bgetfield(Bfd, 'd', &offset, 0)) < 0) {
+ errorflg = 1;
+ error(WARNING, "file %s:%d illegal offset value\n", filename, line);
+ }
+ if ((rv=Bgetfield(Bfd, 's', psfontnam, 128)) == 0) {
+ errorflg = 1;
+ error(WARNING, "file %s:%d illegal fontname value\n", filename, line);
+ } else if (rv < 0) break;
+ Brdline(Bfd, '\n');
+ if (!errorflg) {
+ struct psfent *psfentp;
+ startfont = RUNEGETGROUP(start);
+ startchar = RUNEGETCHAR(start);
+ endfont = RUNEGETGROUP(end);
+ endchar = RUNEGETCHAR(end);
+ pfid = findpfn(psfontnam, 1);
+ if (startfont != endfont) {
+ error(WARNING, "font descriptions must not cross 256 glyph block boundary\n");
+ errorflg = 1;
+ break;
+ }
+ tp = &(troffontab[trindex]);
+ tp->psfmap = galloc(tp->psfmap, ++(tp->psfmapsize)*sizeof(struct psfent), "readpsfontdesc():psfmap");
+ psfentp = &(tp->psfmap[tp->psfmapsize-1]);
+ psfentp->start = start;
+ psfentp->end = end;
+ psfentp->offset = offset;
+ psfentp->psftid = pfid;
+ if (debug) {
+ Bprint(Bstderr, "\tpsfmap->start=0x%x\n", start);
+ Bprint(Bstderr, "\tpsfmap->end=0x%x\n", end);
+ Bprint(Bstderr, "\tpsfmap->offset=0x%x\n", offset);
+ Bprint(Bstderr, "\tpsfmap->pfid=0x%x\n", pfid);
+ }
+/*
+ for (i=startchar; i<=endchar; i++) {
+ tp->charent[startfont][i].postfontid = pfid;
+ tp->charent[startfont][i].postcharid = i + offset - startchar;
+ }
+ */
+ if (debug) {
+ Bprint(Bstderr, "%x %x ", start, end);
+ if (offset) Bprint(Bstderr, "%x ", offset);
+ Bprint(Bstderr, "%s\n", psfontnam);
+ }
+ line++;
+ }
+ } while(errorflg != 1);
+ Bterm(Bfd);
+ return(1);
+}
+
+int
+readtroffmetric(char *fontname, int trindex) {
+ static char *filename = 0;
+ Biobuf *bfd;
+ Biobuf *Bfd;
+ int warn = 0, errorflg = 0, line =1, rv;
+ struct troffont *tp;
+ struct charent **cp;
+ char stoken[128], *str;
+ int ntoken;
+ Rune troffchar, quote;
+ int width, flag, charnum, thisfont, thischar;
+ BOOLEAN specharflag;
+
+ if (debug) Bprint(Bstderr, "readtroffmetric(%s,%d)\n", fontname, trindex);
+ filename=galloc(filename, strlen(troffmetricdirname)+4+strlen(devname)+1+strlen(fontname)+1, "readtroffmetric():filename");
+ sprint(filename, "%s/dev%s/%s", troffmetricdirname, devname, fontname);
+
+ bfd = Bopen(unsharp(filename), OREAD);
+ if (bfd == 0) {
+ error(WARNING, "cannot open file %s\n", filename);
+ return(0);
+ }
+ Bfd = bfd;
+ do {
+ /* deal with the few lines at the beginning of the
+ * troff font metric files.
+ */
+ if ((rv=Bgetfield(Bfd, 's', stoken, 128)) == 0) {
+ errorflg = 1;
+ error(WARNING, "file %s:%d illegal token\n", filename, line);
+ } else if (rv < 0) break;
+ if (debug) {
+ Bprint(Bstderr, "%s\n", stoken);
+ }
+
+ if (strcmp(stoken, "name") == 0) {
+ if ((rv=Bgetfield(Bfd, 's', stoken, 128)) == 0) {
+ errorflg = 1;
+ error(WARNING, "file %s:%d illegal token\n", filename, line);
+ } else if (rv < 0) break;
+ } else if (strcmp(stoken, "named") == 0) {
+ Brdline(Bfd, '\n');
+ } else if (strcmp(stoken, "fontname") == 0) {
+ if ((rv=Bgetfield(Bfd, 's', stoken, 128)) == 0) {
+ errorflg = 1;
+ error(WARNING, "file %s:%d illegal token\n", filename, line);
+ } else if (rv < 0) break;
+ } else if (strcmp(stoken, "spacewidth") == 0) {
+ if ((rv=Bgetfield(Bfd, 'd', &ntoken, 0)) == 0) {
+ errorflg = 1;
+ error(WARNING, "file %s:%d illegal token\n", filename, line);
+ } else if (rv < 0) break;
+ troffontab[trindex].spacewidth = ntoken;
+ thisfont = RUNEGETGROUP(' ');
+ thischar = RUNEGETCHAR(' ');
+ for (cp = &(troffontab[trindex].charent[thisfont][thischar]); *cp != 0; cp = &((*cp)->next))
+ if ((*cp)->name)
+ if (strcmp((*cp)->name, " ") == 0)
+ break;
+
+ if (*cp == 0) *cp = galloc(0, sizeof(struct charent), "readtroffmetric:charent");
+ (*cp)->postfontid = thisfont;
+ (*cp)->postcharid = thischar;
+ (*cp)->troffcharwidth = ntoken;
+ (*cp)->name = galloc(0, 2, "readtroffmetric: char name");
+ (*cp)->next = 0;
+ strcpy((*cp)->name, " ");
+ } else if (strcmp(stoken, "special") == 0) {
+ troffontab[trindex].special = TRUE;
+ } else if (strcmp(stoken, "charset") == 0) {
+ line++;
+ break;
+ }
+ if (!errorflg) {
+ line++;
+ }
+ } while(!errorflg && rv>=0);
+ while(!errorflg && rv>=0) {
+ if ((rv=Bgetfield(Bfd, 's', stoken, 128)) == 0) {
+ errorflg = 1;
+ error(WARNING, "file %s:%d illegal rune token <0x%x> rv=%d\n", filename, line, troffchar, rv);
+ } else if (rv < 0) break;
+ if (utflen(stoken) > 1) specharflag = TRUE;
+ else specharflag = FALSE;
+ /* if this character is a quote we have to use the previous characters info */
+ if ((rv=Bgetfield(Bfd, 'r', &quote, 0)) == 0) {
+ errorflg = 1;
+ error(WARNING, "file %s:%d illegal width or quote token <0x%x> rv=%d\n", filename, line, quote, rv);
+ } else if (rv < 0) break;
+ if (quote == '"') {
+ /* need some code here */
+
+ goto flush;
+ } else {
+ Bungetrune(Bfd);
+ }
+
+ if ((rv=Bgetfield(Bfd, 'd', &width, 0)) == 0) {
+ errorflg = 1;
+ error(WARNING, "file %s:%d illegal width token <0x%x> rv=%d\n", filename, line, troffchar, rv);
+ } else if (rv < 0) break;
+ if ((rv=Bgetfield(Bfd, 'd', &flag, 0)) == 0) {
+ errorflg = 1;
+ error(WARNING, "file %s:%d illegal flag token <0x%x> rv=%d\n", filename, line, troffchar, rv);
+ } else if (rv < 0) break;
+ if ((rv=Bgetfield(Bfd, 'd', &charnum, 0)) == 0) {
+ errorflg = 1;
+ error(WARNING, "file %s:%d illegal character number token <0x%x> rv=%d\n", filename, line, troffchar, rv);
+ } else if (rv < 0) break;
+flush:
+ str = Brdline(Bfd, '\n');
+ /* stash the crap from the end of the line for debugging */
+ if (debug) {
+ if (str == 0) {
+ Bprint(Bstderr, "premature EOF\n");
+ return(0);
+ }
+ str[Blinelen(Bfd)-1] = '\0';
+ }
+ line++;
+ chartorune(&troffchar, stoken);
+ if (specharflag) {
+ if (debug)
+ Bprint(Bstderr, "%s %d %d 0x%x %s # special\n",stoken, width, flag, charnum, str);
+ }
+ if (strcmp(stoken, "---") == 0) {
+ thisfont = RUNEGETGROUP(charnum);
+ thischar = RUNEGETCHAR(charnum);
+ stoken[0] = '\0';
+ } else {
+ thisfont = RUNEGETGROUP(troffchar);
+ thischar = RUNEGETCHAR(troffchar);
+ }
+ for (cp = &(troffontab[trindex].charent[thisfont][thischar]); *cp != 0; cp = &((*cp)->next))
+ if ((*cp)->name) {
+ if (debug) Bprint(Bstderr, "installing <%s>, found <%s>\n", stoken, (*cp)->name);
+ if (strcmp((*cp)->name, stoken) == 0)
+ break;
+ }
+ if (*cp == 0) *cp = galloc(0, sizeof(struct charent), "readtroffmetric:charent");
+ (*cp)->postfontid = RUNEGETGROUP(charnum);
+ (*cp)->postcharid = RUNEGETCHAR(charnum);
+ (*cp)->troffcharwidth = width;
+ (*cp)->name = galloc(0, strlen(stoken)+1, "readtroffmetric: char name");
+ (*cp)->next = 0;
+ strcpy((*cp)->name, stoken);
+ if (debug) {
+ if (specharflag)
+ Bprint(Bstderr, "%s", stoken);
+ else
+ Bputrune(Bstderr, troffchar);
+ Bprint(Bstderr, " %d %d 0x%x %s # psfontid=0x%x pscharid=0x%x thisfont=0x%x thischar=0x%x\n",
+ width, flag, charnum, str,
+ (*cp)->postfontid,
+ (*cp)->postcharid,
+ thisfont, thischar);
+ }
+ }
+ Bterm(Bfd);
+ Bflush(Bstderr);
+ return(1);
+}
+
+/* find index of troff font name in table
+ * returns -1 if name is not in table
+ * returns -2 if it cannot allocate memory
+ * returns -3 if there is a font mapping problem
+ * If insflg is not zero
+ * and the name is not found in the table, insert it.
+ */
+int
+findtfn(char *fontname, BOOLEAN insflg) {
+ struct troffont *tp;
+ int i, j;
+
+ if (debug) {
+ if (fontname==0) fprint(2, "findtfn(0x%x,%d)\n", fontname, insflg);
+ else fprint(2, "findtfn(%s,%d)\n", fontname, insflg);
+ }
+ for (i=0; i<troffontcnt; i++) {
+ if (troffontab[i].trfontid==0) {
+ error(WARNING, "findtfn:troffontab[%d].trfontid=0x%x, botch!\n",
+ i, troffontab[i].trfontid);
+ continue;
+ }
+ if (strcmp(troffontab[i].trfontid, fontname) == 0)
+ return(i);
+ }
+ if (insflg) {
+ tp = (struct troffont *)galloc(troffontab, sizeof(struct troffont)*(troffontcnt+1), "findtfn: struct troffont:");
+ if (tp == 0)
+ return(-2);
+ troffontab = tp;
+ tp = &(troffontab[troffontcnt]);
+ i = strlen(fontname);
+ tp->trfontid = galloc(0, i+1, "findtfn: trfontid:");
+
+ /* initialize new troff font entry with name and numeric fields to 0 */
+ strncpy(tp->trfontid, fontname, i);
+ tp->trfontid[i] = '\0';
+ tp->special = FALSE;
+ tp->spacewidth = 0;
+ tp->psfmapsize = 0;
+ tp->psfmap = 0;
+ for (i=0; i<NUMOFONTS; i++)
+ for (j=0; j<FONTSIZE; j++)
+ tp->charent[i][j] = 0;
+ troffontcnt++;
+ if (!readtroffmetric(fontname, troffontcnt-1))
+ return(-3);
+ if (!readpsfontdesc(fontname, troffontcnt-1))
+ return(-3);
+ return(troffontcnt-1);
+ }
+ return(-1);
+}
+
+void
+finish(void) {
+ int i;
+
+ Bprint(Bstdout, "%s", TRAILER);
+ Bprint(Bstdout, "done\n");
+ Bprint(Bstdout, "%s", DOCUMENTFONTS);
+
+ for (i=0; i<pfnamcnt; i++)
+ if (pfnafontmtab[i].used)
+ Bprint(Bstdout, " %s", pfnafontmtab[i].str);
+ Bprint(Bstdout, "\n");
+
+ Bprint(Bstdout, "%s %d\n", PAGES, pages_printed);
+
+}
+
+/* Set slant to n degrees. Disable slanting if n is 0. */
+void
+t_slant(int n) {
+ fontslant = n;
+ curpostfontid = -1;
+}
+
+/* Set character height to n points. Disabled if n is 0 or the current size. */
+
+void
+t_charht(int n) {
+ fontheight = (n == fontsize) ? 0 : n;
+ curpostfontid = -1;
+}
diff --git a/src/cmd/postscript/tr2post/conv.c b/src/cmd/postscript/tr2post/conv.c
new file mode 100644
index 00000000..4b7d97f6
--- /dev/null
+++ b/src/cmd/postscript/tr2post/conv.c
@@ -0,0 +1,100 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "../common/common.h"
+#include "tr2post.h"
+
+void
+conv(Biobuf *Bp) {
+ long c, n;
+ int r;
+ char special[10];
+ int save;
+
+ inputlineno = 1;
+ if (debug) Bprint(Bstderr, "conv(Biobuf *Bp=0x%x)\n", Bp);
+ while ((r = Bgetrune(Bp)) >= 0) {
+/* Bprint(Bstderr, "r=<%c>,0x%x\n", r, r); */
+/* Bflush(Bstderr); */
+ switch (r) {
+ case 's': /* set point size */
+ Bgetfield(Bp, 'd', &fontsize, 0);
+ break;
+ case 'f': /* set font to postion */
+ Bgetfield(Bp, 'd', &fontpos, 0);
+ save = inputlineno;
+ settrfont();
+ inputlineno = save; /* ugh */
+ break;
+ case 'c': /* print rune */
+ r = Bgetrune(Bp);
+ runeout(r);
+ break;
+ case 'C': /* print special character */
+ Bgetfield(Bp, 's', special, 10);
+ specialout(special);
+ break;
+ case 'N': /* print character with numeric value from current font */
+ Bgetfield(Bp, 'd', &n, 0);
+ break;
+ case 'H': /* go to absolute horizontal position */
+ Bgetfield(Bp, 'd', &n, 0);
+ hgoto(n);
+ break;
+ case 'V': /* go to absolute vertical position */
+ Bgetfield(Bp, 'd', &n, 0);
+ vgoto(n);
+ break;
+ case 'h': /* go to relative horizontal position */
+ Bgetfield(Bp, 'd', &n, 0);
+ hmot(n);
+ break;
+ case 'v': /* go to relative vertical position */
+ Bgetfield(Bp, 'd', &n, 0);
+ vmot(n);
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ /* move right nn units, then print character c */
+ n = (r - '0') * 10;
+ r = Bgetrune(Bp);
+ if (r < 0)
+ error(FATAL, "EOF or error reading input\n");
+ else if (r < '0' || r > '9')
+ error(FATAL, "integer expected\n");
+ n += r - '0';
+ r = Bgetrune(Bp);
+ hmot(n);
+ runeout(r);
+ break;
+ case 'p': /* begin page */
+ Bgetfield(Bp, 'd', &n, 0);
+ endpage();
+ startpage();
+ break;
+ case 'n': /* end of line (information only 'b a' follows) */
+ Brdline(Bp, '\n'); /* toss rest of line */
+ inputlineno++;
+ break;
+ case 'w': /* paddable word space (information only) */
+ break;
+ case 'D': /* graphics function */
+ draw(Bp);
+ break;
+ case 'x': /* device control functions */
+ devcntl(Bp);
+ break;
+ case '#': /* comment */
+ Brdline(Bp, '\n'); /* toss rest of line */
+ case '\n':
+ inputlineno++;
+ break;
+ default:
+ error(WARNING, "unknown troff function <%c>\n", r);
+ break;
+ }
+ }
+ endpage();
+ if (debug) Bprint(Bstderr, "r=0x%x\n", r);
+ if (debug) Bprint(Bstderr, "leaving conv\n");
+}
diff --git a/src/cmd/postscript/tr2post/devcntl.c b/src/cmd/postscript/tr2post/devcntl.c
new file mode 100644
index 00000000..8b438f76
--- /dev/null
+++ b/src/cmd/postscript/tr2post/devcntl.c
@@ -0,0 +1,178 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <stdio.h>
+#include "../common/common.h"
+#include "tr2post.h"
+
+char devname[20] = { 'u', 't', 'f', '\0' };
+int resolution;
+int minx, miny;
+
+struct sjt {
+ char *str;
+ void (*func)(void *);
+};
+
+/* I won't need this if getfields can replace sscanf
+
+extern void picture(Biobuf *);
+extern void notavail(char *);
+
+void
+PSInclude(Biobuf *inp) {
+ char buf[256];
+
+ Bgetfield(inp, 's', buf, 256);
+ if(pageon()) {
+ endstring();
+ Bprint(Bstdout, "%s\n", buf);
+ }
+}
+
+struct sjt specialjumptable[] = {
+ {"PI", picture},
+ {"PictureInclusion", picture},
+ {"InlinePicture", NULL},
+ {"BeginPath", NULL},
+ {"DrawPath", NULL},
+ {"BeginObject", NULL},
+ {"EndObject", NULL},
+ {"NewBaseline", NULL},
+ {"DrawText", NULL},
+ {"SetText", NULL},
+ {"SetColor", NULL},
+ {"INFO", NULL},
+ {"PS", PSInclude},
+ {"Postscript", PSInclude},
+ {"ExportPS", notavail("ExportPS")},
+ {NULL, NULL}
+};
+*/
+
+void
+devcntl(Biobuf *inp) {
+
+ char cmd[50], buf[256], str[MAXTOKENSIZE], *line;
+ int c, n, linelen;
+
+/*
+ *
+ * Interpret device control commands, ignoring any we don't recognize. The
+ * "x X ..." commands are a device dependent collection generated by troff's
+ * \X'...' request.
+ *
+ */
+
+ Bgetfield(inp, 's', cmd, 50);
+ if (debug) Bprint(Bstderr, "devcntl(cmd=%s)\n", cmd);
+ switch (cmd[0]) {
+ case 'f': /* mount font in a position */
+ Bgetfield(inp, 'd', &n, 0);
+ Bgetfield(inp, 's', str, 100);
+ mountfont(n, str);
+ break;
+
+ case 'i': /* initialize */
+ initialize();
+ break;
+
+ case 'p': /* pause */
+ break;
+
+ case 'r': /* resolution assumed when prepared */
+ Bgetfield(inp, 'd', &resolution, 0);
+ Bgetfield(inp, 'd', &minx, 0);
+ Bgetfield(inp, 'd', &miny, 0);
+ break;
+
+ case 's': /* stop */
+ case 't': /* trailer */
+ /* flushtext(); */
+ break;
+
+ case 'H': /* char height */
+ Bgetfield(inp, 'd', &n, 0);
+ t_charht(n);
+ break;
+
+ case 'S': /* slant */
+ Bgetfield(inp, 'd', &n, 0);
+ t_slant(n);
+ break;
+
+ case 'T': /* device name */
+ Bgetfield(inp, 's', &devname, 16);
+ if (debug) Bprint(Bstderr, "devname=%s\n", devname);
+ break;
+
+ case 'E': /* input encoding - not in troff yet */
+ Bgetfield(inp, 's', &str, 100);
+/* if ( strcmp(str, "UTF") == 0 )
+ reading = UTFENCODING;
+ else reading = ONEBYTE;
+ */
+ break;
+
+ case 'X': /* copy through - from troff */
+ if (Bgetfield(inp, 's', str, MAXTOKENSIZE-1) <= 0)
+ error(FATAL, "incomplete devcntl line\n");
+ if ((line = Brdline(inp, '\n')) == 0)
+ error(FATAL, "incomplete devcntl line\n");
+ strncpy(buf, line, Blinelen(inp)-1);
+ buf[Blinelen(inp)-1] = '\0';
+ Bungetc(inp);
+
+ if (strncmp(str, "PI", sizeof("PI")-1) == 0 || strncmp(str, "PictureInclusion", sizeof("PictureInclusion")-1) == 0) {
+ picture(inp, str);
+ } else if (strncmp(str, "InlinePicture", sizeof("InlinePicture")-1) == 0) {
+ error(FATAL, "InlinePicture not implemented yet.\n");
+/* inlinepic(inp, buf); */
+ } else if (strncmp(str, "BeginPath", sizeof("BeginPath")-1) == 0) {
+ beginpath(buf, FALSE);
+ } else if (strncmp(str, "DrawPath", sizeof("DrawPath")-1) == 0) {
+ drawpath(buf, FALSE);
+ } else if (strncmp(str, "BeginObject", sizeof("BeginObject")-1) == 0) {
+ beginpath(buf, TRUE);
+ } else if (strncmp(str, "EndObject", sizeof("EndObject")-1) == 0) {
+ drawpath(buf, TRUE);
+ } else if (strncmp(str, "NewBaseline", sizeof("NewBaseline")-1) == 0) {
+ error(FATAL, "NewBaseline not implemented yet.\n");
+/* newbaseline(buf); */
+ } else if (strncmp(str, "DrawText", sizeof("DrawText")-1) == 0) {
+ error(FATAL, "DrawText not implemented yet.\n");
+/* drawtext(buf); */
+ } else if (strncmp(str, "SetText", sizeof("SetText")-1) == 0) {
+ error(FATAL, "SetText not implemented yet.\n");
+/* settext(buf); */
+ } else if (strncmp(str, "SetColor", sizeof("SetColor")-1) == 0) {
+ error(FATAL, "SetColor not implemented yet.\n");
+/* newcolor(buf); */
+/* setcolor(); */
+ } else if (strncmp(str, "INFO", sizeof("INFO")-1) == 0) {
+ error(FATAL, "INFO not implemented yet.\n");
+/* flushtext(); */
+/* Bprint(outp, "%%INFO%s", buf); */
+ } else if (strncmp(str, "PS", sizeof("PS")-1) == 0 || strncmp(str, "PostScript", sizeof("PostScript")-1) == 0) {
+ if(pageon()) {
+ endstring();
+ Bprint(Bstdout, "%s\n", buf);
+ }
+ } else if (strncmp(str, "ExportPS", sizeof("ExportPS")-1) == 0) { /* dangerous!! */
+ error(FATAL, "ExportPS not implemented yet.\n");
+/* if (Bfildes(outp) == 1) { */
+/* restore(); */
+/* Bprint(outp, "%s", buf); */
+/* save(); */
+/* } */
+ }
+/* else
+ error(WARNING, "Unknown string <%s %s> after x X\n", str, buf);
+*/
+
+ break;
+ }
+ while ((c = Bgetc(inp)) != '\n' && c != Beof);
+ inputlineno++;
+}
+
diff --git a/src/cmd/postscript/tr2post/draw.c b/src/cmd/postscript/tr2post/draw.c
new file mode 100644
index 00000000..575ec884
--- /dev/null
+++ b/src/cmd/postscript/tr2post/draw.c
@@ -0,0 +1,342 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+#include "../common/common.h"
+#include "tr2post.h"
+
+BOOLEAN drawflag = FALSE;
+BOOLEAN inpath = FALSE; /* TRUE if we're putting pieces together */
+
+void
+cover(double x, double y) {
+}
+
+void
+drawspline(Biobuf *Bp, int flag) { /* flag!=1 connect end points */
+ int x[100], y[100];
+ int i, N;
+/*
+ *
+ * Spline drawing routine for Postscript printers. The complicated stuff is
+ * handled by procedure Ds, which should be defined in the library file. I've
+ * seen wrong implementations of troff's spline drawing, so fo the record I'll
+ * write down the parametric equations and the necessary conversions to Bezier
+ * cubic splines (as used in Postscript).
+ *
+ *
+ * Parametric equation (x coordinate only):
+ *
+ *
+ * (x2 - 2 * x1 + x0) 2 (x0 + x1)
+ * x = ------------------ * t + (x1 - x0) * t + ---------
+ * 2 2
+ *
+ *
+ * The coefficients in the Bezier cubic are,
+ *
+ *
+ * A = 0
+ * B = (x2 - 2 * x1 + x0) / 2
+ * C = x1 - x0
+ *
+ *
+ * while the current point is,
+ *
+ * current-point = (x0 + x1) / 2
+ *
+ * Using the relationships given in the Postscript manual (page 121) it's easy to
+ * see that the control points are given by,
+ *
+ *
+ * x0' = (x0 + 5 * x1) / 6
+ * x1' = (x2 + 5 * x1) / 6
+ * x2' = (x1 + x2) / 2
+ *
+ *
+ * where the primed variables are the ones used by curveto. The calculations
+ * shown above are done in procedure Ds using the coordinates set up in both
+ * the x[] and y[] arrays.
+ *
+ * A simple test of whether your spline drawing is correct would be to use cip
+ * to draw a spline and some tangent lines at appropriate points and then print
+ * the file.
+ *
+ */
+
+ for (N=2; N<sizeof(x)/sizeof(x[0]); N++)
+ if (Bgetfield(Bp, 'd', &x[N], 0)<=0 || Bgetfield(Bp, 'd', &y[N], 0)<=0)
+ break;
+
+ x[0] = x[1] = hpos;
+ y[0] = y[1] = vpos;
+
+ for (i = 1; i < N; i++) {
+ x[i+1] += x[i];
+ y[i+1] += y[i];
+ }
+
+ x[N] = x[N-1];
+ y[N] = y[N-1];
+
+ for (i = ((flag!=1)?0:1); i < ((flag!=1)?N-1:N-2); i++) {
+ endstring();
+ if (pageon())
+ Bprint(Bstdout, "%d %d %d %d %d %d Ds\n", x[i], y[i], x[i+1], y[i+1], x[i+2], y[i+2]);
+/* if (dobbox == TRUE) { /* could be better */
+/* cover((double)(x[i] + x[i+1])/2,(double)-(y[i] + y[i+1])/2);
+/* cover((double)x[i+1], (double)-y[i+1]);
+/* cover((double)(x[i+1] + x[i+2])/2, (double)-(y[i+1] + y[i+2])/2);
+/* }
+ */
+ }
+
+ hpos = x[N]; /* where troff expects to be */
+ vpos = y[N];
+}
+
+void
+draw(Biobuf *Bp) {
+
+ int r, x1, y1, x2, y2, i;
+ int d1, d2;
+
+ drawflag = TRUE;
+ r = Bgetrune(Bp);
+ switch(r) {
+ case 'l':
+ if (Bgetfield(Bp, 'd', &x1, 0)<=0 || Bgetfield(Bp, 'd', &y1, 0)<=0 || Bgetfield(Bp, 'r', &i, 0)<=0)
+ error(FATAL, "draw line function, destination coordinates not found.\n");
+
+ endstring();
+ if (pageon())
+ Bprint(Bstdout, "%d %d %d %d Dl\n", hpos, vpos, hpos+x1, vpos+y1);
+ hpos += x1;
+ vpos += y1;
+ break;
+ case 'c':
+ if (Bgetfield(Bp, 'd', &d1, 0)<=0)
+ error(FATAL, "draw circle function, diameter coordinates not found.\n");
+
+ endstring();
+ if (pageon())
+ Bprint(Bstdout, "%d %d %d %d De\n", hpos, vpos, d1, d1);
+ hpos += d1;
+ break;
+ case 'e':
+ if (Bgetfield(Bp, 'd', &d1, 0)<=0 || Bgetfield(Bp, 'd', &d2, 0)<=0)
+ error(FATAL, "draw ellipse function, diameter coordinates not found.\n");
+
+ endstring();
+ if (pageon())
+ Bprint(Bstdout, "%d %d %d %d De\n", hpos, vpos, d1, d2);
+ hpos += d1;
+ break;
+ case 'a':
+ if (Bgetfield(Bp, 'd', &x1, 0)<=0 || Bgetfield(Bp, 'd', &y1, 0)<=0 || Bgetfield(Bp, 'd', &x2, 0)<=0 || Bgetfield(Bp, 'd', &y2, 0)<=0)
+ error(FATAL, "draw arc function, coordinates not found.\n");
+
+ endstring();
+ if (pageon())
+ Bprint(Bstdout, "%d %d %d %d %d %d Da\n", hpos, vpos, x1, y1, x2, y2);
+ hpos += x1 + x2;
+ vpos += y1 + y2;
+ break;
+ case 'q':
+ drawspline(Bp, 1);
+ break;
+ case '~':
+ drawspline(Bp, 2);
+ break;
+ default:
+ error(FATAL, "unknown draw function <%c>\n", r);
+ break;
+ }
+}
+
+void
+beginpath(char *buf, int copy) {
+
+/*
+ * Called from devcntrl() whenever an "x X BeginPath" command is read. It's used
+ * to mark the start of a sequence of drawing commands that should be grouped
+ * together and treated as a single path. By default the drawing procedures in
+ * *drawfile treat each drawing command as a separate object, and usually start
+ * with a newpath (just as a precaution) and end with a stroke. The newpath and
+ * stroke isolate individual drawing commands and make it impossible to deal with
+ * composite objects. "x X BeginPath" can be used to mark the start of drawing
+ * commands that should be grouped together and treated as a single object, and
+ * part of what's done here ensures that the PostScript drawing commands defined
+ * in *drawfile skip the newpath and stroke, until after the next "x X DrawPath"
+ * command. At that point the path that's been built up can be manipulated in
+ * various ways (eg. filled and/or stroked with a different line width).
+ *
+ * Color selection is one of the options that's available in parsebuf(),
+ * so if we get here we add *colorfile to the output file before doing
+ * anything important.
+ *
+ */
+ if (inpath == FALSE) {
+ endstring();
+ /* getdraw(); */
+ /* getcolor(); */
+ Bprint(Bstdout, "gsave\n");
+ Bprint(Bstdout, "newpath\n");
+ Bprint(Bstdout, "%d %d m\n", hpos, vpos);
+ Bprint(Bstdout, "/inpath true def\n");
+ if ( copy == TRUE )
+ Bprint(Bstdout, "%s\n", buf);
+ inpath = TRUE;
+ }
+}
+
+static void parsebuf(char*);
+
+void
+drawpath(char *buf, int copy) {
+
+/*
+ *
+ * Called from devcntrl() whenever an "x X DrawPath" command is read. It marks the
+ * end of the path started by the last "x X BeginPath" command and uses whatever
+ * has been passed along in *buf to manipulate the path (eg. fill and/or stroke
+ * the path). Once that's been done the drawing procedures are restored to their
+ * default behavior in which each drawing command is treated as an isolated path.
+ * The new version (called after "x X DrawPath") has copy set to FALSE, and calls
+ * parsebuf() to figure out what goes in the output file. It's a feeble attempt
+ * to free users and preprocessors (like pic) from having to know PostScript. The
+ * comments in parsebuf() describe what's handled.
+ *
+ * In the early version a path was started with "x X BeginObject" and ended with
+ * "x X EndObject". In both cases *buf was just copied to the output file, and
+ * was expected to be legitimate PostScript that manipulated the current path.
+ * The old escape sequence will be supported for a while (for Ravi), and always
+ * call this routine with copy set to TRUE.
+ *
+ *
+ */
+
+ if ( inpath == TRUE ) {
+ if ( copy == TRUE )
+ Bprint(Bstdout, "%s\n", buf);
+ else
+ parsebuf(buf);
+ Bprint(Bstdout, "grestore\n");
+ Bprint(Bstdout, "/inpath false def\n");
+/* reset(); */
+ inpath = FALSE;
+ }
+}
+
+
+/*****************************************************************************/
+
+static void
+parsebuf(char *buf)
+{
+ char *p; /* usually the next token */
+ char *q;
+ int gsavelevel = 0; /* non-zero if we've done a gsave */
+
+/*
+ *
+ * Simple minded attempt at parsing the string that followed an "x X DrawPath"
+ * command. Everything not recognized here is simply ignored - there's absolutely
+ * no error checking and what was originally in buf is clobbered by strtok().
+ * A typical *buf might look like,
+ *
+ * gray .9 fill stroke
+ *
+ * to fill the current path with a gray level of .9 and follow that by stroking the
+ * outline of the path. Since unrecognized tokens are ignored the last example
+ * could also be written as,
+ *
+ * with gray .9 fill then stroke
+ *
+ * The "with" and "then" strings aren't recognized tokens and are simply discarded.
+ * The "stroke", "fill", and "wfill" force out appropriate PostScript code and are
+ * followed by a grestore. In otherwords changes to the grahics state (eg. a gray
+ * level or color) are reset to default values immediately after the stroke, fill,
+ * or wfill tokens. For now "fill" gets invokes PostScript's eofill operator and
+ * "wfill" calls fill (ie. the operator that uses the non-zero winding rule).
+ *
+ * The tokens that cause temporary changes to the graphics state are "gray" (for
+ * setting the gray level), "color" (for selecting a known color from the colordict
+ * dictionary defined in *colorfile), and "line" (for setting the line width). All
+ * three tokens can be extended since strncmp() makes the comparison. For example
+ * the strings "line" and "linewidth" accomplish the same thing. Colors are named
+ * (eg. "red"), but must be appropriately defined in *colorfile. For now all three
+ * tokens must be followed immediately by their single argument. The gray level
+ * (ie. the argument that follows "gray") should be a number between 0 and 1, with
+ * 0 for black and 1 for white.
+ *
+ * To pass straight PostScript through enclose the appropriate commands in double
+ * quotes. Straight PostScript is only bracketed by the outermost gsave/grestore
+ * pair (ie. the one from the initial "x X BeginPath") although that's probably
+ * a mistake. Suspect I may have to change the double quote delimiters.
+ *
+ */
+
+ for( ; p != nil ; p = q ) {
+ if( q = strchr(p, ' ') ) {
+ *q++ = '\0';
+ }
+
+ if ( gsavelevel == 0 ) {
+ Bprint(Bstdout, "gsave\n");
+ gsavelevel++;
+ }
+ if ( strcmp(p, "stroke") == 0 ) {
+ Bprint(Bstdout, "closepath stroke\ngrestore\n");
+ gsavelevel--;
+ } else if ( strcmp(p, "openstroke") == 0 ) {
+ Bprint(Bstdout, "stroke\ngrestore\n");
+ gsavelevel--;
+ } else if ( strcmp(p, "fill") == 0 ) {
+ Bprint(Bstdout, "eofill\ngrestore\n");
+ gsavelevel--;
+ } else if ( strcmp(p, "wfill") == 0 ) {
+ Bprint(Bstdout, "fill\ngrestore\n");
+ gsavelevel--;
+ } else if ( strcmp(p, "sfill") == 0 ) {
+ Bprint(Bstdout, "eofill\ngrestore\ngsave\nstroke\ngrestore\n");
+ gsavelevel--;
+ } else if ( strncmp(p, "gray", strlen("gray")) == 0 ) {
+ if( q ) {
+ p = q;
+ if ( q = strchr(p, ' ') )
+ *q++ = '\0';
+ Bprint(Bstdout, "%s setgray\n", p);
+ }
+ } else if ( strncmp(p, "color", strlen("color")) == 0 ) {
+ if( q ) {
+ p = q;
+ if ( q = strchr(p, ' ') )
+ *q++ = '\0';
+ Bprint(Bstdout, "/%s setcolor\n", p);
+ }
+ } else if ( strncmp(p, "line", strlen("line")) == 0 ) {
+ if( q ) {
+ p = q;
+ if ( q = strchr(p, ' ') )
+ *q++ = '\0';
+ Bprint(Bstdout, "%s resolution mul 2 div setlinewidth\n", p);
+ }
+ } else if ( strncmp(p, "reverse", strlen("reverse")) == 0 )
+ Bprint(Bstdout, "reversepath\n");
+ else if ( *p == '"' ) {
+ for ( ; gsavelevel > 0; gsavelevel-- )
+ Bprint(Bstdout, "grestore\n");
+ if ( q != nil )
+ *--q = ' ';
+ if ( (q = strchr(p, '"')) != nil ) {
+ *q++ = '\0';
+ Bprint(Bstdout, "%s\n", p);
+ }
+ }
+ }
+
+ for ( ; gsavelevel > 0; gsavelevel-- )
+ Bprint(Bstdout, "grestore\n");
+
+}
diff --git a/src/cmd/postscript/tr2post/mkfile b/src/cmd/postscript/tr2post/mkfile
new file mode 100644
index 00000000..e1e5aec2
--- /dev/null
+++ b/src/cmd/postscript/tr2post/mkfile
@@ -0,0 +1,36 @@
+<$PLAN9/src/mkhdr
+
+<../config
+
+COMMONDIR=../common
+
+SHORTLIB=bio 9
+TARG=tr2post
+
+OFILES=tr2post.$O\
+ chartab.$O\
+ Bgetfield.$O\
+ conv.$O\
+ utils.$O\
+ devcntl.$O\
+ draw.$O\
+ readDESC.$O\
+ ps_include.$O\
+ pictures.$O\
+ common.$O\
+
+HFILES=tr2post.h\
+ ps_include.h\
+ $COMMONDIR/common.h\
+ $COMMONDIR/comments.h\
+ $COMMONDIR/path.h\
+ $COMMONDIR/ext.h\
+
+BIN=$POSTBIN
+
+<$PLAN9/src/mkone
+
+CFLAGS=$CFLAGS -c -D'PROGRAMVERSION="0.1"' -D'DOROUND=1' -I$COMMONDIR
+
+%.$O: $COMMONDIR/%.c
+ $CC $CFLAGS $COMMONDIR/$stem.c
diff --git a/src/cmd/postscript/tr2post/pictures.c b/src/cmd/postscript/tr2post/pictures.c
new file mode 100644
index 00000000..a20b7ade
--- /dev/null
+++ b/src/cmd/postscript/tr2post/pictures.c
@@ -0,0 +1,295 @@
+/*
+ *
+ * PostScript picture inclusion routines. Support for managing in-line pictures
+ * has been added, and works in combination with the simple picpack pre-processor
+ * that's supplied with this package. An in-line picture begins with a special
+ * device control command that looks like,
+ *
+ * x X InlinPicture name size
+ *
+ * where name is the pathname of the original picture file and size is the number
+ * of bytes in the picture, which begins immediately on the next line. When dpost
+ * encounters the InlinePicture device control command inlinepic() is called and
+ * that routine appends the string name and the integer size to a temporary file
+ * (fp_pic) and then adds the next size bytes read from the current input file to
+ * file fp_pic. All in-line pictures are saved in fp_pic and located later using
+ * the name string and picture file size that separate pictures saved in fp_pic.
+ *
+ * When a picture request (ie. an "x X PI" command) is encountered picopen() is
+ * called and it first looks for the picture file in fp_pic. If it's found there
+ * the entire picture (ie. size bytes) is copied from fp_pic to a new temp file
+ * and that temp file is used as the picture file. If there's nothing in fp_pic
+ * or if the lookup failed the original route is taken.
+ *
+ * Support for in-line pictures is an attempt to address requirements, expressed
+ * by several organizations, of being able to store a document as a single file
+ * (usually troff input) that can then be sent through dpost and ultimately to
+ * a PostScript printer. The mechanism may help some users, but the are obvious
+ * disadvantages to this approach, and the original mechanism is the recommended
+ * approach! Perhaps the most important problem is that troff output, with in-line
+ * pictures included, doesn't fit the device independent language accepted by
+ * important post-processors (like proff) and that means you won't be able to
+ * reliably preview a packed file on your 5620 (or whatever).
+ *
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <stdio.h>
+#include "ext.h"
+#include "common.h"
+#include "tr2post.h"
+/* PostScript file structuring comments */
+#include "comments.h"
+/* general purpose definitions */
+/* #include "gen.h" */
+/* just for TEMPDIR definition */
+#include "path.h"
+/* external variable declarations */
+/* #include "ext.h" */
+
+Biobuf *bfp_pic = NULL;
+Biobuf *Bfp_pic;
+Biobuf *picopen(char *);
+
+#define MAXGETFIELDS 16
+char *fields[MAXGETFIELDS];
+int nfields;
+
+extern int devres, hpos, vpos;
+extern int picflag;
+
+/*****************************************************************************/
+
+void
+picture(Biobuf *inp, char *buf) {
+ int poffset; /* page offset */
+ int indent; /* indent */
+ int length; /* line length */
+ int totrap; /* distance to next trap */
+ char name[100]; /* picture file and page string */
+ char hwo[40], *p; /* height, width and offset strings */
+ char flags[20]; /* miscellaneous stuff */
+ int page = 1; /* page number pulled from name[] */
+ double frame[4]; /* height, width, y, and x offsets from hwo[] */
+ char units; /* scale indicator for frame dimensions */
+ int whiteout = 0; /* white out the box? */
+ int outline = 0; /* draw a box around the picture? */
+ int scaleboth = 0; /* scale both dimensions? */
+ double adjx = 0.5; /* left-right adjustment */
+ double adjy = 0.5; /* top-bottom adjustment */
+ double rot = 0; /* rotation in clockwise degrees */
+ Biobuf *fp_in; /* for *name */
+ int i; /* loop index */
+
+/*
+ *
+ * Called from devcntrl() after an 'x X PI' command is found. The syntax of that
+ * command is:
+ *
+ * x X PI:args
+ *
+ * with args separated by colons and given by:
+ *
+ * poffset
+ * indent
+ * length
+ * totrap
+ * file[(page)]
+ * height[,width[,yoffset[,xoffset]]]
+ * [flags]
+ *
+ * poffset, indent, length, and totrap are given in machine units. height, width,
+ * and offset refer to the picture frame in inches, unless they're followed by
+ * the u scale indicator. flags is a string that provides a little bit of control
+ * over the placement of the picture in the frame. Rotation of the picture, in
+ * clockwise degrees, is set by the a flag. If it's not followed by an angle
+ * the current rotation angle is incremented by 90 degrees, otherwise the angle
+ * is set by the number that immediately follows the a.
+ *
+ */
+
+ if (!picflag) /* skip it */
+ return;
+ endstring();
+
+ flags[0] = '\0'; /* just to be safe */
+
+ nfields = getfields(buf, fields, MAXGETFIELDS, 0, ":\n");
+ if (nfields < 6) {
+ error(WARNING, "too few arguments to specify picture");
+ return;
+ }
+ poffset = atoi(fields[1]);
+ indent = atoi(fields[2]);
+ length = atoi(fields[3]);
+ totrap = atoi(fields[4]);
+ strncpy(name, fields[5], sizeof(name));
+ strncpy(hwo, fields[6], sizeof(hwo));
+ if (nfields >= 6)
+ strncpy(flags, fields[7], sizeof(flags));
+
+ nfields = getfields(buf, fields, MAXGETFIELDS, 0, "()");
+ if (nfields == 2) {
+ strncpy(name, fields[0], sizeof(name));
+ page = atoi(fields[1]);
+ }
+
+ if ((fp_in = picopen(name)) == NULL) {
+ error(WARNING, "can't open picture file %s\n", name);
+ return;
+ }
+
+ frame[0] = frame[1] = -1; /* default frame height, width */
+ frame[2] = frame[3] = 0; /* and y and x offsets */
+
+ for (i = 0, p = hwo-1; i < 4 && p != NULL; i++, p = strchr(p, ','))
+ if (sscanf(++p, "%lf%c", &frame[i], &units) == 2)
+ if (units == 'i' || units == ',' || units == '\0')
+ frame[i] *= devres;
+
+ if (frame[0] <= 0) /* check what we got for height */
+ frame[0] = totrap;
+
+ if (frame[1] <= 0) /* and width - check too big?? */
+ frame[1] = length - indent;
+
+ frame[3] += poffset + indent; /* real x offset */
+
+ for (i = 0; flags[i]; i++)
+ switch (flags[i]) {
+ case 'c': adjx = adjy = 0.5; break; /* move to the center */
+ case 'l': adjx = 0; break; /* left */
+ case 'r': adjx = 1; break; /* right */
+ case 't': adjy = 1; break; /* top */
+ case 'b': adjy = 0; break; /* or bottom justify */
+ case 'o': outline = 1; break; /* outline the picture */
+ case 'w': whiteout = 1; break; /* white out the box */
+ case 's': scaleboth = 1; break; /* scale both dimensions */
+ case 'a': if ( sscanf(&flags[i+1], "%lf", &rot) != 1 )
+ rot += 90;
+ }
+
+ /* restore(); */
+ endstring();
+ Bprint(Bstdout, "cleartomark\n");
+ Bprint(Bstdout, "saveobj restore\n");
+
+ ps_include(fp_in, Bstdout, page, whiteout, outline, scaleboth,
+ frame[3]+frame[1]/2, -vpos-frame[2]-frame[0]/2, frame[1], frame[0], adjx, adjy, -rot);
+ /* save(); */
+ Bprint(Bstdout, "/saveobj save def\n");
+ Bprint(Bstdout, "mark\n");
+ Bterm(fp_in);
+
+}
+
+/*
+ *
+ * Responsible for finding and opening the next picture file. If we've accumulated
+ * any in-line pictures fp_pic won't be NULL and we'll look there first. If *path
+ * is found in *fp_pic we create another temp file, open it for update, unlink it,
+ * copy in the picture, seek back to the start of the new temp file, and return
+ * the file pointer to the caller. If fp_pic is NULL or the lookup fails we just
+ * open file *path and return the resulting file pointer to the caller.
+ *
+ */
+Biobuf *
+picopen(char *path) {
+/* char name[100]; /* pathnames */
+/* long pos; /* current position */
+/* long total; /* and sizes - from *fp_pic */
+ Biobuf *bfp;
+ Biobuf *Bfp; /* and pointer for the new temp file */
+
+
+ if ((bfp = Bopen(path, OREAD)) == 0)
+ error(FATAL, "can't open %s\n", path);
+ Bfp = bfp;
+ return(Bfp);
+#ifdef UNDEF
+ if (Bfp_pic != NULL) {
+ Bseek(Bfp_pic, 0L, 0);
+ while (Bgetfield(Bfp_pic, 's', name, 99)>0
+ && Bgetfield(Bfp_pic, 'd', &total, 0)>0) {
+ pos = Bseek(Bfp_pic, 0L, 1);
+ if (strcmp(path, name) == 0) {
+ if (tmpnam(pictmpname) == NULL)
+ error(FATAL, "can't generate temp file name");
+ if ( (bfp = Bopen(pictmpname, ORDWR)) == NULL )
+ error(FATAL, "can't open %s", pictmpname);
+ Bfp = bfp;
+ piccopy(Bfp_pic, Bfp, total);
+ Bseek(Bfp, 0L, 0);
+ return(Bfp);
+ }
+ Bseek(Bfp_pic, total+pos, 0);
+ }
+ }
+ if ((bfp = Bopen(path, OREAD)) == 0)
+ Bfp = 0;
+ else
+ Bfp = bfp;
+ return(Bfp);
+#endif
+}
+
+/*
+ *
+ * Adds an in-line picture file to the end of temporary file *Bfp_pic. All pictures
+ * grabbed from the input file are saved in the same temp file. Each is preceeded
+ * by a one line header that includes the original picture file pathname and the
+ * size of the picture in bytes. The in-line picture file is opened for update,
+ * left open, and unlinked so it disappears when we do.
+ *
+ */
+/* *fp; /* current input file */
+/* *buf; /* whatever followed "x X InlinePicture" */
+
+#ifdef UNDEF
+void
+inlinepic(Biobuf *Bfp, char *buf) {
+ char name[100]; /* picture file pathname */
+ long total; /* and size - both from *buf */
+
+
+ if (Bfp_pic == NULL ) {
+ tmpnam(pictmpname);
+ if ((bfp_pic = Bopen(pictmpname, ORDWR)) == 0)
+ error(FATAL, "can't open in-line picture file %s", ipictmpname);
+ unlink(pictmpname);
+ }
+
+ if ( sscanf(buf, "%s %ld", name, &total) != 2 )
+ error(FATAL, "in-line picture error");
+
+ fseek(Bfp_pic, 0L, 2);
+ fprintf(Bfp_pic, "%s %ld\n", name, total);
+ getc(fp);
+ fflush(fp_pic);
+ piccopy(fp, fp_pic, total);
+ ungetc('\n', fp);
+
+}
+#endif
+
+/*
+ *
+ * Copies total bytes from file fp_in to fp_out. Used to append picture files to
+ * *fp_pic and then copy them to yet another temporary file immediately before
+ * they're used (in picture()).
+ *
+ */
+/* *fp_in; input */
+/* *fp_out; and output file pointers */
+/* total; number of bytes to be copied */
+void
+piccopy(Biobuf *Bfp_in, Biobuf *Bfp_out, long total) {
+ long i;
+
+ for (i = 0; i < total; i++)
+ if (Bputc(Bfp_out, Bgetc(Bfp_in)) < 0)
+ error(FATAL, "error copying in-line picture file");
+ Bflush(Bfp_out);
+}
diff --git a/src/cmd/postscript/tr2post/ps_include.c b/src/cmd/postscript/tr2post/ps_include.c
new file mode 100644
index 00000000..af30ff21
--- /dev/null
+++ b/src/cmd/postscript/tr2post/ps_include.c
@@ -0,0 +1,191 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <stdio.h>
+#include "../common/common.h"
+#include "ps_include.h"
+
+extern int curpostfontid;
+extern int curfontsize;
+
+typedef struct {long start, end;} Section;
+static char *buf;
+
+static void
+copy(Biobuf *fin, Biobuf *fout, Section *s) {
+ int cond;
+ if (s->end <= s->start)
+ return;
+ Bseek(fin, s->start, 0);
+ while (Bseek(fin, 0L, 1) < s->end && (buf=Brdline(fin, '\n')) != NULL){
+ /*
+ * We have to be careful here, because % can legitimately appear
+ * in Ascii85 encodings, and must not be elided.
+ * The goal here is to make any DSC comments impotent without
+ * actually changing the behavior of the Postscript.
+ * Since stripping ``comments'' breaks Ascii85, we can instead just
+ * indent comments a space, which turns DSC comments into non-DSC comments
+ * and has no effect on binary encodings, which are whitespace-blind.
+ */
+ if(buf[0] == '%')
+ Bputc(fout, ' ');
+ Bwrite(fout, buf, Blinelen(fin));
+ }
+}
+
+/*
+ *
+ * Reads a PostScript file (*fin), and uses structuring comments to locate the
+ * prologue, trailer, global definitions, and the requested page. After the whole
+ * file is scanned, the special ps_include PostScript definitions are copied to
+ * *fout, followed by the prologue, global definitions, the requested page, and
+ * the trailer. Before returning the initial environment (saved in PS_head) is
+ * restored.
+ *
+ * By default we assume the picture is 8.5 by 11 inches, but the BoundingBox
+ * comment, if found, takes precedence.
+ *
+ */
+/* *fin, *fout; /* input and output files */
+/* page_no; /* physical page number from *fin */
+/* whiteout; /* erase picture area */
+/* outline; /* draw a box around it and */
+/* scaleboth; /* scale both dimensions - if not zero */
+/* cx, cy; /* center of the picture and */
+/* sx, sy; /* its size - in current coordinates */
+/* ax, ay; /* left-right, up-down adjustment */
+/* rot; /* rotation - in clockwise degrees */
+
+void
+ps_include(Biobuf *fin, Biobuf *fout, int page_no, int whiteout,
+ int outline, int scaleboth, double cx, double cy, double sx, double sy,
+ double ax, double ay, double rot) {
+ char **strp;
+ int foundpage = 0; /* found the page when non zero */
+ int foundpbox = 0; /* found the page bounding box */
+ int nglobal = 0; /* number of global defs so far */
+ int maxglobal = 0; /* and the number we've got room for */
+ Section prolog, page, trailer; /* prologue, page, and trailer offsets */
+ Section *global; /* offsets for all global definitions */
+ double llx, lly; /* lower left and */
+ double urx, ury; /* upper right corners - default coords */
+ double w = whiteout != 0; /* mostly for the var() macro */
+ double o = outline != 0;
+ double s = scaleboth != 0;
+ int i; /* loop index */
+
+#define has(word) (strncmp(buf, word, strlen(word)) == 0)
+#define grab(n) ((Section *)(nglobal \
+ ? realloc((char *)global, n*sizeof(Section)) \
+ : calloc(n, sizeof(Section))))
+
+ llx = lly = 0; /* default BoundingBox - 8.5x11 inches */
+ urx = 72 * 8.5;
+ ury = 72 * 11.0;
+
+ /* section boundaries and bounding box */
+
+ prolog.start = prolog.end = 0;
+ page.start = page.end = 0;
+ trailer.start = 0;
+ Bseek(fin, 0L, 0);
+
+ while ((buf=Brdline(fin, '\n')) != NULL) {
+ buf[Blinelen(fin)-1] = '\0';
+ if (!has("%%"))
+ continue;
+ else if (has("%%Page: ")) {
+ if (!foundpage)
+ page.start = Bseek(fin, 0L, 1);
+ sscanf(buf, "%*s %*s %d", &i);
+ if (i == page_no)
+ foundpage = 1;
+ else if (foundpage && page.end <= page.start)
+ page.end = Bseek(fin, 0L, 1);
+ } else if (has("%%EndPage: ")) {
+ sscanf(buf, "%*s %*s %d", &i);
+ if (i == page_no) {
+ foundpage = 1;
+ page.end = Bseek(fin, 0L, 1);
+ }
+ if (!foundpage)
+ page.start = Bseek(fin, 0L, 1);
+ } else if (has("%%PageBoundingBox: ")) {
+ if (i == page_no) {
+ foundpbox = 1;
+ sscanf(buf, "%*s %lf %lf %lf %lf",
+ &llx, &lly, &urx, &ury);
+ }
+ } else if (has("%%BoundingBox: ")) {
+ if (!foundpbox)
+ sscanf(buf,"%*s %lf %lf %lf %lf",
+ &llx, &lly, &urx, &ury);
+ } else if (has("%%EndProlog") || has("%%EndSetup") || has("%%EndDocumentSetup"))
+ prolog.end = page.start = Bseek(fin, 0L, 1);
+ else if (has("%%Trailer"))
+ trailer.start = Bseek(fin, 0L, 1);
+ else if (has("%%BeginGlobal")) {
+ if (page.end <= page.start) {
+ if (nglobal >= maxglobal) {
+ maxglobal += 20;
+ global = grab(maxglobal);
+ }
+ global[nglobal].start = Bseek(fin, 0L, 1);
+ }
+ } else if (has("%%EndGlobal"))
+ if (page.end <= page.start)
+ global[nglobal++].end = Bseek(fin, 0L, 1);
+ }
+ Bseek(fin, 0L, 2);
+ if (trailer.start == 0)
+ trailer.start = Bseek(fin, 0L, 1);
+ trailer.end = Bseek(fin, 0L, 1);
+
+ if (page.end <= page.start)
+ page.end = trailer.start;
+
+/*
+fprint(2, "prolog=(%d,%d)\n", prolog.start, prolog.end);
+fprint(2, "page=(%d,%d)\n", page.start, page.end);
+for(i = 0; i < nglobal; i++)
+ fprint(2, "global[%d]=(%d,%d)\n", i, global[i].start, global[i].end);
+fprint(2, "trailer=(%d,%d)\n", trailer.start, trailer.end);
+*/
+
+ /* all output here */
+ for (strp = PS_head; *strp != NULL; strp++)
+ Bwrite(fout, *strp, strlen(*strp));
+
+ Bprint(fout, "/llx %g def\n", llx);
+ Bprint(fout, "/lly %g def\n", lly);
+ Bprint(fout, "/urx %g def\n", urx);
+ Bprint(fout, "/ury %g def\n", ury);
+ Bprint(fout, "/w %g def\n", w);
+ Bprint(fout, "/o %g def\n", o);
+ Bprint(fout, "/s %g def\n", s);
+ Bprint(fout, "/cx %g def\n", cx);
+ Bprint(fout, "/cy %g def\n", cy);
+ Bprint(fout, "/sx %g def\n", sx);
+ Bprint(fout, "/sy %g def\n", sy);
+ Bprint(fout, "/ax %g def\n", ax);
+ Bprint(fout, "/ay %g def\n", ay);
+ Bprint(fout, "/rot %g def\n", rot);
+
+ for (strp = PS_setup; *strp != NULL; strp++)
+ Bwrite(fout, *strp, strlen(*strp));
+
+ copy(fin, fout, &prolog);
+ for(i = 0; i < nglobal; i++)
+ copy(fin, fout, &global[i]);
+ copy(fin, fout, &page);
+ copy(fin, fout, &trailer);
+ for (strp = PS_tail; *strp != NULL; strp++)
+ Bwrite(fout, *strp, strlen(*strp));
+
+ if(nglobal)
+ free(global);
+
+ /* force the program to reestablish its state */
+ curpostfontid = -1;
+ curfontsize = -1;
+}
diff --git a/src/cmd/postscript/tr2post/ps_include.h b/src/cmd/postscript/tr2post/ps_include.h
new file mode 100644
index 00000000..cef49050
--- /dev/null
+++ b/src/cmd/postscript/tr2post/ps_include.h
@@ -0,0 +1,66 @@
+static char *PS_head[] = {
+ "%ps_include: begin\n",
+ "save\n",
+ "/ed {exch def} def\n",
+ "{} /showpage ed\n",
+ "{} /copypage ed\n",
+ "{} /erasepage ed\n",
+ "{} /letter ed\n",
+ "currentdict /findfont known systemdict /findfont known and {\n",
+ " /findfont systemdict /findfont get def\n",
+ "} if\n",
+ "36 dict dup /PS-include-dict-dw ed begin\n",
+ "/context ed\n",
+ "count array astore /o-stack ed\n",
+ "%ps_include: variables begin\n",
+ 0
+};
+
+static char *PS_setup[] = {
+ "%ps_include: variables end\n",
+ "{llx lly urx ury} /bbox ed\n",
+ "{newpath 2 index exch 2 index exch dup 6 index exch\n",
+ " moveto 3 {lineto} repeat closepath} /boxpath ed\n",
+ "{dup mul exch dup mul add sqrt} /len ed\n",
+ "{2 copy gt {exch} if pop} /min ed\n",
+ "{2 copy lt {exch} if pop} /max ed\n",
+ "{transform round exch round exch A itransform} /nice ed\n",
+ "{6 array} /n ed\n",
+ "n defaultmatrix n currentmatrix n invertmatrix n concatmatrix /A ed\n",
+ "urx llx sub 0 A dtransform len /Sx ed\n",
+ "0 ury lly sub A dtransform len /Sy ed\n",
+ "llx urx add 2 div lly ury add 2 div A transform /Cy ed /Cx ed\n",
+ "rot dup sin abs /S ed cos abs /C ed\n",
+ "Sx S mul Sy C mul add /H ed\n",
+ "Sx C mul Sy S mul add /W ed\n",
+ "sy H div /Scaley ed\n",
+ "sx W div /Scalex ed\n",
+ "s 0 eq {Scalex Scaley min dup /Scalex ed /Scaley ed} if\n",
+ "sx Scalex W mul sub 0 max ax 0.5 sub mul cx add /cx ed\n",
+ "sy Scaley H mul sub 0 max ay 0.5 sub mul cy add /cy ed\n",
+ "urx llx sub 0 A dtransform exch atan rot exch sub /rot ed\n",
+ "n currentmatrix initgraphics setmatrix\n",
+ "cx cy translate\n",
+ "Scalex Scaley scale\n",
+ "rot rotate\n",
+ "Cx neg Cy neg translate\n",
+ "A concat\n",
+ "bbox boxpath clip newpath\n",
+ "w 0 ne {gsave bbox boxpath 1 setgray fill grestore} if\n",
+ "end\n",
+ "gsave\n",
+ "%ps_include: inclusion begin\n",
+ 0
+};
+
+static char *PS_tail[] = {
+ "%ps_include: inclusion end\n",
+ "grestore\n",
+ "PS-include-dict-dw begin\n",
+ "o 0 ne {gsave A defaultmatrix /A ed llx lly nice urx ury nice\n",
+ " initgraphics 0.1 setlinewidth boxpath stroke grestore} if\n",
+ "clear o-stack aload pop\n",
+ "context end restore\n",
+ "%ps_include: end\n",
+ 0
+};
diff --git a/src/cmd/postscript/tr2post/readDESC.c b/src/cmd/postscript/tr2post/readDESC.c
new file mode 100644
index 00000000..03b8b64d
--- /dev/null
+++ b/src/cmd/postscript/tr2post/readDESC.c
@@ -0,0 +1,139 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+#include "common.h"
+#include "tr2post.h"
+#include "comments.h"
+#include "path.h"
+
+char *printdesclang = 0;
+char *encoding = 0;
+int devres;
+int unitwidth;
+int nspechars = 0;
+struct charent spechars[MAXSPECHARS];
+
+#define NDESCTOKS 9
+static char *desctoks[NDESCTOKS] = {
+ "PDL",
+ "Encoding",
+ "fonts",
+ "sizes",
+ "res",
+ "hor",
+ "vert",
+ "unitwidth",
+ "charset"
+};
+
+char *spechar[MAXSPECHARS];
+
+int
+hash(char *s, int l) {
+ unsigned i;
+
+ for (i=0; *s; s++)
+ i = i*10 + *s;
+ return(i % l);
+}
+
+BOOLEAN
+readDESC(void) {
+ char token[MAXTOKENSIZE];
+ char *descnameformat = "%s/dev%s/DESC";
+ char *descfilename = 0;
+ Biobuf *bfd;
+ Biobuf *Bfd;
+ int i, state = -1;
+ int fontindex = 0;
+
+ if (debug) Bprint(Bstderr, "readDESC()\n");
+ descfilename = galloc(descfilename, strlen(descnameformat)+strlen(FONTDIR)
+ +strlen(devname)+1, "readdesc");
+ sprint(descfilename, descnameformat, FONTDIR, devname);
+ if ((bfd = Bopen(unsharp(descfilename), OREAD)) == 0) {
+ error(WARNING, "cannot open file %s\n", descfilename);
+ return(0);
+ }
+ Bfd = bfd;
+
+ while (Bgetfield(Bfd, 's', token, MAXTOKENSIZE) > 0) {
+ for (i=0; i<NDESCTOKS; i++) {
+ if (strcmp(desctoks[i], token) == 0) {
+ state = i;
+ break;
+ }
+ }
+ if (i<NDESCTOKS) continue;
+ switch (state) {
+ case 0:
+ printdesclang=galloc(printdesclang, strlen(token)+1, "readdesc:");
+ strcpy(printdesclang, token);
+ if (debug) Bprint(Bstderr, "PDL %s\n", token);
+ break;
+ case 1:
+ encoding=galloc(encoding, strlen(token)+1, "readdesc:");
+ strcpy(encoding, token);
+ if (debug) Bprint(Bstderr, "encoding %s\n", token);
+ break;
+ case 2:
+ if (fontmnt <=0) {
+ if (!isdigit(*token)) {
+ error(WARNING, "readdesc: expecting number of fonts in mount table.\n");
+ return(FALSE);
+ }
+ fontmnt = atoi(token) + 1;
+ fontmtab = galloc(fontmtab, fontmnt*sizeof(char *), "readdesc:");
+
+ for (i=0; i<fontmnt; i++)
+ fontmtab[i] = 0;
+ fontindex = 0;
+ } else {
+ mountfont(++fontindex, token);
+ findtfn(token, TRUE);
+ }
+ break;
+ case 3:
+ /* I don't really care about sizes */
+ break;
+ case 4:
+ /* device resolution in dots per inch */
+ if (!isdigit(*token)) {
+ error(WARNING, "readdesc: expecting device resolution.\n");
+ return(FALSE);
+ }
+ devres = atoi(token);
+ if (debug) Bprint(Bstderr, "res %d\n", devres);
+ break;
+ case 5:
+ /* I don't really care about horizontal motion resolution */
+ if (debug) Bprint(Bstderr, "ignoring horizontal resolution\n");
+ break;
+ case 6:
+ /* I don't really care about vertical motion resolution */
+ if (debug) Bprint(Bstderr, "ignoring vertical resolution\n");
+ break;
+ case 7:
+ /* unitwidth is the font size at which the character widths are 1:1 */
+ if (!isdigit(*token)) {
+ error(WARNING, "readdesc: expecting unitwidth.\n");
+ return(FALSE);
+ }
+ unitwidth = atoi(token);
+ if (debug) Bprint(Bstderr, "unitwidth %d\n", unitwidth);
+ break;
+ case 8:
+ /* I don't really care about this list of special characters */
+ if (debug) Bprint(Bstderr, "ignoring special character <%s>\n", token);
+ break;
+ default:
+ if (*token == '#')
+ Brdline(Bfd, '\n');
+ else
+ error(WARNING, "unknown token %s in DESC file.\n", token);
+ break;
+ }
+ }
+ Bterm(Bfd);
+}
diff --git a/src/cmd/postscript/tr2post/shell.lib b/src/cmd/postscript/tr2post/shell.lib
new file mode 100644
index 00000000..d96e6565
--- /dev/null
+++ b/src/cmd/postscript/tr2post/shell.lib
@@ -0,0 +1,1238 @@
+#
+# Shell library - for building devutf tables.
+#
+
+RESOLUTION=720
+UNITWIDTH=10
+
+OCTALESCAPES=${OCTALESCAPES:-160} # <= code means add \0ddd names
+DOWNLOADVECTOR=FALSE # TRUE can mean incomplete tables
+
+#
+# BuiltinTables returns command lines that generate PostScript programs
+# for building a typesetter description file and font width tables for
+# a relatively standard collection of fonts. Use awk to select a command
+# line or modify an existing command to build a width table for a new
+# font.
+#
+
+BuiltinTables() {
+ cat <<-'//End of BuiltinTables'
+ Proportional R Times-Roman
+ Proportional I Times-Italic
+ Proportional B Times-Bold
+ Proportional BI Times-BoldItalic
+ Proportional AB AvantGarde-Demi
+ Proportional AI AvantGarde-BookOblique
+ Proportional AR AvantGarde-Book
+ Proportional AX AvantGarde-DemiOblique
+ Proportional H Helvetica
+ Proportional HB Helvetica-Bold
+ Proportional HI Helvetica-Oblique
+ Proportional HX Helvetica-BoldOblique
+ Proportional Hb Helvetica-Narrow-Bold
+ Proportional Hi Helvetica-Narrow-Oblique
+ Proportional Hr Helvetica-Narrow
+ Proportional Hx Helvetica-Narrow-BoldOblique
+ Proportional KB Bookman-Demi
+ Proportional KI Bookman-LightItalic
+ Proportional KR Bookman-Light
+ Proportional KX Bookman-DemiItalic
+ Proportional NB NewCenturySchlbk-Bold
+ Proportional NI NewCenturySchlbk-Italic
+ Proportional NR NewCenturySchlbk-Roman
+ Proportional NX NewCenturySchlbk-BoldItalic
+ Proportional PA Palatino-Roman
+ Proportional PB Palatino-Bold
+ Proportional PI Palatino-Italic
+ Proportional PX Palatino-BoldItalic
+ Proportional ZI ZapfChancery-MediumItalic
+ FixedWidth C Courier
+ FixedWidth CB Courier-Bold
+ FixedWidth CI Courier-Oblique
+ FixedWidth CO Courier
+ FixedWidth CW Courier
+ FixedWidth CX Courier-BoldOblique
+ Dingbats ZD ZapfDingbats
+ Greek GR Symbol
+ Symbol S Symbol
+ Special S1 Times-Roman
+ Description DESC ---
+ //End of BuiltinTables
+}
+
+#
+# AllTables prints the complete list of builtin font names.
+#
+
+AllTables() {
+ BuiltinTables | awk '{print $2}'
+}
+
+#
+# Charset functions generate keyword/value pairs (as PostScript objects)
+# that describe the character set available in a font. The keyword is a
+# PostScript string that represents troff's name for the character. The
+# value is usually the literal name (i.e. begins with a /) assigned to
+# the character in the PostScript font. The value can also be an integer
+# or a PostScript string. An integer value is used as an index in the
+# current font's Encoding array. A string value is returned to the host
+# unchanged when the entry for the character is constructed. Entries that
+# have (") as their value are synonyms for the preceeding character.
+#
+# The 18 characters missing from ROM resident fonts on older printers are
+# flagged with the PostScript comment "% missing".
+#
+
+StandardCharset() {
+ cat <<-'//End of StandardCharset'
+ (!) /exclam
+ (") /quotedbl
+ (dq) (") % synonym
+ (#) /numbersign
+ ($) /dollar
+ (%) /percent
+ (&) /ampersand
+ (') /quoteright
+ (\() /parenleft
+ (\)) /parenright
+ (*) /asterisk
+ (+) /plus
+ (,) /comma
+ (-) /hyphen % changed from minus by request
+ (.) /period
+ (/) /slash
+ (0) /zero
+ (1) /one
+ (2) /two
+ (3) /three
+ (4) /four
+ (5) /five
+ (6) /six
+ (7) /seven
+ (8) /eight
+ (9) /nine
+ (:) /colon
+ (;) /semicolon
+ (<) /less
+ (=) /equal
+ (>) /greater
+ (?) /question
+ (@) /at
+ (A) /A
+ (B) /B
+ (C) /C
+ (D) /D
+ (E) /E
+ (F) /F
+ (G) /G
+ (H) /H
+ (I) /I
+ (J) /J
+ (K) /K
+ (L) /L
+ (M) /M
+ (N) /N
+ (O) /O
+ (P) /P
+ (Q) /Q
+ (R) /R
+ (S) /S
+ (T) /T
+ (U) /U
+ (V) /V
+ (W) /W
+ (X) /X
+ (Y) /Y
+ (Z) /Z
+ ([) /bracketleft
+ (\\) /backslash
+ (bs) (") % synonym
+ (]) /bracketright
+ (^) /asciicircum
+ (_) /underscore
+ (`) /quoteleft
+ (a) /a
+ (b) /b
+ (c) /c
+ (d) /d
+ (e) /e
+ (f) /f
+ (g) /g
+ (h) /h
+ (i) /i
+ (j) /j
+ (k) /k
+ (l) /l
+ (m) /m
+ (n) /n
+ (o) /o
+ (p) /p
+ (q) /q
+ (r) /r
+ (s) /s
+ (t) /t
+ (u) /u
+ (v) /v
+ (w) /w
+ (x) /x
+ (y) /y
+ (z) /z
+ ({) /braceleft
+ (|) /bar
+ (}) /braceright
+ (~) /asciitilde
+ (\\`) /grave % devpost character
+ (ga) (") % synonym
+ (!!) /exclamdown
+ (c|) /cent
+ (ct) (") % devpost synonym
+ (L-) /sterling
+ (ps) (") % devpost synonym
+ (xo) /currency
+ (cr) (") % devpost synonym
+ (Y-) /yen
+ (yn) (") % devpost synonym
+ (||) /brokenbar % missing
+ (so) /section
+ (sc) (") % devpost synonym
+ ("") /dieresis
+ (:a) (") % devpost synonym
+ (co) /copyright
+ (a_) /ordfeminine
+ (<<) /guillemotleft
+ (-,) /logicalnot
+ (hy) /hyphen
+ (--) /minus
+ (ro) /registered
+ (rg) (") % devpost synonym
+ (-^) /macron
+ (-a) (") % devpost synonym
+ (0^) /degree % missing
+ (+-) /plusminus % missing
+ (2^) /twosuperior % missing
+ (3^) /threesuperior % missing
+ (\\') /acute
+ (aa) (") % devpost synonym
+ (/u) /mu % missing
+ (P!) /paragraph
+ (pg) (") % devpost synonym
+ (.^) /periodcentered
+ (,,) /cedilla
+ (,a) (") % devpost synonym
+ (1^) /onesuperior % missing
+ (o_) /ordmasculine
+ (>>) /guillemotright
+ (14) /onequarter % missing
+ (12) /onehalf % missing
+ (34) /threequarters % missing
+ (??) /questiondown
+ (A`) /Agrave
+ (A') /Aacute
+ (A^) /Acircumflex
+ (A~) /Atilde
+ (A") /Adieresis
+ (A*) /Aring
+ (AE) /AE
+ (C,) /Ccedilla
+ (E`) /Egrave
+ (E') /Eacute
+ (E^) /Ecircumflex
+ (E") /Edieresis
+ (I`) /Igrave
+ (I') /Iacute
+ (I^) /Icircumflex
+ (I") /Idieresis
+ (D-) /Eth % missing
+ (N~) /Ntilde
+ (O`) /Ograve
+ (O') /Oacute
+ (O^) /Ocircumflex
+ (O~) /Otilde
+ (O") /Odieresis
+ (xx) /multiply % missing
+ (O/) /Oslash
+ (U`) /Ugrave
+ (U') /Uacute
+ (U^) /Ucircumflex
+ (U") /Udieresis
+ (Y') /Yacute % missing
+ (TH) /Thorn % missing
+ (ss) /germandbls
+ (a`) /agrave
+ (a') /aacute
+ (a^) /acircumflex
+ (a~) /atilde
+ (a") /adieresis
+ (a*) /aring
+ (ae) /ae
+ (c,) /ccedilla
+ (e`) /egrave
+ (e') /eacute
+ (e^) /ecircumflex
+ (e") /edieresis
+ (i`) /igrave
+ (i') /iacute
+ (i^) /icircumflex
+ (i") /idieresis
+ (d-) /eth % missing
+ (n~) /ntilde
+ (o`) /ograve
+ (o') /oacute
+ (o^) /ocircumflex
+ (o~) /otilde
+ (o") /odieresis
+ (-:) /divide % missing
+ (o/) /oslash
+ (u`) /ugrave
+ (u') /uacute
+ (u^) /ucircumflex
+ (u") /udieresis
+ (y') /yacute % missing
+ (th) /thorn % missing
+ (y") /ydieresis
+ (^a) /circumflex % devpost accent
+ (~a) /tilde % devpost accent
+ (Ua) /breve % devpost accent
+ (.a) /dotaccent % devpost accent
+ (oa) /ring % devpost accent
+ ("a) /hungarumlaut % devpost accent
+ (Ca) /ogonek % devpost accent
+ (va) /caron % devpost accent
+ //End of StandardCharset
+}
+
+#
+# DingbatsCharset guarantees changes in StandardCharset don't show up in ZD.
+#
+
+DingbatsCharset() {
+ cat <<-'//End of DingbatsCharset'
+ (!) /exclam
+ (") /quotedbl
+ (#) /numbersign
+ ($) /dollar
+ (%) /percent
+ (&) /ampersand
+ (') /quoteright
+ (\() /parenleft
+ (\)) /parenright
+ (*) /asterisk
+ (+) /plus
+ (,) /comma
+ (-) /minus % also hyphen in devpost
+ (.) /period
+ (/) /slash
+ (0) /zero
+ (1) /one
+ (2) /two
+ (3) /three
+ (4) /four
+ (5) /five
+ (6) /six
+ (7) /seven
+ (8) /eight
+ (9) /nine
+ (:) /colon
+ (;) /semicolon
+ (<) /less
+ (=) /equal
+ (>) /greater
+ (?) /question
+ (@) /at
+ (A) /A
+ (B) /B
+ (C) /C
+ (D) /D
+ (E) /E
+ (F) /F
+ (G) /G
+ (H) /H
+ (I) /I
+ (J) /J
+ (K) /K
+ (L) /L
+ (M) /M
+ (N) /N
+ (O) /O
+ (P) /P
+ (Q) /Q
+ (R) /R
+ (S) /S
+ (T) /T
+ (U) /U
+ (V) /V
+ (W) /W
+ (X) /X
+ (Y) /Y
+ (Z) /Z
+ ([) /bracketleft
+ (\\) /backslash
+ (]) /bracketright
+ (^) /asciicircum
+ (_) /underscore
+ (`) /quoteleft
+ (a) /a
+ (b) /b
+ (c) /c
+ (d) /d
+ (e) /e
+ (f) /f
+ (g) /g
+ (h) /h
+ (i) /i
+ (j) /j
+ (k) /k
+ (l) /l
+ (m) /m
+ (n) /n
+ (o) /o
+ (p) /p
+ (q) /q
+ (r) /r
+ (s) /s
+ (t) /t
+ (u) /u
+ (v) /v
+ (w) /w
+ (x) /x
+ (y) /y
+ (z) /z
+ ({) /braceleft
+ (|) /bar
+ (}) /braceright
+ (~) /asciitilde
+ (\\`) /grave % devpost character
+ (!!) /exclamdown
+ (c|) /cent
+ (L-) /sterling
+ (xo) /currency
+ (Y-) /yen
+ (||) /brokenbar % missing
+ (so) /section
+ ("") /dieresis
+ (co) /copyright
+ (a_) /ordfeminine
+ (<<) /guillemotleft
+ (-,) /logicalnot
+ (hy) /hyphen
+ (ro) /registered
+ (-^) /macron
+ (0^) /degree % missing
+ (+-) /plusminus % missing
+ (2^) /twosuperior % missing
+ (3^) /threesuperior % missing
+ (\\') /acute
+ (/u) /mu % missing
+ (P!) /paragraph
+ (.^) /periodcentered
+ (,,) /cedilla
+ (1^) /onesuperior % missing
+ (o_) /ordmasculine
+ (>>) /guillemotright
+ (14) /onequarter % missing
+ (12) /onehalf % missing
+ (34) /threequarters % missing
+ (??) /questiondown
+ (A`) /Agrave
+ (A') /Aacute
+ (A^) /Acircumflex
+ (A~) /Atilde
+ (A") /Adieresis
+ (A*) /Aring
+ (AE) /AE
+ (C,) /Ccedilla
+ (E`) /Egrave
+ (E') /Eacute
+ (E^) /Ecircumflex
+ (E") /Edieresis
+ (I`) /Igrave
+ (I') /Iacute
+ (I^) /Icircumflex
+ (I") /Idieresis
+ (D-) /Eth % missing
+ (N~) /Ntilde
+ (O`) /Ograve
+ (O') /Oacute
+ (O^) /Ocircumflex
+ (O~) /Otilde
+ (O") /Odieresis
+ (xx) /multiply % missing
+ (O/) /Oslash
+ (U`) /Ugrave
+ (U') /Uacute
+ (U^) /Ucircumflex
+ (U") /Udieresis
+ (Y') /Yacute % missing
+ (TH) /Thorn % missing
+ (ss) /germandbls
+ (a`) /agrave
+ (a') /aacute
+ (a^) /acircumflex
+ (a~) /atilde
+ (a") /adieresis
+ (a*) /aring
+ (ae) /ae
+ (c,) /ccedilla
+ (e`) /egrave
+ (e') /eacute
+ (e^) /ecircumflex
+ (e") /edieresis
+ (i`) /igrave
+ (i') /iacute
+ (i^) /icircumflex
+ (i") /idieresis
+ (d-) /eth % missing
+ (n~) /ntilde
+ (o`) /ograve
+ (o') /oacute
+ (o^) /ocircumflex
+ (o~) /otilde
+ (o") /odieresis
+ (-:) /divide % missing
+ (o/) /oslash
+ (u`) /ugrave
+ (u') /uacute
+ (u^) /ucircumflex
+ (u") /udieresis
+ (y') /yacute % missing
+ (th) /thorn % missing
+ (y") /ydieresis
+ //End of DingbatsCharset
+}
+
+SymbolCharset() {
+ cat <<-'//End of SymbolCharset'
+ (---) /exclam
+ (fa) /universal
+ (---) /numbersign
+ (te) /existential
+ (---) /percent
+ (---) /ampersand
+ (st) /suchthat
+ (---) /parenleft
+ (---) /parenright
+ (**) /asteriskmath
+ (pl) /plus
+ (---) /comma
+ (mi) /minus
+ (---) /period
+ (sl) /slash
+ (---) /zero
+ (---) /one
+ (---) /two
+ (---) /three
+ (---) /four
+ (---) /five
+ (---) /six
+ (---) /seven
+ (---) /eight
+ (---) /nine
+ (---) /colon
+ (---) /semicolon
+ (<) /less
+ (eq) /equal
+ (>) /greater
+ (---) /question
+ (cg) /congruent
+ (*A) /Alpha
+ (\244x) (")
+ (*B) /Beta
+ (\244y) (")
+ (*X) /Chi
+ (\244\257) (")
+ (*D) /Delta
+ (\244{) (")
+ (*E) /Epsilon
+ (\244|) (")
+ (*F) /Phi
+ (\244\256) (")
+ (*G) /Gamma
+ (\244z) (")
+ (*Y) /Eta
+ (\244~) (")
+ (*I) /Iota
+ (\244\241) (")
+ (---) /theta1
+ (\244\331) (")
+ (*K) /Kappa
+ (\244\242) (")
+ (*L) /Lambda
+ (\244\243) (")
+ (*M) /Mu
+ (\244\244) (")
+ (*N) /Nu
+ (\244\245) (")
+ (*O) /Omicron
+ (\244\247) (")
+ (*P) /Pi
+ (\244\250) (")
+ (*H) /Theta
+ (\244\240) (")
+ (*R) /Rho
+ (\244\251) (")
+ (*S) /Sigma
+ (\244\253) (")
+ (*T) /Tau
+ (\244\254) (")
+ (*U) /Upsilon
+ (\244\255) (")
+ (ts) /sigma1
+ (\244\312) (")
+ (*W) /Omega
+ (\244\261) (")
+ (*C) /Xi
+ (\244\246) (")
+ (*Q) /Psi
+ (\244\260) (")
+ (*Z) /Zeta
+ (\244}) (")
+ (---) /bracketleft
+ (tf) /therefore
+ (---) /bracketright
+ (pp) /perpendicular
+ (ul) /underscore
+ (_) (") % synonym
+ (rn) /radicalex
+ (*a) /alpha
+ (\244\271) (")
+ (*b) /beta
+ (\244\272) (")
+ (*x) /chi
+ (\244\317) (")
+ (*d) /delta
+ (\244\274) (")
+ (*e) /epsilon
+ (\244\275) (")
+ (*f) /phi
+ (\244\316) (")
+ (*g) /gamma
+ (\244\273) (")
+ (*y) /eta
+ (\244\277) (")
+ (*i) /iota
+ (\244\301) (")
+ (---) /phi1
+ (\244\335) (")
+ (*k) /kappa
+ (\244\302) (")
+ (*l) /lambda
+ (\244\303) (")
+ (*m) /mu
+ (\244\304) (")
+ (*n) /nu
+ (\244\305) (")
+ (*o) /omicron
+ (\244\307) (")
+ (*p) /pi
+ (\244\310) (")
+ (*h) /theta
+ (\244\300) (")
+ (*r) /rho
+ (\244\311) (")
+ (*s) /sigma
+ (\244\313) (")
+ (*t) /tau
+ (\244\314) (")
+ (*u) /upsilon
+ (\244\315) (")
+ (---) /omega1
+ (\244\336) (")
+ (*w) /omega
+ (\244\321) (")
+ (*c) /xi
+ (\244\306) (")
+ (*q) /psi
+ (\244\320) (")
+ (*z) /zeta
+ (\244\276) (")
+ (---) /braceleft
+ (or) /bar
+ (---) /braceright
+ (ap) /similar
+ (---) /Upsilon1
+ (fm) /minute
+ (<=) /lessequal
+ (fr) /fraction % devpost character
+ (if) /infinity
+ (fn) /florin % devpost character
+ (---) /club
+ (---) /diamond
+ (---) /heart
+ (---) /spade
+ (ab) /arrowboth
+ (<-) /arrowleft
+ (ua) /arrowup
+ (->) /arrowright
+ (da) /arrowdown
+ (de) /degree
+ (+-) /plusminus
+ (---) /second
+ (>=) /greaterequal
+ (mu) /multiply
+ (pt) /proportional
+ (pd) /partialdiff
+ (bu) /bullet
+ (di) /divide
+ (!=) /notequal
+ (==) /equivalence
+ (~~) /approxequal
+ (el) /ellipsis
+ (av) /arrowvertex
+ (ah) /arrowhorizex
+ (CR) /carriagereturn
+ (af) /aleph
+ (If) /Ifraktur
+ (Rf) /Rfraktur
+ (ws) /weierstrass
+ (Ox) /circlemultiply
+ (O+) /circleplus
+ (es) /emptyset
+ (ca) /intersection
+ (cu) /union
+ (sp) /propersuperset
+ (ip) /reflexsuperset
+ (!b) /notsubset
+ (sb) /propersubset
+ (ib) /reflexsubset
+ (mo) /element
+ (!m) /notelement
+ (an) /angle
+ (gr) /gradient
+ (rg) /registerserif
+ (co) /copyrightserif
+ (tm) /trademarkserif
+ (---) /product
+ (sr) /radical
+ (c.) /dotmath
+ (no) /logicalnot
+ (l&) /logicaland
+ (l|) /logicalor
+ (---) /arrowdblboth
+ (---) /arrowdblleft
+ (---) /arrowdblup
+ (---) /arrowdblright
+ (---) /arrowdbldown
+ (lz) /lozenge
+ (b<) /angleleft
+ (RG) /registersans
+ (CO) /copyrightsans
+ (TM) /trademarksans
+ (---) /summation
+ (LT) /parenlefttp
+ (br) /parenleftex
+ (LX) (") % synonym
+ (LB) /parenleftbt
+ (lc) /bracketlefttp
+ (lx) /bracketleftex
+ (lf) /bracketleftbt
+ (lt) /bracelefttp
+ (lk) /braceleftmid
+ (lb) /braceleftbt
+ (bv) /braceex
+ (|) (") % synonym
+ (b>) /angleright
+ (is) /integral
+ (---) /integraltp
+ (---) /integralex
+ (---) /integralbt
+ (RT) /parenrighttp
+ (RX) /parenrightex
+ (RB) /parenrightbt
+ (rc) /bracketrighttp
+ (rx) /bracketrightex
+ (rf) /bracketrightbt
+ (rt) /bracerighttp
+ (rk) /bracerightmid
+ (rb) /bracerightbt
+ (~=) (55 0 1) % charlib
+ //End of SymbolCharset
+}
+
+SpecialCharset() {
+ cat <<-'//End of SpecialCharset'
+ (ru) /underscore
+ ('') /quotedblright % devpost character
+ (``) /quotedblleft % devpost character
+ (dg) /dagger % devpost character
+ (dd) /daggerdbl % devpost character
+ (en) /endash % devpost character
+ (\\-) (") % synonym
+ (em) /emdash
+% (ff) (60 2 1) % charlib
+% (Fi) (84 2 1) % charlib
+% (Fl) (84 2 1) % charlib
+ (14) (75 2 1) % charlib
+ (12) (75 2 1) % charlib
+ (34) (75 2 1) % charlib
+ (bx) (50 2 1) % charlib
+ (ob) (38 2 1) % charlib
+ (ci) (75 0 1) % charlib
+ (sq) (50 2 1) % charlib
+ (Sl) (50 2 1) % charlib
+ (L1) (110 1 1) % charlib
+ (LA) (110 1 1) % charlib
+ (LV) (110 3 1) % charlib
+ (LH) (210 1 1) % charlib
+ (lh) (100 0 1) % charlib
+ (rh) (100 0 1) % charlib
+ (lH) (100 0 1) % charlib
+ (rH) (100 0 1) % charlib
+ (PC) (220 2 1) % charlib
+ (DG) (185 2 1) % charlib
+ //End of SpecialCharset
+}
+
+#
+# Latin1 ensures a font uses the ISOLatin1Encoding vector, although only
+# text fonts should be re-encoded. Downloading the Encoding vector doesn't
+# often make sense. No ISOLatin1Encoding array likely means ROM based fonts
+# on your printer are incomplete. Type 1 fonts with a full Latin1 character
+# set appeared sometime after Version 50.0.
+#
+
+Latin1() {
+ if [ "$DOWNLOADVECTOR" = TRUE ]; then
+ cat <<-'//End of ISOLatin1Encoding'
+ /ISOLatin1Encoding [
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /space
+ /exclam
+ /quotedbl
+ /numbersign
+ /dollar
+ /percent
+ /ampersand
+ /quoteright
+ /parenleft
+ /parenright
+ /asterisk
+ /plus
+ /comma
+ /minus
+ /period
+ /slash
+ /zero
+ /one
+ /two
+ /three
+ /four
+ /five
+ /six
+ /seven
+ /eight
+ /nine
+ /colon
+ /semicolon
+ /less
+ /equal
+ /greater
+ /question
+ /at
+ /A
+ /B
+ /C
+ /D
+ /E
+ /F
+ /G
+ /H
+ /I
+ /J
+ /K
+ /L
+ /M
+ /N
+ /O
+ /P
+ /Q
+ /R
+ /S
+ /T
+ /U
+ /V
+ /W
+ /X
+ /Y
+ /Z
+ /bracketleft
+ /backslash
+ /bracketright
+ /asciicircum
+ /underscore
+ /quoteleft
+ /a
+ /b
+ /c
+ /d
+ /e
+ /f
+ /g
+ /h
+ /i
+ /j
+ /k
+ /l
+ /m
+ /n
+ /o
+ /p
+ /q
+ /r
+ /s
+ /t
+ /u
+ /v
+ /w
+ /x
+ /y
+ /z
+ /braceleft
+ /bar
+ /braceright
+ /asciitilde
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /.notdef
+ /dotlessi
+ /grave
+ /acute
+ /circumflex
+ /tilde
+ /macron
+ /breve
+ /dotaccent
+ /dieresis
+ /.notdef
+ /ring
+ /cedilla
+ /.notdef
+ /hungarumlaut
+ /ogonek
+ /caron
+ /space
+ /exclamdown
+ /cent
+ /sterling
+ /currency
+ /yen
+ /brokenbar
+ /section
+ /dieresis
+ /copyright
+ /ordfeminine
+ /guillemotleft
+ /logicalnot
+ /hyphen
+ /registered
+ /macron
+ /degree
+ /plusminus
+ /twosuperior
+ /threesuperior
+ /acute
+ /mu
+ /paragraph
+ /periodcentered
+ /cedilla
+ /onesuperior
+ /ordmasculine
+ /guillemotright
+ /onequarter
+ /onehalf
+ /threequarters
+ /questiondown
+ /Agrave
+ /Aacute
+ /Acircumflex
+ /Atilde
+ /Adieresis
+ /Aring
+ /AE
+ /Ccedilla
+ /Egrave
+ /Eacute
+ /Ecircumflex
+ /Edieresis
+ /Igrave
+ /Iacute
+ /Icircumflex
+ /Idieresis
+ /Eth
+ /Ntilde
+ /Ograve
+ /Oacute
+ /Ocircumflex
+ /Otilde
+ /Odieresis
+ /multiply
+ /Oslash
+ /Ugrave
+ /Uacute
+ /Ucircumflex
+ /Udieresis
+ /Yacute
+ /Thorn
+ /germandbls
+ /agrave
+ /aacute
+ /acircumflex
+ /atilde
+ /adieresis
+ /aring
+ /ae
+ /ccedilla
+ /egrave
+ /eacute
+ /ecircumflex
+ /edieresis
+ /igrave
+ /iacute
+ /icircumflex
+ /idieresis
+ /eth
+ /ntilde
+ /ograve
+ /oacute
+ /ocircumflex
+ /otilde
+ /odieresis
+ /divide
+ /oslash
+ /ugrave
+ /uacute
+ /ucircumflex
+ /udieresis
+ /yacute
+ /thorn
+ /ydieresis
+ ] def
+ //End of ISOLatin1Encoding
+ fi
+
+ echo "ISOLatin1Encoding /$1 ReEncode"
+}
+
+#
+# Generating functions output PostScript programs that build font width
+# tables or a typesetter description file. Send the program to a printer
+# and the complete table will come back on the serial port. All write on
+# stdout and assume the prologue and other required PostScript files are
+# all available.
+#
+
+Proportional() {
+ echo "/unitwidth $UNITWIDTH def"
+ echo "/resolution $RESOLUTION def"
+ echo "/octalescapes $OCTALESCAPES def"
+ echo "/charset ["
+ # Get <>_ and | from S. Use accents for ascii ^ and ~.
+ StandardCharset | awk '
+ $1 == "(<)" && $2 == "/less" {$1 = "(---)"}
+ $1 == "(>)" && $2 == "/greater" {$1 = "(---)"}
+ $1 == "(_)" && $2 == "/underscore" {$1 = "(---)"}
+ $1 == "(|)" && $2 == "/bar" {$1 = "(---)"}
+ $1 == "(^)" && $2 == "/asciicircum" {
+ printf "(^)\t/circumflex\n"
+ $1 = "(---)"
+ }
+ $1 == "(~)" && $2 == "/asciitilde" {
+ printf "(~)\t/tilde\n"
+ $1 = "(---)"
+ }
+ {printf "%s\t%s\n", $1, $2}
+ '
+ echo "] def"
+
+ Latin1 $2
+ echo "/$2 SelectFont"
+ echo "(opO) SetAscender"
+
+ echo "(name $1\\\\n) Print"
+ echo "(fontname $2\\\\n) Print"
+ echo "/$1 NamedInPrologue"
+ echo "(spacewidth ) Print 32 GetWidth Print (\n) Print"
+ echo "(charset\\\\n) Print"
+ echo "BuildFontCharset"
+}
+
+FixedWidth() {
+ echo "/unitwidth $UNITWIDTH def"
+ echo "/resolution $RESOLUTION def"
+ echo "/octalescapes $OCTALESCAPES def"
+ echo "/charset ["
+ StandardCharset
+ echo "] def"
+
+ Latin1 $2
+ echo "/$2 SelectFont"
+ echo "(opO) SetAscender"
+
+ echo "(name $1\\\\n) Print"
+ echo "(fontname $2\\\\n) Print"
+ echo "/$1 NamedInPrologue"
+ echo "(spacewidth ) Print 32 GetWidth Print (\n) Print"
+ echo "(charset\\\\n) Print"
+ echo "BuildFontCharset"
+}
+
+Dingbats() {
+ echo "/unitwidth $UNITWIDTH def"
+ echo "/resolution $RESOLUTION def"
+ echo "/octalescapes $OCTALESCAPES def"
+ echo "/charset ["
+ DingbatsCharset | awk '$1 != "(---)" && $2 ~ /^\/[a-zA-Z]/ {
+ printf "%s\tISOLatin1Encoding %s GetCode\n", $1, $2
+ }'
+ echo "] def"
+
+ echo "/$2 SelectFont"
+ echo "( ) SetAscender"
+
+ echo "(name $1\\\\n) Print"
+ echo "(fontname $2\\\\n) Print"
+ echo "/$1 NamedInPrologue"
+ echo "(charset\\\\n) Print"
+ echo "BuildFontCharset"
+}
+
+Greek() {
+ echo "/unitwidth $UNITWIDTH def"
+ echo "/resolution $RESOLUTION def"
+ echo "/charset ["
+ SymbolCharset | awk '
+ BEGIN {hit = -1}
+ $1 ~ /\(\*[a-zA-Z]\)/ {print; hit = NR}
+ $2 == "(\")" && hit == NR-1 {print; hit = NR}
+ '
+ echo "] def"
+
+ echo "/$2 SelectFont"
+ echo "(orO) SetAscender"
+
+ echo "(name $1\\\\n) Print"
+ echo "(fontname $2\\\\n) Print"
+ echo "/$1 NamedInPrologue"
+ echo "(spacewidth ) Print 32 GetWidth Print (\n) Print"
+ echo "(charset\\\\n) Print"
+ echo "BuildFontCharset"
+}
+
+Symbol() {
+ echo "/unitwidth $UNITWIDTH def"
+ echo "/resolution $RESOLUTION def"
+ echo "/charset ["
+ SymbolCharset
+ echo "] def"
+
+ echo "ChangeMetrics"
+ echo "/S SelectFont"
+ echo "(orO) SetAscender"
+
+ echo "(name $1\\\\n) Print"
+ echo "(fontname $2\\\\n) Print"
+ echo "/$1 NamedInPrologue"
+ echo "(special\\\\n) Print"
+ echo "(charset\\\\n) Print"
+ echo "BuildFontCharset"
+}
+
+Special() {
+ echo "/unitwidth $UNITWIDTH def"
+ echo "/resolution $RESOLUTION def"
+ echo "/charset ["
+ SpecialCharset
+ echo "] def"
+
+ echo "ChangeMetrics"
+ echo "/S1 SelectFont"
+
+ echo "(# Times-Roman special font\\\\n) Print"
+ echo "(name $1\\\\n) Print"
+ echo "(fontname $2\\\\n) Print"
+ echo "/$1 NamedInPrologue"
+ echo "(special\\\\n) Print"
+ echo "(charset\\\\n) Print"
+ echo "BuildFontCharset"
+}
+
+#
+# The DESC file doesn't have to be built on a printer. It's only here for
+# consistency.
+#
+
+Description() {
+ echo "/charset [" # awk - so the stack doesn't overflow
+ StandardCharset | awk '$1 !~ /\(\\[0-9]/ {print $1}'
+ SymbolCharset | awk '$1 !~ /\(\\[0-9]/ {print $1}'
+ SpecialCharset | awk '$1 !~ /\(\\[0-9]/ {print $1}'
+ echo "] def"
+
+ cat <<-//DESC
+ (#Device Description - utf character set
+
+ PDL PostScript
+ Encoding Latin1
+
+ fonts 10 R I B BI CW H HI HB S1 S
+ sizes 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
+ 23 24 25 26 27 28 29 30 31 32 33 34 35 36 38 40 42 44 46
+ 48 50 52 54 56 58 60 64 68 72 78 84 90 96 100 105 110 115
+ 120 125 130 135 140 145 150 155 160 0
+ res $RESOLUTION
+ hor 1
+ vert 1
+ unitwidth $UNITWIDTH
+
+ ) Print
+ //DESC
+ echo "(charset\\\\n) Print"
+ echo "BuildDescCharset"
+ echo "(\\\\n) Print"
+}
+
diff --git a/src/cmd/postscript/tr2post/tr2post.c b/src/cmd/postscript/tr2post/tr2post.c
new file mode 100644
index 00000000..d60e0c7a
--- /dev/null
+++ b/src/cmd/postscript/tr2post/tr2post.c
@@ -0,0 +1,218 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <stdio.h>
+#include "common.h"
+#include "tr2post.h"
+#include "comments.h"
+#include "path.h"
+
+int formsperpage = 1;
+int picflag = 1;
+double aspectratio = 1.0;
+int copies = 1;
+int landscape = 0;
+double magnification = 1.0;
+int linesperpage = 66;
+int pointsize = 10;
+double xoffset = .25;
+double yoffset = .25;
+char *passthrough = 0;
+
+Biobuf binp, *bstdout, bstderr;
+Biobuf *Bstdin, *Bstdout, *Bstderr;
+int debug = 0;
+
+char tmpfilename[MAXTOKENSIZE];
+char copybuf[BUFSIZ];
+
+
+struct charent **build_char_list = 0;
+int build_char_cnt = 0;
+
+void
+prologues(void) {
+ int i;
+ char charlibname[MAXTOKENSIZE];
+
+ Bprint(Bstdout, "%s", CONFORMING);
+ Bprint(Bstdout, "%s %s\n", VERSION, PROGRAMVERSION);
+ Bprint(Bstdout, "%s %s\n", DOCUMENTFONTS, ATEND);
+ Bprint(Bstdout, "%s %s\n", PAGES, ATEND);
+ Bprint(Bstdout, "%s", ENDCOMMENTS);
+
+ if (cat(unsharp(DPOST))) {
+ Bprint(Bstderr, "can't read %s\n", DPOST);
+ exits("dpost prologue");
+ }
+
+ if (drawflag) {
+ if (cat(unsharp(DRAW))) {
+ Bprint(Bstderr, "can't read %s\n", DRAW);
+ exits("draw prologue");
+ }
+ }
+
+ if (DOROUND)
+ cat(unsharp(ROUNDPAGE));
+
+ Bprint(Bstdout, "%s", ENDPROLOG);
+ Bprint(Bstdout, "%s", BEGINSETUP);
+ Bprint(Bstdout, "mark\n");
+ if (formsperpage > 1) {
+ Bprint(Bstdout, "%s %d\n", FORMSPERPAGE, formsperpage);
+ Bprint(Bstdout, "/formsperpage %d def\n", formsperpage);
+ }
+ if (aspectratio != 1) Bprint(Bstdout, "/aspectratio %g def\n", aspectratio);
+ if (copies != 1) Bprint(Bstdout, "/#copies %d store\n", copies);
+ if (landscape) Bprint(Bstdout, "/landscape true def\n");
+ if (magnification != 1) Bprint(Bstdout, "/magnification %g def\n", magnification);
+ if (pointsize != 10) Bprint(Bstdout, "/pointsize %d def\n", pointsize);
+ if (xoffset != .25) Bprint(Bstdout, "/xoffset %g def\n", xoffset);
+ if (yoffset != .25) Bprint(Bstdout, "/yoffset %g def\n", yoffset);
+ cat(unsharp(ENCODINGDIR"/Latin1.enc"));
+ if (passthrough != 0) Bprint(Bstdout, "%s\n", passthrough);
+
+ Bprint(Bstdout, "setup\n");
+ if (formsperpage > 1) {
+ cat(unsharp(FORMFILE));
+ Bprint(Bstdout, "%d setupforms \n", formsperpage);
+ }
+/* output Build character info from charlib if necessary. */
+
+ for (i=0; i<build_char_cnt; i++) {
+ sprint(charlibname, "%s/%s", CHARLIB, build_char_list[i]->name);
+ if (cat(unsharp(charlibname)))
+ Bprint(Bstderr, "cannot open %s\n", charlibname);
+ }
+
+ Bprint(Bstdout, "%s", ENDSETUP);
+}
+
+void
+cleanup(void) {
+ remove(tmpfilename);
+}
+
+main(int argc, char *argv[]) {
+ Biobuf *binp;
+ Biobuf *Binp;
+ int i, tot, ifd;
+ char *t;
+
+ programname = argv[0];
+ if (Binit(&bstderr, 2, OWRITE) == Beof) {
+ exits("Binit");
+ }
+ Bstderr = &bstderr;
+
+ tmpnam(tmpfilename);
+ if ((bstdout=Bopen(tmpfilename, OWRITE)) == 0) {
+ Bprint(Bstderr, "cannot open temporary file %s\n", tmpfilename);
+ exits("Bopen");
+ }
+ atexit(cleanup);
+ Bstdout = bstdout;
+
+ ARGBEGIN{
+ case 'a': /* aspect ratio */
+ aspectratio = atof(ARGF());
+ break;
+ case 'c': /* copies */
+ copies = atoi(ARGF());
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'm': /* magnification */
+ magnification = atof(ARGF());
+ break;
+ case 'n': /* forms per page */
+ formsperpage = atoi(ARGF());
+ break;
+ case 'o': /* output page list */
+ pagelist(ARGF());
+ break;
+ case 'p': /* landscape or portrait mode */
+ if ( ARGF()[0] == 'l' )
+ landscape = 1;
+ else
+ landscape = 0;
+ break;
+ case 'x': /* shift things horizontally */
+ xoffset = atof(ARGF());
+ break;
+ case 'y': /* and vertically on the page */
+ yoffset = atof(ARGF());
+ break;
+ case 'P': /* PostScript pass through */
+ t = ARGF();
+ i = strlen(t) + 1;
+ passthrough = malloc(i);
+ if (passthrough == 0) {
+ Bprint(Bstderr, "cannot allocate memory for argument string\n");
+ exits("malloc");
+ }
+ strncpy(passthrough, t, i);
+ break;
+ default: /* don't know what to do for ch */
+ Bprint(Bstderr, "unknown option %C\n", ARGC());
+ break;
+ }ARGEND;
+ readDESC();
+ if (argc == 0) {
+ if ((binp = (Biobuf *)malloc(sizeof(Biobuf))) < (Biobuf *)0) {
+ Bprint(Bstderr, "malloc failed.\n");
+ exits("malloc");
+ }
+ if (Binit(binp, 0, OREAD) == Beof) {
+ Bprint(Bstderr, "Binit of <stdin> failed.\n");
+ exits("Binit");
+ }
+ Binp = binp;
+ if (debug) Bprint(Bstderr, "using standard input\n");
+ conv(Binp);
+ Bterm(Binp);
+ }
+ for (i=0; i<argc; i++) {
+ if ((binp=Bopen(argv[i], OREAD)) == 0) {
+ Bprint(Bstderr, "cannot open file %s\n", argv[i]);
+ continue;
+ }
+ Binp = binp;
+ inputfilename = argv[i];
+ conv(Binp);
+ Bterm(Binp);
+ }
+ Bterm(Bstdout);
+
+ if ((ifd=open(tmpfilename, OREAD)) < 0) {
+ Bprint(Bstderr, "open of %s failed.\n", tmpfilename);
+ exits("open");
+ }
+
+ bstdout = galloc(0, sizeof(Biobuf), "bstdout");
+ if (Binit(bstdout, 1, OWRITE) == Beof) {
+ Bprint(Bstderr, "Binit of <stdout> failed.\n");
+ exits("Binit");
+ }
+ Bstdout = bstdout;
+ prologues();
+ Bflush(Bstdout);
+ tot = 0; i = 0;
+ while ((i=read(ifd, copybuf, BUFSIZ)) > 0) {
+ if (write(1, copybuf, i) != i) {
+ Bprint(Bstderr, "write error on copying from temp file.\n");
+ exits("write");
+ }
+ tot += i;
+ }
+ if (debug) Bprint(Bstderr, "copied %d bytes to final output i=%d\n", tot, i);
+ if (i < 0) {
+ Bprint(Bstderr, "read error on copying from temp file.\n");
+ exits("read");
+ }
+ finish();
+
+ exits("");
+}
diff --git a/src/cmd/postscript/tr2post/tr2post.h b/src/cmd/postscript/tr2post/tr2post.h
new file mode 100644
index 00000000..b07e0b13
--- /dev/null
+++ b/src/cmd/postscript/tr2post/tr2post.h
@@ -0,0 +1,103 @@
+#define MAXSPECHARS 512
+#define MAXTOKENSIZE 128
+#define CHARLIB "#9/sys/lib/troff/font/devutf/charlib"
+
+extern int debug;
+extern int fontsize;
+extern int fontpos;
+extern int resolution; /* device resolution, goobies per inch */
+extern int minx; /* minimum x motion */
+extern int miny; /* minimum y motion */
+extern char devname[];
+extern int devres;
+extern int unitwidth;
+extern char *printdesclang;
+extern char *encoding;
+extern int fontmnt;
+extern char **fontmtab;
+
+extern int curtrofffontid; /* index into trofftab of current troff font */
+extern int troffontcnt;
+
+extern BOOLEAN drawflag;
+
+struct specname {
+ char *str;
+ struct specname *next;
+};
+
+/* character entries for special characters (those pointed
+ * to by multiple character names, e.g. \(mu for multiply.
+ */
+struct charent {
+ char postfontid; /* index into pfnamtab */
+ char postcharid; /* e.g., 0x00 */
+ short troffcharwidth;
+ char *name;
+ struct charent *next;
+};
+
+extern struct charent **build_char_list;
+extern int build_char_cnt;
+
+struct pfnament {
+ char *str;
+ int used;
+};
+
+/* these entries map troff character code ranges to
+ * postscript font and character ranges.
+ */
+struct psfent {
+ int start;
+ int end;
+ int offset;
+ int psftid;
+};
+
+struct troffont {
+ char *trfontid; /* the common troff font name e.g., `R' */
+ BOOLEAN special; /* flag says this is a special font. */
+ int spacewidth;
+ int psfmapsize;
+ struct psfent *psfmap;
+ struct charent *charent[NUMOFONTS][FONTSIZE];
+};
+
+extern struct troffont *troffontab;
+extern struct charent spechars[];
+
+/** prototypes **/
+void initialize(void);
+void mountfont(int, char*);
+int findtfn(char *, int);
+void runeout(Rune);
+void specialout(char *);
+long nametorune(char *);
+void conv(Biobuf *);
+void hgoto(int);
+void vgoto(int);
+void hmot(int);
+void vmot(int);
+void draw(Biobuf *);
+void devcntl(Biobuf *);
+void notavail(char *);
+void error(int, char *, ...);
+void loadfont(int, char *);
+void flushtext(void);
+void t_charht(int);
+void t_slant(int);
+void startstring(void);
+void endstring(void);
+BOOLEAN pageon(void);
+void setpsfont(int, int);
+void settrfont(void);
+int hash(char *, int);
+BOOLEAN readDESC(void);
+void finish(void);
+void ps_include(Biobuf *, Biobuf *, int, int,
+ int, int, double, double, double, double,
+ double, double, double);
+void picture(Biobuf *, char *);
+void beginpath(char*, int);
+void drawpath(char*, int);
diff --git a/src/cmd/postscript/tr2post/utfmap b/src/cmd/postscript/tr2post/utfmap
new file mode 100644
index 00000000..2f9d6dc3
--- /dev/null
+++ b/src/cmd/postscript/tr2post/utfmap
@@ -0,0 +1,47 @@
+¡ !! ¢ c$ £ l$ ¤ g$
+¥ y$ ¦ || § SS ¨ ""
+© cO ª sa « << ¬ no
+­ -- ® rO ¯ __ ° de
+± +- ² s2 ³ s3 ´ ''
+µ mi ¶ pg · .. ¸ ,,
+¹ s1 º s0 » >> ¼ 14
+½ 12 ¾ 34 ¿ ?? À `A
+Á 'A Â ^A Ã ~A Ä "A
+Å oA Æ AE Ç ,C È `E
+É 'E Ê ^E Ë "E Ì `I
+Í 'I Î ^I Ï "I Ð D-
+Ñ ~N Ò `O Ó 'O Ô ^O
+Õ ~O Ö "O × mu Ø /O
+Ù `U Ú 'U Û ^U Ü "U
+Ý 'Y Þ |P ß ss à `a
+á 'a â ^a ã ~a ä "a
+å oa æ ae ç ,c è `e
+é 'e ê ^e ë "e ì `i
+í 'i î ^i ï "i ð d-
+ñ ~n ò `o ó 'o ô ^o
+õ ~o ö "o ÷ -: ø /o
+ù `u ú 'u û ^u ü "u
+ý 'y þ |p ÿ "y α *a
+β *b γ *g δ *d ε *e
+ζ *z η *y θ *h ι *i
+κ *k λ *l *m μ ν *n
+ξ *c ο *o π *p ρ *r
+ς ts σ *s τ *t υ *u
+φ *f χ *x ψ *q ω *w
+Α *A Β *B Γ *G Δ *D
+Ε *E Ζ *Z Η *Y Θ *H
+Ι *I Κ *K Λ *L Μ *M
+Ν *N Ξ *C Ο *O Π *P
+Ρ *R Σ *S Τ *T Υ *U
+Φ *F Χ *X Ψ *Q Ω *W
+← <- ↑ ua → -> ↓ da
+↔ ab ∀ fa ∃ te ∂ pd
+∅ es ∆ *D ∇ gr ∉ !m
+∍ st ∗ ** ∙ bu √ sr
+∝ pt ∞ if ∠ an ∧ l&
+∨ l| ∩ ca ∪ cu ∫ is
+∴ tf ≃ ~= ≅ cg ≈ ~~
+≠ != ≡ == ≦ <= ≧ >=
+⊂ sb ⊃ sp ⊄ !b ⊆ ib
+⊇ ip ⊕ O+ ⊖ O- ⊗ Ox
+⊢ tu ⊨ Tu ⋄ lz ⋯ el
diff --git a/src/cmd/postscript/tr2post/utils.c b/src/cmd/postscript/tr2post/utils.c
new file mode 100644
index 00000000..8f58ea45
--- /dev/null
+++ b/src/cmd/postscript/tr2post/utils.c
@@ -0,0 +1,264 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "../common/common.h"
+#include "tr2post.h"
+
+int hpos = 0, vpos = 0;
+int fontsize, fontpos;
+
+#define MAXSTR 128
+int trindex; /* index into trofftab of current troff font */
+static int expecthmot = 0;
+
+void
+initialize(void) {
+}
+
+void
+hgoto(int x) {
+ hpos = x;
+ if (pageon()) {
+ endstring();
+/* Bprint(Bstdout, "%d %d m\n", hpos, vpos); */
+ }
+}
+
+void
+vgoto(int y) {
+ vpos = y;
+ if (pageon()) {
+ endstring();
+/* Bprint(Bstdout, "%d %d m\n", hpos, vpos); */
+ }
+}
+
+void
+hmot(int x) {
+ int delta;
+
+ if ((x<expecthmot-1) || (x>expecthmot+1)) {
+ delta = x - expecthmot;
+ if (curtrofffontid <0 || curtrofffontid >= troffontcnt) {
+ Bprint(Bstderr, "troffontcnt=%d curtrofffontid=%d\n", troffontcnt, curtrofffontid);
+ Bflush(Bstderr);
+ exits("");
+ }
+ if (delta == troffontab[curtrofffontid].spacewidth*fontsize/10 && isinstring()) {
+ if (pageon()) runeout(' ');
+ } else {
+ if (pageon()) {
+ endstring();
+ /* Bprint(Bstdout, " %d 0 rmoveto ", delta); */
+/* Bprint(Bstdout, " %d %d m ", hpos+x, vpos); */
+ if (debug) Bprint(Bstderr, "x=%d expecthmot=%d\n", x, expecthmot);
+ }
+ }
+ }
+ hpos += x;
+ expecthmot = 0;
+}
+
+void
+vmot(int y) {
+ endstring();
+/* Bprint(Bstdout, " 0 %d rmoveto ", -y); */
+ vpos += y;
+}
+
+struct charent **
+findglyph(int trfid, Rune rune, char *stoken) {
+ struct charent **cp;
+
+ for (cp = &(troffontab[trfid].charent[RUNEGETGROUP(rune)][RUNEGETCHAR(rune)]); *cp != 0; cp = &((*cp)->next)) {
+ if ((*cp)->name) {
+ if (debug) Bprint(Bstderr, "looking for <%s>, have <%s> in font %s\n", stoken, (*cp)->name, troffontab[trfid].trfontid);
+ if (strcmp((*cp)->name, stoken) == 0)
+ break;
+ }
+ }
+ return(cp);
+}
+
+/* output glyph. Use first rune to look up character (hash)
+ * then use stoken UTF string to find correct glyph in linked
+ * list of glyphs in bucket.
+ */
+void
+glyphout(Rune rune, char *stoken, BOOLEAN specialflag) {
+ struct charent **cp;
+ struct troffont *tfp;
+ struct psfent *psfp;
+ int i, t;
+ int fontid; /* this is the troff font table index, not the mounted font table index */
+ int mi, fi, wid;
+ Rune r;
+
+ settrfont();
+
+ /* check current font for the character, special or not */
+ fontid = curtrofffontid;
+if (debug) fprint(2, " looking through current font: trying %s\n", troffontab[fontid].trfontid);
+ cp = findglyph(fontid, rune, stoken);
+ if (*cp != 0) goto foundit;
+
+ if (specialflag) {
+ if (expecthmot) hmot(0);
+
+ /* check special fonts for the special character */
+ /* cycle through the (troff) mounted fonts starting at the next font */
+ for (mi=0; mi<fontmnt; mi++) {
+ if (troffontab[fontid].trfontid==0) error(WARNING, "glyphout:troffontab[%d].trfontid=0x%x, botch!\n",
+ fontid, troffontab[fontid].trfontid);
+ if (fontmtab[mi]==0) {
+ if (debug) fprint(2, "fontmtab[%d]=0x%x, fontmnt=%d\n", mi, fontmtab[mi], fontmnt);
+ continue;
+ }
+ if (strcmp(troffontab[fontid].trfontid, fontmtab[mi])==0) break;
+ }
+ if (mi==fontmnt) error(FATAL, "current troff font is not mounted, botch!\n");
+ for (i=(mi+1)%fontmnt; i!=mi; i=(i+1)%fontmnt) {
+ if (fontmtab[i]==0) {
+ if (debug) fprint(2, "fontmtab[%d]=0x%x, fontmnt=%d\n", i, fontmtab[i], fontmnt);
+ continue;
+ }
+ fontid = findtfn(fontmtab[i], TRUE);
+if (debug) fprint(2, " looking through special fonts: trying %s\n", troffontab[fontid].trfontid);
+ if (troffontab[fontid].special) {
+ cp = findglyph(fontid, rune, stoken);
+ if (*cp != 0) goto foundit;
+ }
+ }
+
+ /* check font 1 (if current font is not font 1) for the special character */
+ if (mi != 1) {
+ fontid = findtfn(fontmtab[1], TRUE);;
+if (debug) fprint(2, " looking through font at position 1: trying %s\n", troffontab[fontid].trfontid);
+ cp = findglyph(fontid, rune, stoken);
+ if (*cp != 0) goto foundit;
+ }
+ }
+
+ if (*cp == 0) {
+ error(WARNING, "cannot find glyph, rune=0x%x stoken=<%s> troff font %s\n", rune, stoken,
+ troffontab[curtrofffontid].trfontid);
+ expecthmot = 0;
+ }
+
+ /* use the peter face in lieu of the character that we couldn't find */
+ rune = 'p'; stoken = "pw";
+ for (i=(mi+1)%fontmnt; i!=mi; i=(i+1)%fontmnt) {
+ if (fontmtab[i]==0) {
+ if (debug) fprint(2, "fontmtab[%d]=0x%x\n", i, fontmtab[i]);
+ continue;
+ }
+ fontid = findtfn(fontmtab[i], TRUE);
+if (debug) fprint(2, " looking through special fonts: trying %s\n", troffontab[fontid].trfontid);
+ if (troffontab[fontid].special) {
+ cp = findglyph(fontid, rune, stoken);
+ if (*cp != 0) goto foundit;
+ }
+ }
+
+ if (*cp == 0) {
+ error(WARNING, "cannot find glyph, rune=0x%x stoken=<%s> troff font %s\n", rune, stoken,
+ troffontab[curtrofffontid].trfontid);
+ expecthmot = 0;
+ return;
+ }
+
+foundit:
+ t = (((*cp)->postfontid&0xff)<<8) | ((*cp)->postcharid&0xff);
+ if (debug) {
+ Bprint(Bstderr, "runeout(0x%x)<%C> postfontid=0x%x postcharid=0x%x troffcharwidth=%d\n",
+ rune, rune, (*cp)->postfontid, (*cp)->postcharid, (*cp)->troffcharwidth);
+ }
+
+ tfp = &(troffontab[fontid]);
+ for (i=0; i<tfp->psfmapsize; i++) {
+ psfp = &(tfp->psfmap[i]);
+ if(t>=psfp->start && t<=psfp->end) break;
+ }
+ if (i >= tfp->psfmapsize)
+ error(FATAL, "character <0x%x> does not have a Postscript font defined.\n", rune);
+
+ setpsfont(psfp->psftid, fontsize);
+
+ if (t == 0x0001) { /* character is in charlib */
+ endstring();
+ if (pageon()) {
+ struct charent *tcp;
+
+ Bprint(Bstdout, "%d %d m ", hpos, vpos);
+ /* if char is unicode character rather than name, clean up for postscript */
+ wid = chartorune(&r, (*cp)->name);
+ if(' '<r && r<0x7F)
+ Bprint(Bstdout, "%d build_%s\n", (*cp)->troffcharwidth, (*cp)->name);
+ else{
+ if((*cp)->name[wid] != 0)
+ error(FATAL, "character <%s> badly named\n", (*cp)->name);
+ Bprint(Bstdout, "%d build_X%.4x\n", (*cp)->troffcharwidth, r);
+ }
+
+ /* stash charent pointer in a list so that we can print these character definitions
+ * in the prologue.
+ */
+ for (i=0; i<build_char_cnt; i++)
+ if (*cp == build_char_list[i]) break;
+ if (i == build_char_cnt) {
+ build_char_list = galloc(build_char_list, sizeof(struct charent *) * ++build_char_cnt,
+ "build_char_list");
+ build_char_list[build_char_cnt-1] = *cp;
+ }
+ }
+ expecthmot = (*cp)->troffcharwidth * fontsize / unitwidth;
+ } else if (isinstring() || rune != ' ') {
+ startstring();
+ if (pageon()) {
+ if (rune == ' ')
+ Bprint(Bstdout, " ");
+ else
+ Bprint(Bstdout, "%s", charcode[RUNEGETCHAR(t)].str);
+ }
+ expecthmot = (*cp)->troffcharwidth * fontsize / unitwidth;
+ }
+}
+
+/* runeout puts a symbol into a string (queue) to be output.
+ * It also has to keep track of the current and last symbol
+ * output to check that the spacing is correct by default
+ * or needs to be adjusted with a spacing operation.
+ */
+
+void
+runeout(Rune rune) {
+ char stoken[UTFmax+1];
+ int i;
+
+ i = runetochar(stoken, &rune);
+ stoken[i] = '\0';
+ glyphout(rune, stoken, TRUE);
+}
+
+void
+specialout(char *stoken) {
+ Rune rune;
+ int i;
+
+ i = chartorune(&rune, stoken);
+ glyphout(rune, stoken, TRUE);
+}
+
+void
+graphfunc(Biobuf *bp) {
+}
+
+long
+nametorune(char *name) {
+ return(0);
+}
+
+void
+notavail(char *msg) {
+ Bprint(Bstderr, "%s is not available at this time.\n", msg);
+}