在 JavaFX 中显示 pdf

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18207116/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 23:19:32  来源:igfitidea点击:

Displaying pdf in JavaFX

javajavafx

提问by Bilbo Baggins

Developing a desktop application in JavaFXwhich requires to display a pdf. I read that there is no support for pdf viewing/displaying in JavaFX(current version), I read about JPedaltoo.

开发JavaFX需要显示 pdf的桌面应用程序。我读到JavaFX(当前版本)不支持 pdf 查看/显示,我也读过JPedal

Now, questions:

现在,问题:

  1. Is there any external component or library to view pdf in JavaFX? It should be a freeware.
  2. (If I have to use JPedal) How can I embed it in my application.
  1. 是否有任何外部组件或库可以在 JavaFX 中查看 pdf?它应该是一个免费软件。
  2. (如果我必须使用JPedal)如何将它嵌入到我的应用程序中。

采纳答案by jewelsea

JPedalFX Sample Code and Usage

JPedalFX 示例代码和用法

Sample code on using JPedalFX is provided with the JPedalFX download.

JPedalFX 下载提供了使用 JPedalFX 的示例代码。

Kind of lame on my part, but I'll just paste snippets sample code here that have been copied from the sample viewer provided with the JPedalFX library. The code relies on the jpedal_lgpl.jar file included with the JPedalFX distribution being on the classpath (or the library path referenced in the manifest of your application jar).

对我来说有点蹩脚,但我只会在此处粘贴从 JPedalFX 库提供的示例查看器中复制的示例代码片段。该代码依赖于 JPedalFX 发行版中包含的 jpedal_lgpl.jar 文件,该文件位于类路径(或应用程序 jar 清单中引用的库路径)上。

Should you have further questions regarding usage of JPedalFX, I suggest you contact IDR solutions directly(they have been responsive to me in the past).

如果您对 JPedalFX 的使用还有其他疑问,我建议您直接联系 IDR 解决方案(他们过去曾对我做出回应)。

// get file path.
FileChooser fc = new FileChooser();
fc.setTitle("Open PDF file...");
fc.getExtensionFilters().add(new FileChooser.ExtensionFilter("PDF Files", "*.pdf"));     
File f = fc.showOpenDialog(stage.getOwner());
String filename = file.getAbsolutePath();

// open file.
PdfDecoder pdf = new PdfDecoder();
pdf.openPdfFile(filename);
showPage(1);
pdf.closePdfFile();

. . . 

/**
 * Update the GUI to show a specified page.
 * @param page 
 */
private void showPage(int page) {

    //Check in range
    if (page > pdf.getPageCount())
        return;
    if (page < 1)
        return;

    //Store
    pageNumber = page;


    //Show/hide buttons as neccessary
    if (page == pdf.getPageCount())
        next.setVisible(false);
    else
        next.setVisible(true);

    if (page == 1)
        back.setVisible(false);
    else
        back.setVisible(true);


    //Calculate scale
    int pW = pdf.getPdfPageData().getCropBoxWidth(page);
    int pH = pdf.getPdfPageData().getCropBoxHeight(page);

    Dimension s = Toolkit.getDefaultToolkit().getScreenSize();

    s.width -= 100;
    s.height -= 100;

    double xScale = (double)s.width / pW;
    double yScale = (double)s.height / pH;
    double scale = xScale < yScale ? xScale : yScale;

    //Work out target size
    pW *= scale;
    pH *= scale;

    //Get image and set
    Image i = getPageAsImage(page,pW,pH);
    imageView.setImage(i);

    //Set size of components
    imageView.setFitWidth(pW);
    imageView.setFitHeight(pH);
    stage.setWidth(imageView.getFitWidth()+2);
    stage.setHeight(imageView.getFitHeight()+2);
    stage.centerOnScreen();
}

/**
 * Wrapper for usual method since JFX has no BufferedImage support.
 * @param page
 * @param width
 * @param height
 * @return 
 */
private Image getPageAsImage(int page, int width, int height) {

    BufferedImage img;
    try {
        img = pdf.getPageAsImage(page);

        //Use deprecated method since there's no real alternative 
        //(for JavaFX 2.2+ can use SwingFXUtils instead).
        if (Image.impl_isExternalFormatSupported(BufferedImage.class))
            return javafx.scene.image.Image.impl_fromExternalImage(img);

    } catch(Exception e) {
        e.printStackTrace();
    }

    return null;
}

/**
 * ===========================================
 * Java Pdf Extraction Decoding Access Library
 * ===========================================
 *
 * Project Info:  http://www.jpedal.org
 * (C) Copyright 1997-2008, IDRsolutions and Contributors.
 *
 *  This file is part of JPedal
 *
    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


 *
 * ---------------
 * JPedalFX.java
 * ---------------
 */

SwingLabs PDF Renderer

SwingLabs PDF 渲染器

Additionaly, I used an old SwingLabs Swing based pdf renderer with JavaFX in the past for rendering pdf's for my JavaFX web browser. Although the Swing/JavaFX integration wasn't a supported feature of JavaFX at the time that I developed the browser, it still worked fine for me. Code for integration is in PDFViewer.javaand BrowserWindow.java.

另外,我过去使用了旧的基于 SwingLabs Swing 的 pdf 渲染器和 JavaFX 来为我的JavaFX 网络浏览器渲染 pdf 。虽然在我开发浏览器时,Swing/JavaFX 集成不是 JavaFX 支持的功能,但它对我来说仍然很好。集成代码位于PDFViewer.javaBrowserWindow.java 中

Note that embedding JavaFX in a Swing appis supported in Java 2.2 and embedding a Swing app in JavaFXis supported in Java 8.

需要注意的是在Swing应用程序中嵌入的JavaFX中的Java 2.2的支持和嵌入JavaFX的一个Swing应用程序中的Java 8支持。

回答by Moslem Cherif

package de.vogella.itext.write;

import java.io.FileOutputStream;
import java.util.Date;

import com.itextpdf.text.Anchor;
import com.itextpdf.text.BadElementException;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Chapter;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.List;
import com.itextpdf.text.ListItem;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.Section;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;


public class FirstPdf {
  private static String FILE = "c:/temp/FirstPdf.pdf";
  private static Font catFont = new Font(Font.FontFamily.TIMES_ROMAN, 18,
      Font.BOLD);
  private static Font redFont = new Font(Font.FontFamily.TIMES_ROMAN, 12,
      Font.NORMAL, BaseColor.RED);
  private static Font subFont = new Font(Font.FontFamily.TIMES_ROMAN, 16,
      Font.BOLD);
  private static Font smallBold = new Font(Font.FontFamily.TIMES_ROMAN, 12,
      Font.BOLD);

  public static void main(String[] args) {
    try {
      Document document = new Document();
      PdfWriter.getInstance(document, new FileOutputStream(FILE));
      document.open();
      addMetaData(document);
      addTitlePage(document);
      addContent(document);
      document.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  // iText allows to add metadata to the PDF which can be viewed in your Adobe
  // Reader
  // under File -> Properties
  private static void addMetaData(Document document) {
    document.addTitle("My first PDF");
    document.addSubject("Using iText");
    document.addKeywords("Java, PDF, iText");
    document.addAuthor("Lars Vogel");
    document.addCreator("Lars Vogel");
  }

  private static void addTitlePage(Document document)
      throws DocumentException {
    Paragraph preface = new Paragraph();
    // We add one empty line
    addEmptyLine(preface, 1);
    // Lets write a big header
    preface.add(new Paragraph("Title of the document", catFont));

    addEmptyLine(preface, 1);
    // Will create: Report generated by: _name, _date
    preface.add(new Paragraph("Report generated by: " + System.getProperty("user.name") + ", " + new Date(), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
        smallBold));
    addEmptyLine(preface, 3);
    preface.add(new Paragraph("This document describes something which is very important ",
        smallBold));

    addEmptyLine(preface, 8);

    preface.add(new Paragraph("This document is a preliminary version and not subject to your license agreement or any other agreement with vogella.com ;-).",
        redFont));

    document.add(preface);
    // Start a new page
    document.newPage();
  }

  private static void addContent(Document document) throws DocumentException {
    Anchor anchor = new Anchor("First Chapter", catFont);
    anchor.setName("First Chapter");

    // Second parameter is the number of the chapter
    Chapter catPart = new Chapter(new Paragraph(anchor), 1);

    Paragraph subPara = new Paragraph("Subcategory 1", subFont);
    Section subCatPart = catPart.addSection(subPara);
    subCatPart.add(new Paragraph("Hello"));

    subPara = new Paragraph("Subcategory 2", subFont);
    subCatPart = catPart.addSection(subPara);
    subCatPart.add(new Paragraph("Paragraph 1"));
    subCatPart.add(new Paragraph("Paragraph 2"));
    subCatPart.add(new Paragraph("Paragraph 3"));

    // add a list
    createList(subCatPart);
    Paragraph paragraph = new Paragraph();
    addEmptyLine(paragraph, 5);
    subCatPart.add(paragraph);

    // add a table
    createTable(subCatPart);

    // now add all this to the document
    document.add(catPart);

    // Next section
    anchor = new Anchor("Second Chapter", catFont);
    anchor.setName("Second Chapter");

    // Second parameter is the number of the chapter
    catPart = new Chapter(new Paragraph(anchor), 1);

    subPara = new Paragraph("Subcategory", subFont);
    subCatPart = catPart.addSection(subPara);
    subCatPart.add(new Paragraph("This is a very important message"));

    // now add all this to the document
    document.add(catPart);

  }

  private static void createTable(Section subCatPart)
      throws BadElementException {
    PdfPTable table = new PdfPTable(3);

    // t.setBorderColor(BaseColor.GRAY);
    // t.setPadding(4);
    // t.setSpacing(4);
    // t.setBorderWidth(1);

    PdfPCell c1 = new PdfPCell(new Phrase("Table Header 1"));
    c1.setHorizontalAlignment(Element.ALIGN_CENTER);
    table.addCell(c1);

    c1 = new PdfPCell(new Phrase("Table Header 2"));
    c1.setHorizontalAlignment(Element.ALIGN_CENTER);
    table.addCell(c1);

    c1 = new PdfPCell(new Phrase("Table Header 3"));
    c1.setHorizontalAlignment(Element.ALIGN_CENTER);
    table.addCell(c1);
    table.setHeaderRows(1);

    table.addCell("1.0");
    table.addCell("1.1");
    table.addCell("1.2");
    table.addCell("2.1");
    table.addCell("2.2");
    table.addCell("2.3");

    subCatPart.add(table);

  }

  private static void createList(Section subCatPart) {
    List list = new List(true, false, 10);
    list.add(new ListItem("First point"));
    list.add(new ListItem("Second point"));
    list.add(new ListItem("Third point"));
    subCatPart.add(list);
  }

  private static void addEmptyLine(Paragraph paragraph, int number) {
    for (int i = 0; i < number; i++) {
      paragraph.add(new Paragraph(" "));
    }
  }
} 

回答by ALabrosk

I suggest using PDF JSjavascript library.

我建议使用PDF JSjavascript 库。

Create a WebView and load statically the html/javascript content of this javascript pdf viewer example project. Create a function in javascript to which you can send the pdf byte array to be displayed.

创建一个 WebView 并静态加载此javascript pdf 查看器示例项目的 html/javascript 内容。在 javascript 中创建一个函数,您可以将要显示的 pdf 字节数组发送到该函数。

This way all the logic of the pdf viewer is already there. You can even modify the viewers html to remove some features over there.

这样pdf查看器的所有逻辑都已经存在。您甚至可以修改查看器 html 以删除那里的某些功能。

Also be careful about JPedalFXas I found it not reliable in cases where it had to render images that were added to the pdf file. In my case JPedalFX could not render a chart image that was generated with jfreechart

还要注意JPedalFX,因为我发现它在必须渲染添加到 pdf 文件的图像的情况下不可靠。在我的情况下,JPedalFX 无法呈现使用 jfreechart 生成的图表图像

回答by enzo

Ok, here is my 50 cents. In addition to @ALabrosik and @ReneEnriquez answers.

好的,这是我的 50 美分。除了@ALabrosik 和@ReneEnriquez 的答案。

Download pdf.js distand place it under src/main/resources

下载pdf.js dist并将其放在src/main/resources

├── pom.xml
├── src
│?? └── main
│??     ├── java
│??     │?? └── me
│??     │??     └── example
│??     │??         ├── JSLogListener.java
│??     │??         ├── Launcher.java
│??     │??         └── WebController.java
│??     └── resources
│??         ├── build
│??         │?? ├── pdf.js
│??         │?? └── pdf.worker.js
│??         ├── main.fxml
│??         ├── web
│??         │?? ├── cmaps
│??         │?? ├── compatibility.js
│??         │?? ├── debugger.js
│??         │?? ├── images
│??         │?? ├── l10n.js
│??         │?? ├── locale
│??         │?? ├── viewer.css
│??         │?? ├── viewer.html
│??         │?? └── viewer.js

Create the following fxml file (you should wrap WebViewin TabPaneor similar container to avoid problems with scrolling support)

创建以下 fxml 文件(您应该将WebView包装在TabPane或类似容器中以避免滚动支持出现问题)

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.web.WebView?>

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="576.0" prefWidth="1024.0" xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1" fx:controller="me.example.WebController">
    <center>
      <TabPane>
         <tabs>
            <Tab text="PDF test">
               <content>
                    <WebView fx:id="web" minHeight="-1.0" minWidth="-1.0" />
               </content>
            </Tab>
         </tabs>
      </TabPane>
    </center>
   <bottom>
      <Button fx:id="btn" mnemonicParsing="false" text="Open another file" BorderPane.alignment="CENTER" />
   </bottom>
</BorderPane>

To prevent pdf.js from opening demo pdf file on startup, open web/viewer.jsand clear DEFAULT_URLvalue.

为了防止 pdf.js 在启动时打开演示 pdf 文件,请打开web/viewer.js并清除DEFAULT_URL值。

var DEFAULT_URL = '';

Open web/viewer.htmland add script block:

打开web/viewer.html并添加脚本块:

<head>

<!-- ... -->
<script src="viewer.js"></script>

<!-- CUSTOM BLOCK -->
<script>
    var openFileFromBase64 = function(data) {
        var arr = base64ToArrayBuffer(data);
        console.log(arr);
        PDFViewerApplication.open(arr);
    }

    function base64ToArrayBuffer(base64) {
      var binary_string = window.atob(base64);
      var len = binary_string.length;
      var bytes = new Uint8Array( len );
      for (var i = 0; i < len; i++)        {
          bytes[i] = binary_string.charCodeAt(i);
      }
      return bytes.buffer;
    }
</script>
<!-- end of CUSTOM BLOCK -->

</head>

Now the controller (see code comments for explanation).

现在是控制器(有关解释,请参阅代码注释)。

 public class WebController implements Initializable {

    @FXML
    private WebView web;

    @FXML
    private Button btn;

    public void initialize(URL location, ResourceBundle resources) {
        WebEngine engine = web.getEngine();
        String url = getClass().getResource("/web/viewer.html").toExternalForm();

        // connect CSS styles to customize pdf.js appearance
        engine.setUserStyleSheetLocation(getClass().getResource("/web.css").toExternalForm());

        engine.setJavaScriptEnabled(true);
        engine.load(url);

        engine.getLoadWorker()
                .stateProperty()
                .addListener((observable, oldValue, newValue) -> {
                    // to debug JS code by showing console.log() calls in IDE console
                    JSObject window = (JSObject) engine.executeScript("window");
                    window.setMember("java", new JSLogListener());
                    engine.executeScript("console.log = function(message){ java.log(message); };");

                    // this pdf file will be opened on application startup
                    if (newValue == Worker.State.SUCCEEDED) {
                        try {
                            // readFileToByteArray() comes from commons-io library
                            byte[] data = FileUtils.readFileToByteArray(new File("/path/to/file"));
                            String base64 = Base64.getEncoder().encodeToString(data);
                            // call JS function from Java code
                            engine.executeScript("openFileFromBase64('" + base64 + "')");
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });

        // this file will be opened on button click
        btn.setOnAction(actionEvent -> {
            try {
                byte[] data = FileUtils.readFileToByteArray(new File("/path/to/another/file"));
                String base64 = Base64.getEncoder().encodeToString(data);
                engine.executeScript("openFileFromBase64('" + base64 + "')");
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }
}

Some of pdf.js functions will not work: open file (cause pdf.js have no access to URL outside JAR), printing etc. To hide corresponding toolbar buttons you can add the following lines to web.css:

一些 pdf.js 功能将不起作用:打开文件(因为 pdf.js 无法访问 JAR 之外的 URL)、打印等。要隐藏相应的工具栏按钮,您可以将以下几行添加到 web.css:

#toolbarViewerRight {
    display:none;
}

That's all. The rest of the code is trivial.

就这样。其余的代码是微不足道的。

public class JSLogListener {

    public void log(String text) {
        System.out.println(text);
    }
}

public class Launcher extends Application {

    public static void main(String[] args) {
        Application.launch();
    }

    public void start(Stage primaryStage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("/main.fxml"));
        primaryStage.setTitle("PDF test app");
        primaryStage.setScene(new Scene(root, 1280, 576));
        primaryStage.show();
    }
}

Hope this helps to someone.

希望这对某人有帮助。