在JavaFX中显示pdf

Bil*_*ins 18 java javafx

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

现在,问题:

  1. 是否有任何外部组件或库可以在JavaFX中查看pdf?它应该是一个免费软件.
  2. (如果我必须使用JPedal)如何将其嵌入我的应用程序中.

jew*_*sea 12

JPedalFX示例代码和用法

使用JPedalFX的示例代码随JPedalFX下载一起提供.

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

如果您对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
 * ---------------
 */
Run Code Online (Sandbox Code Playgroud)

SwingLabs PDF渲染器

另外,我过去使用旧的基于SwingLabs Swing的pdf渲染器和JavaFX来渲染我的JavaFX Web浏览器的 pdf .虽然Swing/JavaFX集成在我开发浏览器时并不是JavaFX的支持功能,但它仍然适用于我.集成代码在PDFViewer.javaBrowserWindow.java中.

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

  • JPedalFX现在不是免费的:/ (6认同)

ALa*_*osk 11

我建议使用PDF JS javascript库.

创建一个WebView并静态加载此javascript pdf viewer示例项目的html/javascript内容.在javascript中创建一个函数,您可以向其发送要显示的pdf字节数组.

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

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

  • 您可以在答案中看到解决方案[波纹管](/sf/answers/2942824111/)无论如何,老实说,我们最终使用了[PDFBox](https://pdfbox.apache.org /)。客户想要向左 &lt;-&gt; 向右滑动来更改页面,因此结果证明操作 pdf.js 有点困难。制作我们自己的定制 pdf 阅读器对我们和我们的客户都有效:) (2认同)

enz*_*nzo 9

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

下载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
Run Code Online (Sandbox Code Playgroud)

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

<?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>
Run Code Online (Sandbox Code Playgroud)

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

var DEFAULT_URL = '';
Run Code Online (Sandbox Code Playgroud)

打开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>
Run Code Online (Sandbox Code Playgroud)

现在是控制器(参见代码注释以获得解释).

 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();
            }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

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

#toolbarViewerRight {
    display:none;
}
Run Code Online (Sandbox Code Playgroud)

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

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

希望这对某人有帮助.