diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py index cf0034d7d5..fe140df032 100644 --- a/src/calibre/__init__.py +++ b/src/calibre/__init__.py @@ -17,10 +17,15 @@ from calibre.constants import iswindows, isosx, islinux, isfrozen, \ filesystem_encoding import mechanize -mimetypes.add_type('application/epub+zip', '.epub') -mimetypes.add_type('text/x-sony-bbeb+xml', '.lrs') -mimetypes.add_type('application/x-sony-bbeb', '.lrf') -mimetypes.add_type('application/x-dtbncx+xml', '.ncx') +mimetypes.add_type('application/epub+zip', '.epub') +mimetypes.add_type('text/x-sony-bbeb+xml', '.lrs') +mimetypes.add_type('application/x-sony-bbeb', '.lrf') +mimetypes.add_type('application/x-dtbncx+xml', '.ncx') +mimetypes.add_type('application/adobe-page-template+xml', '.xpgt') +mimetypes.add_type('application/x-font-opentype', '.otf') +mimetypes.add_type('application/x-font-truetype', '.ttf') +mimetypes.add_type('application/oebps-package+xml', '.opf') + def to_unicode(raw, encoding='utf-8', errors='strict'): if isinstance(raw, unicode): diff --git a/src/calibre/constants.py b/src/calibre/constants.py index b92fb0f0e1..ab5cc9f6b0 100644 --- a/src/calibre/constants.py +++ b/src/calibre/constants.py @@ -2,7 +2,7 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' __docformat__ = 'restructuredtext en' __appname__ = 'calibre' -__version__ = '0.4.132' +__version__ = '0.4.133' __author__ = "Kovid Goyal " ''' Various run time constants. diff --git a/src/calibre/devices/kindle/driver.py b/src/calibre/devices/kindle/driver.py index d4cbb5b90b..8ef1ba6b9b 100755 --- a/src/calibre/devices/kindle/driver.py +++ b/src/calibre/devices/kindle/driver.py @@ -27,6 +27,7 @@ class KINDLE(USBMS): STORAGE_CARD_VOLUME_LABEL = 'Kindle Storage Card' EBOOK_DIR_MAIN = "documents" + EBOOK_DIR_CARD = "documents" SUPPORTS_SUB_DIRS = True def delete_books(self, paths, end_session=True): diff --git a/src/calibre/devices/prs505/driver.py b/src/calibre/devices/prs505/driver.py index bbda8f95d2..8d505683aa 100644 --- a/src/calibre/devices/prs505/driver.py +++ b/src/calibre/devices/prs505/driver.py @@ -147,6 +147,7 @@ class PRS505(Device): def open_windows(self): + time.sleep(6) drives = [] wmi = __import__('wmi', globals(), locals(), [], -1) c = wmi.WMI() diff --git a/src/calibre/devices/usbms/device.py b/src/calibre/devices/usbms/device.py index aba41c78cb..761fe9ba74 100644 --- a/src/calibre/devices/usbms/device.py +++ b/src/calibre/devices/usbms/device.py @@ -40,7 +40,7 @@ class Device(_Device): %(BCD_start)s - + %(main_memory)s %(deviceclass)s @@ -54,7 +54,7 @@ class Device(_Device): %(BCD_start)s - + %(storage_card)s %(deviceclass)s @@ -172,6 +172,7 @@ class Device(_Device): return prefix def open_windows(self): + time.sleep(6) drives = {} wmi = __import__('wmi', globals(), locals(), [], -1) c = wmi.WMI() diff --git a/src/calibre/ebooks/epub/__init__.py b/src/calibre/ebooks/epub/__init__.py index 1bbb80cf13..863f2f8db0 100644 --- a/src/calibre/ebooks/epub/__init__.py +++ b/src/calibre/ebooks/epub/__init__.py @@ -102,13 +102,23 @@ def config(defaults=None, name='epub'): c.remove_opt('zip') c.add_opt('output', ['-o', '--output'], default=None, - help=_('The output EPUB file. If not specified, it is derived from the input file name.')) + help=_('The output EPUB file. If not specified, it is ' + 'derived from the input file name.')) c.add_opt('profile', ['--profile'], default='PRS505', choices=list(PROFILES.keys()), - help=_('Profile of the target device this EPUB is meant for. Set to None to create a device independent EPUB. The profile is used for device specific restrictions on the EPUB. Choices are: ')+str(list(PROFILES.keys()))) + help=_('Profile of the target device this EPUB is meant for. ' + 'Set to None to create a device independent EPUB. ' + 'The profile is used for device specific restrictions ' + 'on the EPUB. Choices are: ')+str(list(PROFILES.keys()))) c.add_opt('override_css', ['--override-css'], default=None, - help=_('Either the path to a CSS stylesheet or raw CSS. This CSS will override any existing CSS declarations in the source files.')) - structure = c.add_group('structure detection', _('Control auto-detection of document structure.')) - structure('chapter', ['--chapter'], default="//*[re:match(name(), 'h[1-2]') and re:test(., 'chapter|book|section|part', 'i')] | //*[@class = 'chapter']", + help=_('Either the path to a CSS stylesheet or raw CSS. ' + 'This CSS will override any existing CSS ' + 'declarations in the source files.')) + structure = c.add_group('structure detection', + _('Control auto-detection of document structure.')) + structure('chapter', ['--chapter'], + default="//*[re:match(name(), 'h[1-2]') and " + "re:test(., 'chapter|book|section|part', 'i')] | " + "//*[@class = 'chapter']", help=_('''\ An XPath expression to detect chapter titles. The default is to consider

or

tags that contain the words "chapter","book","section" or "part" as chapter titles as @@ -118,14 +128,31 @@ use the expression "/". See the XPath Tutorial in the calibre User Manual for fu help on using this feature. ''').replace('\n', ' ')) structure('chapter_mark', ['--chapter-mark'], choices=['pagebreak', 'rule', 'both', 'none'], - default='pagebreak', help=_('Specify how to mark detected chapters. A value of "pagebreak" will insert page breaks before chapters. A value of "rule" will insert a line before chapters. A value of "none" will disable chapter marking and a value of "both" will use both page breaks and lines to mark chapters.')) + default='pagebreak', + help=_('Specify how to mark detected chapters. A value of ' + '"pagebreak" will insert page breaks before chapters. ' + 'A value of "rule" will insert a line before chapters. ' + 'A value of "none" will disable chapter marking and a ' + 'value of "both" will use both page breaks and lines ' + 'to mark chapters.')) structure('cover', ['--cover'], default=None, help=_('Path to the cover to be used for this book')) structure('prefer_metadata_cover', ['--prefer-metadata-cover'], default=False, action='store_true', - help=_('Use the cover detected from the source file in preference to the specified cover.')) + help=_('Use the cover detected from the source file in preference ' + 'to the specified cover.')) + structure('remove_first_image', ['--remove-first-image'], default=False, + help=_('Remove the first image from the input ebook. Useful if ' + 'the first image in the source file is a cover and you ' + 'are specifying an external cover.')) structure('dont_split_on_page_breaks', ['--dont-split-on-page-breaks'], default=False, - help=_('Turn off splitting at page breaks. Normally, input files are automatically split at every page break into two files. This gives an output ebook that can be parsed faster and with less resources. However, splitting is slow and if your source file contains a very large number of page breaks, you should turn off splitting on page breaks.')) + help=_('Turn off splitting at page breaks. Normally, input files ' + 'are automatically split at every page break into ' + 'two files. This gives an output ebook that can be parsed ' + 'faster and with less resources. However, splitting is ' + 'slow and if your source file contains a very large ' + 'number of page breaks, you should turn off splitting ' + 'on page breaks.')) toc = c.add_group('toc', _('''\ Control the automatic generation of a Table of Contents. If an OPF file is detected @@ -133,21 +160,36 @@ and it specifies a Table of Contents, then that will be used rather than trying to auto-generate a Table of Contents. ''').replace('\n', ' ')) toc('max_toc_links', ['--max-toc-links'], default=50, - help=_('Maximum number of links to insert into the TOC. Set to 0 to disable. Default is: %default. Links are only added to the TOC if less than the --toc-threshold number of chapters were detected.')) + help=_('Maximum number of links to insert into the TOC. Set to 0 ' + 'to disable. Default is: %default. Links are only added to the ' + 'TOC if less than the --toc-threshold number of chapters were detected.')) toc('no_chapters_in_toc', ['--no-chapters-in-toc'], default=False, help=_("Don't add auto-detected chapters to the Table of Contents.")) toc('toc_threshold', ['--toc-threshold'], default=6, - help=_('If fewer than this number of chapters is detected, then links are added to the Table of Contents. Default: %default')) + help=_('If fewer than this number of chapters is detected, then links ' + 'are added to the Table of Contents. Default: %default')) toc('level1_toc', ['--level1-toc'], default=None, - help=_('XPath expression that specifies all tags that should be added to the Table of Contents at level one. If this is specified, it takes precedence over other forms of auto-detection.')) + help=_('XPath expression that specifies all tags that should be added ' + 'to the Table of Contents at level one. If this is specified, ' + 'it takes precedence over other forms of auto-detection.')) toc('level2_toc', ['--level2-toc'], default=None, - help=_('XPath expression that specifies all tags that should be added to the Table of Contents at level two. Each entry is added under the previous level one entry.')) + help=_('XPath expression that specifies all tags that should be added ' + 'to the Table of Contents at level two. Each entry is added ' + 'under the previous level one entry.')) toc('level3_toc', ['--level3-toc'], default=None, - help=_('XPath expression that specifies all tags that should be added to the Table of Contents at level three. Each entry is added under the previous level two entry.')) + help=_('XPath expression that specifies all tags that should be added ' + 'to the Table of Contents at level three. Each entry is added ' + 'under the previous level two entry.')) toc('from_ncx', ['--from-ncx'], default=None, - help=_('Path to a .ncx file that contains the table of contents to use for this ebook. The NCX file should contain links relative to the directory it is placed in. See http://www.niso.org/workrooms/daisy/Z39-86-2005.html#NCX for an overview of the NCX format.')) + help=_('Path to a .ncx file that contains the table of contents to use ' + 'for this ebook. The NCX file should contain links relative to ' + 'the directory it is placed in. See ' + 'http://www.niso.org/workrooms/daisy/Z39-86-2005.html#NCX for ' + 'an overview of the NCX format.')) toc('use_auto_toc', ['--use-auto-toc'], default=False, - help=_('Normally, if the source file already has a Table of Contents, it is used in preference to the autodetected one. With this option, the autodetected one is always used.')) + help=_('Normally, if the source file already has a Table of Contents, ' + 'it is used in preference to the autodetected one. ' + 'With this option, the autodetected one is always used.')) layout = c.add_group('page layout', _('Control page layout')) layout('margin_top', ['--margin-top'], default=5.0, @@ -159,18 +201,33 @@ to auto-generate a Table of Contents. layout('margin_right', ['--margin-right'], default=5.0, help=_('Set the right margin in pts. Default is %default')) layout('base_font_size2', ['--base-font-size'], default=12.0, - help=_('The base font size in pts. Default is %defaultpt. Set to 0 to disable rescaling of fonts.')) + help=_('The base font size in pts. Default is %defaultpt. ' + 'Set to 0 to disable rescaling of fonts.')) layout('remove_paragraph_spacing', ['--remove-paragraph-spacing'], default=False, - help=_('Remove spacing between paragraphs. Will not work if the source file forces inter-paragraph spacing.')) + help=_('Remove spacing between paragraphs. ' + 'Also sets a indent on paragraphs of 1.5em. ' + 'You can override this by adding p {text-indent: 0cm} to ' + '--override-css. Spacing removal will not work if the source ' + 'file forces inter-paragraph spacing.')) + layout('no_justification', ['--no-justification'], default=False, + help=_('Do not force text to be justified in output.')) + layout('linearize_tables', ['--linearize-tables'], default=False, + help=_('Remove table markup, converting it into paragraphs. ' + 'This is useful if your source file uses a table to manage layout.')) layout('preserve_tag_structure', ['--preserve-tag-structure'], default=False, - help=_('Preserve the HTML tag structure while splitting large HTML files. This is only neccessary if the HTML files contain CSS that uses sibling selectors. Enabling this greatly slows down processing of large HTML files.')) + help=_('Preserve the HTML tag structure while splitting large HTML files. ' + 'This is only neccessary if the HTML files contain CSS that ' + 'uses sibling selectors. Enabling this greatly slows down ' + 'processing of large HTML files.')) c.add_opt('show_opf', ['--show-opf'], default=False, group='debug', help=_('Print generated OPF file to stdout')) c.add_opt('show_ncx', ['--show-ncx'], default=False, group='debug', help=_('Print generated NCX file to stdout')) - c.add_opt('keep_intermediate', ['--keep-intermediate-files'], group='debug', default=False, + c.add_opt('keep_intermediate', ['--keep-intermediate-files'], group='debug', + default=False, help=_('Keep intermediate files during processing by html2epub')) c.add_opt('extract_to', ['--extract-to'], group='debug', default=None, - help=_('Extract the contents of the produced EPUB file to the specified directory.')) + help=_('Extract the contents of the produced EPUB file to the ' + 'specified directory.')) return c \ No newline at end of file diff --git a/src/calibre/ebooks/epub/from_html.py b/src/calibre/ebooks/epub/from_html.py index 30191617a5..ca50fe7a5d 100644 --- a/src/calibre/ebooks/epub/from_html.py +++ b/src/calibre/ebooks/epub/from_html.py @@ -141,7 +141,7 @@ class HTMLProcessor(Processor, Rationalizer): p = QPixmap() p.load(path) if not p.isNull(): - p.save(path+'_calibre_converted.jpg') + p.save(path + '_calibre_converted.jpg') os.remove(path) for key, val in self.resource_map.items(): if val == rpath: @@ -194,7 +194,10 @@ class HTMLProcessor(Processor, Rationalizer): if not tag.text and not tag.get('src', False): tag.getparent().remove(tag) - + if self.opts.linearize_tables: + for tag in self.root.xpath('//table | //tr | //th | //td'): + tag.tag = 'div' + def save(self): for meta in list(self.root.xpath('//meta')): @@ -203,6 +206,13 @@ class HTMLProcessor(Processor, Rationalizer): # self.convert_image(img) Processor.save(self) + def remove_first_image(self): + images = self.root.xpath('//img') + if images: + images[0].getparent().remove(images[0]) + return True + return False + @@ -224,10 +234,13 @@ def parse_content(filelist, opts, tdir): resource_map, stylesheets = {}, {} toc = TOC(base_path=tdir, type='root') stylesheet_map = {} + first_image_removed = False for htmlfile in filelist: logging.getLogger('html2epub').debug('Processing %s...'%htmlfile) hp = HTMLProcessor(htmlfile, opts, os.path.join(tdir, 'content'), resource_map, filelist, stylesheets) + if not first_image_removed and opts.remove_first_image: + first_image_removed = hp.remove_first_image() hp.populate_toc(toc) hp.save() stylesheet_map[os.path.basename(hp.save_path())] = \ @@ -441,12 +454,9 @@ def convert(htmlfile, opts, notification=None, create_epub=True, cpath = os.path.join(tdir, 'content', 'resources', '_cover_.jpg') if os.path.exists(cpath): - opf.add_path_to_manifest(cpath, 'image/jpeg') + opf.add_path_to_manifest(cpath, 'image/jpeg') with open(opf_path, 'wb') as f: - raw = opf.render() - if not raw.startswith('\n'+raw - f.write(raw) + f.write(opf.render()) ncx_path = os.path.join(os.path.dirname(opf_path), 'toc.ncx') if os.path.exists(ncx_path) and os.stat(ncx_path).st_size > opts.profile.flow_size: logger.info('Condensing NCX from %d bytes...'%os.stat(ncx_path).st_size) @@ -462,7 +472,7 @@ def convert(htmlfile, opts, notification=None, create_epub=True, logger.info(_('Output written to ')+opts.output) if opts.show_opf: - print open(os.path.join(tdir, 'metadata.opf')).read() + print open(opf_path, 'rb').read() if opts.extract_to is not None: if os.path.exists(opts.extract_to): diff --git a/src/calibre/ebooks/html.py b/src/calibre/ebooks/html.py index a7883ed763..9a273c42ce 100644 --- a/src/calibre/ebooks/html.py +++ b/src/calibre/ebooks/html.py @@ -9,7 +9,7 @@ directory or zip file. All the action starts in :function:`create_dir`. ''' import sys, re, os, shutil, logging, tempfile, cStringIO, operator, functools -from urlparse import urlparse +from urlparse import urlparse, urlunparse from urllib import unquote from lxml import etree @@ -98,7 +98,8 @@ class Link(object): @classmethod def url_to_local_path(cls, url, base): - path = url.path + path = urlunparse(('', '', url.path, url.params, url.query, '')) + path = unquote(path) if os.path.isabs(path): return path return os.path.abspath(os.path.join(base, path)) @@ -111,11 +112,11 @@ class Link(object): ''' assert isinstance(url, unicode) and isinstance(base, unicode) self.url = url - self.parsed_url = urlparse(unquote(self.url)) + self.parsed_url = urlparse(self.url) self.is_local = self.parsed_url.scheme in ('', 'file') self.is_internal = self.is_local and not bool(self.parsed_url.path) self.path = None - self.fragment = self.parsed_url.fragment + self.fragment = unquote(self.parsed_url.fragment) if self.is_local and not self.is_internal: self.path = self.url_to_local_path(self.parsed_url, base) @@ -594,15 +595,21 @@ class Processor(Parser): ''' Populate the Table of Contents from detected chapters and links. ''' - - def add_item(href, fragment, text, target, type='link'): - for entry in toc.flat(): - if entry.href == href and entry.fragment == fragment: - return entry - if len(text) > 50: - text = text[:50] + u'\u2026' - return target.add_item(href, fragment, text, type=type) - + class Adder(object): + + def __init__(self, toc): + self.next_play_order = max([x.play_order for x in toc.flat()]) + + def __call__(self, href, fragment, text, target, type='link'): + for entry in toc.flat(): + if entry.href == href and entry.fragment == fragment: + return entry + if len(text) > 50: + text = text[:50] + u'\u2026' + self.next_play_order += 1 + return target.add_item(href, fragment, text, type=type, + play_order=self.next_play_order) + add_item = Adder(toc) name = self.htmlfile_map[self.htmlfile.path] href = 'content/'+name @@ -629,13 +636,15 @@ class Processor(Parser): if self.opts.level1_toc is not None: level1 = self.opts.level1_toc(self.root) + level1_order = [] if level1: added = {} for elem in level1: text, _href, frag = elem_to_link(elem, href, counter) counter += 1 if text: - added[elem] = add_item(_href, frag, text, toc, type='chapter') + level1_order.append(add_item(_href, frag, text, toc, type='chapter')) + added[elem] = level1_order[-1] add_item(_href, frag, 'Top', added[elem], type='chapter') if self.opts.level2_toc is not None: added2 = {} @@ -664,6 +673,15 @@ class Processor(Parser): if text: add_item(_href, frag, text, level2, type='chapter') + + if level1_order: # Fix play order + next_play_order = level1_order[0].play_order + for x in level1_order: + for y in x.flat(): + y.play_order = next_play_order + next_play_order += 1 + + if len(toc) > 0: return @@ -867,6 +885,8 @@ class Processor(Parser): css += '\n\na { color: inherit; text-decoration: inherit; cursor: default; }\na[href] { color: blue; text-decoration: underline; cursor:pointer; }' if self.opts.remove_paragraph_spacing: css += '\n\np {text-indent: 1.5em; margin-top:0pt; margin-bottom:0pt; padding:0pt; border:0pt;}' + if not self.opts.no_justification: + css += '\n\nbody {text-align: justify}' if self.opts.override_css: css += '\n\n' + self.opts.override_css self.override_css = self.css_parser.parseString(self.preprocess_css(css)) @@ -987,7 +1007,6 @@ def merge_metadata(htmlfile, opf, opts): mi = get_metadata(open(htmlfile, 'rb'), 'html') except: mi = MetaInformation(None, None) - if opts.from_opf is not None and os.access(opts.from_opf, os.R_OK): mi.smart_update(OPF(open(opts.from_opf, 'rb'), os.path.abspath(os.path.dirname(opts.from_opf)))) for attr in ('title', 'authors', 'publisher', 'tags', 'comments'): diff --git a/src/calibre/ebooks/lit/writer.py b/src/calibre/ebooks/lit/writer.py index 5026c9e267..4a059b6433 100644 --- a/src/calibre/ebooks/lit/writer.py +++ b/src/calibre/ebooks/lit/writer.py @@ -23,7 +23,7 @@ from urllib import unquote as urlunquote from lxml import etree from calibre.ebooks.lit.reader import DirectoryEntry import calibre.ebooks.lit.maps as maps -from calibre.ebooks.oeb.base import OEB_DOCS, OEB_STYLES, OEB_CSS_MIME, \ +from calibre.ebooks.oeb.base import OEB_DOCS, XHTML_MIME, OEB_STYLES, \ CSS_MIME, OPF_MIME, XML_NS, XML from calibre.ebooks.oeb.base import namespace, barename, prefixname, \ urlnormalize, xpath @@ -474,7 +474,7 @@ class LitWriter(object): name = '/data/' + item.id data = item.data secnum = 0 - if not isinstance(data, basestring): + if isinstance(data, etree._Element): self._add_folder(name) rebin = ReBinary(data, item, self._oeb, map=HTML_MAP) self._add_file(name + '/ahc', rebin.ahc, 0) @@ -483,6 +483,8 @@ class LitWriter(object): data = rebin.content name = name + '/content' secnum = 1 + elif isinstance(data, unicode): + data = data.encode('utf-8') self._add_file(name, data, secnum) item.size = len(data) @@ -493,7 +495,7 @@ class LitWriter(object): if item.spine_position is not None: key = 'linear' if item.linear else 'nonlinear' manifest[key].append(item) - elif item.media_type == CSS_MIME: + elif item.media_type in OEB_STYLES: manifest['css'].append(item) elif item.media_type in LIT_IMAGES: manifest['images'].append(item) @@ -506,6 +508,11 @@ class LitWriter(object): data.write(pack(' 0 - + def __str__(self): resources = map(repr, self) return '[%s]'%', '.join(resources) - + def __repr__(self): return str(self) - + def append(self, resource): if not isinstance(resource, Resource): raise ValueError('Can only append objects of type Resource') self._resources.append(resource) - + def remove(self, resource): self._resources.remove(resource) - + def replace(self, start, end, items): 'Same as list[start:end] = items' self._resources[start:end] = items - + @staticmethod def from_directory_contents(top, topdown=True): collection = ResourceCollection() @@ -172,16 +172,16 @@ class ResourceCollection(object): res.set_basedir(top) collection.append(res) return collection - + def set_basedir(self, path): for res in self: res.set_basedir(path) - + class MetaInformation(object): '''Convenient encapsulation of book metadata''' - + @staticmethod def copy(mi): ans = MetaInformation(mi.title, mi.authors) @@ -191,7 +191,7 @@ class MetaInformation(object): 'manifest', 'spine', 'toc', 'cover', 'language', 'book_producer'): if hasattr(mi, attr): setattr(ans, attr, getattr(mi, attr)) - + def __init__(self, title, authors=[_('Unknown')]): ''' @param title: title or ``_('Unknown')`` or a MetaInformation object @@ -209,14 +209,14 @@ class MetaInformation(object): self.tags = getattr(mi, 'tags', []) #: mi.cover_data = (ext, data) self.cover_data = getattr(mi, 'cover_data', (None, None)) - - for x in ('author_sort', 'title_sort', 'comments', 'publisher', + + for x in ('author_sort', 'title_sort', 'comments', 'publisher', 'series', 'series_index', 'rating', 'isbn', 'language', 'application_id', 'manifest', 'toc', 'spine', 'guide', 'cover', 'book_producer', ): setattr(self, x, getattr(mi, x, None)) - + def smart_update(self, mi): ''' Merge the information in C{mi} into self. In case of conflicts, the information @@ -224,31 +224,37 @@ class MetaInformation(object): ''' if mi.title and mi.title != _('Unknown'): self.title = mi.title - + if mi.authors and mi.authors[0] != _('Unknown'): self.authors = mi.authors - - for attr in ('author_sort', 'title_sort', 'comments', + + for attr in ('author_sort', 'title_sort', 'comments', 'publisher', 'series', 'series_index', 'rating', - 'isbn', 'application_id', 'manifest', 'spine', 'toc', + 'isbn', 'application_id', 'manifest', 'spine', 'toc', 'cover', 'language', 'guide', 'book_producer'): val = getattr(mi, attr, None) if val is not None: setattr(self, attr, val) - + if mi.tags: self.tags += mi.tags self.tags = list(set(self.tags)) - + if getattr(mi, 'cover_data', None) and mi.cover_data[0] is not None: self.cover_data = mi.cover_data - - + + def format_series_index(self): + try: + x = float(self.series_index) + except ValueError: + x = 1.0 + return '%d'%x if int(x) == x else '%.2f'%x + def __unicode__(self): ans = [ fmt('Title', self.title) ] def fmt(x, y): ans.append(u'%-20s: %s'%(unicode(x), unicode(y))) - + if self.title_sort: fmt('Title sort', self.title_sort) if self.authors: @@ -265,13 +271,13 @@ class MetaInformation(object): if self.tags: fmt('Tags', u', '.join([unicode(t) for t in self.tags])) if self.series: - fmt('Series', self.series + '#%s'%self.series_index) + fmt('Series', self.series + ' #%s'%self.format_series_index()) if self.language: fmt('Language', self.language) if self.rating is not None: fmt('Rating', self.rating) return u'\n'.join(ans) - + def to_html(self): ans = [(_('Title'), unicode(self.title))] ans += [(_('Author(s)'), (authors_to_string(self.authors) if self.authors else _('Unknown')))] @@ -280,16 +286,17 @@ class MetaInformation(object): ans += [(_('Comments'), unicode(self.comments))] ans += [('ISBN', unicode(self.isbn))] ans += [(_('Tags'), u', '.join([unicode(t) for t in self.tags]))] - ans += [(_('Series'), unicode(self.series))] + if self.series: + ans += [(_('Series'), unicode(self.series))+ ' #%s'%self.format_series_index()] ans += [(_('Language'), unicode(self.language))] for i, x in enumerate(ans): ans[i] = u'%s%s'%x return u'%s
'%u'\n'.join(ans) - - - + + + def __str__(self): return self.__unicode__().encode('utf-8') - + def __nonzero__(self): return bool(self.title or self.author or self.comments or self.tags) diff --git a/src/calibre/ebooks/metadata/opf2.py b/src/calibre/ebooks/metadata/opf2.py index 51fc242d69..718d615e71 100644 --- a/src/calibre/ebooks/metadata/opf2.py +++ b/src/calibre/ebooks/metadata/opf2.py @@ -789,7 +789,10 @@ class OPF(object): return elem def render(self, encoding='utf-8'): - return etree.tostring(self.root, encoding='utf-8', pretty_print=True) + raw = etree.tostring(self.root, encoding=encoding, pretty_print=True) + if not raw.lstrip().startswith('\n'%encoding.upper()+raw + return raw def smart_update(self, mi): for attr in ('author_sort', 'title_sort', 'comments', 'category', @@ -877,7 +880,8 @@ class OPFCreator(MetaInformation): self.guide = Guide.from_opf_guide(guide_element, self.base_path) self.guide.set_basedir(self.base_path) - def render(self, opf_stream, ncx_stream=None, ncx_manifest_entry=None): + def render(self, opf_stream=sys.stdout, ncx_stream=None, + ncx_manifest_entry=None): from calibre.resources import opf_template from calibre.utils.genshi.template import MarkupTemplate template = MarkupTemplate(opf_template) diff --git a/src/calibre/ebooks/mobi/mobiml.py b/src/calibre/ebooks/mobi/mobiml.py index 50d7b298b9..7ecd127452 100644 --- a/src/calibre/ebooks/mobi/mobiml.py +++ b/src/calibre/ebooks/mobi/mobiml.py @@ -138,7 +138,8 @@ class MobiMLizer(object): return result def mobimlize_content(self, tag, text, bstate, istates): - bstate.content = True + if text or tag != 'br': + bstate.content = True istate = istates[-1] para = bstate.para if tag in SPECIAL_TAGS and not text: @@ -188,11 +189,6 @@ class MobiMLizer(object): vspace -= 1 if istate.halign != 'auto': para.attrib['align'] = istate.halign - if istate.ids: - last = bstate.body[-1] - for id in istate.ids: - last.addprevious(etree.Element(XHTML('a'), attrib={'id': id})) - istate.ids.clear() pstate = bstate.istate if tag in CONTENT_TAGS: bstate.inline = para @@ -200,6 +196,11 @@ class MobiMLizer(object): etree.SubElement(para, XHTML(tag), attrib=istate.attrib) elif tag in TABLE_TAGS: para.attrib['valign'] = 'top' + if istate.ids: + last = bstate.body[-1] + for id in istate.ids: + last.addprevious(etree.Element(XHTML('a'), attrib={'id': id})) + istate.ids.clear() if not text: return if not pstate or istate != pstate: diff --git a/src/calibre/ebooks/mobi/reader.py b/src/calibre/ebooks/mobi/reader.py index e2bb6f6d5f..6811f9ccda 100644 --- a/src/calibre/ebooks/mobi/reader.py +++ b/src/calibre/ebooks/mobi/reader.py @@ -121,6 +121,7 @@ class BookHeader(object): sublangid = (langcode >> 10) & 0xFF self.language = main_language.get(langid, 'ENGLISH') self.sublanguage = sub_language.get(sublangid, 'NEUTRAL') + self.mobi_version = struct.unpack('>I', raw[0x68:0x6c])[0] self.first_image_index = struct.unpack('>L', raw[0x6c:0x6c+4])[0] self.exth_flag, = struct.unpack('>L', raw[0x80:0x84]) @@ -132,11 +133,8 @@ class BookHeader(object): class MobiReader(object): - PAGE_BREAK_PAT = re.compile(r'(<[/]{0,1}mbp:pagebreak\s*[/]{0,1}>)+', re.IGNORECASE) - IMAGE_PATS = map(re.compile, (r'\shirecindex=[\'"]{0,1}(\d+)[\'"]{0,1}', - r'\srecindex=[\'"]{0,1}(\d+)[\'"]{0,1}', - r'\slorecindex=[\'"]{0,1}(\d+)[\'"]{0,1}')) + IMAGE_ATTRS = ('lowrecindex', 'recindex', 'hirecindex') def __init__(self, filename_or_stream, verbose=False): self.verbose = verbose @@ -247,6 +245,7 @@ class MobiReader(object): self.processed_html = re.sub(r'
', '', self.processed_html) if self.book_header.ancient and '')+'' + self.processed_html = self.processed_html.replace('\r\n', '\n') self.processed_html = self.processed_html.replace('> <', '>\n<') for t, c in [('b', 'bold'), ('i', 'italic')]: self.processed_html = re.sub(r'(?i)<%s>'%t, r''%c, self.processed_html) @@ -264,6 +263,7 @@ class MobiReader(object): 'x-large' : '5', 'xx-large' : '6', } + mobi_version = self.book_header.mobi_version for tag in root.iter(etree.Element): if tag.tag in ('country-region', 'place', 'placetype', 'placename', 'state', 'city'): @@ -290,6 +290,11 @@ class MobiReader(object): align = attrib.pop('align').strip() if align: styles.append('text-align: %s' % align) + if mobi_version == 1 and tag.tag == 'hr': + tag.tag = 'div' + styles.append('page-break-before: always') + styles.append('display: block') + styles.append('margin: 0') if styles: attrib['style'] = '; '.join(styles) @@ -300,6 +305,17 @@ class MobiReader(object): except ValueError: if sz in size_map.keys(): attrib['size'] = size_map[sz] + if 'filepos-id' in attrib: + attrib['id'] = attrib.pop('filepos-id') + if 'filepos' in attrib: + filepos = int(attrib.pop('filepos')) + attrib['href'] = "#filepos%d" % filepos + if tag.tag == 'img': + recindex = None + for attr in self.IMAGE_ATTRS: + recindex = attrib.pop(attr, None) or recindex + if recindex is not None: + attrib['src'] = 'images/%s.jpg' % recindex def create_opf(self, htmlfile, guide=None): mi = self.book_header.exth.mi @@ -399,38 +415,40 @@ class MobiReader(object): def replace_page_breaks(self): - self.processed_html = self.PAGE_BREAK_PAT.sub('
', - self.processed_html) + self.processed_html = self.PAGE_BREAK_PAT.sub( + '
', + self.processed_html) def add_anchors(self): if self.verbose: print 'Adding anchors...' positions = set([]) - link_pattern = re.compile(r'<[^<>]+filepos=[\'"]{0,1}(\d+)[^<>]*>', re.IGNORECASE) + link_pattern = re.compile(r'''<[^<>]+filepos=['"]{0,1}(\d+)[^<>]*>''', + re.IGNORECASE) for match in link_pattern.finditer(self.mobi_html): positions.add(int(match.group(1))) - positions = list(positions) - positions.sort() pos = 0 self.processed_html = '' - for end in positions: + end_tag_re = re.compile(r'<\s*/') + for end in sorted(positions): if end == 0: continue oend = end l = self.mobi_html.find('<', end) r = self.mobi_html.find('>', end) - if r > -1 and r < l: # Move out of tag - end = r+1 - self.processed_html += self.mobi_html[pos:end] + ''%(oend, oend) + anchor = '' + if r > -1 and (r < l or l == end or l == -1): + p = self.mobi_html.rfind('<', 0, end + 1) + if pos < end and p > -1 and \ + not end_tag_re.match(self.mobi_html[p:r]): + anchor = ' filepos-id="filepos%d"' + end = r + else: + end = r + 1 + self.processed_html += self.mobi_html[pos:end] + (anchor % oend) pos = end - self.processed_html += self.mobi_html[pos:] - fpat = re.compile(r'filepos=[\'"]{0,1}(\d+)[\'"]{0,1}', re.IGNORECASE) - def fpos_to_href(match): - return fpat.sub('href="#filepos%d"'%int(match.group(1)), match.group()) - self.processed_html = link_pattern.sub(fpos_to_href, - self.processed_html) - + def extract_images(self, processed_records, output_dir): if self.verbose: print 'Extracting images...' @@ -454,19 +472,6 @@ class MobiReader(object): path = os.path.join(output_dir, '%05d.jpg'%image_index) self.image_names.append(os.path.basename(path)) im.convert('RGB').save(open(path, 'wb'), format='JPEG') - - def fix_images(match): - tag = match.group() - for pat in self.IMAGE_PATS: - m = pat.search(tag) - if m: - return pat.sub(' src="images/%s.jpg"'%m.group(1), tag) - - - if hasattr(self, 'processed_html'): - self.processed_html = \ - re.compile(r'', re.IGNORECASE|re.DOTALL)\ - .sub(fix_images, self.processed_html) def get_metadata(stream): mr = MobiReader(stream) diff --git a/src/calibre/ebooks/mobi/writer.py b/src/calibre/ebooks/mobi/writer.py index 49f4e076a4..380bdbf518 100644 --- a/src/calibre/ebooks/mobi/writer.py +++ b/src/calibre/ebooks/mobi/writer.py @@ -296,9 +296,11 @@ class Serializer(object): class MobiWriter(object): COLLAPSE_RE = re.compile(r'[ \t\r\n\v]+') - def __init__(self, compression=None, imagemax=None): + def __init__(self, compression=None, imagemax=None, + prefer_author_sort=False): self._compression = compression or UNCOMPRESSED self._imagemax = imagemax or OTHER_MAX_IMAGE_SIZE + self._prefer_author_sort = prefer_author_sort def dump(self, oeb, path): if hasattr(path, 'write'): @@ -457,12 +459,19 @@ class MobiWriter(object): for term in oeb.metadata: if term not in EXTH_CODES: continue code = EXTH_CODES[term] - for item in oeb.metadata[term]: + items = oeb.metadata[term] + if term == 'creator': + if self._prefer_author_sort: + creators = [unicode(c.file_as or c) for c in items] + else: + creators = [unicode(c) for c in items] + items = ['; '.join(creators)] + for item in items: data = self.COLLAPSE_RE.sub(' ', unicode(item)) if term == 'identifier': if data.lower().startswith('urn:isbn:'): data = data[9:] - elif item.get('scheme', '').lower() == 'isbn': + elif item.scheme.lower() == 'isbn': pass else: continue @@ -535,6 +544,9 @@ def config(defaults=None): help=_('Render HTML tables as blocks of text instead of actual ' 'tables. This is neccessary if the HTML contains very large ' 'or complex tables.')) + mobi('prefer_author_sort', ['--prefer-author-sort'], default=False, + help=_('When present, use the author sorting information for ' + 'generating the Mobipocket author metadata.')) profiles = c.add_group('profiles', _('Device renderer profiles. ' 'Affects conversion of font sizes, image rescaling and rasterization ' 'of tables. Valid profiles are: %s.') % ', '.join(_profiles)) @@ -594,7 +606,8 @@ def oeb2mobi(opts, inpath): trimmer.transform(oeb, context) mobimlizer = MobiMLizer(ignore_tables=opts.ignore_tables) mobimlizer.transform(oeb, context) - writer = MobiWriter(compression=compression, imagemax=imagemax) + writer = MobiWriter(compression=compression, imagemax=imagemax, + prefer_author_sort=opts.prefer_author_sort) writer.dump(oeb, outpath) run_plugins_on_postprocess(outpath, 'mobi') logger.info(_('Output written to ') + outpath) diff --git a/src/calibre/ebooks/oeb/base.py b/src/calibre/ebooks/oeb/base.py index 106a091c71..778cec54cf 100644 --- a/src/calibre/ebooks/oeb/base.py +++ b/src/calibre/ebooks/oeb/base.py @@ -6,82 +6,120 @@ from __future__ import with_statement __license__ = 'GPL v3' __copyright__ = '2008, Marshall T. Vandegrift ' -import os -import sys +import os, sys, re, uuid, copy +from mimetypes import types_map, guess_type from collections import defaultdict from types import StringTypes from itertools import izip, count, chain from urlparse import urldefrag, urlparse, urlunparse from urllib import unquote as urlunquote -import logging -import re -import uuid -import copy -from lxml import etree -from lxml import html +from lxml import etree, html +import calibre from calibre import LoggingInterface from calibre.translations.dynamic import translate from calibre.startup import get_lang +from calibre.ebooks.chardet import xml_to_unicode from calibre.ebooks.oeb.entitydefs import ENTITYDEFS from calibre.ebooks.metadata.epub import CoverRenderer from calibre.ptempfile import TemporaryDirectory -XML_NS = 'http://www.w3.org/XML/1998/namespace' -XHTML_NS = 'http://www.w3.org/1999/xhtml' -OEB_DOC_NS = 'http://openebook.org/namespaces/oeb-document/1.0/' -OPF1_NS = 'http://openebook.org/namespaces/oeb-package/1.0/' -OPF2_NS = 'http://www.idpf.org/2007/opf' -OPF_NSES = set([OPF1_NS, OPF2_NS]) -DC09_NS = 'http://purl.org/metadata/dublin_core' -DC10_NS = 'http://purl.org/dc/elements/1.0/' -DC11_NS = 'http://purl.org/dc/elements/1.1/' -DC_NSES = set([DC09_NS, DC10_NS, DC11_NS]) -XSI_NS = 'http://www.w3.org/2001/XMLSchema-instance' -DCTERMS_NS = 'http://purl.org/dc/terms/' -NCX_NS = 'http://www.daisy.org/z3986/2005/ncx/' -SVG_NS = 'http://www.w3.org/2000/svg' -XLINK_NS = 'http://www.w3.org/1999/xlink' -CALIBRE_NS = 'http://calibre.kovidgoyal.net/2009/metadata' -XPNSMAP = {'h': XHTML_NS, 'o1': OPF1_NS, 'o2': OPF2_NS, - 'd09': DC09_NS, 'd10': DC10_NS, 'd11': DC11_NS, - 'xsi': XSI_NS, 'dt': DCTERMS_NS, 'ncx': NCX_NS, - 'svg': SVG_NS, 'xl': XLINK_NS} +XML_NS = 'http://www.w3.org/XML/1998/namespace' +XHTML_NS = 'http://www.w3.org/1999/xhtml' +OEB_DOC_NS = 'http://openebook.org/namespaces/oeb-document/1.0/' +OPF1_NS = 'http://openebook.org/namespaces/oeb-package/1.0/' +OPF2_NS = 'http://www.idpf.org/2007/opf' +OPF_NSES = set([OPF1_NS, OPF2_NS]) +DC09_NS = 'http://purl.org/metadata/dublin_core' +DC10_NS = 'http://purl.org/dc/elements/1.0/' +DC11_NS = 'http://purl.org/dc/elements/1.1/' +DC_NSES = set([DC09_NS, DC10_NS, DC11_NS]) +XSI_NS = 'http://www.w3.org/2001/XMLSchema-instance' +DCTERMS_NS = 'http://purl.org/dc/terms/' +NCX_NS = 'http://www.daisy.org/z3986/2005/ncx/' +SVG_NS = 'http://www.w3.org/2000/svg' +XLINK_NS = 'http://www.w3.org/1999/xlink' +CALIBRE_NS = 'http://calibre.kovidgoyal.net/2009/metadata' +XPNSMAP = { + 'h' : XHTML_NS, 'o1' : OPF1_NS, 'o2' : OPF2_NS, + 'd09': DC09_NS, 'd10': DC10_NS, 'd11': DC11_NS, + 'xsi': XSI_NS, 'dt' : DCTERMS_NS, 'ncx': NCX_NS, + 'svg': SVG_NS, 'xl' : XLINK_NS + } DC_PREFIXES = ('d11', 'd10', 'd09') -def XML(name): return '{%s}%s' % (XML_NS, name) -def XHTML(name): return '{%s}%s' % (XHTML_NS, name) -def OPF(name): return '{%s}%s' % (OPF2_NS, name) -def DC(name): return '{%s}%s' % (DC11_NS, name) -def NCX(name): return '{%s}%s' % (NCX_NS, name) -def SVG(name): return '{%s}%s' % (SVG_NS, name) -def XLINK(name): return '{%s}%s' % (XLINK_NS, name) -def CALIBRE(name): return '{%s}%s' % (CALIBRE_NS, name) -EPUB_MIME = 'application/epub+zip' -XHTML_MIME = 'application/xhtml+xml' -CSS_MIME = 'text/css' -NCX_MIME = 'application/x-dtbncx+xml' -OPF_MIME = 'application/oebps-package+xml' -OEB_DOC_MIME = 'text/x-oeb1-document' -OEB_CSS_MIME = 'text/x-oeb1-css' -OPENTYPE_MIME = 'font/opentype' -GIF_MIME = 'image/gif' -JPEG_MIME = 'image/jpeg' -PNG_MIME = 'image/png' -SVG_MIME = 'image/svg+xml' -BINARY_MIME = 'application/octet-stream' +def XML(name): + return '{%s}%s' % (XML_NS, name) -OEB_STYLES = set([CSS_MIME, OEB_CSS_MIME, 'text/x-oeb-css']) -OEB_DOCS = set([XHTML_MIME, 'text/html', OEB_DOC_MIME, 'text/x-oeb-document']) +def XHTML(name): + return '{%s}%s' % (XHTML_NS, name) + +def OPF(name): + return '{%s}%s' % (OPF2_NS, name) + +def DC(name): + return '{%s}%s' % (DC11_NS, name) + +def XSI(name): + return '{%s}%s' % (XSI_NS, name) + +def DCTERMS(name): + return '{%s}%s' % (DCTERMS_NS, name) + +def NCX(name): + return '{%s}%s' % (NCX_NS, name) + +def SVG(name): + return '{%s}%s' % (SVG_NS, name) + +def XLINK(name): + return '{%s}%s' % (XLINK_NS, name) + +def CALIBRE(name): + return '{%s}%s' % (CALIBRE_NS, name) + +def LINK_SELECTORS(): + results = [] + for expr in ('h:head/h:link/@href', 'h:body//h:a/@href', + 'h:body//h:img/@src', 'h:body//h:object/@data', + 'h:body//*/@xl:href', '//ncx:content/@src', + 'o2:page/@href'): + results.append(etree.XPath(expr, namespaces=XPNSMAP)) + return results + +LINK_SELECTORS = LINK_SELECTORS() + +EPUB_MIME = types_map['.epub'] +XHTML_MIME = types_map['.xhtml'] +CSS_MIME = types_map['.css'] +NCX_MIME = types_map['.ncx'] +OPF_MIME = types_map['.opf'] +PAGE_MAP_MIME = 'application/oebps-page-map+xml' +OEB_DOC_MIME = 'text/x-oeb1-document' +OEB_CSS_MIME = 'text/x-oeb1-css' +OPENTYPE_MIME = 'application/x-font-opentype' +GIF_MIME = types_map['.gif'] +JPEG_MIME = types_map['.jpeg'] +PNG_MIME = types_map['.png'] +SVG_MIME = types_map['.svg'] +BINARY_MIME = 'application/octet-stream' + +OEB_STYLES = set([CSS_MIME, OEB_CSS_MIME, 'text/x-oeb-css']) +OEB_DOCS = set([XHTML_MIME, 'text/html', OEB_DOC_MIME, 'text/x-oeb-document']) OEB_RASTER_IMAGES = set([GIF_MIME, JPEG_MIME, PNG_MIME]) -OEB_IMAGES = set([GIF_MIME, JPEG_MIME, PNG_MIME, SVG_MIME]) +OEB_IMAGES = set([GIF_MIME, JPEG_MIME, PNG_MIME, SVG_MIME]) MS_COVER_TYPE = 'other.ms-coverimage-standard' -ENTITY_RE = re.compile(r'&([a-zA-Z_:][a-zA-Z0-9.-_:]+);') -COLLAPSE_RE = re.compile(r'[ \t\r\n\v]+') -QNAME_RE = re.compile(r'^[{][^{}]+[}][^{}]+$') +ENTITY_RE = re.compile(r'&([a-zA-Z_:][a-zA-Z0-9.-_:]+);') +COLLAPSE_RE = re.compile(r'[ \t\r\n\v]+') +QNAME_RE = re.compile(r'^[{][^{}]+[}][^{}]+$') PREFIXNAME_RE = re.compile(r'^[^:]+[:][^:]+') +XMLDECL_RE = re.compile(r'^\s*<[?]xml.*?[?]>') +CSSURL_RE = re.compile(r'''url[(](?P["']?)(?P[^)]+)(?P=q)[)]''') + +RECOVER_PARSER = etree.XMLParser(recover=True) + def element(parent, *args, **kwargs): if parent is not None: @@ -132,21 +170,28 @@ def xpath(elem, expr): def xml2str(root): return etree.tostring(root, encoding='utf-8', xml_declaration=True) -ASCII_CHARS = set(chr(x) for x in xrange(128)) -URL_SAFE = set(u'ABCDEFGHIJKLMNOPQRSTUVWXYZ' - u'abcdefghijklmnopqrstuvwxyz' - u'0123456789' u'_.-/~') -URL_UNSAFE = ASCII_CHARS - URL_SAFE +ASCII_CHARS = set(chr(x) for x in xrange(128)) +UNIBYTE_CHARS = set(chr(x) for x in xrange(256)) +URL_SAFE = set('ABCDEFGHIJKLMNOPQRSTUVWXYZ' + 'abcdefghijklmnopqrstuvwxyz' + '0123456789' '_.-/~') +URL_UNSAFE = [ASCII_CHARS - URL_SAFE, UNIBYTE_CHARS - URL_SAFE] + def urlquote(href): result = [] + unsafe = 0 if isinstance(href, unicode) else 1 + unsafe = URL_UNSAFE[unsafe] for char in href: - if char in URL_UNSAFE: + if char in unsafe: char = "%%%02x" % ord(char) result.append(char) return ''.join(result) def urlnormalize(href): parts = urlparse(href) + if not parts.scheme: + path, frag = urldefrag(href) + parts = ('', '', path, '', '', frag) parts = (part.replace('\\', '/') for part in parts) parts = (urlunquote(part) for part in parts) parts = (urlquote(part) for part in parts) @@ -175,7 +220,7 @@ class AbstractContainer(object): class DirContainer(AbstractContainer): def __init__(self, rootdir): - self.rootdir = rootdir + self.rootdir = unicode(rootdir) def read(self, path): path = os.path.join(self.rootdir, path) @@ -195,32 +240,71 @@ class DirContainer(AbstractContainer): return os.path.isfile(urlunquote(path)) class DirWriter(object): - def __init__(self, version=2.0): + def __init__(self, version='2.0', page_map=False): self.version = version + self.page_map = page_map def dump(self, oeb, path): + version = int(self.version[0]) if not os.path.isdir(path): os.mkdir(path) output = DirContainer(path) for item in oeb.manifest.values(): output.write(item.href, str(item)) - metadata = oeb.to_opf2() if self.version == 2 else oeb.to_opf1() + if version == 1: + metadata = oeb.to_opf1() + elif version == 2: + metadata = oeb.to_opf2(page_map=self.page_map) + else: + raise OEBError("Unrecognized OPF version %r" % self.version) for href, data in metadata.values(): output.write(href, xml2str(data)) return class Metadata(object): - DC_TERMS = set(['contributor', 'coverage', 'creator', 'date', 'description', - 'format', 'identifier', 'language', 'publisher', 'relation', - 'rights', 'source', 'subject', 'title', 'type']) + DC_TERMS = set([ + 'contributor', 'coverage', 'creator', 'date', + 'description', 'format', 'identifier', 'language', + 'publisher', 'relation', 'rights', 'source', 'subject', + 'title', 'type' + ]) CALIBRE_TERMS = set(['series', 'series_index', 'rating']) - OPF_ATTRS = set(['role', 'file-as', 'scheme', 'event']) - OPF1_NSMAP = {'dc': DC11_NS, 'oebpackage': OPF1_NS} - OPF2_NSMAP = {'opf': OPF2_NS, 'dc': DC11_NS, 'dcterms': DCTERMS_NS, - 'xsi': XSI_NS, 'calibre': CALIBRE_NS} + OPF_ATTRS = {'role': OPF('role'), 'file-as': OPF('file-as'), + 'scheme': OPF('scheme'), 'event': OPF('event'), + 'type': XSI('type'), 'lang': XML('lang'), 'id': 'id'} + OPF1_NSMAP = {'dc': DC11_NS, 'oebpackage': OPF1_NS} + OPF2_NSMAP = {'opf': OPF2_NS, 'dc': DC11_NS, 'dcterms': DCTERMS_NS, + 'xsi': XSI_NS, 'calibre': CALIBRE_NS} class Item(object): + + class Attribute(object): + + def __init__(self, attr, allowed=None): + if not callable(attr): + attr_, attr = attr, lambda term: attr_ + self.attr = attr + self.allowed = allowed + + def term_attr(self, obj): + term = obj.term + if namespace(term) != DC11_NS: + term = OPF('meta') + allowed = self.allowed + if allowed is not None and term not in allowed: + raise AttributeError( + 'attribute %r not valid for metadata term %r' \ + % (self.attr(term), barename(obj.term))) + return self.attr(term) + + def __get__(self, obj, cls): + if obj is None: return None + return obj.attrib.get(self.term_attr(obj), '') + + def __set__(self, obj, value): + obj.attrib[self.term_attr(obj)] = value + def __init__(self, term, value, attrib={}, nsmap={}, **kwargs): self.attrib = attrib = dict(attrib) self.nsmap = nsmap = dict(nsmap) @@ -240,30 +324,26 @@ class Metadata(object): for attr, value in attrib.items(): if isprefixname(value): attrib[attr] = qname(value, nsmap) - if attr in Metadata.OPF_ATTRS: - attrib[OPF(attr)] = attrib.pop(attr) - self.__setattr__ = self._setattr + nsattr = Metadata.OPF_ATTRS.get(attr, attr) + if nsattr == OPF('scheme') and namespace(term) != DC11_NS: + # The opf:meta element takes @scheme, not @opf:scheme + nsattr = 'scheme' + if attr != nsattr: + attrib[nsattr] = attrib.pop(attr) + + scheme = Attribute(lambda term : 'scheme' if term == OPF('meta') else OPF('scheme'), + [DC('identifier'), OPF('meta')]) + file_as = Attribute(OPF('file-as'), [DC('creator'), DC('contributor')]) + role = Attribute(OPF('role'), [DC('creator'), DC('contributor')]) + event = Attribute(OPF('event'), [DC('date')]) + id = Attribute('id') + type = Attribute(XSI('type'), [DC('date'), DC('format'), DC('type')]) + lang = Attribute(XML('lang'), [DC('contributor'), DC('coverage'), + DC('creator'), DC('publisher'), + DC('relation'), DC('rights'), + DC('source'), DC('subject'), + OPF('meta')]) - def __getattr__(self, name): - attr = name.replace('_', '-') - if attr in Metadata.OPF_ATTRS: - attr = OPF(attr) - try: - return self.attrib[attr] - except KeyError: - raise AttributeError( - '%r object has no attribute %r' \ - % (self.__class__.__name__, name)) - - def _setattr(self, name, value): - attr = name.replace('_', '-') - if attr in Metadata.OPF_ATTRS: - attr = OPF(attr) - if attr in self.attrib: - self.attrib[attr] = value - return - super(Item, self).__setattr__(self, name, value) - def __getitem__(self, key): return self.attrib[key] @@ -339,6 +419,7 @@ class Metadata(object): def __getattr__(self, term): return self.items[term] + @apply def _nsmap(): def fget(self): nsmap = {} @@ -347,8 +428,8 @@ class Metadata(object): nsmap.update(item.nsmap) return nsmap return property(fget=fget) - _nsmap = _nsmap() + @apply def _opf1_nsmap(): def fget(self): nsmap = self._nsmap @@ -357,15 +438,16 @@ class Metadata(object): del nsmap[key] return nsmap return property(fget=fget) - _opf1_nsmap = _opf1_nsmap() + + @apply def _opf2_nsmap(): def fget(self): nsmap = self._nsmap nsmap.update(self.OPF2_NSMAP) return nsmap return property(fget=fget) - _opf2_nsmap = _opf2_nsmap() + def to_opf1(self, parent=None): nsmap = self._opf1_nsmap @@ -392,7 +474,9 @@ class Metadata(object): class Manifest(object): + class Item(object): + NUM_RE = re.compile('^(.*)([0-9][0-9.]*)(?=[.]|$)') META_XP = XPath('/h:html/h:head/h:meta[@http-equiv="Content-Type"]') @@ -415,9 +499,9 @@ class Manifest(object): % (self.id, self.href, self.media_type) def _force_xhtml(self, data): - # Possibly decode in user-specified encoding - if self.oeb.encoding is not None: - data = data.decode(self.oeb.encoding, 'replace') + # Convert to Unicode and normalize line endings + data = self.oeb.decode(data) + data = XMLDECL_RE.sub('', data) # Handle broken XHTML w/ SVG (ugh) if 'svg:' in data and SVG_NS not in data: data = data.replace( @@ -442,7 +526,10 @@ class Manifest(object): if elem.text: elem.text = elem.text.strip('-') data = etree.tostring(data, encoding=unicode) - data = etree.fromstring(data) + try: + data = etree.fromstring(data) + except etree.XMLSyntaxError: + data = etree.fromstring(data, parser=RECOVER_PARSER) # Force into the XHTML namespace if barename(data.tag) != 'html': raise OEBError( @@ -489,6 +576,7 @@ class Manifest(object): etree.SubElement(data, XHTML('body')) return data + @apply def data(): def fget(self): if self._data is not None: @@ -498,6 +586,8 @@ class Manifest(object): data = self._force_xhtml(data) elif self.media_type[-4:] in ('+xml', '/xml'): data = etree.fromstring(data) + elif self.media_type in OEB_STYLES: + data = self.oeb.decode(data) self._data = data return data def fset(self, value): @@ -505,12 +595,13 @@ class Manifest(object): def fdel(self): self._data = None return property(fget, fset, fdel) - data = data() - + def __str__(self): data = self.data if isinstance(data, etree._Element): return xml2str(data) + if isinstance(data, unicode): + return data.encode('utf-8') return str(data) def __eq__(self, other): @@ -534,7 +625,9 @@ class Manifest(object): return cmp(skey, okey) def relhref(self, href): - if '/' not in self.href or ':' in href: + if urlparse(href).scheme: + return href + if '/' not in self.href: return href base = os.path.dirname(self.href).split('/') target, frag = urldefrag(href) @@ -550,7 +643,12 @@ class Manifest(object): return relhref def abshref(self, href): - if '/' not in self.href or ':' in href: + if urlparse(href).scheme: + return href + path, frag = urldefrag(href) + if not path: + return '#'.join((self.href, frag)) + if '/' not in self.href: return href dirname = os.path.dirname(self.href) href = os.path.join(dirname, href) @@ -577,18 +675,20 @@ class Manifest(object): if item in self.oeb.spine: self.oeb.spine.remove(item) - def generate(self, id, href): - href = urlnormalize(href) - base = id - index = 1 - while id in self.ids: - id = base + str(index) - index += 1 - base, ext = os.path.splitext(href) - index = 1 - while href in self.hrefs: - href = base + str(index) + ext - index += 1 + def generate(self, id=None, href=None): + if id is not None: + base = id + index = 1 + while id in self.ids: + id = base + str(index) + index += 1 + if href is not None: + href = urlnormalize(href) + base, ext = os.path.splitext(href) + index = 1 + while href in self.hrefs: + href = base + str(index) + ext + index += 1 return id, href def __iter__(self): @@ -641,6 +741,7 @@ class Manifest(object): class Spine(object): + def __init__(self, oeb): self.oeb = oeb self.items = [] @@ -706,7 +807,9 @@ class Spine(object): class Guide(object): + class Reference(object): + _TYPES_TITLES = [('cover', __('Cover')), ('title-page', __('Title Page')), ('toc', __('Table of Contents')), @@ -745,24 +848,24 @@ class Guide(object): return 'Reference(type=%r, title=%r, href=%r)' \ % (self.type, self.title, self.href) + @apply def _order(): def fget(self): return self.ORDER.get(self.type, self.type) return property(fget=fget) - _order = _order() def __cmp__(self, other): if not isinstance(other, Guide.Reference): return NotImplemented return cmp(self._order, other._order) + @apply def item(): def fget(self): - path, frag = urldefrag(self.href) + path = urldefrag(self.href)[0] hrefs = self.oeb.manifest.hrefs return hrefs.get(path, None) return property(fget=fget) - item = item() def __init__(self, oeb): self.oeb = oeb @@ -817,6 +920,7 @@ class Guide(object): class TOC(object): + # This needs beefing up to support the interface of toc.TOC def __init__(self, title=None, href=None, klass=None, id=None): self.title = title self.href = urlnormalize(href) if href else href @@ -863,27 +967,75 @@ class TOC(object): node.to_opf1(tour) return tour - def to_ncx(self, parent, order=None, depth=1): - if not order: order = [0] + def to_ncx(self, parent, depth=1): for node in self.nodes: - order[0] += 1 - playOrder = str(order[0]) - id = self.id or 'np' + playOrder - point = etree.SubElement(parent, - NCX('navPoint'), id=id, playOrder=playOrder) - if self.klass: - point.attrib['class'] = node.klass + id = node.id or unicode(uuid.uuid4()) + attrib = {'id': id, 'playOrder': '0'} + if node.klass: + attrib['class'] = node.klass + point = element(parent, NCX('navPoint'), attrib=attrib) label = etree.SubElement(point, NCX('navLabel')) - etree.SubElement(label, NCX('text')).text = node.title + element(label, NCX('text')).text = node.title href = node.href if depth > 1 else urldefrag(node.href)[0] - child = etree.SubElement(point, - NCX('content'), attrib={'src': href}) - node.to_ncx(point, order, depth+1) + element(point, NCX('content'), src=href) + node.to_ncx(point, depth+1) return parent + +class PageList(object): + class Page(object): + def __init__(self, name, href, type='normal', klass=None, id=None): + self.name = name + self.href = urlnormalize(href) + self.type = type + self.id = id + self.klass = klass + + def __init__(self): + self.pages = [] + + def add(self, name, href, type='normal', klass=None, id=None): + page = self.Page(name, href, type, klass, id) + self.pages.append(page) + return page + + def __len__(self): + return len(self.pages) + + def __iter__(self): + for page in self.pages: + yield page + + def __getitem__(self, index): + return self.pages[index] + + def to_ncx(self, parent=None): + plist = element(parent, NCX('pageList'), id=str(uuid.uuid4())) + values = dict((t, count(1)) for t in ('front', 'normal', 'special')) + for page in self.pages: + id = page.id or unicode(uuid.uuid4()) + type = page.type + value = str(values[type].next()) + attrib = {'id': id, 'value': value, 'type': type, 'playOrder': '0'} + if page.klass: + attrib['class'] = page.klass + ptarget = element(plist, NCX('pageTarget'), attrib=attrib) + label = element(ptarget, NCX('navLabel')) + element(label, NCX('text')).text = page.name + element(ptarget, NCX('content'), src=page.href) + return plist + + def to_page_map(self): + pmap = etree.Element(OPF('page-map'), nsmap={None: OPF2_NS}) + for page in self.pages: + element(pmap, OPF('page'), name=page.name, href=page.href) + return pmap + + class OEBBook(object): - COVER_SVG_XP = XPath('h:body//svg:svg[position() = 1]') + + COVER_SVG_XP = XPath('h:body//svg:svg[position() = 1]') COVER_OBJECT_XP = XPath('h:body//h:object[@data][position() = 1]') def __init__(self, opfpath=None, container=None, encoding=None, @@ -912,13 +1064,11 @@ class OEBBook(object): metadata = etree.SubElement(nroot, OPF('metadata'), nsmap=nsmap) ignored = (OPF('dc-metadata'), OPF('x-metadata')) for elem in xpath(opf, 'o2:metadata//*'): + if elem.tag in ignored: + continue if namespace(elem.tag) in DC_NSES: tag = barename(elem.tag).lower() elem.tag = '{%s}%s' % (DC11_NS, tag) - for name in elem.attrib: - if name in ('role', 'file-as', 'scheme', 'event'): - nsname = '{%s}%s' % (OPF2_NS, name) - elem.attrib[nsname] = elem.attrib.pop(name) metadata.append(elem) for element in xpath(opf, 'o2:metadata//o2:meta'): metadata.append(element) @@ -928,13 +1078,15 @@ class OEBBook(object): return nroot def _read_opf(self, opfpath): - opf = self.container.read(opfpath) + data = self.container.read(opfpath) + data = self.decode(data) + data = XMLDECL_RE.sub('', data) try: - opf = etree.fromstring(opf) + opf = etree.fromstring(data) except etree.XMLSyntaxError: repl = lambda m: ENTITYDEFS.get(m.group(1), m.group(0)) - opf = ENTITY_RE.sub(repl, opf) - opf = etree.fromstring(opf) + data = ENTITY_RE.sub(repl, data) + opf = etree.fromstring(data) self.logger.warn('OPF contains invalid HTML named entities') ns = namespace(opf.tag) if ns not in ('', OPF1_NS, OPF2_NS): @@ -943,7 +1095,7 @@ class OEBBook(object): return opf def _metadata_from_opf(self, opf): - uid = opf.get('unique-identifier', 'calibre-uuid') + uid = opf.get('unique-identifier', None) self.uid = None self.metadata = metadata = Metadata(self) for elem in xpath(opf, '/o2:package/o2:metadata//*'): @@ -964,11 +1116,15 @@ class OEBBook(object): haveuuid = True if 'id' in ident.attrib: haveid = True - if not haveuuid and haveid: + if not (haveuuid and haveid): bookid = "urn:uuid:%s" % str(uuid.uuid4()) metadata.add('identifier', bookid, id='calibre-uuid') + if uid is None: + self.logger.warn(u'Unique-identifier not specified') for item in metadata.identifier: - if item.id == uid: + if not item.id: + continue + if uid is None or item.id == uid: self.uid = item break else: @@ -986,6 +1142,45 @@ class OEBBook(object): if not metadata.title: self.logger.warn('Title not specified') metadata.add('title', self.translate(__('Unknown'))) + + def _manifest_add_missing(self): + manifest = self.manifest + known = set(manifest.hrefs) + unchecked = set(manifest.values()) + while unchecked: + new = set() + for item in unchecked: + if (item.media_type in OEB_DOCS or + item.media_type[-4:] in ('/xml', '+xml')) and \ + item.data is not None: + hrefs = [sel(item.data) for sel in LINK_SELECTORS] + for href in chain(*hrefs): + href, _ = urldefrag(href) + if not href: + continue + href = item.abshref(urlnormalize(href)) + scheme = urlparse(href).scheme + if not scheme and href not in known: + new.add(href) + elif item.media_type in OEB_STYLES: + for match in CSSURL_RE.finditer(item.data): + href, _ = urldefrag(match.group('url')) + href = item.abshref(urlnormalize(href)) + scheme = urlparse(href).scheme + if not scheme and href not in known: + new.add(href) + unchecked.clear() + for href in new: + known.add(href) + if not self.container.exists(href): + self.logger.warn('Referenced file %r not found' % href) + continue + self.logger.warn('Referenced file %r not in manifest' % href) + id, _ = manifest.generate(id='added') + guessed = guess_type(href)[0] + media_type = guessed or BINARY_MIME + added = manifest.add(id, href, media_type) + unchecked.add(added) def _manifest_from_opf(self, opf): self.manifest = manifest = Manifest(self) @@ -994,7 +1189,10 @@ class OEBBook(object): href = elem.get('href') media_type = elem.get('media-type', None) if media_type is None: - media_type = elem.get('mediatype', BINARY_MIME) + media_type = elem.get('mediatype', None) + if media_type is None or media_type == 'text/xml': + guessed = guess_type(href)[0] + media_type = guessed or media_type or BINARY_MIME fallback = elem.get('fallback') if href in manifest.hrefs: self.logger.warn(u'Duplicate manifest entry for %r' % href) @@ -1006,6 +1204,40 @@ class OEBBook(object): self.logger.warn(u'Duplicate manifest id %r' % id) id, href = manifest.generate(id, href) manifest.add(id, href, media_type, fallback) + self._manifest_add_missing() + + def _spine_add_extra(self): + manifest = self.manifest + spine = self.spine + unchecked = set(spine) + selector = XPath('h:body//h:a/@href') + extras = set() + while unchecked: + new = set() + for item in unchecked: + if item.media_type not in OEB_DOCS: + # TODO: handle fallback chains + continue + for href in selector(item.data): + href, _ = urldefrag(href) + if not href: + continue + href = item.abshref(urlnormalize(href)) + if href not in manifest.hrefs: + continue + found = manifest.hrefs[href] + if found.media_type not in OEB_DOCS or \ + found in spine or found in extras: + continue + new.add(found) + extras.update(new) + unchecked = new + version = int(self.version[0]) + for item in sorted(extras): + if version >= 2: + self.logger.warn( + 'Spine-referenced file %r not in spine' % item.href) + spine.add(item, linear=False) def _spine_from_opf(self, opf): self.spine = spine = Spine(self) @@ -1016,69 +1248,86 @@ class OEBBook(object): continue item = self.manifest[idref] spine.add(item, elem.get('linear')) - extras = [] - for item in self.manifest.values(): - if item.media_type in OEB_DOCS \ - and item not in spine: - extras.append(item) - extras.sort() - for item in extras: - spine.add(item, False) if len(spine) == 0: raise OEBError("Spine is empty") - + self._spine_add_extra() + def _guide_from_opf(self, opf): self.guide = guide = Guide(self) for elem in xpath(opf, '/o2:package/o2:guide/o2:reference'): href = elem.get('href') - path, frag = urldefrag(href) + path = urldefrag(href)[0] if path not in self.manifest.hrefs: self.logger.warn(u'Guide reference %r not found' % href) continue guide.add(elem.get('type'), elem.get('title'), href) - - def _toc_from_navpoint(self, toc, navpoint): + + def _find_ncx(self, opf): + result = xpath(opf, '/o2:package/o2:spine/@toc') + if result: + id = result[0] + if id not in self.manifest.ids: + return None + item = self.manifest.ids[id] + self.manifest.remove(item) + return item + for item in self.manifest.values(): + if item.media_type == NCX_MIME: + self.manifest.remove(item) + return item + return None + + def _toc_from_navpoint(self, item, toc, navpoint): children = xpath(navpoint, 'ncx:navPoint') for child in children: title = ''.join(xpath(child, 'ncx:navLabel/ncx:text/text()')) - href = xpath(child, 'ncx:content/@src')[0] + title = COLLAPSE_RE.sub(' ', title.strip()) + href = xpath(child, 'ncx:content/@src') + if not title or not href: + continue + href = item.abshref(urlnormalize(href[0])) + path, _ = urldefrag(href) + if path not in self.manifest.hrefs: + self.logger.warn('TOC reference %r not found' % href) + continue id = child.get('id') klass = child.get('class') node = toc.add(title, href, id=id, klass=klass) - self._toc_from_navpoint(node, child) - - def _toc_from_ncx(self, opf): - result = xpath(opf, '/o2:package/o2:spine/@toc') - if not result: - expr = '/o2:package/o2:manifest/o2:item[@media-type="%s"]/@id' - result = xpath(opf, expr % NCX_MIME) - if len(result) != 1: - return False - id = result[0] - if id not in self.manifest.ids: + self._toc_from_navpoint(item, node, child) + + def _toc_from_ncx(self, item): + if item is None: return False - item = self.manifest.ids[id] ncx = item.data - self.manifest.remove(item) - title = xpath(ncx, 'ncx:docTitle/ncx:text/text()') - title = title[0].strip() if title else unicode(self.metadata.title[0]) + title = ''.join(xpath(ncx, 'ncx:docTitle/ncx:text/text()')) + title = COLLAPSE_RE.sub(' ', title.strip()) + title = title or unicode(self.metadata.title[0]) self.toc = toc = TOC(title) navmaps = xpath(ncx, 'ncx:navMap') for navmap in navmaps: - self._toc_from_navpoint(toc, navmap) + self._toc_from_navpoint(item, toc, navmap) return True - + def _toc_from_tour(self, opf): - result = xpath(opf, '/o2:package/o2:tours/o2:tour') + result = xpath(opf, 'o2:tours/o2:tour') if not result: return False tour = result[0] self.toc = toc = TOC(tour.get('title')) sites = xpath(tour, 'o2:site') for site in sites: - toc.add(site.get('title'), site.get('href')) + title = site.get('title') + href = site.get('href') + if not title or not href: + continue + path, _ = urldefrag(urlnormalize(href)) + if path not in self.manifest.hrefs: + self.logger.warn('TOC reference %r not found' % href) + continue + id = site.get('id') + toc.add(title, href, id=id) return True - + def _toc_from_html(self, opf): if 'toc' not in self.guide: return False @@ -1098,11 +1347,12 @@ class OEBBook(object): order = [] for anchor in xpath(html, './/h:a[@href]'): href = anchor.attrib['href'] + href = item.abshref(urlnormalize(href)) path, frag = urldefrag(href) - if not path: - href = '#'.join((itempath, frag)) + if path not in self.manifest.hrefs: + continue title = ' '.join(xpath(anchor, './/text()')) - href = urlnormalize(href) + title = COLLAPSE_RE.sub(' ', title.strip()) if href not in titles: order.append(href) titles[href].append(title) @@ -1117,15 +1367,17 @@ class OEBBook(object): for item in self.spine: if not item.linear: continue html = item.data - title = xpath(html, '/h:html/h:head/h:title/text()') - title = title[0].strip() if title else None - if title: titles.append(title) + title = ''.join(xpath(html, '/h:html/h:head/h:title/text()')) + title = COLLAPSE_RE.sub(' ', 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()' % (tag,) - header = xpath(html, expr) + expr = '/h:html/h:body//h:%s[position()=1]/text()' + header = ''.join(xpath(html, expr % tag)) + header = COLLAPSE_RE.sub(' ', header.strip()) if header: - headers[-1] = header[0] + headers[-1] = header break use = titles if len(titles) > len(set(titles)): @@ -1135,17 +1387,81 @@ class OEBBook(object): toc.add(title, item.href) return True - def _toc_from_opf(self, opf): - if self._toc_from_ncx(opf): return + def _toc_from_opf(self, opf, item): + if self._toc_from_ncx(item): return if self._toc_from_tour(opf): return + self.logger.warn('No metadata table of contents found') if self._toc_from_html(opf): return self._toc_from_spine(opf) - + + def _pages_from_ncx(self, opf, item): + if item is None: + return False + ncx = item.data + ptargets = xpath(ncx, 'ncx:pageList/ncx:pageTarget') + if not ptargets: + return False + pages = self.pages = PageList() + for ptarget in ptargets: + name = ''.join(xpath(ptarget, 'ncx:navLabel/ncx:text/text()')) + name = COLLAPSE_RE.sub(' ', name.strip()) + href = xpath(ptarget, 'ncx:content/@src') + if not href: + continue + href = item.abshref(urlnormalize(href[0])) + id = ptarget.get('id') + type = ptarget.get('type', 'normal') + klass = ptarget.get('class') + pages.add(name, href, type=type, id=id, klass=klass) + return True + + def _find_page_map(self, opf): + result = xpath(opf, '/o2:package/o2:spine/@page-map') + if result: + id = result[0] + if id not in self.manifest.ids: + return None + item = self.manifest.ids[id] + self.manifest.remove(item) + return item + for item in self.manifest.values(): + if item.media_type == PAGE_MAP_MIME: + self.manifest.remove(item) + return item + return None + + def _pages_from_page_map(self, opf): + item = self._find_page_map(opf) + if item is None: + return False + pmap = item.data + pages = self.pages = PageList() + for page in xpath(pmap, 'o2:page'): + name = page.get('name', '') + href = page.get('href') + if not href: + continue + name = COLLAPSE_RE.sub(' ', name.strip()) + href = item.abshref(urlnormalize(href)) + type = 'normal' + if not name: + type = 'special' + elif name.lower().strip('ivxlcdm') == '': + type = 'front' + pages.add(name, href, type=type) + return True + + def _pages_from_opf(self, opf, item): + if self._pages_from_ncx(opf, item): return + if self._pages_from_page_map(opf): return + self.pages = PageList() + return + def _cover_from_html(self, hcover): with TemporaryDirectory('_html_cover') as tdir: writer = DirWriter() writer.dump(self, tdir) - path = os.path.join(tdir, hcover.href) + path = os.path.join(tdir, urlunquote(hcover.href)) renderer = CoverRenderer(path) data = renderer.image_data id, href = self.manifest.generate('cover', 'cover.jpeg') @@ -1156,8 +1472,10 @@ class OEBBook(object): if self.metadata.cover: id = str(self.metadata.cover[0]) item = self.manifest.ids.get(id, None) - if item is not None: + if item is not None and item.media_type in OEB_IMAGES: return item + else: + self.logger.warn('Invalid cover image @id %r' % id) hcover = self.spine[0] if 'cover' in self.guide: href = self.guide['cover'].href @@ -1195,31 +1513,85 @@ class OEBBook(object): self.metadata.add('cover', cover.id) def _all_from_opf(self, opf): + self.version = opf.get('version', '1.2') self._metadata_from_opf(opf) self._manifest_from_opf(opf) self._spine_from_opf(opf) self._guide_from_opf(opf) - self._toc_from_opf(opf) + item = self._find_ncx(opf) + self._toc_from_opf(opf, item) + self._pages_from_opf(opf, item) self._ensure_cover_image() - + def translate(self, text): lang = str(self.metadata.language[0]) lang = lang.split('-', 1)[0].lower() return translate(lang, text) + def decode(self, data): + if isinstance(data, unicode): + return data + if data[:2] in ('\xff\xfe', '\xfe\xff'): + try: + return data.decode('utf-16') + except UnicodeDecodeError: + pass + try: + return data.decode('utf-8') + except UnicodeDecodeError: + pass + if self.encoding is not None: + try: + return data.decode(self.encoding) + except UnicodeDecodeError: + pass + data, _ = xml_to_unicode(data) + data = data.replace('\r\n', '\n') + data = data.replace('\r', '\n') + return data + def to_opf1(self): package = etree.Element('package', attrib={'unique-identifier': self.uid.id}) - metadata = self.metadata.to_opf1(package) - manifest = self.manifest.to_opf1(package) - spine = self.spine.to_opf1(package) + self.metadata.to_opf1(package) + self.manifest.to_opf1(package) + self.spine.to_opf1(package) tours = element(package, 'tours') tour = element(tours, 'tour', attrib={'id': 'chaptertour', 'title': 'Chapter Tour'}) self.toc.to_opf1(tour) - guide = self.guide.to_opf1(package) + self.guide.to_opf1(package) return {OPF_MIME: ('content.opf', package)} + def _update_playorder(self, ncx): + hrefs = set(xpath(ncx, '//ncx:content/@src')) + playorder = {} + next = 1 + selector = XPath('h:body//*[@id or @name]') + for item in self.spine: + base = item.href + if base in hrefs: + playorder[base] = next + next += 1 + for elem in selector(item.data): + added = False + for attr in ('id', 'name'): + id = elem.get(attr) + if not id: + continue + href = '#'.join([base, id]) + if href in hrefs: + playorder[href] = next + added = True + if added: + next += 1 + selector = XPath('ncx:content/@src') + for elem in xpath(ncx, '//*[@playOrder and ./ncx:content[@src]]'): + href = selector(elem)[0] + order = playorder.get(href, 0) + elem.attrib['playOrder'] = str(order) + return + def _to_ncx(self): lang = unicode(self.metadata.language[0]) ncx = etree.Element(NCX('ncx'), @@ -1227,35 +1599,50 @@ class OEBBook(object): nsmap={None: NCX_NS}) head = etree.SubElement(ncx, NCX('head')) etree.SubElement(head, NCX('meta'), - attrib={'name': 'dtb:uid', 'content': unicode(self.uid)}) + name='dtb:uid', content=unicode(self.uid)) etree.SubElement(head, NCX('meta'), - attrib={'name': 'dtb:depth', 'content': str(self.toc.depth())}) + name='dtb:depth', content=str(self.toc.depth())) + generator = ''.join(['calibre (', calibre.__version__, ')']) etree.SubElement(head, NCX('meta'), - attrib={'name': 'dtb:totalPageCount', 'content': '0'}) + name='dtb:generator', content=generator) etree.SubElement(head, NCX('meta'), - attrib={'name': 'dtb:maxPageNumber', 'content': '0'}) + name='dtb:totalPageCount', content=str(len(self.pages))) + maxpnum = etree.SubElement(head, NCX('meta'), + name='dtb:maxPageNumber', content='0') title = etree.SubElement(ncx, NCX('docTitle')) text = etree.SubElement(title, NCX('text')) text.text = unicode(self.metadata.title[0]) navmap = etree.SubElement(ncx, NCX('navMap')) self.toc.to_ncx(navmap) + if len(self.pages) > 0: + plist = self.pages.to_ncx(ncx) + value = max(int(x) for x in xpath(plist, '//@value')) + maxpnum.attrib['content'] = str(value) + self._update_playorder(ncx) return ncx - def to_opf2(self): + def to_opf2(self, page_map=False): + results = {} package = etree.Element(OPF('package'), attrib={'version': '2.0', 'unique-identifier': self.uid.id}, nsmap={None: OPF2_NS}) - metadata = self.metadata.to_opf2(package) + self.metadata.to_opf2(package) manifest = self.manifest.to_opf2(package) - id, href = self.manifest.generate('ncx', 'toc.ncx') - etree.SubElement(manifest, OPF('item'), - attrib={'id': id, 'href': href, 'media-type': NCX_MIME}) spine = self.spine.to_opf2(package) + self.guide.to_opf2(package) + results[OPF_MIME] = ('content.opf', package) + id, href = self.manifest.generate('ncx', 'toc.ncx') + etree.SubElement(manifest, OPF('item'), id=id, href=href, + attrib={'media-type': NCX_MIME}) spine.attrib['toc'] = id - guide = self.guide.to_opf2(package) - ncx = self._to_ncx() - return {OPF_MIME: ('content.opf', package), - NCX_MIME: (href, ncx)} + results[NCX_MIME] = (href, self._to_ncx()) + if page_map and len(self.pages) > 0: + id, href = self.manifest.generate('page-map', 'page-map.xml') + etree.SubElement(manifest, OPF('item'), id=id, href=href, + attrib={'media-type': PAGE_MAP_MIME}) + spine.attrib['page-map'] = id + results[PAGE_MAP_MIME] = (href, self.pages.to_page_map()) + return results def main(argv=sys.argv): @@ -1263,7 +1650,7 @@ def main(argv=sys.argv): oeb = OEBBook(arg) for name, doc in oeb.to_opf1().values(): print etree.tostring(doc, pretty_print=True) - for name, doc in oeb.to_opf2().values(): + for name, doc in oeb.to_opf2(page_map=True).values(): print etree.tostring(doc, pretty_print=True) return 0 diff --git a/src/calibre/ebooks/oeb/stylizer.py b/src/calibre/ebooks/oeb/stylizer.py index 03a1fade10..e4a0cfd7fe 100644 --- a/src/calibre/ebooks/oeb/stylizer.py +++ b/src/calibre/ebooks/oeb/stylizer.py @@ -109,6 +109,7 @@ class Stylizer(object): STYLESHEETS = {} def __init__(self, tree, path, oeb, profile=PROFILES['PRS505']): + self.oeb = oeb self.profile = profile self.logger = oeb.logger item = oeb.manifest.hrefs[path] @@ -117,7 +118,7 @@ class Stylizer(object): stylesheets = [HTML_CSS_STYLESHEET] head = xpath(tree, '/h:html/h:head')[0] parser = cssutils.CSSParser() - parser.setFetcher(lambda path: ('utf-8', oeb.container.read(path))) + parser.setFetcher(self._fetch_css_file) for elem in head: if elem.tag == XHTML('style') and elem.text \ and elem.get('type', CSS_MIME) in OEB_STYLES: @@ -138,8 +139,7 @@ class Stylizer(object): if path in self.STYLESHEETS: stylesheet = self.STYLESHEETS[path] else: - data = XHTML_CSS_NAMESPACE - data += oeb.manifest.hrefs[path].data + data = self._fetch_css_file(path)[1] stylesheet = parser.parseString(data, href=path) stylesheet.namespaces['h'] = XHTML_NS self.STYLESHEETS[path] = stylesheet @@ -167,6 +167,14 @@ class Stylizer(object): for elem in xpath(tree, '//h:*[@style]'): self.style(elem)._apply_style_attr() + def _fetch_css_file(self, path): + hrefs = self.oeb.manifest.hrefs + if path not in hrefs: + return (None, None) + data = hrefs[path].data + data = XHTML_CSS_NAMESPACE + data + return ('utf-8', data) + def flatten_rule(self, rule, href, index): results = [] if isinstance(rule, CSSStyleRule): diff --git a/src/calibre/ebooks/oeb/transforms/trimmanifest.py b/src/calibre/ebooks/oeb/transforms/trimmanifest.py index 643952c03d..a1d28e5a99 100644 --- a/src/calibre/ebooks/oeb/transforms/trimmanifest.py +++ b/src/calibre/ebooks/oeb/transforms/trimmanifest.py @@ -13,13 +13,9 @@ from urlparse import urldefrag from lxml import etree import cssutils from calibre.ebooks.oeb.base import XPNSMAP, CSS_MIME, OEB_DOCS +from calibre.ebooks.oeb.base import LINK_SELECTORS, CSSURL_RE from calibre.ebooks.oeb.base import urlnormalize -LINK_SELECTORS = [] -for expr in ('//h:link/@href', '//h:img/@src', '//h:object/@data', - '//*/@xl:href'): - LINK_SELECTORS.append(etree.XPath(expr, namespaces=XPNSMAP)) - class ManifestTrimmer(object): def transform(self, oeb, context): oeb.logger.info('Trimming unused files from manifest...') @@ -53,15 +49,13 @@ class ManifestTrimmer(object): if found not in used: new.add(found) elif item.media_type == CSS_MIME: - def replacer(uri): - absuri = item.abshref(urlnormalize(uri)) - if absuri in oeb.manifest.hrefs: + for match in CSSURL_RE.finditer(item.data): + href = match.group('url') + href = item.abshref(urlnormalize(href)) + if href in oeb.manifest.hrefs: found = oeb.manifest.hrefs[href] if found not in used: new.add(found) - return uri - sheet = cssutils.parseString(item.data, href=item.href) - cssutils.replaceUrls(sheet, replacer) used.update(new) unchecked = new for item in oeb.manifest.values(): diff --git a/src/calibre/gui2/dialogs/epub.py b/src/calibre/gui2/dialogs/epub.py index 614f18e5b4..387a495f87 100644 --- a/src/calibre/gui2/dialogs/epub.py +++ b/src/calibre/gui2/dialogs/epub.py @@ -18,7 +18,7 @@ from calibre.gui2 import error_dialog, choose_images, pixmap_to_data, ResizableD from calibre.ebooks.epub.from_any import SOURCE_FORMATS, config as epubconfig from calibre.ebooks.metadata import MetaInformation from calibre.ptempfile import PersistentTemporaryFile -from calibre.ebooks.metadata.opf import OPFCreator +from calibre.ebooks.metadata.opf2 import OPFCreator from calibre.ebooks.metadata import authors_to_string, string_to_authors @@ -62,6 +62,7 @@ class Config(ResizableDialog, Ui_Dialog): self.toc_title_label.setVisible(False) self.opt_rescale_images.setVisible(False) self.opt_ignore_tables.setVisible(False) + self.opt_prefer_author_sort.setVisible(False) def initialize(self): self.__w = [] diff --git a/src/calibre/gui2/dialogs/epub.ui b/src/calibre/gui2/dialogs/epub.ui index a346bed4e8..3cf19a1f91 100644 --- a/src/calibre/gui2/dialogs/epub.ui +++ b/src/calibre/gui2/dialogs/epub.ui @@ -105,6 +105,36 @@ Book Cover + + + + + + + + + :/images/book.svg + + + true + + + Qt::AlignCenter + + + + + + + + + Use cover from &source file + + + true + + + @@ -156,36 +186,6 @@ - - - - Use cover from &source file - - - true - - - - - - - - - - - - :/images/book.svg - - - true - - - Qt::AlignCenter - - - - - opt_prefer_metadata_cover @@ -486,6 +486,27 @@ + + + + &Use author sort to set author field in output + + + + + + + No text &justification + + + + + + + &Linearize tables + + + @@ -675,11 +696,7 @@ - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'DejaVu Sans'; font-size:10pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">You can control how calibre detects chapters using a XPath expression. To learn how to use XPath expressions see the <a href="https://calibre.kovidgoyal.net/user_manual/xpath.html"><span style=" text-decoration: underline; color:#0000ff;">XPath tutorial</span></a></p></body></html> + <p>You can control how calibre detects chapters using a XPath expression. To learn how to use XPath expressions see the <a href="http://calibre.kovidgoyal.net/user_manual/xpath.html">XPath tutorial</a></p> Qt::RichText @@ -687,6 +704,9 @@ p, li { white-space: pre-wrap; } true + + true + diff --git a/src/calibre/gui2/dialogs/job_view.ui b/src/calibre/gui2/dialogs/job_view.ui index ffd4419da9..f4b0086497 100644 --- a/src/calibre/gui2/dialogs/job_view.ui +++ b/src/calibre/gui2/dialogs/job_view.ui @@ -16,7 +16,7 @@ :/images/view.svg:/images/view.svg - + @@ -30,10 +30,34 @@ + + + + QDialogButtonBox::Ok + + + - + + + buttonBox + accepted() + Dialog + accept() + + + 617 + 442 + + + 206 + -5 + + + + diff --git a/src/calibre/gui2/dialogs/mobi.py b/src/calibre/gui2/dialogs/mobi.py index 4019950c23..0cf4d78b8c 100644 --- a/src/calibre/gui2/dialogs/mobi.py +++ b/src/calibre/gui2/dialogs/mobi.py @@ -17,4 +17,6 @@ class Config(_Config): self.profile_label.setVisible(False) self.opt_profile.setVisible(False) self.opt_dont_split_on_page_breaks.setVisible(False) - self.opt_preserve_tag_structure.setVisible(False) \ No newline at end of file + self.opt_preserve_tag_structure.setVisible(False) + self.opt_linearize_tables.setVisible(False) + self.opt_no_justification.setVisible(False) \ No newline at end of file diff --git a/src/calibre/gui2/dialogs/scheduler.py b/src/calibre/gui2/dialogs/scheduler.py index d15adebb8b..58e5935b66 100644 --- a/src/calibre/gui2/dialogs/scheduler.py +++ b/src/calibre/gui2/dialogs/scheduler.py @@ -10,8 +10,8 @@ Scheduler for automated recipe downloads import sys, copy, time from datetime import datetime, timedelta, date from PyQt4.Qt import QDialog, QApplication, QLineEdit, QPalette, SIGNAL, QBrush, \ - QColor, QAbstractListModel, Qt, QVariant, QFont, QIcon, \ - QFile, QObject, QTimer, QMutex, QMenu, QAction, QTime + QColor, QAbstractItemModel, Qt, QVariant, QFont, QIcon, \ + QFile, QObject, QTimer, QMutex, QMenu, QAction, QTime, QModelIndex from calibre import english_sort from calibre.gui2.dialogs.scheduler_ui import Ui_Dialog @@ -30,6 +30,7 @@ class Recipe(object): self.id = id self.title = getattr(recipe_class, 'title', None) self.description = getattr(recipe_class, 'description', None) + self.language = getattr(recipe_class, 'language', _('Unknown')) self.last_downloaded = datetime.fromordinal(1) self.downloading = False self.builtin = builtin @@ -86,12 +87,12 @@ def load_recipes(): recipes.append(r) return recipes -class RecipeModel(QAbstractListModel, SearchQueryParser): +class RecipeModel(QAbstractItemModel, SearchQueryParser): LOCATIONS = ['all'] def __init__(self, db, *args): - QAbstractListModel.__init__(self, *args) + QAbstractItemModel.__init__(self, *args) SearchQueryParser.__init__(self) self.default_icon = QIcon(':/images/news.svg') self.custom_icon = QIcon(':/images/user_profile.svg') @@ -99,8 +100,11 @@ class RecipeModel(QAbstractListModel, SearchQueryParser): for x in db.get_recipes(): recipe = compile_recipe(x[1]) self.recipes.append(Recipe(x[0], recipe, False)) - self.refresh() - self._map = list(range(len(self.recipes))) + self.refresh() + self.bold_font = QFont() + self.bold_font.setBold(True) + self.bold_font = QVariant(self.bold_font) + def refresh(self): sr = load_recipes() @@ -110,7 +114,35 @@ class RecipeModel(QAbstractListModel, SearchQueryParser): recipe.last_downloaded = sr[sr.index(recipe)].last_downloaded self.recipes.sort() + self.num_of_recipes = len(self.recipes) + self.category_map = {} + for r in self.recipes: + category = getattr(r, 'language', _('Unknown')) + if not r.builtin: + category = _('Custom') + if r.schedule is not None: + category = _('Scheduled') + if category not in self.category_map.keys(): + self.category_map[category] = [] + self.category_map[category].append(r) + + self.categories = sorted(self.category_map.keys(), cmp=self.sort_categories) + self._map = dict(self.category_map) + + def sort_categories(self, x, y): + + def decorate(x): + if x == _('Scheduled'): + x = '0' + x + elif x == _('Custom'): + x = '1' + x + else: + x = '2' + x + return x + + return cmp(decorate(x), decorate(y)) + def universal_set(self): return set(self.recipes) @@ -129,48 +161,64 @@ class RecipeModel(QAbstractListModel, SearchQueryParser): try: results = self.parse(unicode(query)) except ParseException: - self._map = list(range(len(self.recipes))) + self._map = dict(self.category_map) else: - self._map = [] - for i, recipe in enumerate(self.recipes): - if recipe in results: - self._map.append(i) + self._map = {} + for category in self.categories: + self._map[category] = [] + for recipe in self.category_map[category]: + if recipe in results: + self._map[category].append(recipe) self.reset() def resort(self): self.recipes.sort() self.reset() + + def index(self, row, column, parent): + return self.createIndex(row, column, parent.row() if parent.isValid() else -1) + + def parent(self, index): + if index.internalId() == -1: + return QModelIndex() + return self.createIndex(index.internalId(), 0, -1) + + def columnCount(self, parent): + if not parent.isValid() or not parent.parent().isValid(): + return 1 + return 0 + + def rowCount(self, parent): + if not parent.isValid(): + return len(self.categories) + if not parent.parent().isValid(): + category = self.categories[parent.row()] + return len(self._map[category]) + return 0 - def columnCount(self, *args): - return 1 - - def rowCount(self, *args): - return len(self._map) - def data(self, index, role): - recipe = self.recipes[self._map[index.row()]] - if role == Qt.FontRole: - if recipe.schedule is not None: - font = QFont() - font.setBold(True) - return QVariant(font) - if not recipe.builtin: - font = QFont() - font.setItalic(True) - return QVariant(font) - elif role == Qt.DisplayRole: - return QVariant(recipe.title) - elif role == Qt.UserRole: - return recipe - elif role == Qt.DecorationRole: - icon = self.default_icon - icon_path = (':/images/news/%s.png'%recipe.id).replace('recipe_', '') - if not recipe.builtin: - icon = self.custom_icon - elif QFile().exists(icon_path): - icon = QIcon(icon_path) - return QVariant(icon) - + if index.parent().isValid(): + category = self.categories[index.parent().row()] + recipe = self._map[category][index.row()] + if role == Qt.DisplayRole: + return QVariant(recipe.title) + elif role == Qt.UserRole: + return recipe + elif role == Qt.DecorationRole: + icon = self.default_icon + icon_path = (':/images/news/%s.png'%recipe.id).replace('recipe_', '') + if not recipe.builtin: + icon = self.custom_icon + elif QFile().exists(icon_path): + icon = QIcon(icon_path) + return QVariant(icon) + else: + category = self.categories[index.row()] + if role == Qt.DisplayRole: + num = len(self._map[category]) + return QVariant(category + ' [%d]'%num) + elif role == Qt.FontRole: + return self.bold_font return NONE def update_recipe_schedule(self, recipe): @@ -241,8 +289,7 @@ class SchedulerDialog(QDialog, Ui_Dialog): self._model = RecipeModel(db) self.current_recipe = None self.recipes.setModel(self._model) - self.connect(self.recipes, SIGNAL('activated(QModelIndex)'), self.show_recipe) - self.connect(self.recipes, SIGNAL('clicked(QModelIndex)'), self.show_recipe) + self.recipes.currentChanged = self.currentChanged self.connect(self.username, SIGNAL('textEdited(QString)'), self.set_account_info) self.connect(self.password, SIGNAL('textEdited(QString)'), self.set_account_info) self.connect(self.schedule, SIGNAL('stateChanged(int)'), self.do_schedule) @@ -258,10 +305,14 @@ class SchedulerDialog(QDialog, Ui_Dialog): self.connect(self.download, SIGNAL('clicked()'), self.download_now) self.search.setFocus(Qt.OtherFocusReason) self.old_news.setValue(gconf['oldest_news']) - self.rnumber.setText(_('%d recipes')%self._model.rowCount(None)) + self.rnumber.setText(_('%d recipes')%self._model.num_of_recipes) for day in (_('day'), _('Monday'), _('Tuesday'), _('Wednesday'), _('Thursday'), _('Friday'), _('Saturday'), _('Sunday')): self.day.addItem(day) + + def currentChanged(self, current, previous): + if current.parent().isValid(): + self.show_recipe(current) def download_now(self): recipe = self._model.data(self.recipes.currentIndex(), Qt.UserRole) @@ -305,6 +356,7 @@ class SchedulerDialog(QDialog, Ui_Dialog): hour, minute = t.hour(), t.minute() recipe.schedule = encode_schedule(day_of_week, hour, minute) else: + recipe.schedule = None if recipe in recipes: recipes.remove(recipe) save_recipes(recipes) @@ -359,7 +411,6 @@ class SchedulerDialog(QDialog, Ui_Dialog): else: self.last_downloaded.setText(_('Last downloaded: never')) - class Scheduler(QObject): INTERVAL = 1 # minutes @@ -434,7 +485,7 @@ class Scheduler(QObject): day_matches = day > 6 or day == now.tm_wday tnow = now.tm_hour*60 + now.tm_min matches = day_matches and (hour*60+minute) < tnow - if matches and nowt.toordinal() < date.today().toordinal(): + if matches and recipe.last_downloaded.toordinal() < date.today().toordinal(): needs_downloading.add(recipe) self.debug('Needs downloading:', needs_downloading) @@ -466,7 +517,7 @@ class Scheduler(QObject): recipe = self.recipes[self.recipes.index(recipe)] now = datetime.utcnow() d = now - recipe.last_downloaded - if recipe.schedule is not None: + if recipe.schedule is not None and recipe.schedule < 1e4: interval = timedelta(days=recipe.schedule) if abs(d - interval) < timedelta(hours=1): recipe.last_downloaded += interval @@ -489,7 +540,7 @@ class Scheduler(QObject): if recipe not in self.queue: self.do_download(recipe) finally: - self.lock.unlock() + self.lock.unlock() def refresh_schedule(self, recipes): self.recipes = recipes @@ -515,4 +566,4 @@ def main(args=sys.argv): return 0 if __name__ == '__main__': - sys.exit(main()) \ No newline at end of file + sys.exit(main()) diff --git a/src/calibre/gui2/dialogs/scheduler.ui b/src/calibre/gui2/dialogs/scheduler.ui index b10e777d7d..808fd0a0d2 100644 --- a/src/calibre/gui2/dialogs/scheduler.ui +++ b/src/calibre/gui2/dialogs/scheduler.ui @@ -24,8 +24,20 @@ - - + + + false + + + + 16 + 16 + + + + true + + true diff --git a/src/calibre/gui2/images/news/el_mercurio_chile.png b/src/calibre/gui2/images/news/el_mercurio_chile.png new file mode 100644 index 0000000000..1cba7a6aec Binary files /dev/null and b/src/calibre/gui2/images/news/el_mercurio_chile.png differ diff --git a/src/calibre/gui2/images/news/elmundo.png b/src/calibre/gui2/images/news/elmundo.png new file mode 100644 index 0000000000..754b3d0e15 Binary files /dev/null and b/src/calibre/gui2/images/news/elmundo.png differ diff --git a/src/calibre/gui2/images/news/estadao.png b/src/calibre/gui2/images/news/estadao.png new file mode 100644 index 0000000000..8f5637ad58 Binary files /dev/null and b/src/calibre/gui2/images/news/estadao.png differ diff --git a/src/calibre/gui2/images/news/granma.png b/src/calibre/gui2/images/news/granma.png new file mode 100644 index 0000000000..15eaa11fea Binary files /dev/null and b/src/calibre/gui2/images/news/granma.png differ diff --git a/src/calibre/gui2/images/news/jb_online.png b/src/calibre/gui2/images/news/jb_online.png new file mode 100644 index 0000000000..86b4f296e1 Binary files /dev/null and b/src/calibre/gui2/images/news/jb_online.png differ diff --git a/src/calibre/gui2/images/news/la_cuarta.png b/src/calibre/gui2/images/news/la_cuarta.png new file mode 100644 index 0000000000..48a176e908 Binary files /dev/null and b/src/calibre/gui2/images/news/la_cuarta.png differ diff --git a/src/calibre/gui2/images/news/la_tercera.png b/src/calibre/gui2/images/news/la_tercera.png new file mode 100644 index 0000000000..1aa75d7bee Binary files /dev/null and b/src/calibre/gui2/images/news/la_tercera.png differ diff --git a/src/calibre/gui2/images/news/lanacion_chile.png b/src/calibre/gui2/images/news/lanacion_chile.png new file mode 100644 index 0000000000..f6a89ba883 Binary files /dev/null and b/src/calibre/gui2/images/news/lanacion_chile.png differ diff --git a/src/calibre/gui2/images/news/o_globo.png b/src/calibre/gui2/images/news/o_globo.png new file mode 100644 index 0000000000..248a5d4f95 Binary files /dev/null and b/src/calibre/gui2/images/news/o_globo.png differ diff --git a/src/calibre/gui2/images/news/the_oz.png b/src/calibre/gui2/images/news/the_oz.png new file mode 100644 index 0000000000..93f68f9626 Binary files /dev/null and b/src/calibre/gui2/images/news/the_oz.png differ diff --git a/src/calibre/gui2/images/news/vijesti.png b/src/calibre/gui2/images/news/vijesti.png new file mode 100644 index 0000000000..fc2fade6d1 Binary files /dev/null and b/src/calibre/gui2/images/news/vijesti.png differ diff --git a/src/calibre/gui2/library.py b/src/calibre/gui2/library.py index 0e426aaf42..2b0148a194 100644 --- a/src/calibre/gui2/library.py +++ b/src/calibre/gui2/library.py @@ -575,6 +575,7 @@ class BooksModel(QAbstractTableModel): if column == 'rating': val = 0 if val < 0 else 5 if val > 5 else val val *= 2 + self.db.set_rating(id, val) elif column == 'series': pat = re.compile(r'\[(\d+)\]') match = pat.search(val) diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index 4f73e685a2..5da79794fc 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -294,7 +294,7 @@ class Main(MainWindow, Ui_MainWindow): self.stack.setCurrentIndex(0) try: db = LibraryDatabase2(self.library_path) - except OSError, err: + except Exception, err: error_dialog(self, _('Bad database location'), unicode(err)).exec_() dir = unicode(QFileDialog.getExistingDirectory(self, _('Choose a location for your ebook library.'), os.path.expanduser('~'))) @@ -1426,10 +1426,10 @@ class Main(MainWindow, Ui_MainWindow): def donate(self, *args): BUTTON = '''
- - - - + + + +
''' MSG = _('is the result of the efforts of many volunteers from all over the world. If you find it useful, please consider donating to support its development.') diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index b3213aa040..90651b5481 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -355,6 +355,7 @@ class LibraryDatabase2(LibraryDatabase): if isinstance(self.dbpath, unicode): self.dbpath = self.dbpath.encode(filesystem_encoding) self.connect() + self.is_case_sensitive = not os.path.exists(self.dbpath.replace('metadata.db', 'MeTAdAtA.dB')) # Upgrade database while True: meth = getattr(self, 'upgrade_version_%d'%self.user_version, None) diff --git a/src/calibre/manual/faq.rst b/src/calibre/manual/faq.rst index f587a0122a..b5ec52dd7c 100644 --- a/src/calibre/manual/faq.rst +++ b/src/calibre/manual/faq.rst @@ -102,7 +102,7 @@ Device Integration What devices does |app| support? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -At the moment |app| has full support for the SONY PRS 500/505/700, Cybook Gen 3 as well as the iPhone. In addition, using the :guilabel:`Save to disk` function you can use it with any ebook reader that exports itself as a USB disk. +At the moment |app| has full support for the SONY PRS 500/505/700, Cybook Gen 3, Amazon Kindle as well as the iPhone. In addition, using the :guilabel:`Save to disk` function you can use it with any ebook reader that exports itself as a USB disk. I used |app| to transfer some books to my reader, and now the SONY software hangs every time I connect the reader? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/calibre/parallel.py b/src/calibre/parallel.py index a1a439d579..f9b4513c78 100644 --- a/src/calibre/parallel.py +++ b/src/calibre/parallel.py @@ -161,7 +161,7 @@ class WorkerMother(object): self.executable = self.gui_executable = sys.executable self.prefix = '' if isfrozen: - fd = getattr(sys, 'frameworks_dir') + fd = os.path.realpath(getattr(sys, 'frameworks_dir')) contents = os.path.dirname(fd) self.gui_executable = os.path.join(contents, 'MacOS', os.path.basename(sys.executable)) diff --git a/src/calibre/trac/donations/server.py b/src/calibre/trac/donations/server.py index c3e0337290..0141c6a317 100644 --- a/src/calibre/trac/donations/server.py +++ b/src/calibre/trac/donations/server.py @@ -196,7 +196,7 @@ class Server(object): def calculate_month_trend(self, days=31): stats = self.get_slice(date.today()-timedelta(days=days-1), date.today()) - fig = plt.figure(2, (8, 3), 96)#, facecolor, edgecolor, frameon, FigureClass) + fig = plt.figure(2, (12, 4), 96)#, facecolor, edgecolor, frameon, FigureClass) ax = fig.add_subplot(111) x = list(range(days-1, -1, -1)) y = stats.daily_totals @@ -205,6 +205,17 @@ class Server(object): ax.set_ylabel('Income ($)') ax.hlines([stats.daily_average], 0, days-1) ax.set_xlim([0, days-1]) + text = u'''\ +Total: $%(total).2f +Daily average: $%(da).2f \u00b1 %(dd).2f +Average contribution: $%(ac).2f \u00b1 %(ad).2f +Donors per day: %(dpd).2f + '''%dict(total=stats.total, da=stats.daily_average, + dd=stats.daily_deviation, ac=stats.average, + ad=stats.average_deviation, + dpd=len(stats.totals)/float(stats.period.days), + ) + text = ax.annotate(text, (0.6, 0.65), textcoords='axes fraction') fig.savefig(self.MONTH_TRENDS) def calculate_trend(self): @@ -223,7 +234,7 @@ class Server(object): x = [m.min for m in _months] y = [m.total for m in _months] ml = mdates.MonthLocator() # every month - fig = plt.figure(1, (8, 3), 96)#, facecolor, edgecolor, frameon, FigureClass) + fig = plt.figure(1, (8, 4), 96)#, facecolor, edgecolor, frameon, FigureClass) ax = fig.add_subplot(111) ax.bar(x, y, align='center', width=20, color='g') ax.xaxis.set_major_locator(ml) diff --git a/src/calibre/trac/plugins/templates/binary.html b/src/calibre/trac/plugins/templates/binary.html index cd67bf41f2..7361e37014 100644 --- a/src/calibre/trac/plugins/templates/binary.html +++ b/src/calibre/trac/plugins/templates/binary.html @@ -23,15 +23,14 @@ While you wait for the download to complete, please consider donating to support the development of ${app}.
-
+ - + + - -
- -
-
+ +
+

Note

$note
diff --git a/src/calibre/trac/plugins/templates/distro.html b/src/calibre/trac/plugins/templates/distro.html index 09856c35be..e735c6f4ad 100644 --- a/src/calibre/trac/plugins/templates/distro.html +++ b/src/calibre/trac/plugins/templates/distro.html @@ -57,11 +57,11 @@ python setup.py build && sudo python setup.py install While you wait for the installation to complete, please consider donating to support the development of ${distro.app}.
- - - - -
+ + + + +
diff --git a/src/calibre/translations/ar.po b/src/calibre/translations/ar.po new file mode 100644 index 0000000000..f615c21206 --- /dev/null +++ b/src/calibre/translations/ar.po @@ -0,0 +1,5427 @@ +# Arabic translation for calibre +# Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 +# This file is distributed under the same license as the calibre package. +# FIRST AUTHOR , 2009. +# +msgid "" +msgstr "" +"Project-Id-Version: calibre\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2009-01-27 01:54+0000\n" +"PO-Revision-Date: 2009-02-04 10:04+0000\n" +"Last-Translator: Abdellah Chelli \n" +"Language-Team: Arabic \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2009-02-04 21:04+0000\n" +"X-Generator: Launchpad (build Unknown)\n" + +#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41 +msgid "Does absolutely nothing" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:44 +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/from_any.py:71 +#: /home/kovid/work/calibre/src/calibre/ebooks/html.py:497 +#: /home/kovid/work/calibre/src/calibre/ebooks/html.py:989 +#: /home/kovid/work/calibre/src/calibre/ebooks/html.py:1002 +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:77 +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:79 +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:81 +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:86 +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/comic/convert_from.py:295 +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/fb2/convert_from.py:61 +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/fb2/convert_from.py:95 +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/fb2/convert_from.py:97 +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/fb2/convert_from.py:99 +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/fb2/convert_from.py:101 +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/pdf/convert_from.py:69 +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/rtf/convert_from.py:179 +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/txt/convert_from.py:70 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:199 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:229 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:232 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:255 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:277 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/meta.py:45 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/meta.py:47 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/meta.py:87 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/meta.py:89 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/mobi.py:145 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf.py:334 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf.py:449 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf2.py:820 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/pdf.py:12 +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:36 +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:66 +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:477 +#: /home/kovid/work/calibre/src/calibre/ebooks/odt/to_oeb.py:46 +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:361 +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:366 +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:858 +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:861 +#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/pdftrim.py:53 +#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/pdftrim.py:54 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf.py:48 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:168 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:170 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:365 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:37 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:38 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:362 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:376 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:899 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:700 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:942 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:945 +#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:61 +#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:123 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:257 +#: /home/kovid/work/calibre/src/calibre/library/database.py:920 +#: /home/kovid/work/calibre/src/calibre/library/database.py:1405 +#: /home/kovid/work/calibre/src/calibre/library/database.py:1434 +#: /home/kovid/work/calibre/src/calibre/library/database.py:1468 +#: /home/kovid/work/calibre/src/calibre/library/database.py:1594 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:473 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:485 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:828 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:861 +msgid "Unknown" +msgstr "مجهول" + +#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:62 +msgid "Base" +msgstr "أساس" + +#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:148 +msgid "File type" +msgstr "نوع الملف" + +#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:182 +msgid "Metadata reader" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:209 +msgid "Metadata writer" +msgstr "كاتب البيانات الوصفية" + +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:12 +msgid "" +"Follow all local links in an HTML file and create a ZIP file containing all " +"linked files. This plugin is run every time you add an HTML file to the " +"library." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:32 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:43 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:53 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:64 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:74 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:84 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:94 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:105 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:116 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:126 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:136 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:146 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:156 +msgid "Read metadata from %s files" +msgstr "إقرأ البيانات الوصفية من الملفات %s" + +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:166 +msgid "Extract cover from comic files" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:186 +msgid "Read metadata from ebooks in ZIP archives" +msgstr "إقرأ البيانات الوصفية من كتب إلكترونية في محفوظات ZIP" + +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:196 +msgid "Read metadata from ebooks in RAR archives" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:207 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:217 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:227 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:237 +msgid "Set metadata in %s files" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:28 +msgid "Installed plugins" +msgstr "ملحقات مثبتة" + +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:29 +msgid "Mapping for filetype plugins" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:30 +msgid "Local plugin customization" +msgstr "تخصيص ملحقات محلية" + +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:31 +msgid "Disabled plugins" +msgstr "ملحقات معطلة" + +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:66 +msgid "No valid plugin found in " +msgstr "لم يعثر على أي ملحق صالح في " + +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:184 +msgid "Initialization of plugin %s failed with traceback:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:261 +msgid "" +" %prog options\n" +" \n" +" Customize calibre by loading external plugins.\n" +" " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:267 +msgid "Add a plugin by specifying the path to the zip file containing it." +msgstr "أضف ملحقا بتعيين المسار إلى ملف zip الذي يحتويه." + +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:269 +msgid "Remove a custom plugin by name. Has no effect on builtin plugins" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:271 +msgid "" +"Customize plugin. Specify name of plugin and customization string separated " +"by a comma." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:273 +msgid "List all installed plugins" +msgstr "أسرد كل الملحقات المثبتة" + +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:275 +msgid "Enable the named plugin" +msgstr "شغل الملحق المسمى" + +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:277 +msgid "Disable the named plugin" +msgstr "عطل الملحق المسمى" + +#: /home/kovid/work/calibre/src/calibre/devices/cybookg3/driver.py:41 +#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:384 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:70 +msgid "The reader has no storage card connected." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/devices/cybookg3/driver.py:60 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:89 +msgid "There is insufficient free space on the storage card" +msgstr "لا توجد مساحة كافية في بطاقة التخزين" + +#: /home/kovid/work/calibre/src/calibre/devices/cybookg3/driver.py:62 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:91 +msgid "There is insufficient free space in main memory" +msgstr "لا توجد مساحة كافية في الذاكرة الرئيسية" + +#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:140 +#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:167 +#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:195 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:191 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:227 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:254 +msgid "Unable to detect the %s disk drive. Try rebooting." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:94 +msgid "Options to control the conversion to EPUB" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:105 +msgid "" +"The output EPUB file. If not specified, it is derived from the input file " +"name." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:107 +msgid "" +"Profile of the target device this EPUB is meant for. Set to None to create a " +"device independent EPUB. The profile is used for device specific " +"restrictions on the EPUB. Choices are: " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:109 +msgid "" +"Either the path to a CSS stylesheet or raw CSS. This CSS will override any " +"existing CSS declarations in the source files." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:110 +msgid "Control auto-detection of document structure." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:112 +msgid "" +"An XPath expression to detect chapter titles. The default is to consider " +"

or\n" +"

tags that contain the words \"chapter\",\"book\",\"section\" or " +"\"part\" as chapter titles as \n" +"well as any tags that have class=\"chapter\". \n" +"The expression used must evaluate to a list of elements. To disable chapter " +"detection,\n" +"use the expression \"/\". See the XPath Tutorial in the calibre User Manual " +"for further\n" +"help on using this feature.\n" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:121 +msgid "" +"Specify how to mark detected chapters. A value of \"pagebreak\" will insert " +"page breaks before chapters. A value of \"rule\" will insert a line before " +"chapters. A value of \"none\" will disable chapter marking and a value of " +"\"both\" will use both page breaks and lines to mark chapters." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:123 +msgid "Path to the cover to be used for this book" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:126 +msgid "" +"Use the cover detected from the source file in preference to the specified " +"cover." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:128 +msgid "" +"Turn off splitting at page breaks. Normally, input files are automatically " +"split at every page break into two files. This gives an output ebook that " +"can be parsed faster and with less resources. However, splitting is slow and " +"if your source file contains a very large number of page breaks, you should " +"turn off splitting on page breaks." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:130 +msgid "" +"Control the automatic generation of a Table of Contents. If an OPF file is " +"detected\n" +"and it specifies a Table of Contents, then that will be used rather than " +"trying\n" +"to auto-generate a Table of Contents.\n" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:136 +msgid "" +"Maximum number of links to insert into the TOC. Set to 0 to disable. Default " +"is: %default. Links are only added to the TOC if less than the --toc-" +"threshold number of chapters were detected." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:138 +msgid "Don't add auto-detected chapters to the Table of Contents." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:140 +msgid "" +"If fewer than this number of chapters is detected, then links are added to " +"the Table of Contents. Default: %default" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:142 +msgid "" +"XPath expression that specifies all tags that should be added to the Table " +"of Contents at level one. If this is specified, it takes precedence over " +"other forms of auto-detection." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:144 +msgid "" +"XPath expression that specifies all tags that should be added to the Table " +"of Contents at level two. Each entry is added under the previous level one " +"entry." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:146 +msgid "" +"Path to a .ncx file that contains the table of contents to use for this " +"ebook. The NCX file should contain links relative to the directory it is " +"placed in. See http://www.niso.org/workrooms/daisy/Z39-86-2005.html#NCX for " +"an overview of the NCX format." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:148 +msgid "" +"Normally, if the source file already has a Table of Contents, it is used in " +"preference to the autodetected one. With this option, the autodetected one " +"is always used." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:150 +msgid "Control page layout" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:152 +msgid "Set the top margin in pts. Default is %default" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:154 +msgid "Set the bottom margin in pts. Default is %default" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:156 +msgid "Set the left margin in pts. Default is %default" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:158 +msgid "Set the right margin in pts. Default is %default" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:160 +msgid "" +"The base font size in pts. Default is %defaultpt. Set to 0 to disable " +"rescaling of fonts." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:162 +msgid "" +"Remove spacing between paragraphs. Will not work if the source file forces " +"inter-paragraph spacing." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:164 +msgid "" +"Preserve the HTML tag structure while splitting large HTML files. This is " +"only neccessary if the HTML files contain CSS that uses sibling selectors. " +"Enabling this greatly slows down processing of large HTML files." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:167 +msgid "Print generated OPF file to stdout" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:169 +msgid "Print generated NCX file to stdout" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:171 +msgid "Keep intermediate files during processing by html2epub" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:173 +msgid "" +"Extract the contents of the produced EPUB file to the specified directory." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/from_any.py:184 +msgid "" +"%%prog [options] filename\n" +"\n" +"Convert any of a large number of ebook formats to a %s file. Supported " +"formats are: %s\n" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/from_html.py:102 +msgid "Could not find an ebook inside the archive" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/from_html.py:214 +msgid "" +"%prog [options] file.html|opf\n" +"\n" +"Convert a HTML file to an EPUB ebook. Recursively follows links in the HTML " +"file.\n" +"If you specify an OPF file instead of an HTML file, the list of links is " +"takes from\n" +"the element of the OPF file. \n" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/from_html.py:465 +#: /home/kovid/work/calibre/src/calibre/ebooks/lit/writer.py:746 +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/writer.py:589 +msgid "Output written to " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/from_html.py:487 +#: /home/kovid/work/calibre/src/calibre/ebooks/html.py:1087 +msgid "You must specify an input HTML file" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/iterator.py:36 +msgid "%s format books are not supported" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/split.py:30 +msgid "" +"Could not find reasonable point at which to split: %s Sub-tree size: %d KB" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/epub/split.py:142 +msgid "" +"\t\tToo much markup. Re-splitting without structure preservation. This may " +"cause incorrect rendering." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/html.py:509 +msgid "Written processed HTML to " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/html.py:872 +msgid "Options to control the traversal of HTML" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/html.py:879 +msgid "The output directory. Default is the current directory." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/html.py:881 +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/writer.py:537 +msgid "Character encoding for HTML files. Default is to auto detect." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/html.py:883 +msgid "" +"Create the output in a zip file. If this option is specified, the --output " +"should be the name of a file not a directory." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/html.py:885 +msgid "Control the following of links in HTML files." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/html.py:887 +msgid "" +"Traverse links in HTML files breadth first. Normally, they are traversed " +"depth first" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/html.py:889 +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." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/html.py:891 +msgid "Set metadata of the generated ebook" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/html.py:893 +msgid "Set the title. Default is to autodetect." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/html.py:895 +msgid "The author(s) of the ebook, as a comma separated list." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/html.py:897 +msgid "The subject(s) of this book, as a comma separated list." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/html.py:899 +msgid "Set the publisher of this book." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/html.py:901 +msgid "A summary of this book." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/html.py:903 +msgid "Load metadata from the specified OPF file" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/html.py:905 +msgid "Options useful for debugging" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/html.py:907 +msgid "" +"Be more verbose while processing. Can be specified multiple times to " +"increase verbosity." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/html.py:909 +msgid "Output HTML is \"pretty printed\" for easier parsing by humans" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/html.py:915 +msgid "" +"%prog [options] file.html|opf\n" +"\n" +"Follow all links in an HTML file and collect them into the specified " +"directory.\n" +"Also collects any resources like images, stylesheets, scripts, etc. \n" +"If an OPF file is specified instead, the list of files in its " +"element\n" +"is used.\n" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lit/from_any.py:47 +msgid "Creating LIT file from EPUB..." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lit/reader.py:852 +msgid "%prog [options] LITFILE" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lit/reader.py:855 +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:501 +msgid "Output directory. Defaults to current directory." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lit/reader.py:858 +msgid "Legibly format extracted markup. May modify meaningful whitespace." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lit/reader.py:861 +#: /home/kovid/work/calibre/src/calibre/ebooks/lit/writer.py:719 +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/writer.py:549 +msgid "Useful for debugging." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lit/reader.py:872 +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:525 +msgid "OEB ebook created in" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lit/writer.py:713 +msgid "%prog [options] OPFFILE" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lit/writer.py:716 +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/from_feeds.py:26 +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/writer.py:546 +msgid "Output file. Default is derived from input filename." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:74 +msgid "Set the title. Default: filename." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:76 +msgid "" +"Set the author(s). Multiple authors should be set as a comma separated list. " +"Default: %default" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:79 +msgid "Set the comment." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:81 +msgid "Set the category" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:83 +msgid "Sort key for the title" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:85 +msgid "Sort key for the author" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:87 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:278 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:39 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:108 +msgid "Publisher" +msgstr "ناشر" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:89 +msgid "Path to file containing image to be used as cover" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:91 +msgid "" +"If there is a cover graphic detected in the source file, use that instead of " +"the specified cover." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:94 +msgid "Output file name. Default is derived from input filename" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:96 +msgid "" +"Render HTML tables as blocks of text instead of actual tables. This is " +"neccessary if the HTML contains very large or complex tables." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:99 +msgid "" +"Specify the base font size in pts. All fonts are rescaled accordingly. This " +"option obsoletes the --font-delta option and takes precedence over it. To " +"use --font-delta, set this to 0. Default: %defaultpt" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:101 +msgid "Enable autorotation of images that are wider than the screen width." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:104 +msgid "Set the space between words in pts. Default is %default" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:106 +msgid "Separate paragraphs by blank lines." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:108 +msgid "Add a header to all the pages with title and author." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:110 +msgid "" +"Set the format of the header. %a is replaced by the author and %t by the " +"title. Default is %default" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:112 +msgid "Add extra spacing below the header. Default is %default px." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:114 +msgid "" +"Override the CSS. Can be either a path to a CSS stylesheet or a string. If " +"it is a string it is interpreted as CSS." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:116 +msgid "" +"Use the element from the OPF file to determine the order in which " +"the HTML files are appended to the LRF. The .opf file must be in the same " +"directory as the base HTML file." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:118 +msgid "" +"Minimum paragraph indent (the indent of the first line of a paragraph) in " +"pts. Default: %default" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:120 +msgid "" +"Increase the font size by 2 * FONT_DELTA pts and the line spacing by " +"FONT_DELTA pts. FONT_DELTA can be a fraction.If FONT_DELTA is negative, the " +"font size is decreased." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:125 +msgid "" +"Render all content as black on white instead of the colors specified by the " +"HTML or CSS." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:131 +msgid "" +"Profile of the target device for which this LRF is being generated. The " +"profile determines things like the resolution and screen size of the target " +"device. Default: %s Supported profiles: " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:137 +msgid "Left margin of page. Default is %default px." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:139 +msgid "Right margin of page. Default is %default px." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:141 +msgid "Top margin of page. Default is %default px." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:143 +msgid "Bottom margin of page. Default is %default px." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:145 +msgid "" +"Render tables in the HTML as images (useful if the document has large or " +"complex tables)" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:147 +msgid "" +"Multiply the size of text in rendered tables by this factor. Default is " +"%default" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:152 +msgid "" +"The maximum number of levels to recursively process links. A value of 0 " +"means thats links are not followed. A negative value means that tags are " +"ignored." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:156 +msgid "" +"A regular expression. tags whose href matches will be ignored. Defaults " +"to %default" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:160 +msgid "Don't add links to the table of contents." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:164 +msgid "Prevent the automatic detection chapters." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:167 +msgid "" +"The regular expression used to detect chapter titles. It is searched for in " +"heading tags (h1-h6). Defaults to %default" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:170 +msgid "" +"Detect a chapter beginning at an element having the specified attribute. The " +"format for this option is tagname regexp,attribute name,attribute value " +"regexp. For example to match all heading tags that have the attribute " +"class=\"chapter\" you would use \"h\\d,class,chapter\". You can set the " +"attribute to \"none\" to match only on tag names. So for example, to match " +"all h2 tags, you would use \"h2,none,\". Default is %default" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:172 +msgid "" +"If html2lrf does not find any page breaks in the html file and cannot detect " +"chapter headings, it will automatically insert page-breaks before the tags " +"whose names match this regular expression. Defaults to %default. You can " +"disable it by setting the regexp to \"$\". The purpose of this option is to " +"try to ensure that there are no really long pages as this degrades the page " +"turn performance of the LRF. Thus this option is ignored if the current page " +"has only a few elements." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:182 +msgid "" +"Force a page break before tags whose names match this regular expression." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:184 +msgid "" +"Force a page break before an element having the specified attribute. The " +"format for this option is tagname regexp,attribute name,attribute value " +"regexp. For example to match all heading tags that have the attribute " +"class=\"chapter\" you would use \"h\\d,class,chapter\". Default is %default" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:187 +msgid "Add detected chapters to the table of contents." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:190 +msgid "Preprocess Baen HTML files to improve generated LRF." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:192 +msgid "" +"You must add this option if processing files generated by pdftohtml, " +"otherwise conversion will fail." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:194 +msgid "Use this option on html0 files from Book Designer." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:197 +msgid "" +"Specify trutype font families for serif, sans-serif and monospace fonts. " +"These fonts will be embedded in the LRF file. Note that custom fonts lead to " +"slower page turns. For example: --serif-family \"Times New Roman\"\n" +" " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:205 +msgid "The serif family of fonts to embed" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:208 +msgid "The sans-serif family of fonts to embed" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:211 +msgid "The monospace family of fonts to embed" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:215 +msgid "Be verbose while processing" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:217 +msgid "Convert to LRS" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:219 +msgid "" +"Minimize memory usage at the cost of longer processing times. Use this " +"option if you are on a memory constrained machine." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:221 +msgid "" +"Specify the character encoding of the source file. If the output LRF file " +"contains strange characters, try changing this option. A common encoding for " +"files from windows computers is cp-1252. Another common choice is utf-8. The " +"default is to try and guess the encoding." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/any/convert_from.py:164 +msgid "Converting from %s to LRF is not supported." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/any/convert_from.py:175 +msgid "" +"any2lrf [options] myfile\n" +"\n" +"Convert any ebook format into LRF. Supported formats are:\n" +"LIT, RTF, TXT, HTML, EPUB, MOBI, PRC and PDF. any2lrf will also process a " +"RAR or\n" +"ZIP archive, looking for an ebook inside the archive.\n" +" " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/any/convert_from.py:190 +msgid "No file to convert specified." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/comic/convert_from.py:224 +msgid "Rendered %s" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/comic/convert_from.py:227 +msgid "Failed %s" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/comic/convert_from.py:279 +msgid "" +"Failed to process comic: %s\n" +"\n" +"%s" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/comic/convert_from.py:286 +msgid "" +"Options to control the conversion of comics (CBR, CBZ) files into ebooks" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/comic/convert_from.py:292 +msgid "Title for generated ebook. Default is to use the filename." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/comic/convert_from.py:294 +msgid "" +"Set the author in the metadata of the generated ebook. Default is %default" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/comic/convert_from.py:297 +#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/pdftrim.py:22 +msgid "" +"Path to output file. By default a file is created in the current directory." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/comic/convert_from.py:299 +msgid "Number of colors for grayscale image conversion. Default: %default" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/comic/convert_from.py:301 +msgid "" +"Disable normalize (improve contrast) color range for pictures. Default: False" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/comic/convert_from.py:303 +msgid "Maintain picture aspect ratio. Default is to fill the screen." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/comic/convert_from.py:305 +msgid "Disable sharpening." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/comic/convert_from.py:307 +msgid "Don't split landscape images into two portrait images" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/comic/convert_from.py:309 +msgid "" +"Keep aspect ratio and scale image using screen height as image width for " +"viewing in landscape mode." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/comic/convert_from.py:311 +msgid "" +"Used for right-to-left publications like manga. Causes landscape pages to be " +"split into portrait pages from right to left." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/comic/convert_from.py:313 +msgid "" +"Enable Despeckle. Reduces speckle noise. May greatly increase processing " +"time." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/comic/convert_from.py:315 +msgid "" +"Don't sort the files found in the comic alphabetically by name. Instead use " +"the order they were added to the comic." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/comic/convert_from.py:317 +msgid "" +"Choose a profile for the device you are generating this file for. The " +"default is the SONY PRS-500 with a screen size of 584x754 pixels. This is " +"suitable for any reader with the same screen size. Choices are %s" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/comic/convert_from.py:319 +#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/pdftrim.py:20 +msgid "" +"Be verbose, useful for debugging. Can be specified multiple times for " +"greater verbosity." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/comic/convert_from.py:321 +msgid "Don't show progress bar." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/comic/convert_from.py:324 +msgid "Apply no processing to the image" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/comic/convert_from.py:329 +msgid "" +"%prog [options] comic.cb[z|r]\n" +"\n" +"Convert a comic in a CBZ or CBR file to an ebook. \n" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/comic/convert_from.py:389 +msgid "Output written to" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/comic/convert_from.py:549 +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/from_comic.py:35 +msgid "Rendering comic pages..." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/epub/convert_from.py:17 +msgid "" +"Usage: %prog [options] mybook.epub\n" +" \n" +" \n" +"%prog converts mybook.epub to mybook.lrf" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/fb2/convert_from.py:23 +msgid "" +"%prog [options] mybook.fb2\n" +"\n" +"\n" +"%prog converts mybook.fb2 to mybook.lrf" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/fb2/convert_from.py:28 +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/txt/convert_from.py:24 +msgid "Print generated HTML to stdout and quit." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/fb2/convert_from.py:30 +msgid "Keep generated HTML files after completing conversion to LRF." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/feeds/convert_from.py:20 +msgid "Options to control the behavior of feeds2disk" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/feeds/convert_from.py:22 +msgid "Options to control the behavior of html2lrf" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/feeds/convert_from.py:44 +msgid "Fetching of recipe failed: " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:317 +msgid "\tBook Designer file detected." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:319 +msgid "\tParsing HTML..." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:342 +msgid "\tBaen file detected. Re-parsing..." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:358 +msgid "Written preprocessed HTML to " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:376 +msgid "Processing %s" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:390 +msgid "\tConverting to BBeB..." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:536 +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:549 +msgid "Could not parse file: %s" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:541 +msgid "%s is an empty file" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:561 +msgid "Failed to parse link %s %s" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:605 +msgid "Cannot add link %s to TOC" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:957 +msgid "Unable to process image %s. Error: %s" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:995 +msgid "Unable to process interlaced PNG %s" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:1010 +msgid "" +"Could not process image: %s\n" +"%s" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:1763 +msgid "" +"An error occurred while processing a table: %s. Ignoring table markup." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:1765 +msgid "" +"Bad table:\n" +"%s" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:1787 +msgid "Table has cell that is too large" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:1817 +msgid "" +"You have to save the website %s as an html file first and then run html2lrf " +"on it." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:1860 +msgid "Could not read cover image: %s" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:1863 +msgid "Cannot read from: %s" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:1988 +msgid "Failed to process opf file" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:1994 +msgid "" +"Usage: %prog [options] mybook.html\n" +"\n" +"\n" +"%prog converts mybook.html to mybook.lrf. \n" +"%prog follows all links in mybook.html that point \n" +"to local files recursively. Thus, you can use it to \n" +"convert a whole tree of HTML files." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/lit/convert_from.py:15 +msgid "" +"Usage: %prog [options] mybook.lit\n" +"\n" +"\n" +"%prog converts mybook.lit to mybook.lrf" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/lrfparser.py:136 +msgid "" +"%prog book.lrf\n" +"Convert an LRF file into an LRS (XML UTF-8 encoded) file" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/lrfparser.py:137 +msgid "Output LRS file" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/lrfparser.py:139 +msgid "Do not save embedded image and font files to disk" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/lrfparser.py:158 +msgid "Parsing LRF..." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/lrfparser.py:161 +msgid "Creating XML..." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/lrfparser.py:163 +msgid "LRS written to " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/lrs/convert_from.py:249 +msgid "Could not read from thumbnail file:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/lrs/convert_from.py:269 +msgid "" +"%prog [options] file.lrs\n" +"Compile an LRS file into an LRF file." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/lrs/convert_from.py:270 +msgid "Path to output file" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/lrs/convert_from.py:272 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/isbndb.py:115 +msgid "Verbose processing" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/lrs/convert_from.py:274 +msgid "Convert LRS to LRS, useful for debugging." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:455 +msgid "Invalid LRF file. Could not set metadata." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:580 +msgid "" +"%prog [options] mybook.lrf\n" +"\n" +"\n" +"Show/edit the metadata in an LRF file.\n" +"\n" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:587 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:43 +msgid "Set the book title" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:589 +msgid "Set sort key for the title" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:591 +msgid "Set the author" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:593 +msgid "Set sort key for the author" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:595 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:47 +msgid "The category this book belongs to. E.g.: History" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:598 +msgid "Path to a graphic that will be set as this files' thumbnail" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:601 +msgid "" +"Path to a txt file containing the comment to be stored in the lrf file." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:605 +msgid "Extract thumbnail from LRF file" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:606 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/mobi.py:182 +msgid "Set the publisher" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:607 +msgid "Set the book classification" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:608 +msgid "Set the book creator" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:609 +msgid "Set the book producer" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:611 +msgid "" +"Extract cover from LRF file. Note that the LRF format has no defined cover, " +"so we use some heuristics to guess the cover." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:613 +msgid "Set book ID" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/mobi/convert_from.py:43 +msgid "" +"Usage: %prog [options] mybook.mobi|prc\n" +"\n" +"\n" +"%prog converts mybook.mobi to mybook.lrf" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/pdf/convert_from.py:47 +msgid "Could not find pdftohtml, check it is in your PATH" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/pdf/convert_from.py:61 +msgid "" +" is an image based PDF. Only conversion of text based PDFs is supported." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/pdf/convert_from.py:80 +msgid "" +"%prog [options] mybook.pdf\n" +"\n" +"\n" +"%prog converts mybook.pdf to mybook.lrf" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/pdf/reflow.py:403 +msgid "" +"Path to output directory in which to create the HTML file. Defaults to " +"current directory." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/pdf/reflow.py:405 +msgid "Be more verbose." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/pdf/reflow.py:417 +msgid "You must specify a single PDF file." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/rtf/convert_from.py:21 +msgid "" +"%prog [options] mybook.rtf\n" +"\n" +"\n" +"%prog converts mybook.rtf to mybook.lrf" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/rtf/convert_from.py:146 +msgid "" +"This RTF file has a feature calibre does not support. Convert it to HTML and " +"then convert it." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/txt/convert_from.py:19 +msgid "" +"%prog [options] mybook.txt\n" +"\n" +"\n" +"%prog converts mybook.txt to mybook.lrf" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:45 +msgid "Set the authors" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:49 +msgid "Set the comment" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:276 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:69 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:70 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:36 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:103 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:359 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:969 +msgid "Title" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:277 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:37 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:104 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:364 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:970 +msgid "Author(s)" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:279 +msgid "Producer" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:280 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:493 +msgid "Category" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:281 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:71 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info_ui.py:64 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:432 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:515 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:332 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:320 +#: /home/kovid/work/calibre/src/calibre/gui2/status.py:58 +msgid "Comments" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:283 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:109 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:309 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:909 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:973 +#: /home/kovid/work/calibre/src/calibre/gui2/status.py:60 +#: /home/kovid/work/calibre/src/calibre/gui2/tags.py:50 +msgid "Tags" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:284 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:110 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:325 +#: /home/kovid/work/calibre/src/calibre/gui2/status.py:59 +#: /home/kovid/work/calibre/src/calibre/gui2/tags.py:50 +msgid "Series" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:285 +msgid "Language" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/epub.py:199 +msgid "A comma separated list of tags to set" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/epub.py:201 +msgid "The series to which this book belongs" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/epub.py:203 +msgid "The series index" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/epub.py:205 +msgid "The book language" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/epub.py:207 +msgid "Extract the cover" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/fb2.py:54 +msgid "Usage:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/imp.py:53 +msgid "Usage: imp-meta file.imp" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/imp.py:54 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/pdf.py:59 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/rb.py:60 +msgid "No filename specified." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/isbndb.py:97 +msgid "" +"\n" +"%prog [options] key\n" +"\n" +"Fetch metadata for books from isndb.com. You can specify either the \n" +"books ISBN ID or its title and author. If you specify the title and author,\n" +"then more than one book may be returned.\n" +"\n" +"key is the account key you generate after signing up for a free account from " +"isbndb.com.\n" +"\n" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/isbndb.py:108 +msgid "The ISBN ID of the book you want metadata for." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/isbndb.py:110 +msgid "The author whose book to search for." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/isbndb.py:112 +msgid "The title of the book to search for." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/isbndb.py:114 +msgid "The publisher of the book to search for." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/library_thing.py:47 +msgid "" +"Could not fetch cover as server is experiencing high load. Please try again " +"later." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/library_thing.py:48 +msgid " not found." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/library_thing.py:51 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/library_thing.py:82 +msgid "LibraryThing.com server error. Try again later." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/library_thing.py:60 +msgid "" +"\n" +"%prog [options] ISBN\n" +"\n" +"Fetch a cover image for the book identified by ISBN from LibraryThing.com\n" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/lit.py:35 +msgid "Usage: %s file.lit" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/lit.py:45 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/mobi.py:227 +msgid "Cover saved to" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/mobi.py:178 +msgid "Set the subject tags" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/mobi.py:180 +msgid "Set the language" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/mobi.py:184 +msgid "Set the ISBN" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf2.py:965 +msgid "Set the dc:language field" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/pdf.py:58 +msgid "Usage: pdf-meta file.pdf" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/rb.py:59 +msgid "Usage: rb-meta file.rb" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/from_any.py:55 +msgid "Creating Mobipocket file from EPUB..." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:499 +msgid "%prog [options] myebook.mobi" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:523 +msgid "Raw MOBI HTML saved in" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/writer.py:512 +msgid "Options to control the conversion to MOBI" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/writer.py:519 +msgid "Mobipocket-specific options." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/writer.py:521 +msgid "" +"Compress file text using PalmDOC compression. Results in smaller files, but " +"takes a long time to run." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/writer.py:524 +msgid "Modify images to meet Palm device size limitations." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/writer.py:526 +msgid "Title for any generated in-line table of contents." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/writer.py:527 +msgid "" +"Device renderer profiles. Affects conversion of font sizes, image rescaling " +"and rasterization of tables. Valid profiles are: %s." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/writer.py:532 +msgid "Source renderer profile. Default is %default." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/writer.py:535 +msgid "Destination renderer profile. Default is %default." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/writer.py:543 +msgid "[options]" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/writer.py:561 +msgid "Unknown source profile %r" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/writer.py:565 +msgid "Unknown destination profile %r" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/odt/to_oeb.py:57 +msgid "The output directory. Defaults to the current directory." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:586 +msgid "Cover" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:587 +msgid "Title Page" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:588 +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/transforms/htmltoc.py:18 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:47 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:160 +msgid "Table of Contents" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:589 +msgid "Index" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:590 +msgid "Glossary" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:591 +msgid "Acknowledgements" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:592 +msgid "Bibliography" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:593 +msgid "Colophon" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:594 +msgid "Copyright" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:595 +msgid "Dedication" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:596 +msgid "Epigraph" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:597 +msgid "Foreword" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:598 +msgid "List of Illustrations" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:599 +msgid "List of Tables" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:600 +msgid "Notes" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:601 +msgid "Preface" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:602 +msgid "Main Text" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/pdftrim.py:13 +msgid "Options to control the transformation of pdf" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/pdftrim.py:24 +msgid "Number of pixels to crop from the left most x (default is %d) " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/pdftrim.py:26 +msgid "Number of pixels to crop from the left most y (default is %d) " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/pdftrim.py:28 +msgid "Number of pixels to crop from the right most x (default is %d) " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/pdftrim.py:30 +msgid "Number of pixels to crop from the right most y (default is %d)" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/pdftrim.py:32 +msgid "" +"A file generated by ghostscript which allows each page to be individually " +"cropped [gs -dSAFER -dNOPAUSE -dBATCH -sDEVICE=bbox > bounding] " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/pdftrim.py:38 +msgid "" +"\t%prog [options] file.pdf\n" +"\n" +"\tCrops a pdf. \n" +"\t" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:25 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:433 +msgid "Frequently used directories" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:27 +msgid "Send file to storage card instead of main memory by default" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:29 +msgid "The format to use when saving single files to disk" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:31 +msgid "Confirm before deleting" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:33 +msgid "Toolbar icon size" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:35 +msgid "Show button labels in the toolbar" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:37 +msgid "Main window geometry" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:39 +msgid "Notify when a new version is available" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:41 +msgid "Use Roman numerals for series number" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:43 +msgid "Sort tags list by popularity" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:45 +msgid "Number of covers to show in the cover browsing mode" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:47 +msgid "Defaults for conversion to LRF" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:49 +msgid "Options for the LRF ebook viewer" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:51 +msgid "Formats that are viewed using the internal viewer" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:53 +msgid "Columns to be displayed in the book list" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:54 +msgid "Automatically launch content server on application startup" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:55 +msgid "Oldest news kept in database" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:56 +msgid "Show system tray icon" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:58 +msgid "Upload downloaded news to device" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:60 +msgid "Delete books from library after uploading to device" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:62 +msgid "" +"Show the cover flow in a separate window instead of in the main calibre " +"window" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:64 +msgid "Disable notifications from the system tray icon" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:72 +msgid "Device no longer connected." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:117 +msgid "Get device information" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:128 +msgid "Get list of books on device" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:137 +msgid "Send metadata to device" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:146 +msgid "Upload %d books to device" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:161 +msgid "Delete books from device" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:176 +msgid "Download books from device" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/device.py:186 +msgid "View book on device" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:84 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:85 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:86 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:316 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:904 +#: /home/kovid/work/calibre/src/calibre/gui2/status.py:56 +msgid "Path" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:87 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:88 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:89 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:92 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:315 +#: /home/kovid/work/calibre/src/calibre/gui2/status.py:57 +#: /home/kovid/work/calibre/src/calibre/gui2/tags.py:50 +msgid "Formats" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info_ui.py:61 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:88 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/progress_ui.py:48 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/warning_ui.py:52 +msgid "Dialog" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info_ui.py:62 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info_ui.py:63 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/choose_format_ui.py:41 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/confirm_delete_ui.py:49 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:56 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/progress_ui.py:49 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/progress_ui.py:50 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/warning_ui.py:53 +msgid "TextLabel" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info_ui.py:65 +msgid "&Previous" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info_ui.py:66 +msgid "&Next" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/choose_format_ui.py:40 +msgid "Choose Format" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf.py:34 +msgid "Set defaults for conversion of comics (CBR/CBZ files)" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf.py:49 +msgid "Set options for converting %s" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:89 +msgid "&Title:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:90 +msgid "&Author(s):" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:91 +msgid "&Number of Colors:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:92 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:440 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:538 +msgid "&Profile:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:93 +msgid "Disable &normalize" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:94 +msgid "Keep &aspect ratio" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:95 +msgid "Disable &Sharpening" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:96 +msgid "&Landscape" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:97 +msgid "Don't so&rt" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:98 +msgid "&Right to left" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:99 +msgid "De&speckle" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:100 +msgid "&Wide" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:100 +msgid " plugins" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:106 +msgid "by" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:123 +msgid "Advanced" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:123 +msgid "General" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:123 +msgid "Interface" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:124 +msgid "" +"Content\n" +"Server" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:124 +msgid "Plugins" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:257 +msgid "No valid plugin path" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:258 +msgid "%s is not a valid plugin path" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:261 +msgid "Choose plugin" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:272 +msgid "Plugin cannot be disabled" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:273 +msgid "The plugin: %s cannot be disabled" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:283 +msgid "Plugin not customizable" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:284 +msgid "Plugin: %s does not need customization" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:287 +msgid "Customize %s" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:297 +msgid "Cannot remove builtin plugin" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:298 +msgid " cannot be removed. It is a builtin plugin. Try disabling it instead." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:319 +msgid "Error log:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:323 +msgid "Access log:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:345 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:385 +msgid "Failed to start content server" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:385 +msgid "Invalid size" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:385 +msgid "The size %s is invalid. must be of the form widthxheight" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:423 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:427 +msgid "Invalid database location" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:424 +msgid "
Must be a directory." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:424 +msgid "Invalid database location " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:428 +msgid "Invalid database location.
Cannot write to " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:440 +msgid "Compacting database. This may take a while." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:440 +msgid "Compacting..." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:417 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:343 +msgid "Configuration" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:418 +msgid "" +"&Location of ebooks (The ebooks are stored in folders sorted by author and " +"metadata is stored in the file metadata.db)" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:419 +msgid "Browse for the new database location" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:420 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:435 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:437 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:452 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:453 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:478 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:414 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:497 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:308 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:313 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:327 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:336 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:338 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:340 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:344 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:346 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_editor_ui.py:126 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_editor_ui.py:128 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_editor_ui.py:131 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_editor_ui.py:135 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:267 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:269 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:270 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:332 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:336 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:342 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:344 +msgid "..." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:421 +msgid "Show notification when &new version is available" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:422 +msgid "" +"If you disable this setting, metadata is guessed from the filename instead. " +"This can be configured in the Advanced section." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:423 +msgid "Read &metadata from files" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:424 +msgid "Format for &single file save:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:425 +msgid "Default network &timeout:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:426 +msgid "" +"Set the default timeout for network fetches (i.e. anytime we go out to the " +"internet to get information)" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:427 +msgid " seconds" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:428 +msgid "Choose &language (requires restart):" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:429 +msgid "Normal" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:430 +msgid "High" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:431 +msgid "Low" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:432 +msgid "Job &priority:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:434 +msgid "Add a directory to the frequently used directories list" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:436 +msgid "Remove a directory from the frequently used directories list" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:438 +msgid "Use &Roman numerals for series number" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:439 +msgid "Enable system &tray icon (needs restart)" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:440 +msgid "Show ¬ifications in system tray" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:441 +msgid "Show cover &browser in a separate window (needs restart)" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:442 +msgid "Automatically send downloaded &news to ebook reader" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:443 +msgid "&Delete news from library when it is sent to reader" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:444 +msgid "&Number of covers to show in browse mode (needs restart):" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:445 +msgid "Toolbar" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:446 +msgid "Large" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:447 +msgid "Medium" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:448 +msgid "Small" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:449 +msgid "&Button size in toolbar" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:450 +msgid "Show &text in toolbar buttons" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:451 +msgid "Select visible &columns in library view" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:454 +msgid "Use internal &viewer for:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:455 +msgid "Free unused diskspace from the database" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:456 +msgid "&Compact database" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:457 +msgid "&Metadata from file name" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:458 +msgid "" +"calibre contains a network server that allows you to access your book " +"collection using a browser from anywhere in the world. Any changes to the " +"settings will only take effect after a server restart." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:459 +msgid "Server &port:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:460 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:57 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:174 +msgid "&Username:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:461 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:58 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:175 +msgid "&Password:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:462 +msgid "" +"If you leave the password blank, anyone will be able to access your book " +"collection using the web interface." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:463 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:59 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:176 +msgid "&Show password" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:464 +msgid "" +"The maximum size (widthxheight) for displayed covers. Larger covers are " +"resized. " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:465 +msgid "Max. &cover size:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:466 +msgid "&Start Server" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:467 +msgid "St&op Server" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:468 +msgid "&Test Server" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:469 +msgid "Run server &automatically on startup" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:470 +msgid "View &server logs" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:471 +msgid "" +"If you want to use the content server to access your ebook collection on " +"your iphone with Stanza, you will need to add the URL " +"http://myhostname:8080/stanza as a new catalog in the stanza reader on your " +"iphone. Here myhostname should be the fully qualified hostname or the IP " +"address of this computer." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:472 +msgid "" +"Here you can customize the behavior of Calibre by controlling what plugins " +"it uses." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:473 +msgid "Enable/&Disable plugin" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:474 +msgid "&Customize plugin" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:475 +msgid "&Remove plugin" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:476 +msgid "Add new plugin" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:477 +msgid "Plugin &file:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:479 +msgid "&Add" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/confirm_delete_ui.py:48 +msgid "Are you sure?" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/confirm_delete_ui.py:50 +msgid "&Show this warning again" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/conversion_error_ui.py:41 +msgid "ERROR" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:52 +msgid "Bulk convert to " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:54 +msgid "Convert %s to " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:68 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:95 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:55 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:296 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:143 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:161 +msgid "Metadata" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:70 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:96 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:57 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:297 +msgid "Look & Feel" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:72 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:97 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:59 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:298 +msgid "Page Setup" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:74 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:98 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:61 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:299 +msgid "Chapter Detection" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:95 +msgid "" +"Specify metadata such as title and author for the book.\n" +"\n" +"Metadata will be updated in the database as well as the generated %s file." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:96 +msgid "" +"Adjust the look of the generated ebook by specifying things like font sizes." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:97 +msgid "Specify the page layout settings like margins." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:98 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:299 +msgid "Fine tune the detection of chapter and section headings." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:104 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:167 +msgid "Choose cover for " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:111 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:174 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:59 +msgid "Cannot read" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:112 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:175 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:60 +msgid "You do not have permission to read the file: " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:120 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:183 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:68 +msgid "Error reading file" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:121 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:184 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:69 +msgid "

There was an error reading from file:
" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:127 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:190 +msgid " is not a valid picture" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:238 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1053 +msgid "Cannot convert" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:239 +msgid "This book has no available formats" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:244 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:93 +msgid "No available formats" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:245 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:94 +msgid "Cannot convert %s as this book has no supported formats" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:249 +msgid "Choose the format to convert to " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:260 +msgid "Invalid XPath expression" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub.py:261 +msgid "The expression %s is invalid. Error: %s" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:410 +msgid "Convert to EPUB" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:411 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:494 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:341 +msgid "Book Cover" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:412 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:495 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:342 +msgid "Change &cover image:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:413 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:496 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:343 +msgid "Browse for an image to use as the cover of this book." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:415 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:498 +msgid "Use cover from &source file" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:416 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:499 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:305 +msgid "&Title: " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:417 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:500 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:306 +msgid "Change the title of this book" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:418 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:501 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:128 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:309 +msgid "&Author(s): " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:419 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:502 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:129 +msgid "" +"Change the author(s) of this book. Multiple authors should be separated by " +"an &. If the author name contains an &, use && to represent it." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:420 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:503 +msgid "Author So&rt:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:421 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:504 +msgid "" +"Change the author(s) of this book. Multiple authors should be separated by a " +"comma" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:422 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:505 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:136 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:318 +msgid "&Publisher: " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:423 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:506 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:137 +msgid "Change the publisher of this book" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:424 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:507 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:319 +msgid "Ta&gs: " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:425 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:508 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:139 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:320 +msgid "" +"Tags categorize the book. This is particularly useful while searching. " +"

They can be any words or phrases, separated by commas." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:426 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:509 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:144 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:323 +msgid "&Series:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:427 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:428 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:510 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:511 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:145 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:146 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:324 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:325 +msgid "List of known series. You can add new series." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:429 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:430 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:512 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:513 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:328 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:329 +msgid "Series index." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:431 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:514 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:330 +msgid "Book " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:433 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:522 +msgid "Source en&coding:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:434 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:516 +msgid "Base &font size:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:435 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:442 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:444 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:446 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:448 +msgid " pt" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:436 +msgid "Remove &spacing between paragraphs" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:437 +msgid "Preserve &tag structure when splitting" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:438 +msgid "&Rescale images" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:439 +msgid "Override &CSS" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:441 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:539 +msgid "&Left Margin:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:443 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:541 +msgid "&Right Margin:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:445 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:543 +msgid "&Top Margin:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:447 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:545 +msgid "&Bottom Margin:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:449 +msgid "Do not &split on page breaks" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:450 +msgid "&Source profile:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:451 +msgid "&Destination profile:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:452 +msgid "Automatic &chapter detection" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:453 +msgid "&XPath:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:454 +msgid "" +"\n" +"\n" +"

You can control how " +"calibre detects chapters using a XPath expression. To learn how to use XPath " +"expressions see the XPath " +"tutorial

" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:459 +msgid "Chapter &mark:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:460 +msgid "Automatic &Table of Contents" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:461 +msgid "Number of &links to add to Table of Contents" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:462 +msgid "Do not add &detected chapters to the Table of Contents" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:463 +msgid "Chapter &threshold" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:464 +msgid "&Force use of auto-generated Table of Contents" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:465 +msgid "Level &1 TOC" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:466 +msgid "Level &2 TOC" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/epub_ui.py:467 +msgid "&Title for generated TOC" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:38 +msgid "Author Sort" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:40 +msgid "ISBN" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:104 +msgid "Cannot connect" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:105 +msgid "You must specify a valid access key for isbndb.com" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:139 +msgid "Error fetching metadata" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:144 +msgid "No metadata found" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:144 +msgid "" +"No metadata found, try adjusting the title and author or the ISBN key." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata_ui.py:77 +msgid "Fetch metadata" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata_ui.py:78 +msgid "Fetching metadata for %1" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata_ui.py:79 +msgid "" +"Sign up for a free account from ISBNdb.com to get an access key." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata_ui.py:80 +msgid "&Access Key:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata_ui.py:81 +msgid "Fetch" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata_ui.py:82 +msgid "Matches" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata_ui.py:83 +msgid "" +"Select the book that most closely matches your copy from the list below" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/job_view_ui.py:32 +msgid "Details of job" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/jobs.py:27 +msgid "Unavailable" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/jobs.py:38 +msgid " - Jobs" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/jobs_ui.py:38 +msgid "Active Jobs" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/jobs_ui.py:39 +msgid "&Stop selected job" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:98 +msgid "Choose the format to convert into LRF" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:106 +msgid "Convert %s to LRF" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:109 +#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:360 +msgid "Set conversion defaults" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:260 +msgid "" +"Preprocess the file before converting to LRF. This is useful if you know " +"that the file is from a specific source. Known sources:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:261 +msgid "
  1. baen - Books from BAEN Publishers
  2. " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:262 +msgid "" +"
  3. pdftohtml - HTML files that are the output of the program " +"pdftohtml
  4. " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:263 +msgid "
  5. book-designer - HTML0 files from Book Designer
  6. " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:296 +msgid "" +"Specify metadata such as title and author for the book.

    Metadata will be " +"updated in the database as well as the generated LRF file." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:297 +msgid "" +"Adjust the look of the generated LRF file by specifying things like font " +"sizes and the spacing between words." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:298 +msgid "" +"Specify the page settings like margins and the screen size of the target " +"device." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:308 +msgid "No help available" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single.py:411 +msgid "Bulk convert ebooks to LRF" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:492 +msgid "Convert to LRF" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:517 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:524 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:526 +msgid " pts" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:518 +msgid "Embedded Fonts" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:519 +msgid "&Serif:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:520 +msgid "S&ans-serif:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:521 +msgid "&Monospace:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:523 +msgid "Minimum &indent:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:525 +msgid "&Word spacing:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:527 +msgid "Enable auto &rotation of images" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:528 +msgid "Insert &blank lines between paragraphs" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:529 +msgid "Ignore &tables" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:530 +msgid "Ignore &colors" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:531 +msgid "&Preprocess:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:532 +msgid "Header" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:533 +msgid "&Show header" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:534 +msgid "&Header format:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:535 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:540 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:542 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:544 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:546 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/config_ui.py:107 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/config_ui.py:109 +msgid " px" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:536 +msgid "Header &separation:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:537 +msgid "Override
    CSS" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:547 +msgid "&Convert tables to images (good for large/complex tables)" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:548 +msgid "&Multiplier for text size in rendered tables:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:549 +msgid "Title based detection" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:550 +msgid "&Disable chapter detection" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:551 +msgid "&Regular expression:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:552 +msgid "Add &chapters to table of contents" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:553 +msgid "Don't add &links to the table of contents" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:554 +msgid "Tag based detection" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:555 +msgid "&Page break before tag:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:556 +msgid "&Force page break before tag:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:557 +msgid "Force page break before &attribute:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:558 +msgid "Detect chapter &at tag:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/lrf_single_ui.py:559 +msgid "" +"\n" +"\n" +"

    " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:126 +msgid "Edit Meta information" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:127 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:304 +msgid "Meta information" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:130 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:310 +msgid "Author S&ort: " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:131 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:311 +msgid "" +"Specify how the author(s) of this book should be sorted. For example Charles " +"Dickens should be sorted as Dickens, Charles." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:132 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:314 +msgid "&Rating:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:133 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:134 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:315 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:316 +msgid "Rating of this book. 0-5 stars" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:135 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:317 +msgid " stars" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:138 +msgid "Add Ta&gs: " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:140 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:141 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:321 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:322 +msgid "Open Tag Editor" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:142 +msgid "&Remove tags:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:143 +msgid "Comma separated list of tags to remove from the books. " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:147 +msgid "Remove &format:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:148 +msgid "A&utomatically set author sort" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:124 +msgid "Could not read metadata" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:125 +msgid "Could not read metadata from %s format" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:133 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:139 +msgid "Could not read cover" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:134 +msgid "Could not read cover from %s format" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:140 +msgid "The cover in the %s format is invalid" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:319 +msgid "" +"

    Enter your username and password for LibraryThing.com.
    If you " +"do not have one, you can register " +"for free!.

    " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:349 +msgid "Could not fetch cover.
    " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:349 +msgid "Could not fetch cover" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:355 +msgid "Cannot fetch cover" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:355 +msgid "You must specify the ISBN identifier for this book." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:303 +msgid "Edit Meta Information" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:307 +msgid "Swap the author and title" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:312 +msgid "" +"Automatically create the author sort entry based on the current author entry" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:326 +msgid "Remove unused series (Series that have no books)" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:331 +msgid "IS&BN:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:333 +msgid "Fetch metadata from server" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:334 +msgid "Available Formats" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:335 +msgid "Add a new format for this book to the database" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:337 +msgid "Remove the selected formats for this book from the database." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:339 +msgid "Set the cover for the book from the selected format" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:345 +msgid "Reset cover to default" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:347 +msgid "Fetch cover image from server" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:348 +msgid "" +"Change the username and/or password for your account at LibraryThing.com" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:349 +msgid "Change password" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:55 +msgid "Password needed" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/progress.py:42 +msgid "Aborting..." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:39 +msgid "You" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:184 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:218 +msgid "Search" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:261 +msgid "%d recipes" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:262 +msgid "Monday" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:262 +msgid "Tuesday" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:262 +msgid "Wednesday" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:262 +msgid "day" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:263 +msgid "Friday" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:263 +msgid "Saturday" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:263 +msgid "Sunday" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:263 +msgid "Thursday" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:292 +msgid "Must set account information" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:292 +msgid "This recipe requires a username and password" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:317 +msgid "Created by: " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:355 +msgid "Last downloaded: %s days ago" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:357 +msgid "Last downloaded: never" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:384 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:161 +msgid "Schedule news download" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:387 +msgid "Add a custom news source" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:394 +#: /home/kovid/work/calibre/src/calibre/gui2/tags.py:50 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:772 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:776 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1083 +msgid "News" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:162 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:221 +msgid "Recipes" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:163 +msgid "Schedule for download" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:164 +msgid "title" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:165 +msgid "description" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:166 +msgid "author" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:167 +msgid "&Schedule for download:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:168 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:170 +msgid "Every " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:169 +msgid "at" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:171 +msgid "" +"Interval at which to download this recipe. A value of zero means that the " +"recipe will be downloaded every hour." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:172 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:180 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:263 +msgid " days" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:173 +msgid "&Account" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:177 +msgid "For the scheduling to work, you must leave calibre running." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:178 +msgid "&Download now" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:179 +msgid "" +"Delete downloaded news older than the specified number of days. Set to zero " +"to disable." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:181 +msgid "Delete downloaded news older than " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_item_ui.py:35 +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:96 +msgid "Form" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_item_ui.py:36 +msgid "contains" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_item_ui.py:37 +msgid "The text to search for. It is interpreted as a regular expression." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_item_ui.py:38 +msgid "" +"

    Negate this match. That is, only return results that do not match " +"this query." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_item_ui.py:39 +msgid "Negate" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:87 +msgid "Advanced Search" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:88 +msgid "Find entries that have..." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:89 +msgid "&All these words:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:90 +msgid "This exact &phrase:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:91 +msgid "&One or more of these words:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:92 +msgid "But dont show entries that have..." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:93 +msgid "Any of these &unwanted words:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:94 +msgid "" +"See the User Manual for more help" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_editor_ui.py:123 +msgid "Tag Editor" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_editor_ui.py:124 +msgid "A&vailable tags" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_editor_ui.py:125 +msgid "" +"Delete tag from database. This will unapply the tag from all books and then " +"remove it from the database." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_editor_ui.py:127 +msgid "Apply tag to current book" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_editor_ui.py:129 +msgid "A&pplied tags" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_editor_ui.py:130 +msgid "Unapply (remove) tag from current book" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_editor_ui.py:132 +msgid "&Add tag:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_editor_ui.py:133 +msgid "" +"If the tag you want is not in the available list, you can add it here. " +"Accepts a comma separated list of tags." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_editor_ui.py:134 +msgid "Add tag to available tags and apply it to current book" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:62 +msgid "No recipe selected" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:68 +msgid "The attached file: %s is a recipe to download %s." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:69 +msgid "Recipe for " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:85 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:96 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:255 +msgid "Switch to Advanced mode" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:91 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:99 +msgid "Switch to Basic mode" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:109 +msgid "Feed must have a title" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:110 +msgid "The feed must have a title" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:114 +msgid "Feed must have a URL" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:115 +msgid "The feed %s must have a URL" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:120 +msgid "Already exists" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:121 +msgid "This feed has already been added to the recipe" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:162 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:171 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:228 +msgid "Invalid input" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:163 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:172 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:229 +msgid "

    Could not create recipe. Error:
    %s" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:178 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:210 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:234 +msgid "Replace recipe?" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:179 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:211 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:235 +msgid "A custom recipe named %s already exists. Do you want to replace it?" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:201 +msgid "Pick recipe" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:201 +msgid "Pick the recipe to customize" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:221 +msgid "Choose a recipe file" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:248 +msgid "Add custom news source" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:249 +msgid "Available user recipes" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:250 +msgid "Add/Update &recipe" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:251 +msgid "&Remove recipe" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:252 +msgid "&Share recipe" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:253 +msgid "Customize &builtin recipe" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:254 +msgid "&Load recipe from file" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:256 +msgid "" +"\n" +"

    Create a basic news " +"recipe, by adding RSS feeds to it.
    For most feeds, you will have to " +"use the \"Advanced mode\" to further customize the fetch " +"process.

    " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:260 +msgid "Recipe &title:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:261 +msgid "&Oldest article:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:262 +msgid "The oldest article to download" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:264 +msgid "&Max. number of articles per feed:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:265 +msgid "Maximum number of articles to download per feed." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:266 +msgid "Feeds in recipe" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:268 +msgid "Remove feed from recipe" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:271 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:274 +msgid "Add feed to recipe" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:272 +msgid "&Feed title:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:273 +msgid "Feed &URL:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:275 +msgid "&Add feed" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:276 +msgid "" +"For help with writing advanced news recipes, please visit User Recipes" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:277 +msgid "Recipe source code (python)" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:97 +msgid "" +"\n" +"\n" +"

    Set a regular expression " +"pattern to use when trying to guess ebook metadata from filenames.

    \n" +"

    A reference on the syntax " +"of regular expressions is available.

    \n" +"

    Use the Test functionality below to test your regular " +"expression on a few sample filenames. The group names for the various " +"metadata entries are documented in tooltips.

    " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:104 +msgid "Regular &expression" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:105 +msgid "&Test" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:106 +msgid "File &name:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:107 +msgid "Test" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:108 +msgid "Title:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:109 +msgid "Regular expression (?P<title>)" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:110 +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:113 +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:116 +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:119 +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:122 +#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:54 +#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:58 +#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:63 +#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:68 +#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:70 +msgid "No match" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:111 +msgid "Authors:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:112 +msgid "Regular expression (?P)" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:114 +msgid "Series:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:115 +msgid "Regular expression (?P)" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:117 +msgid "Series index:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:118 +msgid "Regular expression (?P)" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:120 +msgid "ISBN:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:121 +msgid "Regular expression (?P)" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/jobs2.py:46 +msgid "Job" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/jobs2.py:47 +msgid "Status" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/jobs2.py:48 +msgid "Progress" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/jobs2.py:49 +msgid "Running time" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/jobs2.py:65 +msgid "Unknown job" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/jobs2.py:70 +msgid "Finished" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/jobs2.py:72 +msgid "Error" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/jobs2.py:74 +msgid "Waiting" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/jobs2.py:76 +msgid "Working" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/jobs2.py:172 +#: /home/kovid/work/calibre/src/calibre/gui2/jobs2.py:176 +msgid "Cannot kill job" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/jobs2.py:173 +msgid "Cannot kill jobs that communicate with the device" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/jobs2.py:177 +msgid "Job has already run" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:105 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:971 +msgid "Size (MB)" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:106 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:972 +msgid "Date" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:107 +msgid "Rating" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:308 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:314 +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:319 +msgid "None" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:325 +msgid "Book %s of %s." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:739 +msgid "Not allowed" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:740 +msgid "" +"Dropping onto a device is not supported. First add the book to the calibre " +"library." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:903 +msgid "Format" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:908 +msgid "Timestamp" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/library.py:1006 +msgid "Search (For Advanced Search click the button to the left)" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/lrf_renderer/config_ui.py:47 +msgid "Configure Viewer" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/lrf_renderer/config_ui.py:48 +msgid "Use white background" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/lrf_renderer/config_ui.py:49 +msgid "Hyphenate" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/lrf_renderer/config_ui.py:50 +msgid "Changes will only take effect after a restart." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/lrf_renderer/main.py:64 +msgid " - LRF Viewer" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/lrf_renderer/main.py:157 +msgid "No matches for the search phrase %s were found." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/lrf_renderer/main.py:157 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:378 +msgid "No matches found" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/lrf_renderer/main_ui.py:128 +msgid "LRF Viewer" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/lrf_renderer/main_ui.py:129 +msgid "Parsing LRF file" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/lrf_renderer/main_ui.py:130 +msgid "LRF Viewer toolbar" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/lrf_renderer/main_ui.py:131 +msgid "Next Page" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/lrf_renderer/main_ui.py:132 +msgid "Previous Page" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/lrf_renderer/main_ui.py:133 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:154 +msgid "Back" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/lrf_renderer/main_ui.py:134 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:155 +msgid "Forward" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/lrf_renderer/main_ui.py:135 +msgid "Next match" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/lrf_renderer/main_ui.py:136 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:162 +msgid "Open ebook" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/lrf_renderer/main_ui.py:137 +msgid "Configure" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:90 +msgid "Error communicating with device" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:102 +msgid "&Restore" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:103 +msgid "&Donate to support calibre" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:106 +msgid "&Restart" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:142 +msgid "" +"

    For help visit %s.kovidgoyal.net
    " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:143 +msgid "%s: %s by Kovid Goyal %%(version)s
    %%(device)s

    " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:161 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:163 +msgid "Send to main memory" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:162 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:164 +msgid "Send to storage card" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:163 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:164 +msgid "and delete from library" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:166 +msgid "Send to storage card by default" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:179 +msgid "Edit metadata individually" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:181 +msgid "Edit metadata in bulk" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:184 +msgid "Add books from a single directory" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:185 +msgid "" +"Add books from directories, including sub-directories (One book per " +"directory, assumes every ebook file is the same book in a different format)" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:186 +msgid "" +"Add books from directories, including sub directories (Multiple books per " +"directory, assumes every ebook file is a different book)" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:201 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:356 +msgid "Save to disk" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:202 +msgid "Save to disk in a single directory" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:203 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1258 +msgid "Save only %s format to disk" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:206 +#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:362 +msgid "View" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:207 +msgid "View specific format" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:224 +msgid "Convert individually" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:225 +msgid "Bulk convert" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:227 +msgid "Set defaults for conversion" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:228 +msgid "Set defaults for conversion of comics" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:250 +msgid "Similar books..." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:298 +msgid "Bad database location" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:300 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1391 +msgid "Choose a location for your ebook library." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:424 +msgid "Browse by covers" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:513 +msgid "Device: " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:514 +msgid " detected." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:536 +msgid "Connected " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:547 +msgid "Device database corrupted" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:548 +msgid "" +"\n" +"

    The database of books on the reader is corrupted. Try the " +"following:\n" +"

      \n" +"
    1. Unplug the reader. Wait for it to finish regenerating " +"the database (i.e. wait till it is ready to be used). Plug it back in. Now " +"it should work with %(app)s. If not try the next step.
    2. \n" +"
    3. Quit %(app)s. Find the file media.xml in the reader's " +"main memory. Delete it. Unplug the reader. Wait for it to regenerate the " +"file. Re-connect it and start %(app)s.
    4. \n" +"
    \n" +" " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:597 +msgid "Adding books recursively..." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:602 +msgid "Added " +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:602 +msgid "Searching..." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:612 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:716 +msgid "" +"

    Books with the same title as the following already exist in the database. " +"Add them anyway?