pdfdancer-client-python 0.3.14__py3-none-any.whl → 3.0.1__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 +77 -22
- pdfdancer/_runtime_version.py +8 -3
- pdfdancer/_version.py +2 -2
- 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} +848 -1310
- pdfdancer/text_editing.py +1472 -0
- pdfdancer/types.py +94 -399
- {pdfdancer_client_python-0.3.14.dist-info → pdfdancer_client_python-3.0.1.dist-info}/METADATA +181 -137
- pdfdancer_client_python-3.0.1.dist-info/RECORD +18 -0
- {pdfdancer_client_python-0.3.14.dist-info → pdfdancer_client_python-3.0.1.dist-info}/WHEEL +1 -1
- pdfdancer/paragraph_builder.py +0 -554
- pdfdancer/text_line_builder.py +0 -290
- pdfdancer_client_python-0.3.14.dist-info/RECORD +0 -19
- {pdfdancer_client_python-0.3.14.dist-info → pdfdancer_client_python-3.0.1.dist-info}/licenses/LICENSE +0 -0
- {pdfdancer_client_python-0.3.14.dist-info → pdfdancer_client_python-3.0.1.dist-info}/licenses/NOTICE +0 -0
- {pdfdancer_client_python-0.3.14.dist-info → pdfdancer_client_python-3.0.1.dist-info}/top_level.txt +0 -0
{pdfdancer_client_python-0.3.14.dist-info → pdfdancer_client_python-3.0.1.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pdfdancer-client-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 3.0.1
|
|
4
4
|
Summary: Python client for PDFDancer API
|
|
5
5
|
Author-email: "The Famous Cat Ltd." <hi@thefamouscat.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -21,11 +21,11 @@ License-File: NOTICE
|
|
|
21
21
|
Requires-Dist: httpx[http2]>=0.27.0
|
|
22
22
|
Requires-Dist: pydantic>=1.8.0
|
|
23
23
|
Requires-Dist: typing-extensions>=4.0.0
|
|
24
|
-
Requires-Dist: python-dotenv>=0.19.0
|
|
25
24
|
Provides-Extra: dev
|
|
26
25
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
27
26
|
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
28
27
|
Requires-Dist: pytest-mock>=3.10.0; extra == "dev"
|
|
28
|
+
Requires-Dist: pypdf>=5.0.0; extra == "dev"
|
|
29
29
|
Requires-Dist: black>=22.0; extra == "dev"
|
|
30
30
|
Requires-Dist: flake8>=5.0; extra == "dev"
|
|
31
31
|
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
@@ -36,73 +36,74 @@ Dynamic: license-file
|
|
|
36
36
|
|
|
37
37
|
# PDFDancer Python Client
|
|
38
38
|
|
|
39
|
+
This README documents `pdfdancer-client-python` version `3.0.0`.
|
|
40
|
+
|
|
39
41
|

|
|
40
42
|
|
|
41
|
-
##
|
|
43
|
+
## Overview
|
|
44
|
+
|
|
45
|
+
### PDF used to be read-only. We fixed that.
|
|
42
46
|
|
|
43
47
|
Edit text in real-world PDFs—even ones you didn't create. Move images, reposition headers, and change fonts with
|
|
44
48
|
pixel-perfect control from Python. The same API is also available for TypeScript and Java.
|
|
45
49
|
|
|
46
|
-
|
|
47
|
-
> https://bucket.pdfdancer.com/api-doc/development-0.0.yml.
|
|
48
|
-
|
|
49
|
-
## Highlights
|
|
50
|
-
|
|
51
|
-
- Locate paragraphs, text lines, images, vector paths, form fields, and pages by page number, coordinates, or text patterns.
|
|
52
|
-
- Edit existing content in place with fluent editors and context managers that apply changes safely.
|
|
53
|
-
- Programmatically control third-party PDFs—modify invoices, contracts, and reports you did not author.
|
|
54
|
-
- Add content with precise XY positioning using paragraph, image, and vector path builders with custom fonts and colors.
|
|
55
|
-
- Draw lines, rectangles, and Bezier curves with configurable stroke width, dash patterns, and fill colors.
|
|
56
|
-
- Redact sensitive content—replace text, images, or form fields with customizable placeholders.
|
|
57
|
-
- Export results as bytes for downstream processing or save directly to disk with one call.
|
|
58
|
-
|
|
59
|
-
## What Makes PDFDancer Different
|
|
50
|
+
### What Makes PDFDancer Different
|
|
60
51
|
|
|
61
52
|
- **Edit text in real-world PDFs**: Work with documents from customers, governments, or vendors—even ones you didn't create.
|
|
62
53
|
- **Pixel-perfect positioning**: Move or add elements at exact coordinates and keep the original layout intact.
|
|
63
|
-
- **
|
|
54
|
+
- **Selector-based text editing**: Apply literal or regular-expression replacements with page scoping and explicit layout policy.
|
|
64
55
|
- **Form manipulation**: Inspect, fill, and update AcroForm fields programmatically.
|
|
65
56
|
- **Coordinate-based selection**: Select objects by position, bounding box, or text patterns.
|
|
66
57
|
- **Vector graphics**: Draw lines, rectangles, and Bezier curves with full control over stroke and fill properties.
|
|
67
|
-
- **Secure redaction**: Permanently remove sensitive content and replace with customizable markers.
|
|
68
58
|
- **Real PDF editing**: Modify the underlying PDF structure instead of merely stamping overlays.
|
|
69
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
|
+
|
|
70
69
|
## Installation
|
|
71
70
|
|
|
72
71
|
```bash
|
|
73
|
-
pip install pdfdancer-client-python
|
|
72
|
+
pip install pdfdancer-client-python==3.0.0
|
|
74
73
|
|
|
75
74
|
# Editable install for local development
|
|
76
75
|
pip install -e .
|
|
77
76
|
```
|
|
78
77
|
|
|
79
|
-
|
|
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`.
|
|
80
83
|
|
|
81
|
-
## Quick Start
|
|
84
|
+
## Quick Start
|
|
85
|
+
|
|
86
|
+
### Edit an Existing PDF
|
|
82
87
|
|
|
83
88
|
```python
|
|
84
89
|
from pathlib import Path
|
|
85
|
-
from pdfdancer import
|
|
90
|
+
from pdfdancer import PDFDancer, PdfColorRequest, TextReplaceRequest, TextStyleRequest
|
|
86
91
|
|
|
87
92
|
with PDFDancer.open(
|
|
88
93
|
pdf_data=Path("input.pdf"),
|
|
89
94
|
token="your-api-token", # optional when PDFDANCER_API_TOKEN is set
|
|
90
95
|
base_url="https://api.pdfdancer.com",
|
|
91
96
|
) as pdf:
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
.
|
|
101
|
-
|
|
102
|
-
.color(Color(70, 70, 70)) \
|
|
103
|
-
.line_spacing(1.4) \
|
|
104
|
-
.at(page_number=1, x=72, y=520) \
|
|
105
|
-
.add()
|
|
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
|
+
)
|
|
106
107
|
|
|
107
108
|
# Persist the modified document
|
|
108
109
|
pdf.save("output.pdf")
|
|
@@ -114,68 +115,54 @@ with PDFDancer.open(
|
|
|
114
115
|
|
|
115
116
|
```python
|
|
116
117
|
from pathlib import Path
|
|
117
|
-
from pdfdancer import
|
|
118
|
+
from pdfdancer import PDFDancer
|
|
118
119
|
|
|
119
120
|
with PDFDancer.new(token="your-api-token") as pdf:
|
|
120
|
-
pdf.new_paragraph() \
|
|
121
|
-
.text("Quarterly Summary") \
|
|
122
|
-
.font(StandardFonts.TIMES_BOLD, 18) \
|
|
123
|
-
.color(Color(10, 10, 80)) \
|
|
124
|
-
.line_spacing(1.2) \
|
|
125
|
-
.at(page_number=1, x=72, y=730) \
|
|
126
|
-
.add()
|
|
127
|
-
|
|
128
121
|
pdf.new_image() \
|
|
129
122
|
.from_file(Path("logo.png")) \
|
|
130
|
-
.at(page=
|
|
123
|
+
.at(page=1, x=420, y=710) \
|
|
131
124
|
.add()
|
|
132
125
|
|
|
133
126
|
pdf.save("summary.pdf")
|
|
134
127
|
```
|
|
135
128
|
|
|
136
|
-
##
|
|
129
|
+
## Page API
|
|
137
130
|
|
|
138
|
-
|
|
139
|
-
|
|
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.
|
|
140
133
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
# Update form fields
|
|
147
|
-
signature = pdf.select_form_fields_by_name("signature")[0]
|
|
148
|
-
signature.edit().value("Signed by Jane Doe").apply()
|
|
149
|
-
|
|
150
|
-
# Trim or move content at specific coordinates
|
|
151
|
-
images = pdf.page(1).select_images()
|
|
152
|
-
for image in images:
|
|
153
|
-
x = image.position.x()
|
|
154
|
-
if x is not None and x < 100:
|
|
155
|
-
image.delete()
|
|
134
|
+
```python
|
|
135
|
+
first_page = pdf.page(1)
|
|
136
|
+
pages = pdf.pages()
|
|
137
|
+
snapshot = first_page.get_snapshot()
|
|
156
138
|
```
|
|
157
139
|
|
|
158
|
-
|
|
159
|
-
|
|
140
|
+
Page-scoped selectors, text editing, and builders automatically restrict the operation to that page.
|
|
141
|
+
|
|
142
|
+
## Selection
|
|
160
143
|
|
|
161
|
-
|
|
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.
|
|
162
147
|
|
|
163
148
|
```python
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
field = pdf.select_form_field_by_name("email") # Returns first match or None
|
|
149
|
+
document_images = pdf.select_images()
|
|
150
|
+
logo = pdf.page(1).select_image_at(72, 680)
|
|
151
|
+
page_paths = pdf.page(1).select_paths()
|
|
168
152
|
```
|
|
169
153
|
|
|
170
|
-
|
|
154
|
+
Document and page snapshots provide read-only text-line data. Use the selector-based text API for mutations.
|
|
171
155
|
|
|
172
|
-
|
|
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:
|
|
173
160
|
|
|
174
161
|
```python
|
|
175
162
|
from pdfdancer import PDFDancer, Color, Point
|
|
176
163
|
|
|
177
164
|
with PDFDancer.open("document.pdf") as pdf:
|
|
178
|
-
page = pdf.page(
|
|
165
|
+
page = pdf.page(1)
|
|
179
166
|
|
|
180
167
|
# Draw a simple line
|
|
181
168
|
page.new_line() \
|
|
@@ -213,39 +200,95 @@ with PDFDancer.open("document.pdf") as pdf:
|
|
|
213
200
|
pdf.save("annotated.pdf")
|
|
214
201
|
```
|
|
215
202
|
|
|
216
|
-
|
|
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
|
|
217
208
|
|
|
218
|
-
|
|
209
|
+
Create images at document scope with an explicit page or directly from a page client:
|
|
219
210
|
|
|
220
211
|
```python
|
|
221
|
-
from
|
|
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
|
+
```
|
|
222
217
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
para.redact("[REDACTED]")
|
|
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`.
|
|
228
222
|
|
|
229
|
-
|
|
230
|
-
for image in pdf.page(0).select_images():
|
|
231
|
-
image.redact()
|
|
223
|
+
## Form Fields
|
|
232
224
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
result = pdf.redact(form_fields, replacement="[REMOVED]", placeholder_color=Color(0, 0, 0))
|
|
236
|
-
print(f"Redacted {result.count} items")
|
|
225
|
+
Form-field selection uses the same names at document and page scope. Mutate a selected field directly with
|
|
226
|
+
`set_value(...)`:
|
|
237
227
|
|
|
238
|
-
|
|
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
|
+
)
|
|
239
260
|
```
|
|
240
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
|
+
|
|
241
276
|
## Configuration
|
|
242
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.
|
|
243
280
|
- Set `PDFDANCER_API_TOKEN` for authentication (preferred). `PDFDANCER_TOKEN` is also supported for backwards compatibility.
|
|
244
281
|
- Override the API host with `PDFDANCER_BASE_URL` (e.g., sandbox or local environments). Defaults to `https://api.pdfdancer.com`.
|
|
245
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.
|
|
246
284
|
- For testing against self-signed certificates, call `pdfdancer.set_ssl_verify(False)` to temporarily disable TLS verification.
|
|
247
285
|
|
|
248
|
-
## Error Handling
|
|
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`.
|
|
249
292
|
|
|
250
293
|
Operations raise subclasses of `PdfDancerException`:
|
|
251
294
|
|
|
@@ -257,7 +300,7 @@ Operations raise subclasses of `PdfDancerException`:
|
|
|
257
300
|
|
|
258
301
|
Wrap automated workflows in `try/except` blocks to surface actionable errors to your users.
|
|
259
302
|
|
|
260
|
-
## Development
|
|
303
|
+
## Development and Testing
|
|
261
304
|
|
|
262
305
|
### Prerequisites
|
|
263
306
|
|
|
@@ -274,37 +317,31 @@ git clone https://github.com/MenschMachine/pdfdancer-client-python.git
|
|
|
274
317
|
cd pdfdancer-client-python
|
|
275
318
|
```
|
|
276
319
|
|
|
277
|
-
#### 2. Create a Virtual Environment
|
|
320
|
+
#### 2. Create a Virtual Environment and Install Dependencies
|
|
278
321
|
|
|
279
322
|
```bash
|
|
280
|
-
# Create
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
# Activate the virtual environment
|
|
284
|
-
# On macOS/Linux:
|
|
285
|
-
source venv/bin/activate
|
|
286
|
-
|
|
287
|
-
# On Windows:
|
|
288
|
-
venv\Scripts\activate
|
|
323
|
+
# Create `venv` and install the package with development dependencies
|
|
324
|
+
make install-dev
|
|
289
325
|
```
|
|
290
326
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
#### 3. Install Dependencies
|
|
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:
|
|
294
329
|
|
|
295
330
|
```bash
|
|
296
|
-
#
|
|
297
|
-
|
|
331
|
+
# macOS/Linux
|
|
332
|
+
source venv/bin/activate
|
|
298
333
|
|
|
299
|
-
#
|
|
300
|
-
|
|
334
|
+
# Windows
|
|
335
|
+
venv\Scripts\activate
|
|
301
336
|
```
|
|
302
337
|
|
|
303
338
|
This installs:
|
|
304
339
|
- The `pdfdancer` package in editable mode (changes reflect immediately)
|
|
305
340
|
- Development tooling including `pytest`, `pytest-cov`, `pytest-mock`, `black`, `isort`, `flake8`, `mypy`, `build`, and `twine`.
|
|
306
341
|
|
|
307
|
-
|
|
342
|
+
To install runtime dependencies without development tools, use `make install`.
|
|
343
|
+
|
|
344
|
+
#### 3. Configure API Token
|
|
308
345
|
|
|
309
346
|
Set your PDFDancer API token as an environment variable:
|
|
310
347
|
|
|
@@ -321,7 +358,7 @@ $env:PDFDANCER_API_TOKEN="your-api-token-here"
|
|
|
321
358
|
|
|
322
359
|
For permanent configuration, add this to your shell profile (`~/.bashrc`, `~/.zshrc`, etc.).
|
|
323
360
|
|
|
324
|
-
####
|
|
361
|
+
#### 4. Verify Installation
|
|
325
362
|
|
|
326
363
|
```bash
|
|
327
364
|
# Run the test suite
|
|
@@ -338,30 +375,33 @@ All tests should pass if everything is set up correctly.
|
|
|
338
375
|
|
|
339
376
|
### Common Development Tasks
|
|
340
377
|
|
|
378
|
+
Run `make help` to list all developer targets and their configurable variables.
|
|
379
|
+
|
|
341
380
|
#### Running Tests
|
|
342
381
|
|
|
343
382
|
```bash
|
|
344
383
|
# Run all tests with verbose output
|
|
345
|
-
|
|
384
|
+
make test
|
|
346
385
|
|
|
347
|
-
# Run
|
|
348
|
-
|
|
386
|
+
# Run tests that do not require API access
|
|
387
|
+
make test-unit
|
|
349
388
|
|
|
350
389
|
# Run end-to-end tests only
|
|
351
|
-
|
|
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"
|
|
352
395
|
|
|
353
|
-
# Run with coverage report
|
|
354
|
-
|
|
396
|
+
# Run all tests with a coverage report
|
|
397
|
+
make coverage
|
|
355
398
|
```
|
|
356
399
|
|
|
357
400
|
#### Building Distribution Packages
|
|
358
401
|
|
|
359
402
|
```bash
|
|
360
|
-
#
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
# Verify the built packages
|
|
364
|
-
python -m twine check dist/*
|
|
403
|
+
# Clean, build, and verify the wheel and source distribution
|
|
404
|
+
make package
|
|
365
405
|
```
|
|
366
406
|
|
|
367
407
|
Artifacts will be created in the `dist/` directory. Package versions are derived from Git tags via `setuptools-scm`.
|
|
@@ -372,22 +412,27 @@ Releases are published automatically to PyPI when a `v*` tag is pushed to GitHub
|
|
|
372
412
|
|
|
373
413
|
```bash
|
|
374
414
|
# Create and push a release tag — GitHub Actions handles the rest
|
|
375
|
-
git tag
|
|
376
|
-
git push origin
|
|
415
|
+
git tag v2.0.0
|
|
416
|
+
git push origin v2.0.0
|
|
377
417
|
```
|
|
378
418
|
|
|
379
419
|
#### Code Quality
|
|
380
420
|
|
|
381
421
|
```bash
|
|
382
422
|
# Format code
|
|
383
|
-
|
|
384
|
-
|
|
423
|
+
make format
|
|
424
|
+
|
|
425
|
+
# Check formatting without changing files
|
|
426
|
+
make format-check
|
|
385
427
|
|
|
386
428
|
# Lint
|
|
387
|
-
|
|
429
|
+
make lint
|
|
430
|
+
|
|
431
|
+
# Type-check
|
|
432
|
+
make typecheck
|
|
388
433
|
|
|
389
|
-
#
|
|
390
|
-
|
|
434
|
+
# Run formatting checks, linting, type checking, and non-E2E tests
|
|
435
|
+
make check
|
|
391
436
|
```
|
|
392
437
|
|
|
393
438
|
### Project Structure
|
|
@@ -396,14 +441,13 @@ mypy src/pdfdancer/
|
|
|
396
441
|
pdfdancer-client-python/
|
|
397
442
|
├── src/pdfdancer/ # Main package source
|
|
398
443
|
│ ├── __init__.py # Package exports
|
|
399
|
-
│ ├──
|
|
400
|
-
│ ├──
|
|
401
|
-
│ ├── text_line_builder.py # Fluent text line builders
|
|
444
|
+
│ ├── pdfdancer_v2.py # Core PDFDancer and PageClient classes
|
|
445
|
+
│ ├── text_editing.py # Selector-based v2 text request builders
|
|
402
446
|
│ ├── image_builder.py # Fluent image builders
|
|
403
447
|
│ ├── path_builder.py # Vector path builders (lines, beziers, rectangles)
|
|
404
448
|
│ ├── page_builder.py # Page creation builder
|
|
405
449
|
│ ├── models.py # Data models (Position, Font, Color, etc.)
|
|
406
|
-
│ ├── types.py #
|
|
450
|
+
│ ├── types.py # Live object-reference wrappers
|
|
407
451
|
│ └── exceptions.py # Exception hierarchy
|
|
408
452
|
├── tests/ # Test suite
|
|
409
453
|
│ ├── test_models.py # Model unit tests
|
|
@@ -415,7 +459,7 @@ pdfdancer-client-python/
|
|
|
415
459
|
└── README.md # This file
|
|
416
460
|
```
|
|
417
461
|
|
|
418
|
-
|
|
462
|
+
## Troubleshooting
|
|
419
463
|
|
|
420
464
|
#### Virtual Environment Issues
|
|
421
465
|
|
|
@@ -448,7 +492,7 @@ pip install -e .
|
|
|
448
492
|
- Check network connectivity to the PDFDancer API
|
|
449
493
|
- Verify you're using Python 3.10 or higher
|
|
450
494
|
|
|
451
|
-
|
|
495
|
+
## Contributing
|
|
452
496
|
|
|
453
497
|
Contributions are welcome via pull request. Please:
|
|
454
498
|
|
|
@@ -458,7 +502,7 @@ Contributions are welcome via pull request. Please:
|
|
|
458
502
|
4. Follow existing code style and patterns
|
|
459
503
|
5. Update documentation as needed
|
|
460
504
|
|
|
461
|
-
## Helpful
|
|
505
|
+
## Helpful Links
|
|
462
506
|
|
|
463
507
|
- [API documentation](https://docs.pdfdancer.com?utm_source=github&utm_medium=readme&utm_campaign=pdfdancer-python)
|
|
464
508
|
- [Product overview](https://www.pdfdancer.com?utm_source=github&utm_medium=readme&utm_campaign=pdfdancer-python)
|
|
@@ -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=DQj8PXaBgkooJVvjTVd86hfQE03x_PUMKRF3guh3670,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.1.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
14
|
+
pdfdancer_client_python-3.0.1.dist-info/licenses/NOTICE,sha256=xaC4l-IChAmtViNDie8ZWzUk0O6XRMyzOl0zLmVZ2HE,232
|
|
15
|
+
pdfdancer_client_python-3.0.1.dist-info/METADATA,sha256=oiYBb7IxHTiNZL3ZyIo30Ht0Q9xpzMV_rVrB3_GI-vw,17618
|
|
16
|
+
pdfdancer_client_python-3.0.1.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
17
|
+
pdfdancer_client_python-3.0.1.dist-info/top_level.txt,sha256=ICwSVRpcCKrdBF9QlaX9Y0e_N3Nk1p7QVxadGOnbxeY,10
|
|
18
|
+
pdfdancer_client_python-3.0.1.dist-info/RECORD,,
|