001/* ====================================================== 002 * JFreeChart : a chart library for the Java(tm) platform 003 * ====================================================== 004 * 005 * (C) Copyright 2000-present, by David Gilbert and Contributors. 006 * 007 * Project Info: https://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 private ExportUtils() { 061 // no need to instantiate 062 } 063 064 /** 065 * Returns {@code true} if JFreeSVG is on the classpath, and 066 * {@code false} otherwise. The JFreeSVG library can be found at 067 * https://www.jfree.org/jfreesvg/ 068 * 069 * @return A boolean. 070 */ 071 public static boolean isJFreeSVGAvailable() { 072 Class<?> svgClass = null; 073 try { 074 svgClass = Class.forName("org.jfree.svg.SVGGraphics2D"); 075 } catch (ClassNotFoundException e) { 076 // see if there is maybe an older version of JFreeSVG (different package name) 077 try { 078 svgClass = Class.forName("org.jfree.graphics2d.svg.SVGGraphics2D"); 079 } catch (ClassNotFoundException e2) { 080 // svgClass will be null so the function will return false 081 } 082 } 083 return (svgClass != null); 084 } 085 086 /** 087 * Returns {@code true} if OrsonPDF (or JFreePDF) is on the classpath, and 088 * {@code false} otherwise. The OrsonPDF library can be found at 089 * https://github.com/jfree/orsonpdf. JFreePDF is a modular version of 090 * the same library, requiring Java 11 or later. Since JFreeChart might 091 * be used in a modular context, this function has been modified (in version 092 * 1.5.3) to detect JFreePDF also. 093 * 094 * @return A boolean. 095 */ 096 public static boolean isOrsonPDFAvailable() { 097 Class<?> pdfDocumentClass = null; 098 try { 099 pdfDocumentClass = Class.forName("com.orsonpdf.PDFDocument"); 100 } catch (ClassNotFoundException e) { 101 // check also for JFreePDF, which is the new modular version of OrsonPDF 102 try { 103 pdfDocumentClass = Class.forName("org.jfree.pdf.PDFDocument"); 104 } catch (ClassNotFoundException e2) { 105 // pdfDocumentClass will be null so the function will return false 106 } 107 } 108 return (pdfDocumentClass != null); 109 } 110 111 /** 112 * Writes the current content to the specified file in SVG format. This 113 * will only work when the JFreeSVG library is found on the classpath. 114 * Reflection is used to ensure there is no compile-time dependency on 115 * JFreeSVG. 116 * 117 * @param drawable the drawable ({@code null} not permitted). 118 * @param w the chart width. 119 * @param h the chart height. 120 * @param file the output file ({@code null} not permitted). 121 */ 122 public static void writeAsSVG(Drawable drawable, int w, int h, File file) { 123 if (!ExportUtils.isJFreeSVGAvailable()) { 124 throw new IllegalStateException("JFreeSVG is not present on the classpath."); 125 } 126 Args.nullNotPermitted(drawable, "drawable"); 127 Args.nullNotPermitted(file, "file"); 128 try { 129 Class<?> svg2Class; 130 try { 131 svg2Class = Class.forName("org.jfree.graphics2d.svg.SVGGraphics2D"); 132 } catch (ClassNotFoundException ex) { 133 svg2Class = Class.forName("org.jfree.svg.SVGGraphics2D"); 134 } 135 136 Constructor<?> c1 = svg2Class.getConstructor(int.class, int.class); 137 Graphics2D svg2 = (Graphics2D) c1.newInstance(w, h); 138 Rectangle2D drawArea = new Rectangle2D.Double(0, 0, w, h); 139 drawable.draw(svg2, drawArea); 140 Class<?> svgUtilsClass; 141 try { 142 svgUtilsClass = Class.forName("org.jfree.graphics2d.svg.SVGUtils"); 143 } catch (ClassNotFoundException ex) { 144 svgUtilsClass = Class.forName("org.jfree.svg.SVGUtils"); 145 } 146 Method m1 = svg2Class.getMethod("getSVGElement", (Class[]) null); 147 String element = (String) m1.invoke(svg2, (Object[]) null); 148 Method m2 = svgUtilsClass.getMethod("writeToSVG", File.class, 149 String.class); 150 m2.invoke(svgUtilsClass, file, element); 151 } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | 152 NoSuchMethodException | SecurityException | IllegalArgumentException | 153 InvocationTargetException ex) { 154 throw new RuntimeException(ex); 155 } 156 } 157 158 /** 159 * Writes a {@link Drawable} to the specified file in PDF format. This 160 * will only work when the OrsonPDF library is found on the classpath. 161 * Reflection is used to ensure there is no compile-time dependency on 162 * OrsonPDF. 163 * 164 * @param drawable the drawable ({@code null} not permitted). 165 * @param w the chart width. 166 * @param h the chart height. 167 * @param file the output file ({@code null} not permitted). 168 */ 169 public static void writeAsPDF(Drawable drawable, 170 int w, int h, File file) { 171 if (!ExportUtils.isOrsonPDFAvailable()) { 172 throw new IllegalStateException("Neither OrsonPDF nor JFreePDF is present on the classpath."); 173 } 174 Args.nullNotPermitted(drawable, "drawable"); 175 Args.nullNotPermitted(file, "file"); 176 try { 177 Class<?> pdfDocClass; 178 try { 179 pdfDocClass = Class.forName("com.orsonpdf.PDFDocument"); 180 } catch (ClassNotFoundException e) { 181 pdfDocClass = Class.forName("org.jfree.pdf.PDFDocument"); 182 } 183 Object pdfDoc = pdfDocClass.getDeclaredConstructor().newInstance(); 184 Method m = pdfDocClass.getMethod("createPage", Rectangle2D.class); 185 Rectangle2D rect = new Rectangle(w, h); 186 Object page = m.invoke(pdfDoc, rect); 187 Method m2 = page.getClass().getMethod("getGraphics2D"); 188 Graphics2D g2 = (Graphics2D) m2.invoke(page); 189 Rectangle2D drawArea = new Rectangle2D.Double(0, 0, w, h); 190 drawable.draw(g2, drawArea); 191 Method m3 = pdfDocClass.getMethod("writeToFile", File.class); 192 m3.invoke(pdfDoc, file); 193 } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | 194 NoSuchMethodException | SecurityException | IllegalArgumentException | 195 InvocationTargetException ex) { 196 throw new RuntimeException(ex); 197 } 198 } 199 200 /** 201 * Writes the current content to the specified file in PNG format. 202 * 203 * @param drawable the drawable ({@code null} not permitted). 204 * @param w the chart width. 205 * @param h the chart height. 206 * @param file the output file ({@code null} not permitted). 207 * 208 * @throws FileNotFoundException if the file is not found. 209 * @throws IOException if there is an I/O problem. 210 */ 211 public static void writeAsPNG(Drawable drawable, int w, int h, 212 File file) throws FileNotFoundException, IOException { 213 BufferedImage image = new BufferedImage(w, h, 214 BufferedImage.TYPE_INT_ARGB); 215 Graphics2D g2 = image.createGraphics(); 216 drawable.draw(g2, new Rectangle(w, h)); 217 try (OutputStream out = new BufferedOutputStream(new FileOutputStream(file))) { 218 ImageIO.write(image, "png", out); 219 } 220 } 221 222 /** 223 * Writes the current content to the specified file in JPEG format. 224 * 225 * @param drawable the drawable ({@code null} not permitted). 226 * @param w the chart width. 227 * @param h the chart height. 228 * @param file the output file ({@code null} not permitted). 229 * 230 * @throws FileNotFoundException if the file is not found. 231 * @throws IOException if there is an I/O problem. 232 */ 233 public static void writeAsJPEG(Drawable drawable, int w, int h, 234 File file) throws FileNotFoundException, IOException { 235 BufferedImage image = new BufferedImage(w, h, 236 BufferedImage.TYPE_INT_RGB); 237 Graphics2D g2 = image.createGraphics(); 238 drawable.draw(g2, new Rectangle(w, h)); 239 try (OutputStream out = new BufferedOutputStream(new FileOutputStream(file))) { 240 ImageIO.write(image, "jpg", out); 241 } 242 } 243 244}