0

Here is my problem - I have a desktop application written in JavaFX. I need to show a full-screen webpage to an user and save the rendered page as PNG. I need to save the whole page (e.g. resolution 1920×3500).

Now I'm using Selenium and Firefox to do this. It works fine but there is one big disadvantage - the user must have Firefox installed on his machine.

I've tried to render the webpage using WebView by JavaFX and take a .snapshot(). This would be great but this approach doesn't give me the whole page, only the visible part of the WebView. Is there any approach how to get the whole page snapshot using WebView? Or any other ideas how to do that? Thanks.

honzzyk
  • 149
  • 1
  • 7

2 Answers2

0

You may want to check out this post. I don't know if it is working or not, but it seems like a reasonable solution.

Community
  • 1
  • 1
Ugurcan Yildirim
  • 4,820
  • 2
  • 33
  • 65
  • Thanks for reply. I've tried that but it is not working - it gives me the screenshot e.g. 1920×3500 but there is only the visible part of the website and the rest is black. I've tried this sample https://gist.github.com/jewelsea/5632958 too. It is working well but it seems that `WebView` has problems if its height is bigger than the monitor height. Some webpages are not working well in this case - for example this template http://blacktie.co/demo/pratt/ has non-working navigation ... – honzzyk Sep 03 '15 at 08:43
0

After a lot of searching and scraping several pieces together I found that the only problem I had with an example some SO-poster posted in an oracle forum was that the size of the webview was fixed and that my css used in the html (not in JavaFX) needed.

overflow-x: hidden;
overflow-y: hidden;

to hide the last scrollbar.

So I come up with the following snapshot method (application with animation just as example of your application) See if it works for your sizes, otherwise you might scale javafx.scene.web.WebView.setZoom(double value) the webview down to something that works, you might lose some resolution, but at least have the whole picture:

package application;

import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

import javafx.animation.Animation;
import javafx.animation.PauseTransition;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.effect.GaussianBlur;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.web.WebView;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.util.Duration;

public class WebViewSnapshot extends Application {

    BorderPane rootPane;
    TranslateTransition animation;

    @Override
    public void start(Stage primaryStage) {

        Rectangle rect = new Rectangle(50, 50, 50, 50);
        rect.setFill(Color.CORAL);

        animation = createAnimation(rect);

        Button snapshotButton = new Button("Take snapshot");

        Pane pane = new Pane(rect);
        pane.setMinSize(600, 150);

        rootPane = new BorderPane(pane, null, null, snapshotButton, new Label("This is the main scene"));

        snapshotButton.setOnAction(e -> {
            // html file being shown in webview
            File htmlFile = new File ("generated/template.html");
            // the resulting snapshot png file
            File aboutFile = new File ("generated/about.png");
            generate(htmlFile, aboutFile, 1280, 720);
        });

        BorderPane.setAlignment(snapshotButton, Pos.CENTER);
        BorderPane.setMargin(snapshotButton, new Insets(5));
        Scene scene = new Scene(rootPane);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private TranslateTransition createAnimation(Rectangle rect) {
        TranslateTransition animation = new TranslateTransition(Duration.seconds(1), rect);
        animation.setByX(400);
        animation.setCycleCount(Animation.INDEFINITE);
        animation.setAutoReverse(true);
        animation.play();
        return animation;
    }

    public void generate(File htmlFile, File outputFile, double width, double height) {
        animation.pause();
        // rootPane is the root of original scene in an FXML controller you get this when you assign it an id
        rootPane.setEffect(new GaussianBlur());
        Stage primaryStage = (Stage)rootPane.getScene().getWindow();
        // creating separate webview holding same html content as in original scene
        WebView webView = new WebView();
        // with the size I want the snapshot
        webView.setPrefSize(width, height);
        AnchorPane snapshotRoot = new AnchorPane(webView);
        webView.getEngine().load(htmlFile.toURI().toString());
        Stage popupStage = new Stage(StageStyle.TRANSPARENT);
        popupStage.initOwner(primaryStage);
        popupStage.initModality(Modality.APPLICATION_MODAL);
        // this popup doesn't really show anything size = 1x1, it just holds the snapshot-webview
        popupStage.setScene(new Scene(snapshotRoot, 1, 1));
        // pausing to make sure the webview/picture is completely rendered
        PauseTransition pt = new PauseTransition(Duration.seconds(2));
        pt.setOnFinished(new EventHandler<ActionEvent>() {
            @Override public void handle(ActionEvent event) {
                WritableImage image = webView.snapshot(null, null);
                // writing a png to outputFile
                // writing a JPG like this will result in a pink JPG, see other posts
                // if somebody can scrape me simple code to convert it ARGB to RGB or something
                String format = "png";
                try {
                    ImageIO.write(SwingFXUtils.fromFXImage(image, null), format, outputFile);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } finally {
                    rootPane.setEffect(null);
                    popupStage.hide();
                    animation.play();
                }
            }
        });
        // pausing, after pause onFinished event will take + write snapshot
        pt.play();
        // GO!
        popupStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}
Niek
  • 881
  • 1
  • 9
  • 23