I'm using the ReportLab package for python to create a table in a PDF file, but the table is too wide for the page and the first and last columns cut off. Here is a sample of the code I'm using.
t = Table(data, style=[("INNERGRID", (0,0), (-1,-1), 0.25, colors.black),("BOX", (0,0), (-1,-1), 0.25, colors.black)])
I've tried using splitbyRow and similar parameters but none seem to be working. How can I easily make the table fit the page?
you can get your page width using the frame of that page. When you use the colWidths keyword you should know how much space you have available:
table = ar.Table(data, colWidths=columnWidths)#there is also rowHeights=
table.setStyle(tableStyle)
As the table draws itself at build, you have to configure the Table so that it uses the available width of the frame it is going to be drawn.
Those colWidths can be computed so that they use the available space. To show this I would have to use an extended example, I'll try without:
First you should define your own document class, that inherits from BaseDocTemplate:
class MyDocTemplate(BaseDocTemplate):
def __init__(self, filename):
BaseDocTemplate.__init__(self, filename)
#there are more keyword
#arguments you can pass
#pagesize=A4,
#leftMargin=leftMargin,
#rightMargin=rightMargin,
#topMargin=topMargin,
#bottomMargin=bottomMargin,
#title=title,
#author=author,
#subject=subject,
#creator=creator)
then if you create your document:
doc = MyDocTemplate(fileName)
you can get the width of a frame by a self defined function inside of your document class:
frame = doc.getFrame('FirstL')
width = frame._aW
this function can look like:
def getFrame(self,framename,orientation="Portrait"):
"""
returns frame
frame._x1,frame._y1
frame._width,frame._height
frame._leftPadding,frame._bottomPadding
frame._rightPadding,frame._topPadding
"""
f = attrgetter("id")
frame = None
for temp in self.pageTemplates[::-1]:
if f(temp) == framename:
#print( temp.id )
for frame in temp.frames:
print( frame.id )
if f(frame) == orientation:
return frame
reportlab is quite complex, I tried explaining without giving an extensive explanation of how to register page templates as a list on your document class
pageTemplates is a list you can add page templates to:
templates.append( PageTemplate(id='FirstL',frames=frameL,onPage=onFirstPage,pagesize=pagesize) )
onFirstPage is a function, that uses the following scheme:
def drawFirstPage(canv,doc):
"""
This is the Title Page Template (Portrait Oriented)
"""
canv.saveState()
#set Page Size or do some other things on the canvas
frame = doc.getFrame('FirstP')
canv.setPageSize( frame.pagesize )
canv.setFont(_baseFontName,doc.fontSize)
canv.restoreState()
When you add page templates like this, the suggested function should work:
doc.addPageTemplates(templates)
Table is getting cropped becuase it exeeds the page width . The solution is to create custom page size for pdf.
from reportlab.platypus import SimpleDocTemplate , Table
pagesize = (20 * inch, 10 * inch)
doc = SimpleDocTemplate('sample.pdf', pagesize=pagesize)
data = [ ['sarath', 'indiana', 'usa'],
['jose', 'indiana', 'shhs'] ] # give data as lists with lists.
table = Table(data)
elems = []
elems.append(table)
doc.build(elems)
you could add another setting on the TableData called colWidths=[250,250]... here is a snippet of my code where i set the width and some styles as well:
DetailedWorkData = [["Detailed Work Information",""],
["Procedure: XXXXX", "Planned by: XXXXXX"]]
b = Table(DetailedWorkData, colWidths=[285,285])
b.hAlign = "LEFT"
b.setStyle(TableStyle([
('BACKGROUND',(0,0),(-1,0),HexColor("#C0C0C0")),
('GRID',(0,1),(-1,-1),0.01*inch,(0,0,0,)),
('FONT', (0,0), (-1,0), 'Helvetica-Bold')]))
Story.append(b)
If you look at the 3rd line i have a colWidths where i specify how wide i want the columns to be then i set the style a couple of lines later.
I've done a function that once it creates a table it checks if the width is too large to fit in the page. If it is too wide it calculates how many times that table can fit in the page and divides the table.
from reportlab.platypus import Table, Paragraph
def df2table(df, style, alignment = 'CENTER', padding = [72,72,100,18], page_type = A4):
table= Table([[Paragraph(col) for col in df.columns]] + df.values.tolist(), style= style, hAlign = alignment)
if (page_type[0]-padding[0]-padding[1])-table.minWidth() < 0:
times_spread = int(table.minWidth()/(page_type[0]-padding[0]-padding[1]))+1
n_columns = len(df.columns)
number_on_columns = get_number_on_columns(n_columns,times_spread)
table = [df2table(df.loc[:,df.columns[0:number_on_columns[0]]],style)]
min_index = 0
max_index = number_on_columns[0]
for i in range(times_spread-1):
min_index = min_index + number_on_columns[i]
max_index = max_index + number_on_columns[i+1]
table_small = df2table(df.loc[:,df.columns[min_index:max_index]]
table.append(table_small,style))
return table
This code need the function that calculates how many tables will be on each row:
def get_number_on_columns(n_columns,times_spread):
n_with_less = n_columns%times_spread
n_with_more = times_spread-n_with_less
if n_with_less != 0:
n_on_more = int(n_columns/times_spread)+1
n_on_less = int((n_columns - n_on_more*n_with_more)/n_with_less)
else:
n_on_more = int(n_columns/times_spread)
n_on_less = 0
number_on_columns = [n_on_more]*n_with_more+[n_on_less]*n_with_less
return number_on_columns
The function df2table will return a list of tables if the table needs to be split up, otherwise it will return jus a Table. So if you get by this manner the tables and don't know whether it is a table or many i write the tables in the document by a simple function that goes like this:
def put_tables_in_doc(tablas,story):
if type(tablas) == list:
for tabla in tablas:
story.append(tabla)
story.append(Spacer(10,10))
else:
story.append(tablas)
And in the main code you call all this just like this:
tabla_fallos = data_to_check.loc[:,['TIME','REBOOT_ERRORSFAILS','ELECTRIC_ERRORS']]
story.append(Paragraph('Error table:', styles['Heading2']))
tabla = df2table(tabla_fallos, style, table_alingment)
put_tables_in_doc(tabla,story)
To finalize i'll comment that it is not flawless, if the name of the title is longer than the width it will enter on a loop and raise an error. It probably has more flaws but it has worked for me.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.