merged changes from trunk

This commit is contained in:
Fabian Graßl 2010-10-05 12:36:25 +02:00
commit 68ab64166d
9 changed files with 163 additions and 43 deletions

View File

@ -8,10 +8,16 @@ www.guardian.co.uk
'''
from calibre import strftime
from calibre.web.feeds.news import BasicNewsRecipe
from datetime import date
class Guardian(BasicNewsRecipe):
title = u'The Guardian'
title = u'The Guardian / The Observer'
if date.today().weekday() == 6:
base_url = "http://www.guardian.co.uk/theobserver"
else:
base_url = "http://www.guardian.co.uk/theguardian"
__author__ = 'Seabound and Sujata Raman'
language = 'en_GB'
@ -19,6 +25,10 @@ class Guardian(BasicNewsRecipe):
max_articles_per_feed = 100
remove_javascript = True
# List of section titles to ignore
# For example: ['Sport']
ignore_sections = []
timefmt = ' [%a, %d %b %Y]'
keep_only_tags = [
dict(name='div', attrs={'id':["content","article_header","main-article-info",]}),
@ -28,6 +38,7 @@ class Guardian(BasicNewsRecipe):
dict(name='div', attrs={'id':["article-toolbox","subscribe-feeds",]}),
dict(name='ul', attrs={'class':["pagination"]}),
dict(name='ul', attrs={'id':["content-actions"]}),
dict(name='img'),
]
use_embedded_content = False
@ -43,18 +54,6 @@ class Guardian(BasicNewsRecipe):
#match-stats-summary{font-size:small; font-family:Arial,Helvetica,sans-serif;font-weight:normal;}
'''
feeds = [
('Front Page', 'http://www.guardian.co.uk/rss'),
('Business', 'http://www.guardian.co.uk/business/rss'),
('Sport', 'http://www.guardian.co.uk/sport/rss'),
('Culture', 'http://www.guardian.co.uk/culture/rss'),
('Money', 'http://www.guardian.co.uk/money/rss'),
('Life & Style', 'http://www.guardian.co.uk/lifeandstyle/rss'),
('Travel', 'http://www.guardian.co.uk/travel/rss'),
('Environment', 'http://www.guardian.co.uk/environment/rss'),
('Comment','http://www.guardian.co.uk/commentisfree/rss'),
]
def get_article_url(self, article):
url = article.get('guid', None)
if '/video/' in url or '/flyer/' in url or '/quiz/' in url or \
@ -76,7 +75,8 @@ class Guardian(BasicNewsRecipe):
return soup
def find_sections(self):
soup = self.index_to_soup('http://www.guardian.co.uk/theguardian')
# soup = self.index_to_soup("http://www.guardian.co.uk/theobserver")
soup = self.index_to_soup(self.base_url)
# find cover pic
img = soup.find( 'img',attrs ={'alt':'Guardian digital edition'})
if img is not None:
@ -113,13 +113,10 @@ class Guardian(BasicNewsRecipe):
try:
feeds = []
for title, href in self.find_sections():
feeds.append((title, list(self.find_articles(href))))
if not title in self.ignore_sections:
feeds.append((title, list(self.find_articles(href))))
return feeds
except:
raise NotImplementedError
def postprocess_html(self,soup,first):
return soup.findAll('html')[0]

View File

@ -404,14 +404,16 @@ class MetadataUpdater(object):
if self.cover_record is not None:
size = len(self.cover_record)
cover = rescale_image(data, size)
cover += '\0' * (size - len(cover))
self.cover_record[:] = cover
if len(cover) <= size:
cover += '\0' * (size - len(cover))
self.cover_record[:] = cover
if self.thumbnail_record is not None:
size = len(self.thumbnail_record)
thumbnail = rescale_image(data, size, dimen=MAX_THUMB_DIMEN)
thumbnail += '\0' * (size - len(thumbnail))
self.thumbnail_record[:] = thumbnail
return
if len(thumbnail) <= size:
thumbnail += '\0' * (size - len(thumbnail))
self.thumbnail_record[:] = thumbnail
return
def set_metadata(stream, mi):
mu = MetadataUpdater(stream)

View File

@ -112,15 +112,34 @@ def align_block(raw, multiple=4, pad='\0'):
def rescale_image(data, maxsizeb, dimen=None):
if dimen is not None:
return thumbnail(data, width=dimen, height=dimen)[-1]
# Replace transparent pixels with white pixels and convert to JPEG
data = save_cover_data_to(data, 'img.jpg', return_data=True)
data = thumbnail(data, width=dimen, height=dimen)[-1]
else:
# Replace transparent pixels with white pixels and convert to JPEG
data = save_cover_data_to(data, 'img.jpg', return_data=True)
if len(data) <= maxsizeb:
return data
orig_data = data
img = Image()
quality = 95
if hasattr(img, 'set_compression_quality'):
img.load(data)
while len(data) >= maxsizeb and quality >= 10:
quality -= 5
img.set_compression_quality(quality)
data = img.export('jpg')
if len(data) <= maxsizeb:
return data
orig_data = data
scale = 0.9
while len(data) >= maxsizeb and scale >= 0.05:
img = Image()
img.load(data)
img.load(orig_data)
w, h = img.size
img.size = (int(scale*w), int(scale*h))
if hasattr(img, 'set_compression_quality'):
img.set_compression_quality(quality)
data = img.export('jpg')
scale -= 0.05
return data

View File

@ -308,7 +308,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
im = Image()
im.load(cdata)
im.trim(10)
cdata = im.export('jpg')
cdata = im.export('png')
pix = QPixmap()
pix.loadFromData(cdata)
self.cover.setPixmap(pix)

View File

@ -490,26 +490,39 @@ class BooksView(QTableView): # {{{
drag.setMimeData(md)
cover = self.drag_icon(m.cover(self.currentIndex().row()),
len(selected) > 1)
drag.setHotSpot(QPoint(cover.width()//3, cover.height()//3))
drag.setHotSpot(QPoint(-15, -15))
drag.setPixmap(cover)
return drag
def event_has_mods(self, event=None):
mods = event.modifiers() if event is not None else \
QApplication.keyboardModifiers()
return mods & Qt.ControlModifier or mods & Qt.ShiftModifier
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
if event.button() == Qt.LeftButton and not self.event_has_mods():
self.drag_start_pos = event.pos()
return QTableView.mousePressEvent(self, event)
def mouseMoveEvent(self, event):
if not (event.buttons() & Qt.LeftButton) or self.drag_start_pos is None:
if self.drag_start_pos is None:
return QTableView.mouseMoveEvent(self, event)
if self.event_has_mods():
self.drag_start_pos = None
return
if (event.pos() - self.drag_start_pos).manhattanLength() \
< QApplication.startDragDistance():
if not (event.buttons() & Qt.LeftButton) or \
(event.pos() - self.drag_start_pos).manhattanLength() \
< QApplication.startDragDistance():
return
index = self.indexAt(event.pos())
if not index.isValid():
return
drag = self.drag_data()
drag.exec_(Qt.CopyAction)
self.drag_start_pos = None
def dragEnterEvent(self, event):
if int(event.possibleActions() & Qt.CopyAction) + \
@ -643,7 +656,7 @@ class DeviceBooksView(BooksView): # {{{
drag.setMimeData(md)
cover = self.drag_icon(m.cover(self.currentIndex().row()), len(paths) >
1)
drag.setHotSpot(QPoint(cover.width()//3, cover.height()//3))
drag.setHotSpot(QPoint(-15, -15))
drag.setPixmap(cover)
return drag

View File

@ -80,6 +80,7 @@ class TagsView(QTreeView): # {{{
self.setItemDelegate(TagDelegate(self))
self.made_connections = False
self.setAcceptDrops(True)
self.setDragDropMode(self.DropOnly)
self.setDropIndicatorShown(True)
def set_database(self, db, tag_match, sort_by):

View File

@ -158,7 +158,7 @@ class Image(_magick.Image): # {{{
format = ext[1:]
format = format.upper()
with open(path, 'wb') as f:
with lopen(path, 'wb') as f:
f.write(self.export(format))
def compose(self, img, left=0, top=0, operation='OverCompositeOp'):

View File

@ -11,22 +11,57 @@ from calibre.utils.magick import Image, DrawingWand, create_canvas
from calibre.constants import __appname__, __version__
from calibre import fit_image
def normalize_format_name(fmt):
fmt = fmt.lower()
if fmt == 'jpeg':
fmt = 'jpg'
return fmt
def save_cover_data_to(data, path, bgcolor='#ffffff', resize_to=None,
return_data=False):
return_data=False, compression_quality=90):
'''
Saves image in data to path, in the format specified by the path
extension. Composes the image onto a blank canvas so as to
properly convert transparent images.
extension. Removes any transparency. If there is no transparency and no
resize and the input and output image formats are the same, no changes are
made.
:param compression_quality: The quality of the image after compression.
Number between 1 and 100. 1 means highest compression, 100 means no
compression (lossless).
:param bgcolor: The color for transparent pixels. Must be specified in hex.
:param resize_to: A tuple (width, height) or None for no resizing
'''
changed = False
img = Image()
img.load(data)
orig_fmt = normalize_format_name(img.format)
fmt = os.path.splitext(path)[1]
fmt = normalize_format_name(fmt[1:])
if resize_to is not None:
img.size = (resize_to[0], resize_to[1])
canvas = create_canvas(img.size[0], img.size[1], bgcolor)
canvas.compose(img)
changed = True
if not hasattr(img, 'has_transparent_pixels') or img.has_transparent_pixels():
canvas = create_canvas(img.size[0], img.size[1], bgcolor)
canvas.compose(img)
img = canvas
changed = True
if not changed:
changed = fmt != orig_fmt
if return_data:
return canvas.export(os.path.splitext(path)[1][1:])
canvas.save(path)
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)
else:
with lopen(path, 'wb') as f:
f.write(data)
def thumbnail(data, width=120, height=120, bgcolor='#ffffff', fmt='jpg'):
img = Image()
@ -37,6 +72,8 @@ def thumbnail(data, width=120, height=120, bgcolor='#ffffff', fmt='jpg'):
img.size = (nwidth, nheight)
canvas = create_canvas(img.size[0], img.size[1], bgcolor)
canvas.compose(img)
if fmt == 'jpg' and hasattr(canvas, 'set_compression_quality'):
canvas.set_compression_quality(70)
return (canvas.size[0], canvas.size[1], canvas.export(fmt))
def identify_data(data):

View File

@ -725,6 +725,49 @@ magick_Image_set_page(magick_Image *self, PyObject *args, PyObject *kwargs) {
}
// }}}
// Image.set_compression_quality {{{
static PyObject *
magick_Image_set_compression_quality(magick_Image *self, PyObject *args, PyObject *kwargs) {
Py_ssize_t quality;
if (!PyArg_ParseTuple(args, "n", &quality)) return NULL;
if (!MagickSetImageCompressionQuality(self->wand, quality)) return magick_set_exception(self->wand);
Py_RETURN_NONE;
}
// }}}
// Image.has_transparent_pixels {{{
static PyObject *
magick_Image_has_transparent_pixels(magick_Image *self, PyObject *args, PyObject *kwargs) {
PixelIterator *pi = NULL;
PixelWand **pixels = NULL;
int found = 0;
size_t r, c, width, height;
double alpha;
height = MagickGetImageHeight(self->wand);
pi = NewPixelIterator(self->wand);
for (r = 0; r < height; r++) {
pixels = PixelGetNextIteratorRow(pi, &width);
for (c = 0; c < width; c++) {
alpha = PixelGetAlpha(pixels[c]);
if (alpha < 1.00) {
found = 1;
c = width; r = height;
}
}
}
pi = DestroyPixelIterator(pi);
if (found) Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
// }}}
// Image.normalize {{{
static PyObject *
@ -872,6 +915,14 @@ static PyMethodDef magick_Image_methods[] = {
"set_page(width, height, x, y) \n\n Sets the page geometry of the image."
},
{"set_compression_quality", (PyCFunction)magick_Image_set_compression_quality, METH_VARARGS,
"set_compression_quality(quality) \n\n Sets the compression quality when exporting the image."
},
{"has_transparent_pixels", (PyCFunction)magick_Image_has_transparent_pixels, METH_VARARGS,
"has_transparent_pixels() \n\n Returns True iff image has a (semi-) transparent pixel"
},
{"thumbnail", (PyCFunction)magick_Image_thumbnail, METH_VARARGS,
"thumbnail(width, height) \n\n Convert to a thumbnail of specified size."
},