1#Copyright ReportLab Europe Ltd. 2000-2017
2#see license.txt for license details
3"""Tests for the reportlab.platypus.paragraphs module.
4"""
5__version__='3.3.0'
6from reportlab.lib.testutils import setOutDir,makeSuiteForClasses, outputfile, printLocation
7setOutDir(__name__)
8import sys, os, unittest
9from operator import truth
10from reportlab.pdfgen.canvas import Canvas
11from reportlab.pdfbase.pdfmetrics import stringWidth, registerFont, registerFontFamily
12from reportlab.pdfbase.ttfonts import TTFont
13from reportlab.platypus.paraparser import ParaParser
14from reportlab.platypus.flowables import Flowable, DocAssert
15from reportlab.lib.colors import Color
16from reportlab.lib.units import cm
17from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY
18from reportlab.lib.utils import _className, asBytes, asUnicode, asNative
19from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
20from reportlab.platypus.xpreformatted import XPreformatted
21from reportlab.platypus.frames import Frame, ShowBoundaryValue
22from reportlab.platypus.doctemplate import PageTemplate, BaseDocTemplate, PageBreak, NextPageTemplate
23from reportlab.platypus import tableofcontents
24from reportlab.platypus.tableofcontents import TableOfContents
25from reportlab.platypus.tables import TableStyle, Table
26from reportlab.platypus.paragraph import Paragraph, _getFragWords, _splitWord, _fragWordSplitRep, ABag, pyphen
27from reportlab.rl_config import rtlSupport, trustedHosts, trustedSchemes
28
29def myMainPageFrame(canvas, doc):
30    "The page frame used for all PDF documents."
31
32    canvas.saveState()
33
34    canvas.rect(2.5*cm, 2.5*cm, 15*cm, 25*cm)
35    canvas.setFont('Times-Roman', 12)
36    pageNumber = canvas.getPageNumber()
37    canvas.drawString(10*cm, cm, str(pageNumber))
38
39    canvas.restoreState()
40
41class MyDocTemplate(BaseDocTemplate):
42    _invalidInitArgs = ('pageTemplates',)
43
44    def __init__(self, filename, **kw):
45        frame1 = Frame(2.5*cm, 2.5*cm, 15*cm, 25*cm, id='F1')
46        frame2 = Frame(2.5*cm, 2.5*cm, 310, 25*cm, id='F2')
47        self.allowSplitting = 0
48        BaseDocTemplate.__init__(self, filename, **kw)
49        template = PageTemplate('normal', [frame1], myMainPageFrame)
50        template1 = PageTemplate('special', [frame2], myMainPageFrame)
51        template2 = PageTemplate('template2', [Frame(395, 108, 165, 645, id='second2')])
52        self.addPageTemplates([template,template1,template2])
53
54class ParagraphCorners(unittest.TestCase):
55    "some corner cases which should parse"
56    def check(self,text,bt = getSampleStyleSheet()['BodyText']):
57        try:
58            P = Paragraph(text,style=bt)
59        except:
60            raise AssertionError("'%s' should parse"%text)
61
62    def test0(self):
63        self.check('<para />')
64        self.check('<para/>')
65        self.check('\t\t\t\n\n\n<para />')
66        self.check('\t\t\t\n\n\n<para/>')
67        self.check('<para\t\t\t\t/>')
68        self.check('<para></para>')
69        self.check('<para>      </para>')
70        self.check('\t\t\n\t\t\t   <para>      </para>')
71
72    def test1(self):
73        "This makes several special paragraphs."
74
75        # Build story.
76        story = []
77        styleSheet = getSampleStyleSheet()
78        bt = styleSheet['BodyText']
79        btN = ParagraphStyle('BodyTextTTNone',parent=bt,textTransform='none')
80        btL = ParagraphStyle('BodyTextTTLower',parent=bt,textTransform='lowercase')
81        btU = ParagraphStyle('BodyTextTTUpper',parent=bt,textTransform='uppercase')
82        btC = ParagraphStyle('BodyTextTTCapitalize',parent=bt,textTransform='capitalize')
83        story.append(Paragraph('''This should be ORDINARY text.''',style=bt))
84        story.append(Paragraph('''This should be ORDINARY text.''',style=btN))
85        story.append(Paragraph('''This should be LOWER text.''',style=btL))
86        story.append(Paragraph('''This should be upper text.''',style=btU))
87        story.append(Paragraph('''This should be cAPITALIZED text.''',style=btC))
88
89        story.append(Paragraph('''T<i>hi</i>s shoul<font color="red">d b</font>e <b>ORDINARY</b> text.''',style=bt))
90        story.append(Paragraph('''T<i>hi</i>s shoul<font color="red">d b</font>e <b>ORDINARY</b> text.''',style=btN))
91        story.append(Paragraph('''T<i>hi</i>s shoul<font color="red">d b</font>e <b>LOWER</b> text.''',style=btL))
92        story.append(Paragraph('''T<i>hi</i>s shoul<font color="red">d b</font>e <b>upper</b> text.''',style=btU))
93        story.append(Paragraph('''T<i>hi</i>s shoul<font color="red">d b</font>e <b>cAPITALIZED</b> text.''',style=btC))
94        doc = MyDocTemplate(outputfile('test_platypus_specialparagraphs.pdf'))
95        doc.multiBuild(story)
96
97    def test2(self):
98        '''CJK splitting in multi-frag case'''
99        style = ParagraphStyle('test', wordWrap = 'CJK')
100        p = Paragraph('bla <i>blub</i> '*130 , style)
101        aW,aH=439.275590551,121.88976378
102        w,h=p.wrap(aW,aH)
103        S=p.split(aW,aH)
104        assert len(S)==2, 'Multi frag CJK splitting failed'
105        w0,h0=S[0].wrap(aW,aH)
106        assert h0<=aH,'Multi-frag CJK split[0] has wrong height %s >= available %s' % (H0,aH)
107        w1,h1=S[1].wrap(aW,aH)
108        assert h0+h1==h, 'Multi-frag-CJK split[0].height(%s)+split[1].height(%s) don\'t add to original %s' % (h0,h1,h)
109
110    def test3(self):
111        '''compare CJK splitting in some edge cases'''
112        from reportlab.platypus.paragraph import Paragraph
113        from reportlab.lib.styles import ParagraphStyle
114        from reportlab.pdfbase import pdfmetrics
115        from reportlab.lib.enums import TA_LEFT
116        sty = ParagraphStyle('A')
117        sty.fontSize = 15
118        sty.leading = sty.fontSize*1.2
119        sty.fontName = 'Courier'
120        sty.alignment = TA_LEFT
121        sty.wordWrap = 'CJK'
122        p0=Paragraph('ABCDEFGHIJKL]N',sty)
123        p1=Paragraph('AB<font color="red">C</font>DEFGHIJKL]N',sty)
124        canv = Canvas(outputfile('test_platypus_paragraph_cjk3.pdf'))
125        ix = len(canv._code)
126        aW = pdfmetrics.stringWidth('ABCD','Courier',15)
127        w,h=p0.wrap(aW,1000000)
128        y = canv._pagesize[1]-72-h
129        p0.drawOn(canv,72,y)
130        w,h=p1.wrap(aW,1000000)
131        y -= h+10
132        p1.drawOn(canv,72,y)
133        w,h=p0.wrap(aW*0.25-2,1000000)
134        y -= h+10
135        p0.drawOn(canv,72,y)
136        w,h=p1.wrap(aW/4.-2,1000000)
137        y -= h+10
138        p1.drawOn(canv,72,y)
139        assert canv._code[ix:]==['q', '1 0 0 1 72 697.8898 cm', 'q', '0 0 0 rg', 'BT 1 0 0 1 0 57 Tm /F2 15 Tf 18 TL (ABCD) Tj T* (EFGH) Tj T* (IJKL]) Tj T* (N) Tj T* ET', 'Q', 'Q', 'q', '1 0 0 1 72 615.8898 cm', 'q', 'BT 1 0 0 1 0 57 Tm 18 TL /F2 15 Tf 0 0 0 rg (AB) Tj 1 0 0 rg (C) Tj 0 0 0 rg (D) Tj T* (EFGH) Tj T* (IJKL]) Tj T* (N) Tj T* ET', 'Q', 'Q', 'q', '1 0 0 1 72 353.8898 cm', 'q', '0 0 0 rg', 'BT 1 0 0 1 0 237 Tm /F2 15 Tf 18 TL (A) Tj T* (B) Tj T* (C) Tj T* (D) Tj T* (E) Tj T* (F) Tj T* (G) Tj T* (H) Tj T* (I) Tj T* (J) Tj T* (K) Tj T* (L) Tj T* (]) Tj T* (N) Tj T* ET', 'Q', 'Q', 'q', '1 0 0 1 72 91.88976 cm', 'q', 'BT 1 0 0 1 0 237 Tm 18 TL /F2 15 Tf 0 0 0 rg (A) Tj T* (B) Tj T* 1 0 0 rg (C) Tj T* 0 0 0 rg (D) Tj T* (E) Tj T* (F) Tj T* (G) Tj T* (H) Tj T* (I) Tj T* (J) Tj T* (K) Tj T* (L) Tj T* (]) Tj T* (N) Tj T* ET', 'Q', 'Q']
140        canv.showPage()
141        canv.save()
142
143    def test4(self):
144        from reportlab.platypus.paragraph import _SHYIndexedStr, stringWidth, _SHYWord, ABag, _shyUnsplit
145        fontName = 'Helvetica'
146        fontSize = 10
147        f = ABag(fontName=fontName,fontSize=fontSize)
148        sW = lambda _: stringWidth(_,fontName,fontSize)
149        text = u'Super\xadcali\xadfragi\xadlistic\xadexpi\xadali\xaddocious'
150        u = _SHYIndexedStr(text)
151        self.assertEqual(u,u'Supercalifragilisticexpialidocious','_SHYIndexStr not as expected')
152        self.assertEqual(u._shyIndices,[5, 9, 14, 20, 24, 27], '_SHYIndexStr._shyIndices are wrong')
153        shyW = _SHYWord([sW(u),(f,u)])
154        hsw = shyW.shyphenate(shyW[0],50)
155        self.assertTrue(hsw,'shyphenate failed')
156        self.assertTrue(
157                hsw[0][0] == 45.01
158                and hsw[0][1][0].__dict__== ABag(fontName='Helvetica', fontSize=10).__dict__
159                and hsw[0][1][1]==u'Supercali-'
160                and hsw[0][1][1]._shyIndices==[5,9], 'left part of shyphenate split failed')
161        self.assertTrue(
162                hsw[1][0] == 101.69000000000001
163                and hsw[1][1][0].__dict__== ABag(fontName='Helvetica', fontSize=10).__dict__
164                and hsw[1][1][1]==u'fragilisticexpialidocious'
165                and hsw[1][1][1]._shyIndices== [5, 11, 15, 18], 'right part of shyphenate split failed')
166        uj = _shyUnsplit(hsw[0][1][1],hsw[1][1][1])
167        self.assertTrue(
168                uj == u
169                and uj._shyIndices==u._shyIndices, '_shyUnsplit failed')
170
171    def test5(self):
172        '''some soft hyphenation'''
173        from reportlab.pdfgen import canvas
174        from reportlab.platypus import Frame, ShowBoundaryValue, Paragraph
175        from reportlab.lib.styles import ParagraphStyle
176
177        pagesize = (80+20, 400)
178        c = canvas.Canvas(  outputfile('test_platypus_soft_hyphenation.pdf'), pagesize=pagesize)
179        f = Frame(10, 0, 68, 400,
180                showBoundary=ShowBoundaryValue(dashArray=(1,1)),
181                leftPadding=0,
182                rightPadding=0,
183                topPadding=0,
184                bottomPadding=0,
185                )
186        style = ParagraphStyle(
187            'normal', fontName='Helvetica', fontSize=12,
188            embeddedHyphenation=1, splitLongWords=0, hyphenationLang='en-GB')
189        shy = asNative(u'\xad')
190        text = shy.join(('Super','cali','fragi','listic','expi','ali','docious'))
191        f.addFromList([Paragraph(text, style)], c)
192        text = u'<span color="red">Super</span><span color="pink">&#173;cali&#173;</span>fragi&#173;listic&#173;expi&#173;ali&#173;docious'
193        f.addFromList([Paragraph(text, style)], c)
194        c.showPage()
195        c.save()
196
197    def test_platypus_paragraphs_embedded2(self):
198        from hashlib import md5 as hashlib_md5
199
200        fontName = 'Helvetica'
201        fontSize = 10
202
203        texts = [asUnicode(text) for text
204                in  [
205                    b'UU\xc2\xadDIS\xc2\xadTU\xc2\xadSA\xc2\xadLA JA TAI\xc2\xadMIK\xc2\xadKO',
206                    b'NUO\xc2\xadRI KAS\xc2\xadVA\xc2\xadTUS\xc2\xadMET\xc2\xadSIK\xc2\xadK\xc3\x96',
207                    b'VART\xc2\xadTU\xc2\xadNUT KAS\xc2\xadVA\xc2\xadTUS\xc2\xadMET\xc2\xadSIK\xc2\xadK\xc3\x96',
208                    b'UU\xc2\xadDIS\xc2\xadTUS-KYP\xc2\xadS\xc3\x84 MET\xc2\xadSIK\xc2\xadK\xc3\x96',
209                    b'SIE\xc2\xadMEN- JA SUO-JUS\xc2\xadPUU-MET\xc2\xadSIK\xc2\xadK\xc3\x96',
210                    b'E\xc2\xadRI-I\xc2\xadK\xc3\x84\xc2\xadIS-RA\xc2\xadKEN\xc2\xadTEI\xc2\xadNEN MET\xc2\xadSIK\xc2\xadK\xc3\x96',
211                    ]
212                ]
213        maxWidth = stringWidth('VARTTUNUT KAS', fontName, fontSize)+0.5
214        span = lambda _t, _c: "<span color='%s'>%s</span>" % (_c, _t)
215
216        pagesize = (20+maxWidth, 800)
217        c = Canvas(outputfile('test_platypus_paragraphs_embedded2.pdf'), pagesize=pagesize)
218
219        expected = [
220                (163, b':\n\x16\x8c\xa9\x87\xf4C\xe0\xe6\xfd/\x07\xe7\xde8'),
221                (163, b'4\x98_\t\xd0\xde\x9a:8\xa7E\xfc\x13\xad\xfbk'),
222                (163, b'9m\xec\xdfX\xe4\x85\xe9tL,\xe5ob\x13w'),
223                (163, b"\xf39*6\x85\xd2\xf9`:<\xe8'\xa9\x8a\xec["),
224                (163, b'\x85\x07\x83\xee\x8d\x11Bp\xd0p\xaa\xcbp\x98\xf4\xb7'),
225                (163, b'\xf0"wp\x91vN\xc8<\xef\xe7\xc8s\xae\xf8\x10'),
226                ]
227        observed = []
228        for eh in 0, 1, 2:
229            for slw in 0, 1:
230                f = Frame(10, 10, maxWidth, 780,
231                        showBoundary=ShowBoundaryValue(dashArray=(1,1)),
232                        leftPadding=0,
233                        rightPadding=0,
234                        topPadding=0,
235                        bottomPadding=0,
236                        )
237                style = ParagraphStyle(
238                    'normal', fontName=fontName, fontSize=fontSize,
239                    embeddedHyphenation=eh, splitLongWords=slw,
240                    hyphenationLang=None)
241                f.addFromList([Paragraph('slw=%d eh=%d' % (slw,eh), style)], c)
242                for text in texts:
243                    f.addFromList([Paragraph(text, style)], c)
244                    mfText = text.split(u'\xad')
245                    mfText[0] = span(mfText[0],'red')
246                    if len(mfText)>1:
247                        mfText[1] = span(mfText[1],'pink')
248                        if len(mfText)>2:
249                            mfText[-1] = span(mfText[-1],'blue')
250                    mfText = u'&#173;'.join(mfText)
251                    f.addFromList([Paragraph(mfText, style)], c)
252                observed.append((len(c._code), hashlib_md5(b"".join((asBytes(b,"latin1") for b in c._code))).digest()))
253                c.showPage()
254        c.save()
255        self.assertEqual(observed, expected)
256
257    def test_lele_img(self):
258        from reportlab.pdfgen.canvas import Canvas
259        from reportlab.platypus import Paragraph
260        from reportlab.lib.styles import getSampleStyleSheet
261        from reportlab.rl_config import defaultPageSize
262        from reportlab.lib.units import cm
263        from reportlab.lib.testutils import testsFolder
264
265        styles = getSampleStyleSheet()
266        c = Canvas(outputfile('test_lele_img.pdf'), pagesize=defaultPageSize)
267        s = styles["Normal"]
268        t = '<img src="%s/pythonpowered.gif" valign="middle"/>' % testsFolder
269        p = Paragraph(t, s)
270        owh = p.wrap(cm,cm)
271        cx0 = len(c._code)
272        p.drawOn(c, cm, 2*cm)
273        xcode = ['q', '1 0 0 1 28.34646 56.69291 cm', 'q', 'q', '110 0 0 44 0 -16 cm', '/FormXob.00cb676cb2b2da8ec875fe2c13cf2496 Do', 'Q', 'BT 1 0 0 1 0 2 Tm 12 TL 110 0 Td /F1 10 Tf 12 TL  T* -110 0 Td ET', 'Q', 'Q']
274        xwh = (28.346456692913385, 12)
275        ocode = c._code[cx0:]
276        c.showPage()
277        c.save()
278        self.assertEqual((owh,ocode),(xwh,xcode),
279                "\n(owh,ocode)=%r\nfor test_lele_img.pdf doesn't match expected\n(xwh,xcode)=%r" %(
280                    (xwh,xcode),(owh,ocode)))
281
282DEJAVUSANS = ('DejaVuSans','DejaVuSans-Bold','DejaVuSans-Oblique','DejaVuSans-BoldOblique')
283def haveDejaVu():
284    from reportlab.pdfbase.ttfonts import TTFont
285    for x in DEJAVUSANS:
286        try:
287            TTFont(x,x+'.ttf')
288        except:
289            return False
290    return True
291
292class ParagraphSplitTestCase(unittest.TestCase):
293    "Test multi-page splitting of paragraphs (eyeball-test)."
294
295    def test0(self):
296        "ParagraphSplitTestCase.test0"
297
298        # Build story.
299        story = []
300        styleSheet = getSampleStyleSheet()
301        bt = styleSheet['BodyText']
302        text = '''If you imagine that the box of X's tothe left is
303an image, what I want to be able to do is flow a
304series of paragraphs around the image
305so that once the bottom of the image is reached, then text will flow back to the
306left margin. I know that it would be possible to something like this
307using tables, but I can't see how to have a generic solution.
308There are two examples of this in the demonstration section of the reportlab
309site.
310If you look at the "minimal" euro python conference brochure, at the end of the
311timetable section (page 8), there are adverts for "AdSu" and "O'Reilly". I can
312see how the AdSu one might be done generically, but the O'Reilly, unsure...
313I guess I'm hoping that I've missed something, and that
314it's actually easy to do using platypus.
315'''
316        from reportlab.platypus.flowables import ParagraphAndImage, Image
317        from reportlab.lib.testutils import testsFolder
318        gif = os.path.join(testsFolder,'pythonpowered.gif')
319        story.append(ParagraphAndImage(Paragraph(text,bt),Image(gif)))
320        phrase = 'This should be a paragraph spanning at least three pages. '
321        description = ''.join([('%d: '%i)+phrase for i in range(250)])
322        story.append(ParagraphAndImage(Paragraph(description, bt),Image(gif),side='left'))
323
324        doc = MyDocTemplate(outputfile('test_platypus_paragraphandimage.pdf'))
325        doc.multiBuild(story)
326
327    def test1(self):
328        "ParagraphSplitTestCase.test1"
329
330        # Build story.
331        story = []
332        styleSheet = getSampleStyleSheet()
333        h3 = styleSheet['Heading3']
334        bt = styleSheet['BodyText']
335        st=ParagraphStyle(
336                            name="base",
337                            fontName="Helvetica",
338                            leading=12,
339                            leftIndent=0,
340                            firstLineIndent=0,
341                            spaceBefore = 9.5,
342                            fontSize=9.5,
343                            )
344        text = b'''If you imagine that the box of X's to the left is
345an image, what I want to be able to do is flow a
346series of paragraphs around the image
347so that once the bottom of the image is reached, then text will flow back to the
348left margin. I know that it would be possible to something like this
349using tables, but I can't see how to have a generic solution.
350There are two examples of this in the demonstration section of the reportlab
351site.
352If you look at the "minimal" euro python conference brochure, at the end of the
353timetable section (page 8), there are adverts for "AdSu" and "O'Reilly". I can
354see how the AdSu one might be done generically, but the O'Reilly, unsure...
355I guess I'm hoping that I've missed something, and that
356it's actually easy to do using platypus.We can do greek letters <greek>mDngG</greek>. This should be a
357u with a dieresis on top &lt;unichar code=0xfc/&gt;="<unichar code="0xfc"/>" and this &amp;#xfc;="&#xfc;" and this \\xc3\\xbc="\xc3\xbc". On the other hand this
358should be a pound sign &amp;pound;="&pound;" and this an alpha &amp;alpha;="&alpha;". You can have links in the page <link href="http://www.reportlab.com" color="blue">ReportLab</link> &amp; <a href="http://www.reportlab.org" color="green">ReportLab.org</a>.
359Use scheme "pdf:" to indicate an external PDF link, "http:", "https:" to indicate an external link eg something to open in
360your browser. If an internal link begins with something that looks like a scheme, precede with "document:". Empty hrefs should be allowed ie <a href="">&lt;a href=""&gt;test&lt;/a&gt;</a> should be allowed.
361<u>This text should be underlined.</u><br/>
362<strike>This text should have a strike through it.</strike><br/>
363<span backcolor="yellow"><strike>This text should have a strike through it and be highlighted.</strike></span><br/>
364<span backcolor="yellow"><strike><u>This text should have a strike through it and be highlighted and underlined.</u></strike></span><br/>
365This should be a mailto link <a href="mailto:reportlab-users@lists2.reportlab.com"><font color="blue">reportlab-users at lists2.reportlab.com</font></a>.<br/>
366This should be an underlined mailto link <a underline="1" href="mailto:reportlab-users@lists2.reportlab.com"><font color="blue">reportlab-users at lists2.reportlab.com</font></a>.<br/>
367This should be a highlighted mailto link <span backcolor="yellow"><a href="mailto:reportlab-users@lists2.reportlab.com"><font color="blue">reportlab-users at lists2.reportlab.com</font></a></span>.<br/>
368This should be a highlighted &amp; underlined mailto link <span backcolor="yellow"><a underline="1" ucolor="red" uwidth="0.01*F" href="mailto:reportlab-users@lists2.reportlab.com"><font color="blue">reportlab-users at lists2.reportlab.com</font></a></span>.<br/>
369<u offset="-.125*F">Underlined <font size="-1">Underlined</font></u><br/>
370This is A<sup><u>underlined</u></sup> as is A<u><sup>this</sup></u>
371<u color="red">This is A<sup><u>underlined</u></sup> as is A<u><sup>this</sup></u></u>
372'''
373        from reportlab.platypus.flowables import ImageAndFlowables, Image
374        from reportlab.lib.testutils import testsFolder
375        gif = os.path.join(testsFolder,'pythonpowered.gif')
376        heading = Paragraph('This is a heading',h3)
377        story.append(ImageAndFlowables(Image(gif),[heading,Paragraph(text,bt)]))
378        phrase = 'This should be a paragraph spanning at least three pages. '
379        description = ''.join([('%d: '%i)+phrase for i in range(250)])
380        story.append(ImageAndFlowables(Image(gif),[heading,Paragraph(description, bt)],imageSide='left'))
381        story.append(NextPageTemplate('special'))
382        story.append(PageBreak())
383        VERA = ('Vera','VeraBd','VeraIt','VeraBI')
384        for v in VERA:
385            registerFont(TTFont(v,v+'.ttf'))
386        registerFontFamily(*(VERA[:1]+VERA))
387        story.append(ImageAndFlowables(
388                        Image(gif,width=280,height=120),
389                        Paragraph('''<font name="Vera">The <b>concept</b> of an <i>integrated</i> one <b><i>box</i></b> solution for <i><b>advanced</b></i> voice and
390data applications began with the introduction of the IMACS. The
391IMACS 200 carries on that tradition with an integrated solution
392optimized for smaller port size applications that the IMACS could not
393economically address. An array of the most popular interfaces and
394features from the IMACS has been bundled into a small 2U chassis
395providing the ultimate in ease of installation.</font>''',
396                        style=st,
397                        ),
398                    imageSide='left',
399                    )
400                )
401        story.append(Paragraph('Width 240 single frag',h3))
402        story.append(ImageAndFlowables(
403                        Image(gif,width=240,height=120),
404                        Paragraph('''The concept of an integrated one box solution for advanced voice and
405data applications began with the introduction of the IMACS. The
406IMACS 200 carries on that tradition with an integrated solution
407optimized for smaller port size applications that the IMACS could not
408economically address. An array of the most popular interfaces and
409features from the IMACS has been bundled into a small 2U chassis
410providing the ultimate in ease of installation.''',
411                        style=st,
412                        ),
413                    imageSide='left',
414                    )
415                )
416
417        story.append(PageBreak())
418        story.append(Paragraph('Image larger than the frame',h3))
419        story.append(ImageAndFlowables(
420                        Image(gif,width=6*110,height=6*44),
421                        Paragraph('''The concept of an integrated one box solution for advanced voice and
422data applications began with the introduction of the IMACS. The
423IMACS 200 carries on that tradition with an integrated solution
424optimized for smaller port size applications that the IMACS could not
425economically address. An array of the most popular interfaces and
426features from the IMACS has been bundled into a small 2U chassis
427providing the ultimate in ease of installation.''',
428                        style=st,
429                        ),
430                    imageSide='left',
431                    )
432                )
433        text = '''With this clarification, an important property of these three types of
434EC can be defined in such a way as to impose problems of phonemic and
435morphological analysis.  Another superficial similarity is the interest
436in simulation of behavior, this analysis of a formative as a pair of
437sets of features does not readily tolerate a stipulation to place the
438constructions into these various categories.  We will bring evidence in
439favor of the following thesis:  the earlier discussion of deviance is
440not to be considered in determining the extended c-command discussed in
441connection with (34).  Another superficial similarity is the interest in
442simulation of behavior, relational information may remedy and, at the
443same time, eliminate a descriptive fact.  There is also a different
444approach to the [unification] problem, the descriptive power of the base
445component delimits the traditional practice of grammarians.'''
446        gif = os.path.join(testsFolder,'pythonpowered.gif')
447        heading = Paragraph('This is a heading',h3)
448        story.append(NextPageTemplate('template2'))
449        story.append(PageBreak())
450        story.append(heading)
451        story.append(ImageAndFlowables(Image(gif,width=66,height=81),[Paragraph(text,bt)],imageSide='left',imageRightPadding=10))
452
453        story.append(NextPageTemplate('special'))
454        story.append(PageBreak())
455        story.append(Paragraph('Width 240, multi-frag free hyphenation',h3))
456        story.append(ImageAndFlowables(
457                        Image(gif,width=240,height=120),
458                        Paragraph('''The concept of an <span color="red">integ</span><span color="green">rated</span> one box solution for advanced voice and
459data applications began with the introduction of the IMACS. The
460IMACS 200 carries on that tradition with an integrated solution
461<span color="pink">optimized</span> for smaller port size applications that the IMACS could not
462<span color="lightgreen">eco</span><span color="blue">nomically</span> address. An array of the most popular interfaces and
463features from the IMACS has been bundled into a small 2U chassis
464providing the ultimate in ease of installation.''',
465                        style=st,
466                        ),
467                    imageSide='left',
468                    )
469                )
470        story.append(PageBreak())
471        story.append(Paragraph('Width 240, multi-frag soft hyphenation',h3))
472        story.append(ImageAndFlowables(
473                        Image(gif,width=240,height=120),
474                        Paragraph(u'''The concept of an <span color="red">integ</span><span color="green">\xadrated</span> one box solution for advanced voice and
475data applica\xadtions began with the in\xadtroduction of the IMACS. The
476IMACS 200 carries on that tradition with an integrated solution
477<span color="pink">op\xadtimized</span> for smaller port size applications that the IMACS could not
478<span color="lightgreen">eco</span><span color="blue">\xadnomically</span> address. An array of the most popular interfaces and
479fea\xadtures from the IMACS has been bundled into a small 2U chassis
480providing the ultimate in ease of installation.''',
481                        style=st,
482                        ),
483                    imageSide='left',
484                    )
485                )
486        story.append(PageBreak())
487        story.append(Paragraph('Width 240, single-frag soft hyphenation',h3))
488        story.append(ImageAndFlowables(
489                        Image(gif,width=240,height=120),
490                        Paragraph(u'''The concept of an integ\xadrated one box solution for advanced voice and
491data applica\xadtions began with the in\xadtroduction of the IMACS. The
492IMACS 200 carries on that tradition with an integrated solution
493op\xadtimized for smaller port size applications that the IMACS could not
494eco\xadnomically address. An array of the most popular interfaces and
495fea\xadtures from the IMACS has been bundled into a small 2U chassis
496providing the ultimate in ease of installation.''',
497                        style=st,
498                        ),
499                    imageSide='left',
500                    )
501                )
502        doc = MyDocTemplate(outputfile('test_platypus_imageandflowables.pdf'),showBoundary=1)
503        doc.multiBuild(story)
504
505    @unittest.skipUnless(rtlSupport,'s')
506    def test1_RTL(self):
507        "ParagraphSplitTestCase.test_RTL"
508        from reportlab.platypus.flowables import ImageAndFlowables, Image
509        from reportlab.lib.testutils import testsFolder
510        from test_paragraphs import getAFont
511        # Build story.
512        fontName = getAFont()
513        story = []
514        styleSheet = getSampleStyleSheet()
515        h3 = styleSheet['Heading3']
516        bt = styleSheet['BodyText']
517        h3.wordWrap = bt.wordWrap = 'RTL'
518        h3.alignment = bt.alignment = TA_RIGHT
519        h3.fontName = bt.fontName = fontName
520        st=ParagraphStyle(
521                            name="base",
522                            fontName="Helvetica",
523                            leading=12,
524                            leftIndent=0,
525                            firstLineIndent=0,
526                            spaceBefore = 9.5,
527                            fontSize=9.5,
528                            wordWrap='RTL',
529                            alignment=TA_RIGHT,
530                            )
531        text=b'''\xd7\x94\xd7\xa0\xd7\x93\xd7\xa1\xd7\xaa \xd7\x90\xd7\x99\xd7\xa0\xd7\x98\xd7\xa8\xd7\xa0\xd7\x98 \xd7\xa2\xd7\x9c
532        \xd7\xa9\xd7\x9e\xd7\x95. \xd7\x91\xd7\x94 \xd7\xa2\xd7\x96\xd7\x94 \xd7\x90\xd7\x97\xd7\xa8\xd7\x95\xd7\xaa
533        \xd7\x9c\xd7\xa2\xd7\xa8\xd7\x99\xd7\x9b\xd7\xaa \xd7\x94\xd7\xa0\xd7\x90\xd7\x9e\xd7\xa0\xd7\x99\xd7\x9d,
534        \xd7\x94\xd7\xa7\xd7\x94\xd7\x99\xd7\x9c\xd7\x94 \xd7\x9e\xd7\x99\xd7\x95\xd7\x97\xd7\x93\xd7\x99\xd7\x9d
535        \xd7\x9e\xd7\x9e\xd7\x95\xd7\xa0\xd7\xa8\xd7\x9b\xd7\x99\xd7\x94 \xd7\x91 \xd7\x90\xd7\xaa\xd7\x94. \xd7\xa2\xd7\x9c
536        \xd7\xa6'\xd7\x98 \xd7\x9c\xd7\xa8\xd7\x90\xd7\x95\xd7\xaa \xd7\x9c\xd7\xa2\xd7\xaa\xd7\x99\xd7\x9d
537        \xd7\xa4\xd7\x99\xd7\x9c\xd7\x95\xd7\xa1\xd7\x95\xd7\xa4\xd7\x99\xd7\x94, \xd7\xa8\xd7\x91\xd7\x94
538        \xd7\x99\xd7\x95\xd7\xa0\xd7\x99 \xd7\x9e\xd7\x93\xd7\xa8\xd7\x99\xd7\x9b\xd7\x99\xd7\x9d \xd7\x90\xd7\x9d.
539        \xd7\x98\xd7\x99\xd7\xa4\xd7\x95\xd7\x9c \xd7\x91\xd7\x99\xd7\x93\xd7\x95\xd7\xa8
540        \xd7\x90\xd7\xa0\xd7\xa6\xd7\x99\xd7\xa7\xd7\x9c\xd7\x95\xd7\xa4\xd7\x93\xd7\x99\xd7\x94
541        \xd7\x90\xd7\x9c \xd7\xa9\xd7\x9e\xd7\x95, \xd7\x91\xd7\x9b\xd7\xa4\xd7\x95\xd7\xa3
542        \xd7\x94\xd7\x9e\xd7\xa7\xd7\x95\xd7\x91\xd7\x9c \xd7\x97\xd7\xa8\xd7\x98\xd7\x95\xd7\x9e\xd7\x99\xd7\x9d
543        \xd7\x96\xd7\x9b\xd7\xa8 \xd7\x90\xd7\x9c. \xd7\x91 \xd7\x9c\xd7\xa2\xd7\xaa\xd7\x99\xd7\x9d
544        \xd7\x95\xd7\x9e\xd7\x93\xd7\xa2\xd7\x99\xd7\x9d \xd7\x94\xd7\x90\xd7\x98\xd7\x9e\xd7\x95\xd7\xa1\xd7\xa4\xd7\x99\xd7\xa8\xd7\x94
545        \xd7\x91\xd7\x93\xd7\xa3, \xd7\xa2\xd7\x96\xd7\x94 \xd7\xa8\xd7\x99\xd7\xa7\xd7\x95\xd7\x93
546        \xd7\x91\xd7\x93\xd7\xa4\xd7\x99\xd7\x9d \xd7\x94\xd7\x9e\xd7\xa7\xd7\x95\xd7\x91\xd7\x9c \xd7\x90\xd7\xaa.
547        \xd7\xa9\xd7\x9e\xd7\x95 \xd7\x91 \xd7\x91\xd7\x9b\xd7\xa4\xd7\x95\xd7\xa3 \xd7\x91\xd7\xa9\xd7\xa4\xd7\x95\xd7\xaa
548        \xd7\x9e\xd7\x90\xd7\x9e\xd7\xa8\xd7\xa9\xd7\x99\xd7\x97\xd7\x94\xd7\xa6\xd7\xa4\xd7\x94, \xd7\x91\xd7\x93\xd7\xa3
549        \xd7\x9e\xd7\xa9\xd7\x95\xd7\xa4\xd7\xa8\xd7\x95\xd7\xaa \xd7\x9e\xd7\x95\xd7\xa0\xd7\x97\xd7\x95\xd7\xa0\xd7\x99\xd7\x9d
550        \xd7\x9e\xd7\x99\xd7\xaa\xd7\x95\xd7\x9c\xd7\x95\xd7\x92\xd7\x99\xd7\x94 \xd7\x90\xd7\x95, \xd7\x9e\xd7\xaa\xd7\x9f
551        \xd7\x90\xd7\x95 \xd7\x95\xd7\x94\xd7\xa0\xd7\x93\xd7\xa1\xd7\x94 \xd7\x91\xd7\x9c\xd7\xa9\xd7\xa0\xd7\x95\xd7\xaa.
552        \xd7\xa6\xd7\xa2\xd7\x93 \xd7\x94\xd7\x97\xd7\x9c\xd7\x9c \xd7\xa7\xd7\x95\xd7\x93\xd7\x9e\xd7\x95\xd7\xaa
553        \xd7\xa8\xd7\x91\xd6\xbe\xd7\x9c\xd7\xa9\xd7\x95\xd7\xa0\xd7\x99 \xd7\x91, \xd7\x90\xd7\x9c
554        \xd7\x91\xd7\x99\xd7\xa9\xd7\x95\xd7\x9c \xd7\x94\xd7\xa0\xd7\x90\xd7\x9e\xd7\xa0\xd7\x99\xd7\x9d
555        \xd7\xa2\xd7\x95\xd7\x93, \xd7\x9e\xd7\xaa\xd7\x9f \xd7\x9e\xd7\x94 \xd7\x9e\xd7\xaa\xd7\x95\xd7\x9a
556        \xd7\xa9\xd7\x95\xd7\xa0\xd7\x94 \xd7\x94\xd7\xa2\xd7\x99\xd7\xa8. \xd7\x91\xd7\xa7\xd7\xa8
557        \xd7\xa2\xd7\xa1\xd7\xa7\xd7\x99\xd7\x9d \xd7\x97\xd7\x99\xd7\xa0\xd7\x95\xd7\x9a \xd7\xaa\xd7\x90\xd7\x95\xd7\x9c\xd7\x95\xd7\x92\xd7\x99\xd7\x94
558        \xd7\x91, \xd7\x90\xd7\x99\xd7\x98\xd7\x9c\xd7\x99\xd7\x94 \xd7\xa0\xd7\x95\xd7\xa1\xd7\x97\xd7\x90\xd7\x95\xd7\xaa
559        \xd7\x9c\xd7\x9e\xd7\x90\xd7\x9e\xd7\xa8\xd7\x99\xd7\x9d \xd7\x90\xd7\xaa \xd7\x9b\xd7\xaa\xd7\x91. \xd7\x90\xd7\x95
560        \xd7\xa4\xd7\xa0\xd7\x90\xd7\x99 \xd7\x9c\xd7\x98\xd7\x99\xd7\xa4\xd7\x95\xd7\x9c \xd7\xa2\xd7\x95\xd7\x93, \xd7\x91\xd7\x94
561        \xd7\x9e\xd7\xaa\xd7\x9f \xd7\xa7\xd7\xa1\xd7\x90\xd7\x9d \xd7\x91\xd7\xa8\xd7\x95\xd7\x9b\xd7\x99\xd7\x9d
562        \xd7\x98\xd7\x91\xd7\x9c\xd7\x90\xd7\x95\xd7\xaa. \xd7\xa6'\xd7\x98 \xd7\xa9\xd7\x9c \xd7\x95\xd7\x99\xd7\xa7\xd7\x99
563        \xd7\xa7\xd7\xa1\xd7\x90\xd7\x9d. \xd7\xa9\xd7\xa2\xd7\xa8 \xd7\x9e\xd7\x94 \xd7\x9c\xd7\xa2\xd7\xa8\xd7\x95\xd7\x9a
564        \xd7\x92\xd7\x99\xd7\x90\xd7\x95\xd7\x92\xd7\xa8\xd7\xa4\xd7\x99\xd7\x94, \xd7\xa1\xd7\x93\xd7\xa8 \xd7\x90\xd7\xaa
565        \xd7\x9b\xd7\x9c\xd7\x99\xd7\x9d \xd7\x91\xd7\x92\xd7\xa8\xd7\xa1\xd7\x94 \xd7\xa1\xd7\xa4\xd7\x99\xd7\xa0\xd7\x95\xd7\xaa,
566        \xd7\xa8\xd7\xa7\xd7\x98\xd7\x95\xd7\xaa \xd7\xaa\xd7\x99\xd7\xa7\xd7\x95\xd7\xa0\xd7\x99\xd7\x9d \xd7\x90\xd7\x9d
567        \xd7\x96\xd7\x90\xd7\xaa. \xd7\x9e\xd7\x94 \xd7\x91\xd7\x94\xd7\x91\xd7\xa0\xd7\x94 \xd7\x94\xd7\xa2\xd7\x9e\xd7\x95\xd7\x93
568        \xd7\xa9\xd7\xaa\xd7\x99, \xd7\xa2\xd7\x9e\xd7\x95\xd7\x93 \xd7\xaa\xd7\xa8\xd7\x95\xd7\x9e\xd7\x94
569        \xd7\x9e\xd7\x99\xd7\x96\xd7\x9e\xd7\x99\xd7\x9d \xd7\x97\xd7\xa4\xd7\xa9 \xd7\x91. \xd7\x90\xd7\x9d
570        \xd7\x90\xd7\xaa\xd7\x94 \xd7\x94\xd7\xa2\xd7\x91\xd7\xa8\xd7\x99\xd7\xaa \xd7\xa0\xd7\x95\xd7\xa1\xd7\x97\xd7\x90\xd7\x95\xd7\xaa
571        \xd7\x94\xd7\x9e\xd7\xa7\xd7\x95\xd7\xa9\xd7\xa8\xd7\x99\xd7\x9d. \xd7\xa6\xd7\x99\xd7\x95\xd7\xa8 \xd7\x91\xd7\xa8\xd7\x99\xd7\xaa
572        \xd7\x90\xd7\xa0\xd7\xa6\xd7\x99\xd7\xa7\xd7\x9c\xd7\x95\xd7\xa4\xd7\x93\xd7\x99\xd7\x94 \xd7\xa9\xd7\x9b\xd7\x9c \xd7\xa2\xd7\x9c.
573        \xd7\xa6'\xd7\x98 \xd7\x90\xd7\x9c \xd7\x9b\xd7\x9c\xd7\xa9\xd7\x94\xd7\x95 \xd7\x91\xd7\xa8\xd7\x95\xd7\x9b\xd7\x99\xd7\x9d
574        \xd7\x94\xd7\x9e\xd7\x93\xd7\x99\xd7\xa0\xd7\x94, \xd7\x9b\xd7\x9c\xd7\x99\xd7\x9d \xd7\xa7\xd7\xa1\xd7\x90\xd7\x9d
575        \xd7\xa7\xd7\x9c\xd7\x90\xd7\xa1\xd7\x99\xd7\x99\xd7\x9d
576        \xd7\xa2\xd7\x9c \xd7\xa9\xd7\xa2\xd7\xa8, \xd7\x90\xd7\x95 \xd7\xa7\xd7\xa8\xd7\x9f \xd7\x9c\xd7\x9b\xd7\x90\xd7\x9f
577        \xd7\xa8\xd7\x91\xd6\xbe\xd7\x9c\xd7\xa9\xd7\x95\xd7\xa0\xd7\x99. \xd7\x90\xd7\xaa\xd7\x94 \xd7\xa9\xd7\xa0\xd7\x95\xd7\xa8\xd7\x95
578        \xd7\x95\xd7\x91\xd7\x9e\xd7\xaa\xd7\x9f \xd7\x91\xd7\x9c\xd7\xa9\xd7\xa0\xd7\x95\xd7\xaa \xd7\x90\xd7\x9c, \xd7\xa9\xd7\x9c
579        \xd7\x94\xd7\xa8\xd7\x95\xd7\x97 \xd7\x91\xd7\x9b\xd7\xa4\xd7\x95\xd7\xa3 \xd7\x9e\xd7\xa9\xd7\x95\xd7\xa4\xd7\xa8\xd7\x95\xd7\xaa
580        \xd7\xa6'\xd7\x98. \xd7\x90\xd7\xaa \xd7\xaa\xd7\x95\xd7\xa8\xd7\xaa \xd7\x9c\xd7\x9e\xd7\xaa\xd7\x97\xd7\x99\xd7\x9c\xd7\x99\xd7\x9d
581        \xd7\xa9\xd7\x9b\xd7\x9c, \xd7\x94\xd7\x99\xd7\x90 \xd7\x90\xd7\x95 \xd7\xa9\xd7\xa0\xd7\x95\xd7\xa8\xd7\x95
582        \xd7\xa1\xd7\xa4\xd7\xa8\xd7\x95\xd7\xaa. \xd7\x9e\xd7\x99\xd7\x96\xd7\x9e\xd7\x99\xd7\x9d \xd7\x9c\xd7\x97\xd7\x99\xd7\x91\xd7\x95\xd7\xa8
583        \xd7\x90\xd7\xa0\xd7\x92\xd7\x9c\xd7\x99\xd7\xaa \xd7\x9e\xd7\x94 \xd7\xa6'\xd7\x98, \xd7\x90\xd7\xa0\xd7\x90
584        \xd7\xaa\xd7\x95\xd7\x9b\xd7\x9c \xd7\x9e\xd7\x95\xd7\xa2\xd7\x9e\xd7\x93\xd7\x99\xd7\x9d
585        \xd7\x94\xd7\x9e\xd7\xa7\xd7\x95\xd7\xa9\xd7\xa8\xd7\x99\xd7\x9d \xd7\xa2\xd7\x9c, \xd7\x93\xd7\xaa
586        \xd7\x94\xd7\x97\xd7\x95\xd7\x9c \xd7\xa2\xd7\xa8\xd7\x91\xd7\x99\xd7\xaa \xd7\x94\xd7\x9e\xd7\xa9\xd7\xa4\xd7\x98
587        \xd7\x91\xd7\x93\xd7\xa3. \xd7\x94\xd7\x90\xd7\xa8\xd7\xa5 \xd7\x91\xd7\x99\xd7\x95\xd7\xa0\xd7\x99
588        \xd7\x94\xd7\xa1\xd7\x91\xd7\x99\xd7\x91\xd7\x94 \xd7\x93\xd7\xaa \xd7\xa8\xd7\x91\xd7\x94, \xd7\xa2\xd7\x96\xd7\x94
589        \xd7\xa9\xd7\xa0\xd7\x95\xd7\xa8\xd7\x95 \xd7\x95\xd7\x90\xd7\x9c\xd7\xa7\xd7\x98\xd7\xa8\xd7\x95\xd7\xa0\xd7\x99\xd7\xa7\xd7\x94
590        \xd7\x90\xd7\xaa. \xd7\x96\xd7\xa7\xd7\x95\xd7\xa7 \xd7\x9e\xd7\x91\xd7\x95\xd7\xa7\xd7\xa9\xd7\x99\xd7\x9d
591        \xd7\x9b\xd7\xaa\xd7\x91 \xd7\x90\xd7\xaa. \xd7\x99\xd7\x95\xd7\xa0\xd7\x99
592        \xd7\xa4\xd7\x95\xd7\x9c\xd7\x99\xd7\x98\xd7\x99\xd7\xa7\xd7\x94 \xd7\x91\xd7\x94 \xd7\x9b\xd7\xaa\xd7\x91,
593        \xd7\x9c\xd7\x95\xd7\x97 \xd7\xa9\xd7\x9c \xd7\x94\xd7\x97\xd7\x95\xd7\xa4\xd7\xa9\xd7\x99\xd7\xaa
594        \xd7\x90\xd7\xaa\xd7\xa0\xd7\x95\xd7\x9c\xd7\x95\xd7\x92\xd7\x99\xd7\x94. \xd7\x91 \xd7\x96\xd7\x9b\xd7\xa8
595        \xd7\x94\xd7\xa1\xd7\x91\xd7\x99\xd7\x91\xd7\x94 \xd7\x9e\xd7\x93\xd7\x99\xd7\xa0\xd7\x95\xd7\xaa
596        \xd7\x9c\xd7\x99\xd7\xa6\xd7\x99\xd7\xa8\xd7\xaa\xd7\x94. \xd7\x96\xd7\x9b\xd7\xa8
597        \xd7\x91\xd7\x99\xd7\x95\xd7\x9c\xd7\x95\xd7\x92\xd7\x99\xd7\x94
598        \xd7\x95\xd7\x94\xd7\x92\xd7\x95\xd7\x9c\xd7\xa9\xd7\x99\xd7\x9d \xd7\x93\xd7\xaa, \xd7\x90\xd7\x97\xd7\x93
599        \xd7\xa2\xd7\x9c \xd7\x92\xd7\xa8\xd7\x9e\xd7\xa0\xd7\x99\xd7\xaa \xd7\x94\xd7\xa1\xd7\xa4\xd7\xa8\xd7\x95\xd7\xaa.
600        \xd7\xa1\xd7\x93\xd7\xa8 \xd7\x9e\xd7\x94 \xd7\x96\xd7\xa7\xd7\x95\xd7\xa7 \xd7\xa2\xd7\x99\xd7\xa6\xd7\x95\xd7\x91.
601        \xd7\xa9\xd7\xaa\xd7\x99 \xd7\xa9\xd7\x9c \xd7\xaa\xd7\xa8\xd7\x95\xd7\x9e\xd7\x94 \xd7\xa7\xd7\x94\xd7\x99\xd7\x9c\xd7\x94,
602        \xd7\xa9\xd7\x9c \xd7\x9e\xd7\x90\xd7\x9e\xd7\xa8 \xd7\x94\xd7\x90\xd7\x98\xd7\x9e\xd7\x95\xd7\xa1\xd7\xa4\xd7\x99\xd7\xa8\xd7\x94
603        \xd7\xa9\xd7\xa2\xd7\xa8, \xd7\x9e\xd7\x94 \xd7\xa9\xd7\xaa\xd7\x99 \xd7\x99\xd7\x99\xd7\x93\xd7\x99\xd7\xa9
604        \xd7\x94\xd7\x9e\xd7\x9c\xd7\x97\xd7\x9e\xd7\x94 \xd7\x98\xd7\x9b\xd7\xa0\xd7\x99\xd7\x99\xd7\x9d.
605        \xd7\x91\xd7\xa9\xd7\xa4\xd7\x95\xd7\xaa \xd7\x9c\xd7\x9e\xd7\x97\xd7\x99\xd7\xa7\xd7\x94 \xd7\x9e\xd7\x94
606        \xd7\x97\xd7\xa4\xd7\xa9, \xd7\x9e\xd7\x94 \xd7\xa2\xd7\x95\xd7\x93 \xd7\x90\xd7\xa7\xd7\xa8\xd7\x90\xd7\x99
607        \xd7\x9c\xd7\x98\xd7\x99\xd7\xa4\xd7\x95\xd7\x9c \xd7\x91\xd7\x95\xd7\x99\xd7\xa7\xd7\x99\xd7\xa4\xd7\x93\xd7\x99\xd7\x94,
608        \xd7\x90\xd7\x95 \xd7\x9e\xd7\xa4\xd7\xaa\xd7\x97 \xd7\x9c\xd7\x99\xd7\x9e\xd7\x95\xd7\x93\xd7\x99\xd7\x9d
609        \xd7\x95\xd7\x99\xd7\xa7\xd7\x99\xd7\xa4\xd7\x93\xd7\x99\xd7\x94 \xd7\xa9\xd7\x9b\xd7\x9c.
610        \xd7\x93\xd7\xa8\xd7\x9b\xd7\x94 \xd7\xa9\xd7\x99\xd7\xaa\xd7\x95\xd7\xa4\xd7\x99\xd7\xaa
611        \xd7\x90\xd7\xaa\xd7\x94 \xd7\x93\xd7\xaa.'''
612
613        text1=b'''\xd7\x94\xd7\xa0\xd7\x93\xd7\xa1\xd7\xaa \xd7\x90\xd7\x99\xd7\xa0\xd7\x98\xd7\xa8\xd7\xa0\xd7\x98 English \xd7\xa2\xd7\x9c
614        \xd7\xa9\xd7\x9e\xd7\x95. \xd7\x91\xd7\x94 \xd7\xa2\xd7\x96\xd7\x94 \xd7\x90\xd7\x97\xd7\xa8\xd7\x95\xd7\xaa
615        \xd7\x9c\xd7\xa2\xd7\xa8\xd7\x99\xd7\x9b\xd7\xaa \xd7\x94\xd7\xa0\xd7\x90\xd7\x9e\xd7\xa0\xd7\x99\xd7\x9d,
616        \xd7\x94\xd7\xa7\xd7\x94\xd7\x99\xd7\x9c\xd7\x94 \xd7\x9e\xd7\x99\xd7\x95\xd7\x97\xd7\x93\xd7\x99\xd7\x9d
617        \xd7\x9e\xd7\x9e\xd7\x95\xd7\xa0\xd7\xa8\xd7\x9b\xd7\x99\xd7\x94 \xd7\x91 \xd7\x90\xd7\xaa\xd7\x94. \xd7\xa2\xd7\x9c
618        \xd7\xa6'\xd7\x98 \xd7\x9c\xd7\xa8\xd7\x90\xd7\x95\xd7\xaa \xd7\x9c\xd7\xa2\xd7\xaa\xd7\x99\xd7\x9d
619        \xd7\xa4\xd7\x99\xd7\x9c\xd7\x95\xd7\xa1\xd7\x95\xd7\xa4\xd7\x99\xd7\x94, \xd7\xa8\xd7\x91\xd7\x94
620        \xd7\x99\xd7\x95\xd7\xa0\xd7\x99 \xd7\x9e\xd7\x93\xd7\xa8\xd7\x99\xd7\x9b\xd7\x99\xd7\x9d \xd7\x90\xd7\x9d.
621        \xd7\x98\xd7\x99\xd7\xa4\xd7\x95\xd7\x9c \xd7\x91\xd7\x99\xd7\x93\xd7\x95\xd7\xa8
622        \xd7\x90\xd7\xa0\xd7\xa6\xd7\x99\xd7\xa7\xd7\x9c\xd7\x95\xd7\xa4\xd7\x93\xd7\x99\xd7\x94
623        \xd7\x90\xd7\x9c \xd7\xa9\xd7\x9e\xd7\x95, \xd7\x91\xd7\x9b\xd7\xa4\xd7\x95\xd7\xa3
624        \xd7\x94\xd7\x9e\xd7\xa7\xd7\x95\xd7\x91\xd7\x9c \xd7\x97\xd7\xa8\xd7\x98\xd7\x95\xd7\x9e\xd7\x99\xd7\x9d
625        \xd7\x96\xd7\x9b\xd7\xa8 \xd7\x90\xd7\x9c. \xd7\x91 \xd7\x9c\xd7\xa2\xd7\xaa\xd7\x99\xd7\x9d
626        \xd7\x95\xd7\x9e\xd7\x93\xd7\xa2\xd7\x99\xd7\x9d \xd7\x94\xd7\x90\xd7\x98\xd7\x9e\xd7\x95\xd7\xa1\xd7\xa4\xd7\x99\xd7\xa8\xd7\x94
627        \xd7\x91\xd7\x93\xd7\xa3, English \xd7\xa2\xd7\x96\xd7\x94 \xd7\xa8\xd7\x99\xd7\xa7\xd7\x95\xd7\x93
628        \xd7\x91\xd7\x93\xd7\xa4\xd7\x99\xd7\x9d \xd7\x94\xd7\x9e\xd7\xa7\xd7\x95\xd7\x91\xd7\x9c \xd7\x90\xd7\xaa.
629        \xd7\xa9\xd7\x9e\xd7\x95 \xd7\x91 \xd7\x91\xd7\x9b\xd7\xa4\xd7\x95\xd7\xa3 \xd7\x91\xd7\xa9\xd7\xa4\xd7\x95\xd7\xaa
630        \xd7\x9e\xd7\x90\xd7\x9e\xd7\xa8\xd7\xa9\xd7\x99\xd7\x97\xd7\x94\xd7\xa6\xd7\xa4\xd7\x94, \xd7\x91\xd7\x93\xd7\xa3
631        \xd7\x9e\xd7\xa9\xd7\x95\xd7\xa4\xd7\xa8\xd7\x95\xd7\xaa \xd7\x9e\xd7\x95\xd7\xa0\xd7\x97\xd7\x95\xd7\xa0\xd7\x99\xd7\x9d
632        \xd7\x9e\xd7\x99\xd7\xaa\xd7\x95\xd7\x9c\xd7\x95\xd7\x92\xd7\x99\xd7\x94 \xd7\x90\xd7\x95, \xd7\x9e\xd7\xaa\xd7\x9f
633        \xd7\x90\xd7\x95 \xd7\x95\xd7\x94\xd7\xa0\xd7\x93\xd7\xa1\xd7\x94 \xd7\x91\xd7\x9c\xd7\xa9\xd7\xa0\xd7\x95\xd7\xaa.
634        \xd7\xa6\xd7\xa2\xd7\x93 \xd7\x94\xd7\x97\xd7\x9c\xd7\x9c \xd7\xa7\xd7\x95\xd7\x93\xd7\x9e\xd7\x95\xd7\xaa
635        \xd7\xa8\xd7\x91\xd6\xbe\xd7\x9c\xd7\xa9\xd7\x95\xd7\xa0\xd7\x99 \xd7\x91, \xd7\x90\xd7\x9c
636        \xd7\x91\xd7\x99\xd7\xa9\xd7\x95\xd7\x9c \xd7\x94\xd7\xa0\xd7\x90\xd7\x9e\xd7\xa0\xd7\x99\xd7\x9d
637        \xd7\xa2\xd7\x95\xd7\x93, \xd7\x9e\xd7\xaa\xd7\x9f \xd7\x9e\xd7\x94 \xd7\x9e\xd7\xaa\xd7\x95\xd7\x9a
638        \xd7\xa9\xd7\x95\xd7\xa0\xd7\x94 \xd7\x94\xd7\xa2\xd7\x99\xd7\xa8. \xd7\x91\xd7\xa7\xd7\xa8
639        \xd7\xa2\xd7\xa1\xd7\xa7\xd7\x99\xd7\x9d \xd7\x97\xd7\x99\xd7\xa0\xd7\x95\xd7\x9a \xd7\xaa\xd7\x90\xd7\x95\xd7\x9c\xd7\x95\xd7\x92\xd7\x99\xd7\x94
640        \xd7\x91, \xd7\x90\xd7\x99\xd7\x98\xd7\x9c\xd7\x99\xd7\x94 \xd7\xa0\xd7\x95\xd7\xa1\xd7\x97\xd7\x90\xd7\x95\xd7\xaa
641        \xd7\x9c\xd7\x9e\xd7\x90\xd7\x9e\xd7\xa8\xd7\x99\xd7\x9d \xd7\x90\xd7\xaa \xd7\x9b\xd7\xaa\xd7\x91. \xd7\x90\xd7\x95
642        \xd7\xa4\xd7\xa0\xd7\x90\xd7\x99 \xd7\x9c\xd7\x98\xd7\x99\xd7\xa4\xd7\x95\xd7\x9c \xd7\xa2\xd7\x95\xd7\x93, \xd7\x91\xd7\x94
643        \xd7\x9e\xd7\xaa\xd7\x9f \xd7\xa7\xd7\xa1\xd7\x90\xd7\x9d \xd7\x91\xd7\xa8\xd7\x95\xd7\x9b\xd7\x99\xd7\x9d
644        \xd7\x98\xd7\x91\xd7\x9c\xd7\x90\xd7\x95\xd7\xaa. \xd7\xa6'\xd7\x98 \xd7\xa9\xd7\x9c \xd7\x95\xd7\x99\xd7\xa7\xd7\x99
645        \xd7\xa7\xd7\xa1\xd7\x90\xd7\x9d. \xd7\xa9\xd7\xa2\xd7\xa8 \xd7\x9e\xd7\x94 \xd7\x9c\xd7\xa2\xd7\xa8\xd7\x95\xd7\x9a
646        \xd7\x92\xd7\x99\xd7\x90\xd7\x95\xd7\x92\xd7\xa8\xd7\xa4\xd7\x99\xd7\x94, \xd7\xa1\xd7\x93\xd7\xa8 \xd7\x90\xd7\xaa
647        \xd7\x9b\xd7\x9c\xd7\x99\xd7\x9d \xd7\x91\xd7\x92\xd7\xa8\xd7\xa1\xd7\x94 \xd7\xa1\xd7\xa4\xd7\x99\xd7\xa0\xd7\x95\xd7\xaa,
648        \xd7\xa8\xd7\xa7\xd7\x98\xd7\x95\xd7\xaa \xd7\xaa\xd7\x99\xd7\xa7\xd7\x95\xd7\xa0\xd7\x99\xd7\x9d \xd7\x90\xd7\x9d
649        \xd7\x96\xd7\x90\xd7\xaa. \xd7\x9e\xd7\x94 \xd7\x91\xd7\x94\xd7\x91\xd7\xa0\xd7\x94 \xd7\x94\xd7\xa2\xd7\x9e\xd7\x95\xd7\x93
650        \xd7\xa9\xd7\xaa\xd7\x99, \xd7\xa2\xd7\x9e\xd7\x95\xd7\x93 \xd7\xaa\xd7\xa8\xd7\x95\xd7\x9e\xd7\x94
651        \xd7\x9e\xd7\x99\xd7\x96\xd7\x9e\xd7\x99\xd7\x9d \xd7\x97\xd7\xa4\xd7\xa9 \xd7\x91. \xd7\x90\xd7\x9d
652        \xd7\x90\xd7\xaa\xd7\x94 \xd7\x94\xd7\xa2\xd7\x91\xd7\xa8\xd7\x99\xd7\xaa \xd7\xa0\xd7\x95\xd7\xa1\xd7\x97\xd7\x90\xd7\x95\xd7\xaa
653        \xd7\x94\xd7\x9e\xd7\xa7\xd7\x95\xd7\xa9\xd7\xa8\xd7\x99\xd7\x9d. \xd7\xa6\xd7\x99\xd7\x95\xd7\xa8 \xd7\x91\xd7\xa8\xd7\x99\xd7\xaa
654        \xd7\x90\xd7\xa0\xd7\xa6\xd7\x99\xd7\xa7\xd7\x9c\xd7\x95\xd7\xa4\xd7\x93\xd7\x99\xd7\x94 \xd7\xa9\xd7\x9b\xd7\x9c \xd7\xa2\xd7\x9c.
655        \xd7\xa6'\xd7\x98 \xd7\x90\xd7\x9c \xd7\x9b\xd7\x9c\xd7\xa9\xd7\x94\xd7\x95 \xd7\x91\xd7\xa8\xd7\x95\xd7\x9b\xd7\x99\xd7\x9d
656        \xd7\x94\xd7\x9e\xd7\x93\xd7\x99\xd7\xa0\xd7\x94, \xd7\x9b\xd7\x9c\xd7\x99\xd7\x9d \xd7\xa7\xd7\xa1\xd7\x90\xd7\x9d
657        \xd7\xa7\xd7\x9c\xd7\x90\xd7\xa1\xd7\x99\xd7\x99\xd7\x9d
658        \xd7\xa2\xd7\x9c \xd7\xa9\xd7\xa2\xd7\xa8, \xd7\x90\xd7\x95 \xd7\xa7\xd7\xa8\xd7\x9f \xd7\x9c\xd7\x9b\xd7\x90\xd7\x9f
659        \xd7\xa8\xd7\x91\xd6\xbe\xd7\x9c\xd7\xa9\xd7\x95\xd7\xa0\xd7\x99. \xd7\x90\xd7\xaa\xd7\x94 \xd7\xa9\xd7\xa0\xd7\x95\xd7\xa8\xd7\x95
660        \xd7\x95\xd7\x91\xd7\x9e\xd7\xaa\xd7\x9f \xd7\x91\xd7\x9c\xd7\xa9\xd7\xa0\xd7\x95\xd7\xaa \xd7\x90\xd7\x9c, \xd7\xa9\xd7\x9c
661        \xd7\x94\xd7\xa8\xd7\x95\xd7\x97 \xd7\x91\xd7\x9b\xd7\xa4\xd7\x95\xd7\xa3 \xd7\x9e\xd7\xa9\xd7\x95\xd7\xa4\xd7\xa8\xd7\x95\xd7\xaa
662        \xd7\xa6'\xd7\x98. \xd7\x90\xd7\xaa \xd7\xaa\xd7\x95\xd7\xa8\xd7\xaa \xd7\x9c\xd7\x9e\xd7\xaa\xd7\x97\xd7\x99\xd7\x9c\xd7\x99\xd7\x9d
663        \xd7\xa9\xd7\x9b\xd7\x9c, \xd7\x94\xd7\x99\xd7\x90 \xd7\x90\xd7\x95 \xd7\xa9\xd7\xa0\xd7\x95\xd7\xa8\xd7\x95
664        \xd7\xa1\xd7\xa4\xd7\xa8\xd7\x95\xd7\xaa. \xd7\x9e\xd7\x99\xd7\x96\xd7\x9e\xd7\x99\xd7\x9d \xd7\x9c\xd7\x97\xd7\x99\xd7\x91\xd7\x95\xd7\xa8
665        \xd7\x90\xd7\xa0\xd7\x92\xd7\x9c\xd7\x99\xd7\xaa \xd7\x9e\xd7\x94 \xd7\xa6'\xd7\x98, \xd7\x90\xd7\xa0\xd7\x90
666        \xd7\xaa\xd7\x95\xd7\x9b\xd7\x9c \xd7\x9e\xd7\x95\xd7\xa2\xd7\x9e\xd7\x93\xd7\x99\xd7\x9d
667        \xd7\x94\xd7\x9e\xd7\xa7\xd7\x95\xd7\xa9\xd7\xa8\xd7\x99\xd7\x9d \xd7\xa2\xd7\x9c, \xd7\x93\xd7\xaa
668        \xd7\x94\xd7\x97\xd7\x95\xd7\x9c \xd7\xa2\xd7\xa8\xd7\x91\xd7\x99\xd7\xaa \xd7\x94\xd7\x9e\xd7\xa9\xd7\xa4\xd7\x98
669        \xd7\x91\xd7\x93\xd7\xa3. \xd7\x94\xd7\x90\xd7\xa8\xd7\xa5 \xd7\x91\xd7\x99\xd7\x95\xd7\xa0\xd7\x99
670        \xd7\x94\xd7\xa1\xd7\x91\xd7\x99\xd7\x91\xd7\x94 \xd7\x93\xd7\xaa \xd7\xa8\xd7\x91\xd7\x94, \xd7\xa2\xd7\x96\xd7\x94
671        \xd7\xa9\xd7\xa0\xd7\x95\xd7\xa8\xd7\x95 \xd7\x95\xd7\x90\xd7\x9c\xd7\xa7\xd7\x98\xd7\xa8\xd7\x95\xd7\xa0\xd7\x99\xd7\xa7\xd7\x94
672        \xd7\x90\xd7\xaa. \xd7\x96\xd7\xa7\xd7\x95\xd7\xa7 \xd7\x9e\xd7\x91\xd7\x95\xd7\xa7\xd7\xa9\xd7\x99\xd7\x9d
673        \xd7\x9b\xd7\xaa\xd7\x91 \xd7\x90\xd7\xaa. \xd7\x99\xd7\x95\xd7\xa0\xd7\x99
674        \xd7\xa4\xd7\x95\xd7\x9c\xd7\x99\xd7\x98\xd7\x99\xd7\xa7\xd7\x94 \xd7\x91\xd7\x94 \xd7\x9b\xd7\xaa\xd7\x91,
675        \xd7\x9c\xd7\x95\xd7\x97 \xd7\xa9\xd7\x9c \xd7\x94\xd7\x97\xd7\x95\xd7\xa4\xd7\xa9\xd7\x99\xd7\xaa
676        \xd7\x90\xd7\xaa\xd7\xa0\xd7\x95\xd7\x9c\xd7\x95\xd7\x92\xd7\x99\xd7\x94. \xd7\x91 \xd7\x96\xd7\x9b\xd7\xa8
677        \xd7\x94\xd7\xa1\xd7\x91\xd7\x99\xd7\x91\xd7\x94 \xd7\x9e\xd7\x93\xd7\x99\xd7\xa0\xd7\x95\xd7\xaa
678        \xd7\x9c\xd7\x99\xd7\xa6\xd7\x99\xd7\xa8\xd7\xaa\xd7\x94. \xd7\x96\xd7\x9b\xd7\xa8
679        \xd7\x91\xd7\x99\xd7\x95\xd7\x9c\xd7\x95\xd7\x92\xd7\x99\xd7\x94
680        \xd7\x95\xd7\x94\xd7\x92\xd7\x95\xd7\x9c\xd7\xa9\xd7\x99\xd7\x9d \xd7\x93\xd7\xaa, \xd7\x90\xd7\x97\xd7\x93
681        \xd7\xa2\xd7\x9c \xd7\x92\xd7\xa8\xd7\x9e\xd7\xa0\xd7\x99\xd7\xaa \xd7\x94\xd7\xa1\xd7\xa4\xd7\xa8\xd7\x95\xd7\xaa.
682        \xd7\xa1\xd7\x93\xd7\xa8 \xd7\x9e\xd7\x94 \xd7\x96\xd7\xa7\xd7\x95\xd7\xa7 \xd7\xa2\xd7\x99\xd7\xa6\xd7\x95\xd7\x91.
683        \xd7\xa9\xd7\xaa\xd7\x99 \xd7\xa9\xd7\x9c \xd7\xaa\xd7\xa8\xd7\x95\xd7\x9e\xd7\x94 \xd7\xa7\xd7\x94\xd7\x99\xd7\x9c\xd7\x94,
684        \xd7\xa9\xd7\x9c \xd7\x9e\xd7\x90\xd7\x9e\xd7\xa8 \xd7\x94\xd7\x90\xd7\x98\xd7\x9e\xd7\x95\xd7\xa1\xd7\xa4\xd7\x99\xd7\xa8\xd7\x94
685        \xd7\xa9\xd7\xa2\xd7\xa8, \xd7\x9e\xd7\x94 \xd7\xa9\xd7\xaa\xd7\x99 \xd7\x99\xd7\x99\xd7\x93\xd7\x99\xd7\xa9
686        \xd7\x94\xd7\x9e\xd7\x9c\xd7\x97\xd7\x9e\xd7\x94 \xd7\x98\xd7\x9b\xd7\xa0\xd7\x99\xd7\x99\xd7\x9d.
687        \xd7\x91\xd7\xa9\xd7\xa4\xd7\x95\xd7\xaa \xd7\x9c\xd7\x9e\xd7\x97\xd7\x99\xd7\xa7\xd7\x94 \xd7\x9e\xd7\x94
688        \xd7\x97\xd7\xa4\xd7\xa9, \xd7\x9e\xd7\x94 \xd7\xa2\xd7\x95\xd7\x93 \xd7\x90\xd7\xa7\xd7\xa8\xd7\x90\xd7\x99
689        \xd7\x9c\xd7\x98\xd7\x99\xd7\xa4\xd7\x95\xd7\x9c \xd7\x91\xd7\x95\xd7\x99\xd7\xa7\xd7\x99\xd7\xa4\xd7\x93\xd7\x99\xd7\x94,
690        \xd7\x90\xd7\x95 \xd7\x9e\xd7\xa4\xd7\xaa\xd7\x97 \xd7\x9c\xd7\x99\xd7\x9e\xd7\x95\xd7\x93\xd7\x99\xd7\x9d
691        \xd7\x95\xd7\x99\xd7\xa7\xd7\x99\xd7\xa4\xd7\x93\xd7\x99\xd7\x94 \xd7\xa9\xd7\x9b\xd7\x9c.
692        \xd7\x93\xd7\xa8\xd7\x9b\xd7\x94 \xd7\xa9\xd7\x99\xd7\xaa\xd7\x95\xd7\xa4\xd7\x99\xd7\xaa
693        \xd7\x90\xd7\xaa\xd7\x94 \xd7\x93\xd7\xaa.'''
694        gif = os.path.join(testsFolder,'pythonpowered.gif')
695        heading = Paragraph(b'\xd7\x96\xd7\x95\xd7\x94\xd7\x99 \xd7\x9b\xd7\x95\xd7\xaa\xd7\xa8\xd7\xaa',h3)
696        story.append(ImageAndFlowables(Image(gif),[heading,Paragraph(text,bt)]))
697        heading = Paragraph(b'\xd7\x96\xd7\x95\xd7\x94\xd7\x99 \xd7\x9b\xd7\x95\xd7\xaa\xd7\xa8\xd7\xaa',h3)
698        story.append(ImageAndFlowables(Image(gif),[heading,Paragraph(text1,bt)]))
699        doc = MyDocTemplate(outputfile('test_platypus_imageandflowables_rtl.pdf'),showBoundary=1)
700        doc.multiBuild(story)
701
702    @unittest.skipUnless(rtlSupport and haveDejaVu(),'s')
703    def test2_RTL(self):
704        '''example & bugfix contributed by Moshe Uminer < mosheduminer at gmail.com >'''
705        from reportlab.platypus import Paragraph, PageBreak
706        from reportlab.lib.enums import TA_JUSTIFY, TA_RIGHT, TA_CENTER, TA_LEFT
707        from reportlab.pdfbase import pdfmetrics
708        from reportlab.pdfbase.ttfonts import TTFont
709        from reportlab.lib.pagesizes import LETTER
710        from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet
711        from reportlab.platypus.doctemplate import SimpleDocTemplate
712        from reportlab.lib.colors import toColor
713        pdfmetrics.registerFont(TTFont("DejaVuSans", "DejaVuSans.ttf"))
714
715        text = u'\u05e9\u05dc\u05d5\u05dd! \u05d6\u05d5 \u05ea\u05d4\u05d9\u05d4 \u05e4\u05e1\u05e7\u05d4 \u05e9\u05d4\u05e9\u05d5\u05e8\u05d4 \u05d4\u05d0\u05d7\u05e8\u05d5\u05e0\u05d4 \u05e9\u05dc\u05d4 \u05dc\u05d0 \u05ea\u05d5\u05e6\u05d3\u05e7 \u05db\u05d4\u05dc\u05db\u05d4.'
716        text = u' '.join((text,text))
717
718        doc = SimpleDocTemplate(
719            outputfile("test_platypus_paragraphs_rtl_2.pdf"),
720            pagesize=LETTER,
721            rightMargin=72,
722            leftMargin=72,
723            topMargin=72,
724            bottomMargin=72,
725            )
726        styles = getSampleStyleSheet()
727        hebrew_j = ParagraphStyle(
728            parent=styles["Normal"],
729            name="NormalHebrew",
730            wordWrap="RTL",
731            alignment=TA_JUSTIFY,
732            fontName="DejaVuSans",
733            fontSize=14,
734            underlineWidth=0.5,
735            underlineColor=toColor('red'),
736            strikeWidth=0.4,
737            strikeColor=toColor('orange'),
738            )
739        latin = hebrew_j.clone('latin',alignment=TA_LEFT,wordWrap='LTR')
740        hebrew_r = hebrew_j.clone('nhr', alignment=TA_RIGHT)
741        hebrew_rd = hebrew_r.clone('nhrd', endDots='.')
742        hebrew_jd = hebrew_j.clone('nhjd', endDots='.')
743        hebrew_c = hebrew_j.clone('nhc', alignment=TA_CENTER)
744        hebrew_cd = hebrew_c.clone('nhc', endDots='.')
745
746        rtext = text.replace(u'\u05db\u05d4\u05dc\u05db\u05d4',u'<span color=red>\u05db\u05d4\u05dc\u05db\u05d4</span>')
747        _utext = u''.join((u'<u>',text,u'</u>'))
748        utext = u''.join(('<u color=green>',text,'</u>'))
749        stext = u''.join(('<strike>',text,'</strike>'))
750        urtext = u''.join(('<u color=blue>',rtext,'</u>'))
751        srtext = u''.join(('<strike color=magenta>',rtext,'</strike>'))
752        flowables = [
753                Paragraph('justified',latin),
754                Paragraph(text, hebrew_j),
755                Paragraph(_utext, hebrew_j), #single frag takes defaults
756                Paragraph(stext, hebrew_j), #single frag takes defaults
757                Paragraph(utext, hebrew_j),
758                Paragraph(rtext, hebrew_j),
759                Paragraph(urtext, hebrew_j),
760                Paragraph(srtext, hebrew_j),
761
762                Paragraph('right',latin),
763                Paragraph(text, hebrew_r),
764                Paragraph(_utext, hebrew_r), #single frag takes defaults
765                Paragraph(stext, hebrew_r), #single frag takes defaults
766                Paragraph(utext, hebrew_r),
767                Paragraph(rtext, hebrew_r),
768                Paragraph(urtext, hebrew_r),
769                Paragraph(srtext, hebrew_r),
770
771                Paragraph('center',latin),
772                Paragraph(text, hebrew_c),
773                Paragraph(_utext, hebrew_c), #single frag takes defaults
774                Paragraph(stext, hebrew_c), #single frag takes defaults
775                Paragraph(utext, hebrew_c),
776                Paragraph(rtext, hebrew_c),
777                Paragraph(urtext, hebrew_c),
778                Paragraph(srtext, hebrew_c),
779
780                PageBreak(),
781                Paragraph('justified dots',latin),
782                Paragraph(text, hebrew_jd),
783                Paragraph(_utext, hebrew_jd), #single frag takes defaults
784                Paragraph(stext, hebrew_jd), #single frag takes defaults
785                Paragraph(utext, hebrew_jd),
786                Paragraph(rtext, hebrew_jd),
787                Paragraph(urtext, hebrew_jd),
788                Paragraph(srtext, hebrew_jd),
789
790                Paragraph('right dots',latin),
791                Paragraph(text, hebrew_rd),
792                Paragraph(_utext, hebrew_rd), #single frag takes defaults
793                Paragraph(stext, hebrew_rd), #single frag takes defaults
794                Paragraph(utext, hebrew_rd),
795                Paragraph(rtext, hebrew_rd),
796                Paragraph(urtext, hebrew_rd),
797                Paragraph(srtext, hebrew_rd),
798
799                Paragraph('center dots',latin),
800                Paragraph(text, hebrew_cd),
801                Paragraph(_utext, hebrew_cd), #single frag takes defaults
802                Paragraph(stext, hebrew_cd), #single frag takes defaults
803                Paragraph(utext, hebrew_cd),
804                Paragraph(rtext, hebrew_cd),
805                Paragraph(urtext, hebrew_cd),
806                Paragraph(srtext, hebrew_cd),
807                ]
808        doc.build(flowables)
809
810    def test_splitJustBug(self):
811        """test that justified paragraphs with </br>last line split properly
812        bug reported by Niharika Singh <nsingh@shoobx.com>
813        """
814        measures = []
815        def _odW(canv,name,label):
816            measures.append((label,canv._curr_tx_info['cur_x']))
817        text = '''<para><onDraw name="_odW" label="start"/>First line<onDraw name="_odW" label="end"/><br/><onDraw name="_odW" label="start"/>Second line<onDraw name="_odW" label="end"/><br/><onDraw name="_odW" label="start"/>split here<onDraw name="_odW" label="end"/><br/><onDraw name="_odW" label="start"/>Third line should not be justified<onDraw name="_odW" label="end"/><br/></para>'''
818        normal = getSampleStyleSheet()['BodyText']
819        normal.fontName = "Helvetica"
820        normal.fontSize = 10
821        normal.leading = 12
822        normal.alignment = TA_JUSTIFY
823        canv = Canvas(outputfile('test_splitJustBug.pdf'))
824        canv._odW = _odW
825        W, H = canv._pagesize
826        aW = W-2*72
827        aH = H-2*72
828        x = 72
829        y = H-72
830        P = Paragraph(text,normal)
831        w,h = P.wrap(aW,aH)
832        P.drawOn(canv,x,y)
833        M0 = measures[:]
834        measures[:] = []
835        y -= h
836        aH -= h
837        P = Paragraph(text,normal)
838        w,h = P.wrap(W-2*72,H-2*72)
839        P1,P2 = P.split(aW,37)
840        w,h = P1.wrap(aW,37)
841        P1.drawOn(canv,x,y)
842        y -= h
843        aH -= h
844        w,h = P2.wrap(aW,aH)
845        P2.drawOn(canv,x,y)
846        y -= h
847        aH -= h
848        canv.save()
849        self.assertEqual(M0,measures,"difference detected in justified split Paragraph rendering")
850
851    def test_unicharCodeSafety(self):
852        """test a bug reported by ravi prakash giri <raviprakashgiri@gmail.com>"""
853        normal = getSampleStyleSheet()['BodyText']
854        self.assertRaises(Exception,Paragraph,
855                """<unichar code="open('/tmp/test.txt','w').write('Hello from unichar')"/>""",
856                normal)
857
858    @unittest.skipUnless(trustedHosts,'s')
859    def test_badUri0(self):
860        """test we catch bad hosts"""
861        normal = getSampleStyleSheet()['BodyText']
862        self.assertRaises(Exception,Paragraph,
863                """<img src='https://badhost.com'/>""",
864                normal)
865        self.assertRaises(Exception,Paragraph,
866                """<img src='https://127.0.0.1:5000'/>""",
867                normal)
868        self.assertRaises(Exception,Paragraph,
869                """<img src='https://www.reportlab.com:5000'/>""",
870                normal)
871
872    @unittest.skipUnless(trustedSchemes,'s')
873    def test_badUri1(self):
874        """test we catch bad schemes"""
875        normal = getSampleStyleSheet()['BodyText']
876        self.assertRaises(Exception,Paragraph,
877                """<img src='badscheme://badhost.com'/>""",
878                normal)
879        self.assertRaises(Exception,Paragraph,
880                """<img src='badscheme://127.0.0.1:5000'/>""",
881                normal)
882        self.assertRaises(Exception,Paragraph,
883                """<img src='myscheme://www.reportlab.com'/>""",
884                normal)
885
886
887class TwoFrameDocTemplate(BaseDocTemplate):
888    "Define a simple document with two frames per page."
889
890    def __init__(self, filename, **kw):
891        m = 2*cm
892        from reportlab.lib import pagesizes
893        PAGESIZE = pagesizes.landscape(pagesizes.A4)
894        cw, ch = (PAGESIZE[0]-2*m)/2., (PAGESIZE[1]-2*m)
895        ch -= 14*cm
896        f1 = Frame(m, m+0.5*cm, cw-0.75*cm, ch-1*cm, id='F1',
897            leftPadding=0, topPadding=0, rightPadding=0, bottomPadding=0,
898            showBoundary=True
899        )
900        f2 = Frame(cw+2.7*cm, m+0.5*cm, cw-0.75*cm, ch-1*cm, id='F2',
901            leftPadding=0, topPadding=0, rightPadding=0, bottomPadding=0,
902            showBoundary=True
903        )
904        BaseDocTemplate.__init__(self, filename, **kw)
905        template = PageTemplate('template', [f1, f2])
906        self.addPageTemplates(template)
907
908
909class SplitFrameParagraphTest(unittest.TestCase):
910    "Test paragraph split over two frames."
911
912    def test(self):
913        stylesheet = getSampleStyleSheet()
914        normal = stylesheet['BodyText']
915        normal.fontName = "Helvetica"
916        normal.fontSize = 12
917        normal.leading = 16
918        normal.alignment = TA_JUSTIFY
919
920        text = b"Bedauerlicherweise ist ein Donaudampfschiffkapit\xc3\xa4n auch <font color='red'>nur</font> <font color='green'>ein</font> Dampfschiffkapit\xc3\xa4n."
921        tagFormat = '%s'
922        # strange behaviour when using next code line
923        # (same for '<a href="http://www.reportlab.org">%s</a>'
924        tagFormat = '<font color="red">%s</font>'
925
926        #text = " ".join([tagFormat % w for w in text.split()])
927
928        story = [Paragraph((text.decode('utf8') + u" ") * 3, style=normal)]
929
930        from reportlab.lib import pagesizes
931        PAGESIZE = pagesizes.landscape(pagesizes.A4)
932
933        doc = TwoFrameDocTemplate(outputfile('test_paragraphs_splitframe.pdf'), pagesize=PAGESIZE)
934        doc.build(story)
935
936class FragmentTestCase(unittest.TestCase):
937    "Test fragmentation of paragraphs."
938
939    def test0(self):
940        "Test empty paragraph."
941
942        styleSheet = getSampleStyleSheet()
943        B = styleSheet['BodyText']
944        text = ''
945        P = Paragraph(text, B)
946        frags = [f.text for f in P.frags]
947        assert frags == []
948
949    def test1(self):
950        "Test simple paragraph."
951
952        styleSheet = getSampleStyleSheet()
953        B = styleSheet['BodyText']
954        text = "X<font name=Courier>Y</font>Z"
955        P = Paragraph(text, B)
956        frags = [f.text for f in P.frags]
957        assert frags == ['X', 'Y', 'Z']
958
959    def test2(self):
960        '''test _splitWord'''
961        self.assertEqual(_splitWord(u'd\'op\u00e9ration',30,[30],0,'Helvetica',12),[u'', u"d'op\xe9", u'ratio', u'n'])
962        self.assertEqual(_splitWord(b'd\'op\xc3\xa9ration',30,[30],0,'Helvetica',12),[u'', u"d'op\xe9", u'ratio', u'n'])
963
964    def test3(self):
965        '''test _fragWordSplitRep'''
966        BF = ABag(rise=0,fontName='Helvetica',fontSize=12)
967        nF = BF.clone
968        ww = 'unused width'
969        W = [ww,(nF(cbDefn=ABag(kind='index',width=0)),''),(BF,'a'),(nF(fontSize=10),'bbb'),(nF(fontName='Helvetica-Bold'),'ccccc')]
970        self.assertEqual(_fragWordSplitRep(W),(u'abbbccccc',((2, 0), (3, 1), (3, 1), (3, 1), (4, 4), (4, 4), (4, 4), (4, 4), (4, 4))))
971        W[1][0].rise=2
972        self.assertEqual(_fragWordSplitRep(W),None)
973        W = [ww,(nF(cbDefn=ABag(kind='img',width=1)),''),(BF,'a'),(BF,'bbb'),(BF,'ccccc')]
974        self.assertEqual(_fragWordSplitRep(W),None)
975
976    def test4(self):
977        from reportlab.platypus.paragraph import _hy_letters_pat, _hy_shy_letters_pat, _hy_letters, _hy_pfx_pat, _hy_sfx_pat
978        self.assertIsNotNone(_hy_shy_letters_pat.match(_hy_letters),'pre-hyphenated word match should succeed')
979        self.assertIsNone(_hy_letters_pat.match(_hy_letters),'all letters word match should fail')
980        self.assertIsNotNone(_hy_letters_pat.match(_hy_letters.replace(u'-',u'')),'all letters word match should succeed')
981        pfx = u'\'"([{\xbf\u2018\u201a\u201c\u201e'
982        m = _hy_pfx_pat.match(pfx)
983        self.assertIsNotNone(m,'pfx pattern should match')
984        self.assertEqual(len(m.group(0)),len(pfx),'pfx pattern should match %d characters not %d' %(len(pfx),len(m.group(0))))
985        sfx = u']\'")}?!.,;:\u2019\u201b\u201d\u201f'
986        m = _hy_sfx_pat.search(sfx)
987        self.assertIsNotNone(m,'sfx pattern should match')
988        self.assertEqual(len(m.group(0)),len(sfx),'sfx pattern should match %d characters not %d' %(len(sfx),len(m.group(0))))
989
990    def test5(self):
991        from reportlab.platypus.paragraph import _hyphenateWord, _hyphenateFragWord, _rebuildFragWord, stringWidth, ABag
992        w = 'https://www.reportlab.com/pypi/packages'
993        fontName = 'Helvetica'
994        fontSize = 12
995        AF = ABag(rise=0,fontName=fontName,fontSize=fontSize)
996        BF = AF.clone(fontName='Helvetica-Bold')
997
998        def applyTest(w, uriWasteReduce, embeddedHyphenation, ex, split=None):
999            if not split:
1000                ww = stringWidth(w,fontName,fontSize)
1001                r = _hyphenateWord(None,fontName,fontSize,w,ww,ww+10,ww+5, uriWasteReduce, embeddedHyphenation)
1002            elif split:
1003                i = int(len(w)*split)
1004                fw = _rebuildFragWord([(AF,w[:i]),(BF,w[i:])])
1005                #print('%s %s %r %r' % (split,fw[0],fw[1][1],fw[2][1]))
1006                ww = fw[0]
1007                r = _hyphenateFragWord(None,fw,ww+10,ww+5, uriWasteReduce, embeddedHyphenation)
1008                if r is not None:
1009                    _r = r
1010                    r = [u''.join((_[1] for _ in _fw[1:])) for _fw in _r]
1011            self.assertEqual(r,ex,'hyphenation of w=%r u=%r e=%r ex=%r split=%r failed'%(w,uriWasteReduce,embeddedHyphenation,ex,split))
1012        for split in (None,0.001,0.1,0.5,0.8,0.9,1.0):
1013            applyTest('https://www.reportlab.com/pypi/packages', 0.3, True, [u'https://www.reportlab.com/pypi/', u'packages'],split=split)
1014            applyTest('https://www.reportlab.com/pypi/packages', False, True, None,split=split)
1015            applyTest('https://www.repor-tlab.com/pypi/packages', 0.3, True, [u'https://www.repor-tlab.com/pypi/', u'packages'],split=split)
1016            applyTest('https//www.repor-tlab.com/pypi/packages', 0.3, True, None,split=split) #not a uri (no :) and contains - and non letters
1017            applyTest('httpsSSwwwDrepor-tlabDcomSpypiSpackages', 0.3, True, [u'httpsSSwwwDrepor-', u'tlabDcomSpypiSpackages'],split=split) #should succeed because '-' with no non-letters
1018            applyTest('httpsSSwwwDrepor-tlabDcomSpypiSpackages', 0.3, False, None, split=split) #fails because embeddedHyphenation=False
1019
1020    @unittest.skipUnless(pyphen,'s')
1021    def test6(self):
1022        bt = getSampleStyleSheet()['BodyText']
1023        bt.fontName = 'Helvetica'
1024        bt.fontSize = 10
1025        bt.leading = 12
1026        bt.alignment = TA_JUSTIFY
1027        canv = Canvas(outputfile('test_platypus_paragraphs_hyphenations.pdf'))
1028        x = 72
1029        y = canv._pagesize[1] - 72
1030
1031        def _t(p,x,y,aW,aH=0x7fffffff,dy=5):
1032            w, h = p.wrap(aW,aH)
1033            y0 = y
1034            y -= h
1035            canv.saveState()
1036            canv.setLineWidth(0.5)
1037            if aH!=0x7fffffff:
1038                canv.setLineWidth(1)
1039                canv.setStrokeColor((1,0,0))
1040                canv.rect(x,y0-aH,aW,aH)
1041                ny = y0 - max(aH,h)
1042            else:
1043                ny = y
1044            canv.setLineWidth(0.5)
1045            canv.setStrokeColor((0.5,0.5,0.5))
1046            canv.rect(x,y,w,h)
1047            p.drawOn(canv,x,y)
1048            canv.restoreState()
1049            return ny-dy
1050        def t(x,y,aW,aH=0x7fffffff,style=bt):
1051            p = Paragraph(text,style=style)
1052            return _t(p,x,y,aW,aH=aH)
1053
1054        raw = """This is a splittable word 'disestablishment'!"""
1055        for text in ("""This is a splittable word '<span color="red">dis</span><span color="blue">estab</span><span color="green">lish</span><span color="magenta">ment</span>'!""",
1056                     """This is a splittable word 'd<span color="red">ise</span><span color="blue">stabl</span><span color="green">ishm</span><span color="magenta">ent</span>'!""",
1057                     """This is a splittable word 'di<span color="red">ses</span><span color="blue">tabli</span><span color="green">shme</span><span color="magenta">ent</span>'!""",
1058                    """This is a splittable word 'disestablishment'!""",
1059                     ):
1060            aW = stringWidth("This is a splittable word 'dis",bt.fontName,bt.fontSize)
1061            y = t(x,y,aW)
1062            aW = stringWidth("This is a splittable word 'dise",bt.fontName,bt.fontSize)
1063            y = t(x,y,aW)
1064            aW = stringWidth("This is a splittable word 'disestabl",bt.fontName,bt.fontSize)
1065            y = t(x,y,aW)
1066            aW = stringWidth("This is a splittable word 'disestabli",bt.fontName,bt.fontSize)
1067            y = t(x,y,aW)
1068            aW = stringWidth("This is a splittable word 'disestablishm",bt.fontName,bt.fontSize)
1069            y = t(x,y,aW)
1070
1071        canv.showPage()
1072        y = canv._pagesize[1] - 72
1073        nt = bt.clone('nt',fontName='Helvetica', fontSize = 12,leading = 16, alignment = TA_JUSTIFY)
1074        naW, naH = 342.992125984252, 56.69291338582681
1075        for ntext in (  b"Bedauerlicherweise ist ein Donaudampfschiffkapit\xc3\xa4n auch <font color='red'>nur</font> <font color='green'>ein</font> Dampfschiffkapit\xc3\xa4n.",
1076                        b"Bedauerlicherweise ist ein Donaudampfschiffkapit\xc3\xa4n auch nur ein Dampfschiffkapit\xc3\xa4n.",
1077                        ):
1078            ntext = (ntext.decode('utf8') + u" ") * 3
1079            p = Paragraph(ntext,style=nt)
1080            y = _t(p,x,y,naW,aH=naH)
1081
1082            S = p.split(naW,naH)
1083            self.assertEqual(len(S),2)
1084            y = _t(S[0],x,y,naW,aH=naH)
1085            y = _t(S[1],x,y,naW,aH=naH)
1086        canv.save()
1087
1088    @unittest.skipUnless(pyphen,'s')
1089    def test7(self):
1090        """test various ways to adjust the hypenationMinWordLength"""
1091        registerFont(TTFont("Vera", "Vera.ttf"))
1092        aW = 51.0236220472
1093        text  = u'\u0440\u044b\u0431\u0430 \u043f\u0438\u043b\u0430 \u0438 \u0431\u0430\u0431\u0443 \u0434\u0430\u0436\u0435 \u0432\u0435\u043b\u0430 \u043d\u0430 \u043c\u0430\u044f\u043a'
1094        text1 = u'\u0440\u044b\u0431\u0430 <span color="blue">\u043f\u0438\u043b\u0430</span> \u0438 \u0431\u0430\u0431\u0443 \u0434\u0430\u0436\u0435 \u0432\u0435\u043b\u0430 \u043d\u0430 \u043c\u0430\u044f\u043a'
1095        tmpls = [
1096                u'%(text)s',
1097                u'<para hyphenationMinWordLength="%(hymwl)d">%(text)s</para>',
1098                u'<para>%(text)s</para>',
1099                u'%(text1)s',
1100                u'<para hyphenationMinWordLength="%(hymwl)d">%(text1)s</para>',
1101                u'<para>%(text1)s</para>',
1102                ]
1103        for ex,x,hymwl in [
1104                (72,0,None),    #default is 5
1105                (72,0,5),
1106                (60,0,4),
1107                (72,2,None),    #default is 5
1108                (72,1,5),
1109                (60,1,4),
1110                (72,3,None),    #default is 5
1111                (72,4,5),
1112                (60,5,4),
1113                ]:
1114            kwds = dict(hyphenationMinWordLength=hymwl) if hymwl!=None else {}
1115            template = tmpls[x]
1116            t = template % locals()
1117            p = Paragraph(
1118                    template % locals(),
1119                    style = ParagraphStyle('P10H5', fontSize=10, fontName="Vera", hyphenationLang="ru_RU",**kwds),
1120                    )
1121            w,h = p.wrap(aW,0x7fffffff)
1122            self.assertEqual(h,ex,'Russion hyphenation test failed for ex=%(ex)s template=%(template)r hymwl=%(hymwl)r h=%(h)s\ntext=%(t)r' % locals())
1123
1124    @unittest.skipUnless(pyphen,'s')
1125    def test8(self):
1126        """display splitting of hyphenated words"""
1127        from reportlab.lib.pagesizes import A4
1128        from reportlab.lib.styles import ParagraphStyle
1129        from reportlab.lib.utils import isBytes
1130        import hashlib
1131        from reportlab.pdfgen.canvas import Canvas
1132        from reportlab.platypus.paragraph import Paragraph
1133        pW, pH = A4
1134        canv = Canvas(outputfile('test_platypus_paragraphs_hysplit.pdf'),pagesize=(pW,pH))
1135        text = u'Supercalifragilisticexpialidocious'
1136        mtext = u'<span color="red">Super</span>califragilisticexpialidocious'
1137        mtext1 = u'Supercalifragilisticexpiali<span color="green">docious</span>'
1138        stext = u'Super\xadcali\xadfragi\xadlistic\xadexpi\xadali\xaddocious'
1139        mstext = u'<span color="red">Super\xad</span>cali\xadfragi\xadlistic\xadexpi\xadali\xaddocious'
1140        mstext1 = u'Super\xadcali\xadfragi\xadlistic\xadexpi\xadali\xad<span color="green">docious</span>'
1141        text =   u'Supercalifragilisticexpialidocious'
1142        mtext2 = 'converted to a <b>Python</b> <font name=Courier><nobr>string</nobr></font>.'
1143        mtext3 = 'This one uses fonts with size "14pt" and also uses the em and strong tags: Here comes <font face="Helvetica" size="14pt">Helvetica 14</font> with <Strong>strong</Strong> <em>emphasis</em>.'
1144        textF = 'Figure [seq template="%(Chapter)s-%(FigureNo+)s"/] - Multi-level templates'
1145        mtextF = 'Figure &lt;seq template="%(Chapter)s-%(FigureNo+)s"/&gt; - Multi-level templates'
1146        sty0=ParagraphStyle(
1147                            name="base",
1148                            fontName="Helvetica",
1149                            leading=12,
1150                            leftIndent=0,
1151                            firstLineIndent=0,
1152                            spaceBefore = 9.5,
1153                            fontSize=10,
1154                            hyphenationLang="en_GB",
1155                            )
1156        sty1=ParagraphStyle(
1157                            name="base",
1158                            fontName="Times-Roman",
1159                            leading=12,
1160                            leftIndent=0,
1161                            firstLineIndent=0,
1162                            spaceBefore = 9.5,
1163                            fontSize=10,
1164                            hyphenationLang="en_GB",
1165                            )
1166        styN =  ParagraphStyle('normal', hyphenationLang="en_GB")
1167        styF=ParagraphStyle(
1168                            name = 'styF',
1169                            fontName='Courier',
1170                            fontSize=8,
1171                            leading=9.6,
1172                            hyphenationLang="en_GB",
1173                            )
1174        def box(x, y, aW, h):
1175            canv.saveState()
1176            canv.setDash(1,1)
1177            canv.setLineWidth(0.1)
1178            canv.rect(x,y,aW,h)
1179            canv.restoreState()
1180
1181        def doPara(P,x,y,aW, wc=1):
1182            for _ in range(wc):
1183                w,h = P.wrap(aW,900)
1184            #print('w=%s h=%s' % (w,h))
1185            y -= h
1186            box(x, y, aW, h)
1187            P.drawOn(canv, x, y)
1188            return y
1189
1190        def doTest(msg, t,x,y,aW, st=None, split=True, wc=1):
1191            if not st: st = sty0
1192            canv.drawString(x+aW+5,y-12,msg)
1193            P = Paragraph(t,st)
1194            y = doPara(P, x, y, aW, wc=wc) - 5
1195            if split:
1196                P = Paragraph(t,st)
1197                S = P.split(aW, st.leading*2+0.1)
1198                y = doPara(S[0], x, y, aW) - 5
1199                y = doPara(S[1], x, y, aW) - 5
1200            return y
1201
1202        aW = 55
1203        x = 72
1204        y = pH - 72
1205
1206        cp0 = len(canv._code)
1207        y = doTest('Single Frag Free Hyphenation', text, x, y, aW) - 5
1208        y = doTest('Single Frag Soft Hyphenation', stext, x, y, aW) - 5
1209        y = doTest('Multi Frag Free Hyphenation', mtext, x, y, aW) - 5
1210        y = doTest('Multi Frag Free Hyphenation 1', mtext1, x, y, aW) - 5
1211        y = doTest('Multi Frag Free Hyphenation 2', mtext2, x, y, 77, st=sty1, split=False, wc=2) - 5
1212        y = doTest('Multi Frag Free Hyphenation 3', mtext3, x, y, 439.27559055118115, st=styN, split=False, wc=1) - 5
1213        y = doTest('Multi Frag Soft Hyphenation', mstext, x, y, aW) - 5
1214        y = doTest('Multi Frag Soft Hyphenation 1', mstext1, x, y, aW) - 5
1215        y = doTest('Single Frag Free F', textF, x, y, 158.13921259842525, st=styF, split=False, wc=3) - 5
1216        y = doTest('Multi Frag Free F', mtextF, x, y, 158.13921259842525, st=styF, split=False, wc=3) - 5
1217        c = '\n'.join(canv._code[cp0:])
1218        if not isBytes(c):
1219            c = c.encode('utf8')
1220        h = hashlib.md5(c).hexdigest()
1221        canv.showPage()
1222        canv.save()
1223        #xh = '32e0e490cc4a53c31bb19f1cc52debdd'
1224        xh = 'eee06395aa68a727d58e688006c85d79'
1225        self.assertEqual(xh, h, 'test8 code is no longer correct %s != expected %s' % (h,xh))
1226
1227class ULTestCase(unittest.TestCase):
1228    "Test underlining and overstriking of paragraphs."
1229    def testUl(self):
1230        from reportlab.platypus import BaseDocTemplate, PageTemplate, Frame, PageBegin
1231        from reportlab.lib.units import inch
1232        from reportlab.platypus.flowables import AnchorFlowable
1233        class MyDocTemplate(BaseDocTemplate):
1234            _invalidInitArgs = ('pageTemplates',)
1235
1236            def __init__(self, filename, **kw):
1237                self.allowSplitting = 0
1238                kw['showBoundary']=1
1239                BaseDocTemplate.__init__(self, filename, **kw)
1240                self.addPageTemplates(
1241                        [
1242                        PageTemplate('normal',
1243                                [Frame(inch, inch, 6.27*inch, 9.69*inch, id='first',topPadding=0,rightPadding=0,leftPadding=0,bottomPadding=0,showBoundary=ShowBoundaryValue(color="red"))],
1244                                ),
1245                        ])
1246
1247        styleSheet = getSampleStyleSheet()
1248        normal = ParagraphStyle(name='normal',fontName='Times-Roman',fontSize=12,leading=1.2*12,parent=styleSheet['Normal'])
1249        normal_sp = ParagraphStyle(name='normal_sp',parent=normal,alignment=TA_JUSTIFY,spaceBefore=12)
1250        normal_just = ParagraphStyle(name='normal_just',parent=normal,alignment=TA_JUSTIFY)
1251        normal_right = ParagraphStyle(name='normal_right',parent=normal,alignment=TA_RIGHT)
1252        normal_center = ParagraphStyle(name='normal_center',parent=normal,alignment=TA_CENTER)
1253        normal_indent = ParagraphStyle(name='normal_indent',firstLineIndent=0.5*inch,parent=normal)
1254        normal_indent_lv_2 = ParagraphStyle(name='normal_indent_lv_2',firstLineIndent=1.0*inch,parent=normal)
1255        texts = ['''Furthermore, a subset of <font size="14">English sentences</font> interesting on quite
1256independent grounds is not quite equivalent to a stipulation to place
1257the constructions into these various categories.''',
1258        '''We will bring evidence in favor of
1259The following thesis:  most of the methodological work in modern
1260linguistics can be defined in such a way as to impose problems of
1261phonemic and morphological analysis.''']
1262        story =[]
1263        a = story.append
1264        a(Paragraph("This should &lt;a href=\"#theEnd\" color=\"blue\"&gt;<a href=\"#theEnd\" color=\"blue\">jump</a>&lt;/a&gt; jump to the end!",style=normal))
1265        a(XPreformatted("This should &lt;a href=\"#theEnd\" color=\"blue\"&gt;<a href=\"#theEnd\" color=\"blue\">jump</a>&lt;/a&gt; jump to the end!",style=normal))
1266        a(Paragraph("<a href=\"#theEnd\"><u><font color=\"blue\">ditto</font></u></a>",style=normal))
1267        a(XPreformatted("<a href=\"#theEnd\"><u><font color=\"blue\">ditto</font></u></a>",style=normal))
1268        a(Paragraph("This <font color='CMYKColor(0,0.6,0.94,0)'>should</font> &lt;a href=\"#thePenultimate\" color=\"blue\"&gt;<a href=\"#thePenultimate\" color=\"blue\">jump</a>&lt;/a&gt; jump to the penultimate page!",style=normal))
1269        a(Paragraph("This should &lt;a href=\"#theThird\" color=\"blue\"&gt;<a href=\"#theThird\" color=\"blue\">jump</a>&lt;/a&gt; jump to a justified para!",style=normal))
1270        a(Paragraph("This should &lt;a href=\"#theFourth\" color=\"blue\"&gt;<a href=\"#theFourth\" color=\"blue\">jump</a>&lt;/a&gt; jump to an indented para!",style=normal))
1271        for mode in (0,1):
1272            text0 = texts[0]
1273            text1 = texts[1]
1274            if mode:
1275                text0 = text0.replace('English sentences','<b>English sentences</b>').replace('quite equivalent','<i>quite equivalent</i>')
1276                text1 = text1.replace('the methodological work','<b>the methodological work</b>').replace('to impose problems','<i>to impose problems</i>')
1277            for t in ('u','strike'):
1278                for n in range(6):
1279                    for s in (normal,normal_center,normal_right,normal_just,normal_indent, normal_indent_lv_2):
1280                        for autoLeading in ('','min','max'):
1281                            if n==4 and s==normal_center and t=='strike' and mode==1:
1282                                a(Paragraph("<font color=green>The second jump at the beginning should come here &lt;a name=\"thePenultimate\"/&gt;<a name=\"thePenultimate\"/>!</font>",style=normal))
1283                            elif n==4 and s==normal_just and t=='strike' and mode==1:
1284                                a(Paragraph("<font color=green>The third jump at the beginning should come just below here to a paragraph with just an a tag in it!</font>",style=normal))
1285                                a(Paragraph("<a name=\"theThird\"/>",style=normal))
1286                            elif n==4 and s==normal_indent and t=='strike' and mode==1:
1287                                a(Paragraph("<font color=green>The fourth jump at the beginning should come just below here!</font>",style=normal))
1288                                a(AnchorFlowable('theFourth'))
1289                            a(Paragraph('n=%d style=%s(autoLeading=%s) tag=%s'%(n,s.name,autoLeading,t),style=normal_sp))
1290                            a(Paragraph('<para autoleading="%s">%s<%s>%s</%s>. %s <%s>%s</%s>. %s</para>' % (
1291                            autoLeading,
1292                            (s==normal_indent_lv_2 and '<seq id="document" inc="no"/>.<seq id="document_lv_2"/>' or ''),
1293                            t,' '.join((n+1)*['A']),t,text0,t,' '.join((n+1)*['A']),t,text1),
1294                            style=s))
1295        a(Paragraph("The jump at the beginning should come here &lt;a name=\"theEnd\"/&gt;<a name=\"theEnd\"/>!",style=normal))
1296        a(Paragraph('Underlining <span fontSize="11"><u color="red">A<u color="green">B</u><u color="blue">C</u>D<sup><strike width="0.5" color="magenta">2</strike><sup><u color="darkgreen" width="0.2">3</u></sup></sup></u></span>',normal))
1297        a(Paragraph('<para autoLeading="max" spaceAfter="10">this is in 12 <font size=30>this is in 30</font> <u offset="-0.5" width="0.5" color="red"><u offset="-1.5" width="0.5" color="blue">and</u></u> <link underline="1" ucolor="blue" href="http://google.com/">the link box<sup><a color="red" ucolor="green" underline="1" href="https://www.reportlab.com">2</a></sup> is right (twice).</link></para>''',normal))
1298        a(Paragraph('<para autoLeading="max" spaceAfter="10">this is in 12 <font size=30>this is in 30</font> and <link underline="1" ucolor="blue" href="http://google.com/">the link box is right.</link></para>''',normal))
1299        a(Paragraph('Underlining <u><span color="red">underlined in red? <span color="blue"><u>or blue</u></span> or red again?</span></u>',normal))
1300        a(Paragraph('Link <a href="#theEnd" color="blue">jump</a> to end.<br/>Underlined link <a href="#theEnd" underline="1" ucolor="red" color="blue">jump</a> to end!',style=normal))
1301        a(Paragraph('<para autoleading=""><u>A</u>. Furthermore, a subset of <font size="14">English sentences</font> interesting on quite\nindependent grounds is not quite equivalent to a stipulation to place\nthe constructions into these various categories. <u>A</u>. We will bring evidence in favor of\nThe following thesis: most of the methodological work in modern\nlinguistics can be defined in such a way as to impose problems of\nphonemic and morphological analysis.</para>',normal))
1302        a(Paragraph('<para autoleading=""><u>A</u>. Furthermore, a subset of <font size="14">English sentences</font> interesting on quite<br/><u>A</u>.</para>',normal))
1303        a(Paragraph(u"<para>This is a <sup rise=5><span color='red'>sup</span></sup>rise=5.</para>",normal))
1304        a(Paragraph('<span fontSize="11"><u color="green"><strike color="blue">AAAAAA</strike></u></span>',normal))
1305        a(Paragraph("Underlining &amp; width proportional to first use font size ('f' suffix) <u offset='-0.125*f' width='0.05*f'>underlined <span size=14>underlined</span></u>!",style=normal))
1306        a(Paragraph("Underlining &amp; width proportional to first use font size ('F' suffix) <u offset='-0.125*F' width='0.05*F'>underlined <span size=14>underlined</span></u>!",style=normal))
1307        a(Paragraph('''<para spaceBefore="10">This is underlined &lt;sup&gt;: a<sup><u><span color="red">sup</span></u></sup></para>''',style=normal))
1308        a(Paragraph('''<para spaceBefore="10">This is <u>underlined</u></para>''',style=normal))
1309        a(Paragraph('''<para spaceBefore="10">This is <u kind="double">underlined double</u></para>''',style=normal))
1310        a(Paragraph('''<para spaceBefore="10">This is <strike>striken</strike></para>''',style=normal))
1311        a(Paragraph('''<para spaceBefore="10">This is <strike><u>both</u></strike></para>''',style=normal))
1312        a(Paragraph('''<para spaceBefore="10">This is <u width="0.5" offset="-1" kind="double">underlined kind="double"</u></para>''',style=normal))
1313        a(Paragraph('''<para spaceBefore="10">This is <u width="0.25" offset="-1" kind="double">double underlined with thinner lines</u></para>''',style=normal))
1314        a(Paragraph('''<para spaceBefore="10">This is <u width="0.5" offset="-0.5" color="red">underlined in red</u></para>''',style=normal))
1315        a(Paragraph('''<para spaceBefore="10">This is <strike width="0.5" color="red">overstruck in red</strike></para>''',style=normal))
1316        a(Paragraph('''<para spaceBefore="10">This is <strike width="0.5" color="red" kind="double">doubly overstruck in red</strike></para>''',style=normal))
1317        a(Paragraph('''<para spaceBefore="10">This is <strike width="0.5" offset="0.125*F" color="red" kind="triple" gap="0.5">triply overstruck in red</strike></para>''',style=normal))
1318        a(Paragraph('''<para autoLeading="max" spaceAfter="10" spaceBefore="30">this is in 12 <font size="30">this is in 30</font> <u offset="-0.5" width="0.5" color="red"><u offset="-1.5" width="0.5" color="blue">and</u></u> <link underline="1" ucolor="blue" href="http://google.com/">the link box<sup><a color="red" ucolor="green" underline="1" href="https://www.reportlab.com">2</a></sup> is right (twice).</link></para>''',style=normal))
1319        a(Paragraph("",style=normal))
1320        # 3.5.x had a bug with leftIndent and underlines, check that
1321        left_indent = ParagraphStyle(name='left_indent',leftIndent=80,parent=styleSheet['Normal'])
1322        a(Paragraph("<u>Replicating a left indent underline bug.</u>",style=left_indent))
1323
1324        #testing PR 49
1325        a(Paragraph("This paragraph contains <strike>really</strike> large <u>spaces</u> <strike color='blue' offset='1' width='0.5'><u color='red' offset='-1' width='0.5'>between words</u></strike>. ThisLongWordDoesntFitIntoPreviousLineCausingWordsSpread",style=normal_just))
1326
1327        doc = MyDocTemplate(outputfile('test_platypus_paragraphs_ul.pdf'))
1328        doc.build(story)
1329
1330class AutoLeadingTestCase(unittest.TestCase):
1331    "Test underlining and overstriking of paragraphs."
1332    def testAutoLeading(self):
1333        from reportlab.platypus import BaseDocTemplate, PageTemplate, Frame, PageBegin
1334        from reportlab.lib.units import inch
1335        from reportlab.platypus.flowables import AnchorFlowable
1336        class MyDocTemplate(BaseDocTemplate):
1337            _invalidInitArgs = ('pageTemplates',)
1338
1339            def __init__(self, filename, **kw):
1340                self.allowSplitting = 0
1341                kw['showBoundary']=1
1342                BaseDocTemplate.__init__(self, filename, **kw)
1343                self.addPageTemplates(
1344                        [
1345                        PageTemplate('normal',
1346                                [Frame(inch, inch, 6.27*inch, 9.69*inch, id='first',topPadding=0,rightPadding=0,leftPadding=0,bottomPadding=0,showBoundary=ShowBoundaryValue(color="red"))],
1347                                ),
1348                        ])
1349
1350        from reportlab.lib.testutils import testsFolder
1351        styleSheet = getSampleStyleSheet()
1352        normal = ParagraphStyle(name='normal',fontName='Times-Roman',fontSize=12,leading=1.2*12,parent=styleSheet['Normal'])
1353        normal_sp = ParagraphStyle(name='normal_sp',parent=normal,alignment=TA_JUSTIFY,spaceBefore=12)
1354        texts = ['''Furthermore, a subset of <font size="14">English sentences</font> interesting on quite
1355independent grounds is not quite equivalent to a stipulation to place
1356<font color="blue">the constructions <img src="%(testsFolder)s/../docs/images/testimg.gif"/> into these various categories.</font>'''%dict(testsFolder=testsFolder),
1357        '''We will bring <font size="18">Ugly Things</font> in favor of
1358The following thesis:  most of the methodological work in Modern
1359Linguistics can be <img src="%(testsFolder)s/../docs/images/testimg.gif" valign="baseline" /> defined in such <img src="%(testsFolder)s/../docs/images/testimg.gif" valign="10" /> a way as to impose problems of
1360phonemic and <u>morphological <img src="%(testsFolder)s/../docs/images/testimg.gif" valign="top"/> </u> analysis.'''%dict(testsFolder=testsFolder)]
1361        story =[]
1362        a = story.append
1363        t = 'u'
1364        n = 1
1365        for s in (normal,normal_sp):
1366            for autoLeading in ('','min','max'):
1367                a(Paragraph('style=%s(autoLeading=%s)'%(s.name,autoLeading),style=normal_sp))
1368                a(Paragraph('<para autoleading="%s"><%s>%s</%s>. %s <%s>%s</%s>. %s</para>' % (
1369                            autoLeading,
1370                            t,' '.join((n+1)*['A']),t,texts[0],t,' '.join((n+1)*['A']),t,texts[1]),
1371                            style=s))
1372        a(Paragraph('''<img src="%(testsFolder)s/../docs/images/testimg.gif" valign="top"/> image is very first thing in the line.'''%dict(testsFolder=testsFolder), style=normal))
1373        a(Paragraph('some text.... some more.... some text.... some more....', normal))
1374        a(Paragraph('<img src="%(testsFolder)s/../docs/images/testimg.gif" width="0.57in" height="0.19in" /> some text <br /> '%dict(testsFolder=testsFolder), normal))
1375        a(Paragraph('some text.... some more.... some text.... some more....', normal))
1376        a(Paragraph('<img src="%(testsFolder)s/../docs/images/testimg.gif" width="0.57in" height="0.19in" /> <br /> '%dict(testsFolder=testsFolder), normal))
1377        a(Paragraph('some text.... some more.... some text.... some more....', normal))
1378
1379        #Volker Haas' valign tests
1380        fmt = '''<font color="red">%(valign)s</font>: Furthermore, a <u>subset</u> <strike>of</strike> <font size="14">English sentences</font> interesting on quite
1381independent grounds is not quite equivalent to a stipulation to place <img src="%(testsFolder)s/../docs/images/redsquare.png" width="0.5in" height="0.5in" valign="%(valign)s"/>
1382the constructions into these <u>various</u> categories. We will bring <font size="18">Ugly Things</font> in favor of
1383The following thesis:  most of the methodological work in Modern
1384Linguistics can be defined in such a way as to impose problems of
1385phonemic and <u>morphological</u> <strike>analysis</strike>.'''
1386
1387        p_style= ParagraphStyle('Normal')
1388        p_style.autoLeading = 'max'
1389        for valign in (
1390                'baseline',
1391                'sub',
1392                'super',
1393                'top',
1394                'text-top',
1395                'middle',
1396                'bottom',
1397                'text-bottom',
1398                '0%',
1399                '2in',
1400                ):
1401            a(Paragraph(fmt % dict(valign=valign,testsFolder=testsFolder),p_style))
1402            a(XPreformatted(fmt % dict(valign=valign,testsFolder=testsFolder),p_style))
1403
1404        a(Paragraph('<br/><b>Some Paragraph tests of &lt;sup rise="pts" size="pts"</b>...', normal))
1405        a(Paragraph("<br/>This is a <sup><span color='red'>sup</span></sup>.",p_style))
1406        a(XPreformatted("This is a <sup><span color='red'>sup</span></sup>.",p_style))
1407        a(Paragraph("This is a <sup rise=5><span color='red'>sup</span></sup>rise=5.",p_style))
1408        a(XPreformatted("This is a <sup rise=5><span color='red'>sup</span></sup>rise=5.",p_style))
1409        a(Paragraph("This is a <sup rise=6><span color='red'>sup</span></sup>rise=6.",p_style))
1410        a(XPreformatted("This is a <sup rise=6><span color='red'>sup</span></sup>rise=6.",p_style))
1411        a(Paragraph("This is a <sup rise=7><span color='red'>sup</span></sup>rise=7.",p_style))
1412        a(XPreformatted("This is a <sup rise=7><span color='red'>sup</span></sup>rise=7.",p_style))
1413        a(Paragraph("This is a <sup rise=8><span color='red'>sup</span></sup>rise=8.",p_style))
1414        a(XPreformatted("This is a <sup rise=8><span color='red'>sup</span></sup>rise=8.",p_style))
1415        a(Paragraph("This is a <sup rise=9><span color='red'>sup</span></sup>rise=9.",p_style))
1416        a(XPreformatted("This is a <sup rise=9><span color='red'>sup</span></sup>rise=9.",p_style))
1417        a(Paragraph("This is a <sup size=7><span color='red'>sup</span></sup>size=7.",p_style))
1418        a(XPreformatted("This is a <sup size=7><span color='red'>sup</span></sup>size=7.",p_style))
1419        a(Paragraph("This is a <sup rise=5 size=7><span color='red'>sup</span></sup>rise=5 size=7.",p_style))
1420        a(XPreformatted("This is a <sup rise=5 size=7><span color='red'>sup</span></sup>rise=5 size=7.",p_style))
1421        a(Paragraph("This is a <sup rise=6 size=7><span color='red'>sup</span></sup>rise=6 size=7.",p_style))
1422        a(XPreformatted("This is a <sup rise=6 size=7><span color='red'>sup</span></sup>rise=6 size=7.",p_style))
1423        a(Paragraph("This is a <sup rise=7 size=7><span color='red'>sup</span></sup>rise=7 size=7.",p_style))
1424        a(XPreformatted("This is a <sup rise=7 size=7><span color='red'>sup</span></sup>rise=7 size=7.",p_style))
1425        a(Paragraph("This is a <sup rise=8 size=7><span color='red'>sup</span></sup>rise=8 size=7.",p_style))
1426        a(XPreformatted("This is a <sup rise=8 size=7><span color='red'>sup</span></sup>rise=8 size=7.",p_style))
1427        a(Paragraph("This is a <sup rise=9 size=7><span color='red'>sup</span></sup>rise=9 size=7.",p_style))
1428        a(XPreformatted("This is a <sup rise=9 size=7><span color='red'>sup</span></sup>rise=9 size=7.",p_style))
1429        a(Paragraph("This is a <sup rise=90% size=70%><span color='red'>sup</span></sup>rise=90% size=70%.",p_style))
1430        a(Paragraph("This is a <sup rise=-2 size=-2><span color='red'>sup</span></sup>rise=-2 size=-2.",p_style))
1431        a(Paragraph("This is a <sup rise=-4 size=-3><span color='red'>sup</span></sup>rise=-4 size=-3.",p_style))
1432        a(PageBreak())
1433
1434        a(Paragraph('<br/><b>Some Paragraph tests of &lt;img width="x%" height="x%"</b>...', normal))
1435        a(Paragraph('H=10%% <img src="%(testsFolder)s/../docs/images/testimg.gif" width="0.57in" height="10%%" />'%dict(testsFolder=testsFolder), normal))
1436        a(Paragraph('H=50%% <img src="%(testsFolder)s/../docs/images/testimg.gif" width="0.57in" height="50%%" />'%dict(testsFolder=testsFolder), normal))
1437        a(Paragraph('H=100%% <img src="%(testsFolder)s/../docs/images/testimg.gif" width="0.57in" height="100%%" />'%dict(testsFolder=testsFolder), normal))
1438        a(Paragraph('H=100%% W=10%% <img src="%(testsFolder)s/../docs/images/testimg.gif" width="10%%" height="100%%" />'%dict(testsFolder=testsFolder), normal))
1439        a(Paragraph('H=100%% W=50%% <img src="%(testsFolder)s/../docs/images/testimg.gif" width="50%%" height="100%%" />'%dict(testsFolder=testsFolder), normal))
1440        a(Paragraph('H=50%% W=50%% <img src="%(testsFolder)s/../docs/images/testimg.gif" width="50%%" height="50%%" />'%dict(testsFolder=testsFolder), normal))
1441        a(Paragraph('<br/><b>Some XPreformatted tests of &lt;img width="x%" height="x%"</b>...', normal))
1442        a(XPreformatted('H=10%% <img src="%(testsFolder)s/../docs/images/testimg.gif" width="0.57in" height="10%%" />'%dict(testsFolder=testsFolder), normal))
1443        a(XPreformatted('H=50%% <img src="%(testsFolder)s/../docs/images/testimg.gif" width="0.57in" height="50%%" />'%dict(testsFolder=testsFolder), normal))
1444        a(XPreformatted('H=100%% <img src="%(testsFolder)s/../docs/images/testimg.gif" width="0.57in" height="100%%" />'%dict(testsFolder=testsFolder), normal))
1445        a(XPreformatted('H=100%% W=10%% <img src="%(testsFolder)s/../docs/images/testimg.gif" width="10%%" height="100%%" />'%dict(testsFolder=testsFolder), normal))
1446        a(XPreformatted('H=100%% W=50%% <img src="%(testsFolder)s/../docs/images/testimg.gif" width="50%%" height="100%%" />'%dict(testsFolder=testsFolder), normal))
1447        a(XPreformatted('H=50%% W=50%% <img src="%(testsFolder)s/../docs/images/testimg.gif" width="50%%" height="50%%" />'%dict(testsFolder=testsFolder), normal))
1448        a(Paragraph('<br/><b>Some CJK Paragraph tests of &lt;img width="x%" height="x%"</b>...', normal))
1449        normalCJK = ParagraphStyle('normalCJK', parent=normal, wordWrap = 'CJK')
1450        a(Paragraph('H=10%% <img src="%(testsFolder)s/../docs/images/testimg.gif" width="0.57in" height="10%%" />'%dict(testsFolder=testsFolder), normalCJK))
1451        a(Paragraph('H=50%% <img src="%(testsFolder)s/../docs/images/testimg.gif" width="0.57in" height="50%%" />'%dict(testsFolder=testsFolder), normalCJK))
1452        a(Paragraph('H=100%% <img src="%(testsFolder)s/../docs/images/testimg.gif" width="0.57in" height="100%%" />'%dict(testsFolder=testsFolder), normalCJK))
1453        a(Paragraph('H=100%% W=10%% <img src="%(testsFolder)s/../docs/images/testimg.gif" width="10%%" height="100%%" />'%dict(testsFolder=testsFolder), normalCJK))
1454        a(Paragraph('H=100%% W=50%% <img src="%(testsFolder)s/../docs/images/testimg.gif" width="50%%" height="100%%" />'%dict(testsFolder=testsFolder), normalCJK))
1455        a(Paragraph('H=50%% W=50%% <img src="%(testsFolder)s/../docs/images/testimg.gif" width="50%%" height="50%%" />'%dict(testsFolder=testsFolder), normalCJK))
1456        doc = MyDocTemplate(outputfile('test_platypus_paragraphs_autoleading.pdf'))
1457        doc.build(story)
1458
1459def alphaSortedItems(d):
1460    return (i[1] for i in sorted((j[0].lower(),j) for j in d.items()))
1461
1462def tentities(title, b, fn):
1463    from reportlab.platypus.paraparser import greeks
1464    from reportlab.platypus.doctemplate import SimpleDocTemplate
1465    from reportlab.pdfbase.pdfmetrics import stringWidth, registerFont, registerFontFamily
1466    from reportlab.pdfbase.ttfonts import TTFont
1467    from reportlab.platypus.tables import TableStyle, Table
1468    from reportlab.platypus.paragraph import Paragraph
1469    from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
1470    from reportlab import ascii
1471
1472    for v in DEJAVUSANS:
1473        registerFont(TTFont(v,v+'.ttf'))
1474    registerFontFamily(*(DEJAVUSANS[:1]+DEJAVUSANS))
1475
1476    def bu(s):
1477        return asUnicode(s) if not b else asBytes(s)
1478
1479    bt = getSampleStyleSheet()['BodyText']
1480    bt.fontName = 'DejaVuSans'
1481    doc = SimpleDocTemplate(fn)
1482    story = [Paragraph('<b>%s</b>' % asNative(title),bt)]
1483    story.extend([Paragraph(bu('&amp;%s; = <span color="red">&%s;</span>' % (k,k)), bt) for k, v in alphaSortedItems(greeks)])
1484    doc.build(story)
1485
1486class JustifyTestCase(unittest.TestCase):
1487    "Test justification of paragraphs."
1488    def testUl(self):
1489        from reportlab.platypus import BaseDocTemplate, PageTemplate, Frame, PageBegin
1490        from reportlab.lib.units import inch
1491        class MyDocTemplate(BaseDocTemplate):
1492            _invalidInitArgs = ('pageTemplates',)
1493
1494            def __init__(self, filename, **kw):
1495                self.allowSplitting = 0
1496                BaseDocTemplate.__init__(self, filename, **kw)
1497                self.addPageTemplates(
1498                        [
1499                        PageTemplate('normal',
1500                                [Frame(inch, inch, 6.27*inch, 9.69*inch, id='first',topPadding=0,rightPadding=0,leftPadding=0,bottomPadding=0,showBoundary=ShowBoundaryValue(color="red"))],
1501                                ),
1502                        ])
1503
1504        styleSheet = getSampleStyleSheet()
1505        normal = ParagraphStyle(name='normal',fontName='Times-Roman',fontSize=12,leading=1.2*12,parent=styleSheet['Normal'])
1506        normal_just = ParagraphStyle(name='normal_just',parent=normal,alignment=TA_JUSTIFY,spaceAfter=12)
1507        text0 = '''Furthermore, a subset of English sentences interesting on quite
1508independent grounds is not quite equivalent to a stipulation to place
1509the constructions into these various categories. We will bring evidence in favor of
1510The following thesis:  most of the methodological work in modern
1511linguistics can be defined in such a way as to impose problems of
1512phonemic and morphological analysis.'''
1513        story =[]
1514        a = story.append
1515        for mode in (0,1,2,3,4,5,6,7):
1516            text = text0
1517            paraStyle = normal_just
1518            if mode==1:
1519                text = text.replace('English sentences','<b>English sentences</b>').replace('quite equivalent','<i>quite equivalent</i>')
1520                text = text.replace('the methodological work','<b>the methodological work</b>').replace('to impose problems','<i>to impose problems</i>')
1521                a(Paragraph('Justified paragraph in normal/bold/italic font',style=normal))
1522            elif mode==2:
1523                text = '<b>%s</b>' % text
1524                a(Paragraph('Justified paragraph in bold font',style=normal))
1525            elif mode==3:
1526                text = '<i>%s</i>' % text
1527                a(Paragraph('Justified paragraph in italic font',style=normal))
1528            elif mode==4:
1529                text = text.replace('English ','English&nbsp;').replace('quite ','quite&nbsp;')
1530                text = text.replace(' methodological','&nbsp;methodological').replace(' impose','&nbsp;impose')
1531                a(Paragraph('Justified paragraph in normal font &amp; some hard spaces',style=normal))
1532            elif mode in (5,6,7):
1533                text = text.replace('as to impose','<br/>as to impose').replace(' most of the','<br/>most of the')
1534                text = text.replace(' grounds','<br/>grounds').replace(' various','<br/>various')
1535                if mode in (6,7):
1536                    msg = []
1537                    msg.append('justifyBreaks=1')
1538                    paraStyle = paraStyle.clone('paraStyle6',paraStyle,justifyBreaks=1)
1539                    if mode==7:
1540                        msg.append('justifyLastLine=3')
1541                        paraStyle = paraStyle.clone('paraStyle7',paraStyle,justifyLastLine=3)
1542                    msg = '(%s) ' % (' '.join(msg))
1543                else:
1544                    a(PageBreak())
1545                    msg = ' '
1546
1547                a(Paragraph('Justified%swith some &lt;br/&gt; tags' % msg,style=normal))
1548            else:
1549                a(Paragraph('Justified paragraph in normal font',style=normal))
1550
1551            a(Paragraph(text,style=paraStyle))
1552        doc = MyDocTemplate(outputfile('test_platypus_paragraphs_just.pdf'))
1553        doc.build(story)
1554
1555    def testAutoPageTemplate(self):
1556        from reportlab.platypus import BaseDocTemplate, PageTemplate, Frame, PageBegin
1557        from reportlab.lib.units import inch
1558        class onPage:
1559            def __init__(self,label):
1560                self.label = label
1561            def __call__(self,canv,doc):
1562                canv.drawString(72,72,'This is pageTemplate(%s)' % (self.label,))
1563        class MyDocTemplate(BaseDocTemplate):
1564            _invalidInitArgs = ('pageTemplates',)
1565
1566            def __init__(self, filename, **kw):
1567                self.allowSplitting = 0
1568                BaseDocTemplate.__init__(self, filename, **kw)
1569                self.addPageTemplates(
1570                        [
1571                        PageTemplate('normal',
1572                                [Frame(inch, inch, 6.27*inch, 9.69*inch, id='first',topPadding=0,rightPadding=0,leftPadding=0,bottomPadding=0,showBoundary=ShowBoundaryValue(color="red"))],
1573                                onPage = onPage('normal'),
1574                                ),
1575                        PageTemplate('auto',
1576                                [Frame(inch, inch, 6.27*inch, 9.69*inch, id='first',topPadding=0,rightPadding=0,leftPadding=0,bottomPadding=0,showBoundary=ShowBoundaryValue(color="red"))],
1577                                onPage = onPage('auto'),
1578                                autoNextPageTemplate = 'autoFollow',
1579                                ),
1580                        PageTemplate('autoFollow',
1581                                [Frame(inch, inch, 6.27*inch, 9.69*inch, id='first',topPadding=0,rightPadding=0,leftPadding=0,bottomPadding=0,showBoundary=ShowBoundaryValue(color="red"))],
1582                                onPage = onPage('autoFollow'),
1583                                ),
1584                        ])
1585        styleSheet = getSampleStyleSheet()
1586        normal = ParagraphStyle(name='normal',fontName='Times-Roman',fontSize=12,leading=1.2*12,parent=styleSheet['Normal'])
1587        story =[]
1588        a = story.append
1589        a(Paragraph('should be on page template normal', normal))
1590        a(NextPageTemplate('auto'))
1591        a(PageBreak())
1592        a(Paragraph('should be on page template auto', normal))
1593        a(PageBreak())
1594        a(DocAssert('doc.pageTemplate.id=="autoFollow"','expected doc.pageTemplate.id=="autoFollow"'))
1595        a(Paragraph('should be on page template autoFollow 1', normal))
1596        a(PageBreak())
1597        a(Paragraph('should be on page template autoFollow 2', normal))
1598        doc = MyDocTemplate(outputfile('test_platypus_paragraphs_AutoNextPageTemplate.pdf'))
1599        doc.build(story)
1600
1601    def testParaBrFlowing(self):
1602        from reportlab.platypus import BaseDocTemplate, PageTemplate, Frame, PageBegin
1603        from reportlab.lib.units import inch
1604        class MyDocTemplate(BaseDocTemplate):
1605            _invalidInitArgs = ('pageTemplates',)
1606
1607            def __init__(self, filename, **kw):
1608                self.allowSplitting = 0
1609                BaseDocTemplate.__init__(self, filename, **kw)
1610                self.addPageTemplates(
1611                        [
1612                        PageTemplate('normal',
1613                                [
1614                                Frame(inch, 4.845*inch, 3*inch, 3.645*inch, id='first',topPadding=0,rightPadding=0,leftPadding=0,bottomPadding=0,showBoundary=ShowBoundaryValue(color="red")),
1615                                Frame(4.27*inch, 4.845*inch, 3*inch, 3.645*inch, id='second',topPadding=0,rightPadding=0,leftPadding=0,bottomPadding=0,showBoundary=ShowBoundaryValue(color="red")),
1616                                Frame(inch, inch, 3*inch, 3.645*inch, id='third',topPadding=0,rightPadding=0,leftPadding=0,bottomPadding=0,showBoundary=ShowBoundaryValue(color="red")),
1617                                Frame(4.27*inch, inch, 3*inch, 3.645*inch, id='fourth',topPadding=0,rightPadding=0,leftPadding=0,bottomPadding=0,showBoundary=ShowBoundaryValue(color="red"))
1618                                ],
1619                                ),
1620                        ])
1621        styleSheet = getSampleStyleSheet()
1622        normal = ParagraphStyle(name='normal',fontName='Helvetica',fontSize=10,leading=12,parent=styleSheet['Normal'])
1623        bold = ParagraphStyle(name='bold',fontName='Helvetica-Bold',fontSize=12,leading=14.4,parent=normal)
1624        brText=u"""
1625Clearly, the natural general principle that will subsume this case is
1626not subject to a parasitic gap construction.  Presumably, most of the
1627methodological work in modern linguistics can be defined in such a way
1628as to impose the system of base rules exclusive of the lexicon.  In the
1629discussion of resumptive pronouns following (81), the fundamental error
1630of regarding functional notions as categorial is to be regarded as a
1631descriptive <span color="red">fact</span>.<br/>So far, the earlier discussion of deviance is not
1632quite equivalent to a parasitic gap construction.  To characterize a
1633linguistic level L, a case of semigrammaticalness of a different sort
1634may remedy and, at the same time, eliminate irrelevant intervening
1635contexts in selectional <span color="red">rules</span>.<br/>
1636Summarizing, then, we assume that the descriptive power of the base
1637component can be defined in such a way as to impose nondistinctness in
1638the sense of distinctive feature theory.  A lot of sophistication has
1639been developed about the utilization of machines for complex purposes,
1640the notion of level of grammaticalness delimits an abstract underlying
1641<span color="red">order</span>.<br/>To provide a constituent structure for T(Z,K), a subset of
1642English sentences interesting on quite independent grounds appears to
1643correlate rather closely with problems of phonemic and morphological
1644analysis.  For one thing, this analysis of a formative as a pair of sets
1645of features is rather different from a general convention regarding the
1646forms of the grammar.  A lot of sophistication has been developed about
1647the utilization of machines for complex purposes, a case of
1648semigrammaticalness of a different sort is not to be considered in
1649determining an important distinction in language <span color="red">use</span>.<br/>
1650We will bring evidence in favor of the following thesis:  a subset of
1651English sentences interesting on quite independent grounds delimits a
1652descriptive <span color="red">fact</span>.<br/>To characterize a linguistic level L, the notion of
1653level of grammaticalness is not to be considered in determining a
1654parasitic gap construction.  It must be emphasized, once again, that the
1655speaker-hearer's linguistic intuition can be defined in such a way as to
1656impose a stipulation to place the constructions into these various
1657categories.  On our assumptions, the appearance of parasitic gaps in
1658domains relatively inaccessible to ordinary extraction raises serious
1659doubts about problems of phonemic and morphological analysis.  For one
1660thing, the fundamental error of regarding functional notions as
1661categorial is not quite equivalent to a stipulation to place the
1662constructions into these various <span color="red">categories</span>.<br/>
1663Thus the descriptive power of the base component is unspecified with
1664respect to the strong generative capacity of the theory.  Presumably,
1665the theory of syntactic features developed earlier appears to correlate
1666rather closely with a corpus of utterance tokens upon which conformity
1667has been defined by the paired utterance test.  To provide a constituent
1668structure for T(Z,K), a case of semigrammaticalness of a different sort
1669is not to be considered in determining the ultimate standard that
1670determines the accuracy of any proposed grammar.  For any transformation
1671which is sufficiently diversified in application to be of any interest,
1672a subset of English sentences interesting on quite independent grounds
1673raises serious doubts about the requirement that branching is not
1674tolerated within the dominance scope of a complex symbol.  We will bring
1675evidence in favor of the following thesis:  an important property of
1676these three types of EC is not to be considered in determining the
1677system of base rules exclusive of the <span color="red">lexicon</span>.<br/>
1678With this clarification, the descriptive power of the base component is
1679not subject to the requirement that branching is not tolerated within
1680the dominance scope of a complex <span color="red">symbol</span>.<br/>In the discussion of
1681resumptive pronouns following (81), this selectionally introduced
1682contextual feature does not readily tolerate a parasitic gap
1683construction.  Another superficial similarity is the interest in
1684simulation of behavior, a descriptively adequate grammar does not affect
1685the structure of a corpus of utterance tokens upon which conformity has
1686been defined by the paired utterance <span color="red">test</span>.<br/>From C1, it follows that the
1687speaker-hearer's linguistic intuition is not to be considered in
1688determining the traditional practice of grammarians.  Let us continue to
1689suppose that the notion of level of grammaticalness is necessary to
1690impose an interpretation on the system of base rules exclusive of the
1691<span color="red">lexicon</span>.<br/>
1692"""
1693        story =[]
1694        a = story.append
1695        a(Paragraph('Paragraph Flowing', bold))
1696        a(Paragraph(brText, normal))
1697        doc = MyDocTemplate(outputfile('test_platypus_paragraphs_para_br_flowing.pdf'))
1698        doc.build(story)
1699
1700    def testParaNBSP(self):
1701        from reportlab.platypus import BaseDocTemplate, PageTemplate, Frame, PageBegin
1702        from reportlab.lib.units import inch
1703        class MyDocTemplate(BaseDocTemplate):
1704            _invalidInitArgs = ('pageTemplates',)
1705
1706            def __init__(self, filename, **kw):
1707                self.allowSplitting = 0
1708                BaseDocTemplate.__init__(self, filename, **kw)
1709                self.addPageTemplates(
1710                        [
1711                        PageTemplate('normal',
1712                                [
1713                                Frame(inch, 4.845*inch, 3*inch, 3.645*inch, id='first',topPadding=0,rightPadding=0,leftPadding=0,bottomPadding=0,showBoundary=ShowBoundaryValue(color="red")),
1714                                Frame(4.27*inch, 4.845*inch, 3*inch, 3.645*inch, id='second',topPadding=0,rightPadding=0,leftPadding=0,bottomPadding=0,showBoundary=ShowBoundaryValue(color="red")),
1715                                Frame(inch, inch, 3*inch, 3.645*inch, id='third',topPadding=0,rightPadding=0,leftPadding=0,bottomPadding=0,showBoundary=ShowBoundaryValue(color="red")),
1716                                Frame(4.27*inch, inch, 3*inch, 3.645*inch, id='fourth',topPadding=0,rightPadding=0,leftPadding=0,bottomPadding=0,showBoundary=ShowBoundaryValue(color="red"))
1717                                ],
1718                                ),
1719                        ])
1720        styleSheet = getSampleStyleSheet()
1721        normal = ParagraphStyle(name='normal',fontName='Helvetica',fontSize=10,leading=12,parent=styleSheet['Normal'])
1722        bold = ParagraphStyle(name='normal',fontName='Helvetica-Bold',fontSize=10,leading=12,parent=styleSheet['Normal'])
1723        registerFontFamily('Helvetica','Helvetica','Helvetica-Bold','Helvetica-Oblique','Helvetica-BoldOblique')
1724        story =[]
1725        a = story.append
1726        a(Paragraph('Paragraph Hard Space Handling', bold))
1727        a(Paragraph(''' <span backcolor="pink">ABCDEFGHI</span> ''', normal))
1728        a(Paragraph(''' <span backcolor="palegreen">&nbsp;ABCDEFGHI&nbsp;</span> ''', normal))
1729        a(Paragraph('''<span backcolor="lightblue">&nbsp;</span>''', normal))
1730        a(Paragraph('''<span backcolor="lightblue">&nbsp;</span><span backcolor="palegreen">&nbsp;</span> ''', normal))
1731        a(Paragraph('''<span backcolor="pink"><b>A</b></span><span backcolor="lightblue"> </span><span backcolor="pink"><b>B</b></span>''', normal))
1732        a(Paragraph('''<span backcolor="pink"><b>A</b></span> <span backcolor="lightblue"> </span><span backcolor="pink"><b>B</b></span>''', normal))
1733        a(Paragraph('''<span backcolor="pink"><b>A</b></span><span backcolor="lightblue">&nbsp;</span><span backcolor="pink"><b>B</b></span>''', normal))
1734        a(Paragraph('''<span backcolor="pink"><b>A</b></span> <span backcolor="lightblue">&nbsp;</span><span backcolor="pink"><b>B</b></span>''', normal))
1735        doc = MyDocTemplate(outputfile('test_platypus_paragraphs_nbsp.pdf'))
1736        doc.build(story)
1737
1738    @unittest.skipUnless(haveDejaVu(),'s')
1739    def testParaEntities(self):
1740        tentities(b'unicode formatted paragraphs',False,outputfile('test_platypus_unicode_paragraph_entities.pdf'))
1741        tentities(b'byte formatted paragraphs',True,outputfile('test_platypus_bytes_paragraph_entities.pdf'))
1742
1743    def testXPreUnderlining(self):
1744        styleSheet = getSampleStyleSheet()
1745        bt = styleSheet['BodyText']
1746        story = [
1747                XPreformatted("xpre<u><font size='1' color='red'>SS</font>        </u>",bt),
1748                XPreformatted("xpre<u><font size='1' color='red'>SS</font>        </u>|",bt),
1749                XPreformatted("<u><font size='1' color='red'>SS</font>        </u>",bt),
1750                XPreformatted("<u><font size='1' color='red'>SS</font>        </u>|",bt),
1751                ]
1752        doc = MyDocTemplate(outputfile('test_platypus_XPreUnderlining.pdf'))
1753        doc.build(story)
1754
1755#noruntests
1756def makeSuite():
1757    return makeSuiteForClasses(ParagraphCorners,SplitFrameParagraphTest,FragmentTestCase, ParagraphSplitTestCase, ULTestCase, JustifyTestCase,
1758            AutoLeadingTestCase)
1759
1760#noruntests
1761if __name__ == "__main__":
1762    unittest.TextTestRunner().run(makeSuite())
1763    printLocation()
1764