Package org.gephi.io.exporter.preview

Source Code of org.gephi.io.exporter.preview.SVGExporter

/*
Copyright 2008-2010 Gephi
Authors : Jeremy Subtil <jeremy.subtil@gephi.org>
Website : http://www.gephi.org

This file is part of Gephi.

Gephi is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

Gephi 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 Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
*/
package org.gephi.io.exporter.preview;

import java.io.Writer;
import java.util.HashMap;
import java.util.Locale;
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 org.apache.batik.dom.svg.SVGDOMImplementation;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.svg2svg.SVGTranscoder;
import org.gephi.io.exporter.preview.util.LengthUnit;
import org.gephi.io.exporter.preview.util.SupportSize;
import org.gephi.io.exporter.spi.CharacterExporter;
import org.gephi.io.exporter.spi.VectorExporter;
import org.gephi.preview.api.BidirectionalEdge;
import org.gephi.preview.api.CubicBezierCurve;
import org.gephi.preview.api.DirectedEdge;
import org.gephi.preview.api.Edge;
import org.gephi.preview.api.EdgeArrow;
import org.gephi.preview.api.EdgeLabel;
import org.gephi.preview.api.EdgeMiniLabel;
import org.gephi.preview.api.Graph;
import org.gephi.preview.api.GraphRenderer;
import org.gephi.preview.api.GraphSheet;
import org.gephi.preview.api.Node;
import org.gephi.preview.api.NodeLabel;
import org.gephi.preview.api.NodeLabelBorder;
import org.gephi.preview.api.Point;
import org.gephi.preview.api.PreviewController;
import org.gephi.preview.api.SelfLoop;
import org.gephi.preview.api.UndirectedEdge;
import org.gephi.preview.api.UnidirectionalEdge;
import org.gephi.utils.longtask.spi.LongTask;
import org.gephi.utils.progress.Progress;
import org.gephi.utils.progress.ProgressTicket;
import org.gephi.project.api.Workspace;
import org.openide.util.Lookup;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;
import org.w3c.dom.Text;
import org.w3c.dom.svg.SVGLocatable;
import org.w3c.dom.svg.SVGRect;

/**
* Class exporting the preview graph as an SVG image.
*
* @author Jérémy Subtil <jeremy.subtil@gephi.org>
*/
public class SVGExporter implements GraphRenderer, CharacterExporter, VectorExporter, LongTask {

    //Architecture
    private Document doc;
    private ProgressTicket progress;
    private boolean cancel = false;
    private Workspace workspace;
    private Writer writer;
    //Settings
    private final static float MARGIN = 25f;
    private final String namespaceURI = SVGDOMImplementation.SVG_NAMESPACE_URI;
    private boolean scaleStrokes = false;
    //Helper
    private final HashMap<NodeLabel, SVGLocatable> nodeLabelMap = new HashMap<NodeLabel, SVGLocatable>();
    private Element svgRoot;
    private Element nodeGroupElem;
    private Element edgeGroupElem;
    private Element labelGroupElem;
    private Element labelBorderGroupElem;
    private float scaleRatio = 1f;

    public boolean execute() {
        PreviewController controller = Lookup.getDefault().lookup(PreviewController.class);
        GraphSheet graphSheet = controller.getGraphSheet();
        try {
            exportData(graphSheet);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return !cancel;
    }

    /**
     * Does export the preview graph as an SVG image.
     *
     * @param file         the output SVG file
     * @param supportSize  the support size of the exported image
     * @throws Exception
     */
    private void exportData(GraphSheet graphSheet) throws Exception {
        SupportSize supportSize = new SupportSize(595, 841, LengthUnit.PIXELS);
        Progress.start(progress);
        Graph graph = graphSheet.getGraph();

        // calculates progress units count
        int max = 0;
        if (graph.showNodes()) {
            max += graph.countNodes();
        }
        if (graph.showEdges()) {
            max += graph.countUnidirectionalEdges() + graph.countBidirectionalEdges();
            if (graph.showSelfLoops()) {
                max += graph.countSelfLoops();
            }
        }
        Progress.switchToDeterminate(progress, max);

        // export tasks
        buildDOM(graphSheet, supportSize);

        // creates SVG-to-SVG transcoder
        SVGTranscoder t = new SVGTranscoder();
        t.addTranscodingHint(SVGTranscoder.KEY_XML_DECLARATION, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>");

        // sets transcoder input and output
        TranscoderInput input = new TranscoderInput(doc);

        // performs transcoding
        try {
            TranscoderOutput output = new TranscoderOutput(writer);
            t.transcode(input, output);
        } finally {
            writer.close();
        }

        Progress.finish(progress);
    }

    public void renderGraph(Graph graph) {
        if (graph.showEdges()) {
            renderGraphEdges(graph);
        }

        if (graph.showNodes()) {
            renderGraphNodes(graph);
        }

        renderGraphLabels(graph);

        renderGraphLabelBorders(graph);
    }

    public void renderGraphEdges(Graph graph) {
        edgeGroupElem = createGroupElem("edges");
        svgRoot.appendChild(edgeGroupElem);

        renderGraphUnidirectionalEdges(graph);
        renderGraphBidirectionalEdges(graph);
        renderGraphUndirectedEdges(graph);

        if (graph.showSelfLoops()) {
            renderGraphSelfLoops(graph);
        }
    }

    public void renderGraphSelfLoops(Graph graph) {
        for (SelfLoop sl : graph.getSelfLoops()) {
            renderSelfLoop(sl);
        }
    }

    public void renderGraphUnidirectionalEdges(Graph graph) {
        for (UnidirectionalEdge edge : graph.getUnidirectionalEdges()) {
            renderDirectedEdge(edge);
        }
    }

    public void renderGraphBidirectionalEdges(Graph graph) {
        for (BidirectionalEdge edge : graph.getBidirectionalEdges()) {
            renderDirectedEdge(edge);
        }
    }

    public void renderGraphUndirectedEdges(Graph graph) {
        for (UndirectedEdge e : graph.getUndirectedEdges()) {
            renderEdge(e);
        }
    }

    public void renderGraphNodes(Graph graph) {
        nodeGroupElem = createGroupElem("nodes");
        svgRoot.appendChild(nodeGroupElem);

        for (Node n : graph.getNodes()) {
            renderNode(n);
        }
    }

    public void renderGraphLabels(Graph graph) {
        labelGroupElem = createGroupElem("labels");
        svgRoot.appendChild(labelGroupElem);

        for (UnidirectionalEdge e : graph.getUnidirectionalEdges()) {
            if (!e.isCurved()) {
                if (e.showLabel() && e.hasLabel() && e.getLabel().getFont() != null) {
                    renderEdgeLabel(e.getLabel());
                }

                if (e.showMiniLabels()) {
                    renderEdgeMiniLabels(e);
                }
            }
        }

        for (BidirectionalEdge e : graph.getBidirectionalEdges()) {
            if (!e.isCurved()) {
                if (e.showLabel() && e.hasLabel() && e.getLabel().getFont() != null) {
                    renderEdgeLabel(e.getLabel());
                }

                if (e.showMiniLabels()) {
                    renderEdgeMiniLabels(e);
                }
            }
        }

        for (UndirectedEdge e : graph.getUndirectedEdges()) {
            if (e.showLabel() && !e.isCurved() && e.hasLabel() && e.getLabel().getFont() != null) {
                renderEdgeLabel(e.getLabel());
            }
        }

        for (Node n : graph.getNodes()) {
            if (n.showLabel() && n.hasLabel() && n.getLabel().getFont() != null) {
                renderNodeLabel(n.getLabel());
            }
        }
    }

    public void renderGraphLabelBorders(Graph graph) {
        labelBorderGroupElem = createGroupElem("label borders");
        svgRoot.insertBefore(labelBorderGroupElem, labelGroupElem);

        for (Node n : graph.getNodes()) {
            if (n.showLabel() && n.hasLabel() && n.showLabelBorders() && n.getLabel().getFont() != null) {
                renderNodeLabelBorder(n.getLabelBorder());
            }
        }
    }

    public void renderNode(Node node) {
        Element nodeElem = createElement("circle");
        nodeElem.setAttribute("cx", node.getPosition().getX().toString());
        nodeElem.setAttribute("cy", node.getPosition().getY().toString());
        nodeElem.setAttribute("r", node.getRadius().toString());
        nodeElem.setAttribute("fill", node.getColor().toHexString());
        nodeElem.setAttribute("stroke", node.getBorderColor().toHexString());
        nodeElem.setAttribute("stroke-width", new Float(node.getBorderWidth() * scaleRatio).toString());
        nodeGroupElem.appendChild(nodeElem);

        Progress.progress(progress);
    }

    public void renderNodeLabel(NodeLabel label) {
        Text labelText = createTextNode(label.getValue());

        Element labelElem = createElement("text");
        labelElem.setAttribute("x", label.getPosition().getX().toString());
        labelElem.setAttribute("y", label.getPosition().getY().toString());
        labelElem.setAttribute("style", "text-anchor: middle");
        labelElem.setAttribute("fill", label.getColor().toHexString());
        labelElem.setAttribute("font-family", label.getFont().getFamily());
        labelElem.setAttribute("font-size", Integer.toString(label.getFont().getSize()));
        labelElem.appendChild(labelText);
        labelGroupElem.appendChild(labelElem);

        // need to save the label element in order to eventually draw its border
        nodeLabelMap.put(label, (SVGLocatable) labelElem);
    }

    public void renderNodeLabelBorder(NodeLabelBorder border) {
        // retrieve label's bounding box
        SVGRect rect = nodeLabelMap.get(border.getLabel()).getBBox();

        Element borderElem = createElement("rect");
        borderElem.setAttribute("x", Float.toString(rect.getX()));
        borderElem.setAttribute("y", Float.toString(rect.getY()));
        borderElem.setAttribute("width", Float.toString(rect.getWidth()));
        borderElem.setAttribute("height", Float.toString(rect.getHeight()));
        borderElem.setAttribute("fill", border.getColor().toHexString());
        labelBorderGroupElem.appendChild(borderElem);
    }

    public void renderSelfLoop(SelfLoop selfLoop) {
        CubicBezierCurve curve = selfLoop.getCurve();

        Element selfLoopElem = createElement("path");
        selfLoopElem.setAttribute("d", String.format(Locale.ENGLISH, "M %f,%f C %f,%f %f,%f %f,%f",
                curve.getPt1().getX(), curve.getPt1().getY(),
                curve.getPt2().getX(), curve.getPt2().getY(),
                curve.getPt3().getX(), curve.getPt3().getY(),
                curve.getPt4().getX(), curve.getPt4().getY()));
        selfLoopElem.setAttribute("stroke", selfLoop.getColor().toHexString());
        selfLoopElem.setAttribute("stroke-width", Float.toString(selfLoop.getThickness() * selfLoop.getScale() * scaleRatio));
        selfLoopElem.setAttribute("fill", "none");
        edgeGroupElem.appendChild(selfLoopElem);
    }

    public void renderDirectedEdge(DirectedEdge edge) {
        renderEdge(edge);

        if (!edge.isCurved() && edge.showArrows()) {
            renderEdgeArrows(edge);
        }
    }

    public void renderEdge(Edge edge) {
        if (edge.isCurved()) {
            renderCurvedEdge(edge);
        } else {
            renderStraightEdge(edge);
        }

        Progress.progress(progress);
    }

    public void renderStraightEdge(Edge edge) {
        Point boundary1 = edge.getNode1().getPosition();
        Point boundary2 = edge.getNode2().getPosition();

        Element edgeElem = createElement("path");
        edgeElem.setAttribute("d", String.format(Locale.ENGLISH, "M %f,%f L %f,%f",
                boundary1.getX(), boundary1.getY(),
                boundary2.getX(), boundary2.getY()));
        edgeElem.setAttribute("stroke", edge.getColor().toHexString());
        edgeElem.setAttribute("stroke-width", Float.toString(edge.getThickness() * edge.getScale() * scaleRatio));
        edgeGroupElem.appendChild(edgeElem);
    }

    public void renderCurvedEdge(Edge edge) {
        for (CubicBezierCurve curve : edge.getCurves()) {
            Element curveElem = createElement("path");
            curveElem.setAttribute("d", String.format(Locale.ENGLISH, "M %f,%f C %f,%f %f,%f %f,%f",
                    curve.getPt1().getX(), curve.getPt1().getY(),
                    curve.getPt2().getX(), curve.getPt2().getY(),
                    curve.getPt3().getX(), curve.getPt3().getY(),
                    curve.getPt4().getX(), curve.getPt4().getY()));
            curveElem.setAttribute("stroke", edge.getColor().toHexString());
            curveElem.setAttribute("stroke-width", Float.toString(edge.getThickness() * edge.getScale() * scaleRatio));
            curveElem.setAttribute("fill", "none");
            edgeGroupElem.appendChild(curveElem);
        }
    }

    public void renderEdgeArrows(DirectedEdge edge) {
        for (EdgeArrow a : edge.getArrows()) {
            renderEdgeArrow(a);
        }
    }

    public void renderEdgeMiniLabels(DirectedEdge edge) {
        for (EdgeMiniLabel ml : edge.getMiniLabels()) {
            renderEdgeMiniLabel(ml);
        }
    }

    public void renderEdgeArrow(EdgeArrow arrow) {
        Element arrowElem = createElement("polyline");
        arrowElem.setAttribute("points", String.format(Locale.ENGLISH, "%f,%f %f,%f %f,%f",
                arrow.getPt1().getX(), arrow.getPt1().getY(),
                arrow.getPt2().getX(), arrow.getPt2().getY(),
                arrow.getPt3().getX(), arrow.getPt3().getY()));
        arrowElem.setAttribute("fill", arrow.getColor().toHexString());
        arrowElem.setAttribute("stroke", "none");
        edgeGroupElem.appendChild(arrowElem);
    }

    public void renderEdgeLabel(EdgeLabel label) {
        Text text = createTextNode(label.getValue());

        Element labelElem = createElement("text");
        labelElem.setAttribute("x", "0");
        labelElem.setAttribute("y", "0");
        labelElem.setAttribute("style", "text-anchor: middle");
        labelElem.setAttribute("fill", label.getColor().toHexString());
        labelElem.setAttribute("font-family", label.getFont().getFamily());
        labelElem.setAttribute("font-size", Integer.toString(label.getFont().getSize()));
        labelElem.setAttribute("transform", String.format(Locale.ENGLISH, "translate(%f,%f) rotate(%f)",
                label.getPosition().getX(), label.getPosition().getY(),
                Math.toDegrees(label.getAngle())));
        labelElem.appendChild(text);
        labelGroupElem.appendChild(labelElem);
    }

    public void renderEdgeMiniLabel(EdgeMiniLabel miniLabel) {
        Text text = createTextNode(miniLabel.getValue());

        Element miniLabelElem = createElement("text");
        miniLabelElem.setAttribute("x", "0");
        miniLabelElem.setAttribute("y", "0");
        miniLabelElem.setAttribute("style", miniLabel.getHAlign().toCSS());
        miniLabelElem.setAttribute("fill", miniLabel.getColor().toHexString());
        miniLabelElem.setAttribute("font-family", miniLabel.getFont().getFamily());
        miniLabelElem.setAttribute("font-size", Integer.toString(miniLabel.getFont().getSize()));
        miniLabelElem.setAttribute("transform", String.format(Locale.ENGLISH, "translate(%f,%f) rotate(%f)",
                miniLabel.getPosition().getX(), miniLabel.getPosition().getY(),
                Math.toDegrees(miniLabel.getAngle())));
        miniLabelElem.appendChild(text);
        labelGroupElem.appendChild(miniLabelElem);
    }

    /**
     * Builds the DOM from the preview graph.
     *
     * @param graphSheet   the preview graph sheet
     * @param supportSize  the support size of the exported image
     */
    protected Document buildDOM(GraphSheet graphSheet, SupportSize supportSize) {
        // creates SVG document
        DOMImplementation impl = SVGDOMImplementation.getDOMImplementation();
        DocumentType doctype = impl.createDocumentType(
                "-//W3C//DTD SVG 1.1//EN",
                "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd",
                "");
        doc = impl.createDocument(namespaceURI, "svg", doctype);

        // initializes CSS and SVG specific DOM interfaces
        UserAgent userAgent = new UserAgentAdapter();
        DocumentLoader loader = new DocumentLoader(userAgent);
        BridgeContext ctx = new BridgeContext(userAgent, loader);
        ctx.setDynamicState(BridgeContext.DYNAMIC);
        GVTBuilder builder = new GVTBuilder();
        builder.build(ctx, doc);

        // image margin
        graphSheet.setMargin(MARGIN);

        //Dimension
        int width = graphSheet.getWidth().intValue();
        int height = graphSheet.getHeight().intValue();
        if (width > height) {
            supportSize = new SupportSize(width * supportSize.getHeightInt() / height, supportSize.getHeightInt(), LengthUnit.PIXELS);
        } else if (height > width) {
            supportSize = new SupportSize(supportSize.getWidthInt(), height * supportSize.getWidthInt() / width, LengthUnit.PIXELS);
        }

        // root element
        svgRoot = doc.getDocumentElement();
        svgRoot.setAttributeNS(null, "width", supportSize.getWidth());
        svgRoot.setAttributeNS(null, "height", supportSize.getHeight());
        svgRoot.setAttributeNS(null, "version", "1.1");
        svgRoot.setAttributeNS(null, "viewBox", String.format(Locale.ENGLISH, "%d %d %d %d",
                graphSheet.getTopLeftPosition().getX().intValue(),
                graphSheet.getTopLeftPosition().getY().intValue(),
                width,
                height));

        //Scale & ratio
        if(scaleStrokes) {
            scaleRatio = supportSize.getWidthInt() / (float)width;
        }
       
        // draws the graph exporting it into the DOM
        renderGraph(graphSheet.getGraph());
        return doc;
    }

    /**
     * Creates a new element from the current document.
     *
     * @param qualifiedName  the qualified name of the element type to
     *   instantiate
     * @return               a new <code>Element</code> object
     */
    private Element createElement(String qualifiedName) {
        return doc.createElementNS(namespaceURI, qualifiedName);
    }

    /**
     * Creates a text node from the current document.
     *
     * @param data  the data for the node
     * @return      a new <code>Text</code> object
     */
    private Text createTextNode(String data) {
        return doc.createTextNode(data);
    }

    /**
     * Creates the group element corresponding to the given type.
     *
     * @param type  the name of the group element to create
     * @return      the created group element
     */
    private Element createGroupElem(String name) {
        Element group = createElement("g");
        group.setAttribute("id", name);

        return group;
    }

    public boolean cancel() {
        cancel = true;
        return true;
    }

    public void setProgressTicket(ProgressTicket progressTicket) {
        this.progress = progressTicket;
    }

    public void setWriter(Writer writer) {
        this.writer = writer;
    }

    public Workspace getWorkspace() {
        return workspace;
    }

    public void setWorkspace(Workspace workspace) {
        this.workspace = workspace;
    }

    public void setScaleStrokes(boolean scaleStrokes) {
        this.scaleStrokes = scaleStrokes;
    }

    public boolean isScaleStrokes() {
        return scaleStrokes;
    }
}
TOP

Related Classes of org.gephi.io.exporter.preview.SVGExporter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.