mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-08-11 09:13:57 -04:00
KG updates
This commit is contained in:
commit
171897529e
@ -17,9 +17,9 @@ from calibre.ebooks.metadata import authors_to_string, MetaInformation, \
|
|||||||
title_sort
|
title_sort
|
||||||
from calibre.ebooks.metadata.book.base import Metadata
|
from calibre.ebooks.metadata.book.base import Metadata
|
||||||
from calibre.ebooks.metadata.epub import set_metadata
|
from calibre.ebooks.metadata.epub import set_metadata
|
||||||
from calibre.gui2.dialogs.confirm_delete import _config_name
|
from calibre.gui2.dialogs.confirm_delete import config_name
|
||||||
from calibre.library.server.utils import strftime
|
from calibre.library.server.utils import strftime
|
||||||
from calibre.utils.config import config_dir, dynamic, DynamicConfig, prefs
|
from calibre.utils.config import config_dir, dynamic, prefs
|
||||||
from calibre.utils.date import now, parse_date
|
from calibre.utils.date import now, parse_date
|
||||||
from calibre.utils.logging import Log
|
from calibre.utils.logging import Log
|
||||||
from calibre.utils.zipfile import ZipFile
|
from calibre.utils.zipfile import ZipFile
|
||||||
@ -33,8 +33,8 @@ class AppleOpenFeedback(OpenFeedback):
|
|||||||
self.plugin = plugin
|
self.plugin = plugin
|
||||||
|
|
||||||
def custom_dialog(self, parent):
|
def custom_dialog(self, parent):
|
||||||
from PyQt4.Qt import (QCheckBox, QDialog, QDialogButtonBox, QIcon,
|
from PyQt4.Qt import (QDialog, QDialogButtonBox, QIcon,
|
||||||
QLabel, QPixmap, QPushButton, QSize, QVBoxLayout)
|
QLabel, QPushButton, QVBoxLayout)
|
||||||
|
|
||||||
class Dialog(QDialog):
|
class Dialog(QDialog):
|
||||||
|
|
||||||
@ -45,19 +45,20 @@ class AppleOpenFeedback(OpenFeedback):
|
|||||||
self.l = l = QVBoxLayout()
|
self.l = l = QVBoxLayout()
|
||||||
self.setLayout(l)
|
self.setLayout(l)
|
||||||
msg = QLabel()
|
msg = QLabel()
|
||||||
msg.setText(
|
msg.setText(_(
|
||||||
'<p>If you do not want calibre to recognize your Apple iDevice '
|
'<p>If you do not want calibre to recognize your Apple iDevice '
|
||||||
'when it is connected to your computer, '
|
'when it is connected to your computer, '
|
||||||
'click <b>Disable Apple Driver</b>.</p>'
|
'click <b>Disable Apple Driver</b>.</p>'
|
||||||
'<p>To transfer books to your iDevice, '
|
'<p>To transfer books to your iDevice, '
|
||||||
'click <b>Disable Apple Driver</b>, '
|
'click <b>Disable Apple Driver</b>, '
|
||||||
"then use the 'Connect to iTunes' method recommended in the "
|
"then use the 'Connect to iTunes' method recommended in the "
|
||||||
'<a href=\"http://www.mobileread.com/forums/showthread.php?t=118559\">Calibre + iDevices FAQ</a>, '
|
'<a href="http://www.mobileread.com/forums/showthread.php?t=118559">Calibre + iDevices FAQ</a>, '
|
||||||
'using the <em>Connect/Share</em>|<em>Connect to iTunes</em> menu item.</p>'
|
'using the <em>Connect/Share</em>|<em>Connect to iTunes</em> menu item.</p>'
|
||||||
'<p>Enabling the Apple driver for direct connection to iDevices '
|
'<p>Enabling the Apple driver for direct connection to iDevices '
|
||||||
'is an unsupported advanced user mode.</p>'
|
'is an unsupported advanced user mode.</p>'
|
||||||
'<p></p>'
|
'<p></p>'
|
||||||
)
|
))
|
||||||
|
msg.setOpenExternalLinks(True)
|
||||||
msg.setWordWrap(True)
|
msg.setWordWrap(True)
|
||||||
l.addWidget(msg)
|
l.addWidget(msg)
|
||||||
|
|
||||||
@ -75,17 +76,19 @@ class AppleOpenFeedback(OpenFeedback):
|
|||||||
self.setWindowIcon(QIcon(I(pixmap)))
|
self.setWindowIcon(QIcon(I(pixmap)))
|
||||||
self.resize(self.sizeHint())
|
self.resize(self.sizeHint())
|
||||||
|
|
||||||
if Dialog(parent).exec_():
|
self.finished.connect(self.do_it)
|
||||||
# Enable Apple driver, inhibit future display of dialog
|
|
||||||
# Reset dialog with Preferences|Behavior|Reset all disabled confirmation dialogs
|
def do_it(self, return_code):
|
||||||
|
if return_code == self.Accepted:
|
||||||
self.log.info(" Apple driver ENABLED")
|
self.log.info(" Apple driver ENABLED")
|
||||||
dynamic[_config_name(self.plugin.DISPLAY_DISABLE_DIALOG)] = False
|
dynamic[config_name(self.plugin.DISPLAY_DISABLE_DIALOG)] = False
|
||||||
else:
|
else:
|
||||||
# Disable Apple driver
|
|
||||||
from calibre.customize.ui import disable_plugin
|
from calibre.customize.ui import disable_plugin
|
||||||
self.log.info(" Apple driver DISABLED")
|
self.log.info(" Apple driver DISABLED")
|
||||||
disable_plugin(self.plugin)
|
disable_plugin(self.plugin)
|
||||||
|
|
||||||
|
return Dialog(parent)
|
||||||
|
|
||||||
|
|
||||||
from PIL import Image as PILImage
|
from PIL import Image as PILImage
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
@ -179,7 +182,7 @@ class ITUNES(DriverBase):
|
|||||||
#: The version of this plugin as a 3-tuple (major, minor, revision)
|
#: The version of this plugin as a 3-tuple (major, minor, revision)
|
||||||
version = (1,0,0)
|
version = (1,0,0)
|
||||||
|
|
||||||
DISPLAY_DISABLE_DIALOG = "display_disable_dialog"
|
DISPLAY_DISABLE_DIALOG = "display_disable_apple_driver_dialog"
|
||||||
|
|
||||||
# EXTRA_CUSTOMIZATION_MESSAGE indexes
|
# EXTRA_CUSTOMIZATION_MESSAGE indexes
|
||||||
USE_SERIES_AS_CATEGORY = 0
|
USE_SERIES_AS_CATEGORY = 0
|
||||||
@ -805,7 +808,7 @@ class ITUNES(DriverBase):
|
|||||||
|
|
||||||
# Display a dialog recommending using 'Connect to iTunes' if user hasn't
|
# Display a dialog recommending using 'Connect to iTunes' if user hasn't
|
||||||
# previously disabled the dialog
|
# previously disabled the dialog
|
||||||
if dynamic.get(_config_name(self.DISPLAY_DISABLE_DIALOG),True):
|
if dynamic.get(config_name(self.DISPLAY_DISABLE_DIALOG),True):
|
||||||
raise AppleOpenFeedback(self)
|
raise AppleOpenFeedback(self)
|
||||||
else:
|
else:
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
|
@ -43,8 +43,8 @@ class OpenFeedback(DeviceError):
|
|||||||
|
|
||||||
def custom_dialog(self, parent):
|
def custom_dialog(self, parent):
|
||||||
'''
|
'''
|
||||||
If you need to show the user a custom dialog, create and
|
If you need to show the user a custom dialog, instead if just
|
||||||
run it from a custom_dialog() method in your subclass.
|
displaying the feedback_msg, create and return it here.
|
||||||
'''
|
'''
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@ -1003,8 +1003,10 @@ OptionRecommendation(name='sr3_replace',
|
|||||||
self.opts.insert_blank_line = oibl
|
self.opts.insert_blank_line = oibl
|
||||||
self.opts.remove_paragraph_spacing = orps
|
self.opts.remove_paragraph_spacing = orps
|
||||||
|
|
||||||
from calibre.ebooks.oeb.transforms.page_margin import RemoveFakeMargins
|
from calibre.ebooks.oeb.transforms.page_margin import \
|
||||||
|
RemoveFakeMargins, RemoveAdobeMargins
|
||||||
RemoveFakeMargins()(self.oeb, self.log, self.opts)
|
RemoveFakeMargins()(self.oeb, self.log, self.opts)
|
||||||
|
RemoveAdobeMargins()(self.oeb, self.log, self.opts)
|
||||||
|
|
||||||
pr(0.9)
|
pr(0.9)
|
||||||
self.flush()
|
self.flush()
|
||||||
|
@ -182,6 +182,19 @@ def metadata_from_filename(name, pat=None):
|
|||||||
mi.isbn = si
|
mi.isbn = si
|
||||||
except (IndexError, ValueError):
|
except (IndexError, ValueError):
|
||||||
pass
|
pass
|
||||||
|
try:
|
||||||
|
publisher = match.group('publisher')
|
||||||
|
mi.publisher = publisher
|
||||||
|
except (IndexError, ValueError):
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
pubdate = match.group('published')
|
||||||
|
if pubdate:
|
||||||
|
from calibre.utils.date import parse_date
|
||||||
|
mi.pubdate = parse_date(pubdate)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
if mi.is_null('title'):
|
if mi.is_null('title'):
|
||||||
mi.title = name
|
mi.title = name
|
||||||
return mi
|
return mi
|
||||||
|
@ -102,6 +102,7 @@ class MobiMLizer(object):
|
|||||||
def __call__(self, oeb, context):
|
def __call__(self, oeb, context):
|
||||||
oeb.logger.info('Converting XHTML to Mobipocket markup...')
|
oeb.logger.info('Converting XHTML to Mobipocket markup...')
|
||||||
self.oeb = oeb
|
self.oeb = oeb
|
||||||
|
self.log = self.oeb.logger
|
||||||
self.opts = context
|
self.opts = context
|
||||||
self.profile = profile = context.dest
|
self.profile = profile = context.dest
|
||||||
self.fnums = fnums = dict((v, k) for k, v in profile.fnums.items())
|
self.fnums = fnums = dict((v, k) for k, v in profile.fnums.items())
|
||||||
@ -118,6 +119,10 @@ class MobiMLizer(object):
|
|||||||
del oeb.guide['cover']
|
del oeb.guide['cover']
|
||||||
item = oeb.manifest.hrefs[href]
|
item = oeb.manifest.hrefs[href]
|
||||||
if item.spine_position is not None:
|
if item.spine_position is not None:
|
||||||
|
self.log.warn('Found an HTML cover,', item.href, 'removing it.',
|
||||||
|
'If you find some content missing from the output MOBI, it '
|
||||||
|
'is because you misidentified the HTML cover in the input '
|
||||||
|
'document')
|
||||||
oeb.spine.remove(item)
|
oeb.spine.remove(item)
|
||||||
if item.media_type in OEB_DOCS:
|
if item.media_type in OEB_DOCS:
|
||||||
self.oeb.manifest.remove(item)
|
self.oeb.manifest.remove(item)
|
||||||
@ -206,6 +211,10 @@ class MobiMLizer(object):
|
|||||||
vspace = bstate.vpadding + bstate.vmargin
|
vspace = bstate.vpadding + bstate.vmargin
|
||||||
bstate.vpadding = bstate.vmargin = 0
|
bstate.vpadding = bstate.vmargin = 0
|
||||||
if tag not in TABLE_TAGS:
|
if tag not in TABLE_TAGS:
|
||||||
|
if tag in ('ul', 'ol') and vspace > 0:
|
||||||
|
wrapper.addprevious(etree.Element(XHTML('div'),
|
||||||
|
height=self.mobimlize_measure(vspace)))
|
||||||
|
else:
|
||||||
wrapper.attrib['height'] = self.mobimlize_measure(vspace)
|
wrapper.attrib['height'] = self.mobimlize_measure(vspace)
|
||||||
para.attrib['width'] = self.mobimlize_measure(indent)
|
para.attrib['width'] = self.mobimlize_measure(indent)
|
||||||
elif tag == 'table' and vspace > 0:
|
elif tag == 'table' and vspace > 0:
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
|
||||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
|
||||||
__docformat__ = 'restructuredtext en'
|
|
||||||
|
|
||||||
|
|
||||||
class RemoveFakeMargins(object):
|
|
||||||
'''
|
|
||||||
Try to detect and remove fake margins inserted by asinine ebook creation
|
|
||||||
software on each paragraph/wrapper div. Can be used only after CSS
|
|
||||||
flattening.
|
|
||||||
'''
|
|
||||||
|
|
||||||
def __call__(self, oeb, opts, log):
|
|
||||||
self.oeb, self.opts, self.log = oeb, opts, log
|
|
||||||
|
|
||||||
from calibre.ebooks.oeb.base import XPath, OEB_STYLES
|
|
||||||
|
|
||||||
stylesheet = None
|
|
||||||
for item in self.oeb.manifest:
|
|
||||||
if item.media_type.lower() in OEB_STYLES:
|
|
||||||
stylesheet = item.data
|
|
||||||
break
|
|
||||||
|
|
||||||
if stylesheet is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
top_level_elements = {}
|
|
||||||
second_level_elements = {}
|
|
||||||
|
|
||||||
for x in self.oeb.spine:
|
|
||||||
root = x.data
|
|
||||||
body = XPath('//h:body')(root)
|
|
||||||
if body:
|
|
||||||
body = body[0]
|
|
||||||
|
|
||||||
if not hasattr(body, 'xpath'):
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Check for margins on top level elements
|
|
||||||
for lb in XPath('./h:div|./h:p|./*/h:div|./*/h:p')(body):
|
|
||||||
cls = lb.get('class', '')
|
|
||||||
level = top_level_elements if lb.getparent() is body else \
|
|
||||||
second_level_elements
|
|
||||||
if cls not in level:
|
|
||||||
level[cls] = []
|
|
||||||
top_level_elements[cls] = []
|
|
||||||
level[cls].append(lb)
|
|
||||||
|
|
||||||
|
|
||||||
def get_margins(self, stylesheet, cls):
|
|
||||||
pass
|
|
||||||
|
|
@ -11,6 +11,26 @@ from collections import Counter
|
|||||||
|
|
||||||
from calibre.ebooks.oeb.base import OEB_STYLES, barename, XPath
|
from calibre.ebooks.oeb.base import OEB_STYLES, barename, XPath
|
||||||
|
|
||||||
|
class RemoveAdobeMargins(object):
|
||||||
|
'''
|
||||||
|
Remove margins specified in Adobe's page templates.
|
||||||
|
'''
|
||||||
|
|
||||||
|
def __call__(self, oeb, log, opts):
|
||||||
|
self.oeb, self.opts, self.log = oeb, opts, log
|
||||||
|
|
||||||
|
for item in self.oeb.manifest:
|
||||||
|
if item.media_type == 'application/vnd.adobe-page-template+xml':
|
||||||
|
self.log('Removing page margins specified in the'
|
||||||
|
' Adobe page template')
|
||||||
|
for elem in item.data.xpath(
|
||||||
|
'//*[@margin-bottom or @margin-top '
|
||||||
|
'or @margin-left or @margin-right]'):
|
||||||
|
for margin in ('left', 'right', 'top', 'bottom'):
|
||||||
|
attr = 'margin-'+margin
|
||||||
|
elem.attrib.pop(attr, None)
|
||||||
|
|
||||||
|
|
||||||
class RemoveFakeMargins(object):
|
class RemoveFakeMargins(object):
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
@ -7,7 +7,7 @@ from calibre.gui2 import dynamic
|
|||||||
from calibre.gui2.dialogs.confirm_delete_ui import Ui_Dialog
|
from calibre.gui2.dialogs.confirm_delete_ui import Ui_Dialog
|
||||||
from PyQt4.Qt import QDialog, Qt, QPixmap, QIcon
|
from PyQt4.Qt import QDialog, Qt, QPixmap, QIcon
|
||||||
|
|
||||||
def _config_name(name):
|
def config_name(name):
|
||||||
return name + '_again'
|
return name + '_again'
|
||||||
|
|
||||||
class Dialog(QDialog, Ui_Dialog):
|
class Dialog(QDialog, Ui_Dialog):
|
||||||
@ -22,11 +22,11 @@ class Dialog(QDialog, Ui_Dialog):
|
|||||||
self.buttonBox.setFocus(Qt.OtherFocusReason)
|
self.buttonBox.setFocus(Qt.OtherFocusReason)
|
||||||
|
|
||||||
def toggle(self, *args):
|
def toggle(self, *args):
|
||||||
dynamic[_config_name(self.name)] = self.again.isChecked()
|
dynamic[config_name(self.name)] = self.again.isChecked()
|
||||||
|
|
||||||
|
|
||||||
def confirm(msg, name, parent=None, pixmap='dialog_warning.png'):
|
def confirm(msg, name, parent=None, pixmap='dialog_warning.png'):
|
||||||
if not dynamic.get(_config_name(name), True):
|
if not dynamic.get(config_name(name), True):
|
||||||
return True
|
return True
|
||||||
d = Dialog(msg, name, parent)
|
d = Dialog(msg, name, parent)
|
||||||
d.label.setPixmap(QPixmap(I(pixmap)))
|
d.label.setPixmap(QPixmap(I(pixmap)))
|
||||||
|
@ -206,6 +206,46 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QLabel" name="label_8">
|
||||||
|
<property name="text">
|
||||||
|
<string>Publisher:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="1">
|
||||||
|
<widget class="QLineEdit" name="publisher">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Regular expression (?P<publisher>)</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>No match</string>
|
||||||
|
</property>
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QLabel" name="label_9">
|
||||||
|
<property name="text">
|
||||||
|
<string>Published:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="1">
|
||||||
|
<widget class="QLineEdit" name="pubdate">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Regular expression (?P<published>)</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>No match</string>
|
||||||
|
</property>
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -121,6 +121,12 @@ class FilenamePattern(QWidget, Ui_Form):
|
|||||||
else:
|
else:
|
||||||
self.series_index.setText(_('No match'))
|
self.series_index.setText(_('No match'))
|
||||||
|
|
||||||
|
if mi.publisher:
|
||||||
|
self.publisher.setText(mi.publisher)
|
||||||
|
|
||||||
|
if mi.pubdate:
|
||||||
|
self.pubdate.setText(mi.pubdate.strftime('%Y-%m-%d'))
|
||||||
|
|
||||||
self.isbn.setText(_('No match') if mi.isbn is None else str(mi.isbn))
|
self.isbn.setText(_('No match') if mi.isbn is None else str(mi.isbn))
|
||||||
|
|
||||||
|
|
||||||
|
@ -76,6 +76,8 @@ class CustomColumns(object):
|
|||||||
'num':record[6],
|
'num':record[6],
|
||||||
'is_multiple':record[7],
|
'is_multiple':record[7],
|
||||||
}
|
}
|
||||||
|
if data['display'] is None:
|
||||||
|
data['display'] = {}
|
||||||
table, lt = self.custom_table_names(data['num'])
|
table, lt = self.custom_table_names(data['num'])
|
||||||
if table not in custom_tables or (data['normalized'] and lt not in
|
if table not in custom_tables or (data['normalized'] and lt not in
|
||||||
custom_tables):
|
custom_tables):
|
||||||
|
@ -31,8 +31,11 @@ class cmd_commit(_cmd_commit):
|
|||||||
summary = ''
|
summary = ''
|
||||||
raw = urllib.urlopen('https://bugs.launchpad.net/calibre/+bug/' +
|
raw = urllib.urlopen('https://bugs.launchpad.net/calibre/+bug/' +
|
||||||
bug).read()
|
bug).read()
|
||||||
|
try:
|
||||||
h1 = html.fromstring(raw).xpath('//h1[@id="edit-title"]')[0]
|
h1 = html.fromstring(raw).xpath('//h1[@id="edit-title"]')[0]
|
||||||
summary = html.tostring(h1, method='text', encoding=unicode).strip()
|
summary = html.tostring(h1, method='text', encoding=unicode).strip()
|
||||||
|
except:
|
||||||
|
summary = 'Private bug'
|
||||||
print 'Working on bug:', summary
|
print 'Working on bug:', summary
|
||||||
if summary:
|
if summary:
|
||||||
msg = msg.replace('#%s'%bug, '#%s (%s)'%(bug, summary))
|
msg = msg.replace('#%s'%bug, '#%s (%s)'%(bug, summary))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user