OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Compare Revisions

Regard whitespace Rev 107 → Rev 108

/trunk/Modules/Module OCR/.classpath
New file
0,0 → 1,11
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry combineaccessrules="false" kind="src" path="/OpenConcerto"/>
<classpathentry kind="lib" path="lib/jsoup-1.7.3.jar"/>
<classpathentry kind="lib" path="lib/ron_cemer_ocr_mod.jar"/>
<classpathentry kind="lib" path="lib/uk.co.mmscomputing.device.sane.jar"/>
<classpathentry kind="lib" path="lib/uk.co.mmscomputing.device.twain.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>
/trunk/Modules/Module OCR/module.properties
New file
0,0 → 1,5
# [\p{Alnum}_.]{3,}
id=org.openconcerto.modules.ocr
# \p{Digit}(\.\p{Digit}+)?
version=1.0
contact=ILM Informatique
/trunk/Modules/Module OCR/.project
New file
0,0 → 1,17
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>Module OCR</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
/trunk/Modules/Module OCR/lib/uk.co.mmscomputing.device.twain.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/Modules/Module OCR/lib/uk.co.mmscomputing.device.twain.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/Modules/Module OCR/lib/uk.co.mmscomputing.device.sane.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/Modules/Module OCR/lib/uk.co.mmscomputing.device.sane.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/Modules/Module OCR/lib/jsoup-1.7.3.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/Modules/Module OCR/lib/jsoup-1.7.3.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/Modules/Module OCR/lib/ron_cemer_ocr_mod.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/Modules/Module OCR/lib/ron_cemer_ocr_mod.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/OCRThread.java
New file
0,0 → 1,299
package org.openconcerto.modules.ocr;
 
import java.io.File;
import java.util.ArrayList;
import java.util.List;
 
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
 
import org.openconcerto.modules.ocr.parser.ACTNInvoiceParser;
import org.openconcerto.modules.ocr.parser.AbstractInvoiceParser;
import org.openconcerto.modules.ocr.parser.AcadiaInvoiceParser;
import org.openconcerto.modules.ocr.parser.AmazonInvoiceParser;
import org.openconcerto.modules.ocr.parser.BNPInvoiceParser;
import org.openconcerto.modules.ocr.parser.EbayENInvoiceParser;
import org.openconcerto.modules.ocr.parser.GenericInvoiceParser;
import org.openconcerto.modules.ocr.parser.LaPosteInvoiceParser;
import org.openconcerto.modules.ocr.parser.OVHInvoiceParser;
import org.openconcerto.modules.ocr.parser.OrangeInvoiceParser;
import org.openconcerto.modules.ocr.parser.ParserUtils;
import org.openconcerto.modules.ocr.parser.TechDataInvoiceParser;
import org.openconcerto.ui.ReloadPanel;
 
public class OCRThread extends Thread {
private List<File> files;
private final InvoiceOCRTable table;
private final InvoiceViewer viewer;
private final JPanel parent;
private final String execDirectory;
private boolean textCleanning = false;
List<InvoiceOCR> invalidInvoices = new ArrayList<InvoiceOCR>();
 
public OCRThread(List<File> files, InvoiceOCRTable table, InvoiceViewer viewer, JPanel parent, String execDirectory) {
this.files = files;
this.table = table;
this.viewer = viewer;
this.parent = parent;
this.execDirectory = execDirectory;
}
 
@Override
public void run() {
final List<AbstractInvoiceParser> parsers = initParser();
final List<InvoiceOCR> invoices = new ArrayList<InvoiceOCR>();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
emptyTable();
}
});
if(this.textCleanning){
textCleanning(this.invalidInvoices);
}
if (this.files != null) {
int fileCount = this.files.size();
for (int i = 0; i < fileCount; i++) {
System.out.println("OCRThread.run(): " + i + "/" + fileCount);
final File file = this.files.get(i);
try {
InvoiceOCR invoice = parse(file, parsers);
if (invoice != null) {
invoice.setValid(false);
invoices.add(invoice);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
 
if (invoices != null) {
final List<InvoiceOCR> res_invoices = attachInvoices(invoices);
commitAll(res_invoices);
}
}
this.textCleanning = false;
}
 
private InvoiceOCR parse(File file, final List<AbstractInvoiceParser> parsers) throws Exception {
InvoiceOCR invoice = null;
final String result = TesseractUtils.DoOcr(file);
final HOCRParser p = new HOCRParser();
final List<OCRLine> lines = p.parse(result);
final OCRPage page = new OCRPage(file, lines);
for (final AbstractInvoiceParser parser : parsers) {
final boolean b = parser.parse(page);
if (b) {
invoice = parser.getInvoice();
invoice.addPage(page);
if (invoice.getActive()) {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
commit(parser.getInvoice());
}
});
for (AbstractInvoiceParser pr : parsers) {
pr.reset();
}
}
break;
}
}
return invoice;
}
 
private void commitAll(final List<InvoiceOCR> invoices) {
final int invoicesCount = invoices.size();
getInvalidInvoices().clear();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
emptyTable();
}
});
for (int i = 0; i < invoicesCount; i++) {
final InvoiceOCR invoice = invoices.get(i);
if (!invoice.getValid()) {
getInvalidInvoices().add(invoice);
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
commit(invoice);
}
});
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
stopLoader();
}
});
}
 
private List<AbstractInvoiceParser> initParser() {
List<AbstractInvoiceParser> parsers = new ArrayList<AbstractInvoiceParser>();
parsers.add(new BNPInvoiceParser());
parsers.add(new TechDataInvoiceParser());
parsers.add(new ACTNInvoiceParser());
parsers.add(new EbayENInvoiceParser());
parsers.add(new OrangeInvoiceParser());
parsers.add(new AmazonInvoiceParser());
parsers.add(new OVHInvoiceParser());
parsers.add(new LaPosteInvoiceParser());
parsers.add(new AcadiaInvoiceParser());
parsers.add(new GenericInvoiceParser());
return parsers;
}
 
private List<InvoiceOCR> attachInvoices(List<InvoiceOCR> invoices) {
InvoiceOCR tempInvoice1;
InvoiceOCR tempInvoice2;
int invoiceCount = invoices.size();
for (int i = 0; i < invoiceCount; i++) {
tempInvoice1 = invoices.get(i);
for (int j = i + 1; j < invoiceCount; j++) {
tempInvoice2 = invoices.get(j);
if (tempInvoice1.getSupplierName() != null && tempInvoice2.getSupplierName() != null && tempInvoice1.getSupplierName().equals(tempInvoice2.getSupplierName())
&& tempInvoice1.getActive() && tempInvoice2.getActive()) {
boolean isSameInvoice = false;
if (tempInvoice1.getInvoiceNumber() != null && tempInvoice2.getInvoiceNumber() != null){
if(tempInvoice1.getInvoiceNumber().equals(tempInvoice2.getInvoiceNumber())) {
isSameInvoice = true;
}
} else if (tempInvoice1.getDate() != null && tempInvoice2.getDate() != null && ParserUtils.compareDate(tempInvoice1.getDate(), tempInvoice2.getDate())) {
isSameInvoice = true;
}
 
if (isSameInvoice) {
tempInvoice1 = setInvoiceFromInvoice(tempInvoice1, tempInvoice2);
invoices.remove(j);
invoiceCount = invoices.size();
}
}
}
tempInvoice1.setTaxId();
if (!tempInvoice1.checkNullValue() || !tempInvoice1.checkAmounts() || tempInvoice1.getTaxId() == -1) {
tempInvoice1.setValid(false);
}
}
return invoices;
}
 
private InvoiceOCR setInvoiceFromInvoice(InvoiceOCR invoice1, InvoiceOCR invoice2) {
if (invoice2.getInvoiceNumber() != null && invoice1.getInvoiceNumber() == null) {
invoice1.setInvoiceNumber(invoice2.getInvoiceNumber());
}
if (invoice2.getAmount() != null && invoice1.getAmount() == null) {
invoice1.setAmount(invoice2.getAmount());
}
if (invoice2.getTax() != null && invoice1.getTax() == null) {
invoice1.setTax(invoice2.getTax());
}
if (invoice2.getAmountWithTax() != null && invoice1.getAmountWithTax() == null) {
invoice1.setAmountWithTax(invoice2.getAmountWithTax());
}
if (invoice2.getPageCount() > 0) {
List<OCRLine> tempHighlight;
OCRPage tempPage;
invoice1.addPage(invoice2.getPage(0));
tempHighlight = invoice2.getHighlight(invoice2.getPage(0));
tempPage = invoice1.getPage(invoice1.getPageCount() - 1);
final int highlightCount = tempHighlight.size();
for (int k = 0; k < highlightCount; k++) {
invoice1.addHighlight(tempPage, tempHighlight.get(k));
}
}
return invoice1;
}
 
private void textCleanning(List<InvoiceOCR> invalidInvoices) {
try {
final File improveDirectory = new File(this.execDirectory, "IMPROVE");
if(improveDirectory.exists() || improveDirectory.mkdirs()){
final int invalidInvoicesCount = invalidInvoices.size();
for (int i = 0; i < invalidInvoicesCount; i++) {
System.out.println("OCRThread.textCleanning(): " + i + "/" + invalidInvoicesCount);
final InvoiceOCR invoice = invalidInvoices.get(i);
final int pageCount = invoice.getPageCount();
for (int j = 0; j < pageCount; j++) {
final OCRPage page = invoice.getPage(j);
File image = page.getFileImage();
final File destFile = new File(improveDirectory, image.getName());
if(sameFileInDirectory(image, improveDirectory)){
image = destFile;
}
final List<String> commands = new ArrayList<String>();
commands.add("./textcleaner");
commands.add("-e");
commands.add("normalize");
commands.add("-s");
commands.add("2");
commands.add(image.getAbsolutePath());
commands.add(destFile.getAbsolutePath());
try {
TesseractUtils.runProcess(commands, new File(this.execDirectory));
page.setFileImage(destFile);
} catch (Exception ex) {
throw new Exception("Le script textcleanner n'est pas accessible à l'endroit: " + new File(this.execDirectory).getAbsolutePath());
}
}
}
}
} catch (Exception ex) {
JOptionPane.showMessageDialog(getParent(), ex.getMessage(), "Erreur", JOptionPane.ERROR_MESSAGE);
}
}
private boolean sameFileInDirectory(File file, File directory){
final File[] files = directory.listFiles();
final int filesCount = files.length;
for(int i = 0; i < filesCount; i++){
if(files[i].getName().equals(file.getName())){
return true;
}
}
return false;
}
private JPanel getParent() {
return this.parent;
}
 
private List<InvoiceOCR> getInvalidInvoices() {
return this.invalidInvoices;
}
public void setTextCleanning(boolean textCleanning) {
this.textCleanning = textCleanning;
}
private void commit(InvoiceOCR invoice) {
if (!SwingUtilities.isEventDispatchThread()) {
throw new IllegalStateException("Must be called in EDT");
}
this.table.add(invoice);
this.viewer.add(invoice);
}
 
private void emptyTable() {
if (!SwingUtilities.isEventDispatchThread()) {
throw new IllegalStateException("Must be called in EDT");
}
this.table.removeAll();
this.viewer.removeAll();
}
 
private void stopLoader() {
if (!SwingUtilities.isEventDispatchThread()) {
throw new IllegalStateException("Must be called in EDT");
}
this.table.comp.setMode(ReloadPanel.MODE_EMPTY);
}
}
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/parser/AcadiaInvoiceParser.java
New file
0,0 → 1,189
package org.openconcerto.modules.ocr.parser;
 
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
import org.openconcerto.modules.ocr.InvoiceOCR;
import org.openconcerto.modules.ocr.OCRLine;
import org.openconcerto.modules.ocr.OCRPage;
 
public class AcadiaInvoiceParser extends AbstractInvoiceParser {
private final List<Date> dates = new ArrayList<Date>();
private boolean pageNumberNear = false;
 
@Override
public boolean parse(OCRPage page) {
if (!(page.contains("acadia") || page.contains("ccadia") || page.contains("czcadia") || page.contains("ccadic") || page.contains("0cadia") || page.contains("acadla") || page.contains("ocodio"))) {
return false;
}
 
final InvoiceOCR invoice = this.getInvoice();
final List<OCRLine> lines = page.getLines();
final int lineCount = lines.size();
invoice.setSupplierName("Acadia");
try {
for (int i = 0; i < lineCount; i++) {
final OCRLine line = lines.get(i);
final String text = line.getText().toLowerCase();
if(text.contains("bon") && text.contains("livraison")){
invoice.setIsNotInvoice(true);
}
// Get invoice number
if(invoice.getInvoiceNumber() == null && text.contains("f")) {
final int indexStart = text.indexOf("f");
final int indexNextSpace = text.indexOf(" ", indexStart);
final int indexStop = (indexNextSpace == -1) ? text.length() : indexNextSpace;
final int numberLength = indexStop - indexStart;
if(numberLength == 9 && ParserUtils.isInteger(text.substring(indexStart + 1, indexStop))){
final String n = text.substring(indexStart, indexStop).trim();
if(!n.equals("")){
invoice.setInvoiceNumber(n.toUpperCase());
addHighlight(page, line);
}
}
}
// Get current page number
if (invoice.getTotalPage() == -1 && text.contains("page") && text.contains("(")) {
final Pattern p = Pattern.compile(".*page.*(\\d+)\\((\\d+)\\).*");
final Matcher m = p.matcher(text);
final boolean b = m.matches();
// If find
if (b && m.groupCount() >= 2) {
final int number = Integer.valueOf(m.group(1));
final int total = Integer.valueOf(m.group(2));
if (number <= total) {
page.setPageNumber(number);
invoice.setTotalPage(total);
addHighlight(page, line);
}
}
}
// Page informations are maybe in next lines
if(invoice.getTotalPage() == -1 && text.contains("page")){
this.pageNumberNear = true;
// Get current page number
} else if(this.pageNumberNear && text.contains("(")){
final Pattern p = Pattern.compile(".*page.*(\\d+)\\((\\d+)\\).*");
final Matcher m = p.matcher(text);
final boolean b = m.matches();
// If find
if (b && m.groupCount() >= 2) {
final int number = Integer.valueOf(m.group(1));
final int total = Integer.valueOf(m.group(2));
this.pageNumberNear = false;
if (number <= total) {
page.setPageNumber(number);
invoice.setTotalPage(total);
addHighlight(page, line);
}
}
}
// Get amount with tax
if (invoice.getAmountWithTax() == null && text.contains("total") && text.contains("t.t.c")) {
final List<BigDecimal> listDecimal = getDecimalsInLine(text);
final int listDecimalSize = listDecimal.size();
if(listDecimalSize > 0){
invoice.setAmountWithTax(listDecimal.get(0));
addHighlight(page, line);
}
}
// Get amount
if (invoice.getAmount() == null && text.trim().startsWith("total") && (text.contains("h.t") || text.contains("h it"))) {
final List<BigDecimal> listDecimal = getDecimalsInLine(text);
final int listDecimalSize = listDecimal.size();
if(listDecimalSize > 0){
if(text.contains("net")){
invoice.setAmount(listDecimal.get(listDecimalSize - 1));
} else {
invoice.setAmount(listDecimal.get(0));
}
addHighlight(page, line);
}
}
// Get tax
if(invoice.getTax() == null && text.trim().contains("tva") && text.trim().contains("montant")){
int index = text.indexOf(")");
if(index == -1){
index = 0;
}
final String cText = text.substring(index, text.length());
final List<BigDecimal> listDecimal = getDecimalsInLine(cText);
int listDecimalSize = listDecimal.size();
if(listDecimalSize > 0){
invoice.setTax(listDecimal.get(listDecimalSize - 1));
addHighlight(page, line);
}
}
// Get all amounts
if(invoice.getAmount() == null && invoice.getAmountWithTax() == null
&& (text.trim().equals("prix") || text.trim().equals("frix") || text.trim().equals("fiix") || text.trim().equals("prix ht"))) {
boolean end = false;
int j = i + 1;
List<BigDecimal> listDecimal = new ArrayList<BigDecimal>();
String cText;
while(!end && j < lineCount){
cText = lines.get(j).getText().toLowerCase();
listDecimal = getDecimalsInLine(cText);
if(listDecimal.size() == 0 && !ParserUtils.isInteger(cText)){
end = true;
}
j++;
}
final int listDecimalSize = listDecimal.size();
if(j - i > 4){
cText = lines.get(j - 2).getText().toLowerCase();
listDecimal = getDecimalsInLine(cText);
if(listDecimalSize > 0){
invoice.setAmountWithTax(listDecimal.get(0));
}
cText = lines.get(j - 3).getText().toLowerCase();
listDecimal = getDecimalsInLine(cText);
if(listDecimalSize > 0){
invoice.setTax(listDecimal.get(0));
}
 
cText = lines.get(j - 4).getText().toLowerCase();
listDecimal = getDecimalsInLine(cText);
if(listDecimal.size() > 0){
invoice.setAmount(listDecimal.get(0));
}
}
}
// Get date
if(invoice.getDate() == null && !text.trim().equals("")) {
Date date = ParserUtils.parseDate(text);
if(date != null){
addHighlight(page, line);
invoice.setDate(date);
}
}
}
missValueCalcul();
invoice.setHighlight(this.getHighlight());
} catch (Exception e) {
return false;
}
return true;
}
 
@Override
public void reset() {
super.reset();
this.dates.clear();
this.pageNumberNear = false;
}
 
@Override
protected boolean checkInvoiceNumber(String invoiceNumber) {
final int numberLength = invoiceNumber.length();
boolean result = true;
if(numberLength != 9 || !invoiceNumber.startsWith("F") || !ParserUtils.isInteger(invoiceNumber.substring(1, numberLength))){
result = false;
}
return result;
}
}
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/parser/AbstractInvoiceParser.java
New file
0,0 → 1,164
package org.openconcerto.modules.ocr.parser;
 
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
import org.openconcerto.modules.ocr.InvoiceOCR;
import org.openconcerto.modules.ocr.OCRLine;
import org.openconcerto.modules.ocr.OCRPage;
 
public abstract class AbstractInvoiceParser implements InvoiceParser {
 
protected boolean valid = false;
protected boolean needModePage;
private InvoiceOCR invoice;
private final Map<OCRPage, List<OCRLine>> map = new HashMap<OCRPage, List<OCRLine>>();
 
public AbstractInvoiceParser() {
this.invoice = new InvoiceOCR();
}
 
public void reset() {
this.valid = false;
this.needModePage = true;
this.invoice = new InvoiceOCR();
this.map.clear();
}
 
@Override
abstract public boolean parse(OCRPage page);
abstract protected boolean checkInvoiceNumber(String invoiceNumber);
 
@Override
public boolean isValid() {
return this.valid;
}
 
@Override
public boolean needModePage() {
return this.needModePage;
}
 
public void addHighlight(OCRPage page, OCRLine line) {
List<OCRLine> l = this.map.get(page);
if (l == null) {
l = new ArrayList<OCRLine>();
this.map.put(page, l);
}
l.add(line);
}
 
public Map<OCRPage, List<OCRLine>> getHighlight() {
return this.map;
}
 
public InvoiceOCR getInvoice() {
return this.invoice;
}
public void checkInvoice(boolean isValidWithCalculatedValue) {
missValueCalcul();
if(!this.invoice.checkNullValue()){
this.invoice.setValid(false);
} else if(this.invoice.getContainCalculatedValue() && !isValidWithCalculatedValue){
this.invoice.setValid(false);
} else if(!this.checkInvoiceNumber(this.invoice.getInvoiceNumber())){
this.invoice.setValid(false);
}
}
protected List<BigDecimal> getDecimalsInLine(String text) {
final List<BigDecimal> listResult = new ArrayList<BigDecimal>();
final StringBuilder result = new StringBuilder();
final int textSize = text.length();
text = text.replace(",", ".");
for (int i = 0; i < textSize; i++) {
if (ParserUtils.isInteger(text.substring(i, i + 1))) {
int firstDot = -1;
int secondDot = -1;
int decimalDot = -1;
String sNumber = "";
int spaceIndex = text.indexOf(" ", i);
int dotIndex = text.indexOf(".", i);
if(dotIndex == spaceIndex + 1){
spaceIndex = text.indexOf(" ", dotIndex);
}
if (spaceIndex != -1) {
sNumber = text.substring(i, spaceIndex);
} else {
int endNumberIndex = i;
while(endNumberIndex < textSize && (endNumberIndex == dotIndex || ParserUtils.isInteger(text.substring(endNumberIndex, endNumberIndex + 1)))){
endNumberIndex++;
}
sNumber = text.substring(i, endNumberIndex);
}
sNumber = sNumber.replace(" ", "").trim();
if (ParserUtils.parseDate(sNumber) == null) {
if (textSize >= i + 8) {
if (sNumber.length() == 1 && ParserUtils.isInteger(text.substring(i + 2, i + 5))) {
sNumber = text.substring(i, i + 8).replace(" ", ".");
}
}
firstDot = sNumber.indexOf(".");
if (sNumber.length() > firstDot + 1) {
secondDot = sNumber.indexOf(".", firstDot + 1);
}
decimalDot = (secondDot == -1) ? firstDot : secondDot;
if (decimalDot != -1) {
for (int j = 0; j < sNumber.length(); j++) {
String character = sNumber.substring(j, j + 1);
if (ParserUtils.isInteger(character)) {
result.append(character);
} else if (decimalDot == j) {
result.append(".");
}
}
if (!result.equals("")) {
try {
BigDecimal dresult = new BigDecimal(result.toString());
if(dresult.compareTo(new BigDecimal("19.30")) == 0) {
System.out.println("capasse");
}
listResult.add(dresult);
} catch (Exception ex) {
// nothing
}
result.delete(0, result.length());
}
}
}
i += sNumber.length() - 1;
}
}
return listResult;
}
 
protected void missValueCalcul() {
if (this.invoice.getAmount() == null) {
if (this.invoice.getAmountWithTax() != null && this.invoice.getTax() != null) {
final BigDecimal amount = this.invoice.getAmountWithTax().subtract(this.invoice.getTax());
this.invoice.setAmount(amount);
this.invoice.setContainCalculatedValue(true);
}
} else if (this.invoice.getAmountWithTax() == null) {
if (this.invoice.getAmount() != null && this.invoice.getTax() != null) {
final BigDecimal amountWithTax = this.invoice.getAmount().add(this.invoice.getTax());
this.invoice.setAmountWithTax(amountWithTax);
this.invoice.setContainCalculatedValue(true);
}
} else if (this.invoice.getTax() == null) {
if (this.invoice.getAmount() != null && this.invoice.getAmountWithTax() != null) {
final BigDecimal tax = this.invoice.getAmountWithTax().subtract(this.invoice.getAmount());
this.invoice.setTax(tax);
this.invoice.setContainCalculatedValue(true);
}
}
}
}
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/parser/EbayENInvoiceParser.java
New file
0,0 → 1,82
package org.openconcerto.modules.ocr.parser;
 
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
import org.openconcerto.modules.ocr.InvoiceOCR;
import org.openconcerto.modules.ocr.OCRLine;
import org.openconcerto.modules.ocr.OCRPage;
 
public class EbayENInvoiceParser extends AbstractInvoiceParser {
 
private final List<Date> dates = new ArrayList<Date>();
 
@Override
public boolean parse(OCRPage page) {
if (!page.contains("ebay.com")) {
return false;
}
final InvoiceOCR invoice = this.getInvoice();
final List<OCRLine> lines = page.getLines();
invoice.setSupplierName("eBay");
try{
for (OCRLine line : lines) {
final String text = line.getText().toLowerCase();
// get current page number
if (text.contains("page") && text.contains("/")) {
final Pattern p = Pattern.compile(".*page.*(\\d+)/(\\d+).*");
final Matcher m = p.matcher(text);
final boolean b = m.matches();
System.out.println(text + " : " + b + " " + m.groupCount());
// if find
if (b && m.groupCount() >= 2) {
final int number = Integer.valueOf(m.group(1));
final int total = Integer.valueOf(m.group(2));
System.out.println(number + ":" + total);
if (number <= total) {
page.setPageNumber(number);
invoice.setTotalPage(total);
}
}
} else if (text.startsWith("total ht")) {
final String cText = text.replace("euros", " ").replace("euro", " ").replace("eur", " ").replace(',', '.').trim();
final String[] split = cText.split("\\s+");
final BigDecimal ht = new BigDecimal(split[split.length - 1]);
this.getInvoice().setAmount(ht);
this.addHighlight(page, line);
} else if(!text.trim().equals("")) {
final Date d = ParserUtils.parseDate(text);
if (d != null) {
this.dates.add(d);
this.getInvoice().setDate(d);
this.addHighlight(page, line);
}
}
}
invoice.setHighlight(this.getHighlight());
} catch (Exception e) {
return false;
}
return true;
}
 
@Override
public void reset() {
super.reset();
this.needModePage = true;
this.dates.clear();
}
 
@Override
protected boolean checkInvoiceNumber(String invoiceNumber) {
// TODO Auto-generated method stub
return false;
}
}
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/parser/ParserUtils.java
New file
0,0 → 1,230
package org.openconcerto.modules.ocr.parser;
 
import java.util.Calendar;
import java.util.Date;
 
public class ParserUtils {
public static Date parseDate(String text) {
Date result = null;
if (text.length() == 8) {
text = text.substring(0, 2) + "." + text.substring(2, 4) + "." + text.substring(4, 8);
} else if((result = getFromFrenchDate(text)) != null){
return result;
} else {
text = text.replace("/", ".").replace("\\", ".").replace("'", ".").replace("-", ".").replace("‘", ".").replace(" ", "");
}
final int textLength = text.length();
final StringBuilder date = new StringBuilder();
for(int i = 0; i < textLength; i++){
final String charac = text.substring(i, i + 1);
if(isInteger(charac)){
date.append(charac);
for(int j = i + 1; j < textLength && date.length() < 10; j++){
final String charac2 = text.substring(j, j + 1);
if(charac2.equals(".") || isInteger(charac2)){
date.append(charac2);
} else if(!charac2.equals(" ")){
break;
}
}
final String[] testDate = date.toString().split("\\.");
final Calendar c = Calendar.getInstance();
int day, month, year = -1, swap;
if (testDate.length > 2) {
try {
day = Integer.parseInt(testDate[0]);
month = Integer.parseInt(testDate[1]) - 1;
if(testDate[2].length() == 2){
year = Integer.parseInt("20" + testDate[2]);
} else {
year = Integer.parseInt(testDate[2]);
}
if (month > 12 && month <= 31 && day <= 12) {
swap = month;
month = day;
day = swap;
}
if (month <= 12 && month > 0 && day <= 31 && day > 0 && year > 2000 && year < 4000) {
c.set(Calendar.DAY_OF_MONTH, day);
c.set(Calendar.MONTH, month);
c.set(Calendar.YEAR, year);
result = c.getTime();
break;
}
} catch (Exception ex) {
// nothing
}
}
date.delete(0, date.length());
}
}
return result;
}
 
public final static String getCleanNumberString(String s) {
if (s.indexOf('.') >= 0 && s.indexOf(',') >= 0) {
s = s.replace('.', ' ').replace(',', '.');
} else if (s.indexOf('.') < 0 && s.indexOf(',') >= 0) {
s = s.replace(',', '.');
}
 
final StringBuilder b = new StringBuilder();
final int length = s.length();
for (int i = 0; i < length; i++) {
final char c = s.charAt(i);
if (Character.isDigit(c) || c == '.') {
b.append(c);
}
}
return b.toString();
 
}
public final static boolean isLong(String text) {
try {
Long.parseLong(text);
return true;
} catch (Exception ex) {
return false;
}
}
 
public final static boolean isInteger(String text) {
try {
Integer.parseInt(text);
return true;
} catch (Exception ex) {
return false;
}
}
public static boolean compareDate(Date date1, Date date2){
final Calendar cal = Calendar.getInstance();
cal.setTime(date1);
final int year1 = cal.get(Calendar.YEAR), month1 = cal.get(Calendar.MONTH), day1 = cal.get(Calendar.DATE);
cal.setTime(date2);
final int year2 = cal.get(Calendar.YEAR), month2 = cal.get(Calendar.MONTH), day2 = cal.get(Calendar.DATE);
return (year1 == year2 && month1 == month2 && day1 == day2 );
}
 
private static Date getFromFrenchDate(String frenchText) {
final int i = frenchText.indexOf("20");
int year = -1;
if (i != -1 && frenchText.length() >= i + 4) {
try {
year = Integer.parseInt(frenchText.substring(i, i + 4));
} catch (Exception e) {
// nothing
}
frenchText = frenchText.toLowerCase();
int month = getFrenchMonth(frenchText.substring(0, i));
if (year < 2000 || month <= 0) {
return null;
}
final String[] split = frenchText.split("\\s+");
final Calendar d = Calendar.getInstance();
int day = 0;
for (int j = 0; j < split.length; j++) {
String sDay = split[j];
try {
day = Integer.parseInt(sDay);
if (day > 0 && day < 32) {
break;
}
} catch (Exception e) {
// nothing
}
try {
if (day != year) {
sDay = sDay.substring(0, 2);
day = Integer.parseInt(sDay);
if (day > 0 && day < 32) {
break;
}
}
} catch (Exception e) {
// nothing
}
}
if (day > 0) {
d.set(year, month, day, 0, 0, 0);
} else {
d.set(year, month, 1, 0, 0, 0);
}
return d.getTime();
} else {
return null;
}
}
 
private static int getFrenchMonth(String frenchDate){
int month = -1;
if (frenchDate.contains("anvier")) {
month = 0;
} else if (frenchDate.contains("vrier")) {
month = 1;
} else if (frenchDate.contains("mars")) {
month = 2;
} else if (frenchDate.contains("avr")) {
month = 3;
} else if (frenchDate.contains("mai")) {
month = 4;
} else if (frenchDate.contains("juin") || frenchDate.contains("ju1n") || (frenchDate.contains("ju") && frenchDate.contains("n"))) {
month = 5;
} else if (frenchDate.contains("llet") || (frenchDate.contains("ju") && frenchDate.contains("et"))) {
month = 6;
} else if (frenchDate.contains("ao")) {
month = 7;
} else if (frenchDate.contains("sept")) {
month = 8;
} else if (frenchDate.contains("oct") || frenchDate.contains("obre")) {
month = 9;
} else if (frenchDate.contains("novem")) {
month = 10;
} else if (frenchDate.contains("cembre") || frenchDate.contains("décerfibre")) {
month = 11;
}
return month;
}
public int LevenshteinDistance(String s0, String s1) {
int len0 = s0.length() + 1;
int len1 = s1.length() + 1;
// the array of distances
int[] cost = new int[len0];
int[] newcost = new int[len0];
// initial cost of skipping prefix in String s0
for (int i = 0; i < len0; i++) cost[i] = i;
// dynamically computing the array of distances
// transformation cost for each letter in s1
for (int j = 1; j < len1; j++) {
// initial cost of skipping prefix in String s1
newcost[0] = j;
// transformation cost for each letter in s0
for(int i = 1; i < len0; i++) {
// matching current letters in both strings
int match = (s0.charAt(i - 1) == s1.charAt(j - 1)) ? 0 : 1;
// computing cost for each transformation
int cost_replace = cost[i - 1] + match;
int cost_insert = cost[i] + 1;
int cost_delete = newcost[i - 1] + 1;
// keep minimum cost
newcost[i] = Math.min(Math.min(cost_insert, cost_delete), cost_replace);
}
// swap cost/newcost arrays
int[] swap = cost; cost = newcost; newcost = swap;
}
// the distance is the cost for transforming all letters in both strings
return cost[len0 - 1];
}
}
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/parser/ACTNInvoiceParser.java
New file
0,0 → 1,107
package org.openconcerto.modules.ocr.parser;
 
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
 
import org.openconcerto.modules.ocr.InvoiceOCR;
import org.openconcerto.modules.ocr.OCRLine;
import org.openconcerto.modules.ocr.OCRPage;
 
public class ACTNInvoiceParser extends AbstractInvoiceParser {
 
private final List<Date> dates = new ArrayList<Date>();
private boolean nextLineIsNumber;
 
@Override
public boolean parse(OCRPage page) {
if (!page.contains("ACTN")) {
return false;
}
 
final InvoiceOCR invoice = this.getInvoice();
final List<OCRLine> lines = page.getLines();
invoice.setSupplierName("ACTN");
try {
for (OCRLine line : lines) {
final String text = line.getText().toLowerCase();
 
if (this.getInvoice().getInvoiceNumber() == null && text.contains("facture") && !text.trim().isEmpty()) {
final String cText = text.replace('!', '/').replace(']', '/');
final String[] split = cText.split("\\s+");
invoice.setInvoiceNumber(split[split.length - 1]);
addHighlight(page, line);
}
if (invoice.getDate() == null && text.contains("page") && text.contains("date")) {
final String[] split = text.split("\\s+");
if(split.length > 2){
final int pageNum = Integer.parseInt(split[2]);
page.setPageNumber(pageNum);
addHighlight(page, line);
}
if(split.length > 6){
if(split[6].length() == 10){
final String strDate = split[6];
final Date d = ParserUtils.parseDate(strDate);
if(d != null){
invoice.setDate(d);
addHighlight(page, line);
}
}
}
}
if (text.contains("frais de port")) {
this.nextLineIsNumber = true;
} else if(invoice.getAmount() == null && invoice.getTax() == null && invoice.getAmountWithTax() == null && this.nextLineIsNumber && !text.trim().isEmpty()){
final String[] split = text.split("\\s+");
final List<String> tmp = new ArrayList<String>();
for(int i = 0; i < split.length; i++){
if(split[i].contains(",")){
tmp.add(split[i]);
}
}
if(tmp.size() > 0){
final String s = tmp.get(0).replace('.', ' ').replace(',', '.');
final String cleanNumberString = ParserUtils.getCleanNumberString(s);
final BigDecimal ht = new BigDecimal(cleanNumberString);
invoice.setAmount(ht);
}
if(tmp.size() > 3){
final String s = tmp.get(3).replace('.', ' ').replace(',', '.');
final String cleanNumberString = ParserUtils.getCleanNumberString(s);
final BigDecimal tva = new BigDecimal(cleanNumberString);
invoice.setTax(tva);
}
if(tmp.size() > 4){
final String s = tmp.get(4).replace('.', ' ').replace(',', '.');
final String cleanNumberString = ParserUtils.getCleanNumberString(s);
final BigDecimal ttc = new BigDecimal(cleanNumberString);
invoice.setAmountWithTax(ttc);
}
addHighlight(page, line);
this.nextLineIsNumber = false;
}
}
invoice.setHighlight(this.getHighlight());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
 
@Override
public void reset() {
super.reset();
this.needModePage = true;
this.dates.clear();;
this.nextLineIsNumber = false;
}
 
@Override
protected boolean checkInvoiceNumber(String invoiceNumber) {
// TODO Auto-generated method stub
return false;
}
}
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/parser/AmazonInvoiceParser.java
New file
0,0 → 1,100
package org.openconcerto.modules.ocr.parser;
 
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
 
import org.openconcerto.modules.ocr.InvoiceOCR;
import org.openconcerto.modules.ocr.OCRLine;
import org.openconcerto.modules.ocr.OCRPage;
 
 
public class AmazonInvoiceParser extends AbstractInvoiceParser {
private boolean nextLineIsNumber;
 
@Override
public boolean parse(OCRPage page) {
if (!page.contains("Amazon")) {
return false;
}
 
final InvoiceOCR invoice = this.getInvoice();
final List<OCRLine> lines = page.getLines();
invoice.setSupplierName("Amazon");
try {
for (OCRLine line : lines) {
final String text = line.getText().toLowerCase();
if (this.getInvoice().getInvoiceNumber() == null && text.startsWith("votre commande")) {
final String[] split = text.split("\\s+");
if(split.length == 12){
final Date d = ParserUtils.parseDate(split[3] + " " + split[4] + " " + split[5]);
if(d != null){
invoice.setDate(d);
}
invoice.setInvoiceNumber(split[7]);
}
final String n = split[split.length - 1];
invoice.setInvoiceNumber(n);
addHighlight(page, line);
}
if (text.contains("Regu (réglé)")) {
invoice.setInvoiceNumber("Ceci n'est pas une facture.");
addHighlight(page, line);
}
if (invoice.getAmount() == null && invoice.getTax() == null && invoice.getAmountWithTax() == null && text.contains("frais de port")) {
this.nextLineIsNumber = true;
} else if(this.nextLineIsNumber && !text.trim().isEmpty()){
final String[] split = text.split("\\s+");
final List<String> tmp = new ArrayList<String>();
for(int i = 0; i < split.length; i++){
if(split[i].contains(",")){
tmp.add(split[i]);
}
}
if(tmp.size() > 0){
final String s = tmp.get(0).replace('.', ' ').replace(',', '.');
final String cleanNumberString = ParserUtils.getCleanNumberString(s);
 
final BigDecimal ht = new BigDecimal(cleanNumberString);
invoice.setAmount(ht);
}
if(tmp.size() > 3){
final String s = tmp.get(3).replace('.', ' ').replace(',', '.');
final String cleanNumberString = ParserUtils.getCleanNumberString(s);
 
final BigDecimal tva = new BigDecimal(cleanNumberString);
invoice.setTax(tva);
}
if(tmp.size() > 4){
final String s = tmp.get(4).replace('.', ' ').replace(',', '.');
final String cleanNumberString = ParserUtils.getCleanNumberString(s);
 
final BigDecimal ttc = new BigDecimal(cleanNumberString);
invoice.setAmountWithTax(ttc);
}
addHighlight(page, line);
this.nextLineIsNumber = false;
}
}
invoice.setHighlight(this.getHighlight());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
 
@Override
public void reset() {
super.reset();
this.needModePage = true;
this.nextLineIsNumber = false;
}
 
@Override
protected boolean checkInvoiceNumber(String invoiceNumber) {
// TODO Auto-generated method stub
return false;
}
}
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/parser/LaPosteInvoiceParser.java
New file
0,0 → 1,141
package org.openconcerto.modules.ocr.parser;
 
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
import org.openconcerto.modules.ocr.InvoiceOCR;
import org.openconcerto.modules.ocr.OCRLine;
import org.openconcerto.modules.ocr.OCRPage;
 
public class LaPosteInvoiceParser extends AbstractInvoiceParser {
private boolean pageNumberNear = false;
 
@Override
public boolean parse(OCRPage page) {
if (!page.contains("la poste") && !page.contains("la paste")) {
return false;
}
final InvoiceOCR invoice = this.getInvoice();
final List<OCRLine> lines = page.getLines();
invoice.setSupplierName("La poste");
try {
for (OCRLine line : lines) {
final String text = line.getText().toLowerCase();
// Get invoice number
if(invoice.getInvoiceNumber() == null && text.contains("lp")) {
final int indexStart = text.indexOf("lp");
final int indexStop = indexStart + 16;
if(text.length() >= indexStop){
final String n = text.substring(indexStart, indexStop);
if(!n.contains(" ")){
invoice.setInvoiceNumber(n.toUpperCase());
}
}
}
// Get current page number
if (invoice.getTotalPage() == -1 && text.contains("page") && text.contains("/")) {
final Pattern p = Pattern.compile(".*page.*(\\d+)\\((\\d+)\\).*");
final Matcher m = p.matcher(text);
final boolean b = m.matches();
// If find
if (b && m.groupCount() >= 2) {
final int number = Integer.valueOf(m.group(1));
final int total = Integer.valueOf(m.group(2));
if (number <= total) {
page.setPageNumber(number);
invoice.setTotalPage(total);
addHighlight(page, line);
}
}
}
// Page informations are maybe in next lines
if(invoice.getTotalPage() == -1 && text.contains("page")){
this.pageNumberNear = true;
}
// Get current page number
if(this.pageNumberNear && text.contains("/")){
final Pattern p = Pattern.compile(".*page.*(\\d+)\\((\\d+)\\).*");
final Matcher m = p.matcher(text);
final boolean b = m.matches();
// If find
if (b && m.groupCount() >= 2) {
final int number = Integer.valueOf(m.group(1));
final int total = Integer.valueOf(m.group(2));
this.pageNumberNear = false;
if (number <= total) {
page.setPageNumber(number);
invoice.setTotalPage(total);
addHighlight(page, line);
}
}
}
// Get amount with tax
if (this.getInvoice().getAmountWithTax() == null && text.contains("net") && text.contains("payer")) {
final List<BigDecimal> listWithTax = getDecimalsInLine(text);
if(listWithTax.size() > 0){
invoice.setAmountWithTax(listWithTax.get(0));
addHighlight(page, line);
}
}
// Get amount
if (this.getInvoice().getAmount() == null && (text.contains("total") || text.contains("totat")) && text.contains("net")) {
final List<BigDecimal> listAmount = getDecimalsInLine(text);
if(listAmount.size() > 0){
invoice.setAmount(listAmount.get(0));
addHighlight(page, line);
}
}
// Get tax
if(this.getInvoice().getTax() == null && text.trim().contains("tva")){
final List<BigDecimal> listTax = getDecimalsInLine(text);
if(listTax.size() > 0){
invoice.setTax(listTax.get(0));
addHighlight(page, line);
}
}
// Get date
if(invoice.getDate() == null && text.contains("le")) {
final String[] split = text.split("\\s+");
if(split.length > 1){
final Date d = ParserUtils.parseDate(split[1]);
if (d != null) {
invoice.setDate(d);
addHighlight(page, line);
}
}
}
}
checkInvoice(false);
invoice.setHighlight(this.getHighlight());
} catch (Exception e) {
return false;
}
return true;
}
 
@Override
public void reset() {
super.reset();
this.pageNumberNear = false;
}
 
@Override
protected boolean checkInvoiceNumber(String invoiceNumber) {
boolean result = true;
final int numberLength = invoiceNumber.length();
if(numberLength != 16){
result = false;
} else if(!ParserUtils.isLong(invoiceNumber.substring(2, numberLength))){
result = false;
} else if(!invoiceNumber.startsWith("LP")){
result = false;
}
return result;
}
}
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/parser/OrangeInvoiceParser.java
New file
0,0 → 1,174
package org.openconcerto.modules.ocr.parser;
 
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
 
import org.openconcerto.modules.ocr.InvoiceOCR;
import org.openconcerto.modules.ocr.OCRLine;
import org.openconcerto.modules.ocr.OCRPage;
 
public class OrangeInvoiceParser extends AbstractInvoiceParser {
@Override
public boolean parse(OCRPage page) {
if (!page.contains("orange") || page.contains("tech data")) {
return false;
}
final InvoiceOCR invoice = this.getInvoice();
final List<OCRLine> lines = page.getLines();
invoice.setSupplierName("Orange");
try {
for (OCRLine line : lines) {
final String text = line.getText().toLowerCase();
// Get invoice number
if (invoice.getInvoiceNumber() == null) {
final String[] split = text.split("\\s+");
final StringBuilder n = new StringBuilder();
if (split.length > 2) {
final String label = split[0].replace("li", "u").replace("t", "f");
if(label.length() == 7 && label.startsWith("f")){
int splitLength = split.length;
for(int i = 2; i < splitLength; i++){
n.append(split[i]);
}
final String[] split2 = n.toString().replace(":", "-").replace("—", "-").replace(".", "-").replace("~", "-").replace(" ", "").replace("(", "").split("-");
if(split2.length > 1){
String subs = split2[0].substring(split2[0].length() - 2, split2[0].length() - 1);
if(ParserUtils.isInteger(subs)){
subs = (subs.equals("6") || subs.equals("3")) ? "g" : subs;
subs = (subs.equals("1")) ? "l" : subs;
}
split2[0] = split2[0].substring(0, split2[0].length() - 2) + subs + split2[0].substring(split2[0].length() - 1, split2[0].length());
}
n.delete(0, n.length());
int split2Length = split2.length;
for(int i = 0; i < split2Length; i++){
n.append(split2[i]);
}
if(n.length() > 9){
n.insert(10, " ");
if(n.length() > 15){
String tempChar = n.substring(1, 2);
if(tempChar.equals("8")){
n.replace(1, 2, "3");
}
tempChar = n.substring(13, 14);
if(ParserUtils.isInteger(tempChar)){
if(tempChar.equals("9")){
n.replace(13, 14, "G");
}
}
tempChar = n.substring(14, 15);
if(!ParserUtils.isInteger(tempChar)){
if(tempChar.equals("o")){
n.replace(14, 15, "0");
}
}
tempChar = n.substring(15, 16);
if(!ParserUtils.isInteger(tempChar)){
if(tempChar.equals("i") || tempChar.equals("l")){
n.replace(15, 16, "1");
}
}
tempChar = n.substring(17, 18);
if(!ParserUtils.isInteger(tempChar)){
if(tempChar.equals("m")){
n.replace(18, 19, "11");
}
}
n.insert(15, " - ");
if(n.length() >= 22){
tempChar = n.substring(19, 20);
if(!tempChar.equals("f")){
n.replace(19, 20, "f");
}
tempChar = n.substring(21, 22);
if(!ParserUtils.isInteger(tempChar)){
if(tempChar.equals("o")){
n.delete(21, 22);
n.insert(21, "0");
}
}
n.delete(22, n.length());
}
}
}
String finalNumber = n.toString();
if(checkInvoiceNumber(finalNumber)){
invoice.setInvoiceNumber(finalNumber.toUpperCase());
}
}
}
}
// Get amount and amount with tax
if (invoice.getAmount() == null && text.contains("total de votre facture")) {
List<BigDecimal> listValue = getDecimalsInLine(text);
if (listValue.size() > 1) {
invoice.setAmount(listValue.get(0));
invoice.setAmountWithTax(listValue.get(1));
addHighlight(page, line);
}
}
// Get current page number and total page
if (invoice.getTotalPage() == -1 && text.contains("page")) {
final String[] split = text.split("\\s+");
final String[] split2 = split[split.length - 1].split("/");
if (split2.length > 1) {
if(ParserUtils.isInteger(split[0]) && ParserUtils.isInteger(split[1])){
page.setPageNumber(Integer.parseInt(split2[0]));
invoice.setTotalPage(Integer.parseInt(split2[1]));
}
}
}
// Get date
if(invoice.getDate() == null && text.contains("du")){
final Date d = ParserUtils.parseDate(text);
if (d != null) {
invoice.setDate(d);
addHighlight(page, line);
}
}
}
this.checkInvoice(true);
invoice.setHighlight(this.getHighlight());
} catch (Exception e) {
return false;
}
return true;
}
 
@Override
public void reset() {
super.reset();
}
 
@Override
protected boolean checkInvoiceNumber(String invoiceNumber) {
boolean result = true;
final String[] split = invoiceNumber.split("\\s+");
if(split.length != 4){
result = false;
} else {
if(split[0].length() != 10 || !ParserUtils.isLong(split[0])){
result = false;
} else if(split[1].length() != 4){
result = false;
} else if(!split[2].equals("-")){
result = false;
} else if(split[3].length() != 4){
result = false;
}
}
return result;
}
}
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/parser/GenericInvoiceParser.java
New file
0,0 → 1,127
package org.openconcerto.modules.ocr.parser;
 
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
import org.openconcerto.modules.ocr.InvoiceOCR;
import org.openconcerto.modules.ocr.OCRLine;
import org.openconcerto.modules.ocr.OCRPage;
 
public class GenericInvoiceParser extends AbstractInvoiceParser {
private final List<Date> dates = new ArrayList<Date>();
 
@Override
public boolean parse(OCRPage page) {
final InvoiceOCR invoice = this.getInvoice();
final List<OCRLine> lines = page.getLines();
invoice.setSupplierName("??");
for (OCRLine line : lines) {
try {
final String text = line.getText().toLowerCase();
// Get page number
if (text.contains("page") && text.contains("/")) {
final Pattern p = Pattern.compile(".*page.*(\\d+)/(\\d+).*");
final Matcher m = p.matcher(text);
final boolean b = m.matches();
System.out.println(text + " : " + b + " " + m.groupCount());
// si recherche fructueuse
if (b && m.groupCount() >= 2) {
final int number = Integer.valueOf(m.group(1));
final int total = Integer.valueOf(m.group(2));
if (number <= total) {
page.setPageNumber(number);
invoice.setTotalPage(total);
}
}
}
// Get amount
if (invoice.getAmount() == null && text.startsWith("total ht")) {
final List<BigDecimal> listAmount = getDecimalsInLine(text);
if(listAmount.size() > 0){
invoice.setAmount(listAmount.get(0));
this.addHighlight(page, line);
}
}
// Get amount with tax
if (invoice.getAmountWithTax() == null && text.startsWith("total ttc")) {
final List<BigDecimal> listAmountWithTax = getDecimalsInLine(text);
if(listAmountWithTax.size() > 0){
invoice.setAmountWithTax(listAmountWithTax.get(0));
this.addHighlight(page, line);
}
}
// Get tax
if (invoice.getTax() == null && text.contains("montant") && text.contains("tva") ) {
final List<BigDecimal> listTax = getDecimalsInLine(text);
if(listTax.size() > 0){
invoice.setTax(listTax.get(0));
this.addHighlight(page, line);
}
}
// Get invoice number
if (invoice.getInvoiceNumber() == null && text.contains("facture") && text.contains("n")) {
final String[] split = text.split("\\s+");
if(split.length > 1){
final int splitLength = split.length;
for(int i = 0; i < splitLength; i++){
final String n = split[i].trim();
final int nLength = n.length();
int startIndex = -1;
int stopIndex = -1;
for(int j = 0; j < nLength; j++){
final String c = n.substring(j, j + 1);
if(ParserUtils.isInteger(c)){
if(startIndex == -1){
startIndex = j;
}
} else if(startIndex != -1){
stopIndex = j;
}
}
if(startIndex != -1){
if(stopIndex == -1){
stopIndex = nLength;
}
if(stopIndex - startIndex > 4){
invoice.setInvoiceNumber(split[i]);
this.addHighlight(page, line);
break;
}
}
}
}
}
// Get date
if(invoice.getDate() == null && !text.trim().equals("")){
final Date d = ParserUtils.parseDate(text);
if (d != null) {
this.dates.add(d);
invoice.setDate(d);
this.addHighlight(page, line);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
invoice.setHighlight(this.getHighlight());
return true;
}
@Override
public void reset() {
super.reset();
this.dates.clear();;
}
 
@Override
protected boolean checkInvoiceNumber(String invoiceNumber) {
// TODO Auto-generated method stub
return false;
}
}
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/parser/OVHInvoiceParser.java
New file
0,0 → 1,224
package org.openconcerto.modules.ocr.parser;
 
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
import org.openconcerto.modules.ocr.InvoiceOCR;
import org.openconcerto.modules.ocr.OCRLine;
import org.openconcerto.modules.ocr.OCRPage;
 
public class OVHInvoiceParser extends AbstractInvoiceParser {
private final List<Date> dates = new ArrayList<Date>();
private boolean pageNumberNear = false;
private boolean isTotalPage = false;
 
@Override
public boolean parse(OCRPage page) {
if (!page.contains("OVH")) {
return false;
}
 
final InvoiceOCR invoice = this.getInvoice();
final List<OCRLine> lines = page.getLines();
final int lineCount = lines.size();
invoice.setSupplierName("OVH");
try {
for (int i = 0; i < lineCount; i++) {
final OCRLine line = lines.get(i);
final String text = line.getText().toLowerCase();
 
// Get invoice number
if (invoice.getInvoiceNumber() == null && text.replace(" ", "").startsWith("facture")) {
final String cText = text.replace('!', '/').replace(']', '/');
final String[] split = cText.split("\\:");
if (split.length > 1 && checkInvoiceNumber(split[1])) {
final String n = split[1].substring(0, 10).trim();
if (!n.equals("")) {
invoice.setInvoiceNumber(n);
}
}
final String[] split2 = cText.split("/");
if (split2.length > 2) {
String strDate = split2[1].trim().replace('-', '.');
final String[] partDate = strDate.split("\\.");
 
if (partDate.length > 2) {
System.out.println(text);
System.out.println(strDate);
 
try {
int d = Integer.parseInt(partDate[0]);
int m = Integer.parseInt(partDate[1]) - 1;
int y = Integer.parseInt(partDate[2]);
Calendar cal = Calendar.getInstance();
cal.set(y, m, d, 0, 0, 0);
invoice.setDate(cal.getTime());
 
} catch (Exception e) {
// nothing
}
}
}
addHighlight(page, line);
}
// Get invoice number
if (invoice.getInvoiceNumber() == null && text.startsWith("fr") || text.startsWith("sysfr")) {
int indexStart = text.indexOf("sysfr");
if (indexStart == -1) {
indexStart = text.indexOf("fr");
}
final int indexNextSpace = text.indexOf(" ", indexStart);
final int indexStop = (indexNextSpace == -1) ? text.length() : indexNextSpace;
final int numberLength = indexStop - indexStart;
if (numberLength == 10 || numberLength == 9) {
final String n = text.substring(indexStart, indexStop).trim().replace("g", "9").replace("q", "9").toUpperCase();
if (!n.equals("")) {
invoice.setInvoiceNumber(n);
addHighlight(page, line);
}
}
}
// Get current page number
if (invoice.getTotalPage() == -1 && text.contains("page") && text.contains("(")) {
final Pattern p = Pattern.compile(".*page.*(\\d+)\\((\\d+)\\).*");
final Matcher m = p.matcher(text);
final boolean b = m.matches();
// If find
if (b && m.groupCount() >= 2) {
final int number = Integer.valueOf(m.group(1));
final int total = Integer.valueOf(m.group(2));
if (number <= total) {
page.setPageNumber(number);
invoice.setTotalPage(total);
addHighlight(page, line);
}
}
}
// Page informations are maybe in next lines
if (invoice.getTotalPage() == -1 && text.contains("page")) {
this.pageNumberNear = true;
// Get current page number
} else if (this.pageNumberNear && text.contains("(")) {
final Pattern p = Pattern.compile(".*page.*(\\d+)\\((\\d+)\\).*");
final Matcher m = p.matcher(text);
final boolean b = m.matches();
// If find
if (b && m.groupCount() >= 2) {
final int number = Integer.valueOf(m.group(1));
final int total = Integer.valueOf(m.group(2));
this.pageNumberNear = false;
if (number <= total) {
page.setPageNumber(number);
invoice.setTotalPage(total);
addHighlight(page, line);
}
}
 
}
 
// Get amount with tax
if (invoice.getAmountWithTax() == null && text.contains("total") && !text.contains("sous")) {
final List<BigDecimal> listTTC = getDecimalsInLine(text);
if (listTTC.size() > 0) {
invoice.setAmountWithTax(listTTC.get(0));
addHighlight(page, line);
}
}
// Get amount
if (invoice.getAmount() == null && (text.trim().startsWith("prix") || text.trim().startsWith("fiix") || text.trim().startsWith("frix"))) {
final List<BigDecimal> listHT = getDecimalsInLine(text);
if (listHT.size() > 0) {
invoice.setAmount(listHT.get(0));
addHighlight(page, line);
}
}
// Get tax
if (invoice.getTax() == null && text.trim().contains("tva")) {
final List<BigDecimal> listTVA = getDecimalsInLine(text);
if (listTVA.size() > 0) {
invoice.setTax(listTVA.get(0));
addHighlight(page, line);
}
}
// Get all amounts
if (invoice.getAmount() == null && invoice.getAmountWithTax() == null
&& (text.trim().equals("prix") || text.trim().equals("frix") || text.trim().equals("fiix") || text.trim().equals("prix ht"))) {
boolean end = false;
int j = i + 1;
List<BigDecimal> list;
String cText;
while (!end) {
cText = lines.get(j).getText().toLowerCase().replace("e", "€").replace("€", "");
list = getDecimalsInLine(cText);
if (list.size() == 0 && !ParserUtils.isInteger(cText)) {
end = true;
}
j++;
}
if (j - i > 4) {
cText = lines.get(j - 2).getText().toLowerCase();
list = getDecimalsInLine(cText);
if (list.size() > 0) {
invoice.setAmountWithTax(list.get(0));
}
cText = lines.get(j - 3).getText().toLowerCase();
list = getDecimalsInLine(cText);
if (list.size() > 0) {
invoice.setTax(list.get(0));
}
 
cText = lines.get(j - 4).getText().toLowerCase();
list = getDecimalsInLine(cText);
if (list.size() > 0) {
invoice.setAmount(list.get(0));
}
}
}
// Get date
if (invoice.getDate() == null && !text.trim().equals("")) {
final Date d = ParserUtils.parseDate(text);
if (d != null) {
this.dates.add(d);
invoice.setDate(d);
addHighlight(page, line);
}
}
}
checkInvoice(false);
invoice.setHighlight(this.getHighlight());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
 
@Override
public void reset() {
super.reset();
this.dates.clear();
this.pageNumberNear = false;
this.isTotalPage = false;
}
 
@Override
protected boolean checkInvoiceNumber(String invoiceNumber) {
boolean result = true;
final String cInvoiceNumber = invoiceNumber.trim().toLowerCase();
final boolean bSys = cInvoiceNumber.startsWith("sysfr");
final int numberLength = cInvoiceNumber.length();
if (!cInvoiceNumber.startsWith("fr") && !bSys) {
result = false;
} else if (bSys && numberLength > 5 && !ParserUtils.isLong(cInvoiceNumber.substring(5, numberLength))) {
result = false;
} else if (numberLength > 2 && !ParserUtils.isLong(cInvoiceNumber.substring(2, numberLength))) {
result = false;
}
return result;
}
}
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/parser/TechDataInvoiceParser.java
New file
0,0 → 1,148
package org.openconcerto.modules.ocr.parser;
 
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
import org.openconcerto.modules.ocr.InvoiceOCR;
import org.openconcerto.modules.ocr.OCRLine;
import org.openconcerto.modules.ocr.OCRPage;
 
public class TechDataInvoiceParser extends AbstractInvoiceParser {
 
private final List<Date> dates = new ArrayList<Date>();
private boolean numberIsNear = false;
private boolean pageNumberNear = false;
 
@Override
public boolean parse(OCRPage page) {
if (!page.contains("tech data")) {
return false;
}
final InvoiceOCR invoice = this.getInvoice();
final List<OCRLine> lines = page.getLines();
invoice.setSupplierName("Tech Data");
try {
for (OCRLine line : lines) {
final String text = line.getText().toLowerCase();
// Maybe the invoice number is in next lines
if ((text.contains("date") || text.contains("dme")) && (text.contains("document") || text.contains("docmnmn"))) {
this.numberIsNear = true;
// Get invoice number
} else if (invoice.getInvoiceNumber() == null && this.numberIsNear) {
final String cText = text.replace('!', '/').replace(']', '/');
final String[] split = cText.split("\\/");
if (split.length > 2) {
if (split[2].trim().length() == 8) {
final String n = split[2];
invoice.setInvoiceNumber(n.trim());
}
final String strDate = split[1].trim();
final Date d = ParserUtils.parseDate(strDate);
if (d != null) {
invoice.setDate(d);
this.numberIsNear = false;
addHighlight(page, line);
}
}
 
}
// Get current page number and total pages
if (invoice.getTotalPage() == -1 && text.contains("page") && text.contains("(")) {
final Pattern p = Pattern.compile(".*page.*(\\d+)\\((\\d+)\\).*");
final Matcher m = p.matcher(text);
final boolean b = m.matches();
// If find
if (b && m.groupCount() >= 2) {
final int number = Integer.valueOf(m.group(1));
final int total = Integer.valueOf(m.group(2));
if (number <= total) {
page.setPageNumber(number);
invoice.setTotalPage(total);
addHighlight(page, line);
}
}
}
// Page number is maybe in next lines
if (invoice.getTotalPage() == -1 && text.contains("page")) {
this.pageNumberNear = true;
// Search of current page number and total pages
} else if (this.pageNumberNear && text.contains("(")) {
final Pattern p = Pattern.compile(".*page.*(\\d+)\\((\\d+)\\).*");
final Matcher m = p.matcher(text);
final boolean b = m.matches();
// If find
if (b && m.groupCount() >= 2) {
final int number = Integer.valueOf(m.group(1));
final int total = Integer.valueOf(m.group(2));
this.pageNumberNear = false;
addHighlight(page, line);
if (number <= total) {
page.setPageNumber(number);
invoice.setTotalPage(total);
addHighlight(page, line);
}
}
}
 
// FIXME : espace dans le montant
// Get amount with tax
if (invoice.getAmountWithTax() == null && (text.contains("net a payer") || text.contains("neta payer") || text.contains("netapayer"))) {
final List<BigDecimal> listWithTax = getDecimalsInLine(text);
if (listWithTax.size() > 0) {
invoice.setAmountWithTax(listWithTax.get(0));
addHighlight(page, line);
}
}
// Get amount
if (text.trim().startsWith("bases") && this.getInvoice().getAmount() == null) {
final List<BigDecimal> listAmount = getDecimalsInLine(text);
if (listAmount.size() > 0) {
invoice.setAmount(listAmount.get(0));
addHighlight(page, line);
}
}
// Get tax
if (text.trim().startsWith("total t.v.a.") && this.getInvoice().getTax() == null) {
final List<BigDecimal> listTax = getDecimalsInLine(text);
if (listTax.size() > 0) {
invoice.setTax(listTax.get(0));
addHighlight(page, line);
}
}
// Get date
if (invoice.getDate() == null && !text.trim().equals("")) {
final Date d = ParserUtils.parseDate(text);
if (d != null) {
this.dates.add(d);
invoice.setDate(d);
addHighlight(page, line);
}
}
}
missValueCalcul();
invoice.setHighlight(this.getHighlight());
} catch (Exception e) {
return false;
}
return true;
}
 
@Override
public void reset() {
super.reset();
this.dates.clear();
this.numberIsNear = false;
this.pageNumberNear = false;
}
 
@Override
protected boolean checkInvoiceNumber(String invoiceNumber) {
// TODO Auto-generated method stub
return false;
}
}
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/parser/BNPInvoiceParser.java
New file
0,0 → 1,192
package org.openconcerto.modules.ocr.parser;
 
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
import org.openconcerto.modules.ocr.InvoiceOCR;
import org.openconcerto.modules.ocr.OCRLine;
import org.openconcerto.modules.ocr.OCRPage;
 
public class BNPInvoiceParser extends AbstractInvoiceParser {
 
private final List<Date> dates = new ArrayList<Date>();
 
@Override
public boolean parse(OCRPage page) {
if (!((page.contains("bnp paribas") || page.contains("bn p pari bas")) && page.contains("domiciliation"))) {
return false;
}
boolean result = true;
final InvoiceOCR invoice = this.getInvoice();
final List<OCRLine> lines = page.getLines();
final int lineCount = lines.size();
invoice.setSupplierName("BNP Paribas");
try {
for (int i = 0; i < lineCount; i++) {
final OCRLine line = lines.get(i);
final String text = line.getText().toLowerCase();
// Get current page number and total page number
if (invoice.getTotalPage() == -1 && text.contains("page") && text.contains("/")) {
final Pattern p = Pattern.compile(".*page.*(\\d+)/(\\d+).*");
final Matcher m = p.matcher(text);
final boolean b = m.matches();
// if find
if (b && m.groupCount() >= 2) {
final int number = Integer.valueOf(m.group(1));
final int total = Integer.valueOf(m.group(2));
if (number <= total) {
page.setPageNumber(number);
invoice.setTotalPage(total);
this.addHighlight(page, line);
}
}
}
// Get invoice number
if (invoice.getInvoiceNumber() == null && text.startsWith("facture")) {
final String[] split = text.split("\\s+");
StringBuilder invoiceNumber = new StringBuilder();
if (split.length > 3 && split[1].contains("n")) {
for (int j = 2; j < split.length; j++) {
invoiceNumber.append(split[j]);
}
if (!invoiceNumber.equals("")) {
final int index = invoiceNumber.indexOf("fr");
if(index == 0){
invoiceNumber.replace(2, 3, "/");
invoiceNumber.insert(7, " ");
invoiceNumber.insert(invoiceNumber.length() - 3, " ");
}
invoice.setInvoiceNumber(invoiceNumber.toString().toUpperCase());
this.addHighlight(page, line);
}
}
}
// Get amount, amount with tax and tax
if (invoice.getAmount() == null && text.trim().startsWith("total")) {
final List<BigDecimal> listDecimal = getDecimalsInLine(text);
final int listDecimalSize = listDecimal.size();
if (listDecimalSize > 2) {
invoice.setAmount(listDecimal.get(0));
invoice.setTax(listDecimal.get(1));
invoice.setAmountWithTax(listDecimal.get(2));
addHighlight(page, line);
} else if (text.contains("000") && listDecimalSize > 1) {
invoice.setAmount(listDecimal.get(0));
invoice.setTax(new BigDecimal(0.00));
invoice.setAmountWithTax(listDecimal.get(1));
} else if (text.trim().equals("total") || (text.trim().startsWith("total") && listDecimalSize == 1)) {
boolean amountFind = false, taxFind = false, amountWithTaxFind = false;
final int limit = i + 7;
for (int j = i; j < limit && j < lineCount && !(amountFind && taxFind && amountWithTaxFind); j++) {
final String ctext = lines.get(j).getText().replace(" ", "").toLowerCase();
if (!ctext.trim().equals("total")) {
final List<BigDecimal> list = getDecimalsInLine(ctext);
if (!amountFind) {
if (list.size() > 0) {
invoice.setAmount(list.get(0));
}
if (ctext.trim().length() > 0) {
amountFind = true;
}
} else if (!taxFind) {
if (list.size() > 0) {
invoice.setTax(list.get(0));
}
if (ctext.trim().length() > 0) {
taxFind = true;
}
} else if (!amountWithTaxFind) {
if (list.size() > 0) {
invoice.setAmountWithTax(list.get(0));
}
if (ctext.trim().length() > 0) {
amountWithTaxFind = true;
}
}
}
}
}
if(invoice.getAmount() != null && invoice.getAmountWithTax() != null && invoice.getTax() != null && !invoice.checkAmounts()){
invoice.setNullAmounts();
}
}
// Get date
if (invoice.getDate() == null && !text.trim().equals("")) {
final Date d = ParserUtils.parseDate(text);
if (d != null) {
this.dates.add(d);
if(text.startsWith("le ") || text.startsWith("ie ")){
invoice.setDate(d);
}
}
}
// Check if it's invoice or not
if(!invoice.getIsNotInvoice() && !readValidity(text)){
invoice.setIsNotInvoice(true);
}
}
if(invoice.getDate() == null){
final int dateSize = this.dates.size();
Date dateMax = null;
for(int i = 0; i < dateSize; i++){
Date d1 = this.dates.get(i);
if(dateMax == null || d1.compareTo(dateMax) > 0){
dateMax = d1;
}
}
invoice.setDate(dateMax);
}
this.checkInvoice(false);
invoice.setHighlight(this.getHighlight());
} catch (Exception ex) {
result = false;
ex.printStackTrace();
}
return result;
}
 
@Override
public void reset() {
super.reset();
this.dates.clear();
}
protected boolean readValidity(String text){
final String cText = text.replace("é", "e");
if(cText.contains("releve") && cText.contains("change")){
return false;
}
return true;
}
 
@Override
protected boolean checkInvoiceNumber(String invoiceNumber) {
boolean result = true;
final int numberLength = invoiceNumber.length();
final boolean shortNumber = (numberLength == 17);
final boolean longNumber = (numberLength == 29);
final String cInvoiceNumber = invoiceNumber.toLowerCase();
if(!longNumber && !shortNumber){
result = false;
} else if(shortNumber){
if(!ParserUtils.isLong(cInvoiceNumber)){
result = false;
}
} else if(longNumber){
if(!ParserUtils.isLong(cInvoiceNumber.substring(8, numberLength - 4))){
result = false;
} else if(!cInvoiceNumber.startsWith("fr/bddf") && !cInvoiceNumber.endsWith("pef")){
result = false;
}
}
return result;
}
}
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/parser/InvoiceParser.java
New file
0,0 → 1,17
package org.openconcerto.modules.ocr.parser;
 
import org.openconcerto.modules.ocr.InvoiceOCR;
import org.openconcerto.modules.ocr.OCRPage;
 
public interface InvoiceParser {
public boolean parse(OCRPage page);
 
public boolean isValid();
 
public boolean needModePage();
 
public void reset();
 
public InvoiceOCR getInvoice();
 
}
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/InvoiceViewer.java
New file
0,0 → 1,149
package org.openconcerto.modules.ocr;
 
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
 
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
 
public class InvoiceViewer extends JPanel {
private JButton bNext;
private JButton bPrevious;
private JLabel label;
private int currentInvoiceIndex = -1;
private int currentPageIndex = -1;
private List<InvoiceOCR> invoices = new ArrayList<InvoiceOCR>();
private JComponent oldPanel;
 
public InvoiceViewer() {
this.setLayout(new BorderLayout());
this.bNext = new JButton("->");
this.bPrevious = new JButton("<-");
this.label = new JLabel("Analyse en cours...");
final JPanel toolbar = new JPanel();
toolbar.setLayout(new FlowLayout(FlowLayout.LEFT));
toolbar.add(this.bPrevious);
toolbar.add(this.bNext);
toolbar.add(this.label);
this.add(toolbar, BorderLayout.NORTH);
this.oldPanel = new JScrollPane(new JPanel());
this.add(this.oldPanel, BorderLayout.CENTER);
this.bNext.addActionListener(new ActionListener() {
 
@Override
public void actionPerformed(ActionEvent e) {
next();
}
});
this.bPrevious.addActionListener(new ActionListener() {
 
@Override
public void actionPerformed(ActionEvent e) {
previous();
}
});
 
}
 
private void clearPageImage(int invoiceIndex, int pageIndex) {
this.invoices.get(invoiceIndex).getPage(pageIndex).clearImage();
}
 
protected void next() {
if (this.invoices.isEmpty()) {
return;
}
final InvoiceOCR invoice = this.invoices.get(this.currentInvoiceIndex);
final int pageCount = invoice.getPageCount();
final int lastInvoiceIndex = this.currentInvoiceIndex;
final int lastPageIndex = this.currentPageIndex;
if (this.currentPageIndex < pageCount - 1) {
this.currentPageIndex++;
} else {
if (this.currentInvoiceIndex < this.invoices.size() - 1) {
this.currentPageIndex = 0;
this.currentInvoiceIndex++;
}
}
 
try {
select(this.invoices.get(this.currentInvoiceIndex), this.currentPageIndex);
} catch (IOException e) {
// nothing
}
 
clearPageImage(lastInvoiceIndex, lastPageIndex);
}
 
protected void previous() {
if (this.invoices.isEmpty()) {
return;
}
final int lastInvoiceIndex = this.currentInvoiceIndex;
final int lastPageIndex = this.currentPageIndex;
if (this.currentPageIndex > 0) {
this.currentPageIndex--;
} else {
if (this.currentInvoiceIndex > 0) {
 
this.currentInvoiceIndex--;
this.currentPageIndex = this.invoices.get(this.currentInvoiceIndex).getPageCount() - 1;
}
}
try {
clearPageImage(lastInvoiceIndex, lastPageIndex);
select(this.invoices.get(this.currentInvoiceIndex), this.currentPageIndex);
} catch (IOException e) {
e.printStackTrace();
}
}
 
public void select(InvoiceOCR invoice, int page) throws IOException {
for (int i = 0; i < this.invoices.size(); i++) {
if (this.invoices.get(i) == invoice) {
this.currentInvoiceIndex = i;
}
}
this.currentPageIndex = page;
this.label.setText("Facture " + (this.currentInvoiceIndex + 1) + " page " + (this.currentPageIndex + 1) + " / " + invoice.getPageCount());
this.remove(this.oldPanel);
final JScrollPane scroll = new JScrollPane(new InvoiceRendererComponent(invoice, invoice.getPage(this.currentPageIndex)));
scroll.setOpaque(false);
scroll.getHorizontalScrollBar().setUnitIncrement(30);
scroll.getVerticalScrollBar().setUnitIncrement(30);
scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
scroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
this.oldPanel = scroll;
this.add(this.oldPanel, BorderLayout.CENTER);
this.invalidate();
this.revalidate();
}
 
public void add(InvoiceOCR invoice) {
this.invoices.add(invoice);
if (this.invoices.size() == 1 && invoice.getPageCount() > 0) {
try {
select(invoice, 0);
} catch (IOException e) {
// nothing
}
}
}
 
@Override
public void removeAll() {
this.invoices.clear();
this.remove(this.oldPanel);
this.invalidate();
this.revalidate();
}
}
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/TesseractUtils.java
New file
0,0 → 1,155
package org.openconcerto.modules.ocr;
 
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
 
import org.apache.poi.hpsf.MissingSectionException;
import org.openconcerto.utils.FileUtils;
 
public class TesseractUtils {
 
public static String DoOcr(File sourceFile) throws Exception {
 
System.err.println("TesseractUtils.DoOcr() " + sourceFile.getAbsolutePath());
 
String result = "";
final File imageHOCRDirectory = new File("ImageScan/HOCR");
imageHOCRDirectory.mkdirs();
final File execDirectory = new File(".");
final String destFileName = getDestFileName(sourceFile);
final File destFile = new File(imageHOCRDirectory.getAbsolutePath(), destFileName);
String destPath = destFile.getAbsolutePath();
if (destPath.length() >= 5) {
destPath = destPath.substring(0, destPath.length() - 5);
}
 
if (!destFile.exists()) {
 
List<String> commands = new ArrayList<String>();
commands.add("tesseract");
// Add arguments
commands.add("-v");
System.out.println(commands);
 
// Run macro on target
try {
runProcess(commands, execDirectory);
} catch (Exception e) {
throw new MissingSectionException("L'application tesseract n'est pas installée.");
}
 
commands = new ArrayList<String>();
commands.add("tesseract");
commands.add(sourceFile.getAbsolutePath());
commands.add(destPath);
commands.add("hocr");
 
System.out.println(commands);
 
// Run macro on target
runProcess(commands, execDirectory);
 
result = FileUtils.read(destFile);
 
} else {
final Scanner sc = new Scanner(destFile);
final StringBuilder fileContent = new StringBuilder();
while (sc.hasNextLine()) {
fileContent.append(sc.nextLine());
}
sc.close();
result = fileContent.toString();
}
return result;
}
 
public static String getDestFileName(File sourceFile) throws Exception {
String sourceFileChecksum = "";
String sourceFileName = "";
String fileHOCRName = "";
 
sourceFileName = sourceFile.getName();
sourceFileChecksum = getMD5Checksum(sourceFile);
fileHOCRName = sourceFileName.substring(0, sourceFileName.length() - 4) + "_" + sourceFileChecksum + ".html";
return fileHOCRName;
}
 
private static byte[] createChecksum(File file) throws IOException, NoSuchAlgorithmException {
final InputStream fis = new BufferedInputStream(new FileInputStream(file), 512 * 1024);
final byte[] buffer = new byte[1024];
final MessageDigest complete = MessageDigest.getInstance("MD5");
int numRead;
 
do {
numRead = fis.read(buffer);
if (numRead > 0) {
complete.update(buffer, 0, numRead);
}
} while (numRead != -1);
fis.close();
return complete.digest();
}
 
private static String getMD5Checksum(File file) throws IOException, NoSuchAlgorithmException {
final byte[] b = createChecksum(file);
final StringBuilder result = new StringBuilder();
for (int i = 0; i < b.length; i++) {
result.append(Integer.toString((b[i] & 0xff) + 0x100, 16).substring(1));
}
return result.toString();
}
 
public static String runProcess(List<String> commands, File runDirectory) throws Exception {
final ProcessBuilder pb = new ProcessBuilder(commands);
final StringBuilder output = new StringBuilder();
Process p = null;
 
pb.directory(runDirectory);
pb.redirectErrorStream(true);
p = pb.start();
// Read output
final BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null, previous = null;
while ((line = br.readLine()) != null) {
if (!line.equals(previous)) {
previous = line;
output.append(line).append('\n');
}
}
// Wait process
p.waitFor();
 
return output.toString();
}
 
public static String padLeft(String str, int size, char padChar) {
final StringBuffer padded = new StringBuffer(str);
while (padded.length() < size) {
padded.insert(0, padChar);
}
return padded.toString();
}
 
public static String padRight(String str, int size, char padChar) {
final StringBuffer padded = new StringBuffer(str);
while (padded.length() < size) {
padded.append(padChar);
}
return padded.toString();
}
 
public static void main(String[] args) throws Exception {
String str = TesseractUtils.DoOcr(new File("PNG/workingimage002.png"));
System.out.println(str);
}
}
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/InvoiceOCRPanel.java
New file
0,0 → 1,188
package org.openconcerto.modules.ocr;
 
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.List;
 
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.SwingUtilities;
 
import org.openconcerto.ui.ReloadPanel;
 
public class InvoiceOCRPanel extends JPanel {
 
private final InvoiceOCRTable table;
private final InvoiceViewer viewer = new InvoiceViewer();
private final JButton bCancel = new JButton("Annuler");
private final JButton bInterrupt = new JButton("Interrompre");
private final JButton bSave = new JButton("Valider");
private final JButton bImprove = new JButton("Améliorer les textes");
private final JButton bRealodFirst = new JButton("Recharger les fichiers");
private OCRThread ocrThread;
private SaveThread saveThread;
private String execDirectory;
private List<File> files;
 
private class InvoiceOCREvent implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource().equals(getBSave())) {
startSave();
} else if (e.getSource().equals(getBInterrupt())) {
interrupt();
} else if (e.getSource().equals(getBImprove())) {
startOCR(true);
} else if (e.getSource().equals(getBRelaodFirst())) {
interrupt();
}
}
 
private void interrupt() {
if (getOCRThread() != null && getOCRThread().isAlive()) {
getOCRThread().interrupt();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
getTable().comp.setMode(ReloadPanel.MODE_EMPTY);
}
});
}
}
}
 
private void startSave() {
this.saveThread = new SaveThread(this.table, this.bSave);
this.saveThread.setDaemon(true);
this.saveThread.setPriority(Thread.MIN_PRIORITY);
getBSave().setEnabled(false);
getBImprove().setEnabled(false);
getBRelaodFirst().setEnabled(false);
this.saveThread.start();
 
new Thread(new Runnable() {
public void run() {
try {
getOCRThread().join();
} catch (Exception ex) {
;
} finally {
getBSave().setEnabled(true);
getBImprove().setEnabled(true);
getBRelaodFirst().setEnabled(true);
}
}
}).start();
}
 
public void startOCR(boolean textCleanning) {
this.ocrThread.setTextCleanning(textCleanning);
this.ocrThread.setDaemon(true);
this.ocrThread.setPriority(Thread.MIN_PRIORITY);
getBSave().setEnabled(false);
getBImprove().setEnabled(false);
getBRelaodFirst().setEnabled(false);
getBInterrupt().setEnabled(true);
 
final Thread t = new Thread(this.ocrThread);
t.start();
 
new Thread(new Runnable() {
public void run() {
try {
t.join();
} catch (Exception ex) {
;
} finally {
getBSave().setEnabled(true);
getBImprove().setEnabled(true);
getBRelaodFirst().setEnabled(true);
getBInterrupt().setEnabled(false);
getOCRThread().interrupt();
}
}
}).start();
}
 
public InvoiceOCRPanel(String execDirectory, List<File> files) {
this.execDirectory = execDirectory;
this.files = files;
this.table = new InvoiceOCRTable(this.viewer);
this.ocrThread = new OCRThread(this.files, this.table, this.viewer, this, this.execDirectory);
 
final JSplitPane split = new JSplitPane();
split.setLeftComponent(this.viewer);
split.setRightComponent(this.table);
split.setDividerLocation(500);
 
this.bSave.addActionListener(new InvoiceOCREvent());
this.bInterrupt.addActionListener(new InvoiceOCREvent());
this.bImprove.addActionListener(new InvoiceOCREvent());
this.bRealodFirst.addActionListener(new InvoiceOCREvent());
 
GridBagLayout gb = new GridBagLayout();
GridBagConstraints gbc = new GridBagConstraints();
this.setLayout(gb);
 
gbc.fill = GridBagConstraints.BOTH;
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1;
gbc.weighty = 1;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.gridheight = 1;
this.add(split, gbc);
 
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.CENTER;
gbc.gridx = 0;
gbc.gridy = 1;
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.weightx = 0;
gbc.weighty = 0;
this.add(this.bInterrupt, gbc);
 
gbc.gridx = 1;
gbc.gridy = 1;
this.add(this.bSave, gbc);
 
gbc.gridx = 2;
this.add(this.bImprove, gbc);
 
gbc.gridx = 3;
this.add(this.bRealodFirst, gbc);
}
 
protected JButton getBSave() {
return this.bSave;
}
 
protected JButton getBInterrupt() {
return this.bInterrupt;
}
 
protected JButton getBCancel() {
return this.bCancel;
}
 
protected JButton getBImprove() {
return this.bImprove;
}
 
protected JButton getBRelaodFirst() {
return this.bRealodFirst;
}
 
protected OCRThread getOCRThread() {
return this.ocrThread;
}
 
protected InvoiceOCRTable getTable() {
return this.table;
}
}
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/OCRPage.java
New file
0,0 → 1,61
package org.openconcerto.modules.ocr;
 
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.List;
 
import javax.imageio.ImageIO;
 
public class OCRPage {
private File fileImage;
private BufferedImage image;
private final List<OCRLine> lines;
 
private int pageNumber;
 
public OCRPage(File fileImage, List<OCRLine> lines) throws IOException {
this.fileImage = fileImage;
this.lines = lines;
}
 
public BufferedImage getImage() throws IOException {
if(this.image == null){
this.image = ImageIO.read(this.fileImage);
}
return this.image;
}
 
public void clearImage() {
this.image = null;
}
public List<OCRLine> getLines() {
return this.lines;
}
 
public File getFileImage(){
return this.fileImage;
}
public int getPageNumber() {
return this.pageNumber;
}
public void setPageNumber(int pageNumber) {
this.pageNumber = pageNumber;
}
public void setFileImage(File fileImage){
this.fileImage = fileImage;
}
 
public boolean contains(String str) {
final String s = str.toLowerCase();
for (OCRLine line : this.lines) {
if (line.getText().toLowerCase().contains(s))
return true;
}
return false;
}
}
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/ModuleResources_fr.properties
New file
0,0 → 1,2
name=OCR
description=OCR
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/Module.java
New file
0,0 → 1,45
package org.openconcerto.modules.ocr;
 
import java.awt.event.ActionEvent;
import java.io.IOException;
 
import javax.swing.AbstractAction;
import javax.swing.JFrame;
 
import org.openconcerto.erp.modules.AbstractModule;
import org.openconcerto.erp.modules.MenuContext;
import org.openconcerto.erp.modules.ModuleFactory;
import org.openconcerto.modules.ocr.scan.ScannerPanel;
import org.openconcerto.ui.FrameUtil;
 
public class Module extends AbstractModule {
 
public Module(ModuleFactory f) throws IOException {
super(f);
}
 
@Override
protected void setupMenu(MenuContext menuContext) {
super.setupMenu(menuContext);
menuContext.addMenuItem(new AbstractAction("Factures fournisseur") {
@Override
public void actionPerformed(ActionEvent e) {
System.err.println("Module.setupMenu(...).new AbstractAction() {...}.actionPerformed()" + System.getProperty("java.version"));
final JFrame frame = new JFrame("Source d'acquisition");
frame.setContentPane(new ScannerPanel("."));
FrameUtil.showPacked(frame);
frame.setLocationRelativeTo(null);
}
}, "menu.ocr");
}
 
@Override
protected void start() {
}
 
@Override
protected void stop() {
 
}
 
}
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/SaveThread.java
New file
0,0 → 1,144
package org.openconcerto.modules.ocr;
 
import java.io.File;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
import javax.swing.JButton;
import javax.swing.SwingUtilities;
 
import org.openconcerto.erp.core.finance.accounting.element.ComptePCESQLElement;
import org.openconcerto.erp.generationEcritures.GenerationMvtSaisieAchat;
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.SQLBackgroundTableCache;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowListRSH;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.ui.ReloadPanel;
 
public class SaveThread extends Thread {
private InvoiceOCRTable invoiceTable;
private JButton bSave;
 
public SaveThread(InvoiceOCRTable table, JButton bSave) {
this.invoiceTable = table;
this.bSave = bSave;
}
 
@Override
public void run() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
getBSave().setEnabled(false);
getInvoiceTable().comp.setMode(ReloadPanel.MODE_ROTATE);
}
});
final Map<String, SQLRow> supplierMap = new HashMap<String, SQLRow>();
final SQLTable purchaseTable = Configuration.getInstance().getDirectory().getElement("SAISIE_ACHAT").getTable();
final SQLTable supplierTable = purchaseTable.getForeignTable("ID_FOURNISSEUR");
final SQLSelect sel = new SQLSelect();
sel.addSelectStar(supplierTable);
final List<SQLRow> result = SQLRowListRSH.execute(sel);
for (SQLRow sqlRow : result) {
supplierMap.put(sqlRow.getString("NOM"), sqlRow);
}
final int invoiceCount = this.invoiceTable.dm.getRowCount();
for (int i = 1; i <= invoiceCount; i++) {
final InvoiceOCR invoice = this.invoiceTable.getInvoice(i - 1);
if (invoice.getValid()) {
final SQLRow supplierRow = supplierMap.get(invoice.getSupplierName());
if (supplierRow != null) {
final int idCompteAchat = getIdCompteAchat(supplierRow, purchaseTable);
// Get payment mode
final SQLRow r = supplierRow.getForeignRow("ID_MODE_REGLEMENT");
final SQLElement eltModeReglement = Configuration.getInstance().getDirectory().getElement("MODE_REGLEMENT");
final SQLRowValues rowValsMdr = eltModeReglement.createCopy(r.getID());
final SQLRowValues rowVals = new SQLRowValues(purchaseTable);
rowVals.put("ID_FOURNISSEUR", supplierRow.getID());
rowVals.put("MONTANT_TTC", invoice.getAmountWithTax().multiply(new BigDecimal(100)).longValue());
rowVals.put("MONTANT_HT", invoice.getAmount().multiply(new BigDecimal(100)).longValue());
rowVals.put("MONTANT_TVA", invoice.getTax().multiply(new BigDecimal(100)).longValue());
rowVals.put("ID_TAXE", invoice.getTaxId());
rowVals.put("ID_MODE_REGLEMENT", rowValsMdr);
rowVals.put("ID_COMPTE_PCE", idCompteAchat);
rowVals.put("NUMERO_FACTURE", invoice.getInvoiceNumber());
rowVals.put("DATE", invoice.getDate());
try {
final SQLRow row = rowVals.insert();
final GenerationMvtSaisieAchat g = new GenerationMvtSaisieAchat(row);
g.genereMouvement();
for (int j = 0; j < invoice.getPageCount(); j++) {
final File pngFile = invoice.getPage(j).getFileImage();
final File pngFileDirectory = pngFile.getParentFile();
final File archivePngFileDirectory = new File(pngFileDirectory, "Save");
final File hocrFileDirectory = new File(pngFileDirectory.getParentFile(), "HOCR");
final File archiveHocrFileDirectory = new File(hocrFileDirectory, "Save");
final File hocrFile = new File(hocrFileDirectory, TesseractUtils.getDestFileName(pngFile));
 
if (pngFile.exists()) {
if (pngFile.renameTo(new File(archivePngFileDirectory, pngFile.getName()))) {
System.out.println("fichier png déplacé");
} else {
System.out.println("echec png déplacé");
}
}
if (hocrFile.exists()) {
if (hocrFile.renameTo(new File(archiveHocrFileDirectory, hocrFile.getName()))) {
System.out.println("fichier hocr déplacé");
} else {
System.out.println("echec hocr déplacé");
}
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
} else {
System.out.println("fournisseur inconnue:" + invoice.getSupplierName());
}
}
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
getInvoiceTable().comp.setMode(ReloadPanel.MODE_EMPTY);
getBSave().setEnabled(true);
}
});
}
 
private InvoiceOCRTable getInvoiceTable() {
return this.invoiceTable;
}
 
private JButton getBSave() {
return this.bSave;
}
 
private int getIdCompteAchat(SQLRow supplierRow, SQLTable purchaseTable) {
int idCompteAchat = -1;
if (supplierRow.isForeignEmpty("ID_COMPTE_PCE_CHARGE")) {
// Select Compte charge par defaut
final SQLTable tablePrefCompte = purchaseTable.getTable("PREFS_COMPTE");
final SQLRow rowPrefsCompte = SQLBackgroundTableCache.getInstance().getCacheForTable(tablePrefCompte).getRowFromId(2);
// compte Achat
idCompteAchat = rowPrefsCompte.getInt("ID_COMPTE_PCE_ACHAT");
if (idCompteAchat <= 1) {
try {
idCompteAchat = ComptePCESQLElement.getIdComptePceDefault("Achats");
} catch (Exception ex) {
ex.printStackTrace();
}
}
} else {
idCompteAchat = supplierRow.getForeignID("ID_COMPTE_PCE_CHARGE");
}
return idCompteAchat;
}
}
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/OCRLine.java
New file
0,0 → 1,36
package org.openconcerto.modules.ocr;
 
public class OCRLine {
private final String text;
private final int xMin, yMin, xMax, yMax;
 
public OCRLine(String text, int xMin, int yMin, int xMax, int yMax) {
super();
this.text = text;
this.xMin = xMin;
this.yMin = yMin;
this.xMax = xMax;
this.yMax = yMax;
}
 
public String getText() {
return this.text;
}
 
public int getxMin() {
return this.xMin;
}
 
public int getyMin() {
return this.yMin;
}
 
public int getxMax() {
return this.xMax;
}
 
public int getyMax() {
return this.yMax;
}
 
}
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/ModuleResources.properties
New file
0,0 → 1,2
name=OCR
description=OCR
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/InvoiceOCR.java
New file
0,0 → 1,228
package org.openconcerto.modules.ocr;
 
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
 
import org.openconcerto.erp.core.finance.tax.model.TaxeCache;
import org.openconcerto.sql.model.SQLRowAccessor;
 
public class InvoiceOCR {
private String supplierName;
private String invoiceNumber;
private Date date;
private BigDecimal amount;
private BigDecimal tax;
private BigDecimal amountWithTax;
private int taxId = -1;
private boolean valid = true;
private boolean active = true;
private boolean isNotInvoice = true;
private boolean containCalculatedValue = false;
private int totalPage = -1;
private Map<OCRPage, List<OCRLine>> map = new HashMap<OCRPage, List<OCRLine>>();
private final List<OCRPage> pages = new ArrayList<OCRPage>();
 
public String getSupplierName() {
return this.supplierName;
}
 
public String getInvoiceNumber() {
return this.invoiceNumber;
}
 
public Date getDate() {
return this.date;
}
 
public BigDecimal getAmount() {
return this.amount;
}
 
/**
* % tax (0.20, 0.55,...),
*/
public BigDecimal getTax() {
return this.tax;
}
 
public int getTaxId() {
return this.taxId;
}
 
public BigDecimal getAmountWithTax() {
return this.amountWithTax;
}
 
public boolean getValid() {
return this.valid;
}
 
public boolean getActive() {
return this.active;
}
public boolean getContainCalculatedValue(){
return this.containCalculatedValue;
}
 
public int getTotalPage() {
return this.totalPage;
}
 
public Map<OCRPage, List<OCRLine>> getMap() {
return this.map;
}
public boolean getIsNotInvoice(){
return this.isNotInvoice;
}
public void setIsNotInvoice(boolean isNotInvoice){
this.isNotInvoice = isNotInvoice;
}
 
public void setMap(Map<OCRPage, List<OCRLine>> map) {
this.map = map;
}
 
public void setSupplierName(String supplierName) {
this.supplierName = supplierName;
}
 
public void setInvoiceNumber(String invoiceNumber) {
this.invoiceNumber = invoiceNumber;
}
 
public void setDate(Date date) {
this.date = date;
}
 
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
 
public void setTax(BigDecimal tax) {
this.tax = tax;
}
 
public void setAmountWithTax(BigDecimal amountWithTax) {
this.amountWithTax = amountWithTax;
}
 
public void setTaxId() {
final BigDecimal realTaxPercent = getTaxPercent();
Integer id = null;
if (realTaxPercent != null) {
Set<SQLRowAccessor> taxes = TaxeCache.getCache().getAllTaxe();
for (SQLRowAccessor taxe : taxes) {
final BigDecimal taxPercent = (new BigDecimal(((Float) taxe.getFloat("TAUX")).toString())).divide(new BigDecimal(100));
final BigDecimal tempTax = this.amount.multiply(taxPercent).setScale(2, BigDecimal.ROUND_HALF_UP);
if (this.tax.compareTo(tempTax) == 0) {
id = taxe.getInt("ID_TAXE");
break;
}
}
if (id != null) {
this.taxId = id.intValue();
} else {
this.taxId = -1;
}
} else {
this.taxId = -1;
}
}
 
public void setValid(boolean valid) {
this.valid = valid;
}
 
public void setActive(boolean active) {
this.active = active;
}
public void setContainCalculatedValue(boolean containCalculatedValue){
this.containCalculatedValue = containCalculatedValue;
}
 
public void setTotalPage(int total) {
this.totalPage = total;
}
 
public void addHighlight(OCRPage page, OCRLine line) {
List<OCRLine> l = this.map.get(page);
if (l == null) {
l = new ArrayList<OCRLine>();
this.map.put(page, l);
}
l.add(line);
}
 
public void setHighlight(Map<OCRPage, List<OCRLine>> m) {
this.map = m;
}
 
public List<OCRLine> getHighlight(OCRPage page) {
List<OCRLine> l = this.map.get(page);
if (l == null) {
return Collections.emptyList();
}
return l;
}
 
public void addPage(OCRPage page) {
this.pages.add(page);
}
 
public void addPages(List<OCRPage> list) {
this.pages.addAll(list);
}
 
public OCRPage getPage(int i) {
return this.pages.get(i);
}
 
public int getPageCount() {
return this.pages.size();
}
 
public boolean checkNullValue() {
if (this.amount == null || this.tax == null || this.amountWithTax == null || this.invoiceNumber == null || this.date == null) {
return false;
}
return true;
}
 
public boolean checkAmounts() {
if (!(this.amount.add(this.tax).compareTo(this.amountWithTax) == 0)) {
return false;
} else if (this.amount.compareTo(new BigDecimal(0)) == -1 || this.amountWithTax.compareTo(new BigDecimal(0)) == -1 || this.tax.compareTo(new BigDecimal(0)) == -1) {
return false;
}
return true;
}
public void setNullAmounts() {
this.amount = null;
this.tax = null;
this.amountWithTax = null;
}
 
private BigDecimal getTaxPercent() {
if (this.tax != null && this.amount != null) {
return this.tax.divide(this.amount, 10, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP);
}
return null;
}
 
@Override
public String toString() {
return super.toString() + " : " + this.invoiceNumber + " Fournisseur:" + this.supplierName + " " + this.date + " Total: " + this.amount + " € HT TVA:" + this.tax + " TTC: "
+ this.amountWithTax;
}
}
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/translation_fr.xml
New file
0,0 → 1,4
<translation lang="fr">
<menu id="menu.ocr" label="Numérisation" />
<menu id="menu.ocr.supplier.invoice" label="Facture fournisseurs" />
</translation>
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/scan/ScannerUtil.java
New file
0,0 → 1,176
package org.openconcerto.modules.ocr.scan;
 
import java.io.File;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
 
import javax.swing.JOptionPane;
 
import org.openconcerto.modules.ocr.TesseractUtils;
 
public class ScannerUtil {
public static int MODE_ICOPY = 0;
public static int MODE_NAPS2 = 1;
public static int MODE_SANE = 2;
 
static public boolean scanTo(File directory, int mode) throws Exception {
 
Boolean result = true;
 
List<String> commands = new ArrayList<String>();
List<File> filesScan = null;
 
Process p = null;
 
File imageTIFFPrinterDirectory = new File(directory, "Printer");
 
if (!createDirectory(imageTIFFPrinterDirectory)) {
 
if (mode == MODE_ICOPY) {
p = Runtime.getRuntime().exec("icopy/iCopy.exe /file /silent /path \"" + imageTIFFPrinterDirectory.getCanonicalPath() + "\"");
p.waitFor();
result = (p.exitValue() == 0);
} else if (mode == MODE_NAPS2) {
p = Runtime.getRuntime().exec("naps2/NAPS2.Console.exe -o \"" + imageTIFFPrinterDirectory.getCanonicalPath() + "\" -p wia");
p.waitFor();
result = (p.exitValue() == 0);
} else if (mode == MODE_SANE) {
String output = "";
Integer nbFileScan = 0;
 
commands.add("scanimage");
commands.add("--batch");
commands.add("--format=tiff");
commands.add("--resolution");
commands.add("300");
commands.add("--mode");
commands.add("Gray");
commands.add("--source");
commands.add("adf");
 
output = TesseractUtils.runProcess(commands, imageTIFFPrinterDirectory);
 
String[] split = output.split("\\s+");
for (int i = 0; i < split.length; i++) {
if (split[i].equals("Scanned")) {
nbFileScan++;
}
}
 
if (nbFileScan.equals(0)) {
JOptionPane.showConfirmDialog(null, "Aucun document n'a été scanné", "Alert", JOptionPane.ERROR_MESSAGE);
} else {
JOptionPane.showConfirmDialog(null, nbFileScan.toString() + " documents ont été scannés", "Information", JOptionPane.DEFAULT_OPTION);
}
 
filesScan = scanDirectory(imageTIFFPrinterDirectory);
 
for (int i = 0; i < filesScan.size(); i++) {
File fileScan = filesScan.get(i);
 
Calendar c = Calendar.getInstance();
Integer year = c.get(Calendar.YEAR);
Integer month = c.get(Calendar.MONTH) + 1;
Integer day = c.get(Calendar.DATE);
Integer hour = c.get(Calendar.HOUR);
Integer minute = c.get(Calendar.MINUTE);
Integer second = c.get(Calendar.SECOND);
Integer millis = c.get(Calendar.MILLISECOND);
Integer counter = 0;
 
String finalName = TesseractUtils.padLeft(year.toString(), 4, '0') + TesseractUtils.padLeft(month.toString(), 2, '0') + TesseractUtils.padLeft(day.toString(), 2, '0')
+ TesseractUtils.padLeft(hour.toString(), 2, '0') + TesseractUtils.padLeft(minute.toString(), 2, '0') + TesseractUtils.padLeft(second.toString(), 2, '0')
+ TesseractUtils.padLeft(millis.toString(), 3, '0') + "_" + TesseractUtils.padLeft(counter.toString(), 3, '0') + ".tiff";
 
File outputFile = new File(directory, finalName);
 
while (outputFile.exists()) {
counter++;
finalName = finalName.substring(0, finalName.length() - 8) + TesseractUtils.padLeft(counter.toString(), 3, '0') + ".tiff";
 
outputFile = new File(directory, finalName);
}
 
if (fileScan.renameTo(new File(directory, finalName))) {
System.out.println("File is moved successful!");
} else {
result = Boolean.FALSE;
throw new Exception("Echec du déplacement du fichier:" + fileScan.getName());
}
}
} else {
result = Boolean.FALSE;
throw new IllegalArgumentException("Mode " + mode + " not supported");
}
}
 
return result;
}
 
static public boolean scanTo(File imageFile) throws Exception {
try {
String os_name = System.getProperty("os.name");
if (os_name.equals("Linux")) {
return scanTo(imageFile, MODE_SANE);
} else {
return scanTo(imageFile, MODE_ICOPY);
}
} catch (Exception ex) {
throw ex;
}
 
}
 
static public List<File> scanDirectory(File directory) {
List<File> res = new ArrayList<File>();
 
File[] children = directory.listFiles();
if (children != null) {
for (File child : children) {
if (!child.isDirectory()) {
res.add(child);
}
}
}
 
return res;
}
 
public static Boolean createDirectory(File directory) {
Boolean error = Boolean.FALSE;
 
if (!directory.exists()) {
System.out.println("Creating directory: " + directory.getName());
 
try {
directory.mkdir();
} catch (SecurityException se) {
error = Boolean.TRUE;
}
if (!error) {
System.out.println("Directory created");
}
}
return error;
}
 
public static void convertFile(File sourceFile, File destFile) throws Exception {
List<String> commands = new ArrayList<String>();
 
commands.add("convert");
commands.add(sourceFile.getAbsolutePath());
commands.add(destFile.getAbsolutePath());
 
try {
TesseractUtils.runProcess(commands, new File("."));
System.out.println("Image convert successfully! " + sourceFile.getAbsolutePath() + " " + destFile.getAbsolutePath());
} catch (Exception ex) {
throw new Exception("L'application ImageMagik n'est pas installé.");
}
 
if (!destFile.exists()) {
throw new Exception("Erreur lors de la conversion en PNG, le fichier de destination n'a pas été créé (" + destFile.getAbsolutePath() + ").\nL'application ImageMagik n'est pas installée?");
}
}
}
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/scan/ScannerPanel.java
New file
0,0 → 1,267
package org.openconcerto.modules.ocr.scan;
 
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.List;
 
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.SwingWorker;
 
import org.openconcerto.modules.ocr.InvoiceOCRPanel;
import org.openconcerto.ui.DefaultGridBagConstraints;
import org.openconcerto.ui.JLabelBold;
import org.openconcerto.ui.ReloadPanel;
import org.openconcerto.utils.ExceptionHandler;
 
public class ScannerPanel extends JPanel implements ActionListener {
private final JRadioButton rbDirectory = new JRadioButton("Dossier images");
private final JRadioButton rbPDF = new JRadioButton("Dossier PDFs");
private final JRadioButton rbScan = new JRadioButton("Scanner");
 
private final ButtonGroup bgRadio = new ButtonGroup();
 
private final JButton bValid = new JButton("Valider");
private final ReloadPanel rpLoader = new ReloadPanel();
private final JLabel pathLabel = new JLabel();
private String imagePath = "";
 
public ScannerPanel(String path) {
 
this.imagePath = path;
 
final GridBagConstraints c = new DefaultGridBagConstraints();
this.setLayout(new GridBagLayout());
 
c.gridwidth = 2;
c.gridheight = 1;
c.weightx = 0;
c.weighty = 0;
c.gridx = 0;
c.gridy = 0;
 
JLabelBold l = new JLabelBold("Dossier de travail");
 
this.add(l, c);
c.gridy++;
 
updateLabelPath();
pathLabel.setMinimumSize(new Dimension(400, 16));
this.add(pathLabel, c);
c.gridy++;
c.gridwidth = 1;
c.fill = GridBagConstraints.NONE;
final JButton bFile = new JButton("Sélectionner un dossier");
bFile.addActionListener(new ActionListener() {
 
@Override
public void actionPerformed(ActionEvent e) {
final JFileChooser chooser = new JFileChooser();
chooser.setCurrentDirectory(new File(imagePath));
chooser.setDialogTitle("Dossier");
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
 
chooser.setAcceptAllFileFilterUsed(false);
//
if (chooser.showOpenDialog(ScannerPanel.this) == JFileChooser.APPROVE_OPTION) {
final File selectedFile = chooser.getSelectedFile();
if (selectedFile != null) {
imagePath = selectedFile.getAbsolutePath();
updateLabelPath();
}
}
 
}
});
this.add(bFile, c);
c.gridwidth = 2;
c.gridy++;
this.add(new JLabelBold("Mode de traitement"), c);
 
c.weightx = 1;
c.weighty++;
c.gridx = 0;
c.gridy++;
c.anchor = GridBagConstraints.NORTHWEST;
this.add(createCenterPanel(), c);
c.gridwidth = 1;
c.weightx = 0;
c.weighty = 0;
c.gridx = 1;
c.gridy++;
c.anchor = GridBagConstraints.LINE_END;
 
this.add(this.rpLoader, c);
c.gridx++;
this.add(this.bValid, c);
 
this.bValid.addActionListener(this);
 
}
 
private void updateLabelPath() {
this.pathLabel.setText(new File(getImagePath()).getAbsolutePath());
 
}
 
public JPanel createCenterPanel() {
final JPanel centerPanel = new JPanel();
centerPanel.setLayout(new GridBagLayout());
this.bgRadio.add(this.rbDirectory);
this.bgRadio.add(this.rbPDF);
this.bgRadio.add(this.rbScan);
 
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
c.gridwidth = 1;
c.gridheight = 1;
c.weightx = 1;
c.weighty = 1;
c.gridx = 1;
centerPanel.add(this.rbDirectory, c);
centerPanel.add(this.rbPDF, c);
centerPanel.add(this.rbScan, c);
this.rbDirectory.setSelected(true);
return centerPanel;
}
 
@Override
public void actionPerformed(ActionEvent e) {
 
if (e.getSource().equals(this.bValid)) {
this.rpLoader.setMode(ReloadPanel.MODE_ROTATE);
this.bValid.setEnabled(false);
SwingWorker<List<File>, Object> worker = new SwingWorker<List<File>, Object>() {
 
@Override
protected List<File> doInBackground() throws Exception {
final File imageScanDirectory = new File(getImagePath());
final File imageTIFFDirectory = new File(imageScanDirectory, "TIFF");
final File imagePDFDirectory = new File(imageScanDirectory, "PDF");
final File imagePNGDirectory = new File(imageScanDirectory, "PNG");
final File imageHOCRDirectory = new File(imageScanDirectory, "HOCR");
List<File> filesTIFF, filesPNG = null, filesPDF;
// Creation of necessary directories
if (((!imageTIFFDirectory.exists() && imageTIFFDirectory.mkdirs()) || imageTIFFDirectory.exists())
&& ((!imagePDFDirectory.exists() && imagePDFDirectory.mkdirs()) || imagePDFDirectory.exists())
&& ((!imagePNGDirectory.exists() && imagePNGDirectory.mkdirs()) || imagePNGDirectory.exists())
&& ((!imageHOCRDirectory.exists() && imageHOCRDirectory.mkdirs()) || imageHOCRDirectory.exists())) {
// Get file for OCR
if (directoryIsCheck()) {
filesPNG = ScannerUtil.scanDirectory(imagePNGDirectory);
} else if (pdfIsCheck()) {
filesPDF = ScannerUtil.scanDirectory(imagePDFDirectory);
try {
for (File filePDF : filesPDF) {
final String filePDFName = filePDF.getName();
File filePNG;
String filePNGName = "";
 
if (filePDFName.length() > 3 && filePDFName.substring(filePDFName.length() - 3, filePDFName.length()).toLowerCase().equals("pdf")) {
filePNGName = filePDFName.substring(0, filePDFName.length() - 3) + "png";
filePNG = new File(imagePNGDirectory, filePNGName);
ScannerUtil.convertFile(filePDF, filePNG);
}
}
} catch (Exception ex) {
throw ex;
}
} else if (scanIsCheck()) {
try {
if (ScannerUtil.scanTo(imageTIFFDirectory)) {
filesTIFF = ScannerUtil.scanDirectory(imageTIFFDirectory);
for (File fileTIFF : filesTIFF) {
File filePNG;
final String fileTIFFName = fileTIFF.getName();
String filePNGName = "";
 
if (fileTIFFName.toLowerCase().endsWith(".tiff")) {
filePNGName = fileTIFFName.substring(0, fileTIFFName.length() - 4) + "png";
filePNG = new File(imagePNGDirectory, filePNGName);
ScannerUtil.convertFile(fileTIFF, filePNG);
}
}
filesPNG = ScannerUtil.scanDirectory(imagePNGDirectory);
for (File file : imageTIFFDirectory.listFiles()) {
if (!file.isDirectory()) {
file.delete();
}
}
}
} catch (Exception ex) {
throw ex;
}
}
 
}
return filesPNG;
}
 
@Override
protected void done() {
try {
List<File> result = get();
 
if (result != null) {
int choice = JOptionPane.showConfirmDialog(null, "Les fichiers sont prêts à être lus, voulez-vous continuer?", "Confirmation", JOptionPane.YES_NO_OPTION);
if (choice == JOptionPane.YES_OPTION) {
final JFrame frame = new JFrame("Lecture OCR");
final InvoiceOCRPanel panel = new InvoiceOCRPanel(getImagePath(), result);
frame.setSize(1024, 768);
frame.setContentPane(panel);
frame.setVisible(true);
panel.startOCR(false);
}
getRpLoader().setMode(ReloadPanel.MODE_EMPTY);
getBValid().setEnabled(true);
} else {
JOptionPane.showMessageDialog(ScannerPanel.this, "Aucun fichier trouvé.\nLe dossier de travail doit contenir des fichiers dans les sous-dossiers PNG, TIFF, PDF ou HOCR.");
getRpLoader().setMode(ReloadPanel.MODE_EMPTY);
getBValid().setEnabled(true);
}
 
} catch (Exception ex) {
ExceptionHandler.handle("Erreur OCR", ex);
getRpLoader().setMode(ReloadPanel.MODE_BLINK);
getBValid().setEnabled(true);
}
}
};
worker.execute();
}
}
 
protected synchronized JButton getBValid() {
return this.bValid;
}
 
protected synchronized ReloadPanel getRpLoader() {
return this.rpLoader;
}
 
protected synchronized String getImagePath() {
return this.imagePath;
}
 
protected synchronized boolean scanIsCheck() {
return this.rbScan.isSelected();
}
 
protected synchronized boolean pdfIsCheck() {
return this.rbPDF.isSelected();
}
 
protected synchronized boolean directoryIsCheck() {
return this.rbDirectory.isSelected();
}
}
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/InvoiceRendererComponent.java
New file
0,0 → 1,133
package org.openconcerto.modules.ocr;
 
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.List;
 
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
 
public class InvoiceRendererComponent extends JComponent {
private BufferedImage image;
private int mX = -1000;
private int mY = -1000;
 
public InvoiceRendererComponent(InvoiceOCR invoice, final OCRPage page) throws IOException {
this.image = new BufferedImage(page.getImage().getWidth(), page
.getImage().getHeight(), BufferedImage.TYPE_INT_ARGB);
 
page.getImage();
 
this.addMouseListener(new MouseAdapter() {
public void mouseExited(MouseEvent e) {
setPos(-1000, -1000);
};
 
@Override
public void mouseClicked(MouseEvent e) {
JFrame f = new JFrame();
StringBuilder b = new StringBuilder();
List<OCRLine> lines = page.getLines();
int i = 0;
for (OCRLine ocrLine : lines) {
b.append(i);
b.append(":");
b.append(ocrLine.getText());
b.append("\n");
i++;
}
JTextArea area = new JTextArea(b.toString());
f.setContentPane(new JScrollPane(area));
f.setSize(new Dimension(500, 500));
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setVisible(true);
 
}
});
this.addMouseMotionListener(new MouseAdapter() {
 
@Override
public void mouseMoved(MouseEvent e) {
setPos(e.getX(), e.getY());
}
 
});
Graphics g = this.image.getGraphics();
g.drawImage(page.getImage(), 0, 0, null);
g.setColor(new Color(255, 255, 0, 60));
List<OCRLine> lines = invoice.getHighlight(page);
for (OCRLine ocrLine : lines) {
 
final int x = ocrLine.getxMin();
int w = ocrLine.getxMax() - x;
final int y = ocrLine.getyMin();
int h = ocrLine.getyMax() - y;
 
g.fillRect(x, y, w, h);
}
g.dispose();
}
 
protected void setPos(int x, int y) {
this.mX = x;
this.mY = y;
repaint();
}
 
@Override
public void paint(Graphics g) {
 
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
double ratio = (double) this.image.getWidth()
/ (double) this.image.getHeight();
 
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
 
g.drawImage(this.image, 0, 0, this.getWidth(),
(int) Math.round(this.getWidth() / ratio), 0, 0,
this.image.getWidth(), this.image.getHeight(), null);
double zoomRatio = (double) this.getWidth()
/ (double) this.image.getWidth();
 
int B_W = 150;
int B_H = 90;
int B_W2 = B_W / 2;
int B_H2 = B_H / 2;
 
// Destination
final int dx1 = this.mX - B_W2;
final int dy1 = this.mY - B_H2;
final int dx2 = dx1 + B_W;
final int dy2 = dy1 + B_H;
 
// Source
final int sx1 = (int) ((dx1 + B_W2 / 2) / zoomRatio);
final int sy1 = (int) ((dy1 + B_H2 / 2) / zoomRatio);
final int sx2 = (int) ((dx2 - B_W2 / 2) / zoomRatio);
final int sy2 = (int) ((dy2 - B_H2 / 2) / zoomRatio);
 
g.drawImage(this.image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null);
g.setColor(new Color(232, 242, 255));
 
g.drawRect(dx1, dy1, dx2 - dx1, dy2 - dy1);
g.drawRect(dx1 - 1, dy1 - 1, dx2 - dx1 + 2, dy2 - dy1 + 2);
}
 
@Override
public Dimension getPreferredSize() {
Dimension d = getParent().getSize();
return new Dimension(d.width, (int) Math.round(((d.width * this.image
.getHeight()) / (double) this.image.getWidth())));
}
}
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/HOCRParser.java
New file
0,0 → 1,29
package org.openconcerto.modules.ocr;
 
import java.util.ArrayList;
import java.util.List;
 
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
 
public class HOCRParser {
public HOCRParser() {
}
 
public List<OCRLine> parse(String htmlOcr) {
Document doc = Jsoup.parse(htmlOcr);
List<OCRLine> result = new ArrayList<OCRLine>();
Elements lines = doc.select(".ocr_line");
int size = lines.size();
for (int i = 0; i < size; i++) {
Element e = lines.get(i);
String title = e.attr("title");
String[] r = title.split(" ");
result.add(new OCRLine(e.text().trim(), Integer.parseInt(r[1].replace(";","")), Integer.parseInt(r[2].replace(";","")), Integer.parseInt(r[3].replace(";","")), Integer.parseInt(r[4].replace(";",""))));
}
return result;
}
}
/trunk/Modules/Module OCR/src/org/openconcerto/modules/ocr/InvoiceOCRTable.java
New file
0,0 → 1,230
package org.openconcerto.modules.ocr;
 
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
 
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
 
import org.openconcerto.erp.core.common.ui.DeviseTableCellRenderer;
import org.openconcerto.ui.DefaultGridBagConstraints;
import org.openconcerto.ui.JLabelBold;
import org.openconcerto.ui.ReloadPanel;
import org.openconcerto.ui.table.TimestampTableCellEditor;
 
public class InvoiceOCRTable extends JPanel {
List<InvoiceOCR> invoices = new ArrayList<InvoiceOCR>();
final DefaultTableModel dm;
final ReloadPanel comp = new ReloadPanel();
 
public InvoiceOCRTable(final InvoiceViewer viewer) {
this.dm = new DefaultTableModel() {
@Override
public int getRowCount() {
return getInvoicesSize();
}
 
@Override
public int getColumnCount() {
return 7;
}
 
@Override
public String getColumnName(int column) {
switch (column) {
case 0:
return "Date";
case 1:
return "Fournisseur";
case 2:
return "Numéro de facture";
case 3:
return "Montant HT";
case 4:
return "Montant TVA";
case 5:
return "Montant TTC";
case 6:
return "Valider";
}
return "" + column;
}
 
@Override
public Object getValueAt(int row, int column) {
if (!SwingUtilities.isEventDispatchThread()) {
throw new IllegalStateException();
}
final InvoiceOCR i = getInvoice(row);
if (column == 0) {
return i.getDate();
} else if (column == 1) {
return i.getSupplierName();
} else if (column == 2) {
return i.getInvoiceNumber();
} else if (column == 3) {
return i.getAmount();
} else if (column == 4) {
return i.getTax();
} else if (column == 5) {
return i.getAmountWithTax();
} else if (column == 6) {
return i.getValid();
}
return "?";
}
 
@Override
public void setValueAt(Object aValue, int row, int column) {
if (!SwingUtilities.isEventDispatchThread()) {
throw new IllegalStateException();
}
final InvoiceOCR i = getInvoice(row);
if (column == 0) {
i.setDate((Date) aValue);
} else if (column == 1) {
i.setSupplierName((String) aValue);
} else if (column == 2) {
i.setInvoiceNumber((String) aValue);
} else if (column == 3) {
i.setAmount((BigDecimal) aValue);
} else if (column == 4) {
i.setTax((BigDecimal) aValue);
} else if (column == 5) {
i.setAmountWithTax((BigDecimal) aValue);
} else if (column == 6) {
boolean valid = ((Boolean) aValue).booleanValue();
if (valid) {
i.setTaxId();
if (!i.checkNullValue()) {
JOptionPane.showMessageDialog(null, "Tous les champs ne sont pas renseignés", "alert", JOptionPane.ERROR_MESSAGE);
} else if (!i.checkAmounts()) {
JOptionPane.showMessageDialog(null, "Les montants saisis ne sont pas corrects", "alert", JOptionPane.ERROR_MESSAGE);
} else if (i.getTaxId() == -1) {
JOptionPane.showMessageDialog(null, "Le taux de TVA n'est pas pris en charge", "alert", JOptionPane.ERROR_MESSAGE);
} else {
i.setValid(true);
}
} else {
i.setValid(false);
}
}
}
 
@Override
public Class<?> getColumnClass(int columnIndex) {
if (columnIndex == 0) {
return Date.class;
} else if (columnIndex == 1) {
return String.class;
} else if (columnIndex == 2) {
return String.class;
} else if (columnIndex == 3) {
return BigDecimal.class;
} else if (columnIndex == 4) {
return BigDecimal.class;
} else if (columnIndex == 5) {
return BigDecimal.class;
} else if (columnIndex == 6) {
return Boolean.class;
}
 
return super.getColumnClass(columnIndex);
}
 
};
 
final JTable t = new JTable(this.dm);
t.setAutoCreateRowSorter(true);
 
t.getColumnModel().getColumn(0).setCellRenderer(new DefaultTableCellRenderer() {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (value != null) {
final DateFormat f = SimpleDateFormat.getDateInstance(SimpleDateFormat.LONG);
value = f.format(value);
}
return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
}
});
t.getColumnModel().getColumn(0).setCellEditor(new TimestampTableCellEditor(false));
t.getColumnModel().getColumn(3).setCellRenderer(new DeviseTableCellRenderer());
this.setLayout(new GridBagLayout());
GridBagConstraints c = new DefaultGridBagConstraints();
c.weightx = 1;
this.add(new JLabelBold("Factures analysées"), c);
c.gridy++;
c.weighty = 1;
c.fill = GridBagConstraints.BOTH;
this.add(new JScrollPane(t), c);
c.gridy++;
c.weighty = 0;
this.comp.setMode(ReloadPanel.MODE_ROTATE);
this.add(this.comp, c);
 
t.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
int viewRow = t.getSelectedRow();
if (viewRow >= 0) {
int modelRow = t.convertRowIndexToModel(viewRow);
if (modelRow >= 0) {
try {
viewer.select(getInvoice(modelRow), 0);
} catch (IOException e1) {
// nothing
}
}
}
}
}
});
}
 
protected int getInvoicesSize() {
return this.invoices.size();
}
 
public InvoiceOCR getInvoice(int index) {
return this.invoices.get(index);
}
 
public void add(InvoiceOCR invoice) {
if (!SwingUtilities.isEventDispatchThread()) {
throw new IllegalStateException();
}
this.invoices.add(invoice);
this.dm.fireTableDataChanged();
}
 
@Override
public void removeAll() {
if (!SwingUtilities.isEventDispatchThread()) {
throw new IllegalStateException();
}
this.invoices.clear();
this.dm.fireTableDataChanged();
}
 
public void setModelCompleted() {
this.comp.setMode(ReloadPanel.MODE_EMPTY);
}
}