From 29fe5a371e865874ba752bcc1a512272da768a10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Kreuzberger?= Date: Wed, 21 Feb 2024 16:56:38 +0100 Subject: [PATCH] Adding tests for rect extractions (#36) * adding tests for rect * finish basic tests * disable visual debug for tests, seems to depend on external tools * adding ghostscript hint in visual debug --- docs/contents/visual_debugging.rst | 10 +- libpdf/apiobjects.py | 11 +- libpdf/catalog.py | 14 +- libpdf/extract.py | 14 +- libpdf/log.py | 15 +- libpdf/models/model_base.py | 5 +- libpdf/process.py | 52 +++---- libpdf/textbox.py | 25 ++- libpdf/utils.py | 67 ++++---- poetry.lock | 38 ++--- pyproject.toml | 2 +- tests/conftest.py | 79 +++------- tests/pdf/test_rects_extraction.pdf | Bin 0 -> 82959 bytes tests/test_catalog.py | 2 +- tests/test_cli.py | 4 +- tests/test_figures.py | 2 +- tests/test_rects.py | 233 ++++++++++++++++++++++++++++ tox.ini | 5 +- 18 files changed, 388 insertions(+), 190 deletions(-) create mode 100644 tests/pdf/test_rects_extraction.pdf create mode 100644 tests/test_rects.py diff --git a/docs/contents/visual_debugging.rst b/docs/contents/visual_debugging.rst index 002c894..5c9cc5f 100644 --- a/docs/contents/visual_debugging.rst +++ b/docs/contents/visual_debugging.rst @@ -1,2 +1,10 @@ Visual debugging -================ \ No newline at end of file +================ + +Requirements +------------ + +The Visual debugging feature requires Ghostscript to extract images from pdf. +This is required as a separated install on your system. + +See `Ghostscript `_ for installation instructions. diff --git a/libpdf/apiobjects.py b/libpdf/apiobjects.py index 9ca56cf..cc3a223 100644 --- a/libpdf/apiobjects.py +++ b/libpdf/apiobjects.py @@ -61,13 +61,12 @@ def __init__( # pylint: disable=too-many-arguments # the parameters are needed if pdfminer is not None: # take argument first self.pdfminer = pdfminer + elif pdfplumber is not None: + # set from pdfplumber document + self.pdfminer = pdfplumber.doc else: - if pdfplumber is not None: - # set from pdfplumber document - self.pdfminer = pdfplumber.doc - else: - # nothing available - self.pdfminer = None + # nothing available + self.pdfminer = None class Flattened(NamedTuple): diff --git a/libpdf/catalog.py b/libpdf/catalog.py index fedc50b..6e940c4 100644 --- a/libpdf/catalog.py +++ b/libpdf/catalog.py @@ -124,7 +124,7 @@ def resolve_name_obj(name_tree_kids): """ temp_list = [] for kid in name_tree_kids: - if "Kids" in kid and kid["Kids"]: + if kid.get("Kids"): temp_list.extend([kid_kid.resolve() for kid_kid in kid["Kids"]]) elif "Names" in kid: return name_tree_kids @@ -311,14 +311,12 @@ def resolve_outline(outline_obj, outline_list, des_dict, pdf): # pylint: disabl raise RuntimeError( f"Page {outline_obj['Dest'][0]} is not an indirect reference to a page object" ) + elif isinstance(outline_obj["Dest"], PSLiteral): + # PDF 1.1 name object + outline_dest = outline_obj["Dest"].name else: - # named destination - if isinstance(outline_obj["Dest"], PSLiteral): - # PDF 1.1 name object - outline_dest = outline_obj["Dest"].name - else: - # PDF 1.2 byte string - outline_dest = outline_obj["Dest"].decode("utf-8") + # PDF 1.2 byte string + outline_dest = outline_obj["Dest"].decode("utf-8") title_bytes = outline_obj["Title"] else: raise ValueError("No key A and Dest in outline.") diff --git a/libpdf/extract.py b/libpdf/extract.py index 311710d..d2f65ba 100644 --- a/libpdf/extract.py +++ b/libpdf/extract.py @@ -429,9 +429,8 @@ def check_false_positive_header_footer(pdf, elements_list): # pylint: disable=t # recursively check again, to find the next min_low_pos, which will determine the header/footer boundary if elements_list: return check_false_positive_header_footer(pdf, elements_list) - else: - if len(elements_list) == 1: - elements_list.pop() + elif len(elements_list) == 1: + elements_list.pop() else: for idx, element in enumerate(elements_list): if float(f"{element.position.y0:.4f}") == header_low_pos: @@ -853,12 +852,9 @@ def check_and_filter_figures(figures_list): # pylint: disable=too-many-branches "remove filtered figure fig0 due to partially overlap" ) filtered_figures.remove(fig0) - else: - if fig1 in filtered_figures: - LOG.debug( - "remove filtered figure fig1 due to partially overlap" - ) - filtered_figures.remove(fig1) + elif fig1 in filtered_figures: + LOG.debug("remove filtered figure fig1 due to partially overlap") + filtered_figures.remove(fig1) if len(filtered_figures) < len(figures_list): LOG.debug( diff --git a/libpdf/log.py b/libpdf/log.py index ca1c9f4..7239cc5 100644 --- a/libpdf/log.py +++ b/libpdf/log.py @@ -71,14 +71,13 @@ def config_logger(cli=True): init_tqdm = True else: init_basic = True - else: # API usage - if TQDM_AVAILABLE: - # this needs to be documented so any API user is not surprised that the libpdf logger has an attached - # handler; users may delete it if unwanted or it could be configurable later if tqdm handler should be - # used or the user wants to define something else - init_tqdm = True - else: # don't init anything, it's up to the user - pass + elif TQDM_AVAILABLE: + # this needs to be documented so any API user is not surprised that the libpdf logger has an attached + # handler; users may delete it if unwanted or it could be configurable later if tqdm handler should be + # used or the user wants to define something else + init_tqdm = True + else: # don't init anything, it's up to the user + pass log_format = "[%(levelname)5s] %(name)s - %(message)s" if init_tqdm: diff --git a/libpdf/models/model_base.py b/libpdf/models/model_base.py index 3f99327..a54dd9b 100644 --- a/libpdf/models/model_base.py +++ b/libpdf/models/model_base.py @@ -18,9 +18,8 @@ def to_dict(self): for key, value in vars_dict.items(): if key.startswith("b_"): delete_backref_keys.append(key) - else: - if isinstance(value, ModelBase): - vars_dict[key] = value.to_dict() + elif isinstance(value, ModelBase): + vars_dict[key] = value.to_dict() # delete back references for key in delete_backref_keys: del vars_dict[key] diff --git a/libpdf/process.py b/libpdf/process.py index 6500aec..f723c77 100644 --- a/libpdf/process.py +++ b/libpdf/process.py @@ -309,21 +309,20 @@ def fill_elements_content( id_dict = {"table": 1, "figure": 1, "paragraph": 1, "rect": 1} content = elements_in_outline[index_element].content index_b_chapter = index_element + elif "content" in locals(): + element.idx = id_dict[element.type] + element.b_chapter = elements_in_outline[index_b_chapter] + content.append(element) + id_dict[element.type] += 1 else: - if "content" in locals(): - element.idx = id_dict[element.type] - element.b_chapter = elements_in_outline[index_b_chapter] - content.append(element) - id_dict[element.type] += 1 - else: - # TODO 1. this exception is not caught in libpdf code and will go all the way up to the user (wanted?) - # 2. the message is unclear - # 3. if it's a programming error, fix the code - # 4. if it's a real runtime issue coming from wrong PDF input, catch the error one level above - # and log an understandable, critical error - raise ValueError( - "elements can not fill into the content because it does not exist" - ) + # TODO 1. this exception is not caught in libpdf code and will go all the way up to the user (wanted?) + # 2. the message is unclear + # 3. if it's a programming error, fix the code + # 4. if it's a real runtime issue coming from wrong PDF input, catch the error one level above + # and log an understandable, critical error + raise ValueError( + "elements can not fill into the content because it does not exist" + ) chapters_content = list( filter(lambda x: isinstance(x, Chapter), elements_in_outline) @@ -435,20 +434,19 @@ def libpdf_target_explorer( # pylint: disable=too-many-nested-blocks # local al for link in element.links: target_id = find_target_id(link, pages_list, element) link.libpdf_target = target_id + elif isinstance(element, Cell): + # Cell is not considered as element + pass else: - if isinstance(element, Cell): - # Cell is not considered as element - pass - else: - # TODO reason about the overall logic; which cases can be removed? distinguish between - # programming errors (raise RuntimeErrors) and cases that actually may exist in the - # wild and write human-readable log messages (e.g. - # The link on page xy with text xy cannot be resolved to a libpdf element; linking - # to the target page position instead - LOG.error( - "The source link in the paragraph %s is missing", - repr(element), - ) + # TODO reason about the overall logic; which cases can be removed? distinguish between + # programming errors (raise RuntimeErrors) and cases that actually may exist in the + # wild and write human-readable log messages (e.g. + # The link on page xy with text xy cannot be resolved to a libpdf element; linking + # to the target page position instead + LOG.error( + "The source link in the paragraph %s is missing", + repr(element), + ) def elements_with_anno_finder( diff --git a/libpdf/textbox.py b/libpdf/textbox.py index e4db528..9794826 100644 --- a/libpdf/textbox.py +++ b/libpdf/textbox.py @@ -79,10 +79,9 @@ def extract_paragraphs_chapters( chapter_list = [] if no_chapters: LOG.info("Excluding chapters extraction") - else: - if catalog["outline"]: - LOG.info("Extracting chapters ...") - chapter_list = render_chapters(extracted_lt_textboxes, page_list, pdf) + elif catalog["outline"]: + LOG.info("Extracting chapters ...") + chapter_list = render_chapters(extracted_lt_textboxes, page_list, pdf) paragraph_list = [] if no_paragraphs: @@ -782,17 +781,15 @@ def first_last_char_in_anno_marker( # pylint: disable=too-many-branches # bette # the incoming char is outside the anno-rectangle pass - else: - # the char is LTAnno - if idx_char == len(ltobjs_in_lttextline) - 1: - # the last char of the textline + elif idx_char == len(ltobjs_in_lttextline) - 1: + # the last char of the textline + anno_complete = True + elif isinstance(ltobjs_in_lttextline[idx_char + 1], LTChar): + if ltobjs_in_lttextline[idx_char + 1].x0 > anno["rect"][2]: + # the next char is outside of the current anno-rectangle anno_complete = True - elif isinstance(ltobjs_in_lttextline[idx_char + 1], LTChar): - if ltobjs_in_lttextline[idx_char + 1].x0 > anno["rect"][2]: - # the next char is outside of the current anno-rectangle - anno_complete = True - else: - raise ValueError("two LTAnno occurs in a row") + else: + raise ValueError("two LTAnno occurs in a row") return anno_complete diff --git a/libpdf/utils.py b/libpdf/utils.py index b593191..24ca914 100644 --- a/libpdf/utils.py +++ b/libpdf/utils.py @@ -303,43 +303,40 @@ def find_lt_obj_in_bbox( ): # This is the case when a LT object is neither inside nor intersected with the given bounding box. pass - else: - # This is the case when a LT object is intersected with the given box. In this case, the LT objects inside the - # given bounding box need to be hierarchically and recursively found. - if hasattr(lt_obj, "_objs"): - # All the downwards hierarchical LT objects are stored in the attribute "_objs". - # If the _objs attribute doesn't exist, it means it's the bottom of the hierarchy. - text_inside_bbox = False # True on LTTextLine level when the first LTChar is inside the BBOX - for item in lt_obj._objs: # pylint: disable=protected-access - if isinstance(item, LTAnno): - # special treatment of LTAnno because it is virtual with no position data - if text_inside_bbox: - # LTAnno is added because an LTChar was inside the bbox before + elif hasattr(lt_obj, "_objs"): + # All the downwards hierarchical LT objects are stored in the attribute "_objs". + # If the _objs attribute doesn't exist, it means it's the bottom of the hierarchy. + text_inside_bbox = ( + False # True on LTTextLine level when the first LTChar is inside the BBOX + ) + for item in lt_obj._objs: # pylint: disable=protected-access + if isinstance(item, LTAnno): + # special treatment of LTAnno because it is virtual with no position data + if text_inside_bbox: + # LTAnno is added because an LTChar was inside the bbox before + lt_objs_in_bbox.append(item) + elif isinstance(item, LTChar): + # check if the first and last LTChar have shown in the given bbox to decide if the trailing + # LTAnno should be added + ltchar_inside = check_lt_obj_in_bbox(item, bbox) + if text_inside_bbox: + if ltchar_inside: lt_objs_in_bbox.append(item) - else: - if isinstance(item, LTChar): - # check if the first and last LTChar have shown in the given bbox to decide if the trailing - # LTAnno should be added - ltchar_inside = check_lt_obj_in_bbox(item, bbox) - if text_inside_bbox: - if ltchar_inside: - lt_objs_in_bbox.append(item) - else: - # the bbox just ended and can't enter again - break - else: - if ltchar_inside: - lt_objs_in_bbox.append(item) - text_inside_bbox = True - else: - # no LTChar was added before, so not in BBOX yet - pass else: - # it is not an LTAnno nor an LTChar, so recurse and break it further down - find_lt_obj_in_bbox(lt_objs_in_bbox, item, bbox) - else: - # no attribute "_objs" exists. It reaches the bottom of the hierarchy - pass + # the bbox just ended and can't enter again + break + elif ltchar_inside: + lt_objs_in_bbox.append(item) + text_inside_bbox = True + else: + # no LTChar was added before, so not in BBOX yet + pass + else: + # it is not an LTAnno nor an LTChar, so recurse and break it further down + find_lt_obj_in_bbox(lt_objs_in_bbox, item, bbox) + else: + # no attribute "_objs" exists. It reaches the bottom of the hierarchy + pass def lt_page_crop( diff --git a/poetry.lock b/poetry.lock index 6354f62..8cce306 100644 --- a/poetry.lock +++ b/poetry.lock @@ -874,28 +874,28 @@ files = [ [[package]] name = "ruff" -version = "0.1.13" +version = "0.2.0" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.1.13-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e3fd36e0d48aeac672aa850045e784673449ce619afc12823ea7868fcc41d8ba"}, - {file = "ruff-0.1.13-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9fb6b3b86450d4ec6a6732f9f60c4406061b6851c4b29f944f8c9d91c3611c7a"}, - {file = "ruff-0.1.13-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b13ba5d7156daaf3fd08b6b993360a96060500aca7e307d95ecbc5bb47a69296"}, - {file = "ruff-0.1.13-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9ebb40442f7b531e136d334ef0851412410061e65d61ca8ce90d894a094feb22"}, - {file = "ruff-0.1.13-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:226b517f42d59a543d6383cfe03cccf0091e3e0ed1b856c6824be03d2a75d3b6"}, - {file = "ruff-0.1.13-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5f0312ba1061e9b8c724e9a702d3c8621e3c6e6c2c9bd862550ab2951ac75c16"}, - {file = "ruff-0.1.13-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2f59bcf5217c661254bd6bc42d65a6fd1a8b80c48763cb5c2293295babd945dd"}, - {file = "ruff-0.1.13-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6894b00495e00c27b6ba61af1fc666f17de6140345e5ef27dd6e08fb987259d"}, - {file = "ruff-0.1.13-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a1600942485c6e66119da294c6294856b5c86fd6df591ce293e4a4cc8e72989"}, - {file = "ruff-0.1.13-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ee3febce7863e231a467f90e681d3d89210b900d49ce88723ce052c8761be8c7"}, - {file = "ruff-0.1.13-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:dcaab50e278ff497ee4d1fe69b29ca0a9a47cd954bb17963628fa417933c6eb1"}, - {file = "ruff-0.1.13-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f57de973de4edef3ad3044d6a50c02ad9fc2dff0d88587f25f1a48e3f72edf5e"}, - {file = "ruff-0.1.13-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7a36fa90eb12208272a858475ec43ac811ac37e91ef868759770b71bdabe27b6"}, - {file = "ruff-0.1.13-py3-none-win32.whl", hash = "sha256:a623349a505ff768dad6bd57087e2461be8db58305ebd5577bd0e98631f9ae69"}, - {file = "ruff-0.1.13-py3-none-win_amd64.whl", hash = "sha256:f988746e3c3982bea7f824c8fa318ce7f538c4dfefec99cd09c8770bd33e6539"}, - {file = "ruff-0.1.13-py3-none-win_arm64.whl", hash = "sha256:6bbbc3042075871ec17f28864808540a26f0f79a4478c357d3e3d2284e832998"}, - {file = "ruff-0.1.13.tar.gz", hash = "sha256:e261f1baed6291f434ffb1d5c6bd8051d1c2a26958072d38dfbec39b3dda7352"}, + {file = "ruff-0.2.0-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:638ea3294f800d18bae84a492cb5a245c8d29c90d19a91d8e338937a4c27fca0"}, + {file = "ruff-0.2.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3ff35433fcf4dff6d610738712152df6b7d92351a1bde8e00bd405b08b3d5759"}, + {file = "ruff-0.2.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf9faafbdcf4f53917019f2c230766da437d4fd5caecd12ddb68bb6a17d74399"}, + {file = "ruff-0.2.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8153a3e4128ed770871c47545f1ae7b055023e0c222ff72a759f5a341ee06483"}, + {file = "ruff-0.2.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8a75a98ae989a27090e9c51f763990ad5bbc92d20626d54e9701c7fe597f399"}, + {file = "ruff-0.2.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:87057dd2fdde297130ff99553be8549ca38a2965871462a97394c22ed2dfc19d"}, + {file = "ruff-0.2.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6d232f99d3ab00094ebaf88e0fb7a8ccacaa54cc7fa3b8993d9627a11e6aed7a"}, + {file = "ruff-0.2.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d3c641f95f435fc6754b05591774a17df41648f0daf3de0d75ad3d9f099ab92"}, + {file = "ruff-0.2.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3826fb34c144ef1e171b323ed6ae9146ab76d109960addca730756dc19dc7b22"}, + {file = "ruff-0.2.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:eceab7d85d09321b4de18b62d38710cf296cb49e98979960a59c6b9307c18cfe"}, + {file = "ruff-0.2.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:30ad74687e1f4a9ff8e513b20b82ccadb6bd796fe5697f1e417189c5cde6be3e"}, + {file = "ruff-0.2.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a7e3818698f8460bd0f8d4322bbe99db8327e9bc2c93c789d3159f5b335f47da"}, + {file = "ruff-0.2.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:edf23041242c48b0d8295214783ef543847ef29e8226d9f69bf96592dba82a83"}, + {file = "ruff-0.2.0-py3-none-win32.whl", hash = "sha256:e155147199c2714ff52385b760fe242bb99ea64b240a9ffbd6a5918eb1268843"}, + {file = "ruff-0.2.0-py3-none-win_amd64.whl", hash = "sha256:ba918e01cdd21e81b07555564f40d307b0caafa9a7a65742e98ff244f5035c59"}, + {file = "ruff-0.2.0-py3-none-win_arm64.whl", hash = "sha256:3fbaff1ba9564a2c5943f8f38bc221f04bac687cc7485e45237579fee7ccda79"}, + {file = "ruff-0.2.0.tar.gz", hash = "sha256:63856b91837606c673537d2889989733d7dffde553828d3b0f0bacfa6def54be"}, ] [[package]] @@ -1244,4 +1244,4 @@ tqdm = ["tqdm"] [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "c1685b3c0330bc060c0c174892c686176eac896e4dd3eb4d4b66bc91b4abbff8" +content-hash = "9c1eef38de5b33209d134a69d9c8c36cb66cdc159738482e05108e702ee3c34b" diff --git a/pyproject.toml b/pyproject.toml index d810eeb..102ec1a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,7 +84,7 @@ tox = "*" pytest-xdist = "*" # parallelisation # linting, formatting -ruff = "^0.1.13" +ruff = "^0.2.0" # docs sphinx = "*" diff --git a/tests/conftest.py b/tests/conftest.py index 9d21e3b..1fd8395 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,72 +1,43 @@ """Pytest conftest module containing common test configuration and fixtures.""" -import os +from __future__ import annotations + +from pathlib import Path +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from libpdf.apiobjects import ApiObjects import pytest from libpdf import load # test PDFs from pdfplumber -PDF_LOREM_IPSUM = os.path.join(os.path.dirname(__file__), "pdf", "lorem-ipsum.pdf") -PDF_TWO_COLUMNS = os.path.join(os.path.dirname(__file__), "pdf", "two_colums_sampe.pdf") -PDF_WITH_EMPTY_OUTLINE = os.path.join( - os.path.dirname(__file__), "pdf", "issue-67-example.pdf" -) -PDF_OUTLINE_NO_DEST = os.path.join(os.path.dirname(__file__), "pdf", "pdffill-demo.pdf") -PDF_FIGURE_WITH_INVALID_BBOX = os.path.join( - os.path.dirname(__file__), "pdf", "pr-138-example.pdf" -) -PDF_CHAPTER_DETECTION = os.path.join( - os.path.dirname(__file__), "pdf", "DS93-chapter-issue-fix.pdf" -) +PDF_LOREM_IPSUM = Path(__file__).parent / "pdf" / "lorem-ipsum.pdf" +PDF_TWO_COLUMNS = Path(__file__).parent / "pdf" / "two_colums_sampe.pdf" +PDF_WITH_EMPTY_OUTLINE = Path(__file__).parent / "pdf" / "issue-67-example.pdf" + +PDF_OUTLINE_NO_DEST = Path(__file__).parent / "pdf" / "pdffill-demo.pdf" +PDF_FIGURE_WITH_INVALID_BBOX = Path(__file__).parent / "pdf" / "pr-138-example.pdf" +PDF_CHAPTER_DETECTION = Path(__file__).parent / "pdf" / "DS93-chapter-issue-fix.pdf" # full features PDF -PDF_FULL_FEATURES = os.path.join(os.path.dirname(__file__), "pdf", "full_features.pdf") -PDF_FIGURES_EXTRACTION = os.path.join( - os.path.dirname(__file__), "pdf", "test_figures_extraction.pdf" -) -PDF_SMART_HEADER_FOOTER_DETECTION = os.path.join( - os.path.dirname(__file__), "pdf", "test_header_footer_detection.pdf" +PDF_FULL_FEATURES = Path(__file__).parent / "pdf" / "full_features.pdf" +PDF_FIGURES_EXTRACTION = Path(__file__).parent / "pdf" / "test_figures_extraction.pdf" +PDF_SMART_HEADER_FOOTER_DETECTION = ( + Path(__file__).parent / "pdf" / "test_header_footer_detection.pdf" ) # test PDFs from official python documentation -PDF_PYTHON_LOGGING = os.path.join(os.path.dirname(__file__), "pdf", "howto-logging.pdf") - - -def obj_equal(class_type, instance1, instance2): - """ - Do a attribute based comparison of instances. - - :param class_type: both instances must be of this type - :param instance1: first object - :param instance2: second object - :return: True if all attributes are equal else False - """ - if not isinstance(instance1, class_type) or not isinstance(instance2, class_type): - # don't attempt to compare against unrelated types - return NotImplemented +PDF_PYTHON_LOGGING = Path(__file__).parent / "pdf" / "howto-logging.pdf" - # get attributes of each and exclude special names and back references - self_attr = [ - attr - for attr in dir(instance1) - if (not attr.startswith("__") and not attr.startswith("b_")) - ] - other_attr = [ - attr - for attr in dir(instance2) - if (not attr.startswith("__") and not attr.startswith("b_")) - ] - if set(self_attr) == set(other_attr): - for attr in self_attr: - if getattr(instance1, attr) != getattr(instance1, attr): - # TODO this uses the equality operator which might fail for referred elements like page on Position - return False - return True - return False +# test PDF for rect extraction generateby by sphinx-simplepdf +PDF_RECTS_EXTRACTION = Path(__file__).parent / "pdf" / "test_rects_extraction.pdf" @pytest.fixture(scope="session") -def load_full_features_pdf(tmpdir_factory, request): +def load_full_features_pdf( + tmpdir_factory: pytest.TempPathFactory, request: pytest.FixtureRequest +) -> tuple(str, ApiObjects | None): """Load test pdf and return temporary directory path and the libpdf object.""" tmpdir = tmpdir_factory.mktemp("full_features_pdf") tmpdir_path = str(tmpdir) @@ -74,5 +45,5 @@ def load_full_features_pdf(tmpdir_factory, request): return tmpdir_path, load( PDF_FULL_FEATURES, save_figures=save_figures, - figure_dir=os.path.join(tmpdir_path, "figures"), + figure_dir=Path(tmpdir_path) / "figures", ) diff --git a/tests/pdf/test_rects_extraction.pdf b/tests/pdf/test_rects_extraction.pdf new file mode 100644 index 0000000000000000000000000000000000000000..b8b9096f00f3b9d2f65d6015a5560f1a6756c8a1 GIT binary patch literal 82959 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 ?lazx4J?vC?ZM>W;hgE$2J%T)jygMy3au{_; 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�<;r^8j!YGw;e1xcU}m*V%JuY5?V}-!bkaONbKq+hxaaly9657r$9UDr{c)C z1a_n;kEV4d@j` zl*h1?^Jwk1`Lx{Mml1GQIF~>eMs}j{J|5CprtK4 z8-_WJ&0j_$#)q6=x{iu9^d&-8lt`mU_W%PB9jO(mk!f3PGwhvx($@&vfdLC+&aA0x zCBfB8Ju0etLO2n`-tj8vT-rFt&Zt8{pY-}VxWtiTe$Z7)*V!eKCvhmG+n=Uf73Ni$^Ps^NOet3;G9w|eA}euK+l8JLc|_g^ zO@Ox*pR>0My6kkg*>RvIh9Je?hA@eAYNMRdK{iBscO&ngy|TPt6d-$gco6Grq}q6V zLy4_9v*GXf@K}42p7C>!;E;ok)KQ~1Zh|2xLvCYhgy@&~RKZw{kCETp2}S~h1zZ8r z!XG5a6RnL9$WXLeXuu5+S1ShX?;gQ?~)me*ElJhKKy+tRw3Y++vig-_YnB% z5O(0&u*DcOQ+4~R4OP^YK&KrU`S#WMVRg>1)f2@R=2zcx+spM6GQZ9{!-pe5gd{qK zXU<=zmu=YjdN>-gKh1Cq@dd^~IJEW`Wau=@yC&+~owxnod(b!iE43KE-`bFpu*IlM zJ^Pt_0lV5Q1BiH#^_#Sajx8sd#PULeP2L>wOWH4OR*Y{ z)ORWB?J;>;GI+krRqCdiKO^YcVr;dBp=)*HVSpWkW<4Owo^`Vxeajw0vjG10lYLLn z#xHqLgKoi0t22z}Sxd>qmLSp6k@*c3k;&LOVlnX+OUv20<_)G|z3Kh&Os7|j!0GC1 p#p#q~;w*7D0X^BjSG22}v5T9Ri@60nH!BAhCp;ykgrX$;{{a~U@4Ns2 literal 0 HcmV?d00001 diff --git a/tests/test_catalog.py b/tests/test_catalog.py index 5987933..6a7aced 100644 --- a/tests/test_catalog.py +++ b/tests/test_catalog.py @@ -12,7 +12,7 @@ def test_catalog_with_empty_outline(): """Check if catalog extracted correctly with pdf that has empty outline.""" runner = CliRunner() - result = runner.invoke(libpdf.core.main_cli, [PDF_WITH_EMPTY_OUTLINE]) + result = runner.invoke(libpdf.core.main_cli, [str(PDF_WITH_EMPTY_OUTLINE)]) assert result.exit_code == 0 objects = libpdf.load(PDF_WITH_EMPTY_OUTLINE) diff --git a/tests/test_cli.py b/tests/test_cli.py index d2a6a8b..eec1c55 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -13,6 +13,8 @@ def test_cli_ok(path): """Check if CLI exits with code 0 when no errors occur.""" runner = CliRunner() - result = runner.invoke(main_cli, [path, "-o", "out.yaml", "-f", "yaml"]) + result = runner.invoke( + main_cli, [str(path.absolute()), "-o", "out.yaml", "-f", "yaml"] + ) assert result.exception is None assert result.exit_code == 0 diff --git a/tests/test_figures.py b/tests/test_figures.py index 7054eb2..f5d662f 100644 --- a/tests/test_figures.py +++ b/tests/test_figures.py @@ -12,7 +12,7 @@ def test_figures_extract_with_invalid_bbox(): """Check if figures extraction correctly when figures have invalid bbox.""" runner = CliRunner() - result = runner.invoke(libpdf.core.main_cli, [PDF_FIGURE_WITH_INVALID_BBOX]) + result = runner.invoke(libpdf.core.main_cli, [str(PDF_FIGURE_WITH_INVALID_BBOX)]) assert result.exit_code == 0 objects = libpdf.load(PDF_FIGURE_WITH_INVALID_BBOX) diff --git a/tests/test_rects.py b/tests/test_rects.py new file mode 100644 index 0000000..6393151 --- /dev/null +++ b/tests/test_rects.py @@ -0,0 +1,233 @@ +"""Test rects extraction.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from collections.abc import Sequence + + from libpdf.apiobjects import ApiObjects, Chapter, Paragraph, Rect, Table + +import libpdf +from tests.conftest import ( + PDF_RECTS_EXTRACTION, +) + + +def find_chapter(objects: ApiObjects, chapter_name: str) -> Chapter: + """ + search for given chapter in the objects. + + :return: found chapter + """ + chapters = objects.flattened.chapters + assert len(chapters) > 0 + + chapter = next((c for c in chapters if c.title == chapter_name), None) + + assert chapter is not None + return chapter + + +def check_chapter_contains_text_paragraph( + chapter: Chapter, text: str +) -> [Paragraph | None]: + """ + check for text in chapter paragraphs. + + :return: found paragraph + """ + assert chapter.content is not None + + for content in chapter.content: + if content.type == "paragraph" and text in content.textbox.text: + return content + + return None + + +def check_chapter_contains_text_rect(chapter: Chapter, text: str) -> [Paragraph | None]: + """ + check for text in chapter rects. + + :return: found rect + """ + assert chapter.content is not None + + for content in chapter.content: + if content.type == "rect" and text in content.textbox.text: + return content + + return None + + +def check_chapter_rects_count(chapter: Chapter) -> int: + """ + check for number of rects in chapter. + + :return: number of rects + """ + assert chapter.content is not None + count = 0 + + for content in chapter.content: + if content.type == "rect": + count += 1 + + return count + + +def check_content_color(content: Rect, color: Sequence[int]) -> bool: + """ + check rect color is equal given color. + + :return: True if equal + """ + return content.non_stroking_color == color + + +def check_content_margins_equal(rect: Rect, paragraph: Paragraph) -> bool: + """ + check for text margins between rect and paragraph. + + :return: True if x0 equals + """ + return paragraph.textbox.x0 == rect.textbox.x0 + + +def check_content_margins_greater( + rect: Rect, paragraph: Paragraph, offset: int +) -> bool: + """ + check for text margins of given rect.x0 + offset is greater or equal paragraph x0. + + :return: found paragraph + """ + return rect.textbox.x0 + offset >= paragraph.textbox.x0 + + +def find_tables(chapter: Chapter) -> [Table]: + """ + check for number of rects in chapter. + + :return: List of Tables + """ + assert chapter.content is not None + + return [content for content in chapter.content if content.type == "table"] + + +def test_rects_extraction_code_block() -> None: + """Test rect extraction of multiline codeblock.""" + smart_page_crop = ( + True # remove header and footers so rects IN chapters are left only. + ) + + objects = libpdf.load(PDF_RECTS_EXTRACTION, smart_page_crop=smart_page_crop) + assert objects.flattened.rects is not None + + chapter = find_chapter(objects, "Code Block Highlighting") + + assert chapter is not None + + assert check_chapter_rects_count(chapter) == 1 + + paragraph = check_chapter_contains_text_paragraph( + chapter, "def decode_title(obj_bytes: bytes) -> str:" + ) + assert paragraph is not None + + rect = check_chapter_contains_text_rect( + chapter, "def decode_title(obj_bytes: bytes) -> str:" + ) + assert rect is not None + + assert check_content_color(rect, (0.941176, 0.941176, 0.941176)) + + assert check_content_margins_equal(paragraph, rect) + + +def test_rects_extraction_code_inline() -> None: + """Test rect extraction of inline codeblock.""" + smart_page_crop = ( + True # remove header and footers so rects IN chapters are left only. + ) + + objects = libpdf.load(PDF_RECTS_EXTRACTION, smart_page_crop=smart_page_crop) + assert objects.flattened.rects is not None + + chapter = find_chapter(objects, "Code Inline Highlighting") + + # 2 inline code blocks, but the first one is broken in two lines + assert check_chapter_rects_count(chapter) == 1 * 3 + + paragraph = check_chapter_contains_text_paragraph( + chapter, "from pathlib import Path" + ) + assert paragraph is not None + + rect = check_chapter_contains_text_rect(chapter, "from pathlib import Path") + assert rect is not None + assert rect.textbox.text == "from pathlib import Path" + assert check_content_color(rect, (0.945098, 0.945098, 0.945098)) + assert check_content_margins_greater(rect, paragraph, 234) + + assert ( + check_chapter_contains_text_rect( + chapter, "decode_title(obj_bytes: bytes) -> str" + ) + is None + ) + rect = check_chapter_contains_text_rect(chapter, "decode_title(obj_bytes: bytes)") + assert rect is not None + rect_str = check_chapter_contains_text_rect(chapter, "str ") + assert rect_str is not None + + assert rect_str.textbox.x0 < rect.textbox.x0 + + +def test_rects_extraction_adminition() -> None: + """Test rect extraction of 3 admonitions.""" + smart_page_crop = ( + True # remove header and footers so rects IN chapters are left only. + ) + + objects = libpdf.load(PDF_RECTS_EXTRACTION, smart_page_crop=smart_page_crop) + assert objects.flattened.rects is not None + + chapter = find_chapter(objects, "Adminition") + + assert ( + check_chapter_rects_count(chapter) == 3 * 2 + ) # 2 rects per admonition, 3 types of admonition + + rect = check_chapter_contains_text_rect(chapter, "A very importing Adminition") + assert rect is not None + assert check_content_color(rect, (0.858824, 0.980392, 0.956863)) + + rect_inner = check_chapter_contains_text_rect(chapter, "Wichtig") + assert rect_inner is not None + assert check_content_color(rect, (0.858824, 0.980392, 0.956863)) + + +def test_rects_extraction_table() -> None: + """Test rect extraction of table colored cells.""" + smart_page_crop = ( + True # remove header and footers so rects IN chapters are left only. + ) + + objects = libpdf.load(PDF_RECTS_EXTRACTION, smart_page_crop=smart_page_crop) + assert objects.flattened.rects is not None + + chapter = find_chapter(objects, "Tables") + + tables = find_tables(chapter) + + assert len(tables) == 1 + + table = tables[0] + assert table.columns_count == 1 * 3 + assert table.rows_count == 1 + + assert check_chapter_rects_count(chapter) == 1 * 5 diff --git a/tox.ini b/tox.ini index e7e178a..e649c91 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ isolated_build = True # python 3.11 and 3.12 test ok on Linux but fail on Windows due to Pillow # Currently used Pillow 9.0.1 only supports Python 3.10 according to # https://pillow.readthedocs.io/en/latest/installation.html#python-support -envlist = py{38,39,310,311,312}, docs, black +envlist = py{38,39,310,311,312}, docs, lint, format-check [testenv] allowlist_externals = poetry @@ -36,7 +36,8 @@ basepython = python3.12 commands= poetry install --no-root poetry run ruff format --check {[testenv]py_folders} - poetry run ruff {[testenv]py_folders} + # extend the file list once a file is touched so lint issues get fixed over time + poetry run ruff tests/conftest.py tests/test_rects.py [testenv:format-check] envdir = {toxworkdir}/py312