From 126fec5c7d923304c79f7207a3dc757ceb77e081 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 16 Dec 2008 14:46:54 -0800 Subject: [PATCH 1/4] IGN:... --- src/calibre/ebooks/epub/from_html.py | 2 +- src/calibre/ebooks/lit/from_any.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/calibre/ebooks/epub/from_html.py b/src/calibre/ebooks/epub/from_html.py index 3552a1bf70..ae4be49ddb 100644 --- a/src/calibre/ebooks/epub/from_html.py +++ b/src/calibre/ebooks/epub/from_html.py @@ -62,7 +62,7 @@ def remove_bad_link(element, attribute, link, pos): def check(opf_path, pretty_print): ''' - Find a remove all invalid links in the HTML files + Find and remove all invalid links in the HTML files ''' logger = logging.getLogger('html2epub') logger.info('\tChecking files for bad links...') diff --git a/src/calibre/ebooks/lit/from_any.py b/src/calibre/ebooks/lit/from_any.py index 75cfc01bc2..cda70e189a 100644 --- a/src/calibre/ebooks/lit/from_any.py +++ b/src/calibre/ebooks/lit/from_any.py @@ -38,6 +38,7 @@ def any2lit(opts, path): os.mkdir(oebdir) opts.output = os.path.join(tdir, 'dummy.epub') opts.extract_to = oebdir + opts.profile = 'None' any2epub(opts, path) opf = glob.glob(os.path.join(oebdir, '*.opf'))[0] opts.output = orig_output From 9dcad3216a3fd0b98b1e555308c70145d7ac238e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 16 Dec 2008 15:19:02 -0800 Subject: [PATCH 2/4] Fix display of job output in GUI --- src/calibre/ebooks/epub/from_html.py | 4 ++-- src/calibre/gui2/jobs2.py | 2 +- src/calibre/parallel.py | 28 ++++++++++++++++------------ upload.py | 2 +- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/calibre/ebooks/epub/from_html.py b/src/calibre/ebooks/epub/from_html.py index ae4be49ddb..3763d99cc4 100644 --- a/src/calibre/ebooks/epub/from_html.py +++ b/src/calibre/ebooks/epub/from_html.py @@ -39,7 +39,7 @@ from lxml import html from PyQt4.Qt import QApplication, QPixmap from calibre.ebooks.html import Processor, merge_metadata, get_filelist,\ - opf_traverse, create_metadata, rebase_toc, Link + opf_traverse, create_metadata, rebase_toc, Link, parser from calibre.ebooks.epub import config as common_config, tostring from calibre.ptempfile import TemporaryDirectory from calibre.ebooks.metadata.toc import TOC @@ -77,7 +77,7 @@ def check(opf_path, pretty_print): for path in html_files: base = os.path.dirname(path) - root = html.fromstring(open(content(path), 'rb').read()) + root = html.fromstring(open(content(path), 'rb').read(), parser=parser) for element, attribute, link, pos in list(root.iterlinks()): link = to_unicode(link) plink = Link(link, base) diff --git a/src/calibre/gui2/jobs2.py b/src/calibre/gui2/jobs2.py index a0b7063c79..e5b17ef664 100644 --- a/src/calibre/gui2/jobs2.py +++ b/src/calibre/gui2/jobs2.py @@ -188,6 +188,6 @@ class DetailView(QDialog, Ui_Dialog): def update(self): - self.log.setPlainText(self.job.gui_text()) + self.log.setPlainText(self.job.console_text()) vbar = self.log.verticalScrollBar() vbar.setValue(vbar.maximum()) diff --git a/src/calibre/parallel.py b/src/calibre/parallel.py index fbc9ba3d9b..0b543ea27f 100644 --- a/src/calibre/parallel.py +++ b/src/calibre/parallel.py @@ -567,23 +567,27 @@ class Job(object): return 'ERROR' def console_text(self): - ans = [u'Error in job: '] + ans = [u'Job: '] if self.description: ans[0] += self.description + if self.exception is not None: + header = unicode(self.exception.__class__.__name__) if \ + hasattr(self.exception, '__class__') else u'Error' + header = u'**%s**'%header + header += u': ' + try: + header += unicode(self.exception) + except: + header += unicode(repr(self.exception)) + ans.append(header) + if self.traceback: + ans.append(u'**Traceback**:') + ans.extend(self.traceback.split('\n')) + if self.log: if isinstance(self.log, str): self.log = unicode(self.log, 'utf-8', 'replace') ans.append(self.log) - header = unicode(self.exception.__class__.__name__) if \ - hasattr(self.exception, '__class__') else u'Error' - header += u': ' - try: - header += unicode(self.exception) - except: - header += unicode(repr(self.exception)) - ans.append(header) - if self.traceback: - ans.append(self.traceback) return (u'\n'.join(ans)).encode('utf-8') def gui_text(self): @@ -611,7 +615,7 @@ class Job(object): self.log = unicode(self.log, 'utf-8', 'replace') ans.extend(self.log.split('\n')) - return '\n'.join(ans) + return '
'.join(ans) class ParallelJob(Job): diff --git a/upload.py b/upload.py index 583b32677c..4dc7d92d6b 100644 --- a/upload.py +++ b/upload.py @@ -213,7 +213,7 @@ def upload_src_tarball(): check_call('scp dist/calibre-*.tar.gz divok:%s/'%DOWNLOADS) def stage_one(): - check_call('sudo rm -rf build', shell=True) + check_call('sudo rm -rf build src/calibre/plugins/*', shell=True) os.mkdir('build') shutil.rmtree('docs') os.mkdir('docs') From 83086312a3b152effde2aece8c012492ac9f29ef Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 16 Dec 2008 18:10:05 -0800 Subject: [PATCH 3/4] Implement #1389 (Select Output Format at Conversion Time) --- src/calibre/gui2/dialogs/config.py | 6 --- src/calibre/gui2/dialogs/config.ui | 43 ++++------------- src/calibre/gui2/main.py | 17 ++++++- src/calibre/gui2/main.ui | 74 +++++++++++++++++------------- 4 files changed, 67 insertions(+), 73 deletions(-) diff --git a/src/calibre/gui2/dialogs/config.py b/src/calibre/gui2/dialogs/config.py index 4db64e465a..a4d60552c6 100644 --- a/src/calibre/gui2/dialogs/config.py +++ b/src/calibre/gui2/dialogs/config.py @@ -101,7 +101,6 @@ class ConfigDialog(QDialog, Ui_Dialog): for item in items: self.language.addItem(item[1], QVariant(item[0])) - self.output_format.setCurrentIndex(0 if prefs['output_format'] == 'LRF' else 1) self.pdf_metadata.setChecked(prefs['read_file_metadata']) added_html = False @@ -255,16 +254,11 @@ class ConfigDialog(QDialog, Ui_Dialog): sc.set('max_cover', mcs) config['delete_news_from_library_on_upload'] = self.delete_news.isChecked() config['upload_news_to_device'] = self.sync_news.isChecked() - of = str(self.output_format.currentText()) fmts = [] for i in range(self.viewer.count()): if self.viewer.item(i).checkState() == Qt.Checked: fmts.append(str(self.viewer.item(i).text())) config['internally_viewed_formats'] = fmts - if of != prefs['output_format'] and 'epub' in of.lower(): - warning_dialog(self, 'Warning', - '

EPUB support is still in beta. If you find bugs, please report them by opening a ticket.').exec_() - prefs['output_format'] = of if not path or not os.path.exists(path) or not os.path.isdir(path): d = error_dialog(self, _('Invalid database location'), diff --git a/src/calibre/gui2/dialogs/config.ui b/src/calibre/gui2/dialogs/config.ui index fb6dc3df06..2def8741c1 100644 --- a/src/calibre/gui2/dialogs/config.ui +++ b/src/calibre/gui2/dialogs/config.ui @@ -149,7 +149,7 @@ - + Format for &single file save: @@ -159,10 +159,10 @@ - + - + Default network &timeout: @@ -172,7 +172,7 @@ - + Set the default timeout for network fetches (i.e. anytime we go out to the internet to get information) @@ -191,10 +191,10 @@ - + - + Choose &language (requires restart): @@ -204,34 +204,7 @@ - - - - The default output format for ebook conversions. - - - - LRF - - - - - EPUB - - - - - - - - &Output format: - - - output_format - - - - + @@ -250,7 +223,7 @@ - + Job &priority: diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index f0a0ad1d40..12b4e99ee6 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -126,6 +126,21 @@ class Main(MainWindow, Ui_MainWindow): self.location_selected) QObject.connect(self.stack, SIGNAL('currentChanged(int)'), self.location_view.location_changed) + + self.output_formats = sorted(['EPUB', 'LRF']) + for f in self.output_formats: + self.output_format.addItem(f) + self.output_format.setCurrentIndex(self.output_formats.index(prefs['output_format'])) + def change_output_format(x): + of = unicode(x).strip() + if of != prefs['output_format']: + if of in ('EPUB', 'LIT'): + warning_dialog(self, 'Warning', + '

%s support is still in beta. If you find bugs, please report them by opening a ticket.'%of).exec_() + prefs.set('output_format', of) + + self.connect(self.output_format, SIGNAL('currentIndexChanged(QString)'), + change_output_format) ####################### Vanity ######################## self.vanity_template = _('

For help visit %s.kovidgoyal.net
')%(__appname__, __appname__) @@ -489,7 +504,7 @@ class Main(MainWindow, Ui_MainWindow): return info, cp, fs = job.result self.location_view.model().update_devices(cp, fs) - self.device_info = _('Connected ')+' '.join(info[:-1]) + self.device_info = _('Connected ')+info[0] self.vanity.setText(self.vanity_template%dict(version=self.latest_version, device=self.device_info)) self.device_manager.books(Dispatcher(self.metadata_downloaded)) diff --git a/src/calibre/gui2/main.ui b/src/calibre/gui2/main.ui index 1833789f61..2733a61be3 100644 --- a/src/calibre/gui2/main.ui +++ b/src/calibre/gui2/main.ui @@ -27,15 +27,9 @@ :/library:/library - + - - - 6 - - - 0 - + @@ -89,29 +83,47 @@ - - - - 0 - 0 - - - - - 16777215 - 90 - - - - - - - Qt::RichText - - - true - - + + + + + + 0 + 0 + + + + + 16777215 + 90 + + + + + + + Qt::RichText + + + true + + + + + + + + + Output: + + + + + + + + + From ba0ae514574f9e3902d1bff800df7c7ff0a671d8 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 17 Dec 2008 00:02:50 -0800 Subject: [PATCH 4/4] IGN:Improve handling of covers in any2lit --- installer/linux/freeze.py | 4 +- src/calibre/ebooks/epub/from_any.py | 7 ++- src/calibre/ebooks/epub/from_html.py | 75 ++++++++++++++++++---------- src/calibre/ebooks/lit/from_any.py | 3 +- src/calibre/ebooks/lit/writer.py | 3 ++ 5 files changed, 62 insertions(+), 30 deletions(-) diff --git a/installer/linux/freeze.py b/installer/linux/freeze.py index 7fa6e4bbd6..4fdeb69c37 100644 --- a/installer/linux/freeze.py +++ b/installer/linux/freeze.py @@ -122,6 +122,8 @@ def freeze(): elif exe not in executables: print >>sys.stderr, 'Invalid invocation of calibre loader. CALIBRE_CX_EXE=%%s is unknown'%%exe else: + from PyQt4.QtCore import QCoreApplication + QCoreApplication.setLibraryPaths([sys.frozen_path, os.path.join(sys.frozen_path, "qtplugins")]) sys.argv[0] = exe module, func = executables[exe] module = __import__(module, fromlist=[1]) @@ -179,7 +181,7 @@ def freeze(): if not f.endswith('.so') or 'designer' in dirpath or 'codecs' in dirpath or 'sqldrivers' in dirpath: continue f = os.path.join(dirpath, f) - dest_dir = dirpath.replace(plugdir, os.path.join(FREEZE_DIR, 'qtlugins')) + dest_dir = dirpath.replace(plugdir, os.path.join(FREEZE_DIR, 'qtplugins')) copy_binary(f, dest_dir) print 'Creating launchers' diff --git a/src/calibre/ebooks/epub/from_any.py b/src/calibre/ebooks/epub/from_any.py index 6340180562..6cf56aa43c 100644 --- a/src/calibre/ebooks/epub/from_any.py +++ b/src/calibre/ebooks/epub/from_any.py @@ -116,7 +116,8 @@ def unarchive(path, tdir): return f, ext return find_html_index(files) -def any2epub(opts, path, notification=None): +def any2epub(opts, path, notification=None, create_epub=True, + oeb_cover=False, extract_to=None): ext = os.path.splitext(path)[1] if not ext: raise ValueError('Unknown file type: '+path) @@ -139,7 +140,9 @@ def any2epub(opts, path, notification=None): raise ValueError('Conversion from %s is not supported'%ext.upper()) print 'Creating EPUB file...' - html2epub(path, opts, notification=notification) + html2epub(path, opts, notification=notification, + create_epub=create_epub, oeb_cover=oeb_cover, + extract_to=extract_to) def config(defaults=None): return common_config(defaults=defaults) diff --git a/src/calibre/ebooks/epub/from_html.py b/src/calibre/ebooks/epub/from_html.py index 3763d99cc4..d62bb936b2 100644 --- a/src/calibre/ebooks/epub/from_html.py +++ b/src/calibre/ebooks/epub/from_html.py @@ -32,7 +32,7 @@ Conversion of HTML/OPF files follows several stages: * The EPUB container is created. ''' -import os, sys, cStringIO, logging, re, functools +import os, sys, cStringIO, logging, re, functools, shutil from lxml.etree import XPath from lxml import html @@ -210,17 +210,16 @@ TITLEPAGE = '''\ ''' -def create_cover_image(src, dest, screen_size): - from PyQt4.Qt import QApplication, QImage, Qt - if QApplication.instance() is None: - app = QApplication([]) - app - im = QImage() +def create_cover_image(src, dest, screen_size, rescale_cover=True): try: + from PyQt4.Qt import QImage, Qt + if QApplication.instance() is None: + QApplication([]) + im = QImage() im.load(src) if im.isNull(): - raise ValueError - if screen_size is not None: + raise ValueError('Invalid cover image') + if rescale_cover and screen_size is not None: width, height = im.width(), im.height() dw, dh = (screen_size[0]-width)/float(width), (screen_size[1]-height)/float(height) delta = min(dw, dh) @@ -228,7 +227,7 @@ def create_cover_image(src, dest, screen_size): nwidth = int(width + delta*(width)) nheight = int(height + delta*(height)) im = im.scaled(int(nwidth), int(nheight), Qt.IgnoreAspectRatio, Qt.SmoothTransformation) - im.save(dest) + im.save(dest) except: import traceback traceback.print_exc() @@ -241,7 +240,6 @@ def process_title_page(mi, filelist, htmlfilemap, opts, tdir): if mi.cover: if f(filelist[0].path) == f(mi.cover): old_title_page = htmlfilemap[filelist[0].path] - #logger = logging.getLogger('html2epub') metadata_cover = mi.cover if metadata_cover and not os.path.exists(metadata_cover): @@ -250,14 +248,15 @@ def process_title_page(mi, filelist, htmlfilemap, opts, tdir): cpath = '/'.join(('resources', '_cover_.jpg')) cover_dest = os.path.join(tdir, 'content', *cpath.split('/')) if metadata_cover is not None: - if not create_cover_image(metadata_cover, cover_dest, opts.profile.screen_size): + if not create_cover_image(metadata_cover, cover_dest, + opts.profile.screen_size): metadata_cover = None - specified_cover = opts.cover if specified_cover and not os.path.exists(specified_cover): specified_cover = None if specified_cover is not None: - if not create_cover_image(specified_cover, cover_dest, opts.profile.screen_size): + if not create_cover_image(specified_cover, cover_dest, + opts.profile.screen_size): specified_cover = None cover = metadata_cover if specified_cover is None or (opts.prefer_metadata_cover and metadata_cover is not None) else specified_cover @@ -272,9 +271,16 @@ def process_title_page(mi, filelist, htmlfilemap, opts, tdir): elif os.path.exists(cover_dest): os.remove(cover_dest) return None, old_title_page is not None - -def convert(htmlfile, opts, notification=None): +def find_oeb_cover(htmlfile): + if os.stat(htmlfile).st_size > 2048: + return None + match = re.search(r'(?i)]+src\s*=\s*[\'"](.+?)[\'"]', open(htmlfile, 'rb').read()) + if match: + return match.group(1) + +def convert(htmlfile, opts, notification=None, create_epub=True, + oeb_cover=False, extract_to=None): htmlfile = os.path.abspath(htmlfile) if opts.output is None: opts.output = os.path.splitext(os.path.basename(htmlfile))[0] + '.epub' @@ -326,7 +332,7 @@ def convert(htmlfile, opts, notification=None): title_page, has_title_page = process_title_page(mi, filelist, htmlfile_map, opts, tdir) spine = [htmlfile_map[f.path] for f in filelist] - if title_page is not None: + if not oeb_cover and title_page is not None: spine = [title_page] + spine mi.cover = None mi.cover_data = (None, None) @@ -358,24 +364,43 @@ def convert(htmlfile, opts, notification=None): check(opf_path, opts.pretty_print) opf = OPF(opf_path, tdir) opf.remove_guide() - if has_title_page: + oeb_cover_file = None + if oeb_cover and title_page is not None: + oeb_cover_file = find_oeb_cover(os.path.join(tdir, 'content', title_page)) + if has_title_page or (oeb_cover and oeb_cover_file): opf.create_guide_element() - opf.add_guide_item('cover', 'Cover', 'content/'+spine[0]) + if has_title_page and not oeb_cover: + opf.add_guide_item('cover', 'Cover', 'content/'+spine[0]) + if oeb_cover and oeb_cover_file: + opf.add_guide_item('cover', 'Cover', 'content/'+oeb_cover_file) - opf.add_path_to_manifest(os.path.join(tdir, 'content', 'resources', '_cover_.jpg'), 'image/jpeg') + cpath = os.path.join(tdir, 'content', 'resources', '_cover_.jpg') + if os.path.exists(cpath): + opf.add_path_to_manifest(cpath, 'image/jpeg') with open(opf_path, 'wb') as f: raw = opf.render() if not raw.startswith('\n'+raw f.write(raw) - epub = initialize_container(opts.output) - epub.add_dir(tdir) + if create_epub: + epub = initialize_container(opts.output) + epub.add_dir(tdir) + epub.close() + logger.info(_('Output written to ')+opts.output) + if opts.show_opf: print open(os.path.join(tdir, 'metadata.opf')).read() - logger.info('Output written to %s'%opts.output) + if opts.extract_to is not None: - epub.extractall(opts.extract_to) - epub.close() + if os.path.exists(opts.extract_to): + shutil.rmtree(opts.extract_to) + shutil.copytree(tdir, opts.extract_to) + + if extract_to is not None: + if os.path.exists(extract_to): + shutil.rmtree(extract_to) + shutil.copytree(tdir, extract_to) + def main(args=sys.argv): diff --git a/src/calibre/ebooks/lit/from_any.py b/src/calibre/ebooks/lit/from_any.py index cda70e189a..cd8b5a115c 100644 --- a/src/calibre/ebooks/lit/from_any.py +++ b/src/calibre/ebooks/lit/from_any.py @@ -37,9 +37,8 @@ def any2lit(opts, path): oebdir = os.path.join(tdir, 'oeb') os.mkdir(oebdir) opts.output = os.path.join(tdir, 'dummy.epub') - opts.extract_to = oebdir opts.profile = 'None' - any2epub(opts, path) + any2epub(opts, path, create_epub=False, oeb_cover=True, extract_to=oebdir) opf = glob.glob(os.path.join(oebdir, '*.opf'))[0] opts.output = orig_output logging.getLogger('html2epub').info(_('Creating LIT file from EPUB...')) diff --git a/src/calibre/ebooks/lit/writer.py b/src/calibre/ebooks/lit/writer.py index 4d8a76fff2..8e2f93f845 100644 --- a/src/calibre/ebooks/lit/writer.py +++ b/src/calibre/ebooks/lit/writer.py @@ -313,6 +313,9 @@ class LitWriter(object): elif MS_COVER_TYPE in oeb.guide: href = oeb.guide[MS_COVER_TYPE].href cover = oeb.manifest.hrefs[href] + elif 'cover' in oeb.guide: + href = oeb.guide['cover'].href + cover = oeb.manifest.hrefs[href] else: html = oeb.spine[0].data imgs = xpath(html, '//img[position()=1]')