[reportlab-users] Truncate table cell content to end with ellipsis

Robin Becker robin at reportlab.com
Tue Apr 14 10:49:29 EDT 2009


Yves Forkl wrote:

> Using Platypus (RLTK 2.3), I would like to make sure that the contents

> of a table cell of a known fixed width do only consist of no more than a

> single line that is truncated if too long. An ellipsis ("...", or rather

> U+2026) should be substituted to the missing part. The contents are

> passed as a Paragraph so I can use HTML formatting, its font size and

> family are known of course.

>

> I already have a kind of hack which involves a function that

> incrementally reduces the content to be inserted (using the stringWidth

> method on a dummy canvas) until it fits, based on some heuristics.

>

> But I would much prefer a clean and neat solution. I know about

> KeepInFrame and its truncate method, but looking at the unit test in

> test_platypus_pleaseturnover.py I can't figure out how to proceed,

> especially to push the ellipsis in.

>

> Any hints?

>

........
it's not exactly clear what you want. If you want to ensure that a particular
paragraph is only of a specified width then you need to mess around with the
paragraph wrapping stuff if you need the html style formatting. If in fact you
just need a string that's truncated and the end replaced with '...' then it
might be better to think about messing with a Table class. Unfortunately we
don't have an intermediate thing that knows about different fonts/sizes without
the full horror of the paragraph class.

In the paragraph approach you can do

w,h = p.wrap(availWidth,0x7fffffff)

to create the broken lines structure and see if the paragraph is in fact too
long. Alternatively just call the paragraph's breakLines method.

eg

>>> from reportlab.platypus import paragraph
>>> from reportlab.lib.styles import getSampleStyleSheet
>>> styles=getSampleStyleSheet()
>>> normal=getSampleStyleSheet()['Normal']
>>> P=paragraph.Paragraph('This is my long and tedious text',normal)
>>> P.width=72
>>> blpara=P.breakLines([72])
>>> len(blpara.lines)
2
>>> blpara.lines[0]
(9.4899999999999949, ['This', 'is', 'my', 'long'])

The above shows what happens for a simple single style paragraph. Things will be
more interesting and the blpara stuff more complex if you switch styles eg

>>> P=paragraph.Paragraph('This <i>is</i> my long and tedious text',normal)
>>> P.width=72
>>> blpara=P.breakLines([72])
>>> len(blpara.lines)
2
>>> blpara.lines[0]
FragLine(ascent=6.8300000000000001, descent=-2.1699999999999999,
extraSpace=9.4899999999999949, fontSize=10, lineBreak=False, wordCount=4,
words=[ABag(bold=0, fontName='Times-Roman', fontSize=10, greek=0, italic=0,
link=None, rise=0, strike=0, sub=0, super=0, text='This ',
textColor=Color(0,0,0), underline=0), ABag(bold=0, fontName='Times-Italic',
fontSize=10, greek=0, italic=1, link=None, rise=0, strike=0, sub=0, super=0,
text='is ', textColor=Color(0,0,0), underline=0), ABag(bold=0,
fontName='Times-Roman', fontSize=10, greek=0, italic=0, link=None, rise=0,
strike=0, sub=0, super=0, text='my long', textColor=Color(0,0,0), underline=0)])
>>>

however, both allow you to estimate where the paragraph wishes to wrap the first
line and how much space is left over. In the above 9.4899999999999949 is the
amount of extra space that is left after putting the words onto the line (with
the standard spacing). In the first case a simple list suffices to hold the
strings, but in the complex case the words attribute of the FragLine holds
fragment words. It should be easy to determine that 'my long' is the last thing
in each case.

A bit of messing with the width of your '...' char would allow some kind of
outcome to be determined (probably using XPreformatted).


As a last resort some kind of special flowable could be used that knows how to
calculate widths of its outcome and then truncate with ....
--
Robin Becker


More information about the reportlab-users mailing list