unit RtfDoc; { Class for creating RTF document. Author: Phil Hess. Copyright: Copyright (C) 2007 Phil Hess. All rights reserved. License: Modified LGPL. This means you can link your code to this compiled unit (statically in a standalone executable or dynamically in a library) without releasing your code. Only changes to this unit need to be made publicly available. } interface {$IFDEF FPC} {$MODE Delphi} {$ENDIF} uses SysUtils, RtfPars; type TRtfDoc = class(TObject) private FParser : TRTFParser; procedure DoGroup; procedure DoCtrl; procedure DoText; {$IFDEF FPC} procedure HandleError(s : ShortString); {$ELSE} {Delphi} procedure HandleError(s : string); {$ENDIF} protected FFileName : string; FFileVar : TextFile; public constructor Create; destructor Destroy; override; property Parser : TRTFParser read FParser; property FileName : string read FFileName; procedure Start(const FileName : string); virtual; procedure Done; virtual; procedure OutDefaultFontTable(DefFont : Integer); virtual; procedure OutToken( AClass : Integer; Major : Integer; Minor : Integer; Param : Integer; const Text : string); virtual; procedure OutCtrl(Major : Integer; Minor : Integer; Param : Integer); virtual; procedure OutText(const Text : string); virtual; end; implementation constructor TRtfDoc.Create; begin inherited Create; FParser := TRTFParser.Create(nil); end; destructor TRtfDoc.Destroy; begin Parser.Free; inherited Destroy; end; procedure TRtfDoc.Start(const FileName : string); var CurYear : Word; CurMonth : Word; CurDay : Word; CurHour : Word; CurMinute : Word; CurSecond : Word; CurSec100 : Word; begin FFileName := FileName; AssignFile(FFileVar, FFileName); Rewrite(FFileVar); Parser.ResetParser; Parser.ClassCallbacks[rtfGroup] := DoGroup; Parser.ClassCallbacks[rtfText] := DoText; Parser.ClassCallbacks[rtfControl] := DoCtrl; Parser.DestinationCallbacks[rtfFontTbl] := nil; Parser.DestinationCallbacks[rtfColorTbl] := nil; Parser.DestinationCallbacks[rtfInfo] := nil; Parser.OnRTFError := HandleError; OutToken(rtfGroup, rtfBeginGroup, -1, rtfNoParam, ''); OutCtrl(rtfVersion, -1, 1); OutCtrl(rtfCharSet, rtfAnsiCharSet, rtfNoParam); {Output document creation date and time} DecodeDate(Now, CurYear, CurMonth, CurDay); DecodeTime(Now, CurHour, CurMinute, CurSecond, CurSec100); OutToken(rtfGroup, rtfBeginGroup, -1, rtfNoParam, ''); OutCtrl(rtfDestination, rtfInfo, rtfNoParam); OutToken(rtfGroup, rtfBeginGroup, -1, rtfNoParam, ''); OutCtrl(rtfSpecialChar, rtfICreateTime, rtfNoParam); OutCtrl(rtfSpecialChar, rtfIYear, CurYear); OutCtrl(rtfSpecialChar, rtfIMonth, CurMonth); OutCtrl(rtfSpecialChar, rtfIDay, CurDay); OutCtrl(rtfSpecialChar, rtfIHour, CurHour); OutCtrl(rtfSpecialChar, rtfIMinute, CurMinute); OutToken(rtfGroup, rtfEndGroup, -1, rtfNoParam, ''); OutToken(rtfGroup, rtfEndGroup, -1, rtfNoParam, ''); WriteLn(FFileVar); end; {TRtfDoc.Start} procedure TRtfDoc.OutDefaultFontTable(DefFont : Integer); begin {Output default font number} OutCtrl(rtfDefFont, -1, DefFont); {Output font table} OutToken(rtfGroup, rtfBeginGroup, -1, rtfNoParam, ''); OutCtrl(rtfDestination, rtfFontTbl, rtfNoParam); OutToken(rtfGroup, rtfBeginGroup, -1, rtfNoParam, ''); OutCtrl(rtfCharAttr, rtfFontNum, 0); OutToken(rtfControl, rtfFontFamily, rtfFFModern, rtfNoParam, 'Courier New;'); OutToken(rtfGroup, rtfEndGroup, -1, rtfNoParam, ''); OutToken(rtfGroup, rtfBeginGroup, -1, rtfNoParam, ''); OutCtrl(rtfCharAttr, rtfFontNum, 1); OutToken(rtfControl, rtfFontFamily, rtfFFRoman, rtfNoParam, 'Times New Roman;'); OutToken(rtfGroup, rtfEndGroup, -1, rtfNoParam, ''); OutToken(rtfGroup, rtfBeginGroup, -1, rtfNoParam, ''); OutCtrl(rtfCharAttr, rtfFontNum, 2); OutToken(rtfControl, rtfFontFamily, rtfFFSwiss, rtfNoParam, 'Arial;'); OutToken(rtfGroup, rtfEndGroup, -1, rtfNoParam, ''); OutToken(rtfGroup, rtfEndGroup, -1, rtfNoParam, ''); WriteLn(FFileVar); end; {TRtfDoc.OutDefaultFontTable} procedure TRtfDoc.Done; begin WriteLn(FFileVar); OutToken(rtfGroup, rtfEndGroup, -1, rtfNoParam, ''); CloseFile(FFileVar); end; procedure TRtfDoc.OutToken( AClass : Integer; Major : Integer; Minor : Integer; Param : Integer; const Text : string); begin Parser.SetToken(AClass, Major, Minor, Param, Text); Parser.RouteToken; if Text <> '' then Write(FFileVar, Text); end; procedure TRtfDoc.OutCtrl(Major : Integer; Minor : Integer; Param : Integer); begin OutToken(rtfControl, Major, Minor, Param, ''); end; procedure TRtfDoc.OutText(const Text : string); var CharNum : Integer; begin for CharNum := 1 to Length(Text) do OutToken(rtfText, Ord(Text[CharNum]), 0, rtfNoParam, ''); end; procedure TRtfDoc.DoGroup; begin if Parser.rtfMajor = rtfBeginGroup then Write(FFileVar, '{') else Write(FFileVar, '}'); end; procedure TRtfDoc.DoCtrl; var RtfIdx : Integer; begin if (Parser.rtfMajor = rtfSpecialChar) and (Parser.rtfMinor = rtfPar) then WriteLn(FFileVar); {Make RTF file more human readable} RtfIdx := 0; while rtfKey[RtfIdx].rtfKStr <> '' do begin if (Parser.rtfMajor = rtfKey[RtfIdx].rtfKMajor) and (Parser.rtfMinor = rtfKey[RtfIdx].rtfKMinor) then begin Write(FFileVar, '\'); Write(FFileVar, rtfKey[RtfIdx].rtfKStr); if Parser.rtfParam <> rtfNoParam then Write(FFileVar, IntToStr(Parser.rtfParam)); if rtfKey[RtfIdx].rtfKStr <> '*' then Write(FFileVar, ' '); Exit; end; Inc(RtfIdx); end; end; {TRtfDoc.DoCtrl} procedure TRtfDoc.DoText; var AChar : Char; begin {rtfMajor contains the character ASCII code, so just output it for now, preceded by \ if special char.} AChar := Chr(Parser.rtfMajor); case AChar of '\' : Write(FFileVar, '\\'); '{' : Write(FFileVar, '\{'); '}' : Write(FFileVar, '\}'); else begin if AChar > #127 then {8-bit ANSI character?} Write(FFileVar, '\''' + IntToHex(Ord(AChar), 2)) {Encode using 7-bit chars} else {7-bit ANSI character} Write(FFileVar, AChar); end; end; end; {TRtfDoc.DoText} {$IFDEF FPC} procedure TRtfDoc.HandleError(s : ShortString); begin WriteLn(StdErr, s); end; {$ELSE} {Delphi} procedure TRtfDoc.HandleError(s : string); begin WriteLn(ErrOutput, S); end; {$ENDIF} end.