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'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
@ -6,10 +6,12 @@ __docformat__ = 'restructuredtext en'
|
|||||||
'''
|
'''
|
||||||
Conversion to EPUB.
|
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.config import Config, StringConfig
|
||||||
from calibre.utils.zipfile import ZipFile, ZIP_STORED
|
from calibre.utils.zipfile import ZipFile, ZIP_STORED
|
||||||
from calibre.ebooks.html import config as common_config, tostring
|
from calibre.ebooks.html import config as common_config, tostring
|
||||||
|
from lxml import etree
|
||||||
|
|
||||||
class DefaultProfile(object):
|
class DefaultProfile(object):
|
||||||
|
|
||||||
@ -36,6 +38,38 @@ def rules(stylesheets):
|
|||||||
if r.type == r.STYLE_RULE:
|
if r.type == r.STYLE_RULE:
|
||||||
yield r
|
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'):
|
def initialize_container(path_to_container, opf_name='metadata.opf'):
|
||||||
'''
|
'''
|
||||||
Create an empty EPUB document, with a default skeleton.
|
Create an empty EPUB document, with a default skeleton.
|
||||||
|
@ -12,7 +12,7 @@ from contextlib import nested
|
|||||||
|
|
||||||
from calibre import extract, walk
|
from calibre import extract, walk
|
||||||
from calibre.ebooks import DRMError
|
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.ebooks.epub.from_html import convert as html2epub, find_html_index
|
||||||
from calibre.ptempfile import TemporaryDirectory
|
from calibre.ptempfile import TemporaryDirectory
|
||||||
from calibre.ebooks.metadata import MetaInformation
|
from calibre.ebooks.metadata import MetaInformation
|
||||||
@ -72,12 +72,19 @@ def epub2opf(path, tdir, opts):
|
|||||||
zf = ZipFile(path)
|
zf = ZipFile(path)
|
||||||
zf.extractall(tdir)
|
zf.extractall(tdir)
|
||||||
opts.chapter_mark = 'none'
|
opts.chapter_mark = 'none'
|
||||||
if os.path.exists(os.path.join(tdir, 'META-INF', 'encryption.xml')):
|
encfile = os.path.join(tdir, 'META-INF', 'encryption.xml')
|
||||||
raise DRMError(os.path.basename(path))
|
opf = None
|
||||||
for f in walk(tdir):
|
for f in walk(tdir):
|
||||||
if f.lower().endswith('.opf'):
|
if f.lower().endswith('.opf'):
|
||||||
return f
|
opf = f
|
||||||
raise ValueError('%s is not a valid EPUB file'%path)
|
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):
|
def odt2epub(path, tdir, opts):
|
||||||
from calibre.ebooks.odt.to_oeb import Extract
|
from calibre.ebooks.odt.to_oeb import Extract
|
||||||
|
@ -37,14 +37,16 @@ class UnsupportedFormatError(Exception):
|
|||||||
|
|
||||||
class SpineItem(unicode):
|
class SpineItem(unicode):
|
||||||
|
|
||||||
def __init__(self, path):
|
def __new__(cls, *args):
|
||||||
unicode.__init__(self, path)
|
obj = super(SpineItem, cls).__new__(cls, *args)
|
||||||
|
path = args[0]
|
||||||
raw = open(path, 'rb').read()
|
raw = open(path, 'rb').read()
|
||||||
raw, self.encoding = xml_to_unicode(raw)
|
raw, obj.encoding = xml_to_unicode(raw)
|
||||||
self.character_count = character_count(raw)
|
obj.character_count = character_count(raw)
|
||||||
self.start_page = -1
|
obj.start_page = -1
|
||||||
self.pages = -1
|
obj.pages = -1
|
||||||
self.max_page = -1
|
obj.max_page = -1
|
||||||
|
return obj
|
||||||
|
|
||||||
def html2opf(path, tdir, opts):
|
def html2opf(path, tdir, opts):
|
||||||
opts = copy.copy(opts)
|
opts = copy.copy(opts)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user