PyPDFForm 1.3.2__tar.gz → 1.3.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of PyPDFForm might be problematic. Click here for more details.

Files changed (32) hide show
  1. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/PKG-INFO +2 -6
  2. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/PyPDFForm/__init__.py +1 -1
  3. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/PyPDFForm/core/constants.py +4 -0
  4. PyPDFForm-1.3.3/PyPDFForm/core/font.py +65 -0
  5. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/PyPDFForm/core/font_size.py +2 -2
  6. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/PyPDFForm/core/template.py +6 -0
  7. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/PyPDFForm/core/utils.py +20 -2
  8. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/PyPDFForm/middleware/checkbox.py +6 -0
  9. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/PyPDFForm/middleware/constants.py +0 -4
  10. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/PyPDFForm/middleware/dropdown.py +6 -0
  11. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/PyPDFForm/middleware/element.py +7 -1
  12. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/PyPDFForm/middleware/radio.py +6 -0
  13. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/PyPDFForm/middleware/template.py +0 -2
  14. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/PyPDFForm/middleware/text.py +7 -0
  15. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/PyPDFForm/wrapper.py +27 -4
  16. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/PyPDFForm.egg-info/PKG-INFO +2 -6
  17. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/README.md +1 -5
  18. PyPDFForm-1.3.2/PyPDFForm/core/font.py +0 -24
  19. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/LICENSE +0 -0
  20. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/PyPDFForm/core/__init__.py +0 -0
  21. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/PyPDFForm/core/filler.py +0 -0
  22. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/PyPDFForm/core/image.py +0 -0
  23. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/PyPDFForm/core/patterns.py +0 -0
  24. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/PyPDFForm/core/watermark.py +0 -0
  25. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/PyPDFForm/middleware/__init__.py +0 -0
  26. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/PyPDFForm/middleware/adapter.py +0 -0
  27. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/PyPDFForm.egg-info/SOURCES.txt +0 -0
  28. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/PyPDFForm.egg-info/dependency_links.txt +0 -0
  29. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/PyPDFForm.egg-info/requires.txt +0 -0
  30. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/PyPDFForm.egg-info/top_level.txt +0 -0
  31. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/setup.cfg +0 -0
  32. {PyPDFForm-1.3.2 → PyPDFForm-1.3.3}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyPDFForm
3
- Version: 1.3.2
3
+ Version: 1.3.3
4
4
  Summary: The Python library for PDF forms.
5
5
  Home-page: https://github.com/chinapandaman/PyPDFForm
6
6
  Author: Jinge Li
@@ -81,10 +81,6 @@ and it should look like [this](https://github.com/chinapandaman/PyPDFForm/raw/ma
81
81
 
82
82
  ## How to Contribute
83
83
 
84
- If you wish to improve this library, there is one specific way you can contribute
85
- on top of the usual open source project norms such as issues and pull requests.
86
-
87
84
  It is difficult to make sure that the library supports all the PDF form creating tools out
88
- there. So if you run into a case where the library does not work for certain PDF forms created by
89
- certain tools, feel free to open an issue with the problematic PDF form attached. I will seek
85
+ there. So if you run into a case where the library does not work for certain PDF forms created by certain tools, feel free to open an issue with the problematic PDF form attached. I will seek
90
86
  to make the library support the attached PDF form as well as the tool used to create it.
@@ -5,4 +5,4 @@ from .wrapper import Wrapper
5
5
 
6
6
  PyPDFForm = Wrapper
7
7
 
8
- __version__ = "1.3.2"
8
+ __version__ = "1.3.3"
@@ -19,6 +19,10 @@ CHOICES_IDENTIFIER = "/Opt"
19
19
 
20
20
  FONT_SIZE_IDENTIFIER = "Tf"
21
21
  FONT_COLOR_IDENTIFIER = " rg"
22
+ DEFAULT_FONT = "Helvetica"
23
+ DEFAULT_FONT_SIZE = 12
24
+ DEFAULT_FONT_COLOR = (0, 0, 0)
25
+ PREVIEW_FONT_COLOR = (1, 0, 0)
22
26
 
23
27
  CHECKBOX_TO_DRAW = "\u2713"
24
28
  RADIO_TO_DRAW = "\u25CF"
@@ -0,0 +1,65 @@
1
+ # -*- coding: utf-8 -*-
2
+ """Contains helpers for font."""
3
+
4
+ import re
5
+ from io import BytesIO
6
+
7
+ import pdfrw
8
+ from reportlab.pdfbase import pdfmetrics
9
+ from reportlab.pdfbase.ttfonts import TTFError, TTFont
10
+
11
+ from . import constants
12
+ from .patterns import TEXT_FIELD_APPEARANCE_PATTERNS
13
+ from .template import traverse_pattern
14
+
15
+
16
+ def register_font(font_name: str, ttf_stream: bytes) -> bool:
17
+ """Registers a font from a ttf file stream."""
18
+
19
+ buff = BytesIO()
20
+ buff.write(ttf_stream)
21
+ buff.seek(0)
22
+
23
+ try:
24
+ pdfmetrics.registerFont(TTFont(name=font_name, filename=buff))
25
+ result = True
26
+ except TTFError:
27
+ result = False
28
+
29
+ buff.close()
30
+ return result
31
+
32
+
33
+ def auto_detect_font(element: pdfrw.PdfDict) -> str:
34
+ """Returns the font of the text field if it is one of the standard fonts."""
35
+
36
+ result = constants.DEFAULT_FONT
37
+
38
+ for pattern in TEXT_FIELD_APPEARANCE_PATTERNS:
39
+ text_appearance = traverse_pattern(pattern, element)
40
+
41
+ if text_appearance:
42
+ text_appearance = (
43
+ text_appearance.replace("(", "").replace(")", "").split(" ")
44
+ )
45
+
46
+ for each in text_appearance:
47
+ if each.startswith("/"):
48
+ text_segments = re.findall("[A-Z][^A-Z]*", each.replace("/", ""))
49
+
50
+ for font in pdfmetrics.standardFonts:
51
+ font_segments = re.findall(
52
+ "[A-Z][^A-Z]*", font.replace("-", "")
53
+ )
54
+ if len(font_segments) != len(text_segments):
55
+ continue
56
+
57
+ found = True
58
+ for i, val in enumerate(font_segments):
59
+ if not val.startswith(text_segments[i]):
60
+ found = False
61
+
62
+ if found:
63
+ return font
64
+
65
+ return result
@@ -6,8 +6,8 @@ from typing import Union
6
6
 
7
7
  import pdfrw
8
8
 
9
- from ..middleware.constants import GLOBAL_FONT_SIZE
10
9
  from . import constants, template
10
+ from .constants import DEFAULT_FONT_SIZE
11
11
 
12
12
 
13
13
  def text_field_font_size(element: pdfrw.PdfDict) -> Union[float, int]:
@@ -17,7 +17,7 @@ def text_field_font_size(element: pdfrw.PdfDict) -> Union[float, int]:
17
17
  """
18
18
 
19
19
  if template.is_text_multiline(element):
20
- return GLOBAL_FONT_SIZE
20
+ return DEFAULT_FONT_SIZE
21
21
 
22
22
  height = abs(
23
23
  float(element[constants.ANNOTATION_RECTANGLE_KEY][1])
@@ -276,6 +276,12 @@ def get_draw_text_coordinates(
276
276
  ) -> Tuple[Union[float, int], Union[float, int]]:
277
277
  """Returns coordinates to draw text at given a PDF form text element."""
278
278
 
279
+ if element_middleware.preview:
280
+ return (
281
+ float(element[constants.ANNOTATION_RECTANGLE_KEY][0]),
282
+ float(element[constants.ANNOTATION_RECTANGLE_KEY][3]) + 5,
283
+ )
284
+
279
285
  element_value = element_middleware.value or ""
280
286
  length = (
281
287
  min(len(element_value), element_middleware.max_length)
@@ -12,6 +12,7 @@ from ..middleware.constants import ELEMENT_TYPES
12
12
  from ..middleware.radio import Radio
13
13
  from ..middleware.text import Text
14
14
  from . import constants
15
+ from . import font as font_core
15
16
  from . import font_size as font_size_core
16
17
  from . import template
17
18
 
@@ -43,6 +44,8 @@ def update_text_field_attributes(
43
44
  key = template.get_element_key(_element)
44
45
 
45
46
  if isinstance(elements[key], Text):
47
+ if elements[key].font is None:
48
+ elements[key].font = font_core.auto_detect_font(_element)
46
49
  if elements[key].font_size is None:
47
50
  elements[key].font_size = template.get_text_field_font_size(
48
51
  _element
@@ -139,9 +142,9 @@ def checkbox_radio_to_draw(
139
142
  element_name=element.name,
140
143
  element_value="",
141
144
  )
142
- new_element.font = "Helvetica"
145
+ new_element.font = constants.DEFAULT_FONT
143
146
  new_element.font_size = font_size
144
- new_element.font_color = (0, 0, 0)
147
+ new_element.font_color = constants.DEFAULT_FONT_COLOR
145
148
 
146
149
  if isinstance(element, Checkbox):
147
150
  new_element.value = constants.CHECKBOX_TO_DRAW
@@ -151,6 +154,21 @@ def checkbox_radio_to_draw(
151
154
  return new_element
152
155
 
153
156
 
157
+ def preview_element_to_draw(element: ELEMENT_TYPES) -> Text:
158
+ """Converts an element to a preview text element."""
159
+
160
+ new_element = Text(
161
+ element_name=element.name,
162
+ element_value="{" + f" {element.name} " + "}",
163
+ )
164
+ new_element.font = constants.DEFAULT_FONT
165
+ new_element.font_size = constants.DEFAULT_FONT_SIZE
166
+ new_element.font_color = constants.PREVIEW_FONT_COLOR
167
+ new_element.preview = True
168
+
169
+ return new_element
170
+
171
+
154
172
  def remove_all_elements(pdf: bytes) -> bytes:
155
173
  """Removes all elements from a pdfrw parsed PDF form."""
156
174
 
@@ -21,3 +21,9 @@ class Checkbox(Element):
21
21
  """Json schema definition of the checkbox."""
22
22
 
23
23
  return {"type": "boolean"}
24
+
25
+ @property
26
+ def sample_value(self) -> bool:
27
+ """Sample value of the checkbox."""
28
+
29
+ return True
@@ -8,10 +8,6 @@ from .dropdown import Dropdown
8
8
  from .radio import Radio
9
9
  from .text import Text
10
10
 
11
- GLOBAL_FONT = "Helvetica"
12
- GLOBAL_FONT_SIZE = 12
13
- GLOBAL_FONT_COLOR = (0, 0, 0)
14
-
15
11
  VERSION_IDENTIFIERS = [
16
12
  b"%PDF-1.0",
17
13
  b"%PDF-1.1",
@@ -23,3 +23,9 @@ class Dropdown(Element):
23
23
  """Json schema definition of the dropdown."""
24
24
 
25
25
  return {"type": "integer", "maximum": len(self.choices) - 1}
26
+
27
+ @property
28
+ def sample_value(self) -> int:
29
+ """Sample value of the dropdown."""
30
+
31
+ return 0
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """Contains element middleware."""
3
3
 
4
- from typing import Union
4
+ from typing import Any, Union
5
5
 
6
6
 
7
7
  class Element:
@@ -28,3 +28,9 @@ class Element:
28
28
  """Json schema definition of the element."""
29
29
 
30
30
  raise NotImplementedError
31
+
32
+ @property
33
+ def sample_value(self) -> Any:
34
+ """Sample value of the element."""
35
+
36
+ raise NotImplementedError
@@ -23,3 +23,9 @@ class Radio(Element):
23
23
  """Json schema definition of the radiobutton."""
24
24
 
25
25
  return {"type": "integer", "maximum": self.number_of_options - 1}
26
+
27
+ @property
28
+ def sample_value(self) -> int:
29
+ """Sample value of the radiobutton."""
30
+
31
+ return 0
@@ -67,8 +67,6 @@ def dropdown_to_text(dropdown: Dropdown) -> Text:
67
67
 
68
68
  result = Text(dropdown.name)
69
69
 
70
- result.font = constants.GLOBAL_FONT
71
-
72
70
  if dropdown.value is not None:
73
71
  result.value = (
74
72
  dropdown.choices[dropdown.value]
@@ -25,6 +25,7 @@ class Text(Element):
25
25
  self.character_paddings = None
26
26
  self.text_lines = None
27
27
  self.text_line_x_coordinates = None
28
+ self.preview = False
28
29
 
29
30
  @property
30
31
  def schema_definition(self) -> dict:
@@ -36,3 +37,9 @@ class Text(Element):
36
37
  result["maxLength"] = self.max_length
37
38
 
38
39
  return result
40
+
41
+ @property
42
+ def sample_value(self) -> str:
43
+ """Sample value of the text field."""
44
+
45
+ return self.name
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
 
6
6
  from typing import BinaryIO, Dict, Union
7
7
 
8
+ from .core import constants as core_constants
8
9
  from .core import filler, font
9
10
  from .core import image as image_core
10
11
  from .core import utils
@@ -32,7 +33,7 @@ class Wrapper:
32
33
 
33
34
  for each in self.elements.values():
34
35
  if isinstance(each, Text):
35
- each.font = kwargs.get("global_font", constants.GLOBAL_FONT)
36
+ each.font = kwargs.get("global_font")
36
37
  each.font_size = kwargs.get("global_font_size")
37
38
  each.font_color = kwargs.get("global_font_color")
38
39
 
@@ -41,6 +42,12 @@ class Wrapper:
41
42
 
42
43
  return self.stream
43
44
 
45
+ @property
46
+ def sample_data(self) -> dict:
47
+ """Returns a valid sample data that can be filled into the PDF form."""
48
+
49
+ return {key: value.sample_value for key, value in self.elements.items()}
50
+
44
51
  @property
45
52
  def version(self) -> Union[str, None]:
46
53
  """Gets the version of the PDF."""
@@ -76,6 +83,18 @@ class Wrapper:
76
83
 
77
84
  return new_obj
78
85
 
86
+ @property
87
+ def preview(self) -> bytes:
88
+ """Inspects all supported elements' names for the PDF form."""
89
+
90
+ return filler.fill(
91
+ self.stream,
92
+ {
93
+ key: utils.preview_element_to_draw(value)
94
+ for key, value in self.elements.items()
95
+ },
96
+ )
97
+
79
98
  def fill(
80
99
  self,
81
100
  data: Dict[str, Union[str, bool, int]],
@@ -112,9 +131,13 @@ class Wrapper:
112
131
 
113
132
  new_element = Text("new")
114
133
  new_element.value = text
115
- new_element.font = kwargs.get("font", constants.GLOBAL_FONT)
116
- new_element.font_size = kwargs.get("font_size", constants.GLOBAL_FONT_SIZE)
117
- new_element.font_color = kwargs.get("font_color", constants.GLOBAL_FONT_COLOR)
134
+ new_element.font = kwargs.get("font", core_constants.DEFAULT_FONT)
135
+ new_element.font_size = kwargs.get(
136
+ "font_size", core_constants.DEFAULT_FONT_SIZE
137
+ )
138
+ new_element.font_color = kwargs.get(
139
+ "font_color", core_constants.DEFAULT_FONT_COLOR
140
+ )
118
141
 
119
142
  watermarks = watermark_core.create_watermarks_and_draw(
120
143
  self.stream,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyPDFForm
3
- Version: 1.3.2
3
+ Version: 1.3.3
4
4
  Summary: The Python library for PDF forms.
5
5
  Home-page: https://github.com/chinapandaman/PyPDFForm
6
6
  Author: Jinge Li
@@ -81,10 +81,6 @@ and it should look like [this](https://github.com/chinapandaman/PyPDFForm/raw/ma
81
81
 
82
82
  ## How to Contribute
83
83
 
84
- If you wish to improve this library, there is one specific way you can contribute
85
- on top of the usual open source project norms such as issues and pull requests.
86
-
87
84
  It is difficult to make sure that the library supports all the PDF form creating tools out
88
- there. So if you run into a case where the library does not work for certain PDF forms created by
89
- certain tools, feel free to open an issue with the problematic PDF form attached. I will seek
85
+ there. So if you run into a case where the library does not work for certain PDF forms created by certain tools, feel free to open an issue with the problematic PDF form attached. I will seek
90
86
  to make the library support the attached PDF form as well as the tool used to create it.
@@ -68,10 +68,6 @@ and it should look like [this](https://github.com/chinapandaman/PyPDFForm/raw/ma
68
68
 
69
69
  ## How to Contribute
70
70
 
71
- If you wish to improve this library, there is one specific way you can contribute
72
- on top of the usual open source project norms such as issues and pull requests.
73
-
74
71
  It is difficult to make sure that the library supports all the PDF form creating tools out
75
- there. So if you run into a case where the library does not work for certain PDF forms created by
76
- certain tools, feel free to open an issue with the problematic PDF form attached. I will seek
72
+ there. So if you run into a case where the library does not work for certain PDF forms created by certain tools, feel free to open an issue with the problematic PDF form attached. I will seek
77
73
  to make the library support the attached PDF form as well as the tool used to create it.
@@ -1,24 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- """Contains helpers for font."""
3
-
4
- from io import BytesIO
5
-
6
- from reportlab.pdfbase import pdfmetrics
7
- from reportlab.pdfbase.ttfonts import TTFError, TTFont
8
-
9
-
10
- def register_font(font_name: str, ttf_stream: bytes) -> bool:
11
- """Registers a font from a ttf file stream."""
12
-
13
- buff = BytesIO()
14
- buff.write(ttf_stream)
15
- buff.seek(0)
16
-
17
- try:
18
- pdfmetrics.registerFont(TTFont(name=font_name, filename=buff))
19
- result = True
20
- except TTFError:
21
- result = False
22
-
23
- buff.close()
24
- return result
File without changes
File without changes
File without changes