{************************************************} { } { Printer output filter exammple } { Copyright (c) 1992 by Borland International } { } {************************************************} program PrinterOutputFilter; { Printer filters read input from the IDE by way of StdIn (by using Read or ReadLn). It then converts the syntax highlight codes inserted into the text into appropriate printer command codes. This converted text is then output Lst (which defaults to LPT1). The syntax highlight codes are in the form of #, where '#' is an ASCII digit from 1($31) to 8($38). The last code sent remains in effect until another code is found. The following is a list of the codes and what type of text they represent: 1 - Whitespace (space, tab) 2 - Comment 3 - Reserved word (begin, end, procedure, etc...) 4 - Identifier (Writeln, Reset, etc...) 5 - Symbol (;, :, ., etc...) 6 - String ('string', #32, #$30) 7 - Number (24, $56) 8 - Assembler (asm mov ax,5 end;) The following printers are supported: EPSON and compatibles HP LaserJet II, III, IIP, IID, IIID, IIISi and compatibles (Italics are available on IIIx, IIP) ADOBE(R) PostScript(R) ASCII (simply strips the highlight codes before sending to Lst) Command line options: /EPSON - Output EPSON printer codes /HP - Output HP LaserJet codes /PS - Output PostScript /ASCII - Strip highlight codes (Default) /Lxx - Lines per page (Default 55) /Txx - Tabsize (Default 8) /O[file] - Output to file or device (Default LPT1) } {$M 2048, 0, 0} {$I-,S-,X+} const MaxAttributes = 8; type TPCharArray = array[0..16380] of PChar; PPCharArray = ^TPCharArray; PPrinterCodes = ^TPrinterCodes; TPrinterCodes = record { Number of preamble strings in the Preamble array. } PreambleCount: Byte; { Pointer to an array of PChars that define the preamble sequence for this printer. Sent at the start of a print job. } Preamble: PPCharArray; { Pointer to an array of PChars that define the code sequences for changing the current attribute. } CodeArray: PPCharArray; { Array of indexes into the CodeArray corresponing to attributes supported for this printer. } Attributes: array[0..MaxAttributes - 1] of Byte; { Codes sent at the start of a page. } StartPage: PChar; { Codes sent at the end of a page. } EndPage: PChar; { Codes sent at the end of a line. } EndLine: PChar; { Codes sent at the end of the print job. } Postamble: PChar; end; const { EPSON Printer code definition } EpsonItalic = #27'4'; EpsonNoItalic = #27'5'; EpsonBold = #27'E'; EpsonNoBold = #27'F'; EpsonULine = #27'-'#1; EpsonNoULine = #27'-'#0; EpsonCodeArray: array[0..7] of PChar = ( EpsonBold, EpsonNoBold, EpsonItalic, EpsonNoItalic, EpsonULine, EpsonNoULine, EpsonBold + EpsonItalic, EpsonNoBold + EpsonNoItalic); EpsonCodes: TPrinterCodes = ( PreambleCount: 0; Preamble: nil; CodeArray: @EpsonCodeArray; Attributes: ( 0, { Whitespace } 2, { Comment } 1, { Reserved word } 0, { Identifier } 0, { Symbol } 4, { String } 0, { Number } 1); { Assembler } StartPage: ''; EndPage: #12; EndLine: #13#10; Postamble: '' ); { HP LaserJet code definition } HPInit = #27'E'#27'(10U'#27'&k0S'#27'(s3T'; HPItalic = #27'(s1S'; HPNoItalic = #27'(s0S'; HPBold = #27'(s3B'; HPNoBold = #27'(s0B'; HPULine = #27'&dD'; HPNoULine = #27'&d@'; HPCodeArray: array[0..7] of PChar = ( HPBold, HPNoBold, HPItalic, HPNoItalic, HPULine, HPNoULine, HPBold + HPItalic, HPNoBold + HPNoItalic); LaserJetPreamble: PChar = HPInit; LaserJetCodes: TPrinterCodes = ( PreambleCount: 1; Preamble: @LaserJetPreamble; CodeArray: @HPCodeArray; Attributes: ( 0, { Whitespace } 2, { Comment } 1, { Reserved word } 0, { Identifier } 0, { Symbol } 4, { String } 0, { Number } 1); { Assembler } StartPage: ''; EndPage: #12; EndLine: #13#10; Postamble: #12 ); { Raw ASCII definition } AsciiCodes: TPrinterCodes = ( PreambleCount: 0; Preamble: nil; CodeArray: nil; Attributes: ( 0, { Whitespace } 0, { Comment } 0, { Reserved word } 0, { Identifier } 0, { Symbol } 0, { String } 0, { Number } 0); { Assembler } StartPage: ''; EndPage: #12; EndLine: #13#10; Postamble: '' ); { PostScript code definition } PSPreamble0 = #4'%!PS-Adobe-3.0'#13#10+ 'initgraphics'#13#10; PSPreamble1 = '/fnr /Courier findfont 10 scalefont def'#13#10; PSPreamble2 = '/fni /Courier-Oblique findfont 10 scalefont def'#13#10; PSPreamble3 = '/fnb /Courier-Bold findfont 10 scalefont def'#13#10; PSPreamble4 = '/fnbi /Courier-BoldOblique findfont 10 scalefont def'#13#10; PSPreamble5 = '/newl {20 currentpoint exch pop 12 sub moveto} def'#13#10+ '/newp {20 765 moveto} def'#13#10+ 'fnr setfont'#13#10; PSNormal = 'fnr setfont'#13#10; PSItalic = 'fni setfont'#13#10; PSBold = 'fnb setfont'#13#10; PSBoldItalic = 'fnbi setfont'#13#10; PSCodeArray: array[0..5] of PChar = ( PSBold, PSNormal, PSItalic, PSNormal, PSBoldItalic, PSNormal); PSPreamble: array[0..5] of PChar = ( PSPreamble0, PSPreamble1, PSPreamble2, PSPreamble3, PSPreamble4, PSPreamble5); PSCodes: TPrinterCodes = ( PreambleCount: High(PSPreamble) - Low(PSPreamble) + 1; Preamble: @PSPreamble; CodeArray: @PSCodeArray; Attributes: ( 0, { Whitespace } 2, { Comment } 1, { Reserved word } 0, { Identifier } 0, { Symbol } 3, { String } 0, { Number } 1); { Assembler } StartPage: 'newp'#13#10; EndPage: 'showpage'#13#10; EndLine: 'newl'#13#10; Postamble: #4 ); { Special case printer modes. This facilitates indicating a special case printer such as PostScript } pmNormal = $0001; pmPostScript = $0002; PrintMode: Word = pmNormal; LinesPerPage: Word = 55; ToFile: Boolean = False; TabSize: Word = 8; var C, LineCount, TabCount: Integer; Line, OutputLine: String; InputBuffer: array[0..4095] of Char; PrinterCodes: PPrinterCodes; CurCode, NewCode: Byte; AKey: Word; Lst: Text; procedure UpStr(var S: String); var I: Integer; begin for I := 1 to Length(S) do S[I] := UpCase(S[I]); end; { Checks whether or not the Text file is a device. If so, it is forced to "raw" mode } procedure SetDeviceRaw(var T: Text); assembler; asm LES DI,T MOV BX,WORD PTR ES:[DI] MOV AX,4400H INT 21H TEST DX,0080H JZ @@1 OR DL,20H MOV DH,DH MOV AX,4401H INT 21H @@1: end; { Process the command line. If any new printers are to be supported, simply add a command line switch here. } procedure ProcessCommandLine; var Param: String; I: Integer; function ParamVal(var P: String; Default: Word): Word; var N, E: Integer; begin Delete(P, 1, 1); Val(P, N, E); if E = 0 then ParamVal := N else ParamVal := Default; end; begin PrinterCodes := @AsciiCodes; for I := 1 to ParamCount do begin Param := ParamStr(I); if (Length(Param) >= 2) and ((Param[1] = '/') or (Param[1] = '-')) then begin Delete(Param, 1, 1); UpStr(Param); if Param = 'EPSON' then PrinterCodes := @EpsonCodes else if Param = 'HP' then PrinterCodes := @LaserJetCodes else if Param = 'ASCII' then PrinterCodes := @AsciiCodes else if Param = 'PS' then begin PrinterCodes := @PSCodes; PrintMode := pmPostScript; end else if Param[1] = 'L' then LinesPerPage := ParamVal(Param, LinesPerPage) else if Param[1] = 'T' then TabSize := ParamVal(Param, TabSize) else if Param[1] = 'O' then begin Delete(Param, 1, 1); Assign(Lst, Param); Rewrite(Lst); ToFile := True; SetDeviceRaw(Lst); end; end; end; if not ToFile then begin Assign(Lst, 'LPT1'); Rewrite(Lst); SetDeviceRaw(Lst); end; end; { Flush the currently assembled string to the output } procedure PurgeOutputBuf; begin if OutputLine = '' then Exit; case PrintMode of pmNormal: Write(Lst, OutputLine); pmPostScript: begin Write(Lst, '('); Write(Lst, OutputLine); Write(Lst, ') show'#13#10); end; end; OutputLine := ''; if IOResult <> 0 then Halt(1); end; { Add the chracter to the output string. Process special case characters and tabs, purging the output buffer when nessesary } procedure AddToOutputBuf(AChar: Char); var I: Integer; begin case AChar of '(',')','\': begin case PrintMode of pmPostScript: begin if Length(OutputLine) > 253 then PurgeOutputBuf; Inc(OutputLine[0]); OutputLine[Length(OutputLine)] := '\'; end; end; end; #9: begin if Length(OutputLine) > (255 - TabSize) then PurgeOutputBuf; for I := 1 to TabSize - (TabCount mod TabSize) do begin Inc(OutputLine[0]); OutputLine[Length(OutputLine)] := ' '; end; Inc(TabCount, TabSize - (TabCount mod TabSize)); Exit; end; end; if Length(OutputLine) > 254 then PurgeOutputBuf; Inc(OutputLine[0]); OutputLine[Length(OutputLine)] := AChar; Inc(TabCount); end; { End the current page and start a new one } procedure NewPage(const PCodes: TPrinterCodes); begin PurgeOutputBuf; Write(Lst, PCodes.EndPage); Write(Lst, PCodes.StartPage); LineCount := 0; TabCount := 0; end; { End the current line } procedure NewLine(const PCodes: TPrinterCodes); begin PurgeOutputBuf; Write(Lst, PCodes.EndLine); Inc(LineCount); TabCount := 0; if LineCount > LinesPerPage then NewPage(PCodes); end; { Check for the presence of a keypressed and return it if available } function GetKey(var Key: Word): Boolean; assembler; asm MOV AH,1 INT 16H MOV AL,0 JE @@1 XOR AH,AH INT 16H LES DI,Key MOV WORD PTR ES:[DI],AX MOV AL,1 @@1: end; begin SetTextBuf(Input, InputBuffer); ProcessCommandLine; LineCount := 0; with PrinterCodes^ do begin if PreambleCount > 0 then for C := 0 to PreambleCount - 1 do Write(Lst, Preamble^[C]); if IOResult <> 0 then Halt(1); LineCount := 0; CurCode := $FF; TabCount := 0; Write(Lst, StartPage); Line := ''; while True do begin if (Line = '') and Eof then begin PurgeOutputBuf; Break; end; ReadLn(Line); if GetKey(AKey) and (AKey = $011B) then Halt(1); C := 1; while C <= length(Line) do begin case Line[C] of #27: if (Line[C + 1] >= '1') and (Line[C + 1] <= '8') then begin NewCode := Attributes[Byte(Line[C + 1]) - $31]; if NewCode <> CurCode then begin PurgeOutputBuf; if (CurCode > 0) and (CurCode < MaxAttributes) then Write(Lst, CodeArray^[(CurCode - 1) * 2 + 1]); if (NewCode > 0) and (NewCOde < MaxAttributes) then Write(Lst, CodeArray^[(NewCode - 1) * 2]); CurCode := NewCode; end; Inc(C); end; #12: NewPage(PrinterCodes^); else AddToOutputBuf(Line[C]); end; Inc(C); end; NewLine(PrinterCodes^); end; if LineCount > 0 then Write(Lst, EndPage); Write(Lst, Postamble); end; Close(Lst); end.