001/* 002 * Copyright (c) 2002-2010 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: welker $' 006 * '$Date: 2010-05-06 05:21:26 +0000 (Thu, 06 May 2010) $' 007 * '$Revision: 24234 $' 008 * 009 * Permission is hereby granted, without written agreement and without 010 * license or royalty fees, to use, copy, modify, and distribute this 011 * software and its documentation for any purpose, provided that the above 012 * copyright notice and the following two paragraphs appear in all copies 013 * of this software. 014 * 015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 019 * SUCH DAMAGE. 020 * 021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 026 * ENHANCEMENTS, OR MODIFICATIONS. 027 * 028 */ 029 030package org.geon; 031 032import java.io.BufferedReader; 033import java.io.BufferedWriter; 034import java.io.File; 035import java.io.FileInputStream; 036import java.io.FileReader; 037import java.io.FileWriter; 038import java.io.IOException; 039import java.io.InputStream; 040import java.net.URI; 041 042import javax.xml.parsers.DocumentBuilder; 043import javax.xml.parsers.DocumentBuilderFactory; 044 045import org.w3c.dom.Document; 046import org.w3c.dom.Element; 047import org.w3c.dom.NodeList; 048 049import ptolemy.actor.TypedAtomicActor; 050import ptolemy.actor.TypedIOPort; 051import ptolemy.data.ArrayToken; 052import ptolemy.data.BooleanToken; 053import ptolemy.data.DoubleToken; 054import ptolemy.data.StringToken; 055import ptolemy.data.expr.FileParameter; 056import ptolemy.data.expr.Parameter; 057import ptolemy.data.type.ArrayType; 058import ptolemy.data.type.BaseType; 059import ptolemy.gui.GraphicalMessageHandler; 060import ptolemy.kernel.CompositeEntity; 061import ptolemy.kernel.attributes.URIAttribute; 062import ptolemy.kernel.util.IllegalActionException; 063import ptolemy.kernel.util.NameDuplicationException; 064 065////////////////////////////////////////////////////////////////////////// 066//// AddPointToSVG 067/** 068 * Concatanate several SVG files to a single file. Typical Usage is to create 069 * output displays. The actor accepts the input SVG paths as an array of 070 * strings, and x,y shift values for positioning each of the files and returns 071 * the path of the concatenated output file. 072 * 073 * @author Efrat Jaeger 074 * @version $Id: SVGConcat.java 24234 2010-05-06 05:21:26Z welker $ 075 * @since Ptolemy II 3.0.2 076 */ 077public class SVGConcat extends TypedAtomicActor { 078 079 /** 080 * Construct an actor with the given container and name. 081 * 082 * @param container 083 * The container. 084 * @param name 085 * The name of this actor. 086 * @exception IllegalActionException 087 * If the actor cannot be contained by the proposed 088 * container. 089 * @exception NameDuplicationException 090 * If the container already has an actor with this name. 091 */ 092 public SVGConcat(CompositeEntity container, String name) 093 throws IllegalActionException, NameDuplicationException { 094 super(container, name); 095 096 svgFiles = new TypedIOPort(this, "svgFiles", true, false); 097 svgFiles.setTypeEquals(new ArrayType(BaseType.STRING)); 098 099 outputPathPort = new TypedIOPort(this, "outputPath", true, false); 100 outputPathPort.setTypeEquals(BaseType.STRING); 101 102 output = new TypedIOPort(this, "output", false, true); 103 output.setTypeEquals(BaseType.STRING); 104 105 outputPathParam = new FileParameter(this, "outputPathParam"); 106 outputPathParam.setDisplayName("SVG output file"); 107 108 shiftX = new Parameter(this, "shiftX"); 109 shiftY = new Parameter(this, "shiftY"); 110 shiftX.setExpression("0.0"); 111 shiftY.setExpression("0.0"); 112 113 // trigger = new TypedIOPort(this, "trigger", true, false); 114 // trigger.setMultiport(true); 115 116 confirmOverwrite = new Parameter(this, "confirmOverwrite"); 117 confirmOverwrite.setTypeEquals(BaseType.BOOLEAN); 118 confirmOverwrite.setToken(BooleanToken.TRUE); 119 120 _attachText("_iconDescription", "<svg>\n" 121 + "<rect x=\"-25\" y=\"-20\" " + "width=\"50\" height=\"40\" " 122 + "style=\"fill:white\"/>\n" 123 + "<polygon points=\"-15,-10 -12,-10 -8,-14 -1,-14 3,-10" 124 + " 15,-10 15,10, -15,10\" " + "style=\"fill:red\"/>\n" 125 + "</svg>\n"); 126 } 127 128 // ///////////////////////////////////////////////////////////////// 129 // // ports and parameters //// 130 131 /** 132 * Array of SVG input files. 133 */ 134 public TypedIOPort svgFiles; 135 136 /** 137 * An SVG output file URL port. 138 */ 139 public TypedIOPort outputPathPort; 140 141 /** 142 * Output SVG output file URL. 143 */ 144 public TypedIOPort output; 145 146 /** 147 * Trigger actor execution. 148 */ 149 // public TypedIOPort trigger; 150 /** 151 * An SVG output file name or URL. This is a string with any form accepted 152 * by FileParameter. 153 * 154 * @see FileParameter 155 */ 156 public FileParameter outputPathParam; 157 158 /** 159 * Relative shift over the X coordinate during concatenation. 160 */ 161 public Parameter shiftX; 162 163 /** 164 * Relative shift over the Y coordinate during concatenation. 165 */ 166 public Parameter shiftY; 167 168 /** 169 * If <i>false</i>, then overwrite the specified file if it exists without 170 * asking. If <i>true</i> (the default), then if the file exists, ask for 171 * confirmation before overwriting. 172 */ 173 public Parameter confirmOverwrite; 174 175 /** 176 * Translate the point into the svg ratio parameters and add it to the file. 177 * Broadcasts the URL to the svg output file. 178 * 179 * @exception IllegalActionException 180 * If there's no director. 181 */ 182 public void fire() throws IllegalActionException { 183 184 // trigger the actor. 185 /* 186 * for (int i = 0; i < trigger.getWidth(); i++) { if 187 * (trigger.hasToken(i)) { trigger.get(i); } } 188 */ 189 190 _svgFile = null; 191 _svgOutFile = null; 192 String fileName = ""; 193 // get SVG output file. 194 if (outputPathPort.getWidth() > 0) { 195 fileName = ((StringToken) outputPathPort.get(0)).stringValue(); 196 int lineEndInd = fileName.indexOf("\n"); 197 if (lineEndInd != -1) { // The string contains a CR. 198 fileName = fileName.substring(0, lineEndInd); 199 } 200 outputPathParam.setExpression(fileName); 201 } 202 _svgOutFile = outputPathParam.asFile(); 203 204 boolean confirmOverwriteValue = ((BooleanToken) confirmOverwrite 205 .getToken()).booleanValue(); 206 // Don't ask for confirmation in append mode, since there 207 // will be no loss of data. 208 if (_svgOutFile.exists() && confirmOverwriteValue) { 209 // Query for overwrite. 210 // FIXME: This should be called in the event thread! 211 // There is a chance of deadlock since it is not. 212 if (!GraphicalMessageHandler.yesNoQuestion("OK to overwrite " 213 + _svgOutFile + "?")) { 214 throw new IllegalActionException(this, 215 "Please select another file name."); 216 } 217 } 218 double _shiftX = 0.0, _shiftY = 0.0; 219 try { 220 _shiftX = ((DoubleToken) shiftX.getToken()).doubleValue(); 221 } catch (Exception ex) { 222 _shiftX = 0.0; 223 } 224 try { 225 _shiftY = ((DoubleToken) shiftY.getToken()).doubleValue(); 226 } catch (Exception ex) { 227 _shiftY = 0.0; 228 } 229 230 // FIXME: Currently assuming all files have the same header 231 // (except for width and height) and the same scripts 232 // and that tags begin with a new line. 233 String line; 234 _maxWidth = 0; 235 _maxHeight = 0; 236 StringBuffer _sbheader = new StringBuffer(); 237 StringBuffer _sbmicro = new StringBuffer(); 238 StringBuffer _sbcontent = new StringBuffer(); 239 // Get SVG input array. 240 ArrayToken arrayToken = (ArrayToken) svgFiles.get(0); 241 for (int i = 0; i < arrayToken.length(); i++) { 242 String svgFilePath = ((StringToken) arrayToken.getElement(i)) 243 .stringValue(); 244 _svgFile = new File(svgFilePath); 245 if (!_svgFile.isAbsolute()) { 246 // Try to resolve the base directory. 247 URI modelURI = URIAttribute.getModelURI(this); 248 if (modelURI != null) { 249 URI newURI = modelURI.resolve(svgFilePath); 250 _svgFile = new File(newURI); 251 } 252 } 253 254 try { 255 BufferedReader br = new BufferedReader(new FileReader(_svgFile)); 256 if (i == 0) {// get header. 257 while ((line = br.readLine()) != null) { 258 int ind = line.toLowerCase().indexOf("width"); 259 if (ind != -1) { 260 break; 261 } else { 262 _sbheader.append(line + "\n"); 263 } 264 } 265 // _widthHeightLine = line; 266 267 while ((line = br.readLine()) != null) { 268 int ind = line.toLowerCase().indexOf("</script>"); 269 if (ind != -1) { 270 _sbmicro.append(line + "\n"); 271 break; 272 } else { 273 _sbmicro.append(line + "\n"); 274 } 275 } 276 } else { // skip to after script. 277 while ((line = br.readLine()) != null) { 278 int ind = line.toLowerCase().indexOf("</script>"); 279 if (ind != -1) { 280 break; 281 } 282 } 283 } 284 285 String transform = "<g transform=\"translate("; 286 if (_shiftX > 0) 287 transform += _maxWidth; 288 else 289 transform += "0.0"; 290 transform += ","; 291 if (_shiftY > 0) 292 transform += _maxHeight; 293 else 294 transform += "0.0"; 295 transform += ")\">"; 296 _sbcontent.append(transform); 297 298 while ((line = br.readLine()) != null) { 299 int ind = line.toLowerCase().indexOf("</svg>"); 300 if (ind != -1) { 301 // System.out.println("Inside extra line"); 302 _sbcontent.append(line.substring(0, ind) + "\n"); 303 _sbcontent.append("</g>\n"); 304 break; 305 } else 306 _sbcontent.append(line + "\n"); 307 } 308 br.close(); 309 310 _getWidthHeight(); 311 if (_shiftX > 0) 312 _maxWidth = _maxWidth + _tempWidth + _shiftX; 313 else 314 _maxWidth = (_maxWidth > _tempWidth) ? _maxWidth 315 : _tempWidth; 316 if (_shiftY > 0) 317 _maxHeight = _maxHeight + _tempHeight + _shiftY; 318 else 319 _maxHeight = (_maxHeight > _tempHeight) ? _maxHeight 320 : _tempHeight; 321 322 } catch (Exception ex) { 323 throw new IllegalActionException(this, ex, "Error parsing " 324 + _svgFile + " in actor " + this.getName()); 325 } 326 } 327 _sbcontent.append("</svg>"); 328 _sbheader.append(" width='" + _maxWidth); 329 _sbheader.append("' height='" + _maxHeight + "'>\n"); 330 331 try { 332 BufferedWriter out = new BufferedWriter(new FileWriter(_svgOutFile)); 333 out.write(_sbheader.toString()); 334 out.write(_sbmicro.toString()); 335 out.write(_sbcontent.toString()); 336 out.flush(); 337 out.close(); 338 } catch (IOException ex) { 339 throw new IllegalActionException(this, ex, "Error writing to file " 340 + _svgOutFile); 341 } 342 343 // broadcast SVG output file URL. 344 output.broadcast(new StringToken(_svgOutFile.getAbsolutePath())); 345 } 346 347 // ///////////////////////////////////////////////////////////////// 348 // // private methods //// 349 350 private void _getWidthHeight() throws IllegalActionException { 351 try { 352 DocumentBuilderFactory factory = DocumentBuilderFactory 353 .newInstance(); 354 factory.setValidating(false); 355 DocumentBuilder builder = factory.newDocumentBuilder(); 356 InputStream is = new FileInputStream(_svgFile); 357 Document doc = builder.parse(is); 358 359 NodeList nodes = doc.getElementsByTagName("svg"); 360 for (int i = 0; i < nodes.getLength(); i++) { 361 String attr = ((Element) nodes.item(i)).getAttribute("width"); 362 _tempWidth = Double.parseDouble(attr); 363 attr = ((Element) nodes.item(i)).getAttribute("height"); 364 _tempHeight = Double.parseDouble(attr); 365 } 366 } catch (Exception ex) { 367 throw new IllegalActionException(this, "Error parsing SVG file " 368 + _svgFile); 369 } 370 } 371 372 // ///////////////////////////////////////////////////////////////// 373 // // private members //// 374 375 /** Previous value of fileOrURL parameter. */ 376 private File _svgFile, _svgOutFile; 377 private double param, xVal = 0.0, yVal = 0.0; 378 private double _tempWidth, _tempHeight, _maxWidth, _maxHeight; 379 private String _widthHeightLine; 380}