From 0bab82e9b1e0dec31ca0e924fe7e1e72c9de83f6 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 11 Jan 2011 17:25:35 -0700 Subject: [PATCH] RTF Input: Substitute a dummy image for WMF images in the RTF document --- src/calibre/ebooks/rtf/input.py | 28 +++++++++++++++--- src/calibre/utils/wmf/__init__.py | 47 +++++++++++++++++++++++++++++ src/calibre/utils/wmf/wmf.c | 49 ++++++++++++++++++++++++++----- 3 files changed, 112 insertions(+), 12 deletions(-) diff --git a/src/calibre/ebooks/rtf/input.py b/src/calibre/ebooks/rtf/input.py index 5154373eda..714a5b656f 100644 --- a/src/calibre/ebooks/rtf/input.py +++ b/src/calibre/ebooks/rtf/input.py @@ -159,11 +159,31 @@ class RTFInput(InputFormatPlugin): return imap def convert_image(self, name): - from calibre.utils.magick import Image - img = Image() - img.open(name) + try: + return self.rasterize_wmf(name) + except: + self.log.exception('Failed to convert WMF image %r'%name) + return self.replace_wmf(name) + + def replace_wmf(self, name): + from calibre.ebooks import calibre_cover + data = calibre_cover('Conversion of WMF images is not supported', + 'Use Microsoft Word or OpenOffice to save this RTF file' + ' as HTML and convert that in calibre.', title_size=36, + author_size=20) name = name.replace('.wmf', '.jpg') - img.save(name) + with open(name, 'wb') as f: + f.write(data) + return name + + def rasterize_wmf(self, name): + from calibre.utils.wmf import extract_raster_image + with open(name, 'rb') as f: + data = f.read() + data = extract_raster_image(data) + name = name.replace('.wmf', '.jpg') + with open(name, 'wb') as f: + f.write(data) return name diff --git a/src/calibre/utils/wmf/__init__.py b/src/calibre/utils/wmf/__init__.py index 68dfb8d2b5..cb7736e06a 100644 --- a/src/calibre/utils/wmf/__init__.py +++ b/src/calibre/utils/wmf/__init__.py @@ -5,5 +5,52 @@ __license__ = 'GPL v3' __copyright__ = '2011, Kovid Goyal ' __docformat__ = 'restructuredtext en' +import glob + +from calibre.constants import plugins, iswindows, filesystem_encoding +from calibre.ptempfile import TemporaryDirectory +from calibre import CurrentDir +from calibre.utils.magick import Image, PixelWand + +class Unavailable(Exception): + pass + +class NoRaster(Exception): + pass + +def extract_raster_image(wmf_data): + try: + wmf, wmf_err = plugins['wmf'] + except KeyError: + raise Unavailable('libwmf not available on this platform') + if wmf_err: + raise Unavailable(wmf_err) + + if iswindows: + import sys, os + appdir = sys.app_dir + if isinstance(appdir, unicode): + appdir = appdir.encode(filesystem_encoding) + fdir = os.path.join(appdir, 'wmffonts') + wmf.set_font_dir(fdir) + + data = '' + + with TemporaryDirectory('wmf2png') as tdir: + with CurrentDir(tdir): + wmf.render(wmf_data) + + images = list(sorted(glob.glob('*.png'))) + if not images: + raise NoRaster('No raster images in WMF') + data = open(images[0], 'rb').read() + + im = Image() + im.load(data) + pw = PixelWand() + pw.color = '#ffffff' + im.rotate(pw, 180) + + return im.export('png') diff --git a/src/calibre/utils/wmf/wmf.c b/src/calibre/utils/wmf/wmf.c index 1f8e8a27f3..74d3ca813f 100644 --- a/src/calibre/utils/wmf/wmf.c +++ b/src/calibre/utils/wmf/wmf.c @@ -4,6 +4,7 @@ #include #include +//#include typedef struct { char *data; @@ -13,7 +14,7 @@ typedef struct { //This code is taken mostly from the Abiword wmf plugin - +// Buffer read {{{ // returns unsigned char cast to int, or EOF static int wmf_WMF_read(void * context) { char c; @@ -22,11 +23,11 @@ static int wmf_WMF_read(void * context) { if (info->pos == info->len) return EOF; - c = info->data[pos]; + c = info->data[info->pos]; info->pos++; - return (int)c; + return (int)((unsigned char)c); } // returns (-1) on error, else 0 @@ -44,8 +45,17 @@ static long wmf_WMF_tell(void * context) { return (long) info->pos; } +// }}} +char _png_name_buf[100]; +char *wmf_png_name(void *ctxt) { + int *num = (int*)ctxt; + *num = *num + 1; + snprintf(_png_name_buf, 90, "%04d.png", *num); + return _png_name_buf; +} + #define CLEANUP if(API) { if (stream) wmf_free(API, stream); wmf_api_destroy(API); }; static PyObject * @@ -66,9 +76,9 @@ wmf_render(PyObject *self, PyObject *args) { unsigned int max_width = 1600; unsigned int max_height = 1200; - unsigned long max_flags = 0; static const char* Default_Description = "wmf2svg"; + int fname_counter = 0; wmf_error_t err; @@ -125,6 +135,8 @@ wmf_render(PyObject *self, PyObject *args) { ddata->Description = (char *)Default_Description; ddata->bbox = bbox; + ddata->image.context = (void *)&fname_counter; + ddata->image.name = wmf_png_name; wmf_display_size(API, &disp_width, &disp_height, 96, 96); @@ -156,9 +168,9 @@ wmf_render(PyObject *self, PyObject *args) { ddata->height = (unsigned int) ceil ((double) wmf_height); } - ddata->flags |= WMF_SVG_INLINE_IMAGES; - - ddata->flags |= WMF_GD_OUTPUT_MEMORY | WMF_GD_OWN_BUFFER; + // Needs GD + //ddata->flags |= WMF_SVG_INLINE_IMAGES; + //ddata->flags |= WMF_GD_OUTPUT_MEMORY | WMF_GD_OWN_BUFFER; err = wmf_play(API, 0, &(bbox)); @@ -178,11 +190,32 @@ wmf_render(PyObject *self, PyObject *args) { return ans; } +#ifdef _WIN32 +void set_libwmf_fontdir(const char *); + +static PyObject * +wmf_setfontdir(PyObject *self, PyObject *args) { + char *path; + if (!PyArg_ParseTuple(args, "s", &path)) + return NULL; + set_libwmf_fontdir(path); + + Py_RETURN_NONE; +} +#endif + + + static PyMethodDef wmf_methods[] = { {"render", wmf_render, METH_VARARGS, - "render(path) -> Render wmf as svg." + "render(data) -> Render wmf as svg." }, +#ifdef _WIN32 + {"set_font_dir", wmf_setfontdir, METH_VARARGS, + "set_font_dir(path) -> Set the path to the fonts dir on windows, must be called at least once before using render()" + }, +#endif {NULL} /* Sentinel */ };