diff --git a/resources/images/devices/nook.jpg b/resources/images/devices/nook.jpg
new file mode 100644
index 0000000000..1ca38d8119
Binary files /dev/null and b/resources/images/devices/nook.jpg differ
diff --git a/resources/recipes/strategy-business.recipe b/resources/recipes/strategy-business.recipe
new file mode 100644
index 0000000000..1ea85d6f21
--- /dev/null
+++ b/resources/recipes/strategy-business.recipe
@@ -0,0 +1,64 @@
+from calibre.web.feeds.news import BasicNewsRecipe
+
+class StrategyBusinessRecipe(BasicNewsRecipe):
+ __license__ = 'GPL v3'
+ __author__ = 'kwetal'
+ language = 'en'
+ version = 1
+
+ title = u'Strategy+Business'
+ publisher = u' Booz & Company'
+ category = u'Business'
+ description = u'Business magazine for senior business executives and the people who influence them.'
+
+ oldest_article = 13 * 7 # 3 months
+ max_articles_per_feed = 100
+ use_embedded_content = False
+ remove_empty_feeds = True
+
+ no_stylesheets = True
+ remove_javascript = True
+
+ extra_css = '''
+ body{font-family:verdana,arial,helvetica,geneva,sans-serif ;}
+ a {text-decoration: none; color: blue;}
+ h1 {margin: 0em; padding: 0em;}
+ h2 {font-size: medium; font-weight: bold;}
+ #sb-date {font-size: xx-small; color: #696969}
+ #category {font-style: italic; font-size: small; color: black; margin: 0em; padding: 0em;}
+ #byline {font-size: small; color: #666666}
+ div.profiles {font-size: small; font-style: italic; color: #696969}
+ div.profiles h2 {font-size: medium; font-style: normal; font-weight: bold; color: black}
+ '''
+
+ feeds = []
+ feeds.append((u'Finance', u'http://feeds.feedburner.com/StrategyBusiness-Finance?format=xml'))
+ feeds.append((u'Global Perspective', u'http://feeds.feedburner.com/StrategyBusiness-GlobalPerspective?format=xml'))
+ feeds.append((u'Innovation', u'http://feeds.feedburner.com/StrategyBusiness-Innovation?format=xml'))
+ feeds.append((u'Marketing And Sales', u'http://feeds.feedburner.com/StrategyBusiness-MarketingAndSales?format=xml'))
+ feeds.append((u'Operations And Manufacturing', u'http://feeds.feedburner.com/StrategyBusiness-OperationsAndManufacturing?format=xml'))
+ feeds.append((u'Organizations And People', u'http://feeds.feedburner.com/StrategyBusiness-OrganizationsAndPeople?format=xml'))
+ feeds.append((u'Strategy And Leadership', u'http://feeds.feedburner.com/StrategyBusiness-StrategyAndLeadership?format=xml'))
+ feeds.append((u'Sustainability', u'http://feeds.feedburner.com/StrategyBusiness-Sustainability?format=xml'))
+ feeds.append((u'Auto, Airlines And Transport', u'http://feeds.feedburner.com/StrategyBusiness-AutoAirlinesAndTransport?format=xml'))
+ feeds.append((u'Consumer Products', u'http://feeds.feedburner.com/StrategyBusiness-ConsumerProducts?format=xml'))
+ feeds.append((u'Energy', u'http://feeds.feedburner.com/StrategyBusiness-Energy?format=xml'))
+ feeds.append((u'Health Care', u'http://feeds.feedburner.com/StrategyBusiness-HealthCare?format=xml'))
+ feeds.append((u'Technology', u'http://feeds.feedburner.com/StrategyBusiness-Technology?format=xml'))
+ feeds.append((u'Thought Leaders', u'http://feeds.feedburner.com/StrategyBusiness-ThoughtLeaders?format=xml'))
+ feeds.append((u'Business Literature', u'http://feeds.feedburner.com/StrategyBusiness-BusinessLiterature?format=xml'))
+ feeds.append((u'Recent Research', u'http://feeds.feedburner.com/StrategyBusiness-RecentResearch?format=xml'))
+
+
+ keep_only_tags = []
+ keep_only_tags.append(dict(name = 'div', attrs = {'id': 'sb-column2'}))
+
+ remove_tags = []
+ remove_tags.append(dict(name = 'img', attrs = {'class': 'content1'}))
+ remove_tags.append(dict(name = 'img', attrs = {'src': '/media/image/end_of_story.gif'}))
+ remove_tags.append(dict(name = 'div', attrs = {'class': 'sb-adarea468'}))
+ remove_tags.append(dict(name = 'div', attrs = {'id': 'sb-paging'}))
+ remove_tags.append(dict(name = 'div', attrs = {'id': 'textsize'}))
+
+ def print_version(self, url):
+ return url + '?pg=all'
diff --git a/resources/recipes/watchingamerica.recipe b/resources/recipes/watchingamerica.recipe
new file mode 100644
index 0000000000..9048e2550c
--- /dev/null
+++ b/resources/recipes/watchingamerica.recipe
@@ -0,0 +1,96 @@
+from calibre.web.feeds.news import BasicNewsRecipe
+from calibre.ebooks.BeautifulSoup import BeautifulSoup
+
+class WatchingAmericaRecipe(BasicNewsRecipe):
+ __license__ = 'GPL v3'
+ __author__ = 'kwetal'
+ language = 'en'
+ version = 1
+
+ title = u'Watching America'
+ publisher = u'watchingamerica.com'
+ category = u'News'
+ description = u'Global opinion about the United States'
+
+ oldest_article = 7
+ max_articles_per_feed = 100
+ use_embedded_content = False
+
+ no_stylesheets = True
+ remove_javascript = True
+ remove_attributes = ['style']
+
+ extra_css = '''
+ body{font-family:verdana,arial,helvetica,geneva,sans-serif ;}
+ .main_content em {font-size: x-small; font-style: italic; color: #696969;}
+ .main_content span strong {font-size: x-large; font-weight: bold;}
+ .insideitro {font-size: xx-small; font-style: italic; color: #666666;}
+ span {padding: 0em; margin 0em;}
+ '''
+
+ INDEX = u'http://watchingamerica.com/News/'
+
+ def parse_index(self):
+ answer = []
+
+ soup = self.index_to_soup(self.INDEX)
+
+ articles = []
+ feature = soup.find('div', attrs = {'id': 'headzone'})
+ if feature:
+ link = feature.find('a', attrs = {'class': 'feature'})
+ url = link.get('href', None)
+ title = self.tag_to_string(link)
+ description = self.tag_to_string(feature.find('h1', attrs = {'class': 'pull'}))
+ article = {'title': title, 'date': u'', 'url': url, 'description': description}
+ articles.append(article)
+ answer.append(('Feature', articles))
+
+ feed_titles = ['Translations from the West', 'Translations from the East']
+ for i in range(1, 3):
+ articles = []
+ div = soup.find('div', attrs = {'class': 'newscol' + str(i)})
+ if div:
+ for link in div.findAll('a', attrs = {'class': 'headline'}):
+ url = link.get('href', None)
+ title = self.tag_to_string(link)
+
+ description = None
+ h3 = link.findNextSibling('h3')
+ if h3:
+ description = self.tag_to_string(h3)
+
+ article = {'title': title, 'date': u'', 'url': url, 'description': description}
+ articles.append(article)
+ answer.append((feed_titles[i - 1], articles))
+
+ return answer
+
+ def preprocess_html(self, soup):
+ freshSoup = self.get_fresh_soup(soup)
+ article = soup.find('p', attrs = {'class': 'MsoNormal'}).parent
+ if article:
+ article.name = 'div'
+ del article['width']
+ article['class'] = 'main_content'
+ org = article.find('a', attrs = {'href': '?SHOW_ORIGINAL_TEXT'})
+ if org:
+ org.parent.extract()
+
+ intro = article.find('span', attrs = {'class': 'insideitro'})
+ if intro:
+ for el in intro.findAll(['strong', 'em', 'br']):
+ if el.name == 'br':
+ el.extract()
+ else:
+ el.name = 'div'
+
+ freshSoup.body.append(article)
+
+ return freshSoup
+
+ def get_fresh_soup(self, oldSoup):
+ freshSoup = BeautifulSoup('
')
+ if oldSoup.head.title:
+ freshSoup.head.title.append(self.tag_to_string(oldSoup.head.title))
+ return freshSoup
diff --git a/setup/installer/osx/app/launcher.c b/setup/installer/osx/app/launcher.c
index 47d1c723c2..c36909efde 100644
--- a/setup/installer/osx/app/launcher.c
+++ b/setup/installer/osx/app/launcher.c
@@ -1,138 +1,17 @@
+#include "util.h"
#include
-#include
-#include
-#include
-#include
-static const char *ERR_UNKNOWNPYTHONEXCEPTION = "An uncaught exception was raised during execution of the main script, but its class or name could not be determined";
-
-static int
-report_error(const char *msg) {
- fprintf(stderr, msg);
- fprintf(stderr, "\n");
- fflush(stderr);
- return -1;
-}
-
-// These variable must be filled in before compiling
+// These variables must be filled in before compiling
static const char *ENV_VARS[] = { /*ENV_VARS*/ NULL };
static const char *ENV_VAR_VALS[] = { /*ENV_VAR_VALS*/ NULL};
static char PROGRAM[] = "**PROGRAM**";
static const char MODULE[] = "**MODULE**";
+static const char FUNCTION[] = "**FUNCTION**";
+static const char PYVER[] = "**PYVER**";
-#define EXE "@executable_path/.."
-
-static void
-set_env_vars(const char* exe_path, const char* rpath) {
- int i = 0;
- char buf[3*PATH_MAX];
- const char *env_var, *val;
-
- while(1) {
- env_var = ENV_VARS[i];
- if (env_var == NULL) break;
- val = ENV_VAR_VALS[i++];
- if (strstr(val, EXE) == val && strlen(val) >= strlen(EXE)+1) {
- strncpy(buf, exe_path, 3*PATH_MAX-150);
- strncpy(buf+strlen(exe_path), val+strlen(EXE), 150);
- setenv(env_var, buf, 1);
- } else
- setenv(env_var, val, 1);
- }
- setenv("CALIBRE_LAUNCH_MODULE", MODULE, 1);
- setenv("RESOURCEPATH", rpath, 1);
- return;
-}
int
-main(int argc, char * const *argv, char * const *envp) {
- char *pathPtr = NULL;
- char buf[3*PATH_MAX];
- int ret, i;
-
-
- uint32_t buf_size = PATH_MAX+1;
- char *ebuf = calloc(buf_size, sizeof(char));
- ret = _NSGetExecutablePath(ebuf, &buf_size);
- if (ret == -1) {
- free(ebuf);
- ebuf = calloc(buf_size, sizeof(char));
- if (_NSGetExecutablePath(ebuf, &buf_size) != 0)
- return report_error("Failed to find real path of executable.");
- }
- pathPtr = realpath(ebuf, buf);
- if (pathPtr == NULL) {
- return report_error(strerror(errno));
- }
- char *t;
- for (i = 0; i < 3; i++) {
- t = rindex(pathPtr, '/');
- if (t == NULL) return report_error("Failed to determine bundle path.");
- *t = '\0';
- }
-
-
-
- char rpath[PATH_MAX+1];
- strncpy(rpath, pathPtr, strlen(pathPtr));
- strncat(rpath, "/Contents/Resources", 50);
- char exe_path[PATH_MAX+1];
- strncpy(exe_path, pathPtr, strlen(pathPtr));
- strncat(exe_path, "/Contents", 50);
-
- set_env_vars(exe_path, rpath);
-
- char main_script[PATH_MAX+1];
- strncpy(main_script, rpath, strlen(rpath));
- strncat(main_script, "/launcher.py", 20);
-
- Py_SetProgramName(PROGRAM);
-
- Py_Initialize();
-
- char **argv_new = calloc(argc+1, sizeof(char *));
- argv_new[argc] = NULL;
- argv_new[0] = main_script;
- memcpy(&argv_new[1], &argv[1], (argc - 1) * sizeof(char *));
- PySys_SetArgv(argc, argv_new);
-
- FILE *main_script_file = fopen(main_script, "r");
- int rval = PyRun_SimpleFileEx(main_script_file, main_script, 1);
-
- while (rval != 0) {
- PyObject *exc, *exceptionClassName, *v, *exceptionName;
- exc = PySys_GetObject("last_type");
-
- if ( !exc ) {
- rval = report_error(ERR_UNKNOWNPYTHONEXCEPTION);
- break;
- }
-
- exceptionClassName = PyObject_GetAttrString(exc, "__name__");
- if (!exceptionClassName) {
- rval = report_error(ERR_UNKNOWNPYTHONEXCEPTION);
- break;
- }
-
- v = PySys_GetObject("last_value");
- exceptionName = (v ? PyObject_Str(v) : NULL);
-
- char *class = PyString_AsString(exceptionClassName);
- char *exception = "";
- Py_DecRef(exceptionClassName);
- if (exceptionName) {
- exception = PyString_AsString(exceptionName);
- Py_DecRef(exceptionName);
- }
- char msg[2000];
- strncpy(msg, "An unexpected error occurred: ", 100);
- strncpy(msg, class, 500);
- strncpy(msg, " : ", 3);
- strncpy(msg, exception, 500);
- rval = report_error(msg);
- break;
-
- }
- Py_Finalize();
- return rval;
+main(int argc, const char **argv, const char **envp) {
+ return run(ENV_VARS, ENV_VAR_VALS, PROGRAM, MODULE, FUNCTION, PYVER, argc, argv, envp);
}
+
diff --git a/setup/installer/osx/app/launcher.py b/setup/installer/osx/app/launcher.py
deleted file mode 100644
index 14d304e7ee..0000000000
--- a/setup/installer/osx/app/launcher.py
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/usr/bin/env python
-# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
-
-__license__ = 'GPL v3'
-__copyright__ = '2009, Kovid Goyal '
-__docformat__ = 'restructuredtext en'
-
-def _disable_linecache():
- import linecache
- def fake_getline(*args, **kwargs):
- return ''
- linecache.orig_getline = linecache.getline
- linecache.getline = fake_getline
-_disable_linecache()
-
-def _recipes_pil_prescript(plugins):
- from PIL import Image
- import sys
- def init():
- if Image._initialized >= 2:
- return
- for plugin in plugins:
- try:
- __import__(plugin, globals(), locals(), [])
- except ImportError:
- if Image.DEBUG:
- print 'Image: failed to import'
- print plugin, ':', sys.exc_info()[1]
- if Image.OPEN or Image.SAVE:
- Image._initialized = 2
- Image.init = init
-
-
-_recipes_pil_prescript(['Hdf5StubImagePlugin', 'FitsStubImagePlugin', 'SunImagePlugin', 'GbrImagePlugin', 'PngImagePlugin', 'MicImagePlugin', 'FpxImagePlugin', 'PcxImagePlugin', 'ImImagePlugin', 'SpiderImagePlugin', 'PsdImagePlugin', 'BufrStubImagePlugin', 'SgiImagePlugin', 'McIdasImagePlugin', 'XpmImagePlugin', 'BmpImagePlugin', 'TgaImagePlugin', 'PalmImagePlugin', 'XVThumbImagePlugin', 'GribStubImagePlugin', 'ArgImagePlugin', 'PdfImagePlugin', 'ImtImagePlugin', 'GifImagePlugin', 'CurImagePlugin', 'WmfImagePlugin', 'MpegImagePlugin', 'IcoImagePlugin', 'TiffImagePlugin', 'PpmImagePlugin', 'MspImagePlugin', 'EpsImagePlugin', 'JpegImagePlugin', 'PixarImagePlugin', 'PcdImagePlugin', 'IptcImagePlugin', 'XbmImagePlugin', 'DcxImagePlugin', 'IcnsImagePlugin', 'FliImagePlugin'])
-
-def _run():
- global __file__
- import os, sys
- base = os.environ['RESOURCEPATH']
-
- sys.frozen = 'macosx_app'
- sys.frameworks_dir = os.path.join(os.path.dirname(base), 'Frameworks')
- sys.new_app_bundle = True
- sys.site_packages = os.path.join(base, 'Python', 'site-packages')
- sys.binaries_path = os.path.join(os.path.dirname(base), 'MacOS')
- sys.console_binaries_path = os.path.join(os.path.dirname(base),
- 'console.app', 'Contents', 'MacOS')
-
- exe = os.environ.get('CALIBRE_LAUNCH_MODULE', 'calibre.gui2.main')
- exe = os.path.join(base, 'Python', 'site-packages', *exe.split('.'))
- exe += '.py'
- sys.argv[0] = __file__ = exe
- for arg in list(sys.argv[1:]):
- if arg.startswith('-psn'):
- sys.argv.remove(arg)
- execfile(exe, globals(), globals())
-
-_run()
diff --git a/setup/installer/osx/app/main.py b/setup/installer/osx/app/main.py
index e1819c9bfc..bde50376f7 100644
--- a/setup/installer/osx/app/main.py
+++ b/setup/installer/osx/app/main.py
@@ -10,33 +10,58 @@ import sys, os, shutil, plistlib, subprocess, glob, zipfile, tempfile, \
py_compile, stat, operator
abspath, join, basename = os.path.abspath, os.path.join, os.path.basename
-#TODO: WMF support in ImageMagick
-
-l = {}
-exec open('setup.py').read() in l
-VERSION = l['VERSION']
-APPNAME = l['APPNAME']
-scripts = l['scripts']
-basenames = l['basenames']
-main_functions = l['main_functions']
-main_modules = l['main_modules']
+from setup import __version__ as VERSION, __appname__ as APPNAME, basenames, \
+ modules as main_modules, Command, SRC, functions as main_functions
LICENSE = open('LICENSE', 'rb').read()
ENV = dict(
- PYTHONPATH='@executable_path/../Resources/Python/site-packages',
- PYTHONHOME='@executable_path/../Resources/Python',
FC_CONFIG_DIR='@executable_path/../Resources/fonts',
MAGICK_HOME='@executable_path/../Frameworks/ImageMagick',
QT_PLUGIN_PATH='@executable_path/../MacOS',
- PYTHONDONTWRITEBYTECODE='1',
- PYTHONIOENCODING='utf-8:replace',
- PYTHONOPTIMIZE='2',
+ PYTHONIOENCODING='UTF-8',
)
SW = os.environ.get('SW', '/sw')
-def compile_launchers(contents_dir, xprograms):
+info = warn = None
+
+class OSX32_Freeze(Command):
+
+ description = 'Freeze OSX calibre installation'
+
+ def add_options(self, parser):
+ parser.add_option('--test-launchers', default=False,
+ action='store_true',
+ help='Only build launchers')
+
+
+ def run(self, opts):
+ global info, warn
+ info, warn = self.info, self.warn
+ main(opts.test_launchers)
+
+def compile_launcher_lib(contents_dir, gcc, base):
+ info('\tCompiling calibre_launcher.dylib')
+ fd = join(contents_dir, 'Frameworks')
+ dest = join(fd, 'calibre-launcher.dylib')
+ src = join(base, 'util.c')
+ cmd = [gcc] + '-Wall -arch i386 -arch ppc -dynamiclib -std=gnu99'.split() + [src] + \
+ ['-I'+base] + \
+ ['-I%s/python/Python.framework/Headers'%SW] + \
+ '-current_version 1.0 -compatibility_version 1.0'.split() + \
+ '-fvisibility=hidden -o'.split() + [dest, '-F%s/python'%SW] + \
+ ['-install_name',
+ '@executable_path/../Frameworks/'+os.path.basename(dest)] + \
+ ['-framework', 'Python', '-framework', 'CoreFoundation', '-headerpad_max_install_names']
+ info('\t'+' '.join(cmd))
+ sys.stdout.flush()
+ subprocess.check_call(cmd)
+ return dest
+
+
+def compile_launchers(contents_dir, xprograms, pyver):
gcc = os.environ.get('CC', 'gcc')
base = os.path.dirname(__file__)
+ lib = compile_launcher_lib(contents_dir, gcc, base)
src = open(join(base, 'launcher.c'), 'rb').read()
env, env_vals = [], []
for key, val in ENV.items():
@@ -46,22 +71,23 @@ def compile_launchers(contents_dir, xprograms):
env_vals = ', '.join(env_vals)+', '
src = src.replace('/*ENV_VARS*/', env)
src = src.replace('/*ENV_VAR_VALS*/', env_vals)
- programs = []
- for program, module in xprograms.items():
- print '\tCompiling', program
+ programs = [lib]
+ for program, x in xprograms.items():
+ module, func = x
+ info('\tCompiling', program)
out = join(contents_dir, 'MacOS', program)
programs.append(out)
psrc = src.replace('**PROGRAM**', program)
psrc = psrc.replace('**MODULE**', module)
+ psrc = psrc.replace('**FUNCTION**', func)
+ psrc = psrc.replace('**PYVER**', pyver)
fsrc = '/tmp/%s.c'%program
with open(fsrc, 'wb') as f:
f.write(psrc)
- cmd = [gcc, '-Wall', '-arch', 'x86_64',
- '-I%s/python/Python.framework/Headers'%SW,
- fsrc, '-o', out, '-F%s/python'%SW,
- '-framework', 'Python', '-framework', 'CoreFoundation',
+ cmd = [gcc, '-Wall', '-arch', 'ppc', '-arch', 'i386',
+ '-I'+base, fsrc, lib, '-o', out,
'-headerpad_max_install_names']
- print ' '.join(cmd)
+ info('\t'+' '.join(cmd))
sys.stdout.flush()
subprocess.check_call(cmd)
return programs
@@ -81,7 +107,7 @@ def flipwritable(fn, mode=None):
def thin(path):
try:
subprocess.check_call(['lipo', path, '-verify_arch', 'ppc64'])
- print '\tThinning', path
+ info('\tThinning', path)
except:
return
else:
@@ -92,7 +118,7 @@ def strip_files(files, argv_max=(256 * 1024)):
"""
Strip a list of files
"""
- tostrip = [(fn, flipwritable(fn)) for fn in files]
+ tostrip = [(fn, flipwritable(fn)) for fn in files if os.path.exists(fn)]
while tostrip:
cmd = list(STRIPCMD)
flips = []
@@ -125,61 +151,66 @@ class Py2App(object):
FID = '@executable_path/../Frameworks'
- def __init__(self, build_dir):
+ def __init__(self, build_dir, test_launchers=False):
self.build_dir = build_dir
self.contents_dir = join(self.build_dir, 'Contents')
self.resources_dir = join(self.contents_dir, 'Resources')
self.frameworks_dir = join(self.contents_dir, 'Frameworks')
self.version_info = '.'.join(map(str, sys.version_info[:2]))
+ self.site_packages = join(self.resources_dir, 'Python', 'site-packages')
self.to_strip = []
self.warnings = []
+ self.run(test_launchers)
+
def warn(self, *args):
- self.warnings.append(args)
- prefix = '' if args and args[0].startswith('WARNING:') else 'WARNING: '
- sys.stdout.write(prefix+' '.join(args)+'\n')
- sys.stdout.flush()
+ warn(*args)
+ def run(self, test_launchers):
+ ret = 0
+ if not test_launchers:
+ if os.path.exists(self.build_dir):
+ shutil.rmtree(self.build_dir)
+ os.makedirs(self.build_dir)
+ self.create_skeleton()
+ self.create_plist()
- def run(self):
- self.create_skeleton()
- self.create_plist()
+ self.add_python_framework()
+ self.add_qt_frameworks()
+ self.add_calibre_plugins()
+ self.add_podofo()
+ self.add_poppler()
+ self.add_libjpeg()
+ self.add_libpng()
+ self.add_fontconfig()
+ self.add_imagemagick()
+ self.add_misc_libraries()
- self.add_python_framework()
- self.add_qt_frameworks()
- self.add_calibre_plugins()
- self.add_podofo()
- self.add_poppler()
- self.add_libjpeg()
- self.add_libpng()
- self.add_fontconfig()
- self.add_imagemagick()
- self.add_misc_libraries()
+ self.add_site_packages()
+ self.add_stdlib()
+ self.add_resources()
+ self.compile_py_modules()
- self.add_site_packages()
- self.add_stdlib()
- self.compile_py_modules()
+ self.create_console_app()
- self.create_console_app()
-
- self.copy_launcher_and_site()
+ self.copy_site()
self.create_exe()
- self.thin_to_x86_64()
- self.strip_files()
+ if not test_launchers:
+ #self.thin_to_x86_64()
+ self.strip_files()
- ret = self.makedmg(self.build_dir, APPNAME+'-'+VERSION+'-x86_64')
- sys.stdout.flush()
- sys.stderr.flush()
+ ret = self.makedmg(self.build_dir, APPNAME+'-'+VERSION)
- print '\nThere were', len(self.warnings), 'warnings'
- for w in list(self.warnings):
- print
- self.warn(*w)
return ret
+ @flush
+ def add_resources(self):
+ shutil.copytree('resources', os.path.join(self.resources_dir,
+ 'resources'))
+
@flush
def thin_to_x86_64(self):
- print '\nThinning to x86_64'
+ info('\nThinning to x86_64')
for y in (self.frameworks_dir, join(self.resources_dir, 'Python')):
for x in os.walk(y):
for f in x[-1]:
@@ -192,24 +223,22 @@ class Py2App(object):
@flush
def strip_files(self):
- print '\nStripping files...'
+ info('\nStripping files...')
strip_files(self.to_strip)
@flush
def create_exe(self):
- print '\nCreating launchers'
+ info('\nCreating launchers')
programs = {}
- for program, module in zip(basenames['console'],
- main_modules['console'])+zip(basenames['gui'],
- main_modules['gui']):
- programs[program] = module
- programs = compile_launchers(self.contents_dir, programs)
+ progs = []
+ for x in ('console', 'gui'):
+ progs += list(zip(basenames[x], main_modules[x], main_functions[x]))
+ for program, module, func in progs:
+ programs[program] = (module, func)
+ programs = compile_launchers(self.contents_dir, programs,
+ self.version_info)
for out in programs:
self.fix_dependencies_in_lib(out)
- for module in main_modules['console'] + main_modules['gui']:
- base = join(*module.split('.'))+'.py'
- shutil.copy2(join('src', base),
- join(self.resources_dir, 'Python', 'site-packages', base))
@flush
def set_id(self, path_to_lib, new_id):
@@ -233,20 +262,20 @@ class Py2App(object):
def get_local_dependencies(self, path_to_lib):
for x in self.get_dependencies(path_to_lib):
for y in (SW+'/lib/', '/usr/local/lib/', SW+'/qt/lib/',
- SW+'/python/'):
+ SW+'/python/', SW+'/freetype/lib/'):
if x.startswith(y):
yield x, x[len(y):]
break
@flush
def change_dep(self, old_dep, new_dep, path_to_lib):
- print '\tResolving dependency %s to'%old_dep, new_dep
+ info('\tResolving dependency %s to'%old_dep, new_dep)
subprocess.check_call(['install_name_tool', '-change', old_dep, new_dep,
path_to_lib])
@flush
def fix_dependencies_in_lib(self, path_to_lib):
- print '\nFixing dependencies in', path_to_lib
+ info('\nFixing dependencies in', path_to_lib)
self.to_strip.append(path_to_lib)
old_mode = flipwritable(path_to_lib)
for dep, bname in self.get_local_dependencies(path_to_lib):
@@ -259,7 +288,7 @@ class Py2App(object):
@flush
def add_python_framework(self):
- print '\nAdding Python framework'
+ info('\nAdding Python framework')
src = join(SW, 'python', 'Python.framework')
x = join(self.frameworks_dir, 'Python.framework')
curr = os.path.realpath(join(src, 'Versions', 'Current'))
@@ -274,7 +303,7 @@ class Py2App(object):
@flush
def add_qt_frameworks(self):
for f in ('QtCore', 'QtGui', 'QtXml', 'QtNetwork', 'QtSvg', 'QtWebkit',
- 'phonon'):
+ 'QtXmlPatterns', 'phonon'):
self.add_qt_framework(f)
for d in glob.glob(join(SW, 'qt', 'plugins', '*')):
shutil.copytree(d, join(self.contents_dir, 'MacOS', basename(d)))
@@ -353,30 +382,30 @@ class Py2App(object):
@flush
def add_podofo(self):
- print '\nAdding PoDoFo'
+ info('\nAdding PoDoFo')
pdf = join(SW, 'lib', 'libpodofo.0.6.99.dylib')
self.install_dylib(pdf)
@flush
def add_poppler(self):
- print '\nAdding poppler'
- for x in ('libpoppler.4.dylib', 'libpoppler-qt4.3.dylib'):
+ info('\nAdding poppler')
+ for x in ('libpoppler.5.dylib', 'libpoppler-qt4.3.dylib'):
self.install_dylib(os.path.join(SW, 'lib', x))
self.install_dylib(os.path.join(SW, 'bin', 'pdftohtml'), False)
@flush
def add_libjpeg(self):
- print '\nAdding libjpeg'
+ info('\nAdding libjpeg')
self.install_dylib(os.path.join(SW, 'lib', 'libjpeg.7.dylib'))
@flush
def add_libpng(self):
- print '\nAdding libpng'
+ info('\nAdding libpng')
self.install_dylib(os.path.join(SW, 'lib', 'libpng12.0.dylib'))
@flush
def add_fontconfig(self):
- print '\nAdding fontconfig'
+ info('\nAdding fontconfig')
for x in ('fontconfig.1', 'freetype.6', 'expat.1'):
src = os.path.join(SW, 'lib', 'lib'+x+'.dylib')
self.install_dylib(src)
@@ -400,7 +429,7 @@ class Py2App(object):
@flush
def add_imagemagick(self):
- print '\nAdding ImageMagick'
+ info('\nAdding ImageMagick')
for x in ('Wand', 'Core'):
self.install_dylib(os.path.join(SW, 'lib', 'libMagick%s.2.dylib'%x))
idir = glob.glob(os.path.join(SW, 'lib', 'ImageMagick-*'))[-1]
@@ -419,16 +448,15 @@ class Py2App(object):
@flush
def add_misc_libraries(self):
- for x in ('usb', 'unrar', 'readline.6.0'):
- print '\nAdding', x
+ for x in ('usb', 'unrar', 'readline.6.0', 'wmflite-0.2.7'):
+ info('\nAdding', x)
x = 'lib%s.dylib'%x
shutil.copy2(join(SW, 'lib', x), self.frameworks_dir)
self.set_id(join(self.frameworks_dir, x), self.FID+'/'+x)
@flush
def add_site_packages(self):
- print '\nAdding site-packages'
- self.site_packages = join(self.resources_dir, 'Python', 'site-packages')
+ info('\nAdding site-packages')
os.makedirs(self.site_packages)
paths = reversed(map(abspath, [x for x in sys.path if x.startswith('/')]))
upaths = []
@@ -455,12 +483,12 @@ class Py2App(object):
finally:
if tdir is not None:
shutil.rmtree(tdir)
+ shutil.rmtree(os.path.join(self.site_packages, 'calibre', 'plugins'))
self.remove_bytecode(join(self.resources_dir, 'Python', 'site-packages'))
@flush
def add_modules_from_dir(self, src):
for x in glob.glob(join(src, '*.py'))+glob.glob(join(src, '*.so')):
- dest = join(self.site_packages, basename(x))
shutil.copy2(x, self.site_packages)
if x.endswith('.so'):
self.fix_dependencies_in_lib(x)
@@ -507,7 +535,7 @@ class Py2App(object):
@flush
def add_stdlib(self):
- print '\nAdding python stdlib'
+ info('\nAdding python stdlib')
src = join(SW, 'python/Python.framework/Versions/Current/lib/python')
src += self.version_info
dest = join(self.resources_dir, 'Python', 'lib', 'python')
@@ -537,7 +565,7 @@ class Py2App(object):
@flush
def compile_py_modules(self):
- print '\nCompiling Python modules'
+ info( '\nCompiling Python modules')
base = join(self.resources_dir, 'Python')
for x in os.walk(base):
root = x[0]
@@ -553,7 +581,7 @@ class Py2App(object):
@flush
def create_console_app(self):
- print '\nCreating console.app'
+ info( '\nCreating console.app')
cc_dir = os.path.join(self.contents_dir, 'console.app', 'Contents')
os.makedirs(cc_dir)
for x in os.listdir(self.contents_dir):
@@ -568,9 +596,8 @@ class Py2App(object):
join(cc_dir, x))
@flush
- def copy_launcher_and_site(self):
+ def copy_site(self):
base = os.path.dirname(__file__)
- shutil.copy2(join(base, 'launcher.py'), self.resources_dir)
shutil.copy2(join(base, 'site.py'), join(self.resources_dir, 'Python',
'lib', 'python'+self.version_info))
@@ -581,7 +608,7 @@ class Py2App(object):
internet_enable=True,
format='UDBZ'):
''' Copy a directory d into a dmg named volname '''
- print '\nCreating dmg'
+ info('\nCreating dmg')
sys.stdout.flush()
if not os.path.exists(destdir):
os.makedirs(destdir)
@@ -593,7 +620,7 @@ class Py2App(object):
if internet_enable:
subprocess.check_call(['/usr/bin/hdiutil', 'internet-enable', '-yes', dmg])
size = os.stat(dmg).st_size/(1024*1024.)
- print '\nInstaller size: %.2fMB\n'%size
+ info('\nInstaller size: %.2fMB\n'%size)
return dmg
def test_exe():
@@ -603,15 +630,11 @@ def test_exe():
return 0
-def main():
+def main(test=False):
if 'test_exe' in sys.argv:
return test_exe()
- build_dir = abspath(join('build', APPNAME+'.app'))
- if os.path.exists(build_dir):
- shutil.rmtree(build_dir)
- os.makedirs(build_dir)
- py2app = Py2App(build_dir)
- py2app.run()
+ build_dir = abspath(join(os.path.dirname(SRC), 'build', APPNAME+'.app'))
+ Py2App(build_dir, test_launchers=test)
return 0
diff --git a/setup/installer/osx/app/site.py b/setup/installer/osx/app/site.py
index 4c98a871ca..5232706727 100644
--- a/setup/installer/osx/app/site.py
+++ b/setup/installer/osx/app/site.py
@@ -11,11 +11,15 @@ def makepath(*paths):
dir = os.path.abspath(os.path.join(*paths))
return dir, os.path.normcase(dir)
-for m in sys.modules.values():
- f = getattr(m, '__file__', None)
- if isinstance(f, basestring) and os.path.exists(f):
- m.__file__ = os.path.abspath(m.__file__)
-del m
+def abs__file__():
+ """Set all module __file__ attribute to an absolute path"""
+ for m in sys.modules.values():
+ if hasattr(m, '__loader__'):
+ continue # don't mess with a PEP 302-supplied __file__
+ try:
+ m.__file__ = os.path.abspath(m.__file__)
+ except AttributeError:
+ continue
# This ensures that the initial path provided by the interpreter contains
# only absolute pathnames, even if we're running from the build directory.
@@ -104,3 +108,42 @@ sys.setdefaultencoding('utf-8')
#
if hasattr(sys, "setdefaultencoding"):
del sys.setdefaultencoding
+
+def run_entry_point():
+ bname, mod, func = sys.calibre_basename, sys.calibre_module, sys.calibre_function
+ sys.argv[0] = bname
+ pmod = __import__(mod, fromlist=[1], level=0)
+ return getattr(pmod, func)()
+
+def add_calibre_vars(base):
+ sys.frameworks_dir = os.path.join(os.path.dirname(base), 'Frameworks')
+ sys.resources_location = os.path.abspath(os.path.join(base, 'resources'))
+ sys.extensions_location = os.path.join(sys.frameworks_dir, 'plugins')
+ sys.binaries_path = os.path.join(os.path.dirname(base), 'MacOS')
+ sys.console_binaries_path = os.path.join(os.path.dirname(base),
+ 'console.app', 'Contents', 'MacOS')
+
+ dv = os.environ.get('CALIBRE_DEVELOP_FROM', None)
+ if dv and os.path.exists(dv):
+ sys.path.insert(0, os.path.abspath(dv))
+
+
+def main():
+ global __file__
+ base = sys.resourcepath
+
+ sys.frozen = 'macosx_app'
+ sys.new_app_bundle = True
+ abs__file__()
+
+ add_calibre_vars(base)
+ addsitedir(sys.site_packages)
+
+
+ for arg in list(sys.argv[1:]):
+ if arg.startswith('-psn'):
+ sys.argv.remove(arg)
+
+ return run_entry_point()
+
+
diff --git a/setup/installer/osx/app/util.c b/setup/installer/osx/app/util.c
new file mode 100644
index 0000000000..763e07a309
--- /dev/null
+++ b/setup/installer/osx/app/util.c
@@ -0,0 +1,220 @@
+#include "util.h"
+#include
+#include
+#include
+#include
+#include
+
+#define EXPORT __attribute__((visibility("default")))
+
+static const char *ERR_OOM = "Out of memory";
+
+static int
+report_error(const char *msg) {
+ fprintf(stderr, msg);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+ return -1;
+}
+
+static int
+report_code(const char *preamble, const char* msg, int code) {
+ fprintf(stderr, "%s: %s\n", preamble, msg);
+ fflush(stderr);
+ return code;
+}
+
+#define EXE "@executable_path/.."
+
+static void
+set_env_vars(const char **ENV_VARS, const char **ENV_VAR_VALS, const char* exe_path) {
+ int i = 0;
+ char buf[3*PATH_MAX];
+ const char *env_var, *val;
+
+ while(1) {
+ env_var = ENV_VARS[i];
+ if (env_var == NULL) break;
+ val = ENV_VAR_VALS[i++];
+ if (strstr(val, EXE) == val && strlen(val) >= strlen(EXE)+1) {
+ strncpy(buf, exe_path, 3*PATH_MAX-150);
+ strncpy(buf+strlen(exe_path), val+strlen(EXE), 150);
+ setenv(env_var, buf, 1);
+ } else
+ setenv(env_var, val, 1);
+ }
+ return;
+}
+
+void initialize_interpreter(const char **ENV_VARS, const char **ENV_VAR_VALS,
+ char *PROGRAM, const char *MODULE, const char *FUNCTION, const char *PYVER,
+ const char* exe_path, const char *rpath, int argc, const char **argv) {
+ PyObject *pargv, *v;
+ int i;
+ Py_OptimizeFlag = 2;
+ Py_NoSiteFlag = 1;
+ Py_DontWriteBytecodeFlag = 1;
+ Py_IgnoreEnvironmentFlag = 1;
+ Py_NoUserSiteDirectory = 1;
+
+ //Py_VerboseFlag = 1;
+ //Py_DebugFlag = 1;
+
+ Py_SetProgramName(PROGRAM);
+
+ char pyhome[1000];
+ snprintf(pyhome, 1000, "%s/Python", rpath);
+ Py_SetPythonHome(pyhome);
+
+ set_env_vars(ENV_VARS, ENV_VAR_VALS, exe_path);
+
+ //printf("Path before Py_Initialize(): %s\r\n\n", Py_GetPath());
+ Py_Initialize();
+
+ char *dummy_argv[1] = {""};
+ PySys_SetArgv(1, dummy_argv);
+ //printf("Path after Py_Initialize(): %s\r\n\n", Py_GetPath());
+ char path[3000];
+ snprintf(path, 3000, "%s/lib/python%s:%s/lib/python%s/lib-dynload:%s/site-packages", pyhome, PYVER, pyhome, PYVER, pyhome);
+
+ PySys_SetPath(path);
+ //printf("Path set by me: %s\r\n\n", path);
+
+ PySys_SetObject("calibre_basename", PyBytes_FromString(PROGRAM));
+ PySys_SetObject("calibre_module", PyBytes_FromString(MODULE));
+ PySys_SetObject("calibre_function", PyBytes_FromString(FUNCTION));
+ PySys_SetObject("resourcepath", PyBytes_FromString(rpath));
+ snprintf(path, 3000, "%s/site-packages", pyhome);
+ PySys_SetObject("site_packages", PyBytes_FromString(pyhome));
+
+
+ pargv = PyList_New(argc);
+ if (pargv == NULL) exit(report_error(ERR_OOM));
+ for (i = 0; i < argc; i++) {
+ v = PyBytes_FromString(argv[i]);
+ if (v == NULL) exit(report_error(ERR_OOM));
+ PyList_SetItem(pargv, i, v);
+ }
+ PySys_SetObject("argv", pargv);
+
+}
+
+
+int pyobject_to_int(PyObject *res) {
+ int ret; PyObject *tmp;
+ tmp = PyNumber_Int(res);
+ if (tmp == NULL) ret = (PyObject_IsTrue(res)) ? 1 : 0;
+ else ret = (int)PyInt_AS_LONG(tmp);
+
+ return ret;
+}
+
+int handle_sysexit(PyObject *e) {
+ PyObject *code;
+
+ code = PyObject_GetAttrString(e, "code");
+ if (!code) return 0;
+ return pyobject_to_int(code);
+}
+
+int calibre_show_python_error(const char *preamble, int code) {
+ PyObject *exc, *val, *tb, *str;
+ int ret, issysexit = 0; char *i;
+
+ if (!PyErr_Occurred()) return code;
+ issysexit = PyErr_ExceptionMatches(PyExc_SystemExit);
+
+ PyErr_Fetch(&exc, &val, &tb);
+
+ if (exc != NULL) {
+ PyErr_NormalizeException(&exc, &val, &tb);
+
+ if (issysexit) {
+ return (val) ? handle_sysexit(val) : 0;
+ }
+ if (val != NULL) {
+ str = PyObject_Unicode(val);
+ if (str == NULL) {
+ PyErr_Clear();
+ str = PyObject_Str(val);
+ }
+ i = PyString_AsString(str);
+ ret = report_code(preamble, (i==NULL)?ERR_OOM:i, code);
+ if (tb != NULL) {
+ PyErr_Restore(exc, val, tb);
+ PyErr_Print();
+ }
+ return ret;
+ }
+ }
+ return report_code(preamble, "", code);
+}
+
+EXPORT
+int
+run(const char **ENV_VARS, const char **ENV_VAR_VALS, char *PROGRAM,
+ const char *MODULE, const char *FUNCTION, const char *PYVER,
+ int argc, const char **argv, const char **envp) {
+ char *pathPtr = NULL;
+ char buf[3*PATH_MAX];
+ int ret = 0, i;
+ PyObject *site, *mainf, *res;
+
+
+ uint32_t buf_size = PATH_MAX+1;
+ char *ebuf = calloc(buf_size, sizeof(char));
+ ret = _NSGetExecutablePath(ebuf, &buf_size);
+ if (ret == -1) {
+ free(ebuf);
+ ebuf = calloc(buf_size, sizeof(char));
+ if (_NSGetExecutablePath(ebuf, &buf_size) != 0)
+ return report_error("Failed to find real path of executable.");
+ }
+ pathPtr = realpath(ebuf, buf);
+ if (pathPtr == NULL) {
+ return report_error(strerror(errno));
+ }
+ char *t;
+ for (i = 0; i < 3; i++) {
+ t = rindex(pathPtr, '/');
+ if (t == NULL) return report_error("Failed to determine bundle path.");
+ *t = '\0';
+ }
+
+
+
+ char rpath[PATH_MAX+1], exe_path[PATH_MAX+1];
+ snprintf(exe_path, PATH_MAX+1, "%s/Contents", pathPtr);
+ snprintf(rpath, PATH_MAX+1, "%s/Resources", exe_path);
+ initialize_interpreter(ENV_VARS, ENV_VAR_VALS, PROGRAM, MODULE, FUNCTION, PYVER,
+ exe_path, rpath, argc, argv);
+
+ site = PyImport_ImportModule("site");
+
+ if (site == NULL)
+ ret = calibre_show_python_error("Failed to import site module", -1);
+ else {
+ Py_XINCREF(site);
+
+ mainf = PyObject_GetAttrString(site, "main");
+ if (mainf == NULL || !PyCallable_Check(mainf))
+ ret = calibre_show_python_error("site module has no main function", -1);
+ else {
+ Py_XINCREF(mainf);
+ res = PyObject_CallObject(mainf, NULL);
+
+ if (res == NULL)
+ ret = calibre_show_python_error("Python function terminated unexpectedly", -1);
+ else {
+ }
+ }
+ }
+ PyErr_Clear();
+ Py_Finalize();
+
+ //printf("11111 Returning: %d\r\n", ret);
+ return ret;
+}
+
+
+
diff --git a/setup/installer/osx/app/util.h b/setup/installer/osx/app/util.h
new file mode 100644
index 0000000000..636af4db13
--- /dev/null
+++ b/setup/installer/osx/app/util.h
@@ -0,0 +1,5 @@
+#pragma once
+
+int run(const char **ENV_VARS, const char **ENV_VAR_VALS, char *PROGRAM,
+ const char *MODULE, const char *FUNCTION, const char *PYVER,
+ int argc, const char **argv, const char **envp);
diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py
index ccef0f39e4..f63bbd9ade 100644
--- a/src/calibre/__init__.py
+++ b/src/calibre/__init__.py
@@ -11,8 +11,6 @@ from math import floor
warnings.simplefilter('ignore', DeprecationWarning)
-from PyQt4.QtCore import QUrl
-from PyQt4.QtGui import QDesktopServices
from calibre.startup import plugins, winutil, winutilerror
from calibre.constants import iswindows, isosx, islinux, isfrozen, \
terminal_controller, preferred_encoding, \
@@ -140,9 +138,6 @@ def prints(*args, **kwargs):
class CommandLineError(Exception):
pass
-
-
-
def setup_cli_handlers(logger, level):
if os.environ.get('CALIBRE_WORKER', None) is not None and logger.handlers:
return
@@ -347,6 +342,8 @@ def detect_ncpus():
def launch(path_or_url):
+ from PyQt4.QtCore import QUrl
+ from PyQt4.QtGui import QDesktopServices
if os.path.exists(path_or_url):
path_or_url = 'file:'+path_or_url
QDesktopServices.openUrl(QUrl(path_or_url))
@@ -456,6 +453,60 @@ def ipython(user_ns=None):
from calibre.utils.config import config_dir
ipydir = os.path.join(config_dir, ('_' if iswindows else '.')+'ipython')
os.environ['IPYTHONDIR'] = ipydir
+ if not os.path.exists(ipydir):
+ os.makedirs(ipydir)
+ rc = os.path.join(ipydir, 'ipythonrc')
+ if not os.path.exists(rc):
+ open(rc, 'wb').write(' ')
+ UC = '''
+import IPython.ipapi
+ip = IPython.ipapi.get()
+
+# You probably want to uncomment this if you did %upgrade -nolegacy
+import ipy_defaults
+
+import os, re, sys
+
+def main():
+ # Handy tab-completers for %cd, %run, import etc.
+ # Try commenting this out if you have completion problems/slowness
+ import ipy_stock_completers
+
+ # uncomment if you want to get ipython -p sh behaviour
+ # without having to use command line switches
+
+ import ipy_profile_sh
+
+
+ # Configure your favourite editor?
+ # Good idea e.g. for %edit os.path.isfile
+
+ import ipy_editors
+
+ # Choose one of these:
+
+ #ipy_editors.scite()
+ #ipy_editors.scite('c:/opt/scite/scite.exe')
+ #ipy_editors.komodo()
+ #ipy_editors.idle()
+ # ... or many others, try 'ipy_editors??' after import to see them
+
+ # Or roll your own:
+ #ipy_editors.install_editor("c:/opt/jed +$line $file")
+
+ ipy_editors.kate()
+
+ o = ip.options
+ # An example on how to set options
+ #o.autocall = 1
+ o.system_verbose = 0
+ o.confirm_exit = 0
+
+main()
+ '''
+ uc = os.path.join(ipydir, 'ipy_user_conf.py')
+ if not os.path.exists(uc):
+ open(uc, 'wb').write(UC)
from IPython.Shell import IPShellEmbed
ipshell = IPShellEmbed(user_ns=user_ns)
ipshell()
diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py
index 9e53a433ad..5b95f8f489 100644
--- a/src/calibre/customize/builtins.py
+++ b/src/calibre/customize/builtins.py
@@ -417,6 +417,7 @@ from calibre.devices.nokia.driver import N770, N810
from calibre.devices.eslick.driver import ESLICK
from calibre.devices.nuut2.driver import NUUT2
from calibre.devices.iriver.driver import IRIVER_STORY
+from calibre.devices.boox.driver import BOOX
from calibre.ebooks.metadata.fetch import GoogleBooks, ISBNDB, Amazon
plugins = [HTML2ZIP, PML2PMLZ, GoogleBooks, ISBNDB, Amazon]
@@ -481,6 +482,7 @@ plugins += [
ITALICA,
SHINEBOOK,
ECLICTO,
+ BOOX,
EB600,
]
plugins += [x for x in list(locals().values()) if isinstance(x, type) and \
diff --git a/src/calibre/devices/boox/__init__.py b/src/calibre/devices/boox/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/calibre/devices/boox/driver.py b/src/calibre/devices/boox/driver.py
new file mode 100644
index 0000000000..85495c459c
--- /dev/null
+++ b/src/calibre/devices/boox/driver.py
@@ -0,0 +1,87 @@
+__license__ = 'GPL v3'
+__copyright__ = '2009, Jesus Manuel Marinho Valcarce '
+__docformat__ = 'restructuredtext en'
+
+'''
+Device driver for BOOX
+'''
+
+import re
+
+from calibre.devices.usbms.driver import USBMS
+
+class BOOX(USBMS):
+
+ name = 'BOOX driver'
+ gui_name = 'BOOX'
+ description = _('Communicate with the BOOX eBook reader.')
+ author = 'Jesus Manuel Marinho Valcarce'
+ supported_platforms = ['windows', 'osx', 'linux']
+
+ # Ordered list of supported formats
+ FORMATS = ['ebub', 'pdf', 'html', 'txt', 'rtf', 'mobi', 'prc', 'chm']
+
+ VENDOR_ID = [0x0525]
+ PRODUCT_ID = [0xa4a5]
+ BCD = [0x322]
+
+ VENDOR_NAME = 'Linux 2.6.26-466-ga04670e with fsl-usb2-udc'
+ WINDOWS_MAIN_MEM = 'FILE-STOR_GADGET'
+ WINDOWS_CARD_A_MEM = 'FILE-STOR_GADGET'
+
+ OSX_MAIN_MEM = 'Linux File-Stor Gadget Media'
+ OSX_CARD_A_MEM = 'Linux File-Stor Gadget Media'
+
+ MAIN_MEMORY_VOLUME_LABEL = 'BOOX Internal Memory'
+ STORAGE_CARD_VOLUME_LABEL = 'BOOX Storage Card'
+
+ EBOOK_DIR_MAIN = 'MyBooks'
+ EBOOK_DIR_CARD_A = 'MyBooks'
+ SUPPORTS_SUB_DIRS = True
+
+ def windows_sort_drives(self, drives):
+ main = drives.get('main', None)
+ card = drives.get('carda', None)
+ if card and main and card > main:
+ drives['main'] = card
+ drives['carda'] = main
+
+ if card and not main:
+ drives['main'] = card
+ drives['carda'] = None
+
+ return drives
+
+ def osx_sort_names(self, names):
+ main = names.get('main', None)
+ card = names.get('carda', None)
+
+ try:
+ main_num = int(re.findall('\d+', main)[0]) if main else None
+ except:
+ main_num = None
+ try:
+ card_num = int(re.findall('\d+', card)[0]) if card else None
+ except:
+ card_num = None
+
+ if card_num is not None and main_num is not None and card_num > main_num:
+ names['main'] = card
+ names['carda'] = main
+
+ if card and not main:
+ names['main'] = card
+ names['carda'] = None
+
+ return names
+
+ def linux_swap_drives(self, drives):
+ if len(drives) < 2: return drives
+ drives = list(drives)
+ t = drives[0]
+ drives[0] = drives[1]
+ drives[1] = t
+ return tuple(drives)
+
+
+
diff --git a/src/calibre/devices/nook/driver.py b/src/calibre/devices/nook/driver.py
index cc3f26d730..332c4d86eb 100644
--- a/src/calibre/devices/nook/driver.py
+++ b/src/calibre/devices/nook/driver.py
@@ -23,6 +23,7 @@ class NOOK(USBMS):
gui_name = _('The Nook')
description = _('Communicate with the Nook eBook reader.')
author = 'John Schember'
+ icon = I('devices/nook.jpg')
supported_platforms = ['windows', 'linux', 'osx']
# Ordered list of supported formats
diff --git a/src/calibre/ebooks/comic/input.py b/src/calibre/ebooks/comic/input.py
index f0acfe3a06..85590a7bae 100755
--- a/src/calibre/ebooks/comic/input.py
+++ b/src/calibre/ebooks/comic/input.py
@@ -8,6 +8,7 @@ Based on ideas from comiclrf created by FangornUK.
'''
import os, shutil, traceback, textwrap, time
+from ctypes import byref
from Queue import Empty
from calibre.customize.conversion import InputFormatPlugin, OptionRecommendation
@@ -75,7 +76,10 @@ class PageProcessor(list):
if img < 0:
raise RuntimeError('Cannot create wand.')
if not pw.MagickReadImage(img, self.path_to_page):
- raise IOError('Failed to read image from: %'%self.path_to_page)
+ severity = pw.ExceptionType(0)
+ msg = pw.MagickGetException(img, byref(severity))
+ raise IOError('Failed to read image from: %s: %s'
+ %(self.path_to_page, msg))
width = pw.MagickGetImageWidth(img)
height = pw.MagickGetImageHeight(img)
if self.num == 0: # First image so create a thumbnail from it
@@ -363,14 +367,14 @@ class ComicInput(InputFormatPlugin):
else:
new_pages, failures = process_pages(new_pages, self.opts,
self.report_progress, tdir2)
- if not new_pages:
- raise ValueError('Could not find any valid pages in comic: %s'
- % comic)
if failures:
self.log.warning('Could not process the following pages '
'(run with --verbose to see why):')
for f in failures:
self.log.warning('\t', f)
+ if not new_pages:
+ raise ValueError('Could not find any valid pages in comic: %s'
+ % comic)
thumbnail = os.path.join(tdir2,
'thumbnail.'+self.opts.output_format.lower())
if not os.access(thumbnail, os.R_OK):
diff --git a/src/calibre/gui2/dialogs/config/config.ui b/src/calibre/gui2/dialogs/config/config.ui
index 77c1d9eccd..f593df5e14 100644
--- a/src/calibre/gui2/dialogs/config/config.ui
+++ b/src/calibre/gui2/dialogs/config/config.ui
@@ -7,7 +7,7 @@
0
0
- 800
+ 838
730
@@ -89,7 +89,7 @@
0
0
- 524
+ 562
683
diff --git a/src/calibre/gui2/filename_pattern.ui b/src/calibre/gui2/filename_pattern.ui
index d0a4cf74b0..d120ca80b2 100644
--- a/src/calibre/gui2/filename_pattern.ui
+++ b/src/calibre/gui2/filename_pattern.ui
@@ -7,7 +7,7 @@
0
0
335
- 540
+ 570
@@ -84,104 +84,123 @@ p, li { white-space: pre-wrap; }
-
-
-
-
-
- Title:
-
-
-
- -
-
-
- Regular expression (?P<title>)
-
-
- No match
-
-
- true
-
-
-
- -
-
-
- Authors:
-
-
-
- -
-
-
- Regular expression (?P<author>)
-
-
- No match
-
-
- true
-
-
-
- -
-
-
- Series:
-
-
-
- -
-
-
- Regular expression (?P<series>)
-
-
- No match
-
-
- true
-
-
-
- -
-
-
- Series index:
-
-
-
- -
-
-
- Regular expression (?P<series_index>)
-
-
- No match
-
-
- true
-
-
-
- -
-
-
- ISBN:
-
-
-
- -
-
-
- Regular expression (?P<isbn>)
-
-
- No match
-
-
+
-
+
+
true
+
+
+
+ 0
+ 0
+ 301
+ 234
+
+
+
+
-
+
+
+ Title:
+
+
+
+ -
+
+
+ Regular expression (?P<title>)
+
+
+ No match
+
+
+ true
+
+
+
+ -
+
+
+ Authors:
+
+
+
+ -
+
+
+ Regular expression (?P<author>)
+
+
+ No match
+
+
+ true
+
+
+
+ -
+
+
+ Series:
+
+
+
+ -
+
+
+ Regular expression (?P<series>)
+
+
+ No match
+
+
+ true
+
+
+
+ -
+
+
+ Series index:
+
+
+
+ -
+
+
+ Regular expression (?P<series_index>)
+
+
+ No match
+
+
+ true
+
+
+
+ -
+
+
+ ISBN:
+
+
+
+ -
+
+
+ Regular expression (?P<isbn>)
+
+
+ No match
+
+
+ true
+
+
+
+
+
diff --git a/src/calibre/ptempfile.py b/src/calibre/ptempfile.py
index fe69949f99..f4bcfa8675 100644
--- a/src/calibre/ptempfile.py
+++ b/src/calibre/ptempfile.py
@@ -46,7 +46,10 @@ class PersistentTemporaryFile(object):
self.close()
def __del__(self):
- self.close()
+ try:
+ self.close()
+ except:
+ pass
def PersistentTemporaryDirectory(suffix='', prefix='', dir=None):
diff --git a/src/calibre/utils/PythonMagickWand.py b/src/calibre/utils/PythonMagickWand.py
index 16ca8d2935..9920334b0a 100644
--- a/src/calibre/utils/PythonMagickWand.py
+++ b/src/calibre/utils/PythonMagickWand.py
@@ -828,7 +828,7 @@ else:
IsMagickWand = _magick.IsMagickWand
# MagickGetException
try:
- _magick.MagickGetException.restype = ctypes.POINTER(ctypes.c_char)
+ _magick.MagickGetException.restype = ctypes.c_char_p
_magick.MagickGetException.argtypes = (MagickWand,ctypes.POINTER(ExceptionType))
except AttributeError,e:
pass
diff --git a/src/calibre/utils/osx_symlinks.py b/src/calibre/utils/osx_symlinks.py
index 2cfdd72fa3..63863cb810 100644
--- a/src/calibre/utils/osx_symlinks.py
+++ b/src/calibre/utils/osx_symlinks.py
@@ -7,6 +7,7 @@ __copyright__ = '2009, Kovid Goyal '
__docformat__ = 'restructuredtext en'
import sys, os, cPickle
+from calibre.constants import isnewosx
AUTHTOOL="""#!/usr/bin/python
import os
@@ -27,7 +28,7 @@ for s, l in zip(scripts, links):
DEST_PATH = '/usr/bin'
def create_symlinks():
- return create_symlinks_new() if getattr(sys, 'new_app_bundle', False) else create_symlinks_old()
+ return create_symlinks_new() if isnewosx else create_symlinks_old()
def get_scripts():
return cPickle.load(open(P('scripts.pickle'), 'rb'))