Autopsy  4.20.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 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.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;
46 import java.util.List;
47 import java.util.logging.Level;
48 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 int MAX_EXTRACT_SIZE_BYTES = 25600;
64  private static final List<String> INSTALLED_LANGUAGE_PACKS = PlatformUtil.getOcrLanguagePacks();
65  private final TranslationContentPanel panel = new TranslationContentPanel();
66 
67  private volatile Node node;
69  private final ThreadFactory translationThreadFactory
70  = new ThreadFactoryBuilder().setNameFormat("translation-content-viewer-%d").build();
71  private final ExecutorService executorService = Executors.newSingleThreadExecutor(translationThreadFactory);
72 
73  @NbBundle.Messages({
74  "TranslatedTextViewer.maxPayloadSize=Up to the first %dKB of text will be translated"
75  })
76  @Override
77  public void setNode(final Node node) {
78  this.node = node;
79  SelectionChangeListener displayDropDownListener = new DisplayDropDownChangeListener();
80  panel.addDisplayTextActionListener(displayDropDownListener);
81  panel.addOcrDropDownActionListener(new OCRDropdownChangeListener());
83  panel.addLanguagePackNames(INSTALLED_LANGUAGE_PACKS);
84  }
85  panel.enableOCRSelection(UserPreferences.getUseOcrInTranslation());
86 
87  int payloadMaxInKB = TextTranslationService.getInstance().getMaxTextChars() / 1000;
88  panel.setWarningLabelMsg(String.format(Bundle.TranslatedTextViewer_maxPayloadSize(), payloadMaxInKB));
89 
90  //Force a background task.
91  displayDropDownListener.actionPerformed(null);
92  }
93 
94  @NbBundle.Messages({"TranslatedTextViewer.title=Translation"})
95  @Override
96  public String getTitle() {
97  return Bundle.TranslatedTextViewer_title();
98  }
99 
100  @NbBundle.Messages({"TranslatedTextViewer.toolTip=Displays translated file text."})
101  @Override
102  public String getToolTip() {
103  return Bundle.TranslatedTextViewer_toolTip();
104  }
105 
106  @Override
107  public TextViewer createInstance() {
108  return new TranslatedTextViewer();
109  }
110 
111  @Override
112  public Component getComponent() {
113  return panel;
114  }
115 
116  @Override
117  public void resetComponent() {
118  panel.reset();
119  this.node = null;
120  if (backgroundTask != null) {
121  backgroundTask.cancel(true);
122  }
123  backgroundTask = null;
124  }
125 
126  @Override
127  public boolean isSupported(Node node) {
128  if (null == node) {
129  return false;
130  }
131 
133  return false;
134  }
135 
136  AbstractFile file = node.getLookup().lookup(AbstractFile.class);
137  return file != null;
138  }
139 
140  @Override
141  public int isPreferred(Node node) {
142  // Returning zero makes it unlikely this object will be the preferred content viewer,
143  // i.e., the active tab, when the content viewers are first displayed.
144  return ViewerPriority.viewerPriority.LevelZero.getFlag();
145  }
146 
151 
152  private final AbstractFile file;
153 
154  private ExtractAndTranslateTextTask(AbstractFile file, boolean translateText) {
155  super(translateText, String.format("%s (objId=%d)", file.getName(), file.getId()));
156  this.file = file;
157  }
158 
166  @NbBundle.Messages({
167  "TranslatedContentViewer.extractingText=Extracting text, please wait...",
168  "# {0} - exception message", "TranslatedContentViewer.errorExtractingText=An error occurred while extracting the text ({0}).",
169  })
170  protected String retrieveText() throws IOException, InterruptedException, IllegalStateException {
171  SwingUtilities.invokeLater(() -> {
172  onProgressDisplay(Bundle.TranslatedContentViewer_extractingText(), ComponentOrientation.LEFT_TO_RIGHT, Font.ITALIC);
173  });
174 
175  try {
176  return getFileText(file);
177  } catch (IOException | TextExtractor.InitReaderException ex) {
178  logger.log(Level.WARNING, String.format("Error extracting text for file %s (objId=%d)", file.getName(), file.getId()), ex);
179  // throw new exception with message to be displayed to user
180  throw new IllegalStateException(Bundle.TranslatedContentViewer_errorExtractingText(ex.getMessage()), ex);
181  }
182  }
183 
184 
197  @NbBundle.Messages({
198  "TranslatedContentViewer.ocrNotEnabled=OCR is not enabled. To change, go to Tools->Options->Machine Translation",
199  })
200  private String getFileText(AbstractFile file) throws IOException, InterruptedException, TextExtractor.InitReaderException {
201  final boolean isImage = file.getMIMEType().toLowerCase().startsWith("image/"); // NON-NLS
202  if (isImage && ! UserPreferences.getUseOcrInTranslation()) {
203  return Bundle.TranslatedContentViewer_ocrNotEnabled();
204  }
205 
206  String result = extractText(file, UserPreferences.getUseOcrInTranslation());
207 
208  //Correct for UTF-8
209  byte[] resultInUTF8Bytes = result.getBytes("UTF8");
210  byte[] trimToArraySize = Arrays.copyOfRange(resultInUTF8Bytes, 0,
211  Math.min(resultInUTF8Bytes.length, MAX_EXTRACT_SIZE_BYTES));
212  return new String(trimToArraySize, "UTF-8");
213  }
214 
228  private String extractText(AbstractFile source, boolean ocrEnabled) throws IOException, InterruptedException, TextExtractor.InitReaderException {
229  Reader textExtractor = getTextExtractor(source, ocrEnabled);
230 
231  char[] cbuf = new char[8096];
232  StringBuilder textBuilder = new StringBuilder();
233 
234  //bytesRead can be an int so long as the max file size
235  //is sufficiently small
236  int bytesRead = 0;
237  int read;
238 
239  while ((read = textExtractor.read(cbuf)) != -1) {
240  if (this.isCancelled()) {
241  throw new InterruptedException();
242  }
243 
244  //Short-circuit the read if its greater than our max
245  //translatable size
246  int bytesLeft = MAX_EXTRACT_SIZE_BYTES - bytesRead;
247 
248  if (bytesLeft < read) {
249  textBuilder.append(cbuf, 0, bytesLeft);
250  return textBuilder.toString();
251  }
252 
253  textBuilder.append(cbuf, 0, read);
254  bytesRead += read;
255  }
256 
257  return textBuilder.toString();
258  }
259 
273  private Reader getTextExtractor(AbstractFile file, boolean ocrEnabled) throws IOException,
275  Lookup context = null;
276 
277  if (ocrEnabled) {
278  ImageConfig imageConfig = new ImageConfig();
279  imageConfig.setOCREnabled(true);
280 
281  String ocrSelection = panel.getSelectedOcrLanguagePack();
282  if (!ocrSelection.isEmpty()) {
283  imageConfig.setOCRLanguages(Lists.newArrayList(ocrSelection));
284  }
285 
286  //Terminate any OS process running in the extractor if this
287  //SwingWorker has been cancelled.
288  ProcessTerminator terminator = () -> isCancelled();
289  context = Lookups.fixed(imageConfig, terminator);
290  }
291 
292  try {
293  return TextExtractorFactory.getExtractor(file, context).getReader();
295  //Fall-back onto the strings extractor
296  return TextExtractorFactory.getStringsExtractor(file, context).getReader();
297  }
298  }
299 
300 
301  @Override
302  protected void onTextDisplay(String text, ComponentOrientation orientation, int font) {
303  panel.display(text, orientation, font);
304  }
305  }
306 
307 
312  private abstract class SelectionChangeListener implements ActionListener {
313 
314  private String currentSelection;
315 
316  abstract String getSelection();
317 
318  @Override
319  public final void actionPerformed(ActionEvent e) {
320  String selection = getSelection();
321  if (!selection.equals(currentSelection)) {
322  currentSelection = selection;
323 
324  if (backgroundTask != null && !backgroundTask.isDone()) {
325  backgroundTask.cancel(true);
326  }
327 
328  AbstractFile file = node.getLookup().lookup(AbstractFile.class);
329  String textDisplaySelection = panel.getDisplayDropDownSelection();
330  boolean translateText = !textDisplaySelection.equals(DisplayDropdownOptions.ORIGINAL_TEXT.toString());
331  backgroundTask = new ExtractAndTranslateTextTask(file, translateText);
332 
333  //Pass the background task to a single threaded pool to keep
334  //the number of jobs running to one.
335  executorService.execute(backgroundTask);
336  }
337  }
338  }
339 
344 
345  @Override
346  String getSelection() {
347  return panel.getDisplayDropDownSelection();
348  }
349  }
350 
355 
356  @Override
357  String getSelection() {
358  return panel.getSelectedOcrLanguagePack();
359  }
360  }
361 }
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-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.