Conversion EPUB Output: Add an option to output EPUB 3 files.

This commit is contained in:
Kovid Goyal 2018-05-22 12:13:32 +05:30
parent 94058dd07b
commit 10ab8bfa26
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 62 additions and 16 deletions

View File

@ -117,6 +117,12 @@ class EPUBOutput(OutputFormatPlugin):
help=_('Title for any generated in-line table of contents.') help=_('Title for any generated in-line table of contents.')
), ),
OptionRecommendation(name='epub_version', recommended_value='2', choices=('2', '3'),
help=_('The version of the EPUB file to generate. EPUB 2 is the'
' most widely compatible, only use EPUB 3 if you know you'
' actually need it.')
),
]) ])
recommendations = set([('pretty_print', True, OptionRecommendation.HIGH)]) recommendations = set([('pretty_print', True, OptionRecommendation.HIGH)])
@ -168,6 +174,7 @@ class EPUBOutput(OutputFormatPlugin):
seen_names.add(name) seen_names.add(name)
# }}} # }}}
def convert(self, oeb, output_path, input_plugin, opts, log): def convert(self, oeb, output_path, input_plugin, opts, log):
self.log, self.opts, self.oeb = log, opts, oeb self.log, self.opts, self.oeb = log, opts, oeb
@ -249,6 +256,8 @@ class EPUBOutput(OutputFormatPlugin):
opf = [x for x in os.listdir(tdir) if x.endswith('.opf')][0] 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) self.condense_ncx([os.path.join(tdir, x) for x in os.listdir(tdir)
if x.endswith('.ncx')][0]) if x.endswith('.ncx')][0])
if self.opts.epub_version == '3':
self.upgrade_to_epub3(tdir, opf)
encryption = None encryption = None
if encrypted_fonts: if encrypted_fonts:
encryption = self.encrypt_fonts(encrypted_fonts, tdir, uuid) encryption = self.encrypt_fonts(encrypted_fonts, tdir, uuid)
@ -274,6 +283,26 @@ class EPUBOutput(OutputFormatPlugin):
zf.extractall(path=opts.extract_to) zf.extractall(path=opts.extract_to)
self.log.info('EPUB extracted to', opts.extract_to) self.log.info('EPUB extracted to', opts.extract_to)
def upgrade_to_epub3(self, tdir, opf):
self.log.info('Upgrading to EPUB 3...')
from calibre.ebooks.epub import simple_container_xml
try:
os.mkdir(os.path.join(tdir, 'META-INF'))
except EnvironmentError:
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'))
from calibre.ebooks.oeb.polish.container import EpubContainer
container = EpubContainer(tdir, self.log)
from calibre.ebooks.oeb.polish.upgrade import epub_2_to_3
epub_2_to_3(container, self.log.info)
container.commit()
os.remove(f.name)
try:
os.rmdir(os.path.join(tdir, 'META-INF'))
except EnvironmentError:
pass
def encrypt_fonts(self, uris, tdir, uuid): # {{{ def encrypt_fonts(self, uris, tdir, uuid): # {{{
from binascii import unhexlify from binascii import unhexlify
@ -324,7 +353,7 @@ class EPUBOutput(OutputFormatPlugin):
return ans return ans
# }}} # }}}
def condense_ncx(self, ncx_path): def condense_ncx(self, ncx_path): # {{{
from lxml import etree from lxml import etree
if not self.opts.pretty_print: if not self.opts.pretty_print:
tree = etree.parse(ncx_path) tree = etree.parse(ncx_path)
@ -335,6 +364,7 @@ class EPUBOutput(OutputFormatPlugin):
tag.tail = tag.tail.strip() tag.tail = tag.tail.strip()
compressed = etree.tostring(tree.getroot(), encoding='utf-8') compressed = etree.tostring(tree.getroot(), encoding='utf-8')
open(ncx_path, 'wb').write(compressed) open(ncx_path, 'wb').write(compressed)
# }}}
def workaround_ade_quirks(self): # {{{ def workaround_ade_quirks(self): # {{{
''' '''
@ -420,8 +450,7 @@ class EPUBOutput(OutputFormatPlugin):
if not tag.text: if not tag.text:
tag.getparent().remove(tag) tag.getparent().remove(tag)
for tag in XPath('//h:script')(root): for tag in XPath('//h:script')(root):
if (not tag.text and not tag.get('src', False) and if (not tag.text and not tag.get('src', False) and tag.get('type', None) != 'text/x-mathjax-config'):
tag.get('type', None) != 'text/x-mathjax-config'):
tag.getparent().remove(tag) tag.getparent().remove(tag)
for tag in XPath('//h:body/descendant::h:script')(root): for tag in XPath('//h:body/descendant::h:script')(root):
tag.getparent().remove(tag) tag.getparent().remove(tag)

View File

@ -17,6 +17,18 @@ def rules(stylesheets):
yield r yield r
def simple_container_xml(opf_path, extra_entries=''):
return u'''\
<?xml version="1.0"?>
<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
<rootfiles>
<rootfile full-path="{0}" media-type="application/oebps-package+xml"/>
{extra_entries}
</rootfiles>
</container>
'''.format(opf_path, extra_entries=extra_entries)
def initialize_container(path_to_container, opf_name='metadata.opf', def initialize_container(path_to_container, opf_name='metadata.opf',
extra_entries=[]): extra_entries=[]):
''' '''
@ -26,15 +38,7 @@ def initialize_container(path_to_container, opf_name='metadata.opf',
for path, mimetype, _ in extra_entries: for path, mimetype, _ in extra_entries:
rootfiles += u'<rootfile full-path="{0}" media-type="{1}"/>'.format( rootfiles += u'<rootfile full-path="{0}" media-type="{1}"/>'.format(
path, mimetype) path, mimetype)
CONTAINER = u'''\ CONTAINER = simple_container_xml(opf_name, rootfiles).encode('utf-8')
<?xml version="1.0"?>
<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
<rootfiles>
<rootfile full-path="{0}" media-type="application/oebps-package+xml"/>
{extra_entries}
</rootfiles>
</container>
'''.format(opf_name, extra_entries=rootfiles).encode('utf-8')
zf = ZipFile(path_to_container, 'w') zf = ZipFile(path_to_container, 'w')
zf.writestr('mimetype', 'application/epub+zip', compression=ZIP_STORED) zf.writestr('mimetype', 'application/epub+zip', compression=ZIP_STORED)
zf.writestr('META-INF/', '', 0755) zf.writestr('META-INF/', '', 0755)
@ -42,5 +46,3 @@ def initialize_container(path_to_container, opf_name='metadata.opf',
for path, _, data in extra_entries: for path, _, data in extra_entries:
zf.writestr(path, data) zf.writestr(path, data)
return zf return zf

View File

@ -23,9 +23,11 @@ class PluginWidget(Widget, Ui_Form):
['dont_split_on_page_breaks', 'flow_size', ['dont_split_on_page_breaks', 'flow_size',
'no_default_epub_cover', 'no_svg_cover', 'no_default_epub_cover', 'no_svg_cover',
'epub_inline_toc', 'epub_toc_at_end', 'toc_title', 'epub_inline_toc', 'epub_toc_at_end', 'toc_title',
'preserve_cover_aspect_ratio', 'epub_flatten'] 'preserve_cover_aspect_ratio', 'epub_flatten', 'epub_version']
) )
for i in range(2): for i in range(2):
self.opt_no_svg_cover.toggle() self.opt_no_svg_cover.toggle()
ev = get_option('epub_version')
self.opt_epub_version.addItems(list(ev.option.choices))
self.db, self.book_id = db, book_id self.db, self.book_id = db, book_id
self.initialize_options(get_option, get_help, db, book_id) self.initialize_options(get_option, get_help, db, book_id)

View File

@ -50,7 +50,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="0"> <item row="7" column="0">
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
@ -118,6 +118,19 @@
<item row="4" column="1"> <item row="4" column="1">
<widget class="QLineEdit" name="opt_toc_title"/> <widget class="QLineEdit" name="opt_toc_title"/>
</item> </item>
<item row="6" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>EP&amp;UB version:</string>
</property>
<property name="buddy">
<cstring>opt_epub_version</cstring>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QComboBox" name="opt_epub_version"/>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>