Batik Java - JSVGCanvas Gradient not rendering?

578 views Asked by At

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).

1

There are 1 answers

0
David L On BEST ANSWER

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:

if (entry.getKey().equals("xlink:href")) {
      rad.setAttributeNS(XMLConstants.XLINK_NAMESPACE_URI, "xlink:href", entry.getValue().getAsJsonPrimitive().getAsString());
}

After adding that in, my gradients worked successfully.