mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge from trunk and fix for bug #5414
This commit is contained in:
commit
c79091e71b
@ -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 ')]
|
||||
|
||||
|
@ -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:]
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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 &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 &SVG cover</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user