diff --git a/pom.xml b/pom.xml index e849d0e..2835f4e 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ imagej.public http://maven.imagej.net/content/groups/public - + @@ -48,6 +48,7 @@ SVG_ROI + C:/Fiji @@ -55,6 +56,11 @@ net.imagej ij + + sc.fiji + fiji-lib + 2.1.1 + batik batik-all diff --git a/src/main/java/SVG_ROI.java b/src/main/java/SVG_ROI.java index d6aabac..d91d16a 100644 --- a/src/main/java/SVG_ROI.java +++ b/src/main/java/SVG_ROI.java @@ -1,133 +1,236 @@ -import org.apache.batik.*; -import org.apache.batik.bridge.BridgeContext; -import org.apache.batik.bridge.DocumentLoader; -import org.apache.batik.bridge.GVTBuilder; -import org.apache.batik.bridge.UserAgent; -import org.apache.batik.bridge.UserAgentAdapter; +import java.awt.Color; +import java.awt.geom.PathIterator; +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.net.URI; +import java.util.ArrayList; + import org.apache.batik.dom.svg.SAXSVGDocumentFactory; -import org.apache.batik.dom.svg.SVGOMSVGElement; import org.apache.batik.util.XMLResourceDescriptor; import org.w3c.dom.Document; -import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; import org.w3c.dom.NodeList; -import ij.IJ; - -import java.net.URI; -import java.io.Console; -import java.io.File; -import java.io.IOException; +import fiji.util.gui.GenericDialogPlus; +import ij.ImageJ; +import ij.gui.Roi; +import ij.gui.ShapeRoi; +import ij.plugin.PlugIn; +import ij.plugin.frame.RoiManager; /** * Responsible for converting all SVG path elements into MetaPost curves. */ -public class SVG_ROI { - private static final String PATH_ELEMENT_NAME = "path"; - - private Document svgDocument; - - /** - * Creates an SVG Document given a URI. - * - * @param uri Path to the file. - * @throws Exception Something went wrong parsing the SVG file. - */ - public SVG_ROI( String uri ) throws IOException { - setSVGDocument( createSVGROIs( uri ) ); - } - - /** - * Finds all the path nodes and converts them to MetaPost code. - */ - public void run() { - NodeList pathNodes = getPathElements(); - int pathNodeCount = pathNodes.getLength(); - - for( int iPathNode = 0; iPathNode < pathNodeCount; iPathNode++ ) { - System.out.println(pathNodes.item(iPathNode).toString()); - } - } - - /** - * Returns a list of elements in the SVG document with names that - * match PATH_ELEMENT_NAME. - * - * @return The list of "path" elements in the SVG document. - */ - private NodeList getPathElements() { - return getSVGDocumentRoot().getElementsByTagName( PATH_ELEMENT_NAME ); - } - - /** - * Returns an SVGOMSVGElement that is the document's root element. - * - * @return The SVG document typecast into an SVGOMSVGElement. - */ - private Element getSVGDocumentRoot() { - return getSVGDocument().getDocumentElement(); - } - - /** - * This will set the document to parse. This method also initializes - * the SVG DOM enhancements, which are necessary to perform SVG and CSS - * manipulations. The initialization is also required to extract information - * from the SVG path elements. - * - * @param document The document that contains SVG content. - */ - public void setSVGDocument( Document document ) { - initSVGDOM( document ); - this.svgDocument = document; - } - - /** - * Returns the SVG document parsed upon instantiating this class. - * - * @return A valid, parsed, non-null SVG document instance. - */ - public Document getSVGDocument() { - return this.svgDocument; - } - - /** - * Enhance the SVG DOM for the given document to provide CSS- and SVG-specific - * DOM interfaces. - * - * @param document The document to enhance. - * @link http://wiki.apache.org/xmlgraphics-batik/BootSvgAndCssDom - */ - private void initSVGDOM( Document document ) { - UserAgent userAgent = new UserAgentAdapter(); - DocumentLoader loader = new DocumentLoader( userAgent ); - BridgeContext bridgeContext = new BridgeContext( userAgent, loader ); - bridgeContext.setDynamicState( BridgeContext.DYNAMIC ); - - // Enable CSS- and SVG-specific enhancements. - (new GVTBuilder()).build( bridgeContext, document ); - } - - /** - * Use the SAXSVGDocumentFactory to parse the given URI into a DOM. - * - * @param uri The path to the SVG file to read. - * @return A Document instance that represents the SVG file. - * @throws Exception The file could not be read. - */ - private Document createSVGROIs( String uri ) throws IOException { - String parser = XMLResourceDescriptor.getXMLParserClassName(); - SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory( parser ); - return factory.createDocument( uri ); - } - - /** - * Reads a file and parses the path elements. - * - * @param args args[0] - Filename to parse. - * @throws IOException Error reading the SVG file. - */ - public static void main( String args[] ) throws IOException { - URI uri = new File( "D:\\People\\Bianca\\svg\\Annotation2014_141_0000.svg" ).toURI(); - SVG_ROI svgroi = new SVG_ROI( uri.toString() ); - svgroi.run(); - } +public class SVG_ROI implements PlugIn{ + private static final String PATH_ELEMENT_NAME = "path"; + + @Override + public void run(String arg0) { + RoiManager rm = RoiManager.getInstance(); + if (rm == null) { + rm = new RoiManager(); + } + + GenericDialogPlus gd = new GenericDialogPlus("SVGs To ROI Conversion"); + gd.addDirectoryField("SVG Folder", ""); + + gd.showDialog(); + + if (gd.wasCanceled()) + return; + + //Navigate folder + File folder = new File(gd.getNextString()); + + File[] filelist = folder.listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.toLowerCase().endsWith(".svg"); + } + }); + + File save_folder = new File(folder.getAbsolutePath()+File.separator+"ROI Sets"); + save_folder.mkdirs(); + for (File file : filelist) { + rm.reset(); + convertSVGToRois(file); + // Save the ROIs to a subfolder + rm.runCommand("Save", save_folder.getAbsolutePath()+File.separator+file.getName().substring(0, file.getName().length()-4)+".zip"); + + } + + } + public static void convertSVGToRois(File file) { + RoiManager rm = RoiManager.getInstance2(); + if (rm == null) { + rm = new RoiManager(); + } + + URI uri = file.toURI(); + Document svg = null; + //Prepare SVG file + try { + svg = createDocument( uri.toString() ); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + // Iterate through the paths + NodeList pathNodes = svg.getDocumentElement().getElementsByTagName( PATH_ELEMENT_NAME ); + int pathNodeCount = pathNodes.getLength(); + for( int iPathNode = 0; iPathNode < pathNodeCount; iPathNode++ ) { + rm.addRoi(convertToShapeRoi(pathNodes.item(iPathNode).getAttributes())); + } + } + + public static ArrayList convertSVGFileToRois(File file) { + RoiManager rm = RoiManager.getInstance2(); + if (rm == null) { + rm = new RoiManager(); + } + + URI uri = file.toURI(); + Document svg = null; + //Prepare SVG file + try { + svg = createDocument( uri.toString() ); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + ArrayList rois = new ArrayList(); + + // Iterate through the paths + NodeList pathNodes = svg.getDocumentElement().getElementsByTagName( PATH_ELEMENT_NAME ); + int pathNodeCount = pathNodes.getLength(); + for( int iPathNode = 0; iPathNode < pathNodeCount; iPathNode++ ) { + rois.add(convertToShapeRoi(pathNodes.item(iPathNode).getAttributes())); + } + return rois; + } + + + private static Roi convertToShapeRoi(NamedNodeMap pathAttributes) { + //Get the paths + String paths = pathAttributes.getNamedItem("d").getTextContent(); + + // Remove the Z key if there + int is_closed = 0; + if (paths.endsWith("Z")) { + paths = paths.substring(0, paths.length()-1); + is_closed = 1; + } + + // Get the fill or Stroke, at least + String stroke = pathAttributes.getNamedItem("stroke").getTextContent(); + + // Work on a shapeRoi, as this allows for multiple ROIs to make a shape... + // ShapeRois can be built similarily to paths in SVG + + paths = paths.trim(); + String[] commands = paths.split("(?=L)|(?=M)|(?=C)"); + + ArrayList instructions = new ArrayList(); + + for (String command : commands ) { + char c = command.charAt(0); + command = command.trim(); + + command = command.substring(1); + + switch (c) { + case 'M': // This also means that we need to start a new path... + instructions.add((float)PathIterator.SEG_MOVETO); + // Add the two coordinates + instructions.addAll(parseCoordinateAsArray(command," ")); + + break; + + case 'L': + instructions.add((float)PathIterator.SEG_LINETO); + instructions.addAll(parseCoordinateAsArray(command,",")); + break; + case 'C': + + instructions.addAll(parseCurveAsArray(command)); + break; + + default: + System.out.println("WHAT IS: "+c +": "+command+"?"); + break; + } + } + + float[] floatArray = new float[instructions.size()+is_closed]; + int i = 0; + + for (Float f : instructions) { + floatArray[i++] = (f != null ? f : Float.NaN); // Or whatever default you want. + } + if( is_closed == 1) + floatArray[i] = PathIterator.SEG_CLOSE; + + ShapeRoi the_roi = new ShapeRoi(floatArray); + the_roi.setStrokeColor(Color.decode(stroke)); + return the_roi; + } + + private static ArrayList parseCurveAsArray(String command) { + // Split at spaces first + ArrayList curve_points = new ArrayList(); + + String[] curve = command.split(" "); + // should be multiples of 3 + if (curve.length%3 == 0) { + + for (int i=0; i parseCoordinateAsArray(String cs, String delimiter) { + String[] xy = cs.split(delimiter); + ArrayList aPoint = new ArrayList(); + aPoint.add(Float.parseFloat(xy[0])); + aPoint.add(Float.parseFloat(xy[1])); + return aPoint; + } + + /** + * Use the SAXSVGDocumentFactory to parse the given URI into a DOM. + * + * @param uri The path to the SVG file to read. + * @return A Document instance that represents the SVG file. + * @throws Exception The file could not be read. + */ + private static Document createDocument( String uri ) throws IOException { + String parser = XMLResourceDescriptor.getXMLParserClassName(); + SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory( parser ); + return factory.createDocument( uri ); + } + +/** + * Main method for debugging. + * @param args unused + */ +public static void main(String[] args) { + // set the plugins.dir property to make the plugin appear in the Plugins menu + Class clazz = SVG_ROI.class; + String url = clazz.getResource("/" + clazz.getName().replace('.', '/') + ".class").toString(); + String pluginsDir = url.substring(5, url.length() - clazz.getName().length() - 6); + System.setProperty("plugins.dir", pluginsDir); + + // start ImageJ + new ImageJ(); +} } \ No newline at end of file diff --git a/src/main/resources/plugins.config b/src/main/resources/plugins.config index 3435b67..a61fafe 100644 --- a/src/main/resources/plugins.config +++ b/src/main/resources/plugins.config @@ -9,4 +9,4 @@ # If something like ("") is appended to the class name, the setup() method # will get that as arg parameter; otherwise arg is simply the empty string. -Plugins>BIOP, "DEMO Plugin", Demo_Plugin +Plugins>BIOP, "SVG to ROI", SVG_ROI