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 isUnicode(s): 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 = asNative(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 text2PathDescription(text, x=0, y=0, fontName=_baseGFontName, fontSize=1000, anchor='start', truncate=1, pathReverse=0, gs=None): font = getFont(fontName) if font._multiByte and not font._dynamicFont: raise ValueError( "text2PathDescription doesn't support multi byte fonts like %r" % fontName) P_extend = [].extend if not anchor == 'start': textLen = stringWidth(text, fontName, fontSize) if anchor == 'end': x = x - textLen elif anchor == 'middle': x = x - textLen / 2. if gs is None: from ._renderPM import gstate gs = gstate(1, 1) setFont(gs, fontName, fontSize) if font._dynamicFont: for g in gs._stringPath(text, x, y): P_extend( processGlyph(g, truncate=truncate, pathReverse=pathReverse)) else: if isBytes(text): try: text = text.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], text[max(i - 10, 0):i], text[i:j], text[j:j + 10]), ))) fc = font FT = unicode2T1(text, [font] + font.substitutionFonts) nm1 = len(FT) - 1 for i, (f, t) in enumerate(FT): if f != fc: setFont(gs, f.fontName, fontSize) fc = f for g in gs._stringPath(t, x, y): P_extend( processGlyph(g, truncate=truncate, pathReverse=pathReverse)) if i != nm1: x += f.stringWidth(t.decode(f.encName), fontSize) return P_extend.__self__
def _formatText(self, text): "Generates PDF text output operator(s)" if log2vis and self.direction in ('LTR', 'RTL'): # Use pyfribidi to write the text in the correct visual order. text = log2vis(text, directionsMap.get(self.direction, DIR_ON), clean=True) 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 isBytes(text): try: text = text.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], 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))) return ' '.join(R)
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 drawString(self, x, y, text, _fontInfo=None, text_anchor='left'): gs = self._gs gs_fontSize = gs.fontSize gs_fontName = gs.fontName if _fontInfo and _fontInfo!=(gs_fontSize,gs_fontName): fontName, fontSize = _fontInfo _setFont(gs,fontName,fontSize) else: fontName = gs_fontName fontSize = gs_fontSize try: if text_anchor in ('end','middle', 'end'): textLen = stringWidth(text, fontName,fontSize) if text_anchor=='end': x -= textLen elif text_anchor=='middle': x -= textLen/2. elif text_anchor=='numeric': x -= numericXShift(text_anchor,text,textLen,fontName,fontSize) if self._backend=='rlPyCairo': gs.drawString(x,y,text) else: font = getFont(fontName) if font._dynamicFont: gs.drawString(x,y,text) else: fc = font if not isUnicode(text): try: text = text.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],text[i-10:i],text[i:j],text[j:j+10]),))) FT = unicode2T1(text,[font]+font.substitutionFonts) n = len(FT) nm1 = n-1 for i in range(n): f, t = FT[i] if f!=fc: _setFont(gs,f.fontName,fontSize) fc = f gs.drawString(x,y,t) if i!=nm1: x += f.stringWidth(t.decode(f.encName),fontSize) finally: gs.setFont(gs_fontName,gs_fontSize)
def draw(self, txt, new_line=False): doc = self.page.document font = getFont(self.font_name) if font._dynamicFont: for subset, t in font.splitString(txt, doc): if self.font_subset != subset: name = font.getSubsetInternalName(subset, doc) if name not in doc.font_references: doc.font_references[name] = doc.ref() if name not in self.page.font: self.page.font[name] = doc.font_references[name] self.write("{} {} Tf {} TL\n".format( name, self.font_size, self.font_leading)) self.write("({}) Tj ".format(escapePDF(t))) elif font._multiByte: name = doc.fontMapping.get(font.fontName) if name is None: name = doc.fontMapping[font.fontName] = '/F{}'.format( len(doc.fontMapping) + 1) doc.delayedFonts.append(font) if name not in doc.font_references: doc.font_references[name] = doc.ref() if name not in self.page.font: self.page.font[name] = doc.font_references[name] self.write("/{} {} Tf {} TL\n".format(name, self.font_size, self.font_leading)) self.write("(%s) Tj ".format(font.formatForPdf(txt))) else: _font = None for f, t in pdfmetrics.unicode2T1(txt, [font] + font.substitutionFonts): if f != _font: name = doc.fontMapping.get(f.fontName) if name is None: name = doc.fontMapping[f.fontName] = '/F{}'.format( len(doc.fontMapping) + 1) doc.delayedFonts.append(f) if name not in doc.font_references: doc.font_references[name] = doc.ref() if name not in self.page.font: self.page.font[name] = doc.font_references[name] self.write("{} {} Tf {} TL\n".format( name, self.font_size, self.font_leading)) _font = f self.write("({}) Tj ".format(escapePDF(t))) if new_line: self.write('T*\n')
def _formatText(self, text): "Generates PDF text output operator(s)" 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 drawString(self, x, y, text, _fontInfo=None): gs = self._gs if _fontInfo: fontName, fontSize = _fontInfo else: fontSize = gs.fontSize fontName = gs.fontName try: gfont = getFont(gs.fontName) except: gfont = None font = getFont(fontName) if font._dynamicFont: if isinstance(text, unicode): text = text.encode("utf8") gs.drawString(x, y, text) else: 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[i - 10 : i], text[i:j], text[j : j + 10]),) ) ) FT = unicode2T1(text, [font] + font.substitutionFonts) n = len(FT) nm1 = n - 1 wscale = 0.001 * fontSize for i in xrange(n): f, t = FT[i] if f != fc: _setFont(gs, f.fontName, fontSize) fc = f gs.drawString(x, y, t) if i != nm1: x += wscale * sum(map(f.widths.__getitem__, map(ord, t))) if font != fc: _setFont(gs, fontName, fontSize)
def drawString(self, x, y, text, _fontInfo=None): gs = self._gs if _fontInfo: fontName, fontSize = _fontInfo else: fontSize = gs.fontSize fontName = gs.fontName try: gfont = getFont(gs.fontName) except: gfont = None font = getFont(fontName) if font._dynamicFont: if isinstance(text, unicode): text = text.encode('utf8') gs.drawString(x, y, text) else: 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[i - 10:i], text[i:j], text[j:j + 10]), ))) FT = unicode2T1(text, [font] + font.substitutionFonts) n = len(FT) nm1 = n - 1 wscale = 0.001 * fontSize for i in xrange(n): f, t = FT[i] if f != fc: _setFont(gs, f.fontName, fontSize) fc = f gs.drawString(x, y, t) if i != nm1: x += wscale * sum(map(f.widths.__getitem__, map(ord, t))) if font != fc: _setFont(gs, fontName, fontSize)
def drawString(self, x, y, text, _fontInfo=None): gs = self._gs if _fontInfo: fontName, fontSize = _fontInfo else: fontSize = gs.fontSize fontName = gs.fontName try: gfont = getFont(gs.fontName) except: gfont = None font = getFont(fontName) if font._dynamicFont: gs.drawString(x, y, text) else: fc = font if not isUnicode(text): try: text = text.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], text[i - 10 : i], text[i:j], text[j : j + 10]),) ) ) FT = unicode2T1(text, [font] + font.substitutionFonts) n = len(FT) nm1 = n - 1 for i in range(n): f, t = FT[i] if f != fc: _setFont(gs, f.fontName, fontSize) fc = f gs.drawString(x, y, t) if i != nm1: x += f.stringWidth(t.decode(f.encName), fontSize) if font != fc: _setFont(gs, fontName, fontSize)
def drawString(self, x, y, text, _fontInfo=None): gs = self._gs if _fontInfo: fontName, fontSize = _fontInfo else: fontSize = gs.fontSize fontName = gs.fontName try: gfont = getFont(gs.fontName) except: gfont = None font = getFont(fontName) if font._dynamicFont: gs.drawString(x, y, text) else: fc = font if not isUnicode(text): try: text = text.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], text[i - 10:i], text[i:j], text[j:j + 10]), ))) FT = unicode2T1(text, [font] + font.substitutionFonts) n = len(FT) nm1 = n - 1 for i in range(n): f, t = FT[i] if f != fc: _setFont(gs, f.fontName, fontSize) fc = f gs.drawString(x, y, t) if i != nm1: x += f.stringWidth(t.decode(f.encName), fontSize) if font != fc: _setFont(gs, fontName, fontSize)
def _formatText(self, text): "Generates PDF text output operator(s)" 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 isBytes(text): try: text = text.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],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))) return ' '.join(R)
def tfunc(f, ts): w1 = unicode2T1(ts, [f] + f.substitutionFonts) w2 = _py_unicode2T1(ts, [f] + f.substitutionFonts) assert w1 == w2, "%r != %r" % (w1, w2)
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 tfunc(f,ts): w1 = unicode2T1(ts,[f]+f.substitutionFonts) w2 = _py_unicode2T1(ts,[f]+f.substitutionFonts) assert w1==w2,"%r != %r" % (w1,w2)
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)))