Edit book: Fix obfuscated fonts in EPUB files not being handled correctly when editing the book (obfuscated fonts would get corrupted when making changes).

This commit is contained in:
Kovid Goyal 2014-04-24 08:53:27 +05:30
parent 65b1c5ac57
commit 8e3c1c7425
2 changed files with 20 additions and 11 deletions

View File

@ -11,17 +11,18 @@ from calibre.customize.conversion import InputFormatPlugin, OptionRecommendation
ADOBE_OBFUSCATION = 'http://ns.adobe.com/pdf/enc#RC'
IDPF_OBFUSCATION = 'http://www.idpf.org/2008/embedding'
def decrypt_font(key, path, algorithm):
def decrypt_font_data(key, data, algorithm):
is_adobe = algorithm == ADOBE_OBFUSCATION
crypt_len = 1024 if is_adobe else 1040
with open(path, 'rb') as f:
raw = f.read()
crypt = bytearray(raw[:crypt_len])
crypt = bytearray(data[:crypt_len])
key = cycle(iter(bytearray(key)))
decrypt = bytes(bytearray(x^key.next() for x in crypt))
with open(path, 'wb') as f:
f.write(decrypt)
f.write(raw[crypt_len:])
return decrypt + data[crypt_len:]
def decrypt_font(key, path, algorithm):
with open(path, 'r+b') as f:
data = decrypt_font_data(key, f.read(), algorithm)
f.seek(0), f.truncate(), f.write(data)
class EPUBInput(InputFormatPlugin):

View File

@ -21,7 +21,7 @@ from calibre.customize.ui import (plugin_for_input_format,
plugin_for_output_format)
from calibre.ebooks.chardet import xml_to_unicode
from calibre.ebooks.conversion.plugins.epub_input import (
ADOBE_OBFUSCATION, IDPF_OBFUSCATION, decrypt_font)
ADOBE_OBFUSCATION, IDPF_OBFUSCATION, decrypt_font_data)
from calibre.ebooks.conversion.preprocess import HTMLPreProcessor, CSSPreProcessor as cssp
from calibre.ebooks.mobi import MobiError
from calibre.ebooks.mobi.reader.headers import MetadataHeader
@ -969,27 +969,35 @@ class EpubContainer(Container):
key = None
for font, alg in fonts.iteritems():
path = self.name_path_map[font]
tkey = key if alg == ADOBE_OBFUSCATION else idpf_key
if not tkey:
raise InvalidBook('Failed to find obfuscation key')
decrypt_font(tkey, path, alg)
raw = self.raw_data(font, decode=False)
raw = decrypt_font_data(tkey, raw, alg)
with self.open(font, 'wb') as f:
f.write(raw)
self.obfuscated_fonts[font] = (alg, tkey)
def commit(self, outpath=None, keep_parsed=False):
super(EpubContainer, self).commit(keep_parsed=keep_parsed)
restore_fonts = {}
for name in self.obfuscated_fonts:
if name not in self.name_path_map:
continue
alg, key = self.obfuscated_fonts[name]
# Decrypting and encrypting are the same operation (XOR with key)
decrypt_font(key, self.name_path_map[name], alg)
restore_fonts[name] = data = self.raw_data(name, decode=False)
with self.open(name, 'wb') as f:
f.write(decrypt_font_data(key, data, alg))
if outpath is None:
outpath = self.pathtoepub
from calibre.ebooks.tweak import zip_rebuilder
with open(join(self.root, 'mimetype'), 'wb') as f:
f.write(guess_type('a.epub'))
zip_rebuilder(self.root, outpath)
for name, data in restore_fonts.iteritems():
with self.open(name, 'wb') as f:
f.write(data)
@dynamic_property
def path_to_ebook(self):