001/* An attribute with a reference to an image. 002 003 Copyright (c) 2009-2016 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 027 */ 028package ptolemy.vergil.pdfrenderer; 029 030import java.io.File; 031import java.io.IOException; 032import java.io.RandomAccessFile; 033import java.net.URL; 034import java.nio.ByteBuffer; 035import java.nio.channels.FileChannel; 036 037import com.sun.pdfview.PDFFile; 038import com.sun.pdfview.PDFPage; 039 040import ptolemy.data.DoubleToken; 041import ptolemy.data.expr.FileParameter; 042import ptolemy.data.expr.Parameter; 043import ptolemy.data.type.BaseType; 044import ptolemy.kernel.util.Attribute; 045import ptolemy.kernel.util.IllegalActionException; 046import ptolemy.kernel.util.KernelException; 047import ptolemy.kernel.util.NameDuplicationException; 048import ptolemy.kernel.util.NamedObj; 049import ptolemy.kernel.util.Workspace; 050import ptolemy.util.FileUtilities; 051import ptolemy.vergil.kernel.attributes.VisibleAttribute; 052 053/////////////////////////////////////////////////////////////////// 054//// PDFAttribute 055 056/** 057 <p>This is an attribute that renders the first page of 058 a specified PDF file. Its <i>source</i> 059 parameter specifies a file containing the PDF, and 060 its <i>scale</i> attribute specifies a scaling factor, as a percentage. 061 </p> 062 <p> 063 This class uses pdf-renderer, obtainable from 064 <a href="https://java.net/projects/pdf-renderer/#in_browser">https://java.net/projects/pdf-renderer/</a>. 065 This is an "an open source, all Java library which renders PDF documents 066 to the screen using Java2D." This attribute can be put into a 067 Vergil diagram and its visual appearance will be be defined 068 by a PDF file. Using this attribute requires that 069 PDFRenderer.jar in the classpath, it is usually 070 found in $PTII/lib/PDFRenderer.jar. 071 </p> 072 <p>The pdf-renderer package is licensed under the 073<a href="http://www.gnu.org/copyleft/lesser.html#in_browser">GNU Lesser General Public License (LGPL)</a>. 074 </p> 075 @author Edward A. Lee 076 @version $Id$ 077 @since Ptolemy II 8.0 078 @Pt.ProposedRating Yellow (eal) 079 @Pt.AcceptedRating Red (cxh) 080 */ 081public class PDFAttribute extends VisibleAttribute { 082 /** Construct an attribute with the given name contained by the 083 * specified container. The container argument must not be null, or a 084 * NullPointerException will be thrown. This attribute will use the 085 * workspace of the container for synchronization and version counts. 086 * If the name argument is null, then the name is set to the empty 087 * string. Increment the version of the workspace. 088 * @param container The container. 089 * @param name The name of this attribute. 090 * @exception IllegalActionException If the attribute is not of an 091 * acceptable class for the container, or if the name contains a period. 092 * @exception NameDuplicationException If the name coincides with 093 * an attribute already in the container. 094 */ 095 public PDFAttribute(NamedObj container, String name) 096 throws IllegalActionException, NameDuplicationException { 097 super(container, name); 098 099 _icon = new PDFIcon(this, "_icon"); 100 _icon.setPersistent(false); 101 102 source = new FileParameter(this, "source"); 103 104 // Put the sample PDF in the local directory so that it stays with this actor. 105 // Use $CLASSSPATH intstead of $PTII so that this class can find sample.pdf 106 // under Web Start. 107 source.setExpression( 108 "$CLASSPATH/ptolemy/vergil/pdfrenderer/sample.pdf"); 109 110 scale = new Parameter(this, "scale"); 111 scale.setTypeEquals(BaseType.DOUBLE); 112 scale.setExpression("100.0"); 113 } 114 115 /////////////////////////////////////////////////////////////////// 116 //// parameters //// 117 118 /** The scale, as a percentage. 119 * This is a double that defaults to 100.0. 120 */ 121 public Parameter scale; 122 123 /** The source image file. This is a file name or URL, where the default 124 * is "$CLASSPATH/ptolemy/vergil/pdfrenderer/sample.pdf". 125 */ 126 public FileParameter source; 127 128 /////////////////////////////////////////////////////////////////// 129 //// public methods //// 130 131 /** React to a change in the source or scale attributes by changing 132 * the icon. 133 * @param attribute The attribute that changed. 134 * @exception IllegalActionException If the change is not acceptable 135 * to this container (should not be thrown). 136 */ 137 @Override 138 public void attributeChanged(Attribute attribute) 139 throws IllegalActionException { 140 if (attribute == source) { 141 try { 142 ByteBuffer byteBuffer = null; 143 FileChannel channel = null; 144 RandomAccessFile randomAccessFile = null; 145 try { 146 File file = source.asFile(); 147 randomAccessFile = new RandomAccessFile(file, "r"); 148 channel = randomAccessFile.getChannel(); 149 byteBuffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, 150 channel.size()); 151 } catch (Exception ex) { 152 URL jarURL = null; 153 // We might be under WebStart. In theory, we should be able to read 154 // the URL and create a ByteBuffer, but there are problems with the non-ascii bytes 155 // in the pdf file. The basic idea was to new BufferedOutputStream(new ByteArrayOutputStream()). 156 try { 157 jarURL = FileUtilities.nameToURL(source.getExpression(), 158 null, null); 159 } catch (Exception ex2) { 160 throw new IllegalActionException(this, ex, 161 "Failed to open " + source.getExpression() 162 + ". Tried opening as URL, exception was: " 163 + ex2); 164 } 165 if (!jarURL.toString().startsWith("jar:")) { 166 throw new IllegalActionException(this, ex, 167 "Failed to open " + source.getExpression()); 168 } else { 169 try { 170 byte[] contents = FileUtilities 171 .binaryReadURLToByteArray(jarURL); 172 byteBuffer = ByteBuffer.wrap(contents); 173 } catch (Exception ex3) { 174 throw new IllegalActionException(this, ex, 175 "Failed to open " + source.getExpression() 176 + ". Also, tried to open jar URL " 177 + jarURL + ", exception was: \n" 178 + KernelException 179 .stackTraceToString(ex3)); 180 } 181 } 182 } finally { 183 if (channel != null) { 184 channel.close(); 185 } 186 if (randomAccessFile != null) { 187 randomAccessFile.close(); 188 } 189 } 190 191 PDFFile pdffile = new PDFFile(byteBuffer); 192 193 // draw the first page to an image 194 PDFPage page = pdffile.getPage(0); 195 196 _icon.setPage(page); 197 } catch (IOException ex) { 198 // FIXME: Better would be to show an ERROR icon 199 // like ImageAttribute does. 200 throw new IllegalActionException(this, ex, 201 "Cannot read PDF file."); 202 } 203 } else if (attribute == scale) { 204 double scaleValue = ((DoubleToken) scale.getToken()).doubleValue(); 205 _icon.setScale(scaleValue); 206 } else { 207 super.attributeChanged(attribute); 208 } 209 } 210 211 /** Clone the object into the specified workspace. The new object is 212 * <i>not</i> added to the directory of that workspace (you must do this 213 * yourself if you want it there). 214 * The result is an object with no container. 215 * @param workspace The workspace for the cloned object. 216 * @exception CloneNotSupportedException Not thrown in this base class 217 * @return The new Attribute. 218 */ 219 @Override 220 public Object clone(Workspace workspace) throws CloneNotSupportedException { 221 PDFAttribute newObject = (PDFAttribute) super.clone(workspace); 222 newObject._icon = (PDFIcon) newObject.getAttribute("_icon"); 223 return newObject; 224 } 225 226 /////////////////////////////////////////////////////////////////// 227 /// private variables //// 228 229 /** The PDF icon. */ 230 private PDFIcon _icon; 231}