這不是一個實際的解決方案(因爲提供的代碼不工作),相反,它是我試圖解決
我花了一些時間,試圖解決這一點,無法對問題的文件至。我已經包含了我的解決方案的示例代碼,因爲它提供了一個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中可能存在一些錯誤。
建議
還有許多其他的API從JavaFX的創建PDF文件。與其繼續排除將JavaFX輸出的圖像與pdfbox整合爲一體,我建議嘗試使用其他apis來執行創建pdf文件的任務(谷歌搜索將顯示它們)。
可執行示例代碼
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 = [email protected] 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); }
}
這可能是這個錯誤:http://issues.apache.org/jira/browse/PDFBOX-2026將固定在1.8.5。或者下載快照。 –