Merge from trunk

This commit is contained in:
Sengian 2010-12-08 20:50:50 +01:00
commit bb2d2a6641
7 changed files with 303 additions and 75 deletions

View File

@ -1,4 +1,5 @@
from calibre.web.feeds.news import BasicNewsRecipe from calibre.web.feeds.news import BasicNewsRecipe
import re
class MainichiDailyITNews(BasicNewsRecipe): class MainichiDailyITNews(BasicNewsRecipe):
title = u'\u6bce\u65e5\u65b0\u805e(IT&\u5bb6\u96fb)' title = u'\u6bce\u65e5\u65b0\u805e(IT&\u5bb6\u96fb)'
@ -14,6 +15,7 @@ class MainichiDailyITNews(BasicNewsRecipe):
remove_tags_before = {'class':"NewsTitle"} remove_tags_before = {'class':"NewsTitle"}
remove_tags = [{'class':"RelatedArticle"}] remove_tags = [{'class':"RelatedArticle"}]
remove_tags_after = {'class':"Credit"}
def parse_feeds(self): def parse_feeds(self):
@ -29,4 +31,4 @@ class MainichiDailyITNews(BasicNewsRecipe):
index = curfeed.articles.index(d) index = curfeed.articles.index(d)
curfeed.articles[index:index+1] = [] curfeed.articles[index:index+1] = []
return feeds remove_tags_after = {'class':"Credit"} return feeds

View File

@ -1,8 +1,9 @@
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2010, Eddie Lau' __copyright__ = '2010, Eddie Lau'
''' '''
modified from Singtao Toronto calibre recipe by rty
Change Log: 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 2010/11/22: add English section, remove eco-news section which is not updated daily, correct
ordering of articles ordering of articles
2010/11/12: add news image and eco-news section 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 contextlib import nested
from calibre import __appname__, strftime from calibre import __appname__
from calibre.ebooks.BeautifulSoup import BeautifulSoup from calibre.ebooks.BeautifulSoup import BeautifulSoup
from calibre.ebooks.metadata.opf2 import OPFCreator from calibre.ebooks.metadata.opf2 import OPFCreator
from calibre.ebooks.metadata.toc import TOC from calibre.ebooks.metadata.toc import TOC
from calibre.ebooks.metadata import MetaInformation from calibre.ebooks.metadata import MetaInformation
from calibre.utils.date import now as nowf
class MPHKRecipe(BasicNewsRecipe): 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' title = 'Ming Pao - Hong Kong'
oldest_article = 1 oldest_article = 1
max_articles_per_feed = 100 max_articles_per_feed = 100
@ -39,13 +41,13 @@ class MPHKRecipe(BasicNewsRecipe):
encoding = 'Big5-HKSCS' encoding = 'Big5-HKSCS'
recursions = 0 recursions = 0
conversion_options = {'linearize_tables':True} conversion_options = {'linearize_tables':True}
extra_css = 'img {display: block; margin-left: auto; margin-right: auto; margin-top: 10px; margin-bottom: 10px;}' timefmt = ''
#extra_css = 'img {float:right; margin:4px;}' 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' masthead_url = 'http://news.mingpao.com/image/portals_top_logo_news.gif'
keep_only_tags = [dict(name='h1'), 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={'class':['photo']}),
dict(attrs={'id':['newscontent']}), dict(attrs={'id':['newscontent']}), # entertainment page content
dict(attrs={'id':['newscontent01','newscontent02']})] dict(attrs={'id':['newscontent01','newscontent02']})]
remove_tags = [dict(name='style'), remove_tags = [dict(name='style'),
dict(attrs={'id':['newscontent135']})] # for the finance page dict(attrs={'id':['newscontent135']})] # for the finance page
@ -55,51 +57,68 @@ class MPHKRecipe(BasicNewsRecipe):
lambda match: '<h1>'), lambda match: '<h1>'),
(re.compile(r'</h5>', re.DOTALL|re.IGNORECASE), (re.compile(r'</h5>', re.DOTALL|re.IGNORECASE),
lambda match: '</h1>'), lambda match: '</h1>'),
(re.compile(r'<p><a href=.+?</a></p>', re.DOTALL|re.IGNORECASE), # for entertainment page
lambda match: '')
] ]
def image_url_processor(cls, baseurl, url): def image_url_processor(cls, baseurl, url):
# trick: break the url at the first occurance of digit, add an additional # trick: break the url at the first occurance of digit, add an additional
# '_' at the front # '_' at the front
# not working, may need to move this to preprocess_html() method # not working, may need to move this to preprocess_html() method
#minIdx = 10000 # minIdx = 10000
#i0 = url.find('0') # i0 = url.find('0')
#if i0 >= 0 and i0 < minIdx: # if i0 >= 0 and i0 < minIdx:
# minIdx = i0 # minIdx = i0
#i1 = url.find('1') # i1 = url.find('1')
#if i1 >= 0 and i1 < minIdx: # if i1 >= 0 and i1 < minIdx:
# minIdx = i1 # minIdx = i1
#i2 = url.find('2') # i2 = url.find('2')
#if i2 >= 0 and i2 < minIdx: # if i2 >= 0 and i2 < minIdx:
# minIdx = i2 # minIdx = i2
#i3 = url.find('3') # i3 = url.find('3')
#if i3 >= 0 and i0 < minIdx: # if i3 >= 0 and i0 < minIdx:
# minIdx = i3 # minIdx = i3
#i4 = url.find('4') # i4 = url.find('4')
#if i4 >= 0 and i4 < minIdx: # if i4 >= 0 and i4 < minIdx:
# minIdx = i4 # minIdx = i4
#i5 = url.find('5') # i5 = url.find('5')
#if i5 >= 0 and i5 < minIdx: # if i5 >= 0 and i5 < minIdx:
# minIdx = i5 # minIdx = i5
#i6 = url.find('6') # i6 = url.find('6')
#if i6 >= 0 and i6 < minIdx: # if i6 >= 0 and i6 < minIdx:
# minIdx = i6 # minIdx = i6
#i7 = url.find('7') # i7 = url.find('7')
#if i7 >= 0 and i7 < minIdx: # if i7 >= 0 and i7 < minIdx:
# minIdx = i7 # minIdx = i7
#i8 = url.find('8') # i8 = url.find('8')
#if i8 >= 0 and i8 < minIdx: # if i8 >= 0 and i8 < minIdx:
# minIdx = i8 # minIdx = i8
#i9 = url.find('9') # i9 = url.find('9')
#if i9 >= 0 and i9 < minIdx: # if i9 >= 0 and i9 < minIdx:
# minIdx = i9 # minIdx = i9
#return url[0:minIdx] + '_' + url[minIdx+1:]
return url return url
def get_fetchdate(self): def get_dtlocal(self):
dt_utc = datetime.datetime.utcnow() dt_utc = datetime.datetime.utcnow()
# convert UTC to local hk time - at around HKT 6.00am, all news are available # convert UTC to local hk time - at around HKT 6.00am, all news are available
dt_local = dt_utc - datetime.timedelta(-2.0/24) 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): def parse_index(self):
feeds = [] feeds = []
@ -127,9 +146,9 @@ class MPHKRecipe(BasicNewsRecipe):
# if eco_articles: # if eco_articles:
# feeds.append((u'\u74b0\u4fdd Eco News', eco_articles)) # feeds.append((u'\u74b0\u4fdd Eco News', eco_articles))
# special - entertainment # special - entertainment
#ent_articles = self.parse_ent_section('http://ol.mingpao.com/cfm/star1.cfm') ent_articles = self.parse_ent_section('http://ol.mingpao.com/cfm/star1.cfm')
#if ent_articles: if ent_articles:
# feeds.append(('Entertainment', ent_articles)) feeds.append((u'\u5f71\u8996 Entertainment', ent_articles))
return feeds return feeds
def parse_section(self, url): def parse_section(self, url):
@ -164,6 +183,7 @@ class MPHKRecipe(BasicNewsRecipe):
return current_articles return current_articles
def parse_eco_section(self, url): def parse_eco_section(self, url):
dateStr = self.get_fetchdate()
soup = self.index_to_soup(url) soup = self.index_to_soup(url)
divs = soup.findAll(attrs={'class': ['bullet']}) divs = soup.findAll(attrs={'class': ['bullet']})
current_articles = [] current_articles = []
@ -173,23 +193,25 @@ class MPHKRecipe(BasicNewsRecipe):
title = self.tag_to_string(a) title = self.tag_to_string(a)
url = a.get('href', False) url = a.get('href', False)
url = 'http://tssl.mingpao.com/htm/marketing/eco/cfm/' +url 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':''}) current_articles.append({'title': title, 'url': url, 'description':''})
included_urls.append(url) included_urls.append(url)
return current_articles return current_articles
#def parse_ent_section(self, url): def parse_ent_section(self, url):
# dateStr = self.get_fetchdate() soup = self.index_to_soup(url)
# soup = self.index_to_soup(url) a = soup.findAll('a', href=True)
# a = soup.findAll('a', href=True) a.reverse()
# current_articles = [] current_articles = []
# included_urls = [] included_urls = []
# for i in a: for i in a:
# title = self.tag_to_string(i) title = self.tag_to_string(i)
# url = 'http://ol.mingpao.com/cfm/' + i.get('href', False) 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 == '': 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': ''}) current_articles.append({'title': title, 'url': url, 'description': ''})
# return current_articles included_urls.append(url)
current_articles.reverse()
return current_articles
def preprocess_html(self, soup): def preprocess_html(self, soup):
for item in soup.findAll(style=True): for item in soup.findAll(style=True):
@ -201,21 +223,26 @@ class MPHKRecipe(BasicNewsRecipe):
return soup return soup
def create_opf(self, feeds, dir=None): 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: if dir is None:
dir = self.output_dir dir = self.output_dir
title = self.short_title() title = self.short_title()
if self.output_profile.periodical_date_in_title: title += ' ' + self.get_fetchdate()
title += strftime(self.timefmt) #if self.output_profile.periodical_date_in_title:
# title += strftime(self.timefmt)
mi = MetaInformation(title, [__appname__]) mi = MetaInformation(title, [__appname__])
mi.publisher = __appname__ mi.publisher = __appname__
mi.author_sort = __appname__ mi.author_sort = __appname__
mi.publication_type = self.publication_type+':'+self.short_title() mi.publication_type = self.publication_type+':'+self.short_title()
mi.timestamp = nowf() #mi.timestamp = nowf()
mi.timestamp = self.get_dtlocal()
mi.comments = self.description mi.comments = self.description
if not isinstance(mi.comments, unicode): if not isinstance(mi.comments, unicode):
mi.comments = mi.comments.decode('utf-8', 'replace') 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') opf_path = os.path.join(dir, 'index.opf')
ncx_path = os.path.join(dir, 'index.ncx') ncx_path = os.path.join(dir, 'index.ncx')
opf = OPFCreator(dir, mi) opf = OPFCreator(dir, mi)

View File

@ -14,7 +14,7 @@ class TheHeiseOnline(BasicNewsRecipe):
oldest_article = 3 oldest_article = 3
description = 'In association with Heise Online' description = 'In association with Heise Online'
publisher = 'Heise Media UK Ltd.' publisher = 'Heise Media UK Ltd.'
category = 'news, technology, security' category = 'news, technology, security, OSS, internet'
max_articles_per_feed = 100 max_articles_per_feed = 100
language = 'en' language = 'en'
encoding = 'utf-8' encoding = 'utf-8'
@ -27,6 +27,12 @@ class TheHeiseOnline(BasicNewsRecipe):
feeds = [ feeds = [
(u'The H News Feed', u'http://www.h-online.com/news/atom.xml') (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): def print_version(self, url):
return url + '?view=print' return url + '?view=print'

View File

@ -0,0 +1,68 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Hiroshi Miura <miurahr@linux.com>'
'''
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

View File

@ -129,6 +129,7 @@ class CoverCache(Thread): # {{{
self.keep_running = True self.keep_running = True
self.cache = {} self.cache = {}
self.lock = RLock() self.lock = RLock()
self.allowed_ids = frozenset([])
self.null_image = QImage() self.null_image = QImage()
def stop(self): def stop(self):
@ -175,6 +176,11 @@ class CoverCache(Thread): # {{{
break break
for id_ in ids: for id_ in ids:
time.sleep(0.050) # Limit 20/second to not overwhelm the GUI 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: try:
img = self._image_for_id(id_) img = self._image_for_id(id_)
except: except:
@ -193,6 +199,7 @@ class CoverCache(Thread): # {{{
def set_cache(self, ids): def set_cache(self, ids):
with self.lock: with self.lock:
self.allowed_ids = frozenset(ids)
already_loaded = set([]) already_loaded = set([])
for id in self.cache.keys(): for id in self.cache.keys():
if id in ids: if id in ids:
@ -213,8 +220,9 @@ class CoverCache(Thread): # {{{
def refresh(self, ids): def refresh(self, ids):
with self.lock: with self.lock:
for id_ in ids: for id_ in ids:
self.cache.pop(id_, None) cover = self.cache.pop(id_, None)
self.load_queue.put(id_) if cover is not None:
self.load_queue.put(id_)
# }}} # }}}
### Global utility function for get_match here and in gui2/library.py ### Global utility function for get_match here and in gui2/library.py

View File

@ -54,19 +54,23 @@ def save_cover_data_to(data, path, bgcolor='#ffffff', resize_to=None,
changed = True changed = True
if not changed: if not changed:
changed = fmt != orig_fmt changed = fmt != orig_fmt
ret = None
if return_data: if return_data:
ret = data
if changed: if changed:
if hasattr(img, 'set_compression_quality') and fmt == 'jpg': if hasattr(img, 'set_compression_quality') and fmt == 'jpg':
img.set_compression_quality(compression_quality) img.set_compression_quality(compression_quality)
return img.export(fmt) ret = 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)
else: else:
with lopen(path, 'wb') as f: if changed:
f.write(data) 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'): def thumbnail(data, width=120, height=120, bgcolor='#ffffff', fmt='jpg'):
img = Image() img = Image()

View File

@ -5,6 +5,9 @@
#include "magick_constants.h" #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 {{{ // magick_set_exception {{{
PyObject* magick_set_exception(MagickWand *wand) { PyObject* magick_set_exception(MagickWand *wand) {
ExceptionType ext; ExceptionType ext;
@ -54,6 +57,7 @@ magick_PixelWand_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
static PyObject * static PyObject *
magick_PixelWand_color_getter(magick_PixelWand *self, void *closure) { magick_PixelWand_color_getter(magick_PixelWand *self, void *closure) {
const char *fp; const char *fp;
NULL_CHECK(NULL);
fp = PixelGetColorAsNormalizedString(self->wand); fp = PixelGetColorAsNormalizedString(self->wand);
return Py_BuildValue("s", fp); return Py_BuildValue("s", fp);
} }
@ -62,6 +66,8 @@ static int
magick_PixelWand_color_setter(magick_PixelWand *self, PyObject *val, void *closure) { magick_PixelWand_color_setter(magick_PixelWand *self, PyObject *val, void *closure) {
char *fmt; char *fmt;
NULL_CHECK(-1);
if (val == NULL) { if (val == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete PixelWand color"); PyErr_SetString(PyExc_TypeError, "Cannot delete PixelWand color");
return -1; 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 {{{ // PixelWand attr list {{{
static PyMethodDef magick_PixelWand_methods[] = { 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 */ {NULL} /* Sentinel */
}; };
@ -175,10 +194,21 @@ magick_DrawingWand_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return (PyObject *)self; 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 {{{ // DrawingWand.font {{{
static PyObject * static PyObject *
magick_DrawingWand_font_getter(magick_DrawingWand *self, void *closure) { magick_DrawingWand_font_getter(magick_DrawingWand *self, void *closure) {
const char *fp; const char *fp;
NULL_CHECK(NULL);
fp = DrawGetFont(self->wand); fp = DrawGetFont(self->wand);
return Py_BuildValue("s", fp); return Py_BuildValue("s", fp);
} }
@ -186,6 +216,7 @@ magick_DrawingWand_font_getter(magick_DrawingWand *self, void *closure) {
static int static int
magick_DrawingWand_font_setter(magick_DrawingWand *self, PyObject *val, void *closure) { magick_DrawingWand_font_setter(magick_DrawingWand *self, PyObject *val, void *closure) {
char *fmt; char *fmt;
NULL_CHECK(-1);
if (val == NULL) { if (val == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete DrawingWand font"); 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 {{{ // DrawingWand.font_size {{{
static PyObject * static PyObject *
magick_DrawingWand_fontsize_getter(magick_DrawingWand *self, void *closure) { magick_DrawingWand_fontsize_getter(magick_DrawingWand *self, void *closure) {
NULL_CHECK(NULL)
return Py_BuildValue("d", DrawGetFontSize(self->wand)); return Py_BuildValue("d", DrawGetFontSize(self->wand));
} }
static int static int
magick_DrawingWand_fontsize_setter(magick_DrawingWand *self, PyObject *val, void *closure) { magick_DrawingWand_fontsize_setter(magick_DrawingWand *self, PyObject *val, void *closure) {
NULL_CHECK(-1)
if (val == NULL) { if (val == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete DrawingWand fontsize"); PyErr_SetString(PyExc_TypeError, "Cannot delete DrawingWand fontsize");
return -1; return -1;
@ -233,12 +266,14 @@ magick_DrawingWand_fontsize_setter(magick_DrawingWand *self, PyObject *val, void
// DrawingWand.text_antialias {{{ // DrawingWand.text_antialias {{{
static PyObject * static PyObject *
magick_DrawingWand_textantialias_getter(magick_DrawingWand *self, void *closure) { magick_DrawingWand_textantialias_getter(magick_DrawingWand *self, void *closure) {
NULL_CHECK(NULL);
if (DrawGetTextAntialias(self->wand)) Py_RETURN_TRUE; if (DrawGetTextAntialias(self->wand)) Py_RETURN_TRUE;
Py_RETURN_FALSE; Py_RETURN_FALSE;
} }
static int static int
magick_DrawingWand_textantialias_setter(magick_DrawingWand *self, PyObject *val, void *closure) { magick_DrawingWand_textantialias_setter(magick_DrawingWand *self, PyObject *val, void *closure) {
NULL_CHECK(-1);
if (val == NULL) { if (val == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete DrawingWand textantialias"); PyErr_SetString(PyExc_TypeError, "Cannot delete DrawingWand textantialias");
return -1; return -1;
@ -253,6 +288,7 @@ magick_DrawingWand_textantialias_setter(magick_DrawingWand *self, PyObject *val,
// DrawingWand.gravity {{{ // DrawingWand.gravity {{{
static PyObject * static PyObject *
magick_DrawingWand_gravity_getter(magick_DrawingWand *self, void *closure) { magick_DrawingWand_gravity_getter(magick_DrawingWand *self, void *closure) {
NULL_CHECK(NULL);
return Py_BuildValue("n", DrawGetGravity(self->wand)); return Py_BuildValue("n", DrawGetGravity(self->wand));
} }
@ -260,6 +296,8 @@ static int
magick_DrawingWand_gravity_setter(magick_DrawingWand *self, PyObject *val, void *closure) { magick_DrawingWand_gravity_setter(magick_DrawingWand *self, PyObject *val, void *closure) {
int grav; int grav;
NULL_CHECK(-1);
if (val == NULL) { if (val == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete DrawingWand gravity"); PyErr_SetString(PyExc_TypeError, "Cannot delete DrawingWand gravity");
return -1; return -1;
@ -281,6 +319,9 @@ magick_DrawingWand_gravity_setter(magick_DrawingWand *self, PyObject *val, void
// DrawingWand attr list {{{ // DrawingWand attr list {{{
static PyMethodDef magick_DrawingWand_methods[] = { 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 */ {NULL} /* Sentinel */
}; };
@ -402,6 +443,7 @@ magick_Image_load(magick_Image *self, PyObject *args, PyObject *kwargs) {
Py_ssize_t dlen; Py_ssize_t dlen;
MagickBooleanType res; MagickBooleanType res;
NULL_CHECK(NULL)
if (!PyArg_ParseTuple(args, "s#", &data, &dlen)) return NULL; if (!PyArg_ParseTuple(args, "s#", &data, &dlen)) return NULL;
res = MagickReadImageBlob(self->wand, data, dlen); res = MagickReadImageBlob(self->wand, data, dlen);
@ -420,7 +462,8 @@ magick_Image_read(magick_Image *self, PyObject *args, PyObject *kwargs) {
const char *data; const char *data;
MagickBooleanType res; 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); res = MagickReadImage(self->wand, data);
@ -441,6 +484,8 @@ magick_Image_create_canvas(magick_Image *self, PyObject *args, PyObject *kwargs)
PixelWand *pw; PixelWand *pw;
MagickBooleanType res = MagickFalse; MagickBooleanType res = MagickFalse;
NULL_CHECK(NULL)
if (!PyArg_ParseTuple(args, "nns", &width, &height, &bgcolor)) return NULL; if (!PyArg_ParseTuple(args, "nns", &width, &height, &bgcolor)) return NULL;
pw = NewPixelWand(); pw = NewPixelWand();
@ -464,6 +509,8 @@ magick_Image_font_metrics(magick_Image *self, PyObject *args, PyObject *kwargs)
DrawingWand *dw; DrawingWand *dw;
double *metrics; double *metrics;
NULL_CHECK(NULL)
if (!PyArg_ParseTuple(args, "O!s", &magick_DrawingWandType, &dw_, &text)) return NULL; if (!PyArg_ParseTuple(args, "O!s", &magick_DrawingWandType, &dw_, &text)) return NULL;
dw = ((magick_DrawingWand*)dw_)->wand; dw = ((magick_DrawingWand*)dw_)->wand;
if (!IsDrawingWand(dw)) { PyErr_SetString(PyExc_TypeError, "Invalid drawing wand"); return NULL; } if (!IsDrawingWand(dw)) { PyErr_SetString(PyExc_TypeError, "Invalid drawing wand"); return NULL; }
@ -491,6 +538,8 @@ magick_Image_annotate(magick_Image *self, PyObject *args, PyObject *kwargs) {
DrawingWand *dw; DrawingWand *dw;
double x, y, angle; double x, y, angle;
NULL_CHECK(NULL)
if (!PyArg_ParseTuple(args, "O!ddds", &magick_DrawingWandType, &dw_, &x, &y, &angle, &text)) return NULL; if (!PyArg_ParseTuple(args, "O!ddds", &magick_DrawingWandType, &dw_, &x, &y, &angle, &text)) return NULL;
dw = ((magick_DrawingWand*)dw_)->wand; dw = ((magick_DrawingWand*)dw_)->wand;
if (!IsDrawingWand(dw)) { PyErr_SetString(PyExc_TypeError, "Invalid drawing wand"); return NULL; } if (!IsDrawingWand(dw)) { PyErr_SetString(PyExc_TypeError, "Invalid drawing wand"); return NULL; }
@ -510,6 +559,8 @@ magick_Image_export(magick_Image *self, PyObject *args, PyObject *kwargs) {
PyObject *ans; PyObject *ans;
size_t len = 0; size_t len = 0;
NULL_CHECK(NULL)
if (!PyArg_ParseTuple(args, "s", &fmt)) return NULL; if (!PyArg_ParseTuple(args, "s", &fmt)) return NULL;
if (!MagickSetFormat(self->wand, fmt)) { if (!MagickSetFormat(self->wand, fmt)) {
@ -533,6 +584,8 @@ magick_Image_export(magick_Image *self, PyObject *args, PyObject *kwargs) {
static PyObject * static PyObject *
magick_Image_size_getter(magick_Image *self, void *closure) { magick_Image_size_getter(magick_Image *self, void *closure) {
size_t width, height; size_t width, height;
NULL_CHECK(NULL)
width = MagickGetImageWidth(self->wand); width = MagickGetImageWidth(self->wand);
height = MagickGetImageHeight(self->wand); height = MagickGetImageHeight(self->wand);
return Py_BuildValue("nn", width, height); return Py_BuildValue("nn", width, height);
@ -545,6 +598,9 @@ magick_Image_size_setter(magick_Image *self, PyObject *val, void *closure) {
double blur; double blur;
MagickBooleanType res; MagickBooleanType res;
NULL_CHECK(-1)
if (val == NULL) { if (val == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete image size"); PyErr_SetString(PyExc_TypeError, "Cannot delete image size");
return -1; return -1;
@ -592,6 +648,8 @@ magick_Image_size_setter(magick_Image *self, PyObject *val, void *closure) {
static PyObject * static PyObject *
magick_Image_format_getter(magick_Image *self, void *closure) { magick_Image_format_getter(magick_Image *self, void *closure) {
const char *fmt; const char *fmt;
NULL_CHECK(NULL)
fmt = MagickGetImageFormat(self->wand); fmt = MagickGetImageFormat(self->wand);
return Py_BuildValue("s", fmt); return Py_BuildValue("s", fmt);
} }
@ -599,6 +657,8 @@ magick_Image_format_getter(magick_Image *self, void *closure) {
static int static int
magick_Image_format_setter(magick_Image *self, PyObject *val, void *closure) { magick_Image_format_setter(magick_Image *self, PyObject *val, void *closure) {
char *fmt; char *fmt;
NULL_CHECK(-1)
if (val == NULL) { if (val == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete image format"); PyErr_SetString(PyExc_TypeError, "Cannot delete image format");
@ -628,6 +688,8 @@ magick_Image_distort(magick_Image *self, PyObject *args, PyObject *kwargs) {
MagickBooleanType res; MagickBooleanType res;
double *arguments = NULL; double *arguments = NULL;
NULL_CHECK(NULL)
if (!PyArg_ParseTuple(args, "iOO", &method, &argv, &bestfit)) return 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; } 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) { magick_Image_trim(magick_Image *self, PyObject *args, PyObject *kwargs) {
double fuzz; double fuzz;
NULL_CHECK(NULL)
if (!PyArg_ParseTuple(args, "d", &fuzz)) return NULL; if (!PyArg_ParseTuple(args, "d", &fuzz)) return NULL;
if (!MagickTrimImage(self->wand, fuzz)) return magick_set_exception(self->wand); 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) { magick_Image_thumbnail(magick_Image *self, PyObject *args, PyObject *kwargs) {
Py_ssize_t width, height; Py_ssize_t width, height;
NULL_CHECK(NULL)
if (!PyArg_ParseTuple(args, "nn", &width, &height)) return NULL; if (!PyArg_ParseTuple(args, "nn", &width, &height)) return NULL;
if (!MagickThumbnailImage(self->wand, width, height)) return magick_set_exception(self->wand); 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) { magick_Image_crop(magick_Image *self, PyObject *args, PyObject *kwargs) {
Py_ssize_t width, height, x, y; Py_ssize_t width, height, x, y;
NULL_CHECK(NULL)
if (!PyArg_ParseTuple(args, "nnnn", &width, &height, &x, &y)) return 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); 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; PyObject *obj;
magick_PixelWand *pw; magick_PixelWand *pw;
NULL_CHECK(NULL)
if (!PyArg_ParseTuple(args, "O!", &magick_PixelWandType, &obj)) return NULL; if (!PyArg_ParseTuple(args, "O!", &magick_PixelWandType, &obj)) return NULL;
pw = (magick_PixelWand*)obj; pw = (magick_PixelWand*)obj;
if (!IsPixelWand(pw->wand)) { PyErr_SetString(PyExc_TypeError, "Invalid PixelWand"); return NULL; } 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; magick_PixelWand *pw;
double degrees; double degrees;
NULL_CHECK(NULL)
if (!PyArg_ParseTuple(args, "O!d", &magick_PixelWandType, &obj, &degrees)) return NULL; if (!PyArg_ParseTuple(args, "O!d", &magick_PixelWandType, &obj, &degrees)) return NULL;
pw = (magick_PixelWand*)obj; pw = (magick_PixelWand*)obj;
if (!IsPixelWand(pw->wand)) { PyErr_SetString(PyExc_TypeError, "Invalid PixelWand"); return NULL; } 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) { magick_Image_set_page(magick_Image *self, PyObject *args, PyObject *kwargs) {
Py_ssize_t width, height, x, y; Py_ssize_t width, height, x, y;
NULL_CHECK(NULL)
if (!PyArg_ParseTuple(args, "nnnn", &width, &height, &x, &y)) return 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); 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) { magick_Image_set_compression_quality(magick_Image *self, PyObject *args, PyObject *kwargs) {
Py_ssize_t quality; Py_ssize_t quality;
NULL_CHECK(NULL)
if (!PyArg_ParseTuple(args, "n", &quality)) return NULL; if (!PyArg_ParseTuple(args, "n", &quality)) return NULL;
if (!MagickSetImageCompressionQuality(self->wand, quality)) return magick_set_exception(self->wand); 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; size_t r, c, width, height;
double alpha; double alpha;
NULL_CHECK(NULL)
height = MagickGetImageHeight(self->wand); height = MagickGetImageHeight(self->wand);
pi = NewPixelIterator(self->wand); pi = NewPixelIterator(self->wand);
@ -790,6 +868,8 @@ magick_Image_has_transparent_pixels(magick_Image *self, PyObject *args, PyObject
static PyObject * static PyObject *
magick_Image_normalize(magick_Image *self, PyObject *args, PyObject *kwargs) { magick_Image_normalize(magick_Image *self, PyObject *args, PyObject *kwargs) {
NULL_CHECK(NULL)
if (!MagickNormalizeImage(self->wand)) return magick_set_exception(self->wand); if (!MagickNormalizeImage(self->wand)) return magick_set_exception(self->wand);
Py_RETURN_NONE; Py_RETURN_NONE;
@ -804,6 +884,8 @@ magick_Image_add_border(magick_Image *self, PyObject *args, PyObject *kwargs) {
PyObject *obj; PyObject *obj;
magick_PixelWand *pw; magick_PixelWand *pw;
NULL_CHECK(NULL)
if (!PyArg_ParseTuple(args, "O!nn", &magick_PixelWandType, &obj, &dx, &dy)) return NULL; if (!PyArg_ParseTuple(args, "O!nn", &magick_PixelWandType, &obj, &dx, &dy)) return NULL;
pw = (magick_PixelWand*)obj; pw = (magick_PixelWand*)obj;
if (!IsPixelWand(pw->wand)) { PyErr_SetString(PyExc_TypeError, "Invalid PixelWand"); return NULL; } 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) { magick_Image_sharpen(magick_Image *self, PyObject *args, PyObject *kwargs) {
double radius, sigma; double radius, sigma;
NULL_CHECK(NULL)
if (!PyArg_ParseTuple(args, "dd", &radius, &sigma)) return NULL; if (!PyArg_ParseTuple(args, "dd", &radius, &sigma)) return NULL;
if (!MagickSharpenImage(self->wand, radius, sigma)) return magick_set_exception(self->wand); if (!MagickSharpenImage(self->wand, radius, sigma)) return magick_set_exception(self->wand);
@ -836,6 +920,9 @@ magick_Image_quantize(magick_Image *self, PyObject *args, PyObject *kwargs) {
int colorspace; int colorspace;
PyObject *dither, *measure_error; PyObject *dither, *measure_error;
NULL_CHECK(NULL)
if (!PyArg_ParseTuple(args, "ninOO", &number_colors, &colorspace, &treedepth, &dither, &measure_error)) return NULL; if (!PyArg_ParseTuple(args, "ninOO", &number_colors, &colorspace, &treedepth, &dither, &measure_error)) return NULL;
if (!MagickQuantizeImage(self->wand, number_colors, colorspace, treedepth, PyObject_IsTrue(dither), PyObject_IsTrue(measure_error))) return magick_set_exception(self->wand); if (!MagickQuantizeImage(self->wand, number_colors, colorspace, treedepth, PyObject_IsTrue(dither), PyObject_IsTrue(measure_error))) return magick_set_exception(self->wand);
@ -848,6 +935,8 @@ magick_Image_quantize(magick_Image *self, PyObject *args, PyObject *kwargs) {
static PyObject * static PyObject *
magick_Image_despeckle(magick_Image *self, PyObject *args, PyObject *kwargs) { magick_Image_despeckle(magick_Image *self, PyObject *args, PyObject *kwargs) {
NULL_CHECK(NULL)
if (!MagickDespeckleImage(self->wand)) return magick_set_exception(self->wand); if (!MagickDespeckleImage(self->wand)) return magick_set_exception(self->wand);
Py_RETURN_NONE; Py_RETURN_NONE;
@ -857,6 +946,8 @@ magick_Image_despeckle(magick_Image *self, PyObject *args, PyObject *kwargs) {
// Image.type {{{ // Image.type {{{
static PyObject * static PyObject *
magick_Image_type_getter(magick_Image *self, void *closure) { magick_Image_type_getter(magick_Image *self, void *closure) {
NULL_CHECK(NULL)
return Py_BuildValue("n", MagickGetImageType(self->wand)); return Py_BuildValue("n", MagickGetImageType(self->wand));
} }
@ -864,6 +955,8 @@ static int
magick_Image_type_setter(magick_Image *self, PyObject *val, void *closure) { magick_Image_type_setter(magick_Image *self, PyObject *val, void *closure) {
int type; int type;
NULL_CHECK(-1)
if (val == NULL) { if (val == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete image type"); PyErr_SetString(PyExc_TypeError, "Cannot delete image type");
return -1; 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 {{{ // Image attr list {{{
static PyMethodDef magick_Image_methods[] = { 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", (PyCFunction)magick_Image_load, METH_VARARGS,
"Load an image from a byte buffer (string)" "Load an image from a byte buffer (string)"
}, },
@ -1001,6 +1107,7 @@ static PyGetSetDef magick_Image_getsetters[] = {
// }}} // }}}
static PyTypeObject magick_ImageType = { // {{{ static PyTypeObject magick_ImageType = { // {{{
PyObject_HEAD_INIT(NULL) PyObject_HEAD_INIT(NULL)
0, /*ob_size*/ 0, /*ob_size*/
@ -1053,6 +1160,9 @@ magick_Image_compose(magick_Image *self, PyObject *args, PyObject *kwargs)
magick_Image *src; magick_Image *src;
MagickBooleanType res = MagickFalse; MagickBooleanType res = MagickFalse;
NULL_CHECK(NULL)
if (!PyArg_ParseTuple(args, "O!nnO", &magick_ImageType, &img, &left, &top, &op_)) return NULL; if (!PyArg_ParseTuple(args, "O!nnO", &magick_ImageType, &img, &left, &top, &op_)) return NULL;
src = (magick_Image*)img; src = (magick_Image*)img;
if (!IsMagickWand(src->wand)) {PyErr_SetString(PyExc_TypeError, "Not a valid ImageMagick wand"); return NULL;} 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; PyObject *img;
magick_Image *src; magick_Image *src;
NULL_CHECK(NULL)
if (!PyArg_ParseTuple(args, "O!", &magick_ImageType, &img)) return NULL; if (!PyArg_ParseTuple(args, "O!", &magick_ImageType, &img)) return NULL;
src = (magick_Image*)img; src = (magick_Image*)img;
if (!IsMagickWand(src->wand)) {PyErr_SetString(PyExc_TypeError, "Not a valid ImageMagick wand"); return NULL;} if (!IsMagickWand(src->wand)) {PyErr_SetString(PyExc_TypeError, "Not a valid ImageMagick wand"); return NULL;}
@ -1153,3 +1265,4 @@ initmagick(void)
MagickWandGenesis(); MagickWandGenesis();
} }
// }}} // }}}