Click here to Skip to main content
15,867,453 members
Articles / Programming Languages / C#

Generating PDF reports using nfop

,
Rate me:
Please Sign up or sign in to vote.
4.81/5 (38 votes)
7 Jun 2011CPOL7 min read 91K   1.9K   127   22
This article will help you to examine the main features of XSL schemes to generate programmatically advanced PDF reports.

Table of Contents

  1. Introduction
  2. Brief survey of the topic
  3. C# example
  4. XSL Features
    1. Brief survey of main XSL operators and constructions
    2. Usage of Cyrillic and East Asian fonts
    3. Building the bookmarks tree
    4. Building the table of contents
    5. Page numbering
    6. Logo insertion
    7. Hyperlink insertion
    8. Table rotation
    9. Gallery
  5. Conclusion

Introduction

There are lots of articles which describe how to convert XML documents to the documents of other types using the XSL Formatting Objects. Most of them describe how to obtain HTML from XML and only few tell about PDF documents.

Such articles usually demonstrate simple examples (how to build the table or perform the text output). But sometimes, we need to create representative reports or documents. In this case, developers face nontrivial problems – for example, creating the table of contents (using internal or external links), bookmark trees, picture galleries, etc. This article will help you to examine the main features of XSL schemes.

Brief Survey of the Topic

Generation of PDF documents is based on the Apache XML FOP technology that was initially written in Java language. But in our case, we use  nFOP – the C# wrapper that is based on the Visual J#. This wrapper makes it easier to write pure .NET reporting modules. The generation of PDF documents is very simple and can be described in the following way:

scheme.png

We have an XML document (created by some application or manually) and  XSLT scheme, with the help of which we obtain the XSL-FO document. The PDF file will be created on its basis. Then we compile the obtained XSL-FO document to PDF with the help of FOP.

C# Example

The process of PDF document generation can be described using a small example. Supposing there is a list of writers, who have their books and these books consist of articles, respectively:

sample.JPG

This structure is represented by Author and Book classes.

C#
public class Author
    {
        public List<Book> created;
        public string name;
        public int id;
	 ...
    }
public class Book
    {
        public int id;
        public Props bookProps;
        public List<Pare> articles;
	 ...
    }

C# language allows you to serialize any class to XML document. In this case, we can provide the following example:

C#
//Serializing data	
FileStream fs = System.IO.File.Create("source.xml");          
XmlSerializer s = new XmlSerializer(typeof(List<Author>));

List<Author> list = new List<Author>();
list.Add(writer);
list.Add(writer1);

s.Serialize(fs, list);
fs.Close();

Then, according to the scheme described above, the process of XLS-FO document generation is performed:

C#
//Generate FO file
XslTransform xslt = new XslTransform();
//Loading XSL template 
xslt.Load("schema.xsl");
//Loading XSL template 
xslt.Transform("source.xml", "source.fo");

The last step is to call the methods of nfop, which compiles XLS-FO to PDF:

C#
//Generate PDF file
java.io.FileInputStream streamFO = null;
java.io.FileOutputStream streamOut = null;
try
{
 	streamFO = new java.io.FileInputStream("source.fo");
 	streamOut = new java.io.FileOutputStream(fileName + ".pdf");

 	InputSource src = new InputSource(streamFO);
 	Driver driver = new Driver(src, streamOut);
 	driver.setRenderer(Driver.RENDER_PDF);
 	driver.run();
 }
 catch(FOPException ex)
 {
  	Console.WriteLine(ex.Message);
  	throw;
 }
 catch (System.Exception ex)
 {
  	Console.WriteLine(ex.Message);
  	throw;
 }
 finally
 {
  	if (streamOut != null)
  	{
   		streamOut.close();
  	}
  	if (streamFO != null)
  	{
   		streamFO.close();
  	}
 }

It is important to include the following references to the solution being developed:

  • vjslib
  • nfop

References.JPG

vjslib.dll can be downloaded here.

nfop.dll can be downloaded here.

XSL Features

As it was mentioned above, the majority of articles about XSL-FO describe such common things as the output of the formatted text or the creation of tables. But often you need to add to the document:

  • Table of contents with internal references to articles (taking into account that we do not know the page numbers beforehand as the document is generated dynamically)
  • The bookmark tree (rather convenient tool for navigating in the document)
  • Footnotes with definite blocks of the text and, perhaps, references to external web pages
  • The picture gallery (it can be also dynamically saved by means of the C# language).

sample1.png

  1. The bookmark tree
  2. Table of contents with internal references to chapters of the document
  3. Page numbering
  4. Static picture (can be the company logo)

Brief Survey of Main XSL Operators and Constructions

XSL language includes all main constructions:

  • if analog:
XML
<xsl:if test="'condition'">
 <!--Operations-->
</xsl:if>
  • switch analog:
XML
<xsl:choose>
 <xsl:when test="'condition'">
  <!--Operations-->
 </xsl:when>
 <xsl:otherwise>
  <!--Operations-->
 </xsl:otherwise>
</xsl:choose>
  • for analog (processes a number of XML nodes with the same level of nesting):
XML
<xsl:for-each select="Author">
 <!--Operations-->
</xsl:for-each>

You can access the definite node by index (if you know its number in the list beforehand):

XML
<xsl:value-of select="/Column[1]"/>

Also you can use some variable as the index:

XML
<xsl:variable name="index">
 <xsl:value-of select="position()"/>
</xsl:variable>
...
<xsl:value-of select="Column[number($index)]"/>

number($index) conversion returns the number, which was generated from the string stored in $index. If you do not use it, the $index variable will possess “1” as any  value as is considered to be of the string type by default.

If you use the position() operator, it’s better to check also the condition:

XML
<xsl:if test="position() != last()">
  ...
</xsl:if> 

Here, position() is the number of the current node of the XML document (last() is the last in the list, respectively).

Usage of Cyrillic and East Asian Fonts

There is often a problem of using specific languages in reports. By default, they are not included in the list of nfop fonts. To add new languages, you have to add a new font to the useconfig.xml file. The configuratiob file will look like the following:

XML
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
 <fonts>
  <font kerning="yes"
  metrics-file="..\fonts\arialuni.xml" embed-file="..\fonts\arialuni.ttf">
   <font-triplet name="arialuni" style="normal" weight="normal" />
   <font-triplet name="arialuni" style="normal" weight="bold" />
   <font-triplet name="arialuni" style="italic" weight="normal" />
   <font-triplet name="arialuni" style="italic" weight="bold" />
  </font>
 </fonts>
</configuration>

Here, metrics-file is a path to the font metric, embed-file is a path to the font file.

In the example above, the standard arialuni.ttf font was added. It is a part of Windows OS (WINDOWS\Fonts\ARIALUNI.TTF) and is the most universal one. Fonts should be placed in the same folder as the configuration file.

Building the Bookmarks Tree

When building the bookmark tree (with references to the articles), you should specify an additional id field in the nodes. With its help, references will be organized in the XSLT scheme. 

We can create the static class, which generates unique id for each object that we need to refer to later:

C#
public static class IDGenerator
    {
        private static int id = 0;
        public static int Generate()
        {
            return id++;
        }
    }

Labels to refer on are placed when building the tables. We just add one more attribute to the current block, which will be used in references:

XML
<fo:block>
 <xsl:attribute name="id">
  <xsl:value-of select="id"/>
 </xsl:attribute>
</fo:block>

The process of building the tree is based on placing the blocks of such type:

XML
<fox:outline>
<xsl:attribute name="internal-destination">
<xsl:value-of select="id"/>
</xsl:attribute>
<fox:label>
<xsl:value-of select="name"/><!--Name of  bookmark-->
</fox:label>
</fox:outline>

They can be placed as the nested ones and it will create the treelike structure.

Building the Table of Contents

The table of contents of the document is created in the similar way.  The same id field (as in the previous example) is used for references:

XML
<fo:basic-link>

<xsl:attribute name="internal-destination">
 <xsl:value-of select="id"/>
</xsl:attribute>
<xsl:text>Chapter 1</xsl:text><!--Name of link-->

<fo:leader leader-pattern="dots"/>

<fo:page-number-citation>
 <xsl:attribute name="ref-id">
  <xsl:value-of select="id"/>
 </xsl:attribute>
</fo:page-number-citation>

</fo:basic-link>

The <fo:leader leader-pattern="dots"/> line fills space between the text and page number by dots.

Page Numbering

One more interesting moment is that we do not know exactly on which page the table will be located while building the PDF document. Here we can use an expression that will define the page number of the dynamically generated document automatically:

XML
<fo:page-number-citation>
 <xsl:attribute name="ref-id">
  <xsl:value-of select="id"/>
 </xsl:attribute>
</fo:page-number-citation>

Logo Insertion

XSL Templates allow you to create the static content in any page region that will be repeated on each page. For example, the following code inserts the logo to the lower right corner of the document:

XML
<fo:static-content flow-name="xsl-region-after" font-family="Arial">
 <!-- logo in lower right part of report  page-->
 <fo:block text-align="right" margin-right="25pt" >
  <fo:external-graphic scaling="unifiorm" height="0.4in" width="1in"
  src="file:///logo.JPG"/>
 </fo:block>
</fo:static-content>

The building of the document frame can be organized in a similar way. You need to create four separate images for each border of the document (top, bottom, left and right), which can be then used as the background of the region  (region-after, region-before, region-end, region-start).

Hyperlink Insertion

To insert external references, you can use the following construction:

XML
<xsl:template name="ExternalLink">
<xsl:param name="link"/>
<xsl:param name="link_text"/>
<fo:basic-link font-size="10pt" font-weight="bold" color="blue">
<xsl:attribute name="external-destination">
<xsl:value-of select="$link"/>
</xsl:attribute>
<xsl:value-of select="$link_text"/>
</fo:basic-link>
</xsl:template>

Table Rotation

There is often a problem of displaying the tables with numerous columns. Such tables cannot be placed within the page borders.

Name

Prop1

Prop2

Prop3

PropN

1

Some data

Some data

Some data

Some data

2

Some data

Some data

Some data

Some data

In this case, it is possible to perform table rotation, where a new table will correspond to each row:

Table 1 – first row

Name

1

Prop1

Some data

Prop2

Some data

Prop3

Some data

PropN

Some data

Table 2 – second row

Name

2

Prop1

Some data

Prop2

Some data

Prop3

Some data

PropN

Some data

Sometimes there is a nonstandard situation when it is necessary to process the data, which is stored in two related lists of nodes of the XML document. For example, you need to fill in the table where the first column Columns contains the name of the data, the second one - Rows - contains its values.

XML
<Columns>
 <Column>
  <Title>Name</Title>
 </Column>
 <Column>
  <Title>Phone Number</Title>
 </Column>
</Columns>
<Rows>
 <Row>
  <Value>
   <Link>Johny</Link>
  </Value>
  <Value>
   <Link>555-55-55</Link>
  </Value>
 </Row>
 <Row>
  <Value>
   <Link>Ann</Link>
  </Value>
  <Value>
   <Link>333-60-00</Link>
  </Value>
 </Row>
</Rows>

To create the table from the given XML document, you can use the following pattern:

XML
<xsl:template name="ParallelReading">
 <xsl:for-each select="Rows">
  <fo:table>
   <fo:table-column background-color="#DCDCDC" column-width="20%"/>
   <fo:table-column background-color="#FFFFFF" />
   <fo:table-body>
    <xsl:for-each select="Row">
     <fo:table-row>
      <fo:table-cell padding="4pt" border="1pt solid black">
       <xsl:for-each select="../../Columns/Column">
        <fo:block>
         <xsl:value-of select="Title"/>
        </fo:block>
       </xsl:for-each>
      </fo:table-cell>
      <fo:table-cell padding="4pt" border="1pt solid black">
       <xsl:for-each select="Value">
        <fo:block>
         <xsl:value-of select="Link"/>
        </fo:block>
       </xsl:for-each>
      </fo:table-cell>
     </fo:table-row>
    </xsl:for-each>
   </fo:table-body>
  </fo:table>
 </xsl:for-each>
</xsl:template>

Gallery

The process of creating the picture gallery can be described in the following way:

  • The required pictures are saved to the temporary folder on the hard drive (programmatically).
  • The XML document is created. It contains the full paths and the description of the saved files (this data will be used later in the XSLT scheme).
  • The PDF document is generated. Pictures from the temporary folder are built in it.
  • The temporary folder with its contents is deleted.
XML
<Galery xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <Picture>
  <Pare>
   <Name>Name</Name>
   <Value>Blue hills</Value>
  </Pare>
  <Pare>
   <Name>Path</Name>
   <Value>E:/pdf_root/Blue hills.jpg</Value>
  </Pare>
  <Pare>
   <Name>Size</Name>
   <Value>28 521 bytes</Value>
  </Pare>
  <Pare>
   <Name>Type</Name>
   <Value>JPG File</Value>
  </Pare>
  <Pare>
   <Name>Created</Name>
   <Value>12 may 2010, 15:01:36</Value>
  </Pare>
 </Picture>
...
</Galery>

The pattern, which builds the gallery, is given below:

XML
<fo:block text-align="center" font-size="25pt">
  Gallery
</fo:block>
<fo:block>
 <fo:table>
  <fo:table-column/>
   <fo:table-body>
    <fo:table-row>
     <fo:table-cell padding="2pt" border="1pt solid indigo" background-color="silver">
      <fo:block>
       <fo:table>
        <fo:table-column column-width="1.5in"/>
         <fo:table-column/>
          <fo:table-body>
           <xsl:for-each select="Galery/Picture">
            <fo:table-row>
             <fo:table-cell padding="2pt" border="0.75pt solid black" 
			background-color="lightgray">
              <fo:block>
               <fo:external-graphic display-align="center" 
		content-height="scale-to-fit" height="1.5in" 
		content-width="scale-to-fit">
               <xsl:attribute name="src">
                <xsl:value-of select="concat('file:',Pare[number(2)]/Value)"/>
               </xsl:attribute>
               </fo:external-graphic>
              </fo:block>
             </fo:table-cell>
             <fo:table-cell padding="2pt" border="0.75pt solid black" 
		background-color="lightgray">
              <fo:table>
               <fo:table-column column-width="0.7in"/>
               <fo:table-column/>
               <fo:table-body>
                <xsl:for-each select="Pare">
                 <fo:table-row>
                  <fo:table-cell padding="2pt" border="0.5pt solid black" 
			background-color="lavender">
                   <fo:block font-size="7pt" padding="2pt" >
                    <xsl:value-of select="Name"/>
                   </fo:block>
                  </fo:table-cell>
                  <fo:table-cell padding="2pt" border="0.5pt solid black" 
			background-color="lightgoldenrodyellow">
                   <fo:block font-size="7pt" padding="2pt" >
                    <xsl:value-of select="Value"/>
                   </fo:block>
                  </fo:table-cell>
                 </fo:table-row>
                </xsl:for-each>
               </fo:table-body>
              </fo:table>
             </fo:table-cell>
            </fo:table-row>
           </xsl:for-each>
          </fo:table-body>
         </fo:table>
        </fo:block>
       </fo:table-cell>
      </fo:table-row>
     </fo:table-body>            
    </fo:table>
  </fo:block>

As a result, we receive the following table:

gallery.png

Conclusion

In this article, we introduced a brief description of creating the PDF report using the existing data in the program. Also we gave a brief survey of main constructions and operators of the XSL language. There are a lot of simple but rather nonstandard situations when we need to transpose tables, move between levels in the XML document, and create picture galleries.

For additional information, see the following links:

I hope that this article will be useful while developing your applications.

History

  • 06/18/2010 - Source archive updated
  • 06/22/2010 - Source archive updated

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Chief Technology Officer Apriorit Inc.
United States United States
ApriorIT is a software research and development company specializing in cybersecurity and data management technology engineering. We work for a broad range of clients from Fortune 500 technology leaders to small innovative startups building unique solutions.

As Apriorit offers integrated research&development services for the software projects in such areas as endpoint security, network security, data security, embedded Systems, and virtualization, we have strong kernel and driver development skills, huge system programming expertise, and are reals fans of research projects.

Our specialty is reverse engineering, we apply it for security testing and security-related projects.

A separate department of Apriorit works on large-scale business SaaS solutions, handling tasks from business analysis, data architecture design, and web development to performance optimization and DevOps.

Official site: https://www.apriorit.com
Clutch profile: https://clutch.co/profile/apriorit
This is a Organisation

33 members

Written By
Ukraine Ukraine
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
BugFont load problem Pin
Andrzej Gołąb15-Oct-15 0:29
Andrzej Gołąb15-Oct-15 0:29 
Question.Net Framework 4 Crash Pin
alikhanovlevon16-Apr-13 22:43
alikhanovlevon16-Apr-13 22:43 
GeneralMy vote of 5 Pin
Manoj Kumar Choubey15-Apr-12 23:42
professionalManoj Kumar Choubey15-Apr-12 23:42 
GeneralGood post Pin
Shahriar Iqbal Chowdhury/Galib7-Jun-11 10:31
professionalShahriar Iqbal Chowdhury/Galib7-Jun-11 10:31 
GeneralMy vote of 5 Pin
genka-kamikaze1-Apr-11 6:19
genka-kamikaze1-Apr-11 6:19 
GeneralMy vote of 5 Pin
Global Analyser13-Mar-11 5:59
Global Analyser13-Mar-11 5:59 
GeneralMy vote of 5 Pin
mvoronitskiy28-Jan-11 1:30
mvoronitskiy28-Jan-11 1:30 
GeneralStill, Russian characters are not showing propery Pin
ntrraorao5-Jan-11 20:35
ntrraorao5-Jan-11 20:35 
GeneralRe: Still, Russian characters are not showing propery Pin
ankurp6-Jun-11 22:34
ankurp6-Jun-11 22:34 
GeneralRe: Still, Russian characters are not showing propery Pin
Petro Vodopyan7-Jun-11 2:35
Petro Vodopyan7-Jun-11 2:35 
GeneralRe: Still, Russian characters are not showing propery Pin
konduc7-Jun-11 3:09
konduc7-Jun-11 3:09 
AnswerRe: Still, Russian characters are not showing propery Pin
MrBool2-Nov-11 10:57
MrBool2-Nov-11 10:57 
GeneralMy vote of 5 Pin
Anthony_23-Jul-10 14:19
Anthony_23-Jul-10 14:19 
GeneralRe: My vote of 5 Pin
Petro Vodopyan26-Jul-10 5:16
Petro Vodopyan26-Jul-10 5:16 
GeneralApache FOP and IKVM Pin
ben_j_scott29-Jun-10 17:11
ben_j_scott29-Jun-10 17:11 
GeneralCool technique Pin
Dmitri Nеstеruk20-Jun-10 2:10
Dmitri Nеstеruk20-Jun-10 2:10 
GeneralXslt file missing Pin
geuko18-Jun-10 9:11
geuko18-Jun-10 9:11 
AnswerRe: Xslt file missing Pin
Petro Vodopyan22-Jun-10 5:51
Petro Vodopyan22-Jun-10 5:51 
QuestionHow to do the configuration file? Pin
Member 446357515-Jun-10 6:31
Member 446357515-Jun-10 6:31 
AnswerRe: How to do the configuration file? Pin
Petro Vodopyan18-Jun-10 1:09
Petro Vodopyan18-Jun-10 1:09 
GeneralBy the way... Pin
andre1234510-Jun-10 0:35
andre1234510-Jun-10 0:35 
Have you applied this to transform the xml documentation generated from Visual Studio? Is their a generic xls file to do this?
GeneralRe: By the way... Pin
Petro Vodopyan10-Jun-10 1:16
Petro Vodopyan10-Jun-10 1:16 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.