From 60b3951b72324b9ac215ae0eacf6c534582ae962 Mon Sep 17 00:00:00 2001 From: Jake Stockwin Date: Mon, 22 Jun 2020 15:31:59 +0100 Subject: [PATCH] [docs] Add documentation example for element_ordering --- docs/source/example_files/columns.pdf | Bin 0 -> 7353 bytes docs/source/example_files/grid.pdf | Bin 0 -> 12768 bytes docs/source/examples/element_ordering.rst | 163 ++++++++++++++++++ docs/source/examples/index.rst | 2 + .../test_element_ordering.py | 100 +++++++++++ 5 files changed, 265 insertions(+) create mode 100644 docs/source/example_files/columns.pdf create mode 100644 docs/source/example_files/grid.pdf create mode 100644 docs/source/examples/element_ordering.rst create mode 100644 tests/test_doc_examples/test_element_ordering.py diff --git a/docs/source/example_files/columns.pdf b/docs/source/example_files/columns.pdf new file mode 100644 index 0000000000000000000000000000000000000000..de4db28a7b6e6a7f78d5efc7e4535c40bd1052c0 GIT binary patch literal 7353 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 T3VoDaV);u_^75+csZstPyhnpx literal 0 HcmV?d00001 diff --git a/docs/source/example_files/grid.pdf b/docs/source/example_files/grid.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a0107f003d08cf3453f84d1ba8c6050a0948145b GIT binary patch literal 12768 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. + + +Default +....... + +The default element ordering is left to right, top to bottom. + +.. code-block:: python + + from py_pdf_parser.loaders import load_file + + file_path = "grid.pdf" + + # Default - left to right, top to bottom + document = load_file(file_path) + print([element.text() for element in document.elements]) + +This results in +:: + + ['Top Left', 'Top Right', 'Bottom Left', 'Bottom Right'] + +Presets +....... + +There are also preset orderings for ``right to left, top to bottom``, +``top to bottom, left to right``, and ``top to bottom, right to left``. You can use +these by importing the :class:`~py_pdf_parser.components.ElementOrdering` class from +:py:mod:`py_pdf_parser.components` and passing these as the ``element_ordering`` +argument to :class:`~py_pdf_parser.components.PDFDocument`. Note that keyword arguments +to :meth:`~py_pdf_parser.loaders.load` and :meth:`~py_pdf_parser.loaders.load_file` get +passed through to the :class:`~py_pdf_parser.components.PDFDocument`. + +.. code-block:: python + + from py_pdf_parser.loaders import load_file + from py_pdf_parser.components import ElementOrdering + + # Preset - right to left, top to bottom + document = load_file( + file_path, element_ordering=ElementOrdering.RIGHT_TO_LEFT_TOP_TO_BOTTOM + ) + print([element.text() for element in document.elements]) + + # Preset - top to bottom, left to right + document = load_file( + file_path, element_ordering=ElementOrdering.TOP_TO_BOTTOM_LEFT_TO_RIGHT + ) + print([element.text() for element in document.elements]) + + # Preset - top to bottom, right to left + document = load_file( + file_path, element_ordering=ElementOrdering.TOP_TO_BOTTOM_RIGHT_TO_LEFT + ) + print([element.text() for element in document.elements]) + +which results in + +:: + + ['Top Right', 'Top Left', 'Bottom Right', 'Bottom Left'] + ['Bottom Left', 'Top Left', 'Bottom Right', 'Top Right'] + ['Top Right', 'Bottom Right', 'Top Left', 'Bottom Left'] + +Custom Ordering +............... + +If none of the presets give an ordering you are looking for, you can also pass a +callable as the ``element_ordering`` argument of +:class:`~py_pdf_parser.components.PDFDocument`. This callable will be given a list of +elements for each page, and should return a list of the same elements, in the desired +order. + +.. important:: + + The elements which get passed to your function will be PDFMiner.six elements, and NOT + class :class:`~py_pdf_parser.componenets.PDFElement`. You can access the ``x0``, + ``x1``, ``y0``, ``y1`` directly, and extract the text using `get_text()`. Other + options are available: please familiarise yourself with the PDFMiner.six + documentation. + +.. note:: + + Your function will be called multiple times, once for each page of the document. + Elements will always be considered in order of increasing page number, your function + only controls the ordering within each page. + +For example, if we wanted to implement an ordering which is bottom to top, left to right +then we can do this as follows: + +.. code-block:: python + + from py_pdf_parser.loaders import load_file + + # Custom - bottom to top, left to right + def ordering_function(elements): + """ + Note: Elements will be PDFMiner.six elements. The x axis is positive as you go left + to right, and the y axis is positive as you go bottom to top, and hence we can + simply sort according to this. + """ + return sorted(elements, key=lambda elem: (elem.x0, elem.y0)) + + + document = load_file(file_path, element_ordering=ordering_function) + print([element.text() for element in document.elements]) + +which results in + +:: + + ['Bottom Left', 'Top Left', 'Bottom Right', 'Top Right'] + +Multiple Columns +................ + +Finally, suppose our PDF has multiple columns, like +:download:`this example `. + +If we don't specify an ``element_ordering``, the elements will be extracted in the +following order: + +:: + + ['Column 1 Title', 'Column 2 Title', 'Here is some column 1 text.', 'Here is some column 2 text.', 'Col 1 left', 'Col 1 right', 'Col 2 left', 'Col 2 right'] + +If we visualise this document +(see the :ref:`simple-memo` example if you don't know how to do this), then we can see +that the column divider is at an ``x`` value of about 300. Using this information, we +can specify a custom ordering function which will order the elements left to right, +top to bottom, but in each column individually. + +.. code-block:: python + + from py_pdf_parser.loaders import load_file + + document = load_file("columns.pdf") + + def column_ordering_function(elements): + """ + The first entry in the key is False for colum 1, and Tru for column 2. The second + and third keys just give left to right, top to bottom. + """ + return sorted(elements, key=lambda elem: (elem.x0 > 300, -elem.y0, elem.x0)) + + + document = load_file(file_path, element_ordering=column_ordering_function) + print([element.text() for element in document.elements]) + +which returns the elements in the correct order: + +:: + + ['Column 1 Title', 'Here is some column 1 text.', 'Col 1 left', 'Col 1 right', 'Column 2 Title', 'Here is some column 2 text.', 'Col 2 left', 'Col 2 right'] diff --git a/docs/source/examples/index.rst b/docs/source/examples/index.rst index 873761a8..31e0fce3 100644 --- a/docs/source/examples/index.rst +++ b/docs/source/examples/index.rst @@ -6,10 +6,12 @@ Below you can find links to the following examples: - The :ref:`simple-memo` example shows the very basics of using py-pdf-parser. You will see how to load a pdf document, start filtering the elements, and extract text from certain elements in the document. - The :ref:`order-summary` example explains how to use font mappings, sections, and how to extract simple tables. - The :ref:`more-tables` example explains tables in more detail, showing how to extract more complex tables. +- The :ref:`element-ordering` example shows how to specify different orderings for the elements on a page. .. toctree:: simple_memo order_summary more_tables + element_ordering diff --git a/tests/test_doc_examples/test_element_ordering.py b/tests/test_doc_examples/test_element_ordering.py new file mode 100644 index 00000000..ec7939aa --- /dev/null +++ b/tests/test_doc_examples/test_element_ordering.py @@ -0,0 +1,100 @@ +import os + +from py_pdf_parser.components import ElementOrdering +from py_pdf_parser.loaders import load_file + +from tests.base import BaseTestCase + + +class TestSimpleMemo(BaseTestCase): + def test_output_is_correct(self): + file_path = os.path.join( + os.path.dirname(__file__), "../../docs/source/example_files/grid.pdf" + ) + + # Default - left to right, top to bottom + document = load_file(file_path) + self.assertListEqual( + [element.text() for element in document.elements], + ["Top Left", "Top Right", "Bottom Left", "Bottom Right"], + ) + + # Preset - right to left, top to bottom + document = load_file( + file_path, element_ordering=ElementOrdering.RIGHT_TO_LEFT_TOP_TO_BOTTOM + ) + self.assertListEqual( + [element.text() for element in document.elements], + ["Top Right", "Top Left", "Bottom Right", "Bottom Left"], + ) + + # Preset - top to bottom, left to right + document = load_file( + file_path, element_ordering=ElementOrdering.TOP_TO_BOTTOM_LEFT_TO_RIGHT + ) + self.assertListEqual( + [element.text() for element in document.elements], + ["Bottom Left", "Top Left", "Bottom Right", "Top Right"], + ) + + # Preset - top to bottom, right to left + document = load_file( + file_path, element_ordering=ElementOrdering.TOP_TO_BOTTOM_RIGHT_TO_LEFT + ) + self.assertListEqual( + [element.text() for element in document.elements], + ["Top Right", "Bottom Right", "Top Left", "Bottom Left"], + ) + + # Custom - bottom to top, left to right + def ordering_function(elements): + return sorted(elements, key=lambda elem: (elem.x0, elem.y0)) + + document = load_file(file_path, element_ordering=ordering_function) + self.assertListEqual( + [element.text() for element in document.elements], + ["Bottom Left", "Top Left", "Bottom Right", "Top Right"], + ) + + # Custom - This PDF has columns! + # TODO: CHANGE PATH! + file_path = os.path.join( + os.path.dirname(__file__), "../../docs/source/example_files/columns.pdf" + ) + + # Default - left to right, top to bottom + document = load_file(file_path) + self.assertListEqual( + [element.text() for element in document.elements], + [ + "Column 1 Title", + "Column 2 Title", + "Here is some column 1 text.", + "Here is some column 2 text.", + "Col 1 left", + "Col 1 right", + "Col 2 left", + "Col 2 right", + ], + ) + + # Visualise, and we can see that the middle is at around x = 300. + # visualise(document) + + def column_ordering_function(elements): + return sorted(elements, key=lambda elem: (elem.x0 > 300, -elem.y0, elem.x0)) + + document = load_file(file_path, element_ordering=column_ordering_function) + self.assertListEqual( + [element.text() for element in document.elements], + [ + "Column 1 Title", + "Here is some column 1 text.", + "Col 1 left", + "Col 1 right", + "Column 2 Title", + "Here is some column 2 text.", + "Col 2 left", + "Col 2 right", + ], + )