Articles   Members Online:
-Article/Tip Search
-News Group Search over 21 Million news group articles.
-Delphi/Pascal
-CBuilder/C++
-C#Builder/C#
-JBuilder/Java
-Kylix
Member Area
-Home
-Account Center
-Top 10 NEW!!
-Submit Article/Tip
-Forums Upgraded!!
-My Articles
-Edit Information
-Login/Logout
-Become a Member
-Why sign up!
-Newsletter
-Chat Online!
-Indexes NEW!!
Employment
-Build your resume
-Find a job
-Post a job
-Resume Search
Contacts
-Contacts
-Feedbacks
-Link to us
-Privacy/Disclaimer
Embarcadero
Visit Embarcadero
Embarcadero Community
JEDI
Links
Syntax Highlighted Source Code Export to HTML or RTF Turn on/off line numbers in source code. Switch to Orginial background IDE or DSP color Comment or reply to this aritlce/tip for discussion. Bookmark this article to my favorite article(s). Print this article
07-Jul-03
Category
Reporting /Printing
Language
Delphi 3.x
Views
202
User Rating
No Votes
# Votes
0
Replies
0
Publisher:
DSP, Administrator
Reference URL:
DKB
			Author: Jim McKeeth

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:

1   {
2   Syntax Highlighted Source Code Export to HTML or RTF
3   Written and (c) 2002 by Jim McKeeth jim@bsdg.org
4   
5   Pascal, Borland Dfm, HTML, CSS, HC11, ADSP21xx, AWK, Baan, Cache,
6   CAC, CPM, Fortran, Foxpro, Galaxy, Dml, General, GWScript, HP48, INI, Inno, Java, 
7   JScript, Kix, Modelica, M3, VBScript, Bat, Perl, PHP, Progress, SDD, SQL, SML, 
8   TclTk, VB, Asm, Cpp, Python to HTML or RTF with end user customization.
9   \Uses the open source SynEdit component suite.
10  
11  It was asked for an easy way to export all types of source code to HTML.  The Open 
12  Source SynEdit components provide this functionality.  Using those I created a 
13  simple utility to allow for command line driven exporting of most source code to 
14  both HTML and RTF formats.
15  
16  Note, this was written in Delphi 6 but should work with C++ Builder 3 or better, 
17  Delphi 3 or better or Kylix with only minimal changes.
18  
19  First rule when developing with Delphi: No need to reinvent the wheel.  Sure, I 
20  could have come up with my own routines to format source code to HTML, but why when 
21  SynEdit is freely available and works great.  Before you start, you will need to 
22  download and install the SynEdit suite of components from
23  http://synedit.sourceforge.net/ .
24  
25  There are three main parts to this: Parse the command-line parameters;  Verify the 
26  parameters;  Format the source code.
27  
28  Here is the unit header along with a list of internal supported highlighters.
29  }
30  unit ExportUnit;
31  
32  { Internal supported highlighter keywords
33  Pas
34  Dfm
35  HTML
36  Css
37  HC11
38  ADSP21xx
39  AWK
40  Baan
41  Cache
42  CAC
43  CPM
44  Fortran
45  Foxpro
46  Galaxy
47  Dml
48  General
49  GWScript
50  HP48
51  Ini
52  Inno
53  Java
54  JScript
55  Kix
56  Modelica
57  M3
58  VBScript
59  Bat
60  Perl
61  PHP
62  Progress
63  SDD
64  SQL
65  SML
66  TclTk
67  VB
68  Asm
69  Cpp
70  Python
71  }
72  
73  interface
74  
75  uses
76    Windows, Messages, SysUtils, Variants, Classes, Dialogs, Controls, Forms,
77      SynHighlighterAsm, SynHighlighterVB, SynHighlighterTclTk, SynHighlighterSml,
78      SynHighlighterSQL, SynHighlighterSDD, SynHighlighterPython, 
79  SynHighlighterProgress,
80      SynHighlighterPHP, SynHighlighterPerl, SynHighlighterBat, 
81  SynHighlighterVBScript,
82      SynHighlighterM3, SynHighlighterModelica, SynHighlighterKix, 
83  SynHighlighterJScript,
84      SynHighlighterJava, SynHighlighterInno, SynHighlighterIni, SynHighlighterHtml,
85      SynHighlighterHP48, SynHighlighterGWS, SynHighlighterGeneral, SynHighlighterDml,
86      SynHighlighterGalaxy, SynHighlighterFoxpro, SynHighlighterFortran,
87      SynHighlighterDfm, SynHighlighterCPM, SynHighlighterCss, SynHighlighterCAC,
88      SynHighlighterCache, SynHighlighterCpp, SynHighlighterBaan, SynHighlighterAWK,
89      SynHighlighterADSP21xx, SynHighlighterHC11, SynEditHighlighter, 
90  SynHighlighterPas,
91      SynExportRTF, SynEditExport, SynExportHTML, SynHighlighterMulti, StdCtrls, 
92  ExtCtrls;
93  
94  {
95  First we need to setup the form.  I simple have a large TMemo called memoLog that 
96  is set to client justified.  Now we add the SynEdit components we need.
97  Simply add one TsynExporterHTML and one TsynExporterRTF from the SynEdit tab.
98  Rename them ExporterHTML and ExporterRTF respeively.  Now add the
99  TsynHighlightManager.  When you add this component it brings up a dialog allowing 
100 you to choose which Highlighters to add.  Simple click "Select All" and "Ok" to add 
101 one of each.  Leave the names as the defaults.
102 }
103 
104 type
105   TformSynEdit = class(TForm)
106     ExporterHTML: TSynExporterHTML;
107     ExporterRTF: TSynExporterRTF;
108     memoLog: TMemo;
109     SynHC11Syn1: TSynHC11Syn;
110     SynADSP21xxSyn1: TSynADSP21xxSyn;
111     SynAWKSyn1: TSynAWKSyn;
112     SynBaanSyn1: TSynBaanSyn;
113     SynCppSyn1: TSynCppSyn;
114     SynCacheSyn1: TSynCacheSyn;
115     SynCACSyn1: TSynCACSyn;
116     SynCssSyn1: TSynCssSyn;
117     SynCPMSyn1: TSynCPMSyn;
118     SynDfmSyn1: TSynDfmSyn;
119     SynFortranSyn1: TSynFortranSyn;
120     SynFoxproSyn1: TSynFoxproSyn;
121     SynGalaxySyn1: TSynGalaxySyn;
122     SynDmlSyn1: TSynDmlSyn;
123     SynGeneralSyn1: TSynGeneralSyn;
124     SynGWScriptSyn1: TSynGWScriptSyn;
125     SynHP48Syn1: TSynHP48Syn;
126     SynHTMLSyn1: TSynHTMLSyn;
127     SynIniSyn1: TSynIniSyn;
128     SynInnoSyn1: TSynInnoSyn;
129     SynJavaSyn1: TSynJavaSyn;
130     SynJScriptSyn1: TSynJScriptSyn;
131     SynKixSyn1: TSynKixSyn;
132     SynModelicaSyn1: TSynModelicaSyn;
133     SynM3Syn1: TSynM3Syn;
134     SynVBScriptSyn1: TSynVBScriptSyn;
135     SynBatSyn1: TSynBatSyn;
136     SynPasSyn1: TSynPasSyn;
137     SynPerlSyn1: TSynPerlSyn;
138     SynPHPSyn1: TSynPHPSyn;
139     SynProgressSyn1: TSynProgressSyn;
140     SynPythonSyn1: TSynPythonSyn;
141     SynSDDSyn1: TSynSDDSyn;
142     SynSQLSyn1: TSynSQLSyn;
143     SynSMLSyn1: TSynSMLSyn;
144     SynTclTkSyn1: TSynTclTkSyn;
145     SynVBSyn1: TSynVBSyn;
146     SynAsmSyn1: TSynAsmSyn;
147     procedure PerformHighlight(const sInFile, sOutFile: string;
148       sceHighlighter: TSynCustomExporter);
149     procedure VerifyParameters(const sInFile: string; sOutFile: string = '';
150       sHighlighter: string = '');
151     procedure ParseParameters;
152     procedure FormShow(Sender: TObject);
153   private
154     { Private declarations }
155   public
156     { Public declarations }
157     procedure Log(s: string);
158 
159   end;
160 
161 var
162   formSynEdit: TformSynEdit;
163 
164   {These are some simple string parsing rontines that wrote a long time ago and
165   use in many of my new programs.}
166 function pright(const s, divisor: string): string;
167 function pleft(const s, divisor: string): string;
168 function ReverseStr(const s: string): string;
169 function ValueOf(const S: string): string;
170 function NameOf(const S: string): string;
171 
172 implementation
173 
174 {$R *.dfm}
175 
176 { I have a method for adding lines to he memo called log.
177 I use the ~ or character #126 for line breaks.}
178 
179 procedure TformSynEdit.Log(s: string);
180 begin
181   memoLog.Lines.Add(StringReplace(s, #126, #13#10, [rfReplaceAll]));
182 end;
183 
184 {Returns the portion of the string left of the divisor}
185 
186 function pleft(const s, divisor: string): string;
187 begin
188   if pos(divisor, s) > 0 then
189     result := copy(s, 1, pos(divisor, s) - 1)
190   else
191     result := s;
192 end;
193 
194 {Returns string in reverse}
195 
196 function ReverseStr(const s: string): string;
197 var
198   ctr: Integer;
199   s2: string;
200 begin
201   s2 := s;
202   for ctr := 1 to length(s) do
203     s2[length(s) - ctr + 1] := s[ctr];
204   result := s2;
205 end;
206 
207 {Returns the portion of the string right of the divisor}
208 
209 function pright(const s, divisor: string): string;
210 var
211   rs: string;
212 begin
213   rs := ReverseStr(s);
214   result := ReverseStr(PLeft(rs, reverseStr(divisor)));
215 end;
216 
217 {Returns the portion of the string right of the '='}
218 
219 function ValueOf(const s: string): string;
220 begin
221   result := pright(s, pleft(s, '=') + '=');
222 end;
223 
224 {Returns the portion of the string left of the '='}
225 
226 function NameOf(const s: string): string;
227 begin
228   result := pleft(s, '=');
229 end;
230 
231 {This is called from the VerifyParamters routine to find a matching highlighter 
232 based on the extension of the input file.  It works by seperating each extension of 
233 the filter as a item in a string list and then look to see if the specified 
234 extension is in the list.}
235 
236 function FilterMatch(sExt, sFilter: string): Boolean;
237 var
238   slExts: TStringList;
239 begin
240   slExts := TStringList.Create;
241   try
242     slExts.Delimiter := ';';
243     slExts.DelimitedText := pright(sFilter, '|');
244     Result := slExts.indexof('*' + sExt) > -1;
245   finally
246     slExts.Free;
247   end;
248 end;
249 
250 {This routine is not currently used, but was used to save some template 
251 highlighters to disk.}
252 {
253 function ComponentToFile(Component: TComponent; const sFileName: string)
254            : boolean;
255 var
256   BinStream: TMemoryStream;
257   FileStream: TFileStream;
258 begin
259   BinStream := TMemoryStream.Create;
260   try
261     FileStream := TFileStream.Create(sFileName, fmCreate or fmShareExclusive);
262     try
263       BinStream.WriteComponent(Component);  // write the component to the stream
264       BinStream.Seek(0, soFromBeginning);  // seek to the origin of the stream
265       // convert the binary representation of the component to easily editable
266       // text format and save it to a FileStream
267       ObjectBinaryToText(BinStream, FileStream);
268       Result:= True;
269     finally
270       FileStream.Free;
271     end;
272   finally
273     BinStream.Free
274   end;
275 end;
276 }
277 
278 {This is the routine used to load the external highlighter as a component.}
279 
280 function FileToComponent(sFileName: string): TComponent;
281 var
282   FileStream: TFileStream;
283   BinStream: TMemoryStream;
284 begin
285   FileStream := TFileStream.Create(sFileName, fmOpenRead or fmShareExclusive);
286   try
287     BinStream := TMemoryStream.Create;
288     try
289       // convert the user editable text format to binary
290       ObjectTextToBinary(FileStream, BinStream);
291       BinStream.Seek(0, soFromBeginning); // seek to the origin of the stream
292       // create the component from the stream
293       Result := BinStream.ReadComponent(nil);
294     finally
295       BinStream.Free;
296     end;
297   finally
298     FileStream.Free;
299   end;
300 end;
301 
302 {-- Parsing command-line parameters --
303 
304 We accept up to three parameters, but only require one.
305 Here is the usage statement:
306 
307 Command-line usage:
308 > SrcFormat IN=(Input File) [OUT=(Output File)] [HIGHLIGHTER=(Highligher Name)]
309 
310 Where
311 > IN=(Input File) is the required input file.
312   Example: 'In="C:\My Documents\Source\SrcFormat.dpr"'
313 > OUT=(Output File) is the optional output file.
314   Format is based on extension HTML or RTF.
315   Default is same file name and path with an additional '.HTM'.
316   Example: 'Out=C:\Source.RTF'
317 > HIGHLIGHTER=(Highlighter Name) is the optional Highlighter to use.
318   If not provided then guessed based on extension.
319   Can also be the file name of a saved Highlighter.
320   Example: 'Highlighter=Pas'
321   Example: 'Highlighter="C:\My Documents\Highlighters\MyPascal.hi"'
322 
323 We only require that they specify the input file, and in fact if they only
324 specify a single parameter, even without the "IN=" prefix, then we assume it is
325 the input file.  We attempt an educated guess on the rest.
326 
327 Here is the code we can use to parse the command-line parameters:}
328 
329 procedure TformSynEdit.ParseParameters;
330 var
331   sInFile, sOutFile, sHighlighter: string;
332   iCtr: Integer;
333 begin
334   if (ParamCount = 1) and (FileExists(ParamStr(1))) then
335     sInFile := ParamStr(1) // if only one then it is the input file
336   else if ParamCount > 0 then
337     for iCtr := 1 to ParamCount do // spin though the parameters
338     begin
339       if CompareText(NameOf(ParamStr(iCtr)), 'IN') = 0 then
340         sInFile := ValueOf(ParamStr(iCtr)) // Input file
341       else if CompareText(NameOf(ParamStr(iCtr)), 'OUT') = 0 then
342         sOutFile := ValueOf(ParamStr(iCtr)) // Output file
343       else if CompareText(NameOf(ParamStr(iCtr)), 'HIGHLIGHTER') = 0 then
344         sHighlighter := ValueOf(ParamStr(iCtr)) // highlighter
345     end
346   else
347   begin // explain the usage
348     Log('Command-line usage: '#126 +
349       '> SrcFormat IN=(Input File) [OUT=(Output File)] ' +
350       '[HIGHLIGHTER=(Highligher Name)]' + #126 + #126 +
351       'Where' + #126 +
352       '> IN=(Input File) is the required input file.  ' + #126 +
353       '  Example: ''In="C:\My Documents\Source\SrcFormat.dpr"''' + #126 +
354       '> OUT=(Output File) is the optional output file.  ' + #126 +
355       '  Format is based on extension HTML or RTF.' + #126 +
356       '  Default is same file name and path with an additional ''.HTM''.'
357       + #126 +
358       '  Example: ''Out=C:\Source.RTF''' + #126 +
359       '> HIGHLIGHTER=(Highlighter Name) is the Highlighter to use.'
360       + #126 +
361       '  If not provided then guessed based on extension.  ' + #126 +
362       '  Can also be the file name of a saved Highlighter.' + #126 +
363       '  Example: ''Highlighter=Pas''' + #126 +
364       '  Example: ''Highlighter="C:\My Documents\SrcExport\MyPascal.hi"'''
365       );
366     Exit;
367   end;
368 
369   // Finally we pass all the variables to the VerifyParameters routine.
370   VerifyParameters(sInFile, sOutFile, sHighlighter);
371 end;
372 
373 {You could actually call this routine from a GUI interface as well as the
374 ParseParameters method, but I will let you add that functionality.  I'll step
375 you through each section of this routine.}
376 
377 procedure TformSynEdit.VerifyParameters(const sInFile: string; sOutFile,
378   sHighlighter: string);
379 var
380   sInExt, sOutExt: string;
381   myExporter: TSynCustomExporter;
382   iCtr: Integer;
383 begin
384   {First verify that the input file does exist.  We cannot format something that
385   has not been saved to disk yet (although that would make a great Delphi Expert!)
386   We simply add a log line and exit if the file is non-existent.}
387   if not FileExists(sInFile) then
388   begin
389     Log('The input file "' + sInFile + '" does not exist');
390     Exit;
391   end;
392 
393   {If they did not specify an output file then we append an 'HTM' extension.}
394   if sOutFile = '' then
395     sOutFile := sInFile + '.HTM';
396 
397   {Make sure the output file does not exist.}
398   if FileExists(sOutFile) then
399   try
400     DeleteFile(sOutFile);
401   except
402     log('Output file exists and cannot be deleted');
403     Exit;
404   end;
405 
406   {Make sure we can create the output file.}
407   try
408     // Make sure we can create the path
409     ForceDirectories(ExtractFilePath(sOutFile));
410     // Create and close a test file
411     FileClose(FileCreate(sOutFile));
412   except
413     log('Cannot create output file!');
414     Exit;
415   end;
416 
417   {Extract the extensions of the files for guessing the highlighter and format.}
418   sInExt := UpperCase(ExtractFileExt(sInFile));
419   sOutExt := UpperCase(ExtractFileExt(sOutFile));
420 
421   {Now we guess the export format.
422   If it is not an .RTF extension then we assume HTML.}
423   if sOutExt = '.RTF' then
424   begin
425     log('Exporting to RTF');
426     myExporter := ExporterRTF;
427   end
428   else
429   begin
430     log('Exporting to HTML');
431     myExporter := ExporterHTML;
432   end;
433 
434   {Now we guess the highlighter.  To do this we will spin through all the
435   DefaultFilter properties of the highlighters we included on the form.  Since we
436   stop on the first match, you may want to change the creation order (by right
437   clicking on the form) to put your most common highlighters first.  }
438   myExporter.Highlighter := nil;
439 
440   // only do with if no highlighter was specified at the command-line
441   if sHighlighter = '' then
442   begin
443     for iCtr := 0 to pred(ComponentCount) do // go through all the componets
444       // only look at highlighters
445       if Components[iCtr] is TSynCustomHighlighter then
446         // use the filter match method to see if the extension matches the filter
447         if FilterMatch(sInExt,
448           (Components[iCtr] as TSynCustomHighlighter).DefaultFilter) then
449         begin
450           // Set the name of the highlighter as the meaningful part of the
451           // component name.
452           sHighlighter := Copy(Components[iCtr].Name, 4,
453             Length(Components[iCtr].Name) - 7);
454           // set the actual highlighter property of the exporter
455           myExporter.Highlighter := Components[iCtr] as TSynCustomHighlighter;
456           // no more looping, we have what we want.
457           Break;
458         end;
459   end;
460 
461   if sHighlighter = '' then
462   begin // we didn't find an internal one, but we might find an external one.
463     log('No highlighter was found for the extension ' + sInExt);
464   end;
465 
466   // if they specified a highlighter at the command line find it now.
467   if (myExporter.Highlighter = nil) and (sHighlighter <> '') then
468     for iCtr := 0 to pred(ComponentCount) do
469       if Components[iCtr] is TSynCustomHighlighter then
470         if CompareText(Components[iCtr].Name, 'Syn' + sHighlighter + 'Syn1') = 0 
471 then
472         begin
473           myExporter.Highlighter := Components[iCtr] as TSynCustomHighlighter;
474           Break;
475         end;
476 
477   // we still don't have a highlighter but one was specified
478   if (myExporter.Highlighter = nil) and (sHighlighter <> '') then
479   begin
480     log('No internal highlighter named ''' + sHighlighter + ''' found!');
481     // but there is a file with the same name as the specified highlighter,
482     // maybe it is an external highlighter!
483     if FileExists(sHighlighter) then
484     begin
485       log('Loading highlighter: ' + sHighlighter);
486       // before you can load a component you need to register the class.
487       RegisterClasses([TSynExporterHTML, TSynExporterRTF, TSynPasSyn,
488         TSynDfmSyn, TSynHTMLSyn, TSynCssSyn, TSynHC11Syn, TSynADSP21xxSyn,
489           TSynAWKSyn, TSynBaanSyn, TSynCacheSyn, TSynCACSyn, TSynCPMSyn,
490           TSynFortranSyn, TSynFoxproSyn, TSynGalaxySyn, TSynDmlSyn,
491           TSynGeneralSyn, TSynGWScriptSyn, TSynHP48Syn, TSynIniSyn, TSynInnoSyn,
492           TSynJavaSyn, TSynJScriptSyn, TSynKixSyn, TSynModelicaSyn, TSynM3Syn,
493           TSynVBScriptSyn, TSynBatSyn, TSynPerlSyn, TSynPHPSyn, TSynProgressSyn,
494           TSynSDDSyn, TSynSQLSyn, TSynSMLSyn, TSynTclTkSyn, TSynVBSyn,
495           TSynAsmSyn, TSynCppSyn, TSynPythonSyn, TSynPasSyn]);
496       try // try to load the component
497         myExporter.Highlighter :=
498           FileToComponent(sHighlighter) as TSynCustomHighlighter;
499       except
500         // failed to load the component, it must have been invalid!
501         log('External highlighter named ''' + sHighlighter + ''' is inavlid!');
502         Exit;
503       end;
504     end
505     else
506     begin
507       log('No external highlighter named ''' + sHighlighter + ''' found!');
508       Exit;
509     end;
510   end;
511 
512   // Note: if not highlighter was specifed, and none can be found based on
513   // extension then we can export without a highlighter which results in
514   // no syntax highlighting, but does change the format.
515 
516   // nothing caused us to exit along the way, we must have everything we need.
517   // list it out in the log window
518   Log('Intput file: ' + sInFile + #126 +
519     'Output file: ' + sOutFile + #126 +
520     'Highlighter: ' + sHighlighter);
521 
522   // Now we call Perform Highlight with the final parameters.
523   PerformHighlight(sInFile, sOutFile, myExporter);
524 end;
525 
526 { After all that work, all we did is protect the actual functionality of
527 this program from the user.  We should now have valid parameters for this
528 method. }
529 
530 procedure TformSynEdit.PerformHighlight(const sInFile, sOutFile: string;
531   sceHighlighter: TSynCustomExporter);
532 var
533   slSrc: TStringList;
534 begin
535   slSrc := TStringList.Create; // to load the source code into
536   try
537     // load the source code from disk
538     slSrc.LoadFromFile(sInFile);
539     sceHighlighter.ExportAsText := True;
540     // Might be a good idea to make this user definable at some point, but for
541     // now we will just us a generic title
542     sceHighlighter.Title := ExtractFileName(sInFile) + ' source code';
543     // Read in the source code and convert it to the highlighter format
544     sceHighlighter.ExportAll(slSrc);
545     // Save the output to disk.
546     sceHighlighter.SaveToFile(sOutFile);
547   finally
548     slSrc.Free; // all done
549   end;
550 end;
551 
552 {Now assign an event handler to the Form Show event.  You could use a button
553 instead if you wanted.  You might also want to close when it is done.}
554 
555 procedure TformSynEdit.FormShow(Sender: TObject);
556 begin
557   Application.ProcessMessages; // finish drawing the form
558   ParseParameters; // Get Started.
559 end;
560 
561 end.


			
Vote: How useful do you find this Article/Tip?
Bad Excellent
1 2 3 4 5 6 7 8 9 10

 

Advertisement
Share this page
Advertisement
Download from Google

Copyright © Mendozi Enterprises LLC