Sync to trunk.

This commit is contained in:
John Schember 2009-09-21 17:11:31 -04:00
commit 5375c1bee0
82 changed files with 17244 additions and 14805 deletions

View File

@ -11,6 +11,7 @@ resources/localization
resources/images.qrc
resources/recipes.pickle
resources/scripts.pickle
resources/ebook-convert-complete.pickle
setup/installer/windows/calibre/build.log
src/calibre/translations/.errors
src/cssutils/.svn/

View File

@ -55,6 +55,7 @@ p, dl, multicol {
dd {
display: block;
margin-left: 40px;
}
blockquote {

210
resources/templates/lrf.xsl Normal file
View File

@ -0,0 +1,210 @@
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:c="calibre"
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"
extension-element-prefixes="c"
xsl:version = "1.1"
>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<package version="2.0">
<metadata>
<xsl:call-template name="make-metadata"/>
</metadata>
<manifest>
<xsl:call-template name="make-manifest"/>
</manifest>
<spine toc="ncx">
<xsl:call-template name="make-spine"/>
</spine>
</package>
<xsl:call-template name="make-ncx"/>
<xsl:call-template name="make-css"/>
<xsl:for-each select="//Page">
<xsl:call-template name="make-page"/>
</xsl:for-each>
</xsl:template>
<xsl:template name="make-css">
<xsl:for-each select="//TextStyle|//BlockStyle">
<c:styles/>
</xsl:for-each>
</xsl:template>
<xsl:template name="make-page">
<xsl:variable name="pid" select="@objid"/>
<xsl:document href="{$pid}.xhtml" method="xml" indent="yes">
<html>
<head>
<title><xsl:value-of select="//Title"/></title>
<link rel="stylesheet" type="text/css" href="styles.css"/>
</head>
<body class="body">
<xsl:apply-templates />
</body>
</html>
</xsl:document>
</xsl:template>
<xsl:template match="RuledLine">
<c:ruled-line/>
</xsl:template>
<xsl:template match="TextBlock">
<c:text-block/>
</xsl:template>
<xsl:template match="ImageBlock">
<c:image-block/>
</xsl:template>
<xsl:template match="Canvas">
<c:canvas/>
</xsl:template>
<xsl:template name="make-metadata">
<xsl:for-each select='//BookInformation/Info/BookInfo'>
<xsl:apply-templates select="Title"/>
<xsl:apply-templates select="Author"/>
<xsl:apply-templates select="Publisher"/>
<xsl:apply-templates select="Category|Classification"/>
</xsl:for-each>
<xsl:for-each select='//BookInformation/Info/DocInfo'>
<xsl:apply-templates select="Language"/>
<xsl:apply-templates select="Producer"/>
</xsl:for-each>
</xsl:template>
<xsl:template match="Title">
<xsl:element name="dc:title">
<xsl:if test="@reading and @reading != ''">
<xsl:attribute name="opf:file-as"><xsl:value-of select="@reading"/></xsl:attribute>
</xsl:if>
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
<xsl:template match="Author">
<xsl:element name="dc:creator">
<xsl:attribute name="opf:role">aut</xsl:attribute>
<xsl:if test="@reading and @reading != ''">
<xsl:attribute name="opf:file-as"><xsl:value-of select="@reading"/></xsl:attribute>
</xsl:if>
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
<xsl:template match="Publisher">
<xsl:element name="dc:publisher">
<xsl:if test="@reading and @reading != ''">
<xsl:attribute name="opf:file-as"><xsl:value-of select="@reading"/></xsl:attribute>
</xsl:if>
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
<xsl:template match="Producer">
<xsl:element name="dc:creator">
<xsl:attribute name="opf:role">bkp</xsl:attribute>
<xsl:if test="@reading and @reading != ''">
<xsl:attribute name="opf:file-as"><xsl:value-of select="@reading"/></xsl:attribute>
</xsl:if>
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
<xsl:template match="Language">
<xsl:element name="dc:language">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
<xsl:template match="Category|Classification">
<xsl:if test=".!=''">
<xsl:element name="dc:subject">
<xsl:value-of select="."/>
</xsl:element>
</xsl:if>
</xsl:template>
<xsl:template name="make-manifest">
<xsl:for-each select='//Page'>
<xsl:element name="item">
<xsl:attribute name="id"><xsl:value-of select="@objid"/></xsl:attribute>
<xsl:attribute name="media-type"><xsl:text>application/xhtml+xml</xsl:text></xsl:attribute>
<xsl:attribute name="href"><xsl:value-of select="@objid"/><xsl:text>.xhtml</xsl:text></xsl:attribute>
</xsl:element>
</xsl:for-each>
<xsl:for-each select="//ImageStream">
<xsl:element name="item">
<xsl:attribute name="id"><xsl:value-of select="@objid"/></xsl:attribute>
<xsl:attribute name="media-type"><c:media-type/></xsl:attribute>
<xsl:attribute name="href"><xsl:value-of select="@file"/></xsl:attribute>
</xsl:element>
</xsl:for-each>
<xsl:for-each select="//RegistFont">
<xsl:element name="item">
<xsl:attribute name="id"><xsl:value-of select="@objid"/></xsl:attribute>
<xsl:attribute name="media-type"><c:media-type/></xsl:attribute>
<xsl:attribute name="href"><xsl:value-of select="@file"/></xsl:attribute>
</xsl:element>
</xsl:for-each>
<item id="ncx" href="toc.ncx" media-type="application/x-dtbncx+xml" />
<item id="styles" href="styles.css" media-type="text/css" />
</xsl:template>
<xsl:template name="make-spine">
<xsl:for-each select='//Page'>
<xsl:element name="itemref">
<xsl:attribute name="idref"><xsl:value-of select="@objid"/></xsl:attribute>
</xsl:element>
</xsl:for-each>
</xsl:template>
<xsl:template match="*">
<xsl:message>
<xsl:text>no match for element: "</xsl:text>
<xsl:value-of select="name(.)"/>
<xsl:text>" &#xA;</xsl:text>
</xsl:message>
<xsl:apply-templates/>
</xsl:template>
<xsl:template name="make-ncx">
<xsl:document href="toc.ncx" method="xml" indent="yes">
<ncx version="2005-1"
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="1"/>
<meta name="dtb:generator" content="calibre"/>
<meta name="dtb:totalPageCount" content="0"/>
<meta name="dtb:maxPageNumber" content="0"/>
</head>
<docTitle><text>Table of Contents</text></docTitle>
<navMap>
<xsl:for-each select="//TOC/TocLabel">
<xsl:element name="navPoint">
<xsl:attribute name="id"><xsl:value-of select="count(preceding-sibling::*)"/></xsl:attribute>
<xsl:attribute name="playOrder"><xsl:value-of select="count(preceding-sibling::*)+1"/></xsl:attribute>
<navLabel><text><xsl:value-of select="."/></text></navLabel>
<xsl:element name="content">
<xsl:attribute name="src">
<xsl:value-of select="@refpage"/>.xhtml#<xsl:value-of select="@refobj"/>
</xsl:attribute>
</xsl:element>
</xsl:element>
</xsl:for-each>
</navMap>
</ncx>
</xsl:document>
</xsl:template>
</xsl:stylesheet>

View File

@ -1,27 +1,10 @@
#########################################################################
# #
# #
# copyright 2002 Paul Henry Tremblay #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU #
# General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program; if not, write to the Free Software #
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA #
# 02111-1307 USA #
# #
# #
#########################################################################
xhtml = '''\
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns:rtf="http://rtf2xml.sourceforge.net/"
xmlns:c="calibre"
extension-element-prefixes="c"
exclude-result-prefixes="rtf"
>
@ -147,7 +130,7 @@ xhtml = '''\
<xsl:text>generator</xsl:text>
</xsl:attribute>
<xsl:attribute name="content">
<xsl:text>http://rtf2xml.sourceforge.net/</xsl:text>
<xsl:text>http://calibre-ebook.com</xsl:text>
</xsl:attribute>
</xsl:element>
@ -233,9 +216,7 @@ xhtml = '''\
<xsl:text>span.italic-bold{font-style:italic;font-weight:bold}&#xA;</xsl:text>
<xsl:text>span.italic-underline{font-style:italic;text-decoration:underline}&#xA;</xsl:text>
<xsl:text>span.bold-underline{font-weight:bold;text-decoration:underline}&#xA;</xsl:text>
<xsl:for-each select="//rtf:inline">
<xsl:call-template name="parse-inline"/>
</xsl:for-each>
<xsl:text>&#xA;</xsl:text>
</xsl:document>
</xsl:template>
@ -287,52 +268,6 @@ xhtml = '''\
</xsl:if>
</xsl:template>
<xsl:template name="parse-inline">
<xsl:variable name="num-attrs" select="count(@*)"/>
<xsl:choose>
<xsl:when test="$num-attrs = 1 and @italics"/>
<xsl:when test="$num-attrs = 1 and @bold"/>
<xsl:when test="$num-attrs = 1 and @underline"/>
<xsl:when test="$num-attrs = 2 and @italics and @bold"/>
<xsl:when test="$num-attrs = 2 and @italcs and @underline"/>
<xsl:when test="$num-attrs = 2 and @bold and @underline"/>
<xsl:otherwise>
<xsl:text>span.</xsl:text>
<xsl:value-of select="generate-id(.)"/>
<xsl:text>{</xsl:text>
<xsl:if test="@italics = 'true'">
<xsl:text>font-style:italic;</xsl:text>
</xsl:if>
<xsl:if test="@italics = 'false'">
<xsl:text>font-style:normal;</xsl:text>
</xsl:if>
<xsl:if test="@bold = 'true'">
<xsl:text>font-weight:bold;</xsl:text>
</xsl:if>
<xsl:if test="@bold = 'false'">
<xsl:text>font-weight:normal;</xsl:text>
</xsl:if>
<xsl:if test="@underline and @underline != 'false'">
<xsl:text>text-decoration:underline;</xsl:text>
</xsl:if>
<xsl:if test="@underline= 'false'">
<xsl:text>text-decoration:none;</xsl:text>
</xsl:if>
<xsl:if test="@strike-through = 'true'">
<xsl:text>text-decoration:line-through;</xsl:text>
</xsl:if>
<xsl:if test="@strike-through = 'false'">
<xsl:text>text-decoration:none;</xsl:text>
</xsl:if>
<xsl:if test="@font-size">
<xsl:text>font-size:</xsl:text>
<xsl:value-of select="@font-size"/>
<xsl:text>pt;</xsl:text>
</xsl:if>
<xsl:text>}</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="rtf:inline">
<xsl:variable name="num-attrs" select="count(@*)"/>
@ -345,45 +280,7 @@ xhtml = '''\
<xsl:otherwise>
<xsl:element name="span">
<xsl:attribute name="class">
<xsl:choose>
<xsl:when test="$num-attrs=1 and @italics='true'">
<xsl:text>italic</xsl:text>
</xsl:when>
<xsl:when test="$num-attrs=1 and @italics='false'">
<xsl:text>no-italic</xsl:text>
</xsl:when>
<xsl:when test="$num-attrs=1 and @bold='true'">
<xsl:text>bold</xsl:text>
</xsl:when>
<xsl:when test="$num-attrs=1 and @bold='true'">
<xsl:text>bold</xsl:text>
</xsl:when>
<xsl:when test="$num-attrs=1 and @bold='false'">
<xsl:text>no-bold</xsl:text>
</xsl:when>
<xsl:when test="$num-attrs=1 and @underlined">
<xsl:choose>
<xsl:when test="not(@underlined='false')">
<xsl:text>underline</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>no-underline</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="$num-attrs=2 and @bold='true' and @italics='true'">
<xsl:text>italic-bold</xsl:text>
</xsl:when>
<xsl:when test="$num-attrs=2 and @italics='true' and @underline and @underline != 'false'">
<xsl:text>italic-underline</xsl:text>
</xsl:when>
<xsl:when test="$num-attrs=2 and @bold='true' and @underline and @underline != 'false'">
<xsl:text>bold-underline</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="generate-id(.)"/>
</xsl:otherwise>
</xsl:choose>
<c:inline-class/>
</xsl:attribute>
<xsl:apply-templates/>
</xsl:element>
@ -539,4 +436,3 @@ xhtml = '''\
</xsl:template>
</xsl:stylesheet>
'''

View File

@ -16,7 +16,7 @@ from setup.build_environment import fc_inc, fc_lib, qt_inc, qt_lib, \
fc_error, poppler_libs, poppler_lib, poppler_inc, podofo_inc, \
podofo_lib, podofo_error, poppler_error, pyqt, OSX_SDK, NMAKE, \
leopard_build, QMAKE, msvc, MT, win_inc, win_lib
MT
isunix = islinux or isosx
make = 'make' if isunix else NMAKE
@ -262,11 +262,11 @@ class Build(Command):
print ' '.join(cmd)
subprocess.check_call(cmd)
if iswindows:
manifest = dest+'.manifest'
cmd = [MT, '-manifest', manifest, '-outputresource:%s;2'%dest]
self.info(*cmd)
subprocess.check_call(cmd)
os.remove(manifest)
#manifest = dest+'.manifest'
#cmd = [MT, '-manifest', manifest, '-outputresource:%s;2'%dest]
#self.info(*cmd)
#subprocess.check_call(cmd)
#os.remove(manifest)
for x in ('.exp', '.lib'):
x = os.path.splitext(dest)[0]+x
if os.path.exists(x):

View File

@ -11,7 +11,7 @@ import sys, os, textwrap, subprocess, shutil, tempfile, atexit
from setup import Command, islinux, basenames, modules, functions, \
__appname__, __version__
TEMPLATE = '''\
HEADER = '''\
#!/usr/bin/env python
"""
@ -20,6 +20,9 @@ Do not modify it unless you know what you are doing.
"""
import sys
'''
TEMPLATE = HEADER+'''
sys.path.insert(0, {path!r})
sys.resources_location = {resources!r}
@ -29,6 +32,18 @@ from {module} import {func!s}
sys.exit({func!s}())
'''
COMPLETE_TEMPLATE = HEADER+'''
import os
sys.path.insert(0, {path!r})
sys.path.insert(0, os.path.join({path!r}, 'calibre', 'utils'))
import complete
sys.path = sys.path[1:]
sys.resources_location = {resources!r}
sys.extensions_location = {extensions!r}
sys.exit(complete.main())
'''
class Develop(Command):
description = textwrap.dedent('''\
@ -43,8 +58,9 @@ class Develop(Command):
sub_commands = ['build', 'resources', 'gui']
def add_options(self, parser):
parser.add_option('--prefix', '--root',
parser.add_option('--prefix',
help='Binaries will be installed in <prefix>/bin')
self.root = ''
def pre_sub_commands(self, opts):
if not islinux:
@ -76,7 +92,7 @@ class Develop(Command):
return warn()
import stat
src = os.path.join(self.SRC, 'calibre', 'devices', 'linux_mount_helper.c')
dest = os.path.join(self.bindir, 'calibre-mount-helper')
dest = self.root + os.path.join(self.bindir, 'calibre-mount-helper')
self.info('Installing mount helper to '+ dest)
p = subprocess.Popen(['gcc', '-Wall', src, '-o', dest])
ret = p.wait()
@ -116,11 +132,12 @@ class Develop(Command):
self.write_template(opts, 'calibre_postinstall', 'calibre.linux', 'main')
def write_template(self, opts, name, mod, func):
script = TEMPLATE.format(
template = COMPLETE_TEMPLATE if name == 'calibre-complete' else TEMPLATE
script = template.format(
module=mod, func=func,
path=self.path, resources=self.resources,
extensions=self.extensions)
path = self.j(self.bindir, name)
path = self.root + self.j(self.bindir, name)
if not os.path.exists(self.bindir):
os.makedirs(self.bindir)
self.info('Installing binary:', path)
@ -141,10 +158,13 @@ class Install(Develop):
sub_commands = ['build', 'gui']
def add_options(self, parser):
parser.add_option('--prefix', '--root', help='Installation prefix')
parser.add_option('--prefix', help='Installation prefix')
parser.add_option('--libdir', help='Where to put calibre library files')
parser.add_option('--bindir', help='Where to install calibre binaries')
parser.add_option('--sharedir', help='Where to install calibre data files')
parser.add_option('--root', default='',
help='Use a different installation root (mainly for packaging)')
self.root = ''
def find_locations(self, opts):
if opts.prefix is None:
@ -160,13 +180,19 @@ class Install(Develop):
self.path = opts.libdir
self.resources = opts.sharedir
self.extensions = self.j(self.path, 'calibre', 'plugins')
self.root = opts.root
def install_files(self, opts):
dest = self.path
dest = self.root + self.path
if os.path.exists(dest):
shutil.rmtree(dest)
shutil.copytree(self.SRC, dest)
dest = self.resources
for x in ('calibre/manual', 'calibre/trac',
'calibre/ebooks/lrf/html/demo'):
x = self.j(dest, x)
if os.path.exists(dest):
shutil.rmtree(x)
dest = self.root + self.resources
if os.path.exists(dest):
shutil.rmtree(dest)
shutil.copytree(self.RESOURCES, dest)

View File

@ -37,6 +37,7 @@ class LinuxFreeze(Command):
binary_includes = [
'/usr/bin/pdftohtml',
'/usr/lib/libwmflite-0.2.so.7',
'/tmp/calibre-mount-helper',
'/usr/lib/libunrar.so',
'/usr/lib/libsqlite3.so.0',
@ -202,6 +203,15 @@ class LinuxFreeze(Command):
for f in matches:
os.remove(f)
self.info('Adding ImageMagick...')
im = glob.glob('/usr/lib/ImageMagick-*')[0]
dest = os.path.join(FREEZE_DIR, 'ImageMagick')
shutil.copytree(im, dest)
for x in os.walk(dest):
for f in x[-1]:
if f.endswith('.a'):
os.remove(os.path.join(x[0], f))
self.info('Adding calibre plugins...')
os.makedirs(os.path.join(FREEZE_DIR, 'plugins'))
for f in glob.glob(os.path.join(CALIBREPLUGINS, '*.so')):
@ -230,6 +240,9 @@ class LinuxFreeze(Command):
base=`dirname $path`
loader=$base/loader
export LD_LIBRARY_PATH=$base:$LD_LIBRARY_PATH
export MAGICK_CONFIGURE_PATH=$base/ImageMagick/config
export MAGICK_CODER_MODULE_PATH=$base/ImageMagick/modules-Q16/coders
export MAGICK_CODER_FILTER_PATH=$base/ImageMagick/modules-Q16/filter
$loader "$@"
''')%exe)
os.chmod(path, 0755)

View File

@ -10,6 +10,8 @@ import sys, os, shutil, plistlib, subprocess, glob, zipfile, tempfile, \
py_compile, stat, operator
abspath, join, basename = os.path.abspath, os.path.join, os.path.basename
#TODO: WMF support in ImageMagick
l = {}
exec open('setup.py').read() in l
VERSION = l['VERSION']

View File

@ -188,6 +188,31 @@ os.execv(python, args)
self.fix_python_dependencies(deps)
self.fix_misc_dependencies(deps)
def fix_image_magick_deps(self, root):
modules = []
frameworks_dir = os.path.dirname(root)
for x in os.walk(root):
for f in x[-1]:
if f.endswith('.so'):
modules.append(os.path.join(x[0], f))
deps = {}
for x in ('Core.1', 'Wand.1'):
modules.append(os.path.join(root, 'lib', 'libMagick%s.dylib'%x))
x = modules[-1]
deps[os.path.join('/Users/kovid/ImageMagick/lib',
os.path.basename(x))] = '@executable_path/../Frameworks/ImageMagick/lib/'+os.path.basename(x)
subprocess.check_call(['install_name_tool', '-id',
'@executable_path/../Frameworks/ImageMagick/lib/'+os.path.basename(x),
x])
for x in ('/usr/local/lib/libfreetype.6.dylib',
'/Volumes/sw/lib/libwmflite-0.2.7.dylib'):
deps[x] = '@executable_path/../Frameworks/'+ os.path.basename(x)
for x in modules:
print 'Fixing deps in', x
for f, t in deps.items():
subprocess.check_call(['install_name_tool', '-change', f, t, x])
def run(self):
py2app.run(self)
@ -252,11 +277,17 @@ os.execv(python, args)
info('Adding ImageMagick')
libwmf = '/Volumes/sw/lib/libwmflite-0.2.7.dylib'
dest = os.path.join(frameworks_dir, os.path.basename(libwmf))
shutil.copy2(libwmf, frameworks_dir)
nid = '@executable_path/../Frameworks/'+os.path.basename(dest)
subprocess.check_call(['install_name_tool', '-id', nid, dest])
dest = os.path.join(frameworks_dir, 'ImageMagick')
if os.path.exists(dest):
shutil.rmtree(dest)
shutil.copytree(os.path.expanduser('~/ImageMagick'), dest, True)
shutil.copyfile('/usr/local/lib/libpng12.0.dylib', os.path.join(dest, 'lib', 'libpng12.0.dylib'))
self.fix_image_magick_deps(dest)
info('Installing prescipt')

View File

@ -34,6 +34,10 @@ class Stage2(Command):
def pre_sub_commands(self, opts):
for x in glob.glob(os.path.join(self.d(self.SRC), 'dist', '*')):
os.remove(x)
build = os.path.join(self.d(self.SRC), 'build')
if os.path.exists(build):
shutil.rmtree(build)
class Stage3(Command):

View File

@ -10,6 +10,18 @@ import os, cPickle
from setup import Command, basenames
def get_opts_from_parser(parser):
def do_opt(opt):
for x in opt._long_opts:
yield x
for x in opt._short_opts:
yield x
for o in parser.option_list:
for x in do_opt(o): yield x
for g in parser.option_groups:
for o in g.option_list:
for x in do_opt(o): yield x
class Resources(Command):
def get_recipes(self):
@ -45,9 +57,42 @@ class Resources(Command):
f = open(dest, 'wb')
cPickle.dump(recipes, f, -1)
dest = self.j(self.RESOURCES, 'ebook-convert-complete.pickle')
files = []
for x in os.walk(self.j(self.SRC, 'calibre')):
for f in x[-1]:
if f.endswith('.py'):
files.append(self.j(x[0], f))
if self.newer(dest, files):
self.info('\tCreating ebook-convert-complete.pickle')
complete = {}
from calibre.ebooks.conversion.plumber import supported_input_formats
complete['input_fmts'] = set(supported_input_formats())
from calibre.web.feeds.recipes import recipes
complete['input_recipes'] = [t.title+'.recipe ' for t in recipes]
from calibre.customize.ui import available_output_formats
complete['output'] = set(available_output_formats())
from calibre.ebooks.conversion.cli import create_option_parser
from calibre.utils.logging import Log
log = Log()
#log.outputs = []
for inf in supported_input_formats():
if inf in ('zip', 'rar', 'oebzip'):
continue
for ouf in available_output_formats():
of = ouf if ouf == 'oeb' else 'dummy.'+ouf
p = create_option_parser(('ec', 'dummy1.'+inf, of, '-h'),
log)[0]
complete[(inf, ouf)] = [x+' 'for x in
get_opts_from_parser(p)]
cPickle.dump(complete, open(dest, 'wb'), -1)
def clean(self):
for x in ('scripts', 'recipes'):
for x in ('scripts', 'recipes', 'ebook-convert-complete'):
x = self.j(self.RESOURCES, x+'.pickle')
if os.path.exists(x):
os.remove(x)

View File

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

View File

@ -338,6 +338,7 @@ from calibre.ebooks.rb.input import RBInput
from calibre.web.feeds.input import RecipeInput
from calibre.ebooks.rtf.input import RTFInput
from calibre.ebooks.txt.input import TXTInput
from calibre.ebooks.lrf.input import LRFInput
from calibre.ebooks.epub.output import EPUBOutput
from calibre.ebooks.fb2.output import FB2Output
@ -385,6 +386,7 @@ plugins += [
RecipeInput,
RTFInput,
TXTInput,
LRFInput,
]
plugins += [
EPUBOutput,

View File

@ -173,7 +173,7 @@ class PageProcessor(list):
p.MagickDespeckleImage(wand)
p.MagickQuantizeImage(wand, self.opts.colors, p.RGBColorspace, 0, 1, 0)
dest = '%d_%d.png'%(self.num, i)
dest = '%d_%d.%s'%(self.num, i, self.opts.output_format)
dest = os.path.join(self.dest, dest)
p.MagickWriteImage(wand, dest+'8')
os.rename(dest+'8', dest)
@ -270,8 +270,10 @@ class ComicInput(InputFormatPlugin):
is_image_collection = True
options = set([
OptionRecommendation(name='colors', recommended_value=64,
help=_('Number of colors for grayscale image conversion. Default: %default')),
OptionRecommendation(name='colors', recommended_value=256,
help=_('Number of colors for grayscale image conversion. Default: '
'%default. Values of less than 256 may result in blurred text '
'on your device if you are creating your comics in EPUB format.')),
OptionRecommendation(name='dont_normalize', recommended_value=False,
help=_('Disable normalize (improve contrast) color range '
'for pictures. Default: False')),
@ -298,6 +300,10 @@ class ComicInput(InputFormatPlugin):
help=_("Don't sort the files found in the comic "
"alphabetically by name. Instead use the order they were "
"added to the comic.")),
OptionRecommendation(name='output_format', choices=['png', 'jpg'],
recommended_value='png', help=_('The format that images in the created ebook '
'are converted to. You can experiment to see which format gives '
'you optimal size and look on your device.')),
OptionRecommendation(name='no_process', recommended_value=False,
help=_("Apply no processing to the image")),
])
@ -365,7 +371,8 @@ class ComicInput(InputFormatPlugin):
'(run with --verbose to see why):')
for f in failures:
self.log.warning('\t', f)
thumbnail = os.path.join(tdir2, 'thumbnail.png')
thumbnail = os.path.join(tdir2,
'thumbnail.'+self.opts.output_format.lower())
if not os.access(thumbnail, os.R_OK):
thumbnail = None
return new_pages

View File

@ -51,6 +51,9 @@ def load_specifics(db, book_id):
r.from_string(raw)
return r
def delete_specifics(db, book_id):
db.delete_conversion_options(book_id, 'PIPE')
class GuiRecommendations(dict):
def __new__(cls, *args):

View File

@ -14,7 +14,8 @@ XMLDECL_RE = re.compile(r'^\s*<[?]xml.*?[?]>')
SVG_NS = 'http://www.w3.org/2000/svg'
XLINK_NS = 'http://www.w3.org/1999/xlink'
convert_entities = functools.partial(entity_to_unicode, exceptions=['quot', 'apos', 'lt', 'gt', 'amp'])
convert_entities = functools.partial(entity_to_unicode, exceptions=['quot',
'apos', 'lt', 'gt', 'amp', '#60', '#62'])
_span_pat = re.compile('<span.*?</span>', re.DOTALL|re.IGNORECASE)

View File

@ -0,0 +1,412 @@
#!/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, textwrap
from copy import deepcopy
from lxml import etree
from calibre.customize.conversion import InputFormatPlugin
from calibre import guess_type
class Canvas(etree.XSLTExtension):
def __init__(self, doc, styles, text_block, log):
self.doc = doc
self.styles = styles
self.text_block = text_block
self.log = log
self.processed = set([])
def execute(self, context, self_node, input_node, output_parent):
cid = input_node.get('objid', None)
if cid is None or cid in self.processed:
return
self.processed.add(cid)
input_node = self.doc.xpath('//Canvas[@objid="%s"]'%cid)[0]
objects = list(self.get_objects(input_node))
if len(objects) == 1 and objects[0][0].tag == 'ImageBlock':
self.image_page(input_node, objects[0][0], output_parent)
else:
canvases = [input_node]
for x in input_node.itersiblings():
if x.tag == 'Canvas':
oid = x.get('objid', None)
if oid is not None:
canvases.append(x)
self.processed.add(oid)
else:
break
table = etree.Element('table')
table.text = '\n\t'
for canvas in canvases:
oid = canvas.get('objid')
tr = table.makeelement('tr')
tr.set('id', oid)
tr.tail = '\n\t'
table.append(tr)
for obj, x, y in self.get_objects(canvas):
if obj.tag != 'TextBlock':
self.log.warn(obj.tag, 'elements in Canvas not supported')
continue
td = table.makeelement('td')
self.text_block.render_block(obj, td)
tr.append(td)
output_parent.append(table)
def image_page(self, input_node, block, output_parent):
div = etree.Element('div')
div.set('id', input_node.get('objid', 'scuzzy'))
div.set('class', 'image_page')
width = self.styles.to_num(block.get("xsize", None))
height = self.styles.to_num(block.get("ysize", None))
img = div.makeelement('img')
if width is not None:
img.set('width', str(int(width)))
if height is not None:
img.set('height', str(int(height)))
ref = block.get('refstream', None)
if ref is not None:
imstr = self.doc.xpath('//ImageStream[@objid="%s"]'%ref)
if imstr:
src = imstr[0].get('file', None)
if src:
img.set('src', src)
div.append(img)
output_parent.append(div)
def get_objects(self, node):
for x in node.xpath('descendant::PutObj[@refobj and @x1 and @y1]'):
objs = node.xpath('//*[@objid="%s"]'%x.get('refobj'))
x, y = map(self.styles.to_num, (x.get('x1'), x.get('y1')))
if objs and x is not None and y is not None:
yield objs[0], int(x), int(y)
class MediaType(etree.XSLTExtension):
def execute(self, context, self_node, input_node, output_parent):
name = input_node.get('file', None)
typ = guess_type(name)[0]
if not typ:
typ = 'application/octet-stream'
output_parent.text = typ
class ImageBlock(etree.XSLTExtension):
def __init__(self, canvas):
etree.XSLTExtension.__init__(self)
self.canvas = canvas
def execute(self, context, self_node, input_node, output_parent):
self.canvas.image_page(input_node, input_node, output_parent)
class RuledLine(etree.XSLTExtension):
def execute(self, context, self_node, input_node, output_parent):
hr = etree.Element('hr')
output_parent.append(hr)
class TextBlock(etree.XSLTExtension):
def __init__(self, styles, char_button_map, plot_map, log):
etree.XSLTExtension.__init__(self)
self.styles = styles
self.log = log
self.char_button_map = char_button_map
self.plot_map = plot_map
def execute(self, context, self_node, input_node, output_parent):
input_node = deepcopy(input_node)
div = etree.Element('div')
self.render_block(input_node, div)
output_parent.append(div)
def render_block(self, node, root):
ts = node.get('textstyle', None)
classes = []
bs = node.get('blockstyle')
if bs in self.styles.block_style_map:
classes.append('bs%d'%self.styles.block_style_map[bs])
if ts in self.styles.text_style_map:
classes.append('ts%d'%self.styles.text_style_map[ts])
if classes:
root.set('class', ' '.join(classes))
objid = node.get('objid', None)
if objid:
root.set('id', objid)
root.text = node.text
self.root = root
self.parent = root
self.add_text_to = (self.parent, 'text')
for child in node:
self.process_child(child)
def add_text(self, text):
if text:
if getattr(self.add_text_to[0], self.add_text_to[1]) is None:
setattr(self.add_text_to[0], self.add_text_to[1], '')
setattr(self.add_text_to[0], self.add_text_to[1],
getattr(self.add_text_to[0], self.add_text_to[1])+ text)
def process_container(self, child, tgt):
idx = self.styles.get_text_styles(child)
if idx is not None:
tgt.set('class', 'ts%d'%idx)
self.parent.append(tgt)
orig_parent = self.parent
self.parent = tgt
self.add_text_to = (self.parent, 'text')
self.add_text(child.text)
for gchild in child:
self.process_child(gchild)
self.parent = orig_parent
self.add_text_to = (tgt, 'tail')
self.add_text(child.tail)
def process_child(self, child):
if child.tag == 'CR':
if self.parent == self.root:
self.parent = self.root.makeelement('p')
self.root.append(self.parent)
self.add_text_to = (self.parent, 'text')
else:
br = self.parent.makeelement('br')
self.parent.append(br)
self.add_text_to = (br, 'tail')
self.add_text(child.tail)
elif child.tag in ('P', 'Span', 'EmpLine', 'NoBR'):
span = self.root.makeelement('span')
if child.tag == 'EmpLine':
td = 'underline' if child.get('emplineposition', 'before') == 'before' else 'overline'
span.set('style', 'text-decoration: '+td)
self.process_container(child, span)
elif child.tag == 'Sup':
sup = self.root.makeelement('sup')
self.process_container(child, sup)
elif child.tag == 'Sub':
sub = self.root.makeelement('sub')
self.process_container(child, sub)
elif child.tag == 'Italic':
sup = self.root.makeelement('i')
self.process_container(child, sup)
elif child.tag == 'CharButton':
a = self.root.makeelement('a')
oid = child.get('refobj', None)
if oid in self.char_button_map:
a.set('href', self.char_button_map[oid])
self.process_container(child, a)
elif child.tag == 'Plot':
xsize = self.styles.to_num(child.get('xsize', None), 166./720)
ysize = self.styles.to_num(child.get('ysize', None), 166./720)
img = self.root.makeelement('img')
if xsize is not None:
img.set('width', str(int(xsize)))
if ysize is not None:
img.set('height', str(int(ysize)))
ro = child.get('refobj', None)
if ro in self.plot_map:
img.set('src', self.plot_map[ro])
self.parent.append(img)
self.add_text_to = (img, 'tail')
self.add_text(child.tail)
else:
self.log.warn('Unhandled Text element:', child.tag)
class Styles(etree.XSLTExtension):
def __init__(self):
etree.XSLTExtension.__init__(self)
self.text_styles, self.block_styles = [], []
self.text_style_map, self.block_style_map = {}, {}
self.CSS = textwrap.dedent('''
.image_page { text-align:center }
''')
def write(self, name='styles.css'):
def join(style):
ans = ['%s : %s;'%(k, v) for k, v in style.items()]
if ans:
ans[-1] = ans[-1][:-1]
return '\n\t'.join(ans)
with open(name, 'wb') as f:
f.write(self.CSS)
for (w, sel) in [(self.text_styles, 'ts'), (self.block_styles,
'bs')]:
for i, s in enumerate(w):
if not s:
continue
rsel = '.%s%d'%(sel, i)
s = join(s)
f.write(rsel + ' {\n\t' + s + '\n}\n\n')
def execute(self, context, self_node, input_node, output_parent):
if input_node.tag == 'TextStyle':
idx = self.get_text_styles(input_node)
if idx is not None:
self.text_style_map[input_node.get('objid')] = idx
else:
idx = self.get_block_styles(input_node)
self.block_style_map[input_node.get('objid')] = idx
def px_to_pt(self, px):
try:
px = float(px)
return px * 72./166.
except:
return None
def color(self, val):
try:
val = int(val, 16)
r, g, b, a = val & 0xFF, (val>>8)&0xFF, (val>>16)&0xFF, (val>>24)&0xFF
if a == 255:
return None
if a == 0:
return 'rgb(%d,%d,%d)'%(r,g,b)
return 'rgba(%d,%d,%d,%f)'%(r,g,b,1.-a/255.)
except:
return None
def get_block_styles(self, node):
ans = {}
sm = self.px_to_pt(node.get('sidemargin', None))
if sm is not None:
ans['margin-left'] = ans['margin-right'] = '%fpt'%sm
ts = self.px_to_pt(node.get('topskip', None))
if ts is not None:
ans['margin-top'] = '%fpt'%ts
fs = self.px_to_pt(node.get('footskip', None))
if fs is not None:
ans['margin-bottom'] = '%fpt'%fs
fw = self.px_to_pt(node.get('framewidth', None))
if fw is not None:
ans['border-width'] = '%fpt'%fw
ans['border-style'] = 'solid'
fc = self.color(node.get('framecolor', None))
if fc is not None:
ans['border-color'] = fc
bc = self.color(node.get('bgcolor', None))
if bc is not None:
ans['background-color'] = bc
if ans not in self.block_styles:
self.block_styles.append(ans)
return self.block_styles.index(ans)
def to_num(self, val, factor=1.):
try:
return float(val)*factor
except:
return None
def get_text_styles(self, node):
ans = {}
fs = self.to_num(node.get('fontsize', None), 0.1)
if fs is not None:
ans['font-size'] = '%fpt'%fs
fw = self.to_num(node.get('fontweight', None))
if fw is not None:
ans['font-weight'] = ('bold' if fw >= 700 else 'normal')
#fn = getattr(obj, 'fontfacename', None)
#if fn is not None:
# fn = cls.FONT_MAP[fn]
# item('font-family: %s;'%fn)
fg = self.color(node.get('textcolor', None))
if fg is not None:
ans['color'] = fg
bg = self.color(node.get('textbgcolor', None))
if bg is not None:
ans['background-color'] = bg
al = node.get('align', None)
if al is not None:
all = dict(head='left', center='center', foot='right')
ans['text-align'] = all.get(al, 'left')
#lh = self.to_num(node.get('linespace', None), 0.1)
#if lh is not None:
# ans['line-height'] = '%fpt'%lh
pi = self.to_num(node.get('parindent', None), 0.1)
if pi is not None:
ans['text-indent'] = '%fpt'%pi
if not ans:
return None
if ans not in self.text_styles:
self.text_styles.append(ans)
return self.text_styles.index(ans)
class LRFInput(InputFormatPlugin):
name = 'LRF Input'
author = 'Kovid Goyal'
description = 'Convert LRF files to HTML'
file_types = set(['lrf'])
def convert(self, stream, options, file_ext, log,
accelerators):
self.log = log
self.log('Generating XML')
from calibre.ebooks.lrf.lrfparser import LRFDocument
d = LRFDocument(stream)
d.parse()
xml = d.to_xml(write_files=True)
parser = etree.XMLParser(recover=True, no_network=True)
doc = etree.fromstring(xml, parser=parser)
char_button_map = {}
for x in doc.xpath('//CharButton[@refobj]'):
ro = x.get('refobj')
jump_button = doc.xpath('//*[@objid="%s"]'%ro)
if jump_button:
jump_to = jump_button[0].xpath('descendant::JumpTo[@refpage and @refobj]')
if jump_to:
char_button_map[ro] = '%s.xhtml#%s'%(jump_to[0].get('refpage'),
jump_to[0].get('refobj'))
plot_map = {}
for x in doc.xpath('//Plot[@refobj]'):
ro = x.get('refobj')
image = doc.xpath('//Image[@objid="%s" and @refstream]'%ro)
if image:
imgstr = doc.xpath('//ImageStream[@objid="%s" and @file]'%
image[0].get('refstream'))
if imgstr:
plot_map[ro] = imgstr[0].get('file')
self.log('Converting XML to HTML...')
styledoc = etree.fromstring(P('templates/lrf.xsl', data=True))
media_type = MediaType()
styles = Styles()
text_block = TextBlock(styles, char_button_map, plot_map, log)
canvas = Canvas(doc, styles, text_block, log)
image_block = ImageBlock(canvas)
ruled_line = RuledLine()
extensions = {
('calibre', 'media-type') : media_type,
('calibre', 'text-block') : text_block,
('calibre', 'ruled-line') : ruled_line,
('calibre', 'styles') : styles,
('calibre', 'canvas') : canvas,
('calibre', 'image-block'): image_block,
}
transform = etree.XSLT(styledoc, extensions=extensions)
result = transform(doc)
with open('content.opf', 'wb') as f:
f.write(result)
styles.write()
return os.path.abspath('content.opf')

View File

@ -165,13 +165,16 @@ class BookHeader(object):
if not isinstance(self.title, unicode):
self.title = self.title.decode(self.codec, 'replace')
if self.exth_flag & 0x40:
try:
self.exth = EXTHHeader(raw[16 + self.length:], self.codec, self.title)
self.exth.mi.uid = self.unique_id
try:
self.exth.mi.language = mobi2iana(langid, sublangid)
except:
import traceback
traceback.print_exc()
self.log.exception('Unknown language code')
except:
self.log.exception('Invalid EXTH header')
self.exth_flag = 0
class MetadataHeader(BookHeader):
@ -501,10 +504,20 @@ class MobiReader(object):
height = attrib.pop('height').strip()
if height and '<' not in height and '>' not in height and \
re.search(r'\d+', height):
if tag.tag in ('table', 'td', 'tr'):
pass
elif tag.tag == 'img':
tag.set('height', height)
else:
styles.append('margin-top: %s' % self.ensure_unit(height))
if attrib.has_key('width'):
width = attrib.pop('width').strip()
if width and re.search(r'\d+', width):
if tag.tag in ('table', 'td', 'tr'):
pass
elif tag.tag == 'img':
tag.set('width', width)
else:
styles.append('text-indent: %s' % self.ensure_unit(width))
if width.startswith('-'):
styles.append('margin-left: %s' % self.ensure_unit(width[1:]))

View File

@ -34,6 +34,11 @@ class Extract(ODF2XHTML):
with CurrentDir(odir):
print 'Extracting ODT file...'
html = self.odf2xhtml(stream)
# A blanket img specification like this causes problems
# with EPUB output as the contaiing element often has
# an absolute height and width set that is larger than
# the available screen real estate
html = html.replace('img { width: 100%; height: 100%; }', '')
with open('index.xhtml', 'wb') as f:
f.write(html.encode('utf-8'))
zf = ZipFile(stream, 'r')

View File

@ -759,13 +759,15 @@ class Manifest(object):
% (self.id, self.href, self.media_type)
def _parse_xml(self, data):
parser = etree.XMLParser(recover=True)
try:
return etree.fromstring(data)
return etree.fromstring(data, parser=parser)
except etree.XMLSyntaxError, err:
if getattr(err, 'code', 0) == 26 or str(err).startswith('Entity'):
data = xml_to_unicode(data, strip_encoding_pats=True,
resolve_entities=True)[0]
return etree.fromstring(data)
raise
def _parse_xhtml(self, data):
self.oeb.log.debug('Parsing', self.href, '...')
@ -843,6 +845,7 @@ class Manifest(object):
if not namespace(data.tag):
data.attrib['xmlns'] = XHTML_NS
data = etree.tostring(data, encoding=unicode)
try:
data = etree.fromstring(data)
except:

View File

@ -132,8 +132,8 @@ class Stylizer(object):
stylesheet.namespaces['h'] = XHTML_NS
stylesheets.append(stylesheet)
elif elem.tag == XHTML('link') and elem.get('href') \
and elem.get('rel', 'stylesheet') == 'stylesheet' \
and elem.get('type', CSS_MIME) in OEB_STYLES:
and elem.get('rel', 'stylesheet').lower() == 'stylesheet' \
and elem.get('type', CSS_MIME).lower() in OEB_STYLES:
href = urlnormalize(elem.attrib['href'])
path = item.abshref(href)
sitem = oeb.manifest.hrefs.get(path, None)

View File

@ -362,12 +362,13 @@ class FlowSplitter(object):
self.log.debug('\t\tSplitting...')
root = tree.getroot()
# Split large <pre> tags
for pre in list(root.xpath('//pre')):
for pre in list(XPath('//h:pre')(root)):
text = u''.join(pre.xpath('descendant::text()'))
pre.text = text
for child in list(pre.iterchildren()):
pre.remove(child)
if len(pre.text) > self.max_flow_size*0.5:
self.log.debug('\t\tSplitting large <pre> tag')
frags = self.split_text(pre.text, root, int(0.2*self.max_flow_size))
new_pres = []
for frag in frags:

View File

@ -2,12 +2,41 @@ from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import os, glob, re
import os, glob, re, textwrap
from lxml import etree
from calibre.customize.conversion import InputFormatPlugin
class InlineClass(etree.XSLTExtension):
FMTS = ('italics', 'bold', 'underlined', 'strike-through', 'small-caps')
def __init__(self, log):
etree.XSLTExtension.__init__(self)
self.log = log
self.font_sizes = []
self.colors = []
def execute(self, context, self_node, input_node, output_parent):
classes = ['none']
for x in self.FMTS:
if input_node.get(x, None) == 'true':
classes.append(x)
fs = input_node.get('font-size', False)
if fs:
if fs not in self.font_sizes:
self.font_sizes.append(fs)
classes.append('fs%d'%self.font_sizes.index(fs))
fc = input_node.get('font-color', False)
if fc:
if fc not in self.colors:
self.colors.append(fc)
classes.append('col%d'%self.colors.index(fc))
output_parent.text = ' '.join(classes)
class RTFInput(InputFormatPlugin):
name = 'RTF Input'
@ -21,15 +50,15 @@ class RTFInput(InputFormatPlugin):
parser = ParseRtf(
in_file = stream,
out_file = ofile,
# Convert symbol fonts to unicode equivelents. Default
# Convert symbol fonts to unicode equivalents. Default
# is 1
convert_symbol = 1,
# Convert Zapf fonts to unicode equivelents. Default
# Convert Zapf fonts to unicode equivalents. Default
# is 1.
convert_zapf = 1,
# Convert Wingding fonts to unicode equivelents.
# Convert Wingding fonts to unicode equivalents.
# Default is 1.
convert_wingdings = 1,
@ -63,6 +92,7 @@ class RTFInput(InputFormatPlugin):
def extract_images(self, picts):
self.log('Extracting images...')
count = 0
raw = open(picts, 'rb').read()
starts = []
@ -82,21 +112,66 @@ class RTFInput(InputFormatPlugin):
if len(enc) % 2 == 1:
enc = enc[:-1]
data = enc.decode('hex')
ext = '.jpg'
if 'EMF' in data[:200]:
ext = '.wmf'
elif 'PNG' in data[:200]:
ext = '.png'
count += 1
name = (('%4d'%count).replace(' ', '0'))+ext
name = (('%4d'%count).replace(' ', '0'))+'.wmf'
open(name, 'wb').write(data)
imap[count] = name
#open(name+'.hex', 'wb').write(enc)
return self.convert_images(imap)
def convert_images(self, imap):
from calibre.utils.PythonMagickWand import ImageMagick
with ImageMagick():
for count, val in imap.items():
try:
imap[count] = self.convert_image(val)
except:
self.log.exception('Failed to convert', val)
return imap
def convert_image(self, name):
import calibre.utils.PythonMagickWand as p
img = p.NewMagickWand()
if img < 0:
raise RuntimeError('Cannot create wand.')
if not p.MagickReadImage(img, name):
self.log.warn('Failed to read image:', name)
name = name.replace('.wmf', '.jpg')
p.MagickWriteImage(img, name)
return name
def write_inline_css(self, ic):
font_size_classes = ['span.fs%d { font-size: %spt }'%(i, x) for i, x in
enumerate(ic.font_sizes)]
color_classes = ['span.col%d { color: %s }'%(i, x) for i, x in
enumerate(ic.colors)]
css = textwrap.dedent('''
span.none {
text-decoration: none; font-weight: normal;
font-style: normal; font-variant: normal
}
span.italics { font-style: italic }
span.bold { font-weight: bold }
span.small-caps { font-variant: small-caps }
span.underlined { text-decoration: underline }
span.strike-through { text-decoration: line-through }
''')
css += '\n'+'\n'.join(font_size_classes)
css += '\n' +'\n'.join(color_classes)
with open('styles.css', 'ab') as f:
f.write(css)
def convert(self, stream, options, file_ext, log,
accelerators):
from calibre.ebooks.rtf.xsl import xhtml
from calibre.ebooks.metadata.meta import get_metadata
from calibre.ebooks.metadata.opf2 import OPFCreator
from calibre.ebooks.rtf2xml.ParseRtf import RtfInvalidCodeException
@ -124,15 +199,18 @@ class RTFInput(InputFormatPlugin):
if name is not None:
pict.set('num', name)
self.log('Converting XML to HTML...')
styledoc = etree.fromstring(xhtml)
inline_class = InlineClass(self.log)
styledoc = etree.fromstring(P('templates/rtf.xsl', data=True))
transform = etree.XSLT(styledoc)
extensions = { ('calibre', 'inline-class') : inline_class }
transform = etree.XSLT(styledoc, extensions=extensions)
result = transform(doc)
html = 'index.xhtml'
with open(html, 'wb') as f:
res = transform.tostring(result)
res = res[:100].replace('xmlns:html', 'xmlns') + res[100:]
f.write(res)
self.write_inline_css(inline_class)
stream.seek(0)
mi = get_metadata(stream, 'rtf')
if not mi.title:

View File

@ -43,21 +43,26 @@ class Pict:
self.__out_file = out_file
# this is left over
self.__no_ask = 1
def __initiate_pict_dict(self):
self.__pict_dict = {
'ob<nu<open-brack' : self.__open_br_func,
'cb<nu<clos-brack' : self.__close_br_func,
'tx<nu<__________' : self.__text_func,
}
def __open_br_func(self, line):
return "{\n"
def __close_br_func(self, line):
return "}\n"
def __text_func(self, line):
#tx<nu<__________<true text
return line[17:]
def __make_dir(self):
""" Make a dirctory to put the image data in"""
""" Make a directory to put the image data in"""
base_name = os.path.basename(getattr(self.__orig_file, 'name',
self.__orig_file))
base_name = os.path.splitext(base_name)[0]
@ -97,6 +102,7 @@ class Pict:
pass
if self.__run_level > 1:
sys.stderr.write('Files removed.\n')
def __create_pict_file(self):
"""Create a file for all the pict data to be written to.
"""
@ -104,6 +110,7 @@ class Pict:
write_pic_obj = open(self.__pict_file, 'w')
write_pic_obj.close()
self.__write_pic_obj = open(self.__pict_file, 'a')
def __in_pict_func(self, line):
if self.__cb_count == self.__pict_br_count:
self.__in_pict = 0
@ -115,6 +122,7 @@ class Pict:
line = action(line)
self.__write_pic_obj.write(line)
return 0
def __default(self, line, write_obj):
"""Determine if each token marks the beginning of pict data.
If it does, create a new file to write data to (if that file
@ -142,6 +150,7 @@ class Pict:
self.__write_pic_obj.write("{\\pict\n")
return 0
return 1
def __print_rtf_header(self):
"""Print to pict file the necessary RTF data for the file to be
recognized as an RTF file.
@ -150,6 +159,7 @@ class Pict:
self.__write_pic_obj.write("{\\fonttbl\\f0\\null;} \n")
self.__write_pic_obj.write("{\\colortbl\\red255\\green255\\blue255;} \n")
self.__write_pic_obj.write("\\pard \n")
def process_pict(self):
self.__make_dir()
read_obj = open(self.__file)

View File

@ -19,9 +19,11 @@ class PluginWidget(Widget, Ui_Form):
Widget.__init__(self, parent, 'comic_input',
['colors', 'dont_normalize', 'keep_aspect_ratio', 'right2left',
'despeckle', 'no_sort', 'no_process', 'landscape',
'dont_sharpen', 'disable_trim', 'wide']
'dont_sharpen', 'disable_trim', 'wide', 'output_format']
)
self.db, self.book_id = db, book_id
for x in get_option('output_format').option.choices:
self.opt_output_format.addItem(x)
self.initialize_options(get_option, get_help, db, book_id)
self.opt_no_process.toggle()
self.opt_no_process.toggle()

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>599</width>
<height>305</height>
<height>343</height>
</rect>
</property>
<property name="windowTitle">
@ -100,7 +100,7 @@
</property>
</widget>
</item>
<item row="11" column="0">
<item row="12" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -120,6 +120,19 @@
</property>
</widget>
</item>
<item row="11" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>&amp;Output format:</string>
</property>
<property name="buddy">
<cstring>opt_output_format</cstring>
</property>
</widget>
</item>
<item row="11" column="1">
<widget class="QComboBox" name="opt_output_format"/>
</item>
</layout>
</widget>
<resources/>
@ -263,8 +276,8 @@
<y>15</y>
</hint>
<hint type="destinationlabel">
<x>52</x>
<y>225</y>
<x>56</x>
<y>248</y>
</hint>
</hints>
</connection>
@ -300,5 +313,21 @@
</hint>
</hints>
</connection>
<connection>
<sender>opt_no_process</sender>
<signal>toggled(bool)</signal>
<receiver>opt_output_format</receiver>
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>237</x>
<y>12</y>
</hint>
<hint type="destinationlabel">
<x>370</x>
<y>308</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -14,16 +14,16 @@ from calibre.gui2 import choose_images, error_dialog
from calibre.gui2.convert.metadata_ui import Ui_Form
from calibre.ebooks.metadata import authors_to_string, string_to_authors, \
MetaInformation
from calibre.ebooks.metadata.opf2 import OPFCreator
from calibre.ebooks.metadata.opf2 import metadata_to_opf
from calibre.ptempfile import PersistentTemporaryFile
from calibre.gui2.convert import Widget
def create_opf_file(db, book_id):
mi = db.get_metadata(book_id, index_is_id=True)
mi.application_id = uuid.uuid4()
opf = OPFCreator(os.getcwdu(), mi)
raw = metadata_to_opf(mi)
opf_file = PersistentTemporaryFile('.opf')
opf.render(opf_file)
opf_file.write(raw)
opf_file.close()
return mi, opf_file

View File

@ -23,6 +23,7 @@ from calibre.gui2.convert.debug import DebugWidget
from calibre.ebooks.conversion.plumber import Plumber, supported_input_formats
from calibre.ebooks.conversion.config import delete_specifics
from calibre.customize.ui import available_output_formats
from calibre.customize.conversion import OptionRecommendation
from calibre.utils.config import prefs
@ -115,9 +116,10 @@ class Config(ResizableDialog, Ui_Dialog):
def __init__(self, parent, db, book_id,
preferred_input_format=None, preferred_output_format=None):
ResizableDialog.__init__(self, parent)
self.setup_input_output_formats(db, book_id, preferred_input_format,
preferred_output_format)
self.db, self.book_id = db, book_id
self.setup_input_output_formats(self.db, self.book_id, preferred_input_format,
preferred_output_format)
self.setup_pipeline()
self.connect(self.input_formats, SIGNAL('currentIndexChanged(QString)'),
@ -130,8 +132,14 @@ class Config(ResizableDialog, Ui_Dialog):
self.show_pane)
self.connect(self.groups, SIGNAL('entered(QModelIndex)'),
self.show_group_help)
rb = self.buttonBox.button(self.buttonBox.RestoreDefaults)
self.connect(rb, SIGNAL('clicked()'), self.restore_defaults)
self.groups.setMouseTracking(True)
def restore_defaults(self):
delete_specifics(self.db, self.book_id)
self.setup_pipeline()
@property
def input_format(self):
return unicode(self.input_formats.currentText()).lower()

View File

@ -108,8 +108,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>471</height>
<width>810</width>
<height>492</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
@ -138,7 +138,7 @@
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults</set>
</property>
</widget>
</item>

View File

@ -207,6 +207,9 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
self.series.setEditText(mi.series)
if mi.series_index is not None:
self.series_index.setValue(float(mi.series_index))
if mi.comments and mi.comments.strip():
self.comments.setText(mi.comments)
def set_cover(self):
mi, ext = self.get_selected_format_metadata()
@ -330,6 +333,8 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
if not ext:
ext = ''
size = self.db.sizeof_format(row, ext)
if size is None:
continue
Format(self.formats, ext, size)
@ -545,18 +550,11 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
if self.formats_changed:
self.sync_formats()
title = unicode(self.title.text())
except IOError, err:
if err.errno == 13: # Permission denied
fname = err.filename if err.filename else 'file'
return error_dialog(self, _('Permission denied'),
_('Could not open %s. Is it being used by another'
' program?')%fname, show=True)
raise
self.db.set_title(self.id, title, notify=False)
au = unicode(self.authors.text())
if au:
self.db.set_authors(self.id, string_to_authors(au), notify=False)
aus = qstring_to_unicode(self.author_sort.text())
aus = unicode(self.author_sort.text())
if aus:
self.db.set_author_sort(self.id, aus, notify=False)
self.db.set_isbn(self.id,
@ -572,6 +570,14 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
if self.cover_changed and self.cover_data is not None:
self.db.set_cover(self.id, self.cover_data)
except IOError, err:
if err.errno == 13: # Permission denied
fname = err.filename if err.filename else 'file'
return error_dialog(self, _('Permission denied'),
_('Could not open %s. Is it being used by another'
' program?')%fname, show=True)
raise
QDialog.accept(self)
if callable(self.accepted_callback):
self.accepted_callback(self.id)

View File

@ -15,7 +15,7 @@ from PyQt4.Qt import Qt, SIGNAL, QObject, QCoreApplication, QUrl, QTimer, \
from PyQt4.QtSvg import QSvgRenderer
from calibre import prints, patheq
from calibre.constants import __version__, __appname__, \
from calibre.constants import __version__, __appname__, isfrozen, islinux, \
iswindows, isosx, filesystem_encoding
from calibre.utils.filenames import ascii_filename
from calibre.ptempfile import PersistentTemporaryFile
@ -1290,7 +1290,15 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.job_manager.launch_gui_app(viewer,
kwargs=dict(args=args))
else:
paths = os.environ.get('LD_LIBRARY_PATH',
'').split(os.pathsep)
paths = [x for x in paths if x]
if isfrozen and islinux and paths:
npaths = [x for x in paths if x != sys.frozen_path]
os.environ['LD_LIBRARY_PATH'] = os.pathsep.join(npaths)
QDesktopServices.openUrl(QUrl.fromLocalFile(name))#launch(name)
if isfrozen and islinux and paths:
os.environ['LD_LIBRARY_PATH'] = os.pathsep.join(paths)
time.sleep(2) # User feedback
finally:
self.unsetCursor()

View File

@ -1081,6 +1081,10 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
return cPickle.loads(str(data))
return None
def delete_conversion_options(self, id, format):
self.conn.execute('DELETE FROM conversion_options WHERE book=? AND format=?',
(id, format.upper()))
def add_format(self, index, ext, stream, index_is_id=False):
'''

View File

@ -413,6 +413,8 @@ class LibraryDatabase2(LibraryDatabase):
self.library_path = os.path.abspath(library_path)
self.row_factory = row_factory
self.dbpath = os.path.join(library_path, 'metadata.db')
self.dbpath = os.environ.get('CALIBRE_OVERRIDE_DATABASE_PATH',
self.dbpath)
if isinstance(self.dbpath, unicode):
self.dbpath = self.dbpath.encode(filesystem_encoding)
self.connect()

View File

@ -366,6 +366,21 @@ class LibraryServer(object):
return base.intersection(epub.union(pdb))
def stanza_sortby_subcategory(self, updated, sortby, offset):
pat = re.compile(r'\(.*\)')
def clean_author(x):
return pat.sub('', x).strip()
def author_cmp(x, y):
x = x if ',' in x else clean_author(x).rpartition(' ')[-1]
y = y if ',' in y else clean_author(y).rpartition(' ')[-1]
return cmp(x.lower(), y.lower())
def get_author(x):
pref, ___, suff = clean_author(x).rpartition(' ')
return suff + (', '+pref) if pref else suff
what, subtitle = sortby[2:], ''
if sortby == 'byseries':
data = self.db.all_series()
@ -379,13 +394,15 @@ class LibraryServer(object):
data = self.db.all_tags2()
data = [(x[0], x[1], len(self.get_matches('tags', x[1]))) for x in data]
subtitle = 'Books by tag'
fcmp = author_cmp if sortby == 'byauthor' else cmp
data = [x for x in data if x[2] > 0]
data.sort(cmp=lambda x, y: cmp(x[1], y[1]))
data.sort(cmp=lambda x, y: fcmp(x[1], y[1]))
next_offset = offset + self.max_stanza_items
rdata = data[offset:next_offset]
if next_offset >= len(data):
next_offset = -1
entries = [self.STANZA_SUBCATALOG_ENTRY.generate(title=title, id=id,
gt = get_author if sortby == 'byauthor' else lambda x: x
entries = [self.STANZA_SUBCATALOG_ENTRY.generate(title=gt(title), id=id,
what=what, updated=updated, count=c).render('xml').decode('utf-8') for id,
title, c in rdata]
next_link = ''

View File

@ -256,7 +256,13 @@ def setup_udev_rules(group_file, reload, fatal_errors):
sys.stdout.flush()
groups = open(group_file, 'rb').read()
group = 'plugdev' if 'plugdev' in groups else 'usb'
udev = open_file('/etc/udev/rules.d/95-calibre.rules')
old_udev = '/etc/udev/rules.d/95-calibre.rules'
if os.path.exists(old_udev):
os.remove(old_udev)
if os.path.exists('/lib/udev/rules.d'):
udev = open_file('/lib/udev/rules.d/95-calibre.rules')
else:
udev = open_file(old_udev)
manifest.append(udev.name)
udev.write('''# Sony Reader PRS-500\n'''
'''BUS=="usb", SYSFS{idProduct}=="029b", SYSFS{idVendor}=="054c", MODE="660", GROUP="%s"\n'''%(group,)

View File

@ -20,7 +20,7 @@ What formats does |app| support conversion to/from?
|app| supports the conversion of many input formats to many output formats.
It can convert every input format in the following list, to every output format.
*Input Formats:* CBZ, CBR, CBC, EPUB, FB2, HTML, LIT, MOBI, ODT, PDF, PRC**, PDB, PML, RB, RTF, TXT
*Input Formats:* CBZ, CBR, CBC, EPUB, FB2, HTML, LIT, LRF, MOBI, ODT, PDF, PRC**, PDB, PML, RB, RTF, TXT
*Output Formats:* EPUB, FB2, OEB, LIT, LRF, MOBI, PDB, PML, RB, PDF, TXT
** PRC is a generic format, |app| supports PRC files with TextRead and MOBIBook headers

View File

@ -21,6 +21,7 @@ DEPENDENCIES = [
('dnspython', '1.6.0', 'dnspython', 'dnspython', 'dnspython', 'dnspython'),
('poppler-qt4', '0.10.6', 'poppler-qt4', 'poppler-qt4', 'poppler-qt4', 'poppler-qt4'),
('podofo', '0.7', 'podofo', 'podofo', 'podofo', 'podofo'),
('libwmf', '0.2.8', 'libwmf', 'libwmf', 'libwmf', 'libwmf'),
]

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.12\n"
"POT-Creation-Date: 2009-09-13 10:26+MDT\n"
"PO-Revision-Date: 2009-09-13 10:26+MDT\n"
"Project-Id-Version: calibre 0.6.13\n"
"POT-Creation-Date: 2009-09-18 12:45+MDT\n"
"PO-Revision-Date: 2009-09-18 12:45+MDT\n"
"Last-Translator: Automatically generated\n"
"Language-Team: LANGUAGE\n"
"MIME-Version: 1.0\n"
@ -26,7 +26,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/prs505/books.py:199
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:703
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:706
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:403
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:410
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:65
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:67
#: /home/kovid/work/calibre/src/calibre/ebooks/html/input.py:318
@ -60,13 +60,13 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:79
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:121
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:154
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:575
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:762
#: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:44
#: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:46
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:883
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:888
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:944
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:588
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:775
#: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:49
#: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:51
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:886
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:891
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:947
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/reader.py:135
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/reader.py:137
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/transforms/jacket.py:105
@ -92,8 +92,8 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/split.py:82
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/writer.py:28
#: /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/ebooks/rtf/input.py:217
#: /home/kovid/work/calibre/src/calibre/ebooks/rtf/input.py:219
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:261
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:268
#: /home/kovid/work/calibre/src/calibre/gui2/add.py:111
@ -109,7 +109,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf.py:48
#: /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:397
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:402
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:41
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:42
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:391
@ -119,15 +119,15 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:211
#: /home/kovid/work/calibre/src/calibre/library/cli.py:277
#: /home/kovid/work/calibre/src/calibre/library/database.py:917
#: /home/kovid/work/calibre/src/calibre/library/database2.py:652
#: /home/kovid/work/calibre/src/calibre/library/database2.py:664
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1059
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1096
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1426
#: /home/kovid/work/calibre/src/calibre/library/database2.py:654
#: /home/kovid/work/calibre/src/calibre/library/database2.py:666
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1061
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1098
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1428
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1537
#: /home/kovid/work/calibre/src/calibre/library/server.py:476
#: /home/kovid/work/calibre/src/calibre/library/server.py:548
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1430
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1539
#: /home/kovid/work/calibre/src/calibre/library/server.py:493
#: /home/kovid/work/calibre/src/calibre/library/server.py:565
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:103
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:45
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:63
@ -525,9 +525,9 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:686
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:434
#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:83
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1003
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1007
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1330
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1005
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1009
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1332
msgid "News"
msgstr ""
@ -586,51 +586,55 @@ msgid ""
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:274
msgid "Number of colors for grayscale image conversion. Default: %default"
msgid "Number of colors for grayscale image conversion. Default: %default. Values of less than 256 may result in blurred text on your device if you are creating your comics in EPUB format."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:276
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:278
msgid "Disable normalize (improve contrast) color range for pictures. Default: False"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:279
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:281
msgid "Maintain picture aspect ratio. Default is to fill the screen."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:281
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:283
msgid "Disable sharpening."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:283
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:285
msgid "Disable trimming of comic pages. For some comics, trimming might remove content as well as borders."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:286
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:288
msgid "Don't split landscape images into two portrait images"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:288
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:290
msgid "Keep aspect ratio and scale image using screen height as image width for viewing in landscape mode."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:291
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:293
msgid "Used for right-to-left publications like manga. Causes landscape pages to be split into portrait pages from right to left."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:295
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:297
msgid "Enable Despeckle. Reduces speckle noise. May greatly increase processing time."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:298
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:300
msgid "Don't sort the files found in the comic alphabetically by name. Instead use the order they were added to the comic."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:302
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:304
msgid "The format that images in the created ebook are converted to. You can experiment to see which format gives you optimal size and look on your device."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:308
msgid "Apply no processing to the image"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:427
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:438
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:434
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:445
msgid "Page"
msgstr ""
@ -1434,7 +1438,7 @@ msgid ""
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf2.py:1055
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1307
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1310
msgid "Cover"
msgstr ""
@ -1463,70 +1467,70 @@ msgstr ""
msgid "All articles"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1308
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1311
msgid "Title Page"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1309
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1312
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/transforms/htmltoc.py:15
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:51
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:168
msgid "Table of Contents"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1310
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1313
msgid "Index"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1311
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1314
msgid "Glossary"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1312
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1315
msgid "Acknowledgements"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1313
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1316
msgid "Bibliography"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1314
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1317
msgid "Colophon"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1315
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1318
msgid "Copyright"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1316
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1319
msgid "Dedication"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1317
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1320
msgid "Epigraph"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1318
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1321
msgid "Foreword"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1319
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1322
msgid "List of Illustrations"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1320
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1323
msgid "List of Tables"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1321
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1324
msgid "Notes"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1322
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1325
msgid "Preface"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1323
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1326
msgid "Main Text"
msgstr ""
@ -1814,7 +1818,7 @@ msgstr ""
msgid "Specify the character encoding of the output document. The default is cp1252."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/rtf/input.py:108
#: /home/kovid/work/calibre/src/calibre/ebooks/rtf/input.py:183
msgid "This RTF file has a feature calibre does not support. Convert it to HTML first and then try it."
msgstr ""
@ -2001,7 +2005,7 @@ msgid "Bulk Convert"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/bulk.py:73
#: /home/kovid/work/calibre/src/calibre/gui2/convert/single.py:177
#: /home/kovid/work/calibre/src/calibre/gui2/convert/single.py:184
msgid "Options specific to the output format."
msgstr ""
@ -2033,7 +2037,7 @@ msgstr ""
msgid "input"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input_ui.py:76
#: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input_ui.py:84
#: /home/kovid/work/calibre/src/calibre/gui2/convert/debug_ui.py:45
#: /home/kovid/work/calibre/src/calibre/gui2/convert/epub_output_ui.py:41
#: /home/kovid/work/calibre/src/calibre/gui2/convert/fb2_input_ui.py:28
@ -2061,60 +2065,65 @@ msgstr ""
msgid "Form"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input_ui.py:77
#: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input_ui.py:85
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:94
msgid "&Number of Colors:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input_ui.py:78
#: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input_ui.py:86
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:96
msgid "Disable &normalize"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input_ui.py:79
#: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input_ui.py:87
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:97
msgid "Keep &aspect ratio"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input_ui.py:80
#: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input_ui.py:88
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:98
msgid "Disable &Sharpening"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input_ui.py:81
#: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input_ui.py:89
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:104
msgid "Disable &Trimming"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input_ui.py:82
#: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input_ui.py:90
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:103
msgid "&Wide"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input_ui.py:83
#: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input_ui.py:91
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:99
msgid "&Landscape"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input_ui.py:84
#: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input_ui.py:92
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:101
msgid "&Right to left"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input_ui.py:85
#: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input_ui.py:93
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:100
msgid "Don't so&rt"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input_ui.py:86
#: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input_ui.py:94
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:102
msgid "De&speckle"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input_ui.py:87
#: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input_ui.py:95
msgid "&Disable comic processing"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input_ui.py:96
#: /home/kovid/work/calibre/src/calibre/gui2/convert/single_ui.py:107
msgid "&Output format:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/debug.py:19
msgid "Debug"
msgstr ""
@ -2571,7 +2580,7 @@ msgid "RB Output"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/regex_builder.py:76
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1313
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1321
msgid "Choose the format to view"
msgstr ""
@ -2603,11 +2612,11 @@ msgstr ""
msgid "Regex:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/single.py:163
#: /home/kovid/work/calibre/src/calibre/gui2/convert/single.py:170
msgid "Convert"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/single.py:188
#: /home/kovid/work/calibre/src/calibre/gui2/convert/single.py:195
msgid "Options specific to the input format."
msgstr ""
@ -2622,10 +2631,6 @@ msgstr ""
msgid "&Input format:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/single_ui.py:107
msgid "&Output format:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection.py:17
msgid ""
"Structure\n"
@ -2652,22 +2657,22 @@ msgstr ""
msgid "Footer regular expression:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection.py:51
#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection.py:56
#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:72
msgid "Invalid regular expression"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection.py:52
#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection.py:57
#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:73
msgid "Invalid regular expression: %s"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection.py:57
#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection.py:62
#: /home/kovid/work/calibre/src/calibre/gui2/convert/toc.py:38
msgid "Invalid XPath"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection.py:58
#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection.py:63
#: /home/kovid/work/calibre/src/calibre/gui2/convert/toc.py:39
msgid "The XPath expression %s is invalid."
msgstr ""
@ -3807,67 +3812,67 @@ msgstr ""
msgid "Could not read metadata from %s format"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:221
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:227
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:224
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:230
msgid "Could not read cover"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:222
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:225
msgid "Could not read cover from %s format"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:228
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:231
msgid "The cover in the %s format is invalid"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:265
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:268
msgid "Abort the editing of all remaining books"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:454
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:459
msgid "Downloading cover..."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:466
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:471
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:477
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:476
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:482
msgid "Cannot fetch cover"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:467
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:478
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:472
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:483
msgid "<b>Could not fetch cover.</b><br/>"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:468
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:473
msgid "The download timed out."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:472
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:477
msgid "Could not find cover for this book. Try specifying the ISBN first."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:484
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:489
msgid "Bad cover"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:485
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:490
msgid "The cover is not a valid picture"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:524
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:529
msgid "Cannot fetch metadata"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:525
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:530
msgid "You must specify at least one of ISBN, Title, Authors or Publisher"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:551
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:576
msgid "Permission denied"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:552
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:577
msgid "Could not open %s. Is it being used by another program?"
msgstr ""
@ -4677,7 +4682,7 @@ msgid "Save to disk in a single directory"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:280
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1415
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1423
msgid "Save only %s format to disk"
msgstr ""
@ -4717,7 +4722,7 @@ msgid "Calibre Library"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:437
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1558
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1566
msgid "Choose a location for your ebook library."
msgstr ""
@ -4891,158 +4896,158 @@ msgstr ""
msgid "Cannot convert"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1307
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1326
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1315
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1334
msgid "No book selected"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1307
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1357
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1315
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1365
msgid "Cannot view"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1325
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1333
msgid "Cannot open folder"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1342
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1350
msgid "Multiple Books Selected"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1343
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1351
msgid "You are attempting to open %d books. Opening too many books at once can be slow and have a negative effect on the responsiveness of your computer. Once started the process cannot be stopped until complete. Do you wish to continue?"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1358
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1366
msgid "%s has no available formats."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1399
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1407
msgid "Cannot configure"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1400
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1408
msgid "Cannot configure while there are running jobs."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1443
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1451
msgid "No detailed info available"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1444
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1452
msgid "No detailed information is available for books on the device."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1496
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1504
msgid "Error talking to device"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1497
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1505
msgid "There was a temporary error talking to the device. Please unplug and reconnect the device and or reboot."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1520
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1538
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1528
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1546
msgid "Conversion Error"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1521
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1529
msgid "<p>Could not convert: %s<p>It is a <a href=\"%s\">DRM</a>ed book. You must first remove the DRM using third party tools."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1539
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1547
msgid "<b>Failed</b>"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1567
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1575
msgid "Invalid library location"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1568
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1576
msgid "Could not access %s. Using %s as the library."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1615
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1623
msgid "is the result of the efforts of many volunteers from all over the world. If you find it useful, please consider donating to support its development."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1639
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1647
msgid "There are active jobs. Are you sure you want to quit?"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1642
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1650
msgid ""
" is communicating with the device!<br>\n"
" Quitting may cause corruption on the device.<br>\n"
" Are you sure you want to quit?"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1646
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1654
msgid "WARNING: Active jobs"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1697
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1705
msgid "will keep running in the system tray. To close it, choose <b>Quit</b> in the context menu of the system tray."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1716
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1724
msgid "<span style=\"color:red; font-weight:bold\">Latest version: <a href=\"%s\">%s</a></span>"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1724
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1732
msgid "Update available"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1725
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1733
msgid "%s has been updated to version %s. See the <a href=\"http://calibre.kovidgoyal.net/wiki/Changelog\">new features</a>. Visit the download page?"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1743
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1751
msgid "Use the library located at the specified path."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1745
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1753
msgid "Start minimized to system tray."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1747
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1755
msgid "Log debugging information to console"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1749
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1757
msgid "Do not check for updates"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1797
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1805
msgid "If you are sure it is not running"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1799
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1807
msgid "Cannot Start "
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1800
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1808
msgid "%s is already running."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1803
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1811
msgid "may be running in the system tray, in the"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1805
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1813
msgid "upper right region of the screen."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1807
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1815
msgid "lower right region of the screen."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1810
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1818
msgid "try rebooting your computer."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1812
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1824
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1820
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1832
msgid "try deleting the file"
msgstr ""
@ -5229,46 +5234,46 @@ msgid "Publishers"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:34
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:105
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:106
msgid "Starting conversion of %d books"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:64
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:183
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:184
msgid "Convert book %d of %d (%s)"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:90
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:203
msgid "Could not convert some books"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:91
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:204
msgid "Could not convert some books"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:92
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:205
msgid "Could not convert %d of %d books, because no suitable source format was found."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:122
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:123
msgid "Queueing books for bulk conversion"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:182
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:183
msgid "Queueing "
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:236
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:237
msgid "You must set a username and password for %s"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:241
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:242
msgid "Fetch news from "
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:252
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:253
msgid "Convert existing"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:253
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:254
msgid "The following books have already been converted to %s format. Do you wish to reconvert them?"
msgstr ""
@ -6120,27 +6125,27 @@ msgid ""
"For help on an individual command: %%prog command --help\n"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1563
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1565
msgid "<p>Migrating old database to ebook library in %s<br><center>"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1592
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1594
msgid "Copying <b>%s</b>"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1609
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1611
msgid "Compacting database"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1697
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1699
msgid "Checking SQL integrity..."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1734
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1736
msgid "Checking for missing files."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1758
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1760
msgid "Checked id"
msgstr ""
@ -6240,7 +6245,7 @@ msgstr ""
msgid "Password to access your calibre library. Username is "
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server.py:628
#: /home/kovid/work/calibre/src/calibre/library/server.py:645
msgid ""
"[options]\n"
"\n"
@ -6466,7 +6471,7 @@ msgstr ""
msgid "Article download failed: %s"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1022
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1024
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_borba.py:81
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_glas_srpske.py:77
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_instapaper.py:59
@ -6483,12 +6488,12 @@ msgid "sr-Latn-RS"
msgstr ""
#:
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_le_monde.py:108
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_le_monde.py:119
msgid "Skipping duplicated article: %s"
msgstr ""
#:
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_le_monde.py:113
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_le_monde.py:126
msgid "Skipping filtered article: %s"
msgstr ""

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

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

@ -83,6 +83,9 @@ if isosx:
elif iswindows:
_lib = os.path.join(os.path.dirname(sys.executable), 'CORE_RL_wand_.dll') \
if isfrozen else 'CORE_RL_wand_'
else:
if isfrozen:
_lib = os.path.join(sys.frozen_path, 'libMagickWand.so')
else:
_lib = util.find_library('MagickWand')
if _lib is None:

View File

@ -12,12 +12,47 @@ BASH completion for calibre commands that are too complex for simple
completion.
'''
import sys, os, shlex, glob, re
from functools import partial
import sys, os, shlex, glob, re, cPickle
def prints(*args, **kwargs):
'''
Print unicode arguments safely by encoding them to preferred_encoding
Has the same signature as the print function from Python 3, except for the
additional keyword argument safe_encode, which if set to True will cause the
function to use repr when encoding fails.
'''
file = kwargs.get('file', sys.stdout)
sep = kwargs.get('sep', ' ')
end = kwargs.get('end', '\n')
enc = 'utf-8'
safe_encode = kwargs.get('safe_encode', False)
for i, arg in enumerate(args):
if isinstance(arg, unicode):
try:
arg = arg.encode(enc)
except UnicodeEncodeError:
if not safe_encode:
raise
arg = repr(arg)
if not isinstance(arg, str):
try:
arg = str(arg)
except ValueError:
arg = unicode(arg)
if isinstance(arg, unicode):
try:
arg = arg.encode(enc)
except UnicodeEncodeError:
if not safe_encode:
raise
arg = repr(arg)
file.write(arg)
if i != len(args)-1:
file.write(sep)
file.write(end)
from calibre import prints
debug = partial(prints, file=sys.stderr)
def split(src):
try:
@ -47,10 +82,10 @@ def get_opts_from_parser(parser, prefix):
if x.startswith(prefix):
yield x
for o in parser.option_list:
for x in do_opt(o): yield x
for x in do_opt(o): yield x+' '
for g in parser.option_groups:
for o in g.option_list:
for x in do_opt(o): yield x
for x in do_opt(o): yield x+' '
def send(ans):
pat = re.compile('([^0-9a-zA-Z_./-])')
@ -74,6 +109,8 @@ class EbookConvert(object):
self.words = words
self.prefix = prefix
self.previous = words[-2 if prefix else -1]
self.cache = cPickle.load(open(os.path.join(sys.resources_location,
'ebook-convert-complete.pickle'), 'rb'))
self.complete(wc)
def complete(self, wc):
@ -81,6 +118,14 @@ class EbookConvert(object):
self.complete_input()
elif wc == 3:
self.complete_output()
else:
q = list(self.words[1:3])
q = [os.path.splitext(x)[0 if x.startswith('.') else 1].partition('.')[-1].lower() for x in q]
if not q[1]:
q[1] = 'oeb'
q = tuple(q)
if q in self.cache:
ans = [x for x in self.cache[q] if x.startswith(self.prefix)]
else:
from calibre.ebooks.conversion.cli import create_option_parser
from calibre.utils.logging import Log
@ -98,25 +143,18 @@ class EbookConvert(object):
send(ans)
def complete_input(self):
from calibre.ebooks.conversion.plumber import supported_input_formats
ans = list(files_and_dirs(self.prefix, supported_input_formats()))
from calibre.web.feeds.recipes import recipes
ans += [t.title+'.recipe ' for t in recipes if
(t.title+'.recipe').startswith(self.prefix)]
ans = list(files_and_dirs(self.prefix, self.cache['input_fmts']))
ans += [t for t in self.cache['input_recipes'] if
t.startswith(self.prefix)]
send(ans)
def complete_output(self):
from calibre.customize.ui import available_output_formats
fmts = available_output_formats()
fmts = self.cache['output']
ans = list(files_and_dirs(self.prefix, fmts))
ans += ['.'+x+' ' for x in fmts if ('.'+x).startswith(self.prefix)]
send(ans)
def main(args=sys.argv):
comp_line, pos = os.environ['COMP_LINE'], int(os.environ['COMP_POINT'])
module = split(comp_line)[0].split(os.sep)[-1]

View File

@ -1019,6 +1019,8 @@ class BasicNewsRecipe(Recipe):
title, url = None, obj
else:
title, url = obj
if url.startswith('feed://'):
url = 'http'+url[4:]
self.report_progress(0, _('Fetching feed')+' %s...'%(title if title else url))
try:
with closing(self.browser.open(url)) as f:

View File

@ -57,7 +57,7 @@ recipe_modules = ['recipe_' + r for r in (
'monitor', 'republika', 'beta', 'beta_en', 'glasjavnosti',
'esquire', 'livemint', 'thedgesingapore', 'darknet', 'rga',
'intelligencer', 'theoldfoodie', 'hln_be', 'honvedelem',
'the_new_republic', 'philly',
'the_new_republic', 'philly', 'salon', 'tweakers',
)]

View File

@ -10,8 +10,8 @@ from calibre.web.feeds.news import BasicNewsRecipe
class DerStandardRecipe(BasicNewsRecipe):
title = u'derStandard'
__author__ = 'Gerhard Aigner'
description = u'Nachrichten aus Österreich'
__author__ = 'Gerhard Aigner and Sujata Raman'
description = u'Nachrichten aus ??sterreich'
publisher ='derStandard.at'
category = 'news, politics, nachrichten, Austria'
use_embedded_content = False
@ -21,18 +21,15 @@ class DerStandardRecipe(BasicNewsRecipe):
encoding = 'utf-8'
language = 'de'
recursions = 0
oldest_article = 1
max_articles_per_feed = 100
html2lrf_options = [
'--comment' , description
, '--category' , category
, '--publisher', publisher
]
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
extra_css = '''
.artikelBody{font-family:Arial,Helvetica,sans-serif;}
.artikelLeft{font-family:Arial,Helvetica,sans-serif;font-size:x-small;}
h4{color:#404450;font-size:x-small;}
h6{color:#404450; font-size:x-small;}
'''
feeds = [(u'International', u'http://derstandard.at/?page=rss&ressort=internationalpolitik'),
(u'Inland', u'http://derstandard.at/?page=rss&ressort=innenpolitik'),
(u'Wirtschaft', u'http://derstandard.at/?page=rss&ressort=investor'),
@ -43,22 +40,34 @@ class DerStandardRecipe(BasicNewsRecipe):
(u'Kultur', u'http://derstandard.at/?page=rss&ressort=kultur'),
(u'Wissenschaft', u'http://derstandard.at/?page=rss&ressort=wissenschaft'),
(u'Gesundheit', u'http://derstandard.at/?page=rss&ressort=gesundheit'),
(u'Bildung', u'http://derstandard.at/?page=rss&ressort=subildung')]
remove_tags = [dict(name='div'), dict(name='a'), dict(name='link'), dict(name='meta'),
dict(name='form',attrs={'name':'sitesearch'}), dict(name='hr')]
(u'Bildung', u'http://derstandard.at/?page=rss&ressort=subildung')
]
keep_only_tags = [
dict(name='div', attrs={'class':["artikel","artikelLeft","artikelBody"]}) ,
]
remove_tags = [
dict(name='link'), dict(name='meta'),dict(name='iframe'),dict(name='style'),
dict(name='form',attrs={'name':'sitesearch'}), dict(name='hr'),
dict(name='div', attrs={'class':["diashow"]})]
preprocess_regexps = [
(re.compile(r'\[[\d]*\]', re.DOTALL|re.IGNORECASE), lambda match: ''),
(re.compile(r'bgcolor="#\w{3,6}"', re.DOTALL|re.IGNORECASE), lambda match: '')
]
def print_version(self, url):
return url.replace('?id=', 'txt/?id=')
filter_regexps = [r'/r[1-9]*']
def get_article_url(self, article):
'''if the article links to a index page (ressort) or a picture gallery
(ansichtssache), don't add it'''
if ( article.link.count('ressort') > 0 or article.title.lower().count('ansichtssache') > 0 ):
return None
matchObj = re.search( re.compile(r'/r'+'[1-9]*',flags=0), article.link,flags=0)
if matchObj:
return None
return article.link
def preprocess_html(self, soup):
@ -66,4 +75,7 @@ class DerStandardRecipe(BasicNewsRecipe):
soup.html['lang'] = self.lang
mtag = '<meta http-equiv="Content-Type" content="text/html; charset=' + self.encoding + '">'
soup.head.insert(0,mtag)
for t in soup.findAll(['ul', 'li']):
t.name = 'div'
return soup

View File

@ -8,7 +8,7 @@ Fetch FTD.
from calibre.web.feeds.news import BasicNewsRecipe
class FTheiseDe(BasicNewsRecipe):
class FTDe(BasicNewsRecipe):
title = 'FTD'
description = 'Financial Times Deutschland'
@ -16,24 +16,47 @@ class FTheiseDe(BasicNewsRecipe):
use_embedded_content = False
timefmt = ' [%d %b %Y]'
language = 'de'
max_articles_per_feed = 40
no_stylesheets = True
remove_tags = [dict(id='navi_top'),
dict(id='topbanner'),
dict(id='seitenkopf'),
dict(id='BoxA-0-0-0'),
dict(id='footer'),
dict(id='rating_open'),
dict(id='ADS_Top'),
dict(id='spinner'),
dict(id='ftd-contentad'),
dict(id='nava-50009007-1-0'),
dict(id='navli-50009007-1-0'),
dict(id='starRating'),
dict(id='saveRating'),
dict(id='yLayer'),
dict(id='yTip'),
dict(id='ftdCookieStorage'),
dict(id='ADS_Middle1'),
#dict(id='IDMS_ajax_chart_price_information_table'),
dict(id='ivwimg'),
dict(name='span', attrs={'class':'rsaquo'}),
dict(name='p', attrs={'class':'zwischenhead'}),
dict(name='ul', attrs={'class':'list'}),
dict(name='ul', attrs={'class':'nav'}),
dict(name='p', attrs={'class':'articleOptionHead'}),
dict(name='p', attrs={'class':'articleOptionFoot'}),
dict(name='div', attrs={'class':'chartBox'}),
dict(name='div', attrs={'class':'ratingOpt starRatingContainer articleOptionFootFrame'}),
dict(name='div', attrs={'class':'box boxArticleBasic boxComments boxTransparent'}),
dict(name='div', attrs={'class':'box boxNavTabs '}),
dict(name='span', attrs={'class':'vote_455857'}),
dict(name='div', attrs={'class':'relatedhalb'}),
dict(name='div', attrs={'class':'box boxListScrollOutline'}),
dict(name='div', attrs={'class':'tagCloud'}),
dict(name='div', attrs={'class':'box boxArticleBasic boxNavTabsOutline'}),
dict(name='div', attrs={'class':'ftdHpNav'}),
dict(name='div', attrs={'class':'ftdHead'}),
dict(name='div', attrs={'class':'ftdTicker'}),
dict(name='div', attrs={'class':'ftdBreadcrumb'}),
dict(name='div', attrs={'class':'bpoll'}),
dict(name='div', attrs={'class':'pollokknopf'}),
dict(name='div', attrs={'class':'videohint'}),
@ -43,9 +66,24 @@ class FTheiseDe(BasicNewsRecipe):
dict(name='div', attrs={'class':'abspielen'}),
dict(name='div', attrs={'class':'wertungoben'}),
dict(name='div', attrs={'class':'artikelfuss'}),
dict(name='a', attrs={'class':'rating'}),
dict(name='div', attrs={'class':'articleOptionFootFrame'}),
dict(name='div', attrs={'class':'artikelsplitfaq'})]
remove_tags_after = [dict(name='div', attrs={'class':'artikelfuss'})]
remove_tags_after = [dict(name='a', attrs={'class':'more'})]
feeds = [ ('FTD', 'http://www.ftd.de/static/ticker/ftd-topnews.rdf') ]
feeds = [ ('Finanzen', 'http://www.ftd.de/rss2/finanzen/maerkte'),
('Meinungshungrige', 'http://www.ftd.de/rss2/meinungshungrige'),
('Unternehmen', 'http://www.ftd.de/rss2/unternehmen'),
('Politik', 'http://www.ftd.de/rss2/politik'),
('Karriere_Management', 'http://www.ftd.de/rss2/karriere-management'),
('IT_Medien', 'http://www.ftd.de/rss2/it-medien'),
('Wissen', 'http://www.ftd.de/rss2/wissen'),
('Sport', 'http://www.ftd.de/rss2/sport'),
('Auto', 'http://www.ftd.de/rss2/auto'),
('Lifestyle', 'http://www.ftd.de/rss2/lifestyle')
]
def print_version(self, url):
return url + '?mode=print'

View File

@ -7,13 +7,12 @@ lemonde.fr
'''
import re
#from datetime import date
from calibre.web.feeds.news import BasicNewsRecipe
class LeMonde(BasicNewsRecipe):
title = 'LeMonde.fr'
__author__ = 'Mathieu Godlewski <mathieu at godlewski.fr>'
__author__ = 'Mathieu Godlewski and Sujata Raman'
description = 'Global news in french'
oldest_article = 3
language = 'fr'
@ -27,20 +26,23 @@ class LeMonde(BasicNewsRecipe):
extra_css = '''
.dateline{color:#666666;font-family:verdana,sans-serif;font-size:xx-small;}
.dateline{color:#666666;font-family:verdana,sans-serif;font-size:x-small;}
.author{font-family:verdana,sans-serif;font-size:x-small;color:#222222;}
.articleImage{color:#666666;font-family:verdana,sans-serif;font-size:x-small;}
.mainText{font-family:Georgia,serif;color:#222222;}
.LM_articleText{font-family:Georgia,serif;}
.LM_articleText{font-family:Arial,Helvetica,sans-serif;}
.LM_titleZone{font-family:Arial,Helvetica,sans-serif;}
.mainContent{font-family:Georgia,serif;}
.mainTitle{font-family:Georgia,serif;}
.LM_content{font-family:Georgia,serif;}
.LM_caption{font-family:Georgia,serif;font-size:-small;}
.LM_imageSource{font-family:Arial,Helvetica,sans-serif;font-size:x-small;color:#666666;}
h1{font-family:Arial,Helvetica,sans-serif;font-size:medium;color:#000000;}
.post{font-family:Arial,Helvetica,sans-serif;}
.mainTitle{font-family:Georgia,serif;}
.content{font-family:Georgia,serif;}
.LM_caption{font-family:Georgia,serif;font-size:xx-small;}
.LM_imageSource{font-family:Arial,Helvetica,sans-serif;font-size:xx-small;color:#666666;}
h1{font-family:Georgia,serif;font-size:medium;color:#000000;}
.entry{font-family:Georgia,Times New Roman,Times,serif;}
.mainTitle{font-family:Georgia,Times New Roman,Times,serif;}
h2{font-family:Georgia,Times New Roman,Times,serif;font-size:large;}
small{{font-family:Arial,Helvetica,sans-serif;font-size:xx-small;}
.entry{font-family:Georgia,serif;}
h2{font-family:Arial,Helvetica,sans-serif;font-size:large;}
small{font-family:Arial,Helvetica,sans-serif; color:#ED1B23;}
'''
feeds = [
@ -71,11 +73,10 @@ class LeMonde(BasicNewsRecipe):
dict(name='iframe', attrs={}),
dict(name='table', attrs={'id':["toolBox"]}),
dict(name='table', attrs={'class':["bottomToolBox"]}),
dict(name='div', attrs={'class':["pageNavigation","fenetreBoxesContainer","breakingNews","LM_toolsBottom","LM_comments","LM_tools","pave_meme_sujet_hidden","boxMemeSujet"]}),
dict(name='div', attrs={'class':["pageNavigation","LM_pagination","fenetreBoxesContainer","breakingNews","LM_toolsBottom","LM_comments","LM_tools","pave_meme_sujet_hidden","boxMemeSujet"]}),
dict(name='div', attrs={'id':["miniUne","LM_sideBar"]}),
]
preprocess_regexps = [ (re.compile(i[0], re.IGNORECASE|re.DOTALL), i[1]) for i in
[
(r'<html.*(<div class="post".*?>.*?</div>.*?<div class="entry">.*?</div>).*You can start editing here.*</html>', lambda match : '<html><body>'+match.group(1)+'</body></html>'),
@ -101,6 +102,16 @@ class LeMonde(BasicNewsRecipe):
# Used to filter duplicated articles
articles_list = []
def get_cover_url(self):
cover_url = None
soup = self.index_to_soup('http://www.lemonde.fr/web/monde_pdf/0,33-0,1-0,0.html')
link_item = soup.find('div',attrs={'class':'pg-gch'})
if link_item and link_item.img:
cover_url = link_item.img['src']
return cover_url
def get_article_url(self, article):
url=article.get('link', None)
url=url[0:url.find("#")]
@ -109,8 +120,13 @@ class LeMonde(BasicNewsRecipe):
return False
if self.is_article_wanted(url):
self.articles_list.append(url)
if '/portfolio/' in url or '/video/' in url:
url = None
return url
self.log_debug(_('Skipping filtered article: %s')%url)
url = article.get('guid', None)
return False
@ -122,4 +138,15 @@ class LeMonde(BasicNewsRecipe):
return False
return False
def preprocess_html(self, soup):
for item in soup.findAll(style=True):
del item['style']
for item in soup.findAll(face=True):
del item['face']
for tag in soup.findAll(name=['ul','li']):
tag.name = 'div'
return soup

View File

@ -0,0 +1,41 @@
#!/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'
from calibre.web.feeds.news import BasicNewsRecipe
class Salon_com(BasicNewsRecipe):
title = 'Salon.com'
__author__ = 'cix3'
description = 'Salon.com - Breaking news, opinion, politics, entertainment, sports and culture.'
timefmt = ' [%b %d, %Y]'
language = 'en'
oldest_article = 7
max_articles_per_feed = 100
remove_tags = [dict(name='div', attrs={'class':['ad_content', 'clearfix']}), dict(name='hr'), dict(name='img')]
remove_tags_before = dict(name='h2')
feeds = [
('All News & Politics', 'http://feeds.salon.com/salon/news'),
('War Room', 'http://feeds.salon.com/salon/war_room'),
('All Arts & Entertainment', 'http://feeds.salon.com/salon/ent'),
('I Like to Watch', 'http://feeds.salon.com/salon/iltw'),
('Book Reviews', 'http://feeds.salon.com/salon/books'),
('All Life stories', 'http://feeds.salon.com/salon/mwt'),
('Broadsheet', 'http://feeds.salon.com/salon/broadsheet'),
('All Opinion', 'http://feeds.salon.com/salon/opinion'),
('All Sports', 'http://feeds.salon.com/salon/sports'),
('All Tech & Business', 'http://feeds.salon.com/salon/tech'),
('Ask the Pilot', 'http://feeds.salon.com/salon/ask_the_pilot'),
('How the World Works', 'http://feeds.salon.com/salon/htww')
]
def print_version(self, url):
return url.replace('/index.html', '/print.html')

View File

@ -7,7 +7,6 @@ spiegel.de
'''
from calibre.web.feeds.news import BasicNewsRecipe
from calibre.ebooks.BeautifulSoup import Tag
class Spiegel_ger(BasicNewsRecipe):
title = 'Spiegel Online - German'
@ -18,48 +17,30 @@ class Spiegel_ger(BasicNewsRecipe):
oldest_article = 7
max_articles_per_feed = 100
language = 'de'
lang = 'de-DE'
no_stylesheets = True
use_embedded_content = False
encoding = 'cp1252'
html2lrf_options = [
'--comment', description
, '--category', category
, '--publisher', publisher
]
conversion_options = {
'comment' : description
, 'tags' : category
, 'publisher' : publisher
, 'language' : lang
}
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
keep_only_tags = [dict(name='div', attrs={'id':'spMainContent'})]
keep_only_tags = [dict(name='div', attrs={'id':'spArticleContent'})]
remove_tags = [dict(name=['object','link','base'])]
remove_tags = [dict(name=['object','link','base','iframe'])]
remove_tags_after = dict(name='div', attrs={'id':'spArticleBody'})
feeds = [(u'Spiegel Online', u'http://www.spiegel.de/schlagzeilen/index.rss')]
def print_version(self, url):
main, sep, rest = url.rpartition(',')
rmt = url.rpartition('#')[0]
main, sep, rest = rmt.rpartition(',')
rmain, rsep, rrest = main.rpartition(',')
return rmain + ',druck-' + rrest + ',' + rest
def preprocess_html(self, soup):
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)
for item in soup.findAll(style=True):
del item['style']
htmltag = soup.find('html')
if not htmltag:
thtml = Tag(soup,'html',[("lang",self.lang),("xml:lang",self.lang),("dir","ltr")])
soup.insert(0,thtml)
thead = soup.head
tbody = soup.body
thead.extract()
tbody.extract()
soup.html.insert(0,tbody)
soup.html.insert(0,thead)
return soup
purl = rmain + ',druck-' + rrest + ',' + rest
return purl

View File

@ -0,0 +1,44 @@
#!/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 re
from calibre.web.feeds.news import BasicNewsRecipe
class Tweakers(BasicNewsRecipe):
title = u'Tweakers.net'
__author__ = 'Kovid Goyal'
language = 'nl'
oldest_article = 4
max_articles_per_feed = 40
keep_only_tags = [dict(name='div', attrs={'class':'columnwrapper news'})]
remove_tags = [dict(name='div', attrs={'class':'reacties'}),
{'id' : ['utracker']},
{'class' : ['sidebar']},
{'class' : re.compile('nextPrevious')},
]
no_stylesheets=True
feeds = [(u'Tweakers.net', u'http://tweakers.net/feeds/nieuws.xml')]
def preprocess_html(self, soup):
for a in soup.findAll('a', href=True, rel=True):
if a['rel'].startswith('imageview'):
a['src'] = a['href']
del a['href']
a.name = 'img'
for x in a.findAll(True):
x.extract()
return soup
def postprocess_html(self, soup, first):
for base in soup.findAll('base'):
base.extract()
return soup

View File

@ -19,10 +19,12 @@ class ZeitDe(BasicNewsRecipe):
timefmt = ' [%d %b %Y]'
max_articles_per_feed = 40
no_stylesheets = True
encoding = 'latin1'
encoding = 'utf8'
remove_tags = [{'class': 'adwrap'}]
keep_only_tags = [{'name': 'div', 'class': 'content'}]
feeds = [ ('Kurznachrichten', 'http://newsfeed.zeit.de/news/index'),
feeds = [ ('Kurznachrichten', 'http://newsfeed.zeit.de/index'),
('Politik', 'http://newsfeed.zeit.de/politik/index'),
('Wirtschaft', 'http://newsfeed.zeit.de/wirtschaft/index'),
('Meinung', 'http://newsfeed.zeit.de/meinung/index'),
@ -32,5 +34,5 @@ class ZeitDe(BasicNewsRecipe):
]
def print_version(self,url):
return url.replace('http://www.zeit.de/', 'http://images.zeit.de/text/').replace('?from=rss', '')
return url.replace('http://www.zeit.de/', 'http://mobil.zeit.de/')