feature:实现了幻灯片的pdf和图片导出
This commit is contained in:
@@ -50,6 +50,10 @@ public class Controller implements Initializable {
|
||||
private Button openSlideButton; // 添加打开幻灯片按钮
|
||||
@FXML
|
||||
private Button saveSlideButton; // 添加保存幻灯片按钮
|
||||
@FXML
|
||||
private Button exportPageButton; // 添加导出页面按钮
|
||||
@FXML
|
||||
private Button exportSlideButton; // 添加导出幻灯片按钮
|
||||
|
||||
private Slide currentSlide;
|
||||
private DrawingCanvas drawingCanvas;
|
||||
@@ -430,6 +434,86 @@ public class Controller implements Initializable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出页面为图片
|
||||
*/
|
||||
@FXML
|
||||
public void onExportPage(ActionEvent actionEvent) {
|
||||
if (drawingCanvas == null) {
|
||||
showWarning("请先选择页面", "请先选择一个页面。");
|
||||
return;
|
||||
}
|
||||
|
||||
FileChooser fileChooser = new FileChooser();
|
||||
fileChooser.setTitle("导出页面为图片");
|
||||
fileChooser.getExtensionFilters().addAll(
|
||||
new FileChooser.ExtensionFilter("PNG图片 (*.png)", "*.png"),
|
||||
new FileChooser.ExtensionFilter("JPG图片 (*.jpg)", "*.jpg"),
|
||||
new FileChooser.ExtensionFilter("所有文件", "*.*")
|
||||
);
|
||||
|
||||
SlidePage currentPage = pageListView.getSelectionModel().getSelectedItem();
|
||||
if (currentPage != null) {
|
||||
fileChooser.setInitialFileName(currentPage.getTitle() + ".png");
|
||||
}
|
||||
|
||||
stage = (Stage) exportPageButton.getScene().getWindow();
|
||||
File file = fileChooser.showSaveDialog(stage);
|
||||
|
||||
if (file != null) {
|
||||
try {
|
||||
String format = file.getName().endsWith(".jpg") || file.getName().endsWith(".jpeg") ? "jpg" : "png";
|
||||
ExportUtil.exportPageAsImage(drawingCanvas, file, format);
|
||||
|
||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||
alert.setTitle("成功");
|
||||
alert.setHeaderText("页面已导出");
|
||||
alert.setContentText("页面已导出到: " + file.getAbsolutePath());
|
||||
alert.showAndWait();
|
||||
} catch (Exception e) {
|
||||
showWarning("错误", "无法导出页面: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出幻灯片为PDF
|
||||
*/
|
||||
@FXML
|
||||
public void onExportSlide(ActionEvent actionEvent) {
|
||||
if (currentSlide == null) {
|
||||
showWarning("无幻灯片", "请先创建或打开一个幻灯片。");
|
||||
return;
|
||||
}
|
||||
|
||||
FileChooser fileChooser = new FileChooser();
|
||||
fileChooser.setTitle("导出幻灯片为PDF");
|
||||
fileChooser.getExtensionFilters().addAll(
|
||||
new FileChooser.ExtensionFilter("PDF文件 (*.pdf)", "*.pdf"),
|
||||
new FileChooser.ExtensionFilter("所有文件", "*.*")
|
||||
);
|
||||
fileChooser.setInitialFileName(currentSlide.getName() + ".pdf");
|
||||
|
||||
stage = (Stage) exportSlideButton.getScene().getWindow();
|
||||
File file = fileChooser.showSaveDialog(stage);
|
||||
|
||||
if (file != null) {
|
||||
try {
|
||||
ExportUtil.exportSlideToPDF(currentSlide, file);
|
||||
|
||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||
alert.setTitle("成功");
|
||||
alert.setHeaderText("幻灯片已导出为PDF");
|
||||
alert.setContentText("PDF已导出到: " + file.getAbsolutePath());
|
||||
alert.showAndWait();
|
||||
} catch (Exception e) {
|
||||
showWarning("错误", "无法导出PDF: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示警告对话框
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
package dev.bytevibe.hyperpoint;
|
||||
|
||||
import javafx.scene.image.WritableImage;
|
||||
import javafx.scene.image.PixelReader;
|
||||
import javafx.scene.layout.Pane;
|
||||
import com.itextpdf.text.*;
|
||||
import com.itextpdf.text.pdf.PdfWriter;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 导出工具类,用于导出页面为图片和导出幻灯片为PDF
|
||||
*/
|
||||
public class ExportUtil {
|
||||
|
||||
/**
|
||||
* 导出页面为图片(PNG或JPG)
|
||||
*
|
||||
* @param pane 要导出的页面Pane
|
||||
* @param file 输出文件
|
||||
* @param format 图片格式 "png" 或 "jpg"
|
||||
* @throws IOException 如果导出失败
|
||||
*/
|
||||
public static void exportPageAsImage(Pane pane, File file, String format) throws IOException {
|
||||
// 获取Pane的宽度和高度
|
||||
double width = pane.getWidth();
|
||||
double height = pane.getHeight();
|
||||
|
||||
// 如果宽度或高度无效,尝试使用布局宽度/高度
|
||||
if (width <= 0 || height <= 0) {
|
||||
width = pane.getLayoutBounds().getWidth();
|
||||
height = pane.getLayoutBounds().getHeight();
|
||||
}
|
||||
|
||||
// 如果仍然无效,使用固定的默认大小
|
||||
if (width <= 0) {
|
||||
width = 1024;
|
||||
}
|
||||
if (height <= 0) {
|
||||
height = 768;
|
||||
}
|
||||
|
||||
// 创建WritableImage用于捕获页面内容
|
||||
WritableImage writableImage = new WritableImage((int) width, (int) height);
|
||||
pane.snapshot(null, writableImage);
|
||||
|
||||
// 转换为BufferedImage
|
||||
BufferedImage bufferedImage = convertFXImageToBufferedImage(writableImage);
|
||||
|
||||
// 写入文件
|
||||
String formatName = format.equalsIgnoreCase("jpg") ? "jpg" : "png";
|
||||
if (!ImageIO.write(bufferedImage, formatName, file)) {
|
||||
throw new IOException("无法写入" + formatName + "文件");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将JavaFX WritableImage转换为BufferedImage
|
||||
*/
|
||||
private static BufferedImage convertFXImageToBufferedImage(WritableImage fxImage) {
|
||||
int width = (int) fxImage.getWidth();
|
||||
int height = (int) fxImage.getHeight();
|
||||
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
|
||||
PixelReader pixelReader = fxImage.getPixelReader();
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
int argb = pixelReader.getArgb(x, y);
|
||||
bufferedImage.setRGB(x, y, argb);
|
||||
}
|
||||
}
|
||||
|
||||
return bufferedImage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出整个幻灯片为PDF
|
||||
*
|
||||
* @param slide 要导出的幻灯片
|
||||
* @param file 输出PDF文件
|
||||
* @throws Exception 如果导出失败
|
||||
*/
|
||||
public static void exportSlideToPDF(Slide slide, File file) throws Exception {
|
||||
if (slide == null || slide.getPages().isEmpty()) {
|
||||
throw new IOException("幻灯片没有页面");
|
||||
}
|
||||
|
||||
// 创建PDF文档
|
||||
Document document = new Document(PageSize.A4);
|
||||
PdfWriter.getInstance(document, new FileOutputStream(file));
|
||||
document.open();
|
||||
|
||||
// 为每一页添加内容
|
||||
for (SlidePage page : slide.getPages()) {
|
||||
// 添加页面标题
|
||||
Paragraph title = new Paragraph(page.getTitle(), new com.itextpdf.text.Font(
|
||||
com.itextpdf.text.Font.FontFamily.HELVETICA, 16, com.itextpdf.text.Font.BOLD));
|
||||
title.setSpacingAfter(10);
|
||||
document.add(title);
|
||||
|
||||
// 添加页面内容(如果有的话)
|
||||
if (page.getContent() != null && !page.getContent().isEmpty()) {
|
||||
Paragraph content = new Paragraph(page.getContent(), new com.itextpdf.text.Font(
|
||||
com.itextpdf.text.Font.FontFamily.HELVETICA, 12));
|
||||
content.setSpacingAfter(10);
|
||||
document.add(content);
|
||||
}
|
||||
|
||||
// 添加对象列表
|
||||
addObjectsToDocument(document, page.getPageContent());
|
||||
|
||||
// 每页之间添加分页符
|
||||
document.newPage();
|
||||
}
|
||||
|
||||
document.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将页面内容的对象添加到PDF文档
|
||||
*/
|
||||
private static void addObjectsToDocument(Document document, PageContent pageContent) throws DocumentException {
|
||||
for (DrawableObject obj : pageContent.getDrawableObjects()) {
|
||||
if (obj instanceof TextObject) {
|
||||
addTextObjectToDocument(document, (TextObject) obj);
|
||||
} else if (obj instanceof ShapeObject) {
|
||||
addShapeObjectToDocument(document, (ShapeObject) obj);
|
||||
} else if (obj instanceof ImageObject) {
|
||||
addImageObjectToDocument(document, (ImageObject) obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加文本对象到PDF
|
||||
*/
|
||||
private static void addTextObjectToDocument(Document document, TextObject textObj) throws DocumentException {
|
||||
float fontSize = (float) textObj.getFontSize();
|
||||
int fontStyle = com.itextpdf.text.Font.NORMAL;
|
||||
|
||||
if (textObj.getFontStyle().contains("BOLD")) {
|
||||
fontStyle |= com.itextpdf.text.Font.BOLD;
|
||||
}
|
||||
if (textObj.getFontStyle().contains("ITALIC")) {
|
||||
fontStyle |= com.itextpdf.text.Font.ITALIC;
|
||||
}
|
||||
|
||||
com.itextpdf.text.Font font = new com.itextpdf.text.Font(
|
||||
com.itextpdf.text.Font.FontFamily.HELVETICA, fontSize, fontStyle);
|
||||
|
||||
Paragraph paragraph = new Paragraph(textObj.getText(), font);
|
||||
paragraph.setSpacingAfter(5);
|
||||
document.add(paragraph);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加形状对象到PDF
|
||||
*/
|
||||
private static void addShapeObjectToDocument(Document document, ShapeObject shapeObj) throws DocumentException {
|
||||
String shapeInfo = "图形: " + shapeObj.getShapeType().name() +
|
||||
" (位置: " + (int)shapeObj.getX() + ", " + (int)shapeObj.getY() +
|
||||
" 大小: " + (int)shapeObj.getWidth() + "x" + (int)shapeObj.getHeight() + ")";
|
||||
|
||||
Paragraph paragraph = new Paragraph(shapeInfo, new com.itextpdf.text.Font(
|
||||
com.itextpdf.text.Font.FontFamily.HELVETICA, 10));
|
||||
paragraph.setSpacingAfter(5);
|
||||
document.add(paragraph);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加图片对象到PDF
|
||||
*/
|
||||
private static void addImageObjectToDocument(Document document, ImageObject imageObj) throws DocumentException {
|
||||
String imagePath = imageObj.getImagePath();
|
||||
if (imagePath != null && !imagePath.isEmpty()) {
|
||||
try {
|
||||
// 检查文件是否存在
|
||||
File imageFile = new File(imagePath);
|
||||
if (imageFile.exists()) {
|
||||
Image img = Image.getInstance(imagePath);
|
||||
// 限制图片大小
|
||||
float maxWidth = document.getPageSize().getWidth() - 40;
|
||||
float maxHeight = document.getPageSize().getHeight() / 3;
|
||||
if (img.getWidth() > maxWidth) {
|
||||
img.scaleToFit(maxWidth, maxHeight);
|
||||
}
|
||||
img.setSpacingAfter(10);
|
||||
document.add(img);
|
||||
} else {
|
||||
Paragraph para = new Paragraph("[图片不存在: " + imagePath + "]",
|
||||
new com.itextpdf.text.Font(com.itextpdf.text.Font.FontFamily.HELVETICA, 10));
|
||||
para.setSpacingAfter(5);
|
||||
document.add(para);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Paragraph para = new Paragraph("[无法加载图片: " + e.getMessage() + "]",
|
||||
new com.itextpdf.text.Font(com.itextpdf.text.Font.FontFamily.HELVETICA, 10));
|
||||
para.setSpacingAfter(5);
|
||||
document.add(para);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
module dev.bytevibe.hyperpoint {
|
||||
requires javafx.controls;
|
||||
requires javafx.fxml;
|
||||
requires javafx.graphics;
|
||||
requires java.desktop;
|
||||
requires com.google.gson;
|
||||
|
||||
requires itextpdf;
|
||||
requires javafx.swing;
|
||||
|
||||
opens dev.bytevibe.hyperpoint to javafx.fxml;
|
||||
exports dev.bytevibe.hyperpoint;
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
<Button fx:id="newPageButton" mnemonicParsing="false" onAction="#onNewPage" text="新建页面" />
|
||||
<Button fx:id="deletePageButton" mnemonicParsing="false" onAction="#onDeletePage" text="删除页面" />
|
||||
<Button fx:id="saveSlideButton" mnemonicParsing="false" onAction="#onSaveSlide" text="保存幻灯片" />
|
||||
<Button fx:id="exportPageButton" mnemonicParsing="false" onAction="#onExportPage" text="导出页面" />
|
||||
<Button fx:id="exportSlideButton" mnemonicParsing="false" onAction="#onExportSlide" text="导出幻灯片" />
|
||||
<Label text=" | " />
|
||||
<Button fx:id="addTextButton" mnemonicParsing="false" onAction="#onAddText" text="添加文本" />
|
||||
<Button fx:id="addLineButton" mnemonicParsing="false" onAction="#onAddLine" text="直线" />
|
||||
|
||||
Reference in New Issue
Block a user