pdfdancer-client-python 0.3.6__tar.gz → 0.3.8__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.
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/PKG-INFO +12 -4
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/README.md +11 -3
- pdfdancer_client_python-0.3.8/media/logo-silver-512h.webp +0 -0
- pdfdancer_client_python-0.3.8/media/logo-silver-60h.webp +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/pyproject.toml +1 -1
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/src/pdfdancer/__init__.py +0 -2
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/src/pdfdancer/models.py +15 -9
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/src/pdfdancer/pdfdancer_v1.py +40 -16
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/src/pdfdancer_client_python.egg-info/PKG-INFO +12 -4
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/src/pdfdancer_client_python.egg-info/SOURCES.txt +2 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_line.py +3 -1
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_template_replace.py +75 -33
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/.claude/commands/discuss.md +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/.claude/commands/implement-new-api-features.md +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/.flake8 +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/.github/workflows/ci.yml +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/.github/workflows/daily-tests.yml +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/.gitignore +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/CLAUDE.md +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/LICENSE +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/NOTICE +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/TODO.md +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/check.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/docs/api-schemas/v0.yml +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/docs/api-schemas/v1.yml +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/media/logo-orange-512h.webp +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/media/logo-orange-60h.webp +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/release.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/setup.cfg +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/src/pdfdancer/exceptions.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/src/pdfdancer/fingerprint.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/src/pdfdancer/image_builder.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/src/pdfdancer/page_builder.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/src/pdfdancer/paragraph_builder.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/src/pdfdancer/path_builder.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/src/pdfdancer/text_line_builder.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/src/pdfdancer/types.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/src/pdfdancer_client_python.egg-info/dependency_links.txt +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/src/pdfdancer_client_python.egg-info/requires.txt +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/src/pdfdancer_client_python.egg-info/top_level.txt +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/test.sh +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/__init__.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/conftest.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/__init__.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/pdf_assertions.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_acroform.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_bezier_builder.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_context_manager.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_form_x_objects.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_image.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_image_transform.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_line_builder.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_new_pdf.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_page.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_paragraph.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_path.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_path_builder.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_path_builder_rectangle.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_path_comprehensive.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_pdfdancer.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_positioning.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_rectangle_builder.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_redact.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_singular_selection.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_snapshot.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_text_line_edit.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/fixtures/DancingScript-Regular.ttf +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/fixtures/Empty.pdf +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/fixtures/JetBrainsMono-Regular.ttf +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/fixtures/Showcase.pdf +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/fixtures/basic-paths.pdf +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/fixtures/form-xobject-example.pdf +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/fixtures/logo-80.png +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/fixtures/mixed-form-types.pdf +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/test_anonymous_token.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/test_fingerprint.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/test_models.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/test_openapi_compliance.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/test_path_models.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/test_pdf_object_equality.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/test_rate_limit.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/test_standard_fonts.py +0 -0
- {pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/update-api-spec.sh +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pdfdancer-client-python
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.8
|
|
4
4
|
Summary: Python client for PDFDancer API
|
|
5
5
|
Author-email: "The Famous Cat Ltd." <hi@thefamouscat.com>
|
|
6
6
|
License:
|
|
@@ -239,9 +239,9 @@ Dynamic: license-file
|
|
|
239
239
|
|
|
240
240
|
# PDFDancer Python Client
|
|
241
241
|
|
|
242
|
-

|
|
243
243
|
|
|
244
|
-
|
|
244
|
+
## PDF used to be read-only. We fixed that.
|
|
245
245
|
|
|
246
246
|
Edit text in real-world PDFs—even ones you didn't create. Move images, reposition headers, and change fonts with
|
|
247
247
|
pixel-perfect control from Python. The same API is also available for TypeScript and Java.
|
|
@@ -665,9 +665,17 @@ Contributions are welcome via pull request. Please:
|
|
|
665
665
|
4. Follow existing code style and patterns
|
|
666
666
|
5. Update documentation as needed
|
|
667
667
|
|
|
668
|
+
## Helpful links
|
|
669
|
+
|
|
670
|
+
- [API documentation](https://docs.pdfdancer.com?utm_source=github&utm_medium=readme&utm_campaign=pdfdancer-python)
|
|
671
|
+
- [Product overview](https://www.pdfdancer.com?utm_source=github&utm_medium=readme&utm_campaign=pdfdancer-python)
|
|
672
|
+
- [PyPI](https://pypi.org/project/pdfdancer-client-python/)
|
|
673
|
+
- [Changelog](https://www.pdfdancer.com/changelog/?utm_source=github&utm_medium=readme&utm_campaign=pdfdancer-python)
|
|
674
|
+
- [Status](https://status.pdfdancer.com?utm_source=github&utm_medium=readme&utm_campaign=pdfdancer-python)
|
|
675
|
+
|
|
668
676
|
## Related SDKs
|
|
669
677
|
|
|
670
|
-
- TypeScript client: https://github.com/MenschMachine/pdfdancer-client-
|
|
678
|
+
- TypeScript client: https://github.com/MenschMachine/pdfdancer-client-typescript
|
|
671
679
|
- Java client: https://github.com/MenschMachine/pdfdancer-client-java
|
|
672
680
|
|
|
673
681
|
## License
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# PDFDancer Python Client
|
|
2
2
|
|
|
3
|
-

|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## PDF used to be read-only. We fixed that.
|
|
6
6
|
|
|
7
7
|
Edit text in real-world PDFs—even ones you didn't create. Move images, reposition headers, and change fonts with
|
|
8
8
|
pixel-perfect control from Python. The same API is also available for TypeScript and Java.
|
|
@@ -426,9 +426,17 @@ Contributions are welcome via pull request. Please:
|
|
|
426
426
|
4. Follow existing code style and patterns
|
|
427
427
|
5. Update documentation as needed
|
|
428
428
|
|
|
429
|
+
## Helpful links
|
|
430
|
+
|
|
431
|
+
- [API documentation](https://docs.pdfdancer.com?utm_source=github&utm_medium=readme&utm_campaign=pdfdancer-python)
|
|
432
|
+
- [Product overview](https://www.pdfdancer.com?utm_source=github&utm_medium=readme&utm_campaign=pdfdancer-python)
|
|
433
|
+
- [PyPI](https://pypi.org/project/pdfdancer-client-python/)
|
|
434
|
+
- [Changelog](https://www.pdfdancer.com/changelog/?utm_source=github&utm_medium=readme&utm_campaign=pdfdancer-python)
|
|
435
|
+
- [Status](https://status.pdfdancer.com?utm_source=github&utm_medium=readme&utm_campaign=pdfdancer-python)
|
|
436
|
+
|
|
429
437
|
## Related SDKs
|
|
430
438
|
|
|
431
|
-
- TypeScript client: https://github.com/MenschMachine/pdfdancer-client-
|
|
439
|
+
- TypeScript client: https://github.com/MenschMachine/pdfdancer-client-typescript
|
|
432
440
|
- Java client: https://github.com/MenschMachine/pdfdancer-client-java
|
|
433
441
|
|
|
434
442
|
## License
|
|
Binary file
|
|
Binary file
|
|
@@ -44,7 +44,6 @@ from .models import (
|
|
|
44
44
|
ShapeType,
|
|
45
45
|
Size,
|
|
46
46
|
StandardFonts,
|
|
47
|
-
TemplateReplacement,
|
|
48
47
|
TextObjectRef,
|
|
49
48
|
TextStatus,
|
|
50
49
|
)
|
|
@@ -93,7 +92,6 @@ __all__ = [
|
|
|
93
92
|
"RedactTarget",
|
|
94
93
|
"RedactResponse",
|
|
95
94
|
"ReflowPreset",
|
|
96
|
-
"TemplateReplacement",
|
|
97
95
|
"PdfDancerException",
|
|
98
96
|
"FontNotFoundException",
|
|
99
97
|
"ValidationException",
|
|
@@ -1686,25 +1686,31 @@ class TemplateReplacement:
|
|
|
1686
1686
|
Parameters:
|
|
1687
1687
|
- placeholder: The exact text to find and replace in the PDF.
|
|
1688
1688
|
- text: The text to replace the placeholder with.
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
```python
|
|
1692
|
-
replacement = TemplateReplacement(
|
|
1693
|
-
placeholder="{{NAME}}",
|
|
1694
|
-
text="John Doe"
|
|
1695
|
-
)
|
|
1696
|
-
```
|
|
1689
|
+
- font: Optional font for the replacement text.
|
|
1690
|
+
- color: Optional color for the replacement text.
|
|
1697
1691
|
"""
|
|
1698
1692
|
|
|
1699
1693
|
placeholder: str
|
|
1700
1694
|
text: str
|
|
1695
|
+
font: Optional[Font] = None
|
|
1696
|
+
color: Optional[Color] = None
|
|
1701
1697
|
|
|
1702
1698
|
def to_dict(self) -> dict:
|
|
1703
1699
|
"""Convert to dictionary for JSON serialization."""
|
|
1704
|
-
|
|
1700
|
+
result: Dict[str, Any] = {
|
|
1705
1701
|
"placeholder": self.placeholder,
|
|
1706
1702
|
"text": self.text,
|
|
1707
1703
|
}
|
|
1704
|
+
if self.font:
|
|
1705
|
+
result["font"] = {"name": self.font.name, "size": self.font.size}
|
|
1706
|
+
if self.color:
|
|
1707
|
+
result["color"] = {
|
|
1708
|
+
"red": self.color.r,
|
|
1709
|
+
"green": self.color.g,
|
|
1710
|
+
"blue": self.color.b,
|
|
1711
|
+
"alpha": self.color.a,
|
|
1712
|
+
}
|
|
1713
|
+
return result
|
|
1708
1714
|
|
|
1709
1715
|
|
|
1710
1716
|
@dataclass
|
{pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/src/pdfdancer/pdfdancer_v1.py
RENAMED
|
@@ -16,7 +16,7 @@ import time
|
|
|
16
16
|
from datetime import datetime, timezone
|
|
17
17
|
from importlib.metadata import version as get_package_version
|
|
18
18
|
from pathlib import Path
|
|
19
|
-
from typing import TYPE_CHECKING, Any, BinaryIO, List, Mapping, Optional, Union
|
|
19
|
+
from typing import TYPE_CHECKING, Any, BinaryIO, Dict, List, Mapping, Optional, Union
|
|
20
20
|
|
|
21
21
|
import httpx
|
|
22
22
|
from dotenv import load_dotenv
|
|
@@ -123,6 +123,26 @@ DEFAULT_RETRY_BACKOFF_FACTOR = float(
|
|
|
123
123
|
)
|
|
124
124
|
|
|
125
125
|
|
|
126
|
+
def _dict_to_replacements(
|
|
127
|
+
replacements: Dict[str, Union[str, dict]]
|
|
128
|
+
) -> List[TemplateReplacement]:
|
|
129
|
+
"""Convert dict-based replacements to TemplateReplacement list."""
|
|
130
|
+
result = []
|
|
131
|
+
for placeholder, value in replacements.items():
|
|
132
|
+
if isinstance(value, str):
|
|
133
|
+
result.append(TemplateReplacement(placeholder=placeholder, text=value))
|
|
134
|
+
else:
|
|
135
|
+
result.append(
|
|
136
|
+
TemplateReplacement(
|
|
137
|
+
placeholder=placeholder,
|
|
138
|
+
text=value["text"],
|
|
139
|
+
font=value.get("font"),
|
|
140
|
+
color=value.get("color"),
|
|
141
|
+
)
|
|
142
|
+
)
|
|
143
|
+
return result
|
|
144
|
+
|
|
145
|
+
|
|
126
146
|
def _generate_timestamp() -> str:
|
|
127
147
|
"""
|
|
128
148
|
Generate a timestamp string in the format expected by the API.
|
|
@@ -611,7 +631,7 @@ class PageClient:
|
|
|
611
631
|
|
|
612
632
|
def apply_replacements(
|
|
613
633
|
self,
|
|
614
|
-
replacements:
|
|
634
|
+
replacements: Dict[str, Union[str, dict]],
|
|
615
635
|
reflow_preset: Optional[ReflowPreset] = None,
|
|
616
636
|
) -> bool:
|
|
617
637
|
"""
|
|
@@ -621,8 +641,9 @@ class PageClient:
|
|
|
621
641
|
content. All placeholders must be found or the operation fails atomically.
|
|
622
642
|
|
|
623
643
|
Args:
|
|
624
|
-
replacements:
|
|
625
|
-
|
|
644
|
+
replacements: Dict mapping placeholder strings to replacement values.
|
|
645
|
+
- Simple: {"{{NAME}}": "John Doe"}
|
|
646
|
+
- With options: {"{{NAME}}": {"text": "John", "font": Font(...), "color": Color(...)}}
|
|
626
647
|
reflow_preset: Optional ReflowPreset to control text reflow behavior.
|
|
627
648
|
- BEST_EFFORT: Attempt to reflow, proceed even if imperfect
|
|
628
649
|
- FIT_OR_FAIL: Reflow must succeed or operation fails
|
|
@@ -633,14 +654,15 @@ class PageClient:
|
|
|
633
654
|
|
|
634
655
|
Example:
|
|
635
656
|
```python
|
|
636
|
-
page.apply_replacements(
|
|
637
|
-
|
|
638
|
-
|
|
657
|
+
page.apply_replacements({
|
|
658
|
+
"{{NAME}}": "John Doe",
|
|
659
|
+
})
|
|
639
660
|
```
|
|
640
661
|
"""
|
|
662
|
+
replacement_list = _dict_to_replacements(replacements)
|
|
641
663
|
# noinspection PyProtectedMember
|
|
642
664
|
return self.root._apply_replacements(
|
|
643
|
-
replacements=
|
|
665
|
+
replacements=replacement_list,
|
|
644
666
|
page_number=self.page_number,
|
|
645
667
|
reflow_preset=reflow_preset,
|
|
646
668
|
)
|
|
@@ -2246,7 +2268,7 @@ class PDFDancer:
|
|
|
2246
2268
|
|
|
2247
2269
|
def apply_replacements(
|
|
2248
2270
|
self,
|
|
2249
|
-
replacements:
|
|
2271
|
+
replacements: Dict[str, Union[str, dict]],
|
|
2250
2272
|
reflow_preset: Optional[ReflowPreset] = None,
|
|
2251
2273
|
) -> bool:
|
|
2252
2274
|
"""
|
|
@@ -2256,8 +2278,9 @@ class PDFDancer:
|
|
|
2256
2278
|
content. All placeholders must be found or the operation fails atomically.
|
|
2257
2279
|
|
|
2258
2280
|
Args:
|
|
2259
|
-
replacements:
|
|
2260
|
-
|
|
2281
|
+
replacements: Dict mapping placeholder strings to replacement values.
|
|
2282
|
+
- Simple: {"{{NAME}}": "John Doe"}
|
|
2283
|
+
- With options: {"{{NAME}}": {"text": "John", "font": Font(...), "color": Color(...)}}
|
|
2261
2284
|
reflow_preset: Optional ReflowPreset to control text reflow behavior.
|
|
2262
2285
|
- BEST_EFFORT: Attempt to reflow, proceed even if imperfect
|
|
2263
2286
|
- FIT_OR_FAIL: Reflow must succeed or operation fails
|
|
@@ -2268,14 +2291,15 @@ class PDFDancer:
|
|
|
2268
2291
|
|
|
2269
2292
|
Example:
|
|
2270
2293
|
```python
|
|
2271
|
-
pdf.apply_replacements(
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2294
|
+
pdf.apply_replacements({
|
|
2295
|
+
"{{NAME}}": "John Doe",
|
|
2296
|
+
"{{DATE}}": "2025-01-15",
|
|
2297
|
+
})
|
|
2275
2298
|
```
|
|
2276
2299
|
"""
|
|
2300
|
+
replacement_list = _dict_to_replacements(replacements)
|
|
2277
2301
|
return self._apply_replacements(
|
|
2278
|
-
replacements=
|
|
2302
|
+
replacements=replacement_list,
|
|
2279
2303
|
page_number=None,
|
|
2280
2304
|
reflow_preset=reflow_preset,
|
|
2281
2305
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pdfdancer-client-python
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.8
|
|
4
4
|
Summary: Python client for PDFDancer API
|
|
5
5
|
Author-email: "The Famous Cat Ltd." <hi@thefamouscat.com>
|
|
6
6
|
License:
|
|
@@ -239,9 +239,9 @@ Dynamic: license-file
|
|
|
239
239
|
|
|
240
240
|
# PDFDancer Python Client
|
|
241
241
|
|
|
242
|
-

|
|
243
243
|
|
|
244
|
-
|
|
244
|
+
## PDF used to be read-only. We fixed that.
|
|
245
245
|
|
|
246
246
|
Edit text in real-world PDFs—even ones you didn't create. Move images, reposition headers, and change fonts with
|
|
247
247
|
pixel-perfect control from Python. The same API is also available for TypeScript and Java.
|
|
@@ -665,9 +665,17 @@ Contributions are welcome via pull request. Please:
|
|
|
665
665
|
4. Follow existing code style and patterns
|
|
666
666
|
5. Update documentation as needed
|
|
667
667
|
|
|
668
|
+
## Helpful links
|
|
669
|
+
|
|
670
|
+
- [API documentation](https://docs.pdfdancer.com?utm_source=github&utm_medium=readme&utm_campaign=pdfdancer-python)
|
|
671
|
+
- [Product overview](https://www.pdfdancer.com?utm_source=github&utm_medium=readme&utm_campaign=pdfdancer-python)
|
|
672
|
+
- [PyPI](https://pypi.org/project/pdfdancer-client-python/)
|
|
673
|
+
- [Changelog](https://www.pdfdancer.com/changelog/?utm_source=github&utm_medium=readme&utm_campaign=pdfdancer-python)
|
|
674
|
+
- [Status](https://status.pdfdancer.com?utm_source=github&utm_medium=readme&utm_campaign=pdfdancer-python)
|
|
675
|
+
|
|
668
676
|
## Related SDKs
|
|
669
677
|
|
|
670
|
-
- TypeScript client: https://github.com/MenschMachine/pdfdancer-client-
|
|
678
|
+
- TypeScript client: https://github.com/MenschMachine/pdfdancer-client-typescript
|
|
671
679
|
- Java client: https://github.com/MenschMachine/pdfdancer-client-java
|
|
672
680
|
|
|
673
681
|
## License
|
|
@@ -18,6 +18,8 @@ docs/api-schemas/v0.yml
|
|
|
18
18
|
docs/api-schemas/v1.yml
|
|
19
19
|
media/logo-orange-512h.webp
|
|
20
20
|
media/logo-orange-60h.webp
|
|
21
|
+
media/logo-silver-512h.webp
|
|
22
|
+
media/logo-silver-60h.webp
|
|
21
23
|
src/pdfdancer/__init__.py
|
|
22
24
|
src/pdfdancer/exceptions.py
|
|
23
25
|
src/pdfdancer/fingerprint.py
|
|
@@ -111,7 +111,9 @@ def test_modify_line():
|
|
|
111
111
|
line = pdf.page(1).select_text_lines_starting_with(
|
|
112
112
|
"This is regular Sans text showing alignment and styles."
|
|
113
113
|
)[0]
|
|
114
|
-
|
|
114
|
+
|
|
115
|
+
line.edit().replace(" replaced ").apply()
|
|
116
|
+
# TODO assert line.text == " replaced "
|
|
115
117
|
|
|
116
118
|
# Validate replacements
|
|
117
119
|
assert (
|
{pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_template_replace.py
RENAMED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
4
|
|
|
5
|
-
from pdfdancer import
|
|
5
|
+
from pdfdancer import Color, Font, ReflowPreset, ValidationException
|
|
6
6
|
from pdfdancer.pdfdancer_v1 import PDFDancer
|
|
7
7
|
from tests.e2e import _require_env_and_fixture
|
|
8
8
|
from tests.e2e.pdf_assertions import PDFAssertions
|
|
@@ -21,9 +21,7 @@ def test_replace_single_template():
|
|
|
21
21
|
)
|
|
22
22
|
|
|
23
23
|
# Replace the existing "Showcase" text
|
|
24
|
-
result = pdf.apply_replacements(
|
|
25
|
-
TemplateReplacement(placeholder="Showcase", text="Replaced"),
|
|
26
|
-
])
|
|
24
|
+
result = pdf.apply_replacements({"Showcase": "Replaced"})
|
|
27
25
|
|
|
28
26
|
assert result is True
|
|
29
27
|
|
|
@@ -42,10 +40,10 @@ def test_replace_multiple_templates():
|
|
|
42
40
|
|
|
43
41
|
with PDFDancer.open(pdf_path, token=token, base_url=base_url, timeout=30.0) as pdf:
|
|
44
42
|
# Replace multiple placeholders
|
|
45
|
-
result = pdf.apply_replacements(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
result = pdf.apply_replacements({
|
|
44
|
+
"PDFDancer": "TestApp",
|
|
45
|
+
"Engine": "System",
|
|
46
|
+
})
|
|
49
47
|
|
|
50
48
|
assert result is True
|
|
51
49
|
|
|
@@ -71,9 +69,7 @@ def test_replace_template_on_specific_page():
|
|
|
71
69
|
)
|
|
72
70
|
|
|
73
71
|
# Replace only on page 1
|
|
74
|
-
result = pdf.page(1).apply_replacements(
|
|
75
|
-
TemplateReplacement(placeholder="Showcase", text="PageOneOnly"),
|
|
76
|
-
])
|
|
72
|
+
result = pdf.page(1).apply_replacements({"Showcase": "PageOneOnly"})
|
|
77
73
|
|
|
78
74
|
assert result is True
|
|
79
75
|
|
|
@@ -98,7 +94,7 @@ def test_replace_template_with_reflow_best_effort():
|
|
|
98
94
|
|
|
99
95
|
# Replace with longer text using BEST_EFFORT reflow
|
|
100
96
|
result = pdf.apply_replacements(
|
|
101
|
-
|
|
97
|
+
{"Showcase": "ThisIsAMuchLongerReplacementText"},
|
|
102
98
|
reflow_preset=ReflowPreset.BEST_EFFORT,
|
|
103
99
|
)
|
|
104
100
|
|
|
@@ -125,7 +121,7 @@ def test_replace_template_with_reflow_none():
|
|
|
125
121
|
|
|
126
122
|
# Replace without reflow
|
|
127
123
|
result = pdf.apply_replacements(
|
|
128
|
-
|
|
124
|
+
{"Showcase": "NoReflow"},
|
|
129
125
|
reflow_preset=ReflowPreset.NONE,
|
|
130
126
|
)
|
|
131
127
|
|
|
@@ -139,40 +135,86 @@ def test_replace_template_with_reflow_none():
|
|
|
139
135
|
)
|
|
140
136
|
|
|
141
137
|
|
|
142
|
-
def
|
|
143
|
-
"""Test that replacing with an empty
|
|
138
|
+
def test_replace_template_empty_dict_raises():
|
|
139
|
+
"""Test that replacing with an empty dict raises ValidationException."""
|
|
144
140
|
base_url, token, pdf_path = _require_env_and_fixture("Showcase.pdf")
|
|
145
141
|
|
|
146
142
|
with PDFDancer.open(pdf_path, token=token, base_url=base_url, timeout=30.0) as pdf:
|
|
147
143
|
with pytest.raises(ValidationException):
|
|
148
|
-
pdf.apply_replacements(
|
|
144
|
+
pdf.apply_replacements({})
|
|
149
145
|
|
|
150
146
|
|
|
151
|
-
def
|
|
152
|
-
"""Test that page-level replacement with empty
|
|
147
|
+
def test_replace_template_page_level_empty_dict_raises():
|
|
148
|
+
"""Test that page-level replacement with empty dict raises ValidationException."""
|
|
153
149
|
base_url, token, pdf_path = _require_env_and_fixture("Showcase.pdf")
|
|
154
150
|
|
|
155
151
|
with PDFDancer.open(pdf_path, token=token, base_url=base_url, timeout=30.0) as pdf:
|
|
156
152
|
with pytest.raises(ValidationException):
|
|
157
|
-
pdf.page(1).apply_replacements(
|
|
153
|
+
pdf.page(1).apply_replacements({})
|
|
158
154
|
|
|
159
155
|
|
|
160
|
-
def
|
|
161
|
-
"""Test
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
156
|
+
def test_replace_template_with_font():
|
|
157
|
+
"""Test template replacement with custom font."""
|
|
158
|
+
base_url, token, pdf_path = _require_env_and_fixture("Showcase.pdf")
|
|
159
|
+
|
|
160
|
+
with PDFDancer.open(pdf_path, token=token, base_url=base_url, timeout=30.0) as pdf:
|
|
161
|
+
result = pdf.apply_replacements({
|
|
162
|
+
"Showcase": {
|
|
163
|
+
"text": "FontTest",
|
|
164
|
+
"font": Font("Helvetica-Bold", 14),
|
|
165
|
+
}
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
assert result is True
|
|
169
|
+
|
|
170
|
+
(
|
|
171
|
+
PDFAssertions(pdf)
|
|
172
|
+
.assert_textline_does_not_exist("Showcase")
|
|
173
|
+
.assert_textline_exists("FontTest")
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def test_replace_template_with_color():
|
|
178
|
+
"""Test template replacement with custom color."""
|
|
179
|
+
base_url, token, pdf_path = _require_env_and_fixture("Showcase.pdf")
|
|
180
|
+
|
|
181
|
+
with PDFDancer.open(pdf_path, token=token, base_url=base_url, timeout=30.0) as pdf:
|
|
182
|
+
result = pdf.apply_replacements({
|
|
183
|
+
"Showcase": {
|
|
184
|
+
"text": "ColorTest",
|
|
185
|
+
"color": Color(255, 0, 0),
|
|
186
|
+
}
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
assert result is True
|
|
190
|
+
|
|
191
|
+
(
|
|
192
|
+
PDFAssertions(pdf)
|
|
193
|
+
.assert_textline_does_not_exist("Showcase")
|
|
194
|
+
.assert_textline_exists("ColorTest")
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def test_replace_template_with_font_and_color():
|
|
199
|
+
"""Test template replacement with both font and color."""
|
|
200
|
+
base_url, token, pdf_path = _require_env_and_fixture("Showcase.pdf")
|
|
201
|
+
|
|
202
|
+
with PDFDancer.open(pdf_path, token=token, base_url=base_url, timeout=30.0) as pdf:
|
|
203
|
+
result = pdf.apply_replacements({
|
|
204
|
+
"Showcase": {
|
|
205
|
+
"text": "StyledText",
|
|
206
|
+
"font": Font("Helvetica-Bold", 16),
|
|
207
|
+
"color": Color(0, 100, 0),
|
|
208
|
+
}
|
|
209
|
+
})
|
|
166
210
|
|
|
167
|
-
|
|
168
|
-
assert replacement.text == "John Doe"
|
|
211
|
+
assert result is True
|
|
169
212
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
213
|
+
(
|
|
214
|
+
PDFAssertions(pdf)
|
|
215
|
+
.assert_textline_does_not_exist("Showcase")
|
|
216
|
+
.assert_textline_exists("StyledText")
|
|
217
|
+
)
|
|
176
218
|
|
|
177
219
|
|
|
178
220
|
def test_reflow_preset_values():
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/.github/workflows/daily-tests.yml
RENAMED
|
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
|
{pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/src/pdfdancer/fingerprint.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/src/pdfdancer/image_builder.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/src/pdfdancer/page_builder.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/src/pdfdancer/paragraph_builder.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/src/pdfdancer/path_builder.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/src/pdfdancer/text_line_builder.py
RENAMED
|
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
|
{pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_bezier_builder.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_context_manager.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_form_x_objects.py
RENAMED
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_image_transform.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_line_builder.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_path_builder.py
RENAMED
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_path_comprehensive.py
RENAMED
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_positioning.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_rectangle_builder.py
RENAMED
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_singular_selection.py
RENAMED
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/e2e/test_text_line_edit.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/fixtures/basic-paths.pdf
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/fixtures/mixed-form-types.pdf
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/test_anonymous_token.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/test_openapi_compliance.py
RENAMED
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/test_pdf_object_equality.py
RENAMED
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.3.6 → pdfdancer_client_python-0.3.8}/tests/test_standard_fonts.py
RENAMED
|
File without changes
|
|
File without changes
|