mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge from main branch
This commit is contained in:
commit
b305655e12
@ -61,6 +61,9 @@ def osx_version():
|
|||||||
if m:
|
if m:
|
||||||
return int(m.group(1)), int(m.group(2)), int(m.group(3))
|
return int(m.group(1)), int(m.group(2)), int(m.group(3))
|
||||||
|
|
||||||
|
def confirm_config_name(name):
|
||||||
|
return name + '_again'
|
||||||
|
|
||||||
_filename_sanitize = re.compile(r'[\xae\0\\|\?\*<":>\+/]')
|
_filename_sanitize = re.compile(r'[\xae\0\\|\?\*<":>\+/]')
|
||||||
_filename_sanitize_unicode = frozenset([u'\\', u'|', u'?', u'*', u'<',
|
_filename_sanitize_unicode = frozenset([u'\\', u'|', u'?', u'*', u'<',
|
||||||
u'"', u':', u'>', u'+', u'/'] + list(map(unichr, xrange(32))))
|
u'"', u':', u'>', u'+', u'/'] + list(map(unichr, xrange(32))))
|
||||||
|
@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
import cStringIO, ctypes, datetime, os, re, shutil, subprocess, sys, tempfile, time
|
import cStringIO, ctypes, datetime, os, re, shutil, subprocess, sys, tempfile, time
|
||||||
from calibre.constants import __appname__, __version__, DEBUG
|
from calibre.constants import __appname__, __version__, DEBUG
|
||||||
from calibre import fit_image
|
from calibre import fit_image, confirm_config_name
|
||||||
from calibre.constants import isosx, iswindows
|
from calibre.constants import isosx, iswindows
|
||||||
from calibre.devices.errors import OpenFeedback, UserFeedback
|
from calibre.devices.errors import OpenFeedback, UserFeedback
|
||||||
from calibre.devices.usbms.deviceconfig import DeviceConfig
|
from calibre.devices.usbms.deviceconfig import DeviceConfig
|
||||||
@ -18,34 +18,76 @@ from calibre.ebooks.metadata import authors_to_string, MetaInformation, \
|
|||||||
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.library.server.utils import strftime
|
from calibre.library.server.utils import strftime
|
||||||
from calibre.utils.config import config_dir, 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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AppleOpenFeedback(OpenFeedback):
|
class AppleOpenFeedback(OpenFeedback):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, plugin):
|
||||||
OpenFeedback.__init__(self, u'')
|
OpenFeedback.__init__(self, u'')
|
||||||
|
self.log = plugin.log
|
||||||
|
self.plugin = plugin
|
||||||
|
|
||||||
def custom_dialog(self, parent):
|
def custom_dialog(self, parent):
|
||||||
from PyQt4.Qt import (QDialog, QVBoxLayout, QLabel, QDialogButtonBox)
|
from PyQt4.Qt import (QDialog, QDialogButtonBox, QIcon,
|
||||||
|
QLabel, QPushButton, QVBoxLayout)
|
||||||
|
|
||||||
class Dialog(QDialog):
|
class Dialog(QDialog):
|
||||||
|
|
||||||
def __init__(self, p):
|
def __init__(self, p, cd, pixmap='dialog_information.png'):
|
||||||
QDialog.__init__(self, p)
|
QDialog.__init__(self, p)
|
||||||
|
self.cd = cd
|
||||||
|
self.setWindowTitle("Apple iDevice detected")
|
||||||
self.l = l = QVBoxLayout()
|
self.l = l = QVBoxLayout()
|
||||||
self.setLayout(l)
|
self.setLayout(l)
|
||||||
l.addWidget(QLabel('test'))
|
msg = QLabel()
|
||||||
self.bb = QDialogButtonBox(QDialogButtonBox.OK)
|
msg.setText(_(
|
||||||
|
'<p>If you do not want calibre to recognize your Apple iDevice '
|
||||||
|
'when it is connected to your computer, '
|
||||||
|
'click <b>Disable Apple Driver</b>.</p>'
|
||||||
|
'<p>To transfer books to your iDevice, '
|
||||||
|
'click <b>Disable Apple Driver</b>, '
|
||||||
|
"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>, '
|
||||||
|
'using the <em>Connect/Share</em>|<em>Connect to iTunes</em> menu item.</p>'
|
||||||
|
'<p>Enabling the Apple driver for direct connection to iDevices '
|
||||||
|
'is an unsupported advanced user mode.</p>'
|
||||||
|
'<p></p>'
|
||||||
|
))
|
||||||
|
msg.setOpenExternalLinks(True)
|
||||||
|
msg.setWordWrap(True)
|
||||||
|
l.addWidget(msg)
|
||||||
|
|
||||||
|
self.bb = QDialogButtonBox()
|
||||||
|
disable_driver = QPushButton(_("Disable Apple driver"))
|
||||||
|
disable_driver.setDefault(True)
|
||||||
|
self.bb.addButton(disable_driver, QDialogButtonBox.RejectRole)
|
||||||
|
|
||||||
|
enable_driver = QPushButton(_("Enable Apple driver"))
|
||||||
|
self.bb.addButton(enable_driver, QDialogButtonBox.AcceptRole)
|
||||||
l.addWidget(self.bb)
|
l.addWidget(self.bb)
|
||||||
self.bb.accepted.connect(self.accept)
|
self.bb.accepted.connect(self.accept)
|
||||||
self.bb.rejected.connect(self.reject)
|
self.bb.rejected.connect(self.reject)
|
||||||
|
|
||||||
return Dialog(parent)
|
self.setWindowIcon(QIcon(I(pixmap)))
|
||||||
|
self.resize(self.sizeHint())
|
||||||
|
|
||||||
|
self.finished.connect(self.do_it)
|
||||||
|
|
||||||
|
def do_it(self, return_code):
|
||||||
|
if return_code == self.Accepted:
|
||||||
|
self.cd.log.info(" Apple driver ENABLED")
|
||||||
|
dynamic[confirm_config_name(self.cd.plugin.DISPLAY_DISABLE_DIALOG)] = False
|
||||||
|
else:
|
||||||
|
from calibre.customize.ui import disable_plugin
|
||||||
|
self.cd.log.info(" Apple driver DISABLED")
|
||||||
|
disable_plugin(self.cd.plugin)
|
||||||
|
|
||||||
|
return Dialog(parent, self)
|
||||||
|
|
||||||
|
|
||||||
from PIL import Image as PILImage
|
from PIL import Image as PILImage
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
@ -77,15 +119,11 @@ class DriverBase(DeviceConfig, DevicePlugin):
|
|||||||
'iBooks Category'),
|
'iBooks Category'),
|
||||||
_('Cache covers from iTunes/iBooks') +
|
_('Cache covers from iTunes/iBooks') +
|
||||||
':::' +
|
':::' +
|
||||||
_('Enable to cache and display covers from iTunes/iBooks'),
|
_('Enable to cache and display covers from iTunes/iBooks')
|
||||||
_("Skip 'Connect to iTunes' recommendation") +
|
|
||||||
':::' +
|
|
||||||
_("Enable to skip the 'Connect to iTunes' recommendation dialog")
|
|
||||||
]
|
]
|
||||||
EXTRA_CUSTOMIZATION_DEFAULT = [
|
EXTRA_CUSTOMIZATION_DEFAULT = [
|
||||||
True,
|
True,
|
||||||
True,
|
True,
|
||||||
False,
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -141,12 +179,13 @@ class ITUNES(DriverBase):
|
|||||||
supported_platforms = ['osx','windows']
|
supported_platforms = ['osx','windows']
|
||||||
author = 'GRiker'
|
author = 'GRiker'
|
||||||
#: 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 = (0,9,0)
|
version = (1,0,0)
|
||||||
|
|
||||||
|
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
|
||||||
CACHE_COVERS = 1
|
CACHE_COVERS = 1
|
||||||
SKIP_CONNECT_TO_ITUNES_DIALOG = 2
|
|
||||||
|
|
||||||
OPEN_FEEDBACK_MESSAGE = _(
|
OPEN_FEEDBACK_MESSAGE = _(
|
||||||
'Apple device detected, launching iTunes, please wait ...')
|
'Apple device detected, launching iTunes, please wait ...')
|
||||||
@ -762,15 +801,17 @@ class ITUNES(DriverBase):
|
|||||||
Note that most of the initialization is necessarily performed in can_handle(), as
|
Note that most of the initialization is necessarily performed in can_handle(), as
|
||||||
we need to talk to iTunes to discover if there's a connected iPod
|
we need to talk to iTunes to discover if there's a connected iPod
|
||||||
'''
|
'''
|
||||||
|
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info("ITUNES.open()")
|
self.log.info("ITUNES.open()")
|
||||||
|
|
||||||
# Display a dialog recommending using 'Connect to iTunes'
|
# Display a dialog recommending using 'Connect to iTunes' if user hasn't
|
||||||
if False and not self.settings().extra_customization[self.SKIP_CONNECT_TO_ITUNES_DIALOG]:
|
# previously disabled the dialog
|
||||||
raise AppleOpenFeedback()
|
if dynamic.get(confirm_config_name(self.DISPLAY_DISABLE_DIALOG),True):
|
||||||
|
raise AppleOpenFeedback(self)
|
||||||
if DEBUG:
|
else:
|
||||||
self.log.info(" advanced user mode, directly connecting to iDevice")
|
if DEBUG:
|
||||||
|
self.log.info(" advanced user mode, directly connecting to iDevice")
|
||||||
|
|
||||||
# Confirm/create thumbs archive
|
# Confirm/create thumbs archive
|
||||||
if not os.path.exists(self.cache_dir):
|
if not os.path.exists(self.cache_dir):
|
||||||
|
@ -43,7 +43,7 @@ class OpenFeedback(DeviceError):
|
|||||||
|
|
||||||
def custom_dialog(self, parent):
|
def custom_dialog(self, parent):
|
||||||
'''
|
'''
|
||||||
If you need to show the user a custom dialog, instead if just
|
If you need to show the user a custom dialog, instead of just
|
||||||
displaying the feedback_msg, create and return it here.
|
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()
|
||||||
|
@ -79,7 +79,7 @@ def identify(log, abort, title=None, authors=None, identifiers=[], timeout=30):
|
|||||||
time.sleep(0.2)
|
time.sleep(0.2)
|
||||||
|
|
||||||
if get_results() and first_result_at is None:
|
if get_results() and first_result_at is None:
|
||||||
first_result_at = time.time()
|
first_result_at = time.time()
|
||||||
|
|
||||||
if not is_worker_alive(workers):
|
if not is_worker_alive(workers):
|
||||||
break
|
break
|
||||||
|
@ -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):
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
@ -3,12 +3,11 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
from calibre.gui2 import dynamic
|
|
||||||
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):
|
from calibre import confirm_config_name
|
||||||
return name + '_again'
|
from calibre.gui2 import dynamic
|
||||||
|
from calibre.gui2.dialogs.confirm_delete_ui import Ui_Dialog
|
||||||
|
|
||||||
class Dialog(QDialog, Ui_Dialog):
|
class Dialog(QDialog, Ui_Dialog):
|
||||||
|
|
||||||
@ -22,11 +21,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[confirm_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(confirm_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)))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user