{ $Id$ This file is part of the Free Component Library HTML writing routines Copyright (c) 2000 by Areca Systems GmbH / Sebastian Guenther, sg@freepascal.org See the file COPYING.FPC, included in this distribution, for details about the copyright. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. **********************************************************************} unit HTMWrite; {$MODE objfpc} {$H+} interface uses Classes, DOM; procedure WriteHTMLFile(doc: TXMLDocument; const AFileName: String); procedure WriteHTMLFile(doc: TXMLDocument; var AFile: Text); procedure WriteHTMLFile(doc: TXMLDocument; var AStream: TStream); procedure WriteHTML(Element: TDOMElement; const AFileName: String); procedure WriteHTML(Element: TDOMElement; var AFile: Text); procedure WriteHTML(Element: TDOMElement; var AStream: TStream); // =================================================================== implementation uses SysUtils; // ------------------------------------------------------------------- // Writers for the different node types // ------------------------------------------------------------------- procedure WriteElement(node: TDOMNode); forward; procedure WriteAttribute(node: TDOMNode); forward; procedure WriteText(node: TDOMNode); forward; procedure WriteCDATA(node: TDOMNode); forward; procedure WriteEntityRef(node: TDOMNode); forward; procedure WriteEntity(node: TDOMNode); forward; procedure WritePI(node: TDOMNode); forward; procedure WriteComment(node: TDOMNode); forward; procedure WriteDocument(node: TDOMNode); forward; procedure WriteDocumentType(node: TDOMNode); forward; procedure WriteDocumentFragment(node: TDOMNode); forward; procedure WriteNotation(node: TDOMNode); forward; type TWriteNodeProc = procedure(node: TDOMNode); const WriteProcs: array[ELEMENT_NODE..NOTATION_NODE] of TWriteNodeProc = (WriteElement, WriteAttribute, WriteText, WriteCDATA, WriteEntityRef, WriteEntity, WritePI, WriteComment, WriteDocument, WriteDocumentType, WriteDocumentFragment, WriteNotation); procedure WriteNode(node: TDOMNode); begin WriteProcs[node.NodeType](node); end; // ------------------------------------------------------------------- // Text file and TStream support // ------------------------------------------------------------------- type TOutputProc = procedure(s: String); var f: ^Text; stream: TStream; wrt, wrtln: TOutputProc; InsideTextNode: Boolean; procedure Text_Write(s: String); begin Write(f^, s); end; procedure Text_WriteLn(s: String); begin WriteLn(f^, s); end; procedure Stream_Write(s: String); begin if Length(s) > 0 then stream.Write(s[1], Length(s)); end; procedure Stream_WriteLn(s: String); begin if Length(s) > 0 then stream.Write(s[1], Length(s)); stream.WriteByte(10); end; // ------------------------------------------------------------------- // String conversion // ------------------------------------------------------------------- type TCharacters = set of Char; TSpecialCharCallback = procedure(c: Char); const AttrSpecialChars = ['"', '&']; TextSpecialChars = ['<', '>', '&']; procedure ConvWrite(const s: String; const SpecialChars: TCharacters; const SpecialCharCallback: TSpecialCharCallback); var StartPos, EndPos: Integer; begin StartPos := 1; EndPos := 1; while EndPos <= Length(s) do begin if s[EndPos] in SpecialChars then begin wrt(Copy(s, StartPos, EndPos - StartPos)); SpecialCharCallback(s[EndPos]); StartPos := EndPos + 1; end; Inc(EndPos); end; if EndPos > StartPos then wrt(Copy(s, StartPos, EndPos - StartPos)); end; procedure AttrSpecialCharCallback(c: Char); begin if c = '"' then wrt('"') else if c = '&' then wrt('&') else wrt(c); end; procedure TextnodeSpecialCharCallback(c: Char); begin if c = '<' then wrt('<') else if c = '>' then wrt('>') else if c = '&' then wrt('&') else wrt(c); end; function IsTextNode(Node: TDOMNode): Boolean; begin Result := Node.NodeType in [TEXT_NODE, ENTITY_REFERENCE_NODE]; end; // ------------------------------------------------------------------- // Node writers implementations // ------------------------------------------------------------------- procedure WriteElement(node: TDOMNode); var i: Integer; attr, child: TDOMNode; SavedInsideTextNode: Boolean; s: String; begin wrt('<' + node.NodeName); for i := 0 to node.Attributes.Length - 1 do begin attr := node.Attributes.Item[i]; wrt(' ' + attr.NodeName + '='); s := attr.NodeValue; // !!!: Replace special characters in "s" such as '&', '<', '>' wrt('"'); ConvWrite(s, AttrSpecialChars, @AttrSpecialCharCallback); wrt('"'); end; Child := node.FirstChild; if Child = nil then if InsideTextNode then wrt(' />') else wrtln(' />') else begin SavedInsideTextNode := InsideTextNode; if InsideTextNode or IsTextNode(Child) then wrt('>') else wrtln('>'); repeat if IsTextNode(Child) then InsideTextNode := True; WriteNode(Child); Child := Child.NextSibling; until child = nil; InsideTextNode := SavedInsideTextNode; s := ''; if InsideTextNode then wrt(s) else wrtln(s); end; end; procedure WriteAttribute(node: TDOMNode); begin WriteLn('WriteAttribute'); end; procedure WriteText(node: TDOMNode); begin ConvWrite(node.NodeValue, TextSpecialChars, @TextnodeSpecialCharCallback); end; procedure WriteCDATA(node: TDOMNode); begin if InsideTextNode then wrt('') else wrtln('') end; procedure WriteEntityRef(node: TDOMNode); begin wrt('&' + node.NodeName + ';'); end; procedure WriteEntity(node: TDOMNode); begin WriteLn('WriteEntity'); end; procedure WritePI(node: TDOMNode); var s: String; begin s := ''; if InsideTextNode then wrt(s) else wrtln( s); end; procedure WriteComment(node: TDOMNode); begin if InsideTextNode then wrt('') else wrtln('') end; procedure WriteDocument(node: TDOMNode); begin WriteLn('WriteDocument'); end; procedure WriteDocumentType(node: TDOMNode); begin WriteLn('WriteDocumentType'); end; procedure WriteDocumentFragment(node: TDOMNode); begin WriteLn('WriteDocumentFragment'); end; procedure WriteNotation(node: TDOMNode); begin WriteLn('WriteNotation'); end; procedure InitWriter; begin InsideTextNode := False; end; procedure RootWriter(doc: TXMLDocument); var Child: TDOMNode; begin InitWriter; child := doc.FirstChild; while Assigned(Child) do begin WriteNode(Child); Child := Child.NextSibling; end; end; // ------------------------------------------------------------------- // Interface implementation // ------------------------------------------------------------------- procedure WriteHTMLFile(doc: TXMLDocument; const AFileName: String); begin Stream := TFileStream.Create(AFileName, fmCreate); wrt := @Stream_Write; wrtln := @Stream_WriteLn; RootWriter(doc); Stream.Free; end; procedure WriteHTMLFile(doc: TXMLDocument; var AFile: Text); begin f := @AFile; wrt := @Text_Write; wrtln := @Text_WriteLn; RootWriter(doc); end; procedure WriteHTMLFile(doc: TXMLDocument; var AStream: TStream); begin Stream := AStream; wrt := @Stream_Write; wrtln := @Stream_WriteLn; RootWriter(doc); end; procedure WriteHTML(Element: TDOMElement; const AFileName: String); begin Stream := TFileStream.Create(AFileName, fmCreate); wrt := @Stream_Write; wrtln := @Stream_WriteLn; InitWriter; WriteNode(Element); Stream.Free; end; procedure WriteHTML(Element: TDOMElement; var AFile: Text); begin f := @AFile; wrt := @Text_Write; wrtln := @Text_WriteLn; InitWriter; WriteNode(Element); end; procedure WriteHTML(Element: TDOMElement; var AStream: TStream); begin stream := AStream; wrt := @Stream_Write; wrtln := @Stream_WriteLn; InitWriter; WriteNode(Element); end; end. { $Log$ Revision 1.2 2000-10-15 15:31:26 sg * Improved whitespace handling (entity references as first child of an element is now handled as indicator to stop the insertion of automatic linefeeds. Until now this was only the case with text nodes.) Revision 1.1 2000/10/03 20:33:22 sg * Added new Units "htmwrite" and "xhtml" }