PDF Output: Fix rendering of translucent images

PDF Output: Fix rendering of semi-transparent images. All
semi-transparent images are now rendered using soft masks. Fixes #1243829 [Images in PDF conversion corrupted](https://bugs.launchpad.net/calibre/+bug/1243829)
This commit is contained in:
Kovid Goyal 2013-10-24 13:45:47 +05:30
parent 795ae4e84f
commit 238be8d5c0

View File

@ -10,7 +10,7 @@ __docformat__ = 'restructuredtext en'
import hashlib import hashlib
from future_builtins import map from future_builtins import map
from PyQt4.Qt import QBuffer, QByteArray, QImage, Qt, QColor, qRgba from PyQt4.Qt import QBuffer, QByteArray, QImage, Qt, QColor, qRgba, QPainter
from calibre.constants import (__appname__, __version__) from calibre.constants import (__appname__, __version__)
from calibre.ebooks.pdf.render.common import ( from calibre.ebooks.pdf.render.common import (
@ -418,41 +418,35 @@ class PDFStream(object):
data = image.constBits().asstring(bytes_per_line * h) data = image.constBits().asstring(bytes_per_line * h)
return self.write_image(data, w, h, d, cache_key=cache_key) return self.write_image(data, w, h, d, cache_key=cache_key)
ba = QByteArray() has_alpha = False
buf = QBuffer(ba) soft_mask = None
image.save(buf, 'jpeg', 94)
data = bytes(ba.data())
has_alpha = has_mask = False
soft_mask = mask = None
if fmt == QImage.Format_ARGB32: if fmt == QImage.Format_ARGB32:
tmask = image.constBits().asstring(4*w*h)[self.alpha_bit::4] tmask = image.constBits().asstring(4*w*h)[self.alpha_bit::4]
sdata = bytearray(tmask) sdata = bytearray(tmask)
vals = set(sdata) vals = set(sdata)
vals.discard(255) vals.discard(255) # discard opaque pixels
has_mask = bool(vals)
vals.discard(0)
has_alpha = bool(vals) has_alpha = bool(vals)
if has_alpha:
# Blend image onto a white background as otherwise Qt will render
# transparent pixels as black
background = QImage(image.size(), QImage.Format_ARGB32_Premultiplied)
background.fill(Qt.white)
painter = QPainter(background)
painter.drawImage(0, 0, image)
painter.end()
image = background
ba = QByteArray()
buf = QBuffer(ba)
image.save(buf, 'jpeg', 94)
data = bytes(ba.data())
if has_alpha: if has_alpha:
soft_mask = self.write_image(tmask, w, h, 8) soft_mask = self.write_image(tmask, w, h, 8)
elif has_mask:
# dither the soft mask to 1bit and add it. This also helps PDF
# viewers without transparency support
bytes_per_line = (w + 7) >> 3
mdata = bytearray(0 for i in xrange(bytes_per_line * h))
spos = mpos = 0
for y in xrange(h):
for x in xrange(w):
if sdata[spos]:
mdata[mpos + x>>3] |= (0x80 >> (x&7))
spos += 1
mpos += bytes_per_line
mdata = bytes(mdata)
mask = self.write_image(mdata, w, h, 1)
return self.write_image(data, w, h, 32, mask=mask, dct=True, return self.write_image(data, w, h, 32, dct=True,
soft_mask=soft_mask, cache_key=cache_key) soft_mask=soft_mask, cache_key=cache_key)
def add_pattern(self, pattern): def add_pattern(self, pattern):
if pattern.cache_key not in self.pattern_cache: if pattern.cache_key not in self.pattern_cache: