mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Refactor graphics state handling
This commit is contained in:
parent
df25363d3e
commit
d03a5f252c
@ -14,12 +14,13 @@ from future_builtins import map
|
||||
|
||||
import sip
|
||||
from PyQt4.Qt import (QPaintEngine, QPaintDevice, Qt, QApplication, QPainter,
|
||||
QTransform, QPainterPath, QImage, QByteArray, QBuffer,
|
||||
QTransform, QImage, QByteArray, QBuffer,
|
||||
qRgba)
|
||||
|
||||
from calibre.constants import plugins
|
||||
from calibre.ebooks.pdf.render.serialize import (Color, PDFStream, Path)
|
||||
from calibre.ebooks.pdf.render.serialize import (PDFStream, Path)
|
||||
from calibre.ebooks.pdf.render.common import inch, A4, fmtnum
|
||||
from calibre.ebooks.pdf.render.graphics import convert_path, Graphics
|
||||
from calibre.utils.fonts.sfnt.container import Sfnt
|
||||
from calibre.utils.fonts.sfnt.metrics import FontMetrics
|
||||
|
||||
@ -42,146 +43,6 @@ def store_error(func):
|
||||
|
||||
return errh
|
||||
|
||||
class GraphicsState(object): # {{{
|
||||
|
||||
def __init__(self):
|
||||
self.ops = {}
|
||||
self.initial_state = {
|
||||
'fill': ColorState(Color(0., 0., 0., 1.), 1.0, False),
|
||||
'transform': QTransform(),
|
||||
'dash': [],
|
||||
'line_width': 0,
|
||||
'stroke': ColorState(Color(0., 0., 0., 1.), 1.0, True),
|
||||
'line_cap': 'flat',
|
||||
'line_join': 'miter',
|
||||
'clip': (Qt.NoClip, QPainterPath()),
|
||||
}
|
||||
self.current_state = self.initial_state.copy()
|
||||
|
||||
def reset(self):
|
||||
self.current_state = self.initial_state.copy()
|
||||
|
||||
def update_color_state(self, which, color=None, opacity=None,
|
||||
brush_style=None, pen_style=None):
|
||||
current = self.ops.get(which, self.current_state[which])
|
||||
n = ColorState(*current)
|
||||
if color is not None:
|
||||
n = n._replace(color=Color(*color.getRgbF()))
|
||||
if opacity is not None:
|
||||
n = n._replace(opacity=opacity)
|
||||
if opacity is not None:
|
||||
opacity *= n.color.opacity
|
||||
if brush_style is not None:
|
||||
if which == 'fill':
|
||||
do = (False if opacity == 0.0 or brush_style == Qt.NoBrush else
|
||||
True)
|
||||
else:
|
||||
do = (False if opacity == 0.0 or brush_style == Qt.NoBrush or
|
||||
pen_style == Qt.NoPen else True)
|
||||
n = n._replace(do=do)
|
||||
self.ops[which] = n
|
||||
|
||||
def read(self, state):
|
||||
flags = state.state()
|
||||
|
||||
if flags & QPaintEngine.DirtyTransform:
|
||||
self.ops['transform'] = state.transform()
|
||||
|
||||
# TODO: Add support for brush patterns
|
||||
if flags & QPaintEngine.DirtyBrush:
|
||||
brush = state.brush()
|
||||
color = brush.color()
|
||||
self.update_color_state('fill', color=color,
|
||||
brush_style=brush.style())
|
||||
|
||||
if flags & QPaintEngine.DirtyPen:
|
||||
pen = state.pen()
|
||||
brush = pen.brush()
|
||||
color = pen.color()
|
||||
self.update_color_state('stroke', color, brush_style=brush.style(),
|
||||
pen_style=pen.style())
|
||||
ps = {Qt.DashLine:[3], Qt.DotLine:[1,2], Qt.DashDotLine:[3,2,1,2],
|
||||
Qt.DashDotDotLine:[3, 2, 1, 2, 1, 2]}.get(pen.style(), [])
|
||||
self.ops['dash'] = ps
|
||||
self.ops['line_width'] = pen.widthF()
|
||||
self.ops['line_cap'] = {Qt.FlatCap:'flat', Qt.RoundCap:'round',
|
||||
Qt.SquareCap:'square'}.get(pen.capStyle(), 'flat')
|
||||
self.ops['line_join'] = {Qt.MiterJoin:'miter', Qt.RoundJoin:'round',
|
||||
Qt.BevelJoin:'bevel'}.get(pen.joinStyle(), 'miter')
|
||||
|
||||
if flags & QPaintEngine.DirtyOpacity:
|
||||
self.update_color_state('fill', opacity=state.opacity())
|
||||
self.update_color_state('stroke', opacity=state.opacity())
|
||||
|
||||
if flags & QPaintEngine.DirtyClipPath or flags & QPaintEngine.DirtyClipRegion:
|
||||
self.ops['clip'] = True
|
||||
|
||||
def __call__(self, engine):
|
||||
if not self.ops:
|
||||
return
|
||||
pdf = engine.pdf
|
||||
ops = self.ops
|
||||
current_transform = self.current_state['transform']
|
||||
transform_changed = 'transform' in ops and ops['transform'] != current_transform
|
||||
reset_stack = transform_changed or 'clip' in ops
|
||||
|
||||
if reset_stack:
|
||||
pdf.restore_stack()
|
||||
pdf.save_stack()
|
||||
# Since we have reset the stack we need to re-apply all previous
|
||||
# operations, that are different from the default value (clip is
|
||||
# handled separately).
|
||||
for op in set(self.initial_state) - {'clip'}:
|
||||
if op in ops: # These will be applied below
|
||||
self.current_state[op] = self.initial_state[op]
|
||||
elif self.current_state[op] != self.initial_state[op]:
|
||||
self.apply(op, self.current_state[op], engine, pdf)
|
||||
|
||||
# Now apply the new operations
|
||||
for op, val in ops.iteritems():
|
||||
if op != 'clip' and self.current_state[op] != val:
|
||||
self.apply(op, val, engine, pdf)
|
||||
self.current_state[op] = val
|
||||
|
||||
if 'clip' in ops:
|
||||
# Get the current clip
|
||||
path = engine.painter().clipPath()
|
||||
if not path.isEmpty():
|
||||
engine.add_clip(path)
|
||||
self.ops = {}
|
||||
|
||||
def apply(self, op, val, engine, pdf):
|
||||
getattr(self, 'apply_'+op)(val, engine, pdf)
|
||||
|
||||
def apply_transform(self, val, engine, pdf):
|
||||
if not val.isIdentity():
|
||||
pdf.transform(val)
|
||||
|
||||
def apply_stroke(self, val, engine, pdf):
|
||||
self.apply_color_state('stroke', val, engine, pdf)
|
||||
|
||||
def apply_fill(self, val, engine, pdf):
|
||||
self.apply_color_state('fill', val, engine, pdf)
|
||||
|
||||
def apply_color_state(self, which, val, engine, pdf):
|
||||
color = val.color._replace(opacity=val.opacity*val.color.opacity)
|
||||
getattr(pdf, 'set_%s_color'%which)(color)
|
||||
setattr(engine, 'do_%s'%which, val.do)
|
||||
|
||||
def apply_dash(self, val, engine, pdf):
|
||||
pdf.set_dash(val)
|
||||
|
||||
def apply_line_width(self, val, engine, pdf):
|
||||
pdf.set_line_width(val)
|
||||
|
||||
def apply_line_cap(self, val, engine, pdf):
|
||||
pdf.set_line_cap(val)
|
||||
|
||||
def apply_line_join(self, val, engine, pdf):
|
||||
pdf.set_line_join(val)
|
||||
|
||||
# }}}
|
||||
|
||||
class Font(FontMetrics):
|
||||
|
||||
def __init__(self, sfnt):
|
||||
@ -215,9 +76,7 @@ class PdfEngine(QPaintEngine):
|
||||
self.bottom_margin) / self.pixel_height
|
||||
|
||||
self.pdf_system = QTransform(sx, 0, 0, -sy, dx, dy)
|
||||
self.do_stroke = True
|
||||
self.do_fill = False
|
||||
self.graphics_state = GraphicsState()
|
||||
self.graphics = Graphics()
|
||||
self.errors_occurred = False
|
||||
self.errors, self.debug = errors, debug
|
||||
self.fonts = {}
|
||||
@ -230,14 +89,21 @@ class PdfEngine(QPaintEngine):
|
||||
if err:
|
||||
raise RuntimeError('Failed to load qt_hack with err: %s'%err)
|
||||
|
||||
def apply_graphics_state(self):
|
||||
self.graphics(self.pdf, self.pdf_system, self.painter())
|
||||
|
||||
@property
|
||||
def do_fill(self):
|
||||
return self.graphics.current_state.do_fill
|
||||
|
||||
@property
|
||||
def do_stroke(self):
|
||||
return self.graphics.current_state.do_stroke
|
||||
|
||||
def init_page(self):
|
||||
self.pdf.transform(self.pdf_system)
|
||||
self.pdf.set_rgb_colorspace()
|
||||
width = self.painter().pen().widthF() if self.isActive() else 0
|
||||
self.pdf.set_line_width(width)
|
||||
self.do_stroke = True
|
||||
self.do_fill = False
|
||||
self.graphics_state.reset()
|
||||
self.graphics.reset()
|
||||
self.pdf.save_stack()
|
||||
self.current_page_inited = True
|
||||
|
||||
@ -287,7 +153,7 @@ class PdfEngine(QPaintEngine):
|
||||
|
||||
@store_error
|
||||
def drawPixmap(self, rect, pixmap, source_rect):
|
||||
self.graphics_state(self)
|
||||
self.apply_graphics_state()
|
||||
source_rect = source_rect.toRect()
|
||||
pixmap = (pixmap if source_rect == pixmap.rect() else
|
||||
pixmap.copy(source_rect))
|
||||
@ -299,7 +165,7 @@ class PdfEngine(QPaintEngine):
|
||||
|
||||
@store_error
|
||||
def drawImage(self, rect, image, source_rect, flags=Qt.AutoColor):
|
||||
self.graphics_state(self)
|
||||
self.apply_graphics_state()
|
||||
source_rect = source_rect.toRect()
|
||||
image = (image if source_rect == image.rect() else
|
||||
image.copy(source_rect))
|
||||
@ -374,50 +240,20 @@ class PdfEngine(QPaintEngine):
|
||||
|
||||
@store_error
|
||||
def updateState(self, state):
|
||||
self.graphics_state.read(state)
|
||||
|
||||
def convert_path(self, path):
|
||||
p = Path()
|
||||
i = 0
|
||||
while i < path.elementCount():
|
||||
elem = path.elementAt(i)
|
||||
em = (elem.x, elem.y)
|
||||
i += 1
|
||||
if elem.isMoveTo():
|
||||
p.move_to(*em)
|
||||
elif elem.isLineTo():
|
||||
p.line_to(*em)
|
||||
elif elem.isCurveTo():
|
||||
added = False
|
||||
if path.elementCount() > i+1:
|
||||
c1, c2 = path.elementAt(i), path.elementAt(i+1)
|
||||
if (c1.type == path.CurveToDataElement and c2.type ==
|
||||
path.CurveToDataElement):
|
||||
i += 2
|
||||
p.curve_to(em[0], em[1], c1.x, c1.y, c2.x, c2.y)
|
||||
added = True
|
||||
if not added:
|
||||
raise ValueError('Invalid curve to operation')
|
||||
return p
|
||||
self.graphics.update_state(state, self.painter())
|
||||
|
||||
@store_error
|
||||
def drawPath(self, path):
|
||||
self.graphics_state(self)
|
||||
p = self.convert_path(path)
|
||||
self.apply_graphics_state()
|
||||
p = convert_path(path)
|
||||
fill_rule = {Qt.OddEvenFill:'evenodd',
|
||||
Qt.WindingFill:'winding'}[path.fillRule()]
|
||||
self.pdf.draw_path(p, stroke=self.do_stroke,
|
||||
fill=self.do_fill, fill_rule=fill_rule)
|
||||
|
||||
def add_clip(self, path):
|
||||
p = self.convert_path(path)
|
||||
fill_rule = {Qt.OddEvenFill:'evenodd',
|
||||
Qt.WindingFill:'winding'}[path.fillRule()]
|
||||
self.pdf.add_clip(p, fill_rule=fill_rule)
|
||||
|
||||
@store_error
|
||||
def drawPoints(self, points):
|
||||
self.graphics_state(self)
|
||||
self.apply_graphics_state()
|
||||
p = Path()
|
||||
for point in points:
|
||||
p.move_to(point.x(), point.y())
|
||||
@ -426,7 +262,7 @@ class PdfEngine(QPaintEngine):
|
||||
|
||||
@store_error
|
||||
def drawRects(self, rects):
|
||||
self.graphics_state(self)
|
||||
self.apply_graphics_state()
|
||||
for rect in rects:
|
||||
bl = rect.topLeft()
|
||||
self.pdf.draw_rect(bl.x(), bl.y(), rect.width(), rect.height(),
|
||||
@ -446,7 +282,7 @@ class PdfEngine(QPaintEngine):
|
||||
@store_error
|
||||
def drawTextItem(self, point, text_item):
|
||||
# super(PdfEngine, self).drawTextItem(point, text_item)
|
||||
self.graphics_state(self)
|
||||
self.apply_graphics_state()
|
||||
gi = self.qt_hack.get_glyphs(point, text_item)
|
||||
if not gi.indices:
|
||||
sip.delete(gi)
|
||||
@ -477,7 +313,7 @@ class PdfEngine(QPaintEngine):
|
||||
|
||||
@store_error
|
||||
def drawPolygon(self, points, mode):
|
||||
self.graphics_state(self)
|
||||
self.apply_graphics_state()
|
||||
if not points: return
|
||||
p = Path()
|
||||
p.move_to(points[0].x(), points[0].y())
|
||||
@ -510,14 +346,6 @@ class PdfEngine(QPaintEngine):
|
||||
link.append((llx, lly, urx, ury))
|
||||
self.pdf.links.add(current_item, start_page, links, anchors)
|
||||
|
||||
def __enter__(self):
|
||||
self.pdf.save_stack()
|
||||
self.saved_ps = (self.do_stroke, self.do_fill)
|
||||
|
||||
def __exit__(self, *args):
|
||||
self.do_stroke, self.do_fill = self.saved_ps
|
||||
self.pdf.restore_stack()
|
||||
|
||||
class PdfDevice(QPaintDevice): # {{{
|
||||
|
||||
|
||||
@ -584,8 +412,8 @@ class PdfDevice(QPaintDevice): # {{{
|
||||
# }}}
|
||||
|
||||
if __name__ == '__main__':
|
||||
from PyQt4.Qt import (QBrush, QColor, QPoint, QPixmap)
|
||||
QBrush, QColor, QPoint, QPixmap
|
||||
from PyQt4.Qt import (QBrush, QColor, QPoint, QPixmap, QPainterPath)
|
||||
QBrush, QColor, QPoint, QPixmap, QPainterPath
|
||||
app = QApplication([])
|
||||
p = QPainter()
|
||||
with open('/t/painter.pdf', 'wb') as f:
|
||||
@ -593,6 +421,7 @@ if __name__ == '__main__':
|
||||
p.begin(dev)
|
||||
dev.init_page()
|
||||
xmax, ymax = p.viewport().width(), p.viewport().height()
|
||||
b = p.brush()
|
||||
try:
|
||||
p.drawRect(0, 0, xmax, ymax)
|
||||
# p.drawPolyline(QPoint(0, 0), QPoint(xmax, 0), QPoint(xmax, ymax),
|
||||
@ -600,27 +429,22 @@ 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.fillRect(0, 0, xmax/10, xmax/10, QBrush(QColor(*col)))
|
||||
p.setOpacity(1)
|
||||
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)
|
||||
# p.scale(2, 2)
|
||||
# p.rotate(45)
|
||||
# p.drawLine(0, 0, 5000, 0)
|
||||
# p.restore()
|
||||
p.drawPixmap(0, 0, 2048, 2048, QPixmap(I('library.png')))
|
||||
p.drawRect(0, 0, 2048, 2048)
|
||||
|
||||
f = p.font()
|
||||
f.setPointSize(20)
|
||||
|
196
src/calibre/ebooks/pdf/render/graphics.py
Normal file
196
src/calibre/ebooks/pdf/render/graphics.py
Normal file
@ -0,0 +1,196 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai
|
||||
from __future__ import (unicode_literals, division, absolute_import,
|
||||
print_function)
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
from math import sqrt
|
||||
|
||||
from PyQt4.Qt import (QBrush, QPen, Qt, QPointF, QTransform, QPainterPath,
|
||||
QPaintEngine)
|
||||
|
||||
from calibre.ebooks.pdf.render.common import Array
|
||||
from calibre.ebooks.pdf.render.serialize import Path, Color
|
||||
|
||||
def convert_path(path):
|
||||
p = Path()
|
||||
i = 0
|
||||
while i < path.elementCount():
|
||||
elem = path.elementAt(i)
|
||||
em = (elem.x, elem.y)
|
||||
i += 1
|
||||
if elem.isMoveTo():
|
||||
p.move_to(*em)
|
||||
elif elem.isLineTo():
|
||||
p.line_to(*em)
|
||||
elif elem.isCurveTo():
|
||||
added = False
|
||||
if path.elementCount() > i+1:
|
||||
c1, c2 = path.elementAt(i), path.elementAt(i+1)
|
||||
if (c1.type == path.CurveToDataElement and c2.type ==
|
||||
path.CurveToDataElement):
|
||||
i += 2
|
||||
p.curve_to(em[0], em[1], c1.x, c1.y, c2.x, c2.y)
|
||||
added = True
|
||||
if not added:
|
||||
raise ValueError('Invalid curve to operation')
|
||||
return p
|
||||
|
||||
|
||||
class GraphicsState(object):
|
||||
|
||||
FIELDS = ('fill', 'stroke', 'opacity', 'transform', 'brush_origin',
|
||||
'clip', 'do_fill', 'do_stroke')
|
||||
|
||||
def __init__(self):
|
||||
self.fill = QBrush()
|
||||
self.stroke = QPen()
|
||||
self.opacity = 1.0
|
||||
self.transform = QTransform()
|
||||
self.brush_origin = QPointF()
|
||||
self.clip = QPainterPath()
|
||||
self.do_fill = False
|
||||
self.do_stroke = True
|
||||
|
||||
def __eq__(self, other):
|
||||
for x in self.FIELDS:
|
||||
if getattr(other, x) != getattr(self, x):
|
||||
return False
|
||||
return True
|
||||
|
||||
def copy(self):
|
||||
ans = GraphicsState()
|
||||
ans.fill = QBrush(self.fill)
|
||||
ans.stroke = QPen(self.stroke)
|
||||
ans.opacity = self.opacity
|
||||
ans.transform = self.transform * QTransform()
|
||||
ans.brush_origin = QPointF(self.brush_origin)
|
||||
ans.clip = self.clip
|
||||
ans.do_fill, ans.do_stroke = self.do_fill, self.do_stroke
|
||||
return ans
|
||||
|
||||
class Graphics(object):
|
||||
|
||||
def __init__(self):
|
||||
self.base_state = GraphicsState()
|
||||
self.current_state = GraphicsState()
|
||||
self.pending_state = None
|
||||
|
||||
def update_state(self, state, painter):
|
||||
flags = state.state()
|
||||
if self.pending_state is None:
|
||||
self.pending_state = self.current_state.copy()
|
||||
|
||||
s = self.pending_state
|
||||
|
||||
if flags & QPaintEngine.DirtyTransform:
|
||||
s.transform = state.transform()
|
||||
|
||||
if flags & QPaintEngine.DirtyBrushOrigin:
|
||||
s.brush_origin = state.brushOrigin()
|
||||
|
||||
if flags & QPaintEngine.DirtyBrush:
|
||||
s.fill = state.brush()
|
||||
|
||||
if flags & QPaintEngine.DirtyPen:
|
||||
s.stroke = state.pen()
|
||||
|
||||
if flags & QPaintEngine.DirtyOpacity:
|
||||
s.opacity = state.opacity()
|
||||
|
||||
if flags & QPaintEngine.DirtyClipPath or flags & QPaintEngine.DirtyClipRegion:
|
||||
s.clip = painter.clipPath()
|
||||
|
||||
def reset(self):
|
||||
self.current_state = GraphicsState()
|
||||
self.pending_state = None
|
||||
|
||||
def __call__(self, pdf, pdf_system, painter):
|
||||
# Apply the currently pending state to the PDF
|
||||
if self.pending_state is None:
|
||||
return
|
||||
|
||||
pdf_state = self.current_state
|
||||
ps = self.pending_state
|
||||
|
||||
if (ps.transform != pdf_state.transform or ps.clip != pdf_state.clip):
|
||||
pdf.restore_stack()
|
||||
pdf.save_stack()
|
||||
pdf_state = self.base_state
|
||||
|
||||
if (pdf_state.transform != ps.transform):
|
||||
pdf.transform(ps.transform)
|
||||
|
||||
if (pdf_state.opacity != ps.opacity or pdf_state.stroke != ps.stroke):
|
||||
self.apply_stroke(ps, pdf, pdf_system, painter)
|
||||
|
||||
if (pdf_state.opacity != ps.opacity or pdf_state.fill != ps.fill or
|
||||
pdf_state.brush_origin != ps.brush_origin):
|
||||
self.apply_fill(ps, pdf, pdf_system, painter)
|
||||
|
||||
if (pdf_state.clip != ps.clip):
|
||||
p = convert_path(ps.clip)
|
||||
fill_rule = {Qt.OddEvenFill:'evenodd',
|
||||
Qt.WindingFill:'winding'}[ps.clip.fillRule()]
|
||||
pdf.add_clip(p, fill_rule=fill_rule)
|
||||
|
||||
self.current_state = self.pending_state
|
||||
self.pending_state = None
|
||||
|
||||
def apply_stroke(self, state, pdf, 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
|
||||
if pen.style() == Qt.NoPen:
|
||||
self.pending_state.do_stroke = False
|
||||
|
||||
# Width
|
||||
w = pen.widthF()
|
||||
if pen.isCosmetic():
|
||||
t = painter.transform()
|
||||
w /= sqrt(t.m11()**2 + t.m22()**2)
|
||||
pdf.serialize(w)
|
||||
pdf.current_page.write(' w ')
|
||||
|
||||
# Line cap
|
||||
cap = {Qt.FlatCap:0, Qt.RoundCap:1, Qt.SquareCap:
|
||||
2}.get(pen.capStyle(), 0)
|
||||
pdf.current_page.write('%d J '%cap)
|
||||
|
||||
# Line join
|
||||
join = {Qt.MiterJoin:0, Qt.RoundJoin:1,
|
||||
Qt.BevelJoin:2}.get(pen.joinStyle(), 0)
|
||||
pdf.current_page.write('%d j '%join)
|
||||
|
||||
# Dash pattern
|
||||
ps = {Qt.DashLine:[3], Qt.DotLine:[1,2], Qt.DashDotLine:[3,2,1,2],
|
||||
Qt.DashDotDotLine:[3, 2, 1, 2, 1, 2]}.get(pen.style(), [])
|
||||
if ps:
|
||||
pdf.serialize(Array(ps))
|
||||
pdf.current_page.write(' 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
|
||||
|
||||
def apply_fill(self, state, pdf, pdf_system, painter):
|
||||
self.pending_state.do_fill = True
|
||||
b = state.fill
|
||||
if b.style() == Qt.NoBrush:
|
||||
self.pending_state.do_fill = False
|
||||
vals = list(b.color().getRgbF())
|
||||
vals[-1] *= state.opacity
|
||||
color = Color(*vals)
|
||||
pdf.set_fill_color(color)
|
||||
|
@ -369,25 +369,8 @@ class PDFStream(object):
|
||||
op = 'W' if fill_rule == 'winding' else 'W*'
|
||||
self.current_page.write_line(op + ' ' + 'n')
|
||||
|
||||
def set_dash(self, array, phase=0):
|
||||
array = Array(array)
|
||||
serialize(array, self.current_page)
|
||||
self.current_page.write(b' ')
|
||||
serialize(phase, self.current_page)
|
||||
self.current_page.write_line(' d')
|
||||
|
||||
def set_line_width(self, width):
|
||||
serialize(width, self.current_page)
|
||||
self.current_page.write_line(' w')
|
||||
|
||||
def set_line_cap(self, style):
|
||||
serialize({'flat':0, 'round':1, 'square':2}.get(style),
|
||||
self.current_page)
|
||||
self.current_page.write_line(' J')
|
||||
|
||||
def set_line_join(self, style):
|
||||
serialize({'miter':0, 'round':1, 'bevel':2}[style], self.current_page)
|
||||
self.current_page.write_line(' j')
|
||||
def serialize(self, o):
|
||||
serialize(o, self.current_page)
|
||||
|
||||
def set_stroke_color(self, color):
|
||||
opacity = color.opacity
|
||||
|
Loading…
x
Reference in New Issue
Block a user