dw_htmlchm.inc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. {%mainunit dw_html}
  2. {$IFDEF chmInterface}
  3. type
  4. { TCHMHTMLWriter }
  5. TCHMHTMLWriter = class(THTMLWriter)
  6. private
  7. FOutChm: TStream;
  8. FChm: TChmWriter;
  9. FTempUncompressed: TStream;
  10. FTempUncompressedName: String;
  11. FTOCName,
  12. FIndexName,
  13. FDefaultPage: String;
  14. FCSSFile: String;
  15. FAutoTOC,
  16. FAutoIndex: Boolean;
  17. FOtherFiles: String;
  18. procedure ProcessOptions;
  19. function RetrieveOtherFiles(const DataName: String; out PathInChm: String; out FileName: String; var Stream: TStream): Boolean;
  20. procedure LastFileAdded(Sender: TObject);
  21. procedure GenerateTOC;
  22. procedure GenerateIndex;
  23. public
  24. procedure WriteHTMLPages; override;
  25. function InterPretOption(const Cmd,Arg : String): boolean; override;
  26. class procedure Usage(List: TStrings); override;
  27. end;
  28. {$ELSE} // implementation
  29. { TCHMHTMLWriter }
  30. procedure TCHMHTMLWriter.ProcessOptions;
  31. var
  32. TempStream: TMemoryStream;
  33. begin
  34. if FDefaultPage = '' then
  35. FDefaultPage := 'index.html'
  36. else
  37. begin
  38. WriteLn('Note: --index-page not assigned. Using default "index.html"');
  39. end;
  40. if FCSSFile <> '' then
  41. begin
  42. TempStream := TMemoryStream.Create;
  43. TempStream.LoadFromFile(FCSSFile);
  44. TempStream.Position := 0;
  45. FChm.AddStreamToArchive('fpdoc.css', '/', TempStream, True);
  46. TempStream.Free;
  47. end;
  48. FChm.DefaultPage := FDefaultPage;
  49. if FOtherFiles <> '' then
  50. begin
  51. FChm.FilesToCompress.LoadFromFile(FOtherFiles);
  52. end;
  53. end;
  54. function TCHMHTMLWriter.RetrieveOtherFiles(const DataName: String; out
  55. PathInChm: String; out FileName: String; var Stream: TStream): Boolean;
  56. var
  57. Dir: String;
  58. begin
  59. if Stream <> nil then
  60. Stream.Free;
  61. Stream := TMemoryStream.Create;
  62. TMemoryStream(Stream).LoadFromFile(DataName);
  63. FileName := ExtractFileName(DataName);
  64. if ExtractFileDir(DataName) <> '' then
  65. PathInChm := ExtractRelativepath(GetCurrentDir, ExtractFileDir(DataName))
  66. else
  67. PathInChm := '/';
  68. FixHTMLpath(PathInChm);
  69. Stream.Position := 0;
  70. end;
  71. procedure TCHMHTMLWriter.LastFileAdded(Sender: TObject);
  72. var
  73. TmpStream: TMemoryStream;
  74. begin
  75. TmpStream := TMemoryStream.Create;
  76. if FAutoTOC then
  77. GenerateTOC
  78. else
  79. if FTOCName <> '' then
  80. begin
  81. TmpStream.LoadFromFile(FTOCName);
  82. TmpStream.Position := 0;
  83. FChm.AppendTOC(TmpStream);
  84. TmpStream.Size := 0;
  85. end;
  86. if FAutoIndex then
  87. GenerateIndex
  88. else
  89. if FIndexName <> '' then
  90. begin
  91. TmpStream.LoadFromFile(FIndexName);
  92. TmpStream.Position := 0;
  93. FChm.AppendIndex(TmpStream);
  94. end;
  95. TmpStream.Free;
  96. WriteLn('Finishing compressing...');
  97. end;
  98. function TOCSort(Item1, Item2: TChmSiteMapItem): Integer;
  99. begin
  100. Result := CompareText(LowerCase(Item1.Text), LowerCase(Item2.Text));
  101. end;
  102. function GetAlphaItem(AItems: TChmSiteMapItems; AName: String): TChmSiteMapItem;
  103. var
  104. x: Integer;
  105. begin
  106. Result := nil;
  107. for x := 0 to AItems.Count-1 do
  108. begin
  109. if AItems.Item[x].Text = AName then
  110. Exit(AItems.Item[x]);
  111. end;
  112. Result := AItems.NewItem;
  113. Result.Text := AName;
  114. end;
  115. procedure TCHMHTMLWriter.GenerateTOC;
  116. var
  117. TOC: TChmSiteMap;
  118. Element: TPasElement;
  119. k: Integer;
  120. j: Integer;
  121. i: Integer;
  122. AModule: TPasModule;
  123. Member: TPasElement;
  124. Stream: TMemoryStream;
  125. TmpItem: TChmSiteMapItem;
  126. ObjByUnitItem,
  127. AlphaObjItem,
  128. ObjUnitItem,
  129. RoutinesByUnitItem,
  130. RoutinesUnitItem,
  131. AlphaRoutinesItem: TChmSiteMapItem;
  132. begin
  133. WriteLn('Generating Table of contents...');
  134. if Assigned(Package) then
  135. begin
  136. Toc := TChmSiteMap.Create(stTOC);
  137. Stream := TMemoryStream.Create;
  138. ObjByUnitItem := TOC.Items.NewItem;
  139. ObjByUnitItem.Text := 'Classes and Objects, by Unit';
  140. AlphaObjItem := TOC.Items.NewItem;
  141. AlphaObjItem.Text := 'Alphabetical Classes and Objects List';
  142. RoutinesByUnitItem := TOC.Items.NewItem;
  143. RoutinesByUnitItem.Text := 'Routines, by Unit';
  144. AlphaRoutinesItem := TOC.Items.NewItem;
  145. AlphaRoutinesItem.Text := 'Alphabetical Routines List';
  146. // objects and classes
  147. for i := 0 to Package.Modules.Count - 1 do
  148. begin
  149. AModule := TPasModule(Package.Modules[i]);
  150. ObjUnitItem := ObjByUnitItem.Children.NewItem;
  151. ObjUnitItem.Text := AModule.Name;
  152. RoutinesUnitItem := RoutinesByUnitItem.Children.NewItem;
  153. RoutinesUnitItem.Text := AModule.Name;
  154. for j := 0 to AModule.InterfaceSection.Classes.Count-1 do
  155. begin
  156. Element := TPasClassType(AModule.InterfaceSection.Classes[j]);
  157. // by unit
  158. TmpItem := ObjUnitItem.Children.NewItem;
  159. TmpItem.Text := Element.Name;
  160. TmpItem.Local := Allocator.GetFilename(Element, 0);
  161. //alpha
  162. TmpItem := GetAlphaItem(AlphaObjItem.Children, UpperCase(Copy(Element.Name, 1, 2))).Children.NewItem;
  163. TmpItem.Text := Element.Name;
  164. TmpItem.Local := Allocator.GetFilename(Element, 0);
  165. end;
  166. // non object procedures and functions
  167. for j := 0 to AModule.InterfaceSection.Functions.Count-1 do
  168. begin
  169. Element := TPasFunctionType(AModule.InterfaceSection.Functions[j]);
  170. // by unit
  171. TmpItem := RoutinesUnitItem.Children.NewItem;
  172. TmpItem.Text := Element.Name;
  173. TmpItem.Local := Allocator.GetFilename(Element, 0);
  174. // alpha
  175. TmpItem := GetAlphaItem(AlphaRoutinesItem.Children, UpperCase(Element.Name[1])).Children.NewItem;
  176. TmpItem.Text := Element.Name;
  177. TmpItem.Local := Allocator.GetFilename(Element, 0);
  178. end;
  179. end;
  180. end;
  181. // cleanup
  182. for i := ObjByUnitItem.Children.Count-1 downto 0 do
  183. begin
  184. if ObjByUnitItem.Children.Item[i].Children.Count = 0 then
  185. ObjByUnitItem.Children.Delete(i);
  186. end;
  187. for i := RoutinesByUnitItem.Children.Count-1 downto 0 do
  188. begin
  189. if RoutinesByUnitItem.Children.Item[i].Children.Count = 0 then
  190. RoutinesByUnitItem.Children.Delete(i);
  191. end;
  192. for i := TOC.Items.Count-1 downto 0 do
  193. begin
  194. if TOC.Items.Item[i].Children.Count = 0 then
  195. TOC.Items.Delete(i);
  196. end;
  197. // Sort
  198. for i := 0 to TOC.Items.Count-1 do
  199. begin
  200. TOC.Items.Item[i].Children.Sort(TListSortCompare(@TOCSort));
  201. for j := 0 to TOC.Items.Item[i].Children.Count-1 do
  202. begin
  203. TOC.Items.Item[i].Children.Item[j].Children.Sort(TListSortCompare(@TOCSort));
  204. end;
  205. end;
  206. TOC.SaveToStream(Stream);
  207. TOC.Free;
  208. fchm.AppendTOC(Stream);
  209. Stream.Free;
  210. end;
  211. type
  212. TClassMemberType = (cmtProcedure, cmtFunction, cmtConstructor, cmtDestructor,
  213. cmtInterface, cmtProperty, cmtVariable, cmtUnknown);
  214. function ElementType(Element: TPasElement): TClassMemberType;
  215. var
  216. ETypeName: String;
  217. begin
  218. Result := cmtUnknown;
  219. ETypeName := Element.ElementTypeName;
  220. //overloaded we don't care
  221. if ETypeName[1] = 'o' then ETypeName := Copy(ETypeName, 11, Length(ETypeName));
  222. if ETypeName[1] = 'f' then Exit(cmtFunction);
  223. if ETypeName[1] = 'c' then Exit(cmtConstructor);
  224. if ETypeName[1] = 'v' then Exit(cmtVariable);
  225. if ETypeName[1] = 'i' then Exit(cmtInterface);
  226. // the p's
  227. if ETypeName[4] = 'c' then Exit(cmtProcedure);
  228. if ETypeName[4] = 'p' then Exit(cmtProperty);
  229. end;
  230. procedure TCHMHTMLWriter.GenerateIndex;
  231. var
  232. Index: TChmSiteMap;
  233. i, j, k: Integer;
  234. TmpItem: TChmSiteMapItem;
  235. ParentItem: TChmSiteMapItem;
  236. AModule: TPasModule;
  237. TmpElement: TPasElement;
  238. ParentElement: TPasElement;
  239. MemberItem: TChmSiteMapItem;
  240. Stream: TMemoryStream;
  241. begin
  242. WriteLn('Generating Index...');
  243. if Assigned(Package) then
  244. begin
  245. try
  246. Index := TChmSiteMap.Create(stIndex);
  247. Stream := TMemoryStream.Create;
  248. for i := 0 to Package.Modules.Count - 1 do
  249. begin
  250. AModule := TPasModule(Package.Modules[i]);
  251. ParentItem := Index.Items.NewItem;
  252. ParentItem.Text := AModule.Name;
  253. ParentItem.Local := Allocator.GetFilename(AModule, 0);
  254. // classes
  255. for j := 0 to AModule.InterfaceSection.Classes.Count-1 do
  256. begin
  257. ParentElement := TPasClassType(AModule.InterfaceSection.Classes[j]);
  258. ParentItem := Index.Items.NewItem;
  259. ParentItem.Text := ParentELement.Name;
  260. ParentItem.Local := Allocator.GetFilename(ParentElement, 0);
  261. for k := 0 to TPasClassType(ParentElement).Members.Count-1 do
  262. begin
  263. TmpElement := TPasElement(TPasClassType(ParentElement).Members.Items[k]);
  264. if Engine.HidePrivate and(TmpElement.Visibility = visPrivate) then
  265. continue;
  266. if Engine.HideProtected and(TmpElement.Visibility = visProtected) then
  267. continue;
  268. TmpItem := ParentItem.Children.NewItem;
  269. case ElementType(TmpElement) of
  270. cmtProcedure : TmpItem.Text := TmpElement.Name + ' procedure';
  271. cmtFunction : TmpItem.Text := TmpElement.Name + ' function';
  272. cmtConstructor : TmpItem.Text := TmpElement.Name + ' constructor';
  273. cmtDestructor : TmpItem.Text := TmpElement.Name + ' destructor';
  274. cmtProperty : TmpItem.Text := TmpElement.Name + ' property';
  275. cmtVariable : TmpItem.Text := TmpElement.Name + ' variable';
  276. cmtInterface : TmpItem.Text := TmpElement.Name + ' interface';
  277. cmtUnknown : TmpItem.Text := TmpElement.Name;
  278. end;
  279. TmpItem.Local := Allocator.GetFilename(TmpElement, 0);
  280. {
  281. ParentElement = Class
  282. TmpElement = Member
  283. }
  284. MemberItem := nil;
  285. MemberItem := GetAlphaItem(Index.Items, TmpElement.Name);
  286. // ahh! if MemberItem.Local is empty MemberType is not shown!
  287. MemberItem.Local := Allocator.GetFilename(TmpElement, 0);
  288. TmpItem := MemberItem.Children.NewItem;
  289. TmpItem.Text := ParentElement.Name;
  290. TmpITem.Local := Allocator.GetFilename(TmpElement, 0);
  291. end;
  292. end;
  293. // routines
  294. for j := 0 to AModule.InterfaceSection.Functions.Count-1 do
  295. begin
  296. ParentElement := TPasProcedureType(AModule.InterfaceSection.Functions[j]);
  297. TmpItem := Index.Items.NewItem;
  298. TmpItem.Text := ParentElement.Name + ' ' + TPasFunction(ParentElement).ElementTypeName;
  299. TmpItem.Local := Allocator.GetFilename(ParentElement, 0);
  300. end;
  301. // consts
  302. for j := 0 to AModule.InterfaceSection.Consts.Count-1 do
  303. begin
  304. ParentElement := TPasElement(AModule.InterfaceSection.Consts[j]);
  305. TmpItem := Index.Items.NewItem;
  306. TmpItem.Text := ParentElement.Name;
  307. TmpItem.Local := Allocator.GetFilename(ParentElement, 0);
  308. end;
  309. // types
  310. for j := 0 to AModule.InterfaceSection.Types.Count-1 do
  311. begin
  312. ParentElement := TPasType(AModule.InterfaceSection.Types[j]);
  313. TmpItem := Index.Items.NewItem;
  314. TmpItem.Text := ParentElement.Name;
  315. TmpItem.Local := Allocator.GetFilename(ParentElement, 0);
  316. // enums
  317. if ParentELement is TPasEnumType then
  318. begin
  319. ParentItem := TmpItem;
  320. for k := 0 to TPasEnumType(ParentElement).Values.Count-1 do
  321. begin
  322. TmpElement := TPasType(TPasEnumType(ParentElement).Values.Items[k]);
  323. // subitem
  324. TmpItem := ParentItem.Children.NewItem;
  325. TmpItem.Text := TmpElement.Name;
  326. TmpItem.Local := ParentItem.Local;
  327. // root level
  328. TmpItem := Index.Items.NewItem;
  329. TmpItem.Text := TmpElement.Name;
  330. TmpItem.Local := ParentItem.Local;
  331. end;
  332. end;
  333. end;
  334. // variables
  335. for j := 0 to AModule.InterfaceSection.Variables.Count-1 do
  336. begin
  337. ParentElement := TPasElement(AModule.InterfaceSection.Variables[j]);
  338. TmpItem := Index.Items.NewItem;
  339. TmpItem.Text := ParentElement.Name + ' var';
  340. TmpItem.Local := Allocator.GetFilename(ParentElement, 0);
  341. end;
  342. // declarations
  343. {
  344. for j := 0 to AModule.InterfaceSection.Declarations.Count-1 do
  345. begin
  346. ParentElement := TPasElement(AModule.InterfaceSection.Declarations[j]);
  347. TmpItem := Index.Items.NewItem;
  348. TmpItem.Text := ParentElement.Name;
  349. TmpItem.Local := Allocator.GetFilename(ParentElement, 0);
  350. end;
  351. // resource strings
  352. for j := 0 to AModule.InterfaceSection.ResStrings.Count-1 do
  353. begin
  354. ParentElement := TPasElement(AModule.InterfaceSection.ResStrings[j]);
  355. TmpItem := Index.Items.NewItem;
  356. TmpItem.Text := ParentElement.Name;
  357. TmpItem.Local := Allocator.GetFilename(ParentElement, 0);
  358. end;
  359. }
  360. end;
  361. // Sort
  362. Index.Items.Sort(TListSortCompare(@TOCSort));
  363. for i := 0 to Index.Items.Count-1 do
  364. begin
  365. Index.Items.Item[i].Children.Sort(TListSortCompare(@TOCSort));
  366. end;
  367. // save
  368. Index.SaveToStream(Stream);
  369. Index.Free;
  370. Stream.Position :=0 ;
  371. FChm.AppendIndex(Stream);
  372. Stream.Free;
  373. except
  374. Dump_Stack(StdOut, get_frame);
  375. Halt(1);
  376. end;
  377. end;
  378. end;
  379. procedure TCHMHTMLWriter.WriteHTMLPages;
  380. var
  381. i: Integer;
  382. PageDoc: TXMLDocument;
  383. FileStream: TMemoryStream;
  384. FileName: String;
  385. FilePath: String;
  386. begin
  387. if Engine.Output = '' then
  388. begin
  389. WriteLn('Error: no --output option used.');
  390. Exit;
  391. end;
  392. if ExtractFileExt(Engine.Output) <> '.chm' then
  393. ChangeFileExt(Engine.OutPut, '.chm');
  394. FOutChm := TFileStream.Create(Engine.Output, fmOpenReadWrite or fmCreate);
  395. FTempUncompressedName := GetTempFileName+IntToStr(GetProcessID) +'.raw';
  396. FTempUncompressed := TFileStream.Create(FTempUncompressedName, fmOpenReadWrite or fmCreate);
  397. FChm := TChmWriter.Create(FOutChm, False);
  398. FChm.Title := Copy(Package.Name, 2, Length(Package.Name));
  399. FChm.TempRawStream := FTempUncompressed;
  400. FChm.OnGetFileData := @RetrieveOtherFiles;
  401. FChm.OnLastFile := @LastFileAdded;
  402. ProcessOptions;
  403. FileStream := TMemoryStream.Create;
  404. for i := 0 to PageInfos.Count - 1 do
  405. with TPageInfo(PageInfos[i]) do
  406. begin
  407. PageDoc := CreateHTMLPage(Element, SubpageIndex);
  408. try
  409. FileName := ExtractFileName(Allocator.GetFilename(Element, SubpageIndex));
  410. FilePath := '/'+FixHTMLpath(ExtractFilePath(Allocator.GetFilename(Element, SubpageIndex)));
  411. try
  412. WriteHTMLFile(PageDoc, FileStream);
  413. FChm.AddStreamToArchive(FileName, FilePath, FileStream, True);
  414. except
  415. on E: Exception do
  416. WriteLn(Format(SErrCouldNotCreateFile, [FileName, e.Message]));
  417. end;
  418. finally
  419. PageDoc.Free;
  420. FileStream.Size := 0;
  421. end;
  422. end;
  423. FileStream.Free;
  424. WriteLn('HTML Files written. Collecting other files and compressing...this could take some time');
  425. FChm.Execute;
  426. FChm.Free;
  427. // we don't need to free FTempUncompressed
  428. // FTempUncompressed.Free;
  429. FOutChm.Free;
  430. DeleteFile(FTempUncompressedName);
  431. end;
  432. function TCHMHTMLWriter.InterPretOption(const Cmd, Arg: String): boolean;
  433. begin
  434. Result:=True;
  435. if Cmd = '--toc-file' then
  436. FTOCName := arg
  437. else if Cmd = '--index-file' then
  438. FIndexName := arg
  439. else if Cmd = '--default-page' then
  440. FDefaultPage := arg
  441. else if Cmd = '--other-files' then
  442. FOtherFiles := arg
  443. else if Cmd = '--css-file' then
  444. FCSSFile := arg
  445. else if Cmd = '--auto-index' then
  446. FAutoIndex := True
  447. else if Cmd = '--auto-toc' then
  448. FAutoTOC := True
  449. else
  450. Result:=inherited InterPretOption(Cmd, Arg);
  451. end;
  452. class procedure TCHMHTMLWriter.Usage(List: TStrings);
  453. begin
  454. THTMLWriter.Usage(List);
  455. List.add('--default-page');
  456. List.Add(SCHMUsageDefPage);
  457. List.add('--toc-file');
  458. List.Add(SCHMUsageTOC);
  459. List.add('--index-file');
  460. List.Add(SCHMUsageIndex);
  461. List.add('--other-files');
  462. List.Add(SCHMUsageOtrFiles);
  463. List.add('--css-file');
  464. List.Add(SCHMUsageCSSFile);
  465. List.add('--auto-index');
  466. List.Add(SCHMUsageAutoIDX);
  467. List.add('--auto-toc');
  468. List.Add(SCHMUsageAutoTOC);
  469. end;
  470. {$ENDIF}