PyPDFForm 4.5.2__tar.gz → 4.6.0__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.5.2 → pypdfform-4.6.0}/PKG-INFO +1 -1
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/__init__.py +1 -1
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/annotations/__init__.py +17 -2
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/annotations/base.py +14 -0
- pypdfform-4.6.0/PyPDFForm/annotations/link.py +57 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/annotations/text.py +18 -6
- pypdfform-4.6.0/PyPDFForm/annotations/text_markup.py +82 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/raw/__init__.py +1 -1
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/template.py +1 -4
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/widgets/__init__.py +1 -1
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm.egg-info/PKG-INFO +1 -1
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm.egg-info/SOURCES.txt +2 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/pyproject.toml +1 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/tests/test_functional.py +12 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/tests/test_generate_appearance_streams.py +8 -93
- {pypdfform-4.5.2 → pypdfform-4.6.0}/tests/test_need_appearances.py +119 -88
- {pypdfform-4.5.2 → pypdfform-4.6.0}/LICENSE +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/adapter.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/ap.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/assets/__init__.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/assets/bedrock.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/assets/blank.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/constants.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/coordinate.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/deprecation.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/filler.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/font.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/hooks.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/image.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/middleware/__init__.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/middleware/base.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/middleware/checkbox.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/middleware/dropdown.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/middleware/image.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/middleware/radio.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/middleware/signature.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/middleware/text.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/patterns.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/raw/circle.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/raw/ellipse.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/raw/image.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/raw/line.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/raw/rect.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/raw/text.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/types.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/utils.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/watermark.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/widgets/base.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/widgets/checkbox.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/widgets/dropdown.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/widgets/image.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/widgets/radio.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/widgets/signature.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/widgets/text.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm/wrapper.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm.egg-info/dependency_links.txt +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm.egg-info/requires.txt +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/PyPDFForm.egg-info/top_level.txt +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/README.md +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/setup.cfg +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/tests/test_bulk_create_fields.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/tests/test_create_widget.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/tests/test_draw_elements.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/tests/test_dropdown.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/tests/test_extract_middleware_attributes.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/tests/test_fill_max_length_text_field.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/tests/test_font_widths.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/tests/test_js.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/tests/test_paragraph.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/tests/test_signature.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/tests/test_use_full_widget_name.py +0 -0
- {pypdfform-4.5.2 → pypdfform-4.6.0}/tests/test_widget_attr_trigger.py +0 -0
|
@@ -20,7 +20,7 @@ The library supports various PDF form features, including:
|
|
|
20
20
|
PyPDFForm aims to simplify PDF form manipulation, making it accessible to developers of all skill levels.
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
|
-
__version__ = "4.
|
|
23
|
+
__version__ = "4.6.0"
|
|
24
24
|
|
|
25
25
|
from .annotations import Annotations
|
|
26
26
|
from .assets.blank import BlankPage
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"""
|
|
3
3
|
The `annotations` package provides classes representing various types of PDF annotations.
|
|
4
4
|
|
|
5
|
-
It defines `AnnotationTypes` as a
|
|
5
|
+
It defines `AnnotationTypes` as a collection of all supported annotation types, allowing for
|
|
6
6
|
flexible type hinting when working with different annotation configurations.
|
|
7
7
|
|
|
8
8
|
Classes within this package encapsulate the properties and behaviors of individual
|
|
@@ -11,9 +11,19 @@ annotations, facilitating their creation and manipulation within PDF documents.
|
|
|
11
11
|
|
|
12
12
|
from dataclasses import dataclass
|
|
13
13
|
|
|
14
|
+
from .link import LinkAnnotation
|
|
14
15
|
from .text import TextAnnotation
|
|
16
|
+
from .text_markup import (HighlightAnnotation, SquigglyAnnotation,
|
|
17
|
+
StrikeOutAnnotation, UnderlineAnnotation)
|
|
15
18
|
|
|
16
|
-
AnnotationTypes =
|
|
19
|
+
AnnotationTypes = (
|
|
20
|
+
TextAnnotation
|
|
21
|
+
| LinkAnnotation
|
|
22
|
+
| HighlightAnnotation
|
|
23
|
+
| UnderlineAnnotation
|
|
24
|
+
| SquigglyAnnotation
|
|
25
|
+
| StrikeOutAnnotation
|
|
26
|
+
)
|
|
17
27
|
|
|
18
28
|
|
|
19
29
|
@dataclass
|
|
@@ -26,3 +36,8 @@ class Annotations:
|
|
|
26
36
|
"""
|
|
27
37
|
|
|
28
38
|
TextAnnotation = TextAnnotation
|
|
39
|
+
LinkAnnotation = LinkAnnotation
|
|
40
|
+
HighlightAnnotation = HighlightAnnotation
|
|
41
|
+
UnderlineAnnotation = UnderlineAnnotation
|
|
42
|
+
SquigglyAnnotation = SquigglyAnnotation
|
|
43
|
+
StrikeOutAnnotation = StrikeOutAnnotation
|
|
@@ -36,3 +36,17 @@ class Annotation:
|
|
|
36
36
|
width: float = 20
|
|
37
37
|
height: float = 20
|
|
38
38
|
contents: str = ""
|
|
39
|
+
|
|
40
|
+
def get_specific_properties(self) -> dict:
|
|
41
|
+
"""
|
|
42
|
+
Gets properties specific to the annotation type.
|
|
43
|
+
|
|
44
|
+
This method should be implemented by subclasses to return a dictionary
|
|
45
|
+
containing PDF properties and their values that are unique to that
|
|
46
|
+
specific type of annotation. These properties are used when creating
|
|
47
|
+
the annotation's entry in the PDF document.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
dict: A dictionary of PDF properties specific to the annotation type.
|
|
51
|
+
"""
|
|
52
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# TODO: finish other features
|
|
3
|
+
"""
|
|
4
|
+
This module defines the class for link annotations in PyPDFForm.
|
|
5
|
+
|
|
6
|
+
It provides a structure for representing and interacting with PDF link
|
|
7
|
+
annotations, which allow users to click and navigate to a URI.
|
|
8
|
+
|
|
9
|
+
Classes:
|
|
10
|
+
- `LinkAnnotation`: A dataclass representing the properties of a PDF link annotation.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
from typing import Optional
|
|
15
|
+
|
|
16
|
+
from pypdf.generic import DictionaryObject, NameObject, TextStringObject
|
|
17
|
+
|
|
18
|
+
from ..constants import A, S
|
|
19
|
+
from .base import Annotation
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class LinkAnnotation(Annotation):
|
|
24
|
+
"""
|
|
25
|
+
A dataclass representing the properties of a PDF link annotation.
|
|
26
|
+
|
|
27
|
+
This class extends the `Annotation` base class to specifically handle
|
|
28
|
+
link annotations, including the target URI.
|
|
29
|
+
|
|
30
|
+
Attributes:
|
|
31
|
+
uri (str): The URI that the link annotation points to. Defaults to None.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
_annotation_type: str = "/Link"
|
|
35
|
+
|
|
36
|
+
uri: Optional[str] = None
|
|
37
|
+
|
|
38
|
+
def get_specific_properties(self) -> dict:
|
|
39
|
+
"""
|
|
40
|
+
Gets properties specific to the link annotation type.
|
|
41
|
+
|
|
42
|
+
This method returns a dictionary containing PDF properties and their
|
|
43
|
+
values that are unique to a link annotation, perforated with the URI action.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
dict: A dictionary of PDF properties specific to the link annotation.
|
|
47
|
+
"""
|
|
48
|
+
result = {}
|
|
49
|
+
if self.uri is not None:
|
|
50
|
+
result[NameObject(A)] = DictionaryObject(
|
|
51
|
+
{
|
|
52
|
+
NameObject(S): NameObject("/URI"),
|
|
53
|
+
NameObject("/URI"): TextStringObject(self.uri),
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
return result
|
|
@@ -27,8 +27,6 @@ class TextAnnotation(Annotation):
|
|
|
27
27
|
|
|
28
28
|
Attributes:
|
|
29
29
|
_annotation_type (str): The PDF internal type of the annotation, which is "/Text".
|
|
30
|
-
_additional_properties (tuple): A tuple defining how specific attributes are mapped
|
|
31
|
-
to PDF dictionary keys for the annotation.
|
|
32
30
|
note_icon (str): The identifier for a "Note" icon.
|
|
33
31
|
comment_icon (str): The identifier for a "Comment" icon.
|
|
34
32
|
help_icon (str): The identifier for a "Help" icon.
|
|
@@ -39,10 +37,6 @@ class TextAnnotation(Annotation):
|
|
|
39
37
|
"""
|
|
40
38
|
|
|
41
39
|
_annotation_type: str = "/Text"
|
|
42
|
-
_additional_properties: tuple = (
|
|
43
|
-
(NameObject(T), (TextStringObject, "title")),
|
|
44
|
-
(NameObject("/Name"), (NameObject, "icon")),
|
|
45
|
-
)
|
|
46
40
|
|
|
47
41
|
note_icon: str = "/Note"
|
|
48
42
|
comment_icon: str = "/Comment"
|
|
@@ -52,3 +46,21 @@ class TextAnnotation(Annotation):
|
|
|
52
46
|
|
|
53
47
|
title: Optional[str] = None
|
|
54
48
|
icon: Optional[str] = None
|
|
49
|
+
|
|
50
|
+
def get_specific_properties(self) -> dict:
|
|
51
|
+
"""
|
|
52
|
+
Gets properties specific to the text annotation.
|
|
53
|
+
|
|
54
|
+
This method includes the title (author) and the icon name for the
|
|
55
|
+
text annotation if they are provided.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
dict: A dictionary of PDF properties specific to the text annotation.
|
|
59
|
+
"""
|
|
60
|
+
result = {}
|
|
61
|
+
if self.title is not None:
|
|
62
|
+
result[NameObject(T)] = TextStringObject(self.title)
|
|
63
|
+
if self.icon is not None:
|
|
64
|
+
result[NameObject("/Name")] = NameObject(self.icon)
|
|
65
|
+
|
|
66
|
+
return result
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
This module defines the `TextMarkupAnnotation` class and its subclasses,
|
|
4
|
+
which are used to represent text markup annotations in a PDF document.
|
|
5
|
+
|
|
6
|
+
Classes:
|
|
7
|
+
- `TextMarkupAnnotation`: Base class for all text markup annotations.
|
|
8
|
+
- `HighlightAnnotation`: Represents a highlight annotation.
|
|
9
|
+
- `UnderlineAnnotation`: Represents an underline annotation.
|
|
10
|
+
- `SquigglyAnnotation`: Represents a squiggly (curly) underline annotation.
|
|
11
|
+
- `StrikeOutAnnotation`: Represents a strikeout annotation.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from dataclasses import dataclass
|
|
15
|
+
|
|
16
|
+
from pypdf.generic import ArrayObject, FloatObject, NameObject
|
|
17
|
+
|
|
18
|
+
from .base import Annotation
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class TextMarkupAnnotation(Annotation):
|
|
23
|
+
"""
|
|
24
|
+
Base dataclass for all text markup annotations.
|
|
25
|
+
|
|
26
|
+
Text markup annotations appear as marks on the text of a document,
|
|
27
|
+
such as highlights, underlines, etc. They are defined by a set of
|
|
28
|
+
quadrilateral points (QuadPoints) that encompass the text being marked.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def get_specific_properties(self) -> dict:
|
|
32
|
+
"""
|
|
33
|
+
Gets properties specific to the text markup annotation.
|
|
34
|
+
|
|
35
|
+
This method calculates the `QuadPoints` for the markup based on the
|
|
36
|
+
annotation's position and dimensions.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
dict: A dictionary containing the `/QuadPoints` property.
|
|
40
|
+
"""
|
|
41
|
+
return {
|
|
42
|
+
NameObject("/QuadPoints"): ArrayObject(
|
|
43
|
+
[
|
|
44
|
+
FloatObject(self.x),
|
|
45
|
+
FloatObject(self.y),
|
|
46
|
+
FloatObject(self.x + self.width),
|
|
47
|
+
FloatObject(self.y),
|
|
48
|
+
FloatObject(self.x),
|
|
49
|
+
FloatObject(self.y + self.height),
|
|
50
|
+
FloatObject(self.x + self.width),
|
|
51
|
+
FloatObject(self.y + self.height),
|
|
52
|
+
]
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class HighlightAnnotation(TextMarkupAnnotation):
|
|
59
|
+
"""Represents a highlight annotation."""
|
|
60
|
+
|
|
61
|
+
_annotation_type: str = "/Highlight"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclass
|
|
65
|
+
class UnderlineAnnotation(TextMarkupAnnotation):
|
|
66
|
+
"""Represents an underline annotation."""
|
|
67
|
+
|
|
68
|
+
_annotation_type: str = "/Underline"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass
|
|
72
|
+
class SquigglyAnnotation(TextMarkupAnnotation):
|
|
73
|
+
"""Represents a squiggly (curly) underline annotation."""
|
|
74
|
+
|
|
75
|
+
_annotation_type: str = "/Squiggly"
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@dataclass
|
|
79
|
+
class StrikeOutAnnotation(TextMarkupAnnotation):
|
|
80
|
+
"""Represents a strikeout annotation."""
|
|
81
|
+
|
|
82
|
+
_annotation_type: str = "/StrikeOut"
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
The `raw` package provides classes representing raw drawable elements
|
|
4
4
|
(like text and images) that can be rendered directly onto a PDF document.
|
|
5
5
|
|
|
6
|
-
It defines `RawTypes` as a
|
|
6
|
+
It defines `RawTypes` as a collection of all supported raw element types, used for
|
|
7
7
|
type hinting in methods that handle drawing onto the PDF.
|
|
8
8
|
"""
|
|
9
9
|
|
|
@@ -358,10 +358,7 @@ def _create_annotation_object(annotation: AnnotationTypes) -> DictionaryObject:
|
|
|
358
358
|
NameObject(Contents): TextStringObject(annotation.contents),
|
|
359
359
|
}
|
|
360
360
|
)
|
|
361
|
-
|
|
362
|
-
val = getattr(annotation, v[1])
|
|
363
|
-
if val is not None:
|
|
364
|
-
annot[k] = v[0](val)
|
|
361
|
+
annot.update(**annotation.get_specific_properties())
|
|
365
362
|
return annot
|
|
366
363
|
|
|
367
364
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"""
|
|
3
3
|
The `widgets` package provides a collection of classes representing various types of PDF form fields (widgets).
|
|
4
4
|
|
|
5
|
-
It defines `FieldTypes` as a
|
|
5
|
+
It defines `FieldTypes` as a collection of all supported field types, allowing for flexible
|
|
6
6
|
type hinting when working with different widget configurations.
|
|
7
7
|
|
|
8
8
|
Classes within this package encapsulate the properties and behaviors of individual
|
|
@@ -24,7 +24,9 @@ PyPDFForm.egg-info/requires.txt
|
|
|
24
24
|
PyPDFForm.egg-info/top_level.txt
|
|
25
25
|
PyPDFForm/annotations/__init__.py
|
|
26
26
|
PyPDFForm/annotations/base.py
|
|
27
|
+
PyPDFForm/annotations/link.py
|
|
27
28
|
PyPDFForm/annotations/text.py
|
|
29
|
+
PyPDFForm/annotations/text_markup.py
|
|
28
30
|
PyPDFForm/assets/__init__.py
|
|
29
31
|
PyPDFForm/assets/bedrock.py
|
|
30
32
|
PyPDFForm/assets/blank.py
|
|
@@ -7,6 +7,7 @@ import pytest
|
|
|
7
7
|
from jsonschema import ValidationError, validate
|
|
8
8
|
|
|
9
9
|
from PyPDFForm import Annotations, BlankPage, Fields, PdfArray, PdfWrapper
|
|
10
|
+
from PyPDFForm.annotations.base import Annotation
|
|
10
11
|
from PyPDFForm.constants import DA, UNIQUE_SUFFIX_LENGTH, T, V
|
|
11
12
|
from PyPDFForm.deprecation import deprecation_notice
|
|
12
13
|
from PyPDFForm.middleware.base import Widget
|
|
@@ -788,6 +789,17 @@ def test_merge(template_stream):
|
|
|
788
789
|
assert field[V] == f"test_3_{int(page / 3 - 0.5)}"
|
|
789
790
|
|
|
790
791
|
|
|
792
|
+
def test_base_annotation_get_specific_properties_not_implemented():
|
|
793
|
+
annotation = Annotation(1, 100, 100)
|
|
794
|
+
try:
|
|
795
|
+
annotation.get_specific_properties()
|
|
796
|
+
pytest.fail(
|
|
797
|
+
reason="base annotation shouldn't have get_specific_properties implemented."
|
|
798
|
+
)
|
|
799
|
+
except NotImplementedError:
|
|
800
|
+
pass
|
|
801
|
+
|
|
802
|
+
|
|
791
803
|
def test_annotate(template_stream, pdf_samples, request):
|
|
792
804
|
expected_path = os.path.join(pdf_samples, "test_annotate.pdf")
|
|
793
805
|
with open(expected_path, "rb+") as f:
|
|
@@ -5,7 +5,8 @@ import os
|
|
|
5
5
|
|
|
6
6
|
import pytest
|
|
7
7
|
|
|
8
|
-
from PyPDFForm import
|
|
8
|
+
from PyPDFForm import PdfWrapper
|
|
9
|
+
from tests.test_need_appearances import run_sample_template_library_test
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
def test_fill(template_stream, pdf_samples, data_dict, request):
|
|
@@ -106,96 +107,10 @@ def test_issue_613(pdf_samples, request):
|
|
|
106
107
|
def test_sample_template_library(
|
|
107
108
|
pdf_samples, image_samples, sample_font_stream, request
|
|
108
109
|
):
|
|
109
|
-
|
|
110
|
-
pdf_samples,
|
|
110
|
+
run_sample_template_library_test(
|
|
111
|
+
pdf_samples,
|
|
112
|
+
image_samples,
|
|
113
|
+
sample_font_stream,
|
|
114
|
+
request,
|
|
115
|
+
generate_appearance_streams=True,
|
|
111
116
|
)
|
|
112
|
-
|
|
113
|
-
with open(expected_path, "rb+") as f:
|
|
114
|
-
obj = (
|
|
115
|
-
PdfWrapper(
|
|
116
|
-
os.path.join(pdf_samples, "dummy.pdf"), generate_appearance_streams=True
|
|
117
|
-
)
|
|
118
|
-
.register_font("new_font", sample_font_stream)
|
|
119
|
-
.create_field(
|
|
120
|
-
Fields.TextField(
|
|
121
|
-
name="new_text_field_widget",
|
|
122
|
-
page_number=1,
|
|
123
|
-
x=60,
|
|
124
|
-
y=710,
|
|
125
|
-
)
|
|
126
|
-
)
|
|
127
|
-
.create_field(
|
|
128
|
-
Fields.CheckBoxField(
|
|
129
|
-
name="new_checkbox_widget",
|
|
130
|
-
page_number=1,
|
|
131
|
-
x=100,
|
|
132
|
-
y=600,
|
|
133
|
-
)
|
|
134
|
-
)
|
|
135
|
-
.create_field(
|
|
136
|
-
Fields.RadioGroup(
|
|
137
|
-
name="new_radio_group",
|
|
138
|
-
page_number=1,
|
|
139
|
-
x=[50, 100, 150],
|
|
140
|
-
y=[50, 100, 150],
|
|
141
|
-
)
|
|
142
|
-
)
|
|
143
|
-
.create_field(
|
|
144
|
-
Fields.DropdownField(
|
|
145
|
-
name="new_dropdown_widget",
|
|
146
|
-
page_number=1,
|
|
147
|
-
x=300,
|
|
148
|
-
y=710,
|
|
149
|
-
options=[
|
|
150
|
-
"foo",
|
|
151
|
-
"bar",
|
|
152
|
-
"foobar",
|
|
153
|
-
],
|
|
154
|
-
font="new_font",
|
|
155
|
-
)
|
|
156
|
-
)
|
|
157
|
-
.create_field(
|
|
158
|
-
Fields.ImageField(
|
|
159
|
-
name="new_image_widget",
|
|
160
|
-
page_number=1,
|
|
161
|
-
x=300,
|
|
162
|
-
y=200,
|
|
163
|
-
)
|
|
164
|
-
)
|
|
165
|
-
.create_field(
|
|
166
|
-
Fields.SignatureField(
|
|
167
|
-
name="new_signature_wiget",
|
|
168
|
-
page_number=1,
|
|
169
|
-
x=300,
|
|
170
|
-
y=400,
|
|
171
|
-
)
|
|
172
|
-
)
|
|
173
|
-
.fill(
|
|
174
|
-
{
|
|
175
|
-
"new_text_field_widget": "test text",
|
|
176
|
-
"new_checkbox_widget": True,
|
|
177
|
-
"new_radio_group": 1,
|
|
178
|
-
"new_dropdown_widget": "barfoo",
|
|
179
|
-
"new_image_widget": os.path.join(image_samples, "sample_image.jpg"),
|
|
180
|
-
"new_signature_wiget": os.path.join(
|
|
181
|
-
image_samples, "sample_signature.png"
|
|
182
|
-
),
|
|
183
|
-
},
|
|
184
|
-
)
|
|
185
|
-
)
|
|
186
|
-
|
|
187
|
-
obj.widgets["new_text_field_widget"].font = "new_font"
|
|
188
|
-
obj.widgets["new_text_field_widget"].font_color = (1, 0, 0)
|
|
189
|
-
obj.widgets["new_text_field_widget"].alignment = (
|
|
190
|
-
2 # TODO: why is alignment not rendered right in the appearance stream?
|
|
191
|
-
)
|
|
192
|
-
obj.widgets["new_checkbox_widget"].size = 40
|
|
193
|
-
obj.widgets["new_radio_group"].size = 50
|
|
194
|
-
|
|
195
|
-
request.config.results["expected_path"] = expected_path
|
|
196
|
-
request.config.results["stream"] = obj.read()
|
|
197
|
-
|
|
198
|
-
expected = f.read()
|
|
199
|
-
|
|
200
|
-
assert len(obj.read()) == len(expected)
|
|
201
|
-
request.config.results["skip_regenerate"] = len(obj.read()) == len(expected)
|
|
@@ -7,6 +7,119 @@ import pytest
|
|
|
7
7
|
from PyPDFForm import Fields, PdfWrapper
|
|
8
8
|
|
|
9
9
|
|
|
10
|
+
def run_sample_template_library_test(
|
|
11
|
+
pdf_samples,
|
|
12
|
+
image_samples,
|
|
13
|
+
sample_font_stream,
|
|
14
|
+
request,
|
|
15
|
+
generate_appearance_streams=False,
|
|
16
|
+
need_appearances=False,
|
|
17
|
+
):
|
|
18
|
+
subdir = (
|
|
19
|
+
"generate_appearance_streams"
|
|
20
|
+
if generate_appearance_streams
|
|
21
|
+
else "need_appearances"
|
|
22
|
+
)
|
|
23
|
+
expected_path = os.path.join(
|
|
24
|
+
pdf_samples, subdir, "test_sample_template_library.pdf"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
with open(expected_path, "rb+") as f:
|
|
28
|
+
obj = (
|
|
29
|
+
PdfWrapper(
|
|
30
|
+
os.path.join(pdf_samples, "dummy.pdf"),
|
|
31
|
+
generate_appearance_streams=generate_appearance_streams,
|
|
32
|
+
need_appearances=need_appearances,
|
|
33
|
+
)
|
|
34
|
+
.register_font("new_font", sample_font_stream)
|
|
35
|
+
.create_field(
|
|
36
|
+
Fields.TextField(
|
|
37
|
+
name="new_text_field_widget",
|
|
38
|
+
page_number=1,
|
|
39
|
+
x=60,
|
|
40
|
+
y=710,
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
.create_field(
|
|
44
|
+
Fields.CheckBoxField(
|
|
45
|
+
name="new_checkbox_widget",
|
|
46
|
+
page_number=1,
|
|
47
|
+
x=100,
|
|
48
|
+
y=600,
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
.create_field(
|
|
52
|
+
Fields.RadioGroup(
|
|
53
|
+
name="new_radio_group",
|
|
54
|
+
page_number=1,
|
|
55
|
+
x=[50, 100, 150],
|
|
56
|
+
y=[50, 100, 150],
|
|
57
|
+
)
|
|
58
|
+
)
|
|
59
|
+
.create_field(
|
|
60
|
+
Fields.DropdownField(
|
|
61
|
+
name="new_dropdown_widget",
|
|
62
|
+
page_number=1,
|
|
63
|
+
x=300,
|
|
64
|
+
y=710,
|
|
65
|
+
options=[
|
|
66
|
+
"foo",
|
|
67
|
+
"bar",
|
|
68
|
+
"foobar",
|
|
69
|
+
],
|
|
70
|
+
font="new_font",
|
|
71
|
+
)
|
|
72
|
+
)
|
|
73
|
+
.create_field(
|
|
74
|
+
Fields.ImageField(
|
|
75
|
+
name="new_image_widget",
|
|
76
|
+
page_number=1,
|
|
77
|
+
x=300,
|
|
78
|
+
y=200,
|
|
79
|
+
)
|
|
80
|
+
)
|
|
81
|
+
.create_field(
|
|
82
|
+
Fields.SignatureField(
|
|
83
|
+
name="new_signature_wiget",
|
|
84
|
+
page_number=1,
|
|
85
|
+
x=300,
|
|
86
|
+
y=400,
|
|
87
|
+
)
|
|
88
|
+
)
|
|
89
|
+
.fill(
|
|
90
|
+
{
|
|
91
|
+
"new_text_field_widget": "test text",
|
|
92
|
+
"new_checkbox_widget": True,
|
|
93
|
+
"new_radio_group": 1,
|
|
94
|
+
"new_dropdown_widget": "barfoo",
|
|
95
|
+
"new_image_widget": os.path.join(image_samples, "sample_image.jpg"),
|
|
96
|
+
"new_signature_wiget": os.path.join(
|
|
97
|
+
image_samples, "sample_signature.png"
|
|
98
|
+
),
|
|
99
|
+
},
|
|
100
|
+
)
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
obj.widgets["new_text_field_widget"].font = "new_font"
|
|
104
|
+
obj.widgets["new_text_field_widget"].font_color = (1, 0, 0)
|
|
105
|
+
# TODO: why is alignment not rendered right in the appearance stream?
|
|
106
|
+
obj.widgets["new_text_field_widget"].alignment = 2
|
|
107
|
+
|
|
108
|
+
obj.widgets["new_checkbox_widget"].size = 40
|
|
109
|
+
obj.widgets["new_radio_group"].size = 50
|
|
110
|
+
|
|
111
|
+
request.config.results["expected_path"] = expected_path
|
|
112
|
+
request.config.results["stream"] = obj.read()
|
|
113
|
+
|
|
114
|
+
expected = f.read()
|
|
115
|
+
|
|
116
|
+
assert len(obj.read()) == len(expected)
|
|
117
|
+
if generate_appearance_streams:
|
|
118
|
+
request.config.results["skip_regenerate"] = len(obj.read()) == len(expected)
|
|
119
|
+
else:
|
|
120
|
+
assert obj.read() == expected
|
|
121
|
+
|
|
122
|
+
|
|
10
123
|
def test_fill(template_stream, pdf_samples, data_dict, request):
|
|
11
124
|
expected_path = os.path.join(pdf_samples, "need_appearances", "sample_filled.pdf")
|
|
12
125
|
with open(expected_path, "rb+") as f:
|
|
@@ -98,92 +211,10 @@ def test_issue_613(pdf_samples, request):
|
|
|
98
211
|
def test_sample_template_library(
|
|
99
212
|
pdf_samples, image_samples, sample_font_stream, request
|
|
100
213
|
):
|
|
101
|
-
|
|
102
|
-
pdf_samples,
|
|
214
|
+
run_sample_template_library_test(
|
|
215
|
+
pdf_samples,
|
|
216
|
+
image_samples,
|
|
217
|
+
sample_font_stream,
|
|
218
|
+
request,
|
|
219
|
+
need_appearances=True,
|
|
103
220
|
)
|
|
104
|
-
|
|
105
|
-
with open(expected_path, "rb+") as f:
|
|
106
|
-
obj = (
|
|
107
|
-
PdfWrapper(os.path.join(pdf_samples, "dummy.pdf"), need_appearances=True)
|
|
108
|
-
.register_font("new_font", sample_font_stream)
|
|
109
|
-
.create_field(
|
|
110
|
-
Fields.TextField(
|
|
111
|
-
name="new_text_field_widget",
|
|
112
|
-
page_number=1,
|
|
113
|
-
x=60,
|
|
114
|
-
y=710,
|
|
115
|
-
)
|
|
116
|
-
)
|
|
117
|
-
.create_field(
|
|
118
|
-
Fields.CheckBoxField(
|
|
119
|
-
name="new_checkbox_widget",
|
|
120
|
-
page_number=1,
|
|
121
|
-
x=100,
|
|
122
|
-
y=600,
|
|
123
|
-
)
|
|
124
|
-
)
|
|
125
|
-
.create_field(
|
|
126
|
-
Fields.RadioGroup(
|
|
127
|
-
name="new_radio_group",
|
|
128
|
-
page_number=1,
|
|
129
|
-
x=[50, 100, 150],
|
|
130
|
-
y=[50, 100, 150],
|
|
131
|
-
)
|
|
132
|
-
)
|
|
133
|
-
.create_field(
|
|
134
|
-
Fields.DropdownField(
|
|
135
|
-
name="new_dropdown_widget",
|
|
136
|
-
page_number=1,
|
|
137
|
-
x=300,
|
|
138
|
-
y=710,
|
|
139
|
-
options=[
|
|
140
|
-
"foo",
|
|
141
|
-
"bar",
|
|
142
|
-
"foobar",
|
|
143
|
-
],
|
|
144
|
-
font="new_font",
|
|
145
|
-
)
|
|
146
|
-
)
|
|
147
|
-
.create_field(
|
|
148
|
-
Fields.ImageField(
|
|
149
|
-
name="new_image_widget",
|
|
150
|
-
page_number=1,
|
|
151
|
-
x=300,
|
|
152
|
-
y=200,
|
|
153
|
-
)
|
|
154
|
-
)
|
|
155
|
-
.create_field(
|
|
156
|
-
Fields.SignatureField(
|
|
157
|
-
name="new_signature_wiget",
|
|
158
|
-
page_number=1,
|
|
159
|
-
x=300,
|
|
160
|
-
y=400,
|
|
161
|
-
)
|
|
162
|
-
)
|
|
163
|
-
.fill(
|
|
164
|
-
{
|
|
165
|
-
"new_text_field_widget": "test text",
|
|
166
|
-
"new_checkbox_widget": True,
|
|
167
|
-
"new_radio_group": 1,
|
|
168
|
-
"new_dropdown_widget": "barfoo",
|
|
169
|
-
"new_image_widget": os.path.join(image_samples, "sample_image.jpg"),
|
|
170
|
-
"new_signature_wiget": os.path.join(
|
|
171
|
-
image_samples, "sample_signature.png"
|
|
172
|
-
),
|
|
173
|
-
},
|
|
174
|
-
)
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
obj.widgets["new_text_field_widget"].font = "new_font"
|
|
178
|
-
obj.widgets["new_text_field_widget"].font_color = (1, 0, 0)
|
|
179
|
-
obj.widgets["new_text_field_widget"].alignment = 2
|
|
180
|
-
obj.widgets["new_checkbox_widget"].size = 40
|
|
181
|
-
obj.widgets["new_radio_group"].size = 50
|
|
182
|
-
|
|
183
|
-
request.config.results["expected_path"] = expected_path
|
|
184
|
-
request.config.results["stream"] = obj.read()
|
|
185
|
-
|
|
186
|
-
expected = f.read()
|
|
187
|
-
|
|
188
|
-
assert len(obj.read()) == len(expected)
|
|
189
|
-
assert obj.read() == expected
|
|
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
|