org.icepdf.ri.common.utility.signatures.SignaturesPanel Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of icepdf-viewer Show documentation
Show all versions of icepdf-viewer Show documentation
ICEpdf PDF Viewer Reference Implementation (RI)
/*
* Copyright 2006-2017 ICEsoft Technologies Canada Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS
* IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package org.icepdf.ri.common.utility.signatures;
import org.icepdf.core.pobjects.Document;
import org.icepdf.core.pobjects.Reference;
import org.icepdf.core.pobjects.acroform.InteractiveForm;
import org.icepdf.core.pobjects.acroform.SignatureDictionary;
import org.icepdf.core.pobjects.acroform.SignatureFieldDictionary;
import org.icepdf.core.pobjects.acroform.signature.SignatureValidator;
import org.icepdf.core.pobjects.acroform.signature.exceptions.SignatureIntegrityException;
import org.icepdf.core.pobjects.annotations.SignatureWidgetAnnotation;
import org.icepdf.ri.common.SwingController;
import org.icepdf.ri.common.views.DocumentViewController;
import org.icepdf.ri.common.views.DocumentViewModel;
import org.icepdf.ri.common.views.annotations.signatures.CertificatePropertiesDialog;
import org.icepdf.ri.common.views.annotations.signatures.SignaturePropertiesDialog;
import org.icepdf.ri.common.views.annotations.signatures.SignatureValidationDialog;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* The SignaturesPanel lists all the digital signatures in a document as well as the signature fields components
* that are just placeholders. SwingWorkers are used to
*/
public class SignaturesPanel extends JPanel {
private static final Logger logger =
Logger.getLogger(SignaturesPanel.class.toString());
protected DocumentViewController documentViewController;
protected Document currentDocument;
private SwingController controller;
protected JTree signatureTree;
private DefaultMutableTreeNode rootTreeNode;
private DefaultTreeModel treeModel;
// show progress of the signature validation process.
protected JProgressBar progressBar;
// task to complete in separate thread
protected SigVerificationTask sigVerificationTask;
// status label for validation progress reporting.
protected JLabel progressLabel;
// time class to manage gui updates
protected Timer timer;
// refresh rate of gui elements
private static final int REFRESH_TIME = 100;
protected JScrollPane scrollPane;
private GridBagConstraints constraints;
protected DocumentViewModel documentViewModel;
// message bundle for internationalization
protected ResourceBundle messageBundle;
protected NodeSelectionListener nodeSelectionListener;
public SignaturesPanel(SwingController controller) {
super(true);
setFocusable(true);
this.controller = controller;
this.messageBundle = this.controller.getMessageBundle();
buildUI();
}
private void buildUI() {
rootTreeNode = new DefaultMutableTreeNode(messageBundle.getString("viewer.utilityPane.signatures.tab.title"));
rootTreeNode.setAllowsChildren(true);
treeModel = new DefaultTreeModel(rootTreeNode);
signatureTree = new SignaturesTree(treeModel);
signatureTree.setRootVisible(false);
signatureTree.setExpandsSelectedPaths(true);
signatureTree.setShowsRootHandles(true);
signatureTree.setScrollsOnExpand(true);
nodeSelectionListener = new NodeSelectionListener(signatureTree);
signatureTree.addMouseListener(nodeSelectionListener);
this.setLayout(new BorderLayout());
scrollPane = new JScrollPane(signatureTree,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scrollPane.getVerticalScrollBar().setUnitIncrement(20);
scrollPane.getHorizontalScrollBar().setUnitIncrement(20);
// setup validation progress bar and status label
progressBar = new JProgressBar(0, 1);
progressBar.setValue(0);
progressBar.setVisible(false);
progressLabel = new JLabel("");
progressLabel.setVisible(false);
timer = new Timer(REFRESH_TIME, new TimerListener());
/**
* Build signature tree GUI
*/
GridBagLayout layout = new GridBagLayout();
constraints = new GridBagConstraints();
constraints.fill = GridBagConstraints.NONE;
constraints.weightx = 1.0;
constraints.weighty = 0;
constraints.anchor = GridBagConstraints.NORTH;
constraints.anchor = GridBagConstraints.WEST;
constraints.insets = new Insets(10, 5, 1, 5);
JPanel signaturePanel = new JPanel(layout);
this.add(signaturePanel);
// add the lit to scroll pane
constraints.fill = GridBagConstraints.BOTH;
constraints.insets = new Insets(1, 5, 1, 5);
constraints.weightx = 1.0;
constraints.weighty = 1.0;
addGB(signaturePanel, scrollPane, 0, 0, 1, 1);
// add progress label
constraints.insets = new Insets(1, 5, 1, 5);
constraints.weighty = 0;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.anchor = GridBagConstraints.EAST;
progressLabel.setAlignmentX(JLabel.RIGHT_ALIGNMENT);
addGB(signaturePanel, progressLabel, 0, 1, 1, 1);
// add progress
constraints.insets = new Insets(5, 5, 1, 5);
constraints.fill = GridBagConstraints.HORIZONTAL;
addGB(signaturePanel, progressBar, 0, 2, 1, 1);
}
/**
* Called from the worker task to add a new signature node to the tree. It is assumed that
* this call is made from the AWT thread.
*
* @param signatureWidgetAnnotation annotation to add to tree.
*/
public void addSignature(SignatureWidgetAnnotation signatureWidgetAnnotation) {
if (signatureWidgetAnnotation != null) {
SignatureDictionary signatureDictionary = signatureWidgetAnnotation.getSignatureDictionary();
// filter any unsigned signer fields.
if (signatureDictionary.getEntries().size() > 0) {
SignatureTreeNode tmp = new SignatureTreeNode(signatureWidgetAnnotation, messageBundle);
tmp.refreshSignerNode();
tmp.setAllowsChildren(true);
// insert and expand the root node.
treeModel.insertNodeInto(tmp, rootTreeNode, rootTreeNode.getChildCount());
signatureTree.expandPath(new TreePath(rootTreeNode));
}
}
}
/**
* Called from the worker task to add a new unsigned signature node to the tree. It is assumed that
* this call is made from the AWT thread.
*
* @param signatures list off unsigned signatures annotation to add to tree.
*/
public void addUnsignedSignatures(ArrayList signatures) {
DefaultMutableTreeNode unsignedFieldNode = new DefaultMutableTreeNode(
messageBundle.getString("viewer.utilityPane.signatures.tab.certTree.unsigned.label"));
treeModel.insertNodeInto(unsignedFieldNode, rootTreeNode,
rootTreeNode.getChildCount());
for (SignatureWidgetAnnotation signature : signatures) {
SignatureDictionary signatureDictionary = signature.getSignatureDictionary();
// filter for only unsigned signer fields.
if (signatureDictionary.getEntries().size() == 0) {
DefaultMutableTreeNode field =
new DefaultMutableTreeNode(signature.getFieldDictionary().getPartialFieldName());
field.setAllowsChildren(false);
unsignedFieldNode.add(field);
}
}
signatureTree.expandPath(new TreePath(rootTreeNode));
signatureTree.expandPath(new TreePath(unsignedFieldNode));
revalidate();
}
/**
* Updates the data fields on a signature tree node after verification has taken place. It is assumed
* this method is always called from the AWT thread.
*
* @param signatureWidgetAnnotation annotation to update
* @param signatureTreeNode node that will be updated.
*/
public void updateSignature(SignatureWidgetAnnotation signatureWidgetAnnotation,
SignatureTreeNode signatureTreeNode) {
if (signatureWidgetAnnotation != null) {
try {
TreePath treePath = new TreePath(signatureTreeNode.getPath());
boolean isExpanded = signatureTree.isExpanded(treePath);
signatureTreeNode.validateSignatureNode();
signatureTreeNode.refreshSignerNode();
treeModel.reload();
if (isExpanded) {
signatureTree.expandPath(new TreePath(signatureTreeNode.getPath()));
}
} catch (SignatureIntegrityException e) {
logger.log(Level.WARNING, "Could not build signature node.", e);
}
}
}
/**
* Shows the signatureValidationDialog for the given SignatureWidgetAnnotation. This method should
* be called from the AWT thread.
*
* @param signatureWidgetAnnotation annotation to show the properties of.
*/
public void showSignatureValidationDialog(SignatureWidgetAnnotation signatureWidgetAnnotation) {
if (signatureWidgetAnnotation != null) {
// show the dialog
SignatureFieldDictionary fieldDictionary = signatureWidgetAnnotation.getFieldDictionary();
if (fieldDictionary != null) {
SignatureValidator signatureValidator = signatureWidgetAnnotation.getSignatureValidator();
if (signatureValidator != null) {
new SignatureValidationDialog(controller.getViewerFrame(),
messageBundle, signatureWidgetAnnotation, signatureValidator).setVisible(true);
}
}
}
}
/**
* Set the current document instance and starts the validation process of any found signature annotations.
*
* @param document current document, can be null.
*/
public void setDocument(Document document) {
// First have to stop any existing validation processes.
if (timer != null) {
timer.stop();
}
if (sigVerificationTask != null) {
sigVerificationTask.stop();
while (sigVerificationTask.isCurrentlyVerifying()) {
try {
Thread.sleep(50L);
} catch (Exception e) {
// intentional
}
}
}
this.currentDocument = document;
documentViewController = controller.getDocumentViewController();
documentViewModel = documentViewController.getDocumentViewModel();
// clear the previously loaded signature tree.
if (rootTreeNode != null) {
resetTree();
// set title
rootTreeNode.setAllowsChildren(true);
signatureTree.setRootVisible(false);
}
if (this.currentDocument != null &&
currentDocument.getCatalog().getInteractiveForm() != null) {
InteractiveForm interactiveForm = currentDocument.getCatalog().getInteractiveForm();
final ArrayList signatures = interactiveForm.getSignatureFields();
// build out the tree
if (signatures.size() > 0) {
if (!timer.isRunning()) {
// show the progress components.
progressLabel.setVisible(true);
progressBar.setVisible(true);
// clean the previous results and repaint the tree
resetTree();
// start a new verification task
sigVerificationTask = new SigVerificationTask(this, controller, messageBundle);
progressBar.setMaximum(sigVerificationTask.getLengthOfTask());
// start the task and the timer
sigVerificationTask.verifyAllSignatures();
timer.start();
}
}
}
}
/**
* Reset the tree for a new document or a new validation.
*/
protected void resetTree() {
signatureTree.setSelectionPath(null);
rootTreeNode.removeAllChildren();
treeModel.nodeStructureChanged(rootTreeNode);
}
/**
* Component clean on on document window tear down.
*/
public void dispose() {
this.removeAll();
sigVerificationTask = null;
controller = null;
documentViewModel = null;
currentDocument = null;
timer = null;
}
/**
* NodeSelectionListener handles the root node context menu creation display and command execution.
*/
class NodeSelectionListener extends MouseAdapter {
protected JTree tree;
protected JPopupMenu contextMenu;
private SignatureTreeNode signatureTreeNode;
NodeSelectionListener(JTree tree) {
this.tree = tree;
// add context menu for quick access to validating and signature properties.
contextMenu = new JPopupMenu();
JMenuItem validateMenu = new JMenuItem(messageBundle.getString(
"viewer.annotation.signature.menu.validateSignature.label"));
validateMenu.addActionListener(new validationActionListener());
contextMenu.add(validateMenu);
contextMenu.add(new JPopupMenu.Separator());
JMenuItem signaturePropertiesMenu = new JMenuItem(messageBundle.getString(
"viewer.annotation.signature.menu.signatureProperties.label"));
signaturePropertiesMenu.addActionListener(new SignaturesPropertiesActionListener(tree));
contextMenu.add(signaturePropertiesMenu);
contextMenu.add(new JPopupMenu.Separator());
JMenuItem signaturePageNavigationMenu = new JMenuItem(messageBundle.getString(
"viewer.annotation.signature.menu.signaturePageNavigation.label"));
signaturePageNavigationMenu.addActionListener(new SignaturesPageNavigationListener());
contextMenu.add(signaturePageNavigationMenu);
}
public void mouseClicked(MouseEvent e) {
int x = e.getX();
int y = e.getY();
int row = tree.getRowForLocation(x, y);
TreePath path = tree.getPathForRow(row);
if (path != null) {
Object node = path.getLastPathComponent();
if (node instanceof SignatureCertTreeNode) {
// someone clicked on the show certificate node.
// create new dialog to show certificate properties.
SignatureCertTreeNode selectedSignatureCert = (SignatureCertTreeNode) node;
new CertificatePropertiesDialog(controller.getViewerFrame(), messageBundle,
selectedSignatureCert.getCertificateChain())
.setVisible(true);
} else if (node instanceof SignatureTreeNode &&
(e.getButton() == MouseEvent.BUTTON3 || e.getButton() == MouseEvent.BUTTON2)) {
signatureTreeNode = (SignatureTreeNode) node;
// show context menu.
contextMenu.show(e.getComponent(), e.getX(), e.getY());
}
}
}
public SignatureTreeNode getSignatureTreeNode() {
return signatureTreeNode;
}
}
/**
* GridBag constructor helper
*
* @param panel parent adding component too.
* @param component component to add to grid
* @param x row
* @param y col
* @param rowSpan rowspane of field
* @param colSpan colspane of field.
*/
private void addGB(JPanel panel, Component component,
int x, int y,
int rowSpan, int colSpan) {
constraints.gridx = x;
constraints.gridy = y;
constraints.gridwidth = rowSpan;
constraints.gridheight = colSpan;
panel.add(component, constraints);
}
/**
* Shows the SignatureValidationDialog dialog.
*/
class validationActionListener implements ActionListener {
public void actionPerformed(ActionEvent actionEvent) {
if (!sigVerificationTask.isCurrentlyVerifying()) {
// validate the signature and show the summary dialog.
final SignatureTreeNode signatureTreeNode = nodeSelectionListener.getSignatureTreeNode();
SignatureWidgetAnnotation signatureWidgetAnnotation = signatureTreeNode.getOutlineItem();
if (!timer.isRunning()) {
// update gui components
progressLabel.setVisible(true);
progressBar.setVisible(true);
progressBar.setMaximum(1);
// start the task and the timer
sigVerificationTask.verifySignature(signatureWidgetAnnotation, signatureTreeNode);
timer.start();
}
}
}
}
/**
* Navigates to the page the selected signature annotation exists on.
*/
class SignaturesPageNavigationListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (nodeSelectionListener.getSignatureTreeNode() != null) {
final SignatureTreeNode signatureTreeNode = nodeSelectionListener.getSignatureTreeNode();
SignatureWidgetAnnotation signatureWidgetAnnotation = signatureTreeNode.getOutlineItem();
// turn out the parent is seldom used correctly and generally just points to page zero.
// so we should
Document document = controller.getDocument();
int pages = controller.getPageTree().getNumberOfPages();
boolean found = false;
for (int i = 0; i < pages && !found; i++) {
// check is page's annotation array for a matching reference.
ArrayList annotationReferences = document.getPageTree().getPage(i).getAnnotationReferences();
if (annotationReferences != null) {
for (Reference reference : annotationReferences) {
if (reference.equals(signatureWidgetAnnotation.getPObjectReference())) {
controller.showPage(i);
found = true;
break;
}
}
}
}
}
}
}
/**
* Command object for displaying the SignaturePropertiesDialog.
*/
class SignaturesPropertiesActionListener implements ActionListener {
protected JTree tree;
public SignaturesPropertiesActionListener(JTree tree) {
this.tree = tree;
}
public void actionPerformed(ActionEvent e) {
if (nodeSelectionListener.getSignatureTreeNode() != null) {
final SignatureTreeNode signatureTreeNode = nodeSelectionListener.getSignatureTreeNode();
new SignaturePropertiesDialog(controller.getViewerFrame(),
messageBundle, signatureTreeNode.getOutlineItem()).setVisible(true);
}
}
}
/**
* The actionPerformed method in this class is called each time the Timer "goes off".
*/
class TimerListener implements ActionListener {
public void actionPerformed(ActionEvent evt) {
progressBar.setValue(sigVerificationTask.getCurrent());
String s = sigVerificationTask.getMessage();
if (s != null) {
progressLabel.setText(s);
}
// update the text and stop the timer when the validation is completed or terminated.
if (sigVerificationTask.isDone() || !sigVerificationTask.isCurrentlyVerifying()) {
// update search status
timer.stop();
sigVerificationTask.stop();
// update progress bar then hide it.
progressBar.setValue(progressBar.getMinimum());
progressBar.setVisible(false);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy