Introduction
我几年前必须面对的挑战之一是将Java桌面应用程序从32位迁移到64位架构,这可能就像使用正确的JDK一样简单,切换到适当的外部库版本并获得 摆脱不推荐使用的代码实际上还有一个额外的问题。 该应用程序在面板上嵌入了32位PDF阅读器,并且这种限制已经使这种迁移停止了一段时间。
我几年前必须面对的挑战之一是将Java桌面应用程序从32位迁移到64位架构,这可能就像使用正确的JDK一样简单,切换到适当的外部库版本并获得 摆脱不推荐使用的代码实际上还有一个额外的问题。 该应用程序在面板上嵌入了32位PDF阅读器,并且这种限制已经使这种迁移停止了一段时间。
在代码本身中,它使用的是XULRunner,这是Firefox引擎的组件之一,因此它实际上所做的是“使用嵌入式的32位Firefox浏览器”。 它还使用Adobe Acrobat生成的交互式表单,实际上需要正确呈现Adobe Acrobat Reader本身,这使Apache PDFBox脱颖而出以完成此任务。
当我研究运行嵌入式浏览器的原理时,我试图了解笔记本电脑中发生的情况:当我上网浏览并打开Firefox 32位浏览器的PDF文件时,我没有使用某些浏览器代码:我委托了此代码 在浏览器的Adobe Acrobat Reader X插件中。 那么,为什么我们不应该再进一步一步,通过集成使用相同的插件呢? Adobe本身提供了该系统,因此不会出现不兼容问题,我只需要通过操作系统配置进行设置即可获得解决方法,并为我们当前正在运行该程序的计算机体系结构获取正确的版本。
因此,让我们认识一下DJNativeSwing,这是一个基于SWT(标准小工具工具包)的库,它使我们能够在代码中拥有自己的嵌入式浏览器。 强烈建议使用SWT库,因为它具有可移植性,因为它可以访问本机操作系统GUI,而这正是我们为此类问题所需要的。 它也是Java Swing的下一个包装级别,并且实际上更轻,更快,并且我在一天中早就使用它来避免不得不处理Macromedia Flash插件集成时出现的问题。
作为如何配置此示例,我们将遵循2个基本步骤:
- 在Java swing组件上设置浏览器选项卡。能够在该浏览器标签上打开PDF。
Process
1. Previous set up: the Maven dependecies required
让我们从基础开始:首先,我们将获得Maven依赖项。
was这是最初撰写帖子时的稳定版本。
<dependecy>
<group>chrriis.dj.nativeswing</group>
<artifactId>DJNativeSwing</artifactId>
<version>1.0.2</version>
</dependecy>
2. Building a basic Swing interface
之后,我们将定义基本的窗口框架,并通过按钮选择文件(通过FileChooser)和显示结果的面板。
all将所有文本字符串设置为常量可以使它们更容易发现它们,以便在需要时替换它们。 确实不是必需的,但是这样做可以提高可重用性。
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.util.Map;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileNameExtensionFilter;
import nativeSwing.BrowserPanel;
import pdfHandler.PdfReader;
import xmlHandler.XMLHandler;
import chrriis.common.UIUtils;
import chrriis.dj.nativeswing.swtimpl.NativeInterface;
public class DemoPDFRenderLauncher {
private static final String TITLE = "PDF Renderer demo";
private static final String NO_OUTPUT_MESSAGE = "No output available";
private static final String NO_DATA_MESSAGE = "There is no data available from form";
private static final int LENGTH = 800;
private static final int WIDTH = 600;
private static final String FILTER_FILES "PDF files";
private static final String FILE_EXTENSION "pdf";
/**
* The main app window
*/
private JFrame window;
/**
* The path of the file we will open
*/
private String path;
/**
* Button for open file function
*/
private JButton buttonOpen;
/**
* A browser panel
*/
private BrowserPanel browserPanel;
/**
* Constructor method, creates the GUI
*/
public Launcher() {
window = new JFrame(TITLE);
window.getContentPane().setLayout(new BorderLayout());
window.setSize(LENGTH, WIDTH);
window.add(createButtonsPanel(), BorderLayout.NORTH);
window.add(createContentPanel(), BorderLayout.CENTER);
window.setVisible(true);
window.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
NativeInterface.close();
System.exit(0);
}
});
}
/**
* Creates a button panel with the action button: open a file
* @return the buttons panel
*/
private Component createButtonsPanel() {
JPanel panel = new JPanel();
panel.setLayout(new FlowLayout());
buttonOpen = new JButton("Open file");
buttonOpen.addActionListener(new ButtonOpenController());
panel.add(buttonOpen);
return panel;
}
/**
* Creates a panel to render the content
*
* @return the buttons panel
*/
private Component createContentPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
JScrollPane scrollPaneText = new JScrollPane(textPanel);
panel.add(scrollPaneText);
//here we will insert the DJNativeSwing panle
browserPanel = new BrowserPanel();
JScrollPane scrollPaneBrowser = new JScrollPane(browserPanel);
panel.add(scrollPaneBrowser);
return panel;
}
/**
* Load button controller, which launches the FileChooser.
*/
private class ButtonOpenController implements ActionListener {
@Override
public void actionPerformed(ActionEvent arg0) {
launchOpenSelectFile();
}
}
/**
* Launches the FileChooser window, and invokes the pdf opener window.
*/
private void launchOpenSelectFile() {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setAcceptAllFileFilterUsed(false);
FileNameExtensionFilter filter = new FileNameExtensionFilter(
FILTER_FILES, FILE_EXTENSION);
fileChooser.addChoosableFileFilter(filter);
if (fileChooser.showOpenDialog(window) == JFileChooser.APPROVE_OPTION) {
path = fileChooser.getSelectedFile().getAbsolutePath();
browserPanel.navigate(path);
}
}
@SuppressWarnings("unused")
public static void main(String[] args) {
UIUtils.setPreferredLookAndFeel();
NativeInterface.open();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Launcher demo = new Launcher();
}
});
}
}
3. Embeding a web browser
然后,让我们继续浏览器标签:如果我们只想创建一个浏览器面板,就像编写以下几行一样简单:
import java.awt.BorderLayout;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
import chrriis.dj.nativeswing.swtimpl.components.JWebBrowser;
public class BasicBrowserPanel extends JPanel {
private static final String TITLE = "";
/**
* The browser will be handled in this specific component
*/
private JWebBrowser webBrowser;
/**
* Constructor
*/
public BrowserPanel() {
super(new BorderLayout());
JPanel webBrowserPanel = new JPanel(new BorderLayout());
webBrowserPanel.setBorder(BorderFactory.createTitledBorder(TITLE));
webBrowser = new JWebBrowser();
webBrowser.setBarsVisible(false);
webBrowser.setStatusBarVisible(false);
webBrowserPanel.add(webBrowser, BorderLayout.CENTER);
add(webBrowserPanel, BorderLayout.CENTER);
}
/**
* Initializes the browser and sets a value in the URL storage
* @param path the URL value o file path to open
*/
public void navigate(String path) {
webBrowser.setVisible(true);
webBrowser.navigate(path);
}
/**
* Makes the browser retrieve and render the content from the path previously stored
*/
public String getAddress(){
return webBrowser.getHTMLContent();
}
/**
* Hides the browser controls (forward, back, home buttons...)
*/
public void hideContent() {
webBrowser.setVisible(false);
}
}
这样,我们将获得一个功能齐全的Web浏览器,与Eclipse IDE中的浏览器非常相似,但是在此处尝试执行的操作具有太多不必要的功能。 由于只通过在Adobe上委派渲染过程来进行渲染,因此我们可以从整个自定义浏览系统中删除GUI多余的元素,并保留裸面板。
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import chrriis.dj.nativeswing.swtimpl.NativeInterface;
import chrriis.dj.nativeswing.swtimpl.components.JWebBrowser;
/**
* Allows to launch a JFrame containing an embedded browser.
*/
public class BrowserFrameLauncher {
/**
* Renders the file content on a browser via DJ Native Swing
*
* @param path
* the file url if we pass as a parameter any webpage URL,
* the system would try to render it
* @return a JPanel with a native browser, to render the file content
*/
private Component createBrowserPanel(String path) {
JWebBrowser.useXULRunnerRuntime();
JPanel fileBrowserPanel = new JPanel(new BorderLayout());
final JWebBrowser fileBrowser = new JWebBrowser();
fileBrowser.setBarsVisible(false);
fileBrowser.setStatusBarVisible(false);
fileBrowser.navigate(path);
fileBrowserPanel.add(fileBrowser, BorderLayout.CENTER);
return fileBrowserPanel;
}
}
4. Setting the last piece of the puzzle: getting the PDF itself
最后,我们来作最后的修改以打开PDF。 实际上,我们正在做的是将PDF文件路径输入浏览器,因此最终我们在旧朋友XULRunner上有了新的层,但这为我们提供了一种通过“正确的架构版本” SWT库集成插件的方法。 因此,作为结论,我们可以连接到“正确的体系结构版本”插件,解决我们的渲染问题,并使我们永久脱离32位平台。
// excerpt from BrowserFrameLauncherPDF.java
private static final String CLOSING_MESSAGE = "Do you really want to close the file?";
private static final String RENDERED_TITLE = "PDF Renderer demo - Embed Browser";
/**
* Opens a file and shows it content in a JFrame.
*
* @param path
* the url of the file to open in a JFrame
*/
public static void openPDF(final String path) {
NativeInterface.open();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
final JFrame frame = new JFrame(RENDERED_TITLE);
frame.setLocation(0, 0);
//we may set up a default size for this test
//frame.setSize(800, 600);
frame.setVisible(true);
frame.add(createBrowserPanel(path));
//a window listener would allow us to control the closing actions
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
int i = JOptionPane.showConfirmDialog(frame,CLOSING_MESSAGE);
if (i == 0) {
NativeInterface.close();
}
}
});
}
});
}
Please️请注意NativeInterface.open()行,以确保正确启动组件以及对该组件的线程化,以避免其他进程干扰渲染。