Skip to content

Commit

Permalink
Merge pull request #413 from chinapandaman/PPF-412
Browse files Browse the repository at this point in the history
PPF-412: specify imported methods more explicitly
  • Loading branch information
chinapandaman authored Nov 26, 2023
2 parents e672d64 + f5e28a6 commit bca068a
Show file tree
Hide file tree
Showing 11 changed files with 186 additions and 168 deletions.
8 changes: 4 additions & 4 deletions PyPDFForm/core/coordinate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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."""
Expand Down Expand Up @@ -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."""

Expand Down Expand Up @@ -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
Expand Down
21 changes: 11 additions & 10 deletions PyPDFForm/core/filler.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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
)
50 changes: 25 additions & 25 deletions PyPDFForm/core/font.py
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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
Expand All @@ -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:
Expand All @@ -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

Expand All @@ -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.
Expand All @@ -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
Expand All @@ -119,29 +119,29 @@ 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."""

result = (0, 0, 0)
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]),
Expand All @@ -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:
Expand Down
6 changes: 3 additions & 3 deletions PyPDFForm/core/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from io import BytesIO
from typing import Union

from PIL import Image as Img
from PIL import Image


def rotate_image(image_stream: bytes, rotation: Union[float, int]) -> bytes:
Expand All @@ -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 = Image.open(buff)

rotated_buff = BytesIO()
image.rotate(rotation, expand=True).save(rotated_buff, format=image.format)
Expand All @@ -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 = Image.open(buff)

if image.format == "JPEG":
buff.close()
Expand Down
53 changes: 29 additions & 24 deletions PyPDFForm/core/patterns.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
},
),
Expand All @@ -33,8 +38,8 @@
(
(
{
constants.PARENT_KEY: {
constants.ELEMENT_TYPE_KEY: constants.TEXT_FIELD_IDENTIFIER
PARENT_KEY: {
ELEMENT_TYPE_KEY: TEXT_FIELD_IDENTIFIER
}
},
),
Expand All @@ -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
}
},
),
Expand All @@ -58,8 +63,8 @@
(
(
{
constants.PARENT_KEY: {
constants.ELEMENT_TYPE_KEY: constants.SELECTABLE_IDENTIFIER
PARENT_KEY: {
ELEMENT_TYPE_KEY: SELECTABLE_IDENTIFIER
}
},
),
Expand All @@ -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}},
]
Loading

0 comments on commit bca068a

Please sign in to comment.