Java – How do I insert HTML text in JavaFX shapes?

How do I insert HTML text in JavaFX shapes?… here is a solution to the problem.

How do I insert HTML text in JavaFX shapes?

I want to create a tree-based algorithm visualization in JavaFx with a lot of subscripts and super scripts in the symbols. I want to add these symbols to shapes like circles.

I tried to do this using the WebView object, but it just covered the entire screen.

public void start(Stage primaryStage) throws Exception{

primaryStage.setTitle("Shape Text");
        Group circles = new Group();
        Circle circle = new Circle(50, Color.web("white", 0.7));
        circle.setCenterX(500.0f);
        circle.setCenterY(200.0f);
        circle.setStrokeType(StrokeType.OUTSIDE);
        circle.setStroke(Color.web("white", 0.16));
        circle.setStrokeWidth(4);
        circles.getChildren().add(circle);

WebView webView = new WebView();
        WebEngine webEngine = webView.getEngine();
        webEngine.loadContent("<h1>B<sub>0</sub></h1>");

StackPane stack = new StackPane();
        stack.getChildren().addAll(circles, webView);

Scene scene = new Scene(stack, 1000, 800, Color.BLACK);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

The above code replaces the entire View with HTML text. I’ve also tried the javafx.scene.text.Text class, but it doesn’t support HTML content.

Thanks in advance!

Solution

There are three things you may need to do:

  1. Size the WebView to the HTML content (or the internal display area of the shape).
  2. Make background of the WebView pages transparent .
  3. Center the HTML content in the WebView, which is centered on Shape.

sized webview

The following code demonstrates some of these techniques:

import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.StrokeType;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;

import java.lang.reflect.Field;

public class ShapedHTML extends Application {
    @Override
    public void start(Stage stage) throws Exception{
        stage.setTitle("Shape Text");
        Group circles = new Group();
        Circle circle = new Circle(50, Color.web("white", 0.7));
        circle.setCenterX(500.0f);
        circle.setCenterY(200.0f);
        circle.setStrokeType(StrokeType.OUTSIDE);
        circle.setStroke(Color.web("white", 0.16));
        circle.setStrokeWidth(4);
        circles.getChildren().add(circle);

WebView webView = new WebView();
        WebEngine webEngine = webView.getEngine();
        webView.maxWidthProperty().bind(circle.radiusProperty().multiply(2));
        webView.maxHeightProperty().bind(circle.radiusProperty().multiply(2));
        webEngine.documentProperty().addListener(observable -> {                   
            try {
                 Use reflection to retrieve the WebEngine's private 'page' field.
                Field f = webEngine.getClass().getDeclaredField("page");
                f.setAccessible(true);
                com.sun.webkit.WebPage page = (com.sun.webkit.WebPage) f.get(webEngine);
                page.setBackgroundColor((new java.awt.Color(0, 0, 0, 0)).getRGB());
            } catch (Exception e) {
                System.out.println("Difficulty to make WebView background transparent");
                e.printStackTrace();
            }
        });

webEngine.loadContent("<h1 id='root' style='background : rgba(0,0,0,0); margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); '>B<sub>0</sub></h1>");

StackPane stack = new StackPane();
        stack.getChildren().addAll(circles, webView);

Scene scene = new Scene(stack, 1000, 800, Color.BLACK);
        stage.setScene(scene);
        stage.show();
    }
    public static void main(String[] args) {
        launch(args);
    }

}

Usage considerations for the com.sun class

The above code uses the com.sun class (generally not recommended because it is not a publicly supported API). However, it worked for me (on Java 8) and I don’t know of any better way to achieve transparency of the WebView background.

If you are using a later version of Java (e.g. Java 11+), then you will need to provide some VM parameters to allow the use of the relevant com.sun class. See also, for example, the stack overflow problem Cannot access JavaFX class “WebPage” in IntelliJ-IDEA addresses the accessibility of com.sun.webkit.WebPage in Java 11+. The answer to this question suggests the following VM parameters (I haven’t tried them yet):

--add-exports javafx.web/com.sun.webkit=projectname

The last parameter is the module name that you declared in the module-info.java of the project.

Related Problems and Solutions