From: <jo...@us...> - 2007-09-07 06:58:04
|
Revision: 3803 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3803&view=rev Author: jouni Date: 2007-09-06 23:58:00 -0700 (Thu, 06 Sep 2007) Log Message: ----------- Clip path support in pdf backend Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2007-09-06 20:13:11 UTC (rev 3802) +++ trunk/matplotlib/CHANGELOG 2007-09-07 06:58:00 UTC (rev 3803) @@ -1,3 +1,5 @@ +2007-09-07 Added clip path support to pdf backend. - JKS + 2007-09-06 Fixed a bug in the embedding of Type 1 fonts in PDF. Now it doesn't crash Preview.app. - JKS Modified: trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2007-09-06 20:13:11 UTC (rev 3802) +++ trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2007-09-07 06:58:00 UTC (rev 3803) @@ -64,11 +64,9 @@ # # Some tricky points: # -# 1. The clip rectangle (which could in pdf be an arbitrary path, not -# necessarily a rectangle) can only be widened by popping from the -# state stack. Thus the state must be pushed onto the stack before -# narrowing the rectangle. This is taken care of by -# GraphicsContextPdf. +# 1. The clip path can only be widened by popping from the state +# stack. Thus the state must be pushed onto the stack before narrowing +# the clip path. This is taken care of by GraphicsContextPdf. # # 2. Sometimes it is necessary to refer to something (e.g. font, # image, or extended graphics state, which contains the alpha value) @@ -1072,6 +1070,7 @@ self.writePath(path, fillp) self.endStream() + #@staticmethod def pathBbox(path, lw): path.rewind(0) x, y = [], [] @@ -1086,25 +1085,32 @@ return min(x)-lw, min(y)-lw, max(x)+lw, max(y)+lw pathBbox = staticmethod(pathBbox) - def writePath(self, path, fillp): + #@staticmethod + def pathOperations(path): path.rewind(0) + result = [] while True: - code, xp, yp = path.vertex() + code, x, y = path.vertex() code = code & agg.path_cmd_mask if code == agg.path_cmd_stop: break elif code == agg.path_cmd_move_to: - self.output(xp, yp, Op.moveto) + result += (x, y, Op.moveto) elif code == agg.path_cmd_line_to: - self.output(xp, yp, Op.lineto) + result += (x, y, Op.lineto) elif code == agg.path_cmd_curve3: - pass + pass # TODO elif code == agg.path_cmd_curve4: - pass + pass # TODO elif code == agg.path_cmd_end_poly: - self.output(Op.closepath) + result += (Op.closepath,) else: - print >>sys.stderr, "writePath", code, xp, yp + print >>sys.stderr, "pathOperations", code, xp, yp + return result + pathOperations = staticmethod(pathOperations) + + def writePath(self, path, fillp): + self.output(*self.pathOperations(path)) if fillp: self.output(Op.fill_stroke) else: @@ -1785,49 +1791,64 @@ self.parent = self.parent.parent return [Op.grestore] - def cliprect_cmd(self, cliprect): + def clip_cmd(self, cliprect, clippath): """Set clip rectangle. Calls self.pop() and self.push().""" cmds = [] - while self._cliprect != cliprect and self.parent is not None: + # Pop graphics state until we hit the right one or the stack is empty + while (self._cliprect, self._clippath) != (cliprect, clippath) \ + and self.parent is not None: cmds.extend(self.pop()) - if self._cliprect != cliprect: - cmds.extend(self.push() + - [t for t in cliprect] + - [Op.rectangle, Op.clip, Op.endpath]) + # Unless we hit the right one, set the clip polygon + if (self._cliprect, self._clippath) != (cliprect, clippath): + cmds.append(self.push()) + if self._cliprect != cliprect: + cmds.extend([t for t in cliprect] + + [Op.rectangle, Op.clip, Op.endpath]) + if self._clippath != clippath: + cmds.extend(PdfFile.pathOperations(clippath) + + [Op.clip, Op.endpath]) return cmds commands = ( - ('_cliprect', cliprect_cmd), # must come first since may pop - ('_alpha', alpha_cmd), - ('_capstyle', capstyle_cmd), - ('_fillcolor', fillcolor_cmd), - ('_joinstyle', joinstyle_cmd), - ('_linewidth', linewidth_cmd), - ('_dashes', dash_cmd), - ('_rgb', rgb_cmd), - ('_hatch', hatch_cmd), # must come after fillcolor and rgb + (('_cliprect', '_clippath'), clip_cmd), # must come first since may pop + (('_alpha',), alpha_cmd), + (('_capstyle',), capstyle_cmd), + (('_fillcolor',), fillcolor_cmd), + (('_joinstyle',), joinstyle_cmd), + (('_linewidth',), linewidth_cmd), + (('_dashes',), dash_cmd), + (('_rgb',), rgb_cmd), + (('_hatch',), hatch_cmd), # must come after fillcolor and rgb ) # TODO: _linestyle - def copy_properties(self, other): - """Copy properties of other into self.""" - GraphicsContextBase.copy_properties(self, other) - self._fillcolor = other._fillcolor - def delta(self, other): - """Copy properties of other into self and return PDF commands + """ + Copy properties of other into self and return PDF commands needed to transform self into other. """ cmds = [] - for param, cmd in self.commands: - if getattr(self, param) != getattr(other, param): - cmds.extend(cmd(self, getattr(other, param))) - setattr(self, param, getattr(other, param)) + for params, cmd in self.commands: + ours = [ getattr(self, p) for p in params ] + theirs = [ getattr(other, p) for p in params ] + if ours != theirs: + cmds.extend(cmd(self, *theirs)) + for p in params: + setattr(self, p, getattr(other, p)) return cmds + def copy_properties(self, other): + """ + Copy properties of other into self. + """ + GraphicsContextBase.copy_properties(self, other) + self._fillcolor = other._fillcolor + def finalize(self): - """Make sure every pushed graphics state is popped.""" + """ + Make sure every pushed graphics state is popped. + """ cmds = [] while self.parent is not None: cmds.extend(self.pop()) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |