Autopsy  4.20.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
PDFViewer.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2020 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.contentviewers;
20 
21 import java.awt.BorderLayout;
22 import java.awt.Component;
23 import java.awt.Container;
24 import java.io.IOException;
25 import java.lang.reflect.InvocationTargetException;
26 import java.util.Arrays;
27 import java.util.List;
28 import java.util.Properties;
29 import java.util.ResourceBundle;
30 import java.util.concurrent.ExecutionException;
31 import java.util.logging.Level;
32 
33 import javax.swing.JPanel;
34 import javax.swing.SwingUtilities;
35 import javax.swing.SwingWorker;
36 import org.icepdf.core.SecurityCallback;
37 
38 import org.openide.util.NbBundle;
39 
40 import org.sleuthkit.datamodel.AbstractFile;
41 import org.sleuthkit.datamodel.ReadContentInputStream;
42 
45 
46 import org.icepdf.core.exceptions.PDFException;
47 import org.icepdf.core.exceptions.PDFSecurityException;
48 import org.icepdf.core.pobjects.Document;
49 
50 import org.icepdf.ri.common.ComponentKeyBinding;
51 import org.icepdf.ri.common.MyGUISecurityCallback;
52 import org.icepdf.ri.common.SwingController;
53 import org.icepdf.ri.common.SwingViewBuilder;
54 import org.icepdf.ri.common.views.DocumentViewControllerImpl;
55 import org.icepdf.ri.common.views.DocumentViewModelImpl;
56 import org.icepdf.ri.util.PropertiesManager;
57 
61 final class PDFViewer implements FileTypeViewer {
62 
63  private static final Logger logger = Logger.getLogger(PDFViewer.class.getName());
64 
65  private JPanel container;
66  private final PropertiesManager propsManager;
67  private final ResourceBundle messagesBundle;
68 
69  PDFViewer() {
70  container = createNewContainer();
71  messagesBundle = getMessagesBundle();
72  propsManager = getCustomProperties();
73  }
74 
75  @Override
76  public List<String> getSupportedMIMETypes() {
77  return Arrays.asList("application/pdf");
78  }
79 
80  @Override
81  public void setFile(AbstractFile file) {
82  // The 'C' in IcePDFs MVC set up.
83  SwingController controller = new SwingController(messagesBundle);
84 
85  // Builder for the 'V' in IcePDFs MVC set up
86  SwingViewBuilder viewBuilder = new SwingViewBuilder(controller, propsManager);
87 
88  // The 'V' in IcePDFs MVC set up.
89  JPanel icePdfPanel = viewBuilder.buildViewerPanel();
90 
91  // This connects keyboard commands performed on the view to the controller.
92  // The only keyboard commands that the controller supports is Ctrl-C for
93  // copying selected text.
94  ComponentKeyBinding.install(controller, icePdfPanel);
95 
96  // Ensure the preferredSize is in sync with the parent container.
97  icePdfPanel.setPreferredSize(this.container.getPreferredSize());
98 
99  // Add the IcePDF view to the center of our container.
100  this.container.add(icePdfPanel, BorderLayout.CENTER);
101 
102  // Disable all components until the document is ready to view.
103  enableComponents(container, false);
104 
105  // Document is the 'M' in IcePDFs MVC set up. Read the data needed to
106  // populate the model in the background.
107  new SwingWorker<Document, Void>() {
108  @Override
109  protected Document doInBackground() throws PDFException, PDFSecurityException, IOException {
110  ReadContentInputStream stream = new ReadContentInputStream(file);
111  Document doc = new Document();
112 
113  // Prompts the user for a password if the document is password
114  // protected.
115  doc.setSecurityCallback(createPasswordDialogCallback());
116 
117  // This will read the stream into memory and invoke the
118  // security callback if needed.
119  doc.setInputStream(stream, null);
120  return doc;
121  }
122 
123  @Override
124  protected void done() {
125  // Customize the view selection modes on the EDT. Each of these
126  // will cause UI widgets to be updated.
127  try {
128  Document doc = get();
129  controller.openDocument(doc, file.getName());
130  // This makes the PDF viewer appear as one continuous
131  // document, which is the default for most popular PDF viewers.
132  controller.setPageViewMode(DocumentViewControllerImpl.ONE_COLUMN_VIEW, true);
133  // This makes it possible to select text by left clicking and dragging.
134  controller.setDisplayTool(DocumentViewModelImpl.DISPLAY_TOOL_TEXT_SELECTION);
135  enableComponents(container, true);
136  } catch (InterruptedException ex) {
137  // Do nothing.
138  } catch (ExecutionException ex) {
139  Throwable exCause = ex.getCause();
140  if (exCause instanceof PDFSecurityException) {
141  showEncryptionDialog();
142  } else {
143  logger.log(Level.WARNING, String.format("PDF content viewer "
144  + "was unable to open document with id %d and name %s",
145  file.getId(), file.getName()), ex);
146  showErrorDialog();
147  }
148  } catch (Throwable ex) {
149  logger.log(Level.WARNING, String.format("PDF content viewer "
150  + "was unable to open document with id %d and name %s",
151  file.getId(), file.getName()), ex);
152  }
153  }
154  }.execute();
155  }
156 
161  private void enableComponents(Container container, boolean enabled) {
162  Component[] components = container.getComponents();
163  for(Component component : components) {
164  component.setEnabled(enabled);
165  if (component instanceof Container) {
166  enableComponents((Container)component, enabled);
167  }
168  }
169  }
170 
171  @Override
172  public Component getComponent() {
173  return container;
174  }
175 
176  @Override
177  public void resetComponent() {
178  container = createNewContainer();
179  }
180 
181  // The container should have a BorderLayout otherwise the IcePDF panel may
182  // not be visible.
183  private JPanel createNewContainer() {
184  return new JPanel(new BorderLayout());
185  }
186 
187  @Override
188  public boolean isSupported(AbstractFile file) {
189  return getSupportedMIMETypes().contains(file.getMIMEType());
190  }
191 
196  private PropertiesManager getCustomProperties() {
197  Properties props = new Properties();
198 
199  // See link for available properties. https://www.icesoft.org/wiki/display/PDF/Customizing+the+Viewer
200  props.setProperty(PropertiesManager.PROPERTY_SHOW_UTILITY_SAVE, "false");
201  props.setProperty(PropertiesManager.PROPERTY_SHOW_UTILITY_OPEN, "false");
202  props.setProperty(PropertiesManager.PROPERTY_SHOW_UTILITY_PRINT, "false");
203  props.setProperty(PropertiesManager.PROPERTY_SHOW_TOOLBAR_ANNOTATION, "false");
204  props.setProperty(PropertiesManager.PROPERTY_SHOW_UTILITYPANE_ANNOTATION, "false");
205 
206  // This suppresses a pop-up, from IcePDF, that asks if you'd like to
207  // save configuration changes to disk.
208  props.setProperty("application.showLocalStorageDialogs", "false");
209 
210  return new PropertiesManager(System.getProperties(), props, messagesBundle);
211  }
212 
213  private ResourceBundle getMessagesBundle() {
214  return NbBundle.getBundle(PDFViewer.class);
215  }
216 
217  @NbBundle.Messages({
218  "PDFViewer.errorDialog=An error occurred while opening this PDF document. "
219  + "Check the logs for more information. You may continue to use "
220  + "this feature on other PDF documents."
221  })
222  private void showErrorDialog() {
223  MessageNotifyUtil.Message.error(Bundle.PDFViewer_errorDialog());
224  }
225 
226  @NbBundle.Messages({
227  "PDFViewer.encryptedDialog=This document is password protected."
228  })
229  private void showEncryptionDialog() {
230  MessageNotifyUtil.Message.error(Bundle.PDFViewer_encryptedDialog());
231  }
232 
236  private SecurityCallback createPasswordDialogCallback() {
237  // MyGUISecurityCallback is a reference implementation from IcePDF.
238  return new MyGUISecurityCallback(null, messagesBundle) {
239  private String password;
240 
241  @Override
242  public String requestPassword(Document document) {
243  try {
244  SwingUtilities.invokeAndWait(() -> {
245  // Show the password dialog on the EDT.
246  this.password = super.requestPassword(document);
247  });
248  return this.password;
249  } catch (InterruptedException | InvocationTargetException ex) {
250  return null;
251  }
252  }
253  };
254  }
255 }

Copyright © 2012-2022 Basis Technology. Generated on: Tue Aug 1 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.