diff --git a/PyPDFForm/patterns.py b/PyPDFForm/patterns.py index 479f60a7..86e1cdad 100644 --- a/PyPDFForm/patterns.py +++ b/PyPDFForm/patterns.py @@ -158,3 +158,14 @@ def simple_flatten_generic(annot: DictionaryObject) -> None: annot[NameObject(Ff)] = NumberObject( int(annot.get(NameObject(Ff), 0)) | READ_ONLY # noqa ) + + +def update_created_text_field_alignment(annot: DictionaryObject, val: int) -> None: + """Patterns to update text alignment for text annotations created by the library.""" + + annot[NameObject(Q)] = NumberObject(val) + + +NON_ACRO_FORM_PARAM_TO_FUNC = { + "alignment": update_created_text_field_alignment +} diff --git a/PyPDFForm/widgets/base.py b/PyPDFForm/widgets/base.py index 398e6058..d772fb80 100644 --- a/PyPDFForm/widgets/base.py +++ b/PyPDFForm/widgets/base.py @@ -2,12 +2,16 @@ """Contains base class for all widgets to create.""" from io import BytesIO -from typing import List +from typing import List, cast -from pypdf import PdfReader +from pypdf import PdfReader, PdfWriter +from pypdf.generic import DictionaryObject from reportlab.lib.colors import Color from reportlab.pdfgen.canvas import Canvas +from ..constants import Annots +from ..template import get_widget_key +from ..patterns import NON_ACRO_FORM_PARAM_TO_FUNC from ..utils import stream_to_io @@ -16,6 +20,7 @@ class Widget: USER_PARAMS = [] COLOR_PARAMS = [] + ALLOWED_NON_ACRO_FORM_PARAMS = [] NONE_DEFAULTS = [] ACRO_FORM_FUNC = "" @@ -36,6 +41,7 @@ def __init__( "x": x, "y": y, } + self.non_acro_form_params = [] for each in self.USER_PARAMS: user_input, param = each @@ -51,6 +57,10 @@ def __init__( elif user_input in self.NONE_DEFAULTS: self.acro_form_params[param] = None + for each in self.ALLOWED_NON_ACRO_FORM_PARAMS: + if each in kwargs: + self.non_acro_form_params.append((each, kwargs.get(each))) + def watermarks(self, stream: bytes) -> List[bytes]: """Returns a list of watermarks after creating the widget.""" @@ -76,3 +86,26 @@ def watermarks(self, stream: bytes) -> List[bytes]: watermark.read() if i == self.page_number - 1 else b"" for i in range(page_count) ] + + +def handle_non_acro_form_params(pdf: bytes, key: str, params: list) -> bytes: + """Handles non acro form parameters when creating a widget.""" + + pdf_file = PdfReader(stream_to_io(pdf)) + out = PdfWriter() + out.append(pdf_file) + + for page in out.pages: + for annot in page.get(Annots, []): # noqa + annot = cast(DictionaryObject, annot.get_object()) + _key = get_widget_key(annot.get_object()) + + if _key == key: + for param in params: + if param[0] in NON_ACRO_FORM_PARAM_TO_FUNC: + NON_ACRO_FORM_PARAM_TO_FUNC[param[0]](annot, param[1]) + + with BytesIO() as f: + out.write(f) + f.seek(0) + return f.read() diff --git a/PyPDFForm/widgets/text.py b/PyPDFForm/widgets/text.py index 962bb08f..a4d74a81 100644 --- a/PyPDFForm/widgets/text.py +++ b/PyPDFForm/widgets/text.py @@ -19,5 +19,6 @@ class TextWidget(Widget): ("max_length", "maxlen"), ] COLOR_PARAMS = ["font_color", "bg_color", "border_color"] + ALLOWED_NON_ACRO_FORM_PARAMS = ["alignment"] NONE_DEFAULTS = ["max_length"] ACRO_FORM_FUNC = "textfield" diff --git a/PyPDFForm/wrapper.py b/PyPDFForm/wrapper.py index 6d962364..df9c732a 100644 --- a/PyPDFForm/wrapper.py +++ b/PyPDFForm/wrapper.py @@ -22,6 +22,7 @@ from .utils import (get_page_streams, merge_two_pdfs, preview_widget_to_draw, remove_all_widgets) from .watermark import create_watermarks_and_draw, merge_watermarks_with_pdf +from .widgets.base import handle_non_acro_form_params from .widgets.checkbox import CheckBoxWidget from .widgets.dropdown import DropdownWidget from .widgets.text import TextWidget @@ -213,11 +214,17 @@ def create_widget( if _class is None: return self - watermarks = _class( + obj = _class( name=name, page_number=page_number, x=x, y=y, **kwargs - ).watermarks(self.read()) + ) + watermarks = obj.watermarks(self.read()) self.stream = merge_watermarks_with_pdf(self.read(), watermarks) + if obj.non_acro_form_params: + self.stream = handle_non_acro_form_params(self.stream, + name, + obj.non_acro_form_params) + new_widgets = build_widgets(self.read()) for k, v in self.widgets.items(): if k in new_widgets: diff --git a/docs/prepare.md b/docs/prepare.md index 03154ec0..0ecd1a7d 100644 --- a/docs/prepare.md +++ b/docs/prepare.md @@ -40,7 +40,8 @@ new_form = PdfWrapper("dummy.pdf").create_widget( font_color=(1, 0, 0), # optional bg_color=(0, 0, 1), # optional border_color=(1, 0, 0), # optional - border_width=5 # optional + border_width=5, # optional + alignment=0 # optional, 0=left, 1=center, 2=right ) with open("output.pdf", "wb+") as output: diff --git a/pdf_samples/scenario/issues/PPF-627-expected-0.pdf b/pdf_samples/scenario/issues/PPF-627-expected-0.pdf index 2fc5de99..06903d72 100644 Binary files a/pdf_samples/scenario/issues/PPF-627-expected-0.pdf and b/pdf_samples/scenario/issues/PPF-627-expected-0.pdf differ diff --git a/pdf_samples/scenario/issues/PPF-627-expected-1.pdf b/pdf_samples/scenario/issues/PPF-627-expected-1.pdf index e60a9a99..2cef48c3 100644 Binary files a/pdf_samples/scenario/issues/PPF-627-expected-1.pdf and b/pdf_samples/scenario/issues/PPF-627-expected-1.pdf differ diff --git a/pdf_samples/scenario/issues/PPF-627-expected-2.pdf b/pdf_samples/scenario/issues/PPF-627-expected-2.pdf index 24fa46c7..fd11d272 100644 Binary files a/pdf_samples/scenario/issues/PPF-627-expected-2.pdf and b/pdf_samples/scenario/issues/PPF-627-expected-2.pdf differ diff --git a/pdf_samples/scenario/issues/PPF-627-expected-3.pdf b/pdf_samples/scenario/issues/PPF-627-expected-3.pdf index a2f125af..fd895443 100644 Binary files a/pdf_samples/scenario/issues/PPF-627-expected-3.pdf and b/pdf_samples/scenario/issues/PPF-627-expected-3.pdf differ diff --git a/pdf_samples/simple/scenario/issues/437_expected.pdf b/pdf_samples/simple/scenario/issues/437_expected.pdf index d858327c..cb163bde 100644 Binary files a/pdf_samples/simple/scenario/issues/437_expected.pdf and b/pdf_samples/simple/scenario/issues/437_expected.pdf differ diff --git a/pdf_samples/simple/scenario/issues/PPF-627-expected-0.pdf b/pdf_samples/simple/scenario/issues/PPF-627-expected-0.pdf index b535b134..d9498276 100644 Binary files a/pdf_samples/simple/scenario/issues/PPF-627-expected-0.pdf and b/pdf_samples/simple/scenario/issues/PPF-627-expected-0.pdf differ diff --git a/pdf_samples/simple/scenario/issues/PPF-627-expected-1.pdf b/pdf_samples/simple/scenario/issues/PPF-627-expected-1.pdf index 1c8188f3..e326a108 100644 Binary files a/pdf_samples/simple/scenario/issues/PPF-627-expected-1.pdf and b/pdf_samples/simple/scenario/issues/PPF-627-expected-1.pdf differ diff --git a/pdf_samples/simple/scenario/issues/PPF-627-expected-2.pdf b/pdf_samples/simple/scenario/issues/PPF-627-expected-2.pdf index adf1b2cf..954a4da5 100644 Binary files a/pdf_samples/simple/scenario/issues/PPF-627-expected-2.pdf and b/pdf_samples/simple/scenario/issues/PPF-627-expected-2.pdf differ diff --git a/pdf_samples/simple/scenario/issues/PPF-627-expected-3.pdf b/pdf_samples/simple/scenario/issues/PPF-627-expected-3.pdf index 95d73527..7720dcd8 100644 Binary files a/pdf_samples/simple/scenario/issues/PPF-627-expected-3.pdf and b/pdf_samples/simple/scenario/issues/PPF-627-expected-3.pdf differ diff --git a/pdf_samples/widget/create_text_align_center.pdf b/pdf_samples/widget/create_text_align_center.pdf new file mode 100644 index 00000000..066394da Binary files /dev/null and b/pdf_samples/widget/create_text_align_center.pdf differ diff --git a/pdf_samples/widget/create_text_align_right.pdf b/pdf_samples/widget/create_text_align_right.pdf new file mode 100644 index 00000000..c2acc499 Binary files /dev/null and b/pdf_samples/widget/create_text_align_right.pdf differ diff --git a/tests/test_create_widget.py b/tests/test_create_widget.py index 6728e2e0..16d6f9c1 100644 --- a/tests/test_create_widget.py +++ b/tests/test_create_widget.py @@ -198,6 +198,50 @@ def test_create_text_default(template_stream, pdf_samples, request): assert obj.stream == expected +def test_create_text_align_center(template_stream, pdf_samples, request): + expected_path = os.path.join(pdf_samples, "widget", "create_text_align_center.pdf") + with open(expected_path, "rb+") as f: + obj = PdfWrapper(template_stream).create_widget( + "text", + "foo", + 1, + 100, + 100, + alignment=1, + ) + assert obj.schema["properties"]["foo"]["type"] == "string" + + request.config.results["expected_path"] = expected_path + request.config.results["stream"] = obj.read() + + expected = f.read() + + assert len(obj.stream) == len(expected) + assert obj.stream == expected + + +def test_create_text_align_right(template_stream, pdf_samples, request): + expected_path = os.path.join(pdf_samples, "widget", "create_text_align_right.pdf") + with open(expected_path, "rb+") as f: + obj = PdfWrapper(template_stream).create_widget( + "text", + "foo", + 1, + 100, + 100, + alignment=2, + ) + assert obj.schema["properties"]["foo"]["type"] == "string" + + request.config.results["expected_path"] = expected_path + request.config.results["stream"] = obj.read() + + expected = f.read() + + assert len(obj.stream) == len(expected) + assert obj.stream == expected + + def test_create_text_default_filled(template_stream, pdf_samples, request): expected_path = os.path.join( pdf_samples, "widget", "create_text_default_filled.pdf"