reportlab SimpleDocTemplate - 设置 table 的固定高度,行数可变
reportlab SimpleDocTemplate - set fixed height of table with variable number of rows
我尝试在 Python
中使用 reportlab
生成 PDF 发票。
发票将只有一页,并且绝不会比单页上的 space 更详细;我的代码在生成 PDF 之前检查最大数量的细节。
现在我正在使用 SimpleDocTemplate
将所有内容添加到页面,并使用 Table
构建详细信息。
这是一个简化的代码示例:
from reportlab.lib.units import mm
from reportlab.platypus import Paragraph, Spacer, Table, TableStyle
from reportlab.platypus import SimpleDocTemplate
# add invoice header
flowable_list = [
Spacer(1, 5*mm),
Paragraph('Date: ...', pg_style_1),
Spacer(1, 5*mm),
]
# add invoice details
detail_list = [
('Item 1', 8, 45),
('Item 2', 1, 14),
]
row_list = [
[
Paragraph(desc, pg_style_1),
quantity,
amount,
]
for desc, quantity, amount in detail_list]
story.append(
Table(
data=row_list,
colWidths=[100*mm, 40*mm, 40*mm],
style=TableStyle([
('VALIGN', (0, 0), (-1, -1), 'TOP'),
... some other options ...
])))
# add invoice footer; this should be at a specific position on the page
flowable_list.append(Spacer(1, 5*mm))
flowable_list.append(Paragraph('Total: 0', pg_style_1))
# build PDF
buffer = io.BytesIO()
doc = SimpleDocTemplate(buffer)
doc.build(flowable_list)
我的问题:底部的总金额每次都必须在特定位置(类似于底部的x*mm
),但可以有可变数量的细节导致细节 table 具有非固定高度。
我目前的解决方案:在table之后加一个Spacer
;这个 spacer 的高度必须根据 table 中的行数来计算(行数越多意味着 spacer 会更小;行数越少产生更大的 spacer).但是,如果其中一行环绕并占用比单行更多的space,这将失败。
我的问题:有没有办法给details设置一个固定的高度table,不管行数多少,还是继续用SimpleDocTemplate
?
This similar question 展示了一个解决方案,可以手动将所有内容绘制到 canvas,但如果可能的话,我想要一种继续使用 SimpleDocTemplate
的方法。
我还没有找到更好的方法,所以我会添加我目前的解决方案;也许它会对以后 reader 遇到类似问题有所帮助。
...
story.append(
Table(
data=row_list,
colWidths=[100*mm, 40*mm, 40*mm],
style=TableStyle([
('VALIGN', (0, 0), (-1, -1), 'TOP'),
... some other options ...
])))
# calculate real height of details table, so we can add a 'Spacer' for the missing
# part to have the invoice totals at the correct height at the bottom of the page
_, real_height = story[-1].wrap(doc_width, doc_height)
height_reserved_for_details = 100*mm
remaining_height = max(0, height_reserved_for_details - real_height)
story.append(Spacer(1, remaining_height))
flowable_list.append(Paragraph('Total: 0', pg_style_1))
试试这个利用 "TopPadder" 的可行示例,(结果是 Total 被推到 Frame 的底部,我假设你可以在它下面添加一个垫子,以控制从底部):
########################################################################
from reportlab.lib.units import mm
from reportlab.platypus import Paragraph, Spacer, Table, TableStyle
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.platypus import SimpleDocTemplate
from reportlab.platypus.flowables import TopPadder
import io
def create_pdf():
styles=getSampleStyleSheet()
# add invoice header
flowable_list = [
Spacer(1, 5 * mm),
Paragraph(f'Date: ...', styles['Normal']),
Spacer(1, 5 * mm),
]
# add invoice details
detail_list = [
('Item 1', 8, 45),
('Item 2', 1, 14),
]
row_list = [
[
Paragraph(f'desc', styles['Normal']),
# quantity,
# amount,
]
for desc, quantity, amount in detail_list]
story = []
story.append(
Table(
data=row_list,
colWidths=[100 * mm, 40 * mm, 40 * mm],
style=TableStyle([
('VALIGN', (0, 0), (-1, -1), 'TOP'),
# ... some other options...
])))
# add invoice footer; this should be at a specific position on the page
flowable_list.append(Spacer(1, 5 * mm))
flowable_list.append(TopPadder(Paragraph(f'Total: 0', styles['Normal'])))
# build PDF
buffer = io.BytesIO()
doc = SimpleDocTemplate("test2.pdf")
doc.build(flowable_list)
# ----------------------------------------------------------------------
if __name__ == "__main__":
create_pdf() # Printing the pdf
我尝试在 Python
中使用 reportlab
生成 PDF 发票。
发票将只有一页,并且绝不会比单页上的 space 更详细;我的代码在生成 PDF 之前检查最大数量的细节。
现在我正在使用 SimpleDocTemplate
将所有内容添加到页面,并使用 Table
构建详细信息。
这是一个简化的代码示例:
from reportlab.lib.units import mm
from reportlab.platypus import Paragraph, Spacer, Table, TableStyle
from reportlab.platypus import SimpleDocTemplate
# add invoice header
flowable_list = [
Spacer(1, 5*mm),
Paragraph('Date: ...', pg_style_1),
Spacer(1, 5*mm),
]
# add invoice details
detail_list = [
('Item 1', 8, 45),
('Item 2', 1, 14),
]
row_list = [
[
Paragraph(desc, pg_style_1),
quantity,
amount,
]
for desc, quantity, amount in detail_list]
story.append(
Table(
data=row_list,
colWidths=[100*mm, 40*mm, 40*mm],
style=TableStyle([
('VALIGN', (0, 0), (-1, -1), 'TOP'),
... some other options ...
])))
# add invoice footer; this should be at a specific position on the page
flowable_list.append(Spacer(1, 5*mm))
flowable_list.append(Paragraph('Total: 0', pg_style_1))
# build PDF
buffer = io.BytesIO()
doc = SimpleDocTemplate(buffer)
doc.build(flowable_list)
我的问题:底部的总金额每次都必须在特定位置(类似于底部的x*mm
),但可以有可变数量的细节导致细节 table 具有非固定高度。
我目前的解决方案:在table之后加一个Spacer
;这个 spacer 的高度必须根据 table 中的行数来计算(行数越多意味着 spacer 会更小;行数越少产生更大的 spacer).但是,如果其中一行环绕并占用比单行更多的space,这将失败。
我的问题:有没有办法给details设置一个固定的高度table,不管行数多少,还是继续用SimpleDocTemplate
?
This similar question 展示了一个解决方案,可以手动将所有内容绘制到 canvas,但如果可能的话,我想要一种继续使用 SimpleDocTemplate
的方法。
我还没有找到更好的方法,所以我会添加我目前的解决方案;也许它会对以后 reader 遇到类似问题有所帮助。
...
story.append(
Table(
data=row_list,
colWidths=[100*mm, 40*mm, 40*mm],
style=TableStyle([
('VALIGN', (0, 0), (-1, -1), 'TOP'),
... some other options ...
])))
# calculate real height of details table, so we can add a 'Spacer' for the missing
# part to have the invoice totals at the correct height at the bottom of the page
_, real_height = story[-1].wrap(doc_width, doc_height)
height_reserved_for_details = 100*mm
remaining_height = max(0, height_reserved_for_details - real_height)
story.append(Spacer(1, remaining_height))
flowable_list.append(Paragraph('Total: 0', pg_style_1))
试试这个利用 "TopPadder" 的可行示例,(结果是 Total 被推到 Frame 的底部,我假设你可以在它下面添加一个垫子,以控制从底部):
########################################################################
from reportlab.lib.units import mm
from reportlab.platypus import Paragraph, Spacer, Table, TableStyle
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.platypus import SimpleDocTemplate
from reportlab.platypus.flowables import TopPadder
import io
def create_pdf():
styles=getSampleStyleSheet()
# add invoice header
flowable_list = [
Spacer(1, 5 * mm),
Paragraph(f'Date: ...', styles['Normal']),
Spacer(1, 5 * mm),
]
# add invoice details
detail_list = [
('Item 1', 8, 45),
('Item 2', 1, 14),
]
row_list = [
[
Paragraph(f'desc', styles['Normal']),
# quantity,
# amount,
]
for desc, quantity, amount in detail_list]
story = []
story.append(
Table(
data=row_list,
colWidths=[100 * mm, 40 * mm, 40 * mm],
style=TableStyle([
('VALIGN', (0, 0), (-1, -1), 'TOP'),
# ... some other options...
])))
# add invoice footer; this should be at a specific position on the page
flowable_list.append(Spacer(1, 5 * mm))
flowable_list.append(TopPadder(Paragraph(f'Total: 0', styles['Normal'])))
# build PDF
buffer = io.BytesIO()
doc = SimpleDocTemplate("test2.pdf")
doc.build(flowable_list)
# ----------------------------------------------------------------------
if __name__ == "__main__":
create_pdf() # Printing the pdf