Autopsy  4.14.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
TranslatedTextViewer.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2019 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.texttranslation.ui;
20 
21 import com.google.common.collect.Lists;
22 import com.google.common.util.concurrent.ThreadFactoryBuilder;
23 import java.awt.Component;
24 import java.awt.ComponentOrientation;
25 import java.awt.Font;
26 import java.awt.event.ActionEvent;
27 import java.awt.event.ActionListener;
28 import java.io.IOException;
29 import java.io.Reader;
30 import java.util.Arrays;
31 import java.util.concurrent.ExecutorService;
32 import java.util.concurrent.Executors;
33 import java.util.concurrent.ThreadFactory;
34 import org.openide.nodes.Node;
35 import org.openide.util.lookup.ServiceProvider;
37 import org.sleuthkit.datamodel.AbstractFile;
38 import org.openide.util.Lookup;
39 import org.openide.util.NbBundle;
40 import org.openide.util.lookup.Lookups;
47 import org.sleuthkit.datamodel.Content;
48 import java.util.List;
49 import java.util.logging.Level;
50 import javax.swing.SwingUtilities;
54 
58 @ServiceProvider(service = TextViewer.class, position = 4)
59 public final class TranslatedTextViewer implements TextViewer {
60 
61  private static final Logger logger = Logger.getLogger(TranslatedTextViewer.class.getName());
62 
63  private static final boolean OCR_ENABLED = true;
64  private static final boolean OCR_DISABLED = false;
65  private static final int MAX_EXTRACT_SIZE_BYTES = 25600;
66  private static final List<String> INSTALLED_LANGUAGE_PACKS = PlatformUtil.getOcrLanguagePacks();
67  private final TranslationContentPanel panel = new TranslationContentPanel();
68 
69  private volatile Node node;
71  private final ThreadFactory translationThreadFactory
72  = new ThreadFactoryBuilder().setNameFormat("translation-content-viewer-%d").build();
73  private final ExecutorService executorService = Executors.newSingleThreadExecutor(translationThreadFactory);
74 
75  @NbBundle.Messages({
76  "TranslatedTextViewer.maxPayloadSize=Up to the first %dKB of text will be translated"
77  })
78  @Override
79  public void setNode(final Node node) {
80  this.node = node;
81  SelectionChangeListener displayDropDownListener = new DisplayDropDownChangeListener();
82  panel.addDisplayTextActionListener(displayDropDownListener);
83  panel.addOcrDropDownActionListener(new OCRDropdownChangeListener());
84  Content source = DataContentViewerUtility.getDefaultContent(node);
85 
86  if (source instanceof AbstractFile) {
87  boolean isImage = ((AbstractFile) source).getMIMEType().toLowerCase().startsWith("image/");
88  if (isImage) {
89  panel.enableOCRSelection(OCR_ENABLED);
90  panel.addLanguagePackNames(INSTALLED_LANGUAGE_PACKS);
91  }
92  }
93 
94  int payloadMaxInKB = TextTranslationService.getInstance().getMaxTextChars() / 1000;
95  panel.setWarningLabelMsg(String.format(Bundle.TranslatedTextViewer_maxPayloadSize(), payloadMaxInKB));
96 
97  //Force a background task.
98  displayDropDownListener.actionPerformed(null);
99  }
100 
101  @NbBundle.Messages({"TranslatedTextViewer.title=Translation"})
102  @Override
103  public String getTitle() {
104  return Bundle.TranslatedTextViewer_title();
105  }
106 
107  @NbBundle.Messages({"TranslatedTextViewer.toolTip=Displays translated file text."})
108  @Override
109  public String getToolTip() {
110  return Bundle.TranslatedTextViewer_toolTip();
111  }
112 
113  @Override
114  public TextViewer createInstance() {
115  return new TranslatedTextViewer();
116  }
117 
118  @Override
119  public Component getComponent() {
120  return panel;
121  }
122 
123  @Override
124  public void resetComponent() {
125  panel.reset();
126  this.node = null;
127  if (backgroundTask != null) {
128  backgroundTask.cancel(true);
129  }
130  backgroundTask = null;
131  }
132 
133  @Override
134  public boolean isSupported(Node node) {
135  if (null == node) {
136  return false;
137  }
138 
140  return false;
141  }
142 
143  AbstractFile file = node.getLookup().lookup(AbstractFile.class);
144  return file != null;
145  }
146 
147  @Override
148  public int isPreferred(Node node) {
149  // Returning zero makes it unlikely this object will be the preferred content viewer,
150  // i.e., the active tab, when the content viewers are first displayed.
151  return 0;
152  }
153 
158 
159  private final AbstractFile file;
160 
161  private ExtractAndTranslateTextTask(AbstractFile file, boolean translateText) {
162  super(translateText, String.format("%s (objId=%d)", file.getName(), file.getId()));
163  this.file = file;
164  }
165 
173  @NbBundle.Messages({
174  "TranslatedContentViewer.extractingText=Extracting text, please wait...",
175  "# {0} - exception message", "TranslatedContentViewer.errorExtractingText=An error occurred while extracting the text ({0}).",
176  })
177  protected String retrieveText() throws IOException, InterruptedException, IllegalStateException {
178  SwingUtilities.invokeLater(() -> {
179  onProgressDisplay(Bundle.TranslatedContentViewer_extractingText(), ComponentOrientation.LEFT_TO_RIGHT, Font.ITALIC);
180  });
181 
182  try {
183  return getFileText(file);
184  } catch (IOException | TextExtractor.InitReaderException ex) {
185  logger.log(Level.WARNING, String.format("Error extracting text for file %s (objId=%d)", file.getName(), file.getId()), ex);
186  // throw new exception with message to be displayed to user
187  throw new IllegalStateException(Bundle.TranslatedContentViewer_errorExtractingText(ex.getMessage()), ex);
188  }
189  }
190 
191 
204  private String getFileText(AbstractFile file) throws IOException, InterruptedException, TextExtractor.InitReaderException {
205  final boolean isImage = file.getMIMEType().toLowerCase().startsWith("image/"); // NON-NLS
206  String result;
207  if (isImage) {
208  result = extractText(file, OCR_ENABLED);
209  } else {
210  result = extractText(file, OCR_DISABLED);
211  }
212 
213  //Correct for UTF-8
214  byte[] resultInUTF8Bytes = result.getBytes("UTF8");
215  byte[] trimToArraySize = Arrays.copyOfRange(resultInUTF8Bytes, 0,
216  Math.min(resultInUTF8Bytes.length, MAX_EXTRACT_SIZE_BYTES));
217  return new String(trimToArraySize, "UTF-8");
218  }
219 
233  private String extractText(AbstractFile source, boolean ocrEnabled) throws IOException, InterruptedException, TextExtractor.InitReaderException {
234  Reader textExtractor = getTextExtractor(source, ocrEnabled);
235 
236  char[] cbuf = new char[8096];
237  StringBuilder textBuilder = new StringBuilder();
238 
239  //bytesRead can be an int so long as the max file size
240  //is sufficiently small
241  int bytesRead = 0;
242  int read;
243 
244  while ((read = textExtractor.read(cbuf)) != -1) {
245  if (this.isCancelled()) {
246  throw new InterruptedException();
247  }
248 
249  //Short-circuit the read if its greater than our max
250  //translatable size
251  int bytesLeft = MAX_EXTRACT_SIZE_BYTES - bytesRead;
252 
253  if (bytesLeft < read) {
254  textBuilder.append(cbuf, 0, bytesLeft);
255  return textBuilder.toString();
256  }
257 
258  textBuilder.append(cbuf, 0, read);
259  bytesRead += read;
260  }
261 
262  return textBuilder.toString();
263  }
264 
278  private Reader getTextExtractor(AbstractFile file, boolean ocrEnabled) throws IOException,
280  Lookup context = null;
281 
282  if (ocrEnabled) {
283  ImageConfig imageConfig = new ImageConfig();
284  imageConfig.setOCREnabled(true);
285 
286  String ocrSelection = panel.getSelectedOcrLanguagePack();
287  if (!ocrSelection.isEmpty()) {
288  imageConfig.setOCRLanguages(Lists.newArrayList(ocrSelection));
289  }
290 
291  //Terminate any OS process running in the extractor if this
292  //SwingWorker has been cancelled.
293  ProcessTerminator terminator = () -> isCancelled();
294  context = Lookups.fixed(imageConfig, terminator);
295  }
296 
297  try {
298  return TextExtractorFactory.getExtractor(file, context).getReader();
300  //Fall-back onto the strings extractor
301  return TextExtractorFactory.getStringsExtractor(file, context).getReader();
302  }
303  }
304 
305 
306  @Override
307  protected void onTextDisplay(String text, ComponentOrientation orientation, int font) {
308  panel.display(text, orientation, font);
309  }
310  }
311 
312 
317  private abstract class SelectionChangeListener implements ActionListener {
318 
319  private String currentSelection;
320 
321  abstract String getSelection();
322 
323  @Override
324  public final void actionPerformed(ActionEvent e) {
325  String selection = getSelection();
326  if (!selection.equals(currentSelection)) {
327  currentSelection = selection;
328 
329  if (backgroundTask != null && !backgroundTask.isDone()) {
330  backgroundTask.cancel(true);
331  }
332 
333  AbstractFile file = node.getLookup().lookup(AbstractFile.class);
334  String textDisplaySelection = panel.getDisplayDropDownSelection();
335  boolean translateText = !textDisplaySelection.equals(DisplayDropdownOptions.ORIGINAL_TEXT.toString());
336  backgroundTask = new ExtractAndTranslateTextTask(file, translateText);
337 
338  //Pass the background task to a single threaded pool to keep
339  //the number of jobs running to one.
340  executorService.execute(backgroundTask);
341  }
342  }
343  }
344 
349 
350  @Override
351  String getSelection() {
352  return panel.getDisplayDropDownSelection();
353  }
354  }
355 
360 
361  @Override
362  String getSelection() {
363  return panel.getSelectedOcrLanguagePack();
364  }
365  }
366 }
void display(String text, ComponentOrientation direction, int font)
static TextExtractor getStringsExtractor(Content content, Lookup context)
static TextExtractor getExtractor(Content content, Lookup context)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124

Copyright © 2012-2020 Basis Technology. Generated on: Wed Apr 8 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.