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}