Rewrite HTML input plugin. Fixes #2560 (Images not included when converting HTML to OEB with ebook-convert)

This commit is contained in:
Kovid Goyal 2009-06-17 21:32:13 -07:00
parent 38e60f714b
commit c4476db003
7 changed files with 215 additions and 217 deletions

View File

@ -16,7 +16,7 @@ file containing all linked files. This plugin is run \
every time you add an HTML file to the library.\ every time you add an HTML file to the library.\
''')) '''))
version = numeric_version version = numeric_version
file_types = set(['html', 'htm', 'xhtml', 'xhtm']) file_types = set(['html', 'htm', 'xhtml', 'xhtm', 'shtm', 'shtml'])
supported_platforms = ['windows', 'osx', 'linux'] supported_platforms = ['windows', 'osx', 'linux']
on_import = True on_import = True

View File

@ -688,7 +688,7 @@ OptionRecommendation(name='list_recipes',
self.flush() self.flush()
def create_oebbook(log, path_or_stream, opts, input_plugin, reader=None, def create_oebbook(log, path_or_stream, opts, input_plugin, reader=None,
encoding='utf-8'): encoding='utf-8', populate=True):
''' '''
Create an OEBBook. Create an OEBBook.
''' '''
@ -697,6 +697,8 @@ def create_oebbook(log, path_or_stream, opts, input_plugin, reader=None,
opts.preprocess_html) opts.preprocess_html)
oeb = OEBBook(log, html_preprocessor, oeb = OEBBook(log, html_preprocessor,
pretty_print=opts.pretty_print, input_encoding=encoding) pretty_print=opts.pretty_print, input_encoding=encoding)
if not populate:
return oeb
# Read OEB Book into OEBBook # Read OEB Book into OEBBook
log('Parsing all content...') log('Parsing all content...')
if reader is None: if reader is None:

View File

@ -11,15 +11,18 @@ __docformat__ = 'restructuredtext en'
Input plugin for HTML or OPF ebooks. Input plugin for HTML or OPF ebooks.
''' '''
import os, re, sys import os, re, sys, uuid
from urlparse import urlparse, urlunparse from urlparse import urlparse, urlunparse
from urllib import unquote from urllib import unquote
from functools import partial
from itertools import izip
from calibre.customize.conversion import InputFormatPlugin from calibre.customize.conversion import InputFormatPlugin
from calibre.ebooks.metadata.opf2 import OPFCreator
from calibre.ebooks.chardet import xml_to_unicode from calibre.ebooks.chardet import xml_to_unicode
from calibre.customize.conversion import OptionRecommendation from calibre.customize.conversion import OptionRecommendation
from calibre.constants import islinux
from calibre import unicode_path from calibre import unicode_path
from calibre.startup import get_lang
class Link(object): class Link(object):
''' '''
@ -120,8 +123,6 @@ class HTMLFile(object):
self.title = match.group(1) if match is not None else self.title self.title = match.group(1) if match is not None else self.title
self.find_links(src) self.find_links(src)
def __eq__(self, other): def __eq__(self, other):
return self.path == getattr(other, 'path', other) return self.path == getattr(other, 'path', other)
@ -264,37 +265,163 @@ class HTMLInput(InputFormatPlugin):
def convert(self, stream, opts, file_ext, log, def convert(self, stream, opts, file_ext, log,
accelerators): accelerators):
from calibre.ebooks.metadata.html import get_metadata_
basedir = os.getcwd() basedir = os.getcwd()
self.opts = opts self.opts = opts
if hasattr(stream, 'name'): if hasattr(stream, 'name'):
basedir = os.path.dirname(stream.name) basedir = os.path.dirname(stream.name)
if file_ext == 'opf':
opfpath = stream.name
else:
filelist = get_filelist(stream.name, basedir, opts, log)
mi = get_metadata_(stream.read(), opts.input_encoding)
mi = OPFCreator(os.getcwdu(), mi)
mi.guide = None
entries = [(f.path, 'application/xhtml+xml') for f in filelist]
mi.create_manifest(entries)
mi.create_spine([f.path for f in filelist])
mi.render(open('metadata.opf', 'wb'), encoding=opts.input_encoding) if file_ext != 'opf':
opfpath = os.path.abspath('metadata.opf') if opts.dont_package:
raise ValueError('The --dont-package option is not supported for an HTML input file')
if opts.dont_package: from calibre.ebooks.metadata.html import get_metadata
return opfpath oeb = self.create_oebbook(stream.name, basedir, opts, log,
get_metadata(stream))
return oeb
from calibre.ebooks.conversion.plumber import create_oebbook from calibre.ebooks.conversion.plumber import create_oebbook
oeb = create_oebbook(log, opfpath, opts, self, return create_oebbook(log, stream.name, opts, self,
encoding=opts.input_encoding) encoding=opts.input_encoding)
from calibre.ebooks.oeb.transforms.package import Package def create_oebbook(self, htmlpath, basedir, opts, log, mi):
Package(os.getcwdu())(oeb, opts) from calibre.ebooks.conversion.plumber import create_oebbook
from calibre.ebooks.oeb.base import DirContainer, \
rewrite_links, urlnormalize, urldefrag, BINARY_MIME, OEB_STYLES, \
xpath
from calibre import guess_type
import cssutils
oeb = create_oebbook(log, None, opts, self,
encoding=opts.input_encoding, populate=False)
self.oeb = oeb
metadata = oeb.metadata
if mi.title:
metadata.add('title', mi.title)
if mi.authors:
for a in mi.authors:
metadata.add('creator', a, attrib={'role':'aut'})
if mi.publisher:
metadata.add('publisher', mi.publisher)
if mi.isbn:
metadata.add('identifier', mi.isbn, attrib={'scheme':'ISBN'})
if not metadata.language:
oeb.logger.warn(u'Language not specified')
metadata.add('language', get_lang())
if not metadata.creator:
oeb.logger.warn('Creator not specified')
metadata.add('creator', self.oeb.translate(__('Unknown')))
if not metadata.title:
oeb.logger.warn('Title not specified')
metadata.add('title', self.oeb.translate(__('Unknown')))
bookid = "urn:uuid:%s" % str(uuid.uuid4())
metadata.add('identifier', bookid, id='calibre-uuid')
for ident in metadata.identifier:
if 'id' in ident.attrib:
self.oeb.uid = metadata.identifier[0]
break
filelist = get_filelist(htmlpath, basedir, opts, log)
htmlfile_map = {}
for f in filelist:
path = f.path
oeb.container = DirContainer(os.path.dirname(path), log)
bname = os.path.basename(path)
id, href = oeb.manifest.generate(id='html', href=bname)
htmlfile_map[path] = href
item = oeb.manifest.add(id, href, 'text/html')
oeb.spine.add(item, True)
self.added_resources = {}
self.log = log
for path, href in htmlfile_map.items():
if not islinux:
path = path.lower()
self.added_resources[path] = href
self.urlnormalize, self.DirContainer = urlnormalize, DirContainer
self.urldefrag = urldefrag
self.guess_type, self.BINARY_MIME = guess_type, BINARY_MIME
for f in filelist:
path = f.path
dpath = os.path.dirname(path)
oeb.container = DirContainer(dpath, log)
item = oeb.manifest.hrefs[htmlfile_map[path]]
rewrite_links(item.data, partial(self.resource_adder, base=dpath))
for item in oeb.manifest.values():
if item.media_type in OEB_STYLES:
dpath = None
for path, href in self.added_resources.items():
if href == item.href:
dpath = os.path.dirname(path)
break
cssutils.replaceUrls(item.data,
partial(self.resource_adder, base=dpath))
toc = self.oeb.toc
self.oeb.auto_generated_toc = True
titles = []
headers = []
for item in self.oeb.spine:
if not item.linear: continue
html = item.data
title = ''.join(xpath(html, '/h:html/h:head/h:title/text()'))
title = re.sub(r'\s+', ' ', title.strip())
if title:
titles.append(title)
headers.append('(unlabled)')
for tag in ('h1', 'h2', 'h3', 'h4', 'h5', 'strong'):
expr = '/h:html/h:body//h:%s[position()=1]/text()'
header = ''.join(xpath(html, expr % tag))
header = re.sub(r'\s+', ' ', header.strip())
if header:
headers[-1] = header
break
use = titles
if len(titles) > len(set(titles)):
use = headers
for title, item in izip(use, self.oeb.spine):
if not item.linear: continue
toc.add(title, item.href)
oeb.container = DirContainer(os.getcwdu(), oeb.log)
return oeb return oeb
def resource_adder(self, link_, base=None):
link = self.urlnormalize(link_)
link, frag = self.urldefrag(link)
link = unquote(link).replace('/', os.sep)
if not link.strip():
return link_
if base and not os.path.isabs(link):
link = os.path.join(base, link)
link = os.path.abspath(link)
if not os.access(link, os.R_OK):
return link_
if not islinux:
link = link.lower()
if link not in self.added_resources:
id, href = self.oeb.manifest.generate(id='added',
href=os.path.basename(link))
self.oeb.log.debug('Added', link)
self.oeb.container = self.DirContainer(os.path.dirname(link),
self.oeb.log)
# Load into memory
guessed = self.guess_type(href)[0]
media_type = guessed or self.BINARY_MIME
self.oeb.manifest.add(id, href, media_type).data
self.added_resources[link] = href
nlink = self.added_resources[link]
if frag:
nlink = '#'.join((nlink, frag))
return nlink

View File

@ -126,7 +126,7 @@ class EbookIterator(object):
from calibre.ebooks.conversion.plumber import Plumber, create_oebbook from calibre.ebooks.conversion.plumber import Plumber, create_oebbook
plumber = Plumber(self.pathtoebook, self.base, self.log) plumber = Plumber(self.pathtoebook, self.base, self.log)
plumber.setup_options() plumber.setup_options()
if hasattr(plumber.opts, 'dont_package'): if self.pathtoebook.lower().endswith('.opf'):
plumber.opts.dont_package = True plumber.opts.dont_package = True
if hasattr(plumber.opts, 'no_process'): if hasattr(plumber.opts, 'no_process'):
plumber.opts.no_process = True plumber.opts.no_process = True

View File

@ -1,150 +0,0 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os, re
from urllib import unquote as urlunquote
from functools import partial
from lxml import etree
import cssutils
from calibre import sanitize_file_name
from calibre.constants import islinux
from calibre.ebooks.oeb.base import OEB_DOCS, urlnormalize, urldefrag, \
rewrite_links
class Package(object):
'''
Move all the parts of an OEB into a folder structure rooted
at the specified folder. All links in recognized content types
are processed, the linked to resources are copied into the local
folder tree and all references to those resources are updated.
The created folder structure is
Base directory(OPF, NCX) -- content (XHTML) -- resources (CSS, Images, etc)
'''
def __init__(self, base='.'):
':param base: The base folder at which the OEB will be rooted'
self.new_base_path = os.path.abspath(base)
def rewrite_links_in(self, item):
old_href = item.old_href.split('#')[0]
new_href = item.href.split('#')[0]
base = os.path.join(self.old_base_path, *old_href.split('/'))
base = os.path.dirname(base)
self.log.debug('\tRewriting links in', base+'/'+
item.href.rpartition('/')[-1])
new_base = os.path.join(self.new_base_path, *new_href.split('/'))
new_base = os.path.dirname(new_base)
if etree.iselement(item.data):
self.rewrite_links_in_xml(item.data, base, new_base)
elif hasattr(item.data, 'cssText'):
self.rewrite_links_in_css(item.data, base, new_base)
def link_replacer(self, link_, base='', new_base=''):
link = urlnormalize(link_)
link, frag = urldefrag(link)
link = urlunquote(link).replace('/', os.sep)
if base and not os.path.isabs(link):
link = os.path.join(base, link)
link = os.path.abspath(link)
if not islinux:
link = link.lower()
if link not in self.map:
return link_
nlink = os.path.relpath(self.map[link], new_base)
if frag:
nlink = '#'.join((nlink, frag))
return nlink.replace(os.sep, '/')
def rewrite_links_in_css(self, sheet, base, new_base):
repl = partial(self.link_replacer, base=base, new_base=new_base)
cssutils.replaceUrls(sheet, repl)
def rewrite_links_in_xml(self, root, base, new_base):
repl = partial(self.link_replacer, base=base, new_base=new_base)
rewrite_links(root, repl)
def uniqify_name(self, new_href, hrefs):
c = 0
while new_href in hrefs:
c += 1
parts = new_href.split('/')
name, ext = os.path.splitext(parts[-1])
name = re.sub(r'_\d+$', '', name)
name += '_%d'%c
parts[-1] = name + ext
new_href = '/'.join(parts)
return new_href
def move_manifest_item(self, item, hrefs):
item.data # Make sure the data has been loaded and cached
old_abspath = os.path.join(self.old_base_path,
*(urldefrag(item.href)[0].split('/')))
old_abspath = os.path.abspath(old_abspath)
bname = item.href.split('/')[-1].partition('#')[0]
new_href = 'content/resources/'
if item.media_type in OEB_DOCS:
new_href = 'content/'
elif item.href.lower().endswith('.ncx'):
new_href = ''
new_href += sanitize_file_name(bname)
if new_href in hrefs:
new_href = self.uniqify_name(new_href, hrefs)
hrefs.add(new_href)
new_abspath = os.path.join(self.new_base_path, *new_href.split('/'))
new_abspath = os.path.abspath(new_abspath)
item.old_href = self.oeb.manifest.hrefs.pop(item.href).href
item.href = new_href
self.oeb.manifest.hrefs[item.href] = item
if not islinux:
old_abspath, new_abspath = old_abspath.lower(), new_abspath.lower()
if old_abspath != new_abspath:
self.map[old_abspath] = new_abspath
def rewrite_links_in_toc(self, toc):
if toc.href:
toc.href = self.link_replacer(toc.href, base=self.old_base_path,
new_base=self.new_base_path)
for x in toc:
self.rewrite_links_in_toc(x)
def __call__(self, oeb, context):
self.map = {}
self.log = oeb.log
self.oeb = oeb
self.old_base_path = os.path.abspath(oeb.container.rootdir)
self.log.info('Packaging HTML files...')
hrefs = set([])
for item in self.oeb.manifest:
self.move_manifest_item(item, hrefs)
self.log.debug('Rewriting links in OEB documents...')
for item in self.oeb.manifest:
self.rewrite_links_in(item)
if getattr(oeb.toc, 'nodes', False):
self.log.debug('Rewriting links in TOC...')
self.rewrite_links_in_toc(oeb.toc)
if hasattr(oeb, 'guide'):
self.log.debug('Rewriting links in guide...')
for ref in oeb.guide.values():
ref.href = self.link_replacer(ref.href,
base=self.old_base_path,
new_base=self.new_base_path)

View File

@ -60,6 +60,10 @@ class DetectStructure(object):
style += '; ' style += '; '
elem.set('style', style+'page-break-before:always') elem.set('style', style+'page-break-before:always')
for node in self.oeb.toc.iter():
if not node.title or not node.title.strip():
node.title = _('Unnamed')
def detect_chapters(self): def detect_chapters(self):
self.detected_chapters = [] self.detected_chapters = []
if self.opts.chapter: if self.opts.chapter:

View File

@ -4,9 +4,9 @@
# #
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: calibre 0.6.0b5\n" "Project-Id-Version: calibre 0.6.0b6\n"
"POT-Creation-Date: 2009-06-11 15:24+PDT\n" "POT-Creation-Date: 2009-06-17 21:18+PDT\n"
"PO-Revision-Date: 2009-06-11 15:24+PDT\n" "PO-Revision-Date: 2009-06-17 21:18+PDT\n"
"Last-Translator: Automatically generated\n" "Last-Translator: Automatically generated\n"
"Language-Team: LANGUAGE\n" "Language-Team: LANGUAGE\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@ -33,6 +33,8 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:404 #: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:404
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:50 #: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:50
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:52 #: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:52
#: /home/kovid/work/calibre/src/calibre/ebooks/html/input.py:312
#: /home/kovid/work/calibre/src/calibre/ebooks/html/input.py:315
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/output.py:24 #: /home/kovid/work/calibre/src/calibre/ebooks/lrf/output.py:24
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:223 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:223
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:253 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:253
@ -57,15 +59,16 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:43 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:43
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:69 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:69
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:78 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:78
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:149 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:117
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:535 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:150
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:719 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:540
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:724
#: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:44 #: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:44
#: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:46 #: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:46
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:798 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:826
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:803 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:831
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/reader.py:162 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/reader.py:169
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/reader.py:165 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/reader.py:172
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/transforms/jacket.py:82 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/transforms/jacket.py:82
#: /home/kovid/work/calibre/src/calibre/ebooks/pdb/ereader/writer.py:101 #: /home/kovid/work/calibre/src/calibre/ebooks/pdb/ereader/writer.py:101
#: /home/kovid/work/calibre/src/calibre/ebooks/pdb/ereader/writer.py:102 #: /home/kovid/work/calibre/src/calibre/ebooks/pdb/ereader/writer.py:102
@ -354,6 +357,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/cybookg3/driver.py:17 #: /home/kovid/work/calibre/src/calibre/devices/cybookg3/driver.py:17
#: /home/kovid/work/calibre/src/calibre/devices/kindle/driver.py:14 #: /home/kovid/work/calibre/src/calibre/devices/kindle/driver.py:14
#: /home/kovid/work/calibre/src/calibre/devices/kindle/driver.py:70 #: /home/kovid/work/calibre/src/calibre/devices/kindle/driver.py:70
#: /home/kovid/work/calibre/src/calibre/devices/kindle/driver.py:79
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:26 #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:26
msgid "John Schember" msgid "John Schember"
msgstr "" msgstr ""
@ -413,6 +417,7 @@ msgid "Communicate with the Kindle eBook reader."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/kindle/driver.py:69 #: /home/kovid/work/calibre/src/calibre/devices/kindle/driver.py:69
#: /home/kovid/work/calibre/src/calibre/devices/kindle/driver.py:78
msgid "Communicate with the Kindle 2 eBook reader." msgid "Communicate with the Kindle 2 eBook reader."
msgstr "" msgstr ""
@ -867,15 +872,15 @@ msgstr ""
msgid "Split all HTML files larger than this size (in KB). This is necessary as most EPUB readers cannot handle large file sizes. The default of %defaultKB is the size required for Adobe Digital Editions." msgid "Split all HTML files larger than this size (in KB). This is necessary as most EPUB readers cannot handle large file sizes. The default of %defaultKB is the size required for Adobe Digital Editions."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/html/input.py:241 #: /home/kovid/work/calibre/src/calibre/ebooks/html/input.py:242
msgid "Traverse links in HTML files breadth first. Normally, they are traversed depth first." msgid "Traverse links in HTML files breadth first. Normally, they are traversed depth first."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/html/input.py:248 #: /home/kovid/work/calibre/src/calibre/ebooks/html/input.py:249
msgid "Maximum levels of recursion when following links in HTML files. Must be non-negative. 0 implies that no links in the root HTML file are followed. Default is %default." msgid "Maximum levels of recursion when following links in HTML files. Must be non-negative. 0 implies that no links in the root HTML file are followed. Default is %default."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/html/input.py:257 #: /home/kovid/work/calibre/src/calibre/ebooks/html/input.py:258
msgid "Normally this input plugin re-arranges all the input files into a standard folder hierarchy. Only use this option if you know what you are doing as it can result in various nasty side effects in the rest of of the conversion pipeline." msgid "Normally this input plugin re-arranges all the input files into a standard folder hierarchy. Only use this option if you know what you are doing as it can result in various nasty side effects in the rest of of the conversion pipeline."
msgstr "" msgstr ""
@ -1356,74 +1361,81 @@ msgstr ""
msgid "Disable compression of the file contents." msgid "Disable compression of the file contents."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1177 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/writer.py:430
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/writer.py:875
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/writer.py:888
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/transforms/structure.py:65
msgid "Unnamed"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1243
msgid "Cover" msgid "Cover"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1178 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1244
msgid "Title Page" msgid "Title Page"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1179 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1245
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/transforms/htmltoc.py:15 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/transforms/htmltoc.py:15
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:48 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:48
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:166 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:166
msgid "Table of Contents" msgid "Table of Contents"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1180 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1246
msgid "Index" msgid "Index"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1181 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1247
msgid "Glossary" msgid "Glossary"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1182 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1248
msgid "Acknowledgements" msgid "Acknowledgements"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1183 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1249
msgid "Bibliography" msgid "Bibliography"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1184 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1250
msgid "Colophon" msgid "Colophon"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1185 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1251
msgid "Copyright" msgid "Copyright"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1186 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1252
msgid "Dedication" msgid "Dedication"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1187 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1253
msgid "Epigraph" msgid "Epigraph"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1188 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1254
msgid "Foreword" msgid "Foreword"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1189 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1255
msgid "List of Illustrations" msgid "List of Illustrations"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1190 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1256
msgid "List of Tables" msgid "List of Tables"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1191 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1257
msgid "Notes" msgid "Notes"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1192 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1258
msgid "Preface" msgid "Preface"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1193 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1259
msgid "Main Text" msgid "Main Text"
msgstr "" msgstr ""
@ -2488,7 +2500,7 @@ msgid "TXT Output"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_output_ui.py:32 #: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_output_ui.py:32
msgid "Newline Type:" msgid "Line ending style:"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/xpath_edit_ui.py:42 #: /home/kovid/work/calibre/src/calibre/gui2/convert/xpath_edit_ui.py:42
@ -5882,39 +5894,39 @@ msgstr ""
msgid "Created by " msgid "Created by "
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/config.py:536 #: /home/kovid/work/calibre/src/calibre/utils/config.py:538
msgid "Path to the database in which books are stored" msgid "Path to the database in which books are stored"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/config.py:538 #: /home/kovid/work/calibre/src/calibre/utils/config.py:540
msgid "Pattern to guess metadata from filenames" msgid "Pattern to guess metadata from filenames"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/config.py:540 #: /home/kovid/work/calibre/src/calibre/utils/config.py:542
msgid "Access key for isbndb.com" msgid "Access key for isbndb.com"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/config.py:542 #: /home/kovid/work/calibre/src/calibre/utils/config.py:544
msgid "Default timeout for network operations (seconds)" msgid "Default timeout for network operations (seconds)"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/config.py:544 #: /home/kovid/work/calibre/src/calibre/utils/config.py:546
msgid "Path to directory in which your library of books is stored" msgid "Path to directory in which your library of books is stored"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/config.py:546 #: /home/kovid/work/calibre/src/calibre/utils/config.py:548
msgid "The language in which to display the user interface" msgid "The language in which to display the user interface"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/config.py:548 #: /home/kovid/work/calibre/src/calibre/utils/config.py:550
msgid "The default output format for ebook conversions." msgid "The default output format for ebook conversions."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/config.py:550 #: /home/kovid/work/calibre/src/calibre/utils/config.py:552
msgid "Read metadata from files" msgid "Read metadata from files"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/config.py:552 #: /home/kovid/work/calibre/src/calibre/utils/config.py:554
msgid "The priority of worker processes" msgid "The priority of worker processes"
msgstr "" msgstr ""
@ -6147,6 +6159,8 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_joelonsoftware.py:15 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_joelonsoftware.py:15
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_jpost.py:8 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_jpost.py:8
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_juventudrebelde_english.py:44 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_juventudrebelde_english.py:44
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_kellog_faculty.py:19
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_kellog_insight.py:19
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_krstarica_en.py:23 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_krstarica_en.py:23
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_latimes.py:17 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_latimes.py:17
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_linux_magazine.py:16 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_linux_magazine.py:16
@ -6194,6 +6208,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_the_oz.py:16 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_the_oz.py:16
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_the_register.py:6 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_the_register.py:6
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_the_scotsman.py:19 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_the_scotsman.py:19
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_theeconomictimes_india.py:25
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_themarketticker.py:17 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_themarketticker.py:17
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_theonion.py:20 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_theonion.py:20
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_time_magazine.py:18 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_time_magazine.py:18