mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Fix #6928 (Calibre corrupts EPUB)
This commit is contained in:
parent
ee0b2541c8
commit
54c3e009d5
@ -15,7 +15,7 @@ from calibre.customize.ui import available_input_formats
|
|||||||
from calibre.ebooks.metadata.opf2 import OPF
|
from calibre.ebooks.metadata.opf2 import OPF
|
||||||
from calibre.ptempfile import TemporaryDirectory
|
from calibre.ptempfile import TemporaryDirectory
|
||||||
from calibre.ebooks.chardet import xml_to_unicode
|
from calibre.ebooks.chardet import xml_to_unicode
|
||||||
from calibre.utils.zipfile import safe_replace, ZipFile
|
from calibre.utils.zipfile import safe_replace
|
||||||
from calibre.utils.config import DynamicConfig
|
from calibre.utils.config import DynamicConfig
|
||||||
from calibre.utils.logging import Log
|
from calibre.utils.logging import Log
|
||||||
from calibre import guess_type, prints
|
from calibre import guess_type, prints
|
||||||
@ -294,12 +294,8 @@ class EbookIterator(object):
|
|||||||
zf = open(self.pathtoebook, 'r+b')
|
zf = open(self.pathtoebook, 'r+b')
|
||||||
except IOError:
|
except IOError:
|
||||||
return
|
return
|
||||||
zipf = ZipFile(zf, mode='a')
|
safe_replace(zf, 'META-INF/calibre_bookmarks.txt', StringIO(dat),
|
||||||
for name in zipf.namelist():
|
add_missing=True)
|
||||||
if name == 'META-INF/calibre_bookmarks.txt':
|
|
||||||
safe_replace(zf, 'META-INF/calibre_bookmarks.txt', StringIO(dat))
|
|
||||||
return
|
|
||||||
zipf.writestr('META-INF/calibre_bookmarks.txt', dat)
|
|
||||||
else:
|
else:
|
||||||
self.config['bookmarks_'+self.pathtoebook] = dat
|
self.config['bookmarks_'+self.pathtoebook] = dat
|
||||||
|
|
||||||
|
@ -281,7 +281,7 @@ class ZipInfo (object):
|
|||||||
'file_offset',
|
'file_offset',
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)):
|
def __init__(self, filename=u"NoName", date_time=(1980,1,1,0,0,0)):
|
||||||
self.orig_filename = filename # Original file name in archive
|
self.orig_filename = filename # Original file name in archive
|
||||||
|
|
||||||
# Terminate the file name at the first null byte. Null bytes in file
|
# Terminate the file name at the first null byte. Null bytes in file
|
||||||
@ -1362,30 +1362,42 @@ class ZipFile:
|
|||||||
self.fp.close()
|
self.fp.close()
|
||||||
self.fp = None
|
self.fp = None
|
||||||
|
|
||||||
def safe_replace(zipstream, name, datastream, extra_replacements={}):
|
def safe_replace(zipstream, name, datastream, extra_replacements={},
|
||||||
|
add_missing=False):
|
||||||
'''
|
'''
|
||||||
Replace a file in a zip file in a safe manner. This proceeds by extracting
|
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`
|
and re-creating the zipfile. This is necessary because :method:`ZipFile.replace`
|
||||||
sometimes created corrupted zip files.
|
sometimes created corrupted zip files.
|
||||||
|
|
||||||
|
|
||||||
:param zipstream: Stream from a zip file
|
:param zipstream: Stream from a zip file
|
||||||
:param name: The name of the file to replace
|
:param name: The name of the file to replace
|
||||||
:param datastream: The data to replace the file with.
|
:param datastream: The data to replace the file with.
|
||||||
:param extra_replacements: Extra replacements. Mapping of name to file-like
|
:param extra_replacements: Extra replacements. Mapping of name to file-like
|
||||||
objects
|
objects
|
||||||
|
:param add_missing: If a replacement does not exist in the zip file, it is
|
||||||
|
added. Use with care as currently parent directories
|
||||||
|
are not created.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
z = ZipFile(zipstream, 'r')
|
z = ZipFile(zipstream, 'r')
|
||||||
replacements = {name:datastream}
|
replacements = {name:datastream}
|
||||||
replacements.update(extra_replacements)
|
replacements.update(extra_replacements)
|
||||||
names = frozenset(replacements.keys())
|
names = frozenset(replacements.keys())
|
||||||
|
found = set([])
|
||||||
with SpooledTemporaryFile(max_size=100*1024*1024) as temp:
|
with SpooledTemporaryFile(max_size=100*1024*1024) as temp:
|
||||||
ztemp = ZipFile(temp, 'w')
|
ztemp = ZipFile(temp, 'w')
|
||||||
for obj in z.infolist():
|
for obj in z.infolist():
|
||||||
|
if isinstance(obj.filename, unicode):
|
||||||
|
obj.flag_bits |= 0x16 # Set isUTF-8 bit
|
||||||
if obj.filename in names:
|
if obj.filename in names:
|
||||||
ztemp.writestr(obj, replacements[obj.filename].read())
|
ztemp.writestr(obj, replacements[obj.filename].read())
|
||||||
|
found.add(obj.filename)
|
||||||
else:
|
else:
|
||||||
ztemp.writestr(obj, z.read_raw(obj), raw_bytes=True)
|
ztemp.writestr(obj, z.read_raw(obj), raw_bytes=True)
|
||||||
|
if add_missing:
|
||||||
|
for name in names - found:
|
||||||
|
ztemp.writestr(name, replacements[name].read())
|
||||||
ztemp.close()
|
ztemp.close()
|
||||||
z.close()
|
z.close()
|
||||||
temp.seek(0)
|
temp.seek(0)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user