Pulled upstream changes

This commit is contained in:
Marshall T. Vandegrift 2008-07-17 23:42:01 -04:00
commit 7060bcc681
12 changed files with 135 additions and 56 deletions

View File

@ -30,7 +30,12 @@ islinux = not(iswindows or isosx)
try:
locale.setlocale(locale.LC_ALL, '')
except:
pass
dl = locale.getdefaultlocale()
try:
if dl:
locale.setlocale(dl[0])
except:
pass
try:
preferred_encoding = locale.getpreferredencoding()

View File

@ -3,15 +3,13 @@ __copyright__ = '2008, Anatoly Shipitsin <norguhtar at gmail.com>'
"""
Convert .fb2 files to .lrf
"""
import os, sys, tempfile, subprocess, shutil, logging, glob
import os, sys, tempfile, shutil, logging
from base64 import b64decode
from calibre.ptempfile import PersistentTemporaryFile
from calibre.ebooks.lrf import option_parser as lrf_option_parser
from calibre.ebooks.metadata.meta import get_metadata
from calibre.ebooks import ConversionError
from calibre.ebooks.lrf.html.convert_from import process_file as html_process_file
from calibre import setup_cli_handlers, __appname__
from calibre.ebooks.BeautifulSoup import BeautifulStoneSoup
from calibre.resources import fb2_xsl
def option_parser():
@ -22,25 +20,27 @@ _('''%prog [options] mybook.fb2
%prog converts mybook.fb2 to mybook.lrf'''))
parser.add_option('--debug-html-generation', action='store_true', default=False,
dest='debug_html_generation', help=_('Print generated HTML to stdout and quit.'))
parser.add_option('--keep-intermediate-files', action='store_true', default=False,
help=_('Keep generated HTML files after completing conversion to LRF.'))
return parser
def extract_embedded_content(doc):
for elem in doc.xpath('./*'):
if 'binary' in elem.tag and elem.attrib.has_key('id'):
fname = elem.attrib['id']
data = b64decode(elem.text.strip())
open(fname, 'wb').write(data)
def generate_html(fb2file, encoding, logger):
from lxml import etree
tdir = tempfile.mkdtemp(prefix=__appname__+'_')
ofile = os.path.join(tdir, 'index.xml')
tdir = tempfile.mkdtemp(prefix=__appname__+'_fb2_')
cwd = os.getcwdu()
os.chdir(tdir)
try:
logger.info('Parsing XML...')
parser = etree.XMLParser(recover=True, no_network=True)
try:
doc = etree.parse(fb2file, parser)
except:
raise
logger.info('Parsing failed. Trying to clean up XML...')
soup = BeautifulStoneSoup(open(fb2file, 'rb').read())
doc = etree.fromstring(str(soup))
doc = etree.parse(fb2file, parser)
extract_embedded_content(doc)
logger.info('Converting XML to HTML...')
styledoc = etree.fromstring(fb2_xsl)
@ -72,7 +72,7 @@ def process_file(path, options, logger=None):
options.output = os.path.abspath(os.path.basename(os.path.splitext(path)[0]) + ext)
options.output = os.path.abspath(os.path.expanduser(options.output))
if not mi.title:
mi.title = os.path.splitext(os.path.basename(rtf))[0]
mi.title = os.path.splitext(os.path.basename(fb2))[0]
if (not options.title or options.title == 'Unknown'):
options.title = mi.title
if (not options.author or options.author == 'Unknown') and mi.authors:
@ -85,7 +85,7 @@ def process_file(path, options, logger=None):
html_process_file(htmlfile, options, logger)
finally:
os.chdir(cwd)
if hasattr(options, 'keep_intermediate_files') and options.keep_intermediate_files:
if getattr(options, 'keep_intermediate_files', False):
logger.debug('Intermediate files in '+ tdir)
else:
shutil.rmtree(tdir)

View File

@ -128,21 +128,40 @@
</xsl:template>
<xsl:template match="fb:section">
<a name="TOC_{generate-id()}"></a>
<xsl:if test="@id">
<xsl:element name="a">
<xsl:attribute name="name"><xsl:value-of select="@id"/></xsl:attribute>
</xsl:element>
</xsl:if>
<xsl:apply-templates/>
<xsl:variable name="section_has_title">
<xsl:choose>
<xsl:when test="./fb:title"><xsl:value-of select="generate-id()" /></xsl:when>
<xsl:otherwise>None</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:if test="$section_has_title = 'None'">
<a name="TOC_{generate-id()}" />
<xsl:if test="@id">
<xsl:element name="a">
<xsl:attribute name="name"><xsl:value-of select="@id"/></xsl:attribute>
</xsl:element>
</xsl:if>
</xsl:if>
<xsl:apply-templates>
<xsl:with-param name="section_toc_id" select="$section_has_title" />
</xsl:apply-templates>
</xsl:template>
<!-- section/title -->
<xsl:template match="fb:section/fb:title|fb:poem/fb:title">
<xsl:param name="section_toc_id" />
<xsl:choose>
<xsl:when test="count(ancestor::node()) &lt; 9">
<xsl:element name="{concat('h',count(ancestor::node())-3)}">
<xsl:if test="../@id">
<xsl:attribute name="id"><xsl:value-of select="../@id" /></xsl:attribute>
</xsl:if>
<xsl:if test="$section_toc_id != 'None'">
<xsl:element name="a">
<xsl:attribute name="name">TOC_<xsl:value-of select="$section_toc_id"/></xsl:attribute>
</xsl:element>
</xsl:if>
<a name="TOC_{generate-id()}"></a>
<xsl:if test="@id">
<xsl:element name="a">
@ -166,7 +185,9 @@
</xsl:template>
<!-- section/title -->
<xsl:template match="fb:body/fb:title">
<h1><xsl:apply-templates mode="title"/></h1>
<xsl:element name="h1">
<xsl:apply-templates mode="title"/>
</xsl:element>
</xsl:template>
<xsl:template match="fb:title/fb:p">

View File

@ -5,7 +5,8 @@ __copyright__ = '2008, Anatoly Shipitsin <norguhtar at gmail.com>'
'''Read meta information from fb2 files'''
import sys, os
import sys, os, mimetypes
from base64 import b64decode
from calibre.ebooks.BeautifulSoup import BeautifulStoneSoup
from calibre.ebooks.metadata import MetaInformation
@ -18,15 +19,30 @@ def get_metadata(stream):
author= [firstname+" "+lastname]
title = soup.find("book-title").string
comments = soup.find("annotation")
cp = soup.find('coverpage')
cdata = None
if cp:
cimage = cp.find('image', attrs={'l:href':True})
if cimage:
id = cimage['l:href'].replace('#', '')
binary = soup.find('binary', id=id, attrs={'content-type':True})
if binary:
mt = binary['content-type']
exts = mimetypes.guess_all_extensions(mt)
if not exts:
exts = ['.jpg']
cdata = (exts[0][1:], b64decode(binary.string.strip()))
if comments and len(comments) > 1:
comments = comments.p.contents[0]
comments = comments.p.contents[0]
series = soup.find("sequence")
# series_index = series.index
mi = MetaInformation(title, author)
mi.comments = comments
mi.author_sort = lastname+'; '+firstname
if series:
mi.series = series.get('name', None)
# mi.series_index = series_index
if cdata:
mi.cover_data = cdata
return mi
def main(args=sys.argv):

View File

@ -70,7 +70,7 @@ class TOC(list):
break
if toc is not None:
if toc.lower() != 'ncx':
if toc.lower() not in ('ncx', 'ncxtoc'):
toc = urlparse(unquote(toc))[2]
toc = toc.replace('/', os.sep)
if not os.path.isabs(toc):
@ -88,6 +88,10 @@ class TOC(list):
traceback.print_exc(file=sys.stdout)
print 'Continuing anyway'
else:
path = opfreader.manifest.item(toc.lower())
if path and os.access(path, os.R_OK):
self.read_ncx_toc(path)
return
cwd = os.path.abspath(self.base_path)
m = glob.glob(os.path.join(cwd, '*.ncx'))
if m:

View File

@ -574,7 +574,7 @@ class DeviceBooksModel(BooksModel):
for row in rows:
if not succeeded:
indices = self.row_indices(self.index(row, 0))
self.emit(SIGNAL('dataChanged(QModelIndex, QModelIndex)'), indices[0], indices[-1])
self.emit(SIGNAL('dataChanged(QModelIndex, QModelIndex)'), indices[0], indices[-1])
def paths_deleted(self, paths):
self.map = list(range(0, len(self.db)))
@ -691,7 +691,7 @@ class DeviceBooksModel(BooksModel):
dt = item.datetime
dt = datetime(*dt[0:6])
dt = dt - timedelta(seconds=time.timezone) + timedelta(hours=time.daylight)
data[_('Timestamp')] = dt.ctime()
data[_('Timestamp')] = dt.strftime('%a %b %d %H:%M:%S %Y')
data[_('Tags')] = ', '.join(item.tags)
self.emit(SIGNAL('new_bookdisplay_data(PyQt_PyObject)'), data)

View File

@ -59,6 +59,9 @@ class Main(MainWindow, Ui_MainWindow):
def __init__(self, single_instance, opts, parent=None):
MainWindow.__init__(self, opts, parent)
# Initialize fontconfig in a separate thread as this can be a lengthy
# process if run for the first time on this machine
self.fc = __import__('calibre.utils.fontconfig', fromlist=1)
self.single_instance = single_instance
if self.single_instance is not None:
self.connect(self.single_instance, SIGNAL('message_received(PyQt_PyObject)'),

View File

@ -170,7 +170,8 @@ def do_add(db, paths, one_book_per_directory, recurse, add_duplicates):
for mi, formats in dir_dups:
db.import_book(mi, formats)
else:
print >>sys.stderr, _('The following books were not added as they already exist in the database (see --duplicates option):')
if dir_dups or file_duplicates:
print >>sys.stderr, _('The following books were not added as they already exist in the database (see --duplicates option):')
for mi, formats in dir_dups:
title = mi.title
if isinstance(title, unicode):

View File

@ -240,13 +240,21 @@ def do_postinstall(destdir):
os.chdir(cwd)
def download_tarball():
pb = ProgressBar(TerminalController(sys.stdout), 'Downloading calibre...')
try:
pb = ProgressBar(TerminalController(sys.stdout), 'Downloading calibre...')
except ValueError:
print 'Downloading calibre...'
pb = None
src = urllib2.urlopen(MOBILEREAD+'calibre-%version-i686.tar.bz2')
size = int(src.info()['content-length'])
f = tempfile.NamedTemporaryFile()
while f.tell() < size:
f.write(src.read(4*1024))
pb.update(f.tell()/float(size))
percent = f.tell()/float(size)
if pb is not None:
pb.update(percent)
else:
print '%d%%, '%int(percent*100),
f.seek(0)
return f
@ -269,4 +277,4 @@ def main(args=sys.argv):
return 0
if __name__ == '__main__':
sys.exit(main())
sys.exit(main())

View File

@ -69,4 +69,4 @@ class ChangeLogMacro(WikiMacroBase):
if __name__ == '__main__':
print bzr_log_to_txt()

View File

@ -44,4 +44,4 @@
</div>
</div>
</body>
</html>
</html>

View File

@ -33,7 +33,7 @@ except:
preferred_encoding = 'utf-8'
iswindows = 'win32' in sys.platform or 'win64' in sys.platform
isosx = 'darwin' in sys.platform
isosx = 'darwin' in sys.platform
def load_library():
if isosx:
@ -94,7 +94,7 @@ class FcValue(Structure):
_fields_ = [
('type', c_int),
('u', _FcValue)
]
]
lib = load_library()
lib.FcPatternCreate.restype = c_void_p
@ -128,30 +128,49 @@ lib.FcConfigParseAndLoad.restype = c_int
lib.FcConfigBuildFonts.argtypes = [c_void_p]
lib.FcConfigBuildFonts.restype = c_int
_init_error = None
_initialized = False
from threading import Timer
def _do_init():
# Initialize the fontconfig library. This has to be done manually
# for the OS X bundle as it may have its own private fontconfig.
if hasattr(sys, 'frameworks_dir'):
config_dir = os.path.join(os.path.dirname(getattr(sys, 'frameworks_dir')), 'Resources', 'fonts')
if isinstance(config_dir, unicode):
config_dir = config_dir.encode(sys.getfilesystemencoding())
config = lib.FcConfigCreate()
if not lib.FcConfigParseAndLoad(config, os.path.join(config_dir, 'fonts.conf'), 1):
_init_error = 'Could not parse the fontconfig configuration'
return
if not lib.FcConfigBuildFonts(config):
_init_error = 'Could not build fonts'
return
if not lib.FcConfigSetCurrent(config):
_init_error = 'Could not set font config'
return
elif not lib.FcInit():
_init_error = _('Could not initialize the fontconfig library')
return
global _initialized
_initialized = True
_init_timer = Timer(0.1, _do_init)
_init_timer.start()
# Initialize the fontconfig library. This has to be done manually
# for the OS X bundle as it may have its own private fontconfig.
if hasattr(sys, 'frameworks_dir'):
config_dir = os.path.join(os.path.dirname(getattr(sys, 'frameworks_dir')), 'Resources', 'fonts')
if isinstance(config_dir, unicode):
config_dir = config_dir.encode(sys.getfilesystemencoding())
config = lib.FcConfigCreate()
if not lib.FcConfigParseAndLoad(config, os.path.join(config_dir, 'fonts.conf'), 1):
raise RuntimeError('Could not parse the fontconfig configuration')
if not lib.FcConfigBuildFonts(config):
raise RuntimeError('Could not build fonts')
if not lib.FcConfigSetCurrent(config):
raise RuntimeError('Could not set font config')
elif not lib.FcInit():
raise RuntimeError(_('Could not initialize the fontconfig library'))
def join():
_init_timer.join()
if _init_error is not None:
raise RuntimeError(_init_error)
def find_font_families(allowed_extensions=['ttf']):
def find_font_families(allowed_extensions=['ttf', 'otf']):
'''
Return an alphabetically sorted list of font families available on the system.
`allowed_extensions`: A list of allowed extensions for font file types. Defaults to
`['ttf']`. If it is empty, it is ignored.
`['ttf', 'otf']`. If it is empty, it is ignored.
'''
join()
allowed_extensions = [i.lower() for i in allowed_extensions]
empty_pattern = lib.FcPatternCreate()
@ -193,6 +212,7 @@ def files_for_family(family, normalize=True):
they are a tuple (slant, weight) otherwise they are strings from the set
`('normal', 'bold', 'italic', 'bi', 'light', 'li')`
'''
join()
if isinstance(family, unicode):
family = family.encode(preferred_encoding)
family_pattern = lib.FcPatternBuild(0, 'family', FcTypeString, family, 0)
@ -268,6 +288,7 @@ def match(name, sort=False, verbose=False):
decreasing closeness of matching.
`verbose`: If `True` print debugging information to stdout
'''
join()
if isinstance(name, unicode):
name = name.encode(preferred_encoding)
pat = lib.FcNameParse(name)