Sync to trunk.

This commit is contained in:
John Schember 2009-08-22 18:35:34 -04:00
commit fd1e0711e0
59 changed files with 23565 additions and 17407 deletions

View File

@ -91,7 +91,6 @@ class BuildEXE(py2exe.build_exe.py2exe):
print
print 'Adding third party dependencies'
print '\tAdding devcon'
tdir = os.path.join(PY2EXE_DIR, 'driver')
os.makedirs(tdir)
for pat in ('*.dll', '*.sys', '*.cat', '*.inf'):
@ -104,11 +103,6 @@ class BuildEXE(py2exe.build_exe.py2exe):
'bin\\freetype.dll', 'bin\\jpeg62.dll'):
shutil.copyfile(os.path.join(POPPLER, x),
os.path.join(PY2EXE_DIR, os.path.basename(x)))
#shutil.copyfile(PDFTOHTML, os.path.join(PY2EXE_DIR, os.path.basename(PDFTOHTML)))
#shutil.copyfile(PDFTOHTML+'.manifest', os.path.join(PY2EXE_DIR,
# os.path.basename(PDFTOHTML)+'.manifest'))
#print '\tAdding pdftk'
#shutil.copyfile(PDFTK, os.path.join(PY2EXE_DIR, os.path.basename(PDFTK)))
print '\tAdding podofo'
for f in glob.glob(os.path.join(PODOFO, '*.dll')):
shutil.copyfile(f, os.path.join(PY2EXE_DIR, os.path.basename(f)))

View File

@ -2,7 +2,7 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
__appname__ = 'calibre'
__version__ = '0.6.7'
__version__ = '0.6.8'
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
import re

View File

@ -312,6 +312,8 @@ class EPUBOutput(OutputFormatPlugin):
for tag in XPath('//h:script')(root):
if not tag.text and not tag.get('src', False):
tag.getparent().remove(tag)
for tag in XPath('//h:body/descendant::h:script')(root):
tag.getparent().remove(tag)
for tag in XPath('//h:form')(root):
tag.getparent().remove(tag)

View File

@ -95,7 +95,8 @@ def option_parser():
writers = set([])
for w in metadata_writers():
writers = writers.union(set(w.file_types))
return config().option_parser(USAGE%(list(filetypes()), list(writers)))
ft, w = ', '.join(filetypes()), ', '.join(writers)
return config().option_parser(USAGE%(ft, w))
def do_set_metadata(opts, mi, stream, stream_type):
mi = MetaInformation(mi)

View File

@ -2,12 +2,13 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import os, re, time, textwrap
from PyQt4.Qt import QDialog, QMessageBox, QListWidgetItem, QIcon, \
from PyQt4.Qt import QDialog, QListWidgetItem, QIcon, \
QDesktopServices, QVBoxLayout, QLabel, QPlainTextEdit, \
QStringListModel, QAbstractItemModel, QFont, \
SIGNAL, QThread, Qt, QSize, QVariant, QUrl, \
QModelIndex, QInputDialog, QAbstractTableModel, \
QDialogButtonBox, QTabWidget, QBrush, QLineEdit
QDialogButtonBox, QTabWidget, QBrush, QLineEdit, \
QProgressDialog
from calibre.constants import islinux, iswindows
from calibre.gui2.dialogs.config.config_ui import Ui_Dialog
@ -648,7 +649,7 @@ class ConfigDialog(QDialog, Ui_Dialog):
QDesktopServices.openUrl(QUrl('http://127.0.0.1:'+str(self.port.value())))
def compact(self, toggled):
d = Vacuum(self, self.db)
d = CheckIntegrity(self.db, self)
d.exec_()
def browse(self):
@ -739,25 +740,48 @@ class VacThread(QThread):
self._parent = parent
def run(self):
bad = self.db.check_integrity()
self.emit(SIGNAL('check_done(PyQt_PyObject)'), bad)
err = bad = None
try:
bad = self.db.check_integrity(self.callback)
except:
import traceback
err = traceback.format_exc()
self.emit(SIGNAL('check_done(PyQt_PyObject, PyQt_PyObject)'), bad, err)
class Vacuum(QMessageBox):
def callback(self, progress, msg):
self.emit(SIGNAL('callback(PyQt_PyObject,PyQt_PyObject)'), progress,
msg)
class CheckIntegrity(QProgressDialog):
def __init__(self, db, parent=None):
QProgressDialog.__init__(self, parent)
self.setCancelButtonText('')
self.setMinimum(0)
self.setMaximum(100)
self.setWindowTitle(_('Checking database integrity'))
self.setAutoReset(False)
self.setValue(0)
def __init__(self, parent, db):
self.db = db
QMessageBox.__init__(self, QMessageBox.Information, _('Checking...'),
_('Checking database integrity. This may take a while.'),
QMessageBox.NoButton, parent)
self.vthread = VacThread(self, db)
self.connect(self.vthread, SIGNAL('check_done(PyQt_PyObject)'),
self.connect(self.vthread, SIGNAL('check_done(PyQt_PyObject,PyQt_PyObject)'),
self.check_done,
Qt.QueuedConnection)
self.connect(self.vthread,
SIGNAL('callback(PyQt_PyObject,PyQt_PyObject)'),
self.callback, Qt.QueuedConnection)
self.vthread.start()
def callback(self, progress, msg):
self.setLabelText(msg)
self.setValue(int(100*progress))
def check_done(self, bad):
if bad:
def check_done(self, bad, err):
if err:
error_dialog(self, _('Error'),
_('Failed to check database integrity'),
det_msg=err, show=True)
elif bad:
titles = [self.db.title(x, index_is_id=True) for x in bad]
det_msg = '\n'.join(titles)
warning_dialog(self, _('Some inconsistencies found'),
@ -767,7 +791,7 @@ class Vacuum(QMessageBox):
'You should check them manually. This can '
'happen if you manipulate the files in the '
'library folder directly.'), det_msg=det_msg, show=True)
self.accept()
self.reset()

View File

@ -92,7 +92,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
def select_cover(self, checked):
files = choose_images(self, 'change cover dialog',
u'Choose cover for ' + qstring_to_unicode(self.title.text()))
_('Choose cover for ') + unicode(self.title.text()))
if not files:
return
_file = files[0]
@ -203,7 +203,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
if mi.pubdate:
self.pubdate.setDate(QDate(mi.pubdate.year, mi.pubdate.month,
mi.pubdate.day))
if mi.series:
if mi.series and mi.series.strip():
self.series.setEditText(mi.series)
if mi.series_index is not None:
self.series_index.setValue(float(mi.series_index))

View File

@ -303,7 +303,7 @@ class SchedulerDialog(QDialog, Ui_Dialog):
username, password = username.strip(), password.strip()
recipe = self._model.data(self.recipes.currentIndex(), Qt.UserRole)
key = 'recipe_account_info_%s'%recipe.id
config[key] = (username, password) if username and password else None
config[key] = (username, password) if username else None
def do_schedule(self, *args):
if not getattr(self, 'allow_scheduling', False):

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 B

View File

@ -1046,10 +1046,10 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
############################## Save to disk ################################
def save_single_format_to_disk(self, checked):
self.save_to_disk(checked, True, prefs['output_format'])
self.save_to_disk(checked, False, prefs['output_format'])
def save_specific_format_disk(self, fmt):
self.save_to_disk(False, True, fmt)
self.save_to_disk(False, False, fmt)
def save_to_single_dir(self, checked):
self.save_to_disk(checked, True)
@ -1071,6 +1071,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
if single_format is not None:
opts.formats = single_format
if single_dir:
opts.template = opts.template.split('/')[-1].strip()
if not opts.template:
opts.template = '{title} - {authors}'
self._saver = Saver(self, self.library_view.model().db,
Dispatcher(self._books_saved), rows, path, opts,

View File

@ -368,6 +368,7 @@ class DocumentView(QWebView):
self.setPage(self.document)
self.manager = None
self._reference_mode = False
self._ignore_scrollbar_signals = False
self.connect(self.document, SIGNAL('loadStarted()'), self.load_started)
self.connect(self.document, SIGNAL('loadFinished(bool)'), self.load_finished)
self.connect(self.document, SIGNAL('linkClicked(QUrl)'), self.link_clicked)
@ -467,13 +468,16 @@ class DocumentView(QWebView):
if getattr(self, 'scrollbar', None) is not None:
delta = self.document.width - self.size().width()
if delta > 0:
self._ignore_scrollbar_signals = True
self.scrollbar.blockSignals(True)
self.scrollbar.setRange(0, delta)
self.scrollbar.setValue(0)
self.scrollbar.setSingleStep(1)
self.scrollbar.setPageStep(int(delta/10.))
self.scrollbar.blockSignals(False)
self.scrollbar.setVisible(delta > 0)
self.scrollbar.blockSignals(False)
self._ignore_scrollbar_signals = False
def load_finished(self, ok):
self._size_hint = self.document.mainFrame().contentsSize()
@ -565,6 +569,8 @@ class DocumentView(QWebView):
self.manager.scrolled(self.scroll_fraction)
def scroll_to(self, pos, notify=True):
if self._ignore_scrollbar_signals:
return
old_pos = self.document.ypos
if isinstance(pos, basestring):
self.document.jump_to_anchor(pos)
@ -572,8 +578,9 @@ class DocumentView(QWebView):
if pos >= 1:
self.document.scroll_to(0, self.document.height)
else:
self.document.scroll_to(0, int(math.ceil(
pos*(self.document.height-self.document.window_height))))
y = int(math.ceil(
pos*(self.document.height-self.document.window_height)))
self.document.scroll_to(0, y)
if notify and self.manager is not None and self.document.ypos != old_pos:
self.manager.scrolled(self.scroll_fraction)

View File

@ -23,7 +23,7 @@ from PyQt4.QtGui import QImage
from calibre.ebooks.metadata import title_sort
from calibre.library.database import LibraryDatabase
from calibre.library.sqlite import connect, IntegrityError
from calibre.library.sqlite import connect, IntegrityError, DBThread
from calibre.utils.search_query_parser import SearchQueryParser
from calibre.ebooks.metadata import string_to_authors, authors_to_string, \
MetaInformation, authors_to_sort_string
@ -1670,9 +1670,40 @@ books_series_link feeds
return duplicates
def check_integrity(self):
def check_integrity(self, callback):
callback(0., _('Checking SQL integrity...'))
user_version = self.user_version
sql = self.conn.dump()
self.conn.close()
dest = self.dbpath+'.old'
if os.path.exists(dest):
os.remove(dest)
shutil.copyfile(self.dbpath, dest)
try:
os.remove(self.dbpath)
ndb = DBThread(self.dbpath, None)
ndb.connect()
conn = ndb.conn
conn.executescript(sql)
conn.commit()
conn.execute('pragma user_version=%d'%user_version)
conn.commit()
conn.close()
except:
if os.path.exists(self.dbpath):
os.remove(self.dbpath)
shutil.copyfile(dest, self.dbpath)
os.remove(dest)
raise
else:
os.remove(dest)
self.connect()
self.refresh()
callback(0.1, _('Checking for missing files.'))
bad = {}
for id in self.data.universal_set():
us = self.data.universal_set()
total = float(len(us))
for i, id in enumerate(us):
formats = self.data.get(id, FIELD_MAP['formats'], row_is_id=True)
if not formats:
formats = []
@ -1692,6 +1723,7 @@ books_series_link feeds
if id not in bad:
bad[id] = []
bad[id].append(fmt)
callback(0.1+0.9*(1+i)/total, _('Checked id') + ' %d'%id)
for id in bad:
for fmt in bad[id]:
@ -1699,8 +1731,6 @@ books_series_link feeds
self.conn.commit()
self.refresh_ids(list(bad.keys()))
self.vacuum()
return bad

View File

@ -105,6 +105,15 @@ class LibraryServer(object):
</entry>
'''))
STANZA_AUTHOR_ENTRY=MarkupTemplate(textwrap.dedent('''\
<entry xmlns:py="http://genshi.edgewall.org/">
<title>${authors}</title>
<id>urn:calibre:${record[FM['id']]}</id>
<updated>${timestamp}</updated>
<link type="application/atom+xml" href="/?authorid=${record[FM['id']]}" />
</entry>
'''))
STANZA = MarkupTemplate(textwrap.dedent('''\
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:py="http://genshi.edgewall.org/">
@ -330,24 +339,30 @@ class LibraryServer(object):
@expose
def stanza(self, search=None, sortby=None):
def stanza(self, search=None, sortby=None, authorid=None):
'Feeds to read calibre books on a ipod with stanza.'
books = []
updated = self.db.last_modified()
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
cherrypy.response.headers['Content-Type'] = 'text/xml'
if not sortby and not search:
if not sortby and not search and not authorid:
return self.STANZA_MAIN.generate(subtitle='', data=books, FM=FIELD_MAP,
updated=updated, id='urn:calibre:main').render('xml')
if authorid:
authorid=int(authorid)
au = self.db.authors(authorid, index_is_id=True)
ids = self.db.data.get_matches('authors', au)
else:
ids = self.db.data.parse(search) if search and search.strip() else self.db.data.universal_set()
record_list = list(iter(self.db))
if sortby == "byauthor":
record_list.sort(lambda x, y: cmp(x[FIELD_MAP['author_sort']], y[FIELD_MAP['author_sort']]))
elif sortby == "bytitle":
elif sortby == "bytitle" or authorid:
record_list.sort(lambda x, y: cmp(title_sort(x[FIELD_MAP['title']]),
title_sort(y[FIELD_MAP['title']])))
else:
record_list = reversed(record_list)
author_list=[]
for record in record_list:
if record[0] not in ids: continue
r = record[FIELD_MAP['formats']]
@ -374,6 +389,19 @@ class LibraryServer(object):
fmt_sidx(float(record[FIELD_MAP['series_index']]))))
fmt = 'epub' if 'EPUB' in r else 'pdb'
mimetype = guess_type('dummy.'+fmt)[0]
if sortby == "byauthor":
if authors and authors not in author_list:
author_list.append(authors)
books.append(self.STANZA_AUTHOR_ENTRY.generate(
authors=authors,
record=record, FM=FIELD_MAP,
port=self.opts.port,
extra=''.join(extra),
mimetype=mimetype,
fmt=fmt,
timestamp=strftime('%Y-%m-%dT%H:%M:%S+00:00', record[5]),
).render('xml').decode('utf8'))
else:
books.append(self.STANZA_ENTRY.generate(
authors=authors,
record=record, FM=FIELD_MAP,
@ -434,7 +462,7 @@ class LibraryServer(object):
'The / URL'
want_opds = cherrypy.request.headers.get('Stanza-Device-Name', 919) != \
919 or cherrypy.request.headers.get('Want-OPDS-Catalog', 919) != 919
return self.stanza(search=kwargs.get('search', None), sortby=kwargs.get('sortby',None)) if want_opds else self.static('index.html')
return self.stanza(search=kwargs.get('search', None), sortby=kwargs.get('sortby',None), authorid=kwargs.get('authorid',None)) if want_opds else self.static('index.html')
@expose

View File

@ -130,6 +130,12 @@ class DBThread(Thread):
if func == self.CLOSE:
self.conn.close()
break
if func == 'dump':
try:
ok, res = True, '\n'.join(self.conn.iterdump())
except Exception, err:
ok, res = False, (err, traceback.format_exc())
else:
func = getattr(self.conn, func)
try:
ok, res = True, func(*args, **kwargs)
@ -197,6 +203,9 @@ class ConnectionProxy(object):
@proxy
def cursor(self): pass
@proxy
def dump(self): pass
def connect(dbpath, row_factory=None):
conn = ConnectionProxy(DBThread(dbpath, row_factory))
conn.proxy.start()

View File

@ -29,6 +29,20 @@ html:
@echo
@echo "Build finished. The HTML pages are in .build/html."
qthelp:
mkdir -p .build/qthelp .build/doctrees
$(SPHINXBUILD) -b customqt $(ALLSPHINXOPTS) .build/qthelp
@echo
@echo "Build finished."
epub:
mkdir -p .build/qthelp .build/doctrees
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) .build/epub
@echo
@echo "Build finished."
web:
mkdir -p .build/web .build/doctrees
$(SPHINXBUILD) -b web $(ALLSPHINXOPTS) .build/web

View File

@ -91,12 +91,15 @@ html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
html_use_smartypants = True
# Overall title of the documentation
html_title = 'calibre User Manual'
html_short_title = 'Start'
html_logo = 'resources/logo.png'
epub_titlepage = 'resources/titlepage.html'
epub_logo = 'resources/logo.png'
epub_author = 'Kovid Goyal'
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
@ -105,8 +108,8 @@ html_logo = 'resources/logo.png'
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
# html_use_modindex = True
html_use_modindex = False
html_use_index = False
# If true, the reST sources are included in the HTML build as _sources/<name>.
html_copy_source = False
@ -116,6 +119,8 @@ htmlhelp_basename = 'calibredoc'
html_use_opensearch = 'http://calibre.kovidgoyal.net/user_manual'
html_show_sphinx = False
# Options for LaTeX output
# ------------------------

View File

@ -6,156 +6,143 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import sys, os, inspect, re, textwrap
from sphinx.builder import StandaloneHTMLBuilder
from qthelp import QtHelpBuilder
from epub import EPUBHelpBuilder
from sphinx.util import rpartition
from sphinx.util.console import bold
from sphinx.ext.autodoc import prepare_docstring
from docutils.statemachine import ViewList
from docutils import nodes
from genshi.template import OldTextTemplate as TextTemplate
sys.path.append(os.path.abspath('../../../'))
from calibre.linux import entry_points
class CustomBuilder(StandaloneHTMLBuilder):
name = 'custom'
class CustomQtBuild(QtHelpBuilder):
name = 'customqt'
def substitute(app, doctree):
pass
CLI_INDEX = '''\
CLI_INDEX='''
.. include:: ../global.rst
||
.. _cli:
||
||
Command Line Interface
==========================
||
.. image:: ../images/cli.png
||
||
Documented Commands
--------------------
||
.. toctree::
:maxdepth: 1
||
#for cmd, parser in documented_commands
$cmd
#end
||
{documented}
Undocumented Commands
-------------------------
||
#for cmd in undocumented_commands
* ${cmd}
||
#end
||
You can see usage for undocumented commands by executing them without arguments in a terminal
{undocumented}
You can see usage for undocumented commands by executing them without arguments
in a terminal.
'''
CLI_CMD=r'''
CLI_PREAMBLE='''\
.. include:: ../global.rst
||
.. _$cmd:
||
$cmd
====================================================================
||
.. _{cmd}:
{cmd}
===================================================================
.. code-block:: none
||
$cmdline
||
#for line in usage
#choose
#when len(line) > 0
$line
#end
#otherwise
||
#end
#end
#end
||
'''
CLI_GROUPS=r'''
[options]
------------
||
#def option(opt)
:option:`${opt.get_opt_string() + ((', '+', '.join(opt._short_opts)) if opt._short_opts else '')}`
#end
#for title, desc, options in groups
#if title
$title
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
||
#end
#if desc
$desc
||
#end
#for opt in options
${option(opt)}
${opt.help.replace('\n', ' ').replace('*', '\\*').replace('%default', str(opt.default)) if opt.help else ''}
||
#end
#end
{cmdline}
{usage}
'''
EBOOK_CONVERT = CLI_CMD + r'''
$groups
'''
CLI_CMD += CLI_GROUPS
def generate_ebook_convert_help():
def generate_ebook_convert_help(preamble, info):
from calibre.ebooks.conversion.cli import create_option_parser
from calibre.customize.ui import input_format_plugins, output_format_plugins
from calibre.utils.logging import default_log
ans = textwrap.dedent('''
preamble = re.sub(r'http.*\.html', ':ref:`conversion`', preamble)
raw = preamble + textwrap.dedent('''
Since the options supported by ebook-convert vary depending on both the
input and the output formats, the various combinations are listed below:
''')
c = 0
sections = []
toc = {}
for ip in input_format_plugins():
toc[ip.name] = []
sec_templ = textwrap.dedent('''\
.. include:: ../global.rst
{0}
================================================================
.. contents:: Contents
:depth: 1
:local:
''')
for i, ip in enumerate(input_format_plugins()):
path = os.path.join('cli', 'ebook-convert-%d.rst'%i)
sraw = sec_templ.format(ip.name)
toc[ip.name] = 'ebook-convert-%d'%i
for op in output_format_plugins():
c += 1
idr = 'ebook-convert-sec-'+str(c)
title = ip.name + ' to ' + op.name
section = '.. _'+idr+':||||'
section += title+'||'+\
'-------------------------------------------------------'
toc[ip.name].append([idr, op.name])
parser, plumber = create_option_parser(['ebook-convert',
'dummyi.'+list(ip.file_types)[0],
'dummyo.'+op.file_type, '-h'], default_log)
cmd = 'ebook-convert '+list(ip.file_types)[0]+' '+op.file_type
groups = [(None, None, parser.option_list)]
for grp in parser.option_groups:
groups.append((grp.title, grp.description, grp.option_list))
template = str(CLI_GROUPS)
template = TextTemplate(template[template.find('||'):])
section += template.generate(groups=groups).render()
options = '\n'.join(render_options(cmd, groups, False))
sraw += title+'\n------------------------------------------------------\n\n'
sraw += options + '\n\n'
update_cli_doc(os.path.join('cli', toc[ip.name]+'.rst'), sraw, info)
sections.append(section)
toct = '||||'
toct = '\n\n.. toctree::\n :maxdepth: 2\n\n'
for ip in sorted(toc):
toct += ' * '+ip+'||||'
for idr, name in toc[ip]:
toct += ' * :ref:`'+name +' <'+idr+'>`||'
toct += '||'
toct += ' ' + toc[ip]+'\n'
ans += toct+'||||'+'||||'.join(sections)
raw += toct+'\n\n'
update_cli_doc(os.path.join('cli', 'ebook-convert.rst'), raw, info)
return ans
def update_cli_doc(path, raw, info):
if isinstance(raw, unicode):
raw = raw.encode('utf-8')
if not os.path.exists(path) or open(path, 'rb').read() != raw:
info('creating '+os.path.splitext(os.path.basename(path))[0])
open(path, 'wb').write(raw)
def render_options(cmd, groups, options_header=True):
lines = []
if options_header:
lines = ['[options]', '-'*15, '']
lines += ['.. program:: '+cmd, '']
for title, desc, options in groups:
if title:
lines.extend([title, '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'])
lines.append('')
if desc:
lines.extend([desc, ''])
for opt in options:
help = opt.help if opt.help else ''
help = help.replace('\n', ' ').replace('*', '\\*').replace('%default', str(opt.default))
opt = opt.get_opt_string() + ((', '+', '.join(opt._short_opts)) if opt._short_opts else '')
opt = '.. cmdoption:: '+opt
lines.extend([opt, '', ' '+help, ''])
return lines
def cli_docs(app):
info = app.builder.info
@ -175,36 +162,32 @@ def cli_docs(app):
documented_cmds.sort(cmp=lambda x, y: cmp(x[0], y[0]))
undocumented_cmds.sort()
templ = TextTemplate(CLI_INDEX)
raw = templ.generate(documented_commands=documented_cmds,
undocumented_commands=undocumented_cmds).render()
raw = raw.replace('||', '\n')
documented = [' '*4 + cmd[0] for cmd in documented_cmds]
undocumented = [' * ' + cmd for cmd in undocumented_cmds]
raw = CLI_INDEX.format(documented='\n'.join(documented),
undocumented='\n'.join(undocumented))
if not os.path.exists('cli'):
os.makedirs('cli')
if not os.path.exists(os.path.join('cli', 'global.rst')):
os.link('global.rst', os.path.join('cli', 'global.rst'))
if not os.path.exists(os.path.join('cli', 'cli-index.rst')):
info(bold('creating cli-index...'))
open(os.path.join('cli', 'cli-index.rst'), 'wb').write(raw)
update_cli_doc(os.path.join('cli', 'cli-index.rst'), raw, info)
templ = TextTemplate(CLI_CMD)
for cmd, parser in documented_cmds:
usage = [i for i in parser.usage.replace('%prog', cmd).splitlines()]
cmdline = usage[0]
usage = usage[1:]
usage = [i.replace(cmd, ':command:`%s`'%cmd) for i in usage]
usage = '\n'.join(usage)
preamble = CLI_PREAMBLE.format(cmd=cmd, cmdline=cmdline, usage=usage)
if cmd == 'ebook-convert':
generate_ebook_convert_help(preamble, info)
else:
groups = [(None, None, parser.option_list)]
for grp in parser.option_groups:
groups.append((grp.title, grp.description, grp.option_list))
if cmd == 'ebook-convert':
groups = generate_ebook_convert_help()
templ = TextTemplate(EBOOK_CONVERT)
raw = templ.generate(cmd=cmd, cmdline=cmdline, usage=usage, groups=groups).render()
raw = raw.replace('||', '\n').replace('&lt;', '<').replace('&gt;', '>')
if not os.path.exists(os.path.join('cli', cmd+'.rst')):
info(bold('creating docs for %s...'%cmd))
open(os.path.join('cli', cmd+'.rst'), 'wb').write(raw)
raw = preamble
lines = render_options(cmd, groups)
raw += '\n'+'\n'.join(lines)
update_cli_doc(os.path.join('cli', cmd+'.rst'), raw, info)
def auto_member(dirname, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
@ -258,7 +241,12 @@ def auto_member(dirname, arguments, options, content, lineno,
return list(node)
def setup(app):
app.add_config_value('epub_titlepage', None, False)
app.add_config_value('epub_author', '', False)
app.add_config_value('epub_logo', None, False)
app.add_builder(CustomBuilder)
app.add_builder(CustomQtBuild)
app.add_builder(EPUBHelpBuilder)
app.add_directive('automember', auto_member, 1, (1, 0, 1))
app.connect('doctree-read', substitute)
app.connect('builder-inited', cli_docs)

View File

@ -112,3 +112,8 @@ Metadata plugins
-------------------
Metadata plugins add the ability to read/write metadata from ebook files to |app|. See :ref:`pluginsMetadataPlugin` for details.
.. toctree::
:hidden:
plugins

280
src/calibre/manual/epub.py Normal file
View File

@ -0,0 +1,280 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os, mimetypes, uuid, shutil
from datetime import datetime
from docutils import nodes
from xml.sax.saxutils import escape, quoteattr
from urlparse import urldefrag
from zipfile import ZipFile, ZIP_STORED, ZipInfo
from sphinx import addnodes
from sphinx.builders.html import StandaloneHTMLBuilder
NCX = '''\
<?xml version="1.0" encoding="UTF-8"?>
<ncx version="2005-1"
xml:lang="en"
xmlns="http://www.daisy.org/z3986/2005/ncx/"
xmlns:calibre="http://calibre.kovidgoyal.net/2009/metadata"
>
<head>
<meta name="dtb:uid" content="{uid}"/>
<meta name="dtb:depth" content="{depth}"/>
<meta name="dtb:generator" content="sphinx"/>
<meta name="dtb:totalPageCount" content="0"/>
<meta name="dtb:maxPageNumber" content="0"/>
</head>
<docTitle><text>Table of Contents</text></docTitle>
<navMap>
{navpoints}
</navMap>
</ncx>
'''
OPF = '''\
<?xml version="1.0" encoding="UTF-8"?>
<package version="2.0"
xmlns="http://www.idpf.org/2007/opf"
unique-identifier="sphinx_id"
>
<metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf" xmlns:calibre="http://calibre.kovidgoyal.net/2009/metadata">
<dc:title>{title}</dc:title>
<dc:creator opf:role="aut">{author}</dc:creator>
<dc:contributor opf:role="bkp">Sphinx</dc:contributor>
<dc:identifier opf:scheme="sphinx" id="sphinx_id">{uid}</dc:identifier>
<dc:date>{date}</dc:date>
<meta name="calibre:publication_type" content="sphinx_manual" />
</metadata>
<manifest>
{manifest}
</manifest>
<spine toc="ncx">
{spine}
</spine>
<guide>
{guide}
</guide>
</package>
'''
CONTAINER='''\
<?xml version="1.0"?>
<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
<rootfiles>
<rootfile full-path="{0}" media-type="application/oebps-package+xml"/>
</rootfiles>
</container>
'''
class TOC(list):
def __init__(self, title=None, href=None):
list.__init__(self)
self.title, self.href = title, href
def create_child(self, title, href):
self.append(TOC(title, href))
return self[-1]
def depth(self):
try:
return max(node.depth() for node in self)+1
except ValueError:
return 1
class EPUBHelpBuilder(StandaloneHTMLBuilder):
"""
Builder that also outputs Qt help project, contents and index files.
"""
name = 'epub'
# don't copy the reST source
copysource = False
supported_image_types = ['image/svg+xml', 'image/png', 'image/gif',
'image/jpeg']
# don't add links
add_permalinks = False
# don't add sidebar etc.
embedded = True
def init(self):
StandaloneHTMLBuilder.init(self)
self.out_suffix = '.html'
self.link_suffix = '.html'
self.html_outdir = self.outdir = os.path.join(self.outdir, 'src')
self.conf = self.config
def finish(self):
StandaloneHTMLBuilder.finish(self)
self.create_titlepage()
self.outdir = os.path.dirname(self.outdir)
cwd = os.getcwd()
os.chdir(self.html_outdir)
try:
self.generate_manifest()
self.generate_toc()
self.render_opf()
self.render_epub()
finally:
os.chdir(cwd)
def render_epub(self):
container = CONTAINER.format('content.opf')
path = os.path.abspath('..'+os.sep+self.conf.project+'.epub')
zf = ZipFile(path, 'w')
zi = ZipInfo('mimetype')
zi.compress_type = ZIP_STORED
zf.writestr(zi, 'application/epub+zip')
zf.writestr('META-INF/container.xml', container)
for url in self.manifest:
fp = os.path.join(self.html_outdir, *url.split('/'))
zf.write(fp, url)
zf.close()
self.info('EPUB created at: '+path)
def render_opf(self):
manifest = []
for href in self.manifest:
mt, id = self.manifest[href]
manifest.append(' '*8 + '<item id=%s href=%s media-type=%s />'%\
tuple(map(quoteattr, (id, href, mt))))
manifest = '\n'.join(manifest)
spine = [' '*8+'<itemref idref=%s />'%quoteattr(x) for x in self.spine]
spine = '\n'.join(spine)
guide = ''
if self.conf.epub_titlepage:
guide = ' '*8 + '<reference type="cover" href="_static/titlepage.html" />'
opf = OPF.format(title=escape(self.conf.html_title),
author=escape(self.conf.epub_author), uid=str(uuid.uuid4()),
date=datetime.now().isoformat(), manifest=manifest, spine=spine,
guide=guide)
open('content.opf', 'wb').write(opf)
self.manifest['content.opf'] = ('application/oebps-package+xml', 'opf')
def create_titlepage(self):
if self.conf.epub_titlepage:
img = ''
if self.conf.epub_logo:
img = '_static/epub_logo'+os.path.splitext(self.conf.epub_logo)[1]
shutil.copyfile(self.conf.epub_logo,
os.path.join(self.html_outdir, *img.split('/')))
raw = open(self.conf.epub_titlepage, 'rb').read()
raw = raw%dict(title=self.conf.html_title,
version=self.conf.version,
img=img.split('/')[-1],
author=self.conf.epub_author)
open(os.path.join(self.html_outdir, '_static', 'titlepage.html'), 'wb').write(raw)
def generate_manifest(self):
self.manifest = {}
id = 1
for dirpath, dirnames, filenames in os.walk('.'):
for fname in filenames:
if fname == '.buildinfo':
continue
fpath = os.path.abspath(os.path.join(dirpath, fname))
url = os.path.relpath(fpath).replace(os.sep, '/')
self.manifest[url] = mimetypes.guess_type(url, False)[0]
if self.manifest[url] is None:
self.warn('Unknown mimetype for: ' + url)
self.manifest[url] = 'application/octet-stream'
if self.manifest[url] == 'text/html':
self.manifest[url] = 'application/xhtml+xml'
self.manifest[url] = (self.manifest[url], 'id'+str(id))
id += 1
def isdocnode(self, node):
if not isinstance(node, nodes.list_item):
return False
if len(node.children) != 2:
return False
if not isinstance(node.children[0], addnodes.compact_paragraph):
return False
if not isinstance(node.children[0][0], nodes.reference):
return False
if not isinstance(node.children[1], nodes.bullet_list):
return False
return True
def generate_toc(self):
tocdoc = self.env.get_and_resolve_doctree(self.config.master_doc, self,
prune_toctrees=False)
istoctree = lambda node: (
isinstance(node, addnodes.compact_paragraph)
and node.has_key('toctree'))
toc = TOC()
for node in tocdoc.traverse(istoctree):
self.extend_toc(toc, node)
self._parts = []
self._po = 0
self._po_map = {}
self.spine_map = {}
self.spine = []
self.render_toc(toc)
navpoints = '\n'.join(self._parts).strip()
ncx = NCX.format(uid=str(uuid.uuid4()), depth=toc.depth(),
navpoints=navpoints)
open('toc.ncx', 'wb').write(ncx)
self.manifest['toc.ncx'] = ('application/x-dtbncx+xml', 'ncx')
self.spine.insert(0, self.manifest[self.conf.master_doc+'.html'][1])
if self.conf.epub_titlepage:
self.spine.insert(0, self.manifest['_static/titlepage.html'][1])
def add_to_spine(self, href):
href = urldefrag(href)[0]
if href not in self.spine_map:
for url in self.manifest:
if url == href:
self.spine_map[href]= self.manifest[url][1]
self.spine.append(self.spine_map[href])
def render_toc(self, toc, level=2):
for child in toc:
if child.title and child.href:
href = child.href
self.add_to_spine(href)
title = escape(child.title)
if isinstance(title, unicode):
title = title.encode('utf-8')
if child.href in self._po_map:
po = self._po_map[child.href]
else:
self._po += 1
po = self._po
self._parts.append(' '*(level*4)+
'<navPoint id="%s" playOrder="%d">'%(uuid.uuid4(),
po))
self._parts.append(' '*((level+1)*4)+
'<navLabel><text>%s</text></navLabel>'%title)
self._parts.append(' '*((level+1)*4)+
'<content src=%s />'%quoteattr(href))
self.render_toc(child, level+1)
self._parts.append(' '*(level*4)+'</navPoint>')
def extend_toc(self, toc, node):
if self.isdocnode(node):
refnode = node.children[0][0]
parent = toc.create_child(refnode.astext(), refnode['refuri'])
for subnode in node.children[1]:
self.extend_toc(parent, subnode)
elif isinstance(node, (nodes.list_item, nodes.bullet_list,
addnodes.compact_paragraph)):
for subnode in node:
self.extend_toc(toc, subnode)
elif isinstance(node, nodes.reference):
parent = toc.create_child(node.astext(), node['refuri'])

View File

@ -64,7 +64,7 @@ There are two aspects to this problem:
How do I use some of the advanced features of the conversion tools?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can get help on any individual feature of the converters by mousing over it in the GUI or running ``html2lrf --help`` at a terminal. A good place to start is to look at the following demo files that demonstrate some of the advanced features:
You can get help on any individual feature of the converters by mousing over it in the GUI or running ``ebook-convert dummy.html .epub -h`` at a terminal. A good place to start is to look at the following demo files that demonstrate some of the advanced features:
* `html-demo.zip <http://calibre.kovidgoyal.net/downloads/html-demo.zip>`_
* `txt-demo.zip <http://calibre.kovidgoyal.net/downloads/txt-demo.zip>`_
@ -78,7 +78,7 @@ Device Integration
What devices does |app| support?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
At the moment |app| has full support for the SONY PRS 500/505/700, Cybook Gen 3, Amazon Kindle 1/2/DX, Netronix EB600, Ectaco Jetbook, BeBook/BeBook Mini and the iPhone. In addition, using the :guilabel:`Save to disk` function you can use it with any ebook reader that exports itself as a USB disk.
At the moment |app| has full support for the SONY PRS 500/505/700, Cybook Gen 3/Opus, Amazon Kindle 1/2/DX, Netronix EB600, Ectaco Jetbook, BeBook/BeBook Mini, Irex Illiad/DR1000, Adroid phones and the iPhone. In addition, using the :guilabel:`Save to disk` function you can use it with any ebook reader that exports itself as a USB disk.
I used |app| to transfer some books to my reader, and now the SONY software hangs every time I connect the reader?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -121,7 +121,7 @@ Library Management
What formats does |app| read metadata from?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|app| reads metadata from the following formats: LRF, PDF, LIT, RTF, OPF, MOBI, PRC, EPUB, FB2, IMP, RB, HTML. In addition it can write metadata to: LRF, RTF, OPF
|app| reads metadata from the following formats: LRF, PDF, LIT, RTF, OPF, MOBI, PRC, EPUB, FB2, IMP, RB, HTML. In addition it can write metadata to: LRF, RTF, OPF, EPUB, PDF, MOBI
Where are the book files stored?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -5,18 +5,22 @@
|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.
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 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:
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:
.. 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. For the SONY Reader that's the LRF format. 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 hourglass in the bottom right corner will start spinning. Once it's finished spinning, your converted book is ready. Click to "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 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 hourglass 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 hourglass stops spinning again, disconnect your reader and read away!
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 hourglass 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>`.
You will find the list of :ref:`Frequently Asked Questions <faq>` useful as well.
.. only:: html and online
An e-book version of this User Manual is available in `EPUB format <calibre.epub>`_. Because the User Manual uses advanced formatting, it is only suitable for use with the |app| e-book viewer.
Sections
------------
@ -25,22 +29,12 @@ Sections
gui
news
cli/cli-index
conversion
metadata
faq
xpath
customize
cli/cli-index
glossary
Convenience
-----------------------
:ref:`search`: Search the help
:ref:`genindex`

View File

@ -101,6 +101,8 @@ Pre/post processing of downloaded HTML
.. automember:: BasicNewsRecipe.remove_tags_before
.. automember:: BasicNewsRecipe.remove_attributes
.. automember:: BasicNewsRecipe.keep_only_tags
.. automember:: BasicNewsRecipe.preprocess_regexps

View File

@ -0,0 +1,296 @@
# -*- coding: utf-8 -*-
import os
import re
import cgi
import subprocess
from os import path
from docutils import nodes
from sphinx import addnodes
from sphinx.builders.html import StandaloneHTMLBuilder
_idpattern = re.compile(
r'(?P<title>.+) (\((?P<id>[\w\.]+)( (?P<descr>\w+))?\))$')
# Qt Help Collection Project (.qhcp).
# Is the input file for the help collection generator.
# It contains references to compressed help files which should be
# included in the collection.
# It may contain various other information for customizing Qt Assistant.
collection_template = '''\
<?xml version="1.0" encoding="utf-8" ?>
<QHelpCollectionProject version="1.0">
<docFiles>
<generate>
<file>
<input>%(outname)s.qhp</input>
<output>%(outname)s.qch</output>
</file>
</generate>
<register>
<file>%(outname)s.qch</file>
</register>
</docFiles>
<assistant>
<title>calibre User Manual</title>
<applicationIcon>_static/logo.png</applicationIcon>
<enableDocumentationManager>false</enableDocumentationManager>
<enableAddressBar visible="false">true</enableAddressBar>
<cacheDirectory>calibre/user_manual</cacheDirectory>
<aboutMenuText>
<text>About calibre</text>
</aboutMenuText>
<aboutDialog>
<file>about.txt</file>
<icon>_static/logo.png</icon>
</aboutDialog>
</assistant>
</QHelpCollectionProject>
'''
ABOUT='''\
calibre is the one stop solution for all your e-book needs. It was created
originally by Kovid Goyal, to help him manage his e-book collection and is now
very actively developed by an international community of e-book enthusiasts.
Its goal is to empower you, the user, to do whatever you like with the e-books
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
and covers, etc.
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
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
calibre were originally contributed by users.
'''
# Qt Help Project (.qhp)
# This is the input file for the help generator.
# It contains the table of contents, indices and references to the
# actual documentation files (*.html).
# In addition it defines a unique namespace for the documentation.
project_template = '''\
<?xml version="1.0" encoding="UTF-8"?>
<QtHelpProject version="1.0">
<namespace>%(outname)s.org.%(outname)s.%(nversion)s</namespace>
<virtualFolder>doc</virtualFolder>
<customFilter name="%(project)s %(version)s">
<filterAttribute>%(outname)s</filterAttribute>
<filterAttribute>%(version)s</filterAttribute>
</customFilter>
<filterSection>
<filterAttribute>%(outname)s</filterAttribute>
<filterAttribute>%(version)s</filterAttribute>
<toc>
<section title="%(title)s" ref="%(masterdoc)s.html">
%(sections)s
</section>
</toc>
<files>
%(files)s
</files>
</filterSection>
</QtHelpProject>
'''
section_template = '<section title="%(title)s" ref="%(ref)s"/>'
file_template = ' '*12 + '<file>%(filename)s</file>'
class QtHelpBuilder(StandaloneHTMLBuilder):
"""
Builder that also outputs Qt help project, contents and index files.
"""
name = 'qthelp'
# don't copy the reST source
copysource = False
supported_image_types = ['image/svg+xml', 'image/png', 'image/gif',
'image/jpeg']
# don't add links
add_permalinks = False
# don't add sidebar etc.
embedded = True
def init(self):
StandaloneHTMLBuilder.init(self)
# the output files for HTML help must be .html only
self.out_suffix = '.html'
self.link_suffix = '.html'
#self.config.html_style = 'traditional.css'
def handle_finish(self):
self.build_qhcp(self.outdir, self.config.qthelp_basename)
self.build_qhp(self.outdir, self.config.qthelp_basename)
self.build_qhc(self.outdir, self.config.qthelp_basename)
def build_qhc(self, outdir, outname):
self.info('create Qt Help Collection...')
with open(os.path.join(outdir, 'about.txt'), 'wb') as f:
f.write(ABOUT)
qhcp = os.path.abspath(os.path.join(outdir, outname+'.qhcp'))
subprocess.check_call(['qcollectiongenerator', qhcp])
qhc = qhcp[:-5]+'.qhc'
self.info('Qt Help Collection: '+qhc)
self.info('To test: assistant -collectionFile '+qhc)
def build_qhcp(self, outdir, outname):
self.info('writing collection project file...')
f = open(path.join(outdir, outname+'.qhcp'), 'w')
try:
f.write(collection_template % {'outname': outname})
finally:
f.close()
def build_qhp(self, outdir, outname):
self.info('writing project file...')
# sections
tocdoc = self.env.get_and_resolve_doctree(self.config.master_doc, self,
prune_toctrees=False)
istoctree = lambda node: (
isinstance(node, addnodes.compact_paragraph)
and node.has_key('toctree'))
sections = []
for node in tocdoc.traverse(istoctree):
sections.extend(self.write_toc(node))
if self.config.html_use_modindex:
item = section_template % {'title': 'Global Module Index',
'ref': 'modindex.html'}
sections.append(' '*4*4 + item)
sections = '\n'.join(sections)
# keywords
keywords = []
index = self.env.create_index(self)
for (key, group) in index:
for title, (refs, subitems) in group:
keywords.extend(self.build_keywords(title, refs, subitems))
keywords = '\n'.join(keywords)
# files
if not outdir.endswith(os.sep):
outdir += os.sep
olen = len(outdir)
projectfiles = []
staticdir = path.join(outdir, '_static')
imagesdir = path.join(outdir, '_images')
for root, dirs, files in os.walk(outdir):
resourcedir = root.startswith(staticdir) or root.startswith(imagesdir)
for fn in files:
if (resourcedir and not fn.endswith('.js')) or \
fn.endswith('.html'):
filename = path.join(root, fn)[olen:]
#filename = filename.replace(os.sep, '\\') # XXX
projectfiles.append(file_template % {'filename': filename})
projectfiles = '\n'.join(projectfiles)
# write the project file
f = open(path.join(outdir, outname+'.qhp'), 'w')
try:
nversion = self.config.version.replace('.', '_')
nversion = nversion.replace(' ', '_')
f.write(project_template % {'outname': outname,
'title': self.config.html_title,
'version': self.config.version,
'project': self.config.project,
'nversion': nversion,
'masterdoc': self.config.master_doc,
'sections': sections,
'keywords': keywords,
'files': projectfiles})
finally:
f.close()
def isdocnode(self, node):
if not isinstance(node, nodes.list_item):
return False
if len(node.children) != 2:
return False
if not isinstance(node.children[0], addnodes.compact_paragraph):
return False
if not isinstance(node.children[0][0], nodes.reference):
return False
if not isinstance(node.children[1], nodes.bullet_list):
return False
return True
def write_toc(self, node, indentlevel=4):
parts = []
if self.isdocnode(node):
refnode = node.children[0][0]
link = refnode['refuri']
title = cgi.escape(refnode.astext()).replace('"','&quot;')
item = '<section title="%(title)s" ref="%(ref)s">' % {
'title': title,
'ref': link}
parts.append(' '*4*indentlevel + item)
for subnode in node.children[1]:
parts.extend(self.write_toc(subnode, indentlevel+1))
parts.append(' '*4*indentlevel + '</section>')
elif isinstance(node, nodes.list_item):
for subnode in node:
parts.extend(self.write_toc(subnode, indentlevel))
elif isinstance(node, nodes.reference):
link = node['refuri']
title = cgi.escape(node.astext()).replace('"','&quot;')
item = section_template % {'title': title, 'ref': link}
item = ' '*4*indentlevel + item.encode('ascii', 'xmlcharrefreplace')
parts.append(item.encode('ascii', 'xmlcharrefreplace'))
elif isinstance(node, nodes.bullet_list):
for subnode in node:
parts.extend(self.write_toc(subnode, indentlevel))
elif isinstance(node, addnodes.compact_paragraph):
for subnode in node:
parts.extend(self.write_toc(subnode, indentlevel))
return parts
def keyword_item(self, name, ref):
matchobj = _idpattern.match(name)
if matchobj:
groupdict = matchobj.groupdict()
shortname = groupdict['title']
id = groupdict.get('id')
# descr = groupdict.get('descr')
if shortname.endswith('()'):
shortname = shortname[:-2]
id = '%s.%s' % (id, shortname)
else:
id = descr = None
if id:
item = ' '*12 + '<keyword name="%s" id="%s" ref="%s"/>' % (
name, id, ref)
else:
item = ' '*12 + '<keyword name="%s" ref="%s"/>' % (name, ref)
item.encode('ascii', 'xmlcharrefreplace')
return item
def build_keywords(self, title, refs, subitems):
keywords = []
title = cgi.escape(title)
# if len(refs) == 0: # XXX
# write_param('See Also', title)
if len(refs) == 1:
keywords.append(self.keyword_item(title, refs[0]))
elif len(refs) > 1:
for i, ref in enumerate(refs): # XXX
# item = (' '*12 +
# '<keyword name="%s [%d]" ref="%s"/>' % (
# title, i, ref))
# item.encode('ascii', 'xmlcharrefreplace')
# keywords.append(item)
keywords.append(self.keyword_item(title, ref))
if subitems:
for subitem in subitems:
keywords.extend(self.build_keywords(subitem[0], subitem[1], []))
return keywords

View File

@ -0,0 +1,29 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>%(title)s</title>
<style type="text/css">
body {
text-align: center;
vertical-align: center;
overflow: hidden;
font-size: 16pt;
}
.logo {
text-align:center;
font-size: 1pt;
overflow:hidden;
}
h1 { font-family: serif; }
h2, h4 { font-family: monospace; }
</style>
</head>
<body>
<h1>%(title)s</h1>
<h4 style="font-family:monospace">%(version)s</h4>
<div style="text-align:center">
<img class="logo" src="%(img)s" alt="calibre logo" />
</div>
<h2>%(author)s</h2>
</body>
</html>

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

View File

@ -4,9 +4,9 @@
#
msgid ""
msgstr ""
"Project-Id-Version: calibre 0.6.7\n"
"POT-Creation-Date: 2009-08-18 20:55+MDT\n"
"PO-Revision-Date: 2009-08-18 20:55+MDT\n"
"Project-Id-Version: calibre 0.6.8\n"
"POT-Creation-Date: 2009-08-21 15:14+MDT\n"
"PO-Revision-Date: 2009-08-21 15:14+MDT\n"
"Last-Translator: Automatically generated\n"
"Language-Team: LANGUAGE\n"
"MIME-Version: 1.0\n"
@ -24,8 +24,8 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/kindle/driver.py:52
#: /home/kovid/work/calibre/src/calibre/devices/prs505/books.py:58
#: /home/kovid/work/calibre/src/calibre/devices/prs505/books.py:199
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:695
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:698
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:702
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:403
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:66
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:68
@ -94,8 +94,8 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/writer.py:29
#: /home/kovid/work/calibre/src/calibre/ebooks/rtf/input.py:139
#: /home/kovid/work/calibre/src/calibre/ebooks/rtf/input.py:141
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:258
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:265
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:262
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:269
#: /home/kovid/work/calibre/src/calibre/gui2/add.py:91
#: /home/kovid/work/calibre/src/calibre/gui2/add.py:98
#: /home/kovid/work/calibre/src/calibre/gui2/convert/__init__.py:19
@ -110,10 +110,10 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:106
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:139
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:387
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:34
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:39
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:35
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:40
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:123
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:41
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:124
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:391
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:404
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:874
@ -126,15 +126,15 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1093
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1423
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1425
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1510
#: /home/kovid/work/calibre/src/calibre/library/server.py:309
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1514
#: /home/kovid/work/calibre/src/calibre/library/server.py:373
#: /home/kovid/work/calibre/src/calibre/library/server.py:446
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:45
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:63
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:77
#: /home/kovid/work/calibre/src/calibre/utils/poppler/__init__.py:27
#: /home/kovid/work/calibre/src/calibre/utils/poppler/__init__.py:31
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:51
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:52
msgid "Unknown"
msgstr ""
@ -483,47 +483,47 @@ msgstr ""
msgid "Communicate with the Sony PRS-700 eBook reader."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:278
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:350
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:277
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:349
msgid "Unable to detect the %s disk drive. Try rebooting."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:418
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:417
msgid "Unable to detect the %s disk drive."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:511
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:510
msgid "Could not find mount helper: %s."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:523
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:522
msgid "Unable to detect the %s disk drive. Your kernel is probably exporting a deprecated version of SYSFS."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:531
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:530
msgid "Unable to mount main memory (Error code: %d)"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:636
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:638
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:635
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:637
msgid "The reader has no storage card in this slot."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:640
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:639
msgid "Selected slot: %s is not supported."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:664
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:663
msgid "There is insufficient free space in main memory"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:666
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:668
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:665
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:667
msgid "There is insufficient free space on the storage card"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:678
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:467
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:433
#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:83
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1000
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1004
@ -1905,16 +1905,16 @@ msgstr ""
msgid "Default action to perform when send to device button is clicked"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:120
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:124
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:397
msgid "Copied"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:149
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:153
msgid "Copy"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:149
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:153
msgid "Copy to Clipboard"
msgstr ""
@ -3796,93 +3796,88 @@ msgstr ""
msgid "Aborting..."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:41
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:42
msgid "You"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:125
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:144
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:126
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:145
msgid "Custom"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:127
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:136
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:142
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:228
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:128
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:137
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:143
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:230
msgid "Scheduled"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:240
#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:45
msgid "Search"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:318
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:284
msgid "%d recipes"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:319
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:285
msgid "Monday"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:319
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:285
msgid "Tuesday"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:319
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:285
msgid "Wednesday"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:319
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:285
msgid "day"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:320
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:286
msgid "Friday"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:320
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:286
msgid "Saturday"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:320
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:286
msgid "Sunday"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:320
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:286
msgid "Thursday"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:357
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:323
msgid "Must set account information"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:358
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:324
msgid "This recipe requires a username and password"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:389
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:355
msgid "Created by: "
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:427
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:393
msgid "%d days, %d hours and %d minutes ago"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:429
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:395
msgid "Last downloaded"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:431
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:397
msgid "Last downloaded: never"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:457
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:423
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:165
msgid "Schedule news download"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:460
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:426
msgid "Add a custom news source"
msgstr ""
@ -5051,6 +5046,10 @@ msgstr ""
msgid "No matches found for this book"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:54
msgid "Search"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/status.py:115
msgid "Jobs:"
msgstr ""
@ -5959,15 +5958,15 @@ msgid ""
"For help on an individual command: %%prog command --help\n"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1536
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1540
msgid "<p>Migrating old database to ebook library in %s<br><center>"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1565
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1569
msgid "Copying <b>%s</b>"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1582
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1586
msgid "Compacting database"
msgstr ""
@ -6055,17 +6054,21 @@ msgstr ""
msgid "Requested formats not available"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server.py:159
#: /home/kovid/work/calibre/src/calibre/library/server.py:203
msgid "Password to access your calibre library. Username is "
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server.py:450
#: /home/kovid/work/calibre/src/calibre/library/server.py:523
msgid ""
"[options]\n"
"\n"
"Start the calibre content server."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/manual/qthelp.py:163
msgid "Global Module Index"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/config.py:43
msgid ""
"%sUsage%s: %s\n"
@ -6181,81 +6184,83 @@ msgstr ""
msgid "Password for sites that require a login to access content."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:41
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:42
msgid "Unknown News Source"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:587
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:589
msgid "Download finished"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:589
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:591
msgid "Failed to download the following articles:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:595
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:597
msgid "Failed to download parts of the following articles:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:597
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:599
msgid " from "
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:599
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:601
msgid "\tFailed links:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:680
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:682
msgid "Could not fetch article. Run with --debug to see the reason"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:701
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:703
msgid "Fetching feeds..."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:705
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:707
msgid "Got feeds from index page"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:711
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:713
msgid "Trying to download cover..."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:762
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:764
msgid "Starting download [%d thread(s)]..."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:778
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:780
msgid "Feeds downloaded to %s"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:788
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:790
msgid "Could not download cover: %s"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:795
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:797
msgid "Downloading cover from %s"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:916
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:920
msgid "Untitled Article"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:987
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:991
msgid "Article downloaded: %s"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:998
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1002
msgid "Article download failed: %s"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1013
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1017
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_borba.py:80
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_glas_srpske.py:76
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_instapaper.py:59
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_lamujerdemivida.py:59
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_laprensa_ni.py:63
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_monitor.py:65
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_pobjeda.py:83
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_republika.py:66
msgid "Fetching feed"
msgstr ""
@ -6271,11 +6276,13 @@ msgstr ""
#:
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_24sata_rs.py:25
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_b92.py:22
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_beta.py:22
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_blic.py:25
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_borba.py:19
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_danas.py:22
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_e_novine.py:25
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_glas_srpske.py:27
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_glasjavnosti.py:21
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_krstarica.py:23
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_nin.py:30
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_novosti.py:24
@ -6283,6 +6290,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_pescanik.py:24
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_politika.py:24
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_pressonline.py:25
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_republika.py:19
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_rts.py:23
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_tanjug.py:23
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_vreme.py:27
@ -6339,6 +6347,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_azstarnet.py:15
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_barrons.py:18
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_bbc.py:15
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_beta_en.py:22
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_business_week.py:16
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_chicago_breaking_news.py:22
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_chicago_tribune.py:17
@ -6366,7 +6375,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_fudzilla.py:15
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_glasgow_herald.py:10
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_globe_and_mail.py:15
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_guardian.py:17
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_guardian.py:16
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_harpers.py:14
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_harpers_full.py:26
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_hindu.py:10
@ -6548,6 +6557,7 @@ msgstr ""
msgid "Skipping filtered article: %s"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_monitor.py:25
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_pobjeda.py:24
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_vijesti.py:25
msgid "Montenegrin"

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

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

View File

@ -164,6 +164,7 @@ class BasicNewsRecipe(Recipe):
#: Recipe specific options to control the conversion of the downloaded
#: content into an e-book. These will override any user or plugin specified
#: values, so only use if absolutely necessary. For example::
#:
#: conversion_options = {
#: 'base_font_size' : 16,
#: 'tags' : 'mytag1,mytag2',

View File

@ -55,6 +55,7 @@ recipe_modules = ['recipe_' + r for r in (
'eltiempo_hn', 'slate', 'tnxm', 'bbcvietnamese', 'vnexpress',
'volksrant', 'theeconomictimes_india', 'ourdailybread',
'monitor', 'republika', 'beta', 'beta_en', 'glasjavnosti',
'esquire',
)]

View File

@ -0,0 +1,62 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
'''
www.esquire.com
'''
from calibre import strftime
from calibre.web.feeds.news import BasicNewsRecipe
from calibre.ebooks.BeautifulSoup import Tag
class Esquire(BasicNewsRecipe):
title = 'Esquire'
__author__ = 'Darko Miletic'
description = 'Esquire: Man at His Best'
publisher = 'Hearst Communications, Inc.'
category = 'magazine, men, women we love, style, the guide, sex, screen'
oldest_article = 30
max_articles_per_feed = 100
no_stylesheets = True
encoding = 'cp1250'
use_embedded_content = False
language = _('English')
lang = 'en-US'
cover_url = strftime('http://www.esquire.com/cm/esquire/cover-images/%Y_') + strftime('%m').strip('0') + '.jpg'
conversion_options = {
'comment' : description
, 'tags' : category
, 'publisher' : publisher
, 'language' : lang
, 'pretty_print' : True
}
keep_only_tags = [dict(name='div', attrs={'id':'content'})]
remove_tags = [dict(name=['object','link','embed','iframe'])]
feeds = [
(u'Style' , u'http://www.esquire.com/style/rss/' )
,(u'Women' , u'http://www.esquire.com/women/rss/' )
,(u'Features' , u'http://www.esquire.com/features/rss/' )
,(u'Fiction' , u'http://www.esquire.com/fiction/rss/' )
,(u'Frontpage', u'http://www.esquire.com/rss/' )
]
def print_version(self, url):
rest = url.rpartition('?')[0]
article = rest.rpartition('/')[2]
return 'http://www.esquire.com/print-this/' + article
def preprocess_html(self, soup):
soup.html['xml:lang'] = self.lang
soup.html['lang'] = self.lang
mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)])
soup.head.insert(0,mlang)
for item in soup.findAll(style=True):
del item['style']
return soup

View File

@ -39,7 +39,7 @@ class GlasJavnosti(BasicNewsRecipe):
,dict(name='div', attrs={'class':'node' })
]
remove_tags = [
dict(name=['object','link'])
dict(name=['object','link','img'])
,dict(name='div',attrs={'class':['links','meta']})
,dict(name='div',attrs={'id':'block-block-12'})
]
@ -74,7 +74,5 @@ class GlasJavnosti(BasicNewsRecipe):
,(u'Konjske Snage', u'http://www.glas-javnosti.rs/aktuelni-clanci/46')
]
def preprocess_html(self, soup):
for item in soup.findAll(style=True):
del item['style']
return self.adeify_images(soup)
remove_attributes = ['style','width','height','font','border','align']

View File

@ -26,7 +26,7 @@ class MonitorCG(BasicNewsRecipe):
lang ='sr-Latn-Me'
INDEX = 'http://www.monitorcg.com'
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: serif1, serif} .article_description{font-family: sans1, sans-serif}'
extra_css = ' @font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{font-family: serif1, serif} '
conversion_options = {
'comment' : description
@ -43,14 +43,27 @@ class MonitorCG(BasicNewsRecipe):
remove_tags = [ dict(name=['object','link','embed'])
, dict(attrs={'class':['buttonheading','article-section']})]
remove_attributes = ['style','width','height','font','border','align']
def adeify_images2(cls, soup):
for item in soup.findAll('img'):
for attrib in ['height','width','border','align','style']:
if item.has_key(attrib):
del item[attrib]
oldParent = item.parent
if oldParent.name == 'a':
oldParent.name == 'p'
myIndex = oldParent.contents.index(item)
brtag = Tag(soup,'br')
oldParent.insert(myIndex+1,brtag)
return soup
def preprocess_html(self, soup):
soup.html['xml:lang'] = self.lang
soup.html['lang'] = self.lang
mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)])
mcharset = Tag(soup,'meta',[("http-equiv","Content-Type"),("content","text/html; charset=utf-8")])
soup.head.insert(0,mlang)
soup.head.insert(1,mcharset)
return self.adeify_images(soup)
soup.html.insert(0,mlang)
return self.adeify_images2(soup)
def parse_index(self):
totalfeeds = []

6
todo
View File

@ -3,5 +3,9 @@
* Testing framework
* MOBI navigation indexing support
* Add a languages column to books. Best implementation is comma separated IANA codes
* Add a hash column to the formats table (used for stanza identifier)
* Fix blockquote handling in sphinx templates
* Fix ebook-viewer going to links

View File

@ -123,8 +123,13 @@ class manual(OptionlessCommand):
os.makedirs(d)
if not os.path.exists('.build'+os.sep+'html'):
os.makedirs('.build'+os.sep+'html')
check_call(['sphinx-build', '-b', 'custom', '-d',
'.build/doctrees', '.', '.build/html'])
check_call(['sphinx-build', '-b', 'custom', '-t', 'online',
'-d', '.build/doctrees', '.', '.build/html'])
check_call(['sphinx-build', '-b', 'epub', '-d',
'.build/doctrees', '.', '.build/epub'])
j = os.path.join
shutil.copyfile(j('.build', 'epub', 'calibre.epub'), j('.build',
'html', 'calibre.epub'))
finally:
os.chdir(cwd)
@ -701,6 +706,9 @@ class stage3(OptionlessCommand):
shell=True)
check_call('ssh divok bzr update /usr/local/calibre',
shell=True)
check_call('ssh divok /etc/init.d/apache2 graceful',
shell=True)
def run(self):