diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py index acd5d2e930..5c55632da9 100644 --- a/src/calibre/__init__.py +++ b/src/calibre/__init__.py @@ -30,7 +30,12 @@ islinux = not(iswindows or isosx) try: locale.setlocale(locale.LC_ALL, '') except: - pass + dl = locale.getdefaultlocale() + try: + if dl: + locale.setlocale(dl[0]) + except: + pass try: preferred_encoding = locale.getpreferredencoding() diff --git a/src/calibre/ebooks/lrf/fb2/convert_from.py b/src/calibre/ebooks/lrf/fb2/convert_from.py index 29ceaa11cf..27c55757be 100644 --- a/src/calibre/ebooks/lrf/fb2/convert_from.py +++ b/src/calibre/ebooks/lrf/fb2/convert_from.py @@ -3,15 +3,13 @@ __copyright__ = '2008, Anatoly Shipitsin ' """ Convert .fb2 files to .lrf """ -import os, sys, tempfile, subprocess, shutil, logging, glob +import os, sys, tempfile, shutil, logging +from base64 import b64decode -from calibre.ptempfile import PersistentTemporaryFile from calibre.ebooks.lrf import option_parser as lrf_option_parser from calibre.ebooks.metadata.meta import get_metadata -from calibre.ebooks import ConversionError from calibre.ebooks.lrf.html.convert_from import process_file as html_process_file from calibre import setup_cli_handlers, __appname__ -from calibre.ebooks.BeautifulSoup import BeautifulStoneSoup from calibre.resources import fb2_xsl def option_parser(): @@ -22,25 +20,27 @@ _('''%prog [options] mybook.fb2 %prog converts mybook.fb2 to mybook.lrf''')) parser.add_option('--debug-html-generation', action='store_true', default=False, dest='debug_html_generation', help=_('Print generated HTML to stdout and quit.')) + parser.add_option('--keep-intermediate-files', action='store_true', default=False, + help=_('Keep generated HTML files after completing conversion to LRF.')) return parser +def extract_embedded_content(doc): + for elem in doc.xpath('./*'): + if 'binary' in elem.tag and elem.attrib.has_key('id'): + fname = elem.attrib['id'] + data = b64decode(elem.text.strip()) + open(fname, 'wb').write(data) def generate_html(fb2file, encoding, logger): from lxml import etree - tdir = tempfile.mkdtemp(prefix=__appname__+'_') - ofile = os.path.join(tdir, 'index.xml') + tdir = tempfile.mkdtemp(prefix=__appname__+'_fb2_') cwd = os.getcwdu() os.chdir(tdir) try: logger.info('Parsing XML...') parser = etree.XMLParser(recover=True, no_network=True) - try: - doc = etree.parse(fb2file, parser) - except: - raise - logger.info('Parsing failed. Trying to clean up XML...') - soup = BeautifulStoneSoup(open(fb2file, 'rb').read()) - doc = etree.fromstring(str(soup)) + doc = etree.parse(fb2file, parser) + extract_embedded_content(doc) logger.info('Converting XML to HTML...') styledoc = etree.fromstring(fb2_xsl) @@ -72,7 +72,7 @@ def process_file(path, options, logger=None): options.output = os.path.abspath(os.path.basename(os.path.splitext(path)[0]) + ext) options.output = os.path.abspath(os.path.expanduser(options.output)) if not mi.title: - mi.title = os.path.splitext(os.path.basename(rtf))[0] + mi.title = os.path.splitext(os.path.basename(fb2))[0] if (not options.title or options.title == 'Unknown'): options.title = mi.title if (not options.author or options.author == 'Unknown') and mi.authors: @@ -85,7 +85,7 @@ def process_file(path, options, logger=None): html_process_file(htmlfile, options, logger) finally: os.chdir(cwd) - if hasattr(options, 'keep_intermediate_files') and options.keep_intermediate_files: + if getattr(options, 'keep_intermediate_files', False): logger.debug('Intermediate files in '+ tdir) else: shutil.rmtree(tdir) diff --git a/src/calibre/ebooks/lrf/fb2/fb2.xsl b/src/calibre/ebooks/lrf/fb2/fb2.xsl index 75f3c245ed..7a977aee76 100644 --- a/src/calibre/ebooks/lrf/fb2/fb2.xsl +++ b/src/calibre/ebooks/lrf/fb2/fb2.xsl @@ -128,21 +128,40 @@ - - - - - - - + + + + None + + + + + + + + + + + + + + + + + + + + TOC_ + + @@ -166,7 +185,9 @@ -

+ + +
diff --git a/src/calibre/ebooks/metadata/fb2.py b/src/calibre/ebooks/metadata/fb2.py index 5bffb47409..672fc3e9ee 100644 --- a/src/calibre/ebooks/metadata/fb2.py +++ b/src/calibre/ebooks/metadata/fb2.py @@ -5,7 +5,8 @@ __copyright__ = '2008, Anatoly Shipitsin ' '''Read meta information from fb2 files''' -import sys, os +import sys, os, mimetypes +from base64 import b64decode from calibre.ebooks.BeautifulSoup import BeautifulStoneSoup from calibre.ebooks.metadata import MetaInformation @@ -18,15 +19,30 @@ def get_metadata(stream): author= [firstname+" "+lastname] title = soup.find("book-title").string comments = soup.find("annotation") + cp = soup.find('coverpage') + cdata = None + if cp: + cimage = cp.find('image', attrs={'l:href':True}) + if cimage: + id = cimage['l:href'].replace('#', '') + binary = soup.find('binary', id=id, attrs={'content-type':True}) + if binary: + mt = binary['content-type'] + exts = mimetypes.guess_all_extensions(mt) + if not exts: + exts = ['.jpg'] + cdata = (exts[0][1:], b64decode(binary.string.strip())) + if comments and len(comments) > 1: - comments = comments.p.contents[0] + comments = comments.p.contents[0] series = soup.find("sequence") - # series_index = series.index mi = MetaInformation(title, author) mi.comments = comments + mi.author_sort = lastname+'; '+firstname if series: mi.series = series.get('name', None) - # mi.series_index = series_index + if cdata: + mi.cover_data = cdata return mi def main(args=sys.argv): diff --git a/src/calibre/ebooks/metadata/toc.py b/src/calibre/ebooks/metadata/toc.py index a966dd6fae..dc039a7f80 100644 --- a/src/calibre/ebooks/metadata/toc.py +++ b/src/calibre/ebooks/metadata/toc.py @@ -70,7 +70,7 @@ class TOC(list): break if toc is not None: - if toc.lower() != 'ncx': + if toc.lower() not in ('ncx', 'ncxtoc'): toc = urlparse(unquote(toc))[2] toc = toc.replace('/', os.sep) if not os.path.isabs(toc): @@ -88,6 +88,10 @@ class TOC(list): traceback.print_exc(file=sys.stdout) print 'Continuing anyway' else: + path = opfreader.manifest.item(toc.lower()) + if path and os.access(path, os.R_OK): + self.read_ncx_toc(path) + return cwd = os.path.abspath(self.base_path) m = glob.glob(os.path.join(cwd, '*.ncx')) if m: diff --git a/src/calibre/gui2/library.py b/src/calibre/gui2/library.py index 70f92e2805..76527c3034 100644 --- a/src/calibre/gui2/library.py +++ b/src/calibre/gui2/library.py @@ -574,7 +574,7 @@ class DeviceBooksModel(BooksModel): for row in rows: if not succeeded: indices = self.row_indices(self.index(row, 0)) - self.emit(SIGNAL('dataChanged(QModelIndex, QModelIndex)'), indices[0], indices[-1]) + self.emit(SIGNAL('dataChanged(QModelIndex, QModelIndex)'), indices[0], indices[-1]) def paths_deleted(self, paths): self.map = list(range(0, len(self.db))) @@ -691,7 +691,7 @@ class DeviceBooksModel(BooksModel): dt = item.datetime dt = datetime(*dt[0:6]) dt = dt - timedelta(seconds=time.timezone) + timedelta(hours=time.daylight) - data[_('Timestamp')] = dt.ctime() + data[_('Timestamp')] = dt.strftime('%a %b %d %H:%M:%S %Y') data[_('Tags')] = ', '.join(item.tags) self.emit(SIGNAL('new_bookdisplay_data(PyQt_PyObject)'), data) diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index d09922848f..5dafe3c683 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -59,6 +59,9 @@ class Main(MainWindow, Ui_MainWindow): def __init__(self, single_instance, opts, parent=None): MainWindow.__init__(self, opts, parent) + # Initialize fontconfig in a separate thread as this can be a lengthy + # process if run for the first time on this machine + self.fc = __import__('calibre.utils.fontconfig', fromlist=1) self.single_instance = single_instance if self.single_instance is not None: self.connect(self.single_instance, SIGNAL('message_received(PyQt_PyObject)'), diff --git a/src/calibre/library/cli.py b/src/calibre/library/cli.py index 6e4ceb95b8..0e972cb20b 100644 --- a/src/calibre/library/cli.py +++ b/src/calibre/library/cli.py @@ -170,7 +170,8 @@ def do_add(db, paths, one_book_per_directory, recurse, add_duplicates): for mi, formats in dir_dups: db.import_book(mi, formats) else: - print >>sys.stderr, _('The following books were not added as they already exist in the database (see --duplicates option):') + if dir_dups or file_duplicates: + print >>sys.stderr, _('The following books were not added as they already exist in the database (see --duplicates option):') for mi, formats in dir_dups: title = mi.title if isinstance(title, unicode): diff --git a/src/calibre/linux_installer.py b/src/calibre/linux_installer.py index db9c8dd1b1..145b255b05 100644 --- a/src/calibre/linux_installer.py +++ b/src/calibre/linux_installer.py @@ -240,13 +240,21 @@ def do_postinstall(destdir): os.chdir(cwd) def download_tarball(): - pb = ProgressBar(TerminalController(sys.stdout), 'Downloading calibre...') + try: + pb = ProgressBar(TerminalController(sys.stdout), 'Downloading calibre...') + except ValueError: + print 'Downloading calibre...' + pb = None src = urllib2.urlopen(MOBILEREAD+'calibre-%version-i686.tar.bz2') size = int(src.info()['content-length']) f = tempfile.NamedTemporaryFile() while f.tell() < size: f.write(src.read(4*1024)) - pb.update(f.tell()/float(size)) + percent = f.tell()/float(size) + if pb is not None: + pb.update(percent) + else: + print '%d%%, '%int(percent*100), f.seek(0) return f @@ -269,4 +277,4 @@ def main(args=sys.argv): return 0 if __name__ == '__main__': - sys.exit(main()) \ No newline at end of file + sys.exit(main()) diff --git a/src/calibre/trac/plugins/Changelog.py b/src/calibre/trac/plugins/Changelog.py index 35c54b1bda..ff15329859 100644 --- a/src/calibre/trac/plugins/Changelog.py +++ b/src/calibre/trac/plugins/Changelog.py @@ -69,4 +69,4 @@ class ChangeLogMacro(WikiMacroBase): if __name__ == '__main__': print bzr_log_to_txt() - \ No newline at end of file + diff --git a/src/calibre/trac/plugins/templates/pyinstaller.html b/src/calibre/trac/plugins/templates/pyinstaller.html index c04fd13da4..4061b0aadc 100644 --- a/src/calibre/trac/plugins/templates/pyinstaller.html +++ b/src/calibre/trac/plugins/templates/pyinstaller.html @@ -44,4 +44,4 @@ - \ No newline at end of file + diff --git a/src/calibre/utils/fontconfig.py b/src/calibre/utils/fontconfig.py index 3a92a76670..3e74362720 100644 --- a/src/calibre/utils/fontconfig.py +++ b/src/calibre/utils/fontconfig.py @@ -33,7 +33,7 @@ except: preferred_encoding = 'utf-8' iswindows = 'win32' in sys.platform or 'win64' in sys.platform -isosx = 'darwin' in sys.platform +isosx = 'darwin' in sys.platform def load_library(): if isosx: @@ -94,7 +94,7 @@ class FcValue(Structure): _fields_ = [ ('type', c_int), ('u', _FcValue) - ] + ] lib = load_library() lib.FcPatternCreate.restype = c_void_p @@ -128,30 +128,49 @@ lib.FcConfigParseAndLoad.restype = c_int lib.FcConfigBuildFonts.argtypes = [c_void_p] lib.FcConfigBuildFonts.restype = c_int +_init_error = None +_initialized = False +from threading import Timer +def _do_init(): + # Initialize the fontconfig library. This has to be done manually + # for the OS X bundle as it may have its own private fontconfig. + if hasattr(sys, 'frameworks_dir'): + config_dir = os.path.join(os.path.dirname(getattr(sys, 'frameworks_dir')), 'Resources', 'fonts') + if isinstance(config_dir, unicode): + config_dir = config_dir.encode(sys.getfilesystemencoding()) + config = lib.FcConfigCreate() + if not lib.FcConfigParseAndLoad(config, os.path.join(config_dir, 'fonts.conf'), 1): + _init_error = 'Could not parse the fontconfig configuration' + return + if not lib.FcConfigBuildFonts(config): + _init_error = 'Could not build fonts' + return + if not lib.FcConfigSetCurrent(config): + _init_error = 'Could not set font config' + return + elif not lib.FcInit(): + _init_error = _('Could not initialize the fontconfig library') + return + global _initialized + _initialized = True + + +_init_timer = Timer(0.1, _do_init) +_init_timer.start() -# Initialize the fontconfig library. This has to be done manually -# for the OS X bundle as it may have its own private fontconfig. -if hasattr(sys, 'frameworks_dir'): - config_dir = os.path.join(os.path.dirname(getattr(sys, 'frameworks_dir')), 'Resources', 'fonts') - if isinstance(config_dir, unicode): - config_dir = config_dir.encode(sys.getfilesystemencoding()) - config = lib.FcConfigCreate() - if not lib.FcConfigParseAndLoad(config, os.path.join(config_dir, 'fonts.conf'), 1): - raise RuntimeError('Could not parse the fontconfig configuration') - if not lib.FcConfigBuildFonts(config): - raise RuntimeError('Could not build fonts') - if not lib.FcConfigSetCurrent(config): - raise RuntimeError('Could not set font config') -elif not lib.FcInit(): - raise RuntimeError(_('Could not initialize the fontconfig library')) +def join(): + _init_timer.join() + if _init_error is not None: + raise RuntimeError(_init_error) -def find_font_families(allowed_extensions=['ttf']): +def find_font_families(allowed_extensions=['ttf', 'otf']): ''' Return an alphabetically sorted list of font families available on the system. `allowed_extensions`: A list of allowed extensions for font file types. Defaults to - `['ttf']`. If it is empty, it is ignored. + `['ttf', 'otf']`. If it is empty, it is ignored. ''' + join() allowed_extensions = [i.lower() for i in allowed_extensions] empty_pattern = lib.FcPatternCreate() @@ -193,6 +212,7 @@ def files_for_family(family, normalize=True): they are a tuple (slant, weight) otherwise they are strings from the set `('normal', 'bold', 'italic', 'bi', 'light', 'li')` ''' + join() if isinstance(family, unicode): family = family.encode(preferred_encoding) family_pattern = lib.FcPatternBuild(0, 'family', FcTypeString, family, 0) @@ -268,6 +288,7 @@ def match(name, sort=False, verbose=False): decreasing closeness of matching. `verbose`: If `True` print debugging information to stdout ''' + join() if isinstance(name, unicode): name = name.encode(preferred_encoding) pat = lib.FcNameParse(name)