Werkzeug um Belege zu scannen, Texterkennung durchzuführen und Belege sortiert abzulegen
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

310 lines
8.3 KiB

package de.srsoftware.belegscanner.gui;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
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 javax.swing.JFrame;
import org.json.JSONArray;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import de.srsoftware.belegscanner.Configuration;
public class MainFrame extends JFrame {
private static final Logger LOG = LoggerFactory.getLogger(MainFrame.class);
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 FilenameFilter PAGES = (dir,name) -> name.toLowerCase().endsWith("pdf") && name.toLowerCase().startsWith("page");
private StatusBar statusBar;
private Toolbar toolbar;
private HashMap<String,String> fields = new HashMap<>();
private String path = "";
private String category ="";
private Date date = new Date();
private String patchedPath = "";
private Configuration config;
private int xSize = 209;
private int ySize = 297;
private int resoultion = 150;
private String mode = "Color";
public MainFrame(Configuration config) {
super("BelegScanner");
this.config = config;
int width = config.getOrCreate("app.main.dimenstion.w",800);
int height = config.getOrCreate("app.main.dimenstion.h",600);
BorderLayout layout = new BorderLayout();
setLayout(layout);
toolbar = new Toolbar(config);
statusBar = new StatusBar();
add(toolbar,BorderLayout.EAST);
add(statusBar,BorderLayout.SOUTH);
toolbar.addCategoryListener(this::setCategory)
.addDateListener(this::setDate)
.addFieldListener(this::setField)
.addFolderListener(this::openFolder)
.addJoinListener(this::join)
.addPathListener(this::setPath)
.addScanListener(this::scan);
int x = config.getOrCreate("app.main.position.x", 20);
int y = config.getOrCreate("app.main.position.y", 20);
setLocation(new Point(x, y));
setPreferredSize(new Dimension(width,height));
pack();
setVisible(true);
}
private void addFieldsFor(String path) {
Vector<String> marks = new Vector<>();
Matcher matches = MARKEN.matcher(path);
while (matches.find()) marks.add(matches.group(1));
toolbar.addFieldsFor(marks);
};
private void join(ActionEvent ev) {
new Thread(() -> joinFiles(patchedPath)).start();
}
private void joinFiles(String path) {
LOG.debug("joinFiles({})",path);
File folder = new File(path);
if (!folder.exists()) return;
List<String> pdfs = Arrays.asList(folder.list(PAGES));
Collections.sort(pdfs);
Vector<String> cmd = new Vector<>();
cmd.add("pdftk");
cmd.addAll(pdfs);
cmd.add("cat");
cmd.add("output");
cmd.add(folder.getName()+".pdf");
LOG.debug("executing {}",cmd);
ProcessBuilder builder = new ProcessBuilder(cmd);
builder.directory(folder);
try {
Process process = builder.start();
statusBar.setAction("Verbinde PDFs…");
int errorCode = process.waitFor();
if (errorCode != 0) {
LOG.error("error code: {} for {}",errorCode,cmd);
} else LOG.debug("error code: {}",errorCode);
} catch (InterruptedException | IOException e) {
LOG.error("{} terminated: ",builder,e);
}
for (String page : pdfs) Path.of(path,page).toFile().delete();
statusBar.setAction("Bereit.");
}
private void openFolder(ActionEvent ev) {
new Thread(() -> openFolder(patchedPath)).start();
}
private void openFolder(String path) {
File folder = new File(path);
if (!folder.exists()) return;
ProcessBuilder builder = new ProcessBuilder(List.of("killall","thunar"));
builder.directory(folder);
try {
builder.start().waitFor();
} catch (IOException | InterruptedException e) {
LOG.error("{} terminated: ",builder,e);
}
builder = new ProcessBuilder("thunar");
builder.directory(folder);
try {
builder.start();
} catch (IOException e) {
LOG.error("{} terminated: ",builder,e);
}
}
private void performScan(String path) {
LOG.debug("performScan({})",path);
toolbar.readyToScan(false);
File folder = new File(path);
if (!folder.exists()) {
LOG.warn("Path '{}' does not exist!",path);
folder.mkdirs();
}
long timestamp = new Date().getTime();
String raw = timestamp+".jpg";
Vector<String> cmd = new Vector<>();
cmd.add("scanimage");
cmd.add("-x");
cmd.add(xSize+"");
cmd.add("-y");
cmd.add(ySize+"");
cmd.add("--mode");
cmd.add(mode);
cmd.add("--resolution");
cmd.add(resoultion+"");
cmd.add("-o");
cmd.add(raw);
LOG.debug("executing {}",cmd);
ProcessBuilder builder = new ProcessBuilder(cmd);
builder.directory(folder);
try {
Process process = builder.start();
statusBar.setAction("Scanne nach "+path+"…");
int errorCode = process.waitFor();
if (errorCode != 0) {
LOG.error("error code: {} for {}",errorCode,cmd);
} else LOG.debug("error code: {}",errorCode);
} catch (InterruptedException | IOException e) {
LOG.error("{} terminated: ",builder,e);
}
String pdf = "page."+timestamp+".pdf";
cmd = new Vector<>();
cmd.add("convert");
cmd.add(raw);
cmd.add(pdf);
builder = new ProcessBuilder(cmd);
builder.directory(folder);
try {
Process process = builder.start();
statusBar.setAction("Kovertiere zu PDF…");
toolbar.readyToScan(true);
int errorCode = process.waitFor();
if (errorCode != 0) {
LOG.error("error code: {} for {}",errorCode,cmd);
} else LOG.debug("error code: {}",errorCode);
} catch (InterruptedException | IOException e) {
LOG.error("{} terminated: ",builder,e);
}
Path.of(path, raw).toFile().delete();
String ocr = "page."+timestamp+".ocr.pdf";
cmd = new Vector<>();
cmd.add("pdfsandwich");
cmd.add("-lang");
cmd.add("deu");
cmd.add("-rgb");
cmd.add("-o");
cmd.add(ocr);
cmd.add(pdf);
builder = new ProcessBuilder(cmd);
builder.directory(folder);
try {
Process process = builder.start();
statusBar.setAction("Texterkennung…");
toolbar.readyToScan(true);
int errorCode = process.waitFor();
if (errorCode != 0) {
LOG.error("error code: {} for {}",errorCode,cmd);
} else LOG.debug("error code: {}",errorCode);
} catch (InterruptedException | IOException e) {
LOG.error("{} terminated: ",builder,e);
}
Path.of(path, pdf).toFile().delete();
statusBar.setAction("Bereit.");
}
private void scan(ActionEvent ev) {
updateConfig();
new Thread(() -> performScan(patchedPath)).start();
}
private void setCategory(String category) {
this.category = category;
updatePath();
}
private void setDate(Date date) {
this.date = date;
updatePath();
}
private void setField(String key, String val) {
if (val == null || val.isEmpty()) {
fields.remove(key);
} else fields.put(key, val);
updatePath();
}
private void setPath(String path) {
this.path = path;
updatePath();
}
private void updateConfig() {
String prefix = "app.categories."+category+".";
for (Entry<String, String> entry : fields.entrySet()) {
String key = entry.getKey();
String val = entry.getValue();
JSONArray arr = config.getOrCreateArray(prefix+"fields."+key);
HashSet<Object> existing = new HashSet<>();
arr.forEach(existing::add);
if (!existing.contains(val)) arr.put(val);
}
config.set(prefix+"path",path);
Point loc = getLocation();
config.set("app.main.position.x", loc.x);
config.set("app.main.position.y", loc.y);
}
@SuppressWarnings("deprecation")
private void updatePath() {
LOG.debug("updatePath() [path = {}]",path);
int year = 1900+date.getYear();
int month = date.getMonth()+1;
int day = date.getDate();
patchedPath = path.replace("$HOME",HOME)
.replace("$KATEGORIE", category)
.replace("$JAHR", year+"")
.replace("$MONAT", month<10 ? "0"+month : ""+month)
.replace("$TAG", day < 10 ? "0"+day : ""+day);
addFieldsFor(patchedPath);
for (Entry<String, String> entry : fields.entrySet()) patchedPath = patchedPath.replace("$"+entry.getKey(), entry.getValue());
statusBar.setPath(patchedPath);
}
}