diff --git a/src/calibre/utils/magick/__init__.py b/src/calibre/utils/magick/__init__.py index 68b5ba3f5f..0eede354a4 100644 --- a/src/calibre/utils/magick/__init__.py +++ b/src/calibre/utils/magick/__init__.py @@ -7,14 +7,41 @@ __docformat__ = 'restructuredtext en' import os -from calibre.constants import plugins +from calibre.constants import plugins, filesystem_encoding _magick, _merr = plugins['magick'] if _magick is None: raise RuntimeError('Failed to load ImageMagick: '+_merr) -class Image(_magick.Image): +_gravity_map = dict([(getattr(_magick, x), x) for x in dir(_magick) if + x.endswith('Gravity')]) + +class DrawingWand(_magick.DrawingWand): # {{{ + + @dynamic_property + def font(self): + def fget(self): + return self.font_.decode(filesystem_encoding, 'replace').lower() + def fset(self, val): + if isinstance(val, unicode): + val = val.encode(filesystem_encoding) + self.font_ = str(val) + return property(fget=fget, fset=fset, doc=_magick.DrawingWand.font_.__doc__) + + @dynamic_property + def gravity(self): + def fget(self): + val = self.gravity_ + return _gravity_map[val] + def fset(self, val): + val = getattr(_magick, str(val)) + self.gravity_ = val + return property(fget=fget, fset=fset, doc=_magick.DrawingWand.gravity_.__doc__) + +# }}} + +class Image(_magick.Image): # {{{ def load(self, data): return _magick.Image.load(self, bytes(data)) @@ -70,6 +97,8 @@ class Image(_magick.Image): raise ValueError('left and/or top out of bounds') _magick.Image.compose(self, img, int(left), int(top), op) +# }}} + def create_canvas(width, height, bgcolor): canvas = Image() canvas.create_canvas(int(width), int(height), str(bgcolor)) diff --git a/src/calibre/utils/magick/generate.py b/src/calibre/utils/magick/generate.py index 7331152020..0555d6538e 100644 --- a/src/calibre/utils/magick/generate.py +++ b/src/calibre/utils/magick/generate.py @@ -49,7 +49,7 @@ def get_value(const): def main(): constants = [] - for x in ('resample', 'image', 'draw', 'distort', 'composite'): + for x in ('resample', 'image', 'draw', 'distort', 'composite', 'geometry'): constants += list(parse_enums('magick/%s.h'%x)) base = os.path.dirname(__file__) constants = [ diff --git a/src/calibre/utils/magick/magick.c b/src/calibre/utils/magick/magick.c index f363553d4b..552650a086 100644 --- a/src/calibre/utils/magick/magick.c +++ b/src/calibre/utils/magick/magick.c @@ -16,6 +16,220 @@ PyObject* magick_set_exception(MagickWand *wand) { } // }}} +// DrawingWand object definition {{{ +typedef struct { + PyObject_HEAD + // Type-specific fields go here. + DrawingWand *wand; + +} magick_DrawingWand; + +static void +magick_DrawingWand_dealloc(magick_DrawingWand* self) +{ + if (self->wand != NULL) self->wand = DestroyDrawingWand(self->wand); + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject * +magick_DrawingWand_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + magick_DrawingWand *self; + + self = (magick_DrawingWand *)type->tp_alloc(type, 0); + if (self != NULL) { + self->wand = NewDrawingWand(); + if (self->wand == NULL || self->wand < 0) { + PyErr_SetString(PyExc_Exception, "Failed to allocate wand."); + self->wand = NULL; + Py_DECREF(self); + return NULL; + } + } + + return (PyObject *)self; +} + +// DrawingWand.font {{{ +static PyObject * +magick_DrawingWand_font_getter(magick_DrawingWand *self, void *closure) { + const char *fp; + fp = DrawGetFont(self->wand); + return Py_BuildValue("s", fp); +} + +static int +magick_DrawingWand_font_setter(magick_DrawingWand *self, PyObject *val, void *closure) { + char *fmt; + + if (val == NULL) { + PyErr_SetString(PyExc_TypeError, "Cannot delete DrawingWand font"); + return -1; + } + + fmt = PyString_AsString(val); + if (fmt == NULL) return -1; + + if (!DrawSetFont(self->wand, fmt)) { + PyErr_SetString(PyExc_ValueError, "Unknown font"); + return -1; + } + + return 0; +} + +// }}} + +// DrawingWand.font_size {{{ +static PyObject * +magick_DrawingWand_fontsize_getter(magick_DrawingWand *self, void *closure) { + return Py_BuildValue("d", DrawGetFontSize(self->wand)); +} + +static int +magick_DrawingWand_fontsize_setter(magick_DrawingWand *self, PyObject *val, void *closure) { + if (val == NULL) { + PyErr_SetString(PyExc_TypeError, "Cannot delete DrawingWand fontsize"); + return -1; + } + + if (!PyFloat_Check(val)) { + PyErr_SetString(PyExc_TypeError, "Font size must be a float"); + return -1; + } + + DrawSetFontSize(self->wand, PyFloat_AsDouble(val)); + + return 0; +} + +// }}} + +// DrawingWand.text_antialias {{{ +static PyObject * +magick_DrawingWand_textantialias_getter(magick_DrawingWand *self, void *closure) { + return PyBool_FromLong((long)DrawGetTextAntialias(self->wand)); +} + +static int +magick_DrawingWand_textantialias_setter(magick_DrawingWand *self, PyObject *val, void *closure) { + if (val == NULL) { + PyErr_SetString(PyExc_TypeError, "Cannot delete DrawingWand textantialias"); + return -1; + } + DrawSetTextAntialias(self->wand, (MagickBooleanType)PyObject_IsTrue(val)); + + return 0; +} + +// }}} + +// DrawingWand.gravity {{{ +static PyObject * +magick_DrawingWand_gravity_getter(magick_DrawingWand *self, void *closure) { + return Py_BuildValue("n", DrawGetGravity(self->wand)); +} + +static int +magick_DrawingWand_gravity_setter(magick_DrawingWand *self, PyObject *val, void *closure) { + GravityType grav; + + if (val == NULL) { + PyErr_SetString(PyExc_TypeError, "Cannot delete DrawingWand gravity"); + return -1; + } + + if (!PyInt_Check(val)) { + PyErr_SetString(PyExc_TypeError, "Gravity must be an integer"); + return -1; + } + + grav = PyInt_AsSsize_t(val); + + DrawSetGravity(self->wand, grav); + + return 0; +} + +// }}} + + +// DrawingWand attr list {{{ +static PyMethodDef magick_DrawingWand_methods[] = { + {NULL} /* Sentinel */ +}; + +static PyGetSetDef magick_DrawingWand_getsetters[] = { + {(char *)"font_", + (getter)magick_DrawingWand_font_getter, (setter)magick_DrawingWand_font_setter, + (char *)"DrawingWand font path. Absolute path to font file.", + NULL}, + + {(char *)"font_size", + (getter)magick_DrawingWand_fontsize_getter, (setter)magick_DrawingWand_fontsize_setter, + (char *)"DrawingWand fontsize", + NULL}, + + {(char *)"text_antialias", + (getter)magick_DrawingWand_textantialias_getter, (setter)magick_DrawingWand_textantialias_setter, + (char *)"DrawingWand text antialias", + NULL}, + + {(char *)"gravity_", + (getter)magick_DrawingWand_gravity_getter, (setter)magick_DrawingWand_gravity_setter, + (char *)"DrawingWand gravity", + NULL}, + + {NULL} /* Sentinel */ +}; + +// }}} + +static PyTypeObject magick_DrawingWandType = { // {{{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "magick.DrawingWand", /*tp_name*/ + sizeof(magick_DrawingWand), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)magick_DrawingWand_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "DrawingWand", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + magick_DrawingWand_methods, /* tp_methods */ + 0, /* tp_members */ + magick_DrawingWand_getsetters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + magick_DrawingWand_new, /* tp_new */ +}; // }}} + + +// }}} + // Image object definition {{{ typedef struct { PyObject_HEAD @@ -44,7 +258,7 @@ magick_Image_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (self != NULL) { self->wand = NewMagickWand(); if (self->wand == NULL || self->wand < 0) { - PyErr_SetString(PyExc_Exception, "Failed to allocate wand. Did you initialize ImageMgick?"); + PyErr_SetString(PyExc_Exception, "Failed to allocate wand."); self->wand = NULL; Py_DECREF(self); return NULL; @@ -140,8 +354,8 @@ magick_Image_size_setter(magick_Image *self, PyObject *val, void *closure) { MagickBooleanType res; if (val == NULL) { - return -1; PyErr_SetString(PyExc_TypeError, "Cannot delete image size"); + return -1; } if (!PySequence_Check(val) || PySequence_Length(val) < 4) { @@ -189,8 +403,8 @@ magick_Image_format_setter(magick_Image *self, PyObject *val, void *closure) { char *fmt; if (val == NULL) { - return -1; PyErr_SetString(PyExc_TypeError, "Cannot delete image format"); + return -1; } fmt = PyString_AsString(val); @@ -361,12 +575,16 @@ initmagick(void) if (PyType_Ready(&magick_ImageType) < 0) return; + if (PyType_Ready(&magick_DrawingWandType) < 0) + return; m = Py_InitModule3("magick", magick_methods, "Wrapper for the ImageMagick imaging library"); Py_INCREF(&magick_ImageType); PyModule_AddObject(m, "Image", (PyObject *)&magick_ImageType); + Py_INCREF(&magick_DrawingWandType); + PyModule_AddObject(m, "DrawingWand", (PyObject *)&magick_DrawingWandType); magick_add_module_constants(m); MagickWandGenesis(); diff --git a/src/calibre/utils/magick/magick_constants.h b/src/calibre/utils/magick/magick_constants.h index 5a5b18ed8f..d53158bd4c 100644 --- a/src/calibre/utils/magick/magick_constants.h +++ b/src/calibre/utils/magick/magick_constants.h @@ -226,4 +226,41 @@ static void magick_add_module_constants(PyObject *m) { PyModule_AddIntConstant(m, "LinearDodgeCompositeOp", 61); PyModule_AddIntConstant(m, "LinearBurnCompositeOp", 62); PyModule_AddIntConstant(m, "MathematicsCompositeOp", 63); + PyModule_AddIntConstant(m, "NoValue", 0); + PyModule_AddIntConstant(m, "XValue", 1); + PyModule_AddIntConstant(m, "XiValue", 1); + PyModule_AddIntConstant(m, "YValue", 2); + PyModule_AddIntConstant(m, "PsiValue", 2); + PyModule_AddIntConstant(m, "WidthValue", 4); + PyModule_AddIntConstant(m, "RhoValue", 4); + PyModule_AddIntConstant(m, "HeightValue", 8); + PyModule_AddIntConstant(m, "SigmaValue", 8); + PyModule_AddIntConstant(m, "ChiValue", 16); + PyModule_AddIntConstant(m, "XiNegative", 32); + PyModule_AddIntConstant(m, "XNegative", 32); + PyModule_AddIntConstant(m, "PsiNegative", 64); + PyModule_AddIntConstant(m, "YNegative", 64); + PyModule_AddIntConstant(m, "ChiNegative", 128); + PyModule_AddIntConstant(m, "PercentValue", 4096); + PyModule_AddIntConstant(m, "AspectValue", 8192); + PyModule_AddIntConstant(m, "NormalizeValue", 8192); + PyModule_AddIntConstant(m, "LessValue", 16384); + PyModule_AddIntConstant(m, "GreaterValue", 32768); + PyModule_AddIntConstant(m, "MinimumValue", 65536); + PyModule_AddIntConstant(m, "CorrelateNormalizeValue", 65536); + PyModule_AddIntConstant(m, "AreaValue", 131072); + PyModule_AddIntConstant(m, "DecimalValue", 262144); + PyModule_AddIntConstant(m, "AllValues", 2147483647); + PyModule_AddIntConstant(m, "UndefinedGravity", 0); + PyModule_AddIntConstant(m, "ForgetGravity", 0); + PyModule_AddIntConstant(m, "NorthWestGravity", 1); + PyModule_AddIntConstant(m, "NorthGravity", 2); + PyModule_AddIntConstant(m, "NorthEastGravity", 3); + PyModule_AddIntConstant(m, "WestGravity", 4); + PyModule_AddIntConstant(m, "CenterGravity", 5); + PyModule_AddIntConstant(m, "EastGravity", 6); + PyModule_AddIntConstant(m, "SouthWestGravity", 7); + PyModule_AddIntConstant(m, "SouthGravity", 8); + PyModule_AddIntConstant(m, "SouthEastGravity", 9); + PyModule_AddIntConstant(m, "StaticGravity", 10); }