2

here's the problem: I have several images and would like to use them when displaying a HTML in JavaFX's WebView.

Current implementation is very obvious: there is a file, which is linked to in the HTML content. I assume that WebView does not regress from JEditorPane and will only do a single I/O operation even if the image is referenced 10 000 times throughout the content.

However, it would be great to have a single Image instance and feed it to WebView when it encounters the relevant <img> tag.

I have seen that there is a great half-solution involving URL handling, but the problem remains: you have an Image instance that you convert to a storage format (BMP, PNG with proprietary extensions, etc) and keep that in memory. However, this means that each time WebView desires an image resolution, it must then manually parse the image from binary data. In the end, you just have a file mapped to memory plus an internal Image instance instead of a shared Image instance.

With JEditorPane, you could push Images to its image cache and get rid of such problems. Unfortunately, since Java 7, that component is unusable and is out of question.

Basically, is there any chance WebView/WebEngine maintains such a cache/equivalent and is there a way to pre-populate it?

Tadas S
  • 1,955
  • 19
  • 33
  • Hey man if you got any solution to your problem since 6 months than please share with me I need the same thing that you want to do with webview, Thanks :) – Muhammad Oct 27 '14 at 11:24
  • 1
    Unfortunately, the situation is unchanged, my app still uses a temporary cache on the file system, since that approach works with either component. Nothing new. I guess unless JFX becomes as widespread as Swing, this will have to suffice. – Tadas S Oct 27 '14 at 15:10
  • Thank you Friend For your reply :) – Muhammad Oct 28 '14 at 01:23

1 Answers1

2
    /**
     * Encodes the image as a whole into PNG, then into Base64 and finally into an URI suitable for the HTML {@code <img>} tag.
     * 
     * @param image an image
     * @return image as URI (image within the URI)
     * @throws IIOException if there is a fault with an image writer
     * @throws IOException in case of a general I/O error
     */
    public static final String getImageSrcForWebEngine(RenderedImage image) throws IIOException, IOException
    {
        final ByteArrayOutputStream output = new ByteArrayOutputStream();
        ImageIO.write(image, "PNG", output);
        return "data:base64," + Base64.getMimeEncoder().encodeToString(output.toByteArray());
    }

Example of usage:

RenderedImage image = […];
String tag = "<img src=\"" + getImageSrcForWebEngine(image) + "\" border=\"0\" />";

SSCCE:

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;

import javax.imageio.IIOException;
import javax.imageio.ImageIO;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.web.WebView;
import javafx.stage.Stage;

public class WebViewWithMemoryImages extends Application
{
    private static String IMAGE_IN_MEMORY;

    @Override
    public void start(Stage primaryStage)
    {
        WebView webView = new WebView();
        webView.getEngine().loadContent("<html><body><img src=\"" + IMAGE_IN_MEMORY + "\"></body></html>");
        primaryStage.setScene(new Scene(webView, 420, 420));
        primaryStage.show();
    }

    public static void main(String[] args) throws Exception
    {
        BufferedImage image = new BufferedImage(400, 400, BufferedImage.TYPE_INT_BGR);
        Graphics2D g = image.createGraphics();
        try
        {
            g.setColor(Color.RED);
            g.fillRect(0, 0, 400, 400);
            g.setColor(Color.WHITE);
            g.fillRect(50, 50, 300, 300);
            g.setColor(Color.BLACK);
            g.fillRect(100, 100, 200, 200);
            g.drawString("No image files were used in this WebView.", 90, 70);
        }
        finally
        {
            g.dispose();
        }
        IMAGE_IN_MEMORY = getImageSrcForWebEngine(image);

        launch(args);
    }

    public static String getImageSrcForWebEngine(RenderedImage image) throws IIOException, IOException
    {
        final ByteArrayOutputStream output = new ByteArrayOutputStream();
        ImageIO.write(image, "PNG", output);
        return "data:base64," + Base64.getMimeEncoder().encodeToString(output.toByteArray());
    }
}
Tadas S
  • 1,955
  • 19
  • 33
  • I'm setting the attribute via the setAttribute method of org.w3c.dom.Element and I had to put "data:image/png;base64" at the beginning of the stream for it to work, instead of only "data:base64,". I found the correct string here: https://stackoverflow.com/a/16449483/261769 – Customizer Mar 11 '18 at 21:56
  • That's correct, JavaFX internals were updated since this answer was originally posted. – Tadas S Mar 15 '18 at 13:05
  • There is a problem with this approach when dragging the images. Any ideas how to fix this? https://stackoverflow.com/questions/45398912/unknown-protocol-data-when-using-data-uris-in-img-tags-displayed-by-javafx-we – Customizer May 09 '18 at 21:46
  • It's an open bug (https://bugs.openjdk.java.net/browse/JDK-8160597). Do you think there is a workaround? – Customizer May 09 '18 at 21:51