Merge upstream changes

This commit is contained in:
Marshall T. Vandegrift 2009-01-26 08:26:30 -05:00
commit 3b5b9f1849
8 changed files with 86 additions and 28 deletions

View File

@ -3,7 +3,7 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>' __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
''' Create an OSX installer ''' ''' Create an OSX installer '''
import sys, re, os, shutil, subprocess, stat, glob, zipfile import sys, re, os, shutil, subprocess, stat, glob, zipfile, plistlib
l = {} l = {}
exec open('setup.py').read() in l exec open('setup.py').read() in l
VERSION = l['VERSION'] VERSION = l['VERSION']
@ -36,7 +36,7 @@ loader = open(loader_path, 'w')
site_packages = glob.glob(resources_dir+'/lib/python*/site-packages.zip')[0] site_packages = glob.glob(resources_dir+'/lib/python*/site-packages.zip')[0]
print >>loader, '#!'+python print >>loader, '#!'+python
print >>loader, 'import sys' print >>loader, 'import sys'
print >>loader, 'sys.path.remove('+repr(dirpath)+')' print >>loader, 'if', repr(dirpath), 'in sys.path: sys.path.remove(', repr(dirpath), ')'
print >>loader, 'sys.path.append(', repr(site_packages), ')' print >>loader, 'sys.path.append(', repr(site_packages), ')'
print >>loader, 'sys.frozen = "macosx_app"' print >>loader, 'sys.frozen = "macosx_app"'
print >>loader, 'sys.frameworks_dir =', repr(frameworks_dir) print >>loader, 'sys.frameworks_dir =', repr(frameworks_dir)
@ -294,10 +294,25 @@ sys.frameworks_dir = os.path.join(os.path.dirname(os.environ['RESOURCEPATH']), '
f.close() f.close()
print print
print 'Adding main scripts to site-packages' print 'Adding main scripts to site-packages'
f = zipfile.ZipFile(os.path.join(self.dist_dir, APPNAME+'.app', 'Contents', 'Resources', 'lib', 'python2.6', 'site-packages.zip'), 'a', zipfile.ZIP_DEFLATED) f = zipfile.ZipFile(os.path.join(self.dist_dir, APPNAME+'.app', 'Contents', 'Resources', 'lib', 'python'+sys.version[:3], 'site-packages.zip'), 'a', zipfile.ZIP_DEFLATED)
for script in scripts['gui']+scripts['console']: for script in scripts['gui']+scripts['console']:
f.write(script, script.partition('/')[-1]) f.write(script, script.partition('/')[-1])
f.close() f.close()
print
print 'Creating console.app'
contents_dir = os.path.dirname(resource_dir)
cc_dir = os.path.join(contents_dir, 'console.app', 'Contents')
os.makedirs(cc_dir)
for x in os.listdir(contents_dir):
if x == 'console.app':
continue
if x == 'Info.plist':
plist = plistlib.readPlist(os.path.join(contents_dir, x))
plist['LSUIElement'] = '1'
plistlib.writePlist(plist, os.path.join(cc_dir, x))
else:
os.symlink(os.path.join('../..', x),
os.path.join(cc_dir, x))
print print
print 'Building disk image' print 'Building disk image'
BuildAPP.makedmg(os.path.join(self.dist_dir, APPNAME+'.app'), APPNAME+'-'+VERSION) BuildAPP.makedmg(os.path.join(self.dist_dir, APPNAME+'.app'), APPNAME+'-'+VERSION)

View File

@ -21,6 +21,7 @@ Run an embedded python interpreter.
'Module specifications are of the form full.name.of.module,path_to_module.py', default=None 'Module specifications are of the form full.name.of.module,path_to_module.py', default=None
) )
parser.add_option('-c', '--command', help='Run python code.', default=None) parser.add_option('-c', '--command', help='Run python code.', default=None)
parser.add_option('-e', '--exec-file', default=None, help='Run the python code in file.')
parser.add_option('-g', '--gui', default=False, action='store_true', parser.add_option('-g', '--gui', default=False, action='store_true',
help='Run the GUI',) help='Run the GUI',)
parser.add_option('--migrate', action='store_true', default=False, parser.add_option('--migrate', action='store_true', default=False,
@ -87,6 +88,9 @@ def main(args=sys.argv):
elif opts.command: elif opts.command:
sys.argv = args[:1] sys.argv = args[:1]
exec opts.command exec opts.command
elif opts.exec_file:
sys.argv = args[:1]
execfile(opts.exec_file)
elif opts.migrate: elif opts.migrate:
if len(args) < 3: if len(args) < 3:
print 'You must specify the path to library1.db and the path to the new library folder' print 'You must specify the path to library1.db and the path to the new library folder'

View File

@ -193,6 +193,8 @@ class HTMLProcessor(Processor, Rationalizer):
for tag in self.root.xpath('//script'): for tag in self.root.xpath('//script'):
if not tag.text and not tag.get('src', False): if not tag.text and not tag.get('src', False):
tag.getparent().remove(tag) tag.getparent().remove(tag)
def save(self): def save(self):
for meta in list(self.root.xpath('//meta')): for meta in list(self.root.xpath('//meta')):

View File

@ -417,39 +417,44 @@ class Parser(PreProcessor, LoggingInterface):
self.level = self.htmlfile.level self.level = self.htmlfile.level
for f in self.htmlfiles: for f in self.htmlfiles:
name = os.path.basename(f.path) name = os.path.basename(f.path)
name = os.path.splitext(name)[0] + '.xhtml'
if name in self.htmlfile_map.values(): if name in self.htmlfile_map.values():
name = os.path.splitext(name)[0] + '_cr_%d'%save_counter + os.path.splitext(name)[1] name = os.path.splitext(name)[0] + '_cr_%d'%save_counter + os.path.splitext(name)[1]
save_counter += 1 save_counter += 1
self.htmlfile_map[f.path] = name self.htmlfile_map[f.path] = name
self.parse_html() self.parse_html()
# Handle <image> tags inside embedded <svg>
# At least one source of EPUB files (Penguin) uses xlink:href
# without declaring the xlink namespace
for image in self.root.xpath('//image'):
for attr in image.attrib.keys():
if attr.endswith(':href'):
nhref = self.rewrite_links(image.get(attr))
image.set(attr, nhref)
self.root.rewrite_links(self.rewrite_links, resolve_base_href=False) self.root.rewrite_links(self.rewrite_links, resolve_base_href=False)
for bad in ('xmlns', 'lang', 'xml:lang'): # lxml also adds these attributes for XHTML documents, leading to duplicates for bad in ('xmlns', 'lang', 'xml:lang'): # lxml also adds these attributes for XHTML documents, leading to duplicates
if self.root.get(bad, None) is not None: if self.root.get(bad, None) is not None:
self.root.attrib.pop(bad) self.root.attrib.pop(bad)
def save_path(self): def save_path(self):
return os.path.join(self.tdir, self.htmlfile_map[self.htmlfile.path]) return os.path.join(self.tdir, self.htmlfile_map[self.htmlfile.path])
def declare_xhtml_namespace(self, match):
if not match.group('raw'):
return '<html xmlns="http://www.w3.org/1999/xhtml">'
raw = match.group('raw')
m = re.search(r'(?i)xmlns\s*=\s*[\'"](?P<uri>[^"\']*)[\'"]', raw)
if not m:
return '<html xmlns="http://www.w3.org/1999/xhtml" %s>'%raw
else:
return match.group().replace(m.group('uri'), "http://www.w3.org/1999/xhtml")
def save(self): def save(self):
''' '''
Save processed HTML into the content directory. Save processed HTML into the content directory.
Should be called after all HTML processing is finished. Should be called after all HTML processing is finished.
''' '''
self.root.set('xmlns', 'http://www.w3.org/1999/xhtml')
self.root.set('xmlns:xlink', 'http://www.w3.org/1999/xlink')
for svg in self.root.xpath('//svg'):
svg.set('xmlns', 'http://www.w3.org/2000/svg')
ans = tostring(self.root, pretty_print=self.opts.pretty_print) ans = tostring(self.root, pretty_print=self.opts.pretty_print)
ans = re.sub(r'(?i)<\s*html(?P<raw>\s+[^>]*){0,1}>', self.declare_xhtml_namespace, ans[:1000]) + ans[1000:]
ans = re.compile(r'<head>', re.IGNORECASE).sub('<head>\n\t<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />\n', ans[:1000])+ans[1000:] ans = re.compile(r'<head>', re.IGNORECASE).sub('<head>\n\t<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />\n', ans[:1000])+ans[1000:]
with open(self.save_path(), 'wb') as f: with open(self.save_path(), 'wb') as f:
f.write(ans) f.write(ans)
return f.name return f.name

View File

@ -46,9 +46,10 @@ class SVGRasterizer(object):
data = QByteArray(xml2str(elem)) data = QByteArray(xml2str(elem))
svg = QSvgRenderer(data) svg = QSvgRenderer(data)
size = svg.defaultSize() size = svg.defaultSize()
view_box = elem.get('viewBox', elem.get('viewbox', None))
if size.width() == 100 and size.height() == 100 \ if size.width() == 100 and size.height() == 100 \
and 'viewBox' in elem.attrib: and view_box is not None:
box = [float(x) for x in elem.attrib['viewBox'].split()] box = [float(x) for x in view_box.split()]
size.setWidth(box[2] - box[0]) size.setWidth(box[2] - box[0])
size.setHeight(box[3] - box[1]) size.setHeight(box[3] - box[1])
if width or height: if width or height:

View File

@ -63,7 +63,8 @@ class Main(MainWindow, Ui_MainWindow):
p.end() p.end()
self.default_thumbnail = (pixmap.width(), pixmap.height(), pixmap_to_data(pixmap)) self.default_thumbnail = (pixmap.width(), pixmap.height(), pixmap_to_data(pixmap))
def __init__(self, single_instance, opts, parent=None): def __init__(self, single_instance, opts, actions, parent=None):
self.preferences_action, self.quit_action = actions
MainWindow.__init__(self, opts, parent) MainWindow.__init__(self, opts, parent)
# Initialize fontconfig in a separate thread as this can be a lengthy # Initialize fontconfig in a separate thread as this can be a lengthy
# process if run for the first time on this machine # process if run for the first time on this machine
@ -100,7 +101,6 @@ class Main(MainWindow, Ui_MainWindow):
self.system_tray_menu = QMenu() self.system_tray_menu = QMenu()
self.restore_action = self.system_tray_menu.addAction(QIcon(':/images/page.svg'), _('&Restore')) self.restore_action = self.system_tray_menu.addAction(QIcon(':/images/page.svg'), _('&Restore'))
self.donate_action = self.system_tray_menu.addAction(QIcon(':/images/donate.svg'), _('&Donate')) self.donate_action = self.system_tray_menu.addAction(QIcon(':/images/donate.svg'), _('&Donate'))
self.quit_action = QAction(QIcon(':/images/window-close.svg'), _('&Quit'), self)
self.addAction(self.quit_action) self.addAction(self.quit_action)
self.action_restart = QAction(_('&Restart'), self) self.action_restart = QAction(_('&Restart'), self)
self.addAction(self.action_restart) self.addAction(self.action_restart)
@ -242,6 +242,7 @@ class Main(MainWindow, Ui_MainWindow):
self.tool_bar.setContextMenuPolicy(Qt.PreventContextMenu) self.tool_bar.setContextMenuPolicy(Qt.PreventContextMenu)
QObject.connect(self.config_button, SIGNAL('clicked(bool)'), self.do_config) QObject.connect(self.config_button, SIGNAL('clicked(bool)'), self.do_config)
self.connect(self.preferences_action, SIGNAL('triggered(bool)'), self.do_config)
QObject.connect(self.advanced_search_button, SIGNAL('clicked(bool)'), self.do_advanced_search) QObject.connect(self.advanced_search_button, SIGNAL('clicked(bool)'), self.do_advanced_search)
####################### Library view ######################## ####################### Library view ########################
@ -1547,6 +1548,7 @@ def main(args=sys.argv):
prefs.set('library_path', opts.with_library) prefs.set('library_path', opts.with_library)
print 'Using library at', prefs['library_path'] print 'Using library at', prefs['library_path']
app = Application(args) app = Application(args)
actions = tuple(Main.create_application_menubar())
app.setWindowIcon(QIcon(':/library')) app.setWindowIcon(QIcon(':/library'))
QCoreApplication.setOrganizationName(ORG_NAME) QCoreApplication.setOrganizationName(ORG_NAME)
QCoreApplication.setApplicationName(APP_UID) QCoreApplication.setApplicationName(APP_UID)
@ -1561,7 +1563,7 @@ def main(args=sys.argv):
'<p>%s is already running. %s</p>'%(__appname__, extra)) '<p>%s is already running. %s</p>'%(__appname__, extra))
return 1 return 1
initialize_file_icon_provider() initialize_file_icon_provider()
main = Main(single_instance, opts) main = Main(single_instance, opts, actions)
sys.excepthook = main.unhandled_exception sys.excepthook = main.unhandled_exception
if len(args) > 1: if len(args) > 1:
main.add_filesystem_book(args[1]) main.add_filesystem_book(args[1])

View File

@ -3,7 +3,8 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import StringIO, traceback, sys import StringIO, traceback, sys
from PyQt4.Qt import QMainWindow, QString, Qt, QFont, QCoreApplication, SIGNAL from PyQt4.Qt import QMainWindow, QString, Qt, QFont, QCoreApplication, SIGNAL,\
QAction, QMenu, QMenuBar, QIcon
from calibre.gui2.dialogs.conversion_error import ConversionErrorDialog from calibre.gui2.dialogs.conversion_error import ConversionErrorDialog
from calibre.utils.config import OptionParser from calibre.utils.config import OptionParser
@ -33,6 +34,27 @@ class DebugWindow(ConversionErrorDialog):
pass pass
class MainWindow(QMainWindow): class MainWindow(QMainWindow):
_menu_bar = None
@classmethod
def create_application_menubar(cls):
mb = QMenuBar(None)
menu = QMenu()
for action in cls.get_menubar_actions():
menu.addAction(action)
yield action
mb.addMenu(menu)
cls._menu_bar = mb
@classmethod
def get_menubar_actions(cls):
preferences_action = QAction(QIcon(':/images/config.svg'), _('&Preferences'), None)
quit_action = QAction(QIcon(':/images/window-close.svg'), _('&Quit'), None)
preferences_action.setMenuRole(QAction.PreferencesRole)
quit_action.setMenuRole(QAction.QuitRole)
return preferences_action, quit_action
def __init__(self, opts, parent=None): def __init__(self, opts, parent=None):
QMainWindow.__init__(self, parent) QMainWindow.__init__(self, parent)
@ -58,4 +80,4 @@ class MainWindow(QMainWindow):
d = ConversionErrorDialog(self, _('ERROR: Unhandled exception'), msg) d = ConversionErrorDialog(self, _('ERROR: Unhandled exception'), msg)
d.exec_() d.exec_()
except: except:
pass pass

View File

@ -158,21 +158,27 @@ class WorkerMother(object):
self.executable = os.path.join(os.path.dirname(sys.executable), self.executable = os.path.join(os.path.dirname(sys.executable),
'calibre-parallel.exe' if isfrozen else 'Scripts\\calibre-parallel.exe') 'calibre-parallel.exe' if isfrozen else 'Scripts\\calibre-parallel.exe')
elif isosx: elif isosx:
self.executable = sys.executable self.executable = self.gui_executable = sys.executable
self.prefix = '' self.prefix = ''
if isfrozen: if isfrozen:
fd = getattr(sys, 'frameworks_dir') fd = getattr(sys, 'frameworks_dir')
contents = os.path.dirname(fd) contents = os.path.dirname(fd)
self.gui_executable = os.path.join(contents, 'MacOS',
os.path.basename(sys.executable))
contents = os.path.join(contents, 'console.app', 'Contents')
self.executable = os.path.join(contents, 'MacOS',
os.path.basename(sys.executable))
resources = os.path.join(contents, 'Resources') resources = os.path.join(contents, 'Resources')
fd = os.path.join(contents, 'Frameworks')
sp = os.path.join(resources, 'lib', 'python'+sys.version[:3], 'site-packages.zip') sp = os.path.join(resources, 'lib', 'python'+sys.version[:3], 'site-packages.zip')
self.prefix += 'import sys; sys.frameworks_dir = "%s"; sys.frozen = "macosx_app"; '%fd self.prefix += 'import sys; sys.frameworks_dir = "%s"; sys.frozen = "macosx_app"; '%fd
self.prefix += 'sys.path.insert(0, %s); '%repr(sp) self.prefix += 'sys.path.insert(0, %s); '%repr(sp)
if fd not in os.environ['PATH']: if fd not in os.environ['PATH']:
self.env['PATH'] = os.environ['PATH']+':'+fd self.env['PATH'] = os.environ['PATH']+':'+fd
self.env['PYTHONHOME'] = resources self.env['PYTHONHOME'] = resources
self.env['MAGICK_HOME'] = os.path.join(getattr(sys, 'frameworks_dir'), 'ImageMagick') self.env['MAGICK_HOME'] = os.path.join(fd, 'ImageMagick')
self.env['DYLD_LIBRARY_PATH'] = os.path.join(getattr(sys, 'frameworks_dir'), 'ImageMagick', 'lib') self.env['DYLD_LIBRARY_PATH'] = os.path.join(fd, 'ImageMagick', 'lib')
else: else:
self.executable = os.path.join(getattr(sys, 'frozen_path'), 'calibre-parallel') \ self.executable = os.path.join(getattr(sys, 'frozen_path'), 'calibre-parallel') \
if isfrozen else 'calibre-parallel' if isfrozen else 'calibre-parallel'
@ -186,7 +192,7 @@ class WorkerMother(object):
for func in ('spawn_free_spirit', 'spawn_worker'): for func in ('spawn_free_spirit', 'spawn_worker'):
setattr(self, func, getattr(self, func+'_'+ext)) setattr(self, func, getattr(self, func+'_'+ext))
def cleanup_child_windows(self, child, name=None, fd=None): def cleanup_child_windows(self, child, name=None, fd=None):
try: try:
child.kill() child.kill()
@ -219,7 +225,8 @@ class WorkerMother(object):
def spawn_free_spirit_osx(self, arg, type='free_spirit'): def spawn_free_spirit_osx(self, arg, type='free_spirit'):
script = 'from calibre.parallel import main; main(args=["calibre-parallel", %s]);'%repr(arg) script = 'from calibre.parallel import main; main(args=["calibre-parallel", %s]);'%repr(arg)
cmdline = [self.executable, '-c', self.prefix+script] exe = self.gui_executable if type == 'free_spirit' else self.executable
cmdline = [exe, '-c', self.prefix+script]
child = WorkerStatus(subprocess.Popen(cmdline, env=self.get_env())) child = WorkerStatus(subprocess.Popen(cmdline, env=self.get_env()))
atexit.register(self.cleanup_child_linux, child) atexit.register(self.cleanup_child_linux, child)
return child return child