JEditorPane with inline image
Asked Answered
U

1

12

I'm attempting to display an inline image in a Java JEditorPane. The code below uses HTML content that properly displays the image in Firefox, but not in the JEditorPane. Any ideas why? Thanks.

import javax.swing.*;
import java.awt.*;

public class InlineImage {

    public InlineImage() {
        JFrame frame=new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JEditorPane edit=new JEditorPane();
        frame.getContentPane().add(edit);
        edit.setContentType("text/html");

        String html = "<html><body>Local image<br><img src=\"data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAyCAYAAACqNX6+AAACeklEQVR42u1bHZBCURgNgiBYCINgIVhYCIKFhSBYCIIgCIKFxSBoZpsJgjAIgmAhCIIgCIKFIAiChSAIF4IgCL7d82abnWl69Xq9+7r1Dhyp93PfOff7ufd+n8/nEyF0AkmgIAQFoSDEjQgSCn1LPD6SbPZDSqWKNBqv0m5nZDh8lsnkUebziIH1OiC/d+wF/tteN50+GPfiGbVaQcrld8nnm8Y78C4K8odAYC3R6Jfkci2pVosGaYtFWDYbvynRKgDx8G4Ij7FgTBjbzQuC2ZhOd4wZCgIOzfBLYysSxooxh8OL2xAEH4KPGo3irs98pwF3CZcXi42vS5CtCPiAaxfBDLPZvRQKNUWW49CDEomBdDrpmxXBDN1uSlKprvj9m8sLgkHAx47HMU+JYObSkBmenxDYvDGTaRum63UhdoFUG9maa4IgW4KZkvzD6PVebMaYEy6GSS6XdyTcIlaroA1rsRgr6vU3zwVsp4BFZzC4ckYQBCmYH4k9D4NBwmLAP2IZFMNZUY6nxwf+rFRKJNJhYLVvSxAs9Bgz1ADcniQIzIprDLVbL+aua8+PyWSfxCkGOLYsSKuVI2mKAY4tC4LlP0lTv8ViWRAS5g4oyLUKQpelmctiUNcsqDPt1Szt5cJQs4Uht0402zrh5qKGm4tb19XvJ0mkq2ciPKC6ngOq3SNcEms/xXXsCJdFDhoWOeyWAdGFWSsDikTm7hXKwVq4VjEvlLNfWnpmKSkqGFlK+l9Kaj1WuFBs7cWKRrgmbYqtvdyOUCxW9W5HOCQOXBobdtjSxpY2J5o+L0W+55o+7bZFN5t5JW3RT0+fbIsmKAgFISgIBSHU4QdCoO0W7Xd4AwAAAABJRU5ErkJggg==\"></body></html>";
        edit.setText(html);

        frame.setSize(500,300);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {new InlineImage();}
}
Urion answered 22/2, 2012 at 2:20 Comment(0)
K
18

You need to add a protocol handler for "data:" so an URL/URLConnection can be opened for it. Alternatively you could create some protocol handler "resource:" for class path resources.

You need a package data with a class Handler (fixed name convention!). This will be the factory class for "data:" return an URLConnection. We will create DataConnection for that.

Installing a protocol handler can be done via System.setProperty. Here I provided Handler.install(); to do that in a generic way.

package test1.data;

import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;

public class Handler extends URLStreamHandler {

    @Override
    protected URLConnection openConnection(URL u) throws IOException {
        return new DataConnection(u);
    }

    public static void install() {
        String pkgName = Handler.class.getPackage().getName();
        String pkg = pkgName.substring(0, pkgName.lastIndexOf('.'));

        String protocolHandlers = System.getProperty("java.protocol.handler.pkgs", "");
        if (!protocolHandlers.contains(pkg)) {
            if (!protocolHandlers.isEmpty()) {
                protocolHandlers += "|";
            }
            protocolHandlers += pkg;
            System.setProperty("java.protocol.handler.pkgs", protocolHandlers);
        }
    }
}

The URLConnection gives an InputStream to the bytes:

package test1.data;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import javax.xml.bind.DatatypeConverter;

public class DataConnection extends URLConnection {

    public DataConnection(URL u) {
        super(u);
    }

    @Override
    public void connect() throws IOException {
        connected = true;
    }

    @Override
    public InputStream getInputStream() throws IOException {
        String data = url.toString();
        data = data.replaceFirst("^.*;base64,", "");
        System.out.println("Data: " + data);
        byte[] bytes = DatatypeConverter.parseBase64Binary(data);
        return new ByteArrayInputStream(bytes);
    }

}

The clever thing here is to use Base64 decoding of DatatypeConverter in standard Java SE.


P.S.

Nowadays one would use Base64.getEncoder().encode(...).

Kruse answered 22/2, 2012 at 3:36 Comment(8)
"The clever thing here" I saw about 4 things that made me think - Wow, that's clever! My 'learn item(s)' of the day. :)Rework
Worked like a charm! Many thanks. Researching your answer did raise one question. I found several references to the fact that the java.protocol.handler.pkgs property is read by the JVM at startup, so the handlers must be provided on the command line (e.g. java -Djava.protocol.handler.pkgs=xxxxxx). But I simply called test1.data.Handler.install() in my main() method and it worked. Has the behavior changed in newer versions of Java, or did I simply read it wrong?Urion
The command line definition -D is equivalent to System.setProperty. Common sense is not to preload Handlers (like the rare ftp, https), as this would slow down startup. You probably encountered the alternative, using an URLStreamFactory as extra parameter. So really it very dynamic.Kruse
I was hoping that was the case. Thanks again for the help.Urion
Fantastic answer, using it to render image's in this jasper report answerAgathy
fixed name convention! : Strange, You will have to take care to keep the class name AND the end of the package name: have to end with .data (this is an obscure tip or me, but useful here)Monopolist
@Monopolist yes java SE there really chose a baroque solution. Maybe because it is a very old solution, before java SPI I believe.Kruse
This one works awesome. Thanks a lot @JoopEggen. It saved a lot of effort for me.Tricycle

© 2022 - 2024 — McMap. All rights reserved.