dw_chm.pp 24 KB

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