dw_chm.pp 24 KB

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