JavaFX 2.0+ WebView /WebEngine 将网页呈现为图像

Posted

技术标签:

【中文标题】JavaFX 2.0+ WebView /WebEngine 将网页呈现为图像【英文标题】:JavaFX 2.0+ WebView /WebEngine render web page to an image 【发布时间】:2011-12-09 10:34:14 【问题描述】:

我正在寻找一种方法来加载页面并将渲染保存为图像,就像您对 CutyCapt 所做的那样(QT + webkit EXE 可以做到这一点)。

目前,在没有 JavaFX 的情况下,我通过从 java 调用外部进程并渲染到文件而不是将该文件加载到 ImageBuffer 中来做到这一点......既不是非常优化也不实用,甚至更不跨平台......

使用 JavaFX2+ 我尝试使用 WebView 和 WebEngine:

public class WebComponentTrial extends Application 
    private Scene scene;

    @Override
    public void start(final Stage primaryStage) throws Exception 
        primaryStage.setTitle("Web View");
        final Browser browser = new Browser();
        scene = new Scene(browser, 1180, 800, Color.web("#666970"));
        primaryStage.setScene(scene);
        scene.getStylesheets().add("webviewsample/BrowserToolbar.css");
        primaryStage.show();
    

    public static void main(final String[] args) 
        launch(args);
    

class Browser extends Region 
    static  // use system proxy settings when standalone application
    // System.setProperty("java.net.useSystemProxies", "true");
    

    final WebView browser = new WebView();
    final WebEngine webEngine = browser.getEngine();

    public Browser() 
        getStyleClass().add("browser");
        webEngine.load("http://www.google.com/");
        getChildren().add(browser);
    

    @Override
    protected void layoutChildren() 
        final double w = getWidth();
        final double h = getHeight();
        layoutInArea(browser, 0, 0, w, h, 0, HPos.CENTER, VPos.CENTER);
    

    @Override
    protected double computePrefWidth(final double height) 
        return 800;
    

    @Override
    protected double computePrefHeight(final double width) 
        return 600;
    

有一个已弃用的方法:renderToImage in Scene(请参阅下面的链接)可以做一些接近的事情,我可能可以使用它,但它已被弃用...... 它在 JavaFX 中被弃用似乎意味着没有 javadoc 广告替换方法,因为我无权访问代码,我看不到它是如何完成的......

这里有几个网站,我在其中找到了一些信息,但没有将网页呈现为图像:

http://tornorbye.blogspot.com/2010/02/how-to-render-javafx-node-into-image.html

canvasImagesaveImage(canvasImage, fc.getSelectedFile()) 来自这个:

http://javafx.com/samples/EffectsPlayground/src/Main.fx.html

其他:

http://download.oracle.com/javafx/2.0/webview/jfxpub-webview.htm
http://download.oracle.com/javafx/2.0/get_started/jfxpub-get_started.htm
http://fxexperience.com/2011/05/maps-in-javafx-2-0/

【问题讨论】:

【参考方案1】:

我通过在 Swing JFrame 和 JFXPanel 上启动 JavaFX WebView 来做到这一点。然后,一旦 WebEngine 状态为 SUCCEEDED,我就在 JFXPanel 上使用 paint() 方法。

您可以按照本教程制作 WebView:Integrating JavaFX into Swing Applications

下面的代码演示了我如何从 JFXPanel 捕获渲染的屏幕。

public static void main(String args[]) 
    jFrame = new JFrame("Demo Browser");
    jfxPanel = new JFXPanel();
    jFrame.add(jfxPanel);
    jFrame.setVisible(true);
    jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    SwingUtilities.invokeLater(new Runnable() 
        @Override
        public void run() 
            Platform.runLater(new Runnable() 
                @Override
                public void run() 
                    browser = new FXBrowser();
                    jfxPanel.setScene(browser.getScene());
                    jFrame.setSize((int) browser.getWebView().getWidth(), (int) browser.getWebView().getHeight());

                    browser.getWebEngine().getLoadWorker().stateProperty().addListener(
                            new ChangeListener() 
                                @Override
                                public void changed(ObservableValue observable,
                                                    Object oldValue, Object newValue) 
                                    State oldState = (State) oldValue;
                                    State newState = (State) newValue;
                                    if (State.SUCCEEDED == newValue) 
                                        captureView();
                                    
                                
                            );
                
            );
        
    );


private static void captureView() 
    BufferedImage bi = new BufferedImage(jfxPanel.getWidth(), jfxPanel.getHeight(), BufferedImage.TYPE_INT_ARGB);
    Graphics graphics = bi.createGraphics();
    jfxPanel.paint(graphics);
    try 
        ImageIO.write(bi, "PNG", new File("demo.png"));
     catch (IOException e) 
        e.printStackTrace();
    
    graphics.dispose();
    bi.flush();

【讨论】:

我会使用printprintAll 而不是paint,但基本的想法很酷! 什么是 FXBrowser?【参考方案2】:

对于 JavaFX 2.2 用户,有一个基于 JavaFX 节点快照的更简洁和优雅的解决方案。您可以在以下位置查看 JavaFX 节点文档:

http://docs.oracle.com/javafx/2/api/javafx/scene/Node.html

这是一个从 WebView 节点(作为 webView)进行捕获的示例

   File destFile = new File("test.png");
   WritableImage snapshot = webView.snapshot(new SnapshotParameters(), null);
   RenderedImage renderedImage = SwingFXUtils.fromFXImage(snapshot, null);
   try 
       ImageIO.write(renderedImage, "png", destFile);
    catch (IOException ex) 
       Logger.getLogger(GoogleMap.class.getName()).log(Level.SEVERE, null, ex);
   

我对这个解决方案没有任何问题,并且 webview 根据节点大小在 PNG 中完美呈现。使用此方法可以呈现任何 JavaFx 节点并将其保存到文件中。

希望对您有所帮助!

【讨论】:

但是无头截图呢?所以我可以在服务器端启动它来截取像 PhantomJS 一样的屏幕截图 对不起,我不明白你的意思 我只是在寻找可以拍摄快照但不显示任何形式/框架的工作示例 - “无头”。就像启动应用程序一样,它会拍摄我通过的任何 html 的快照,而不会在屏幕上显示任何窗口。就像 PhantomJS 一样。【参考方案3】:

JavaFX 工程师发布的解决方法:Snapshot does not work with (invisible) WebView nodes。

拍摄包含WebView 节点的场景快照时,请等待至少 2 帧,然后再发出snapshot 命令。这可以通过使用AnimationTimer 中的计数器跳过 2 个脉冲并在第 3 个脉冲上拍摄快照来完成。

获得快照后,您可以将图像转换为 awt BufferedImage,并使用 ImageIO 将图像编码为 png 或 jpg 等格式。

【讨论】:

你有没有让这个工作?我无法让 WebView 向图像写入任何内容,除非我将它实际附加到场景并在舞台上显示。

以上是关于JavaFX 2.0+ WebView /WebEngine 将网页呈现为图像的主要内容,如果未能解决你的问题,请参考以下文章

为啥JavaFX的WebView是红绿闪烁?

使用 javafx 从 webview 获取内容

JavaFX : 使用 JavaFX 嵌入浏览器而不是可用的 webview

JavaFX WebView:透明WebView保持绘制旧内容

JavaFX 中 WebView 的性能

javafx webview js交互问题