Using ASP.NET and Visual Basic .NET to Transform XML to RTF and Display in Word

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.

Summary: Learn different ways of converting XML data to RTF. XML is a flexible way of organizing and presenting data. RTF is a text-based format that encodes formatted text, document layout, and graphics. Converting XML data to RTF allows you to create useful documents easily and quickly from a wide variety of data sources. (15 printed pages)

Frank Rice, Microsoft Corporation

July 2004

Applies to: Microsoft Visual Basic .NET, Microsoft Office Word 2003, Microsoft Word 2002

Contents

  • Overview

  • The Rich Text Format Specification

  • Transform XML to RTF

  • Transform a DataSet Into RTF

  • RTF Troubleshooting Tips

  • Conclusion

  • Additional Resources

Overview

This article demonstrates how to generate Rich Text Format (RTF) from Extensible Markup Language (XML) by using Microsoft Visual Basic .NET. Rich Text Format is a text-based format that encodes formatted text, document layout, and graphics. It is commonly used with word processors such as Microsoft Office Word 2003. Because RTF is text-based, you can easily generate it with code. If you have XML data that you want to display in Word such as a catalog, list or mail-merge 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 code, with step-by-step instructions, to transform XML into RTF for display in Word by using various approaches:

  • Save the RTF to a file and open it in Word.

  • Transfer the RTF to Word by using the Clipboard.

  • Stream the RTF to Word hosted in Microsoft Internet Explorer from an ASP.NET Web Application using a simple and then a more complex transformation.

Note

While the RTF specification discussed in this article is a method commonly used for creating documents in Microsoft Office 2002 and later versions, for Microsoft Office 2003 Editions, the preferred method for creating Word documents from XML data is Microsoft WordML in conjunction with XSLT.

For more information, see the following:

The Rich Text Format Specification

The RTF specification is a public specification to generate RTF-compatible text files. You can use the Word 2002 Tool: Rich Text Format Specification as a resource to help you build your own RTF files.

Transform XML to RTF

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.

To transform custom XML into RTF either saved to a file or copied to the Clipboard

  1. Create a Visual Basic .NET Windows Application. Form1 is created for you.

  2. Add two button controls to Form1.

  3. On the View menu, click Code.

  4. Insert the following at the top of the code window before the Form1 Class implementation:

    Imports System.Xml
    Imports System.Xml.Xsl
    Imports System.IO
    
  5. 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 a file.
                    Dim TWrtr As New XmlTextWriter(sPath & 
                       "\Dictionary.RTF", System.Text.Encoding.Default)
                    xslDoc.Transform(xmlDoc, Nothing, TWrtr)
                    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)
                    Dim datObj As New DataObject(DataFormats.Rtf, SWrtr)
                    Clipboard.SetDataObject(datObj)
                    SWrtr.Close()
                    MsgBox("Transformed RTF copied to the Clipboard.")
            End Select
    
        End Sub
    
  6. Add an XML file to your project with the following steps:

    1. On the Project menu, click Add New Item.

    2. From the list of templates, click XML File.

    3. Type the name Dictionary.xml and then click Open.

    4. 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> 
      
  7. Add an XSLT file to your project with the following steps:

    1. On the Project menu, click Add New Item.

    2. From the list of templates, click XSLT File.

    3. Type the name Dictionary.xslt and then click Open.

    4. Replace the contents of Dictionary.xslt with the following:

      <?xml version="1.0" encoding="UTF-8" ?>
      <xsl:stylesheet version="1.0" xmlns:xsl=
          "http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="text"/>
      <xsl:template match="/">
      <xsl:text>{\rtf1</xsl:text>
      <xsl:for-each select="Dictionary/Entries/Entry">
      <xsl:text>\par\b </xsl:text>
      <xsl:value-of select="Word"/>
      <xsl:text>\b0\i  </xsl:text> 
      <xsl:if test="Word[@Type='1']">adj.</xsl:if>
      <xsl:if test="Word[@Type='2']">n.</xsl:if>
      <xsl:text>\i0\par </xsl:text>
      <xsl:value-of select="Definition"/>
      <xsl:text>\par</xsl:text>
      </xsl:for-each>
      <xsl:text>}</xsl:text>
      </xsl:template>
      </xsl:stylesheet>
      
  8. Press F5 to build and run the program.

  9. 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.

  10. Click To Clipboard to copy the transformed XML to the Clipboard. You can then paste the Clipboard contents into a new or existing Word document to see the results. For example, start Word, click into the document to place the cursor, click Edit, and then click Paste.

Transform a DataSet Into RTF

Visual Basic .NET allows you to transform DataSet objects. DataSet objects generally contain a subset of the data from the data source, similar to an in-memory relational database.

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.

To transform a DataSet into RTF

  1. Create a Visual Basic ASP.NET Web Application and save it to http://localhost/RTFDemo. WebForm1 is created for you.

  2. Add two Button controls to WebForm1.

  3. On the View menu, click Code.

  4. Add the following code to the Page_Load function:

    Button1.Text = "View Contact Information"
    Button2.Text = "View Customer Orders"
    
  5. Add the following function to the WebForm1 class.

    Note

    The following code assumes you have Microsoft SQL Server installed on the local host. 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
    
  6. Add the following lines of code at the top of WebForm1.aspx.vb, before the WebForm1 class implementation:

    Imports System.Data.SqlClient
    Imports System.Xml
    Imports System.Xml.Xsl
    
  7. On the Project menu, click Add New Item.

  8. From the list of templates, click XSLT File, name the file Contacts.xslt, and then click Open.

  9. Replace the contents of Contacts.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"/>
    <xsl:template match="/">
    <xsl:text>{\rtf1</xsl:text>
    <xsl:text>{\fonttbl{\f0\froman\fcharset0\fprq2 Times 
        New Roman;}{\f1\fswiss\fcharset0\fprq2 Arial;}}</xsl:text>
    <xsl:text>{\header\pard\fs50 My Customer Contacts}</xsl:text>
    <xsl:text>{\footer\pard\fs18 Page {\field{\*\fldinst PAGE}</xsl:text>
    <xsl:text>{\fldrslt }} of {\field{\*\fldinst NUMPAGES}{\fldrslt 1}} 
         \par}</xsl:text>
    <xsl:text>\f1\fs20</xsl:text>
        <xsl:for-each select="Contacts/Customers">
    <xsl:text>\par\b </xsl:text><xsl:value-of 
       select="CustomerID"/><xsl:text>\b0</xsl:text>
    <xsl:text>\par </xsl:text><xsl:value-of select="CompanyName"/>
    <xsl:text>\par </xsl:text><xsl:value-of select="ContactName"/>
    <xsl:text>\par </xsl:text><xsl:value-of select="Phone"/>
    <xsl:text>\par</xsl:text>
        </xsl:for-each>
    <xsl:text>}</xsl:text>
    </xsl:template>
    <xsl:template match="Customers">
        <xsl:text>\par\b </xsl:text><xsl:value-of select="CustomerID"/><xsl:text>\b0</xsl:text>
        <xsl:text>\par </xsl:text><xsl:value-of select="CompanyName"/>
        <xsl:text>\par </xsl:text><xsl:value-of select="ContactName"/>
        <xsl:text>\par </xsl:text><xsl:value-of select="Phone"/>
        <xsl:text>\par</xsl:text>
    </xsl:template>
    </xsl:stylesheet>
    
  10. 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.

  11. 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>
    
  12. On the Build menu, click Build Solution.

  13. Start Internet Explorer and browse to http://localhost/RTFDemo/Webform1.aspx.

  14. Click View Contact Information to view the first XML transformation to RTF in Word.

  15. Click Back in Internet Explorer.

  16. Click View Customer Orders to view the second XML transformation to RTF in Word.

RTF Troubleshooting Tips

Received a Message Such as "Login failed for user 'MachineName\ASPNET"

If you receive a message such as "Login failed for user MachineName\ASPNET" when you click either of these buttons, the most likely problem is that the account \ASPNET does not have the proper permissions in SQL Server, and therefore it cannot access the database. To correct the problem, do the following:

  1. Start SQL Server Enterprise Manager.

  2. Expand the different levels to display (local) (Windows NT).

  3. Expand the Security folder.

  4. Click the Logins icon, right-click, and select the New Login option.

  5. In the Name box, type aspnet. Ensure that Windows Authentication is selected.

  6. In the Domain drop-down box, select the local domain for your computer. Leave the default Security access as Grant access.

  7. Under Defaults, in the Database drop-down box, select Northwind. Leave English as the default language.

  8. Click OK.

  9. Retry either button.

To save the RTF to a file and redirect to the saved file

As written, the sample code in the section Transform a DataSet Into RTF 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

Response.ContentType = "application/msword"
Response.ContentEncoding = System.Text.Encoding.Default
Response.Charset = ""
xslTran.Transform(xmlDoc, Nothing, Response.Output)

with:

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 a 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.

Be aware of How You Present White Space and Carriage Returns

When transforming to RTF, be aware of how you present white space and carriage returns in your style sheet because that can affect how Word interprets your RTF. Both code samples in this section use the <xsl:text> element because it forces any white space information in it to be retained.

Ensure That Your XML Is Transformed to Text

Use <xsl:output method="text"> in your style sheet 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 interpreting the text as RTF.

Conclusion

In this article, we looked at different ways to convert XML data into RTF. For example, we saw how you can transform custom XML into RTF either saved to a file or copied to the Clipboard. We also looked at how you can use Visual Basic .NET to transform datasets easily. Since RTF is a text-based format, it easily lends itself to creation in code. In addition, XML has become the de facto standard for presenting data. For this reason, it is well worth the time to learn these ways to create RTF documents, where applicable, in your own applications.

Additional Resources

For additional information about using RTF in your solutions, click the article links below:

For additional information about transforming XML by using Visual Basic .NET, click the article links below: