Skip to content

Instantly share code, notes, and snippets.

@danialfarid
Last active November 20, 2019 17:31
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danialfarid/2ddbab04803ae4fd2dca to your computer and use it in GitHub Desktop.
Save danialfarid/2ddbab04803ae4fd2dca to your computer and use it in GitHub Desktop.
Java Image Capture, HTML Snapshot, HTML to image, no external dependencies. Using JavaFX for supporting Operating Systems
import javafx.animation.AnimationTimer;
import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.awt.image.BufferedImage;
import java.lang.reflect.Field;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
@Service
public class JavaFXHtmlImageCapture extends Application {
private static WebView webView;
private static Stage stage;
@PostConstruct
public void initialize() {
System.setProperty("javafx.macosx.embedded", "true");
System.setProperty("glass.platform", "Monocle");
System.setProperty("monocle.platform", "Headless");
System.setProperty("prism.order", "sw");
initMonocleHeadless(true);
new Thread(() -> {
Application.launch(JavaFXHtmlImageCapture.class);
}).start();
}
@Override
public void start(Stage stage) throws Exception {
JavaFXHtmlImageCapture.stage = stage;
webView = new WebView();
webView.setPrefSize(800, 100);
webView.getEngine().load("about:blank");
VBox layout = new VBox(10);
layout.getChildren().setAll(webView);
stage.setScene(new Scene(layout));
stage.show();
}
public synchronized static BufferedImage captureHtml(String html) {
AtomicReference<BufferedImage> captured = new AtomicReference<>();
AtomicReference<Throwable> throwable = new AtomicReference<>();
runLater(() -> {
try {
webView.setPrefSize(800, 100);
webView.setMaxWidth(800);
if (html.startsWith("http")) {
webView.getEngine().load(html);
} else {
webView.getEngine().loadContent(html, "text/html");
}
webView.autosize();
stage.show();
runLater(() -> {
try {
String heightText = webView.getEngine().executeScript("document.body.offsetHeight").toString();
double height = Double.valueOf(heightText) + 5;
stage.setHeight(height);
webView.setPrefHeight(height);
runLater(() -> {
try {
SnapshotParameters parameters = new SnapshotParameters();
parameters.setViewport(new Rectangle2D(0, 0, 760, Math.min(height, 1200) - 3));
WritableImage image = webView.snapshot(parameters, null);
BufferedImage bufferedImage = SwingFXUtils.fromFXImage(image, null);
captured.set(bufferedImage);
// ImageIO.write(bufferedImage, "png", new File("capture.png"));
} catch (Throwable t) {
throwable.set(t);
}
});
} catch (Throwable t) {
throwable.set(t);
}
});
} catch (Throwable t) {
throwable.set(t);
}
});
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
for (int i = 0; i < 200; i++) {
if (captured.get() == null && throwable.get() == null) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
if (throwable.get() != null) {
throw new RuntimeException(throwable.get());
}
return captured.get();
}
public static void runInPlatform(Runnable runnable) {
Platform.runLater(() -> {
final AnimationTimer timer = new AnimationTimer() {
private int pulseCounter;
@Override
public void handle(long now) {
pulseCounter += 1;
if (pulseCounter > 2) {
stop();
runnable.run();
}
}
};
timer.start();
});
}
public static void runLater(Runnable runnable) {
final PauseTransition pt = new PauseTransition();
pt.setDuration(Duration.millis(500));
pt.setOnFinished(actionEvent -> runInPlatform(runnable::run));
pt.play();
}
private static void initMonocleHeadless(boolean headless) {
if (checkSystemPropertyEquals("testfx.headless", "true") || headless) {
try {
assignMonoclePlatform();
assignHeadlessPlatform();
} catch (ClassNotFoundException var3) {
throw new IllegalStateException("Monocle headless platform not found", var3);
} catch (Exception var4) {
throw new RuntimeException(var4);
}
}
}
private static boolean checkSystemPropertyEquals(String propertyName, String valueOrNull) {
return Objects.equals(System.getProperty(propertyName, null), valueOrNull);
}
private static void assignMonoclePlatform() throws Exception {
Class platformFactoryClass = Class.forName("com.sun.glass.ui.PlatformFactory");
Object platformFactoryImpl = Class.forName("com.sun.glass.ui.monocle.MonoclePlatformFactory").newInstance();
assignPrivateStaticField(platformFactoryClass, "instance", platformFactoryImpl);
}
private static void assignHeadlessPlatform() throws Exception {
Class nativePlatformFactoryClass = Class.forName("com.sun.glass.ui.monocle.NativePlatformFactory");
Object nativePlatformImpl = Class.forName("com.sun.glass.ui.monocle.headless.HeadlessPlatform").newInstance();
assignPrivateStaticField(nativePlatformFactoryClass, "platform", nativePlatformImpl);
}
private static void assignPrivateStaticField(Class<?> cls, String name, Object value) throws Exception {
Field field = cls.getDeclaredField(name);
field.setAccessible(true);
field.set(cls, value);
field.setAccessible(false);
}
}
Usage:
new JavaFXHtmlImageCapture().initialize()
BufferedImage image = JavaFXHtmlImageCapture.captureHtml(url or html content);
@angela-tran
Copy link

Does this require usage of OpenJDK?

@TheWizz
Copy link

TheWizz commented Jun 10, 2017

Seems so. Tried it on a Mac with Oracle Java 8 JDK, and it complained about some monocle class not existing.

@Lotzy
Copy link

Lotzy commented Feb 27, 2018

If you are using Maven, then adding this dependencies, should solve compilation problems:

<dependency>
  <!-- for Oracle JDK 1.8 -->
  <groupId>com.oracle</groupId>
  <artifactId>javafx</artifactId>
  <version>2.2</version>
  <systemPath>${java.home}/lib/ext/jfxrt.jar</systemPath>
  <scope>system</scope>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>4.2.5.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.jfxtras</groupId>
  <artifactId>openjfx-monocle</artifactId>
  <version>1.8.0_20</version>
</dependency>

@hmf
Copy link

hmf commented Nov 20, 2019

Won't work with JDK11. Need to use:

    private static void assignHeadlessPlatform() throws Exception {
        Class<?> nativePlatformFactoryClass = Class.forName("com.sun.glass.ui.monocle.NativePlatformFactory");
        try {
            Constructor<?> nativePlatformCtor = Class.forName(
                    "com.sun.glass.ui.monocle.HeadlessPlatform").getDeclaredConstructor();
            nativePlatformCtor.setAccessible(true);
            assignPrivateStaticField(nativePlatformFactoryClass, "platform", nativePlatformCtor.newInstance());
        }
        catch (ClassNotFoundException exception) {
            // Before Java 8u40 HeadlessPlatform was located inside of a "headless" package.
            Constructor<?> nativePlatformCtor = Class.forName(
                    "com.sun.glass.ui.monocle.headless.HeadlessPlatform").getDeclaredConstructor();
            nativePlatformCtor.setAccessible(true);
            assignPrivateStaticField(nativePlatformFactoryClass, "platform", nativePlatformCtor.newInstance());
        }
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment