feature:实现了幻灯片的pdf和图片导出

This commit is contained in:
2025-11-25 13:33:25 +08:00
parent f7e77cdc5d
commit 16d9e58c57
5 changed files with 309 additions and 1 deletions
@@ -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);
}
}
}
}
+3 -1
View File
@@ -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="直线" />