#!/usr/bin/env python """ PAUL WINKLER'S HACKS to sping/PS.py. Last modified: 10/29/00 Based on sping version: 1.1.0 Use this as a replacement for PS.py by placing it in your python path, then do this in your script: import sping, pwPS; sping.PS = pwPS """ import sping.PS # MODIFIED BY PW: 'name' was a string representing a file # to be opened, written, etc. I'm instead using an actual file # object, but trying to also preserve the old behavior if a string # is passed in... # The reason is that I need to be able to write to stdout. # Also, this version should default to .eps, not .ps if type(name) == type(''): ##print " ****** STRING name!" if len(name) < 4 or string.lower(name[-4:]) != '.eps': self.filename = name + ".eps" else: pass # assume name is actually a file object # First, rename PSCanvas as EPSCanvas. EPSCanvas = sping.PS.PSCanvas def __pw_flush(canvas): # Redefine flush() to deal with possible file objects. if type(self.filename) == type(''): file = open(self.filename,'w') else: # assume self.filename is actually a writable object file = self.filename from string import join file.write(join(self.code,linesep)) # I don't think EPS files end with showpage!!! file.write('\n% showpage\n') EPSCanvas.flush = __pw_flush # Now, here's our new multi-page PSCanvas. class PSCanvas(EPSCanvas): '''This is really intended for multi-page PS documents. For single-page documents, you might be better off with EPSCanvas (which is really the old PSCanvas from the real piddlePS.py). ''' def __init__(self,size=(300,300),name='piddlePS', PostScriptLevel=2): Canvas.__init__(self,size,name) width, height = self.size = size # MODIFIED BY PW: 'name' was a string representing a file # to be opened, written, etc. I'm instead using an actual file # object, but trying to also preserve the old behavior if a string # is passed in... # Also, this version should default to .eps, not .ps if type(name) == type(''): ##print " ****** STRING name!" if len(name) < 4 or string.lower(name[-4:]) != '.eps': self.filename = name + ".eps" else: self.filename = name # assume name is actually a file object ##print "----- USING FOR OUTPUT:" + `self.filename` self.code = [] c = self._currentColor = self.defaultLineColor # Not needed since this is now done on separate pages: #r,g,b = c.red, c.green, c.blue self._currentWidth = self.defaultLineWidth # w = self._currentWidth self.defaultFont = Font(face='serif') self._currentFont = self.defaultFont f = self._findFont(self._currentFont) s = self._currentFont.size # Most setup stuff moved to page setup to make independent pages # draw correctly... self.code.append('''\ %%!PS-Adobe-3.0 %%%%Pages: (atend) %%%%BoundingBox: 0 0 %d %d %%%% Initialization: (%s) findfont %s scalefont setfont ''' % (width, height, f, s)) # select between postscript level 1 or level 2 if PostScriptLevel == 1 : self.drawImage = self._drawImageLevel1 elif PostScriptLevel == 2 : self.drawImage = self._drawImageLevel2 else : raise 'PostScriptLevelException' self.filename = name if type(name) == type(''): if len(name) < 3 or string.lower(name[-3:]) != '.ps': self.filename = name + ".ps" else: pass # assume name is actually a file object # UNCHANGED and thus inherited from EPSCanvas: # _findfont(self, font) #_findExternalFontName(self, font) def beginPage(self, page_offset=0, page_label=None, dsc='', dsc_page_setup='', reset_defaults=1): '''Begins a new page in a multi-page PostScript document. You should call this at least once, and then call endPage() when done with the page. This replaces nextPage in piddlePS.py. Page count is maintained automatically, but a string page_label can also be set, either by giving a numeric page_offset to the page count, or by giving a string value to use. This label is used by e.g. Ghostview for its page selection dialogs. The actual page count is used by default. If you need more advanced postscript code and Adobe Document Structuring Conventions, use the optional arguments dsc and dsc_page_setup. Note that I have not tested these. :-) dsc must be a string containing ONLY DSC comments. It is placed between the %%Page: comment and the %%BeginPageSetup comment. dsc_page_setup is a string which can contain DSC comments or arbitrary postscript code for the %%BeginPageSetup ... %%EndPageSetup section. Note that default font, color, and line-width are normally restored at the start of a new page. You can prevent this by setting the optional reset_defaults argument to a false value. ''' # PW's third attempt at following adobe's document conventions. # Many thanks to Bernhard Herzog for explaining how to do it! try: self._pagecount = self._pagecount + 1 except AttributeError: # must be the first page. self._pagecount = 1 pagename = page_label or `self._pagecount + page_offset` pagename = '(%s)' % pagename self.code.append('\n%%%%Page: %s %d' % (pagename, self._pagecount)) if dsc: self.code.append(dsc) # Now do page setup. height = self.size[1] self.code.append('''%%%%BeginPageSetup 2 setlinecap 0 %d translate''' % height) if reset_defaults: c = self._currentColor = self.defaultLineColor r,g,b = c.red, c.green, c.blue w = self._currentWidth = self.defaultLineWidth self.code.append('''%s %s %s setrgbcolor %s setlinewidth''' % (r, g, b, w)) # user-defined page setup stuff goes here... if dsc_page_setup: self.code.append(dsc_page_setup) self.code.append('%%EndPageSetup') self.code.append('/pgsave save def') def endPage(self): '''Must be called between beginPage calls.''' self.code.append('%% end of page.') # Weird. Adobe DSC spec says showpage should come # after pgsave restore ... but # viewing the resulting files in Ghostview, the pages # are blank. Doing it with showpage first is wrong, # as the pages are not independent; but it seems to work for me. # Note that some other popular PS utilities # put them in this "wrong" order, too... e.g. # GNU enscript 1.6.1 ... I don't know what to think... self.code.append('\npgsave restore\nshowpage') def clear(self): self.endPage() def nextPage(self): raise AttributeError, "Don't use nextPage anymore. Use beginPage and endPage pairs instead!" def flush(self): #self.code.append('showpage') # this appears to prevent multiple calls to flush(). ergh -cwl # PW's attempt at adobe document conventions... if type(self.filename) == type(''): file = open(self.filename,'w') else: # assume self.filename is actually a writable object file = self.filename from string import join file.write(join(self.code,linesep)) try: self._pagecount except AttributeError: self._pagecount = 1 file.write('\n%%%%Trailer\n%%%%Pages: %d\n' % self._pagecount) file.write('%%EOF\n') file.close() #del self.code[-1] # UNCHANGED and thus inherited from original EPSCanvas: # save(self, name=None) # stringWidth(self, s, font=None) # fontAscent(self, font=None) # fontDescent(self, font=None) # _updateLineColor(self, color) # _updateFillColor(self, color) # _updateLineWidth(self, width) # _updateFont(self, font): # def drawLine(self, x1, y1, x2, y2, color=None, width=None): # def drawLines(self, lineList, color=None, width=None): # def _escape(self, s): # def _drawStringOneLineNoRot(self, s, x, y, font=None) : # def _drawStringOneLine(self, s, x, y, font=None, color=None, angle=0): # def drawString(self, s, x, y, font=None, color=None, angle=0): # def drawCurve(self, x1, y1, x2, y2, x3, y3, x4, y4, # edgeColor=None, edgeWidth=None, fillColor=None, closed=0): # def drawRoundRect(self, x1,y1, x2,y2, rx=8, ry=8, # edgeColor=None, edgeWidth=None, fillColor=None): # def drawEllipse(self, x1,y1, x2,y2, edgeColor=None, edgeWidth=None, # fillColor=None): # def drawArc(self, x1,y1, x2,y2, startAng=0, extent=360, edgeColor=None, # edgeWidth=None, fillColor=None): # def _genArcCode(self, x1, y1, x2, y2, startAng, extent): # def drawPolygon(self, pointlist, # edgeColor=None, edgeWidth=None, fillColor=None, closed=0): # def drawFigure(self, partList, # edgeColor=None, edgeWidth=None, fillColor=None, closed=0): # def _drawImageLevel1(self, image, x1, y1, x2=None,y2=None): # def _AsciiHexEncode(self, input): # also based on piddlePDF # def _drawImageLevel2(self, image, x1,y1, x2=None,y2=None): # Postscript Level2 version