def __init__(self, enum,*args): if isSeq(enum): if args!=(): raise ValueError("Either all singleton args or a single sequence argument") self._enum = tuple(enum)+args else: self._enum = (enum,)+args
def addProxyAttribute(src,name,validate=None,desc=None,initial=None,dst=None): ''' Add a proxy attribute 'name' to src with targets dst ''' #sanity assert hasattr(src,'_attrMap'), 'src object has no _attrMap' A, oA = _privateAttrMap(src,1) if not isSeq(dst): dst = dst, D = [] DV = [] for d in dst: if isSeq(d): d, e = d[0], d[1:] obj, attr = _findObjectAndAttr(src,d) if obj: dA = getattr(obj,'_attrMap',None)
def __init__( self, id=None, frames=[], onPage=_doNothing, onPageEnd=_doNothing, pagesize=None, autoNextPageTemplate=None, cropBox=None, artBox=None, trimBox=None, bleedBox=None, ): frames = frames or [] if not isSeq(frames): frames = [frames] assert [x for x in frames if not isinstance(x, Frame)] == [], "frames argument error" self.id = id self.frames = frames self.onPage = onPage self.onPageEnd = onPageEnd self.pagesize = pagesize self.autoNextPageTemplate = autoNextPageTemplate self.cropBox = cropBox self.artBox = artBox self.trimBox = trimBox self.bleedBox = bleedBox
def _expandWidths(self, compactWidthArray): """Expands Adobe nested list structure to get a dictionary of widths. Here is an example of such a structure.:: ( # starting at character ID 1, next n characters have the widths given. 1, (277,305,500,668,668,906,727,305,445,445,508,668,305,379,305,539), # all Characters from ID 17 to 26 are 668 em units wide 17, 26, 668, 27, (305, 305, 668, 668, 668, 566, 871, 727, 637, 652, 699, 574, 555, 676, 687, 242, 492, 664, 582, 789, 707, 734, 582, 734, 605, 605, 641, 668, 727, 945, 609, 609, 574, 445, 668, 445, 668, 668, 590, 555, 609, 547, 602, 574, 391, 609, 582, 234, 277, 539, 234, 895, 582, 605, 602, 602, 387, 508, 441, 582, 562, 781, 531, 570, 555, 449, 246, 449, 668), # these must be half width katakana and the like. 231, 632, 500 ) """ data = compactWidthArray[:] widths = {} while data: start, data = data[0], data[1:] if isSeq(data[0]): items, data = data[0], data[1:] for offset in range(len(items)): widths[start + offset] = items[offset] else: end, width, data = data[0], data[1], data[2:] for idx in range(start, end+1): widths[idx] = width return widths
def __init__(self,action=()): #must call super init to ensure it has a width and height (of zero), #as in some cases the packer might get called on it... Flowable.__init__(self) if not isSeq(action): action = (action,) self.action = tuple(action)
def addPageTemplates(self,pageTemplates): 'add one or a sequence of pageTemplates' if not isSeq(pageTemplates): pageTemplates = [pageTemplates] #this test below fails due to inconsistent imports! #assert filter(lambda x: not isinstance(x,PageTemplate), pageTemplates)==[], "pageTemplates argument error" for t in pageTemplates: self.pageTemplates.append(t)
def _evalMeasurement(n): if isinstance(n, str): from reportlab.platypus.paraparser import _num n = _num(n) if isSeq(n): n = n[1] return n
def _samePT(self,npt): if isSeq(npt): return getattr(self,'_nextPageTemplateCycle',[]) if isinstance(npt,strTypes): return npt == (self.pageTemplates[self._nextPageTemplateIndex].id if hasattr(self,'_nextPageTemplateIndex') else self.pageTemplate.id) if isinstance(npt,int) and 0<=npt<len(self.pageTemplates): if hasattr(self,'_nextPageTemplateIndex'): return npt==self._nextPageTemplateIndex return npt==self.pageTemplates.find(self.pageTemplate)
def test(self, x): if not isSeq(x): if x is None: return self._NoneOK return False if x==[] or x==(): return self._emptyOK elif not self._lo<=len(x)<=self._hi: return False for e in x: if not self._elemTest(e): return False return True
def test(self, x): from reportlab.graphics.shapes import Shape if isSeq(x): answer = 1 for e in x: if not isinstance(e, Shape): answer = 0 return answer else: return False
def test(self, x): if isSeq(x): if len(x) == 6: for element in x: if not isNumber(element): return False return True else: return False else: return False
def getContext(self, tag): """Return main context for tag >>> g = Cleaner() >>> eqCheck(g.getContext('i'),'inline') >>> eqCheck(g.getContext('li'),'list') """ context = self._tagDict[tag]['context'] if isSeq(context): return context[0] return context
def structToPDF(structure): "Converts deeply nested structure to PDFdoc dictionary/array objects" if isinstance(structure,dict): newDict = {} for k, v in structure.items(): newDict[k] = structToPDF(v) return pdfdoc.PDFDictionary(newDict) elif isSeq(structure): newList = [] for elem in structure: newList.append(structToPDF(elem)) return pdfdoc.PDFArray(newList) else: return structure
def __init__(self,BASE=None,UNWANTED=[],**kw): data = {} if BASE: if isinstance(BASE,AttrMap): data = BASE else: if not isSeq(BASE): BASE = (BASE,) for B in BASE: am = getattr(B,'_attrMap',self) if am is not self: if am: data.update(am) else: raise ValueError('BASE=%s has wrong kind of value' % ascii(B)) dict.__init__(self,data) self.remove(UNWANTED) self.update(kw)
def __init__(self, id=None, frames=[], onPage=_doNothing, onPageEnd=_doNothing, pagesize=None, autoNextPageTemplate=None): frames = frames or [] if not isSeq(frames): frames = [frames] assert [x for x in frames if not isinstance(x, Frame)] == [], "frames argument error" self.id = id self.frames = frames self.onPage = onPage self.onPageEnd = onPageEnd self.pagesize = pagesize self.autoNextPageTemplate = autoNextPageTemplate
def start_stylesheet(self, args): #makes it the current style sheet. path = self._arg('stylesheet',args,'path') if path=='None': path = [] if not isSeq(path): path = [path] path.append('styles') path.append(os.getcwd()) modulename = self._arg('stylesheet', args, 'module') funcname = self._arg('stylesheet', args, 'function') try: found = imp.find_module(modulename, path) (file, pathname, description) = found mod = imp.load_module(modulename, file, pathname, description) except ImportError: #last gasp mod = getModule(modulename) #now get the function func = getattr(mod, funcname) pythonpoint.setStyles(func())
def _getWidths(i,s, fontName, fontSize, subCols): S = [] aS = S.append if isSeq(s): for j,t in enumerate(s): sc = subCols[j,i] fN = getattr(sc,'fontName',fontName) fS = getattr(sc,'fontSize',fontSize) m = [stringWidth(x, fN, fS) for x in t.split('\n')] m = max(sc.minWidth,m and max(m) or 0) aS(m) aS(sc.rpad) del S[-1] else: sc = subCols[0,i] fN = getattr(sc,'fontName',fontName) fS = getattr(sc,'fontSize',fontSize) m = [stringWidth(x, fN, fS) for x in s.split('\n')] aS(max(sc.minWidth,m and max(m) or 0)) return S
def start_stylesheet(self, args): #makes it the current style sheet. path = self._arg('stylesheet', args, 'path') if path == 'None': path = [] if not isSeq(path): path = [path] path.append('styles') path.append(os.getcwd()) modulename = self._arg('stylesheet', args, 'module') funcname = self._arg('stylesheet', args, 'function') try: found = imp.find_module(modulename, path) (file, pathname, description) = found mod = imp.load_module(modulename, file, pathname, description) except ImportError: #last gasp mod = getModule(modulename) #now get the function func = getattr(mod, funcname) pythonpoint.setStyles(func())
def fp_str(*a): '''convert separate arguments (or single sequence arg) into space separated numeric strings''' if len(a) == 1 and isSeq(a[0]): a = a[0] s = [] A = s.append for i in a: sa = abs(i) if sa <= 1e-7: A('0') else: l = sa <= 1 and 6 or min(max(0, (6 - int(_log_10(sa)))), 6) n = _fp_fmts[l] % i if l: j = len(n) while j: j -= 1 if n[j] != '0': if n[j] != '.': j += 1 break n = n[:j] A((n[0] != '0' or len(n) == 1) and n or n[1:]) return ' '.join(s)
def fp_str(*a): '''convert separate arguments (or single sequence arg) into space separated numeric strings''' if len(a)==1 and isSeq(a[0]): a = a[0] s = [] A = s.append for i in a: sa =abs(i) if sa<=1e-7: A('0') else: l = sa<=1 and 6 or min(max(0,(6-int(_log_10(sa)))),6) n = _fp_fmts[l]%i if l: j = len(n) while j: j -= 1 if n[j]!='0': if n[j]!='.': j += 1 break n = n[:j] A((n[0]!='0' or len(n)==1) and n or n[1:]) return ' '.join(s)
def handle_nextPageTemplate(self, pt): '''On endPage change to the page template with name or index pt''' if isinstance(pt, strTypes): if hasattr(self, '_nextPageTemplateCycle'): del self._nextPageTemplateCycle for t in self.pageTemplates: if t.id == pt: self._nextPageTemplateIndex = self.pageTemplates.index(t) return raise ValueError("can't find template('%s')" % pt) elif isinstance(pt, int): if hasattr(self, '_nextPageTemplateCycle'): del self._nextPageTemplateCycle self._nextPageTemplateIndex = pt elif isSeq(pt): #used for alternating left/right pages #collect the refs to the template objects, complain if any are bad c = PTCycle() for ptn in pt: found = 0 if ptn == '*': #special case name used to short circuit the iteration c._restart = len(c) continue for t in self.pageTemplates: if t.id == ptn: c.append(t) found = 1 if not found: raise ValueError("Cannot find page template called %s" % ptn) if not c: raise ValueError("No valid page templates in cycle") elif c._restart > len(c): raise ValueError("Invalid cycle restart position") #ensure we start on the first one self._nextPageTemplateCycle = c.cyclicIterator() else: raise TypeError("argument pt should be string or integer or list")
def __init__(self, name, base=None): self.name = name self.frozen = 0 if name in standardEncodings: assert base is None, "Can't have a base encoding for a standard encoding" self.baseEncodingName = name self.vector = _fontdata.encodings[name] elif base == None: # assume based on the usual one self.baseEncodingName = defaultEncoding self.vector = _fontdata.encodings[defaultEncoding] elif isStr(base): baseEnc = getEncoding(base) self.baseEncodingName = baseEnc.name self.vector = baseEnc.vector[:] elif isSeq(base): self.baseEncodingName = defaultEncoding self.vector = base[:] elif isinstance(base, Encoding): # accept a vector self.baseEncodingName = base.name self.vector = base.vector[:]
def handle_nextPageTemplate(self, pt): """On endPage change to the page template with name or index pt""" if isinstance(pt, strTypes): if hasattr(self, "_nextPageTemplateCycle"): del self._nextPageTemplateCycle for t in self.pageTemplates: if t.id == pt: self._nextPageTemplateIndex = self.pageTemplates.index(t) return raise ValueError("can't find template('%s')" % pt) elif isinstance(pt, int): if hasattr(self, "_nextPageTemplateCycle"): del self._nextPageTemplateCycle self._nextPageTemplateIndex = pt elif isSeq(pt): # used for alternating left/right pages # collect the refs to the template objects, complain if any are bad c = PTCycle() for ptn in pt: found = 0 if ptn == "*": # special case name used to short circuit the iteration c._restart = len(c) continue for t in self.pageTemplates: if t.id == ptn: c.append(t) found = 1 if not found: raise ValueError("Cannot find page template called %s" % ptn) if not c: raise ValueError("No valid page templates in cycle") elif c._restart > len(c): raise ValueError("Invalid cycle restart position") # ensure we start on the first one self._nextPageTemplateCycle = c else: raise TypeError("argument pt should be string or integer or list")
def draw(self): colorNamePairs = self.colorNamePairs autoCP = isAuto(colorNamePairs) if autoCP: chart = getattr(colorNamePairs,'chart',getattr(colorNamePairs,'obj',None)) swatchMarker = None autoCP = Auto(obj=chart) n = chart._seriesCount chartTexts = self._getTexts(colorNamePairs) else: swatchMarker = getattr(self,'swatchMarker',None) if isAuto(swatchMarker): chart = getattr(swatchMarker,'chart',getattr(swatchMarker,'obj',None)) swatchMarker = Auto(obj=chart) n = len(colorNamePairs) dx = self.dx dy = self.dy alignment = self.alignment columnMaximum = self.columnMaximum deltax = self.deltax deltay = self.deltay dxTextSpace = self.dxTextSpace fontName = self.fontName fontSize = self.fontSize fillColor = self.fillColor strokeWidth = self.strokeWidth strokeColor = self.strokeColor subCols = self.subCols leading = fontSize*1.2 yGap = self.yGap if not deltay: deltay = max(dy,leading)+self.autoYPadding ba = self.boxAnchor maxWidth = self._calculateMaxBoundaries(colorNamePairs) nCols = int((n+columnMaximum-1)/(columnMaximum*1.0)) xW = dx+dxTextSpace+self.autoXPadding variColumn = self.variColumn if variColumn: width = sum([m[-1] for m in maxWidth])+xW*nCols else: deltax = max(maxWidth[-1]+xW,deltax) width = nCols*deltax maxWidth = nCols*[maxWidth] thisx = self.x thisy = self.y - self.dy if ba not in ('ne','n','nw','autoy'): height = self._calcHeight() if ba in ('e','c','w'): thisy += height/2. else: thisy += height if ba not in ('nw','w','sw','autox'): if ba in ('n','c','s'): thisx -= width/2 else: thisx -= width upperlefty = thisy g = Group() ascent=getFont(fontName).face.ascent/1000. if ascent==0: ascent=0.718 # default (from helvetica) ascent *= fontSize # normalize lim = columnMaximum - 1 callout = getattr(self,'callout',None) scallout = getattr(self,'swatchCallout',None) dividerLines = self.dividerLines if dividerLines: dividerWidth = self.dividerWidth dividerColor = self.dividerColor dividerDashArray = self.dividerDashArray dividerOffsX = self.dividerOffsX dividerOffsY = self.dividerOffsY for i in xrange(n): if autoCP: col = autoCP col.index = i name = chartTexts[i] else: col, name = colorNamePairs[i] if isAuto(swatchMarker): col = swatchMarker col.index = i if isAuto(name): name = getattr(swatchMarker,'chart',getattr(swatchMarker,'obj',None)).getSeriesName(i,'series %d' % i) T = _getLines(name) S = [] aS = S.append j = int(i/(columnMaximum*1.0)) jOffs = maxWidth[j] # thisy+dy/2 = y+leading/2 y = y0 = thisy+(dy-ascent)*0.5 if callout: callout(self,g,thisx,y,(col,name)) if alignment == "left": x = thisx xn = thisx+jOffs[-1]+dxTextSpace elif alignment == "right": x = thisx+dx+dxTextSpace xn = thisx else: raise ValueError("bad alignment") if not isSeq(name): T = [T] yd = y for k,lines in enumerate(T): y = y0 kk = k*2 x1 = x+jOffs[kk] x2 = x+jOffs[kk+1] sc = subCols[k,i] anchor = sc.align scdx = sc.dx scdy = sc.dy fN = getattr(sc,'fontName',fontName) fS = getattr(sc,'fontSize',fontSize) fC = getattr(sc,'fillColor',fillColor) fL = getattr(sc,'leading',1.2*fontSize) if fN==fontName: fA = (ascent*fS)/fontSize else: fA = getFont(fontName).face.ascent/1000. if fA==0: fA=0.718 fA *= fS if anchor=='left': anchor = 'start' xoffs = x1 elif anchor=='right': anchor = 'end' xoffs = x2 elif anchor=='numeric': xoffs = x2 else: anchor = 'middle' xoffs = 0.5*(x1+x2) for t in lines: aS(String(xoffs+scdx,y+scdy,t,fontName=fN,fontSize=fS,fillColor=fC, textAnchor = anchor)) y -= fL yd = min(yd,y) y += fL for iy, a in ((y-max(fL-fA,0),'underlines'),(y+fA,'overlines')): il = getattr(sc,a,None) if il: if not isinstance(il,(tuple,list)): il = (il,) for l in il: l = copy.copy(l) l.y1 += iy l.y2 += iy l.x1 += x1 l.x2 += x2 aS(l) x = xn y = yd leadingMove = 2*y0-y-thisy if dividerLines: xd = thisx+dx+dxTextSpace+jOffs[-1]+dividerOffsX[1] yd = thisy+dy*0.5+dividerOffsY if ((dividerLines&1) and i%columnMaximum) or ((dividerLines&2) and not i%columnMaximum): g.add(Line(thisx+dividerOffsX[0],yd,xd,yd, strokeColor=dividerColor, strokeWidth=dividerWidth, strokeDashArray=dividerDashArray)) if (dividerLines&4) and (i%columnMaximum==lim or i==(n-1)): yd -= max(deltay,leadingMove)+yGap g.add(Line(thisx+dividerOffsX[0],yd,xd,yd, strokeColor=dividerColor, strokeWidth=dividerWidth, strokeDashArray=dividerDashArray)) # Make a 'normal' color swatch... swatchX = x + getattr(self,'swdx',0) swatchY = thisy + getattr(self,'swdy',0) if isAuto(col): chart = getattr(col,'chart',getattr(col,'obj',None)) c = chart.makeSwatchSample(getattr(col,'index',i),swatchX,swatchY,dx,dy) elif isinstance(col, colors.Color): if isSymbol(swatchMarker): c = uSymbol2Symbol(swatchMarker,swatchX+dx/2.,swatchY+dy/2.,col) else: c = self._defaultSwatch(swatchX,swatchY,dx,dy,fillColor=col,strokeWidth=strokeWidth,strokeColor=strokeColor) elif col is not None: try: c = copy.deepcopy(col) c.x = swatchX c.y = swatchY c.width = dx c.height = dy except: c = None else: c = None if c: g.add(c) if scallout: scallout(self,g,thisx,y0,i,(col,name),c) for s in S: g.add(s) if self.colEndCallout and (i%columnMaximum==lim or i==(n-1)): if alignment == "left": xt = thisx else: xt = thisx+dx+dxTextSpace yd = thisy+dy*0.5+dividerOffsY - (max(deltay,leadingMove)+yGap) self.colEndCallout(self, g, thisx, xt, yd, jOffs[-1], jOffs[-1]+dx+dxTextSpace) if i%columnMaximum==lim: if variColumn: thisx += jOffs[-1]+xW else: thisx = thisx+deltax thisy = upperlefty else: thisy = thisy-max(deltay,leadingMove)-yGap return g
def _getLines(s): if isSeq(s): return tuple([(x or '').split('\n') for x in s]) else: return (s or '').split('\n')
def _getLineCount(s): T = _getLines(s) if isSeq(s): return max([len(x) for x in T]) else: return len(T)
def _getStr(s): if isSeq(s): return list(map(_getStr,s)) else: return _objStr(s)
def __init__(self,tests,name=None): if not isSeq(tests): tests = (tests,) self._tests = tests if name: self._str = name
def _evalMeasurement(n): if isinstance(n,str): from reportlab.platypus.paraparser import _num n = _num(n) if isSeq(n): n = n[1] return n
def breakLines(self, width): """ Returns a broken line structure. There are two cases A) For the simple case of a single formatting input fragment the output is A fragment specifier with - kind = 0 - fontName, fontSize, leading, textColor - lines= A list of lines Each line has two items: 1. unused width in points 2. a list of words B) When there is more than one input formatting fragment the out put is A fragment specifier with - kind = 1 - lines = A list of fragments each having fields: - extraspace (needed for justified) - fontSize - words=word list - each word is itself a fragment with - various settings This structure can be used to easily draw paragraphs with the various alignments. You can supply either a single width or a list of widths; the latter will have its last item repeated until necessary. A 2-element list is useful when there is a different first line indent; a longer list could be created to facilitate custom wraps around irregular objects.""" if not isSeq(width): maxWidths = [width] else: maxWidths = width lines = [] lineno = 0 maxWidth = maxWidths[lineno] style = self.style fFontSize = float(style.fontSize) requiredWidth = 0 #for bullets, work out width and ensure we wrap the right amount onto line one _handleBulletWidth(self.bulletText,style,maxWidths) self.height = 0 autoLeading = getattr(self,'autoLeading',getattr(style,'autoLeading','')) calcBounds = autoLeading not in ('','off') frags = self.frags nFrags= len(frags) if nFrags==1: f = frags[0] if hasattr(f,'text'): fontSize = f.fontSize fontName = f.fontName ascent, descent = getAscentDescent(fontName,fontSize) kind = 0 L=f.text.split('\n') for l in L: currentWidth = stringWidth(l,fontName,fontSize) requiredWidth = max(currentWidth,requiredWidth) extraSpace = maxWidth-currentWidth lines.append((extraSpace,l.split(' '),currentWidth)) lineno = lineno+1 maxWidth = lineno<len(maxWidths) and maxWidths[lineno] or maxWidths[-1] blPara = f.clone(kind=kind, lines=lines,ascent=ascent,descent=descent,fontSize=fontSize) else: kind = f.kind lines = f.lines for L in lines: if kind==0: currentWidth = L[2] else: currentWidth = L.currentWidth requiredWidth = max(currentWidth,requiredWidth) blPara = f.clone(kind=kind, lines=lines) self.width = max(self.width,requiredWidth) return blPara elif nFrags<=0: return ParaLines(kind=0, fontSize=style.fontSize, fontName=style.fontName, textColor=style.textColor, ascent=style.fontSize,descent=-0.2*style.fontSize, lines=[]) else: for L in _getFragLines(frags): currentWidth, n, w = _getFragWord(L,maxWidth) f = w[0][0] maxSize = f.fontSize maxAscent, minDescent = getAscentDescent(f.fontName,maxSize) words = [f.clone()] words[-1].text = w[0][1] for i in w[1:]: f = i[0].clone() f.text=i[1] words.append(f) fontSize = f.fontSize fontName = f.fontName if calcBounds: cbDefn = getattr(f,'cbDefn',None) if getattr(cbDefn,'width',0): descent,ascent = imgVRange(imgNormV(cbDefn.height,fontSize),cbDefn.valign,fontSize) else: ascent, descent = getAscentDescent(fontName,fontSize) else: ascent, descent = getAscentDescent(fontName,fontSize) maxSize = max(maxSize,fontSize) maxAscent = max(maxAscent,ascent) minDescent = min(minDescent,descent) lineno += 1 maxWidth = lineno<len(maxWidths) and maxWidths[lineno] or maxWidths[-1] requiredWidth = max(currentWidth,requiredWidth) extraSpace = maxWidth - currentWidth lines.append(ParaLines(extraSpace=extraSpace,wordCount=n, words=words, fontSize=maxSize, ascent=maxAscent,descent=minDescent,currentWidth=currentWidth)) self.width = max(self.width,requiredWidth) return ParaLines(kind=1, lines=lines) return lines
def _setupGrammar(self): self.DEFAULT_BLOCK_TAG = 'p' #used to close blocks if nothing else given def uniDict(d): r = {} for k in d: r[asUnicode(k)] = d[k] return r def byName(listOfDicts): out = {} for d in listOfDicts: d = uniDict(d) out[d['name']] = d return out #situations in which a tag can appear. self._contextDict = byName([ dict(name='block', allowedIn=None), #None=top-level dict(name='inline', allowedIn='block'), dict(name='table', allowedIn='block'), dict(name='list', allowedIn='block'), dict(name='li', allowedIn='list'), dict(name='tr', allowedIn='table'), dict(name='td', allowedIn='tr'), #no contents for now ]) # if not self.allowTables: # del self._contextDict['table'] for context in self._contextDict.values(): context['canContain'] = set() allowParaAttrs = [] if self.allowStyleAttr: allowParaAttrs.append('style') if self.allowAlignAttr: allowParaAttrs.append('align') self._tagDict = byName([ dict(name='p',context='block', attrs=allowParaAttrs), dict(name='h1',context='block', attrs=allowParaAttrs), dict(name='h2',context='block', attrs=allowParaAttrs), dict(name='h3',context='block', attrs=allowParaAttrs), dict(name='h4',context='block', attrs=allowParaAttrs), dict(name='h5',context='block', attrs=allowParaAttrs), dict(name='h6',context='block', attrs=allowParaAttrs), dict(name='strong',context=('inline', 'li', 'td'), attrs=[]), dict(name='em',context=('inline', 'li', 'td'), attrs=[]), dict(name='i',context=('inline', 'li', 'td'), attrs=[]), dict(name='b',context=('inline', 'li', 'td'), attrs=[]), dict(name='',context=('inline', 'li', 'td'), attrs=[]), dict(name='sup',context=('inline', 'li', 'td'), attrs=[]), dict(name='sub',context=('inline', 'li', 'td'), attrs=[]), dict(name='br',context=('inline', 'li', 'td'), attrs=[], selfClosing=True), dict(name='a',context=('inline', 'li', 'td'), attrs=['href']), # force_attrs. These attributes will be added to make sure xhtml validation passes. dict(name='img',context=('inline', 'li'), attrs=['src','width','height','alt'], force_attrs=['alt'], selfClosing=True), dict(name='table',context='block', attrs=[]), dict(name='tr',context='table', attrs=[]), dict(name='td',context='tr', attrs=[]), dict(name='th',context='tr', attrs=[]), dict(name='ul',context='block', attrs=[]), dict(name='ol',context='block', attrs=[]), dict(name='li',context='list', attrs=[]), ]) # Tags to use to cover naked text up with in the given context self._dataCover = uniDict(dict( block=(self.DEFAULT_BLOCK_TAG,), table=('tr', 'td'), tr=('td',), list=('li',), )) # if not self.allowTables: # del self._tagDict['table'] # del self._tagDict['tr'] # del self._tagDict['td'] if not self.allowImages: del self._tagDict['img'] if not self.allowAtags: del self._tagDict['a'] #work out in-place the set of tags allowed in each context. for tagName,tag in self._tagDict.items(): if 'selfClosing' not in tag: tag['selfClosing'] = False contexts = tag['context'] if not isSeq(contexts): contexts = contexts, for ctxName in contexts: context = self._contextDict.get(ctxName) context['canContain'].add(tagName) #work out certain useful attributes self.valid_tags = set(self._tagDict.keys()) self.valid_block_tags = self.tagsAllowedInContext('block') self.valid_inline_tags = self.tagsAllowedInContext('inline') self.valid_other_tags = self.valid_tags - self.valid_block_tags - self.valid_inline_tags
def breakLines(self, width): """ Returns a broken line structure. There are two cases A) For the simple case of a single formatting input fragment the output is A fragment specifier with - kind = 0 - fontName, fontSize, leading, textColor - lines= A list of lines Each line has two items: 1. unused width in points 2. a list of words B) When there is more than one input formatting fragment the out put is A fragment specifier with - kind = 1 - lines = A list of fragments each having fields: - extraspace (needed for justified) - fontSize - words=word list - each word is itself a fragment with - various settings This structure can be used to easily draw paragraphs with the various alignments. You can supply either a single width or a list of widths; the latter will have its last item repeated until necessary. A 2-element list is useful when there is a different first line indent; a longer list could be created to facilitate custom wraps around irregular objects.""" self._width_max = 0 if not isSeq(width): maxWidths = [width] else: maxWidths = width lines = [] lineno = 0 maxWidth = maxWidths[lineno] style = self.style fFontSize = float(style.fontSize) requiredWidth = 0 #for bullets, work out width and ensure we wrap the right amount onto line one _handleBulletWidth(self.bulletText, style, maxWidths) self.height = 0 autoLeading = getattr(self, 'autoLeading', getattr(style, 'autoLeading', '')) calcBounds = autoLeading not in ('', 'off') frags = self.frags nFrags = len(frags) if nFrags == 1: f = frags[0] if hasattr(f, 'text'): fontSize = f.fontSize fontName = f.fontName ascent, descent = getAscentDescent(fontName, fontSize) kind = 0 L = f.text.split('\n') for l in L: currentWidth = stringWidth(l, fontName, fontSize) if currentWidth > self._width_max: self._width_max = currentWidth requiredWidth = max(currentWidth, requiredWidth) extraSpace = maxWidth - currentWidth lines.append((extraSpace, l.split(' '), currentWidth)) lineno = lineno + 1 maxWidth = lineno < len( maxWidths) and maxWidths[lineno] or maxWidths[-1] blPara = f.clone(kind=kind, lines=lines, ascent=ascent, descent=descent, fontSize=fontSize) else: kind = f.kind lines = f.lines for L in lines: if kind == 0: currentWidth = L[2] else: currentWidth = L.currentWidth requiredWidth = max(currentWidth, requiredWidth) blPara = f.clone(kind=kind, lines=lines) self.width = max(self.width, requiredWidth) return blPara elif nFrags <= 0: return ParaLines(kind=0, fontSize=style.fontSize, fontName=style.fontName, textColor=style.textColor, ascent=style.fontSize, descent=-0.2 * style.fontSize, lines=[]) else: for L in _getFragLines(frags): currentWidth, n, w = _getFragWord(L, maxWidth) f = w[0][0] maxSize = f.fontSize maxAscent, minDescent = getAscentDescent(f.fontName, maxSize) words = [f.clone()] words[-1].text = w[0][1] for i in w[1:]: f = i[0].clone() f.text = i[1] words.append(f) fontSize = f.fontSize fontName = f.fontName if calcBounds: cbDefn = getattr(f, 'cbDefn', None) if getattr(cbDefn, 'width', 0): descent, ascent = imgVRange( imgNormV(cbDefn.height, fontSize), cbDefn.valign, fontSize) else: ascent, descent = getAscentDescent( fontName, fontSize) else: ascent, descent = getAscentDescent(fontName, fontSize) maxSize = max(maxSize, fontSize) maxAscent = max(maxAscent, ascent) minDescent = min(minDescent, descent) lineno += 1 maxWidth = lineno < len( maxWidths) and maxWidths[lineno] or maxWidths[-1] requiredWidth = max(currentWidth, requiredWidth) extraSpace = maxWidth - currentWidth if currentWidth > self._width_max: self._width_max = currentWidth lines.append( ParaLines(extraSpace=extraSpace, wordCount=n, words=words, fontSize=maxSize, ascent=maxAscent, descent=minDescent, currentWidth=currentWidth, preformatted=True)) self.width = max(self.width, requiredWidth) return ParaLines(kind=1, lines=lines) return lines
def __init__(self, tests, name=None): if not isSeq(tests): tests = (tests, ) self._tests = tests if name: self._str = name