From 8e3c1c74251a4c35e3449af7e838cf2d26cf8308 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 24 Apr 2014 08:53:27 +0530 Subject: [PATCH] Edit book: Fix obfuscated fonts in EPUB files not being handled correctly when editing the book (obfuscated fonts would get corrupted when making changes). --- .../ebooks/conversion/plugins/epub_input.py | 15 ++++++++------- src/calibre/ebooks/oeb/polish/container.py | 16 ++++++++++++---- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/calibre/ebooks/conversion/plugins/epub_input.py b/src/calibre/ebooks/conversion/plugins/epub_input.py index 50bb8f1a6d..409c128836 100644 --- a/src/calibre/ebooks/conversion/plugins/epub_input.py +++ b/src/calibre/ebooks/conversion/plugins/epub_input.py @@ -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): diff --git a/src/calibre/ebooks/oeb/polish/container.py b/src/calibre/ebooks/oeb/polish/container.py index 092bc174d2..5dd478b1ce 100644 --- a/src/calibre/ebooks/oeb/polish/container.py +++ b/src/calibre/ebooks/oeb/polish/container.py @@ -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):