dw_htmlchm.inc 17 KB

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