Create a chart using JFreeChart and incorporate it into a PDF document

This is an example of an utility app useful to automate the generation of graphs and PDFs containing graphs. One use case could be a stock market research platform that does Technical Analysis on stock prices, detects patterns and generates PDF reports automatically.

To start, create a new Java project or open an existing one, then add JFreeChart https://github.com/jfree/jfreechart and PDFBox https://pdfbox.apache.org/ dependencies in pom.xml:

<dependencies>
   ...
    <dependency>
      <groupId>org.jfree</groupId>
      <artifactId>jfreechart</artifactId>
      <version>1.5.3</version>
    </dependency>
    <dependency>
      <groupId>org.apache.pdfbox</groupId>
      <artifactId>pdfbox</artifactId>
      <version>2.0.27</version>
    </dependency>
    ...
</dependencies>

Create a new package com.pdf.pdf_charts and add a dummy time series data generator:

package com.pdf.pdf_charts;

import org.jfree.data.general.SeriesException;
import org.jfree.data.time.Day;
import org.jfree.data.time.TimeSeries;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;

public class DummyDataGenerator {

    public static TimeSeries generate(String name, int nrOfElements, double startValue) {
        final TimeSeries series = new TimeSeries( name );
        LocalDateTime dtl = LocalDateTime.now();
        double value = startValue;

        for (int i = 0; i < nrOfElements; i++) {
            var ld = dtl.minus(nrOfElements - i, ChronoUnit.DAYS);
            var current = new Day(ld.getDayOfMonth(), ld.getMonthValue(), ld.getYear());

            try {
                value = value + Math.random( ) - 0.5;
                series.add( current , Double.valueOf(value) );
            } catch ( SeriesException e ) {
                System.err.println( "Error adding to series" );
            }
        }
        return series;
    }
}

How to generate a chart with JFreeChart

Create a class named ChartGenerator in com.pdf.pdf_charts.

The function fromTimeSeries creates and returns a Chart from an array of timeSeries, with a title, X/Y axes labels and an array of chart lines colors corresponding to each time series. Grid lines strokes and colors, background colors etc. are all customizable. You can also save the chart as jpg with saveAsJPG by specifying the output file path and the picture's dimensions.

package com.pdf.pdf_charts;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtils;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.AbstractRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.XYDataset;

import java.awt.*;
import java.io.File;
import java.io.IOException;

public class ChartGenerator {

    public static JFreeChart fromTimeSeries(TimeSeries[] timeSeries, String title, String XLabel, String YLabel, Color[] seriesColors) {
        TimeSeriesCollection tsCollection = new TimeSeriesCollection();
        for (int i = 0; i < timeSeries.length; i++) {
            tsCollection.addSeries(timeSeries[i]);
        }

        final XYDataset dataset = (XYDataset) tsCollection;
        JFreeChart timeChart = ChartFactory.createTimeSeriesChart(
                title,
                XLabel,
                YLabel,
                dataset,
                true,
                false,
                false);


        // customize colors etc.
        XYPlot plot = timeChart.getXYPlot();
        plot.setBackgroundPaint(Color.decode("#F1F0EA"));

        plot.setRangeGridlinesVisible(true);
        plot.setRangeGridlinePaint(Color.black);
        plot.setRangeGridlineStroke(new BasicStroke(0.1f));
        plot.setDomainGridlinesVisible(true);
        plot.setDomainGridlinePaint(Color.BLACK);
        plot.setDomainGridlineStroke(new BasicStroke(0.1f));

        plot.setDomainZeroBaselineVisible(false);
        plot.setOutlineVisible(false);
        plot.setRangeZeroBaselineVisible(false);
        plot.setDomainCrosshairVisible(false);

        XYItemRenderer renderer = plot.getRenderer();
        // each time series can have a different color
        for (int i = 0; i < timeSeries.length; i++) {
            renderer.setSeriesPaint(i, seriesColors[i]);
        }
        renderer.setDefaultStroke(new BasicStroke(1.0f));
        renderer.setDefaultOutlineStroke(new BasicStroke(0.5f));
        ((AbstractRenderer) renderer).setAutoPopulateSeriesStroke(false);

        return timeChart;
    }

    public static void saveAsJPG(JFreeChart chart, String filePath, int width, int height) {
        File timeChart = new File(filePath);
        try {
            ChartUtils.saveChartAsJPEG(timeChart, chart, width, height);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

Example of a generated graph:

Result

Generate the PDF containing a chart

Create a class PDFWithChart in the package com.pdf.pdf_charts.

The function generatePDF takes as input the chart data and a path as String (where to save the pdf). It embeds the chart into the document at some coordinates .drawImage(pdfChartImage, 20, 300); and inserts a title.

package com.pdf.pdf_charts;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.jfree.chart.JFreeChart;
import org.jfree.data.time.TimeSeries;

import java.awt.*;
import java.awt.image.BufferedImage;

public class PDFWithChart {

    public static void generatePDF(String filePath, JFreeChart chart) {
        try {
            PDDocument document = new PDDocument();
            PDPage page = new PDPage();
            document.addPage(page);
            PDPageContentStream content = new PDPageContentStream(document, page);

            // add graph
            BufferedImage chartImage = chart.createBufferedImage(500, 400);
            PDImageXObject pdfChartImage = JPEGFactory.createFromImage(document, chartImage, 1f);
            content.drawImage(pdfChartImage, 20, 300);

            // Doc. Title
            content.beginText();
            content.setFont(PDType1Font.TIMES_BOLD, 24);
            content.newLineAtOffset(50, 750);
            content.showText("Random Data Graph");
            content.endText();

            // save
            content.close();
            document.save(filePath);
            document.close();
        } catch (Exception e) {
            System.out.println(e);
        }
    }
}

To try it, let's add a main function that generates a dummy data time series, creates the chart and saves it as JPG, then embeds it into the PDF:

    public static void main(String[] args) {
        // generate dummy data stream
        TimeSeries dummyData = DummyDataGenerator.generate("Price", 1000, 50);
        TimeSeries[] series = {dummyData};
        Color[] colors = {Color.decode("#000000")};

        // generate chart
        var chartData = ChartGenerator.fromTimeSeries(series, "Random Data", "Date", "Price", colors);

        // save as jpg
        ChartGenerator.saveAsJPG(chartData, "chart.jpg", 400, 300);

        // generate pdf
        generatePDF("chart.pdf", chartData);
    }

Full source code is here;