📄 xmltranslator.cs
字号:
using System;
using System.Xml;
using System.Collections;
using System.Drawing;
namespace RichTextBoxSupportsXHTML
{
/// <summary>
/// This class extends XmlDocument. It can hold a DOM of any XML document or fragment,
/// which can be used to translate the XML into other forms. Currently, this class
/// only supports translation from XML to RTF (Rich Text Format).
/// </summary>
public class XmlTranslator : XmlDocument
{
private RtfDocument rtfDocument;
private XmlTextReader reader;
private ArrayList errors = new ArrayList();
/// <summary>
/// Any errors encountered during translation will be contained
/// in this ArrayList of strings.
/// </summary>
public ArrayList Errors
{
get
{
return errors;
}
}
public XmlTranslator() : base() { }
/// <summary>
/// Passing a string of XML code to be translated creates a DOM of the Xml fragment.
/// </summary>
/// <param name="xmlText">XML code to be translated.</param>
public XmlTranslator(string xmlText) : base()
{
try
{
// All XML code except that contained within the body element is removed.
xmlText = TrimBody(xmlText);
// A space is added after all carriage returns to prevent word squishes
// across line wraps from the input string.
xmlText = xmlText.Replace("\n", " \n");
// In case the input string is an XML fragment that contains multiple
// roots, an additional element is placed around the input string to serve
// as the undisputed root. Otherwise, the DOM creation would fail.
reader = new XmlTextReader(
new System.IO.StringReader("<XmlTranslator_root>" + xmlText + "</XmlTranslator_root>"));
// All whitespace is preserved.
reader.WhitespaceHandling = WhitespaceHandling.All;
this.Load(reader);
}
catch (Exception e)
{
// If the XML is not well-formed, there may be an exception thrown
// during creation of the DOM. The DOM will be empty and an error
// will be added to the Errors property.
errors.Add("Error creating DOM. " + e.Message);
}
}
/// <summary>
/// If there is a body element, then all XML code outside of it is removed.
/// </summary>
/// <param name="xmlText">XML code to be trimmed.</param>
/// <returns></returns>
public string TrimBody(string xmlText)
{
string lower = xmlText.ToLower();
int bodyStart = lower.IndexOf("<body");
if (bodyStart > 0)
{
xmlText = xmlText.Substring(bodyStart);
}
lower = xmlText.ToLower();
int bodyEnd = lower.LastIndexOf("</body>") + 7;
if (bodyEnd > 7)
{
xmlText = xmlText.Substring(0, bodyEnd);
}
return xmlText;
}
/// <summary>
/// Initiates translation from XML to RTF (Rich Text Format)
/// Returns an object representing a RTF document.
/// </summary>
/// <returns></returns>
public RtfDocument ToRtfDocument()
{
rtfDocument = new RtfDocument();
// Starting at the placeholding element XmlTranslator_root,
// the primary translation method is called recursively on all
// of the nodes in the XML DOM.
try
{
foreach (XmlNode child in this.DocumentElement.ChildNodes)
{
rtfDocument.AppendText("{\\pard ");
TranslateXmlNodeIntoRtfGroup(child, 3);
rtfDocument.AppendText("}");
}
}
catch (Exception e)
{
// Catch any other exceptions during translation.
errors.Add("Error during translation. " + e.Message);
}
return rtfDocument;
}
/// <summary>
/// The primary method responsible for translating XML into RTF code.
/// It recursively walks the XML DOM tree, building the RTF document
/// as it goes.
/// </summary>
/// <param name="x">The current node to be translated.</param>
/// <param name="baseFontSize">The XHTML font size (1-7) of the parent element.</param>
public void TranslateXmlNodeIntoRtfGroup(XmlNode x, int baseFontSize)
{
if (x != null)
{
// If a node of this type is reached, then its content should be added
// to the RTF document, and it has no children to recursively translate.
if (x.Name.ToLower().ToLower() == "#text")
{
rtfDocument.AppendText("{" + x.InnerText + "}");
}
// Otherwise this node is a tag of some sort, potentially with children
// that need to be translated. The supported tags and their attributes
// are deciphered, and the equivalent RTF control word(s) is(are) added
// to the RTF document.
else
{
bool eval = true; // Evaluate this node's children?
rtfDocument.AppendText("{"); // Begin new RTF group
if (x.Name.ToLower() == "body")
{
foreach (XmlAttribute attribute in x.Attributes)
{
if (attribute.Name.ToLower() == "text")
{
int index = 0;
index = rtfDocument.UseColor(TranslateXhtmlColorIntoColorFromArgb(attribute.Value));
rtfDocument.AppendText("\\cf" + index + " ");
}
}
}
else if (x.Name.ToLower() == "br")
{
rtfDocument.AppendText("{\\par}");
}
else if (x.Name.ToLower() == "h1")
{
rtfDocument.AppendText("\\b\\fs48\\par ");
baseFontSize = 6;
}
else if (x.Name.ToLower() == "h2")
{
rtfDocument.AppendText("\\b\\fs36\\par ");
baseFontSize = 5;
}
else if (x.Name.ToLower() == "h3")
{
rtfDocument.AppendText("\\b\\fs27\\par ");
baseFontSize = 4;
}
else if (x.Name.ToLower() == "h4")
{
rtfDocument.AppendText("\\b\\fs24\\par ");
baseFontSize = 3;
}
else if (x.Name.ToLower() == "h5")
{
rtfDocument.AppendText("\\b\\fs20\\par ");
baseFontSize = 2;
}
else if (x.Name.ToLower() == "h6")
{
rtfDocument.AppendText("\\b\\fs15\\par ");
baseFontSize = 1;
}
else if ((x.Name.ToLower() == "b") || (x.Name.ToLower() == "strong"))
{
rtfDocument.AppendText("\\b ");
}
else if ( (x.Name.ToLower() == "i")
|| (x.Name.ToLower() == "em")
|| (x.Name.ToLower() == "cite")
|| (x.Name.ToLower() == "dfn")
|| (x.Name.ToLower() == "var"))
{
rtfDocument.AppendText("\\i ");
}
else if (x.Name.ToLower() == "u")
{
rtfDocument.AppendText("\\ul ");
}
else if ((x.Name.ToLower() == "s") || (x.Name.ToLower() == "strike"))
{
rtfDocument.AppendText("\\strike ");
}
else if ( (x.Name.ToLower() == "tt")
|| (x.Name.ToLower() == "pre")
|| (x.Name.ToLower() == "code")
|| (x.Name.ToLower() == "samp"))
{
int index = rtfDocument.UseFont("Courier New");
rtfDocument.AppendText("\\f" + index + "\\fs20 ");
baseFontSize = 2;
}
else if (x.Name.ToLower() == "big")
{
baseFontSize++;
rtfDocument.AppendText("\\fs"
+ TranslateXhtmlFontSizeIntoRtfFontSize(baseFontSize) + " ");
}
else if (x.Name.ToLower() == "small")
{
baseFontSize--;
rtfDocument.AppendText("\\fs"
+ TranslateXhtmlFontSizeIntoRtfFontSize(baseFontSize) + " ");
}
else if (x.Name.ToLower() == "a")
{
int index = rtfDocument.UseColor(Color.FromArgb(0, 0, 255));
rtfDocument.AppendText("\\cf" + index + "\\ul ");
}
else if (x.Name.ToLower() == "ul")
{
rtfDocument.AppendText("\\pn\\pnlvlblt ");
}
else if (x.Name.ToLower() == "li")
{
rtfDocument.AppendText("\\pnlvlcont\\par\\pnlvlblt ");
}
else if (x.Name.ToLower() == "basefont")
{
foreach (XmlAttribute attribute in x.Attributes)
{
if (attribute.Name.ToLower() == "size")
{
baseFontSize = Int32.Parse(attribute.Value);
rtfDocument.AppendText("\\fs"
+ TranslateXhtmlFontSizeIntoRtfFontSize(baseFontSize));
}
}
}
else if (x.Name.ToLower() == "font")
{
foreach (XmlAttribute attribute in x.Attributes)
{
if (attribute.Name.ToLower() == "face")
{
int commaIndex = attribute.Value.IndexOf(',');
int index = 0;
if (commaIndex > 0)
{
index = rtfDocument.UseFont(attribute.Value.Substring(0, commaIndex));
}
else
{
index = rtfDocument.UseFont(attribute.Value);
}
rtfDocument.AppendText("\\f" + index + " ");
}
else if (attribute.Name.ToLower() == "size")
{
if (attribute.Value.StartsWith("+"))
{
baseFontSize += Int32.Parse(attribute.Value);
rtfDocument.AppendText("\\fs"
+ TranslateXhtmlFontSizeIntoRtfFontSize(baseFontSize) + " ");
}
else if (attribute.Value.StartsWith("-"))
{
baseFontSize -= Int32.Parse(attribute.Value);
rtfDocument.AppendText("\\fs"
+ TranslateXhtmlFontSizeIntoRtfFontSize(baseFontSize) + " ");
}
else
{
baseFontSize = Int32.Parse(attribute.Value);
rtfDocument.AppendText("\\fs"
+ TranslateXhtmlFontSizeIntoRtfFontSize(baseFontSize) + " ");
}
}
else if (attribute.Name.ToLower() == "color")
{
int index = 0;
index = rtfDocument.UseColor(TranslateXhtmlColorIntoColorFromArgb(attribute.Value));
rtfDocument.AppendText("\\cf" + index + " ");
}
}
}
// Head elements should not be found, but this is just for extra robustness.
// Comments should not be evaluated.
else if ((x.Name.ToLower() == "head")
|| (x.Name.ToLower() == "#comment"))
{
eval = false;
}
if (eval)
{
// All known and unknown tags may have the style attribute,
// which in turn may contain certain properties which will
// affect the format of descendent elements. This for loop
// parses the style attribute if one exists on the current node.
// token[0] == property name; token[1] == property value
foreach (XmlAttribute attribute in x.Attributes)
{
if (attribute.Name.ToLower() == "style")
{
char[] semi = {';'};
string[] tokens = attribute.Value.Split(semi);
for (int i = 0; i < tokens.Length; i++)
{
char[] colon = {':'};
string[] token = tokens[i].Split(colon);
if (token[0].ToLower().Trim() == "font-family")
{
int commaIndex = token[1].Trim().IndexOf(',');
int index = 0;
if (commaIndex > 0)
{
index = rtfDocument.UseFont(token[1].Substring(0, commaIndex).Trim());
}
else
{
index = rtfDocument.UseFont(token[1].Trim());
}
rtfDocument.AppendText("\\f" + index + " ");
}
else if (token[0].ToLower().Trim() == "font-size")
{
int rtfFontSize = TranslateStyleFontSizeIntoRtfFontSize(token[1].Trim(), baseFontSize);
baseFontSize = TranslateRtfFontSizeIntoXhtmlFontSize(rtfFontSize);
rtfDocument.AppendText("\\fs" + rtfFontSize + " ");
}
else if (token[0].ToLower().Trim() == "font-style")
{
if (token[1].ToLower().Trim() == "normal")
{
rtfDocument.AppendText("\\i0 ");
}
else if ((token[1].ToLower().Trim() == "italic")
|| (token[1].ToLower().Trim() == "oblique"))
{
rtfDocument.AppendText("\\i ");
}
}
else if (token[0].ToLower().Trim() == "font-weight")
{
if ((token[1].ToLower().Trim() == "normal")
|| (token[1].ToLower().Trim() == "lighter"))
{
rtfDocument.AppendText("\\b0 ");
}
else
{
rtfDocument.AppendText("\\b ");
}
}
else if (token[0].ToLower().Trim() == "text-align")
{
if (token[1].ToLower().Trim() == "right")
{
rtfDocument.AppendText("\\qr ");
}
else if (token[1].ToLower().Trim() == "center")
{
rtfDocument.AppendText("\\qc ");
}
else if (token[1].ToLower().Trim() == "justify")
{
rtfDocument.AppendText("\\qj ");
}
else
{
rtfDocument.AppendText("\\ql ");
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -