mirror of
https://gitlab.os-k.eu/neox/CNIRevelator.git
synced 2023-08-25 14:03:10 +02:00
Main files of the project
This commit is contained in:
parent
ccf4adc566
commit
6a5a01c117
50
CNI_GLOBALVAR.py
Normal file
50
CNI_GLOBALVAR.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
"""
|
||||||
|
********************************************************************************
|
||||||
|
*** Projet CNI_Revelator ***
|
||||||
|
|
||||||
|
GNU GPL * 07/2018
|
||||||
|
|
||||||
|
Adrien Bourmault
|
||||||
|
|
||||||
|
VARIABLES
|
||||||
|
|
||||||
|
********************************************************************************
|
||||||
|
"""
|
||||||
|
CST_REV = "8"
|
||||||
|
CST_VERTITLE = "2.2"
|
||||||
|
CST_TAB_VER = ["2","2","1"]
|
||||||
|
CST_VER = "{0}.{1}.{2}".format(CST_TAB_VER[0], CST_TAB_VER[1], CST_TAB_VER[2])
|
||||||
|
CST_TYPE = "Final Release"
|
||||||
|
CST_NAME = "CNIRevelator"
|
||||||
|
CST_TITLE = CST_NAME + " " + CST_VER + " - GNU/GPL Licensing 2018"
|
||||||
|
CST_MARK = CST_NAME + " " + CST_TYPE + " " + CST_VER + " - by NeoX, GNU/GPL Licensing 2018"
|
||||||
|
CST_SUM_VER = int(CST_TAB_VER[0])*100 + int(CST_TAB_VER[1])*10 + int(CST_TAB_VER[2])
|
||||||
|
CST_LINK = <lien vers serveur>
|
||||||
|
CST_COLOR = "#003380"
|
||||||
|
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import hashlib
|
||||||
|
from Crypto import Random
|
||||||
|
from Crypto.Cipher import AES
|
||||||
|
from tkinter import *
|
||||||
|
from tkinter.messagebox import *
|
||||||
|
from tkinter import filedialog
|
||||||
|
from tkinter import ttk as ttk
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import threading
|
||||||
|
import sys
|
||||||
|
import urllib.request as urllib2
|
||||||
|
import urllib.error as URLExcept
|
||||||
|
import random
|
||||||
|
from pypac import PACSession
|
||||||
|
from requests.auth import HTTPProxyAuth
|
||||||
|
import subprocess
|
||||||
|
from datetime import datetime
|
||||||
|
from PIL import Image, ImageFont, ImageDraw, ImageTk, ImageEnhance, ImageFilter
|
||||||
|
import math
|
||||||
|
import warnings
|
||||||
|
import string
|
||||||
|
|
||||||
|
CST_FOLDER = os.getenv('APPDATA') + "/CNIRevelator/"
|
131
CNI_Revelator.py
Normal file
131
CNI_Revelator.py
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
"""
|
||||||
|
********************************************************************************
|
||||||
|
*** Projet CNI_Revelator ***
|
||||||
|
|
||||||
|
GNU GPL * 07/2018
|
||||||
|
|
||||||
|
Adrien Bourmault
|
||||||
|
|
||||||
|
main
|
||||||
|
|
||||||
|
********************************************************************************
|
||||||
|
"""
|
||||||
|
|
||||||
|
###IMPORTS GLOBAUX
|
||||||
|
from CNI_GLOBALVAR import *
|
||||||
|
|
||||||
|
|
||||||
|
##LOGGING
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.remove("error.log")
|
||||||
|
os.remove("conf.ig")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
import logging
|
||||||
|
|
||||||
|
CST_NIVEAU_LOG = logging.ERROR
|
||||||
|
|
||||||
|
from logging.handlers import RotatingFileHandler
|
||||||
|
|
||||||
|
# création de l'objet logger qui va nous servir à écrire dans les logs
|
||||||
|
logger = logging.getLogger()
|
||||||
|
# on met le niveau du logger à DEBUG, comme ça il écrit tout
|
||||||
|
logger.setLevel(CST_NIVEAU_LOG)
|
||||||
|
|
||||||
|
# création d'un formateur qui va ajouter le temps, le niveau
|
||||||
|
# de chaque message quand on écrira un message dans le log
|
||||||
|
formatter = logging.Formatter('%(asctime)s :: %(levelname)s :: %(message)s')
|
||||||
|
# création d'un handler qui va rediriger une écriture du log vers
|
||||||
|
# un fichier en mode 'append', avec 1 backup et une taille max de 1Mo
|
||||||
|
file_handler = RotatingFileHandler('error.log', 'a', 1000000, 1)
|
||||||
|
# on lui met le niveau sur DEBUG, on lui dit qu'il doit utiliser le formateur
|
||||||
|
# créé précédement et on ajoute ce handler au logger
|
||||||
|
file_handler.setLevel(CST_NIVEAU_LOG)
|
||||||
|
file_handler.setFormatter(formatter)
|
||||||
|
logger.addHandler(file_handler)
|
||||||
|
|
||||||
|
##History
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### IMPORTS LOCAUX
|
||||||
|
from CNI_classes import *
|
||||||
|
from CNI_Update import *
|
||||||
|
|
||||||
|
### FONCTION PRINCIPALE
|
||||||
|
|
||||||
|
|
||||||
|
def main(logger):
|
||||||
|
|
||||||
|
logger.info("main() : " +"**** Creating App_main() ****")
|
||||||
|
main_w = App_main(logger)
|
||||||
|
|
||||||
|
main_w.montext("* " + CST_NAME + " " + CST_VER + " " + CST_TYPE + " Revision " + CST_REV + " *\n")
|
||||||
|
|
||||||
|
import CNI_pytesseract as pytesseract
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
os.environ["PATH"] = CST_FOLDER + "Tesseract-OCR4\\"
|
||||||
|
os.environ["TESSDATA_PREFIX"] = CST_FOLDER + "Tesseract-OCR4\\tessdata"
|
||||||
|
tesser_version = pytesseract.get_tesseract_version()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("main() : " +"**** ERROR WITH TESSERACT MODULE " + str(e) + " ****")
|
||||||
|
else:
|
||||||
|
text = "Tesseract version " + str(tesser_version) +" Licensed Apache 2004 successfully initiated\n"
|
||||||
|
main_w.montext(text)
|
||||||
|
|
||||||
|
|
||||||
|
main_w.montext("\n\nEntrez la première ligne de MRZ svp \n")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
logger.info("main() : " +"**** Launching App_main() ****")
|
||||||
|
main_w.mainloop()
|
||||||
|
logger.info("main() : " +"**** Ending App_main() ****")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##Launcher
|
||||||
|
|
||||||
|
logger.info("launcher : " + CST_NAME +" "+ CST_VER)
|
||||||
|
logger.info("launcher : " +"*****Hello World*****")
|
||||||
|
|
||||||
|
logger.info("launcher : " +"*****Launching SoftUpdate()*****")
|
||||||
|
try:
|
||||||
|
Answer = SoftUpdate(logger)
|
||||||
|
except Exception as e:
|
||||||
|
logger.info("launcher : " +"*****FATAL ERROR*****" + str(e))
|
||||||
|
os.abort()
|
||||||
|
logger.info("launcher : " +"*****Ending SoftUpdate()*****")
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
if Answer == True:
|
||||||
|
logger.info("launcher : " +"*****Launching main()*****")
|
||||||
|
State = main(logger)
|
||||||
|
except Exception as e:
|
||||||
|
logger.info("launcher : " +"*****FATAL ERROR*****" + str(e))
|
||||||
|
os.abort()
|
||||||
|
|
||||||
|
logger.info("launcher : " +"*****Ending main()*****")
|
||||||
|
logger.info("launcher : " +"*****Goodbye!*****")
|
||||||
|
|
||||||
|
handlers = logger.handlers[:]
|
||||||
|
for handler in handlers:
|
||||||
|
handler.close()
|
||||||
|
logger.removeHandler(handler)
|
||||||
|
|
||||||
|
|
||||||
|
if os.path.getsize("error.log") == 0:
|
||||||
|
try:
|
||||||
|
os.remove("error.log")
|
||||||
|
except:
|
||||||
|
raise(OSError)
|
||||||
|
os.abort()
|
||||||
|
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
568
CNI_Update.py
Normal file
568
CNI_Update.py
Normal file
@ -0,0 +1,568 @@
|
|||||||
|
"""
|
||||||
|
********************************************************************************
|
||||||
|
*** Projet CNI_Revelator ***
|
||||||
|
|
||||||
|
GNU GPL * 07/2018
|
||||||
|
|
||||||
|
Adrien Bourmault
|
||||||
|
|
||||||
|
UPDATE
|
||||||
|
|
||||||
|
********************************************************************************
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
###IMPORTS GLOBAUX
|
||||||
|
from CNI_GLOBALVAR import *
|
||||||
|
|
||||||
|
|
||||||
|
###IMPORTS LOCAUX
|
||||||
|
from CNI_classes import *
|
||||||
|
|
||||||
|
|
||||||
|
###BEGIN
|
||||||
|
|
||||||
|
def SoftUpdate(logger): #Fonction de mise à jour de l'application
|
||||||
|
|
||||||
|
import zipfile
|
||||||
|
# zip_ref = zipfile.ZipFile(path_to_zip_file, 'r')
|
||||||
|
# zip_ref.extractall(directory_to_extract_to)
|
||||||
|
# zip_ref.close()
|
||||||
|
|
||||||
|
logger.info("SoftUpdate() : " +"Verifying local data dir...")
|
||||||
|
if not os.path.exists(CST_FOLDER):
|
||||||
|
os.makedirs(CST_FOLDER)
|
||||||
|
logger.info("SoftUpdate() : " +"Data dir created !")
|
||||||
|
|
||||||
|
logger.info("SoftUpdate() : " +"Looking for older version in dir...")
|
||||||
|
list = os.listdir('.')
|
||||||
|
|
||||||
|
for file in list:
|
||||||
|
|
||||||
|
if file.startswith("CNIRevelator_"):
|
||||||
|
|
||||||
|
temp = ["0","0","0"]
|
||||||
|
ver = file[13:].split(".")
|
||||||
|
|
||||||
|
|
||||||
|
for i in range(len(ver)):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if ver[i] != "exe" :
|
||||||
|
try:
|
||||||
|
temp[i] = ver[i]
|
||||||
|
except:
|
||||||
|
None
|
||||||
|
|
||||||
|
|
||||||
|
ver = temp.copy()
|
||||||
|
|
||||||
|
try :
|
||||||
|
sum_ver = int(ver[0])*100 + int(ver[1])*10 + int(ver[2])
|
||||||
|
|
||||||
|
if sum_ver < CST_SUM_VER:
|
||||||
|
|
||||||
|
os.remove(file)
|
||||||
|
logger.info("SoftUpdate() : " + "Removed old version : " + str(file) )
|
||||||
|
|
||||||
|
except:
|
||||||
|
|
||||||
|
logger.error("SoftUpdate() : " +"Failing to remove old version : " + str(file) )
|
||||||
|
|
||||||
|
None
|
||||||
|
|
||||||
|
|
||||||
|
def updating(): #Fonction de lancement du thread de maj
|
||||||
|
|
||||||
|
def updator(): #Fonction d'exécution de la maj
|
||||||
|
|
||||||
|
logger.info("[updator() thread] : " + "Welcome !")
|
||||||
|
global ret
|
||||||
|
ret = 11
|
||||||
|
canvas.itemconfigure(message, text="Recherche de mises-à-jour...")
|
||||||
|
p.configure(mode = "indeterminate", value = 0, maximum = 20)
|
||||||
|
p.start()
|
||||||
|
upwin.update()
|
||||||
|
logger.info("[updator() thread] : " + "Looking for updates...")
|
||||||
|
|
||||||
|
try:#Essai par proxy
|
||||||
|
|
||||||
|
def download(url, filename):
|
||||||
|
|
||||||
|
try: # if identifiants disponibles dans un fichier
|
||||||
|
|
||||||
|
logger.info("[download() thread] : " + "Trying getting credentials in the config file")
|
||||||
|
|
||||||
|
with open(CST_FOLDER +'conf.ig', "rb") as config:
|
||||||
|
|
||||||
|
AESObj = AESCipher("<clé>")
|
||||||
|
IPN, IPN_PASS = AESObj.decrypt(config.read()).split("||")[0:2]
|
||||||
|
logger.info("[download() thread] : " + "Got credentials !")
|
||||||
|
|
||||||
|
session = PACSession(proxy_auth=HTTPProxyAuth(IPN, IPN_PASS))
|
||||||
|
logger.info("[download() thread] : " + "Authenticated to proxy successfully")
|
||||||
|
|
||||||
|
|
||||||
|
except IOError as e: #Else : on vérifie les identifiants de proxy
|
||||||
|
|
||||||
|
logger.error("[download() thread] : " + "False or absent credentials in the config file : " + str(e))
|
||||||
|
|
||||||
|
NoConnect = True
|
||||||
|
|
||||||
|
while NoConnect:
|
||||||
|
|
||||||
|
class LoginDialog(Toplevel):
|
||||||
|
global login
|
||||||
|
global key
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.title("Connexion")
|
||||||
|
# ---------------------------------------
|
||||||
|
Label(self, text="IPN : ").pack()
|
||||||
|
self.entry_login = Entry(self)
|
||||||
|
self.entry_login.insert(0, "")
|
||||||
|
self.entry_login.pack()
|
||||||
|
# ---------------------------------------
|
||||||
|
Label(self, text="Mot de passe : ").pack()
|
||||||
|
self.entry_pass = Entry(self, show='*')
|
||||||
|
self.entry_pass.insert(0, "")
|
||||||
|
self.entry_pass.pack()
|
||||||
|
# ---------------------------------------
|
||||||
|
Button(self, text="Connexion", command=self.connecti).pack()
|
||||||
|
self.resizable(width=False, height=False)
|
||||||
|
#taille souhaite de la fenetre
|
||||||
|
w = 150
|
||||||
|
h = 110
|
||||||
|
#pour centrer la fenetre
|
||||||
|
#taille de l'ecran
|
||||||
|
self.update()
|
||||||
|
ws = self.winfo_screenwidth()
|
||||||
|
hs = self.winfo_screenheight()
|
||||||
|
|
||||||
|
if getattr( sys, 'frozen', False ) :
|
||||||
|
self.iconbitmap(sys._MEIPASS + "\id-card.ico\id-card.ico")
|
||||||
|
else:
|
||||||
|
self.iconbitmap("id-card.ico")
|
||||||
|
|
||||||
|
upwin.update()
|
||||||
|
#calcul la position de la fenetre
|
||||||
|
x = (ws/2) - (w/2)
|
||||||
|
y = (hs/2) - (h/2)
|
||||||
|
#applique la taille et la position
|
||||||
|
self.geometry('%dx%d+%d+%d' % (w, h, x, y))
|
||||||
|
|
||||||
|
def connecti(self):
|
||||||
|
global login
|
||||||
|
global key
|
||||||
|
login = self.entry_login.get().strip()
|
||||||
|
key = self.entry_pass.get().strip()
|
||||||
|
self.destroy()
|
||||||
|
|
||||||
|
session = PACSession()
|
||||||
|
|
||||||
|
if session.get_pac() == None:
|
||||||
|
IPN = ""
|
||||||
|
IPN_PASS = ""
|
||||||
|
NoConnect = False
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
canvas.itemconfigure(message, text="En attente de connexion au serveur proxy...")
|
||||||
|
global login
|
||||||
|
global key
|
||||||
|
login = ""
|
||||||
|
key = ""
|
||||||
|
|
||||||
|
result = LoginDialog(upwin)
|
||||||
|
result.transient(upwin)
|
||||||
|
result.grab_set()
|
||||||
|
upwin.wait_window(result)
|
||||||
|
|
||||||
|
IPN = login
|
||||||
|
IPN_PASS = key
|
||||||
|
|
||||||
|
session = PACSession(proxy_auth=HTTPProxyAuth(IPN, IPN_PASS))
|
||||||
|
Ans = session.get("http://www.google.com")
|
||||||
|
|
||||||
|
if str(Ans) == "<Response [407]>":
|
||||||
|
canvas.itemconfigure(message, text="Identifiants erronés, accès refusé")
|
||||||
|
logger.info("[download() thread] : " + "407 Error")
|
||||||
|
time.sleep(1)
|
||||||
|
elif str(Ans) == "<Response [200]>":
|
||||||
|
logger.info("[download() thread] : " + "Connection ok !")
|
||||||
|
NoConnect = False
|
||||||
|
else:
|
||||||
|
raise(IOError())
|
||||||
|
|
||||||
|
AESObj = AESCipher("<clé>")
|
||||||
|
|
||||||
|
with open(CST_FOLDER +"conf.ig","wb+") as f:
|
||||||
|
logger.info("[download() thread] : " + "Saving credentials in encrypted config file")
|
||||||
|
f.write(AESObj.encrypt(IPN + "||" + IPN_PASS))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if IPN == "i005316":
|
||||||
|
canvas.itemconfigure(message, text="Bienvenue Thierry !")
|
||||||
|
elif IPN == "i020251":
|
||||||
|
canvas.itemconfigure(message, text="Bienvenue Samia !")
|
||||||
|
elif IPN == "i018410":
|
||||||
|
canvas.itemconfigure(message, text="Bienvenue Adrien !")
|
||||||
|
elif IPN == "i003067":
|
||||||
|
canvas.itemconfigure(message, text="Bienvenue Remy !")
|
||||||
|
elif IPN == "i018422":
|
||||||
|
canvas.itemconfigure(message, text="Bienvenue Eloise !")
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
Prox_us = session.get_pac().find_proxy_for_url(CST_LINK,"neoxgroup.eu")
|
||||||
|
PROXY_USABLE = Prox_us[6:-1].split(";")[0]
|
||||||
|
proxy_server_url = IPN +":"+IPN_PASS+"@" + PROXY_USABLE
|
||||||
|
ph = urllib2.ProxyHandler( { 'http' : proxy_server_url } )
|
||||||
|
auth = urllib2.ProxyBasicAuthHandler()
|
||||||
|
server= urllib2.build_opener( ph, auth, urllib2.HTTPHandler )
|
||||||
|
urllib2.install_opener(server)
|
||||||
|
logger.info("[download() thread] : " + "Proxy connection initiated successfully")
|
||||||
|
|
||||||
|
except:
|
||||||
|
|
||||||
|
logger.info("[download() thread] : " + "Proxy connection not initiated")
|
||||||
|
|
||||||
|
try:
|
||||||
|
urllib2.urlretrieve(url, filename)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("[download() thread] : " + "HTTP ERROR " )
|
||||||
|
return e
|
||||||
|
|
||||||
|
##updator()
|
||||||
|
logger.info("[updator() thread] : " + "Prepare downloading the version recap file...")
|
||||||
|
tempfile = CST_FOLDER + 'temp' + str(random.randint(11111,99999)) + ".cniu"
|
||||||
|
isOk = download(CST_LINK+"Version.txt", tempfile)
|
||||||
|
|
||||||
|
if not isOk:
|
||||||
|
raise(isOk)
|
||||||
|
|
||||||
|
urllib2.urlcleanup()
|
||||||
|
logger.info("[updator() thread] : " + "Opening version recap file...")
|
||||||
|
file_ver = open(tempfile, "r")
|
||||||
|
logger.info("[updator() thread] : " + "Reading version recap file...")
|
||||||
|
version = file_ver.read()
|
||||||
|
logger.info("[updator() thread] : " + "Closing version recap file...")
|
||||||
|
repert = version.split("|")
|
||||||
|
file_ver.close()
|
||||||
|
logger.info("[updator() thread] : " + "Deleting version recap file...")
|
||||||
|
os.remove(tempfile)
|
||||||
|
logger.info("[updator() thread] : " + "Parsing informations about version...")
|
||||||
|
final_f = "CNI_file"
|
||||||
|
final_ver = ['0','0','0']
|
||||||
|
|
||||||
|
for file in repert:
|
||||||
|
|
||||||
|
if str.startswith(file,CST_NAME): #On prend les fichiers d'interêt du répertoire
|
||||||
|
|
||||||
|
ver = file.replace(CST_NAME+"_","").split(".") #On ne garde que le numéro de version
|
||||||
|
|
||||||
|
temp = ["0","0","0"]
|
||||||
|
|
||||||
|
for i in range(len(ver)):
|
||||||
|
|
||||||
|
temp[i] = ver[i]
|
||||||
|
|
||||||
|
ver = temp.copy()
|
||||||
|
|
||||||
|
sum_fver = int(final_ver[0])*100 + int(final_ver[1])*10 + int(final_ver[2])
|
||||||
|
|
||||||
|
sum_ver = int(ver[0])*100 + int(ver[1])*10 + int(ver[2])
|
||||||
|
|
||||||
|
|
||||||
|
if sum_ver > sum_fver:
|
||||||
|
|
||||||
|
final_ver = ver.copy()
|
||||||
|
final_f = file
|
||||||
|
|
||||||
|
sum_ver = int(final_ver[0])*100 + int(final_ver[1])*10 + int(final_ver[2])
|
||||||
|
|
||||||
|
if final_f != "CNI_file" and (sum_ver > CST_SUM_VER):
|
||||||
|
#On a une maj
|
||||||
|
logger.info("[updator() thread] : " + "New version of CNIRevelator found !")
|
||||||
|
canvas.itemconfigure(message, text="Mise à jour disponible ! Préparation du téléchargement...")
|
||||||
|
logger.info("[updator() thread] : " + "Preparing download")
|
||||||
|
|
||||||
|
with open(CST_FOLDER +'conf.ig', "rb") as config:
|
||||||
|
|
||||||
|
logger.info("[updator() thread] : " + "Reading credentials for proxy in config file...")
|
||||||
|
|
||||||
|
AESObj = AESCipher("<clé>")
|
||||||
|
IPN, IPN_PASS = AESObj.decrypt(config.read()).split("||")[0:2]
|
||||||
|
|
||||||
|
session = PACSession(proxy_auth=HTTPProxyAuth(IPN, IPN_PASS))
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
Prox_us = session.get_pac().find_proxy_for_url(CST_LINK,"neoxgroup.eu")
|
||||||
|
PROXY_USABLE = Prox_us[6:-1].split(";")[0]
|
||||||
|
proxy_server_url = IPN +":"+IPN_PASS+"@" + PROXY_USABLE
|
||||||
|
ph = urllib2.ProxyHandler( { 'http' : proxy_server_url } )
|
||||||
|
auth = urllib2.ProxyBasicAuthHandler()
|
||||||
|
server= urllib2.build_opener( ph, auth, urllib2.HTTPHandler )
|
||||||
|
logger.info("[updator() thread] : " + "Connection to the proxy initiated successfully !")
|
||||||
|
|
||||||
|
|
||||||
|
except:
|
||||||
|
|
||||||
|
canvas.itemconfigure(message, text="Téléchargement en connexion directe...")
|
||||||
|
server= urllib2.build_opener()
|
||||||
|
logger.info("[updator() thread] : " + "Direct connection initiated successfully")
|
||||||
|
|
||||||
|
logger.info("[updator() thread] : " + "Launching download of " + final_f)
|
||||||
|
Statut = Download( CST_LINK + final_f, final_f, final_f, server, p, canvas, message, logger )
|
||||||
|
|
||||||
|
if Statut.success:
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.rename(final_f, final_f + ".exe")
|
||||||
|
except IOError:
|
||||||
|
logger.error("[updator() thread] : " + "Unable to rename the file ! Wait 3 sec and retry")
|
||||||
|
time.sleep(3)
|
||||||
|
try:
|
||||||
|
os.rename(final_f, final_f + ".exe")
|
||||||
|
except IOError:
|
||||||
|
logger.critical("[updator() thread] : " + "Unable to rename the file !")
|
||||||
|
|
||||||
|
else:
|
||||||
|
canvas.itemconfigure(message, text="Téléchargement terminé ! Préparation du lancement...")
|
||||||
|
logger.info("[updator() thread] : " + "Download of " + final_f + "finished successfully")
|
||||||
|
p.configure(mode = "indeterminate", value = 0, maximum = 20)
|
||||||
|
p.start()
|
||||||
|
time.sleep(1)
|
||||||
|
logger.info("[updator() thread] : " + "Launching " + final_f)
|
||||||
|
try:
|
||||||
|
proc = subprocess.Popen(final_f + ".exe", shell=False,stdin=None, stdout=None, stderr=None, close_fds=True)
|
||||||
|
except:
|
||||||
|
logger.error("[updator() thread] : " + "Unable to start the new version ! Wait 3 sec and retry")
|
||||||
|
time.sleep(3)
|
||||||
|
try:
|
||||||
|
proc = subprocess.Popen(final_f + ".exe", shell=False,stdin=None, stdout=None, stderr=None, close_fds=True)
|
||||||
|
except Exception as e:
|
||||||
|
logger.critical("[updator() thread] : " + "Unable to start the new version ! Stopping : " + str(e))
|
||||||
|
showerror("Erreur d'appel de procédure distante", "Le lancement du nouveau programme a échoué, vous devez le lancer manuellement une fois cette fenêtre fermée")
|
||||||
|
ret = 12
|
||||||
|
else:
|
||||||
|
canvas.itemconfigure(message, text="Echec de la mise à jour : Erreur HTTP. Préparation du lancement...")
|
||||||
|
logger.error("[updator() thread] : " + "Update has failed with HTTP error")
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
else:
|
||||||
|
canvas.itemconfigure(message, text="Logiciel déjà à jour. Préparation du lancement...")
|
||||||
|
logger.info("[updator() thread] : " + "CNIRevelator is up to date !")
|
||||||
|
time.sleep(1)
|
||||||
|
ret = 11
|
||||||
|
|
||||||
|
|
||||||
|
#Tesseract
|
||||||
|
if os.path.exists(CST_FOLDER + "Tesseract-OCR4\\tesseract.exe"):
|
||||||
|
os.environ["PATH"] = CST_FOLDER + "Tesseract-OCR4\\"
|
||||||
|
os.environ["TESSDATA_PREFIX"] = CST_FOLDER + "Tesseract-OCR4\\tessdata"
|
||||||
|
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
final_f = "tesseract_4"
|
||||||
|
|
||||||
|
#On a une maj
|
||||||
|
logger.info("[updator() thread] : " + "Downloading tesseract 4 !")
|
||||||
|
canvas.itemconfigure(message, text="Mise à jour du module OCR ! Préparation du téléchargement...")
|
||||||
|
logger.info("[updator() thread] : " + "Preparing download")
|
||||||
|
|
||||||
|
with open(CST_FOLDER +'conf.ig', "rb") as config:
|
||||||
|
|
||||||
|
logger.info("[updator() thread] : " + "Reading credentials for proxy in config file...")
|
||||||
|
|
||||||
|
AESObj = AESCipher("<clé>")
|
||||||
|
IPN, IPN_PASS = AESObj.decrypt(config.read()).split("||")[0:2]
|
||||||
|
|
||||||
|
session = PACSession(proxy_auth=HTTPProxyAuth(IPN, IPN_PASS))
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
Prox_us = session.get_pac().find_proxy_for_url(CST_LINK,"neoxgroup.eu")
|
||||||
|
PROXY_USABLE = Prox_us[6:-1].split(";")[0]
|
||||||
|
proxy_server_url = IPN +":"+IPN_PASS+"@" + PROXY_USABLE
|
||||||
|
ph = urllib2.ProxyHandler( { 'http' : proxy_server_url } )
|
||||||
|
auth = urllib2.ProxyBasicAuthHandler()
|
||||||
|
server= urllib2.build_opener( ph, auth, urllib2.HTTPHandler )
|
||||||
|
logger.info("[updator() thread] : " + "Connection to the proxy initiated successfully !")
|
||||||
|
|
||||||
|
|
||||||
|
except:
|
||||||
|
|
||||||
|
canvas.itemconfigure(message, text="Téléchargement en connexion directe...")
|
||||||
|
server= urllib2.build_opener()
|
||||||
|
logger.info("[updator() thread] : " + "Direct connection initiated successfully")
|
||||||
|
|
||||||
|
logger.info("[updator() thread] : " + "Launching download of " + final_f)
|
||||||
|
|
||||||
|
Statut = Download( CST_LINK + final_f, CST_FOLDER + final_f, final_f, server, p, canvas, message, logger )
|
||||||
|
|
||||||
|
if Statut.success:
|
||||||
|
|
||||||
|
canvas.itemconfigure(message, text="Téléchargement terminé ! Installation...")
|
||||||
|
logger.info("[updator() thread] : " + "Download of " + final_f + "finished successfully")
|
||||||
|
p.configure(mode = "indeterminate", value = 0, maximum = 20)
|
||||||
|
p.start()
|
||||||
|
|
||||||
|
try:
|
||||||
|
zip_ref = zipfile.ZipFile(CST_FOLDER + final_f, 'r')
|
||||||
|
zip_ref.extractall(CST_FOLDER)
|
||||||
|
zip_ref.close()
|
||||||
|
os.environ["PATH"] = CST_FOLDER + "Tesseract-OCR4\\"
|
||||||
|
os.environ["TESSDATA_PREFIX"] = CST_FOLDER + "Tesseract-OCR4\\tessdata"
|
||||||
|
canvas.itemconfigure(message, text="Installation terminée !")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
except:
|
||||||
|
logger.error("[updator() thread] : " + "Unable to install the module. Wait and retry")
|
||||||
|
time.sleep(3)
|
||||||
|
try:
|
||||||
|
zip_ref = zipfile.ZipFile(CST_FOLDER + final_f, 'r')
|
||||||
|
zip_ref.extractall(CST_FOLDER)
|
||||||
|
zip_ref.close()
|
||||||
|
os.environ["PATH"] = CST_FOLDER + "Tesseract-OCR4\\"
|
||||||
|
os.environ["TESSDATA_PREFIX"] = CST_FOLDER + "Tesseract-OCR4\\tessdata"
|
||||||
|
canvas.itemconfigure(message, text="Installation terminée !")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.critical("[updator() thread] : " + "Unable to install the module ! Stopping : " + str(e))
|
||||||
|
showerror("Erreur d'appel de procédure distante", "L'installation du module OCR a échoué, contactez le développeur.")
|
||||||
|
ret = 11
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
logger.critical("[updator() thread] : " + "Unable to download the module ! ")
|
||||||
|
showerror("Erreur de téléchargement", "L'installation du module OCR a échoué, merci d'indiquer le chemin d'accès au fichier tesseract_4 dans la fenêtre suivante")
|
||||||
|
|
||||||
|
path = filedialog.askopenfilename(title = "Indiquez le chemin d'accès à tesseract_4...",filetypes = (("Tesseract_4","*.cni4"), ("Tesseract_4","*.cni4")))
|
||||||
|
|
||||||
|
if path != "":
|
||||||
|
|
||||||
|
try:
|
||||||
|
canvas.itemconfigure(message, text="Installation...")
|
||||||
|
zip_ref = zipfile.ZipFile(path, 'r')
|
||||||
|
zip_ref.extractall(CST_FOLDER)
|
||||||
|
zip_ref.close()
|
||||||
|
logger.error("[updator() thread] : " + "Manual installation successed")
|
||||||
|
canvas.itemconfigure(message, text="Installation terminée !")
|
||||||
|
os.environ["PATH"] = CST_FOLDER + "Tesseract-OCR4\\"
|
||||||
|
os.environ["TESSDATA_PREFIX"] = CST_FOLDER + "Tesseract-OCR4\\tessdata"
|
||||||
|
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
|
||||||
|
logger.error("[updator() thread] : " + "Manual installation has failed" + str(e) )
|
||||||
|
showerror("Erreur de lecture", "Le module OCR n'a pas pu être installé, la saisie automatique de scans ne pourra donc fonctionner")
|
||||||
|
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
showerror("Opération annulée", "Le module OCR n'a été installé, la saisie automatique de scans ne pourra donc fonctionner")
|
||||||
|
|
||||||
|
|
||||||
|
except URLExcept.HTTPError as e:
|
||||||
|
|
||||||
|
canvas.itemconfigure(message, text="Echec de la mise à jour : Erreur HTTP " + str(e.code) + " . Préparation du lancement...")
|
||||||
|
logger.error("[updator() thread] : " + "Update has failed with HTTP error" + str(e.code) )
|
||||||
|
|
||||||
|
if int(e.code) == 407:
|
||||||
|
showerror("Erreur 407", "Attention : le système de mise à jour automatique a fait face à une erreur 407, signifiant que la connexion au serveur proxy a été refusée. Vos identifiants vous seront redemandés au prochain démarrage. La mise à jour a échoué.")
|
||||||
|
logger.info("[updator() thread] : " + "Credential error. Deleting the config file...")
|
||||||
|
os.remove(CST_FOLDER +"conf.ig")
|
||||||
|
p.configure(mode = "indeterminate", value = 0, maximum = 20)
|
||||||
|
p.start()
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
|
||||||
|
canvas.itemconfigure(message, text="Echec de la mise à jour. Préparation du lancement...")
|
||||||
|
logger.error("[updator() thread] : " + "Error from the updating system : " + str(e))
|
||||||
|
p.configure(mode = "indeterminate", value = 0, maximum = 20)
|
||||||
|
p.start()
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
|
||||||
|
p.stop()
|
||||||
|
upwin.destroy()
|
||||||
|
root.destroy()
|
||||||
|
return ret
|
||||||
|
|
||||||
|
##updating()
|
||||||
|
global ret
|
||||||
|
logger.info("updating() : " + "Launching updator() thread...")
|
||||||
|
threading.Thread(target=updator, daemon=True).start()
|
||||||
|
logger.info("updating() [Thread] : " + "Ending updator() thread")
|
||||||
|
return None
|
||||||
|
|
||||||
|
##SoftUpdate()
|
||||||
|
|
||||||
|
global ret
|
||||||
|
ret = 11
|
||||||
|
global upwin
|
||||||
|
root = Tk()
|
||||||
|
root.attributes('-alpha', 0.0) #For icon
|
||||||
|
#root.lower()
|
||||||
|
root.iconify()
|
||||||
|
|
||||||
|
upwin = Toplevel(root)
|
||||||
|
upwin.overrideredirect(1)
|
||||||
|
upwin.configure(bg = CST_COLOR)
|
||||||
|
|
||||||
|
upwin.resizable(width=False, height=False)
|
||||||
|
#taille souhaite de la fenetre
|
||||||
|
w = 600
|
||||||
|
h = 300
|
||||||
|
#pour centrer la fenetre
|
||||||
|
#taille de l'ecran
|
||||||
|
upwin.update()
|
||||||
|
canvas = Canvas(upwin, width=600, height=270, bg=CST_COLOR, highlightthickness=0)
|
||||||
|
pbar = Canvas(upwin, width=600, height=30, bg=CST_COLOR)
|
||||||
|
p = ttk.Progressbar(pbar, orient=HORIZONTAL, length=590, mode='determinate')
|
||||||
|
upwin.update()
|
||||||
|
ws = upwin.winfo_screenwidth()
|
||||||
|
hs = upwin.winfo_screenheight()
|
||||||
|
canvas.create_text(w/2, h/3, text=CST_NAME + " " + CST_VERTITLE, font="Calibri 30 bold", fill="white")
|
||||||
|
message = canvas.create_text(w/2, h/1.150, text=" ", font="Calibri 9", fill="white")
|
||||||
|
upwin.update()
|
||||||
|
#calcul la position de la fenetre
|
||||||
|
x = (ws/2) - (w/2)
|
||||||
|
y = (hs/2) - (h/2)
|
||||||
|
#applique la taille et la position
|
||||||
|
upwin.geometry('%dx%d+%d+%d' % (w, h, x, y))
|
||||||
|
canvas.grid()
|
||||||
|
pbar.grid()
|
||||||
|
p.grid()
|
||||||
|
upwin.after(2000, updating)
|
||||||
|
|
||||||
|
if getattr( sys, 'frozen', False ) :
|
||||||
|
root.iconbitmap(sys._MEIPASS + "\id-card.ico\id-card.ico")
|
||||||
|
else:
|
||||||
|
root.iconbitmap("id-card.ico")
|
||||||
|
|
||||||
|
# On démarre la boucle Tkinter qui s'interrompt quand on ferme la fenêtre
|
||||||
|
logger.info("SoftUpdate() : " + "Entering upwin mainloop()")
|
||||||
|
upwin.protocol('WM_DELETE_WINDOW', lambda: root.destroy())
|
||||||
|
upwin.mainloop()
|
||||||
|
logger.info("SoftUpdate() : " + "Exiting upwin mainloop()")
|
||||||
|
|
||||||
|
if ret == 11:
|
||||||
|
logger.info("SoftUpdate() : " + "OK to start to main() normally !")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.info("SoftUpdate() : " + "Program will stop !")
|
||||||
|
return False
|
1551
CNI_classes.py
Normal file
1551
CNI_classes.py
Normal file
File diff suppressed because one or more lines are too long
413
CNI_pytesseract.py
Normal file
413
CNI_pytesseract.py
Normal file
@ -0,0 +1,413 @@
|
|||||||
|
"""
|
||||||
|
********************************************************************************
|
||||||
|
*** Projet CNI_Revelator ***
|
||||||
|
|
||||||
|
GNU GPL * 07/2018
|
||||||
|
|
||||||
|
Adrien Bourmault
|
||||||
|
|
||||||
|
Pytesseract modification to comply with Pyinstaller
|
||||||
|
|
||||||
|
*****
|
||||||
|
"""
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
'''
|
||||||
|
Python-tesseract. For more information: https://github.com/madmaze/pytesseract
|
||||||
|
'''
|
||||||
|
|
||||||
|
try:
|
||||||
|
import Image
|
||||||
|
except ImportError:
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import tempfile
|
||||||
|
import shlex
|
||||||
|
import string
|
||||||
|
from glob import iglob
|
||||||
|
from pkgutil import find_loader
|
||||||
|
from distutils.version import LooseVersion
|
||||||
|
from os.path import realpath, normpath, normcase
|
||||||
|
|
||||||
|
numpy_installed = find_loader('numpy') is not None
|
||||||
|
if numpy_installed:
|
||||||
|
from numpy import ndarray
|
||||||
|
|
||||||
|
# CHANGE THIS IF TESSERACT IS NOT IN YOUR PATH, OR IS NAMED DIFFERENTLY
|
||||||
|
tesseract_cmd = 'tesseract'
|
||||||
|
RGB_MODE = 'RGB'
|
||||||
|
OSD_KEYS = {
|
||||||
|
'Page number': ('page_num', int),
|
||||||
|
'Orientation in degrees': ('orientation', int),
|
||||||
|
'Rotate': ('rotate', int),
|
||||||
|
'Orientation confidence': ('orientation_conf', float),
|
||||||
|
'Script': ('script', str),
|
||||||
|
'Script confidence': ('script_conf', float)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Output:
|
||||||
|
STRING = "string"
|
||||||
|
BYTES = "bytes"
|
||||||
|
DICT = "dict"
|
||||||
|
|
||||||
|
|
||||||
|
class TesseractError(RuntimeError):
|
||||||
|
def __init__(self, status, message):
|
||||||
|
self.status = status
|
||||||
|
self.message = message
|
||||||
|
self.args = (status, message)
|
||||||
|
|
||||||
|
|
||||||
|
class TesseractNotFoundError(EnvironmentError):
|
||||||
|
def __init__(self):
|
||||||
|
super(TesseractNotFoundError, self).__init__(
|
||||||
|
tesseract_cmd + " is not installed or it's not in your path"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TSVNotSupported(EnvironmentError):
|
||||||
|
def __init__(self):
|
||||||
|
super(TSVNotSupported, self).__init__(
|
||||||
|
'TSV output not supported. Tesseract >= 3.05 required'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def run_once(func):
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
if wrapper._result is wrapper:
|
||||||
|
wrapper._result = func(*args, **kwargs)
|
||||||
|
return wrapper._result
|
||||||
|
|
||||||
|
wrapper._result = wrapper
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
def get_errors(error_string):
|
||||||
|
return u' '.join(
|
||||||
|
line for line in error_string.decode('utf-8').splitlines()
|
||||||
|
).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def cleanup(temp_name):
|
||||||
|
''' Tries to remove files by filename wildcard path. '''
|
||||||
|
for filename in iglob(temp_name + '*' if temp_name else temp_name):
|
||||||
|
try:
|
||||||
|
os.remove(filename)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def prepare(image):
|
||||||
|
if isinstance(image, Image.Image):
|
||||||
|
return image
|
||||||
|
|
||||||
|
if numpy_installed and isinstance(image, ndarray):
|
||||||
|
return Image.fromarray(image)
|
||||||
|
|
||||||
|
raise TypeError('Unsupported image object')
|
||||||
|
|
||||||
|
|
||||||
|
def save_image(image):
|
||||||
|
temp_name = tempfile.mktemp(prefix='tess_')
|
||||||
|
if isinstance(image, str):
|
||||||
|
return temp_name, realpath(normpath(normcase(image)))
|
||||||
|
|
||||||
|
image = prepare(image)
|
||||||
|
img_extension = image.format
|
||||||
|
if image.format not in {'JPEG', 'PNG', 'TIFF', 'BMP', 'GIF'}:
|
||||||
|
img_extension = 'PNG'
|
||||||
|
|
||||||
|
if not image.mode.startswith(RGB_MODE):
|
||||||
|
image = image.convert(RGB_MODE)
|
||||||
|
|
||||||
|
if 'A' in image.getbands():
|
||||||
|
# discard and replace the alpha channel with white background
|
||||||
|
background = Image.new(RGB_MODE, image.size, (255, 255, 255))
|
||||||
|
background.paste(image, (0, 0), image)
|
||||||
|
image = background
|
||||||
|
|
||||||
|
input_file_name = temp_name + os.extsep + img_extension
|
||||||
|
image.save(input_file_name, format=img_extension, **image.info)
|
||||||
|
return temp_name, input_file_name
|
||||||
|
|
||||||
|
|
||||||
|
def subprocess_args(include_stdout=True):
|
||||||
|
# The following is true only on Windows.
|
||||||
|
if hasattr(subprocess, 'STARTUPINFO'):
|
||||||
|
# On Windows, subprocess calls will pop up a command window by default
|
||||||
|
# when run from Pyinstaller with the ``--noconsole`` option. Avoid this
|
||||||
|
# distraction.
|
||||||
|
si = subprocess.STARTUPINFO()
|
||||||
|
si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||||
|
# Windows doesn't search the path by default. Pass it an environment so
|
||||||
|
# it will.
|
||||||
|
env = os.environ
|
||||||
|
else:
|
||||||
|
si = None
|
||||||
|
env = None
|
||||||
|
|
||||||
|
# ``subprocess.check_output`` doesn't allow specifying ``stdout``::
|
||||||
|
#
|
||||||
|
# Traceback (most recent call last):
|
||||||
|
# File "test_subprocess.py", line 58, in <module>
|
||||||
|
# **subprocess_args(stdout=None))
|
||||||
|
# File "C:\Python27\lib\subprocess.py", line 567, in check_output
|
||||||
|
# raise ValueError('stdout argument not allowed, it will be overridden.')
|
||||||
|
# ValueError: stdout argument not allowed, it will be overridden.
|
||||||
|
#
|
||||||
|
# So, add it only if it's needed.
|
||||||
|
if include_stdout:
|
||||||
|
ret = {'stdout': subprocess.PIPE}
|
||||||
|
else:
|
||||||
|
ret = {}
|
||||||
|
|
||||||
|
# On Windows, running this from the binary produced by Pyinstaller
|
||||||
|
# with the ``--noconsole`` option requires redirecting everything
|
||||||
|
# (stdin, stdout, stderr) to avoid an OSError exception
|
||||||
|
# "[Error 6] the handle is invalid."
|
||||||
|
ret.update({'stdin': subprocess.PIPE,
|
||||||
|
'stderr': subprocess.PIPE,
|
||||||
|
'startupinfo': si,
|
||||||
|
'env': env
|
||||||
|
})
|
||||||
|
return ret
|
||||||
|
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
|
def run_tesseract(input_filename,
|
||||||
|
output_filename_base,
|
||||||
|
extension,
|
||||||
|
lang,
|
||||||
|
config='',
|
||||||
|
nice=0):
|
||||||
|
cmd_args = []
|
||||||
|
|
||||||
|
if not sys.platform.startswith('win32') and nice != 0:
|
||||||
|
cmd_args += ('nice', '-n', str(nice))
|
||||||
|
|
||||||
|
cmd_args += (tesseract_cmd, input_filename, output_filename_base)
|
||||||
|
|
||||||
|
if lang is not None:
|
||||||
|
cmd_args += ('-l', lang)
|
||||||
|
|
||||||
|
cmd_args += shlex.split(config)
|
||||||
|
|
||||||
|
if extension not in ('box', 'osd', 'tsv'):
|
||||||
|
cmd_args.append(extension)
|
||||||
|
|
||||||
|
try:
|
||||||
|
proc = subprocess.Popen(cmd_args, **subprocess_args())
|
||||||
|
except OSError:
|
||||||
|
raise TesseractNotFoundError()
|
||||||
|
|
||||||
|
status_code, error_string = proc.wait(), proc.stderr.read()
|
||||||
|
proc.stderr.close()
|
||||||
|
|
||||||
|
if status_code:
|
||||||
|
raise TesseractError(status_code, get_errors(error_string))
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def run_and_get_output(image,
|
||||||
|
extension,
|
||||||
|
lang=None,
|
||||||
|
config='',
|
||||||
|
nice=0,
|
||||||
|
return_bytes=False):
|
||||||
|
|
||||||
|
temp_name, input_filename = '', ''
|
||||||
|
try:
|
||||||
|
temp_name, input_filename = save_image(image)
|
||||||
|
kwargs = {
|
||||||
|
'input_filename': input_filename,
|
||||||
|
'output_filename_base': temp_name + '_out',
|
||||||
|
'extension': extension,
|
||||||
|
'lang': lang,
|
||||||
|
'config': config,
|
||||||
|
'nice': nice
|
||||||
|
}
|
||||||
|
|
||||||
|
run_tesseract(**kwargs)
|
||||||
|
filename = kwargs['output_filename_base'] + os.extsep + extension
|
||||||
|
with open(filename, 'rb') as output_file:
|
||||||
|
if return_bytes:
|
||||||
|
return output_file.read()
|
||||||
|
return output_file.read().decode('utf-8').strip()
|
||||||
|
finally:
|
||||||
|
cleanup(temp_name)
|
||||||
|
|
||||||
|
|
||||||
|
def file_to_dict(tsv, cell_delimiter, str_col_idx):
|
||||||
|
result = {}
|
||||||
|
rows = [row.split(cell_delimiter) for row in tsv.split('\n')]
|
||||||
|
if not rows:
|
||||||
|
return result
|
||||||
|
|
||||||
|
header = rows.pop(0)
|
||||||
|
if len(rows[-1]) < len(header):
|
||||||
|
# Fixes bug that occurs when last text string in TSV is null, and
|
||||||
|
# last row is missing a final cell in TSV file
|
||||||
|
rows[-1].append('')
|
||||||
|
|
||||||
|
if str_col_idx < 0:
|
||||||
|
str_col_idx += len(header)
|
||||||
|
|
||||||
|
for i, head in enumerate(header):
|
||||||
|
result[head] = [
|
||||||
|
int(row[i]) if i != str_col_idx else row[i] for row in rows
|
||||||
|
]
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def is_valid(val, _type):
|
||||||
|
if _type is int:
|
||||||
|
return val.isdigit()
|
||||||
|
|
||||||
|
if _type is float:
|
||||||
|
try:
|
||||||
|
float(val)
|
||||||
|
return True
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def osd_to_dict(osd):
|
||||||
|
return {
|
||||||
|
OSD_KEYS[kv[0]][0]: OSD_KEYS[kv[0]][1](kv[1]) for kv in (
|
||||||
|
line.split(': ') for line in osd.split('\n')
|
||||||
|
) if len(kv) == 2 and is_valid(kv[1], OSD_KEYS[kv[0]][1])
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@run_once
|
||||||
|
def get_tesseract_version():
|
||||||
|
'''
|
||||||
|
Returns LooseVersion object of the Tesseract version
|
||||||
|
'''
|
||||||
|
try:
|
||||||
|
return LooseVersion(
|
||||||
|
subprocess.check_output([tesseract_cmd, '--version'],
|
||||||
|
**subprocess_args(False)).decode('utf-8').split()[1].lstrip(string.printable[10:])
|
||||||
|
)
|
||||||
|
except OSError:
|
||||||
|
raise TesseractNotFoundError()
|
||||||
|
|
||||||
|
|
||||||
|
def image_to_string(image,
|
||||||
|
lang=None,
|
||||||
|
config='',
|
||||||
|
nice=0,
|
||||||
|
boxes=False,
|
||||||
|
output_type=Output.STRING):
|
||||||
|
'''
|
||||||
|
Returns the result of a Tesseract OCR run on the provided image to string
|
||||||
|
'''
|
||||||
|
if boxes:
|
||||||
|
# Added for backwards compatibility
|
||||||
|
print('\nWarning: Argument \'boxes\' is deprecated and will be removed'
|
||||||
|
' in future versions. Use function image_to_boxes instead.\n')
|
||||||
|
return image_to_boxes(image, lang, config, nice, output_type)
|
||||||
|
|
||||||
|
args = [image, 'txt', lang, config, nice]
|
||||||
|
|
||||||
|
if output_type == Output.DICT:
|
||||||
|
return {'text': run_and_get_output(*args)}
|
||||||
|
elif output_type == Output.BYTES:
|
||||||
|
args.append(True)
|
||||||
|
|
||||||
|
return run_and_get_output(*args)
|
||||||
|
|
||||||
|
|
||||||
|
def image_to_boxes(image,
|
||||||
|
lang=None,
|
||||||
|
config='',
|
||||||
|
nice=0,
|
||||||
|
output_type=Output.STRING):
|
||||||
|
'''
|
||||||
|
Returns string containing recognized characters and their box boundaries
|
||||||
|
'''
|
||||||
|
config += ' batch.nochop makebox'
|
||||||
|
args = [image, 'box', lang, config, nice]
|
||||||
|
|
||||||
|
if output_type == Output.DICT:
|
||||||
|
box_header = 'char left bottom right top page\n'
|
||||||
|
return file_to_dict(box_header + run_and_get_output(*args), ' ', 0)
|
||||||
|
elif output_type == Output.BYTES:
|
||||||
|
args.append(True)
|
||||||
|
|
||||||
|
return run_and_get_output(*args)
|
||||||
|
|
||||||
|
|
||||||
|
def image_to_data(image,
|
||||||
|
lang=None,
|
||||||
|
config='',
|
||||||
|
nice=0,
|
||||||
|
output_type=Output.STRING):
|
||||||
|
'''
|
||||||
|
Returns string containing box boundaries, confidences,
|
||||||
|
and other information. Requires Tesseract 3.05+
|
||||||
|
'''
|
||||||
|
|
||||||
|
if get_tesseract_version() < '3.05':
|
||||||
|
raise TSVNotSupported()
|
||||||
|
|
||||||
|
config = '{} {}'.format('-c tessedit_create_tsv=1', config.strip()).strip()
|
||||||
|
args = [image, 'tsv', lang, config, nice]
|
||||||
|
|
||||||
|
if output_type == Output.DICT:
|
||||||
|
return file_to_dict(run_and_get_output(*args), '\t', -1)
|
||||||
|
elif output_type == Output.BYTES:
|
||||||
|
args.append(True)
|
||||||
|
|
||||||
|
return run_and_get_output(*args)
|
||||||
|
|
||||||
|
|
||||||
|
def image_to_osd(image,
|
||||||
|
lang='osd',
|
||||||
|
config='',
|
||||||
|
nice=0,
|
||||||
|
output_type=Output.STRING):
|
||||||
|
'''
|
||||||
|
Returns string containing the orientation and script detection (OSD)
|
||||||
|
'''
|
||||||
|
config = '{}-psm 0 {}'.format(
|
||||||
|
'' if get_tesseract_version() < '3.05' else '-',
|
||||||
|
config.strip()
|
||||||
|
).strip()
|
||||||
|
args = [image, 'osd', lang, config, nice]
|
||||||
|
|
||||||
|
if output_type == Output.DICT:
|
||||||
|
return osd_to_dict(run_and_get_output(*args))
|
||||||
|
elif output_type == Output.BYTES:
|
||||||
|
args.append(True)
|
||||||
|
|
||||||
|
return run_and_get_output(*args)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) == 2:
|
||||||
|
filename, lang = sys.argv[1], None
|
||||||
|
elif len(sys.argv) == 4 and sys.argv[1] == '-l':
|
||||||
|
filename, lang = sys.argv[3], sys.argv[2]
|
||||||
|
else:
|
||||||
|
sys.stderr.write('Usage: python pytesseract.py [-l lang] input_file\n')
|
||||||
|
exit(2)
|
||||||
|
|
||||||
|
try:
|
||||||
|
print(image_to_string(Image.open(filename), lang=lang))
|
||||||
|
except IOError:
|
||||||
|
sys.stderr.write('ERROR: Could not open file "%s"\n' % filename)
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
Loading…
Reference in New Issue
Block a user