Search code examples
javascriptjavasvgjavax.scriptidenticon

Save an SVG image from a website using Java (desktop)


I'm generating a website with JDenticon using this code (test.html):

<!DOCTYPE HTML>

<html>
    <head>
        <title>Test</title>
    </head>
    <body>
        <script src="https://cdn.jsdelivr.net/jdenticon/1.3.2/jdenticon.min.js" async></script>

        <svg width="200" height="200" data-jdenticon-hash="ff8adece0631821959f443c9d956fc39">
        Fallback text for browsers not supporting inline svg</svg>

    </body>
</html>

I want to get the image generated in this code to be accesible by a Java desktop application and want it to be saved on my PC. Is this possible and maybe also with changing the hascode (ff8adece0631821959f443c9d956fc39)

And whats the code for it?

Edit #1:

I've found something in the API of JDenticon that looks like this and is used for .NET Framework:

var engine = new Jurassic.ScriptEngine();
engine.ExecuteFile("<path to jdenticon.js>");
engine.SetGlobalValue("size", 200);
engine.SetGlobalValue("hash", "ff8adece0631821959f443c9d956fc39");

var svg = engine.Evaluate<string>("jdenticon.toSvg(hash, size)");
File.WriteAllText("testicon.svg", svg);

I wanted to do something like this in Java and found Java ScriptEngine (Tutorials: Oracle). But I don't know how to use it, right now my code looks like this:

ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("JavaScript");

engine.put("size", 200);
engine.put("hash", "ff8adece0631821959f443c9d956fc39");

String svg = (String) engine.eval(new java.io.FileReader("C:/jdenticon.js"));

This obviously won't work because it does not call the jdenticon.toSvg(hash, size) method in the jdenticon.js file. So if someone has an idea on how to solve the problem with thisit would be very nice.


Solution

  • Your updated question including the interesting idea to just run the original code inside a JavaScript interpreter that is apparently built in into Java SE, made me pursue the approach further, resulting in the following code that I have tested to generate the SVG document text, which at least on my computer generates an expected SVG icon image.

    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import javax.script.*;
    
    public class JDenticonClient {
        public static void main(String args[]) throws FileNotFoundException, ScriptException {
            String svgText = new JDenticonClient().getSVGDocumentText("ff8adece0631821959f443c9d956fc39", 200);
            System.out.print(svgText);
        }
        public String getSVGDocumentText(String hash, int size) throws FileNotFoundException, ScriptException {
            ScriptEngineManager factory = new ScriptEngineManager();
            ScriptEngine scriptEngine = factory.getEngineByName("JavaScript");
            scriptEngine.eval(new FileReader("jdenticon.min.js"));
            return scriptEngine.eval("jdenticon.toSvg(\"" + hash + "\", \"" + size + "\")").toString();
        }
    }
    

    Even though we approached the same solution, you have to consider the following:

    1. The put method exposes additional script host objects to the scripts being evaluated. This is basically the entire point using script engines with a custom host that is not a web browser with a certain set of APIs or Node.js or anything like that. Basically, you can create your own APIs using put or simulate existing ones -- yes, even replicate the entire browser API set, making your application a scripting host compatible with a modern browser.
    2. I compose the evaluated string myself, but this is a technicality -- exposing native hash and size as String and int objects respectively, to the script using put would have worked as well, but then you have those as globals, which is not always a smart thing to do, given how these are natural one-shot parameters to the toSvg procedure.
    3. Even though you correctly included the JDenticon script evaluation, you may have not taken into account that by default, merely including (and running) the script simply calls jdenticon procedure which looks for canvas and svg elements inside a DOM tree, which in your case does not even exist. In fact, I am impressed the evaluation of the contents of the script file did not abort mid-way due to an exception where assumptions about DOM are made. But that's probably because the script is made to work with Node.js as well, which does not have DOM either.
    4. The code above can be optimized to not have to create a new factory and new script engine every time it needs to generate an SVG. I leave it all as an exercise.
    5. The getSVGDocumentText returns the entire generated SVG document as text. You'd have to add some logic if you want to e.g. save said text into an *.svg file.

    P.S. Thanks for pointing me out in the direction of ScriptEngine. I didn't even know Java had one. Nice to know, never know.