001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-present, by David Gilbert and Contributors.
006 *
007 * Project Info:  http://www.jfree.org/jfreechart/index.html
008 *
009 * This library is free software; you can redistribute it and/or modify it
010 * under the terms of the GNU Lesser General Public License as published by
011 * the Free Software Foundation; either version 2.1 of the License, or
012 * (at your option) any later version.
013 *
014 * This library is distributed in the hope that it will be useful, but
015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017 * License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this library; if not, write to the Free Software
021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
022 * USA.
023 *
024 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 
025 * Other names may be trademarks of their respective owners.]
026 *
027 * ----------------
028 * ExportUtils.java
029 * ----------------
030 * (C) Copyright 2014-present, by David Gilbert and Contributors.
031 *
032 * Original Author:  David Gilbert;
033 * Contributor(s):   -;
034 *
035 */
036
037package org.jfree.chart.util;
038
039import java.awt.Graphics2D;
040import java.awt.Rectangle;
041import java.awt.geom.Rectangle2D;
042import java.awt.image.BufferedImage;
043import java.io.BufferedOutputStream;
044import java.io.File;
045import java.io.FileNotFoundException;
046import java.io.FileOutputStream;
047import java.io.IOException;
048import java.io.OutputStream;
049import java.lang.reflect.Constructor;
050import java.lang.reflect.InvocationTargetException;
051import java.lang.reflect.Method;
052import javax.imageio.ImageIO;
053import org.jfree.chart.ui.Drawable;
054
055/**
056 * Utility functions for exporting charts to SVG and PDF format.
057 */
058public class ExportUtils {
059
060    /**
061     * Returns {@code true} if JFreeSVG is on the classpath, and 
062     * {@code false} otherwise.  The JFreeSVG library can be found at
063     * http://www.jfree.org/jfreesvg/
064     * 
065     * @return A boolean.
066     */
067    public static boolean isJFreeSVGAvailable() {
068        Class<?> svgClass = null;
069        try {
070            svgClass = Class.forName("org.jfree.svg.SVGGraphics2D");
071        } catch (ClassNotFoundException e) {
072            // see if there is maybe an older version of JFreeSVG (different package name)
073            try {
074                svgClass = Class.forName("org.jfree.graphics2d.svg.SVGGraphics2D");
075            } catch (ClassNotFoundException e2) {
076                // svgClass will be null so the function will return false
077            }
078        }
079        return (svgClass != null);
080    }
081
082    /**
083     * Returns {@code true} if OrsonPDF (or JFreePDF) is on the classpath, and 
084     * {@code false} otherwise.  The OrsonPDF library can be found at
085     * https://github.com/jfree/orsonpdf.  JFreePDF is a modular version of
086     * the same library, requiring Java 11 or later.  Since JFreeChart might
087     * be used in a modular context, this function has been modified (in version
088     * 1.5.3) to detect JFreePDF also.
089     * 
090     * @return A boolean.
091     */
092    public static boolean isOrsonPDFAvailable() {
093        Class<?> pdfDocumentClass = null;
094        try {
095            pdfDocumentClass = Class.forName("com.orsonpdf.PDFDocument");
096        } catch (ClassNotFoundException e) {
097            // check also for JFreePDF, which is the new modular version of OrsonPDF
098            try {
099                pdfDocumentClass = Class.forName("org.jfree.pdf.PDFDocument");
100            } catch (ClassNotFoundException e2) {
101                // pdfDocumentClass will be null so the function will return false
102            }
103        }
104        return (pdfDocumentClass != null);
105    }
106
107    /**
108     * Writes the current content to the specified file in SVG format.  This 
109     * will only work when the JFreeSVG library is found on the classpath.
110     * Reflection is used to ensure there is no compile-time dependency on
111     * JFreeSVG.
112     * 
113     * @param drawable  the drawable ({@code null} not permitted).
114     * @param w  the chart width.
115     * @param h  the chart height.
116     * @param file  the output file ({@code null} not permitted).
117     */
118    public static void writeAsSVG(Drawable drawable, int w, int h, File file) {
119        if (!ExportUtils.isJFreeSVGAvailable()) {
120            throw new IllegalStateException("JFreeSVG is not present on the classpath.");
121        }
122        Args.nullNotPermitted(drawable, "drawable");
123        Args.nullNotPermitted(file, "file");
124        try {
125            Class<?> svg2Class;
126            try {
127                svg2Class = Class.forName("org.jfree.graphics2d.svg.SVGGraphics2D");
128            } catch (ClassNotFoundException ex) {
129                svg2Class = Class.forName("org.jfree.svg.SVGGraphics2D");
130            }
131            
132            Constructor<?> c1 = svg2Class.getConstructor(int.class, int.class);
133            Graphics2D svg2 = (Graphics2D) c1.newInstance(w, h);
134            Rectangle2D drawArea = new Rectangle2D.Double(0, 0, w, h);
135            drawable.draw(svg2, drawArea);
136            Class<?> svgUtilsClass;
137            try {
138                svgUtilsClass = Class.forName("org.jfree.graphics2d.svg.SVGUtils");
139            } catch (ClassNotFoundException ex) {
140                svgUtilsClass = Class.forName("org.jfree.svg.SVGUtils");
141            }
142            Method m1 = svg2Class.getMethod("getSVGElement", (Class[]) null);
143            String element = (String) m1.invoke(svg2, (Object[]) null);
144            Method m2 = svgUtilsClass.getMethod("writeToSVG", File.class, 
145                    String.class);
146            m2.invoke(svgUtilsClass, file, element);
147        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException |
148                NoSuchMethodException | SecurityException | IllegalArgumentException |
149                InvocationTargetException ex) {
150            throw new RuntimeException(ex);
151        }
152    }
153
154    /**
155     * Writes a {@link Drawable} to the specified file in PDF format.  This 
156     * will only work when the OrsonPDF library is found on the classpath.
157     * Reflection is used to ensure there is no compile-time dependency on
158     * OrsonPDF.
159     * 
160     * @param drawable  the drawable ({@code null} not permitted).
161     * @param w  the chart width.
162     * @param h  the chart height.
163     * @param file  the output file ({@code null} not permitted).
164     */
165    public static void writeAsPDF(Drawable drawable,
166                                  int w, int h, File file) {
167        if (!ExportUtils.isOrsonPDFAvailable()) {
168            throw new IllegalStateException("Neither OrsonPDF nor JFreePDF is present on the classpath.");
169        }
170        Args.nullNotPermitted(drawable, "drawable");
171        Args.nullNotPermitted(file, "file");
172        try {
173            Class<?> pdfDocClass;
174            try {
175                pdfDocClass = Class.forName("com.orsonpdf.PDFDocument");
176            } catch (ClassNotFoundException e) {                
177                pdfDocClass = Class.forName("org.jfree.pdf.PDFDocument");
178            }
179            Object pdfDoc = pdfDocClass.getDeclaredConstructor().newInstance();
180            Method m = pdfDocClass.getMethod("createPage", Rectangle2D.class);
181            Rectangle2D rect = new Rectangle(w, h);
182            Object page = m.invoke(pdfDoc, rect);
183            Method m2 = page.getClass().getMethod("getGraphics2D");
184            Graphics2D g2 = (Graphics2D) m2.invoke(page);
185            Rectangle2D drawArea = new Rectangle2D.Double(0, 0, w, h);
186            drawable.draw(g2, drawArea);
187            Method m3 = pdfDocClass.getMethod("writeToFile", File.class);
188            m3.invoke(pdfDoc, file);
189        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException |
190                NoSuchMethodException | SecurityException | IllegalArgumentException |
191                InvocationTargetException ex) {
192            throw new RuntimeException(ex);
193        }
194    }
195    
196    /**
197     * Writes the current content to the specified file in PNG format.
198     * 
199     * @param drawable  the drawable ({@code null} not permitted).
200     * @param w  the chart width.
201     * @param h  the chart height.
202     * @param file  the output file ({@code null} not permitted).
203     * 
204     * @throws FileNotFoundException if the file is not found.
205     * @throws IOException if there is an I/O problem.
206     */
207    public static void writeAsPNG(Drawable drawable, int w, int h, 
208            File file) throws FileNotFoundException, IOException {
209        BufferedImage image = new BufferedImage(w, h, 
210                BufferedImage.TYPE_INT_ARGB);
211        Graphics2D g2 = image.createGraphics();
212        drawable.draw(g2, new Rectangle(w, h));
213        try (OutputStream out = new BufferedOutputStream(new FileOutputStream(file))) {
214            ImageIO.write(image, "png", out);
215        }
216    }
217
218    /**
219     * Writes the current content to the specified file in JPEG format.
220     * 
221     * @param drawable  the drawable ({@code null} not permitted).
222     * @param w  the chart width.
223     * @param h  the chart height.
224     * @param file  the output file ({@code null} not permitted).
225     * 
226     * @throws FileNotFoundException if the file is not found.
227     * @throws IOException if there is an I/O problem.
228     */
229    public static void writeAsJPEG(Drawable drawable, int w, int h, 
230            File file) throws FileNotFoundException, IOException {
231        BufferedImage image = new BufferedImage(w, h, 
232                BufferedImage.TYPE_INT_RGB);
233        Graphics2D g2 = image.createGraphics();
234        drawable.draw(g2, new Rectangle(w, h));
235        try (OutputStream out = new BufferedOutputStream(new FileOutputStream(file))) {
236            ImageIO.write(image, "jpg", out);
237        }
238    }
239 
240}