diff --git a/src/calibre/ebooks/metadata/epub.py b/src/calibre/ebooks/metadata/epub.py index b3980451bf..d8a527aba7 100644 --- a/src/calibre/ebooks/metadata/epub.py +++ b/src/calibre/ebooks/metadata/epub.py @@ -5,7 +5,7 @@ __copyright__ = '2008, Kovid Goyal ' '''Read meta information from epub files''' -import os, re +import os, re, posixpath from cStringIO import StringIO from contextlib import closing @@ -126,7 +126,6 @@ class OCFDirReader(OCFReader): return open(os.path.join(self.root, path), *args, **kwargs) def get_cover(opf, opf_path, stream, reader=None): - import posixpath from calibre.ebooks import render_html_svg_workaround from calibre.utils.logging import default_log raster_cover = opf.raster_cover @@ -185,7 +184,37 @@ def get_quick_metadata(stream): def set_metadata(stream, mi, apply_null=False, update_timestamp=False): stream.seek(0) reader = OCFZipReader(stream, root=os.getcwdu()) + raster_cover = reader.opf.raster_cover mi = MetaInformation(mi) + new_cdata = None + replacements = {} + try: + new_cdata = mi.cover_data[1] + if not new_cdata: + raise Exception('no cover') + except: + try: + new_cdata = open(mi.cover, 'rb').read() + except: + import traceback + traceback.print_exc() + if new_cdata and raster_cover: + try: + cpath = posixpath.join(posixpath.dirname(reader.opf_path), + raster_cover) + cover_replacable = not reader.encryption_meta.is_encrypted(cpath) and \ + os.path.splitext(cpath)[1].lower() in ('.png', '.jpg', '.jpeg') + if cover_replacable: + from calibre.ptempfile import PersistentTemporaryFile + from calibre.utils.magick_draw import save_cover_data_to + new_cover = PersistentTemporaryFile(suffix=os.path.splitext(cpath)[1]) + new_cover.close() + save_cover_data_to(new_cdata, new_cover.name) + replacements[cpath] = open(new_cover.name, 'rb') + except: + import traceback + traceback.print_exc() + for x in ('guide', 'toc', 'manifest', 'spine'): setattr(mi, x, None) reader.opf.smart_update(mi) @@ -200,5 +229,6 @@ def set_metadata(stream, mi, apply_null=False, update_timestamp=False): reader.opf.timestamp = mi.timestamp newopf = StringIO(reader.opf.render()) - safe_replace(stream, reader.container[OPF.MIMETYPE], newopf) + safe_replace(stream, reader.container[OPF.MIMETYPE], newopf, + extra_replacements=replacements) diff --git a/src/calibre/library/server/content.py b/src/calibre/library/server/content.py index 0e04fdfdb5..6784abd8f4 100644 --- a/src/calibre/library/server/content.py +++ b/src/calibre/library/server/content.py @@ -183,7 +183,8 @@ class ContentServer(object): fmt = TemporaryFile() fmt.write(raw) fmt.seek(0) - set_metadata(fmt, self.db.get_metadata(id, index_is_id=True), + set_metadata(fmt, self.db.get_metadata(id, index_is_id=True, + get_cover=True), 'epub') fmt.seek(0) mt = guess_type('dummy.'+format.lower())[0] diff --git a/src/calibre/utils/zipfile.py b/src/calibre/utils/zipfile.py index 50d621a986..79f8a1a344 100644 --- a/src/calibre/utils/zipfile.py +++ b/src/calibre/utils/zipfile.py @@ -1362,7 +1362,7 @@ class ZipFile: self.fp.close() self.fp = None -def safe_replace(zipstream, name, datastream): +def safe_replace(zipstream, name, datastream, extra_replacements={}): ''' Replace a file in a zip file in a safe manner. This proceeds by extracting and re-creating the zipfile. This is necessary because :method:`ZipFile.replace` @@ -1371,13 +1371,19 @@ def safe_replace(zipstream, name, datastream): :param zipstream: Stream from a zip file :param name: The name of the file to replace :param datastream: The data to replace the file with. + :param extra_replacements: Extra replacements. Mapping of name to file-like + objects + ''' z = ZipFile(zipstream, 'r') + replacements = {name:datastream} + replacements.update(extra_replacements) + names = frozenset(replacements.keys()) with SpooledTemporaryFile(max_size=100*1024*1024) as temp: ztemp = ZipFile(temp, 'w') for obj in z.infolist(): - if obj.filename == name: - ztemp.writestr(obj, datastream.read()) + if obj.filename in names: + ztemp.writestr(obj, replacements[obj.filename].read()) else: ztemp.writestr(obj, z.read_raw(obj), raw_bytes=True) ztemp.close()