mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Support Adobe's idiotic font encryption scheme for EPUB books created by InDesign
This commit is contained in:
parent
81726aa967
commit
e76b1ab174
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user