From c4476db00306ecc7fa19b69cb9da2c84ae9c39f8 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 17 Jun 2009 21:32:13 -0700 Subject: [PATCH] Rewrite HTML input plugin. Fixes #2560 (Images not included when converting HTML to OEB with ebook-convert) --- src/calibre/customize/builtins.py | 2 +- src/calibre/ebooks/conversion/plumber.py | 4 +- src/calibre/ebooks/html/input.py | 175 +++++++++++++++--- src/calibre/ebooks/oeb/iterator.py | 2 +- src/calibre/ebooks/oeb/transforms/package.py | 150 --------------- .../ebooks/oeb/transforms/structure.py | 4 + src/calibre/translations/calibre.pot | 95 ++++++---- 7 files changed, 215 insertions(+), 217 deletions(-) delete mode 100644 src/calibre/ebooks/oeb/transforms/package.py diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index e709e5593d..74102f4a27 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -16,7 +16,7 @@ file containing all linked files. This plugin is run \ every time you add an HTML file to the library.\ ''')) version = numeric_version - file_types = set(['html', 'htm', 'xhtml', 'xhtm']) + file_types = set(['html', 'htm', 'xhtml', 'xhtm', 'shtm', 'shtml']) supported_platforms = ['windows', 'osx', 'linux'] on_import = True diff --git a/src/calibre/ebooks/conversion/plumber.py b/src/calibre/ebooks/conversion/plumber.py index 4f5df32479..e9b9051159 100644 --- a/src/calibre/ebooks/conversion/plumber.py +++ b/src/calibre/ebooks/conversion/plumber.py @@ -688,7 +688,7 @@ OptionRecommendation(name='list_recipes', self.flush() def create_oebbook(log, path_or_stream, opts, input_plugin, reader=None, - encoding='utf-8'): + encoding='utf-8', populate=True): ''' Create an OEBBook. ''' @@ -697,6 +697,8 @@ def create_oebbook(log, path_or_stream, opts, input_plugin, reader=None, opts.preprocess_html) oeb = OEBBook(log, html_preprocessor, pretty_print=opts.pretty_print, input_encoding=encoding) + if not populate: + return oeb # Read OEB Book into OEBBook log('Parsing all content...') if reader is None: diff --git a/src/calibre/ebooks/html/input.py b/src/calibre/ebooks/html/input.py index 4385291f69..c1652c81bb 100644 --- a/src/calibre/ebooks/html/input.py +++ b/src/calibre/ebooks/html/input.py @@ -11,15 +11,18 @@ __docformat__ = 'restructuredtext en' Input plugin for HTML or OPF ebooks. ''' -import os, re, sys +import os, re, sys, uuid from urlparse import urlparse, urlunparse from urllib import unquote +from functools import partial +from itertools import izip from calibre.customize.conversion import InputFormatPlugin -from calibre.ebooks.metadata.opf2 import OPFCreator from calibre.ebooks.chardet import xml_to_unicode from calibre.customize.conversion import OptionRecommendation +from calibre.constants import islinux from calibre import unicode_path +from calibre.startup import get_lang class Link(object): ''' @@ -120,8 +123,6 @@ class HTMLFile(object): self.title = match.group(1) if match is not None else self.title self.find_links(src) - - def __eq__(self, other): return self.path == getattr(other, 'path', other) @@ -264,37 +265,163 @@ class HTMLInput(InputFormatPlugin): def convert(self, stream, opts, file_ext, log, accelerators): - from calibre.ebooks.metadata.html import get_metadata_ - basedir = os.getcwd() self.opts = opts if hasattr(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) - opfpath = os.path.abspath('metadata.opf') - - if opts.dont_package: - return opfpath + if file_ext != 'opf': + if opts.dont_package: + raise ValueError('The --dont-package option is not supported for an HTML input file') + from calibre.ebooks.metadata.html import get_metadata + oeb = self.create_oebbook(stream.name, basedir, opts, log, + get_metadata(stream)) + return oeb 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) - from calibre.ebooks.oeb.transforms.package import Package - Package(os.getcwdu())(oeb, opts) + def create_oebbook(self, htmlpath, basedir, opts, log, mi): + 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 + 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 + + + + + diff --git a/src/calibre/ebooks/oeb/iterator.py b/src/calibre/ebooks/oeb/iterator.py index 6d05dc74fd..af8dd84162 100644 --- a/src/calibre/ebooks/oeb/iterator.py +++ b/src/calibre/ebooks/oeb/iterator.py @@ -126,7 +126,7 @@ class EbookIterator(object): from calibre.ebooks.conversion.plumber import Plumber, create_oebbook plumber = Plumber(self.pathtoebook, self.base, self.log) plumber.setup_options() - if hasattr(plumber.opts, 'dont_package'): + if self.pathtoebook.lower().endswith('.opf'): plumber.opts.dont_package = True if hasattr(plumber.opts, 'no_process'): plumber.opts.no_process = True diff --git a/src/calibre/ebooks/oeb/transforms/package.py b/src/calibre/ebooks/oeb/transforms/package.py deleted file mode 100644 index 20fe6e2650..0000000000 --- a/src/calibre/ebooks/oeb/transforms/package.py +++ /dev/null @@ -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 ' -__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) diff --git a/src/calibre/ebooks/oeb/transforms/structure.py b/src/calibre/ebooks/oeb/transforms/structure.py index 2f083153ce..4e653c212f 100644 --- a/src/calibre/ebooks/oeb/transforms/structure.py +++ b/src/calibre/ebooks/oeb/transforms/structure.py @@ -60,6 +60,10 @@ class DetectStructure(object): style += '; ' 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): self.detected_chapters = [] if self.opts.chapter: diff --git a/src/calibre/translations/calibre.pot b/src/calibre/translations/calibre.pot index d4e86afcc9..4a9d307250 100644 --- a/src/calibre/translations/calibre.pot +++ b/src/calibre/translations/calibre.pot @@ -4,9 +4,9 @@ # msgid "" msgstr "" -"Project-Id-Version: calibre 0.6.0b5\n" -"POT-Creation-Date: 2009-06-11 15:24+PDT\n" -"PO-Revision-Date: 2009-06-11 15:24+PDT\n" +"Project-Id-Version: calibre 0.6.0b6\n" +"POT-Creation-Date: 2009-06-17 21:18+PDT\n" +"PO-Revision-Date: 2009-06-17 21:18+PDT\n" "Last-Translator: Automatically generated\n" "Language-Team: LANGUAGE\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/fb2/input.py:50 #: /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/metadata/__init__.py:223 #: /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:69 #: /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:535 -#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:719 +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:117 +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:150 +#: /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:46 -#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:798 -#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:803 -#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/reader.py:162 -#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/reader.py:165 +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:826 +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:831 +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/reader.py:169 +#: /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/pdb/ereader/writer.py:101 #: /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/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:79 #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:26 msgid "John Schember" msgstr "" @@ -413,6 +417,7 @@ msgid "Communicate with the Kindle eBook reader." msgstr "" #: /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." 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." 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." 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." 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." msgstr "" @@ -1356,74 +1361,81 @@ msgstr "" msgid "Disable compression of the file contents." 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" 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" 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/gui2/viewer/main.py:48 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:166 msgid "Table of Contents" 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" 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" 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" 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" 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" 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" 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" 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" 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" 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" 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" 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" 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" 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" msgstr "" @@ -2488,7 +2500,7 @@ msgid "TXT Output" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_output_ui.py:32 -msgid "Newline Type:" +msgid "Line ending style:" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/xpath_edit_ui.py:42 @@ -5882,39 +5894,39 @@ msgstr "" msgid "Created by " 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" 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" 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" 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)" 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" 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" 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." 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" 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" 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_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_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_latimes.py:17 #: /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_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_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_theonion.py:20 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_time_magazine.py:18