diff --git a/PyPDFForm/core/constants.py b/PyPDFForm/core/constants.py index b1831163..9527e81a 100644 --- a/PyPDFForm/core/constants.py +++ b/PyPDFForm/core/constants.py @@ -22,6 +22,7 @@ DEFAULT_FONT = "Helvetica" DEFAULT_FONT_SIZE = 12 DEFAULT_FONT_COLOR = (0, 0, 0) +PREVIEW_FONT_COLOR = (1, 0, 0) CHECKBOX_TO_DRAW = "\u2713" RADIO_TO_DRAW = "\u25CF" diff --git a/PyPDFForm/core/template.py b/PyPDFForm/core/template.py index fd035610..1cf05c42 100644 --- a/PyPDFForm/core/template.py +++ b/PyPDFForm/core/template.py @@ -276,6 +276,12 @@ def get_draw_text_coordinates( ) -> Tuple[Union[float, int], Union[float, int]]: """Returns coordinates to draw text at given a PDF form text element.""" + if element_middleware.preview: + return ( + float(element[constants.ANNOTATION_RECTANGLE_KEY][0]), + float(element[constants.ANNOTATION_RECTANGLE_KEY][3]) + 5 + ) + element_value = element_middleware.value or "" length = ( min(len(element_value), element_middleware.max_length) diff --git a/PyPDFForm/core/utils.py b/PyPDFForm/core/utils.py index c0fab8fb..b7b2768d 100644 --- a/PyPDFForm/core/utils.py +++ b/PyPDFForm/core/utils.py @@ -154,6 +154,23 @@ def checkbox_radio_to_draw( return new_element +def preview_element_to_draw( + element: ELEMENT_TYPES +) -> Text: + """Converts an element to a preview text element.""" + + new_element = Text( + element_name=element.name, + element_value="{" + f" {element.name} " + "}", + ) + new_element.font = constants.DEFAULT_FONT + new_element.font_size = constants.DEFAULT_FONT_SIZE + new_element.font_color = constants.PREVIEW_FONT_COLOR + new_element.preview = True + + return new_element + + def remove_all_elements(pdf: bytes) -> bytes: """Removes all elements from a pdfrw parsed PDF form.""" diff --git a/PyPDFForm/middleware/text.py b/PyPDFForm/middleware/text.py index adfd7564..3d67aba4 100644 --- a/PyPDFForm/middleware/text.py +++ b/PyPDFForm/middleware/text.py @@ -25,6 +25,7 @@ def __init__( self.character_paddings = None self.text_lines = None self.text_line_x_coordinates = None + self.preview = False @property def schema_definition(self) -> dict: diff --git a/PyPDFForm/wrapper.py b/PyPDFForm/wrapper.py index 35185891..210cc05a 100644 --- a/PyPDFForm/wrapper.py +++ b/PyPDFForm/wrapper.py @@ -83,6 +83,18 @@ def __add__(self, other: Wrapper) -> Wrapper: return new_obj + @property + def preview(self) -> bytes: + """Inspects all supported elements' names for the PDF form.""" + + return filler.fill( + self.stream, + { + key: utils.preview_element_to_draw(value) + for key, value in self.elements.items() + }, + ) + def fill( self, data: Dict[str, Union[str, bool, int]], diff --git a/docs/examples.md b/docs/examples.md index 78d4a42e..126bf01c 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -14,6 +14,30 @@ found [here](https://github.com/chinapandaman/PyPDFForm/raw/master/pdf_samples/s It has three text fields `test`, `test_2`, `test_3` and three checkboxes `check`, `check_2`, `check_3` scattered on three pages. +## Preview a PDF form + +This example demos how to preview a PDF form. The preview can be used to inspect the elements' names for the PDF form. +The generated output will have each element's name labeled on top of it in red. + +```python +import os + +from PyPDFForm import PyPDFForm + +PATH_TO_DOWNLOADED_SAMPLE_PDF_FORM = os.path.join( + os.path.expanduser("~/Downloads"), "sample_template.pdf" +) # Change this to where you downloaded the sample PDF form + +PATH_TO_FILLED_PDF_FORM = os.path.join( + os.path.expanduser("~"), "output.pdf" +) # Change this to where you wish to put your filled PDF form + +with open(PATH_TO_FILLED_PDF_FORM, "wb+") as output: + output.write( + PyPDFForm(PATH_TO_DOWNLOADED_SAMPLE_PDF_FORM).preview + ) +``` + ## Filling a PDF form This example demos filling a PDF form in the most straight forward way diff --git a/pdf_samples/preview/test_preview.pdf b/pdf_samples/preview/test_preview.pdf new file mode 100644 index 00000000..7e047b1c Binary files /dev/null and b/pdf_samples/preview/test_preview.pdf differ diff --git a/pdf_samples/preview/test_preview_comb_text_field.pdf b/pdf_samples/preview/test_preview_comb_text_field.pdf new file mode 100644 index 00000000..a9533f70 Binary files /dev/null and b/pdf_samples/preview/test_preview_comb_text_field.pdf differ diff --git a/pdf_samples/preview/test_preview_paragraph_complex.pdf b/pdf_samples/preview/test_preview_paragraph_complex.pdf new file mode 100644 index 00000000..93ea1844 Binary files /dev/null and b/pdf_samples/preview/test_preview_paragraph_complex.pdf differ diff --git a/pdf_samples/preview/test_preview_sejda.pdf b/pdf_samples/preview/test_preview_sejda.pdf new file mode 100644 index 00000000..7db2378f Binary files /dev/null and b/pdf_samples/preview/test_preview_sejda.pdf differ diff --git a/pdf_samples/preview/test_preview_sejda_complex.pdf b/pdf_samples/preview/test_preview_sejda_complex.pdf new file mode 100644 index 00000000..cfe185c4 Binary files /dev/null and b/pdf_samples/preview/test_preview_sejda_complex.pdf differ diff --git a/tests/test_preview.py b/tests/test_preview.py new file mode 100644 index 00000000..f66611ee --- /dev/null +++ b/tests/test_preview.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- + +import os + +from PyPDFForm import PyPDFForm + + +def test_preview(template_stream, pdf_samples, request): + expected_path = os.path.join(pdf_samples, "preview", "test_preview.pdf") + with open(expected_path, "rb+") as f: + preview = PyPDFForm(template_stream).preview + + request.config.results["expected_path"] = expected_path + request.config.results["stream"] = preview + + expected = f.read() + + assert len(preview) == len(expected) + assert preview == expected + + +def test_preview_sejda(sejda_template, pdf_samples, request): + expected_path = os.path.join(pdf_samples, "preview", "test_preview_sejda.pdf") + with open(expected_path, "rb+") as f: + preview = PyPDFForm(sejda_template).preview + + request.config.results["expected_path"] = expected_path + request.config.results["stream"] = preview + + expected = f.read() + + assert len(preview) == len(expected) + assert preview == expected + + +def test_preview_paragraph_complex(sample_template_paragraph_complex, pdf_samples, request): + expected_path = os.path.join(pdf_samples, "preview", "test_preview_paragraph_complex.pdf") + with open(expected_path, "rb+") as f: + preview = PyPDFForm(sample_template_paragraph_complex).preview + + request.config.results["expected_path"] = expected_path + request.config.results["stream"] = preview + + expected = f.read() + + assert len(preview) == len(expected) + assert preview == expected + + +def test_preview_sejda_complex(sejda_template_complex, pdf_samples, request): + expected_path = os.path.join(pdf_samples, "preview", "test_preview_sejda_complex.pdf") + with open(expected_path, "rb+") as f: + preview = PyPDFForm(sejda_template_complex).preview + + request.config.results["expected_path"] = expected_path + request.config.results["stream"] = preview + + expected = f.read() + + assert len(preview) == len(expected) + assert preview == expected + + +def test_preview_comb_text_field(sample_template_with_comb_text_field, pdf_samples, request): + expected_path = os.path.join(pdf_samples, "preview", "test_preview_comb_text_field.pdf") + with open(expected_path, "rb+") as f: + preview = PyPDFForm(sample_template_with_comb_text_field).preview + + request.config.results["expected_path"] = expected_path + request.config.results["stream"] = preview + + expected = f.read() + + assert len(preview) == len(expected) + assert preview == expected