Skip to content

Commit

Permalink
Merge pull request #518 from chinapandaman/PPF-517
Browse files Browse the repository at this point in the history
PPF-517: support change checkbox/radio button size and button style
  • Loading branch information
chinapandaman authored Mar 5, 2024
2 parents 3a474ec + ba5f4fd commit 8ac35a8
Show file tree
Hide file tree
Showing 12 changed files with 153 additions and 11 deletions.
2 changes: 1 addition & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[MESSAGES CONTROL]
disable=C0103, R0913, R0902, R0903, R0914, C0209
disable=C0103, R0913, R0902, R0903, R0914, C0209, C0123
6 changes: 3 additions & 3 deletions PyPDFForm/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@
DEFAULT_CHECKBOX_STYLE = "\u2713"
DEFAULT_RADIO_STYLE = "\u25CF"
BUTTON_STYLES = {
"4": "\u2713",
"5": "\u00D7",
"l": "\u25CF",
"4": "\u2713", # check
"5": "\u00D7", # cross
"l": "\u25CF", # circle
}

COORDINATE_GRID_FONT_SIZE_MARGIN_RATIO = DEFAULT_FONT_SIZE / 100
8 changes: 6 additions & 2 deletions PyPDFForm/filler.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,14 @@ def fill(
_to_draw = x = y = None

if isinstance(widgets[key], (Checkbox, Radio)):
font_size = checkbox_radio_font_size(_widget)
font_size = (
checkbox_radio_font_size(_widget)
if widgets[key].size is None
else widgets[key].size
)
_to_draw = checkbox_radio_to_draw(widgets[key], font_size)
x, y = get_draw_checkbox_radio_coordinates(_widget, _to_draw)
if isinstance(widgets[key], Checkbox) and widgets[key].value:
if type(widgets[key]) is Checkbox and widgets[key].value:
text_needs_to_be_drawn = True
elif isinstance(widgets[key], Radio):
if key not in radio_button_tracker:
Expand Down
26 changes: 25 additions & 1 deletion PyPDFForm/middleware/checkbox.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
# -*- coding: utf-8 -*-
"""Contains checkbox middleware."""

from typing import Union

from .base import Widget


class Checkbox(Widget):
"""A class to represent a checkbox widget."""

BUTTON_STYLE_MAPPING = {
"check": "4",
"cross": "5",
"circle": "l",
}

def __init__(
self,
name: str,
Expand All @@ -16,7 +24,8 @@ def __init__(

super().__init__(name, value)

self.button_style = None
self.size = None
self._button_style = None

@property
def schema_definition(self) -> dict:
Expand All @@ -29,3 +38,18 @@ def sample_value(self) -> bool:
"""Sample value of the checkbox."""

return True

@property
def button_style(self) -> Union[str, None]:
"""Shape of the tick for the checkbox."""

return self._button_style

@button_style.setter
def button_style(self, value) -> None:
"""Converts user specified button styles to acroform values."""

if value in self.BUTTON_STYLE_MAPPING:
self._button_style = self.BUTTON_STYLE_MAPPING[value]
elif value in self.BUTTON_STYLE_MAPPING.values():
self._button_style = value
5 changes: 2 additions & 3 deletions PyPDFForm/middleware/radio.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*-
"""Contains radio middleware."""

from .base import Widget
from .checkbox import Checkbox


class Radio(Widget):
class Radio(Checkbox):
"""A class to represent a radiobutton widget."""

def __init__(
Expand All @@ -16,7 +16,6 @@ def __init__(

super().__init__(name, value)

self.button_style = None
self.number_of_options = 0

@property
Expand Down
2 changes: 1 addition & 1 deletion PyPDFForm/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def checkbox_radio_to_draw(
new_widget.font_size = font_size
new_widget.font_color = DEFAULT_FONT_COLOR
new_widget.value = BUTTON_STYLES.get(widget.button_style) or (
DEFAULT_CHECKBOX_STYLE if isinstance(widget, Checkbox) else DEFAULT_RADIO_STYLE
DEFAULT_CHECKBOX_STYLE if type(widget) is Checkbox else DEFAULT_RADIO_STYLE
)

return new_widget
Expand Down
55 changes: 55 additions & 0 deletions docs/button_style.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Change checkbox and radio button styles

Similar to text fields discussed in the last chapter, PyPDFForm gives you the ability to
modify some styles of checkboxes and radio buttons without changing the template.

## Change size

You can change the size of the selection by specifying a `float` value. Consider
[this PDF](https://github.com/chinapandaman/PyPDFForm/raw/master/pdf_samples/sample_template.pdf):

```python
from PyPDFForm import PdfWrapper

form = PdfWrapper("sample_template.pdf")
form.widgets["check"].size = 50
form.widgets["check_2"].size = 40
form.widgets["check_3"].size = 60

form.fill(
{
"check": True,
"check_2": True,
"check_3": True,
},
)

with open("output.pdf", "wb+") as output:
output.write(form.read())
```

## Change button style

The button style is the shape of the selection on a checkbox or radio button. PyPDFForm lets you pick
three different button styles: `check`, `circle`, and `cross`. Consider
[this PDF](https://github.com/chinapandaman/PyPDFForm/raw/master/pdf_samples/sample_template_with_radio_button.pdf):

```python
from PyPDFForm import PdfWrapper

form = PdfWrapper("sample_template_with_radio_button.pdf")
form.widgets["radio_1"].button_style = "cross"
form.widgets["radio_2"].button_style = "circle"
form.widgets["radio_3"].button_style = "check"

form.fill(
{
"radio_1": 0,
"radio_2": 1,
"radio_3": 2,
},
)

with open("output.pdf", "wb+") as output:
output.write(form.read())
```
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ It also supports other common utilities such as extracting pages and merging mul
* [Inspect a PDF form](inspect.md)
* [Fill a PDF form](fill.md)
* [Change text field styles](style.md)
* [Change checkbox and radio button styles](button_style.md)
* [Draw stuffs](draw.md)
* [Other utilities](utils.md)
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ nav:
- inspect.md
- fill.md
- style.md
- button_style.md
- draw.md
- utils.md
theme:
Expand Down
Binary file not shown.
Binary file not shown.
58 changes: 58 additions & 0 deletions tests/test_functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -550,3 +550,61 @@ def test_generate_coordinate_grid_margin_50(template_stream, pdf_samples, reques

assert len(obj.read()) == len(expected)
assert obj.stream == expected


def test_checkbox_change_size_and_button_style(template_stream, pdf_samples, request):
expected_path = os.path.join(
pdf_samples, "test_checkbox_change_size_and_button_style.pdf"
)
with open(expected_path, "rb+") as f:
obj = PdfWrapper(template_stream)
obj.widgets["check"].size = 50
obj.widgets["check"].button_style = "cross"
obj.widgets["check_2"].size = 40
obj.widgets["check_2"].button_style = "circle"
obj.widgets["check_3"].size = 60
obj.widgets["check_3"].button_style = "check"
obj = obj.fill(
{
"check": True,
"check_2": True,
"check_3": True,
},
)

request.config.results["expected_path"] = expected_path
request.config.results["stream"] = obj.read()

expected = f.read()

assert len(obj.read()) == len(expected)
assert obj.stream == expected


def test_radio_change_size_and_button_style(template_with_radiobutton_stream, pdf_samples, request):
expected_path = os.path.join(
pdf_samples, "test_radio_change_size_and_button_style.pdf"
)
with open(expected_path, "rb+") as f:
obj = PdfWrapper(template_with_radiobutton_stream)
obj.widgets["radio_1"].size = 50
obj.widgets["radio_1"].button_style = "cross"
obj.widgets["radio_2"].size = 40
obj.widgets["radio_2"].button_style = "circle"
obj.widgets["radio_3"].size = 60
obj.widgets["radio_3"].button_style = "check"
obj = obj.fill(
{
"radio_1": 0,
"radio_2": 1,
"radio_3": 2,
},
)

request.config.results["expected_path"] = expected_path
request.config.results["stream"] = obj.read()

expected = f.read()

assert len(obj.read()) == len(expected)
assert obj.stream == expected

0 comments on commit 8ac35a8

Please sign in to comment.