wchmhwrap.pas 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. {
  2. This file is part of the Free Pascal Integrated Development Environment
  3. Copyright (c) 2008 by Marco van de Voort
  4. Wrapper for CHM reading to avoid having to import Delphi units into whtmlhlp,
  5. which can cause all kinds of namespace conflicts.
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. unit wchmhwrap;
  13. interface
  14. {$Mode Delphi}
  15. Uses wutils,whelp,whtml,SysUtils,ChmReader,ChmSiteMap,Classes;
  16. Type
  17. // TopicLinks: PTopicLinkCollection;IndexEntries : PUnsortedIndexEntryCollection;
  18. TChmWrapper = Class
  19. private
  20. ffs : Classes.TFileStream;
  21. fchmr : TChmReader;
  22. findex: TChmSiteMap;
  23. ftopic: TChmSiteMap;
  24. floaded : boolean;
  25. public
  26. constructor Create(name:String);
  27. function LoadIndex(id:integer;TopicLinks: PTopicLinkCollection;IndexEntries : PUnsortedIndexEntryCollection;helpfacility:PHelpFacility):boolean;
  28. function GetTopic(name:string):PMemoryTextFile;
  29. destructor Destroy;override;
  30. end;
  31. function combinepaths(relpath,basepath:String):String;
  32. implementation
  33. function combinepaths(relpath,basepath:String):String;
  34. begin
  35. {$ifdef combinedebug}
  36. debugmessageS({$i %file%},'combine in "'+relpath+'" and "'+basepath+'"',{$i %line%},'1',0,0);
  37. {$endif}
  38. if relpath='' then exit;
  39. if relpath[length(relpath)]<>'/' Then
  40. basepath:=extractfiledir(basepath);
  41. while (length(relpath)>0) and (copy(relpath,1,3)='../') do
  42. begin
  43. basepath:=extractfiledir(basepath);
  44. delete(relpath,1,3);
  45. end;
  46. {$ifdef combinedebug}
  47. debugmessageS({$i %file%},'combine out "'+relpath+'" and "'+basepath+'"',{$i %line%},'1',0,0);
  48. {$endif}
  49. if (length(basepath)>0) and (length(relpath)>0) then
  50. begin
  51. if (relpath[1]<>'/') and (basepath[length(basepath)]<>'/') then
  52. basepath:=basepath+'/';
  53. {$ifdef combinedebug}
  54. debugmessageS({$i %file%},'combine out2 "'+relpath+'" and "'+basepath+'"',{$i %line%},'1',0,0);
  55. {$endif}
  56. end;
  57. result:=basepath+relpath;
  58. end;
  59. Constructor TChmWrapper.Create(name:string);
  60. begin
  61. ffs:=Classes.TFileStream.create(name,fmOpenRead);
  62. fchmr:=TChmReader.Create(ffs,True); // owns ffs
  63. findex:=nil;
  64. if not fchmr.isvalidfile then
  65. begin
  66. freeandnil(fchmr);
  67. freeandnil(ffs);
  68. exit;
  69. end;
  70. {$ifdef wdebug}
  71. debugmessageS({$i %file%},'TCHMWrapper: before sitemap creation ',{$i %line%},'1',0,0);
  72. {$endif}
  73. findex:=TChmSiteMap.create(stindex);
  74. ftopic:=TChmSiteMap.create(sttoc);
  75. {$ifdef wdebug}
  76. debugmessageS({$i %file%},'TCHMWrapper: after sitemap creation ',{$i %line%},'1',0,0);
  77. {$endif}
  78. floaded:=false;
  79. end;
  80. function TChmWrapper.LoadIndex(id:integer;TopicLinks: PTopicLinkCollection;IndexEntries : PUnsortedIndexEntryCollection;helpfacility:PHelpFacility):boolean;
  81. function FormatAlias(Alias: string): string;
  82. begin
  83. if Assigned(HelpFacility) then
  84. if length(Alias)>HelpFacility^.IndexTabSize-4 then
  85. Alias:=Trim(copy(Alias,1,HelpFacility^.IndexTabSize-4-2))+'..';
  86. // if (length(alias)>0) and (alias[1]<>'/') then Alias:='/'+alias;
  87. FormatAlias:=Alias;
  88. end;
  89. var
  90. m : Classes.TMemoryStream;
  91. i,j : integer;
  92. item : TChmSiteMapItem;
  93. tli: integer;
  94. begin
  95. result:=false;
  96. if not assigned (fchmr) then exit;
  97. if floaded then exit;
  98. {$ifdef wdebug}
  99. debugmessageS({$i %file%},'TCHMWrapper: indexfilename:'+fchmr.indexfile,{$i %line%},'1',0,0);
  100. {$endif}
  101. m:=fchmr.getobject(fchmr.indexfile);
  102. try
  103. if assigned(m) then
  104. begin
  105. {$ifdef wdebug}
  106. debugmessageS({$i %file%},'TCHMWrapper: stream size loaded :'+inttostr(m.size),{$i %line%},'1',0,0);
  107. {$endif}
  108. findex.loadfromStream(m);
  109. end;
  110. finally
  111. freeandnil(m);
  112. end;
  113. {$ifdef wdebug}
  114. debugmessageS({$i %file%},'TCHMWrapper: loadindex after final ',{$i %line%},'1',0,0);
  115. {$endif}
  116. tli:=TopicLinks^.AddItem(fchmr.defaultpage);
  117. TLI:=EncodeHTMLCtx(ID,TLI+1);
  118. IndexEntries^.Insert(NewIndexEntry( FormatAlias('Table of contents'),ID,TLI));
  119. for i:=0 to findex.items.count-1 do
  120. begin
  121. item:=findex.items.item[i];
  122. tli:=TopicLinks^.AddItem('/'+item.local);
  123. TLI:=EncodeHTMLCtx(ID,TLI+1);
  124. IndexEntries^.Insert(NewIndexEntry( FormatAlias(item.text),ID,TLI));
  125. end;
  126. {$ifdef wdebug}
  127. debugmessageS({$i %file%},'TCHMWrapper: endloadindex ',{$i %line%},'1',0,0);
  128. {$endif}
  129. floaded:=true;
  130. result:=true;
  131. end;
  132. procedure splitline(idestream:PMemoryTextFile;s:ansistring);
  133. function scanvalue:integer; // searches for a possible breaking point left of char 255.
  134. var n,i : integer;
  135. lastpoint:integer;
  136. inquote : boolean;
  137. begin
  138. lastpoint:=-1;
  139. n:=length(s);
  140. if n>250 then n:=250;
  141. i:=1; inquote:=false;
  142. while (i<=n) do
  143. begin
  144. while (s[i]<>' ') and (s[i]<>'"') and (i<=n) do inc(i);
  145. if (s[i]=' ') and not inquote then lastpoint:=i;
  146. if (s[i]='"') then inquote:=not inquote;
  147. inc(i);
  148. end;
  149. scanvalue:=lastpoint;
  150. end;
  151. var position : longint;
  152. begin
  153. position:=0;
  154. while (length(s)>250) and (position<>-1) do
  155. begin
  156. position:=scanvalue;
  157. if position<>-1 then
  158. begin
  159. idestream.addline(copy(s,1,position-1));
  160. delete(s,1,position);
  161. end;
  162. end;
  163. if length(s)<>0 then
  164. idestream.addline(s);
  165. end;
  166. function TChmWrapper.GetTopic(name:string):PMemoryTextFile;
  167. var
  168. m : Classes.TMemorystream;
  169. linedata:Classes.TStringList;
  170. i : integer;
  171. begin
  172. result:=nil;
  173. if not assigned(fchmr) or (name='') then exit;
  174. If (name[1]<>'/') and (copy(name,1,7)<>'ms-its:') Then
  175. name:='/'+name;
  176. linedata:=Classes.TStringList.create;
  177. try
  178. {$ifdef wdebug}
  179. debugmessageS({$i %file%},'TCHMWrapper: Getting file '+name,{$i %line%},'1',0,0);
  180. {$endif}
  181. // if uppercase(name)='TABLE OF CONTENTS' Then
  182. // m:=fchmr.getobject(fchmr.tocfile)
  183. // else
  184. m:=fchmr.getobject(name);
  185. if not assigned(m) then exit;
  186. linedata.loadfromstream(m);
  187. result:=new(PMemoryTextFile,Init);
  188. for i:=0 to linedata.count-1 do
  189. begin
  190. if length(linedata[i])>250 Then
  191. splitline(result,linedata[i])
  192. else
  193. result.addline(linedata[i]);
  194. end;
  195. finally
  196. m.free;
  197. linedata.free;
  198. end;
  199. end;
  200. destructor TChmWrapper.Destroy;
  201. begin
  202. freeandnil(ftopic);
  203. freeandnil(findex);
  204. freeandnil(fchmr);
  205. end;
  206. // m:=r.getobject(r.indexfile);
  207. // siteindex.loadfromStream(m);
  208. end.