/** * This file is part of VisiCut. * Copyright (C) 2011 - 2013 Thomas Oster <thomas.oster@rwth-aachen.de> * RWTH Aachen University - 52062 Aachen, Germany * * VisiCut is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * VisiCut is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with VisiCut. If not, see <http://www.gnu.org/licenses/>. **/ package com.t_oster.visicut.model.graphicelements.svgsupport; import com.kitfox.svg.Defs; import com.kitfox.svg.Gradient; import com.kitfox.svg.Group; import com.kitfox.svg.ImageSVG; import com.kitfox.svg.PatternSVG; import com.kitfox.svg.SVGConst; import com.kitfox.svg.SVGElement; import com.kitfox.svg.SVGException; import com.kitfox.svg.SVGRoot; import com.kitfox.svg.SVGUniverse; import com.kitfox.svg.ShapeElement; import com.kitfox.svg.xml.NumberWithUnits; import com.kitfox.svg.xml.StyleAttribute; import com.t_oster.liblasercut.platform.Util; import com.t_oster.visicut.misc.ExtensionFilter; import com.t_oster.visicut.misc.Helper; import com.t_oster.visicut.model.graphicelements.GraphicObject; import com.t_oster.visicut.model.graphicelements.GraphicSet; import com.t_oster.visicut.model.graphicelements.ImportException; import com.t_oster.visicut.model.graphicelements.Importer; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URI; import java.util.List; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; import javax.swing.filechooser.FileFilter; /** * * @author Thomas Oster <thomas.oster@rwth-aachen.de> */ public class SVGImporter implements Importer { private SVGUniverse u = new SVGUniverse(); private SVGRoot root; private void importNode(SVGElement e, List<GraphicObject> result, double svgResolution, List<String> warnings) { if (e instanceof PatternSVG || e instanceof Gradient || e instanceof Defs) {//Ignore Patterns,Gradients and Children return; } StyleAttribute display = e.getStyleAbsolute("display"); if (display != null && "none".equals(display.getStringValue())) { return; } StyleAttribute visibility = e.getStyleAbsolute("visibility"); if (visibility != null && "hidden".equals(visibility.getStringValue())) { return; } if (e instanceof ShapeElement && !(e instanceof Group)) { if (((ShapeElement) e).getShape() != null) { result.add(new SVGShape((ShapeElement) e, svgResolution)); } else { //warnings.add("Ignoring SVGShape: " + e + " because can't get Shape"); } } else { if (e instanceof ImageSVG) { result.add(new SVGImage((ImageSVG) e)); } } for (int i = 0; i < e.getNumChildren(); i++) { importNode(e.getChild(i), result, svgResolution, warnings); } } public GraphicSet importFile(InputStream in, String name, double svgResolution, final List<String> warnings) throws Exception { Handler svgImportLoggerHandler = new Handler() { @Override public void publish(LogRecord lr) { warnings.add(lr.getMessage()); } @Override public void flush(){} @Override public void close() throws SecurityException{} }; try { u.clear(); Logger.getLogger(SVGConst.SVG_LOGGER).addHandler(svgImportLoggerHandler); URI svg = u.loadSVG(in, Helper.toPathName(name)); root = u.getDiagram(svg).getRoot(); GraphicSet result = new GraphicSet(); result.setBasicTransform(determineTransformation(root, svgResolution)); importNode(root, result, svgResolution, warnings); Logger.getLogger(SVGConst.SVG_LOGGER).removeHandler(svgImportLoggerHandler); return result; } catch (Exception e) { throw new ImportException(e); } } /** * Calculates the size in mm (with repolution dpi) * of a NumberWithUnits element (SVG-Element) * @param n * @param dpi */ public static double numberWithUnitsToMm(NumberWithUnits n, double dpi) { switch (n.getUnits()) { case NumberWithUnits.UT_MM: return n.getValue(); case NumberWithUnits.UT_CM: return 10.0 * n.getValue(); case NumberWithUnits.UT_PT: case NumberWithUnits.UT_PX: return Util.px2mm(n.getValue(), dpi); case NumberWithUnits.UT_IN: return Util.inch2mm(n.getValue()); case NumberWithUnits.UT_UNITLESS: return n.getValue(); default: return n.getValue(); } } /** * Since different programs have a different idea of the reference resolution * in SVG, this method tries to determine it. * @param root * @param f * @return */ private double determineResolution(File f, List<String> warnings) { BufferedReader in = null;; double result = 90; boolean usesFlowRoot = false; try { in = new BufferedReader(new InputStreamReader(new FileInputStream(f))); try { String line = in.readLine(); while (line != null) { if (line.startsWith("<!-- Generator: Adobe Illustrator")) { result = 72; } if (line.contains("</flowRoot>")) { usesFlowRoot = true; } line = in.readLine(); } in.close(); } catch (IOException ex) { Logger.getLogger(SVGImporter.class.getName()).log(Level.SEVERE, null, ex); } } catch (FileNotFoundException ex) { Logger.getLogger(SVGImporter.class.getName()).log(Level.SEVERE, null, ex); } finally { try { if (in != null) { in.close(); } } catch (IOException ex) { Logger.getLogger(SVGImporter.class.getName()).log(Level.SEVERE, null, ex); } } if (usesFlowRoot) { warnings.add(java.util.ResourceBundle.getBundle("com/t_oster/visicut/model/graphicelements/svgsupport/resources/SVGImporter").getString("FLOWROOT_WARNING")); } return result; } /* * Tries to determine the Coordinate resolution in DPI. * SVG default is 90, but AI generates 72?? * */ private AffineTransform determineTransformation(SVGRoot root, double svgResolution) { try { StyleAttribute sty = new StyleAttribute(); double x=0; double y=0; double width=0; double height=0; if (root.getPres(sty.setName("x"))) { x = numberWithUnitsToMm(sty.getNumberWithUnits(), svgResolution); } if (root.getPres(sty.setName("y"))) { y = numberWithUnitsToMm(sty.getNumberWithUnits(), svgResolution); } if (root.getPres(sty.setName("width"))) { width = numberWithUnitsToMm(sty.getNumberWithUnits(), svgResolution); } if (root.getPres(sty.setName("height"))) { height = numberWithUnitsToMm(sty.getNumberWithUnits(), svgResolution); } if (width != 0 && height != 0 && root.getPres(sty.setName("viewBox"))) { float[] coords = sty.getFloatList(); Rectangle2D coordinateBox = new Rectangle2D.Double(x,y,width,height); Rectangle2D viewBox = new Rectangle2D.Float(coords[0], coords[1], coords[2], coords[3]); return Helper.getTransform(viewBox, coordinateBox); } } catch (SVGException ex) { Logger.getLogger(SVGImporter.class.getName()).log(Level.SEVERE, null, ex); } double px2mm = Util.inch2mm(1/svgResolution); return AffineTransform.getScaleInstance(px2mm, px2mm); } @Override public GraphicSet importFile(File inputFile, List<String> warnings) throws ImportException { try { double svgResolution = determineResolution(inputFile, warnings); GraphicSet result = this.importFile(new FileInputStream(inputFile), inputFile.getName(), svgResolution, warnings); return result; } catch (Exception ex) { throw new ImportException(ex); } } public FileFilter getFileFilter() { return new ExtensionFilter(".svg", "Scalable Vector Graphic (*.svg)"); } }