dwriter.pp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212
  1. {
  2. FPDoc - Free Pascal Documentation Tool
  3. Copyright (C) 2000 - 2003 by
  4. Areca Systems GmbH / Sebastian Guenther, [email protected]
  5. 2005-2012 by
  6. various FPC contributors
  7. * Output string definitions
  8. * Basic writer (output generator) class
  9. See the file COPYING, included in this distribution,
  10. for details about the copyright.
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  14. }
  15. unit dWriter;
  16. {$MODE objfpc}
  17. {$H+}
  18. interface
  19. uses Classes, DOM, dGlobals, PasTree, SysUtils;
  20. resourcestring
  21. SErrFileWriting = 'An error occured during writing of file "%s": %s';
  22. SErrInvalidShortDescr = 'Invalid short description';
  23. SErrInvalidDescr = 'Invalid description (illegal XML element: "%s")';
  24. SErrInvalidParaContent = 'Invalid paragraph content';
  25. SErrInvalidElementInList = 'Invalid element in list - only "li" allowed';
  26. SErrInvalidListContent = 'Invalid list content';
  27. SErrInvalidRemarkContent = 'Invalid <remark> content (illegal XML element: "%s")';
  28. SErrListIsEmpty = 'List is empty - need at least one "li" element';
  29. SErrInvalidDefinitionTermContent = 'Invalid content in definition term';
  30. SErrDefinitionEntryMissing = 'Definition entry after definition term is missing';
  31. SErrInvalidBorderValue = 'Invalid "border" value for %s';
  32. SErrInvalidTableContent = 'Invalid table content';
  33. SErrTableRowEmpty = 'Table row is empty (no "td" elements found)';
  34. SErrInvalidContentBeforeSectionTitle = 'Invalid content before section title';
  35. SErrSectionTitleExpected = 'Section title ("title" element) expected';
  36. SErrDescrTagUnknown = 'Warning: Unknown tag "%s" in description';
  37. SErrUnknownEntityReference = 'Warning: Unknown entity reference "&%s;" found';
  38. SErrUnknownLinkID = 'Warning: Target ID of <link> in unit "%s" is unknown: "%s"';
  39. SErrUnknownPrintShortID = 'Warning: Target ID of <printshort> is unknown: "%s"';
  40. SErrUnknownLink = 'Could not resolve link to "%s"';
  41. SErralreadyRegistered = 'Class for output format "%s" already registered';
  42. SErrUnknownWriterClass = 'Unknown output format "%s"';
  43. type
  44. // Phony element for pas pages.
  45. TTopicElement = Class(TPaselement)
  46. TopicNode : TDocNode;
  47. Previous,
  48. Next : TPasElement;
  49. Subtopics : TList;
  50. Constructor Create(const AName: String; AParent: TPasElement); override;
  51. Destructor Destroy; override;
  52. end;
  53. TWriterLogEvent = Procedure(Sender : TObject; Const Msg : String) of object;
  54. TWriterNoteEvent = Procedure(Sender : TObject; Note : TDomElement; Var EmitNote : Boolean) of object;
  55. { TFPDocWriter }
  56. TFPDocWriter = class
  57. private
  58. FEmitNotes: Boolean;
  59. FEngine : TFPDocEngine;
  60. FPackage : TPasPackage;
  61. FTopics : TList;
  62. FImgExt : String;
  63. FBeforeEmitNote : TWriterNoteEvent;
  64. procedure ConvertURL(AContext: TPasElement; El: TDOMElement);
  65. protected
  66. Procedure DoLog(Const Msg : String);
  67. Procedure DoLog(Const Fmt : String; Args : Array of const);
  68. procedure Warning(AContext: TPasElement; const AMsg: String);
  69. procedure Warning(AContext: TPasElement; const AMsg: String;
  70. const Args: array of const);
  71. // function FindShortDescr(const Name: String): TDOMElement;
  72. // Description conversion
  73. function IsDescrNodeEmpty(Node: TDOMNode): Boolean;
  74. function IsExtShort(Node: TDOMNode): Boolean;
  75. function ConvertShort(AContext: TPasElement; El: TDOMElement): Boolean;
  76. function ConvertNotes(AContext: TPasElement; El: TDOMElement): Boolean; virtual;
  77. function ConvertBaseShort(AContext: TPasElement; Node: TDOMNode): Boolean;
  78. procedure ConvertBaseShortList(AContext: TPasElement; Node: TDOMNode;
  79. MayBeEmpty: Boolean);
  80. procedure ConvertLink(AContext: TPasElement; El: TDOMElement);
  81. function ConvertExtShort(AContext: TPasElement; Node: TDOMNode): Boolean;
  82. procedure ConvertDescr(AContext: TPasElement; El: TDOMElement;
  83. AutoInsertBlock: Boolean);
  84. function ConvertNonSectionBlock(AContext: TPasElement;
  85. Node: TDOMNode): Boolean;
  86. procedure ConvertExtShortOrNonSectionBlocks(AContext: TPasElement;
  87. Node: TDOMNode);
  88. function ConvertSimpleBlock(AContext: TPasElement; Node: TDOMNode): Boolean;
  89. Function FindTopicElement(Node : TDocNode): TTopicElement;
  90. Procedure ConvertImage(El : TDomElement);
  91. Procedure DescrEmitNotesHeader(AContext : TPasElement); virtual;
  92. Procedure DescrEmitNotesFooter(AContext : TPasElement); virtual;
  93. procedure DescrWriteText(const AText: DOMString); virtual; abstract;
  94. procedure DescrBeginBold; virtual; abstract;
  95. procedure DescrEndBold; virtual; abstract;
  96. procedure DescrBeginItalic; virtual; abstract;
  97. procedure DescrEndItalic; virtual; abstract;
  98. procedure DescrBeginEmph; virtual; abstract;
  99. procedure DescrEndEmph; virtual; abstract;
  100. procedure DescrWriteImageEl(const AFileName, ACaption,ALinkName : DOMString); virtual;
  101. procedure DescrWriteFileEl(const AText: DOMString); virtual; abstract;
  102. procedure DescrWriteKeywordEl(const AText: DOMString); virtual; abstract;
  103. procedure DescrWriteVarEl(const AText: DOMString); virtual; abstract;
  104. procedure DescrBeginLink(const AId: DOMString); virtual; abstract;
  105. procedure DescrEndLink; virtual; abstract;
  106. procedure DescrBeginURL(const AURL: DOMString); virtual; abstract;
  107. procedure DescrEndURL; virtual; abstract;
  108. procedure DescrWriteLinebreak; virtual; abstract;
  109. procedure DescrBeginParagraph; virtual; abstract;
  110. procedure DescrEndParagraph; virtual; abstract;
  111. procedure DescrBeginCode(HasBorder: Boolean; const AHighlighterName: String); virtual; abstract;
  112. procedure DescrWriteCodeLine(const ALine: String); virtual; abstract;
  113. procedure DescrEndCode; virtual; abstract;
  114. procedure DescrBeginOrderedList; virtual; abstract;
  115. procedure DescrEndOrderedList; virtual; abstract;
  116. procedure DescrBeginUnorderedList; virtual; abstract;
  117. procedure DescrEndUnorderedList; virtual; abstract;
  118. procedure DescrBeginDefinitionList; virtual; abstract;
  119. procedure DescrEndDefinitionList; virtual; abstract;
  120. procedure DescrBeginListItem; virtual; abstract;
  121. procedure DescrEndListItem; virtual; abstract;
  122. procedure DescrBeginDefinitionTerm; virtual; abstract;
  123. procedure DescrEndDefinitionTerm; virtual; abstract;
  124. procedure DescrBeginDefinitionEntry; virtual; abstract;
  125. procedure DescrEndDefinitionEntry; virtual; abstract;
  126. procedure DescrBeginSectionTitle; virtual; abstract;
  127. procedure DescrBeginSectionBody; virtual; abstract;
  128. procedure DescrEndSection; virtual; abstract;
  129. procedure DescrBeginRemark; virtual; abstract;
  130. procedure DescrEndRemark; virtual; abstract;
  131. procedure DescrBeginTable(ColCount: Integer; HasBorder: Boolean); virtual; abstract;
  132. procedure DescrEndTable; virtual; abstract;
  133. procedure DescrBeginTableCaption; virtual; abstract;
  134. procedure DescrEndTableCaption; virtual; abstract;
  135. procedure DescrBeginTableHeadRow; virtual; abstract;
  136. procedure DescrEndTableHeadRow; virtual; abstract;
  137. procedure DescrBeginTableRow; virtual; abstract;
  138. procedure DescrEndTableRow; virtual; abstract;
  139. procedure DescrBeginTableCell; virtual; abstract;
  140. procedure DescrEndTableCell; virtual; abstract;
  141. public
  142. Constructor Create(APackage: TPasPackage; AEngine: TFPDocEngine); virtual;
  143. destructor Destroy; override;
  144. property Engine : TFPDocEngine read FEngine;
  145. Property Package : TPasPackage read FPackage;
  146. Property Topics : TList Read FTopics;
  147. Property ImageExtension : String Read FImgExt Write FImgExt;
  148. // Should return True if option was succesfully interpreted.
  149. Function InterpretOption(Const Cmd,Arg : String) : Boolean; Virtual;
  150. Class Function FileNameExtension : String; virtual;
  151. Class Procedure Usage(List : TStrings); virtual;
  152. Class procedure SplitImport(var AFilename, ALinkPrefix: String); virtual;
  153. procedure WriteDoc; virtual; Abstract;
  154. Function WriteDescr(Element: TPasElement) : TDocNode;
  155. procedure WriteDescr(Element: TPasElement; DocNode: TDocNode);
  156. procedure WriteDescr(AContext: TPasElement; DescrNode: TDOMElement); virtual;
  157. Procedure FPDocError(Msg : String);
  158. Procedure FPDocError(Fmt : String; Args : Array of Const);
  159. Function ShowMember(M : TPasElement) : boolean;
  160. Procedure GetMethodList(ClassDecl: TPasClassType; List : TStringList);
  161. Property EmitNotes : Boolean Read FEmitNotes Write FEmitNotes;
  162. Property BeforeEmitNote : TWriterNoteEvent Read FBeforeEmitNote Write FBeforeEmitNote;
  163. end;
  164. TFPDocWriterClass = Class of TFPDocWriter;
  165. EFPDocWriterError = Class(Exception);
  166. // Register backend
  167. Procedure RegisterWriter(AClass : TFPDocWriterClass; Const AName,ADescr : String);
  168. // UnRegister backend
  169. Procedure UnRegisterWriter(Const AName : String);
  170. // Return back end class. Exception if not found.
  171. Function GetWriterClass(AName : String) : TFPDocWriterClass;
  172. // Return index of back end class.
  173. Function FindWriterClass(AName : String) : Integer;
  174. // List of backend in name=descr form.
  175. Procedure EnumWriters(List : TStrings);
  176. implementation
  177. { ---------------------------------------------------------------------
  178. Writer registration
  179. ---------------------------------------------------------------------}
  180. Type
  181. { TWriterRecord }
  182. TWriterRecord = Class(TObject)
  183. Private
  184. FClass : TFPDocWriterClass;
  185. FName : String;
  186. FDescription : String;
  187. Public
  188. Constructor Create (AClass : TFPDocWriterClass; Const AName,ADescr : String);
  189. end;
  190. { TWriterRecord }
  191. constructor TWriterRecord.Create(AClass: TFPDocWriterClass; const AName,
  192. ADescr: String);
  193. begin
  194. FClass:=AClass;
  195. FName:=AName;
  196. FDescription:=ADescr;
  197. end;
  198. Var
  199. Writers : TStringList;
  200. Procedure InitWriterList;
  201. begin
  202. Writers:=TStringList.Create;
  203. Writers.Sorted:=True;
  204. end;
  205. Procedure DoneWriterList;
  206. Var
  207. I : Integer;
  208. begin
  209. For I:=Writers.Count-1 downto 0 do
  210. Writers.Objects[i].Free;
  211. FreeAndNil(Writers);
  212. end;
  213. procedure RegisterWriter(AClass : TFPDocWriterClass; Const AName, ADescr : String);
  214. begin
  215. If Writers.IndexOf(AName)<>-1 then
  216. Raise EFPDocWriterError.CreateFmt(SErralreadyRegistered,[ANAme]);
  217. Writers.AddObject(AName,TWriterRecord.Create(AClass,AName,ADescr));
  218. end;
  219. function FindWriterClass(AName : String) : Integer;
  220. begin
  221. Result:=Writers.IndexOf(AName);
  222. end;
  223. function GetWriterClass(AName : String) : TFPDocWriterClass;
  224. Var
  225. Index : Integer;
  226. begin
  227. Index:=FindWriterClass(AName);
  228. If Index=-1 then
  229. Raise EFPDocWriterError.CreateFmt(SErrUnknownWriterClass,[ANAme]);
  230. Result:=(Writers.Objects[Index] as TWriterRecord).FClass;
  231. end;
  232. // UnRegister backend
  233. Procedure UnRegisterWriter(Const AName : String);
  234. Var
  235. Index : Integer;
  236. begin
  237. Index:=Writers.IndexOf(AName);
  238. If Index=-1 then
  239. Raise EFPDocWriterError.CreateFmt(SErrUnknownWriterClass,[ANAme]);
  240. Writers.Objects[Index].Free;
  241. Writers.Delete(Index);
  242. end;
  243. Procedure EnumWriters(List : TStrings);
  244. Var
  245. I : Integer;
  246. begin
  247. List.Clear;
  248. For I:=0 to Writers.Count-1 do
  249. With (Writers.Objects[I] as TWriterRecord) do
  250. List.Add(FName+'='+FDescription);
  251. end;
  252. function IsWhitespaceNode(Node: TDOMText): Boolean;
  253. var
  254. I,L: Integer;
  255. S: DOMString;
  256. P : PWideChar;
  257. begin
  258. S := Node.Data;
  259. Result := True;
  260. I:=0;
  261. L:=Length(S);
  262. P:=PWideChar(S);
  263. While Result and (I<L) do
  264. begin
  265. Result:=P^ in [#32,#10,#9,#13];
  266. Inc(P);
  267. Inc(I);
  268. end;
  269. end;
  270. { ---------------------------------------------------------------------
  271. TFPDocWriter
  272. ---------------------------------------------------------------------}
  273. {
  274. fmtIPF:
  275. begin
  276. if Length(Engine.Output) = 0 then
  277. WriteLn(SCmdLineOutputOptionMissing)
  278. else
  279. CreateIPFDocForPackage(Engine.Package, Engine);
  280. end;
  281. }
  282. Constructor TFPDocWriter.Create(APackage: TPasPackage; AEngine: TFPDocEngine);
  283. begin
  284. inherited Create;
  285. FEngine := AEngine;
  286. FPackage := APackage;
  287. FTopics:=Tlist.Create;
  288. FImgExt:='.png';
  289. end;
  290. destructor TFPDocWriter.Destroy;
  291. Var
  292. i : integer;
  293. begin
  294. For I:=0 to FTopics.Count-1 do
  295. TTopicElement(FTopics[i]).Free;
  296. FTopics.Free;
  297. Inherited;
  298. end;
  299. function TFPDocWriter.InterpretOption(const Cmd, Arg: String): Boolean;
  300. begin
  301. Result:=False;
  302. end;
  303. class function TFPDocWriter.FileNameExtension: String;
  304. begin
  305. //Override in linear writers with the expected extension.
  306. Result := ''; //Output must not contain an extension.
  307. end;
  308. class procedure TFPDocWriter.Usage(List: TStrings);
  309. begin
  310. // Do nothing.
  311. end;
  312. class procedure TFPDocWriter.SplitImport(var AFilename, ALinkPrefix: String);
  313. var
  314. i: integer;
  315. begin
  316. //override in HTML and CHM writer
  317. i := Pos(',', AFilename);
  318. if i > 0 then
  319. begin //split CSV into filename and prefix
  320. ALinkPrefix := Copy(AFilename,i+1,Length(AFilename));
  321. SetLength(AFilename, i-1);
  322. end;
  323. end;
  324. Function TFPDocWriter.FindTopicElement(Node : TDocNode): TTopicElement;
  325. Var
  326. I : Integer;
  327. begin
  328. Result:=Nil;
  329. I:=FTopics.Count-1;
  330. While (I>=0) and (Result=Nil) do
  331. begin
  332. If (TTopicElement(FTopics[i]).TopicNode=Node) Then
  333. Result:=TTopicElement(FTopics[i]);
  334. Dec(I);
  335. end;
  336. end;
  337. procedure TFPDocWriter.DescrWriteImageEl(const AFileName, ACaption,
  338. ALinkName: DOMString);
  339. begin
  340. DoLog('%s : No support for images yet: %s (caption: "%s")',[ClassName,AFileName,ACaption]);
  341. end;
  342. { ---------------------------------------------------------------------
  343. Generic documentation node conversion
  344. ---------------------------------------------------------------------}
  345. function IsContentNodeType(Node: TDOMNode): Boolean;
  346. begin
  347. Result := (Node.NodeType = ELEMENT_NODE) or
  348. ((Node.NodeType = TEXT_NODE) and not IsWhitespaceNode(TDOMText(Node))) or
  349. (Node.NodeType = ENTITY_REFERENCE_NODE);
  350. end;
  351. procedure TFPDocWriter.Warning(AContext: TPasElement; const AMsg: String);
  352. begin
  353. if (AContext<>nil) then
  354. DoLog('[%s] %s',[AContext.PathName,AMsg])
  355. else
  356. DoLog('[<no context>] %s', [AMsg]);
  357. end;
  358. procedure TFPDocWriter.Warning(AContext: TPasElement; const AMsg: String;
  359. const Args: array of const);
  360. begin
  361. Warning(AContext, Format(AMsg, Args));
  362. end;
  363. function TFPDocWriter.IsDescrNodeEmpty(Node: TDOMNode): Boolean;
  364. var
  365. Child: TDOMNode;
  366. begin
  367. if (not Assigned(Node)) or (not Assigned(Node.FirstChild)) then
  368. Result := True
  369. else
  370. begin
  371. Child := Node.FirstChild;
  372. while Assigned(Child) do
  373. begin
  374. if (Child.NodeType = ELEMENT_NODE) or (Child.NodeType = TEXT_NODE) or
  375. (Child.NodeType = ENTITY_REFERENCE_NODE) then
  376. begin
  377. Result := False;
  378. exit;
  379. end;
  380. Child := Child.NextSibling;
  381. end;
  382. end;
  383. Result := True;
  384. end;
  385. { Check wether the nodes starting with the node given as argument make up an
  386. 'extshort' production. }
  387. function TFPDocWriter.IsExtShort(Node: TDOMNode): Boolean;
  388. begin
  389. while Assigned(Node) do
  390. begin
  391. if Node.NodeType = ELEMENT_NODE then
  392. if (Node.NodeName <> 'br') and
  393. (Node.NodeName <> 'link') and
  394. (Node.NodeName <> 'url') and
  395. (Node.NodeName <> 'b') and
  396. (Node.NodeName <> 'file') and
  397. (Node.NodeName <> 'i') and
  398. (Node.NodeName <> 'kw') and
  399. (Node.NodeName <> 'printshort') and
  400. (Node.NodeName <> 'var') then
  401. begin
  402. Result := False;
  403. exit;
  404. end;
  405. Node := Node.NextSibling;
  406. end;
  407. Result := True;
  408. end;
  409. function TFPDocWriter.ConvertShort(AContext: TPasElement;
  410. El: TDOMElement): Boolean;
  411. var
  412. Node: TDOMNode;
  413. begin
  414. Result := False;
  415. if not Assigned(El) then
  416. exit;
  417. Node := El.FirstChild;
  418. while Assigned(Node) do
  419. begin
  420. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'link') then
  421. ConvertLink(AContext, TDOMElement(Node))
  422. else if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'url') then
  423. ConvertURL(AContext, TDOMElement(Node))
  424. else
  425. if not ConvertBaseShort(AContext, Node) then
  426. exit;
  427. Node := Node.NextSibling;
  428. end;
  429. Result := True;
  430. end;
  431. function TFPDocWriter.ConvertNotes(AContext: TPasElement; El: TDOMElement
  432. ): Boolean;
  433. Var
  434. L : TFPList;
  435. N : TDomNode;
  436. I : Integer;
  437. B : Boolean;
  438. begin
  439. Result:=Assigned(El) and EmitNotes;
  440. If Not Result then
  441. exit;
  442. L:=TFPList.Create;
  443. try
  444. N:=El.FirstChild;
  445. While Assigned(N) do
  446. begin
  447. If (N.NodeType=ELEMENT_NODE) and (N.NodeName='note') then
  448. begin
  449. B:=True;
  450. if Assigned(FBeforeEmitNote) then
  451. FBeforeEmitNote(Self,TDomElement(N),B);
  452. If B then
  453. L.Add(N);
  454. end;
  455. N:=N.NextSibling;
  456. end;
  457. Result:=L.Count>0;
  458. If Not Result then
  459. exit;
  460. DescrEmitNotesHeader(AContext);
  461. DescrBeginUnorderedList;
  462. For i:=0 to L.Count-1 do
  463. begin
  464. DescrBeginListItem;
  465. ConvertExtShortOrNonSectionBlocks(AContext, TDOMNode(L[i]).FirstChild);
  466. DescrEndListItem;
  467. end;
  468. DescrEndUnorderedList;
  469. DescrEmitNotesFooter(AContext);
  470. finally
  471. L.Free;
  472. end;
  473. end;
  474. function TFPDocWriter.ConvertBaseShort(AContext: TPasElement;
  475. Node: TDOMNode): Boolean;
  476. function ConvertText: DOMString;
  477. var
  478. s: DOMString;
  479. i: Integer;
  480. begin
  481. if Node.NodeType = TEXT_NODE then
  482. begin
  483. s := Node.NodeValue;
  484. i := 1;
  485. SetLength(Result, 0);
  486. while i <= Length(s) do
  487. if s[i] = #13 then
  488. begin
  489. Result := Result + ' ';
  490. Inc(i);
  491. if s[i] = #10 then
  492. Inc(i);
  493. end else if s[i] = #10 then
  494. begin
  495. Result := Result + ' ';
  496. Inc(i);
  497. end else
  498. begin
  499. Result := Result + s[i];
  500. Inc(i);
  501. end;
  502. end else if Node.NodeType = ENTITY_REFERENCE_NODE then
  503. if Node.NodeName = 'fpc' then
  504. Result := 'Free Pascal'
  505. else if Node.NodeName = 'delphi' then
  506. Result := 'Delphi'
  507. else
  508. begin
  509. Warning(AContext, Format(SErrUnknownEntityReference, [Node.NodeName]));
  510. Result := Node.NodeName;
  511. end
  512. else if Node.NodeType = ELEMENT_NODE then
  513. SetLength(Result, 0);
  514. end;
  515. function ConvertTextContent: DOMString;
  516. begin
  517. SetLength(Result, 0);
  518. Node := Node.FirstChild;
  519. while Assigned(Node) do
  520. begin
  521. Result := Result + ConvertText;
  522. Node := Node.NextSibling;
  523. end;
  524. end;
  525. var
  526. El, DescrEl: TDOMElement;
  527. FPEl: TPasElement;
  528. begin
  529. Result := True;
  530. if Node.NodeType = ELEMENT_NODE then
  531. if Node.NodeName = 'b' then
  532. begin
  533. DescrBeginBold;
  534. ConvertBaseShortList(AContext, Node, False);
  535. DescrEndBold;
  536. end else
  537. if Node.NodeName = 'i' then
  538. begin
  539. DescrBeginItalic;
  540. ConvertBaseShortList(AContext, Node, False);
  541. DescrEndItalic;
  542. end else
  543. if Node.NodeName = 'em' then
  544. begin
  545. DescrBeginEmph;
  546. ConvertBaseShortList(AContext, Node, False);
  547. DescrEndEmph;
  548. end else
  549. if Node.NodeName = 'file' then
  550. DescrWriteFileEl(ConvertTextContent)
  551. else if Node.NodeName = 'kw' then
  552. DescrWriteKeywordEl(ConvertTextContent)
  553. else if Node.NodeName = 'printshort' then
  554. begin
  555. El := TDOMElement(Node);
  556. DescrEl := Engine.FindShortDescr(AContext.GetModule, El['id']);
  557. if Assigned(DescrEl) then
  558. ConvertShort(AContext, DescrEl)
  559. else
  560. begin
  561. Warning(AContext, Format(SErrUnknownPrintShortID, [El['id']]));
  562. DescrBeginBold;
  563. DescrWriteText('#ShortDescr:' + El['id']);
  564. DescrEndBold;
  565. end;
  566. end else if Node.NodeName = 'var' then
  567. DescrWriteVarEl(ConvertTextContent)
  568. else
  569. Result := False
  570. else
  571. DescrWriteText(ConvertText);
  572. end;
  573. procedure TFPDocWriter.ConvertBaseShortList(AContext: TPasElement;
  574. Node: TDOMNode; MayBeEmpty: Boolean);
  575. var
  576. Child: TDOMNode;
  577. begin
  578. Child := Node.FirstChild;
  579. while Assigned(Child) do
  580. begin
  581. if not ConvertBaseShort(AContext, Child) then
  582. Warning(AContext, SErrInvalidShortDescr)
  583. else
  584. MayBeEmpty := True;
  585. Child := Child.NextSibling;
  586. end;
  587. if not MayBeEmpty then
  588. Warning(AContext, SErrInvalidShortDescr)
  589. end;
  590. procedure TFPDocWriter.ConvertLink(AContext: TPasElement; El: TDOMElement);
  591. begin
  592. DescrBeginLink(El['id']);
  593. if not IsDescrNodeEmpty(El) then
  594. ConvertBaseShortList(AContext, El, True)
  595. else
  596. DescrWriteText(El['id']);
  597. DescrEndLink;
  598. end;
  599. procedure TFPDocWriter.ConvertURL(AContext: TPasElement; El: TDOMElement);
  600. begin
  601. DescrBeginURL(El['href']);
  602. if not IsDescrNodeEmpty(El) then
  603. ConvertBaseShortList(AContext, El, True)
  604. else
  605. DescrWriteText(El['href']);
  606. DescrEndURL;
  607. end;
  608. procedure TFPDocWriter.DoLog(const Msg: String);
  609. begin
  610. If Assigned(FEngine.OnLog) then
  611. FEngine.OnLog(Self,Msg);
  612. end;
  613. procedure TFPDocWriter.DoLog(const Fmt: String; Args: array of const);
  614. begin
  615. DoLog(Format(Fmt,Args));
  616. end;
  617. function TFPDocWriter.ConvertExtShort(AContext: TPasElement;
  618. Node: TDOMNode): Boolean;
  619. begin
  620. Result := False;
  621. while Assigned(Node) do
  622. begin
  623. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'link') then
  624. ConvertLink(AContext, TDOMElement(Node))
  625. else if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'url') then
  626. ConvertURL(AContext, TDOMElement(Node))
  627. else if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'br') then
  628. DescrWriteLinebreak
  629. else
  630. if not ConvertBaseShort(AContext, Node) then
  631. exit;
  632. Node := Node.NextSibling;
  633. end;
  634. Result := True;
  635. end;
  636. procedure TFPDocWriter.ConvertDescr(AContext: TPasElement; El: TDOMElement;
  637. AutoInsertBlock: Boolean);
  638. var
  639. Node, Child: TDOMNode;
  640. ParaCreated: Boolean;
  641. begin
  642. if AutoInsertBlock then
  643. if IsExtShort(El.FirstChild) then
  644. DescrBeginParagraph
  645. else
  646. AutoInsertBlock := False;
  647. Node := El.FirstChild;
  648. if not ConvertExtShort(AContext, Node) then
  649. begin
  650. while Assigned(Node) do
  651. begin
  652. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'section') then
  653. begin
  654. DescrBeginSectionTitle;
  655. Child := Node.FirstChild;
  656. while Assigned(Child) and (Child.NodeType <> ELEMENT_NODE) do
  657. begin
  658. if not IsDescrNodeEmpty(Child) then
  659. Warning(AContext, SErrInvalidContentBeforeSectionTitle);
  660. Child := Child.NextSibling;
  661. end;
  662. if not Assigned(Child) or (Child.NodeName <> 'title') then
  663. Warning(AContext, SErrSectionTitleExpected)
  664. else
  665. ConvertShort(AContext, TDOMElement(Child));
  666. DescrBeginSectionBody;
  667. if IsExtShort(Child) then
  668. begin
  669. DescrBeginParagraph;
  670. ParaCreated := True;
  671. end else
  672. ParaCreated := False;
  673. ConvertExtShortOrNonSectionBlocks(AContext, Child.NextSibling);
  674. if ParaCreated then
  675. DescrEndParagraph;
  676. DescrEndSection;
  677. end else if not ConvertNonSectionBlock(AContext, Node) then
  678. Warning(AContext, SErrInvalidDescr, [Node.NodeName]);
  679. Node := Node.NextSibling;
  680. end;
  681. end else
  682. if AutoInsertBlock then
  683. DescrEndParagraph;
  684. end;
  685. procedure TFPDocWriter.ConvertExtShortOrNonSectionBlocks(AContext: TPasElement;
  686. Node: TDOMNode);
  687. begin
  688. if not ConvertExtShort(AContext, Node) then
  689. while Assigned(Node) do
  690. begin
  691. if not ConvertNonSectionBlock(AContext, Node) then
  692. Warning(AContext, SErrInvalidDescr, [Node.NodeName]);
  693. Node := Node.NextSibling;
  694. end;
  695. end;
  696. function TFPDocWriter.ConvertNonSectionBlock(AContext: TPasElement;
  697. Node: TDOMNode): Boolean;
  698. procedure ConvertCells(Node: TDOMNode);
  699. var
  700. Child: TDOMNode;
  701. IsEmpty: Boolean;
  702. begin
  703. Node := Node.FirstChild;
  704. IsEmpty := True;
  705. while Assigned(Node) do
  706. begin
  707. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'td') then
  708. begin
  709. DescrBeginTableCell;
  710. Child := Node.FirstChild;
  711. if not ConvertExtShort(AContext, Child) then
  712. while Assigned(Child) do
  713. begin
  714. if not ConvertSimpleBlock(AContext, Child) then
  715. Warning(AContext, SErrInvalidTableContent);
  716. Child := Child.NextSibling;
  717. end;
  718. DescrEndTableCell;
  719. IsEmpty := False;
  720. end else
  721. if IsContentNodeType(Node) then
  722. Warning(AContext, SErrInvalidTableContent);
  723. Node := Node.NextSibling;
  724. end;
  725. if IsEmpty then
  726. Warning(AContext, SErrTableRowEmpty);
  727. end;
  728. procedure ConvertTable;
  729. function GetColCount(Node: TDOMNode): Integer;
  730. begin
  731. Result := 0;
  732. Node := Node.FirstChild;
  733. while Assigned(Node) do
  734. begin
  735. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'td') then
  736. Inc(Result);
  737. Node := Node.NextSibling;
  738. end;
  739. end;
  740. var
  741. s: String;
  742. HasBorder, CaptionPossible, HeadRowPossible: Boolean;
  743. ColCount, ThisRowColCount: Integer;
  744. Subnode: TDOMNode;
  745. begin
  746. s := TDOMElement(Node)['border'];
  747. if s = '1' then
  748. HasBorder := True
  749. else
  750. begin
  751. HasBorder := False;
  752. if (Length(s) <> 0) and (s <> '0') then
  753. Warning(AContext, SErrInvalidBorderValue, ['<table>']);
  754. end;
  755. // Determine the number of columns
  756. ColCount := 0;
  757. Subnode := Node.FirstChild;
  758. while Assigned(Subnode) do
  759. begin
  760. if Subnode.NodeType = ELEMENT_NODE then
  761. if (Subnode.NodeName = 'caption') or (Subnode.NodeName = 'th') or
  762. (Subnode.NodeName = 'tr') then
  763. begin
  764. ThisRowColCount := GetColCount(Subnode);
  765. if ThisRowColCount > ColCount then
  766. ColCount := ThisRowColCount;
  767. end;
  768. Subnode := Subnode.NextSibling;
  769. end;
  770. DescrBeginTable(ColCount, HasBorder);
  771. Node := Node.FirstChild;
  772. CaptionPossible := True;
  773. HeadRowPossible := True;
  774. while Assigned(Node) do
  775. begin
  776. if Node.NodeType = ELEMENT_NODE then
  777. if CaptionPossible and (Node.NodeName = 'caption') then
  778. begin
  779. DescrBeginTableCaption;
  780. if not ConvertExtShort(AContext, Node.FirstChild) then
  781. Warning(AContext, SErrInvalidTableContent);
  782. DescrEndTableCaption;
  783. CaptionPossible := False;
  784. end else if HeadRowPossible and (Node.NodeName = 'th') then
  785. begin
  786. DescrBeginTableHeadRow;
  787. ConvertCells(Node);
  788. DescrEndTableHeadRow;
  789. CaptionPossible := False;
  790. HeadRowPossible := False;
  791. end else if Node.NodeName = 'tr' then
  792. begin
  793. DescrBeginTableRow;
  794. ConvertCells(Node);
  795. DescrEndTableRow;
  796. end else
  797. Warning(AContext, SErrInvalidTableContent)
  798. else if IsContentNodeType(Node) then
  799. Warning(AContext, SErrInvalidTableContent);
  800. Node := Node.NextSibling;
  801. end;
  802. DescrEndTable;
  803. end;
  804. begin
  805. if Node.NodeType <> ELEMENT_NODE then
  806. begin
  807. if Node.NodeType = TEXT_NODE then
  808. Result := IsWhitespaceNode(TDOMText(Node))
  809. else
  810. Result := Node.NodeType = COMMENT_NODE;
  811. exit;
  812. end;
  813. if Node.NodeName = 'remark' then
  814. begin
  815. DescrBeginRemark;
  816. Node := Node.FirstChild;
  817. if not ConvertExtShort(AContext, Node) then
  818. while Assigned(Node) do
  819. begin
  820. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'table') then
  821. ConvertTable
  822. else
  823. if not ConvertSimpleBlock(AContext, Node) then
  824. Warning(AContext, SErrInvalidRemarkContent, [Node.NodeName]);
  825. Node := Node.NextSibling;
  826. end;
  827. DescrEndRemark;
  828. Result := True;
  829. end else if Node.NodeName = 'table' then
  830. begin
  831. ConvertTable;
  832. Result := True;
  833. end else
  834. Result := ConvertSimpleBlock(AContext, Node);
  835. end;
  836. function TFPDocWriter.ConvertSimpleBlock(AContext: TPasElement;
  837. Node: TDOMNode): Boolean;
  838. procedure ConvertListItems;
  839. var
  840. Empty: Boolean;
  841. begin
  842. Node := Node.FirstChild;
  843. Empty := True;
  844. while Assigned(Node) do
  845. begin
  846. if ((Node.NodeType = TEXT_NODE) and not IsWhitespaceNode(TDOMText(Node))) or (Node.NodeType = ENTITY_REFERENCE_NODE)
  847. then
  848. Warning(AContext, SErrInvalidListContent)
  849. else if Node.NodeType = ELEMENT_NODE then
  850. if Node.NodeName = 'li' then
  851. begin
  852. DescrBeginListItem;
  853. ConvertExtShortOrNonSectionBlocks(AContext, Node.FirstChild);
  854. DescrEndListItem;
  855. Empty := False;
  856. end else
  857. Warning(AContext, SErrInvalidElementInList);
  858. Node := Node.NextSibling;
  859. end;
  860. if Empty then
  861. Warning(AContext, SErrListIsEmpty);
  862. end;
  863. procedure ConvertDefinitionList;
  864. var
  865. Empty, ExpectDTNext: Boolean;
  866. begin
  867. Node := Node.FirstChild;
  868. Empty := True;
  869. ExpectDTNext := True;
  870. while Assigned(Node) do
  871. begin
  872. if ((Node.NodeType = TEXT_NODE) and not IsWhitespaceNode(TDOMText(Node))) or (Node.NodeType = ENTITY_REFERENCE_NODE)
  873. then
  874. Warning(AContext, SErrInvalidListContent)
  875. else if Node.NodeType = ELEMENT_NODE then
  876. if ExpectDTNext and (Node.NodeName = 'dt') then
  877. begin
  878. DescrBeginDefinitionTerm;
  879. if not ConvertShort(AContext, TDOMElement(Node)) then
  880. Warning(AContext, SErrInvalidDefinitionTermContent);
  881. DescrEndDefinitionTerm;
  882. Empty := False;
  883. ExpectDTNext := False;
  884. end else if not ExpectDTNext and (Node.NodeName = 'dd') then
  885. begin
  886. DescrBeginDefinitionEntry;
  887. ConvertExtShortOrNonSectionBlocks(AContext, Node.FirstChild);
  888. DescrEndDefinitionEntry;
  889. ExpectDTNext := True;
  890. end else
  891. Warning(AContext, SErrInvalidElementInList);
  892. Node := Node.NextSibling;
  893. end;
  894. if Empty then
  895. Warning(AContext, SErrListIsEmpty)
  896. else if not ExpectDTNext then
  897. Warning(AContext, SErrDefinitionEntryMissing);
  898. end;
  899. procedure ProcessCodeBody(Node: TDOMNode);
  900. var
  901. s: String;
  902. i, j: Integer;
  903. begin
  904. Node := Node.FirstChild;
  905. SetLength(s, 0);
  906. while Assigned(Node) do
  907. begin
  908. if Node.NodeType = TEXT_NODE then
  909. begin
  910. s := s + Node.NodeValue;
  911. j := 1;
  912. for i := 1 to Length(s) do
  913. // In XML, linefeeds are normalized to #10 by the parser!
  914. if s[i] = #10 then
  915. begin
  916. DescrWriteCodeLine(Copy(s, j, i - j));
  917. j := i + 1;
  918. end;
  919. if j > 1 then
  920. s := Copy(s, j, Length(s));
  921. end;
  922. Node := Node.NextSibling;
  923. end;
  924. if Length(s) > 0 then
  925. DescrWriteCodeLine(s);
  926. end;
  927. var
  928. s: String;
  929. HasBorder: Boolean;
  930. begin
  931. if Node.NodeType <> ELEMENT_NODE then
  932. begin
  933. Result := (Node.NodeType = TEXT_NODE) and IsWhitespaceNode(TDOMText(Node));
  934. exit;
  935. end;
  936. if Node.NodeName = 'p' then
  937. begin
  938. DescrBeginParagraph;
  939. if not ConvertExtShort(AContext, Node.FirstChild) then
  940. Warning(AContext, SErrInvalidParaContent);
  941. DescrEndParagraph;
  942. Result := True;
  943. end else if Node.NodeName = 'code' then
  944. begin
  945. s := TDOMElement(Node)['border'];
  946. if s = '1' then
  947. HasBorder := True
  948. else
  949. begin
  950. if (Length(s) > 0) and (s <> '0') then
  951. Warning(AContext, SErrInvalidBorderValue, ['<code>']);
  952. end;
  953. DescrBeginCode(HasBorder, TDOMElement(Node)['highlighter']);
  954. ProcessCodeBody(Node);
  955. DescrEndCode;
  956. Result := True;
  957. end else if Node.NodeName = 'pre' then
  958. begin
  959. DescrBeginCode(False, 'none');
  960. ProcessCodeBody(Node);
  961. DescrEndCode;
  962. Result := True;
  963. end else if Node.NodeName = 'ul' then
  964. begin
  965. DescrBeginUnorderedList;
  966. ConvertListItems;
  967. DescrEndUnorderedList;
  968. Result := True;
  969. end else if Node.NodeName = 'ol' then
  970. begin
  971. DescrBeginOrderedList;
  972. ConvertListItems;
  973. DescrEndOrderedList;
  974. Result := True;
  975. end else if Node.NodeName = 'dl' then
  976. begin
  977. DescrBeginDefinitionList;
  978. ConvertDefinitionList;
  979. DescrEndDefinitionList;
  980. Result := True;
  981. end else if Node.NodeName = 'img' then
  982. begin
  983. begin
  984. ConvertImage(Node as TDomElement);
  985. Result:=True;
  986. end;
  987. end else
  988. Result := False;
  989. end;
  990. Procedure TFPDocWriter.ConvertImage(El : TDomElement);
  991. Var
  992. FN,Cap,LinkName : DOMString;
  993. begin
  994. FN:=El['file'];
  995. Cap:=El['caption'];
  996. LinkName:=El['name'];
  997. FN:=ChangeFileExt(FN,ImageExtension);
  998. DescrWriteImageEl(FN,Cap,LinkName);
  999. end;
  1000. procedure TFPDocWriter.DescrEmitNotesHeader(AContext: TPasElement);
  1001. begin
  1002. DescrWriteLinebreak;
  1003. DescrBeginBold;
  1004. DescrWriteText(SDocNotes);
  1005. DescrEndBold;
  1006. DescrWriteLinebreak;
  1007. end;
  1008. procedure TFPDocWriter.DescrEmitNotesFooter(AContext: TPasElement);
  1009. begin
  1010. DescrWriteLinebreak;
  1011. end;
  1012. Constructor TTopicElement.Create(const AName: String; AParent: TPasElement);
  1013. begin
  1014. Inherited Create(AName,AParent);
  1015. SubTopics:=TList.Create;
  1016. end;
  1017. Destructor TTopicElement.Destroy;
  1018. begin
  1019. // Actual subtopics are freed by TFPDocWriter Topics list.
  1020. SubTopics.Free;
  1021. Inherited;
  1022. end;
  1023. Function TFPDocWriter.WriteDescr(Element: TPasElement) : TDocNode;
  1024. begin
  1025. Result:=Engine.FindDocNode(Element);
  1026. WriteDescr(ELement,Result);
  1027. end;
  1028. procedure TFPDocWriter.WriteDescr(Element: TPasElement; DocNode: TDocNode);
  1029. begin
  1030. if Assigned(DocNode) then
  1031. begin
  1032. if not IsDescrNodeEmpty(DocNode.Descr) then
  1033. WriteDescr(Element, DocNode.Descr)
  1034. else if not IsDescrNodeEmpty(DocNode.ShortDescr) then
  1035. WriteDescr(Element, DocNode.ShortDescr);
  1036. end;
  1037. end;
  1038. procedure TFPDocWriter.WriteDescr(AContext: TPasElement; DescrNode: TDOMElement);
  1039. begin
  1040. if Assigned(DescrNode) then
  1041. ConvertDescr(AContext, DescrNode, False);
  1042. end;
  1043. procedure TFPDocWriter.FPDocError(Msg: String);
  1044. begin
  1045. Raise EFPDocWriterError.Create(Msg);
  1046. end;
  1047. procedure TFPDocWriter.FPDocError(Fmt: String; Args: array of const);
  1048. begin
  1049. FPDocError(Format(Fmt,Args));
  1050. end;
  1051. function TFPDocWriter.ShowMember(M: TPasElement): boolean;
  1052. begin
  1053. Result:=not ((M.Visibility=visPrivate) and Engine.HidePrivate);
  1054. If Result then
  1055. Result:=Not ((M.Visibility=visProtected) and Engine.HideProtected)
  1056. end;
  1057. Procedure TFPDocWriter.GetMethodList(ClassDecl: TPasClassType; List : TStringList);
  1058. Var
  1059. I : Integer;
  1060. M : TPasElement;
  1061. begin
  1062. List.Clear;
  1063. List.Sorted:=False;
  1064. for i := 0 to ClassDecl.Members.Count - 1 do
  1065. begin
  1066. M:=TPasElement(ClassDecl.Members[i]);
  1067. if M.InheritsFrom(TPasProcedureBase) and ShowMember(M) then
  1068. List.AddObject(M.Name,M);
  1069. end;
  1070. List.Sorted:=False;
  1071. end;
  1072. initialization
  1073. InitWriterList;
  1074. finalization
  1075. DoneWriterList;
  1076. end.