Checks for raster images

This commit is contained in:
Kovid Goyal 2013-12-09 13:38:29 +05:30
parent 089a76662f
commit 158a168b1d
5 changed files with 83 additions and 5 deletions

View File

@ -16,6 +16,7 @@ DEBUG, INFO, WARN, ERROR, CRITICAL = xrange(5)
class BaseError(object):
HELP = ''
INDIVIDUAL_FIX = ''
def __init__(self, msg, name, line=None, col=None):
self.msg, self.line, self.col = msg, line, col

View File

@ -0,0 +1,60 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
from __future__ import (unicode_literals, division, absolute_import,
print_function)
__license__ = 'GPL v3'
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
from calibre import as_unicode
from calibre.utils.magick import Image
from calibre.ebooks.oeb.polish.check.base import BaseError
class InvalidImage(BaseError):
HELP = _('An invalid image is an image that could not be loaded, typically because'
' it is corrupted. You should replace it with a good image or remove it.')
def __init__(self, msg, *args, **kwargs):
BaseError.__init__(self, 'Invalid image: ' + msg, *args, **kwargs)
class CMYKImage(BaseError):
HELP = _('Reader devices based on Adobe Digital Editions cannot display images whose'
' colors are specified in the CMYK colorspace. You should convert this image'
' to the RGB colorspace, for maximum compatibility.')
INDIVIDUAL_FIX = _('Convert image to RGB automatically')
def __call__(self, container):
from PyQt4.Qt import QImage
from calibre.gui2 import pixmap_to_data
ext = container.mime_map[self.name].split('/')[-1].upper()
if ext == 'JPG':
ext = 'JPEG'
if ext not in ('PNG', 'JPEG', 'GIF'):
return False
with container.open(self.name, 'r+b') as f:
raw = f.read()
i = QImage()
i.loadFromData(raw)
if i.isNull():
return False
raw = pixmap_to_data(i, format=ext, quality=95)
f.seek(0)
f.truncate()
f.write(raw)
return True
def check_raster_images(name, mt, raw):
errors = []
i = Image()
try:
i.load(raw)
except Exception as e:
errors.append(InvalidImage(as_unicode(e.message), name))
else:
if i.colorspace == 'CMYKColorspace':
errors.append(CMYKImage(_('Image is in the CMYK colorspace'), name))
return errors

View File

@ -10,8 +10,10 @@ from future_builtins import map
from calibre.ebooks.oeb.base import OEB_DOCS
from calibre.ebooks.oeb.polish.container import guess_type
from calibre.ebooks.oeb.polish.cover import is_raster_image
from calibre.ebooks.oeb.polish.check.base import run_checkers
from calibre.ebooks.oeb.polish.check.parsing import check_xml_parsing
from calibre.ebooks.oeb.polish.check.images import check_raster_images
XML_TYPES = frozenset(map(guess_type, ('a.xml', 'a.svg', 'a.opf', 'a.ncx')))
@ -20,23 +22,34 @@ def run_checks(container):
errors = []
# Check parsing
xml_items, html_items = [], []
xml_items, html_items, raster_images = [], [], []
for name, mt in container.mime_map.iteritems():
items = None
if mt in XML_TYPES:
items = xml_items
elif mt in OEB_DOCS:
items = html_items
elif is_raster_image(mt):
items = raster_images
if items is not None:
items.append((name, mt, container.open(name, 'rb').read()))
errors.extend(run_checkers(check_xml_parsing, xml_items))
errors.extend(run_checkers(check_xml_parsing, html_items))
errors.extend(run_checkers(check_raster_images, raster_images))
return errors
def fix_errors(container, errors):
# Fix parsing
changed = False
for name in {e.name for e in errors if getattr(e, 'is_parsing_error', False)}:
container.parsed(name)
container.dirty(name)
changed = True
for err in errors:
if err.INDIVIDUAL_FIX:
if err(container):
changed = True
return changed

View File

@ -711,9 +711,12 @@ class Boss(QObject):
c = self.gui.check_book
c.parent().show()
c.parent().raise_()
c.fix_errors(current_container())
self.apply_container_update_to_gui()
self.set_modified()
changed = c.fix_errors(current_container())
if changed:
self.apply_container_update_to_gui()
self.set_modified()
else:
self.rewind_savepoint()
@in_thread_job
def merge_requested(self, category, names, master):

View File

@ -143,8 +143,9 @@ class Check(QSplitter):
errors = [self.items.item(i).data(Qt.UserRole).toPyObject() for i in xrange(self.items.count())]
self.show_busy(_('Running fixers, please wait...'))
QApplication.processEvents()
fix_errors(container, errors)
changed = fix_errors(container, errors)
self.run_checks(container)
return changed
def show_busy(self, msg=_('Running checks, please wait...')):
self.help.setText(msg)