处理和调试Reportlab的'LayoutError
我最近在用reportlab处理一些复杂的PDF输出。大部分情况下都没问题,但有时候会遇到布局错误,这通常是因为某些元素太大了。
调试这些问题挺困难的,因为我得到的信息往往就像这样:
Flowable <Table@0x104C32290 4 rows x 6 cols> with cell(0,0) containing
'<Paragraph at 0x104df2ea8>Authors'(789.0 x 1176) too large on page 5 in frame 'normal'(801.543307087 x 526.582677165*) of template 'Later'
这真的没什么帮助。我希望能知道一些更好的调试和测试方法,来处理这种情况。
- 有没有办法查看一个出错的PDF?也就是说,能显示出布局错误的PDF,这样我可以更容易地看出问题所在。
- 有没有办法在reportlab中添加一些功能,以更好地处理这些错误?而不是直接让整个PDF失败?
- 还有其他建议吗?比如如何改善、测试和处理这些问题。
我没有具体的例子,所以这些建议比较笼统。上面提到的异常我已经解决了,但主要是通过反复试验(也就是猜测然后看看结果)。
2 个回答
确保你没有重复使用任何可流动的对象(也就是说,使用相同的模板部分来渲染多个版本的文档)。这是ReportLab不支持的,可能会导致错误。
原因似乎是,ReportLab在布局时会给这些对象设置一个属性,用来表示需要把它们移动到单独的页面。如果这个对象需要被移动两次,就会出现错误。这些属性在你渲染文档时不会被重置,所以看起来好像一个对象被移动到了两个不同的页面,其实并不是这样。
我之前通过手动重置这个属性来解决过这个问题(我现在记不清具体名字了;好像是'_deferred'之类的),但正确的方法是,在渲染完文档后,丢弃你用来渲染的任何对象。
我们在使用Reportlab格式化一些原本是HTML的内容时遇到了问题,有时候这些HTML太复杂了。解决办法(我不想占这个功劳,这个主意是Reportlab的团队想出来的)是当错误发生时,直接把错误信息输出到PDF里。
这样你就能在正确的上下文中看到问题的原因。你可以进一步扩展这个方法,输出更多的异常细节,但在我们的情况下,由于我们的问题是把HTML转换成RML,我们只需要显示我们的输入内容:
这个模板包含了以下内容:
{{script}}
#This section contains python functions used within the rml.
#we can import any helper code we need within the template,
#to save passing in hundreds of helper functions at the top
from rml_helpers import blocks
{{endscript}}
然后后面还有一些模板的部分,比如:
{{if equip.specification}}
<condPageBreak height="1in"/>
<para style="h2">Item specification</para>
{{blocks(equip.specification)}}
{{endif}}
在rml_helpers.py文件里,我们有:
from xml.sax.saxutils import escape
from rlextra.radxml.html_cleaner import cleanBlocks
from rlextra.radxml.xhtml2rml import xhtml2rml
def q(stuff):
"""Quoting function which works with unicode strings.
The data from Zope is Unicode objects. We need to explicitly
convert to UTF8; then escape any ampersands. So
u"Black & Decker drill"
becomes
"Black & Decker drill"
and any special characters (Euro, curly quote etc) end up
suitable for XML. For completeness we'll accept 'None'
objects as well and output an empty string.
"""
if stuff is None:
return ''
elif isinstance(stuff,unicode):
stuff = escape(stuff.encode('utf8'))
else:
stuff = escape(str(stuff))
return stuff.replace('"','"').replace("'", ''')
def blocks(txt):
try:
txt2 = cleanBlocks(txt)
rml = xhtml2rml(txt2)
return rml
except:
return '<para style="big_warning">Could not process markup</para><para style="normal">%s</para>' % q(txt)
所以任何对于xhtml2rml
来说太复杂的内容都会抛出一个异常,并在输出中被替换成一个大警告,内容是“无法处理标记”,后面跟着导致错误的标记,并且这些标记会被转义,以便它们能以字面形式显示出来。
然后我们只需要记得在输出的PDF中查找这个错误信息,并相应地修正输入内容。