mirror of
https://github.com/jsvine/pdfplumber.git
synced 2026-08-29 08:34:23 +08:00
Came across this bit of code, which helps to solve some of the mystery in issues #461 and #842: https://git.ghostscript.com/?p=mupdf.git;a=blob;f=source/pdf/pdf-font.c;h=6322cedf2c26cfb312c0c0878d7aff97b4c7470e;hb=HEAD#l774 Now, for every char's fontname, we: - Check whether its a `str` or `byte` - If the latter, we check whether it's one of the well-known codes from the link above - If so, we use that (preserving the part, if present, before the `+`) - If not, we just cast to str
This commit is contained in:
@@ -71,6 +71,27 @@ if TYPE_CHECKING: # pragma: nocover
|
||||
from .display import PageImage
|
||||
from .pdf import PDF
|
||||
|
||||
# via https://git.ghostscript.com/?p=mupdf.git;a=blob;f=source/pdf/pdf-font.c;h=6322cedf2c26cfb312c0c0878d7aff97b4c7470e;hb=HEAD#l774 # noqa
|
||||
|
||||
CP936_FONTNAMES = {
|
||||
b"\xcb\xce\xcc\xe5": "SimSun,Regular",
|
||||
b"\xba\xda\xcc\xe5": "SimHei,Regular",
|
||||
b"\xbf\xac\xcc\xe5_GB2312": "SimKai,Regular",
|
||||
b"\xb7\xc2\xcb\xce_GB2312": "SimFang,Regular",
|
||||
b"\xc1\xa5\xca\xe9": "SimLi,Regular",
|
||||
}
|
||||
|
||||
|
||||
def fix_fontname_bytes(fontname: bytes) -> str:
|
||||
if b"+" in fontname:
|
||||
split_at = fontname.index(b"+") + 1
|
||||
prefix, suffix = fontname[:split_at], fontname[split_at:]
|
||||
else:
|
||||
prefix, suffix = b"", fontname
|
||||
|
||||
suffix_new = CP936_FONTNAMES.get(suffix, str(suffix)[2:-1])
|
||||
return str(prefix)[2:-1] + suffix_new
|
||||
|
||||
|
||||
class Page(Container):
|
||||
cached_properties: List[str] = Container.cached_properties + ["_layout"]
|
||||
@@ -221,6 +242,10 @@ class Page(Container):
|
||||
attr["stroking_color"] = gs.scolor
|
||||
attr["non_stroking_color"] = gs.ncolor
|
||||
|
||||
# Handle (rare) byte-encoded fontnames
|
||||
if isinstance(attr["fontname"], bytes):
|
||||
attr["fontname"] = fix_fontname_bytes(attr["fontname"])
|
||||
|
||||
if "pts" in attr:
|
||||
attr["pts"] = list(map(self.point2coord, attr["pts"]))
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -190,6 +190,31 @@ class Test(unittest.TestCase):
|
||||
chars = (char for char in pdf.chars)
|
||||
pdfplumber.utils.extract_text(chars)
|
||||
|
||||
def test_issue_461_and_842(self):
|
||||
"""
|
||||
pdfplumber should gracefully handle characters with byte-encoded
|
||||
font names.
|
||||
"""
|
||||
before = b"RGJSAP+\xcb\xce\xcc\xe5"
|
||||
after = pdfplumber.page.fix_fontname_bytes(before)
|
||||
assert after == "RGJSAP+SimSun,Regular"
|
||||
|
||||
before = b"\xcb\xce\xcc\xe5"
|
||||
after = pdfplumber.page.fix_fontname_bytes(before)
|
||||
assert after == "SimSun,Regular"
|
||||
|
||||
path = os.path.join(HERE, "pdfs/issue-461-example.pdf")
|
||||
with pdfplumber.open(path) as pdf:
|
||||
page = pdf.pages[0]
|
||||
assert all(isinstance(c["fontname"], str) for c in page.chars)
|
||||
page.dedupe_chars()
|
||||
|
||||
path = os.path.join(HERE, "pdfs/issue-842-example.pdf")
|
||||
with pdfplumber.open(path) as pdf:
|
||||
page = pdf.pages[0]
|
||||
assert all(isinstance(c["fontname"], str) for c in page.chars)
|
||||
page.dedupe_chars()
|
||||
|
||||
def test_issue_463(self):
|
||||
"""
|
||||
Extracting annotations should not raise UnicodeDecodeError on utf-16 text
|
||||
|
||||
Reference in New Issue
Block a user