mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
pdf2lrf
This commit is contained in:
parent
1a36564830
commit
8e8940f69e
@ -196,6 +196,9 @@ _check_symlinks_prescript()
|
|||||||
print
|
print
|
||||||
print 'Adding unrtf'
|
print 'Adding unrtf'
|
||||||
os.link(os.path.expanduser('~/unrtf'), os.path.join(frameworks_dir, 'unrtf'))
|
os.link(os.path.expanduser('~/unrtf'), os.path.join(frameworks_dir, 'unrtf'))
|
||||||
|
print
|
||||||
|
print 'Adding pdftohtml'
|
||||||
|
os.link(os.path.expanduser('~/pdftohtml'), os.path.join(frameworks_dir, 'pdftohtml'))
|
||||||
print
|
print
|
||||||
print 'Installing prescipt'
|
print 'Installing prescipt'
|
||||||
sf = [os.path.basename(s) for s in all_names]
|
sf = [os.path.basename(s) for s in all_names]
|
||||||
|
1
setup.py
1
setup.py
@ -30,6 +30,7 @@ entry_points = {
|
|||||||
'rtf2lrf = libprs500.ebooks.lrf.rtf.convert_from:main',\
|
'rtf2lrf = libprs500.ebooks.lrf.rtf.convert_from:main',\
|
||||||
'web2disk = libprs500.web.fetch.simple:main',\
|
'web2disk = libprs500.web.fetch.simple:main',\
|
||||||
'web2lrf = libprs500.ebooks.lrf.web.convert_from:main',\
|
'web2lrf = libprs500.ebooks.lrf.web.convert_from:main',\
|
||||||
|
'pdf2lrf = libprs500.ebooks.lrf.pdf.convert_from:main',\
|
||||||
],
|
],
|
||||||
'gui_scripts' : [ APPNAME+' = libprs500.gui.main:main']
|
'gui_scripts' : [ APPNAME+' = libprs500.gui.main:main']
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
## with this program; if not, write to the Free Software Foundation, Inc.,
|
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
''' E-book management software'''
|
''' E-book management software'''
|
||||||
__version__ = "0.3.79"
|
__version__ = "0.3.81"
|
||||||
__docformat__ = "epytext"
|
__docformat__ = "epytext"
|
||||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
||||||
__appname__ = 'libprs500'
|
__appname__ = 'libprs500'
|
||||||
|
@ -255,11 +255,17 @@ class HTMLConverter(object):
|
|||||||
# Fix pdftohtml markup
|
# Fix pdftohtml markup
|
||||||
PDFTOHTML = [
|
PDFTOHTML = [
|
||||||
# Remove <hr> tags
|
# Remove <hr> tags
|
||||||
(re.compile(r'<hr.*?>', re.IGNORECASE), lambda match: ''),
|
(re.compile(r'<hr.*?>', re.IGNORECASE), lambda match: '<span style="page-break-after:always"> </span>'),
|
||||||
|
# Remove page numbers
|
||||||
|
(re.compile(r'\d+<br>', re.IGNORECASE), lambda match: ''),
|
||||||
# Remove <br> and replace <br><br> with <p>
|
# Remove <br> and replace <br><br> with <p>
|
||||||
(re.compile(r'<br.*?>\s*<br.*?>', re.IGNORECASE), lambda match: '<p>'),
|
(re.compile(r'<br.*?>\s*<br.*?>', re.IGNORECASE), lambda match: '<p>'),
|
||||||
(re.compile(r'(.{75,}?)<br.*?>', re.IGNORECASE),
|
(re.compile(r'(.*)<br.*?>', re.IGNORECASE),
|
||||||
lambda match: match.group(1)),
|
lambda match: match.group() if re.match('<', match.group(1).lstrip()) or len(match.group(1)) < 40
|
||||||
|
else match.group(1)),
|
||||||
|
# Remove hyphenation
|
||||||
|
(re.compile(r'-\n|-\n\r'), lambda match: ''),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
class Link(object):
|
class Link(object):
|
||||||
|
@ -18,7 +18,7 @@ from subprocess import Popen, PIPE
|
|||||||
from libprs500.ebooks.lrf import option_parser as lrf_option_parser
|
from libprs500.ebooks.lrf import option_parser as lrf_option_parser
|
||||||
from libprs500.ebooks import ConversionError
|
from libprs500.ebooks import ConversionError
|
||||||
from libprs500.ebooks.lrf.html.convert_from import process_file
|
from libprs500.ebooks.lrf.html.convert_from import process_file
|
||||||
from libprs500 import isosx
|
from libprs500 import isosx, __appname__
|
||||||
CLIT = 'clit'
|
CLIT = 'clit'
|
||||||
if isosx and hasattr(sys, 'frameworks_dir'):
|
if isosx and hasattr(sys, 'frameworks_dir'):
|
||||||
CLIT = os.path.join(sys.frameworks_dir, CLIT)
|
CLIT = os.path.join(sys.frameworks_dir, CLIT)
|
||||||
@ -32,7 +32,7 @@ def option_parser():
|
|||||||
def generate_html(pathtolit):
|
def generate_html(pathtolit):
|
||||||
if not os.access(pathtolit, os.R_OK):
|
if not os.access(pathtolit, os.R_OK):
|
||||||
raise ConversionError, 'Cannot read from ' + pathtolit
|
raise ConversionError, 'Cannot read from ' + pathtolit
|
||||||
tdir = mkdtemp(prefix='libprs500_lit2lrf_')
|
tdir = mkdtemp(prefix=__appname__+'_')
|
||||||
cmd = ' '.join([CLIT, '"'+pathtolit+'"', tdir])
|
cmd = ' '.join([CLIT, '"'+pathtolit+'"', tdir])
|
||||||
p = Popen(cmd, shell=True, stderr=PIPE)
|
p = Popen(cmd, shell=True, stderr=PIPE)
|
||||||
ret = p.wait()
|
ret = p.wait()
|
||||||
|
14
src/libprs500/ebooks/lrf/pdf/__init__.py
Normal file
14
src/libprs500/ebooks/lrf/pdf/__init__.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
## Copyright (C) 2007 Kovid Goyal kovid@kovidgoyal.net
|
||||||
|
## This program is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
## the Free Software Foundation; either version 2 of the License, or
|
||||||
|
## (at your option) any later version.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License along
|
||||||
|
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
73
src/libprs500/ebooks/lrf/pdf/convert_from.py
Normal file
73
src/libprs500/ebooks/lrf/pdf/convert_from.py
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
## Copyright (C) 2007 Kovid Goyal kovid@kovidgoyal.net
|
||||||
|
## This program is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
## the Free Software Foundation; either version 2 of the License, or
|
||||||
|
## (at your option) any later version.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License along
|
||||||
|
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
''''''
|
||||||
|
|
||||||
|
import sys, os, subprocess
|
||||||
|
from libprs500 import isosx
|
||||||
|
from libprs500.ebooks import ConversionError
|
||||||
|
from libprs500.ptempfile import PersistentTemporaryFile
|
||||||
|
from libprs500.ebooks.lrf import option_parser as lrf_option_parser
|
||||||
|
from libprs500.ebooks.lrf.html.convert_from import process_file
|
||||||
|
|
||||||
|
PDFTOHTML = 'pdftohtml'
|
||||||
|
if isosx and hasattr(sys, 'frameworks_dir'):
|
||||||
|
PDFTOHTML = os.path.join(sys.frameworks_dir, PDFTOHTML)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_html(pathtopdf):
|
||||||
|
'''
|
||||||
|
Convert the pdf into html.
|
||||||
|
@return: A closed PersistentTemporaryFile.
|
||||||
|
'''
|
||||||
|
if not os.access(pathtopdf, os.R_OK):
|
||||||
|
raise ConversionError, 'Cannot read from ' + pathtopdf
|
||||||
|
pf = PersistentTemporaryFile('.html')
|
||||||
|
pf.close()
|
||||||
|
cmd = PDFTOHTML + ' -noframes -p -nomerge "%s" "%s"'%(pathtopdf, pf.name)
|
||||||
|
p = subprocess.Popen(cmd, shell=True, stderr=subprocess.PIPE)
|
||||||
|
ret = p.wait()
|
||||||
|
if ret != 0:
|
||||||
|
err = p.stderr.read()
|
||||||
|
raise ConversionError, err
|
||||||
|
return pf
|
||||||
|
|
||||||
|
def option_parser():
|
||||||
|
return lrf_option_parser(
|
||||||
|
'''Usage: %prog [options] mybook.pdf\n\n'''
|
||||||
|
'''%prog converts mybook.pdf to mybook.lrf\n\n'''
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main(args=sys.argv):
|
||||||
|
parser = option_parser()
|
||||||
|
options, args = parser.parse_args(args)
|
||||||
|
if len(args) != 2:
|
||||||
|
parser.print_help()
|
||||||
|
print
|
||||||
|
print 'No pdf file specified'
|
||||||
|
return 1
|
||||||
|
pdf = os.path.abspath(os.path.expanduser(args[1]))
|
||||||
|
htmlfile = generate_html(pdf)
|
||||||
|
if not options.output:
|
||||||
|
ext = '.lrs' if options.lrs else '.lrf'
|
||||||
|
options.output = os.path.abspath(os.path.basename(os.path.splitext(args[1])[0]) + ext)
|
||||||
|
else:
|
||||||
|
options.output = os.path.abspath(options.output)
|
||||||
|
options.pdftohtml = True
|
||||||
|
process_file(htmlfile.name, options)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
@ -7,10 +7,6 @@ RC = images_rc.pyc
|
|||||||
%_rc.pyc : %.qrc %
|
%_rc.pyc : %.qrc %
|
||||||
pyrcc4 $< > $*_rc.py
|
pyrcc4 $< > $*_rc.py
|
||||||
python -c "import compiler; compiler.compileFile('$*_rc.py')"
|
python -c "import compiler; compiler.compileFile('$*_rc.py')"
|
||||||
echo > $*_rc.py
|
|
||||||
touch $*_rc.pyc
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
all : $(UI) $(RC)
|
all : $(UI) $(RC)
|
||||||
|
|
||||||
|
@ -14,8 +14,8 @@
|
|||||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
""" The GUI for libprs500. """
|
""" The GUI for libprs500. """
|
||||||
import sys, os, re, StringIO, traceback
|
import sys, os, re, StringIO, traceback
|
||||||
from PyQt4.QtCore import QVariant, QSettings
|
from PyQt4.QtCore import QVariant, QSettings, QFileInfo
|
||||||
from PyQt4.QtGui import QFileDialog, QMessageBox, QPixmap
|
from PyQt4.QtGui import QFileDialog, QMessageBox, QPixmap, QFileIconProvider
|
||||||
from libprs500 import __appname__ as APP_TITLE
|
from libprs500 import __appname__ as APP_TITLE
|
||||||
from libprs500 import __author__
|
from libprs500 import __author__
|
||||||
NONE = QVariant() #: Null value to return from the data function of item models
|
NONE = QVariant() #: Null value to return from the data function of item models
|
||||||
@ -37,6 +37,8 @@ def error_dialog(parent, title, msg):
|
|||||||
d.setIconPixmap(QPixmap(':/images/dialog_error.svg'))
|
d.setIconPixmap(QPixmap(':/images/dialog_error.svg'))
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
def qstring_to_unicode(q):
|
||||||
|
return unicode(q.toUtf8(), 'utf8')
|
||||||
|
|
||||||
def human_readable(size):
|
def human_readable(size):
|
||||||
""" Convert a size in bytes into a human readable form """
|
""" Convert a size in bytes into a human readable form """
|
||||||
@ -53,31 +55,111 @@ def human_readable(size):
|
|||||||
size = size[:size.find(".")+2]
|
size = size[:size.find(".")+2]
|
||||||
return size + " " + suffix
|
return size + " " + suffix
|
||||||
|
|
||||||
def choose_files(window, dialog, title, filetype='',
|
|
||||||
extensions=[], all_files=True):
|
class FileIconProvider(QFileIconProvider):
|
||||||
|
|
||||||
|
ICONS = {
|
||||||
|
'default' : 'unknown',
|
||||||
|
'dir' : 'dir',
|
||||||
|
'zero' : 'zero',
|
||||||
|
'jpeg' : 'jpeg',
|
||||||
|
'jpg' : 'jpeg',
|
||||||
|
'gif' : 'gif',
|
||||||
|
'png' : 'png',
|
||||||
|
'bmp' : 'bmp',
|
||||||
|
'svg' : 'svg',
|
||||||
|
'html' : 'html',
|
||||||
|
'lit' : 'lit',
|
||||||
|
'lrf' : 'lrf',
|
||||||
|
'lrx' : 'lrx',
|
||||||
|
'pdf' : 'pdf',
|
||||||
|
'rar' : 'rar',
|
||||||
|
'zip' : 'zip',
|
||||||
|
'txt' : 'txt',
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
QFileIconProvider.__init__(self)
|
||||||
|
self.icons = {}
|
||||||
|
for key in self.__class__.ICONS.keys():
|
||||||
|
self.icons[key] = ':/images/mimetypes/'+self.__class__.ICONS[key]+'.svg'
|
||||||
|
|
||||||
|
|
||||||
|
def pixmap(self, fileinfo):
|
||||||
|
key = 'default'
|
||||||
|
icons = self.icons
|
||||||
|
if fileinfo.isSymlink():
|
||||||
|
if not fileinfo.exists():
|
||||||
|
return icons['zero']
|
||||||
|
fileinfo = QFileInfo(fileinfo.readLink())
|
||||||
|
if fileinfo.isDir():
|
||||||
|
key = 'dir'
|
||||||
|
else:
|
||||||
|
ext = qstring_to_unicode(fileinfo.extension(True)).lower()
|
||||||
|
key = [i for i in icons.keys() if i in ext]
|
||||||
|
key = key[0] if key else 'default'
|
||||||
|
if key:
|
||||||
|
key = key[0]
|
||||||
|
candidate = icons[key]
|
||||||
|
if isinstance(candidate, QPixmap):
|
||||||
|
return candidate
|
||||||
|
pixmap = QPixmap(candidate)
|
||||||
|
icons[key] = pixmap
|
||||||
|
return pixmap
|
||||||
|
|
||||||
|
file_icon_provider = None
|
||||||
|
|
||||||
|
class FileDialog(QFileDialog):
|
||||||
|
def __init__(self, title='Choose Files',
|
||||||
|
filters=[],
|
||||||
|
add_all_files_filter=True,
|
||||||
|
parent=None,
|
||||||
|
modal = True,
|
||||||
|
name = '',
|
||||||
|
mode = QFileDialog.ExistingFiles,
|
||||||
|
):
|
||||||
|
QFileDialog.__init__(self, parent)
|
||||||
|
self.setModal(modal)
|
||||||
|
global file_icon_provider
|
||||||
|
if not file_icon_provider:
|
||||||
|
file_icon_provider = FileIconProvider()
|
||||||
|
if not self.iconProvider() is file_icon_provider:
|
||||||
|
self.setIconProvider(file_icon_provider)
|
||||||
|
|
||||||
|
settings = QSettings()
|
||||||
|
_dir = settings.value(name, QVariant(os.path.expanduser("~"))).toString()
|
||||||
|
self.setDirectory(_dir)
|
||||||
|
ftext = ''
|
||||||
|
if filters:
|
||||||
|
for filter in filters:
|
||||||
|
text, extensions = filter
|
||||||
|
extensions = ['.'+i if not i.startswith('.') else i for i in extensions]
|
||||||
|
ftext += '%s (%s);;'%(text, ' '.join(extensions))
|
||||||
|
if add_all_files_filter or not ftext:
|
||||||
|
ftext += 'All files (*)'
|
||||||
|
self.setFilters(ftext)
|
||||||
|
self.setWindowTitle(title)
|
||||||
|
|
||||||
|
def get_files(self):
|
||||||
|
self.exec_()
|
||||||
|
return tuple(os.path.abspath(qstring_to_unicode(i)) for i in self.selectedFiles())
|
||||||
|
|
||||||
|
|
||||||
|
def choose_files(window, name, title,
|
||||||
|
filters=[], all_files=True, select_only_single_file=False):
|
||||||
'''
|
'''
|
||||||
Ask user to choose a bunch of files.
|
Ask user to choose a bunch of files.
|
||||||
@param dialog: Unique gialog name used to store the opened directory
|
@param name: Unique dialog name used to store the opened directory
|
||||||
@param title: Title to show in dialogs titlebar
|
@param title: Title to show in dialogs titlebar
|
||||||
@param filetype: What types of files is this dialog choosing
|
@param filters: list of allowable extensions. Each element of the list
|
||||||
@params extensions: list of allowable extension
|
must be a 2-tuple with first element a string describing
|
||||||
@params all_files: If True show all files
|
the type of files to be filtered and second element a list
|
||||||
|
of extensions.
|
||||||
|
@param all_files: If True add All files to filters.
|
||||||
|
@param select_only_single_file: If True only one file can be selected
|
||||||
'''
|
'''
|
||||||
settings = QSettings()
|
mode = QFileDialog.ExistingFile if select_only_single_file else QFileDialog.ExistingFiles
|
||||||
_dir = settings.value(dialog, QVariant(os.path.expanduser("~"))).toString()
|
fd = FileDialog(title=title, name=name, filters=filters,
|
||||||
books = []
|
parent=window, add_all_files_filter=all_files, mode=mode,
|
||||||
extensions = ['*.'+i for i in extensions]
|
)
|
||||||
if extensions:
|
return fd.get_files()
|
||||||
filter = filetype + ' (' + ' '.join(extensions) + ')'
|
|
||||||
if all_files:
|
|
||||||
filter += ';;All files (*)'
|
|
||||||
else:
|
|
||||||
filter = 'All files (*)'
|
|
||||||
files = QFileDialog.getOpenFileNames(window, title, _dir, filter)
|
|
||||||
for file in files:
|
|
||||||
file = unicode(file.toUtf8(), 'utf8')
|
|
||||||
books.append(os.path.abspath(file))
|
|
||||||
if books:
|
|
||||||
settings.setValue(dialog, QVariant(os.path.dirname(books[0])))
|
|
||||||
return books
|
|
||||||
|
|
@ -14,5 +14,13 @@
|
|||||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
'''Various dialogs used in the GUI'''
|
'''Various dialogs used in the GUI'''
|
||||||
|
|
||||||
|
from PyQt4.QtCore import QObject
|
||||||
|
from PyQt4.QtGui import QDialog
|
||||||
|
|
||||||
|
class ModalDialog(QObject):
|
||||||
|
def __init__(self, window):
|
||||||
|
QObject.__init__(self, window)
|
||||||
|
self.dialog = QDialog(window)
|
||||||
|
self.accept = self.dialog.accept
|
||||||
|
self.reject = self.dialog.reject
|
||||||
|
self.window = window
|
@ -20,9 +20,11 @@ import os
|
|||||||
|
|
||||||
from PyQt4.QtCore import Qt, SIGNAL
|
from PyQt4.QtCore import Qt, SIGNAL
|
||||||
from PyQt4.Qt import QObject, QPixmap, QListWidgetItem, QErrorMessage, \
|
from PyQt4.Qt import QObject, QPixmap, QListWidgetItem, QErrorMessage, \
|
||||||
QVariant, QSettings, QFileDialog, QDialog
|
QVariant, QSettings, QFileDialog
|
||||||
|
|
||||||
|
|
||||||
|
from libprs500.gui2 import qstring_to_unicode, error_dialog
|
||||||
|
from libprs500.gui2.dialogs import ModalDialog
|
||||||
from libprs500.gui2.dialogs.metadata_single_ui import Ui_MetadataSingleDialog
|
from libprs500.gui2.dialogs.metadata_single_ui import Ui_MetadataSingleDialog
|
||||||
|
|
||||||
class Format(QListWidgetItem):
|
class Format(QListWidgetItem):
|
||||||
@ -32,7 +34,7 @@ class Format(QListWidgetItem):
|
|||||||
QListWidgetItem.__init__(self, ext.upper(), parent, \
|
QListWidgetItem.__init__(self, ext.upper(), parent, \
|
||||||
QListWidgetItem.UserType)
|
QListWidgetItem.UserType)
|
||||||
|
|
||||||
class EditBookDialog(Ui_MetadataSingleDialog, QDialog):
|
class MetadataSingleDialog(Ui_MetadataSingleDialog, ModalDialog):
|
||||||
|
|
||||||
def select_cover(self, checked):
|
def select_cover(self, checked):
|
||||||
settings = QSettings()
|
settings = QSettings()
|
||||||
@ -40,25 +42,27 @@ class EditBookDialog(Ui_MetadataSingleDialog, QDialog):
|
|||||||
QVariant(os.path.expanduser("~"))).toString()
|
QVariant(os.path.expanduser("~"))).toString()
|
||||||
_file = str(QFileDialog.getOpenFileName(self.parent, \
|
_file = str(QFileDialog.getOpenFileName(self.parent, \
|
||||||
"Choose cover for " + str(self.title.text()), _dir, \
|
"Choose cover for " + str(self.title.text()), _dir, \
|
||||||
"Images (*.png *.gif *.jpeg *.jpg);;All files (*)"))
|
"Images (*.png *.gif *.jpeg *.jpg *.svg);;All files (*)"))
|
||||||
if len(_file):
|
if len(_file):
|
||||||
_file = os.path.abspath(_file)
|
_file = os.path.abspath(_file)
|
||||||
settings.setValue("change cover dir", \
|
settings.setValue("change cover dir", \
|
||||||
QVariant(os.path.dirname(_file)))
|
QVariant(os.path.dirname(_file)))
|
||||||
if not os.access(_file, os.R_OK):
|
if not os.access(_file, os.R_OK):
|
||||||
QErrorMessage(self.parent).showMessage("You do not have "+\
|
d = error_dialog(self.window, 'Cannot read',
|
||||||
"permission to read the file: " + _file)
|
'You do not have permission to read the file: ' + _file)
|
||||||
|
d.exec_()
|
||||||
return
|
return
|
||||||
cf, cover = None, None
|
cf, cover = None, None
|
||||||
try:
|
try:
|
||||||
cf = open(_file, "rb")
|
cf = open(_file, "rb")
|
||||||
cover = cf.read()
|
cover = cf.read()
|
||||||
except IOError, e:
|
except IOError, e:
|
||||||
QErrorMessage(self.parent).showMessage("There was an error"+\
|
d = error_dialog(self.window, 'Error reading file',
|
||||||
" reading from file: " + _file + "\n"+str(e))
|
"<p>There was an error reading from file: <br /><b>" + _file + "</b></p><br />"+str(e))
|
||||||
|
d.exec_()
|
||||||
if cover:
|
if cover:
|
||||||
pix = QPixmap()
|
pix = QPixmap()
|
||||||
pix.loadFromData(cover, "", Qt.AutoColor)
|
pix.loadFromData(cover)
|
||||||
if pix.isNull():
|
if pix.isNull():
|
||||||
QErrorMessage(self.parent).showMessage(_file + \
|
QErrorMessage(self.parent).showMessage(_file + \
|
||||||
" is not a valid picture")
|
" is not a valid picture")
|
||||||
@ -83,7 +87,7 @@ class EditBookDialog(Ui_MetadataSingleDialog, QDialog):
|
|||||||
for _file in files:
|
for _file in files:
|
||||||
_file = os.path.abspath(_file)
|
_file = os.path.abspath(_file)
|
||||||
if not os.access(_file, os.R_OK):
|
if not os.access(_file, os.R_OK):
|
||||||
QErrorMessage(self.parent).showMessage("You do not have "+\
|
QErrorMessage(self.window).showMessage("You do not have "+\
|
||||||
"permission to read the file: " + _file)
|
"permission to read the file: " + _file)
|
||||||
continue
|
continue
|
||||||
ext = os.path.splitext(_file)[1].lower()
|
ext = os.path.splitext(_file)[1].lower()
|
||||||
@ -124,15 +128,17 @@ class EditBookDialog(Ui_MetadataSingleDialog, QDialog):
|
|||||||
self.db.remove_format(self.id, ext)
|
self.db.remove_format(self.id, ext)
|
||||||
self.db.update_max_size(self.id)
|
self.db.update_max_size(self.id)
|
||||||
|
|
||||||
def __init__(self, parent, row, db):
|
def __init__(self, window, row, db, slot):
|
||||||
Ui_MetadataSingleDialog.__init__(self)
|
Ui_MetadataSingleDialog.__init__(self)
|
||||||
QDialog.__init__(parent)
|
ModalDialog.__init__(self, window)
|
||||||
self.setupUi(parent)
|
self.setupUi(self.dialog)
|
||||||
self.splitter.setStretchFactor(100, 1)
|
self.splitter.setStretchFactor(100, 1)
|
||||||
self.db = db
|
self.db = db
|
||||||
self.id = db.id(row)
|
self.id = db.id(row)
|
||||||
self.cover_data = None
|
self.cover_data = None
|
||||||
self.formats_changed = False
|
self.formats_changed = False
|
||||||
|
self.cover_changed = False
|
||||||
|
self.slot = slot
|
||||||
QObject.connect(self.cover_button, SIGNAL("clicked(bool)"), \
|
QObject.connect(self.cover_button, SIGNAL("clicked(bool)"), \
|
||||||
self.select_cover)
|
self.select_cover)
|
||||||
QObject.connect(self.add_format_button, SIGNAL("clicked(bool)"), \
|
QObject.connect(self.add_format_button, SIGNAL("clicked(bool)"), \
|
||||||
@ -140,10 +146,8 @@ class EditBookDialog(Ui_MetadataSingleDialog, QDialog):
|
|||||||
QObject.connect(self.remove_format_button, SIGNAL("clicked(bool)"), \
|
QObject.connect(self.remove_format_button, SIGNAL("clicked(bool)"), \
|
||||||
self.remove_format)
|
self.remove_format)
|
||||||
QObject.connect(self.button_box, SIGNAL("accepted()"), \
|
QObject.connect(self.button_box, SIGNAL("accepted()"), \
|
||||||
self.sync_formats)
|
self.sync)
|
||||||
|
|
||||||
data = self.db.get_row_by_id(self.id, \
|
|
||||||
["title","authors","rating","publisher","tags","comments"])
|
|
||||||
self.title.setText(db.title(row))
|
self.title.setText(db.title(row))
|
||||||
au = self.db.authors(row)
|
au = self.db.authors(row)
|
||||||
self.authors.setText(au if au else '')
|
self.authors.setText(au if au else '')
|
||||||
@ -154,7 +158,8 @@ class EditBookDialog(Ui_MetadataSingleDialog, QDialog):
|
|||||||
rating = self.db.rating(row)
|
rating = self.db.rating(row)
|
||||||
if rating > 0:
|
if rating > 0:
|
||||||
self.rating.setValue(rating)
|
self.rating.setValue(rating)
|
||||||
self.comments.setPlainText(data["comments"] if data["comments"] else "")
|
comments = self.db.comments(row)
|
||||||
|
self.comments.setPlainText(comments if comments else '')
|
||||||
cover = self.db.cover(row)
|
cover = self.db.cover(row)
|
||||||
if cover:
|
if cover:
|
||||||
pm = QPixmap()
|
pm = QPixmap()
|
||||||
@ -166,3 +171,17 @@ class EditBookDialog(Ui_MetadataSingleDialog, QDialog):
|
|||||||
# if not ext:
|
# if not ext:
|
||||||
# ext = "Unknown"
|
# ext = "Unknown"
|
||||||
# Format(self.formats, ext)
|
# Format(self.formats, ext)
|
||||||
|
|
||||||
|
self.dialog.exec_()
|
||||||
|
|
||||||
|
def sync(self):
|
||||||
|
if self.formats_changed:
|
||||||
|
self.sync_formats()
|
||||||
|
title = qstring_to_unicode(self.title.text())
|
||||||
|
self.db.set_title(self.id, title)
|
||||||
|
au = qstring_to_unicode(self.authors.text()).split(',')
|
||||||
|
self.db.set_authors(self.id, au)
|
||||||
|
self.slot()
|
||||||
|
|
||||||
|
def reject(self):
|
||||||
|
self.rejected = True
|
||||||
|
@ -494,22 +494,6 @@
|
|||||||
<include location="../images.qrc" />
|
<include location="../images.qrc" />
|
||||||
</resources>
|
</resources>
|
||||||
<connections>
|
<connections>
|
||||||
<connection>
|
|
||||||
<sender>button_box</sender>
|
|
||||||
<signal>rejected()</signal>
|
|
||||||
<receiver>MetadataSingleDialog</receiver>
|
|
||||||
<slot>reject()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel" >
|
|
||||||
<x>316</x>
|
|
||||||
<y>260</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel" >
|
|
||||||
<x>286</x>
|
|
||||||
<y>274</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
<connection>
|
<connection>
|
||||||
<sender>button_box</sender>
|
<sender>button_box</sender>
|
||||||
<signal>accepted()</signal>
|
<signal>accepted()</signal>
|
||||||
@ -526,5 +510,21 @@
|
|||||||
</hint>
|
</hint>
|
||||||
</hints>
|
</hints>
|
||||||
</connection>
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>button_box</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>MetadataSingleDialog</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel" >
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel" >
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
</connections>
|
</connections>
|
||||||
</ui>
|
</ui>
|
||||||
|
@ -10,22 +10,27 @@
|
|||||||
<file>images/jobs.svg</file>
|
<file>images/jobs.svg</file>
|
||||||
<file alias="library" >images/library.png</file>
|
<file alias="library" >images/library.png</file>
|
||||||
<file>images/list_remove.svg</file>
|
<file>images/list_remove.svg</file>
|
||||||
|
<file>images/mimetypes/bmp.svg</file>
|
||||||
|
<file>images/mimetypes/dir.svg</file>
|
||||||
|
<file>images/mimetypes/gif.svg</file>
|
||||||
|
<file>images/mimetypes/html.svg</file>
|
||||||
|
<file>images/mimetypes/jpeg.svg</file>
|
||||||
|
<file>images/mimetypes/lit.svg</file>
|
||||||
|
<file>images/mimetypes/lrf.svg</file>
|
||||||
|
<file>images/mimetypes/lrx.svg</file>
|
||||||
|
<file>images/mimetypes/pdf.svg</file>
|
||||||
|
<file>images/mimetypes/png.svg</file>
|
||||||
|
<file>images/mimetypes/rar.svg</file>
|
||||||
|
<file>images/mimetypes/rtf.svg</file>
|
||||||
|
<file>images/mimetypes/svg.svg</file>
|
||||||
|
<file>images/mimetypes/txt.svg</file>
|
||||||
|
<file>images/mimetypes/unknown.svg</file>
|
||||||
|
<file>images/mimetypes/zero.svg</file>
|
||||||
|
<file>images/mimetypes/zip.svg</file>
|
||||||
<file>images/plus.svg</file>
|
<file>images/plus.svg</file>
|
||||||
<file>images/reader.svg</file>
|
<file>images/reader.svg</file>
|
||||||
<file>images/sd.svg</file>
|
<file>images/sd.svg</file>
|
||||||
<file>images/sync.svg</file>
|
<file>images/sync.svg</file>
|
||||||
<file>images/trash.svg</file>
|
<file>images/trash.svg</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="/" >
|
|
||||||
<file>images/mimetypes/format_html.svg</file>
|
|
||||||
<file>images/mimetypes/format_lit.svg</file>
|
|
||||||
<file>images/mimetypes/format_lrf.svg</file>
|
|
||||||
<file>images/mimetypes/format_lrx.svg</file>
|
|
||||||
<file>images/mimetypes/format_pdf.svg</file>
|
|
||||||
<file>images/mimetypes/format_rar.svg</file>
|
|
||||||
<file>images/mimetypes/format_rtf.svg</file>
|
|
||||||
<file>images/mimetypes/format_txt.svg</file>
|
|
||||||
<file>images/mimetypes/format_unknown.svg</file>
|
|
||||||
<file>images/mimetypes/format_zip.svg</file>
|
|
||||||
</qresource>
|
|
||||||
</RCC>
|
</RCC>
|
||||||
|
@ -28,6 +28,7 @@ from libprs500.gui2.main_ui import Ui_MainWindow
|
|||||||
from libprs500.gui2.device import DeviceDetector, DeviceManager
|
from libprs500.gui2.device import DeviceDetector, DeviceManager
|
||||||
from libprs500.gui2.status import StatusBar
|
from libprs500.gui2.status import StatusBar
|
||||||
from libprs500.gui2.jobs import JobManager, JobException
|
from libprs500.gui2.jobs import JobManager, JobException
|
||||||
|
from libprs500.gui2.dialogs.metadata_single import MetadataSingleDialog
|
||||||
|
|
||||||
class Main(QObject, Ui_MainWindow):
|
class Main(QObject, Ui_MainWindow):
|
||||||
|
|
||||||
@ -194,8 +195,9 @@ class Main(QObject, Ui_MainWindow):
|
|||||||
'''
|
'''
|
||||||
Add books from the local filesystem to either the library or the device.
|
Add books from the local filesystem to either the library or the device.
|
||||||
'''
|
'''
|
||||||
books = choose_files(self.window, 'add books dialog dir', 'Select books', 'Books',
|
books = choose_files(self.window, 'add books dialog dir', 'Select books',
|
||||||
extensions=['lrf', 'lrx', 'rar', 'zip', 'rtf', 'lit', 'txt', 'htm', 'html', 'xhtml', 'epub'])
|
filters=[('Books', ['lrf', 'lrx', 'rar', 'zip',
|
||||||
|
'rtf', 'lit', 'txt', 'htm', 'html', 'xhtml', 'epub',])])
|
||||||
if not books:
|
if not books:
|
||||||
return
|
return
|
||||||
on_card = False if self.stack.currentIndex() != 2 else True
|
on_card = False if self.stack.currentIndex() != 2 else True
|
||||||
@ -309,9 +311,21 @@ class Main(QObject, Ui_MainWindow):
|
|||||||
############################### Edit metadata ##############################
|
############################### Edit metadata ##############################
|
||||||
def edit_metadata(self, checked):
|
def edit_metadata(self, checked):
|
||||||
'''
|
'''
|
||||||
Edit metadata of selected books in library or on device.
|
Edit metadata of selected books in library individually.
|
||||||
'''
|
'''
|
||||||
pass
|
rows = self.library_view.selectionModel().selectedRows()
|
||||||
|
if not rows or len(rows) == 0:
|
||||||
|
return
|
||||||
|
changed = False
|
||||||
|
def cs():
|
||||||
|
changed = True
|
||||||
|
for row in rows:
|
||||||
|
MetadataSingleDialog(self.window, row.row(), self.library_view.model().db, cs)
|
||||||
|
|
||||||
|
if changed:
|
||||||
|
self.library_view.model().resort()
|
||||||
|
self.library_view.model().research()
|
||||||
|
|
||||||
############################################################################
|
############################################################################
|
||||||
|
|
||||||
############################# Syncing to device#############################
|
############################# Syncing to device#############################
|
||||||
@ -413,7 +427,7 @@ def main():
|
|||||||
"manually delete the file", lock
|
"manually delete the file", lock
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
from PyQt4.Qt import QApplication, QMainWindow
|
from PyQt4.Qt import QApplication, QMainWindow
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
#from IPython.Shell import IPShellEmbed
|
#from IPython.Shell import IPShellEmbed
|
||||||
#ipshell = IPShellEmbed([],
|
#ipshell = IPShellEmbed([],
|
||||||
# banner = 'Dropping into IPython',
|
# banner = 'Dropping into IPython',
|
||||||
|
@ -626,6 +626,7 @@ class LibraryDatabase(object):
|
|||||||
return self.data[index][1]
|
return self.data[index][1]
|
||||||
|
|
||||||
def authors(self, index):
|
def authors(self, index):
|
||||||
|
''' Authors as a comman separated list or None'''
|
||||||
return self.data[index][2]
|
return self.data[index][2]
|
||||||
|
|
||||||
def publisher(self, index):
|
def publisher(self, index):
|
||||||
@ -641,6 +642,7 @@ class LibraryDatabase(object):
|
|||||||
return self.data[index][6]
|
return self.data[index][6]
|
||||||
|
|
||||||
def cover(self, index):
|
def cover(self, index):
|
||||||
|
'''Cover as a data string or None'''
|
||||||
id = self.id(index)
|
id = self.id(index)
|
||||||
matches = self.conn.execute('SELECT data from covers where id=?', (id,)).fetchall()
|
matches = self.conn.execute('SELECT data from covers where id=?', (id,)).fetchall()
|
||||||
if not matches:
|
if not matches:
|
||||||
@ -651,6 +653,7 @@ class LibraryDatabase(object):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def tags(self, index):
|
def tags(self, index):
|
||||||
|
'''tags as a comman separated list or None'''
|
||||||
id = self.id(index)
|
id = self.id(index)
|
||||||
matches = self.conn.execute('SELECT concat(name) FROM tags WHERE tags.id IN (SELECT tag from books_tags_link WHERE book=?)', (id,)).fetchall()
|
matches = self.conn.execute('SELECT concat(name) FROM tags WHERE tags.id IN (SELECT tag from books_tags_link WHERE book=?)', (id,)).fetchall()
|
||||||
if not matches:
|
if not matches:
|
||||||
@ -658,6 +661,7 @@ class LibraryDatabase(object):
|
|||||||
return matches[0][0]
|
return matches[0][0]
|
||||||
|
|
||||||
def comments(self, index):
|
def comments(self, index):
|
||||||
|
'''Comments as string or None'''
|
||||||
id = self.id(index)
|
id = self.id(index)
|
||||||
matches = self.conn.execute('SELECT text FROM comments WHERE book=?', (id,)).fetchall()
|
matches = self.conn.execute('SELECT text FROM comments WHERE book=?', (id,)).fetchall()
|
||||||
if not matches:
|
if not matches:
|
||||||
@ -678,7 +682,9 @@ class LibraryDatabase(object):
|
|||||||
|
|
||||||
|
|
||||||
def set(self, row, column, val):
|
def set(self, row, column, val):
|
||||||
''' Convenience method for setting the title, authors, publisher or rating '''
|
'''
|
||||||
|
Convenience method for setting the title, authors, publisher or rating
|
||||||
|
'''
|
||||||
id = self.data[row][0]
|
id = self.data[row][0]
|
||||||
cols = {'title' : 1, 'authors': 2, 'publisher': 3, 'rating':4}
|
cols = {'title' : 1, 'authors': 2, 'publisher': 3, 'rating':4}
|
||||||
col = cols[column]
|
col = cols[column]
|
||||||
@ -699,10 +705,14 @@ class LibraryDatabase(object):
|
|||||||
self.set_rating(id, val)
|
self.set_rating(id, val)
|
||||||
|
|
||||||
def set_authors(self, id, authors):
|
def set_authors(self, id, authors):
|
||||||
|
'''
|
||||||
|
@param authors: A list of authors.
|
||||||
|
'''
|
||||||
self.conn.execute('DELETE FROM books_authors_link WHERE book=?',(id,))
|
self.conn.execute('DELETE FROM books_authors_link WHERE book=?',(id,))
|
||||||
for a in authors:
|
for a in authors:
|
||||||
if not a:
|
if not a:
|
||||||
continue
|
continue
|
||||||
|
a = a.strip()
|
||||||
author = self.conn.execute('SELECT id from authors WHERE name=?', (a,)).fetchone()
|
author = self.conn.execute('SELECT id from authors WHERE name=?', (a,)).fetchone()
|
||||||
if author:
|
if author:
|
||||||
aid = author[0]
|
aid = author[0]
|
||||||
|
@ -80,6 +80,7 @@ def setup_completion():
|
|||||||
f.write(opts_and_exts('txt2lrf', txtop, ['txt']))
|
f.write(opts_and_exts('txt2lrf', txtop, ['txt']))
|
||||||
f.write(opts_and_exts('lit2lrf', htmlop, ['lit']))
|
f.write(opts_and_exts('lit2lrf', htmlop, ['lit']))
|
||||||
f.write(opts_and_exts('rtf2lrf', htmlop, ['rtf']))
|
f.write(opts_and_exts('rtf2lrf', htmlop, ['rtf']))
|
||||||
|
f.write(opts_and_exts('pdf2lrf', htmlop, ['pdf']))
|
||||||
f.write(opts_and_exts('lrf-meta', metaop, ['lrf']))
|
f.write(opts_and_exts('lrf-meta', metaop, ['lrf']))
|
||||||
f.write('''
|
f.write('''
|
||||||
_prs500_ls()
|
_prs500_ls()
|
||||||
|
@ -57,6 +57,7 @@ Var MUI_TEMP
|
|||||||
!define LIBUNRAR_DIR "C:\Program Files\UnrarDLL"
|
!define LIBUNRAR_DIR "C:\Program Files\UnrarDLL"
|
||||||
!define CLIT "C:\clit\clit.exe"
|
!define CLIT "C:\clit\clit.exe"
|
||||||
!define UNRTF "C:\unrtf\unrtf.exe"
|
!define UNRTF "C:\unrtf\unrtf.exe"
|
||||||
|
!define PDFTOHTML "C:\pdftohtml\pdftohtml.exe"
|
||||||
|
|
||||||
;------------------------------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------------------------------
|
||||||
;General
|
;General
|
||||||
@ -126,6 +127,7 @@ Section "libprs500" Seclibprs500
|
|||||||
File /r "${PY2EXE_DIR}\*"
|
File /r "${PY2EXE_DIR}\*"
|
||||||
File "${CLIT}"
|
File "${CLIT}"
|
||||||
File "${UNRTF}"
|
File "${UNRTF}"
|
||||||
|
File "${PDFTOHTML}"
|
||||||
|
|
||||||
SetOutPath "$INSTDIR\driver"
|
SetOutPath "$INSTDIR\driver"
|
||||||
File "${LIBUSB_DIR}\*.dll"
|
File "${LIBUSB_DIR}\*.dll"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user