Jump to content
We've recently updated our Privacy Statement, available here ×
  • Using the REST API to Populate Native MS Word Tables


    Steve Park
    • Features: Web Services Version: v6.2 Product: JasperReports® Server

    Using the REST API to Add Report Data to a MS Word Document using Native Tables

    TIBCO JasperReports® Server can export reports to DOC and DOCX formats, however some end users would prefer to inject report data or objects into existing MS Word documents or create reports that allow for a layout that is not limited by a typical banded report structure.  This wiki article will demonstrate how to add report content to an existing MS Word document containing native tables using the REST API.  Future versions of this document will also show how to create native Excel charts using reporting data.  We are assuming that the reader is familiar with .NET programming.  The code snippets are written in the C# programming language.  Conversion to Visual Basic or another .NET-compatible language is left as an exercise for the reader.

    IMPORTANT:  This is community-contributed content and is not supported by TIBCO Analytics.  It is provided as-is with no express or implied warranties of any kind.  The source code is included in the WordDocument3.zip file and may be modified to meet your business requirements.  By doing so, however, the end consumer assumes all liabilities and responsibilities for modifying the code.

    Prerequisites

    • Microsoft Visual Studio 2010 (newer versions might work, but will likely require API changes)

    • .NET 4.0

    • Microsoft Office 2010 (newer versions might work, but will likely require API changes)

    • Visual Studio Tools for Office runtime, available here

    • RestSharp REST client for .NET, available here (I used the .NET 4.0 client DLL in version 105.2.3 downloaded using NuGet)

    • JasperReports Server with SugarCRM sample data (I used version 6.2 Professional, but Community and older versions should work with little or no adaptation)

    Configuration Steps

    You can download the entire source code solution WordDocument3.zip or you can create a new Visual Studio solution.  To create a new solution:

    1. Open Visual Studio and select File -> New -> Project

    2. Expand the Visual C# and Office nodes

    3. Select 2010 and Word 2010 Document

    4. Specify the name and location of the project, solution name, ensure the Create new solution option is selected and the Create directory for solution checkbox is checked

    5. Click OK

    VS2010-NewProject.png.96ba5c27a5abcadc6829b3c54d9fa2bc.png

    When the solution opens, a new MS Word document will appear.  This solution will assume the MS Word document has at least one empty table already created.  For this proof-of-concept, I created a MS Word document that looks like this:

    VS2010-SampleMSWordDoc.png.32c913a1915ee103c2039dee89d96793.png

    This solution will call the REST API to execute a report, export it in XML format, and then add rows to the existing table in the MS Word document. This example will use the EmployeeAccounts report that was included in the old report samples prior to the 5.5 release. The repository export of this report is available EmployeeAccounts-repo-export.zip.  You will need to import this report unit to your repository by logging in as superuser, clicking Manage -> Server Settings, clicking the Import menu option on the left side, and importing the EmployeeAccounts-repo-export.zip file.

    The EmployeeAccounts report contains a single input control that allows you to choose from a list of 14 employees.  In this proof-of-concept, we will execute a REST API call to retrieve a list of all employees, then we will loop through this list, copy the first page of the MS Word document, replace the <> placeholder in the salutation with the employee name, execute a REST API call that executes the report on the server, export it to XML format, and create rows on the table for the report data for each employee.

    The first task we need to do is authenticate to JasperReports Server.  There are many ways we could do this, but for this exercise we will create a simple Windows form that will pop-up when the MS Word document is opened and prompt for credentials.  To create a Windows form for retrieving JasperReports Server credentials:

    In the Toolbox window, expand the All Windows Forms node

    1. In the Solution Explorer window, right-click on the WordDocument3 project node and select Add -> Windows Form...

    2. The Add New Item dialog appears and the Windows Form option should be selected

    3. Specify a filename for the Windows form (this example uses JSCredentials.js)

    4. Click the Add button

    5. Drag labels, textboxes and buttons to the new Windows form until it looks like this:

      VS2010-JSCredentials.png.a1cdab67e498a42fa391c0b8abe69265.png

    6. The source code samples below assume you've named the textfield and Connect button objects according to the following table:

      Textfield/ButtonName
      ServerserverTB
      UsernameUsernameTB
      PasswordPasswordTB
      OrganizationOrgTB
      Connect ButtonOK
    7. Once you've named the textfield objects and Connect button, you'll need to write some code for the JSCredentials Windows form so that the main application can retrieve the values.  First, select the Connect button, and on the Properties window, click the Events button.  On the Click property, select the OK_Click option.  Then to add the source code, right-click on the Windows form and select View Code.  Finally, in the CSharp editor, modify the source code so that it looks like this:

      public partial class JSCredentials : Form
      {
          private String serverURL;
          private String username;
          private String password;
          private String org;
      
          public JSCredentials()
          {
              InitializeComponent();
          }
      
          private void OK_Click(object sender, EventArgs e)
          {
              this.ServerLabel.ForeColor = Color.Black;
              this.UsernameLabel.ForeColor = Color.Black;
              this.PasswordLabel.ForeColor = Color.Black;
              this.OrgLabel.ForeColor = Color.Black;
              if (this.serverTB.Text == null || this.serverTB.Text.Length == 0)
              {
                  this.ServerLabel.ForeColor = Color.Red;
              }
              if (this.UsernameTB.Text == null || this.UsernameTB.Text.Length == 0)
              {
                  this.UsernameLabel.ForeColor = Color.Red;
              }
              if (this.PasswordTB.Text == null || this.PasswordTB.Text.Length == 0)
              {
                  this.PasswordLabel.ForeColor = Color.Red;
              }
              if (this.OrgTB.Text == null || this.OrgTB.Text.Length == 0)
              {
                  this.OrgLabel.ForeColor = Color.Red;
              }
              if (this.ServerLabel.ForeColor == Color.Black
                  && this.UsernameLabel.ForeColor == Color.Black
                  && this.PasswordLabel.ForeColor == Color.Black
                  && this.OrgLabel.ForeColor == Color.Black)
              {
                  serverURL = this.serverTB.Text;
                  username = this.UsernameTB.Text;
                  password = this.PasswordTB.Text;
                  org = this.OrgTB.Text;
                  this.DialogResult = DialogResult.OK;
                  this.Close();
              }
              else
              {
                  MessageBox.Show("One or more required fields was blank", "Validation Error", MessageBoxButtons.OK);
              }
      
          }
      
          public String getServerURL()
          {
              return this.serverURL;
          }
      
          public String getUsername()
          {
              return this.username;
          }
      
          public String getPassword()
          {
              return this.password;
          }
      
          public String getOrg()
          {
              return this.org;
          }
      }
      

      NOTE: This example assumes you have the Professional version of JasperReports Server with multi-tenancy enabled and have more than one organization existing on the repository.  If this is not the case in your environment, you'll need to remove the Organization textbox and label, remove the validation checks for OrgTB in the OK_Click method and remove the getOrg() method.

    8. Save the source code and close the JSCredentials.cs file

    9. If you've not already done so, open the ThisDocument.cs file associated with the MS Word document.  Now that we have a way to connect to the server, we'll need to open this Windows form from the MS Word document.  For the sake of simplicity, we will always open this JSCredentials dialog box every time the MS Word document opens.  To do this, create the following method:

      private RestClient restClient;
      
      private void initServerConnection()
      {
          // Get server credentials
          JSCredentials credentials = new JSCredentials();
          credentials.Margin = Padding.Empty;
          credentials.Padding = Padding.Empty;
          credentials.StartPosition = FormStartPosition.CenterParent;
          NativeWindow wordMain = new NativeWindow();
          wordMain.AssignHandle(Process.GetCurrentProcess().MainWindowHandle);
          DialogResult result = credentials.ShowDialog(wordMain);
          wordMain.ReleaseHandle();
          if (result != DialogResult.OK) this.Close(ref missing, ref missing, ref missing);
          string url = "http://" + credentials.getServerURL() + "/jasperserver-pro/rest_v2/reports/reports/samples";
          restClient = new RestClient(url);
          // Multi-tenant version (comment out for single tenant or community)
          String credentialsStr = String.Format("{0}|{1}", credentials.getUsername(), credentials.getOrg());
          restClient.Authenticator = new HttpBasicAuthenticator(credentialsStr, credentials.getPassword());
          // Single-tenant or community version (comment out for multi-tenancy)
          //restClient.Authenticator = new HttpBasicAuthenticator(credentials.getUsername(), credentials.getPassword());
      }
      

      NOTE: Be sure to comment and uncomment the last four lines of code properly depending on whether you have the Professional version of JasperReports Server with multi-tenancy enabled or not.  Also, note that if you have multi-tenancy enabled but only one organization exists in your repository, you'll either need to uncomment the single-tenant code or create another tenant in your repository.

    10. Add a call to the initServerConnection() method in the ThisDocument_Startup() method.  You'll need to surround this method with a try/catch block and catch a possible WebException object.  As a further enhancement, we can automatically prompt the user again for valid credentials and give them up to five attempts to provide valid credentials before exiting. To do this, modify the ThisDocument_Startup method as follows:

       

      private const int MAX_ATTEMPT_COUNT = 5;
      
      private void ThisDocument_Startup(object sender, System.EventArgs e)
      {
         object paramMissing = System.Type.Missing;
      
          int connAttempt = 0;
          bool success = false;
          while (connAttempt < MAX_ATTEMPT_COUNT && !success)
          {
              try
              {
                  initServerConnection();
                  success = true;
              }
              catch (WebException we)
              {
                  String errorMsg = we.GetBaseException().Message + ".  Please try again.";
                  if (we.GetBaseException().Message.Contains("401"))
                  {
                      errorMsg = "Invalid JasperReports Server credentials.  Please double-check your credentials and try again.";
                  }
                  DialogResult result = MessageBox.Show(errorMsg,
                      "Error connecting to JasperReports Server", MessageBoxButtons.RetryCancel);
                  if (result == DialogResult.Cancel)
                  {
                      this.Close(ref paramMissing, ref paramMissing, ref paramMissing);
                  }
                  connAttempt++;
              }
          }
          if (!success)
          {
              MessageBox.Show("Maximum server attempt count exceeded.  Please ensure your JasperReports Server instance is running and try again later.",
                  "Max Connection Attempts", MessageBoxButtons.OK);
              this.Close(ref paramMissing, ref paramMissing, ref paramMissing);
          }
      }
      
    11. The EmployeeAccounts report contains a single option query-based input control that lets end users choose from a list of employees.  As our plan is to iterate over all the employees and create a separate letter for each, we need to make a REST API call to retrieve the list of values from the input control.  Once we have those values, we store them in a KeyValuePair class variable for access later by other methods.  We can leverage the JSON deserializer from the RestSharp library to help parse the response from the server.  This is done in the following code:

      private List> employeeIDMap;
      private JsonDeserializer deserial = new JsonDeserializer();
      
      private void processInputControlValues()
      {
          employeeIDMap = new List>();
      
          // Create web request
          RestRequest icRequest = new RestRequest("/EmployeeAccounts/inputControls/values");
      
          // Get response
          IRestResponse res = restClient.Execute(icRequest);
      
          if (res.StatusCode == HttpStatusCode.OK)
          {
              InputControlResponse icRes = deserial.Deserialize(res);
              // Since there's only one IC, we can hard-code the inputControlState array reference without looping
              foreach (InputControlOptions option in icRes.inputControlState[0].options)
              {
                  employeeIDMap.Add(new KeyValuePair(option.value, option.label));
              }
          }
          else
          {
              throw new WebException("Status code: " + res.StatusCode);
          }
      }
      

      The JSONDeserializer depends on helper classes to store the parsed result.  Based on the guidance given in section 14.2 of the JasperReports Server REST API Reference, we will need to create the following helper classes to parse the response:

      private class InputControlOptions
      {
          public string label { get; set; }
          public Boolean selected { get; set; }
          public string value { get; set; }
      }
      
      private class InputControlState
      {
          public string uri { get; set; }
          public string value { get; set; }
          public List options { get; set; }
      }
      
      private class InputControlResponse
      {
          public List inputControlState { get; set; }
      }
      

      Next, we need to add this method call to the ThisDocument_Startup method.  For brevity, we will only show the containing try block, however the rest of the code should remain as shown above:

      try
      {
          initServerConnection();
      
          processInputControlValues();
          success = true;
      }
      
    12. Now that we have a list of employees, it's time to execute the report and pass in each employee ID as a parameter.  We'll use the following code to loop over each employee and make a REST API call to execute the report and return the output in XML format:

      private void execReports()
      {
          int empCount = 1;
          foreach (KeyValuePair mapEntry in employeeIDMap)
          {
              String empId = mapEntry.Key;
      
              // Create web request
              RestRequest icRequest = new RestRequest("/EmployeeAccounts.xml");
              icRequest.AddParameter("EmployeeID", empId);
      
              // Get response
              IRestResponse res = restClient.Execute(icRequest);
      
              empCount++;
          }
      }
      
    13. Now that we have the report output in XML format for each employee, we need to process it and begin adding the data to the MS Word table.  This part is fairly complex, so we'll develop it in several stages.  First, so that we don't lose the template that we're adding reporting to, we need to copy the first page of the MS Word document and keep the first page as the template that we'll use for the rest of the document.  Also, since the MS Word document doesn't have a native implementation of pages as containers at the API level as of Word 2010, we'll need to manually insert a page break.  To do this, we'll add the following code:

      private void copyPage()
      {
          insertPageBreak();
      
          object start = this.Content.Start;
          if (templateEnd == null)
          {
              templateEnd = this.Content.End / 2 - 1;
          }
      
          object docStart = this.Content.End - 1;
          object docEnd = this.Content.End;
      
          this.Range(ref start, ref templateEnd).Copy();
          Microsoft.Office.Interop.Word.Range rng = this.Range(ref docStart, ref docEnd);
          rng.Paste();
      }
      
      private void insertPageBreak()
      {
          Object objBreak = Word.WdBreakType.wdPageBreak;
          this.Characters.Last.InsertBreak(ref objBreak);
      }
      

      Before we move on to the next stage, we need to add a call to the copyPage() method in the execReports method.  We'll insert this in the beginning of the foreach loop:

      private void execReports()
      {
          int empCount = 1;
          foreach (KeyValuePair mapEntry in employeeIDMap)
          {
              copyPage();
      
              String empId = mapEntry.Key;
              ...
          }
      }
      
    14. Next, let's begin processing the report output.  We'll begin by checking to see if the REST API call returned valid report output or not. If not, we'll display an error message and shut down the process gracefully:

      private void processReportOutput(IRestResponse res, int empCount)
      {
          // Check to see if the report ran successfully on the server
          if (res.StatusCode != HttpStatusCode.OK)
          {
              MessageBox.Show("There was an error executing the report.  Please ensure a valid report exists on the server.",
                   "Report Execution Error", MessageBoxButtons.OK);
              this.Close(ref missing, ref missing, ref missing);
          }
         else
         {
              string reportStr = res.Content;
              int row = writeToWordDoc(reportStr, empCount);
              cleanUpTable(row, empCount);
         }
      }
      
    15. Next, we need to define the body of the writeToWordDoc() method.  The algorithm for this method is:

      private int writeToWordDoc(String reportStr, int empCount)
      {
          // 1.  replace <> placeholder with actual employee name
          // 2.  parse XML output
          // 3.  begin looping through the frame nodes in the XML output that denote a row of the report
          // 4.  process group headers first
          // 5.  process individual fields
      }
      

      First, we need to replace the <<employee>> placeholder:

      // Replace salutation 
      object docEnd = this.Content.End;
      Word.Range tmpRange = this.Range(ref templateEnd, ref docEnd);
      tmpRange.Find.ClearFormatting();
      tmpRange.Find.Text = "<>";
      tmpRange.Find.Replacement.ClearFormatting();
      tmpRange.Find.Replacement.Text = employeeIDMap[empCount - 1].Value.Substring(0, 1).ToUpper() + employeeIDMap[empCount - 1].Value.Substring(1);
      
      tmpRange.Find.Wrap = Word.WdFindWrap.wdFindContinue;
      
      object replaceOne = Word.WdReplace.wdReplaceOne;
      
      tmpRange.Find.Execute(ref missing, ref missing, ref missing,
              ref missing, ref missing, ref missing, ref missing,
              ref missing, ref missing, ref missing, ref replaceOne,
              ref missing, ref missing, ref missing, ref missing);
      
    16. Next, we need to get a handle to the Word table, parse the XML output, and begin looping over the frames. Since we've already made a copy of the first page and keeping it as a template, we need to get a handle to the second table in the report, which is why the Tables reference uses empCount + 1 as its index.

      // Get a handle to the Word table
      Word.Table docTable = this.Tables[empCount + 1];
      
      // parse report output
      XmlDocument doc = new XmlDocument();
      doc.LoadXml(reportStr);
      XmlNamespaceManager nsManager = new XmlNamespaceManager(doc.NameTable);
      nsManager.AddNamespace("js", "http://jasperreports.sourceforge.net/jasperreports/print");
      
      // loop thru frames
      XmlNodeList frameList = doc.SelectNodes("//js:frame", nsManager);
      int row = 2;  // skip the header row
      foreach (XmlNode frame in frameList)
      {
          // process each row, discussed in next section
      }
      
      return row;
      
    17. Next, we need to process the rows.  We're going to treat the group headers separately so that we can format the MS Word table accordingly. Since the group headers in the report output use a bold font, we can take advantage of this knowledge to determine which rows contain the group headers.  The code to format the group headers is:

      if (node.Attributes["style"] != null && node.Attributes["style"].InnerText.Equals("Sans_Bold"))
      {
          // Add group name to the table
          String groupName = frame.SelectSingleNode("js:text/js:textContent", nsManager).InnerText;
          docTable.Cell(row, 1).Range.Text = groupName;
          docTable.Cell(row, 1).Range.Font.Color = Word.WdColor.wdColorWhite;
          docTable.Cell(row, 1).Shading.BackgroundPatternColor = Word.WdColor.wdColorBlueGray;
          docTable.Cell(row, 1).Range.Font.Bold = 1;
          docTable.Cell(row, 2).Shading.BackgroundPatternColor = Word.WdColor.wdColorBlueGray;
          docTable.Cell(row, 3).Shading.BackgroundPatternColor = Word.WdColor.wdColorBlueGray;
      }
      else
      {
          // process regular rows, described below
      }
      
    18. Next, we need to process the data for each regular row.  Since the report contains three fields, we'll need to create three table cells for each row.  The code to process the regular rows is:

      String accountName = frame.SelectSingleNode("js:text[2]/js:textContent", nsManager).InnerText;
      docTable.Cell(row, 1).Range.Text = accountName;
      docTable.Cell(row, 1).Range.Font.Color = Word.WdColor.wdColorBlack;
      docTable.Cell(row, 1).Shading.BackgroundPatternColor = Word.WdColor.wdColorWhite;
      docTable.Cell(row, 1).Range.Font.Bold = 0;
      
      String phone = frame.SelectSingleNode("js:text[3]/js:textContent", nsManager).InnerText;
      docTable.Cell(row, 2).Range.Text = phone;
      docTable.Cell(row, 2).Range.Font.Color = Word.WdColor.wdColorBlack;
      docTable.Cell(row, 2).Shading.BackgroundPatternColor = Word.WdColor.wdColorWhite;
      docTable.Cell(row, 2).Range.Font.Bold = 0;
      
      String address = frame.SelectSingleNode("js:text[4]/js:textContent", nsManager).InnerText;
      docTable.Cell(row, 3).Range.Text = address;
      docTable.Cell(row, 3).Range.Font.Color = Word.WdColor.wdColorBlack;
      docTable.Cell(row, 3).Shading.BackgroundPatternColor = Word.WdColor.wdColorWhite;
      docTable.Cell(row, 3).Range.Font.Bold = 0;
      
    19. Finally, we may have an extra row on the MS Word table that we will need to remove.  The code to handle this is:

      private void cleanUpTable(int row, int empCount)
      {
          // Get a handle to the Word table
          Word.Table docTable = this.Tables[empCount + 1];
          while (docTable.Rows.Count > row)
          {
              docTable.Rows[row].Delete();
          }
      }
      

    That's all.  Now we're ready to build and execute the project.  For reference, here's the complete code for ThisDocument.cs file:

    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Linq;
    using System.Text;
    using System.Xml;
    using System.Xml.Linq;
    using System.Diagnostics;
    using System.IO;
    using System.Net;
    using System.Windows.Forms;
    using Microsoft.Office.Tools.Word;
    using Microsoft.VisualStudio.Tools.Applications.Runtime;
    using Office = Microsoft.Office.Core;
    using Word = Microsoft.Office.Interop.Word;
    using RestSharp;
    using RestSharp.Deserializers;
    using RestSharp.Authenticators;
    
    namespace WordDocument3
    {
        public partial class ThisDocument
        {
            private const int MAX_ATTEMPT_COUNT = 5;
            private RestClient restClient;
            private List> employeeIDMap;
            private JsonDeserializer deserial = new JsonDeserializer();
            private object templateEnd = null;
    
            private void ThisDocument_Startup(object sender, System.EventArgs e)
            {
                object paramMissing = System.Type.Missing;
    
                int connAttempt = 0;
                bool success = false;
                while (connAttempt < MAX_ATTEMPT_COUNT && !success)
                {
                    try
                    {
                        initServerConnection();
    
                        processInputControlValues();
                        execReports();
                        success = true;
                    }
                    catch (WebException we)
                    {
                        String errorMsg = we.GetBaseException().Message + ".  Please try again.";
                        if (we.GetBaseException().Message.Contains("401"))
                        {
                            errorMsg = "Invalid JasperReports Server credentials.  Please double-check your credentials and try again.";
                        }
                        DialogResult result = MessageBox.Show(errorMsg,
                            "Error connecting to JasperReports Server", MessageBoxButtons.RetryCancel);
                        if (result == DialogResult.Cancel)
                        {
                            this.Close(ref paramMissing, ref paramMissing, ref paramMissing);
                        }
                        connAttempt++;
                    }
                }
                if (!success)
                {
                    MessageBox.Show("Maximum server attempt count exceeded.  Please ensure your JasperReports Server instance is running and try again later.",
                        "Max Connection Attempts", MessageBoxButtons.OK);
                    this.Close(ref paramMissing, ref paramMissing, ref paramMissing);
                }
            }
    
            private void ThisDocument_Shutdown(object sender, System.EventArgs e)
            {
            }
    
            private void initServerConnection()
            {
                // Get server credentials
                JSCredentials credentials = new JSCredentials();
                credentials.Margin = Padding.Empty;
                credentials.Padding = Padding.Empty;
                credentials.StartPosition = FormStartPosition.CenterParent;
                NativeWindow wordMain = new NativeWindow();
                wordMain.AssignHandle(Process.GetCurrentProcess().MainWindowHandle);
                DialogResult result = credentials.ShowDialog(wordMain);
                wordMain.ReleaseHandle();
                if (result != DialogResult.OK) this.Close(ref missing, ref missing, ref missing);
                string url = "http://" + credentials.getServerURL() + "/jasperserver-pro/rest_v2/reports/reports/samples";
                restClient = new RestClient(url);
                // Multi-tenant version (comment out for single tenant or community)
                String credentialsStr = String.Format("{0}|{1}", credentials.getUsername(), credentials.getOrg());
                restClient.Authenticator = new HttpBasicAuthenticator(credentialsStr, credentials.getPassword());
                // Single-tenant or community version (comment out for multi-tenancy)
                //restClient.Authenticator = new HttpBasicAuthenticator(credentials.getUsername(), credentials.getPassword());
            }
    
            private void processInputControlValues()
            {
                employeeIDMap = new List>();
    
                // Create web request
                RestRequest icRequest = new RestRequest("/EmployeeAccounts/inputControls/values");
    
                // Get response
                IRestResponse res = restClient.Execute(icRequest);
    
                if (res.StatusCode == HttpStatusCode.OK)
                {
                    InputControlResponse icRes = deserial.Deserialize(res);
                    // Since there's only one IC, we can hard-code the inputControlState array reference without looping
                    foreach (InputControlOptions option in icRes.inputControlState[0].options)
                    {
                        employeeIDMap.Add(new KeyValuePair(option.value, option.label));
                    }
                }
                else
                {
                    throw new WebException("Status code: " + res.StatusCode);
                }
            }
    
            private void execReports()
            {
                int empCount = 1;
                foreach (KeyValuePair mapEntry in employeeIDMap)
                {
                    copyPage();
    
                    String empId = mapEntry.Key;
    
                    // Create web request
                    RestRequest icRequest = new RestRequest("/EmployeeAccounts.xml");
                    icRequest.AddParameter("EmployeeID", empId);
    
                    // Get response
                    IRestResponse res = restClient.Execute(icRequest);
    
                    processReportOutput(res, empCount);
    
                    empCount++;
                }
            }
    
            private void copyPage()
            {
                insertPageBreak();
    
                object start = this.Content.Start;
                if (templateEnd == null)
                {
                    templateEnd = this.Content.End / 2 - 1;
                }
    
                object docStart = this.Content.End - 1;
                object docEnd = this.Content.End;
    
                this.Range(ref start, ref templateEnd).Copy();
                Microsoft.Office.Interop.Word.Range rng = this.Range(ref docStart, ref docEnd);
                rng.Paste();
            }
    
            private void insertPageBreak()
            {
                Object objBreak = Word.WdBreakType.wdPageBreak;
                this.Characters.Last.InsertBreak(ref objBreak);
            }
    
            private void processReportOutput(IRestResponse res, int empCount)
            {
                // Check to see if the report ran successfully on the server
                if (res.StatusCode != HttpStatusCode.OK)
                {
                    MessageBox.Show("There was an error executing the report.  Please ensure a valid report exists on the server.",
                         "Report Execution Error", MessageBoxButtons.OK);
                    this.Close(ref missing, ref missing, ref missing);
                }
                else
                {
                    string reportStr = res.Content;
                    int row = writeToWordDoc(reportStr, empCount);
                    cleanUpTable(row, empCount);
                }
            }
    
            private int writeToWordDoc(String reportStr, int empCount)
            {
                // Replace salutation 
                object docEnd = this.Content.End;
                Word.Range tmpRange = this.Range(ref templateEnd, ref docEnd);
                tmpRange.Find.ClearFormatting();
                tmpRange.Find.Text = "<>";
                tmpRange.Find.Replacement.ClearFormatting();
                tmpRange.Find.Replacement.Text = employeeIDMap[empCount - 1].Value.Substring(0, 1).ToUpper() + employeeIDMap[empCount - 1].Value.Substring(1);
    
                tmpRange.Find.Wrap = Word.WdFindWrap.wdFindContinue;
    
                object replaceOne = Word.WdReplace.wdReplaceOne;
    
                tmpRange.Find.Execute(ref missing, ref missing, ref missing,
                        ref missing, ref missing, ref missing, ref missing,
                        ref missing, ref missing, ref missing, ref replaceOne,
                        ref missing, ref missing, ref missing, ref missing);
    
                // Get a handle to the Word table
                Word.Table docTable = this.Tables[empCount + 1];
    
                // parse report output
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(reportStr);
                XmlNamespaceManager nsManager = new XmlNamespaceManager(doc.NameTable);
                nsManager.AddNamespace("js", "http://jasperreports.sourceforge.net/jasperreports/print");
    
                // loop thru frames
                XmlNodeList frameList = doc.SelectNodes("//js:frame", nsManager);
                int row = 2;  // skip the header row
                foreach (XmlNode frame in frameList)
                {
                    XmlNode node = frame.SelectSingleNode("js:text/js:reportElement", nsManager);
                    if (node.Attributes["style"] != null && node.Attributes["style"].InnerText.Equals("PageHeader"))
                    {
                        // ignore page headers
                        continue;
                    }
                    else
                    {
                        Object beforeRow = System.Type.Missing;
                        docTable.Rows.Add(ref beforeRow);
                        if (node.Attributes["style"] != null && node.Attributes["style"].InnerText.Equals("Sans_Bold"))
                        {
                            // Add group name to the table
                            String groupName = frame.SelectSingleNode("js:text/js:textContent", nsManager).InnerText;
                            docTable.Cell(row, 1).Range.Text = groupName;
                            docTable.Cell(row, 1).Range.Font.Color = Word.WdColor.wdColorWhite;
                            docTable.Cell(row, 1).Shading.BackgroundPatternColor = Word.WdColor.wdColorBlueGray;
                            docTable.Cell(row, 1).Range.Font.Bold = 1;
                            docTable.Cell(row, 2).Shading.BackgroundPatternColor = Word.WdColor.wdColorBlueGray;
                            docTable.Cell(row, 3).Shading.BackgroundPatternColor = Word.WdColor.wdColorBlueGray;
                        }
                        else
                        {
                            String accountName = frame.SelectSingleNode("js:text[2]/js:textContent", nsManager).InnerText;
                            docTable.Cell(row, 1).Range.Text = accountName;
                            docTable.Cell(row, 1).Range.Font.Color = Word.WdColor.wdColorBlack;
                            docTable.Cell(row, 1).Shading.BackgroundPatternColor = Word.WdColor.wdColorWhite;
                            docTable.Cell(row, 1).Range.Font.Bold = 0;
    
                            String phone = frame.SelectSingleNode("js:text[3]/js:textContent", nsManager).InnerText;
                            docTable.Cell(row, 2).Range.Text = phone;
                            docTable.Cell(row, 2).Range.Font.Color = Word.WdColor.wdColorBlack;
                            docTable.Cell(row, 2).Shading.BackgroundPatternColor = Word.WdColor.wdColorWhite;
                            docTable.Cell(row, 2).Range.Font.Bold = 0;
    
                            String address = frame.SelectSingleNode("js:text[4]/js:textContent", nsManager).InnerText;
                            docTable.Cell(row, 3).Range.Text = address;
                            docTable.Cell(row, 3).Range.Font.Color = Word.WdColor.wdColorBlack;
                            docTable.Cell(row, 3).Shading.BackgroundPatternColor = Word.WdColor.wdColorWhite;
                            docTable.Cell(row, 3).Range.Font.Bold = 0;
                        }
                    }
    
                    row++;
                }
    
                return row;
            }
    
            private void cleanUpTable(int row, int empCount)
            {
                // Get a handle to the Word table
                Word.Table docTable = this.Tables[empCount + 1];
                while (docTable.Rows.Count > row)
                {
                    docTable.Rows[row].Delete();
                }
            }
    
            private class InputControlOptions
            {
                public string label { get; set; }
                public Boolean selected { get; set; }
                public string value { get; set; }
            }
    
            private class InputControlState
            {
                public string uri { get; set; }
                public string value { get; set; }
                public List options { get; set; }
            }
    
            private class InputControlResponse
            {
                public List inputControlState { get; set; }
            }
        }
    }
    

    VS2010-NewProject.png.3229367f833a3470d88f28f82806d05e.png


    User Feedback

    Recommended Comments

    There are no comments to display.



    Create an account or sign in to comment

    You need to be a member in order to leave a comment

    Create an account

    Sign up for a new account in our community. It's easy!

    Register a new account

    Sign in

    Already have an account? Sign in here.

    Sign In Now

×
×
  • Create New...