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.
Files changed (79) hide show
  1. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/PKG-INFO +112 -25
  2. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/README.md +111 -24
  3. pdfdancer_client_python-0.3.3/docs/api-schemas/v0.yml +4945 -0
  4. pdfdancer_client_python-0.3.1/docs/openapi.yml → pdfdancer_client_python-0.3.3/docs/api-schemas/v1.yml +1433 -1006
  5. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/pyproject.toml +1 -1
  6. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer/__init__.py +4 -0
  7. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer/models.py +49 -0
  8. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer/pdfdancer_v1.py +105 -3
  9. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer/types.py +8 -0
  10. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer_client_python.egg-info/PKG-INFO +112 -25
  11. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer_client_python.egg-info/SOURCES.txt +3 -1
  12. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_line.py +1 -4
  13. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_paragraph.py +0 -3
  14. pdfdancer_client_python-0.3.3/tests/e2e/test_redact.py +287 -0
  15. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_singular_selection.py +0 -1
  16. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_text_line_edit.py +1 -1
  17. pdfdancer_client_python-0.3.3/update-api-spec.sh +5 -0
  18. pdfdancer_client_python-0.3.1/update-api-spec.sh +0 -3
  19. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/.claude/commands/discuss.md +0 -0
  20. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/.flake8 +0 -0
  21. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/.github/workflows/ci.yml +0 -0
  22. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/.github/workflows/daily-tests.yml +0 -0
  23. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/.gitignore +0 -0
  24. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/CLAUDE.md +0 -0
  25. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/LICENSE +0 -0
  26. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/NOTICE +0 -0
  27. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/TODO.md +0 -0
  28. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/check.py +0 -0
  29. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/media/logo-orange-512h.webp +0 -0
  30. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/media/logo-orange-60h.webp +0 -0
  31. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/release.py +0 -0
  32. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/setup.cfg +0 -0
  33. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer/exceptions.py +0 -0
  34. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer/fingerprint.py +0 -0
  35. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer/image_builder.py +0 -0
  36. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer/page_builder.py +0 -0
  37. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer/paragraph_builder.py +0 -0
  38. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer/path_builder.py +0 -0
  39. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer/text_line_builder.py +0 -0
  40. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer_client_python.egg-info/dependency_links.txt +0 -0
  41. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer_client_python.egg-info/requires.txt +0 -0
  42. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/src/pdfdancer_client_python.egg-info/top_level.txt +0 -0
  43. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/test.sh +0 -0
  44. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/__init__.py +0 -0
  45. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/conftest.py +0 -0
  46. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/__init__.py +0 -0
  47. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/pdf_assertions.py +0 -0
  48. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_acroform.py +0 -0
  49. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_bezier_builder.py +0 -0
  50. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_context_manager.py +0 -0
  51. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_form_x_objects.py +0 -0
  52. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_image.py +0 -0
  53. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_line_builder.py +0 -0
  54. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_new_pdf.py +0 -0
  55. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_page.py +0 -0
  56. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_path.py +0 -0
  57. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_path_builder.py +0 -0
  58. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_path_builder_rectangle.py +0 -0
  59. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_path_comprehensive.py +0 -0
  60. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_pdfdancer.py +0 -0
  61. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_positioning.py +0 -0
  62. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_rectangle_builder.py +0 -0
  63. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/e2e/test_snapshot.py +0 -0
  64. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/fixtures/DancingScript-Regular.ttf +0 -0
  65. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/fixtures/Empty.pdf +0 -0
  66. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/fixtures/JetBrainsMono-Regular.ttf +0 -0
  67. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/fixtures/Showcase.pdf +0 -0
  68. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/fixtures/basic-paths.pdf +0 -0
  69. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/fixtures/form-xobject-example.pdf +0 -0
  70. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/fixtures/logo-80.png +0 -0
  71. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/fixtures/mixed-form-types.pdf +0 -0
  72. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/test_anonymous_token.py +0 -0
  73. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/test_fingerprint.py +0 -0
  74. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/test_models.py +0 -0
  75. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/test_openapi_compliance.py +0 -0
  76. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/test_path_models.py +0 -0
  77. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/test_pdf_object_equality.py +0 -0
  78. {pdfdancer_client_python-0.3.1 → pdfdancer_client_python-0.3.3}/tests/test_rate_limit.py +0 -0
  79. {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.1
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 prefixes.
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 image builders, custom fonts, and color helpers.
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=0, x=72, y=520) \
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=0, x=72, y=730) \
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/_main
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/_main/
518
- ├── src/pdfdancer/ # Main package source
519
- │ ├── __init__.py # Package exports
520
- │ ├── pdfdancer_v1.py # Core PDFDancer and PageClient classes
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
- │ ├── image_builder.py # Fluent image builders
523
- │ ├── models.py # Data models (Position, Font, Color, etc.)
524
- │ ├── types.py # Object wrappers (ParagraphObject, etc.)
525
- └── exceptions.py # Exception hierarchy
526
- ├── tests/ # Test suite
527
- │ ├── test_models.py # Model unit tests
528
- ├── e2e/ # End-to-end integration tests
529
- │ └── fixtures/ # Test fixtures and sample PDFs
530
- ├── docs/ # Documentation
531
- ├── dist/ # Build artifacts (created after packaging)
532
- ├── logs/ # Local execution logs (ignored in VCS)
533
- ├── pyproject.toml # Project metadata and dependencies
534
- ├── release.py # Helper for publishing releases
535
- └── README.md # This file
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 prefixes.
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 image builders, custom fonts, and color helpers.
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=0, x=72, y=520) \
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=0, x=72, y=730) \
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/_main
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/_main/
279
- ├── src/pdfdancer/ # Main package source
280
- │ ├── __init__.py # Package exports
281
- │ ├── pdfdancer_v1.py # Core PDFDancer and PageClient classes
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
- │ ├── image_builder.py # Fluent image builders
284
- │ ├── models.py # Data models (Position, Font, Color, etc.)
285
- │ ├── types.py # Object wrappers (ParagraphObject, etc.)
286
- └── exceptions.py # Exception hierarchy
287
- ├── tests/ # Test suite
288
- │ ├── test_models.py # Model unit tests
289
- ├── e2e/ # End-to-end integration tests
290
- │ └── fixtures/ # Test fixtures and sample PDFs
291
- ├── docs/ # Documentation
292
- ├── dist/ # Build artifacts (created after packaging)
293
- ├── logs/ # Local execution logs (ignored in VCS)
294
- ├── pyproject.toml # Project metadata and dependencies
295
- ├── release.py # Helper for publishing releases
296
- └── README.md # This file
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