From bf1c3207d7a0d99006f95717397666dbec38481d Mon Sep 17 00:00:00 2001 From: chinapandaman Date: Sun, 26 Nov 2023 01:44:57 -0600 Subject: [PATCH 1/3] PPF-412: changed imports --- PyPDFForm/core/coordinate.py | 8 ++-- PyPDFForm/core/filler.py | 21 +++++----- PyPDFForm/core/font.py | 50 +++++++++++------------ PyPDFForm/core/image.py | 6 +-- PyPDFForm/core/patterns.py | 53 +++++++++++++----------- PyPDFForm/core/template.py | 47 +++++++++++----------- PyPDFForm/core/utils.py | 42 ++++++++++--------- PyPDFForm/core/watermark.py | 22 +++++----- PyPDFForm/middleware/template.py | 31 +++++++------- PyPDFForm/wrapper.py | 69 ++++++++++++++++++-------------- 10 files changed, 184 insertions(+), 165 deletions(-) diff --git a/PyPDFForm/core/coordinate.py b/PyPDFForm/core/coordinate.py index 233de952..344bda7f 100644 --- a/PyPDFForm/core/coordinate.py +++ b/PyPDFForm/core/coordinate.py @@ -4,7 +4,7 @@ from copy import deepcopy from typing import List, Tuple, Union -import pdfrw +from pdfrw import PdfDict from reportlab.pdfbase.pdfmetrics import stringWidth from ..middleware.text import Text @@ -14,7 +14,7 @@ def get_draw_checkbox_radio_coordinates( - element: pdfrw.PdfDict, + element: PdfDict, element_middleware: Text, ) -> Tuple[Union[float, int], Union[float, int]]: """Returns coordinates to draw at given a PDF form checkbox/radio element.""" @@ -42,7 +42,7 @@ def get_draw_checkbox_radio_coordinates( def get_draw_text_coordinates( - element: pdfrw.PdfDict, element_middleware: Text + element: PdfDict, element_middleware: Text ) -> Tuple[Union[float, int], Union[float, int]]: """Returns coordinates to draw text at given a PDF form text element.""" @@ -129,7 +129,7 @@ def get_draw_text_coordinates( def get_text_line_x_coordinates( - element: pdfrw.PdfDict, element_middleware: Text + element: PdfDict, element_middleware: Text ) -> Union[List[float], None]: """ Returns the x coordinates to draw lines diff --git a/PyPDFForm/core/filler.py b/PyPDFForm/core/filler.py index 983734f7..f2007e44 100644 --- a/PyPDFForm/core/filler.py +++ b/PyPDFForm/core/filler.py @@ -3,13 +3,14 @@ from typing import Dict -import pdfrw +from pdfrw import PdfReader from ..middleware.checkbox import Checkbox from ..middleware.constants import ELEMENT_TYPES from ..middleware.radio import Radio -from . import template, utils -from . import watermark as watermark_core +from .template import get_elements_by_page, get_element_key +from .utils import checkbox_radio_to_draw, generate_stream +from .watermark import create_watermarks_and_draw, merge_watermarks_with_pdf from .coordinate import (get_draw_checkbox_radio_coordinates, get_draw_text_coordinates, get_text_line_x_coordinates) @@ -22,23 +23,23 @@ def fill( ) -> bytes: """Fills a PDF using watermarks.""" - template_pdf = pdfrw.PdfReader(fdata=template_stream) + template_pdf = PdfReader(fdata=template_stream) texts_to_draw = {} text_watermarks = [] radio_button_tracker = {} - for page, _elements in template.get_elements_by_page(template_pdf).items(): + for page, _elements in get_elements_by_page(template_pdf).items(): texts_to_draw[page] = [] text_watermarks.append(b"") for _element in _elements: - key = template.get_element_key(_element) + key = get_element_key(_element) needs_to_be_drawn = False if isinstance(elements[key], (Checkbox, Radio)): font_size = checkbox_radio_font_size(_element) - _to_draw = utils.checkbox_radio_to_draw(elements[key], font_size) + _to_draw = checkbox_radio_to_draw(elements[key], font_size) x, y = get_draw_checkbox_radio_coordinates(_element, _to_draw) if isinstance(elements[key], Checkbox) and elements[key].value: needs_to_be_drawn = True @@ -66,13 +67,13 @@ def fill( ) for page, texts in texts_to_draw.items(): - _watermarks = watermark_core.create_watermarks_and_draw( + _watermarks = create_watermarks_and_draw( template_stream, page, "text", texts ) for i, watermark in enumerate(_watermarks): if watermark: text_watermarks[i] = watermark - return watermark_core.merge_watermarks_with_pdf( - utils.generate_stream(template_pdf), text_watermarks + return merge_watermarks_with_pdf( + generate_stream(template_pdf), text_watermarks ) diff --git a/PyPDFForm/core/font.py b/PyPDFForm/core/font.py index 2f554b28..e4b034e2 100644 --- a/PyPDFForm/core/font.py +++ b/PyPDFForm/core/font.py @@ -1,19 +1,19 @@ # -*- coding: utf-8 -*- """Contains helpers for font.""" -import re +from re import findall from io import BytesIO from math import sqrt from typing import Dict, Tuple, Union -import pdfrw -from reportlab.pdfbase import pdfmetrics +from pdfrw import PdfDict, PdfReader +from reportlab.pdfbase.pdfmetrics import registerFont, standardFonts from reportlab.pdfbase.ttfonts import TTFError, TTFont from ..middleware.constants import ELEMENT_TYPES from ..middleware.text import Text -from . import constants -from .constants import DEFAULT_FONT_SIZE +from .constants import DEFAULT_FONT, ANNOTATION_RECTANGLE_KEY, \ + FONT_SIZE_IDENTIFIER, DEFAULT_FONT_SIZE, FONT_COLOR_IDENTIFIER from .patterns import TEXT_FIELD_APPEARANCE_PATTERNS from .template import (get_element_key, get_elements_by_page, get_paragraph_auto_wrap_length, get_paragraph_lines, @@ -29,7 +29,7 @@ def register_font(font_name: str, ttf_stream: bytes) -> bool: buff.seek(0) try: - pdfmetrics.registerFont(TTFont(name=font_name, filename=buff)) + registerFont(TTFont(name=font_name, filename=buff)) result = True except TTFError: result = False @@ -38,10 +38,10 @@ def register_font(font_name: str, ttf_stream: bytes) -> bool: return result -def auto_detect_font(element: pdfrw.PdfDict) -> str: +def auto_detect_font(element: PdfDict) -> str: """Returns the font of the text field if it is one of the standard fonts.""" - result = constants.DEFAULT_FONT + result = DEFAULT_FONT text_appearance = None for pattern in TEXT_FIELD_APPEARANCE_PATTERNS: @@ -57,10 +57,10 @@ def auto_detect_font(element: pdfrw.PdfDict) -> str: for each in text_appearance: if each.startswith("/"): - text_segments = re.findall("[A-Z][^A-Z]*", each.replace("/", "")) + text_segments = findall("[A-Z][^A-Z]*", each.replace("/", "")) - for font in pdfmetrics.standardFonts: - font_segments = re.findall("[A-Z][^A-Z]*", font.replace("-", "")) + for font in standardFonts: + font_segments = findall("[A-Z][^A-Z]*", font.replace("-", "")) if len(font_segments) != len(text_segments): continue @@ -75,7 +75,7 @@ def auto_detect_font(element: pdfrw.PdfDict) -> str: return result -def text_field_font_size(element: pdfrw.PdfDict) -> Union[float, int]: +def text_field_font_size(element: PdfDict) -> Union[float, int]: """ Calculates the font size it should be drawn with given a text field element. @@ -85,31 +85,31 @@ def text_field_font_size(element: pdfrw.PdfDict) -> Union[float, int]: return DEFAULT_FONT_SIZE height = abs( - float(element[constants.ANNOTATION_RECTANGLE_KEY][1]) - - float(element[constants.ANNOTATION_RECTANGLE_KEY][3]) + float(element[ANNOTATION_RECTANGLE_KEY][1]) + - float(element[ANNOTATION_RECTANGLE_KEY][3]) ) return height * 2 / 3 -def checkbox_radio_font_size(element: pdfrw.PdfDict) -> Union[float, int]: +def checkbox_radio_font_size(element: PdfDict) -> Union[float, int]: """ Calculates the font size it should be drawn with given a checkbox/radio button element. """ area = abs( - float(element[constants.ANNOTATION_RECTANGLE_KEY][0]) - - float(element[constants.ANNOTATION_RECTANGLE_KEY][2]) + float(element[ANNOTATION_RECTANGLE_KEY][0]) + - float(element[ANNOTATION_RECTANGLE_KEY][2]) ) * abs( - float(element[constants.ANNOTATION_RECTANGLE_KEY][1]) - - float(element[constants.ANNOTATION_RECTANGLE_KEY][3]) + float(element[ANNOTATION_RECTANGLE_KEY][1]) + - float(element[ANNOTATION_RECTANGLE_KEY][3]) ) return sqrt(area) * 72 / 96 -def get_text_field_font_size(element: pdfrw.PdfDict) -> Union[float, int]: +def get_text_field_font_size(element: PdfDict) -> Union[float, int]: """Returns the font size of the text field if presented or zero.""" result = 0 @@ -119,14 +119,14 @@ def get_text_field_font_size(element: pdfrw.PdfDict) -> Union[float, int]: text_appearance = text_appearance.replace("(", "").replace(")", "") properties = text_appearance.split(" ") for i, val in enumerate(properties): - if val == constants.FONT_SIZE_IDENTIFIER: + if val == FONT_SIZE_IDENTIFIER: return float(properties[i - 1]) return result def get_text_field_font_color( - element: pdfrw.PdfDict, + element: PdfDict, ) -> Union[Tuple[float, float, float], None]: """Returns the font color tuple of the text field if presented or black.""" @@ -134,14 +134,14 @@ def get_text_field_font_color( for pattern in TEXT_FIELD_APPEARANCE_PATTERNS: text_appearance = traverse_pattern(pattern, element) if text_appearance: - if constants.FONT_COLOR_IDENTIFIER not in text_appearance: + if FONT_COLOR_IDENTIFIER not in text_appearance: return result text_appearance = ( text_appearance.replace("(", "").replace(")", "").split(" ") ) for i, val in enumerate(text_appearance): - if val == constants.FONT_COLOR_IDENTIFIER.replace(" ", ""): + if val == FONT_COLOR_IDENTIFIER.replace(" ", ""): result = ( float(text_appearance[i - 3]), float(text_appearance[i - 2]), @@ -158,7 +158,7 @@ def update_text_field_attributes( ) -> None: """Auto updates text fields' attributes.""" - template_pdf = pdfrw.PdfReader(fdata=template_stream) + template_pdf = PdfReader(fdata=template_stream) for _, _elements in get_elements_by_page(template_pdf).items(): for _element in _elements: diff --git a/PyPDFForm/core/image.py b/PyPDFForm/core/image.py index 0516b6ab..5358c3d5 100644 --- a/PyPDFForm/core/image.py +++ b/PyPDFForm/core/image.py @@ -4,7 +4,7 @@ from io import BytesIO from typing import Union -from PIL import Image as Img +from PIL.Image import open def rotate_image(image_stream: bytes, rotation: Union[float, int]) -> bytes: @@ -14,7 +14,7 @@ def rotate_image(image_stream: bytes, rotation: Union[float, int]) -> bytes: buff.write(image_stream) buff.seek(0) - image = Img.open(buff) + image = open(buff) rotated_buff = BytesIO() image.rotate(rotation, expand=True).save(rotated_buff, format=image.format) @@ -35,7 +35,7 @@ def any_image_to_jpg(image_stream: bytes) -> bytes: buff.write(image_stream) buff.seek(0) - image = Img.open(buff) + image = open(buff) if image.format == "JPEG": buff.close() diff --git a/PyPDFForm/core/patterns.py b/PyPDFForm/core/patterns.py index df911912..dd5c5301 100644 --- a/PyPDFForm/core/patterns.py +++ b/PyPDFForm/core/patterns.py @@ -5,26 +5,31 @@ from ..middleware.dropdown import Dropdown from ..middleware.radio import Radio from ..middleware.text import Text -from . import constants +from .constants import ELEMENT_TYPE_KEY, TEXT_FIELD_IDENTIFIER, \ + SELECTABLE_IDENTIFIER, CHOICE_FIELD_IDENTIFIER, \ + PARENT_KEY, SUBTYPE_KEY, WIDGET_SUBTYPE_KEY, \ + ANNOTATION_FIELD_KEY, CHOICES_IDENTIFIER, \ + TEXT_FIELD_ALIGNMENT_IDENTIFIER, FIELD_FLAG_KEY, \ + TEXT_FIELD_APPEARANCE_IDENTIFIER ELEMENT_TYPE_PATTERNS = [ ( - ({constants.ELEMENT_TYPE_KEY: constants.TEXT_FIELD_IDENTIFIER},), + ({ELEMENT_TYPE_KEY: TEXT_FIELD_IDENTIFIER},), Text, ), ( - ({constants.ELEMENT_TYPE_KEY: constants.SELECTABLE_IDENTIFIER},), + ({ELEMENT_TYPE_KEY: SELECTABLE_IDENTIFIER},), Checkbox, ), ( - ({constants.ELEMENT_TYPE_KEY: constants.CHOICE_FIELD_IDENTIFIER},), + ({ELEMENT_TYPE_KEY: CHOICE_FIELD_IDENTIFIER},), Dropdown, ), ( ( { - constants.PARENT_KEY: { - constants.ELEMENT_TYPE_KEY: constants.CHOICE_FIELD_IDENTIFIER + PARENT_KEY: { + ELEMENT_TYPE_KEY: CHOICE_FIELD_IDENTIFIER } }, ), @@ -33,8 +38,8 @@ ( ( { - constants.PARENT_KEY: { - constants.ELEMENT_TYPE_KEY: constants.TEXT_FIELD_IDENTIFIER + PARENT_KEY: { + ELEMENT_TYPE_KEY: TEXT_FIELD_IDENTIFIER } }, ), @@ -43,13 +48,13 @@ ( ( { - constants.PARENT_KEY: { - constants.ELEMENT_TYPE_KEY: constants.SELECTABLE_IDENTIFIER + PARENT_KEY: { + ELEMENT_TYPE_KEY: SELECTABLE_IDENTIFIER } }, { - constants.PARENT_KEY: { - constants.SUBTYPE_KEY: constants.WIDGET_SUBTYPE_KEY + PARENT_KEY: { + SUBTYPE_KEY: WIDGET_SUBTYPE_KEY } }, ), @@ -58,8 +63,8 @@ ( ( { - constants.PARENT_KEY: { - constants.ELEMENT_TYPE_KEY: constants.SELECTABLE_IDENTIFIER + PARENT_KEY: { + ELEMENT_TYPE_KEY: SELECTABLE_IDENTIFIER } }, ), @@ -68,26 +73,26 @@ ] ELEMENT_KEY_PATTERNS = [ - {constants.ANNOTATION_FIELD_KEY: True}, - {constants.PARENT_KEY: {constants.ANNOTATION_FIELD_KEY: True}}, + {ANNOTATION_FIELD_KEY: True}, + {PARENT_KEY: {ANNOTATION_FIELD_KEY: True}}, ] DROPDOWN_CHOICE_PATTERNS = [ - {constants.CHOICES_IDENTIFIER: True}, - {constants.PARENT_KEY: {constants.CHOICES_IDENTIFIER: True}}, + {CHOICES_IDENTIFIER: True}, + {PARENT_KEY: {CHOICES_IDENTIFIER: True}}, ] ELEMENT_ALIGNMENT_PATTERNS = [ - {constants.TEXT_FIELD_ALIGNMENT_IDENTIFIER: True}, - {constants.PARENT_KEY: {constants.TEXT_FIELD_ALIGNMENT_IDENTIFIER: True}}, + {TEXT_FIELD_ALIGNMENT_IDENTIFIER: True}, + {PARENT_KEY: {TEXT_FIELD_ALIGNMENT_IDENTIFIER: True}}, ] TEXT_FIELD_FLAG_PATTERNS = [ - {constants.FIELD_FLAG_KEY: True}, - {constants.PARENT_KEY: {constants.FIELD_FLAG_KEY: True}}, + {FIELD_FLAG_KEY: True}, + {PARENT_KEY: {FIELD_FLAG_KEY: True}}, ] TEXT_FIELD_APPEARANCE_PATTERNS = [ - {constants.TEXT_FIELD_APPEARANCE_IDENTIFIER: True}, - {constants.PARENT_KEY: {constants.TEXT_FIELD_APPEARANCE_IDENTIFIER: True}}, + {TEXT_FIELD_APPEARANCE_IDENTIFIER: True}, + {PARENT_KEY: {TEXT_FIELD_APPEARANCE_IDENTIFIER: True}}, ] diff --git a/PyPDFForm/core/template.py b/PyPDFForm/core/template.py index 3ca826da..6ad04dd9 100644 --- a/PyPDFForm/core/template.py +++ b/PyPDFForm/core/template.py @@ -3,12 +3,13 @@ from typing import Dict, List, Tuple, Union -import pdfrw +from pdfrw import PdfReader, PdfDict from reportlab.pdfbase.pdfmetrics import stringWidth from ..middleware.constants import ELEMENT_TYPES from ..middleware.text import Text -from . import constants +from .constants import ANNOTATION_KEY, TEXT_FIELD_MAX_LENGTH_KEY, \ + FIELD_FLAG_KEY, ANNOTATION_RECTANGLE_KEY from .patterns import (DROPDOWN_CHOICE_PATTERNS, ELEMENT_ALIGNMENT_PATTERNS, ELEMENT_KEY_PATTERNS, ELEMENT_TYPE_PATTERNS, TEXT_FIELD_FLAG_PATTERNS) @@ -16,17 +17,17 @@ def get_elements_by_page( - pdf: Union[bytes, pdfrw.PdfReader] -) -> Dict[int, List[pdfrw.PdfDict]]: + pdf: Union[bytes, PdfReader] +) -> Dict[int, List[PdfDict]]: """Iterates through a PDF and returns all elements found grouped by page.""" if isinstance(pdf, bytes): - pdf = pdfrw.PdfReader(fdata=pdf) + pdf = PdfReader(fdata=pdf) result = {} for i, page in enumerate(pdf.pages): - elements = page[constants.ANNOTATION_KEY] + elements = page[ANNOTATION_KEY] result[i + 1] = [] if elements: for element in elements: @@ -42,7 +43,7 @@ def get_elements_by_page( return result -def get_element_key(element: pdfrw.PdfDict) -> Union[str, None]: +def get_element_key(element: PdfDict) -> Union[str, None]: """Finds a PDF element's annotated key by pattern matching.""" result = None @@ -54,7 +55,7 @@ def get_element_key(element: pdfrw.PdfDict) -> Union[str, None]: return result -def get_element_alignment(element: pdfrw.PdfDict) -> Union[str, None]: +def get_element_alignment(element: PdfDict) -> Union[str, None]: """Finds a PDF element's alignment by pattern matching.""" result = None @@ -66,7 +67,7 @@ def get_element_alignment(element: pdfrw.PdfDict) -> Union[str, None]: return result -def construct_element(element: pdfrw.PdfDict, key: str) -> Union[ELEMENT_TYPES, None]: +def construct_element(element: PdfDict, key: str) -> Union[ELEMENT_TYPES, None]: """Finds a PDF element's annotated type by pattern matching.""" result = None @@ -81,26 +82,26 @@ def construct_element(element: pdfrw.PdfDict, key: str) -> Union[ELEMENT_TYPES, return result -def get_text_field_max_length(element: pdfrw.PdfDict) -> Union[int, None]: +def get_text_field_max_length(element: PdfDict) -> Union[int, None]: """Returns the max length of the text field if presented or None.""" return ( - int(element[constants.TEXT_FIELD_MAX_LENGTH_KEY]) - if constants.TEXT_FIELD_MAX_LENGTH_KEY in element + int(element[TEXT_FIELD_MAX_LENGTH_KEY]) + if TEXT_FIELD_MAX_LENGTH_KEY in element else None ) -def is_text_field_comb(element: pdfrw.PdfDict) -> bool: +def is_text_field_comb(element: PdfDict) -> bool: """Returns true if characters in a text field needs to be formatted into combs.""" try: - return "{0:b}".format(int(element[constants.FIELD_FLAG_KEY]))[::-1][24] == "1" + return "{0:b}".format(int(element[FIELD_FLAG_KEY]))[::-1][24] == "1" except (IndexError, TypeError): return False -def is_text_multiline(element: pdfrw.PdfDict) -> bool: +def is_text_multiline(element: PdfDict) -> bool: """Returns true if a text field is a paragraph field.""" field_flag = None @@ -118,7 +119,7 @@ def is_text_multiline(element: pdfrw.PdfDict) -> bool: return False -def get_dropdown_choices(element: pdfrw.PdfDict) -> Union[Tuple[str], None]: +def get_dropdown_choices(element: PdfDict) -> Union[Tuple[str], None]: """Returns string options of a dropdown field.""" result = None @@ -136,18 +137,18 @@ def get_dropdown_choices(element: pdfrw.PdfDict) -> Union[Tuple[str], None]: return result -def get_char_rect_width(element: pdfrw.PdfDict, element_middleware: Text) -> float: +def get_char_rect_width(element: PdfDict, element_middleware: Text) -> float: """Returns rectangular width of each character for combed text fields.""" rect_width = abs( - float(element[constants.ANNOTATION_RECTANGLE_KEY][0]) - - float(element[constants.ANNOTATION_RECTANGLE_KEY][2]) + float(element[ANNOTATION_RECTANGLE_KEY][0]) + - float(element[ANNOTATION_RECTANGLE_KEY][2]) ) return rect_width / element_middleware.max_length def get_character_x_paddings( - element: pdfrw.PdfDict, element_middleware: Text + element: PdfDict, element_middleware: Text ) -> List[float]: """Returns paddings between characters for combed text fields.""" @@ -207,14 +208,14 @@ def get_paragraph_lines(element_middleware: Text) -> List[str]: def get_paragraph_auto_wrap_length( - element: pdfrw.PdfDict, element_middleware: Text + element: PdfDict, element_middleware: Text ) -> int: """Calculates the text wrap length of a paragraph field.""" value = element_middleware.value or "" width = abs( - float(element[constants.ANNOTATION_RECTANGLE_KEY][0]) - - float(element[constants.ANNOTATION_RECTANGLE_KEY][2]) + float(element[ANNOTATION_RECTANGLE_KEY][0]) + - float(element[ANNOTATION_RECTANGLE_KEY][2]) ) text_width = stringWidth( value, diff --git a/PyPDFForm/core/utils.py b/PyPDFForm/core/utils.py index 0b62a0ba..6a994607 100644 --- a/PyPDFForm/core/utils.py +++ b/PyPDFForm/core/utils.py @@ -4,21 +4,23 @@ from io import BytesIO from typing import Union -import pdfrw +from pdfrw import PdfReader, PdfWriter, PdfDict from ..middleware.checkbox import Checkbox from ..middleware.constants import ELEMENT_TYPES from ..middleware.radio import Radio from ..middleware.text import Text -from . import constants +from .constants import DEFAULT_FONT, DEFAULT_FONT_COLOR, \ + CHECKBOX_TO_DRAW, RADIO_TO_DRAW, \ + DEFAULT_FONT_SIZE, PREVIEW_FONT_COLOR, ANNOTATION_KEY -def generate_stream(pdf: pdfrw.PdfReader) -> bytes: +def generate_stream(pdf: PdfReader) -> bytes: """Generates new stream for manipulated PDF form.""" result_stream = BytesIO() - pdfrw.PdfWriter().write(result_stream, pdf) + PdfWriter().write(result_stream, pdf) result_stream.seek(0) result = result_stream.read() @@ -36,14 +38,14 @@ def checkbox_radio_to_draw( element_name=element.name, element_value="", ) - new_element.font = constants.DEFAULT_FONT + new_element.font = DEFAULT_FONT new_element.font_size = font_size - new_element.font_color = constants.DEFAULT_FONT_COLOR + new_element.font_color = DEFAULT_FONT_COLOR if isinstance(element, Checkbox): - new_element.value = constants.CHECKBOX_TO_DRAW + new_element.value = CHECKBOX_TO_DRAW elif isinstance(element, Radio): - new_element.value = constants.RADIO_TO_DRAW + new_element.value = RADIO_TO_DRAW return new_element @@ -55,9 +57,9 @@ def preview_element_to_draw(element: ELEMENT_TYPES) -> 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.font = DEFAULT_FONT + new_element.font_size = DEFAULT_FONT_SIZE + new_element.font_color = PREVIEW_FONT_COLOR new_element.preview = True return new_element @@ -66,10 +68,10 @@ def preview_element_to_draw(element: ELEMENT_TYPES) -> Text: def remove_all_elements(pdf: bytes) -> bytes: """Removes all elements from a pdfrw parsed PDF form.""" - pdf = pdfrw.PdfReader(fdata=pdf) + pdf = PdfReader(fdata=pdf) for page in pdf.pages: - elements = page[constants.ANNOTATION_KEY] + elements = page[ANNOTATION_KEY] if elements: for j in reversed(range(len(elements))): elements.pop(j) @@ -80,10 +82,10 @@ def remove_all_elements(pdf: bytes) -> bytes: def merge_two_pdfs(pdf: bytes, other: bytes) -> bytes: """Merges two PDFs into one PDF.""" - writer = pdfrw.PdfWriter() + writer = PdfWriter() - writer.addpages(pdfrw.PdfReader(fdata=pdf).pages) - writer.addpages(pdfrw.PdfReader(fdata=other).pages) + writer.addpages(PdfReader(fdata=pdf).pages) + writer.addpages(PdfReader(fdata=other).pages) result_stream = BytesIO() writer.write(result_stream) @@ -95,13 +97,13 @@ def merge_two_pdfs(pdf: bytes, other: bytes) -> bytes: return result -def find_pattern_match(pattern: dict, element: pdfrw.PdfDict) -> bool: +def find_pattern_match(pattern: dict, element: PdfDict) -> bool: """Checks if a PDF dict pattern exists in a PDF element.""" for key, value in element.items(): result = False if key in pattern: - if isinstance(pattern[key], dict) and isinstance(value, pdfrw.PdfDict): + if isinstance(pattern[key], dict) and isinstance(value, PdfDict): result = find_pattern_match(pattern[key], value) else: result = pattern[key] == value @@ -110,13 +112,13 @@ def find_pattern_match(pattern: dict, element: pdfrw.PdfDict) -> bool: return False -def traverse_pattern(pattern: dict, element: pdfrw.PdfDict) -> Union[str, list, None]: +def traverse_pattern(pattern: dict, element: PdfDict) -> Union[str, list, None]: """Traverses down a PDF dict pattern and find the value.""" for key, value in element.items(): result = None if key in pattern: - if isinstance(pattern[key], dict) and isinstance(value, pdfrw.PdfDict): + if isinstance(pattern[key], dict) and isinstance(value, PdfDict): result = traverse_pattern(pattern[key], value) else: if pattern[key] is True and value: diff --git a/PyPDFForm/core/watermark.py b/PyPDFForm/core/watermark.py index 111672b6..f995ad3f 100644 --- a/PyPDFForm/core/watermark.py +++ b/PyPDFForm/core/watermark.py @@ -4,17 +4,17 @@ from io import BytesIO from typing import List, Union -import pdfrw +from pdfrw import PdfReader, PageMerge from reportlab.lib.utils import ImageReader -from reportlab.pdfgen import canvas +from reportlab.pdfgen.canvas import Canvas +from .utils import generate_stream from ..middleware.text import Text -from . import utils def draw_text( *args: Union[ - canvas.Canvas, + Canvas, Text, float, int, @@ -83,7 +83,7 @@ def draw_text( canv.restoreState() -def draw_image(*args: Union[canvas.Canvas, bytes, float, int]) -> None: +def draw_image(*args: Union[Canvas, bytes, float, int]) -> None: """Draws an image on the watermark.""" canv = args[0] @@ -126,10 +126,10 @@ def create_watermarks_and_draw( ) -> List[bytes]: """Creates a canvas watermark and draw some stuffs on it.""" - pdf_file = pdfrw.PdfReader(fdata=pdf) + pdf_file = PdfReader(fdata=pdf) buff = BytesIO() - canv = canvas.Canvas( + canv = Canvas( buff, pagesize=( float(pdf_file.pages[page_number - 1].MediaBox[2]), @@ -164,13 +164,13 @@ def merge_watermarks_with_pdf( ) -> bytes: """Merges watermarks with PDF.""" - pdf_file = pdfrw.PdfReader(fdata=pdf) + pdf_file = PdfReader(fdata=pdf) for i, page in enumerate(pdf_file.pages): if watermarks[i]: - watermark = pdfrw.PdfReader(fdata=watermarks[i]) + watermark = PdfReader(fdata=watermarks[i]) if watermark.pages: - merger = pdfrw.PageMerge(page) + merger = PageMerge(page) merger.add(watermark.pages[0]).render() - return utils.generate_stream(pdf_file) + return generate_stream(pdf_file) diff --git a/PyPDFForm/middleware/template.py b/PyPDFForm/middleware/template.py index 4bbbc577..a7200c61 100644 --- a/PyPDFForm/middleware/template.py +++ b/PyPDFForm/middleware/template.py @@ -3,52 +3,55 @@ from typing import Dict -from ..core import template -from . import constants +from .constants import ELEMENT_TYPES from .dropdown import Dropdown from .radio import Radio from .text import Text +from ..core.template import get_element_key, \ + get_elements_by_page, get_character_x_paddings, \ + construct_element, get_text_field_max_length, \ + is_text_field_comb, get_dropdown_choices def set_character_x_paddings( - pdf_stream: bytes, eles: Dict[str, constants.ELEMENT_TYPES] -) -> Dict[str, constants.ELEMENT_TYPES]: + pdf_stream: bytes, eles: Dict[str, ELEMENT_TYPES] +) -> Dict[str, ELEMENT_TYPES]: """Sets paddings between characters for combed text fields.""" - for elements in template.get_elements_by_page(pdf_stream).values(): + for elements in get_elements_by_page(pdf_stream).values(): for element in elements: - key = template.get_element_key(element) + key = get_element_key(element) _element = eles[key] if isinstance(_element, Text) and _element.comb is True: - _element.character_paddings = template.get_character_x_paddings( + _element.character_paddings = get_character_x_paddings( element, _element ) return eles -def build_elements(pdf_stream: bytes) -> Dict[str, constants.ELEMENT_TYPES]: +def build_elements(pdf_stream: bytes) -> Dict[str, ELEMENT_TYPES]: """Builds an element dict given a PDF form stream.""" results = {} - for elements in template.get_elements_by_page(pdf_stream).values(): + for elements in get_elements_by_page(pdf_stream).values(): for element in elements: - key = template.get_element_key(element) + key = get_element_key(element) - _element = template.construct_element(element, key) + _element = construct_element(element, key) if _element is not None: if isinstance(_element, Text): - _element.max_length = template.get_text_field_max_length(element) - if _element.max_length is not None and template.is_text_field_comb( + _element.max_length = get_text_field_max_length(element) + if _element.max_length is not None and is_text_field_comb( element ): _element.comb = True if isinstance(_element, Dropdown): - _element.choices = template.get_dropdown_choices(element) + _element.choices = get_dropdown_choices(element) if isinstance(_element, Radio): if key not in results: diff --git a/PyPDFForm/wrapper.py b/PyPDFForm/wrapper.py index 74d6c714..081593b9 100644 --- a/PyPDFForm/wrapper.py +++ b/PyPDFForm/wrapper.py @@ -5,15 +5,22 @@ from typing import BinaryIO, Dict, Union -from .core import constants as core_constants -from .core import filler, font -from .core import image as image_core -from .core import utils -from .core import watermark as watermark_core +from .core.constants import DEFAULT_FONT, \ + DEFAULT_FONT_SIZE, DEFAULT_FONT_COLOR +from .core.filler import fill +from .core.font import register_font from .core.font import update_text_field_attributes -from .middleware import adapter, constants -from .middleware import template as template_middleware +from .core.image import any_image_to_jpg, rotate_image +from .core.utils import merge_two_pdfs, \ + preview_element_to_draw, remove_all_elements +from .core.watermark import create_watermarks_and_draw, \ + merge_watermarks_with_pdf +from .middleware.adapter import fp_or_f_obj_or_stream_to_stream +from .middleware.constants import VERSION_IDENTIFIERS, \ + VERSION_IDENTIFIER_PREFIX from .middleware.dropdown import Dropdown +from .middleware.template import build_elements, \ + dropdown_to_text, set_character_x_paddings from .middleware.text import Text @@ -27,9 +34,9 @@ def __init__( ) -> None: """Constructs all attributes for the object.""" - self.stream = adapter.fp_or_f_obj_or_stream_to_stream(template) + self.stream = fp_or_f_obj_or_stream_to_stream(template) self.elements = ( - template_middleware.build_elements(self.stream) if self.stream else {} + build_elements(self.stream) if self.stream else {} ) for each in self.elements.values(): @@ -53,9 +60,9 @@ def sample_data(self) -> dict: def version(self) -> Union[str, None]: """Gets the version of the PDF.""" - for each in constants.VERSION_IDENTIFIERS: + for each in VERSION_IDENTIFIERS: if self.stream.startswith(each): - return each.replace(constants.VERSION_IDENTIFIER_PREFIX, b"").decode() + return each.replace(VERSION_IDENTIFIER_PREFIX, b"").decode() return None @@ -63,8 +70,8 @@ def change_version(self, version: str) -> Wrapper: """Changes the version of the PDF.""" self.stream = self.stream.replace( - constants.VERSION_IDENTIFIER_PREFIX + bytes(self.version, "utf-8"), - constants.VERSION_IDENTIFIER_PREFIX + bytes(version, "utf-8"), + VERSION_IDENTIFIER_PREFIX + bytes(self.version, "utf-8"), + VERSION_IDENTIFIER_PREFIX + bytes(version, "utf-8"), 1, ) @@ -80,7 +87,7 @@ def __add__(self, other: Wrapper) -> Wrapper: return self new_obj = self.__class__() - new_obj.stream = utils.merge_two_pdfs(self.stream, other.stream) + new_obj.stream = merge_two_pdfs(self.stream, other.stream) return new_obj @@ -88,10 +95,10 @@ def __add__(self, other: Wrapper) -> Wrapper: def preview(self) -> bytes: """Inspects all supported elements' names for the PDF form.""" - return filler.fill( + return fill( self.stream, { - key: utils.preview_element_to_draw(value) + key: preview_element_to_draw(value) for key, value in self.elements.items() }, ) @@ -108,15 +115,15 @@ def fill( for key, value in self.elements.items(): if isinstance(value, Dropdown): - self.elements[key] = template_middleware.dropdown_to_text(value) + self.elements[key] = dropdown_to_text(value) update_text_field_attributes(self.stream, self.elements) if self.read(): - self.elements = template_middleware.set_character_x_paddings( + self.elements = set_character_x_paddings( self.stream, self.elements ) - self.stream = utils.remove_all_elements(filler.fill(self.stream, self.elements)) + self.stream = remove_all_elements(fill(self.stream, self.elements)) return self @@ -132,15 +139,15 @@ def draw_text( new_element = Text("new") new_element.value = text - new_element.font = kwargs.get("font", core_constants.DEFAULT_FONT) + new_element.font = kwargs.get("font", DEFAULT_FONT) new_element.font_size = kwargs.get( - "font_size", core_constants.DEFAULT_FONT_SIZE + "font_size", DEFAULT_FONT_SIZE ) new_element.font_color = kwargs.get( - "font_color", core_constants.DEFAULT_FONT_COLOR + "font_color", DEFAULT_FONT_COLOR ) - watermarks = watermark_core.create_watermarks_and_draw( + watermarks = create_watermarks_and_draw( self.stream, page_number, "text", @@ -153,7 +160,7 @@ def draw_text( ], ) - self.stream = watermark_core.merge_watermarks_with_pdf(self.stream, watermarks) + self.stream = merge_watermarks_with_pdf(self.stream, watermarks) return self @@ -169,14 +176,14 @@ def draw_image( ) -> Wrapper: """Draws an image on a PDF form.""" - image = adapter.fp_or_f_obj_or_stream_to_stream(image) - image = image_core.any_image_to_jpg(image) - image = image_core.rotate_image(image, rotation) - watermarks = watermark_core.create_watermarks_and_draw( + image = fp_or_f_obj_or_stream_to_stream(image) + image = any_image_to_jpg(image) + image = rotate_image(image, rotation) + watermarks = create_watermarks_and_draw( self.stream, page_number, "image", [[image, x, y, width, height]] ) - self.stream = watermark_core.merge_watermarks_with_pdf(self.stream, watermarks) + self.stream = merge_watermarks_with_pdf(self.stream, watermarks) return self @@ -198,8 +205,8 @@ def register_font( ) -> bool: """Registers a font from a ttf file.""" - ttf_file = adapter.fp_or_f_obj_or_stream_to_stream(ttf_file) + ttf_file = fp_or_f_obj_or_stream_to_stream(ttf_file) return ( - font.register_font(font_name, ttf_file) if ttf_file is not None else False + register_font(font_name, ttf_file) if ttf_file is not None else False ) From 583067771f47f8bca033f83744fef8ebef67f324 Mon Sep 17 00:00:00 2001 From: chinapandaman Date: Sun, 26 Nov 2023 01:55:07 -0600 Subject: [PATCH 2/3] PPF-412: missed some places --- PyPDFForm/middleware/adapter.py | 4 ++-- PyPDFForm/wrapper.py | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/PyPDFForm/middleware/adapter.py b/PyPDFForm/middleware/adapter.py index 35236947..ae55ec11 100644 --- a/PyPDFForm/middleware/adapter.py +++ b/PyPDFForm/middleware/adapter.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """Contains user input adapters.""" -import os +from os.path import isfile from typing import Any, BinaryIO, Union @@ -24,7 +24,7 @@ def fp_or_f_obj_or_stream_to_stream( result = fp_or_f_obj_or_stream.read() elif isinstance(fp_or_f_obj_or_stream, str): - if not os.path.isfile(fp_or_f_obj_or_stream): + if not isfile(fp_or_f_obj_or_stream): result = None else: diff --git a/PyPDFForm/wrapper.py b/PyPDFForm/wrapper.py index 081593b9..26b65591 100644 --- a/PyPDFForm/wrapper.py +++ b/PyPDFForm/wrapper.py @@ -8,8 +8,7 @@ from .core.constants import DEFAULT_FONT, \ DEFAULT_FONT_SIZE, DEFAULT_FONT_COLOR from .core.filler import fill -from .core.font import register_font -from .core.font import update_text_field_attributes +from .core.font import register_font, update_text_field_attributes from .core.image import any_image_to_jpg, rotate_image from .core.utils import merge_two_pdfs, \ preview_element_to_draw, remove_all_elements From f5e28a672230c31e0d17a4b47985d45349e92850 Mon Sep 17 00:00:00 2001 From: chinapandaman Date: Sun, 26 Nov 2023 01:57:54 -0600 Subject: [PATCH 3/3] PPF-412: nvm about this one --- PyPDFForm/core/image.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PyPDFForm/core/image.py b/PyPDFForm/core/image.py index 5358c3d5..cb582e20 100644 --- a/PyPDFForm/core/image.py +++ b/PyPDFForm/core/image.py @@ -4,7 +4,7 @@ from io import BytesIO from typing import Union -from PIL.Image import open +from PIL import Image def rotate_image(image_stream: bytes, rotation: Union[float, int]) -> bytes: @@ -14,7 +14,7 @@ def rotate_image(image_stream: bytes, rotation: Union[float, int]) -> bytes: buff.write(image_stream) buff.seek(0) - image = open(buff) + image = Image.open(buff) rotated_buff = BytesIO() image.rotate(rotation, expand=True).save(rotated_buff, format=image.format) @@ -35,7 +35,7 @@ def any_image_to_jpg(image_stream: bytes) -> bytes: buff.write(image_stream) buff.seek(0) - image = open(buff) + image = Image.open(buff) if image.format == "JPEG": buff.close()