Skip Headers

Oracle Business Intelligence Publisher Online Help
Release 10.1.3.4
Part Number E12602-01
Go to Table of Contents
Contents
Go to previous page
Previous
Go to next page
Next

Using the BI Publisher APIs

This chapter covers the following topics:

Introduction

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 Core APIs

BI Publisher is made up of the following core API components:

The following diagram illustrates the template type and output type options for each core processing engine:

the picture is described in the document text

Prerequisites

In order to use the BI Publisher APIs you need the following JAR files in your class path:

Obtaining the Libraries

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:

PDF Form Processing Engine

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:

Merging a PDF Template with XML Data

XML data can be merged with a PDF template to produce a PDF output document in three ways:

You can optionally include a metadata XML file to describe the placement of overflow data in your template.

Merging XML Data with a PDF Template Using Input/Output File Names

Input:

Output:

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();

Merging XML Data with a PDF Template Using Input/Output Streams

Input:

Output:

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();

 

Merging an XML Data String with a PDF Template

Input:

Output:

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();

Retrieving a List of Field Names

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:

Output:

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));
}

Generating XFDF Data

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()); 

Converting XML Data into XFDF Format Using XSLT

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());

RTF Processor Engine

Generating XSL

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:

Generating XSL with Input/Output File Names

Input:

Output:

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);
  }

Generating XSL with Input/Output Stream

Input:

Output:

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);
  }

FO Processor Engine

Generating Output from an XML File and an XSL File

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:

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:

An XSL-FO utility is also provided that creates XSL-FO from the following inputs:

The FO object output from the XSL-FO utility can then be used as input to the FO processor.

Major Features of the FO Processor

Bidirectional Text

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.

Font Fallback Mechanism

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.

Variable Header and Footer

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.

Horizontal Table Break

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 picture is described in the document text

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 picture is described in the document text

The following figure shows another option. The table breaks after the third column, but includes the first column with each new page.

the picture is described in the document text

Generating Output Using File Names

The following example shows how to use the FO Processor to create an output file using file names.

Input:

Output:

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);
  }

Generating Output Using Streams

The processor can also be used with input/output streams as shown in the following example:

Input:

Output:

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);
    
  }

Generating Output from an Array of XSL Templates and XML Data

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:

Output:

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);
    }

  }
    

Using the XSL-FO Utility

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.

Creating XSL-FO from an XML File and an XSL File

Input:

Output:

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);
  }

Creating XSL-FO from Two XML Files and Two XSL files

Input:

Output:

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);
  }

Merging Two XSL-FO Files

Input:

Output:

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);
  }
 

Generating Output from an FO file

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.

Generating Output Using File Names

Input:

Output:

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);
  }

Generating Output Using Streams

Input:

Output:

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);
    }
  }

Generating Output with an Array of FO Data

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:

Output:

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);
    }

  }
          

PDF Document Merger

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.

Merging PDF Documents

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.

Merging with Input/Output File Names

The following code demonstrates how to merge (concatenate) two PDF documents using physical files to generate a single output document.

Input:

Output:

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();
    }
  }

Merging with Input/Output Streams

Input:

Output:

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;
    }
  }

Merging with Background to Place Page Numbering

The following code demonstrates how to merge two PDF documents using input streams to generate a single merged output stream.

To add page numbers:

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

  2. Name the form field @pagenum@.

  3. 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:

Output:

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;
    }
  }

Adding Page Numbers to Merged Documents

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:

Input:

Output:

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;
    }
  }

Setting a Text or Image Watermark

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.

Setting a Text Watermark

Use the SetTextDefaultWatermark( ) method to set a text watermark with the following attributes:

Alternatively, use the SetTextWatermark( ) method to set each attribute separately. Use the SetTextWatermark() method as follows:

The following example shows how to set these properties and then call the PDFDocMerger.

Input:

Output:

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;
    }
  }

Setting Image Watermark

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:

Output:

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;
    }
  }

PDF Book Binder Processor

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.

Usage

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.

XML Control File

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:

the picture is described in the document text

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:

Command Line Options

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.

API Method Call

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();

Document Processor Engine

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:

Hierarchy and Elements of the Document Processor XML File

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 picture is described in the document text

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.
print
  • printer

  • server-alias

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
  • server

  • server-alias

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.
email
  • server

  • port

  • from

  • reply-to

  • server-alias

Specify the outgoing mail server (SMTP) in the server attribute.
Specify the mail server port in the port attribute.
message
  • to

  • cc

  • bcc

  • attachment

  • subject

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
  • title

  • default

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
  • initial-page-index

  • initial-value

  • x-pos

  • y-pos

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
  • locale

  • location

  • type

Contains template information.
Valid values for the type attribute are
pdf
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.

XML File Samples

Following are sample XML files that show:

Simple XML sample

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>

Defining two data sets

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>

Defining multiple templates and data

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>

Retrieving templates over HTTP

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>

Retrieving data over HTTP

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>

Generating more than one output

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>

Defining page numbers

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>

Invoke Processors

The following code samples show how to invoke the document processor engine using an input file name and an input stream.

Invoke Processors with Input File Name

Input:

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);
  }

Invoke Processors with InputStream

Input:

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);
  }

 

Bursting Engine

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:

Usage

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

Control File

The control file takes the same format as the Document Processor XML with a few extensions:

Dynamic Delivery Destination

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.

Dynamic Delivery Content

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>

Multiple Bursting Options

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:

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>

Bursting Listeners

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:

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.

Calling the Bursting API

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();
…

Bursting Control File Examples

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>

BI Publisher Properties

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.

Setting Properties

The properties can be set in two ways:

Passing Properties to the FO Engine

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:

Output:

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);
  }

Passing a Configuration File to the FO Processor

The following code shows an example of passing the location of a configuration file.

Input:

Output:

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);
  }

Passing Properties to the Document Processor

Input:

Output:

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);
  }

Advanced Barcode Font Formatting Implementation

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:

They can then use this information to successfully encode their data for barcode output.