Skip to main content
 首页 » 编程设计

python之Reportlab:更改边距后重建文档

2025年05月04日15开发

在我为 friend 的公司编写的程序中,我正在使用 reportlab 模块为各种报告构建 PDF 文档。在大多数情况下,报告可以包含在一个页面上,但在极少数情况下可能会跨越两个页面。在这些罕见的情况下,我想做的是用较小的顶部和底部边距重新格式化页面,看看我是否可以让它适合单个页面。如果没有,我将只使用最小边距并让它跨越两页。

为了构建报告,我正在创建 SimpleDocTemplate 类的实例。将我所有的可流动对象传递给 build 方法后,我发现我可以查询 page 属性以查看它使用了多少页。这是我尝试的第一件事:

parts = [] 
path = "/path/to/my/file.pdf" 
# ... a bunch of code that appends various flowables to 'parts' 
doc = SimpleDocTemplate(path, pagesize=landscape(letter)) 
doc.build(parts) 
# Shrink the margins while it's more than one page and the margins are above zero 
while doc.page > 1 and not any([doc.topMargin <= 0, doc.bottomMargin <= 0]): 
    # Decrease the page margins and rebuild the page 
    doc.topMargin -= inch * .125 
    doc.bottomMargin -= inch * .125 
    doc.build(parts) 
# If the margins are nil and we're still at 2 or more pages, use minimal margins 
if doc.page > 1: 
    doc.topMargin = inch * .25 
    doc.bottomMargin = inch * .25 
    doc.build(parts) 

我假设在更改边距后调用具有相同部分的 build 方法会重新计算所有内容。然而,经过反复试验,我了解到传递给 build 方法的 parts 列表在构建过程中基本上被清除干净,留下 parts 作为一个空列表。一旦它再次传递回 build 方法,它就会创建一个零页的文档。

为了解决这个问题,我尝试使用 parts 列表的副本构建文档:

doc.build(parts[:]) 

这导致了一些奇怪的异常,所以我尝试使用 copy 模块进行深度复制:

doc.build(copy.deepcopy(parts)) 

这没有抛出任何异常,但也没有改变边距。

有点绝望,我深入研究了 SimpleDocTemplate 属性并找到了一个名为 _calc 的方法。考虑到这可能会重新计算页面,我尝试在更改页边距后调用它。它没有抛出任何异常,但也不起作用。

我设法让它工作的唯一方法是使用 deepcopy 过程并在每次调整边距时构建全新的文档:

doc = SimpleDocTemplate(path, pagesize=landscape(letter)) 
doc.build(copy.deepcopy(parts)) 
# Shrink the margins while it's more than one page and the margins are above zero 
while doc.page > 1 and not any([doc.topMargin <= 0, doc.bottomMargin <= 0]): 
    doc.topMargin -= inch * .125 
    doc.bottomMargin -= inch * .125 
    doc = SimpleDocTemplate(path, pagesize=landscape(letter), 
                            topMargin = doc.topMargin, 
                            bottomMargin = doc.bottomMargin) 
    doc.build(copy.deepcopy(parts)) 
# If the margins are nil and we're still at 2 or more pages, use minimal margins 
if doc.page > 1: 
    doc.topMargin = inch * .25 
    doc.bottomMargin = inch * .25 
    doc = SimpleDocTemplate(path, pagesize=landscape(letter), 
                            topMargin = doc.topMargin, 
                            bottomMargin = doc.bottomMargin) 
    doc.build(copy.deepcopy(parts)) 

但是,走那条路感觉像是做了很多不必要的工作。我宁愿只更改文档边距并告诉文档使用这些新值重建自身,但我不知道如何做。这可能吗?

请您参考如下方法:

一个很有趣的问题。但是,我相信您已经在 ReportLab 中找到了执行此操作的正确方法。构建过程是您可以对文档执行的一次性操作,因为它会产生您无法返回的副作用。值得庆幸的是,正如您已经发现的那样,尽管有些烦人,但做您想做的事并不难。