implemented handling of jpeg files
This commit is contained in:
2
pom.xml
2
pom.xml
@@ -4,7 +4,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>BelegScanner</groupId>
|
||||
<artifactId>BelegScanner</artifactId>
|
||||
<version>1.0.7</version>
|
||||
<version>1.0.8</version>
|
||||
|
||||
<name>BelegScanner</name>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -8,6 +8,7 @@ current resolution : aktuelle Auflösung
|
||||
DAY : TAG
|
||||
Directory : Ordner
|
||||
display preview : Vorschau anzeigen
|
||||
drop file : Datei löschen
|
||||
done : fertig
|
||||
Error code\: {} for {} : Fehlercode: {} für {}
|
||||
Finished text recognition. : Texterkennung beendet.
|
||||
@@ -25,6 +26,7 @@ Scan failed. : Scannen fehlgeschlagen.
|
||||
Scanning… : Scanne…
|
||||
scan! : scannen!
|
||||
State : Status
|
||||
Target type : Zieltyp
|
||||
{} terminated\: : {} abgebrochen:
|
||||
Text recognition… : Texterkennung…
|
||||
This application requires the following helper commands to be installed\: : Diese Anwendung erfordert es, dass auch die folgenden Hilfsprogramme installiert sind:
|
||||
|
||||
@@ -3,8 +3,12 @@ package de.srsoftware.belegscanner;
|
||||
public class Constants {
|
||||
|
||||
public static final String APPLICATION_NAME = "BelegScanner";
|
||||
public static final String FILE_BROWSER = "thunar";
|
||||
public static final String CONFIG_TARGET = "app.main.target";
|
||||
public static final String DEFAULT_WIDTH = "209";
|
||||
public static final String DEFAULT_HEIGHT = "297";
|
||||
public static final String FILE_BROWSER = "thunar";
|
||||
public static final String JPG = "jpeg";
|
||||
public static final String OCR = "pdf+ocr";
|
||||
public static final String PDF = "pdf";
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package de.srsoftware.belegscanner.gui;
|
||||
|
||||
import static de.srsoftware.belegscanner.Application.t;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.Font;
|
||||
@@ -29,7 +31,6 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import de.srsoftware.belegscanner.Constants;
|
||||
import static de.srsoftware.belegscanner.Application.*;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -40,13 +41,12 @@ import static de.srsoftware.belegscanner.Application.*;
|
||||
public class DocTable extends JPanel{
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DocTable.class);
|
||||
private static FilenameFilter PAGES = (dir,name) -> name.toLowerCase().endsWith("pdf") && name.toLowerCase().startsWith("page");
|
||||
private static FilenameFilter PDFS = (dir,name) -> name.toLowerCase().endsWith("pdf");
|
||||
private static FilenameFilter PAGES = (dir,name) -> name.toLowerCase().endsWith("pdf") && name.toLowerCase().contains("page");
|
||||
private static int rowCounter = 0;
|
||||
private static Font btnFont = new Font("Arial", Font.PLAIN, 28);
|
||||
|
||||
public interface PreviewListener{
|
||||
public void show(String filename);
|
||||
public void show(Path filename);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,13 +78,12 @@ public class DocTable extends JPanel{
|
||||
buttons.add(joinButton = button("⎗",t("join PDFs"),ev -> joinDocs(path)));
|
||||
buttons.add(button("🖻",t("display preview"),ev -> preview(path)));
|
||||
buttons.add(button("✓",t("done"),ev -> drop(this)));
|
||||
buttons.add(button("❌",t("drop file"),ev -> dropFile(path)));
|
||||
|
||||
constraints.gridx = 2;
|
||||
add(buttons,constraints);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* drop the selected document from the table
|
||||
* @param row
|
||||
@@ -181,6 +180,18 @@ public class DocTable extends JPanel{
|
||||
return btn;
|
||||
}
|
||||
|
||||
private static int compareFiles(String file1, String file2) {
|
||||
return timestampFrom(file1) < timestampFrom(file2) ? 1 : -1;
|
||||
}
|
||||
|
||||
private void dropFile(String path) {
|
||||
Path latest = latestFile(path);
|
||||
if (latest != null) {
|
||||
latest.toFile().delete();
|
||||
preview(path); // show lates of remaining files
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* join documents in given path
|
||||
* @param path
|
||||
@@ -217,6 +228,14 @@ public class DocTable extends JPanel{
|
||||
setState(path,t("PDFs joined."));
|
||||
preview(path);
|
||||
}
|
||||
|
||||
private Path latestFile(String path) {
|
||||
File folder = new File(path);
|
||||
if (!folder.exists()) return null;
|
||||
List<String> files = Arrays.asList(folder.list());
|
||||
String latest = files.stream().sorted(DocTable::compareFiles).findFirst().orElse(null);
|
||||
return latest == null ? null : Path.of(path,latest);
|
||||
}
|
||||
|
||||
/**
|
||||
* open given path in external file browser
|
||||
@@ -245,11 +264,7 @@ public class DocTable extends JPanel{
|
||||
*/
|
||||
public void preview(String path) {
|
||||
LOG.debug("preview({})",path);
|
||||
File folder = new File(path);
|
||||
if (!folder.exists()) return;
|
||||
List<String> pdfs = Arrays.asList(folder.list(PDFS));
|
||||
LOG.debug("PDFs: {}",pdfs);
|
||||
if (!pdfs.isEmpty()) previewListeners.forEach(l -> l.show(Path.of(path,pdfs.get(0)).toString()));
|
||||
previewListeners.forEach(l -> l.show(latestFile(path)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -260,5 +275,12 @@ public class DocTable extends JPanel{
|
||||
public void setState(String path, String state) {
|
||||
Row row = rows.get(path);
|
||||
if (row != null) row.status(state);
|
||||
}
|
||||
|
||||
private static long timestampFrom(String filename) {
|
||||
try {
|
||||
return Long.parseLong(filename.split("\\.",2)[0]);
|
||||
} catch (Exception e) {}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,35 @@
|
||||
package de.srsoftware.belegscanner.gui;
|
||||
|
||||
import static de.srsoftware.belegscanner.Application.t;
|
||||
import static de.srsoftware.belegscanner.Constants.CONFIG_TARGET;
|
||||
import static de.srsoftware.belegscanner.Constants.FILE_BROWSER;
|
||||
import static de.srsoftware.belegscanner.Constants.JPG;
|
||||
import static de.srsoftware.belegscanner.Constants.PDF;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Image;
|
||||
import java.awt.Point;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Vector;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
@@ -29,10 +44,7 @@ import org.json.JSONArray;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static de.srsoftware.belegscanner.Application.*;
|
||||
import de.srsoftware.belegscanner.Configuration;
|
||||
import de.srsoftware.belegscanner.Constants;
|
||||
|
||||
|
||||
/**
|
||||
* The "guts" of the application:
|
||||
@@ -46,7 +58,7 @@ public class MainFrame extends JFrame {
|
||||
private static final long serialVersionUID = 210283601541223645L;
|
||||
|
||||
private static final String HOME = System.getProperty("user.home");
|
||||
private static final Pattern MARKEN = Pattern.compile("\\$([a-zA-Z]+)");
|
||||
private static final Pattern MARKEN = Pattern.compile("\\$([a-zäöüA-ZÄÖÜ]+)");
|
||||
|
||||
private static final String APP_WIDTH = "app.main.dimension.w";
|
||||
|
||||
@@ -83,6 +95,10 @@ public class MainFrame extends JFrame {
|
||||
|
||||
private boolean isScanning = false;
|
||||
|
||||
private JComponent previewComponent = null;
|
||||
|
||||
private JPanel pdfPreview;
|
||||
|
||||
/**
|
||||
* Create application main frame.
|
||||
* @param config
|
||||
@@ -103,7 +119,9 @@ public class MainFrame extends JFrame {
|
||||
add(toolbar,BorderLayout.EAST);
|
||||
add(statusBar,BorderLayout.SOUTH);
|
||||
add(docTable,BorderLayout.NORTH);
|
||||
add(preview(),BorderLayout.CENTER);
|
||||
pdfPreview = preview();
|
||||
add(previewComponent = pdfPreview,BorderLayout.CENTER);
|
||||
|
||||
toolbar.addCategoryListener(this::setCategory)
|
||||
.addDateListener(this::setDate)
|
||||
.addDimensionListener(this::setDimension)
|
||||
@@ -138,7 +156,7 @@ public class MainFrame extends JFrame {
|
||||
};
|
||||
|
||||
public void checkDependencies() {
|
||||
List<String> missing = List.of("convert --version", "killall --version", "pdfsandwich -version", "pdftk --version", "scanimage --version",Constants.FILE_BROWSER+" --version").stream().map(this::appMissing).filter(app -> app != null).collect(Collectors.toList());
|
||||
List<String> missing = List.of("convert --version", "killall --version", "pdfsandwich -version", "pdftk --version", "scanimage --version",FILE_BROWSER+" --version").stream().map(this::appMissing).filter(app -> app != null).collect(Collectors.toList());
|
||||
if (!missing.isEmpty()) notifyAppsMissing(missing);
|
||||
}
|
||||
|
||||
@@ -185,6 +203,9 @@ public class MainFrame extends JFrame {
|
||||
*/
|
||||
private void performScan(String path, Dimension dimension) {
|
||||
LOG.debug("performScan({})",path);
|
||||
/* We need to all required config values before starting to scan, because the might be changed during scanning */
|
||||
String target = config.getOrCreate(CONFIG_TARGET, "pdf");
|
||||
|
||||
setScanning(true);
|
||||
|
||||
File folder = new File(path);
|
||||
@@ -230,8 +251,13 @@ public class MainFrame extends JFrame {
|
||||
LOG.error(t("{} terminated: "),builder,e);
|
||||
}
|
||||
setScanning(false);
|
||||
if (JPG.equals(target)) {
|
||||
docTable.setState(path, t("Image scanned"));
|
||||
docTable.preview(path);
|
||||
return;
|
||||
};
|
||||
|
||||
String pdf = "page."+timestamp+".pdf";
|
||||
String pdf = timestamp+".page.pdf";
|
||||
cmd = new Vector<>();
|
||||
cmd.add("convert");
|
||||
cmd.add(raw);
|
||||
@@ -253,8 +279,13 @@ public class MainFrame extends JFrame {
|
||||
}
|
||||
|
||||
Path.of(path, raw).toFile().delete();
|
||||
|
||||
String ocr = "page."+timestamp+".ocr.pdf";
|
||||
if (PDF.equals(target)) {
|
||||
docTable.setState(path, t("PDF created"));
|
||||
docTable.preview(path);
|
||||
return;
|
||||
}
|
||||
|
||||
String ocr = timestamp+".page.ocr.pdf";
|
||||
|
||||
cmd = new Vector<>();
|
||||
cmd.add("pdfsandwich");
|
||||
@@ -319,9 +350,37 @@ public class MainFrame extends JFrame {
|
||||
* open file in preview panel
|
||||
* @param file
|
||||
*/
|
||||
private void preview(String file) {
|
||||
pdfViewer.openDocument(file);
|
||||
pdfViewer.setPageFitMode(DocumentViewController.PAGE_FIT_WINDOW_HEIGHT, false);
|
||||
private void preview(Path filePath) {
|
||||
if (filePath == null) return;
|
||||
String file = filePath.toString();
|
||||
if (file.toLowerCase().endsWith("pdf")) {
|
||||
if (previewComponent != pdfPreview) {
|
||||
remove(previewComponent);
|
||||
previewComponent = pdfPreview;
|
||||
add(previewComponent,BorderLayout.CENTER);
|
||||
pack();
|
||||
}
|
||||
pdfViewer.openDocument(file);
|
||||
pdfViewer.setPageFitMode(DocumentViewController.PAGE_FIT_WINDOW_HEIGHT, false);
|
||||
}
|
||||
if (file.toLowerCase().endsWith("jpg")) {
|
||||
try {
|
||||
Image img = ImageIO.read(new File(file));
|
||||
Dimension dim = previewComponent.getSize();
|
||||
double w = img.getWidth(null);
|
||||
double h = img.getHeight(null);
|
||||
double factor = Math.min(dim.width/w, dim.height/h);
|
||||
if (factor != 1.0) img = img.getScaledInstance((int)(w*factor), (int)(h*factor), Image.SCALE_DEFAULT);
|
||||
ImageIcon icon = new ImageIcon(img);
|
||||
JLabel label = new JLabel(icon);
|
||||
this.remove(previewComponent);
|
||||
previewComponent = label;
|
||||
this.add(previewComponent, BorderLayout.CENTER);
|
||||
pack();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Was not able to preview {}:",file,e);
|
||||
}
|
||||
}
|
||||
fileName.setText(file);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ import de.srsoftware.tools.gui.DateChooser;
|
||||
import de.srsoftware.tools.gui.SelectComboBox;
|
||||
|
||||
import static de.srsoftware.belegscanner.Application.*;
|
||||
import static de.srsoftware.belegscanner.Constants.*;
|
||||
|
||||
/**
|
||||
* create the application's toolbar
|
||||
@@ -95,7 +96,7 @@ public class Toolbar extends JPanel {
|
||||
private JTextField height;
|
||||
private SelectComboBox catPicker;
|
||||
private int resolution = 150;
|
||||
|
||||
|
||||
/**
|
||||
* create toolbar
|
||||
* @param config
|
||||
@@ -108,6 +109,7 @@ public class Toolbar extends JPanel {
|
||||
add(input(t("Path"),pathPicker = pathPicker()));
|
||||
addFormatSelector();
|
||||
add(resolutionSelector());
|
||||
add(typeSelector());
|
||||
add(scanButton());
|
||||
add(Box.createGlue());
|
||||
Arrays.stream(getComponents()).filter(c -> c instanceof JPanel).map(JPanel.class::cast).forEach(p -> p.setBorder(BORDER));
|
||||
@@ -372,7 +374,7 @@ public class Toolbar extends JPanel {
|
||||
}
|
||||
|
||||
/**
|
||||
* create a small for to select the resolution for scanning
|
||||
* create a radio group to select the resolution for scanning
|
||||
* @return
|
||||
*/
|
||||
private JPanel resolutionSelector() {
|
||||
@@ -405,6 +407,42 @@ public class Toolbar extends JPanel {
|
||||
scanListeners.forEach(l->l.actionPerformed(evt));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* create one radio button for the typeSelector
|
||||
* @param group
|
||||
* @param label
|
||||
* @param i
|
||||
* @return
|
||||
*/
|
||||
private JRadioButton typeButton(ButtonGroup group, String label,String selection) {
|
||||
JRadioButton btn = new JRadioButton(label);
|
||||
group.add(btn);
|
||||
btn.addActionListener(ev -> config.set(CONFIG_TARGET, label));
|
||||
if (label.equals(selection)) btn.setSelected(true);
|
||||
return btn;
|
||||
}
|
||||
|
||||
/**
|
||||
* create a radio group to select the target type for scanning
|
||||
* @param jsonObject
|
||||
* @return
|
||||
*/
|
||||
private JPanel typeSelector() {
|
||||
ButtonGroup group = new ButtonGroup();
|
||||
JPanel typeSelector = new JPanel(new BorderLayout());
|
||||
JLabel label = new JLabel(t("Target type"));
|
||||
String target = config.getOrCreate(CONFIG_TARGET, PDF);
|
||||
typeSelector.add(label, BorderLayout.NORTH);
|
||||
typeSelector.add(typeButton(group,t(JPG),target),BorderLayout.WEST);
|
||||
typeSelector.add(typeButton(group,t(PDF),target),BorderLayout.CENTER);
|
||||
typeSelector.add(typeButton(group,t(OCR),target),BorderLayout.EAST);
|
||||
typeSelector.setMaximumSize(new Dimension(600,200));
|
||||
return typeSelector;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* category updated: notify listeners and update dependent fields
|
||||
* @param newCat
|
||||
|
||||
Reference in New Issue
Block a user