def arcTo(self, x1, y1, x2, y2, startAng=0, extent=90): """Like arc, but draws a line from the current point to the start if the start is not the current point.""" pointList = pdfgeom.bezierArc(x1, y1, x2, y2, startAng, extent) self._code.append("%s l" % fp_str(pointList[0][:2])) for curve in pointList: self._code.append("%s c" % fp_str(curve[2:]))
def setStrokeColor(self, aColor, alpha=None): """Takes a color object, allowing colors to be referred to by name""" if isinstance(aColor, CMYKColor): d = aColor.density c,m,y,k = (d*aColor.cyan, d*aColor.magenta, d*aColor.yellow, d*aColor.black) self._strokeColorObj = aColor name = self._checkSeparation(aColor) if name: self._code.append('/%s CS %s SCN' % (name,fp_str(d))) else: self._code.append('%s K' % fp_str(c, m, y, k)) elif isinstance(aColor, Color): rgb = (aColor.red, aColor.green, aColor.blue) self._strokeColorObj = aColor self._code.append('%s RG' % fp_str(rgb) ) elif isinstance(aColor,(tuple,list)): l = len(aColor) if l==3: self._strokeColorObj = aColor self._code.append('%s RG' % fp_str(aColor) ) elif l==4: self.setStrokeColorCMYK(aColor[0], aColor[1], aColor[2], aColor[3]) else: raise ValueError('Unknown color %r' % aColor) elif isinstance(aColor,basestring): self.setStrokeColor(toColor(aColor)) else: raise ValueError('Unknown color %r' % aColor) if alpha is not None: self.setStrokeAlpha(alpha) elif getattr(aColor, 'alpha', None) is not None: self.setStrokeAlpha(aColor.alpha)
def setFillColor(self, aColor, alpha=None): """Takes a color object, allowing colors to be referred to by name""" if self._enforceColorSpace: aColor = self._enforceColorSpace(aColor) if isinstance(aColor, CMYKColor): d = aColor.density c, m, y, k = (d * aColor.cyan, d * aColor.magenta, d * aColor.yellow, d * aColor.black) self._fillColorObj = aColor name = self._checkSeparation(aColor) if name: self._code.append("/%s cs %s scn" % (name, fp_str(d))) else: self._code.append("%s k" % fp_str(c, m, y, k)) elif isinstance(aColor, Color): rgb = (aColor.red, aColor.green, aColor.blue) self._fillColorObj = aColor self._code.append("%s rg" % fp_str(rgb)) elif isinstance(aColor, (tuple, list)): l = len(aColor) if l == 3: self._fillColorObj = aColor self._code.append("%s rg" % fp_str(aColor)) elif l == 4: self._fillColorObj = aColor self._code.append("%s k" % fp_str(aColor)) else: raise ValueError("Unknown color %r" % aColor) elif isinstance(aColor, basestring): self.setFillColor(toColor(aColor)) else: raise ValueError("Unknown color %r" % aColor) if alpha is not None: self.setFillAlpha(alpha) elif getattr(aColor, "alpha", None) is not None: self.setFillAlpha(aColor.alpha)
def __repr__(self): return "CMYKColor(%s%s%s%s)" % ( string.replace(fp_str(self.cyan, self.magenta, self.yellow, self.black), " ", ","), (self.spotName and (",spotName=" + repr(self.spotName)) or ""), (self.density != 1 and (",density=" + fp_str(self.density)) or ""), (self.knockout is not None and (",knockout=%d" % self.knockout) or ""), )
def __repr__(self): return "CMYKColor(%s%s%s%s)" % ( string.replace(fp_str(self.cyan, self.magenta, self.yellow, self.black),' ',','), (self.spotName and (',spotName='+repr(self.spotName)) or ''), (self.density!=1 and (',density='+fp_str(self.density)) or ''), (self.knockout is not None and (',knockout=%d' % self.knockout) or ''), )
def _issueT1String(self,fontObj,x,y,s): fc = fontObj code_append = self.code_append fontSize = self._fontSize fontsUsed = self._fontsUsed escape = self._escape if not isinstance(s,str): try: s = s.decode('utf8') except UnicodeDecodeError as e: i,j = e.args[2:4] raise UnicodeDecodeError(*(e.args[:4]+('%s\n%s-->%s<--%s' % (e.args[4],s[i-10:i],s[i:j],s[j:j+10]),))) for f, t in unicode2T1(s,[fontObj]+fontObj.substitutionFonts): if f!=fc: psName = f.face.name code_append('(%s) findfont %s scalefont setfont' % (psName,fp_str(fontSize))) if psName not in fontsUsed: fontsUsed.append(psName) fc = f code_append('%s m (%s) show ' % (fp_str(x,y),escape(t))) x += f.stringWidth(t.decode(f.encName),fontSize) if fontObj!=fc: self._font = None self.setFont(fontObj.face.name,fontSize)
def __repr__(self): return "%s(%s%s%s%s%s)" % (self.__class__.__name__, fp_str(self.cyan*100, self.magenta*100, self.yellow*100, self.black*100).replace(' ',','), (self.spotName and (',spotName='+repr(self.spotName)) or ''), (self.density!=1 and (',density='+fp_str(self.density*100)) or ''), (self.knockout is not None and (',knockout=%d' % self.knockout) or ''), (self.alpha is not None and (',alpha=%d' % (self.alpha*100)) or ''), )
def setColor(self, color): if self._color!=color: self._color = color if color: if hasattr(color, "cyan"): self.code_append('%s setcmykcolor' % fp_str(color.cyan, color.magenta, color.yellow, color.black)) else: self.code_append('%s setrgbcolor' % fp_str(color.red, color.green, color.blue))
def setTextOrigin(self, x, y): if self._canvas.bottomup: self._code.append('1 0 0 1 %s Tm' % fp_str(x, y)) #bottom up else: self._code.append('1 0 0 -1 %s Tm' % fp_str(x, y)) #top down self._x = x self._y = y self._x0 = x #the margin
def drawCurve(self, x1, y1, x2, y2, x3, y3, x4, y4, closed=0): codeline = "%s m %s curveto" data = (fp_str(x1, y1), fp_str(x2, y2, x3, y3, x4, y4)) if self._fillColor != None: self.setColor(self._fillColor) self.code_append((codeline % data) + " eofill") if self._strokeColor != None: self.setColor(self._strokeColor) self.code_append((codeline % data) + ((closed and " closepath") or "") + " stroke")
def __repr__(self): return "%s(%s%s%s%s%s)" % ( self.__class__.__name__, fp_str(self.cyan * 100, self.magenta * 100, self.yellow * 100, self.black * 100).replace(" ", ","), (self.spotName and (",spotName=" + repr(self.spotName)) or ""), (self.density != 1 and (",density=" + fp_str(self.density * 100)) or ""), (self.knockout is not None and (",knockout=%d" % self.knockout) or ""), (self.alpha is not None and (",alpha=%s" % (fp_str(self.alpha * 100))) or ""), )
def lines(self, lineList, color=None, width=None): # print "### lineList", lineList return if self._strokeColor != None: self._setColor(self._strokeColor) codeline = "%s m %s l stroke" for line in lineList: self.code.append(codeline % (fp_str(line[0]), fp_str(line[1])))
def setTextOrigin(self, x, y): if self._canvas.bottomup: self._code.append('1 0 0 1 %s Tm' % fp_str(x, y)) #bottom up else: self._code.append('1 0 0 -1 %s Tm' % fp_str(x, y)) #top down # The current cursor position is at the text origin self._x0 = self._x = x self._y0 = self._y = y
def drawCurve(self, x1, y1, x2, y2, x3, y3, x4, y4, closed=0): codeline = '%s m %s curveto' data = (fp_str(x1, y1), fp_str(x2, y2, x3, y3, x4, y4)) if self._fillColor != None: self.setColor(self._fillColor) self.code_append((codeline % data) + ' eofill') if self._strokeColor != None: self.setColor(self._strokeColor) self.code_append((codeline % data) + ((closed and ' closepath') or '') + ' stroke')
def drawInlineImage(self, canvas): #, image, x,y, width=None,height=None): """Draw an Image into the specified rectangle. If width and height are omitted, they are calculated from the image size. Also allow file names as well as images. This allows a caching mechanism""" (x,y) = self.point # this says where and how big to draw it if not canvas.bottomup: y = y+self.height canvas._code.append('q %s 0 0 %s cm' % (fp_str(self.width), fp_str(self.height, x, y))) # self._code.extend(imagedata) if >=python-1.5.2 for line in self.imageData: canvas._code.append(line) canvas._code.append('Q')
def polygon(self, p, closed=0, stroke=1, fill=1): assert len(p) >= 2, 'Polygon must have 2 or more points' start = p[0] p = p[1:] polyCode = [] polyCode.append("%s m" % fp_str(start)) for point in p: polyCode.append("%s l" % fp_str(point)) if closed: polyCode.append("closepath") self._fillAndStroke(polyCode,stroke=stroke,fill=fill)
def arc(self, x1, y1, x2, y2, startAng=0, extent=90): """Contributed to piddlePDF by Robert Kern, 28/7/99. Draw a partial ellipse inscribed within the rectangle x1,y1,x2,y2, starting at startAng degrees and covering extent degrees. Angles start with 0 to the right (+x) and increase counter-clockwise. These should have x1<x2 and y1<y2. The algorithm is an elliptical generalization of the formulae in Jim Fitzsimmon's TeX tutorial <URL: http://www.tinaja.com/bezarc1.pdf>.""" pointList = pdfgeom.bezierArc(x1, y1, x2, y2, startAng, extent) # move to first point self._code.append("%s m" % fp_str(pointList[0][:2])) for curve in pointList: self._code.append("%s c" % fp_str(curve[2:]))
def polygon(self, p, closed=0, stroke=1, fill=1): assert len(p) >= 2, "Polygon must have 2 or more points" start = p[0] p = p[1:] poly = [] a = poly.append a("%s m" % fp_str(start)) for point in p: a("%s l" % fp_str(point)) if closed: a("closepath") self._fillAndStroke(poly, stroke=stroke, fill=fill)
def _formatText(self, text): "Generates PDF text output operator(s)" if self._dynamicFont: #it's a truetype font and should be utf8. If an error is raised, results = [] font = pdfmetrics.getFont(self._fontname) try: #assume UTF8 stuff = font.splitString(text, self._canvas._doc) except UnicodeDecodeError: #assume latin1 as fallback from reportlab.pdfbase.ttfonts import latin1_to_utf8 from reportlab.lib.logger import warnOnce warnOnce('non-utf8 data fed to truetype font, assuming latin-1 data') text = latin1_to_utf8(text) stuff = font.splitString(text, self._canvas._doc) for subset, chunk in stuff: if subset != self._curSubset: pdffontname = font.getSubsetInternalName(subset, self._canvas._doc) results.append("%s %s Tf %s TL" % (pdffontname, fp_str(self._fontsize), fp_str(self._leading))) self._curSubset = subset chunk = self._canvas._escape(chunk) results.append("(%s) Tj" % chunk) return string.join(results, ' ') else: text = self._canvas._escape(text) return "(%s) Tj" % text
def _repr(self,I=None): '''return a repr style string with named fixed args first, then keywords''' if type(self) is InstanceType: if self is EmptyClipPath: _addObjImport(self,I,'EmptyClipPath') return 'EmptyClipPath' if I: _addObjImport(self,I) if isinstance(self,Shape): from inspect import getargs args, varargs, varkw = getargs(self.__init__.im_func.func_code) P = self.getProperties() s = self.__class__.__name__+'(' for n in args[1:]: v = P[n] del P[n] s = s + '%s,' % _repr(v,I) for n,v in P.items(): v = P[n] s = s + '%s=%s,' % (n, _repr(v,I)) return s[:-1]+')' else: return repr(self) elif type(self) is FloatType: return fp_str(self) elif type(self) in (ListType,TupleType): s = '' for v in self: s = s + '%s,' % _repr(v,I) if type(self) is ListType: return '[%s]' % s[:-1] else: return '(%s%s)' % (s[:-1],len(self)==1 and ',' or '') else: return repr(self)
def setFont(self, psfontname, size, leading = None): """Sets the font. If leading not specified, defaults to 1.2 x font size. Raises a readable exception if an illegal font is supplied. Font names are case-sensitive! Keeps track of font anme and size for metrics.""" self._fontname = psfontname self._fontsize = size if leading is None: leading = size * 1.2 self._leading = leading font = pdfmetrics.getFont(self._fontname) if font._dynamicFont: self._curSubset = -1 else: pdffontname = self._canvas._doc.getInternalFontName(psfontname) self._code.append('%s %s Tf %s TL' % (pdffontname, fp_str(size), fp_str(leading)))
def setStrokeColorRGB(self, r, g, b, alpha=None): """Set the stroke color using positive color description (Red,Green,Blue). Takes 3 arguments between 0.0 and 1.0""" self._strokeColorObj = (r, g, b) self._code.append('%s RG' % fp_str(r,g,b)) if alpha is not None: self.setStrokeAlpha(alpha)
def setStrokeColorCMYK(self, c, m, y, k, alpha=None): """set the stroke color useing negative color values (cyan, magenta, yellow and darkness value). Takes 4 arguments between 0.0 and 1.0""" self._strokeColorObj = (c, m, y, k) self._code.append('%s K' % fp_str(c, m, y, k)) if alpha is not None: self.setStrokeAlpha(alpha)
def setStrokeColor(self, aColor): """Takes a color object, allowing colors to be referred to by name""" if type(aColor) == ColorType: rgb = (aColor.red, aColor.green, aColor.blue) self._strokeColorRGB = rgb self._code.append('%s RG' % fp_str(rgb) ) elif type(aColor) in _SeqTypes: l = len(aColor) if l==3: self._strokeColorRGB = aColor self._code.append('%s RG' % fp_str(aColor) ) elif l==4: self.setStrokeColorCMYK(self, aColor[0], aColor[1], aColor[2], aColor[3]) else: raise 'Unknown color', str(aColor) else: raise 'Unknown color', str(aColor)
def drawInlineImage(self, canvas, preserveAspectRatio=False,anchor='sw'): """Draw an Image into the specified rectangle. If width and height are omitted, they are calculated from the image size. Also allow file names as well as images. This allows a caching mechanism""" width = self.width height = self.height if width<1e-6 or height<1e-6: return False x,y,self.width,self.height, scaled = aspectRatioFix(preserveAspectRatio,anchor,self.x,self.y,width,height,self.imgwidth,self.imgheight) # this says where and how big to draw it if not canvas.bottomup: y = y+height canvas._code.append('q %s 0 0 %s cm' % (fp_str(self.width), fp_str(self.height, x, y))) # self._code.extend(imagedata) if >=python-1.5.2 for line in self.imageData: canvas._code.append(line) canvas._code.append('Q') return True
def setTextTransform(self, a, b, c, d, e, f): "Like setTextOrigin, but does rotation, scaling etc." if not self._canvas.bottomup: c = -c #reverse bottom row of the 2D Transform d = -d self._code.append('%s Tm' % fp_str(a, b, c, d, e, f)) #we only measure coords relative to present text matrix self._x = e self._y = f
def drawString(self, x, y, s, angle=0): if self._fillColor != None: fontObj = getFont(self._font) if not self.code[self._fontCodeLoc]: psName = fontObj.face.name self.code[self._fontCodeLoc]='(%s) findfont %s scalefont setfont' % (psName,fp_str(self._fontSize)) if psName not in self._fontsUsed: self._fontsUsed.append(psName) self.setColor(self._fillColor) if angle!=0: self.code_append('gsave %s translate %s rotate' % (fp_str(x,y),fp_str(angle))) x = y = 0 if fontObj._dynamicFont: s = self._escape(s) self.code_append('%s m (%s) show ' % (fp_str(x,y),s)) else: self._issueT1String(fontObj,x,y,s) if angle!=0: self.code_append('grestore')
def _formatText(self, text): "Generates PDF text output operator(s)" # Use pyfribidi to write the text in the correct visual order. directions = { 'LTR': DIR_LTR, 'RTL': DIR_RTL } text = log2vis(text, directions.get(self.direction, DIR_ON), reordernsm=False) text = remove_noprint(text) canv = self._canvas font = pdfmetrics.getFont(self._fontname) R = [] if font._dynamicFont: #it's a truetype font and should be utf8. If an error is raised, for subset, t in font.splitString(text, canv._doc): if subset!=self._curSubset: pdffontname = font.getSubsetInternalName(subset, canv._doc) R.append("%s %s Tf %s TL" % (pdffontname, fp_str(self._fontsize), fp_str(self._leading))) self._curSubset = subset R.append("(%s) Tj" % canv._escape(t)) elif font._multiByte: #all the fonts should really work like this - let them know more about PDF... R.append("%s %s Tf %s TL" % ( canv._doc.getInternalFontName(font.fontName), fp_str(self._fontsize), fp_str(self._leading) )) R.append("(%s) Tj" % font.formatForPdf(text)) else: #convert to T1 coding fc = font if not isinstance(text,unicode): try: text = text.decode('utf8') except UnicodeDecodeError,e: i,j = e.args[2:4] raise UnicodeDecodeError(*(e.args[:4]+('%s\n%s-->%s<--%s' % (e.args[4],text[max(i-10,0):i],text[i:j],text[j:j+10]),))) for f, t in pdfmetrics.unicode2T1(text,[font]+font.substitutionFonts): if f!=fc: R.append("%s %s Tf %s TL" % (canv._doc.getInternalFontName(f.fontName), fp_str(self._fontsize), fp_str(self._leading))) fc = f R.append("(%s) Tj" % canv._escape(t)) if font!=fc: R.append("%s %s Tf %s TL" % (canv._doc.getInternalFontName(self._fontname), fp_str(self._fontsize), fp_str(self._leading)))
def drawFigure(self, partList, closed=0): figureCode = [] a = figureCode.append first = 1 for part in partList: op = part[0] args = list(part[1:]) if op == figureLine: if first: first = 0 a("%s m" % fp_str(args[:2])) else: a("%s l" % fp_str(args[:2])) a("%s l" % fp_str(args[2:])) elif op == figureArc: first = 0 x1,y1,x2,y2,startAngle,extent = args[:6] a(self._genArcCode(x1,y1,x2,y2,startAngle,extent)) elif op == figureCurve: if first: first = 0 a("%s m" % fp_str(args[:2])) else: a("%s l" % fp_str(args[:2])) a("%s curveto" % fp_str(args[2:])) else: raise TypeError, "unknown figure operator: "+op if closed: a("closepath") self._fillAndStroke(figureCode)
def setTextTransform(self, a, b, c, d, e, f): "Like setTextOrigin, but does rotation, scaling etc." if not self._canvas.bottomup: c = -c #reverse bottom row of the 2D Transform d = -d self._code.append('%s Tm' % fp_str(a, b, c, d, e, f)) # The current cursor position is at the text origin Note that # we aren't keeping track of all the transform on these # coordinates: they are relative to the rotations/sheers # defined in the matrix. self._x0 = self._x = e self._y0 = self._y = f
def setStrokeColorCMYK(self, c, m, y, k): """set the stroke color useing negative color values (cyan, magenta, yellow and darkness value). Takes 4 arguments between 0.0 and 1.0""" self._strokeColorCMYK = (c, m, y, k) self._code.append('%s K' % fp_str(c, m, y, k))
def setStrokeColorRGB(self, r, g, b): """Set the stroke color using positive color description (Red,Green,Blue). Takes 3 arguments between 0.0 and 1.0""" self._strokeColorRGB = (r, g, b) self._code.append('%s RG' % fp_str(r, g, b))
def transform(self, a, b, c, d, e, f): self.code_append('[%s] concat' % fp_str(a, b, c, d, e, f))
def __repr__(self): return "Color(%s)" % fp_str(*(self.red, self.green, self.blue, self.alpha)).replace(' ', ',')
def translate(self, x, y): self.code_append('%s translate' % fp_str(x, y))
def scale(self, x, y): self.code_append('%s scale' % fp_str(x, y))
def _density_str(self): return fp_str(self.density)
def rect(self, x, y, width, height): """Adds a rectangle to the path""" self._code_append('%s re' % fp_str((x, y, width, height)))
def lineTo(self, x, y): self.code_append('%s l' % fp_str(x, y))
def moveTo(self, x, y): self.code_append('%s m' % fp_str(x, y))
def lines(self, lineList, color=None, width=None): if self._strokeColor != None: self._setColor(self._strokeColor) codeline = '%s m %s l stroke' for line in lineList: self.code_append(codeline % (fp_str(line[0]), fp_str(line[1])))
def setHorizScale(self, horizScale): "Stretches text out horizontally" self._horizScale = 100 + horizScale self._code.append('%s Tz' % fp_str(horizScale))
def setStrokeGray(self, gray): """Sets the gray level; 0.0=black, 1.0=white""" self._strokeColorRGB = (gray, gray, gray) self._code.append('%s G' % fp_str(gray))
def setWordSpace(self, wordSpace): """Adjust inter-word spacing. This can be used to flush-justify text - you get the width of the words, and add some space between them.""" self._wordSpace = wordSpace self._code.append('%s Tw' % fp_str(wordSpace))
def setFillGray(self, gray): """Sets the gray level; 0.0=black, 1.0=white""" self._fillColorRGB = (gray, gray, gray) self._code.append('%s g' % fp_str(gray))
def curveTo(self, x1, y1, x2, y2, x3, y3): self._code_append('%s c' % fp_str(x1, y1, x2, y2, x3, y3))
def setRise(self, rise): "Move text baseline up or down to allow superscrip/subscripts" self._rise = rise self._y = self._y - rise # + ? _textLineMatrix? self._code.append('%s Ts' % fp_str(rise))
def ellipse(self, x, y, width, height): """adds an ellipse to the path""" pointList = pdfgeom.bezierArc(x, y, x + width, y + height, 0, 360) self._code_append('%s m' % fp_str(pointList[0][:2])) for curve in pointList: self._code_append('%s c' % fp_str(curve[2:]))
def __repr__(self): return "Color(%s)" % string.replace( fp_str(self.red, self.green, self.blue), ' ', ',')
def setCharSpace(self, charSpace): """Adjusts inter-character spacing""" self._charSpace = charSpace self._code.append('%s Tc' % fp_str(charSpace))
class PSCanvas: def __init__(self, size=(300, 300), PostScriptLevel=2): self.width, self.height = size xtraState = [] self._xtraState_push = xtraState.append self._xtraState_pop = xtraState.pop self.comments = 0 self.code = [] self.code_append = self.code.append self._sep = '\n' self._strokeColor = self._fillColor = self._lineWidth = \ self._font = self._fontSize = self._lineCap = \ self._lineJoin = self._color = None self._fontsUsed = [] # track them as we go self.setFont(STATE_DEFAULTS['fontName'], STATE_DEFAULTS['fontSize']) self.setStrokeColor(STATE_DEFAULTS['strokeColor']) self.setLineCap(2) self.setLineJoin(0) self.setLineWidth(1) self.PostScriptLevel = PostScriptLevel def comment(self, msg): if self.comments: self.code_append('%' + msg) def drawImage(self, image, x1, y1, x2=None, y2=None): # Postscript Level2 version # select between postscript level 1 or level 2 if self.PostScriptLevel == 1: self._drawImageLevel1(image, x1, y1, x2=None, y2=None) elif self.PostScriptLevel == 2: self._drawImageLevel2(image, x1, y1, x2=None, y2=None) else: raise ValueError('Unsupported Postscript Level %s' % self.PostScriptLevel) def clear(self): self.code_append('showpage') # ugh, this makes no sense oh well. def _t1_re_encode(self): if not self._fontsUsed: return # for each font used, reencode the vectors C = [] for fontName in self._fontsUsed: fontObj = getFont(fontName) if not fontObj._dynamicFont and fontObj.encName == 'WinAnsiEncoding': C.append('WinAnsiEncoding /%s /%s RE' % (fontName, fontName)) if C: C.insert(0, PS_WinAnsiEncoding) self.code.insert(1, string.join(C, self._sep)) def save(self, f=None): if not hasattr(f, 'write'): file = open(f, 'wb') else: file = f if self.code[-1] != 'showpage': self.clear() self.code.insert( 0, '''\ %%!PS-Adobe-3.0 EPSF-3.0 %%%%BoundingBox: 0 0 %d %d %%%% Initialization: /m {moveto} bind def /l {lineto} bind def /c {curveto} bind def ''' % (self.width, self.height)) self._t1_re_encode() file.write(string.join(self.code, self._sep)) if file is not f: file.close() from reportlab.lib.utils import markfilename markfilename(f, creatorcode='XPR3', filetype='EPSF') def saveState(self): self._xtraState_push((self._fontCodeLoc, )) self.code_append('gsave') def restoreState(self): self.code_append('grestore') self._fontCodeLoc, = self._xtraState_pop() def stringWidth(self, s, font=None, fontSize=None): """Return the logical width of the string if it were drawn in the current font (defaults to self.font).""" font = font or self._font fontSize = fontSize or self._fontSize return stringWidth(s, font, fontSize) def setLineCap(self, v): if self._lineCap != v: self._lineCap = v self.code_append('%d setlinecap' % v) def setLineJoin(self, v): if self._lineJoin != v: self._lineJoin = v self.code_append('%d setlinejoin' % v) def setDash(self, array=[], phase=0): """Two notations. pass two numbers, or an array and phase""" # copied and modified from reportlab.canvas psoperation = "setdash" if type(array) == types.IntType or type(array) == types.FloatType: self.code_append('[%s %s] 0 %s' % (array, phase, psoperation)) elif type(array) == types.ListType or type(array) == types.TupleType: assert phase >= 0, "phase is a length in user space" textarray = string.join(map(str, array)) self.code_append('[%s] %s %s' % (textarray, phase, psoperation)) def setStrokeColor(self, color): self._strokeColor = color self.setColor(color) def setColor(self, color): if self._color != color: self._color = color if color: if hasattr(color, "cyan"): self.code_append('%s setcmykcolor' % fp_str( color.cyan, color.magenta, color.yellow, color.black)) else: self.code_append( '%s setrgbcolor' % fp_str(color.red, color.green, color.blue)) def setFillColor(self, color): self._fillColor = color self.setColor(color) def setLineWidth(self, width): if width != self._lineWidth: self._lineWidth = width self.code_append('%s setlinewidth' % width) def setFont(self, font, fontSize, leading=None): if self._font != font or self._fontSize != fontSize: self._fontCodeLoc = len(self.code) self._font = font self._fontSize = fontSize self.code_append('') def line(self, x1, y1, x2, y2): if self._strokeColor != None: self.setColor(self._strokeColor) self.code_append('%s m %s l stroke' % (fp_str(x1, y1), fp_str(x2, y2))) def _escape(self, s): ''' return a copy of string s with special characters in postscript strings escaped with backslashes. ''' try: return _escape_and_limit(s) except: raise ValueError("cannot escape %s %s" % (s, repr(s))) def _issueT1String(self, fontObj, x, y, s): fc = fontObj code_append = self.code_append fontSize = self._fontSize fontsUsed = self._fontsUsed escape = self._escape if not isinstance(s, unicode): try: s = s.decode('utf8') except UnicodeDecodeError, e: i, j = e.args[2:4] raise UnicodeDecodeError( *(e.args[:4] + ('%s\n%s-->%s<--%s' % (e.args[4], s[i - 10:i], s[i:j], s[j:j + 10]), ))) for f, t in unicode2T1(s, [fontObj] + fontObj.substitutionFonts): if f != fc: psName = f.face.name code_append('(%s) findfont %s scalefont setfont' % (psName, fp_str(fontSize))) if psName not in fontsUsed: fontsUsed.append(psName) fc = f code_append('%s m (%s) show ' % (fp_str(x, y), escape(t))) x += f.stringWidth(t.decode(f.encName), fontSize) if fontObj != fc: self._font = None self.setFont(fontObj.face.name, fontSize)
def setLeading(self, leading): "How far to move down at the end of a line." self._leading = leading self._code.append('%s TL' % fp_str(leading))
class PDFTextObject(_PDFColorSetter): """PDF logically separates text and graphics drawing; text operations need to be bracketed between BT (Begin text) and ET operators. This class ensures text operations are properly encapusalted. Ask the canvas for a text object with beginText(x, y). Do not construct one directly. Do not use multiple text objects in parallel; PDF is not multi-threaded! It keeps track of x and y coordinates relative to its origin.""" def __init__(self, canvas, x=0, y=0): self._code = ['BT'] #no point in [] then append RGB self._canvas = canvas #canvas sets this so it has access to size info self._fontname = self._canvas._fontname self._fontsize = self._canvas._fontsize self._leading = self._canvas._leading self._doc = self._canvas._doc self._colorsUsed = self._canvas._colorsUsed self._enforceColorSpace = getattr(canvas, '_enforceColorSpace', None) font = pdfmetrics.getFont(self._fontname) self._curSubset = -1 self.setTextOrigin(x, y) self._textRenderMode = 0 self._clipping = 0 def getCode(self): "pack onto one line; used internally" self._code.append('ET') if self._clipping: self._code.append('%d Tr' % (self._textRenderMode ^ 4)) return string.join(self._code, ' ') def setTextOrigin(self, x, y): if self._canvas.bottomup: self._code.append('1 0 0 1 %s Tm' % fp_str(x, y)) #bottom up else: self._code.append('1 0 0 -1 %s Tm' % fp_str(x, y)) #top down # The current cursor position is at the text origin self._x0 = self._x = x self._y0 = self._y = y def setTextTransform(self, a, b, c, d, e, f): "Like setTextOrigin, but does rotation, scaling etc." if not self._canvas.bottomup: c = -c #reverse bottom row of the 2D Transform d = -d self._code.append('%s Tm' % fp_str(a, b, c, d, e, f)) # The current cursor position is at the text origin Note that # we aren't keeping track of all the transform on these # coordinates: they are relative to the rotations/sheers # defined in the matrix. self._x0 = self._x = e self._y0 = self._y = f def moveCursor(self, dx, dy): """Starts a new line at an offset dx,dy from the start of the current line. This does not move the cursor relative to the current position, and it changes the current offset of every future line drawn (i.e. if you next do a textLine() call, it will move the cursor to a position one line lower than the position specificied in this call. """ # Check if we have a previous move cursor call, and combine # them if possible. if self._code and self._code[-1][-3:] == ' Td': L = string.split(self._code[-1]) if len(L) == 3: del self._code[-1] else: self._code[-1] = string.join(L[:-4]) # Work out the last movement lastDx = float(L[-3]) lastDy = float(L[-2]) # Combine the two movement dx += lastDx dy -= lastDy # We will soon add the movement to the line origin, so if # we've already done this for lastDx, lastDy, remove it # first (so it will be right when added back again). self._x0 -= lastDx self._y0 -= lastDy # Output the move text cursor call. self._code.append('%s Td' % fp_str(dx, -dy)) # Keep track of the new line offsets and the cursor position self._x0 += dx self._y0 += dy self._x = self._x0 self._y = self._y0 def setXPos(self, dx): """Starts a new line dx away from the start of the current line - NOT from the current point! So if you call it in mid-sentence, watch out.""" self.moveCursor(dx, 0) def getCursor(self): """Returns current text position relative to the last origin.""" return (self._x, self._y) def getStartOfLine(self): """Returns a tuple giving the text position of the start of the current line.""" return (self._x0, self._y0) def getX(self): """Returns current x position relative to the last origin.""" return self._x def getY(self): """Returns current y position relative to the last origin.""" return self._y def _setFont(self, psfontname, size): """Sets the font and fontSize Raises a readable exception if an illegal font is supplied. Font names are case-sensitive! Keeps track of font anme and size for metrics.""" self._fontname = psfontname self._fontsize = size font = pdfmetrics.getFont(self._fontname) if font._dynamicFont: self._curSubset = -1 else: pdffontname = self._canvas._doc.getInternalFontName(psfontname) self._code.append('%s %s Tf' % (pdffontname, fp_str(size))) def setFont(self, psfontname, size, leading=None): """Sets the font. If leading not specified, defaults to 1.2 x font size. Raises a readable exception if an illegal font is supplied. Font names are case-sensitive! Keeps track of font anme and size for metrics.""" self._fontname = psfontname self._fontsize = size if leading is None: leading = size * 1.2 self._leading = leading font = pdfmetrics.getFont(self._fontname) if font._dynamicFont: self._curSubset = -1 else: pdffontname = self._canvas._doc.getInternalFontName(psfontname) self._code.append('%s %s Tf %s TL' % (pdffontname, fp_str(size), fp_str(leading))) def setCharSpace(self, charSpace): """Adjusts inter-character spacing""" self._charSpace = charSpace self._code.append('%s Tc' % fp_str(charSpace)) def setWordSpace(self, wordSpace): """Adjust inter-word spacing. This can be used to flush-justify text - you get the width of the words, and add some space between them.""" self._wordSpace = wordSpace self._code.append('%s Tw' % fp_str(wordSpace)) def setHorizScale(self, horizScale): "Stretches text out horizontally" self._horizScale = 100 + horizScale self._code.append('%s Tz' % fp_str(horizScale)) def setLeading(self, leading): "How far to move down at the end of a line." self._leading = leading self._code.append('%s TL' % fp_str(leading)) def setTextRenderMode(self, mode): """Set the text rendering mode. 0 = Fill text 1 = Stroke text 2 = Fill then stroke 3 = Invisible 4 = Fill text and add to clipping path 5 = Stroke text and add to clipping path 6 = Fill then stroke and add to clipping path 7 = Add to clipping path after we start clipping we mustn't change the mode back until after the ET """ assert mode in (0, 1, 2, 3, 4, 5, 6, 7), "mode must be in (0,1,2,3,4,5,6,7)" if (mode & 4) != self._clipping: mode |= 4 self._clipping = mode & 4 if self._textRenderMode != mode: self._textRenderMode = mode self._code.append('%d Tr' % mode) def setRise(self, rise): "Move text baseline up or down to allow superscrip/subscripts" self._rise = rise self._y = self._y - rise # + ? _textLineMatrix? self._code.append('%s Ts' % fp_str(rise)) def _formatText(self, text): "Generates PDF text output operator(s)" canv = self._canvas font = pdfmetrics.getFont(self._fontname) R = [] # Adding Unicode BiDirection Support # Refer to http://github.com/barsi/openerp-rtl # for more info try: text = type(text) == type(u'') and text or text.decode('utf8') reshaped_text = arabic_reshaper.reshape(text) text = get_display(reshaped_text) except UnicodeDecodeError, e: i, j = e.args[2:4] raise UnicodeDecodeError(*(e.args[:4] + ('%s\n%s==[%s]==%s' % (e.args[4], text[max(i - 10, 0):i], text[i:j], text[j:j + 10]), ))) # end Unicode BiDirection Support if font._dynamicFont: #it's a truetype font and should be utf8. If an error is raised, for subset, t in font.splitString(text, canv._doc): if subset != self._curSubset: pdffontname = font.getSubsetInternalName(subset, canv._doc) R.append("%s %s Tf %s TL" % (pdffontname, fp_str( self._fontsize), fp_str(self._leading))) self._curSubset = subset R.append("(%s) Tj" % canv._escape(t)) elif font._multiByte: #all the fonts should really work like this - let them know more about PDF... R.append("%s %s Tf %s TL" % (canv._doc.getInternalFontName( font.fontName), fp_str(self._fontsize), fp_str(self._leading))) R.append("(%s) Tj" % font.formatForPdf(text)) else: #convert to T1 coding fc = font if not isinstance(text, unicode): try: text = text.decode('utf8') except UnicodeDecodeError, e: i, j = e.args[2:4] raise UnicodeDecodeError( *(e.args[:4] + ('%s\n%s-->%s<--%s' % (e.args[4], text[max(i - 10, 0):i], text[i:j], text[j:j + 10]), ))) for f, t in pdfmetrics.unicode2T1(text, [font] + font.substitutionFonts): if f != fc: R.append("%s %s Tf %s TL" % (canv._doc.getInternalFontName(f.fontName), fp_str(self._fontsize), fp_str(self._leading))) fc = f R.append("(%s) Tj" % canv._escape(t)) if font != fc: R.append("%s %s Tf %s TL" % (canv._doc.getInternalFontName(self._fontname), fp_str(self._fontsize), fp_str(self._leading)))
def setStrokeGray(self, gray, alpha=None): """Sets the gray level; 0.0=black, 1.0=white""" self._strokeColorObj = (gray, gray, gray) self._code.append('%s G' % fp_str(gray)) if alpha is not None: self.setFillAlpha(alpha)
def line(self, x1, y1, x2, y2): if self._strokeColor != None: self.setColor(self._strokeColor) self.code_append('%s m %s l stroke' % (fp_str(x1, y1), fp_str(x2, y2)))
def identity(self, maxLen=None): return "<%s at %s%s%s> size=%sx%s" % (self.__class__.__name__, hex(id(self)), self._frameName(), getattr(self,'name','') and (' name="%s"'% getattr(self,'name','')) or '', getattr(self,'maxWidth','') and (' maxWidth=%s'%fp_str(getattr(self,'maxWidth',0))) or '', getattr(self,'maxHeight','')and (' maxHeight=%s' % fp_str(getattr(self,'maxHeight')))or '')