I am creating a small program that will make an SVG file based on options I select. The problem I am having is that JSVGCanvas (from Batik) will not seem to render gradients that are made (using the DOM api). The weird part is, if I export the file and open it in Inkscape or with the test class here, it (the gradient) renders! Here is all the info I think may be important:
Batik Version: 1.7
Gradient Build Code:
public Element parse(Document doc, String id) { //This is inside a gradient object.
Element defs = doc.createElementNS(Main.svgNS, "defs"); //Since multiple defs work in an SVG (or at least Inkscape), I add a defs tag per set of gradients.
defs.setAttribute("id", id); //Sets the id of the defs tag.
for (JsonElement je : json.getAsJsonArray("Linears")) { //I use GSON to store data, I am iterating through all objects in the Linears array.
if (je.isJsonObject()) { //Just a type check.
JsonObject jo = je.getAsJsonObject();
Element lin = doc.createElementNS(Main.svgNS, "linearGradient"); //Creates the linearGradient element.
for (Entry<String, JsonElement> entry : jo.entrySet()) { //For all elements other than Stops (it is an array) we want to set the attribute.
if (!entry.getKey().equals("Stops")) {
lin.setAttribute(entry.getKey(), entry.getValue().getAsJsonPrimitive().getAsString());
}
}
for (JsonElement stop : jo.getAsJsonArray("Stops")) { //For all objects in stops we want to add a stop.
Element el = doc.createElementNS(Main.svgNS, "stop"); //Creates a stop element.
for (Entry<String, JsonElement> entry : stop.getAsJsonObject().entrySet()) {
if (entry.getValue().isJsonObject()) {
el.setAttribute(entry.getKey(), parseObject(entry.getValue().getAsJsonObject())); //The parseObject method makes a string based on the JsonObject (this is used to get "variables" such as colors).
} else {
el.setAttribute(entry.getKey(), entry.getValue().getAsString());
}
}
lin.appendChild(el); //Add stop to the linear gradient.
}
defs.appendChild(lin); //Add the linear gradient to defs.
}
}
for (JsonElement je : json.getAsJsonArray("Radials")) { //Loops through all the radial gradients.
if (je.isJsonObject()) { //Type check.
JsonObject jo = je.getAsJsonObject();
Element rad = doc.createElementNS(Main.svgNS, "radialGradient"); //Creates the radialGradient element.
for (Entry<String, JsonElement> entry : jo.entrySet()) {
if (entry.getValue().isJsonPrimitive()) {
rad.setAttribute(entry.getKey(), entry.getValue().getAsJsonPrimitive().getAsString());
} else {
rad.setAttribute(entry.getKey(), parseObject(entry.getValue().getAsJsonObject()));
}
}
defs.appendChild(rad); //Adds the radial gradient to defs.
}
}
cache = defs;
return cache; //We return the defs to be added in the main build code.
}
Document Creation: Constructor:
impl = SVGDOMImplementation.getDOMImplementation();
Build Method:
doc = (SVGDocument) impl.createDocument(svgNS, "svg", null);
JSVGCanvas Creation:
preview = new JSVGCanvas();
preview.setEnableImageZoomInteractor(true);
preview.setEnablePanInteractor(true);
Here is what the gradients part of my template files looks like (JSON):
"Gradients": {
"Radials": [
{
"gradientTransform": "matrix(0.96774313,-1.5022501e-7,3.5488612e-7,1.483871,-201.69415,-217.84922)",
"inkscape:collect": "always",
"xmlns:xlink": "http://www.w3.org/1999/xlink",
"id": "radialGradient5607",
"gradientUnits": "userSpaceOnUse",
"xlink:show": "other",
"xlink:type": "simple",
"r": "15.5",
"cx":{
pre:"",
var:"X",
add:327,
post:""
},
"fx":{
pre:"",
var:"Y",
add:327,
post:""
},
"cy": {
pre:"",
var:"X",
add:209,
post:""
},
"fy": {
pre:"",
var:"Y",
add:209,
post:""
},
"xlink:href": "#linearGradient4114",
"xlink:actuate": "onLoad"
}
],
"Linears": [
{
"xlink:actuate": "onLoad",
"xlink:type": "simple",
"id": "linearGradient4114",
"xlink:show": "other",
"xmlns:xlink": "http://www.w3.org/1999/xlink",
"Stops": [
{
"offset": "0",
"style": {
"pre": "stop-color:",
"var": "Eye",
"post": ";stop-opacity:1;"
},
"id": "stop4116"
},
{
"style": {
"pre": "stop-color:",
"var": "Eye2",
"post": ";stop-opacity:1;"
},
"offset": "0.5",
"id": "stop4122"
},
{
"offset": "1",
"style": {
"pre": "stop-color:",
"var": "Eye3",
"post": ";stop-opacity:1;"
},
"id": "stop4118"
}
]
}
]
}
That part of the config file then gets turned into:
<defs id="defs0">
<linearGradient
xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:type="simple"
xlink:actuate="onLoad"
id="linearGradient4114"
xlink:show="other">
<stop
style="stop-color:#d80a00;stop-opacity:1;"
offset="0"
id="stop4116"/>
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0.5"
id="stop4122"/>
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="1"
id="stop4118"/>
</linearGradient>
<radialGradient
gradientTransform="matrix(0.96774313,-1.5022501e-7,3.5488612e-7,1.483871,-201.69415,-217.84922)"
xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:type="simple"
xlink:href="#linearGradient4114"
id="radialGradient5607"
xlink:show="other"
gradientUnits="userSpaceOnUse"
r="15.5"
xlink:actuate="onLoad"
cx="392.0"
fx="427.0"
cy="274.0"
fy="309.0"
inkscape:collect="always"/>
I assume this is a namespace thing (I don't quite get XML/SVG). I assume this because I have had an error like this before, where it wouldn't work in the preview but it would work after export (and loading the svg file). Note that this is a quick home application, so the code isn't the finest (nor the design), but it works for what I am doing. Also note that I do not type these config files by hand (that would be insane), I have a converter that converts an svg file to these configs (yes it sounds like going back and forth but I am combining multiple of these configs to make an image).
I figured it out after messing around yesterday and today. The xlink:href attribute requires that you set its namespace to be xlink's. For my project that meant just doing this:
After adding that in, my gradients worked successfully.