2
0

dw_chm.pp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810
  1. unit dw_chm;
  2. {$mode objfpc}
  3. {$h+}
  4. interface
  5. uses Classes, DOM,
  6. dGlobals, PasTree, dwriter, dw_html, chmwriter, chmtypes, chmsitemap;
  7. type
  8. { TCHmFileNameAllocator }
  9. TCHmFileNameAllocator = Class(TLongNameFileAllocator)
  10. // Override this, because the logic messes up the filenames for plain html files.
  11. function GetFilename(AElement: TPasElement; ASubindex: Integer): String; override;
  12. end;
  13. { TFpDocChmWriter }
  14. TFpDocChmWriter = class (TChmWriter)
  15. protected
  16. procedure FileAdded(AStream: TStream; const AEntry: TFileEntryRec); override;
  17. end;
  18. { TCHMHTMLWriter }
  19. TCHMHTMLWriter = class(THTMLWriter)
  20. private
  21. FOutChm: TStream;
  22. FChm: TFpDocChmWriter;
  23. FTempUncompressed: TStream;
  24. FTempUncompressedName: String;
  25. FChmTitle: String;
  26. FTOCName,
  27. FIndexName,
  28. FDefaultPage: String;
  29. FMakeSearchable,
  30. FNoBinToc,
  31. FNoBinIndex,
  32. FAutoTOC,
  33. FAutoIndex: Boolean;
  34. FOtherFiles: String;
  35. procedure ProcessOptions;
  36. function ResolveLinkIDAbs(const Name: String): DOMString;
  37. function RetrieveOtherFiles(const DataName: String; out PathInChm: String;
  38. out FileName: String; var Stream: TStream): Boolean;
  39. procedure LastFileAdded(Sender: TObject);
  40. function FindAlphaItem(AItems: TChmSiteMapItems; AName: String): TChmSiteMapItem;
  41. function GetAlphaItem(AItems: TChmSiteMapItems; AName: String): TChmSiteMapItem;
  42. procedure MultiAlphaItem(AItems: TChmSiteMapItems; AName: String;
  43. APasEl: TPasElement; Prefix:String);
  44. procedure GenerateTOC;
  45. procedure GenerateIndex;
  46. protected
  47. procedure DoWriteDocumentation; override;
  48. public
  49. function CreateAllocator: TFileAllocator; override;
  50. function InterPretOption(const Cmd,Arg : String): boolean; override;
  51. class procedure Usage(List: TStrings); override;
  52. Class Function FileNameExtension : String; override;
  53. Class procedure SplitImport(var AFilename, ALinkPrefix: String); override;
  54. end;
  55. implementation
  56. uses fpdocstrs, SysUtils, HTMWrite, dw_basehtml;
  57. { TCHmFileNameAllocator }
  58. function TCHmFileNameAllocator.GetFilename(AElement: TPasElement; ASubindex: Integer): String;
  59. var
  60. n,s: String;
  61. i: Integer;
  62. excl: Boolean; //search
  63. MElement: TPasElement;
  64. begin
  65. Result:='';
  66. excl := False;
  67. if AElement.ClassType = TPasPackage then
  68. begin
  69. Result := 'index';
  70. excl := True;
  71. end
  72. else if AElement.ClassType = TPasModule then
  73. begin
  74. Result := LowerCase(AElement.Name) + PathDelim + 'index';
  75. excl := True;
  76. end
  77. else
  78. begin
  79. if AElement is TPasOperator then
  80. begin
  81. if Assigned(AElement.Parent) then
  82. result:=LowerCase(AElement.Parent.PathName);
  83. With TPasOperator(aElement) do
  84. Result:= Result + 'op-'+OperatorTypeToOperatorName(OperatorType);
  85. s := '';
  86. N:=LowerCase(aElement.Name); // Should not contain any weird chars.
  87. Delete(N,1,Pos('(',N));
  88. i := 1;
  89. Repeat
  90. I:=Pos(',',N);
  91. if I=0 then
  92. I:=Pos(')',N);
  93. if I>1 then
  94. begin
  95. if (S<>'') then
  96. S:=S+'-';
  97. S:=S+Copy(N,1,I-1);
  98. end;
  99. Delete(N,1,I);
  100. until I=0;
  101. // First char is maybe :
  102. if (N<>'') and (N[1]=':') then
  103. Delete(N,1,1);
  104. Result:=Result + '-'+ s + '-' + N;
  105. end
  106. else
  107. begin
  108. Result := LowerCase(AElement.PathName);
  109. excl := (ASubindex > 0);
  110. end;
  111. // cut off Package Name
  112. MElement:= AElement.GetModule;
  113. if Assigned(MElement) then
  114. AElement:= MElement;
  115. Result := Copy(Result, Length(AElement.Parent.Name) + 2, MaxInt);
  116. // to skip dots in unit name
  117. i := Length(AElement.Name);
  118. while (i <= Length(Result)) and (Result[i] <> '.') do
  119. Inc(i);
  120. if (i <= Length(Result)) and (i > 0) then
  121. Result[i] := PathDelim;
  122. if excl or (Length(Result)=0) then
  123. begin
  124. // exclude the from full text search index
  125. s:= '.'+ExtractFileName(Result + '.');
  126. n:= ExtractFileDir(Result);
  127. Result := n + DirectorySeparator + s;
  128. Result := Copy(Result, 1, Length(Result)-1);
  129. end;
  130. end;
  131. if ASubindex > 0 then
  132. Result := Result + '-' + GetFilePostfix(ASubindex);
  133. Result := Result + Extension;
  134. end;
  135. { TFpDocChmWriter }
  136. procedure TFpDocChmWriter.FileAdded ( AStream: TStream;
  137. const AEntry: TFileEntryRec ) ;
  138. var FTsave : boolean;
  139. begin
  140. // Exclude Full text index for files starting from the dot
  141. if Pos('.', AEntry.Name) <> 1 then
  142. inherited FileAdded(AStream, AEntry)
  143. else
  144. begin
  145. FTsave:=FullTextSearch;
  146. FullTextSearch:=False;
  147. inherited FileAdded(AStream, AEntry);
  148. FullTextSearch:=FTsave;
  149. end;
  150. end;
  151. { TCHMHTMLWriter }
  152. function TCHMHTMLWriter.ResolveLinkIDAbs(const Name: String): DOMString;
  153. begin
  154. Result:=UTF8Decode(FixHTMLpath(Engine.ResolveLink(Module,Name, True)));
  155. // for global index: don't make it relative to the current document.
  156. end;
  157. procedure TCHMHTMLWriter.ProcessOptions;
  158. var
  159. TempStream: TMemoryStream;
  160. begin
  161. if FDefaultPage = '' then
  162. FDefaultPage := 'index.html'
  163. else
  164. begin
  165. DoLog('Note: --index-page not assigned. Using default "index.html"');
  166. end;
  167. if CSSFile <> '' then
  168. begin
  169. if not FileExists(CSSFile) Then
  170. Raise Exception.CreateFmt('Can''t find CSS file "%S"',[CSSFILE]);
  171. TempStream := TMemoryStream.Create;
  172. TempStream.LoadFromFile(CSSFile);
  173. TempStream.Position := 0;
  174. FChm.AddStreamToArchive('fpdoc.css', '/', TempStream, True);
  175. TempStream.Free;
  176. end;
  177. FChm.DefaultPage := FDefaultPage;
  178. if FOtherFiles <> '' then
  179. begin
  180. FChm.FilesToCompress.LoadFromFile(FOtherFiles);
  181. end;
  182. FChm.FullTextSearch := FMakeSearchable;
  183. end;
  184. function TCHMHTMLWriter.RetrieveOtherFiles(const DataName: String; out
  185. PathInChm: String; out FileName: String; var Stream: TStream): Boolean;
  186. begin
  187. Result:=True;
  188. if Stream <> nil then
  189. Stream.Free;
  190. Stream := TMemoryStream.Create;
  191. TMemoryStream(Stream).LoadFromFile(DataName);
  192. FileName := ExtractFileName(DataName);
  193. if ExtractFileDir(DataName) <> '' then
  194. PathInChm := ExtractRelativepath(GetCurrentDir, ExtractFileDir(DataName))
  195. else
  196. PathInChm := '/';
  197. FixHTMLpath(PathInChm);
  198. Stream.Position := 0;
  199. end;
  200. procedure TCHMHTMLWriter.LastFileAdded(Sender: TObject);
  201. var
  202. TmpStream: TMemoryStream;
  203. begin
  204. TmpStream := TMemoryStream.Create;
  205. if FAutoTOC then
  206. GenerateTOC
  207. else
  208. if FTOCName <> '' then
  209. begin
  210. TmpStream.LoadFromFile(FTOCName);
  211. TmpStream.Position := 0;
  212. FChm.AppendTOC(TmpStream);
  213. TmpStream.Size := 0;
  214. end;
  215. if FAutoIndex then
  216. GenerateIndex
  217. else
  218. if FIndexName <> '' then
  219. begin
  220. TmpStream.LoadFromFile(FIndexName);
  221. TmpStream.Position := 0;
  222. FChm.AppendIndex(TmpStream);
  223. end;
  224. TmpStream.Free;
  225. DoLog('Finishing compressing...');
  226. end;
  227. function TOCSort(Item1, Item2: TChmSiteMapItem): Integer;
  228. begin
  229. Result := CompareText(LowerCase(Item1.Text), LowerCase(Item2.Text));
  230. end;
  231. function TCHMHTMLWriter.FindAlphaItem(AItems: TChmSiteMapItems; AName: String
  232. ): TChmSiteMapItem;
  233. var
  234. x: Integer;
  235. begin
  236. Result := nil;
  237. for x := 0 to AItems.Count-1 do
  238. begin
  239. if AItems.Item[x].Text = AName then
  240. Exit(AItems.Item[x]);
  241. end;
  242. end;
  243. function TCHMHTMLWriter.GetAlphaItem(AItems: TChmSiteMapItems; AName: String
  244. ): TChmSiteMapItem;
  245. begin
  246. Result := FindAlphaItem(AItems, AName);
  247. if Result <> nil then Exit;
  248. Result := AItems.NewItem;
  249. Result.Text := AName;
  250. end;
  251. procedure TCHMHTMLWriter.MultiAlphaItem(AItems: TChmSiteMapItems; AName: String;
  252. APasEl: TPasElement; Prefix: String);
  253. var
  254. AChmItem, AChmChld: TChmSiteMapItem;
  255. begin
  256. AChmItem:= FindAlphaItem(AItems, AName);
  257. if AChmItem = nil then
  258. begin
  259. // add new
  260. AChmItem := AItems.NewItem;
  261. AChmItem.Text := AName;
  262. AChmItem.addLocal(FixHTMLpath(Allocator.GetFilename(APasEl, 0)));
  263. end
  264. else
  265. begin
  266. // add as child
  267. AChmChld := AChmItem.Children.NewItem;
  268. AChmChld.Text := Prefix + '.' + AName;
  269. AChmChld.addLocal(FixHTMLpath(Allocator.GetFilename(APasEl, 0)));
  270. end;
  271. end;
  272. procedure TCHMHTMLWriter.GenerateTOC;
  273. var
  274. TOC: TChmSiteMap;
  275. Element: TPasElement;
  276. j: Integer;
  277. i: Integer;
  278. AModule: TPasModule;
  279. Stream: TMemoryStream;
  280. TmpItem: TChmSiteMapItem;
  281. ObjByUnitItem,
  282. AlphaObjItem,
  283. ObjUnitItem,
  284. RoutinesByUnitItem,
  285. RoutinesUnitItem,
  286. AlphaRoutinesItem: TChmSiteMapItem;
  287. begin
  288. DoLog('Generating Table of contents...');
  289. if not Assigned(Package) then
  290. begin
  291. DoLog('Package is not assigned...');
  292. Exit;
  293. end;
  294. Toc := TChmSiteMap.Create(stTOC);
  295. Stream := TMemoryStream.Create;
  296. ObjByUnitItem := TOC.Items.NewItem;
  297. ObjByUnitItem.Text := 'Classes and Objects, by Unit';
  298. AlphaObjItem := TOC.Items.NewItem;
  299. AlphaObjItem.Text := 'Alphabetical Classes and Objects List';
  300. RoutinesByUnitItem := TOC.Items.NewItem;
  301. RoutinesByUnitItem.Text := 'Routines, by Unit';
  302. AlphaRoutinesItem := TOC.Items.NewItem;
  303. AlphaRoutinesItem.Text := 'Alphabetical Routines List';
  304. // objects and classes
  305. for i := 0 to Package.Modules.Count - 1 do
  306. begin
  307. AModule := TPasModule(Package.Modules[i]);
  308. If not assigned(AModule.InterfaceSection) Then
  309. Continue;
  310. ObjUnitItem := ObjByUnitItem.Children.NewItem;
  311. ObjUnitItem.Text := AModule.Name;
  312. ObjUnitItem.addLocal(FixHTMLpath(Allocator.GetFilename(AModule, ClassesSubindex)));
  313. RoutinesUnitItem := RoutinesByUnitItem.Children.NewItem;
  314. RoutinesUnitItem.Text := AModule.Name;
  315. RoutinesUnitItem.addLocal(FixHTMLpath(Allocator.GetFilename(AModule, ProcsSubindex)));
  316. for j := 0 to AModule.InterfaceSection.Classes.Count-1 do
  317. begin
  318. Element := TPasClassType(AModule.InterfaceSection.Classes[j]);
  319. // by unit
  320. TmpItem := ObjUnitItem.Children.NewItem;
  321. TmpItem.Text := Element.Name;
  322. TmpItem.addLocal(FixHTMLpath(Allocator.GetFilename(Element, 0)));
  323. //alpha
  324. TmpItem := GetAlphaItem(AlphaObjItem.Children, UpperCase(Copy(Element.Name, 1, 2))).Children.NewItem;
  325. TmpItem.Text := Element.Name;
  326. TmpItem.addLocal(FixHTMLpath(Allocator.GetFilename(Element, 0)));
  327. end;
  328. // non object procedures and functions
  329. for j := 0 to AModule.InterfaceSection.Functions.Count-1 do
  330. begin
  331. Element := TPasFunctionType(AModule.InterfaceSection.Functions[j]);
  332. // by unit
  333. TmpItem := RoutinesUnitItem.Children.NewItem;
  334. TmpItem.Text := Element.Name;
  335. TmpItem.addLocal(FixHTMLpath(Allocator.GetFilename(Element, 0)));
  336. // alpha
  337. TmpItem := GetAlphaItem(AlphaRoutinesItem.Children, UpperCase(Element.Name[1])).Children.NewItem;
  338. TmpItem.Text := Element.Name;
  339. TmpItem.addLocal(FixHTMLpath(Allocator.GetFilename(Element, 0)));
  340. end;
  341. end;
  342. // cleanup
  343. for i := ObjByUnitItem.Children.Count-1 downto 0 do
  344. begin
  345. if ObjByUnitItem.Children.Item[i].Children.Count = 0 then
  346. ObjByUnitItem.Children.Delete(i);
  347. end;
  348. for i := RoutinesByUnitItem.Children.Count-1 downto 0 do
  349. begin
  350. if RoutinesByUnitItem.Children.Item[i].Children.Count = 0 then
  351. RoutinesByUnitItem.Children.Delete(i);
  352. end;
  353. for i := TOC.Items.Count-1 downto 0 do
  354. begin
  355. if TOC.Items.Item[i].Children.Count = 0 then
  356. TOC.Items.Delete(i);
  357. end;
  358. // Sort
  359. for i := 0 to TOC.Items.Count-1 do
  360. begin
  361. TOC.Items.Item[i].Children.Sort(TListSortCompare(@TOCSort));
  362. for j := 0 to TOC.Items.Item[i].Children.Count-1 do
  363. begin
  364. TOC.Items.Item[i].Children.Item[j].Children.Sort(TListSortCompare(@TOCSort));
  365. end;
  366. end;
  367. if not fnobintoc then
  368. fchm.AppendBinaryTOCFromSiteMap(Toc);
  369. TOC.SaveToStream(Stream);
  370. TOC.Free;
  371. fchm.AppendTOC(Stream);
  372. Stream.Free;
  373. DoLog('Generating TOC done');
  374. end;
  375. type
  376. TClassMemberType = (cmtProcedure, cmtFunction, cmtConstructor, cmtDestructor,
  377. cmtInterface, cmtProperty, cmtVariable, cmtOperator, cmtConstant, cmtUnknown);
  378. function ElementType(Element: TPasElement): TClassMemberType;
  379. var
  380. ETypeName: String;
  381. begin
  382. Result := cmtUnknown;
  383. if not Assigned(Element) then Exit;
  384. ETypeName := Element.ElementTypeName;
  385. if Length(ETypeName) = 0 then Exit;
  386. // opearator
  387. if ETypeName[2] = 'p' then Exit(cmtOperator);
  388. if ETypeName[3] = 'n' then Exit(cmtConstant);
  389. // overloaded we don't care
  390. if ETypeName[1] = 'o' then ETypeName := Copy(ETypeName, 12, Length(ETypeName));
  391. if ETypeName[1] = 'f' then Exit(cmtFunction);
  392. if ETypeName[1] = 'c' then Exit(cmtConstructor);
  393. if ETypeName[1] = 'd' then Exit(cmtDestructor);
  394. if ETypeName[1] = 'v' then Exit(cmtVariable);
  395. if ETypeName[1] = 'i' then Exit(cmtInterface);
  396. // the p's
  397. if ETypeName[4] = 'c' then Exit(cmtProcedure);
  398. if ETypeName[4] = 'p' then Exit(cmtProperty);
  399. // Unknown
  400. // WriteLn(' Warning El name: '+ Element.Name+' path: '+Element.PathName+' TypeName: '+Element.ElementTypeName);
  401. end;
  402. procedure TCHMHTMLWriter.GenerateIndex;
  403. var
  404. Index: TChmSiteMap;
  405. i, j, k: Integer;
  406. TmpItem: TChmSiteMapItem;
  407. ParentItem: TChmSiteMapItem;
  408. AModule: TPasModule;
  409. TmpElement: TPasElement;
  410. ParentElement: TPasElement;
  411. MemberItem: TChmSiteMapItem;
  412. Stream: TMemoryStream;
  413. RedirectUrl,Urls,SName: String;
  414. begin
  415. DoLog('Generating Index...');
  416. if not Assigned(Package) then
  417. begin
  418. DoLog('Package is not assigned...');
  419. Exit;
  420. end;
  421. Index := TChmSiteMap.Create(stIndex);
  422. Stream := TMemoryStream.Create;
  423. for i := 0 to Package.Modules.Count - 1 do
  424. //if false then
  425. begin
  426. AModule := TPasModule(Package.Modules[i]);
  427. if not assigned(AModule.InterfaceSection) then
  428. continue;
  429. ParentItem := Index.Items.NewItem;
  430. ParentItem.Text := AModule.Name;
  431. ParentItem.addLocal(FixHTMLpath(Allocator.GetFilename(AModule, 0)));
  432. // classes
  433. for j := 0 to AModule.InterfaceSection.Classes.Count-1 do
  434. begin
  435. ParentElement := TPasClassType(AModule.InterfaceSection.Classes[j]);
  436. ParentItem := Index.Items.NewItem;
  437. ParentItem.Text := ParentELement.Name;
  438. ParentItem.addLocal(FixHTMLpath(Allocator.GetFilename(ParentElement, 0)));
  439. for k := 0 to TPasClassType(ParentElement).Members.Count-1 do
  440. begin
  441. TmpElement := TPasElement(TPasClassType(ParentElement).Members.Items[k]);
  442. if Engine.HidePrivate and(TmpElement.Visibility = visPrivate) then
  443. continue;
  444. if Engine.HideProtected and(TmpElement.Visibility = visProtected) then
  445. continue;
  446. Urls:=FixHTMLpath(Allocator.GetFilename(TmpElement, 0));
  447. RedirectUrl:='';
  448. if TmpElement is TPasEnumValue then
  449. RedirectUrl := UTF8Encode(ResolveLinkIDAbs(tmpElement.Parent.PathName))
  450. else
  451. RedirectUrl := UTF8Encode(ResolveLinkIDAbs(tmpElement.PathName));
  452. if(trim(RedirectUrl)<>'') and (RedirectUrl<>urls) then
  453. begin
  454. //writeln('Hint: Index Resolved:',urls,' to ',RedirectUrl);
  455. urls:=RedirectUrl;
  456. end;
  457. TmpItem := ParentItem.Children.NewItem;
  458. case ElementType(TmpElement) of
  459. cmtProcedure : TmpItem.Text := TmpElement.Name + ' procedure';
  460. cmtFunction : TmpItem.Text := TmpElement.Name + ' function';
  461. cmtConstructor : TmpItem.Text := TmpElement.Name + ' constructor';
  462. cmtDestructor : TmpItem.Text := TmpElement.Name + ' destructor';
  463. cmtProperty : TmpItem.Text := TmpElement.Name + ' property';
  464. cmtVariable : TmpItem.Text := TmpElement.Name + ' variable';
  465. cmtInterface : TmpItem.Text := TmpElement.Name + ' interface';
  466. cmtOperator : TmpItem.Text := TmpElement.Name + ' operator';
  467. cmtConstant : TmpItem.Text := TmpElement.Name + ' const';
  468. cmtUnknown : TmpItem.Text := TmpElement.Name;
  469. end;
  470. TmpItem.addLocal(Urls);
  471. {
  472. ParentElement = Class
  473. TmpElement = Member
  474. }
  475. MemberItem := nil;
  476. MemberItem := GetAlphaItem(Index.Items, TmpElement.Name);
  477. // ahh! if MemberItem.Local is empty MemberType is not shown!
  478. MemberItem.addLocal(Urls);
  479. TmpItem := MemberItem.Children.NewItem;
  480. TmpItem.Text := ParentElement.Name;
  481. TmpItem.AddLocal(Urls);
  482. end;
  483. end;
  484. // routines
  485. for j := 0 to AModule.InterfaceSection.Functions.Count-1 do
  486. begin
  487. // routine name
  488. ParentElement := TPasElement(AModule.InterfaceSection.Functions[j]);
  489. case ElementType(ParentElement) of
  490. cmtProcedure : SName:= ' procedure';
  491. cmtFunction : SName:= ' function';
  492. cmtOperator : SName:= ' operator';
  493. //cmtConstant : SName:= ' const';
  494. else SName:= ' unknown'
  495. end;
  496. SName:= ParentElement.Name + ' ' + SName;
  497. MultiAlphaItem(Index.Items, SName, ParentElement, AModule.Name);
  498. end;
  499. // consts
  500. for j := 0 to AModule.InterfaceSection.Consts.Count-1 do
  501. begin
  502. ParentElement := TPasElement(AModule.InterfaceSection.Consts[j]);
  503. SName:= ParentElement.Name + ' const';
  504. MultiAlphaItem(Index.Items, SName, ParentElement, AModule.Name);
  505. end;
  506. // types
  507. for j := 0 to AModule.InterfaceSection.Types.Count-1 do
  508. begin
  509. ParentElement := TPasType(AModule.InterfaceSection.Types[j]);
  510. TmpItem := Index.Items.NewItem;
  511. TmpItem.Text := ParentElement.Name;
  512. TmpItem.addLocal(FixHTMLpath(Allocator.GetFilename(ParentElement, 0)));
  513. // enums
  514. if ParentELement is TPasEnumType then
  515. begin
  516. ParentItem := TmpItem;
  517. for k := 0 to TPasEnumType(ParentElement).Values.Count-1 do
  518. begin
  519. TmpElement := TPasType(TPasEnumType(ParentElement).Values.Items[k]);
  520. // subitem
  521. TmpItem := ParentItem.Children.NewItem;
  522. TmpItem.Text := TmpElement.Name;
  523. TmpItem.addLocal(ParentItem.Local);
  524. // root level
  525. TmpItem := Index.Items.NewItem;
  526. TmpItem.Text := TmpElement.Name;
  527. TmpItem.addLocal(ParentItem.Local);
  528. end;
  529. end;
  530. end;
  531. // variables
  532. for j := 0 to AModule.InterfaceSection.Variables.Count-1 do
  533. begin
  534. ParentElement := TPasElement(AModule.InterfaceSection.Variables[j]);
  535. SName:= ParentElement.Name + ' variable';
  536. MultiAlphaItem(Index.Items, SName, ParentElement, AModule.Name);
  537. end;
  538. // declarations
  539. {
  540. for j := 0 to AModule.InterfaceSection.Declarations.Count-1 do
  541. begin
  542. ParentElement := TPasElement(AModule.InterfaceSection.Declarations[j]);
  543. TmpItem := Index.Items.NewItem;
  544. TmpItem.Text := ParentElement.Name;
  545. TmpItem.Local := FixHTMLpath(Allocator.GetFilename(ParentElement, 0));
  546. end;
  547. // resource strings
  548. for j := 0 to AModule.InterfaceSection.ResStrings.Count-1 do
  549. begin
  550. ParentElement := TPasElement(AModule.InterfaceSection.ResStrings[j]);
  551. TmpItem := Index.Items.NewItem;
  552. TmpItem.Text := ParentElement.Name;
  553. TmpItem.Local := FixHTMLpath(Allocator.GetFilename(ParentElement, 0));
  554. end;
  555. }
  556. end;
  557. // Sort
  558. Index.Items.Sort(TListSortCompare(@TOCSort));
  559. for i := 0 to Index.Items.Count-1 do
  560. begin
  561. Index.Items.Item[i].Children.Sort(TListSortCompare(@TOCSort));
  562. end;
  563. // save
  564. Index.SaveToStream(Stream);
  565. if not fnobinindex then
  566. fchm.AppendBinaryindexFromSitemap(index,false);
  567. Index.Free;
  568. Stream.Position :=0 ;
  569. FChm.AppendIndex(Stream);
  570. Stream.Free;
  571. DoLog('Generating Index Done');
  572. end;
  573. procedure TCHMHTMLWriter.DoWriteDocumentation;
  574. var
  575. i: Integer;
  576. PageDoc: TXMLDocument;
  577. FileStream: TMemoryStream;
  578. IFileName,FileName: String;
  579. FilePath: String;
  580. begin
  581. AllocatePages;
  582. DoLog(SWritingPages, [PageCount]);
  583. FileName := Engine.Output;
  584. if FileName = '' then
  585. Raise Exception.Create('Error: no --output option used.');
  586. if ExtractFileExt(FileName) <> FileNameExtension then
  587. FileName := ChangeFileExt(FileName, FileNameExtension);
  588. FOutChm := TFileStream.Create(FileName, fmOpenReadWrite or fmCreate);
  589. FTempUncompressedName := GetTempFileName+IntToStr(GetProcessID) +'.raw';
  590. FTempUncompressed := TFileStream.Create(FTempUncompressedName, fmOpenReadWrite or fmCreate);
  591. FChm := TFpDocChmWriter.Create(FOutChm, False);
  592. FChm.Title := FChmTitle;
  593. FChm.TempRawStream := FTempUncompressed;
  594. FChm.OnGetFileData := @RetrieveOtherFiles;
  595. FChm.OnLastFile := @LastFileAdded;
  596. FChm.hasbinarytoc:=not fnobintoc;
  597. FChm.hasbinaryindex:=not fnobinindex;
  598. //FChm.Cores:=1;
  599. ProcessOptions;
  600. FileStream := TMemoryStream.Create;
  601. for i := 0 to PageInfos.Count - 1 do
  602. with TPageInfo(PageInfos[i]) do
  603. begin
  604. PageDoc := CreateHTMLPage(Element, SubpageIndex);
  605. try
  606. FileName := ExtractFileName(Allocator.GetFilename(Element, SubpageIndex));
  607. FilePath := '/'+FixHTMLpath(ExtractFilePath(Allocator.GetFilename(Element, SubpageIndex)));
  608. try
  609. WriteHTMLFile(PageDoc, FileStream);
  610. FChm.AddStreamToArchive(FileName, FilePath, FileStream, True);
  611. except
  612. on E: Exception do
  613. DoLog(Format(SErrCouldNotCreateFile, [FileName, e.Message]));
  614. end;
  615. finally
  616. PageDoc.Free;
  617. FileStream.Size := 0;
  618. end;
  619. end;
  620. FileStream.Free;
  621. DoLog('HTML Files written. Collecting other files and compressing...this could take some time');
  622. //write any found images to CHM stream
  623. FileStream := TMemoryStream.Create;
  624. for iFilename in ImageFileList do
  625. begin
  626. {$ifdef imagetest} DoLog(' adding image: '+iFileName); {$endif}
  627. if FileExists(iFileName) then
  628. begin
  629. {$ifdef imagetest} DoLog(' - found'); {$endif}
  630. FileName := ExtractFileName(iFileName);
  631. FilePath := '/'+FixHTMLpath(ExtractFilePath(iFileName));
  632. FileStream.LoadFromFile(iFileName);
  633. FChm.AddStreamToArchive(FileName, FilePath, FileStream, True);
  634. FileStream.Size := 0;
  635. end
  636. else
  637. {$ifdef imagetest} DoLog(' - not found'){$endif};
  638. end;
  639. FileStream.Free;
  640. FChm.Execute;
  641. FChm.Free;
  642. DoLog('Collecting done');
  643. // we don't need to free FTempUncompressed it is freed into TFpDocChmWriter
  644. // FTempUncompressed.Free;
  645. FOutChm.Free;
  646. DeleteFile(FTempUncompressedName);
  647. end;
  648. function TCHMHTMLWriter.CreateAllocator: TFileAllocator;
  649. begin
  650. Result:=TCHmFileNameAllocator.Create('.html');
  651. end;
  652. function TCHMHTMLWriter.InterPretOption(const Cmd, Arg: String): boolean;
  653. begin
  654. Result:=True;
  655. FNoBinToc:=False;
  656. FnoBinIndex:=False;
  657. if Cmd = '--toc-file' then
  658. FTOCName := arg
  659. else if Cmd = '--index-file' then
  660. FIndexName := arg
  661. else if Cmd = '--default-page' then
  662. FDefaultPage := arg
  663. else if Cmd = '--other-files' then
  664. FOtherFiles := arg
  665. else if Cmd = '--auto-index' then
  666. FAutoIndex := True
  667. else if Cmd = '--auto-toc' then
  668. FAutoTOC := True
  669. else if Cmd = '--no-bintoc' then
  670. FNoBinToc := True
  671. else if Cmd = '--no-binindex' then
  672. FNoBinIndex := True
  673. else if Cmd = '--make-searchable' then
  674. FMakeSearchable := True
  675. else if Cmd = '--chm-title' then
  676. FChmTitle := arg
  677. else
  678. Result:=inherited InterPretOption(Cmd, Arg);
  679. if Length(FChmTitle) = 0 then
  680. FChmTitle := Copy(Package.Name, 2, Length(Package.Name));
  681. end;
  682. class procedure TCHMHTMLWriter.Usage(List: TStrings);
  683. begin
  684. THTMLWriter.Usage(List);
  685. List.add('--default-page');
  686. List.Add(SCHMUsageDefPage);
  687. List.add('--toc-file');
  688. List.Add(SCHMUsageTOC);
  689. List.add('--index-file');
  690. List.Add(SCHMUsageIndex);
  691. List.add('--other-files');
  692. List.Add(SCHMUsageOtrFiles);
  693. List.add('--css-file');
  694. List.Add(SCHMUsageCSSFile);
  695. List.add('--auto-index');
  696. List.Add(SCHMUsageAutoIDX);
  697. List.add('--auto-toc');
  698. List.Add(SCHMUsageAutoTOC);
  699. List.add('--make-searchable');
  700. List.Add(SCHMUsageMakeSearch);
  701. List.Add('--chm-title');
  702. List.Add(SCHMUsageChmTitle);
  703. end;
  704. class function TCHMHTMLWriter.FileNameExtension: String;
  705. begin
  706. result:='.chm';
  707. end;
  708. class procedure TCHMHTMLWriter.SplitImport(var AFilename, ALinkPrefix: String);
  709. var
  710. i: integer;
  711. begin
  712. i := Pos(',', AFilename);
  713. if i > 0 then
  714. begin //split into filename and prefix
  715. ALinkPrefix := Copy(AFilename,i+1,Length(AFilename));
  716. SetLength(AFilename, i-1);
  717. if copy(ALinkPrefix,1,2)='..' then // workaround for project files.
  718. begin
  719. ALinkPrefix := 'ms-its:' + ChangeFileExt(ExtractFileName(AFilename), '.chm') + '::/';
  720. AFilename := ChangeFileExt(AFilename, '.xct');
  721. end;
  722. end
  723. else if ALinkPrefix = '' then
  724. begin //synthesize outdir\pgk.xct, ms-its:pkg.chm::/
  725. ALinkPrefix := 'ms-its:' + ChangeFileExt(ExtractFileName(AFilename), '.chm') + '::/';
  726. AFilename := ChangeFileExt(AFilename, '.xct');
  727. end;
  728. end;
  729. initialization
  730. RegisterWriter(TCHMHTMLWriter,'chm','Compressed HTML file output using fpdoc.css stylesheet.');
  731. finalization
  732. UnRegisterWriter('chm');
  733. end.