Unicode 编码错误:'latin-1' 编解码器无法编码字符 '\u2013'(写入 PDF)。

10

我在使用Python写入PDF时,遇到了一个关于变量内容的Unicode问题。

它输出以下错误信息:

UnicodeEncodeError: 'latin-1' codec can't encode character '\u2013'

问题出在它基本上被悬挂在一个em dash上。

我尝试将包含“em dash”的变量重新定义为例如'.encode('utf-8')',即如下所示:

Body = msg.Body

BodyC = Body.encode('utf-8')

现在我遇到了以下错误:

Traceback (most recent call last):
  File "script.py", line 37, in <module>
    pdf.cell(200, 10, txt="Bod: " + BodyC,  ln=4, align="C")
TypeError: can only concatenate str (not "bytes") to str

以下是完整代码,我该如何简单地解决'Body'变量内容中的Unicode错误。
将其转换为utf-8western,任何一个超出'latin-1'范围的都要进行转换。有什么建议吗? 完整代码:
from fpdf import FPDF
import win32com.client

outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
msg = outlook.OpenSharedItem(r"C:\User\language\python\Msg-To-PDF\test_msg.msg")

print (msg.SenderName)
print (msg.SenderEmailAddress)
print (msg.SentOn)
print (msg.To)
print (msg.CC)
print (msg.BCC)
print (msg.Subject)
print (msg.Body)

SenderName = msg.SenderName
SenderEmailAddress = msg.SenderEmailAddress
SentOn = msg.SentOn
To = msg.To
CC = msg.CC
BCC = msg.BCC
Subject = msg.Subject
Body = msg.Body
BodyC = Body.encode('utf-8')

pdf = FPDF()
pdf.add_page()

# pdf.add_font('DejaVu', '', 'DejaVuSansCondensed.ttf', uni=True)
pdf.set_font("Helvetica", style = '', size = 11)
pdf.cell(200, 10, txt="From: " + SenderName, ln=1, align="C")
# pdf.cell(200, 10, border=SentOn, ln=1, align="C")
pdf.cell(200, 10, txt="To: " + To, ln=1, align="C")
pdf.cell(200, 10, txt="CC: " + CC, ln=1, align="C")
pdf.cell(200, 10, txt="BCC: " + BCC, ln=1, align="C")
pdf.cell(200, 10, txt="Subject: " + Subject, ln=1, align="C")
pdf.cell(200, 10, txt="Bod: " + BodyC,  ln=4, align="C")

pdf.output("Sample.pdf")
  • 如何从 'latin1' 转换?

  • 有没有一种全局修复这些问题的方法?

1
你尝试过使用 str(msg.Body) 进行消息正文的转换吗? - ladygremlin
你在说什么?在哪里? - Coastal-Cam
Body = msg.Body -> Body = str(msg.Body) Body = msg.Body -> Body = str(msg.Body) - ladygremlin
1
它仍然产生完全相同的错误:“UnicodeEncodeError:'latin-1'编解码器无法在位置485处编码字符'\ u2013':序数不在范围内(256)”。 - Coastal-Cam
1
@dsgdfg 啊,我没意识到 Windows 总是会抛出这个。那不是我首选的操作系统。 :) - ladygremlin
显示剩余7条评论
4个回答

21

一个解决方法是在传递给库之前将所有文本转换为Latin-1编码。您可以使用以下命令执行此操作:

一个解决方法是在传递给库之前将所有文本转换为Latin-1编码。您可以使用以下命令执行此操作:

text2 = text.encode('latin-1', 'replace').decode('latin-1')

text2 将不包含任何非 Latin-1 字符。然而,一些字符可能会被替换为 ?


这在Python 3中可行吗?我遇到了问题,无法使其正常工作。我可以使用“?”将其转换为字符串,但是fpdf仍然会拒绝它... - BostonMacOSX
是的,我也用Python 3运行了这个。 - Erik Kalkoken
我的'都变成了?....你是否使用了字体替换方法,其中定义了一个UTF8字体? - BostonMacOSX
1
这是解决方案。要在此答案中添加,您可以使用以下方法忽略它: text.encode('latin-1', 'ignore').decode('latin-1') - Akshay

5
这个错误的原因是您正在尝试在PDF中呈现一个字符,该字符超出了latin-1编码范围。 FPDF将latin-1作为所有内置字体的默认编码。
所以,解决方法是删除所有不适合latin-1编码的文本字符。 (请参见我的其他答案以获得此解决方法)。
要修复此错误并能够呈现这些字符,请使用支持更广泛字符范围的字体。为此,FPDF库支持Unicode字体。
例如,您可以获取免费的Google Noto字体,它支持广泛的Unicode端点。对于大多数西方语言,我建议选择NotoSans字体集。但是,您还可以获取许多其他语言和脚本的字体,包括中文,希伯来语或阿拉伯语。
以下是如何在FPDF中启用Unicode字体的步骤:
首先,您需要告诉FPDF库在哪里找到字体文件。在此示例中,我将其设置为当前文件夹的子文件夹fonts
import fpdf
fpdf.set_global("SYSTEM_TTFONTS", os.path.join(os.path.dirname(__file__),'fonts'))

然后您需要将字体添加到PDF文档中。在这个例子中,我正在添加NotoSans字体,以用于正常、加粗、斜体和加粗斜体样式:

pdf = fpdf.FPDF()
pdf.add_font("NotoSans", style="", fname="NotoSans-Regular.ttf", uni=True)
pdf.add_font("NotoSans", style="B", fname="NotoSans-Bold.ttf", uni=True)
pdf.add_font("NotoSans", style="I", fname="NotoSans-Italic.ttf", uni=True)
pdf.add_font("NotoSans", style="BI", fname="NotoSans-BoldItalic.ttf", uni=True)

现在您可以使用 set_font() 在PDF文档中正常使用新字体,以下是普通文本的示例:

pdf.set_font("NotoSans", size=12)

1
我尝试了这个解决方案,但出现了错误 AttributeError: module 'fpdf' has no attribute 'set_global'。请问有没有特定版本的fpdf推荐使用?错误出现在 fpdf.set_global... 这一行。我跳过了set_global并在 pdf.add_font(.. 中给出了相对路径,它可以正常工作 pdf.add_font("NotoKufiArabic", style="", fname="./fonts/NotoKufiArabic-Regular.ttf", uni=True) - akarahman

1
你也可以通过.set_doc_option()方法(文档在这里)更改编码。我尝试了Erik的方法,它对我起作用,但是在添加一些复杂性之后(例如第二个PDF和使用write_html()方法需要创建一个新类),我回到了相同的错误。像你说的那样,更改整个文档的编码应该解决整个问题。
readthedocs页面表示你只能使用latin-1或windows-1252,但是pdf.set_doc_option('core_fonts_encoding', 'utf-8')根据调试器的说法对我起作用。只需注意某些字符需要修复,例如撇号(')在PDF中显示为â€ÂTM。
希望这是你寻找的全局解决方案,即使有点晚!

1
无法工作,'FPDF'对象没有属性'set_doc_option'。 - Carlost

0

我尝试了Erik的解决方案并进行了一些更改,使用英文和阿拉伯文本的混合效果很好。下面张贴了示例代码,使用pyFPDF生成PDF。

from datetime import datetime
def getFileName():
    now=datetime.now()
    time = now.strftime('%d_%H_%M_%S')
    filename = "Test_"+time + ".pdf"
    return filename


from fpdf import FPDF

pdf = FPDF()

#Download NotoSansArabic-Regular.ttf from Google noto fonts
pdf.add_font("NotoSansArabic", style="", fname="./fonts/NotoSansArabic-Regular.ttf", uni=True)


pdf.add_page()

pdf.set_font('Arial', '', 12)
pdf.write(8, 'Hello World')
pdf.ln(8)

# مرحبا Marhaba in arabic 
pdf.set_font('NotoSansArabic', '', 12)
text = 'مرحبا'
pdf.write(8, text)
pdf.ln(8)

pdf.output(getFileName(), 'F')

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接