Рубрики
Без рубрики

Интеграция браузера в Java GUI

Введение Одной из проблем, с которой мне пришлось столкнуться пару лет назад, была миграция aJa… С тегами java, интеграция, браузер, swt.

Одна из проблем, с которой мне пришлось столкнуться пару лет назад, заключалась в переносе настольного приложения Java с 32-разрядной на 64-разрядную архитектуру, и то, что могло быть таким простым, как использование правильного JDK, переключение на соответствующие версии внешних библиотек и избавление от устаревшего кода, на самом деле имело дополнительную проблему. Приложение встроило 32-разрядный PDF-ридер на панель, и это ограничение некоторое время останавливало эту миграцию.

Одна из проблем, с которой мне пришлось столкнуться пару лет назад, заключалась в переносе настольного приложения Java с 32-разрядной на 64-разрядную архитектуру, и то, что могло быть таким простым, как использование правильного JDK, переключение на соответствующие версии внешних библиотек и избавление от устаревшего кода, на самом деле имело дополнительную проблему. Приложение встроило 32-разрядный PDF-ридер на панель, и это ограничение некоторое время останавливало эту миграцию.

Переходя к самому коду, он использовал XULRunner, один из компонентов движка Firefox, так что на самом деле он делал “с использованием встроенного 32-разрядного браузера Firefox”. Он также использовал интерактивные формы, созданные Adobe Acrobat, которые фактически требовали правильного отображения самого Adobe Acrobat Reader, и для выполнения этой задачи Apache PDFBox не использовался.

Когда я изучил философию запуска встроенного браузера, я попытался использовать то, что произошло на моем ноутбуке: когда я занимался серфингом в Сети и открывал PDF-файл в своем 32-разрядном браузере Firefox, я не использовал какой-либо код браузера: я делегировал это в плагин Adobe Acrobat Reader X для браузера. Итак, почему бы нам не продвинуть эту идею еще на один шаг и не использовать тот же плагин через интеграцию? Сама Adobe предоставляет эту систему, поэтому проблем с несовместимостью не возникнет, мне просто нужно было настроить это с помощью конфигурации операционной системы, чтобы найти обходной путь и получить подходящую версию для архитектуры компьютера, в которой мы в настоящее время запускаем программу.

Итак, давайте познакомимся с DJNativeSwing, библиотекой, основанной на SWT (Standard Widget Toolkit), которая позволяет нам иметь наш собственный встроенный браузер в нашем коде. SWT – это настоятельно рекомендуемая библиотека из-за ее переносимости, поскольку она имеет доступ к графическим интерфейсам собственной операционной системы, и это именно то, что нам требуется для такого рода проблем. Это также следующий уровень упаковки для Java Swing, и на самом деле он легче и быстрее, и я уже использовал его в свое время, чтобы избежать проблем, когда мне приходилось иметь дело с интеграцией плагинов Macromedia Flash.

В качестве примера того, как это настроить, мы собираемся выполнить 2 основных шага:

  • Настройте вкладку браузера в компоненте Java swing.
  • Иметь возможность открывать PDF-файл на указанной вкладке браузера.

1. Предыдущая настройка: требуемые зависимости Maven

Давайте начнем с основ: прежде всего мы получим зависимости maven.

❕ Это была стабильная версия, когда сообщение было первоначально написано.


  chrriis.dj.nativeswing
  DJNativeSwing
  1.0.2

2. Создание базового интерфейса Swing

Сразу после этого мы определим базовую рамку окна с кнопкой выбора файла (через FileChooser) и панелью для отображения результатов.

❕ Настройка всех текстовых строк в качестве констант облегчает их поиск, чтобы при необходимости заменить. На самом деле в этом нет необходимости, но это улучшает возможность повторного использования.

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. Встраивание веб-браузера

Затем давайте перейдем к вкладке браузера: если бы мы просто хотели создать панель браузера, это было бы так же просто, как написать следующие строки:

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

Таким образом, мы получаем полнофункциональный веб-браузер, очень похожий на браузер в Eclipse IDE, но со слишком большим количеством ненужных функций для того, что мы пытаемся здесь сделать. Поскольку мы выполняем процесс рендеринга только путем делегирования его в Adobe, мы можем удалить дополнительные элементы графического интерфейса из всей этой пользовательской системы просмотра и оставить пустую панель.

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. Установка последнего фрагмента головоломки: получение самого PDF-файла

Наконец, давайте внесем последние штрихи, чтобы открыть PDF-файл. Что мы на самом деле делаем, так это вводим путь к файлу PDF в браузер, так что в итоге у нас есть новый слой поверх нашего старого друга XULRunner, но это дает нам возможность интегрировать плагины с помощью библиотеки SWT “right architecture version”. Итак, в заключение мы можем подключиться к плагину “правильная версия архитектуры”, устраняя нашу проблему с рендерингом и делая нас независимыми от 32-битной plaform раз и навсегда.

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

❗ ️ Пожалуйста, обратите внимание на строку NativeInterface.open(), чтобы убедиться в правильной загрузке компонентов, и потоковую обработку этого компонента, чтобы избежать вмешательства других процессов в рендеринг.

Оригинал: “https://dev.to/angelesbroullon/browser-integration-in-java-guis-2nnm”