mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
EPUB Output: Fix converting a document with obfuscated fonts to EPUB 3 not working. Fixes #1990185 [Private bug](https://bugs.launchpad.net/calibre/+bug/1990185)
This commit is contained in:
parent
1f13e36547
commit
19cb5117f0
@ -257,11 +257,11 @@ class EPUBOutput(OutputFormatPlugin):
|
||||
opf = [x for x in os.listdir(tdir) if x.endswith('.opf')][0]
|
||||
self.condense_ncx([os.path.join(tdir, x) for x in os.listdir(tdir)
|
||||
if x.endswith('.ncx')][0])
|
||||
if self.opts.epub_version == '3':
|
||||
self.upgrade_to_epub3(tdir, opf)
|
||||
encryption = None
|
||||
if encrypted_fonts:
|
||||
encryption = self.encrypt_fonts(encrypted_fonts, tdir, uuid)
|
||||
if self.opts.epub_version == '3':
|
||||
encryption = self.upgrade_to_epub3(tdir, opf, encryption)
|
||||
|
||||
from calibre.ebooks.epub import initialize_container
|
||||
with initialize_container(output_path, os.path.basename(opf),
|
||||
@ -284,7 +284,7 @@ class EPUBOutput(OutputFormatPlugin):
|
||||
zf.extractall(path=opts.extract_to)
|
||||
self.log.info('EPUB extracted to', opts.extract_to)
|
||||
|
||||
def upgrade_to_epub3(self, tdir, opf):
|
||||
def upgrade_to_epub3(self, tdir, opf, encryption=None):
|
||||
self.log.info('Upgrading to EPUB 3...')
|
||||
from calibre.ebooks.epub import simple_container_xml
|
||||
from calibre.ebooks.oeb.polish.cover import fix_conversion_titlepage_links_in_nav
|
||||
@ -294,6 +294,9 @@ class EPUBOutput(OutputFormatPlugin):
|
||||
pass
|
||||
with open(os.path.join(tdir, 'META-INF', 'container.xml'), 'wb') as f:
|
||||
f.write(simple_container_xml(os.path.basename(opf)).encode('utf-8'))
|
||||
if encryption is not None:
|
||||
with open(os.path.join(tdir, 'META-INF', 'encryption.xml'), 'wb') as ef:
|
||||
ef.write(as_bytes(encryption))
|
||||
from calibre.ebooks.oeb.polish.container import EpubContainer
|
||||
container = EpubContainer(tdir, self.log)
|
||||
from calibre.ebooks.oeb.polish.upgrade import epub_2_to_3
|
||||
@ -304,10 +307,14 @@ class EPUBOutput(OutputFormatPlugin):
|
||||
fix_conversion_titlepage_links_in_nav(container)
|
||||
container.commit()
|
||||
os.remove(f.name)
|
||||
if encryption is not None:
|
||||
encryption = open(ef.name, 'rb').read()
|
||||
os.remove(ef.name)
|
||||
try:
|
||||
os.rmdir(os.path.join(tdir, 'META-INF'))
|
||||
except OSError:
|
||||
pass
|
||||
return encryption
|
||||
|
||||
def encrypt_fonts(self, uris, tdir, uuid): # {{{
|
||||
from polyglot.binary import from_hex_bytes
|
||||
|
@ -1283,24 +1283,7 @@ class EpubContainer(Container):
|
||||
self.dirty('META-INF/encryption.xml')
|
||||
super().remove_item(name, remove_from_guide=remove_from_guide)
|
||||
|
||||
def process_encryption(self):
|
||||
fonts = {}
|
||||
enc = self.parsed('META-INF/encryption.xml')
|
||||
for em in enc.xpath('//*[local-name()="EncryptionMethod" and @Algorithm]'):
|
||||
alg = em.get('Algorithm')
|
||||
if alg not in {ADOBE_OBFUSCATION, IDPF_OBFUSCATION}:
|
||||
raise DRMError()
|
||||
try:
|
||||
cr = em.getparent().xpath('descendant::*[local-name()="CipherReference" and @URI]')[0]
|
||||
except (IndexError, ValueError, KeyError):
|
||||
continue
|
||||
name = self.href_to_name(cr.get('URI'))
|
||||
path = self.name_path_map.get(name, None)
|
||||
if path is not None:
|
||||
fonts[name] = alg
|
||||
if not fonts:
|
||||
return
|
||||
|
||||
def read_raw_unique_identifier(self):
|
||||
package_id = raw_unique_identifier = idpf_key = None
|
||||
for attrib, val in iteritems(self.opf.attrib):
|
||||
if attrib.endswith('unique-identifier'):
|
||||
@ -1315,6 +1298,32 @@ class EpubContainer(Container):
|
||||
idpf_key = raw_unique_identifier
|
||||
idpf_key = re.sub('[\u0020\u0009\u000d\u000a]', '', idpf_key)
|
||||
idpf_key = hashlib.sha1(idpf_key.encode('utf-8')).digest()
|
||||
return package_id, raw_unique_identifier, idpf_key
|
||||
|
||||
def iter_encryption_entries(self):
|
||||
if 'META-INF/encryption.xml' in self.name_path_map:
|
||||
enc = self.parsed('META-INF/encryption.xml')
|
||||
for em in enc.xpath('//*[local-name()="EncryptionMethod" and @Algorithm]'):
|
||||
try:
|
||||
cr = em.getparent().xpath('descendant::*[local-name()="CipherReference" and @URI]')[0]
|
||||
except Exception:
|
||||
cr = None
|
||||
yield em, cr
|
||||
|
||||
def process_encryption(self):
|
||||
fonts = {}
|
||||
for em, cr in self.iter_encryption_entries():
|
||||
alg = em.get('Algorithm')
|
||||
if alg not in {ADOBE_OBFUSCATION, IDPF_OBFUSCATION}:
|
||||
raise DRMError()
|
||||
if cr is None:
|
||||
continue
|
||||
name = self.href_to_name(cr.get('URI'))
|
||||
path = self.name_path_map.get(name, None)
|
||||
if path is not None:
|
||||
fonts[name] = alg
|
||||
|
||||
package_id, raw_unique_identifier, idpf_key = self.read_raw_unique_identifier()
|
||||
key = None
|
||||
for item in self.opf_xpath('//*[local-name()="metadata"]/*'
|
||||
'[local-name()="identifier"]'):
|
||||
|
@ -4,14 +4,19 @@
|
||||
|
||||
import sys
|
||||
|
||||
from calibre.ebooks.conversion.plugins.epub_input import (
|
||||
ADOBE_OBFUSCATION, IDPF_OBFUSCATION
|
||||
)
|
||||
from calibre.ebooks.metadata.opf3 import XPath
|
||||
from calibre.ebooks.metadata.opf_2_to_3 import upgrade_metadata
|
||||
from calibre.ebooks.oeb.base import EPUB_NS, OEB_DOCS, xpath
|
||||
from calibre.ebooks.oeb.base import DC, EPUB_NS, OEB_DOCS, xpath
|
||||
from calibre.ebooks.oeb.parse_utils import ensure_namespace_prefixes
|
||||
from calibre.ebooks.oeb.polish.utils import OEB_FONTS
|
||||
from calibre.ebooks.oeb.polish.opf import get_book_language
|
||||
from calibre.ebooks.oeb.polish.toc import (
|
||||
commit_nav_toc, find_existing_ncx_toc, get_landmarks, get_toc
|
||||
)
|
||||
from calibre.ebooks.oeb.polish.utils import OEB_FONTS
|
||||
from calibre.utils.short_uuid import uuid4
|
||||
|
||||
|
||||
def add_properties(item, *props):
|
||||
@ -31,6 +36,38 @@ def fix_font_mime_types(container):
|
||||
return changed
|
||||
|
||||
|
||||
def migrate_obfuscated_fonts(container):
|
||||
if not container.obfuscated_fonts:
|
||||
return
|
||||
name_to_elem_map = {}
|
||||
for em, cr in container.iter_encryption_entries():
|
||||
alg = em.get('Algorithm')
|
||||
if cr is None or alg not in {ADOBE_OBFUSCATION, IDPF_OBFUSCATION}:
|
||||
continue
|
||||
name = container.href_to_name(cr.get('URI'))
|
||||
name_to_elem_map[name] = em, cr
|
||||
package_id, raw_unique_identifier, idpf_key = container.read_raw_unique_identifier()
|
||||
if not idpf_key:
|
||||
if not package_id:
|
||||
package_id = uuid4()
|
||||
container.opf.set('unique-identifier', package_id)
|
||||
metadata = XPath('./opf:metadata')(container.opf)[0]
|
||||
ident = metadata.makeelement(DC('identifier'))
|
||||
ident.text = uuid4()
|
||||
metadata.append(ident)
|
||||
package_id, raw_unique_identifier, idpf_key = container.read_raw_unique_identifier()
|
||||
for name in tuple(container.obfuscated_fonts):
|
||||
try:
|
||||
em, cr = name_to_elem_map[name]
|
||||
except KeyError:
|
||||
container.obfuscated_fonts.pop(name)
|
||||
continue
|
||||
em.set('Algorithm', IDPF_OBFUSCATION)
|
||||
cr.set('URI', container.name_to_href(name))
|
||||
container.obfuscated_fonts[name] = (IDPF_OBFUSCATION, idpf_key)
|
||||
container.commit_item('META-INF/encryption.xml')
|
||||
|
||||
|
||||
def collect_properties(container):
|
||||
for item in container.opf_xpath('//opf:manifest/opf:item[@href and @media-type]'):
|
||||
mt = item.get('media-type') or ''
|
||||
@ -128,6 +165,7 @@ def epub_2_to_3(container, report, previous_nav=None, remove_ncx=True):
|
||||
container.opf.set('version', '3.0')
|
||||
if fix_font_mime_types(container):
|
||||
container.refresh_mime_map()
|
||||
migrate_obfuscated_fonts(container)
|
||||
container.dirty(container.opf_name)
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user