使用JavaFX将图像放在PDF文件上 [英] Place an image on a PDF file using JavaFX

查看:161
本文介绍了使用JavaFX将图像放在PDF文件上的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经使用来自Apache的Javafx和PDFbox将文本放入pdf文件中。现在我试图将拍摄场景的屏幕截图放到pdf文档中。

I've manged to place text into a pdf file using Javafx and PDFbox from Apache. Now I am trying to place an image from taking a screen shot of a scene, to the pdf document.

    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类而不是在上面的代码中注释掉时,文档很简单,没有创建,我不知道原因。

When I use the InputStream and OutputStream classes, the document is created but when I open it, Adobe along with other programs gives me an error saying Acrobat cannot display page correctly. When I use the BufferedImage class instead that is commented out in the above code, the document is simple not created and I don't know why.

注意

这是在JavaFx 2.2中截取场景或场景的一部分,但具体到pdf创建部分而不是节点快照部分。

This is a follow-up question to Taking a screenshot of a scene or a portion of a scene in JavaFx 2.2, but specific to the pdf creation portion rather than the node snapshot portion.

推荐答案

这不是一个实际的解决方案(因为代码提供不起作用),而是它是我试图解决问题的文件

我花了一些时间试图解决这个问题而且无法做到。我已经为我的解决方案提供了示例代码,因为它提供了一个SSCCE和方法,可以帮助其他人尝试解决这个问题。

I spent some time trying to solve this and was unable to. I have included the sample code for my solution as it provides an SSCCE and approach which may help other people trying to solve this problem.

我尝试的两种方法是:


  1. 通过JavaFX方法SwingFXUtils.fromFXImage获取BufferedImage输出并将其用作pdfbox api的输入。

  1. Taking a BufferedImage output by the JavaFX method SwingFXUtils.fromFXImage and using that as input to the pdfbox api.

这种方法不起作用,因为SwingFXUtils创建的BufferedImage使用与pdfbox api所要求的SampleModel不兼容的SampleModel进行编码。

This approach did not work because the BufferedImage created by SwingFXUtils is encoded with a SampleModel that is incompatible with that required by the pdfbox api.

通过JavaFX方法SwingFXUtils.fromFXImage从BufferedImage输出中创建一个Jpeg流(使用ImageIO),并将其用作pdfbox api的输入。

Creating a Jpeg stream (using ImageIO) out of the BufferedImage output by the JavaFX method SwingFXUtils.fromFXImage and using that as input to the pdfbox api.

这种方法不起作用。也许失败的原因是ImageIO从SwingFXUtils创建的BufferedImage创建了粉红色(即错误编码)的jpeg。这可能是ImageIO中的一个错误。此外,我可能会在使用pdfbox将结果图像添加到jpeg中时出现错误。

This approach did not work. Perhaps the reason for the failure was that ImageIO creates pink tinted (i.e. incorrectly encoded) jpegs from the BufferedImage created by SwingFXUtils. This may a bug in ImageIO. Additionally there may be bugs in how I am adding the result image to the jpeg using pdfbox.

推荐

还有许多用于从JavaFX创建PDF文件的api。我建议尝试使用任何其他api来执行创建pdf文件的任务(谷歌搜索会显示它们),而不是继续解决JavaFX输出的图像与pdfbox的集成问题。

There are numerous other apis for creating PDF files from JavaFX. Rather than continuing to troubleshoot integration of images output from JavaFX with pdfbox, I suggest trying any of the other apis to perform the task of creating a pdf file (a google search will reveal them).

可执行示例代码

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); }
}

这篇关于使用JavaFX将图像放在PDF文件上的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆