PKXHa33edocuments/process.py# -*- coding: utf-8 -*- import os import re from tempfile import NamedTemporaryFile from subprocess import check_call from shutil import copyfile from PyQt5.QtCore import QObject, pyqtSignal import edocuments class Process(QObject): progress = pyqtSignal(int, str, str, dict) cancel = False def process( self, names, filename=None, destination_filename=None, in_extention=None, get_content=False): cmds = edocuments.config.get("cmds", {}) out_ext = in_extention original_filename = filename if destination_filename is None: destination_filename = filename for no, name in enumerate(names): cmd = cmds.get(name) if cmd is None: raise "Missing command '%s' in `cmds`" % name if isinstance(cmd, str): cmd = dict(cmd=cmd) if cmd.get('type') == 'rename': destination_filename = self._rename(cmd, destination_filename) else: if 'out_ext' in cmd: out_ext = cmd['out_ext'] inplace = cmd.get('inplace', False) cmd_cmd = cmd.get('cmd') if inplace: out_name = filename else: if out_ext is None: out_name = NamedTemporaryFile(mode='w+b').name else: out_name = NamedTemporaryFile( mode='w+b', suffix='.' + out_ext ).name params = {} if filename is not None: params["in"] = "'%s'" % filename.replace("'", "'\"'\"'") if not inplace: params["out"] = "'%s'" % out_name.replace("'", "'\"'\"'") try: cmd_cmd = cmd_cmd.format(**params) except: print("Error in {name}: {cmd}, with {params}".format( name=name, cmd=cmd_cmd, params=params)) raise if self.cancel is True: return None, None print("{name}: {cmd}".format(name=name, cmd=cmd_cmd)) self.progress.emit(no, name, cmd_cmd, cmd) check_call(cmd_cmd, shell=True) filename = out_name if get_content: content = None if os.path.exists(filename): with open(filename) as f: content = f.read() if original_filename is None or original_filename != filename: os.unlink(filename) return content, out_ext else: if original_filename is not None and original_filename != filename: os.unlink(original_filename) if out_ext is not None: destination_filename = "%s.%s" % (re.sub( r"\.[a-z0-9A-Z]{2,5}$", "", destination_filename ), out_ext) if filename != destination_filename: directory = os.path.dirname(destination_filename) if not os.path.exists(directory): os.makedirs(directory) copyfile(filename, destination_filename) os.unlink(filename) return destination_filename, out_ext def _rename(self, cmd, destination_filename): from_re = cmd.get('from') to_re = cmd.get('to') if cmd.get('format') in ['upper', 'lower']: def format_term(term): if cmd.get('format') == 'upper': return term.upper() else: return term.lower() to_re = lambda m: format_term(m.group(0)) return re.sub(from_re, to_re, destination_filename) def destination_filename(self, names, filename, extension=None): cmds = edocuments.config.get("cmds", {}) for name in names: cmd = cmds.get(name) if cmd is None: raise "Missing command '%s' in `cmds`" % name if isinstance(cmd, str): cmd = {} if cmd.get('type') == 'rename': filename = self._rename(cmd, filename) else: if 'out_ext' in cmd: extension = cmd['out_ext'] if extension is not None: filename = "%s.%s" % (re.sub( r"\.[a-z0-9A-Z]{2,5}$", "", filename ), extension) return filename, extension PK>HOedocuments/label_dialog.py# -*- coding: utf-8 -*- from PyQt5.QtWidgets import QDialog from PyQt5.QtGui import QPixmap from edocuments.ui.label_dialog import Ui_Dialog class Dialog(QDialog): def __init__(self): super().__init__() self.ui = Ui_Dialog() self.ui.setupUi(self) def set_image(self, image_filename): size = 800 pixmap = QPixmap(image_filename) if pixmap.width() > pixmap.height(): if pixmap.width() > size: pixmap = pixmap.scaledToWidth(size) else: if pixmap.height() > size: pixmap = pixmap.scaledToHeight(size) self.ui.label.setPixmap(pixmap) self.ui.label.setMask(pixmap.mask()) self.ui.label.show() PKMJH`edocuments/colorize.py# -*- coding: utf-8 -*- BLACK = 0 RED = 1 GREEN = 2 YELLOW = 3 BLUE = 4 MAGENTA = 5 CYAN = 6 WHITE = 7 def colorize(text, color): return "\x1b[01;3%im%s\x1b[0m" % (color, text) PKZHtF edocuments/index.py# -*- coding: utf-8 -*- import os from whoosh.index import create_in, open_dir, exists_in from whoosh.fields import Schema, ID, TEXT, STORED from whoosh.qparser import QueryParser from whoosh.query import Term from whoosh.scoring import BM25F from whoosh import writing import edocuments PATH = 'path_id' CONTENT = 'content' DATE = 'date' DIRECTORY = 'directory' MD5 = 'md5' class Index: def __init__(self): self.directory = os.path.join(edocuments.root_folder, '.index') self.dirty = False schema = Schema(**{ PATH: ID(stored=True, unique=True), CONTENT: TEXT(stored=True), DATE: STORED, DIRECTORY: STORED, MD5: TEXT(stored=True), }) self.parser_path = QueryParser("path_id", schema) self.parser_content = QueryParser("content", schema) if not exists_in(self.directory): os.makedirs(self.directory) self.index = create_in(self.directory, schema) else: self.index = open_dir(self.directory) if 'path' in self.index.schema.names(): with self.index.writer() as writer: writer.remove_field('path') if 'directory' not in self.index.schema.names(): with self.index.writer() as writer: writer.add_field('directory', STORED) if 'md5' not in self.index.schema.names(): with self.index.writer() as writer: writer.add_field('md5', TEXT(stored=True)) print( 'Field length:\npath: %i\ncontent: %i\nmd5: %i' % ( self.index.field_length("path_id"), self.index.field_length("content"), self.index.field_length("md5"), ) ) def get(self, filename): filename = edocuments.short_path(filename) with self.index.searcher() as searcher: results = searcher.search(Term("path_id", filename)) if len(results) == 0: return None assert(len(results) == 1) result = {} for field in self.index.schema.names(): result[filed] = results[0].get(filed) return result def add(self, filename, text, date, md5): filename = edocuments.short_path(filename) with self.index.writer() as writer: writer.update_document(**{ PATH: filename, CONTENT: text, DATE: date, DIRECTORY: False, }) def optimize(self): self.index.optimize() def clear(self): with self.index.writer() as writer: writer.mergetype = writing.CLEAR def search(self, text): with self.index.searcher(weighting=BM25F(B=0, K1=1.2)) as searcher: query = self.parser_content.parse(text) results = searcher.search( query, terms=True, limit=1000, ) return [{ 'path': r.get(PATH), 'content': r.get(CONTENT), 'directory': r.get(DIRECTORY), 'highlight': r.highlights( PATH if PATH in r.matched_terms() else CONTENT ), } for r in results] _index = None def index(): global _index if _index is None: _index = Index() return _index PKZHkEiedocuments/__init__.py# -*- coding: utf-8 -*- import os import sys import re import shutil import subprocess from pathlib import Path from yaml import load from argparse import ArgumentParser from bottle import mako_template from PyQt5.QtCore import QSettings from PyQt5.QtWidgets import QApplication from edocuments.main_widget import MainWindow CONFIG_FILENAME = "edocuments.yaml" if 'APPDATA' in os.environ: CONFIG_PATH = os.path.join(os.environ['APPDATA'], CONFIG_FILENAME) elif 'XDG_CONFIG_HOME' in os.environ: CONFIG_PATH = os.path.join(os.environ['XDG_CONFIG_HOME'], CONFIG_FILENAME) else: CONFIG_PATH = os.path.join(os.environ['HOME'], '.config', CONFIG_FILENAME) config = {} root_folder = None settings = None main_window = None def short_path(filename): global root_folder filename = str(filename) if filename[:len(root_folder)] == root_folder: return filename[len(root_folder):] return filename def long_path(filename): global root_folder if len(filename) == 0 or filename[0] != '/': return os.path.join(root_folder, filename) return filename def gui_main(): global config, root_folder, settings, main_window with open(CONFIG_PATH) as f: config = load(f.read()) root_folder = os.path.expanduser(config.get("root_folder")) if root_folder[-1] != '/': root_folder += '/' settings = QSettings("org", "edocuments") app = QApplication(sys.argv) main_window = MainWindow() if settings.value("geometry") is not None: main_window.restoreGeometry(settings.value("geometry")) if settings.value("state") is not None: main_window.restoreState(settings.value("state")) main_window.show() app.exec() settings.setValue("geometry", main_window.saveGeometry()) settings.setValue("state", main_window.saveState()) settings.sync() def cmd_main(): parser = ArgumentParser( description='eDocuments - a simple and productive personal documents ' 'library.', prog=sys.argv[0] ) parser.add_argument( '--install', action='store_true', help='Install the application icon, the required packages, ' 'and default config file', ) parser.add_argument( '--lang3', default='eng', metavar='LANG', help='the language used by the OCR', ) parser.add_argument( '--list-available-lang3', action='store_true', help='List the available language used by the OCR.', ) options = parser.parse_args() if options.list_available_lang3: if Path('/usr/bin/apt-cache').exists(): result = subprocess.check_output([ '/usr/bin/apt-cache', 'search', 'tesseract-ocr-']) result = str(result)[1:].strip("'") result = result.replace('\\n', '\n') result = re.sub( '\ntesseract-ocr-all - [^\n]* packages\n', '', result, flags=re.MULTILINE) result = re.sub(r'tesseract-ocr-', '', result) result = re.sub(r' - tesseract-ocr language files ', ' ', result) print(result) else: exit('Works only on Debian base OS') if options.install: if input( 'Create desktop and icon files (edocuments.desktop and ' 'edocuments.png in ~/.local/share/applications)?\n' ) in ['y', 'Y']: if not Path(os.path.expanduser( '~/.local/share/applications')).exists(): os.makedirs(os.path.expanduser('~/.local/share/applications')) ressource_dir = os.path.join(os.path.dirname( os.path.abspath(__file__)), 'ressources') shutil.copyfile( os.path.join(ressource_dir, 'edocuments.desktop'), os.path.expanduser( '~/.local/share/applications/edocuments.desktop') ) shutil.copyfile( os.path.join(ressource_dir, 'edocuments.png'), os.path.expanduser( '~/.local/share/applications/edocuments.png') ) if input( 'Create the basic configuration ' '(~/.config/edocuments.yaml)?\n' ) in ['y', 'Y']: config = mako_template( os.path.join(ressource_dir, 'config.yaml'), lang=options.lang3 ) with open( os.path.expanduser('~/.config/edocuments.yaml'), 'w' ) as file_open: file_open.write(config) if Path('/usr/bin/apt-get').exists(): installed_packages = [] for line in str(subprocess.check_output(['dpkg', '-l'])) \ .split(r'\n'): if line.find('ii ') == 0: installed_packages.append(re.split(r' +', line)[1]) packages = [p for p in [ 'python3-pyqt5', 'sane-utils', 'imagemagick', 'tesseract-ocr', 'tesseract-ocr-' + options.lang3, 'optipng', 'poppler-utils', 'odt2txt', 'docx2txt', ] if p not in installed_packages] print(packages) if len(packages) != 0: if input( 'Install the requires packages (%s)?\n' % ', '.join(packages) ) in ['y', 'Y']: subprocess.check_call([ 'sudo', 'apt-get', 'install', ] + packages) else: print( 'WARNING: the package installation works only on Debian ' 'base OS') PKZH*hhedocuments/backend.py# -*- coding: utf-8 -*- import sys import hashlib import traceback from pathlib import Path from threading import Lock from concurrent.futures import ThreadPoolExecutor, as_completed from PyQt5.QtCore import QObject, pyqtSignal import edocuments from edocuments.process import Process from edocuments.index import index, PATH, CONTENT, DATE, DIRECTORY, MD5 class Backend(QObject): update_library_progress = pyqtSignal(int, str, str) scan_end = pyqtSignal(str) scan_error = pyqtSignal(str) process = Process() lock = Lock() def do_scan(self, filename, cmds, postprocess): try: filename, extension = self.process.process( cmds, destination_filename=filename, ) except: traceback.print_exc() self.scan_error.emit(str(sys.exc_info()[1])) raise if filename is None: return self.scan_end.emit(filename) try: filename, extension = self.process.process( postprocess, filename=filename, in_extention=extension, ) conv = [ c for c in edocuments.config.get('to_txt') if c['extension'] == extension ] if len(conv) >= 1: conv = conv[0] cmds = conv.get("cmds") try: text, extension = self.process.process( cmds, filename=filename, get_content=True, ) index().add(filename, text) except: traceback.print_exc() self.scan_error.emit(str(sys.exc_info()[1])) raise except: traceback.print_exc() self.scan_error.emit(str(sys.exc_info()[1])) raise def do_update_library(self): docs_to_rm = [] docs_date = {} with index().index.reader() as reader: for num, doc in reader.iter_docs(): if \ doc[PATH] in docs_date or \ not Path(edocuments.long_path(doc[PATH])).exists() or \ doc[PATH] != edocuments.short_path(doc[PATH]): print("Delete document: " + doc[PATH]) docs_to_rm.append(num) else: docs_date[doc[PATH]] = (doc.get(DATE), doc.get(MD5)) self.update_library_progress.emit( 0, 'Adding the directories...', '') index_folder = '.index' for directory in Path(edocuments.root_folder).rglob('*'): dir_ = edocuments.short_path(directory) if \ dir_ not in docs_date and \ directory.is_dir() and \ directory != index_folder: ignore = False for ignore_pattern in edocuments.config.get('ignore', []): if directory.match(ignore_pattern): ignore = False break if not ignore: with index().index.writer() as writer: writer.update_document(**{ PATH: dir_, CONTENT: dir_, DATE: directory.stat().st_mtime, DIRECTORY: True, }) self.update_library_progress.emit( 0, 'Browsing the files (0)...', '') index_folder += '/' todo = [] for conv in edocuments.config.get('to_txt'): cmds = conv.get("cmds") for filename in Path(edocuments.root_folder).rglob( "*." + conv.get('extension')): ignore = False for ignore_pattern in edocuments.config.get('ignore', []): if directory.match(ignore_pattern): ignore = False break if not ignore and filename.exists() and str(filename).find(index_folder) != 0: current_date, md5 = docs_date.get(edocuments.short_path(filename), (None, None)) new_date = filename.stat().st_mtime new_md5 = hashlib.md5() with open(str(filename), "rb") as f: for chunk in iter(lambda: f.read(4096), b""): new_md5.update(chunk) if current_date is None or new_date > current_date: if current_date is not None and (md5 is None or md5 == new_md5.hexdigest()): doc = index().get(filename) index().add( filename, doc[CONTENT], max(new_date, current_date), new_md5.hexdigest() ) else: print("Add document: " + edocuments.short_path(filename)) todo.append((str(filename), cmds, new_date, new_md5.hexdigest())) self.update_library_progress.emit( 0, 'Browsing the files (%i)...' % len(todo), edocuments.short_path(filename)) self.nb = len(todo) self.nb_error = 0 self.no = 0 print('Removes %i old documents.' % len(docs_to_rm)) with index().index.writer() as writer: for num in docs_to_rm: writer.delete_document(num) self.update_library_progress.emit( 0, 'Parsing the files %i/%i.' % (self.no, self.nb), '', ) print('Process %i documents.' % len(todo)) with ThreadPoolExecutor( max_workers=edocuments.config.get('nb_process', 8) ) as executor: future_results = { executor.submit(self.to_txt, t): t for t in todo } for feature in as_completed(future_results): pass self.update_library_progress.emit( 0, 'Optimise the index...', '', ) index().optimize() if self.nb_error != 0: self.scan_error.emit("Finished with %i errors" % self.nb_error) else: self.update_library_progress.emit( 100, 'Finish', '', ) def to_txt(self, job): filename, cmds, date, md5 = job try: text, extension = Process().process( cmds, filename=str(filename), get_content=True, ) if text is None: text = '' self.lock.acquire() self.no += 1 self.update_library_progress.emit( self.no * 100 / self.nb, 'Parsing the files %i/%i.' % (self.no, self.nb), edocuments.short_path(filename), ) print("%i/%i" % (self.no, self.nb)) if text is False: print("Error with document: " + filename) self.nb_error += 1 else: index().add( filename, "%s\n%s" % (filename, text), date, md5 ) self.lock.release() except: traceback.print_exc() return filename, False def optimize_library(self): index().optimize() PKZHO*edocuments/main_widget.py# -*- coding: utf-8 -*- import os import re import pathlib from threading import Thread from subprocess import call from PyQt5.Qt import Qt from PyQt5.QtWidgets import QMainWindow, QFileDialog, \ QErrorMessage, QMessageBox, QProgressDialog, QListWidgetItem import edocuments from edocuments.backend import Backend from edocuments.index import index from edocuments.ui.main import Ui_MainWindow from edocuments.label_dialog import Dialog class MainWindow(QMainWindow): def __init__(self): super().__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) self.backend = Backend() self.ui.scan_comments.setText(edocuments.config.get("scan_comments")) default_index = 0 for s in edocuments.config.get("scans", []): if s.get("default") is True: default_index = self.ui.scan_type.count() self.ui.scan_type.addItem(s.get("name"), s) self.ui.scan_type.setCurrentIndex(default_index) self.ui.scan_browse.clicked.connect(self.scan_browse) self.ui.scan_to.returnPressed.connect(self.scan_start) self.ui.scan_start.clicked.connect(self.scan_start) self.ui.open.clicked.connect(self.open_selected) self.ui.open_folder.clicked.connect(self.open_folder) self.image_dialog = Dialog() self.backend.scan_end.connect(self.end_scan) self.backend.scan_error.connect(self.on_scan_error) self.backend.update_library_progress.connect( self.on_update_update_library_progress) self.ui.search_text.textChanged.connect(self.search) self.ui.search_result_list.itemSelectionChanged.connect( self.selection_change) self.ui.library_update.triggered.connect(self.update_library) self.ui.library_optimize.triggered.connect( self.backend.optimize_library) self.ui.library_reset.triggered.connect(self.reset_library) self.backend.process.progress.connect(self.on_progress) def open_selected(self): item = self.ui.search_result_list.currentItem() if item is not None: cmd = edocuments.config.get('open_cmd').split(' ') cmd.append(edocuments.long_path(item.result.get('path'))) call(cmd) def open_folder(self): item = self.ui.search_result_list.currentItem() if item is not None: cmd = edocuments.config.get('open_cmd').split(' ') cmd.append(os.path.dirname( edocuments.long_path(item.result.get('path')))) call(cmd) def selection_change(self): item = self.ui.search_result_list.currentItem() if item is not None: self.ui.search_result_text.document().setHtml( item.result.get('highlight')) else: self.ui.search_result_text.document().setHtml('') def reset_library(self): msg = QMessageBox(self) msg.setWindowTitle("Reset the library...") msg.setInformativeText("Are you sure to reset all you index?") msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) if msg.exec() == QMessageBox.Ok: index().clear() def search(self, text): model = self.ui.search_result_list.model() model.removeRows(0, model.rowCount()) raw_results = index().search(self.ui.search_text.text()) dirs = dict([ (r.get('path'), -1) for r in raw_results if r.get('directory') ]) results = {} for index_, result in enumerate(raw_results): path_ = result.get('path') dir_ = os.path.dirname(path_) if dir_ not in dirs: results[path_] = [result, float(index_) / len(raw_results)] else: dirs[dir_] += 1 for dir_, count in dirs.items(): if dir_ in results: results[dir_][1] += count results = sorted(results.values(), key=lambda x: -x[1]) for result, count in results: postfix = ' (%i)' % (count + 1) if result.get('directory') else '' item = QListWidgetItem( result['path'] + postfix, self.ui.search_result_list ) item.result = result def scan_browse(self, event): filename = QFileDialog.getSaveFileName( self, "Scan to", directory=self.filename() )[0] filename = re.sub(r"\.[a-z0-9A-Z]{2,5}$", "", filename) filename = edocuments.short_path(filename) self.ui.scan_to.setText(filename) def update_library(self): self.update_library_progress = QProgressDialog( "Scanning...", None, 0, 100, self) self.update_library_progress.setWindowTitle('Updating the library...') self.update_library_progress.setLabelText('Browsing the files...') self.update_library_progress.setWindowModality(Qt.WindowModal) self.update_library_progress.show() t = Thread(target=self.backend.do_update_library) t.start() def on_update_update_library_progress(self, pos, text, status): self.update_library_progress.setValue(pos) self.update_library_progress.setLabelText(text) self.statusBar().showMessage(status) def filename(self): return edocuments.long_path(self.ui.scan_to.text()) def scan_start(self, event=None): if pathlib.Path(self.filename()).is_dir(): err = QErrorMessage(self) err.setWindowTitle("eDocuments - Error") err.showMessage("The destination is a directory!") return destination, extension = self.backend.process.destination_filename( self.ui.scan_type.currentData().get("cmds"), self.filename() ) if pathlib.Path(destination).is_file(): msg = QMessageBox(self) msg.setWindowTitle("Scanning...") msg.setText("The destination file already exists") msg.setInformativeText("Do you want to overwrite it?") msg.setStandardButtons( QMessageBox.Ok | QMessageBox.Cancel | QMessageBox.Open) ret = msg.exec() if ret == QMessageBox.Ok: self._scan() elif ret == QMessageBox.Open: cmd = edocuments.config.get('open_cmd').split(' ') cmd.append(destination) call(cmd) else: self._scan() def _scan(self): cmds = self.ui.scan_type.currentData().get("cmds") self.progress = QProgressDialog( "Scanning...", "Cancel", 0, len(cmds), self) self.progress.setWindowTitle("Scanning...") self.progress.setWindowModality(Qt.WindowModal) self.progress.setLabelText('Scanning...') self.progress.show() t = Thread( target=self.backend.do_scan, args=[ self.filename(), self.ui.scan_type.currentData().get("cmds"), self.ui.scan_type.currentData().get("postprocess", []), ], ) t.start() def on_progress(self, no, name, cmd_cmd, cmd): if self.progress is not None: self.progress.setValue(no) self.progress.setLabelText(cmd.get('display', '')) if self.progress.wasCanceled() is True: print("Cancel") self.backend.process.cancel = True self.statusBar().showMessage(cmd_cmd) def end_scan(self, filename): self.progress.hide() self.image_dialog.set_image(filename) self.image_dialog.exec() self.ui.scan_to.setText(re.sub( ' ([0-9]{1,3})$', lambda m: ' ' + str(int(m.group(1)) + 1), self.ui.scan_to.text() )) self.statusBar().showMessage('') def on_scan_error(self, error): print('Error: %s' % error) err = QErrorMessage(self) err.setWindowTitle("eDocuments - scan error") err.showMessage(error) PK>H"..edocuments/ui/label_dialog.py# -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'edocuments/ui/label_dialog.ui' # # Created by: PyQt5 UI code generator 5.4.2 # # WARNING! All changes made in this file will be lost! from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Dialog(object): def setupUi(self, Dialog): Dialog.setObjectName("Dialog") Dialog.resize(194, 70) self.verticalLayout = QtWidgets.QVBoxLayout(Dialog) self.verticalLayout.setObjectName("verticalLayout") self.label = QtWidgets.QLabel(Dialog) self.label.setText("") self.label.setObjectName("label") self.verticalLayout.addWidget(self.label) self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Ok) self.buttonBox.setObjectName("buttonBox") self.verticalLayout.addWidget(self.buttonBox) self.retranslateUi(Dialog) self.buttonBox.accepted.connect(Dialog.accept) self.buttonBox.rejected.connect(Dialog.reject) QtCore.QMetaObject.connectSlotsByName(Dialog) def retranslateUi(self, Dialog): _translate = QtCore.QCoreApplication.translate Dialog.setWindowTitle(_translate("Dialog", "Dialog")) PKXH$aedocuments/ui/main.py# -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'edocuments/ui/main.ui' # # Created by: PyQt5 UI code generator 5.4.2 # # WARNING! All changes made in this file will be lost! from PyQt5 import QtCore, QtGui, QtWidgets class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(583, 525) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap("../../ressources/edocuments.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) MainWindow.setWindowIcon(icon) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget) self.verticalLayout.setObjectName("verticalLayout") self.tabWidget = QtWidgets.QTabWidget(self.centralwidget) self.tabWidget.setEnabled(True) self.tabWidget.setObjectName("tabWidget") self.search = QtWidgets.QWidget() self.search.setObjectName("search") self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.search) self.verticalLayout_4.setObjectName("verticalLayout_4") self.search_text = QtWidgets.QLineEdit(self.search) self.search_text.setObjectName("search_text") self.verticalLayout_4.addWidget(self.search_text) self.horizontalLayout_2 = QtWidgets.QHBoxLayout() self.horizontalLayout_2.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint) self.horizontalLayout_2.setObjectName("horizontalLayout_2") self.search_result_list = QtWidgets.QListWidget(self.search) self.search_result_list.setObjectName("search_result_list") self.horizontalLayout_2.addWidget(self.search_result_list) self.verticalLayout_5 = QtWidgets.QVBoxLayout() self.verticalLayout_5.setObjectName("verticalLayout_5") self.search_result_text = QtWidgets.QTextBrowser(self.search) self.search_result_text.setObjectName("search_result_text") self.verticalLayout_5.addWidget(self.search_result_text) self.horizontalWidget = QtWidgets.QWidget(self.search) self.horizontalWidget.setObjectName("horizontalWidget") self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalWidget) self.horizontalLayout.setObjectName("horizontalLayout") spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) self.horizontalLayout.addItem(spacerItem) self.open_folder = QtWidgets.QPushButton(self.horizontalWidget) self.open_folder.setObjectName("open_folder") self.horizontalLayout.addWidget(self.open_folder) self.open = QtWidgets.QPushButton(self.horizontalWidget) self.open.setObjectName("open") self.horizontalLayout.addWidget(self.open) self.verticalLayout_5.addWidget(self.horizontalWidget) self.horizontalLayout_2.addLayout(self.verticalLayout_5) self.horizontalLayout_2.setStretch(0, 1) self.verticalLayout_4.addLayout(self.horizontalLayout_2) self.tabWidget.addTab(self.search, "") self.scan = QtWidgets.QWidget() self.scan.setObjectName("scan") self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.scan) self.verticalLayout_2.setObjectName("verticalLayout_2") self.horizontalLayout_3 = QtWidgets.QHBoxLayout() self.horizontalLayout_3.setObjectName("horizontalLayout_3") self.label = QtWidgets.QLabel(self.scan) self.label.setObjectName("label") self.horizontalLayout_3.addWidget(self.label) self.scan_type = QtWidgets.QComboBox(self.scan) self.scan_type.setObjectName("scan_type") self.horizontalLayout_3.addWidget(self.scan_type) self.verticalLayout_2.addLayout(self.horizontalLayout_3) self.direct = QtWidgets.QGroupBox(self.scan) self.direct.setObjectName("direct") self.horizontalLayout_5 = QtWidgets.QHBoxLayout(self.direct) self.horizontalLayout_5.setObjectName("horizontalLayout_5") self.scan_to = QtWidgets.QLineEdit(self.direct) self.scan_to.setObjectName("scan_to") self.horizontalLayout_5.addWidget(self.scan_to) self.scan_browse = QtWidgets.QPushButton(self.direct) self.scan_browse.setObjectName("scan_browse") self.horizontalLayout_5.addWidget(self.scan_browse) self.scan_start = QtWidgets.QPushButton(self.direct) self.scan_start.setObjectName("scan_start") self.horizontalLayout_5.addWidget(self.scan_start) self.verticalLayout_2.addWidget(self.direct) self.groupBox = QtWidgets.QGroupBox(self.scan) self.groupBox.setObjectName("groupBox") self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.groupBox) self.verticalLayout_6.setObjectName("verticalLayout_6") self.scan_comments = QtWidgets.QLabel(self.groupBox) self.scan_comments.setText("") self.scan_comments.setObjectName("scan_comments") self.verticalLayout_6.addWidget(self.scan_comments) self.verticalLayout_2.addWidget(self.groupBox) spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) self.verticalLayout_2.addItem(spacerItem1) self.tabWidget.addTab(self.scan, "") self.verticalLayout.addWidget(self.tabWidget) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 583, 25)) self.menubar.setObjectName("menubar") self.menuUpdate_library = QtWidgets.QMenu(self.menubar) self.menuUpdate_library.setObjectName("menuUpdate_library") MainWindow.setMenuBar(self.menubar) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar) self.library_update = QtWidgets.QAction(MainWindow) self.library_update.setObjectName("library_update") self.library_reset = QtWidgets.QAction(MainWindow) self.library_reset.setObjectName("library_reset") self.library_optimize = QtWidgets.QAction(MainWindow) self.library_optimize.setObjectName("library_optimize") self.menuUpdate_library.addAction(self.library_update) self.menuUpdate_library.addAction(self.library_optimize) self.menuUpdate_library.addAction(self.library_reset) self.menubar.addAction(self.menuUpdate_library.menuAction()) self.retranslateUi(MainWindow) self.tabWidget.setCurrentIndex(0) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "eDocuments - a Simple and Productive Personal Documents Library")) self.open_folder.setText(_translate("MainWindow", "Open folder")) self.open.setText(_translate("MainWindow", "Open")) self.tabWidget.setTabText(self.tabWidget.indexOf(self.search), _translate("MainWindow", "Search")) self.label.setText(_translate("MainWindow", "Scan type")) self.direct.setTitle(_translate("MainWindow", "Scan")) self.scan_browse.setText(_translate("MainWindow", "Browse...")) self.scan_start.setText(_translate("MainWindow", "Scan")) self.groupBox.setTitle(_translate("MainWindow", "Comments")) self.tabWidget.setTabText(self.tabWidget.indexOf(self.scan), _translate("MainWindow", "Scan")) self.menuUpdate_library.setTitle(_translate("MainWindow", "&Library")) self.library_update.setText(_translate("MainWindow", "&Update")) self.library_reset.setText(_translate("MainWindow", "Reset")) self.library_optimize.setText(_translate("MainWindow", "Optimise")) PK=Hedocuments/ui/__init__.pyPK>HJ AA$edocuments/ressources/edocuments.svg image/svg+xml PKJ[HO  !edocuments/ressources/config.yaml#lang: root_folder: "~/Documents" nb_process: 8 ignore: - '*_fichiers/*' - '*_files/*' scan_comments: | Recomanded file names: * - - scans: - name: Color cmds: - scanc - crop - 2png - auto-rotate postprocess: - optipng - name: Black & White default: true cmds: - scan - crop - cleanup - 2png - auto-rotate postprocess: - optipng to_txt: - extension: png cmds: - ocr - extension: jpeg cmds: - ocr - extension: pdf cmds: - pdf2txt - extension: txt cmds: - cp - extension: csv cmds: - cp - extension: odt cmds: - odt2txt - extension: ods cmds: - ods2txt - extension: ods cmds: - ods2txt - extension: docx cmds: - docx2txt open_cmd: gnome-open task: - name: Optimise images on_ext: png cmds: - optipng - name: Fix files names cmds: - fixextensions_case - fixextensions_jpeg cmds: scan: display: Scanning... cmd: "scanimage --format tiff --resolution 300 --mode Gray --gamma 1 -l 0 -t 0 -x 216.069 -y 297.011 > {out}" out_ext: tiff scanc: display: Scanning... cmd: "scanimage --format tiff --resolution 300 --gamma 1 -l 0 -t 0 -x 216.069 -y 297.011 > {out}" out_ext: tiff crop: display: Cropping. cmd: "convert {in} -crop `convert {in} -crop 2502x3458+25+25 +repage -level 20%,80%,4 -virtual-pixel edge -blur 0x5 -fuzz 4% -trim -format '%[fx:w+50]x%[fx:h+50]+%[fx:page.x]+%[fx:page.y]' info:` +repage -normalize {out}" cleanup: display: Cleanup the piture. cmd: "convert {in} -background white +matte -fuzz 10% -fill white -level 10%,80%,1 +matte -format tiff {out}" 2png: display: Convert to PNG. cmd: "convert {in} -format png {out}" out_ext: png auto-rotate: display: Automatic rotate. cmd: "convert {in} -rotate `(tesseract -psm 0 -l ${lang} {in} text 2>&1 || echo 'Orientation in degrees 0') | grep 'Orientation in degrees' | awk '{{print $4}}'` {out}" ocr: display: Optical character recognition. cmd: "tesseract -l ${lang} {in} stdout > {out}" out_ext: txt pdf2txt: display: Convert PDF to text. cmd: "pdftotext {in} {out}" out_ext: txt odt2txt: display: Convert OpenDocument to text. cmd: "odt2txt {in} --output={out}" out_ext: txt ods2txt: display: Convert OpenDocument to text. cmd: "ods2txt {in} --output={out}" out_ext: txt docx2txt: display: Convert Docx to text. cmd: "docx2txt {in} {out}" out_ext: txt cp: display: Copy. cmd: "cp {in} {out}" optipng: display: Compress the picture. cmd: "optipng -o7 {in}" inplace: true fixextensions_case: display: Fix extension type case. type: remame from: '\.([a-zA-Z]+)$' format: lower fixextensions_jpeg: display: Fix the extensions (.jpg -> .jpeg). type: remame from: '\.jpg$' to: '.jpeg' PKՃ>HT\,,$edocuments/ressources/edocuments.pngPNG  IHDR\rfsBIT|d pHYs DtEXtSoftwarewww.inkscape.org< IDATx$u&=82άh@BHq@QɑIiF3&45蜽5flG+EiFFP!Djq]wUVUfFȨ|@vWx}Sԧ>O}Sԧ>O}Sԧ>O}Sԧ>O}Sԧ>O}Sԧ>O}Sԧ>O}Sԧ>O}Sԧ>O}Sԧ>O}Sԧ>O}Sԧ>O}Sԧ>O}Sԧ>O}Sԧ>O}Sԧ>O}Sԧ>O}Sԧ>O}z_3N3Ӎnn~G=I2g0Bb? A u!į63>)N60Y!QUzX3nnSv+7+ I`H sxDw35>ZzNH LH`(qݬ+;188Ohh$E}8P]"K˘e@*çٶ=MH} O;Je1x+@5 0:at0HMbXX1?S1W _̶࠾ O8 S hL0Z&ƣWj})_J>:ǎי0A /f3~aYPUHj`Uh7,婧{M osx EZ Ɵ槾O<X`HaZʬES44b4bC/WW_S{Ã͏ۋ9~21#~5F1 {LXh/qzw[T6ΑyƠOx4L0<`x8Fs0h*0ln;j@UJ Le# z) ,sn}xnstMk]e žUǧ 7=MzY&C` ^z* ŨOR92@})_h/||9R%}R^Cx݂_;Ì .@`7&YIT+`|f3^R#hzu'Y܏o[kFHXff-ĨHx|6@1@h4hcC 1$PI )_V;Z!KjwR<JZ?fWsf{;r]leS^_&|~q+yShfR4$F$Vv /0W20'3݀BihЬKȹ^|;W =(}mE)`n\lLuSly~Y!Cgom z'GfG@8,~>e_ ]Swo$u/1~67`J HwkMnċyL3hnu$:FFh%kdsU$*ưA |i]@(}i]tym6# ;t>5nLvfuF:#-ێGbd\⁥i$Òp9AS1va̳a/zGc-u N⻓+/#`2 2TÚ2@Q!JR~;]t7x.:_j TIv]ZhҀ7{*!쒚]ه߷{W+XvVpKz ʽ޴e`#8T >=^JC݃O#p203| }@mP@c5 %h$L wo5'*| oD|x礼~<)܀e)3 O[Ff:t>3ۮOuV:y #;f*cǢ\p`xvY]1A%!Ek.S 2sW^ݟv4z{xpJ1)xsbfhцoIYxg>@(}\ B{ek&S~#;i+jkwE%)^Yteҽݘo KнaSGT9ϬȻɹ{#pҜxmDH)" -obq*l Ũس .@89v^˾7c4#4k1E<Rwc|F|xY<5Ӯ_k7fb) q2,Wv\ @8m-9k8uqQh BS[֮fU^)_Ǔyӥ Wj߽XrjKs3J;t3wsPzdFAX$I@m6Os`_kK2 Hyne޶%eޖeLt>iwyDрC,AmP"2őd"k2B87+2mv6ȌDx>bt4PHjx 4xO! ?o:G=40 )"@8t]';7^yȫ/:~Ad?ZQʥV+:=x.B?|pۏNqω}U6۹<Ҙ4ff;V+)m%z&) JQ,( I Tl(Q.jmHL4J EIo4m NL92۶I7]'ݛo=yc/ >>u0|LJ\xo71hrDZ}weZwy4MNusl=R~-]9ӮMwz7c+(X7a 2(e,"5Р'h8R 5KRۦu;yGk(LQ‰vvdby #dhZƻVտ<~sWZ{ O`9&`xG,`qz srWCuf`  ̕{֓wy=>Oh tm?VJczS6Zu%;YGο+o EY^Fw2DQ˚H""לrD@DBp*Z,(Ad4BD-)k/B'C+NX$kYWa ֞)76q:Y*Z OWqLlv#GO|{svPVsK],-gX^ɐej k d @C[X~{d`_o8#Omx'"!DQ,cƱ$ȪOH6S!$GjRƒ+;\֗59L@7gKE}k z|jg9<ڝKxʠs: ?:S=Hw NGayvvZ4% N6؋lV~i97"!;.\n YQ)(DQłHjs]:7pHI=|WQ5b8b4"`*D&A Yp-~ {v$va|7>{ťwQ$qOccp o RXYZhE7S% ~.+F9rK/7bg_=.$c,(q xE_VFHRzߟ_[ +GdI-5+p9 '+Bb˘~[XxV)PY<ѱ-zchCZ1~maSuo]{0BZkށ2 `ٟa gwl!0L$5X}s/6i;/{%o|ŀ&F/ ntHZ֮ 3{5ʳ c%:ʽ:CI8~Bވ@0wj#5iR> Rg4TG7zOlm+lvUԍd-9(<>I} OcN m %;Y`_w|OZLJ~_M,]ՃP m3>`O#΋g  Fj)!߃&{x4F}^oZoakWcG汪:V(%@Ļklu:p?t_kLJ[*:ÓZx%O'o%k#B}x|0ms1z%ʧs;L~N_:3dI#p]`&0\# g{keַSpwIyu\ZQHy54nhˑb-lRpw5n4m^8;>6"X6!=\\NѬw͕>_126q졇a*e-w+%ddT"%L*@#!jC@T+g<<?:z̗Ϳ6z= ``غ `f+Z$lp{̫> #azw<7̋=C"1j#p'%q̥RJ muh6ҋaHbIJHw 2"'zU6 _rkQ\/^V7_W/Z')0@G#v(Q] L{14.=Lgfx~Y~gg?Zv11_Lj^z8w^ߓ$DDdv"E54 kC5PMSfsT[]g sci9󥿮J+m TH^^դz*A%dfuS1o*_ww k{5VǞ?Kq~}wgRSGP)=D&G2FD@,)a`JH@sognmfS]`8ߟ_7-#}&dJ^ehR!$.6iT 3 WgF0 `@=?W>\;ߕF5m!"ؔ^YnA;0q x(| H.H0X Xy3A~p"a`ts%sKu !`mٵ7ZsYy#e)[὿mu)W~˗ׯw7g6.Z??Td&~y>PAz{}FńȈ2|#[Qf^27+{+ik4&;gW/]1A /? m#ƻa}.z+3rBv2ȇh`~1LCbdR>xCʳr6d.3τ˯!28?E $.ՠLHǘUBSe^;gua)*y :% 8F~A)"…7^On4qHz֗|Yz#1#Y2BVgkPM`ж0`(6 Ŵ5j=zxA&}Z[p˃GmTJ D>{@ )θgA\c ffs(y'ν* ũ~ÿ2W0xCM6pHs%sfEzT8^]x,zзZtt'$A^$Ph `&aTWܟ}paZABs$° ҳ4QȰ9C^Ա1Em£my??_yz9|h^CmsZ*|yH<vɲ u=J@xg9\6[e~?UTWVQX?Rܔr krmzO+vhLXovn?|A9!oH` !b(çث̯Zpݔcsv"N ~.r+U| cslEeGL6 +Ԡwȇן{3ZRJ1>.`h)ϊs^$tp1 E=l\xMdyeԠۣlQm d`pq ˀ@>x>}f|p}+ΙR0|#zu5~jJ+<@00,|fY,!IBb 07Gt fꝷI 38<lݥ5xFx6 x. ` r}X #a٫!z쩻!SI8UXaTh % Fשm+iuBBSX<"]fv(±Q <$-[>ڀ<{/= 8Y ^R]KSy]B b/m>OKW2 ҝa٨hA,}I @T32?E3οvV&NPLr8WH f{Ӷ3N v ["UzHUV+gbRud ok>m\HI/4BL8(tj|]yYJz(Br|<E4s x:䘄ʺt̫%tC, H~&Y)xo?ֶ `Wrm$qF܀O_@lwg1 UJ[PNZZ⹥Njur¬N}4l}I{ZXuzQpU^uT3a$MI.[TyoІ+"~{Ϗ.󮶧p҃'w\ymݴKuޘ{ݞ=qT dw$cm;4*xm\i$$6C}\)HMV@ӕ -,,ujuӥ,]ii)kg(a`hy'TɖSfZqV {@ޛ(VM?o}Z&c? ‽ @-w@v bL;]YjP~_p%RMQR),5yCKo c ^) V!to|e)߳Ҫx p,/l;m-\+-/wr;*1~%2i x& L)|@"܊%m 0Z/7Dpcq}\bBR1WE @?{E] #7usoN޷VPxyaR/29P7cK6r3 pJHN3uSKr+1@w13kP(g+Pk);n'YֳL /_=uwZeŬWeyC~qw~޲J\0MU^޵DyxsBXs!U}0lY똆Q7+}-=x׽K)ަpi=d\JRd.r~inK3\` ],噁v;<R7]^S@%+!^[kPSWG1[Rc4pĝabJ\_ oer=MgJn7|B2JD p26x@n=q2w301f/_H25*~cߺZ￧ 5f.כ_\ "gmW#{Fd7F8_]ƌ+7O_xenbVcsٳV~z :b|:0~ffsVeXRGI26ƨZz J7[n78 !"fAX]l3V TvT^u|^q>'1EH 市pfs.$*r Hee.&)f\AFlf'*fd]1BlĖx<Kuqk.̚3,d LK˃oۚZoX>p0 ArbK)#IHRƉ .7zҥkBZ{q2-RR/= wJjJ2ZuulگՈ.p|c 0'ajHN?5OA>tۭnO̘r!MKy~ qoAYwٕb8Մ=`^U.jxa QW4`0dv\xj@0pz DBRJX( !A?~hЎATQgn2B.Vos` 9i݅Ez =Ejfқ5B-[%pwfb aa~L¼s.VwU')galYɭX Xl,U N: dCcϽuzW zW*Hx!zXhDZB 1@8"3+GRdpщ"Ah]bYLGÍF Y^Fh 8(LKrjN վX2WL(&g!)vN#O Ͽ0uQ[+uu@"ҽ0P"蒘s-B&kџI$O/6?uE AhwC02>^=7ܪHUJ*V0*>>Ҟ|\*;4\Qߝf`UC=u kp0Bq={Lzͤ"Ujz`ۗeJX*SUXOoCӏI&`VR99<-͜J cH!,@qh:R9۩>L2RZ7csӓ@ 14 `x4 y)NG%y ,Hw+Yw*-ьihdԩOY!]cj.A>A+ș`p XZ$ ޯLz^lr s'NO .1nZo U׏jB 2Q ΁++(ģQՔc3y+s ݤv@ $3y/\*҂K11VUB;]T;VI#> WJ27 r^;`-ܨnzAЎx6<q'AW0YL@u3_Zp=>WրWd]-``u]B;.+(^l*tSpjQmV;IXJsݣ l\l%Ū'@,KsB3v(J6$tt0WCD^/-,Ͼ<60V~{Z^v"({HD3 6Yffp z7i ؜Z8驿bVxUȀx?2g9sKɂۘKgcn;Qo@ZYz@=@ġTpbAh:]y=WuވzFr170?Lg詹~1{(I 2Ri h% XkUWscNf'iGg'//|Re*N,Z7>>~39'!^T^x`+j+ɜu:d5 p,߻]ܓ'*Mxa89^8 + =aDEQ[f,Vi h5 ƻj=Pz2r%MuK qR{e oOptrN$, !i7cF7}.Y \zs{%PW+>^EU93V[`Sye~!n "rY^G\/c#U])%M=`ЎT1Z_y933 3B3ABAnk+23Y=VbddC],Y'VଛJBn;XWQo+Az>VO!<27ʇWN1 79"'_u5aJSB!h v 1e֣c~frv! J虄Y m.ϼ\ J6B`|t;D;RisoR*efV,Q䄍w3 +1ŒR815=m$n6 KzvV: ^q#ūҭuRUioN>)|u 5"ޣV3=+M~l{rry7 &;+d~f>u;ՍAr{2B̧t*;X lu"(ekPZm 5 f~f*Ÿ8kv[h#퉩 d1C/P~SXQ r_L f;:b.*aNCk'd[ `gh8+i?rMaJҟXo\ɥR W=n*ol~ ^XO_v%//.7G'TIֈq/*֖ʿ [ޭ@)R% * դpnBLEC#tp{ +SSm gO3SH]e\꛲ws,~))!$ DP讗>Mq+֦wRp2 'ĒVBT& Hg<:E IDAT L2k:TeXRB `nz̾^YO7-%\6=eU}˿)Zma(.2z3:FB 4xnk$op.]mv_z|zܔ0we6~L9 #+:XeZ3l}޶&v@5Z)Fn-V tT/tU0Sy1Rzx@AL&DBjA"Gy!>kYps3Nafv#+m `=44hUΎ@,nڠG]yh9+*; ope o+ 7~&9 N0}"]E*O^OzOC-C {~}Cw7E4a!1j]LO:㍬ d @FȻ ebeAO>﹮]/Gɓ46DaGAybVHN@3l\坭,g=`$>[#)45 ._tbp1h\Hsʻ{^- Ȗ-& kC]~ ;]n{ahf\]Zږ@l'{ ||4^׺^ĆIt s͢ ` ?';I` V‘<ˍS^?N7*I_& h Hh=,KWʟ ʰ[P2+$o_f. 0d*FM[M}Ͻ\>y.|mi{e϶f-أt6"LPg TyrwNją_;tAɵ=nR  ׭av15o-- '\~RΕ B#2, ~d&xh%{̼!Іܭ`ZX'XZjgf0};FpX_QRkדëK֣;qv xCRY*1@{VϨ9^"@ވ}zx6:=J1í|bX opCxoh\q1пV.W_~d=21;!YSTepP߾T9 б<6l7] Xo}lbZ 1$ +3@z+0Vb`6`h>Ve=H҆D׏PJ92s/ԁVo$W\.GXTzH_1ڕ OAydd^WFP8</iknU}l7? *ؐ3g[_㥤]4<&! 7IfÏRnz^;4{/& W4061(ciꚿzӗ.'z#  rK?}`("X2 {A@W\#W^IU(8[ɥw/`zj?]X7v7RfMKs&}=ڴ ذŋ |6~,NI ܩD"r>38ω\;'$ÙJa x(@TUTwU T[wϭdO_0%S>xHS4ɋ+0a"=4 7b&C`C0xc!L[12ڨS-Y  00;@Ve,uS}Af:MKn[DhNtf#W\uyp~Qj=584|/4 Z m!cNV=r?<0m oU~=߿eDg76CC`-mĔgy$bB !8uo3mK@Y(HR}BC3ns^R٭5\FCGھF19¹< 6Sכz{*~RAEE9Vp1JM YF}Oc\E7٭-91e.0tv7pRrިu蚆>zy-K9lsaXW+U8I[qʊٗh:$3ү%~-eۦ\8, $|%v+'FE u)HVa"Iz- 8̬:X[o9S9ݗ+oByϛt+i|WR5w7ۨ @ON ./+_*w}"? @m`c?y-pw[zA'r&7CڴdN@Ê%_ۋ90V %:5뙘I)ct@1%5fjUeLzBؗ025(Ri@+#0{l@/Y$(_zЄ0\)e-Ǒ?kDarȶHsKG99pUTT1 Tգ+{5R/.,\LU1~©JxQh;_8yΪw ].@m(N!1ab%ynD8BHc0:uMh UÏ?|?V/(/_?cL?7% 8G!?0ۖF/.Q * ;pcʗN)a0+~:^>E5x3/-*0jW*<6W`(lu_Jz8Jēi,=k $ZZ`ؖױ[TxK=|OQ@o1c;9-&P6s(GTSr=J+.zuf03\džm[K^ǞϦ]% ;P xE, 2;;W*(? O S`N}&0 $ęJv~"f*IUw4b%;xl੏}2@oNSչkd?>o 5s/?$o5?"݆R6DůKg^yW*QŚG:p?zY{x|s:i@Im^ #$$&("pfD#>4Ls kb\{m50d r^3)fqϜϼnj_cCd#'Q`__b;Q;}I}z̍tE.:;;hժU޹sgh4ȫׄ ?5su@a$NlW*J4>~92 B_ȝdr؇ 5!b 4oh&<񙩨ɺCEqh:E"w|==Eh4'wRߏgڎm%+5 Hh8ǠkQi2,$+F{={ /6kml,as&_: .`/z JI"N^DOgv!r}6J-`#Icx򱧒5~A! a1N-(!?p[Dj%I٪(fQqW B< _xt"y @g?mo{#)Jp,]o0HM6K <䴳pAPبn&9=DZzRZ is:,>k6p5j*/-},5Hh@اqƟA޽_L"I+͟%yPEC aet8R}i1Q ?>!d7;`HTv/E3?by|Auz""r|;Vbv)HO?P~v bmVVX1Q32 `hp¼S_dkhIRqn<.`7tcO/rd/]\j?W}><9Z%ۙJ10JC-țODث>7h P%%a;JI` \g˥!*pf|keG\/Kv/LjtNmi]"nmKs:IAA^۳]XqۚLKi̡}6i^3z{zaVGv)F ,^m$rH9ܱv *>ŹR*(\DQ+ #Q`Y2& K_2~w_sCwe ؎ضH6}]R]u[\W.I[.WJ}W/_Nb&3_ 3>3Ò)dB?-!Q`v@_/T؞&Jq/.Wp8&f}m Ak+I#=^>N9џcs Km* C02 ~iIT)8{j cH_! "Jdo~Po?˗d *jE ֐u~R~RJ6^e&Wsi+e&;bi fo*(c|i0 ~o f4d*?N -'kvis&?ŵo\wBGOیP&$-x{{94ǹ>hk{G%'](m UC|䩑c'!#0 >8|?"K!REK@KBE(*hLLBZbSWJ8&o5w_q[ly4d=[韸^i~?|"p(=w^3:-%1 XqDވ#~y* dC_=#.\tJ:Z`Je= kݼϺCs__Pέ ?Ss_Z6],U'RuzC_6kia( ob=̰˖5%@!nVk^g -(nHK,:`1>YbQT˓:ӕ7>&nI awܭ-D $‘>76S#3:u bpxl/ .qru_C'-1{OXuǶШ0朋pu{;w\vB JzwlY3s [,rFI03#:$@U3ED[}jG*㓕5Q@`µnK'QcU@ R * ؗ9&AaXlA:*^o  M8X7,ζ0W46,1o[BJ6v!cXd3GK?;Ôz wZ9) QB oEx7ܟש~|s'O~- )>t`zzڟ!E]^t]2,_(T~wn^u:2Zn8 Ij/֡e/ 3ؖGN}O?1 q F$u7cMoخ} ߾V/F3xK'b !;^u;4}?$;C#ʩ*i=w/F|-BH$o{ IDATq(Z<;N?ָoTT̙3+++ &{ܔa-,I@[YleePP`w27)R*o܇ Ozՠ@|gW/݇K'N;OKE1)Q7O(5y h,]4` E|73T 7balj;Q*7Յy\ZXÇp#hnnQA:ͷnLyDiF&)-vF;2J-]}96sVAQ܀nAUǭ/9\.3 61hR@0)zX E:Dy;_E`D^'h6#N]V+Ȼ\ز}8UtT2{Po.!ʗFNn,8@v?a BDQA7`t.Tp%8 wh"=&*V/.^DHn|֫5wTiQRYIXU,ėVٟdjiuY# $#V-2Wgo[UV6)@zBˁb;Iv 77 $97K%OZ-7ko6P;MYOm#O3:Q5Hc'>s^9W5hd &H!TN"P l'w=ɼ\idnJNk?Kbwx":{#aҲМ a(Ј#kl΁|LJYJ%^D ,ntz 2Y$:DKKKY%ZuQTV(l!J)#Lu3 $فsa7F_~I) yYEqBta۴)QeYTɶmRJ(jMLL "MOOjQ?@KKKwN<@%~ܿ?͉qKKKdvh^Oo6a(6 _ׅ)أ(y9YIQ;|/ VB~dv]dpz  90ɃtGھXN@/hbL<W,Vj5pKN'/%ҙ[$"@xeIض8, )۳Gv֍kC(U~n>\[eptظX}Wbsgןɀ;dx/BȷKNIOw)+ǯ"k2{GD_JI5JP’e Br\.߿n W hvvQ"Ç/0Kmsj(<#4b@aL{DM5].Ȑa4cK g`&J'?ee>#}Z=~|^Rx .Bv?6Nu؍MRrAڟmھtkcxr:m֖$~L5=Oy@<)+yg m@+avzsudc峇 _wٷL<3nn}ӴtD(n ԣk(xuy뎾u]x}^ՐwZ]\A݅|@j tw\.[|$n(77֚YH@,l)M PQB*O Y22H@h\\pjN3%!c rԭ;HWX5qky]S+x4jKAIQ6󍥅Kg,5W/lF]LOn /"t0H(-IJ ~ѣ7ƳRBFJ WH}>@?w]75|q[nM k'$5zTH9%`ѢT^zg0Z+V}[;n,`Es/g\M\fuuΏK跨$ʹ:Μ]?X;]SJ)sWr2U2&X.<7|7L.@w i&Bwg.~ݶo8z4  {2AnZ^~71aad~ o]˫K[QJ* 黃?13.? d,-nWaHK {\h2l6Ewdr%*m &ʎ]tK?Z_^cMt*EgKgN֗_tEx\Q~衇 hvvv8b_-GQDR);vԬLv*R$`PaH,@Ti([Bh a$mb{*^c] *8T#!zB+D4=-jg_[@ M3峿jT V &iMfak.+6h_m2 *+)[ xJ1z}7U^kzY;+Q*my g*tᮝO[ye_zUj_6/}cc<\8{l@/G1RH$[~r4WJ%AxF$кYw7=} &\U@8(XfJ%O=<]ym 2w= C2A/@y"!$$P*bo5xAY~n wmK!`1@o'RpShEg6.xvؙ|eqci7(3Cb>r8ls<&?)َ ter#1l7PZy:yr/"clk3/=پ}ǿ>{VoJ z?َHr].{GR]EVOlT* 3[-IPbee011nD`u>k{  bm6mNA!&(JCenv ZZ\~[xZ]<0}bqt@<,+ݷp6 q۔9=;!U f?uxE'&l_u@ 墺 r ,[)YY筂ɠW۩OMY q g)$Dm}牠J"Ð z4:T*]u[>'ڞRQ9`Atޔ֦WK%W +_F?"VuVrԼܩ>}nGV(SXvDUتy8:=?d' ^V9:#ێaX]0~+kws8"\6N'%$" ݱ4zY]W}l } J"h6!T*X__GZjJ###HF tauz } |e洽#J);GJȺW:$(i'D.}~GxKѵS[,/7f$<̣PWR " RuAW|# D#ʹ%02qb Z@@_? vi ¶ܒL@_HFssgm`Du]l@RE4$h6(\.$m @5,,,r 9mo2mS_BuGo[JQ4r`ypb=/}f;bi5y;#ǿ9pgV""3$2&x|>oCҶ8N# <1 @dHЖ^{EGQZ^;E~_"׊"C{w  8 f Zظ yT}5b"0F\FGGVϫT*C:LLLYZ?1C2~vh}۶S6DO"v'hͺeP |Rdi!7s>{<{GWso@5k&ͧRB&=멊 ײ`-1ci:DHbj"+ d4 @?@Pe"ݎ5dۑ55H8Y Ǝ}lX2eY'}ضMYk@ -EhZ2iޛHܱmZ0}dAGJT)x=V0Y֭}ғB *I >htfcǾv߱O>po6MvZRFRԨ]lK' tM {qaM >UDD$]_ EHȀT~D!8~ur~{x^B;gl@б0I4I\;`X$#7wXgv72d !mٶ͞%ϣ .AVWWiffAoF3?:OSlG®v^aE##WW xf*:e'j3|gɹ f{B^m/d$0Xq$8<33J-jGI3Q]) BL$+=\ď"frE)iCvo\7Tn< 0s-G@+'` vOOΟZخgrրg"q]p]JZ7;$`X,+<(yb@XJ, -D /8pYݒȸ|H p~ -B,6@qI$# H~[]=ʯ,c{hkoym z >`9H$Tv= #wXcQ`k$!ݥ$jT*uru}JXXXjʩ &;N4wwzBF>@fj{p& _;& e$b_Ph5V.:v#_:vO-DqX/($  %0D!a,D# 9H?! ""RRJYkNC|#:9`&'E|FLDxqYn|^GH U9?Ј@ !# w ]oܷp|M/bA Y1uYW2N%N 9== dqA0/EG]cL_^]hK"IrHE^3ݲ;JCӓ|)jΜ?GNnV3Ŝ6U,Z^iy!Md+,Xpqua)u壖EtmL=b5. [$-6?$00H㌶ZfriXNJV-naa{sC> p|:K|Mӈ=#2<@<ϰ;@G-n(2=LY@{&DJUО{DQ,p[ &W-tЩ?>[/|u[D`,L{JRr')%ҶxV&tţ$vaʳG}Q;Ev1 9-铉 n{ YxGa_s_Z|Q\kZ#ͯ7Uy7+ve+0 f 0kT@E_dklIEۥ={F5mZr',j<|_g7%> %Sbcˉ@65 1)>0zOO@6QĜg/Y" M!M,bfrN>#ZZkO G3@ojxf~6H8\$eS) ABOgr9 zA-N$`wZW*ǮHаBtׂA _X):t_@/.7_ 74 7# $ dnd ]5Ј[藶_" ȷ%K"rnf%duHϫ/DK j G?gOET ydl@ $# `[l_SAK_( .Ӕ0+ Ā?(adx!RtSgi7DE2sj D]/?L?<.,.gVV x@(S5鑽ĽkGvڿ,)?0wBy\ m)'vV%sBH+H|&OX ZΟ>GND7l!z;cGQ@$o>;J Sbw# ^뺛R#Oa8rIT%>)M8mGpaєھHI j$J z%qmϏLo :Uѕb釛3t@JQEmX!?j!+'ϲ2}lcѱu\qbr+qmL #0HvȃaO-`/ ~!׎?ykt#D%S dx~BPhٶVcB{yIDATSǎ>~#ܷKWϱ,+~k+IJ,( l"b,7 8wF8M^e636mmEAA_ڲ9M2@ Dٴ>BȪhlk6SJq"Zg'[򖍟GpM[ ۙBJ@pxUGߢLI h_qFvQE2`G0 )mAHu1L$e]Ń^;m1΢/-AF%@KC-Fm^h,/:u|+UlĠ.!f8#˲ұ(6NUd~^f{d2Đ́hkj8(C$1} ",DV/mllLZ,u` c^&nޞO }5ޓru(?F(T$ߙ>Zk-qX'ض}l:]VEv+jQP*%qʷH`>e][o`t2Vj@=I/m&QՖ-9}W?sdטA0E-,` /&K5bN&,!4tYpx;u-Y%kyN7MkH)ԅ`6CEQ2۱Ac#[{6'D&y:\eDh!YfAZv(dH N뺌v]A^E\= Zw% H 20 Y_=yv f HsK=eRJܣF] oO2{,s54>P |)ecǎ9f_^^l6B=!JO>RzK7JkԜ B_ @}wHd1gc.<"EC/~+ͅF̵@N>&hyUj 3@Ev0gYf#ɩ?nxX!4BwVGǘ?Q8,G,  G={_8v&mAI63qC)Fz+%QxHL} |Hg[70L"Ϧ; Snjs b/(]'~ܻV4DvܹZVM=5:%fC8w_JPߙ%:50@.A0^YƯFy{Ǘ3g2e L!#A  '"0$a 4ՀF ֍mq_Dٲj9m2Zh__;zǟ?}+q/ p833)d63S! M@vh<)-M]~%#$!F7hoE}6fjΝ'&&/^8# jHf/qG O8:Gk·ɘޛN*bq㻀0-(D\P8o@ Z 1ʙVMٲ,I /i|yk DQ v360DP<7 GCk6PnZbe ve ^%z|C'|f%TݴωͿ#"xr,}Hl" !),>9@<|Rݻs.\n4Sɒ9Dai(ts Tߝ.?D`5 6L, d tj 4_x5 '4dYA@&dx \9.T%+hbpyH-ZE5p-\;v쁕O8@:R ;S*z  Bs 4]F# 7Y镒-`_dn$V@OSxtm,L-5@__.믿kڔR6&$_EJқG_no|/}2G G:F>ab+!ږP.=/Z9"RAZF~s-Asՠ y|ׅ`gL}2ُw#2AB&L@Bp8qǯ=۶NZyᵹ?yR6;L련wp2=:C z٘dbj55 eGKq@!$+ Em}&`˲}}ӧj5,B'm>{cTHɕ anP"Bcms0)j_YYauuQX  l"PGMk ( )HΔm#=j|p7}v~zW?fBN&2~?B0vu[|LwC:z?}ƳV~w.j˃ױA ?ܡGwזw1hɵ`,XwLk|M:u kaikS.`Yo'ܗRr5~R7hz0|`4R`A*p|?[:/)8Eg1]5{wkE nqMRԷ!E ~clEm xZ =qر=J)לmYۣ  _MieB?%-^ȴ v}p( {> 3 }sЎՁ/Z8yh `hNvV6y2(,lz|^^Đ?/lnmE2hAȠ[ˡ'}mދڤIn-ѧ~zzyyyOX\]Ae_/\Qv`?3/UF2כ~)`I*ou-/>;;;<99zNE˄Y `˄ n˄|A\.!;6( j+k~'/WB&@7ỹ^{H涠>sss{7Bn╾;=k?R Zၯ:e`(yKcm[ \SY<)F?g,({ڀF2q KU\w,6.du`i!I{ז߾R"֧[K$ۋ$!^֖jI<)ymgϞ<]U)F\O?`9qU ʭQn_pl'/J1Lm猶O[`NOOc}}t+%w tB1Y1[滹XBay ~ι\nzB?E ~Ayh}3ǎ]l6'@:ru{`3Z15Bq1^[R|uP@3wuw+++433UN@<21EGAn1tk{nmݏ.W@n /jg@hn͹ [٦5|7L~zСC3Fc L\>>y-~.p7(`I8Q*RT<7p3?<4 z`ھH{hbb4K%ʌ$t%^E f[m+{DGzJ t;YAncnc) z?mw֕xuc'nd5o}k#qa*nIIwm z`k#?@LOOSrqڿ?SBV)@RI:Cf2ȒC7R:݇㹶CzN7p'Qߍ,]gǯ"nL@;n7R赭 d'#YJ-E^t88O=O̬Altmsݑ[{xWGmtt4z x`7fgg;ġCȒl:u1;)}ȴC zW~dѳ=G tF}RTaR՞  H*]a0mp{`@"vzB K?;duז##f]wE~PHy OF !Z /~rگj7߭߱C`x?}Lj @w.`/J=v~vdE#aZZQ~ypezH)*RD~Sx60!`~Le.Bh[ f{u ŖCz I䭈|ߎsrRW_[\m?h@h6%f@]tN&7EDҡiv?{fff<ڵL1e'.Y|3u0Y-a={f/cȭˠf Bs&`'rȴ (<I"w~2Ow"L ~~&\ow:J4&&&xee({}Y7%Tqf,鯯{ꭾS kjfc%3TR:-71-Œ/ mcpHx>IlW&l z@@'7lcE o6<11X7OLD~5Zmg@(ԙRRQ"Ɨs/=]M_s`R:-Z: @pn/"`0@iEtat;ౌ uEႮYf%܃Ho~m;S{^7m3CY0xCl +?i]=L'JvY%DAAB>~!waxyqwMMfz=ttdv $}~nhTUwFUz \IkF8csU;S-lSV|Ƹoo.ʊYi{AW=Kԗ疖 mn+n^~i*G$R$:u{X"/ <8 ى(z'IZM< hM >\2#fza\f_n~gqjr76  "oy^6!($Q^3`e &xz *RњŽǥvaJ5ٳGh}]cJO !4(9:Gâ)7Ƃvh{ 8 o[Fᠷ淀f-:[IUzuEoD{AREʽQ{} .|۳8j1!y{O86&cRHwg0_.4_ 13 r& b[ujNTj ^ 08jhЉ>ݭ%YGۉ }dSB$L>b3˶럓IzЉZC/b5@2ybFR.bL(ؑ|ȎF򠉊qj1X_ޏxKYNn"G&bT*0^*SSY~*NӉ&'7CNͦ Q ƳA3@.]wNuލ@w++^_w|kװ{+vmcpF~l (Dh h(_L2XjIENDB`PKyw?H'I""(edocuments/ressources/edocuments.desktop#!/usr/bin/env xdg-open [Desktop Entry] Version=1.0 Name=eDocuments Comment=a simple and productive personal documents library Exec=/home/sbrunner/.py3/bin/edocuments-gui #Terminal=false #X-MultipleArgs=false Type=Application Icon=edocuments Categories=Office;Utility; #StartupNotify=true PKlkMHJe00&edocuments/ressources/.config.yaml.swpb0VIM 7.42/VЌ sbrunnerstephane-Why-W240EU~sbrunner/workspace/edocuments/ressources/config.yamlutf-8 3210#"! Utpradrhg`PF:/$}l^]UB8. ~ n d V < 2  \ F E :  w ^ y x f C X   tWVI"u_^F to: '.jpeg' from: '\.jpg$' type: remame display: Fix the extensions (.jpg -> .jpeg). fixextensions_jpeg: format: lower from: '\.([a-zA-Z]+)$' type: remame display: Fix extension type case. fixextensions_case: inplace: true cmd: "optipng -o7 {in}" display: Compress the picture. optipng: cmd: "cp {in} {out}" display: Copy. cp: out_ext: txt cmd: "pdftotext {in} {out}" display: Optical character recognition. pdf2txt: out_ext: txt cmd: "tesseract -l ${lang} {in} stdout > {out}" display: Optical character recognition. ocr: cmd: "convert {in} -rotate `(tesseract -psm 0 -l ${lang} {in} text 2>&1 || echo 'Orientation in degrees 0') | grep 'Orientation in degrees' | awk '{{print $4}}'` {out}" display: Automatic rotate. auto-rotate: out_ext: png cmd: "convert {in} -format png {out}" display: Convert to PNG. 2png: cmd: "convert {in} -background white +matte -fuzz 10% -fill white -level 10%,80%,1 +matte -format tiff {out}" display: Cleanup the piture. cleanup: cmd: "convert {in} -crop `convert {in} -crop 2502x3458+25+25 +repage -level 20%,80%,4 -virtual-pixel edge -blur 0x5 -fuzz 4% -trim -format '%[fx:w+50]x%[fx:h+50]+%[fx:page.x]+%[fx:page.y]' info:` +repage -normalize {out}" display: Cropping. crop: out_ext: tiff cmd: "scanimage --format tiff --resolution 300 --gamma 1 -l 0 -t 0 -x 216.069 -y 297.011 > {out}" display: Scanning... scanc: out_ext: tiff cmd: "scanimage --format tiff --resolution 300 --mode Gray --gamma 1 -l 0 -t 0 -x 216.069 -y 297.011 > {out}" display: Scanning... scan:cmds: - fixextensions_jpeg - fixextensions_case cmds: - name: Fix files names - optipng cmds: on_ext: png - name: Optimise imagestask:open_cmd: gnome-open - cp cmds: - extension: txt - pdf2txt cmds: - extension: pdf - ocr cmds: - extension: jpeg - ocr cmds: - extension: pngto_txt: - optipng postprocess: - auto-rotate - 2png - cleanup - crop - scan cmds: default: true - name: Black & White - optipng postprocess: - auto-rotate - 2png - crop - scanc cmds: - name: Colorscans: * - - Recomanded file names:scan_comments: |nb_process: 8save_interval: 60root_folder: "~/Documents"#lang:PKlA\H_1aa*edocuments-0.9.0.dist-info/DESCRIPTION.rstA sample and productive personal documents library Scan your documents: * Auto rotate * index them on the file name and on the content (OCR) Mange your pdf: * index them on the file name and on the content Search in your library: * Build the index * Quick search using the index `Sources `_ PKlA\HY?]]+edocuments-0.9.0.dist-info/entry_points.txt[console_scripts] edocuments-cmd = edocuments:cmd_main edocuments-gui = edocuments:gui_main PKlA\HF{(edocuments-0.9.0.dist-info/metadata.json{"classifiers": ["Programming Language :: Python :: 3"], "extensions": {"python.commands": {"wrap_console": {"edocuments-cmd": "edocuments:cmd_main", "edocuments-gui": "edocuments:gui_main"}}, "python.details": {"contacts": [{"email": "stephane.brunner@gmail.com", "name": "St\u00e9phane Brunner", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/sbrunner/edocuments/"}}, "python.exports": {"console_scripts": {"edocuments-cmd": "edocuments:cmd_main", "edocuments-gui": "edocuments:gui_main"}}}, "extras": [], "generator": "bdist_wheel (0.26.0)", "keywords": ["simple", "productive", "personal", "documents", "library", "scan", "index", "search"], "metadata_version": "2.0", "name": "edocuments", "run_requires": [{"requires": ["Mako", "PyYAML", "Whoosh", "bottle"]}], "summary": "eDocuments - a simple and productive personal documents library", "test_requires": [{"requires": ["Mako", "PyYAML", "Whoosh", "bottle"]}], "version": "0.9.0"}PKlA\H^x (edocuments-0.9.0.dist-info/top_level.txtedocuments PKlA\H}\\ edocuments-0.9.0.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.26.0) Root-Is-Purelib: true Tag: py3-none-any PKlA\HEII#edocuments-0.9.0.dist-info/METADATAMetadata-Version: 2.0 Name: edocuments Version: 0.9.0 Summary: eDocuments - a simple and productive personal documents library Home-page: https://github.com/sbrunner/edocuments/ Author: Stéphane Brunner Author-email: stephane.brunner@gmail.com License: UNKNOWN Keywords: simple productive personal documents library scan index search Platform: UNKNOWN Classifier: Programming Language :: Python :: 3 Requires-Dist: Mako Requires-Dist: PyYAML Requires-Dist: Whoosh Requires-Dist: bottle A sample and productive personal documents library Scan your documents: * Auto rotate * index them on the file name and on the content (OCR) Mange your pdf: * index them on the file name and on the content Search in your library: * Build the index * Quick search using the index `Sources `_ PKlA\Hcbb!edocuments-0.9.0.dist-info/RECORDedocuments/__init__.py,sha256=GpW_Ry9dauI03NN6MGIsGdAeiYPVIn7O84j5Px9IDTw,5635 edocuments/backend.py,sha256=bwUNiyD3Cn0nLF_gqPmwdtw5W2vHIyaLx5oy6pocqdo,7528 edocuments/colorize.py,sha256=1xfk4jozdL_nfDoZ4mwQer8vMdw9xxfvWR8DpfON_ww,184 edocuments/index.py,sha256=G4T9f1zVAGS6caHxbW2rErDIE72cBe1zUiBc4b6rkuc,3459 edocuments/label_dialog.py,sha256=5gKnx_3ov0YnNO2yTieT_ojGfrhLyXtYCY6KXmwnaqI,736 edocuments/main_widget.py,sha256=-EZoOR9Pn6md5kXuQjjJSKLvMjDtsiflzl-OeqmH_pk,8089 edocuments/process.py,sha256=UcH0g1C5gRiyByPwvbwv7FEtyoD-0XmRM2F2NbuYTEo,4659 edocuments/ressources/.config.yaml.swp,sha256=Me6p8AmVzmvuRYplbEzMkGE24vuV27YXFNxjAAnXidk,12288 edocuments/ressources/config.yaml,sha256=eb1BO-bWWasiWyl0SSqsx9wrnZ4p7iFETUOQdoPZiFo,3199 edocuments/ressources/edocuments.desktop,sha256=mHbt28g2fCJMdS2QY9IR9UYxJzwiggvj38kayeGVWiw,290 edocuments/ressources/edocuments.png,sha256=NHwD04rvyHPvne5MGsn6Fbi6Ruyx7oM-0APO4eziMPA,40236 edocuments/ressources/edocuments.svg,sha256=Z8luuo2sllzl7gqWY1ieRPpkRSufrEXFOCb3R0lZvvo,16875 edocuments/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 edocuments/ui/label_dialog.py,sha256=Xwrsrq7chUq55Lk7OI-AqYWI0GV_2e_BhyiwuSiY0w0,1326 edocuments/ui/main.py,sha256=fRYBEfrTWyxiJzFZrb2taHxf4_cz2wjwpIK695m22YU,7966 edocuments-0.9.0.dist-info/DESCRIPTION.rst,sha256=mu3KwRCPV-zxdHGINYV5xJDq34-jVfIh1vKGUkecSkQ,353 edocuments-0.9.0.dist-info/METADATA,sha256=mM9IPel26oRimlUaplhid8ecDnnZNk1buM0FmspV_-8,841 edocuments-0.9.0.dist-info/RECORD,, edocuments-0.9.0.dist-info/WHEEL,sha256=zX7PHtH_7K-lEzyK75et0UBa3Bj8egCBMXe1M4gc6SU,92 edocuments-0.9.0.dist-info/entry_points.txt,sha256=P_eXij8vRbfFmwuMKSwtV99Zv4h1wZBwN1mq_ajJUkc,93 edocuments-0.9.0.dist-info/metadata.json,sha256=GGY2Ec-hgnS-S0bhamp3zM8VLaFaNswudqbkvzSWVh8,1009 edocuments-0.9.0.dist-info/top_level.txt,sha256=yvF7f7apfBqVzBbWZQP6Pp2iqiVAHWruuNkQ7SDS2mg,11 PKXHa33edocuments/process.pyPK>HOfedocuments/label_dialog.pyPKMJH`~edocuments/colorize.pyPKZHtF jedocuments/index.pyPKZHkEi$edocuments/__init__.pyPKZH*hhU:edocuments/backend.pyPKZHO*Wedocuments/main_widget.pyPK>H"..wedocuments/ui/label_dialog.pyPKXH$a)}edocuments/ui/main.pyPK=Hzedocuments/ui/__init__.pyPK>HJ AA$edocuments/ressources/edocuments.svgPKJ[HO  !edocuments/ressources/config.yamlPKՃ>HT\,,$edocuments/ressources/edocuments.pngPKyw?H'I""( edocuments/ressources/edocuments.desktopPKlkMHJe00&redocuments/ressources/.config.yaml.swpPKlA\H_1aa*edocuments-0.9.0.dist-info/DESCRIPTION.rstPKlA\HY?]]+_edocuments-0.9.0.dist-info/entry_points.txtPKlA\HF{(edocuments-0.9.0.dist-info/metadata.jsonPKlA\H^x (<edocuments-0.9.0.dist-info/top_level.txtPKlA\H}\\ edocuments-0.9.0.dist-info/WHEELPKlA\HEII#'edocuments-0.9.0.dist-info/METADATAPKlA\Hcbb!edocuments-0.9.0.dist-info/RECORDPKR