htmwrite.pp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. {
  2. $Id$
  3. This file is part of the Free Component Library
  4. HTML writing routines
  5. Copyright (c) 2000 by
  6. Areca Systems GmbH / Sebastian Guenther, [email protected]
  7. See the file COPYING.FPC, included in this distribution,
  8. for details about the copyright.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12. **********************************************************************}
  13. unit HTMWrite;
  14. {$MODE objfpc}
  15. {$H+}
  16. interface
  17. uses Classes, DOM;
  18. procedure WriteHTMLFile(doc: TXMLDocument; const AFileName: String);
  19. procedure WriteHTMLFile(doc: TXMLDocument; var AFile: Text);
  20. procedure WriteHTMLFile(doc: TXMLDocument; var AStream: TStream);
  21. procedure WriteHTML(Element: TDOMElement; const AFileName: String);
  22. procedure WriteHTML(Element: TDOMElement; var AFile: Text);
  23. procedure WriteHTML(Element: TDOMElement; var AStream: TStream);
  24. // ===================================================================
  25. implementation
  26. uses SysUtils;
  27. // -------------------------------------------------------------------
  28. // Writers for the different node types
  29. // -------------------------------------------------------------------
  30. procedure WriteElement(node: TDOMNode); forward;
  31. procedure WriteAttribute(node: TDOMNode); forward;
  32. procedure WriteText(node: TDOMNode); forward;
  33. procedure WriteCDATA(node: TDOMNode); forward;
  34. procedure WriteEntityRef(node: TDOMNode); forward;
  35. procedure WriteEntity(node: TDOMNode); forward;
  36. procedure WritePI(node: TDOMNode); forward;
  37. procedure WriteComment(node: TDOMNode); forward;
  38. procedure WriteDocument(node: TDOMNode); forward;
  39. procedure WriteDocumentType(node: TDOMNode); forward;
  40. procedure WriteDocumentFragment(node: TDOMNode); forward;
  41. procedure WriteNotation(node: TDOMNode); forward;
  42. type
  43. TWriteNodeProc = procedure(node: TDOMNode);
  44. const
  45. WriteProcs: array[ELEMENT_NODE..NOTATION_NODE] of TWriteNodeProc =
  46. (WriteElement, WriteAttribute, WriteText, WriteCDATA, WriteEntityRef,
  47. WriteEntity, WritePI, WriteComment, WriteDocument, WriteDocumentType,
  48. WriteDocumentFragment, WriteNotation);
  49. procedure WriteNode(node: TDOMNode);
  50. begin
  51. WriteProcs[node.NodeType](node);
  52. end;
  53. // -------------------------------------------------------------------
  54. // Text file and TStream support
  55. // -------------------------------------------------------------------
  56. type
  57. TOutputProc = procedure(s: String);
  58. var
  59. f: ^Text;
  60. stream: TStream;
  61. wrt, wrtln: TOutputProc;
  62. InsideTextNode: Boolean;
  63. procedure Text_Write(s: String);
  64. begin
  65. Write(f^, s);
  66. end;
  67. procedure Text_WriteLn(s: String);
  68. begin
  69. WriteLn(f^, s);
  70. end;
  71. procedure Stream_Write(s: String);
  72. begin
  73. if Length(s) > 0 then
  74. stream.Write(s[1], Length(s));
  75. end;
  76. procedure Stream_WriteLn(s: String);
  77. begin
  78. if Length(s) > 0 then
  79. stream.Write(s[1], Length(s));
  80. stream.WriteByte(10);
  81. end;
  82. // -------------------------------------------------------------------
  83. // String conversion
  84. // -------------------------------------------------------------------
  85. type
  86. TCharacters = set of Char;
  87. TSpecialCharCallback = procedure(c: Char);
  88. const
  89. AttrSpecialChars = ['"', '&'];
  90. TextSpecialChars = ['<', '>', '&'];
  91. procedure ConvWrite(const s: String; const SpecialChars: TCharacters;
  92. const SpecialCharCallback: TSpecialCharCallback);
  93. var
  94. StartPos, EndPos: Integer;
  95. begin
  96. StartPos := 1;
  97. EndPos := 1;
  98. while EndPos <= Length(s) do
  99. begin
  100. if s[EndPos] in SpecialChars then
  101. begin
  102. wrt(Copy(s, StartPos, EndPos - StartPos));
  103. SpecialCharCallback(s[EndPos]);
  104. StartPos := EndPos + 1;
  105. end;
  106. Inc(EndPos);
  107. end;
  108. if EndPos > StartPos then
  109. wrt(Copy(s, StartPos, EndPos - StartPos));
  110. end;
  111. procedure AttrSpecialCharCallback(c: Char);
  112. begin
  113. if c = '"' then
  114. wrt('&quot;')
  115. else if c = '&' then
  116. wrt('&amp;')
  117. else
  118. wrt(c);
  119. end;
  120. procedure TextnodeSpecialCharCallback(c: Char);
  121. begin
  122. if c = '<' then
  123. wrt('&lt;')
  124. else if c = '>' then
  125. wrt('&gt;')
  126. else if c = '&' then
  127. wrt('&amp;')
  128. else
  129. wrt(c);
  130. end;
  131. function IsTextNode(Node: TDOMNode): Boolean;
  132. begin
  133. Result := Node.NodeType in [TEXT_NODE, ENTITY_REFERENCE_NODE];
  134. end;
  135. // -------------------------------------------------------------------
  136. // Node writers implementations
  137. // -------------------------------------------------------------------
  138. procedure WriteElement(node: TDOMNode);
  139. var
  140. i: Integer;
  141. attr, child: TDOMNode;
  142. SavedInsideTextNode: Boolean;
  143. s: String;
  144. begin
  145. wrt('<' + node.NodeName);
  146. for i := 0 to node.Attributes.Length - 1 do
  147. begin
  148. attr := node.Attributes.Item[i];
  149. wrt(' ' + attr.NodeName + '=');
  150. s := attr.NodeValue;
  151. // !!!: Replace special characters in "s" such as '&', '<', '>'
  152. wrt('"');
  153. ConvWrite(s, AttrSpecialChars, @AttrSpecialCharCallback);
  154. wrt('"');
  155. end;
  156. Child := node.FirstChild;
  157. if Child = nil then
  158. if InsideTextNode then
  159. wrt(' />')
  160. else
  161. wrtln(' />')
  162. else
  163. begin
  164. SavedInsideTextNode := InsideTextNode;
  165. if InsideTextNode or IsTextNode(Child) then
  166. wrt('>')
  167. else
  168. wrtln('>');
  169. repeat
  170. if IsTextNode(Child) then
  171. InsideTextNode := True;
  172. WriteNode(Child);
  173. Child := Child.NextSibling;
  174. until child = nil;
  175. InsideTextNode := SavedInsideTextNode;
  176. s := '</' + node.NodeName + '>';
  177. if InsideTextNode then
  178. wrt(s)
  179. else
  180. wrtln(s);
  181. end;
  182. end;
  183. procedure WriteAttribute(node: TDOMNode);
  184. begin
  185. WriteLn('WriteAttribute');
  186. end;
  187. procedure WriteText(node: TDOMNode);
  188. begin
  189. ConvWrite(node.NodeValue, TextSpecialChars, @TextnodeSpecialCharCallback);
  190. end;
  191. procedure WriteCDATA(node: TDOMNode);
  192. begin
  193. if InsideTextNode then
  194. wrt('<![CDATA[' + node.NodeValue + ']]>')
  195. else
  196. wrtln('<![CDATA[' + node.NodeValue + ']]>')
  197. end;
  198. procedure WriteEntityRef(node: TDOMNode);
  199. begin
  200. wrt('&' + node.NodeName + ';');
  201. end;
  202. procedure WriteEntity(node: TDOMNode);
  203. begin
  204. WriteLn('WriteEntity');
  205. end;
  206. procedure WritePI(node: TDOMNode);
  207. var
  208. s: String;
  209. begin
  210. s := '<!' + TDOMProcessingInstruction(node).Target + ' ' +
  211. TDOMProcessingInstruction(node).Data + '>';
  212. if InsideTextNode then
  213. wrt(s)
  214. else
  215. wrtln( s);
  216. end;
  217. procedure WriteComment(node: TDOMNode);
  218. begin
  219. if InsideTextNode then
  220. wrt('<!--' + node.NodeValue + '-->')
  221. else
  222. wrtln('<!--' + node.NodeValue + '-->')
  223. end;
  224. procedure WriteDocument(node: TDOMNode);
  225. begin
  226. WriteLn('WriteDocument');
  227. end;
  228. procedure WriteDocumentType(node: TDOMNode);
  229. begin
  230. WriteLn('WriteDocumentType');
  231. end;
  232. procedure WriteDocumentFragment(node: TDOMNode);
  233. begin
  234. WriteLn('WriteDocumentFragment');
  235. end;
  236. procedure WriteNotation(node: TDOMNode);
  237. begin
  238. WriteLn('WriteNotation');
  239. end;
  240. procedure InitWriter;
  241. begin
  242. InsideTextNode := False;
  243. end;
  244. procedure RootWriter(doc: TXMLDocument);
  245. var
  246. Child: TDOMNode;
  247. begin
  248. InitWriter;
  249. child := doc.FirstChild;
  250. while Assigned(Child) do
  251. begin
  252. WriteNode(Child);
  253. Child := Child.NextSibling;
  254. end;
  255. end;
  256. // -------------------------------------------------------------------
  257. // Interface implementation
  258. // -------------------------------------------------------------------
  259. procedure WriteHTMLFile(doc: TXMLDocument; const AFileName: String);
  260. begin
  261. Stream := TFileStream.Create(AFileName, fmCreate);
  262. wrt := @Stream_Write;
  263. wrtln := @Stream_WriteLn;
  264. RootWriter(doc);
  265. Stream.Free;
  266. end;
  267. procedure WriteHTMLFile(doc: TXMLDocument; var AFile: Text);
  268. begin
  269. f := @AFile;
  270. wrt := @Text_Write;
  271. wrtln := @Text_WriteLn;
  272. RootWriter(doc);
  273. end;
  274. procedure WriteHTMLFile(doc: TXMLDocument; var AStream: TStream);
  275. begin
  276. Stream := AStream;
  277. wrt := @Stream_Write;
  278. wrtln := @Stream_WriteLn;
  279. RootWriter(doc);
  280. end;
  281. procedure WriteHTML(Element: TDOMElement; const AFileName: String);
  282. begin
  283. Stream := TFileStream.Create(AFileName, fmCreate);
  284. wrt := @Stream_Write;
  285. wrtln := @Stream_WriteLn;
  286. InitWriter;
  287. WriteNode(Element);
  288. Stream.Free;
  289. end;
  290. procedure WriteHTML(Element: TDOMElement; var AFile: Text);
  291. begin
  292. f := @AFile;
  293. wrt := @Text_Write;
  294. wrtln := @Text_WriteLn;
  295. InitWriter;
  296. WriteNode(Element);
  297. end;
  298. procedure WriteHTML(Element: TDOMElement; var AStream: TStream);
  299. begin
  300. stream := AStream;
  301. wrt := @Stream_Write;
  302. wrtln := @Stream_WriteLn;
  303. InitWriter;
  304. WriteNode(Element);
  305. end;
  306. end.
  307. {
  308. $Log$
  309. Revision 1.2 2000-10-15 15:31:26 sg
  310. * Improved whitespace handling (entity references as first child of an
  311. element is now handled as indicator to stop the insertion of automatic
  312. linefeeds. Until now this was only the case with text nodes.)
  313. Revision 1.1 2000/10/03 20:33:22 sg
  314. * Added new Units "htmwrite" and "xhtml"
  315. }