pdfdancer-client-python 0.3.1__tar.gz → 0.3.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/PKG-INFO +112 -25
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/README.md +111 -24
- pdfdancer_client_python-0.3.3/docs/api-schemas/v0.yml +4945 -0
- pdfdancer_client_python-0.3.1/docs/openapi.yml → pdfdancer_client_python-0.3.3/docs/api-schemas/v1.yml +1433 -1006
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/pyproject.toml +1 -1
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer/__init__.py +4 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer/models.py +49 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer/pdfdancer_v1.py +105 -3
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer/types.py +8 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer_client_python.egg-info/PKG-INFO +112 -25
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer_client_python.egg-info/SOURCES.txt +3 -1
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_line.py +1 -4
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_paragraph.py +0 -3
- pdfdancer_client_python-0.3.3/tests/e2e/test_redact.py +287 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_singular_selection.py +0 -1
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_text_line_edit.py +1 -1
- pdfdancer_client_python-0.3.3/update-api-spec.sh +5 -0
- pdfdancer_client_python-0.3.1/update-api-spec.sh +0 -3
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/.claude/commands/discuss.md +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/.flake8 +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/.github/workflows/ci.yml +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/.github/workflows/daily-tests.yml +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/.gitignore +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/CLAUDE.md +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/LICENSE +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/NOTICE +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/TODO.md +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/check.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/media/logo-orange-512h.webp +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/media/logo-orange-60h.webp +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/release.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/setup.cfg +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer/exceptions.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer/fingerprint.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer/image_builder.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer/page_builder.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer/paragraph_builder.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer/path_builder.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer/text_line_builder.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer_client_python.egg-info/dependency_links.txt +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer_client_python.egg-info/requires.txt +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer_client_python.egg-info/top_level.txt +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/test.sh +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/__init__.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/conftest.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/__init__.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/pdf_assertions.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_acroform.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_bezier_builder.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_context_manager.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_form_x_objects.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_image.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_line_builder.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_new_pdf.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_page.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_path.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_path_builder.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_path_builder_rectangle.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_path_comprehensive.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_pdfdancer.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_positioning.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_rectangle_builder.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_snapshot.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/fixtures/DancingScript-Regular.ttf +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/fixtures/Empty.pdf +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/fixtures/JetBrainsMono-Regular.ttf +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/fixtures/Showcase.pdf +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/fixtures/basic-paths.pdf +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/fixtures/form-xobject-example.pdf +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/fixtures/logo-80.png +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/fixtures/mixed-form-types.pdf +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/test_anonymous_token.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/test_fingerprint.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/test_models.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/test_openapi_compliance.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/test_path_models.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/test_pdf_object_equality.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/test_rate_limit.py +0 -0
- {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/test_standard_fonts.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pdfdancer-client-python
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: Python client for PDFDancer API
|
|
5
5
|
Author-email: "The Famous Cat Ltd." <hi@thefamouscat.com>
|
|
6
6
|
License:
|
|
@@ -251,10 +251,12 @@ pixel-perfect control from Python. The same API is also available for TypeScript
|
|
|
251
251
|
|
|
252
252
|
## Highlights
|
|
253
253
|
|
|
254
|
-
- Locate paragraphs, text lines, images, vector paths, form fields, and pages by page number, coordinates, or text
|
|
254
|
+
- Locate paragraphs, text lines, images, vector paths, form fields, and pages by page number, coordinates, or text patterns.
|
|
255
255
|
- Edit existing content in place with fluent editors and context managers that apply changes safely.
|
|
256
256
|
- Programmatically control third-party PDFs—modify invoices, contracts, and reports you did not author.
|
|
257
|
-
- Add content with precise XY positioning using paragraph and
|
|
257
|
+
- Add content with precise XY positioning using paragraph, image, and vector path builders with custom fonts and colors.
|
|
258
|
+
- Draw lines, rectangles, and Bezier curves with configurable stroke width, dash patterns, and fill colors.
|
|
259
|
+
- Redact sensitive content—replace text, images, or form fields with customizable placeholders.
|
|
258
260
|
- Export results as bytes for downstream processing or save directly to disk with one call.
|
|
259
261
|
|
|
260
262
|
## What Makes PDFDancer Different
|
|
@@ -264,6 +266,8 @@ pixel-perfect control from Python. The same API is also available for TypeScript
|
|
|
264
266
|
- **Surgical text replacement**: Swap or rewrite paragraphs without reflowing the rest of the page.
|
|
265
267
|
- **Form manipulation**: Inspect, fill, and update AcroForm fields programmatically.
|
|
266
268
|
- **Coordinate-based selection**: Select objects by position, bounding box, or text patterns.
|
|
269
|
+
- **Vector graphics**: Draw lines, rectangles, and Bezier curves with full control over stroke and fill properties.
|
|
270
|
+
- **Secure redaction**: Permanently remove sensitive content and replace with customizable markers.
|
|
267
271
|
- **Real PDF editing**: Modify the underlying PDF structure instead of merely stamping overlays.
|
|
268
272
|
|
|
269
273
|
## Installation
|
|
@@ -300,7 +304,7 @@ with PDFDancer.open(
|
|
|
300
304
|
.font(StandardFonts.HELVETICA, 12) \
|
|
301
305
|
.color(Color(70, 70, 70)) \
|
|
302
306
|
.line_spacing(1.4) \
|
|
303
|
-
.at(page_number=
|
|
307
|
+
.at(page_number=1, x=72, y=520) \
|
|
304
308
|
.add()
|
|
305
309
|
|
|
306
310
|
# Persist the modified document
|
|
@@ -321,7 +325,7 @@ with PDFDancer.new(token="your-api-token") as pdf:
|
|
|
321
325
|
.font(StandardFonts.TIMES_BOLD, 18) \
|
|
322
326
|
.color(Color(10, 10, 80)) \
|
|
323
327
|
.line_spacing(1.2) \
|
|
324
|
-
.at(page_number=
|
|
328
|
+
.at(page_number=1, x=72, y=730) \
|
|
325
329
|
.add()
|
|
326
330
|
|
|
327
331
|
pdf.new_image() \
|
|
@@ -355,7 +359,87 @@ with PDFDancer.open("contract.pdf") as pdf:
|
|
|
355
359
|
```
|
|
356
360
|
|
|
357
361
|
Selectors return typed objects (`ParagraphObject`, `TextLineObject`, `ImageObject`, `FormFieldObject`, `PageClient`, …)
|
|
358
|
-
with helpers such as `delete()`, `move_to(x, y)`, or `edit()` depending on the object type.
|
|
362
|
+
with helpers such as `delete()`, `move_to(x, y)`, `redact()`, or `edit()` depending on the object type.
|
|
363
|
+
|
|
364
|
+
**Singular selection methods** return the first match (or `None`) for convenience:
|
|
365
|
+
|
|
366
|
+
```python
|
|
367
|
+
# Instead of: paragraphs = page.select_paragraphs_starting_with("Invoice")[0]
|
|
368
|
+
paragraph = page.select_paragraph_starting_with("Invoice") # Returns first match or None
|
|
369
|
+
image = page.select_image_at(100, 200) # Returns first match or None
|
|
370
|
+
field = pdf.select_form_field_by_name("email") # Returns first match or None
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
## Draw Vector Paths
|
|
374
|
+
|
|
375
|
+
Add lines, curves, and shapes to your PDFs with fluent builders:
|
|
376
|
+
|
|
377
|
+
```python
|
|
378
|
+
from pdfdancer import PDFDancer, Color, Point
|
|
379
|
+
|
|
380
|
+
with PDFDancer.open("document.pdf") as pdf:
|
|
381
|
+
page = pdf.page(0)
|
|
382
|
+
|
|
383
|
+
# Draw a simple line
|
|
384
|
+
page.new_line() \
|
|
385
|
+
.from_point(100, 700) \
|
|
386
|
+
.to_point(500, 700) \
|
|
387
|
+
.stroke_color(Color(0, 0, 255)) \
|
|
388
|
+
.stroke_width(2.0) \
|
|
389
|
+
.add()
|
|
390
|
+
|
|
391
|
+
# Draw a rectangle
|
|
392
|
+
page.new_rectangle() \
|
|
393
|
+
.at_coordinates(100, 500) \
|
|
394
|
+
.with_size(200, 100) \
|
|
395
|
+
.stroke_color(Color(0, 0, 0)) \
|
|
396
|
+
.fill_color(Color(255, 255, 200)) \
|
|
397
|
+
.add()
|
|
398
|
+
|
|
399
|
+
# Draw a bezier curve
|
|
400
|
+
page.new_bezier() \
|
|
401
|
+
.from_point(100, 400) \
|
|
402
|
+
.control_point_1(150, 450) \
|
|
403
|
+
.control_point_2(250, 350) \
|
|
404
|
+
.to_point(300, 400) \
|
|
405
|
+
.stroke_width(1.5) \
|
|
406
|
+
.add()
|
|
407
|
+
|
|
408
|
+
# Build complex paths with multiple segments
|
|
409
|
+
page.new_path() \
|
|
410
|
+
.stroke_color(Color(255, 0, 0)) \
|
|
411
|
+
.add_line(Point(50, 200), Point(150, 200)) \
|
|
412
|
+
.add_line(Point(150, 200), Point(100, 280)) \
|
|
413
|
+
.add_line(Point(100, 280), Point(50, 200)) \
|
|
414
|
+
.add()
|
|
415
|
+
|
|
416
|
+
pdf.save("annotated.pdf")
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
## Redact Sensitive Content
|
|
420
|
+
|
|
421
|
+
Remove text, images, or form fields and replace them with redaction markers:
|
|
422
|
+
|
|
423
|
+
```python
|
|
424
|
+
from pdfdancer import PDFDancer, Color
|
|
425
|
+
|
|
426
|
+
with PDFDancer.open("confidential.pdf") as pdf:
|
|
427
|
+
# Redact paragraphs containing sensitive patterns
|
|
428
|
+
for para in pdf.select_paragraphs():
|
|
429
|
+
if "SSN:" in para.text or "Password:" in para.text:
|
|
430
|
+
para.redact("[REDACTED]")
|
|
431
|
+
|
|
432
|
+
# Redact all images on a specific page
|
|
433
|
+
for image in pdf.page(0).select_images():
|
|
434
|
+
image.redact()
|
|
435
|
+
|
|
436
|
+
# Bulk redact multiple objects with custom placeholder color
|
|
437
|
+
form_fields = pdf.select_form_fields_by_name("credit_card")
|
|
438
|
+
result = pdf.redact(form_fields, replacement="[REMOVED]", placeholder_color=Color(0, 0, 0))
|
|
439
|
+
print(f"Redacted {result.count} items")
|
|
440
|
+
|
|
441
|
+
pdf.save("redacted.pdf")
|
|
442
|
+
```
|
|
359
443
|
|
|
360
444
|
## Configuration
|
|
361
445
|
|
|
@@ -372,6 +456,7 @@ Operations raise subclasses of `PdfDancerException`:
|
|
|
372
456
|
- `FontNotFoundException`: requested font unavailable on the service.
|
|
373
457
|
- `HttpClientException`: transport or server errors with detailed context.
|
|
374
458
|
- `SessionException`: session creation and lifecycle failures.
|
|
459
|
+
- `RateLimitException`: API rate limit exceeded; includes retry-after timing.
|
|
375
460
|
|
|
376
461
|
Wrap automated workflows in `try/except` blocks to surface actionable errors to your users.
|
|
377
462
|
|
|
@@ -389,7 +474,7 @@ Wrap automated workflows in `try/except` blocks to surface actionable errors to
|
|
|
389
474
|
|
|
390
475
|
```bash
|
|
391
476
|
git clone https://github.com/MenschMachine/pdfdancer-client-python.git
|
|
392
|
-
cd pdfdancer-client-python
|
|
477
|
+
cd pdfdancer-client-python
|
|
393
478
|
```
|
|
394
479
|
|
|
395
480
|
#### 2. Create a Virtual Environment
|
|
@@ -514,25 +599,27 @@ mypy src/pdfdancer/
|
|
|
514
599
|
### Project Structure
|
|
515
600
|
|
|
516
601
|
```
|
|
517
|
-
pdfdancer-client-python/
|
|
518
|
-
├── src/pdfdancer/
|
|
519
|
-
│ ├── __init__.py
|
|
520
|
-
│ ├── pdfdancer_v1.py
|
|
602
|
+
pdfdancer-client-python/
|
|
603
|
+
├── src/pdfdancer/ # Main package source
|
|
604
|
+
│ ├── __init__.py # Package exports
|
|
605
|
+
│ ├── pdfdancer_v1.py # Core PDFDancer and PageClient classes
|
|
521
606
|
│ ├── paragraph_builder.py # Fluent paragraph builders
|
|
522
|
-
│ ├──
|
|
523
|
-
│ ├──
|
|
524
|
-
│ ├──
|
|
525
|
-
│
|
|
526
|
-
├──
|
|
527
|
-
│ ├──
|
|
528
|
-
│
|
|
529
|
-
|
|
530
|
-
├──
|
|
531
|
-
├──
|
|
532
|
-
|
|
533
|
-
├──
|
|
534
|
-
├──
|
|
535
|
-
|
|
607
|
+
│ ├── text_line_builder.py # Fluent text line builders
|
|
608
|
+
│ ├── image_builder.py # Fluent image builders
|
|
609
|
+
│ ├── path_builder.py # Vector path builders (lines, beziers, rectangles)
|
|
610
|
+
│ ├── page_builder.py # Page creation builder
|
|
611
|
+
│ ├── models.py # Data models (Position, Font, Color, etc.)
|
|
612
|
+
│ ├── types.py # Object wrappers (ParagraphObject, etc.)
|
|
613
|
+
│ └── exceptions.py # Exception hierarchy
|
|
614
|
+
├── tests/ # Test suite
|
|
615
|
+
│ ├── test_models.py # Model unit tests
|
|
616
|
+
│ ├── e2e/ # End-to-end integration tests
|
|
617
|
+
│ └── fixtures/ # Test fixtures and sample PDFs
|
|
618
|
+
├── docs/ # Documentation
|
|
619
|
+
├── dist/ # Build artifacts (created after packaging)
|
|
620
|
+
├── pyproject.toml # Project metadata and dependencies
|
|
621
|
+
├── release.py # Helper for publishing releases
|
|
622
|
+
└── README.md # This file
|
|
536
623
|
```
|
|
537
624
|
|
|
538
625
|
### Troubleshooting
|
|
@@ -12,10 +12,12 @@ pixel-perfect control from Python. The same API is also available for TypeScript
|
|
|
12
12
|
|
|
13
13
|
## Highlights
|
|
14
14
|
|
|
15
|
-
- Locate paragraphs, text lines, images, vector paths, form fields, and pages by page number, coordinates, or text
|
|
15
|
+
- Locate paragraphs, text lines, images, vector paths, form fields, and pages by page number, coordinates, or text patterns.
|
|
16
16
|
- Edit existing content in place with fluent editors and context managers that apply changes safely.
|
|
17
17
|
- Programmatically control third-party PDFs—modify invoices, contracts, and reports you did not author.
|
|
18
|
-
- Add content with precise XY positioning using paragraph and
|
|
18
|
+
- Add content with precise XY positioning using paragraph, image, and vector path builders with custom fonts and colors.
|
|
19
|
+
- Draw lines, rectangles, and Bezier curves with configurable stroke width, dash patterns, and fill colors.
|
|
20
|
+
- Redact sensitive content—replace text, images, or form fields with customizable placeholders.
|
|
19
21
|
- Export results as bytes for downstream processing or save directly to disk with one call.
|
|
20
22
|
|
|
21
23
|
## What Makes PDFDancer Different
|
|
@@ -25,6 +27,8 @@ pixel-perfect control from Python. The same API is also available for TypeScript
|
|
|
25
27
|
- **Surgical text replacement**: Swap or rewrite paragraphs without reflowing the rest of the page.
|
|
26
28
|
- **Form manipulation**: Inspect, fill, and update AcroForm fields programmatically.
|
|
27
29
|
- **Coordinate-based selection**: Select objects by position, bounding box, or text patterns.
|
|
30
|
+
- **Vector graphics**: Draw lines, rectangles, and Bezier curves with full control over stroke and fill properties.
|
|
31
|
+
- **Secure redaction**: Permanently remove sensitive content and replace with customizable markers.
|
|
28
32
|
- **Real PDF editing**: Modify the underlying PDF structure instead of merely stamping overlays.
|
|
29
33
|
|
|
30
34
|
## Installation
|
|
@@ -61,7 +65,7 @@ with PDFDancer.open(
|
|
|
61
65
|
.font(StandardFonts.HELVETICA, 12) \
|
|
62
66
|
.color(Color(70, 70, 70)) \
|
|
63
67
|
.line_spacing(1.4) \
|
|
64
|
-
.at(page_number=
|
|
68
|
+
.at(page_number=1, x=72, y=520) \
|
|
65
69
|
.add()
|
|
66
70
|
|
|
67
71
|
# Persist the modified document
|
|
@@ -82,7 +86,7 @@ with PDFDancer.new(token="your-api-token") as pdf:
|
|
|
82
86
|
.font(StandardFonts.TIMES_BOLD, 18) \
|
|
83
87
|
.color(Color(10, 10, 80)) \
|
|
84
88
|
.line_spacing(1.2) \
|
|
85
|
-
.at(page_number=
|
|
89
|
+
.at(page_number=1, x=72, y=730) \
|
|
86
90
|
.add()
|
|
87
91
|
|
|
88
92
|
pdf.new_image() \
|
|
@@ -116,7 +120,87 @@ with PDFDancer.open("contract.pdf") as pdf:
|
|
|
116
120
|
```
|
|
117
121
|
|
|
118
122
|
Selectors return typed objects (`ParagraphObject`, `TextLineObject`, `ImageObject`, `FormFieldObject`, `PageClient`, …)
|
|
119
|
-
with helpers such as `delete()`, `move_to(x, y)`, or `edit()` depending on the object type.
|
|
123
|
+
with helpers such as `delete()`, `move_to(x, y)`, `redact()`, or `edit()` depending on the object type.
|
|
124
|
+
|
|
125
|
+
**Singular selection methods** return the first match (or `None`) for convenience:
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
# Instead of: paragraphs = page.select_paragraphs_starting_with("Invoice")[0]
|
|
129
|
+
paragraph = page.select_paragraph_starting_with("Invoice") # Returns first match or None
|
|
130
|
+
image = page.select_image_at(100, 200) # Returns first match or None
|
|
131
|
+
field = pdf.select_form_field_by_name("email") # Returns first match or None
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Draw Vector Paths
|
|
135
|
+
|
|
136
|
+
Add lines, curves, and shapes to your PDFs with fluent builders:
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
from pdfdancer import PDFDancer, Color, Point
|
|
140
|
+
|
|
141
|
+
with PDFDancer.open("document.pdf") as pdf:
|
|
142
|
+
page = pdf.page(0)
|
|
143
|
+
|
|
144
|
+
# Draw a simple line
|
|
145
|
+
page.new_line() \
|
|
146
|
+
.from_point(100, 700) \
|
|
147
|
+
.to_point(500, 700) \
|
|
148
|
+
.stroke_color(Color(0, 0, 255)) \
|
|
149
|
+
.stroke_width(2.0) \
|
|
150
|
+
.add()
|
|
151
|
+
|
|
152
|
+
# Draw a rectangle
|
|
153
|
+
page.new_rectangle() \
|
|
154
|
+
.at_coordinates(100, 500) \
|
|
155
|
+
.with_size(200, 100) \
|
|
156
|
+
.stroke_color(Color(0, 0, 0)) \
|
|
157
|
+
.fill_color(Color(255, 255, 200)) \
|
|
158
|
+
.add()
|
|
159
|
+
|
|
160
|
+
# Draw a bezier curve
|
|
161
|
+
page.new_bezier() \
|
|
162
|
+
.from_point(100, 400) \
|
|
163
|
+
.control_point_1(150, 450) \
|
|
164
|
+
.control_point_2(250, 350) \
|
|
165
|
+
.to_point(300, 400) \
|
|
166
|
+
.stroke_width(1.5) \
|
|
167
|
+
.add()
|
|
168
|
+
|
|
169
|
+
# Build complex paths with multiple segments
|
|
170
|
+
page.new_path() \
|
|
171
|
+
.stroke_color(Color(255, 0, 0)) \
|
|
172
|
+
.add_line(Point(50, 200), Point(150, 200)) \
|
|
173
|
+
.add_line(Point(150, 200), Point(100, 280)) \
|
|
174
|
+
.add_line(Point(100, 280), Point(50, 200)) \
|
|
175
|
+
.add()
|
|
176
|
+
|
|
177
|
+
pdf.save("annotated.pdf")
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Redact Sensitive Content
|
|
181
|
+
|
|
182
|
+
Remove text, images, or form fields and replace them with redaction markers:
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
from pdfdancer import PDFDancer, Color
|
|
186
|
+
|
|
187
|
+
with PDFDancer.open("confidential.pdf") as pdf:
|
|
188
|
+
# Redact paragraphs containing sensitive patterns
|
|
189
|
+
for para in pdf.select_paragraphs():
|
|
190
|
+
if "SSN:" in para.text or "Password:" in para.text:
|
|
191
|
+
para.redact("[REDACTED]")
|
|
192
|
+
|
|
193
|
+
# Redact all images on a specific page
|
|
194
|
+
for image in pdf.page(0).select_images():
|
|
195
|
+
image.redact()
|
|
196
|
+
|
|
197
|
+
# Bulk redact multiple objects with custom placeholder color
|
|
198
|
+
form_fields = pdf.select_form_fields_by_name("credit_card")
|
|
199
|
+
result = pdf.redact(form_fields, replacement="[REMOVED]", placeholder_color=Color(0, 0, 0))
|
|
200
|
+
print(f"Redacted {result.count} items")
|
|
201
|
+
|
|
202
|
+
pdf.save("redacted.pdf")
|
|
203
|
+
```
|
|
120
204
|
|
|
121
205
|
## Configuration
|
|
122
206
|
|
|
@@ -133,6 +217,7 @@ Operations raise subclasses of `PdfDancerException`:
|
|
|
133
217
|
- `FontNotFoundException`: requested font unavailable on the service.
|
|
134
218
|
- `HttpClientException`: transport or server errors with detailed context.
|
|
135
219
|
- `SessionException`: session creation and lifecycle failures.
|
|
220
|
+
- `RateLimitException`: API rate limit exceeded; includes retry-after timing.
|
|
136
221
|
|
|
137
222
|
Wrap automated workflows in `try/except` blocks to surface actionable errors to your users.
|
|
138
223
|
|
|
@@ -150,7 +235,7 @@ Wrap automated workflows in `try/except` blocks to surface actionable errors to
|
|
|
150
235
|
|
|
151
236
|
```bash
|
|
152
237
|
git clone https://github.com/MenschMachine/pdfdancer-client-python.git
|
|
153
|
-
cd pdfdancer-client-python
|
|
238
|
+
cd pdfdancer-client-python
|
|
154
239
|
```
|
|
155
240
|
|
|
156
241
|
#### 2. Create a Virtual Environment
|
|
@@ -275,25 +360,27 @@ mypy src/pdfdancer/
|
|
|
275
360
|
### Project Structure
|
|
276
361
|
|
|
277
362
|
```
|
|
278
|
-
pdfdancer-client-python/
|
|
279
|
-
├── src/pdfdancer/
|
|
280
|
-
│ ├── __init__.py
|
|
281
|
-
│ ├── pdfdancer_v1.py
|
|
363
|
+
pdfdancer-client-python/
|
|
364
|
+
├── src/pdfdancer/ # Main package source
|
|
365
|
+
│ ├── __init__.py # Package exports
|
|
366
|
+
│ ├── pdfdancer_v1.py # Core PDFDancer and PageClient classes
|
|
282
367
|
│ ├── paragraph_builder.py # Fluent paragraph builders
|
|
283
|
-
│ ├──
|
|
284
|
-
│ ├──
|
|
285
|
-
│ ├──
|
|
286
|
-
│
|
|
287
|
-
├──
|
|
288
|
-
│ ├──
|
|
289
|
-
│
|
|
290
|
-
|
|
291
|
-
├──
|
|
292
|
-
├──
|
|
293
|
-
|
|
294
|
-
├──
|
|
295
|
-
├──
|
|
296
|
-
|
|
368
|
+
│ ├── text_line_builder.py # Fluent text line builders
|
|
369
|
+
│ ├── image_builder.py # Fluent image builders
|
|
370
|
+
│ ├── path_builder.py # Vector path builders (lines, beziers, rectangles)
|
|
371
|
+
│ ├── page_builder.py # Page creation builder
|
|
372
|
+
│ ├── models.py # Data models (Position, Font, Color, etc.)
|
|
373
|
+
│ ├── types.py # Object wrappers (ParagraphObject, etc.)
|
|
374
|
+
│ └── exceptions.py # Exception hierarchy
|
|
375
|
+
├── tests/ # Test suite
|
|
376
|
+
│ ├── test_models.py # Model unit tests
|
|
377
|
+
│ ├── e2e/ # End-to-end integration tests
|
|
378
|
+
│ └── fixtures/ # Test fixtures and sample PDFs
|
|
379
|
+
├── docs/ # Documentation
|
|
380
|
+
├── dist/ # Build artifacts (created after packaging)
|
|
381
|
+
├── pyproject.toml # Project metadata and dependencies
|
|
382
|
+
├── release.py # Helper for publishing releases
|
|
383
|
+
└── README.md # This file
|
|
297
384
|
```
|
|
298
385
|
|
|
299
386
|
### Troubleshooting
|