Merge from trunk and fix for bug #5414

This commit is contained in:
Charles Haley 2010-04-30 07:08:50 +01:00
commit c79091e71b
11 changed files with 146 additions and 56 deletions

View File

@ -9,8 +9,9 @@ __description__ = 'PCMag (www.pcmag.com) delivers authoritative, labs-based comp
'''
http://www.pcmag.com/
'''
import re
from calibre.web.feeds.news import BasicNewsRecipe
from calibre.ebooks.BeautifulSoup import Comment
class pcMag(BasicNewsRecipe):
__author__ = 'Lorenzo Vigentini'
@ -33,9 +34,6 @@ class pcMag(BasicNewsRecipe):
remove_javascript = True
no_stylesheets = True
keep_only_tags = [
dict(name='div', attrs={'id':'articleContent'})
]
feeds = [
(u'Tech Commentary from the Editors of PC Magazine', u'http://rssnewsapps.ziffdavis.com/PCMAG_commentary.xml'),
@ -49,8 +47,13 @@ class pcMag(BasicNewsRecipe):
(u'Technology News from Ziff Davis', u'http://rssnewsapps.ziffdavis.com/pcmagbreakingnews.xml')
]
keep_only_tags = [dict(attrs={'class':'content-page'})]
remove_tags = [
dict(name='div', attrs={'id':['microAd','intellitxt','articleDeckTalkback','inlineDigg','underArticleLinks','w_talkback']}),
dict(name='span', attrs={'id':['highlights_content','yahooBuzzBadge-48558872521263350499378']})
]
dict(attrs={'class':['control-side','comment','highlights_content','btn-holder','subscribe-panel',
'grey-box comments-box']}),
dict(id=['inlineDigg']),
dict(text=lambda text:isinstance(text, Comment)),
dict(name='img', width='1'),
]
preprocess_regexps = [(re.compile(r"<img '[^']+?'"), lambda m : '<img ')]

View File

@ -7,7 +7,7 @@ import os
import glob
from calibre.customize import FileTypePlugin, MetadataReaderPlugin, MetadataWriterPlugin
from calibre.constants import numeric_version
from calibre.ebooks.metadata.archive import ArchiveExtract
from calibre.ebooks.metadata.archive import ArchiveExtract, get_cbz_metadata
class HTML2ZIP(FileTypePlugin):
name = 'HTML to ZIP'
@ -97,6 +97,12 @@ class ComicMetadataReader(MetadataReaderPlugin):
from calibre.ebooks.metadata import MetaInformation
ret = extract_first(stream)
mi = MetaInformation(None, None)
stream.seek(0)
if ftype == 'cbz':
try:
mi.smart_update(get_cbz_metadata(stream))
except:
pass
if ret is not None:
path, data = ret
ext = os.path.splitext(path)[1][1:]

View File

@ -202,9 +202,11 @@ class PRS505(CLI, Device):
def write_card_prefix(prefix, listid):
if prefix is not None and hasattr(booklists[listid], 'write'):
if not os.path.exists(prefix):
os.makedirs(prefix)
with open(prefix + self.__class__.CACHE_XML, 'wb') as f:
tgt = os.path.join(prefix, *(self.CACHE_XML.split('/')))
base = os.path.dirname(tgt)
if not os.path.exists(base):
os.makedirs(base)
with open(tgt, 'wb') as f:
booklists[listid].write(f)
write_card_prefix(self._card_a_prefix, 1)
write_card_prefix(self._card_b_prefix, 2)

View File

@ -81,12 +81,40 @@ class EPUBOutput(OutputFormatPlugin):
OptionRecommendation(name='no_default_epub_cover', recommended_value=False,
help=_('Normally, if the input file has no cover and you don\'t'
' specify one, a default cover is generated with the title, '
'authors, etc. This option disables the generation of this cover.')),
'authors, etc. This option disables the generation of this cover.')
),
OptionRecommendation(name='no_svg_cover', recommended_value=False,
help=_('Do not use SVG for the book cover. Use this option if '
'your EPUB is going to be used ona device that does not '
'support SVG, like the iPhone or the JetBook Lite. '
'Without this option, such devices will display the cover '
'as a blank page.')
),
])
recommendations = set([('pretty_print', True, OptionRecommendation.HIGH)])
NONSVG_TITLEPAGE_COVER = '''\
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="calibre:cover" content="true" />
<title>Cover</title>
<style type="text/css" title="override_css">
@page {padding: 0pt; margin:0pt}
body { text-align: center; padding:0pt; margin: 0pt; }
div { padding:0pt; margin: 0pt; }
</style>
</head>
<body>
<div>
<img src="%s" alt="cover" style="height: 100%%" />
</div>
</body>
</html>
'''
TITLEPAGE_COVER = '''\
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
@ -301,7 +329,9 @@ class EPUBOutput(OutputFormatPlugin):
else:
href = self.default_cover()
if href is not None:
tp = self.TITLEPAGE_COVER%unquote(href)
templ = self.NONSVG_TITLEPAGE_COVER if self.opts.no_svg_cover \
else self.TITLEPAGE_COVER
tp = templ%unquote(href)
id, href = m.generate('titlepage', 'titlepage.xhtml')
item = m.add(id, href, guess_type('t.xhtml')[0],
data=etree.fromstring(tp))

View File

@ -64,3 +64,45 @@ class ArchiveExtract(FileTypePlugin):
of.write(zf.read(fname))
return of.name
def get_comic_book_info(d, mi):
series = d.get('series', '')
if series.strip():
mi.series = series
if d.get('volume', -1) > -1:
mi.series_index = float(d['volume'])
if d.get('rating', -1) > -1:
mi.rating = d['rating']
for x in ('title', 'publisher'):
y = d.get(x, '').strip()
if y:
setattr(mi, x, y)
tags = d.get('tags', [])
if tags:
mi.tags = tags
authors = []
for credit in d.get('credits', []):
if credit.get('role', '') in ('Writer', 'Artist', 'Cartoonist',
'Creator'):
x = credit.get('person', '')
if x:
x = ' '.join((reversed(x.split(', '))))
authors.append(x)
if authors:
mi.authors = authors
def get_cbz_metadata(stream):
from calibre.utils.zipfile import ZipFile
from calibre.ebooks.metadata import MetaInformation
import json
zf = ZipFile(stream)
mi = MetaInformation(None, None)
if zf.comment:
m = json.loads(zf.comment)
if hasattr(m, 'keys'):
for cat in m.keys():
if cat.startswith('ComicBookInfo'):
get_comic_book_info(m[cat], mi)
return mi

View File

@ -96,6 +96,8 @@ class CSSSelector(etree.XPath):
path = css_to_xpath(css)
except UnicodeEncodeError: # Bug in css_to_xpath
path = '/'
except NotImplementedError: # Probably a subselect like :hover
path = '/'
path = self.LOCAL_NAME_RE.sub(r"local-name() = '", path)
etree.XPath.__init__(self, path, namespaces=namespaces)
self.css = css
@ -534,6 +536,8 @@ class Style(object):
result = base
else:
result = self._unit_convert(width, base=base)
if isinstance(result, (unicode, str, bytes)):
result = self._profile.width
self._width = result
return self._width
@ -555,6 +559,8 @@ class Style(object):
result = base
else:
result = self._unit_convert(height, base=base)
if isinstance(result, (unicode, str, bytes)):
result = self._profile.height
self._height = result
return self._height

View File

@ -17,7 +17,8 @@ class PluginWidget(Widget, Ui_Form):
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
Widget.__init__(self, parent, 'epub_output',
['dont_split_on_page_breaks', 'flow_size', 'no_default_epub_cover']
['dont_split_on_page_breaks', 'flow_size',
'no_default_epub_cover', 'no_svg_cover']
)
self.db, self.book_id = db, book_id
self.initialize_options(get_option, get_help, db, book_id)

View File

@ -21,7 +21,7 @@
</property>
</widget>
</item>
<item row="2" column="0">
<item row="3" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Split files &amp;larger than:</string>
@ -31,7 +31,7 @@
</property>
</widget>
</item>
<item row="2" column="1">
<item row="3" column="1">
<widget class="QSpinBox" name="opt_flow_size">
<property name="suffix">
<string> KB</string>
@ -47,7 +47,7 @@
</property>
</widget>
</item>
<item row="3" column="0">
<item row="4" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -67,6 +67,13 @@
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="opt_no_svg_cover">
<property name="text">
<string>No &amp;SVG cover</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>

View File

@ -8,7 +8,7 @@ import re
from PyQt4.QtCore import SIGNAL, Qt
from PyQt4.QtGui import QDialog, QWidget, QDialogButtonBox, QFileDialog, \
QBrush, QSyntaxHighlighter, QTextCharFormat
QBrush, QTextCursor, QTextEdit
from calibre.gui2.convert.regex_builder_ui import Ui_RegexBuilder
from calibre.gui2.convert.xexp_edit_ui import Ui_Form as Ui_Edit
@ -17,31 +17,6 @@ from calibre.gui2 import error_dialog
from calibre.ebooks.oeb.iterator import EbookIterator
from calibre.gui2.dialogs.choose_format import ChooseFormatDialog
class RegexHighlighter(QSyntaxHighlighter):
def __init__(self, *args):
QSyntaxHighlighter.__init__(self, *args)
self.regex = u''
def update_regex(self, regex):
self.regex = regex
self.rehighlight()
def highlightBlock(self, text):
valid_regex = True
text = qstring_to_unicode(text)
format = QTextCharFormat()
format.setBackground(QBrush(Qt.yellow))
if self.regex:
try:
for mo in re.finditer(self.regex, text):
self.setFormat(mo.start(), mo.end() - mo.start(), format)
except:
valid_regex = False
self.emit(SIGNAL('regex_valid(PyQt_PyObject)'), valid_regex)
class RegexBuilder(QDialog, Ui_RegexBuilder):
def __init__(self, db, book_id, regex, *args):
@ -49,9 +24,7 @@ class RegexBuilder(QDialog, Ui_RegexBuilder):
self.setupUi(self)
self.regex.setText(regex)
self.regex_valid(True)
self.highlighter = RegexHighlighter(self.preview.document())
self.highlighter.update_regex(regex)
self.regex_valid()
if not db or not book_id:
self.button_box.addButton(QDialogButtonBox.Open)
@ -62,7 +35,7 @@ class RegexBuilder(QDialog, Ui_RegexBuilder):
self.connect(self.regex, SIGNAL('textChanged(QString)'), self.regex_valid)
self.connect(self.test, SIGNAL('clicked()'), self.do_test)
def regex_valid(self, valid):
def regex_valid(self):
regex = qstring_to_unicode(self.regex.text())
if regex:
try:
@ -70,11 +43,29 @@ class RegexBuilder(QDialog, Ui_RegexBuilder):
self.regex.setStyleSheet('QLineEdit { color: black; background-color: rgba(0,255,0,20%); }')
except:
self.regex.setStyleSheet('QLineEdit { color: black; background-color: rgb(255,0,0,20%); }')
return False
else:
self.regex.setStyleSheet('QLineEdit { color: black; background-color: white; }')
return True
def do_test(self):
self.highlighter.update_regex(qstring_to_unicode(self.regex.text()))
selections = []
if self.regex_valid():
text = qstring_to_unicode(self.preview.toPlainText())
regex = qstring_to_unicode(self.regex.text())
try:
for match in re.finditer(regex, text):
cursor = QTextCursor(self.preview.document())
cursor.setPosition(match.start(), QTextCursor.MoveAnchor)
cursor.setPosition(match.end(), QTextCursor.KeepAnchor)
sel = QTextEdit.ExtraSelection()
sel.cursor = cursor
sel.format.setBackground(QBrush(Qt.yellow))
selections.append(sel)
except:
pass
self.preview.setExtraSelections(selections)
def select_format(self, db, book_id):
format = None

View File

@ -350,7 +350,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.view_menu = QMenu()
self.view_menu.addAction(_('View'))
self.view_menu.addAction(_('View specific format'))
ac = self.view_menu.addAction(_('View specific format'))
ac.setShortcut(Qt.AltModifier+Qt.Key_V)
self.action_view.setMenu(self.view_menu)
self.delete_menu = QMenu()

View File

@ -394,13 +394,14 @@ class Document(QWebPage):
return self.mainFrame().contentsSize().width() # offsetWidth gives inaccurate results
def set_bottom_padding(self, amount):
padding = '%dpx'%amount
try:
old_padding = unicode(self.javascript('$("body").css("padding-bottom")').toString())
except:
old_padding = ''
body = self.mainFrame().documentElement().findFirst('body')
if body.isNull():
return
old_padding = unicode(body.styleProperty('padding-bottom',
body.ComputedStyle)).strip()
padding = u'%dpx'%amount
if old_padding != padding:
self.javascript('$("body").css("padding-bottom", "%s")' % padding)
body.setStyleProperty('padding-bottom', padding + ' !important')
class EntityDeclarationProcessor(object):
@ -423,7 +424,7 @@ class DocumentView(QWebView):
QWebView.__init__(self, *args)
self.debug_javascript = False
self.shortcuts = Shortcuts(SHORTCUTS, 'shortcuts/viewer')
self.self_closing_pat = re.compile(r'<([a-z]+)\s+([^>]+)/>',
self.self_closing_pat = re.compile(r'<([a-z1-6]+)\s+([^>]+)/>',
re.IGNORECASE)
self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
self._size_hint = QSize(510, 680)