7 Using the BI Publisher Java APIs

This chapter describes the BI Publisher Java APIs that can be called from a custom application to generate and process documents.

It includes the following sections:

Note:

The information in this chapter is intended to be used with the Oracle Fusion Middleware Java API Reference for Oracle Business Intelligence Publisher, which is available in the Oracle Fusion Middleware Business Intelligence Documentation Library. Also, this chapter assumes you are familiar with Java programming, XML, and XSL technologies.

7.1 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:

Figure 7-1 Template and Output Types for BI Publisher Core Processing Engines

Surrounding text describes Figure 7-1 .

7.2 Prerequisites

To use the BI Publisher APIs, ensure that xdocore.jar is in your class path. xdocore.jar contains the main library for the BI Publisher APIs.

In addition, the following libraries are required:

  • aolj.jar - supports various BI Publisher functions

  • collections.jar - you only need this if you are working with the delivery APIs or bursting engine.

  • dvt-jclient.jar - a charting library

  • dvt-utils.jar - a charting library

  • groovy-all-1.6.3.jar - Groovy

  • i18nAPI_v3.jar - the i18n library used for localization functions

  • jewt4.jar - a charting support library

  • mail.jar - used for SMTP delivery

  • ojdl.jar - used for Oracle Diagnostic Logging

  • orai18n-collation.jar - used by XDK

  • orai18n-mapping.jar - used by XDK

  • orai18n.jar - contains character set and globalization support files used by XDK

  • osdt_cert.jar - security library for SMIME support

  • osdt_cms.jar - security library for SMIME support

  • osdt_core.jar - security library for SMIME support

  • osdt_smime.jar - security library for SMIME support

  • share.jar - a charting support library

  • versioninfo.jar - contains version information for BI Publisher

  • xdoparser.jar - the scalable XML parser and XSLT 2.0 engine (10g)

  • xdoparser11g.jar - the scalable XML parser and XSLT 2.0 engine (11g)

  • xmlparserv2.jar - 11g XDK (SAX and DOM)

7.3 Obtaining the Libraries

If you are using Oracle JDeveloper, then the charting and XML Parser libraries are 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.

The easiest method to obtain the libraries is to download and install the Template Builder for Microsoft Word Add-in. Download the Template Builder for Word from the Home page, under Get Started, click Download BI Publisher Tools, then click Template Builder for Word.

The JAR files are packaged with the Template Builder in the jlib library under the install directory.

A sample path to jlib would be:

C:\Program Files\Oracle\BI Publisher\BI Publisher Desktop\Template Builder for Word\jlib

7.4 PDF Form Processing Engine

This section discusses how to use the RTP Processor Engine, and includes the following topics:

7.4.1 Overview of the 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:

  • Retrieve a list of field names from a PDF template

  • Convert XML data into XFDF using XSLT

7.4.2 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:

  • Using input/output file names

  • Using input/output streams

  • Using an input XML data string

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

Input:

  • Template file name (String)

  • XML file name (String)

Output:

  • PDF file name (String)

Example 7-1 Sample Code for Merging XML Data with PDF Templates Using Input/Output File Names

import oracle.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.process();

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

Input:

  • PDF Template (Input Stream)

  • XML Data (Input Stream)

Output:

  • PDF (Output Stream)

Example 7-2 Sample Code for Merging XML Data with PDF Templates Using Input/Output Streams

import java.io.*;
import oracle.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.process();

  fIs.close();
  fOs.close();

 

7.4.5 Merging an XML Data String with a PDF Template

Input:

  • Template file name (String)

  • XML data (String)

Output:

  • PDF file name (String)

Example 7-3 Sample Code for Merging XML Data Strings with PDF Templates

import oracle.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.process();

7.4.6 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:

  • PDF Template

Output:

  • Enumeration Object

Example 7-4 Sample Code for Retrieving a List of Field Names

import java.util.Enumeration;
import oracle.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));
}

7.4.7 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 enables you to append XML data by calling appendXML() methods. Note that you must set the appropriate XSL style sheet 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:

Example 7-5 Sample XFDF Data

<?xml version="1.0" encoding="UTF-8"?>
<xfdf xmlns="http://ns.adobe.com/xfdf/">
<fields>
 <field name="TITLE">
  <value>Purchase Order</value>
  </field>
 <field name="SUPPLIER_TITLE">
   <value>Supplie</value>
 </field>
  ...
 </fields>

The following code example shows how you can use the API:

Example 7-6 Sample Code for Retrieving Internal XFDF Documents

import oracle.xdo.template.FormProcessor;
import oracle.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()); 

7.4.8 Converting XML Data into XFDF Format Using XSLT

Use an XSL style sheet 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 perform the transformation:

Example 7-7 Sample XLST for Transforming XML Data into XFDF Format

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

Example 7-8 Sample Code for Executing Transformation of XML Data into XFDF Format

import java.io.*;
import oracle.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());

7.5 RTF Processor Engine

This section discusses how to use the RTP Processor Engine, and includes the following topics:

7.5.1 Pairing with XLIFF FIle

The RTFProcessor can generate the pairing XLIFF file. The API example is as follows:

Example 7-9 Sample Code for Generating Pairing XLIFF Files

public static void main(String[] args)
{
RTFProcessor rtfp = new RTFProcessor(args[0]); //input RTF template
rtfp.setOutput(args[1]); // XSL output file
rtfp.setXLIFFOutput(args[2]); // XLIFF output file
rtfp.process();
System.exit(0);
}

To generate the translated report, call FOProcessor as follows:

Example 7-10 Sample Code for Generating Translated Reports

FOProcessor p1 = new FOProcessor();
p1.setXLIFF(xliff);// set xliff file, which is generated from RTFProcessor
p1.setData(xml); // set data file
p1.setTemplate(xsl); // set xsl file
p1.setOutput(pdf);
p1.generate();

7.5.2 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:

7.5.2.1 Generating XSL with Input/Output File Names

Input:

  • RTF file name (String)

Output:

  • XSL file name (String)

Example 7-11 Sample Code for Generating XSL with Input/Output File Names

import oracle.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);
 }

7.5.2.2 Generating XSL with Input/Output Stream

Input:

  • RTF (InputStream)

Output:

  • XSL (OutputStream)

Example 7-12 Sample Code for Generating XSL with Input/Output Streams

import oracle.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);
  }

7.6 FO Processor Engine

This section discusses how to use the FO Processor Engine, and includes the following topics:

7.6.1 Major Features of the FO Processor

The FO Processor Engine provides the following features:

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

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

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

7.6.1.4 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 a single page:

Figure 7-2 Example of Wide Table

Surrounding text describes Figure 7-2 .

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.

Figure 7-3 Example of Horizontal Table Break on Wide Table

Surrounding text describes Figure 7-3 .

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

Figure 7-4 Example of Horizontal Table Break and Column Repeating on Wide Table

Surrounding text describes Figure 7-4 .

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

The FO Processor can generate output in PDF, RTF, HTML, or Excel (HTML) from either of the following input types:

  • Template (XSL) and Data (XML) combination

  • FO object

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

  • FORMAT_PPTX

  • FORMAT_MHTML

  • FORMAT_PPTMHT

  • FORMAT_EXCEL_MHTML

  • FORMAT_PDFZ

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.

7.6.3 Generating Output Using File Names

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)

Example 7-13 Sample Code for Generating Output Using File Names

import oracle.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);
  }

7.6.4 Generating Output Using Streams

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)

Example 7-14 Sample Code for Generating Output Using Streams

import java.io.InputStream;
import java.io.OutputStream;
import oracle.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);
    
  }

7.6.5 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:

  • XML data (Array)

  • XSL data (template) (Array)

Output:

  • File Name (String)

Example 7-15 Sample Code for Generating Output from XSL Template Arrays and XML Data

import java.io.InputStream;
import java.io.OutputStream;
import oracle.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);
    }

  }
    

7.6.6 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. You can use the output from this utility to generate your final output. See Section 7.6.2, "Generating Output from an XML File and an XSL File."

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

Input:

  • XML file

  • XSL file

Output:

  • XSL-FO (InputStream)

Example 7-16 Sample Code for Creating XSL-FO from XML and XSL Files

import oracle.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);
  }

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

Input:

  • XML File 1

  • XML File 2

  • XSL File 1

  • XSL File 2

Output:

  • XSL-FO (InputStream)

Example 7-17 Sample Code for Creating XSL-FO from Two XML Files and Two XSL Files

import oracle.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);
  }

7.6.6.3 Merging Two XSL-FO Files

Input:

  • Two XSL-FO file names (Array)

Output:

  • One XSL-FO (InputStream)

Example 7-18 Sample Code for Merging Two XSL-FO Files

import oracle.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);
  }
 

7.6.6.4 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 style sheet 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.

7.6.6.5 Generating Output Using File Names

Input:

  • FO file name (String)

Output:

  • PDF file name (String)

Example 7-19 Sample Code for Generating Output Using File Names

import oracle.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);
  }

7.6.6.6 Generating Output Using Streams

Input:

  • FO data (InputStream)

Output:

  • Output (OutputStream)

Example 7-20 Sample Code for Generating Output Using Streams

import java.io.InputStream;
import java.io.OutputStream;
import oracle.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);
    }
  }

7.6.6.7 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:

  • FO data (Array)

Output:

  • Output File Name (String)

Example 7-21 Sample Code for Generating Output with an Array of FO Data

import java.lang.reflect.Array;
import oracle.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);
    }

  }
          

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

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

7.7.1.1 Merging PDF Documents 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:

  • PDF_1 file name (String)

  • PDF_2 file name (String)

Output:

  • PDF file name (String)

Example 7-22 Sample Code for Merging PDF Documents with Input/Output File Names

import java.io.*;
import oracle.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();
    }
  }

7.7.1.2 Merging PDF Documents with Input/Output Streams

Input:

  • PDF Documents (InputStream Array)

Output:

  • PDF Document (OutputStream)

Example 7-23 Merging PDF Documents with Input/Output Streams

import java.io.*;
import oracle.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;
    }
  }

7.7.1.3 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:

  • PDF Documents (InputStream Array)

  • Background PDF Document (InputStream)

Output:

  • PDF Document (OutputStream)

Example 7-24 Sample Code for Merging PDF Documents with Background to Place Page Numbering

import java.io.*;
import oracle.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;
    }
  }

7.7.1.4 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:

  • 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 Array)

Output:

  • PDF Document (OutputStream)

Example 7-25 Sample Code for Adding Page Numbers to Merged PDF Documents

import java.io.*;
import oracle.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;
    }
  }

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

7.7.2.1 Setting a Text 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)

Example 7-26 Sample Code for Setting a Text Watermark in PDF Documents

import java.io.*;
import oracle.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;
    }
  }

7.7.2.2 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:

  • PDF Documents (InputStream)

  • Image File (InputStream)

Output:

  • PDF Document (OutputStream)

Example 7-27 Sample Code for Setting an Image Watermark in PDF Documents

import java.io.*;
import oracle.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;
    }
  }

7.8 PDF Bookbinder 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.

7.8.1 Usage

The table of contents formatting and style is defined by 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. You can also pass as parameters a title page, cover page, and closing pages for each chapter or section.

7.8.2 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, and 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:

Figure 7-5 Structure of XML Control File

Surrounding text describes Figure 7-5 .

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 are specially recognized BI Publisher protocols, such as:

  • xdoxsl:/// used to load subtemplate.

  • "xdo://" - used to load other resources such as images.

7.8.3 Command Line Options

Following is an example of the command line usage:

Example 7-28 Sample of Command Line Options

java oracle.xdo.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.

7.8.4 API Method Call

The following is an example of an API method call:

Example 7-29 Sample 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();

7.9 PDF Digital Signature Engine

This section discusses how to use the PDF Digital Signature Engine, and includes the following topics:

7.9.1 Overview of the PDF Digital Signature Engine

The PDF Digital Signature Engine creates signed PDF documents by processing unsigned PDF documents with a signature field name and a password-protected Personal Information Exchange (PFX) file. PFX files adhere to the Public Key Cryptography Standards #12 (PCKS-12) format and contain a digital certificate and a corresponding private key.

To create signed PDF documents, see Section 7.9.2, "Signing PDF Documents."

To distribute or deliver signed PDF documents, use the Schedule Service. See Chapter 3, "ScheduleService."

To verify signed PDF documents, see Section 7.9.4, "Verifying Signed PDF Documents."

7.9.2 Signing PDF Documents

Signing a PDF document requires the following items:

After you obtain or create the items listed above, you are ready to sign a PDF document.

To sign a PDF document, process your PDF file with your PFX file using the PDFSignature Java API that Oracle BI Publisher provides. Example 7-30 provides sample code for this purpose.

Example 7-30 Sample Code for Creating Signed PDF Documents

String workDir = "C:/projects/";
    String inPDF = workDir + "VerySimpleContent.pdf";
    String outPDF = workDir + "VerySimpleContent_signed.pdf";
    String pkcs12File = workDir + "YourName.pfx";
 
    try
    {
      PDFSignature pdfSignature = new PDFSignature(inPDF, outPDF);
      pdfSignature.init("password123", pkcs12File);
      // If your PDF document does not have a signature field, uncomment the
      // following line of code, which adds a signature field with the name
      // "Signature1".
      // pdfSignature.addSignatureField(1, PDFSignature.PDF_SIGNFIELD_UPPER_RIGHT, "Signature1", -1, -1);
      pdfSignature.sign("Signature1", "Reason to Sign");
      pdfSignature.cleanup();
    }
    catch(Throwable t)
    {
      t.printStackTrace();
    }

7.9.3 Delivering Signed PDF Documents

To distribute or deliver signed PDF documents, use the Schedule Service. See Chapter 3, "ScheduleService."

7.9.4 Verifying Signed PDF Documents

You can verify signed PDF documents by processing them with your digital certificate. Example 7-31 provides sample code for this purpose.

Example 7-31 Sample Code for Verifying Signed PDF Documents

    String workDir = "C:/projects/";
    String inPDF = workDir + "VerySimpleContent_signedWithAcrobat.pdf";
    String trustedRootCert = workDir + "VerisignFreeCertificate.cer";
    File trustedRootCertFile = new File(trustedRootCert);
    Vector trustedCerts = new Vector();
    trustedCerts.addElement(trustedRootCertFile);
 
    try
    {
      PDFSignature pdfSignature = new PDFSignature(inPDF);
      pdfSignature.init();
      SignatureFields signFields = pdfSignature.getSignatureFields();
      Vector signedFieldNames = signFields.getSignatureFieldNames();
      int size = signedFieldNames.size();
      for(int i = 0 ; i < size ; i++)
      {
        String signFieldName = (String)signedFieldNames.elementAt(i);
        SignatureField signField = signFields.getSignatureField(signFieldName);
        boolean isValid = signField.verifyDocument();
        System.out.println("Valid? : " + isValid);
        boolean isCertValid = signField.verifyCertificates(trustedCerts, null);
        System.out.println("Trusted? : " + isCertValid);
      }
    }
    catch(Throwable t)
    {
      t.printStackTrace();
    }

7.10 eText Processor

The eText Processor enables you to convert RTF eText templates to XSL, and merge the XSL with XML to produce text output for EDI and EFT transmissions.

7.10.1 Converting RTF eText Templates to XSL

The following is an example of an API method call that converts an RTF eText template to XSL:

Example 7-32 Sample Code for Converting RTF eText Templates to XSL

String rtf = "test.rtf"; // etext template filename
String xsl = "out.xsl"; //  xsl-fo filename
Properties prop = new Properties();

        try
     {
         EFTProcessor p = new EFTProcessor();   
         
         p.setTemplate(rtf);
           p.setConfig(prop);
         p.setOutput(xsl);
         p.process();
     }
     catch (Exception e)
     {
       e.printStackTrace();
     }

7.10.2 Producing Text Output for EDI and EFT Transmissions

The following is an example of an API method call that merges XSL with XML to produce eText output:

Example 7-33 Sample Code for Producing Text Output for EDI and EFT Transmissions

String rtf = "test.rtf"; // etext template filename
String xml = "data.xml"; //xml data filename
String etext = "etext.txt"; //etext output filename
Properties prop = new Properties();

try
     {
         EFTProcessor p = new EFTProcessor();   
         p.setConfig(prop);
         p.setTemplate(rtf);
         p.setData(xml);
         p.setOutput(etext);
         p.process();
     }
     catch (Exception e)
     {
       e.printStackTrace();
     }

7.11 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:

  • 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

7.11.1 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:

Figure 7-6 Hierarchy and Elements of the Document Processor XML File

Surrounding text describes Figure 7-6 .

The following table describes each of the elements:

Table 7-1 Elements in Document Processor XML File Hierarchy

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.

Thex-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 sub-elements. 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 sub-elements for the actual data. This must not include an XML declaration.


7.11.2 XML File Samples

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

7.11.2.1 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:

Example 7-34 Sample XML for Defining Two Data Sets

<?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>

7.11.2.2 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".

Example 7-35 Sample XML for Defining Multiple Templates and Data

<?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>

7.11.2.3 Retrieving templates over HTTP

This sample is identical to the previous example, except in this case the two templates are retrieved over HTTP:

Example 7-36 Sample XML for Retrieving Templates 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>

7.11.2.4 Retrieving data over HTTP

This sample builds on the previous example and shows one template with two data sources, all retrieved through HTTP; and a second template retrieved through HTTP with its two data sources embedded in the XML:

Example 7-37 Sample XML for Retrieving Data 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\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>

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

Example 7-38 Sample XML for Generating More than One 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>

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

Example 7-39 Sample XML for Defining Page Numbers

<?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>

7.11.3 Invoke Processors

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

7.11.3.1 Invoking Processors with Input File Name

Input:

  • Data file name (String)

  • Directory for Temporary Files (String)

Example 7-40 Sample Code for Invoking Processors with Input File Names

import oracle.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);
  }

7.11.3.2 Invoking Processors with InputStream

Input:

  • Data file (InputStream)

  • Directory for Temporary Files (String)

Example 7-41 Sample Code for Invoking Processors with Input Streams

import oracle.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);
  }

 

7.12 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 run time, you can use the font properties to specify the location of the font. At run time 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.

7.12.1 Setting Properties

The properties can be set in the following ways:

  • At run time, 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).

7.12.1.1 Passing Properties to the FO Engine

To pass a property as a Property object, set the name/value pair for the property before calling the FO Processor, as shown in the following example:

Input:

  • XML file name (String)

  • XSL file name (String)

Output:

  • PDF file name (String)

Example 7-42 Sample Code for Passing Properties to the FO Engine

import oracle.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);
  }

7.12.1.2 Passing a Configuration File to the FO Processor

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)

Example 7-43 Sample Code for Passing a Configuration File to the FO Processor

import oracle.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);
  }

7.12.1.3 Passing Properties to the Document Processor

Input:

  • Data file name (String)

  • Directory for Temporary Files (String)

Output:

  • PDF FIle

Example 7-44 Sample Code for Passing Properties to the Document Processor

import oracle.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);
  }

7.13 Advanced Barcode Formatting

For the advanced formatting to work in the template, you must provide a Java class with the appropriate methods to format the data at run time. Many font vendors offer the code with their fonts to perform the formatting; these must be incorporated as methods into a class that is available to the BI Publisher formatting libraries at run time. There are some specific interfaces that you must provide in the class for the library to call the correct method for encoding.

If you use one of the three barcodes provided with BI Publisher, you do not need to provide the Java class. For more information see "Using the Barcode Fonts Shipped with BI Publisher" in the Oracle Fusion Middleware Report Designer's Guide for Oracle Business Intelligence Publisher.

You must implement the following methods in this class:

/**
 * Return a unique ID for this barcode 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.

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:

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.

Example 7-45 Sample Code for Advanced Barcode Formatting

package oracle.xdo.template.rtf.util.barcoder;

import java.util.Hashtable;
import java.lang.reflect.Method;
import oracle.xdo.template.rtf.util.XDOBarcodeEncoder;
import oracle.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.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 successfully encode their data for barcode output.

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