dwriter.pp 27 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009
  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, SysUtils;
  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. SErralreadyRegistered = 'Class for output format "%s" already registered';
  41. SErrUnknownWriterClass = 'Unknown output format "%s"';
  42. type
  43. // Phony element for pas pages.
  44. TTopicElement = Class(TPaselement)
  45. Constructor Create(const AName: String; AParent: TPasElement); override;
  46. Destructor Destroy; override;
  47. TopicNode : TDocNode;
  48. Previous,
  49. Next : TPasElement;
  50. Subtopics : TList;
  51. end;
  52. { TFPDocWriter }
  53. TFPDocWriter = class
  54. private
  55. FEngine : TFPDocEngine;
  56. FPackage : TPasPackage;
  57. FTopics : TList;
  58. protected
  59. procedure Warning(AContext: TPasElement; const AMsg: String);
  60. procedure Warning(AContext: TPasElement; const AMsg: String;
  61. const Args: array of const);
  62. // function FindShortDescr(const Name: String): TDOMElement;
  63. // Description conversion
  64. function IsDescrNodeEmpty(Node: TDOMNode): Boolean;
  65. function IsExtShort(Node: TDOMNode): Boolean;
  66. function ConvertShort(AContext: TPasElement; El: TDOMElement): Boolean;
  67. function ConvertBaseShort(AContext: TPasElement; Node: TDOMNode): Boolean;
  68. procedure ConvertBaseShortList(AContext: TPasElement; Node: TDOMNode;
  69. MayBeEmpty: Boolean);
  70. procedure ConvertLink(AContext: TPasElement; El: TDOMElement);
  71. function ConvertExtShort(AContext: TPasElement; Node: TDOMNode): Boolean;
  72. procedure ConvertDescr(AContext: TPasElement; El: TDOMElement;
  73. AutoInsertBlock: Boolean);
  74. function ConvertNonSectionBlock(AContext: TPasElement;
  75. Node: TDOMNode): Boolean;
  76. procedure ConvertExtShortOrNonSectionBlocks(AContext: TPasElement;
  77. Node: TDOMNode);
  78. function ConvertSimpleBlock(AContext: TPasElement; Node: TDOMNode): Boolean;
  79. Function FindTopicElement(Node : TDocNode): TTopicElement;
  80. procedure DescrWriteText(const AText: DOMString); virtual; abstract;
  81. procedure DescrBeginBold; virtual; abstract;
  82. procedure DescrEndBold; virtual; abstract;
  83. procedure DescrBeginItalic; virtual; abstract;
  84. procedure DescrEndItalic; virtual; abstract;
  85. procedure DescrBeginEmph; virtual; abstract;
  86. procedure DescrEndEmph; virtual; abstract;
  87. procedure DescrWriteFileEl(const AText: DOMString); virtual; abstract;
  88. procedure DescrWriteKeywordEl(const AText: DOMString); virtual; abstract;
  89. procedure DescrWriteVarEl(const AText: DOMString); virtual; abstract;
  90. procedure DescrBeginLink(const AId: DOMString); virtual; abstract;
  91. procedure DescrEndLink; virtual; abstract;
  92. procedure DescrWriteLinebreak; virtual; abstract;
  93. procedure DescrBeginParagraph; virtual; abstract;
  94. procedure DescrEndParagraph; virtual; abstract;
  95. procedure DescrBeginCode(HasBorder: Boolean; const AHighlighterName: String); virtual; abstract;
  96. procedure DescrWriteCodeLine(const ALine: String); virtual; abstract;
  97. procedure DescrEndCode; virtual; abstract;
  98. procedure DescrBeginOrderedList; virtual; abstract;
  99. procedure DescrEndOrderedList; virtual; abstract;
  100. procedure DescrBeginUnorderedList; virtual; abstract;
  101. procedure DescrEndUnorderedList; virtual; abstract;
  102. procedure DescrBeginDefinitionList; virtual; abstract;
  103. procedure DescrEndDefinitionList; virtual; abstract;
  104. procedure DescrBeginListItem; virtual; abstract;
  105. procedure DescrEndListItem; virtual; abstract;
  106. procedure DescrBeginDefinitionTerm; virtual; abstract;
  107. procedure DescrEndDefinitionTerm; virtual; abstract;
  108. procedure DescrBeginDefinitionEntry; virtual; abstract;
  109. procedure DescrEndDefinitionEntry; virtual; abstract;
  110. procedure DescrBeginSectionTitle; virtual; abstract;
  111. procedure DescrBeginSectionBody; virtual; abstract;
  112. procedure DescrEndSection; virtual; abstract;
  113. procedure DescrBeginRemark; virtual; abstract;
  114. procedure DescrEndRemark; virtual; abstract;
  115. procedure DescrBeginTable(ColCount: Integer; HasBorder: Boolean); virtual; abstract;
  116. procedure DescrEndTable; virtual; abstract;
  117. procedure DescrBeginTableCaption; virtual; abstract;
  118. procedure DescrEndTableCaption; virtual; abstract;
  119. procedure DescrBeginTableHeadRow; virtual; abstract;
  120. procedure DescrEndTableHeadRow; virtual; abstract;
  121. procedure DescrBeginTableRow; virtual; abstract;
  122. procedure DescrEndTableRow; virtual; abstract;
  123. procedure DescrBeginTableCell; virtual; abstract;
  124. procedure DescrEndTableCell; virtual; abstract;
  125. public
  126. Constructor Create(APackage: TPasPackage; AEngine: TFPDocEngine); virtual;
  127. destructor Destroy; override;
  128. property Engine : TFPDocEngine read FEngine;
  129. Property Package : TPasPackage read FPackage;
  130. Property Topics : TList Read FTopics;
  131. // Should return True if option was succesfully interpreted.
  132. Function InterpretOption(Const Cmd,Arg : String) : Boolean; Virtual;
  133. Class Procedure Usage(List : TStrings); virtual;
  134. procedure WriteDoc; virtual; Abstract;
  135. end;
  136. TFPDocWriterClass = Class of TFPDocWriter;
  137. EFPDocWriterError = Class(Exception);
  138. // Register backend
  139. Procedure RegisterWriter(AClass : TFPDocWriterClass; Const AName,ADescr : String);
  140. // UnRegister backend
  141. Procedure UnRegisterWriter(Const AName : String);
  142. // Return back end class. Exception if not found.
  143. Function GetWriterClass(AName : String) : TFPDocWriterClass;
  144. // Return index of back end class.
  145. Function FindWriterClass(AName : String) : Integer;
  146. // List of backend in name=descr form.
  147. Procedure EnumWriters(List : TStrings);
  148. implementation
  149. { ---------------------------------------------------------------------
  150. Writer registration
  151. ---------------------------------------------------------------------}
  152. Type
  153. { TWriterRecord }
  154. TWriterRecord = Class(TObject)
  155. Private
  156. FClass : TFPDocWriterClass;
  157. FName : String;
  158. FDescription : String;
  159. Public
  160. Constructor Create (AClass : TFPDocWriterClass; Const AName,ADescr : String);
  161. end;
  162. { TWriterRecord }
  163. constructor TWriterRecord.Create(AClass: TFPDocWriterClass; const AName,
  164. ADescr: String);
  165. begin
  166. FClass:=AClass;
  167. FName:=AName;
  168. FDescription:=ADescr;
  169. end;
  170. Var
  171. Writers : TStringList;
  172. Procedure InitWriterList;
  173. begin
  174. Writers:=TStringList.Create;
  175. Writers.Sorted:=True;
  176. end;
  177. Procedure DoneWriterList;
  178. Var
  179. I : Integer;
  180. begin
  181. For I:=Writers.Count-1 downto 0 do
  182. Writers.Objects[i].Free;
  183. FreeAndNil(Writers);
  184. end;
  185. procedure RegisterWriter(AClass : TFPDocWriterClass; Const AName, ADescr : String);
  186. begin
  187. If Writers.IndexOf(AName)<>-1 then
  188. Raise EFPDocWriterError.CreateFmt(SErralreadyRegistered,[ANAme]);
  189. Writers.AddObject(AName,TWriterRecord.Create(AClass,AName,ADescr));
  190. end;
  191. function FindWriterClass(AName : String) : Integer;
  192. begin
  193. Result:=Writers.IndexOf(AName);
  194. end;
  195. function GetWriterClass(AName : String) : TFPDocWriterClass;
  196. Var
  197. Index : Integer;
  198. begin
  199. Index:=FindWriterClass(AName);
  200. If Index=-1 then
  201. Raise EFPDocWriterError.CreateFmt(SErrUnknownWriterClass,[ANAme]);
  202. Result:=(Writers.Objects[Index] as TWriterRecord).FClass;
  203. end;
  204. // UnRegister backend
  205. Procedure UnRegisterWriter(Const AName : String);
  206. Var
  207. Index : Integer;
  208. begin
  209. Index:=Writers.IndexOf(AName);
  210. If Index=-1 then
  211. Raise EFPDocWriterError.CreateFmt(SErrUnknownWriterClass,[ANAme]);
  212. Writers.Objects[Index].Free;
  213. Writers.Delete(Index);
  214. end;
  215. Procedure EnumWriters(List : TStrings);
  216. Var
  217. I : Integer;
  218. begin
  219. List.Clear;
  220. For I:=0 to Writers.Count-1 do
  221. With (Writers.Objects[I] as TWriterRecord) do
  222. List.Add(FName+'='+FDescription);
  223. end;
  224. { ---------------------------------------------------------------------
  225. TFPDocWriter
  226. ---------------------------------------------------------------------}
  227. {
  228. fmtIPF:
  229. begin
  230. if Length(Engine.Output) = 0 then
  231. WriteLn(SCmdLineOutputOptionMissing)
  232. else
  233. CreateIPFDocForPackage(Engine.Package, Engine);
  234. end;
  235. }
  236. Constructor TFPDocWriter.Create(APackage: TPasPackage; AEngine: TFPDocEngine);
  237. begin
  238. inherited Create;
  239. FEngine := AEngine;
  240. FPackage := APackage;
  241. FTopics:=Tlist.Create;
  242. end;
  243. destructor TFPDocWriter.Destroy;
  244. Var
  245. i : integer;
  246. begin
  247. For I:=0 to FTopics.Count-1 do
  248. TTopicElement(FTopics[i]).Free;
  249. FTopics.Free;
  250. Inherited;
  251. end;
  252. function TFPDocWriter.InterpretOption(Const Cmd,Arg : String): Boolean;
  253. begin
  254. Result:=False;
  255. end;
  256. Class procedure TFPDocWriter.Usage(List: TStrings);
  257. begin
  258. // Do nothing.
  259. end;
  260. Function TFPDocWriter.FindTopicElement(Node : TDocNode): TTopicElement;
  261. Var
  262. I : Integer;
  263. begin
  264. Result:=Nil;
  265. I:=FTopics.Count-1;
  266. While (I>=0) and (Result=Nil) do
  267. begin
  268. If (TTopicElement(FTopics[i]).TopicNode=Node) Then
  269. Result:=TTopicElement(FTopics[i]);
  270. Dec(I);
  271. end;
  272. end;
  273. { ---------------------------------------------------------------------
  274. Generic documentation node conversion
  275. ---------------------------------------------------------------------}
  276. function IsContentNodeType(Node: TDOMNode): Boolean;
  277. begin
  278. Result := (Node.NodeType = ELEMENT_NODE) or (Node.NodeType = TEXT_NODE) or
  279. (Node.NodeType = ENTITY_REFERENCE_NODE);
  280. end;
  281. procedure TFPDocWriter.Warning(AContext: TPasElement; const AMsg: String);
  282. begin
  283. if (AContext<>nil) then
  284. WriteLn('[', AContext.PathName, '] ', AMsg)
  285. else
  286. WriteLn('[<no context>] ', AMsg);
  287. end;
  288. procedure TFPDocWriter.Warning(AContext: TPasElement; const AMsg: String;
  289. const Args: array of const);
  290. begin
  291. Warning(AContext, Format(AMsg, Args));
  292. end;
  293. function TFPDocWriter.IsDescrNodeEmpty(Node: TDOMNode): Boolean;
  294. var
  295. Child: TDOMNode;
  296. begin
  297. if (not Assigned(Node)) or (not Assigned(Node.FirstChild)) then
  298. Result := True
  299. else
  300. begin
  301. Child := Node.FirstChild;
  302. while Assigned(Child) do
  303. begin
  304. if (Child.NodeType = ELEMENT_NODE) or (Child.NodeType = TEXT_NODE) or
  305. (Child.NodeType = ENTITY_REFERENCE_NODE) then
  306. begin
  307. Result := False;
  308. exit;
  309. end;
  310. Child := Child.NextSibling;
  311. end;
  312. end;
  313. Result := True;
  314. end;
  315. { Check wether the nodes starting with the node given as argument make up an
  316. 'extshort' production. }
  317. function TFPDocWriter.IsExtShort(Node: TDOMNode): Boolean;
  318. begin
  319. while Assigned(Node) do
  320. begin
  321. if Node.NodeType = ELEMENT_NODE then
  322. if (Node.NodeName <> 'br') and
  323. (Node.NodeName <> 'link') and
  324. (Node.NodeName <> 'b') and
  325. (Node.NodeName <> 'file') and
  326. (Node.NodeName <> 'i') and
  327. (Node.NodeName <> 'kw') and
  328. (Node.NodeName <> 'printshort') and
  329. (Node.NodeName <> 'var') then
  330. begin
  331. Result := False;
  332. exit;
  333. end;
  334. Node := Node.NextSibling;
  335. end;
  336. Result := True;
  337. end;
  338. function TFPDocWriter.ConvertShort(AContext: TPasElement;
  339. El: TDOMElement): Boolean;
  340. var
  341. Node: TDOMNode;
  342. begin
  343. Result := False;
  344. if not Assigned(El) then
  345. exit;
  346. Node := El.FirstChild;
  347. while Assigned(Node) do
  348. begin
  349. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'link') then
  350. ConvertLink(AContext, TDOMElement(Node))
  351. else
  352. if not ConvertBaseShort(AContext, Node) then
  353. exit;
  354. Node := Node.NextSibling;
  355. end;
  356. Result := True;
  357. end;
  358. function TFPDocWriter.ConvertBaseShort(AContext: TPasElement;
  359. Node: TDOMNode): Boolean;
  360. function ConvertText: DOMString;
  361. var
  362. s: String;
  363. i: Integer;
  364. begin
  365. if Node.NodeType = TEXT_NODE then
  366. begin
  367. s := Node.NodeValue;
  368. i := 1;
  369. SetLength(Result, 0);
  370. while i <= Length(s) do
  371. if s[i] = #13 then
  372. begin
  373. Result := Result + ' ';
  374. Inc(i);
  375. if s[i] = #10 then
  376. Inc(i);
  377. end else if s[i] = #10 then
  378. begin
  379. Result := Result + ' ';
  380. Inc(i);
  381. end else
  382. begin
  383. Result := Result + s[i];
  384. Inc(i);
  385. end;
  386. end else if Node.NodeType = ENTITY_REFERENCE_NODE then
  387. if Node.NodeName = 'fpc' then
  388. Result := 'Free Pascal'
  389. else if Node.NodeName = 'delphi' then
  390. Result := 'Delphi'
  391. else
  392. begin
  393. Warning(AContext, Format(SErrUnknownEntityReference, [Node.NodeName]));
  394. Result := Node.NodeName;
  395. end
  396. else if Node.NodeType = ELEMENT_NODE then
  397. SetLength(Result, 0);
  398. end;
  399. function ConvertTextContent: DOMString;
  400. begin
  401. SetLength(Result, 0);
  402. Node := Node.FirstChild;
  403. while Assigned(Node) do
  404. begin
  405. Result := Result + ConvertText;
  406. Node := Node.NextSibling;
  407. end;
  408. end;
  409. var
  410. El, DescrEl: TDOMElement;
  411. FPEl: TPasElement;
  412. begin
  413. Result := True;
  414. if Node.NodeType = ELEMENT_NODE then
  415. if Node.NodeName = 'b' then
  416. begin
  417. DescrBeginBold;
  418. ConvertBaseShortList(AContext, Node, False);
  419. DescrEndBold;
  420. end else
  421. if Node.NodeName = 'i' then
  422. begin
  423. DescrBeginItalic;
  424. ConvertBaseShortList(AContext, Node, False);
  425. DescrEndItalic;
  426. end else
  427. if Node.NodeName = 'em' then
  428. begin
  429. DescrBeginEmph;
  430. ConvertBaseShortList(AContext, Node, False);
  431. DescrEndEmph;
  432. end else
  433. if Node.NodeName = 'file' then
  434. DescrWriteFileEl(ConvertTextContent)
  435. else if Node.NodeName = 'kw' then
  436. DescrWriteKeywordEl(ConvertTextContent)
  437. else if Node.NodeName = 'printshort' then
  438. begin
  439. El := TDOMElement(Node);
  440. DescrEl := Engine.FindShortDescr(AContext.GetModule, El['id']);
  441. if Assigned(DescrEl) then
  442. ConvertShort(AContext, DescrEl)
  443. else
  444. begin
  445. Warning(AContext, Format(SErrUnknownPrintShortID, [El['id']]));
  446. DescrBeginBold;
  447. DescrWriteText('#ShortDescr:' + El['id']);
  448. DescrEndBold;
  449. end;
  450. end else if Node.NodeName = 'var' then
  451. DescrWriteVarEl(ConvertTextContent)
  452. else
  453. Result := False
  454. else
  455. DescrWriteText(ConvertText);
  456. end;
  457. procedure TFPDocWriter.ConvertBaseShortList(AContext: TPasElement;
  458. Node: TDOMNode; MayBeEmpty: Boolean);
  459. var
  460. Child: TDOMNode;
  461. begin
  462. Child := Node.FirstChild;
  463. while Assigned(Child) do
  464. begin
  465. if not ConvertBaseShort(AContext, Child) then
  466. Warning(AContext, SErrInvalidShortDescr)
  467. else
  468. MayBeEmpty := True;
  469. Child := Child.NextSibling;
  470. end;
  471. if not MayBeEmpty then
  472. Warning(AContext, SErrInvalidShortDescr)
  473. end;
  474. procedure TFPDocWriter.ConvertLink(AContext: TPasElement; El: TDOMElement);
  475. begin
  476. DescrBeginLink(El['id']);
  477. if not IsDescrNodeEmpty(El) then
  478. ConvertBaseShortList(AContext, El, True)
  479. else
  480. DescrWriteText(El['id']);
  481. DescrEndLink;
  482. end;
  483. function TFPDocWriter.ConvertExtShort(AContext: TPasElement;
  484. Node: TDOMNode): Boolean;
  485. begin
  486. Result := False;
  487. while Assigned(Node) do
  488. begin
  489. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'link') then
  490. ConvertLink(AContext, TDOMElement(Node))
  491. else if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'br') then
  492. DescrWriteLinebreak
  493. else
  494. if not ConvertBaseShort(AContext, Node) then
  495. exit;
  496. Node := Node.NextSibling;
  497. end;
  498. Result := True;
  499. end;
  500. procedure TFPDocWriter.ConvertDescr(AContext: TPasElement; El: TDOMElement;
  501. AutoInsertBlock: Boolean);
  502. var
  503. Node, Child: TDOMNode;
  504. ParaCreated: Boolean;
  505. begin
  506. if AutoInsertBlock then
  507. if IsExtShort(El.FirstChild) then
  508. DescrBeginParagraph
  509. else
  510. AutoInsertBlock := False;
  511. Node := El.FirstChild;
  512. if not ConvertExtShort(AContext, Node) then
  513. begin
  514. while Assigned(Node) do
  515. begin
  516. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'section') then
  517. begin
  518. DescrBeginSectionTitle;
  519. Child := Node.FirstChild;
  520. while Assigned(Child) and (Child.NodeType <> ELEMENT_NODE) do
  521. begin
  522. if not IsDescrNodeEmpty(Child) then
  523. Warning(AContext, SErrInvalidContentBeforeSectionTitle);
  524. Child := Child.NextSibling;
  525. end;
  526. if not Assigned(Child) or (Child.NodeName <> 'title') then
  527. Warning(AContext, SErrSectionTitleExpected)
  528. else
  529. ConvertShort(AContext, TDOMElement(Child));
  530. DescrBeginSectionBody;
  531. if IsExtShort(Child) then
  532. begin
  533. DescrBeginParagraph;
  534. ParaCreated := True;
  535. end else
  536. ParaCreated := False;
  537. ConvertExtShortOrNonSectionBlocks(AContext, Child.NextSibling);
  538. if ParaCreated then
  539. DescrEndParagraph;
  540. DescrEndSection;
  541. end else if not ConvertNonSectionBlock(AContext, Node) then
  542. Warning(AContext, SErrInvalidDescr, [Node.NodeName]);
  543. Node := Node.NextSibling;
  544. end;
  545. end else
  546. if AutoInsertBlock then
  547. DescrEndParagraph;
  548. end;
  549. procedure TFPDocWriter.ConvertExtShortOrNonSectionBlocks(AContext: TPasElement;
  550. Node: TDOMNode);
  551. begin
  552. if not ConvertExtShort(AContext, Node) then
  553. while Assigned(Node) do
  554. begin
  555. if not ConvertNonSectionBlock(AContext, Node) then
  556. Warning(AContext, SErrInvalidDescr, [Node.NodeName]);
  557. Node := Node.NextSibling;
  558. end;
  559. end;
  560. function TFPDocWriter.ConvertNonSectionBlock(AContext: TPasElement;
  561. Node: TDOMNode): Boolean;
  562. procedure ConvertCells(Node: TDOMNode);
  563. var
  564. Child: TDOMNode;
  565. IsEmpty: Boolean;
  566. begin
  567. Node := Node.FirstChild;
  568. IsEmpty := True;
  569. while Assigned(Node) do
  570. begin
  571. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'td') then
  572. begin
  573. DescrBeginTableCell;
  574. Child := Node.FirstChild;
  575. if not ConvertExtShort(AContext, Child) then
  576. while Assigned(Child) do
  577. begin
  578. if not ConvertSimpleBlock(AContext, Child) then
  579. Warning(AContext, SErrInvalidTableContent);
  580. Child := Child.NextSibling;
  581. end;
  582. DescrEndTableCell;
  583. IsEmpty := False;
  584. end else
  585. if IsContentNodeType(Node) then
  586. Warning(AContext, SErrInvalidTableContent);
  587. Node := Node.NextSibling;
  588. end;
  589. if IsEmpty then
  590. Warning(AContext, SErrTableRowEmpty);
  591. end;
  592. procedure ConvertTable;
  593. function GetColCount(Node: TDOMNode): Integer;
  594. begin
  595. Result := 0;
  596. Node := Node.FirstChild;
  597. while Assigned(Node) do
  598. begin
  599. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'td') then
  600. Inc(Result);
  601. Node := Node.NextSibling;
  602. end;
  603. end;
  604. var
  605. s: String;
  606. HasBorder, CaptionPossible, HeadRowPossible: Boolean;
  607. ColCount, ThisRowColCount: Integer;
  608. Subnode: TDOMNode;
  609. begin
  610. s := TDOMElement(Node)['border'];
  611. if s = '1' then
  612. HasBorder := True
  613. else
  614. begin
  615. HasBorder := False;
  616. if (Length(s) <> 0) and (s <> '0') then
  617. Warning(AContext, SErrInvalidBorderValue, ['<table>']);
  618. end;
  619. // Determine the number of columns
  620. ColCount := 0;
  621. Subnode := Node.FirstChild;
  622. while Assigned(Subnode) do
  623. begin
  624. if Subnode.NodeType = ELEMENT_NODE then
  625. if (Subnode.NodeName = 'caption') or (Subnode.NodeName = 'th') or
  626. (Subnode.NodeName = 'tr') then
  627. begin
  628. ThisRowColCount := GetColCount(Subnode);
  629. if ThisRowColCount > ColCount then
  630. ColCount := ThisRowColCount;
  631. end;
  632. Subnode := Subnode.NextSibling;
  633. end;
  634. DescrBeginTable(ColCount, HasBorder);
  635. Node := Node.FirstChild;
  636. CaptionPossible := True;
  637. HeadRowPossible := True;
  638. while Assigned(Node) do
  639. begin
  640. if Node.NodeType = ELEMENT_NODE then
  641. if CaptionPossible and (Node.NodeName = 'caption') then
  642. begin
  643. DescrBeginTableCaption;
  644. if not ConvertExtShort(AContext, Node.FirstChild) then
  645. Warning(AContext, SErrInvalidTableContent);
  646. DescrEndTableCaption;
  647. CaptionPossible := False;
  648. end else if HeadRowPossible and (Node.NodeName = 'th') then
  649. begin
  650. DescrBeginTableHeadRow;
  651. ConvertCells(Node);
  652. DescrEndTableHeadRow;
  653. CaptionPossible := False;
  654. HeadRowPossible := False;
  655. end else if Node.NodeName = 'tr' then
  656. begin
  657. DescrBeginTableRow;
  658. ConvertCells(Node);
  659. DescrEndTableRow;
  660. end else
  661. Warning(AContext, SErrInvalidTableContent)
  662. else if IsContentNodeType(Node) then
  663. Warning(AContext, SErrInvalidTableContent);
  664. Node := Node.NextSibling;
  665. end;
  666. DescrEndTable;
  667. end;
  668. begin
  669. if Node.NodeType <> ELEMENT_NODE then
  670. begin
  671. Result := Node.NodeType = COMMENT_NODE;
  672. exit;
  673. end;
  674. if Node.NodeName = 'remark' then
  675. begin
  676. DescrBeginRemark;
  677. Node := Node.FirstChild;
  678. if not ConvertExtShort(AContext, Node) then
  679. while Assigned(Node) do
  680. begin
  681. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'table') then
  682. ConvertTable
  683. else
  684. if not ConvertSimpleBlock(AContext, Node) then
  685. Warning(AContext, SErrInvalidRemarkContent, [Node.NodeName]);
  686. Node := Node.NextSibling;
  687. end;
  688. DescrEndRemark;
  689. Result := True;
  690. end else if Node.NodeName = 'table' then
  691. begin
  692. ConvertTable;
  693. Result := True;
  694. end else
  695. Result := ConvertSimpleBlock(AContext, Node);
  696. end;
  697. function TFPDocWriter.ConvertSimpleBlock(AContext: TPasElement;
  698. Node: TDOMNode): Boolean;
  699. procedure ConvertListItems;
  700. var
  701. Empty: Boolean;
  702. begin
  703. Node := Node.FirstChild;
  704. Empty := True;
  705. while Assigned(Node) do
  706. begin
  707. if (Node.NodeType = TEXT_NODE) or (Node.NodeType = ENTITY_REFERENCE_NODE)
  708. then
  709. Warning(AContext, SErrInvalidListContent)
  710. else if Node.NodeType = ELEMENT_NODE then
  711. if Node.NodeName = 'li' then
  712. begin
  713. DescrBeginListItem;
  714. ConvertExtShortOrNonSectionBlocks(AContext, Node.FirstChild);
  715. DescrEndListItem;
  716. Empty := False;
  717. end else
  718. Warning(AContext, SErrInvalidElementInList);
  719. Node := Node.NextSibling;
  720. end;
  721. if Empty then
  722. Warning(AContext, SErrListIsEmpty);
  723. end;
  724. procedure ConvertDefinitionList;
  725. var
  726. Empty, ExpectDTNext: Boolean;
  727. begin
  728. Node := Node.FirstChild;
  729. Empty := True;
  730. ExpectDTNext := True;
  731. while Assigned(Node) do
  732. begin
  733. if (Node.NodeType = TEXT_NODE) or (Node.NodeType = ENTITY_REFERENCE_NODE)
  734. then
  735. Warning(AContext, SErrInvalidListContent)
  736. else if Node.NodeType = ELEMENT_NODE then
  737. if ExpectDTNext and (Node.NodeName = 'dt') then
  738. begin
  739. DescrBeginDefinitionTerm;
  740. if not ConvertShort(AContext, TDOMElement(Node)) then
  741. Warning(AContext, SErrInvalidDefinitionTermContent);
  742. DescrEndDefinitionTerm;
  743. Empty := False;
  744. ExpectDTNext := False;
  745. end else if not ExpectDTNext and (Node.NodeName = 'dd') then
  746. begin
  747. DescrBeginDefinitionEntry;
  748. ConvertExtShortOrNonSectionBlocks(AContext, Node.FirstChild);
  749. DescrEndDefinitionEntry;
  750. ExpectDTNext := True;
  751. end else
  752. Warning(AContext, SErrInvalidElementInList);
  753. Node := Node.NextSibling;
  754. end;
  755. if Empty then
  756. Warning(AContext, SErrListIsEmpty)
  757. else if not ExpectDTNext then
  758. Warning(AContext, SErrDefinitionEntryMissing);
  759. end;
  760. procedure ProcessCodeBody(Node: TDOMNode);
  761. var
  762. s: String;
  763. i, j: Integer;
  764. begin
  765. Node := Node.FirstChild;
  766. SetLength(s, 0);
  767. while Assigned(Node) do
  768. begin
  769. if Node.NodeType = TEXT_NODE then
  770. begin
  771. s := s + Node.NodeValue;
  772. j := 1;
  773. for i := 1 to Length(s) do
  774. // In XML, linefeeds are normalized to #10 by the parser!
  775. if s[i] = #10 then
  776. begin
  777. DescrWriteCodeLine(Copy(s, j, i - j));
  778. j := i + 1;
  779. end;
  780. if j > 1 then
  781. s := Copy(s, j, Length(s));
  782. end;
  783. Node := Node.NextSibling;
  784. end;
  785. if Length(s) > 0 then
  786. DescrWriteCodeLine(s);
  787. end;
  788. var
  789. s: String;
  790. HasBorder: Boolean;
  791. begin
  792. if Node.NodeType <> ELEMENT_NODE then
  793. begin
  794. Result := False;
  795. exit;
  796. end;
  797. if Node.NodeName = 'p' then
  798. begin
  799. DescrBeginParagraph;
  800. if not ConvertExtShort(AContext, Node.FirstChild) then
  801. Warning(AContext, SErrInvalidParaContent);
  802. DescrEndParagraph;
  803. Result := True;
  804. end else if Node.NodeName = 'code' then
  805. begin
  806. s := TDOMElement(Node)['border'];
  807. if s = '1' then
  808. HasBorder := True
  809. else
  810. begin
  811. if (Length(s) > 0) and (s <> '0') then
  812. Warning(AContext, SErrInvalidBorderValue, ['<code>']);
  813. end;
  814. DescrBeginCode(HasBorder, TDOMElement(Node)['highlighter']);
  815. ProcessCodeBody(Node);
  816. DescrEndCode;
  817. Result := True;
  818. end else if Node.NodeName = 'pre' then
  819. begin
  820. DescrBeginCode(False, 'none');
  821. ProcessCodeBody(Node);
  822. DescrEndCode;
  823. Result := True;
  824. end else if Node.NodeName = 'ul' then
  825. begin
  826. DescrBeginUnorderedList;
  827. ConvertListItems;
  828. DescrEndUnorderedList;
  829. Result := True;
  830. end else if Node.NodeName = 'ol' then
  831. begin
  832. DescrBeginOrderedList;
  833. ConvertListItems;
  834. DescrEndOrderedList;
  835. Result := True;
  836. end else if Node.NodeName = 'dl' then
  837. begin
  838. DescrBeginDefinitionList;
  839. ConvertDefinitionList;
  840. DescrEndDefinitionList;
  841. Result := True;
  842. end else
  843. Result := False;
  844. end;
  845. Constructor TTopicElement.Create(const AName: String; AParent: TPasElement);
  846. begin
  847. Inherited Create(AName,AParent);
  848. SubTopics:=TList.Create;
  849. end;
  850. Destructor TTopicElement.Destroy;
  851. begin
  852. // Actual subtopics are freed by TFPDocWriter Topics list.
  853. SubTopics.Free;
  854. Inherited;
  855. end;
  856. initialization
  857. InitWriterList;
  858. finalization
  859. DoneWriterList;
  860. end.
  861. {
  862. $Log$
  863. Revision 1.4 2005-01-12 21:11:41 michael
  864. + New structure for writers. Implemented TXT writer
  865. Revision 1.3 2004/08/28 18:05:17 michael
  866. + Check for non-nil context
  867. Revision 1.2 2004/06/06 10:53:02 michael
  868. + Added Topic support
  869. Revision 1.1 2003/03/17 23:03:20 michael
  870. + Initial import in CVS
  871. Revision 1.9 2003/03/13 22:02:13 sg
  872. * New version with many bugfixes and our own parser (now independent of the
  873. compiler source)
  874. Revision 1.8 2002/05/24 00:13:22 sg
  875. * much improved new version, including many linking and output fixes
  876. Revision 1.7 2002/03/12 10:58:36 sg
  877. * reworked linking engine and internal structure
  878. Revision 1.6 2001/07/27 10:21:42 sg
  879. * Just a new, improved version ;)
  880. (detailed changelogs will be provided again with the next commits)
  881. Revision 1.5 2000/11/13 00:18:42 sg
  882. * Comments within descriptions should be ignored now in all cases
  883. Revision 1.4 2000/11/11 23:53:56 sg
  884. * Added <pre> tag (with unified handling of <pre> and <code>)
  885. Revision 1.3 2000/10/30 21:19:59 sg
  886. * Changed syntax highlighting attribute in 'code' element from
  887. 'highlight' to 'highlighter'
  888. }