whtmlhlp.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. unit WHTMLHlp;
  2. interface
  3. uses Objects,WHTML,WHelp;
  4. const
  5. ListIndent = 2;
  6. DefIndent = 4;
  7. MaxTopicLinks = 100;
  8. type
  9. PTopicLinkCollection = ^TTopicLinkCollection;
  10. TTopicLinkCollection = object(TStringCollection)
  11. procedure Insert(Item: Pointer); virtual;
  12. function At(Index: sw_Integer): PString;
  13. function AddItem(const Item: string): Sw_integer;
  14. end;
  15. TParagraphAlign = (paLeft,paCenter,paRight);
  16. PHTMLTopicRenderer = ^THTMLTopicRenderer;
  17. THTMLTopicRenderer = object(THTMLParser)
  18. function BuildTopic(P: PTopic; AURL: string; HTMLFile: PTextFile; ATopicLinks: PTopicLinkCollection): boolean;
  19. public
  20. procedure DocAddTextChar(C: char); virtual;
  21. procedure DocSoftBreak; virtual;
  22. procedure DocTYPE; virtual;
  23. procedure DocHTML(Entered: boolean); virtual;
  24. procedure DocHEAD(Entered: boolean); virtual;
  25. procedure DocMETA; virtual;
  26. procedure DocTITLE(Entered: boolean); virtual;
  27. procedure DocBODY(Entered: boolean); virtual;
  28. procedure DocAnchor(Entered: boolean); virtual;
  29. procedure DocHeading(Level: integer; Entered: boolean); virtual;
  30. procedure DocParagraph(Entered: boolean); virtual;
  31. procedure DocBreak; virtual;
  32. procedure DocImage; virtual;
  33. procedure DocBold(Entered: boolean); virtual;
  34. procedure DocCite(Entered: boolean); virtual;
  35. procedure DocCode(Entered: boolean); virtual;
  36. procedure DocEmphasized(Entered: boolean); virtual;
  37. procedure DocItalic(Entered: boolean); virtual;
  38. procedure DocKbd(Entered: boolean); virtual;
  39. procedure DocPreformatted(Entered: boolean); virtual;
  40. procedure DocSample(Entered: boolean); virtual;
  41. procedure DocStrong(Entered: boolean); virtual;
  42. procedure DocTeleType(Entered: boolean); virtual;
  43. procedure DocVariable(Entered: boolean); virtual;
  44. procedure DocList(Entered: boolean); virtual;
  45. procedure DocOrderedList(Entered: boolean); virtual;
  46. procedure DocListItem; virtual;
  47. procedure DocDefList(Entered: boolean); virtual;
  48. procedure DocDefTerm; virtual;
  49. procedure DocDefExp; virtual;
  50. procedure DocHorizontalRuler; virtual;
  51. private
  52. URL: string;
  53. Topic: PTopic;
  54. TopicLinks: PTopicLinkCollection;
  55. TextPtr: word;
  56. InTitle: boolean;
  57. InBody: boolean;
  58. InAnchor: boolean;
  59. InParagraph: boolean;
  60. InPreformatted: boolean;
  61. TopicTitle: string;
  62. Indent: integer;
  63. AnyCharsInLine: boolean;
  64. CurHeadLevel: integer;
  65. PAlign: TParagraphAlign;
  66. LinkIndexes: array[0..MaxTopicLinks] of sw_integer;
  67. LinkPtr: sw_integer;
  68. { Anchor: TAnchor;}
  69. procedure AddText(S: string);
  70. procedure AddChar(C: char);
  71. end;
  72. PHTMLHelpFile = ^THTMLHelpFile;
  73. THTMLHelpFile = object(THelpFile)
  74. constructor Init(AFileName: string; AID: word; ATOCEntry: string);
  75. destructor Done; virtual;
  76. public
  77. function LoadIndex: boolean; virtual;
  78. function SearchTopic(HelpCtx: THelpCtx): PTopic; virtual;
  79. function ReadTopic(T: PTopic): boolean; virtual;
  80. private
  81. Renderer: PHTMLTopicRenderer;
  82. FileName: string;
  83. CurFileName: string;
  84. TOCEntry: string;
  85. TopicLinks: PTopicLinkCollection;
  86. end;
  87. implementation
  88. uses Dos;
  89. const
  90. {$ifdef LINUX}
  91. dirsep = '/';
  92. {$else}
  93. dirsep = '\';
  94. {$endif}
  95. function FormatPath(Path: string): string;
  96. var P: sw_integer;
  97. begin
  98. repeat
  99. if DirSep='/' then P:=Pos('\',Path)
  100. else P:=Pos('/',Path);
  101. if P>0 then Path[P]:=DirSep;
  102. until P=0;
  103. FormatPath:=Path;
  104. end;
  105. function CompletePath(const Base, InComplete: string): string;
  106. var Drv,BDrv: string[40]; D,BD: DirStr; N,BN: NameStr; E,BE: ExtStr;
  107. P: sw_integer;
  108. Complete: string;
  109. begin
  110. Complete:=FormatPath(InComplete);
  111. FSplit(FormatPath(InComplete),D,N,E);
  112. P:=Pos(':',D); if P=0 then Drv:='' else begin Drv:=copy(D,1,P); Delete(D,1,P); end;
  113. FSplit(FormatPath(Base),BD,BN,BE);
  114. P:=Pos(':',BD); if P=0 then BDrv:='' else begin BDrv:=copy(BD,1,P); Delete(BD,1,P); end;
  115. if copy(D,1,1)<>'\' then
  116. Complete:=BD+D+N+E;
  117. if Drv='' then
  118. Complete:=BDrv+Complete;
  119. Complete:=FExpand(Complete);
  120. CompletePath:=Complete;
  121. end;
  122. function UpcaseStr(S: string): string;
  123. var I: integer;
  124. begin
  125. for I:=1 to length(S) do
  126. S[I]:=Upcase(S[I]);
  127. UpcaseStr:=S;
  128. end;
  129. function EncodeHTMLCtx(FileID: integer; LinkNo: word): longint;
  130. var Ctx: longint;
  131. begin
  132. Ctx:=(longint(FileID) shl 16)+LinkNo;
  133. EncodeHTMLCtx:=Ctx;
  134. end;
  135. procedure DecodeHTMLCtx(Ctx: longint; var FileID: word; var LinkNo: word);
  136. begin
  137. if (Ctx shr 16)=0 then
  138. begin
  139. FileID:=$ffff; LinkNo:=0;
  140. end
  141. else
  142. begin
  143. FileID:=Ctx shr 16; LinkNo:=Ctx and $ffff;
  144. end;
  145. end;
  146. function CharStr(C: char; Count: byte): string;
  147. var S: string;
  148. begin
  149. S[0]:=chr(Count);
  150. if Count>0 then FillChar(S[1],Count,C);
  151. CharStr:=S;
  152. end;
  153. procedure TTopicLinkCollection.Insert(Item: Pointer);
  154. begin
  155. AtInsert(Count,Item);
  156. end;
  157. function TTopicLinkCollection.At(Index: sw_Integer): PString;
  158. begin
  159. At:=inherited At(Index);
  160. end;
  161. function TTopicLinkCollection.AddItem(const Item: string): Sw_integer;
  162. var Idx: sw_integer;
  163. begin
  164. if Item='' then Idx:=-1 else
  165. if Search(@Item,Idx)=false then
  166. begin
  167. AtInsert(Count,NewStr(Item));
  168. Idx:=Count-1;
  169. end;
  170. AddItem:=Idx;
  171. end;
  172. procedure THTMLTopicRenderer.DocAddTextChar(C: char);
  173. begin
  174. if InTitle then TopicTitle:=TopicTitle+C else
  175. if InBody then
  176. begin
  177. if (C<>#32) or (AnyCharsInLine=true) then AddChar(C);
  178. end;
  179. end;
  180. procedure THTMLTopicRenderer.DocSoftBreak;
  181. begin
  182. if InPreformatted then DocBreak else
  183. if AnyCharsInLine then AddChar(' ');
  184. end;
  185. procedure THTMLTopicRenderer.DocTYPE;
  186. begin
  187. end;
  188. procedure THTMLTopicRenderer.DocHTML(Entered: boolean);
  189. begin
  190. end;
  191. procedure THTMLTopicRenderer.DocHEAD(Entered: boolean);
  192. begin
  193. end;
  194. procedure THTMLTopicRenderer.DocMETA;
  195. begin
  196. end;
  197. procedure THTMLTopicRenderer.DocTITLE(Entered: boolean);
  198. begin
  199. if Entered then
  200. begin
  201. TopicTitle:='';
  202. end
  203. else
  204. begin
  205. { render topic title here }
  206. if TopicTitle<>'' then
  207. begin
  208. AddText(' '+TopicTitle+' Ü'); DocBreak;
  209. AddText(' '+CharStr('ß',length(TopicTitle)+3)); DocBreak;
  210. end;
  211. end;
  212. InTitle:=Entered;
  213. end;
  214. procedure THTMLTopicRenderer.DocBODY(Entered: boolean);
  215. begin
  216. InBody:=Entered;
  217. end;
  218. procedure THTMLTopicRenderer.DocAnchor(Entered: boolean);
  219. var HRef: string;
  220. begin
  221. if Entered and InAnchor then DocAnchor(false);
  222. if Entered then
  223. begin
  224. if DocGetTagParam('HREF',HRef)=false then HRef:='';
  225. if (HRef<>'') and (copy(HRef,1,1)<>'#') then
  226. begin
  227. InAnchor:=true;
  228. AddChar(hscLink);
  229. HRef:=CompletePath(URL,HRef);
  230. LinkIndexes[LinkPtr]:=TopicLinks^.AddItem(HRef);
  231. Inc(LinkPtr);
  232. end;
  233. end
  234. else
  235. begin
  236. if InAnchor=true then AddChar(hscLink);
  237. InAnchor:=false;
  238. end;
  239. end;
  240. procedure DecodeAlign(Align: string; var PAlign: TParagraphAlign);
  241. begin
  242. Align:=UpcaseStr(Align);
  243. if Align='LEFT' then PAlign:=paLeft else
  244. if Align='CENTER' then PAlign:=paCenter else
  245. if Align='RIGHT' then PAlign:=paRight;
  246. end;
  247. procedure THTMLTopicRenderer.DocHeading(Level: integer; Entered: boolean);
  248. var Align: string;
  249. begin
  250. if Entered then
  251. begin
  252. DocBreak;
  253. CurHeadLevel:=Level;
  254. PAlign:=paLeft;
  255. if DocGetTagParam('ALIGN',Align) then
  256. DecodeAlign(Align,PAlign);
  257. end
  258. else
  259. begin
  260. CurHeadLevel:=0;
  261. DocBreak;
  262. end;
  263. end;
  264. procedure THTMLTopicRenderer.DocParagraph(Entered: boolean);
  265. var Align: string;
  266. begin
  267. if Entered and InParagraph then DocParagraph(false);
  268. if Entered then
  269. begin
  270. if AnyCharsInLine then DocBreak;
  271. if DocGetTagParam('ALIGN',Align) then
  272. DecodeAlign(Align,PAlign);
  273. end
  274. else
  275. begin
  276. { if AnyCharsInLine then }DocBreak;
  277. PAlign:=paLeft;
  278. end;
  279. InParagraph:=Entered;
  280. end;
  281. procedure THTMLTopicRenderer.DocBreak;
  282. begin
  283. if (CurHeadLevel=1) or (PAlign=paCenter) then
  284. AddChar(hscCenter);
  285. if (PAlign=paRight) then
  286. AddChar(hscRight);
  287. AddChar(hscLineBreak);
  288. if Indent>0 then
  289. AddText(CharStr(#255,Indent)+hscLineStart);
  290. AnyCharsInLine:=false;
  291. end;
  292. procedure THTMLTopicRenderer.DocImage;
  293. var Alt: string;
  294. begin
  295. if DocGetTagParam('ALT',Alt)=false then Alt:='IMG';
  296. if Alt<>'' then
  297. begin
  298. AddText('['+Alt+']');
  299. end;
  300. end;
  301. procedure THTMLTopicRenderer.DocBold(Entered: boolean);
  302. begin
  303. end;
  304. procedure THTMLTopicRenderer.DocCite(Entered: boolean);
  305. begin
  306. end;
  307. procedure THTMLTopicRenderer.DocCode(Entered: boolean);
  308. begin
  309. end;
  310. procedure THTMLTopicRenderer.DocEmphasized(Entered: boolean);
  311. begin
  312. end;
  313. procedure THTMLTopicRenderer.DocItalic(Entered: boolean);
  314. begin
  315. end;
  316. procedure THTMLTopicRenderer.DocKbd(Entered: boolean);
  317. begin
  318. end;
  319. procedure THTMLTopicRenderer.DocPreformatted(Entered: boolean);
  320. begin
  321. if AnyCharsInLine then DocBreak;
  322. DocBreak;
  323. InPreformatted:=Entered;
  324. end;
  325. procedure THTMLTopicRenderer.DocSample(Entered: boolean);
  326. begin
  327. end;
  328. procedure THTMLTopicRenderer.DocStrong(Entered: boolean);
  329. begin
  330. end;
  331. procedure THTMLTopicRenderer.DocTeleType(Entered: boolean);
  332. begin
  333. end;
  334. procedure THTMLTopicRenderer.DocVariable(Entered: boolean);
  335. begin
  336. end;
  337. procedure THTMLTopicRenderer.DocList(Entered: boolean);
  338. begin
  339. if Entered then
  340. begin
  341. Inc(Indent,ListIndent);
  342. DocBreak;
  343. end
  344. else
  345. begin
  346. Dec(Indent,ListIndent);
  347. if AnyCharsInLine then DocBreak;
  348. end;
  349. end;
  350. procedure THTMLTopicRenderer.DocOrderedList(Entered: boolean);
  351. begin
  352. DocList(Entered);
  353. end;
  354. procedure THTMLTopicRenderer.DocListItem;
  355. begin
  356. if AnyCharsInLine then
  357. DocBreak;
  358. AddText('þ'+hscLineStart);
  359. end;
  360. procedure THTMLTopicRenderer.DocDefList(Entered: boolean);
  361. begin
  362. if Entered then
  363. begin
  364. { if LastChar<>hscLineBreak then DocBreak;}
  365. end
  366. else
  367. begin
  368. if AnyCharsInLine then DocBreak;
  369. end;
  370. end;
  371. procedure THTMLTopicRenderer.DocDefTerm;
  372. begin
  373. DocBreak;
  374. end;
  375. procedure THTMLTopicRenderer.DocDefExp;
  376. begin
  377. Inc(Indent,DefIndent);
  378. DocBreak;
  379. Dec(Indent,DefIndent);
  380. end;
  381. procedure THTMLTopicRenderer.DocHorizontalRuler;
  382. var OAlign: TParagraphAlign;
  383. begin
  384. OAlign:=PAlign;
  385. if AnyCharsInLine then DocBreak;
  386. PAlign:=paCenter;
  387. DocAddText(' '+CharStr('Ä',60)+' ');
  388. DocBreak;
  389. PAlign:=OAlign;
  390. end;
  391. procedure THTMLTopicRenderer.AddChar(C: char);
  392. begin
  393. if Topic=nil then Exit;
  394. Topic^.Text^[TextPtr]:=ord(C);
  395. Inc(TextPtr);
  396. if (C>#15) and (C<>' ') then
  397. AnyCharsInLine:=true;
  398. end;
  399. procedure THTMLTopicRenderer.AddText(S: string);
  400. var I: sw_integer;
  401. begin
  402. for I:=1 to length(S) do
  403. AddChar(S[I]);
  404. end;
  405. function THTMLTopicRenderer.BuildTopic(P: PTopic; AURL: string; HTMLFile: PTextFile;
  406. ATopicLinks: PTopicLinkCollection): boolean;
  407. var OK: boolean;
  408. TP: pointer;
  409. I: sw_integer;
  410. begin
  411. URL:=AURL;
  412. Topic:=P; TopicLinks:=ATopicLinks;
  413. OK:=Assigned(Topic) and Assigned(HTMLFile) and Assigned(TopicLinks);
  414. if OK then
  415. begin
  416. if (Topic^.TextSize<>0) and Assigned(Topic^.Text) then
  417. begin
  418. FreeMem(Topic^.Text,Topic^.TextSize);
  419. Topic^.TextSize:=0; Topic^.Text:=nil;
  420. end;
  421. Topic^.TextSize:=MaxHelpTopicSize;
  422. GetMem(Topic^.Text,Topic^.TextSize);
  423. TopicTitle:='';
  424. InTitle:=false; InBody:=false; InAnchor:=false;
  425. InParagraph:=false; InPreformatted:=false;
  426. Indent:=0; CurHeadLevel:=0;
  427. PAlign:=paLeft;
  428. TextPtr:=0; LinkPtr:=0;
  429. AnyCharsInLine:=false;
  430. OK:=Process(HTMLFile);
  431. if OK then
  432. begin
  433. { --- topic links --- }
  434. if (Topic^.Links<>nil) and (Topic^.LinkSize>0) then
  435. begin
  436. FreeMem(Topic^.Links,Topic^.LinkSize);
  437. Topic^.Links:=nil; Topic^.LinkSize:=0; Topic^.LinkCount:=0;
  438. end;
  439. Topic^.LinkCount:=TopicLinks^.Count;
  440. Topic^.LinkSize:=SizeOf(Topic^.Links^[0])*Topic^.LinkCount;
  441. GetMem(Topic^.Links,Topic^.LinkSize);
  442. for I:=0 to Topic^.LinkCount-1 do
  443. begin
  444. Topic^.Links^[I].FileID:=Topic^.FileID;
  445. Topic^.Links^[I].Context:=EncodeHTMLCtx(Topic^.FileID,LinkIndexes[I]+1);
  446. end;
  447. { --- topic text --- }
  448. GetMem(TP,TextPtr);
  449. Move(Topic^.Text^,TP^,TextPtr);
  450. FreeMem(Topic^.Text,Topic^.TextSize);
  451. Topic^.Text:=TP; Topic^.TextSize:=TextPtr;
  452. end
  453. else
  454. begin
  455. DisposeTopic(Topic);
  456. Topic:=nil;
  457. end;
  458. end;
  459. BuildTopic:=OK;
  460. end;
  461. constructor THTMLHelpFile.Init(AFileName: string; AID: word; ATOCEntry: string);
  462. begin
  463. inherited Init(AID);
  464. FileName:=AFileName; TOCEntry:=ATOCEntry;
  465. if FileName='' then Fail;
  466. New(Renderer, Init);
  467. New(TopicLinks, Init(50,500));
  468. end;
  469. function THTMLHelpFile.LoadIndex: boolean;
  470. begin
  471. IndexEntries^.Insert(NewIndexEntry(TOCEntry,ID,0));
  472. LoadIndex:=true;
  473. end;
  474. function THTMLHelpFile.SearchTopic(HelpCtx: THelpCtx): PTopic;
  475. function MatchCtx(P: PTopic): boolean; {$ifndef FPC}far;{$endif}
  476. begin
  477. MatchCtx:=P^.HelpCtx=HelpCtx;
  478. end;
  479. var FileID,LinkNo: word;
  480. P: PTopic;
  481. FName: string;
  482. begin
  483. DecodeHTMLCtx(HelpCtx,FileID,LinkNo);
  484. if (HelpCtx<>0) and (FileID<>ID) then P:=nil else
  485. if (FileID=ID) and (LinkNo>TopicLinks^.Count) then P:=nil else
  486. begin
  487. P:=Topics^.FirstThat(@MatchCtx);
  488. if P=nil then
  489. begin
  490. if LinkNo=0 then
  491. FName:=FileName
  492. else
  493. FName:=TopicLinks^.At(LinkNo-1)^;
  494. P:=NewTopic(ID,HelpCtx,0,FName);
  495. Topics^.Insert(P);
  496. end;
  497. end;
  498. SearchTopic:=P;
  499. end;
  500. function THTMLHelpFile.ReadTopic(T: PTopic): boolean;
  501. var OK: boolean;
  502. HTMLFile: PDOSTextFile;
  503. Name: string;
  504. Link: string;
  505. FileNo,LinkNo : word;
  506. P: sw_integer;
  507. begin
  508. OK:=T<>nil;
  509. if OK then
  510. begin
  511. if (T^.HelpCtx=0) then
  512. Name:=FileName
  513. else
  514. begin
  515. DecodeHTMLCtx(T^.HelpCtx-1,FileNo,LinkNo);
  516. Link:=TopicLinks^.At(LinkNo)^;
  517. Link:=FormatPath(Link);
  518. P:=Pos('#',Link); if P>0 then Delete(Link,P,255);
  519. { if CurFileName='' then Name:=Link else
  520. Name:=CompletePath(CurFileName,Link);}
  521. Name:=Link;
  522. end;
  523. New(HTMLFile, Init(Name));
  524. OK:=Renderer^.BuildTopic(T,Name,HTMLFile,TopicLinks);
  525. if OK then CurFileName:=Name;
  526. if HTMLFile<>nil then Dispose(HTMLFile, Done);
  527. end;
  528. ReadTopic:=OK;
  529. end;
  530. destructor THTMLHelpFile.Done;
  531. begin
  532. inherited Done;
  533. if Renderer<>nil then Dispose(Renderer, Done);
  534. if TopicLinks<>nil then Dispose(TopicLinks, Done);
  535. end;
  536. END.