Using version 2.0 of the BFO Graph Library with BFO Report Generator

BFO Report Generator by default includes the BFO Graph Library, but the older version 1.0 library. While this is adequate for many users, people may want to use the new graphs and features available in version 2.0 of the Graph library. This article will show how to achieve this.

Web Application Usage

If you're generating PDFs from XML or JSP files and returning PDFs to the client browser then it's fairly straightforward to include version 2.x graphs by using a JSP page that includes the version 2.0 tag library, and then using the version 2.0 graphs as normal.

You will need to have version 2.x of the Graph Library deployed in your web application, container as described in the Graph Documentation, to use the newer tag library,

Note Line #2, which sets up the tag library to allow us to use a funnel chart, one of the new charts available in Graph Library 2.1.4.

<?xml version="1.0"?>
<%@ taglib uri="http://big.faceless.org/products/graph" prefix="bfg" %>
<%@ page language="java" contentType="text/xml; charset=UTF-8"%> 
<!DOCTYPE pdf PUBLIC "-//big.faceless.org//report" "report-1.1.dtd">   
<pdf> 
<body>
<p font-size="14">Integrating Version 2.0 Graphs into Report Generator</p>
<bfg:axesgraph width="600" height="600" backgroundcolor="#FFFFFF" yrotation="10" zrotation="180" xrotation="45">
 <bfg:stackedbarseries name="Funnel Example" topbarwidth="0.9" bottombarwidth="0" bargap="1.2">
  <bfg:barseries name="2001" color="#F7953A"  bordercolor="#000000">
   <bfg:data x="Data" y="7"/>
   <bfg:label x="Data" y="3" textcolor="#FFFFFF">35 percent</bfg:label>
  </bfg:barseries>
  <bfg:barseries name="2002" color="#79847E"  bordercolor="#000000">
   <bfg:data x="Data" y="3" />
   <bfg:label x="Data" y="2" textcolor="#FFFFFF">8.75 percent</bfg:label>
  </bfg:barseries>
  <bfg:barseries name="2003" color="#F6CB9E" bordercolor="#000000">
   <bfg:data x="Data" y="5" />
   <bfg:label x="Data" y="3" textcolor="#FFFFFF">18.5 percent</bfg:label>
  </bfg:barseries>
  <bfg:barseries name="2004" color="#ACA3B3" bordercolor="#000000">
   <bfg:data x="Data" y="4" />
   <bfg:label x="Data" y="3" textcolor="#FFFFFF">12.5 percent</bfg:label>
  </bfg:barseries>
  <bfg:barseries name="2005" color="#ED6032" bordercolor="#000000">
   <bfg:data x="Data" y="5"/>
   <bfg:label x="Data" y="3" textcolor="#FFFFFF">25 percent</bfg:label>
  </bfg:barseries>
 </bfg:stackedbarseries>
 <bfg:axis pos="left" type="null" bordercolor="#FFFFFF" color="none"/>
 <bfg:axis pos="bottom" type="null" color="none"/>
</bfg:axesgraph>
</body>
</pdf>

Application Usage

The rest of this article describes an approach which is no longer necessary since Report Generator release 1.1.51. The newer, simpler approach is described in this article. The following remains for posterity.

If you're generating PDFs from a Java application then it's slightly more complicated - you need to generate the version 2.0 chart and then embed a link to it in your PDF, as shown below:

// Create the graph image
InputSource graphxml = ....;
XMLGraph xmlgraph = new XMLGraph();
xmlgraph.parse(graphxml);
Graph graph = xmlgraph.getGraph();
ImageOutput image = new ImageOutput(600, 400);
graph.draw(image);
FileOutputStream out = new FileOutputStream("mygraph.png");
image.writePNG(out, 0);
out.close();

// Create the PDF with a link to the graph image inside
String reportxml = "<pdf>....<img src='mygraph.png' />...</pdf>"
ReportParser parser = ReportParser.getInstance();
InputSource source = new InputSource(new StringReader(reportxml));
source.setSystemId(new File(".").toString());
PDF pdf = parser.parse(source);

The drawback with this approach is that it relies on writing a file out to the local filesystem and linking to it, which may not be appropriate in some use cases, such as when used in a Servlet.

The alternative to writing local files is to use a data URL to reference the graph image. Data URLs (http://en.wikipedia.org/wiki/Data_URI_scheme) can be used to embed data directly into an image URL, so by using the BFO org.faceless.util.DataStreamHandler class you can embed the image into a URL and reference that from the PDF. The code them becomes:

InputSource graphxml = ....;
XMLGraph xmlgraph = new XMLGraph();
xmlgraph.parse(graphxml);
Graph graph = xmlgraph.getGraph();
ImageOutput image = new ImageOutput(600,400);
graph.draw(image);
ByteArrayOutputStream out = new ByteArrayOutputStream();
image.writePNG(out, 0);
out.close();

String urlstring = 
   DataStreamHandler.encode(out.toByteArray(), "image/png");

URL.setURLStreamHandlerFactory(DataStreamHandler.createFactory());
String reportxml = "<pdf>....<img src=\""+urlstring+"\" />...</pdf>"
ReportParser parser = ReportParser.getInstance();
InputSource source = new InputSource(new StringReader(reportxml));
source.setSystemId(new File(".").toString());
PDF pdf = parser.parse(source);
The only issues that may arise from this is if the Servlet container does not allow you to replace the URLStreamHandlerFactory (WebLogic does not allow this), but it works in most Servlet containers.

Using vector based graphs

If you want to use vector based graphs in your PDF reports then the code is similar - you just render the graph to an intermediate PDF and reference it from the main PDF report, using code such as:
InputSource graphxml = ....;
XMLGraph xmlgraph = new XMLGraph();
xmlgraph.parse(graphxml);
Graph graph = xmlgraph.getGraph();

// create intermediate PDF
PDF graphpdf = new PDF();
PDFCanvas canvas = new PDFCanvas(600, 400);
PDFPage page = graphpdf.newPage((int)canvas.getWidth(). (int)canvas.getHeight());
PDFOutput pdfout = new PDFOutput(canvas);

// draw graph to pdf
graph.draw(pdfout);
page.drawCanvas(canvas, 0, 0, canvas.getWidth(), canvas.getHeight());

// render to intermediate PDF
ByteArrayOutputStream out = new ByteArrayOutputStream();
graphpdf.render(out);
out.close();

String urlstring = 
   DataStreamHandler.encode(out.toByteArray(), "application/pdf");

URL.setURLStreamHandlerFactory(DataStreamHandler.createFactory());
String reportxml = "<pdf><head></head>" 
   + "<body size=\"A4-landscape\"><div width=\""  
   + canvas.getWidth() + "\" height=\"" 
   + canvas.getHeight() + "\" background-pdf=\""
   + urlstring + "\" /></body></pdf>";
ReportParser parser = ReportParser.getInstance();
InputSource source = new InputSource(new StringReader(reportxml));
source.setSystemId(new File(".").toString());
PDF pdf = parser.parse(source); 
If you are using JSPs and the ServletFilter then you simply use the format attribute on the axesgraph element and specify the value 'rg1pdf', as shown below:
<bfg:axesgraph width="200" height="200" format="rg1pdf">