/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2013 by Alexander Heusel
*
* This file is part of svgfx.
*
* svgfx is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* as published by the Free Software Foundation.
*
* see: http://opensource.org/licenses/LGPL-3.0
*
* svgfx 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.
*
* This version of svgfx includes copyright notice and attribution requirements.
* According to the LGPL this information must be displayed even if you modify
* the source code of svgfx. Neither the copyright statement nor the attribution
* may be removed.
*
* Attribution Requirements:
*
* If you create derived work you must do two things regarding copyright notice
* and author attribution.
*
* First, the copyright notice must remain. It must be reproduced in any program
* that uses svgfx.
*
* Second, add an additional notice, stating that you modified svgfx. A suitable
* notice might read "svgfx source code modified by YourName 2012".
*
* Note, that these requirements are in full accordance with the LGPL v3
* (see 7. Additional Terms, b).
*
*/
package svg2fx;
import java.io.IOException;
import java.io.InputStream;
import javafx.scene.shape.Path;
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.SAXSVGDocumentFactory;
import org.apache.batik.gvt.GraphicsNode;
import org.apache.batik.util.XMLResourceDescriptor;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.css.ViewCSS;
import org.w3c.dom.svg.SVGCircleElement;
import org.w3c.dom.svg.SVGDefsElement;
import org.w3c.dom.svg.SVGDocument;
import org.w3c.dom.svg.SVGEllipseElement;
import org.w3c.dom.svg.SVGFilterElement;
import org.w3c.dom.svg.SVGGElement;
import org.w3c.dom.svg.SVGLineElement;
import org.w3c.dom.svg.SVGPathElement;
import org.w3c.dom.svg.SVGPathSeg;
import org.w3c.dom.svg.SVGPathSegArcAbs;
import org.w3c.dom.svg.SVGPathSegArcRel;
import org.w3c.dom.svg.SVGPathSegClosePath;
import org.w3c.dom.svg.SVGPathSegCurvetoCubicAbs;
import org.w3c.dom.svg.SVGPathSegCurvetoCubicRel;
import org.w3c.dom.svg.SVGPathSegCurvetoCubicSmoothAbs;
import org.w3c.dom.svg.SVGPathSegCurvetoCubicSmoothRel;
import org.w3c.dom.svg.SVGPathSegCurvetoQuadraticAbs;
import org.w3c.dom.svg.SVGPathSegCurvetoQuadraticRel;
import org.w3c.dom.svg.SVGPathSegCurvetoQuadraticSmoothAbs;
import org.w3c.dom.svg.SVGPathSegCurvetoQuadraticSmoothRel;
import org.w3c.dom.svg.SVGPathSegLinetoAbs;
import org.w3c.dom.svg.SVGPathSegLinetoHorizontalAbs;
import org.w3c.dom.svg.SVGPathSegLinetoHorizontalRel;
import org.w3c.dom.svg.SVGPathSegLinetoRel;
import org.w3c.dom.svg.SVGPathSegLinetoVerticalAbs;
import org.w3c.dom.svg.SVGPathSegLinetoVerticalRel;
import org.w3c.dom.svg.SVGPathSegList;
import org.w3c.dom.svg.SVGPathSegMovetoAbs;
import org.w3c.dom.svg.SVGPathSegMovetoRel;
import org.w3c.dom.svg.SVGPatternElement;
import org.w3c.dom.svg.SVGPolylineElement;
import org.w3c.dom.svg.SVGRectElement;
import org.w3c.dom.svg.SVGSVGElement;
import svg2fx.interfaces.DocumentVisitor;
import svg2fx.interfaces.StylePropertyVisitor;
/**
*
* @author Alexander Heusel
*/
public class Document
{
private SAXSVGDocumentFactory f;
private SVGDocument doc;
private UserAgent userAgent;
private DocumentLoader loader;
private BridgeContext ctx;
private GVTBuilder builder;
private GraphicsNode rootGN;
private SVGSVGElement svgRoot;
private ViewCSS vcss;
public Document(InputStream stream) throws IOException
{
f = new SAXSVGDocumentFactory(XMLResourceDescriptor.getXMLParserClassName());
doc = f.createSVGDocument(null, stream);
// Boot the CSS engine
userAgent = new UserAgentAdapter();
loader = new DocumentLoader(userAgent);
ctx = new BridgeContext(userAgent, loader);
ctx.setDynamicState(BridgeContext.DYNAMIC);
builder = new GVTBuilder();
rootGN = builder.build(ctx, doc);
svgRoot = doc.getRootElement();
vcss = (ViewCSS) doc.getDocumentElement();
}
public void visit(DocumentVisitor db)
{
visitDefs(db);
visitGroup(db, null);
}
private void visitDefs(DocumentVisitor db)
{
// Visit all definition of the svg-document
NodeList nl = svgRoot.getElementsByTagName("defs");
for(int i = 0; i < nl.getLength(); i++)
{
if(nl.item(i) instanceof SVGDefsElement)
{
SVGDefsElement svgDefs = (SVGDefsElement)nl.item(i);
NodeList dnl = svgDefs.getChildNodes();
for(int j = 0; j < dnl.getLength(); j++)
{
if(dnl.item(j) instanceof SVGPatternElement)
{
db.visitSVGPatternElement((SVGPatternElement)dnl.item(j));
}
else if(dnl.item(j) instanceof SVGFilterElement)
{
db.visitSVGFilterElement((SVGFilterElement)dnl.item(j));
}
}
}
}
}
private void visitGroup(DocumentVisitor db, SVGGElement group)
{
db.visitSVGGElement(group);
NodeList nl = group == null ? svgRoot.getChildNodes() : group.getChildNodes();
for(int i = 0; i < nl.getLength(); i++)
{
if(nl.item(i) instanceof SVGGElement)
{
visitGroup(db, (SVGGElement) nl.item(i));
visitStyleAttributes(db, ((SVGGElement) nl.item(i)).getAttributeNode("style"));
}
else if(nl.item(i) instanceof SVGPathElement)
{
visitPathSeg(db, (SVGPathElement) nl.item(i));
visitStyleAttributes(db, ((SVGPathElement) nl.item(i)).getAttributeNode("style"));
}
else if(nl.item(i) instanceof SVGCircleElement)
{
db.visitSVGCircleElement((SVGCircleElement) nl.item(i));
visitStyleAttributes(db, ((SVGCircleElement) nl.item(i)).getAttributeNode("style"));
}
else if(nl.item(i) instanceof SVGEllipseElement)
{
db.visitSVGEllipseElement((SVGEllipseElement) nl.item(i));
visitStyleAttributes(db, ((SVGEllipseElement) nl.item(i)).getAttributeNode("style"));
}
else if(nl.item(i) instanceof SVGRectElement)
{
db.visitSVGRectElement((SVGRectElement) nl.item(i));
visitStyleAttributes(db, ((SVGRectElement) nl.item(i)).getAttributeNode("style"));
}
else if(nl.item(i) instanceof SVGLineElement)
{
db.visitSVGLineElement((SVGLineElement) nl.item(i));
visitStyleAttributes(db, ((SVGLineElement) nl.item(i)).getAttributeNode("style"));
}
else if(nl.item(i) instanceof SVGPolylineElement)
{
db.visitSVGPolylineElement((SVGPolylineElement) nl.item(i));
visitStyleAttributes(db, ((SVGPolylineElement) nl.item(i)).getAttributeNode("style"));
}
}
db.visitSVGGElementClose(group);
}
private void visitPathSeg(DocumentVisitor db, SVGPathElement svgPath)
{
db.visitSVGPathElement(svgPath);
SVGPathSegList psl = svgPath.getPathSegList();
SVGPathSeg ps;
for(int i = 0; i < psl.getNumberOfItems(); i++)
{
ps = psl.getItem(i);
switch(ps.getPathSegType())
{
case SVGPathSeg.PATHSEG_MOVETO_ABS:
db.visitSVGPathSegMovetoAbs((SVGPathSegMovetoAbs)ps);
break;
case SVGPathSeg.PATHSEG_MOVETO_REL:
db.visitSVGPathSegMovetoRel((SVGPathSegMovetoRel)ps);
break;
case SVGPathSeg.PATHSEG_LINETO_ABS:
db.visitSVGPathSegLinetoAbs((SVGPathSegLinetoAbs)ps);
break;
case SVGPathSeg.PATHSEG_LINETO_REL:
db.visitSVGPathSegLinetoRel((SVGPathSegLinetoRel)ps);
break;
case SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS:
db.visitSVGPathSegLinetoHorizontalAbs((SVGPathSegLinetoHorizontalAbs)ps);
break;
case SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL:
db.visitSVGPathSegLinetoHorizontalRel((SVGPathSegLinetoHorizontalRel)ps);
break;
case SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS:
db.visitSVGPathSegLinetoVerticalAbs((SVGPathSegLinetoVerticalAbs)ps);
break;
case SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL:
db.visitSVGPathSegLinetoVerticalRel((SVGPathSegLinetoVerticalRel)ps);
break;
case SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS:
db.visitSVGPathSegCurvetoCubicAbs((SVGPathSegCurvetoCubicAbs)ps);
break;
case SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL:
db.visitSVGPathSegCurvetoCubicRel((SVGPathSegCurvetoCubicRel)ps);
break;
case SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
db.visitSVGPathSegCurvetoCubicSmoothAbs((SVGPathSegCurvetoCubicSmoothAbs)ps);
break;
case SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
db.visitSVGPathSegCurvetoCubicSmoothRel((SVGPathSegCurvetoCubicSmoothRel)ps);
break;
case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS:
db.visitSVGPathSegCurvetoQuadraticAbs((SVGPathSegCurvetoQuadraticAbs)ps);
break;
case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL:
db.visitSVGPathSegCurvetoQuadraticRel((SVGPathSegCurvetoQuadraticRel)ps);
break;
case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
db.visitSVGPathSegCurvetoQuadraticSmoothAbs((SVGPathSegCurvetoQuadraticSmoothAbs)ps);
break;
case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
db.visitSVGPathSegCurvetoQuadraticSmoothRel((SVGPathSegCurvetoQuadraticSmoothRel)ps);
break;
case SVGPathSeg.PATHSEG_ARC_ABS:
db.visitSVGPathSegArcAbs((SVGPathSegArcAbs)ps);
break;
case SVGPathSeg.PATHSEG_ARC_REL:
db.visitSVGPathSegArcRel((SVGPathSegArcRel)ps);
break;
case SVGPathSeg.PATHSEG_CLOSEPATH:
db.visitSVGPathSegClosePath((SVGPathSegClosePath)ps);
break;
default:
throw new java.lang.IllegalStateException("Unknown type of SVGPathSegm in SVGPathElement!");
}
}
}
private void visitStyleAttributes(StylePropertyVisitor db, Node el)
{
if(el != null)
{
String[] parts = el.getNodeValue().split(";");
String[][] properties = new String[parts.length][];
for(int i = 0; i < parts.length; i++)
{
properties[i] = parts[i].split(":");
// remove all whitespace and force lower case.
properties[i][0] = properties[i][0].trim().toLowerCase();
properties[i][1] = properties[i][1].trim().toLowerCase();
}
for(int i = 0; i < properties.length; i++)
{
switch(properties[i][0])
{
case "alignment-baseline":
db.visitAlignmentBaseline(properties[i][1]);
break;
case "baseline-shift":
db.visitClip(properties[i][1]);
break;
case "clip-path":
db.visitClipPath(properties[i][1]);
break;
case "clip-rule":
db.visitClipRule(properties[i][1]);
break;
case "clip":
db.visitClip(properties[i][1]);
break;
case "color-interpolation-filters":
db.visitColorInterpolationFilters(properties[i][1]);
break;
case "color-interpolation":
db.visitColorInterpolation(properties[i][1]);
break;
case "color-profile":
db.visitColorProfile(properties[i][1]);
break;
case "color-rendering":
db.visitColorRendering(properties[i][1]);
break;
case "color":
db.visitColor(properties[i][1]);
break;
case "cursor":
db.visitCursor(properties[i][1]);
break;
case "direction":
db.visitDirection(properties[i][1]);
break;
case "display":
db.visitDisplay(properties[i][1]);
break;
case "dominant-baseline":
db.visitDominantBaseline(properties[i][1]);
break;
case "enable-background":
db.visitEnableBackground(properties[i][1]);
break;
case "fill-opacity":
db.visitFillOpacity(properties[i][1]);
break;
case "fill-rule":
db.visitFillRule(properties[i][1]);
break;
case "fill":
db.visitFill(properties[i][1]);
break;
case "filter":
db.visitFilter(properties[i][1]);
break;
case "flood-color":
db.visitFloodColor(properties[i][1]);
break;
case "flood-opacity":
db.visitFloodOpacity(properties[i][1]);
break;
case "font-family":
db.visitFontFamily(properties[i][1]);
break;
case "font-size-adjust":
db.visitFontSizeAdjust(properties[i][1]);
break;
case "font-size":
db.visitFontSize(properties[i][1]);
break;
case "font-stretch":
db.visitFontStretch(properties[i][1]);
break;
case "font-style":
db.visitFontStyle(properties[i][1]);
break;
case "font-variant":
db.visitFontVariant(properties[i][1]);
break;
case "font-weight":
db.visitFontWeight(properties[i][1]);
break;
case "glyph-orientation-horizontal":
db.visitGlyphOrientationHorizontal(properties[i][1]);
break;
case "glyph-orientation-vertical":
db.visitGlyphOrientationVertical(properties[i][1]);
break;
case "image-rendering":
db.visitImageRendering(properties[i][1]);
break;
case "kerning":
db.visitKerning(properties[i][1]);
break;
case "letter-spacing":
db.visitLetterSpacing(properties[i][1]);
break;
case "lighting-color":
db.visitLightingColor(properties[i][1]);
break;
case "marker-end":
db.visitMarkerEnd(properties[i][1]);
break;
case "marker-mid":
db.visitMarkerMid(properties[i][1]);
break;
case "marker-start":
db.visitMarkerStart(properties[i][1]);
break;
case "mask":
db.visitMask(properties[i][1]);
break;
case "opacity":
db.visitOpacity(properties[i][1]);
break;
case "overflow":
db.visitOverflow(properties[i][1]);
break;
case "pointer-events":
db.visitPointerEvents(properties[i][1]);
break;
case "shape-rendering":
db.visitShapeRendering(properties[i][1]);
break;
case "stop-color":
db.visitStopColor(properties[i][1]);
break;
case "stop-opacity":
db.visitStopOpacity(properties[i][1]);
break;
case "stroke-dasharray":
db.visitStrokeDasharray(properties[i][1]);
break;
case "stroke-dashoffset":
db.visitStrokeDashoffset(properties[i][1]);
break;
case "stroke-linecap":
db.visitStrokeLinecap(properties[i][1]);
break;
case "stroke-linejoin":
db.visitStrokeLinejoin(properties[i][1]);
break;
case "stroke-miterlimit":
db.visitStrokeMiterlimit(properties[i][1]);
break;
case "stroke-opacity":
db.visitStrokeOpacity(properties[i][1]);
break;
case "stroke-width":
db.visitStrokeWidth(properties[i][1]);
break;
case "stroke":
db.visitStroke(properties[i][1]);
break;
case "text-anchor":
db.visitTextAnchor(properties[i][1]);
break;
case "text-decoration":
db.visitTextDecoration(properties[i][1]);
break;
case "text-rendering":
db.visitTextRendering(properties[i][1]);
break;
case "unicode-bidi":
db.visitUnicodeBidi(properties[i][1]);
break;
case "visibility":
db.visitVisibility(properties[i][1]);
break;
case "word-spacing":
db.visitWordSpacing(properties[i][1]);
break;
case "writing-mode":
db.visitWritingMode(properties[i][1]);
break;
default:
//System.out.format("Unsupportet style property: %s ignored.\n", properties[i][0]);
}
}
}
}
}