PyPDFForm 4.7.3__tar.gz → 4.7.5__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.
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PKG-INFO +2 -2
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/__init__.py +1 -1
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/coordinate.py +2 -2
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/egress.py +3 -4
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/filler.py +1 -2
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/font.py +77 -26
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/hooks.py +1 -2
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/template.py +5 -5
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/utils.py +6 -28
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/watermark.py +17 -34
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/widgets/base.py +1 -2
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/widgets/signature.py +7 -7
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/wrapper.py +8 -7
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm.egg-info/PKG-INFO +2 -2
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm.egg-info/requires.txt +1 -1
- {pypdfform-4.7.3 → pypdfform-4.7.5}/pyproject.toml +2 -2
- {pypdfform-4.7.3 → pypdfform-4.7.5}/LICENSE +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/adapter.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/annotations/__init__.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/annotations/base.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/annotations/link.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/annotations/stamp.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/annotations/text.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/annotations/text_markup.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/assets/__init__.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/assets/bedrock.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/assets/blank.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/constants.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/deprecation.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/image.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/middleware/__init__.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/middleware/base.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/middleware/checkbox.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/middleware/dropdown.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/middleware/image.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/middleware/radio.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/middleware/signature.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/middleware/text.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/patterns.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/raw/__init__.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/raw/circle.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/raw/ellipse.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/raw/image.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/raw/line.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/raw/rect.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/raw/text.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/types.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/widgets/__init__.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/widgets/checkbox.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/widgets/dropdown.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/widgets/image.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/widgets/radio.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm/widgets/text.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm.egg-info/SOURCES.txt +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm.egg-info/dependency_links.txt +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/PyPDFForm.egg-info/top_level.txt +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/README.md +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/setup.cfg +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/tests/test_bulk_create_fields.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/tests/test_create_widget.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/tests/test_draw_elements.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/tests/test_dropdown.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/tests/test_extract_middleware_attributes.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/tests/test_fill_max_length_text_field.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/tests/test_font_widths.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/tests/test_functional.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/tests/test_generate_appearance_streams.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/tests/test_js.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/tests/test_need_appearances.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/tests/test_paragraph.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/tests/test_signature.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/tests/test_use_full_widget_name.py +0 -0
- {pypdfform-4.7.3 → pypdfform-4.7.5}/tests/test_widget_attr_trigger.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyPDFForm
|
|
3
|
-
Version: 4.7.
|
|
3
|
+
Version: 4.7.5
|
|
4
4
|
Summary: The Python library for PDF forms.
|
|
5
5
|
Author: Jinge Li
|
|
6
6
|
License-Expression: MIT
|
|
@@ -24,7 +24,7 @@ Requires-Dist: cryptography<47.0.0,>=46.0.3
|
|
|
24
24
|
Requires-Dist: fonttools<5.0.0,>=4.60.1
|
|
25
25
|
Requires-Dist: pikepdf<11.0.0,>=10.5.0
|
|
26
26
|
Requires-Dist: pillow<13.0.0,>=12.0.0
|
|
27
|
-
Requires-Dist: pypdf<7.0.0,>=6.
|
|
27
|
+
Requires-Dist: pypdf<7.0.0,>=6.9.0
|
|
28
28
|
Requires-Dist: reportlab<5.0.0,>=4.4.6
|
|
29
29
|
Provides-Extra: dev
|
|
30
30
|
Requires-Dist: black<27.0.0,>=25.11.0; extra == "dev"
|
|
@@ -7,6 +7,7 @@ It allows developers to visualize the coordinate system of each page in a PDF, w
|
|
|
7
7
|
for debugging and precisely positioning elements when filling or drawing on PDF forms.
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
+
from io import BytesIO
|
|
10
11
|
from typing import Tuple
|
|
11
12
|
|
|
12
13
|
from pypdf import PdfReader
|
|
@@ -14,7 +15,6 @@ from reportlab.pdfbase.pdfmetrics import stringWidth
|
|
|
14
15
|
|
|
15
16
|
from .constants import COORDINATE_GRID_FONT_SIZE_MARGIN_RATIO, DEFAULT_FONT
|
|
16
17
|
from .middleware.text import Text
|
|
17
|
-
from .utils import stream_to_io
|
|
18
18
|
from .watermark import create_watermarks_and_draw, merge_watermarks_with_pdf
|
|
19
19
|
|
|
20
20
|
|
|
@@ -39,7 +39,7 @@ def generate_coordinate_grid(
|
|
|
39
39
|
Returns:
|
|
40
40
|
bytes: The PDF file with the coordinate grid overlay as bytes.
|
|
41
41
|
"""
|
|
42
|
-
pdf_file = PdfReader(
|
|
42
|
+
pdf_file = PdfReader(BytesIO(pdf))
|
|
43
43
|
lines_by_page = {}
|
|
44
44
|
texts_by_page = {}
|
|
45
45
|
|
|
@@ -19,7 +19,6 @@ from pypdf.generic import DictionaryObject, NameObject, TextStringObject
|
|
|
19
19
|
|
|
20
20
|
from .constants import (JS, XFA, AcroForm, JavaScript, OpenAction, Root, S,
|
|
21
21
|
Title)
|
|
22
|
-
from .utils import stream_to_io
|
|
23
22
|
|
|
24
23
|
|
|
25
24
|
@lru_cache
|
|
@@ -44,7 +43,7 @@ def appearance_streams_handler(pdf: bytes, generate_appearance_streams: bool) ->
|
|
|
44
43
|
Returns:
|
|
45
44
|
bytes: The modified PDF content as a bytes stream.
|
|
46
45
|
"""
|
|
47
|
-
reader = PdfReader(
|
|
46
|
+
reader = PdfReader(BytesIO(pdf))
|
|
48
47
|
writer = PdfWriter()
|
|
49
48
|
|
|
50
49
|
if AcroForm in reader.trailer[Root] and XFA in reader.trailer[Root][AcroForm]:
|
|
@@ -59,7 +58,7 @@ def appearance_streams_handler(pdf: bytes, generate_appearance_streams: bool) ->
|
|
|
59
58
|
result = f.read()
|
|
60
59
|
|
|
61
60
|
if generate_appearance_streams:
|
|
62
|
-
with Pdf.open(
|
|
61
|
+
with Pdf.open(BytesIO(result)) as f:
|
|
63
62
|
f.generate_appearance_streams()
|
|
64
63
|
with BytesIO() as r:
|
|
65
64
|
f.save(r)
|
|
@@ -87,7 +86,7 @@ def preserve_pdf_properties(
|
|
|
87
86
|
Returns:
|
|
88
87
|
bytes: The modified PDF content as a bytes stream.
|
|
89
88
|
"""
|
|
90
|
-
reader = PdfReader(
|
|
89
|
+
reader = PdfReader(BytesIO(pdf))
|
|
91
90
|
writer = PdfWriter()
|
|
92
91
|
writer.append(reader)
|
|
93
92
|
|
|
@@ -27,7 +27,6 @@ from .middleware.text import Text
|
|
|
27
27
|
from .patterns import (get_widget_key, update_checkbox_value,
|
|
28
28
|
update_dropdown_value, update_radio_value,
|
|
29
29
|
update_text_value)
|
|
30
|
-
from .utils import stream_to_io
|
|
31
30
|
from .watermark import create_watermarks_and_draw, merge_watermarks_with_pdf
|
|
32
31
|
|
|
33
32
|
|
|
@@ -177,7 +176,7 @@ def fill(
|
|
|
177
176
|
The image drawn stream is only returned if there are any image or signature widgets
|
|
178
177
|
in the form.
|
|
179
178
|
"""
|
|
180
|
-
pdf = PdfReader(
|
|
179
|
+
pdf = PdfReader(BytesIO(template))
|
|
181
180
|
out = PdfWriter()
|
|
182
181
|
out.append(pdf)
|
|
183
182
|
|
|
@@ -7,17 +7,21 @@ allowing these fonts to be used when filling form fields. The module also provid
|
|
|
7
7
|
for extracting font information from TTF streams and managing font names within a PDF.
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
+
from contextlib import contextmanager
|
|
10
11
|
from functools import lru_cache
|
|
11
12
|
from io import BytesIO
|
|
13
|
+
from typing import Generator
|
|
14
|
+
from uuid import uuid4
|
|
12
15
|
from zlib import compress
|
|
13
16
|
|
|
14
17
|
from fontTools.ttLib import TTFont as FT_TTFont
|
|
15
18
|
from pypdf import PdfReader, PdfWriter
|
|
16
19
|
from pypdf.generic import (ArrayObject, DictionaryObject, FloatObject,
|
|
17
20
|
NameObject, NumberObject, StreamObject)
|
|
18
|
-
from reportlab.pdfbase.pdfmetrics import
|
|
21
|
+
from reportlab.pdfbase.pdfmetrics import _fonts
|
|
19
22
|
from reportlab.pdfbase.ttfonts import TTFError, TTFont
|
|
20
23
|
|
|
24
|
+
from .assets.blank import BlankPage
|
|
21
25
|
from .constants import (DEFAULT_ASSUMED_GLYPH_WIDTH, DR, EM_TO_PDF_FACTOR,
|
|
22
26
|
ENCODING_TABLE_SIZE, FIRST_CHAR_CODE, FONT_NAME_PREFIX,
|
|
23
27
|
LAST_CHAR_CODE, AcroForm, BaseFont, Encoding, Fields,
|
|
@@ -26,26 +30,26 @@ from .constants import (DEFAULT_ASSUMED_GLYPH_WIDTH, DR, EM_TO_PDF_FACTOR,
|
|
|
26
30
|
FontName, FontNotdef, LastChar, Length, Length1,
|
|
27
31
|
MissingWidth, Resources, Subtype, TrueType, Type,
|
|
28
32
|
Widths, WinAnsiEncoding)
|
|
29
|
-
from .
|
|
30
|
-
from .watermark import
|
|
33
|
+
from .raw.text import RawText
|
|
34
|
+
from .watermark import create_watermarks_and_draw
|
|
31
35
|
|
|
32
36
|
|
|
33
37
|
@lru_cache
|
|
34
|
-
def
|
|
38
|
+
def validate_font(font_name: str, ttf_stream: bytes) -> bool:
|
|
35
39
|
"""
|
|
36
|
-
|
|
40
|
+
Validates a TrueType font stream.
|
|
37
41
|
|
|
38
|
-
This
|
|
42
|
+
This checks if the provided stream is a valid TrueType font by parsing it
|
|
43
|
+
with ReportLab's TTFont.
|
|
39
44
|
|
|
40
45
|
Args:
|
|
41
|
-
font_name (str): The name
|
|
42
|
-
to reference the font when creating PDF documents with ReportLab.
|
|
46
|
+
font_name (str): The name of the font.
|
|
43
47
|
ttf_stream (bytes): The font file data in TTF format. This should be the raw
|
|
44
48
|
bytes of the TTF file.
|
|
45
49
|
|
|
46
50
|
Returns:
|
|
47
|
-
bool: True if the font
|
|
48
|
-
Returns False if a TTFError occurs during
|
|
51
|
+
bool: True if the font stream is valid, False otherwise.
|
|
52
|
+
Returns False if a TTFError occurs during parsing, which usually
|
|
49
53
|
indicates an invalid TTF stream.
|
|
50
54
|
"""
|
|
51
55
|
buff = BytesIO()
|
|
@@ -53,7 +57,7 @@ def register_font(font_name: str, ttf_stream: bytes) -> bool:
|
|
|
53
57
|
buff.seek(0)
|
|
54
58
|
|
|
55
59
|
try:
|
|
56
|
-
|
|
60
|
+
TTFont(name=font_name, filename=buff)
|
|
57
61
|
result = True
|
|
58
62
|
except TTFError:
|
|
59
63
|
result = False
|
|
@@ -62,7 +66,7 @@ def register_font(font_name: str, ttf_stream: bytes) -> bool:
|
|
|
62
66
|
return result
|
|
63
67
|
|
|
64
68
|
|
|
65
|
-
def
|
|
69
|
+
def _get_additional_font_params(pdf: bytes, base_font_name: str) -> tuple:
|
|
66
70
|
"""
|
|
67
71
|
Retrieves additional font parameters from a PDF document for a given base font name.
|
|
68
72
|
|
|
@@ -83,7 +87,7 @@ def get_additional_font_params(pdf: bytes, base_font_name: str) -> tuple:
|
|
|
83
87
|
"""
|
|
84
88
|
font_descriptor_params = {}
|
|
85
89
|
font_dict_params = {}
|
|
86
|
-
reader = PdfReader(
|
|
90
|
+
reader = PdfReader(BytesIO(pdf))
|
|
87
91
|
first_page = reader.get_page(0)
|
|
88
92
|
|
|
89
93
|
for font in first_page[Resources][Font].values():
|
|
@@ -144,8 +148,58 @@ def compute_font_glyph_widths(ttf_file: BytesIO, missing_width: float) -> list[f
|
|
|
144
148
|
return widths
|
|
145
149
|
|
|
146
150
|
|
|
151
|
+
@contextmanager
|
|
152
|
+
def temporary_font_registration(
|
|
153
|
+
fonts: list[tuple[str, bytes]],
|
|
154
|
+
) -> Generator[dict[str, str], None, None]:
|
|
155
|
+
"""
|
|
156
|
+
Registers a list of fonts temporarily with unique names, yielding a mapping
|
|
157
|
+
from the original font names to the unique names.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
fonts (list[tuple[str, bytes]]): A list of tuples, each containing a font name and its TTF stream.
|
|
161
|
+
|
|
162
|
+
Yields:
|
|
163
|
+
dict: A mapping of the original font names to the temporary unique names used by ReportLab.
|
|
164
|
+
"""
|
|
165
|
+
font_mapping = {}
|
|
166
|
+
for font_name, ttf_stream in fonts:
|
|
167
|
+
rl_name = uuid4().hex
|
|
168
|
+
font_mapping[font_name] = rl_name
|
|
169
|
+
_fonts[rl_name] = TTFont(rl_name, BytesIO(ttf_stream))
|
|
170
|
+
|
|
171
|
+
try:
|
|
172
|
+
yield font_mapping
|
|
173
|
+
finally:
|
|
174
|
+
for rl_name in font_mapping.values():
|
|
175
|
+
if rl_name in _fonts:
|
|
176
|
+
del _fonts[rl_name]
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
@lru_cache
|
|
180
|
+
def _get_watermark_with_font(ttf_stream: bytes) -> bytes:
|
|
181
|
+
"""
|
|
182
|
+
Creates a watermark PDF with a single space character using the specified font.
|
|
183
|
+
|
|
184
|
+
This function is primarily used to generate a dummy PDF page that includes
|
|
185
|
+
a specific font, which can then be merged with another PDF to ensure the
|
|
186
|
+
font is available or embedded. The result is cached for performance.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
ttf_stream (bytes): The TrueType font stream to use.
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
bytes: The watermark PDF as a byte stream.
|
|
193
|
+
"""
|
|
194
|
+
with temporary_font_registration([("temp", ttf_stream)]) as font_mapping:
|
|
195
|
+
return create_watermarks_and_draw(
|
|
196
|
+
BlankPage().read(),
|
|
197
|
+
[RawText(" ", 1, 0, 0, font=font_mapping["temp"]).to_draw],
|
|
198
|
+
)[0]
|
|
199
|
+
|
|
200
|
+
|
|
147
201
|
def register_font_acroform(
|
|
148
|
-
pdf: bytes,
|
|
202
|
+
pdf: bytes, ttf_stream: bytes, need_appearances: bool
|
|
149
203
|
) -> tuple:
|
|
150
204
|
"""
|
|
151
205
|
Registers a TrueType font within the PDF's AcroForm dictionary.
|
|
@@ -157,7 +211,6 @@ def register_font_acroform(
|
|
|
157
211
|
Args:
|
|
158
212
|
pdf (bytes): The PDF file data as bytes. This is the PDF document that
|
|
159
213
|
will be modified to include the new font.
|
|
160
|
-
font_name (str): The name of the font being registered.
|
|
161
214
|
ttf_stream (bytes): The font file data in TTF format as bytes. This is the
|
|
162
215
|
raw data of the TrueType font file.
|
|
163
216
|
need_appearances (bool): If True, attempts to retrieve existing font parameters
|
|
@@ -168,16 +221,16 @@ def register_font_acroform(
|
|
|
168
221
|
tuple: A tuple containing the modified PDF data as bytes and the new font name
|
|
169
222
|
(str) that was assigned to the registered font within the PDF.
|
|
170
223
|
"""
|
|
171
|
-
base_font_name =
|
|
172
|
-
reader = PdfReader(
|
|
224
|
+
base_font_name = _get_base_font_name(ttf_stream)
|
|
225
|
+
reader = PdfReader(BytesIO(pdf))
|
|
173
226
|
writer = PdfWriter()
|
|
174
227
|
writer.append(reader)
|
|
175
228
|
|
|
176
229
|
font_descriptor_params = {}
|
|
177
230
|
font_dict_params = {}
|
|
178
231
|
if need_appearances:
|
|
179
|
-
font_descriptor_params, font_dict_params =
|
|
180
|
-
|
|
232
|
+
font_descriptor_params, font_dict_params = _get_additional_font_params(
|
|
233
|
+
_get_watermark_with_font(ttf_stream), base_font_name
|
|
181
234
|
)
|
|
182
235
|
|
|
183
236
|
font_file_stream = StreamObject()
|
|
@@ -245,7 +298,7 @@ def register_font_acroform(
|
|
|
245
298
|
dr[NameObject(Font)] = DictionaryObject()
|
|
246
299
|
fonts = dr[Font]
|
|
247
300
|
|
|
248
|
-
new_font_name =
|
|
301
|
+
new_font_name = _get_new_font_name(fonts)
|
|
249
302
|
fonts[NameObject(new_font_name)] = font_dict_ref
|
|
250
303
|
|
|
251
304
|
with BytesIO() as f:
|
|
@@ -255,7 +308,7 @@ def register_font_acroform(
|
|
|
255
308
|
|
|
256
309
|
|
|
257
310
|
@lru_cache
|
|
258
|
-
def
|
|
311
|
+
def _get_base_font_name(ttf_stream: bytes) -> str:
|
|
259
312
|
"""
|
|
260
313
|
Extracts the base font name from a TrueType font stream.
|
|
261
314
|
|
|
@@ -269,12 +322,10 @@ def get_base_font_name(ttf_stream: bytes) -> str:
|
|
|
269
322
|
Returns:
|
|
270
323
|
str: The base font name, prefixed with a forward slash.
|
|
271
324
|
"""
|
|
272
|
-
return (
|
|
273
|
-
f"/{TTFont(name='new_font', filename=stream_to_io(ttf_stream)).face.name.ustr}"
|
|
274
|
-
)
|
|
325
|
+
return f"/{TTFont(name='new_font', filename=BytesIO(ttf_stream)).face.name.ustr}"
|
|
275
326
|
|
|
276
327
|
|
|
277
|
-
def
|
|
328
|
+
def _get_new_font_name(fonts: dict) -> str:
|
|
278
329
|
"""
|
|
279
330
|
Generates a new unique font name to avoid conflicts with existing fonts in the PDF.
|
|
280
331
|
|
|
@@ -314,7 +365,7 @@ def get_all_available_fonts(pdf: bytes) -> dict:
|
|
|
314
365
|
(without the leading slash) and the values are the corresponding font
|
|
315
366
|
identifiers in the PDF. Returns an empty dictionary if no fonts are found.
|
|
316
367
|
"""
|
|
317
|
-
reader = PdfReader(
|
|
368
|
+
reader = PdfReader(BytesIO(pdf))
|
|
318
369
|
try:
|
|
319
370
|
fonts = reader.root_object[AcroForm][DR][Font]
|
|
320
371
|
except KeyError:
|
|
@@ -24,7 +24,6 @@ from .constants import (AA, COMB, DA, FONT_COLOR_IDENTIFIER,
|
|
|
24
24
|
JavaScript, MaxLen, Opt, Parent, Q, Rect, S, Type, U,
|
|
25
25
|
X)
|
|
26
26
|
from .patterns import get_widget_key
|
|
27
|
-
from .utils import stream_to_io
|
|
28
27
|
|
|
29
28
|
|
|
30
29
|
def trigger_widget_hooks(
|
|
@@ -52,7 +51,7 @@ def trigger_widget_hooks(
|
|
|
52
51
|
Returns:
|
|
53
52
|
bytes: The modified PDF data as bytes, with the widget hooks applied.
|
|
54
53
|
"""
|
|
55
|
-
pdf_file = PdfReader(
|
|
54
|
+
pdf_file = PdfReader(BytesIO(pdf))
|
|
56
55
|
output = PdfWriter()
|
|
57
56
|
output.append(pdf_file)
|
|
58
57
|
|
|
@@ -28,7 +28,7 @@ from .patterns import (WIDGET_DESCRIPTION_PATTERNS, WIDGET_TYPE_PATTERNS,
|
|
|
28
28
|
get_field_hidden, get_field_rect, get_radio_value,
|
|
29
29
|
get_text_field_alignment, get_text_field_max_length,
|
|
30
30
|
get_text_value, get_widget_key, update_annotation_name)
|
|
31
|
-
from .utils import extract_widget_property, find_pattern_match
|
|
31
|
+
from .utils import extract_widget_property, find_pattern_match
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
@lru_cache
|
|
@@ -45,7 +45,7 @@ def get_metadata(pdf: bytes) -> dict:
|
|
|
45
45
|
if not pdf:
|
|
46
46
|
return {}
|
|
47
47
|
|
|
48
|
-
reader = PdfReader(
|
|
48
|
+
reader = PdfReader(BytesIO(pdf))
|
|
49
49
|
return reader.metadata or {}
|
|
50
50
|
|
|
51
51
|
|
|
@@ -218,7 +218,7 @@ def get_widgets_by_page(pdf: bytes) -> Dict[int, List[dict]]:
|
|
|
218
218
|
Dict[int, List[dict]]: A dictionary where keys are page numbers (1-indexed)
|
|
219
219
|
and values are lists of widget dictionaries.
|
|
220
220
|
"""
|
|
221
|
-
pdf_file = PdfReader(
|
|
221
|
+
pdf_file = PdfReader(BytesIO(pdf))
|
|
222
222
|
|
|
223
223
|
result = {}
|
|
224
224
|
|
|
@@ -335,7 +335,7 @@ def create_annotations(
|
|
|
335
335
|
Returns:
|
|
336
336
|
bytes: The updated PDF stream with the added annotations.
|
|
337
337
|
"""
|
|
338
|
-
writer = PdfWriter(
|
|
338
|
+
writer = PdfWriter(BytesIO(template))
|
|
339
339
|
annotations_by_page = _group_annotations_by_page(annotations)
|
|
340
340
|
|
|
341
341
|
for i, page in enumerate(writer.pages):
|
|
@@ -382,7 +382,7 @@ def update_widget_keys(
|
|
|
382
382
|
Returns:
|
|
383
383
|
bytes: The updated PDF template as a byte stream.
|
|
384
384
|
"""
|
|
385
|
-
pdf = PdfReader(
|
|
385
|
+
pdf = PdfReader(BytesIO(template))
|
|
386
386
|
out = PdfWriter()
|
|
387
387
|
out.append(pdf)
|
|
388
388
|
|
|
@@ -18,7 +18,7 @@ from functools import lru_cache
|
|
|
18
18
|
from io import BytesIO
|
|
19
19
|
from secrets import choice
|
|
20
20
|
from string import ascii_letters, digits, punctuation
|
|
21
|
-
from typing import Any,
|
|
21
|
+
from typing import Any, List
|
|
22
22
|
|
|
23
23
|
from pypdf import PdfReader, PdfWriter
|
|
24
24
|
from pypdf.generic import ArrayObject, DictionaryObject, NameObject
|
|
@@ -26,28 +26,6 @@ from pypdf.generic import ArrayObject, DictionaryObject, NameObject
|
|
|
26
26
|
from .constants import SLASH, UNIQUE_SUFFIX_LENGTH, Annots
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
def stream_to_io(stream: bytes) -> BinaryIO:
|
|
30
|
-
"""
|
|
31
|
-
Converts a bytes stream to a BinaryIO object, which can be used by PyPDFForm.
|
|
32
|
-
|
|
33
|
-
This function takes a bytes stream as input and returns a BinaryIO object
|
|
34
|
-
that represents the same data. This is useful because PyPDFForm often
|
|
35
|
-
works with BinaryIO objects, so this function allows you to easily convert
|
|
36
|
-
a bytes stream to the correct format.
|
|
37
|
-
|
|
38
|
-
Args:
|
|
39
|
-
stream (bytes): The bytes stream to convert.
|
|
40
|
-
|
|
41
|
-
Returns:
|
|
42
|
-
BinaryIO: A BinaryIO object representing the stream.
|
|
43
|
-
"""
|
|
44
|
-
result = BytesIO()
|
|
45
|
-
result.write(stream)
|
|
46
|
-
result.seek(0)
|
|
47
|
-
|
|
48
|
-
return result
|
|
49
|
-
|
|
50
|
-
|
|
51
29
|
@lru_cache
|
|
52
30
|
def remove_all_widgets(pdf: bytes) -> bytes:
|
|
53
31
|
"""
|
|
@@ -63,7 +41,7 @@ def remove_all_widgets(pdf: bytes) -> bytes:
|
|
|
63
41
|
Returns:
|
|
64
42
|
bytes: The PDF with all widgets removed, as a bytes stream.
|
|
65
43
|
"""
|
|
66
|
-
pdf_file = PdfReader(
|
|
44
|
+
pdf_file = PdfReader(BytesIO(pdf))
|
|
67
45
|
result_stream = BytesIO()
|
|
68
46
|
writer = PdfWriter()
|
|
69
47
|
for page in pdf_file.pages:
|
|
@@ -89,7 +67,7 @@ def get_page_streams(pdf: bytes) -> List[bytes]:
|
|
|
89
67
|
Returns:
|
|
90
68
|
List[bytes]: A list of bytes streams, one for each page.
|
|
91
69
|
"""
|
|
92
|
-
pdf_file = PdfReader(
|
|
70
|
+
pdf_file = PdfReader(BytesIO(pdf))
|
|
93
71
|
result = []
|
|
94
72
|
|
|
95
73
|
for page in pdf_file.pages:
|
|
@@ -167,8 +145,8 @@ def merge_two_pdfs(pdf: bytes, other: bytes) -> bytes:
|
|
|
167
145
|
bytes: The merged PDF file as a byte stream.
|
|
168
146
|
"""
|
|
169
147
|
output = PdfWriter()
|
|
170
|
-
pdf_file = PdfReader(
|
|
171
|
-
other_file = PdfReader(
|
|
148
|
+
pdf_file = PdfReader(BytesIO(pdf))
|
|
149
|
+
other_file = PdfReader(BytesIO(other))
|
|
172
150
|
result = BytesIO()
|
|
173
151
|
|
|
174
152
|
for page in pdf_file.pages:
|
|
@@ -179,7 +157,7 @@ def merge_two_pdfs(pdf: bytes, other: bytes) -> bytes:
|
|
|
179
157
|
output.write(result)
|
|
180
158
|
result.seek(0)
|
|
181
159
|
|
|
182
|
-
merged_no_widgets = PdfReader(
|
|
160
|
+
merged_no_widgets = PdfReader(BytesIO(remove_all_widgets(result.read())))
|
|
183
161
|
output = PdfWriter()
|
|
184
162
|
output.append(merged_no_widgets)
|
|
185
163
|
|
|
@@ -9,7 +9,6 @@ and to copy specific widgets from the watermarks to the original PDF.
|
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
11
|
from collections import defaultdict
|
|
12
|
-
from functools import lru_cache
|
|
13
12
|
from io import BytesIO
|
|
14
13
|
from typing import Any, Dict, List, Optional
|
|
15
14
|
|
|
@@ -18,11 +17,8 @@ from pypdf.generic import ArrayObject, NameObject
|
|
|
18
17
|
from reportlab.lib.utils import ImageReader
|
|
19
18
|
from reportlab.pdfgen.canvas import Canvas
|
|
20
19
|
|
|
21
|
-
from .assets.blank import BlankPage
|
|
22
20
|
from .constants import Annots
|
|
23
21
|
from .patterns import get_widget_key
|
|
24
|
-
from .raw.text import RawText
|
|
25
|
-
from .utils import stream_to_io
|
|
26
22
|
|
|
27
23
|
|
|
28
24
|
def draw_text(canvas: Canvas, **kwargs) -> None:
|
|
@@ -43,9 +39,10 @@ def draw_text(canvas: Canvas, **kwargs) -> None:
|
|
|
43
39
|
widget = kwargs["widget"]
|
|
44
40
|
coordinate_x = kwargs["x"]
|
|
45
41
|
coordinate_y = kwargs["y"]
|
|
42
|
+
font_mapping = kwargs.get("font_mapping", {})
|
|
46
43
|
|
|
47
44
|
text_to_draw = widget.value
|
|
48
|
-
canvas.setFont(widget.font, widget.font_size)
|
|
45
|
+
canvas.setFont(font_mapping.get(widget.font, widget.font), widget.font_size)
|
|
49
46
|
canvas.setFillColorRGB(
|
|
50
47
|
widget.font_color[0], widget.font_color[1], widget.font_color[2]
|
|
51
48
|
)
|
|
@@ -225,7 +222,9 @@ def draw_image(canvas: Canvas, **kwargs) -> None:
|
|
|
225
222
|
image_buff.close()
|
|
226
223
|
|
|
227
224
|
|
|
228
|
-
def create_watermarks_and_draw(
|
|
225
|
+
def create_watermarks_and_draw(
|
|
226
|
+
pdf: bytes, to_draw: List[dict], font_mapping: Optional[Dict[str, str]] = None
|
|
227
|
+
) -> List[bytes]:
|
|
229
228
|
"""
|
|
230
229
|
Creates a watermark PDF for each page of the input PDF based on the drawing instructions.
|
|
231
230
|
|
|
@@ -238,6 +237,8 @@ def create_watermarks_and_draw(pdf: bytes, to_draw: List[dict]) -> List[bytes]:
|
|
|
238
237
|
to_draw (List[dict]): A list of drawing instructions, where each dictionary
|
|
239
238
|
must contain a "page_number" key (1-based) and a "type" key ("image", "text", or "line")
|
|
240
239
|
along with type-specific parameters.
|
|
240
|
+
font_mapping (Optional[Dict[str, str]]): A dictionary mapping original font names
|
|
241
|
+
to temporary unique font names used by ReportLab.
|
|
241
242
|
|
|
242
243
|
Returns:
|
|
243
244
|
List[bytes]: A list of watermark PDF byte streams. An empty byte string (b"")
|
|
@@ -258,7 +259,7 @@ def create_watermarks_and_draw(pdf: bytes, to_draw: List[dict]) -> List[bytes]:
|
|
|
258
259
|
for each in to_draw:
|
|
259
260
|
page_to_to_draw[each["page_number"]].append(each)
|
|
260
261
|
|
|
261
|
-
pdf_file = PdfReader(
|
|
262
|
+
pdf_file = PdfReader(BytesIO(pdf))
|
|
262
263
|
buff = BytesIO()
|
|
263
264
|
|
|
264
265
|
for i, page in enumerate(pdf_file.pages):
|
|
@@ -279,7 +280,9 @@ def create_watermarks_and_draw(pdf: bytes, to_draw: List[dict]) -> List[bytes]:
|
|
|
279
280
|
)
|
|
280
281
|
|
|
281
282
|
for element in elements:
|
|
282
|
-
type_to_func[element["type"]](
|
|
283
|
+
type_to_func[element["type"]](
|
|
284
|
+
canvas, **element, font_mapping=font_mapping or {}
|
|
285
|
+
)
|
|
283
286
|
|
|
284
287
|
canvas.save()
|
|
285
288
|
buff.seek(0)
|
|
@@ -306,13 +309,13 @@ def merge_watermarks_with_pdf(
|
|
|
306
309
|
bytes: A byte stream representing the merged PDF with watermarks applied.
|
|
307
310
|
"""
|
|
308
311
|
result = BytesIO()
|
|
309
|
-
pdf_file = PdfReader(
|
|
312
|
+
pdf_file = PdfReader(BytesIO(pdf))
|
|
310
313
|
output = PdfWriter()
|
|
311
314
|
output.append(pdf_file)
|
|
312
315
|
|
|
313
316
|
for i, page in enumerate(output.pages):
|
|
314
317
|
if watermarks[i]:
|
|
315
|
-
watermark = PdfReader(
|
|
318
|
+
watermark = PdfReader(BytesIO(watermarks[i]))
|
|
316
319
|
if watermark.pages:
|
|
317
320
|
page.merge_page(watermark.pages[0])
|
|
318
321
|
|
|
@@ -321,26 +324,6 @@ def merge_watermarks_with_pdf(
|
|
|
321
324
|
return result.read()
|
|
322
325
|
|
|
323
326
|
|
|
324
|
-
@lru_cache
|
|
325
|
-
def get_watermark_with_font(font_name: str) -> bytes:
|
|
326
|
-
"""
|
|
327
|
-
Creates a watermark PDF with a single space character using the specified font.
|
|
328
|
-
|
|
329
|
-
This function is primarily used to generate a dummy PDF page that includes
|
|
330
|
-
a specific font, which can then be merged with another PDF to ensure the
|
|
331
|
-
font is available or embedded. The result is cached for performance.
|
|
332
|
-
|
|
333
|
-
Args:
|
|
334
|
-
font_name (str): The name of the font to use.
|
|
335
|
-
|
|
336
|
-
Returns:
|
|
337
|
-
bytes: The watermark PDF as a byte stream.
|
|
338
|
-
"""
|
|
339
|
-
return create_watermarks_and_draw(
|
|
340
|
-
BlankPage().read(), [RawText(" ", 1, 0, 0, font=font_name).to_draw]
|
|
341
|
-
)[0]
|
|
342
|
-
|
|
343
|
-
|
|
344
327
|
def _clone_page_widgets(
|
|
345
328
|
writer: PdfWriter,
|
|
346
329
|
page: PageObject,
|
|
@@ -384,7 +367,7 @@ def _collect_from_single_watermark_specific_page(
|
|
|
384
367
|
Dict[int, List[Any]]: A dictionary mapping the first output page (index 0) to cloned widgets.
|
|
385
368
|
"""
|
|
386
369
|
widgets_to_copy = defaultdict(list)
|
|
387
|
-
watermark_reader = PdfReader(
|
|
370
|
+
watermark_reader = PdfReader(BytesIO(watermark))
|
|
388
371
|
if page_num < len(watermark_reader.pages):
|
|
389
372
|
widgets_to_copy[0] = _clone_page_widgets(
|
|
390
373
|
writer, watermark_reader.pages[page_num], keys
|
|
@@ -409,7 +392,7 @@ def _collect_from_single_watermark_1_to_1(
|
|
|
409
392
|
Dict[int, List[Any]]: A dictionary mapping output page indices to cloned widgets.
|
|
410
393
|
"""
|
|
411
394
|
widgets_to_copy = defaultdict(list)
|
|
412
|
-
watermark_reader = PdfReader(
|
|
395
|
+
watermark_reader = PdfReader(BytesIO(watermark))
|
|
413
396
|
for i, page in enumerate(watermark_reader.pages):
|
|
414
397
|
widgets_to_copy[i] = _clone_page_widgets(writer, page, keys)
|
|
415
398
|
return widgets_to_copy
|
|
@@ -437,7 +420,7 @@ def _collect_from_multiple_watermarks(
|
|
|
437
420
|
for i, watermark_stream in enumerate(watermarks):
|
|
438
421
|
if not watermark_stream:
|
|
439
422
|
continue
|
|
440
|
-
watermark_reader = PdfReader(
|
|
423
|
+
watermark_reader = PdfReader(BytesIO(watermark_stream))
|
|
441
424
|
for j, page in enumerate(watermark_reader.pages):
|
|
442
425
|
if page_num is None or j == page_num:
|
|
443
426
|
widgets_to_copy[i].extend(_clone_page_widgets(writer, page, keys))
|
|
@@ -525,7 +508,7 @@ def copy_watermark_widgets(
|
|
|
525
508
|
bytes: The modified PDF byte stream with copied widgets.
|
|
526
509
|
"""
|
|
527
510
|
pdf_writer = PdfWriter()
|
|
528
|
-
pdf_writer.append(PdfReader(
|
|
511
|
+
pdf_writer.append(PdfReader(BytesIO(pdf)))
|
|
529
512
|
|
|
530
513
|
widgets_to_copy = _collect_widgets_to_copy(pdf_writer, watermarks, keys, page_num)
|
|
531
514
|
_apply_widgets_to_pages(pdf_writer, widgets_to_copy)
|
|
@@ -24,7 +24,6 @@ from reportlab.lib.colors import Color
|
|
|
24
24
|
from reportlab.pdfgen.canvas import Canvas
|
|
25
25
|
|
|
26
26
|
from ..constants import fieldFlags, required
|
|
27
|
-
from ..utils import stream_to_io
|
|
28
27
|
|
|
29
28
|
|
|
30
29
|
class Widget:
|
|
@@ -177,7 +176,7 @@ class Widget:
|
|
|
177
176
|
"""
|
|
178
177
|
result = []
|
|
179
178
|
|
|
180
|
-
pdf = PdfReader(
|
|
179
|
+
pdf = PdfReader(BytesIO(stream))
|
|
181
180
|
watermark = BytesIO()
|
|
182
181
|
|
|
183
182
|
widgets_by_page = {}
|
|
@@ -26,7 +26,6 @@ from reportlab.pdfgen.canvas import Canvas
|
|
|
26
26
|
from ..assets.bedrock import BEDROCK_PDF
|
|
27
27
|
from ..constants import Annots, Rect, T
|
|
28
28
|
from ..patterns import get_widget_key
|
|
29
|
-
from ..utils import stream_to_io
|
|
30
29
|
from .base import Field
|
|
31
30
|
|
|
32
31
|
|
|
@@ -34,10 +33,11 @@ class SignatureWidget:
|
|
|
34
33
|
"""
|
|
35
34
|
Represents a signature widget in a PDF form.
|
|
36
35
|
|
|
37
|
-
This class is responsible for handling the creation
|
|
38
|
-
|
|
39
|
-
the base Widget class
|
|
40
|
-
|
|
36
|
+
This class is responsible for handling the creation and integration of
|
|
37
|
+
signature fields in a PDF document. Unlike other widget types, it does not
|
|
38
|
+
inherit from the base Widget class — instead of using ReportLab's AcroForm
|
|
39
|
+
API, it copies a pre-built signature annotation from a bedrock PDF and
|
|
40
|
+
places it at the specified coordinates.
|
|
41
41
|
|
|
42
42
|
Attributes:
|
|
43
43
|
OPTIONAL_PARAMS (list): A list of tuples, where each tuple contains the
|
|
@@ -111,9 +111,9 @@ class SignatureWidget:
|
|
|
111
111
|
for widget in widgets:
|
|
112
112
|
page_to_widgets[widget.page_number].append(widget)
|
|
113
113
|
|
|
114
|
-
input_pdf = PdfReader(
|
|
114
|
+
input_pdf = PdfReader(BytesIO(stream))
|
|
115
115
|
|
|
116
|
-
bedrock = PdfReader(
|
|
116
|
+
bedrock = PdfReader(BytesIO(BEDROCK_PDF))
|
|
117
117
|
page = bedrock.pages[0]
|
|
118
118
|
annot_type_to_annot = {}
|
|
119
119
|
for annot in page.get(Annots, []): # pylint: disable=E1101
|
|
@@ -31,8 +31,8 @@ from .constants import VERSION_IDENTIFIER_PREFIX, VERSION_IDENTIFIERS
|
|
|
31
31
|
from .coordinate import generate_coordinate_grid
|
|
32
32
|
from .egress import appearance_streams_handler, preserve_pdf_properties
|
|
33
33
|
from .filler import fill
|
|
34
|
-
from .font import (get_all_available_fonts,
|
|
35
|
-
|
|
34
|
+
from .font import (get_all_available_fonts, register_font_acroform,
|
|
35
|
+
temporary_font_registration, validate_font)
|
|
36
36
|
from .hooks import trigger_widget_hooks
|
|
37
37
|
from .middleware.dropdown import Dropdown
|
|
38
38
|
from .middleware.signature import Signature
|
|
@@ -765,9 +765,10 @@ class PdfWrapper:
|
|
|
765
765
|
PdfWrapper: The `PdfWrapper` object, allowing for method chaining.
|
|
766
766
|
"""
|
|
767
767
|
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
768
|
+
with temporary_font_registration(self._font_register_events) as font_mapping:
|
|
769
|
+
watermarks = create_watermarks_and_draw(
|
|
770
|
+
self._read(), [each.to_draw for each in elements], font_mapping
|
|
771
|
+
)
|
|
771
772
|
|
|
772
773
|
stream_with_widgets = self._read()
|
|
773
774
|
self._stream = merge_watermarks_with_pdf(self._read(), watermarks)
|
|
@@ -801,9 +802,9 @@ class PdfWrapper:
|
|
|
801
802
|
|
|
802
803
|
ttf_file = fp_or_f_obj_or_stream_to_stream(ttf_file)
|
|
803
804
|
|
|
804
|
-
if
|
|
805
|
+
if validate_font(font_name, ttf_file) if ttf_file is not None else False:
|
|
805
806
|
self._stream, new_font_name = register_font_acroform(
|
|
806
|
-
self._read(),
|
|
807
|
+
self._read(), ttf_file, getattr(self, "need_appearances")
|
|
807
808
|
)
|
|
808
809
|
self._available_fonts[font_name] = new_font_name
|
|
809
810
|
self._font_register_events.append((font_name, ttf_file))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyPDFForm
|
|
3
|
-
Version: 4.7.
|
|
3
|
+
Version: 4.7.5
|
|
4
4
|
Summary: The Python library for PDF forms.
|
|
5
5
|
Author: Jinge Li
|
|
6
6
|
License-Expression: MIT
|
|
@@ -24,7 +24,7 @@ Requires-Dist: cryptography<47.0.0,>=46.0.3
|
|
|
24
24
|
Requires-Dist: fonttools<5.0.0,>=4.60.1
|
|
25
25
|
Requires-Dist: pikepdf<11.0.0,>=10.5.0
|
|
26
26
|
Requires-Dist: pillow<13.0.0,>=12.0.0
|
|
27
|
-
Requires-Dist: pypdf<7.0.0,>=6.
|
|
27
|
+
Requires-Dist: pypdf<7.0.0,>=6.9.0
|
|
28
28
|
Requires-Dist: reportlab<5.0.0,>=4.4.6
|
|
29
29
|
Provides-Extra: dev
|
|
30
30
|
Requires-Dist: black<27.0.0,>=25.11.0; extra == "dev"
|
|
@@ -31,7 +31,7 @@ dependencies = [
|
|
|
31
31
|
"fonttools>=4.60.1,<5.0.0",
|
|
32
32
|
"pikepdf>=10.5.0,<11.0.0",
|
|
33
33
|
"pillow>=12.0.0,<13.0.0",
|
|
34
|
-
"pypdf>=6.
|
|
34
|
+
"pypdf>=6.9.0,<7.0.0",
|
|
35
35
|
"reportlab>=4.4.6,<5.0.0",
|
|
36
36
|
]
|
|
37
37
|
|
|
@@ -136,5 +136,5 @@ include = ["PyPDFForm*"]
|
|
|
136
136
|
|
|
137
137
|
[tool.pytest.ini_options]
|
|
138
138
|
markers = [
|
|
139
|
-
"posix_only",
|
|
139
|
+
"posix_only", # mainly because of zlib vs zlib-ng
|
|
140
140
|
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|