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/images.qrc
resources/recipes.pickle resources/recipes.pickle
resources/scripts.pickle resources/scripts.pickle
resources/ebook-convert-complete.pickle
setup/installer/windows/calibre/build.log setup/installer/windows/calibre/build.log
src/calibre/translations/.errors src/calibre/translations/.errors
src/cssutils/.svn/ src/cssutils/.svn/

View File

@ -55,6 +55,7 @@ p, dl, multicol {
dd { dd {
display: block; display: block;
margin-left: 40px;
} }
blockquote { 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"?> <?xml version="1.0"?>
<xsl:stylesheet version="1.0" <xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:html="http://www.w3.org/1999/xhtml" xmlns:html="http://www.w3.org/1999/xhtml"
xmlns:rtf="http://rtf2xml.sourceforge.net/" xmlns:rtf="http://rtf2xml.sourceforge.net/"
xmlns:c="calibre"
extension-element-prefixes="c"
exclude-result-prefixes="rtf" exclude-result-prefixes="rtf"
> >
@ -147,7 +130,7 @@ xhtml = '''\
<xsl:text>generator</xsl:text> <xsl:text>generator</xsl:text>
</xsl:attribute> </xsl:attribute>
<xsl:attribute name="content"> <xsl:attribute name="content">
<xsl:text>http://rtf2xml.sourceforge.net/</xsl:text> <xsl:text>http://calibre-ebook.com</xsl:text>
</xsl:attribute> </xsl:attribute>
</xsl:element> </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-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.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:text>span.bold-underline{font-weight:bold;text-decoration:underline}&#xA;</xsl:text>
<xsl:for-each select="//rtf:inline"> <xsl:text>&#xA;</xsl:text>
<xsl:call-template name="parse-inline"/>
</xsl:for-each>
</xsl:document> </xsl:document>
</xsl:template> </xsl:template>
@ -287,52 +268,6 @@ xhtml = '''\
</xsl:if> </xsl:if>
</xsl:template> </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:template match="rtf:inline">
<xsl:variable name="num-attrs" select="count(@*)"/> <xsl:variable name="num-attrs" select="count(@*)"/>
@ -345,45 +280,7 @@ xhtml = '''\
<xsl:otherwise> <xsl:otherwise>
<xsl:element name="span"> <xsl:element name="span">
<xsl:attribute name="class"> <xsl:attribute name="class">
<xsl:choose> <c:inline-class/>
<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>
</xsl:attribute> </xsl:attribute>
<xsl:apply-templates/> <xsl:apply-templates/>
</xsl:element> </xsl:element>
@ -539,4 +436,3 @@ xhtml = '''\
</xsl:template> </xsl:template>
</xsl:stylesheet> </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, \ fc_error, poppler_libs, poppler_lib, poppler_inc, podofo_inc, \
podofo_lib, podofo_error, poppler_error, pyqt, OSX_SDK, NMAKE, \ podofo_lib, podofo_error, poppler_error, pyqt, OSX_SDK, NMAKE, \
leopard_build, QMAKE, msvc, MT, win_inc, win_lib leopard_build, QMAKE, msvc, MT, win_inc, win_lib
MT
isunix = islinux or isosx isunix = islinux or isosx
make = 'make' if isunix else NMAKE make = 'make' if isunix else NMAKE
@ -262,11 +262,11 @@ class Build(Command):
print ' '.join(cmd) print ' '.join(cmd)
subprocess.check_call(cmd) subprocess.check_call(cmd)
if iswindows: if iswindows:
manifest = dest+'.manifest' #manifest = dest+'.manifest'
cmd = [MT, '-manifest', manifest, '-outputresource:%s;2'%dest] #cmd = [MT, '-manifest', manifest, '-outputresource:%s;2'%dest]
self.info(*cmd) #self.info(*cmd)
subprocess.check_call(cmd) #subprocess.check_call(cmd)
os.remove(manifest) #os.remove(manifest)
for x in ('.exp', '.lib'): for x in ('.exp', '.lib'):
x = os.path.splitext(dest)[0]+x x = os.path.splitext(dest)[0]+x
if os.path.exists(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, \ from setup import Command, islinux, basenames, modules, functions, \
__appname__, __version__ __appname__, __version__
TEMPLATE = '''\ HEADER = '''\
#!/usr/bin/env python #!/usr/bin/env python
""" """
@ -20,6 +20,9 @@ Do not modify it unless you know what you are doing.
""" """
import sys import sys
'''
TEMPLATE = HEADER+'''
sys.path.insert(0, {path!r}) sys.path.insert(0, {path!r})
sys.resources_location = {resources!r} sys.resources_location = {resources!r}
@ -29,6 +32,18 @@ from {module} import {func!s}
sys.exit({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): class Develop(Command):
description = textwrap.dedent('''\ description = textwrap.dedent('''\
@ -43,8 +58,9 @@ class Develop(Command):
sub_commands = ['build', 'resources', 'gui'] sub_commands = ['build', 'resources', 'gui']
def add_options(self, parser): def add_options(self, parser):
parser.add_option('--prefix', '--root', parser.add_option('--prefix',
help='Binaries will be installed in <prefix>/bin') help='Binaries will be installed in <prefix>/bin')
self.root = ''
def pre_sub_commands(self, opts): def pre_sub_commands(self, opts):
if not islinux: if not islinux:
@ -76,7 +92,7 @@ class Develop(Command):
return warn() return warn()
import stat import stat
src = os.path.join(self.SRC, 'calibre', 'devices', 'linux_mount_helper.c') 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) self.info('Installing mount helper to '+ dest)
p = subprocess.Popen(['gcc', '-Wall', src, '-o', dest]) p = subprocess.Popen(['gcc', '-Wall', src, '-o', dest])
ret = p.wait() ret = p.wait()
@ -116,11 +132,12 @@ class Develop(Command):
self.write_template(opts, 'calibre_postinstall', 'calibre.linux', 'main') self.write_template(opts, 'calibre_postinstall', 'calibre.linux', 'main')
def write_template(self, opts, name, mod, func): 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, module=mod, func=func,
path=self.path, resources=self.resources, path=self.path, resources=self.resources,
extensions=self.extensions) extensions=self.extensions)
path = self.j(self.bindir, name) path = self.root + self.j(self.bindir, name)
if not os.path.exists(self.bindir): if not os.path.exists(self.bindir):
os.makedirs(self.bindir) os.makedirs(self.bindir)
self.info('Installing binary:', path) self.info('Installing binary:', path)
@ -141,10 +158,13 @@ class Install(Develop):
sub_commands = ['build', 'gui'] sub_commands = ['build', 'gui']
def add_options(self, parser): 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('--libdir', help='Where to put calibre library files')
parser.add_option('--bindir', help='Where to install calibre binaries') parser.add_option('--bindir', help='Where to install calibre binaries')
parser.add_option('--sharedir', help='Where to install calibre data files') 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): def find_locations(self, opts):
if opts.prefix is None: if opts.prefix is None:
@ -160,13 +180,19 @@ class Install(Develop):
self.path = opts.libdir self.path = opts.libdir
self.resources = opts.sharedir self.resources = opts.sharedir
self.extensions = self.j(self.path, 'calibre', 'plugins') self.extensions = self.j(self.path, 'calibre', 'plugins')
self.root = opts.root
def install_files(self, opts): def install_files(self, opts):
dest = self.path dest = self.root + self.path
if os.path.exists(dest): if os.path.exists(dest):
shutil.rmtree(dest) shutil.rmtree(dest)
shutil.copytree(self.SRC, 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): if os.path.exists(dest):
shutil.rmtree(dest) shutil.rmtree(dest)
shutil.copytree(self.RESOURCES, dest) shutil.copytree(self.RESOURCES, dest)

View File

@ -37,6 +37,7 @@ class LinuxFreeze(Command):
binary_includes = [ binary_includes = [
'/usr/bin/pdftohtml', '/usr/bin/pdftohtml',
'/usr/lib/libwmflite-0.2.so.7',
'/tmp/calibre-mount-helper', '/tmp/calibre-mount-helper',
'/usr/lib/libunrar.so', '/usr/lib/libunrar.so',
'/usr/lib/libsqlite3.so.0', '/usr/lib/libsqlite3.so.0',
@ -202,6 +203,15 @@ class LinuxFreeze(Command):
for f in matches: for f in matches:
os.remove(f) 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...') self.info('Adding calibre plugins...')
os.makedirs(os.path.join(FREEZE_DIR, 'plugins')) os.makedirs(os.path.join(FREEZE_DIR, 'plugins'))
for f in glob.glob(os.path.join(CALIBREPLUGINS, '*.so')): for f in glob.glob(os.path.join(CALIBREPLUGINS, '*.so')):
@ -230,6 +240,9 @@ class LinuxFreeze(Command):
base=`dirname $path` base=`dirname $path`
loader=$base/loader loader=$base/loader
export LD_LIBRARY_PATH=$base:$LD_LIBRARY_PATH 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 "$@" $loader "$@"
''')%exe) ''')%exe)
os.chmod(path, 0755) os.chmod(path, 0755)

View File

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

View File

@ -188,6 +188,31 @@ os.execv(python, args)
self.fix_python_dependencies(deps) self.fix_python_dependencies(deps)
self.fix_misc_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): def run(self):
py2app.run(self) py2app.run(self)
@ -252,11 +277,17 @@ os.execv(python, args)
info('Adding ImageMagick') 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') dest = os.path.join(frameworks_dir, 'ImageMagick')
if os.path.exists(dest): if os.path.exists(dest):
shutil.rmtree(dest) shutil.rmtree(dest)
shutil.copytree(os.path.expanduser('~/ImageMagick'), dest, True) shutil.copytree(os.path.expanduser('~/ImageMagick'), dest, True)
shutil.copyfile('/usr/local/lib/libpng12.0.dylib', os.path.join(dest, 'lib', 'libpng12.0.dylib')) 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') info('Installing prescipt')

View File

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

View File

@ -10,6 +10,18 @@ import os, cPickle
from setup import Command, basenames 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): class Resources(Command):
def get_recipes(self): def get_recipes(self):
@ -45,9 +57,42 @@ class Resources(Command):
f = open(dest, 'wb') f = open(dest, 'wb')
cPickle.dump(recipes, f, -1) 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): def clean(self):
for x in ('scripts', 'recipes'): for x in ('scripts', 'recipes', 'ebook-convert-complete'):
x = self.j(self.RESOURCES, x+'.pickle') x = self.j(self.RESOURCES, x+'.pickle')
if os.path.exists(x): if os.path.exists(x):
os.remove(x) os.remove(x)

View File

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

View File

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

View File

@ -173,7 +173,7 @@ class PageProcessor(list):
p.MagickDespeckleImage(wand) p.MagickDespeckleImage(wand)
p.MagickQuantizeImage(wand, self.opts.colors, p.RGBColorspace, 0, 1, 0) 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) dest = os.path.join(self.dest, dest)
p.MagickWriteImage(wand, dest+'8') p.MagickWriteImage(wand, dest+'8')
os.rename(dest+'8', dest) os.rename(dest+'8', dest)
@ -270,8 +270,10 @@ class ComicInput(InputFormatPlugin):
is_image_collection = True is_image_collection = True
options = set([ options = set([
OptionRecommendation(name='colors', recommended_value=64, OptionRecommendation(name='colors', recommended_value=256,
help=_('Number of colors for grayscale image conversion. Default: %default')), 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, OptionRecommendation(name='dont_normalize', recommended_value=False,
help=_('Disable normalize (improve contrast) color range ' help=_('Disable normalize (improve contrast) color range '
'for pictures. Default: False')), 'for pictures. Default: False')),
@ -298,6 +300,10 @@ class ComicInput(InputFormatPlugin):
help=_("Don't sort the files found in the comic " help=_("Don't sort the files found in the comic "
"alphabetically by name. Instead use the order they were " "alphabetically by name. Instead use the order they were "
"added to the comic.")), "added to the comic.")),
OptionRecommendation(name='output_format', choices=['png', 'jpg'],
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, OptionRecommendation(name='no_process', recommended_value=False,
help=_("Apply no processing to the image")), help=_("Apply no processing to the image")),
]) ])
@ -365,7 +371,8 @@ class ComicInput(InputFormatPlugin):
'(run with --verbose to see why):') '(run with --verbose to see why):')
for f in failures: for f in failures:
self.log.warning('\t', f) 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): if not os.access(thumbnail, os.R_OK):
thumbnail = None thumbnail = None
return new_pages return new_pages

View File

@ -51,6 +51,9 @@ def load_specifics(db, book_id):
r.from_string(raw) r.from_string(raw)
return r return r
def delete_specifics(db, book_id):
db.delete_conversion_options(book_id, 'PIPE')
class GuiRecommendations(dict): class GuiRecommendations(dict):
def __new__(cls, *args): 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' SVG_NS = 'http://www.w3.org/2000/svg'
XLINK_NS = 'http://www.w3.org/1999/xlink' 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) _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): if not isinstance(self.title, unicode):
self.title = self.title.decode(self.codec, 'replace') self.title = self.title.decode(self.codec, 'replace')
if self.exth_flag & 0x40: if self.exth_flag & 0x40:
try:
self.exth = EXTHHeader(raw[16 + self.length:], self.codec, self.title) self.exth = EXTHHeader(raw[16 + self.length:], self.codec, self.title)
self.exth.mi.uid = self.unique_id self.exth.mi.uid = self.unique_id
try: try:
self.exth.mi.language = mobi2iana(langid, sublangid) self.exth.mi.language = mobi2iana(langid, sublangid)
except: except:
import traceback self.log.exception('Unknown language code')
traceback.print_exc() except:
self.log.exception('Invalid EXTH header')
self.exth_flag = 0
class MetadataHeader(BookHeader): class MetadataHeader(BookHeader):
@ -501,10 +504,20 @@ class MobiReader(object):
height = attrib.pop('height').strip() height = attrib.pop('height').strip()
if height and '<' not in height and '>' not in height and \ if height and '<' not in height and '>' not in height and \
re.search(r'\d+', height): 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)) styles.append('margin-top: %s' % self.ensure_unit(height))
if attrib.has_key('width'): if attrib.has_key('width'):
width = attrib.pop('width').strip() width = attrib.pop('width').strip()
if width and re.search(r'\d+', width): 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)) styles.append('text-indent: %s' % self.ensure_unit(width))
if width.startswith('-'): if width.startswith('-'):
styles.append('margin-left: %s' % self.ensure_unit(width[1:])) styles.append('margin-left: %s' % self.ensure_unit(width[1:]))

View File

@ -34,6 +34,11 @@ class Extract(ODF2XHTML):
with CurrentDir(odir): with CurrentDir(odir):
print 'Extracting ODT file...' print 'Extracting ODT file...'
html = self.odf2xhtml(stream) 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: with open('index.xhtml', 'wb') as f:
f.write(html.encode('utf-8')) f.write(html.encode('utf-8'))
zf = ZipFile(stream, 'r') zf = ZipFile(stream, 'r')

View File

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

View File

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

View File

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

View File

@ -2,12 +2,41 @@ from __future__ import with_statement
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>' __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import os, glob, re import os, glob, re, textwrap
from lxml import etree from lxml import etree
from calibre.customize.conversion import InputFormatPlugin 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): class RTFInput(InputFormatPlugin):
name = 'RTF Input' name = 'RTF Input'
@ -21,15 +50,15 @@ class RTFInput(InputFormatPlugin):
parser = ParseRtf( parser = ParseRtf(
in_file = stream, in_file = stream,
out_file = ofile, out_file = ofile,
# Convert symbol fonts to unicode equivelents. Default # Convert symbol fonts to unicode equivalents. Default
# is 1 # is 1
convert_symbol = 1, convert_symbol = 1,
# Convert Zapf fonts to unicode equivelents. Default # Convert Zapf fonts to unicode equivalents. Default
# is 1. # is 1.
convert_zapf = 1, convert_zapf = 1,
# Convert Wingding fonts to unicode equivelents. # Convert Wingding fonts to unicode equivalents.
# Default is 1. # Default is 1.
convert_wingdings = 1, convert_wingdings = 1,
@ -63,6 +92,7 @@ class RTFInput(InputFormatPlugin):
def extract_images(self, picts): def extract_images(self, picts):
self.log('Extracting images...') self.log('Extracting images...')
count = 0 count = 0
raw = open(picts, 'rb').read() raw = open(picts, 'rb').read()
starts = [] starts = []
@ -82,21 +112,66 @@ class RTFInput(InputFormatPlugin):
if len(enc) % 2 == 1: if len(enc) % 2 == 1:
enc = enc[:-1] enc = enc[:-1]
data = enc.decode('hex') data = enc.decode('hex')
ext = '.jpg'
if 'EMF' in data[:200]:
ext = '.wmf'
elif 'PNG' in data[:200]:
ext = '.png'
count += 1 count += 1
name = (('%4d'%count).replace(' ', '0'))+ext name = (('%4d'%count).replace(' ', '0'))+'.wmf'
open(name, 'wb').write(data) open(name, 'wb').write(data)
imap[count] = name imap[count] = name
#open(name+'.hex', 'wb').write(enc) #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 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, def convert(self, stream, options, file_ext, log,
accelerators): accelerators):
from calibre.ebooks.rtf.xsl import xhtml
from calibre.ebooks.metadata.meta import get_metadata from calibre.ebooks.metadata.meta import get_metadata
from calibre.ebooks.metadata.opf2 import OPFCreator from calibre.ebooks.metadata.opf2 import OPFCreator
from calibre.ebooks.rtf2xml.ParseRtf import RtfInvalidCodeException from calibre.ebooks.rtf2xml.ParseRtf import RtfInvalidCodeException
@ -124,15 +199,18 @@ class RTFInput(InputFormatPlugin):
if name is not None: if name is not None:
pict.set('num', name) pict.set('num', name)
self.log('Converting XML to HTML...') 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) result = transform(doc)
html = 'index.xhtml' html = 'index.xhtml'
with open(html, 'wb') as f: with open(html, 'wb') as f:
res = transform.tostring(result) res = transform.tostring(result)
res = res[:100].replace('xmlns:html', 'xmlns') + res[100:] res = res[:100].replace('xmlns:html', 'xmlns') + res[100:]
f.write(res) f.write(res)
self.write_inline_css(inline_class)
stream.seek(0) stream.seek(0)
mi = get_metadata(stream, 'rtf') mi = get_metadata(stream, 'rtf')
if not mi.title: if not mi.title:

View File

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

View File

@ -19,9 +19,11 @@ class PluginWidget(Widget, Ui_Form):
Widget.__init__(self, parent, 'comic_input', Widget.__init__(self, parent, 'comic_input',
['colors', 'dont_normalize', 'keep_aspect_ratio', 'right2left', ['colors', 'dont_normalize', 'keep_aspect_ratio', 'right2left',
'despeckle', 'no_sort', 'no_process', 'landscape', '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 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.initialize_options(get_option, get_help, db, book_id)
self.opt_no_process.toggle() self.opt_no_process.toggle()
self.opt_no_process.toggle() self.opt_no_process.toggle()

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>599</width> <width>599</width>
<height>305</height> <height>343</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -100,7 +100,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="11" column="0"> <item row="12" column="0">
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
@ -120,6 +120,19 @@
</property> </property>
</widget> </widget>
</item> </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> </layout>
</widget> </widget>
<resources/> <resources/>
@ -263,8 +276,8 @@
<y>15</y> <y>15</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>52</x> <x>56</x>
<y>225</y> <y>248</y>
</hint> </hint>
</hints> </hints>
</connection> </connection>
@ -300,5 +313,21 @@
</hint> </hint>
</hints> </hints>
</connection> </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> </connections>
</ui> </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.gui2.convert.metadata_ui import Ui_Form
from calibre.ebooks.metadata import authors_to_string, string_to_authors, \ from calibre.ebooks.metadata import authors_to_string, string_to_authors, \
MetaInformation MetaInformation
from calibre.ebooks.metadata.opf2 import OPFCreator from calibre.ebooks.metadata.opf2 import metadata_to_opf
from calibre.ptempfile import PersistentTemporaryFile from calibre.ptempfile import PersistentTemporaryFile
from calibre.gui2.convert import Widget from calibre.gui2.convert import Widget
def create_opf_file(db, book_id): def create_opf_file(db, book_id):
mi = db.get_metadata(book_id, index_is_id=True) mi = db.get_metadata(book_id, index_is_id=True)
mi.application_id = uuid.uuid4() mi.application_id = uuid.uuid4()
opf = OPFCreator(os.getcwdu(), mi) raw = metadata_to_opf(mi)
opf_file = PersistentTemporaryFile('.opf') opf_file = PersistentTemporaryFile('.opf')
opf.render(opf_file) opf_file.write(raw)
opf_file.close() opf_file.close()
return mi, opf_file 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.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.ui import available_output_formats
from calibre.customize.conversion import OptionRecommendation from calibre.customize.conversion import OptionRecommendation
from calibre.utils.config import prefs from calibre.utils.config import prefs
@ -115,9 +116,10 @@ class Config(ResizableDialog, Ui_Dialog):
def __init__(self, parent, db, book_id, def __init__(self, parent, db, book_id,
preferred_input_format=None, preferred_output_format=None): preferred_input_format=None, preferred_output_format=None):
ResizableDialog.__init__(self, parent) 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.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.setup_pipeline()
self.connect(self.input_formats, SIGNAL('currentIndexChanged(QString)'), self.connect(self.input_formats, SIGNAL('currentIndexChanged(QString)'),
@ -130,8 +132,14 @@ class Config(ResizableDialog, Ui_Dialog):
self.show_pane) self.show_pane)
self.connect(self.groups, SIGNAL('entered(QModelIndex)'), self.connect(self.groups, SIGNAL('entered(QModelIndex)'),
self.show_group_help) self.show_group_help)
rb = self.buttonBox.button(self.buttonBox.RestoreDefaults)
self.connect(rb, SIGNAL('clicked()'), self.restore_defaults)
self.groups.setMouseTracking(True) self.groups.setMouseTracking(True)
def restore_defaults(self):
delete_specifics(self.db, self.book_id)
self.setup_pipeline()
@property @property
def input_format(self): def input_format(self):
return unicode(self.input_formats.currentText()).lower() return unicode(self.input_formats.currentText()).lower()

View File

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

View File

@ -207,6 +207,9 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
self.series.setEditText(mi.series) self.series.setEditText(mi.series)
if mi.series_index is not None: if mi.series_index is not None:
self.series_index.setValue(float(mi.series_index)) self.series_index.setValue(float(mi.series_index))
if mi.comments and mi.comments.strip():
self.comments.setText(mi.comments)
def set_cover(self): def set_cover(self):
mi, ext = self.get_selected_format_metadata() mi, ext = self.get_selected_format_metadata()
@ -330,6 +333,8 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
if not ext: if not ext:
ext = '' ext = ''
size = self.db.sizeof_format(row, ext) size = self.db.sizeof_format(row, ext)
if size is None:
continue
Format(self.formats, ext, size) Format(self.formats, ext, size)
@ -545,18 +550,11 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
if self.formats_changed: if self.formats_changed:
self.sync_formats() self.sync_formats()
title = unicode(self.title.text()) 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) self.db.set_title(self.id, title, notify=False)
au = unicode(self.authors.text()) au = unicode(self.authors.text())
if au: if au:
self.db.set_authors(self.id, string_to_authors(au), notify=False) 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: if aus:
self.db.set_author_sort(self.id, aus, notify=False) self.db.set_author_sort(self.id, aus, notify=False)
self.db.set_isbn(self.id, 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: if self.cover_changed and self.cover_data is not None:
self.db.set_cover(self.id, self.cover_data) 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) QDialog.accept(self)
if callable(self.accepted_callback): if callable(self.accepted_callback):
self.accepted_callback(self.id) 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 PyQt4.QtSvg import QSvgRenderer
from calibre import prints, patheq from calibre import prints, patheq
from calibre.constants import __version__, __appname__, \ from calibre.constants import __version__, __appname__, isfrozen, islinux, \
iswindows, isosx, filesystem_encoding iswindows, isosx, filesystem_encoding
from calibre.utils.filenames import ascii_filename from calibre.utils.filenames import ascii_filename
from calibre.ptempfile import PersistentTemporaryFile from calibre.ptempfile import PersistentTemporaryFile
@ -1290,7 +1290,15 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.job_manager.launch_gui_app(viewer, self.job_manager.launch_gui_app(viewer,
kwargs=dict(args=args)) kwargs=dict(args=args))
else: 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) 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 time.sleep(2) # User feedback
finally: finally:
self.unsetCursor() self.unsetCursor()

View File

@ -1081,6 +1081,10 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
return cPickle.loads(str(data)) return cPickle.loads(str(data))
return None 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): 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.library_path = os.path.abspath(library_path)
self.row_factory = row_factory self.row_factory = row_factory
self.dbpath = os.path.join(library_path, 'metadata.db') 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): if isinstance(self.dbpath, unicode):
self.dbpath = self.dbpath.encode(filesystem_encoding) self.dbpath = self.dbpath.encode(filesystem_encoding)
self.connect() self.connect()

View File

@ -366,6 +366,21 @@ class LibraryServer(object):
return base.intersection(epub.union(pdb)) return base.intersection(epub.union(pdb))
def stanza_sortby_subcategory(self, updated, sortby, offset): 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:], '' what, subtitle = sortby[2:], ''
if sortby == 'byseries': if sortby == 'byseries':
data = self.db.all_series() data = self.db.all_series()
@ -379,13 +394,15 @@ class LibraryServer(object):
data = self.db.all_tags2() data = self.db.all_tags2()
data = [(x[0], x[1], len(self.get_matches('tags', x[1]))) for x in data] data = [(x[0], x[1], len(self.get_matches('tags', x[1]))) for x in data]
subtitle = 'Books by tag' subtitle = 'Books by tag'
fcmp = author_cmp if sortby == 'byauthor' else cmp
data = [x for x in data if x[2] > 0] 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 next_offset = offset + self.max_stanza_items
rdata = data[offset:next_offset] rdata = data[offset:next_offset]
if next_offset >= len(data): if next_offset >= len(data):
next_offset = -1 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, what=what, updated=updated, count=c).render('xml').decode('utf-8') for id,
title, c in rdata] title, c in rdata]
next_link = '' next_link = ''

View File

@ -256,7 +256,13 @@ def setup_udev_rules(group_file, reload, fatal_errors):
sys.stdout.flush() sys.stdout.flush()
groups = open(group_file, 'rb').read() groups = open(group_file, 'rb').read()
group = 'plugdev' if 'plugdev' in groups else 'usb' 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) manifest.append(udev.name)
udev.write('''# Sony Reader PRS-500\n''' udev.write('''# Sony Reader PRS-500\n'''
'''BUS=="usb", SYSFS{idProduct}=="029b", SYSFS{idVendor}=="054c", MODE="660", GROUP="%s"\n'''%(group,) '''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. |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. 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 *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 ** 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'), ('dnspython', '1.6.0', 'dnspython', 'dnspython', 'dnspython', 'dnspython'),
('poppler-qt4', '0.10.6', 'poppler-qt4', 'poppler-qt4', 'poppler-qt4', 'poppler-qt4'), ('poppler-qt4', '0.10.6', 'poppler-qt4', 'poppler-qt4', 'poppler-qt4', 'poppler-qt4'),
('podofo', '0.7', 'podofo', 'podofo', 'podofo', 'podofo'), ('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 "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: calibre 0.6.12\n" "Project-Id-Version: calibre 0.6.13\n"
"POT-Creation-Date: 2009-09-13 10:26+MDT\n" "POT-Creation-Date: 2009-09-18 12:45+MDT\n"
"PO-Revision-Date: 2009-09-13 10:26+MDT\n" "PO-Revision-Date: 2009-09-18 12:45+MDT\n"
"Last-Translator: Automatically generated\n" "Last-Translator: Automatically generated\n"
"Language-Team: LANGUAGE\n" "Language-Team: LANGUAGE\n"
"MIME-Version: 1.0\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/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:703
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:706 #: /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:65
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:67 #: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:67
#: /home/kovid/work/calibre/src/calibre/ebooks/html/input.py:318 #: /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:79
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:121 #: /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:154
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:575 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:588
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:762 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:775
#: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:44 #: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:49
#: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:46 #: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:51
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:883 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:886
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:888 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:891
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:944 #: /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:135
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/reader.py:137 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/reader.py:137
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/transforms/jacket.py:105 #: /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/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:28
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/writer.py:29 #: /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:217
#: /home/kovid/work/calibre/src/calibre/ebooks/rtf/input.py:141 #: /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:261
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:268 #: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:268
#: /home/kovid/work/calibre/src/calibre/gui2/add.py:111 #: /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/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:106
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:139 #: /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:41
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:42 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:42
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:391 #: /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/gui2/viewer/main.py:211
#: /home/kovid/work/calibre/src/calibre/library/cli.py:277 #: /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/database.py:917
#: /home/kovid/work/calibre/src/calibre/library/database2.py:652 #: /home/kovid/work/calibre/src/calibre/library/database2.py:654
#: /home/kovid/work/calibre/src/calibre/library/database2.py:664 #: /home/kovid/work/calibre/src/calibre/library/database2.py:666
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1059 #: /home/kovid/work/calibre/src/calibre/library/database2.py:1061
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1096 #: /home/kovid/work/calibre/src/calibre/library/database2.py:1098
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1426
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1428 #: /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/database2.py:1430
#: /home/kovid/work/calibre/src/calibre/library/server.py:476 #: /home/kovid/work/calibre/src/calibre/library/database2.py:1539
#: /home/kovid/work/calibre/src/calibre/library/server.py:548 #: /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/localization.py:103
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:45 #: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:45
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:63 #: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:63
@ -525,9 +525,9 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:686 #: /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/dialogs/scheduler.py:434
#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:83 #: /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:1005
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1007 #: /home/kovid/work/calibre/src/calibre/library/database2.py:1009
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1330 #: /home/kovid/work/calibre/src/calibre/library/database2.py:1332
msgid "News" msgid "News"
msgstr "" msgstr ""
@ -586,51 +586,55 @@ msgid ""
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:274 #: /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 "" 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" msgid "Disable normalize (improve contrast) color range for pictures. Default: False"
msgstr "" 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." msgid "Maintain picture aspect ratio. Default is to fill the screen."
msgstr "" 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." msgid "Disable sharpening."
msgstr "" 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." msgid "Disable trimming of comic pages. For some comics, trimming might remove content as well as borders."
msgstr "" 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" msgid "Don't split landscape images into two portrait images"
msgstr "" 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." msgid "Keep aspect ratio and scale image using screen height as image width for viewing in landscape mode."
msgstr "" 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." msgid "Used for right-to-left publications like manga. Causes landscape pages to be split into portrait pages from right to left."
msgstr "" 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." msgid "Enable Despeckle. Reduces speckle noise. May greatly increase processing time."
msgstr "" 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." msgid "Don't sort the files found in the comic alphabetically by name. Instead use the order they were added to the comic."
msgstr "" 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" msgid "Apply no processing to the image"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:427 #: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:434
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:438 #: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:445
msgid "Page" msgid "Page"
msgstr "" msgstr ""
@ -1434,7 +1438,7 @@ msgid ""
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf2.py:1055 #: /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" msgid "Cover"
msgstr "" msgstr ""
@ -1463,70 +1467,70 @@ msgstr ""
msgid "All articles" msgid "All articles"
msgstr "" 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" msgid "Title Page"
msgstr "" 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/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.py:51
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:168 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:168
msgid "Table of Contents" msgid "Table of Contents"
msgstr "" 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" msgid "Index"
msgstr "" 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" msgid "Glossary"
msgstr "" 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" msgid "Acknowledgements"
msgstr "" 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" msgid "Bibliography"
msgstr "" 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" msgid "Colophon"
msgstr "" 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" msgid "Copyright"
msgstr "" 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" msgid "Dedication"
msgstr "" 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" msgid "Epigraph"
msgstr "" 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" msgid "Foreword"
msgstr "" 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" msgid "List of Illustrations"
msgstr "" 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" msgid "List of Tables"
msgstr "" 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" msgid "Notes"
msgstr "" 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" msgid "Preface"
msgstr "" 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" msgid "Main Text"
msgstr "" msgstr ""
@ -1814,7 +1818,7 @@ msgstr ""
msgid "Specify the character encoding of the output document. The default is cp1252." msgid "Specify the character encoding of the output document. The default is cp1252."
msgstr "" 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." msgid "This RTF file has a feature calibre does not support. Convert it to HTML first and then try it."
msgstr "" msgstr ""
@ -2001,7 +2005,7 @@ msgid "Bulk Convert"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/bulk.py:73 #: /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." msgid "Options specific to the output format."
msgstr "" msgstr ""
@ -2033,7 +2037,7 @@ msgstr ""
msgid "input" msgid "input"
msgstr "" 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/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/epub_output_ui.py:41
#: /home/kovid/work/calibre/src/calibre/gui2/convert/fb2_input_ui.py:28 #: /home/kovid/work/calibre/src/calibre/gui2/convert/fb2_input_ui.py:28
@ -2061,60 +2065,65 @@ msgstr ""
msgid "Form" msgid "Form"
msgstr "" 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 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:94
msgid "&Number of Colors:" msgid "&Number of Colors:"
msgstr "" 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 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:96
msgid "Disable &normalize" msgid "Disable &normalize"
msgstr "" 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 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:97
msgid "Keep &aspect ratio" msgid "Keep &aspect ratio"
msgstr "" 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 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:98
msgid "Disable &Sharpening" msgid "Disable &Sharpening"
msgstr "" 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 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:104
msgid "Disable &Trimming" msgid "Disable &Trimming"
msgstr "" 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 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:103
msgid "&Wide" msgid "&Wide"
msgstr "" 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 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:99
msgid "&Landscape" msgid "&Landscape"
msgstr "" 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 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:101
msgid "&Right to left" msgid "&Right to left"
msgstr "" 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 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:100
msgid "Don't so&rt" msgid "Don't so&rt"
msgstr "" 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 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:102
msgid "De&speckle" msgid "De&speckle"
msgstr "" 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" msgid "&Disable comic processing"
msgstr "" 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 #: /home/kovid/work/calibre/src/calibre/gui2/convert/debug.py:19
msgid "Debug" msgid "Debug"
msgstr "" msgstr ""
@ -2571,7 +2580,7 @@ msgid "RB Output"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/regex_builder.py:76 #: /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" msgid "Choose the format to view"
msgstr "" msgstr ""
@ -2603,11 +2612,11 @@ msgstr ""
msgid "Regex:" msgid "Regex:"
msgstr "" 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" msgid "Convert"
msgstr "" 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." msgid "Options specific to the input format."
msgstr "" msgstr ""
@ -2622,10 +2631,6 @@ msgstr ""
msgid "&Input format:" msgid "&Input format:"
msgstr "" 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 #: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection.py:17
msgid "" msgid ""
"Structure\n" "Structure\n"
@ -2652,22 +2657,22 @@ msgstr ""
msgid "Footer regular expression:" msgid "Footer regular expression:"
msgstr "" 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 #: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:72
msgid "Invalid regular expression" msgid "Invalid regular expression"
msgstr "" 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 #: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:73
msgid "Invalid regular expression: %s" msgid "Invalid regular expression: %s"
msgstr "" 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 #: /home/kovid/work/calibre/src/calibre/gui2/convert/toc.py:38
msgid "Invalid XPath" msgid "Invalid XPath"
msgstr "" 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 #: /home/kovid/work/calibre/src/calibre/gui2/convert/toc.py:39
msgid "The XPath expression %s is invalid." msgid "The XPath expression %s is invalid."
msgstr "" msgstr ""
@ -3807,67 +3812,67 @@ msgstr ""
msgid "Could not read metadata from %s format" msgid "Could not read metadata from %s format"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:221 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:224
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:227 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:230
msgid "Could not read cover" msgid "Could not read cover"
msgstr "" 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" msgid "Could not read cover from %s format"
msgstr "" 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" msgid "The cover in the %s format is invalid"
msgstr "" 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" msgid "Abort the editing of all remaining books"
msgstr "" 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..." msgid "Downloading cover..."
msgstr "" 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: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" msgid "Cannot fetch cover"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:467 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:472
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:478 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:483
msgid "<b>Could not fetch cover.</b><br/>" msgid "<b>Could not fetch cover.</b><br/>"
msgstr "" 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." msgid "The download timed out."
msgstr "" 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." msgid "Could not find cover for this book. Try specifying the ISBN first."
msgstr "" 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" msgid "Bad cover"
msgstr "" 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" msgid "The cover is not a valid picture"
msgstr "" 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" msgid "Cannot fetch metadata"
msgstr "" 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" msgid "You must specify at least one of ISBN, Title, Authors or Publisher"
msgstr "" 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" msgid "Permission denied"
msgstr "" 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?" msgid "Could not open %s. Is it being used by another program?"
msgstr "" msgstr ""
@ -4677,7 +4682,7 @@ msgid "Save to disk in a single directory"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:280 #: /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" msgid "Save only %s format to disk"
msgstr "" msgstr ""
@ -4717,7 +4722,7 @@ msgid "Calibre Library"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:437 #: /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." msgid "Choose a location for your ebook library."
msgstr "" msgstr ""
@ -4891,158 +4896,158 @@ msgstr ""
msgid "Cannot convert" msgid "Cannot convert"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1307 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1315
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1326 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1334
msgid "No book selected" msgid "No book selected"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1307 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1315
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1357 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1365
msgid "Cannot view" msgid "Cannot view"
msgstr "" 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" msgid "Cannot open folder"
msgstr "" 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" msgid "Multiple Books Selected"
msgstr "" 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?" 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 "" 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." msgid "%s has no available formats."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1399 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1407
msgid "Cannot configure" msgid "Cannot configure"
msgstr "" 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." msgid "Cannot configure while there are running jobs."
msgstr "" 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" msgid "No detailed info available"
msgstr "" 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." msgid "No detailed information is available for books on the device."
msgstr "" 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" msgid "Error talking to device"
msgstr "" 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." msgid "There was a temporary error talking to the device. Please unplug and reconnect the device and or reboot."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1520 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1528
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1538 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1546
msgid "Conversion Error" msgid "Conversion Error"
msgstr "" 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." 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 "" 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>" msgid "<b>Failed</b>"
msgstr "" 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" msgid "Invalid library location"
msgstr "" 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." msgid "Could not access %s. Using %s as the library."
msgstr "" 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." 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 "" 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?" msgid "There are active jobs. Are you sure you want to quit?"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1642 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1650
msgid "" msgid ""
" is communicating with the device!<br>\n" " is communicating with the device!<br>\n"
" Quitting may cause corruption on the device.<br>\n" " Quitting may cause corruption on the device.<br>\n"
" Are you sure you want to quit?" " Are you sure you want to quit?"
msgstr "" 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" msgid "WARNING: Active jobs"
msgstr "" 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." msgid "will keep running in the system tray. To close it, choose <b>Quit</b> in the context menu of the system tray."
msgstr "" 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>" msgid "<span style=\"color:red; font-weight:bold\">Latest version: <a href=\"%s\">%s</a></span>"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1724 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1732
msgid "Update available" msgid "Update available"
msgstr "" 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?" 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 "" 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." msgid "Use the library located at the specified path."
msgstr "" 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." msgid "Start minimized to system tray."
msgstr "" 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" msgid "Log debugging information to console"
msgstr "" 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" msgid "Do not check for updates"
msgstr "" 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" msgid "If you are sure it is not running"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1799 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1807
msgid "Cannot Start " msgid "Cannot Start "
msgstr "" 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." msgid "%s is already running."
msgstr "" 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" msgid "may be running in the system tray, in the"
msgstr "" 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." msgid "upper right region of the screen."
msgstr "" 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." msgid "lower right region of the screen."
msgstr "" 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." msgid "try rebooting your computer."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1812 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1820
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1824 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1832
msgid "try deleting the file" msgid "try deleting the file"
msgstr "" msgstr ""
@ -5229,46 +5234,46 @@ msgid "Publishers"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:34 #: /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" msgid "Starting conversion of %d books"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:64 #: /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)" msgid "Convert book %d of %d (%s)"
msgstr "" 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:91
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:204 #: /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." msgid "Could not convert %d of %d books, because no suitable source format was found."
msgstr "" 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" msgid "Queueing books for bulk conversion"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:182 #: /home/kovid/work/calibre/src/calibre/gui2/tools.py:183
msgid "Queueing " msgid "Queueing "
msgstr "" 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" msgid "You must set a username and password for %s"
msgstr "" 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 " msgid "Fetch news from "
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:252 #: /home/kovid/work/calibre/src/calibre/gui2/tools.py:253
msgid "Convert existing" msgid "Convert existing"
msgstr "" 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?" msgid "The following books have already been converted to %s format. Do you wish to reconvert them?"
msgstr "" msgstr ""
@ -6120,27 +6125,27 @@ msgid ""
"For help on an individual command: %%prog command --help\n" "For help on an individual command: %%prog command --help\n"
msgstr "" 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>" msgid "<p>Migrating old database to ebook library in %s<br><center>"
msgstr "" 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>" msgid "Copying <b>%s</b>"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1609 #: /home/kovid/work/calibre/src/calibre/library/database2.py:1611
msgid "Compacting database" msgid "Compacting database"
msgstr "" 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..." msgid "Checking SQL integrity..."
msgstr "" 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." msgid "Checking for missing files."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1758 #: /home/kovid/work/calibre/src/calibre/library/database2.py:1760
msgid "Checked id" msgid "Checked id"
msgstr "" msgstr ""
@ -6240,7 +6245,7 @@ msgstr ""
msgid "Password to access your calibre library. Username is " msgid "Password to access your calibre library. Username is "
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server.py:628 #: /home/kovid/work/calibre/src/calibre/library/server.py:645
msgid "" msgid ""
"[options]\n" "[options]\n"
"\n" "\n"
@ -6466,7 +6471,7 @@ msgstr ""
msgid "Article download failed: %s" msgid "Article download failed: %s"
msgstr "" 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_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_glas_srpske.py:77
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_instapaper.py:59 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_instapaper.py:59
@ -6483,12 +6488,12 @@ msgid "sr-Latn-RS"
msgstr "" 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" msgid "Skipping duplicated article: %s"
msgstr "" 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" msgid "Skipping filtered article: %s"
msgstr "" 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: elif iswindows:
_lib = os.path.join(os.path.dirname(sys.executable), 'CORE_RL_wand_.dll') \ _lib = os.path.join(os.path.dirname(sys.executable), 'CORE_RL_wand_.dll') \
if isfrozen else 'CORE_RL_wand_' if isfrozen else 'CORE_RL_wand_'
else:
if isfrozen:
_lib = os.path.join(sys.frozen_path, 'libMagickWand.so')
else: else:
_lib = util.find_library('MagickWand') _lib = util.find_library('MagickWand')
if _lib is None: if _lib is None:

View File

@ -12,12 +12,47 @@ BASH completion for calibre commands that are too complex for simple
completion. completion.
''' '''
import sys, os, shlex, glob, re import sys, os, shlex, glob, re, cPickle
from functools import partial
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): def split(src):
try: try:
@ -47,10 +82,10 @@ def get_opts_from_parser(parser, prefix):
if x.startswith(prefix): if x.startswith(prefix):
yield x yield x
for o in parser.option_list: 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 g in parser.option_groups:
for o in g.option_list: 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): def send(ans):
pat = re.compile('([^0-9a-zA-Z_./-])') pat = re.compile('([^0-9a-zA-Z_./-])')
@ -74,6 +109,8 @@ class EbookConvert(object):
self.words = words self.words = words
self.prefix = prefix self.prefix = prefix
self.previous = words[-2 if prefix else -1] 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) self.complete(wc)
def complete(self, wc): def complete(self, wc):
@ -81,6 +118,14 @@ class EbookConvert(object):
self.complete_input() self.complete_input()
elif wc == 3: elif wc == 3:
self.complete_output() 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: else:
from calibre.ebooks.conversion.cli import create_option_parser from calibre.ebooks.conversion.cli import create_option_parser
from calibre.utils.logging import Log from calibre.utils.logging import Log
@ -98,25 +143,18 @@ class EbookConvert(object):
send(ans) send(ans)
def complete_input(self): def complete_input(self):
from calibre.ebooks.conversion.plumber import supported_input_formats ans = list(files_and_dirs(self.prefix, self.cache['input_fmts']))
ans = list(files_and_dirs(self.prefix, supported_input_formats())) ans += [t for t in self.cache['input_recipes'] if
from calibre.web.feeds.recipes import recipes t.startswith(self.prefix)]
ans += [t.title+'.recipe ' for t in recipes if
(t.title+'.recipe').startswith(self.prefix)]
send(ans) send(ans)
def complete_output(self): def complete_output(self):
from calibre.customize.ui import available_output_formats fmts = self.cache['output']
fmts = available_output_formats()
ans = list(files_and_dirs(self.prefix, fmts)) ans = list(files_and_dirs(self.prefix, fmts))
ans += ['.'+x+' ' for x in fmts if ('.'+x).startswith(self.prefix)] ans += ['.'+x+' ' for x in fmts if ('.'+x).startswith(self.prefix)]
send(ans) send(ans)
def main(args=sys.argv): def main(args=sys.argv):
comp_line, pos = os.environ['COMP_LINE'], int(os.environ['COMP_POINT']) comp_line, pos = os.environ['COMP_LINE'], int(os.environ['COMP_POINT'])
module = split(comp_line)[0].split(os.sep)[-1] module = split(comp_line)[0].split(os.sep)[-1]

View File

@ -1019,6 +1019,8 @@ class BasicNewsRecipe(Recipe):
title, url = None, obj title, url = None, obj
else: else:
title, url = obj title, url = obj
if url.startswith('feed://'):
url = 'http'+url[4:]
self.report_progress(0, _('Fetching feed')+' %s...'%(title if title else url)) self.report_progress(0, _('Fetching feed')+' %s...'%(title if title else url))
try: try:
with closing(self.browser.open(url)) as f: 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', 'monitor', 'republika', 'beta', 'beta_en', 'glasjavnosti',
'esquire', 'livemint', 'thedgesingapore', 'darknet', 'rga', 'esquire', 'livemint', 'thedgesingapore', 'darknet', 'rga',
'intelligencer', 'theoldfoodie', 'hln_be', 'honvedelem', '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): class DerStandardRecipe(BasicNewsRecipe):
title = u'derStandard' title = u'derStandard'
__author__ = 'Gerhard Aigner' __author__ = 'Gerhard Aigner and Sujata Raman'
description = u'Nachrichten aus Österreich' description = u'Nachrichten aus ??sterreich'
publisher ='derStandard.at' publisher ='derStandard.at'
category = 'news, politics, nachrichten, Austria' category = 'news, politics, nachrichten, Austria'
use_embedded_content = False use_embedded_content = False
@ -21,18 +21,15 @@ class DerStandardRecipe(BasicNewsRecipe):
encoding = 'utf-8' encoding = 'utf-8'
language = 'de' language = 'de'
recursions = 0
oldest_article = 1 oldest_article = 1
max_articles_per_feed = 100 max_articles_per_feed = 100
html2lrf_options = [ extra_css = '''
'--comment' , description .artikelBody{font-family:Arial,Helvetica,sans-serif;}
, '--category' , category .artikelLeft{font-family:Arial,Helvetica,sans-serif;font-size:x-small;}
, '--publisher', publisher h4{color:#404450;font-size:x-small;}
] h6{color:#404450; font-size:x-small;}
'''
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
feeds = [(u'International', u'http://derstandard.at/?page=rss&ressort=internationalpolitik'), feeds = [(u'International', u'http://derstandard.at/?page=rss&ressort=internationalpolitik'),
(u'Inland', u'http://derstandard.at/?page=rss&ressort=innenpolitik'), (u'Inland', u'http://derstandard.at/?page=rss&ressort=innenpolitik'),
(u'Wirtschaft', u'http://derstandard.at/?page=rss&ressort=investor'), (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'Kultur', u'http://derstandard.at/?page=rss&ressort=kultur'),
(u'Wissenschaft', u'http://derstandard.at/?page=rss&ressort=wissenschaft'), (u'Wissenschaft', u'http://derstandard.at/?page=rss&ressort=wissenschaft'),
(u'Gesundheit', u'http://derstandard.at/?page=rss&ressort=gesundheit'), (u'Gesundheit', u'http://derstandard.at/?page=rss&ressort=gesundheit'),
(u'Bildung', u'http://derstandard.at/?page=rss&ressort=subildung')] (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')]
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 = [ preprocess_regexps = [
(re.compile(r'\[[\d]*\]', re.DOTALL|re.IGNORECASE), lambda match: ''), (re.compile(r'\[[\d]*\]', re.DOTALL|re.IGNORECASE), lambda match: ''),
(re.compile(r'bgcolor="#\w{3,6}"', re.DOTALL|re.IGNORECASE), lambda match: '') (re.compile(r'bgcolor="#\w{3,6}"', re.DOTALL|re.IGNORECASE), lambda match: '')
] ]
def print_version(self, url): filter_regexps = [r'/r[1-9]*']
return url.replace('?id=', 'txt/?id=')
def get_article_url(self, article): def get_article_url(self, article):
'''if the article links to a index page (ressort) or a picture gallery '''if the article links to a index page (ressort) or a picture gallery
(ansichtssache), don't add it''' (ansichtssache), don't add it'''
if ( article.link.count('ressort') > 0 or article.title.lower().count('ansichtssache') > 0 ): if ( article.link.count('ressort') > 0 or article.title.lower().count('ansichtssache') > 0 ):
return None return None
matchObj = re.search( re.compile(r'/r'+'[1-9]*',flags=0), article.link,flags=0)
if matchObj:
return None
return article.link return article.link
def preprocess_html(self, soup): def preprocess_html(self, soup):
@ -66,4 +75,7 @@ class DerStandardRecipe(BasicNewsRecipe):
soup.html['lang'] = self.lang soup.html['lang'] = self.lang
mtag = '<meta http-equiv="Content-Type" content="text/html; charset=' + self.encoding + '">' mtag = '<meta http-equiv="Content-Type" content="text/html; charset=' + self.encoding + '">'
soup.head.insert(0,mtag) soup.head.insert(0,mtag)
for t in soup.findAll(['ul', 'li']):
t.name = 'div'
return soup return soup

View File

@ -8,7 +8,7 @@ Fetch FTD.
from calibre.web.feeds.news import BasicNewsRecipe from calibre.web.feeds.news import BasicNewsRecipe
class FTheiseDe(BasicNewsRecipe): class FTDe(BasicNewsRecipe):
title = 'FTD' title = 'FTD'
description = 'Financial Times Deutschland' description = 'Financial Times Deutschland'
@ -16,24 +16,47 @@ class FTheiseDe(BasicNewsRecipe):
use_embedded_content = False use_embedded_content = False
timefmt = ' [%d %b %Y]' timefmt = ' [%d %b %Y]'
language = 'de' language = 'de'
max_articles_per_feed = 40 max_articles_per_feed = 40
no_stylesheets = True no_stylesheets = True
remove_tags = [dict(id='navi_top'), remove_tags = [dict(id='navi_top'),
dict(id='topbanner'), dict(id='topbanner'),
dict(id='seitenkopf'), dict(id='seitenkopf'),
dict(id='BoxA-0-0-0'),
dict(id='footer'), dict(id='footer'),
dict(id='rating_open'), dict(id='rating_open'),
dict(id='ADS_Top'), 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='ADS_Middle1'),
#dict(id='IDMS_ajax_chart_price_information_table'), #dict(id='IDMS_ajax_chart_price_information_table'),
dict(id='ivwimg'), dict(id='ivwimg'),
dict(name='span', attrs={'class':'rsaquo'}), dict(name='span', attrs={'class':'rsaquo'}),
dict(name='p', attrs={'class':'zwischenhead'}), 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':'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='span', attrs={'class':'vote_455857'}),
dict(name='div', attrs={'class':'relatedhalb'}), 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':'bpoll'}),
dict(name='div', attrs={'class':'pollokknopf'}), dict(name='div', attrs={'class':'pollokknopf'}),
dict(name='div', attrs={'class':'videohint'}), dict(name='div', attrs={'class':'videohint'}),
@ -43,9 +66,24 @@ class FTheiseDe(BasicNewsRecipe):
dict(name='div', attrs={'class':'abspielen'}), dict(name='div', attrs={'class':'abspielen'}),
dict(name='div', attrs={'class':'wertungoben'}), dict(name='div', attrs={'class':'wertungoben'}),
dict(name='div', attrs={'class':'artikelfuss'}), dict(name='div', attrs={'class':'artikelfuss'}),
dict(name='a', attrs={'class':'rating'}),
dict(name='div', attrs={'class':'articleOptionFootFrame'}),
dict(name='div', attrs={'class':'artikelsplitfaq'})] 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 import re
#from datetime import date
from calibre.web.feeds.news import BasicNewsRecipe from calibre.web.feeds.news import BasicNewsRecipe
class LeMonde(BasicNewsRecipe): class LeMonde(BasicNewsRecipe):
title = 'LeMonde.fr' title = 'LeMonde.fr'
__author__ = 'Mathieu Godlewski <mathieu at godlewski.fr>' __author__ = 'Mathieu Godlewski and Sujata Raman'
description = 'Global news in french' description = 'Global news in french'
oldest_article = 3 oldest_article = 3
language = 'fr' language = 'fr'
@ -27,20 +26,23 @@ class LeMonde(BasicNewsRecipe):
extra_css = ''' 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;} .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;} .mainContent{font-family:Georgia,serif;}
.mainTitle{font-family:Georgia,serif;}
.LM_content{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;} .content{font-family:Georgia,serif;}
.LM_caption{font-family:Georgia,serif;font-size:xx-small;} .entry{font-family:Georgia,serif;}
.LM_imageSource{font-family:Arial,Helvetica,sans-serif;font-size:xx-small;color:#666666;} h2{font-family:Arial,Helvetica,sans-serif;font-size:large;}
h1{font-family:Georgia,serif;font-size:medium;color:#000000;} small{font-family:Arial,Helvetica,sans-serif; color:#ED1B23;}
.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;}
''' '''
feeds = [ feeds = [
@ -71,11 +73,10 @@ class LeMonde(BasicNewsRecipe):
dict(name='iframe', attrs={}), dict(name='iframe', attrs={}),
dict(name='table', attrs={'id':["toolBox"]}), dict(name='table', attrs={'id':["toolBox"]}),
dict(name='table', attrs={'class':["bottomToolBox"]}), 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"]}), dict(name='div', attrs={'id':["miniUne","LM_sideBar"]}),
] ]
preprocess_regexps = [ (re.compile(i[0], re.IGNORECASE|re.DOTALL), i[1]) for i in 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>'), (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 # Used to filter duplicated articles
articles_list = [] 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): def get_article_url(self, article):
url=article.get('link', None) url=article.get('link', None)
url=url[0:url.find("#")] url=url[0:url.find("#")]
@ -109,8 +120,13 @@ class LeMonde(BasicNewsRecipe):
return False return False
if self.is_article_wanted(url): if self.is_article_wanted(url):
self.articles_list.append(url) self.articles_list.append(url)
if '/portfolio/' in url or '/video/' in url:
url = None
return url return url
self.log_debug(_('Skipping filtered article: %s')%url) self.log_debug(_('Skipping filtered article: %s')%url)
url = article.get('guid', None)
return False return False
@ -122,4 +138,15 @@ class LeMonde(BasicNewsRecipe):
return False return False
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.web.feeds.news import BasicNewsRecipe
from calibre.ebooks.BeautifulSoup import Tag
class Spiegel_ger(BasicNewsRecipe): class Spiegel_ger(BasicNewsRecipe):
title = 'Spiegel Online - German' title = 'Spiegel Online - German'
@ -18,48 +17,30 @@ class Spiegel_ger(BasicNewsRecipe):
oldest_article = 7 oldest_article = 7
max_articles_per_feed = 100 max_articles_per_feed = 100
language = 'de' language = 'de'
lang = 'de-DE' lang = 'de-DE'
no_stylesheets = True no_stylesheets = True
use_embedded_content = False use_embedded_content = False
encoding = 'cp1252' encoding = 'cp1252'
html2lrf_options = [ conversion_options = {
'--comment', description 'comment' : description
, '--category', category , 'tags' : category
, '--publisher', publisher , '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'}) remove_tags_after = dict(name='div', attrs={'id':'spArticleBody'})
feeds = [(u'Spiegel Online', u'http://www.spiegel.de/schlagzeilen/index.rss')] feeds = [(u'Spiegel Online', u'http://www.spiegel.de/schlagzeilen/index.rss')]
def print_version(self, url): def print_version(self, url):
main, sep, rest = url.rpartition(',') rmt = url.rpartition('#')[0]
main, sep, rest = rmt.rpartition(',')
rmain, rsep, rrest = main.rpartition(',') rmain, rsep, rrest = main.rpartition(',')
return rmain + ',druck-' + rrest + ',' + rest purl = rmain + ',druck-' + rrest + ',' + rest
return purl
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

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]' timefmt = ' [%d %b %Y]'
max_articles_per_feed = 40 max_articles_per_feed = 40
no_stylesheets = True 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'), ('Politik', 'http://newsfeed.zeit.de/politik/index'),
('Wirtschaft', 'http://newsfeed.zeit.de/wirtschaft/index'), ('Wirtschaft', 'http://newsfeed.zeit.de/wirtschaft/index'),
('Meinung', 'http://newsfeed.zeit.de/meinung/index'), ('Meinung', 'http://newsfeed.zeit.de/meinung/index'),
@ -32,5 +34,5 @@ class ZeitDe(BasicNewsRecipe):
] ]
def print_version(self,url): 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/')