Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python & ReportLab: wrong column width and alignment in PDF table

I use Python 2.7 (iOS Pythonista App) and the reportlab 2.7 module to create a PDF with a table. Everything works fine. RepotLab formats the width of the columns automatically. But in two cases I can't understand why reportlab formats the output the way it does and how I can get the format I want.

Case 1: Everything almost as I want …

+-----+----------+---------------+----------+---------------------------------------------------------------------------------------------------------------+
| Day | Date     | Time          | Duration | Notes                                                                                                         |
+-----+----------+---------------+----------+---------------------------------------------------------------------------------------------------------------+
| Tue | 01.04.14 | 14:00 - 17:15 | 3.25     | Here are some notes.                                                                                          |
+-----+----------+---------------+----------+---------------------------------------------------------------------------------------------------------------+
| Wed | 02.04.14 | 18:00 - 20:15 | 2.25     | Sometime these notes are a little longer text so there must be a line break to let the whole note be visible! |
+-----+----------+---------------+----------+---------------------------------------------------------------------------------------------------------------+
| Thu | 02.04.14 | 14:00 - 17:15 | 3.25     | And sometimes these notes are only a few words.                                                               |
+-----+----------+---------------+----------+---------------------------------------------------------------------------------------------------------------+

Case 2: I try to line up the float points

With the help of

TableStyle([('ALIGN', (3,1), (3,-1), 'DECIMAL'),])

I tried to get every float point exactly in one vertical line. It works but in the Duration column everything is so (hard) aligned to the right that a part of the number in the Notes column (and there is much space to the left of the Duration values). It seems as if the right padding is set to the point or the number before the point and not to the whole value.

+-----+----------+---------------+-----------+-------------------------------------------------+
| Day | Date     | Time          | Duration  | Notes                                           |
+-----+----------+---------------+-----------+-------------------------------------------------+
| Tue | 01.04.14 | 14:00 - 17:15 |        3.2|5Here are some notes.                            |
+-----+----------+---------------+-----------+-------------------------------------------------+
| Wed | 02.04.14 | 14:00 - 17:15 |        3.2|5And sometimes these notes are only a few words. |
+-----+----------+---------------+-----------+-------------------------------------------------+

With the help of

TableStyle([('RIGHTPADDING', (3,1), (3,-1), 18),])

I got get better results, but I think there should be a better way!?

Case 3: Try to wrap the notes text.

When I use a Paragraph to insert the notes texts, I get wrapping text, but the column width is astonishing. I can't get the following example exactly as the real output is.

The whole table width fits to the document ( DIN A4 without the margins). But it seems as if every column gets a equal width. So that the Day, Date, Time and Duration columns are much wider than they need to be and the Notes column is much narrower than it need to be. These two things I couldn't present in the following example table as I wish, but I trust in your imagination ;-) .

+-----+----------+---------------+----------+----------------------+
| Day | Date     | Time          | Duration | Notes                |
+-----+----------+---------------+----------+----------------------+
| Tue | 01.04.14 | 14:00 - 17:15 | 3.25     | Here are some notes. |
+-----+----------+---------------+----------+----------------------+
| Wed | 02.04.14 | 18:00 - 20:15 | 2.25     | Sometime these       |
|     |          |               |          | notes are a          |
|     |          |               |          | little longer        |
|     |          |               |          | text so there        |
|     |          |               |          | must be a line       |
|     |          |               |          | break to let         |
|     |          |               |          | the whole note       |
|     |          |               |          | be visible!          |
+-----+----------+---------------+----------+----------------------+
| Thu | 02.04.14 | 14:00 - 17:15 | 3.25     | And sometimes        |
|     |          |               |          | these notes are      |
|     |          |               |          | only a few words.    |
+-----+----------+---------------+----------+----------------------+

And here is the code I use:

import pipista
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.platypus import SimpleDocTemplate, Paragraph, Table, TableStyle

styleSheet = getSampleStyleSheet()

def makeReportData(n):
  monthreport = []
  monthreport += ('Day', '', 'Time', 'Duration', 'Notes'),
  monthreport += ('Tue', '01.04.14', '14:00 - 17:15', '3.25', n[0]),
  monthreport += ('Wed', '02.04.14', '14:00 - 17:15', '3.25', n[1]),
  monthreport += ('Thu', '03.04.14', '14:00 - 17:15', '3.25', n[2]),
  return monthreport

notes = ['Here are some notes.',
         'Sometime these notes are a little longer text so there must be a line break to let the whole note be visible!',
         'And sometimes these notes are only a few words.']

paranote = []
for note in notes:
  paranote += (Paragraph(note, styleSheet["BodyText"]),)

tstyle = [('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
          ('BOX', (0,0), (-1,-1), 0.25, colors.black),]

case1 = []
t = Table(makeReportData(notes))
t.setStyle(TableStyle(tstyle))
case1.append(t)
doc = SimpleDocTemplate('Table normal.pdf', pagesize = A4)
doc.build(case1)

case2 = []
tstyledez = tstyle + [('ALIGN', (3,1), (3,-1), 'DECIMAL'),]
t.setStyle(TableStyle(tstyledez))
case2.append(t)
doc = SimpleDocTemplate('Table align decimal.pdf', pagesize = A4)
doc.build(case2)

case3 = []
t = Table(makeReportData(paranote))
tstylepara = tstyle + [('VALIGN',(0,1),(3,-1),'TOP'),]
t.setStyle(TableStyle(tstylepara))
case3.append(t)
doc = SimpleDocTemplate('Table Paragraph.pdf', pagesize = A4)
doc.build(case3)

I hope someone can push me in the right direction.

like image 230
nealtz Avatar asked May 08 '14 16:05

nealtz


1 Answers

OK, RTM is always a good idea! From the ReportLab documentation:

If a cell value is a Flowable or list of Flowables these must either have a determined width or the containing column must have a fixed width.

Solution:

from reportlab.lib.units import mm

t = Table(makeReportData(paranote), colWidths=(None, None, None, None, 100*mm))

I thought maybe the problem with the DECIMAL align (case 2) could be solved in the same way but that doesn't work.

like image 83
nealtz Avatar answered Sep 28 '22 03:09

nealtz