Support Adobe's idiotic font encryption scheme for EPUB books created by InDesign

This commit is contained in:
Kovid Goyal 2008-12-05 19:00:30 -08:00
parent 81726aa967
commit e76b1ab174
3 changed files with 57 additions and 14 deletions

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
@ -6,10 +6,12 @@ __docformat__ = 'restructuredtext en'
'''
Conversion to EPUB.
'''
import sys, textwrap, re
import sys, textwrap, re, os, uuid
from itertools import cycle
from calibre.utils.config import Config, StringConfig
from calibre.utils.zipfile import ZipFile, ZIP_STORED
from calibre.ebooks.html import config as common_config, tostring
from lxml import etree
class DefaultProfile(object):
@ -36,6 +38,38 @@ def rules(stylesheets):
if r.type == r.STYLE_RULE:
yield r
def decrypt_font(key, path):
raw = open(path, 'rb').read()
crypt = raw[:1024]
key = cycle(iter(key))
decrypt = ''.join([chr(ord(x)^key.next()) for x in crypt])
with open(path, 'wb') as f:
f.write(decrypt)
f.write(raw[1024:])
def process_encryption(encfile, opf):
key = None
m = re.search(r'(?i)(urn:uuid:[0-9a-f-]+)', open(opf, 'rb').read())
if m:
key = m.group(1)
key = list(map(ord, uuid.UUID(key).bytes))
try:
root = etree.parse(encfile)
for em in root.xpath('descendant::*[contains(name(), "EncryptionMethod")]'):
algorithm = em.get('Algorithm', '')
if algorithm != 'http://ns.adobe.com/pdf/enc#RC':
return False
cr = em.getparent().xpath('descendant::*[contains(name(), "CipherReference")]')[0]
uri = cr.get('URI')
path = os.path.abspath(os.path.join(os.path.dirname(encfile), '..', *uri.split('/')))
if os.path.exists(path):
decrypt_font(key, path)
return True
except:
import traceback
traceback.print_exc()
return False
def initialize_container(path_to_container, opf_name='metadata.opf'):
'''
Create an empty EPUB document, with a default skeleton.

View File

@ -12,7 +12,7 @@ from contextlib import nested
from calibre import extract, walk
from calibre.ebooks import DRMError
from calibre.ebooks.epub import config as common_config
from calibre.ebooks.epub import config as common_config, process_encryption
from calibre.ebooks.epub.from_html import convert as html2epub, find_html_index
from calibre.ptempfile import TemporaryDirectory
from calibre.ebooks.metadata import MetaInformation
@ -72,12 +72,19 @@ def epub2opf(path, tdir, opts):
zf = ZipFile(path)
zf.extractall(tdir)
opts.chapter_mark = 'none'
if os.path.exists(os.path.join(tdir, 'META-INF', 'encryption.xml')):
raise DRMError(os.path.basename(path))
encfile = os.path.join(tdir, 'META-INF', 'encryption.xml')
opf = None
for f in walk(tdir):
if f.lower().endswith('.opf'):
return f
opf = f
break
if opf and os.path.exists(encfile):
if not process_encryption(encfile, opf):
raise DRMError(os.path.basename(path))
if opf is None:
raise ValueError('%s is not a valid EPUB file'%path)
return opf
def odt2epub(path, tdir, opts):
from calibre.ebooks.odt.to_oeb import Extract

View File

@ -37,14 +37,16 @@ class UnsupportedFormatError(Exception):
class SpineItem(unicode):
def __init__(self, path):
unicode.__init__(self, path)
def __new__(cls, *args):
obj = super(SpineItem, cls).__new__(cls, *args)
path = args[0]
raw = open(path, 'rb').read()
raw, self.encoding = xml_to_unicode(raw)
self.character_count = character_count(raw)
self.start_page = -1
self.pages = -1
self.max_page = -1
raw, obj.encoding = xml_to_unicode(raw)
obj.character_count = character_count(raw)
obj.start_page = -1
obj.pages = -1
obj.max_page = -1
return obj
def html2opf(path, tdir, opts):
opts = copy.copy(opts)