dwriter.pp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853
  1. {
  2. $Id$
  3. FPDoc - Free Pascal Documentation Tool
  4. Copyright (C) 2000 - 2003 by
  5. Areca Systems GmbH / Sebastian Guenther, [email protected]
  6. * Output string definitions
  7. * Basic writer (output generator) class
  8. See the file COPYING, included in this distribution,
  9. for details about the copyright.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  13. }
  14. unit dWriter;
  15. {$MODE objfpc}
  16. {$H+}
  17. interface
  18. uses Classes, DOM, dGlobals, PasTree;
  19. resourcestring
  20. SErrFileWriting = 'An error occured during writing of file "%s": %s';
  21. SErrInvalidShortDescr = 'Invalid short description';
  22. SErrInvalidDescr = 'Invalid description (illegal XML element: "%s")';
  23. SErrInvalidParaContent = 'Invalid paragraph content';
  24. SErrInvalidElementInList = 'Invalid element in list - only "li" allowed';
  25. SErrInvalidListContent = 'Invalid list content';
  26. SErrInvalidRemarkContent = 'Invalid <remark> content (illegal XML element: "%s")';
  27. SErrListIsEmpty = 'List is empty - need at least one "li" element';
  28. SErrInvalidDefinitionTermContent = 'Invalid content in definition term';
  29. SErrDefinitionEntryMissing = 'Definition entry after definition term is missing';
  30. SErrInvalidBorderValue = 'Invalid "border" value for %s';
  31. SErrInvalidTableContent = 'Invalid table content';
  32. SErrTableRowEmpty = 'Table row is empty (no "td" elements found)';
  33. SErrInvalidContentBeforeSectionTitle = 'Invalid content before section title';
  34. SErrSectionTitleExpected = 'Section title ("title" element) expected';
  35. SErrDescrTagUnknown = 'Warning: Unknown tag "%s" in description';
  36. SErrUnknownEntityReference = 'Warning: Unknown entity reference "&%s;" found';
  37. SErrUnknownLinkID = 'Warning: Target ID of <link> is unknown: "%s"';
  38. SErrUnknownPrintShortID = 'Warning: Target ID of <printshort> is unknown: "%s"';
  39. SErrUnknownLink = 'Could not resolve link to "%s"';
  40. type
  41. // Phony element for pas pages.
  42. TTopicElement = Class(TPaselement)
  43. Constructor Create(const AName: String; AParent: TPasElement); override;
  44. Destructor Destroy; override;
  45. TopicNode : TDocNode;
  46. Previous,
  47. Next : TPasElement;
  48. Subtopics : TList;
  49. end;
  50. TFPDocWriter = class
  51. private
  52. FEngine : TFPDocEngine;
  53. FTopics : TList;
  54. protected
  55. procedure Warning(AContext: TPasElement; const AMsg: String);
  56. procedure Warning(AContext: TPasElement; const AMsg: String;
  57. const Args: array of const);
  58. // function FindShortDescr(const Name: String): TDOMElement;
  59. // Description conversion
  60. function IsDescrNodeEmpty(Node: TDOMNode): Boolean;
  61. function IsExtShort(Node: TDOMNode): Boolean;
  62. function ConvertShort(AContext: TPasElement; El: TDOMElement): Boolean;
  63. function ConvertBaseShort(AContext: TPasElement; Node: TDOMNode): Boolean;
  64. procedure ConvertBaseShortList(AContext: TPasElement; Node: TDOMNode;
  65. MayBeEmpty: Boolean);
  66. procedure ConvertLink(AContext: TPasElement; El: TDOMElement);
  67. function ConvertExtShort(AContext: TPasElement; Node: TDOMNode): Boolean;
  68. procedure ConvertDescr(AContext: TPasElement; El: TDOMElement;
  69. AutoInsertBlock: Boolean);
  70. function ConvertNonSectionBlock(AContext: TPasElement;
  71. Node: TDOMNode): Boolean;
  72. procedure ConvertExtShortOrNonSectionBlocks(AContext: TPasElement;
  73. Node: TDOMNode);
  74. function ConvertSimpleBlock(AContext: TPasElement; Node: TDOMNode): Boolean;
  75. Function FindTopicElement(Node : TDocNode): TTopicElement;
  76. procedure DescrWriteText(const AText: DOMString); virtual; abstract;
  77. procedure DescrBeginBold; virtual; abstract;
  78. procedure DescrEndBold; virtual; abstract;
  79. procedure DescrBeginItalic; virtual; abstract;
  80. procedure DescrEndItalic; virtual; abstract;
  81. procedure DescrBeginEmph; virtual; abstract;
  82. procedure DescrEndEmph; virtual; abstract;
  83. procedure DescrWriteFileEl(const AText: DOMString); virtual; abstract;
  84. procedure DescrWriteKeywordEl(const AText: DOMString); virtual; abstract;
  85. procedure DescrWriteVarEl(const AText: DOMString); virtual; abstract;
  86. procedure DescrBeginLink(const AId: DOMString); virtual; abstract;
  87. procedure DescrEndLink; virtual; abstract;
  88. procedure DescrWriteLinebreak; virtual; abstract;
  89. procedure DescrBeginParagraph; virtual; abstract;
  90. procedure DescrEndParagraph; virtual; abstract;
  91. procedure DescrBeginCode(HasBorder: Boolean; const AHighlighterName: String); virtual; abstract;
  92. procedure DescrWriteCodeLine(const ALine: String); virtual; abstract;
  93. procedure DescrEndCode; virtual; abstract;
  94. procedure DescrBeginOrderedList; virtual; abstract;
  95. procedure DescrEndOrderedList; virtual; abstract;
  96. procedure DescrBeginUnorderedList; virtual; abstract;
  97. procedure DescrEndUnorderedList; virtual; abstract;
  98. procedure DescrBeginDefinitionList; virtual; abstract;
  99. procedure DescrEndDefinitionList; virtual; abstract;
  100. procedure DescrBeginListItem; virtual; abstract;
  101. procedure DescrEndListItem; virtual; abstract;
  102. procedure DescrBeginDefinitionTerm; virtual; abstract;
  103. procedure DescrEndDefinitionTerm; virtual; abstract;
  104. procedure DescrBeginDefinitionEntry; virtual; abstract;
  105. procedure DescrEndDefinitionEntry; virtual; abstract;
  106. procedure DescrBeginSectionTitle; virtual; abstract;
  107. procedure DescrBeginSectionBody; virtual; abstract;
  108. procedure DescrEndSection; virtual; abstract;
  109. procedure DescrBeginRemark; virtual; abstract;
  110. procedure DescrEndRemark; virtual; abstract;
  111. procedure DescrBeginTable(ColCount: Integer; HasBorder: Boolean); virtual; abstract;
  112. procedure DescrEndTable; virtual; abstract;
  113. procedure DescrBeginTableCaption; virtual; abstract;
  114. procedure DescrEndTableCaption; virtual; abstract;
  115. procedure DescrBeginTableHeadRow; virtual; abstract;
  116. procedure DescrEndTableHeadRow; virtual; abstract;
  117. procedure DescrBeginTableRow; virtual; abstract;
  118. procedure DescrEndTableRow; virtual; abstract;
  119. procedure DescrBeginTableCell; virtual; abstract;
  120. procedure DescrEndTableCell; virtual; abstract;
  121. public
  122. constructor Create(AEngine: TFPDocEngine);
  123. destructor Destroy; override;
  124. property Engine : TFPDocEngine read FEngine;
  125. Property Topics : TList Read FTopics;
  126. end;
  127. implementation
  128. uses SysUtils;
  129. constructor TFPDocWriter.Create(AEngine: TFPDocEngine);
  130. begin
  131. inherited Create;
  132. FEngine := AEngine;
  133. FTopics:=Tlist.Create;
  134. end;
  135. destructor TFPDocWriter.Destroy;
  136. Var
  137. i : integer;
  138. begin
  139. For I:=0 to FTopics.Count-1 do
  140. TTopicElement(FTopics[i]).Free;
  141. FTopics.Free;
  142. Inherited;
  143. end;
  144. Function TFPDocWriter.FindTopicElement(Node : TDocNode): TTopicElement;
  145. Var
  146. I : Integer;
  147. begin
  148. Result:=Nil;
  149. I:=FTopics.Count-1;
  150. While (I>=0) and (Result=Nil) do
  151. begin
  152. If (TTopicElement(FTopics[i]).TopicNode=Node) Then
  153. Result:=TTopicElement(FTopics[i]);
  154. Dec(I);
  155. end;
  156. end;
  157. // ===================================================================
  158. // Generic documentation node conversion
  159. // ===================================================================
  160. function IsContentNodeType(Node: TDOMNode): Boolean;
  161. begin
  162. Result := (Node.NodeType = ELEMENT_NODE) or (Node.NodeType = TEXT_NODE) or
  163. (Node.NodeType = ENTITY_REFERENCE_NODE);
  164. end;
  165. procedure TFPDocWriter.Warning(AContext: TPasElement; const AMsg: String);
  166. begin
  167. if (AContext<>nil) then
  168. WriteLn('[', AContext.PathName, '] ', AMsg)
  169. else
  170. WriteLn('[<no context>] ', AMsg);
  171. end;
  172. procedure TFPDocWriter.Warning(AContext: TPasElement; const AMsg: String;
  173. const Args: array of const);
  174. begin
  175. Warning(AContext, Format(AMsg, Args));
  176. end;
  177. function TFPDocWriter.IsDescrNodeEmpty(Node: TDOMNode): Boolean;
  178. var
  179. Child: TDOMNode;
  180. begin
  181. if (not Assigned(Node)) or (not Assigned(Node.FirstChild)) then
  182. Result := True
  183. else
  184. begin
  185. Child := Node.FirstChild;
  186. while Assigned(Child) do
  187. begin
  188. if (Child.NodeType = ELEMENT_NODE) or (Child.NodeType = TEXT_NODE) or
  189. (Child.NodeType = ENTITY_REFERENCE_NODE) then
  190. begin
  191. Result := False;
  192. exit;
  193. end;
  194. Child := Child.NextSibling;
  195. end;
  196. end;
  197. Result := True;
  198. end;
  199. { Check wether the nodes starting with the node given as argument make up an
  200. 'extshort' production. }
  201. function TFPDocWriter.IsExtShort(Node: TDOMNode): Boolean;
  202. begin
  203. while Assigned(Node) do
  204. begin
  205. if Node.NodeType = ELEMENT_NODE then
  206. if (Node.NodeName <> 'br') and
  207. (Node.NodeName <> 'link') and
  208. (Node.NodeName <> 'b') and
  209. (Node.NodeName <> 'file') and
  210. (Node.NodeName <> 'i') and
  211. (Node.NodeName <> 'kw') and
  212. (Node.NodeName <> 'printshort') and
  213. (Node.NodeName <> 'var') then
  214. begin
  215. Result := False;
  216. exit;
  217. end;
  218. Node := Node.NextSibling;
  219. end;
  220. Result := True;
  221. end;
  222. function TFPDocWriter.ConvertShort(AContext: TPasElement;
  223. El: TDOMElement): Boolean;
  224. var
  225. Node: TDOMNode;
  226. begin
  227. Result := False;
  228. if not Assigned(El) then
  229. exit;
  230. Node := El.FirstChild;
  231. while Assigned(Node) do
  232. begin
  233. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'link') then
  234. ConvertLink(AContext, TDOMElement(Node))
  235. else
  236. if not ConvertBaseShort(AContext, Node) then
  237. exit;
  238. Node := Node.NextSibling;
  239. end;
  240. Result := True;
  241. end;
  242. function TFPDocWriter.ConvertBaseShort(AContext: TPasElement;
  243. Node: TDOMNode): Boolean;
  244. function ConvertText: DOMString;
  245. var
  246. s: String;
  247. i: Integer;
  248. begin
  249. if Node.NodeType = TEXT_NODE then
  250. begin
  251. s := Node.NodeValue;
  252. i := 1;
  253. SetLength(Result, 0);
  254. while i <= Length(s) do
  255. if s[i] = #13 then
  256. begin
  257. Result := Result + ' ';
  258. Inc(i);
  259. if s[i] = #10 then
  260. Inc(i);
  261. end else if s[i] = #10 then
  262. begin
  263. Result := Result + ' ';
  264. Inc(i);
  265. end else
  266. begin
  267. Result := Result + s[i];
  268. Inc(i);
  269. end;
  270. end else if Node.NodeType = ENTITY_REFERENCE_NODE then
  271. if Node.NodeName = 'fpc' then
  272. Result := 'Free Pascal'
  273. else if Node.NodeName = 'delphi' then
  274. Result := 'Delphi'
  275. else
  276. begin
  277. Warning(AContext, Format(SErrUnknownEntityReference, [Node.NodeName]));
  278. Result := Node.NodeName;
  279. end
  280. else if Node.NodeType = ELEMENT_NODE then
  281. SetLength(Result, 0);
  282. end;
  283. function ConvertTextContent: DOMString;
  284. begin
  285. SetLength(Result, 0);
  286. Node := Node.FirstChild;
  287. while Assigned(Node) do
  288. begin
  289. Result := Result + ConvertText;
  290. Node := Node.NextSibling;
  291. end;
  292. end;
  293. var
  294. El, DescrEl: TDOMElement;
  295. FPEl: TPasElement;
  296. begin
  297. Result := True;
  298. if Node.NodeType = ELEMENT_NODE then
  299. if Node.NodeName = 'b' then
  300. begin
  301. DescrBeginBold;
  302. ConvertBaseShortList(AContext, Node, False);
  303. DescrEndBold;
  304. end else
  305. if Node.NodeName = 'i' then
  306. begin
  307. DescrBeginItalic;
  308. ConvertBaseShortList(AContext, Node, False);
  309. DescrEndItalic;
  310. end else
  311. if Node.NodeName = 'em' then
  312. begin
  313. DescrBeginEmph;
  314. ConvertBaseShortList(AContext, Node, False);
  315. DescrEndEmph;
  316. end else
  317. if Node.NodeName = 'file' then
  318. DescrWriteFileEl(ConvertTextContent)
  319. else if Node.NodeName = 'kw' then
  320. DescrWriteKeywordEl(ConvertTextContent)
  321. else if Node.NodeName = 'printshort' then
  322. begin
  323. El := TDOMElement(Node);
  324. DescrEl := Engine.FindShortDescr(AContext.GetModule, El['id']);
  325. if Assigned(DescrEl) then
  326. ConvertShort(AContext, DescrEl)
  327. else
  328. begin
  329. Warning(AContext, Format(SErrUnknownPrintShortID, [El['id']]));
  330. DescrBeginBold;
  331. DescrWriteText('#ShortDescr:' + El['id']);
  332. DescrEndBold;
  333. end;
  334. end else if Node.NodeName = 'var' then
  335. DescrWriteVarEl(ConvertTextContent)
  336. else
  337. Result := False
  338. else
  339. DescrWriteText(ConvertText);
  340. end;
  341. procedure TFPDocWriter.ConvertBaseShortList(AContext: TPasElement;
  342. Node: TDOMNode; MayBeEmpty: Boolean);
  343. var
  344. Child: TDOMNode;
  345. begin
  346. Child := Node.FirstChild;
  347. while Assigned(Child) do
  348. begin
  349. if not ConvertBaseShort(AContext, Child) then
  350. Warning(AContext, SErrInvalidShortDescr)
  351. else
  352. MayBeEmpty := True;
  353. Child := Child.NextSibling;
  354. end;
  355. if not MayBeEmpty then
  356. Warning(AContext, SErrInvalidShortDescr)
  357. end;
  358. procedure TFPDocWriter.ConvertLink(AContext: TPasElement; El: TDOMElement);
  359. begin
  360. DescrBeginLink(El['id']);
  361. if not IsDescrNodeEmpty(El) then
  362. ConvertBaseShortList(AContext, El, True)
  363. else
  364. DescrWriteText(El['id']);
  365. DescrEndLink;
  366. end;
  367. function TFPDocWriter.ConvertExtShort(AContext: TPasElement;
  368. Node: TDOMNode): Boolean;
  369. begin
  370. Result := False;
  371. while Assigned(Node) do
  372. begin
  373. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'link') then
  374. ConvertLink(AContext, TDOMElement(Node))
  375. else if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'br') then
  376. DescrWriteLinebreak
  377. else
  378. if not ConvertBaseShort(AContext, Node) then
  379. exit;
  380. Node := Node.NextSibling;
  381. end;
  382. Result := True;
  383. end;
  384. procedure TFPDocWriter.ConvertDescr(AContext: TPasElement; El: TDOMElement;
  385. AutoInsertBlock: Boolean);
  386. var
  387. Node, Child: TDOMNode;
  388. ParaCreated: Boolean;
  389. begin
  390. if AutoInsertBlock then
  391. if IsExtShort(El.FirstChild) then
  392. DescrBeginParagraph
  393. else
  394. AutoInsertBlock := False;
  395. Node := El.FirstChild;
  396. if not ConvertExtShort(AContext, Node) then
  397. begin
  398. while Assigned(Node) do
  399. begin
  400. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'section') then
  401. begin
  402. DescrBeginSectionTitle;
  403. Child := Node.FirstChild;
  404. while Assigned(Child) and (Child.NodeType <> ELEMENT_NODE) do
  405. begin
  406. if not IsDescrNodeEmpty(Child) then
  407. Warning(AContext, SErrInvalidContentBeforeSectionTitle);
  408. Child := Child.NextSibling;
  409. end;
  410. if not Assigned(Child) or (Child.NodeName <> 'title') then
  411. Warning(AContext, SErrSectionTitleExpected)
  412. else
  413. ConvertShort(AContext, TDOMElement(Child));
  414. DescrBeginSectionBody;
  415. if IsExtShort(Child) then
  416. begin
  417. DescrBeginParagraph;
  418. ParaCreated := True;
  419. end else
  420. ParaCreated := False;
  421. ConvertExtShortOrNonSectionBlocks(AContext, Child.NextSibling);
  422. if ParaCreated then
  423. DescrEndParagraph;
  424. DescrEndSection;
  425. end else if not ConvertNonSectionBlock(AContext, Node) then
  426. Warning(AContext, SErrInvalidDescr, [Node.NodeName]);
  427. Node := Node.NextSibling;
  428. end;
  429. end else
  430. if AutoInsertBlock then
  431. DescrEndParagraph;
  432. end;
  433. procedure TFPDocWriter.ConvertExtShortOrNonSectionBlocks(AContext: TPasElement;
  434. Node: TDOMNode);
  435. begin
  436. if not ConvertExtShort(AContext, Node) then
  437. while Assigned(Node) do
  438. begin
  439. if not ConvertNonSectionBlock(AContext, Node) then
  440. Warning(AContext, SErrInvalidDescr, [Node.NodeName]);
  441. Node := Node.NextSibling;
  442. end;
  443. end;
  444. function TFPDocWriter.ConvertNonSectionBlock(AContext: TPasElement;
  445. Node: TDOMNode): Boolean;
  446. procedure ConvertCells(Node: TDOMNode);
  447. var
  448. Child: TDOMNode;
  449. IsEmpty: Boolean;
  450. begin
  451. Node := Node.FirstChild;
  452. IsEmpty := True;
  453. while Assigned(Node) do
  454. begin
  455. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'td') then
  456. begin
  457. DescrBeginTableCell;
  458. Child := Node.FirstChild;
  459. if not ConvertExtShort(AContext, Child) then
  460. while Assigned(Child) do
  461. begin
  462. if not ConvertSimpleBlock(AContext, Child) then
  463. Warning(AContext, SErrInvalidTableContent);
  464. Child := Child.NextSibling;
  465. end;
  466. DescrEndTableCell;
  467. IsEmpty := False;
  468. end else
  469. if IsContentNodeType(Node) then
  470. Warning(AContext, SErrInvalidTableContent);
  471. Node := Node.NextSibling;
  472. end;
  473. if IsEmpty then
  474. Warning(AContext, SErrTableRowEmpty);
  475. end;
  476. procedure ConvertTable;
  477. function GetColCount(Node: TDOMNode): Integer;
  478. begin
  479. Result := 0;
  480. Node := Node.FirstChild;
  481. while Assigned(Node) do
  482. begin
  483. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'td') then
  484. Inc(Result);
  485. Node := Node.NextSibling;
  486. end;
  487. end;
  488. var
  489. s: String;
  490. HasBorder, CaptionPossible, HeadRowPossible: Boolean;
  491. ColCount, ThisRowColCount: Integer;
  492. Subnode: TDOMNode;
  493. begin
  494. s := TDOMElement(Node)['border'];
  495. if s = '1' then
  496. HasBorder := True
  497. else
  498. begin
  499. HasBorder := False;
  500. if (Length(s) <> 0) and (s <> '0') then
  501. Warning(AContext, SErrInvalidBorderValue, ['<table>']);
  502. end;
  503. // Determine the number of columns
  504. ColCount := 0;
  505. Subnode := Node.FirstChild;
  506. while Assigned(Subnode) do
  507. begin
  508. if Subnode.NodeType = ELEMENT_NODE then
  509. if (Subnode.NodeName = 'caption') or (Subnode.NodeName = 'th') or
  510. (Subnode.NodeName = 'tr') then
  511. begin
  512. ThisRowColCount := GetColCount(Subnode);
  513. if ThisRowColCount > ColCount then
  514. ColCount := ThisRowColCount;
  515. end;
  516. Subnode := Subnode.NextSibling;
  517. end;
  518. DescrBeginTable(ColCount, HasBorder);
  519. Node := Node.FirstChild;
  520. CaptionPossible := True;
  521. HeadRowPossible := True;
  522. while Assigned(Node) do
  523. begin
  524. if Node.NodeType = ELEMENT_NODE then
  525. if CaptionPossible and (Node.NodeName = 'caption') then
  526. begin
  527. DescrBeginTableCaption;
  528. if not ConvertExtShort(AContext, Node.FirstChild) then
  529. Warning(AContext, SErrInvalidTableContent);
  530. DescrEndTableCaption;
  531. CaptionPossible := False;
  532. end else if HeadRowPossible and (Node.NodeName = 'th') then
  533. begin
  534. DescrBeginTableHeadRow;
  535. ConvertCells(Node);
  536. DescrEndTableHeadRow;
  537. CaptionPossible := False;
  538. HeadRowPossible := False;
  539. end else if Node.NodeName = 'tr' then
  540. begin
  541. DescrBeginTableRow;
  542. ConvertCells(Node);
  543. DescrEndTableRow;
  544. end else
  545. Warning(AContext, SErrInvalidTableContent)
  546. else if IsContentNodeType(Node) then
  547. Warning(AContext, SErrInvalidTableContent);
  548. Node := Node.NextSibling;
  549. end;
  550. DescrEndTable;
  551. end;
  552. begin
  553. if Node.NodeType <> ELEMENT_NODE then
  554. begin
  555. Result := Node.NodeType = COMMENT_NODE;
  556. exit;
  557. end;
  558. if Node.NodeName = 'remark' then
  559. begin
  560. DescrBeginRemark;
  561. Node := Node.FirstChild;
  562. if not ConvertExtShort(AContext, Node) then
  563. while Assigned(Node) do
  564. begin
  565. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'table') then
  566. ConvertTable
  567. else
  568. if not ConvertSimpleBlock(AContext, Node) then
  569. Warning(AContext, SErrInvalidRemarkContent, [Node.NodeName]);
  570. Node := Node.NextSibling;
  571. end;
  572. DescrEndRemark;
  573. Result := True;
  574. end else if Node.NodeName = 'table' then
  575. begin
  576. ConvertTable;
  577. Result := True;
  578. end else
  579. Result := ConvertSimpleBlock(AContext, Node);
  580. end;
  581. function TFPDocWriter.ConvertSimpleBlock(AContext: TPasElement;
  582. Node: TDOMNode): Boolean;
  583. procedure ConvertListItems;
  584. var
  585. Empty: Boolean;
  586. begin
  587. Node := Node.FirstChild;
  588. Empty := True;
  589. while Assigned(Node) do
  590. begin
  591. if (Node.NodeType = TEXT_NODE) or (Node.NodeType = ENTITY_REFERENCE_NODE)
  592. then
  593. Warning(AContext, SErrInvalidListContent)
  594. else if Node.NodeType = ELEMENT_NODE then
  595. if Node.NodeName = 'li' then
  596. begin
  597. DescrBeginListItem;
  598. ConvertExtShortOrNonSectionBlocks(AContext, Node.FirstChild);
  599. DescrEndListItem;
  600. Empty := False;
  601. end else
  602. Warning(AContext, SErrInvalidElementInList);
  603. Node := Node.NextSibling;
  604. end;
  605. if Empty then
  606. Warning(AContext, SErrListIsEmpty);
  607. end;
  608. procedure ConvertDefinitionList;
  609. var
  610. Empty, ExpectDTNext: Boolean;
  611. begin
  612. Node := Node.FirstChild;
  613. Empty := True;
  614. ExpectDTNext := True;
  615. while Assigned(Node) do
  616. begin
  617. if (Node.NodeType = TEXT_NODE) or (Node.NodeType = ENTITY_REFERENCE_NODE)
  618. then
  619. Warning(AContext, SErrInvalidListContent)
  620. else if Node.NodeType = ELEMENT_NODE then
  621. if ExpectDTNext and (Node.NodeName = 'dt') then
  622. begin
  623. DescrBeginDefinitionTerm;
  624. if not ConvertShort(AContext, TDOMElement(Node)) then
  625. Warning(AContext, SErrInvalidDefinitionTermContent);
  626. DescrEndDefinitionTerm;
  627. Empty := False;
  628. ExpectDTNext := False;
  629. end else if not ExpectDTNext and (Node.NodeName = 'dd') then
  630. begin
  631. DescrBeginDefinitionEntry;
  632. ConvertExtShortOrNonSectionBlocks(AContext, Node.FirstChild);
  633. DescrEndDefinitionEntry;
  634. ExpectDTNext := True;
  635. end else
  636. Warning(AContext, SErrInvalidElementInList);
  637. Node := Node.NextSibling;
  638. end;
  639. if Empty then
  640. Warning(AContext, SErrListIsEmpty)
  641. else if not ExpectDTNext then
  642. Warning(AContext, SErrDefinitionEntryMissing);
  643. end;
  644. procedure ProcessCodeBody(Node: TDOMNode);
  645. var
  646. s: String;
  647. i, j: Integer;
  648. begin
  649. Node := Node.FirstChild;
  650. SetLength(s, 0);
  651. while Assigned(Node) do
  652. begin
  653. if Node.NodeType = TEXT_NODE then
  654. begin
  655. s := s + Node.NodeValue;
  656. j := 1;
  657. for i := 1 to Length(s) do
  658. // In XML, linefeeds are normalized to #10 by the parser!
  659. if s[i] = #10 then
  660. begin
  661. DescrWriteCodeLine(Copy(s, j, i - j));
  662. j := i + 1;
  663. end;
  664. if j > 1 then
  665. s := Copy(s, j, Length(s));
  666. end;
  667. Node := Node.NextSibling;
  668. end;
  669. if Length(s) > 0 then
  670. DescrWriteCodeLine(s);
  671. end;
  672. var
  673. s: String;
  674. HasBorder: Boolean;
  675. begin
  676. if Node.NodeType <> ELEMENT_NODE then
  677. begin
  678. Result := False;
  679. exit;
  680. end;
  681. if Node.NodeName = 'p' then
  682. begin
  683. DescrBeginParagraph;
  684. if not ConvertExtShort(AContext, Node.FirstChild) then
  685. Warning(AContext, SErrInvalidParaContent);
  686. DescrEndParagraph;
  687. Result := True;
  688. end else if Node.NodeName = 'code' then
  689. begin
  690. s := TDOMElement(Node)['border'];
  691. if s = '1' then
  692. HasBorder := True
  693. else
  694. begin
  695. if (Length(s) > 0) and (s <> '0') then
  696. Warning(AContext, SErrInvalidBorderValue, ['<code>']);
  697. end;
  698. DescrBeginCode(HasBorder, TDOMElement(Node)['highlighter']);
  699. ProcessCodeBody(Node);
  700. DescrEndCode;
  701. Result := True;
  702. end else if Node.NodeName = 'pre' then
  703. begin
  704. DescrBeginCode(False, 'none');
  705. ProcessCodeBody(Node);
  706. DescrEndCode;
  707. Result := True;
  708. end else if Node.NodeName = 'ul' then
  709. begin
  710. DescrBeginUnorderedList;
  711. ConvertListItems;
  712. DescrEndUnorderedList;
  713. Result := True;
  714. end else if Node.NodeName = 'ol' then
  715. begin
  716. DescrBeginOrderedList;
  717. ConvertListItems;
  718. DescrEndOrderedList;
  719. Result := True;
  720. end else if Node.NodeName = 'dl' then
  721. begin
  722. DescrBeginDefinitionList;
  723. ConvertDefinitionList;
  724. DescrEndDefinitionList;
  725. Result := True;
  726. end else
  727. Result := False;
  728. end;
  729. Constructor TTopicElement.Create(const AName: String; AParent: TPasElement);
  730. begin
  731. Inherited Create(AName,AParent);
  732. SubTopics:=TList.Create;
  733. end;
  734. Destructor TTopicElement.Destroy;
  735. begin
  736. // Actual subtopics are freed by TFPDocWriter Topics list.
  737. SubTopics.Free;
  738. Inherited;
  739. end;
  740. end.
  741. {
  742. $Log$
  743. Revision 1.3 2004-08-28 18:05:17 michael
  744. + Check for non-nil context
  745. Revision 1.2 2004/06/06 10:53:02 michael
  746. + Added Topic support
  747. Revision 1.1 2003/03/17 23:03:20 michael
  748. + Initial import in CVS
  749. Revision 1.9 2003/03/13 22:02:13 sg
  750. * New version with many bugfixes and our own parser (now independent of the
  751. compiler source)
  752. Revision 1.8 2002/05/24 00:13:22 sg
  753. * much improved new version, including many linking and output fixes
  754. Revision 1.7 2002/03/12 10:58:36 sg
  755. * reworked linking engine and internal structure
  756. Revision 1.6 2001/07/27 10:21:42 sg
  757. * Just a new, improved version ;)
  758. (detailed changelogs will be provided again with the next commits)
  759. Revision 1.5 2000/11/13 00:18:42 sg
  760. * Comments within descriptions should be ignored now in all cases
  761. Revision 1.4 2000/11/11 23:53:56 sg
  762. * Added <pre> tag (with unified handling of <pre> and <code>)
  763. Revision 1.3 2000/10/30 21:19:59 sg
  764. * Changed syntax highlighting attribute in 'code' element from
  765. 'highlight' to 'highlighter'
  766. }