Skip to content

Commit

Permalink
PPF-405: removed circular imports
Browse files Browse the repository at this point in the history
  • Loading branch information
chinapandaman committed Nov 19, 2023
1 parent eec771d commit fde8958
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 109 deletions.
40 changes: 38 additions & 2 deletions PyPDFForm/core/font.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import re
from io import BytesIO
from math import sqrt
from typing import Union
from typing import Dict, Union

import pdfrw
from reportlab.pdfbase import pdfmetrics
Expand All @@ -13,7 +13,10 @@
from . import constants
from .constants import DEFAULT_FONT_SIZE
from .patterns import TEXT_FIELD_APPEARANCE_PATTERNS
from .template import is_text_multiline, traverse_pattern
from .template import is_text_multiline, traverse_pattern, get_elements_by_page, get_element_key, \
get_text_field_font_size, get_text_field_font_color, get_paragraph_auto_wrap_length, get_paragraph_lines
from ..middleware.constants import ELEMENT_TYPES
from ..middleware.text import Text


def register_font(font_name: str, ttf_stream: bytes) -> bool:
Expand Down Expand Up @@ -102,3 +105,36 @@ def checkbox_radio_font_size(element: pdfrw.PdfDict) -> Union[float, int]:
)

return sqrt(area) * 72 / 96


def update_text_field_attributes(
template_stream: bytes,
elements: Dict[str, ELEMENT_TYPES],
) -> None:
"""Auto updates text fields' attributes."""

template_pdf = pdfrw.PdfReader(fdata=template_stream)

for _, _elements in get_elements_by_page(template_pdf).items():
for _element in _elements:
key = get_element_key(_element)

if isinstance(elements[key], Text):
if elements[key].font is None:
elements[key].font = auto_detect_font(_element)
if elements[key].font_size is None:
elements[key].font_size = get_text_field_font_size(
_element
) or text_field_font_size(_element)
if elements[key].font_color is None:
elements[key].font_color = get_text_field_font_color(
_element
)
if (
is_text_multiline(_element)
and elements[key].text_wrap_length is None
):
elements[key].text_wrap_length = get_paragraph_auto_wrap_length(
_element, elements[key]
)
elements[key].text_lines = get_paragraph_lines(elements[key])
69 changes: 69 additions & 0 deletions PyPDFForm/core/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,3 +384,72 @@ def get_text_line_x_coordinates(
return result

return None


def get_paragraph_lines(element_middleware: Text) -> List[str]:
"""Splits the paragraph field's text to a list of lines."""

lines = []
result = []
text_wrap_length = element_middleware.text_wrap_length
value = element_middleware.value or ""
if element_middleware.max_length is not None:
value = value[: element_middleware.max_length]
characters = value.split(" ")
current_line = ""
for each in characters:
line_extended = f"{current_line} {each}" if current_line else each
if len(line_extended) <= text_wrap_length:
current_line = line_extended
else:
lines.append(current_line)
current_line = each
lines.append(current_line)

for each in lines:
while len(each) > text_wrap_length:
last_index = text_wrap_length - 1
result.append(each[:last_index])
each = each[last_index:]
if each:
if result and len(each) + 1 + len(result[-1]) <= text_wrap_length:
result[-1] = f"{result[-1]}{each} "
else:
result.append(f"{each} ")

if result:
result[-1] = result[-1][:-1]

return result


def get_paragraph_auto_wrap_length(
element: pdfrw.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])
)
text_width = stringWidth(
value,
element_middleware.font,
element_middleware.font_size,
)

lines = text_width / width
if lines > 1:
counter = 0
_width = 0
while _width <= width:
counter += 1
_width = stringWidth(
value[:counter],
element_middleware.font,
element_middleware.font_size,
)
return counter - 1

return len(value) + 1
108 changes: 2 additions & 106 deletions PyPDFForm/core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,15 @@
"""Contains utility helpers."""

from io import BytesIO
from typing import Dict, List, Union
from typing import Union

import pdfrw
from reportlab.pdfbase.pdfmetrics import stringWidth

from ..middleware.checkbox import Checkbox
from ..middleware.constants import ELEMENT_TYPES
from ..middleware.radio import Radio
from ..middleware.text import Text
from . import constants, template
from .font import auto_detect_font, text_field_font_size
from . import constants


def generate_stream(pdf: pdfrw.PdfReader) -> bytes:
Expand All @@ -29,108 +27,6 @@ def generate_stream(pdf: pdfrw.PdfReader) -> bytes:
return result


def update_text_field_attributes(
template_stream: bytes,
elements: Dict[str, ELEMENT_TYPES],
) -> None:
"""Auto updates text fields' attributes."""

template_pdf = pdfrw.PdfReader(fdata=template_stream)

for _, _elements in template.get_elements_by_page(template_pdf).items():
for _element in _elements:
key = template.get_element_key(_element)

if isinstance(elements[key], Text):
if elements[key].font is None:
elements[key].font = auto_detect_font(_element)
if elements[key].font_size is None:
elements[key].font_size = template.get_text_field_font_size(
_element
) or text_field_font_size(_element)
if elements[key].font_color is None:
elements[key].font_color = template.get_text_field_font_color(
_element
)
if (
template.is_text_multiline(_element)
and elements[key].text_wrap_length is None
):
elements[key].text_wrap_length = get_paragraph_auto_wrap_length(
_element, elements[key]
)
elements[key].text_lines = get_paragraph_lines(elements[key])


def get_paragraph_lines(element_middleware: Text) -> List[str]:
"""Splits the paragraph field's text to a list of lines."""

lines = []
result = []
text_wrap_length = element_middleware.text_wrap_length
value = element_middleware.value or ""
if element_middleware.max_length is not None:
value = value[: element_middleware.max_length]
characters = value.split(" ")
current_line = ""
for each in characters:
line_extended = f"{current_line} {each}" if current_line else each
if len(line_extended) <= text_wrap_length:
current_line = line_extended
else:
lines.append(current_line)
current_line = each
lines.append(current_line)

for each in lines:
while len(each) > text_wrap_length:
last_index = text_wrap_length - 1
result.append(each[:last_index])
each = each[last_index:]
if each:
if result and len(each) + 1 + len(result[-1]) <= text_wrap_length:
result[-1] = f"{result[-1]}{each} "
else:
result.append(f"{each} ")

if result:
result[-1] = result[-1][:-1]

return result


def get_paragraph_auto_wrap_length(
element: pdfrw.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])
)
text_width = stringWidth(
value,
element_middleware.font,
element_middleware.font_size,
)

lines = text_width / width
if lines > 1:
counter = 0
_width = 0
while _width <= width:
counter += 1
_width = stringWidth(
value[:counter],
element_middleware.font,
element_middleware.font_size,
)
return counter - 1

return len(value) + 1


def checkbox_radio_to_draw(
element: Union[Checkbox, Radio], font_size: Union[float, int]
) -> Text:
Expand Down
3 changes: 2 additions & 1 deletion PyPDFForm/wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from .core import image as image_core
from .core import utils
from .core import watermark as watermark_core
from .core.font import update_text_field_attributes
from .middleware import adapter, constants
from .middleware import template as template_middleware
from .middleware.dropdown import Dropdown
Expand Down Expand Up @@ -109,7 +110,7 @@ def fill(
if isinstance(value, Dropdown):
self.elements[key] = template_middleware.dropdown_to_text(value)

utils.update_text_field_attributes(self.stream, self.elements)
update_text_field_attributes(self.stream, self.elements)
if self.read():
self.elements = template_middleware.set_character_x_paddings(
self.stream, self.elements
Expand Down

0 comments on commit fde8958

Please sign in to comment.