diff --git a/installer/osx/freeze.py b/installer/osx/freeze.py index f5e62c4649..06155d3c48 100644 --- a/installer/osx/freeze.py +++ b/installer/osx/freeze.py @@ -31,6 +31,7 @@ resources_dir = os.path.join(base_dir, 'Resources') frameworks_dir = os.path.join(base_dir, 'Frameworks') base_name = os.path.splitext(name)[0] python = os.path.join(base_dir, 'MacOS', 'python') +qt_plugins = os.path.join(os.path.realpath(base_dir), 'MacOS') loader_path = os.path.join(dirpath, base_name+'.py') loader = open(loader_path, 'w') site_packages = glob.glob(resources_dir+'/lib/python*/site-packages.zip')[0] @@ -46,9 +47,10 @@ print >>loader, '%(function)s()' loader.close() os.chmod(loader_path, 0700) os.environ['PYTHONHOME'] = resources_dir -os.environ['FC_CONFIG_DIR'] = os.path.join(resources_dir, 'fonts') +os.environ['FONTCONFIG_PATH'] = os.path.join(resources_dir, 'fonts') os.environ['MAGICK_HOME'] = os.path.join(frameworks_dir, 'ImageMagick') os.environ['DYLD_LIBRARY_PATH'] = os.path.join(frameworks_dir, 'ImageMagick', 'lib') +os.environ['QT_PLUGIN_PATH'] = qt_plugins args = [path, loader_path] + sys.argv[1:] os.execv(python, args) ''' @@ -259,19 +261,11 @@ _check_symlinks_prescript() dest = os.path.join(frameworks_dir, os.path.basename(f)) if os.path.exists(dest): os.remove(dest) - os.link(f, dest) + shutil.copyfile(f, dest) dst = os.path.join(resource_dir, 'fonts') if os.path.exists(dst): shutil.rmtree(dst) shutil.copytree('/usr/local/etc/fonts', dst, symlinks=False) - for x in os.listdir('/usr/local/etc/fonts'): - dst = os.path.join(frameworks_dir, x) - y = os.path.join('/usr/local/etc/fonts', x) - if os.path.isdir(dst): - if os.path.exists(dst): shutil.rmtree(dst) - shutil.copytree(y, dst) - else: - os.link(y, dst) print print 'Adding IPython' diff --git a/setup.py b/setup.py index 89c4f5c925..6232be75b2 100644 --- a/setup.py +++ b/setup.py @@ -59,6 +59,16 @@ if __name__ == '__main__': 'calibre_postinstall = calibre.linux:post_install') optional = [] + if iswindows: + optional.append(Extension('calibre.plugins.winutil', + sources=['src/calibre/utils/windows/winutil.c'], + libraries=['shell32', 'setupapi'], + include_dirs=os.environ.get('INCLUDE', + 'C:/WinDDK/6001.18001/inc/api/;' + 'C:/WinDDK/6001.18001/inc/crt/').split(';'), + extra_compile_args=['/X'] + )) + podofo_inc = '/usr/include/podofo' if islinux else \ 'C:\\podofo\\include\\podofo' if iswindows else \ @@ -83,6 +93,7 @@ if __name__ == '__main__': r'C:\cygwin\home\kovid\fontconfig\lib' if iswindows else \ '/Users/kovid/fontconfig/lib' + ext_modules = optional + [ Extension('calibre.plugins.fontconfig', @@ -113,15 +124,6 @@ if __name__ == '__main__': ['src/calibre/gui2/pictureflow/pictureflow.sip'] ) ] - if iswindows: - ext_modules.append(Extension('calibre.plugins.winutil', - sources=['src/calibre/utils/windows/winutil.c'], - libraries=['shell32', 'setupapi'], - include_dirs=os.environ.get('INCLUDE', - 'C:/WinDDK/6001.18001/inc/api/;' - 'C:/WinDDK/6001.18001/inc/crt/').split(';'), - extra_compile_args=['/X'] - )) if isosx: ext_modules.append(Extension('calibre.plugins.usbobserver', sources=['src/calibre/devices/usbobserver/usbobserver.c'], diff --git a/src/calibre/devices/usbms/cli.py b/src/calibre/devices/usbms/cli.py index 40e2225486..d31b26d5f4 100644 --- a/src/calibre/devices/usbms/cli.py +++ b/src/calibre/devices/usbms/cli.py @@ -48,9 +48,9 @@ class CLI(object): (self._card_b_prefix and path.startswith(self._card_b_prefix))): path = self._main_prefix + path[1:] elif path.startswith('carda:'): - path = path.replace('carda:', self._card_prefix[:-1]) + path = path.replace('carda:', self._card_a_prefix[:-1]) elif path.startswith('cardb:'): - path = path.replace('cardb:', self._card_prefix[:-1]) + path = path.replace('cardb:', self._card_b_prefix[:-1]) return path def list(self, path, recurse=False, end_session=True, munge=True): diff --git a/src/calibre/devices/usbms/device.py b/src/calibre/devices/usbms/device.py index d5dcf3e440..ba2fb1a5e0 100644 --- a/src/calibre/devices/usbms/device.py +++ b/src/calibre/devices/usbms/device.py @@ -174,7 +174,8 @@ class Device(DeviceConfig, DevicePlugin): cbsz = stats.f_frsize * stats.f_bavail else: msz = self._windows_space(self._main_prefix)[1] - csz = self._windows_space(self._card_prefix)[1] + casz = self._windows_space(self._card_a_prefix)[1] + cbsz = self._windows_space(self._card_b_prefix)[1] return (msz, casz, cbsz) @@ -422,14 +423,13 @@ class Device(DeviceConfig, DevicePlugin): if label is None: label = self.STORAGE_CARD_VOLUME_LABEL + ' 2' extra = 0 - label = label.replace(' ', '_') while True: q = '_(%d)'%extra if extra else '' if not os.path.exists('/media/'+label+q): break extra += 1 if extra: - label += '_(%d)'%extra + label += ' (%d)'%extra def do_mount(node, label): cmd = ['pmount', '-w', '-s'] @@ -504,7 +504,7 @@ class Device(DeviceConfig, DevicePlugin): def do_it(drives): for d in drives: try: - winutil.eject_drive(d) + winutil.eject_drive(bytes(d)[0]) except: pass diff --git a/src/calibre/ebooks/epub/input.py b/src/calibre/ebooks/epub/input.py index b748429725..3405e1ef4e 100644 --- a/src/calibre/ebooks/epub/input.py +++ b/src/calibre/ebooks/epub/input.py @@ -28,7 +28,7 @@ class EPUBInput(InputFormatPlugin): f.write(raw[1024:]) @classmethod - def process_ecryption(cls, encfile, opf, log): + def process_encryption(cls, encfile, opf, log): key = None m = re.search(r'(?i)(urn:uuid:[0-9a-f-]+)', open(opf, 'rb').read()) if m: diff --git a/src/calibre/ebooks/metadata/pdf.py b/src/calibre/ebooks/metadata/pdf.py index 0f7c1e4a89..0dcecac740 100644 --- a/src/calibre/ebooks/metadata/pdf.py +++ b/src/calibre/ebooks/metadata/pdf.py @@ -18,8 +18,7 @@ except: from calibre.ebooks.metadata import MetaInformation, authors_to_string from calibre.utils.pdftk import set_metadata as pdftk_set_metadata from calibre.utils.podofo import get_metadata as podofo_get_metadata, \ - set_metadata as podofo_set_metadata, Unavailable, write_first_page, \ - get_metadata_quick + set_metadata as podofo_set_metadata, Unavailable, get_metadata_quick def get_quick_metadata(stream): raw = stream.read() @@ -32,19 +31,18 @@ def get_quick_metadata(stream): def get_metadata(stream, extract_cover=True): try: - mi = podofo_get_metadata(stream) + with TemporaryDirectory('_pdfmeta') as tdir: + cpath = os.path.join(tdir, 'cover.pdf') + if not extract_cover: + cpath = None + mi = podofo_get_metadata(stream, cpath=cpath) + if mi.cover is not None: + cdata = get_cover(mi.cover) + mi.cover = None + if cdata is not None: + mi.cover_data = ('jpg', cdata) except Unavailable: mi = get_metadata_pypdf(stream) - stream.seek(0) - - if extract_cover and _imagemagick_loaded: - try: - cdata = get_cover(stream) - if cdata is not None: - mi.cover_data = ('jpg', cdata) - except: - import traceback - traceback.print_exc() return mi @@ -127,17 +125,13 @@ def set_metadata_pypdf(stream, mi): stream.write(out_str.read()) stream.seek(0) -def get_cover(stream): - stream.seek(0) - with TemporaryDirectory('_pdfmeta') as tdir: - cover_path = os.path.join(tdir, 'cover.pdf') - write_first_page(stream, cover_path) - with ImageMagick(): - wand = NewMagickWand() - MagickReadImage(wand, cover_path) - MagickSetImageFormat(wand, 'JPEG') - MagickWriteImage(wand, '%s.jpg' % cover_path) - return open('%s.jpg' % cover_path, 'rb').read() +def get_cover(cover_path): + with ImageMagick(): + wand = NewMagickWand() + MagickReadImage(wand, cover_path) + MagickSetImageFormat(wand, 'JPEG') + MagickWriteImage(wand, '%s.jpg' % cover_path) + return open('%s.jpg' % cover_path, 'rb').read() diff --git a/src/calibre/utils/ipc/launch.py b/src/calibre/utils/ipc/launch.py index 2a578ed7d9..4aa8f684ad 100644 --- a/src/calibre/utils/ipc/launch.py +++ b/src/calibre/utils/ipc/launch.py @@ -108,6 +108,11 @@ class Worker(object): self._env['PYTHONHOME'] = resources self._env['MAGICK_HOME'] = os.path.join(fd, 'ImageMagick') self._env['DYLD_LIBRARY_PATH'] = os.path.join(fd, 'ImageMagick', 'lib') + self._env['FONTCONFIG_PATH'] = \ + os.path.join(os.path.realpath(resources), 'fonts') + self._env['QT_PLUGIN_PATH'] = \ + os.path.join(self.osx_contents_dir, 'MacOS') + if isfrozen and not (iswindows or isosx): self._env['LD_LIBRARY_PATH'] = getattr(sys, 'frozen_path') + ':'\ + os.environ.get('LD_LIBRARY_PATH', '') diff --git a/src/calibre/utils/ipc/worker.py b/src/calibre/utils/ipc/worker.py index 0e637a6b55..a53d1818ba 100644 --- a/src/calibre/utils/ipc/worker.py +++ b/src/calibre/utils/ipc/worker.py @@ -39,9 +39,6 @@ PARALLEL_FUNCS = { 'write_pdf_metadata' : ('calibre.utils.podofo.__init__', 'set_metadata_', None), - 'write_pdf_first_page' : - ('calibre.utils.podofo.__init__', 'write_first_page_', None), - 'save_book' : ('calibre.ebooks.metadata.worker', 'save_book', 'notification'), } diff --git a/src/calibre/utils/podofo/__init__.py b/src/calibre/utils/podofo/__init__.py index d52e1b5658..9fc0c981e6 100644 --- a/src/calibre/utils/podofo/__init__.py +++ b/src/calibre/utils/podofo/__init__.py @@ -19,39 +19,7 @@ podofo, podofo_err = plugins['podofo'] class Unavailable(Exception): pass -def write_first_page(stream, opath): - if not podofo: - raise Unavailable(podofo_err) - pt = PersistentTemporaryFile('_podofo.pdf') - pt.write(stream.read()) - pt.close() - server = Server(pool_size=1) - job = ParallelJob('write_pdf_first_page', 'Extract first page of pdf', - lambda x,y:x, args=[pt.name, opath]) - server.add_job(job) - while not job.is_finished: - time.sleep(0.1) - job.update() - - job.update() - server.close() - if not job.result: - raise ValueError('Failed to extract first page: ' + job.details) - -def write_first_page_(inpath, outpath): - p = podofo.PDFDoc() - p.open(inpath) - pages = p.pages - if pages < 1: - raise ValueError('PDF has no pages') - if pages == 1: - shutil.copyfile(inpath, outpath) - return True - p.delete_pages(1, pages-1) - p.save(outpath) - return True - -def get_metadata(stream): +def get_metadata(stream, cpath=None): if not podofo: raise Unavailable(podofo_err) pt = PersistentTemporaryFile('_podofo.pdf') @@ -59,7 +27,7 @@ def get_metadata(stream): pt.close() server = Server(pool_size=1) job = ParallelJob('read_pdf_metadata', 'Read pdf metadata', - lambda x,y:x, args=[pt.name]) + lambda x,y:x, args=[pt.name, cpath]) server.add_job(job) while not job.is_finished: time.sleep(0.1) @@ -69,7 +37,10 @@ def get_metadata(stream): server.close() if job.result is None: raise ValueError('Failed to read metadata: ' + job.details) - title, authors, creator = job.result + title, authors, creator, ok = job.result + if not ok: + print 'Failed to extract cover:' + print job.details if title == '_': title = getattr(stream, 'name', _('Unknown')) title = os.path.splitext(title)[0] @@ -78,6 +49,8 @@ def get_metadata(stream): if creator: mi.book_producer = creator if os.path.exists(pt.name): os.remove(pt.name) + if ok: + mi.cover = cpath return mi def get_metadata_quick(raw): @@ -94,8 +67,7 @@ def get_metadata_quick(raw): mi.book_producer = creator return mi - -def get_metadata_(path): +def get_metadata_(path, cpath=None): p = podofo.PDFDoc() p.open(path) title = p.title @@ -104,7 +76,23 @@ def get_metadata_(path): author = p.author authors = string_to_authors(author) if author else [_('Unknown')] creator = p.creator - return (title, authors, creator) + ok = True + try: + if cpath is not None: + pages = p.pages + if pages < 1: + raise ValueError('PDF has no pages') + if True or pages == 1: + shutil.copyfile(path, cpath) + else: + p.extract_first_page() + p.save(cpath) + except: + import traceback + traceback.print_exc() + ok = False + + return (title, authors, creator, ok) def prep(val): if not val: diff --git a/src/calibre/utils/podofo/podofo.cpp b/src/calibre/utils/podofo/podofo.cpp index e64faa53e2..188ed0c5dc 100644 --- a/src/calibre/utils/podofo/podofo.cpp +++ b/src/calibre/utils/podofo/podofo.cpp @@ -143,18 +143,15 @@ podofo_PDFDoc_version_getter(podofo_PDFDoc *self, void *closure) { static PyObject * -podofo_PDFDoc_delete_pages(podofo_PDFDoc *self, PyObject *args, PyObject *kwargs) { - int first_page, num_pages; - if (PyArg_ParseTuple(args, "ii", &first_page, &num_pages)) { - try { - self->doc->DeletePages(first_page, num_pages); - } catch(const PdfError & err) { - podofo_set_exception(err); - return NULL; - } - } else return NULL; - Py_INCREF(Py_None); - return Py_None; +podofo_PDFDoc_extract_first_page(podofo_PDFDoc *self, PyObject *args, PyObject *kwargs) { + int i, num_pages; + try { + while (self->doc->GetPageCount() > 1) self->doc->GetPagesTree()->DeletePage(1); + } catch(const PdfError & err) { + podofo_set_exception(err); + return NULL; + } + Py_RETURN_NONE; } static PyObject * @@ -313,8 +310,8 @@ static PyMethodDef podofo_PDFDoc_methods[] = { {"save", (PyCFunction)podofo_PDFDoc_save, METH_VARARGS, "Save the PDF document to a path on disk" }, - {"delete_pages", (PyCFunction)podofo_PDFDoc_delete_pages, METH_VARARGS, - "delete_pages(start_page, num_pages) -> int, int\nDelete pages from the PDF document." + {"extract_first_page", (PyCFunction)podofo_PDFDoc_extract_first_page, METH_VARARGS, + "extract_first_page() -> Remove all but the first page." }, {NULL} /* Sentinel */ diff --git a/src/calibre/utils/windows/winutil.c b/src/calibre/utils/windows/winutil.c index 5666c5bc0f..88091ffc52 100644 --- a/src/calibre/utils/windows/winutil.c +++ b/src/calibre/utils/windows/winutil.c @@ -308,7 +308,7 @@ get_all_removable_disks(struct tagDrives *g_drives) static DEVINST GetDrivesDevInstByDeviceNumber(long DeviceNumber, - UINT DriveType, char* szDosDeviceName) + UINT DriveType, LPWSTR szDosDeviceName) { GUID *guid; HDEVINFO hDevInfo; @@ -319,8 +319,12 @@ GetDrivesDevInstByDeviceNumber(long DeviceNumber, long res; HANDLE hDrive; STORAGE_DEVICE_NUMBER sdn; + SP_DEVICE_INTERFACE_DATA spdid; + SP_DEVINFO_DATA spdd; + DWORD dwSize; - IsFloppy = (strstr(szDosDeviceName, "\\Floppy") != NULL); // is there a better way? + + IsFloppy = (wcsstr(szDosDeviceName, L"\\Floppy") != NULL); // is there a better way? switch (DriveType) { case DRIVE_REMOVABLE: @@ -357,12 +361,7 @@ GetDrivesDevInstByDeviceNumber(long DeviceNumber, bRet = FALSE; - pspdidd = - (PSP_DEVICE_INTERFACE_DETAIL_DATA)Buf; - SP_DEVICE_INTERFACE_DATA spdid; - SP_DEVINFO_DATA spdd; - DWORD dwSize; - + pspdidd = (PSP_DEVICE_INTERFACE_DETAIL_DATA)Buf; spdid.cbSize = sizeof(spdid); while ( TRUE ) { @@ -420,8 +419,11 @@ GetDrivesDevInstByDeviceNumber(long DeviceNumber, static BOOL -eject_drive_letter(char DriveLetter) { - char szRootPath[4], szDevicePath[3], szVolumeAccessPath[7], szDosDeviceName[MAX_PATH]; +eject_drive_letter(WCHAR DriveLetter) { + LPWSTR szRootPath = L"X:\\", + szDevicePath = L"X:", + szVolumeAccessPath = L"\\\\.\\X:"; + WCHAR szDosDeviceName[MAX_PATH]; long DeviceNumber, res, tries; HANDLE hVolume; STORAGE_DEVICE_NUMBER sdn; @@ -429,17 +431,15 @@ eject_drive_letter(char DriveLetter) { DEVINST DevInst; ULONG Status; ULONG ProblemNumber; + UINT DriveType; PNP_VETO_TYPE VetoType; WCHAR VetoNameW[MAX_PATH]; BOOL bSuccess; DEVINST DevInstParent; - szRootPath[0] = DriveLetter; szRootPath[1] = ':'; szRootPath[2] = '\\'; szRootPath[3] = (char)0; - szDevicePath[0] = DriveLetter; szDevicePath[1] = ':'; szDevicePath[2] = (char)0; - szVolumeAccessPath[0] = '\\'; szVolumeAccessPath[1] = '\\'; szVolumeAccessPath[2] = '.'; - szVolumeAccessPath[3] = '\\'; szVolumeAccessPath[4] = DriveLetter; szVolumeAccessPath[5] = ':'; - szVolumeAccessPath[6] = (char)0; - + szRootPath[0] = DriveLetter; + szDevicePath[0] = DriveLetter; + szVolumeAccessPath[4] = DriveLetter; DeviceNumber = -1; @@ -472,6 +472,8 @@ eject_drive_letter(char DriveLetter) { return FALSE; } + DriveType = GetDriveType(szRootPath); + DevInst = GetDrivesDevInstByDeviceNumber(DeviceNumber, DriveType, szDosDeviceName); if (DevInst == 0) return FALSE; @@ -479,7 +481,6 @@ eject_drive_letter(char DriveLetter) { DevInstParent = 0; Status = 0; ProblemNumber = 0; - PNP_VETO_TYPE VetoType; bSuccess = FALSE; res = CM_Get_Parent(&DevInstParent, DevInst, 0); @@ -508,7 +509,7 @@ winutil_eject_drive(PyObject *self, PyObject *args) { if (!PyArg_ParseTuple(args, "c", &DriveLetter)) return NULL; - if (!eject_drive_letter(DriveLetter)) return NULL; + if (!eject_drive_letter((WCHAR)DriveLetter)) return NULL; Py_RETURN_NONE; }