19 package org.sleuthkit.autopsy.texttranslation.ui;
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;
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.CancellationException;
32 import java.util.concurrent.ExecutionException;
33 import java.util.concurrent.ExecutorService;
34 import java.util.concurrent.Executors;
35 import java.util.concurrent.ThreadFactory;
36 import org.openide.nodes.Node;
37 import org.openide.util.lookup.ServiceProvider;
40 import javax.swing.SwingWorker;
41 import org.openide.util.Lookup;
42 import org.openide.util.NbBundle;
43 import org.openide.util.lookup.Lookups;
54 import java.util.List;
55 import java.util.logging.Level;
63 @ServiceProvider(service =
TextViewer.class, position = 4)
68 private static final boolean OCR_ENABLED =
true;
69 private static final boolean OCR_DISABLED =
false;
70 private static final int MAX_EXTRACT_SIZE_BYTES = 25600;
74 private volatile Node
node;
76 private final ThreadFactory translationThreadFactory
77 =
new ThreadFactoryBuilder().setNameFormat(
"translation-content-viewer-%d").build();
78 private final ExecutorService executorService = Executors.newSingleThreadExecutor(translationThreadFactory);
81 "TranslatedTextViewer.maxPayloadSize=Up to the first %dKB of text will be translated"
87 panel.addDisplayTextActionListener(displayDropDownListener);
91 if (source instanceof AbstractFile) {
92 boolean isImage = ((AbstractFile) source).getMIMEType().toLowerCase().startsWith(
"image/");
94 panel.enableOCRSelection(OCR_ENABLED);
95 panel.addLanguagePackNames(INSTALLED_LANGUAGE_PACKS);
100 panel.setWarningLabelMsg(String.format(Bundle.TranslatedTextViewer_maxPayloadSize(), payloadMaxInKB));
106 @NbBundle.Messages({
"TranslatedTextViewer.title=Translation"})
109 return Bundle.TranslatedTextViewer_title();
112 @NbBundle.Messages({
"TranslatedTextViewer.toolTip=Displays translated file text."})
115 return Bundle.TranslatedTextViewer_toolTip();
132 if (updateTask != null) {
133 updateTask.cancel(
true);
148 AbstractFile file = node.getLookup().lookup(AbstractFile.class);
165 "TranslatedContentViewer.noIndexedTextMsg=Run the Keyword Search Ingest Module to get text for translation.",
166 "TranslatedContentViewer.textAlreadyIndexed=Please view the original text in the Indexed Text viewer.",
167 "TranslatedContentViewer.errorMsg=Error encountered while getting file text.",
168 "TranslatedContentViewer.errorExtractingText=Could not extract text from file.",
169 "TranslatedContentViewer.translatingText=Translating text, please wait..."
173 if (this.isCancelled()) {
174 throw new InterruptedException();
176 String dropdownSelection = panel.getDisplayDropDownSelection();
178 if (dropdownSelection.equals(DisplayDropdownOptions.ORIGINAL_TEXT.toString())) {
180 return getFileText(node);
181 }
catch (IOException ex) {
182 logger.log(Level.WARNING,
"Error getting text", ex);
183 return Bundle.TranslatedContentViewer_errorMsg();
185 logger.log(Level.WARNING,
"Error getting text", ex);
186 return Bundle.TranslatedContentViewer_errorExtractingText();
190 return translate(getFileText(node));
191 }
catch (IOException ex) {
192 logger.log(Level.WARNING,
"Error translating text", ex);
193 return Bundle.TranslatedContentViewer_errorMsg();
195 logger.log(Level.WARNING,
"Error translating text", ex);
196 return Bundle.TranslatedContentViewer_errorExtractingText();
206 @NbBundle.Messages({
"TranslatedContentViewer.extractingImageText=Extracting text from image, please wait...",
207 "TranslatedContentViewer.extractingFileText=Extracting text from file, please wait...",})
210 panel.
display(Bundle.TranslatedContentViewer_extractingImageText(),
211 ComponentOrientation.LEFT_TO_RIGHT, Font.ITALIC);
213 panel.
display(Bundle.TranslatedContentViewer_extractingFileText(),
214 ComponentOrientation.LEFT_TO_RIGHT, Font.ITALIC);
221 String result =
get();
222 if (this.isCancelled()) {
223 throw new InterruptedException();
225 int len = result.length();
226 int maxOrientChars = Math.min(len, 1024);
227 String orientDetectSubstring = result.substring(0, maxOrientChars);
229 panel.
display(result, orientation, Font.PLAIN);
230 }
catch (InterruptedException | ExecutionException | CancellationException ignored) {
243 "TranslatedContentViewer.emptyTranslation=The resulting translation was empty.",
244 "TranslatedContentViewer.noServiceProvider=Machine Translation software was not found.",
245 "TranslatedContentViewer.translationException=Error encountered while attempting translation."})
246 private String
translate(String input)
throws InterruptedException {
247 if (this.isCancelled()) {
248 throw new InterruptedException();
251 panel.
display(Bundle.TranslatedContentViewer_translatingText(),
252 ComponentOrientation.LEFT_TO_RIGHT, Font.ITALIC);
256 String translatedResult = translatorInstance.
translate(input);
257 if (translatedResult.isEmpty()) {
258 return Bundle.TranslatedContentViewer_emptyTranslation();
260 return translatedResult;
262 return Bundle.TranslatedContentViewer_noServiceProvider();
264 logger.log(Level.WARNING,
"Error translating text", ex);
265 return Bundle.TranslatedContentViewer_translationException() +
" (" + ex.getMessage() +
")";
287 boolean isImage =
false;
289 if (source != null) {
290 isImage = source.getMIMEType().toLowerCase().startsWith(
"image/");
293 updateExtractionLoadingMessage(isImage);
298 result = extractText(source, OCR_ENABLED);
300 result = extractText(source, OCR_DISABLED);
304 byte[] resultInUTF8Bytes = result.getBytes(
"UTF8");
305 byte[] trimToArraySize = Arrays.copyOfRange(resultInUTF8Bytes, 0,
306 Math.min(resultInUTF8Bytes.length, MAX_EXTRACT_SIZE_BYTES) );
307 return new String(trimToArraySize,
"UTF-8");
324 Reader textExtractor = getTextExtractor(source, ocrEnabled);
326 char[] cbuf =
new char[8096];
327 StringBuilder textBuilder =
new StringBuilder();
334 while ((read = textExtractor.read(cbuf)) != -1) {
335 if (this.isCancelled()) {
336 throw new InterruptedException();
341 int bytesLeft = MAX_EXTRACT_SIZE_BYTES - bytesRead;
343 if (bytesLeft < read) {
344 textBuilder.append(cbuf, 0, bytesLeft);
345 return textBuilder.toString();
348 textBuilder.append(cbuf, 0, read);
352 return textBuilder.toString();
370 Lookup context = null;
376 String ocrSelection = panel.getSelectedOcrLanguagePack();
377 if (!ocrSelection.isEmpty()) {
384 context = Lookups.fixed(imageConfig, terminator);
402 public String currentSelection = null;
404 public abstract String getSelection();
408 String selection = getSelection();
409 if (!selection.equals(currentSelection)) {
410 currentSelection = selection;
411 if (updateTask != null && !updateTask.isDone()) {
412 updateTask.cancel(
true);
418 executorService.execute(updateTask);
430 return panel.getDisplayDropDownSelection();
441 return panel.getSelectedOcrLanguagePack();
void setNode(final Node node)
void updateExtractionLoadingMessage(boolean isImage)
synchronized int getMaxTextChars()
String extractText(AbstractFile source, boolean ocrEnabled)
static Content getDefaultContent(Node node)
synchronized String translate(String input)
void display(String text, ComponentOrientation direction, int font)
boolean isSupported(Node node)
final void actionPerformed(ActionEvent e)
volatile BackgroundTranslationTask updateTask
static TextTranslationService getInstance()
String getFileText(Node node)
String translate(String input)
synchronized boolean hasProvider()
synchronized static Logger getLogger(String name)
static ComponentOrientation getTextDirection(String text)
Reader getTextExtractor(AbstractFile file, boolean ocrEnabled)
TextViewer createInstance()
int isPreferred(Node node)