/**
* 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.*;
import com.kitfox.svg.xml.StyleAttribute;
import com.t_oster.visicut.misc.Helper;
import com.t_oster.visicut.model.graphicelements.GraphicObject;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author Thomas Oster <thomas.oster@rwth-aachen.de>
*/
public abstract class SVGObject implements GraphicObject
{
public enum Attribute
{
Stroke_Width,
Stroke_Color,
Fill_Color,
Color,
Type,
Group,
Id,
}
/**
* Returns a List of SVGElements representing the Path
* from the current Decorated Element to the root Node
* @return
*/
public List<SVGElement> getPathToRoot()
{
List<SVGElement> result = new LinkedList<SVGElement>();
SVGElement current = this.getDecoratee();
while (current != null)
{
result.add(current);
current = current.getParent();
}
return result;
}
public abstract RenderableElement getDecoratee();
/**
* This
* applies all transformations in the Path of the SVGShape
* and returns the Transformed Shape, which can be displayed
* or printed on the position it appears in the original image.
* @param selectedSVGElement
* @return
*/
public AffineTransform getAbsoluteTransformation() throws SVGException
{
if (this.getDecoratee() != null)
{
AffineTransform tr = new AffineTransform();
for (Object o : this.getPathToRoot())
{
if (o instanceof Group)
{
StyleAttribute sty = new StyleAttribute("transform");
if (((SVGElement) o).getPres(sty))
{
String value = sty.getStringValue();
for (String v : value.split("\\)"))
{
v = v.trim();
if (!"".equals(v))
{
AffineTransform trans = SVGElement.parseSingleTransform(v+")");
trans.concatenate(tr);
tr = trans;
}
}
}
}
}
return tr;
}
return null;
}
@Override
public void render(Graphics2D g)
{
AffineTransform bak = g.getTransform();
try
{
AffineTransform trans = g.getTransform();
trans.concatenate(this.getAbsoluteTransformation());
g.setTransform(trans);
this.getDecoratee().render(g);
}
catch (SVGException ex)
{
Logger.getLogger(SVGShape.class.getName()).log(Level.SEVERE, null, ex);
}
g.setTransform(bak);
}
private Map<String, List<Object>> attributeValues = new LinkedHashMap<String, List<Object>>();
public List<Object> getAttributeValues(String name)
{
if (attributeValues.containsKey(name))
{
return attributeValues.get(name);
}
List<Object> result = new LinkedList<Object>();
try
{
switch (Attribute.valueOf(name.replace(" ", "_")))
{
case Id:
{
if (this.getDecoratee().getId() != null)
{
result.add(this.getDecoratee().getId());
}
break;
}
case Group:
{
for (SVGElement e : this.getPath(this.getDecoratee()))
{
//Exclude SVGRoot, because is not a real group
if (e instanceof Group && !(e instanceof SVGRoot))
{
String id = ((Group) e).getId();
//Use Inkscape-Labels instead of IDs for Groups (e.g. on Layers)
StyleAttribute s = ((Group) e).getPresAbsolute("inkscape:label");
if (s != null && s.getStringValue() != null)
{
id = s.getStringValue();
}
if (id != null)
{
result.add(id);
}
}
}
break;
}
}
}
catch (IllegalArgumentException e)
{
//Attribute not supported in SVG => Ignore
}
attributeValues.put(name, result);
return result;
}
private List<String> attributes = null;
public List<String> getAttributes()
{
if (attributes != null)
{
return attributes;
}
attributes = new LinkedList<String>();
for (Attribute a : Attribute.values())
{
if (this.getAttributeValues(a.toString()).size() > 0)
{
attributes.add(a.toString().replace("_", " "));
}
}
return attributes;
}
/**
* Returns the path from root to the given Element
* @param e
* @return
*/
protected List<SVGElement> getPath(SVGElement e)
{
List<SVGElement> result = new LinkedList<SVGElement>();
while (e != null)
{
result.add(0, e);
e = e.getParent();
}
return result;
}
public Rectangle2D getBoundingBox()
{
try
{
Rectangle2D result = getDecoratee().getBoundingBox();
if (result == null)
{
//Return boundingbox of parent
SVGElement p = this.getDecoratee();
do
{
p = p.getParent();
if (p != null && p instanceof RenderableElement)
{
result = ((RenderableElement) p).getBoundingBox();
}
}
while (result == null && p != null);
}
AffineTransform tr = this.getAbsoluteTransformation();
return Helper.transform(result, tr);
}
catch (SVGException ex)
{
Logger.getLogger(SVGObject.class.getName()).log(Level.SEVERE, null, ex);
return new Rectangle(0, 0, 0, 0);
}
}
}