Use this step-by-step guide to generate Rich Text Format
(RTF) from Extensible Markup Language (XML) by using Visual Basic .NET.
Rich Text Format is a text-based format that encodes formatted text,
document layout, and graphics. It is commonly used with Microsoft Word. Because
RTF is text-based, it can be easily generated with code. If you have XML data
that you want to display in Word as a catalog/list or mail merge type document,
then transforming your XML data into an RTF stream might be an ideal solution
for you. And, in fact, if you are developing a solution where you generate
documents on a Web server, then generating those documents using a text-based
format, such as HTML or RTF, is preferred over using server-side Automation of
Word.
This article provides sample code, with step-by-step
instructions, to transform XML into RTF for display in Word by using several
approaches:
Save the RTF to a file and open it in Word.
Transfer the RTF to Word by using the Windows
Clipboard.
Stream the RTF to Word hosted in Microsoft Internet
Explorer from an ASP.NET Web Application.
The Rich Text Format Specification
The Rich Text Format (RTF) specification is a public
specification to generate RTF-compatible text files. You can use the
documentation for the specification at the following Microsoft Developer
Network (MSDN) Web site as a resource to help you build your own RTF files.
However, the specification is provided "as-is", and no support is provided by
Microsoft Technical Support for the specification. Click the following links
for the RTF Specifications:
You can take valid XML from any source and transform it to RTF.
The following procedure illustrates how you can transform custom XML into RTF
either saved to a file or copied to the clipboard.
Create a new Visual Basic .NET Windows Application. Form1 is created for you.
Add two button controls to Form1.
On the View menu, click Code.
Insert the following at the top of the code window before
the Form1 Class implementation:
Add the following code to the Form1 Class implementation
(before End Class):
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Button1.Text = "To File"
Button2.Text = "To Clipboard"
End Sub
Private Sub ButtonsClick(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles Button1.Click, Button2.Click
Dim sPath As String = Directory.GetParent(Directory.GetCurrentDirectory()).ToString
' Open the XML file.
Dim xmlDoc As New XmlDocument()
xmlDoc.Load(sPath & "\Dictionary.xml")
' Open the XSL file.
Dim xslDoc As New XslTransform()
xslDoc.Load(sPath & "\Dictionary.xslt")
Select Case sender.name
Case "Button1"
' Transform the XSL and save it to file.
Dim TWrtr As New XmlTextWriter(sPath & "\Dictionary.RTF", System.Text.Encoding.Default)
xslDoc.Transform(xmlDoc, Nothing, TWrtr, Nothing)
TWrtr.Close()
MsgBox("Transformed RTF saved to " & sPath & "\Dictionary.RTF")
Case "Button2"
' Transform the XSL and copy it to the clipboard.
Dim SWrtr As New StringWriter()
xslDoc.Transform(xmlDoc, Nothing, SWrtr, Nothing)
Dim datObj As New DataObject(DataFormats.Rtf, SWrtr)
Clipboard.SetDataObject(datObj)
SWrtr.Close()
MsgBox("Transformed RTF copied to the clipboard.")
End Select
End Sub
Add an XML file to your project:
On the Project menu, click Add New Item.
From the list of templates, click XML File.
Type the name Dictionary.xml and
then click Open.
Append the following to the contents of Dictionary.xml:
<Dictionary>
<Entries>
<Entry>
<Word Type="1">Energetic</Word>
<Definition>Having, exerting, or displaying energy</Definition>
</Entry>
<Entry>
<Word Type="1">Happy</Word>
<Definition>Enjoying, displaying, or characterized by pleasure or joy</Definition>
</Entry>
<Entry>
<Word Type="2">Emotion</Word>
<Definition>A complex, strong subjective response</Definition>
</Entry>
</Entries>
</Dictionary>
Add an XSLT file to your project:
On the Project menu, click Add New Item.
From the list of templates, click XSLT File.
Type the name Dictionary.xslt
and then click Open.
Replace the contents of Dictionary.xslt with the
following:
Click To File to save the transformed XML to a file (Dictionary.rtf). You can
open the RTF file in Word to examine the results of the
transformation.
Click To Clipboard to copy the transformed XML to the Windows clipboard. You can
then paste the clipboard contents into a new or existing Word document to see
the results.
Transform a DataSet Into RTF
Visual Basic .NET allows to you to easily do transformations on
datasets. This procedure shows you how you can take related data from the
sample Northwind database and transform it to RTF. Two different
transformations are demonstrated: a simple RTF document that lists Customer
contact information and a somewhat more complex RTF document that displays
order information for customers in a mail merge-type format.
Start a new Visual Basic ASP.NET Web Application and save it to http://localhost/RTFDemo.
NOTE: The following code assumes you have SQL Server installed on the
localhost. If you have to use another computer, change the Data Source member
of the connection string accordingly.
Private Sub ButtonsClick(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles Button1.Click, Button2.Click
' Connect to the data source.
Dim nwindConn As SqlConnection = New SqlConnection( _
"Data Source=localhost;Initial Catalog=Northwind;Integrated Security=SSPI")
nwindConn.Open()
' Build a dataset based on whether you requested to view a list of
' orders or a list of contacts.
Dim ds As DataSet
Dim sXSL As String
Select Case (sender.id)
Case "Button1"
ds = New DataSet("Contacts")
Dim ContactsDA As SqlDataAdapter = New SqlDataAdapter("SELECT * FROM Customers", nwindConn)
ContactsDA.Fill(ds, "Customers")
' XSLT to use for transforming this dataset.
sXSL = "Contacts.xslt"
Case "Button2"
ds = New DataSet("CustomerOrders")
Dim custDA As SqlDataAdapter = New SqlDataAdapter("SELECT CustomerID, CompanyName, " & _
"Address, City, Region, PostalCode, Country FROM Customers", nwindConn)
custDA.Fill(ds, "Customers")
Dim ordersDA As SqlDataAdapter = New SqlDataAdapter("SELECT OrderID, CustomerID, Freight " & _
"FROM Orders", nwindConn)
ordersDA.Fill(ds, "Orders")
Dim ordersdetailDA As SqlDataAdapter = New SqlDataAdapter( _
"SELECT [Order Details].OrderID, Products.ProductName, [Order Details].Quantity, " & _
"[Order Details].[UnitPrice]*[Quantity]*(1-[Discount]) AS ItemTotal " & _
"FROM Products INNER JOIN [Order Details] ON Products.ProductID = [Order Details].ProductID " _
, nwindConn)
ordersdetailDA.Fill(ds, "OrderDetails")
nwindConn.Close()
ds.Relations.Add("CustOrders", _
ds.Tables("Customers").Columns("CustomerID"), _
ds.Tables("Orders").Columns("CustomerID")).Nested = True
ds.Relations.Add("OrdersToOrdersDetail", _
ds.Tables("Orders").Columns("OrderID"), _
ds.Tables("OrderDetails").Columns("OrderID")).Nested = True
' XSLT to use for transforming this dataset.
sXSL = "CustOrders.xslt"
End Select
' Close the connection to the data source.
nwindConn.Close()
' Transform the dataset by using the appropriate stylesheet.
Dim xmlDoc As XmlDataDocument = New XmlDataDocument(ds)
Dim xslTran As XslTransform = New XslTransform()
xslTran.Load(Server.MapPath(sXSL))
' Stream the results of the transformation to Word.
Response.ContentType = "application/msword"
Response.Charset = ""
Response.ContentEncoding = System.Text.Encoding.Default
xslTran.Transform(xmlDoc, Nothing, Response.Output)
End Sub
Add the following lines of code at the top of
WebForm1.aspx.vb, before the WebForm1 class implementation:
On the Project menu, click Add New Item. From the list of templates, click XSLT File, name the file CustOrders.xslt, and then click Open.
Replace the contents of CustOrders.xslt with the following:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:user="urn:my-scripts" >
<xsl:output method="text"/>
<msxsl:script language="VB" implements-prefix="user">
Dim CustomerTotal as Double = 0
Dim OrderSubtotal as Double = 0
Function AddToOrderSubtotal(amt)
amt.MoveNext
OrderSubtotal = OrderSubtotal + System.Convert.ToDouble(amt.Current.Value)
End Function
Function GetOrderSubtotal
GetOrderSubtotal = OrderSubtotal
End Function
Function GetCustomerTotal
GetCustomerTotal = CustomerTotal
CustomerTotal = 0
End Function
Function GetOrderTotal(freight)
freight.MoveNext
nFreight = System.Convert.ToDouble(freight.Current.Value)
GetOrderTotal = nFreight + OrderSubtotal
CustomerTotal = nFreight + OrderSubtotal + CustomerTotal
OrderSubtotal = 0
End Function
</msxsl:script>
<xsl:template match="CustomerOrders">
<xsl:text>{\rtf1</xsl:text>
<xsl:text>{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;
\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;
\red255\green255\blue255;\red221\green221\blue221;}</xsl:text>
<xsl:text>{\info{\title Sample RTF Document}{\author Microsoft Developer Support}}</xsl:text>
<xsl:text>{\header\pard\qc{\fs50 ASP-Generated RTF\par}{\fs18\chdate\par}\par\par}</xsl:text>
<xsl:text>{\footer\pard\qc\brdrt\brdrs\brdrw10\brsp100\fs18 Page {\field{\*\fldinst PAGE}</xsl:text>
<xsl:text>{\fldrslt }} of {\field{\*\fldinst NUMPAGES}{\fldrslt 1}} \par}</xsl:text>
<xsl:apply-templates select="Customers"/>
<xsl:text>}</xsl:text>
</xsl:template>
<xsl:template match="Customers">
<xsl:text>\par\pard\fs20\cf2\qr\b </xsl:text><xsl:value-of select="CustomerID"/><xsl:text>\cf0\b0</xsl:text>
<xsl:text>\par\pard </xsl:text><xsl:value-of select="CompanyName"/>
<xsl:text>\par </xsl:text><xsl:value-of select="Address"/>
<xsl:text>\par </xsl:text><xsl:value-of select="City"/>
<xsl:text>, </xsl:text><xsl:value-of select="Region"/>
<xsl:text> </xsl:text><xsl:value-of select="PostalCode"/>
<xsl:text>\par </xsl:text><xsl:value-of select="Country"/>
<xsl:text>\par\par</xsl:text>
<xsl:apply-templates select="Orders"/>
<xsl:text>\trowd\cellx7000\cellx9000\pard\intbl\ql\b\cbpat1 </xsl:text>
<xsl:text>Order Total for the Current Period:\cell </xsl:text>
<xsl:text>\qr</xsl:text>
<xsl:variable name="CustTtl" select="user:GetCustomerTotal()"/>
<xsl:value-of select="format-number($CustTtl,'$###0.00')"/>
<xsl:text>\cell</xsl:text>
<xsl:text>\pard\intbl\row</xsl:text>
<xsl:text>\pard\par\pard</xsl:text>
<xsl:text>\pard\plain\fs18\cf6\qc</xsl:text>
<xsl:choose>
<xsl:when test="$CustTtl = 0">
<xsl:text>\b We've missed hearing from you!\b0 </xsl:text>
<xsl:text> At your convenience, please call your personal sales representative </xsl:text>
<xsl:text>so that we may discuss our specials for new and returning customers!</xsl:text>
</xsl:when>
<xsl:when test="$CustTtl > 2000">
<xsl:text>\b Congratulations!\b0 Your purchases for this period qualify you for a \b 20%\b0 </xsl:text>
<xsl:text> discount on one of your next orders. To take advantage of this offer, provide </xsl:text>
<xsl:text>the coupon code ABC123XYZ when placing your order.</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text> We value your patronage with Northwind Traders and would love to hear from you. </xsl:text>
<xsl:text>If you have any questions about our upcoming line of products or if you want </xsl:text>
<xsl:text>a catalog for the coming season, call 1-888-000-000.</xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:text>\par\pard</xsl:text>
<xsl:text>\par \page</xsl:text>
</xsl:template>
<xsl:template match="Orders">
<xsl:text>\trowd\cellx9000\pard\intbl\cbpat9</xsl:text>
<xsl:text>\ql\b </xsl:text><xsl:value-of select="OrderID"/><xsl:text>\b0\cell </xsl:text>
<xsl:text>\pard\intbl\row</xsl:text>
<xsl:apply-templates select="OrderDetails"/>
<xsl:text>\trowd\cellx7000\cellx9000\pard\intbl</xsl:text>
<xsl:text>\qr Subtotal:\cell </xsl:text>
<xsl:value-of select="format-number(user:GetOrderSubtotal(),'$###0.00')"/><xsl:text>\cell</xsl:text>
<xsl:text>\pard\intbl\row</xsl:text>
<xsl:text>\trowd\cellx7000\cellx9000\pard\intbl</xsl:text>
<xsl:text>\qr Freight:\cell </xsl:text>
<xsl:value-of select="format-number(Freight,'$###0.00')"/><xsl:text>\cell</xsl:text>
<xsl:text>\pard\intbl\row</xsl:text>
<xsl:text>\trowd\cellx7000\cellx9000\pard\intbl</xsl:text>
<xsl:text>\qr Total:\cell </xsl:text>
<xsl:value-of select="format-number(user:GetOrderTotal(Freight), '$###0.00')"/><xsl:text>\cell</xsl:text>
<xsl:text>\pard\intbl\row</xsl:text>
<xsl:text>\trowd\cellx9000\pard\intbl \cell\pard\intbl\row</xsl:text>
</xsl:template>
<xsl:template match="OrderDetails">
<xsl:text>\trowd\cellx5000\cellx7000\cellx9000\pard\intbl\ql </xsl:text>
<xsl:value-of select="ProductName"/><xsl:text>\cell </xsl:text>
<xsl:text>\qc </xsl:text><xsl:value-of select="Quantity"/><xsl:text>\cell </xsl:text>
<xsl:text>\qr </xsl:text>
<xsl:value-of select="format-number(ItemTotal,'$###0.00')"/><xsl:text>\cell</xsl:text>
<xsl:variable name="RunTotal" select="user:AddToOrderSubtotal(ItemTotal)"/>
<xsl:text>\pard\intbl\row</xsl:text>
</xsl:template>
</xsl:stylesheet>
On the Build menu, click Build Solution.
Start Internet Explorer and browse to
http://localhost/RTFDemo/Webform1.aspx.
Click View Contact Information to view the first XML transformation to RTF in Word.
Click Back in Internet Explorer.
Click View Customer Orders to view the second XML transformation to RTF in Word.
RTF Troubleshooting Tips
As written, the sample code in the
Transform a DataSet Into RTF
section, streams RTF directly to the browser. Alternatively, you can save the
RTF to a file and redirect to the saved file. To do this, replace these lines
of code in the sample
Dim writer As XmlTextWriter = New XmlTextWriter( _
Server.MapPath("Results.doc"), System.Text.Encoding.Default)
xslTran.Transform(xmlDoc, Nothing, writer)
writer.Close()
Response.Redirect("Results.doc")
Storing the RTF to a file in this manner allows you to easily examine
the structure of the RTF in the file by using any text editor, such as Notepad.
Storing the RTF to a file can be a helpful troubleshooting technique if the XSL
transformation does not produce the results you expect.
When transforming to RTF, be aware of how you present
whitespace and carriage returns in your stylesheet because that can affect how
Word interprets your RTF. Both code samples in this article use the <xsl:text> element because it forces any white space information in it to be
retained.
Use <xsl:output method="text"> in your stylesheet to make sure that your XML is transformed to
text (rather than XML, which is the default output method). If you do not
specify text as the output method, XML processing instructions may be added to
the file. This can prevent Word from correctly interpeting the text as
RTF.
For additional information about
server-side Automation of Microsoft Word and other Office applications, click
the article number below to view the article in the Microsoft Knowledge Base:
HOWTO: Paste RichText Formatted String into Word with Visual Basic Automation
For additional information about transforming XML by using Visual
Basic .NET, click the article number below to view the article in the Microsoft
Knowledge Base: