Merge from trunk

This commit is contained in:
Charles Haley 2013-01-30 09:59:49 +01:00
commit efa9a64625
5 changed files with 173 additions and 8 deletions

View File

@ -15,6 +15,14 @@ from calibre.utils.ipc.simple_worker import fork_job, WorkerError
#_isbn_pat = re.compile(r'ISBN[: ]*([-0-9Xx]+)') #_isbn_pat = re.compile(r'ISBN[: ]*([-0-9Xx]+)')
def get_tools():
from calibre.ebooks.pdf.pdftohtml import PDFTOHTML
base = os.path.dirname(PDFTOHTML)
suffix = '.exe' if iswindows else ''
pdfinfo = os.path.join(base, 'pdfinfo') + suffix
pdftoppm = os.path.join(base, 'pdftoppm') + suffix
return pdfinfo, pdftoppm
def read_info(outputdir, get_cover): def read_info(outputdir, get_cover):
''' Read info dict and cover from a pdf file named src.pdf in outputdir. ''' Read info dict and cover from a pdf file named src.pdf in outputdir.
Note that this function changes the cwd to outputdir and is therefore not Note that this function changes the cwd to outputdir and is therefore not
@ -22,13 +30,8 @@ def read_info(outputdir, get_cover):
way to pass unicode paths via command line arguments. This also ensures way to pass unicode paths via command line arguments. This also ensures
that if poppler crashes, no stale file handles are left for the original that if poppler crashes, no stale file handles are left for the original
file, only for src.pdf.''' file, only for src.pdf.'''
from calibre.ebooks.pdf.pdftohtml import PDFTOHTML
os.chdir(outputdir) os.chdir(outputdir)
base = os.path.dirname(PDFTOHTML) pdfinfo, pdftoppm = get_tools()
suffix = '.exe' if iswindows else ''
pdfinfo = os.path.join(base, 'pdfinfo') + suffix
pdftoppm = os.path.join(base, 'pdftoppm') + suffix
try: try:
raw = subprocess.check_output([pdfinfo, '-enc', 'UTF-8', 'src.pdf']) raw = subprocess.check_output([pdfinfo, '-enc', 'UTF-8', 'src.pdf'])
@ -58,6 +61,20 @@ def read_info(outputdir, get_cover):
return ans return ans
def page_images(pdfpath, outputdir, first=1, last=1):
pdftoppm = get_tools()[1]
outputdir = os.path.abspath(outputdir)
args = {}
if iswindows:
import win32process as w
args['creationflags'] = w.HIGH_PRIORITY_CLASS | w.CREATE_NO_WINDOW
try:
subprocess.check_call([pdftoppm, '-jpeg', '-f', unicode(first),
'-l', unicode(last), pdfpath,
os.path.join(outputdir, 'page-images')], **args)
except subprocess.CalledProcessError as e:
raise ValueError('Failed to render PDF, pdftoppm errorcode: %s'%e.returncode)
def get_metadata(stream, cover=True): def get_metadata(stream, cover=True):
with TemporaryDirectory('_pdf_metadata_read') as pdfpath: with TemporaryDirectory('_pdf_metadata_read') as pdfpath:
stream.seek(0) stream.seek(0)

View File

@ -819,6 +819,27 @@ class FormatsManager(QWidget):
def show_format(self, item, *args): def show_format(self, item, *args):
self.dialog.do_view_format(item.path, item.ext) self.dialog.do_view_format(item.path, item.ext)
def get_selected_format(self):
row = self.formats.currentRow()
fmt = self.formats.item(row)
if fmt is None:
if self.formats.count() == 1:
fmt = self.formats.item(0)
if fmt is None:
error_dialog(self, _('No format selected'),
_('No format selected')).exec_()
return None
return fmt.ext.lower()
def get_format_path(self, db, id_, fmt):
for i in xrange(self.formats.count()):
f = self.formats.item(i)
ext = f.ext.lower()
if ext == fmt:
if f.path is None:
return db.format(id_, ext, as_path=True, index_is_id=True)
return f.path
def get_selected_format_metadata(self, db, id_): def get_selected_format_metadata(self, db, id_):
old = prefs['read_file_metadata'] old = prefs['read_file_metadata']
if not old: if not old:

View File

@ -0,0 +1,108 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai
from __future__ import (unicode_literals, division, absolute_import,
print_function)
__license__ = 'GPL v3'
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import sys, shutil, os
from threading import Thread
from glob import glob
from PyQt4.Qt import (QDialog, QApplication, QLabel, QGridLayout,
QDialogButtonBox, Qt, pyqtSignal, QListWidget,
QListWidgetItem, QSize, QIcon)
from calibre import as_unicode
from calibre.ebooks.metadata.pdf import page_images
from calibre.gui2 import error_dialog, file_icon_provider
from calibre.ptempfile import PersistentTemporaryDirectory
class PDFCovers(QDialog):
rendering_done = pyqtSignal()
def __init__(self, pdfpath, parent=None):
QDialog.__init__(self, parent)
self.pdfpath = pdfpath
self.l = l = QGridLayout()
self.setLayout(l)
self.la = la = QLabel(_('Choose a cover from the list of PDF pages below'))
l.addWidget(la)
self.loading = la = QLabel('<b>'+_('Rendering PDF pages, please wait...'))
l.addWidget(la)
self.covers = c = QListWidget(self)
l.addWidget(c)
c.setIconSize(QSize(120, 160))
c.setSelectionMode(c.SingleSelection)
c.setViewMode(c.IconMode)
c.setUniformItemSizes(True)
c.setResizeMode(c.Adjust)
c.itemDoubleClicked.connect(self.accept)
self.bb = bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel)
bb.accepted.connect(self.accept)
bb.rejected.connect(self.reject)
l.addWidget(bb)
self.rendering_done.connect(self.show_pages, type=Qt.QueuedConnection)
self.tdir = PersistentTemporaryDirectory('_pdf_covers')
self.thread = Thread(target=self.render)
self.thread.daemon = True
self.thread.start()
self.setWindowTitle(_('Choose cover from PDF'))
self.setWindowIcon(file_icon_provider().icon_from_ext('pdf'))
self.resize(QSize(800, 600))
@property
def cover_path(self):
for item in self.covers.selectedItems():
return unicode(item.data(Qt.UserRole).toString())
if self.covers.count() > 0:
return unicode(self.covers.item(0).data(Qt.UserRole).toString())
def cleanup(self):
try:
shutil.rmtree(self.tdir)
except:
pass
def render(self):
self.error = None
try:
page_images(self.pdfpath, self.tdir, last=10)
except Exception as e:
self.error = as_unicode(e)
if self.isVisible():
self.rendering_done.emit()
def show_pages(self):
self.loading.setVisible(False)
if self.error is not None:
error_dialog(self, _('Failed to render'),
_('Could not render this PDF file'), show=True)
self.reject()
return
files = (glob(os.path.join(self.tdir, '*.jpg')) +
glob(os.path.join(self.tdir, '*.jpeg')))
if not files:
error_dialog(self, _('Failed to render'),
_('This PDF has no pages'), show=True)
self.reject()
return
for f in sorted(files):
i = QListWidgetItem(QIcon(f), '')
i.setData(Qt.UserRole, f)
self.covers.addItem(i)
if __name__ == '__main__':
app = QApplication([])
app
d = PDFCovers(sys.argv[-1])
d.exec_()
print (d.cover_path)

View File

@ -318,7 +318,23 @@ class MetadataSingleDialogBase(ResizableDialog):
if mi is not None: if mi is not None:
self.update_from_mi(mi) self.update_from_mi(mi)
def get_pdf_cover(self):
pdfpath = self.formats_manager.get_format_path(self.db, self.book_id,
'pdf')
from calibre.gui2.metadata.pdf_covers import PDFCovers
d = self.__pdf_covers = PDFCovers(pdfpath, parent=self)
if d.exec_() == d.Accepted:
cpath = d.cover_path
if cpath:
with open(cpath, 'rb') as f:
self.update_cover(f.read(), 'PDF')
d.cleanup()
def cover_from_format(self, *args): def cover_from_format(self, *args):
ext = self.formats_manager.get_selected_format()
if ext is None: return
if ext == 'pdf':
return self.get_pdf_cover()
try: try:
mi, ext = self.formats_manager.get_selected_format_metadata(self.db, mi, ext = self.formats_manager.get_selected_format_metadata(self.db,
self.book_id) self.book_id)
@ -343,12 +359,15 @@ class MetadataSingleDialogBase(ResizableDialog):
error_dialog(self, _('Could not read cover'), error_dialog(self, _('Could not read cover'),
_('Could not read cover from %s format')%ext).exec_() _('Could not read cover from %s format')%ext).exec_()
return return
self.update_cover(cdata, ext)
def update_cover(self, cdata, fmt):
orig = self.cover.current_val orig = self.cover.current_val
self.cover.current_val = cdata self.cover.current_val = cdata
if self.cover.current_val is None: if self.cover.current_val is None:
self.cover.current_val = orig self.cover.current_val = orig
return error_dialog(self, _('Could not read cover'), return error_dialog(self, _('Could not read cover'),
_('The cover in the %s format is invalid')%ext, _('The cover in the %s format is invalid')%fmt,
show=True) show=True)
return return

View File

@ -290,7 +290,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
self.update_font_display() self.update_font_display()
gui.tags_view.reread_collapse_parameters() gui.tags_view.reread_collapse_parameters()
gui.library_view.refresh_book_details() gui.library_view.refresh_book_details()
if gui.cover_flow is not None: if hasattr(gui.cover_flow, 'setShowReflections'):
gui.cover_flow.setShowReflections(gprefs['cover_browser_reflections']) gui.cover_flow.setShowReflections(gprefs['cover_browser_reflections'])
if __name__ == '__main__': if __name__ == '__main__':