/**
* Copyright 2007-2010 非也
* All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License v3 as published by the Free Software
* Foundation.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this library; if not, see http://www.gnu.org/licenses/lgpl.html.
*
*/
package org.fireflow.designer.swing.mxgraphext.canvas;
import java.util.Map;
import java.util.StringTokenizer;
import org.fireflow.clientwidget.servlet.Constants;
import org.fireflow.designer.swing.mxgraphext.shape.CommentShape;
import org.fireflow.designer.swing.proxy.Wrapper;
import org.fireflow.pdl.fpdl.io.FPDLNames;
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;
import com.mxgraph.canvas.mxSvgCanvas;
import com.mxgraph.model.mxCell;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxRectangle;
import com.mxgraph.util.mxUtils;
import com.mxgraph.view.mxCellState;
/**
*
* @author 非也 nychen2000@163.com Fire Workflow 官方网站:www.firesoa.com 或者
* www.fireflow.org
*
*/
public class SvgCanvasEx extends mxSvgCanvas {
private String clientWidgetServletPath = null;
private String contextPath = null;
private Node viewportNode = null;
public SvgCanvasEx(Document document){
super(document);
}
/**
* @param document
*/
public SvgCanvasEx(Document document,String clientWidgetServletPath,String contextPath) {
super(document);
this.clientWidgetServletPath = clientWidgetServletPath;
this.contextPath = contextPath;
if (this.contextPath==null)this.contextPath = "";
if (this.clientWidgetServletPath==null) this.clientWidgetServletPath="";
if (this.contextPath.endsWith("/") && this.clientWidgetServletPath.startsWith("/")){
this.contextPath = this.contextPath.substring(0,contextPath.length()-1);
}
Element root = document.getDocumentElement();
NodeList nodeList = root.getElementsByTagName("g");
int l = nodeList.getLength();
if (nodeList!=null && nodeList.getLength()>0){
for (int i=0;i<l;i++){
Node gNode = nodeList.item(i);
NamedNodeMap map = gNode.getAttributes();
if (map!=null){
Node idNode = map.getNamedItem("id");
if(idNode!=null && "viewport".equals(idNode.getNodeValue())){
viewportNode = gNode;
break;
}
}
}
}
}
public void appendSvgElement(Element node)
{
if (viewportNode!=null){
viewportNode.appendChild(node);
}
else if (document != null)
{
Element root = document.getDocumentElement();
root.appendChild(node);
}
}
protected Element createImageElement(double x, double y, double w,
double h, String src, boolean aspect, boolean flipH, boolean flipV,
boolean embedded)
{
Element elem = null;
if (embedded)
{
elem = document.createElement("use");
Element img = getEmbeddedImageElement(src);
elem.setAttributeNS(mxConstants.NS_XLINK, "xlink:href",
"#" + img.getAttribute("id"));
}
else
{
elem = document.createElement("image");
String imgUri = contextPath+this.clientWidgetServletPath+src;
elem.setAttributeNS(mxConstants.NS_XLINK, "xlink:href", imgUri);
}
elem.setAttribute("x", String.valueOf(x));
elem.setAttribute("y", String.valueOf(y));
elem.setAttribute("width", String.valueOf(w));
elem.setAttribute("height", String.valueOf(h));
// FIXME: SVG element must be used for reference to image with
// aspect but for images with no aspect this does not work.
if (aspect)
{
elem.setAttribute("preserveAspectRatio", "xMidYMid");
}
else
{
elem.setAttribute("preserveAspectRatio", "none");
}
double sx = 1;
double sy = 1;
double dx = 0;
double dy = 0;
if (flipH)
{
sx *= -1;
dx = -w - 2 * x;
}
if (flipV)
{
sy *= -1;
dy = -h - 2 * y;
}
String transform = "";
if (sx != 1 || sy != 1)
{
transform += "scale(" + sx + " " + sy + ") ";
}
if (dx != 0 || dy != 0)
{
transform += "translate(" + dx + " " + dy + ") ";
}
if (transform.length() > 0)
{
elem.setAttribute("transform", transform);
}
return elem;
}
/*
* (non-Javadoc)
*
* @see com.mxgraph.canvas.mxSvgCanvas#drawShape(int, int, int, int,
* java.util.Map)
*/
@Override
public Element drawShape(int x, int y, int w, int h,
Map<String, Object> style) {
String fillColor = mxUtils.getString(style,
mxConstants.STYLE_FILLCOLOR, "none");
String gradientColor = mxUtils.getString(style,
mxConstants.STYLE_GRADIENTCOLOR, "none");
String strokeColor = mxUtils.getString(style,
mxConstants.STYLE_STROKECOLOR, "none");
float strokeWidth = (float) (mxUtils.getFloat(style,
mxConstants.STYLE_STROKEWIDTH, 1) * scale);
float opacity = mxUtils.getFloat(style, mxConstants.STYLE_OPACITY, 100);
// Draws the shape
String shape = mxUtils.getString(style, mxConstants.STYLE_SHAPE, "");
Element elem = null;
Element background = null;
if (shape.equals(CommentShape.SHAPE_FIREFLOW_COMMENT)) {
elem = document.createElement("g");
background = document.createElement("rect");
background.setAttribute("x", String.valueOf(x + strokeWidth));// strokeWidth
background.setAttribute("y", String.valueOf(y + strokeWidth));
background.setAttribute("width",
String.valueOf(w - strokeWidth * 2));
background.setAttribute("height",
String.valueOf(h - strokeWidth * 2));
background.setAttribute("stroke", "#ffffff");
background.setAttribute("stroke-opacity", "0");
background.setAttribute("stroke-width", "1");
elem.appendChild(background);
Element foreground = document.createElement("path");
String d = "M " + (x + w / 3) + " " + y + " L " + x + " " + y
+ " L " + x + " " + (y + h) + " L " + (x + w / 3) + " "
+ (y + h);
foreground.setAttribute("d", d);
foreground.setAttribute("fill", "none");
foreground.setAttribute("stroke", strokeColor);
foreground
.setAttribute("stroke-width", String.valueOf(strokeWidth));
elem.appendChild(foreground);
double rotation = mxUtils.getDouble(style,
mxConstants.STYLE_ROTATION);
int cx = x + w / 2;
int cy = y + h / 2;
Element bg = background;
if (!bg.getNodeName().equalsIgnoreCase("use")
&& !bg.getNodeName().equalsIgnoreCase("image")) {
if (!fillColor.equalsIgnoreCase("none")
&& !gradientColor.equalsIgnoreCase("none")) {
String direction = mxUtils.getString(style,
mxConstants.STYLE_GRADIENT_DIRECTION);
Element gradient = getGradientElement(fillColor,
gradientColor, direction);
if (gradient != null) {
bg.setAttribute("fill",
"url(#" + gradient.getAttribute("id") + ")");
}
} else {
bg.setAttribute("fill", fillColor);
}
bg.setAttribute("stroke", strokeColor);
bg.setAttribute("stroke-width", String.valueOf(strokeWidth));
// Adds the shadow element
Element shadowElement = null;
if (mxUtils.isTrue(style, mxConstants.STYLE_SHADOW, false)
&& !fillColor.equals("none")) {
shadowElement = (Element) bg.cloneNode(true);
shadowElement.setAttribute("transform",
mxConstants.SVG_SHADOWTRANSFORM);
shadowElement.setAttribute("fill",
mxConstants.W3C_SHADOWCOLOR);
shadowElement.setAttribute("stroke",
mxConstants.W3C_SHADOWCOLOR);
shadowElement.setAttribute("stroke-width",
String.valueOf(strokeWidth));
if (rotation != 0) {
shadowElement.setAttribute("transform", "rotate("
+ rotation + "," + cx + "," + cy + ") "
+ mxConstants.SVG_SHADOWTRANSFORM);
}
if (opacity != 100) {
String value = String.valueOf(opacity / 100);
shadowElement.setAttribute("fill-opacity", value);
shadowElement.setAttribute("stroke-opacity", value);
}
appendSvgElement(shadowElement);
}
}
if (rotation != 0) {
elem.setAttribute("transform", elem.getAttribute("transform")
+ " rotate(" + rotation + "," + cx + "," + cy + ")");
}
if (opacity != 100) {
String value = String.valueOf(opacity / 100);
elem.setAttribute("fill-opacity", value);
elem.setAttribute("stroke-opacity", value);
}
if (mxUtils.isTrue(style, mxConstants.STYLE_DASHED)) {
String pattern = mxUtils.getString(style,
mxConstants.STYLE_DASH_PATTERN, "3, 3");
elem.setAttribute("stroke-dasharray", pattern);
}
appendSvgElement(elem);
return elem;
} else {
return super.drawShape(x, y, w, h, style);
}
}
/* (non-Javadoc)
* @see com.mxgraph.canvas.mxSvgCanvas#drawLabel(java.lang.String, com.mxgraph.view.mxCellState, boolean)
*/
@Override
public Object drawLabel(String label, mxCellState state, boolean html) {
Map<String,Object> style = state.getStyle();
String shape = mxUtils.getString(style, mxConstants.STYLE_SHAPE, "");
if (shape.equals(CommentShape.SHAPE_FIREFLOW_COMMENT)) {
mxRectangle bounds = state.getPerimeterBounds();
//state.getLabelBounds();
mxCell cell = (mxCell)state.getCell();
Wrapper wrapper = (Wrapper)cell.getValue();
String originalLabel = (String)wrapper.getAttribute(FPDLNames.DESCRIPTION);
if (drawLabels && bounds != null)
{
int x = (int) bounds.getX() + translate.x;
int y = (int) bounds.getY() + translate.y;
int w = (int) bounds.getWidth();
int h = (int) bounds.getHeight();
state.getCell();
return drawCommentText(originalLabel,label,x,y,w,h,style);
}
return null;
}else{
return super.drawLabel(label, state, html);
}
}
public Object drawCommentText(String originalText,String text, int x, int y, int w, int h,
Map<String, Object> style)
{
Element result = null;
String fontColor = mxUtils.getString(style,
mxConstants.STYLE_FONTCOLOR, "black");
String fontFamily = "FangSong_GB2312";//mxUtils.getString(style,
//mxConstants.STYLE_FONTFAMILY, mxConstants.DEFAULT_FONTFAMILIES);
int fontSize = (int) (mxUtils.getInt(style, mxConstants.STYLE_FONTSIZE,
mxConstants.DEFAULT_FONTSIZE) * scale);
int fontStyle = mxUtils.getInt(style, mxConstants.STYLE_FONTSTYLE);
String weight = ((fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD) ? "bold"
: "normal";
String uline = ((fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE) ? "underline"
: "none";
if (text != null && text.length() > 0)
{
// Applies the opacity
float opacity = mxUtils.getFloat(style,
mxConstants.STYLE_TEXT_OPACITY, 100);
String transform = null;
if (!mxUtils.isTrue(style, mxConstants.STYLE_HORIZONTAL, true))
{
double cx = x + w / 2;
double cy = y + h / 2;
transform = "rotate(270 " + cx + " " + cy + ")";
}
//0、构造switch元素
Element switchElm = document.createElement("switch");
result = switchElm;
//1、首先svg1.2的textarea元素
Element g = document.createElement("g");
g.setAttribute("requiredFeatures", "http://www.w3.org/Graphics/SVG/feature/1.2/#TextFlow");
switchElm.appendChild(g);
Element textArea = document.createElement("textArea");
textArea.setAttribute("width", Integer.toString(w));
textArea.setAttribute("height", Integer.toString(h));
textArea.setAttribute("font-weight", weight);
textArea.setAttribute("font-decoration", uline);
if ((fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC)
{
textArea.setAttribute("font-style", "italic");
}
textArea.setAttribute("font-size", String.valueOf(fontSize));
textArea.setAttribute("font-family", fontFamily);
textArea.setAttribute("fill", fontColor);
if (opacity != 100)
{
String value = String.valueOf(opacity / 100);
textArea.setAttribute("fill-opacity", value);
textArea.setAttribute("stroke-opacity", value);
}
g.appendChild(textArea);
textArea.appendChild(document.createTextNode(originalText));
//2、创建foreignObject
Element foreignObject = document.createElement("foreignObject");
foreignObject.setAttribute("x", Integer.toString(x+CommentShape.COMMENT_FIGURE_INSETS));
foreignObject.setAttribute("y", Integer.toString(y+CommentShape.COMMENT_FIGURE_INSETS));
foreignObject.setAttribute("width", Integer.toString(w-CommentShape.COMMENT_FIGURE_INSETS*2));
foreignObject.setAttribute("height", Integer.toString(h-CommentShape.COMMENT_FIGURE_INSETS*2));
foreignObject.setAttribute("requiredFeatures", "http://www.w3.org/TR/SVG11/feature#Extensibility");
switchElm.appendChild(foreignObject);
StringBuffer styleBuf = new StringBuffer();
styleBuf.append("font-weight:").append(weight).append(";")
.append("font-size:").append(String.valueOf(fontSize)).append("px;")
.append("font-family:").append(fontFamily).append(";")
.append("color:").append(fontColor).append(";");
if ((fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC)
{
styleBuf.append("font-style:").append( "italic").append(";");
}
StringTokenizer tokenizer = new StringTokenizer(originalText,"\n");
while (tokenizer.hasMoreTokens()){
String tmpStr = tokenizer.nextToken();
Element pElem = document.createElement("p");
pElem.setAttribute("style", styleBuf.toString());
pElem.setAttribute("xmlns","http://www.w3.org/1999/xhtml");
pElem.appendChild(document.createTextNode(tmpStr));
foreignObject.appendChild(pElem);
}
//4、构造text节点
Element textElm = null;
textElm = document.createElement("text");
textElm.setAttribute("font-weight", weight);
textElm.setAttribute("font-decoration", uline);
if ((fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC)
{
textElm.setAttribute("font-style", "italic");
}
textElm.setAttribute("font-size", String.valueOf(fontSize));
textElm.setAttribute("font-family", fontFamily);
textElm.setAttribute("fill", fontColor);
if (opacity != 100)
{
String value = String.valueOf(opacity / 100);
textElm.setAttribute("fill-opacity", value);
textElm.setAttribute("stroke-opacity", value);
}
String[] lines = text.split("\n");
y += fontSize
+ (h - lines.length * (fontSize + mxConstants.LINESPACING))
/ 2 - 2;
String align = mxUtils.getString(style, mxConstants.STYLE_ALIGN,
mxConstants.ALIGN_CENTER);
String anchor = "start";
if (align.equals(mxConstants.ALIGN_RIGHT))
{
anchor = "end";
x += w - mxConstants.LABEL_INSET * scale;
}
else if (align.equals(mxConstants.ALIGN_CENTER))
{
anchor = "middle";
x += w / 2;
}
else
{
x += mxConstants.LABEL_INSET * scale;
}
textElm.setAttribute("text-anchor", anchor);
for (int i = 0; i < lines.length; i++)
{
Element tspan = document.createElement("tspan");
tspan.setAttribute("x", String.valueOf(x));
tspan.setAttribute("y", String.valueOf(y));
tspan.appendChild(document.createTextNode(lines[i]));
textElm.appendChild(tspan);
y += fontSize + mxConstants.LINESPACING;
}
if (transform != null)
{
textElm.setAttribute("transform", transform);
}
switchElm.appendChild(textElm);
appendSvgElement(switchElm);//为什么要append在根节点呢?
}
return result;
}
}