pdfdancer-client-python 0.3.13__py3-none-any.whl → 3.0.0__py3-none-any.whl
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/__init__.py +79 -22
- pdfdancer/_runtime_version.py +30 -0
- pdfdancer/_version.py +24 -0
- pdfdancer/image_builder.py +23 -3
- pdfdancer/models.py +127 -470
- pdfdancer/page_builder.py +6 -17
- pdfdancer/path_builder.py +127 -6
- pdfdancer/{pdfdancer_v1.py → pdfdancer_v2.py} +850 -1316
- pdfdancer/text_editing.py +1472 -0
- pdfdancer/types.py +94 -399
- pdfdancer_client_python-3.0.0.dist-info/METADATA +521 -0
- pdfdancer_client_python-3.0.0.dist-info/RECORD +18 -0
- {pdfdancer_client_python-0.3.13.dist-info → pdfdancer_client_python-3.0.0.dist-info}/WHEEL +1 -1
- pdfdancer/paragraph_builder.py +0 -554
- pdfdancer/text_line_builder.py +0 -290
- pdfdancer_client_python-0.3.13.dist-info/METADATA +0 -685
- pdfdancer_client_python-0.3.13.dist-info/RECORD +0 -17
- {pdfdancer_client_python-0.3.13.dist-info → pdfdancer_client_python-3.0.0.dist-info}/licenses/LICENSE +0 -0
- {pdfdancer_client_python-0.3.13.dist-info → pdfdancer_client_python-3.0.0.dist-info}/licenses/NOTICE +0 -0
- {pdfdancer_client_python-0.3.13.dist-info → pdfdancer_client_python-3.0.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pdfdancer-client-python
|
|
3
|
+
Version: 3.0.0
|
|
4
|
+
Summary: Python client for PDFDancer API
|
|
5
|
+
Author-email: "The Famous Cat Ltd." <hi@thefamouscat.com>
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://www.pdfdancer.com/
|
|
8
|
+
Project-URL: Documentation, https://www.pdfdancer.com/
|
|
9
|
+
Project-URL: Source, https://github.com/MenschMachine/pdfdancer-client-python
|
|
10
|
+
Project-URL: Issues, https://github.com/MenschMachine/pdfdancer-client-python/issues
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
License-File: NOTICE
|
|
21
|
+
Requires-Dist: httpx[http2]>=0.27.0
|
|
22
|
+
Requires-Dist: pydantic>=1.8.0
|
|
23
|
+
Requires-Dist: typing-extensions>=4.0.0
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
26
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest-mock>=3.10.0; extra == "dev"
|
|
28
|
+
Requires-Dist: pypdf>=5.0.0; extra == "dev"
|
|
29
|
+
Requires-Dist: black>=22.0; extra == "dev"
|
|
30
|
+
Requires-Dist: flake8>=5.0; extra == "dev"
|
|
31
|
+
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
32
|
+
Requires-Dist: isort>=5.10.0; extra == "dev"
|
|
33
|
+
Requires-Dist: build>=0.8.0; extra == "dev"
|
|
34
|
+
Requires-Dist: twine>=4.0.0; extra == "dev"
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
# PDFDancer Python Client
|
|
38
|
+
|
|
39
|
+
This README documents `pdfdancer-client-python` version `3.0.0`.
|
|
40
|
+
|
|
41
|
+

|
|
42
|
+
|
|
43
|
+
## Overview
|
|
44
|
+
|
|
45
|
+
### PDF used to be read-only. We fixed that.
|
|
46
|
+
|
|
47
|
+
Edit text in real-world PDFs—even ones you didn't create. Move images, reposition headers, and change fonts with
|
|
48
|
+
pixel-perfect control from Python. The same API is also available for TypeScript and Java.
|
|
49
|
+
|
|
50
|
+
### What Makes PDFDancer Different
|
|
51
|
+
|
|
52
|
+
- **Edit text in real-world PDFs**: Work with documents from customers, governments, or vendors—even ones you didn't create.
|
|
53
|
+
- **Pixel-perfect positioning**: Move or add elements at exact coordinates and keep the original layout intact.
|
|
54
|
+
- **Selector-based text editing**: Apply literal or regular-expression replacements with page scoping and explicit layout policy.
|
|
55
|
+
- **Form manipulation**: Inspect, fill, and update AcroForm fields programmatically.
|
|
56
|
+
- **Coordinate-based selection**: Select objects by position, bounding box, or text patterns.
|
|
57
|
+
- **Vector graphics**: Draw lines, rectangles, and Bezier curves with full control over stroke and fill properties.
|
|
58
|
+
- **Real PDF editing**: Modify the underlying PDF structure instead of merely stamping overlays.
|
|
59
|
+
|
|
60
|
+
## Highlights
|
|
61
|
+
|
|
62
|
+
- Replace, insert, delete, and style text with selector-based v2 operations.
|
|
63
|
+
- Locate images, vector paths, form fields, and pages by page number or coordinates; inspect text-line data through snapshots.
|
|
64
|
+
- Programmatically control third-party PDFs—modify invoices, contracts, and reports you did not author.
|
|
65
|
+
- Add images and vector paths with precise XY positioning.
|
|
66
|
+
- Draw lines, rectangles, and Bezier curves with configurable stroke width, dash patterns, and fill colors.
|
|
67
|
+
- Export results as bytes for downstream processing or save directly to disk with one call.
|
|
68
|
+
|
|
69
|
+
## Installation
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pip install pdfdancer-client-python==3.0.0
|
|
73
|
+
|
|
74
|
+
# Editable install for local development
|
|
75
|
+
pip install -e .
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Requirements
|
|
79
|
+
|
|
80
|
+
- Python 3.10 or newer.
|
|
81
|
+
- A PDFDancer API token, supplied explicitly or through `PDFDANCER_API_TOKEN` or `PDFDANCER_TOKEN`.
|
|
82
|
+
- Access to the PDFDancer API host. The default is `https://api.pdfdancer.com`.
|
|
83
|
+
|
|
84
|
+
## Quick Start
|
|
85
|
+
|
|
86
|
+
### Edit an Existing PDF
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from pathlib import Path
|
|
90
|
+
from pdfdancer import PDFDancer, PdfColorRequest, TextReplaceRequest, TextStyleRequest
|
|
91
|
+
|
|
92
|
+
with PDFDancer.open(
|
|
93
|
+
pdf_data=Path("input.pdf"),
|
|
94
|
+
token="your-api-token", # optional when PDFDANCER_API_TOKEN is set
|
|
95
|
+
base_url="https://api.pdfdancer.com",
|
|
96
|
+
) as pdf:
|
|
97
|
+
result = pdf.page(1).text().replace(
|
|
98
|
+
TextReplaceRequest.literal("Executive Summary", "Overview").build()
|
|
99
|
+
)
|
|
100
|
+
assert result.changed == 1
|
|
101
|
+
|
|
102
|
+
pdf.text().style(
|
|
103
|
+
TextStyleRequest.literal("Overview")
|
|
104
|
+
.fill_color(PdfColorRequest.rgb(0.2, 0.2, 0.6))
|
|
105
|
+
.build()
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# Persist the modified document
|
|
109
|
+
pdf.save("output.pdf")
|
|
110
|
+
# or keep it in memory
|
|
111
|
+
pdf_bytes = pdf.get_bytes()
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Create a Blank PDF
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from pathlib import Path
|
|
118
|
+
from pdfdancer import PDFDancer
|
|
119
|
+
|
|
120
|
+
with PDFDancer.new(token="your-api-token") as pdf:
|
|
121
|
+
pdf.new_image() \
|
|
122
|
+
.from_file(Path("logo.png")) \
|
|
123
|
+
.at(page=1, x=420, y=710) \
|
|
124
|
+
.add()
|
|
125
|
+
|
|
126
|
+
pdf.save("summary.pdf")
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Page API
|
|
130
|
+
|
|
131
|
+
Page numbers are 1-based. `pdf.page(1)` returns a page-scoped client, while `pdf.pages()` returns page clients for the
|
|
132
|
+
document. Use `get_snapshot()` on a page client for a read-only page snapshot.
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
first_page = pdf.page(1)
|
|
136
|
+
pages = pdf.pages()
|
|
137
|
+
snapshot = first_page.get_snapshot()
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Page-scoped selectors, text editing, and builders automatically restrict the operation to that page.
|
|
141
|
+
|
|
142
|
+
## Selection
|
|
143
|
+
|
|
144
|
+
Document- and page-scoped selectors return typed objects for images, paths, form XObjects, and form fields. Position
|
|
145
|
+
selectors use PDF coordinates and a default tolerance of `0.01` point. Singular selectors return the first match or
|
|
146
|
+
`None`; plural selectors return lists.
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
document_images = pdf.select_images()
|
|
150
|
+
logo = pdf.page(1).select_image_at(72, 680)
|
|
151
|
+
page_paths = pdf.page(1).select_paths()
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Document and page snapshots provide read-only text-line data. Use the selector-based text API for mutations.
|
|
155
|
+
|
|
156
|
+
## Builders and Vector Paths
|
|
157
|
+
|
|
158
|
+
All five dedicated builders are available at document and page scope: image, path, line, Bezier, and rectangle. Add
|
|
159
|
+
lines, curves, and shapes with fluent builders:
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
from pdfdancer import PDFDancer, Color, Point
|
|
163
|
+
|
|
164
|
+
with PDFDancer.open("document.pdf") as pdf:
|
|
165
|
+
page = pdf.page(1)
|
|
166
|
+
|
|
167
|
+
# Draw a simple line
|
|
168
|
+
page.new_line() \
|
|
169
|
+
.from_point(100, 700) \
|
|
170
|
+
.to_point(500, 700) \
|
|
171
|
+
.stroke_color(Color(0, 0, 255)) \
|
|
172
|
+
.stroke_width(2.0) \
|
|
173
|
+
.add()
|
|
174
|
+
|
|
175
|
+
# Draw a rectangle
|
|
176
|
+
page.new_rectangle() \
|
|
177
|
+
.at_coordinates(100, 500) \
|
|
178
|
+
.with_size(200, 100) \
|
|
179
|
+
.stroke_color(Color(0, 0, 0)) \
|
|
180
|
+
.fill_color(Color(255, 255, 200)) \
|
|
181
|
+
.add()
|
|
182
|
+
|
|
183
|
+
# Draw a bezier curve
|
|
184
|
+
page.new_bezier() \
|
|
185
|
+
.from_point(100, 400) \
|
|
186
|
+
.control_point_1(150, 450) \
|
|
187
|
+
.control_point_2(250, 350) \
|
|
188
|
+
.to_point(300, 400) \
|
|
189
|
+
.stroke_width(1.5) \
|
|
190
|
+
.add()
|
|
191
|
+
|
|
192
|
+
# Build complex paths with multiple segments
|
|
193
|
+
page.new_path() \
|
|
194
|
+
.stroke_color(Color(255, 0, 0)) \
|
|
195
|
+
.add_line(Point(50, 200), Point(150, 200)) \
|
|
196
|
+
.add_line(Point(150, 200), Point(100, 280)) \
|
|
197
|
+
.add_line(Point(100, 280), Point(50, 200)) \
|
|
198
|
+
.add()
|
|
199
|
+
|
|
200
|
+
pdf.save("annotated.pdf")
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
`PathBuilder` also provides cursor-based `move_to(...)`, `line_to(...)`, and `bezier_to(...)` operations plus
|
|
204
|
+
`close_path()`, `rectangle(...)`, `circle(...)`, and `solid()` conveniences. A circle is a `PathBuilder` convenience,
|
|
205
|
+
not a separate builder type.
|
|
206
|
+
|
|
207
|
+
## Images
|
|
208
|
+
|
|
209
|
+
Create images at document scope with an explicit page or directly from a page client:
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
from pathlib import Path
|
|
213
|
+
|
|
214
|
+
pdf.new_image().from_file(Path("logo.png")).at(page=1, x=72, y=700).add()
|
|
215
|
+
pdf.page(1).new_image().from_file(Path("stamp.png")).at(x=300, y=700).add()
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
`ImageObject` exposes `width`, `height`, and `aspect_ratio`. It supports replacement from a filesystem path or `Image`,
|
|
219
|
+
proportional or explicit scaling, cropping, opacity, horizontal and vertical flips, region filling, and rotation.
|
|
220
|
+
Positive rotation angles are clockwise. Image transformations return `CommandResult`, which exposes `success`,
|
|
221
|
+
`message`, `warning`, and `element_id`.
|
|
222
|
+
|
|
223
|
+
## Form Fields
|
|
224
|
+
|
|
225
|
+
Form-field selection uses the same names at document and page scope. Mutate a selected field directly with
|
|
226
|
+
`set_value(...)`:
|
|
227
|
+
|
|
228
|
+
```python
|
|
229
|
+
signature = pdf.select_form_fields_by_name("signature")[0]
|
|
230
|
+
changed = signature.set_value("Signed by Jane Doe")
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Selectors return typed objects (`ImageObject`, `FormFieldObject`, `PathObject`, `PageClient`, …) with generic helpers
|
|
234
|
+
such as `delete()`, `move_to(x, y)`, and `clear_clipping()` where supported by the selected object type.
|
|
235
|
+
|
|
236
|
+
## Text Editing
|
|
237
|
+
|
|
238
|
+
Text editing is selector-based and is available through `pdf.text()` and `pdf.page(page_number).text()`. It supports
|
|
239
|
+
replace, delete, insert, and style operations:
|
|
240
|
+
|
|
241
|
+
```python
|
|
242
|
+
from pdfdancer import TextDeleteRequest, TextInsertRequest, TextReplaceRequest
|
|
243
|
+
|
|
244
|
+
pdf.text().replace(
|
|
245
|
+
TextReplaceRequest.literal("Old product", "New product")
|
|
246
|
+
.whole_words(True)
|
|
247
|
+
.max_matches(5)
|
|
248
|
+
.build()
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
pdf.page(2).text().delete(
|
|
252
|
+
TextDeleteRequest.regex(r"Confidential\s+draft")
|
|
253
|
+
.case_sensitive(False)
|
|
254
|
+
.build()
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
pdf.text().insert(
|
|
258
|
+
TextInsertRequest.before("Terms", "Updated ").whole_words(True).build()
|
|
259
|
+
)
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Each mutation returns `TextEditResponse`, including match and change counts, changed page numbers, per-change
|
|
263
|
+
diagnostics, warnings, and errors.
|
|
264
|
+
|
|
265
|
+
## Shared Models
|
|
266
|
+
|
|
267
|
+
`Color` requires integral RGBA components in the inclusive range 0–255. Alpha defaults to 255; `BLACK`, `WHITE`, and
|
|
268
|
+
`RED` are provided as constants.
|
|
269
|
+
|
|
270
|
+
`PageSize` provides A0–A6, B4–B5, Letter, Legal, Tabloid, Executive, Postcard, and 3×5 Index sizes.
|
|
271
|
+
`PageSize.from_dimensions(...)` recognizes both portrait and rotated standard dimensions; custom dimensions must be
|
|
272
|
+
finite and positive.
|
|
273
|
+
|
|
274
|
+
The exported `ObjectType` enum covers every object type returned by the v2 snapshot and selection APIs.
|
|
275
|
+
|
|
276
|
+
## Configuration
|
|
277
|
+
|
|
278
|
+
- The SDK reads the process environment but does not load `.env` files. Applications that use `.env` files must load
|
|
279
|
+
them before calling the SDK.
|
|
280
|
+
- Set `PDFDANCER_API_TOKEN` for authentication (preferred). `PDFDANCER_TOKEN` is also supported for backwards compatibility.
|
|
281
|
+
- Override the API host with `PDFDANCER_BASE_URL` (e.g., sandbox or local environments). Defaults to `https://api.pdfdancer.com`.
|
|
282
|
+
- Tune HTTP read timeouts via the `timeout` argument on `PDFDancer.open()` and `PDFDancer.new()` (default: 30 seconds).
|
|
283
|
+
- Configure total request attempts with `max_attempts` or `PDFDANCER_MAX_ATTEMPTS`; the initial request counts as one attempt.
|
|
284
|
+
- For testing against self-signed certificates, call `pdfdancer.set_ssl_verify(False)` to temporarily disable TLS verification.
|
|
285
|
+
|
|
286
|
+
## Retry and Error Handling
|
|
287
|
+
|
|
288
|
+
The default HTTP policy makes three total attempts, including the initial request. It uses exponential backoff starting
|
|
289
|
+
at one second, a multiplier of two, and a five-second delay cap. Statuses 408, 429, 500, 502, 503, 504, and 520 are
|
|
290
|
+
retryable, as are timeout and connection failures. `Retry-After` is honored only for HTTP 429; retry delays do not use
|
|
291
|
+
jitter. Configure the total attempt count with `max_attempts` and the multiplier with `retry_backoff_factor`.
|
|
292
|
+
|
|
293
|
+
Operations raise subclasses of `PdfDancerException`:
|
|
294
|
+
|
|
295
|
+
- `ValidationException`: input validation problems (missing token, invalid coordinates, etc.).
|
|
296
|
+
- `FontNotFoundException`: requested font unavailable on the service.
|
|
297
|
+
- `HttpClientException`: transport or server errors with detailed context.
|
|
298
|
+
- `SessionException`: session creation and lifecycle failures.
|
|
299
|
+
- `RateLimitException`: API rate limit exceeded; includes retry-after timing.
|
|
300
|
+
|
|
301
|
+
Wrap automated workflows in `try/except` blocks to surface actionable errors to your users.
|
|
302
|
+
|
|
303
|
+
## Development and Testing
|
|
304
|
+
|
|
305
|
+
### Prerequisites
|
|
306
|
+
|
|
307
|
+
- **Python 3.10 or higher** (Python 3.9 has SSL issues with large file uploads)
|
|
308
|
+
- **Git** for cloning the repository
|
|
309
|
+
- **PDFDancer API token** for running end-to-end tests
|
|
310
|
+
|
|
311
|
+
### Step-by-Step Setup
|
|
312
|
+
|
|
313
|
+
#### 1. Clone the Repository
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
git clone https://github.com/MenschMachine/pdfdancer-client-python.git
|
|
317
|
+
cd pdfdancer-client-python
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
#### 2. Create a Virtual Environment and Install Dependencies
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
# Create `venv` and install the package with development dependencies
|
|
324
|
+
make install-dev
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
The Makefile creates the local `venv` when needed and runs all developer targets with its Python interpreter. Activating
|
|
328
|
+
the environment is optional; activate it if you also want to run commands directly:
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
# macOS/Linux
|
|
332
|
+
source venv/bin/activate
|
|
333
|
+
|
|
334
|
+
# Windows
|
|
335
|
+
venv\Scripts\activate
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
This installs:
|
|
339
|
+
- The `pdfdancer` package in editable mode (changes reflect immediately)
|
|
340
|
+
- Development tooling including `pytest`, `pytest-cov`, `pytest-mock`, `black`, `isort`, `flake8`, `mypy`, `build`, and `twine`.
|
|
341
|
+
|
|
342
|
+
To install runtime dependencies without development tools, use `make install`.
|
|
343
|
+
|
|
344
|
+
#### 3. Configure API Token
|
|
345
|
+
|
|
346
|
+
Set your PDFDancer API token as an environment variable:
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
# On macOS/Linux:
|
|
350
|
+
export PDFDANCER_API_TOKEN="your-api-token-here"
|
|
351
|
+
|
|
352
|
+
# On Windows (Command Prompt):
|
|
353
|
+
set PDFDANCER_API_TOKEN=your-api-token-here
|
|
354
|
+
|
|
355
|
+
# On Windows (PowerShell):
|
|
356
|
+
$env:PDFDANCER_API_TOKEN="your-api-token-here"
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
For permanent configuration, add this to your shell profile (`~/.bashrc`, `~/.zshrc`, etc.).
|
|
360
|
+
|
|
361
|
+
#### 4. Verify Installation
|
|
362
|
+
|
|
363
|
+
```bash
|
|
364
|
+
# Run the test suite
|
|
365
|
+
pytest tests/ -v
|
|
366
|
+
|
|
367
|
+
# Run only unit tests (faster)
|
|
368
|
+
pytest tests/test_models.py -v
|
|
369
|
+
|
|
370
|
+
# Run end-to-end tests (requires API token)
|
|
371
|
+
pytest tests/e2e/ -v
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
All tests should pass if everything is set up correctly.
|
|
375
|
+
|
|
376
|
+
### Common Development Tasks
|
|
377
|
+
|
|
378
|
+
Run `make help` to list all developer targets and their configurable variables.
|
|
379
|
+
|
|
380
|
+
#### Running Tests
|
|
381
|
+
|
|
382
|
+
```bash
|
|
383
|
+
# Run all tests with verbose output
|
|
384
|
+
make test
|
|
385
|
+
|
|
386
|
+
# Run tests that do not require API access
|
|
387
|
+
make test-unit
|
|
388
|
+
|
|
389
|
+
# Run end-to-end tests only
|
|
390
|
+
make test-e2e
|
|
391
|
+
|
|
392
|
+
# Run a specific test file or pass additional pytest arguments
|
|
393
|
+
make test TEST_PATH=tests/test_models.py
|
|
394
|
+
make test PYTEST_ARGS="-v -x"
|
|
395
|
+
|
|
396
|
+
# Run all tests with a coverage report
|
|
397
|
+
make coverage
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
#### Building Distribution Packages
|
|
401
|
+
|
|
402
|
+
```bash
|
|
403
|
+
# Clean, build, and verify the wheel and source distribution
|
|
404
|
+
make package
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
Artifacts will be created in the `dist/` directory. Package versions are derived from Git tags via `setuptools-scm`.
|
|
408
|
+
|
|
409
|
+
#### Publishing to PyPI
|
|
410
|
+
|
|
411
|
+
Releases are published automatically to PyPI when a `v*` tag is pushed to GitHub (via GitHub Actions with Trusted Publishers).
|
|
412
|
+
|
|
413
|
+
```bash
|
|
414
|
+
# Create and push a release tag — GitHub Actions handles the rest
|
|
415
|
+
git tag v2.0.0
|
|
416
|
+
git push origin v2.0.0
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
#### Code Quality
|
|
420
|
+
|
|
421
|
+
```bash
|
|
422
|
+
# Format code
|
|
423
|
+
make format
|
|
424
|
+
|
|
425
|
+
# Check formatting without changing files
|
|
426
|
+
make format-check
|
|
427
|
+
|
|
428
|
+
# Lint
|
|
429
|
+
make lint
|
|
430
|
+
|
|
431
|
+
# Type-check
|
|
432
|
+
make typecheck
|
|
433
|
+
|
|
434
|
+
# Run formatting checks, linting, type checking, and non-E2E tests
|
|
435
|
+
make check
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
### Project Structure
|
|
439
|
+
|
|
440
|
+
```
|
|
441
|
+
pdfdancer-client-python/
|
|
442
|
+
├── src/pdfdancer/ # Main package source
|
|
443
|
+
│ ├── __init__.py # Package exports
|
|
444
|
+
│ ├── pdfdancer_v2.py # Core PDFDancer and PageClient classes
|
|
445
|
+
│ ├── text_editing.py # Selector-based v2 text request builders
|
|
446
|
+
│ ├── image_builder.py # Fluent image builders
|
|
447
|
+
│ ├── path_builder.py # Vector path builders (lines, beziers, rectangles)
|
|
448
|
+
│ ├── page_builder.py # Page creation builder
|
|
449
|
+
│ ├── models.py # Data models (Position, Font, Color, etc.)
|
|
450
|
+
│ ├── types.py # Live object-reference wrappers
|
|
451
|
+
│ └── exceptions.py # Exception hierarchy
|
|
452
|
+
├── tests/ # Test suite
|
|
453
|
+
│ ├── test_models.py # Model unit tests
|
|
454
|
+
│ ├── e2e/ # End-to-end integration tests
|
|
455
|
+
│ └── fixtures/ # Test fixtures and sample PDFs
|
|
456
|
+
├── docs/ # Documentation
|
|
457
|
+
├── dist/ # Build artifacts (created after packaging)
|
|
458
|
+
├── pyproject.toml # Project metadata and dependencies
|
|
459
|
+
└── README.md # This file
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
## Troubleshooting
|
|
463
|
+
|
|
464
|
+
#### Virtual Environment Issues
|
|
465
|
+
|
|
466
|
+
If `python -m venv venv` fails, ensure you have the `venv` module:
|
|
467
|
+
|
|
468
|
+
```bash
|
|
469
|
+
# On Ubuntu/Debian
|
|
470
|
+
sudo apt-get install python3-venv
|
|
471
|
+
|
|
472
|
+
# On macOS (using Homebrew)
|
|
473
|
+
brew install python@3.10
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
#### SSL Errors with Large Files
|
|
477
|
+
|
|
478
|
+
Upgrade to Python 3.10+ if you encounter SSL errors during large file uploads.
|
|
479
|
+
|
|
480
|
+
#### Import Errors
|
|
481
|
+
|
|
482
|
+
Ensure the virtual environment is activated and the package is installed in editable mode:
|
|
483
|
+
|
|
484
|
+
```bash
|
|
485
|
+
source venv/bin/activate # or venv\Scripts\activate on Windows
|
|
486
|
+
pip install -e .
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
#### Test Failures
|
|
490
|
+
|
|
491
|
+
- Ensure `PDFDANCER_API_TOKEN` is set for e2e tests
|
|
492
|
+
- Check network connectivity to the PDFDancer API
|
|
493
|
+
- Verify you're using Python 3.10 or higher
|
|
494
|
+
|
|
495
|
+
## Contributing
|
|
496
|
+
|
|
497
|
+
Contributions are welcome via pull request. Please:
|
|
498
|
+
|
|
499
|
+
1. Create a feature branch from `main`
|
|
500
|
+
2. Add tests for new functionality
|
|
501
|
+
3. Ensure all tests pass: `pytest tests/ -v`
|
|
502
|
+
4. Follow existing code style and patterns
|
|
503
|
+
5. Update documentation as needed
|
|
504
|
+
|
|
505
|
+
## Helpful Links
|
|
506
|
+
|
|
507
|
+
- [API documentation](https://docs.pdfdancer.com?utm_source=github&utm_medium=readme&utm_campaign=pdfdancer-python)
|
|
508
|
+
- [Product overview](https://www.pdfdancer.com?utm_source=github&utm_medium=readme&utm_campaign=pdfdancer-python)
|
|
509
|
+
- [PyPI](https://pypi.org/project/pdfdancer-client-python/)
|
|
510
|
+
- [Changelog](https://www.pdfdancer.com/changelog/?utm_source=github&utm_medium=readme&utm_campaign=pdfdancer-python)
|
|
511
|
+
- [Status](https://status.pdfdancer.com?utm_source=github&utm_medium=readme&utm_campaign=pdfdancer-python)
|
|
512
|
+
- [Issue tracker](https://github.com/MenschMachine/pdfdancer)
|
|
513
|
+
|
|
514
|
+
## Related SDKs
|
|
515
|
+
|
|
516
|
+
- TypeScript client: https://github.com/MenschMachine/pdfdancer-client-typescript
|
|
517
|
+
- Java client: https://github.com/MenschMachine/pdfdancer-client-java
|
|
518
|
+
|
|
519
|
+
## License
|
|
520
|
+
|
|
521
|
+
Apache License 2.0 © 2025 The Famous Cat Ltd. See `LICENSE` and `NOTICE` for details.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
pdfdancer/__init__.py,sha256=iD0IWfpLFrPoFQhsZ_fGUpv9fH21QNJchM_23pTtdds,4419
|
|
2
|
+
pdfdancer/_runtime_version.py,sha256=UIId9w7Jk97tPVOppEJeq4HKVEt3BIMc6jeThKU31k4,825
|
|
3
|
+
pdfdancer/_version.py,sha256=Q5KDuzxagP6DhS0ZWCmUUKtPbVMNBgYpVtHVHkQdyPQ,520
|
|
4
|
+
pdfdancer/exceptions.py,sha256=U6kD3NvcdNt05_OU11Tml5dXGtlodEtYC9zcV4fHXkc,2270
|
|
5
|
+
pdfdancer/fingerprint.py,sha256=eL3PHPgv-knMya7s95RXg3qzzpkAA1aevxqb6tuOb34,3061
|
|
6
|
+
pdfdancer/image_builder.py,sha256=R4ik7ZN-iGWUsy7UHoMvpAEzc_MHquPY9Q27g_zuEtI,2987
|
|
7
|
+
pdfdancer/models.py,sha256=6N-rybJnXo2Y3KrWcBxN0uVq5HxyrVDqMO52lcvW_G4,51558
|
|
8
|
+
pdfdancer/page_builder.py,sha256=2Ll98fkNen7dtlQe_uhYDJPKqqnqMMeOTYlymeWtaII,3528
|
|
9
|
+
pdfdancer/path_builder.py,sha256=znQD3zgBs7u--pQ3OdUJOSvBhBLCm80pdH6Kg0_T-io,28502
|
|
10
|
+
pdfdancer/pdfdancer_v2.py,sha256=8YiMMDeaybPNBpybf9kESwnE48wscdOlk1AcJrDCBSc,120419
|
|
11
|
+
pdfdancer/text_editing.py,sha256=iA3n-yWca95ilNJSHMxFirGKhXpl_SGgg2NuuAUVGOQ,51768
|
|
12
|
+
pdfdancer/types.py,sha256=Mwr4d1C-pS7kSctgXH0F6LEOu8Q0EFaFAOMjiGlrRaQ,16961
|
|
13
|
+
pdfdancer_client_python-3.0.0.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
14
|
+
pdfdancer_client_python-3.0.0.dist-info/licenses/NOTICE,sha256=xaC4l-IChAmtViNDie8ZWzUk0O6XRMyzOl0zLmVZ2HE,232
|
|
15
|
+
pdfdancer_client_python-3.0.0.dist-info/METADATA,sha256=TcvRgCLXAywFJ4GMBMtzI4DqWSD1CWytHHctQN7TD4E,17618
|
|
16
|
+
pdfdancer_client_python-3.0.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
17
|
+
pdfdancer_client_python-3.0.0.dist-info/top_level.txt,sha256=ICwSVRpcCKrdBF9QlaX9Y0e_N3Nk1p7QVxadGOnbxeY,10
|
|
18
|
+
pdfdancer_client_python-3.0.0.dist-info/RECORD,,
|