我知道如何使用Swing有限的内置HTML支持将HTML绘制到Graphics2D对象(请参阅http://www.java.net/node/680674),但我需要更好的渲染。最重要的是,对于这个特定的化学图绘制应用程序,Swing的HTML支持不会扩展到嵌套的子/上标。更好的CSS支持也会很好。我不需要图像嵌入或交互功能,如Javascript或热链接。
HTML文本被缩放并旋转,然后绘制成可能包含其他文本和图形的图表。 Graphics2D目标可以是屏幕,打印机或(通过iText)PDF文件。我怀疑在生成出版质量的PDF文件时,任何涉及通过BufferedImage等进行转换的解决方案都可以充分紧凑。
我的(可能是不正确的)印象是JavaFX 2.0还没有解决方案,尽管最终可能会这样。 (如果早期版本可以做到这一点,那可能就是一个解决方案。)将整个应用程序从Swing重写为JavaFX是不现实的。
此应用程序是免费和开源的,因此它使用的任何工具也可能需要自由分发。否则,我相信JWebEngine可能符合要求。
任何帮助都将不胜感激。
答案 0 :(得分:1)
您可以使用JavaFX WebView节点 - 它具有非常好的HTML标记和CSS支持。您可以使用JavaFX原语rotate和scale WebView节点。可以在WebView中使用MathJax来获得高质量的方程式渲染(如果仅仅是普通的html和css不能为您完成工作)。使用JavaFX 2.2,您可以拍摄WebView节点的快照并将其渲染为JavaFX图像。您可以使用JavaFX 2.2 SwingFXUtils将该JavaFX映像转换为awt BufferedImage,并使用ImageIO将其写入多种格式的文件中。
这是example of rendering a piechart node to a png。根据html的复杂程度,有时高质量的图像会很好地压缩到(例如)png文件。在pichart示例中,带有文本和彩色渐变的2000x2000像素饼图保存为168kb的png文件。
不需要将整个应用程序从Swing重写为JavaFX,因为JavaFX包含用于在现有Swing应用程序中嵌入JavaFX应用程序的JFXPanel。节点快照步骤甚至不需要将节点呈现到屏幕(它可以通过内存缓冲区完成) - 尽管JavaFX系统可能需要首先在JavaFX应用程序或JFXPanel中启动和启动。 / p>
以上所有可能会或可能不会最终为您提供您想要的结果,但这似乎是一个有前景的途径。
<强>更新强>
我对此进行了几次测试,虽然我可以对屏幕上显示的WebView进行快照,如本文所述,由于JavaFX 2.2的某些限制,我无法对作为屏幕外场景的一部分显示的WebView进行快照。这意味着此答案中的信息是准确的,但仅适用于可在屏幕上的WebView中显示的HTML部分;例如该技术目前不适用于像素大小超过屏幕像素大小的大型文档。有关示例代码,请参阅https://forums.oracle.com/forums/thread.jspa?threadID=2456191。
答案 1 :(得分:0)
经过大量搜索和拼凑,我发现上面 Update oracle论坛链接中的示例的唯一问题是固定的Webview大小和需要在html中使用的CSS (在JavaFX中不是)
。overflow-x: hidden;
overflow-y: hidden;
隐藏最后一个滚动条。
所以我想出了以下快照方法(带有动画的应用程序作为您的应用程序示例):
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);
}
}