Sync to trunk.

This commit is contained in:
John Schember 2011-07-11 16:29:12 -04:00
commit 381fa159f4
152 changed files with 40049 additions and 35039 deletions

View File

@ -0,0 +1,39 @@
import re
from calibre.web.feeds.news import BasicNewsRecipe
class autogids(BasicNewsRecipe):
title = u'Automatiseringgids IT'
oldest_article = 7
__author__ = 'DrMerry'
description = 'IT-nieuws van Automatiseringgids'
language = 'nl'
publisher = 'AutomatiseringGids'
category = 'Nieuws, IT, Nederlandstalig'
simultaneous_downloads = 5
#delay = 1
timefmt = ' [%A, %d %B, %Y]'
#timefmt = ''
no_stylesheets = True
remove_javascript = True
remove_empty_feeds = True
publication_type = 'newspaper'
encoding = 'utf-8'
cover_url = 'http://www.automatiseringgids.nl/siteimg/header_logo.gif'
keep_only_tags = [dict(id=['content'])]
extra_css = '.artikelheader {font-size:0.8em; color: #666;} .artikelintro {font-weight:bold} div.imgArticle {float: right; margin: 0 0em 1em 1em; display: block; position: relative; } \
h2 { margin: 0 0 0.5em; min-height: 30px; font-size: 1.5em; letter-spacing: -0.2px; margin: 0 0 0.5em; color: black; font-weight: bold; line-height: 1.2em; padding: 4px 3px 0; }'
remove_tags = [dict(name='div', attrs={'id':['loginbox','reactiecollapsible','reactiebox']}),
dict(name='div', attrs={'class':['column_a','column_c','bannerfullsize','reactieheader','reactiecollapsible','formulier','artikel_headeroptions']}),
dict(name='ul', attrs={'class':['highlightlist']}),
dict(name='input', attrs={'type':['button']}),
dict(name='div', attrs={'style':['display:block; width:428px; height:30px; float:left;']}),
]
preprocess_regexps = [
(re.compile(r'(<h3>Reacties</h3>|<h2>Zie ook:</h2>|<div style=".*</div>|<a[^>]*>|</a>)', re.DOTALL|re.IGNORECASE),
lambda match: ''),
]
feeds = [(u'Actueel', u'http://www.automatiseringgids.nl/rss.aspx')]

View File

@ -1,9 +1,9 @@
#!/usr/bin/env python #!/usr/bin/env python
__license__ = 'GPL v3' __license__ = 'GPL v3'
__author__ = 'DrMerry Based on v1.01 by Lorenzo Vigentini' __author__ = 'DrMerry Based on v1.01 by Lorenzo Vigentini'
__copyright__ = 'For version 1.02: DrMerry' __copyright__ = 'For version 1.02, 1.03: DrMerry'
__version__ = 'v1.02' __version__ = 'v1.03'
__date__ = '08, July 2011' __date__ = '11, July 2011'
__description__ = 'Computeractive publishes new downloads, reviews, news stories, step-by-step guides and answers to PC problems every day. Original version (c): 2009, Lorenzo Vigentini <l.vigentini at gmail.com>' __description__ = 'Computeractive publishes new downloads, reviews, news stories, step-by-step guides and answers to PC problems every day. Original version (c): 2009, Lorenzo Vigentini <l.vigentini at gmail.com>'
''' '''
@ -11,9 +11,10 @@ http://www.computeractive.co.uk/
''' '''
from calibre.web.feeds.news import BasicNewsRecipe from calibre.web.feeds.news import BasicNewsRecipe
import re
class computeractive(BasicNewsRecipe): class computeractive(BasicNewsRecipe):
__author__ = 'Lorenzo Vigentini' __author__ = 'DrMerry'
description = 'Computeractive publishes new downloads, reviews, news stories, step-by-step guides and answers to PC problems every day.' description = 'Computeractive publishes new downloads, reviews, news stories, step-by-step guides and answers to PC problems every day.'
cover_url = 'http://images.pcworld.com/images/common/header/header-logo.gif' cover_url = 'http://images.pcworld.com/images/common/header/header-logo.gif'
@ -31,6 +32,8 @@ class computeractive(BasicNewsRecipe):
remove_javascript = True remove_javascript = True
no_stylesheets = True no_stylesheets = True
remove_empty_feeds = True
remove_tags_after = dict(name='div', attrs={'class':'article_tags_block'})
keep_only_tags = [ keep_only_tags = [
dict(name='div', attrs={'id':'container_left'}) dict(name='div', attrs={'id':'container_left'})
@ -42,13 +45,14 @@ class computeractive(BasicNewsRecipe):
dict(name='a', attrs={'class':'largerImage'}) dict(name='a', attrs={'class':'largerImage'})
] ]
preprocess_regexps = [
(re.compile(r'(<a [^>]*>|</a>)', re.DOTALL|re.IGNORECASE),
lambda match: ''),
]
feeds = [ feeds = [
(u'General content', u'http://feeds.computeractive.co.uk/rss/latest/computeractive/all'), (u'General content', u'http://feeds.computeractive.co.uk/rss/latest/computeractive/all'),
(u'News', u'http://feeds.computeractive.co.uk/rss/latest/computeractive/news'), (u'News', u'http://feeds.computeractive.co.uk/rss/latest/computeractive/news'),
(u'Downloads', u'http://feeds.computeractive.co.uk/rss/latest/computeractive/downloads'),
(u'Hardware', u'http://feeds.computeractive.co.uk/rss/latest/computeractive/hardware'),
(u'Software', u'http://feeds.computeractive.co.uk/rss/latest/computeractive/software'),
(u'Competitions', u'http://www.v3.co.uk/feeds/rss20/personal-technology/competitions')
] ]

View File

@ -9,7 +9,7 @@ engadget.com
from calibre.web.feeds.news import BasicNewsRecipe from calibre.web.feeds.news import BasicNewsRecipe
class Engadget(BasicNewsRecipe): class Engadget(BasicNewsRecipe):
title = u'Engadget_Full' title = u'Engadget'
__author__ = 'Starson17' __author__ = 'Starson17'
__version__ = 'v1.00' __version__ = 'v1.00'
__date__ = '02, July 2011' __date__ = '02, July 2011'

35
recipes/geek_poke.recipe Normal file
View File

@ -0,0 +1,35 @@
#!/usr/bin/python
from calibre.web.feeds.news import BasicNewsRecipe
import re
class AdvancedUserRecipe1307556816(BasicNewsRecipe):
title = u'Geek and Poke'
__author__ = u'DrMerry'
description = u'Geek and Poke Cartoons'
oldest_article = 31
max_articles_per_feed = 100
language = u'en'
simultaneous_downloads = 5
#delay = 1
timefmt = ' [%A, %d %B, %Y]'
summary_length = -1
no_stylesheets = True
cover_url = 'http://geekandpoke.typepad.com/aboutcoders.jpeg'
remove_javascript = True
remove_empty_feeds = True
publication_type = 'blog'
preprocess_regexps = [ (re.compile(r'(<p>&nbsp;</p>|<iframe.*</iframe>|<a[^>]*>Tweet</a>|<a[^>]*>|</a>)', re.DOTALL|re.IGNORECASE),lambda match: ''),
(re.compile(r'(&nbsp;| )', re.DOTALL|re.IGNORECASE),lambda match: ' '),
(re.compile(r'<br( /)?>(<br( /)?>)+', re.DOTALL|re.IGNORECASE),lambda match: '<br>')
]
extra_css = 'body, h3, p, h2, h1, div, span{margin:0px} h2.date-header {font-size: 0.7em; color:#eee;} h3.entry-header{font-size: 1.0em} div.entry-body{font-size: 0.9em}'
remove_tags_before = dict(name='h2', attrs={'class':'date-header'})
remove_tags_after = dict(name='div', attrs={'class':'entry-body'})
feeds = [(u'Geek and Poke', u'http://feeds.feedburner.com/GeekAndPoke?format=xml')]

BIN
recipes/icons/pecat.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 B

View File

@ -16,16 +16,14 @@ class i09(BasicNewsRecipe):
max_articles_per_feed = 100 max_articles_per_feed = 100
no_stylesheets = True no_stylesheets = True
encoding = 'utf-8' encoding = 'utf-8'
use_embedded_content = False use_embedded_content = True
language = 'en' language = 'en'
masthead_url = 'http://cache.gawkerassets.com/assets/io9.com/img/logo.png' masthead_url = 'http://cache.gawkerassets.com/assets/io9.com/img/logo.png'
extra_css = ''' extra_css = '''
body{font-family: "Lucida Grande",Helvetica,Arial,sans-serif} body{font-family: "Lucida Grande",Helvetica,Arial,sans-serif}
img{margin-bottom: 1em} img{margin-bottom: 1em}
h1{font-family :Arial,Helvetica,sans-serif; font-size:large} h1{font-family :Arial,Helvetica,sans-serif; font-size:large}
h2{font-family :Arial,Helvetica,sans-serif; font-size:x-small}
''' '''
conversion_options = { conversion_options = {
'comment' : description 'comment' : description
, 'tags' : category , 'tags' : category
@ -33,13 +31,11 @@ class i09(BasicNewsRecipe):
, 'language' : language , 'language' : language
} }
remove_attributes = ['width','height'] feeds = [(u'Articles', u'http://feeds.gawker.com/io9/vip?format=xml')]
keep_only_tags = [dict(attrs={'class':'content permalink'})]
remove_tags_before = dict(name='h1')
remove_tags = [dict(attrs={'class':'contactinfo'})]
remove_tags_after = dict(attrs={'class':'contactinfo'})
feeds = [(u'Articles', u'http://feeds.gawker.com/io9/full')] remove_tags = [
{'class': 'feedflare'},
]
def preprocess_html(self, soup): def preprocess_html(self, soup):
return self.adeify_images(soup) return self.adeify_images(soup)

72
recipes/pecat.recipe Normal file
View File

@ -0,0 +1,72 @@
__license__ = 'GPL v3'
__copyright__ = '2011, Darko Miletic <darko.miletic at gmail.com>'
'''
www.pecat.co.rs
'''
import re
from calibre.web.feeds.news import BasicNewsRecipe
class Pecat_rs(BasicNewsRecipe):
title = 'Pecat'
__author__ = 'Darko Miletic'
description = 'Internet portal slobodne Srbije'
oldest_article = 15
max_articles_per_feed = 100
language = 'sr'
encoding = 'utf-8'
no_stylesheets = True
use_embedded_content = True
masthead_url = 'http://www.pecat.co.rs/wp-content/themes/zenko-v1/images/logo.jpg'
publication_type = 'magazine'
extra_css = """
@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)}
@font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)}
body{font-family: Arial,Helvetica,sans1,sans-serif}
img{display: block; margin-bottom: 1em; margin-top: 1em}
p{display: block; margin-bottom: 1em; margin-top: 1em}
"""
conversion_options = {
'comment' : description
, 'tags' : 'politika, Srbija'
, 'publisher': 'Pecat'
, 'language' : language
}
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
feeds = [(u'Clanci', u'http://www.pecat.co.rs/feed/')]
def preprocess_html(self, soup):
for item in soup.findAll(style=True):
del item['style']
for item in soup.findAll('a'):
limg = item.find('img')
if item.string is not None:
str = item.string
item.replaceWith(str)
else:
if limg:
limg.extract()
item.replaceWith(limg)
else:
str = self.tag_to_string(item)
item.replaceWith(str)
for item in soup.findAll('img'):
dad = item.findParent('p')
if dad:
mydad = dad.parent
myIndex = mydad.contents.index(dad)
item.extract()
mydad.insert(myIndex,item)
for item in soup.findAll('strong'):
dad = item.findParent('p')
if dad:
mydad = dad.parent
myIndex = mydad.contents.index(dad)
item.extract()
item.name='h4'
mydad.insert(myIndex,item)
return soup

View File

@ -1,5 +1,5 @@
" Project wide builtins " Project wide builtins
let g:pyflakes_builtins += ["dynamic_property", "__", "P", "I", "lopen", "icu_lower", "icu_upper", "icu_title"] let g:pyflakes_builtins += ["dynamic_property", "__", "P", "I", "lopen", "icu_lower", "icu_upper", "icu_title", "ngettext"]
python << EOFPY python << EOFPY
import os import os

View File

@ -64,7 +64,7 @@ class Check(Command):
description = 'Check for errors in the calibre source code' description = 'Check for errors in the calibre source code'
BUILTINS = ['_', '__', 'dynamic_property', 'I', 'P', 'lopen', 'icu_lower', BUILTINS = ['_', '__', 'dynamic_property', 'I', 'P', 'lopen', 'icu_lower',
'icu_upper', 'icu_title'] 'icu_upper', 'icu_title', 'ngettext']
CACHE = '.check-cache.pickle' CACHE = '.check-cache.pickle'
def get_files(self, cache): def get_files(self, cache):

View File

@ -1,646 +0,0 @@
#! /usr/bin/env python
# Originally written by Barry Warsaw <barry@zope.com>
#
# Minimally patched to make it even more xgettext compatible
# by Peter Funk <pf@artcom-gmbh.de>
#
# 2002-11-22 Jrgen Hermann <jh@web.de>
# Added checks that _() only contains string literals, and
# command line args are resolved to module lists, i.e. you
# can now pass a filename, a module or package name, or a
# directory (including globbing chars, important for Win32).
# Made docstring fit in 80 chars wide displays using pydoc.
#
__doc__ = """pygettext -- Python equivalent of xgettext(1)
Many systems (Solaris, Linux, Gnu) provide extensive tools that ease the
internationalization of C programs. Most of these tools are independent of
the programming language and can be used from within Python programs.
Martin von Loewis' work[1] helps considerably in this regard.
There's one problem though; xgettext is the program that scans source code
looking for message strings, but it groks only C (or C++). Python
introduces a few wrinkles, such as dual quoting characters, triple quoted
strings, and raw strings. xgettext understands none of this.
Enter pygettext, which uses Python's standard tokenize module to scan
Python source code, generating .pot files identical to what GNU xgettext[2]
generates for C and C++ code. From there, the standard GNU tools can be
used.
A word about marking Python strings as candidates for translation. GNU
xgettext recognizes the following keywords: gettext, dgettext, dcgettext,
and gettext_noop. But those can be a lot of text to include all over your
code. C and C++ have a trick: they use the C preprocessor. Most
internationalized C source includes a #define for gettext() to _() so that
what has to be written in the source is much less. Thus these are both
translatable strings:
gettext("Translatable String")
_("Translatable String")
Python of course has no preprocessor so this doesn't work so well. Thus,
pygettext searches only for _() by default, but see the -k/--keyword flag
below for how to augment this.
[1] http://www.python.org/workshops/1997-10/proceedings/loewis.html
[2] http://www.gnu.org/software/gettext/gettext.html
NOTE: pygettext attempts to be option and feature compatible with GNU
xgettext where ever possible. However some options are still missing or are
not fully implemented. Also, xgettext's use of command line switches with
option arguments is broken, and in these cases, pygettext just defines
additional switches.
Usage: pygettext [options] inputfile ...
Options:
-a
--extract-all
Extract all strings.
-d name
--default-domain=name
Rename the default output file from messages.pot to name.pot.
-E
--escape
Replace non-ASCII characters with octal escape sequences.
-D
--docstrings
Extract module, class, method, and function docstrings. These do
not need to be wrapped in _() markers, and in fact cannot be for
Python to consider them docstrings. (See also the -X option).
-h
--help
Print this help message and exit.
-k word
--keyword=word
Keywords to look for in addition to the default set, which are:
%(DEFAULTKEYWORDS)s
You can have multiple -k flags on the command line.
-K
--no-default-keywords
Disable the default set of keywords (see above). Any keywords
explicitly added with the -k/--keyword option are still recognized.
--no-location
Do not write filename/lineno location comments.
-n
--add-location
Write filename/lineno location comments indicating where each
extracted string is found in the source. These lines appear before
each msgid. The style of comments is controlled by the -S/--style
option. This is the default.
-o filename
--output=filename
Rename the default output file from messages.pot to filename. If
filename is `-' then the output is sent to standard out.
-p dir
--output-dir=dir
Output files will be placed in directory dir.
-S stylename
--style stylename
Specify which style to use for location comments. Two styles are
supported:
Solaris # File: filename, line: line-number
GNU #: filename:line
The style name is case insensitive. GNU style is the default.
-v
--verbose
Print the names of the files being processed.
-V
--version
Print the version of pygettext and exit.
-w columns
--width=columns
Set width of output to columns.
-x filename
--exclude-file=filename
Specify a file that contains a list of strings that are not be
extracted from the input files. Each string to be excluded must
appear on a line by itself in the file.
-X filename
--no-docstrings=filename
Specify a file that contains a list of files (one per line) that
should not have their docstrings extracted. This is only useful in
conjunction with the -D option above.
If `inputfile' is -, standard input is read.
"""
import os
import imp
import sys
import glob
import time
import getopt
import token
import tokenize
import operator
__version__ = '1.5'
default_keywords = ['_']
DEFAULTKEYWORDS = ', '.join(default_keywords)
EMPTYSTRING = ''
from setup import __appname__, __version__ as version
# The normal pot-file header. msgmerge and Emacs's po-mode work better if it's
# there.
pot_header = '''\
# Translation template file..
# Copyright (C) %(year)s Kovid Goyal
# Kovid Goyal <kovid@kovidgoyal.net>, %(year)s.
#
msgid ""
msgstr ""
"Project-Id-Version: %(appname)s %(version)s\\n"
"POT-Creation-Date: %%(time)s\\n"
"PO-Revision-Date: %%(time)s\\n"
"Last-Translator: Automatically generated\\n"
"Language-Team: LANGUAGE\\n"
"MIME-Version: 1.0\\n"
"Content-Type: text/plain; charset=UTF-8\\n"
"Content-Transfer-Encoding: 8bit\\n"
"Generated-By: pygettext.py %%(version)s\\n"
'''%dict(appname=__appname__, version=version, year=time.strftime('%Y'))
def usage(code, msg=''):
print >> sys.stderr, __doc__ % globals()
if msg:
print >> sys.stderr, msg
sys.exit(code)
escapes = []
def make_escapes(pass_iso8859):
global escapes
if pass_iso8859:
# Allow iso-8859 characters to pass through so that e.g. 'msgid
# would result not result in 'msgid "H\366he"'. Otherwise we
# escape any character outside the 32..126 range.
mod = 128
else:
mod = 256
for i in range(256):
if 32 <= (i % mod) <= 126:
escapes.append(chr(i))
else:
escapes.append("\\%03o" % i)
escapes[ord('\\')] = '\\\\'
escapes[ord('\t')] = '\\t'
escapes[ord('\r')] = '\\r'
escapes[ord('\n')] = '\\n'
escapes[ord('\"')] = '\\"'
def escape(s):
global escapes
s = list(s)
for i in range(len(s)):
s[i] = escapes[ord(s[i])]
return EMPTYSTRING.join(s)
def safe_eval(s):
# unwrap quotes, safely
return eval(s, {'__builtins__':{}}, {})
def normalize(s):
# This converts the various Python string types into a format that is
# appropriate for .po files, namely much closer to C style.
lines = s.split('\n')
if len(lines) == 1:
s = '"' + escape(s) + '"'
else:
if not lines[-1]:
del lines[-1]
lines[-1] = lines[-1] + '\n'
for i in range(len(lines)):
lines[i] = escape(lines[i])
lineterm = '\\n"\n"'
s = '""\n"' + lineterm.join(lines) + '"'
return s
def containsAny(str, set):
"""Check whether 'str' contains ANY of the chars in 'set'"""
return 1 in [c in str for c in set]
def _visit_pyfiles(list, dirname, names):
"""Helper for getFilesForName()."""
# get extension for python source files
if not globals().has_key('_py_ext'):
global _py_ext
_py_ext = [triple[0] for triple in imp.get_suffixes()
if triple[2] == imp.PY_SOURCE][0]
# don't recurse into CVS directories
if 'CVS' in names:
names.remove('CVS')
# add all *.py files to list
list.extend(
[os.path.join(dirname, file) for file in names
if os.path.splitext(file)[1] == _py_ext]
)
def _get_modpkg_path(dotted_name, pathlist=None):
"""Get the filesystem path for a module or a package.
Return the file system path to a file for a module, and to a directory for
a package. Return None if the name is not found, or is a builtin or
extension module.
"""
# split off top-most name
parts = dotted_name.split('.', 1)
if len(parts) > 1:
# we have a dotted path, import top-level package
try:
file, pathname, description = imp.find_module(parts[0], pathlist)
if file: file.close()
except ImportError:
return None
# check if it's indeed a package
if description[2] == imp.PKG_DIRECTORY:
# recursively handle the remaining name parts
pathname = _get_modpkg_path(parts[1], [pathname])
else:
pathname = None
else:
# plain name
try:
file, pathname, description = imp.find_module(
dotted_name, pathlist)
if file:
file.close()
if description[2] not in [imp.PY_SOURCE, imp.PKG_DIRECTORY]:
pathname = None
except ImportError:
pathname = None
return pathname
def getFilesForName(name):
"""Get a list of module files for a filename, a module or package name,
or a directory.
"""
if not os.path.exists(name):
# check for glob chars
if containsAny(name, "*?[]"):
files = glob.glob(name)
list = []
for file in files:
list.extend(getFilesForName(file))
return list
# try to find module or package
name = _get_modpkg_path(name)
if not name:
return []
if os.path.isdir(name):
# find all python files in directory
list = []
os.path.walk(name, _visit_pyfiles, list)
return list
elif os.path.exists(name):
# a single file
return [name]
return []
class TokenEater:
def __init__(self, options):
self.__options = options
self.__messages = {}
self.__state = self.__waiting
self.__data = []
self.__lineno = -1
self.__freshmodule = 1
self.__curfile = None
def __call__(self, ttype, tstring, stup, etup, line):
# dispatch
## import token
## print >> sys.stderr, 'ttype:', token.tok_name[ttype], \
## 'tstring:', tstring
self.__state(ttype, tstring, stup[0])
def __waiting(self, ttype, tstring, lineno):
opts = self.__options
# Do docstring extractions, if enabled
if opts.docstrings and not opts.nodocstrings.get(self.__curfile):
# module docstring?
if self.__freshmodule:
if ttype == tokenize.STRING:
self.__addentry(safe_eval(tstring), lineno, isdocstring=1)
self.__freshmodule = 0
elif ttype not in (tokenize.COMMENT, tokenize.NL):
self.__freshmodule = 0
return
# class docstring?
if ttype == tokenize.NAME and tstring in ('class', 'def'):
self.__state = self.__suiteseen
return
if ttype == tokenize.NAME and tstring in opts.keywords:
self.__state = self.__keywordseen
def __suiteseen(self, ttype, tstring, lineno):
# ignore anything until we see the colon
if ttype == tokenize.OP and tstring == ':':
self.__state = self.__suitedocstring
def __suitedocstring(self, ttype, tstring, lineno):
# ignore any intervening noise
if ttype == tokenize.STRING:
self.__addentry(safe_eval(tstring), lineno, isdocstring=1)
self.__state = self.__waiting
elif ttype not in (tokenize.NEWLINE, tokenize.INDENT,
tokenize.COMMENT):
# there was no class docstring
self.__state = self.__waiting
def __keywordseen(self, ttype, tstring, lineno):
if ttype == tokenize.OP and tstring == '(':
self.__data = []
self.__lineno = lineno
self.__state = self.__openseen
else:
self.__state = self.__waiting
def __openseen(self, ttype, tstring, lineno):
if ttype == tokenize.OP and tstring == ')':
# We've seen the last of the translatable strings. Record the
# line number of the first line of the strings and update the list
# of messages seen. Reset state for the next batch. If there
# were no strings inside _(), then just ignore this entry.
if self.__data:
self.__addentry(EMPTYSTRING.join(self.__data))
self.__state = self.__waiting
elif ttype == tokenize.STRING:
self.__data.append(safe_eval(tstring))
elif ttype not in [tokenize.COMMENT, token.INDENT, token.DEDENT,
token.NEWLINE, tokenize.NL]:
# warn if we see anything else than STRING or whitespace
print >> sys.stderr, \
'*** %(file)s:%(lineno)s: Seen unexpected token "%(token)s"'\
% {
'token': tstring,
'file': self.__curfile,
'lineno': self.__lineno
}
self.__state = self.__waiting
def __addentry(self, msg, lineno=None, isdocstring=0):
if lineno is None:
lineno = self.__lineno
if not msg in self.__options.toexclude:
entry = (self.__curfile, lineno)
self.__messages.setdefault(msg, {})[entry] = isdocstring
def set_filename(self, filename):
self.__curfile = filename
self.__freshmodule = 1
def write(self, fp):
options = self.__options
timestamp = time.strftime('%Y-%m-%d %H:%M+%Z')
# The time stamp in the header doesn't have the same format as that
# generated by xgettext...
print >> fp, pot_header % {'time': timestamp, 'version': __version__}
# Sort the entries. First sort each particular entry's keys, then
# sort all the entries by their first item.
reverse = {}
for k, v in self.__messages.items():
keys = v.keys()
keys.sort()
reverse.setdefault(tuple(keys), []).append((k, v))
rkeys = reverse.keys()
rkeys.sort()
for rkey in rkeys:
rentries = reverse[rkey]
rentries.sort()
for k, v in rentries:
isdocstring = 0
# If the entry was gleaned out of a docstring, then add a
# comment stating so. This is to aid translators who may wish
# to skip translating some unimportant docstrings.
if reduce(operator.__add__, v.values()):
isdocstring = 1
# k is the message string, v is a dictionary-set of (filename,
# lineno) tuples. We want to sort the entries in v first by
# file name and then by line number.
v = v.keys()
v.sort()
if not options.writelocations:
pass
# location comments are different b/w Solaris and GNU:
elif options.locationstyle == options.SOLARIS:
for filename, lineno in v:
d = {'filename': filename, 'lineno': lineno}
print >>fp, \
'# File: %(filename)s, line: %(lineno)d' % d
elif options.locationstyle == options.GNU:
# fit as many locations on one line, as long as the
# resulting line length doesn't exceeds 'options.width'
locline = '#:'
for filename, lineno in v:
d = {'filename': filename, 'lineno': lineno}
s = ' %(filename)s:%(lineno)d' % d
if len(locline) + len(s) <= options.width:
locline = locline + s
else:
print >> fp, locline
locline = "#:" + s
if len(locline) > 2:
print >> fp, locline
if isdocstring:
print >> fp, '#, docstring'
print >> fp, 'msgid', normalize(k)
print >> fp, 'msgstr ""\n'
def main(outfile, args=sys.argv[1:]):
global default_keywords
try:
opts, args = getopt.getopt(
args,
'ad:DEhk:Kno:p:S:Vvw:x:X:',
['extract-all', 'default-domain=', 'escape', 'help',
'keyword=', 'no-default-keywords',
'add-location', 'no-location', 'output=', 'output-dir=',
'style=', 'verbose', 'version', 'width=', 'exclude-file=',
'docstrings', 'no-docstrings',
])
except getopt.error, msg:
usage(1, msg)
# for holding option values
class Options:
# constants
GNU = 1
SOLARIS = 2
# defaults
extractall = 0 # FIXME: currently this option has no effect at all.
escape = 0
keywords = []
outpath = ''
outfile = 'messages.pot'
writelocations = 1
locationstyle = GNU
verbose = 0
width = 78
excludefilename = ''
docstrings = 0
nodocstrings = {}
options = Options()
locations = {'gnu' : options.GNU,
'solaris' : options.SOLARIS,
}
# parse options
for opt, arg in opts:
if opt in ('-h', '--help'):
usage(0)
elif opt in ('-a', '--extract-all'):
options.extractall = 1
elif opt in ('-d', '--default-domain'):
options.outfile = arg + '.pot'
elif opt in ('-E', '--escape'):
options.escape = 1
elif opt in ('-D', '--docstrings'):
options.docstrings = 1
elif opt in ('-k', '--keyword'):
options.keywords.append(arg)
elif opt in ('-K', '--no-default-keywords'):
default_keywords = []
elif opt in ('-n', '--add-location'):
options.writelocations = 1
elif opt in ('--no-location',):
options.writelocations = 0
elif opt in ('-S', '--style'):
options.locationstyle = locations.get(arg.lower())
if options.locationstyle is None:
usage(1, ('Invalid value for --style: %s') % arg)
elif opt in ('-o', '--output'):
options.outfile = arg
elif opt in ('-p', '--output-dir'):
options.outpath = arg
elif opt in ('-v', '--verbose'):
options.verbose = 1
elif opt in ('-V', '--version'):
print ('pygettext.py (xgettext for Python) %s') % __version__
sys.exit(0)
elif opt in ('-w', '--width'):
try:
options.width = int(arg)
except ValueError:
usage(1, ('--width argument must be an integer: %s') % arg)
elif opt in ('-x', '--exclude-file'):
options.excludefilename = arg
elif opt in ('-X', '--no-docstrings'):
fp = open(arg)
try:
while 1:
line = fp.readline()
if not line:
break
options.nodocstrings[line[:-1]] = 1
finally:
fp.close()
# calculate escapes
make_escapes(options.escape)
# calculate all keywords
options.keywords.extend(default_keywords)
# initialize list of strings to exclude
if options.excludefilename:
try:
fp = open(options.excludefilename)
options.toexclude = fp.readlines()
fp.close()
except IOError:
print >> sys.stderr, (
"Can't read --exclude-file: %s") % options.excludefilename
sys.exit(1)
else:
options.toexclude = []
# resolve args to module lists
expanded = []
for arg in args:
if arg == '-':
expanded.append(arg)
else:
expanded.extend(getFilesForName(arg))
args = expanded
# slurp through all the files
eater = TokenEater(options)
for filename in args:
if filename == '-':
if options.verbose:
print ('Reading standard input')
fp = sys.stdin
closep = 0
else:
if options.verbose:
print ('Working on %s') % filename
fp = open(filename)
closep = 1
try:
eater.set_filename(filename)
try:
tokenize.tokenize(fp.readline, eater)
except tokenize.TokenError, e:
print >> sys.stderr, '%s: %s, line %d, column %d' % (
e[0], filename, e[1][0], e[1][1])
except IndentationError, e:
print >> sys.stderr, '%s: %s, line %s, column %s' % (
e[0], filename, e.lineno, e[1][1])
finally:
if closep:
fp.close()
# write the output
eater.write(outfile)
if __name__ == '__main__':
main(sys.stdout)

View File

@ -6,11 +6,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 os, cStringIO, tempfile, shutil, atexit, subprocess, glob, re import os, tempfile, shutil, subprocess, glob, re, time, textwrap
from distutils import sysconfig from distutils import sysconfig
from setup import Command, __appname__ from setup import Command, __appname__, __version__
from setup.pygettext import main as pygettext
from setup.build_environment import pyqt from setup.build_environment import pyqt
class POT(Command): class POT(Command):
@ -60,13 +59,44 @@ class POT(Command):
def run(self, opts): def run(self, opts):
pot_header = textwrap.dedent('''\
# Translation template file..
# Copyright (C) %(year)s Kovid Goyal
# Kovid Goyal <kovid@kovidgoyal.net>, %(year)s.
#
msgid ""
msgstr ""
"Project-Id-Version: %(appname)s %(version)s\\n"
"POT-Creation-Date: %(time)s\\n"
"PO-Revision-Date: %(time)s\\n"
"Last-Translator: Automatically generated\\n"
"Language-Team: LANGUAGE\\n"
"MIME-Version: 1.0\\n"
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/calibre\\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\\n"
"Content-Type: text/plain; charset=UTF-8\\n"
"Content-Transfer-Encoding: 8bit\\n"
''')%dict(appname=__appname__, version=__version__,
year=time.strftime('%Y'),
time=time.strftime('%Y-%m-%d %H:%M+%Z'))
files = self.source_files() files = self.source_files()
buf = cStringIO.StringIO() with tempfile.NamedTemporaryFile() as fl:
fl.write('\n'.join(files))
fl.flush()
out = tempfile.NamedTemporaryFile(suffix='.pot', delete=False)
out.close()
self.info('Creating translations template...') self.info('Creating translations template...')
tempdir = tempfile.mkdtemp() subprocess.check_call(['xgettext', '-f', fl.name,
atexit.register(shutil.rmtree, tempdir) '--default-domain=calibre', '-o', out.name, '-L', 'Python',
pygettext(buf, ['-k', '__', '-p', tempdir]+files) '--from-code=UTF-8', '--sort-by-file', '--omit-header',
src = buf.getvalue() '--no-wrap', '-k__',
])
with open(out.name, 'rb') as f:
src = f.read()
os.remove(out.name)
src = pot_header + '\n' + src
src += '\n\n' + self.get_tweaks_docs() src += '\n\n' + self.get_tweaks_docs()
pot = os.path.join(self.PATH, __appname__+'.pot') pot = os.path.join(self.PATH, __appname__+'.pot')
with open(pot, 'wb') as f: with open(pot, 'wb') as f:

View File

@ -211,6 +211,28 @@ if __name__ == '__main__':
class TestSHLock(unittest.TestCase): class TestSHLock(unittest.TestCase):
"""Testcases for SHLock class.""" """Testcases for SHLock class."""
def test_multithread_deadlock(self):
lock = SHLock()
def two_shared():
r = RWLockWrapper(lock)
with r:
time.sleep(0.2)
with r:
pass
def one_exclusive():
time.sleep(0.1)
w = RWLockWrapper(lock, is_shared=False)
with w:
pass
threads = [Thread(target=two_shared), Thread(target=one_exclusive)]
for t in threads:
t.daemon = True
t.start()
for t in threads:
t.join(5)
live = [t for t in threads if t.is_alive()]
self.assertListEqual(live, [], 'ShLock hung')
def test_upgrade(self): def test_upgrade(self):
lock = SHLock() lock = SHLock()
lock.acquire(shared=True) lock.acquire(shared=True)

View File

@ -124,11 +124,11 @@ class ANDROID(USBMS):
'IDEOS_TABLET', 'MYTOUCH_4G', 'UMS_COMPOSITE', 'SCH-I800_CARD', 'IDEOS_TABLET', 'MYTOUCH_4G', 'UMS_COMPOSITE', 'SCH-I800_CARD',
'7', 'A956', 'A955', 'A43', 'ANDROID_PLATFORM', 'TEGRA_2', '7', 'A956', 'A955', 'A43', 'ANDROID_PLATFORM', 'TEGRA_2',
'MB860', 'MULTI-CARD', 'MID7015A', 'INCREDIBLE', 'A7EB', 'STREAK', 'MB860', 'MULTI-CARD', 'MID7015A', 'INCREDIBLE', 'A7EB', 'STREAK',
'MB525', 'ANDROID2.3'] 'MB525', 'ANDROID2.3', 'SGH-I997']
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897', WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897',
'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD', 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
'A70S', 'A101IT', '7', 'INCREDIBLE', 'A7EB', 'SGH-T849_CARD', 'A70S', 'A101IT', '7', 'INCREDIBLE', 'A7EB', 'SGH-T849_CARD',
'__UMS_COMPOSITE'] '__UMS_COMPOSITE', 'SGH-I997_CARD']
OSX_MAIN_MEM = 'Android Device Main Memory' OSX_MAIN_MEM = 'Android Device Main Memory'

View File

@ -446,7 +446,8 @@ class ITUNES(DriverBase):
} }
if self.report_progress is not None: if self.report_progress is not None:
self.report_progress((i+1)/book_count, _('%d of %d') % (i+1, book_count)) self.report_progress((i+1)/book_count,
_('%(num)d of %(tot)d') % dict(num=i+1, tot=book_count))
self._purge_orphans(library_books, cached_books) self._purge_orphans(library_books, cached_books)
elif iswindows: elif iswindows:
@ -485,7 +486,8 @@ class ITUNES(DriverBase):
if self.report_progress is not None: if self.report_progress is not None:
self.report_progress((i+1)/book_count, self.report_progress((i+1)/book_count,
_('%d of %d') % (i+1, book_count)) _('%(num)d of %(tot)d') % dict(num=i+1,
tot=book_count))
self._purge_orphans(library_books, cached_books) self._purge_orphans(library_books, cached_books)
finally: finally:
@ -1075,7 +1077,8 @@ class ITUNES(DriverBase):
# Report progress # Report progress
if self.report_progress is not None: if self.report_progress is not None:
self.report_progress((i+1)/file_count, _('%d of %d') % (i+1, file_count)) self.report_progress((i+1)/file_count,
_('%(num)d of %(tot)d') % dict(num=i+1, tot=file_count))
elif iswindows: elif iswindows:
try: try:
@ -1118,7 +1121,8 @@ class ITUNES(DriverBase):
# Report progress # Report progress
if self.report_progress is not None: if self.report_progress is not None:
self.report_progress((i+1)/file_count, _('%d of %d') % (i+1, file_count)) self.report_progress((i+1)/file_count,
_('%(num)d of %(tot)d') % dict(num=i+1, tot=file_count))
finally: finally:
pythoncom.CoUninitialize() pythoncom.CoUninitialize()
@ -3107,7 +3111,8 @@ class ITUNES_ASYNC(ITUNES):
} }
if self.report_progress is not None: if self.report_progress is not None:
self.report_progress((i+1)/book_count, _('%d of %d') % (i+1, book_count)) self.report_progress((i+1)/book_count,
_('%(num)d of %(tot)d') % dict(num=i+1, tot=book_count))
elif iswindows: elif iswindows:
try: try:
@ -3147,7 +3152,8 @@ class ITUNES_ASYNC(ITUNES):
if self.report_progress is not None: if self.report_progress is not None:
self.report_progress((i+1)/book_count, self.report_progress((i+1)/book_count,
_('%d of %d') % (i+1, book_count)) _('%(num)d of %(tot)d') % dict(num=i+1,
tot=book_count))
finally: finally:
pythoncom.CoUninitialize() pythoncom.CoUninitialize()

View File

@ -57,6 +57,7 @@ class KOBO(USBMS):
def initialize(self): def initialize(self):
USBMS.initialize(self) USBMS.initialize(self)
self.book_class = Book self.book_class = Book
self.dbversion = 7
def books(self, oncard=None, end_session=True): def books(self, oncard=None, end_session=True):
from calibre.ebooks.metadata.meta import path_to_ext from calibre.ebooks.metadata.meta import path_to_ext
@ -214,7 +215,7 @@ class KOBO(USBMS):
'BookID is Null and ( ___ExpirationStatus <> "3" or ___ExpirationStatus is Null)' 'BookID is Null and ( ___ExpirationStatus <> "3" or ___ExpirationStatus is Null)'
elif self.dbversion < 16 and self.dbversion >= 14: elif self.dbversion < 16 and self.dbversion >= 14:
query= 'select Title, Attribution, DateCreated, ContentID, MimeType, ContentType, ' \ query= 'select Title, Attribution, DateCreated, ContentID, MimeType, ContentType, ' \
'ImageID, ReadStatus, ___ExpirationStatus, "-1" as FavouritesIndex, "-1" as Accessibility from content where ' \ 'ImageID, ReadStatus, ___ExpirationStatus, FavouritesIndex, "-1" as Accessibility from content where ' \
'BookID is Null and ( ___ExpirationStatus <> "3" or ___ExpirationStatus is Null)' 'BookID is Null and ( ___ExpirationStatus <> "3" or ___ExpirationStatus is Null)'
elif self.dbversion < 14 and self.dbversion >= 8: elif self.dbversion < 14 and self.dbversion >= 8:
query= 'select Title, Attribution, DateCreated, ContentID, MimeType, ContentType, ' \ query= 'select Title, Attribution, DateCreated, ContentID, MimeType, ContentType, ' \
@ -319,8 +320,15 @@ class KOBO(USBMS):
# Kobo does not delete the Book row (ie the row where the BookID is Null) # Kobo does not delete the Book row (ie the row where the BookID is Null)
# The next server sync should remove the row # The next server sync should remove the row
cursor.execute('delete from content where BookID = ?', t) cursor.execute('delete from content where BookID = ?', t)
try:
cursor.execute('update content set ReadStatus=0, FirstTimeReading = \'true\', ___PercentRead=0, ___ExpirationStatus=3 ' \ cursor.execute('update content set ReadStatus=0, FirstTimeReading = \'true\', ___PercentRead=0, ___ExpirationStatus=3 ' \
'where BookID is Null and ContentID =?',t) 'where BookID is Null and ContentID =?',t)
except Exception as e:
if 'no such column' not in str(e):
raise
cursor.execute('update content set ReadStatus=0, FirstTimeReading = \'true\', ___PercentRead=0 ' \
'where BookID is Null and ContentID =?',t)
connection.commit() connection.commit()
@ -609,8 +617,9 @@ class KOBO(USBMS):
cursor = connection.cursor() cursor = connection.cursor()
try: try:
cursor.execute (query) cursor.execute (query)
except: except Exception as e:
debug_print(' Database Exception: Unable to reset Shortlist list') debug_print(' Database Exception: Unable to reset Shortlist list')
if 'no such column' not in str(e):
raise raise
else: else:
connection.commit() connection.commit()
@ -623,8 +632,9 @@ class KOBO(USBMS):
try: try:
cursor.execute('update content set FavouritesIndex=1 where BookID is Null and ContentID = ?', t) cursor.execute('update content set FavouritesIndex=1 where BookID is Null and ContentID = ?', t)
except: except Exception as e:
debug_print(' Database Exception: Unable set book as Shortlist') debug_print(' Database Exception: Unable set book as Shortlist')
if 'no such column' not in str(e):
raise raise
else: else:
connection.commit() connection.commit()
@ -664,6 +674,7 @@ class KOBO(USBMS):
# Need to reset the collections outside the particular loops # Need to reset the collections outside the particular loops
# otherwise the last item will not be removed # otherwise the last item will not be removed
self.reset_readstatus(connection, oncard) self.reset_readstatus(connection, oncard)
if self.dbversion >= 14:
self.reset_favouritesindex(connection, oncard) self.reset_favouritesindex(connection, oncard)
# Process any collections that exist # Process any collections that exist
@ -682,7 +693,7 @@ class KOBO(USBMS):
if category in readstatuslist.keys(): if category in readstatuslist.keys():
# Manage ReadStatus # Manage ReadStatus
self.set_readstatus(connection, ContentID, readstatuslist.get(category)) self.set_readstatus(connection, ContentID, readstatuslist.get(category))
if category == 'Shortlist': if category == 'Shortlist' and self.dbversion >= 14:
# Manage FavouritesIndex/Shortlist # Manage FavouritesIndex/Shortlist
self.set_favouritesindex(connection, ContentID) self.set_favouritesindex(connection, ContentID)
if category in accessibilitylist.keys(): if category in accessibilitylist.keys():
@ -692,6 +703,7 @@ class KOBO(USBMS):
# Since no collections exist the ReadStatus needs to be reset to 0 (Unread) # Since no collections exist the ReadStatus needs to be reset to 0 (Unread)
debug_print("No Collections - reseting ReadStatus") debug_print("No Collections - reseting ReadStatus")
self.reset_readstatus(connection, oncard) self.reset_readstatus(connection, oncard)
if self.dbversion >= 14:
debug_print("No Collections - reseting FavouritesIndex") debug_print("No Collections - reseting FavouritesIndex")
self.reset_favouritesindex(connection, oncard) self.reset_favouritesindex(connection, oncard)

View File

@ -67,10 +67,10 @@ class PRS505(USBMS):
_('Comma separated list of metadata fields ' _('Comma separated list of metadata fields '
'to turn into collections on the device. Possibilities include: ')+\ 'to turn into collections on the device. Possibilities include: ')+\
'series, tags, authors' +\ 'series, tags, authors' +\
_('. Two special collections are available: %s:%s and %s:%s. Add ' _('. Two special collections are available: %(abt)s:%(abtv)s and %(aba)s:%(abav)s. Add '
'these values to the list to enable them. The collections will be ' 'these values to the list to enable them. The collections will be '
'given the name provided after the ":" character.')%( 'given the name provided after the ":" character.')%dict(
'abt', ALL_BY_TITLE, 'aba', ALL_BY_AUTHOR), abt='abt', abtv=ALL_BY_TITLE, aba='aba', abav=ALL_BY_AUTHOR),
_('Upload separate cover thumbnails for books (newer readers)') + _('Upload separate cover thumbnails for books (newer readers)') +
':::'+_('Normally, the SONY readers get the cover image from the' ':::'+_('Normally, the SONY readers get the cover image from the'
' ebook file itself. With this option, calibre will send a ' ' ebook file itself. With this option, calibre will send a '

View File

@ -144,9 +144,9 @@ 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 %s to enable. ' ' patterns. Disabled by default. Use %(en)s to enable. '
' Individual actions can be disabled with the %s options.') ' Individual actions can be disabled with the %(dis)s options.')
% ('--enable-heuristics', '--disable-*'), % dict(en='--enable-heuristics', dis='--disable-*'),
['enable_heuristics'] + HEURISTIC_OPTIONS ['enable_heuristics'] + HEURISTIC_OPTIONS
), ),

View File

@ -17,7 +17,8 @@ class ParseError(ValueError):
self.name = name self.name = name
self.desc = desc self.desc = desc
ValueError.__init__(self, ValueError.__init__(self,
_('Failed to parse: %s with error: %s')%(name, desc)) _('Failed to parse: %(name)s with error: %(err)s')%dict(
name=name, err=desc))
class ePubFixer(Plugin): class ePubFixer(Plugin):

View File

@ -561,7 +561,9 @@ class HTMLConverter(object):
para = children[i] para = children[i]
break break
if para is None: if para is None:
raise ConversionError(_('Failed to parse link %s %s')%(tag, children)) raise ConversionError(
_('Failed to parse link %(tag)s %(children)s')%dict(
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'
@ -954,7 +956,9 @@ 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(_('Unable to process image %s. Error: %s')%(path, err)) self.log.warning(
_('Unable to process image %(path)s. Error: %(err)s')%dict(
path=path, err=err))
if width == None or height == None: if width == None or height == None:
width, height = im.size width, height = im.size
@ -1014,7 +1018,7 @@ class HTMLConverter(object):
try: try:
self.images[path] = ImageStream(path, encoding=encoding) self.images[path] = ImageStream(path, encoding=encoding)
except LrsError as err: except LrsError as err:
self.log.warning(_('Could not process image: %s\n%s')%( self.log.warning(('Could not process image: %s\n%s')%(
original_path, err)) original_path, err))
return return

View File

@ -4,8 +4,9 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import sys, array, os, re, codecs, logging import sys, array, os, re, codecs, logging
from calibre import setup_cli_handlers, sanitize_file_name from calibre import setup_cli_handlers
from calibre.utils.config import OptionParser from calibre.utils.config import OptionParser
from calibre.utils.filenames import ascii_filename
from calibre.ebooks.lrf.meta import LRFMetaFile from calibre.ebooks.lrf.meta import LRFMetaFile
from calibre.ebooks.lrf.objects import get_object, PageTree, StyleObject, \ from calibre.ebooks.lrf.objects import get_object, PageTree, StyleObject, \
Font, Text, TOCObject, BookAttr, ruby_tags Font, Text, TOCObject, BookAttr, ruby_tags
@ -89,7 +90,7 @@ class LRFDocument(LRFMetaFile):
bookinfo += u'<FreeText reading="">%s</FreeText>\n</BookInfo>\n<DocInfo>\n'%(self.metadata.free_text,) bookinfo += u'<FreeText reading="">%s</FreeText>\n</BookInfo>\n<DocInfo>\n'%(self.metadata.free_text,)
th = self.doc_info.thumbnail th = self.doc_info.thumbnail
if th: if th:
prefix = sanitize_file_name(self.metadata.title, as_unicode=True) prefix = ascii_filename(self.metadata.title)
bookinfo += u'<CThumbnail file="%s" />\n'%(prefix+'_thumbnail.'+self.doc_info.thumbnail_extension,) bookinfo += u'<CThumbnail file="%s" />\n'%(prefix+'_thumbnail.'+self.doc_info.thumbnail_extension,)
if write_files: if write_files:
open(prefix+'_thumbnail.'+self.doc_info.thumbnail_extension, 'wb').write(th) open(prefix+'_thumbnail.'+self.doc_info.thumbnail_extension, 'wb').write(th)

View File

@ -529,8 +529,8 @@ class Metadata(object):
for t in st.intersection(ot): for t in st.intersection(ot):
sidx = lstags.index(t) sidx = lstags.index(t)
oidx = lotags.index(t) oidx = lotags.index(t)
self_tags[sidx] = other.tags[oidx] self_tags[sidx] = other_tags[oidx]
self_tags += [t for t in other.tags if t.lower() in ot-st] self_tags += [t for t in other_tags if t.lower() in ot-st]
setattr(self, x, self_tags) setattr(self, x, self_tags)
my_comments = getattr(self, 'comments', '') my_comments = getattr(self, 'comments', '')
@ -742,7 +742,7 @@ class Metadata(object):
ans += [('ISBN', unicode(self.isbn))] ans += [('ISBN', unicode(self.isbn))]
ans += [(_('Tags'), u', '.join([unicode(t) for t in self.tags]))] ans += [(_('Tags'), u', '.join([unicode(t) for t in self.tags]))]
if self.series: if self.series:
ans += [(_('Series'), unicode(self.series)+ ' #%s'%self.format_series_index())] ans += [_('Series'), unicode(self.series) + ' #%s'%self.format_series_index()]
ans += [(_('Language'), unicode(self.language))] ans += [(_('Language'), unicode(self.language))]
if self.timestamp is not None: if self.timestamp is not None:
ans += [(_('Timestamp'), unicode(self.timestamp.isoformat(' ')))] ans += [(_('Timestamp'), unicode(self.timestamp.isoformat(' ')))]

View File

@ -21,9 +21,9 @@ USAGE='%%prog ebook_file [' + _('options') + ']\n' + \
_(''' _('''
Read/Write metadata from/to ebook files. Read/Write metadata from/to ebook files.
Supported formats for reading metadata: %s Supported formats for reading metadata: %(read)s
Supported formats for writing metadata: %s Supported formats for writing metadata: %(write)s
Different file types support different kinds of metadata. If you try to set Different file types support different kinds of metadata. If you try to set
some metadata on a file type that does not support it, the metadata will be some metadata on a file type that does not support it, the metadata will be
@ -99,7 +99,7 @@ def option_parser():
for w in metadata_writers(): for w in metadata_writers():
writers = writers.union(set(w.file_types)) writers = writers.union(set(w.file_types))
ft, w = ', '.join(sorted(filetypes())), ', '.join(sorted(writers)) ft, w = ', '.join(sorted(filetypes())), ', '.join(sorted(writers))
return config().option_parser(USAGE%(ft, w)) return config().option_parser(USAGE%dict(read=ft, write=w))
def do_set_metadata(opts, mi, stream, stream_type): def do_set_metadata(opts, mi, stream, stream_type):
mi = MetaInformation(mi) mi = MetaInformation(mi)

View File

@ -95,9 +95,9 @@ class CoverManager(object):
authors = [unicode(x) for x in m.creator if x.role == 'aut'] authors = [unicode(x) for x in m.creator if x.role == 'aut']
series_string = None series_string = None
if m.series and m.series_index: if m.series and m.series_index:
series_string = _('Book %s of %s')%( series_string = _('Book %(sidx)s of %(series)s')%dict(
fmt_sidx(m.series_index[0], use_roman=True), sidx=fmt_sidx(m.series_index[0], use_roman=True),
unicode(m.series[0])) series=unicode(m.series[0]))
try: try:
from calibre.ebooks import calibre_cover from calibre.ebooks import calibre_cover

View File

@ -32,8 +32,8 @@ class SplitError(ValueError):
size = len(tostring(root))/1024. size = len(tostring(root))/1024.
ValueError.__init__(self, ValueError.__init__(self,
_('Could not find reasonable point at which to split: ' _('Could not find reasonable point at which to split: '
'%s Sub-tree size: %d KB')% '%(path)s Sub-tree size: %(size)d KB')%dict(
(path, size)) path=path, size=size))
class Split(object): class Split(object):

View File

@ -7,6 +7,7 @@ __docformat__ = 'restructuredtext en'
import sys, struct, zlib, bz2, os import sys, struct, zlib, bz2, os
from calibre import guess_type from calibre import guess_type
from calibre.utils.filenames import ascii_filename
class FileStream: class FileStream:
def IsBinary(self): def IsBinary(self):
@ -156,6 +157,8 @@ class SNBFile:
f.fileSize = os.path.getsize(os.path.join(tdir,fileName)) f.fileSize = os.path.getsize(os.path.join(tdir,fileName))
f.fileBody = open(os.path.join(tdir,fileName), 'rb').read() f.fileBody = open(os.path.join(tdir,fileName), 'rb').read()
f.fileName = fileName.replace(os.sep, '/') f.fileName = fileName.replace(os.sep, '/')
if isinstance(f.fileName, unicode):
f.fileName = ascii_filename(f.fileName).encode('ascii')
self.files.append(f) self.files.append(f)
def AppendBinary(self, fileName, tdir): def AppendBinary(self, fileName, tdir):
@ -164,6 +167,8 @@ class SNBFile:
f.fileSize = os.path.getsize(os.path.join(tdir,fileName)) f.fileSize = os.path.getsize(os.path.join(tdir,fileName))
f.fileBody = open(os.path.join(tdir,fileName), 'rb').read() f.fileBody = open(os.path.join(tdir,fileName), 'rb').read()
f.fileName = fileName.replace(os.sep, '/') f.fileName = fileName.replace(os.sep, '/')
if isinstance(f.fileName, unicode):
f.fileName = ascii_filename(f.fileName).encode('ascii')
self.files.append(f) self.files.append(f)
def GetFileStream(self, fileName): def GetFileStream(self, fileName):

View File

@ -1,4 +1,4 @@
# coding:utf8 # coding:utf-8
__license__ = 'GPL 3' __license__ = 'GPL 3'
__copyright__ = '2010, Hiroshi Miura <miurahr@linux.com>' __copyright__ = '2010, Hiroshi Miura <miurahr@linux.com>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'

View File

@ -74,6 +74,13 @@ gprefs.defaults['action-layout-context-menu-device'] = (
'Add To Library', 'Edit Collections', 'Add To Library', 'Edit Collections',
) )
gprefs.defaults['action-layout-context-menu-cover-browser'] = (
'Edit Metadata', 'Send To Device', 'Save To Disk',
'Connect Share', 'Copy To Library', None,
'Convert Books', 'View', 'Open Folder', 'Show Book Details',
'Similar Books', 'Tweak ePub', None, 'Remove Books',
)
gprefs.defaults['show_splash_screen'] = True gprefs.defaults['show_splash_screen'] = True
gprefs.defaults['toolbar_icon_size'] = 'medium' gprefs.defaults['toolbar_icon_size'] = 'medium'
gprefs.defaults['automerge'] = 'ignore' gprefs.defaults['automerge'] = 'ignore'

View File

@ -120,16 +120,16 @@ class FetchAnnotationsAction(InterfaceAction):
spanTag['style'] = 'font-weight:bold' spanTag['style'] = 'font-weight:bold'
if bookmark.book_format == 'pdf': if bookmark.book_format == 'pdf':
spanTag.insert(0,NavigableString( spanTag.insert(0,NavigableString(
_("%s<br />Last Page Read: %d (%d%%)") % \ _("%(time)s<br />Last Page Read: %(loc)d (%(pr)d%%)") % \
(strftime(u'%x', timestamp.timetuple()), dict(time=strftime(u'%x', timestamp.timetuple()),
last_read_location, loc=last_read_location,
percent_read))) pr=percent_read)))
else: else:
spanTag.insert(0,NavigableString( spanTag.insert(0,NavigableString(
_("%s<br />Last Page Read: Location %d (%d%%)") % \ _("%(time)s<br />Last Page Read: Location %(loc)d (%(pr)d%%)") % \
(strftime(u'%x', timestamp.timetuple()), dict(time=strftime(u'%x', timestamp.timetuple()),
last_read_location, loc=last_read_location,
percent_read))) pr=percent_read)))
divTag.insert(dtc, spanTag) divTag.insert(dtc, spanTag)
dtc += 1 dtc += 1
@ -145,23 +145,23 @@ class FetchAnnotationsAction(InterfaceAction):
for location in sorted(user_notes): for location in sorted(user_notes):
if user_notes[location]['text']: if user_notes[location]['text']:
annotations.append( annotations.append(
_('<b>Location %d &bull; %s</b><br />%s<br />') % \ _('<b>Location %(dl)d &bull; %(typ)s</b><br />%(text)s<br />') % \
(user_notes[location]['displayed_location'], dict(dl=user_notes[location]['displayed_location'],
user_notes[location]['type'], typ=user_notes[location]['type'],
user_notes[location]['text'] if \ text=(user_notes[location]['text'] if \
user_notes[location]['type'] == 'Note' else \ user_notes[location]['type'] == 'Note' else \
'<i>%s</i>' % user_notes[location]['text'])) '<i>%s</i>' % user_notes[location]['text'])))
else: else:
if bookmark.book_format == 'pdf': if bookmark.book_format == 'pdf':
annotations.append( annotations.append(
_('<b>Page %d &bull; %s</b><br />') % \ _('<b>Page %(dl)d &bull; %(typ)s</b><br />') % \
(user_notes[location]['displayed_location'], dict(dl=user_notes[location]['displayed_location'],
user_notes[location]['type'])) typ=user_notes[location]['type']))
else: else:
annotations.append( annotations.append(
_('<b>Location %d &bull; %s</b><br />') % \ _('<b>Location %(dl)d &bull; %(typ)s</b><br />') % \
(user_notes[location]['displayed_location'], dict(dl=user_notes[location]['displayed_location'],
user_notes[location]['type'])) typ=user_notes[location]['type']))
for annotation in annotations: for annotation in annotations:
divTag.insert(dtc, annotation) divTag.insert(dtc, annotation)

View File

@ -82,7 +82,8 @@ class GenerateCatalogAction(InterfaceAction):
self.gui.sync_catalogs() self.gui.sync_catalogs()
if job.fmt not in ['EPUB','MOBI']: if job.fmt not in ['EPUB','MOBI']:
export_dir = choose_dir(self.gui, _('Export Catalog Directory'), export_dir = choose_dir(self.gui, _('Export Catalog Directory'),
_('Select destination for %s.%s') % (job.catalog_title, job.fmt.lower())) _('Select destination for %(title)s.%(fmt)s') % dict(
title=job.catalog_title, fmt=job.fmt.lower()))
if export_dir: if export_dir:
destination = os.path.join(export_dir, '%s.%s' % (job.catalog_title, job.fmt.lower())) destination = os.path.join(export_dir, '%s.%s' % (job.catalog_title, job.fmt.lower()))
shutil.copyfile(job.catalog_file_path, destination) shutil.copyfile(job.catalog_file_path, destination)

View File

@ -160,8 +160,9 @@ class CopyToLibraryAction(InterfaceAction):
error_dialog(self.gui, _('Failed'), _('Could not copy books: ') + e, error_dialog(self.gui, _('Failed'), _('Could not copy books: ') + e,
det_msg=tb, show=True) det_msg=tb, show=True)
else: else:
self.gui.status_bar.show_message(_('Copied %d books to %s') % self.gui.status_bar.show_message(
(len(ids), loc), 2000) _('Copied %(num)d books to %(loc)s') %
dict(num=len(ids), loc=loc), 2000)
if delete_after and self.worker.processed: if delete_after and self.worker.processed:
v = self.gui.library_view v = self.gui.library_view
ci = v.currentIndex() ci = v.currentIndex()

View File

@ -159,9 +159,9 @@ def render_data(mi, use_roman_numbers=True, all_fields=False):
sidx = mi.get(field+'_index') sidx = mi.get(field+'_index')
if sidx is None: if sidx is None:
sidx = 1.0 sidx = 1.0
val = _('Book %s of <span class="series_name">%s</span>')%(fmt_sidx(sidx, val = _('Book %(sidx)s of <span class="series_name">%(series)s</span>')%dict(
use_roman=use_roman_numbers), sidx=fmt_sidx(sidx, use_roman=use_roman_numbers),
prepare_string_for_xml(getattr(mi, field))) series=prepare_string_for_xml(getattr(mi, field)))
ans.append((field, u'<td class="title">%s</td><td>%s</td>'%(name, val))) ans.append((field, u'<td class="title">%s</td><td>%s</td>'%(name, val)))
@ -541,7 +541,8 @@ class BookDetails(QWidget): # {{{
self.setToolTip( self.setToolTip(
'<p>'+_('Double-click to open Book Details window') + '<p>'+_('Double-click to open Book Details window') +
'<br><br>' + _('Path') + ': ' + self.current_path + '<br><br>' + _('Path') + ': ' + self.current_path +
'<br><br>' + _('Cover size: %dx%d')%(sz.width(), sz.height()) '<br><br>' + _('Cover size: %(width)d x %(height)d')%dict(
width=sz.width(), height=sz.height())
) )
def reset_info(self): def reset_info(self):

View File

@ -9,8 +9,8 @@ Module to implement the Cover Flow feature
import sys, os, time import sys, os, time
from PyQt4.Qt import QImage, QSizePolicy, QTimer, QDialog, Qt, QSize, \ from PyQt4.Qt import (QImage, QSizePolicy, QTimer, QDialog, Qt, QSize,
QStackedLayout, QLabel, QByteArray, pyqtSignal QStackedLayout, QLabel, QByteArray, pyqtSignal)
from calibre import plugins from calibre import plugins
from calibre.gui2 import config, available_height, available_width, gprefs from calibre.gui2 import config, available_height, available_width, gprefs
@ -84,6 +84,7 @@ if pictureflow is not None:
class CoverFlow(pictureflow.PictureFlow): class CoverFlow(pictureflow.PictureFlow):
dc_signal = pyqtSignal() dc_signal = pyqtSignal()
context_menu_requested = pyqtSignal()
def __init__(self, parent=None): def __init__(self, parent=None):
pictureflow.PictureFlow.__init__(self, parent, pictureflow.PictureFlow.__init__(self, parent,
@ -94,6 +95,17 @@ if pictureflow is not None:
QSizePolicy.Expanding)) QSizePolicy.Expanding))
self.dc_signal.connect(self._data_changed, self.dc_signal.connect(self._data_changed,
type=Qt.QueuedConnection) type=Qt.QueuedConnection)
self.context_menu = None
self.setContextMenuPolicy(Qt.DefaultContextMenu)
def set_context_menu(self, cm):
self.context_menu = cm
def contextMenuEvent(self, event):
if self.context_menu is not None:
self.context_menu_requested.emit()
self.context_menu.popup(event.globalPos())
event.accept()
def sizeHint(self): def sizeHint(self):
return self.minimumSize() return self.minimumSize()
@ -149,6 +161,7 @@ class CoverFlowMixin(object):
self.cover_flow_sync_flag = True self.cover_flow_sync_flag = True
self.cover_flow = CoverFlow(parent=self) self.cover_flow = CoverFlow(parent=self)
self.cover_flow.currentChanged.connect(self.sync_listview_to_cf) self.cover_flow.currentChanged.connect(self.sync_listview_to_cf)
self.cover_flow.context_menu_requested.connect(self.cf_context_menu_requested)
self.library_view.selectionModel().currentRowChanged.connect( self.library_view.selectionModel().currentRowChanged.connect(
self.sync_cf_to_listview) self.sync_cf_to_listview)
self.db_images = DatabaseImages(self.library_view.model()) self.db_images = DatabaseImages(self.library_view.model())
@ -234,6 +247,14 @@ class CoverFlowMixin(object):
self.cover_flow.setCurrentSlide(current.row()) self.cover_flow.setCurrentSlide(current.row())
self.cover_flow_sync_flag = True self.cover_flow_sync_flag = True
def cf_context_menu_requested(self):
row = self.cover_flow.currentSlide()
m = self.library_view.model()
index = m.index(row, 0)
sm = self.library_view.selectionModel()
sm.select(index, sm.ClearAndSelect|sm.Rows)
self.library_view.setCurrentIndex(index)
def cover_flow_do_sync(self): def cover_flow_do_sync(self):
self.cover_flow_sync_flag = True self.cover_flow_sync_flag = True
try: try:

View File

@ -912,8 +912,9 @@ class DeviceMixin(object): # {{{
format_count[f] = 1 format_count[f] = 1
for f in self.device_manager.device.settings().format_map: for f in self.device_manager.device.settings().format_map:
if f in format_count.keys(): if f in format_count.keys():
formats.append((f, _('%i of %i Books') % (format_count[f], formats.append((f, _('%(num)i of %(total)i Books') % dict(
len(rows)), True if f in aval_out_formats else False)) num=format_count[f], total=len(rows)),
True if f in aval_out_formats else False))
elif f in aval_out_formats: elif f in aval_out_formats:
formats.append((f, _('0 of %i Books') % len(rows), True)) formats.append((f, _('0 of %i Books') % len(rows), True))
d = ChooseFormatDeviceDialog(self, _('Choose format to send to device'), formats) d = ChooseFormatDeviceDialog(self, _('Choose format to send to device'), formats)

View File

@ -106,7 +106,8 @@ class BookInfo(QDialog, Ui_BookInfo):
Qt.KeepAspectRatio, Qt.SmoothTransformation) Qt.KeepAspectRatio, Qt.SmoothTransformation)
self.cover.set_pixmap(pixmap) self.cover.set_pixmap(pixmap)
sz = pixmap.size() sz = pixmap.size()
self.cover.setToolTip(_('Cover size: %dx%d')%(sz.width(), sz.height())) self.cover.setToolTip(_('Cover size: %(width)d x %(height)d')%dict(
width=sz.width(), height=sz.height()))
def refresh(self, row): def refresh(self, row):
if isinstance(row, QModelIndex): if isinstance(row, QModelIndex):

View File

@ -173,10 +173,10 @@ class MyBlockingBusy(QDialog): # {{{
mi = self.db.get_metadata(id, index_is_id=True) mi = self.db.get_metadata(id, index_is_id=True)
series_string = None series_string = None
if mi.series: if mi.series:
series_string = _('Book %s of %s')%( series_string = _('Book %(sidx)s of %(series)s')%dict(
fmt_sidx(mi.series_index, sidx=fmt_sidx(mi.series_index,
use_roman=config['use_roman_numerals_for_series_number']), use_roman=config['use_roman_numerals_for_series_number']),
mi.series) series=mi.series)
cdata = calibre_cover(mi.title, mi.format_field('authors')[-1], cdata = calibre_cover(mi.title, mi.format_field('authors')[-1],
series_string=series_string) series_string=series_string)
@ -749,15 +749,9 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
val = self.s_r_do_regexp(mi) val = self.s_r_do_regexp(mi)
val = self.s_r_do_destination(mi, val) val = self.s_r_do_destination(mi, val)
if dfm['is_multiple']: if dfm['is_multiple']:
if dest == 'authors' and len(val) == 0:
error_dialog(self, _('Search/replace invalid'),
_('Authors cannot be set to the empty string. '
'Book title %s not processed')%mi.title,
show=True)
return
# convert the colon-separated pair strings back into a dict, which
# is what set_identifiers wants
if dfm['is_csp']: if dfm['is_csp']:
# convert the colon-separated pair strings back into a dict,
# which is what set_identifiers wants
dst_id_type = unicode(self.s_r_dst_ident.text()) dst_id_type = unicode(self.s_r_dst_ident.text())
if dst_id_type: if dst_id_type:
v = ''.join(val) v = ''.join(val)
@ -769,11 +763,7 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
else: else:
val = self.s_r_replace_mode_separator().join(val) val = self.s_r_replace_mode_separator().join(val)
if dest == 'title' and len(val) == 0: if dest == 'title' and len(val) == 0:
error_dialog(self, _('Search/replace invalid'), val = _('Unknown')
_('Title cannot be set to the empty string. '
'Book title %s not processed')%mi.title,
show=True)
return
if dfm['is_custom']: if dfm['is_custom']:
extra = self.db.get_custom_extra(id, label=dfm['label'], index_is_id=True) extra = self.db.get_custom_extra(id, label=dfm['label'], index_is_id=True)

View File

@ -701,7 +701,9 @@ class PluginUpdaterDialog(SizePersistedDialog):
if DEBUG: if DEBUG:
prints('Locating zip file for %s: %s'% (display_plugin.name, display_plugin.forum_link)) prints('Locating zip file for %s: %s'% (display_plugin.name, display_plugin.forum_link))
self.gui.status_bar.showMessage(_('Locating zip file for %s: %s') % (display_plugin.name, display_plugin.forum_link)) self.gui.status_bar.showMessage(
_('Locating zip file for %(name)s: %(link)s') % dict(
name=display_plugin.name, link=display_plugin.forum_link))
plugin_zip_url = self._read_zip_attachment_url(display_plugin.forum_link) plugin_zip_url = self._read_zip_attachment_url(display_plugin.forum_link)
if not plugin_zip_url: if not plugin_zip_url:
return error_dialog(self.gui, _('Install Plugin Failed'), return error_dialog(self.gui, _('Install Plugin Failed'),

View File

@ -381,7 +381,9 @@ class SchedulerDialog(QDialog, Ui_Dialog):
d = utcnow() - last_downloaded d = utcnow() - last_downloaded
def hm(x): return (x-x%3600)//3600, (x%3600 - (x%3600)%60)//60 def hm(x): return (x-x%3600)//3600, (x%3600 - (x%3600)%60)//60
hours, minutes = hm(d.seconds) hours, minutes = hm(d.seconds)
tm = _('%d days, %d hours and %d minutes ago')%(d.days, hours, minutes) tm = _('%(days)d days, %(hours)d hours'
' and %(mins)d minutes ago')%dict(
days=d.days, hours=hours, mins=minutes)
if d < timedelta(days=366): if d < timedelta(days=366):
ld_text = tm ld_text = tm
else: else:

View File

@ -57,7 +57,7 @@ class TagCategories(QDialog, Ui_TagCategories):
lambda: [n for (id, n) in self.db.all_publishers()], lambda: [n for (id, n) in self.db.all_publishers()],
lambda: self.db.all_tags() lambda: self.db.all_tags()
] ]
category_names = ['', _('Authors'), _('Series'), _('Publishers'), _('Tags')] category_names = ['', _('Authors'), ngettext('Series', 'Series', 2), _('Publishers'), _('Tags')]
cvals = {} cvals = {}
for key,cc in self.db.custom_field_metadata().iteritems(): for key,cc in self.db.custom_field_metadata().iteritems():
@ -260,6 +260,7 @@ class TagCategories(QDialog, Ui_TagCategories):
self.applied_items = [cat[2] for cat in self.categories.get(self.current_cat_name, [])] self.applied_items = [cat[2] for cat in self.categories.get(self.current_cat_name, [])]
else: else:
self.applied_items = [] self.applied_items = []
self.applied_items.sort(key=lambda x:sort_key(self.all_items[x].name))
self.display_filtered_categories(None) self.display_filtered_categories(None)
def accept(self): def accept(self):

View File

@ -18,7 +18,8 @@ class ListWidgetItem(QListWidgetItem):
def data(self, role): def data(self, role):
if role == Qt.DisplayRole: if role == Qt.DisplayRole:
if self.initial_value != self.current_value: if self.initial_value != self.current_value:
return _('%s (was %s)')%(self.current_value, self.initial_value) return _('%(curr)s (was %(initial)s)')%dict(
curr=self.current_value, initial=self.initial_value)
else: else:
return self.current_value return self.current_value
elif role == Qt.EditRole: elif role == Qt.EditRole:

View File

@ -143,7 +143,9 @@ class UserProfiles(ResizableDialog, Ui_Dialog):
pt = PersistentTemporaryFile(suffix='.recipe') pt = PersistentTemporaryFile(suffix='.recipe')
pt.write(src.encode('utf-8')) pt.write(src.encode('utf-8'))
pt.close() pt.close()
body = _('The attached file: %s is a recipe to download %s.')%(os.path.basename(pt.name), title) body = _('The attached file: %(fname)s is a '
'recipe to download %(title)s.')%dict(
fname=os.path.basename(pt.name), title=title)
subject = _('Recipe for ')+title subject = _('Recipe for ')+title
url = QUrl('mailto:') url = QUrl('mailto:')
url.addQueryItem('subject', subject) url.addQueryItem('subject', subject)

View File

@ -51,8 +51,8 @@ class DownloadDialog(QDialog): # {{{
self.setWindowTitle(_('Download %s')%fname) self.setWindowTitle(_('Download %s')%fname)
self.l = QVBoxLayout(self) self.l = QVBoxLayout(self)
self.purl = urlparse(url) self.purl = urlparse(url)
self.msg = QLabel(_('Downloading <b>%s</b> from %s')%(fname, self.msg = QLabel(_('Downloading <b>%(fname)s</b> from %(url)s')%dict(
self.purl.netloc)) fname=fname, url=self.purl.netloc))
self.msg.setWordWrap(True) self.msg.setWordWrap(True)
self.l.addWidget(self.msg) self.l.addWidget(self.msg)
self.pb = QProgressBar(self) self.pb = QProgressBar(self)
@ -82,8 +82,8 @@ class DownloadDialog(QDialog): # {{{
self.exec_() self.exec_()
if self.worker.err is not None: if self.worker.err is not None:
error_dialog(self.parent(), _('Download failed'), error_dialog(self.parent(), _('Download failed'),
_('Failed to download from %r with error: %s')%( _('Failed to download from %(url)r with error: %(err)s')%dict(
self.worker.url, self.worker.err), url=self.worker.url, err=self.worker.err),
det_msg=self.worker.tb, show=True) det_msg=self.worker.tb, show=True)
def update(self): def update(self):

View File

@ -120,7 +120,7 @@ def send_mails(jobnames, callback, attachments, to_s, subjects,
texts, attachment_names, job_manager): texts, attachment_names, job_manager):
for name, attachment, to, subject, text, aname in zip(jobnames, for name, attachment, to, subject, text, aname in zip(jobnames,
attachments, to_s, subjects, texts, attachment_names): attachments, to_s, subjects, texts, attachment_names):
description = _('Email %s to %s') % (name, to) description = _('Email %(name)s to %(to)s') % dict(name=name, to=to)
job = ThreadedJob('email', description, gui_sendmail, (attachment, aname, to, job = ThreadedJob('email', description, gui_sendmail, (attachment, aname, to,
subject, text), {}, callback) subject, text), {}, callback)
job_manager.run_threaded_job(job) job_manager.run_threaded_job(job)

View File

@ -62,7 +62,6 @@ class LibraryViewMixin(object): # {{{
view = getattr(self, view+'_view') view = getattr(self, view+'_view')
view.verticalHeader().sectionDoubleClicked.connect(self.iactions['View'].view_specific_book) view.verticalHeader().sectionDoubleClicked.connect(self.iactions['View'].view_specific_book)
self.build_context_menus()
self.library_view.model().set_highlight_only(config['highlight_search_matches']) self.library_view.model().set_highlight_only(config['highlight_search_matches'])
def build_context_menus(self): def build_context_menus(self):
@ -81,6 +80,11 @@ class LibraryViewMixin(object): # {{{
for v in (self.memory_view, self.card_a_view, self.card_b_view): for v in (self.memory_view, self.card_a_view, self.card_b_view):
v.set_context_menu(dm, ec) v.set_context_menu(dm, ec)
if self.cover_flow is not None:
cm = QMenu(self.cover_flow)
populate_menu(cm,
gprefs['action-layout-context-menu-cover-browser'])
self.cover_flow.set_context_menu(cm)
def search_done(self, view, ok): def search_done(self, view, ok):
if view is self.current_view(): if view is self.current_view():

View File

@ -878,9 +878,10 @@ class Cover(ImageView): # {{{
series = self.dialog.series.current_val series = self.dialog.series.current_val
series_string = None series_string = None
if series: if series:
series_string = _('Book %s of %s')%( series_string = _('Book %(sidx)s of %(series)s')%dict(
fmt_sidx(self.dialog.series_index.current_val, sidx=fmt_sidx(self.dialog.series_index.current_val,
use_roman=config['use_roman_numerals_for_series_number']), series) use_roman=config['use_roman_numerals_for_series_number']),
series=series)
self.current_val = calibre_cover(title, author, self.current_val = calibre_cover(title, author,
series_string=series_string) series_string=series_string)
@ -921,8 +922,8 @@ class Cover(ImageView): # {{{
self.setPixmap(pm) self.setPixmap(pm)
tt = _('This book has no cover') tt = _('This book has no cover')
if self._cdata: if self._cdata:
tt = _('Cover size: %dx%d pixels') % \ tt = _('Cover size: %(width)d x %(height)d pixels') % \
(pm.width(), pm.height()) dict(width=pm.width(), height=pm.height())
self.setToolTip(tt) self.setToolTip(tt)
return property(fget=fget, fset=fset) return property(fget=fget, fset=fset)

View File

@ -196,7 +196,7 @@ def download(ids, db, do_identify, covers,
ans[i] = mi ans[i] = mi
count += 1 count += 1
notifications.put((count/len(ids), notifications.put((count/len(ids),
_('Downloaded %d of %d')%(count, len(ids)))) _('Downloaded %(num)d of %(tot)d')%dict(num=count, tot=len(ids))))
log('Download complete, with %d failures'%len(failed_ids)) log('Download complete, with %d failures'%len(failed_ids))
return (ans, failed_ids, failed_covers, title_map, all_failed) return (ans, failed_ids, failed_covers, title_map, all_failed)

View File

@ -726,8 +726,8 @@ class CoversWidget(QWidget): # {{{
if num < 2: if num < 2:
txt = _('Could not find any covers for <b>%s</b>')%self.book.title txt = _('Could not find any covers for <b>%s</b>')%self.book.title
else: else:
txt = _('Found <b>%d</b> covers of %s. Pick the one you like' txt = _('Found <b>%(num)d</b> covers of %(title)s. Pick the one you like'
' best.')%(num-1, self.title) ' best.')%dict(num=num-1, title=self.title)
self.msg.setText(txt) self.msg.setText(txt)
self.finished.emit() self.finished.emit()

View File

@ -1332,6 +1332,7 @@ void PictureFlow::mousePressEvent(QMouseEvent* event)
void PictureFlow::mouseReleaseEvent(QMouseEvent* event) void PictureFlow::mouseReleaseEvent(QMouseEvent* event)
{ {
bool accepted = false;
int sideWidth = (d->buffer.width() - slideSize().width()) /2; int sideWidth = (d->buffer.width() - slideSize().width()) /2;
if (d->singlePress) if (d->singlePress)
@ -1339,14 +1340,21 @@ void PictureFlow::mouseReleaseEvent(QMouseEvent* event)
if (event->x() < sideWidth ) if (event->x() < sideWidth )
{ {
showPrevious(); showPrevious();
accepted = true;
} else if ( event->x() > sideWidth + slideSize().width() ) { } else if ( event->x() > sideWidth + slideSize().width() ) {
showNext(); showNext();
accepted = true;
} else { } else {
if (event->button() == Qt::LeftButton) {
emit itemActivated(d->getTarget()); emit itemActivated(d->getTarget());
accepted = true;
}
} }
if (accepted) {
event->accept(); event->accept();
} }
}
emit inputReceived(); emit inputReceived();
} }

View File

@ -445,15 +445,15 @@ class RulesModel(QAbstractListModel): # {{{
def rule_to_html(self, col, rule): def rule_to_html(self, col, rule):
if not isinstance(rule, Rule): if not isinstance(rule, Rule):
return _(''' return _('''
<p>Advanced Rule for column <b>%s</b>: <p>Advanced Rule for column <b>%(col)s</b>:
<pre>%s</pre> <pre>%(rule)s</pre>
''')%(col, prepare_string_for_xml(rule)) ''')%dict(col=col, rule=prepare_string_for_xml(rule))
conditions = [self.condition_to_html(c) for c in rule.conditions] conditions = [self.condition_to_html(c) for c in rule.conditions]
return _('''\ return _('''\
<p>Set the color of <b>%s</b> to <b>%s</b> if the following <p>Set the color of <b>%(col)s</b> to <b>%(color)s</b> if the following
conditions are met:</p> conditions are met:</p>
<ul>%s</ul> <ul>%(rule)s</ul>
''') % (col, rule.color, ''.join(conditions)) ''') % dict(col=col, color=rule.color, rule=''.join(conditions))
def condition_to_html(self, condition): def condition_to_html(self, condition):
c, a, v = condition c, a, v = condition
@ -464,8 +464,8 @@ class RulesModel(QAbstractListModel): # {{{
action_name = trans action_name = trans
return ( return (
_('<li>If the <b>%s</b> column <b>%s</b> value: <b>%s</b>') % _('<li>If the <b>%(col)s</b> column <b>%(action)s</b> value: <b>%(val)s</b>') %
(c, action_name, prepare_string_for_xml(v))) dict(col=c, action=action_name, val=prepare_string_for_xml(v)))
# }}} # }}}

View File

@ -262,8 +262,8 @@ class PluginConfig(QWidget): # {{{
self.l = l = QVBoxLayout() self.l = l = QVBoxLayout()
self.setLayout(l) self.setLayout(l)
self.c = c = QLabel(_('<b>Configure %s</b><br>%s') % (plugin.name, self.c = c = QLabel(_('<b>Configure %(name)s</b><br>%(desc)s') % dict(
plugin.description)) name=plugin.name, desc=plugin.description))
c.setAlignment(Qt.AlignHCenter) c.setAlignment(Qt.AlignHCenter)
l.addWidget(c) l.addWidget(c)

View File

@ -225,6 +225,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
'calibre library')), 'calibre library')),
('context-menu-device', _('The context menu for the books on ' ('context-menu-device', _('The context menu for the books on '
'the device')), 'the device')),
('context-menu-cover-browser', _('The context menu for the cover '
'browser')),
] ]
def genesis(self, gui): def genesis(self, gui):

View File

@ -67,7 +67,9 @@ class CacheUpdateThread(Thread, QObject):
self.total_changed.emit(len(raw_books)) self.total_changed.emit(len(raw_books))
for i, book_data in enumerate(raw_books): for i, book_data in enumerate(raw_books):
self.update_details.emit(_('%s of %s books processed.') % (i, len(raw_books))) self.update_details.emit(
_('%(num)s of %(tot)s books processed.') % dict(
num=i, tot=len(raw_books)))
book = SearchResult() book = SearchResult()
book.detail_item = ''.join(book_data.xpath('.//a/@href')) book.detail_item = ''.join(book_data.xpath('.//a/@href'))
book.formats = ''.join(book_data.xpath('.//i/text()')) book.formats = ''.join(book_data.xpath('.//i/text()'))

View File

@ -394,7 +394,7 @@ class TagsModel(QAbstractItemModel): # {{{
not fm['is_custom'] and \ not fm['is_custom'] and \
not fm['kind'] == 'user' \ not fm['kind'] == 'user' \
else False else False
in_uc = fm['kind'] == 'user' in_uc = fm['kind'] == 'user' and not is_gst
tt = key if in_uc else None tt = key if in_uc else None
if collapse_model == 'first letter': if collapse_model == 'first letter':
@ -685,42 +685,35 @@ class TagsModel(QAbstractItemModel): # {{{
def handle_user_category_drop(self, on_node, ids, column): def handle_user_category_drop(self, on_node, ids, column):
categories = self.db.prefs.get('user_categories', {}) categories = self.db.prefs.get('user_categories', {})
category = categories.get(on_node.category_key[1:], None) cat_contents = categories.get(on_node.category_key[1:], None)
if category is None: if cat_contents is None:
return return
cat_contents = set([(v, c) for v,c,ign in cat_contents])
fm_src = self.db.metadata_for_field(column) fm_src = self.db.metadata_for_field(column)
for id in ids:
label = fm_src['label'] label = fm_src['label']
for id in ids:
if not fm_src['is_custom']: if not fm_src['is_custom']:
if label == 'authors': if label == 'authors':
items = self.db.get_authors_with_ids()
items = [(i[0], i[1].replace('|', ',')) for i in items]
value = self.db.authors(id, index_is_id=True) value = self.db.authors(id, index_is_id=True)
value = [v.replace('|', ',') for v in value.split(',')] value = [v.replace('|', ',') for v in value.split(',')]
elif label == 'publisher': elif label == 'publisher':
items = self.db.get_publishers_with_ids()
value = self.db.publisher(id, index_is_id=True) value = self.db.publisher(id, index_is_id=True)
elif label == 'series': elif label == 'series':
items = self.db.get_series_with_ids()
value = self.db.series(id, index_is_id=True) value = self.db.series(id, index_is_id=True)
else: else:
items = self.db.get_custom_items_with_ids(label=label)
if fm_src['datatype'] != 'composite': if fm_src['datatype'] != 'composite':
value = self.db.get_custom(id, label=label, index_is_id=True) value = self.db.get_custom(id, label=label, index_is_id=True)
else: else:
value = self.db.get_property(id, loc=fm_src['rec_index'], value = self.db.get_property(id, loc=fm_src['rec_index'],
index_is_id=True) index_is_id=True)
if value is None: if value:
return
if not isinstance(value, list): if not isinstance(value, list):
value = [value] value = [value]
for val in value: cat_contents |= set([(v, column) for v in value])
for (v, c, id) in category:
if v == val and c == column: categories[on_node.category_key[1:]] = [[v, c, 0] for v,c in cat_contents]
break
else:
category.append([val, column, 0])
categories[on_node.category_key[1:]] = category
self.db.prefs.set('user_categories', categories) self.db.prefs.set('user_categories', categories)
self.refresh_required.emit() self.refresh_required.emit()

View File

@ -384,8 +384,8 @@ class TagsView(QTreeView): # {{{
action='delete_search', key=tag.name)) action='delete_search', key=tag.name))
if key.startswith('@') and not item.is_gst: if key.startswith('@') and not item.is_gst:
self.context_menu.addAction(self.user_category_icon, self.context_menu.addAction(self.user_category_icon,
_('Remove %s from category %s')% _('Remove %(item)s from category %(cat)s')%
(display_name(tag), item.py_name), dict(item=display_name(tag), cat=item.py_name),
partial(self.context_menu_handler, partial(self.context_menu_handler,
action='delete_item_from_user_category', action='delete_item_from_user_category',
key = key, index = tag_item)) key = key, index = tag_item))

View File

@ -94,8 +94,8 @@ def convert_single_ebook(parent, db, book_ids, auto_conversion=False, # {{{
msg = '%s' % '\n'.join(res) msg = '%s' % '\n'.join(res)
warning_dialog(parent, _('Could not convert some books'), warning_dialog(parent, _('Could not convert some books'),
_('Could not convert %d of %d books, because no suitable source' _('Could not convert %(num)d of %(tot)d books, because no suitable source'
' format was found.') % (len(res), total), ' format was found.') % dict(num=len(res), tot=total),
msg).exec_() msg).exec_()
return jobs, changed, bad return jobs, changed, bad
@ -187,7 +187,8 @@ class QueueBulk(QProgressDialog):
except: except:
dtitle = repr(mi.title) dtitle = repr(mi.title)
self.setLabelText(_('Queueing ')+dtitle) self.setLabelText(_('Queueing ')+dtitle)
desc = _('Convert book %d of %d (%s)') % (self.i, len(self.book_ids), dtitle) desc = _('Convert book %(num)d of %(tot)d (%(title)s)') % dict(
num=self.i, tot=len(self.book_ids), title=dtitle)
args = [in_file.name, out_file.name, lrecs] args = [in_file.name, out_file.name, lrecs]
temp_files.append(out_file) temp_files.append(out_file)
@ -209,8 +210,8 @@ class QueueBulk(QProgressDialog):
msg = '%s' % '\n'.join(res) msg = '%s' % '\n'.join(res)
warning_dialog(self.parent, _('Could not convert some books'), warning_dialog(self.parent, _('Could not convert some books'),
_('Could not convert %d of %d books, because no suitable ' _('Could not convert %(num)d of %(tot)d books, because no suitable '
'source format was found.') % (len(res), len(self.book_ids)), 'source format was found.') % dict(num=len(res), tot=len(self.book_ids)),
msg).exec_() msg).exec_()
self.parent = None self.parent = None
self.jobs.reverse() self.jobs.reverse()

View File

@ -308,6 +308,8 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
self.height()) self.height())
self.resize(self.width(), self._calculated_available_height) self.resize(self.width(), self._calculated_available_height)
self.build_context_menus()
for ac in self.iactions.values(): for ac in self.iactions.values():
try: try:
ac.gui_layout_complete() ac.gui_layout_complete()

View File

@ -70,10 +70,10 @@ class UpdateNotification(QDialog):
self.logo.setPixmap(QPixmap(I('lt.png')).scaled(100, 100, self.logo.setPixmap(QPixmap(I('lt.png')).scaled(100, 100,
Qt.IgnoreAspectRatio, Qt.SmoothTransformation)) Qt.IgnoreAspectRatio, Qt.SmoothTransformation))
self.label = QLabel(('<p>'+ self.label = QLabel(('<p>'+
_('%s has been updated to version <b>%s</b>. ' _('%(app)s has been updated to version <b>%(ver)s</b>. '
'See the <a href="http://calibre-ebook.com/whats-new' 'See the <a href="http://calibre-ebook.com/whats-new'
'">new features</a>.'))%( '">new features</a>.'))%dict(
__appname__, calibre_version)) app=__appname__, ver=calibre_version))
self.label.setOpenExternalLinks(True) self.label.setOpenExternalLinks(True)
self.label.setWordWrap(True) self.label.setWordWrap(True)
self.setWindowTitle(_('Update available!')) self.setWindowTitle(_('Update available!'))

View File

@ -492,11 +492,11 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
self.set_page_number(frac) self.set_page_number(frac)
def magnification_changed(self, val): def magnification_changed(self, val):
tt = _('Make font size %s\nCurrent magnification: %.1f') tt = _('Make font size %(which)s\nCurrent magnification: %(mag).1f')
self.action_font_size_larger.setToolTip( self.action_font_size_larger.setToolTip(
tt %(_('larger'), val)) tt %dict(which=_('larger'), mag=val))
self.action_font_size_smaller.setToolTip( self.action_font_size_smaller.setToolTip(
tt %(_('smaller'), val)) tt %dict(which=_('smaller'), mag=val))
def find(self, text, repeat=False, backwards=False): def find(self, text, repeat=False, backwards=False):
if not text: if not text:

View File

@ -569,9 +569,9 @@ def move_library(oldloc, newloc, parent, callback_on_complete):
det = traceback.format_exc() det = traceback.format_exc()
error_dialog(parent, _('Invalid database'), error_dialog(parent, _('Invalid database'),
_('<p>An invalid library already exists at ' _('<p>An invalid library already exists at '
'%s, delete it before trying to move the ' '%(loc)s, delete it before trying to move the '
'existing library.<br>Error: %s')%(newloc, 'existing library.<br>Error: %(err)s')%dict(loc=newloc,
str(err)), det, show=True) err=str(err)), det, show=True)
callback(None) callback(None)
return return
else: else:

View File

@ -31,9 +31,9 @@ class TestEmail(QDialog, TE_Dialog):
if pa: if pa:
self.to.setText(pa) self.to.setText(pa)
if opts.relay_host: if opts.relay_host:
self.label.setText(_('Using: %s:%s@%s:%s and %s encryption')% self.label.setText(_('Using: %(un)s:%(pw)s@%(host)s:%(port)s and %(enc)s encryption')%
(opts.relay_username, unhexlify(opts.relay_password), dict(un=opts.relay_username, pw=unhexlify(opts.relay_password),
opts.relay_host, opts.relay_port, opts.encryption)) host=opts.relay_host, port=opts.relay_port, enc=opts.encryption))
def test(self, *args): def test(self, *args):
self.log.setPlainText(_('Sending...')) self.log.setPlainText(_('Sending...'))

View File

@ -54,12 +54,12 @@ class CSV_XML(CatalogPlugin): # {{{
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: %s,\n' 'Available fields: %(fields)s,\n'
'plus user-created custom fields.\n' 'plus user-created custom fields.\n'
'Example: %s=title,authors,tags\n' 'Example: %(opt)s=title,authors,tags\n'
"Default: '%%default'\n" "Default: '%%default'\n"
"Applies to: CSV, XML output formats")%(', '.join(FIELDS), "Applies to: CSV, XML output formats")%dict(
'--fields')), fields=', '.join(FIELDS), opt='--fields')),
Option('--sort-by', Option('--sort-by',
default = 'id', default = 'id',
@ -250,12 +250,12 @@ class BIBTEX(CatalogPlugin): # {{{
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: %s.\n' 'Available fields: %(fields)s.\n'
'plus user-created custom fields.\n' 'plus user-created custom fields.\n'
'Example: %s=title,authors,tags\n' 'Example: %(opt)s=title,authors,tags\n'
"Default: '%%default'\n" "Default: '%%default'\n"
"Applies to: BIBTEX output format")%(', '.join(FIELDS), "Applies to: BIBTEX output format")%dict(
'--fields')), fields=', '.join(FIELDS), opt='--fields')),
Option('--sort-by', Option('--sort-by',
default = 'id', default = 'id',

View File

@ -62,7 +62,8 @@ class Tag(object):
if self.avg_rating > 0: if self.avg_rating > 0:
if tooltip: if tooltip:
tooltip = tooltip + ': ' tooltip = tooltip + ': '
tooltip = _('%sAverage rating is %3.1f')%(tooltip, self.avg_rating) tooltip = _('%(tt)sAverage rating is %(rating)3.1f')%dict(
tt=tooltip, rating=self.avg_rating)
self.tooltip = tooltip self.tooltip = tooltip
self.icon = icon self.icon = icon
self.category = category self.category = category

View File

@ -121,7 +121,7 @@ class FieldMetadata(dict):
'datatype':'series', 'datatype':'series',
'is_multiple':{}, 'is_multiple':{},
'kind':'field', 'kind':'field',
'name':_('Series'), 'name':ngettext('Series', 'Series', 2),
'search_terms':['series'], 'search_terms':['series'],
'is_custom':False, 'is_custom':False,
'is_category':True, 'is_category':True,

View File

@ -92,16 +92,17 @@ def config(defaults=None):
' By default all available formats are saved.')) ' By default all available formats are saved.'))
x('template', default=DEFAULT_TEMPLATE, x('template', default=DEFAULT_TEMPLATE,
help=_('The template to control the filename and directory structure of the saved files. ' help=_('The template to control the filename and directory structure of the saved files. '
'Default is "%s" which will save books into a per-author ' 'Default is "%(templ)s" which will save books into a per-author '
'subdirectory with filenames containing title and author. ' 'subdirectory with filenames containing title and author. '
'Available controls are: {%s}')%(DEFAULT_TEMPLATE, ', '.join(FORMAT_ARGS))) 'Available controls are: {%(controls)s}')%dict(
templ=DEFAULT_TEMPLATE, controls=', '.join(FORMAT_ARGS)))
x('send_template', default=DEFAULT_SEND_TEMPLATE, x('send_template', default=DEFAULT_SEND_TEMPLATE,
help=_('The template to control the filename and directory structure of files ' help=_('The template to control the filename and directory structure of files '
'sent to the device. ' 'sent to the device. '
'Default is "%s" which will save books into a per-author ' 'Default is "%(templ)s" which will save books into a per-author '
'directory with filenames containing title and author. ' 'directory with filenames containing title and author. '
'Available controls are: {%s}')%(DEFAULT_SEND_TEMPLATE, ', '.join(FORMAT_ARGS))) 'Available controls are: {%(controls)s}')%dict(
templ=DEFAULT_SEND_TEMPLATE, controls=', '.join(FORMAT_ARGS)))
x('asciiize', default=True, x('asciiize', default=True,
help=_('Normally, calibre will convert all non English characters into English equivalents ' help=_('Normally, calibre will convert all non English characters into English equivalents '
'for the file names. ' 'for the file names. '

View File

@ -124,7 +124,8 @@ def render_rating(rating, url_prefix, container='span', prefix=None): # {{{
added = 0 added = 0
if prefix is None: if prefix is None:
prefix = _('Average rating') prefix = _('Average rating')
rstring = xml(_('%s: %.1f stars')% (prefix, rating if rating else 0.0), rstring = xml(_('%(prefix)s: %(rating).1f stars')%dict(
prefix=prefix, rating=rating if rating else 0.0),
True) True)
ans = ['<%s class="rating">' % (container)] ans = ['<%s class="rating">' % (container)]
for i in range(5): for i in range(5):

View File

@ -171,9 +171,9 @@ def ACQUISITION_ENTRY(item, version, db, updated, CFM, CKEYS, prefix):
no_tag_count=True))) no_tag_count=True)))
series = item[FM['series']] series = item[FM['series']]
if series: if series:
extra.append(_('SERIES: %s [%s]<br />')%\ extra.append(_('SERIES: %(series)s [%(sidx)s]<br />')%\
(xml(series), dict(series=xml(series),
fmt_sidx(float(item[FM['series_index']])))) sidx=fmt_sidx(float(item[FM['series_index']]))))
for key in CKEYS: for key in CKEYS:
mi = db.get_metadata(item[CFM['id']['rec_index']], index_is_id=True) mi = db.get_metadata(item[CFM['id']['rec_index']], index_is_id=True)
name, val = mi.format_field(key) name, val = mi.format_field(key)

View File

@ -96,8 +96,8 @@ html_sidebars = {
html_favicon = 'favicon.ico' html_favicon = 'favicon.ico'
# Add any paths that contain custom static files (such as style sheets) here, # Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files, # relative to this directory. They are copied after the built-in static files,
# so a file named "default.css" will overwrite the builtin "default.css". # so a file named "default.css" will overwrite the built-in "default.css".
html_static_path = ['resources', '../../../icons/favicon.ico'] html_static_path = ['resources', '../../../icons/favicon.ico']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,

View File

@ -2,16 +2,16 @@
.. _conversion: .. _conversion:
E-book Conversion Ebook Conversion
=================== ===================
|app| has a conversion system that is designed to be very easy to use. Normally, you just |app| has a conversion system that is designed to be very easy to use. Normally, you just
add a book to |app|, click convert and |app| will try hard to generate output that is as add a book to |app|, click convert and |app| will try hard to generate output that is as
close as possible to the input. However, |app| accepts a very large number of input formats, close as possible to the input. However, |app| accepts a very large number of input formats,
not all of which are as suitable as others for conversion to e-books. In the case of not all of which are as suitable as others for conversion to ebooks. In the case of
such input formats, or if you just want greater control over the conversion system, such input formats, or if you just want greater control over the conversion system,
|app| has a lot of options to fine tune the conversion process. Note however that |app|'s |app| has a lot of options to fine tune the conversion process. Note however that |app|'s
conversion system is not a substitute for a full blown e-book editor. To edit e-books, I conversion system is not a substitute for a full blown ebook editor. To edit ebooks, I
would recommend first converting them to EPUB using |app| and then using a dedicated EPUB editor, would recommend first converting them to EPUB using |app| and then using a dedicated EPUB editor,
like `Sigil <http://code.google.com/p/sigil/>`_ to get the book into perfect shape. You can then like `Sigil <http://code.google.com/p/sigil/>`_ to get the book into perfect shape. You can then
use the edited EPUB as input for conversion into other formats in |app|. use the edited EPUB as input for conversion into other formats in |app|.
@ -23,7 +23,7 @@ mouse over it, a tooltip will appear describing the setting.
.. image:: images/conv_dialog.png .. image:: images/conv_dialog.png
:align: center :align: center
:alt: E-book conversion dialog :alt: Ebook conversion dialog
:scale: 50 :scale: 50
.. contents:: Contents .. contents:: Contents
@ -75,7 +75,7 @@ The four sub-directories are:
input This contains the HTML output by the Input Plugin. Use this to debug the Input Plugin. input This contains the HTML output by the Input Plugin. Use this to debug the Input Plugin.
parsed The result of pre-processing and converting to XHTML the output from the Input Plugin. Use to debug structure detection. parsed The result of pre-processing and converting to XHTML the output from the Input Plugin. Use to debug structure detection.
structure Post structure detection, but before CSS flattening and font size conversion. Use to debug font size conversion and CSS transforms. structure Post structure detection, but before CSS flattening and font size conversion. Use to debug font size conversion and CSS transforms.
processed Just before the e-book is passed to the output plugin. Use to debug the Output Plugin. processed Just before the ebook is passed to the output plugin. Use to debug the Output Plugin.
========== ============= ========== =============
If you want to edit the input document a little before having |app| convert it, the best thing to If you want to edit the input document a little before having |app| convert it, the best thing to
@ -94,7 +94,7 @@ Look & Feel
:depth: 1 :depth: 1
:local: :local:
This group of options controls various aspects of the look and feel of the converted e-book. This group of options controls various aspects of the look and feel of the converted ebook.
.. _font-size-rescaling: .. _font-size-rescaling:
@ -209,7 +209,7 @@ Miscellaneous
There are a few more options in this section. There are a few more options in this section.
:guilabel:`No text justification` :guilabel:`No text justification`
Normally, if the output format supports it, |app| will force the output e-book Normally, if the output format supports it, |app| will force the output ebook
to have *justified* text (i.e., a smooth right margin). This option will turn to have *justified* text (i.e., a smooth right margin). This option will turn
off this behavior, in which case whatever justification is specified in the input document off this behavior, in which case whatever justification is specified in the input document
will be used instead. will be used instead.
@ -227,7 +227,7 @@ There are a few more options in this section.
with "Mikhail Gorbachiov". Also, note that in cases where there are multiple representations with "Mikhail Gorbachiov". Also, note that in cases where there are multiple representations
of a character (characters shared by Chinese and Japanese for instance) the representation used of a character (characters shared by Chinese and Japanese for instance) the representation used
by the largest number of people will be used (Chinese in the previous example). by the largest number of people will be used (Chinese in the previous example).
This option is mainly useful if you are going to view the e-book on a device that does not This option is mainly useful if you are going to view the ebook on a device that does not
have support for unicode. have support for unicode.
:guilabel:`Input character encoding` :guilabel:`Input character encoding`
@ -416,7 +416,7 @@ There are a few more options in this section.
:guilabel:`Insert metadata as page at start of book` :guilabel:`Insert metadata as page at start of book`
One of the great things about |app| is that it allows you to maintain very complete metadata One of the great things about |app| is that it allows you to maintain very complete metadata
about all of your books, for example, a rating, tags, comments, etc. This option will create about all of your books, for example, a rating, tags, comments, etc. This option will create
a single page with all this metadata and insert it into the converted e-book, typically just a single page with all this metadata and insert it into the converted ebook, typically just
after the cover. Think of it as a way to create your own customised book jacket. after the cover. Think of it as a way to create your own customised book jacket.
:guilabel:`Remove first image` :guilabel:`Remove first image`
@ -432,7 +432,7 @@ Table of Contents
When the input document has a Table of Contents in its metadata, |app| will just use that. However, When the input document has a Table of Contents in its metadata, |app| will just use that. However,
a number of older formats either do not support a metadata based Table of Contents, or individual a number of older formats either do not support a metadata based Table of Contents, or individual
documents do not have one. In these cases, the options in this section can help you automatically documents do not have one. In these cases, the options in this section can help you automatically
generate a Table of Contents in the converted e-book, based on the actual content in the input document. generate a Table of Contents in the converted ebook, based on the actual content in the input document.
The first option is :guilabel:`Force use of auto-generated Table of Contents`. By checking this option The first option is :guilabel:`Force use of auto-generated Table of Contents`. By checking this option
you can have |app| override any Table of Contents found in the metadata of the input document with the you can have |app| override any Table of Contents found in the metadata of the input document with the
@ -681,7 +681,7 @@ The .cbc file will then contain::
two.cbz two.cbz
three.cbz three.cbz
|app| will automatically convert this .cbc file into a e-book with a Table of Contents pointing to each entry in comics.txt. |app| will automatically convert this .cbc file into a ebook with a Table of Contents pointing to each entry in comics.txt.
EPUB advanced formatting demo EPUB advanced formatting demo
@ -695,5 +695,5 @@ EPUB from the ZIP file are::
ebook-convert demo.zip .epub -vv --authors "Kovid Goyal" --language en --level1-toc '//*[@class="title"]' --disable-font-rescaling --page-breaks-before / --no-default-epub-cover ebook-convert demo.zip .epub -vv --authors "Kovid Goyal" --language en --level1-toc '//*[@class="title"]' --disable-font-rescaling --page-breaks-before / --no-default-epub-cover
Note that because this file explores the potential of EPUB, most of the advanced formatting is not going to work on readers less capable than |app|'s builtin EPUB viewer. Note that because this file explores the potential of EPUB, most of the advanced formatting is not going to work on readers less capable than |app|'s built-in EPUB viewer.

View File

@ -6,7 +6,7 @@
Writing your own plugins to extend |app|'s functionality Writing your own plugins to extend |app|'s functionality
==================================================================== ====================================================================
|app| has a very modular design. Almost all functionality in |app| comes in the form of plugins. Plugins are used for conversion, for downloading news (though these are called recipes), for various components of the user interface, to connect to different devices, to process files when adding them to |app| and so on. You can get a complete list of all the builtin plugins in |app| by going to :guilabel:`Preferences->Plugins`. |app| has a very modular design. Almost all functionality in |app| comes in the form of plugins. Plugins are used for conversion, for downloading news (though these are called recipes), for various components of the user interface, to connect to different devices, to process files when adding them to |app| and so on. You can get a complete list of all the built-in plugins in |app| by going to :guilabel:`Preferences->Plugins`.
Here, we will teach you how to create your own plugins to add new features to |app|. Here, we will teach you how to create your own plugins to add new features to |app|.
@ -127,7 +127,7 @@ The actual logic to implement the Interface Plugin Demo dialog.
Getting resources from the plugin zip file Getting resources from the plugin zip file
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|app|'s plugin loading system defines a couple of builtin functions that allow you to conveniently get files from the plugin zip file. |app|'s plugin loading system defines a couple of built-in functions that allow you to conveniently get files from the plugin zip file.
**get_resources(name_or_list_of_names)** **get_resources(name_or_list_of_names)**
This function should be called with a list of paths to files inside the zip file. For example to access the file icon.png in This function should be called with a list of paths to files inside the zip file. For example to access the file icon.png in

View File

@ -53,15 +53,15 @@ from the calibre website it will be :file:`/opt/calibre/resources`. These paths
You should not change the files in this resources folder, as your changes will get overwritten the next time you update |app|. Instead, go to You should not change the files in this resources folder, as your changes will get overwritten the next time you update |app|. Instead, go to
:guilabel:`Preferences->Advanced->Miscellaneous` and click :guilabel:`Open calibre configuration directory`. In this configuration directory, create a sub-folder called resources and place the files you want to override in it. Place the files in the appropriate sub folders, for example place images in :file:`resources/images`, etc. :guilabel:`Preferences->Advanced->Miscellaneous` and click :guilabel:`Open calibre configuration directory`. In this configuration directory, create a sub-folder called resources and place the files you want to override in it. Place the files in the appropriate sub folders, for example place images in :file:`resources/images`, etc.
|app| will automatically use your custom file in preference to the builtin one the next time it is started. |app| will automatically use your custom file in preference to the built-in one the next time it is started.
For example, if you wanted to change the icon for the :guilabel:`Remove books` action, you would first look in the builtin resources folder and see that the relevant file is For example, if you wanted to change the icon for the :guilabel:`Remove books` action, you would first look in the built-in resources folder and see that the relevant file is
:file:`resources/images/trash.png`. Assuming you have an alternate icon in PNG format called :file:`mytrash.png` you would save it in the configuration directory as :file:`resources/images/trash.png`. All the icons used by the calibre user interface are in :file:`resources/images` and its sub-folders. :file:`resources/images/trash.png`. Assuming you have an alternate icon in PNG format called :file:`mytrash.png` you would save it in the configuration directory as :file:`resources/images/trash.png`. All the icons used by the calibre user interface are in :file:`resources/images` and its sub-folders.
Customizing |app| with plugins Customizing |app| with plugins
-------------------------------- --------------------------------
|app| has a very modular design. Almost all functionality in |app| comes in the form of plugins. Plugins are used for conversion, for downloading news (though these are called recipes), for various components of the user interface, to connect to different devices, to process files when adding them to |app| and so on. You can get a complete list of all the builtin plugins in |app| by going to :guilabel:`Preferences->Plugins`. |app| has a very modular design. Almost all functionality in |app| comes in the form of plugins. Plugins are used for conversion, for downloading news (though these are called recipes), for various components of the user interface, to connect to different devices, to process files when adding them to |app| and so on. You can get a complete list of all the built-in plugins in |app| by going to :guilabel:`Preferences->Plugins`.
You can write your own plugins to customize and extend the behavior of |app|. The plugin architecture in |app| is very simple, see the tutorial :ref:`pluginstutorial`. You can write your own plugins to customize and extend the behavior of |app|. The plugin architecture in |app| is very simple, see the tutorial :ref:`pluginstutorial`.

112
src/calibre/manual/develop.rst Normal file → Executable file
View File

@ -6,7 +6,7 @@ Setting up a |app| development environment
=========================================== ===========================================
|app| is completely open source, licensed under the `GNU GPL v3 <http://www.gnu.org/copyleft/gpl.html>`_. |app| is completely open source, licensed under the `GNU GPL v3 <http://www.gnu.org/copyleft/gpl.html>`_.
This means that you are free to download and modify the program to your hearts content. In this section, This means that you are free to download and modify the program to your heart's content. In this section,
you will learn how to get a |app| development environment set up on the operating system of your choice. you will learn how to get a |app| development environment set up on the operating system of your choice.
|app| is written primarily in `Python <http://www.python.org>`_ with some C/C++ code for speed and system interfacing. |app| is written primarily in `Python <http://www.python.org>`_ with some C/C++ code for speed and system interfacing.
Note that |app| is not compatible with Python 3 and requires at least Python 2.7. Note that |app| is not compatible with Python 3 and requires at least Python 2.7.
@ -18,7 +18,7 @@ Note that |app| is not compatible with Python 3 and requires at least Python 2.7
Design philosophy Design philosophy
------------------- -------------------
|app| has its roots in the Unix world, which means that it's design is highly modular. |app| has its roots in the Unix world, which means that its design is highly modular.
The modules interact with each other via well defined interfaces. This makes adding new features and fixing The modules interact with each other via well defined interfaces. This makes adding new features and fixing
bugs in |app| very easy, resulting in a frenetic pace of development. Because of its roots, |app| has a bugs in |app| very easy, resulting in a frenetic pace of development. Because of its roots, |app| has a
comprehensive command line interface for all its functions, documented in :ref:`cli`. comprehensive command line interface for all its functions, documented in :ref:`cli`.
@ -28,7 +28,7 @@ For example, adding support for a new device to |app| typically involves writing
a device driver plugin. You can browse the a device driver plugin. You can browse the
`built-in drivers <http://bazaar.launchpad.net/%7Ekovid/calibre/trunk/files/head%3A/src/calibre/devices/>`_. Similarly, adding support `built-in drivers <http://bazaar.launchpad.net/%7Ekovid/calibre/trunk/files/head%3A/src/calibre/devices/>`_. Similarly, adding support
for new conversion formats involves writing input/output format plugins. Another example of the modular design is the :ref:`recipe system <news>` for for new conversion formats involves writing input/output format plugins. Another example of the modular design is the :ref:`recipe system <news>` for
fetching news. For more examples of plugins designed to add features to |app|, see the `plugin index <http://www.mobileread.com/forums/showthread.php?t=118764>`_. fetching news. For more examples of plugins designed to add features to |app|, see the `plugin index <http://www.mobileread.com/forums/showthread.php?p=1362767#post1362767>`_.
Code layout Code layout
^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^
@ -38,15 +38,15 @@ All the |app| python code is in the ``calibre`` package. This package contains t
* devices - All the device drivers. Just look through some of the built-in drivers to get an idea for how they work. * devices - All the device drivers. Just look through some of the built-in drivers to get an idea for how they work.
* For details, see: devices.interface which defines the interface supported by device drivers and devices.usbms which * For details, see: devices.interface which defines the interface supported by device drivers and devices.usbms which
defines a generic driver that connects to a USBMS device. All USBMS based drivers in calibre inherit from it. defines a generic driver that connects to a USBMS device. All USBMS based drivers in |app| inherit from it.
* ebooks - All the ebook conversion/metadata code. A good starting point is ``calibre.ebooks.conversion.cli`` which is the * ebooks - All the ebook conversion/metadata code. A good starting point is ``calibre.ebooks.conversion.cli`` which is the
module powering the :command:`ebook-convert` command. The conversion process is controlled via conversion.plumber. module powering the :command:`ebook-convert` command. The conversion process is controlled via conversion.plumber.
The format independent code is all in ebooks.oeb and the format dependent stuff is in ebooks.format_name. The format independent code is all in ebooks.oeb and the format dependent code is in ebooks.format_name.
* Metadata reading writing and downloading is all in ebooks.metadata * Metadata reading, writing, and downloading is all in ebooks.metadata
* library - The database backed and the content server. See library.database2 for the interface to the calibre library. library.server is the calibre Content Server. * library - The database back-end and the content server. See library.database2 for the interface to the |app| library. library.server is the |app| Content Server.
* gui2 - The Graphical User Interface. GUI initialization happens in gui2.main and gui2.ui. The ebook-viewer is in gui2.viewer. * gui2 - The Graphical User Interface. GUI initialization happens in gui2.main and gui2.ui. The ebook-viewer is in gui2.viewer.
If you need help understanding the code, post in the `development forum <http://www.mobileread.com/forums/forumdisplay.php?f=240>`_ If you need help understanding the code, post in the `development forum <http://www.mobileread.com/forums/forumdisplay.php?f=240>`_
@ -55,8 +55,8 @@ and you will most likely get help from one of |app|'s many developers.
Getting the code Getting the code
------------------ ------------------
|app| uses `Bazaar <http://bazaar-vcs.org/>`_ a distributed version control system. Bazaar is available on all the platforms |app| supports. |app| uses `Bazaar <http://bazaar-vcs.org/>`_, a distributed version control system. Bazaar is available on all the platforms |app| supports.
After installing Bazaar, you can get the calibre source code with the command:: After installing Bazaar, you can get the |app| source code with the command::
bzr branch lp:calibre bzr branch lp:calibre
@ -68,7 +68,7 @@ to the latest code, use the command::
Submitting your changes to be included Submitting your changes to be included
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If you only plan to only make a few small changes, you can make your changes and create a If you only plan to make a few small changes, you can make your changes and create a
"merge directive" which you can then attach to a ticket in the |app| bug tracker for consideration. To do "merge directive" which you can then attach to a ticket in the |app| bug tracker for consideration. To do
this, make your changes, then run:: this, make your changes, then run::
@ -76,11 +76,11 @@ this, make your changes, then run::
bzr send -o my-changes bzr send -o my-changes
This will create a :file:`my-changes` file in the current directory, This will create a :file:`my-changes` file in the current directory,
simply attach that to a ticket on the |app| `bug tracker <http://calibre-ebook.com/bugs>`_. simply attach that to a ticket on the |app| `bug tracker <https://bugs.launchpad.net/calibre>`_.
If you plan to do a lot of development on |app|, then the best method is to create a If you plan to do a lot of development on |app|, then the best method is to create a
`Launchpad <http://launchpad.net>`_ account. Once you have the account, you can use it to register `Launchpad <http://launchpad.net>`_ account. Once you have an account, you can use it to register
your bzr branch created by the `bzr branch` command above with the |app| project. First run the your bzr branch created by the `bzr branch` command above. First run the
following command to tell bzr about your launchpad account:: following command to tell bzr about your launchpad account::
bzr launchpad-login your_launchpad_username bzr launchpad-login your_launchpad_username
@ -88,31 +88,31 @@ following command to tell bzr about your launchpad account::
Now, you have to setup SSH access to Launchpad. First create an SSH public/private keypair. Then upload Now, you have to setup SSH access to Launchpad. First create an SSH public/private keypair. Then upload
the public key to Launchpad by going to your Launchpad account page. Instructions for setting up the the public key to Launchpad by going to your Launchpad account page. Instructions for setting up the
private key in bzr are at http://bazaar-vcs.org/Bzr_and_SSH. Now you can upload your branch to the |app| private key in bzr are at http://bazaar-vcs.org/Bzr_and_SSH. Now you can upload your branch to the |app|
project in Launchapd by following the instructions at https://help.launchpad.net/Code/UploadingABranch. project in Launchpad by following the instructions at https://help.launchpad.net/Code/UploadingABranch.
Now whenever you commit changes to your branch with the command:: Whenever you commit changes to your branch with the command::
bzr commit -m "Comment describing your change" bzr commit -m "Comment describing your change"
I can merge it directly from you branch into the main |app| source tree. You should also keep an eye on the |app| Kovid can merge it directly from your branch into the main |app| source tree. You should also keep an eye on the |app|
`development forum <http://www.mobileread.com/forums/forumdisplay.php?f=240>`. Before making major changes, you should `development forum <http://www.mobileread.com/forums/forumdisplay.php?f=240>`. Before making major changes, you should
discuss them in the forum or contact Kovid directly (his email address is all over the source code). discuss them in the forum or contact Kovid directly (his email address is all over the source code).
Windows development environment Windows development environment
--------------------------------- ---------------------------------
Install |app| normally, using the windows installer. Then, open a Command Prompt and change to Install |app| normally, using the Windows installer. Then open a Command Prompt and change to
the previously checked out calibre code directory, for example:: the previously checked out |app| code directory. For example::
cd C:\Users\kovid\work\calibre cd C:\Users\kovid\work\calibre
calibre is the directory that contains the src and resources sub directories. calibre is the directory that contains the src and resources sub-directories.
The next step is to set the environment variable ``CALIBRE_DEVELOP_FROM`` to the absolute path to the src directory. The next step is to set the environment variable ``CALIBRE_DEVELOP_FROM`` to the absolute path of the src directory.
So, following the example above, it would be ``C:\Users\kovid\work\calibre\src``. A short So, following the example above, it would be ``C:\Users\kovid\work\calibre\src``. `Here is a short
`guide <http://docs.python.org/using/windows.html#excursus-setting-environment-variables>`_ to setting environment guide <http://docs.python.org/using/windows.html#excursus-setting-environment-variables>`_ to setting environment
variables on windows. variables on Windows.
Once you have set the environment variable, open a new Command Prompt and check that it was correctly set by using Once you have set the environment variable, open a new command prompt and check that it was correctly set by using
the command:: the command::
echo %CALIBRE_DEVELOP_FROM% echo %CALIBRE_DEVELOP_FROM%
@ -129,14 +129,14 @@ near the top of the file. Now run the command :command:`calibredb`. The very fir
OS X development environment OS X development environment
------------------------------ ------------------------------
Install |app| normally, using the provided .dmg. Then, open a Terminal and change to Install |app| normally using the provided .dmg. Then open a Terminal and change to
the previously checked out calibre code directory, for example:: the previously checked out |app| code directory, for example::
cd /Users/kovid/work/calibre cd /Users/kovid/work/calibre
calibre is the directory that contains the src and resources sub directories. Ensure you have installed the |app| commandline tools via :guilabel:Preferences->Advanced->Miscellaneous in the |app| GUI. calibre is the directory that contains the src and resources sub-directories. Ensure you have installed the |app| commandline tools via :guilabel:Preferences->Advanced->Miscellaneous in the |app| GUI.
The next step is to set the environment variable ``CALIBRE_DEVELOP_FROM`` to the absolute path to the src directory. The next step is to set the environment variable ``CALIBRE_DEVELOP_FROM`` to the absolute path of the src directory.
So, following the example above, it would be ``/Users/kovid/work/calibre/src``. Apple So, following the example above, it would be ``/Users/kovid/work/calibre/src``. Apple
`documentation <http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPRuntimeConfig/Articles/EnvironmentVars.html#//apple_ref/doc/uid/20002093-BCIJIJBH>`_ `documentation <http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPRuntimeConfig/Articles/EnvironmentVars.html#//apple_ref/doc/uid/20002093-BCIJIJBH>`_
on how to set environment variables. on how to set environment variables.
@ -158,21 +158,21 @@ near the top of the file. Now run the command :command:`calibredb`. The very fir
Linux development environment Linux development environment
------------------------------ ------------------------------
|app| is primarily developed on linux. You have two choices in setting up the development environment. You can install the |app| is primarily developed on Linux. You have two choices in setting up the development environment. You can install the
|app| binary as normal and use that as a runtime environment to do your development. This approach is similar to that |app| binary as normal and use that as a runtime environment to do your development. This approach is similar to that
used in windows and OS X. Alternatively, you can install |app| from source. Instructions for setting up a development used in Windows and OS X. Alternatively, you can install |app| from source. Instructions for setting up a development
environment from source are in the INSTALL file in the source tree. Here we will address using the binary a runtime, which is the environment from source are in the INSTALL file in the source tree. Here we will address using the binary at runtime, which is the
recommended method. recommended method.
Install the |app| using the binary installer. Then open a terminal and change to the previously checked out |app| code directory, for example:: Install the |app| using the binary installer. Then open a terminal and change to the previously checked out |app| code directory, for example::
cd /home/kovid/work/calibre cd /home/kovid/work/calibre
calibre is the directory that contains the src and resources sub directories. calibre is the directory that contains the src and resources sub-directories.
The next step is to set the environment variable ``CALIBRE_DEVELOP_FROM`` to the absolute path to the src directory. The next step is to set the environment variable ``CALIBRE_DEVELOP_FROM`` to the absolute path of the src directory.
So, following the example above, it would be ``/home/kovid/work/calibre/src``. How to set environment variables depends on So, following the example above, it would be ``/home/kovid/work/calibre/src``. How to set environment variables depends on
your linux distribution and what shell you are using. your Linux distribution and what shell you are using.
Once you have set the environment variable, open a new terminal and check that it was correctly set by using Once you have set the environment variable, open a new terminal and check that it was correctly set by using
the command:: the command::
@ -191,10 +191,10 @@ near the top of the file. Now run the command :command:`calibredb`. The very fir
Having separate "normal" and "development" |app| installs on the same computer Having separate "normal" and "development" |app| installs on the same computer
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
The calibre source tree is very stable, it rarely breaks, but if you feel the need to run from source on a separate The |app| source tree is very stable and rarely breaks, but if you feel the need to run from source on a separate
test library and run the released calibre version with your everyday library, you can achieve this easily using test library and run the released |app| version with your everyday library, you can achieve this easily using
.bat files or shell scripts to launch |app|. The example below shows how to do this on windows using .bat files (the .bat files or shell scripts to launch |app|. The example below shows how to do this on Windows using .bat files (the
instructions for other platforms are the same, just use a BASH script instead of a .bat file) instructions for other platforms are the same, just use a shell script instead of a .bat file)
To launch the release version of |app| with your everyday library: To launch the release version of |app| with your everyday library:
@ -211,9 +211,9 @@ calibre-dev.bat::
Debugging tips Debugging tips
---------------- ----------------
Running calibre code in a python debugger is not easy, unless you install from source on linux. However, python is a Running |app| code in a python debugger is not easy unless you install from source on Linux. However, Python is a
dynamically typed language with excellent facilities for introspection. I wrote the core calibre code without once dynamically typed language with excellent facilities for introspection. Kovid wrote the core |app| code without once
using a debugger. There are two main strategies to debug calibre code: using a debugger. There are two main strategies to debug |app| code:
Using an interactive python interpreter Using an interactive python interpreter
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -223,15 +223,15 @@ You can insert the following two lines of code to start an interactive python se
from calibre import ipython from calibre import ipython
ipython(locals()) ipython(locals())
When running from the command line, this will start an interactive python interpreter with access to all When running from the command line, this will start an interactive Python interpreter with access to all
locally defined variables (variables in the local scope). The interactive prompt even has TAB completion locally defined variables (variables in the local scope). The interactive prompt even has TAB completion
for object properties and you can use the various python facilities for introspection, such as for object properties and you can use the various Python facilities for introspection, such as
:func:`dir`, :func:`type`, :func:`repr`, etc. :func:`dir`, :func:`type`, :func:`repr`, etc.
Using print statements Using print statements
^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
This is my favorite way to debug. Simply insert print statements at points of interest and run your program in the This is Kovid's favorite way to debug. Simply insert print statements at points of interest and run your program in the
terminal. For example, you can start the GUI from the terminal as:: terminal. For example, you can start the GUI from the terminal as::
calibre-debug -g calibre-debug -g
@ -240,11 +240,11 @@ Similarly, you can start the ebook-viewer as::
calibre-debug -w /path/to/file/to/be/viewed calibre-debug -w /path/to/file/to/be/viewed
Executing arbitrary scripts in the calibre python environment Executing arbitrary scripts in the |app| python environment
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The :command:`calibre-debug` command provides a couple of handy switches to execute your own The :command:`calibre-debug` command provides a couple of handy switches to execute your own
code, with access to the calibre modules:: code, with access to the |app| modules::
calibre-debug -c "some python code" calibre-debug -c "some python code"
@ -252,33 +252,33 @@ is great for testing a little snippet of code on the command line. It works in t
calibre-debug -e myscript.py calibre-debug -e myscript.py
can be used to execute your own python script. It works in the same way as passing the script to the python interpreter, except can be used to execute your own Python script. It works in the same way as passing the script to the Python interpreter, except
that the calibre environment is fully initialized, so you can use all the calibre code in your script. that the calibre environment is fully initialized, so you can use all the calibre code in your script.
Using calibre in your projects Using |app| in your projects
---------------------------------------- ----------------------------------------
It is possible to directly use calibre functions/code in your python project. Two ways exist to do this: It is possible to directly use |app| functions/code in your Python project. Two ways exist to do this:
Binary install of calibre Binary install of |app|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If you have a binary install of calibre, you can use the python interpreter bundled with calibre, like this:: If you have a binary install of |app|, you can use the Python interpreter bundled with |app|, like this::
calibre-debug -e /path/to/your/python/script.py calibre-debug -e /path/to/your/python/script.py
Source install on linux Source install on Linux
^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^
In addition to using the above technique, if you do a source install on linux, In addition to using the above technique, if you do a source install on Linux,
you can also directly import calibre, as follows:: you can also directly import |app|, as follows::
import init_calibre import init_calibre
import calibre import calibre
print calibre.__version__ print calibre.__version__
It is essential that you import the init_calibre module before any other calibre modules/packages as It is essential that you import the init_calibre module before any other |app| modules/packages as
it sets up the interpreter to run calibre code. it sets up the interpreter to run |app| code.

View File

@ -9,7 +9,7 @@ Frequently Asked Questions
:depth: 1 :depth: 1
:local: :local:
E-book Format Conversion Ebook Format Conversion
------------------------- -------------------------
.. contents:: Contents .. contents:: Contents
:depth: 1 :depth: 1
@ -112,8 +112,8 @@ How can I help get my device supported in |app|?
If your device appears as a USB disk to the operating system, adding support for it to |app| is very easy. If your device appears as a USB disk to the operating system, adding support for it to |app| is very easy.
We just need some information from you: We just need some information from you:
* What e-book formats does your device support? * What ebook formats does your device support?
* Is there a special directory on the device in which all e-book files should be placed? * Is there a special directory on the device in which all ebook files should be placed?
* We also need information about your device that |app| will collect automatically. First, if your * We also need information about your device that |app| will collect automatically. First, if your
device supports SD cards, insert them. Then connect your device. In calibre go to :guilabel:`Preferences->Advanced->Miscellaneous` device supports SD cards, insert them. Then connect your device. In calibre go to :guilabel:`Preferences->Advanced->Miscellaneous`
and click the "Debug device detection" button. This will create some debug output. Copy it to a file and click the "Debug device detection" button. This will create some debug output. Copy it to a file
@ -259,7 +259,7 @@ This method only works on Windows XP and higher, and OS X 10.5 and higher. Linux
How do I use |app| with my Android phone? How do I use |app| with my Android phone?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
First install the WordPlayer e-book reading app from the Android Marketplace onto you phone. Then simply plug your phone into the computer with a USB cable. |app| should automatically detect the phone and then you can transfer books to it by clicking the Send to Device button. |app| does not have support for every single androind device out there, so if you would like to have support for your device added, follow the instructions above for getting your device supported in |app|. First install the WordPlayer ebook reading app from the Android Marketplace onto you phone. Then simply plug your phone into the computer with a USB cable. |app| should automatically detect the phone and then you can transfer books to it by clicking the Send to Device button. |app| does not have support for every single androind device out there, so if you would like to have support for your device added, follow the instructions above for getting your device supported in |app|.
Can I access my |app| books using the web browser in my Kindle or other reading device? Can I access my |app| books using the web browser in my Kindle or other reading device?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -442,16 +442,16 @@ Miscellaneous
Why the name calibre? Why the name calibre?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Take your pick: Take your pick:
* Convertor And LIBRary for E-books * Convertor And LIBRary for Ebooks
* A high *calibre* product * A high *calibre* product
* A tribute to the SONY Librie which was the first e-ink based e-book reader * A tribute to the SONY Librie which was the first e-ink based ebook reader
* My wife chose it ;-) * My wife chose it ;-)
|app| is pronounced as cal-i-ber *not* ca-li-bre. If you're wondering, |app| is the British/commonwealth spelling for caliber. Being Indian, that's the natural spelling for me. |app| is pronounced as cal-i-ber *not* ca-li-bre. If you're wondering, |app| is the British/commonwealth spelling for caliber. Being Indian, that's the natural spelling for me.
Why does |app| show only some of my fonts on OS X? Why does |app| show only some of my fonts on OS X?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|app| embeds fonts in ebook files it creates. E-book files support embedding only TrueType (.ttf) fonts. Most fonts on OS X systems are in .dfont format, thus they cannot be embedded. |app| shows only TrueType fonts found on your system. You can obtain many TrueType fonts on the web. Simply download the .ttf files and add them to the Library/Fonts directory in your home directory. |app| embeds fonts in ebook files it creates. Ebook files support embedding only TrueType (.ttf) fonts. Most fonts on OS X systems are in .dfont format, thus they cannot be embedded. |app| shows only TrueType fonts found on your system. You can obtain many TrueType fonts on the web. Simply download the .ttf files and add them to the Library/Fonts directory in your home directory.
|app| is not starting on Windows? |app| is not starting on Windows?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -462,7 +462,7 @@ There can be several causes for this:
* Uninstall calibre * Uninstall calibre
* Reboot your computer * Reboot your computer
* Re-install calibre. But do not start calibre from the installation wizard. * Re-install calibre. But do not start calibre from the installation wizard.
* Temporarily disable your antivirus program (disconnect from the internet before doing so, to be safe) * Temporarily disable your antivirus program (disconnect from the Internet before doing so, to be safe)
* Look inside the folder you chose for your calibre library. If you see a file named metadata.db, delete it. * Look inside the folder you chose for your calibre library. If you see a file named metadata.db, delete it.
* Start calibre * Start calibre
* From now on you should be able to start calibre normally. * From now on you should be able to start calibre normally.
@ -515,7 +515,7 @@ menu, choose "Validate fonts".
I downloaded the installer, but it is not working? I downloaded the installer, but it is not working?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Downloading from the internet can sometimes result in a corrupted download. If the |app| installer you downloaded is not opening, try downloading it again. If re-downloading it does not work, download it from `an alternate location <http://sourceforge.net/projects/calibre/files/>`_. If the installer still doesn't work, then something on your computer is preventing it from running. Downloading from the Internet can sometimes result in a corrupted download. If the |app| installer you downloaded is not opening, try downloading it again. If re-downloading it does not work, download it from `an alternate location <http://sourceforge.net/projects/calibre/files/>`_. If the installer still doesn't work, then something on your computer is preventing it from running.
* Try temporarily disabling your antivirus program (Microsoft Security Essentials, or Kaspersky or Norton or McAfee or whatever). This is most likely the culprit if the upgrade process is hanging in the middle. * Try temporarily disabling your antivirus program (Microsoft Security Essentials, or Kaspersky or Norton or McAfee or whatever). This is most likely the culprit if the upgrade process is hanging in the middle.
* Try rebooting your computer and running a registry cleaner like `Wise registry cleaner <http://www.wisecleaner.com>`_. * Try rebooting your computer and running a registry cleaner like `Wise registry cleaner <http://www.wisecleaner.com>`_.
@ -539,7 +539,7 @@ If you want to backup the |app| configuration/plugins, you have to backup the co
How do I use purchased EPUB books with |app|? How do I use purchased EPUB books with |app|?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Most purchased EPUB books have `DRM <http://wiki.mobileread.com/wiki/DRM>`_. This prevents |app| from opening them. You can still use |app| to store and transfer them to your e-book reader. First, you must authorize your reader on a windows machine with Adobe Digital Editions. Once this is done, EPUB books transferred with |app| will work fine on your reader. When you purchase an epub book from a website, you will get an ".acsm" file. This file should be opened with Adobe Digital Editions, which will then download the actual ".epub" e-book. The e-book file will be stored in the folder "My Digital Editions", from where you can add it to |app|. Most purchased EPUB books have `DRM <http://drmfree.calibre-ebook.com/about#drm>`_. This prevents |app| from opening them. You can still use |app| to store and transfer them to your ebook reader. First, you must authorize your reader on a windows machine with Adobe Digital Editions. Once this is done, EPUB books transferred with |app| will work fine on your reader. When you purchase an epub book from a website, you will get an ".acsm" file. This file should be opened with Adobe Digital Editions, which will then download the actual ".epub" ebook. The ebook file will be stored in the folder "My Digital Editions", from where you can add it to |app|.
I am getting a "Permission Denied" error? I am getting a "Permission Denied" error?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

12
src/calibre/manual/glossary.rst Normal file → Executable file
View File

@ -6,25 +6,25 @@ Glossary
.. glossary:: .. glossary::
RSS RSS
**RSS** *(Really Simple Syndication)* is a web feed format that is used to publish frequently updated content, like news articles, blog posts, etc. It is a format that is particularly suited to being read by computers, and is therefore the preferred way of getting content from the web into an e-book. There are many other feed formats in use on the internet, and |app| understands most of them. In particular, it has good support for the *ATOM* format, which is commonly used for blogs. **RSS** *(Really Simple Syndication)* is a web feed format that is used to publish frequently updated content, like news articles, blog posts, etc. It is a format that is particularly suited to being read by computers, and is therefore the preferred way of getting content from the web into an ebook. There are many other feed formats in use on the Internet, and |app| understands most of them. In particular, it has good support for the *ATOM* format, which is commonly used for blogs.
recipe recipe
A recipe is a set of instructions that teach |app| how to convert an online news source, like a magazine or a blog into an e-book. A recipe, is essentially `python <http://www.python.org>`_ code. As such, it is capable of converting arbitrarily complex news sources into e-books. At the simplest level, it is just a set of variables such as URLs that give |app| enough information to go out onto the internet and download the news. A recipe is a set of instructions that teach |app| how to convert an online news source, such as a magazine or a blog, into an ebook. A recipe is essentially `Python <http://www.python.org>`_ code. As such, it is capable of converting arbitrarily complex news sources into ebooks. At the simplest level, it is just a set of variables, such as URLs, that give |app| enough information to go out onto the Internet and download the news.
HTML HTML
**HTML** *(Hyper Text Mark-Up Language)*, a subset of Standard Generalized Mark-Up Language (SGML) for electronic publishing, the specific standard used for the World Wide Web. **HTML** *(Hyper Text Mark-Up Language)*, a subset of Standard Generalized Mark-Up Language (SGML) for electronic publishing, is the specific standard used for the World Wide Web.
CSS CSS
**CSS** *(Cascading Style Sheets)* a language used to describe how an :term:`HTML` document should be rendered (visual styling). **CSS** *(Cascading Style Sheets)* is a language used to describe how an :term:`HTML` document should be rendered (visual styling).
API API
**API** *(Application Programming Interface)* is a source code interface that a library provides to support requests for services to be made of it by computer programs. **API** *(Application Programming Interface)* is a source code interface that a library provides to support requests for services to be made of it by computer programs.
LRF LRF
**LRF** The e-book format that is read by the SONY e-book readers. **LRF** The ebook format that is read by the SONY ebook readers.
URL URL
**URL** *(Uniform Resource Locator)* for example: ``http://example.com`` **URL** *(Uniform Resource Locator)* for example: ``http://example.com``
regexp regexp
**Regular expressions** provide a concise and flexible means for identifying strings of text of interest, such as particular characters, words, or patterns of characters. See `regexp syntax <http://docs.python.org/lib/re-syntax.html>`_ for the syntax of regular expressions used in python. **Regular expressions** provide a concise and flexible means for identifying strings of text of interest, such as particular characters, words, or patterns of characters. See `regexp syntax <http://docs.python.org/lib/re-syntax.html>`_ for the syntax of regular expressions used in Python.

208
src/calibre/manual/gui.rst Normal file → Executable file
View File

@ -9,10 +9,11 @@ The Graphical User Interface *(GUI)* provides access to all
library management and ebook format conversion features. The basic workflow library management and ebook format conversion features. The basic workflow
for using |app| is to first add books to the library from your hard disk. for using |app| is to first add books to the library from your hard disk.
|app| will automatically try to read metadata from the books and add them |app| will automatically try to read metadata from the books and add them
to its internal database. Once they are in the database, you can perform a various to its internal database. Once they are in the database, you can perform various
:ref:`actions` on them that include conversion from one format to another, :ref:`actions` on them that include conversion from one format to another,
transfer to the reading device, viewing on your computer, editing metadata, including covers, etc. transfer to the reading device, viewing on your computer, and editing metadata.
Note that |app| creates copies of the files you add to it, your original files are left untouched. The latter includes modifying the cover, description, and tags among other details.
Note that |app| creates copies of the files you add to it. Your original files are left untouched.
The interface is divided into various sections: The interface is divided into various sections:
@ -42,25 +43,25 @@ Add books
.. |adbi| image:: images/add_books.png .. |adbi| image:: images/add_books.png
:class: float-right-img :class: float-right-img
|adbi| The :guilabel:`Add books` action has five variations, accessed by the clicking the down arrow on the right side of the button. |adbi| The :guilabel:`Add books` action has six variations accessed by the clicking the down arrow on the right side of the button.
1. **Add books from a single directory**: Opens a file chooser dialog and allows you to specify which books in a directory should be added. This action is *context sensitive*, i.e. it depends on which :ref:`catalog <catalogs>` you have selected. If you have selected the :guilabel:`Library`, books will be added to the library. If you have selected the ebook reader device, the books will be uploaded to the device, and so on. 1. **Add books from a single directory**: Opens a file chooser dialog and allows you to specify which books in a directory should be added. This action is *context sensitive*, i.e. it depends on which :ref:`catalog <catalogs>` you have selected. If you have selected the :guilabel:`Library`, books will be added to the library. If you have selected the ebook reader device, the books will be uploaded to the device, and so on.
2. **Add books from directories, including sub-directories (One book per directory, assumes every ebook file is the same book in a different format)**: Allows you to choose a directory. The directory and all its sub-directories are scanned recursively and any ebooks found are added to the library. The algorithm assumes that each directory contains a single book. All ebook files in a directory are assumedto be the same book in different formats. This action is the inverse of the :ref:`Save to disk <save_to_disk_multiple>` action, i.e. you can :guilabel:`Save to disk`, delete the books and re-add them with no lost information (except date). 2. **Add books from directories, including sub-directories (One book per directory, assumes every ebook file is the same book in a different format)**: Allows you to choose a directory. The directory and all its sub-directories are scanned recursively, and any ebooks found are added to the library. |app| assumes that each directory contains a single book. All ebook files in a directory are assumed to be the same book in different formats. This action is the inverse of the :ref:`Save to disk <save_to_disk_multiple>` action, i.e. you can :guilabel:`Save to disk`, delete the books and re-add them with no lost information except for the date.
3. **Add books directories, including sub-directories (Multiple books per directory, assumes every ebook file is a different book)**: Allows you to choose a directory. The directory and all its sub-directories are scanned recursively and any ebooks found are added to the library. The algorithm assumes that each directory contains many books. All ebook files with the same name in a directory are assumed to be the same book in different formats. Ebooks with different names are added as different books. This action is the inverse of the :ref:`Save to disk <save_to_disk_single>` action, i.e. you can :guilabel:`Save to disk`, delete the books and re-add them with no lost information (except date). 3. **Add books from directories, including sub-directories (Multiple books per directory, assumes every ebook file is a different book)**: Allows you to choose a directory. The directory and all its sub-directories are scanned recursively and any ebooks found are added to the library. |app| assumes that each directory contains many books. All ebook files with the same name in a directory are assumed to be the same book in different formats. Ebooks with different names are added as different books. This action is the inverse of the :ref:`Save to disk <save_to_disk_single>` action, i.e. you can :guilabel:`Save to disk`, delete the books and re-add them with no lost information except for the date.
4. **Add empty book. (Book Entry with blank formats)**: Allows you to create a blank book record. This can be used to then manually fill out the information about a book that you may not have yet in your collection. 4. **Add empty book. (Book Entry with no formats)**: Allows you to create a blank book record. This can be used to then manually fill out the information about a book that you may not have yet in your collection.
5. **Add by ISBN**: Allows you to add one or more books by entering just their ISBN into a list or pasting the list of ISBNs from your clipboard. 5. **Add from ISBN**: Allows you to add one or more books by entering their ISBNs.
6. **Add files to selected book records**: Allows you to add or update the files associated with an existing book in your library. 6. **Add files to selected book records**: Allows you to add or update the files associated with an existing book in your library.
The :guilabel:`Add books` action can read metadata from a wide variety of e-book formats. In addition it tries to guess metadata from the filename. The :guilabel:`Add books` action can read metadata from a wide variety of ebook formats. In addition, it tries to guess metadata from the filename.
See the :ref:`config_filename_metadata` section, to learn how to configure this. See the :ref:`config_filename_metadata` section, to learn how to configure this.
To add an additional format for an existing book, use the :ref:`edit_meta_information` action. To add an additional format for an existing book use the :ref:`edit_meta_information` action.
.. _edit_meta_information: .. _edit_meta_information:
@ -69,41 +70,41 @@ Edit metadata
.. |emii| image:: images/edit_meta_information.png .. |emii| image:: images/edit_meta_information.png
:class: float-right-img :class: float-right-img
|emii| The :guilabel:`Edit metadata` action has six variations, which can be accessed by clicking the down arrow on the right side of the button. |emii| The :guilabel:`Edit metadata` action has four variations which can be accessed by clicking the down arrow on the right side of the button.
1. **Edit metadata individually**: This allows you to edit the metadata of books one-by-one, with the option of fetching metadata, including covers from the internet. It also allows you to add/remove particular ebook formats from a book. 1. **Edit metadata individually**: Allows you to edit the metadata of books one-by-one with the option of fetching metadata, including covers, from the Internet. It also allows you to add or remove particular ebook formats from a book.
2. **Edit metadata in bulk**: This allows you to edit common metadata fields for large numbers of books simulataneously. It operates on all the books you have selected in the :ref:`Library view <search_sort>`. 2. **Edit metadata in bulk**: Allows you to edit common metadata fields for large numbers of books simulataneously. It operates on all the books you have selected in the :ref:`Library view <search_sort>`.
3. **Download metadata and covers**: Downloads metadata and covers (if available), for the books that are selected in the book list. 3. **Download metadata and covers**: Downloads metadata and covers (if available) for the books that are selected in the book list.
4. **Merge Book Records**: Gives you the capability of merging the metadata and formats of two or more book records together. You can choose to either delete or keep the records that were not clicked first. 4. **Merge book records**: Gives you the capability of merging the metadata and formats of two or more book records. You can choose to either delete or keep the records that were not clicked first.
For more details see :ref:`metadata`. For more details see :ref:`metadata`.
.. _convert_ebooks: .. _convert_ebooks:
Convert e-books Convert books
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
.. |cei| image:: images/convert_ebooks.png .. |cei| image:: images/convert_ebooks.png
:class: float-right-img :class: float-right-img
|cei| Ebooks can be converted from a number of formats into whatever format your e-book reader prefers. |cei| Ebooks can be converted from a number of formats into whatever format your ebook reader prefers.
Note that ebooks you purchase will typically have `Digital Rights Management <http://bugs.calibre-ebook.com/wiki/DRM>`_ *(DRM)*. Many ebooks available for purchase will be protected by `Digital Rights Management <http://drmfree.calibre-ebook.com/about#drm>`_ *(DRM)* technology.
|app| will not convert these ebooks. For many DRM formats, it is easy to remove the DRM, but as this may be illegal, |app| will not convert these ebooks. It is easy to remove the DRM from many formats, but as this may be illegal,
you have to find tools to liberate your books yourself and then use |app| to convert them. you will have to find tools to liberate your books yourself and then use |app| to convert them.
For most people, conversion should be a simple 1-click affair. But if you want to learn more about the conversion process, see :ref:`conversion`. For most people, conversion should be a simple one-click affair. If you want to learn more about the conversion process, see :ref:`conversion`.
The :guilabel:`Convert E-books` action has three variations, accessed by the arrow next to the button. The :guilabel:`Convert books` action has three variations, accessed by the arrow next to the button.
1. **Convert individually**: This will allow you to specify conversion options to customize the conversion of each selected ebook. 1. **Convert individually**: Allows you to specify conversion options to customize the conversion of each selected ebook.
2. **Bulk convert**: This allows you to specify options only once to convert a number of ebooks in bulk. 2. **Bulk convert**: Allows you to specify options only once to convert a number of ebooks in bulk.
3. **Create catalog**: This action allows you to generate a complete listing with all metadata of the books in your library, 3. **Create a catalog of the books in your calibre library**: Allows you to generate a complete listing of the books in your library, including all metadata,
in several formats, like XML, CSV, BiBTeX, EPUB and MOBI. The catalog will contain all the books showing in the library view currently, in several formats such as XML, CSV, BiBTeX, EPUB and MOBI. The catalog will contain all the books currently showing in the library view.
so you can use the search features to limit the books to be catalogued. In addition, if you select multiple books using the mouse, This allows you to use the search features to limit the books to be catalogued. In addition, if you select multiple books using the mouse,
only those books will be added to the catalog. If you generate the catalog in an e-book format such as EPUB or MOBI, only those books will be added to the catalog. If you generate the catalog in an ebook format such as EPUB or MOBI,
the next time you connect your e-book reader, the catalog will be automatically sent to the device. the next time you connect your ebook reader the catalog will be automatically sent to the device.
For details on how catalogs work, see `here <http://www.mobileread.com/forums/showthread.php?p=755468#post755468>`. For more information on how catalogs work, read the `catalog creation tutorial <http://www.mobileread.com/forums/showthread.php?p=755468#post755468>`_at MobileRead.
.. _view: .. _view:
@ -112,7 +113,7 @@ View
.. |vi| image:: images/view.png .. |vi| image:: images/view.png
:class: float-right-img :class: float-right-img
|vi| The :guilabel:`View` action displays the book in an ebook viewer program. |app| has a builtin viewer for the most e-book formats. |vi| The :guilabel:`View` action displays the book in an ebook viewer program. |app| has a built-in viewer for many ebook formats.
For other formats it uses the default operating system application. You can configure which formats should open with the internal viewer via For other formats it uses the default operating system application. You can configure which formats should open with the internal viewer via
Preferences->Behavior. If a book has more than one format, you can view a particular format by clicking the down arrow Preferences->Behavior. If a book has more than one format, you can view a particular format by clicking the down arrow
on the right of the :guilabel:`View` button. on the right of the :guilabel:`View` button.
@ -130,11 +131,11 @@ Send to device
1. **Send to main memory**: The selected books are transferred to the main memory of the ebook reader. 1. **Send to main memory**: The selected books are transferred to the main memory of the ebook reader.
2. **Send to card (A)**: The selected books are transferred to the storage card (A) on the ebook reader. 2. **Send to card (A)**: The selected books are transferred to the storage card (A) on the ebook reader.
3. **Send to card (B)**: The selected books are transferred to the storage card (B) on the ebook reader. 3. **Send to card (B)**: The selected books are transferred to the storage card (B) on the ebook reader.
4. **Send and delete from library>**: The selected books are transferred to the selected storage location on the device, and then **deleted** from the Library. 4. **Send specific format to**: The selected books are transferred to the selected storage location on the device, in the format that you specify.
5. **Send Specific format>**: The selected books are transferred to the selected storage location on the device, in the format that you specify. 5. **Eject device**: Detaches the device from |app|.
6. **Eject device**: The device is detached from |app|. 6. **Set default send to device action**: Allows you to specify which of the options, 1 through 5 above or 7 below, will be the default action when you click the main button.
7. **Set default send to device action>**: This action allows you to Specify which of the option 1) through 6) above will be the default action when you click the main button. 7. **Send and delete from library**: The selected books are transferred to the selected storage location on the device and then **deleted** from the Library.
8. **Fetch Annotations**: This is an experimental action which will transfer annotations you may have made on an ebook on your device, and add those annotations to the comments metadata of the book in the |app| library 8. **Fetch Annotations (experimental)**: Transfers annotations you may have made on an ebook on your device to the comments metadata of the book in the |app| library.
You can control the file name and folder structure of files sent to the device by setting up a template in You can control the file name and folder structure of files sent to the device by setting up a template in
:guilabel:`Preferences->Import/Export->Sending books to devices`. Also see :ref:`templatelangcalibre`. :guilabel:`Preferences->Import/Export->Sending books to devices`. Also see :ref:`templatelangcalibre`.
@ -152,9 +153,9 @@ The :guilabel:`Fetch news` action uses simple recipes (10-15 lines of code) for
The :guilabel:`Fetch news` action has three variations, accessed by clicking the down arrow on the right of the button. The :guilabel:`Fetch news` action has three variations, accessed by clicking the down arrow on the right of the button.
1. **Schedule news download**: This action allows you to schedule the download of of your selected news sources from a list of hundreds of available. Scheduling can be set individually for each news source you select and the scheduling is flexible allowing you to select specific days of the week or a frequency of days between downloads. 1. **Schedule news download**: Allows you to schedule the download of of your selected news sources from a list of hundreds available. Scheduling can be set individually for each news source you select and the scheduling is flexible allowing you to select specific days of the week or a frequency of days between downloads.
2. **Add a custom news service**: This action allows you to create a simple recipe for downloading news from a custom news site that you wish to access. Creating the recipe can be as simple as specifying an RSS news feed URL, or you can be more prescriptive by creating python based code for the task, see :ref:`news`. 2. **Add a custom news source**: Allows you to create a simple recipe for downloading news from a custom news site that you wish to access. Creating the recipe can be as simple as specifying an RSS news feed URL, or you can be more prescriptive by creating Python-based code for the task. For more information see :ref:`news`.
3. **Download all scheduled news sources**: This action causes |app| to immediately begin to download all news sources that you have previously scheduled. 3. **Download all scheduled news sources**: Causes |app| to immediately begin downloading all news sources that you have scheduled.
.. _library: .. _library:
@ -164,16 +165,16 @@ Library
.. |lii| image:: images/library.png .. |lii| image:: images/library.png
:class: float-right-img :class: float-right-img
|lii| The :guilabel:`Library` action allows you to create, switch between, rename or remove a Library. |app| allows you to create as many libraries as you wish. You could for instance create a fiction library, a non fiction library, a foreign language library, a project library, basically any structure that suits your needs. Libraries are the highest organizational structure within |app|, each library has its own set of books, tags, categories and base storage location. |lii| The :guilabel:`Library` action allows you to create, switch between, rename or remove a Library. |app| allows you to create as many libraries as you wish. You could, for instance, create a fiction library, a non-fiction library, a foreign language library, a project library, or any structure that suits your needs. Libraries are the highest organizational structure within |app|. Each library has its own set of books, tags, categories and base storage location.
1. **Switch/Create library**: This action allows you to; a) connect to a pre-existing |app| library at another location from your currently open library, b) Create and empty library at a new location or, c) Move the current Library to a newly specified location. 1. **Switch/create library...**: Allows you to; a) connect to a pre-existing |app| library at another location, b) create an empty library at a new location or, c) move the current library to a newly specified location.
2. **Quick Switch**: This action allows you to switch between libraries that have been registered or created within |app|. 2. **Quick switch**: Allows you to switch between libraries that have been registered or created within |app|.
3. **Rename Library**: This action allows you to rename a Library. 3. **Rename library**: Allows you to rename a Library.
4. **Remove Library**: This action allows you to unregister a library from |app|. 4. **Delete library**: Allows you to unregister a library from |app|.
5. **<library name>**: Actions 5, 6 etc .. give you immediate switch access between multiple Libraries that you have created or attached to. This list contains only the 5 most frequently used libraries. For the complete list, use the Quick Switch menu. 5. **<library name>**: Actions 5, 6 etc... give you immediate switch access between multiple libraries that you have created or attached to. This list contains only the 5 most frequently used libraries. For the complete list, use the Quick Switch menu.
6. **Library Maintenance**: This action allows you to check the current library for data consistency issues and restore the current libraries' database from backups. 6. **Library maintenance**: Allows you to check the current library for data consistency issues and restore the current library's database from backups.
.. note:: Metadata about your ebooks like title/author/tags/etc. is stored in a single file in your |app| library folder called metadata.db. If this file gets corrupted (a very rare event), you can lose the metadata. Fortunately, |app| automatically backs up the metadata for every individual book in the book's folder as an .opf file. By using the Restore Library action under Library Maintenance described above, you can have |app| rebuild the metadata.db file from the individual .opf files for you. .. note:: Metadata about your ebooks, e.g. title, author, and tags, is stored in a single file in your |app| library folder called metadata.db. If this file gets corrupted (a very rare event), you can lose the metadata. Fortunately, |app| automatically backs up the metadata for every individual book in the book's folder as an OPF file. By using the Restore Library action under Library Maintenance described above, you can have |app| rebuild the metadata.db file from the individual OPF files for you.
You can copy or move books between different libraries (once you have more than one library setup) by right clicking on the book and selecting the action :guilabel:`Copy to library`. You can copy or move books between different libraries (once you have more than one library setup) by right clicking on the book and selecting the action :guilabel:`Copy to library`.
@ -185,7 +186,7 @@ Device
:class: float-right-img :class: float-right-img
|dvi| The :guilabel:`Device` action allows you to view the books in the main memory or storage cards of your device, or to eject the device (detach it from |app|). |dvi| The :guilabel:`Device` action allows you to view the books in the main memory or storage cards of your device, or to eject the device (detach it from |app|).
This icon shows up automatically on the main |app| toolbar when you connect a supported device. You can click on it to see the books on your device. You can also drag and drop books from your |app| library onto the icon to transfer them to your device. Conversely, you can drag and drop books from your device onto the |app| icon on the toolbar to transfer books from your device to the |app| library. This icon shows up automatically on the main |app| toolbar when you connect a supported device. You can click on it to see the books on your device. You can also drag and drop books from your |app| library onto the icon to transfer them to your device. Conversely, you can drag and drop books from your device onto the library icon on the toolbar to transfer books from your device to the |app| library.
.. _save_to_disk: .. _save_to_disk:
@ -199,7 +200,7 @@ Save to disk
.. _save_to_disk_multiple: .. _save_to_disk_multiple:
1. **Save to disk**: This will save the selected books to disk organized in directories. The directory structure looks like:: 1. **Save to disk**: Saves the selected books to disk organized in directories. The directory structure looks like::
Author_(sort) Author_(sort)
Title Title
@ -210,17 +211,15 @@ Save to disk
.. _save_to_disk_single: .. _save_to_disk_single:
2. **Save to disk in a single directory**: The selected books are saved to disk in a single directory. 2. **Save to disk in a single directory**: Saves the selected books to disk in a single directory.
For 1. and 2. All available formats as well as metadata is stored to disk for each selected book. Metadata is stored in an OPF file. For 1. and 2., all available formats, as well as metadata, are stored to disk for each selected book. Metadata is stored in an OPF file. Saved books can be re-imported to the library without any loss of information by using the :ref:`Add books <add_books>` action.
Saved books can be re-imported to the library without any loss of information by using the :ref:`Add books <add_books>` action. 3. **Save only *<your preferred>* format to disk**: Saves the selected books to disk in the directory structure as shown in (1.) but only in your preferred ebook format. You can set your preferred format in :guilabel:`Preferences->Behaviour->Preferred output format`
3. **Save only *<your preferred>* format to disk**: The selected books are saved to disk in the directory structure as shown in (1.) but only in your preferred ebook format you can set <your preferred> format in :guilabel:`Preferences->Behaviour->Preferred output format` 4. **Save only *<your preferred>* format to disk in a single directory**: Saves the selected books to disk in a single directory but only in your preferred ebook format. You can set your preferred format in :guilabel:`Preferences->Behaviour->Preferred output format`
4. **Save only *<your preferred>* format to disk in a single directory**: The selected books are saved to disk in a single directory but only in <your preferred> ebook format you can set <your preferred> format in :guilabel:`Preferences->Behaviour->Preferred output format` 5. **Save single format to disk...**: Saves the selected books to disk in the directory structure as shown in (1.) but only in the format you select from the pop-out list.
5. **Save single format to disk ..**: The selected books are saved to disk in the directory structure as shown in (1.) but only in the format you select from the pop-out list. There are currently 35 formats available and new ones are being added all the time.
.. _connect_share: .. _connect_share:
@ -229,17 +228,17 @@ Connect/Share
.. |csi| image:: images/connect_share.png .. |csi| image:: images/connect_share.png
:class: float-right-img :class: float-right-img
|csi| The :guilabel:`Connect/Share` action allows you to manually connect to a device or folder on your computer, it also allows you to set up you |app| library for access via a web browser, or email. |csi| The :guilabel:`Connect/Share` action allows you to manually connect to a device or folder on your computer. It also allows you to set up you |app| library for access via a web browser or email.
The :guilabel:`Connect/Share` action has four variations, accessed by clicking the down arrow on the right of the button. The :guilabel:`Connect/Share` action has four variations, accessed by clicking the down arrow on the right of the button.
1. **Connect to folder**: This action allows you to connect to any folder on your computer as though it were a device and use all the facilities |app| has for devices with that folder. Useful if your device cannot be supported by |app| but is available as a USB disk. 1. **Connect to folder**: Allows you to connect to any folder on your computer as though it were a device and use all the facilities |app| has for devices with that folder. Useful if your device cannot be supported by |app| but is available as a USB disk.
2. **Connect to iTunes**: Allows you to connect to your iTunes books database as though it were a device. Once the books are sent to iTunes, you can then use iTunes to make them available on your various iDevices. Useful if you would rather not have |app| send books to your iDevice directly. 2. **Connect to iTunes**: Allows you to connect to your iTunes books database as though it were a device. Once the books are sent to iTunes, you can use iTunes to make them available to your various iDevices. This is useful if you would rather not have |app| send books to your iDevice directly.
3. **Start Content Server**: This action causes |app| to start up its built-in web server. When this is started, your |app| library will be accessible via a web browser from the internet (if you choose). You can configure how the web server is accessed by setting preferences at :guilabel:`Preferences->Sharing->Sharing over the net` 3. **Start Content Server**: Starts |app|'s built-in web server. When started, your |app| library will be accessible via a web browser from the Internet (if you choose). You can configure how the web server is accessed by setting preferences at :guilabel:`Preferences->Sharing->Sharing over the net`
4. **Setup email based sharing of books**: This action allows you to setup |app| to share books (and news feeds) by email. After setting up email addresses for this option |app| will send news updates and book updates to the entered email addresses. You can configure how the |app| sends email by setting preferences at :guilabel:`Preferences->Sharing->Sharing books by email`. Once you have setup one or more email addresses, this menu entry get replaced by menu entries to send books to the setup email addresses. 4. **Setup email based sharing of books**: Allows sharing of books and news feeds by email. After setting up email addresses for this option, |app| will send news updates and book updates to the entered email addresses. You can configure how |app| sends email by setting preferences at :guilabel:`Preferences->Sharing->Sharing books by email`. Once you have set up one or more email addresses, this menu entry will be replaced by menu entries to send books to the configured email addresses.
.. _remove_books: .. _remove_books:
@ -248,29 +247,36 @@ Remove books
.. |rbi| image:: images/remove_books.png .. |rbi| image:: images/remove_books.png
:class: float-right-img :class: float-right-img
|rbi| The :guilabel:`Remove books` action **deletes books permanently**, so use it with care. It is *context sensitive*, i.e. it depends on which :ref:`catalog <catalogs>` you have selected. If you have selected the :guilabel:`Library`, books will be removed from the library. If you have selected the ebook reader device, the books will be removed from the device. To remove only a particular format for a given book use the :ref:`edit_meta_information` action. Remove books also has five variations which can be accessed by clicking the down arrow on the right side of the button. |rbi| The :guilabel:`Remove books` action **deletes books permanently**, so use it with care. It is *context sensitive*, i.e. it depends on which :ref:`catalog <catalogs>` you have selected. If you have selected the :guilabel:`Library`, books will be removed from the library. If you have selected the ebook reader device, books will be removed from the device. To remove only a particular format for a given book use the :ref:`edit_meta_information` action. Remove books also has five variations which can be accessed by clicking the down arrow on the right side of the button.
1. **Remove Selected Books**: Allows you to **permanently** remove all books that are selected in the book list. 1. **Remove selected books**: Allows you to **permanently** remove all books that are selected in the book list.
2. **Remove files of a specified format from selected books..**: Allows you to **permanently** remove ebook files of a specified format, from books that are selected in the book list. 2. **Remove files of a specific format from selected books...**: Allows you to **permanently** remove ebook files of a specified format from books that are selected in the book list.
3. **Remove all files of a specified format, except..**: Allows you to **permanently** remove ebook files of a multiple formats except a given format, from books that are selected in the book list. 3. **Remove all formats from selected books, except...**: Allows you to **permanently** remove ebook files of any format except a specified format from books that are selected in the book list.
4. **Remove covers from selected books**: Allows you to **permanently** remove cover images files, from books that are selected in the book list. 3. **Remove all formats from selected books**: Allows you to **permanently** remove all ebook files from books that are selected in the book list. Only the metadata will remain.
5. **Remove matching books from device**: Allows you to remove ebook files from a connected device, that match the books that are selected in the book list. 4. **Remove covers from selected books**: Allows you to **permanently** remove cover image files from books that are selected in the book list.
5. **Remove matching books from device**: Allows you to remove ebook files from a connected device that match the books that are selected in the book list.
.. note:: .. note::
Note that when you use Remove books to delete books from your |app| library, the book record is permanently deleted, but, on (Windows and OS X) the files are placed into the recycle bin, so you can recover them if you change your mind. Note that when you use Remove books to delete books from your |app| library, the book record is permanently deleted, but on Windows and OS X the files are placed into the recycle bin. This allows you to recover them if you change your mind.
.. _configuration: .. _configuration:
Preferences Preferences
--------------- ---------------
.. |cbi| image:: images/preferences.png .. |cbi| image:: images/preferences.png
:class: float-right-img
The Preferences Action allows you to change the way various aspects of |app| work. To access it, click the |cbi|. |cbi| The :guilabel:`Preferences` action allows you to change the way various aspects of |app| work. It has four variations, accessed by clicking the down arrow on the right of the button.
You can also re-run the Welcome Wizard by clicking the arrow next to the preferences button.
1. **Preferences**: Allows you to change the way various aspects of |app| work. Clicking the button also performs this action.
2. **Run welcome wizard**: Allows you to start the Welcome Wizard which appeared the first time you started |app|.
3. **Get plugins to enhance |app|**: Opens a new windows that shows plugins for |app|. These plugins are developed by third parties to extend |app|'s functionality.
4. **Restart in debug mode**: Allows you to enable a debugging mode that can assist the |app| developers in solving problems you encounter with the program. For most users this should remain disabled unless instructed by a developer to enable it.
.. _catalogs: .. _catalogs:
@ -281,12 +287,11 @@ Catalogs
A *catalog* is a collection of books. |app| can manage two types of different catalogs: A *catalog* is a collection of books. |app| can manage two types of different catalogs:
1. **Library**: This is a collection of books stored in your |app| library on your computer 1. **Library**: This is a collection of books stored in your |app| library on your computer.
2. **Device**: This is a collection of books stored in the main memory of your ebook reader. It will be available when you connect the reader to your computer. 2. **Device**: This is a collection of books stored in your ebook reader. It will be available when you connect the reader to your computer.
- In addition, you can see the books on the storage card (if any) in your reader device.
Many operations, like Adding books, deleting, viewing, etc. are context sensitive. So, for example, if you click the View button when you have the **Device** catalog selected, |app| will open the files on the device to view. If you have the **Library** catalog selected, files in your |app| library will be opened instead. Many operations, such as adding books, deleting, viewing, etc., are context sensitive. So, for example, if you click the View button when you have the **Device** catalog selected, |app| will open the files on the device to view. If you have the **Library** catalog selected, files in your |app| library will be opened instead.
.. _search_sort: .. _search_sort:
@ -297,7 +302,7 @@ Search & Sort
The Search & Sort section allows you to perform several powerful actions on your book collections. The Search & Sort section allows you to perform several powerful actions on your book collections.
* You can sort them by title, author, date, rating etc. by clicking on the column titles. You can also sub-sort (i.e. sort on multiple columns). For example, if you click on the title column and then the author column, the book will be sorted by author and then all the entries for the same author will be sorted by title. * You can sort them by title, author, date, rating, etc. by clicking on the column titles. You can also sub-sort, i.e. sort on multiple columns. For example, if you click on the title column and then the author column, the book will be sorted by author and then all the entries for the same author will be sorted by title.
* You can search for a particular book or set of books using the search bar. More on that below. * You can search for a particular book or set of books using the search bar. More on that below.
@ -327,21 +332,21 @@ are available in the LRF format. Some more examples::
format:epub publisher:feedbooks.com format:epub publisher:feedbooks.com
Searches are by default 'contains'. An item matches if the search string appears anywhere in the indicated metadata. Searches are by default 'contains'. An item matches if the search string appears anywhere in the indicated metadata.
Two other kinds of searches are available: equality search and search using regular expressions. Two other kinds of searches are available: equality search and search using `regular expressions <http://en.wikipedia.org/wiki/Regular_expression>`_.
Equality searches are indicated by prefixing the search string with an equals sign (=). For example, the query Equality searches are indicated by prefixing the search string with an equals sign (=). For example, the query
``tag:"=science"`` will match "science", but not "science fiction" or "hard science". Regular expression searches are ``tag:"=science"`` will match "science", but not "science fiction" or "hard science". Regular expression searches are
indicated by prefixing the search string with a tilde (~). Any python-compatible regular expression can indicated by prefixing the search string with a tilde (~). Any `python-compatible regular expression <http://docs.python.org/library/re.html>`_ can
be used. Note that backslashes use to escape special characters in reqular expressions must be doubled, because single backslashes will be removed during query parsing. For example, to match a literal parenthesis, you must enter ``\\(``. Regular expression searches are contains searches unless the expression contains anchors. be used. Note that backslashes used to escape special characters in reqular expressions must be doubled because single backslashes will be removed during query parsing. For example, to match a literal parenthesis you must enter ``\\(``. Regular expression searches are 'contains' searches unless the expression contains anchors.
Should you need to search for a string with a leading equals or tilde, prefix the string with a backslash. Should you need to search for a string with a leading equals or tilde, prefix the string with a backslash.
Enclose search strings with quotes (") if the string contains parenthesis or spaces. For example, to search Enclose search strings with quotes (") if the string contains parenthesis or spaces. For example, to search
for the tag ``Science Fiction``, you would need to search for ``tag:"=science fiction"``. If you search for for the tag ``Science Fiction`` you would need to search for ``tag:"=science fiction"``. If you search for
``tag:=science fiction``, you will find all books with the tag 'science' and containing the word 'fiction' in any ``tag:=science fiction`` you will find all books with the tag 'science' and containing the word 'fiction' in any
metadata. metadata.
You can build advanced search queries easily using the :guilabel:`Advanced Search Dialog`, accessed by You can build advanced search queries easily using the :guilabel:`Advanced Search Dialog` accessed by
clicking the button |sbi|. clicking the button |sbi|.
Available fields for searching are: ``tag, title, author, publisher, series, series_index, rating, cover, Available fields for searching are: ``tag, title, author, publisher, series, series_index, rating, cover,
@ -354,10 +359,13 @@ The syntax for searching for dates is::
pubdate:=2009 Will find all books published in 2009 pubdate:=2009 Will find all books published in 2009
If the date is ambiguous, the current locale is used for date comparison. For example, in an mm/dd/yyyy If the date is ambiguous, the current locale is used for date comparison. For example, in an mm/dd/yyyy
locale, 2/1/2009 is interpreted as 1 Feb 2009. In a dd/mm/yyyy locale, it is interpreted as 2 Jan 2009. Some locale 2/1/2009 is interpreted as 1 Feb 2009. In a dd/mm/yyyy locale it is interpreted as 2 Jan 2009. Some
special date strings are available. The string ``today`` translates to today's date, whatever it is. The special date strings are available. The string ``today`` translates to today's date, whatever it is. The
strings ``yesterday`` and ``thismonth`` also work. In addition, the string ``daysago`` can be used to compare strings ``yesterday`` and ``thismonth`` also work. In addition, the string ``daysago`` can be used to compare
to a date some number of days ago, for example: date:>10daysago, date:<=45daysago. to a date some number of days ago. For example::
date:>10daysago
date:<=45daysago
You can search for books that have a format of a certain size like this:: You can search for books that have a format of a certain size like this::
@ -369,7 +377,7 @@ Dates and numeric fields support the relational operators ``=`` (equals), ``>``
Rating fields are considered to be numeric. For example, the search ``rating:>=3`` will find all books rated 3 Rating fields are considered to be numeric. For example, the search ``rating:>=3`` will find all books rated 3
or higher. or higher.
You can search for the number of items in multiple-valued fields such as tags). These searches begin with the character ``#``, then use the same syntax as numeric fields. For example, to find all books with more than 4 tags, use ``tags:#>4``. To find all books with exactly 10 tags, use ``tags:#=10``. You can search for the number of items in multiple-valued fields such as tags. These searches begin with the character ``#``, then use the same syntax as numeric fields. For example, to find all books with more than 4 tags use ``tags:#>4``. To find all books with exactly 10 tags use ``tags:#=10``.
Series indices are searchable. For the standard series, the search name is 'series_index'. For Series indices are searchable. For the standard series, the search name is 'series_index'. For
custom series columns, use the column search name followed by _index. For example, to search the indices for a custom series columns, use the column search name followed by _index. For example, to search the indices for a
@ -378,7 +386,7 @@ Series indices are numbers, so you can use the relational operators described ab
The special field ``search`` is used for saved searches. So if you save a search with the name The special field ``search`` is used for saved searches. So if you save a search with the name
"My spouse's books" you can enter ``search:"My spouse's books"`` in the search bar to reuse the saved "My spouse's books" you can enter ``search:"My spouse's books"`` in the search bar to reuse the saved
search. More about saving searches, below. search. More about saving searches below.
You can search for the absence or presence of a field using the special "true" and "false" values. For example:: You can search for the absence or presence of a field using the special "true" and "false" values. For example::
@ -418,9 +426,9 @@ Identifiers (e.g., isbn, doi, lccn etc) also use an extended syntax. First, note
Saving searches Saving searches
----------------- -----------------
|app| has a useful feature, it allows you to save a search you use frequently under a special name and then re-use that search with a single click. To do this, create your search, either by typing it in the search bar, or using the Tag Browser. Then, type the name you would like to give to the search in the Saved Searches box next to the search bar and click the plus icon next to the saved searches box to save the search. |app| allows you to save a frequently used search under a special name and then reuse that search with a single click. To do this, create your search either by typing it in the search bar or using the Tag Browser. Then type the name you would like to give to the search in the Saved Searches box next to the search bar. Click the plus icon next to the saved searches box to save the search.
Now, you can access your saved search in the Tag Browser under "Searches". A single click will allow you to re-use any arbitrarily complex search easily, without needing to re-create it. Now you can access your saved search in the Tag Browser under "Searches". A single click will allow you to reuse any arbitrarily complex search easily, without needing to re-create it.
.. _config_filename_metadata: .. _config_filename_metadata:
@ -431,7 +439,7 @@ that you add to the library. The default regular expression is::
title - author title - author
i.e., it will assumes that all character up to the first ``-`` are the title of the book and subsequent characters are the author of the book. For example, the filename:: i.e., it assumes that all characters up to the first ``-`` are the title of the book and subsequent characters are the author of the book. For example, the filename::
Foundation and Earth - Isaac Asimov.txt Foundation and Earth - Isaac Asimov.txt
@ -447,7 +455,7 @@ Book Details
.. image:: images/book_details.png .. image:: images/book_details.png
:align: center :align: center
The Book Details display shows you extra information and the cover for the currently selected book. The Book Details display shows extra information and the cover for the currently selected book.
.. _jobs: .. _jobs:
@ -460,15 +468,19 @@ Tag Browser
The Tag Browser allows you to easily browse your collection by Author/Tags/Series/etc. If you click on any item in the Tag Browser, for example the author name Isaac Asimov, then the list of books to the right is restricted to showing books by that author. You can click on category names as well. For example, clicking on "Series" will show you all books in any series. The Tag Browser allows you to easily browse your collection by Author/Tags/Series/etc. If you click on any item in the Tag Browser, for example the author name Isaac Asimov, then the list of books to the right is restricted to showing books by that author. You can click on category names as well. For example, clicking on "Series" will show you all books in any series.
The first click on an item will restrict the list of books to those that contain/match the item. Continuing the above example, clicking on Isaac Asimov will show books by that author. Clicking again on the item will change what is shown, depending on whether the item has children (see sub-categories and hierarchical items below). Continuing the Isaac Asimov example, clicking again on Isaac Asimov will restrict the list of books to those not by Isaac Asimov. A third click will remove the restriction, showing all books. If you hold down the Ctrl or Shift keys and click on multiple items, then restrictions based on multiple items are created. For example you could hold Ctrl and click on the tags History and Europe for find books on European history. The Tag Browser works by constructing search expressions that are automatically entered into the Search bar. Looking at what the Tag Browser generates is a good way to learn how to construct basic search expressions. The first click on an item will restrict the list of books to those that contain or match the item. Continuing the above example, clicking on Isaac Asimov will show books by that author. Clicking again on the item will change what is shown, depending on whether the item has children (see sub-categories and hierarchical items below). Continuing the Isaac Asimov example, clicking again on Isaac Asimov will restrict the list of books to those not by Isaac Asimov. A third click will remove the restriction, showing all books. If you hold down the Ctrl or Shift keys and click on multiple items, then restrictions based on multiple items are created. For example you could hold Ctrl and click on the tags History and Europe for finding books on European history. The Tag Browser works by constructing search expressions that are automatically entered into the Search bar. Looking at what the Tag Browser generates is a good way to learn how to construct basic search expressions.
Items in the Tag browser have their icons partially colored. The amount of color depends on the average rating of the books in that category. So for example if the books by Isaac Asimov have an average of four stars, the icon for Isaac Asimov in the Tag Browser will be 4/5th colored. You can hover your mouse over the icon to see the average rating. Items in the Tag browser have their icons partially colored. The amount of color depends on the average rating of the books in that category. So for example if the books by Isaac Asimov have an average of four stars, the icon for Isaac Asimov in the Tag Browser will be 4/5th colored. You can hover your mouse over the icon to see the average rating.
The outer-level items in the tag browser such as Authors and Series are called categories. You can create your own categories, called User Categories, which are useful for organizing items. For example, you can use the User Categories Editor (push the Manage User Categories button) to create a user category called Favorite Authors, then put the items for your favorites into the category. User categories can have sub-categories. For example, the user category Favorites.Authors is a sub-category of Favorites. You might also have Favorites.Series, in which case there will be two sub-categories under Favorites. Sub-categories can be created by right-clicking on a user category, choosing "Add sub-category to ...", and entering the sub-category name; or by using the User Categories Editor by entering names like the Favorites example above. The outer-level items in the tag browser, such as Authors and Series, are called categories. You can create your own categories, called User Categories, which are useful for organizing items. For example, you can use the User Categories Editor (click the Manage User Categories button) to create a user category called Favorite Authors, then put the items for your favorites into the category. User categories can have sub-categories. For example, the user category Favorites.Authors is a sub-category of Favorites. You might also have Favorites.Series, in which case there will be two sub-categories under Favorites. Sub-categories can be created by right-clicking on a user category, choosing "Add sub-category to ...", and entering the sub-category name; or by using the User Categories Editor by entering names like the Favorites example above.
You can search user categories in the same way as built-in categories, by clicking on them. There are four different searches cycled through by clicking: "everything matching an item in the category" indicated by a single green plus sign, "everything matching an item in the category or its sub-categories" indicated by two green plus signs, "everything not matching an item in the category" shown by a single red minus sign, and "everything not matching an item in the category or its sub-categories" shown by two red minus signs. You can search user categories in the same way as built-in categories, by clicking on them. There are four different searches cycled through by clicking:
1. "everything matching an item in the category" indicated by a single green plus sign.
2. "everything matching an item in the category or its sub-categories" indicated by two green plus signs.
3. "everything not matching an item in the category" shown by a single red minus sign.
4. "everything not matching an item in the category or its sub-categories" shown by two red minus signs.
It is also possible to create hierarchies inside some of the text categories such as tags, series, and custom columns. These hierarchies show with the small triangle, permitting the sub-items to be hidden. To use hierarchies of items in a category, you must first go to Preferences / Look & Feel and enter the category name(s) into the "Categories with hierarchical items" box. Once this is done, items in that category that contain periods will be shown using the small triangle. For example, assume you create a custom column called "Genre" and indicate that it contains hierarchical items. Once done, items such as Mystery.Thriller and Mystery.English will display as Mystery with the small triangle next to it. Clicking on the triangle will show Thriller and English as sub-items. See :ref:`Managing subgroups of books, for example "genre" <subgroups-tutorial>` for more information. It is also possible to create hierarchies inside some of the text categories such as tags, series, and custom columns. These hierarchies show with the small triangle, permitting the sub-items to be hidden. To use hierarchies of items in a category, you must first go to Preferences->Look & Feel and enter the category name(s) into the "Categories with hierarchical items" box. Once this is done, items in that category that contain periods will be shown using the small triangle. For example, assume you create a custom column called "Genre" and indicate that it contains hierarchical items. Once done, items such as Mystery.Thriller and Mystery.English will display as Mystery with the small triangle next to it. Clicking on the triangle will show Thriller and English as sub-items. See :ref:`Managing subgroups of books, for example "genre" <subgroups-tutorial>` for more information.
Hierarchical items (items with children) use the same four 'click-on' searches as user categories. Items that do not have children use two of the searches: "everything matching" and "everything not matching". Hierarchical items (items with children) use the same four 'click-on' searches as user categories. Items that do not have children use two of the searches: "everything matching" and "everything not matching".
@ -476,23 +488,23 @@ You can drag and drop items in the Tag browser onto user categories to add them
There is a search bar at the top of the Tag Browser that allows you to easily find any item in the Tag Browser. In addition, you can right click on any item and choose one of several operations. Some examples are to hide the it, rename it, or open a "Manage x" dialog that allows you to manage items of that kind. For example, the "Manage Authors" dialog allows you to rename authors and control how their names are sorted. There is a search bar at the top of the Tag Browser that allows you to easily find any item in the Tag Browser. In addition, you can right click on any item and choose one of several operations. Some examples are to hide the it, rename it, or open a "Manage x" dialog that allows you to manage items of that kind. For example, the "Manage Authors" dialog allows you to rename authors and control how their names are sorted.
You can control how items are sorted in the Tag browser via the box at the bottom of the Tag Browser. You can choose to sort by name, average rating or popularity (popularity is the number of books with an item in your library; for example; the popularity of Isaac Asimov is the number of book sin your library by Isaac Asimov). You can control how items are sorted in the Tag browser via the box at the bottom of the Tag Browser. You can choose to sort by name, average rating or popularity (popularity is the number of books with an item in your library; for example, the popularity of Isaac Asimov is the number of books in your library by Isaac Asimov).
Jobs Jobs
----- -----
.. image:: images/jobs.png .. image:: images/jobs.png
:class: float-left-img :class: float-left-img
The Jobs panel shows you the number of currently running jobs. Jobs are tasks that run in a separate process, they include converting ebooks and talking to your reader device. You can click on the jobs panel to access the list of jobs. Once a job has completed, by double-clicking it in the list, you can see a detailed log from that job. This is useful to debug jobs that may not have completed successfully. The Jobs panel shows the number of currently running jobs. Jobs are tasks that run in a separate process. They include converting ebooks and talking to your reader device. You can click on the jobs panel to access the list of jobs. Once a job has completed you can see a detailed log from that job by double-clicking it in the list. This is useful to debug jobs that may not have completed successfully.
Keyboard Shortcuts Keyboard Shortcuts
--------------------- ---------------------
Calibre has several keyboard shortcuts to save you time and mouse movement. These shortcuts are active in the book list view (when you're not editing the details of a particular book), and most of them affect the title you have selected. The |app| e-book viewer has its own shortcuts, which can be customised by clicking the Preferences button in the viewer. Calibre has several keyboard shortcuts to save you time and mouse movement. These shortcuts are active in the book list view (when you're not editing the details of a particular book), and most of them affect the title you have selected. The |app| ebook viewer has its own shortcuts which can be customised by clicking the Preferences button in the viewer.
.. note:: .. note::
Note: The Calibre keyboard shortcuts do not require a modifier key (Command, Option, Control etc.), unless specifically noted. You only need to press the letter key, e.g. E to edit. Note: The Calibre keyboard shortcuts do not require a modifier key (Command, Option, Control, etc.), unless specifically noted. You only need to press the letter key, e.g. E to edit.
.. list-table:: Keyboard Shortcuts .. list-table:: Keyboard Shortcuts
:widths: 10 100 :widths: 10 100
@ -535,7 +547,7 @@ Calibre has several keyboard shortcuts to save you time and mouse movement. Thes
* - :kbd:`Alt+Shift+T` * - :kbd:`Alt+Shift+T`
- Toggle Tag Browser - Toggle Tag Browser
* - :kbd:`Alt+A` * - :kbd:`Alt+A`
- Show books by the Same author as the current book - Show books by the same author as the current book
* - :kbd:`Alt+T` * - :kbd:`Alt+T`
- Show books with the same tags as current book - Show books with the same tags as current book
* - :kbd:`Alt+P` * - :kbd:`Alt+P`

20
src/calibre/manual/index.rst Normal file → Executable file
View File

@ -3,23 +3,21 @@
|app| User Manual |app| User Manual
=============================================== ===============================================
|app| is an e-book library manager. It can view, convert and catalog e-books in most of the major e-book formats. It can also talk to a few e-book reader devices. It can go out to the internet and fetch metadata for your books. It can download newspapers and convert them into e-books for convenient reading. It is cross platform, running on Linux, Windows and OS X. |app| is an ebook library manager. It can view, convert and catalog ebooks in most of the major ebook formats. It can also talk to many ebook reader devices. It can go out to the Internet and fetch metadata for your books. It can download newspapers and convert them into ebooks for convenient reading. It is cross platform, running on Linux, Windows and OS X.
So you've just started |app|. What do you do now? Well, before |app| can do anything with your ebooks, it first has to know about them. So drag and drop a few e-book files into |app|, or click the "Add books" button and browse for the ebooks you want to work with. Once you've added the books, they will show up in the main view looking something like this: You've just started |app|. What do you do now? Before |app| can do anything with your ebooks, it first has to know about them. Drag and drop a few ebook files into |app|, or click the "Add books" button and browse for the ebooks you want to work with. Once you've added the books, they will show up in the main view looking something like this:
.. image:: images/added_books.png .. image:: images/added_books.png
Once you've admired the list of books you just added to your heart's content, you'll probably want to actually read one. In order to do that you'll have to convert the book to a format your reader understands. When first running |app|, the Welcome Wizard starts and it will have setup calibre for your reader device. Conversion is a breeze, just select the book you want to convert, and click the "Convert E-book" button. Ignore all the options for now and just click "OK". The little icon in the bottom right corner will start spinning. Once it's finished spinning, your converted book is ready. Click the "View" button to read the book. Once you've admired the list of books you just added to your heart's content, you'll probably want to read one. In order to do that you'll have to convert the book to a format your reader understands. When first running |app|, the Welcome Wizard starts and will set up calibre for your reader device. Conversion is a breeze. Just select the book you want to convert then click the "Convert books" button. Ignore all the options for now and click "OK". The little icon in the bottom right corner will start spinning. Once it's finished spinning, your converted book is ready. Click the "View" button to read the book.
Now if you want to read the book on your reader, just connect it to the computer, wait till calibre detects it (10-20secs) and then click the "Send to device" button. Once the icon stops spinning again, disconnect your reader and read away! If you didn't convert the book in the previous step, |app| will auto convert it to the format your reader device understands. If you want to read the book on your reader, connect it to the computer, wait till calibre detects it (10-20 seconds) and then click the "Send to device" button. Once the icon stops spinning again, disconnect your reader and read away! If you didn't convert the book in the previous step, |app| will auto convert it to the format your reader device understands.
To get started with more advanced usage, you should read about the :ref:`Graphical User Interface <gui>`. For even more power and versatility, learn the :ref:`Command Line Interface <cli>`. To get started with more advanced usage, you should read about the :ref:`Graphical User Interface <gui>`. For even more power and versatility, learn the :ref:`Command Line Interface <cli>`. You will find the list of :ref:`Frequently Asked Questions <faq>` useful as well.
You will find the list of :ref:`Frequently Asked Questions <faq>` useful as well.
.. only:: online .. only:: online
An e-book version of this User Manual is available in `EPUB format <calibre.epub>`_. An ebook version of this user manual is available in `EPUB format <calibre.epub>`_.
Sections Sections
------------ ------------
@ -56,7 +54,7 @@ Adding your favorite news website to |app|
news news
The |app| e-book viewer The |app| ebook viewer
^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^
.. toctree:: .. toctree::
@ -64,7 +62,7 @@ The |app| e-book viewer
viewer viewer
Customizing |app|'s e-book conversion Customizing |app|'s ebook conversion
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. toctree:: .. toctree::
@ -72,7 +70,7 @@ Customizing |app|'s e-book conversion
conversion conversion
Editing e-book metadata Editing ebook metadata
^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. toctree:: .. toctree::

View File

@ -2,14 +2,14 @@
.. _metadata: .. _metadata:
Editing E-book Metadata Editing Ebook Metadata
======================== ========================
.. contents:: Contents .. contents:: Contents
:depth: 2 :depth: 2
:local: :local:
E-books come in all shapes and sizes and more often than not, their metadata (things like title/author/series/publisher) is incomplete or incorrect. Ebooks come in all shapes and sizes and more often than not, their metadata (things like title/author/series/publisher) is incomplete or incorrect.
The simplest way to change metadata in |app| is to simply double click on an entry and type in the correct replacement. The simplest way to change metadata in |app| is to simply double click on an entry and type in the correct replacement.
For more sophisticated, "power editing" use the edit metadata tools discussed below. For more sophisticated, "power editing" use the edit metadata tools discussed below.

26
src/calibre/manual/news.rst Normal file → Executable file
View File

@ -5,7 +5,7 @@
Adding your favorite news website Adding your favorite news website
================================== ==================================
|app| has a powerful, flexible and easy-to-use framework for downloading news from the internet and converting it into an e-book. In the following, I will show you by means of examples, how to get news from various websites. |app| has a powerful, flexible and easy-to-use framework for downloading news from the Internet and converting it into an ebook. The following will show you, by means of examples, how to get news from various websites.
To gain an understanding of how to use the framework, follow the examples in the order listed below: To gain an understanding of how to use the framework, follow the examples in the order listed below:
@ -23,27 +23,27 @@ If your news source is simple enough, |app| may well be able to fetch it complet
portfolio.com portfolio.com
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
*portfolio.com* is the website for *Condé Nast Portfolio*, a business related magazine. In order to download articles from the magazine and convert them to e-books, we rely on the :term:`RSS` feeds of portfolio.com. A list of such feeds is available at http://www.portfolio.com/rss/. *portfolio.com* is the website for *Condé Nast Portfolio*, a business related magazine. In order to download articles from the magazine and convert them to ebooks, we rely on the :term:`RSS` feeds of portfolio.com. A list of such feeds is available at http://www.portfolio.com/rss/.
Lets pick a couple of feeds that look interesting: Lets pick a couple of feeds that look interesting:
#. Business Travel: http://feeds.portfolio.com/portfolio/businesstravel #. Business Travel: http://feeds.portfolio.com/portfolio/businesstravel
#. Tech Observer: http://feeds.portfolio.com/portfolio/thetechobserver #. Tech Observer: http://feeds.portfolio.com/portfolio/thetechobserver
I got the URLs by clicking the little orange RSS icon next to each feed name. To make |app| download the feeds and convert them into an e-book, you should click the :guilabel:`Fetch news` button and then the :guilabel:`Add a custom news source` menu item. A dialog similar to that shown below should open up. I got the URLs by clicking the little orange RSS icon next to each feed name. To make |app| download the feeds and convert them into an ebook, you should click the :guilabel:`Fetch news` button and then the :guilabel:`Add a custom news source` menu item. A dialog similar to that shown below should open up.
.. image:: images/custom_news.png .. image:: images/custom_news.png
:align: center :align: center
First enter ``Portfolio`` into the :guilabel:`Recipe title` field. This will be the title of the e-book that will be created from the articles in the above feeds. First enter ``Portfolio`` into the :guilabel:`Recipe title` field. This will be the title of the ebook that will be created from the articles in the above feeds.
The next two fields (:guilabel:`Oldest article` and :guilabel:`Max. number of articles`) allow you some control over how many articles should be downloaded from each feed, and they are pretty self explanatory. The next two fields (:guilabel:`Oldest article` and :guilabel:`Max. number of articles`) allow you some control over how many articles should be downloaded from each feed, and they are pretty self explanatory.
To add the feeds to the recipe, enter the feed title and the feed URL and click the :guilabel:`Add feed` button. Once you have added both feeds, simply click the :guilabel:`Add/update recipe` button and you're done! Close the dialog. To add the feeds to the recipe, enter the feed title and the feed URL and click the :guilabel:`Add feed` button. Once you have added both feeds, simply click the :guilabel:`Add/update recipe` button and you're done! Close the dialog.
To test your new :term:`recipe`, click the :guilabel:`Fetch news` button and in the :guilabel:`Custom news sources` sub-menu click :guilabel:`Portfolio`. After a couple of minutes, the newly downloaded Portfolio e-book will appear in the main library view (if you have your reader connected, it will be put onto the reader instead of into the library). Select it and hit the :guilabel:`View` button to read! To test your new :term:`recipe`, click the :guilabel:`Fetch news` button and in the :guilabel:`Custom news sources` sub-menu click :guilabel:`Portfolio`. After a couple of minutes, the newly downloaded Portfolio ebook will appear in the main library view (if you have your reader connected, it will be put onto the reader instead of into the library). Select it and hit the :guilabel:`View` button to read!
The reason this worked so well, with so little effort is that *portfolio.com* provides *full-content* :term:`RSS` feeds, i.e., the article content is embedded in the feed itself. For most news sources that provide news in this fashion, with *full-content* feeds, you don't need any more effort to convert them to e-books. Now we will look at a news source that does not provide full content feeds. In such feeds, the full article is a webpage and the feed only contains a link to the webpage with a short summary of the article. The reason this worked so well, with so little effort is that *portfolio.com* provides *full-content* :term:`RSS` feeds, i.e., the article content is embedded in the feed itself. For most news sources that provide news in this fashion, with *full-content* feeds, you don't need any more effort to convert them to ebooks. Now we will look at a news source that does not provide full content feeds. In such feeds, the full article is a webpage and the feed only contains a link to the webpage with a short summary of the article.
.. _bbc: .. _bbc:
@ -55,19 +55,19 @@ Lets try the following two feeds from *The BBC*:
#. News Front Page: http://newsrss.bbc.co.uk/rss/newsonline_world_edition/front_page/rss.xml #. News Front Page: http://newsrss.bbc.co.uk/rss/newsonline_world_edition/front_page/rss.xml
#. Science/Nature: http://newsrss.bbc.co.uk/rss/newsonline_world_edition/science/nature/rss.xml #. Science/Nature: http://newsrss.bbc.co.uk/rss/newsonline_world_edition/science/nature/rss.xml
Follow the procedure outlined in :ref:`portfolio` to create a recipe for *The BBC* (using the feeds above). Looking at the downloaded e-book, we see that |app| has done a creditable job of extracting only the content you care about from each article's webpage. However, the extraction process is not perfect. Sometimes it leaves in undesirable content like menus and navigation aids or it removes content that should have been left alone, like article headings. In order, to have perfect content extraction, we will need to customize the fetch process, as described in the next section. Follow the procedure outlined in :ref:`portfolio` to create a recipe for *The BBC* (using the feeds above). Looking at the downloaded ebook, we see that |app| has done a creditable job of extracting only the content you care about from each article's webpage. However, the extraction process is not perfect. Sometimes it leaves in undesirable content like menus and navigation aids or it removes content that should have been left alone, like article headings. In order, to have perfect content extraction, we will need to customize the fetch process, as described in the next section.
Customizing the fetch process Customizing the fetch process
-------------------------------- --------------------------------
When you want to perfect the download process, or download content from a particularly complex website, you can avail yourself of all the power and flexibility of the :term:`recipe` framework. In order to do that, in the :guilabel:`Add custom news sources` dialog, simply click the :guilabel:`Switch to Advanced mode` button. When you want to perfect the download process, or download content from a particularly complex website, you can avail yourself of all the power and flexibility of the :term:`recipe` framework. In order to do that, in the :guilabel:`Add custom news sources` dialog, simply click the :guilabel:`Switch to Advanced mode` button.
The easiest and often most productive customization is to use the print version of the online articles. The print version typically has much less cruft and translates much more smoothly to an e-book. Let's try to use the print version of the articles from *The BBC*. The easiest and often most productive customization is to use the print version of the online articles. The print version typically has much less cruft and translates much more smoothly to an ebook. Let's try to use the print version of the articles from *The BBC*.
Using the print version of bbc.co.uk Using the print version of bbc.co.uk
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The first step is to look at the e-book we downloaded previously from :ref:`bbc`. At the end of each article, in the e-book is a little blurb telling you where the article was downloaded from. Copy and paste that URL into a browser. Now on the article webpage look for a link that points to the "Printable version". Click it to see the print version of the article. It looks much neater! Now compare the two URLs. For me they were: The first step is to look at the ebook we downloaded previously from :ref:`bbc`. At the end of each article, in the ebook is a little blurb telling you where the article was downloaded from. Copy and paste that URL into a browser. Now on the article webpage look for a link that points to the "Printable version". Click it to see the print version of the article. It looks much neater! Now compare the two URLs. For me they were:
Article URL Article URL
http://news.bbc.co.uk/2/hi/science/nature/7312016.stm http://news.bbc.co.uk/2/hi/science/nature/7312016.stm
@ -98,7 +98,7 @@ This is python, so indentation is important. After you've added the lines, it sh
In the above, ``def print_version(self, url)`` defines a *method* that is called by |app| for every article. ``url`` is the URL of the original article. What ``print_version`` does is take that url and replace it with the new URL that points to the print version of the article. To learn about `python <http://www.python.org>`_ see the `tutorial <http://docs.python.org/tut/>`_. In the above, ``def print_version(self, url)`` defines a *method* that is called by |app| for every article. ``url`` is the URL of the original article. What ``print_version`` does is take that url and replace it with the new URL that points to the print version of the article. To learn about `python <http://www.python.org>`_ see the `tutorial <http://docs.python.org/tut/>`_.
Now, click the :guilabel:`Add/update recipe` button and your changes will be saved. Re-download the e-book. You should have a much improved e-book. One of the problems with the new version is that the fonts on the print version webpage are too small. This is automatically fixed when converting to an e-book, but even after the fixing process, the font size of the menus and navigation bar to become too large relative to the article text. To fix this, we will do some more customization, in the next section. Now, click the :guilabel:`Add/update recipe` button and your changes will be saved. Re-download the ebook. You should have a much improved ebook. One of the problems with the new version is that the fonts on the print version webpage are too small. This is automatically fixed when converting to an ebook, but even after the fixing process, the font size of the menus and navigation bar to become too large relative to the article text. To fix this, we will do some more customization, in the next section.
Replacing article styles Replacing article styles
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -131,7 +131,7 @@ This can be removed by adding::
remove_tags = [dict(name='div', attrs={'class':'footer'})] remove_tags = [dict(name='div', attrs={'class':'footer'})]
to the recipe. Finally, lets replace some of the :term:`CSS` that we disabled earlier, with our own :term:`CSS` that is suitable for conversion to an e-book:: to the recipe. Finally, lets replace some of the :term:`CSS` that we disabled earlier, with our own :term:`CSS` that is suitable for conversion to an ebook::
extra_css = '.headline {font-size: x-large;} \n .fact { padding-top: 10pt }' extra_css = '.headline {font-size: x-large;} \n .fact { padding-top: 10pt }'
@ -234,7 +234,7 @@ We see several new features in this :term:`recipe`. First, we have::
timefmt = ' [%a, %d %b, %Y]' timefmt = ' [%a, %d %b, %Y]'
This sets the displayed time on the front page of the created e-book to be in the format, This sets the displayed time on the front page of the created ebook to be in the format,
``Day, Day_Number Month, Year``. See :attr:`timefmt <calibre.web.feeds.news.BasicNewsRecipe.timefmt>`. ``Day, Day_Number Month, Year``. See :attr:`timefmt <calibre.web.feeds.news.BasicNewsRecipe.timefmt>`.
Then we see a group of directives to cleanup the downloaded :term:`HTML`:: Then we see a group of directives to cleanup the downloaded :term:`HTML`::
@ -254,7 +254,7 @@ The next interesting feature is::
``needs_subscription = True`` tells |app| that this recipe needs a username and password in order to access the content. This causes, |app| to ask for a username and password whenever you try to use this recipe. The code in :meth:`calibre.web.feeds.news.BasicNewsRecipe.get_browser` actually does the login into the NYT website. Once logged in, |app| will use the same, logged in, browser instance to fetch all content. See `mechanize <http://wwwsearch.sourceforge.net/mechanize/>`_ to understand the code in ``get_browser``. ``needs_subscription = True`` tells |app| that this recipe needs a username and password in order to access the content. This causes, |app| to ask for a username and password whenever you try to use this recipe. The code in :meth:`calibre.web.feeds.news.BasicNewsRecipe.get_browser` actually does the login into the NYT website. Once logged in, |app| will use the same, logged in, browser instance to fetch all content. See `mechanize <http://wwwsearch.sourceforge.net/mechanize/>`_ to understand the code in ``get_browser``.
The next new feature is the :meth:`calibre.web.feeds.news.BasicNewsRecipe.parse_index` method. Its job is to go to http://www.nytimes.com/pages/todayspaper/index.html and fetch the list of articles that appear in *todays* paper. While more complex than simply using :term:`RSS`, the recipe creates an e-book that corresponds very closely to the days paper. ``parse_index`` makes heavy use of `BeautifulSoup <http://www.crummy.com/software/BeautifulSoup/documentation.html>`_ to parse the daily paper webpage. The next new feature is the :meth:`calibre.web.feeds.news.BasicNewsRecipe.parse_index` method. Its job is to go to http://www.nytimes.com/pages/todayspaper/index.html and fetch the list of articles that appear in *todays* paper. While more complex than simply using :term:`RSS`, the recipe creates an ebook that corresponds very closely to the days paper. ``parse_index`` makes heavy use of `BeautifulSoup <http://www.crummy.com/software/BeautifulSoup/documentation.html>`_ to parse the daily paper webpage.
The final new feature is the :meth:`calibre.web.feeds.news.BasicNewsRecipe.preprocess_html` method. It can be used to perform arbitrary transformations on every downloaded HTML page. Here it is used to bypass the ads that the nytimes shows you before each article. The final new feature is the :meth:`calibre.web.feeds.news.BasicNewsRecipe.preprocess_html` method. It can be used to perform arbitrary transformations on every downloaded HTML page. Here it is used to bypass the ads that the nytimes shows you before each article.

View File

@ -51,19 +51,19 @@ collection_template = '''\
''' '''
ABOUT='''\ ABOUT='''\
calibre is the one stop solution for all your e-book needs. It was created calibre is the one stop solution for all your ebook needs. It was created
originally by Kovid Goyal, to help him manage his e-book collection and is now originally by Kovid Goyal, to help him manage his ebook collection and is now
very actively developed by an international community of e-book enthusiasts. very actively developed by an international community of ebook enthusiasts.
Its goal is to empower you, the user, to do whatever you like with the e-books Its goal is to empower you, the user, to do whatever you like with the ebooks
in your collection. You can convert them to many different formats, read them in your collection. You can convert them to many different formats, read them
on your computer, send them to many different devices, edit their metadata on your computer, send them to many different devices, edit their metadata
and covers, etc. and covers, etc.
calibre also allows you to download news from a variety of different sources all calibre also allows you to download news from a variety of different sources all
over the internet and read conveniently in e-books form. In keeping with its over the Internet and read conveniently in ebooks form. In keeping with its
philosophy of empowering the user, it has a simple system to allow you to add philosophy of empowering the user, it has a simple system to allow you to add
your own favorite news sources. In fact, most the builtin news sources in your own favorite news sources. In fact, most the built-in news sources in
calibre were originally contributed by users. calibre were originally contributed by users.
''' '''

View File

@ -179,7 +179,7 @@ The example shows several things:
* program mode is used if the expression begins with ``:'`` and ends with ``'``. Anything else is assumed to be single-function. * program mode is used if the expression begins with ``:'`` and ends with ``'``. Anything else is assumed to be single-function.
* the variable ``$`` stands for the field the expression is operating upon, ``#series`` in this case. * the variable ``$`` stands for the field the expression is operating upon, ``#series`` in this case.
* functions must be given all their arguments. There is no default value. For example, the standard builtin functions must be given an additional initial parameter indicating the source field, which is a significant difference from single-function mode. * functions must be given all their arguments. There is no default value. For example, the standard built-in functions must be given an additional initial parameter indicating the source field, which is a significant difference from single-function mode.
* white space is ignored and can be used anywhere within the expression. * white space is ignored and can be used anywhere within the expression.
* constant strings are enclosed in matching quotes, either ``'`` or ``"``. * constant strings are enclosed in matching quotes, either ``'`` or ``"``.

View File

@ -11,10 +11,10 @@ PREAMBLE = '''\
.. _templaterefcalibre: .. _templaterefcalibre:
Reference for all builtin template language functions Reference for all built-in template language functions
======================================================== ========================================================
Here, we document all the builtin functions available in the |app| template language. Every function is implemented as a class in python and you can click the source links to see the source code, in case the documentation is insufficient. The functions are arranged in logical groups by type. Here, we document all the built-in functions available in the |app| template language. Every function is implemented as a class in python and you can click the source links to see the source code, in case the documentation is insufficient. The functions are arranged in logical groups by type.
.. contents:: .. contents::
:depth: 2 :depth: 2

2
src/calibre/manual/tutorials.rst Normal file → Executable file
View File

@ -6,7 +6,7 @@
Tutorials Tutorials
======================================================= =======================================================
Here you will find tutorials to get you started using |app|'s more advanced features, like XPath and templates. Here you will find tutorials to get you started using |app|'s more advanced features, such as XPath and templates.
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1

30
src/calibre/manual/viewer.rst Normal file → Executable file
View File

@ -2,10 +2,10 @@
.. _viewer: .. _viewer:
The E-book Viewer The Ebook Viewer
============================= =============================
|app| includes a built-in E-book viewer that can view all the major e-book formats. |app| includes a built-in ebook viewer that can view all the major ebook formats.
The viewer is highly customizable and has many advanced features. The viewer is highly customizable and has many advanced features.
.. contents:: .. contents::
@ -16,11 +16,11 @@ Starting the viewer
-------------------- --------------------
You can view any of the books in your |app| library by selecting the book and pressing the View button. This You can view any of the books in your |app| library by selecting the book and pressing the View button. This
will open up the book in the e-book viewer. You can also launch the viewer by itself, from the Start menu in windows will open up the book in the ebook viewer. You can also launch the viewer by itself from the Start menu in Windows
or using the command :command:`ebook-viewer` in Linux and OS X (you have to install the command line tools on OS X or using the command :command:`ebook-viewer` in Linux and OS X (you have to install the command line tools on OS X
first by going to :guilabel:`Preferences->Advanced->Miscellaneous`). first by going to :guilabel:`Preferences->Advanced->Miscellaneous`).
Navigating around an e-book Navigating around an ebook
----------------------------- -----------------------------
.. |pni| image:: images/prev_next.png .. |pni| image:: images/prev_next.png
@ -35,7 +35,7 @@ Navigating around an e-book
You can "turn pages" in a book by using the :guilabel:`Page Next` and :guilabel:`Page Previous` buttons |pni|, or by pressing You can "turn pages" in a book by using the :guilabel:`Page Next` and :guilabel:`Page Previous` buttons |pni|, or by pressing
the Page Down/Page Up keys. Unlike most e-book viewers, |app| does not force you to view books in paged mode. You can the Page Down/Page Up keys. Unlike most ebook viewers, |app| does not force you to view books in paged mode. You can
scroll by amounts less than a page by using the scroll bar or various customizable keyboard shortcuts. scroll by amounts less than a page by using the scroll bar or various customizable keyboard shortcuts.
Bookmarks Bookmarks
@ -43,27 +43,27 @@ Bookmarks
When you are in the middle of a book and close the viewer, it will remember where you stopped reading and return there When you are in the middle of a book and close the viewer, it will remember where you stopped reading and return there
the next time you open the book. You can also set bookmarks in the book by using the Bookmark button |bookmi|. When viewing EPUB format the next time you open the book. You can also set bookmarks in the book by using the Bookmark button |bookmi|. When viewing EPUB format
books, these bookmarks are actually saved in the EPUB file itself, so you can add bookmarks, then send the file to a friend and books, these bookmarks are actually saved in the EPUB file itself. You can add bookmarks, then send the file to a friend.
when they open the file, they will be able to see your bookmarks. When they open the file, they will be able to see your bookmarks.
Table of Contents Table of Contents
^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^
If the book you are reading defines a Table of Contents, you can access it by pressing the Table of Contents button |toci|. If the book you are reading defines a Table of Contents, you can access it by pressing the Table of Contents button |toci|.
This will bring up a list of sections in the book and you can click on any of them to jump to that portion of the book. This will bring up a list of sections in the book. You can click on any of them to jump to that portion of the book.
Navigating by location Navigating by location
^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^
E-books, unlike paper books have no concept of pages. Instead, Ebooks, unlike paper books, have no concept of pages. Instead,
as you read through the book, you will notice that your position in the book is displayed in the upper left corner in a box as you read through the book, you will notice that your position in the book is displayed in the upper left corner in a box
like this |navposi|. This is both your current position and the total length of the book. These numbers are independent of the screen size and font like this |navposi|. This is both your current position and the total length of the book. These numbers are independent of the screen size and font
size you are viewing the boko at, and they play a similar role to page numbers in paper books. size you are viewing the book at, and they play a similar role to page numbers in paper books.
You can enter any number you like to go to the corresponding location in the book. You can enter any number you like to go to the corresponding location in the book.
|app| also has a very handy |app| also has a very handy
reference mode. You can turn it on by clicking the Reference Mode button |refmi|. Once you do this, every time you move your reference mode. You can turn it on by clicking the Reference Mode button |refmi|. Once you do this, every time you move your
mouse over a paragraph, calibre will display a unique number made up of the section and paragraph numbers. mouse over a paragraph, |app| will display a unique number made up of the section and paragraph numbers.
.. image:: images/ref_mode.png .. image:: images/ref_mode.png
:align: center :align: center
@ -72,8 +72,8 @@ You can use this number to unambiguously refer to parts of the books when discus
in other works. You can enter these numbers in the box marked Go to at the top of the window to go to a particular in other works. You can enter these numbers in the box marked Go to at the top of the window to go to a particular
reference location. reference location.
If you click on links inside the e-book to take you to different parts of the book, like an endnote, you can use the back and forward buttons If you click on links inside the ebook to take you to different parts of the book, such as an endnote, you can use the back and forward buttons
in the top left corner to return to where you were. These button behave just like those in a web browser. in the top left corner to return to where you were. These buttons behave just like those in a web browser.
Customizing the look and feel of your reading experience Customizing the look and feel of your reading experience
------------------------------------------------------------ ------------------------------------------------------------
@ -90,7 +90,7 @@ by the viewer to ones you like as well as the default font size when the viewer
More advanced customization can be achieved by the User Stylesheet setting. This is a stylesheet you can set that will be applied More advanced customization can be achieved by the User Stylesheet setting. This is a stylesheet you can set that will be applied
to every book. Using it you can do things like have white text on a black background, change paragraph styles, text justification, etc. to every book. Using it you can do things like have white text on a black background, change paragraph styles, text justification, etc.
For examples if custom stylesheets used by |app|'s users, see `the forums <http://www.mobileread.com/forums/showthread.php?t=51500>`_. For examples of custom stylesheets used by |app|'s users, see `the forums <http://www.mobileread.com/forums/showthread.php?t=51500>`_.
Dictionary lookup Dictionary lookup
------------------- -------------------
@ -101,6 +101,6 @@ server at ``dict.org`` to lookup words. The definition is displayed in a small b
Copying text and images Copying text and images
------------------------- -------------------------
You can select text and images by dragging the content with your mouse and then right click to copy to the clipboard. You can select text and images by dragging the content with your mouse and then right clicking to copy to the clipboard.
The copied material can be pasted into another application as plain text and images. The copied material can be pasted into another application as plain text and images.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More