2013-08-04 51 views
1

我已經使用Javafx和PDFbox從Apache將文本放置到PDF文件中。現在,我正在嘗試將拍攝場景的屏幕截圖放置到PDF文檔中。使用JavaFX在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

這可能是這個錯誤:http://issues.apache.org/jira/browse/PDFBOX-2026將固定在1.8.5。或者下載快照。 –

回答

4

這不是一個實際的解決方案(因爲提供的代碼不工作),相反,它是我試圖解決

我花了一些時間,試圖解決這一點,無法對問題的文件至。我已經包含了我的解決方案的示例代碼,因爲它提供了一個SSCCE和方法,可以幫助其他人試圖解決這個問題。

我嘗試這兩種方法是:

  1. 由JavaFX的方法SwingFXUtils.fromFXImage以一個BufferedImage輸出,並將它作爲輸入到PDFBOX API。

    此方法無效,因爲由SwingFXUtils創建的BufferedImage使用與pdfbox api所需的SampleModel不兼容的SampleModel進行編碼。

  2. 從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); } 
} 
+1

我最近遇到了ImageIO jpg錯誤,其中的東西有紫色色調。這裏有更多的信息(需要帳戶)https://javafx-jira.kenai.com/browse/RT-23502。如果PDF庫支持另一種圖像格式,則可以嘗試。在我的項目中,我切換到PNG,圖像看起來很好。但我沒有處理PDF的... –

+0

是的PNG確實看起來更好,但我使用的PDF庫似乎並不支持它。我設法弄清楚了紫色的問題。 – Morelka

1

我的快照到PDF從OutputStream的圖像轉換成PNG和第一它寫入到一個OutputStream和讀轉換PNG使用BufferedImage。

最終的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; 
}