diff options
author | rsc <devnull@localhost> | 2004-05-15 23:55:53 +0000 |
---|---|---|
committer | rsc <devnull@localhost> | 2004-05-15 23:55:53 +0000 |
commit | 61f5c35c9465f0702739b41249a664d409f0482c (patch) | |
tree | 17546b7dcc76abd9ee74dc7543cc77121acfe39a /src/cmd/postscript | |
parent | 173302913ebce353eadcbb12d71c3759cbe79e34 (diff) | |
download | plan9port-61f5c35c9465f0702739b41249a664d409f0482c.tar.gz plan9port-61f5c35c9465f0702739b41249a664d409f0482c.tar.bz2 plan9port-61f5c35c9465f0702739b41249a664d409f0482c.zip |
more files
Diffstat (limited to 'src/cmd/postscript')
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', "e, 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); +} |