/*
* Copyright 2015 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jbpm.process.svg;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.List;
import java.util.Map;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.batik.dom.svg.SAXSVGDocumentFactory;
import org.apache.batik.dom.svg.SVGOMTSpanElement;
import org.apache.batik.util.XMLResourceDescriptor;
import org.jbpm.process.svg.model.NodeSummary;
import org.jbpm.process.svg.model.SVGSummary;
import org.jbpm.process.svg.model.SetBackgroundColorTransformation;
import org.jbpm.process.svg.model.SetBorderColorTransformation;
import org.jbpm.process.svg.model.SetSubProcessLinkTransformation;
import org.jbpm.process.svg.model.Transformation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class SVGImageProcessor {
private Document svgDocument;
private SVGSummary summary = new SVGSummary();
private boolean mapById = true;
public SVGImageProcessor(InputStream svg) {
this(svg, true);
}
public SVGImageProcessor(InputStream svg, boolean mapById) {
this.mapById = mapById;
try {
String parser = XMLResourceDescriptor.getXMLParserClassName();
SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory(parser);
factory.setValidating(false);
svgDocument = factory.createDocument("http://jbpm.org", svg);
processNodes(svgDocument.getChildNodes());
} catch (IOException e) {
throw new RuntimeException("Could not parse svg", e);
}
}
public static String transform(InputStream svg, List<String> completed, List<String> active) {
return transform(svg, completed, active, null);
}
public static String transform(InputStream svg, List<String> completed, List<String> active, Map<String, String> subProcessLinks) {
SVGImageProcessor processor = new SVGImageProcessor(svg);
for (String nodeId : completed) {
if (!active.contains(nodeId)) {
processor.defaultCompletedTransformation(nodeId);
}
}
for (String nodeId : active) {
processor.defaultActiveTransformation(nodeId);
}
if (subProcessLinks != null) {
for (Map.Entry<String, String> subProcessLink : subProcessLinks.entrySet()) {
processor.defaultSubProcessLinkTransformation(subProcessLink.getKey(), subProcessLink.getValue());
}
}
return processor.getSVG();
}
public static String transformByName(InputStream svg, List<String> completed, List<String> active) {
SVGImageProcessor processor = new SVGImageProcessor(svg, false);
for (String nodeId : completed) {
if (!active.contains(nodeId)) {
processor.defaultCompletedTransformation(nodeId);
}
}
for (String nodeId : active) {
processor.defaultActiveTransformation(nodeId);
}
return processor.getSVG();
}
public void transform(Transformation t) {
t.transform(summary);
}
public void defaultCompletedTransformation(String nodeId) {
transform(new SetBackgroundColorTransformation(nodeId, "#C0C0C0"));
}
public void defaultActiveTransformation(String nodeId) {
transform(new SetBorderColorTransformation(nodeId, "#FF0000"));
}
public void defaultSubProcessLinkTransformation(String nodeId, String link) {
transform(new SetSubProcessLinkTransformation(nodeId, link));
}
public String getSVG() {
try {
DOMSource domSource = new DOMSource(svgDocument.getFirstChild());
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.transform(domSource, result);
return writer.toString();
} catch (TransformerException e) {
throw new RuntimeException("Could not transform svg", e);
}
}
private void processNodes(NodeList nodes) {
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
NamedNodeMap attributes = node.getAttributes();
if (attributes != null) {
Node svgIdNode = attributes.getNamedItem("id");
if (svgIdNode != null) {
String svgId = svgIdNode.getNodeValue();
if (mapById) {
Node nodeIdNode = attributes.getNamedItem("bpmn2nodeid");
if (nodeIdNode != null) {
String nodeId = nodeIdNode.getNodeValue();
Element border = null;
Element background = null;
Element subProcessLink = null;
if (nodeId != null) {
background = svgDocument.getElementById(svgId + "fill_el");
border = svgDocument.getElementById(svgId + "bg_frame");
Element borderSubProcess = svgDocument.getElementById(svgId + "frame");
subProcessLink = svgDocument.getElementById(svgId + "pimg");
summary.addNode(new NodeSummary(nodeId, border, background, borderSubProcess, subProcessLink));
}
}
} else {
// map by name
if (svgId.endsWith("text_name")) {
svgId = svgId.substring(0, svgId.length() - 9);
StringBuilder taskLabel = new StringBuilder();
for (int j = 0; j < node.getChildNodes().getLength(); j++) {
if (node.getChildNodes().item(j) instanceof SVGOMTSpanElement) {
SVGOMTSpanElement spanElement = (SVGOMTSpanElement)node.getChildNodes().item(j);
taskLabel.append(spanElement.getFirstChild().getNodeValue());
}
}
String name = taskLabel.toString();
// filtering out nodes with no name
if (!name.trim().isEmpty()) {
Element background = svgDocument.getElementById(svgId + "fill_el");
Element border = svgDocument.getElementById(svgId + "bg_frame");
Element borderSubProcess = svgDocument.getElementById(svgId + "frame");
Element subProcessLink = svgDocument.getElementById(svgId + "pimg");
summary.addNode(new NodeSummary(name, border, background, borderSubProcess, subProcessLink));
}
}
}
}
}
processNodes(node.getChildNodes());
}
}
}