dw_chm.pp 24 KB

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