diff --git a/installer/osx/freeze.py b/installer/osx/freeze.py index de93a344c2..3ec24d3aba 100644 --- a/installer/osx/freeze.py +++ b/installer/osx/freeze.py @@ -3,7 +3,7 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' ''' Create an OSX installer ''' -import sys, re, os, shutil, subprocess, stat, glob, zipfile +import sys, re, os, shutil, subprocess, stat, glob, zipfile, plistlib l = {} exec open('setup.py').read() in l VERSION = l['VERSION'] @@ -36,7 +36,7 @@ loader = open(loader_path, 'w') site_packages = glob.glob(resources_dir+'/lib/python*/site-packages.zip')[0] print >>loader, '#!'+python 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.frozen = "macosx_app"' 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() print 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']: f.write(script, script.partition('/')[-1]) 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 'Building disk image' BuildAPP.makedmg(os.path.join(self.dist_dir, APPNAME+'.app'), APPNAME+'-'+VERSION) diff --git a/src/calibre/debug.py b/src/calibre/debug.py index 81336953e1..54eadb6b65 100644 --- a/src/calibre/debug.py +++ b/src/calibre/debug.py @@ -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 ) 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', help='Run the GUI',) parser.add_option('--migrate', action='store_true', default=False, @@ -87,6 +88,9 @@ def main(args=sys.argv): elif opts.command: sys.argv = args[:1] exec opts.command + elif opts.exec_file: + sys.argv = args[:1] + execfile(opts.exec_file) elif opts.migrate: if len(args) < 3: print 'You must specify the path to library1.db and the path to the new library folder' diff --git a/src/calibre/ebooks/epub/from_html.py b/src/calibre/ebooks/epub/from_html.py index c358471f09..458fca152c 100644 --- a/src/calibre/ebooks/epub/from_html.py +++ b/src/calibre/ebooks/epub/from_html.py @@ -193,6 +193,8 @@ class HTMLProcessor(Processor, Rationalizer): for tag in self.root.xpath('//script'): if not tag.text and not tag.get('src', False): tag.getparent().remove(tag) + + def save(self): for meta in list(self.root.xpath('//meta')): diff --git a/src/calibre/ebooks/html.py b/src/calibre/ebooks/html.py index f6659f9f51..32601320d4 100644 --- a/src/calibre/ebooks/html.py +++ b/src/calibre/ebooks/html.py @@ -417,39 +417,44 @@ class Parser(PreProcessor, LoggingInterface): self.level = self.htmlfile.level for f in self.htmlfiles: name = os.path.basename(f.path) + name = os.path.splitext(name)[0] + '.xhtml' if name in self.htmlfile_map.values(): name = os.path.splitext(name)[0] + '_cr_%d'%save_counter + os.path.splitext(name)[1] save_counter += 1 self.htmlfile_map[f.path] = name self.parse_html() + # Handle tags inside embedded + # 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) 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: self.root.attrib.pop(bad) + + def save_path(self): return os.path.join(self.tdir, self.htmlfile_map[self.htmlfile.path]) - def declare_xhtml_namespace(self, match): - if not match.group('raw'): - return '' - raw = match.group('raw') - m = re.search(r'(?i)xmlns\s*=\s*[\'"](?P[^"\']*)[\'"]', raw) - if not m: - return ''%raw - else: - return match.group().replace(m.group('uri'), "http://www.w3.org/1999/xhtml") - def save(self): ''' Save processed HTML into the content directory. 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 = re.sub(r'(?i)<\s*html(?P\s+[^>]*){0,1}>', self.declare_xhtml_namespace, ans[:1000]) + ans[1000:] ans = re.compile(r'', re.IGNORECASE).sub('\n\t\n', ans[:1000])+ans[1000:] - with open(self.save_path(), 'wb') as f: f.write(ans) return f.name diff --git a/src/calibre/ebooks/oeb/transforms/rasterize.py b/src/calibre/ebooks/oeb/transforms/rasterize.py index 69f1d0d133..97d73d3dcb 100644 --- a/src/calibre/ebooks/oeb/transforms/rasterize.py +++ b/src/calibre/ebooks/oeb/transforms/rasterize.py @@ -46,9 +46,10 @@ class SVGRasterizer(object): data = QByteArray(xml2str(elem)) svg = QSvgRenderer(data) size = svg.defaultSize() + view_box = elem.get('viewBox', elem.get('viewbox', None)) if size.width() == 100 and size.height() == 100 \ - and 'viewBox' in elem.attrib: - box = [float(x) for x in elem.attrib['viewBox'].split()] + and view_box is not None: + box = [float(x) for x in view_box.split()] size.setWidth(box[2] - box[0]) size.setHeight(box[3] - box[1]) if width or height: diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index d3538d79b3..1118d04dab 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -63,7 +63,8 @@ class Main(MainWindow, Ui_MainWindow): p.end() 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) # Initialize fontconfig in a separate thread as this can be a lengthy # 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.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.quit_action = QAction(QIcon(':/images/window-close.svg'), _('&Quit'), self) self.addAction(self.quit_action) self.action_restart = QAction(_('&Restart'), self) self.addAction(self.action_restart) @@ -242,6 +242,7 @@ class Main(MainWindow, Ui_MainWindow): self.tool_bar.setContextMenuPolicy(Qt.PreventContextMenu) 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) ####################### Library view ######################## @@ -1547,6 +1548,7 @@ def main(args=sys.argv): prefs.set('library_path', opts.with_library) print 'Using library at', prefs['library_path'] app = Application(args) + actions = tuple(Main.create_application_menubar()) app.setWindowIcon(QIcon(':/library')) QCoreApplication.setOrganizationName(ORG_NAME) QCoreApplication.setApplicationName(APP_UID) @@ -1561,7 +1563,7 @@ def main(args=sys.argv): '

%s is already running. %s

'%(__appname__, extra)) return 1 initialize_file_icon_provider() - main = Main(single_instance, opts) + main = Main(single_instance, opts, actions) sys.excepthook = main.unhandled_exception if len(args) > 1: main.add_filesystem_book(args[1]) diff --git a/src/calibre/gui2/main_window.py b/src/calibre/gui2/main_window.py index db9cdd389d..95d77fc05c 100644 --- a/src/calibre/gui2/main_window.py +++ b/src/calibre/gui2/main_window.py @@ -3,7 +3,8 @@ __copyright__ = '2008, Kovid Goyal ' 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.utils.config import OptionParser @@ -33,6 +34,27 @@ class DebugWindow(ConversionErrorDialog): pass 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): QMainWindow.__init__(self, parent) @@ -58,4 +80,4 @@ class MainWindow(QMainWindow): d = ConversionErrorDialog(self, _('ERROR: Unhandled exception'), msg) d.exec_() except: - pass \ No newline at end of file + pass diff --git a/src/calibre/parallel.py b/src/calibre/parallel.py index fa9284ce46..a1a439d579 100644 --- a/src/calibre/parallel.py +++ b/src/calibre/parallel.py @@ -158,21 +158,27 @@ class WorkerMother(object): self.executable = os.path.join(os.path.dirname(sys.executable), 'calibre-parallel.exe' if isfrozen else 'Scripts\\calibre-parallel.exe') elif isosx: - self.executable = sys.executable + self.executable = self.gui_executable = sys.executable self.prefix = '' if isfrozen: fd = getattr(sys, 'frameworks_dir') 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') + fd = os.path.join(contents, 'Frameworks') 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 += 'sys.path.insert(0, %s); '%repr(sp) if fd not in os.environ['PATH']: self.env['PATH'] = os.environ['PATH']+':'+fd self.env['PYTHONHOME'] = resources - self.env['MAGICK_HOME'] = os.path.join(getattr(sys, 'frameworks_dir'), 'ImageMagick') - self.env['DYLD_LIBRARY_PATH'] = os.path.join(getattr(sys, 'frameworks_dir'), 'ImageMagick', 'lib') + self.env['MAGICK_HOME'] = os.path.join(fd, 'ImageMagick') + self.env['DYLD_LIBRARY_PATH'] = os.path.join(fd, 'ImageMagick', 'lib') else: self.executable = os.path.join(getattr(sys, 'frozen_path'), 'calibre-parallel') \ if isfrozen else 'calibre-parallel' @@ -186,7 +192,7 @@ class WorkerMother(object): for func in ('spawn_free_spirit', 'spawn_worker'): setattr(self, func, getattr(self, func+'_'+ext)) - + def cleanup_child_windows(self, child, name=None, fd=None): try: child.kill() @@ -219,7 +225,8 @@ class WorkerMother(object): def spawn_free_spirit_osx(self, arg, type='free_spirit'): 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())) atexit.register(self.cleanup_child_linux, child) return child