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