improved file naming
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
@@ -14,10 +14,11 @@ import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.TreeMap;
|
||||
import java.util.Vector;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
@@ -62,23 +63,23 @@ public class DocTable extends JPanel{
|
||||
private String path;
|
||||
private JButton joinButton;
|
||||
|
||||
public Row(String path) {
|
||||
this.path = path;
|
||||
rows.put(path, this);
|
||||
public Row(String project) {
|
||||
this.path = project;
|
||||
rows.put(project, this);
|
||||
constraints.gridy = ++rowCounter;
|
||||
constraints.gridx = 0;
|
||||
add(pathLabel = new JLabel(path), constraints);
|
||||
add(pathLabel = new JLabel(project), constraints);
|
||||
constraints.gridx = 1;
|
||||
add(status = new JLabel("neu"),constraints);
|
||||
|
||||
buttons = new JPanel();
|
||||
buttons.setLayout(new FlowLayout());
|
||||
|
||||
buttons.add(button("⚁",t("open folder"),ev -> openFolder(path)));
|
||||
buttons.add(joinButton = button("⎗",t("join PDFs"),ev -> joinDocs(path)));
|
||||
buttons.add(button("✉",t("display preview"),ev -> preview(path)));
|
||||
buttons.add(button("⚁",t("open folder"),ev -> openFolder(project)));
|
||||
buttons.add(joinButton = button("⎗",t("join PDFs"),ev -> joinDocs(project)));
|
||||
buttons.add(button("✉",t("display preview"),ev -> preview(project)));
|
||||
buttons.add(button("✓",t("done"),ev -> drop(this)));
|
||||
buttons.add(button("❌",t("drop file"),ev -> dropFile(path)));
|
||||
buttons.add(button("❌",t("drop file"),ev -> dropFile(project)));
|
||||
|
||||
constraints.gridx = 2;
|
||||
add(buttons,constraints);
|
||||
@@ -111,11 +112,10 @@ public class DocTable extends JPanel{
|
||||
/**
|
||||
* remove the join button assigned with the selected document
|
||||
*/
|
||||
public void removeJoinButton() {
|
||||
joinButton.setEnabled(false);
|
||||
buttons.remove(joinButton);
|
||||
public void setJoinButton(boolean enabled) {
|
||||
joinButton.setEnabled(enabled);
|
||||
joinButton.setVisible(enabled);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 1073955198529023744L;
|
||||
@@ -151,10 +151,13 @@ public class DocTable extends JPanel{
|
||||
|
||||
/**
|
||||
* Ad a table entry for the directory represented by path
|
||||
* @param path
|
||||
* @param project
|
||||
*/
|
||||
public void add(String path) {
|
||||
if (!rows.containsKey(path)) new Row(path).status("neu");
|
||||
public void add(String project) {
|
||||
var row = rows.get(project);
|
||||
if (row == null) {
|
||||
new Row(project).status("neu");
|
||||
} else row.setJoinButton(true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -187,10 +190,11 @@ public class DocTable extends JPanel{
|
||||
}
|
||||
|
||||
private void dropFile(String path) {
|
||||
Path latest = latestFile(path);
|
||||
Path latest = latestFile(new File(path));
|
||||
if (latest != null) {
|
||||
LOG.debug("Removing {}",latest);
|
||||
latest.toFile().delete();
|
||||
preview(path); // show lates of remaining files
|
||||
preview(latest.toFile().getParent()); // show lates of remaining files
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,19 +204,37 @@ public class DocTable extends JPanel{
|
||||
*/
|
||||
public void joinDocs(String path) {
|
||||
LOG.debug("joinFiles({})",path);
|
||||
rows.get(path).removeJoinButton();
|
||||
File folder = new File(path);
|
||||
rows.get(path).setJoinButton(false);
|
||||
File folder = new File(path).getParentFile();
|
||||
if (!folder.exists()) return;
|
||||
|
||||
List<String> pdfs = Arrays.asList(folder.list(PAGES));
|
||||
Collections.sort(pdfs);
|
||||
|
||||
|
||||
String pattern = new File(path).getName();
|
||||
var parts = pattern.split("\\$"+t("PAGE"));
|
||||
var prefix = parts[0];
|
||||
var suffix = parts.length>1 ? parts[1] : "";
|
||||
if (parts.length<2) return;
|
||||
|
||||
var pages = new TreeMap<Integer,String>();
|
||||
for (var file : folder.list()){
|
||||
Optional<Integer> opt = getPage(file, prefix, suffix);
|
||||
if (opt.isPresent()){
|
||||
pages.put(opt.get(),file);
|
||||
}
|
||||
}
|
||||
if (pages.size()<2) return;
|
||||
int first = pages.firstKey();
|
||||
int last = pages.lastKey();
|
||||
|
||||
var range = first + (last != first ? "…"+last:"");
|
||||
var target = pattern.replace("$"+t("PAGE"),range);
|
||||
|
||||
var pdfs = pages.values();
|
||||
Vector<String> cmd = new Vector<>();
|
||||
cmd.add("pdftk");
|
||||
cmd.addAll(pdfs);
|
||||
cmd.add("cat");
|
||||
cmd.add("output");
|
||||
cmd.add(folder.getName()+".pdf");
|
||||
cmd.add(target);
|
||||
LOG.debug("executing {}",cmd);
|
||||
ProcessBuilder builder = new ProcessBuilder(cmd);
|
||||
builder.directory(folder);
|
||||
@@ -226,17 +248,33 @@ public class DocTable extends JPanel{
|
||||
} catch (InterruptedException | IOException e) {
|
||||
LOG.error("{} terminated: ",builder,e);
|
||||
}
|
||||
for (String page : pdfs) Path.of(path,page).toFile().delete();
|
||||
|
||||
pdfs.stream()
|
||||
.peek(page -> LOG.debug("removing {}",page))
|
||||
.map(page -> folder.toPath().resolve(page))
|
||||
.map(Path::toFile)
|
||||
.forEach(File::delete);
|
||||
setState(path,t("PDFs joined."));
|
||||
preview(path);
|
||||
preview(folder);
|
||||
}
|
||||
|
||||
private Path latestFile(String path) {
|
||||
File folder = new File(path);
|
||||
|
||||
private Optional<Integer> getPage(String file, String prefix, String suffix) {
|
||||
if (file.startsWith(prefix) && file.endsWith(suffix)) {
|
||||
file = file.substring(prefix.length(), file.length() - suffix.length());
|
||||
try {
|
||||
return Optional.of(Integer.parseInt(file));
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
return Optional.empty();
|
||||
|
||||
}
|
||||
|
||||
private Path latestFile(File folder) {
|
||||
if (!folder.exists()) return null;
|
||||
if (!folder.isDirectory()) folder = folder.getParentFile();
|
||||
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);
|
||||
return latest == null ? null : folder.toPath().resolve(latest);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -254,17 +292,21 @@ public class DocTable extends JPanel{
|
||||
}
|
||||
|
||||
try {
|
||||
process = new ProcessBuilder(List.of(Constants.FILE_BROWSER,path)).start();
|
||||
process = new ProcessBuilder(List.of(Constants.FILE_BROWSER,new File(path).getParent())).start();
|
||||
} catch (IOException e) {
|
||||
LOG.error(t("{} terminated: "),process,e);
|
||||
}
|
||||
}
|
||||
|
||||
public void preview(String path){
|
||||
preview(new File(path));
|
||||
}
|
||||
|
||||
/**
|
||||
* show lates document of the given directory in preview area
|
||||
* @param path
|
||||
*/
|
||||
public void preview(String path) {
|
||||
public void preview(File path) {
|
||||
LOG.debug("preview({})",path);
|
||||
previewListeners.forEach(l -> l.show(latestFile(path)));
|
||||
}
|
||||
|
||||
@@ -87,6 +87,7 @@ public class MainFrame extends JFrame {
|
||||
private Date date = new Date();
|
||||
|
||||
private String patchedPath = "";
|
||||
private String project = "";
|
||||
|
||||
private Configuration config;
|
||||
|
||||
@@ -155,13 +156,26 @@ public class MainFrame extends JFrame {
|
||||
* Scans the given path for occurences of '$VARIABLES' and creates input fields for the found variables.
|
||||
* @param path
|
||||
*/
|
||||
private void addFieldsFor(String path) {
|
||||
private void addFieldsForPath(String path) {
|
||||
Vector<String> marks = new Vector<>();
|
||||
Matcher matches = MARKEN.matcher(path);
|
||||
while (matches.find()) marks.add(matches.group(1));
|
||||
toolbar.addFieldsFor(marks);
|
||||
};
|
||||
|
||||
public String appMissing(String appTest){
|
||||
List<String> cmd = Arrays.asList(appTest.split(" "));
|
||||
|
||||
try {
|
||||
Process process = new ProcessBuilder(cmd).start();
|
||||
int errorCode = process.waitFor();
|
||||
if (errorCode == 0) return null;
|
||||
} catch (InterruptedException | IOException e) {
|
||||
LOG.error(t("{} terminated: "),appTest,e);
|
||||
}
|
||||
return cmd.get(0);
|
||||
}
|
||||
|
||||
public void checkDependencies() {
|
||||
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());
|
||||
@@ -226,18 +240,7 @@ public class MainFrame extends JFrame {
|
||||
|
||||
|
||||
|
||||
public String appMissing(String appTest){
|
||||
List<String> cmd = Arrays.asList(appTest.split(" "));
|
||||
|
||||
try {
|
||||
Process process = new ProcessBuilder(cmd).start();
|
||||
int errorCode = process.waitFor();
|
||||
if (errorCode == 0) return null;
|
||||
} catch (InterruptedException | IOException e) {
|
||||
LOG.error(t("{} terminated: "),appTest,e);
|
||||
}
|
||||
return cmd.get(0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* checks, whether the scan button may be enabled.
|
||||
@@ -246,6 +249,12 @@ public class MainFrame extends JFrame {
|
||||
toolbar.readyToScan(!(isScanning || patchedPath==null || patchedPath.isBlank() || patchedPath.contains("$")));
|
||||
}
|
||||
|
||||
private String extension(String path) {
|
||||
var target = config.get(CONFIG_TARGET);
|
||||
return path +"."+ (JPG.equals(target) ? "jpg":"pdf");
|
||||
}
|
||||
|
||||
|
||||
private void notifyAppsMissing(List<String> missingApps) {
|
||||
StringBuilder message = new StringBuilder();
|
||||
message.append("<html>");
|
||||
@@ -267,25 +276,26 @@ public class MainFrame extends JFrame {
|
||||
* @param path
|
||||
* @param dimension
|
||||
*/
|
||||
private void performScan(String path, Dimension dimension) {
|
||||
private void performScan(String project, String path, Dimension dimension) {
|
||||
LOG.debug("performScan({})",path);
|
||||
/* We need to save all required config values before starting to scan, because they might be changed during scanning */
|
||||
String target = config.getOrCreate(CONFIG_TARGET, "pdf");
|
||||
|
||||
setScanning(true);
|
||||
|
||||
File folder = new File(path);
|
||||
|
||||
var targetFile = new File(path);
|
||||
File folder = targetFile.getParentFile();
|
||||
if (!folder.exists()) {
|
||||
LOG.warn(t("Path '{}' does not exist!"),path);
|
||||
folder.mkdirs();
|
||||
}
|
||||
|
||||
docTable.add(path);
|
||||
docTable.add(project);
|
||||
|
||||
int resolution = toolbar.getResolution();
|
||||
boolean improbeBrightness = toolbar.getImproveBrightness();
|
||||
long timestamp = new Date().getTime();
|
||||
String raw = timestamp+".jpg";
|
||||
//long timestamp = new Date().getTime();
|
||||
//String raw = timestamp+".jpg";
|
||||
var raw = targetFile.getName()+".jpg";
|
||||
|
||||
Vector<String> cmd = new Vector<>();
|
||||
cmd.add("scanimage");
|
||||
@@ -305,19 +315,19 @@ public class MainFrame extends JFrame {
|
||||
builder.directory(folder);
|
||||
try {
|
||||
Process process = builder.start();
|
||||
docTable.setState(path,t("Scanning…"));
|
||||
docTable.setState(project,t("Scanning…"));
|
||||
int errorCode = process.waitFor();
|
||||
if (errorCode != 0) {
|
||||
LOG.error(t("Error code: {} for {}"),errorCode,cmd);
|
||||
docTable.setState(path, t("Scan failed."));
|
||||
docTable.setState(project, t("Scan failed."));
|
||||
setScanning(false);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
} catch (InterruptedException | IOException e) {
|
||||
LOG.error(t("{} terminated: "),builder,e);
|
||||
}
|
||||
setScanning(false);
|
||||
toolbar.nextPage();
|
||||
|
||||
if (improbeBrightness) {
|
||||
cmd = new Vector<>();
|
||||
@@ -329,11 +339,11 @@ public class MainFrame extends JFrame {
|
||||
builder.directory(folder);
|
||||
try {
|
||||
Process process = builder.start();
|
||||
docTable.setState(path,t("Adjusting brightness…"));
|
||||
docTable.setState(project,t("Adjusting brightness…"));
|
||||
int errorCode = process.waitFor();
|
||||
if (errorCode != 0) {
|
||||
LOG.error(t("Error code: {} for {}"),errorCode,cmd);
|
||||
docTable.setState(path, t("Adjustment failed."));
|
||||
docTable.setState(project, t("Adjustment failed."));
|
||||
setScanning(false);
|
||||
return;
|
||||
}
|
||||
@@ -344,12 +354,12 @@ public class MainFrame extends JFrame {
|
||||
}
|
||||
|
||||
if (JPG.equals(target)) {
|
||||
docTable.setState(path, t("Image scanned"));
|
||||
docTable.preview(path);
|
||||
docTable.setState(project, t("Image scanned"));
|
||||
docTable.preview(folder);
|
||||
return;
|
||||
};
|
||||
|
||||
String pdf = timestamp+".page.pdf";
|
||||
String pdf = targetFile.getName() + (PDF.equals(target) ? ".pdf" : ".tmp.pdf"); //timestamp+".page.pdf";
|
||||
cmd = new Vector<>();
|
||||
cmd.add("convert");
|
||||
cmd.add(raw);
|
||||
@@ -358,26 +368,27 @@ public class MainFrame extends JFrame {
|
||||
builder.directory(folder);
|
||||
try {
|
||||
Process process = builder.start();
|
||||
docTable.setState(path,t("Converting to PDF…"));
|
||||
docTable.setState(project,t("Converting to PDF…"));
|
||||
int errorCode = process.waitFor();
|
||||
if (errorCode != 0) {
|
||||
LOG.error("Error code: {} for {}",errorCode,cmd);
|
||||
docTable.setState(path, t("Conversion failed."));
|
||||
docTable.setState(project, t("Conversion failed."));
|
||||
return;
|
||||
}
|
||||
|
||||
} catch (InterruptedException | IOException e) {
|
||||
LOG.error("{} terminated: ",builder,e);
|
||||
}
|
||||
|
||||
Path.of(path, raw).toFile().delete();
|
||||
LOG.debug("removing {}",raw);
|
||||
folder.toPath().resolve(raw).toFile().delete();
|
||||
//Path.of(folder,raw).toFile().delete();
|
||||
if (PDF.equals(target)) {
|
||||
docTable.setState(path, t("PDF created"));
|
||||
docTable.preview(path);
|
||||
docTable.setState(project, t("PDF created"));
|
||||
docTable.preview(folder);
|
||||
return;
|
||||
}
|
||||
|
||||
String ocr = timestamp+".page.ocr.pdf";
|
||||
String ocr = targetFile.getName() + ".pdf";//timestamp+".page.ocr.pdf";
|
||||
|
||||
cmd = new Vector<>();
|
||||
cmd.add("pdfsandwich");
|
||||
@@ -396,20 +407,26 @@ public class MainFrame extends JFrame {
|
||||
builder.directory(folder);
|
||||
try {
|
||||
Process process = builder.start();
|
||||
docTable.setState(path,t("Text recognition…"));
|
||||
docTable.setState(project,t("Text recognition…"));
|
||||
int errorCode = process.waitFor();
|
||||
if (errorCode != 0) {
|
||||
LOG.error("error code: {} for {}",errorCode,cmd);
|
||||
docTable.setState(path, t("OCR failed."));
|
||||
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);
|
||||
}
|
||||
Path.of(path, pdf).toFile().delete();
|
||||
docTable.setState(path,t("Finished text recognition."));
|
||||
docTable.preview(path);
|
||||
|
||||
LOG.debug("removing {}",pdf);
|
||||
folder.toPath().resolve(pdf).toFile().delete();
|
||||
docTable.setState(project,t("Finished text recognition."));
|
||||
docTable.preview(folder);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -440,7 +457,7 @@ public class MainFrame extends JFrame {
|
||||
|
||||
/**
|
||||
* open file in preview panel
|
||||
* @param file
|
||||
* @param filePath
|
||||
*/
|
||||
private void preview(Path filePath) {
|
||||
if (filePath == null) return;
|
||||
@@ -482,7 +499,7 @@ public class MainFrame extends JFrame {
|
||||
*/
|
||||
private void scan(ActionEvent ev) {
|
||||
updateConfig();
|
||||
new Thread(() -> performScan(patchedPath,dimension)).start();
|
||||
new Thread(() -> performScan(project,patchedPath,dimension)).start();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -592,10 +609,17 @@ public class MainFrame extends JFrame {
|
||||
.replace("$"+t("YEAR"), year+"")
|
||||
.replace("$"+t("MONTH"), month<10 ? "0"+month : ""+month)
|
||||
.replace("$"+t("DAY"), day < 10 ? "0"+day : ""+day);
|
||||
|
||||
addFieldsFor(patchedPath);
|
||||
for (Entry<String, String> entry : fields.entrySet()) patchedPath = patchedPath.replace("$"+entry.getKey(), entry.getValue());
|
||||
statusBar.setPath(patchedPath);
|
||||
addFieldsForPath(patchedPath);
|
||||
project = extension(patchedPath);
|
||||
for (Entry<String, String> entry : fields.entrySet()) {
|
||||
var key = entry.getKey();
|
||||
patchedPath = patchedPath.replace("$"+entry.getKey(), entry.getValue());
|
||||
if (!t("PAGE").equals(key)){
|
||||
project = project.replace("$"+entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
statusBar.setPath(extension(patchedPath));
|
||||
checkScanButton();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,6 +153,10 @@ public class Toolbar extends JPanel {
|
||||
if (additonalComponents.containsKey(name)) continue;
|
||||
SelectComboBox valuePicker = new SelectComboBox(new Vector<>()).onUpdateText(newText -> updateField(name,newText));
|
||||
JPanel input = input(name, valuePicker);
|
||||
if (t("PAGE").equals(name)) {
|
||||
valuePicker.setText("1");
|
||||
SwingUtilities.invokeLater(() -> updateField(name,"1"));
|
||||
}
|
||||
input.setBorder(BORDER);
|
||||
add(input,getComponentCount()-OFFSET);
|
||||
additonalComponents.put(name, input);
|
||||
@@ -348,6 +352,25 @@ public class Toolbar extends JPanel {
|
||||
panel.add(component);
|
||||
return panel;
|
||||
}
|
||||
|
||||
public Toolbar nextPage(){
|
||||
var page = t("PAGE");
|
||||
var panel = additonalComponents.get(page);
|
||||
if (panel == null) return this;
|
||||
for (var child : panel.getComponents()){
|
||||
if (child instanceof SelectComboBox scb){
|
||||
var val = scb.getText();
|
||||
try {
|
||||
var newVal = ""+(Integer.parseInt(val)+1);
|
||||
scb.setText(newVal);
|
||||
fieldListeners.forEach(l -> l.setField(page,newVal));
|
||||
} catch (NumberFormatException nfe){}
|
||||
}
|
||||
LOG.debug("component: {}",child);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* creates an auto-complete text field for setting the scan target path
|
||||
@@ -359,7 +382,7 @@ public class Toolbar extends JPanel {
|
||||
JSONObject catInfo = cats.getJSONObject(catName);
|
||||
if (catInfo.has("path")) paths.add(catInfo.get("path"));
|
||||
}
|
||||
paths.add("$"+t("HOME")+"/$"+t("YEAR")+"/$"+t("MONTH")+"-$"+t("DAY")+" - $"+t("CATEGORY"));
|
||||
paths.add("$"+t("HOME")+"/$"+t("YEAR")+"/$"+t("MONTH")+"-$"+t("DAY")+" - $"+t("CATEGORY")+"/$"+t("PAGE"));
|
||||
return new SelectComboBox(paths).onUpdateText(this::updatePath);
|
||||
}
|
||||
|
||||
@@ -427,7 +450,7 @@ public class Toolbar extends JPanel {
|
||||
* create one radio button for the typeSelector
|
||||
* @param group
|
||||
* @param label
|
||||
* @param i
|
||||
* @param selection
|
||||
* @return
|
||||
*/
|
||||
private JRadioButton typeButton(ButtonGroup group, String label,String selection) {
|
||||
@@ -440,7 +463,6 @@ public class Toolbar extends JPanel {
|
||||
|
||||
/**
|
||||
* create a radio group to select the target type for scanning
|
||||
* @param jsonObject
|
||||
* @return
|
||||
*/
|
||||
private JPanel typeSelector() {
|
||||
@@ -533,7 +555,4 @@ public class Toolbar extends JPanel {
|
||||
LOG.debug("updatePath({})",newPath);
|
||||
pathListeners.forEach(l -> l.setPath(newPath));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user