XMLMill - convert xml to pdf with java. Generate PDF from xml/xsl.

XMLMill for Domino: UseCases

Version: 2.80 Date: November 4th, 2006
This tutorial is opened in a separate window in order to maximize the legibility of this tutorial.
To return to XMLMill, close this browser window

This page as PDFPrinter friendly pageThis guide (!) as PDF

Use case 3: Create different Invoice PDF's based on a Notes-view per customer using XMLMill as a batch-procedure (R5, R6 and R7)

Introduction

In this use case, we start from a view of customers who must receive an invoice (View To Be Invoiced). In the sample of the view you find below, for every Notes-document an invoice must be generated.

images/duinvoicesshortview.jpg
Select to enlarge

The Notes-documents consist of customer information such as customerid, customeraddress, invoice information such as reference number, total, duedate and product line information such as productid, quantity, price. As you can see in the example below the product line information is contained in different multivalue fields in the invoice document.

images/duinvoicesshortform.jpg
Select to enlarge

As mentioned before, for every Notes-document an invoice will be generated. The generation process will create a subdirectory for every customer and create as many PDF's as there are Notes-documents in status "To Be Invoiced" for that customer. The referencenumber of the invoice will be used as filename for the PDF-document. After the creation of the PDF's, the PDF's are captured and attached to the correct Notes-documents. In case you do not want to attach the PDF's, we provide a link to the corresponding PDF-file in the Notes-documents.

In order to try out this use case you need following building blocks:

  1. A trial or licensed version of XMLMill for Domino.
  2. The Domino Database "XMLMill Domino Usecases" (UseCases.nsf), which can be found in the download of XMLMill for Domino.
  3. The Notes-agent GenerateInvoices which can be found in the UseCases.nsf.
  4. The XSLT-stylesheet invoicesshort.xsl.
  5. The Batch-file invoices.cmd.

The result of this use case will be different PDF's organised in subdirectories in the output-directory. The PDF's consist of a heading containing invoice and customerinformation, a producttable containing product line information, a table containing the payment details and table with the invoice conditions. A sample of the PDF is shown in the picture below. Keep in mind, that when you try out the use cases with the trial version, you will only see 2 pages of the entire PDF. Although the entire PDF is generated, all pages will be blank except for the first 2.

images/duinvoicesshortpdf.jpg
Select to enlarge

A trial or licensed version of XMLMill for Domino

To install XMLMill for Domino, please go to the download page of the XMLMill website and choose the appropriate download distribution (.zip or .tar.gz format)

In case you want to do tests with scheduled agents, extract all the files in the download to a separate subdirectory on the filesystem of your Domino-server or a shared networkdrive on which the Domino-server has read and write access. Make sure that you inherit the directory-structure when you extract the files from your .zip or .tar.gz download to your filesystem.

In case you want to test the use cases locally, extract all the files in the download to a separate subdirectory on your pc. Make sure that you inherit the directory-structure when you extract the files from your .zip or .tar.gz download to your filesystem.

The Domino Database XMLMill Domino Usecases

Please refer to the subdirectory domino in the download to find the database Usecases.nsf . Open the database via the Notes-client and locate Use Case 3 - To Be Invoiced in the outline. Activate the view and click on the button Create Invoices. Note however that in order to make the button work correctly, you probably have to modify the paths used in the different building blocks:

  • Check all filepaths used in the Notes-agent GenerateInvoices and modify them where necessary.
  • Check all filepaths used in the invoices.cmd and modify them where necessary.

The agent GenerateInvoices

You can find the agent GenerateInvoices in the Domino-database Usecases.nsf . We will repeat and explain the different parts of this agent below.

The agent opens a file on the filesystem to write the XML-structure to. The view containing all the customers that have to be invoiced is traversed and the XML-structure is built. For every Notes-document in the view a PDF will be built. Then XMLMill-batch is called to transform the XML-file combined with the according XSLT-file into different invoice-PDFs. The PDF's will be written to the output-folder the user defines, but in this output folder subdirectories per customer will be created. This directory-structure is defined in the XSLT-stylesheet. We will come back on this later. After XMLMill has generated all PDF's the agent traverses the view again and attaches each PDF to the correct Notes-document. In case you do not want to attach the PDF's, a link is provided in the Notes-documents as well. The agent consists of following parts:

  1. Initialization of different variables and objects.
  2. Get a handle of the first document of the view that will be traversed.
  3. Start creating the XML-structure.
  4. Traversing through documents in the view.
  5. XML-ending tags.
  6. Start preparing for XMLMill-batch.
  7. Embedding and linking the generated invoice PDF's in the Notes-documents.

Initialization of different variables and objects

[001] Sub Initialize
[002] 	'1) Initializing
[003] 	
[004] 	Dim session As New notessession
[005] 	Dim db As notesdatabase
[006] 	Dim view As notesview
[007] 	Dim doc As notesdocument
[008] 	Dim folder As String
[009] 	Dim invoice As String
[010] 	Dim path As String
[011] 	Dim filename As String
[012] 	Dim filenum As Integer
[013] 	Dim taskId As Integer
[014] 	Dim i As Integer
[015] 	Dim pathName As String
[016] 	Dim rtitem As NotesRichTextItem
[017] 	Dim object As NotesEmbeddedObject	
[018] 	Dim item As notesitem	
[019] 	Dim unit As notesitem
[020] 	Dim description As notesitem
[021] 	Dim priceexcl As notesitem
[022] 	Dim qty As notesitem
[023] 	Dim subtotal As notesitem
[024] 	filenum=1
[025] 	filename= "\\lab_server\xmlmill\domino\invoices.xml"
[026] 
[027] 		

First we initialize the variables and Notes-objects we will need in this script. We define the file that will contain the XML-structure as "\\lab_server\xmlmill\domino\invoices.xml". This file can be placed somewhere on your filesystem, so don't forget to modify the filepath and filename if you want to try this out for your own. Make sure that the user running the agent is able to write to the specified directory on your filesystem.

Get a handle of the first document of the view that will be traversed

[001] 
[002] '2) get a handle of the first document of the view to traverse
[003] 	
[004] 	Set db=session.currentdatabase
[005] 	Set view=db.getview("(LUToBeInvoiced)")
[006] 	Set doc=view.getfirstdocument

The view we will loop through in order to build the XML-structure is (LUToBeInvoiced). It contains all customers for whom we need to create an invoice. The view is sorted by customerid. For every Notes-document in this view, a PDF will be generated.

Start creating the XML-structure

[001] 
[002] '3) Start creating xml
[003] 	
[004] 	Open fileName For Output As filenum
[005] 	Print #fileNum, "<?xml version=""1.0"" encoding=""windows-1252"" 
       standalone=""no""?>"
[007] 	Print #filenum,  "<?xml-stylesheet href=""invoicesshort.xsl""  
       type=""text/xsl""?>"
[009] 	Print #filenum, ""
[010] 	
[011] 	Print 
       #filenum,"<invoices>"

Before we start looping through the view, we have to open the output file and print the XML file-header to it. Here we can already define the XSLT-stylesheet <?xml-stylesheet href=""invoicesshort.xsl"" type=""text/xsl""?> that will be combined with the XML-file. You can also specify the XSLT-stylesheet in the XMLMill batch-file we will discuss later. We also define the opening element of our datastructure <invoices>. This tag contains all PDF's that will be generated.

Traversing through documents in the view

The XML-data we will build while traversing the view has following structure:

[001] <invoice>
[002] 		<invoicedata>
[003] 			<nbr>2001-1966</nbr>
[004] 			<date>December 11th. 2001</date>
[005] 			<totalexcl>2018</totalexcl>
[006] 			<tva>423.78</tva>
[007] 			<total>2441.78</total>
[008] 		</invoicedata>
[009] 		<customerdata>
[010] 			<custid>BLAUS</custid>
[011] 			<company>Blauer See Delikatessen</company>
[012] 			<contact>Hanna Moos</contact>
[013] 			<address>Forsterstr. 57</address>
[014] 			<pcode>68306</pcode>
[015] 			<city>Mannheim</city>
[016] 			<country>Germany</country>
[017] 		</customerdata>
[018] 		<lines>
[019] 			<line id="1">
[020] 				<item>63-2212</item>
[021] 				<unit>ROLL</unit>
[022] 				<description>TAPE. MASKING. 2"</description>
[023] 				<priceexcl>1.53</priceexcl>
[024] 				<qty>1000</qty>
[025] 				<subtotal>1530</subtotal>
[026] 			</line>
[027] 			<line id="2">
[028] 				<item>63-2920</item>
[029] 				<unit>EACH</unit>
[030] 				<description>BINDER. VINYL. 3"</description>
[031] 				<priceexcl>4.88</priceexcl>
[032] 				<qty>10</qty>
[033] 				<subtotal>488</subtotal>
[034] 			</line>
[035] 		</lines>
[036] 	</invoice>
[037] 	<invoice>
  • The information of all PDF's is contained in the <invoices> and </invoices> elements.
  • The information of 1 PDF is contained in the <invoice> and </invoice> elements.
  • The invoicedata (invoicenr, invoicetotal,...) can be found in the <invoicedata> and </invoicedata> elements.
  • The customerinformation (name, address,..) can be found in the </customerdata> and <customerdata> elements.
  • The information concerning all productlines is contained in the <lines> and </lines> elements.
  • The information concerning 1 productline (item, unit, description, price,...) is contained in the <line> and </line> elements.

This structure is built by the script below. We will explain it after the code:

[001] 
[002] ' 4) traversing through documents in the view
[003] 	
[004] 	While Not doc Is Nothing 
[005] 		
[006] 		Print #filenum,"  <invoice> "
[007] 		Print #filenum,"      <invoicedata>"
[008] 		
[009] 		Print #filenum,"           <nbr>" + doc.invoicenr(0)+ "</nbr>"
[010] 		Print #filenum,"           <date>" + doc.invoicedate(0)+ "</date>"
[011] 		Print #filenum,"           <duedate>" + doc.invoiceduedate(0)+ 
        "</duedate>"
[013] 		Print #filenum,"           <totalexcl>" + doc.invoicetotalexcl(0)+ 
        "</totalexcl>"
[015] 		Print #filenum,"           <vat>" + doc.invoicetva(0)+ "</vat>"
[016] 		Print #filenum,"           <total>" + doc.invoicetotal(0)+ "</total>"
[017] 		
[018] 		Print #filenum,"      </invoicedata>"
[019] 		
[020] 		Print #filenum,"      <customerdata>"
[021] 		
[022] 		Print #filenum,"           <custid>" + doc.custid(0)+ "</custid>"
[023] 		Print #filenum,"           <company>" + doc.custcompany(0)+ 
        "</company>"
[025] 		Print #filenum,"           <contact>" + doc.custcontact(0)+ 
        "</contact>"
[027] 		Print #filenum,"           <address>" + doc.custaddress(0)+ 
        "</address>"
[029] 		Print #filenum,"           <zip>" + doc.custzip(0)+ "</zip>"
[030] 		Print #filenum,"           <city>" + doc.custcity(0)+ "</city>"
[031] 		Print #filenum,"           <country>" + doc.custcountry(0)+ 
        "</country>"
[033] 		
[034] 		Print #filenum,"      </customerdata>"
[035] 		
[036] 		Print #filenum,"      <lines>"
[037] 		
[038] 		Set item=doc.getfirstitem("ProdItem")		
[039] 		Set unit=doc.getfirstitem("ProdUnit")	
[040] 		Set description=doc.getfirstitem("ProdDescription")
[041] 		Set priceexcl=doc.getfirstitem("ProdPriceExcl")
[042] 		Set qty=doc.getfirstitem("ProdQuantity")
[043] 		Set subtotal=doc.getfirstitem("Prodsubtotal")
[044] 		
[045] 		For i=0 To Ubound(item.values)
[046] 			Print #filenum,"        <line>"
[047] 			
[048] 			Print #filenum,"          <item>" + item.values(i) + "</item>"
[049] 			Print #filenum,"          <unit>" + unit.values(i) + "</unit>"
[050] 			Print #filenum,"          <description>" + description.values(i) + 
         "</description>"
[052] 			Print #filenum,"          <priceexcl>" + priceexcl.values(i) + 
         "</priceexcl>"
[054] 			Print #filenum,"          <qty>" + qty.values(i) + "</qty>"
[055] 			Print #filenum,"          <subtotal>" + subtotal.values(i) + 
         "</subtotal>"		
[057] 			
[058] 			Print #filenum,"        </line>"
[059] 		Next i
[060] 		Print #filenum,"       </lines>"
[061] 		Print #filenum,"  </invoice> "
[062] 		
[063] 		Set doc=view.getnextdocument(doc)
[064] 	Wend	

When we encounter the first document, we open the <invoice> element, since every Notes-document corresponds to 1 invoice PDF. The structure of an invoice can be divided in customerinfo, invoiceinfo and productlines-info.

The structure of the <customerdata>- and <invoicedata> elements is fairly simple. Between those tags, we capture the relevant field information such as:

for customerdata

  • customerid ("<custid>" + doc.custid(0)+ "</custid>")
  • companyname ("<company>" + doc.custcompany(0) + "</company>")
  • ...

for invoicedata

  • invoicenumber ("<nbr>" + doc.invoicenr(0) + "</nbr>")
  • invoicetotal ("<total>" + doc.invoicetotal(0) + "</total>")
  • ...

Every Notes-document contains of a producttable consisting of 1 or more productlines.The entire producttable is contained in the <lines> ... </lines> elements. Between those tags we have to loop through the Notes-multivalue fields in the producttable (item, unit, description, price excl vat, quantity and subtotal). Therefore we capture the amount of lines in the producttable and loop through each line by the following script:

[001] For i=0 To Ubound(item.values)
[002] 			Print #filenum,"        <line>"
[003] 			
[004] 			Print #filenum,"          <item>" + item.values(i) + "</item>"
[005] 			Print #filenum,"          <unit>" + unit.values(i) + "</unit>"
[006] 			Print #filenum,"          <description>" + description.values(i) + 
         "</description>"
[008] 			Print #filenum,"          <priceexcl>" + priceexcl.values(i) + 
         "</priceexcl>"
[010] 			Print #filenum,"          <qty>" + qty.values(i) + "</qty>"
[011] 			Print #filenum,"          <subtotal>" + subtotal.values(i) + 
         "</subtotal>"		
[013] 			
[014] 			Print #filenum,"        </line>"
[015] 		Next i

After looping through the multivalue-fields in the producttable, we close the producttable element </lines> and the invoice element </invoice>. This processing is repeated for all documents in the view.

XML ending tags

[001] 
[002] '5) xml-closing tags
[003] 
[004] Print #filenum,"                     </invoices>"
[005] Close fileNum	

When the last document has been processed, the remaining opened tag is closed by adding: </invoices>. The file is closed and ready to be processed by XMLMill.

Start preparing for XMLMill-batch

[001] 
[002] '6) Start preparing for XMLMill-batch
[003] 	
[004] pathName$ = "\\lab_server\xmlmill\domino\output\ended.txt"
[005] If  Dir$(pathName$, 0) <> "" Then Kill pathname$
[006] taskId% = Shell("\\lab_server\xmlmill\domino\invoices.cmd", 1)
[007] 	
[008] Do While Dir$(pathName$, 0)= ""
[009] Loop
[010] 
[011] Kill 
      pathname$	

After generating the invoice PDF's, we want the agent to capture the PDF-files and attach them to the correct Notes-document. Therefore we need a means to know when the PDF-files are ready. This is why we introduce a small text-file ended.txt. When this file is present, this means the PDF's are ready and the agent may proceed. So before we call XMLMill, we first test if this file is already on the filesystem. If it is, we delete it:

[001] If  Dir$(pathName$, 0) <> "" Then Kill 
      pathname$

Then XMLMill is called outside the Domino-environment. Therefore we issue a shell-command:

[001] taskId% = Shell("\\lab_server\xmlmill\domino\invoices.cmd", 1) 

It will start the batch-file as a normal (not minimized) window with focus. If you want to run the batch-file minimized without focus, use 6 or 7 instead of 1. After generating the PDF, the batch-file executes a command which copies a little text-file starting.txt to another file ended.txt.

The agent was waiting for the ended.txt. As long as this file isn't available on the filesystem, the agent continously checks if the file is there. If the file is available, the agent deletes the file again and moves on.

If you try this example, do not forget to alter your path to point to the right directory on your filesystem. We will come back on the content of the invoices.cmd in the next sections.

Embedding and linking the generated invoice-PDF's in the correct Notes-document

[001]  '7) Embedding and linking PDF in Notes- invoice documents
[002] 	
[003] 	Set doc=view.getfirstdocument
[004] 	
[005] 	While Not doc Is Nothing
[006] 		folder=doc.custid(0)
[007] 		invoice=doc.invoicenr(0)
[008] 		path=
[009] 		"\\lab_server\xmlmill\domino\output\" + folder + "\" + invoice + ".pdf" 
        
[011] 		If  Dir$(path, 0) <> "" Then
[012] 			Set rtitem = doc.GetFirstItem( "PDFAttachment" )
[013] 			If Not rtitem Is Nothing Then
[014] 				If (rtitem.Type = RICHTEXT ) and doc.hasembedded Then
[015] 					Forall o In rtitem.EmbeddedObjects
[016] 						Call  o.remove
[017] 					End Forall
[018] 				End If
[019] 			Else
[020] 				Set rtitem = doc.CreateRichTextItem( "PDFAttachment" )
[021] 			End If
[022] 			Set object = rtitem.EmbedObject(EMBED_ATTACHMENT,"",_
[023] 			"\\lab_server\xmlmill\domino\output\" + folder + "\" + invoice + 
         ".pdf")
[025] 			
[026] 			doc.pdflink=
[027] 			"file:\\lab_server\xmlmill\domino\output\" +  folder + "\" + invoice + 
         ".pdf"
[029] 			Call doc.save(True,False)
[030] 		End If
[031] 		Set doc=view.getnextdocument(doc)
[032] 	Wend
[033] 	End 
       Sub	

After the generation of the invoice-PDF's, we want to attach them to the correct Notes-documents. As an alternative we also show you how to create a link to the PDF-file in case you do not want to attach the file. After the PDF is attached and the link is provided, the Notes-documents will look like this:

images/duinvoicespdfcontainer.jpg
Select to enlarge

In order to capture the PDF-files on the filesystem, we start by traversing the view "(LUToBeInvoiced)" for the second time. For each Notes-document, we know where to expect the corresponding PDF-file: it is generated on a predefined output-folder in a subdirectory named after the customerid and the filename consists of the document's invoicenumber. So for each Notes-document, we can build the path to the generated PDF by the script:

[001] folder=doc.custid(0)
[002] invoice=doc.invoicenr(0)
[003] path=_
[004] "\\lab_server\xmlmill\domino\output\" + folder + "\" + invoice + ".pdf" 

Next we check if the PDF is present and if so we attach him to the current Notes-document and create a link to the PDF-file in the current Notes-document.

  • If the Richtext-field PDFAttachment already exists, we check if there are attachments present in this field. If there are, we remove them.
  • If the Richtext-field PDFAttachment doesn't exist already we create a new one.
  • Next we embed the corresponding PDF as an attachment to the field PDFAttachment.
  • At last we define a text-field PDFLink and fill it with the filepath of the PDF-file on the filesystem. In the Notes-document there is a button next to the field, invoking a urlopen-command on the value of the PDFLink-field.

The XSLT-stylesheet invoicesshort.xsl

Overview

In order to transform the XML-datastructure in this use case, a stylesheet has been created which can be found in the domino/ directory in the download.

Please open the invoicesshort.xsl in your xml-editor when you go through this chapter.

In the next sections an overview about the most important elements is given.

In the XSLT-stylesheet, you will find instruction elements to generate each of the parts. These instruction elements are grouped in templates. These templates are called either by <xsl:call-template name="..."> or by <xsl:apply-templates select="...">.

The global structure of the invoicesshort.xsl can be summarized as follows:

[001] 
[002] <?xml version="1.0" encoding="UTF-8"?>     
[003]   <xsl:stylesheet 
[004]     xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
[005]     xmlns:ml="http://www.xmlmill.com/XSL/Format" 
[006]     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
[007]     xsi:schemaLocation="http://www.xmlmill.com/XSL/Format xmlmill.xsd"
[008]     version="1.0" >
[009]   <xsl:template match="/">
[010]     <ml:documents>
[011]       <xsl:for-each select="invoices/invoice">
[012]         <ml:document>
[013]           <xsl:call-template name="print-info"/>
[014]           <xsl:call-template name="print-pagetemplate"/>
[015]           <xsl:call-template name="print-content"/>
[016]         </document>
[017]       </xsl:for-each>
[018]     </ml:documents>
[019]   </xsl:template>
[020] 
[021]     <xsl:template name="print-info">
[022]   ...
[023]   </xsl:template>
[024]   
[025]     <xsl:template name="print-pagetemaplate">
[026]   ...
[027]   </xsl:template>  
[028]   
[029]     <xsl:template name="print-content">
[030]   ...
[031]   </xsl:template>  
[032] 
[033]     <xsl:template name="...">
[034]   ...
[035]   </xsl:template>  
[036] 
[037] </xsl:stylesheet>

The <?xml?> prolog

The prolog is the first line of a xml document defining the encoding of the document:

[001] <?xml version="1.0" 
      encoding="UTF-8"?>

This is important as you need an editor that supports the specified encoding in order to correctly see the content of the document and write any changes back in the correct encoding (the one defined here). If the encoding is UTF-8 and you edit the xml document with a editor that only supports ASCII you will see 'strange' characters.

  • It is strongly recommended to use a xml-editor which supports different encodings.

The <xsl:stylesheet> element

The <xsl:stylesheet> element defines a reference to the ml: namespace that contains all valid XMLMill elements and their attributes.

[001] 
[002] <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
[003]                 xmlns:ml="http://www.xmlmill.com/XSL/Format" 
[004]                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
[005]                 xsi:schemaLocation="http://www.xmlmill.com/XSL/Format 
                      xmlmill.xsd" 
[007]                 version="1.0" >
[008]   <xsl:output method="xml" indent="yes" encoding="UTF-16"/>     
[009]   <xsl:output/>
[010]   <xsl:decimal-format decimal-separator="," grouping-separator="."/> 
[011]   ...
[012]           

1

Define the ml: namespace by referring to the correct namespace name. In this case the namespace name is a URI mapping to: http://www.xmlmill.com/XSL/Format.

2

Define an instance of a XMLSchema.

3

Define where the instance of the XMLSchema is located. The XMLSchema is referenced using the namespace name (http://www.xmlmill.com/XSL/Format). The second parameter (xmlmill.xsd) defines the actual location and name of .xsd schema.

4

When the xml document is transformed with the xsl stylesheet, an internal .mill document is generated, used to generate the .pdf document. The <output> element defines the characteristics of this intermediate .mill file.

5

Optionally the decimal symbol is defined as ',' and the thousands-grouping-separator is defined as '.' in the <xsl:decimal-format> element.

  • The docs/xsd/ directory contains a default xsl stylesheet and the xmlmill.xsd and xslt.xsd file. Please keep these two files in the same directory as the xslt.xsd file is referenced in the xmlmill.xsd file.

The <ml:documents> element

[001] <xsl:template match="/">
[002]   <ml:documents whitespace-collapse="on" >
[003]     <xsl:for-each select="invoices/invoice">
[004]       <ml:document>
[005]         <xsl:attribute name="file">output\
[006]         <xsl:value-of select="customerdata/custid"/>\
[007]         <xsl:value-of select="invoicedata/nbr"/gt;.pdf</xsl:attribute>
[008]         <xsl:call-template name="print-info"/>
[009]         <xsl:call-template name="print-pagetemplate"/>
[010]         <xsl:call-template name="print-content"/>
[011]       </ml:document>
[012]     </xsl:for-each>
[013]   </ml:documents>
[014] </xsl:template>

The processing of the stylesheet always start at the expression <xsl:template match="/">.

As a result the <mldocuments> element is processed. The <documents> element is the root tag of the ml: namespace. It can contain multiple <ml:document> elements each contaning elements to define a separate pdf document.

Next the pointer is moved to the invoices/invoice-branch in the XML-file in order to iterate over the different invoice-branches. This is done by the <xsl:for-each>-instruction element:

[001] <xsl:for-each select="invoices/invoice">
[002]    ...
[003] </xsl:for-each>

For every invoices/invoice-branch in the XML-file a new PDF has to be created and a filename must be provided. This is done by opening the <ml:document> element and assigning the attribute file the value of the filename. We want to generate the filename dynamically: it will be saved in a subdirectory of the outputdirectory of XMLMill. The subdirectory will be the customerid and the filename will consist of the referencenumber of the invoice. We capture the value of the subdirectory by the instruction element: <xsl:value-of select="customerdata/custid"/> and the value of the filename is built by: <xsl:value-of select="invoicedata/nbr"/>. So the customerid is in the XML-file found on the branch customerdata/custid and the referencenumber is in the XML-file found on the branch invoicedata/nbr. Both values are then used to form the entire path: output\custid\referencenumber.pdf. The entire instruction element looks like this:

[001] <ml:document> 
[002]   <xsl:attribute name="file">
[003]     output\<xsl:value-of select="customerdata/custid"/>\<xsl:value-of 
          select="invoicedata/nbr"/>.pdf
[005]   </xsl:attribute>

Further remaining instruction elements are grouped in templates. They are called from within the <document> element or from within a template called by the <document> element. The templates which are directly called from within the <document> element are:

  • Template "print-info"
  • Template "print-pagetemplate"
  • Template "print-content"

Deze templates together with their dependents are processed. We will explain them in the next sections.

Processing so far is repeated for every invoice. After the instruction elements, the remaining opened tags are closed:

[001] 
[002]         ...
[003]       </ml:document>
[004]     </xsl:for-each>
[005]   </documents>
[006] </xsl:template>

The <ml:document> element

From within the <ml:document> element grouped instruction elements called templates are executed. They can be called either directly from the <ml:document> element or nested from within a called template. These templates will build the different elements of the PDF:

  • The template print-info is called from within the <ml:document> element and defines the indexing properties of the PDF.
  • The template print-pagetemplate is called from within the <ml:document> element and defines the general page-layout of the PDF document, using the <ml:simple-page-master> element..
  • The template print-content is called from within the <ml:document> element and it is the template from which all processing of the invoice-content is invoked. It starts with the <ml:page-sequence> element which is an important child element of the <document> element. In the <ml:page-sequence> element we specify the body of the required processing (for more explanation of the <ml:page-sequence> see below). Different nested templates are called from within the print-content template: the print-invoiceheader, the print-invoicetable, the acc-details, the print-note and the print-lastpage templates.
  • From within the print-content template the instruction element <xsl:call-template name="print-invoiceheader"/> calls the template responsible for building the table containing the invoiceinfo and the customerinfo.
  • From within the print-content template the instruction element <xsl:call-template name="print-invoicetable"/> calls the template responsible for building the table containing the different productlines. In this template the creation of the headerrow of the producttable is followed by an <apply-templates select="lines"> to iterate over the different "lines"-branches in the XML-file and process the productlines. After the processing of the productlines the instruction element <xsl:call-template name="total-details"/> finishes the producttable by appending a row containing the totals.
  • From within the print-content template the instruction element <xsl:call-template name="acc-details"/> calls the template responsible for building a table with payment conditions below the producttable.
  • From within the print-content template the instruction element <xsl:call-template name="print-note"/> calls the template responsible for building a table with general invoice conditions below the payment conditions.
  • Finally,from within the print-content template the instruction element <xsl:call-template name="print-lastpage"/> calls the template responsible for printing the final logo with the addressinformation of the firm.

The relations between the different templates is summarized in the picture below.

images/duinvoicesshort.jpg
Select to enlarge

In the next sections we will elaborate on these different templates.

Template "print-info"

The first template which is called from within the <document> element is the template "print-info".

[001] <xsl:template name="print-info">
[002]   <meta-info>
[003]     <meta-title>Invoices </meta-title>
[004]     <meta-subject>Invoices</meta-subject>
[005]     <meta-author>xmlmill</meta-author>
[006]     <meta-keywords>www.xmlmill.com</meta-keywords>
[007]   </meta-info>
[008] </xsl:template>

The <meta-info> element is a child of the <ml:document> element. Here we define the indexing information that can be found in the properties of the PDF. In the PDF you must choose "File-Document Info - General" to view these properties. We specify the title, the subject, the author and some keywords for the PDF.

Template "print-pagetemplate"

The second template which is called from within the <ml:document> element is the template "print-pagetemplate".

[001] <xsl:template name="print-pagetemplate">
[002]     <ml:layout-master-set>
[003]       <ml:simple-page-master master-name="content-pages" ...>
[004]         <ml:region-body column-count="1" .../>
[005]         <ml:region-before .../>
[006]         <ml:region-after .../>
[007]         <ml:region-start .../>
[008]         <ml:region-end .../>
[009]       </ml:simple-page-master>
[010]     </ml:layout-master-set>
[011] </xsl:template>

The <ml:layout-master-set> element contains ml:simple-page-master element(s) which define the page-layout of the page where this <ml:layout-master-set> is applied to (referenced by the <ml:page-sequence>).

A page can contain up to five 'regions', of which 3 are mostly used:

<ml:region-before>

The upper region on the page (mainly used to define the 'header' of a page).

<ml:region-body>

This is the center region on the page to define the content of a page.

<ml:region-after>

The bottom region on the page (mainly used to define the 'footer' of a page).

  • For more information regarding these elements please visit the dtdguide.pdf document contained in the docs/ directory in the download.(
  • Also visit the examples in the samples/mill and samples/xmlxsl directory to consult how these elements can be used.

Template "print-content"

The third template which is called from within the <ml:document> element is the template "print-content", with following content structure:

[001] <xsl:template name="print-content">
[002]   <ml:page-sequence initial-page-number="1" 
        master-reference="content-pages">
[004]     <!-- The static content (the header ). -->     
[005]       <ml:static-content flow-name="xsl-region-before">
[006]         <ml:external-graphic src="images\magnolia.jpg" .../>
[007]       </ml:static-content>
[008]       <!-- The static content (the footer ). -->       
[009]       <ml:static-content flow-name="xsl-region-after">
[010]         <ml:textbox valign="top" ...> ... </ml:textbox>
[011]         <ml:textbox valign="top" ...>
[012]           <page-number/>
[013]         </ml:textbox>
[014]       </ml:static-content>
[015]       <!-- The flowing content. -->      
[016]       <ml:flow flow-name="xsl-region-body">
[017]         <xsl:call-template name="print-invoiceheader"/>
[018]         <xsl:call-template name="print-invoicetable"/>
[019]         <xsl:call-template name="acc-details"/>
[020]         <xsl:call-template name="print-note"/>
[021]         <ml:textbox break-after="page"/>
[022]         <xsl:call-template name="print-lastpage"/>
[023]       </ml:flow>
[024]     </ml:page-sequence>
[025]   </xsl:template>

The <page-sequence> element contains the content of the pages to generate. It contains two important elements:

<ml:static-content>

This element defines the content that should be repeated on each page (like a header of footer). THe flow-name refers to a region defined in the simple-page-master that is referenced by the master-reference attribute in the parent <ml:page-sequence>.

<ml:flow>

This element defines the 'flowing' content of a pages, adding new pages to the document if needed.

The bulk of the elements will be children of the <ml:flow> element describing the content of a page (and subsequent page if the content does not fit in one page).

The <ml:flow> calls following templates:

  1. The instruction element <xsl:call-template name="print-invoiceheader"/> calls the template responsible for building the table containing the invoiceinfo and the customerinfo. The invoicedata (invoicedate and invoicenumber are placed to the left of the table. The customerinfo (address information) is placed to the right of the table.
  2. The instruction element <xsl:call-template name="print-invoicetable"/> calls the template responsible for building the table containing the different productlines. In this template the creation of the headerrow of the producttable is followed by an <xsl:apply-templates select="lines"> to iterate over the different "lines"-branches in the XML-file and process the productlines. After the processing of the productlines the instruction element <xsl:call-template name="total-details"/> finishes the producttable by appending a row containing the totals.
  3. The instruction element <xsl:call-template name="acc-details"/> calls the template responsible for building a table with payment conditions below the producttable.
  4. The instruction element <xsl:call-template name="print-note"/> calls the template responsible for building a table with general invoice conditions below the payment conditions.
  5. Finally, a page break is forced and the instruction element <xsl:call-template name="print-lastpage"/> calls the template responsible for printing the final logo with the addressinformation of the firm.

These templates will call other templates:

  • print-invoiceheader
  • print-invoicetable
  • lines
  • total-details
  • acc-details
  • print-note
  • print-lastpage

Please study the content of the templates to know how the document is generated.

  • For more information regarding these elements please visit the dtdguide.pdf document contained in the docs/ directory in the download.(
  • Also visit the examples in the samples/mill and samples/xmlxsl directory to consult how these elements can be used.

The XMLMill batch-file invoices.cmd

The XMLMill batch-file calls XMLMill outside the Domino-environment and tells the system where to find XMLMill and the required XML- and XSLT-files. To try out this example, you should:

  • modify the products.cmd file, so that 1) the classpath points to the directory where you installed XMLMill and 2) all required jar-files are included.
  • make sure the JAVA_HOME variable points to the directory where your java.exe resides.
  • use the correct parameters (see below) so that the correct xml-file and XSL-file are used.

Please find below an overview of a sample batch file:

[001] @echo off
[002] REM -------------------
[003] REM -- XMLMILL BATCH --
[004] REM -------------------
[005] 
[006] REM PLEASE ADAPT CLASSPATH TO INCLUDE JAR FILEs CORRECLY.
[007] REM PLEASE ADAPT DIR POINTING TO JAVA.EXE.
[008] 
[009] set JAVA_HOME="\\labserver\progs\JavaSoft\JRE\1.3.1\bin\java"
[010] set CLASSPATH=\\lab_server\xmlmill\lib\sun-xercesImpl.jar
[011] set CLASSPATH=%CLASSPATH%;\\lab_server\xmlmill\lib\sun-xalan.jar
[012] set CLASSPATH=%CLASSPATH%;\\lab_server\xmlmill\lib\sun-jaxp-api.jar
[013] set CLASSPATH=%CLASSPATH%;\\lab_server\xmlmill\lib\sun-sax.jar
[014] set CLASSPATH=%CLASSPATH%;\\lab_server\xmlmill\lib\sun-dom.jar
[015] set CLASSPATH=%CLASSPATH%;\\lab_server\xmlmill\lib\xmlmill.jar
[016] set CLASSPATH=%CLASSPATH%;\\lab_server\xmlmill\lib\log4j.jar
[017] @echo on
[018] %JAVA_HOME% com.xmlmill.batch.Main  -m 
      \\lab_server\xmlmill\domino\invoices.xml -s 
      \\lab_server\xmlmill\domino\invoicesshort.xsl -l invoices.log -v
[021] 
[022] copy \\lab_server\xmlmill\domino\output\starting.txt 
      \\lab_server\xmlmill\domino\output\ended.txt 

Note that instructions beginning with REM are not executed.

The script calls Java and the com.xmlmill.batch.Main class with the appropriate flags and arguments. You find the command line that processes the products XML- and XSLT-file below:

[001] %JAVA_HOME% com.xmlmill.batch.Main  -m 
      \\lab_server\xmlmill\domino\invoices.xml -s 
      \\lab_server\xmlmill\domino\invoicesshort.xsl -l invoices.log -v

The used flags are explained below:

-v

Enable verbose output.If this flag is omitted, no messages will be printed to the console.

-m

Defines the XML-file or directory containing XML files. Multiple -m files can be defined.

-s

Defines the XSLT-file that will be used to transform the XML-file(s). If specified this .XSL file will overrule any .XSL file defined in the .XML file (defined in the <?xml-stylesheet ...?> tag).

-l

Defines the (existing) logfolder and/or logfile name. If a filename is defined it should end with .log. If the log-folder only contains a filename (ending in .log), the log will be written in the directory of the first xml-file that is processed. If no log-file is defined, all log messages (if any) will be send to the screen.

-o

Defines the (existing) outputfolder. If no outputfolder is defined, the output will be written in the directory defined by file attribute of the <output> tag of the first XML-file processed.

Copyright © 2001 - 2012. All rights reserved. XMLMill and XMLMill logo are trademarks of Pecunia Data Systems, bvba.
Powered by Apache CocoonPowered by XMLMill