~ubuntu-branches/ubuntu/vivid/python-reportlab/vivid-proposed

« back to all changes in this revision

Viewing changes to src/reportlab/platypus/paragraph.py

  • Committer: Package Import Robot
  • Author(s): Matthias Klose
  • Date: 2014-01-12 02:16:21 UTC
  • mfrom: (1.2.9) (4.1.9 sid)
  • Revision ID: package-import@ubuntu.com-20140112021621-f4mpp5qaj1dkq2yb
Tags: 2.8~20140112-1
* New upstream snapshot.
* Build python3 packages.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#Copyright ReportLab Europe Ltd. 2000-2012
2
2
#see license.txt for license details
3
3
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/platypus/paragraph.py
4
 
__version__=''' $Id: paragraph.py 3959 2012-09-27 14:39:39Z robin $ '''
 
4
__version__=''' $Id$ '''
5
5
__doc__='''The standard paragraph implementation'''
6
 
from string import join, whitespace
 
6
from string import whitespace
7
7
from operator import truth
8
 
from types import StringType, ListType
9
8
from unicodedata import category
10
9
from reportlab.pdfbase.pdfmetrics import stringWidth, getFont, getAscentDescent
11
10
from reportlab.platypus.paraparser import ParaParser
19
18
from reportlab.lib.abag import ABag
20
19
from reportlab.rl_config import platypus_link_underline
21
20
from reportlab import rl_config
 
21
from reportlab.lib.utils import isBytes, unicodeT, bytesT, strTypes
 
22
from reportlab.lib.rl_accel import sameFrag
22
23
import re
23
24
 
24
 
#on UTF8 branch, split and strip must be unicode-safe!
 
25
#on UTF8/py33 branch, split and strip must be unicode-safe!
25
26
#thanks to Dirk Holtwick for helpful discussions/insight
26
27
#on this one
27
28
_wsc = ''.join((
59
60
_wsc_re_split=re.compile('[%s]+'% re.escape(_wsc)).split
60
61
 
61
62
def split(text, delim=None):
62
 
    if type(text) is str: text = text.decode('utf8')
63
 
    if type(delim) is str: delim = delim.decode('utf8')
64
 
    if delim is None and u'\xa0' in text:
65
 
        return [uword.encode('utf8') for uword in _wsc_re_split(text)]
66
 
    return [uword.encode('utf8') for uword in text.split(delim)]
 
63
    if isBytes(text): text = text.decode('utf8')
 
64
    if delim is not None and isBytes(delim): delim = delim.decode('utf8')
 
65
    return [uword for uword in (_wsc_re_split(text) if delim is None and u'\xa0' in text else text.split(delim))]
67
66
 
68
67
def strip(text):
69
 
    if type(text) is str: text = text.decode('utf8')
70
 
    return text.strip(_wsc).encode('utf8')
 
68
    if isBytes(text): text = text.decode('utf8')
 
69
    return text.strip(_wsc)
71
70
 
72
71
class ParaLines(ABag):
73
72
    """
91
90
                    but could be used for line spacing.
92
91
    """
93
92
 
94
 
#our one and only parser
95
 
# XXXXX if the parser has any internal state using only one is probably a BAD idea!
96
 
_parser=ParaParser()
97
 
 
98
93
def _lineClean(L):
99
 
    return join(filter(truth,split(strip(L))))
 
94
    return ' '.join(list(filter(truth,split(strip(L)))))
100
95
 
101
96
def cleanBlockQuotedText(text,joiner=' '):
102
97
    """This is an internal utility which takes triple-
103
98
    quoted text form within the document and returns
104
99
    (hopefully) the paragraph the user intended originally."""
105
 
    L=filter(truth,map(_lineClean, split(text, '\n')))
106
 
    return join(L, joiner)
 
100
    L=list(filter(truth,list(map(_lineClean, split(text, '\n')))))
 
101
    return joiner.join(L)
107
102
 
108
103
def setXPos(tx,dx):
109
104
    if dx>1e-6 or dx<-1e-6:
111
106
 
112
107
def _leftDrawParaLine( tx, offset, extraspace, words, last=0):
113
108
    setXPos(tx,offset)
114
 
    tx._textOut(join(words),1)
 
109
    tx._textOut(' '.join(words),1)
115
110
    setXPos(tx,-offset)
116
111
    return offset
117
112
 
118
113
def _centerDrawParaLine( tx, offset, extraspace, words, last=0):
119
114
    m = offset + 0.5 * extraspace
120
115
    setXPos(tx,m)
121
 
    tx._textOut(join(words),1)
 
116
    tx._textOut(' '.join(words),1)
122
117
    setXPos(tx,-m)
123
118
    return m
124
119
 
125
120
def _rightDrawParaLine( tx, offset, extraspace, words, last=0):
126
121
    m = offset + extraspace
127
122
    setXPos(tx,m)
128
 
    tx._textOut(join(words),1)
 
123
    tx._textOut(' '.join(words),1)
129
124
    setXPos(tx,-m)
130
125
    return m
131
126
 
132
127
def _nbspCount(w):
133
 
    if isinstance(w,str):
134
 
        return w.count('\xc2\xa0')
 
128
    if isBytes(w):
 
129
        return w.count(b'\xc2\xa0')
135
130
    else:
136
131
        return w.count(u'\xa0')
137
132
 
138
133
def _justifyDrawParaLine( tx, offset, extraspace, words, last=0):
139
134
    setXPos(tx,offset)
140
 
    text  = join(words)
 
135
    text  = ' '.join(words)
141
136
    if last or extraspace<=1e-8:
142
137
        #last one, left align
143
138
        tx._textOut(text,1)
400
395
        _putFragLine(offset, tx, line, last, 'justify') #no space modification
401
396
    setXPos(tx,-offset)
402
397
 
403
 
try:
404
 
    from _rl_accel import _sameFrag
405
 
except ImportError:
406
 
    try:
407
 
        from reportlab.lib._rl_accel import _sameFrag
408
 
    except ImportError:
409
 
        #if you modify this you need to modify _rl_accel RGB
410
 
        def _sameFrag(f,g):
411
 
            'returns 1 if two ParaFrags map out the same'
412
 
            if (hasattr(f,'cbDefn') or hasattr(g,'cbDefn')
413
 
                    or hasattr(f,'lineBreak') or hasattr(g,'lineBreak')): return 0
414
 
            for a in ('fontName', 'fontSize', 'textColor', 'rise', 'underline', 'strike', 'link', "backColor"):
415
 
                if getattr(f,a,None)!=getattr(g,a,None): return 0
416
 
            return 1
417
 
 
418
398
def _getFragWords(frags,maxWidth=None):
419
399
    ''' given a Parafrag list return a list of fragwords
420
400
        [[size, (f00,w00), ..., (f0n,w0n)],....,[size, (fm0,wm0), ..., (f0n,wmn)]]
488
468
 
489
469
    return R
490
470
 
 
471
def _fragWordIter(w):
 
472
    for f, s in w[1:]:
 
473
        if hasattr(f,'cbDefn'):
 
474
            yield f, getattr(f,'width'), s
 
475
        elif s:
 
476
            if isBytes(s):
 
477
                s = s.decode('utf8')    #only encoding allowed
 
478
            for c in s:
 
479
                yield f, stringWidth(c,f.fontName, f.fontSize), c
 
480
        else:
 
481
            yield f, 0, s
 
482
 
 
483
class _SplitList(list):
 
484
    pass
 
485
 
 
486
def _splitFragWord(w,maxWidth,maxWidths,lineno):
 
487
    '''given a frag word, w, as returned by getFragWords
 
488
    split it into frag words that fit in lines of length
 
489
    maxWidth
 
490
    maxWidths[lineno+1]
 
491
    .....
 
492
    maxWidths[lineno+n]
 
493
 
 
494
    return the new word list
 
495
    '''
 
496
    R = []
 
497
    maxlineno = len(maxWidths)-1
 
498
    W = []
 
499
    lineWidth = 0
 
500
    fragText = ''
 
501
    wordWidth = 0
 
502
    f = w[1][0]
 
503
    for g,cw,c in _fragWordIter(w):
 
504
        newLineWidth = lineWidth+cw
 
505
        tooLong = newLineWidth>maxWidth
 
506
        if g is not f or tooLong:
 
507
            f = f.clone()
 
508
            if hasattr(f,'text'):
 
509
                f.text = fragText
 
510
            W.append((f,fragText))
 
511
            if tooLong:
 
512
                W = _SplitList([wordWidth]+W)
 
513
                R.append(W)
 
514
                lineno += 1
 
515
                maxWidth = maxWidths[min(maxlineno,lineno)]
 
516
                W = []
 
517
                newLineWidth = wordWidth = cw
 
518
            fragText = ''
 
519
            f = g
 
520
            wordWidth = 0
 
521
        wordWidth += cw
 
522
        fragText += c
 
523
        lineWidth = newLineWidth
 
524
    W.append((f,fragText))
 
525
    W = _SplitList([wordWidth]+W)
 
526
    R.append(W)
 
527
    return R
 
528
 
 
529
class _SplitText(str):
 
530
    pass
 
531
 
 
532
def _splitWord(w,maxWidth,maxWidths,lineno,fontName,fontSize,encoding):
 
533
    '''
 
534
    split w into words that fit in lines of length
 
535
    maxWidth
 
536
    maxWidths[lineno+1]
 
537
    .....
 
538
    maxWidths[lineno+n]
 
539
 
 
540
    then push those new words onto words
 
541
    '''
 
542
    #TODO fix this to use binary search for the split points
 
543
    R = []
 
544
    maxlineno = len(maxWidths)-1
 
545
    lineWidth = 0
 
546
    wordText = u''
 
547
    if isBytes(w):
 
548
        w = w.decode('utf8')
 
549
    for c in w:
 
550
        cw = stringWidth(c,fontName,fontSize,encoding)
 
551
        newLineWidth = lineWidth+cw
 
552
        if newLineWidth>maxWidth:
 
553
            R.append(_SplitText(wordText))
 
554
            lineno += 1
 
555
            maxWidth = maxWidths[min(maxlineno,lineno)]
 
556
            newLineWidth = cw
 
557
            wordText = ''
 
558
        wordText += c
 
559
        lineWidth = newLineWidth
 
560
    R.append(_SplitText(wordText))
 
561
    return R
 
562
 
491
563
def _split_blParaSimple(blPara,start,stop):
492
564
    f = blPara.clone()
493
565
    for a in ('lines', 'kind', 'text'):
519
591
    tx2 = canvas.beginText(style.bulletIndent, cur_y+getattr(style,"bulletOffsetY",0))
520
592
    tx2.setFont(style.bulletFontName, style.bulletFontSize)
521
593
    tx2.setFillColor(hasattr(style,'bulletColor') and style.bulletColor or style.textColor)
522
 
    if isinstance(bulletText,basestring):
 
594
    if isinstance(bulletText,strTypes):
523
595
        tx2.textOut(bulletText)
524
596
    else:
525
597
        for f in bulletText:
538
610
    '''work out bullet width and adjust maxWidths[0] if neccessary
539
611
    '''
540
612
    if bulletText:
541
 
        if isinstance(bulletText,basestring):
 
613
        if isinstance(bulletText,strTypes):
542
614
            bulletWidth = stringWidth( bulletText, style.bulletFontName, style.bulletFontSize)
543
615
        else:
544
616
            #it's a list of fragments
611
683
 
612
684
def _do_under_line(i, t_off, ws, tx, lm=-0.125):
613
685
    y = tx.XtraState.cur_y - i*tx.XtraState.style.leading + lm*tx.XtraState.f.fontSize
614
 
    textlen = tx._canvas.stringWidth(join(tx.XtraState.lines[i][1]), tx._fontname, tx._fontsize)
 
686
    textlen = tx._canvas.stringWidth(' '.join(tx.XtraState.lines[i][1]), tx._fontname, tx._fontsize)
615
687
    tx._canvas.line(t_off, y, t_off+textlen+ws, y)
616
688
 
617
689
_scheme_re = re.compile('^[a-zA-Z][-+a-zA-Z0-9]+$')
618
690
def _doLink(tx,link,rect):
619
 
    if isinstance(link,unicode):
620
 
        link = link.encode('utf8')
621
691
    parts = link.split(':',1)
622
692
    scheme = len(parts)==2 and parts[0].lower() or ''
623
693
    if _scheme_re.match(scheme) and scheme!='document':
634
704
    xs = tx.XtraState
635
705
    leading = xs.style.leading
636
706
    y = xs.cur_y - i*leading - xs.f.fontSize/8.0 # 8.0 factor copied from para.py
637
 
    text = join(xs.lines[i][1])
 
707
    text = ' '.join(xs.lines[i][1])
638
708
    textlen = tx._canvas.stringWidth(text, tx._fontname, tx._fontsize)
639
709
    _doLink(tx, xs.link, (t_off, y, t_off+textlen+ws, y+leading))
640
710
 
695
765
    if tt:
696
766
        tt=tt.lower()
697
767
        if tt=='lowercase':
698
 
            tt = unicode.lower
 
768
            tt = unicodeT.lower
699
769
        elif tt=='uppercase':
700
 
            tt = unicode.upper
 
770
            tt = unicodeT.upper
701
771
        elif  tt=='capitalize':
702
 
            tt = unicode.title
 
772
            tt = unicodeT.title
703
773
        elif tt=='none':
704
774
            return
705
775
        else:
707
777
        n = len(frags)
708
778
        if n==1:
709
779
            #single fragment the easy case
710
 
            frags[0].text = tt(frags[0].text.decode('utf8')).encode('utf8')
711
 
        elif tt is unicode.title:
 
780
            frags[0].text = tt(frags[0].text)
 
781
        elif tt is unicodeT.title:
712
782
            pb = True
713
783
            for f in frags:
714
 
                t = f.text
715
 
                if not t: continue
716
 
                u = t.decode('utf8')
 
784
                u = f.text
 
785
                if not u: continue
717
786
                if u.startswith(u' ') or pb:
718
787
                    u = tt(u)
719
788
                else:
721
790
                    if i>=0:
722
791
                        u = u[:i]+tt(u[i:])
723
792
                pb = u.endswith(u' ')
724
 
                f.text = u.encode('utf8')
 
793
                f.text = u
725
794
        else:
726
795
            for f in frags:
727
 
                t = f.text
728
 
                if not t: continue
729
 
                f.text = tt(t.decode('utf8')).encode('utf8')
 
796
                u = f.text
 
797
                if not u: continue
 
798
                f.text = tt(u)
730
799
 
731
 
class cjkU(unicode):
 
800
class cjkU(unicodeT):
732
801
    '''simple class to hold the frag corresponding to a str'''
733
802
    def __new__(cls,value,frag,encoding):
734
 
        self = unicode.__new__(cls,value)
 
803
        self = unicodeT.__new__(cls,value)
735
804
        self._frag = frag
736
805
        if hasattr(frag,'cbDefn'):
737
806
            w = getattr(frag.cbDefn,'width',0)
742
811
    frag = property(lambda self: self._frag)
743
812
    width = property(lambda self: self._width)
744
813
 
745
 
def makeCJKParaLine(U,extraSpace,calcBounds):
 
814
def makeCJKParaLine(U,maxWidth,widthUsed,extraSpace,lineBreak,calcBounds):
746
815
    words = []
747
816
    CW = []
748
817
    f0 = FragLine()
761
830
        maxSize = max(maxSize,fontSize)
762
831
        maxAscent = max(maxAscent,ascent)
763
832
        minDescent = min(minDescent,descent)
764
 
        if not _sameFrag(f0,f):
 
833
        if not sameFrag(f0,f):
765
834
            f0=f0.clone()
766
835
            f0.text = u''.join(CW)
767
836
            words.append(f0)
772
841
        f0=f0.clone()
773
842
        f0.text = u''.join(CW)
774
843
        words.append(f0)
775
 
    return FragLine(kind=1,extraSpace=extraSpace,wordCount=1,words=words[1:],fontSize=maxSize,ascent=maxAscent,descent=minDescent)
 
844
    return FragLine(kind=1,extraSpace=extraSpace,wordCount=1,words=words[1:],fontSize=maxSize,ascent=maxAscent,descent=minDescent,maxWidth=maxWidth,currentWidth=widthUsed,lineBreak=lineBreak)
776
845
 
777
846
def cjkFragSplit(frags, maxWidths, calcBounds, encoding='utf8'):
778
847
    '''This attempts to be wordSplit for frags using the dumb algorithm'''
780
849
    U = []  #get a list of single glyphs with their widths etc etc
781
850
    for f in frags:
782
851
        text = f.text
783
 
        if not isinstance(text,unicode):
 
852
        if isBytes(text):
784
853
            text = text.decode(encoding)
785
854
        if text:
786
855
            U.extend([cjkU(t,f,encoding) for t in text])
817
886
                    #  - reversion to Kanji (which would be a good split point)
818
887
                    #  - in the worst case, roughly half way back along the line
819
888
                    limitCheck = (lineStartPos+i)>>1        #(arbitrary taste issue)
820
 
                    for j in xrange(i-1,limitCheck,-1):
 
889
                    for j in range(i-1,limitCheck,-1):
821
890
                        uj = U[j]
822
891
                        if uj and category(uj)=='Zs' or ord(uj)>=0x3000:
823
892
                            k = j+1
824
893
                            if k<i:
825
894
                                j = k+1
826
 
                                extraSpace += sum(U[ii].width for ii in xrange(j,i))
 
895
                                extraSpace += sum(U[ii].width for ii in range(j,i))
827
896
                                w = U[k].width
828
897
                                u = U[k]
829
898
                                i = j
841
910
                    #the i>lineStart+1 condition ensures progress
842
911
                    i -= 1
843
912
                    extraSpace += w
844
 
            lines.append(makeCJKParaLine(U[lineStartPos:i],extraSpace,calcBounds))
 
913
            lines.append(makeCJKParaLine(U[lineStartPos:i],maxWidth,widthUsed,extraSpace,lineBreak,calcBounds))
845
914
            try:
846
915
                maxWidth = maxWidths[len(lines)]
847
916
            except IndexError:
852
921
 
853
922
    #any characters left?
854
923
    if widthUsed > 0:
855
 
        lines.append(makeCJKParaLine(U[lineStartPos:],maxWidth-widthUsed,calcBounds))
 
924
        lines.append(makeCJKParaLine(U[lineStartPos:],maxWidth,widthUsed,maxWidth-widthUsed,False,calcBounds))
856
925
 
857
926
    return ParaLines(kind=1,lines=lines)
858
927
 
918
987
        self.encoding = encoding
919
988
        self._setup(text, style, bulletText or getattr(style,'bulletText',None), frags, cleanBlockQuotedText)
920
989
 
 
990
 
921
991
    def __repr__(self):
922
992
        n = self.__class__.__name__
923
993
        L = [n+"("]
924
 
        keys = self.__dict__.keys()
 
994
        keys = list(self.__dict__.keys())
925
995
        for k in keys:
926
996
            L.append('%s: %s' % (repr(k).replace("\n", " ").replace("  "," "),repr(getattr(self, k)).replace("\n", " ").replace("  "," ")))
927
997
        L.append(") #"+n)
928
998
        return '\n'.join(L)
929
999
 
930
1000
    def _setup(self, text, style, bulletText, frags, cleaner):
 
1001
        
 
1002
        #This used to be a global parser to save overhead.
 
1003
        #In the interests of thread safety it is being instantiated per paragraph.
 
1004
        #On the next release, we'll replace with a cElementTree parser
 
1005
        _parser = ParaParser()
 
1006
 
931
1007
        if frags is None:
932
1008
            text = cleaner(text)
933
1009
            _parser.caseSensitive = self.caseSensitive
997
1073
        else:
998
1074
            words = _getFragWords(frags)
999
1075
            func  = lambda x: x[0]
1000
 
        return max(map(func,words))
 
1076
        return max(list(map(func,words)))
1001
1077
 
1002
1078
    def _get_split_blParaFunc(self):
1003
1079
        return self.blPara.kind==0 and _split_blParaSimple or _split_blParaHard
1123
1199
        else: maxWidths = width
1124
1200
        lines = []
1125
1201
        self.height = lineno = 0
 
1202
        maxlineno = len(maxWidths)-1
1126
1203
        style = self.style
 
1204
        splitLongWords = style.splitLongWords
1127
1205
 
1128
1206
        #for bullets, work out width and ensure we wrap the right amount onto line one
1129
1207
        _handleBulletWidth(self.bulletText,style,maxWidths)
1146
1224
                else:
1147
1225
                    words = split(text)
1148
1226
            else:
1149
 
                words = f.words
 
1227
                words = f.words[:]
1150
1228
                for w in words:
1151
1229
                    if strip(w): break
1152
1230
                else:
1154
1232
            spaceWidth = stringWidth(' ', fontName, fontSize, self.encoding)
1155
1233
            cLine = []
1156
1234
            currentWidth = -spaceWidth   # hack to get around extra space for word 1
1157
 
            for word in words:
 
1235
            while words:
 
1236
                word = words.pop(0)
1158
1237
                #this underscores my feeling that Unicode throughout would be easier!
1159
1238
                wordWidth = stringWidth(word, fontName, fontSize, self.encoding)
1160
1239
                newWidth = currentWidth + spaceWidth + wordWidth
 
1240
                if newWidth>maxWidth:
 
1241
                    nmw = min(lineno,maxlineno)
 
1242
                    if wordWidth>max(maxWidths[nmw:nmw+1]) and not isinstance(word,_SplitText) and splitLongWords:
 
1243
                        #a long word
 
1244
                        words[0:0] = _splitWord(word,maxWidth-spaceWidth-currentWidth,maxWidths,lineno,fontName,fontSize,self.encoding)
 
1245
                        continue
1161
1246
                if newWidth <= maxWidth or not len(cLine):
1162
1247
                    # fit one more on this line
1163
1248
                    cLine.append(word)
1169
1254
                    cLine = [word]
1170
1255
                    currentWidth = wordWidth
1171
1256
                    lineno += 1
1172
 
                    try:
1173
 
                        maxWidth = maxWidths[lineno]
1174
 
                    except IndexError:
1175
 
                        maxWidth = maxWidths[-1]  # use the last one
 
1257
                    maxWidth = maxWidths[min(maxlineno,lineno)]
1176
1258
 
1177
1259
            #deal with any leftovers on the final line
1178
1260
            if cLine!=[]:
1191
1273
                return self.blPara
1192
1274
            n = 0
1193
1275
            words = []
1194
 
            for w in _getFragWords(frags,maxWidth):
 
1276
            _words = _getFragWords(frags,maxWidth)
 
1277
            while _words:
 
1278
                w = _words.pop(0)
1195
1279
                f=w[-1][0]
1196
1280
                fontName = f.fontName
1197
1281
                fontSize = f.fontSize
1212
1296
                #test to see if this frag is a line break. If it is we will only act on it
1213
1297
                #if the current width is non-negative or the previous thing was a deliberate lineBreak
1214
1298
                lineBreak = hasattr(f,'lineBreak')
 
1299
                if not lineBreak and newWidth>maxWidth and not isinstance(w,_SplitList) and splitLongWords:
 
1300
                    nmw = min(lineno,maxlineno)
 
1301
                    if wordWidth>max(maxWidths[nmw:nmw+1]):
 
1302
                        #a long word
 
1303
                        _words[0:0] = _splitFragWord(w,maxWidth-spaceWidth-currentWidth,maxWidths,lineno)
 
1304
                        continue
1215
1305
                endLine = (newWidth>maxWidth and n>0) or lineBreak
1216
1306
                if not endLine:
1217
1307
                    if lineBreak: continue      #throw it away
1233
1323
                        g = f.clone()
1234
1324
                        words = [g]
1235
1325
                        g.text = nText
1236
 
                    elif not _sameFrag(g,f):
 
1326
                    elif not sameFrag(g,f):
1237
1327
                        if currentWidth>0 and ((nText!='' and nText[0]!=' ') or hasattr(f,'cbDefn')):
1238
1328
                            if hasattr(g,'cbDefn'):
1239
1329
                                i = len(words)-1
1293
1383
 
1294
1384
                    #start new line
1295
1385
                    lineno += 1
1296
 
                    try:
1297
 
                        maxWidth = maxWidths[lineno]
1298
 
                    except IndexError:
1299
 
                        maxWidth = maxWidths[-1]  # use the last one
 
1386
                    maxWidth = maxWidths[min(maxlineno,lineno)]
1300
1387
 
1301
1388
                    if lineBreak:
1302
1389
                        n = 0
1356
1443
        _handleBulletWidth(self.bulletText, style, maxWidths)
1357
1444
        frags = self.frags
1358
1445
        nFrags = len(frags)
1359
 
        if nFrags==1 and not hasattr(frags[0],'cbDefn'):
 
1446
        if nFrags==1 and not hasattr(frags[0],'cbDefn') and not style.endDots:
1360
1447
            f = frags[0]
1361
1448
            if hasattr(self,'blPara') and getattr(self,'_splitpara',0):
1362
1449
                return f.clone(kind=0, lines=self.blPara.lines)
1510
1597
                    if noJustifyLast and nLines==1 and style.endDots and dpl!=_rightDrawParaLine: _do_dots(0, dx, ws, xs, tx, dpl)
1511
1598
 
1512
1599
                    #now the middle of the paragraph, aligned with the left margin which is our origin.
1513
 
                    for i in xrange(1, nLines):
 
1600
                    for i in range(1, nLines):
1514
1601
                        ws = lines[i][0]
1515
1602
                        t_off = dpl( tx, _offsets[i], ws, lines[i][1], noJustifyLast and i==lim)
1516
1603
                        dx = t_off+leftIndent
1520
1607
                        if link: _do_link_line(i, dx, ws, tx)
1521
1608
                        if noJustifyLast and i==lim and style.endDots and dpl!=_rightDrawParaLine: _do_dots(i, dx, ws, xs, tx, dpl)
1522
1609
                else:
1523
 
                    for i in xrange(1, nLines):
 
1610
                    for i in range(1, nLines):
1524
1611
                        dpl( tx, _offsets[i], lines[i][0], lines[i][1], noJustifyLast and i==lim)
1525
1612
            else:
1526
1613
                f = lines[0]
1573
1660
                _do_post_text(tx)
1574
1661
 
1575
1662
                #now the middle of the paragraph, aligned with the left margin which is our origin.
1576
 
                for i in xrange(1, nLines):
 
1663
                for i in range(1, nLines):
1577
1664
                    f = lines[i]
1578
1665
                    dpl( tx, _offsets[i], f, noJustifyLast and i==lim)
1579
1666
                    _do_post_text(tx)
1590
1677
            for frag in frags:
1591
1678
                if hasattr(frag, 'text'):
1592
1679
                    plains.append(frag.text)
1593
 
            return join(plains, '')
 
1680
            return ''.join(plains)
1594
1681
        elif identify:
1595
1682
            text = getattr(self,'text',None)
1596
1683
            if text is None: text = repr(self)
1608
1695
            func = lambda frag, w=self.width: w - frag.extraSpace
1609
1696
        else:
1610
1697
            func = lambda frag, w=self.width: w - frag[0]
1611
 
        return map(func,self.blPara.lines)
 
1698
        return list(map(func,self.blPara.lines))
1612
1699
 
1613
1700
if __name__=='__main__':    #NORUNTESTS
1614
1701
    def dumpParagraphLines(P):
1615
 
        print 'dumpParagraphLines(<Paragraph @ %d>)' % id(P)
 
1702
        print('dumpParagraphLines(<Paragraph @ %d>)' % id(P))
1616
1703
        lines = P.blPara.lines
 
1704
        outw = sys.stdout.write
1617
1705
        for l,line in enumerate(lines):
1618
1706
            line = lines[l]
1619
1707
            if hasattr(line,'words'):
1621
1709
            else:
1622
1710
                words = line[1]
1623
1711
            nwords = len(words)
1624
 
            print 'line%d: %d(%s)\n  ' % (l,nwords,str(getattr(line,'wordCount','Unknown'))),
1625
 
            for w in xrange(nwords):
1626
 
                print "%d:'%s'"%(w,getattr(words[w],'text',words[w])),
1627
 
            print
 
1712
            outw('line%d: %d(%s)\n  ' % (l,nwords,str(getattr(line,'wordCount','Unknown'))))
 
1713
            for w in range(nwords):
 
1714
                outw(" %d:'%s'"%(w,getattr(words[w],'text',words[w])))
 
1715
            print()
1628
1716
 
1629
1717
    def fragDump(w):
1630
1718
        R= ["'%s'" % w[1]]
1634
1722
        return ', '.join(R)
1635
1723
 
1636
1724
    def dumpParagraphFrags(P):
1637
 
        print 'dumpParagraphFrags(<Paragraph @ %d>) minWidth() = %.2f' % (id(P), P.minWidth())
 
1725
        print('dumpParagraphFrags(<Paragraph @ %d>) minWidth() = %.2f' % (id(P), P.minWidth()))
1638
1726
        frags = P.frags
1639
1727
        n =len(frags)
1640
 
        for l in xrange(n):
1641
 
            print "frag%d: '%s' %s" % (l, frags[l].text,' '.join(['%s=%s' % (k,getattr(frags[l],k)) for k in frags[l].__dict__ if k!=text]))
 
1728
        for l in range(n):
 
1729
            print("frag%d: '%s' %s" % (l, frags[l].text,' '.join(['%s=%s' % (k,getattr(frags[l],k)) for k in frags[l].__dict__ if k!=text])))
1642
1730
 
 
1731
        outw = sys.stdout.write
1643
1732
        l = 0
1644
1733
        cum = 0
1645
1734
        for W in _getFragWords(frags,360):
1646
1735
            cum += W[0]
1647
 
            print "fragword%d: cum=%3d size=%d" % (l, cum, W[0]),
 
1736
            outw("fragword%d: cum=%3d size=%d" % (l, cum, W[0]))
1648
1737
            for w in W[1:]:
1649
 
                print '(%s)' % fragDump(w),
1650
 
            print
 
1738
                outw(' (%s)' % fragDump(w))
 
1739
            print()
1651
1740
            l += 1
1652
1741
 
1653
1742
 
1718
1807
        P=Paragraph(text, B)
1719
1808
        dumpParagraphFrags(P)
1720
1809
        w,h = P.wrap(aW,aH)
1721
 
        print 'After initial wrap',w,h
 
1810
        print('After initial wrap',w,h)
1722
1811
        dumpParagraphLines(P)
1723
1812
        S = P.split(aW,aH)
1724
1813
        dumpParagraphFrags(S[0])
1725
1814
        w0,h0 = S[0].wrap(aW,aH)
1726
 
        print 'After split wrap',w0,h0
 
1815
        print('After split wrap',w0,h0)
1727
1816
        dumpParagraphLines(S[0])
1728
1817
 
1729
1818
    if flagged(5):
1757
1846
        w,h = P.wrap(6*72, 9.7*72)
1758
1847
        dumpParagraphLines(P)
1759
1848
        S = P.split(6*72,h/2.0)
1760
 
        print len(S)
 
1849
        print(len(S))
1761
1850
        dumpParagraphLines(S[0])
1762
1851
        dumpParagraphLines(S[1])
1763
1852