1
0
mirror of https://github.com/gryf/ebook-converter.git synced 2026-04-21 13:41:30 +02:00

Removed gettext related functions

This commit is contained in:
2020-05-03 19:00:20 +02:00
parent 35445cb736
commit 212cb56d42
92 changed files with 1505 additions and 1605 deletions
+15 -15
View File
@@ -55,10 +55,10 @@ class Plugin(object):
version = (1, 0, 0)
#: A short string describing what this plugin does
description = _('Does absolutely nothing')
description = 'Does absolutely nothing'
#: The author of this plugin
author = _('Unknown')
author = 'Unknown'
#: When more than one plugin exists for a filetype,
#: the plugins are run in order of decreasing priority.
@@ -76,7 +76,7 @@ class Plugin(object):
#: The type of this plugin. Used for categorizing plugins in the
#: GUI
type = _('Base')
type = 'Base'
def __init__(self, plugin_path):
self.plugin_path = plugin_path
@@ -264,7 +264,7 @@ class FileTypePlugin(Plugin):
#: on the final file produced by the conversion output plugin.
on_postprocess = False
type = _('File type')
type = 'File type'
def run(self, path_to_ebook):
"""
@@ -335,7 +335,7 @@ class MetadataReaderPlugin(Plugin):
version = numeric_version
author = 'Kovid Goyal'
type = _('Metadata reader')
type = 'Metadata reader'
def __init__(self, *args, **kwargs):
Plugin.__init__(self, *args, **kwargs)
@@ -367,7 +367,7 @@ class MetadataWriterPlugin(Plugin):
version = numeric_version
author = 'Kovid Goyal'
type = _('Metadata writer')
type = 'Metadata writer'
def __init__(self, *args, **kwargs):
Plugin.__init__(self, *args, **kwargs)
@@ -398,7 +398,7 @@ class CatalogPlugin(Plugin):
#: For example: 'epub' or 'xml'
file_types = set()
type = _('Catalog generator')
type = 'Catalog generator'
#: CLI parser options specific to this plugin, declared as namedtuple
#: Option:
@@ -406,8 +406,8 @@ class CatalogPlugin(Plugin):
#: from collections import namedtuple
#: Option = namedtuple('Option', 'option, default, dest, help')
#: cli_options = [Option('--catalog-title', default = 'My Catalog',
#: dest = 'catalog_title', help = (_('Title of generated catalog. '
#: '\nDefault:') +
#: dest = 'catalog_title', help = ('Title of generated catalog. '
#: '\nDefault:' +
#: " '" + '%default' + "'"))]
#: cli_options parsed in
#: ebook_converter.db.cli.cmd_catalog:option_parser()
@@ -511,7 +511,7 @@ class InterfaceActionBase(Plugin):
supported_platforms = ['windows', 'osx', 'linux']
author = 'Kovid Goyal'
type = _('User interface action')
type = 'User interface action'
can_be_disabled = False
actual_plugin = None
@@ -544,7 +544,7 @@ class PreferencesPlugin(Plugin):
supported_platforms = ['windows', 'osx', 'linux']
author = 'Kovid Goyal'
type = _('Preferences')
type = 'Preferences'
can_be_disabled = False
#: Import path to module that contains a class named ConfigWidget
@@ -596,11 +596,11 @@ class StoreBase(Plugin):
supported_platforms = ['windows', 'osx', 'linux']
author = 'John Schember'
type = _('Store')
type = 'Store'
# Information about the store. Should be in the primary language
# of the store. This should not be translatable when set by
# a subclass.
description = _('An e-book store.')
description = 'An e-book store.'
minimum_calibre_version = (0, 8, 0)
version = (1, 0, 1)
@@ -643,7 +643,7 @@ class StoreBase(Plugin):
class EditBookToolPlugin(Plugin):
type = _('Edit book tool')
type = 'Edit book tool'
minimum_calibre_version = (1, 46, 0)
@@ -653,7 +653,7 @@ class LibraryClosedPlugin(Plugin):
when the library is changed, or when a library is used in some other way.
At the moment these plugins won't be called by the CLI functions.
"""
type = _('Library closed')
type = 'Library closed'
# minimum version 2.54 because that is when support was added
minimum_calibre_version = (2, 54, 0)
+88 -82
View File
@@ -19,7 +19,7 @@ plugins = []
class PML2PMLZ(FileTypePlugin):
name = 'PML to PMLZ'
author = 'John Schember'
description = _('Create a PMLZ archive containing the PML file '
description = ('Create a PMLZ archive containing the PML file '
'and all images in the directory pmlname_img or images. '
'This plugin is run every time you add '
'a PML file to the library.')
@@ -50,9 +50,10 @@ class PML2PMLZ(FileTypePlugin):
class TXT2TXTZ(FileTypePlugin):
name = 'TXT to TXTZ'
author = 'John Schember'
description = _('Create a TXTZ archive when a TXT file is imported '
'containing Markdown or Textile references to images. The referenced '
'images as well as the TXT file are added to the archive.')
description = ('Create a TXTZ archive when a TXT file is imported '
'containing Markdown or Textile references to images. The '
'referenced images as well as the TXT file are added to '
'the archive.')
version = numeric_version
file_types = {'txt', 'text'}
supported_platforms = ['windows', 'osx', 'linux']
@@ -133,7 +134,7 @@ class ComicMetadataReader(MetadataReaderPlugin):
name = 'Read comic metadata'
file_types = {'cbr', 'cbz'}
description = _('Extract cover from comic files')
description = 'Extract cover from comic files'
def customization_help(self, gui=False):
return 'Read series number from volume or issue number. Default is volume, set this to issue to use issue number instead.'
@@ -174,7 +175,7 @@ class CHMMetadataReader(MetadataReaderPlugin):
name = 'Read CHM metadata'
file_types = {'chm'}
description = _('Read metadata from %s files') % 'CHM'
description = 'Read metadata from CHM files'
def get_metadata(self, stream, ftype):
from ebook_converter.ebooks.chm.metadata import get_metadata
@@ -185,7 +186,7 @@ class EPUBMetadataReader(MetadataReaderPlugin):
name = 'Read EPUB metadata'
file_types = {'epub'}
description = _('Read metadata from %s files')%'EPUB'
description = 'Read metadata from EPUB files'
def get_metadata(self, stream, ftype):
from ebook_converter.ebooks.metadata.epub import get_metadata, get_quick_metadata
@@ -198,7 +199,7 @@ class FB2MetadataReader(MetadataReaderPlugin):
name = 'Read FB2 metadata'
file_types = {'fb2', 'fbz'}
description = _('Read metadata from %s files')%'FB2'
description = 'Read metadata from FB2 files'
def get_metadata(self, stream, ftype):
from ebook_converter.ebooks.metadata.fb2 import get_metadata
@@ -209,7 +210,7 @@ class HTMLMetadataReader(MetadataReaderPlugin):
name = 'Read HTML metadata'
file_types = {'html'}
description = _('Read metadata from %s files')%'HTML'
description = 'Read metadata from HTML files'
def get_metadata(self, stream, ftype):
from ebook_converter.ebooks.metadata.html import get_metadata
@@ -220,7 +221,7 @@ class HTMLZMetadataReader(MetadataReaderPlugin):
name = 'Read HTMLZ metadata'
file_types = {'htmlz'}
description = _('Read metadata from %s files') % 'HTMLZ'
description = 'Read metadata from HTMLZ files'
author = 'John Schember'
def get_metadata(self, stream, ftype):
@@ -232,7 +233,7 @@ class IMPMetadataReader(MetadataReaderPlugin):
name = 'Read IMP metadata'
file_types = {'imp'}
description = _('Read metadata from %s files')%'IMP'
description = 'Read metadata from IMP files'
author = 'Ashish Kulkarni'
def get_metadata(self, stream, ftype):
@@ -244,7 +245,7 @@ class LITMetadataReader(MetadataReaderPlugin):
name = 'Read LIT metadata'
file_types = {'lit'}
description = _('Read metadata from %s files')%'LIT'
description = 'Read metadata from LIT files'
def get_metadata(self, stream, ftype):
from ebook_converter.ebooks.metadata.lit import get_metadata
@@ -255,7 +256,7 @@ class LRFMetadataReader(MetadataReaderPlugin):
name = 'Read LRF metadata'
file_types = {'lrf'}
description = _('Read metadata from %s files')%'LRF'
description = 'Read metadata from LRF files'
def get_metadata(self, stream, ftype):
from ebook_converter.ebooks.lrf.meta import get_metadata
@@ -266,7 +267,7 @@ class LRXMetadataReader(MetadataReaderPlugin):
name = 'Read LRX metadata'
file_types = {'lrx'}
description = _('Read metadata from %s files')%'LRX'
description = 'Read metadata from LRX files'
def get_metadata(self, stream, ftype):
from ebook_converter.ebooks.metadata.lrx import get_metadata
@@ -277,7 +278,7 @@ class MOBIMetadataReader(MetadataReaderPlugin):
name = 'Read MOBI metadata'
file_types = {'mobi', 'prc', 'azw', 'azw3', 'azw4', 'pobi'}
description = _('Read metadata from %s files')%'MOBI'
description = 'Read metadata from MOBI files'
def get_metadata(self, stream, ftype):
from ebook_converter.ebooks.metadata.mobi import get_metadata
@@ -288,7 +289,7 @@ class ODTMetadataReader(MetadataReaderPlugin):
name = 'Read ODT metadata'
file_types = {'odt'}
description = _('Read metadata from %s files')%'ODT'
description = 'Read metadata from ODT files'
def get_metadata(self, stream, ftype):
from ebook_converter.ebooks.metadata.odt import get_metadata
@@ -299,7 +300,7 @@ class DocXMetadataReader(MetadataReaderPlugin):
name = 'Read DOCX metadata'
file_types = {'docx'}
description = _('Read metadata from %s files')%'DOCX'
description = 'Read metadata from DOCX files'
def get_metadata(self, stream, ftype):
from ebook_converter.ebooks.metadata.docx import get_metadata
@@ -310,7 +311,7 @@ class OPFMetadataReader(MetadataReaderPlugin):
name = 'Read OPF metadata'
file_types = {'opf'}
description = _('Read metadata from %s files')%'OPF'
description = 'Read metadata from OPF files'
def get_metadata(self, stream, ftype):
from ebook_converter.ebooks.metadata.opf import get_metadata
@@ -321,7 +322,7 @@ class PDBMetadataReader(MetadataReaderPlugin):
name = 'Read PDB metadata'
file_types = {'pdb', 'updb'}
description = _('Read metadata from %s files') % 'PDB'
description = 'Read metadata from PDB files'
author = 'John Schember'
def get_metadata(self, stream, ftype):
@@ -333,7 +334,7 @@ class PDFMetadataReader(MetadataReaderPlugin):
name = 'Read PDF metadata'
file_types = {'pdf'}
description = _('Read metadata from %s files')%'PDF'
description = 'Read metadata from PDF files'
def get_metadata(self, stream, ftype):
from ebook_converter.ebooks.metadata.pdf import get_metadata, get_quick_metadata
@@ -346,7 +347,7 @@ class PMLMetadataReader(MetadataReaderPlugin):
name = 'Read PML metadata'
file_types = {'pml', 'pmlz'}
description = _('Read metadata from %s files') % 'PML'
description = 'Read metadata from PML files'
author = 'John Schember'
def get_metadata(self, stream, ftype):
@@ -358,7 +359,7 @@ class RARMetadataReader(MetadataReaderPlugin):
name = 'Read RAR metadata'
file_types = {'rar'}
description = _('Read metadata from e-books in RAR archives')
description = 'Read metadata from e-books in RAR archives'
def get_metadata(self, stream, ftype):
from ebook_converter.ebooks.metadata.rar import get_metadata
@@ -369,7 +370,7 @@ class RBMetadataReader(MetadataReaderPlugin):
name = 'Read RB metadata'
file_types = {'rb'}
description = _('Read metadata from %s files')%'RB'
description = 'Read metadata from RB files'
author = 'Ashish Kulkarni'
def get_metadata(self, stream, ftype):
@@ -381,7 +382,7 @@ class RTFMetadataReader(MetadataReaderPlugin):
name = 'Read RTF metadata'
file_types = {'rtf'}
description = _('Read metadata from %s files')%'RTF'
description = 'Read metadata from RTF files'
def get_metadata(self, stream, ftype):
from ebook_converter.ebooks.metadata.rtf import get_metadata
@@ -392,7 +393,7 @@ class SNBMetadataReader(MetadataReaderPlugin):
name = 'Read SNB metadata'
file_types = {'snb'}
description = _('Read metadata from %s files') % 'SNB'
description = 'Read metadata from SNB files'
author = 'Li Fanxi'
def get_metadata(self, stream, ftype):
@@ -404,7 +405,7 @@ class TOPAZMetadataReader(MetadataReaderPlugin):
name = 'Read Topaz metadata'
file_types = {'tpz', 'azw1'}
description = _('Read metadata from %s files')%'MOBI'
description = 'Read metadata from MOBI files'
def get_metadata(self, stream, ftype):
from ebook_converter.ebooks.metadata.topaz import get_metadata
@@ -415,7 +416,7 @@ class TXTMetadataReader(MetadataReaderPlugin):
name = 'Read TXT metadata'
file_types = {'txt'}
description = _('Read metadata from %s files') % 'TXT'
description = 'Read metadata from TXT files'
author = 'John Schember'
def get_metadata(self, stream, ftype):
@@ -427,7 +428,7 @@ class TXTZMetadataReader(MetadataReaderPlugin):
name = 'Read TXTZ metadata'
file_types = {'txtz'}
description = _('Read metadata from %s files') % 'TXTZ'
description = 'Read metadata from TXTZ files'
author = 'John Schember'
def get_metadata(self, stream, ftype):
@@ -439,7 +440,7 @@ class ZipMetadataReader(MetadataReaderPlugin):
name = 'Read ZIP metadata'
file_types = {'zip', 'oebzip'}
description = _('Read metadata from e-books in ZIP archives')
description = 'Read metadata from e-books in ZIP archives'
def get_metadata(self, stream, ftype):
from ebook_converter.ebooks.metadata.zip import get_metadata
@@ -458,7 +459,7 @@ class EPUBMetadataWriter(MetadataWriterPlugin):
name = 'Set EPUB metadata'
file_types = {'epub'}
description = _('Set metadata in %s files')%'EPUB'
description = 'Set metadata in EPUB files'
def set_metadata(self, stream, mi, type):
from ebook_converter.ebooks.metadata.epub import set_metadata
@@ -469,15 +470,16 @@ class EPUBMetadataWriter(MetadataWriterPlugin):
h = 'disable-add-missing-cover'
if gui:
h = '<i>' + h + '</i>'
return _('Enter {0} below to have the EPUB metadata writer plugin not'
' add cover images to EPUB files that have no existing cover image.').format(h)
return ('Enter {0} below to have the EPUB metadata writer plugin not '
'add cover images to EPUB files that have no existing cover '
'image.'.format(h))
class FB2MetadataWriter(MetadataWriterPlugin):
name = 'Set FB2 metadata'
file_types = {'fb2', 'fbz'}
description = _('Set metadata in %s files')%'FB2'
description = 'Set metadata in FB2 files'
def set_metadata(self, stream, mi, type):
from ebook_converter.ebooks.metadata.fb2 import set_metadata
@@ -488,7 +490,7 @@ class HTMLZMetadataWriter(MetadataWriterPlugin):
name = 'Set HTMLZ metadata'
file_types = {'htmlz'}
description = _('Set metadata from %s files') % 'HTMLZ'
description = 'Set metadata from HTMLZ files'
author = 'John Schember'
def set_metadata(self, stream, mi, type):
@@ -500,7 +502,7 @@ class LRFMetadataWriter(MetadataWriterPlugin):
name = 'Set LRF metadata'
file_types = {'lrf'}
description = _('Set metadata in %s files')%'LRF'
description = 'Set metadata in LRF files'
def set_metadata(self, stream, mi, type):
from ebook_converter.ebooks.lrf.meta import set_metadata
@@ -511,7 +513,7 @@ class MOBIMetadataWriter(MetadataWriterPlugin):
name = 'Set MOBI metadata'
file_types = {'mobi', 'prc', 'azw', 'azw3', 'azw4'}
description = _('Set metadata in %s files')%'MOBI'
description = 'Set metadata in MOBI files'
author = 'Marshall T. Vandegrift'
def set_metadata(self, stream, mi, type):
@@ -523,7 +525,7 @@ class PDBMetadataWriter(MetadataWriterPlugin):
name = 'Set PDB metadata'
file_types = {'pdb'}
description = _('Set metadata from %s files') % 'PDB'
description = 'Set metadata from PDB files'
author = 'John Schember'
def set_metadata(self, stream, mi, type):
@@ -535,7 +537,7 @@ class PDFMetadataWriter(MetadataWriterPlugin):
name = 'Set PDF metadata'
file_types = {'pdf'}
description = _('Set metadata in %s files') % 'PDF'
description = 'Set metadata in PDF files'
author = 'Kovid Goyal'
def set_metadata(self, stream, mi, type):
@@ -547,7 +549,7 @@ class RTFMetadataWriter(MetadataWriterPlugin):
name = 'Set RTF metadata'
file_types = {'rtf'}
description = _('Set metadata in %s files')%'RTF'
description = 'Set metadata in RTF files'
def set_metadata(self, stream, mi, type):
from ebook_converter.ebooks.metadata.rtf import set_metadata
@@ -558,7 +560,7 @@ class TOPAZMetadataWriter(MetadataWriterPlugin):
name = 'Set TOPAZ metadata'
file_types = {'tpz', 'azw1'}
description = _('Set metadata in %s files')%'TOPAZ'
description = 'Set metadata in TOPAZ files'
author = 'Greg Riker'
def set_metadata(self, stream, mi, type):
@@ -570,7 +572,7 @@ class TXTZMetadataWriter(MetadataWriterPlugin):
name = 'Set TXTZ metadata'
file_types = {'txtz'}
description = _('Set metadata from %s files') % 'TXTZ'
description = 'Set metadata from TXTZ files'
author = 'John Schember'
def set_metadata(self, stream, mi, type):
@@ -582,7 +584,7 @@ class ODTMetadataWriter(MetadataWriterPlugin):
name = 'Set ODT metadata'
file_types = {'odt'}
description = _('Set metadata from %s files')%'ODT'
description = 'Set metadata from ODT files'
def set_metadata(self, stream, mi, type):
from ebook_converter.ebooks.metadata.odt import set_metadata
@@ -593,7 +595,7 @@ class DocXMetadataWriter(MetadataWriterPlugin):
name = 'Set DOCX metadata'
file_types = {'docx'}
description = _('Set metadata from %s files')%'DOCX'
description = 'Set metadata from DOCX files'
def set_metadata(self, stream, mi, type):
from ebook_converter.ebooks.metadata.docx import set_metadata
@@ -828,228 +830,231 @@ plugins += input_profiles + output_profiles
class ActionAdd(InterfaceActionBase):
name = 'Add Books'
actual_plugin = 'ebook_converter.gui2.actions.add:AddAction'
description = _('Add books to calibre or the connected device')
description = 'Add books to calibre or the connected device'
class ActionFetchAnnotations(InterfaceActionBase):
name = 'Fetch Annotations'
actual_plugin = 'ebook_converter.gui2.actions.annotate:FetchAnnotationsAction'
description = _('Fetch annotations from a connected Kindle (experimental)')
description = 'Fetch annotations from a connected Kindle (experimental)'
class ActionGenerateCatalog(InterfaceActionBase):
name = 'Generate Catalog'
actual_plugin = 'ebook_converter.gui2.actions.catalog:GenerateCatalogAction'
description = _('Generate a catalog of the books in your calibre library')
description = 'Generate a catalog of the books in your calibre library'
class ActionConvert(InterfaceActionBase):
name = 'Convert Books'
actual_plugin = 'ebook_converter.gui2.actions.convert:ConvertAction'
description = _('Convert books to various e-book formats')
description = 'Convert books to various e-book formats'
class ActionPolish(InterfaceActionBase):
name = 'Polish Books'
actual_plugin = 'ebook_converter.gui2.actions.polish:PolishAction'
description = _('Fine tune your e-books')
description = 'Fine tune your e-books'
class ActionEditToC(InterfaceActionBase):
name = 'Edit ToC'
actual_plugin = 'ebook_converter.gui2.actions.toc_edit:ToCEditAction'
description = _('Edit the Table of Contents in your books')
description = 'Edit the Table of Contents in your books'
class ActionDelete(InterfaceActionBase):
name = 'Remove Books'
actual_plugin = 'ebook_converter.gui2.actions.delete:DeleteAction'
description = _('Delete books from your calibre library or connected device')
description = 'Delete books from your calibre library or connected device'
class ActionEmbed(InterfaceActionBase):
name = 'Embed Metadata'
actual_plugin = 'ebook_converter.gui2.actions.embed:EmbedAction'
description = _('Embed updated metadata into the actual book files in your calibre library')
description = ('Embed updated metadata into the actual book files in '
'your calibre library')
class ActionEditMetadata(InterfaceActionBase):
name = 'Edit Metadata'
actual_plugin = 'ebook_converter.gui2.actions.edit_metadata:EditMetadataAction'
description = _('Edit the metadata of books in your calibre library')
description = 'Edit the metadata of books in your calibre library'
class ActionView(InterfaceActionBase):
name = 'View'
actual_plugin = 'ebook_converter.gui2.actions.view:ViewAction'
description = _('Read books in your calibre library')
description = 'Read books in your calibre library'
class ActionFetchNews(InterfaceActionBase):
name = 'Fetch News'
actual_plugin = 'ebook_converter.gui2.actions.fetch_news:FetchNewsAction'
description = _('Download news from the internet in e-book form')
description = 'Download news from the internet in e-book form'
class ActionQuickview(InterfaceActionBase):
name = 'Quickview'
actual_plugin = 'ebook_converter.gui2.actions.show_quickview:ShowQuickviewAction'
description = _('Show a list of related books quickly')
description = 'Show a list of related books quickly'
class ActionTagMapper(InterfaceActionBase):
name = 'Tag Mapper'
actual_plugin = 'ebook_converter.gui2.actions.tag_mapper:TagMapAction'
description = _('Filter/transform the tags for books in the library')
description = 'Filter/transform the tags for books in the library'
class ActionAuthorMapper(InterfaceActionBase):
name = 'Author Mapper'
actual_plugin = 'ebook_converter.gui2.actions.author_mapper:AuthorMapAction'
description = _('Transform the authors for books in the library')
description = 'Transform the authors for books in the library'
class ActionTemplateTester(InterfaceActionBase):
name = 'Template Tester'
actual_plugin = 'ebook_converter.gui2.actions.show_template_tester:ShowTemplateTesterAction'
description = _('Show an editor for testing templates')
description = 'Show an editor for testing templates'
class ActionSaveToDisk(InterfaceActionBase):
name = 'Save To Disk'
actual_plugin = 'ebook_converter.gui2.actions.save_to_disk:SaveToDiskAction'
description = _('Export books from your calibre library to the hard disk')
description = 'Export books from your calibre library to the hard disk'
class ActionShowBookDetails(InterfaceActionBase):
name = 'Show Book Details'
actual_plugin = 'ebook_converter.gui2.actions.show_book_details:ShowBookDetailsAction'
description = _('Show Book details in a separate popup')
description = 'Show Book details in a separate popup'
class ActionRestart(InterfaceActionBase):
name = 'Restart'
actual_plugin = 'ebook_converter.gui2.actions.restart:RestartAction'
description = _('Restart calibre')
description = 'Restart calibre'
class ActionOpenFolder(InterfaceActionBase):
name = 'Open Folder'
actual_plugin = 'ebook_converter.gui2.actions.open:OpenFolderAction'
description = _('Open the folder that contains the book files in your'
description = ('Open the folder that contains the book files in your'
' calibre library')
class ActionSendToDevice(InterfaceActionBase):
name = 'Send To Device'
actual_plugin = 'ebook_converter.gui2.actions.device:SendToDeviceAction'
description = _('Send books to the connected device')
description = 'Send books to the connected device'
class ActionConnectShare(InterfaceActionBase):
name = 'Connect Share'
actual_plugin = 'ebook_converter.gui2.actions.device:ConnectShareAction'
description = _('Send books via email or the web. Also connect to'
description = ('Send books via email or the web. Also connect to'
' folders on your computer as if they are devices')
class ActionHelp(InterfaceActionBase):
name = 'Help'
actual_plugin = 'ebook_converter.gui2.actions.help:HelpAction'
description = _('Browse the calibre User Manual')
description = 'Browse the calibre User Manual'
class ActionPreferences(InterfaceActionBase):
name = 'Preferences'
actual_plugin = 'ebook_converter.gui2.actions.preferences:PreferencesAction'
description = _('Customize calibre')
description = 'Customize calibre'
class ActionSimilarBooks(InterfaceActionBase):
name = 'Similar Books'
actual_plugin = 'ebook_converter.gui2.actions.similar_books:SimilarBooksAction'
description = _('Easily find books similar to the currently selected one')
description = 'Easily find books similar to the currently selected one'
class ActionChooseLibrary(InterfaceActionBase):
name = 'Choose Library'
actual_plugin = 'ebook_converter.gui2.actions.choose_library:ChooseLibraryAction'
description = _('Switch between different calibre libraries and perform'
' maintenance on them')
description = ('Switch between different calibre libraries and perform '
'maintenance on them')
class ActionAddToLibrary(InterfaceActionBase):
name = 'Add To Library'
actual_plugin = 'ebook_converter.gui2.actions.add_to_library:AddToLibraryAction'
description = _('Copy books from the device to your calibre library')
description = 'Copy books from the device to your calibre library'
class ActionEditCollections(InterfaceActionBase):
name = 'Edit Collections'
actual_plugin = 'ebook_converter.gui2.actions.edit_collections:EditCollectionsAction'
description = _('Edit the collections in which books are placed on your device')
description = ('Edit the collections in which books are placed on your '
'device')
class ActionMatchBooks(InterfaceActionBase):
name = 'Match Books'
actual_plugin = 'ebook_converter.gui2.actions.match_books:MatchBookAction'
description = _('Match book on the devices to books in the library')
description = 'Match book on the devices to books in the library'
class ActionCopyToLibrary(InterfaceActionBase):
name = 'Copy To Library'
actual_plugin = 'ebook_converter.gui2.actions.copy_to_library:CopyToLibraryAction'
description = _('Copy a book from one calibre library to another')
description = 'Copy a book from one calibre library to another'
class ActionTweakEpub(InterfaceActionBase):
name = 'Tweak ePub'
actual_plugin = 'ebook_converter.gui2.actions.tweak_epub:TweakEpubAction'
description = _('Edit e-books in the EPUB or AZW3 formats')
description = 'Edit e-books in the EPUB or AZW3 formats'
class ActionUnpackBook(InterfaceActionBase):
name = 'Unpack Book'
actual_plugin = 'ebook_converter.gui2.actions.unpack_book:UnpackBookAction'
description = _('Make small changes to EPUB or HTMLZ files in your calibre library')
description = ('Make small changes to EPUB or HTMLZ files in your '
'calibre library')
class ActionNextMatch(InterfaceActionBase):
name = 'Next Match'
actual_plugin = 'ebook_converter.gui2.actions.next_match:NextMatchAction'
description = _('Find the next or previous match when searching in '
description = ('Find the next or previous match when searching in '
'your calibre library in highlight mode')
class ActionPickRandom(InterfaceActionBase):
name = 'Pick Random Book'
actual_plugin = 'ebook_converter.gui2.actions.random:PickRandomAction'
description = _('Choose a random book from your calibre library')
description = 'Choose a random book from your calibre library'
class ActionSortBy(InterfaceActionBase):
name = 'Sort By'
actual_plugin = 'ebook_converter.gui2.actions.sort:SortByAction'
description = _('Sort the list of books')
description = 'Sort the list of books'
class ActionMarkBooks(InterfaceActionBase):
name = 'Mark Books'
actual_plugin = 'ebook_converter.gui2.actions.mark_books:MarkBooksAction'
description = _('Temporarily mark books')
description = 'Temporarily mark books'
class ActionVirtualLibrary(InterfaceActionBase):
name = 'Virtual Library'
actual_plugin = 'ebook_converter.gui2.actions.virtual_library:VirtualLibraryAction'
description = _('Change the current Virtual library')
description = 'Change the current Virtual library'
class ActionStore(InterfaceActionBase):
name = 'Store'
author = 'John Schember'
actual_plugin = 'ebook_converter.gui2.actions.store:StoreAction'
description = _('Search for books from different book sellers')
description = 'Search for books from different book sellers'
def customization_help(self, gui=False):
return 'Customize the behavior of the store search.'
@@ -1066,7 +1071,8 @@ class ActionStore(InterfaceActionBase):
class ActionPluginUpdater(InterfaceActionBase):
name = 'Plugin Updater'
author = 'Grant Drake'
description = _('Get new ebook_converter plugins or update your existing ones')
description = ('Get new ebook_converter plugins or update your existing '
'ones')
actual_plugin = 'calibre.gui2.actions.plugin_updates:PluginUpdaterAction'
+8 -8
View File
@@ -101,7 +101,7 @@ class InputFormatPlugin(Plugin):
The main action happens in :meth:`convert`.
'''
type = _('Conversion input')
type = 'Conversion input'
can_be_disabled = False
supported_platforms = ['windows', 'osx', 'linux']
commit_name = None # unique name under which options for this plugin are saved
@@ -137,11 +137,11 @@ class InputFormatPlugin(Plugin):
common_options = {
OptionRecommendation(name='input_encoding',
recommended_value=None, level=OptionRecommendation.LOW,
help=_('Specify the character encoding of the input document. If '
help='Specify the character encoding of the input document. If '
'set this option will override any encoding declared by the '
'document itself. Particularly useful for documents that '
'do not declare an encoding or that have erroneous '
'encoding declarations.')
'encoding declarations.'
)}
#: Options to customize the behavior of this plugin. Every option must be an
@@ -239,7 +239,7 @@ class OutputFormatPlugin(Plugin):
The main action happens in :meth:`convert`.
'''
type = _('Conversion output')
type = 'Conversion output'
can_be_disabled = False
supported_platforms = ['windows', 'osx', 'linux']
commit_name = None # unique name under which options for this plugin are saved
@@ -255,9 +255,9 @@ class OutputFormatPlugin(Plugin):
common_options = {
OptionRecommendation(name='pretty_print',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('If specified, the output plugin will try to create output '
'that is as human readable as possible. May not have any effect '
'for some output plugins.')
help='If specified, the output plugin will try to create output '
'that is as human readable as possible. May not have any '
'effect for some output plugins.'
)}
#: Options to customize the behavior of this plugin. Every option must be an
@@ -270,7 +270,7 @@ class OutputFormatPlugin(Plugin):
@property
def description(self):
return _('Convert e-books to the %s format')%self.file_type
return 'Convert e-books to the %s format' % self.file_type
def __init__(self, *args):
Plugin.__init__(self, *args)
+63 -56
View File
@@ -43,11 +43,11 @@ class InputProfile(Plugin):
author = 'Kovid Goyal'
supported_platforms = {'windows', 'osx', 'linux'}
can_be_disabled = False
type = _('Input profile')
type = 'Input profile'
name = 'Default Input Profile'
short_name = 'default' # Used in the CLI so dont use spaces etc. in it
description = _('This profile tries to provide sane defaults and is useful '
description = ('This profile tries to provide sane defaults and is useful '
'if you know nothing about the input document.')
@@ -55,7 +55,7 @@ class SonyReaderInput(InputProfile):
name = 'Sony Reader'
short_name = 'sony'
description = _('This profile is intended for the SONY PRS line. '
description = ('This profile is intended for the SONY PRS line. '
'The 500/505/600/700 etc.')
screen_size = (584, 754)
@@ -68,7 +68,7 @@ class SonyReader300Input(SonyReaderInput):
name = 'Sony Reader 300'
short_name = 'sony300'
description = _('This profile is intended for the SONY PRS 300.')
description = 'This profile is intended for the SONY PRS 300.'
dpi = 200
@@ -78,7 +78,7 @@ class SonyReader900Input(SonyReaderInput):
author = 'John Schember'
name = 'Sony Reader 900'
short_name = 'sony900'
description = _('This profile is intended for the SONY PRS-900.')
description = 'This profile is intended for the SONY PRS-900.'
screen_size = (584, 978)
@@ -87,7 +87,7 @@ class MSReaderInput(InputProfile):
name = 'Microsoft Reader'
short_name = 'msreader'
description = _('This profile is intended for the Microsoft Reader.')
description = 'This profile is intended for the Microsoft Reader.'
screen_size = (480, 652)
dpi = 96
@@ -99,7 +99,7 @@ class MobipocketInput(InputProfile):
name = 'Mobipocket Books'
short_name = 'mobipocket'
description = _('This profile is intended for the Mobipocket books.')
description = 'This profile is intended for the Mobipocket books.'
# Unfortunately MOBI books are not narrowly targeted, so this information is
# quite likely to be spurious
@@ -113,7 +113,7 @@ class HanlinV3Input(InputProfile):
name = 'Hanlin V3'
short_name = 'hanlinv3'
description = _('This profile is intended for the Hanlin V3 and its clones.')
description = 'This profile is intended for the Hanlin V3 and its clones.'
# Screen size is a best guess
screen_size = (584, 754)
@@ -126,7 +126,7 @@ class HanlinV5Input(HanlinV3Input):
name = 'Hanlin V5'
short_name = 'hanlinv5'
description = _('This profile is intended for the Hanlin V5 and its clones.')
description = 'This profile is intended for the Hanlin V5 and its clones.'
# Screen size is a best guess
screen_size = (584, 754)
@@ -137,7 +137,7 @@ class CybookG3Input(InputProfile):
name = 'Cybook G3'
short_name = 'cybookg3'
description = _('This profile is intended for the Cybook G3.')
description = 'This profile is intended for the Cybook G3.'
# Screen size is a best guess
screen_size = (600, 800)
@@ -151,7 +151,7 @@ class CybookOpusInput(InputProfile):
author = 'John Schember'
name = 'Cybook Opus'
short_name = 'cybook_opus'
description = _('This profile is intended for the Cybook Opus.')
description = 'This profile is intended for the Cybook Opus.'
# Screen size is a best guess
screen_size = (600, 800)
@@ -164,7 +164,7 @@ class KindleInput(InputProfile):
name = 'Kindle'
short_name = 'kindle'
description = _('This profile is intended for the Amazon Kindle.')
description = 'This profile is intended for the Amazon Kindle.'
# Screen size is a best guess
screen_size = (525, 640)
@@ -177,7 +177,7 @@ class IlliadInput(InputProfile):
name = 'Illiad'
short_name = 'illiad'
description = _('This profile is intended for the Irex Illiad.')
description = 'This profile is intended for the Irex Illiad.'
screen_size = (760, 925)
dpi = 160.0
@@ -190,7 +190,7 @@ class IRexDR1000Input(InputProfile):
author = 'John Schember'
name = 'IRex Digital Reader 1000'
short_name = 'irexdr1000'
description = _('This profile is intended for the IRex Digital Reader 1000.')
description = 'This profile is intended for the IRex Digital Reader 1000.'
# Screen size is a best guess
screen_size = (1024, 1280)
@@ -204,7 +204,7 @@ class IRexDR800Input(InputProfile):
author = 'Eric Cronin'
name = 'IRex Digital Reader 800'
short_name = 'irexdr800'
description = _('This profile is intended for the IRex Digital Reader 800.')
description = 'This profile is intended for the IRex Digital Reader 800.'
screen_size = (768, 1024)
dpi = 160
@@ -217,7 +217,7 @@ class NookInput(InputProfile):
author = 'John Schember'
name = 'Nook'
short_name = 'nook'
description = _('This profile is intended for the B&N Nook.')
description = 'This profile is intended for the B&N Nook.'
# Screen size is a best guess
screen_size = (600, 800)
@@ -241,13 +241,13 @@ class OutputProfile(Plugin):
author = 'Kovid Goyal'
supported_platforms = {'windows', 'osx', 'linux'}
can_be_disabled = False
type = _('Output profile')
type = 'Output profile'
name = 'Default Output Profile'
short_name = 'default' # Used in the CLI so dont use spaces etc. in it
description = _('This profile tries to provide sane defaults and is useful '
'if you want to produce a document intended to be read at a '
'computer or on a range of devices.')
description = ('This profile tries to provide sane defaults and is useful '
'if you want to produce a document intended to be read at '
'a computer or on a range of devices.')
#: The image size for comics
comic_screen_size = (584, 754)
@@ -282,7 +282,7 @@ class iPadOutput(OutputProfile):
name = 'iPad'
short_name = 'ipad'
description = _('Intended for the iPad and similar devices with a '
description = ('Intended for the iPad and similar devices with a '
'resolution of 768x1024')
screen_size = (768, 1024)
comic_screen_size = (768, 1024)
@@ -445,14 +445,15 @@ class iPad3Output(iPadOutput):
dpi = 264.0
name = 'iPad 3'
short_name = 'ipad3'
description = _('Intended for the iPad 3 and similar devices with a '
description = ('Intended for the iPad 3 and similar devices with a '
'resolution of 1536x2048')
class TabletOutput(iPadOutput):
name = 'Tablet'
short_name = 'tablet'
description = _('Intended for generic tablet devices, does no resizing of images')
description = ('Intended for generic tablet devices, does no resizing of '
'images')
screen_size = (10000, 10000)
comic_screen_size = (10000, 10000)
@@ -461,15 +462,15 @@ class TabletOutput(iPadOutput):
class SamsungGalaxy(TabletOutput):
name = 'Samsung Galaxy'
short_name = 'galaxy'
description = _('Intended for the Samsung Galaxy and similar tablet devices with '
'a resolution of 600x1280')
description = ('Intended for the Samsung Galaxy and similar tablet '
'devices with a resolution of 600x1280')
screen_size = comic_screen_size = (600, 1280)
class NookHD(TabletOutput):
name = 'Nook HD+'
short_name = 'nook_hd_plus'
description = _('Intended for the Nook HD+ and similar tablet devices with '
description = ('Intended for the Nook HD+ and similar tablet devices with '
'a resolution of 1280x1920')
screen_size = comic_screen_size = (1280, 1920)
@@ -478,7 +479,7 @@ class SonyReaderOutput(OutputProfile):
name = 'Sony Reader'
short_name = 'sony'
description = _('This profile is intended for the SONY PRS line. '
description = ('This profile is intended for the SONY PRS line. '
'The 500/505/600/700 etc.')
screen_size = (590, 775)
@@ -496,7 +497,7 @@ class KoboReaderOutput(OutputProfile):
name = 'Kobo Reader'
short_name = 'kobo'
description = _('This profile is intended for the Kobo Reader.')
description = 'This profile is intended for the Kobo Reader.'
screen_size = (536, 710)
comic_screen_size = (536, 710)
@@ -510,7 +511,7 @@ class SonyReader300Output(SonyReaderOutput):
author = 'John Schember'
name = 'Sony Reader 300'
short_name = 'sony300'
description = _('This profile is intended for the SONY PRS-300.')
description = 'This profile is intended for the SONY PRS-300.'
dpi = 200
@@ -520,7 +521,7 @@ class SonyReader900Output(SonyReaderOutput):
author = 'John Schember'
name = 'Sony Reader 900'
short_name = 'sony900'
description = _('This profile is intended for the SONY PRS-900.')
description = 'This profile is intended for the SONY PRS-900.'
screen_size = (600, 999)
comic_screen_size = screen_size
@@ -531,7 +532,7 @@ class SonyReaderT3Output(SonyReaderOutput):
author = 'Kovid Goyal'
name = 'Sony Reader T3'
short_name = 'sonyt3'
description = _('This profile is intended for the SONY PRS-T3.')
description = 'This profile is intended for the SONY PRS-T3.'
screen_size = (758, 934)
comic_screen_size = screen_size
@@ -541,7 +542,7 @@ class GenericEink(SonyReaderOutput):
name = 'Generic e-ink'
short_name = 'generic_eink'
description = _('Suitable for use with any e-ink device')
description = 'Suitable for use with any e-ink device'
epub_periodical_format = None
@@ -549,7 +550,7 @@ class GenericEinkLarge(GenericEink):
name = 'Generic e-ink large'
short_name = 'generic_eink_large'
description = _('Suitable for use with any large screen e-ink device')
description = 'Suitable for use with any large screen e-ink device'
screen_size = (600, 999)
comic_screen_size = screen_size
@@ -559,7 +560,8 @@ class GenericEinkHD(GenericEink):
name = 'Generic e-ink HD'
short_name = 'generic_eink_hd'
description = _('Suitable for use with any modern high resolution e-ink device')
description = ('Suitable for use with any modern high resolution e-ink '
'device')
screen_size = (10000, 10000)
comic_screen_size = (10000, 10000)
@@ -569,7 +571,7 @@ class JetBook5Output(OutputProfile):
name = 'JetBook 5-inch'
short_name = 'jetbook5'
description = _('This profile is intended for the 5-inch JetBook.')
description = 'This profile is intended for the 5-inch JetBook.'
screen_size = (480, 640)
dpi = 168.451
@@ -579,7 +581,7 @@ class SonyReaderLandscapeOutput(SonyReaderOutput):
name = 'Sony Reader Landscape'
short_name = 'sony-landscape'
description = _('This profile is intended for the SONY PRS line. '
description = ('This profile is intended for the SONY PRS line. '
'The 500/505/700 etc, in landscape mode. Mainly useful '
'for comics.')
@@ -591,7 +593,7 @@ class MSReaderOutput(OutputProfile):
name = 'Microsoft Reader'
short_name = 'msreader'
description = _('This profile is intended for the Microsoft Reader.')
description = 'This profile is intended for the Microsoft Reader.'
screen_size = (480, 652)
dpi = 96
@@ -603,7 +605,7 @@ class MobipocketOutput(OutputProfile):
name = 'Mobipocket Books'
short_name = 'mobipocket'
description = _('This profile is intended for the Mobipocket books.')
description = 'This profile is intended for the Mobipocket books.'
# Unfortunately MOBI books are not narrowly targeted, so this information is
# quite likely to be spurious
@@ -617,7 +619,7 @@ class HanlinV3Output(OutputProfile):
name = 'Hanlin V3'
short_name = 'hanlinv3'
description = _('This profile is intended for the Hanlin V3 and its clones.')
description = 'This profile is intended for the Hanlin V3 and its clones.'
# Screen size is a best guess
screen_size = (584, 754)
@@ -630,7 +632,7 @@ class HanlinV5Output(HanlinV3Output):
name = 'Hanlin V5'
short_name = 'hanlinv5'
description = _('This profile is intended for the Hanlin V5 and its clones.')
description = 'This profile is intended for the Hanlin V5 and its clones.'
dpi = 200
@@ -639,7 +641,7 @@ class CybookG3Output(OutputProfile):
name = 'Cybook G3'
short_name = 'cybookg3'
description = _('This profile is intended for the Cybook G3.')
description = 'This profile is intended for the Cybook G3.'
# Screen size is a best guess
screen_size = (600, 800)
@@ -654,7 +656,7 @@ class CybookOpusOutput(SonyReaderOutput):
author = 'John Schember'
name = 'Cybook Opus'
short_name = 'cybook_opus'
description = _('This profile is intended for the Cybook Opus.')
description = 'This profile is intended for the Cybook Opus.'
# Screen size is a best guess
dpi = 200
@@ -668,7 +670,7 @@ class KindleOutput(OutputProfile):
name = 'Kindle'
short_name = 'kindle'
description = _('This profile is intended for the Amazon Kindle.')
description = 'This profile is intended for the Amazon Kindle.'
# Screen size is a best guess
screen_size = (525, 640)
@@ -688,7 +690,7 @@ class KindleDXOutput(OutputProfile):
name = 'Kindle DX'
short_name = 'kindle_dx'
description = _('This profile is intended for the Amazon Kindle DX.')
description = 'This profile is intended for the Amazon Kindle DX.'
# Screen size is a best guess
screen_size = (744, 1022)
@@ -706,7 +708,8 @@ class KindlePaperWhiteOutput(KindleOutput):
name = 'Kindle PaperWhite'
short_name = 'kindle_pw'
description = _('This profile is intended for the Amazon Kindle PaperWhite 1 and 2')
description = ('This profile is intended for the Amazon Kindle '
'PaperWhite 1 and 2')
# Screen size is a best guess
screen_size = (658, 940)
@@ -718,7 +721,7 @@ class KindleVoyageOutput(KindleOutput):
name = 'Kindle Voyage'
short_name = 'kindle_voyage'
description = _('This profile is intended for the Amazon Kindle Voyage')
description = 'This profile is intended for the Amazon Kindle Voyage'
# Screen size is currently just the spec size, actual renderable area will
# depend on someone with the device doing tests.
@@ -731,7 +734,8 @@ class KindlePaperWhite3Output(KindleVoyageOutput):
name = 'Kindle PaperWhite 3'
short_name = 'kindle_pw3'
description = _('This profile is intended for the Amazon Kindle PaperWhite 3 and above')
description = ('This profile is intended for the Amazon Kindle '
'PaperWhite 3 and above')
# Screen size is currently just the spec size, actual renderable area will
# depend on someone with the device doing tests.
screen_size = (1072, 1430)
@@ -743,7 +747,8 @@ class KindleOasisOutput(KindlePaperWhite3Output):
name = 'Kindle Oasis'
short_name = 'kindle_oasis'
description = _('This profile is intended for the Amazon Kindle Oasis 2017 and above')
description = ('This profile is intended for the Amazon Kindle Oasis '
'2017 and above')
# Screen size is currently just the spec size, actual renderable area will
# depend on someone with the device doing tests.
screen_size = (1264, 1680)
@@ -755,7 +760,7 @@ class KindleFireOutput(KindleDXOutput):
name = 'Kindle Fire'
short_name = 'kindle_fire'
description = _('This profile is intended for the Amazon Kindle Fire.')
description = 'This profile is intended for the Amazon Kindle Fire.'
screen_size = (570, 1016)
dpi = 169.0
@@ -766,7 +771,7 @@ class IlliadOutput(OutputProfile):
name = 'Illiad'
short_name = 'illiad'
description = _('This profile is intended for the Irex Illiad.')
description = 'This profile is intended for the Irex Illiad.'
screen_size = (760, 925)
comic_screen_size = (760, 925)
@@ -780,7 +785,7 @@ class IRexDR1000Output(OutputProfile):
author = 'John Schember'
name = 'IRex Digital Reader 1000'
short_name = 'irexdr1000'
description = _('This profile is intended for the IRex Digital Reader 1000.')
description = 'This profile is intended for the IRex Digital Reader 1000.'
# Screen size is a best guess
screen_size = (1024, 1280)
@@ -795,7 +800,7 @@ class IRexDR800Output(OutputProfile):
author = 'Eric Cronin'
name = 'IRex Digital Reader 800'
short_name = 'irexdr800'
description = _('This profile is intended for the IRex Digital Reader 800.')
description = 'This profile is intended for the IRex Digital Reader 800.'
# Screen size is a best guess
screen_size = (768, 1024)
@@ -810,7 +815,7 @@ class NookOutput(OutputProfile):
author = 'John Schember'
name = 'Nook'
short_name = 'nook'
description = _('This profile is intended for the B&N Nook.')
description = 'This profile is intended for the B&N Nook.'
# Screen size is a best guess
screen_size = (600, 730)
@@ -823,7 +828,7 @@ class NookOutput(OutputProfile):
class NookColorOutput(NookOutput):
name = 'Nook Color'
short_name = 'nook_color'
description = _('This profile is intended for the B&N Nook Color.')
description = 'This profile is intended for the B&N Nook Color.'
screen_size = (600, 900)
comic_screen_size = (594, 900)
@@ -835,7 +840,8 @@ class PocketBook900Output(OutputProfile):
author = 'Chris Lockfort'
name = 'PocketBook Pro 900'
short_name = 'pocketbook_900'
description = _('This profile is intended for the PocketBook Pro 900 series of devices.')
description = ('This profile is intended for the PocketBook Pro 900 '
'series of devices.')
screen_size = (810, 1180)
dpi = 150.0
@@ -847,7 +853,8 @@ class PocketBookPro912Output(OutputProfile):
author = 'Daniele Pizzolli'
name = 'PocketBook Pro 912'
short_name = 'pocketbook_pro_912'
description = _('This profile is intended for the PocketBook Pro 912 series of devices.')
description = ('This profile is intended for the PocketBook Pro 912 '
'series of devices.')
# According to http://download.pocketbook-int.com/user-guides/E_Ink/912/User_Guide_PocketBook_912(EN).pdf
screen_size = (825, 1200)
+23 -20
View File
@@ -12,7 +12,6 @@ from ebook_converter.customize import profiles
from ebook_converter.customize import builtins
from ebook_converter.ebooks import metadata
from ebook_converter.utils import config as cfg
from ebook_converter import constants
builtin_names = frozenset(p.name for p in builtins.plugins)
@@ -25,13 +24,13 @@ class NameConflict(ValueError):
def _config():
c = cfg.Config('customize')
c.add_opt('plugins', default={}, help=_('Installed plugins'))
c.add_opt('plugins', default={}, help='Installed plugins')
c.add_opt('filetype_mapping', default={},
help=_('Mapping for filetype plugins'))
help='Mapping for filetype plugins')
c.add_opt('plugin_customization', default={},
help=_('Local plugin customization'))
c.add_opt('disabled_plugins', default=set(), help=_('Disabled plugins'))
c.add_opt('enabled_plugins', default=set(), help=_('Enabled plugins'))
help='Local plugin customization')
c.add_opt('disabled_plugins', default=set(), help='Disabled plugins')
c.add_opt('enabled_plugins', default=set(), help='Enabled plugins')
return cfg.ConfigProxy(c)
@@ -459,8 +458,8 @@ def initialize_plugin(plugin, path_to_zip_file):
except Exception:
print('Failed to initialize plugin:', plugin.name, plugin.version)
tb = traceback.format_exc()
raise customize.InvalidPlugin((_('Initialization of plugin %s failed '
'with traceback:') % tb) + '\n'+tb)
raise customize.InvalidPlugin(('Initialization of plugin %s failed '
'with traceback:' % tb) + '\n'+tb)
def has_external_plugins():
@@ -501,25 +500,29 @@ def initialized_plugins():
# CLI
def option_parser():
parser = cfg.OptionParser(usage=_('''\
parser = cfg.OptionParser(usage='''\
%prog options
Customize calibre by loading external plugins.
'''))
''')
parser.add_option('-a', '--add-plugin', default=None,
help=_('Add a plugin by specifying the path to the ZIP file containing it.'))
help='Add a plugin by specifying the path to the ZIP '
'file containing it.')
parser.add_option('-b', '--build-plugin', default=None,
help=_('For plugin developers: Path to the directory where you are'
' developing the plugin. This command will automatically zip '
'up the plugin and update it in calibre.'))
help='For plugin developers: Path to the directory '
'where you are developing the plugin. This command will '
'automatically zip up the plugin and update it in '
'calibre.')
parser.add_option('-r', '--remove-plugin', default=None,
help=_('Remove a custom plugin by name. Has no effect on builtin plugins'))
help='Remove a custom plugin by name. Has no effect on '
'builtin plugins')
parser.add_option('--customize-plugin', default=None,
help=_('Customize plugin. Specify name of plugin and customization string separated by a comma.'))
parser.add_option('-l', '--list-plugins', default=False, action='store_true',
help=_('List all installed plugins'))
help='Customize plugin. Specify name of plugin and '
'customization string separated by a comma.')
parser.add_option('-l', '--list-plugins', default=False,
action='store_true', help='List all installed plugins')
parser.add_option('--enable-plugin', default=None,
help=_('Enable the named plugin'))
help='Enable the named plugin')
parser.add_option('--disable-plugin', default=None,
help=_('Disable the named plugin'))
help='Disable the named plugin')
return parser
-111
View File
@@ -1,111 +0,0 @@
"""
PEP 302 based plugin loading mechanism, works around the bug in zipimport in
python 2.x that prevents importing from zip files in locations whose paths
have non ASCII characters
"""
import os, zipfile, posixpath, importlib, threading, re, imp, sys
from collections import OrderedDict
from functools import partial
from ebook_converter import as_unicode
from ebook_converter.customize import (Plugin, numeric_version, platform,
InvalidPlugin, PluginNotFound)
from ebook_converter.polyglot.builtins import reload
__license__ = 'GPL v3'
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
def get_resources(zfp, name_or_list_of_names):
'''
Load resources from the plugin zip file
:param name_or_list_of_names: List of paths to resources in the zip file using / as
separator, or a single path
:return: A dictionary of the form ``{name : file_contents}``. Any names
that were not found in the zip file will not be present in the
dictionary. If a single path is passed in the return value will
be just the bytes of the resource or None if it wasn't found.
'''
names = name_or_list_of_names
if isinstance(names, (str, bytes)):
names = [names]
ans = {}
with zipfile.ZipFile(zfp) as zf:
for name in names:
try:
ans[name] = zf.read(name)
except:
import traceback
traceback.print_exc()
if len(names) == 1:
ans = ans.pop(names[0], None)
return ans
def get_icons(zfp, name_or_list_of_names):
'''
Load icons from the plugin zip file
:param name_or_list_of_names: List of paths to resources in the zip file using / as
separator, or a single path
:return: A dictionary of the form ``{name : QIcon}``. Any names
that were not found in the zip file will be null QIcons.
If a single path is passed in the return value will
be A QIcon.
'''
from PyQt5.Qt import QIcon, QPixmap
names = name_or_list_of_names
ans = get_resources(zfp, names)
if isinstance(names, (str, bytes)):
names = [names]
if ans is None:
ans = {}
if isinstance(ans, (str, bytes)):
ans = dict([(names[0], ans)])
ians = {}
for name in names:
p = QPixmap()
raw = ans.get(name, None)
if raw:
p.loadFromData(raw)
ians[name] = QIcon(p)
if len(names) == 1:
ians = ians.pop(names[0])
return ians
_translations_cache = {}
def load_translations(namespace, zfp):
null = object()
trans = _translations_cache.get(zfp, null)
if trans is None:
return
if trans is null:
from ebook_converter.utils.localization import get_lang
lang = get_lang()
if not lang or lang == 'en': # performance optimization
_translations_cache[zfp] = None
return
with zipfile.ZipFile(zfp) as zf:
try:
mo = zf.read('translations/%s.mo' % lang)
except KeyError:
mo = None # No translations for this language present
if mo is None:
_translations_cache[zfp] = None
return
from gettext import GNUTranslations
from io import BytesIO
trans = _translations_cache[zfp] = GNUTranslations(BytesIO(mo))
namespace['_'] = getattr(trans, 'gettext')
namespace['ngettext'] = getattr(trans, 'ngettext')
+2 -2
View File
@@ -15,7 +15,7 @@ class DevicePlugin(Plugin):
Defines the interface that should be implemented by backends that
communicate with an e-book reader.
"""
type = _('Device interface')
type = 'Device interface'
#: Ordered list of supported formats
FORMATS = ["lrf", "rtf", "pdf", "txt"]
@@ -60,7 +60,7 @@ class DevicePlugin(Plugin):
# Set this to None if the books on the device are files that the GUI can
# access in order to add the books from the device to the library
BACKLOADING_ERROR_MESSAGE = _('Cannot get files from this device')
BACKLOADING_ERROR_MESSAGE = 'Cannot get files from this device'
#: Path separator for paths to books on device
path_sep = os.sep
+47 -49
View File
@@ -14,7 +14,7 @@ from ebook_converter import patheq
from ebook_converter.utils.localization import localize_user_manual_link
USAGE = '%prog ' + _('''\
USAGE = '%prog ' + '''\
input_file output_file [options]
Convert an e-book from one format to another.
@@ -38,7 +38,8 @@ To get help on them specify the input and output file and then use the -h \
option.
For full documentation of the conversion system see
''') + localize_user_manual_link('https://manual.calibre-ebook.com/conversion.html')
https://manual.calibre-ebook.com/conversion.html
'''
HEURISTIC_OPTIONS = ['markup_chapter_headings',
'italicize_common_cases', 'fix_indents',
@@ -98,21 +99,20 @@ def option_recommendation_to_cli_option(add_option, rec):
if opt.name == 'read_metadata_from_opf':
switches.append('--from-opf')
if opt.name == 'transform_css_rules':
attrs['help'] = _(
'Path to a file containing rules to transform the CSS styles'
' in this book. The easiest way to create such a file is to'
' use the wizard for creating rules in the calibre GUI. Access'
' it in the "Look & feel->Transform styles" section of the conversion'
' dialog. Once you create the rules, you can use the "Export" button'
' to save them to a file.'
)
attrs['help'] = ('Path to a file containing rules to transform the '
'CSS styles in this book. The easiest way to create '
'such a file is to use the wizard for creating rules '
'in the calibre GUI. Access it in the "Look & '
'feel->Transform styles" section of the conversion '
'dialog. Once you create the rules, you can use the '
'"Export" button to save them to a file.')
if opt.name in DEFAULT_TRUE_OPTIONS and rec.recommended_value is True:
switches = ['--disable-'+opt.long_switch]
add_option(optparse.Option(*switches, **attrs))
def group_titles():
return _('INPUT OPTIONS'), _('OUTPUT OPTIONS')
return 'INPUT OPTIONS', 'OUTPUT OPTIONS'
def recipe_test(option, opt_str, value, parser):
@@ -160,15 +160,17 @@ def add_input_output_options(parser, plumber):
if input_options:
title = group_titles()[0]
io = optparse.OptionGroup(parser, title, _('Options to control the processing'
' of the input %s file')%plumber.input_fmt)
io = optparse.OptionGroup(parser, title, 'Options to control the '
'processing of the input %s file' %
plumber.input_fmt)
add_options(io.add_option, input_options)
parser.add_option_group(io)
if output_options:
title = group_titles()[1]
oo = optparse.OptionGroup(parser, title, _('Options to control the processing'
' of the output %s')%plumber.output_fmt)
oo = optparse.OptionGroup(parser, title, 'Options to control the '
'processing of the output %s' %
plumber.output_fmt)
add_options(oo.add_option, output_options)
parser.add_option_group(oo)
@@ -181,8 +183,8 @@ def add_pipeline_options(parser, plumber):
'output_profile',
]
)),
(_('LOOK AND FEEL') , (
_('Options to control the look and feel of the output'),
('LOOK AND FEEL' , (
'Options to control the look and feel of the output',
[
'base_font_size', 'disable_font_rescaling',
'font_size_mapping', 'embed_font_family',
@@ -200,26 +202,23 @@ def add_pipeline_options(parser, plumber):
]
)),
(_('HEURISTIC PROCESSING') , (
_('Modify the document text and structure using common'
' patterns. Disabled by default. Use %(en)s to enable. '
' Individual actions can be disabled with the %(dis)s options.')
% dict(en='--enable-heuristics', dis='--disable-*'),
('HEURISTIC PROCESSING' ,
('Modify the document text and structure using common '
'patterns. Disabled by default. Use %(en)s to enable. '
'Individual actions can be disabled with the %(dis)s '
'options.' % dict(en='--enable-heuristics', dis='--disable-*'),
['enable_heuristics'] + HEURISTIC_OPTIONS
)),
(_('SEARCH AND REPLACE') , (
_('Modify the document text and structure using user defined patterns.'),
[
'sr1_search', 'sr1_replace',
'sr2_search', 'sr2_replace',
'sr3_search', 'sr3_replace',
'search_replace',
]
('SEARCH AND REPLACE' ,
('Modify the document text and structure using user defined '
'patterns.',
['sr1_search', 'sr1_replace', 'sr2_search', 'sr2_replace',
'sr3_search', 'sr3_replace', 'search_replace']
)),
(_('STRUCTURE DETECTION') , (
_('Control auto-detection of document structure.'),
('STRUCTURE DETECTION' , (
'Control auto-detection of document structure.',
[
'chapter', 'chapter_mark',
'prefer_metadata_cover', 'remove_first_image',
@@ -228,21 +227,20 @@ def add_pipeline_options(parser, plumber):
]
)),
(_('TABLE OF CONTENTS') , (
_('Control the automatic generation of a Table of Contents. By '
('TABLE OF CONTENTS' ,
('Control the automatic generation of a Table of Contents. By '
'default, if the source file has a Table of Contents, it will '
'be used in preference to the automatically generated one.'),
[
'level1_toc', 'level2_toc', 'level3_toc',
'toc_threshold', 'max_toc_links', 'no_chapters_in_toc',
'use_auto_toc', 'toc_filter', 'duplicate_links_in_toc',
]
)),
'be used in preference to the automatically generated one.',
['level1_toc', 'level2_toc', 'level3_toc', 'toc_threshold',
'max_toc_links', 'no_chapters_in_toc', 'use_auto_toc',
'toc_filter', 'duplicate_links_in_toc']
)
),
(_('METADATA') , (_('Options to set metadata in the output'),
('METADATA' , ('Options to set metadata in the output',
plumber.metadata_option_names + ['read_metadata_from_opf'],
)),
(_('DEBUG'), (_('Options to help with debugging the conversion'),
('DEBUG', ('Options to help with debugging the conversion',
[
'verbose',
'debug_pipeline',
@@ -265,9 +263,9 @@ def add_pipeline_options(parser, plumber):
def option_parser():
parser = OptionParser(usage=USAGE)
parser.add_option('--list-recipes', default=False, action='store_true',
help=_('List builtin recipe names. You can create an e-book from '
'a builtin recipe like this: ebook-convert "Recipe Name.recipe" '
'output.epub'))
help='List builtin recipe names. You can create an e-book from '
'a builtin recipe like this: ebook-convert "Recipe '
'Name.recipe" output.epub')
return parser
@@ -393,20 +391,20 @@ def main(args=sys.argv):
plumber.run()
log(_('Output saved to'), ' ', plumber.output)
log('Output saved to', ' ', plumber.output)
return 0
def manual_index_strings():
return _('''\
return '''\
The options and default values for the options change depending on both the
input and output formats, so you should always check with::
%s
Below are the options that are common to all conversion, followed by the
options specific to every input and output format.''')
options specific to every input and output format.'''
if __name__ == '__main__':
@@ -191,7 +191,7 @@ class CHMInput(InputFormatPlugin):
title = param.attrib['value']
elif match_string(param.attrib['name'], 'local'):
href = param.attrib['value']
child = toc.add(title or _('Unknown'), href)
child = toc.add(title or 'Unknown', href)
ancestor_map[node] = child
def _process_nodes(self, root):
@@ -25,51 +25,54 @@ class ComicInput(InputFormatPlugin):
options = {
OptionRecommendation(name='colors', recommended_value=0,
help=_('Reduce the number of colors used in the image. This works only'
' if you choose the PNG output format. It is useful to reduce file sizes.'
' Set to zero to turn off. Maximum value is 256. It is off by default.')),
help='Reduce the number of colors used in the image. This works '
'only if you choose the PNG output format. It is useful to '
'reduce file sizes. Set to zero to turn off. Maximum value '
'is 256. It is off by default.'),
OptionRecommendation(name='dont_normalize', recommended_value=False,
help=_('Disable normalize (improve contrast) color range '
'for pictures. Default: False')),
help='Disable normalize (improve contrast) color range '
'for pictures. Default: False'),
OptionRecommendation(name='keep_aspect_ratio', recommended_value=False,
help=_('Maintain picture aspect ratio. Default is to fill the screen.')),
help='Maintain picture aspect ratio. Default is to fill the '
'screen.'),
OptionRecommendation(name='dont_sharpen', recommended_value=False,
help=_('Disable sharpening.')),
help='Disable sharpening.'),
OptionRecommendation(name='disable_trim', recommended_value=False,
help=_('Disable trimming of comic pages. For some comics, '
'trimming might remove content as well as borders.')),
help='Disable trimming of comic pages. For some comics, trimming '
'might remove content as well as borders.'),
OptionRecommendation(name='landscape', recommended_value=False,
help=_("Don't split landscape images into two portrait images")),
help="Don't split landscape images into two portrait images"),
OptionRecommendation(name='wide', recommended_value=False,
help=_("Keep aspect ratio and scale image using screen height as "
"image width for viewing in landscape mode.")),
help="Keep aspect ratio and scale image using screen height as "
"image width for viewing in landscape mode."),
OptionRecommendation(name='right2left', recommended_value=False,
help=_('Used for right-to-left publications like manga. '
help='Used for right-to-left publications like manga. '
'Causes landscape pages to be split into portrait pages '
'from right to left.')),
'from right to left.'),
OptionRecommendation(name='despeckle', recommended_value=False,
help=_('Enable Despeckle. Reduces speckle noise. '
'May greatly increase processing time.')),
help='Enable Despeckle. Reduces speckle noise. May greatly '
'increase processing time.'),
OptionRecommendation(name='no_sort', recommended_value=False,
help=_("Don't sort the files found in the comic "
help="Don't sort the files found in the comic "
"alphabetically by name. Instead use the order they were "
"added to the comic.")),
"added to the comic."),
OptionRecommendation(name='output_format', choices=['png', 'jpg'],
recommended_value='png', help=_('The format that images in the created e-book '
'are converted to. You can experiment to see which format gives '
'you optimal size and look on your device.')),
recommended_value='png',
help='The format that images in the created e-book are '
'converted to. You can experiment to see which format '
'gives you optimal size and look on your device.'),
OptionRecommendation(name='no_process', recommended_value=False,
help=_("Apply no processing to the image")),
help="Apply no processing to the image"),
OptionRecommendation(name='dont_grayscale', recommended_value=False,
help=_('Do not convert the image to grayscale (black and white)')),
help='Do not convert the image to grayscale (black and white)'),
OptionRecommendation(name='comic_image_size', recommended_value=None,
help=_('Specify the image size as widthxheight pixels. Normally,'
help='Specify the image size as widthxheight pixels. Normally,'
' an image size is automatically calculated from the output '
'profile, this option overrides it.')),
'profile, this option overrides it.'),
OptionRecommendation(name='dont_add_comic_pages_to_toc', recommended_value=False,
help=_('When converting a CBC do not add links to each page to'
' the TOC. Note this only applies if the TOC has more than one'
' section')),
help='When converting a CBC do not add links to each page to'
' the TOC. Note this only applies if the TOC has more than '
'one section'),
}
recommendations = {
@@ -192,7 +195,7 @@ class ComicInput(InputFormatPlugin):
raise ValueError('No comic pages found in %s'%stream.name)
mi = MetaInformation(os.path.basename(stream.name).rpartition('.')[0],
[_('Unknown')])
['Unknown'])
opf = OPFCreator(os.getcwd(), mi)
entries = []
@@ -222,7 +225,7 @@ class ComicInput(InputFormatPlugin):
if len(comics) == 1:
wrappers = comics[0][2]
for i, x in enumerate(wrappers):
toc.add_item(href(x), None, _('Page')+' %d'%(i+1),
toc.add_item(href(x), None, 'Page %d' % (i+1),
play_order=i)
else:
po = 0
@@ -234,7 +237,7 @@ class ComicInput(InputFormatPlugin):
if not opts.dont_add_comic_pages_to_toc:
for i, x in enumerate(wrappers):
stoc.add_item(href(x), None,
_('Page')+' %d'%(i+1), play_order=po)
'Page %d' % (i+1), play_order=po)
po += 1
opf.set_toc(toc)
with open('metadata.opf', 'wb') as m, open('toc.ncx', 'wb') as n:
@@ -8,19 +8,21 @@ __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
class DOCXInput(InputFormatPlugin):
name = 'DOCX Input'
author = 'Kovid Goyal'
description = _('Convert DOCX files (.docx and .docm) to HTML')
description = 'Convert DOCX files (.docx and .docm) to HTML'
file_types = {'docx', 'docm'}
commit_name = 'docx_input'
options = {
OptionRecommendation(name='docx_no_cover', recommended_value=False,
help=_('Normally, if a large image is present at the start of the document that looks like a cover, '
'it will be removed from the document and used as the cover for created e-book. This option '
'turns off that behavior.')),
help='Normally, if a large image is present at the start of the '
'document that looks like a cover, it will be removed from '
'the document and used as the cover for created e-book. This '
'option turns off that behavior.'),
OptionRecommendation(name='docx_no_pagebreaks_between_notes', recommended_value=False,
help=_('Do not insert a page break after every endnote.')),
help='Do not insert a page break after every endnote.'),
OptionRecommendation(name='docx_inline_subsup', recommended_value=False,
help=_('Render superscripts and subscripts so that they do not affect the line height.')),
help='Render superscripts and subscripts so that they do not '
'affect the line height.'),
}
recommendations = {('page_breaks_before', '/', OptionRecommendation.MED)}
@@ -19,52 +19,52 @@ class DOCXOutput(OutputFormatPlugin):
options = {
OptionRecommendation(name='docx_page_size', recommended_value='letter',
level=OptionRecommendation.LOW, choices=PAGE_SIZES,
help=_('The size of the page. Default is letter. Choices '
'are %s') % PAGE_SIZES),
help='The size of the page. Default is letter. Choices '
'are %s' % PAGE_SIZES),
OptionRecommendation(name='docx_custom_page_size', recommended_value=None,
help=_('Custom size of the document. Use the form widthxheight '
help='Custom size of the document. Use the form widthxheight '
'EG. `123x321` to specify the width and height (in pts). '
'This overrides any specified page-size.')),
'This overrides any specified page-size.'),
OptionRecommendation(name='docx_no_cover', recommended_value=False,
help=_('Do not insert the book cover as an image at the start of the document.'
' If you use this option, the book cover will be discarded.')),
help='Do not insert the book cover as an image at the start of the document.'
' If you use this option, the book cover will be discarded.'),
OptionRecommendation(name='preserve_cover_aspect_ratio', recommended_value=False,
help=_('Preserve the aspect ratio of the cover image instead of stretching'
' it out to cover the entire page.')),
help='Preserve the aspect ratio of the cover image instead of stretching'
' it out to cover the entire page.'),
OptionRecommendation(name='docx_no_toc', recommended_value=False,
help=_('Do not insert the table of contents as a page at the start of the document.')),
help='Do not insert the table of contents as a page at the start of the document.'),
OptionRecommendation(name='extract_to',
help=_('Extract the contents of the generated %s file to the '
help='Extract the contents of the generated %s file to the '
'specified directory. The contents of the directory are first '
'deleted, so be careful.') % 'DOCX'),
'deleted, so be careful.' % 'DOCX'),
OptionRecommendation(name='docx_page_margin_left', recommended_value=72.0,
level=OptionRecommendation.LOW,
help=_('The size of the left page margin, in pts. Default is 72pt.'
' Overrides the common left page margin setting.')
help='The size of the left page margin, in pts. Default is 72pt.'
' Overrides the common left page margin setting.'
),
OptionRecommendation(name='docx_page_margin_top', recommended_value=72.0,
level=OptionRecommendation.LOW,
help=_('The size of the top page margin, in pts. Default is 72pt.'
' Overrides the common top page margin setting, unless set to zero.')
help='The size of the top page margin, in pts. Default is 72pt.'
' Overrides the common top page margin setting, unless set to zero.'
),
OptionRecommendation(name='docx_page_margin_right', recommended_value=72.0,
level=OptionRecommendation.LOW,
help=_('The size of the right page margin, in pts. Default is 72pt.'
' Overrides the common right page margin setting, unless set to zero.')
help='The size of the right page margin, in pts. Default is 72pt.'
' Overrides the common right page margin setting, unless set to zero.'
),
OptionRecommendation(name='docx_page_margin_bottom', recommended_value=72.0,
level=OptionRecommendation.LOW,
help=_('The size of the bottom page margin, in pts. Default is 72pt.'
' Overrides the common bottom page margin setting, unless set to zero.')
help='The size of the bottom page margin, in pts. Default is 72pt.'
' Overrides the common bottom page margin setting, unless set to zero.'
),
}
@@ -3,8 +3,9 @@ import re
import shutil
import urllib.parse
from ebook_converter.customize.conversion import (OutputFormatPlugin,
OptionRecommendation)
from ebook_converter.customize.conversion import OutputFormatPlugin
from ebook_converter.customize.conversion import OptionRecommendation
from ebook_converter.ptempfile import TemporaryDirectory
from ebook_converter import CurrentDir
from ebook_converter.polyglot.builtins import as_bytes
@@ -53,78 +54,79 @@ class EPUBOutput(OutputFormatPlugin):
options = {
OptionRecommendation(name='extract_to',
help=_('Extract the contents of the generated %s file to the '
'specified directory. The contents of the directory are first '
'deleted, so be careful.') % 'EPUB'),
help='Extract the contents of the generated %s file to the '
'specified directory. The contents of the directory are '
'first deleted, so be careful.' % 'EPUB'),
OptionRecommendation(name='dont_split_on_page_breaks',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Turn off splitting at page breaks. Normally, input '
help='Turn off splitting at page breaks. Normally, input '
'files are automatically split at every page break into '
'two files. This gives an output e-book 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.'
)
),
OptionRecommendation(name='flow_size', recommended_value=260,
help=_('Split all HTML files larger than this size (in KB). '
help='Split all HTML files larger than this size (in KB). '
'This is necessary as most EPUB readers cannot handle large '
'file sizes. The default of %defaultKB is the size required '
'for Adobe Digital Editions. Set to 0 to disable size based splitting.')
'for Adobe Digital Editions. Set to 0 to disable size based '
'splitting.'
),
OptionRecommendation(name='no_default_epub_cover', recommended_value=False,
help=_('Normally, if the input file has no cover and you don\'t'
help='Normally, if the input file has no cover and you don\'t'
' specify one, a default cover is generated with the title, '
'authors, etc. This option disables the generation of this cover.')
'authors, etc. This option disables the generation of this cover.'
),
OptionRecommendation(name='no_svg_cover', recommended_value=False,
help=_('Do not use SVG for the book cover. Use this option if '
help='Do not use SVG for the book cover. Use this option if '
'your EPUB is going to be used on a device that does not '
'support SVG, like the iPhone or the JetBook Lite. '
'Without this option, such devices will display the cover '
'as a blank page.')
'as a blank page.'
),
OptionRecommendation(name='preserve_cover_aspect_ratio',
recommended_value=False, help=_(
'When using an SVG cover, this option will cause the cover to scale '
'to cover the available screen area, but still preserve its aspect ratio '
'(ratio of width to height). That means there may be white borders '
'at the sides or top and bottom of the image, but the image will '
'never be distorted. Without this option the image may be slightly '
'distorted, but there will be no borders.'
)
recommended_value=False,
help='When using an SVG cover, this option will cause the cover '
'to scale to cover the available screen area, but still '
'preserve its aspect ratio (ratio of width to height). That '
'means there may be white borders at the sides or top and '
'bottom of the image, but the image will never be distorted. '
'Without this option the image may be slightly distorted, '
'but there will be no borders.'
),
OptionRecommendation(name='epub_flatten', recommended_value=False,
help=_('This option is needed only if you intend to use the EPUB'
help='This option is needed only if you intend to use the EPUB'
' with FBReaderJ. It will flatten the file system inside the'
' EPUB, putting all files into the top level.')
' EPUB, putting all files into the top level.'
),
OptionRecommendation(name='epub_inline_toc', recommended_value=False,
help=_('Insert an inline Table of Contents that will appear as part of the main book content.')
help='Insert an inline Table of Contents that will appear as part '
'of the main book content.'
),
OptionRecommendation(name='epub_toc_at_end', recommended_value=False,
help=_('Put the inserted inline Table of Contents at the end of the book instead of the start.')
help='Put the inserted inline Table of Contents at the end of '
'the book instead of the start.'
),
OptionRecommendation(name='toc_title', recommended_value=None,
help=_('Title for any generated in-line table of contents.')
help='Title for any generated in-line table of contents.'
),
OptionRecommendation(name='epub_version', recommended_value='2', choices=ui_data['versions'],
help=_('The version of the EPUB file to generate. EPUB 2 is the'
' most widely compatible, only use EPUB 3 if you know you'
' actually need it.')
),
help='The version of the EPUB file to generate. EPUB 2 is the '
'most widely compatible, only use EPUB 3 if you know you '
'actually need it.'
)
}
recommendations = {('pretty_print', True, OptionRecommendation.HIGH)}
@@ -219,7 +221,7 @@ class EPUBOutput(OutputFormatPlugin):
self.log.warn('This EPUB file has no Table of Contents. '
'Creating a default TOC')
first = next(iter(self.oeb.spine))
self.oeb.toc.add(_('Start'), first.href)
self.oeb.toc.add('Start', first.href)
from ebook_converter.ebooks.oeb.base import OPF
identifiers = oeb.metadata['identifier']
@@ -32,8 +32,7 @@ class FB2Input(InputFormatPlugin):
options = {
OptionRecommendation(name='no_inline_fb2_toc',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Do not insert a Table of Contents at the beginning of the book.'
)
help='Do not insert a Table of Contents at the beginning of the book.'
)}
def convert(self, stream, options, file_ext, log,
@@ -129,9 +128,9 @@ class FB2Input(InputFormatPlugin):
stream.seek(0)
mi = get_metadata(stream, 'fb2')
if not mi.title:
mi.title = _('Unknown')
mi.title = 'Unknown'
if not mi.authors:
mi.authors = [_('Unknown')]
mi.authors = ['Unknown']
cpath = None
if mi.cover_data and mi.cover_data[1]:
with open('fb2_cover_calibre_mi.jpg', 'wb') as f:
@@ -141,31 +141,31 @@ class FB2Output(OutputFormatPlugin):
'home_sex', # Erotica & sex
'home', # Other
]
ui_data = {
'sectionize': {
'toc': _('Section per entry in the ToC'),
'files': _('Section per file'),
'nothing': _('A single section')
},
'genres': FB2_GENRES,
}
ui_data = {'sectionize': {'toc': 'Section per entry in the ToC',
'files': 'Section per file',
'nothing': 'A single section'},
'genres': FB2_GENRES}
options = {
OptionRecommendation(name='sectionize',
recommended_value='files', level=OptionRecommendation.LOW,
choices=list(ui_data['sectionize']),
help=_('Specify how sections are created:\n'
help='Specify how sections are created:\n'
' * nothing: {nothing}\n'
' * files: {files}\n'
' * toc: {toc}\n'
'If ToC based generation fails, adjust the "Structure detection" and/or "Table of Contents" settings '
'(turn on "Force use of auto-generated Table of Contents").').format(**ui_data['sectionize'])
'If ToC based generation fails, adjust the "Structure '
'detection" and/or "Table of Contents" settings (turn on '
'"Force use of auto-generated Table of Contents")'
'.'.format(**ui_data['sectionize'])
),
OptionRecommendation(name='fb2_genre',
recommended_value='antique', level=OptionRecommendation.LOW,
choices=FB2_GENRES,
help=(_('Genre for the book. Choices: %s\n\n See: ') % ', '.join(FB2_GENRES)
) + 'http://www.fictionbook.org/index.php/Eng:FictionBook_2.1_genres ' + _('for a complete list with descriptions.')),
help='Genre for the book. Choices: %s\n\n See: http://www.'
'fictionbook.org/index.php/Eng:FictionBook_2.1_genres for a '
'complete list with descriptions.' % ', '.join(FB2_GENRES)),
}
def convert(self, oeb_book, output_path, input_plugin, opts, log):
@@ -35,27 +35,24 @@ class HTMLInput(InputFormatPlugin):
options = {
OptionRecommendation(name='breadth_first',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Traverse links in HTML files breadth first. Normally, '
help='Traverse links in HTML files breadth first. Normally, '
'they are traversed depth first.'
)
),
OptionRecommendation(name='max_levels',
recommended_value=5, level=OptionRecommendation.LOW,
help=_('Maximum levels of recursion when following links in '
help='Maximum levels of recursion when following links in '
'HTML files. Must be non-negative. 0 implies that no '
'links in the root HTML file are followed. Default is '
'%default.'
)
),
OptionRecommendation(name='dont_package',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Normally this input plugin re-arranges all the input '
help='Normally this input plugin re-arranges all the input '
'files into a standard folder hierarchy. Only use this option '
'if you know what you are doing as it can result in various '
'nasty side effects in the rest of the conversion pipeline.'
)
),
}
@@ -129,12 +126,12 @@ class HTMLInput(InputFormatPlugin):
a = string_to_authors(a)
if not a:
oeb.logger.warn('Creator not specified')
a = [self.oeb.translate(__('Unknown'))]
a = [self.oeb.translate('Unknown')]
for aut in a:
metadata.add('creator', aut)
if not metadata.title:
oeb.logger.warn('Title not specified')
metadata.add('title', self.oeb.translate(__('Unknown')))
metadata.add('title', self.oeb.translate('Unknown'))
bookid = str(uuid.uuid4())
metadata.add('identifier', bookid, id='uuid_id', scheme='uuid')
for ident in metadata.identifier:
@@ -30,18 +30,18 @@ class HTMLOutput(OutputFormatPlugin):
options = {
OptionRecommendation(name='template_css',
help=_('CSS file used for the output instead of the default file')),
help='CSS file used for the output instead of the default file'),
OptionRecommendation(name='template_html_index',
help=_('Template used for generation of the HTML index file instead of the default file')),
help='Template used for generation of the HTML index file instead of the default file'),
OptionRecommendation(name='template_html',
help=_('Template used for the generation of the HTML contents of the book instead of the default file')),
help='Template used for the generation of the HTML contents of the book instead of the default file'),
OptionRecommendation(name='extract_to',
help=_('Extract the contents of the generated ZIP file to the '
help='Extract the contents of the generated ZIP file to the '
'specified directory. WARNING: The contents of the directory '
'will be deleted.')
'will be deleted.'
),
}
@@ -59,16 +59,17 @@ class HTMLZInput(InputFormatPlugin):
# HTMLZ archive probably won't turn out as the user expects. With
# Multiple HTML files ZIP input should be used in place of HTMLZ.
if multiple_html:
log.warn(_('Multiple HTML files found in the archive. Only %s will be used.') % index)
log.warn('Multiple HTML files found in the archive. Only %s will '
'be used.' % index)
if index:
with open(index, 'rb') as tf:
html = tf.read()
else:
raise Exception(_('No top level HTML file found.'))
raise Exception('No top level HTML file found.')
if not html:
raise Exception(_('Top level HTML file %s is empty') % index)
raise Exception('Top level HTML file %s is empty' % index)
# Encoding
if options.input_encoding:
@@ -17,40 +17,33 @@ class HTMLZOutput(OutputFormatPlugin):
author = 'John Schember'
file_type = 'htmlz'
commit_name = 'htmlz_output'
ui_data = {
'css_choices': {
'class': _('Use CSS classes'),
'inline': _('Use the style attribute'),
'tag': _('Use HTML tags wherever possible')
},
'sheet_choices': {
'external': _('Use an external CSS file'),
'inline': _('Use a <style> tag in the HTML file')
}
}
ui_data = {'css_choices': {'class': 'Use CSS classes',
'inline': 'Use the style attribute',
'tag': 'Use HTML tags wherever possible'},
'sheet_choices': {'external': 'Use an external CSS file',
'inline': 'Use a <style> tag in the HTML '
'file'}}
options = {
OptionRecommendation(name='htmlz_css_type', recommended_value='class',
level=OptionRecommendation.LOW,
choices=list(ui_data['css_choices']),
help=_('Specify the handling of CSS. Default is class.\n'
help='Specify the handling of CSS. Default is class.\n'
'class: {class}\n'
'inline: {inline}\n'
'tag: {tag}'
).format(**ui_data['css_choices'])),
'tag: {tag}'.format(**ui_data['css_choices'])),
OptionRecommendation(name='htmlz_class_style', recommended_value='external',
level=OptionRecommendation.LOW,
choices=list(ui_data['sheet_choices']),
help=_('How to handle the CSS when using css-type = \'class\'.\n'
help='How to handle the CSS when using css-type = \'class\'.\n'
'Default is external.\n'
'external: {external}\n'
'inline: {inline}'
).format(**ui_data['sheet_choices'])),
'inline: {inline}'.format(**ui_data['sheet_choices'])),
OptionRecommendation(name='htmlz_title_filename',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('If set this option causes the file name of the HTML file'
' inside the HTMLZ archive to be based on the book title.')
),
help='If set this option causes the file name of the HTML file '
'inside the HTMLZ archive to be based on the book title.'
)
}
def convert(self, oeb_book, output_path, input_plugin, opts, log):
@@ -24,7 +24,7 @@ class LRFOptions(object):
if val < 0:
setattr(opts, attr, 0)
self.title = None
self.author = self.publisher = _('Unknown')
self.author = self.publisher = 'Unknown'
self.title_sort = self.author_sort = ''
for x in m.creator:
if x.role == 'aut':
@@ -91,43 +91,44 @@ class LRFOutput(OutputFormatPlugin):
options = {
OptionRecommendation(name='enable_autorotation', recommended_value=False,
help=_('Enable auto-rotation of images that are wider than the screen width.')
help='Enable auto-rotation of images that are wider than the '
'screen width.'
),
OptionRecommendation(name='wordspace',
recommended_value=2.5, level=OptionRecommendation.LOW,
help=_('Set the space between words in pts. Default is %default')
help='Set the space between words in pts. Default is %default'
),
OptionRecommendation(name='header', recommended_value=False,
help=_('Add a header to all the pages with title and author.')
help='Add a header to all the pages with title and author.'
),
OptionRecommendation(name='header_format', recommended_value="%t by %a",
help=_('Set the format of the header. %a is replaced by the author '
'and %t by the title. Default is %default')
help='Set the format of the header. %a is replaced by the author '
'and %t by the title. Default is %default'
),
OptionRecommendation(name='header_separation', recommended_value=0,
help=_('Add extra spacing below the header. Default is %default pt.')
help='Add extra spacing below the header. Default is %default pt.'
),
OptionRecommendation(name='minimum_indent', recommended_value=0,
help=_('Minimum paragraph indent (the indent of the first line '
'of a paragraph) in pts. Default: %default')
help='Minimum paragraph indent (the indent of the first line '
'of a paragraph) in pts. Default: %default'
),
OptionRecommendation(name='render_tables_as_images',
recommended_value=False,
help=_('This option has no effect')
help='This option has no effect'
),
OptionRecommendation(name='text_size_multiplier_for_rendered_tables',
recommended_value=1.0,
help=_('Multiply the size of text in rendered tables by this '
'factor. Default is %default')
help='Multiply the size of text in rendered tables by this '
'factor. Default is %default'
),
OptionRecommendation(name='serif_family', recommended_value=None,
help=_('The serif family of fonts to embed')
help='The serif family of fonts to embed'
),
OptionRecommendation(name='sans_family', recommended_value=None,
help=_('The sans-serif family of fonts to embed')
help='The sans-serif family of fonts to embed'
),
OptionRecommendation(name='mono_family', recommended_value=None,
help=_('The monospace family of fonts to embed')
help='The monospace family of fonts to embed'
),
}
@@ -151,7 +152,7 @@ class LRFOutput(OutputFormatPlugin):
book = Book(title=opts.title, author=opts.author,
bookid=uuid4().hex,
publisher='%s %s'%(__appname__, __version__),
category=_('Comic'), pagestyledefault=ps,
category='Comic', pagestyledefault=ps,
booksetting=BookSetting(screenwidth=width, screenheight=height))
for page in pages:
imageStream = ImageStream(page)
@@ -44,64 +44,64 @@ class MOBIOutput(OutputFormatPlugin):
options = {
OptionRecommendation(name='prefer_author_sort',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('When present, use author sort field as author.')
help='When present, use author sort field as author.'
),
OptionRecommendation(name='no_inline_toc',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Don\'t add Table of Contents to the book. Useful if '
'the book has its own table of contents.')),
help='Don\'t add Table of Contents to the book. Useful if '
'the book has its own table of contents.'),
OptionRecommendation(name='toc_title', recommended_value=None,
help=_('Title for any generated in-line table of contents.')
help='Title for any generated in-line table of contents.'
),
OptionRecommendation(name='dont_compress',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Disable compression of the file contents.')
help='Disable compression of the file contents.'
),
OptionRecommendation(name='personal_doc', recommended_value='[PDOC]',
help=_('Tag for MOBI files to be marked as personal documents.'
help='Tag for MOBI files to be marked as personal documents.'
' This option has no effect on the conversion. It is used'
' only when sending MOBI files to a device. If the file'
' being sent has the specified tag, it will be marked as'
' a personal document when sent to the Kindle.')
' a personal document when sent to the Kindle.'
),
OptionRecommendation(name='mobi_ignore_margins',
recommended_value=False,
help=_('Ignore margins in the input document. If False, then '
help='Ignore margins in the input document. If False, then '
'the MOBI output plugin will try to convert margins specified'
' in the input document, otherwise it will ignore them.')
' in the input document, otherwise it will ignore them.'
),
OptionRecommendation(name='mobi_toc_at_start',
recommended_value=False,
help=_('When adding the Table of Contents to the book, add it at the start of the '
'book instead of the end. Not recommended.')
help='When adding the Table of Contents to the book, add it at the start of the '
'book instead of the end. Not recommended.'
),
OptionRecommendation(name='extract_to',
help=_('Extract the contents of the generated %s file to the '
help='Extract the contents of the generated %s file to the '
'specified directory. The contents of the directory are first '
'deleted, so be careful.') % 'MOBI'
'deleted, so be careful.' % 'MOBI'
),
OptionRecommendation(name='share_not_sync', recommended_value=False,
help=_('Enable sharing of book content via Facebook etc. '
help='Enable sharing of book content via Facebook etc. '
' on the Kindle. WARNING: Using this feature means that '
' the book will not auto sync its last read position '
' on multiple devices. Complain to Amazon.')
' on multiple devices. Complain to Amazon.'
),
OptionRecommendation(name='mobi_keep_original_images',
recommended_value=False,
help=_('By default calibre converts all images to JPEG format '
help='By default calibre converts all images to JPEG format '
'in the output MOBI file. This is for maximum compatibility '
'as some older MOBI viewers have problems with other image '
'formats. This option tells calibre not to do this. '
'Useful if your document contains lots of GIF/PNG images that '
'become very large when converted to JPEG.')),
'become very large when converted to JPEG.'),
OptionRecommendation(name='mobi_file_type', choices=ui_data['file_types'], recommended_value='old',
help=_('By default calibre generates MOBI files that contain the '
help='By default calibre generates MOBI files that contain the '
'old MOBI 6 format. This format is compatible with all '
'devices. However, by changing this setting, you can tell '
'calibre to generate MOBI files that contain both MOBI 6 and '
'the new KF8 format, or only the new KF8 format. KF8 has '
'more features than MOBI 6, but only works with newer Kindles. '
'Allowed values: {}').format('old, both, new')),
'Allowed values: {}'.format('old, both, new'))
}
@@ -139,7 +139,7 @@ class MOBIOutput(OutputFormatPlugin):
# single section periodical
self.oeb.manifest.remove(one)
self.oeb.manifest.remove(two)
sections = [TOC(klass='section', title=_('All articles'),
sections = [TOC(klass='section', title='All articles',
href=self.oeb.spine[0].href)]
for x in toc:
sections[0].nodes.append(x)
@@ -274,34 +274,34 @@ class AZW3Output(OutputFormatPlugin):
options = {
OptionRecommendation(name='prefer_author_sort',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('When present, use author sort field as author.')
help='When present, use author sort field as author.'
),
OptionRecommendation(name='no_inline_toc',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Don\'t add Table of Contents to the book. Useful if '
'the book has its own table of contents.')),
help='Don\'t add Table of Contents to the book. Useful if '
'the book has its own table of contents.'),
OptionRecommendation(name='toc_title', recommended_value=None,
help=_('Title for any generated in-line table of contents.')
help='Title for any generated in-line table of contents.'
),
OptionRecommendation(name='dont_compress',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Disable compression of the file contents.')
help='Disable compression of the file contents.'
),
OptionRecommendation(name='mobi_toc_at_start',
recommended_value=False,
help=_('When adding the Table of Contents to the book, add it at the start of the '
'book instead of the end. Not recommended.')
help='When adding the Table of Contents to the book, add it at the start of the '
'book instead of the end. Not recommended.'
),
OptionRecommendation(name='extract_to',
help=_('Extract the contents of the generated %s file to the '
help='Extract the contents of the generated %s file to the '
'specified directory. The contents of the directory are first '
'deleted, so be careful.') % 'AZW3'),
'deleted, so be careful.' % 'AZW3'),
OptionRecommendation(name='share_not_sync', recommended_value=False,
help=_('Enable sharing of book content via Facebook etc. '
help='Enable sharing of book content via Facebook etc. '
' on the Kindle. WARNING: Using this feature means that '
' the book will not auto sync its last read position '
' on multiple devices. Complain to Amazon.')
),
' on multiple devices. Complain to Amazon.'
)
}
def convert(self, oeb, output_path, input_plugin, opts, log):
@@ -25,8 +25,10 @@ class PDBInput(InputFormatPlugin):
Reader = get_reader(header.ident)
if Reader is None:
raise PDBError('No reader available for format within container.\n Identity is %s. Book type is %s' %
(header.ident, IDENTITY_TO_NAME.get(header.ident, _('Unknown'))))
raise PDBError('No reader available for format within container.'
'\n Identity is %s. Book type is %s' %
(header.ident,
IDENTITY_TO_NAME.get(header.ident, 'Unknown')))
log.debug('Detected ebook format as: %s with identity: %s' % (IDENTITY_TO_NAME[header.ident], header.ident))
@@ -22,15 +22,16 @@ class PDBOutput(OutputFormatPlugin):
OptionRecommendation(name='format', recommended_value='doc',
level=OptionRecommendation.LOW,
short_switch='f', choices=list(ALL_FORMAT_WRITERS),
help=(_('Format to use inside the pdb container. Choices are:') + ' %s' % sorted(ALL_FORMAT_WRITERS))),
help='Format to use inside the pdb container. Choices are: %s' %
sorted(ALL_FORMAT_WRITERS)),
OptionRecommendation(name='pdb_output_encoding', recommended_value='cp1252',
level=OptionRecommendation.LOW,
help=_('Specify the character encoding of the output document. '
'The default is cp1252. Note: This option is not honored by all '
'formats.')),
help='Specify the character encoding of the output document. '
'The default is cp1252. Note: This option is not honored by '
'all formats.'),
OptionRecommendation(name='inline_toc',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Add Table of Contents to beginning of the book.')),
help='Add Table of Contents to beginning of the book.'),
}
def convert(self, oeb_book, output_path, input_plugin, opts, log):
@@ -19,13 +19,13 @@ class PDFInput(InputFormatPlugin):
options = {
OptionRecommendation(name='no_images', recommended_value=False,
help=_('Do not extract images from the document')),
help='Do not extract images from the document'),
OptionRecommendation(name='unwrap_factor', recommended_value=0.45,
help=_('Scale used to determine the length at which a line should '
help='Scale used to determine the length at which a line should '
'be unwrapped. Valid values are a decimal between 0 and 1. The '
'default is 0.45, just below the median line length.')),
'default is 0.45, just below the median line length.'),
OptionRecommendation(name='new_pdf_engine', recommended_value=False,
help=_('Use the new PDF conversion engine. Currently not operational.'))
help='Use the new PDF conversion engine. Currently not operational.')
}
def convert_new(self, stream, accelerators):
@@ -29,106 +29,104 @@ class PDFOutput(OutputFormatPlugin):
options = {
OptionRecommendation(name='use_profile_size', recommended_value=False,
help=_('Instead of using the paper size specified in the PDF Output options,'
help='Instead of using the paper size specified in the PDF Output options,'
' use a paper size corresponding to the current output profile.'
' Useful if you want to generate a PDF for viewing on a specific device.')),
' Useful if you want to generate a PDF for viewing on a specific device.'),
OptionRecommendation(name='unit', recommended_value='inch',
level=OptionRecommendation.LOW, short_switch='u', choices=UNITS,
help=_('The unit of measure for page sizes. Default is inch. Choices '
help='The unit of measure for page sizes. Default is inch. Choices '
'are {} '
'Note: This does not override the unit for margins!').format(', '.join(UNITS))),
'Note: This does not override the unit for margins!'.format(', '.join(UNITS))),
OptionRecommendation(name='paper_size', recommended_value='letter',
level=OptionRecommendation.LOW, choices=PAPER_SIZES,
help=_('The size of the paper. This size will be overridden when a '
help='The size of the paper. This size will be overridden when a '
'non default output profile is used. Default is letter. Choices '
'are {}').format(', '.join(PAPER_SIZES))),
'are {}'.format(', '.join(PAPER_SIZES))),
OptionRecommendation(name='custom_size', recommended_value=None,
help=_('Custom size of the document. Use the form widthxheight '
help='Custom size of the document. Use the form widthxheight '
'e.g. `123x321` to specify the width and height. '
'This overrides any specified paper-size.')),
'This overrides any specified paper-size.'),
OptionRecommendation(name='preserve_cover_aspect_ratio',
recommended_value=False,
help=_('Preserve the aspect ratio of the cover, instead'
help='Preserve the aspect ratio of the cover, instead'
' of stretching it to fill the full first page of the'
' generated pdf.')),
' generated pdf.'),
OptionRecommendation(name='pdf_serif_family',
recommended_value='Times', help=_(
'The font family used to render serif fonts. Will work only if the font is available system-wide.')),
recommended_value='Times', help=
'The font family used to render serif fonts. Will work only if the font is available system-wide.'),
OptionRecommendation(name='pdf_sans_family',
recommended_value='Helvetica', help=_(
'The font family used to render sans-serif fonts. Will work only if the font is available system-wide.')),
recommended_value='Helvetica', help=
'The font family used to render sans-serif fonts. Will work only if the font is available system-wide.'),
OptionRecommendation(name='pdf_mono_family',
recommended_value='Courier', help=_(
'The font family used to render monospace fonts. Will work only if the font is available system-wide.')),
recommended_value='Courier', help=
'The font family used to render monospace fonts. Will work only if the font is available system-wide.'),
OptionRecommendation(name='pdf_standard_font', choices=ui_data['font_types'],
recommended_value='serif', help=_(
'The font family used to render monospace fonts')),
recommended_value='serif', help=
'The font family used to render monospace fonts'),
OptionRecommendation(name='pdf_default_font_size',
recommended_value=20, help=_(
'The default font size')),
OptionRecommendation(name='pdf_mono_font_size',
recommended_value=16, help=_(
'The default font size for monospaced text')),
recommended_value=20, help='The default font size'),
OptionRecommendation(name='pdf_mono_font_size', recommended_value=16,
help='The default font size for monospaced text'),
OptionRecommendation(name='pdf_hyphenate', recommended_value=False,
help=_('Break long words at the end of lines. This can give the text at the right margin a more even appearance.')),
help='Break long words at the end of lines. This can give the text at the right margin a more even appearance.'),
OptionRecommendation(name='pdf_mark_links', recommended_value=False,
help=_('Surround all links with a red box, useful for debugging.')),
help='Surround all links with a red box, useful for debugging.'),
OptionRecommendation(name='pdf_page_numbers', recommended_value=False,
help=_('Add page numbers to the bottom of every page in the generated PDF file. If you '
help='Add page numbers to the bottom of every page in the generated PDF file. If you '
'specify a footer template, it will take precedence '
'over this option.')),
'over this option.'),
OptionRecommendation(name='pdf_footer_template', recommended_value=None,
help=_('An HTML template used to generate %s on every page.'
' The strings _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_ will be replaced by their current values.')%_('footers')),
help='An HTML template used to generate %s on every page.'
' The strings _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_ will be replaced by their current values.' % 'footers'),
OptionRecommendation(name='pdf_header_template', recommended_value=None,
help=_('An HTML template used to generate %s on every page.'
' The strings _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_ will be replaced by their current values.')%_('headers')),
help='An HTML template used to generate %s on every page.'
' The strings _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_ will be replaced by their current values.' % 'headers'),
OptionRecommendation(name='pdf_add_toc', recommended_value=False,
help=_('Add a Table of Contents at the end of the PDF that lists page numbers. '
'Useful if you want to print out the PDF. If this PDF is intended for electronic use, use the PDF Outline instead.')),
help='Add a Table of Contents at the end of the PDF that lists page numbers. '
'Useful if you want to print out the PDF. If this PDF is intended for electronic use, use the PDF Outline instead.'),
OptionRecommendation(name='toc_title', recommended_value=None,
help=_('Title for generated table of contents.')
help='Title for generated table of contents.'
),
OptionRecommendation(name='pdf_page_margin_left', recommended_value=72.0,
level=OptionRecommendation.LOW,
help=_('The size of the left page margin, in pts. Default is 72pt.'
' Overrides the common left page margin setting.')
help='The size of the left page margin, in pts. Default is 72pt.'
' Overrides the common left page margin setting.'
),
OptionRecommendation(name='pdf_page_margin_top', recommended_value=72.0,
level=OptionRecommendation.LOW,
help=_('The size of the top page margin, in pts. Default is 72pt.'
' Overrides the common top page margin setting, unless set to zero.')
help='The size of the top page margin, in pts. Default is 72pt.'
' Overrides the common top page margin setting, unless set to zero.'
),
OptionRecommendation(name='pdf_page_margin_right', recommended_value=72.0,
level=OptionRecommendation.LOW,
help=_('The size of the right page margin, in pts. Default is 72pt.'
' Overrides the common right page margin setting, unless set to zero.')
help='The size of the right page margin, in pts. Default is 72pt.'
' Overrides the common right page margin setting, unless set to zero.'
),
OptionRecommendation(name='pdf_page_margin_bottom', recommended_value=72.0,
level=OptionRecommendation.LOW,
help=_('The size of the bottom page margin, in pts. Default is 72pt.'
' Overrides the common bottom page margin setting, unless set to zero.')
help='The size of the bottom page margin, in pts. Default is 72pt.'
' Overrides the common bottom page margin setting, unless set to zero.'
),
OptionRecommendation(name='pdf_use_document_margins', recommended_value=False,
help=_('Use the page margins specified in the input document via @page CSS rules.'
help='Use the page margins specified in the input document via @page CSS rules.'
' This will cause the margins specified in the conversion settings to be ignored.'
' If the document does not specify page margins, the conversion settings will be used as a fallback.')
' If the document does not specify page margins, the conversion settings will be used as a fallback.'
),
OptionRecommendation(name='pdf_page_number_map', recommended_value=None,
help=_('Adjust page numbers, as needed. Syntax is a JavaScript expression for the page number.'
' For example, "if (n < 3) 0; else n - 3;", where n is current page number.')
help='Adjust page numbers, as needed. Syntax is a JavaScript expression for the page number.'
' For example, "if (n < 3) 0; else n - 3;", where n is current page number.'
),
OptionRecommendation(name='uncompressed_pdf',
recommended_value=False, help=_(
'Generate an uncompressed PDF, useful for debugging.')
recommended_value=False, help=
'Generate an uncompressed PDF, useful for debugging.'
),
OptionRecommendation(name='pdf_odd_even_offset', recommended_value=0.0,
level=OptionRecommendation.LOW,
help=_(
help=
'Shift the text horizontally by the specified offset (in pts).'
' On odd numbered pages, it is shifted to the right and on even'
' numbered pages to the left. Use negative numbers for the opposite'
@@ -136,7 +134,6 @@ class PDFOutput(OutputFormatPlugin):
' are smaller than the specified offset. Shifting is done by setting'
' the PDF CropBox, not all software respects the CropBox.'
)
),
}
@@ -20,17 +20,17 @@ class PMLOutput(OutputFormatPlugin):
options = {
OptionRecommendation(name='pml_output_encoding', recommended_value='cp1252',
level=OptionRecommendation.LOW,
help=_('Specify the character encoding of the output document. '
'The default is cp1252.')),
help='Specify the character encoding of the output document. '
'The default is cp1252.'),
OptionRecommendation(name='inline_toc',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Add Table of Contents to beginning of the book.')),
help='Add Table of Contents to beginning of the book.'),
OptionRecommendation(name='full_image_depth',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Do not reduce the size or bit depth of images. Images '
help='Do not reduce the size or bit depth of images. Images '
'have their size and depth reduced by default to accommodate '
'applications that can not convert images on their '
'own such as Dropbook.')),
'own such as Dropbook.'),
}
def convert(self, oeb_book, output_path, input_plugin, opts, log):
@@ -18,7 +18,7 @@ class RBOutput(OutputFormatPlugin):
options = {
OptionRecommendation(name='inline_toc',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Add Table of Contents to beginning of the book.'))}
help='Add Table of Contents to beginning of the book.')}
def convert(self, oeb_book, output_path, input_plugin, opts, log):
from ebook_converter.ebooks.rb.writer import RBWriter
@@ -18,7 +18,7 @@ class RecipeInput(InputFormatPlugin):
name = 'Recipe Input'
author = 'Kovid Goyal'
description = _('Download periodical content from the internet')
description = 'Download periodical content from the internet'
file_types = {'recipe', 'downloaded_recipe'}
commit_name = 'recipe_input'
@@ -34,20 +34,19 @@ class RecipeInput(InputFormatPlugin):
options = {
OptionRecommendation(name='test', recommended_value=False,
help=_(
'Useful for recipe development. Forces'
' max_articles_per_feed to 2 and downloads at most 2 feeds.'
' You can change the number of feeds and articles by supplying optional arguments.'
' For example: --test 3 1 will download at most 3 feeds and only 1 article per feed.')),
help='Useful for recipe development. Forces max_articles_per_feed '
'to 2 and downloads at most 2 feeds. You can change the '
'number of feeds and articles by supplying optional '
'arguments. For example: --test 3 1 will download at most 3 '
'feeds and only 1 article per feed.'),
OptionRecommendation(name='username', recommended_value=None,
help=_('Username for sites that require a login to access '
'content.')),
help='Username for sites that require a login to access content.'),
OptionRecommendation(name='password', recommended_value=None,
help=_('Password for sites that require a login to access '
'content.')),
help='Password for sites that require a login to access content.'),
OptionRecommendation(name='dont_download_recipe',
recommended_value=False,
help=_('Do not download latest version of builtin recipes from the calibre server')),
help='Do not download latest version of builtin recipes from the '
'calibre server'),
OptionRecommendation(name='lrf', recommended_value=False,
help='Optimize fetching for subsequent conversion to LRF.'),
}
@@ -49,7 +49,8 @@ class RTFInput(InputFormatPlugin):
options = {
OptionRecommendation(name='ignore_wmf', recommended_value=False,
help=_('Ignore WMF images instead of replacing them with a placeholder image.')),
help='Ignore WMF images instead of replacing them with a '
'placeholder image.'),
}
def generate_xml(self, stream):
@@ -259,8 +260,9 @@ class RTFInput(InputFormatPlugin):
xml = self.generate_xml(stream.name)
except RtfInvalidCodeException as e:
self.log.exception('Unable to parse RTF')
raise ValueError(_('This RTF file has a feature calibre does not '
'support. Convert it to HTML first and then try it.\n%s')%e)
raise ValueError('This RTF file has a feature calibre does not '
'support. Convert it to HTML first and then try '
'it.\n%s' % e)
d = glob.glob(os.path.join('*_rtf_pict_dir', 'picts.rtf'))
if d:
@@ -303,9 +305,9 @@ class RTFInput(InputFormatPlugin):
stream.seek(0)
mi = get_metadata(stream, 'rtf')
if not mi.title:
mi.title = _('Unknown')
mi.title = 'Unknown'
if not mi.authors:
mi.authors = [_('Unknown')]
mi.authors = ['Unknown']
opf = OPFCreator(os.getcwd(), mi)
opf.create_manifest([(u'index.xhtml', None)])
opf.create_spine([u'index.xhtml'])
@@ -20,30 +20,30 @@ class SNBOutput(OutputFormatPlugin):
options = {
OptionRecommendation(name='snb_output_encoding', recommended_value='utf-8',
level=OptionRecommendation.LOW,
help=_('Specify the character encoding of the output document. '
'The default is utf-8.')),
help='Specify the character encoding of the output document. '
'The default is utf-8.'),
OptionRecommendation(name='snb_max_line_length',
recommended_value=0, level=OptionRecommendation.LOW,
help=_('The maximum number of characters per line. This splits on '
'the first space before the specified value. If no space is found '
'the line will be broken at the space after and will exceed the '
'specified value. Also, there is a minimum of 25 characters. '
'Use 0 to disable line splitting.')),
help='The maximum number of characters per line. This splits on '
'the first space before the specified value. If no space is '
'found the line will be broken at the space after and will '
'exceed the specified value. Also, there is a minimum of 25 '
'characters. Use 0 to disable line splitting.'),
OptionRecommendation(name='snb_insert_empty_line',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Specify whether or not to insert an empty line between '
'two paragraphs.')),
help='Specify whether or not to insert an empty line between two '
'paragraphs.'),
OptionRecommendation(name='snb_dont_indent_first_line',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Specify whether or not to insert two space characters '
'to indent the first line of each paragraph.')),
help='Specify whether or not to insert two space characters to '
'indent the first line of each paragraph.'),
OptionRecommendation(name='snb_hide_chapter_name',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Specify whether or not to hide the chapter title for each '
'chapter. Useful for image-only output (eg. comics).')),
help='Specify whether or not to hide the chapter title for each '
'chapter. Useful for image-only output (eg. comics).'),
OptionRecommendation(name='snb_full_screen',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Resize all the images for full screen view. ')),
help='Resize all the images for full screen view. '),
}
def convert(self, oeb_book, output_path, input_plugin, opts, log):
@@ -123,7 +123,7 @@ class SNBOutput(OutputFormatPlugin):
log.warn('This SNB file has no Table of Contents. '
'Creating a default TOC')
first = next(iter(oeb_book.spine))
oeb_book.toc.add(_('Start page'), first.href)
oeb_book.toc.add('Start page', first.href)
else:
first = next(iter(oeb_book.spine))
if oeb_book.toc[0].href != first.href:
@@ -133,9 +133,9 @@ class SNBOutput(OutputFormatPlugin):
# the tocInfoTree directly instead of modifying the toc
ch = etree.SubElement(tocBody, "chapter")
ch.set("src", ProcessFileName(first.href) + ".snbc")
ch.text = _('Cover pages')
ch.text = 'Cover pages'
outputFiles[first.href] = []
outputFiles[first.href].append(("", _("Cover pages")))
outputFiles[first.href].append(("", "Cover pages"))
for tocitem in oeb_book.toc:
if tocitem.href.find('#') != -1:
@@ -148,10 +148,12 @@ class SNBOutput(OutputFormatPlugin):
else:
outputFiles[item[0]] = []
if "" not in outputFiles[item[0]]:
outputFiles[item[0]].append(("", tocitem.title + _(" (Preface)")))
outputFiles[item[0]].append(("",
tocitem.title +
" (Preface)"))
ch = etree.SubElement(tocBody, "chapter")
ch.set("src", ProcessFileName(item[0]) + ".snbc")
ch.text = tocitem.title + _(" (Preface)")
ch.text = tocitem.title + " (Preface)"
outputFiles[item[0]].append((item[1], tocitem.title))
else:
if tocitem.href in outputFiles:
@@ -200,7 +202,8 @@ class SNBOutput(OutputFormatPlugin):
f.write(etree.tostring(oldTree, pretty_print=True, encoding='utf-8'))
else:
log.debug('Merge %s with last TOC item...' % item.href)
snbwriter.merge_content(oldTree, oeb_book, item, [('', _("Start"))], opts)
snbwriter.merge_content(oldTree, oeb_book, item,
[('', "Start")], opts)
# Output the last one if needed
log.debug('Output the last modified chapter again: %s' % lastName)
@@ -19,8 +19,8 @@ class TCROutput(OutputFormatPlugin):
options = {
OptionRecommendation(name='tcr_output_encoding', recommended_value='utf-8',
level=OptionRecommendation.LOW,
help=_('Specify the character encoding of the output document. '
'The default is utf-8.'))}
help='Specify the character encoding of the output document. '
'The default is utf-8.')}
def convert(self, oeb_book, output_path, input_plugin, opts, log):
from ebook_converter.ebooks.txt.txtml import TXTMLizer
@@ -9,23 +9,23 @@ __copyright__ = '2009, John Schember <john@nachtimwald.com>'
__docformat__ = 'restructuredtext en'
MD_EXTENSIONS = {
'abbr': _('Abbreviations'),
'admonition': _('Support admonitions'),
'attr_list': _('Add attribute to HTML tags'),
'codehilite': _('Add code highlighting via Pygments'),
'def_list': _('Definition lists'),
'extra': _('Enables various common extensions'),
'fenced_code': _('Alternative code block syntax'),
'footnotes': _('Footnotes'),
'legacy_attrs': _('Use legacy element attributes'),
'legacy_em': _('Use legacy underscore handling for connected words'),
'meta': _('Metadata in the document'),
'nl2br': _('Treat newlines as hard breaks'),
'sane_lists': _('Do not allow mixing list types'),
'smarty': _('Use markdown\'s internal smartypants parser'),
'tables': _('Support tables'),
'toc': _('Generate a table of contents'),
'wikilinks': _('Wiki style links'),
'abbr': 'Abbreviations',
'admonition': 'Support admonitions',
'attr_list': 'Add attribute to HTML tags',
'codehilite': 'Add code highlighting via Pygments',
'def_list': 'Definition lists',
'extra': 'Enables various common extensions',
'fenced_code': 'Alternative code block syntax',
'footnotes': 'Footnotes',
'legacy_attrs': 'Use legacy element attributes',
'legacy_em': 'Use legacy underscore handling for connected words',
'meta': 'Metadata in the document',
'nl2br': 'Treat newlines as hard breaks',
'sane_lists': 'Do not allow mixing list types',
'smarty': 'Use markdown\'s internal smartypants parser',
'tables': 'Support tables',
'toc': 'Generate a table of contents',
'wikilinks': 'Wiki style links',
}
@@ -39,57 +39,67 @@ class TXTInput(InputFormatPlugin):
ui_data = {
'md_extensions': MD_EXTENSIONS,
'paragraph_types': {
'auto': _('Try to auto detect paragraph type'),
'block': _('Treat a blank line as a paragraph break'),
'single': _('Assume every line is a paragraph'),
'print': _('Assume every line starting with 2+ spaces or a tab starts a paragraph'),
'unformatted': _('Most lines have hard line breaks, few/no blank lines or indents'),
'off': _('Don\'t modify the paragraph structure'),
'auto': 'Try to auto detect paragraph type',
'block': 'Treat a blank line as a paragraph break',
'single': 'Assume every line is a paragraph',
'print': 'Assume every line starting with 2+ spaces or a tab '
'starts a paragraph',
'unformatted': 'Most lines have hard line breaks, few/no blank '
'lines or indents',
'off': 'Don\'t modify the paragraph structure',
},
'formatting_types': {
'auto': _('Automatically decide which formatting processor to use'),
'plain': _('No formatting'),
'heuristic': _('Use heuristics to determine chapter headings, italics, etc.'),
'textile': _('Use the TexTile markup language'),
'markdown': _('Use the Markdown markup language')
'auto': 'Automatically decide which formatting processor to use',
'plain': 'No formatting',
'heuristic': 'Use heuristics to determine chapter headings, '
'italics, etc.',
'textile': 'Use the TexTile markup language',
'markdown': 'Use the Markdown markup language'
},
}
options = {
OptionRecommendation(name='formatting_type', recommended_value='auto',
choices=list(ui_data['formatting_types']),
help=_('Formatting used within the document.\n'
help='Formatting used within the document.\n'
'* auto: {auto}\n'
'* plain: {plain}\n'
'* heuristic: {heuristic}\n'
'* textile: {textile}\n'
'* markdown: {markdown}\n'
'To learn more about markdown see {url}').format(
url='https://daringfireball.net/projects/markdown/', **ui_data['formatting_types'])
'To learn more about markdown see '
'{url}'.format(url='https://daringfireball.net/projects/'
'markdown/',
**ui_data['formatting_types'])
),
OptionRecommendation(name='paragraph_type', recommended_value='auto',
choices=list(ui_data['paragraph_types']),
help=_('Paragraph structure to assume. The value of "off" is useful for formatted documents such as Markdown or Textile. '
help='Paragraph structure to assume. The value of "off" is useful '
'for formatted documents such as Markdown or Textile. '
'Choices are:\n'
'* auto: {auto}\n'
'* block: {block}\n'
'* single: {single}\n'
'* print: {print}\n'
'* unformatted: {unformatted}\n'
'* off: {off}').format(**ui_data['paragraph_types'])
'* off: {off}'.format(**ui_data['paragraph_types'])
),
OptionRecommendation(name='preserve_spaces', recommended_value=False,
help=_('Normally extra spaces are condensed into a single space. '
'With this option all spaces will be displayed.')),
help='Normally extra spaces are condensed into a single space. '
'With this option all spaces will be displayed.'),
OptionRecommendation(name='txt_in_remove_indents', recommended_value=False,
help=_('Normally extra space at the beginning of lines is retained. '
'With this option they will be removed.')),
OptionRecommendation(name="markdown_extensions", recommended_value='footnotes, tables, toc',
help=_('Enable extensions to markdown syntax. Extensions are formatting that is not part '
'of the standard markdown format. The extensions enabled by default: %default.\n'
'To learn more about markdown extensions, see {}\n'
'This should be a comma separated list of extensions to enable:\n'
).format('https://python-markdown.github.io/extensions/') + '\n'.join('* %s: %s' % (k, MD_EXTENSIONS[k]) for k in sorted(MD_EXTENSIONS))),
help='Normally extra space at the beginning of lines is retained. '
'With this option they will be removed.'),
OptionRecommendation(name="markdown_extensions",
recommended_value='footnotes, tables, toc',
help='Enable extensions to markdown syntax. Extensions are '
'formatting that is not part of the standard markdown '
'format. The extensions enabled by default: %default.\nTo '
'learn more about markdown extensions, see {}\nThis should '
'be a comma separated list of extensions to enable:'
'\n'.format('https://python-markdown.github.io/extensions/') +
'\n'.join('* %s: %s' % (k, MD_EXTENSIONS[k])
for k in sorted(MD_EXTENSIONS))),
}
def shift_file(self, fname, data):
@@ -301,5 +311,5 @@ class TXTInput(InputFormatPlugin):
for item in oeb.spine:
if hasattr(item.data, 'xpath'):
for title in item.data.xpath('//*[local-name()="title"]'):
if title.text == _('Unknown'):
if title.text == 'Unknown':
title.text = self.html_postprocess_title
@@ -22,9 +22,9 @@ class TXTOutput(OutputFormatPlugin):
ui_data = {
'newline_types': NEWLINE_TYPES,
'formatting_types': {
'plain': _('Plain text'),
'markdown': _('Markdown formatted text'),
'textile': _('TexTile formatted text')
'plain': 'Plain text',
'markdown': 'Markdown formatted text',
'textile': 'TexTile formatted text'
},
}
@@ -32,52 +32,57 @@ class TXTOutput(OutputFormatPlugin):
OptionRecommendation(name='newline', recommended_value='system',
level=OptionRecommendation.LOW,
short_switch='n', choices=NEWLINE_TYPES,
help=_('Type of newline to use. Options are %s. Default is \'system\'. '
help='Type of newline to use. Options are %s. Default is \'system\'. '
'Use \'old_mac\' for compatibility with Mac OS 9 and earlier. '
'For macOS use \'unix\'. \'system\' will default to the newline '
'type used by this OS.') % sorted(NEWLINE_TYPES)),
'type used by this OS.' % sorted(NEWLINE_TYPES)),
OptionRecommendation(name='txt_output_encoding', recommended_value='utf-8',
level=OptionRecommendation.LOW,
help=_('Specify the character encoding of the output document. '
'The default is utf-8.')),
help='Specify the character encoding of the output document. '
'The default is utf-8.'),
OptionRecommendation(name='inline_toc',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Add Table of Contents to beginning of the book.')),
help='Add Table of Contents to beginning of the book.'),
OptionRecommendation(name='max_line_length',
recommended_value=0, level=OptionRecommendation.LOW,
help=_('The maximum number of characters per line. This splits on '
'the first space before the specified value. If no space is found '
'the line will be broken at the space after and will exceed the '
'specified value. Also, there is a minimum of 25 characters. '
'Use 0 to disable line splitting.')),
help='The maximum number of characters per line. This splits on '
'the first space before the specified value. If no space is '
'found the line will be broken at the space after and will '
'exceed the specified value. Also, there is a minimum of 25 '
'characters. Use 0 to disable line splitting.'),
OptionRecommendation(name='force_max_line_length',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Force splitting on the max-line-length value when no space '
'is present. Also allows max-line-length to be below the minimum')),
help='Force splitting on the max-line-length value when no space '
'is present. Also allows max-line-length to be below the '
'minimum'),
OptionRecommendation(name='txt_output_formatting',
recommended_value='plain',
choices=list(ui_data['formatting_types']),
help=_('Formatting used within the document.\n'
help='Formatting used within the document.\n'
'* plain: {plain}\n'
'* markdown: {markdown}\n'
'* textile: {textile}').format(**ui_data['formatting_types'])),
'* textile: {textile}'
''.format(**ui_data['formatting_types'])),
OptionRecommendation(name='keep_links',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Do not remove links within the document. This is only '
help='Do not remove links within the document. This is only '
'useful when paired with a txt-output-formatting option that '
'is not none because links are always removed with plain text output.')),
'is not none because links are always removed with plain '
'text output.'),
OptionRecommendation(name='keep_image_references',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Do not remove image references within the document. This is only '
'useful when paired with a txt-output-formatting option that '
'is not none because links are always removed with plain text output.')),
help='Do not remove image references within the document. This is '
'only useful when paired with a txt-output-formatting option '
'that is not none because links are always removed with '
'plain text output.'),
OptionRecommendation(name='keep_color',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Do not remove font color from output. This is only useful when '
'txt-output-formatting is set to textile. Textile is the only '
'formatting that supports setting font color. If this option is '
'not specified font color will not be set and default to the '
'color displayed by the reader (generally this is black).')),
help='Do not remove font color from output. This is only useful '
'when txt-output-formatting is set to textile. Textile is '
'the only formatting that supports setting font color. If '
'this option is not specified font color will not be set and '
'default to the color displayed by the reader (generally '
'this is black).')
}
def convert(self, oeb_book, output_path, input_plugin, opts, log):
+135 -164
View File
@@ -110,74 +110,70 @@ class Plumber(object):
OptionRecommendation(name='verbose',
recommended_value=0, level=OptionRecommendation.LOW,
short_switch='v',
help=_('Level of verbosity. Specify multiple times for greater '
help='Level of verbosity. Specify multiple times for greater '
'verbosity. Specifying it twice will result in full '
'verbosity, once medium verbosity and zero times least verbosity.')
'verbosity, once medium verbosity and zero times least verbosity.'
),
OptionRecommendation(name='debug_pipeline',
recommended_value=None, level=OptionRecommendation.LOW,
short_switch='d',
help=_('Save the output from different stages of the conversion '
help='Save the output from different stages of the conversion '
'pipeline to the specified '
'directory. Useful if you are unsure at which stage '
'of the conversion process a bug is occurring.')
'of the conversion process a bug is occurring.'
),
OptionRecommendation(name='input_profile',
recommended_value='default', level=OptionRecommendation.LOW,
choices=[x.short_name for x in input_profiles()],
help=_('Specify the input profile. The input profile gives the '
help='Specify the input profile. The input profile gives the '
'conversion system information on how to interpret '
'various information in the input document. For '
'example resolution dependent lengths (i.e. lengths in '
'pixels). Choices are:')+ ', '.join([
'pixels). Choices are:'+ ', '.join([
x.short_name for x in input_profiles()])
),
OptionRecommendation(name='output_profile',
recommended_value='default', level=OptionRecommendation.LOW,
choices=[x.short_name for x in output_profiles()],
help=_('Specify the output profile. The output profile '
help='Specify the output profile. The output profile '
'tells the conversion system how to optimize the '
'created document for the specified device (such as by resizing images for the device screen size). In some cases, '
'an output profile can be used to optimize the output for a particular device, but this is rarely necessary. '
'Choices are:') + ', '.join([
'Choices are:' + ', '.join([
x.short_name for x in output_profiles()])
),
OptionRecommendation(name='base_font_size',
recommended_value=0, level=OptionRecommendation.LOW,
help=_('The base font size in pts. All font sizes in the produced book '
help='The base font size in pts. All font sizes in the produced book '
'will be rescaled based on this size. By choosing a larger '
'size you can make the fonts in the output bigger and vice '
'versa. By default, when the value is zero, the base font size is chosen based on '
'the output profile you chose.'
)
),
OptionRecommendation(name='font_size_mapping',
recommended_value=None, level=OptionRecommendation.LOW,
help=_('Mapping from CSS font names to font sizes in pts. '
help='Mapping from CSS font names to font sizes in pts. '
'An example setting is 12,12,14,16,18,20,22,24. '
'These are the mappings for the sizes xx-small to xx-large, '
'with the final size being for huge fonts. The font '
'rescaling algorithm uses these sizes to intelligently '
'rescale fonts. The default is to use a mapping based on '
'the output profile you chose.'
)
),
OptionRecommendation(name='disable_font_rescaling',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Disable all rescaling of font sizes.'
)
help='Disable all rescaling of font sizes.'
),
OptionRecommendation(name='minimum_line_height',
recommended_value=120.0, level=OptionRecommendation.LOW,
help=_(
'The minimum line height, as a percentage of the element\'s '
help='The minimum line height, as a percentage of the element\'s '
'calculated font size. calibre will ensure that every element '
'has a line height of at least this setting, irrespective of '
'what the input document specifies. Set to zero to disable. '
@@ -185,134 +181,119 @@ OptionRecommendation(name='minimum_line_height',
'the direct line height specification, unless you know what '
'you are doing. For example, you can achieve "double spaced" '
'text by setting this to 240.'
)
),
OptionRecommendation(name='line_height',
recommended_value=0, level=OptionRecommendation.LOW,
help=_(
'The line height in pts. Controls spacing between consecutive '
help='The line height in pts. Controls spacing between consecutive '
'lines of text. Only applies to elements that do not define '
'their own line height. In most cases, the minimum line height '
'option is more useful. '
'By default no line height manipulation is performed.'
)
),
OptionRecommendation(name='embed_font_family',
recommended_value=None, level=OptionRecommendation.LOW,
help=_(
'Embed the specified font family into the book. This specifies '
help='Embed the specified font family into the book. This specifies '
'the "base" font used for the book. If the input document '
'specifies its own fonts, they may override this base font. '
'You can use the filter style information option to remove fonts from the '
'input document. Note that font embedding only works '
'with some output formats, principally EPUB, AZW3 and DOCX.')
'with some output formats, principally EPUB, AZW3 and DOCX.'
),
OptionRecommendation(name='embed_all_fonts',
recommended_value=False, level=OptionRecommendation.LOW,
help=_(
'Embed every font that is referenced in the input document '
help='Embed every font that is referenced in the input document '
'but not already embedded. This will search your system for the '
'fonts, and if found, they will be embedded. Embedding will only work '
'if the format you are converting to supports embedded fonts, such as '
'EPUB, AZW3, DOCX or PDF. Please ensure that you have the proper license for embedding '
'the fonts used in this document.'
)),
),
OptionRecommendation(name='subset_embedded_fonts',
recommended_value=False, level=OptionRecommendation.LOW,
help=_(
'Subset all embedded fonts. Every embedded font is reduced '
help='Subset all embedded fonts. Every embedded font is reduced '
'to contain only the glyphs used in this document. This decreases '
'the size of the font files. Useful if you are embedding a '
'particularly large font with lots of unused glyphs.')
'particularly large font with lots of unused glyphs.'
),
OptionRecommendation(name='linearize_tables',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Some badly designed documents use tables to control the '
help='Some badly designed documents use tables to control the '
'layout of text on the page. When converted these documents '
'often have text that runs off the page and other artifacts. '
'This option will extract the content from the tables and '
'present it in a linear fashion.'
)
),
OptionRecommendation(name='level1_toc',
recommended_value=None, level=OptionRecommendation.LOW,
help=_('XPath expression that specifies all tags that '
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.'
' See the XPath Tutorial in the calibre User Manual for examples.'
)
),
OptionRecommendation(name='level2_toc',
recommended_value=None, level=OptionRecommendation.LOW,
help=_('XPath expression that specifies all tags that should be '
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.'
' See the XPath Tutorial in the calibre User Manual for examples.'
)
),
OptionRecommendation(name='level3_toc',
recommended_value=None, level=OptionRecommendation.LOW,
help=_('XPath expression that specifies all tags that should be '
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.'
' See the XPath Tutorial in the calibre User Manual for examples.'
)
),
OptionRecommendation(name='use_auto_toc',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Normally, if the source file already has a Table of '
help='Normally, if the source file already has a Table of '
'Contents, it is used in preference to the auto-generated one. '
'With this option, the auto-generated one is always used.'
)
),
OptionRecommendation(name='no_chapters_in_toc',
recommended_value=False, level=OptionRecommendation.LOW,
help=_("Don't add auto-detected chapters to the Table of "
help="Don't add auto-detected chapters to the Table of "
'Contents.'
)
),
OptionRecommendation(name='toc_threshold',
recommended_value=6, level=OptionRecommendation.LOW,
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'
),
OptionRecommendation(name='max_toc_links',
recommended_value=50, level=OptionRecommendation.LOW,
help=_('Maximum number of links to insert into the TOC. Set to 0 '
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 threshold number of chapters were detected.'
)
),
OptionRecommendation(name='toc_filter',
recommended_value=None, level=OptionRecommendation.LOW,
help=_('Remove entries from the Table of Contents whose titles '
help='Remove entries from the Table of Contents whose titles '
'match the specified regular expression. Matching entries and all '
'their children are removed.'
)
),
OptionRecommendation(name='duplicate_links_in_toc',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('When creating a TOC from links in the input document, '
help='When creating a TOC from links in the input document, '
'allow duplicate entries, i.e. allow more than one entry '
'with the same text, provided that they point to a '
'different location.')
'different location.'
),
@@ -320,7 +301,7 @@ OptionRecommendation(name='chapter',
recommended_value="//*[((name()='h1' or name()='h2') and "
r"re:test(., '\s*((chapter|book|section|part)\s+)|((prolog|prologue|epilogue)(\s+|$))', 'i')) or @class "
"= 'chapter']", level=OptionRecommendation.LOW,
help=_('An XPath expression to detect chapter titles. The default '
help='An XPath expression to detect chapter titles. The default '
'is to consider <h1> or <h2> tags that contain the words '
'"chapter", "book", "section", "prologue", "epilogue" or "part" as chapter titles as '
'well as any tags that have class="chapter". The expression '
@@ -328,390 +309,380 @@ OptionRecommendation(name='chapter',
'detection, use the expression "/". See the XPath Tutorial '
'in the calibre User Manual for further help on using this '
'feature.'
)
),
OptionRecommendation(name='chapter_mark',
recommended_value='pagebreak', level=OptionRecommendation.LOW,
choices=['pagebreak', 'rule', 'both', 'none'],
help=_('Specify how to mark detected chapters. A value of '
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.')
'to mark chapters.'
),
OptionRecommendation(name='start_reading_at',
recommended_value=None, level=OptionRecommendation.LOW,
help=_('An XPath expression to detect the location in the document'
help='An XPath expression to detect the location in the document'
' at which to start reading. Some e-book reading programs'
' (most prominently the Kindle) use this location as the'
' position at which to open the book. See the XPath tutorial'
' in the calibre User Manual for further help using this'
' feature.')
' feature.'
),
OptionRecommendation(name='extra_css',
recommended_value=None, level=OptionRecommendation.LOW,
help=_('Either the path to a CSS stylesheet or raw CSS. '
help='Either the path to a CSS stylesheet or raw CSS. '
'This CSS will be appended to the style rules from '
'the source file, so it can be used to override those '
'rules.')
'rules.'
),
OptionRecommendation(name='transform_css_rules',
recommended_value=None, level=OptionRecommendation.LOW,
help=_('Rules for transforming the styles in this book. These'
' rules are applied after all other CSS processing is done.')
help='Rules for transforming the styles in this book. These'
' rules are applied after all other CSS processing is done.'
),
OptionRecommendation(name='filter_css',
recommended_value=None, level=OptionRecommendation.LOW,
help=_('A comma separated list of CSS properties that '
help='A comma separated list of CSS properties that '
'will be removed from all CSS style rules. This is useful '
'if the presence of some style information prevents it '
'from being overridden on your device. '
'For example: '
'font-family,color,margin-left,margin-right')
'font-family,color,margin-left,margin-right'
),
OptionRecommendation(name='expand_css',
recommended_value=False, level=OptionRecommendation.LOW,
help=_(
'By default, calibre will use the shorthand form for various'
help='By default, calibre will use the shorthand form for various'
' CSS properties such as margin, padding, border, etc. This'
' option will cause it to use the full expanded form instead.'
' Note that CSS is always expanded when generating EPUB files'
' with the output profile set to one of the Nook profiles'
' as the Nook cannot handle shorthand CSS.')
' as the Nook cannot handle shorthand CSS.'
),
OptionRecommendation(name='page_breaks_before',
recommended_value="//*[name()='h1' or name()='h2']",
level=OptionRecommendation.LOW,
help=_('An XPath expression. Page breaks are inserted '
'before the specified elements. To disable use the expression: /')
help='An XPath expression. Page breaks are inserted '
'before the specified elements. To disable use the expression: /'
),
OptionRecommendation(name='remove_fake_margins',
recommended_value=True, level=OptionRecommendation.LOW,
help=_('Some documents specify page margins by '
help='Some documents specify page margins by '
'specifying a left and right margin on each individual '
'paragraph. calibre will try to detect and remove these '
'margins. Sometimes, this can cause the removal of '
'margins that should not have been removed. In this '
'case you can disable the removal.')
'case you can disable the removal.'
),
OptionRecommendation(name='margin_top',
recommended_value=5.0, level=OptionRecommendation.LOW,
help=_('Set the top margin in pts. Default is %default. '
help='Set the top margin in pts. Default is %default. '
'Setting this to less than zero will cause no margin to be set '
'(the margin setting in the original document will be preserved). '
'Note: Page oriented formats such as PDF and DOCX have their own'
' margin settings that take precedence.')),
' margin settings that take precedence.'),
OptionRecommendation(name='margin_bottom',
recommended_value=5.0, level=OptionRecommendation.LOW,
help=_('Set the bottom margin in pts. Default is %default. '
help='Set the bottom margin in pts. Default is %default. '
'Setting this to less than zero will cause no margin to be set '
'(the margin setting in the original document will be preserved). '
'Note: Page oriented formats such as PDF and DOCX have their own'
' margin settings that take precedence.')),
' margin settings that take precedence.'),
OptionRecommendation(name='margin_left',
recommended_value=5.0, level=OptionRecommendation.LOW,
help=_('Set the left margin in pts. Default is %default. '
help='Set the left margin in pts. Default is %default. '
'Setting this to less than zero will cause no margin to be set '
'(the margin setting in the original document will be preserved). '
'Note: Page oriented formats such as PDF and DOCX have their own'
' margin settings that take precedence.')),
' margin settings that take precedence.'),
OptionRecommendation(name='margin_right',
recommended_value=5.0, level=OptionRecommendation.LOW,
help=_('Set the right margin in pts. Default is %default. '
help='Set the right margin in pts. Default is %default. '
'Setting this to less than zero will cause no margin to be set '
'(the margin setting in the original document will be preserved). '
'Note: Page oriented formats such as PDF and DOCX have their own'
' margin settings that take precedence.')),
' margin settings that take precedence.'),
OptionRecommendation(name='change_justification',
recommended_value='original', level=OptionRecommendation.LOW,
choices=['left','justify','original'],
help=_('Change text justification. A value of "left" converts all'
help='Change text justification. A value of "left" converts all'
' justified text in the source to left aligned (i.e. '
'unjustified) text. A value of "justify" converts all '
'unjustified text to justified. A value of "original" '
'(the default) does not change justification in the '
'source file. Note that only some output formats support '
'justification.')),
'justification.'),
OptionRecommendation(name='remove_paragraph_spacing',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Remove spacing between paragraphs. Also sets an indent on '
help='Remove spacing between paragraphs. Also sets an indent on '
'paragraphs of 1.5em. Spacing removal will not work '
'if the source file does not use paragraphs (<p> or <div> tags).')
'if the source file does not use paragraphs (<p> or <div> tags).'
),
OptionRecommendation(name='remove_paragraph_spacing_indent_size',
recommended_value=1.5, level=OptionRecommendation.LOW,
help=_('When calibre removes blank lines between paragraphs, it automatically '
help='When calibre removes blank lines between paragraphs, it automatically '
'sets a paragraph indent, to ensure that paragraphs can be easily '
'distinguished. This option controls the width of that indent (in em). '
'If you set this value negative, then the indent specified in the input '
'document is used, that is, calibre does not change the indentation.')
'document is used, that is, calibre does not change the indentation.'
),
OptionRecommendation(name='prefer_metadata_cover',
recommended_value=False, level=OptionRecommendation.LOW,
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.'
),
OptionRecommendation(name='insert_blank_line',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Insert a blank line between paragraphs. Will not work '
help='Insert a blank line between paragraphs. Will not work '
'if the source file does not use paragraphs (<p> or <div> tags).'
)
),
OptionRecommendation(name='insert_blank_line_size',
recommended_value=0.5, level=OptionRecommendation.LOW,
help=_('Set the height of the inserted blank lines (in em).'
help='Set the height of the inserted blank lines (in em).'
' The height of the lines between paragraphs will be twice the value'
' set here.')
' set here.'
),
OptionRecommendation(name='remove_first_image',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Remove the first image from the input e-book. Useful if the '
help='Remove the first image from the input e-book. Useful if the '
'input document has a cover image that is not identified as a cover. '
'In this case, if you set a cover in calibre, the output document will '
'end up with two cover images if you do not specify this option.'
)
),
OptionRecommendation(name='insert_metadata',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Insert the book metadata at the start of '
help='Insert the book metadata at the start of '
'the book. This is useful if your e-book reader does not support '
'displaying/searching metadata directly.'
)
),
OptionRecommendation(name='smarten_punctuation',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Convert plain quotes, dashes and ellipsis to their '
help='Convert plain quotes, dashes and ellipsis to their '
'typographically correct equivalents. For details, see '
'https://daringfireball.net/projects/smartypants'
)
),
OptionRecommendation(name='unsmarten_punctuation',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Convert fancy quotes, dashes and ellipsis to their '
help='Convert fancy quotes, dashes and ellipsis to their '
'plain equivalents.'
)
),
OptionRecommendation(name='read_metadata_from_opf',
recommended_value=None, level=OptionRecommendation.LOW,
short_switch='m',
help=_('Read metadata from the specified OPF file. Metadata read '
help='Read metadata from the specified OPF file. Metadata read '
'from this file will override any metadata in the source '
'file.')
'file.'
),
OptionRecommendation(name='asciiize',
recommended_value=False, level=OptionRecommendation.LOW,
help=(_('Transliterate unicode characters to an ASCII '
help='Transliterate unicode characters to an ASCII '
'representation. Use with care because this will replace '
'unicode characters with ASCII. For instance it will replace "%s" '
'with "Mikhail Gorbachiov". Also, note that in '
'cases where there are multiple representations of a character '
'(characters shared by Chinese and Japanese for instance) the '
'representation based on the current calibre interface language will be '
'used.')%
'used.' %
'\u041c\u0438\u0445\u0430\u0438\u043b '
'\u0413\u043e\u0440\u0431\u0430\u0447\u0451\u0432'
)
),
OptionRecommendation(name='keep_ligatures',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Preserve ligatures present in the input document. '
help='Preserve ligatures present in the input document. '
'A ligature is a special rendering of a pair of '
'characters like ff, fi, fl et cetera. '
'Most readers do not have support for '
'ligatures in their default fonts, so they are '
'unlikely to render correctly. By default, calibre '
'will turn a ligature into the corresponding pair of normal '
'characters. This option will preserve them instead.')
'characters. This option will preserve them instead.'
),
OptionRecommendation(name='title',
recommended_value=None, level=OptionRecommendation.LOW,
help=_('Set the title.')),
help='Set the title.'),
OptionRecommendation(name='authors',
recommended_value=None, level=OptionRecommendation.LOW,
help=_('Set the authors. Multiple authors should be separated by '
'ampersands.')),
help='Set the authors. Multiple authors should be separated by '
'ampersands.'),
OptionRecommendation(name='title_sort',
recommended_value=None, level=OptionRecommendation.LOW,
help=_('The version of the title to be used for sorting. ')),
help='The version of the title to be used for sorting. '),
OptionRecommendation(name='author_sort',
recommended_value=None, level=OptionRecommendation.LOW,
help=_('String to be used when sorting by author. ')),
help='String to be used when sorting by author. '),
OptionRecommendation(name='cover',
recommended_value=None, level=OptionRecommendation.LOW,
help=_('Set the cover to the specified file or URL')),
help='Set the cover to the specified file or URL'),
OptionRecommendation(name='comments',
recommended_value=None, level=OptionRecommendation.LOW,
help=_('Set the e-book description.')),
help='Set the e-book description.'),
OptionRecommendation(name='publisher',
recommended_value=None, level=OptionRecommendation.LOW,
help=_('Set the e-book publisher.')),
help='Set the e-book publisher.'),
OptionRecommendation(name='series',
recommended_value=None, level=OptionRecommendation.LOW,
help=_('Set the series this e-book belongs to.')),
help='Set the series this e-book belongs to.'),
OptionRecommendation(name='series_index',
recommended_value=None, level=OptionRecommendation.LOW,
help=_('Set the index of the book in this series.')),
help='Set the index of the book in this series.'),
OptionRecommendation(name='rating',
recommended_value=None, level=OptionRecommendation.LOW,
help=_('Set the rating. Should be a number between 1 and 5.')),
help='Set the rating. Should be a number between 1 and 5.'),
OptionRecommendation(name='isbn',
recommended_value=None, level=OptionRecommendation.LOW,
help=_('Set the ISBN of the book.')),
help='Set the ISBN of the book.'),
OptionRecommendation(name='tags',
recommended_value=None, level=OptionRecommendation.LOW,
help=_('Set the tags for the book. Should be a comma separated list.')),
help='Set the tags for the book. Should be a comma separated list.'),
OptionRecommendation(name='book_producer',
recommended_value=None, level=OptionRecommendation.LOW,
help=_('Set the book producer.')),
help='Set the book producer.'),
OptionRecommendation(name='language',
recommended_value=None, level=OptionRecommendation.LOW,
help=_('Set the language.')),
help='Set the language.'),
OptionRecommendation(name='pubdate',
recommended_value=None, level=OptionRecommendation.LOW,
help=_('Set the publication date (assumed to be in the local timezone, unless the timezone is explicitly specified)')),
help='Set the publication date (assumed to be in the local timezone, unless the timezone is explicitly specified)'),
OptionRecommendation(name='timestamp',
recommended_value=None, level=OptionRecommendation.LOW,
help=_('Set the book timestamp (no longer used anywhere)')),
help='Set the book timestamp (no longer used anywhere)'),
OptionRecommendation(name='enable_heuristics',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Enable heuristic processing. This option must be set for any '
'heuristic processing to take place.')),
help='Enable heuristic processing. This option must be set for any '
'heuristic processing to take place.'),
OptionRecommendation(name='markup_chapter_headings',
recommended_value=True, level=OptionRecommendation.LOW,
help=_('Detect unformatted chapter headings and sub headings. Change '
help='Detect unformatted chapter headings and sub headings. Change '
'them to h2 and h3 tags. This setting will not create a TOC, '
'but can be used in conjunction with structure detection to create '
'one.')),
'one.'),
OptionRecommendation(name='italicize_common_cases',
recommended_value=True, level=OptionRecommendation.LOW,
help=_('Look for common words and patterns that denote '
'italics and italicize them.')),
help='Look for common words and patterns that denote '
'italics and italicize them.'),
OptionRecommendation(name='fix_indents',
recommended_value=True, level=OptionRecommendation.LOW,
help=_('Turn indentation created from multiple non-breaking space entities '
'into CSS indents.')),
help='Turn indentation created from multiple non-breaking space entities '
'into CSS indents.'),
OptionRecommendation(name='html_unwrap_factor',
recommended_value=0.40, level=OptionRecommendation.LOW,
help=_('Scale used to determine the length at which a line should '
help='Scale used to determine the length at which a line should '
'be unwrapped. Valid values are a decimal between 0 and 1. The '
'default is 0.4, just below the median line length. If only a '
'few lines in the document require unwrapping this value should '
'be reduced')),
'be reduced'),
OptionRecommendation(name='unwrap_lines',
recommended_value=True, level=OptionRecommendation.LOW,
help=_('Unwrap lines using punctuation and other formatting clues.')),
help='Unwrap lines using punctuation and other formatting clues.'),
OptionRecommendation(name='delete_blank_paragraphs',
recommended_value=True, level=OptionRecommendation.LOW,
help=_('Remove empty paragraphs from the document when they exist between '
'every other paragraph')),
help='Remove empty paragraphs from the document when they exist between '
'every other paragraph'),
OptionRecommendation(name='format_scene_breaks',
recommended_value=True, level=OptionRecommendation.LOW,
help=_('Left aligned scene break markers are center aligned. '
'Replace soft scene breaks that use multiple blank lines with '
'horizontal rules.')),
help='Left aligned scene break markers are center aligned. Replace soft '
'scene breaks that use multiple blank lines with horizontal rules.'),
OptionRecommendation(name='replace_scene_breaks',
recommended_value='', level=OptionRecommendation.LOW,
help=_('Replace scene breaks with the specified text. By default, the '
'text from the input document is used.')),
help='Replace scene breaks with the specified text. By default, the text '
'from the input document is used.'),
OptionRecommendation(name='dehyphenate',
recommended_value=True, level=OptionRecommendation.LOW,
help=_('Analyze hyphenated words throughout the document. The '
'document itself is used as a dictionary to determine whether hyphens '
'should be retained or removed.')),
help='Analyze hyphenated words throughout the document. The document '
'itself is used as a dictionary to determine whether hyphens should '
'be retained or removed.'),
OptionRecommendation(name='renumber_headings',
recommended_value=True, level=OptionRecommendation.LOW,
help=_('Looks for occurrences of sequential <h1> or <h2> tags. '
'The tags are renumbered to prevent splitting in the middle '
'of chapter headings.')),
help='Looks for occurrences of sequential <h1> or <h2> tags. The tags are '
'renumbered to prevent splitting in the middle of chapter headings.'),
OptionRecommendation(name='sr1_search',
recommended_value='', level=OptionRecommendation.LOW,
help=_('Search pattern (regular expression) to be replaced with '
'sr1-replace.')),
help='Search pattern (regular expression) to be replaced with '
'sr1-replace.'),
OptionRecommendation(name='sr1_replace',
recommended_value='', level=OptionRecommendation.LOW,
help=_('Replacement to replace the text found with sr1-search.')),
help='Replacement to replace the text found with sr1-search.'),
OptionRecommendation(name='sr2_search',
recommended_value='', level=OptionRecommendation.LOW,
help=_('Search pattern (regular expression) to be replaced with '
'sr2-replace.')),
help='Search pattern (regular expression) to be replaced with '
'sr2-replace.'),
OptionRecommendation(name='sr2_replace',
recommended_value='', level=OptionRecommendation.LOW,
help=_('Replacement to replace the text found with sr2-search.')),
help='Replacement to replace the text found with sr2-search.'),
OptionRecommendation(name='sr3_search',
recommended_value='', level=OptionRecommendation.LOW,
help=_('Search pattern (regular expression) to be replaced with '
'sr3-replace.')),
help='Search pattern (regular expression) to be replaced with '
'sr3-replace.'),
OptionRecommendation(name='sr3_replace',
recommended_value='', level=OptionRecommendation.LOW,
help=_('Replacement to replace the text found with sr3-search.')),
help='Replacement to replace the text found with sr3-search.'),
OptionRecommendation(name='search_replace',
recommended_value=None, level=OptionRecommendation.LOW, help=_(
'Path to a file containing search and replace regular expressions. '
recommended_value=None, level=OptionRecommendation.LOW,
help='Path to a file containing search and replace regular expressions. '
'The file must contain alternating lines of regular expression '
'followed by replacement pattern (which can be an empty line). '
'The regular expression must be in the Python regex syntax and '
'the file must be UTF-8 encoded.')),
'the file must be UTF-8 encoded.'),
]
# }}}
@@ -818,7 +789,7 @@ OptionRecommendation(name='search_replace',
html_pat = re.compile(r'\.(x){0,1}htm(l){0,1}$', re.IGNORECASE)
html_files = [f for f in files if html_pat.search(f) is not None]
if not html_files:
raise ValueError(_('Could not find an e-book inside the archive'))
raise ValueError('Could not find an e-book inside the archive')
html_files = [(f, os.stat(f).st_size) for f in html_files]
html_files.sort(key=lambda x: x[1])
html_files = [f[0] for f in html_files]
@@ -908,14 +879,14 @@ OptionRecommendation(name='search_replace',
try:
val = float(val)
except ValueError:
self.log.warn(_('Values of series index and rating must'
' be numbers. Ignoring'), val)
self.log.warn('Values of series index and rating must'
' be numbers. Ignoring', val)
continue
elif x in ('timestamp', 'pubdate'):
try:
val = parse_date(val, assume_utc=x=='timestamp')
except:
self.log.exception(_('Failed to parse date/time') + ' ' + str(val))
self.log.exception('Failed to parse date/time %s', val)
continue
setattr(mi, x, val)
@@ -1095,7 +1066,7 @@ OptionRecommendation(name='search_replace',
self.flush()
return
self.ui_reporter(0.01, _('Converting input to HTML...'))
self.ui_reporter(0.01, 'Converting input to HTML...')
ir = CompositeProgressReporter(0.01, 0.34, self.ui_reporter)
self.input_plugin.report_progress = ir
if self.for_regex_wizard:
@@ -1129,7 +1100,7 @@ OptionRecommendation(name='search_replace',
self.input_plugin.specialize(self.oeb, self.opts, self.log,
self.output_fmt)
pr(0., _('Running transforms on e-book...'))
pr(0., 'Running transforms on e-book...')
self.oeb.plumber_output_format = self.output_fmt or ''
@@ -1266,7 +1237,7 @@ OptionRecommendation(name='search_replace',
self.log.info('Creating %s...'%self.output_plugin.name)
our = CompositeProgressReporter(0.67, 1., self.ui_reporter)
self.output_plugin.report_progress = our
our(0., _('Running %s plugin')%self.output_plugin.name)
our(0., 'Running %s plugin' % self.output_plugin.name)
with self.output_plugin:
self.output_plugin.convert(self.oeb, self.output, self.input_plugin,
self.opts, self.log)
+3 -9
View File
@@ -261,9 +261,8 @@ def format_fields(mi, prefs):
f = formatter()
def safe_format(field):
return f.safe_format(
getattr(prefs, field), mi, _('Template error'), mi, template_cache=_template_cache
)
return f.safe_format(getattr(prefs, field), mi, 'Template error', mi,
template_cache=_template_cache)
return map(safe_format, ('title_template', 'subtitle_template', 'footer_template'))
@@ -285,7 +284,7 @@ def preserve_fields(obj, fields):
def format_text(mi, prefs):
with preserve_fields(mi, 'authors formatted_series_index'):
mi.authors = [a for a in mi.authors if a != _('Unknown')]
mi.authors = [a for a in mi.authors if a != 'Unknown']
mi.formatted_series_index = fmt_sidx(mi.series_index or 0, use_roman=get_use_roman())
return tuple(format_fields(mi, prefs))
# }}}
@@ -358,7 +357,6 @@ class Style(object):
class Cross(Style):
NAME = 'The Cross'
GUI_NAME = _('The Cross')
def __call__(self, painter, rect, color_theme, title_block, subtitle_block, footer_block):
painter.fillRect(rect, self.color1)
@@ -379,7 +377,6 @@ class Cross(Style):
class Half(Style):
NAME = 'Half and Half'
GUI_NAME = _('Half and half')
def __call__(self, painter, rect, color_theme, title_block, subtitle_block, footer_block):
g = QLinearGradient(QPointF(0, 0), QPointF(0, rect.height()))
@@ -404,7 +401,6 @@ def draw_curved_line(painter_path, dx, dy, c1_frac, c1_amp, c2_frac, c2_amp):
class Banner(Style):
NAME = 'Banner'
GUI_NAME = _('Banner')
GRADE = 0.07
def calculate_margins(self, prefs):
@@ -474,7 +470,6 @@ class Banner(Style):
class Ornamental(Style):
NAME = 'Ornamental'
GUI_NAME = _('Ornamental')
# SVG vectors {{{
CORNER_VECTOR = "m 67.791903,64.260958 c -4.308097,-2.07925 -4.086719,-8.29575 0.334943,-9.40552 4.119758,-1.03399 8.732363,5.05239 5.393055,7.1162 -0.55,0.33992 -1,1.04147 -1,1.55902 0,1.59332 2.597425,1.04548 5.365141,-1.1316 1.999416,-1.57274 2.634859,-2.96609 2.634859,-5.7775 0,-9.55787 -9.827495,-13.42961 -24.43221,-9.62556 -3.218823,0.83839 -5.905663,1.40089 -5.970755,1.25 -0.06509,-0.1509 -0.887601,-1.19493 -1.827799,-2.32007 -1.672708,-2.00174 -1.636693,-2.03722 1.675668,-1.65052 1.861815,0.21736 6.685863,-0.35719 10.720107,-1.27678 12.280767,-2.79934 20.195487,-0.0248 22.846932,8.0092 3.187273,9.65753 -6.423297,17.7497 -15.739941,13.25313 z m 49.881417,-20.53932 c -3.19204,-2.701 -3.72967,-6.67376 -1.24009,-9.16334 2.48236,-2.48236 5.35141,-2.67905 7.51523,-0.51523 1.85966,1.85966 2.07045,6.52954 0.37143,8.22857 -2.04025,2.04024 3.28436,1.44595 6.92316,-0.77272 9.66959,-5.89579 0.88581,-18.22422 -13.0777,-18.35516 -5.28594,-0.0496 -10.31098,1.88721 -14.26764,5.4991 -1.98835,1.81509 -2.16454,1.82692 -2.7936,0.18763 -0.40973,-1.06774 0.12141,-2.82197 1.3628,-4.50104 2.46349,-3.33205 1.67564,-4.01299 -2.891784,-2.49938 -2.85998,0.94777 -3.81038,2.05378 -5.59837,6.51495 -1.184469,2.95536 -3.346819,6.86882 -4.805219,8.69657 -1.4584,1.82776 -2.65164,4.02223 -2.65164,4.87662 0,3.24694 -4.442667,0.59094 -5.872557,-3.51085 -1.361274,-3.90495 0.408198,-8.63869 4.404043,-11.78183 5.155844,-4.05558 1.612374,-3.42079 -9.235926,1.65457 -12.882907,6.02725 -16.864953,7.18038 -24.795556,7.18038 -8.471637,0 -13.38802,-1.64157 -17.634617,-5.88816 -2.832233,-2.83224 -3.849773,-4.81378 -4.418121,-8.6038 -1.946289,-12.9787795 8.03227,-20.91713135 19.767685,-15.7259993 5.547225,2.4538018 6.993631,6.1265383 3.999564,10.1557393 -5.468513,7.35914 -15.917883,-0.19431 -10.657807,-7.7041155 1.486298,-2.1219878 1.441784,-2.2225068 -0.984223,-2.2225068 -1.397511,0 -4.010527,1.3130878 -5.806704,2.9179718 -2.773359,2.4779995 -3.265777,3.5977995 -3.265777,7.4266705 0,5.10943 2.254112,8.84197 7.492986,12.40748 8.921325,6.07175 19.286666,5.61396 37.12088,-1.63946 15.35037,-6.24321 21.294999,-7.42408 34.886123,-6.92999 11.77046,0.4279 19.35803,3.05537 24.34054,8.42878 4.97758,5.3681 2.53939,13.58271 -4.86733,16.39873 -4.17361,1.58681 -11.00702,1.19681 -13.31978,-0.76018 z m 26.50156,-0.0787 c -2.26347,-2.50111 -2.07852,-7.36311 0.39995,-10.51398 2.68134,-3.40877 10.49035,-5.69409 18.87656,-5.52426 l 6.5685,0.13301 -7.84029,0.82767 c -8.47925,0.89511 -12.76997,2.82233 -16.03465,7.20213 -1.92294,2.57976 -1.96722,3.00481 -0.57298,5.5 1.00296,1.79495 2.50427,2.81821 4.46514,3.04333 2.92852,0.33623 2.93789,0.32121 1.08045,-1.73124 -1.53602,-1.69728 -1.64654,-2.34411 -0.61324,-3.58916 2.84565,-3.4288 7.14497,-0.49759 5.03976,3.43603 -1.86726,3.48903 -8.65528,4.21532 -11.3692,1.21647 z m -4.17462,-14.20302 c -0.38836,-0.62838 -0.23556,-1.61305 0.33954,-2.18816 1.3439,-1.34389 4.47714,-0.17168 3.93038,1.47045 -0.5566,1.67168 -3.38637,2.14732 -4.26992,0.71771 z m -8.48037,-9.1829 c -12.462,-4.1101 -12.53952,-4.12156 -25.49998,-3.7694 -24.020921,0.65269 -32.338219,0.31756 -37.082166,-1.49417 -5.113999,-1.95305 -8.192504,-6.3647405 -6.485463,-9.2940713 0.566827,-0.972691 1.020091,-1.181447 1.037211,-0.477701 0.01685,0.692606 1.268676,1.2499998 2.807321,1.2499998 1.685814,0 4.868609,1.571672 8.10041,4.0000015 4.221481,3.171961 6.182506,3.999221 9.473089,3.996261 l 4.149585,-0.004 -3.249996,-1.98156 c -3.056252,-1.863441 -4.051566,-3.8760635 -2.623216,-5.3044145 0.794,-0.794 6.188222,1.901516 9.064482,4.5295635 1.858669,1.698271 3.461409,1.980521 10.559493,1.859621 11.30984,-0.19266 20.89052,1.29095 31.97905,4.95208 7.63881,2.52213 11.51931,3.16471 22.05074,3.65141 7.02931,0.32486 13.01836,0.97543 13.30902,1.44571 0.29065,0.47029 -5.2356,0.83436 -12.28056,0.80906 -12.25942,-0.044 -13.34537,-0.2229 -25.30902,-4.16865 z" # noqa
@@ -541,7 +536,6 @@ class Ornamental(Style):
class Blocks(Style):
NAME = 'Blocks'
GUI_NAME = _('Blocks')
FOOTER_ALIGN = Qt.AlignRight | Qt.AlignTop
def __call__(self, painter, rect, color_theme, title_block, subtitle_block, footer_block):
+1 -1
View File
@@ -222,7 +222,7 @@ class DOCX(object):
@property
def metadata(self):
mi = Metadata(_('Unknown'))
mi = Metadata('Unknown')
dp_name, ap_name = self.get_document_properties_names()
if dp_name:
try:
+5 -4
View File
@@ -61,7 +61,7 @@ class Convert(object):
self.ws_pat = re.compile(r'[\n\r\t]')
self.log = self.docx.log
self.detect_cover = detect_cover
self.notes_text = notes_text or _('Notes')
self.notes_text = notes_text or 'Notes'
self.notes_nopb = notes_nopb
self.nosupsub = nosupsub
self.dest_dir = dest_dir or os.getcwd()
@@ -77,7 +77,7 @@ class Convert(object):
self.html = HTML(
HEAD(
META(charset='utf-8'),
TITLE(self.mi.title or _('Unknown')),
TITLE(self.mi.title or 'Unknown'),
LINK(rel='stylesheet', type='text/css', href='docx.css'),
),
self.body
@@ -390,8 +390,9 @@ class Convert(object):
def process_guide(E, guide):
if self.toc_anchor is not None:
guide.append(E.reference(
href='index.html#' + self.toc_anchor, title=_('Table of Contents'), type='toc'))
guide.append(E.reference(href='index.html#' + self.toc_anchor,
title='Table of Contents',
type='toc'))
toc_file = os.path.join(self.dest_dir, 'toc.ncx')
with open(os.path.join(self.dest_dir, 'metadata.opf'), 'wb') as of, open(toc_file, 'wb') as ncx:
opf.render(of, ncx, 'toc.ncx', process_guide=process_guide)
+2 -1
View File
@@ -202,7 +202,8 @@ class ImagesManager(object):
makeelement(parent, 'wp:extent', cx=str(width), cy=str(height))
makeelement(parent, 'wp:effectExtent', l='0', r='0', t='0', b='0')
makeelement(parent, 'wp:wrapTopAndBottom')
self.create_docx_image_markup(parent, 'cover.jpg', _('Cover'), img.rid, width, height)
self.create_docx_image_markup(parent, 'cover.jpg', 'Cover', img.rid,
width, height)
return ans
def write_cover_block(self, body, cover_image):
+1 -1
View File
@@ -161,7 +161,7 @@ class LinksManager(object):
pbb.set('{%s}val' % self.namespace.namespaces['w'], 'on')
for block in reversed(self.toc):
block.serialize(body, self.namespace.makeelement)
title = __('Table of Contents')
title = 'Table of Contents'
makeelement = self.namespace.makeelement
p = makeelement(body, 'w:p', append=False)
ppr = makeelement(p, 'w:pPr')
+14 -13
View File
@@ -12,11 +12,11 @@ __docformat__ = 'restructuredtext en'
class HTML2ZIP(FileTypePlugin):
name = 'HTML to ZIP'
author = 'Kovid Goyal'
description = textwrap.dedent(_('''\
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.\
'''))
description = textwrap.dedent('''\
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.\
''')
version = numeric_version
file_types = {'html', 'htm', 'xhtml', 'xhtm', 'shtm', 'shtml'}
supported_platforms = ['windows', 'osx', 'linux']
@@ -60,8 +60,8 @@ every time you add an HTML file to the library.\
return of.name
def customization_help(self, gui=False):
return _('Character encoding for the input HTML files. Common choices '
'include: cp1252, cp1251, latin1 and utf-8.')
return 'Character encoding for the input HTML files. Common choices '
'include: cp1252, cp1251, latin1 and utf-8.'
def do_user_config(self, parent=None):
'''
@@ -81,7 +81,7 @@ every time you add an HTML file to the library.\
button_box.accepted.connect(config_dialog.accept)
button_box.rejected.connect(config_dialog.reject)
config_dialog.setWindowTitle(_('Customize') + ' ' + self.name)
config_dialog.setWindowTitle('Customize' + ' ' + self.name)
from ebook_converter.customize.ui import (plugin_customization,
customize_plugin)
help_text = self.customization_help(gui=True)
@@ -90,11 +90,12 @@ every time you add an HTML file to the library.\
help_text.setTextInteractionFlags(Qt.LinksAccessibleByMouse | Qt.LinksAccessibleByKeyboard)
help_text.setOpenExternalLinks(True)
v.addWidget(help_text)
bf = QCheckBox(_('Add linked files in breadth first order'))
bf.setToolTip(_('Normally, when following links in HTML files'
' calibre does it depth first, i.e. if file A links to B and '
' C, but B links to D, the files are added in the order A, B, D, C. '
' With this option, they will instead be added as A, B, C, D'))
bf = QCheckBox('Add linked files in breadth first order')
bf.setToolTip('Normally, when following links in HTML files calibre '
'does it depth first, i.e. if file A links to B and C, '
'but B links to D, the files are added in the order A, '
'B, D, C. With this option, they will instead be added '
'as A, B, C, D')
sc = plugin_customization(self)
if not sc:
sc = ''
+1 -1
View File
@@ -45,7 +45,7 @@ class OEB2HTML(object):
try:
self.book_title = str(oeb_book.metadata.title[0])
except Exception:
self.book_title = _('Unknown')
self.book_title = 'Unknown'
self.links = {}
self.images = {}
self.base_hrefs = [item.href for item in oeb_book.spine]
+28 -24
View File
@@ -322,9 +322,9 @@ class HTMLConverter(object):
if not self.book_designer and self.is_book_designer(raw):
self.book_designer = True
self.log.info(_('\tBook Designer file detected.'))
self.log.info('\tBook Designer file detected.')
self.log.info(_('\tParsing HTML...'))
self.log.info('\tParsing HTML...')
if self.baen:
nmassage.extend(HTMLConverter.BAEN)
@@ -340,7 +340,7 @@ class HTMLConverter(object):
soup = html5_parser(raw)
if not self.baen and self.is_baen(soup):
self.baen = True
self.log.info(_('\tBaen file detected. Re-parsing...'))
self.log.info('\tBaen file detected. Re-parsing...')
return self.preprocess(raw)
if self.book_designer:
t = soup.find(id='BookTitle')
@@ -356,7 +356,7 @@ class HTMLConverter(object):
try:
with open(os.path.join(tdir, 'html2lrf-verbose.html'), 'wb') as f:
f.write(str(soup).encode('utf-8'))
self.log.info(_('Written preprocessed HTML to ')+f.name)
self.log.info('Written preprocessed HTML to '+f.name)
except:
pass
@@ -372,7 +372,8 @@ class HTMLConverter(object):
self.css[selector] = self.override_css[selector]
self.file_name = os.path.basename(path)
self.log.info(_('Processing %s')%(path if self.verbose else self.file_name))
self.log.info('Processing %s' % (path if self.verbose else
self.file_name))
if not os.path.exists(path):
path = path.replace('&', '%26') # convertlit replaces & with %26 in file names
@@ -385,7 +386,7 @@ class HTMLConverter(object):
else:
raw = xml_to_unicode(raw, self.verbose)[0]
soup = self.preprocess(raw)
self.log.info(_('\tConverting to BBeB...'))
self.log.info('\tConverting to BBeB...')
self.current_style = {}
self.page_break_found = False
if not isinstance(path, str):
@@ -533,12 +534,13 @@ class HTMLConverter(object):
for c in self.current_page.contents:
if isinstance(c, (TextBlock, ImageBlock)):
return c
raise ConversionError(_('Could not parse file: %s')%self.file_name)
raise ConversionError('Could not parse file: %s' %
self.file_name)
else:
try:
index = self.book.pages().index(opage)
except ValueError:
self.log.warning(_('%s is an empty file')%self.file_name)
self.log.warning('%s is an empty file' % self.file_name)
tb = self.book.create_text_block()
self.current_page.append(tb)
return tb
@@ -546,7 +548,8 @@ class HTMLConverter(object):
for c in page.contents:
if isinstance(c, (TextBlock, ImageBlock, Canvas)):
return c
raise ConversionError(_('Could not parse file: %s')%self.file_name)
raise ConversionError('Could not parse file: %s' %
self.file_name)
return top
@@ -557,9 +560,8 @@ class HTMLConverter(object):
para = children[i]
break
if para is None:
raise ConversionError(
_('Failed to parse link %(tag)s %(children)s')%dict(
tag=tag, children=children))
raise ConversionError('Failed to parse link %(tag)s %(children)s' %
dict(tag=tag, children=children))
text = self.get_text(tag, 1000)
if not text:
text = 'Link'
@@ -941,9 +943,8 @@ class HTMLConverter(object):
self.scaled_images[path] = pt
return pt.name
except (IOError, SystemError) as err: # PIL chokes on interlaced PNG images as well a some GIF images
self.log.warning(
_('Unable to process image %(path)s. Error: %(err)s')%dict(
path=path, err=err))
self.log.warning('Unable to process image %(path)s. Error: '
'%(err)s' % dict(path=path, err=err))
if width is None or height is None:
width, height = im.size
@@ -988,7 +989,8 @@ class HTMLConverter(object):
self.rotated_images[path] = pt
width, height = im.size
except IOError: # PIL chokes on interlaced PNG files and since auto-rotation is not critical we ignore the error
self.log.debug(_('Unable to process interlaced PNG %s')% original_path)
self.log.debug('Unable to process interlaced PNG %s' %
original_path)
finally:
pt.close()
@@ -1725,9 +1727,11 @@ class HTMLConverter(object):
try:
self.process_table(tag, tag_css)
except Exception as err:
self.log.warning(_('An error occurred while processing a table: %s. Ignoring table markup.')%repr(err))
self.log.warning('An error occurred while processing a '
'table: %s. Ignoring table markup.' %
repr(err))
self.log.exception('')
self.log.debug(_('Bad table:\n%s')%str(tag)[:300])
self.log.debug('Bad table:\n%s' % str(tag)[:300])
self.in_table = False
self.process_children(tag, tag_css, tag_pseudo_css)
finally:
@@ -1749,7 +1753,7 @@ class HTMLConverter(object):
for block, xpos, ypos, delta, targets in table.blocks(int(ps['textwidth']), int(ps['textheight'])):
if not block:
if ypos > int(ps['textheight']):
raise Exception(_('Table has cell that is too large'))
raise Exception('Table has cell that is too large')
canvases.append(Canvas(int(self.current_page.pageStyle.attrs['textwidth']), ypos+rowpad,
blockrule='block-fixed'))
for name in targets:
@@ -1813,10 +1817,10 @@ def process_file(path, options, logger):
tim.save(tf.name)
tpath = tf.name
except IOError as err: # PIL sometimes fails, for example on interlaced PNG files
logger.warn(_('Could not read cover image: %s'), err)
logger.warn('Could not read cover image: %s', err)
options.cover = None
else:
raise ConversionError(_('Cannot read from: %s')% (options.cover,))
raise ConversionError('Cannot read from: %s'% (options.cover,))
if not options.title:
options.title = default_title
@@ -1844,9 +1848,9 @@ def process_file(path, options, logger):
header = Paragraph()
fheader = options.headerformat
if not options.title:
options.title = _('Unknown')
options.title = 'Unknown'
if not options.author:
options.author = _('Unknown')
options.author = 'Unknown'
if not fheader:
fheader = "%t by %a"
fheader = re.sub(r'(?<!%)%t', options.title, fheader)
@@ -1946,4 +1950,4 @@ def try_opf(path, options, logger):
if not getattr(options, 'toc', None):
options.toc = opf.toc
except Exception:
logger.exception(_('Failed to process OPF file'))
logger.exception('Failed to process OPF file')
+2 -2
View File
@@ -344,9 +344,9 @@ class ResourceCollection(object):
res.set_basedir(path)
def MetaInformation(title, authors=(_('Unknown'),)):
def MetaInformation(title, authors=('Unknown',)):
''' Convenient encapsulation of book metadata, needed for compatibility
@param title: title or ``_('Unknown')`` or a MetaInformation object
@param title: title or ``'Unknown'`` or a MetaInformation object
@param authors: List of strings or []
'''
from ebook_converter.ebooks.metadata.book.base import Metadata
+6 -5
View File
@@ -40,8 +40,9 @@ class KPFExtract(FileTypePlugin):
name = 'KPF Extract'
author = 'Kovid Goyal'
description = _('Extract the source DOCX file from Amazon Kindle Create KPF files.'
' Note this will not contain any edits made in the Kindle Create program itself.')
description = ('Extract the source DOCX file from Amazon Kindle Create '
'KPF files. Note this will not contain any edits made in '
'the Kindle Create program itself.')
file_types = {'kpf'}
supported_platforms = ['windows', 'osx', 'linux']
on_import = True
@@ -62,9 +63,9 @@ class KPFExtract(FileTypePlugin):
class ArchiveExtract(FileTypePlugin):
name = 'Archive Extract'
author = 'Kovid Goyal'
description = _('Extract common e-book formats from archive files '
'(ZIP/RAR). Also try to autodetect if they are actually '
'CBZ/CBR files.')
description = ('Extract common e-book formats from archive files (ZIP/'
'RAR). Also try to autodetect if they are actually CBZ/CBR '
'files.')
file_types = {'zip', 'rar'}
supported_platforms = ['windows', 'osx', 'linux']
on_import = True
@@ -21,11 +21,11 @@ TOP_LEVEL_IDENTIFIERS = frozenset((
))
PUBLICATION_METADATA_FIELDS = frozenset((
'title', # title must never be None. Should be _('Unknown')
'title', # title must never be None. Should be 'Unknown'
# Pseudo field that can be set, but if not set is auto generated
# from title and languages
'title_sort',
'authors', # Ordered list. Must never be None, can be [_('Unknown')]
'authors', # Ordered list. Must never be None, can be ['Unknown']
'author_sort_map', # Map of sort strings for each author
# Pseudo field that can be set, but if not set is auto generated
# from authors and languages
+34 -34
View File
@@ -24,21 +24,19 @@ def human_readable(size, precision=2):
return ('%.'+str(precision)+'f'+ 'MB') % (size/(1024*1024),)
NULL_VALUES = {
'user_metadata': {},
'cover_data' : (None, None),
'tags' : [],
'identifiers' : {},
'languages' : [],
NULL_VALUES = {'user_metadata': {},
'cover_data': (None, None),
'tags': [],
'identifiers': {},
'languages': [],
'device_collections': [],
'author_sort_map': {},
'authors' : [_('Unknown')],
'author_sort' : _('Unknown'),
'title' : _('Unknown'),
'user_categories' : {},
'author_link_map' : {},
'language' : 'und'
}
'authors': ['Unknown'],
'author_sort': 'Unknown',
'title': 'Unknown',
'user_categories': {},
'author_link_map': {},
'language': 'und'}
field_metadata = FieldMetadata()
@@ -74,10 +72,10 @@ class Metadata(object):
'''
__calibre_serializable__ = True
def __init__(self, title, authors=(_('Unknown'),), other=None, template_cache=None,
formatter=None):
def __init__(self, title, authors=('Unknown',), other=None,
template_cache=None, formatter=None):
'''
@param title: title or ``_('Unknown')``
@param title: title or ``'Unknown'``
@param authors: List of strings or []
@param other: None or a metadata object
'''
@@ -101,7 +99,7 @@ class Metadata(object):
'''
Return True if the value of field is null in this object.
'null' means it is unknown or evaluates to False. So a title of
_('Unknown') is null or a language of 'und' is null.
'Unknown' is null or a language of 'und' is null.
Be careful with numeric fields since this will return True for zero as
well as None.
@@ -142,9 +140,7 @@ class Metadata(object):
if val is None:
d['#value#'] = 'RECURSIVE_COMPOSITE FIELD (Metadata) ' + field
val = d['#value#'] = self.formatter.safe_format(
d['display']['composite_template'],
self,
_('TEMPLATE ERROR'),
d['display']['composite_template'], self, 'TEMPLATE ERROR',
self, column_name=field,
template_cache=self.template_cache).strip()
return val
@@ -474,7 +470,7 @@ class Metadata(object):
if v not in (None, NULL_VALUES.get(attr, None)):
setattr(dest, attr, copy.deepcopy(v))
unknown = _('Unknown')
unknown = 'Unknown'
if other.title and other.title != unknown:
self.title = other.title
if hasattr(other, 'title_sort'):
@@ -658,7 +654,7 @@ class Metadata(object):
elif datatype == 'datetime':
res = format_date(res, cmeta['display'].get('date_format','dd MMM yyyy'))
elif datatype == 'bool':
res = _('Yes') if res else _('No')
res = 'Yes' if res else 'No'
elif datatype == 'rating':
res = '%.2g'%(res/2)
elif datatype in ['int', 'float']:
@@ -725,7 +721,7 @@ class Metadata(object):
if self.authors:
fmt('Author(s)', authors_to_string(self.authors) +
((' [' + self.author_sort + ']')
if self.author_sort and self.author_sort != _('Unknown') else ''))
if self.author_sort and self.author_sort != 'Unknown' else ''))
if self.publisher:
fmt('Publisher', self.publisher)
if getattr(self, 'book_producer', False):
@@ -764,22 +760,26 @@ class Metadata(object):
'''
from ebook_converter.ebooks.metadata import authors_to_string
from ebook_converter.utils.date import isoformat
ans = [(_('Title'), str(self.title))]
ans += [(_('Author(s)'), (authors_to_string(self.authors) if self.authors else _('Unknown')))]
ans += [(_('Publisher'), str(self.publisher))]
ans += [(_('Producer'), str(self.book_producer))]
ans += [(_('Comments'), str(self.comments))]
ans = [('Title', str(self.title))]
ans += [('Author(s)', (authors_to_string(self.authors)
if self.authors else 'Unknown'))]
ans += [('Publisher', str(self.publisher))]
ans += [('Producer', str(self.book_producer))]
ans += [('Comments', str(self.comments))]
ans += [('ISBN', str(self.isbn))]
ans += [(_('Tags'), ', '.join([str(t) for t in self.tags]))]
ans += [('Tags', ', '.join([str(t) for t in self.tags]))]
if self.series:
ans += [(_('Series'), str(self.series) + ' #%s'%self.format_series_index())]
ans += [(_('Languages'), ', '.join(self.languages))]
ans += [('Series', str(self.series) +
' #%s' % self.format_series_index())]
ans += [('Languages', ', '.join(self.languages))]
if self.timestamp is not None:
ans += [(_('Timestamp'), str(isoformat(self.timestamp, as_utc=False, sep=' ')))]
ans += [('Timestamp', str(isoformat(self.timestamp, as_utc=False,
sep=' ')))]
if self.pubdate is not None:
ans += [(_('Published'), str(isoformat(self.pubdate, as_utc=False, sep=' ')))]
ans += [('Published', str(isoformat(self.pubdate, as_utc=False,
sep=' ')))]
if self.rights is not None:
ans += [(_('Rights'), str(self.rights))]
ans += [('Rights', str(self.rights))]
for key in self.custom_field_keys():
val = self.get(key, None)
if val:
@@ -25,7 +25,7 @@ class SafeFormat(TemplateFormatter):
if hasattr(self.book, orig_key):
key = orig_key
else:
raise ValueError(_('Value: unknown field ') + orig_key)
raise ValueError('Value: unknown field ' + orig_key)
try:
b = self.book.get_user_metadata(key, False)
except:
+3 -3
View File
@@ -110,7 +110,7 @@ def get_metadata(stream):
root = _get_fbroot(get_fb2_data(stream)[0])
ctx = Context(root)
book_title = _parse_book_title(root, ctx)
authors = _parse_authors(root, ctx) or [_('Unknown')]
authors = _parse_authors(root, ctx) or ['Unknown']
# fallback for book_title
if book_title:
@@ -118,7 +118,7 @@ def get_metadata(stream):
else:
book_title = force_unicode(os.path.splitext(
os.path.basename(getattr(stream, 'name',
_('Unknown'))))[0])
'Unknown')))[0])
mi = MetaInformation(book_title, authors)
try:
@@ -173,7 +173,7 @@ def _parse_authors(root, ctx):
# if no author so far
if not authors:
authors.append(_('Unknown'))
authors.append('Unknown')
return authors
+3 -3
View File
@@ -151,10 +151,10 @@ def get_metadata_(src, encoding=None):
return ans
# Title
title = get('title') or title_tag.strip() or _('Unknown')
title = get('title') or title_tag.strip() or 'Unknown'
# Author
authors = authors_to_string(get_all('authors')) or _('Unknown')
authors = authors_to_string(get_all('authors')) or 'Unknown'
# Create MetaInformation with Title and Author
mi = Metadata(title, string_to_authors(authors))
@@ -340,7 +340,7 @@ class MetadataHtmlTest(unittest.TestCase):
def test_input_title(self):
stream_meta = get_metadata(self.get_stream('title'))
canon_meta = Metadata('A Title Tag &amp; Title Ⓒ', [_('Unknown')])
canon_meta = Metadata('A Title Tag &amp; Title Ⓒ', ['Unknown'])
self.compare_metadata(stream_meta, canon_meta)
def test_input_meta_single(self):
+5 -5
View File
@@ -33,7 +33,7 @@ def metadata_from_formats(formats, force_read_metadata=False, pattern=None):
except:
mi = metadata_from_filename(list(iter(formats))[0], pat=pattern)
if not mi.authors:
mi.authors = [_('Unknown')]
mi.authors = ['Unknown']
return mi
@@ -61,9 +61,9 @@ def _metadata_from_formats(formats, force_read_metadata=False, pattern=None):
return mi
if not mi.title:
mi.title = _('Unknown')
mi.title = 'Unknown'
if not mi.authors:
mi.authors = [_('Unknown')]
mi.authors = ['Unknown']
return mi
@@ -106,9 +106,9 @@ def _get_metadata(stream, stream_type, use_libprs_metadata,
base = metadata_from_filename(name, pat=pattern, fallback_pat=re.compile(
r'^(?P<title>.+) - (?P<author>[^-]+)$'))
if not base.authors:
base.authors = [_('Unknown')]
base.authors = ['Unknown']
if not base.title:
base.title = _('Unknown')
base.title = 'Unknown'
mi = MetaInformation(None, None)
if force_read_metadata or prefs['read_file_metadata']:
mi = get_file_type_metadata(stream, stream_type)
+3 -3
View File
@@ -1360,7 +1360,7 @@ class OPFCreator(Metadata):
if not isinstance(self.toc, TOC):
self.toc = None
if not self.authors:
self.authors = [_('Unknown')]
self.authors = ['Unknown']
if self.guide is None:
self.guide = Guide()
if self.cover:
@@ -1470,7 +1470,7 @@ class OPFCreator(Metadata):
metadata = M.metadata()
a = metadata.append
role = {}
a(DC_ELEM('title', self.title if self.title else _('Unknown'),
a(DC_ELEM('title', self.title if self.title else 'Unknown',
opf_attrs=role))
for i, author in enumerate(self.authors):
fa = {'role':'aut'}
@@ -1679,7 +1679,7 @@ def metadata_to_opf(mi, as_string=True, default_lang=None):
mi.cover = mi.cover.decode(filesystem_encoding)
guide.text = '\n'+(' '*8)
r = guide.makeelement(OPF('reference'),
attrib={'type':'cover', 'title':_('Cover'), 'href':mi.cover})
attrib={'type': 'cover', 'title': 'Cover', 'href': mi.cover})
r.tail = '\n' +(' '*4)
guide.append(r)
if pretty_print_opf:
+1 -1
View File
@@ -960,7 +960,7 @@ def set_last_modified_in_opf(root):
def read_metadata(root, ver=None, return_extra_data=False):
ans = Metadata(_('Unknown'), [_('Unknown')])
ans = Metadata('Unknown', ['Unknown'])
prefixes, refines = read_prefixes(root), read_refines(root)
identifiers = read_identifiers(root, prefixes, refines)
ids = {}
+2 -2
View File
@@ -122,10 +122,10 @@ def get_metadata(stream, cover=True):
with open(covpath, 'rb') as f:
cdata = f.read()
title = info.get('Title', None) or _('Unknown')
title = info.get('Title', None) or 'Unknown'
au = info.get('Author', None)
if au is None:
au = [_('Unknown')]
au = ['Unknown']
else:
au = string_to_authors(au)
mi = MetaInformation(title, au)
+3 -3
View File
@@ -110,10 +110,10 @@ def get_metadata(stream):
"""
stream.seek(0)
if stream.read(5) != br'{\rtf':
return MetaInformation(_('Unknown'))
return MetaInformation('Unknown')
block = get_document_info(stream)[0]
if not block:
return MetaInformation(_('Unknown'))
return MetaInformation('Unknown')
stream.seek(0)
cpg = detect_codepage(stream)
@@ -123,7 +123,7 @@ def get_metadata(stream):
if title_match is not None:
title = decode(title_match.group(1).strip(), cpg)
else:
title = _('Unknown')
title = 'Unknown'
author_match = author_pat.search(block)
if author_match is not None:
author = decode(author_match.group(1).strip(), cpg)
+1 -1
View File
@@ -17,7 +17,7 @@ def get_metadata(stream, extract_cover=True):
name = getattr(stream, 'name', '').rpartition('.')[0]
if name:
name = os.path.basename(name)
mi = MetaInformation(name or _('Unknown'), [_('Unknown')])
mi = MetaInformation(name or 'Unknown', ['Unknown'])
stream.seek(0)
mdata = ''
+4 -2
View File
@@ -235,7 +235,7 @@ def more_recent(one, two):
def metadata_from_xmp_packet(raw_bytes):
root = parse_xmp_packet(raw_bytes)
mi = Metadata(_('Unknown'))
mi = Metadata('Unknown')
title = first_alt('//dc:title', root)
if title:
if title.startswith(r'\376\377'):
@@ -346,7 +346,9 @@ def consolidate_metadata(info_mi, info):
import traceback
traceback.print_exc()
return info_mi
info_title, info_authors, info_tags = info_mi.title or _('Unknown'), list(info_mi.authors or ()), list(info_mi.tags or ())
info_title = info_mi.title or 'Unknown'
info_authors = list(info_mi.authors or ())
info_tags = list(info_mi.tags or ())
info_mi.smart_update(xmp_mi, replace_metadata=True)
prefer_info = False
if 'ModDate' in info and hasattr(xmp_mi, 'metadata_date'):
@@ -32,7 +32,7 @@ class EXTHHeader(object): # {{{
self.length, self.num_items = struct.unpack('>LL', raw[4:12])
raw = raw[12:]
pos = 0
self.mi = MetaInformation(_('Unknown'), [_('Unknown')])
self.mi = MetaInformation('Unknown', ['Unknown'])
self.has_fake_cover = True
self.start_offset = None
left = self.num_items
@@ -67,7 +67,7 @@ class EXTHHeader(object): # {{{
if content == b'EBSP':
if not self.mi.tags:
self.mi.tags = []
self.mi.tags.append(_('Sample Book'))
self.mi.tags.append('Sample Book')
elif idx == 502:
# last update time
pass
@@ -127,7 +127,7 @@ class EXTHHeader(object): # {{{
self.mi.authors.append(au)
elif idx == 101:
self.mi.publisher = clean_xml_chars(self.decode(content).strip())
if self.mi.publisher in {'Unknown', _('Unknown')}:
if self.mi.publisher in {'Unknown', 'Unknown'}:
self.mi.publisher = None
elif idx == 103:
self.mi.comments = clean_xml_chars(self.decode(content).strip())
@@ -194,7 +194,7 @@ class BookHeader(object):
if len(raw) <= 16:
self.codec = 'cp1252'
self.extra_flags = 0
self.title = _('Unknown')
self.title = 'Unknown'
self.language = 'ENGLISH'
self.sublanguage = 'NEUTRAL'
self.exth_flag, self.exth = 0, None
@@ -233,7 +233,7 @@ class BookHeader(object):
toff, tlen = struct.unpack('>II', raw[0x54:0x5c])
tend = toff + tlen
self.title = raw[toff:tend] if tend < len(raw) else _('Unknown')
self.title = raw[toff:tend] if tend < len(raw) else 'Unknown'
langcode = struct.unpack('!L', raw[0x5C:0x60])[0]
langid = langcode & 0xFF
sublangid = (langcode >> 10) & 0xFF
+7 -6
View File
@@ -30,10 +30,10 @@ class TopazError(ValueError):
class KFXError(ValueError):
def __init__(self):
ValueError.__init__(self, _(
'This is an Amazon KFX book. It cannot be processed.'
' See {} for information on how to handle KFX books.'
).format('https://www.mobileread.com/forums/showthread.php?t=283371'))
ValueError.__init__(self, 'This is an Amazon KFX book. It cannot be '
'processed. See https://www.mobileread.com/forums/'
'showthread.php?t=283371 for information on how '
'to handle KFX books.')
class MobiReader(object):
@@ -77,7 +77,8 @@ class MobiReader(object):
raw = stream.read()
if raw.startswith(b'TPZ'):
raise TopazError(_('This is an Amazon Topaz book. It cannot be processed.'))
raise TopazError('This is an Amazon Topaz book. It cannot be '
'processed.')
if raw.startswith(b'\xeaDRMION\xee'):
raise KFXError()
@@ -642,7 +643,7 @@ class MobiReader(object):
def create_opf(self, htmlfile, guide=None, root=None):
mi = getattr(self.book_header.exth, 'mi', self.embedded_mi)
if mi is None:
mi = MetaInformation(self.book_header.title, [_('Unknown')])
mi = MetaInformation(self.book_header.title, ['Unknown'])
opf = OPFCreator(os.path.dirname(htmlfile), mi)
if hasattr(self.book_header.exth, 'cover_offset'):
opf.cover = 'images/%05d.jpg' % (self.book_header.exth.cover_offset + 1)
+24 -22
View File
@@ -1,4 +1,5 @@
import os, glob
import glob
import os
from ebook_converter import CurrentDir
from ebook_converter.ebooks.mobi import MobiError
@@ -8,8 +9,8 @@ from ebook_converter.utils.logging import default_log
from ebook_converter.ebooks import DRMError
from ebook_converter.ebooks.mobi.reader.mobi8 import Mobi8Reader
from ebook_converter.ebooks.conversion.plumber import Plumber, create_oebbook
from ebook_converter.customize.ui import (plugin_for_input_format,
plugin_for_output_format)
from ebook_converter.customize.ui import plugin_for_input_format
from ebook_converter.customize.ui import plugin_for_output_format
from ebook_converter.utils.ipc.simple_worker import fork_job
@@ -31,44 +32,46 @@ def do_explode(path, dest):
opf = os.path.abspath(mr())
try:
os.remove('debug-raw.html')
except:
except Exception:
pass
return opf
def explode(path, dest, question=lambda x:True):
def explode(path, dest, question=lambda x: True):
with open(path, 'rb') as stream:
raw = stream.read(3)
stream.seek(0)
if raw == b'TPZ':
raise BadFormat(_('This is not a MOBI file. It is a Topaz file.'))
raise BadFormat('This is not a MOBI file. It is a Topaz file.')
try:
header = MetadataHeader(stream, default_log)
except MobiError:
raise BadFormat(_('This is not a MOBI file.'))
raise BadFormat('This is not a MOBI file.')
if header.encryption_type != 0:
raise DRMError(_('This file is locked with DRM. It cannot be tweaked.'))
raise DRMError('This file is locked with DRM. It cannot be '
'tweaked.')
kf8_type = header.kf8_type
if kf8_type is None:
raise BadFormat(_('This MOBI file does not contain a KF8 format '
'book. KF8 is the new format from Amazon. calibre can '
'only tweak MOBI files that contain KF8 books. Older '
'MOBI files without KF8 are not tweakable.'))
raise BadFormat('This MOBI file does not contain a KF8 format '
'book. KF8 is the new format from Amazon. calibre '
'can only tweak MOBI files that contain KF8 '
'books. Older MOBI files without KF8 are not '
'tweakable.')
if kf8_type == 'joint':
if not question(_('This MOBI file contains both KF8 and '
'older Mobi6 data. Tweaking it will remove the Mobi6 data, which '
'means the file will not be usable on older Kindles. Are you '
'sure?')):
if not question('This MOBI file contains both KF8 and older Mobi6 '
'data. Tweaking it will remove the Mobi6 data, '
'which means the file will not be usable on older '
'Kindles. Are you sure?'):
return None
return fork_job('ebook_converter.ebooks.mobi.tweak', 'do_explode', args=(path,
dest), no_output=True)['result']
return fork_job('ebook_converter.ebooks.mobi.tweak', 'do_explode',
args=(path, dest), no_output=True)['result']
def set_cover(oeb):
@@ -96,11 +99,10 @@ def do_rebuild(opf, dest_path):
def rebuild(src_dir, dest_path):
opf = glob.glob(os.path.join(src_dir, '*.opf'))
if not opf:
raise ValueError('No OPF file found in %s'%src_dir)
raise ValueError('No OPF file found in %s' % src_dir)
opf = opf[0]
# For debugging, uncomment the following two lines
# def fork_job(a, b, args=None, no_output=True):
# do_rebuild(*args)
fork_job('ebook_converter.ebooks.mobi.tweak', 'do_rebuild', args=(opf, dest_path),
no_output=True)
fork_job('ebook_converter.ebooks.mobi.tweak', 'do_rebuild',
args=(opf, dest_path), no_output=True)
+1 -1
View File
@@ -336,7 +336,7 @@ def utf8_text(text):
text = text.decode('utf-8', 'replace')
text = normalize(text).encode('utf-8')
else:
text = _('Unknown').encode('utf-8')
text = 'Unknown'.encode('utf-8')
return text
@@ -461,9 +461,9 @@ class Indexer(object): # {{{
if node.klass == 'article':
aut, desc = node.author, node.description
if not aut:
aut = _('Unknown')
aut = 'Unknown'
if not desc:
desc = _('No details available')
desc = 'No details available'
node.author, node.description = aut, desc
self.cncx = CNCX(oeb.toc, self.is_periodical)
+2 -2
View File
@@ -276,9 +276,9 @@ class Extract(ODF2XHTML):
stream.seek(0)
mi = get_metadata(stream, 'odt')
if not mi.title:
mi.title = _('Unknown')
mi.title = 'Unknown'
if not mi.authors:
mi.authors = [_('Unknown')]
mi.authors = ['Unknown']
self.filter_load(stream, mi, log)
# NOTE(gryf): Here is a workaround for ODF2XHTML.xhtml() method,
+18 -18
View File
@@ -967,7 +967,7 @@ class Manifest(object):
if title:
title = str(title[0])
else:
title = _('Unknown')
title = 'Unknown'
return self._parse_xhtml(convert_markdown(data, title=title))
@@ -1368,23 +1368,23 @@ class Guide(object):
:attr:`href`: Book-internal URL of the referenced section. May include
a fragment identifier.
"""
_TYPES_TITLES = [('cover', __('Cover')),
('title-page', __('Title page')),
('toc', __('Table of Contents')),
('index', __('Index')),
('glossary', __('Glossary')),
('acknowledgements', __('Acknowledgements')),
('bibliography', __('Bibliography')),
('colophon', __('Colophon')),
('copyright-page', __('Copyright')),
('dedication', __('Dedication')),
('epigraph', __('Epigraph')),
('foreword', __('Foreword')),
('loi', __('List of illustrations')),
('lot', __('List of tables')),
('notes', __('Notes')),
('preface', __('Preface')),
('text', __('Main text'))]
_TYPES_TITLES = [('cover', 'Cover'),
('title-page', 'Title page'),
('toc', 'Table of Contents'),
('index', 'Index'),
('glossary', 'Glossary'),
('acknowledgements', 'Acknowledgements'),
('bibliography', 'Bibliography'),
('colophon', 'Colophon'),
('copyright-page', 'Copyright'),
('dedication', 'Dedication'),
('epigraph', 'Epigraph'),
('foreword', 'Foreword'),
('loi', 'List of illustrations'),
('lot', 'List of tables'),
('notes', 'Notes'),
('preface', 'Preface'),
('text', 'Main text')]
TITLES = dict(_TYPES_TITLES)
TYPES = frozenset(TITLES)
ORDER = {t: i for i, (t, _) in enumerate(_TYPES_TITLES)}
+3 -3
View File
@@ -312,14 +312,14 @@ def parse_html(data, log=None, decoder=None, preprocessor=None,
head = etree.Element(XHTML('head'))
data.insert(0, head)
title = etree.SubElement(head, XHTML('title'))
title.text = _('Unknown')
title.text = 'Unknown'
elif not xpath(data, '/h:html/h:head/h:title'):
title = etree.SubElement(head, XHTML('title'))
title.text = _('Unknown')
title.text = 'Unknown'
# Ensure <title> is not empty
title = xpath(data, '/h:html/h:head/h:title')[0]
if not title.text or not title.text.strip():
title.text = _('Unknown')
title.text = 'Unknown'
# Remove any encoding-specifying <meta/> elements
for meta in META_XP(data):
meta.getparent().remove(meta)
+11 -9
View File
@@ -1458,12 +1458,13 @@ class AZW3Container(Container):
with open(pathtoazw3, 'rb') as stream:
raw = stream.read(3)
if raw == b'TPZ':
raise InvalidMobi(_('This is not a MOBI file. It is a Topaz file.'))
raise InvalidMobi('This is not a MOBI file. It is a Topaz '
'file.')
try:
header = MetadataHeader(stream, default_log)
except MobiError:
raise InvalidMobi(_('This is not a MOBI file.'))
raise InvalidMobi('This is not a MOBI file.')
if header.encryption_type != 0:
raise DRMError()
@@ -1471,15 +1472,16 @@ class AZW3Container(Container):
kf8_type = header.kf8_type
if kf8_type is None:
raise InvalidMobi(_('This MOBI file does not contain a KF8 format '
'book. KF8 is the new format from Amazon. calibre can '
'only edit MOBI files that contain KF8 books. Older '
'MOBI files without KF8 are not editable.'))
raise InvalidMobi('This MOBI file does not contain a KF8 '
'format book. KF8 is the new format from '
'Amazon. calibre can only edit MOBI files '
'that contain KF8 books. Older MOBI files '
'without KF8 are not editable.')
if kf8_type == 'joint':
raise InvalidMobi(_('This MOBI file contains both KF8 and '
'older Mobi6 data. calibre can only edit MOBI files '
'that contain only KF8 data.'))
raise InvalidMobi('This MOBI file contains both KF8 and older '
'Mobi6 data. calibre can only edit MOBI '
'files that contain only KF8 data.')
try:
opf_path, obfuscated_fonts = fork_job(
+8 -9
View File
@@ -167,20 +167,19 @@ def remove_unused_css(container, report=None, remove_unused_classes=False, merge
num_changes = num_of_removed_rules + num_merged + num_of_removed_classes
if num_changes > 0:
if num_of_removed_rules > 0:
report(ngettext('Removed one unused CSS style rule', 'Removed {} unused CSS style rules',
num_of_removed_rules).format(num_of_removed_rules))
report('Removed {} unused CSS style '
'rules'.format(num_of_removed_rules))
if num_of_removed_classes > 0:
report(ngettext('Removed one unused class from the HTML', 'Removed {} unused classes from the HTML',
num_of_removed_classes).format(num_of_removed_classes))
report('Removed {} unused classes from the HTML'
.format(num_of_removed_classes))
if num_merged > 0:
report(ngettext('Merged one CSS style rule', 'Merged {} CSS style rules',
num_merged).format(num_merged))
report('Merged {} CSS style rules'.format(num_merged))
if num_of_removed_rules == 0:
report(_('No unused CSS style rules found'))
report('No unused CSS style rules found')
if remove_unused_classes and num_of_removed_classes == 0:
report(_('No unused class attributes found'))
report('No unused class attributes found')
if merge_rules and num_merged == 0:
report(_('No style rules that could be merged found'))
report('No style rules that could be merged found')
return num_changes > 0
+2 -1
View File
@@ -13,7 +13,8 @@ class InvalidBook(ValueError):
class DRMError(_DRMError):
def __init__(self):
super(DRMError, self).__init__(_('This file is locked with DRM. It cannot be edited.'))
super(DRMError, self).__init__('This file is locked with DRM. It '
'cannot be edited.')
class MalformedMarkup(ValueError):
+2 -2
View File
@@ -157,7 +157,7 @@ def smarten_punctuation(container, report):
newhtml = smarten_punctuation(html, container.log)
if newhtml != html:
changed = True
report(_('Smartened punctuation in: %s')%name)
report('Smartened punctuation in: %s' % name)
newhtml = strip_encoding_declarations(newhtml)
f.seek(0)
f.truncate()
@@ -171,7 +171,7 @@ def smarten_punctuation(container, report):
container.dirty(name)
smartened = True
if not smartened:
report(_('No punctuation that could be smartened found'))
report('No punctuation that could be smartened found')
return smartened
+4 -3
View File
@@ -194,8 +194,9 @@ def split(container, name, loc_or_xpath, before=True, totals=None):
try:
split_point = node_from_loc(root, loc_or_xpath, totals=totals)
except MalformedMarkup:
raise MalformedMarkup(_('The file %s has malformed markup. Try running the Fix HTML tool'
' before splitting') % name)
raise MalformedMarkup('The file %s has malformed markup. Try '
'running the Fix HTML tool before '
'splitting' % name)
container.replace(name, root)
if in_table(split_point):
raise AbortError('Cannot split inside tables')
@@ -269,7 +270,7 @@ def multisplit(container, name, xpath, before=True):
root = container.parsed(name)
nodes = root.xpath(xpath, namespaces=XPNSMAP)
if not nodes:
raise AbortError(_('The expression %s did not match any nodes') % xpath)
raise AbortError('The expression %s did not match any nodes' % xpath)
for split_point in nodes:
if in_table(split_point):
raise AbortError('Cannot split inside tables')
+13 -12
View File
@@ -240,17 +240,17 @@ def verify_toc_destinations(container, toc):
name = item.dest
if not name:
item.dest_exists = False
item.dest_error = _('No file named %s exists')%name
item.dest_error = 'No file named %s exists' % name
continue
try:
root = container.parsed(name)
except KeyError:
item.dest_exists = False
item.dest_error = _('No file named %s exists')%name
item.dest_error = 'No file named %s exists' % name
continue
if not hasattr(root, 'xpath'):
item.dest_exists = False
item.dest_error = _('No HTML file named %s exists')%name
item.dest_error = 'No HTML file named %s exists' % name
continue
if not item.frag:
item.dest_exists = True
@@ -259,9 +259,8 @@ def verify_toc_destinations(container, toc):
anchor_map[name] = frozenset(anchor_xpath(root))
item.dest_exists = item.frag in anchor_map[name]
if not item.dest_exists:
item.dest_error = _(
'The anchor %(a)s does not exist in file %(f)s')%dict(
a=item.frag, f=name)
item.dest_error = ('The anchor %(a)s does not exist in file '
'%(f)s' % dict(a=item.frag, f=name))
def find_existing_ncx_toc(container):
@@ -370,7 +369,7 @@ def elem_to_toc_text(elem):
text = re.sub(r'\s+', ' ', text.strip())
text = text[:1000].strip()
if not text:
text = _('(Untitled)')
text = '(Untitled)'
return text
@@ -533,8 +532,9 @@ def from_files(container):
text = find_text(body[0])
if not text:
text = name.rpartition('/')[-1]
if i == 0 and text.rpartition('.')[0].lower() in {'titlepage', 'cover'}:
text = _('Cover')
if i == 0 and text.rpartition('.')[0].lower() in {'titlepage',
'cover'}:
text = 'Cover'
toc.add(text, name)
return toc
@@ -563,8 +563,9 @@ def add_id(container, name, loc, totals=None):
try:
node = node_from_loc(root, loc, totals=totals)
except MalformedMarkup:
raise MalformedMarkup(_('The file %s has malformed markup. Try running the Fix HTML tool'
' before editing.') % name)
raise MalformedMarkup('The file %s has malformed markup. Try '
'running the Fix HTML tool before '
'editing.' % name)
container.replace(name, root)
if not node.get('id'):
@@ -641,7 +642,7 @@ def commit_ncx_toc(container, toc, lang=None, uid=None):
if m:
uid = xml2text(m[0])
title = _('Table of Contents')
title = 'Table of Contents'
m = container.opf_xpath('//dc:title')
if m:
x = xml2text(m[0]).strip()
+2 -2
View File
@@ -146,14 +146,14 @@ class OEBReader(object):
m.add('identifier', str(uuid.uuid4()), id='uuid_id', scheme='uuid')
self.oeb.uid = self.oeb.metadata.identifier[-1]
if not m.title:
m.add('title', self.oeb.translate(__('Unknown')))
m.add('title', self.oeb.translate('Unknown'))
has_aut = False
for x in m.creator:
if getattr(x, 'role', '').lower() in ('', 'aut'):
has_aut = True
break
if not has_aut:
m.add('creator', self.oeb.translate(__('Unknown')), role='aut')
m.add('creator', self.oeb.translate('Unknown'), role='aut')
def _manifest_prune_invalid(self):
'''
@@ -10,7 +10,7 @@ __all__ = ['HTMLTOCAdder']
__license__ = 'GPL v3'
__copyright__ = '2008, Marshall T. Vandegrift <llasram@gmail.com>'
DEFAULT_TITLE = __('Table of Contents')
DEFAULT_TITLE = 'Table of Contents'
STYLE_CSS = {
'nested': """
@@ -52,9 +52,9 @@ class HTMLTOCAdder(object):
@classmethod
def config(cls, cfg):
group = cfg.add_group('htmltoc', _('HTML TOC generation options.'))
group = cfg.add_group('htmltoc', 'HTML TOC generation options.')
group('toc_title', ['--toc-title'], default=None,
help=_('Title for any generated in-line table of contents.'))
help='Title for any generated in-line table of contents.')
return cfg
@classmethod
+28 -22
View File
@@ -110,12 +110,12 @@ class Jacket(Base):
try:
title = str(self.oeb.metadata.title[0])
except:
title = _('Unknown')
title = 'Unknown'
try:
authors = list(map(str, self.oeb.metadata.creator))
except:
authors = [_('Unknown')]
authors = ['Unknown']
root = render_jacket(mi, self.opts.output_profile,
alt_title=title, alt_tags=tags, alt_authors=authors,
@@ -174,10 +174,11 @@ class Series(str):
def __new__(self, series, series_index):
if series and series_index is not None:
roman = _('{1} of <em>{0}</em>').format(
roman = '{1} of <em>{0}</em>'.format(
escape(series), escape(fmt_sidx(series_index, use_roman=True)))
combined = _('{1} of <em>{0}</em>').format(
escape(series), escape(fmt_sidx(series_index, use_roman=False)))
combined = '{1} of <em>{0}</em>'.format(
escape(series), escape(fmt_sidx(series_index,
use_roman=False)))
else:
combined = roman = escape(series or u'')
s = str.__new__(self, combined)
@@ -227,7 +228,7 @@ def postprocess_jacket(root, output_profile, has_data):
def render_jacket(mi, output_profile,
alt_title=_('Unknown'), alt_tags=[], alt_comments='',
alt_title='Unknown', alt_tags=[], alt_comments='',
alt_publisher='', rescale_fonts=False, alt_authors=None):
with open(pkg_resources.resource_filename('ebook_converter',
'data/jacket/stylesheet.css'),
@@ -244,7 +245,7 @@ def render_jacket(mi, output_profile,
try:
title_str = alt_title if mi.is_null('title') else mi.title
except:
title_str = _('Unknown')
title_str = 'Unknown'
title_str = escape(title_str)
title = '<span class="title">%s</span>' % title_str
@@ -275,7 +276,7 @@ def render_jacket(mi, output_profile,
orig = mi.authors
if mi.is_null('authors'):
mi.authors = list(alt_authors or (_('Unknown'),))
mi.authors = list(alt_authors or ('Unknown',))
try:
author = mi.format_authors()
except:
@@ -285,20 +286,25 @@ def render_jacket(mi, output_profile,
has_data = {}
def generate_html(comments):
args = dict(xmlns=XHTML_NS,
title_str=title_str,
css=css,
title=title,
author=author,
publisher=publisher,
pubdate_label=_('Published'), pubdate=pubdate,
series_label=_('Series'), series=series,
rating_label=_('Rating'), rating=rating,
tags_label=_('Tags'), tags=tags,
comments=comments,
footer='',
searchable_tags=' '.join(escape(t)+'ttt' for t in tags.tags_list),
)
args = {'author': author,
'comments': comments,
'css': css,
'footer': '',
'pubdate': pubdate,
'pubdate_label': 'Published',
'publisher': publisher,
'rating': rating,
'rating_label': 'Rating',
'searchable_tags': ' '.join(escape(t) + 'ttt'
for t in tags.tags_list),
'series': series,
'series_label': 'Series',
'tags': tags,
'tags_label': 'Tags',
'title': title,
'title_str': title_str,
'xmlns': XHTML_NS}
for key in mi.custom_field_keys():
m = mi.get_user_metadata(key, False) or {}
try:
@@ -35,10 +35,9 @@ class SplitError(ValueError):
def __init__(self, path, root):
size = len(tostring(root))/1024.
ValueError.__init__(self,
_('Could not find reasonable point at which to split: '
'%(path)s Sub-tree size: %(size)d KB')%dict(
path=path, size=size))
ValueError.__init__(self, 'Could not find reasonable point at which '
'to split: %(path)s Sub-tree size: %(size)d KB' %
{'path': path, 'size': size})
class Split(object):
@@ -100,7 +100,7 @@ class DetectStructure(object):
for node in self.oeb.toc.iter():
if not node.title or not node.title.strip():
node.title = _('Unnamed')
node.title = 'Unnamed'
if self.opts.start_reading_at:
self.detect_start_reading()
@@ -279,7 +279,7 @@ class DetectStructure(object):
node = self.oeb.toc.add(text, _href,
play_order=self.oeb.toc.next_play_order())
added[elem] = node
# node.add(_('Top'), _href)
# node.add('Top', _href)
if self.opts.level2_toc is not None and added:
level2_toc, level2_title = self.get_toc_parts_for_xpath(self.opts.level2_toc)
+4 -4
View File
@@ -29,13 +29,13 @@ class OEBWriter(object):
"""Add any book-writing options to the :class:`Config` object
:param:`cfg`.
"""
oeb = cfg.add_group('oeb', _('OPF/NCX/etc. generation options.'))
oeb = cfg.add_group('oeb', 'OPF/NCX/etc. generation options.')
versions = ['1.2', '2.0']
oeb('opf_version', ['--opf-version'], default='2.0', choices=versions,
help=_('OPF version to generate. Default is %default.'))
help='OPF version to generate. Default is %default.')
oeb('adobe_page_map', ['--adobe-page-map'], default=False,
help=_('Generate an Adobe "page-map" file if pagination '
'information is available.'))
help='Generate an Adobe "page-map" file if pagination '
'information is available.')
return cfg
@classmethod
@@ -124,7 +124,7 @@ class Reader132(FormatReader):
toc = hizer.get_toc()
if self.header_record.footnote_count > 0:
html += '<br /><h1>%s</h1>' % _('Footnotes')
html += '<br /><h1>%s</h1>' % 'Footnotes'
footnoteids = re.findall(
'\\w+(?=\x00)', self.section_data(self.header_record.footnote_offset).decode('cp1252' if self.encoding is None else self.encoding))
for fid, i in enumerate(range(self.header_record.footnote_offset + 1, self.header_record.footnote_offset + self.header_record.footnote_count)):
@@ -136,7 +136,7 @@ class Reader132(FormatReader):
html += footnote_to_html(fid, self.decompress_text(i))
if self.header_record.sidebar_count > 0:
html += '<br /><h1>%s</h1>' % _('Sidebar')
html += '<br /><h1>%s</h1>' % 'Sidebar'
sidebarids = re.findall(
'\\w+(?=\x00)', self.section_data(self.header_record.sidebar_offset).decode('cp1252' if self.encoding is None else self.encoding))
for sid, i in enumerate(range(self.header_record.sidebar_offset + 1, self.header_record.sidebar_offset + self.header_record.sidebar_count)):
+2 -2
View File
@@ -70,8 +70,8 @@ def pdftohtml(output_dir, pdf_path, no_images, as_xml=False):
stdin=subprocess.PIPE)
except OSError as err:
if err.errno == errno.ENOENT:
raise ConversionError(
_('Could not find pdftohtml, check it is in your PATH'))
raise ConversionError('Could not find pdftohtml, check it is '
'in your PATH')
else:
raise
ret = eintr_retry_call(p.wait)
+1 -1
View File
@@ -142,7 +142,7 @@ def convert_markdown_with_metadata(txt, title='', extensions=DEFAULT_MD_EXTENSIO
extensions.append('meta')
md = create_markdown_object(extensions)
html = md.convert(txt)
mi = Metadata(title or _('Unknown'))
mi = Metadata(title or 'Unknown')
m = md.Meta
for k, v in {'date':'pubdate', 'summary':'comments'}.items():
if v not in m and k in m:
+1 -1
View File
@@ -96,7 +96,7 @@ class TXTMLizer(object):
toc = ['']
if getattr(self.opts, 'inline_toc', None):
self.log.debug('Generating table of contents...')
toc.append('%s\n\n' % _('Table of Contents:'))
toc.append('%s\n\n' % 'Table of Contents:')
for item in self.toc_titles:
toc.append('* %s\n\n' % item)
return ''.join(toc)
+17 -16
View File
@@ -30,78 +30,79 @@ class BIBTEX(CatalogPlugin):
default='all',
dest='fields',
action=None,
help=_('The fields to output when cataloging books in the '
help='The fields to output when cataloging books in the '
'database. Should be a comma-separated list of fields.\n'
'Available fields: %(fields)s.\n'
'plus user-created custom fields.\n'
'Example: %(opt)s=title,authors,tags\n'
"Default: '%%default'\n"
"Applies to: BIBTEX output format")%dict(
"Applies to: BIBTEX output format" % dict(
fields=', '.join(FIELDS), opt='--fields')),
Option('--sort-by',
default='id',
dest='sort_by',
action=None,
help=_('Output field to sort on.\n'
help='Output field to sort on.\n'
'Available fields: author_sort, id, rating, size, timestamp, title.\n'
"Default: '%default'\n"
"Applies to: BIBTEX output format")),
"Applies to: BIBTEX output format"),
Option('--create-citation',
default='True',
dest='impcit',
action=None,
help=_('Create a citation for BibTeX entries.\n'
help='Create a citation for BibTeX entries.\n'
'Boolean value: True, False\n'
"Default: '%default'\n"
"Applies to: BIBTEX output format")),
"Applies to: BIBTEX output format"),
Option('--add-files-path',
default='True',
dest='addfiles',
action=None,
help=_('Create a file entry if formats is selected for BibTeX entries.\n'
help='Create a file entry if formats is selected for BibTeX entries.\n'
'Boolean value: True, False\n'
"Default: '%default'\n"
"Applies to: BIBTEX output format")),
"Applies to: BIBTEX output format"),
Option('--citation-template',
default='{authors}{id}',
dest='bib_cit',
action=None,
help=_('The template for citation creation from database fields.\n'
help='The template for citation creation from database fields.\n'
'Should be a template with {} enclosed fields.\n'
'Available fields: %s.\n'
"Default: '%%default'\n"
"Applies to: BIBTEX output format")%', '.join(TEMPLATE_ALLOWED_FIELDS)),
"Applies to: BIBTEX output format" %
', '.join(TEMPLATE_ALLOWED_FIELDS)),
Option('--choose-encoding',
default='utf8',
dest='bibfile_enc',
action=None,
help=_('BibTeX file encoding output.\n'
help='BibTeX file encoding output.\n'
'Available types: utf8, cp1252, ascii.\n'
"Default: '%default'\n"
"Applies to: BIBTEX output format")),
"Applies to: BIBTEX output format"),
Option('--choose-encoding-configuration',
default='strict',
dest='bibfile_enctag',
action=None,
help=_('BibTeX file encoding flag.\n'
help='BibTeX file encoding flag.\n'
'Available types: strict, replace, ignore, backslashreplace.\n'
"Default: '%default'\n"
"Applies to: BIBTEX output format")),
"Applies to: BIBTEX output format"),
Option('--entry-type',
default='book',
dest='bib_entry',
action=None,
help=_('Entry type for BibTeX catalog.\n'
help='Entry type for BibTeX catalog.\n'
'Available types: book, misc, mixed.\n'
"Default: '%default'\n"
"Applies to: BIBTEX output format"))]
"Applies to: BIBTEX output format")]
def run(self, path_to_output, opts, db, notification=DummyReporter()):
from ebook_converter.utils.date import isoformat
+4 -4
View File
@@ -29,23 +29,23 @@ class CSV_XML(CatalogPlugin):
default='all',
dest='fields',
action=None,
help=_('The fields to output when cataloging books in the '
help='The fields to output when cataloging books in the '
'database. Should be a comma-separated list of fields.\n'
'Available fields: %(fields)s,\n'
'plus user-created custom fields.\n'
'Example: %(opt)s=title,authors,tags\n'
"Default: '%%default'\n"
"Applies to: CSV, XML output formats") % dict(
"Applies to: CSV, XML output formats" % dict(
fields=', '.join(FIELDS), opt='--fields')),
Option('--sort-by',
default='id',
dest='sort_by',
action=None,
help=_('Output field to sort on.\n'
help='Output field to sort on.\n'
'Available fields: author_sort, id, rating, size, timestamp, title_sort\n'
"Default: '%default'\n"
"Applies to: CSV, XML output formats"))]
"Applies to: CSV, XML output formats")]
def run(self, path_to_output, opts, db, notification=DummyReporter()):
from ebook_converter.library import current_library_name
+88 -61
View File
@@ -36,154 +36,178 @@ class EPUB_MOBI(CatalogPlugin):
default='My Books',
dest='catalog_title',
action=None,
help=_('Title of generated catalog used as title in metadata.\n'
help='Title of generated catalog used as title in '
'metadata.\n'
"Default: '%default'\n"
"Applies to: AZW3, EPUB, MOBI output formats")),
"Applies to: AZW3, EPUB, MOBI output formats"),
Option('--cross-reference-authors',
default=False,
dest='cross_reference_authors',
action='store_true',
help=_("Create cross-references in Authors section for books with multiple authors.\n"
help="Create cross-references in Authors section "
"for books with multiple authors.\n"
"Default: '%default'\n"
"Applies to: AZW3, EPUB, MOBI output formats")),
"Applies to: AZW3, EPUB, MOBI output formats"),
Option('--debug-pipeline',
default=None,
dest='debug_pipeline',
action=None,
help=_("Save the output from different stages of the conversion "
"pipeline to the specified "
"directory. Useful if you are unsure at which stage "
"of the conversion process a bug is occurring.\n"
help="Save the output from different stages of the "
"conversion pipeline to the specified directory. "
"Useful if you are unsure at which stage of the "
"conversion process a bug is occurring.\n"
"Default: '%default'\n"
"Applies to: AZW3, EPUB, MOBI output formats")),
"Applies to: AZW3, EPUB, MOBI output formats"),
Option('--exclude-genre',
default=r'\[.+\]|^\+$',
dest='exclude_genre',
action=None,
help=_("Regex describing tags to exclude as genres.\n"
"Default: '%default' excludes bracketed tags, e.g. '[Project Gutenberg]', and '+', the default tag for read books.\n"
"Applies to: AZW3, EPUB, MOBI output formats")),
help="Regex describing tags to exclude as genres.\n"
"Default: '%default' excludes bracketed tags, e.g. "
"'[Project Gutenberg]', and '+', the default tag "
"for read books.\n"
"Applies to: AZW3, EPUB, MOBI output formats"),
Option('--exclusion-rules',
default="(('Catalogs','Tags','Catalog'),)",
dest='exclusion_rules',
action=None,
help=_("Specifies the rules used to exclude books from the generated catalog.\n"
"The model for an exclusion rule is either\n('<rule name>','Tags','<comma-separated list of tags>') or\n"
help="Specifies the rules used to exclude books "
"from the generated catalog.\n"
"The model for an exclusion rule is either\n"
"('<rule name>','Tags','<comma-separated list of "
"tags>') or\n"
"('<rule name>','<custom column>','<pattern>').\n"
"For example:\n"
"(('Archived books','#status','Archived'),)\n"
"will exclude a book with a value of 'Archived' in the custom column 'status'.\n"
"When multiple rules are defined, all rules will be applied.\n"
"Default: \n" + '"' + '%default' + '"' + "\n"
"Applies to: AZW3, EPUB, MOBI output formats")),
"will exclude a book with a value of 'Archived' in "
"the custom column 'status'.\n"
"When multiple rules are defined, all rules will be "
"applied.\n"
"Default: \n\"%default\"\n"
"Applies to: AZW3, EPUB, MOBI output formats"),
Option('--generate-authors',
default=False,
dest='generate_authors',
action='store_true',
help=_("Include 'Authors' section in catalog.\n"
help="Include 'Authors' section in catalog.\n"
"Default: '%default'\n"
"Applies to: AZW3, EPUB, MOBI output formats")),
"Applies to: AZW3, EPUB, MOBI output formats"),
Option('--generate-descriptions',
default=False,
dest='generate_descriptions',
action='store_true',
help=_("Include 'Descriptions' section in catalog.\n"
help="Include 'Descriptions' section in catalog.\n"
"Default: '%default'\n"
"Applies to: AZW3, EPUB, MOBI output formats")),
"Applies to: AZW3, EPUB, MOBI output formats"),
Option('--generate-genres',
default=False,
dest='generate_genres',
action='store_true',
help=_("Include 'Genres' section in catalog.\n"
help="Include 'Genres' section in catalog.\n"
"Default: '%default'\n"
"Applies to: AZW3, EPUB, MOBI output formats")),
"Applies to: AZW3, EPUB, MOBI output formats"),
Option('--generate-titles',
default=False,
dest='generate_titles',
action='store_true',
help=_("Include 'Titles' section in catalog.\n"
help="Include 'Titles' section in catalog.\n"
"Default: '%default'\n"
"Applies to: AZW3, EPUB, MOBI output formats")),
"Applies to: AZW3, EPUB, MOBI output formats"),
Option('--generate-series',
default=False,
dest='generate_series',
action='store_true',
help=_("Include 'Series' section in catalog.\n"
help="Include 'Series' section in catalog.\n"
"Default: '%default'\n"
"Applies to: AZW3, EPUB, MOBI output formats")),
"Applies to: AZW3, EPUB, MOBI output formats"),
Option('--generate-recently-added',
default=False,
dest='generate_recently_added',
action='store_true',
help=_("Include 'Recently Added' section in catalog.\n"
help="Include 'Recently Added' section in catalog.\n"
"Default: '%default'\n"
"Applies to: AZW3, EPUB, MOBI output formats")),
"Applies to: AZW3, EPUB, MOBI output formats"),
Option('--genre-source-field',
default=_('Tags'),
default='Tags',
dest='genre_source_field',
action=None,
help=_("Source field for 'Genres' section.\n"
help="Source field for 'Genres' section.\n"
"Default: '%default'\n"
"Applies to: AZW3, EPUB, MOBI output formats")),
"Applies to: AZW3, EPUB, MOBI output formats"),
Option('--header-note-source-field',
default='',
dest='header_note_source_field',
action=None,
help=_("Custom field containing note text to insert in Description header.\n"
help="Custom field containing note text to insert "
"in Description header.\n"
"Default: '%default'\n"
"Applies to: AZW3, EPUB, MOBI output formats")),
"Applies to: AZW3, EPUB, MOBI output formats"),
Option('--merge-comments-rule',
default='::',
dest='merge_comments_rule',
action=None,
help=_("#<custom field>:[before|after]:[True|False] specifying:\n"
" <custom field> Custom field containing notes to merge with Comments\n"
" [before|after] Placement of notes with respect to Comments\n"
" [True|False] - A horizontal rule is inserted between notes and Comments\n"
help="#<custom field>:[before|after]:[True|False] "
"specifying:\n"
" <custom field> Custom field containing notes to "
"merge with Comments\n"
" [before|after] Placement of notes with respect "
"to Comments\n"
" [True|False] - A horizontal rule is inserted "
"between notes and Comments\n"
"Default: '%default'\n"
"Applies to: AZW3, EPUB, MOBI output formats")),
"Applies to: AZW3, EPUB, MOBI output formats"),
Option('--output-profile',
default=None,
dest='output_profile',
action=None,
help=_("Specifies the output profile. In some cases, an output profile is required to optimize"
" the catalog for the device. For example, 'kindle' or 'kindle_dx' creates a structured"
" Table of Contents with Sections and Articles.\n"
help="Specifies the output profile. In some cases, "
"an output profile is required to optimize the "
"catalog for the device. For example, 'kindle' or "
"'kindle_dx' creates a structured Table of Contents "
"with Sections and Articles.\n"
"Default: '%default'\n"
"Applies to: AZW3, EPUB, MOBI output formats")),
"Applies to: AZW3, EPUB, MOBI output formats"),
Option('--prefix-rules',
default="(('Read books','tags','+','\u2713'),('Wishlist item','tags','Wishlist','\u00d7'))",
default="(('Read books','tags','+','\u2713'),"
"('Wishlist item','tags','Wishlist','\u00d7'))",
dest='prefix_rules',
action=None,
help=_("Specifies the rules used to include prefixes indicating read books, wishlist items and other user-specified prefixes.\n"
"The model for a prefix rule is ('<rule name>','<source field>','<pattern>','<prefix>').\n"
"When multiple rules are defined, the first matching rule will be used.\n"
help="Specifies the rules used to include prefixes "
"indicating read books, wishlist items and other "
"user-specified prefixes.\n"
"The model for a prefix rule is ('<rule name>',"
"'<source field>','<pattern>','<prefix>').\n"
"When multiple rules are defined, the first "
"matching rule will be used.\n"
"Default:\n" + '"' + '%default' + '"' + "\n"
"Applies to: AZW3, EPUB, MOBI output formats")),
"Applies to: AZW3, EPUB, MOBI output formats"),
Option('--preset',
default=None,
dest='preset',
action=None,
help=_("Use a named preset created with the GUI catalog builder.\n"
"A preset specifies all settings for building a catalog.\n"
help="Use a named preset created with the GUI "
"catalog builder.\n"
"A preset specifies all settings for building a "
"catalog.\n"
"Default: '%default'\n"
"Applies to: AZW3, EPUB, MOBI output formats")),
"Applies to: AZW3, EPUB, MOBI output formats"),
Option('--use-existing-cover',
default=False,
dest='use_existing_cover',
action='store_true',
help=_("Replace existing cover when generating the catalog.\n"
help="Replace existing cover when generating the "
"catalog.\n"
"Default: '%default'\n"
"Applies to: AZW3, EPUB, MOBI output formats")),
"Applies to: AZW3, EPUB, MOBI output formats"),
Option('--thumb-width',
default='1.0',
dest='thumb_width',
action=None,
help=_("Size hint (in inches) for book covers in catalog.\n"
help="Size hint (in inches) for book covers in "
"catalog.\n"
"Range: 1.0 - 2.0\n"
"Default: '%default'\n"
"Applies to: AZW3, EPUB, MOBI output formats")),
]
"Applies to: AZW3, EPUB, MOBI output formats")]
# }}}
def run(self, path_to_output, opts, db, notification=DummyReporter()):
@@ -196,10 +220,12 @@ class EPUB_MOBI(CatalogPlugin):
available_presets = JSONConfig("catalog_presets")
if opts.preset not in available_presets:
if available_presets:
print(_('Error: Preset "%s" not found.' % opts.preset))
print(_('Stored presets: %s' % ', '.join([p for p in sorted(available_presets.keys())])))
print('Error: Preset "%s" not found.' % opts.preset)
print('Stored presets: %s' %
', '.join([p for p in
sorted(available_presets.keys())]))
else:
print(_('Error: No stored presets.'))
print('Error: No stored presets.')
return 1
# Copy the relevant preset values to the opts object
@@ -329,7 +355,8 @@ class EPUB_MOBI(CatalogPlugin):
opts.log.warn('\n*** No enabled Sections, terminating catalog generation ***')
return ["No Included Sections", "No enabled Sections.\nCheck E-book options tab\n'Included sections'\n"]
if opts.fmt == 'mobi' and sections_list == ['Descriptions']:
warning = _("\n*** Adding 'By authors' section required for MOBI output ***")
warning = ("\n*** Adding 'By authors' section required for MOBI "
"output ***")
opts.log.warn(warning)
sections_list.insert(0, 'Authors')
opts.generate_authors = True
+22 -22
View File
@@ -41,7 +41,7 @@ def _builtin_field_metadata():
'ui_to_list': '&',
'list_to_ui': ' & '},
'kind':'field',
'name':_('Authors'),
'name':'Authors',
'search_terms':['authors', 'author'],
'is_custom':False,
'is_category':True,
@@ -55,7 +55,7 @@ def _builtin_field_metadata():
'ui_to_list': ',',
'list_to_ui': ', '},
'kind':'field',
'name':_('Languages'),
'name':'Languages',
'search_terms':['languages', 'language'],
'is_custom':False,
'is_category':True,
@@ -68,7 +68,7 @@ def _builtin_field_metadata():
'datatype':'series',
'is_multiple':{},
'kind':'field',
'name':ngettext('Series', 'Series', 1),
'name': 'Series',
'search_terms':['series'],
'is_custom':False,
'is_category':True,
@@ -80,7 +80,7 @@ def _builtin_field_metadata():
'ui_to_list': ',',
'list_to_ui': ', '},
'kind':'field',
'name':_('Formats'),
'name':'Formats',
'search_terms':['formats', 'format'],
'is_custom':False,
'is_category':True,
@@ -92,7 +92,7 @@ def _builtin_field_metadata():
'datatype':'text',
'is_multiple':{},
'kind':'field',
'name':_('Publisher'),
'name':'Publisher',
'search_terms':['publisher'],
'is_custom':False,
'is_category':True,
@@ -104,7 +104,7 @@ def _builtin_field_metadata():
'datatype':'rating',
'is_multiple':{},
'kind':'field',
'name':_('Rating'),
'name':'Rating',
'search_terms':['rating'],
'is_custom':False,
'is_category':True,
@@ -115,7 +115,7 @@ def _builtin_field_metadata():
'datatype':None,
'is_multiple':{},
'kind':'category',
'name':_('News'),
'name':'News',
'search_terms':[],
'is_custom':False,
'is_category':True,
@@ -129,7 +129,7 @@ def _builtin_field_metadata():
'ui_to_list': ',',
'list_to_ui': ', '},
'kind':'field',
'name':_('Tags'),
'name':'Tags',
'search_terms':['tags', 'tag'],
'is_custom':False,
'is_category':True,
@@ -141,7 +141,7 @@ def _builtin_field_metadata():
'ui_to_list': ',',
'list_to_ui': ', '},
'kind':'field',
'name':_('Identifiers'),
'name':'Identifiers',
'search_terms':['identifiers', 'identifier', 'isbn'],
'is_custom':False,
'is_category':True,
@@ -151,7 +151,7 @@ def _builtin_field_metadata():
'datatype':'text',
'is_multiple':{},
'kind':'field',
'name':_('Author sort'),
'name':'Author sort',
'search_terms':['author_sort'],
'is_custom':False,
'is_category':False,
@@ -173,7 +173,7 @@ def _builtin_field_metadata():
'datatype':'text',
'is_multiple':{},
'kind':'field',
'name':_('Comments'),
'name':'Comments',
'search_terms':['comments', 'comment'],
'is_custom':False,
'is_category':False,
@@ -183,7 +183,7 @@ def _builtin_field_metadata():
'datatype':'int',
'is_multiple':{},
'kind':'field',
'name':_('Cover'),
'name':'Cover',
'search_terms':['cover'],
'is_custom':False,
'is_category':False,
@@ -203,7 +203,7 @@ def _builtin_field_metadata():
'datatype':'datetime',
'is_multiple':{},
'kind':'field',
'name':_('Modified'),
'name':'Modified',
'search_terms':['last_modified'],
'is_custom':False,
'is_category':False,
@@ -213,7 +213,7 @@ def _builtin_field_metadata():
'datatype':'text',
'is_multiple':{},
'kind':'field',
'name':_('On device'),
'name':'On device',
'search_terms':['ondevice'],
'is_custom':False,
'is_category':False,
@@ -223,7 +223,7 @@ def _builtin_field_metadata():
'datatype':'text',
'is_multiple':{},
'kind':'field',
'name':_('Path'),
'name':'Path',
'search_terms':[],
'is_custom':False,
'is_category':False,
@@ -233,7 +233,7 @@ def _builtin_field_metadata():
'datatype':'datetime',
'is_multiple':{},
'kind':'field',
'name':_('Published'),
'name':'Published',
'search_terms':['pubdate'],
'is_custom':False,
'is_category':False,
@@ -263,7 +263,7 @@ def _builtin_field_metadata():
'datatype':'text',
'is_multiple':{},
'kind':'field',
'name':_('Series sort'),
'name':'Series sort',
'search_terms':['series_sort'],
'is_custom':False,
'is_category':False,
@@ -273,7 +273,7 @@ def _builtin_field_metadata():
'datatype':'text',
'is_multiple':{},
'kind':'field',
'name':_('Title sort'),
'name':'Title sort',
'search_terms':['title_sort'],
'is_custom':False,
'is_category':False,
@@ -283,7 +283,7 @@ def _builtin_field_metadata():
'datatype':'float',
'is_multiple':{},
'kind':'field',
'name':_('Size'),
'name':'Size',
'search_terms':['size'],
'is_custom':False,
'is_category':False,
@@ -293,7 +293,7 @@ def _builtin_field_metadata():
'datatype':'datetime',
'is_multiple':{},
'kind':'field',
'name':_('Date'),
'name':'Date',
'search_terms':['date'],
'is_custom':False,
'is_category':False,
@@ -303,7 +303,7 @@ def _builtin_field_metadata():
'datatype':'text',
'is_multiple':{},
'kind':'field',
'name':_('Title'),
'name':'Title',
'search_terms':['title'],
'is_custom':False,
'is_category':False,
@@ -452,7 +452,7 @@ class FieldMetadata(object):
'series_index', 'path', 'formats', 'identifiers', 'uuid',
'comments',
} if self._tb_cats[k]['name']}
ans['cover'] = _('Has cover')
ans['cover'] = 'Has cover'
return ans
def displayable_field_keys(self):
-13
View File
@@ -12,13 +12,6 @@ import sys
from ebook_converter import constants
# Default translation is NOOP
builtins.__dict__['_'] = lambda s: s
# For strings which belong in the translation tables, but which shouldn't be
# immediately translated to the environment language
builtins.__dict__['__'] = lambda s: s
# For backwards compat with some third party plugins
builtins.__dict__['dynamic_property'] = lambda func: func(None)
@@ -93,12 +86,6 @@ if not _run_once:
import ebook_converter.utils.resources as resources
resources
#
# Setup translations
from ebook_converter.utils.localization import set_translators
set_translators()
#
# Initialize locale
# Import string as we do not want locale specific
+7 -14
View File
@@ -16,10 +16,6 @@ from ebook_converter.utils.config_base import (
from ebook_converter.utils.lock import ExclusiveFile
# optparse uses gettext.gettext instead of _ from builtins, so we
# monkey patch it.
optparse._ = _
if False:
# Make pyflakes happy
Config, ConfigProxy, Option, OptionValues, StringConfig, OptionSet,
@@ -38,7 +34,7 @@ class CustomHelpFormatter(optparse.IndentedHelpFormatter):
if parts:
parts[0] = colored(parts[0], fg='yellow', bold=True)
usage = ' '.join(parts)
return colored(_('Usage'), fg='blue', bold=True) + ': ' + usage
return colored('Usage', fg='blue', bold=True) + ': ' + usage
def format_heading(self, heading):
from ebook_converter.utils.terminal import colored
@@ -89,21 +85,18 @@ class OptionParser(optparse.OptionParser):
usage = textwrap.dedent(usage)
if epilog is None:
epilog = _('Created by ')+colored(__author__, fg='cyan')
usage += '\n\n'+_('''Whenever you pass arguments to %prog that have spaces in them, '''
'''enclose the arguments in quotation marks. For example: "{}"''').format(
"C:\\some path with spaces" if iswindows else '/some path/with spaces') +'\n'
epilog = 'Created by ' + colored(__author__, fg='cyan')
usage += ('\n\nWhenever you pass arguments to %prog that have spaces '
'in them, enclose the arguments in quotation marks. For '
'example: "{}"\n\n').format("C:\\some path with spaces"
if iswindows
else '/some path/with spaces')
if version is None:
version = '%%prog (%s %s)'%(__appname__, get_version())
optparse.OptionParser.__init__(self, usage=usage, version=version, epilog=epilog,
formatter=CustomHelpFormatter(),
conflict_handler=conflict_handler, **kwds)
self.gui_mode = gui_mode
if False:
# Translatable string from optparse
_("Options")
_("show this help message and exit")
_("show program's version number and exit")
def print_usage(self, file=None):
from ebook_converter.utils.terminal import ANSIStream
+27 -29
View File
@@ -473,72 +473,70 @@ def create_global_prefs(conf_obj=None):
c = Config('global', 'calibre wide preferences') if conf_obj is None else conf_obj
c.add_opt('database_path',
default=os.path.expanduser('~/library1.db'),
help=_('Path to the database in which books are stored'))
help='Path to the database in which books are stored')
c.add_opt('filename_pattern', default=u'(?P<title>.+) - (?P<author>[^_]+)',
help=_('Pattern to guess metadata from filenames'))
help='Pattern to guess metadata from filenames')
c.add_opt('isbndb_com_key', default='',
help=_('Access key for isbndb.com'))
help='Access key for isbndb.com')
c.add_opt('network_timeout', default=5,
help=_('Default timeout for network operations (seconds)'))
help='Default timeout for network operations (seconds)')
c.add_opt('library_path', default=None,
help=_('Path to directory in which your library of books is stored'))
help='Path to directory in which your library of books is stored')
c.add_opt('language', default=None,
help=_('The language in which to display the user interface'))
help='The language in which to display the user interface')
c.add_opt('output_format', default='EPUB',
help=_('The default output format for e-book conversions. When auto-converting'
help='The default output format for e-book conversions. When auto-converting'
' to send to a device this can be overridden by individual device preferences.'
' These can be changed by right clicking the device icon in calibre and'
' choosing "Configure".'))
' choosing "Configure".')
c.add_opt('input_format_order', default=['EPUB', 'AZW3', 'MOBI', 'LIT', 'PRC',
'FB2', 'HTML', 'HTM', 'XHTM', 'SHTML', 'XHTML', 'ZIP', 'DOCX', 'ODT', 'RTF', 'PDF',
'TXT'],
help=_('Ordered list of formats to prefer for input.'))
help='Ordered list of formats to prefer for input.')
c.add_opt('read_file_metadata', default=True,
help=_('Read metadata from files'))
help='Read metadata from files')
c.add_opt('worker_process_priority', default='normal',
help=_('The priority of worker processes. A higher priority '
help='The priority of worker processes. A higher priority '
'means they run faster and consume more resources. '
'Most tasks like conversion/news download/adding books/etc. '
'are affected by this setting.'))
'are affected by this setting.')
c.add_opt('swap_author_names', default=False,
help=_('Swap author first and last names when reading metadata'))
help='Swap author first and last names when reading metadata')
c.add_opt('add_formats_to_existing', default=False,
help=_('Add new formats to existing book records'))
help='Add new formats to existing book records')
c.add_opt('check_for_dupes_on_ctl', default=False,
help=_('Check for duplicates when copying to another library'))
help='Check for duplicates when copying to another library')
c.add_opt('installation_uuid', default=None, help='Installation UUID')
c.add_opt('new_book_tags', default=[], help=_('Tags to apply to books added to the library'))
c.add_opt('mark_new_books', default=False, help=_(
'Mark newly added books. The mark is a temporary mark that is automatically removed when calibre is restarted.'))
c.add_opt('new_book_tags', default=[], help='Tags to apply to books added to the library')
c.add_opt('mark_new_books', default=False, help='Mark newly added books. The mark is a temporary mark that is automatically removed when calibre is restarted.')
# these are here instead of the gui preferences because calibredb and
# calibre server can execute searches
c.add_opt('saved_searches', default={}, help=_('List of named saved searches'))
c.add_opt('user_categories', default={}, help=_('User-created Tag browser categories'))
c.add_opt('saved_searches', default={}, help='List of named saved searches')
c.add_opt('user_categories', default={}, help='User-created Tag browser categories')
c.add_opt('manage_device_metadata', default='manual',
help=_('How and when calibre updates metadata on the device.'))
help='How and when calibre updates metadata on the device.')
c.add_opt('limit_search_columns', default=False,
help=_('When searching for text without using lookup '
help='When searching for text without using lookup '
'prefixes, as for example, Red instead of title:Red, '
'limit the columns searched to those named below.'))
'limit the columns searched to those named below.')
c.add_opt('limit_search_columns_to',
default=['title', 'authors', 'tags', 'series', 'publisher'],
help=_('Choose columns to be searched when not using prefixes, '
help='Choose columns to be searched when not using prefixes, '
'as for example, when searching for Red instead of '
'title:Red. Enter a list of search/lookup names '
'separated by commas. Only takes effect if you set the option '
'to limit search columns above.'))
'to limit search columns above.')
c.add_opt('use_primary_find_in_search', default=True,
help=_(u'Characters typed in the search box will match their '
help=u'Characters typed in the search box will match their '
'accented versions, based on the language you have chosen '
'for the calibre interface. For example, in '
u'English, searching for n will match both {} and n, but if '
'your language is Spanish it will only match n. Note that '
'this is much slower than a simple search on very large '
'libraries. Also, this option will have no effect if you turn '
'on case-sensitive searching'))
c.add_opt('case_sensitive', default=False, help=_(
'Make searches case-sensitive'))
'on case-sensitive searching')
c.add_opt('case_sensitive', default=False, help='Make searches case-sensitive')
c.add_opt('migrated', default=False, help='For Internal use. Don\'t modify.')
return c
+1 -1
View File
@@ -402,7 +402,7 @@ class WindowsAtomicFolderMove(object):
self.close_handles()
if getattr(e, 'winerror', 0) == winerror.ERROR_SHARING_VIOLATION:
err = IOError(errno.EACCES,
_('File is open in another process'))
'File is open in another process')
err.filename = f
raise err
prints('CreateFile failed for: %r' % f)
+15 -14
View File
@@ -30,7 +30,8 @@ class _Parser(object):
self.prog = prog[0]
self.prog_len = len(self.prog)
if prog[1] != '':
self.error(_('failed to scan program. Invalid input {0}').format(prog[1]))
self.error('failed to scan program. Invalid input '
'{0}'.format(prog[1]))
self.parent = parent
self.parent_kwargs = parent.kwargs
self.parent_book = parent.book
@@ -38,13 +39,13 @@ class _Parser(object):
self.funcs = funcs
def error(self, message):
m = 'Formatter: ' + message + _(' near ')
m = 'Formatter: ' + message + ' near '
if self.lex_pos > 0:
m = '{0} {1}'.format(m, self.prog[self.lex_pos-1][1])
elif self.lex_pos < self.prog_len:
m = '{0} {1}'.format(m, self.prog[self.lex_pos+1][1])
else:
m = '{0} {1}'.format(m, _('end of program'))
m = '{0} {1}'.format(m, 'end of program')
raise ValueError(m)
def token(self):
@@ -106,7 +107,7 @@ class _Parser(object):
def program(self):
val = self.statement()
if not self.token_is_eof():
self.error(_('syntax error - program ends before EOF'))
self.error('syntax error - program ends before EOF')
return val
def statement(self):
@@ -133,14 +134,14 @@ class _Parser(object):
self.parent_book, self.locals, id, self.expr())
val = self.locals.get(id, None)
if val is None:
self.error(_('Unknown identifier ') + id)
self.error('Unknown identifier ' + id)
return val
# We have a function.
# Check if it is a known one. We do this here so error reporting is
# better, as it can identify the tokens near the problem.
id = id.strip()
if id not in self.funcs:
self.error(_('unknown function {0}').format(id))
self.error('unknown function {0}'.format(id))
# Eat the paren
self.consume()
@@ -160,7 +161,7 @@ class _Parser(object):
break
self.consume()
if self.token() != ')':
self.error(_('missing closing parenthesis'))
self.error('missing closing parenthesis')
# Evaluate the function
cls = self.funcs[id]
@@ -172,7 +173,7 @@ class _Parser(object):
# String or number
return self.token()
else:
self.error(_('expression is not function or constant'))
self.error('expression is not function or constant')
class TemplateFormatter(string.Formatter):
@@ -206,14 +207,14 @@ class TemplateFormatter(string.Formatter):
try:
val = int(val)
except Exception:
raise ValueError(
_('format: type {0} requires an integer value, got {1}').format(typ, val))
raise ValueError('format: type {0} requires an integer value, '
'got {1}'.format(typ, val))
elif 'eEfFgGn%'.find(typ) >= 0:
try:
val = float(val)
except:
raise ValueError(
_('format: type {0} requires a decimal (float) value, got {1}').format(typ, val))
raise ValueError('format: type {0} requires a decimal (float) '
'value, got {1}'.format(typ, val))
return str(('{0:'+fmt+'}').format(val))
def _explode_format_string(self, fmt):
@@ -329,7 +330,7 @@ class TemplateFormatter(string.Formatter):
if self.strip_results:
val = val.strip()
else:
return _('%s: unknown function')%fname
return '%s: unknown function' % fname
if val:
val = self._do_format(val, dispfmt)
if not val:
@@ -408,7 +409,7 @@ class EvalFormatter(TemplateFormatter):
if key == '':
return ''
key = key.lower()
return kwargs.get(key, _('No such variable ') + key)
return kwargs.get(key, 'No such variable ' + key)
# DEPRECATED. This is not thread safe. Do not use.
+107 -104
View File
@@ -23,10 +23,10 @@ __docformat__ = 'restructuredtext en'
class FormatterFunctions(object):
error_function_body = ('def evaluate(self, formatter, kwargs, mi, locals):\n'
'\treturn "' +
_('Duplicate user function name {0}. '
'Change the name or ensure that the functions are identical') + '"')
error_function_body = ('def evaluate(self, formatter, kwargs, mi, '
'locals):\n\treturn "Duplicate user function name '
'{0}. Change the name or ensure that the functions '
'are identical"')
def __init__(self):
self._builtins = {}
@@ -39,7 +39,7 @@ class FormatterFunctions(object):
func_class.__class__.__name__))
name = func_class.name
if name in self._functions:
raise ValueError('Name %s already used'%name)
raise ValueError('Name %s already used' % name)
self._builtins[name] = func_class
self._functions[name] = func_class
for a in func_class.aliases:
@@ -51,7 +51,7 @@ class FormatterFunctions(object):
func_class.__class__.__name__))
name = func_class.name
if not replace and name in self._functions:
raise ValueError('Name %s already used'%name)
raise ValueError('Name %s already used' % name)
self._functions[name] = func_class
def register_functions(self, library_uuid, funcs):
@@ -116,7 +116,7 @@ def formatter_functions():
class FormatterFunction(object):
doc = _('No documentation provided')
doc = 'No documentation provided'
name = 'no name provided'
category = 'Unknown'
arg_count = 0
@@ -152,9 +152,9 @@ class BuiltinStrcmp(BuiltinFormatterFunction):
name = 'strcmp'
arg_count = 5
category = 'Relational'
__doc__ = doc = _('strcmp(x, y, lt, eq, gt) -- does a case-insensitive comparison of x '
'and y as strings. Returns lt if x < y. Returns eq if x == y. '
'Otherwise returns gt.')
__doc__ = doc = ('strcmp(x, y, lt, eq, gt) -- does a case-insensitive '
'comparison of x and y as strings. Returns lt if x < y. '
'Returns eq if x == y. Otherwise returns gt.')
def evaluate(self, formatter, kwargs, mi, locals, x, y, lt, eq, gt):
v = strcmp(x, y)
@@ -169,8 +169,9 @@ class BuiltinCmp(BuiltinFormatterFunction):
name = 'cmp'
category = 'Relational'
arg_count = 5
__doc__ = doc = _('cmp(x, y, lt, eq, gt) -- compares x and y after converting both to '
'numbers. Returns lt if x < y. Returns eq if x == y. Otherwise returns gt.')
__doc__ = doc = ('cmp(x, y, lt, eq, gt) -- compares x and y after '
'converting both to numbers. Returns lt if x < y. '
'Returns eq if x == y. Otherwise returns gt.')
def evaluate(self, formatter, kwargs, mi, locals, x, y, lt, eq, gt):
x = float(x if x and x != 'None' else 0)
@@ -186,16 +187,18 @@ class BuiltinFirstMatchingCmp(BuiltinFormatterFunction):
name = 'first_matching_cmp'
category = 'Relational'
arg_count = -1
__doc__ = doc = _('first_matching_cmp(val, cmp1, result1, cmp2, r2, ..., else_result) -- '
'compares "val < cmpN" in sequence, returning resultN for '
'the first comparison that succeeds. Returns else_result '
'if no comparison succeeds. Example: '
'first_matching_cmp(10,5,"small",10,"middle",15,"large","giant") '
'returns "large". The same example with a first value of 16 returns "giant".')
__doc__ = doc = ('first_matching_cmp(val, cmp1, result1, cmp2, r2, ..., '
'else_result) -- compares "val < cmpN" in sequence, '
'returning resultN for the first comparison that '
'succeeds. Returns else_result if no comparison '
'succeeds. Example: first_matching_cmp(10,5,"small",10,'
'"middle",15,"large","giant") returns "large". The same '
'example with a first value of 16 returns "giant".')
def evaluate(self, formatter, kwargs, mi, locals, *args):
if (len(args) % 2) != 0:
raise ValueError(_('first_matching_cmp requires an even number of arguments'))
raise ValueError('first_matching_cmp requires an even number of '
'arguments')
val = float(args[0] if args[0] and args[0] != 'None' else 0)
for i in range(1, len(args) - 1, 2):
c = float(args[i] if args[i] and args[i] != 'None' else 0)
@@ -208,7 +211,7 @@ class BuiltinStrcat(BuiltinFormatterFunction):
name = 'strcat'
arg_count = -1
category = 'String manipulation'
__doc__ = doc = _('strcat(a, b, ...) -- can take any number of arguments. Returns a '
__doc__ = doc = ('strcat(a, b, ...) -- can take any number of arguments. Returns a '
'string formed by concatenating all the arguments')
def evaluate(self, formatter, kwargs, mi, locals, *args):
@@ -223,7 +226,7 @@ class BuiltinStrlen(BuiltinFormatterFunction):
name = 'strlen'
arg_count = 1
category = 'String manipulation'
__doc__ = doc = _('strlen(a) -- Returns the length of the string passed as '
__doc__ = doc = ('strlen(a) -- Returns the length of the string passed as '
'the argument')
def evaluate(self, formatter, kwargs, mi, locals, a):
@@ -237,7 +240,7 @@ class BuiltinAdd(BuiltinFormatterFunction):
name = 'add'
arg_count = 2
category = 'Arithmetic'
__doc__ = doc = _('add(x, y) -- returns x + y. Throws an exception if either x or y are not numbers.')
__doc__ = doc = 'add(x, y) -- returns x + y. Throws an exception if either x or y are not numbers.'
def evaluate(self, formatter, kwargs, mi, locals, x, y):
x = float(x if x and x != 'None' else 0)
@@ -249,7 +252,7 @@ class BuiltinSubtract(BuiltinFormatterFunction):
name = 'subtract'
arg_count = 2
category = 'Arithmetic'
__doc__ = doc = _('subtract(x, y) -- returns x - y. Throws an exception if either x or y are not numbers.')
__doc__ = doc = 'subtract(x, y) -- returns x - y. Throws an exception if either x or y are not numbers.'
def evaluate(self, formatter, kwargs, mi, locals, x, y):
x = float(x if x and x != 'None' else 0)
@@ -261,7 +264,7 @@ class BuiltinMultiply(BuiltinFormatterFunction):
name = 'multiply'
arg_count = 2
category = 'Arithmetic'
__doc__ = doc = _('multiply(x, y) -- returns x * y. Throws an exception if either x or y are not numbers.')
__doc__ = doc = 'multiply(x, y) -- returns x * y. Throws an exception if either x or y are not numbers.'
def evaluate(self, formatter, kwargs, mi, locals, x, y):
x = float(x if x and x != 'None' else 0)
@@ -273,7 +276,7 @@ class BuiltinDivide(BuiltinFormatterFunction):
name = 'divide'
arg_count = 2
category = 'Arithmetic'
__doc__ = doc = _('divide(x, y) -- returns x / y. Throws an exception if either x or y are not numbers.')
__doc__ = doc = 'divide(x, y) -- returns x / y. Throws an exception if either x or y are not numbers.'
def evaluate(self, formatter, kwargs, mi, locals, x, y):
x = float(x if x and x != 'None' else 0)
@@ -286,7 +289,7 @@ class BuiltinTemplate(BuiltinFormatterFunction):
arg_count = 1
category = 'Recursion'
__doc__ = doc = _('template(x) -- evaluates x as a template. The evaluation is done '
__doc__ = doc = ('template(x) -- evaluates x as a template. The evaluation is done '
'in its own context, meaning that variables are not shared between '
'the caller and the template evaluation. Because the { and } '
'characters are special, you must use [[ for the { character and '
@@ -305,7 +308,7 @@ class BuiltinEval(BuiltinFormatterFunction):
name = 'eval'
arg_count = 1
category = 'Recursion'
__doc__ = doc = _('eval(template) -- evaluates the template, passing the local '
__doc__ = doc = ('eval(template) -- evaluates the template, passing the local '
'variables (those \'assign\'ed to) instead of the book metadata. '
' This permits using the template processor to construct complex '
'results from local variables. Because the { and } '
@@ -325,7 +328,7 @@ class BuiltinAssign(BuiltinFormatterFunction):
name = 'assign'
arg_count = 2
category = 'Other'
__doc__ = doc = _('assign(id, val) -- assigns val to id, then returns val. '
__doc__ = doc = ('assign(id, val) -- assigns val to id, then returns val. '
'id must be an identifier, not an expression')
def evaluate(self, formatter, kwargs, mi, locals, target, value):
@@ -337,7 +340,7 @@ class BuiltinPrint(BuiltinFormatterFunction):
name = 'print'
arg_count = -1
category = 'Other'
__doc__ = doc = _('print(a, b, ...) -- prints the arguments to standard output. '
__doc__ = doc = ('print(a, b, ...) -- prints the arguments to standard output. '
'Unless you start calibre from the command line (calibre-debug -g), '
'the output will go to a black hole.')
@@ -350,7 +353,7 @@ class BuiltinField(BuiltinFormatterFunction):
name = 'field'
arg_count = 1
category = 'Get values from metadata'
__doc__ = doc = _('field(name) -- returns the metadata field named by name')
__doc__ = doc = ('field(name) -- returns the metadata field named by name')
def evaluate(self, formatter, kwargs, mi, locals, name):
return formatter.get_value(name, [], kwargs)
@@ -360,7 +363,7 @@ class BuiltinRawField(BuiltinFormatterFunction):
name = 'raw_field'
arg_count = 1
category = 'Get values from metadata'
__doc__ = doc = _('raw_field(name) -- returns the metadata field named by name '
__doc__ = doc = ('raw_field(name) -- returns the metadata field named by name '
'without applying any formatting.')
def evaluate(self, formatter, kwargs, mi, locals, name):
@@ -377,7 +380,7 @@ class BuiltinRawList(BuiltinFormatterFunction):
name = 'raw_list'
arg_count = 2
category = 'Get values from metadata'
__doc__ = doc = _('raw_list(name, separator) -- returns the metadata list '
__doc__ = doc = ('raw_list(name, separator) -- returns the metadata list '
'named by name without applying any formatting or sorting and '
'with items separated by separator.')
@@ -392,7 +395,7 @@ class BuiltinSubstr(BuiltinFormatterFunction):
name = 'substr'
arg_count = 3
category = 'String manipulation'
__doc__ = doc = _('substr(str, start, end) -- returns the start\'th through the end\'th '
__doc__ = doc = ('substr(str, start, end) -- returns the start\'th through the end\'th '
'characters of str. The first character in str is the zero\'th '
'character. If end is negative, then it indicates that many '
'characters counting from the right. If end is zero, then it '
@@ -407,7 +410,7 @@ class BuiltinLookup(BuiltinFormatterFunction):
name = 'lookup'
arg_count = -1
category = 'Iterating over values'
__doc__ = doc = _('lookup(val, pattern, field, pattern, field, ..., else_field) -- '
__doc__ = doc = ('lookup(val, pattern, field, pattern, field, ..., else_field) -- '
'like switch, except the arguments are field (metadata) names, not '
'text. The value of the appropriate field will be fetched and used. '
'Note that because composite columns are fields, you can use this '
@@ -422,7 +425,7 @@ class BuiltinLookup(BuiltinFormatterFunction):
else:
return formatter.vformat('{'+args[1].strip()+'}', [], kwargs)
if (len(args) % 2) != 1:
raise ValueError(_('lookup requires either 2 or an odd number of arguments'))
raise ValueError('lookup requires either 2 or an odd number of arguments')
i = 0
while i < len(args):
if i + 1 >= len(args):
@@ -436,7 +439,7 @@ class BuiltinTest(BuiltinFormatterFunction):
name = 'test'
arg_count = 3
category = 'If-then-else'
__doc__ = doc = _('test(val, text if not empty, text if empty) -- return `text if not '
__doc__ = doc = ('test(val, text if not empty, text if empty) -- return `text if not '
'empty` if val is not empty, otherwise return `text if empty`')
def evaluate(self, formatter, kwargs, mi, locals, val, value_if_set, value_not_set):
@@ -450,7 +453,7 @@ class BuiltinContains(BuiltinFormatterFunction):
name = 'contains'
arg_count = 4
category = 'If-then-else'
__doc__ = doc = _('contains(val, pattern, text if match, text if not match) -- checks '
__doc__ = doc = ('contains(val, pattern, text if match, text if not match) -- checks '
'if val contains matches for the regular expression `pattern`. '
'Returns `text if match` if matches are found, otherwise it returns '
'`text if no match`')
@@ -467,7 +470,7 @@ class BuiltinSwitch(BuiltinFormatterFunction):
name = 'switch'
arg_count = -1
category = 'Iterating over values'
__doc__ = doc = _('switch(val, pattern, value, pattern, value, ..., else_value) -- '
__doc__ = doc = ('switch(val, pattern, value, pattern, value, ..., else_value) -- '
'for each `pattern, value` pair, checks if `val` matches '
'the regular expression `pattern` and if so, returns that '
'`value`. If no pattern matches, then `else_value` is returned. '
@@ -475,7 +478,7 @@ class BuiltinSwitch(BuiltinFormatterFunction):
def evaluate(self, formatter, kwargs, mi, locals, val, *args):
if (len(args) % 2) != 1:
raise ValueError(_('switch requires an odd number of arguments'))
raise ValueError('switch requires an odd number of arguments')
i = 0
while i < len(args):
if i + 1 >= len(args):
@@ -489,7 +492,7 @@ class BuiltinStrcatMax(BuiltinFormatterFunction):
name = 'strcat_max'
arg_count = -1
category = 'String manipulation'
__doc__ = doc = _('strcat_max(max, string1, prefix2, string2, ...) -- '
__doc__ = doc = ('strcat_max(max, string1, prefix2, string2, ...) -- '
'Returns a string formed by concatenating the arguments. The '
'returned value is initialized to string1. `Prefix, string` '
'pairs are added to the end of the value as long as the '
@@ -499,13 +502,13 @@ class BuiltinStrcatMax(BuiltinFormatterFunction):
def evaluate(self, formatter, kwargs, mi, locals, *args):
if len(args) < 2:
raise ValueError(_('strcat_max requires 2 or more arguments'))
raise ValueError('strcat_max requires 2 or more arguments')
if (len(args) % 2) != 0:
raise ValueError(_('strcat_max requires an even number of arguments'))
raise ValueError('strcat_max requires an even number of arguments')
try:
max = int(args[0])
except:
raise ValueError(_('first argument to strcat_max must be an integer'))
raise ValueError('first argument to strcat_max must be an integer')
i = 2
result = args[1]
@@ -524,7 +527,7 @@ class BuiltinInList(BuiltinFormatterFunction):
name = 'in_list'
arg_count = -1
category = 'List lookup'
__doc__ = doc = _('in_list(val, separator, pattern, found_val, ..., not_found_val) -- '
__doc__ = doc = ('in_list(val, separator, pattern, found_val, ..., not_found_val) -- '
'treat val as a list of items separated by separator, '
'evaluating the pattern against each value in the list. If the '
'pattern matches a value, return found_val, otherwise return '
@@ -535,7 +538,7 @@ class BuiltinInList(BuiltinFormatterFunction):
def evaluate(self, formatter, kwargs, mi, locals, val, sep, *args):
if (len(args) % 2) != 1:
raise ValueError(_('in_list requires an odd number of arguments'))
raise ValueError('in_list requires an odd number of arguments')
l = [v.strip() for v in val.split(sep) if v.strip()]
i = 0
while i < len(args):
@@ -554,7 +557,7 @@ class BuiltinStrInList(BuiltinFormatterFunction):
name = 'str_in_list'
arg_count = -1
category = 'List lookup'
__doc__ = doc = _('str_in_list(val, separator, string, found_val, ..., not_found_val) -- '
__doc__ = doc = ('str_in_list(val, separator, string, found_val, ..., not_found_val) -- '
'treat val as a list of items separated by separator, '
'comparing the string against each value in the list. If the '
'string matches a value (ignoring case) then return found_val, otherwise return '
@@ -566,7 +569,7 @@ class BuiltinStrInList(BuiltinFormatterFunction):
def evaluate(self, formatter, kwargs, mi, locals, val, sep, *args):
if (len(args) % 2) != 1:
raise ValueError(_('str_in_list requires an odd number of arguments'))
raise ValueError('str_in_list requires an odd number of arguments')
l = [v.strip() for v in val.split(sep) if v.strip()]
i = 0
while i < len(args):
@@ -587,7 +590,7 @@ class BuiltinIdentifierInList(BuiltinFormatterFunction):
name = 'identifier_in_list'
arg_count = 4
category = 'List lookup'
__doc__ = doc = _('identifier_in_list(val, id, found_val, not_found_val) -- '
__doc__ = doc = ('identifier_in_list(val, id, found_val, not_found_val) -- '
'treat val as a list of identifiers separated by commas, '
'comparing the string against each value in the list. An identifier '
'has the format "identifier:value". The id parameter should be '
@@ -614,7 +617,7 @@ class BuiltinRe(BuiltinFormatterFunction):
name = 're'
arg_count = 3
category = 'String manipulation'
__doc__ = doc = _('re(val, pattern, replacement) -- return val after applying '
__doc__ = doc = ('re(val, pattern, replacement) -- return val after applying '
'the regular expression. All instances of `pattern` are replaced '
'with `replacement`. As in all of calibre, these are '
'Python-compatible regular expressions')
@@ -627,7 +630,7 @@ class BuiltinReGroup(BuiltinFormatterFunction):
name = 're_group'
arg_count = -1
category = 'String manipulation'
__doc__ = doc = _('re_group(val, pattern, template_for_group_1, for_group_2, ...) -- '
__doc__ = doc = ('re_group(val, pattern, template_for_group_1, for_group_2, ...) -- '
'return a string made by applying the regular expression pattern '
'to the val and replacing each matched instance with the string '
'computed by replacing each matched group by the value returned '
@@ -662,7 +665,7 @@ class BuiltinSwapAroundComma(BuiltinFormatterFunction):
name = 'swap_around_comma'
arg_count = 1
category = 'String manipulation'
__doc__ = doc = _('swap_around_comma(val) -- given a value of the form '
__doc__ = doc = ('swap_around_comma(val) -- given a value of the form '
'"B, A", return "A B". This is most useful for converting names '
'in LN, FN format to FN LN. If there is no comma, the function '
'returns val unchanged')
@@ -675,7 +678,7 @@ class BuiltinIfempty(BuiltinFormatterFunction):
name = 'ifempty'
arg_count = 2
category = 'If-then-else'
__doc__ = doc = _('ifempty(val, text if empty) -- return val if val is not empty, '
__doc__ = doc = ('ifempty(val, text if empty) -- return val if val is not empty, '
'otherwise return `text if empty`')
def evaluate(self, formatter, kwargs, mi, locals, val, value_if_empty):
@@ -689,7 +692,7 @@ class BuiltinShorten(BuiltinFormatterFunction):
name = 'shorten'
arg_count = 4
category = 'String manipulation'
__doc__ = doc = _('shorten(val, left chars, middle text, right chars) -- Return a '
__doc__ = doc = ('shorten(val, left chars, middle text, right chars) -- Return a '
'shortened version of val, consisting of `left chars` '
'characters from the beginning of val, followed by '
'`middle text`, followed by `right chars` characters from '
@@ -716,7 +719,7 @@ class BuiltinCount(BuiltinFormatterFunction):
name = 'count'
arg_count = 2
category = 'List manipulation'
__doc__ = doc = _('count(val, separator) -- interprets the value as a list of items '
__doc__ = doc = ('count(val, separator) -- interprets the value as a list of items '
'separated by `separator`, returning the number of items in the '
'list. Most lists use a comma as the separator, but authors '
'uses an ampersand. Examples: {tags:count(,)}, {authors:count(&)}')
@@ -729,7 +732,7 @@ class BuiltinListitem(BuiltinFormatterFunction):
name = 'list_item'
arg_count = 3
category = 'List lookup'
__doc__ = doc = _('list_item(val, index, separator) -- interpret the value as a list of '
__doc__ = doc = ('list_item(val, index, separator) -- interpret the value as a list of '
'items separated by `separator`, returning the `index`th item. '
'The first item is number zero. The last item can be returned '
'using `list_item(-1,separator)`. If the item is not in the list, '
@@ -751,7 +754,7 @@ class BuiltinSelect(BuiltinFormatterFunction):
name = 'select'
arg_count = 2
category = 'List lookup'
__doc__ = doc = _('select(val, key) -- interpret the value as a comma-separated list '
__doc__ = doc = ('select(val, key) -- interpret the value as a comma-separated list '
'of items, with the items being "id:value". Find the pair with the '
'id equal to key, and return the corresponding value.'
)
@@ -770,7 +773,7 @@ class BuiltinApproximateFormats(BuiltinFormatterFunction):
name = 'approximate_formats'
arg_count = 0
category = 'Get values from metadata'
__doc__ = doc = _('approximate_formats() -- return a comma-separated '
__doc__ = doc = ('approximate_formats() -- return a comma-separated '
'list of formats that at one point were associated with the '
'book. There is no guarantee that this list is correct, '
'although it probably is. '
@@ -791,14 +794,14 @@ class BuiltinApproximateFormats(BuiltinFormatterFunction):
return ''
data = sorted(fmt_data)
return ','.join(v.upper() for v in data)
return _('This function can be used only in the GUI')
return 'This function can be used only in the GUI'
class BuiltinFormatsModtimes(BuiltinFormatterFunction):
name = 'formats_modtimes'
arg_count = 1
category = 'Get values from metadata'
__doc__ = doc = _('formats_modtimes(date_format) -- return a comma-separated '
__doc__ = doc = ('formats_modtimes(date_format) -- return a comma-separated '
'list of colon-separated items representing modification times '
'for the formats of a book. The date_format parameter '
'specifies how the date is to be formatted. See the '
@@ -822,7 +825,7 @@ class BuiltinFormatsSizes(BuiltinFormatterFunction):
name = 'formats_sizes'
arg_count = 0
category = 'Get values from metadata'
__doc__ = doc = _('formats_sizes() -- return a comma-separated list of '
__doc__ = doc = ('formats_sizes() -- return a comma-separated list of '
'colon-separated items representing sizes in bytes '
'of the formats of a book. You can use the select '
'function to get the size for a specific '
@@ -842,7 +845,7 @@ class BuiltinFormatsPaths(BuiltinFormatterFunction):
name = 'formats_paths'
arg_count = 0
category = 'Get values from metadata'
__doc__ = doc = _('formats_paths() -- return a comma-separated list of '
__doc__ = doc = ('formats_paths() -- return a comma-separated list of '
'colon-separated items representing full path to '
'the formats of a book. You can use the select '
'function to get the path for a specific '
@@ -861,7 +864,7 @@ class BuiltinHumanReadable(BuiltinFormatterFunction):
name = 'human_readable'
arg_count = 1
category = 'Formatting values'
__doc__ = doc = _('human_readable(v) -- return a string '
__doc__ = doc = ('human_readable(v) -- return a string '
'representing the number v in KB, MB, GB, etc.'
)
@@ -876,7 +879,7 @@ class BuiltinFormatNumber(BuiltinFormatterFunction):
name = 'format_number'
arg_count = 2
category = 'Formatting values'
__doc__ = doc = _('format_number(v, template) -- format the number v using '
__doc__ = doc = ('format_number(v, template) -- format the number v using '
'a Python formatting template such as "{0:5.2f}" or '
'"{0:,d}" or "${0:5,.2f}". The field_name part of the '
'template must be a 0 (zero) (the "{0:" in the above examples). '
@@ -912,7 +915,7 @@ class BuiltinSublist(BuiltinFormatterFunction):
name = 'sublist'
arg_count = 4
category = 'List manipulation'
__doc__ = doc = _('sublist(val, start_index, end_index, separator) -- interpret the '
__doc__ = doc = ('sublist(val, start_index, end_index, separator) -- interpret the '
'value as a list of items separated by `separator`, returning a '
'new list made from the `start_index` to the `end_index` item. '
'The first item is number zero. If an index is negative, then it '
@@ -948,7 +951,7 @@ class BuiltinSubitems(BuiltinFormatterFunction):
name = 'subitems'
arg_count = 3
category = 'List manipulation'
__doc__ = doc = _('subitems(val, start_index, end_index) -- This function is used to '
__doc__ = doc = ('subitems(val, start_index, end_index) -- This function is used to '
'break apart lists of items such as genres. It interprets the value '
'as a comma-separated list of items, where each item is a period-'
'separated list. Returns a new list made by first finding all the '
@@ -993,7 +996,7 @@ class BuiltinFormatDate(BuiltinFormatterFunction):
name = 'format_date'
arg_count = 2
category = 'Formatting values'
__doc__ = doc = _('format_date(val, format_string) -- format the value, '
__doc__ = doc = ('format_date(val, format_string) -- format the value, '
'which must be a date, using the format_string, returning a string. '
'The formatting codes are: '
'd : the day as number without a leading zero (1 to 31) '
@@ -1031,7 +1034,7 @@ class BuiltinUppercase(BuiltinFormatterFunction):
name = 'uppercase'
arg_count = 1
category = 'String case changes'
__doc__ = doc = _('uppercase(val) -- return val in upper case')
__doc__ = doc = 'uppercase(val) -- return val in upper case'
def evaluate(self, formatter, kwargs, mi, locals, val):
return val.upper()
@@ -1041,7 +1044,7 @@ class BuiltinLowercase(BuiltinFormatterFunction):
name = 'lowercase'
arg_count = 1
category = 'String case changes'
__doc__ = doc = _('lowercase(val) -- return val in lower case')
__doc__ = doc = 'lowercase(val) -- return val in lower case'
def evaluate(self, formatter, kwargs, mi, locals, val):
return val.lower()
@@ -1051,7 +1054,7 @@ class BuiltinTitlecase(BuiltinFormatterFunction):
name = 'titlecase'
arg_count = 1
category = 'String case changes'
__doc__ = doc = _('titlecase(val) -- return val in title case')
__doc__ = doc = 'titlecase(val) -- return val in title case'
def evaluate(self, formatter, kwargs, mi, locals, val):
return titlecase(val)
@@ -1061,7 +1064,7 @@ class BuiltinCapitalize(BuiltinFormatterFunction):
name = 'capitalize'
arg_count = 1
category = 'String case changes'
__doc__ = doc = _('capitalize(val) -- return val capitalized')
__doc__ = doc = 'capitalize(val) -- return val capitalized'
def evaluate(self, formatter, kwargs, mi, locals, val):
return capitalize(val)
@@ -1071,7 +1074,7 @@ class BuiltinBooksize(BuiltinFormatterFunction):
name = 'booksize'
arg_count = 0
category = 'Get values from metadata'
__doc__ = doc = _('booksize() -- return value of the size field. '
__doc__ = doc = ('booksize() -- return value of the size field. '
'This function works only in the GUI. If you want to use this value '
'in save-to-disk or send-to-device templates then you '
'must make a custom "Column built from other columns", use '
@@ -1088,14 +1091,14 @@ class BuiltinBooksize(BuiltinFormatterFunction):
except:
pass
return ''
return _('This function can be used only in the GUI')
return 'This function can be used only in the GUI'
class BuiltinOndevice(BuiltinFormatterFunction):
name = 'ondevice'
arg_count = 0
category = 'Get values from metadata'
__doc__ = doc = _('ondevice() -- return Yes if ondevice is set, otherwise return '
__doc__ = doc = ('ondevice() -- return Yes if ondevice is set, otherwise return '
'the empty string. This function works only in the GUI. If you want to '
'use this value in save-to-disk or send-to-device templates then you '
'must make a custom "Column built from other columns", use '
@@ -1105,16 +1108,16 @@ class BuiltinOndevice(BuiltinFormatterFunction):
def evaluate(self, formatter, kwargs, mi, locals):
if hasattr(mi, '_proxy_metadata'):
if mi._proxy_metadata.ondevice_col:
return _('Yes')
return 'Yes'
return ''
return _('This function can be used only in the GUI')
return 'This function can be used only in the GUI'
class BuiltinSeriesSort(BuiltinFormatterFunction):
name = 'series_sort'
arg_count = 0
category = 'Get values from metadata'
__doc__ = doc = _('series_sort() -- return the series sort value')
__doc__ = doc = 'series_sort() -- return the series sort value'
def evaluate(self, formatter, kwargs, mi, locals):
if mi.series:
@@ -1126,12 +1129,12 @@ class BuiltinHasCover(BuiltinFormatterFunction):
name = 'has_cover'
arg_count = 0
category = 'Get values from metadata'
__doc__ = doc = _('has_cover() -- return Yes if the book has a cover, '
__doc__ = doc = ('has_cover() -- return Yes if the book has a cover, '
'otherwise return the empty string')
def evaluate(self, formatter, kwargs, mi, locals):
if mi.has_cover:
return _('Yes')
return 'Yes'
return ''
@@ -1139,7 +1142,7 @@ class BuiltinFirstNonEmpty(BuiltinFormatterFunction):
name = 'first_non_empty'
arg_count = -1
category = 'Iterating over values'
__doc__ = doc = _('first_non_empty(value, value, ...) -- '
__doc__ = doc = ('first_non_empty(value, value, ...) -- '
'returns the first value that is not empty. If all values are '
'empty, then the empty value is returned. '
'You can have as many values as you want.')
@@ -1157,7 +1160,7 @@ class BuiltinAnd(BuiltinFormatterFunction):
name = 'and'
arg_count = -1
category = 'Boolean'
__doc__ = doc = _('and(value, value, ...) -- '
__doc__ = doc = ('and(value, value, ...) -- '
'returns the string "1" if all values are not empty, otherwise '
'returns the empty string. This function works well with test or '
'first_non_empty. You can have as many values as you want. ')
@@ -1175,7 +1178,7 @@ class BuiltinOr(BuiltinFormatterFunction):
name = 'or'
arg_count = -1
category = 'Boolean'
__doc__ = doc = _('or(value, value, ...) -- '
__doc__ = doc = ('or(value, value, ...) -- '
'returns the string "1" if any value is not empty, otherwise '
'returns the empty string. This function works well with test or '
'first_non_empty. You can have as many values as you want.')
@@ -1193,7 +1196,7 @@ class BuiltinNot(BuiltinFormatterFunction):
name = 'not'
arg_count = 1
category = 'Boolean'
__doc__ = doc = _('not(value) -- '
__doc__ = doc = ('not(value) -- '
'returns the string "1" if the value is empty, otherwise '
'returns the empty string. This function works well with test or '
'first_non_empty.')
@@ -1206,7 +1209,7 @@ class BuiltinListUnion(BuiltinFormatterFunction):
name = 'list_union'
arg_count = 3
category = 'List manipulation'
__doc__ = doc = _('list_union(list1, list2, separator) -- '
__doc__ = doc = ('list_union(list1, list2, separator) -- '
'return a list made by merging the items in list1 and list2, '
'removing duplicate items using a case-insensitive comparison. If '
'items differ in case, the one in list1 is used. '
@@ -1231,7 +1234,7 @@ class BuiltinListDifference(BuiltinFormatterFunction):
name = 'list_difference'
arg_count = 3
category = 'List manipulation'
__doc__ = doc = _('list_difference(list1, list2, separator) -- '
__doc__ = doc = ('list_difference(list1, list2, separator) -- '
'return a list made by removing from list1 any item found in list2, '
'using a case-insensitive comparison. The items in list1 and list2 '
'are separated by separator, as are the items in the returned list.')
@@ -1253,7 +1256,7 @@ class BuiltinListIntersection(BuiltinFormatterFunction):
name = 'list_intersection'
arg_count = 3
category = 'List manipulation'
__doc__ = doc = _('list_intersection(list1, list2, separator) -- '
__doc__ = doc = ('list_intersection(list1, list2, separator) -- '
'return a list made by removing from list1 any item not found in list2, '
'using a case-insensitive comparison. The items in list1 and list2 '
'are separated by separator, as are the items in the returned list.')
@@ -1275,7 +1278,7 @@ class BuiltinListSort(BuiltinFormatterFunction):
name = 'list_sort'
arg_count = 3
category = 'List manipulation'
__doc__ = doc = _('list_sort(list, direction, separator) -- '
__doc__ = doc = ('list_sort(list, direction, separator) -- '
'return list sorted using a case-insensitive sort. If direction is '
'zero, the list is sorted ascending, otherwise descending. The list items '
'are separated by separator, as are the items in the returned list.')
@@ -1291,7 +1294,7 @@ class BuiltinListEquals(BuiltinFormatterFunction):
name = 'list_equals'
arg_count = 6
category = 'List manipulation'
__doc__ = doc = _('list_equals(list1, sep1, list2, sep2, yes_val, no_val) -- '
__doc__ = doc = ('list_equals(list1, sep1, list2, sep2, yes_val, no_val) -- '
'return yes_val if list1 and list2 contain the same items, '
'otherwise return no_val. The items are determined by splitting '
'each list using the appropriate separator character (sep1 or '
@@ -1310,7 +1313,7 @@ class BuiltinListRe(BuiltinFormatterFunction):
name = 'list_re'
arg_count = 4
category = 'List manipulation'
__doc__ = doc = _('list_re(src_list, separator, include_re, opt_replace) -- '
__doc__ = doc = ('list_re(src_list, separator, include_re, opt_replace) -- '
'Construct a list by first separating src_list into items using '
'the separator character. For each item in the list, check if it '
'matches include_re. If it does, then add it to the list to be '
@@ -1336,7 +1339,7 @@ class BuiltinListReGroup(BuiltinFormatterFunction):
name = 'list_re_group'
arg_count = -1
category = 'List manipulation'
__doc__ = doc = _('list_re_group(src_list, separator, include_re, search_re, group_1_template, ...) -- '
__doc__ = doc = ('list_re_group(src_list, separator, include_re, search_re, group_1_template, ...) -- '
'Like list_re except replacements are not optional. It '
'uses re_group(list_item, search_re, group_1_template, ...) when '
'doing the replacements on the resulting list.')
@@ -1376,7 +1379,7 @@ class BuiltinToday(BuiltinFormatterFunction):
name = 'today'
arg_count = 0
category = 'Date functions'
__doc__ = doc = _('today() -- '
__doc__ = doc = ('today() -- '
'return a date string for today. This value is designed for use in '
'format_date or days_between, but can be manipulated like any '
'other string. The date is in ISO format.')
@@ -1389,7 +1392,7 @@ class BuiltinDaysBetween(BuiltinFormatterFunction):
name = 'days_between'
arg_count = 2
category = 'Date functions'
__doc__ = doc = _('days_between(date1, date2) -- '
__doc__ = doc = ('days_between(date1, date2) -- '
'return the number of days between date1 and date2. The number is '
'positive if date1 is greater than date2, otherwise negative. If '
'either date1 or date2 are not dates, the function returns the '
@@ -1413,7 +1416,7 @@ class BuiltinLanguageStrings(BuiltinFormatterFunction):
name = 'language_strings'
arg_count = 2
category = 'Get values from metadata'
__doc__ = doc = _('language_strings(lang_codes, localize) -- '
__doc__ = doc = ('language_strings(lang_codes, localize) -- '
'return the strings for the language codes passed in lang_codes. '
'If localize is zero, return the strings in English. If '
'localize is not zero, return the strings in the language of '
@@ -1435,7 +1438,7 @@ class BuiltinLanguageCodes(BuiltinFormatterFunction):
name = 'language_codes'
arg_count = 1
category = 'Get values from metadata'
__doc__ = doc = _('language_codes(lang_strings) -- '
__doc__ = doc = ('language_codes(lang_strings) -- '
'return the language codes for the strings passed in lang_strings. '
'The strings must be in the language of the current locale. '
'Lang_strings is a comma-separated list.')
@@ -1456,7 +1459,7 @@ class BuiltinCurrentLibraryName(BuiltinFormatterFunction):
name = 'current_library_name'
arg_count = 0
category = 'Get values from metadata'
__doc__ = doc = _('current_library_name() -- '
__doc__ = doc = ('current_library_name() -- '
'return the last name on the path to the current calibre library. '
'This function can be called in template program mode using the '
'template "{:\'current_library_name()\'}".')
@@ -1470,7 +1473,7 @@ class BuiltinCurrentLibraryPath(BuiltinFormatterFunction):
name = 'current_library_path'
arg_count = 0
category = 'Get values from metadata'
__doc__ = doc = _('current_library_path() -- '
__doc__ = doc = ('current_library_path() -- '
'return the path to the current calibre library. This function can '
'be called in template program mode using the template '
'"{:\'current_library_path()\'}".')
@@ -1484,7 +1487,7 @@ class BuiltinFinishFormatting(BuiltinFormatterFunction):
name = 'finish_formatting'
arg_count = 4
category = 'Formatting values'
__doc__ = doc = _('finish_formatting(val, fmt, prefix, suffix) -- apply the '
__doc__ = doc = ('finish_formatting(val, fmt, prefix, suffix) -- apply the '
'format, prefix, and suffix to a value in the same way as '
'done in a template like `{series_index:05.2f| - |- }`. For '
'example, the following program produces the same output '
@@ -1501,7 +1504,7 @@ class BuiltinVirtualLibraries(BuiltinFormatterFunction):
name = 'virtual_libraries'
arg_count = 0
category = 'Get values from metadata'
__doc__ = doc = _('virtual_libraries() -- return a comma-separated list of '
__doc__ = doc = ('virtual_libraries() -- return a comma-separated list of '
'virtual libraries that contain this book. This function '
'works only in the GUI. If you want to use these values '
'in save-to-disk or send-to-device templates then you '
@@ -1512,14 +1515,14 @@ class BuiltinVirtualLibraries(BuiltinFormatterFunction):
def evaluate(self, formatter, kwargs, mi, locals_):
if hasattr(mi, '_proxy_metadata'):
return mi._proxy_metadata.virtual_libraries
return _('This function can be used only in the GUI')
return 'This function can be used only in the GUI'
class BuiltinUserCategories(BuiltinFormatterFunction):
name = 'user_categories'
arg_count = 0
category = 'Get values from metadata'
__doc__ = doc = _('user_categories() -- return a comma-separated list of '
__doc__ = doc = ('user_categories() -- return a comma-separated list of '
'the user categories that contain this book. This function '
'works only in the GUI. If you want to use these values '
'in save-to-disk or send-to-device templates then you '
@@ -1532,14 +1535,14 @@ class BuiltinUserCategories(BuiltinFormatterFunction):
cats = set(k for k, v in mi._proxy_metadata.user_categories.items() if v)
cats = sorted(cats, key=sort_key)
return ', '.join(cats)
return _('This function can be used only in the GUI')
return 'This function can be used only in the GUI'
class BuiltinTransliterate(BuiltinFormatterFunction):
name = 'transliterate'
arg_count = 1
category = 'String manipulation'
__doc__ = doc = _('transliterate(a) -- Returns a string in a latin alphabet '
__doc__ = doc = ('transliterate(a) -- Returns a string in a latin alphabet '
'formed by approximating the sound of the words in the '
'source string. For example, if the source is "{0}"'
' the function returns "{1}".').format(
@@ -1554,7 +1557,7 @@ class BuiltinAuthorLinks(BuiltinFormatterFunction):
name = 'author_links'
arg_count = 2
category = 'Get values from metadata'
__doc__ = doc = _('author_links(val_separator, pair_separator) -- returns '
__doc__ = doc = ('author_links(val_separator, pair_separator) -- returns '
'a string containing a list of authors and that author\'s '
'link values in the '
'form author1 val_separator author1link pair_separator '
@@ -1573,14 +1576,14 @@ class BuiltinAuthorLinks(BuiltinFormatterFunction):
return ''
names = sorted(link_data.keys(), key=sort_key)
return pair_sep.join(n + val_sep + link_data[n] for n in names)
return _('This function can be used only in the GUI')
return 'This function can be used only in the GUI'
class BuiltinAuthorSorts(BuiltinFormatterFunction):
name = 'author_sorts'
arg_count = 1
category = 'Get values from metadata'
__doc__ = doc = _('author_sorts(val_separator) -- returns a string '
__doc__ = doc = ('author_sorts(val_separator) -- returns a string '
'containing a list of author\'s sort values for the '
'authors of the book. The sort is the one in the author '
'metadata (different from the author_sort in books). The '
+1 -1
View File
@@ -9,7 +9,7 @@ def html2text(html):
r'<\s*(?P<solidus>/?)\s*[uU]\b(?P<rest>[^>]*)>',
r'<\g<solidus>span\g<rest>>', html)
h2t = HTML2Text()
h2t.default_image_alt = _('Unnamed image')
h2t.default_image_alt = 'Unnamed image'
h2t.body_width = 0
h2t.single_line_break = True
h2t.emphasis_mark = '*'
+78 -95
View File
@@ -2,9 +2,10 @@ __license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import re, io, sys
import re
import io
import sys
import json
from gettext import GNUTranslations, NullTranslations
import pkg_resources
_available_translations = None
@@ -62,101 +63,92 @@ def load_po(path):
return buf
def set_translators():
t = NullTranslations()
set_translators.lang = t.info().get('language')
t.install(names=('ngettext',))
set_translators.lang = None
_iso639 = None
_extra_lang_codes = {
'pt_BR' : _('Brazilian Portuguese'),
'en_GB' : _('English (UK)'),
'zh_CN' : _('Simplified Chinese'),
'zh_TW' : _('Traditional Chinese'),
'en' : _('English'),
'en_US' : _('English (United States)'),
'en_AR' : _('English (Argentina)'),
'en_AU' : _('English (Australia)'),
'en_JP' : _('English (Japan)'),
'en_DE' : _('English (Germany)'),
'en_BG' : _('English (Bulgaria)'),
'en_EG' : _('English (Egypt)'),
'en_NZ' : _('English (New Zealand)'),
'en_CA' : _('English (Canada)'),
'en_GR' : _('English (Greece)'),
'en_IN' : _('English (India)'),
'en_NP' : _('English (Nepal)'),
'en_TH' : _('English (Thailand)'),
'en_TR' : _('English (Turkey)'),
'en_CY' : _('English (Cyprus)'),
'en_CZ' : _('English (Czech Republic)'),
'en_PH' : _('English (Philippines)'),
'en_PK' : _('English (Pakistan)'),
'en_PL' : _('English (Poland)'),
'en_HR' : _('English (Croatia)'),
'en_HU' : _('English (Hungary)'),
'en_ID' : _('English (Indonesia)'),
'en_IL' : _('English (Israel)'),
'en_RU' : _('English (Russia)'),
'en_SG' : _('English (Singapore)'),
'en_YE' : _('English (Yemen)'),
'en_IE' : _('English (Ireland)'),
'en_CN' : _('English (China)'),
'en_TW' : _('English (Taiwan)'),
'en_ZA' : _('English (South Africa)'),
'es_PY' : _('Spanish (Paraguay)'),
'es_UY' : _('Spanish (Uruguay)'),
'es_AR' : _('Spanish (Argentina)'),
'es_CR' : _('Spanish (Costa Rica)'),
'es_MX' : _('Spanish (Mexico)'),
'es_CU' : _('Spanish (Cuba)'),
'es_CL' : _('Spanish (Chile)'),
'es_EC' : _('Spanish (Ecuador)'),
'es_HN' : _('Spanish (Honduras)'),
'es_VE' : _('Spanish (Venezuela)'),
'es_BO' : _('Spanish (Bolivia)'),
'es_NI' : _('Spanish (Nicaragua)'),
'es_CO' : _('Spanish (Colombia)'),
'de_AT' : _('German (AT)'),
'fr_BE' : _('French (BE)'),
'nl' : _('Dutch (NL)'),
'nl_BE' : _('Dutch (BE)'),
'und' : _('Unknown')
'pt_BR' : 'Brazilian Portuguese',
'en_GB' : 'English (UK)',
'zh_CN' : 'Simplified Chinese',
'zh_TW' : 'Traditional Chinese',
'en' : 'English',
'en_US' : 'English (United States)',
'en_AR' : 'English (Argentina)',
'en_AU' : 'English (Australia)',
'en_JP' : 'English (Japan)',
'en_DE' : 'English (Germany)',
'en_BG' : 'English (Bulgaria)',
'en_EG' : 'English (Egypt)',
'en_NZ' : 'English (New Zealand)',
'en_CA' : 'English (Canada)',
'en_GR' : 'English (Greece)',
'en_IN' : 'English (India)',
'en_NP' : 'English (Nepal)',
'en_TH' : 'English (Thailand)',
'en_TR' : 'English (Turkey)',
'en_CY' : 'English (Cyprus)',
'en_CZ' : 'English (Czech Republic)',
'en_PH' : 'English (Philippines)',
'en_PK' : 'English (Pakistan)',
'en_PL' : 'English (Poland)',
'en_HR' : 'English (Croatia)',
'en_HU' : 'English (Hungary)',
'en_ID' : 'English (Indonesia)',
'en_IL' : 'English (Israel)',
'en_RU' : 'English (Russia)',
'en_SG' : 'English (Singapore)',
'en_YE' : 'English (Yemen)',
'en_IE' : 'English (Ireland)',
'en_CN' : 'English (China)',
'en_TW' : 'English (Taiwan)',
'en_ZA' : 'English (South Africa)',
'es_PY' : 'Spanish (Paraguay)',
'es_UY' : 'Spanish (Uruguay)',
'es_AR' : 'Spanish (Argentina)',
'es_CR' : 'Spanish (Costa Rica)',
'es_MX' : 'Spanish (Mexico)',
'es_CU' : 'Spanish (Cuba)',
'es_CL' : 'Spanish (Chile)',
'es_EC' : 'Spanish (Ecuador)',
'es_HN' : 'Spanish (Honduras)',
'es_VE' : 'Spanish (Venezuela)',
'es_BO' : 'Spanish (Bolivia)',
'es_NI' : 'Spanish (Nicaragua)',
'es_CO' : 'Spanish (Colombia)',
'de_AT' : 'German (AT)',
'fr_BE' : 'French (BE)',
'nl' : 'Dutch (NL)',
'nl_BE' : 'Dutch (BE)',
'und' : 'Unknown'
}
if False:
# Extra strings needed for Qt
# NOTE: Ante Meridian (i.e. like 10:00 AM)
_('AM')
'AM'
# NOTE: Post Meridian (i.e. like 10:00 PM)
_('PM')
'PM'
# NOTE: Ante Meridian (i.e. like 10:00 am)
_('am')
'am'
# NOTE: Post Meridian (i.e. like 10:00 pm)
_('pm')
_('&Copy')
_('Select All')
_('Copy Link')
_('&Select All')
_('Copy &Link Location')
_('&Undo')
_('&Redo')
_('Cu&t')
_('&Paste')
_('Paste and Match Style')
_('Directions')
_('Left to Right')
_('Right to Left')
_('Fonts')
_('&Step up')
_('Step &down')
_('Close without Saving')
_('Close Tab')
'pm'
'&Copy'
'Select All'
'Copy Link'
'&Select All'
'Copy &Link Location'
'&Undo'
'&Redo'
'Cu&t'
'&Paste'
'Paste and Match Style'
'Directions'
'Left to Right'
'Right to Left'
'Fonts'
'&Step up'
'Step &down'
'Close without Saving'
'Close Tab'
_lcase_map = {}
for k in _extra_lang_codes:
@@ -227,15 +219,6 @@ def get_iso_language(lang_trans, lang):
return lang_trans(ans)
def get_language(lang):
translate = _
lang = _lcase_map.get(lang, lang)
if lang in _extra_lang_codes:
# The translator was not active when _extra_lang_codes was defined, so
# re-translate
return translate(_extra_lang_codes[lang])
attr = 'gettext' if sys.version_info.major > 2 else 'ugettext'
return get_iso_language(getattr(_lang_trans, attr, translate), lang)
def calibre_langcode_to_name(lc, localize=True):