Surprising clash of encoders with pikepdf (with patch)
Lennart Regebro via reportlab-users <[email protected]> Fri, 7 Jan 2022 18:16:00 +0100
| Newsgroups | gmane.comp.python.reportlab.user |
|---|---|
| Message-ID | <CAHT-kBf2n2c2uhgEjvahwAqTkz=a+zRU3mvUzocHh9=umfpvrg@mail.gmail.com> |
Hi all! Both Reportlab and PikePDF registers "pdfdoc" encodings, which means that which encoding you actually end up using is arbitrary. I guess it depends on the import order, but I haven't checked. That's all and well in itself, and shouldn't be a problem, but alas, PikePDF's encoding is using the qpdf library, and that library will not tell you which character failed to encode. Therefore, it doesn't raise UnicodeEncodeError which requires that information, but ValueError. This is actually specified in the docs for encode() and decode(): "encoding errors raise ValueError <https://docs.python.org/3/library/exceptions.html#ValueError> (or a more codec specific subclass, such as UnicodeEncodeError <https://docs.python.org/3/library/exceptions.html#UnicodeEncodeError>)" - https://docs.python.org/3/library/codecs.html In other places it says "Raise UnicodeError <https://docs.python.org/3/library/exceptions.html#UnicodeError> (or a subclass); this is the default. Implemented in strict_errors() <https://docs.python.org/3/library/codecs.html#codecs.strict_errors>." - https://docs.python.org/3/library/codecs.html#error-handlers I made a PR to change pikepdf's error from ValueError to UnicodeError which has been iomplemented, but that only fixes half the problem. I believe Reportlab should make one or both of these minor changes: 1. Catch UnicodeErrorsinstead of UnicodeEncodeError when "pdfdoc" encoding is used. This should have no drawbacks. 2. When it uses the pdfdoc codec it should use it directly, and not via the "pdfdoc" name. The first fix is trivial so I didn't do that, but I attach a patch for the second fix here. I hope attachemnts works for this.
pdfdoc.diff
(text/x-patch, 3.5 KB)
diff --git a/src/reportlab/pdfbase/pdfdoc.py b/src/reportlab/pdfbase/pdfdoc.py
index 91827d5..d40f7c2 100755
--- a/src/reportlab/pdfbase/pdfdoc.py
+++ b/src/reportlab/pdfbase/pdfdoc.py
@@ -16,7 +16,7 @@ classes are made available elsewhere for users to manipulate.
"""
import types, binascii, codecs, time
from collections import OrderedDict
-from reportlab.pdfbase import pdfutils
+from reportlab.pdfbase import pdfutils, rl_codecs
from reportlab import rl_config
from reportlab.lib.utils import import_zlib, open_for_read, makeFileName, isSeq, isBytes, isUnicode, _digester, isStr, bytestr, annotateException, TimeStamp
from reportlab.lib.rl_accel import escapePDF, fp_str, asciiBase85Encode, asciiBase85Decode
@@ -27,6 +27,8 @@ from sys import platform
from sys import version_info
from sys import stderr
+pdfdoc_encoding = rl_codecs.RL_Codecs._rl_codecs('pdfdoc')
+
if platform[:4] == 'java' and version_info[:2] == (2, 1):
# workaround for list()-bug in Jython 2.1 (should be fixed in 2.2)
def list(sequence):
@@ -208,7 +210,7 @@ class PDFDocument(PDFObject):
filename = getattr(f,'name',None)
if isinstance(filename,int):
filename = '<os fd:%d>'% filename
- elif not isStr(filename): #try to fix bug reported by Robert Schroll <rschroll at gmail.com>
+ elif not isStr(filename): #try to fix bug reported by Robert Schroll <rschroll at gmail.com>
filename = '<%s@0X%8.8X>' % (f.__class__.__name__,id(f))
filename = makeFileName(filename)
elif isStr(filename):
@@ -216,7 +218,7 @@ class PDFDocument(PDFObject):
filename = makeFileName(filename)
f = open(filename, "wb")
else:
- raise TypeError('Cannot use %s as a filename or file' % repr(filename))
+ raise TypeError('Cannot use %s as a filename or file' % repr(filename))
data = self.GetPDFData(canvas)
if isUnicode(data):
@@ -574,7 +576,7 @@ def _isbalanced(s):
def _checkPdfdoc(utext):
'''return true if no Pdfdoc encoding errors'''
try:
- utext.encode('pdfdoc')
+ pdfdoc_encoding.encode(utext)
return 1
except UnicodeEncodeError as e:
return 0
@@ -610,19 +612,19 @@ class PDFString(PDFObject):
else:
u = s.decode('utf8')
if _checkPdfdoc(u):
- s = u.encode('pdfdoc')
+ s = pdfdoc_encoding.encode(u)[0]
else:
s = codecs.BOM_UTF16_BE+u.encode('utf_16_be')
except:
try:
- s.decode('pdfdoc')
+ pdfdoc_encoding.decode(s)[0]
except:
stderr.write('Error in %s' % (repr(s),))
raise
elif isUnicode(s):
if enc == 'auto':
if _checkPdfdoc(s):
- s = s.encode('pdfdoc')
+ s = pdfdoc_encoding.encode(s)[0]
else:
s = codecs.BOM_UTF16_BE+s.encode('utf_16_be')
elif self.unicodeEncValid:
@@ -1711,7 +1713,7 @@ class TextAnnotation(HighlightAnnotation):
Rect,
Contents,
QuadPoints=kw.pop("QuadPoints",None) or rect_to_quad(Rect),
- Color=kw.pop("Color",(0,0,0)),
+ Color=kw.pop("Color",(0,0,0)),
**kw)
def Dict(self):
d = HighlightAnnotation.Dict(self)