Oracle Business Intelligence Publisher Online Help Release 10.1.3.4 Part Number E12602-01 | Contents | Previous | Next |
This chapter covers the following topics:
This chapter is aimed at developers who wish to create programs or applications that interact with BI Publisher through its application programming interface. This information is meant to be used in conjunction with the Javadocs available with your installation files.
This section assumes the reader is familiar with Java programming, XML, and XSL technologies.
BI Publisher is made up of the following core API components:
PDF Form Processing Engine
Merges a PDF template with XML data (and optional metadata) to produce PDF document output.
RTF Processor
Converts an RTF template to XSL in preparation for input to the FO Engine.
FO Engine
Merges XSL and XML to produce any of the following output formats: Excel (HTML), PDF, RTF, or HTML.
PDF Document Merger
Provides optional postprocessing of PDF files to merge documents, add page numbering, and set watermarks.
eText Processor
Converts RTF eText templates to XSL and merges the XSL with XML to produce text output for EDI and EFT transmissions.
Document Processor (XML APIs)
Provides batch processing functionality to access a single API or multiple APIs by passing a single XML file to specify template names, data sources, languages, output type, output names, and destinations.
The following diagram illustrates the template type and output type options for each core processing engine:
In order to use the BI Publisher APIs you need the following JAR files in your class path:
xdocore.jar - the core BI Publisher library
aolj.jar - although this is an Oracle E-Business Suite library, it is required for standalone implementations as well
i18nAPI_v3.jar - the i18n library used for localization functions
xdoparser.jar - the scalable XML parser and XSLT 2.0 engine
xmlparserv2-904.jar - the main XML parser/XSLT engine
bipres.jar - a charting library
bicmn.jar - a charting library
jewt4.jar - a charting support library
share.jar - a charting support library
collections.jar - you only need this if you are working with the delivery APIs or bursting engine.
If you are using Oracle JDeveloper, then the charting and XML Parser libraries will already be available to you. However, it is recommended that you create a directory with all of the required JAR files to use as a custom library in your project. This will help prevent unexpected errors after deployment.
You have the following options for obtaining the required libraries:
Locate the xmlpserver.ear file in your installation. A typical path would be:
C:\Ora_Home\xmlp\xmlpserver.ear
Unpack this file to find the xmlpserver.war file. Unpack this file to find the WEB-INF directory. The lib directory under WEB-INF contains the JAR files. A sample path once both files are unpacked would be: C:\OraHome\xmlp\xmlpserver\xmlpserver\WEB-INF\lib
Download the and install the Template Builder for Microsoft Word Add-in. The JAR files are packaged with the Template Builder in the jlib library under the install directory.
A sample path to the jlib would be:
C:\Program Files\Oracle\BI Publisher\BI Publisher Desktop\Template Builder for Word\jlib
Note that the Template Builder does include the aolj.jar. You must use the versioninfo.jar instead.
The PDF Form Processing Engine creates a PDF document by merging a PDF template with an XML data file. This can be done using file names, streams, or an XML data string.
As input to the PDF Processing Engine you can optionally include an XML-based Template MetaInfo (.xtm) file. This is a supplemental template to define the placement of overflow data.
The FO Processing Engine also includes utilities to provide information about your PDF template. You can:
Retrieve a list of field names from a PDF template
Generate the XFDF data from the PDF template
Convert XML data into XFDF using XSLT
XML data can be merged with a PDF template to produce a PDF output document in three ways:
Using input/output file names
Using input/output streams
Using an input XML data string
You can optionally include a metadata XML file to describe the placement of overflow data in your template.
Input:
Template file name (String)
XML file name (String)
Metadata XML file name (String)
Output:
PDF file name (String)
import oracle.apps.xdo.template.FormProcessor;
.
.
FormProcessor fProcessor = new FormProcessor();
fProcessor.setTemplate(args[0]); // Input File (PDF) name
fProcessor.setData(args[1]); // Input XML data file name
fProcessor.setOutput(args[2]); // Output File (PDF) name
fProcessor.setMetaInfo(args[3]); // Metadata XML File name You can omit this setting when you do not use Metadata.
fProcessor.process();
Input:
PDF Template (Input Stream)
XML Data (Input Stream)
Metadata XML Data (Input Stream)
Output:
PDF (Output Stream)
import java.io.*;
import oracle.apps.xdo.template.FormProcessor;
.
.
.
FormProcessor fProcessor = new FormProcessor();
FileInputStream fIs = new FileInputStream(originalFilePath); // Input File
FileInputStream fIs2 = new FileInputStream(dataFilePath); // Input Data
FileInputStream fIs3 = new FileInputStream(metaData); // Metadata XML Data
FileOutputStream fOs = new FileOutputStream(newFilePath); // Output File
fProcessor.setTemplate(fIs);
fProcessor.setData(fIs2); // Input Data
fProcessor.setOutput(fOs);
fProcessor.setMetaInfo(fIs3);
fProcessor.process();
fIs.close();
fOs.close();
Input:
Template file name (String)
XML data (String)
Metadata XML file name (String)
Output:
PDF file name (String)
import oracle.apps.xdo.template.FormProcessor;
.
.
.
FormProcessor fProcessor = new FormProcessor();
fProcessor.setTemplate(originalFilePath); // Input File (PDF) name
fProcessor.setDataString(xmlContents); // Input XML string
fProcessor.setOutput(newFilePath); // Output File (PDF) name
fProcessor.setMetaInfo(metaXml); // Metadata XML File name You can omit this setting when you do not use Metadata.
fProcessor.process();
Use the FormProcessor.getFieldNames() API to retrieve the field names from a PDF template. The API returns the field names into an Enumeration object.
Input:
PDF Template
Output:
Enumeration Object
import java.util.Enumeration;
import oracle.apps.xdo.template.FormProcessor;
.
.
.
FormProcessor fProcessor = new FormProcessor();
fProcessor.setTemplate(filePath); // Input File (PDF) name
Enumeration enum = fProcessor.getFieldNames();
while(enum.hasMoreElements()) {
String formName = (String)enum.nextElement();
System.out.println("name : " + formName + " , value : " + fProcessor.getFieldValue(formName));
}
XML Forms Data Format (XFDF) is a format for representing forms data and annotations in a PDF document. XFDF is the XML version of Forms Data Format (FDF), a simplified version of PDF for representing forms data and annotations. Form fields in a PDF document include edit boxes, buttons, and radio buttons.
Use this class to generate XFDF data. When you create an instance of this class, an internal XFDF tree is initialized. Use append() methods to append a FIELD element to the XFDF tree by passing a String name-value pair. You can append data as many times as you want.
This class also allows you to append XML data by calling appendXML() methods. Note that you must set the appropriate XSL stylesheet by calling setStyleSheet() method before calling appendXML() methods. You can append XML data as many times as you want.
You can retrieve the internal XFDF document at any time by calling one of the following methods: toString(), toReader(), toInputStream(), or toXMLDocument().
The following is a sample of XFDF data:
<?xml version="1.0" encoding="UTF-8"?>
<xfdf xmlns="http://ns.adobe.com/xfdf/" xml:space="preserve">
<fields>
<field name="TITLE">
<value>Purchase Order</value>
</field>
<field name="SUPPLIER_TITLE">
<value>Supplie</value>
</field>
...
</fields>
The following code example shows how the API can be used:
import oracle.apps.xdo.template.FormProcessor;
import oracle.apps.xdo.template.pdf.xfdf.XFDFObject;
.
.
.
FormProcessor fProcessor = new FormProcessor();
fProcessor.setTemplate(filePath); // Input File (PDF) name
XFDFObject xfdfObject = new XFDFObject(fProcessor.getFieldInfo());
System.out.println(xfdfObject.toString());
Use an XSL stylesheet to convert standard XML to the XFDF format. Following is an example of the conversion of sample XML data to XFDF:
Assume your starting XML has a ROWSET/ROW format as follows:
<ROWSET>
<ROW num="0">
<SUPPLIER>Supplier</SUPPLIER>
<SUPPLIERNUMBER>Supplier Number</SUPPLIERNUMBER>
<CURRCODE>Currency</CURRCODE>
</ROW>
...
</ROWSET>
From this XML you want to generate the following XFDF format:
<fields>
<field name="SUPPLIER1">
<value>Supplier</value>
</field>
<field name="SUPPLIERNUMBER1">
<value>Supplier Number</value>
</field>
<field name="CURRCODE1">
<value>Currency</value>
</field>
...
</fields>
The following XSLT will carry out the transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<fields>
<xsl:apply-templates/>
</fields>
</xsl:template>
<!-- Count how many ROWs(rows) are in the source XML file. -->
<xsl:variable name="cnt" select="count(//row|//ROW)" />
<!-- Try to match ROW (or row) element.
<xsl:template match="ROW/*|row/*">
<field>
<!-- Set "name" attribute in "field" element. -->
<xsl:attribute name="name">
<!-- Set the name of the current element (column name)as a value of the current name attribute. -->
<xsl:value-of select="name(.)" />
<!-- Add the number at the end of the name attribute value if more than 1 rows found in the source XML file.-->
<xsl:if test="$cnt > 1">
<xsl:number count="ROW|row" level="single" format="1"/>
</xsl:if>
</xsl:attribute>
<value>
<!--Set the text data set in the current column data as a text of the "value" element. -->
<xsl:value-of select="." />
</value>
</field>
</xsl:template>
</xsl:stylesheet>
You can then use the XFDFObject to convert XML to the XFDF format using an XSLT as follows:
import java.io.*;
import oracle.apps.xdo.template.pdf.xfdf.XFDFObject;
.
.
.
XFDFObject xfdfObject = new XFDFObject();
xfdfObject .setStylesheet(new BufferedInputStream(new FileInputStream(xslPath))); // XSL file name
xfdfObject .appendXML( new File(xmlPath1)); // XML data file name
xfdfObject .appendXML( new File(xmlPath2)); // XML data file name
System.out.print(xfdfObject .toString());
The RTF processor engine takes an RTF template as input. The processor parses the template and creates an XSL-FO template. This can then be passed along with a data source (XML file) to the FO Engine to produce PDF, HTML, RTF, or Excel (HTML) output.
Use either input/output file names or input/output streams as shown in the following examples:
Input:
RTF file name (String)
Output:
XSL file name (String)
import oracle.apps.xdo.template.FOProcessor;
.
.
.
public static void main(String[] args)
{
RTFProcessor rtfProcessor = new RTFProcessor(args[0]); //input template
rtfProcessor.setOutput(args[1]); // output file
rtfProcessor.process();
System.exit(0);
}
Input:
RTF (InputStream)
Output:
XSL (OutputStream)
import oracle.apps.xdo.template.FOProcessor;
.
.
.
public static void main(String[] args)
{
FileInputStream fIs = new FileInputStream(args[0]); //input template
FileOutputStream fOs = new FileOutputStream(args[1]); // output
RTFProcessor rtfProcessor = new RTFProcessor(fIs);
rtfProcessor.setOutput(fOs);
rtfProcessor.process();
// Closes inputStreams outputStream
System.exit(0);
}
The FO Processor Engine is BI Publisher's implementation of the W3C XSL-FO standard. It does not represent a complete implementation of every XSL-FO component. For a list of supported XSL-FO elements, see Supported XSL-FO Elements.
The FO Processor can generate output in PDF, RTF, HTML, or Excel (HTML) from either of the following two inputs:
Template (XSL) and Data (XML) combination
FO object
Both input types can be passed as file names, streams, or in an array. Set the output format by setting the setOutputFormat method to one of the following:
FORMAT_EXCEL
FORMAT_HTML
FORMAT_PDF
FORMAT_RTF
An XSL-FO utility is also provided that creates XSL-FO from the following inputs:
XSL file and XML file
Two XML files and two XSL files
Two XSL-FO files (merge)
The FO object output from the XSL-FO utility can then be used as input to the FO processor.
BI Publisher utilizes the Unicode BiDi algorithm for BiDi layout. Based on specific values for the properties writing-mode, direction, and unicode bidi, the FO Processor supports the BiDi layout.
The writing-mode property defines how word order is supported in lines and order of lines in text. That is: right-to-left, top-to-bottom or left-to-right, top-to-bottom. The direction property determines how a string of text will be written: that is, in a specific direction, such as right-to-left or left-to-right. The unicode bidi controls and manages override behavior.
The FO Processor supports a two-level font fallback mechanism. This mechanism provides control over what default fonts to use when a specified font or glyph is not found. BI Publisher provides appropriate default fallback fonts automatically without requiring any configuration. BI Publisher also supports user-defined configuration files that specify the default fonts to use. For glyph fallback, the default mechanism will only replace the glyph and not the entire string.
For headers and footers that require more space than what is defined in the template, the FO Processor extends the regions and reduces the body region by the difference between the value of the page header and footer and the value of the body region margin.
This feature supports a "Z style" of horizontal table break. The horizontal table break is not sensitive to column span, so that if the column-spanned cells exceed the page (or area width), the FO Processor splits it and does not apply any intelligent formatting to the split cell.
The following figure shows a table that is too wide to display on one page:
The following figure shows one option of how the horizontal table break will handle the wide table. In this example, a horizontal table break is inserted after the third column.
The following figure shows another option. The table breaks after the third column, but includes the first column with each new page.
The following example shows how to use the FO Processor to create an output file using file names.
Input:
XML file name (String)
XSL file name (String)
Output:
Output file name (String)
import oracle.apps.xdo.template.FOProcessor;
.
.
.
public static void main(String[] args)
{
FOProcessor processor = new FOProcessor();
processor.setData(args[0]); // set XML input file
processor.setTemplate(args[1]); // set XSL input file
processor.setOutput(args[2]); //set output file
processor.setOutputFormat(FOProcessor.FORMAT_PDF);
// Start processing
try
{
processor.generate();
}
catch (XDOException e)
{
e.printStackTrace();
System.exit(1);
}
System.exit(0);
}
The processor can also be used with input/output streams as shown in the following example:
Input:
XML data (InputStream)
XSL data (InputStream)
Output:
Output stream (OutputStream)
import java.io.InputStream;
import java.io.OutputStream;
import oracle.apps.xdo.template.FOProcessor;
.
.
.
public void runFOProcessor(InputStream xmlInputStream,
InputStream xslInputStream,
OutputStream pdfOutputStream)
{
FOProcessor processor = new FOProcessor();
processor.setData(xmlInputStream);
processor.setTemplate(xslInputStream);
processor.setOutput(pdfOutputStream);
// Set output format (for PDF generation)
processor.setOutputFormat(FOProcessor.FORMAT_PDF);
// Start processing
try
{
processor.generate();
}
catch (XDOException e)
{
e.printStackTrace();
System.exit(1);
}
System.exit(0);
}
An array of data and template combinations can be processed to generate a single output file from the multiple inputs. The number of input data sources must match the number of templates that are to be applied to the data. For example, an input of File1.xml, File2.xml, File3.xml and File1.xsl, File2.xsl, and File3.xsl will produce a single File1_File2_File3.pdf.
Input:
XML data (Array)
XSL data (template) (Array)
Output:
File Name (String)
import java.io.InputStream;
import java.io.OutputStream;
import oracle.apps.xdo.template.FOProcessor;
.
.
.
public static void main(String[] args)
{
String[] xmlInput = {"first.xml", "second.xml", "third.xml"};
String[] xslInput = {"first.xsl", "second.xsl", "third.xsl"};
FOProcessor processor = new FOProcessor();
processor.setData(xmlInput);
processor.setTemplate(xslInput);
processor.setOutput("/tmp/output.pdf); //set (PDF) output file
processor.setOutputFormat(FOProcessor.FORMAT_PDF); processor.process();
// Start processing
try
{
processor.generate();
}
catch (XDOException e)
{
e.printStackTrace();
System.exit(1);
}
}
Use the XSL-FO Utility to create an XSL-FO output file from input XML and XSL files, or to merge two XSL-FO files. Output from this utility can be used to generate your final output. See Generating Output from an XSL-FO file.
Input:
XML file
XSL file
Output:
XSL-FO (InputStream)
import oracle.apps.xdo.template.fo.util.FOUtility;
.
.
.
public static void main(String[] args)
{
InputStream foStream;
// creates XSL-FO InputStream from XML(arg[0])
// and XSL(arg[1]) filepath String
foStream = FOUtility.createFO(args[0], args[1]);
if (mergedFOStream == null)
{
System.out.println("Merge failed.");
System.exit(1);
}
System.exit(0);
}
Input:
XML File 1
XML File 2
XSL File 1
XSL File 2
Output:
XSL-FO (InputStream)
import oracle.apps.xdo.template.fo.util.FOUtility;
.
.
.
public static void main(String[] args)
{
InputStream firstFOStream, secondFOStream, mergedFOStream;
InputStream[] input = InputStream[2];
// creates XSL-FO from arguments
firstFOStream = FOUtility.createFO(args[0], args[1]);
// creates another XSL-FO from arguments
secondFOStream = FOUtility.createFO(args[2], args[3]);
// set each InputStream into the InputStream Array
Array.set(input, 0, firstFOStream);
Array.set(input, 1, secondFOStream);
// merges two XSL-FOs
mergedFOStream = FOUtility.mergeFOs(input);
if (mergedFOStream == null)
{
System.out.println("Merge failed.");
System.exit(1);
}
System.exit(0);
}
Input:
Two XSL-FO file names (Array)
Output:
One XSL-FO (InputStream)
import oracle.apps.xdo.template.fo.util.FOUtility;
.
.
.
public static void main(String[] args)
{
InputStream mergedFOStream;
// creates Array
String[] input = {args[0], args[1]};
// merges two FO files
mergedFOStream = FOUtility.mergeFOs(input);
if (mergedFOStream == null)
{
System.out.println("Merge failed.");
System.exit(1);
}
System.exit(0);
}
The FO Processor can also be used to process an FO object to generate your final output. An FO object is the result of the application of an XSL-FO stylesheet to XML data. These objects can be generated from a third party application and fed as input to the FO Processor.
The processor is called using a similar method to those already described, but a template is not required as the formatting instructions are contained in the FO.
Input:
FO file name (String)
Output:
PDF file name (String)
import oracle.apps.xdo.template.FOProcessor;
.
.
.
public static void main(String[] args) {
FOProcessor processor = new FOProcessor();
processor.setData(args[0]); // set XSL-FO input file
processor.setTemplate((String)null);
processor.setOutput(args[2]); //set (PDF) output file
processor.setOutputFormat(FOProcessor.FORMAT_PDF);
// Start processing
try
{
processor.generate();
}
catch (XDOException e)
{
e.printStackTrace();
System.exit(1);
}
System.exit(0);
}
Input:
FO data (InputStream)
Output:
Output (OutputStream)
import java.io.InputStream;
import java.io.OutputStream;
import oracle.apps.xdo.template.FOProcessor;
.
.
.
public void runFOProcessor(InputStream xmlfoInputStream,
OutputStream pdfOutputStream)
{
FOProcessor processor = new FOProcessor();
processor.setData(xmlfoInputStream);
processor.setTemplate((String)null);
processor.setOutput(pdfOutputStream);
// Set output format (for PDF generation)
processor.setOutputFormat(FOProcessor.FORMAT_PDF);
// Start processing
try
{
processor.generate();
}
catch (XDOException e)
{
e.printStackTrace();
System.exit(1);
}
}
Pass multiple FO inputs as an array to generate a single output file. A template is not required, therefore set the members of the template array to null, as shown in the example.
Input:
FO data (Array)
Output:
Output File Name (String)
import java.lang.reflect.Array;
import oracle.apps.xdo.template.FOProcessor;
.
.
.
public static void main(String[] args)
{
String[] xmlInput = {"first.fo", "second.fo", "third.fo"};
String[] xslInput = {null, null, null}; // null needs for xsl-fo input
FOProcessor processor = new FOProcessor();
processor.setData(xmlInput);
processor.setTemplate(xslInput);
processor.setOutput("/tmp/output.pdf); //set (PDF) output file
processor.setOutputFormat(FOProcessor.FORMAT_PDF); processor.process();
// Start processing
try
{
processor.generate();
}
catch (XDOException e)
{
e.printStackTrace();
System.exit(1);
}
}
The PDF Document Merger class provides a set of utilities to manipulate PDF documents. Using these utilities, you can merge documents, add page numbering, set backgrounds, and add watermarks.
Many business documents are composed of several individual documents that need to be merged into a single final document. The PDFDocMerger class supports the merging of multiple documents to create a single PDF document. This can then be manipulated further to add page numbering, watermarks, or other background images.
The following code demonstrates how to merge (concatenate) two PDF documents using physical files to generate a single output document.
Input:
PDF_1 file name (String)
PDF_2 file name (String)
Output:
PDF file name (String)
import java.io.*;
import oracle.apps.xdo.common.pdf.util.PDFDocMerger;
.
.
.
public static void main(String[] args)
{
try
{
// Last argument is PDF file name for output
int inputNumbers = args.length - 1;
// Initialize inputStreams
FileInputStream[] inputStreams = new FileInputStream[inputNumbers];
inputStreams[0] = new FileInputStream(args[0]);
inputStreams[1] = new FileInputStream(args[1]);
// Initialize outputStream
FileOutputStream outputStream = new FileOutputStream(args[2]);
// Initialize PDFDocMerger
PDFDocMerger docMerger = new PDFDocMerger(inputStreams, outputStream);
// Merge PDF Documents and generates new PDF Document
docMerger.mergePDFDocs();
docMerger = null;
// Closes inputStreams and outputStream
}
catch(Exception exc)
{
exc.printStackTrace();
}
}
Input:
PDF Documents (InputStream Array)
Output:
PDF Document (OutputStream)
import java.io.*;
import oracle.apps.xdo.common.pdf.util.PDFDocMerger;
.
.
.
public boolean mergeDocs(InputStream[] inputStreams, OutputStream outputStream)
{
try
{
// Initialize PDFDocMerger
PDFDocMerger docMerger = new PDFDocMerger(inputStreams, outputStream);
// Merge PDF Documents and generates new PDF Document
docMerger.mergePDFDocs();
docMerger = null;
return true;
}
catch(Exception exc)
{
exc.printStackTrace();
return false;
}
}
The following code demonstrates how to merge two PDF documents using input streams to generate a single merged output stream.
To add page numbers:
Create a background PDF template document that includes a PDF form field in the position that you would like the page number to appear on the final output PDF document.
Name the form field @pagenum@.
Enter the number in the field from which to start the page numbering. If you do not enter a value in the field, the start page number defaults to 1.
Input:
PDF Documents (InputStream Array)
Background PDF Document (InputStream)
Output:
PDF Document (OutputStream)
import java.io.*;
import oracle.apps.xdo.common.pdf.util.PDFDocMerger;
.
.
.
public static boolean mergeDocs(InputStream[] inputStreams, InputStream backgroundStream, OutputStream outputStream)
{
try
{
// Initialize PDFDocMerger
PDFDocMerger docMerger = new PDFDocMerger(inputStreams, outputStream);
// Set Background
docMerger.setBackground(backgroundStream);
// Merge PDF Documents and generates new PDF Document
docMerger.mergePDFDocs();
docMerger = null;
return true;
}
catch(Exception exc)
{
exc.printStackTrace();
return false;
}
}
The FO Processor supports page numbering natively through the XSL-FO templates, but if you are merging multiple documents you must use this class to number the complete document from beginning to end.
The following code example places page numbers in a specific point on the page, formats the numbers, and sets the start value using the following methods:
setPageNumberCoordinates (x, y) - sets the x and y coordinates for the page number position. The following example sets the coordinates to 300, 20.
setPageNumberFontInfo (font name, size) - sets the font and size for the page number. If you do not call this method, the default "Helvetica", size 8 is used. The following example sets the font to "Courier", size 8.
setPageNumberValue (n, n) - sets the start number and the page on which to begin numbering. If you do not call this method, the default values 1, 1 are used.
Input:
PDF Documents (InputStream Arrary)
Output:
PDF Document (OutputStream)
import java.io.*;
import oracle.apps.xdo.common.pdf.util.PDFDocMerger;
.
.
.
public boolean mergeDocs(InputStream[] inputStreams, OutputStream outputStream)
{
try
{
// Initialize PDFDocMerger
PDFDocMerger docMerger = new PDFDocMerger(inputStreams, outputStream);
// Calls several methods to specify Page Number
// Calling setPageNumberCoordinates() method is necessary to set Page Numbering
// Please refer to javadoc for more information
docMerger.setPageNumberCoordinates(300, 20);
// If this method is not called, then the default font"(Helvetica, 8)" is used.
docMerger.setPageNumberFontInfo("Courier", 8);
// If this method is not called, then the default initial value "(1, 1)" is used.
docMerger.setPageNumberValue(1, 1);
// Merge PDF Documents and generates new PDF Document
docMerger.mergePDFDocs();
docMerger = null;
return true;
}
catch(Exception exc)
{
exc.printStackTrace();
return false;
}
}
Some documents that are in a draft phase require that a watermark indicating "DRAFT" be displayed throughout the document. Other documents might require a background image on the document. The following code sample shows how to use the PDFDocMerger class to set a watermark.
Use the SetTextDefaultWatermark( ) method to set a text watermark with the following attributes:
Text angle (in degrees): 55
Color: light gray (0.9, 0.9, 0.9)
Font: Helvetica
Font Size: 100
The start position is calculated based on the length of the text
Alternatively, use the SetTextWatermark( ) method to set each attribute separately. Use the SetTextWatermark() method as follows:
SetTextWatermark ("Watermark Text", x, y) - declare the watermark text, and set the x and y coordinates of the start position. In the following example, the watermark text is "Draft" and the coordinates are 200f, 200f.
setTextWatermarkAngle (n) - sets the angle of the watermark text. If this method is not called, 0 will be used.
setTextWatermarkColor (R, G, B) - sets the RGB color. If this method is not called, light gray (0.9, 0.9, 0.9) will be used.
setTextWatermarkFont ("font name", font size) - sets the font and size. If you do not call this method, Helvetica, 100 will be used.
The following example shows how to set these properties and then call the PDFDocMerger.
Input:
PDF Documents (InputStream)
Output:
PDF Document (OutputStream)
import java.io.*;
import oracle.apps.xdo.common.pdf.util.PDFDocMerger;
.
.
.
public boolean mergeDocs(InputStream inputStreams, OutputStream outputStream)
{
try
{
// Initialize PDFDocMerger
PDFDocMerger docMerger = new PDFDocMerger(inputStreams, outputStream);
// You can use setTextDefaultWatermark() without these detailed setting
docMerger.setTextWatermark("DRAFT", 200f, 200f); //set text and place
docMerger.setTextWatermarkAngle(80); //set angle
docMerger.setTextWatermarkColor(1.0f, 0.3f, 0.5f); // set RGB Color
// Merge PDF Documents and generates new PDF Document
docMerger.mergePDFDocs();
docMerger = null;
return true;
}
catch(Exception exc)
{
exc.printStackTrace();
return false;
}
}
An image watermark can be set to cover the entire background of a document, or just to cover a specific area (for example, to display a logo). Specify the placement and size of the image using rectangular coordinates as follows:
float[ ] rct = {LowerLeft X, LowerLeft Y, UpperRight X, UpperRight Y}
For example:
float[ ] rct = {100f, 100f, 200f, 200f}
The image will be sized to fit the rectangular area defined.
To use the actual image size, without sizing it, define the LowerLeft X and LowerLeft Y positions to define the placement and specify the UpperRight X and UpperRight Y coordinates as -1f. For example:
float[ ] rct = {100f, 100f, -1f, -1f}
Input:
PDF Documents (InputStream)
Image File (InputStream)
Output:
PDF Document (OutputStream)
import java.io.*;
import oracle.apps.xdo.common.pdf.util.PDFDocMerger;
.
.
.
public boolean mergeDocs(InputStream inputStreams, OutputStream outputStream, String imageFilePath)
{
try
{
// Initialize PDFDocMerger
PDFDocMerger docMerger = new PDFDocMerger(inputStreams, outputStream);
FileInputStream wmStream = new FileInputStream(imageFilePath);
float[] rct = {100f, 100f, -1f, -1f};
pdfMerger.setImageWatermark(wmStream, rct);
// Merge PDF Documents and generates new PDF Document
docMerger.mergePDFDocs();
docMerger = null;
// Closes inputStreams
return true;
}
catch(Exception exc)
{
exc.printStackTrace();
return false;
}
}
The PDFBookBinder processor is useful for the merging of multiple PDF documents into a single document consisting of a hierarchy of chapters, sections, and subsections and a table of contents for the document. The processor also generates PDF style “bookmarks”; the outline structure is determined by the chapter and section hierarchy. The processor is extremely powerful allowing you complete control over the combined document.
The table of contents formatting and style is created through the use of an RTF template created in Microsoft Word. The chapters are passed into the program as separate PDF files (one chapter, section, or subsection corresponds to one PDF file). Templates may also be specified at the chapter level for insertion of dynamic or static content, page numbering, and placement of hyperlinks within the document.
The templates can be in RTF or PDF format. RTF templates are more flexible by allowing you to leverage BI Publisher's support for dynamic content. PDF templates are much less flexible, making it difficult to achieve desirable effects such as the reflow of text areas when inserting page numbers and other types of dynamic content.
The templates can be rotated (at right angles) or be made transparent. A PDF template can also be specified at the book level, enabling the ability to specify global page numbering, or other content such as backgrounds and watermarks. A title page can also be passed in as a parameter, as well as cover and closing pages for each chapter or section.
The structure of the book’s chapters, sections, and subsections is represented as XML and passed in as a command line parameter; or it can also be passed in at the API level. All of the chapter and section files, as well as all the templates files and their respective parameters, are specified inside this XML structure. Therefore, the only two required parameters are an XML file and a PDF output file.
You can also specify volume breaks inside the book structure. Specifying volume breaks will split the content up into separate output files for easier file and printer management.
The structure of the XML control file is represented in the following diagram:
To specify template and content file locations in your XML structure, you can specify a path relative to your local file system or you can specify a URL referring to the template or content location. Secure HTTP protocol is supported, as well as specially recognized BI Publisher protocols, such as:
“xdo://” - used to specify BI Publisher Template Manager-specific data.
“fnd://” - used to specify data located in the FND_LOBS table.
“blob://” - used for specifying data in any user-defined BLOB table.
The format for the “blob://” protocol is:
blob://[table_name].[blob_column_name]/[pk_datatype]:[pk_name]=[pk_value]/../../..
Following is an example of the command line usage:
java oracle.apps.template.pdf.book.PDFBookBinder [-debug <true or false>] [-tmp <temp dir>] -xml <input xml> -pdf <output pdf>
where
-xml <file> is the file name of the input XML file containing the table of contents XML structure.
-pdf <file> is the final generated PDF output file.
-tmp <directory> is the temporary directory for better memory management. (This is optional, if not specified, the system environment variable “java.io.tmpdir” will be used.)
-log <file> sets the output log file (optional, default is System.out).
-debug <true or false> turns debugging off or on.
The following is an example of an API method call:
String xmlInputPath = “c:\\tmp\\toc.xml”;
String pdfOutputPath = “c:\\tmp\\final_book.pdf”;
PDFBookBinder bookBinder = new PDFBookBinder(xmlInputPath,
pdfOutputPath);
bookBinder.setConfig(new Properties());
bookBinder.process();
The Document Processor Engine provides batch processing functionality to access a single API or multiple APIs by passing a single XML instance document to specify template names, data sources, languages, output type, output names, and destinations.
This solution enables batch printing with BI Publisher, in which a single XML document can be used to define a set of invoices for customers, including the preferred output format and delivery channel for those customers. The XML format is very flexible allowing multiple documents to be created or a single master document.
This section:
Describes the hierarchy and elements of the Document Processor XML file
Provides sample XML files to demonstrate specific processing options
Provides example code to invoke the processors
The Document Processor XML file has the following element hierarchy:
Requestset
request
delivery
filesystem
print
fax
number
email
message
document
background
text
pagenumber
template
data
This hierarchy is displayed in the following illustration:
The following table describes each of the elements:
Element | Attributes | Description |
---|---|---|
requestset | xmlns version | Root element must contain [xmlns:xapi="http://xmlns.oracle.com/oxp/xapi/"] block The version is not required, but defaults to "1.0". |
request | N/A | Element that contains the data and template processing definitions. |
delivery | N/A | Defines where the generated output is sent. |
document | output-type | Specify one output that can have several template elements. The output-type attribute is optional. Valid values are: pdf (Default) rtf html excel text |
filesystem | output | Specify this element to save the output to the file system. Define the directory path in the output attribute. |
| The print element can occur multiple times under delivery to print one document to several printers. Specify the printer attribute as a URI, such as:"ipp://myprintserver:631/printers/printername" | |
fax |
| Specify a URI in the server attribute, for example: "ipp://myfaxserver1:631/printers/myfaxmachine" |
number | The number element can occur multiple times to list multiple fax numbers. Each element occurrence must contain only one number. | |
| Specify the outgoing mail server (SMTP) in the server attribute. Specify the mail server port in the port attribute. | |
message |
| The message element can be placed several times under the email element. You can specify character data in the message element. You can specify multiple e-mail addresses in the to, cc and bcc attributes separated by a comma. The attachment value is either true or false (default). If attachment is true, then a generated document will be attached when the e-mail is sent. The subject attribute is optional. |
background | where | If the background text is required on a specific page, then set the where value to the page numbers required. The page index starts at 1. The default value is 0, which places the background on all pages. |
text |
| Specify the watermark text in the title value. A default value of "yes" automatically draws the watermark with forward slash type. The default value is yes. |
pagenumber |
| The initial-page-index default value is 0. The initial-value default value is 1. "Helvetica" is used for the page number font. The x-pos provides lower left x position. The y-pos provides lower left y position. |
template |
| Contains template information. Valid values for the type attribute are rtf xsl-fo etext The default value is "pdf". |
data | location | Define the location attribute to specify the location of the data, or attach the actual XML data with subelements. The default value of location is "inline". It the location points to either an XML file or a URL, then the data should contain an XML declaration with the proper encoding. If the location attribute is not specified, the data element should contain the subelements for the actual data. This must not include an XML declaration. |
Following are sample XML files that show:
Simple XML shape
Defining two data sets
Defining multiple templates and data
Retrieving templates over HTTP
Retrieving data over HTTP
Generating more than one output
Defining page numbers
The following sample is a simple example that shows the definition of one template (template1.pdf) and one data source (data1) to produce one output file (outfile.pdf) delivered to the file system:
<?xml version="1.0" encoding="UTF-8" ?>
<xapi:requestset xmlns:xapi="http://xmlns.oracle.com/oxp/xapi">
<xapi:request>
<xapi:delivery>
<xapi:filesystem output="d:\tmp\outfile.pdf" />
</xapi:delivery>
<xapi:document output-type="pdf">
<xapi:template type="pdf" location="d:\mywork\template1.pdf">
<xapi:data>
<field1>data1</field1>
</xapi:data>
</xapi:template>
</xapi:document>
</xapi:request>
</xapi:requestset>
The following example shows how to define two data sources to merge with one template to produce one output file delivered to the file system:
<?xml version="1.0" encoding="UTF-8"?>
<xapi:requestset xmlns:xapi="http://xmlns.oracle.com/oxp/xapi">
<xapi:request>
<xapi:delivery>
<xapi:filesystem output="d:\tmp\outfile.pdf"/>
</xapi:delivery>
<xapi:document output-type="pdf">
<xapi:template type="pdf"
location="d:\mywork\template1.pdf">
<xapi:data>
<field1>The first set of data</field1>
</xapi:data>
<xapi:data>
<field1>The second set of data</field1>
</xapi:data>
</xapi:template>
</xapi:document>
</xapi:request>
</xapi:requestset>
The following example builds on the previous examples by applying two data sources to one template and two data sources to a second template, and then merging the two into a single output file. Note that when merging documents, the output-type must be "pdf".
<?xml version="1.0" encoding="UTF-8"?>
<xapi:requestset xmlns:xapi="http://xmlns.oracle.com/oxp/xapi">
<xapi:request>
<xapi:delivery>
<xapi:filesystem output="d:\tmp\outfile3.pdf"/>
</xapi:delivery>
<xapi:document output-type="pdf">
<xapi:template type="pdf"
location="d:\mywork\template1.pdf">
<xapi:data>
<field1>The first set of data</field1>
</xapi:data>
<xapi:data>
<field1>The second set of data</field1>
</xapi:data>
</xapi:template>
<xapi:template type="pdf"
location="d:\mywork\template2.pdf">
<xapi:data>
<field1>The third set of data</field1>
</xapi:data>
<xapi:data>
<field1>The fourth set of data</field1>
</xapi:data>
</xapi:template>
</xapi:document>
</xapi:request>
</xapi:requestset>
This sample is identical to the previous example, except in this case the two templates are retrieved over HTTP:
<?xml version="1.0" encoding="UTF-8"?>
<xapi:requestset xmlns:xapi="http://xmlns.oracle.com/oxp/xapi">
<xapi:request>
<xapi:delivery>
<xapi:filesystem output="d:\temp\out4.pdf"/>
</xapi:delivery>
<xapi:document output-type="pdf">
<xapi:template type="pdf"
location="http://your.server:9999/templates/template1.pdf">
<xapi:data>
<field1>The first page data</field1>
</xapi:data>
<xapi:data>
<field1>The second page data</field1>
</xapi:data>
</xapi:template>
<xapi:template type="pdf"
location="http://your.server:9999/templates/template2.pdf">
<xapi:data>
<field1>The third page data</field1>
</xapi:data>
<xapi:data>
<field1>The fourth page data</field1>
</xapi:data>
</xapi:template>
</xapi:document>
</xapi:request>
</xapi:requestset>
This sample builds on the previous example and shows one template with two data sources, all retrieved via HTTP; and a second template retrieved via HTTP with its two data sources embedded in the XML:
<?xml version="1.0" encoding="UTF-8"?>
<xapi:requestset xmlns:xapi="http://xmlns.oracle.com/oxp/xapi">
<xapi:request>
<xapi:delivery>
<xapi:filesystem output="d:\temp\out5.pdf"/>
</xapi:delivery>
<xapi:document output-type="pdf">
<xapi:template type="pdf"
location="http://your.server:9999/templates/template1.pdf">
<xapi:data location="http://your.server:9999/data/data_1.xml"/>
<xapi:data location="http://your.server:9999/data/data_2.xml"/>
</xapi:template>
<xapi:template type="pdf"
location="http://your.server:9999/templates/template2.pdf">
<xapi:data>
<field1>The third page data</field1>
</xapi:data>
<xapi:data>
<field1>The fourth page data</field1>
</xapi:data>
</xapi:template>
</xapi:document>
</xapi:request>
</xapi:requestset>
The following sample shows the generation of two outputs: out_1.pdf and out_2.pdf. Note that a request element is defined for each output.
<?xml version="1.0" encoding="UTF-8"?>
<xapi:requestset xmlns:xapi="http://xmlns.oracle.com/oxp/xapi">
<xapi:request>
<xapi:delivery>
<xapi:filesystem output="d:\temp\out_1.pdf"/>
</xapi:delivery>
<xapi:document output-type="pdf">
<xapi:template type="pdf"
location="d:\mywork\template1.pdf">
<xapi:data>
<field1>The first set of data</field1>
</xapi:data>
<xapi:data>
<field1>The second set of data</field1>
</xapi:data>
</xapi:template>
</xapi:document>
</xapi:request>
<xapi:request>
<xapi:delivery>
<xapi:filesystem output="d:\temp\out_2.pdf"/>
</xapi:delivery>
<xapi:document output-type="pdf">
<xapi:template type="pdf"
location="d:mywork\template2.pdf">
<xapi:data>
<field1>The third set of data</field1>
</xapi:data>
<xapi:data>
<field1>The fourth set of data</field1>
</xapi:data>
</xapi:template>
</xapi:document>
</xapi:request>
</xapi:requestset>
The following sample shows the use of the pagenumber element to define page numbers on a PDF output document. The first document that is generated will begin with an initial page number value of 1. The second output document will begin with an initial page number value of 3. The pagenumber element can reside anywhere within the document element tags.
Note that page numbering that is applied using the pagenumber element will not replace page numbers that are defined in the template.
<?xml version="1.0" encoding="UTF-8"?>
<xapi:requestset xmlns:xapi="http://xmlns.oracle.com/oxp/xapi">
<xapi:request>
<xapi:delivery>
<xapi:filesystem output="d:\temp\out7-1.pdf"/>
</xapi:delivery>
<xapi:document output-type="pdf">
<xapi:pagenumber initial-value="1" initial-page-index="1"
x-pos="300" y-pos="20" />
<xapi:template type="pdf"
location="d:\mywork\template1.pdf">
<xapi:data>
<field1>The first page data</field1>
</xapi:data>
<xapi:data>
<field1>The second page data</field1>
</xapi:data>
</xapi:template>
</xapi:document>
</xapi:request>
<xapi:request>
<xapi:delivery>
<xapi:filesystem output="d:\temp\out7-2.pdf"/>
</xapi:delivery>
<xapi:document output-type="pdf">
<xapi:template type="pdf"
location="d:\mywork\template2.pdf">
<xapi:data>
<field1>The third page data</field1>
</xapi:data>
<xapi:data>
<field1>The fourth page data</field1>
</xapi:data>
</xapi:template>
<xapi:pagenumber initial-value="3" initial-page-index="1"
x-pos="300" y-pos="20" />
</xapi:document>
</xapi:request>
</xapi:requestset>
The following code samples show how to invoke the document processor engine using an input file name and an input stream.
Input:
Data file name (String)
Directory for Temporary Files (String)
import oracle.apps.xdo.batch.DocumentProcessor;
.
.
.
public static void main(String[] args)
{
.
.
.
try
{
// dataFile --- File path of the Document Processor XML
// tempDir --- Temporary Directory path
DocumentProcessor docProcessor = new DocumentProcessor(dataFile, tempDir);
docProcessor.process();
}
catch(Exception e)
{
e.printStackTrace();
System.exit(1);
}
System.exit(0);
}
Input:
Data file (InputStream)
Directory for Temporary Files (String)
import oracle.apps.xdo.batch.DocumentProcessor;
import java.io.InputStream;
.
.
.
public static void main(String[] args)
{
.
.
.
try
{
// dataFile --- File path of the Document Processor XML
// tempDir --- Temporary Directory path
FileInputStream fIs = new FileInputStream(dataFile);
DocumentProcessor docProcessor = new DocumentProcessor(fIs, tempDir);
docProcessor.process();
fIs.close();
}
catch(Exception e)
{
e.printStackTrace();
System.exit(1);
}
System.exit(0);
}
BI Publisher's bursting engine accepts a data stream and splits it based on multiple criteria, generates output based on a template, then delivers the individual documents through the delivery channel of choice. The engine provides a flexible range of possibilities for document generation and delivery. Example implementations include:
Invoice generation and delivery based on customer-specific layouts and delivery preference
Financial reporting to generate a master report of all cost centers, bursting out individual cost center reports to the appropriate manager
Generation of payslips to all employees based on one extract and delivered via e-mail
The bursting engine is an extension of the Document Processor Engine and has its own method be called to invoke it. The Document Processor XML structure has been extended to handle the new components required by the bursting engine. It supports all of the delivery functionality that the Document Processor supports using the same format. It accepts the XML data to be burst and a control file that takes the Document Processor XML format (see Hierarchy and Elements of the Document Processor XML File).
The control file takes the same format as the Document Processor XML with a few extensions:
Use the attribute select under the request element to specify the element in the XML data that you wish to burst on.
<xapi:request select="/EMPLOYEES/EMPLOYEE">
Use the attribute id under the lowest level of the delivery structure (for example, for the delivery element email, the id attribute belongs to the message element. This assigns an ID to the delivery method to be referenced later in the XML file.
<xapi:message id="123" to="jo.smith@company.com"
Use the delivery attribute under the document element. This assigns the delivery method for the generated document as defined in the id attribute for the delivery element. You can specify multiple delivery channels separated by a comma.
<xapi:document output-type="pdf" delivery="123">
Use the filter attribute on the template element. Use this to apply a layout template based on a filter on your XML data.
<xapi:template type="rtf" location="/usr/tmp/empGeneric.rtf">
<xapi:template type="rtf" location="usr\tmp\empDet.rtf" filter=".//EMPLOYEE[ENAME='SMITH']"/>
This will apply the empDet template only to those employees with the name "SMITH". All other employees will have the empGeneric template applied. This filter can use any XPATH expression to determine the rules for the template application.
You can reference elements in the data to derive certain delivery attributes, such as an e-mail address or fax number. Enter the value for the attribute using the following form:
${ELEMENT}
where ELEMENT is the element name from the XML data that holds the value for the attribute.
For example:
<xapi:message id="123" to="${EMAIL}"/>
At runtime the value of the to attribute will be set to the value of the EMAIL element from the input XML file.
You can also set the value of an attribute by passing a parameter to API in a Properties object.
You can reference information in the XML data to be put into the delivery content. This takes the same format described above (that is, ${ELEMENT}).
For example, suppose you wanted to burst a document to employees via e-mail and personalize the e-mail by using the employee's name in the subject line. Assuming the employee's name is held in an element called ENAME, you could use ${ENAME} to reference the employee's name in the control file as follows:
subject="Employee Details for ${ENAME}"
Sample Control File
The following sample control file shows an example control file to split data based on an EMPLOYEE element and send an e-mail to each employee with their own data. The sample file is annotated.
<?xml version="1.0" encoding="UTF-8"?>
<xapi:requestset xmlns:xapi="http://xmlns.oracle.com/oxp/xapi">
<xapi:request select="/EMPLOYEES/EMPLOYEE">
<! - This sets the bursting element i.e., EMPLOYEE - >
<xapi:delivery>
<xapi:email server="rgmamersmtp.oraclecorp.com" port="25"
from="xmlpadmin1@oracle.com" reply-to ="reply@oracle.com">
<xapi:message id="123" to="${EMAIL}" cc="${EMAIL_ALL}"
attachment="true" subject="Employee Details
for ${ENAME}"> Mr. ${ENAME}, Please review the
attached document</xapi:message>
<! - This assigns a delivery id of '123'. It also sets the e-mail
address of the employee and a cc copy to a parameter value
EMAIL_ALL; this might be a manager's e-mail. The employee's
name (ENAME) can also be used in the subject/body
of the email. - >
</xapi:email>
</xapi:delivery>
<xapi:document output-type="pdf" delivery="123">
<xapi:template type="rtf" location="/usr/tmp/empGeneric.rtf">
<xapi:template type="rtf" location="/usr/tmp/empDet.rtf"
filter=".//EMPLOYEE[ENAME='SMITH']" >
<! - Employees with the name SMITH will have
the empDet template applied - >
</xapi:template>
</xapi:document>
</xapi:request>
</xapi:requestset>
The bursting engine can support multiple bursting criteria and delivery options. Assume you have a report that generates data for all employees with their manager's information. You can construct a control file that will:
Burst the employee data to each employee
Burst a report to each manager that contains the data about his employees
You can provide a different template for each bursting level. You can therefore generate the employee report based on one template and the summary manager's report based on a different template, but still use the same data set.
To achieve this multibursting result, you must add a second request element to the control file structure.
Multibursting Example
The following sample shows how to construct a control file that will burst on the EMPLOYEE level and the MANAGER level:
?xml version="1.0" encoding="UTF-8" ?>
<xapi:requestset xmlns:xapi="http://xmlns.oracle.com/oxp/xapi">
<! - First request to burst on employee - >
<xapi:request select="/EMPLOYEES/EMPLOYEE">
<xapi:delivery>
<xapi:email <<server details removed>> />
<xapi:message id="123" <<message details removed>>
</xapi:message>
</xapi:email>
<xapi:fax server="ipp://mycupsserver:631/printers/fax2">
<xapi:number id="FAX1">916505069560</xapi:number>
</xapi:fax>
<xapi:print id="printer1"
printer="ipp://mycupsserver:631/printers/printer1"
copies="2" />
</xapi:delivery>
<xapi:document output-type="pdf" delivery="123">
<xapi:template type="rtf" location="usr\tmp\empDet.rtf" />
</xapi:document>
</xapi:request>
<!Second request to burst on department - >
<xapi:request select="/DATA/DEPT/MANAGER">
<xapi:delivery>
<xapi:email server="gmsmtp.oraclecorp.com" port=""
from="XDOburstingTest@oracle.com" reply-to="reply@oracle.com">
<xapi:message id="123" to="${MANAGER_EMAIL}"
cc="${MANAGER_EMAIL}" attachment="true"
subject="Department Summary for ${DEPTNO}">Please review
the attached Department Summary for
department ${DEPTNO}</xapi:message>
</xapi:email>
</xapi:delivery>
<xapi:document output-type="rtf" delivery="123">
<xapi:template type="rtf"
location="d:\burst_test\deptSummary.rtf" />
</xapi:document>
</xapi:request>
</xapi:requestset>
The bursting engine provides a listening interface that allows you to listen to the various stages of the bursting process. Following are the supported modes that you can subscribe to:
beforeProcess() - before the bursting process starts.
afterProcess() - after the bursting process completes.
beforeProcessRequest(int requestIndex) - before the bursting request starts. This interface provides an assigned request ID for the current request.
afterProcessRequest(int requestIndex)- after the bursting request has completed; provides the request ID for the current request.
beforeProcessDocument(int requestIndex,int documentIndex,String deliveryId) - before the document generation starts; provides the request ID and a document ID.
afterProcessDocument(int requestIndex,int documentIndex,Vector documentOutputs) - after the document generation completes; provides the request ID and document ID, plus a Vector list of the document objects generated in the request.
beforeDocumentDelivery(int requestIndex,int documentIndex,String deliveryId) - before the documents in the request are delivered; provides the request ID, the document ID, and a delivery ID.
afterDocumentDelivery(int requestIndex,int documentIndex,String deliveryId,Object deliveryObject,Vector attachments) - after the document delivery completes; provides a request ID, document ID, and delivery ID, plus a Vector list of the documents delivered in the request.
You can subscribe to any of these interfaces in your calling Java class. The listeners are useful to determine if the processing of individual documents is proceeding successfully or to start another process based on the successful completion of a request.
To call the bursting API, instantiate an instance of DocumentProcessor class using on of the following formats:
DocumentProcessor(xmlCtrlInput, xmlDataInput, tmpDir)
where
xmlCtrlInput - is the control file for the bursting process. This can be a string reference to a file, an inputStream object, or a Reader object.
xmlDataInput - is the XML data to be burst. This can a string reference to a file, an inputStream object, or a Reader object.
tmpDir - is a temporary working directory. This takes the format of a String object. This is optional as long as the main BI Publisher temporary directory has been set.
Simple Example Java Class
The following is a sample Java class:
public class BurstingTest
{
public BurstingTest()
{
try
{
DocumentProcessor dp = new DocumentProcessor
("\burst\burstCtrl.xml", "\\burst\\empData.xml","\\burst");
dp.process();
}
}
catch (Exception e)
{ System.out.println(e);
public static void main(String[] args)
{
BurstingTest burst1 = new BurstingTest();
}
}
Example Java Class with Listeners
To take advantage of the bursting listeners, add the interface to the class declaration and use the registerListener method. Then code for the listeners you want to subscribe to as follows:
public class BurstingTest implements BurstingListener
{
public BurstingTest()
{
try
{
DocumentProcessor dp = new DocumentProcessor
("\burst\burstCtrl.xml", "\\burst\\empData.xml","\\burst");
dp.registerListener(this);
dp.process();
}
}
catch (Exception e)
{ System.out.println(e);
public static void main(String[] args)
{
BurstingTest burst1 = new BurstingTest();
}
public void beforeProcess(){
System.out.println("Start of Bursting Process");
}
public void afterProcess()
{
System.out.println("End of Bursting Process");
}
public void beforeProcessRequest(int requestIndex)
{
System.out.println("Start of Process Request ID"+requestIndex);
}
public void afterProcessRequest(int requestIndex)
{
System.out.println("End of Process Request ID"+requestIndex ");
}
public void beforeProcessDocument(int requestIndex,int
documentIndex)
{
System.out.println("Start of Process Document");
System.out.println(" Request Index "+requestIndex);
System.out.println(" Document Index " +documentIndex);
}
public void afterProcessDocument(int requestIndex,int
documentIndex,
Vector documentOutputs)
{
System.out.println(" ========End of Process Document");
System.out.println(" Outputs :"+documentOutputs);
}
public void beforeDocumentDelivery(int requestIndex,int
documentIndex,
String deliveryId)
{
System.out.println(" ========Start of Delivery");
System.out.println(" Request Index "+requestIndex);
System.out.println(" Document Index " +documentIndex);
System.out.println(" DeliveryId " +deliveryId);
}
public void afterDocumentDelivery(int requestIndex,int documentIndex,
String deliveryId,Object deliveryObject,Vector attachments)
{
System.out.println(" ========End of Delivery");
System.out.println(" Attachments : "+attachments);
}
}
Passing a Parameter
To pass a parameter holding a value to be used in the control file for delivery, add the following code:
…
Properties prop= new Properties();
prop.put("user-variable:ADMIN_EMAIL","jo.smith@company.com");
dp.setConfig(prop);
dp.process();
…
All of the examples in this section use the following XML data source:
<?xml version="1.0" encoding="UTF-8"?>
<DATA>
<DEPTS>
<DEPT>
<DEPTNO>20</DEPTNO>
<NAME>Accounting</NAME>
<MANAGER_EMAIL>tdexter@mycomp.com</MANAGER_EMAIL>
<EMPLOYEES>
<EMPLOYEE>
<EMPNO>7369</EMPNO>
<ENAME>SMITH</ENAME>
<JOB>CLERK</JOB>
<MGR>7902</MGR>
<HIREDATE>1980-12-17T00:00:00.000-08:00</HIREDATE>
<SAL>800</SAL>
<DEPTNO>20</DEPTNO>
<EMAIL>jsmith@mycomp.com</EMAIL>
</EMPLOYEE>
<EMPLOYEE>
<EMPNO>7566</EMPNO>
<ENAME>JONES</ENAME>
<JOB>MANAGER</JOB>
<MGR>7839</MGR>
<HIREDATE>1981-04-02T00:00:00.000-08:00</HIREDATE>
<SAL>2975</SAL>
<DEPTNO>20</DEPTNO>
<EMAIL>jjones@mycomp.com</EMAIL>
</EMPLOYEE>
</EMPLOYEES>
</DEPT>
<DEPT>
<DEPTNO>30</DEPTNO>
<NAME>Sales</NAME>
<MANAGER_EMAIL>dsmith@mycomp.com</MANAGER_EMAIL>
<EMPLOYEES>
<EMPLOYEE>
<EMPNO>7788</EMPNO>
<ENAME>SCOTT</ENAME>
<JOB>ANALYST</JOB>
<MGR>7566</MGR>
<HIREDATE>1982-12-09T00:00:00.000-08:00</HIREDATE>
<SAL>3000</SAL>
<DEPTNO>20</DEPTNO>
<EMAIL>jscott@mycomp.com</EMAIL>
</EMPLOYEE>
<EMPLOYEE>
<EMPNO>7876</EMPNO>
<ENAME>ADAMS</ENAME>
<JOB>CLERK</JOB>
<MGR>7788</MGR>
<HIREDATE>1983-01-12T00:00:00.000-08:00</HIREDATE>
<SAL>1100</SAL>
<EMAIL>jadams@mycomp.com</EMAIL>
</EMPLOYEE>
</EMPLOYEES>
</DEPT>
</DEPTS>
</DATA>
Example 1 - Bursting Employee Data to Employees via E-mail
The following sample shows how to apply a template (empDet.rtf) to every employee's data, generate a PDF document, and deliver the document to each employee via e-mail.
<?xml version="1.0" encoding="UTF-8"?>
<xapi:requestset xmlns:xapi="http://xmlns.oracle.com/oxp/xapi">
<xapi:request select="/DATA/DEPTS/DEPT/EMPLOYEES/EMPLOYEE">
<! - Burst on employee element - >
<xapi:delivery>
<xapi:email server="my.smtp.server" port="25"
from="xmlpadmin@mycomp.com" reply-to ="">
<xapi:message id="123" to="${EMAIL}"
<! - Set the id for the delivery method - >
<! - Use the employees EMAIL element to email the document to
the employee - >
cc="${ADMIN_EMAIL}"
<! - Use the ADMIN_EMAIL parameter to CC the document
to the administrator - >
attachment="true" subject="Employee Details for ${ENAME}">
Mr. ${ENAME}, Please review the attached document</xapi:message>
<! - Embed the employees name into the email message - >
</xapi:email>
</xapi:delivery>
<xapi:document output-type="pdf" delivery="123">
<!Specify the delivery method id to be used - >
<xapi:template type="rtf"
location="\usr\empDet.rtf"></xapi:template>
</xapi:document>
</xapi:request>
</xapi:requestset>
Example 2 - Bursting Employee Data to Employees via Multiple Delivery Channels and Conditionally Using Layout Templates
This sample shows how to burst, check the employee name, and generate a PDF using the appropriate template. The documents will then be e-mailed and printed.
<?xml version="1.0" encoding="UTF-8"?>
<xapi:requestset xmlns:xapi="http://xmlns.oracle.com/oxp/xapi" >
<xapi:globalData location="stream">
</xapi:globalData >
<xapi:request select="/DATA/DEPTS/DEPT/EMPLOYEES/EMPLOYEE">
<xapi:delivery>
<xapi:email server="my.smtp.server" port=""
from="xmlpserver@oracle.com"
reply-to ="reply@oracle.com">
<xapi:message id="123" to="${EMAIL}" cc="" attachment="true"
subject="Employee Details for ${ENAME}"> Mr. ${ENAME},
Please review the attached document</xapi:message>
</xapi:email>
<xapi:print id="printer1"
printer="ipp://ipgpc1:631/printers/printer1" copies="2" />
<! - Add an id for this delivery method i.e. printer1 - >
</xapi:delivery>
<xapi:document output-type="pdf" delivery="printer1,123">
<! - Deliver to printer and email - >
<xapi:template type="rtf" location="/usr/empDetSmith.rtf"
filter=".//EMPLOYEE[ENAME='SMITH']">
<!- Specify template to be used for employees called SMITH - >
</xapi:template>
<xapi:template type="rtf" location="/usr/empSummary.rtf">
<! - Default template to be used - >
</xapi:template>
</xapi:document>
</xapi:request>
</xapi:requestset>
Example 3 - Bursting Employee Data to Employees and Their Manager
This sample shows how to burst an e-mail with a PDF attachment to all employees using the empDet template. It will also burst an employee summary PDF to the manager of each department via e-mail.
<?xml version="1.0" encoding="UTF-8"?>
<xapi:requestset xmlns:xapi="http://xmlns.oracle.com/oxp/xapi">
<xapi:request select="/DATA/DEPTS/DEPT/EMPLOYEES/EMPLOYEE">
<xapi:delivery>
<xapi:email server="my.smtp.server" port=""
from="xmlpserver@oracle.com" reply-to ="">
<xapi:message id="123" to="${EMAIL}" cc="${EMAIL}"
attachment="true"
subject="Employee Details for ${ENAME}"> Mr. ${ENAME},
Please review the attached document</xapi:message>
</xapi:email>
</xapi:delivery>
<xapi:document output-type="pdf" delivery="123">
<xapi:template type="rtf"
location="/usr/empDet.rtf"</xapi:template>
</xapi:document>
</xapi:request>
<xapi:request select="/DATA/DEPTS/DEPT">
<! - Second request created to burst the same dataset to the
manager based on the DEPT element - >
<xapi:delivery>
<xapi:email server="my.smtp.server" port=""
from="xmlpserver@oracle.com" reply-to ="">
<xapi:message id="456" to="${MANAGER_EMAIL}"
cc="${MANAGER_EMAIL}" attachment="true" subject="Department
Summary for ${DEPTNO}"> Please review the attached
Department Summary for department ${DEPTNO}</xapi:message>
</xapi:email>
</xapi:delivery>
<xapi:document output-type="rtf" delivery="456">
<xapi:template type="rtf"
location="\usr\deptSumm.rtf"></xapi:template>
</xapi:document>
</xapi:request>
</xapi:requestset>
The FO Processor supports PDF security and other properties that can be applied to your final documents. Security properties include making a document unprintable and applying password security to an encrypted document.
Other properties allow you to define font subsetting and embedding. If your template uses a font that would not normally be available to BI Publisher at runtime, you can use the font properties to specify the location of the font. At runtime BI Publisher will retrieve and use the font in the final document. For example, this property might be used for check printing for which a MICR font is used to generate the account and routing numbers on the checks.
See Setting Runtime Properties for the full list of properties.
The properties can be set in two ways:
At runtime, specify the property as a Java Property object to pass to the FO Processor.
Set the property in a configuration file.
Set the property in the template (RTF templates only). See Setting Properties in the RTF template for this method.
To pass a property as a Property object, set the name/value pair for the property prior to calling the FO Processor, as shown in the following example:
Input:
XML file name (String)
XSL file name (String)
Output:
PDF file name (String)
import oracle.apps.xdo.template.FOProcessor;
.
.
.
public static void main(String[] args)
{
FOProcessor processor = new FOProcessor();
processor.setData(args[0]); // set XML input file
processor.setTemplate(args[1]); // set XSL input file
processor.setOutput(args[2]); //set (PDF) output file
processor.setOutputFormat(FOProcessor.FORMAT_PDF);
Properties prop = new Properties();
/* PDF Security control: */
prop.put("pdf-security", "true");
/* Permissions password: */
prop.put("pdf-permissions-password", "abc");
/* Encryption level: */
prop.put("pdf-encription-level", "0");
processor.setConfig(prop);
// Start processing
try
{
processor.generate();
}
catch (XDOException e)
{
e.printStackTrace();
System.exit(1);
}
System.exit(0);
}
The following code shows an example of passing the location of a configuration file.
Input:
XML file name (String)
XSL file name (String)
Output:
PDF file name (String)
import oracle.apps.xdo.template.FOProcessor;
.
.
.
public static void main(String[] args)
{
FOProcessor processor = new FOProcessor();
processor.setData(args[0]); // set XML input file
processor.setTemplate(args[1]); // set XSL input file
processor.setOutput(args[2]); //set (PDF) output file
processor.setOutputFormat(FOProcessor.FORMAT_PDF);
processor.setConfig(“/tmp/xmlpconfig.xml”);
// Start processing
try
{
processor.generate();
} catch (XDOException e)
{ e.printStackTrace();
System.exit(1);
}
System.exit(0);
}
Input:
Data file name (String)
Directory for Temporary Files (String)
Output:
PDF FIle
import oracle.apps.xdo.batch.DocumentProcessor;
.
.
.
public static void main(String[] args)
{
.
.
.
try
{
// dataFile --- File path of the Document Processor XML
// tempDir --- Temporary Directory path
DocumentProcessor docProcessor = new DocumentProcessor(dataFile, tempDir);
Properties prop = new Properties();
/* PDF Security control: */
prop.put("pdf-security", "true");
/* Permissions password: */
prop.put("pdf-permissions-password", "abc");
/* encryption level: */
prop.put("pdf-encription-level", "0");
processor.setConfig(prop);
docProcessor.process();
}
catch(Exception e)
{
e.printStackTrace();
System.exit(1);
}
System.exit(0);
}
For the advanced formatting to work in the template, you must provide a Java class with the appropriate methods to format the data at runtime. Many font vendors offer the code with their fonts to carry out the formatting; these must be incorporated as methods into a class that is available to the BI Publisher formatting libraries at runtime. There are some specific interfaces that you must provide in the class for the library to call the correct method for encoding.
Note: See Advanced Barcode Formatting for the setup required in the RTF template.
You must implement the following three methods in this class:
/**
* Return a unique ID for this bacode encoder
* @return the id as a string
*/
public String getVendorID();
/**
* Return true if this encoder support a specific type of barcode
* @param type the type of the barcode
* @return true if supported
*/
public boolean isSupported(String type);
/**
* Encode a barcode string by given a specific type
* @param data the original data for the barcode
* @param type the type of the barcode
* @return the formatted data
*/
public String encode(String data, String type);
Place this class in the classpath for the middle tier JVM in which BI Publisher is running.
Note: For E-Business Suite users, the class must be placed in the classpath for the middle tier and any concurrent nodes that are present.
If in the register-barcode-vendor command the barcode_vendor_id is not provided, BI Publisher will call the getVendorID() and use the result of the method as the ID for the vendor.
The following is an example class that supports the code128 a, b and c encodings:
Important: The following code sample can be copied and pasted for use in your system. Note that due to publishing constraints you will need to correct line breaks and ensure that you delete quotes that display as "smart quotes" and replace them with simple quotes.
package oracle.apps.xdo.template.rtf.util.barcoder;
import java.util.Hashtable;
import java.lang.reflect.Method;
import oracle.apps.xdo.template.rtf.util.XDOBarcodeEncoder;
import oracle.apps.xdo.common.log.Logger;
// This class name will be used in the register vendor
// field in the template.
public class BarcodeUtil implements XDOBarcodeEncoder
// The class implements the XDOBarcodeEncoder interface
{
// This is the barcode vendor id that is used in the
// register vendor field and format-barcode fields
public static final String BARCODE_VENDOR_ID = "XMLPBarVendor";
// The hashtable is used to store references to
// the encoding methods
public static final Hashtable ENCODERS = new Hashtable(10);
// The BarcodeUtil class needs to be instantiated
public static final BarcodeUtil mUtility = new BarcodeUtil();
// This is the main code that is executed in the class,
// it is loading the methods for the encoding into the hashtable.
// In this case we are loading the three code128 encoding
// methods we have created.
static {
try {
Class[] clazz = new Class[] { "".getClass() };
ENCODERS.put("code128a",mUtility.getClass().getMethod("code128a", clazz));
ENCODERS.put("code128b",mUtility.getClass().getMethod("code128b", clazz));
ENCODERS.put("code128c",mUtility.getClass().getMethod("code128c", clazz));
} catch (Exception e) {
// This is using the BI Publisher logging class to push
// errors to the XMLP log file.
Logger.log(e,5);
}
}
// The getVendorID method is called from the template layer
// at runtime to ensure the correct encoding method are used
public final String getVendorID()
{
return BARCODE_VENDOR_ID;
}
//The isSupported method is called to ensure that the
// encoding method called from the template is actually
// present in this class.
// If not then XMLP will report this in the log.
public final boolean isSupported(String s)
{
if(s != null)
return ENCODERS.containsKey(s.trim().toLowerCase());
else
return false;
}
// The encode method is called to then call the appropriate
// encoding method, in this example the code128a/b/c methods.
public final String encode(String s, String s1)
{
if(s != null && s1 != null)
{
try
{
Method method = (Method)ENCODERS.get(s1.trim().toLowerCase());
if(method != null)
return (String)method.invoke(this, new Object[] {
s
});
else
return s;
}
catch(Exception exception)
{
Logger.log(exception,5);
}
return s;
} else
{
return s;
}
}
/** This is the complete method for Code128a */
public static final String code128a( String DataToEncode )
{
char C128_Start = (char)203;
char C128_Stop = (char)206;
String Printable_string = "";
char CurrentChar;
int CurrentValue=0;
int weightedTotal=0;
int CheckDigitValue=0;
char C128_CheckDigit='w';
DataToEncode = DataToEncode.trim();
weightedTotal = ((int)C128_Start) - 100;
for( int i = 1; i <= DataToEncode.length(); i++ )
{
//get the value of each character
CurrentChar = DataToEncode.charAt(i-1);
if( ((int)CurrentChar) < 135 )
CurrentValue = ((int)CurrentChar) - 32;
if( ((int)CurrentChar) > 134 )
CurrentValue = ((int)CurrentChar) - 100;
CurrentValue = CurrentValue * i;
weightedTotal = weightedTotal + CurrentValue;
}
//divide the WeightedTotal by 103 and get the remainder,
//this is the CheckDigitValue
CheckDigitValue = weightedTotal % 103;
if( (CheckDigitValue < 95) && (CheckDigitValue > 0) )
C128_CheckDigit = (char)(CheckDigitValue + 32);
if( CheckDigitValue > 94 )
C128_CheckDigit = (char)(CheckDigitValue + 100);
if( CheckDigitValue == 0 ){
C128_CheckDigit = (char)194;
}
Printable_string = C128_Start + DataToEncode + C128_CheckDigit + C128_Stop + " ";
return Printable_string;
}
/** This is the complete method for Code128b ***/
public static final String code128b( String DataToEncode )
{
char C128_Start = (char)204;
char C128_Stop = (char)206;
String Printable_string = "";
char CurrentChar;
int CurrentValue=0;
int weightedTotal=0;
int CheckDigitValue=0;
char C128_CheckDigit='w';
DataToEncode = DataToEncode.trim();
weightedTotal = ((int)C128_Start) - 100;
for( int i = 1; i <= DataToEncode.length(); i++ )
{
//get the value of each character
CurrentChar = DataToEncode.charAt(i-1);
if( ((int)CurrentChar) < 135 )
CurrentValue = ((int)CurrentChar) - 32;
if( ((int)CurrentChar) > 134 )
CurrentValue = ((int)CurrentChar) - 100;
CurrentValue = CurrentValue * i;
weightedTotal = weightedTotal + CurrentValue;
}
//divide the WeightedTotal by 103 and get the remainder,
//this is the CheckDigitValue
CheckDigitValue = weightedTotal % 103;
if( (CheckDigitValue < 95) && (CheckDigitValue > 0) )
C128_CheckDigit = (char)(CheckDigitValue + 32);
if( CheckDigitValue > 94 )
C128_CheckDigit = (char)(CheckDigitValue + 100);
if( CheckDigitValue == 0 ){
C128_CheckDigit = (char)194;
}
Printable_string = C128_Start + DataToEncode + C128_CheckDigit + C128_Stop + " ";
return Printable_string;
}
/** This is the complete method for Code128c **/
public static final String code128c( String s )
{
char C128_Start = (char)205;
char C128_Stop = (char)206;
String Printable_string = "";
String DataToPrint = "";
String OnlyCorrectData = "";
int i=1;
int CurrentChar=0;
int CurrentValue=0;
int weightedTotal=0;
int CheckDigitValue=0;
char C128_CheckDigit='w';
DataToPrint = "";
s = s.trim();
for(i = 1; i <= s.length(); i++ )
{
//Add only numbers to OnlyCorrectData string
CurrentChar = (int)s.charAt(i-1);
if((CurrentChar < 58) && (CurrentChar > 47))
{
OnlyCorrectData = OnlyCorrectData + (char)s.charAt(i-1);
}
}
s = OnlyCorrectData;
//Check for an even number of digits, add 0 if not even
if( (s.length() % 2) == 1 )
{
s = "0" + s;
}
//<<<< Calculate Modulo 103 Check Digit and generate
// DataToPrint >>>>
//Set WeightedTotal to the Code 128 value of
// the start character
weightedTotal = ((int)C128_Start) - 100;
int WeightValue = 1;
for( i = 1; i <= s.length(); i += 2 )
{
//Get the value of each number pair (ex: 5 and 6 = 5*10+6 =56)
//And assign the ASCII values to DataToPrint
CurrentChar = ((((int)s.charAt(i-1))-48)*10) + (((int)s.charAt(i))-48);
if((CurrentChar < 95) && (CurrentChar > 0))
DataToPrint = DataToPrint + (char)(CurrentChar + 32);
if( CurrentChar > 94 )
DataToPrint = DataToPrint + (char)(CurrentChar + 100);
if( CurrentChar == 0)
DataToPrint = DataToPrint + (char)194;
//multiply by the weighting character
//add the values together to get the weighted total
weightedTotal = weightedTotal + (CurrentChar * WeightValue);
WeightValue = WeightValue + 1;
}
//divide the WeightedTotal by 103 and get the remainder,
//this is the CheckDigitValue
CheckDigitValue = weightedTotal % 103;
if((CheckDigitValue < 95) && (CheckDigitValue > 0))
C128_CheckDigit = (char)(CheckDigitValue + 32);
if( CheckDigitValue > 94 )
C128_CheckDigit = (char)(CheckDigitValue + 100);
if( CheckDigitValue == 0 ){
C128_CheckDigit = (char)194;
}
Printable_string = C128_Start + DataToPrint + C128_CheckDigit + C128_Stop + " ";
Logger.log(Printable_string,5);
return Printable_string;
}
}
Once you create the class and place it in the correct classpath, your template creators can start using it to format the data for barcodes. You must give them the following information to include in the template commands:
The class name and path.
In this example:
oracle.apps.xdo.template.rtf.util.barcoder.BarcodeUtil
The barcode vendor ID you created.
In this example: XMLPBarVendor
The available encoding methods.
In this example, code128a, code128b and code128c They can then use this information to sucessfully encode their data for barcode output.
They can then use this information to successfully encode their data for barcode output.
Copyright © 2005, 2008, Oracle and/or its affiliates. All rights reserved.