diff --git a/resources/recipes/mainichi_it_news.recipe b/resources/recipes/mainichi_it_news.recipe
index 4c285a2c01..eddab149cd 100644
--- a/resources/recipes/mainichi_it_news.recipe
+++ b/resources/recipes/mainichi_it_news.recipe
@@ -1,4 +1,5 @@
from calibre.web.feeds.news import BasicNewsRecipe
+import re
class MainichiDailyITNews(BasicNewsRecipe):
title = u'\u6bce\u65e5\u65b0\u805e(IT&\u5bb6\u96fb)'
@@ -14,6 +15,7 @@ class MainichiDailyITNews(BasicNewsRecipe):
remove_tags_before = {'class':"NewsTitle"}
remove_tags = [{'class':"RelatedArticle"}]
+ remove_tags_after = {'class':"Credit"}
def parse_feeds(self):
@@ -29,4 +31,4 @@ class MainichiDailyITNews(BasicNewsRecipe):
index = curfeed.articles.index(d)
curfeed.articles[index:index+1] = []
- return feeds remove_tags_after = {'class':"Credit"}
+ return feeds
diff --git a/resources/recipes/ming_pao.recipe b/resources/recipes/ming_pao.recipe
index 385dbdbdb7..726181f57b 100644
--- a/resources/recipes/ming_pao.recipe
+++ b/resources/recipes/ming_pao.recipe
@@ -1,8 +1,9 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Eddie Lau'
'''
-modified from Singtao Toronto calibre recipe by rty
Change Log:
+2010/12/07: add entertainment section, use newspaper front page as ebook cover, suppress date display in section list
+ (to avoid wrong date display in case the user generates the ebook in a time zone different from HKT)
2010/11/22: add English section, remove eco-news section which is not updated daily, correct
ordering of articles
2010/11/12: add news image and eco-news section
@@ -17,14 +18,15 @@ from calibre.web.feeds.recipes import BasicNewsRecipe
from contextlib import nested
-from calibre import __appname__, strftime
+from calibre import __appname__
from calibre.ebooks.BeautifulSoup import BeautifulSoup
from calibre.ebooks.metadata.opf2 import OPFCreator
from calibre.ebooks.metadata.toc import TOC
from calibre.ebooks.metadata import MetaInformation
-from calibre.utils.date import now as nowf
class MPHKRecipe(BasicNewsRecipe):
+ IsKindleUsed = True # to avoid generating periodical in which CJK characters can't be displayed in section/article view
+
title = 'Ming Pao - Hong Kong'
oldest_article = 1
max_articles_per_feed = 100
@@ -39,13 +41,13 @@ class MPHKRecipe(BasicNewsRecipe):
encoding = 'Big5-HKSCS'
recursions = 0
conversion_options = {'linearize_tables':True}
- extra_css = 'img {display: block; margin-left: auto; margin-right: auto; margin-top: 10px; margin-bottom: 10px;}'
- #extra_css = 'img {float:right; margin:4px;}'
+ timefmt = ''
+ extra_css = 'img {display: block; margin-left: auto; margin-right: auto; margin-top: 10px; margin-bottom: 10px;} font>b {font-size:200%; font-weight:bold;}'
masthead_url = 'http://news.mingpao.com/image/portals_top_logo_news.gif'
keep_only_tags = [dict(name='h1'),
- #dict(name='font', attrs={'style':['font-size:14pt; line-height:160%;']}), # for entertainment page
+ dict(name='font', attrs={'style':['font-size:14pt; line-height:160%;']}), # for entertainment page title
dict(attrs={'class':['photo']}),
- dict(attrs={'id':['newscontent']}),
+ dict(attrs={'id':['newscontent']}), # entertainment page content
dict(attrs={'id':['newscontent01','newscontent02']})]
remove_tags = [dict(name='style'),
dict(attrs={'id':['newscontent135']})] # for the finance page
@@ -55,51 +57,68 @@ class MPHKRecipe(BasicNewsRecipe):
lambda match: '
'),
(re.compile(r'', re.DOTALL|re.IGNORECASE),
lambda match: '
'),
+ (re.compile(r'
', re.DOTALL|re.IGNORECASE), # for entertainment page
+ lambda match: '')
]
def image_url_processor(cls, baseurl, url):
# trick: break the url at the first occurance of digit, add an additional
# '_' at the front
# not working, may need to move this to preprocess_html() method
- #minIdx = 10000
- #i0 = url.find('0')
- #if i0 >= 0 and i0 < minIdx:
- # minIdx = i0
- #i1 = url.find('1')
- #if i1 >= 0 and i1 < minIdx:
- # minIdx = i1
- #i2 = url.find('2')
- #if i2 >= 0 and i2 < minIdx:
- # minIdx = i2
- #i3 = url.find('3')
- #if i3 >= 0 and i0 < minIdx:
- # minIdx = i3
- #i4 = url.find('4')
- #if i4 >= 0 and i4 < minIdx:
- # minIdx = i4
- #i5 = url.find('5')
- #if i5 >= 0 and i5 < minIdx:
- # minIdx = i5
- #i6 = url.find('6')
- #if i6 >= 0 and i6 < minIdx:
- # minIdx = i6
- #i7 = url.find('7')
- #if i7 >= 0 and i7 < minIdx:
- # minIdx = i7
- #i8 = url.find('8')
- #if i8 >= 0 and i8 < minIdx:
- # minIdx = i8
- #i9 = url.find('9')
- #if i9 >= 0 and i9 < minIdx:
- # minIdx = i9
- #return url[0:minIdx] + '_' + url[minIdx+1:]
+# minIdx = 10000
+# i0 = url.find('0')
+# if i0 >= 0 and i0 < minIdx:
+# minIdx = i0
+# i1 = url.find('1')
+# if i1 >= 0 and i1 < minIdx:
+# minIdx = i1
+# i2 = url.find('2')
+# if i2 >= 0 and i2 < minIdx:
+# minIdx = i2
+# i3 = url.find('3')
+# if i3 >= 0 and i0 < minIdx:
+# minIdx = i3
+# i4 = url.find('4')
+# if i4 >= 0 and i4 < minIdx:
+# minIdx = i4
+# i5 = url.find('5')
+# if i5 >= 0 and i5 < minIdx:
+# minIdx = i5
+# i6 = url.find('6')
+# if i6 >= 0 and i6 < minIdx:
+# minIdx = i6
+# i7 = url.find('7')
+# if i7 >= 0 and i7 < minIdx:
+# minIdx = i7
+# i8 = url.find('8')
+# if i8 >= 0 and i8 < minIdx:
+# minIdx = i8
+# i9 = url.find('9')
+# if i9 >= 0 and i9 < minIdx:
+# minIdx = i9
return url
- def get_fetchdate(self):
+ def get_dtlocal(self):
dt_utc = datetime.datetime.utcnow()
# convert UTC to local hk time - at around HKT 6.00am, all news are available
dt_local = dt_utc - datetime.timedelta(-2.0/24)
- return dt_local.strftime("%Y%m%d")
+ return dt_local
+
+ def get_fetchdate(self):
+ return self.get_dtlocal().strftime("%Y%m%d")
+
+ def get_fetchday(self):
+ # convert UTC to local hk time - at around HKT 6.00am, all news are available
+ return self.get_dtlocal().strftime("%d")
+
+ def get_cover_url(self):
+ cover = 'http://news.mingpao.com/' + self.get_fetchdate() + '/' + self.get_fetchdate() + '_' + self.get_fetchday() + 'gacov.jpg'
+ br = BasicNewsRecipe.get_browser()
+ try:
+ br.open(cover)
+ except:
+ cover = None
+ return cover
def parse_index(self):
feeds = []
@@ -127,9 +146,9 @@ class MPHKRecipe(BasicNewsRecipe):
# if eco_articles:
# feeds.append((u'\u74b0\u4fdd Eco News', eco_articles))
# special - entertainment
- #ent_articles = self.parse_ent_section('http://ol.mingpao.com/cfm/star1.cfm')
- #if ent_articles:
- # feeds.append(('Entertainment', ent_articles))
+ ent_articles = self.parse_ent_section('http://ol.mingpao.com/cfm/star1.cfm')
+ if ent_articles:
+ feeds.append((u'\u5f71\u8996 Entertainment', ent_articles))
return feeds
def parse_section(self, url):
@@ -164,6 +183,7 @@ class MPHKRecipe(BasicNewsRecipe):
return current_articles
def parse_eco_section(self, url):
+ dateStr = self.get_fetchdate()
soup = self.index_to_soup(url)
divs = soup.findAll(attrs={'class': ['bullet']})
current_articles = []
@@ -173,23 +193,25 @@ class MPHKRecipe(BasicNewsRecipe):
title = self.tag_to_string(a)
url = a.get('href', False)
url = 'http://tssl.mingpao.com/htm/marketing/eco/cfm/' +url
- if url not in included_urls and url.rfind('Redirect') == -1:
+ if url not in included_urls and url.rfind('Redirect') == -1 and not url.rfind('.txt') == -1 and not url.rfind(dateStr) == -1:
current_articles.append({'title': title, 'url': url, 'description':''})
included_urls.append(url)
return current_articles
- #def parse_ent_section(self, url):
- # dateStr = self.get_fetchdate()
- # soup = self.index_to_soup(url)
- # a = soup.findAll('a', href=True)
- # current_articles = []
- # included_urls = []
- # for i in a:
- # title = self.tag_to_string(i)
- # url = 'http://ol.mingpao.com/cfm/' + i.get('href', False)
- # if url not in included_urls and not url.rfind('.txt') == -1 and not url.rfind(dateStr) == -1 and not title == '':
- # current_articles.append({'title': title, 'url': url, 'description': ''})
- # return current_articles
+ def parse_ent_section(self, url):
+ soup = self.index_to_soup(url)
+ a = soup.findAll('a', href=True)
+ a.reverse()
+ current_articles = []
+ included_urls = []
+ for i in a:
+ title = self.tag_to_string(i)
+ url = 'http://ol.mingpao.com/cfm/' + i.get('href', False)
+ if (url not in included_urls) and (not url.rfind('.txt') == -1) and (not url.rfind('star') == -1):
+ current_articles.append({'title': title, 'url': url, 'description': ''})
+ included_urls.append(url)
+ current_articles.reverse()
+ return current_articles
def preprocess_html(self, soup):
for item in soup.findAll(style=True):
@@ -201,21 +223,26 @@ class MPHKRecipe(BasicNewsRecipe):
return soup
def create_opf(self, feeds, dir=None):
- #super(MPHKRecipe,self).create_opf(feeds, dir)
+ if self.IsKindleUsed == False:
+ super(MPHKRecipe,self).create_opf(feeds, dir)
+ return
if dir is None:
dir = self.output_dir
title = self.short_title()
- if self.output_profile.periodical_date_in_title:
- title += strftime(self.timefmt)
+ title += ' ' + self.get_fetchdate()
+ #if self.output_profile.periodical_date_in_title:
+ # title += strftime(self.timefmt)
mi = MetaInformation(title, [__appname__])
mi.publisher = __appname__
mi.author_sort = __appname__
mi.publication_type = self.publication_type+':'+self.short_title()
- mi.timestamp = nowf()
+ #mi.timestamp = nowf()
+ mi.timestamp = self.get_dtlocal()
mi.comments = self.description
if not isinstance(mi.comments, unicode):
mi.comments = mi.comments.decode('utf-8', 'replace')
- mi.pubdate = nowf()
+ #mi.pubdate = nowf()
+ mi.pubdate = self.get_dtlocal()
opf_path = os.path.join(dir, 'index.opf')
ncx_path = os.path.join(dir, 'index.ncx')
opf = OPFCreator(dir, mi)
diff --git a/resources/recipes/the_h.recipe b/resources/recipes/the_h.recipe
index dbfad7e32a..28a1571dc5 100644
--- a/resources/recipes/the_h.recipe
+++ b/resources/recipes/the_h.recipe
@@ -14,7 +14,7 @@ class TheHeiseOnline(BasicNewsRecipe):
oldest_article = 3
description = 'In association with Heise Online'
publisher = 'Heise Media UK Ltd.'
- category = 'news, technology, security'
+ category = 'news, technology, security, OSS, internet'
max_articles_per_feed = 100
language = 'en'
encoding = 'utf-8'
@@ -27,6 +27,12 @@ class TheHeiseOnline(BasicNewsRecipe):
feeds = [
(u'The H News Feed', u'http://www.h-online.com/news/atom.xml')
]
+ cover_url = 'http://www.h-online.com/icons/logo_theH.gif'
+
+ remove_tags = [
+ dict(id="logo"),
+ dict(id="footer")
+ ]
def print_version(self, url):
return url + '?view=print'
diff --git a/resources/recipes/toyokeizai.recipe b/resources/recipes/toyokeizai.recipe
new file mode 100644
index 0000000000..395a8bb9b7
--- /dev/null
+++ b/resources/recipes/toyokeizai.recipe
@@ -0,0 +1,68 @@
+__license__ = 'GPL v3'
+__copyright__ = '2010, Hiroshi Miura '
+'''
+www.toyokeizai.net
+'''
+
+from calibre.web.feeds.news import BasicNewsRecipe
+import re
+
+class Toyokeizai(BasicNewsRecipe):
+ title = u'ToyoKeizai News'
+ __author__ = 'Hiroshi Miura'
+ oldest_article = 1
+ max_articles_per_feed = 50
+ description = 'Japanese traditional economy and business magazine, only for advanced subscribers supported'
+ publisher = 'Toyokeizai Shinbun Sha'
+ category = 'economy, magazine, japan'
+ language = 'ja'
+ encoding = 'euc-jp'
+ index = 'http://member.toyokeizai.net/news/'
+ remove_javascript = True
+ no_stylesheets = True
+ masthead_title = u'TOYOKEIZAI'
+ needs_subscription = True
+ timefmt = '[%y/%m/%d]'
+ recursions = 5
+ match_regexps =[ r'page/\d+']
+
+ keep_only_tags = [
+ dict(name='div', attrs={'class':['news']}),
+ dict(name='div', attrs={'class':["news_cont"]}),
+ dict(name='div', attrs={'class':["news_con"]}),
+# dict(name='div', attrs={'class':["norightsMessage"]})
+ ]
+ remove_tags = [{'class':"mt35 mgz"},
+ {'class':"mt20 newzia"},
+ {'class':"mt20 fontS"},
+ {'class':"bk_btn_m"},
+ dict(id='newzia_connect_member')
+ ]
+
+ def parse_index(self):
+ feeds = []
+ soup = self.index_to_soup(self.index)
+ topstories = soup.find('ul',attrs={'class':'list6'})
+ if topstories:
+ newsarticles = []
+ for itt in topstories.findAll('li'):
+ itema = itt.find('a',href=True)
+ itemd = itt.find('span')
+ newsarticles.append({
+ 'title' :itema.string
+ ,'date' :re.compile(r"\- ").sub("",itemd.string)
+ ,'url' :'http://member.toyokeizai.net' + itema['href']
+ ,'description':itema['title']
+ })
+ feeds.append(('news', newsarticles))
+ return feeds
+
+ def get_browser(self):
+ br = BasicNewsRecipe.get_browser()
+ if self.username is not None and self.password is not None:
+ br.open('http://member.toyokeizai.net/norights/form/')
+ br.select_form(nr=0)
+ br['kaiin_id'] = self.username
+ br['password'] = self.password
+ res = br.submit()
+ return br
diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py
index 5b6b79e3df..a99c9df125 100644
--- a/src/calibre/library/caches.py
+++ b/src/calibre/library/caches.py
@@ -129,6 +129,7 @@ class CoverCache(Thread): # {{{
self.keep_running = True
self.cache = {}
self.lock = RLock()
+ self.allowed_ids = frozenset([])
self.null_image = QImage()
def stop(self):
@@ -175,6 +176,11 @@ class CoverCache(Thread): # {{{
break
for id_ in ids:
time.sleep(0.050) # Limit 20/second to not overwhelm the GUI
+ if not self.keep_running:
+ return
+ with self.lock:
+ if id_ not in self.allowed_ids:
+ continue
try:
img = self._image_for_id(id_)
except:
@@ -193,6 +199,7 @@ class CoverCache(Thread): # {{{
def set_cache(self, ids):
with self.lock:
+ self.allowed_ids = frozenset(ids)
already_loaded = set([])
for id in self.cache.keys():
if id in ids:
@@ -213,8 +220,9 @@ class CoverCache(Thread): # {{{
def refresh(self, ids):
with self.lock:
for id_ in ids:
- self.cache.pop(id_, None)
- self.load_queue.put(id_)
+ cover = self.cache.pop(id_, None)
+ if cover is not None:
+ self.load_queue.put(id_)
# }}}
### Global utility function for get_match here and in gui2/library.py
diff --git a/src/calibre/utils/magick/draw.py b/src/calibre/utils/magick/draw.py
index d3cbd58c7d..c03a8660c8 100644
--- a/src/calibre/utils/magick/draw.py
+++ b/src/calibre/utils/magick/draw.py
@@ -54,19 +54,23 @@ def save_cover_data_to(data, path, bgcolor='#ffffff', resize_to=None,
changed = True
if not changed:
changed = fmt != orig_fmt
+
+ ret = None
if return_data:
+ ret = data
if changed:
if hasattr(img, 'set_compression_quality') and fmt == 'jpg':
img.set_compression_quality(compression_quality)
- return img.export(fmt)
- return data
- if changed:
- if hasattr(img, 'set_compression_quality') and fmt == 'jpg':
- img.set_compression_quality(compression_quality)
- img.save(path)
+ ret = img.export(fmt)
else:
- with lopen(path, 'wb') as f:
- f.write(data)
+ if changed:
+ if hasattr(img, 'set_compression_quality') and fmt == 'jpg':
+ img.set_compression_quality(compression_quality)
+ img.save(path)
+ else:
+ with lopen(path, 'wb') as f:
+ f.write(data)
+ return ret
def thumbnail(data, width=120, height=120, bgcolor='#ffffff', fmt='jpg'):
img = Image()
diff --git a/src/calibre/utils/magick/magick.c b/src/calibre/utils/magick/magick.c
index 9dac37f2ff..fd9563529a 100644
--- a/src/calibre/utils/magick/magick.c
+++ b/src/calibre/utils/magick/magick.c
@@ -5,6 +5,9 @@
#include "magick_constants.h"
+// Ensure that the underlying MagickWand has not been deleted
+#define NULL_CHECK(x) if(self->wand == NULL) {PyErr_SetString(PyExc_ValueError, "Underlying ImageMagick Wand has been destroyed"); return x; }
+
// magick_set_exception {{{
PyObject* magick_set_exception(MagickWand *wand) {
ExceptionType ext;
@@ -54,6 +57,7 @@ magick_PixelWand_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
static PyObject *
magick_PixelWand_color_getter(magick_PixelWand *self, void *closure) {
const char *fp;
+ NULL_CHECK(NULL);
fp = PixelGetColorAsNormalizedString(self->wand);
return Py_BuildValue("s", fp);
}
@@ -62,6 +66,8 @@ static int
magick_PixelWand_color_setter(magick_PixelWand *self, PyObject *val, void *closure) {
char *fmt;
+ NULL_CHECK(-1);
+
if (val == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete PixelWand color");
return -1;
@@ -80,8 +86,21 @@ magick_PixelWand_color_setter(magick_PixelWand *self, PyObject *val, void *closu
// }}}
+// PixelWand.destroy {{{
+
+static PyObject *
+magick_PixelWand_destroy(magick_PixelWand *self, PyObject *args, PyObject *kwargs) {
+ NULL_CHECK(NULL)
+ self->wand = DestroyPixelWand(self->wand);
+ Py_RETURN_NONE;
+}
+// }}}
+
// PixelWand attr list {{{
static PyMethodDef magick_PixelWand_methods[] = {
+ {"destroy", (PyCFunction)magick_PixelWand_destroy, METH_VARARGS,
+ "Destroy the underlying ImageMagick Wand. WARNING: After using this method, all methods on this object will raise an exception."},
+
{NULL} /* Sentinel */
};
@@ -175,10 +194,21 @@ magick_DrawingWand_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return (PyObject *)self;
}
+// DrawingWand.destroy {{{
+
+static PyObject *
+magick_DrawingWand_destroy(magick_DrawingWand *self, PyObject *args, PyObject *kwargs) {
+ NULL_CHECK(NULL)
+ self->wand = DestroyDrawingWand(self->wand);
+ Py_RETURN_NONE;
+}
+// }}}
+
// DrawingWand.font {{{
static PyObject *
magick_DrawingWand_font_getter(magick_DrawingWand *self, void *closure) {
const char *fp;
+ NULL_CHECK(NULL);
fp = DrawGetFont(self->wand);
return Py_BuildValue("s", fp);
}
@@ -186,6 +216,7 @@ magick_DrawingWand_font_getter(magick_DrawingWand *self, void *closure) {
static int
magick_DrawingWand_font_setter(magick_DrawingWand *self, PyObject *val, void *closure) {
char *fmt;
+ NULL_CHECK(-1);
if (val == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete DrawingWand font");
@@ -208,11 +239,13 @@ magick_DrawingWand_font_setter(magick_DrawingWand *self, PyObject *val, void *cl
// DrawingWand.font_size {{{
static PyObject *
magick_DrawingWand_fontsize_getter(magick_DrawingWand *self, void *closure) {
+ NULL_CHECK(NULL)
return Py_BuildValue("d", DrawGetFontSize(self->wand));
}
static int
magick_DrawingWand_fontsize_setter(magick_DrawingWand *self, PyObject *val, void *closure) {
+ NULL_CHECK(-1)
if (val == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete DrawingWand fontsize");
return -1;
@@ -233,12 +266,14 @@ magick_DrawingWand_fontsize_setter(magick_DrawingWand *self, PyObject *val, void
// DrawingWand.text_antialias {{{
static PyObject *
magick_DrawingWand_textantialias_getter(magick_DrawingWand *self, void *closure) {
+ NULL_CHECK(NULL);
if (DrawGetTextAntialias(self->wand)) Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
static int
magick_DrawingWand_textantialias_setter(magick_DrawingWand *self, PyObject *val, void *closure) {
+ NULL_CHECK(-1);
if (val == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete DrawingWand textantialias");
return -1;
@@ -253,6 +288,7 @@ magick_DrawingWand_textantialias_setter(magick_DrawingWand *self, PyObject *val,
// DrawingWand.gravity {{{
static PyObject *
magick_DrawingWand_gravity_getter(magick_DrawingWand *self, void *closure) {
+ NULL_CHECK(NULL);
return Py_BuildValue("n", DrawGetGravity(self->wand));
}
@@ -260,6 +296,8 @@ static int
magick_DrawingWand_gravity_setter(magick_DrawingWand *self, PyObject *val, void *closure) {
int grav;
+ NULL_CHECK(-1);
+
if (val == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete DrawingWand gravity");
return -1;
@@ -281,6 +319,9 @@ magick_DrawingWand_gravity_setter(magick_DrawingWand *self, PyObject *val, void
// DrawingWand attr list {{{
static PyMethodDef magick_DrawingWand_methods[] = {
+ {"destroy", (PyCFunction)magick_DrawingWand_destroy, METH_VARARGS,
+ "Destroy the underlying ImageMagick Wand. WARNING: After using this method, all methods on this object will raise an exception."},
+
{NULL} /* Sentinel */
};
@@ -402,6 +443,7 @@ magick_Image_load(magick_Image *self, PyObject *args, PyObject *kwargs) {
Py_ssize_t dlen;
MagickBooleanType res;
+ NULL_CHECK(NULL)
if (!PyArg_ParseTuple(args, "s#", &data, &dlen)) return NULL;
res = MagickReadImageBlob(self->wand, data, dlen);
@@ -420,7 +462,8 @@ magick_Image_read(magick_Image *self, PyObject *args, PyObject *kwargs) {
const char *data;
MagickBooleanType res;
- if (!PyArg_ParseTuple(args, "s", &data)) return NULL;
+ NULL_CHECK(NULL)
+ if (!PyArg_ParseTuple(args, "s", &data)) return NULL;
res = MagickReadImage(self->wand, data);
@@ -441,6 +484,8 @@ magick_Image_create_canvas(magick_Image *self, PyObject *args, PyObject *kwargs)
PixelWand *pw;
MagickBooleanType res = MagickFalse;
+ NULL_CHECK(NULL)
+
if (!PyArg_ParseTuple(args, "nns", &width, &height, &bgcolor)) return NULL;
pw = NewPixelWand();
@@ -464,6 +509,8 @@ magick_Image_font_metrics(magick_Image *self, PyObject *args, PyObject *kwargs)
DrawingWand *dw;
double *metrics;
+ NULL_CHECK(NULL)
+
if (!PyArg_ParseTuple(args, "O!s", &magick_DrawingWandType, &dw_, &text)) return NULL;
dw = ((magick_DrawingWand*)dw_)->wand;
if (!IsDrawingWand(dw)) { PyErr_SetString(PyExc_TypeError, "Invalid drawing wand"); return NULL; }
@@ -490,6 +537,8 @@ magick_Image_annotate(magick_Image *self, PyObject *args, PyObject *kwargs) {
PyObject *dw_;
DrawingWand *dw;
double x, y, angle;
+
+ NULL_CHECK(NULL)
if (!PyArg_ParseTuple(args, "O!ddds", &magick_DrawingWandType, &dw_, &x, &y, &angle, &text)) return NULL;
dw = ((magick_DrawingWand*)dw_)->wand;
@@ -510,6 +559,8 @@ magick_Image_export(magick_Image *self, PyObject *args, PyObject *kwargs) {
PyObject *ans;
size_t len = 0;
+ NULL_CHECK(NULL)
+
if (!PyArg_ParseTuple(args, "s", &fmt)) return NULL;
if (!MagickSetFormat(self->wand, fmt)) {
@@ -533,6 +584,8 @@ magick_Image_export(magick_Image *self, PyObject *args, PyObject *kwargs) {
static PyObject *
magick_Image_size_getter(magick_Image *self, void *closure) {
size_t width, height;
+ NULL_CHECK(NULL)
+
width = MagickGetImageWidth(self->wand);
height = MagickGetImageHeight(self->wand);
return Py_BuildValue("nn", width, height);
@@ -545,6 +598,9 @@ magick_Image_size_setter(magick_Image *self, PyObject *val, void *closure) {
double blur;
MagickBooleanType res;
+ NULL_CHECK(-1)
+
+
if (val == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete image size");
return -1;
@@ -592,6 +648,8 @@ magick_Image_size_setter(magick_Image *self, PyObject *val, void *closure) {
static PyObject *
magick_Image_format_getter(magick_Image *self, void *closure) {
const char *fmt;
+ NULL_CHECK(NULL)
+
fmt = MagickGetImageFormat(self->wand);
return Py_BuildValue("s", fmt);
}
@@ -599,6 +657,8 @@ magick_Image_format_getter(magick_Image *self, void *closure) {
static int
magick_Image_format_setter(magick_Image *self, PyObject *val, void *closure) {
char *fmt;
+ NULL_CHECK(-1)
+
if (val == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete image format");
@@ -628,6 +688,8 @@ magick_Image_distort(magick_Image *self, PyObject *args, PyObject *kwargs) {
MagickBooleanType res;
double *arguments = NULL;
+ NULL_CHECK(NULL)
+
if (!PyArg_ParseTuple(args, "iOO", &method, &argv, &bestfit)) return NULL;
if (!PySequence_Check(argv)) { PyErr_SetString(PyExc_TypeError, "arguments must be a sequence"); return NULL; }
@@ -658,6 +720,8 @@ static PyObject *
magick_Image_trim(magick_Image *self, PyObject *args, PyObject *kwargs) {
double fuzz;
+ NULL_CHECK(NULL)
+
if (!PyArg_ParseTuple(args, "d", &fuzz)) return NULL;
if (!MagickTrimImage(self->wand, fuzz)) return magick_set_exception(self->wand);
@@ -672,6 +736,8 @@ static PyObject *
magick_Image_thumbnail(magick_Image *self, PyObject *args, PyObject *kwargs) {
Py_ssize_t width, height;
+ NULL_CHECK(NULL)
+
if (!PyArg_ParseTuple(args, "nn", &width, &height)) return NULL;
if (!MagickThumbnailImage(self->wand, width, height)) return magick_set_exception(self->wand);
@@ -686,6 +752,8 @@ static PyObject *
magick_Image_crop(magick_Image *self, PyObject *args, PyObject *kwargs) {
Py_ssize_t width, height, x, y;
+ NULL_CHECK(NULL)
+
if (!PyArg_ParseTuple(args, "nnnn", &width, &height, &x, &y)) return NULL;
if (!MagickCropImage(self->wand, width, height, x, y)) return magick_set_exception(self->wand);
@@ -701,6 +769,8 @@ magick_Image_set_border_color(magick_Image *self, PyObject *args, PyObject *kwar
PyObject *obj;
magick_PixelWand *pw;
+ NULL_CHECK(NULL)
+
if (!PyArg_ParseTuple(args, "O!", &magick_PixelWandType, &obj)) return NULL;
pw = (magick_PixelWand*)obj;
if (!IsPixelWand(pw->wand)) { PyErr_SetString(PyExc_TypeError, "Invalid PixelWand"); return NULL; }
@@ -719,6 +789,8 @@ magick_Image_rotate(magick_Image *self, PyObject *args, PyObject *kwargs) {
magick_PixelWand *pw;
double degrees;
+ NULL_CHECK(NULL)
+
if (!PyArg_ParseTuple(args, "O!d", &magick_PixelWandType, &obj, °rees)) return NULL;
pw = (magick_PixelWand*)obj;
if (!IsPixelWand(pw->wand)) { PyErr_SetString(PyExc_TypeError, "Invalid PixelWand"); return NULL; }
@@ -735,6 +807,8 @@ static PyObject *
magick_Image_set_page(magick_Image *self, PyObject *args, PyObject *kwargs) {
Py_ssize_t width, height, x, y;
+ NULL_CHECK(NULL)
+
if (!PyArg_ParseTuple(args, "nnnn", &width, &height, &x, &y)) return NULL;
if (!MagickSetImagePage(self->wand, width, height, x, y)) return magick_set_exception(self->wand);
@@ -749,6 +823,8 @@ static PyObject *
magick_Image_set_compression_quality(magick_Image *self, PyObject *args, PyObject *kwargs) {
Py_ssize_t quality;
+ NULL_CHECK(NULL)
+
if (!PyArg_ParseTuple(args, "n", &quality)) return NULL;
if (!MagickSetImageCompressionQuality(self->wand, quality)) return magick_set_exception(self->wand);
@@ -767,6 +843,8 @@ magick_Image_has_transparent_pixels(magick_Image *self, PyObject *args, PyObject
size_t r, c, width, height;
double alpha;
+ NULL_CHECK(NULL)
+
height = MagickGetImageHeight(self->wand);
pi = NewPixelIterator(self->wand);
@@ -790,6 +868,8 @@ magick_Image_has_transparent_pixels(magick_Image *self, PyObject *args, PyObject
static PyObject *
magick_Image_normalize(magick_Image *self, PyObject *args, PyObject *kwargs) {
+ NULL_CHECK(NULL)
+
if (!MagickNormalizeImage(self->wand)) return magick_set_exception(self->wand);
Py_RETURN_NONE;
@@ -804,6 +884,8 @@ magick_Image_add_border(magick_Image *self, PyObject *args, PyObject *kwargs) {
PyObject *obj;
magick_PixelWand *pw;
+ NULL_CHECK(NULL)
+
if (!PyArg_ParseTuple(args, "O!nn", &magick_PixelWandType, &obj, &dx, &dy)) return NULL;
pw = (magick_PixelWand*)obj;
if (!IsPixelWand(pw->wand)) { PyErr_SetString(PyExc_TypeError, "Invalid PixelWand"); return NULL; }
@@ -820,6 +902,8 @@ static PyObject *
magick_Image_sharpen(magick_Image *self, PyObject *args, PyObject *kwargs) {
double radius, sigma;
+ NULL_CHECK(NULL)
+
if (!PyArg_ParseTuple(args, "dd", &radius, &sigma)) return NULL;
if (!MagickSharpenImage(self->wand, radius, sigma)) return magick_set_exception(self->wand);
@@ -835,6 +919,9 @@ magick_Image_quantize(magick_Image *self, PyObject *args, PyObject *kwargs) {
Py_ssize_t number_colors, treedepth;
int colorspace;
PyObject *dither, *measure_error;
+
+ NULL_CHECK(NULL)
+
if (!PyArg_ParseTuple(args, "ninOO", &number_colors, &colorspace, &treedepth, &dither, &measure_error)) return NULL;
@@ -848,6 +935,8 @@ magick_Image_quantize(magick_Image *self, PyObject *args, PyObject *kwargs) {
static PyObject *
magick_Image_despeckle(magick_Image *self, PyObject *args, PyObject *kwargs) {
+ NULL_CHECK(NULL)
+
if (!MagickDespeckleImage(self->wand)) return magick_set_exception(self->wand);
Py_RETURN_NONE;
@@ -857,6 +946,8 @@ magick_Image_despeckle(magick_Image *self, PyObject *args, PyObject *kwargs) {
// Image.type {{{
static PyObject *
magick_Image_type_getter(magick_Image *self, void *closure) {
+ NULL_CHECK(NULL)
+
return Py_BuildValue("n", MagickGetImageType(self->wand));
}
@@ -864,6 +955,8 @@ static int
magick_Image_type_setter(magick_Image *self, PyObject *val, void *closure) {
int type;
+ NULL_CHECK(-1)
+
if (val == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete image type");
return -1;
@@ -885,8 +978,21 @@ magick_Image_type_setter(magick_Image *self, PyObject *val, void *closure) {
// }}}
+// Image.destroy {{{
+
+static PyObject *
+magick_Image_destroy(magick_Image *self, PyObject *args, PyObject *kwargs) {
+ NULL_CHECK(NULL)
+ self->wand = DestroyMagickWand(self->wand);
+ Py_RETURN_NONE;
+}
+// }}}
+
// Image attr list {{{
static PyMethodDef magick_Image_methods[] = {
+ {"destroy", (PyCFunction)magick_Image_destroy, METH_VARARGS,
+ "Destroy the underlying ImageMagick Wand. WARNING: After using this method, all methods on this object will raise an exception."},
+
{"load", (PyCFunction)magick_Image_load, METH_VARARGS,
"Load an image from a byte buffer (string)"
},
@@ -1001,6 +1107,7 @@ static PyGetSetDef magick_Image_getsetters[] = {
// }}}
+
static PyTypeObject magick_ImageType = { // {{{
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
@@ -1053,6 +1160,9 @@ magick_Image_compose(magick_Image *self, PyObject *args, PyObject *kwargs)
magick_Image *src;
MagickBooleanType res = MagickFalse;
+ NULL_CHECK(NULL)
+
+
if (!PyArg_ParseTuple(args, "O!nnO", &magick_ImageType, &img, &left, &top, &op_)) return NULL;
src = (magick_Image*)img;
if (!IsMagickWand(src->wand)) {PyErr_SetString(PyExc_TypeError, "Not a valid ImageMagick wand"); return NULL;}
@@ -1078,6 +1188,8 @@ magick_Image_copy(magick_Image *self, PyObject *args, PyObject *kwargs)
PyObject *img;
magick_Image *src;
+ NULL_CHECK(NULL)
+
if (!PyArg_ParseTuple(args, "O!", &magick_ImageType, &img)) return NULL;
src = (magick_Image*)img;
if (!IsMagickWand(src->wand)) {PyErr_SetString(PyExc_TypeError, "Not a valid ImageMagick wand"); return NULL;}
@@ -1153,3 +1265,4 @@ initmagick(void)
MagickWandGenesis();
}
// }}}
+