[英]C# FlowDocument to HTML conversion
基本上,我有一個 RichTextBox,我想將它的格式化內容轉換為 HTML,以便它可以作為 email 發送。
我目前使用的方法根本沒有給出任何格式:
string message = new TextRange(messageTextBox.Document.ContentStart,
messageTextBox.Document.ContentEnd).Text;
所以我四處搜索,發現 這個,但是,它已經超過 5 年了,在評論中,一位 MSFT 用戶評論說它不再受支持 - "This sample has been removed from our sample set and is no longer supported"
,它生成的 HTML 的格式比現代 HTML 或 XHTML 更舊,后者會更好。
誰能告訴我如何將 RichTextBox 的格式化內容轉換為 HTML?
(因此,當發送 email 時,收件人會看到帶有格式的 email)
The general technique is to use a XamlWriter
to convert the FlowDocument
content to a stream of XML, and then to use an XSLT transform to convert the XML to HTML. 這不是一個很好的答案,但這是因為任何給定的 FlowDocument 都有大量可能的 HTML 表示。
例如,此轉換將每個頂級Section
轉換為div
,將每個Paragraph
轉換為p
,並將每個Run
轉換為span
,其 class 會告訴您它是否為斜體、粗體或下划線,或任何組合以上。 它對我編寫它的目的很有用,但稱它為有損轉換是輕描淡寫的:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl x">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="x:Section[not(parent::x:Section)]">
<div>
<xsl:apply-templates select="node()"/>
</div>
</xsl:template>
<xsl:template match="x:Section">
<xsl:apply-templates select="node()"/>
</xsl:template>
<xsl:template match="x:Paragraph">
<p>
<xsl:apply-templates select="node()"/>
</p>
</xsl:template>
<xsl:template match="x:Run">
<xsl:variable name="class">
<xsl:if test="@FontStyle='Italic'">
<xsl:text>i </xsl:text>
</xsl:if>
<xsl:if test="@FontWeight='Bold'">
<xsl:text>b </xsl:text>
</xsl:if>
<xsl:if test="contains(@TextDecorations, 'Underline')">
<xsl:text>u </xsl:text>
</xsl:if>
</xsl:variable>
<span>
<xsl:if test="normalize-space($class) != ''">
<xsl:attribute name="class">
<xsl:value-of select="normalize-space($class)"/>
</xsl:attribute>
</xsl:if>
<xsl:value-of select="text()"/>
</span>
</xsl:template>
</xsl:stylesheet>
這是我為進行轉換而編寫的值轉換器 - 請注意,為了使用值轉換器,您還必須修改並實現一個RichTextBox
版本,它將內容作為依賴屬性公開。 整個項目真的很痛苦。
public class FlowDocumentToHtmlConverter : IValueConverter
{
private static XslCompiledTransform ToHtmlTransform;
private static XslCompiledTransform ToXamlTransform;
public FlowDocumentToHtmlConverter()
{
if (ToHtmlTransform == null)
{
ToHtmlTransform = LoadTransformResource("Converters/FlowDocumentToXhtml.xslt");
}
if (ToXamlTransform == null)
{
ToXamlTransform = LoadTransformResource("Converters/XhtmlToFlowDocument.xslt");
}
}
private static XslCompiledTransform LoadTransformResource(string path)
{
Uri uri = new Uri(path, UriKind.Relative);
XmlReader xr = XmlReader.Create(Application.GetResourceStream(uri).Stream);
XslCompiledTransform xslt = new XslCompiledTransform();
xslt.Load(xr);
return xslt;
}
#region IValueConverter Members
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (!(value is FlowDocument))
{
return null;
}
if (targetType == typeof(FlowDocument))
{
return value;
}
if (targetType != typeof(string))
{
throw new InvalidOperationException(
"FlowDocumentToHtmlConverter can only convert back from a FlowDocument to a string.");
}
FlowDocument d = (FlowDocument)value;
using (MemoryStream ms = new MemoryStream())
{
// write XAML out to a MemoryStream
TextRange tr = new TextRange(
d.ContentStart,
d.ContentEnd);
tr.Save(ms, DataFormats.Xaml);
ms.Seek(0, SeekOrigin.Begin);
// transform the contents of the MemoryStream to HTML
StringBuilder sb = new StringBuilder();
using (StringWriter sw = new StringWriter(sb))
{
XmlWriterSettings xws = new XmlWriterSettings();
xws.OmitXmlDeclaration = true;
XmlReader xr = XmlReader.Create(ms);
XmlWriter xw = XmlWriter.Create(sw, xws);
ToHtmlTransform.Transform(xr, xw);
}
return sb.ToString();
}
}
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
{
return new FlowDocument();
}
if (value is FlowDocument)
{
return value;
}
if (targetType != typeof(FlowDocument))
{
throw new InvalidOperationException(
"FlowDocumentToHtmlConverter can only convert to a FlowDocument.");
}
if (!(value is string))
{
throw new InvalidOperationException(
"FlowDocumentToHtmlConverter can only convert from a string or FlowDocument.");
}
string s = (string)value;
FlowDocument d;
using (MemoryStream ms = new MemoryStream())
using (StringReader sr = new StringReader(s))
{
XmlWriterSettings xws = new XmlWriterSettings();
xws.OmitXmlDeclaration = true;
using (XmlReader xr = XmlReader.Create(sr))
using (XmlWriter xw = XmlWriter.Create(ms, xws))
{
ToXamlTransform.Transform(xr, xw);
}
ms.Seek(0, SeekOrigin.Begin);
d = XamlReader.Load(ms) as FlowDocument;
}
XamlWriter.Save(d, Console.Out);
return d;
}
#endregion
}
我認為上面的 XSLT 表的擴展版本可能會有所幫助。 不完美,但稍微全面一些。 擴展引用此答案的另一個答案的擴展。
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl x">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<!--<xsl:template match="x:Section[not(parent::x:Section)]">
<div>
<xsl:apply-templates select="node()"/>
</div>
</xsl:template>-->
<xsl:template match="x:Section[not(parent::x:Section)]">
<xsl:variable name="style">
<xsl:if test="@FontStyle='Italic'">
<xsl:text>font-style:italic;</xsl:text>
</xsl:if>
<xsl:if test="@FontWeight='Bold'">
<xsl:text>font-weight:bold;</xsl:text>
</xsl:if>
<xsl:if test="contains(@TextDecorations, 'Underline')">
<xsl:text>text-decoration:underline;</xsl:text>
</xsl:if>
<xsl:if test="@FontSize != ''">
<xsl:text>font-size:</xsl:text>
<xsl:value-of select="@FontSize" />
<xsl:text>pt;</xsl:text>
</xsl:if>
<xsl:if test="@FontFamily != ''">
<xsl:text>font-family:</xsl:text>
<xsl:value-of select="@FontFamily" />
<xsl:text>;</xsl:text>
</xsl:if>
<xsl:if test="@Foreground != ''">
<xsl:text>color:</xsl:text>
<xsl:value-of select="concat(substring(@Foreground, 1, 1), substring(@Foreground, 4))" />
<xsl:text>;</xsl:text>
</xsl:if>
<xsl:if test="@Foreground-Color != ''">
<xsl:text>color:</xsl:text>
<xsl:value-of select="@Foreground-Color"/>
<xsl:text>;</xsl:text>
</xsl:if>
</xsl:variable>
<div>
<xsl:if test="normalize-space($style) != ''">
<xsl:attribute name="style">
<xsl:value-of select="normalize-space($style)"/>
</xsl:attribute>
</xsl:if>
<xsl:value-of select="text()"/>
<xsl:apply-templates select="node()"/>
</div>
</xsl:template>
<xsl:template match="x:Section">
<xsl:apply-templates select="node()"/>
</xsl:template>
<xsl:template match="x:Paragraph">
<xsl:variable name="style">
<xsl:if test="@FontStyle='Italic'">
<xsl:text>font-style:italic;</xsl:text>
</xsl:if>
<xsl:if test="@FontWeight='Bold'">
<xsl:text>font-weight:bold;</xsl:text>
</xsl:if>
<xsl:if test="contains(@TextDecorations, 'Underline')">
<xsl:text>text-decoration:underline;</xsl:text>
</xsl:if>
<xsl:if test="@FontSize != ''">
<xsl:text>font-size:</xsl:text>
<xsl:value-of select="@FontSize" />
<xsl:text>pt;</xsl:text>
</xsl:if>
<xsl:if test="@FontFamily != ''">
<xsl:text>font-family:</xsl:text>
<xsl:value-of select="@FontFamily" />
<xsl:text>;</xsl:text>
</xsl:if>
<xsl:if test="@Foreground != ''">
<xsl:text>color:</xsl:text>
<xsl:value-of select="concat(substring(@Foreground, 1, 1), substring(@Foreground, 4))" />
<xsl:text>;</xsl:text>
</xsl:if>
<xsl:if test="@Foreground-Color != ''">
<xsl:text>color:</xsl:text>
<xsl:value-of select="@Foreground-Color"/>
<xsl:text>;</xsl:text>
</xsl:if>
</xsl:variable>
<p>
<xsl:if test="normalize-space($style) != ''">
<xsl:attribute name="style">
<xsl:value-of select="normalize-space($style)"/>
</xsl:attribute>
</xsl:if>
<xsl:value-of select="text()"/>
<xsl:apply-templates select="node()"/>
</p>
</xsl:template>
<xsl:template match="x:Span">
<xsl:variable name="style">
<xsl:if test="@FontStyle='Italic'">
<xsl:text>font-style:italic;</xsl:text>
</xsl:if>
<xsl:if test="@FontWeight='Bold'">
<xsl:text>font-weight:bold;</xsl:text>
</xsl:if>
<xsl:if test="contains(@TextDecorations, 'Underline')">
<xsl:text>text-decoration:underline;</xsl:text>
</xsl:if>
<xsl:if test="@FontSize != ''">
<xsl:text>font-size:</xsl:text>
<xsl:value-of select="@FontSize" />
<xsl:text>pt;</xsl:text>
</xsl:if>
<xsl:if test="@FontFamily != ''">
<xsl:text>font-family:</xsl:text>
<xsl:value-of select="@FontFamily" />
<xsl:text>;</xsl:text>
</xsl:if>
<xsl:if test="@Foreground != ''">
<xsl:text>color:</xsl:text>
<xsl:value-of select="concat(substring(@Foreground, 1, 1), substring(@Foreground, 4))" />
<xsl:text>;</xsl:text>
</xsl:if>
<xsl:if test="@Foreground-Color != ''">
<xsl:text>color:</xsl:text>
<xsl:value-of select="@Foreground-Color"/>
<xsl:text>;</xsl:text>
</xsl:if>
</xsl:variable>
<span>
<xsl:if test="normalize-space($style) != ''">
<xsl:attribute name="style">
<xsl:value-of select="normalize-space($style)"/>
</xsl:attribute>
</xsl:if>
<xsl:value-of select="text()"/>
<xsl:apply-templates select="node()"/>
</span>
</xsl:template>
<xsl:template match="x:Run">
<xsl:variable name="style">
<xsl:if test="@FontStyle='Italic'">
<xsl:text>font-style:italic;</xsl:text>
</xsl:if>
<xsl:if test="@FontWeight='Bold'">
<xsl:text>font-weight:bold;</xsl:text>
</xsl:if>
<xsl:if test="contains(@TextDecorations, 'Underline')">
<xsl:text>text-decoration:underline;</xsl:text>
</xsl:if>
<xsl:if test="@FontSize != ''">
<xsl:text>font-size:</xsl:text>
<xsl:value-of select="@FontSize" />
<xsl:text>pt;</xsl:text>
</xsl:if>
<xsl:if test="@FontFamily != ''">
<xsl:text>font-family:</xsl:text>
<xsl:value-of select="@FontFamily" />
<xsl:text>;</xsl:text>
</xsl:if>
<xsl:if test="@Foreground != ''">
<xsl:text>color:</xsl:text>
<xsl:value-of select="concat(substring(@Foreground, 1, 1), substring(@Foreground, 4))" />
<xsl:text>;</xsl:text>
</xsl:if>
<xsl:if test="@Foreground-Color != ''">
<xsl:text>color:</xsl:text>
<xsl:value-of select="@Foreground-Color"/>
<xsl:text>;</xsl:text>
</xsl:if>
</xsl:variable>
<span>
<xsl:if test="normalize-space($style) != ''">
<xsl:attribute name="style">
<xsl:value-of select="normalize-space($style)"/>
</xsl:attribute>
</xsl:if>
<xsl:value-of select="text()"/>
<xsl:apply-templates select="node()"/>
</span>
</xsl:template>
</xsl:stylesheet>
對於那些正在尋找.Net Core (APS.Net Core) 解決方案的人 - nuget MarkupConverter 為我解決了問題。 添加依賴引用
<PackageReference Include="MarkupConverter" Version="1.0.6" />
然后使用它
MarkupConverter.MarkupConverter markupConverter = new MarkupConverter.MarkupConverter();
try
{
message = markupConverter.ConvertXamlToHtml(message);
}
catch (Exception ex)
{
_logger.LogError(ex, $"Failed to parse flowdocument");
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.