#!/usr/bin/env python import logging import os import unittest import pdfplumber logging.disable(logging.ERROR) HERE = os.path.abspath(os.path.dirname(__file__)) class Test(unittest.TestCase): @classmethod def setup_class(self): path = os.path.join(HERE, "pdfs/issue-71-duplicate-chars.pdf") self.pdf = pdfplumber.open(path) @classmethod def teardown_class(self): self.pdf.close() def test_extract_table(self): page = self.pdf.pages[0] table_without_drop_duplicates = page.extract_table() table_with_drop_duplicates = page.dedupe_chars().extract_table() last_line_without_drop = table_without_drop_duplicates[1][1].split("\n")[-1] last_line_with_drop = table_with_drop_duplicates[1][1].split("\n")[-1] assert ( last_line_without_drop == "微微软软 培培训训课课程程:: 名名模模意意义义一一些些有有意意义义一一些些" ) assert last_line_with_drop == "微软 培训课程: 名模意义一些有意义一些" def test_extract_words(self): page = self.pdf.pages[0] x0 = 440.143 x1_without_drop = 534.992 x1_with_drop = 534.719 top_windows = 791.849 top_linux = 794.357 bottom = 802.961 last_words_without_drop = page.extract_words()[-1] last_words_with_drop = page.dedupe_chars().extract_words()[-1] assert round(last_words_without_drop["x0"], 3) == x0 assert round(last_words_without_drop["x1"], 3) == x1_without_drop assert round(last_words_without_drop["top"], 3) in (top_windows, top_linux) assert round(last_words_without_drop["bottom"], 3) == bottom assert last_words_without_drop["upright"] == 1 assert ( last_words_without_drop["text"] == "名名模模意意义义一一些些有有意意义义一一些些" ) assert round(last_words_with_drop["x0"], 3) == x0 assert round(last_words_with_drop["x1"], 3) == x1_with_drop assert round(last_words_with_drop["top"], 3) in (top_windows, top_linux) assert round(last_words_with_drop["bottom"], 3) == bottom assert last_words_with_drop["upright"] == 1 assert last_words_with_drop["text"] == "名模意义一些有意义一些" def test_extract_text(self): page = self.pdf.pages[0] last_line_without_drop = page.extract_text().split("\n")[-1] last_line_with_drop = page.dedupe_chars().extract_text().split("\n")[-1] assert ( last_line_without_drop == "微微软软 培培训训课课程程:: 名名模模意意义义一一些些有有意意义义一一些些" ) assert last_line_with_drop == "微软 培训课程: 名模意义一些有意义一些" def test_extract_text2(self): path = os.path.join(HERE, "pdfs/issue-71-duplicate-chars-2.pdf") pdf = pdfplumber.open(path) page = pdf.pages[0] assert ( page.dedupe_chars().extract_text(y_tolerance=6).splitlines()[4] == "UE 8. Circulation - Métabolismes" ) def test_extra_attrs(self): path = os.path.join(HERE, "pdfs/issue-1114-dedupe-chars.pdf") pdf = pdfplumber.open(path) page = pdf.pages[0] def dup_chars(s: str) -> str: return "".join((char if char == " " else char + char) for char in s) ground_truth = ( ("Simple", False, False), ("Duplicated", True, True), ("Font", "fontname", True), ("Size", "size", True), ("Italic", "fontname", True), ("Weight", "fontname", True), ("Horizontal shift", False, "HHoorrizizoonntatal ls shhifitft"), ("Vertical shift", False, True), ) gt = [] for text, should_dedup, dup_text in ground_truth: if isinstance(dup_text, bool): if dup_text: dup_text = dup_chars(text) else: dup_text = text gt.append((text, should_dedup, dup_text)) keys_list = ["no_dedupe", (), ("size",), ("fontname",), ("size", "fontname")] for keys in keys_list: if keys != "no_dedupe": filtered_page = page.dedupe_chars(tolerance=2, extra_attrs=keys) else: filtered_page = page for i, line in enumerate( filtered_page.extract_text(y_tolerance=5).splitlines() ): text, should_dedup, dup_text = gt[i] if keys == "no_dedupe": should_dedup = False if isinstance(should_dedup, str): if should_dedup in keys: fail_msg = ( f"{should_dedup} is not required to match " "so it should be duplicated" ) assert line == dup_text, fail_msg else: fail_msg = ( "Should not be duplicated " f"when requiring matching {should_dedup}" ) assert line == text, fail_msg elif should_dedup: assert line == text else: assert line == dup_text