mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Sync to trunk.
This commit is contained in:
commit
6ba9ee4bd1
@ -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.1'
|
__version__ = '0.6.2'
|
||||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
@ -199,6 +199,17 @@ class SonyReaderOutput(OutputProfile):
|
|||||||
fbase = 12
|
fbase = 12
|
||||||
fsizes = [7.5, 9, 10, 12, 15.5, 20, 22, 24]
|
fsizes = [7.5, 9, 10, 12, 15.5, 20, 22, 24]
|
||||||
|
|
||||||
|
class JetBook5Output(OutputProfile):
|
||||||
|
|
||||||
|
name = 'JetBook 5-inch'
|
||||||
|
short_name = 'jetbook5'
|
||||||
|
description = _('This profile is intended for the 5-inch JetBook.')
|
||||||
|
|
||||||
|
screen_size = (480, 640)
|
||||||
|
dpi = 168.451
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SonyReaderLandscapeOutput(SonyReaderOutput):
|
class SonyReaderLandscapeOutput(SonyReaderOutput):
|
||||||
|
|
||||||
name = 'Sony Reader Landscape'
|
name = 'Sony Reader Landscape'
|
||||||
@ -334,4 +345,4 @@ class IRexDR1000Output(OutputProfile):
|
|||||||
output_profiles = [OutputProfile, SonyReaderOutput, MSReaderOutput,
|
output_profiles = [OutputProfile, SonyReaderOutput, MSReaderOutput,
|
||||||
MobipocketOutput, HanlinV3Output, CybookG3Output, CybookOpusOutput,
|
MobipocketOutput, HanlinV3Output, CybookG3Output, CybookOpusOutput,
|
||||||
KindleOutput, SonyReaderLandscapeOutput, KindleDXOutput, IlliadOutput,
|
KindleOutput, SonyReaderLandscapeOutput, KindleDXOutput, IlliadOutput,
|
||||||
IRexDR1000Output]
|
IRexDR1000Output, JetBook5Output]
|
||||||
|
@ -108,11 +108,16 @@ class EPUBOutput(OutputFormatPlugin):
|
|||||||
<title>%(title)s</title>
|
<title>%(title)s</title>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
body {
|
body {
|
||||||
background: white no-repeat fixed center center;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
vertical-align: center;
|
vertical-align: center;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
font-size: 18px;
|
font-size: 16pt;
|
||||||
|
}
|
||||||
|
.logo {
|
||||||
|
width: 510px; height: 390px;
|
||||||
|
text-align:center;
|
||||||
|
font-size: 1pt;
|
||||||
|
overflow:hidden;
|
||||||
}
|
}
|
||||||
h1 { font-family: serif; }
|
h1 { font-family: serif; }
|
||||||
h2, h4 { font-family: monospace; }
|
h2, h4 { font-family: monospace; }
|
||||||
@ -120,19 +125,11 @@ class EPUBOutput(OutputFormatPlugin):
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>%(title)s</h1>
|
<h1>%(title)s</h1>
|
||||||
<br/><br/>
|
<div style="text-align:center">
|
||||||
<div style="position:relative">
|
<img class="logo" src="%(img)s" alt="calibre logo" />
|
||||||
<div style="position: absolute; left: 0; top: 0; width:100%%; height:100%%; vertical-align:center">
|
|
||||||
<img src="%(img)s" alt="calibre" style="opacity:0.3"/>
|
|
||||||
</div>
|
|
||||||
<div style="position: absolute; left: 0; top: 0; width:100%%; height:100%%; vertical-align:center">
|
|
||||||
<h2>%(date)s</h2>
|
|
||||||
<br/><br/><br/><br/><br/>
|
|
||||||
<h3>%(author)s</h3>
|
|
||||||
<br/><br/><br/><br/><br/><br/><br/><br/><br/>
|
|
||||||
<h4>Produced by %(app)s</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<h2>%(author)s</h2>
|
||||||
|
<h4>Produced by %(app)s</h4>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
'''
|
'''
|
||||||
@ -201,7 +198,7 @@ class EPUBOutput(OutputFormatPlugin):
|
|||||||
images_rc
|
images_rc
|
||||||
m = self.oeb.metadata
|
m = self.oeb.metadata
|
||||||
title = unicode(m.title[0])
|
title = unicode(m.title[0])
|
||||||
a = [unicode(x) for x in m.creators if m.role == 'aut']
|
a = [unicode(x) for x in m.creator if x.role == 'aut']
|
||||||
author = authors_to_string(a)
|
author = authors_to_string(a)
|
||||||
if QApplication.instance() is None: QApplication([])
|
if QApplication.instance() is None: QApplication([])
|
||||||
f = QFile(':/library')
|
f = QFile(':/library')
|
||||||
@ -211,8 +208,9 @@ class EPUBOutput(OutputFormatPlugin):
|
|||||||
'calibre-logo.png')
|
'calibre-logo.png')
|
||||||
self.oeb.manifest.add(id, href, 'image/png', data=img_data)
|
self.oeb.manifest.add(id, href, 'image/png', data=img_data)
|
||||||
title, author = map(prepare_string_for_xml, (title, author))
|
title, author = map(prepare_string_for_xml, (title, author))
|
||||||
|
if not author or not author.strip():
|
||||||
|
author = strftime('%d %b, %Y')
|
||||||
html = self.TITLEPAGE%dict(title=title, author=author,
|
html = self.TITLEPAGE%dict(title=title, author=author,
|
||||||
date=strftime('%d %b, %Y'),
|
|
||||||
app=__appname__ +' '+__version__,
|
app=__appname__ +' '+__version__,
|
||||||
img=href)
|
img=href)
|
||||||
id, href = self.oeb.manifest.generate('calibre-titlepage',
|
id, href = self.oeb.manifest.generate('calibre-titlepage',
|
||||||
|
@ -6,6 +6,16 @@ __docformat__ = 'restructuredtext en'
|
|||||||
'''
|
'''
|
||||||
ebook-meta
|
ebook-meta
|
||||||
'''
|
'''
|
||||||
|
import sys, os
|
||||||
|
|
||||||
|
from calibre.utils.config import StringConfig
|
||||||
|
from calibre.customize.ui import metadata_readers, metadata_writers
|
||||||
|
from calibre.ebooks.metadata.meta import get_metadata, set_metadata
|
||||||
|
from calibre.ebooks.metadata import string_to_authors, authors_to_sort_string, \
|
||||||
|
title_sort, MetaInformation
|
||||||
|
from calibre.ebooks.lrf.meta import LRFMetaFile
|
||||||
|
from calibre import prints
|
||||||
|
|
||||||
USAGE='%%prog ebook_file [' + _('options') + ']\n' + \
|
USAGE='%%prog ebook_file [' + _('options') + ']\n' + \
|
||||||
_('''
|
_('''
|
||||||
Read/Write metadata from/to ebook files.
|
Read/Write metadata from/to ebook files.
|
||||||
@ -19,15 +29,7 @@ some metadata on a file type that does not support it, the metadata will be
|
|||||||
silently ignored.
|
silently ignored.
|
||||||
''')
|
''')
|
||||||
|
|
||||||
import sys, os
|
|
||||||
|
|
||||||
from calibre.utils.config import StringConfig
|
|
||||||
from calibre.customize.ui import metadata_readers, metadata_writers
|
|
||||||
from calibre.ebooks.metadata.meta import get_metadata, set_metadata
|
|
||||||
from calibre.ebooks.metadata import string_to_authors, authors_to_sort_string, \
|
|
||||||
title_sort, MetaInformation
|
|
||||||
from calibre.ebooks.lrf.meta import LRFMetaFile
|
|
||||||
from calibre import prints
|
|
||||||
|
|
||||||
def config():
|
def config():
|
||||||
c = StringConfig('')
|
c = StringConfig('')
|
||||||
|
@ -303,7 +303,12 @@ class MobiReader(object):
|
|||||||
self.cleanup_html()
|
self.cleanup_html()
|
||||||
|
|
||||||
self.log.debug('Parsing HTML...')
|
self.log.debug('Parsing HTML...')
|
||||||
root = html.fromstring(self.processed_html)
|
try:
|
||||||
|
root = html.fromstring(self.processed_html)
|
||||||
|
except:
|
||||||
|
self.log.warning('MOBI markup appears to contain random bytes. Stripping.')
|
||||||
|
self.processed_html = self.remove_random_bytes(self.processed_html)
|
||||||
|
root = html.fromstring(self.processed_html)
|
||||||
if root.xpath('descendant::p/descendant::p'):
|
if root.xpath('descendant::p/descendant::p'):
|
||||||
from lxml.html import soupparser
|
from lxml.html import soupparser
|
||||||
self.log.warning('Malformed markup, parsing using BeautifulSoup')
|
self.log.warning('Malformed markup, parsing using BeautifulSoup')
|
||||||
@ -444,7 +449,10 @@ class MobiReader(object):
|
|||||||
self.processed_html = '<html><p>' + self.processed_html.replace('\n\n', '<p>') + '</html>'
|
self.processed_html = '<html><p>' + self.processed_html.replace('\n\n', '<p>') + '</html>'
|
||||||
self.processed_html = self.processed_html.replace('\r\n', '\n')
|
self.processed_html = self.processed_html.replace('\r\n', '\n')
|
||||||
self.processed_html = self.processed_html.replace('> <', '>\n<')
|
self.processed_html = self.processed_html.replace('> <', '>\n<')
|
||||||
self.processed_html = re.sub('\x14|\x15|\x1c|\x1d|\xef|\x12|\x13|\xec', '', self.processed_html)
|
|
||||||
|
def remove_random_bytes(self, html):
|
||||||
|
return re.sub('\x14|\x15|\x1c|\x1d|\xef|\x12|\x13|\xec',
|
||||||
|
'', html)
|
||||||
|
|
||||||
def ensure_unit(self, raw, unit='px'):
|
def ensure_unit(self, raw, unit='px'):
|
||||||
if re.search(r'\d+$', raw) is not None:
|
if re.search(r'\d+$', raw) is not None:
|
||||||
|
@ -64,4 +64,15 @@ class ODTInput(InputFormatPlugin):
|
|||||||
accelerators):
|
accelerators):
|
||||||
return Extract()(stream, '.')
|
return Extract()(stream, '.')
|
||||||
|
|
||||||
|
def postprocess_book(self, oeb, opts, log):
|
||||||
|
# Fix <p><div> constructs as the asinine epubchecker complains
|
||||||
|
# about them
|
||||||
|
from calibre.ebooks.oeb.base import XPath, XHTML
|
||||||
|
path = XPath('//h:p/h:div')
|
||||||
|
for item in oeb.spine:
|
||||||
|
root = item.data
|
||||||
|
if not hasattr(root, 'xpath'): continue
|
||||||
|
for div in path(root):
|
||||||
|
div.getparent().tag = XHTML('div')
|
||||||
|
|
||||||
|
|
||||||
|
@ -694,7 +694,6 @@ class Metadata(object):
|
|||||||
def to_opf2(self, parent=None):
|
def to_opf2(self, parent=None):
|
||||||
nsmap = self._opf2_nsmap
|
nsmap = self._opf2_nsmap
|
||||||
nsrmap = dict((value, key) for key, value in nsmap.items())
|
nsrmap = dict((value, key) for key, value in nsmap.items())
|
||||||
nsmap.pop('opf', '')
|
|
||||||
elem = element(parent, OPF('metadata'), nsmap=nsmap)
|
elem = element(parent, OPF('metadata'), nsmap=nsmap)
|
||||||
for term in self.items:
|
for term in self.items:
|
||||||
for item in self.items[term]:
|
for item in self.items[term]:
|
||||||
@ -815,15 +814,28 @@ class Manifest(object):
|
|||||||
data = etree.fromstring(data, parser=RECOVER_PARSER)
|
data = etree.fromstring(data, parser=RECOVER_PARSER)
|
||||||
return data
|
return data
|
||||||
data = first_pass(data)
|
data = first_pass(data)
|
||||||
# Force into the XHTML namespace
|
|
||||||
|
# Handle weird (non-HTML/fragment) files
|
||||||
if barename(data.tag) != 'html':
|
if barename(data.tag) != 'html':
|
||||||
self.oeb.log.warn('File %r does not appear to be (X)HTML'%self.href)
|
self.oeb.log.warn('File %r does not appear to be (X)HTML'%self.href)
|
||||||
nroot = etree.fromstring('<html></html>')
|
nroot = etree.fromstring('<html></html>')
|
||||||
|
has_body = False
|
||||||
|
for child in list(data):
|
||||||
|
if barename(child.tag) == 'body':
|
||||||
|
has_body = True
|
||||||
|
break
|
||||||
|
parent = nroot
|
||||||
|
if not has_body:
|
||||||
|
self.oeb.log.warn('File %r appears to be a HTML fragment'%self.href)
|
||||||
|
nroot = etree.fromstring('<html><body/></html>')
|
||||||
|
parent = nroot[0]
|
||||||
for child in list(data):
|
for child in list(data):
|
||||||
child.getparent().remove(child)
|
child.getparent().remove(child)
|
||||||
nroot.append(child)
|
parent.append(child)
|
||||||
data = nroot
|
data = nroot
|
||||||
elif not namespace(data.tag):
|
|
||||||
|
# Force into the XHTML namespace
|
||||||
|
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:
|
||||||
|
@ -3,7 +3,7 @@ __license__ = 'GPL 3'
|
|||||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os
|
import os, re
|
||||||
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
|
||||||
@ -34,6 +34,10 @@ class OEBOutput(OutputFormatPlugin):
|
|||||||
if root is not None:
|
if root is not None:
|
||||||
raw = etree.tostring(root, pretty_print=True,
|
raw = etree.tostring(root, pretty_print=True,
|
||||||
encoding='utf-8', xml_declaration=True)
|
encoding='utf-8', xml_declaration=True)
|
||||||
|
if key == OPF_MIME:
|
||||||
|
# Needed as I can't get lxml to output opf:role and
|
||||||
|
# not output <opf:metadata> as well
|
||||||
|
raw = re.sub(r'(<[/]{0,1})opf:', r'\1', raw)
|
||||||
with open(href, 'wb') as f:
|
with open(href, 'wb') as f:
|
||||||
f.write(raw)
|
f.write(raw)
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ def meta_info_to_oeb_metadata(mi, m, log):
|
|||||||
m.add('title', mi.title_sort)
|
m.add('title', mi.title_sort)
|
||||||
m.title[0].file_as = mi.title_sort
|
m.title[0].file_as = mi.title_sort
|
||||||
if mi.authors:
|
if mi.authors:
|
||||||
m.filter('creator', lambda x : x.role.lower() == 'aut')
|
m.filter('creator', lambda x : x.role.lower() in ['aut', ''])
|
||||||
for a in mi.authors:
|
for a in mi.authors:
|
||||||
attrib = {'role':'aut'}
|
attrib = {'role':'aut'}
|
||||||
if mi.author_sort:
|
if mi.author_sort:
|
||||||
|
@ -1128,6 +1128,7 @@ class SearchBox(QLineEdit):
|
|||||||
self.home(False)
|
self.home(False)
|
||||||
self.initial_state = True
|
self.initial_state = True
|
||||||
self.setStyleSheet("background-color: white")
|
self.setStyleSheet("background-color: white")
|
||||||
|
self.emit(SIGNAL('cleared()'))
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
self.clear_to_help()
|
self.clear_to_help()
|
||||||
|
@ -456,6 +456,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
SIGNAL('count_changed(int)'), self.location_view.count_changed)
|
SIGNAL('count_changed(int)'), self.location_view.count_changed)
|
||||||
self.connect(self.library_view.model(), SIGNAL('count_changed(int)'),
|
self.connect(self.library_view.model(), SIGNAL('count_changed(int)'),
|
||||||
self.tags_view.recount)
|
self.tags_view.recount)
|
||||||
|
self.connect(self.search, SIGNAL('cleared()'), self.tags_view.clear)
|
||||||
self.library_view.model().count_changed()
|
self.library_view.model().count_changed()
|
||||||
########################### Cover Flow ################################
|
########################### Cover Flow ################################
|
||||||
self.cover_flow = None
|
self.cover_flow = None
|
||||||
@ -1409,7 +1410,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
self.view_format(row, format)
|
self.view_format(row, format)
|
||||||
break
|
break
|
||||||
if not in_prefs:
|
if not in_prefs:
|
||||||
self.view_format(row, format[0])
|
self.view_format(row, formats[0])
|
||||||
else:
|
else:
|
||||||
paths = self.current_view().model().paths(rows)
|
paths = self.current_view().model().paths(rows)
|
||||||
for path in paths:
|
for path in paths:
|
||||||
|
@ -36,6 +36,9 @@ class TagsView(QTreeView):
|
|||||||
self.emit(SIGNAL('tags_marked(PyQt_PyObject, PyQt_PyObject)'),
|
self.emit(SIGNAL('tags_marked(PyQt_PyObject, PyQt_PyObject)'),
|
||||||
self._model.tokens(), self.match_all.isChecked())
|
self._model.tokens(), self.match_all.isChecked())
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
self.model().clear_state()
|
||||||
|
|
||||||
def recount(self, *args):
|
def recount(self, *args):
|
||||||
ci = self.currentIndex()
|
ci = self.currentIndex()
|
||||||
if not ci.isValid():
|
if not ci.isValid():
|
||||||
@ -119,6 +122,11 @@ class TagsModel(QStandardItemModel):
|
|||||||
self._data[category], self.cmap[r], self.bold_font, self.icon_map))
|
self._data[category], self.cmap[r], self.bold_font, self.icon_map))
|
||||||
#self.reset()
|
#self.reset()
|
||||||
|
|
||||||
|
def clear_state(self):
|
||||||
|
for category in self._data.values():
|
||||||
|
for tag in category:
|
||||||
|
tag.state = 0
|
||||||
|
self.refresh()
|
||||||
|
|
||||||
def reinit(self, *args, **kwargs):
|
def reinit(self, *args, **kwargs):
|
||||||
if not self.ignore_next_search:
|
if not self.ignore_next_search:
|
||||||
|
@ -166,6 +166,11 @@ def fetch_scheduled_recipe(recipe, script):
|
|||||||
recs.append(('base_font_size', lf['base_font_size'],
|
recs.append(('base_font_size', lf['base_font_size'],
|
||||||
OptionRecommendation.HIGH))
|
OptionRecommendation.HIGH))
|
||||||
|
|
||||||
|
lr = load_defaults('lrf_output')
|
||||||
|
if lr.get('header', False):
|
||||||
|
recs.append(('header', True, OptionRecommendation.HIGH))
|
||||||
|
recs.append(('header_format', '%t', OptionRecommendation.HIGH))
|
||||||
|
|
||||||
args = [script, pt.name, recs]
|
args = [script, pt.name, recs]
|
||||||
if recipe.needs_subscription:
|
if recipe.needs_subscription:
|
||||||
x = config.get('recipe_account_info_%s'%recipe.id, False)
|
x = config.get('recipe_account_info_%s'%recipe.id, False)
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user