Mega Code Archive

 
Categories / Delphi / Activex OLE
 

Syntax Highlighted Source Code Export to HTML or RTF

Title: Syntax Highlighted Source Code Export to HTML or RTF Question: It was asked for an easy way to export all types of source code to HTML. The Open Source SynEdit components provide this functionality. Using those I created a simple utility to allow for command line driven exporting of most source code to both HTML and RTF formats. Answer: { Syntax Highlighted Source Code Export to HTML or RTF Written and (c) 2002 by Jim McKeeth jim@bsdg.org Binary download here: http://www.bsdg.org/jim/SrcExportBin.zip Source download here: http://www.bsdg.org/jim/SrcExportSrc.zip Article link here: http://www.bsdg.org/jim/SrcExport.html Pascal, Borland Dfm, HTML, CSS, HC11, ADSP21xx, AWK, Baan, Cache, CAC, CPM, Fortran, Foxpro, Galaxy, Dml, General, GWScript, HP48, INI, Inno, Java, JScript, Kix, Modelica, M3, VBScript, Bat, Perl, PHP, Progress, SDD, SQL, SML, TclTk, VB, Asm, Cpp, Python to HTML or RTF with end user customization. \Uses the open source SynEdit component suite. It was asked for an easy way to export all types of source code to HTML. The Open Source SynEdit components provide this functionality. Using those I created a simple utility to allow for command line driven exporting of most source code to both HTML and RTF formats. Note, this was written in Delphi 6 but should work with C++ Builder 3 or better, Delphi 3 or better or Kylix with only minimal changes. First rule when developing with Delphi: No need to reinvent the wheel. Sure, I could have come up with my own routines to format source code to HTML, but why when SynEdit is freely available and works great. Before you start, you will need to download and install the SynEdit suite of components from http://synedit.sourceforge.net/ . There are three main parts to this: Parse the command-line parameters; Verify the parameters; Format the source code. Here is the unit header along with a list of internal supported highlighters. } unit ExportUnit; { Internal supported highlighter keywords Pas Dfm HTML Css HC11 ADSP21xx AWK Baan Cache CAC CPM Fortran Foxpro Galaxy Dml General GWScript HP48 Ini Inno Java JScript Kix Modelica M3 VBScript Bat Perl PHP Progress SDD SQL SML TclTk VB Asm Cpp Python } interface uses Windows, Messages, SysUtils, Variants, Classes, Dialogs, Controls, Forms, SynHighlighterAsm, SynHighlighterVB, SynHighlighterTclTk, SynHighlighterSml, SynHighlighterSQL, SynHighlighterSDD, SynHighlighterPython, SynHighlighterProgress, SynHighlighterPHP, SynHighlighterPerl, SynHighlighterBat, SynHighlighterVBScript, SynHighlighterM3, SynHighlighterModelica, SynHighlighterKix, SynHighlighterJScript, SynHighlighterJava, SynHighlighterInno, SynHighlighterIni, SynHighlighterHtml, SynHighlighterHP48, SynHighlighterGWS, SynHighlighterGeneral, SynHighlighterDml, SynHighlighterGalaxy, SynHighlighterFoxpro, SynHighlighterFortran, SynHighlighterDfm, SynHighlighterCPM, SynHighlighterCss, SynHighlighterCAC, SynHighlighterCache, SynHighlighterCpp, SynHighlighterBaan, SynHighlighterAWK, SynHighlighterADSP21xx, SynHighlighterHC11, SynEditHighlighter, SynHighlighterPas, SynExportRTF, SynEditExport, SynExportHTML, SynHighlighterMulti, StdCtrls, ExtCtrls; { First we need to setup the form. I simple have a large TMemo called memoLog that is set to client justified. Now we add the SynEdit components we need. Simply add one TsynExporterHTML and one TsynExporterRTF from the SynEdit tab. Rename them ExporterHTML and ExporterRTF respeively. Now add the TsynHighlightManager. When you add this component it brings up a dialog allowing you to choose which Highlighters to add. Simple click "Select All" and "Ok" to add one of each. Leave the names as the defaults. } type TformSynEdit = class(TForm) ExporterHTML: TSynExporterHTML; ExporterRTF: TSynExporterRTF; memoLog: TMemo; SynHC11Syn1: TSynHC11Syn; SynADSP21xxSyn1: TSynADSP21xxSyn; SynAWKSyn1: TSynAWKSyn; SynBaanSyn1: TSynBaanSyn; SynCppSyn1: TSynCppSyn; SynCacheSyn1: TSynCacheSyn; SynCACSyn1: TSynCACSyn; SynCssSyn1: TSynCssSyn; SynCPMSyn1: TSynCPMSyn; SynDfmSyn1: TSynDfmSyn; SynFortranSyn1: TSynFortranSyn; SynFoxproSyn1: TSynFoxproSyn; SynGalaxySyn1: TSynGalaxySyn; SynDmlSyn1: TSynDmlSyn; SynGeneralSyn1: TSynGeneralSyn; SynGWScriptSyn1: TSynGWScriptSyn; SynHP48Syn1: TSynHP48Syn; SynHTMLSyn1: TSynHTMLSyn; SynIniSyn1: TSynIniSyn; SynInnoSyn1: TSynInnoSyn; SynJavaSyn1: TSynJavaSyn; SynJScriptSyn1: TSynJScriptSyn; SynKixSyn1: TSynKixSyn; SynModelicaSyn1: TSynModelicaSyn; SynM3Syn1: TSynM3Syn; SynVBScriptSyn1: TSynVBScriptSyn; SynBatSyn1: TSynBatSyn; SynPasSyn1: TSynPasSyn; SynPerlSyn1: TSynPerlSyn; SynPHPSyn1: TSynPHPSyn; SynProgressSyn1: TSynProgressSyn; SynPythonSyn1: TSynPythonSyn; SynSDDSyn1: TSynSDDSyn; SynSQLSyn1: TSynSQLSyn; SynSMLSyn1: TSynSMLSyn; SynTclTkSyn1: TSynTclTkSyn; SynVBSyn1: TSynVBSyn; SynAsmSyn1: TSynAsmSyn; procedure PerformHighlight(const sInFile, sOutFile: string; sceHighlighter: TSynCustomExporter); procedure VerifyParameters(const sInFile: string; sOutFile: string = ''; sHighlighter: string = ''); procedure ParseParameters; procedure FormShow(Sender: TObject); private { Private declarations } public { Public declarations } procedure Log(s: string); end; var formSynEdit: TformSynEdit; {These are some simple string parsing rontines that wrote a long time ago and use in many of my new programs.} function pright(const s, divisor:string):String; function pleft(const s, divisor:string):String; function ReverseStr(const s:String):String; function ValueOf(const S: String):String; function NameOf(const S: String):string; implementation {$R *.dfm} { I have a method for adding lines to he memo called log. I use the ~ or character #126 for line breaks.} procedure TformSynEdit.Log(s: string); begin memoLog.Lines.Add(StringReplace(s,#126,#13#10,[rfReplaceAll])); end; {Returns the portion of the string left of the divisor} function pleft(const s, divisor:string):String; begin if pos(divisor,s)0 then result:=copy(s,1,pos(divisor,s)-1) else result:=s; end; {Returns string in reverse} function ReverseStr(const s:String):String; var ctr:Integer; s2: String; begin s2:=s; for ctr:=1 to length(s) do s2[length(s)-ctr+1]:=s[ctr]; result:=s2; end; {Returns the portion of the string right of the divisor} function pright(const s, divisor:string):String; var rs: String; begin rs:=ReverseStr(s); result:=ReverseStr(PLeft(rs,reverseStr(divisor))); end; {Returns the portion of the string right of the '='} function ValueOf(const s:String):String; begin result:=pright(s,pleft(s,'=')+'='); end; {Returns the portion of the string left of the '='} function NameOf(const s:String):String; begin result:=pleft(s,'='); end; {This is called from the VerifyParamters routine to find a matching highlighter based on the extension of the input file. It works by seperating each extension of the filter as a item in a string list and then look to see if the specified extension is in the list.} function FilterMatch(sExt, sFilter: string):Boolean; var slExts: TStringList; begin slExts := TStringList.Create; try slExts.Delimiter := ';'; slExts.DelimitedText := pright(sFilter,'|'); Result := slExts.indexof('*'+sExt)-1; finally slExts.Free; end; end; {This routine is not currently used, but was used to save some template highlighters to disk.} { function ComponentToFile(Component: TComponent; const sFileName: string) : boolean; var BinStream: TMemoryStream; FileStream: TFileStream; begin BinStream := TMemoryStream.Create; try FileStream := TFileStream.Create(sFileName, fmCreate or fmShareExclusive); try BinStream.WriteComponent(Component); // write the component to the stream BinStream.Seek(0, soFromBeginning); // seek to the origin of the stream // convert the binary representation of the component to easily editable // text format and save it to a FileStream ObjectBinaryToText(BinStream, FileStream); Result:= True; finally FileStream.Free; end; finally BinStream.Free end; end; } {This is the routine used to load the external highlighter as a component.} function FileToComponent(sFileName: string): TComponent; var FileStream: TFileStream; BinStream: TMemoryStream; begin FileStream := TFileStream.Create(sFileName, fmOpenRead or fmShareExclusive); try BinStream := TMemoryStream.Create; try // convert the user editable text format to binary ObjectTextToBinary(FileStream, BinStream); BinStream.Seek(0, soFromBeginning); // seek to the origin of the stream // create the component from the stream Result := BinStream.ReadComponent(nil); finally BinStream.Free; end; finally FileStream.Free; end; end; {-- Parsing command-line parameters -- We accept up to three parameters, but only require one. Here is the usage statement: Command-line usage: SrcFormat IN=(Input File) [OUT=(Output File)] [HIGHLIGHTER=(Highligher Name)] Where IN=(Input File) is the required input file. Example: 'In="C:\My Documents\Source\SrcFormat.dpr"' OUT=(Output File) is the optional output file. Format is based on extension HTML or RTF. Default is same file name and path with an additional '.HTM'. Example: 'Out=C:\Source.RTF' HIGHLIGHTER=(Highlighter Name) is the optional Highlighter to use. If not provided then guessed based on extension. Can also be the file name of a saved Highlighter. Example: 'Highlighter=Pas' Example: 'Highlighter="C:\My Documents\Highlighters\MyPascal.hi"' We only require that they specify the input file, and in fact if they only specify a single parameter, even without the "IN=" prefix, then we assume it is the input file. We attempt an educated guess on the rest. Here is the code we can use to parse the command-line parameters:} procedure TformSynEdit.ParseParameters; var sInFile, sOutFile, sHighlighter : string; iCtr: Integer; begin if (ParamCount=1) and (FileExists(ParamStr(1))) then sInFile := ParamStr(1) // if only one then it is the input file else if ParamCount0 then for iCtr := 1 to ParamCount do // spin though the parameters begin if CompareText(NameOf(ParamStr(iCtr)),'IN')=0 then sInFile := ValueOf(ParamStr(iCtr)) // Input file else if CompareText(NameOf(ParamStr(iCtr)),'OUT')=0 then sOutFile := ValueOf(ParamStr(iCtr)) // Output file else if CompareText(NameOf(ParamStr(iCtr)),'HIGHLIGHTER')=0 then sHighlighter := ValueOf(ParamStr(iCtr)) // highlighter end else begin // explain the usage Log('Command-line usage: '#126+ ' SrcFormat IN=(Input File) [OUT=(Output File)] '+ '[HIGHLIGHTER=(Highligher Name)]'+#126+#126+ 'Where'+#126+ ' IN=(Input File) is the required input file. '+#126+ ' Example: ''In="C:\My Documents\Source\SrcFormat.dpr"'''+#126+ ' OUT=(Output File) is the optional output file. '+#126+ ' Format is based on extension HTML or RTF.'+#126+ ' Default is same file name and path with an additional ''.HTM''.' +#126+ ' Example: ''Out=C:\Source.RTF'''+#126+ ' HIGHLIGHTER=(Highlighter Name) is the Highlighter to use.' +#126+ ' If not provided then guessed based on extension. '+#126+ ' Can also be the file name of a saved Highlighter.'+#126+ ' Example: ''Highlighter=Pas'''+#126+ ' Example: ''Highlighter="C:\My Documents\SrcExport\MyPascal.hi"''' ); Exit; end; // Finally we pass all the variables to the VerifyParameters routine. VerifyParameters(sInFile, sOutFile, sHighlighter); end; {You could actually call this routine from a GUI interface as well as the ParseParameters method, but I will let you add that functionality. I'll step you through each section of this routine.} procedure TformSynEdit.VerifyParameters(const sInFile: string; sOutFile, sHighlighter: string); var sInExt, sOutExt : string; myExporter: TSynCustomExporter; iCtr : Integer; begin {First verify that the input file does exist. We cannot format something that has not been saved to disk yet (although that would make a great Delphi Expert!) We simply add a log line and exit if the file is non-existent.} if not FileExists(sInFile) then begin Log('The input file "'+sInFile+'" does not exist'); Exit; end; {If they did not specify an output file then we append an 'HTM' extension.} if sOutFile='' then sOutFile := sInFile + '.HTM'; {Make sure the output file does not exist.} if FileExists(sOutFile) then try DeleteFile(sOutFile); except log('Output file exists and cannot be deleted'); Exit; end; {Make sure we can create the output file.} try // Make sure we can create the path ForceDirectories(ExtractFilePath(sOutFile)); // Create and close a test file FileClose(FileCreate(sOutFile)); except log('Cannot create output file!'); Exit; end; {Extract the extensions of the files for guessing the highlighter and format.} sInExt := UpperCase(ExtractFileExt(sInFile)); sOutExt := UpperCase(ExtractFileExt(sOutFile)); {Now we guess the export format. If it is not an .RTF extension then we assume HTML.} if sOutExt = '.RTF' then begin log('Exporting to RTF'); myExporter := ExporterRTF; end else begin log('Exporting to HTML'); myExporter := ExporterHTML; end; {Now we guess the highlighter. To do this we will spin through all the DefaultFilter properties of the highlighters we included on the form. Since we stop on the first match, you may want to change the creation order (by right clicking on the form) to put your most common highlighters first. } myExporter.Highlighter := nil; // only do with if no highlighter was specified at the command-line if sHighlighter = '' then begin for iCtr := 0 to pred(ComponentCount) do // go through all the componets // only look at highlighters if Components[iCtr] is TSynCustomHighlighter then // use the filter match method to see if the extension matches the filter if FilterMatch(sInExt, (Components[iCtr] as TSynCustomHighlighter).DefaultFilter) then begin // Set the name of the highlighter as the meaningful part of the // component name. sHighlighter := Copy(Components[iCtr].Name,4, Length(Components[iCtr].Name)-7); // set the actual highlighter property of the exporter myExporter.Highlighter := Components[iCtr] as TSynCustomHighlighter; // no more looping, we have what we want. Break; end; end; if sHighlighter = '' then begin // we didn't find an internal one, but we might find an external one. log('No highlighter was found for the extension '+sInExt); end; // if they specified a highlighter at the command line find it now. if (myExporter.Highlighter = nil) and (sHighlighter '') then for iCtr := 0 to pred(ComponentCount) do if Components[iCtr] is TSynCustomHighlighter then if CompareText(Components[iCtr].Name,'Syn'+sHighlighter+'Syn1')=0 then begin myExporter.Highlighter := Components[iCtr] as TSynCustomHighlighter; Break; end; // we still don't have a highlighter but one was specified if (myExporter.Highlighter = nil) and (sHighlighter '') then begin log('No internal highlighter named '''+sHighlighter+''' found!'); // but there is a file with the same name as the specified highlighter, // maybe it is an external highlighter! if FileExists(sHighlighter) then begin log('Loading highlighter: '+sHighlighter); // before you can load a component you need to register the class. RegisterClasses([TSynExporterHTML, TSynExporterRTF, TSynPasSyn, TSynDfmSyn, TSynHTMLSyn, TSynCssSyn, TSynHC11Syn, TSynADSP21xxSyn, TSynAWKSyn,TSynBaanSyn, TSynCacheSyn, TSynCACSyn, TSynCPMSyn, TSynFortranSyn, TSynFoxproSyn, TSynGalaxySyn, TSynDmlSyn, TSynGeneralSyn, TSynGWScriptSyn, TSynHP48Syn, TSynIniSyn, TSynInnoSyn, TSynJavaSyn, TSynJScriptSyn, TSynKixSyn, TSynModelicaSyn, TSynM3Syn, TSynVBScriptSyn, TSynBatSyn, TSynPerlSyn, TSynPHPSyn, TSynProgressSyn, TSynSDDSyn, TSynSQLSyn, TSynSMLSyn, TSynTclTkSyn, TSynVBSyn, TSynAsmSyn, TSynCppSyn, TSynPythonSyn, TSynPasSyn]); try // try to load the component myExporter.Highlighter := FileToComponent(sHighlighter) as TSynCustomHighlighter; except // failed to load the component, it must have been invalid! log('External highlighter named '''+sHighlighter+''' is inavlid!'); Exit; end; end else begin log('No external highlighter named '''+sHighlighter+''' found!'); Exit; end; end; // Note: if not highlighter was specifed, and none can be found based on // extension then we can export without a highlighter which results in // no syntax highlighting, but does change the format. // nothing caused us to exit along the way, we must have everything we need. // list it out in the log window Log('Intput file: '+sInFile+#126+ 'Output file: '+sOutFile+#126+ 'Highlighter: '+sHighlighter); // Now we call Perform Highlight with the final parameters. PerformHighlight(sInFile, sOutFile, myExporter); end; { After all that work, all we did is protect the actual functionality of this program from the user. We should now have valid parameters for this method. } procedure TformSynEdit.PerformHighlight(const sInFile, sOutFile: string; sceHighlighter: TSynCustomExporter); var slSrc : TStringList; begin slSrc := TStringList.Create; // to load the source code into try // load the source code from disk slSrc.LoadFromFile(sInFile); sceHighlighter.ExportAsText := True; // Might be a good idea to make this user definable at some point, but for // now we will just us a generic title sceHighlighter.Title := ExtractFileName(sInFile)+' source code'; // Read in the source code and convert it to the highlighter format sceHighlighter.ExportAll(slSrc); // Save the output to disk. sceHighlighter.SaveToFile(sOutFile); finally slSrc.Free; // all done end; end; {Now assign an event handler to the Form Show event. You could use a button instead if you wanted. You might also want to close when it is done.} procedure TformSynEdit.FormShow(Sender: TObject); begin Application.ProcessMessages; // finish drawing the form ParseParameters; // Get Started. end; end.