pdfdancer-client-python 0.2.15__tar.gz → 0.2.16__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pdfdancer-client-python might be problematic. Click here for more details.
- pdfdancer_client_python-0.2.16/PKG-INFO +190 -0
- pdfdancer_client_python-0.2.16/README.md +158 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/pyproject.toml +1 -1
- pdfdancer_client_python-0.2.16/src/pdfdancer_client_python.egg-info/PKG-INFO +190 -0
- pdfdancer_client_python-0.2.15/PKG-INFO +0 -200
- pdfdancer_client_python-0.2.15/README.md +0 -168
- pdfdancer_client_python-0.2.15/src/pdfdancer_client_python.egg-info/PKG-INFO +0 -200
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/.claude/commands/discuss.md +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/.github/workflows/ci.yml +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/.gitignore +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/CLAUDE.md +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/docs/openapi.yml +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/release.py +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/setup.cfg +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/src/pdfdancer/__init__.py +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/src/pdfdancer/exceptions.py +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/src/pdfdancer/image_builder.py +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/src/pdfdancer/models.py +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/src/pdfdancer/paragraph_builder.py +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/src/pdfdancer/pdfdancer_v1.py +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/src/pdfdancer/types.py +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/src/pdfdancer_client_python.egg-info/SOURCES.txt +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/src/pdfdancer_client_python.egg-info/dependency_links.txt +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/src/pdfdancer_client_python.egg-info/requires.txt +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/src/pdfdancer_client_python.egg-info/top_level.txt +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/__init__.py +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/conftest.py +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/e2e/__init__.py +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/e2e/pdf_assertions.py +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/e2e/test_acroform.py +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/e2e/test_form_x_objects.py +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/e2e/test_image.py +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/e2e/test_line.py +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/e2e/test_new_pdf.py +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/e2e/test_page.py +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/e2e/test_paragraph.py +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/e2e/test_path.py +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/e2e/test_pdfdancer.py +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/e2e/test_positioning.py +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/fixtures/DancingScript-Regular.ttf +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/fixtures/Empty.pdf +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/fixtures/JetBrainsMono-Regular.ttf +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/fixtures/ObviouslyAwesome.pdf +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/fixtures/basic-paths.pdf +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/fixtures/form-xobject-example.pdf +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/fixtures/logo-80.png +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/fixtures/mixed-form-types.pdf +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/test_models.py +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/test_openapi_compliance.py +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/test_pdf_object_equality.py +0 -0
- {pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/test_standard_fonts.py +0 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pdfdancer-client-python
|
|
3
|
+
Version: 0.2.16
|
|
4
|
+
Summary: Python client for PDFDancer API
|
|
5
|
+
Author-email: "The Famous Cat Ltd." <hi@thefamouscat.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://www.pdfdancer.com/
|
|
8
|
+
Project-URL: Repository, https://github.com/MenschMachine/pdfdancer-client-python
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: requests>=2.25.0
|
|
19
|
+
Requires-Dist: pydantic>=1.8.0
|
|
20
|
+
Requires-Dist: typing-extensions>=4.0.0
|
|
21
|
+
Requires-Dist: python-dotenv>=0.19.0
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
24
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
25
|
+
Requires-Dist: pytest-mock>=3.10.0; extra == "dev"
|
|
26
|
+
Requires-Dist: black>=22.0; extra == "dev"
|
|
27
|
+
Requires-Dist: flake8>=5.0; extra == "dev"
|
|
28
|
+
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
29
|
+
Requires-Dist: isort>=5.10.0; extra == "dev"
|
|
30
|
+
Requires-Dist: build>=0.8.0; extra == "dev"
|
|
31
|
+
Requires-Dist: twine>=4.0.0; extra == "dev"
|
|
32
|
+
|
|
33
|
+
# PDFDancer Python Client
|
|
34
|
+
|
|
35
|
+
**Getting Started with PDFDancer**
|
|
36
|
+
|
|
37
|
+
PDFDancer gives you pixel-perfect programmatic control over any PDF document from Python. Locate existing elements by
|
|
38
|
+
coordinates or text, adjust them precisely, add brand-new content, and ship the modified PDF in memory or on disk. The
|
|
39
|
+
same API is also available for TypeScript and Java, so teams can orchestrate identical PDF workflows across stacks.
|
|
40
|
+
|
|
41
|
+
> Need the raw API schema? The latest OpenAPI description lives in `docs/openapi.yml` and is published at
|
|
42
|
+
> https://bucket.pdfdancer.com/api-doc/development-0.0.yml.
|
|
43
|
+
|
|
44
|
+
## Highlights
|
|
45
|
+
|
|
46
|
+
- Locate paragraphs, text lines, images, vector paths, form fields, and pages by index, coordinates, or text prefixes.
|
|
47
|
+
- Edit existing content in place with fluent editors and context managers that apply changes safely.
|
|
48
|
+
- Programmatically control third-party PDFs—modify invoices, contracts, and reports you did not author.
|
|
49
|
+
- Add content with precise XY positioning using paragraph and image builders, custom fonts, and color helpers.
|
|
50
|
+
- Export results as bytes for downstream processing or save directly to disk with one call.
|
|
51
|
+
|
|
52
|
+
## What Makes PDFDancer Different
|
|
53
|
+
|
|
54
|
+
- **Edit any PDF**: Work with documents from customers, governments, or vendors—not just ones you generated.
|
|
55
|
+
- **Pixel-perfect positioning**: Move or add elements at exact coordinates and keep the original layout intact.
|
|
56
|
+
- **Surgical text replacement**: Swap or rewrite paragraphs without reflowing the rest of the page.
|
|
57
|
+
- **Form manipulation**: Inspect, fill, and update AcroForm fields programmatically.
|
|
58
|
+
- **Coordinate-based selection**: Select objects by position, bounding box, or text patterns.
|
|
59
|
+
- **Real PDF editing**: Modify the underlying PDF structure instead of merely stamping overlays.
|
|
60
|
+
|
|
61
|
+
## Installation
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pip install pdfdancer-client-python
|
|
65
|
+
|
|
66
|
+
# Editable install for local development
|
|
67
|
+
pip install -e .
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Requires Python 3.10+ and a PDFDancer API token.
|
|
71
|
+
|
|
72
|
+
## Quick Start — Edit an Existing PDF
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from pathlib import Path
|
|
76
|
+
from pdfdancer import Color, PDFDancer, StandardFonts
|
|
77
|
+
|
|
78
|
+
with PDFDancer.open(
|
|
79
|
+
pdf_data=Path("input.pdf"),
|
|
80
|
+
token="your-api-token", # optional when PDFDANCER_TOKEN is set
|
|
81
|
+
base_url="https://api.pdfdancer.com",
|
|
82
|
+
) as pdf:
|
|
83
|
+
# Locate and update an existing paragraph
|
|
84
|
+
heading = pdf.page(0).select_paragraphs_starting_with("Executive Summary")[0]
|
|
85
|
+
heading.move_to(72, 680)
|
|
86
|
+
with heading.edit() as editor:
|
|
87
|
+
editor.replace("Overview")
|
|
88
|
+
|
|
89
|
+
# Add a new paragraph with precise placement
|
|
90
|
+
pdf.new_paragraph() \
|
|
91
|
+
.text("Generated with PDFDancer") \
|
|
92
|
+
.font(StandardFonts.HELVETICA, 12) \
|
|
93
|
+
.color(Color(70, 70, 70)) \
|
|
94
|
+
.line_spacing(1.4) \
|
|
95
|
+
.at(page_index=0, x=72, y=520) \
|
|
96
|
+
.add()
|
|
97
|
+
|
|
98
|
+
# Persist the modified document
|
|
99
|
+
pdf.save("output.pdf")
|
|
100
|
+
# or keep it in memory
|
|
101
|
+
pdf_bytes = pdf.get_bytes()
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Create a Blank PDF
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from pathlib import Path
|
|
108
|
+
from pdfdancer import Color, PDFDancer, StandardFonts
|
|
109
|
+
|
|
110
|
+
with PDFDancer.new(token="your-api-token") as pdf:
|
|
111
|
+
pdf.new_paragraph() \
|
|
112
|
+
.text("Quarterly Summary") \
|
|
113
|
+
.font(StandardFonts.TIMES_BOLD, 18) \
|
|
114
|
+
.color(Color(10, 10, 80)) \
|
|
115
|
+
.line_spacing(1.2) \
|
|
116
|
+
.at(page_index=0, x=72, y=730) \
|
|
117
|
+
.add()
|
|
118
|
+
|
|
119
|
+
pdf.new_image() \
|
|
120
|
+
.from_file(Path("logo.png")) \
|
|
121
|
+
.at(page=0, x=420, y=710) \
|
|
122
|
+
.add()
|
|
123
|
+
|
|
124
|
+
pdf.save("summary.pdf")
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Work with Forms and Layout
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from pdfdancer import PDFDancer
|
|
131
|
+
|
|
132
|
+
with PDFDancer.open("contract.pdf") as pdf:
|
|
133
|
+
# Inspect global document structure
|
|
134
|
+
pages = pdf.pages()
|
|
135
|
+
print("Total pages:", len(pages))
|
|
136
|
+
|
|
137
|
+
# Update form fields
|
|
138
|
+
signature = pdf.select_form_fields_by_name("signature")[0]
|
|
139
|
+
signature.edit().value("Signed by Jane Doe").apply()
|
|
140
|
+
|
|
141
|
+
# Trim or move content at specific coordinates
|
|
142
|
+
images = pdf.page(1).select_images()
|
|
143
|
+
for image in images:
|
|
144
|
+
x = image.position.x()
|
|
145
|
+
if x is not None and x < 100:
|
|
146
|
+
image.delete()
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Selectors return typed objects (`ParagraphObject`, `TextLineObject`, `ImageObject`, `FormFieldObject`, `PageClient`, …)
|
|
150
|
+
with helpers such as `delete()`, `move_to(x, y)`, or `edit()` depending on the object type.
|
|
151
|
+
|
|
152
|
+
## Configuration
|
|
153
|
+
|
|
154
|
+
- Set `PDFDANCER_TOKEN` for authentication (preferred for local development and CI).
|
|
155
|
+
- Override the API host with `PDFDANCER_BASE_URL` (e.g., sandbox environments).
|
|
156
|
+
- Tune HTTP read timeouts via the `timeout` argument on `PDFDancer.open()` and `PDFDancer.new()`.
|
|
157
|
+
|
|
158
|
+
## Error Handling
|
|
159
|
+
|
|
160
|
+
Operations raise subclasses of `PdfDancerException`:
|
|
161
|
+
|
|
162
|
+
- `ValidationException`: input validation problems (missing token, invalid coordinates, etc.).
|
|
163
|
+
- `FontNotFoundException`: requested font unavailable on the service.
|
|
164
|
+
- `HttpClientException`: transport or server errors with detailed context.
|
|
165
|
+
- `SessionException`: session creation and lifecycle failures.
|
|
166
|
+
|
|
167
|
+
Wrap automated workflows in `try/except` blocks to surface actionable errors to your users.
|
|
168
|
+
|
|
169
|
+
## Development
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
python -m venv venv
|
|
173
|
+
source venv/bin/activate # Windows: venv\Scripts\activate
|
|
174
|
+
pip install -e ".[dev]"
|
|
175
|
+
|
|
176
|
+
pytest -q # unit suite
|
|
177
|
+
pytest tests/e2e # integration tests (requires live API + fixtures)
|
|
178
|
+
python -m build # produce distribution artifacts
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Releases are published with `python release.py`. Contributions are welcome via pull request.
|
|
182
|
+
|
|
183
|
+
## Related SDKs
|
|
184
|
+
|
|
185
|
+
- TypeScript client: https://github.com/MenschMachine/pdfdancer-client-js
|
|
186
|
+
- Java client: https://github.com/MenschMachine/pdfdancer-client-java
|
|
187
|
+
|
|
188
|
+
## License
|
|
189
|
+
|
|
190
|
+
MIT © The Famous Cat Ltd.
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# PDFDancer Python Client
|
|
2
|
+
|
|
3
|
+
**Getting Started with PDFDancer**
|
|
4
|
+
|
|
5
|
+
PDFDancer gives you pixel-perfect programmatic control over any PDF document from Python. Locate existing elements by
|
|
6
|
+
coordinates or text, adjust them precisely, add brand-new content, and ship the modified PDF in memory or on disk. The
|
|
7
|
+
same API is also available for TypeScript and Java, so teams can orchestrate identical PDF workflows across stacks.
|
|
8
|
+
|
|
9
|
+
> Need the raw API schema? The latest OpenAPI description lives in `docs/openapi.yml` and is published at
|
|
10
|
+
> https://bucket.pdfdancer.com/api-doc/development-0.0.yml.
|
|
11
|
+
|
|
12
|
+
## Highlights
|
|
13
|
+
|
|
14
|
+
- Locate paragraphs, text lines, images, vector paths, form fields, and pages by index, coordinates, or text prefixes.
|
|
15
|
+
- Edit existing content in place with fluent editors and context managers that apply changes safely.
|
|
16
|
+
- Programmatically control third-party PDFs—modify invoices, contracts, and reports you did not author.
|
|
17
|
+
- Add content with precise XY positioning using paragraph and image builders, custom fonts, and color helpers.
|
|
18
|
+
- Export results as bytes for downstream processing or save directly to disk with one call.
|
|
19
|
+
|
|
20
|
+
## What Makes PDFDancer Different
|
|
21
|
+
|
|
22
|
+
- **Edit any PDF**: Work with documents from customers, governments, or vendors—not just ones you generated.
|
|
23
|
+
- **Pixel-perfect positioning**: Move or add elements at exact coordinates and keep the original layout intact.
|
|
24
|
+
- **Surgical text replacement**: Swap or rewrite paragraphs without reflowing the rest of the page.
|
|
25
|
+
- **Form manipulation**: Inspect, fill, and update AcroForm fields programmatically.
|
|
26
|
+
- **Coordinate-based selection**: Select objects by position, bounding box, or text patterns.
|
|
27
|
+
- **Real PDF editing**: Modify the underlying PDF structure instead of merely stamping overlays.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install pdfdancer-client-python
|
|
33
|
+
|
|
34
|
+
# Editable install for local development
|
|
35
|
+
pip install -e .
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Requires Python 3.10+ and a PDFDancer API token.
|
|
39
|
+
|
|
40
|
+
## Quick Start — Edit an Existing PDF
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from pathlib import Path
|
|
44
|
+
from pdfdancer import Color, PDFDancer, StandardFonts
|
|
45
|
+
|
|
46
|
+
with PDFDancer.open(
|
|
47
|
+
pdf_data=Path("input.pdf"),
|
|
48
|
+
token="your-api-token", # optional when PDFDANCER_TOKEN is set
|
|
49
|
+
base_url="https://api.pdfdancer.com",
|
|
50
|
+
) as pdf:
|
|
51
|
+
# Locate and update an existing paragraph
|
|
52
|
+
heading = pdf.page(0).select_paragraphs_starting_with("Executive Summary")[0]
|
|
53
|
+
heading.move_to(72, 680)
|
|
54
|
+
with heading.edit() as editor:
|
|
55
|
+
editor.replace("Overview")
|
|
56
|
+
|
|
57
|
+
# Add a new paragraph with precise placement
|
|
58
|
+
pdf.new_paragraph() \
|
|
59
|
+
.text("Generated with PDFDancer") \
|
|
60
|
+
.font(StandardFonts.HELVETICA, 12) \
|
|
61
|
+
.color(Color(70, 70, 70)) \
|
|
62
|
+
.line_spacing(1.4) \
|
|
63
|
+
.at(page_index=0, x=72, y=520) \
|
|
64
|
+
.add()
|
|
65
|
+
|
|
66
|
+
# Persist the modified document
|
|
67
|
+
pdf.save("output.pdf")
|
|
68
|
+
# or keep it in memory
|
|
69
|
+
pdf_bytes = pdf.get_bytes()
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Create a Blank PDF
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from pathlib import Path
|
|
76
|
+
from pdfdancer import Color, PDFDancer, StandardFonts
|
|
77
|
+
|
|
78
|
+
with PDFDancer.new(token="your-api-token") as pdf:
|
|
79
|
+
pdf.new_paragraph() \
|
|
80
|
+
.text("Quarterly Summary") \
|
|
81
|
+
.font(StandardFonts.TIMES_BOLD, 18) \
|
|
82
|
+
.color(Color(10, 10, 80)) \
|
|
83
|
+
.line_spacing(1.2) \
|
|
84
|
+
.at(page_index=0, x=72, y=730) \
|
|
85
|
+
.add()
|
|
86
|
+
|
|
87
|
+
pdf.new_image() \
|
|
88
|
+
.from_file(Path("logo.png")) \
|
|
89
|
+
.at(page=0, x=420, y=710) \
|
|
90
|
+
.add()
|
|
91
|
+
|
|
92
|
+
pdf.save("summary.pdf")
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Work with Forms and Layout
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from pdfdancer import PDFDancer
|
|
99
|
+
|
|
100
|
+
with PDFDancer.open("contract.pdf") as pdf:
|
|
101
|
+
# Inspect global document structure
|
|
102
|
+
pages = pdf.pages()
|
|
103
|
+
print("Total pages:", len(pages))
|
|
104
|
+
|
|
105
|
+
# Update form fields
|
|
106
|
+
signature = pdf.select_form_fields_by_name("signature")[0]
|
|
107
|
+
signature.edit().value("Signed by Jane Doe").apply()
|
|
108
|
+
|
|
109
|
+
# Trim or move content at specific coordinates
|
|
110
|
+
images = pdf.page(1).select_images()
|
|
111
|
+
for image in images:
|
|
112
|
+
x = image.position.x()
|
|
113
|
+
if x is not None and x < 100:
|
|
114
|
+
image.delete()
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Selectors return typed objects (`ParagraphObject`, `TextLineObject`, `ImageObject`, `FormFieldObject`, `PageClient`, …)
|
|
118
|
+
with helpers such as `delete()`, `move_to(x, y)`, or `edit()` depending on the object type.
|
|
119
|
+
|
|
120
|
+
## Configuration
|
|
121
|
+
|
|
122
|
+
- Set `PDFDANCER_TOKEN` for authentication (preferred for local development and CI).
|
|
123
|
+
- Override the API host with `PDFDANCER_BASE_URL` (e.g., sandbox environments).
|
|
124
|
+
- Tune HTTP read timeouts via the `timeout` argument on `PDFDancer.open()` and `PDFDancer.new()`.
|
|
125
|
+
|
|
126
|
+
## Error Handling
|
|
127
|
+
|
|
128
|
+
Operations raise subclasses of `PdfDancerException`:
|
|
129
|
+
|
|
130
|
+
- `ValidationException`: input validation problems (missing token, invalid coordinates, etc.).
|
|
131
|
+
- `FontNotFoundException`: requested font unavailable on the service.
|
|
132
|
+
- `HttpClientException`: transport or server errors with detailed context.
|
|
133
|
+
- `SessionException`: session creation and lifecycle failures.
|
|
134
|
+
|
|
135
|
+
Wrap automated workflows in `try/except` blocks to surface actionable errors to your users.
|
|
136
|
+
|
|
137
|
+
## Development
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
python -m venv venv
|
|
141
|
+
source venv/bin/activate # Windows: venv\Scripts\activate
|
|
142
|
+
pip install -e ".[dev]"
|
|
143
|
+
|
|
144
|
+
pytest -q # unit suite
|
|
145
|
+
pytest tests/e2e # integration tests (requires live API + fixtures)
|
|
146
|
+
python -m build # produce distribution artifacts
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Releases are published with `python release.py`. Contributions are welcome via pull request.
|
|
150
|
+
|
|
151
|
+
## Related SDKs
|
|
152
|
+
|
|
153
|
+
- TypeScript client: https://github.com/MenschMachine/pdfdancer-client-js
|
|
154
|
+
- Java client: https://github.com/MenschMachine/pdfdancer-client-java
|
|
155
|
+
|
|
156
|
+
## License
|
|
157
|
+
|
|
158
|
+
MIT © The Famous Cat Ltd.
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pdfdancer-client-python
|
|
3
|
+
Version: 0.2.16
|
|
4
|
+
Summary: Python client for PDFDancer API
|
|
5
|
+
Author-email: "The Famous Cat Ltd." <hi@thefamouscat.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://www.pdfdancer.com/
|
|
8
|
+
Project-URL: Repository, https://github.com/MenschMachine/pdfdancer-client-python
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: requests>=2.25.0
|
|
19
|
+
Requires-Dist: pydantic>=1.8.0
|
|
20
|
+
Requires-Dist: typing-extensions>=4.0.0
|
|
21
|
+
Requires-Dist: python-dotenv>=0.19.0
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
24
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
25
|
+
Requires-Dist: pytest-mock>=3.10.0; extra == "dev"
|
|
26
|
+
Requires-Dist: black>=22.0; extra == "dev"
|
|
27
|
+
Requires-Dist: flake8>=5.0; extra == "dev"
|
|
28
|
+
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
29
|
+
Requires-Dist: isort>=5.10.0; extra == "dev"
|
|
30
|
+
Requires-Dist: build>=0.8.0; extra == "dev"
|
|
31
|
+
Requires-Dist: twine>=4.0.0; extra == "dev"
|
|
32
|
+
|
|
33
|
+
# PDFDancer Python Client
|
|
34
|
+
|
|
35
|
+
**Getting Started with PDFDancer**
|
|
36
|
+
|
|
37
|
+
PDFDancer gives you pixel-perfect programmatic control over any PDF document from Python. Locate existing elements by
|
|
38
|
+
coordinates or text, adjust them precisely, add brand-new content, and ship the modified PDF in memory or on disk. The
|
|
39
|
+
same API is also available for TypeScript and Java, so teams can orchestrate identical PDF workflows across stacks.
|
|
40
|
+
|
|
41
|
+
> Need the raw API schema? The latest OpenAPI description lives in `docs/openapi.yml` and is published at
|
|
42
|
+
> https://bucket.pdfdancer.com/api-doc/development-0.0.yml.
|
|
43
|
+
|
|
44
|
+
## Highlights
|
|
45
|
+
|
|
46
|
+
- Locate paragraphs, text lines, images, vector paths, form fields, and pages by index, coordinates, or text prefixes.
|
|
47
|
+
- Edit existing content in place with fluent editors and context managers that apply changes safely.
|
|
48
|
+
- Programmatically control third-party PDFs—modify invoices, contracts, and reports you did not author.
|
|
49
|
+
- Add content with precise XY positioning using paragraph and image builders, custom fonts, and color helpers.
|
|
50
|
+
- Export results as bytes for downstream processing or save directly to disk with one call.
|
|
51
|
+
|
|
52
|
+
## What Makes PDFDancer Different
|
|
53
|
+
|
|
54
|
+
- **Edit any PDF**: Work with documents from customers, governments, or vendors—not just ones you generated.
|
|
55
|
+
- **Pixel-perfect positioning**: Move or add elements at exact coordinates and keep the original layout intact.
|
|
56
|
+
- **Surgical text replacement**: Swap or rewrite paragraphs without reflowing the rest of the page.
|
|
57
|
+
- **Form manipulation**: Inspect, fill, and update AcroForm fields programmatically.
|
|
58
|
+
- **Coordinate-based selection**: Select objects by position, bounding box, or text patterns.
|
|
59
|
+
- **Real PDF editing**: Modify the underlying PDF structure instead of merely stamping overlays.
|
|
60
|
+
|
|
61
|
+
## Installation
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pip install pdfdancer-client-python
|
|
65
|
+
|
|
66
|
+
# Editable install for local development
|
|
67
|
+
pip install -e .
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Requires Python 3.10+ and a PDFDancer API token.
|
|
71
|
+
|
|
72
|
+
## Quick Start — Edit an Existing PDF
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from pathlib import Path
|
|
76
|
+
from pdfdancer import Color, PDFDancer, StandardFonts
|
|
77
|
+
|
|
78
|
+
with PDFDancer.open(
|
|
79
|
+
pdf_data=Path("input.pdf"),
|
|
80
|
+
token="your-api-token", # optional when PDFDANCER_TOKEN is set
|
|
81
|
+
base_url="https://api.pdfdancer.com",
|
|
82
|
+
) as pdf:
|
|
83
|
+
# Locate and update an existing paragraph
|
|
84
|
+
heading = pdf.page(0).select_paragraphs_starting_with("Executive Summary")[0]
|
|
85
|
+
heading.move_to(72, 680)
|
|
86
|
+
with heading.edit() as editor:
|
|
87
|
+
editor.replace("Overview")
|
|
88
|
+
|
|
89
|
+
# Add a new paragraph with precise placement
|
|
90
|
+
pdf.new_paragraph() \
|
|
91
|
+
.text("Generated with PDFDancer") \
|
|
92
|
+
.font(StandardFonts.HELVETICA, 12) \
|
|
93
|
+
.color(Color(70, 70, 70)) \
|
|
94
|
+
.line_spacing(1.4) \
|
|
95
|
+
.at(page_index=0, x=72, y=520) \
|
|
96
|
+
.add()
|
|
97
|
+
|
|
98
|
+
# Persist the modified document
|
|
99
|
+
pdf.save("output.pdf")
|
|
100
|
+
# or keep it in memory
|
|
101
|
+
pdf_bytes = pdf.get_bytes()
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Create a Blank PDF
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from pathlib import Path
|
|
108
|
+
from pdfdancer import Color, PDFDancer, StandardFonts
|
|
109
|
+
|
|
110
|
+
with PDFDancer.new(token="your-api-token") as pdf:
|
|
111
|
+
pdf.new_paragraph() \
|
|
112
|
+
.text("Quarterly Summary") \
|
|
113
|
+
.font(StandardFonts.TIMES_BOLD, 18) \
|
|
114
|
+
.color(Color(10, 10, 80)) \
|
|
115
|
+
.line_spacing(1.2) \
|
|
116
|
+
.at(page_index=0, x=72, y=730) \
|
|
117
|
+
.add()
|
|
118
|
+
|
|
119
|
+
pdf.new_image() \
|
|
120
|
+
.from_file(Path("logo.png")) \
|
|
121
|
+
.at(page=0, x=420, y=710) \
|
|
122
|
+
.add()
|
|
123
|
+
|
|
124
|
+
pdf.save("summary.pdf")
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Work with Forms and Layout
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from pdfdancer import PDFDancer
|
|
131
|
+
|
|
132
|
+
with PDFDancer.open("contract.pdf") as pdf:
|
|
133
|
+
# Inspect global document structure
|
|
134
|
+
pages = pdf.pages()
|
|
135
|
+
print("Total pages:", len(pages))
|
|
136
|
+
|
|
137
|
+
# Update form fields
|
|
138
|
+
signature = pdf.select_form_fields_by_name("signature")[0]
|
|
139
|
+
signature.edit().value("Signed by Jane Doe").apply()
|
|
140
|
+
|
|
141
|
+
# Trim or move content at specific coordinates
|
|
142
|
+
images = pdf.page(1).select_images()
|
|
143
|
+
for image in images:
|
|
144
|
+
x = image.position.x()
|
|
145
|
+
if x is not None and x < 100:
|
|
146
|
+
image.delete()
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Selectors return typed objects (`ParagraphObject`, `TextLineObject`, `ImageObject`, `FormFieldObject`, `PageClient`, …)
|
|
150
|
+
with helpers such as `delete()`, `move_to(x, y)`, or `edit()` depending on the object type.
|
|
151
|
+
|
|
152
|
+
## Configuration
|
|
153
|
+
|
|
154
|
+
- Set `PDFDANCER_TOKEN` for authentication (preferred for local development and CI).
|
|
155
|
+
- Override the API host with `PDFDANCER_BASE_URL` (e.g., sandbox environments).
|
|
156
|
+
- Tune HTTP read timeouts via the `timeout` argument on `PDFDancer.open()` and `PDFDancer.new()`.
|
|
157
|
+
|
|
158
|
+
## Error Handling
|
|
159
|
+
|
|
160
|
+
Operations raise subclasses of `PdfDancerException`:
|
|
161
|
+
|
|
162
|
+
- `ValidationException`: input validation problems (missing token, invalid coordinates, etc.).
|
|
163
|
+
- `FontNotFoundException`: requested font unavailable on the service.
|
|
164
|
+
- `HttpClientException`: transport or server errors with detailed context.
|
|
165
|
+
- `SessionException`: session creation and lifecycle failures.
|
|
166
|
+
|
|
167
|
+
Wrap automated workflows in `try/except` blocks to surface actionable errors to your users.
|
|
168
|
+
|
|
169
|
+
## Development
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
python -m venv venv
|
|
173
|
+
source venv/bin/activate # Windows: venv\Scripts\activate
|
|
174
|
+
pip install -e ".[dev]"
|
|
175
|
+
|
|
176
|
+
pytest -q # unit suite
|
|
177
|
+
pytest tests/e2e # integration tests (requires live API + fixtures)
|
|
178
|
+
python -m build # produce distribution artifacts
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Releases are published with `python release.py`. Contributions are welcome via pull request.
|
|
182
|
+
|
|
183
|
+
## Related SDKs
|
|
184
|
+
|
|
185
|
+
- TypeScript client: https://github.com/MenschMachine/pdfdancer-client-js
|
|
186
|
+
- Java client: https://github.com/MenschMachine/pdfdancer-client-java
|
|
187
|
+
|
|
188
|
+
## License
|
|
189
|
+
|
|
190
|
+
MIT © The Famous Cat Ltd.
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: pdfdancer-client-python
|
|
3
|
-
Version: 0.2.15
|
|
4
|
-
Summary: Python client for PDFDancer API
|
|
5
|
-
Author-email: "The Famous Cat Ltd." <hi@thefamouscat.com>
|
|
6
|
-
License: MIT
|
|
7
|
-
Project-URL: Homepage, https://www.pdfdancer.com/
|
|
8
|
-
Project-URL: Repository, https://github.com/MenschMachine/pdfdancer-client-python
|
|
9
|
-
Classifier: Development Status :: 4 - Beta
|
|
10
|
-
Classifier: Intended Audience :: Developers
|
|
11
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
-
Requires-Python: >=3.10
|
|
17
|
-
Description-Content-Type: text/markdown
|
|
18
|
-
Requires-Dist: requests>=2.25.0
|
|
19
|
-
Requires-Dist: pydantic>=1.8.0
|
|
20
|
-
Requires-Dist: typing-extensions>=4.0.0
|
|
21
|
-
Requires-Dist: python-dotenv>=0.19.0
|
|
22
|
-
Provides-Extra: dev
|
|
23
|
-
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
24
|
-
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
25
|
-
Requires-Dist: pytest-mock>=3.10.0; extra == "dev"
|
|
26
|
-
Requires-Dist: black>=22.0; extra == "dev"
|
|
27
|
-
Requires-Dist: flake8>=5.0; extra == "dev"
|
|
28
|
-
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
29
|
-
Requires-Dist: isort>=5.10.0; extra == "dev"
|
|
30
|
-
Requires-Dist: build>=0.8.0; extra == "dev"
|
|
31
|
-
Requires-Dist: twine>=4.0.0; extra == "dev"
|
|
32
|
-
|
|
33
|
-
# PDFDancer Python Client
|
|
34
|
-
|
|
35
|
-
Automate PDF clean-up, redaction, form filling, and content injection against the PDFDancer API from Python. The client
|
|
36
|
-
gives you page-scoped selectors, fluent editors, and builders so you can read, modify, and export PDFs programmatically
|
|
37
|
-
in just a few lines.
|
|
38
|
-
|
|
39
|
-
Latest schema version available at https://bucket.pdfdancer.com/api-doc/development-0.0.yml.
|
|
40
|
-
|
|
41
|
-
## Highlights
|
|
42
|
-
|
|
43
|
-
- Locate anything inside a PDF—paragraphs, text lines, images, vector paths, pages, AcroForm fields—by page,
|
|
44
|
-
coordinates, or text prefixes
|
|
45
|
-
- Edit or delete existing content with fluent paragraph/text editors and safe apply-on-exit context managers
|
|
46
|
-
- Fill or update form fields and propagate the changes back to the document instantly
|
|
47
|
-
- Add brand-new content with paragraph/image builders, custom fonts, and precise page positioning
|
|
48
|
-
- Download results as bytes for downstream processing or save directly to disk with one method call
|
|
49
|
-
|
|
50
|
-
## Core Capabilities
|
|
51
|
-
|
|
52
|
-
- Clean up layout by moving or deleting paragraphs, text lines, or shapes on specific pages
|
|
53
|
-
- Search and filter content (e.g., paragraphs starting with "Invoice") to drive custom workflows
|
|
54
|
-
- Redact or replace text in bulk with chained editor operations
|
|
55
|
-
- Populate AcroForms for contract generation or onboarding flows
|
|
56
|
-
- Insert logos, signatures, and generated paragraphs at deterministic coordinates
|
|
57
|
-
- Export modified PDFs as bytes for APIs, S3 uploads, or direct file saves
|
|
58
|
-
|
|
59
|
-
## Requirements
|
|
60
|
-
|
|
61
|
-
- Python 3.10 or newer
|
|
62
|
-
- A PDFDancer API token (set `PDFDANCER_TOKEN` or pass `token=...`)
|
|
63
|
-
- Network access to a PDFDancer service (defaults to `https://api.pdfdancer.com`; override with `PDFDANCER_BASE_URL`)
|
|
64
|
-
|
|
65
|
-
## Installation
|
|
66
|
-
|
|
67
|
-
```bash
|
|
68
|
-
pip install pdfdancer-client-python
|
|
69
|
-
|
|
70
|
-
# Editable install for local development
|
|
71
|
-
pip install -e .
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
## Getting Started
|
|
75
|
-
|
|
76
|
-
```python
|
|
77
|
-
from pathlib import Path
|
|
78
|
-
from pdfdancer import Color, PDFDancer
|
|
79
|
-
|
|
80
|
-
with PDFDancer.open(
|
|
81
|
-
pdf_data=Path("input.pdf"),
|
|
82
|
-
token="your-api-token", # optional when PDFDANCER_TOKEN is set
|
|
83
|
-
base_url="https://api.pdfdancer.com",
|
|
84
|
-
) as pdf:
|
|
85
|
-
# Locate existing content
|
|
86
|
-
heading = pdf.page(0).select_paragraphs_starting_with("Executive Summary")[0]
|
|
87
|
-
heading.edit().replace("Overview").apply()
|
|
88
|
-
|
|
89
|
-
# Add a new paragraph using the fluent builder
|
|
90
|
-
pdf.new_paragraph()
|
|
91
|
-
.text("Generated with PDFDancer")
|
|
92
|
-
.font("Helvetica", 12)
|
|
93
|
-
.color(Color(70, 70, 70))
|
|
94
|
-
.line_spacing(1.4)
|
|
95
|
-
.at(page_index=0, x=72, y=520)
|
|
96
|
-
.add()
|
|
97
|
-
|
|
98
|
-
# Persist the modified document
|
|
99
|
-
pdf.save("output.pdf")
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
### Authentication Tips
|
|
103
|
-
|
|
104
|
-
- Prefer setting `PDFDANCER_TOKEN` in your environment for local development.
|
|
105
|
-
- Override the API host by setting `PDFDANCER_BASE_URL` or passing `base_url="https://sandbox.pdfdancer.com"`.
|
|
106
|
-
- Use the `timeout` parameter on `PDFDancer.open()` to adjust HTTP read timeouts.
|
|
107
|
-
|
|
108
|
-
## Selecting PDF Content
|
|
109
|
-
|
|
110
|
-
```python
|
|
111
|
-
with PDFDancer.open("report.pdf") as pdf: # environment variables provide token/URL
|
|
112
|
-
all_paragraphs = pdf.select_paragraphs()
|
|
113
|
-
page_zero_images = pdf.page(0).select_images()
|
|
114
|
-
form_fields = pdf.page(2).select_form_fields()
|
|
115
|
-
paths_at_cursor = pdf.page(3).select_paths_at(x=150, y=320)
|
|
116
|
-
|
|
117
|
-
page = pdf.page(0).get()
|
|
118
|
-
print(page.internal_id, page.position.bounding_rect)
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
Selectors return rich objects (`ParagraphObject`, `TextLineObject`, `ImageObject`, `FormFieldObject`, etc.) with helpers
|
|
122
|
-
such as `delete()`, `move_to(x, y)`, or `edit()` depending on the object type.
|
|
123
|
-
|
|
124
|
-
## Editing Text and Forms
|
|
125
|
-
|
|
126
|
-
```python
|
|
127
|
-
with PDFDancer.open("report.pdf") as pdf:
|
|
128
|
-
paragraph = pdf.page(0).select_paragraphs_starting_with("Disclaimer")[0]
|
|
129
|
-
|
|
130
|
-
# Chain updates explicitly…
|
|
131
|
-
paragraph.edit()
|
|
132
|
-
.replace("Updated disclaimer text")
|
|
133
|
-
.font("Roboto-Regular", 11)
|
|
134
|
-
.line_spacing(1.1)
|
|
135
|
-
.move_to(72, 140)
|
|
136
|
-
.apply()
|
|
137
|
-
|
|
138
|
-
# …or use the context manager to auto-apply on success
|
|
139
|
-
with paragraph.edit() as edit:
|
|
140
|
-
edit.replace("Context-managed update").color(Color(120, 0, 0))
|
|
141
|
-
|
|
142
|
-
# Update an AcroForm field
|
|
143
|
-
field = pdf.page(1).select_form_fields_by_name("signature")[0]
|
|
144
|
-
field.edit().value("Signed by Jane Doe").apply()
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
## Adding New Content
|
|
148
|
-
|
|
149
|
-
```python
|
|
150
|
-
with PDFDancer.open("report.pdf") as pdf:
|
|
151
|
-
# Register fonts from the service
|
|
152
|
-
fonts = pdf.find_fonts("Roboto", 12)
|
|
153
|
-
pdf.register_font("/path/to/custom.ttf")
|
|
154
|
-
|
|
155
|
-
# Paragraphs
|
|
156
|
-
pdf.new_paragraph()
|
|
157
|
-
.text("Greetings from PDFDancer!")
|
|
158
|
-
.font(fonts[0].name, fonts[0].size)
|
|
159
|
-
.at(page_index=0, x=220, y=480)
|
|
160
|
-
.add()
|
|
161
|
-
|
|
162
|
-
# Raster images
|
|
163
|
-
pdf.new_image()
|
|
164
|
-
.from_file(Path("logo.png"))
|
|
165
|
-
.at(page=0, x=48, y=700)
|
|
166
|
-
.add()
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
## Downloading Results
|
|
170
|
-
|
|
171
|
-
- `pdf.get_pdf_file()` returns the modified PDF as `bytes` (ideal for storage services or HTTP responses).
|
|
172
|
-
- `pdf.save("output.pdf")` writes directly to disk, creating directories when needed.
|
|
173
|
-
|
|
174
|
-
## Error Handling
|
|
175
|
-
|
|
176
|
-
Most operations raise subclasses of `PdfDancerException`:
|
|
177
|
-
|
|
178
|
-
- `ValidationException` for client-side validation issues (missing token, invalid coordinates, etc.).
|
|
179
|
-
- `FontNotFoundException` when the service cannot locate a requested font.
|
|
180
|
-
- `HttpClientException` for transport or server errors with detailed messages.
|
|
181
|
-
- `SessionException` when session creation fails.
|
|
182
|
-
|
|
183
|
-
Wrap complex workflows in `try/except` blocks to surface actionable errors to your users.
|
|
184
|
-
|
|
185
|
-
## Local Development
|
|
186
|
-
|
|
187
|
-
```bash
|
|
188
|
-
python -m venv venv
|
|
189
|
-
source venv/bin/activate # Windows: venv\Scripts\activate
|
|
190
|
-
pip install -e ".[dev]"
|
|
191
|
-
|
|
192
|
-
pytest -q # run the fast unit suite
|
|
193
|
-
pytest tests/e2e # integration tests (requires live API + fixtures)
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
Package builds are handled by `python -m build`, and release artifacts are published via `python release.py`.
|
|
197
|
-
|
|
198
|
-
## License
|
|
199
|
-
|
|
200
|
-
MIT © The Famous Cat Ltd.
|
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
# PDFDancer Python Client
|
|
2
|
-
|
|
3
|
-
Automate PDF clean-up, redaction, form filling, and content injection against the PDFDancer API from Python. The client
|
|
4
|
-
gives you page-scoped selectors, fluent editors, and builders so you can read, modify, and export PDFs programmatically
|
|
5
|
-
in just a few lines.
|
|
6
|
-
|
|
7
|
-
Latest schema version available at https://bucket.pdfdancer.com/api-doc/development-0.0.yml.
|
|
8
|
-
|
|
9
|
-
## Highlights
|
|
10
|
-
|
|
11
|
-
- Locate anything inside a PDF—paragraphs, text lines, images, vector paths, pages, AcroForm fields—by page,
|
|
12
|
-
coordinates, or text prefixes
|
|
13
|
-
- Edit or delete existing content with fluent paragraph/text editors and safe apply-on-exit context managers
|
|
14
|
-
- Fill or update form fields and propagate the changes back to the document instantly
|
|
15
|
-
- Add brand-new content with paragraph/image builders, custom fonts, and precise page positioning
|
|
16
|
-
- Download results as bytes for downstream processing or save directly to disk with one method call
|
|
17
|
-
|
|
18
|
-
## Core Capabilities
|
|
19
|
-
|
|
20
|
-
- Clean up layout by moving or deleting paragraphs, text lines, or shapes on specific pages
|
|
21
|
-
- Search and filter content (e.g., paragraphs starting with "Invoice") to drive custom workflows
|
|
22
|
-
- Redact or replace text in bulk with chained editor operations
|
|
23
|
-
- Populate AcroForms for contract generation or onboarding flows
|
|
24
|
-
- Insert logos, signatures, and generated paragraphs at deterministic coordinates
|
|
25
|
-
- Export modified PDFs as bytes for APIs, S3 uploads, or direct file saves
|
|
26
|
-
|
|
27
|
-
## Requirements
|
|
28
|
-
|
|
29
|
-
- Python 3.10 or newer
|
|
30
|
-
- A PDFDancer API token (set `PDFDANCER_TOKEN` or pass `token=...`)
|
|
31
|
-
- Network access to a PDFDancer service (defaults to `https://api.pdfdancer.com`; override with `PDFDANCER_BASE_URL`)
|
|
32
|
-
|
|
33
|
-
## Installation
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
pip install pdfdancer-client-python
|
|
37
|
-
|
|
38
|
-
# Editable install for local development
|
|
39
|
-
pip install -e .
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
## Getting Started
|
|
43
|
-
|
|
44
|
-
```python
|
|
45
|
-
from pathlib import Path
|
|
46
|
-
from pdfdancer import Color, PDFDancer
|
|
47
|
-
|
|
48
|
-
with PDFDancer.open(
|
|
49
|
-
pdf_data=Path("input.pdf"),
|
|
50
|
-
token="your-api-token", # optional when PDFDANCER_TOKEN is set
|
|
51
|
-
base_url="https://api.pdfdancer.com",
|
|
52
|
-
) as pdf:
|
|
53
|
-
# Locate existing content
|
|
54
|
-
heading = pdf.page(0).select_paragraphs_starting_with("Executive Summary")[0]
|
|
55
|
-
heading.edit().replace("Overview").apply()
|
|
56
|
-
|
|
57
|
-
# Add a new paragraph using the fluent builder
|
|
58
|
-
pdf.new_paragraph()
|
|
59
|
-
.text("Generated with PDFDancer")
|
|
60
|
-
.font("Helvetica", 12)
|
|
61
|
-
.color(Color(70, 70, 70))
|
|
62
|
-
.line_spacing(1.4)
|
|
63
|
-
.at(page_index=0, x=72, y=520)
|
|
64
|
-
.add()
|
|
65
|
-
|
|
66
|
-
# Persist the modified document
|
|
67
|
-
pdf.save("output.pdf")
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
### Authentication Tips
|
|
71
|
-
|
|
72
|
-
- Prefer setting `PDFDANCER_TOKEN` in your environment for local development.
|
|
73
|
-
- Override the API host by setting `PDFDANCER_BASE_URL` or passing `base_url="https://sandbox.pdfdancer.com"`.
|
|
74
|
-
- Use the `timeout` parameter on `PDFDancer.open()` to adjust HTTP read timeouts.
|
|
75
|
-
|
|
76
|
-
## Selecting PDF Content
|
|
77
|
-
|
|
78
|
-
```python
|
|
79
|
-
with PDFDancer.open("report.pdf") as pdf: # environment variables provide token/URL
|
|
80
|
-
all_paragraphs = pdf.select_paragraphs()
|
|
81
|
-
page_zero_images = pdf.page(0).select_images()
|
|
82
|
-
form_fields = pdf.page(2).select_form_fields()
|
|
83
|
-
paths_at_cursor = pdf.page(3).select_paths_at(x=150, y=320)
|
|
84
|
-
|
|
85
|
-
page = pdf.page(0).get()
|
|
86
|
-
print(page.internal_id, page.position.bounding_rect)
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
Selectors return rich objects (`ParagraphObject`, `TextLineObject`, `ImageObject`, `FormFieldObject`, etc.) with helpers
|
|
90
|
-
such as `delete()`, `move_to(x, y)`, or `edit()` depending on the object type.
|
|
91
|
-
|
|
92
|
-
## Editing Text and Forms
|
|
93
|
-
|
|
94
|
-
```python
|
|
95
|
-
with PDFDancer.open("report.pdf") as pdf:
|
|
96
|
-
paragraph = pdf.page(0).select_paragraphs_starting_with("Disclaimer")[0]
|
|
97
|
-
|
|
98
|
-
# Chain updates explicitly…
|
|
99
|
-
paragraph.edit()
|
|
100
|
-
.replace("Updated disclaimer text")
|
|
101
|
-
.font("Roboto-Regular", 11)
|
|
102
|
-
.line_spacing(1.1)
|
|
103
|
-
.move_to(72, 140)
|
|
104
|
-
.apply()
|
|
105
|
-
|
|
106
|
-
# …or use the context manager to auto-apply on success
|
|
107
|
-
with paragraph.edit() as edit:
|
|
108
|
-
edit.replace("Context-managed update").color(Color(120, 0, 0))
|
|
109
|
-
|
|
110
|
-
# Update an AcroForm field
|
|
111
|
-
field = pdf.page(1).select_form_fields_by_name("signature")[0]
|
|
112
|
-
field.edit().value("Signed by Jane Doe").apply()
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
## Adding New Content
|
|
116
|
-
|
|
117
|
-
```python
|
|
118
|
-
with PDFDancer.open("report.pdf") as pdf:
|
|
119
|
-
# Register fonts from the service
|
|
120
|
-
fonts = pdf.find_fonts("Roboto", 12)
|
|
121
|
-
pdf.register_font("/path/to/custom.ttf")
|
|
122
|
-
|
|
123
|
-
# Paragraphs
|
|
124
|
-
pdf.new_paragraph()
|
|
125
|
-
.text("Greetings from PDFDancer!")
|
|
126
|
-
.font(fonts[0].name, fonts[0].size)
|
|
127
|
-
.at(page_index=0, x=220, y=480)
|
|
128
|
-
.add()
|
|
129
|
-
|
|
130
|
-
# Raster images
|
|
131
|
-
pdf.new_image()
|
|
132
|
-
.from_file(Path("logo.png"))
|
|
133
|
-
.at(page=0, x=48, y=700)
|
|
134
|
-
.add()
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
## Downloading Results
|
|
138
|
-
|
|
139
|
-
- `pdf.get_pdf_file()` returns the modified PDF as `bytes` (ideal for storage services or HTTP responses).
|
|
140
|
-
- `pdf.save("output.pdf")` writes directly to disk, creating directories when needed.
|
|
141
|
-
|
|
142
|
-
## Error Handling
|
|
143
|
-
|
|
144
|
-
Most operations raise subclasses of `PdfDancerException`:
|
|
145
|
-
|
|
146
|
-
- `ValidationException` for client-side validation issues (missing token, invalid coordinates, etc.).
|
|
147
|
-
- `FontNotFoundException` when the service cannot locate a requested font.
|
|
148
|
-
- `HttpClientException` for transport or server errors with detailed messages.
|
|
149
|
-
- `SessionException` when session creation fails.
|
|
150
|
-
|
|
151
|
-
Wrap complex workflows in `try/except` blocks to surface actionable errors to your users.
|
|
152
|
-
|
|
153
|
-
## Local Development
|
|
154
|
-
|
|
155
|
-
```bash
|
|
156
|
-
python -m venv venv
|
|
157
|
-
source venv/bin/activate # Windows: venv\Scripts\activate
|
|
158
|
-
pip install -e ".[dev]"
|
|
159
|
-
|
|
160
|
-
pytest -q # run the fast unit suite
|
|
161
|
-
pytest tests/e2e # integration tests (requires live API + fixtures)
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
Package builds are handled by `python -m build`, and release artifacts are published via `python release.py`.
|
|
165
|
-
|
|
166
|
-
## License
|
|
167
|
-
|
|
168
|
-
MIT © The Famous Cat Ltd.
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: pdfdancer-client-python
|
|
3
|
-
Version: 0.2.15
|
|
4
|
-
Summary: Python client for PDFDancer API
|
|
5
|
-
Author-email: "The Famous Cat Ltd." <hi@thefamouscat.com>
|
|
6
|
-
License: MIT
|
|
7
|
-
Project-URL: Homepage, https://www.pdfdancer.com/
|
|
8
|
-
Project-URL: Repository, https://github.com/MenschMachine/pdfdancer-client-python
|
|
9
|
-
Classifier: Development Status :: 4 - Beta
|
|
10
|
-
Classifier: Intended Audience :: Developers
|
|
11
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
-
Requires-Python: >=3.10
|
|
17
|
-
Description-Content-Type: text/markdown
|
|
18
|
-
Requires-Dist: requests>=2.25.0
|
|
19
|
-
Requires-Dist: pydantic>=1.8.0
|
|
20
|
-
Requires-Dist: typing-extensions>=4.0.0
|
|
21
|
-
Requires-Dist: python-dotenv>=0.19.0
|
|
22
|
-
Provides-Extra: dev
|
|
23
|
-
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
24
|
-
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
25
|
-
Requires-Dist: pytest-mock>=3.10.0; extra == "dev"
|
|
26
|
-
Requires-Dist: black>=22.0; extra == "dev"
|
|
27
|
-
Requires-Dist: flake8>=5.0; extra == "dev"
|
|
28
|
-
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
29
|
-
Requires-Dist: isort>=5.10.0; extra == "dev"
|
|
30
|
-
Requires-Dist: build>=0.8.0; extra == "dev"
|
|
31
|
-
Requires-Dist: twine>=4.0.0; extra == "dev"
|
|
32
|
-
|
|
33
|
-
# PDFDancer Python Client
|
|
34
|
-
|
|
35
|
-
Automate PDF clean-up, redaction, form filling, and content injection against the PDFDancer API from Python. The client
|
|
36
|
-
gives you page-scoped selectors, fluent editors, and builders so you can read, modify, and export PDFs programmatically
|
|
37
|
-
in just a few lines.
|
|
38
|
-
|
|
39
|
-
Latest schema version available at https://bucket.pdfdancer.com/api-doc/development-0.0.yml.
|
|
40
|
-
|
|
41
|
-
## Highlights
|
|
42
|
-
|
|
43
|
-
- Locate anything inside a PDF—paragraphs, text lines, images, vector paths, pages, AcroForm fields—by page,
|
|
44
|
-
coordinates, or text prefixes
|
|
45
|
-
- Edit or delete existing content with fluent paragraph/text editors and safe apply-on-exit context managers
|
|
46
|
-
- Fill or update form fields and propagate the changes back to the document instantly
|
|
47
|
-
- Add brand-new content with paragraph/image builders, custom fonts, and precise page positioning
|
|
48
|
-
- Download results as bytes for downstream processing or save directly to disk with one method call
|
|
49
|
-
|
|
50
|
-
## Core Capabilities
|
|
51
|
-
|
|
52
|
-
- Clean up layout by moving or deleting paragraphs, text lines, or shapes on specific pages
|
|
53
|
-
- Search and filter content (e.g., paragraphs starting with "Invoice") to drive custom workflows
|
|
54
|
-
- Redact or replace text in bulk with chained editor operations
|
|
55
|
-
- Populate AcroForms for contract generation or onboarding flows
|
|
56
|
-
- Insert logos, signatures, and generated paragraphs at deterministic coordinates
|
|
57
|
-
- Export modified PDFs as bytes for APIs, S3 uploads, or direct file saves
|
|
58
|
-
|
|
59
|
-
## Requirements
|
|
60
|
-
|
|
61
|
-
- Python 3.10 or newer
|
|
62
|
-
- A PDFDancer API token (set `PDFDANCER_TOKEN` or pass `token=...`)
|
|
63
|
-
- Network access to a PDFDancer service (defaults to `https://api.pdfdancer.com`; override with `PDFDANCER_BASE_URL`)
|
|
64
|
-
|
|
65
|
-
## Installation
|
|
66
|
-
|
|
67
|
-
```bash
|
|
68
|
-
pip install pdfdancer-client-python
|
|
69
|
-
|
|
70
|
-
# Editable install for local development
|
|
71
|
-
pip install -e .
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
## Getting Started
|
|
75
|
-
|
|
76
|
-
```python
|
|
77
|
-
from pathlib import Path
|
|
78
|
-
from pdfdancer import Color, PDFDancer
|
|
79
|
-
|
|
80
|
-
with PDFDancer.open(
|
|
81
|
-
pdf_data=Path("input.pdf"),
|
|
82
|
-
token="your-api-token", # optional when PDFDANCER_TOKEN is set
|
|
83
|
-
base_url="https://api.pdfdancer.com",
|
|
84
|
-
) as pdf:
|
|
85
|
-
# Locate existing content
|
|
86
|
-
heading = pdf.page(0).select_paragraphs_starting_with("Executive Summary")[0]
|
|
87
|
-
heading.edit().replace("Overview").apply()
|
|
88
|
-
|
|
89
|
-
# Add a new paragraph using the fluent builder
|
|
90
|
-
pdf.new_paragraph()
|
|
91
|
-
.text("Generated with PDFDancer")
|
|
92
|
-
.font("Helvetica", 12)
|
|
93
|
-
.color(Color(70, 70, 70))
|
|
94
|
-
.line_spacing(1.4)
|
|
95
|
-
.at(page_index=0, x=72, y=520)
|
|
96
|
-
.add()
|
|
97
|
-
|
|
98
|
-
# Persist the modified document
|
|
99
|
-
pdf.save("output.pdf")
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
### Authentication Tips
|
|
103
|
-
|
|
104
|
-
- Prefer setting `PDFDANCER_TOKEN` in your environment for local development.
|
|
105
|
-
- Override the API host by setting `PDFDANCER_BASE_URL` or passing `base_url="https://sandbox.pdfdancer.com"`.
|
|
106
|
-
- Use the `timeout` parameter on `PDFDancer.open()` to adjust HTTP read timeouts.
|
|
107
|
-
|
|
108
|
-
## Selecting PDF Content
|
|
109
|
-
|
|
110
|
-
```python
|
|
111
|
-
with PDFDancer.open("report.pdf") as pdf: # environment variables provide token/URL
|
|
112
|
-
all_paragraphs = pdf.select_paragraphs()
|
|
113
|
-
page_zero_images = pdf.page(0).select_images()
|
|
114
|
-
form_fields = pdf.page(2).select_form_fields()
|
|
115
|
-
paths_at_cursor = pdf.page(3).select_paths_at(x=150, y=320)
|
|
116
|
-
|
|
117
|
-
page = pdf.page(0).get()
|
|
118
|
-
print(page.internal_id, page.position.bounding_rect)
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
Selectors return rich objects (`ParagraphObject`, `TextLineObject`, `ImageObject`, `FormFieldObject`, etc.) with helpers
|
|
122
|
-
such as `delete()`, `move_to(x, y)`, or `edit()` depending on the object type.
|
|
123
|
-
|
|
124
|
-
## Editing Text and Forms
|
|
125
|
-
|
|
126
|
-
```python
|
|
127
|
-
with PDFDancer.open("report.pdf") as pdf:
|
|
128
|
-
paragraph = pdf.page(0).select_paragraphs_starting_with("Disclaimer")[0]
|
|
129
|
-
|
|
130
|
-
# Chain updates explicitly…
|
|
131
|
-
paragraph.edit()
|
|
132
|
-
.replace("Updated disclaimer text")
|
|
133
|
-
.font("Roboto-Regular", 11)
|
|
134
|
-
.line_spacing(1.1)
|
|
135
|
-
.move_to(72, 140)
|
|
136
|
-
.apply()
|
|
137
|
-
|
|
138
|
-
# …or use the context manager to auto-apply on success
|
|
139
|
-
with paragraph.edit() as edit:
|
|
140
|
-
edit.replace("Context-managed update").color(Color(120, 0, 0))
|
|
141
|
-
|
|
142
|
-
# Update an AcroForm field
|
|
143
|
-
field = pdf.page(1).select_form_fields_by_name("signature")[0]
|
|
144
|
-
field.edit().value("Signed by Jane Doe").apply()
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
## Adding New Content
|
|
148
|
-
|
|
149
|
-
```python
|
|
150
|
-
with PDFDancer.open("report.pdf") as pdf:
|
|
151
|
-
# Register fonts from the service
|
|
152
|
-
fonts = pdf.find_fonts("Roboto", 12)
|
|
153
|
-
pdf.register_font("/path/to/custom.ttf")
|
|
154
|
-
|
|
155
|
-
# Paragraphs
|
|
156
|
-
pdf.new_paragraph()
|
|
157
|
-
.text("Greetings from PDFDancer!")
|
|
158
|
-
.font(fonts[0].name, fonts[0].size)
|
|
159
|
-
.at(page_index=0, x=220, y=480)
|
|
160
|
-
.add()
|
|
161
|
-
|
|
162
|
-
# Raster images
|
|
163
|
-
pdf.new_image()
|
|
164
|
-
.from_file(Path("logo.png"))
|
|
165
|
-
.at(page=0, x=48, y=700)
|
|
166
|
-
.add()
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
## Downloading Results
|
|
170
|
-
|
|
171
|
-
- `pdf.get_pdf_file()` returns the modified PDF as `bytes` (ideal for storage services or HTTP responses).
|
|
172
|
-
- `pdf.save("output.pdf")` writes directly to disk, creating directories when needed.
|
|
173
|
-
|
|
174
|
-
## Error Handling
|
|
175
|
-
|
|
176
|
-
Most operations raise subclasses of `PdfDancerException`:
|
|
177
|
-
|
|
178
|
-
- `ValidationException` for client-side validation issues (missing token, invalid coordinates, etc.).
|
|
179
|
-
- `FontNotFoundException` when the service cannot locate a requested font.
|
|
180
|
-
- `HttpClientException` for transport or server errors with detailed messages.
|
|
181
|
-
- `SessionException` when session creation fails.
|
|
182
|
-
|
|
183
|
-
Wrap complex workflows in `try/except` blocks to surface actionable errors to your users.
|
|
184
|
-
|
|
185
|
-
## Local Development
|
|
186
|
-
|
|
187
|
-
```bash
|
|
188
|
-
python -m venv venv
|
|
189
|
-
source venv/bin/activate # Windows: venv\Scripts\activate
|
|
190
|
-
pip install -e ".[dev]"
|
|
191
|
-
|
|
192
|
-
pytest -q # run the fast unit suite
|
|
193
|
-
pytest tests/e2e # integration tests (requires live API + fixtures)
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
Package builds are handled by `python -m build`, and release artifacts are published via `python release.py`.
|
|
197
|
-
|
|
198
|
-
## License
|
|
199
|
-
|
|
200
|
-
MIT © The Famous Cat Ltd.
|
{pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/.claude/commands/discuss.md
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
|
{pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/src/pdfdancer/exceptions.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/src/pdfdancer/image_builder.py
RENAMED
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/src/pdfdancer/paragraph_builder.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/src/pdfdancer/pdfdancer_v1.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
|
{pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/e2e/pdf_assertions.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/e2e/test_acroform.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/e2e/test_form_x_objects.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/e2e/test_paragraph.py
RENAMED
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/e2e/test_pdfdancer.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/e2e/test_positioning.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/fixtures/basic-paths.pdf
RENAMED
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/fixtures/logo-80.png
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/test_openapi_compliance.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/test_pdf_object_equality.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.2.15 → pdfdancer_client_python-0.2.16}/tests/test_standard_fonts.py
RENAMED
|
File without changes
|