我已经使用来自Apache的Javafx和PDFbox将文本放入pdf文件中。现在我正在尝试将拍摄场景的屏幕截图放到pdf文档中。
WritableImage snapshot = quotes.getScene().snapshot(null);
PDDocument doc = null;
PDPage page = null;
PDXObjectImage ximg = null;
BufferedImage bufferedImg = SwingFXUtils.fromFXImage(snapshot, null);
try{
doc = new PDDocument();
page = new PDPage();
doc.addPage(page);
PDPageContentStream content = new PDPageContentStream(doc, page);
/* ByteArrayOutputStream out = new ByteArrayOutputStream();
InputStream in = new ByteArrayInputStream(out.toByteArray());*/
ximg = new PDJpeg(doc, bufferedImg);
//ximg = new PDJpeg(doc, in);
content.drawImage(ximg, 100, 100);
content.close();
doc.save("PDFWithText.pdf");
doc.close();
} catch (Exception e){
System.out.println(e);
}
当我使用InputStream和OutputStream类时,文档已创建,但是当我打开它时,Adobe和其他程序会给我一个错误,说Acrobat无法正确显示页面。当我使用BufferedImage类而不是在上面的代码中注释掉时,文档很简单,没有创建,我不知道为什么。
注
这是Taking a screenshot of a scene or a portion of a scene in JavaFx 2.2的后续问题,但特定于pdf创建部分而非节点快照部分。
答案 0 :(得分:4)
这不是一个真正的解决方案(因为提供的代码不起作用),而是我试图解决问题的文档
我花了一些时间试图解决这个问题而且无法做到。我已经为我的解决方案提供了示例代码,因为它提供了一个SSCCE和方法,可以帮助其他人尝试解决这个问题。
我尝试的两种方法是:
通过JavaFX方法SwingFXUtils.fromFXImage输出BufferedImage,并将其用作pdfbox api的输入。
这种方法不起作用,因为SwingFXUtils创建的BufferedImage使用与pdfbox api所需的SampleModel不兼容的SampleModel进行编码。
通过JavaFX方法SwingFXUtils.fromFXImage从BufferedImage输出中创建Jpeg流(使用ImageIO),并将其用作pdfbox api的输入。
这种方法不起作用。也许失败的原因是ImageIO从SwingFXUtils创建的BufferedImage创建了粉红色(即错误编码)的jpeg。这可能是ImageIO中的一个错误。另外,我可能会在使用pdfbox将结果图像添加到jpeg中时出现错误。
建议
还有许多用于从JavaFX创建PDF文件的api。我建议尝试使用任何其他api来执行创建pdf文件的任务(谷歌搜索会显示它们),而不是继续解决从JavaFX输出的图像与pdfbox的集成问题。
可执行示例代码
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.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.apache.pdfbox.exceptions.COSVisitorException;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.edit.PDPageContentStream;
import org.apache.pdfbox.pdmodel.graphics.xobject.PDJpeg;
import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectImage;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageOutputStream;
import java.awt.image.*;
import java.io.*;
import java.nio.file.Paths;
import java.util.Arrays;
// #### THIS CODE CURRENTLY DOES NOT FUNCTION CORRECTLY - SEE INLINE COMMENTS IN THE CODE TO UNDERSTAND WHY ####
//
// Demonstrates converting a JavaFX SceneGraph to a pdf (just as a bitmapped image, not as vector graphics).
// 1. creating a snapshot of a JavaFX node.
// 2. creating a pdf from the snapshot (using apache pdfbox http://pdfbox.apache.org/).
// 3. saving the pdf to a file.
// 4. opening the saved pdf in a web browser so the web browser can trigger showing
// the pdf in an appropriate pdf viewer (if the user has such a viewer installed).
public class PdfWithImageCreator extends Application {
// icon courtesy of http://www.aha-soft.com/ not for commercial use (free for non-commercial use).
private static final String imageURL =
"http://icons.iconarchive.com/icons/aha-soft/free-global-security/512/Global-Network-icon.png";
private static final String PDF_PATH =
Paths.get("exported.pdf").toAbsolutePath().toString();
@Override public void start(Stage stage) {
VBox layout = new VBox(20);
ImageView imageView = new ImageView(
new Image(imageURL)
);
Button export = new Button("Export");
export.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent actionEvent) {
exportToPDF(layout, PDF_PATH);
}
});
layout.setStyle("-fx-font-size: 20px;");
layout.setAlignment(Pos.CENTER);
layout.setPadding(new Insets(20));
layout.getChildren().setAll(
export,
imageView
);
stage.setScene(
new Scene(
new Group(
layout
)
)
);
stage.show();
}
/**
* Snapshots the provided node.
* Encodes the snapshot in a pdf.
* Saves the pdf to the provided file path.
* Opens the pdf in the default system web browser.
*
* @param node the node for which the snapshot is to be taken.
* @param filePath the path where the pdf is to be saved.
*/
private void exportToPDF(Node node, String filePath){
PDDocument doc = null;
PDPage page = null;
PDPageContentStream content = null;
PDXObjectImage ximage = null;
try {
// snapshot the node and convert it to an awt buffered image.
Image fxImage = node.snapshot(null, null);
BufferedImage bufferedImage = SwingFXUtils.fromFXImage(fxImage, null);
// create a pdf containing the snapshot image.
doc = new PDDocument();
page = new PDPage();
doc.addPage(page);
content = new PDPageContentStream(doc, page);
// alternate path A => try to create a PDJpeg from a jpegInputStream.
ximage = createPDJpegFromJpegStream(doc, bufferedImage);
// alternate path B => try to create a PDJpeg from directly from a BufferedImage directly.
// ximage = createPDJpegFromBufferedImage(doc, bufferedImage);
content.drawImage(ximage, 0, 0);
content.close();
// save the created image to disk.
doc.save(filePath);
System.out.println("Exported PDF to: " + filePath);
// show the generated pdf in a web browser.
// (if the browser is pdf enabled, this will display the pdf in the web browser).
getHostServices().showDocument(filePath);
} catch(IOException | COSVisitorException ie) {
ie.printStackTrace();
} finally {
try {
if (content != null) { content.close(); }
if (doc != null) { doc.close(); }
} catch (IOException e) {
e.printStackTrace();
}
}
}
// #### THIS METHOD DOES NOT FUNCTION AS EXPECTED
// alternate path => try to create a PDJpeg from a jpegInputStream.
// when using a jpeg stream this doesn't work, the created pdf is not well formed and
// you end up with adobe pdf reader running out of memory trying to read the resultant pdf.
// Also outputs a weird message that I currently don't understand =>
// INFO: About to return NULL from unhandled branch. filter = COSName{DCTDecode}
private PDXObjectImage createPDJpegFromJpegStream(PDDocument doc, BufferedImage bufferedImage) throws IOException {
// provide the buffered image data as input to a jpeg input stream.
ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
ImageOutputStream jpegImageOutputStream = ImageIO.createImageOutputStream(jpegOutputStream);
ImageIO.write(bufferedImage, "jpeg", jpegImageOutputStream);
InputStream jpegInputStream = new ByteArrayInputStream(
Arrays.copyOf(jpegOutputStream.toByteArray(), jpegOutputStream.size())
);
// output created jpg file for debugging purposes
// => when you view it is pink due to (I believe) an ImageIO bug.
// you can see how the resultant image is pink by opening the image file named in system.out in any image viewer.
// this improper encoding of the jpeg data may be why the subsequent use of it to generate a pdf
// will generate a an invalid pdf.
File file = new File("output.jpg");
FileOutputStream fos = new FileOutputStream(file);
fos.write(Arrays.copyOf(jpegOutputStream.toByteArray(), jpegOutputStream.size()));
System.out.println(file.getAbsolutePath());
return new PDJpeg(doc, jpegInputStream);
}
// #### THIS METHOD DOES NOT FUNCTION AS EXPECTED
// alternate path => try to create a PDJpeg from directly from a BufferedImage directly, get the following exception:
// Exception in thread "JavaFX Application Thread" java.lang.IllegalArgumentException: Raster IntegerInterleavedRaster: width = 552 height = 616 #Bands = 1 xOff = 0 yOff = 0 dataOffset[0] 0 is incompatible with ColorModel ColorModel: #pixelBits = 8 numComponents = 1 color space = java.awt.color.ICC_ColorSpace@125fe1b6 transparency = 1 has alpha = false isAlphaPre = false
// at java.awt.image.BufferedImage.<init>(BufferedImage.java:630)
// Browsing the awt PDJpeg and awt code it appears that the BufferedImage returned by JavaFX uses a
// SinglePixelPackedSampleModel, but PDJpeg required the buffered image to use a ComponentColorModel
// and the two are incompatible. So the bufferedimage needs to be re-encoded to a compatible
// raster format that utilizes a SampleModel (i.e. a ComponentColorModel) that is acceptable by PDJpeg.
//
private PDXObjectImage createPDJpegFromBufferedImage(PDDocument doc, BufferedImage bufferedImage) throws IOException {
return new PDJpeg(doc, bufferedImage);
}
public static void main(String[] args) { launch(args); }
}
答案 1 :(得分:1)
我通过将图像转换为PNG并将其首先写入输出流并将转换后的PNG从输出流读取到BufferedImage,将快照转换为PDF。
最终的BufferedImage在PDJpeg中呈现没有任何问题。
这是我的代码。
public static BufferedImage generate_png_from_container(Node node) {
SnapshotParameters param = new SnapshotParameters();
param.setDepthBuffer(true);
WritableImage snapshot = node.snapshot(param, null);
BufferedImage tempImg = SwingFXUtils.fromFXImage(snapshot, null);
BufferedImage img = null;
byte[] imageInByte;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(tempImg, "png", baos);
baos.flush();
imageInByte = baos.toByteArray();
baos.close();
InputStream in = new ByteArrayInputStream(imageInByte);
img = ImageIO.read(in);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//the final image sent to the PDJpeg
return img;
}