移植到 Python3:PyPDF2 mergePage() 给出 TypeError
Porting to Python3: PyPDF2 mergePage() gives TypeError
我在 windows 7.Python 上使用 Python 3.4.2 和 PyPDF2 1.24(如果有帮助,也使用 reportlab 3.1.44)。
我最近从 Python 2.7 升级到 3.4,并且正在移植我的代码。此代码用于创建一个空白的 pdf 页面,其中嵌入了链接(使用 reportlab)并将其与现有的 pdf 页面合并(使用 PyPDF2)。我在 reportlab 中遇到了一个问题,即保存 canvas 使用的 StringIO 需要更改为 BytesIO,但在这样做之后我 运行 出现了这个错误:
Traceback (most recent call last):
File "C:\cms_software\pdf_replica\builder.py", line 401, in merge_pdf_files
input_page.mergePage(link_page)
File "C:\Python34\lib\site-packages\PyPDF2\pdf.py", line 2013, in mergePage
self.mergePage(page2)
File "C:\Python34\lib\site-packages\PyPDF2\pdf.py", line 2059, in mergePage
page2Content = PageObject._pushPopGS(page2Content, self.pdf)
File "C:\Python34\lib\site-packages\PyPDF2\pdf.py", line 1973, in _pushPopGS
stream = ContentStream(contents, pdf)
File "C:\Python34\lib\site-packages\PyPDF2\pdf.py", line 2446, in __init
stream = BytesIO(b_(stream.getData()))
File "C:\Python34\lib\site-packages\PyPDF2\generic.py", line 826, in getData
decoded._data = filters.decodeStreamData(self)
File "C:\Python34\lib\site-packages\PyPDF2\filters.py", line 326, in decodeStreamData
data = ASCII85Decode.decode(data)
File "C:\Python34\lib\site-packages\PyPDF2\filters.py", line 264, in decode
data = [y for y in data if not (y in ' \n\r\t')]
File "C:\Python34\lib\site-packages\PyPDF2\filters.py", line 264, in
data = [y for y in data if not (y in ' \n\r\t')]
TypeError: 'in <string>' requires string as left operand, not int
这是回溯提到的行和上面的行:
link_page = self.make_pdf_link_page(pdf, size, margin, scale_factor, debug_article_links)
if link_page != None:
input_page.mergePage(link_page)
以下是该 make_pdf_link_page 函数的相关部分:
packet = io.BytesIO()
can = canvas.Canvas(packet, pagesize=(size['width'], size['height']))
....# left out code here is just reportlab specifics for size and url stuff
can.linkURL(url, r1, thickness=1, color=colors.green)
can.rect(x1, y1, width, height, stroke=1, fill=0)
# create a new PDF with Reportlab that has the url link embedded
can.save()
packet.seek(0)
try:
new_pdf = PdfFileReader(packet)
except Exception as e:
logger.exception('e')
return None
return new_pdf.getPage(0)
我假设这是使用 BytesIO 的问题,但我无法使用带有 StringIO 的 reportlab 创建页面。这是一项关键功能,曾经与 Python 2.7 完美配合,因此我将不胜感激对此的任何反馈。谢谢!
更新:
我也尝试过从使用 BytesIO 更改为只写入临时文件,然后合并。不幸的是我遇到了同样的错误。
这是临时文件版本:
import tempfile
temp_dir = tempfile.gettempdir()
temp_path = os.path.join(temp_dir, "tmp.pdf")
can = canvas.Canvas(temp_path, pagesize=(size['width'], size['height']))
....
can.showPage()
can.save()
try:
new_pdf = PdfFileReader(temp_path)
except Exception as e:
logger.exception('e')
return None
return new_pdf.getPage(0)
更新:
我发现了一些有趣的信息。似乎如果我注释掉 can.rect 和 can.linkURL 调用,它就会合并。因此,在页面上绘制任何内容,然后尝试将其与我现有的 pdf 合并会导致错误。
在深入研究 PyPDF2 库代码后,我找到了自己的答案。对于 python 3 用户,旧库可能会很棘手。即使他们说他们支持 python 3,他们也不一定会测试所有内容。在这种情况下,问题出在 PyPDF2 中 filters.py 中的 class ASCII85Decode。对于 python 3,这个 class 需要 return 字节。我从 pdfminer3k 借用了相同类型功能的代码,它是 pdfminer python 3 的端口。如果您将 ASCII85Decode() class 换成此代码,它将起作用:
import struct
class ASCII85Decode(object):
def decode(data, decodeParms=None):
if isinstance(data, str):
data = data.encode('ascii')
n = b = 0
out = bytearray()
for c in data:
if ord('!') <= c and c <= ord('u'):
n += 1
b = b*85+(c-33)
if n == 5:
out += struct.pack(b'>L',b)
n = b = 0
elif c == ord('z'):
assert n == 0
out += b'[=10=][=10=][=10=][=10=]'
elif c == ord('~'):
if n:
for _ in range(5-n):
b = b*85+84
out += struct.pack(b'>L',b)[:n-1]
break
return bytes(out)
我在 windows 7.Python 上使用 Python 3.4.2 和 PyPDF2 1.24(如果有帮助,也使用 reportlab 3.1.44)。
我最近从 Python 2.7 升级到 3.4,并且正在移植我的代码。此代码用于创建一个空白的 pdf 页面,其中嵌入了链接(使用 reportlab)并将其与现有的 pdf 页面合并(使用 PyPDF2)。我在 reportlab 中遇到了一个问题,即保存 canvas 使用的 StringIO 需要更改为 BytesIO,但在这样做之后我 运行 出现了这个错误:
Traceback (most recent call last):
File "C:\cms_software\pdf_replica\builder.py", line 401, in merge_pdf_files
input_page.mergePage(link_page)
File "C:\Python34\lib\site-packages\PyPDF2\pdf.py", line 2013, in mergePage
self.mergePage(page2)
File "C:\Python34\lib\site-packages\PyPDF2\pdf.py", line 2059, in mergePage
page2Content = PageObject._pushPopGS(page2Content, self.pdf)
File "C:\Python34\lib\site-packages\PyPDF2\pdf.py", line 1973, in _pushPopGS
stream = ContentStream(contents, pdf)
File "C:\Python34\lib\site-packages\PyPDF2\pdf.py", line 2446, in __init
stream = BytesIO(b_(stream.getData()))
File "C:\Python34\lib\site-packages\PyPDF2\generic.py", line 826, in getData
decoded._data = filters.decodeStreamData(self)
File "C:\Python34\lib\site-packages\PyPDF2\filters.py", line 326, in decodeStreamData
data = ASCII85Decode.decode(data)
File "C:\Python34\lib\site-packages\PyPDF2\filters.py", line 264, in decode
data = [y for y in data if not (y in ' \n\r\t')]
File "C:\Python34\lib\site-packages\PyPDF2\filters.py", line 264, in
data = [y for y in data if not (y in ' \n\r\t')]
TypeError: 'in <string>' requires string as left operand, not int
这是回溯提到的行和上面的行:
link_page = self.make_pdf_link_page(pdf, size, margin, scale_factor, debug_article_links)
if link_page != None:
input_page.mergePage(link_page)
以下是该 make_pdf_link_page 函数的相关部分:
packet = io.BytesIO()
can = canvas.Canvas(packet, pagesize=(size['width'], size['height']))
....# left out code here is just reportlab specifics for size and url stuff
can.linkURL(url, r1, thickness=1, color=colors.green)
can.rect(x1, y1, width, height, stroke=1, fill=0)
# create a new PDF with Reportlab that has the url link embedded
can.save()
packet.seek(0)
try:
new_pdf = PdfFileReader(packet)
except Exception as e:
logger.exception('e')
return None
return new_pdf.getPage(0)
我假设这是使用 BytesIO 的问题,但我无法使用带有 StringIO 的 reportlab 创建页面。这是一项关键功能,曾经与 Python 2.7 完美配合,因此我将不胜感激对此的任何反馈。谢谢!
更新: 我也尝试过从使用 BytesIO 更改为只写入临时文件,然后合并。不幸的是我遇到了同样的错误。 这是临时文件版本:
import tempfile
temp_dir = tempfile.gettempdir()
temp_path = os.path.join(temp_dir, "tmp.pdf")
can = canvas.Canvas(temp_path, pagesize=(size['width'], size['height']))
....
can.showPage()
can.save()
try:
new_pdf = PdfFileReader(temp_path)
except Exception as e:
logger.exception('e')
return None
return new_pdf.getPage(0)
更新: 我发现了一些有趣的信息。似乎如果我注释掉 can.rect 和 can.linkURL 调用,它就会合并。因此,在页面上绘制任何内容,然后尝试将其与我现有的 pdf 合并会导致错误。
在深入研究 PyPDF2 库代码后,我找到了自己的答案。对于 python 3 用户,旧库可能会很棘手。即使他们说他们支持 python 3,他们也不一定会测试所有内容。在这种情况下,问题出在 PyPDF2 中 filters.py 中的 class ASCII85Decode。对于 python 3,这个 class 需要 return 字节。我从 pdfminer3k 借用了相同类型功能的代码,它是 pdfminer python 3 的端口。如果您将 ASCII85Decode() class 换成此代码,它将起作用:
import struct
class ASCII85Decode(object):
def decode(data, decodeParms=None):
if isinstance(data, str):
data = data.encode('ascii')
n = b = 0
out = bytearray()
for c in data:
if ord('!') <= c and c <= ord('u'):
n += 1
b = b*85+(c-33)
if n == 5:
out += struct.pack(b'>L',b)
n = b = 0
elif c == ord('z'):
assert n == 0
out += b'[=10=][=10=][=10=][=10=]'
elif c == ord('~'):
if n:
for _ in range(5-n):
b = b*85+84
out += struct.pack(b'>L',b)[:n-1]
break
return bytes(out)