mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 10:14:46 -04:00
Images work with the pdf backend
This commit is contained in:
parent
8a6f1b11ba
commit
3538bbe4dc
@ -132,6 +132,7 @@ class Stream(BytesIO):
|
||||
def __init__(self, compress=False):
|
||||
BytesIO.__init__(self)
|
||||
self.compress = compress
|
||||
self.filters = Array()
|
||||
|
||||
def add_extra_keys(self, d):
|
||||
pass
|
||||
@ -139,7 +140,7 @@ class Stream(BytesIO):
|
||||
def pdf_serialize(self, stream):
|
||||
raw = self.getvalue()
|
||||
dl = len(raw)
|
||||
filters = Array()
|
||||
filters = self.filters
|
||||
if self.compress:
|
||||
filters.append(Name('FlateDecode'))
|
||||
raw = zlib.compress(raw)
|
||||
|
@ -13,7 +13,8 @@ from collections import namedtuple
|
||||
from functools import wraps
|
||||
|
||||
from PyQt4.Qt import (QPaintEngine, QPaintDevice, Qt, QApplication, QPainter,
|
||||
QTransform, QPainterPath, QTextOption, QTextLayout)
|
||||
QTransform, QPainterPath, QTextOption, QTextLayout,
|
||||
QImage, QByteArray, QBuffer, qRgba)
|
||||
|
||||
from calibre.constants import DEBUG
|
||||
from calibre.ebooks.pdf.render.serialize import (Color, PDFStream, Path)
|
||||
@ -245,6 +246,9 @@ class PdfEngine(QPaintEngine):
|
||||
self.text_option = QTextOption()
|
||||
self.text_option.setWrapMode(QTextOption.NoWrap)
|
||||
self.fonts = {}
|
||||
i = QImage(1, 1, QImage.Format_ARGB32)
|
||||
i.fill(qRgba(0, 0, 0, 255))
|
||||
self.alpha_bit = i.constBits().asstring(4).find(b'\xff')
|
||||
|
||||
def init_page(self):
|
||||
self.pdf.transform(self.pdf_system)
|
||||
@ -296,11 +300,88 @@ class PdfEngine(QPaintEngine):
|
||||
|
||||
@store_error
|
||||
def drawPixmap(self, rect, pixmap, source_rect):
|
||||
print ('TODO: drawPixmap() currently unimplemented')
|
||||
source_rect = source_rect.toRect()
|
||||
pixmap = (pixmap if source_rect == pixmap.rect() else
|
||||
pixmap.copy(source_rect))
|
||||
image = pixmap.toImage()
|
||||
ref = self.add_image(image, pixmap.cacheKey())
|
||||
if ref is not None:
|
||||
self.pdf.draw_image(rect.x(), rect.y(), rect.width(), rect.height(),
|
||||
ref)
|
||||
|
||||
@store_error
|
||||
def drawImage(self, rect, image, source_rect, flags=Qt.AutoColor):
|
||||
print ('TODO: drawImage() currently unimplemented')
|
||||
source_rect = source_rect.toRect()
|
||||
image = (image if source_rect == image.rect() else
|
||||
image.copy(source_rect))
|
||||
ref = self.add_image(image, image.cacheKey())
|
||||
if ref is not None:
|
||||
self.pdf.draw_image(rect.x(), rect.y(), rect.width(), rect.height(),
|
||||
ref)
|
||||
|
||||
def add_image(self, img, cache_key):
|
||||
if img.isNull(): return
|
||||
ref = self.pdf.get_image(cache_key)
|
||||
if ref is not None:
|
||||
return ref
|
||||
|
||||
fmt = img.format()
|
||||
image = QImage(img)
|
||||
if (image.depth() == 1 and img.colorTable().size() == 2 and
|
||||
img.colorTable().at(0) == QColor(Qt.black).rgba() and
|
||||
img.colorTable().at(1) == QColor(Qt.white).rgba()):
|
||||
if fmt == QImage.Format_MonoLSB:
|
||||
image = image.convertToFormat(QImage.Format_Mono)
|
||||
fmt = QImage.Format_Mono
|
||||
else:
|
||||
if (fmt != QImage.Format_RGB32 and fmt != QImage.Format_ARGB32):
|
||||
image = image.convertToFormat(QImage.Format_ARGB32)
|
||||
fmt = QImage.Format_ARGB32
|
||||
|
||||
w = image.width()
|
||||
h = image.height()
|
||||
d = image.depth()
|
||||
|
||||
if fmt == QImage.Format_Mono:
|
||||
bytes_per_line = (w + 7) >> 3
|
||||
data = image.constBits().asstring(bytes_per_line * h)
|
||||
return self.pdf.write_image(data, w, h, d, cache_key=cache_key)
|
||||
|
||||
ba = QByteArray()
|
||||
buf = QBuffer(ba)
|
||||
image.save(buf, 'jpeg', 94)
|
||||
data = bytes(ba.data())
|
||||
has_alpha = has_mask = False
|
||||
soft_mask = mask = None
|
||||
|
||||
if fmt == QImage.Format_ARGB32:
|
||||
tmask = image.constBits().asstring(4*w*h)[self.alpha_bit::4]
|
||||
sdata = bytearray(tmask)
|
||||
vals = set(sdata)
|
||||
vals.discard(255)
|
||||
has_mask = bool(vals)
|
||||
vals.discard(0)
|
||||
has_alpha = bool(vals)
|
||||
|
||||
if has_alpha:
|
||||
soft_mask = self.pdf.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.pdf.write_image(mdata, w, h, 1)
|
||||
|
||||
return self.pdf.write_image(data, w, h, 32, mask=mask, dct=True,
|
||||
soft_mask=soft_mask, cache_key=cache_key)
|
||||
|
||||
@store_error
|
||||
def updateState(self, state):
|
||||
@ -510,8 +591,8 @@ class PdfDevice(QPaintDevice): # {{{
|
||||
# }}}
|
||||
|
||||
if __name__ == '__main__':
|
||||
from PyQt4.Qt import (QBrush, QColor, QPoint)
|
||||
QBrush, QColor, QPoint
|
||||
from PyQt4.Qt import (QBrush, QColor, QPoint, QPixmap)
|
||||
QBrush, QColor, QPoint, QPixmap
|
||||
app = QApplication([])
|
||||
p = QPainter()
|
||||
with open('/tmp/painter.pdf', 'wb') as f:
|
||||
@ -525,16 +606,21 @@ if __name__ == '__main__':
|
||||
# pp = QPainterPath()
|
||||
# pp.addRect(0, 0, xmax, ymax)
|
||||
# p.drawPath(pp)
|
||||
# p.save()
|
||||
# for i in xrange(3):
|
||||
# col = [0, 0, 0, 200]
|
||||
# col[i] = 255
|
||||
# p.setOpacity(0.3)
|
||||
# p.setBrush(QBrush(QColor(*col)))
|
||||
# p.drawRect(0, 0, xmax/10, xmax/10)
|
||||
# p.translate(xmax/10, xmax/10)
|
||||
# p.scale(1, 1.5)
|
||||
# p.restore()
|
||||
p.save()
|
||||
for i in xrange(3):
|
||||
col = [0, 0, 0, 200]
|
||||
col[i] = 255
|
||||
p.setOpacity(0.3)
|
||||
p.setBrush(QBrush(QColor(*col)))
|
||||
p.drawRect(0, 0, xmax/10, xmax/10)
|
||||
p.translate(xmax/10, xmax/10)
|
||||
p.scale(1, 1.5)
|
||||
p.restore()
|
||||
|
||||
# p.scale(2, 2)
|
||||
# p.rotate(45)
|
||||
p.drawPixmap(0, 0, 2048, 2048, QPixmap(I('library.png')))
|
||||
p.drawRect(0, 0, 2048, 2048)
|
||||
|
||||
# p.save()
|
||||
# p.drawLine(0, 0, 5000, 0)
|
||||
@ -542,18 +628,18 @@ if __name__ == '__main__':
|
||||
# p.drawLine(0, 0, 5000, 0)
|
||||
# p.restore()
|
||||
|
||||
f = p.font()
|
||||
f.setPointSize(24)
|
||||
# f = p.font()
|
||||
# f.setPointSize(24)
|
||||
# f.setLetterSpacing(f.PercentageSpacing, 200)
|
||||
# f.setUnderline(True)
|
||||
# f.setOverline(True)
|
||||
# f.setStrikeOut(True)
|
||||
f.setFamily('Calibri')
|
||||
p.setFont(f)
|
||||
# f.setFamily('Calibri')
|
||||
# p.setFont(f)
|
||||
# p.setPen(QColor(0, 0, 255))
|
||||
# p.scale(2, 2)
|
||||
# p.rotate(45)
|
||||
# p.setPen(QColor(0, 0, 255))
|
||||
p.drawText(QPoint(100, 300), 'Some text ū --- Д AV ff ff')
|
||||
# p.drawText(QPoint(100, 300), 'Some text ū --- Д AV ff ff')
|
||||
finally:
|
||||
p.end()
|
||||
for line in dev.engine.debug:
|
||||
|
@ -88,6 +88,7 @@ class Page(Stream):
|
||||
})
|
||||
self.opacities = {}
|
||||
self.fonts = {}
|
||||
self.xobjects = {}
|
||||
|
||||
def set_opacity(self, opref):
|
||||
if opref not in self.opacities:
|
||||
@ -101,6 +102,11 @@ class Page(Stream):
|
||||
self.fonts[fontref] = 'F%d'%len(self.fonts)
|
||||
return self.fonts[fontref]
|
||||
|
||||
def add_image(self, imgref):
|
||||
if imgref not in self.xobjects:
|
||||
self.xobjects[imgref] = 'Image%d'%len(self.xobjects)
|
||||
return self.xobjects[imgref]
|
||||
|
||||
def add_resources(self):
|
||||
r = Dictionary()
|
||||
if self.opacities:
|
||||
@ -113,6 +119,11 @@ class Page(Stream):
|
||||
for ref, name in self.fonts.iteritems():
|
||||
fonts[name] = ref
|
||||
r['Font'] = fonts
|
||||
if self.xobjects:
|
||||
xobjects = Dictionary()
|
||||
for ref, name in self.xobjects.iteritems():
|
||||
xobjects[name] = ref
|
||||
r['XObject'] = xobjects
|
||||
if r:
|
||||
self.page_dict['Resources'] = r
|
||||
|
||||
@ -223,6 +234,35 @@ class HashingStream(object):
|
||||
if raw:
|
||||
self.last_char = raw[-1]
|
||||
|
||||
class Image(Stream):
|
||||
|
||||
def __init__(self, data, w, h, depth, mask, soft_mask, dct):
|
||||
Stream.__init__(self)
|
||||
self.width, self.height, self.depth = w, h, depth
|
||||
self.mask, self.soft_mask = mask, soft_mask
|
||||
if dct:
|
||||
self.filters.append(Name('DCTDecode'))
|
||||
else:
|
||||
self.compress = True
|
||||
self.write(data)
|
||||
|
||||
def add_extra_keys(self, d):
|
||||
d['Type'] = Name('XObject')
|
||||
d['Subtype']= Name('Image')
|
||||
d['Width'] = self.width
|
||||
d['Height'] = self.height
|
||||
if self.depth == 1:
|
||||
d['ImageMask'] = True
|
||||
d['Decode'] = Array([1, 0])
|
||||
else:
|
||||
d['BitsPerComponent'] = 8
|
||||
d['ColorSpace'] = Name('Device' + ('RGB' if self.depth == 32 else
|
||||
'Gray'))
|
||||
if self.mask is not None:
|
||||
d['Mask'] = self.mask
|
||||
if self.soft_mask is not None:
|
||||
d['SMask'] = self.soft_mask
|
||||
|
||||
class PDFStream(object):
|
||||
|
||||
PATH_OPS = {
|
||||
@ -253,6 +293,7 @@ class PDFStream(object):
|
||||
'Producer':String(creator)})
|
||||
self.stroke_opacities, self.fill_opacities = {}, {}
|
||||
self.font_manager = FontManager(self.objects, self.compress)
|
||||
self.image_cache = {}
|
||||
|
||||
@property
|
||||
def page_tree(self):
|
||||
@ -372,6 +413,22 @@ class PDFStream(object):
|
||||
self.current_page.write(' Tj ')
|
||||
self.current_page.write_line(b' ET')
|
||||
|
||||
def get_image(self, cache_key):
|
||||
return self.image_cache.get(cache_key, None)
|
||||
|
||||
def write_image(self, data, w, h, depth, dct=False, mask=None,
|
||||
soft_mask=None, cache_key=None):
|
||||
imgobj = Image(data, w, h, depth, mask, soft_mask, dct)
|
||||
self.image_cache[cache_key] = self.objects.add(imgobj)
|
||||
return self.image_cache[cache_key]
|
||||
|
||||
def draw_image(self, x, y, w, h, imgref):
|
||||
name = self.current_page.add_image(imgref)
|
||||
sx, sy = w, h
|
||||
self.current_page.write('q %g 0 0 %g %g %g cm '%(sx, -sy, x, y+h))
|
||||
serialize(Name(name), self.current_page)
|
||||
self.current_page.write_line(' Do Q')
|
||||
|
||||
def end(self):
|
||||
if self.current_page.getvalue():
|
||||
self.end_page()
|
||||
|
Loading…
x
Reference in New Issue
Block a user