Belegscanner ist jetzt wieder funktionial, aber folgende Funktionen fehlen noch:
- aktuelle Einstellungen werden beim Scannen nicht gespeichert - Seitenzähler wird nicht direkt beim reaktivieren des Scan-Buttons hochgezählt, sondern erst wenn die komplette Verarbeitung der aktuellen Seite durch ist Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
package de.srsoftware.belegscanner;
|
||||
|
||||
import de.srsoftware.belegscanner.gui.DocTable;
|
||||
import de.srsoftware.belegscanner.gui.MainFrame;
|
||||
import de.srsoftware.belegscanner.gui.StatusBar;
|
||||
import de.srsoftware.belegscanner.gui.Toolbar;
|
||||
import de.srsoftware.belegscanner.model.ScanProject;
|
||||
import de.srsoftware.belegscanner.model.api.Page;
|
||||
import de.srsoftware.belegscanner.model.api.Project;
|
||||
import de.srsoftware.tools.gui.DateChooser;
|
||||
import de.srsoftware.tools.gui.SelectComboBox;
|
||||
@@ -12,8 +13,13 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Optional;
|
||||
import java.util.Vector;
|
||||
|
||||
import static de.srsoftware.belegscanner.Application.main;
|
||||
import static de.srsoftware.belegscanner.Application.t;
|
||||
import static de.srsoftware.belegscanner.Constants.CONFIG_TARGET;
|
||||
import static de.srsoftware.belegscanner.Constants.JPG;
|
||||
@@ -23,15 +29,20 @@ import static de.srsoftware.belegscanner.Constants.PDF;
|
||||
public class Worker {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Worker.class);
|
||||
private static final String HOME = System.getProperty("user.home");
|
||||
private static final String YEAR = t("YEAR");
|
||||
private static final String PAGE = t(Constants.PAGE);
|
||||
private final Configuration config;
|
||||
private SelectComboBox pathPicker;
|
||||
private StatusBar statusBar;
|
||||
private String category;
|
||||
private DateChooser datePicker;
|
||||
private String rawPath, resolvedPath;
|
||||
private String category,rawPath, resolvedPath;
|
||||
private Toolbar toolbar;
|
||||
private DocTable docTable;
|
||||
private boolean improveBrightness,scanning;
|
||||
private int resolution;
|
||||
private Dimension dimension;
|
||||
private String mode = "Color";
|
||||
private MainFrame mainFrame;
|
||||
private int rotationAngle;
|
||||
|
||||
public Worker(Configuration config) {
|
||||
this.config = config;
|
||||
@@ -72,7 +83,7 @@ public class Worker {
|
||||
if (end>start+1){
|
||||
var variable = resolvedPath.substring(start+1, end);
|
||||
LOG.debug("…unresolved '{}'",variable);
|
||||
String value = toolbar.requireField(variable);
|
||||
var value = PAGE.equals(variable) ? "["+variable+"]":toolbar.requireField(variable);
|
||||
variable = "$"+variable;
|
||||
LOG.debug("replacing '{}' in '{}' by '{}'",variable,resolvedPath,value);
|
||||
resolvedPath = resolvedPath.replace(variable,value);
|
||||
@@ -82,8 +93,7 @@ public class Worker {
|
||||
}
|
||||
}
|
||||
toolbar.dropUnusedAdditionalFields();
|
||||
resolvedPath+="."+extension();
|
||||
statusBar.setPath(resolvedPath);
|
||||
statusBar.setPath(resolvedPath+"."+extension());
|
||||
}
|
||||
|
||||
private String extension() {
|
||||
@@ -106,16 +116,235 @@ public class Worker {
|
||||
}
|
||||
|
||||
public void scan() {
|
||||
LOG.debug("scan()");
|
||||
var project = docTable.getProject(resolvedPath).orElseGet(this::createProject);
|
||||
new Thread(this::performScan).start();
|
||||
}
|
||||
|
||||
private Project createProject() {
|
||||
return new ScanProject(resolvedPath);
|
||||
private void performScan(){
|
||||
LOG.debug("scan()");
|
||||
var project = docTable.getProject(resolvedPath).orElseGet(() -> docTable.createProject(resolvedPath));
|
||||
int rotation = this.rotationAngle;
|
||||
File targetFile = prepareProject(project);
|
||||
var type = getType();
|
||||
scanJpeg(targetFile,project);
|
||||
improveJpeg(targetFile,project,rotation);
|
||||
|
||||
if (JPG.equals(type)) {
|
||||
docTable.setState(project, t("Image scanned"));
|
||||
var page = project.addPage(new File(targetFile+".jpg"));
|
||||
preview(page);
|
||||
return;
|
||||
};
|
||||
mainFrame.preview(new File(targetFile+".jpg"));
|
||||
convertToPdf(targetFile,type, project);
|
||||
|
||||
if (PDF.equals(type)) {
|
||||
var page = project.addPage(new File(targetFile+".pdf"));
|
||||
docTable.setState(project, t("PDF created"));
|
||||
preview(page);
|
||||
return;
|
||||
}
|
||||
|
||||
performTextRecognition(targetFile,project);
|
||||
|
||||
var page = project.addPage(new File(targetFile+".pdf"));
|
||||
preview(page);
|
||||
}
|
||||
|
||||
private void performTextRecognition(File targetFile, Project project) {
|
||||
String pdf = targetFile.getName() + ".tmp.pdf";
|
||||
String ocr = targetFile.getName() + ".pdf";
|
||||
File folder = targetFile.getParentFile();
|
||||
var cmd = new Vector<String>();
|
||||
cmd.add("pdfsandwich");
|
||||
cmd.add("-lang");
|
||||
cmd.add("deu");
|
||||
cmd.add("-rgb");
|
||||
if (resolution>300) {
|
||||
cmd.add("-resolution");
|
||||
cmd.add(""+resolution);
|
||||
}
|
||||
cmd.add("-o");
|
||||
cmd.add(ocr);
|
||||
cmd.add(pdf);
|
||||
LOG.debug("executing {}",cmd);
|
||||
var builder = new ProcessBuilder(cmd);
|
||||
builder.directory(folder);
|
||||
try {
|
||||
Process process = builder.start();
|
||||
docTable.setState(project,t("Text recognition…"));
|
||||
int errorCode = process.waitFor();
|
||||
if (errorCode != 0) {
|
||||
LOG.error("error code: {} for {}",errorCode,cmd);
|
||||
docTable.setState(project, t("OCR failed."));
|
||||
return;
|
||||
}
|
||||
if (!folder.toPath().resolve(ocr).toFile().exists()){
|
||||
docTable.setState(project,t("OCR failed."));
|
||||
return;
|
||||
}
|
||||
} catch (InterruptedException | IOException e) {
|
||||
LOG.error("{} terminated: ",builder,e);
|
||||
}
|
||||
|
||||
LOG.debug("removing {}",pdf);
|
||||
folder.toPath().resolve(pdf).toFile().delete();
|
||||
docTable.setState(project,t("Finished text recognition."));
|
||||
}
|
||||
|
||||
private void convertToPdf(File targetFile, String type, Project project) {
|
||||
var extension = PDF.equals(type) ? ".pdf" : ".tmp.pdf";
|
||||
var raw = rawFile(targetFile);
|
||||
String pdf = targetFile.getName() + extension;
|
||||
File folder = targetFile.getParentFile();
|
||||
var cmd = new Vector<String>();
|
||||
cmd.add("convert");
|
||||
cmd.add(raw);
|
||||
cmd.add(pdf);
|
||||
var builder = new ProcessBuilder(cmd);
|
||||
builder.directory(folder);
|
||||
try {
|
||||
Process process = builder.start();
|
||||
docTable.setState(project,t("Converting to PDF…"));
|
||||
int errorCode = process.waitFor();
|
||||
if (errorCode != 0) {
|
||||
LOG.error("Error code: {} for {}",errorCode,cmd);
|
||||
docTable.setState(project, t("Conversion failed."));
|
||||
return;
|
||||
}
|
||||
|
||||
} catch (InterruptedException | IOException e) {
|
||||
LOG.error("{} terminated: ",builder,e);
|
||||
}
|
||||
LOG.debug("removing {}",raw);
|
||||
folder.toPath().resolve(raw).toFile().delete();
|
||||
}
|
||||
|
||||
private String rawFile(File targetFile) {
|
||||
return targetFile.getName()+".jpg";
|
||||
}
|
||||
|
||||
private void improveJpeg(File target, Project project, int rotation) {
|
||||
var raw = rawFile(target);
|
||||
File folder = target.getParentFile();
|
||||
if (improveBrightness) {
|
||||
Vector<String> cmd = new Vector<>();
|
||||
cmd.add("mogrify");
|
||||
cmd.add("-contrast-stretch");
|
||||
cmd.add("0x60%");
|
||||
cmd.add(raw);
|
||||
var builder = new ProcessBuilder(cmd);
|
||||
builder.directory(folder);
|
||||
try {
|
||||
Process process = builder.start();
|
||||
docTable.setState(project,t("Adjusting brightness…"));
|
||||
int errorCode = process.waitFor();
|
||||
if (errorCode != 0) {
|
||||
LOG.error(t("Error code: {} for {}"),errorCode,cmd);
|
||||
docTable.setState(project, t("Adjustment failed."));
|
||||
setScanning(false);
|
||||
return;
|
||||
}
|
||||
} catch (InterruptedException | IOException e) {
|
||||
LOG.error(t("{} terminated: "),builder,e);
|
||||
}
|
||||
}
|
||||
if (rotation != 0){
|
||||
Vector<String> cmd = new Vector<>();
|
||||
cmd.add("mogrify");
|
||||
cmd.add("-rotate");
|
||||
cmd.add(""+rotation);
|
||||
cmd.add(raw);
|
||||
var builder = new ProcessBuilder(cmd);
|
||||
builder.directory(folder);
|
||||
try {
|
||||
Process process = builder.start();
|
||||
docTable.setState(project,t("Rotating image…"));
|
||||
int errorCode = process.waitFor();
|
||||
if (errorCode != 0) {
|
||||
LOG.error(t("Error code: {} for {}"),errorCode,cmd);
|
||||
docTable.setState(project, t("Rotation failed."));
|
||||
setScanning(false);
|
||||
return;
|
||||
}
|
||||
} catch (InterruptedException | IOException e) {
|
||||
LOG.error(t("{} terminated: "),builder,e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private File prepareProject(Project project) {
|
||||
var targetFile = project.nextFile();
|
||||
docTable.setState(project,t("Scanning…"));
|
||||
LOG.debug("scanning as {}",targetFile);
|
||||
File folder = targetFile.getParentFile();
|
||||
if (!folder.exists()) {
|
||||
LOG.warn(t("Path '{}' does not exist!"),folder);
|
||||
folder.mkdirs();
|
||||
}
|
||||
return targetFile;
|
||||
|
||||
}
|
||||
|
||||
private void scanJpeg(File targetFile, Project project) {
|
||||
setScanning(true);
|
||||
LOG.debug("scanning as {}",targetFile);
|
||||
File folder = targetFile.getParentFile();
|
||||
if (!folder.exists()) {
|
||||
LOG.warn(t("Path '{}' does not exist!"),folder);
|
||||
folder.mkdirs();
|
||||
}
|
||||
|
||||
var raw = rawFile(targetFile);
|
||||
|
||||
Vector<String> cmd = new Vector<>();
|
||||
cmd.add("scanimage");
|
||||
cmd.add("-x");
|
||||
cmd.add(dimension.width+"");
|
||||
cmd.add("-y");
|
||||
cmd.add(dimension.height+"");
|
||||
cmd.add("--mode");
|
||||
cmd.add(mode);
|
||||
cmd.add("--resolution");
|
||||
cmd.add(resolution+"");
|
||||
cmd.add("-o");
|
||||
cmd.add(raw);
|
||||
LOG.debug("executing {}",cmd);
|
||||
|
||||
ProcessBuilder builder = new ProcessBuilder(cmd);
|
||||
builder.directory(folder);
|
||||
try {
|
||||
Process process = builder.start();
|
||||
docTable.setState(project,t("Scanning…"));
|
||||
int errorCode = process.waitFor();
|
||||
if (errorCode != 0) {
|
||||
LOG.error(t("Error code: {} for {}"),errorCode,cmd);
|
||||
docTable.setState(project, t("Scan failed."));
|
||||
setScanning(false);
|
||||
throw new RuntimeException(t("Scanning failed, error code = {}",errorCode));
|
||||
}
|
||||
} catch (InterruptedException | IOException e) {
|
||||
LOG.error(t("{} terminated: "),builder,e);
|
||||
}
|
||||
setScanning(false);
|
||||
}
|
||||
|
||||
public void preview(Page page) {
|
||||
mainFrame.preview(page == null ? null : page.file());
|
||||
}
|
||||
|
||||
private void setScanning(boolean isScanning) {
|
||||
scanning = isScanning;
|
||||
if (scanning) {
|
||||
toolbar.disableScan();
|
||||
} else {
|
||||
toolbar.enableScan();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setDimension(Dimension dimension) {
|
||||
LOG.debug("setDimension({})",dimension);
|
||||
this.dimension = dimension;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
@@ -163,4 +392,70 @@ public class Worker {
|
||||
public void setDocTable(DocTable docTable) {
|
||||
this.docTable = docTable;
|
||||
}
|
||||
|
||||
public int setResolution(int resolution) {
|
||||
return this.resolution = resolution;
|
||||
}
|
||||
|
||||
public void setImproveBrightness(boolean newValue) {
|
||||
improveBrightness = newValue;
|
||||
}
|
||||
|
||||
public Worker setMainFrame(MainFrame mainFrame) {
|
||||
this.mainFrame = mainFrame;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setRotation(int angle) {
|
||||
LOG.debug("setRotation({})",angle);
|
||||
rotationAngle = angle;
|
||||
}
|
||||
|
||||
public void join(Project project) {
|
||||
File targetFile = prepareProject(project);
|
||||
|
||||
File folder = targetFile.getParentFile();
|
||||
var cmd = new Vector<String>();
|
||||
cmd.add("pdftk");
|
||||
var pages = project.pages();
|
||||
Integer firstPage = null;
|
||||
Integer lastPage = null;
|
||||
int count = 0;
|
||||
var joined = new ArrayList<Page>();
|
||||
for (var page : pages){
|
||||
count++;
|
||||
var name = page.file().getName();
|
||||
if (name.toLowerCase().endsWith(".pdf")) {
|
||||
if (firstPage == null) firstPage = count;
|
||||
lastPage = count;
|
||||
cmd.add(name);
|
||||
joined.add(page);
|
||||
}
|
||||
}
|
||||
cmd.add("cat");
|
||||
cmd.add("output");
|
||||
File joinedPDF = new File(project.pattern().replace("["+PAGE+"]",firstPage+"…"+lastPage+".pdf"));
|
||||
String target = joinedPDF.getName();
|
||||
cmd.add(target);
|
||||
|
||||
LOG.debug("executing {}",cmd);
|
||||
var builder = new ProcessBuilder(cmd);
|
||||
builder.directory(folder);
|
||||
try {
|
||||
Process process = builder.start();
|
||||
docTable.setState(project,t("Joining pdfs…"));
|
||||
int errorCode = process.waitFor();
|
||||
if (errorCode != 0) {
|
||||
LOG.error("error code: {} for {}",errorCode,cmd);
|
||||
docTable.setState(project, t("OCR failed."));
|
||||
return;
|
||||
}
|
||||
} catch (InterruptedException | IOException e) {
|
||||
LOG.error("{} terminated: ",builder,e);
|
||||
}
|
||||
joined.stream().map(Page::file).forEach(File::delete);
|
||||
pages.removeAll(joined);
|
||||
docTable.setState(project,t("PDFs joined."));
|
||||
preview(project.addPage(joinedPDF));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,24 +2,20 @@ package de.srsoftware.belegscanner.gui;
|
||||
|
||||
import static de.srsoftware.belegscanner.Application.t;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Insets;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.FilenameFilter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.MatteBorder;
|
||||
|
||||
import de.srsoftware.belegscanner.Worker;
|
||||
import de.srsoftware.belegscanner.listeners.ProjectListener;
|
||||
import de.srsoftware.belegscanner.model.ScanProject;
|
||||
import de.srsoftware.belegscanner.model.api.Project;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -36,6 +32,7 @@ public class DocTable extends JPanel{
|
||||
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);
|
||||
private final Worker worker;
|
||||
|
||||
private List<Project> projects = new ArrayList<>();
|
||||
|
||||
@@ -44,12 +41,78 @@ public class DocTable extends JPanel{
|
||||
private static final long serialVersionUID = 1073955198529023744L;
|
||||
|
||||
private GridBagConstraints constraints;
|
||||
private final HashMap<String,Row> rows = new HashMap<>();
|
||||
|
||||
private class Row{
|
||||
|
||||
private JLabel status;
|
||||
private JLabel pathLabel;
|
||||
private JPanel buttons;
|
||||
private Project project;
|
||||
private JButton joinButton;
|
||||
|
||||
public Row(Project project) {
|
||||
this.project = project;
|
||||
rows.put(project.pattern(), this);
|
||||
constraints.gridy = ++rowCounter;
|
||||
constraints.gridx = 0;
|
||||
add(pathLabel = new JLabel(project.nextFile().getParent()), constraints);
|
||||
constraints.gridx = 1;
|
||||
add(status = new JLabel("neu"),constraints);
|
||||
|
||||
buttons = new JPanel();
|
||||
buttons.setLayout(new FlowLayout());
|
||||
|
||||
buttons.add(button("⚁",t("open folder"),ev -> project.openDir()));
|
||||
buttons.add(joinButton = button("⎗",t("join PDFs"),ev -> worker.join(project)));
|
||||
buttons.add(button("✉",t("display preview"),ev -> worker.preview(project.lastPage())));
|
||||
buttons.add(button("✓",t("done"),ev -> drop(this)));
|
||||
buttons.add(button("❌",t("drop last page"),ev -> worker.preview(project.dropLastPage().lastPage())));
|
||||
|
||||
constraints.gridx = 2;
|
||||
add(buttons,constraints);
|
||||
}
|
||||
|
||||
/**
|
||||
* drop the selected document from the table
|
||||
* @param row
|
||||
*/
|
||||
private void drop(Row row) {
|
||||
if (row.joinButton.isEnabled()) project.join();
|
||||
remove(pathLabel);
|
||||
remove(status);
|
||||
remove(buttons);
|
||||
invalidate();
|
||||
repaint();
|
||||
rows.remove(project.pattern());
|
||||
projects.remove(project);
|
||||
}
|
||||
|
||||
/**
|
||||
* update status for selected document
|
||||
* @param status
|
||||
* @return
|
||||
*/
|
||||
public Row status(String status) {
|
||||
this.status.setText(status);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* remove the join button assigned with the selected document
|
||||
*/
|
||||
public void setJoinButton(boolean enabled) {
|
||||
joinButton.setEnabled(enabled);
|
||||
joinButton.setVisible(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create a new DocTable
|
||||
*/
|
||||
public DocTable(Worker worker) {
|
||||
setLayout(new GridBagLayout());
|
||||
this.worker = worker;
|
||||
worker.setDocTable(this);
|
||||
constraints = new GridBagConstraints();
|
||||
constraints.fill = GridBagConstraints.HORIZONTAL;
|
||||
@@ -70,7 +133,34 @@ public class DocTable extends JPanel{
|
||||
add(actions,constraints);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* helper method to create a button with given label, tooltip and action listener
|
||||
* @param label
|
||||
* @param tooltip
|
||||
* @param listener
|
||||
* @return
|
||||
*/
|
||||
private static JButton button(String label, String tooltip, ActionListener listener) {
|
||||
JButton btn = new JButton(label);
|
||||
btn.setFont(btnFont);
|
||||
btn.setToolTipText(tooltip);
|
||||
btn.addActionListener(listener);
|
||||
return btn;
|
||||
}
|
||||
public Project createProject(String resolvedPath) {
|
||||
var project = new ScanProject(resolvedPath);
|
||||
new Row(project);
|
||||
projects.add(project);
|
||||
return project;
|
||||
}
|
||||
public Optional<Project> getProject(String resolvedPath) {
|
||||
return projects.stream().filter(p -> resolvedPath.equals(p.pattern())).findAny();
|
||||
}
|
||||
|
||||
public void setState(Project project, String status) {
|
||||
var row = rows.get(project.pattern());
|
||||
if (row != null) row.status.setText(status);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,9 +18,11 @@ import static java.awt.BorderLayout.WEST;
|
||||
public class FormatSelector extends JPanel {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(FormatSelector.class);
|
||||
private final JTextField height, width;
|
||||
private final Worker worker;
|
||||
|
||||
public FormatSelector(Worker worker){
|
||||
setLayout(new BoxLayout(this,BoxLayout.Y_AXIS));
|
||||
this.worker = worker;
|
||||
JPanel widthSelector = new JPanel();
|
||||
widthSelector.setLayout(new BorderLayout());
|
||||
widthSelector.setMaximumSize(new Dimension(600, 40));
|
||||
@@ -44,9 +46,7 @@ public class FormatSelector extends JPanel {
|
||||
public void keyReleased(KeyEvent e) {
|
||||
LOG.debug("updateDimensions()");
|
||||
try {
|
||||
int w = Integer.parseInt(width.getText().trim());
|
||||
int h = Integer.parseInt(height.getText().trim());
|
||||
worker.setDimension(new Dimension(w < 0 ? 0 : w, h < 0 ? 0 : h));
|
||||
updateDim();
|
||||
} catch (NumberFormatException nfe) {
|
||||
LOG.warn("Invalid dimensions!");
|
||||
}
|
||||
@@ -55,5 +55,12 @@ public class FormatSelector extends JPanel {
|
||||
|
||||
width.addKeyListener(dimensionListener);
|
||||
height.addKeyListener(dimensionListener);
|
||||
updateDim();
|
||||
}
|
||||
|
||||
private void updateDim() {
|
||||
int w = Integer.parseInt(width.getText().trim());
|
||||
int h = Integer.parseInt(height.getText().trim());
|
||||
worker.setDimension(new Dimension(w < 0 ? 0 : w, h < 0 ? 0 : h));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package de.srsoftware.belegscanner.gui;
|
||||
|
||||
import de.srsoftware.belegscanner.Worker;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
@@ -11,7 +13,7 @@ public class ImproveSelector extends JPanel {
|
||||
|
||||
private boolean improveBrightness;
|
||||
|
||||
public ImproveSelector() {
|
||||
public ImproveSelector(Worker worker) {
|
||||
super(new BorderLayout());
|
||||
ButtonGroup group = new ButtonGroup();
|
||||
add(new JLabel(t("Adjustments")), NORTH);
|
||||
@@ -19,11 +21,8 @@ public class ImproveSelector extends JPanel {
|
||||
add(checkbox,WEST);
|
||||
setMaximumSize(new Dimension(600,200));
|
||||
checkbox.addActionListener(e -> {
|
||||
improveBrightness = checkbox.isSelected();
|
||||
worker.setImproveBrightness(checkbox.isSelected());
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public boolean improveBrightness(){
|
||||
return improveBrightness;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,11 @@ package de.srsoftware.belegscanner.gui;
|
||||
import de.srsoftware.belegscanner.Configuration;
|
||||
import de.srsoftware.belegscanner.Worker;
|
||||
import org.icepdf.ri.common.SwingController;
|
||||
import org.icepdf.ri.common.views.DocumentViewController;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@@ -13,6 +15,7 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
@@ -48,6 +51,7 @@ public class MainFrame extends JFrame {
|
||||
|
||||
private static final String POLICY_FILE = "/etc/ImageMagick-6/policy.xml";
|
||||
private final Worker worker;
|
||||
private JComponent previewComponent;
|
||||
|
||||
private StatusBar statusBar;
|
||||
private Toolbar toolbar;
|
||||
@@ -70,7 +74,7 @@ public class MainFrame extends JFrame {
|
||||
private Dimension dimension = new Dimension(209, 297);
|
||||
|
||||
|
||||
private JPanel pdfPreview;
|
||||
private Preview pdfPreview;
|
||||
|
||||
/**
|
||||
* Create application main frame.
|
||||
@@ -79,13 +83,13 @@ public class MainFrame extends JFrame {
|
||||
public MainFrame(Configuration config) {
|
||||
super("BelegScanner");
|
||||
setLayout(new BorderLayout());
|
||||
worker = new Worker(config);
|
||||
worker = new Worker(config).setMainFrame(this);
|
||||
|
||||
add(toolbar = new Toolbar(worker),BorderLayout.EAST);
|
||||
add(statusBar = new StatusBar(worker),BorderLayout.SOUTH);
|
||||
add(docTable = new DocTable(worker),BorderLayout.NORTH);
|
||||
add(pdfPreview = new Preview(worker),BorderLayout.CENTER);
|
||||
|
||||
previewComponent = pdfPreview;
|
||||
int x = config.getOrCreate(APP_X, 20);
|
||||
int y = config.getOrCreate(APP_Y, 20);
|
||||
setLocation(new Point(x, y));
|
||||
@@ -196,4 +200,52 @@ public class MainFrame extends JFrame {
|
||||
JOptionPane.WARNING_MESSAGE);
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* open file in preview panel
|
||||
* @param filePath
|
||||
*/
|
||||
public void preview(File file) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
if (file == null){
|
||||
if (previewComponent != null) remove(previewComponent);
|
||||
previewComponent = null;
|
||||
return;
|
||||
}
|
||||
String filename = file.toString();
|
||||
if (filename.toLowerCase().endsWith("pdf")) {
|
||||
if (previewComponent != pdfPreview) {
|
||||
if (previewComponent != null) remove(previewComponent);
|
||||
previewComponent = pdfPreview;
|
||||
add(previewComponent,BorderLayout.CENTER);
|
||||
pack();
|
||||
}
|
||||
pdfPreview.show(filename);
|
||||
|
||||
}
|
||||
if (filename.toLowerCase().endsWith("jpg")) {
|
||||
try {
|
||||
Image img = ImageIO.read(file);
|
||||
|
||||
if (previewComponent != null) {
|
||||
remove(previewComponent);
|
||||
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);
|
||||
|
||||
previewComponent = label;
|
||||
this.add(previewComponent, BorderLayout.CENTER);
|
||||
pack();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Was not able to preview {}:",file,e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,11 @@ import de.srsoftware.belegscanner.Worker;
|
||||
import org.icepdf.ri.common.ComponentKeyBinding;
|
||||
import org.icepdf.ri.common.SwingController;
|
||||
import org.icepdf.ri.common.SwingViewBuilder;
|
||||
import org.icepdf.ri.common.views.DocumentViewController;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class Preview extends JPanel {
|
||||
private final SwingController pdfViewer;
|
||||
@@ -31,4 +33,9 @@ public class Preview extends JPanel {
|
||||
add(fileName = new JLabel(),BorderLayout.SOUTH);
|
||||
fileName.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
}
|
||||
|
||||
public void show(String file) {
|
||||
pdfViewer.openDocument(file);
|
||||
pdfViewer.setPageFitMode(DocumentViewController.PAGE_FIT_WINDOW_HEIGHT, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +1,47 @@
|
||||
package de.srsoftware.belegscanner.gui;
|
||||
|
||||
import de.srsoftware.belegscanner.Worker;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
import static de.srsoftware.belegscanner.Application.t;
|
||||
import static java.awt.BorderLayout.CENTER;
|
||||
import static java.awt.BorderLayout.EAST;
|
||||
import static java.awt.BorderLayout.NORTH;
|
||||
import static java.awt.BorderLayout.WEST;
|
||||
|
||||
public class ResolutionSelector extends JPanel{
|
||||
|
||||
private int resolution = 150;
|
||||
|
||||
public ResolutionSelector() {
|
||||
private final Worker worker;
|
||||
public ResolutionSelector(Worker worker) {
|
||||
super(new BorderLayout());
|
||||
this.worker = worker;
|
||||
;
|
||||
ButtonGroup group = new ButtonGroup();
|
||||
JLabel label = new JLabel(t("current resolution") + ": " + resolution + "px");
|
||||
JLabel label = new JLabel(t("current resolution") + ": " + worker.setResolution(150) + "px");
|
||||
add(label, NORTH);
|
||||
add(resolutionButton(group, label, 150), WEST);
|
||||
add(resolutionButton(group, label, 300), CENTER);
|
||||
add(resolutionButton(group, label, 600), BorderLayout.EAST);
|
||||
add(resolutionButton(group, label, 600), EAST);
|
||||
setMaximumSize(new Dimension(600, 200));
|
||||
}
|
||||
|
||||
public int resolutiion(){
|
||||
return resolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* create one radio button for the resoultionSelector
|
||||
* @param group
|
||||
* @param label
|
||||
* @param i
|
||||
* @param resolution
|
||||
* @return
|
||||
*/
|
||||
private JRadioButton resolutionButton(ButtonGroup group, JLabel label, int i) {
|
||||
JRadioButton btn = new JRadioButton(i+" px");
|
||||
private JRadioButton resolutionButton(ButtonGroup group, JLabel label, int resolution) {
|
||||
JRadioButton btn = new JRadioButton(resolution+" px");
|
||||
btn.addActionListener(ev -> {
|
||||
resolution = i;
|
||||
label.setText("aktuelle Auflösung: "+resolution+"px");
|
||||
label.setText("aktuelle Auflösung: "+worker.setResolution(resolution)+"px");
|
||||
});
|
||||
group.add(btn);
|
||||
if (i==150) btn.setSelected(true);
|
||||
if (resolution==150) btn.setSelected(true);
|
||||
return btn;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package de.srsoftware.belegscanner.gui;
|
||||
|
||||
import de.srsoftware.belegscanner.Worker;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
import static de.srsoftware.belegscanner.Application.t;
|
||||
import static java.awt.BorderLayout.CENTER;
|
||||
import static java.awt.BorderLayout.NORTH;
|
||||
import static java.awt.BorderLayout.WEST;
|
||||
|
||||
public class RotationSelector extends JPanel {
|
||||
|
||||
private final Worker worker;
|
||||
private final JRadioButton noRotation;
|
||||
|
||||
public RotationSelector(Worker worker) {
|
||||
setLayout(new BoxLayout(this,BoxLayout.X_AXIS));
|
||||
this.worker = worker;
|
||||
ButtonGroup group = new ButtonGroup();
|
||||
worker.setRotation(0);
|
||||
add(noRotation = rotatatonButton(group, 0));
|
||||
add(rotatatonButton(group, 90));
|
||||
add(rotatatonButton(group, 180));
|
||||
add(rotatatonButton(group, -90));
|
||||
noRotation.setSelected(true);
|
||||
add(new JLabel(t("Rotation")));
|
||||
setMaximumSize(new Dimension(600, 200));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* create one radio button for the resoultionSelector
|
||||
* @param group
|
||||
* @param angle
|
||||
* @return
|
||||
*/
|
||||
private JRadioButton rotatatonButton(ButtonGroup group, int angle) {
|
||||
JRadioButton btn = new JRadioButton(angle+"°");
|
||||
btn.addActionListener(ev -> {
|
||||
worker.setRotation(angle);
|
||||
});
|
||||
group.add(btn);
|
||||
if (angle==150) btn.setSelected(true);
|
||||
return btn;
|
||||
}
|
||||
}
|
||||
@@ -34,8 +34,6 @@ public class Toolbar extends JPanel {
|
||||
private final SelectComboBox pathPicker, catPicker;
|
||||
private static final int OFFSET = 2;
|
||||
|
||||
|
||||
|
||||
private class AdditionalInput {
|
||||
private final JPanel panel;
|
||||
private final SelectComboBox input;
|
||||
@@ -73,8 +71,9 @@ public class Toolbar extends JPanel {
|
||||
add(input(t("Category"),catPicker = categoryPicker()));
|
||||
add(input(t("Path"),pathPicker = pathPicker()));
|
||||
add(new FormatSelector(worker));
|
||||
add(new ResolutionSelector());
|
||||
add(new ImproveSelector());
|
||||
add(new ResolutionSelector(worker));
|
||||
add(new ImproveSelector(worker));
|
||||
add(new RotationSelector(worker));
|
||||
add(new TypeSelector(worker));
|
||||
add(scanButton());
|
||||
add(Box.createGlue());
|
||||
@@ -144,9 +143,6 @@ public class Toolbar extends JPanel {
|
||||
if (additionalInput == null) {
|
||||
LOG.debug("Field missing, creating…");
|
||||
var input = new SelectComboBox(new Vector<>());
|
||||
if (varName.equals(t(Constants.PAGE))){
|
||||
return "["+varName+"]";
|
||||
}
|
||||
input.onUpdateText(newText -> worker.resolvePath());
|
||||
JPanel panel = input(varName,input);
|
||||
additionalInputs.put(varName, additionalInput = new AdditionalInput(panel,input,true));
|
||||
@@ -176,4 +172,12 @@ public class Toolbar extends JPanel {
|
||||
scanButton.addActionListener(ev -> worker.scan());
|
||||
return scanButton;
|
||||
}
|
||||
|
||||
public void disableScan() {
|
||||
scanButton.setEnabled(false);
|
||||
}
|
||||
|
||||
public void enableScan() {
|
||||
scanButton.setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,33 @@
|
||||
package de.srsoftware.belegscanner.model;
|
||||
|
||||
import de.srsoftware.belegscanner.Constants;
|
||||
import de.srsoftware.belegscanner.model.api.Page;
|
||||
import de.srsoftware.belegscanner.model.api.Project;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ScanProject implements Project {
|
||||
import static de.srsoftware.belegscanner.Application.t;
|
||||
|
||||
public class ScanProject implements Project {
|
||||
private static final String PAGE = t(Constants.PAGE);
|
||||
private final String pattern;
|
||||
private List<Page> pageList = List.of();
|
||||
private List<Page> pageList = new ArrayList<>();
|
||||
|
||||
public ScanProject(String resolvedPath){
|
||||
this.pattern = resolvedPath;
|
||||
}
|
||||
@Override
|
||||
public String pattern() {
|
||||
return null;
|
||||
return pattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File nextFile() {
|
||||
return new File(pattern.replace("["+PAGE+"]",nextPage()+""));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -30,12 +42,17 @@ public class ScanProject implements Project {
|
||||
|
||||
@Override
|
||||
public void openDir() {
|
||||
|
||||
File dir = new File(pattern).getParentFile();
|
||||
if (dir.exists()) try {
|
||||
Desktop.getDesktop().open(dir);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void previewLastPage() {
|
||||
|
||||
public Page lastPage() {
|
||||
return pageList.isEmpty() ? null : pageList.get(pageList.size()-1);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -44,7 +61,23 @@ public class ScanProject implements Project {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropLastPage() {
|
||||
public Page addPage(File file) {
|
||||
Page newPage = new Page() {
|
||||
@Override
|
||||
public File file() {
|
||||
return file;
|
||||
}
|
||||
};
|
||||
pageList.add(newPage);
|
||||
return newPage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Project dropLastPage() {
|
||||
if (pageList.isEmpty()) return this;
|
||||
var lastPage = pageList.remove(pageList.size()-1);
|
||||
var file = lastPage.file();
|
||||
if (file.exists()) file.delete();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package de.srsoftware.belegscanner.model.api;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
public interface Project {
|
||||
@@ -11,9 +12,13 @@ public interface Project {
|
||||
|
||||
void openDir();
|
||||
|
||||
void previewLastPage();
|
||||
Page lastPage();
|
||||
|
||||
void join();
|
||||
|
||||
void dropLastPage();
|
||||
Project dropLastPage();
|
||||
|
||||
File nextFile();
|
||||
|
||||
Page addPage(File file);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user