From 0dd9c26dbe085f6d2915f30022ec9484088fff49 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 31 Dec 2012 20:08:17 +0530 Subject: [PATCH] Handle non-solid pens --- src/calibre/ebooks/pdf/render/engine.py | 1 - src/calibre/ebooks/pdf/render/graphics.py | 18 ++++------- src/calibre/ebooks/pdf/render/serialize.py | 36 ++++++++++++---------- src/calibre/ebooks/pdf/render/test.py | 18 +++++++++-- 4 files changed, 41 insertions(+), 32 deletions(-) diff --git a/src/calibre/ebooks/pdf/render/engine.py b/src/calibre/ebooks/pdf/render/engine.py index 77f1f00c57..b65aed0660 100644 --- a/src/calibre/ebooks/pdf/render/engine.py +++ b/src/calibre/ebooks/pdf/render/engine.py @@ -109,7 +109,6 @@ class PdfEngine(QPaintEngine): def init_page(self): self.pdf.transform(self.pdf_system) - self.pdf.set_rgb_colorspace() self.graphics.reset() self.pdf.save_stack() self.current_page_inited = True diff --git a/src/calibre/ebooks/pdf/render/graphics.py b/src/calibre/ebooks/pdf/render/graphics.py index f9353d5358..43b1c72fdb 100644 --- a/src/calibre/ebooks/pdf/render/graphics.py +++ b/src/calibre/ebooks/pdf/render/graphics.py @@ -15,7 +15,7 @@ from PyQt4.Qt import ( from calibre.ebooks.pdf.render.common import ( Name, Array, fmtnum, Stream, Dictionary) -from calibre.ebooks.pdf.render.serialize import Path, Color +from calibre.ebooks.pdf.render.serialize import Path def convert_path(path): # {{{ p = Path() @@ -392,8 +392,6 @@ class Graphics(object): return color, opacity, pattern, do_fill def apply_stroke(self, state, pdf_system, painter): - # TODO: Handle pens with non solid brushes by setting the colorspace - # for stroking to a pattern # TODO: Support miter limit by using QPainterPathStroker pen = state.stroke self.pending_state.do_stroke = True @@ -427,14 +425,10 @@ class Graphics(object): pdf.current_page.write(' 0 d ') # Stroke fill - b = pen.brush() - vals = list(b.color().getRgbF()) - vals[-1] *= state.opacity - color = Color(*vals) - pdf.set_stroke_color(color) - - if vals[-1] < 1e-5 or b.style() == Qt.NoBrush: - self.pending_state.do_stroke = False + color, opacity, pattern, self.pending_state.do_stroke = self.convert_brush( + pen.brush(), state.brush_origin, state.opacity, pdf_system, + painter.transform()) + self.pdf.apply_stroke(color, pattern, opacity) def apply_fill(self, state, pdf_system, painter): self.pending_state.do_fill = True @@ -458,7 +452,7 @@ class Graphics(object): the brush origin before painting an object. While not perfect, this is better than nothing. ''' - if not self.current_state.do_fill: + if not hasattr(self, 'last_fill') or not self.current_state.do_fill: return if isinstance(self.last_fill.brush, TexturePattern): diff --git a/src/calibre/ebooks/pdf/render/serialize.py b/src/calibre/ebooks/pdf/render/serialize.py index 54a5f674b4..ce4ae7fc6c 100644 --- a/src/calibre/ebooks/pdf/render/serialize.py +++ b/src/calibre/ebooks/pdf/render/serialize.py @@ -10,7 +10,6 @@ __docformat__ = 'restructuredtext en' import hashlib from future_builtins import map from itertools import izip -from collections import namedtuple from PyQt4.Qt import QBuffer, QByteArray, QImage, Qt, QColor, qRgba @@ -23,8 +22,6 @@ from calibre.ebooks.pdf.render.links import Links PDFVER = b'%PDF-1.3' -Color = namedtuple('Color', 'red green blue opacity') - class IndirectObjects(object): def __init__(self): @@ -353,9 +350,6 @@ class PDFStream(object): cm = ' '.join(map(fmtnum, vals)) self.current_page.write_line(cm + ' cm') - def set_rgb_colorspace(self): - self.current_page.write_line('/DeviceRGB CS /DeviceRGB cs') - def save_stack(self): self.current_page.write_line('q') @@ -391,13 +385,11 @@ class PDFStream(object): def serialize(self, o): serialize(o, self.current_page) - def set_stroke_color(self, color): - opacity = color.opacity + def set_stroke_opacity(self, opacity): if opacity not in self.stroke_opacities: op = Dictionary({'Type':Name('ExtGState'), 'CA': opacity}) self.stroke_opacities[opacity] = self.objects.add(op) self.current_page.set_opacity(self.stroke_opacities[opacity]) - self.current_page.write_line(' '.join(map(fmtnum, color[:3])) + ' SC') def set_fill_opacity(self, opacity): opacity = float(opacity) @@ -518,17 +510,27 @@ class PDFStream(object): serialize(Name(name), self.current_page) self.current_page.write_line(' Do Q') + def apply_color_space(self, color, pattern, stroke=False): + wl = self.current_page.write_line + if color is not None and pattern is None: + wl(' '.join(map(fmtnum, color)) + (' RG' if stroke else ' rg')) + elif color is None and pattern is not None: + wl('/Pattern %s /%s %s'%('CS' if stroke else 'cs', pattern, + 'SCN' if stroke else 'scn')) + elif color is not None and pattern is not None: + col = ' '.join(map(fmtnum, color)) + wl('/PCSp %s %s /%s %s'%('CS' if stroke else 'cs', col, pattern, + 'SCN' if stroke else 'scn')) + def apply_fill(self, color=None, pattern=None, opacity=None): if opacity is not None: self.set_fill_opacity(opacity) - wl = self.current_page.write_line - if color is not None and pattern is None: - wl(' '.join(map(fmtnum, color)) + ' rg') - elif color is None and pattern is not None: - wl('/Pattern cs /%s scn'%pattern) - elif color is not None and pattern is not None: - col = ' '.join(map(fmtnum, color)) - wl('/PCSp cs %s /%s scn'%(col, pattern)) + self.apply_color_space(color, pattern) + + def apply_stroke(self, color=None, pattern=None, opacity=None): + if opacity is not None: + self.set_stroke_opacity(opacity) + self.apply_color_space(color, pattern, stroke=True) def end(self): if self.current_page.getvalue(): diff --git a/src/calibre/ebooks/pdf/render/test.py b/src/calibre/ebooks/pdf/render/test.py index 555af9206f..b631dc8276 100644 --- a/src/calibre/ebooks/pdf/render/test.py +++ b/src/calibre/ebooks/pdf/render/test.py @@ -12,7 +12,7 @@ from tempfile import gettempdir from PyQt4.Qt import (QBrush, QColor, QPoint, QPixmap, QPainterPath, QRectF, QApplication, QPainter, Qt, QImage, QLinearGradient, - QPointF) + QPointF, QPen) QBrush, QColor, QPoint, QPixmap, QPainterPath, QRectF, Qt, QPointF from calibre.ebooks.pdf.render.engine import PdfDevice @@ -69,6 +69,14 @@ def full(p, xmax, ymax): g.setColorAt(1, QColor('#fff')) p.fillRect(x, y, w, w, QBrush(g)) + pen = QPen(QBrush(Qt.blue)) + pen.setWidth(xmax/3) + p.setPen(pen) + x += w + w/10 + y += w + p.drawLine(x, y, x+w, y) + + def run(dev, func): p = QPainter(dev) if isinstance(dev, PdfDevice): @@ -91,12 +99,18 @@ def brush(p, xmax, ymax): p.fillRect(0, y+xmax/1.9, w, w, QBrush(pix)) +def pen(p, xmax, ymax): + pix = QPixmap(I('console.png')) + pen = QPen(QBrush(pix), 60) + p.setPen(pen) + p.drawRect(0, xmax/3, xmax/3, xmax/2) + def main(): app = QApplication([]) app tdir = gettempdir() pdf = os.path.join(tdir, 'painter.pdf') - func = full + func = pen dpi = 100 with open(pdf, 'wb') as f: dev = PdfDevice(f, xdpi=dpi, ydpi=dpi, compress=False)