(* PSWriter defines PostScript output routines used by PSDVI. The output file consists of calls to various PostScript procedures defined in a header file prefixed to the output. Some of the PostScript procedures expect integer arguments that represent page positions in TeX's coordinate system. Units are in "dots" (i.e., device pixels, where resolution defines the number of dots per inch). The origin (0,0) is a dot 1 inch in from both the top and left paper edges. Horizontal coordinates increase to the right and vertical coordinates increase down the page. The header file contains the necessary matrix transformations to convert TeX coordinates back into device coordinates. *) #include 'globals.h'; #include 'options.h'; #include 'pswriter.h'; CONST O_RDONLY = 0; (* SYSDEP: read-only flag for open *) maxrbuf = 256; TYPE rbuf = PACKED ARRAY [1..maxrbuf] OF CHAR; FUNCTION open (VAR path : string; flags, mode : integer) : integer; EXTERNAL; FUNCTION read (f : integer; VAR buf : rbuf; n : integer) : integer; EXTERNAL; FUNCTION close(f : integer) : integer; EXTERNAL; VAR curh, curv, (* for SetBitmapChar and SetPostScriptChar *) stringlen : INTEGER; (* ditto; current string length *) pendingch : CHAR; (* ditto; terminates current string *) (******************************************************************************) FUNCTION OpenOutput (name : string) : BOOLEAN; (* Returns TRUE if given file can be opened for output. *) BEGIN rewrite(PSfile,name); (* create a new file and open it for writing *) OpenOutput := TRUE; (* assume given file is created *) END; (* OpenOutput *) (******************************************************************************) FUNCTION OutputHeader (name : string) : BOOLEAN; (* Returns TRUE if given file can be copied to output. Returns FALSE if file could not be opened. *) VAR f, length, result, buflen : integer; buf : rbuf; BEGIN (* try and open existing file for reading *) length := Len(name); IF length < maxstring THEN name[length] := CHR(0); (* terminate with NULL *) f := open(name, O_RDONLY, 0); (* read only *) IF length < maxstring THEN name[length] := ' '; (* restore space *) IF f >= 0 THEN BEGIN (* copy f to PSfile *) REPEAT buflen := read(f,buf,maxrbuf); IF buflen > 0 THEN write(PSfile,buf:buflen); UNTIL buflen <= 0; result := close(f); OutputHeader := TRUE; END ELSE OutputHeader := FALSE; (* couldn't open given file *) END; (* OutputHeader *) (******************************************************************************) PROCEDURE BeginPage (DVIpage : INTEGER); BEGIN writeln(PSfile,DVIpage:1,' @bop0'); END; (* BeginPage *) (******************************************************************************) PROCEDURE NewBitmapFont (VAR fontid : string); BEGIN writeln(PSfile,'/',fontid:Len(fontid),' @newfont'); END; (* NewBitmapFont *) (******************************************************************************) PROCEDURE OutputPage (DVIpage : INTEGER); BEGIN writeln(PSfile,DVIpage:1,' @bop1'); END; (* OutputPage *) (******************************************************************************) FUNCTION OutputSpecial (VAR name : string; hpos, vpos : INTEGER) : BOOLEAN; (* Returns TRUE if given file can be copied to output. Returns FALSE if file could not be opened. name can also include a space and additional PostScript text that will be prefixed to the given file as a separate line. This allows users to include a command like "\special{foo.ps 2 2 scale}" in their TeX source. name is declared to be a variable parameter for efficiency reasons. hpos and vpos define the page position of the \special command. It's a good idea to do all specials on a page BEFORE chars and rules so that users can do nifty things like overlaying TeX text onto a shaded box. *) LABEL 888, 999; VAR f, i, j, result, buflen : INTEGER; buf : rbuf; fspec : string; BEGIN (* check name for optional space (indicating additional PostScript text) *) i := 0; fspec := ' '; (* SYSDEP: fill with spaces *) WHILE i < maxstring DO BEGIN IF name[i] = ' ' THEN goto 888; fspec[i] := name[i]; (* extract file spec from name *) i := i + 1; END; 888: (* SYSDEP: test if i = 0 otherwise open will succeed and we'll read some file (fspec = all spaces) full of mildly interesting junk! *) IF i = 0 THEN BEGIN OutputSpecial := FALSE; goto 999; END; (* try and open existing file for reading *) IF i < maxstring THEN fspec[i] := CHR(0); (* terminate with NULL *) f := open(fspec, O_RDONLY, 0); (* read only *) IF i < maxstring THEN fspec[i] := ' '; (* restore space *) IF f >= 0 THEN BEGIN writeln(PSfile, hpos:1, ' ', vpos:1, ' p'); writeln(PSfile, '@bsp'); (* use j to see if there is optional text after file name *) j := maxstring - 1; WHILE name[j] = ' ' DO j := j - 1; j := j + 1; IF i < j THEN BEGIN (* name[i] is first ' '; skip this and copy rest of name to output *) i := i + 1; WHILE i < j DO BEGIN write(PSfile,name[i]); i := i + 1; END; writeln(PSfile); (* text becomes first line of file *) END; (* copy f to PSfile *) REPEAT buflen := read(f,buf,maxrbuf); IF buflen > 0 THEN write(PSfile,buf:buflen); UNTIL buflen <= 0; result := close(f); writeln(PSfile, '@esp'); OutputSpecial := TRUE; END ELSE OutputSpecial := FALSE; (* couldn't open given file *) 999: END; (* OutputSpecial *) (******************************************************************************) PROCEDURE SaveVM (VAR fontid : string); BEGIN writeln(PSfile,'/',fontid:Len(fontid),' @saveVM'); END; (* SaveVM *) (******************************************************************************) PROCEDURE BeginPostScriptFont (VAR fontname : string; scaledsize, mag : INTEGER); (* Output PostScript code to scale and set a resident PostScript font. The fontname will be the name of a TFM file (beginning with psprefix value). This TFM name will need to be converted into a PostScript font name. The scaledsize and mag parameters represent the desired size of the font. *) BEGIN (* sp will convert scaled points to dots *) writeln(PSfile, scaledsize:1, ' sp ', mag:1, ' 1000 div mul ', fontname:Len(fontname), ' PSfont'); (* initialize some globals for first SetPostScriptChar in this font *) curh := 999999999; curv := 999999999; stringlen := 0; pendingch := '?'; END; (* BeginPostScriptFont *) (******************************************************************************) PROCEDURE SetPostScriptChar (ch : CHAR; hpos, vpos, pwidth : INTEGER); (* Similar to SetBitmapChar but we cannot use RELATIVE horizontal positioning because the advance widths of characters in a PostScript font are not an integral number of dots, and we must avoid accumulated rounding errors. *) BEGIN IF curv = vpos THEN BEGIN (* don't update v position *) IF curh <> hpos THEN BEGIN (* update h position *) stringlen := 0; writeln(PSfile,')',pendingch); write(PSfile,hpos:1,'('); pendingch := 'H'; END; END ELSE BEGIN (* update h and v position *) IF stringlen > 0 THEN BEGIN stringlen := 0; writeln(PSfile,')',pendingch); END; write(PSfile,hpos:1,' ',vpos:1,'('); pendingch := 'S'; END; IF (ch >= ' ') AND (ch < CHR(127)) THEN BEGIN IF (ch = '(') OR (ch = ')') OR (ch = '\') THEN (* prefix (,),\ with \ *) write(PSfile,'\',ch) ELSE write(PSfile,ch); END ELSE (* put out 3 octal digits representing ch *) write(PSfile,'\', CHR(ORD('0') + (ORD(ch) DIV 64)), CHR(ORD('0') + ((ORD(ch) DIV 8) MOD 8)), CHR(ORD('0') + (ORD(ch) MOD 8)) ); (* update current page position and string length for next call *) curh := hpos + pwidth; curv := vpos; stringlen := stringlen + 1; IF (stringlen MOD 72) = 0 THEN writeln(PSfile,'\'); END; (* SetPostScriptChar *) (******************************************************************************) PROCEDURE BeginBitmapFont (VAR fontid : string); BEGIN writeln(PSfile,fontid:Len(fontid),' sf'); (* Initialize some globals for first SetBitmapChar in this font. This is not relevant when BeginBitmapFont is used before OutputPage. *) curh := 999999999; curv := 999999999; stringlen := 0; pendingch := '?'; END; (* BeginBitmapFont *) (******************************************************************************) PROCEDURE SetBitmapChar (ch : CHAR; hpos, vpos, pwidth : INTEGER); BEGIN IF curv = vpos THEN BEGIN (* don't update v position *) IF curh <> hpos THEN BEGIN (* update h position (kern or space) *) stringlen := 0; writeln(PSfile,')',pendingch); write(PSfile,hpos-curh:1,'('); pendingch := 'h'; END; END ELSE BEGIN (* update h and v position *) IF stringlen > 0 THEN BEGIN stringlen := 0; writeln(PSfile,')',pendingch); END; write(PSfile,hpos:1,' ',vpos:1,'('); pendingch := 's'; END; IF (ch >= ' ') AND (ch < CHR(127)) THEN BEGIN IF (ch = '(') OR (ch = ')') OR (ch = '\') THEN (* prefix (,),\ with \ *) write(PSfile,'\',ch) ELSE write(PSfile,ch); END ELSE (* put out 3 octal digits representing ch *) write(PSfile,'\', CHR(ORD('0') + (ORD(ch) DIV 64)), CHR(ORD('0') + ((ORD(ch) DIV 8) MOD 8)), CHR(ORD('0') + (ORD(ch) MOD 8)) ); (* update current page position and string length for next call *) curh := hpos + pwidth; curv := vpos; stringlen := stringlen + 1; IF (stringlen MOD 72) = 0 THEN writeln(PSfile,'\'); END; (* SetBitmapChar *) (******************************************************************************) PROCEDURE EndFont; (* Terminate the last "h v(..." or "dh(..." for the current font. *) BEGIN IF stringlen > 0 THEN writeln(PSfile,')',pendingch); END; (* EndFont *) (******************************************************************************) PROCEDURE RestoreVM; BEGIN writeln(PSfile,'@restoreVM'); END; (* RestoreVM *) (******************************************************************************) PROCEDURE SetRule (wd, ht, hpos, vpos : INTEGER); (* Output some PostScript to set the given rule at the given position. *) BEGIN writeln(PSfile, wd:1, ' ', ht:1, ' ', hpos:1, ' ', vpos:1, ' r'); END; (* SetRule *) (******************************************************************************) PROCEDURE EndPage (DVIpage : INTEGER); (* Output some PostScript to end the current page. *) BEGIN writeln(PSfile, DVIpage:1, ' @eop'); END; (* EndPage *) (******************************************************************************) PROCEDURE CloseOutput; (* Output some final PostScript. *) BEGIN writeln(PSfile, '@end'); (* no need to close PSfile *) END; (* CloseOutput *)