pdfdancer-client-python 0.2.11__tar.gz → 0.2.13__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pdfdancer-client-python might be problematic. Click here for more details.

Files changed (53) hide show
  1. pdfdancer_client_python-0.2.13/.claude/commands/discuss.md +4 -0
  2. pdfdancer_client_python-0.2.13/.github/workflows/ci.yml +40 -0
  3. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/.gitignore +1 -0
  4. pdfdancer_client_python-0.2.13/CLAUDE.md +206 -0
  5. {pdfdancer_client_python-0.2.11/src/pdfdancer_client_python.egg-info → pdfdancer_client_python-0.2.13}/PKG-INFO +34 -27
  6. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/README.md +31 -25
  7. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/pyproject.toml +5 -4
  8. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/src/pdfdancer/__init__.py +4 -1
  9. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/src/pdfdancer/models.py +135 -2
  10. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/src/pdfdancer/paragraph_builder.py +8 -3
  11. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/src/pdfdancer/pdfdancer_v1.py +339 -22
  12. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/src/pdfdancer/types.py +2 -2
  13. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13/src/pdfdancer_client_python.egg-info}/PKG-INFO +34 -27
  14. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/src/pdfdancer_client_python.egg-info/SOURCES.txt +3 -0
  15. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/tests/conftest.py +3 -1
  16. pdfdancer_client_python-0.2.13/tests/e2e/pdf_assertions.py +230 -0
  17. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/tests/e2e/test_acroform.py +19 -0
  18. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/tests/e2e/test_form_x_objects.py +11 -4
  19. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/tests/e2e/test_image.py +32 -10
  20. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/tests/e2e/test_line.py +25 -15
  21. pdfdancer_client_python-0.2.13/tests/e2e/test_new_pdf.py +121 -0
  22. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/tests/e2e/test_page.py +33 -0
  23. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/tests/e2e/test_path.py +19 -0
  24. pdfdancer_client_python-0.2.13/tests/e2e/test_positioning.py +2 -0
  25. pdfdancer_client_python-0.2.11/.github/workflows/ci.yml +0 -40
  26. pdfdancer_client_python-0.2.11/CLAUDE.md +0 -121
  27. pdfdancer_client_python-0.2.11/tests/e2e/pdf_assertions.py +0 -100
  28. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/docs/openapi.yml +0 -0
  29. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/release.py +0 -0
  30. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/requirements-dev.txt +0 -0
  31. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/requirements.txt +0 -0
  32. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/setup.cfg +0 -0
  33. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/src/pdfdancer/exceptions.py +0 -0
  34. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/src/pdfdancer/image_builder.py +0 -0
  35. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/src/pdfdancer_client_python.egg-info/dependency_links.txt +0 -0
  36. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/src/pdfdancer_client_python.egg-info/requires.txt +0 -0
  37. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/src/pdfdancer_client_python.egg-info/top_level.txt +0 -0
  38. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/tests/__init__.py +0 -0
  39. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/tests/e2e/__init__.py +0 -0
  40. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/tests/e2e/test_paragraph.py +0 -0
  41. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/tests/e2e/test_pdfdancer.py +0 -0
  42. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/tests/fixtures/DancingScript-Regular.ttf +0 -0
  43. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/tests/fixtures/Empty.pdf +0 -0
  44. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/tests/fixtures/JetBrainsMono-Regular.ttf +0 -0
  45. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/tests/fixtures/ObviouslyAwesome.pdf +0 -0
  46. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/tests/fixtures/basic-paths.pdf +0 -0
  47. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/tests/fixtures/form-xobject-example.pdf +0 -0
  48. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/tests/fixtures/logo-80.png +0 -0
  49. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/tests/fixtures/mixed-form-types.pdf +0 -0
  50. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/tests/test_models.py +0 -0
  51. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/tests/test_openapi_compliance.py +0 -0
  52. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/tests/test_pdf_object_equality.py +0 -0
  53. {pdfdancer_client_python-0.2.11 → pdfdancer_client_python-0.2.13}/tests/test_standard_fonts.py +0 -0
@@ -0,0 +1,4 @@
1
+ We discuss this: $ARGUMENTS.
2
+ You ask everything you need to know to completely understand what I want to know.
3
+ We discuss options, pros and cons and side effects.
4
+ You dont start to code until you have my approval.
@@ -0,0 +1,40 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [ main, staging, develop, development, dev ]
6
+ pull_request:
7
+ branches: [ main, staging, develop, development, dev ]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: [ '3.10', '3.11', '3.12', '3.13' ]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Set up Python ${{ matrix.python-version }}
20
+ uses: actions/setup-python@v4
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+
24
+ - name: Create virtual environment
25
+ run: python -m venv venv
26
+
27
+ - name: Install dependencies
28
+ run: |
29
+ venv/bin/pip install --upgrade pip
30
+ venv/bin/pip install -e .
31
+ venv/bin/pip install -r requirements-dev.txt
32
+
33
+ - name: Run tests
34
+ run: PDFDANCER_BASE_URL=https://api.staging.pdfdancer.com venv/bin/python -m pytest tests/ -v --ignore=tests/e2e/
35
+
36
+ - name: Build distribution packages
37
+ run: venv/bin/python -m build
38
+
39
+ - name: Validate packages
40
+ run: venv/bin/python -m twine check dist/*
@@ -7,3 +7,4 @@ src/pdfdancer_python.egg-info
7
7
  .aider*
8
8
  src/pdfdancer_client_python.egg-info
9
9
  /logs/
10
+ /venv
@@ -0,0 +1,206 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with the PDFDancer Python client.
4
+
5
+ ## Project Overview
6
+
7
+ This is a **Python client library** for the PDFDancer PDF manipulation API. It uses a **100% manual implementation**
8
+ that:
9
+
10
+ - **Mirrors Java client structure exactly** - Same methods, validation, and patterns
11
+ - **Pure Python implementation** - Uses `requests` for HTTP calls, no code generation
12
+ - **Pythonic conventions** - Snake_case methods, type hints, context managers
13
+ - **Strict validation** - Matches Java client validation exactly
14
+
15
+ ## Essential Commands
16
+
17
+ ### Development
18
+
19
+ - `python -m venv venv` - Create virtual environment
20
+ - `venv/bin/pip install -e .` - Install in development mode
21
+ - `venv/bin/pip install -r requirements-dev.txt` - Install dev dependencies
22
+
23
+ ### Testing
24
+
25
+ - `venv/bin/python -m pytest tests/ -v` - Run all tests (123 tests)
26
+ - `venv/bin/python -m pytest tests/e2e/ -v` - Run end-to-end tests
27
+ - `venv/bin/python -m pytest tests/test_models.py -v` - Run model tests
28
+
29
+ ### Building & Publishing
30
+
31
+ - `venv/bin/python -m build` - Build distribution packages
32
+ - `venv/bin/python -m twine check dist/*` - Validate packages
33
+ - `venv/bin/python -m twine upload dist/*` - Publish to PyPI
34
+
35
+ ## Architecture
36
+
37
+ ### Manual Implementation
38
+
39
+ The client is a pure manual implementation that closely mirrors the Java client:
40
+
41
+ ```python
42
+ # Open existing PDF
43
+ pdf = PDFDancer.open(pdf_data="document.pdf")
44
+
45
+ # Create new blank PDF
46
+ pdf = PDFDancer.new(page_size=PageSize.A4, orientation=Orientation.PORTRAIT)
47
+
48
+ # Select and manipulate objects
49
+ paragraphs = pdf.select_paragraphs()
50
+ paragraphs[0].delete()
51
+
52
+ images = pdf.select_images()
53
+ images[0].move(Position.at_page_coordinates(0, 100, 200))
54
+
55
+ # Page-level operations
56
+ page = pdf.page(0)
57
+ page_paragraphs = page.select_paragraphs()
58
+ page.delete()
59
+
60
+ # Builder pattern for new content
61
+ pdf.new_paragraph()
62
+ .from_string("Text content")
63
+ .with_font(Font("Arial", 12))
64
+ .at_page_coordinates(0, 100, 200)
65
+ .add()
66
+
67
+ # Save modified PDF
68
+ pdf.save("output.pdf")
69
+ ```
70
+
71
+ ### Package Structure
72
+
73
+ - `src/pdfdancer/` - Main package
74
+ - `pdfdancer_v1.py` - Main PDFDancer class and PageClient
75
+ - `paragraph_builder.py` - ParagraphBuilder and ParagraphPageBuilder for fluent construction
76
+ - `image_builder.py` - ImageBuilder for fluent image construction
77
+ - `models.py` - Model classes (ObjectRef, Position, Font, Color, etc.)
78
+ - `types.py` - Object wrapper types (ParagraphObject, ImageObject, etc.)
79
+ - `exceptions.py` - Exception hierarchy
80
+
81
+ ### Key Features
82
+
83
+ - **Dual initialization**: `PDFDancer.open()` for existing PDFs, `PDFDancer.new()` for blank PDFs
84
+ - **Session-based operations**: All constructors create server session automatically
85
+ - **Object-oriented API**: Selected objects (paragraphs, images, etc.) have methods like `.delete()`, `.move()`
86
+ - **Page-level operations**: `pdf.page(index)` provides page-scoped selections and operations
87
+ - **Builder pattern**: `new_paragraph()` and `new_image()` for fluent construction
88
+ - **Strict validation**: All validation matches Java client exactly
89
+ - **Exception handling**: FontNotFoundException, ValidationException, HttpClientException, etc.
90
+ - **Type safety**: Full type hints throughout
91
+ - **E2E testing utilities**: PDFAssertions for comprehensive PDF validation
92
+
93
+ ## API Patterns
94
+
95
+ ### Initialization
96
+
97
+ ```python
98
+ # Open existing PDF with token from env var PDFDANCER_TOKEN
99
+ pdf = PDFDancer.open(pdf_data="document.pdf")
100
+
101
+ # Open with explicit token
102
+ pdf = PDFDancer.open(pdf_data="document.pdf", token="your-token")
103
+
104
+ # Create new blank PDF
105
+ pdf = PDFDancer.new(
106
+ page_size=PageSize.A4,
107
+ orientation=Orientation.PORTRAIT,
108
+ initial_page_count=5
109
+ )
110
+ ```
111
+
112
+ ### Selection and Manipulation
113
+
114
+ ```python
115
+ # Document-level selections
116
+ paragraphs = pdf.select_paragraphs()
117
+ images = pdf.select_images()
118
+ form_fields = pdf.select_form_fields_by_name("fieldName")
119
+
120
+ # Page-level selections
121
+ page = pdf.page(0)
122
+ page_paragraphs = page.select_paragraphs_starting_with("Invoice")
123
+ page_images = page.select_images_at(100, 200)
124
+
125
+ # Object manipulation
126
+ paragraphs[0].delete()
127
+ paragraphs[0].move(Position.at_page_coordinates(1, 50, 50))
128
+ paragraphs[0].modify("New text content")
129
+
130
+ # Pattern-based selections
131
+ paragraphs = page.select_paragraphs_matching(r"\d{4}-\d{2}-\d{2}")
132
+ text_lines = page.select_text_lines_matching(r"Total: \$\d+")
133
+ ```
134
+
135
+ ### Building New Content
136
+
137
+ ```python
138
+ # Add paragraph to document
139
+ pdf.new_paragraph()
140
+ .from_string("Hello World")
141
+ .with_font(Font("Helvetica", 12))
142
+ .with_color(Color(255, 0, 0))
143
+ .at_page_coordinates(0, 100, 200)
144
+ .add()
145
+
146
+ # Add paragraph to specific page
147
+ page.new_paragraph()
148
+ .from_string("Page-specific text")
149
+ .with_font(Font("Arial", 14))
150
+ .at_coordinates(50, 50)
151
+ .add()
152
+
153
+ # Add image
154
+ pdf.new_image()
155
+ .from_file("logo.png")
156
+ .with_width(100)
157
+ .at_page_coordinates(0, 50, 50)
158
+ .add()
159
+ ```
160
+
161
+ ### Document Operations
162
+
163
+ ```python
164
+ # Get PDF bytes
165
+ pdf_bytes = pdf.get_bytes()
166
+
167
+ # Save to file
168
+ pdf.save("output.pdf")
169
+
170
+ # Page operations
171
+ pages = pdf.pages()
172
+ page = pdf.page(0)
173
+ page.delete()
174
+ ```
175
+
176
+ ## Development Notes
177
+
178
+ - **Python 3.10+ compatibility** (Python 3.9 has SSL issues with large file uploads)
179
+ - **Uses `requests` library** for all HTTP communication
180
+ - **No code generation** - pure manual implementation
181
+ - **Virtual environment auto-setup** via parent Makefile
182
+ - **No code formatter configured** - follow existing style
183
+ - **Comprehensive tests** - 123 tests covering all functionality
184
+ - **E2E test utilities** - `tests/e2e/pdf_assertions.py` provides PDFAssertions class
185
+
186
+ ## Important Instructions
187
+
188
+ ### API Design
189
+
190
+ - **Use object-oriented patterns**: Selected objects should have methods (`.delete()`, `.move()`, etc.)
191
+ - **Provide page-level operations**: `pdf.page(index)` for page-scoped selections
192
+ - **Support fluent builders**: `new_paragraph()` and `new_image()` return builder instances
193
+ - **Use snake_case for methods**: `select_paragraphs()`, `select_images_at()`, etc.
194
+
195
+ ### Validation and Exceptions
196
+
197
+ - **Maintain strict validation**: Don't be more lenient than Java client
198
+ - **Preserve exception hierarchy**: ValidationException, FontNotFoundException, HttpClientException, etc.
199
+ - **Validate early**: Check parameters in Python before making API calls
200
+
201
+ ### Testing
202
+
203
+ - **Follow e2e test patterns**: Use PDFAssertions for comprehensive validation
204
+ - **Test both document and page operations**: Cover both `pdf.select_*()` and `page.select_*()` patterns
205
+ - **Test builder patterns**: Verify fluent interfaces work correctly
206
+ - **Always run tests after changes**: `venv/bin/python -m pytest tests/ -v`
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pdfdancer-client-python
3
- Version: 0.2.11
3
+ Version: 0.2.13
4
4
  Summary: Python client for PDFDancer API
5
5
  Author-email: "The Famous Cat Ltd." <hi@thefamouscat.com>
6
6
  License: MIT
@@ -9,10 +9,11 @@ Project-URL: Repository, https://github.com/MenschMachine/pdfdancer-client-pytho
9
9
  Classifier: Development Status :: 4 - Beta
10
10
  Classifier: Intended Audience :: Developers
11
11
  Classifier: License :: OSI Approved :: MIT License
12
- Classifier: Programming Language :: Python :: 3.9
13
12
  Classifier: Programming Language :: Python :: 3.10
14
13
  Classifier: Programming Language :: Python :: 3.11
15
14
  Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Requires-Python: >=3.10
16
17
  Description-Content-Type: text/markdown
17
18
  Requires-Dist: requests>=2.25.0
18
19
  Requires-Dist: pydantic>=1.8.0
@@ -26,11 +27,16 @@ Requires-Dist: mypy>=1.0; extra == "dev"
26
27
 
27
28
  # PDFDancer Python Client
28
29
 
29
- Automate PDF clean-up, redaction, form filling, and content injection against the PDFDancer API from Python. The client gives you page-scoped selectors, fluent editors, and builders so you can read, modify, and export PDFs programmatically in just a few lines.
30
+ Automate PDF clean-up, redaction, form filling, and content injection against the PDFDancer API from Python. The client
31
+ gives you page-scoped selectors, fluent editors, and builders so you can read, modify, and export PDFs programmatically
32
+ in just a few lines.
33
+
34
+ Latest schema version available at https://bucket.pdfdancer.com/api-doc/development-0.0.yml.
30
35
 
31
36
  ## Highlights
32
37
 
33
- - Locate anything inside a PDF—paragraphs, text lines, images, vector paths, pages, AcroForm fields—by page, coordinates, or text prefixes
38
+ - Locate anything inside a PDF—paragraphs, text lines, images, vector paths, pages, AcroForm fields—by page,
39
+ coordinates, or text prefixes
34
40
  - Edit or delete existing content with fluent paragraph/text editors and safe apply-on-exit context managers
35
41
  - Fill or update form fields and propagate the changes back to the document instantly
36
42
  - Add brand-new content with paragraph/image builders, custom fonts, and precise page positioning
@@ -47,7 +53,7 @@ Automate PDF clean-up, redaction, form filling, and content injection against th
47
53
 
48
54
  ## Requirements
49
55
 
50
- - Python 3.9 or newer
56
+ - Python 3.10 or newer
51
57
  - A PDFDancer API token (set `PDFDANCER_TOKEN` or pass `token=...`)
52
58
  - Network access to a PDFDancer service (defaults to `https://api.pdfdancer.com`; override with `PDFDANCER_BASE_URL`)
53
59
 
@@ -67,21 +73,21 @@ from pathlib import Path
67
73
  from pdfdancer import Color, PDFDancer
68
74
 
69
75
  with PDFDancer.open(
70
- pdf_data=Path("input.pdf"),
71
- token="your-api-token", # optional when PDFDANCER_TOKEN is set
72
- base_url="https://api.pdfdancer.com",
76
+ pdf_data=Path("input.pdf"),
77
+ token="your-api-token", # optional when PDFDANCER_TOKEN is set
78
+ base_url="https://api.pdfdancer.com",
73
79
  ) as pdf:
74
80
  # Locate existing content
75
81
  heading = pdf.page(0).select_paragraphs_starting_with("Executive Summary")[0]
76
82
  heading.edit().replace("Overview").apply()
77
83
 
78
84
  # Add a new paragraph using the fluent builder
79
- pdf.new_paragraph() \
80
- .text("Generated with PDFDancer") \
81
- .font("Helvetica", 12) \
82
- .color(Color(70, 70, 70)) \
83
- .line_spacing(1.4) \
84
- .at(page_index=0, x=72, y=520) \
85
+ pdf.new_paragraph()
86
+ .text("Generated with PDFDancer")
87
+ .font("Helvetica", 12)
88
+ .color(Color(70, 70, 70))
89
+ .line_spacing(1.4)
90
+ .at(page_index=0, x=72, y=520)
85
91
  .add()
86
92
 
87
93
  # Persist the modified document
@@ -107,7 +113,8 @@ with PDFDancer.open("report.pdf") as pdf: # environment variables provide token
107
113
  print(page.internal_id, page.position.bounding_rect)
108
114
  ```
109
115
 
110
- Selectors return rich objects (`ParagraphObject`, `TextLineObject`, `ImageObject`, `FormFieldObject`, etc.) with helpers such as `delete()`, `move_to(x, y)`, or `edit()` depending on the object type.
116
+ Selectors return rich objects (`ParagraphObject`, `TextLineObject`, `ImageObject`, `FormFieldObject`, etc.) with helpers
117
+ such as `delete()`, `move_to(x, y)`, or `edit()` depending on the object type.
111
118
 
112
119
  ## Editing Text and Forms
113
120
 
@@ -116,11 +123,11 @@ with PDFDancer.open("report.pdf") as pdf:
116
123
  paragraph = pdf.page(0).select_paragraphs_starting_with("Disclaimer")[0]
117
124
 
118
125
  # Chain updates explicitly…
119
- paragraph.edit() \
120
- .replace("Updated disclaimer text") \
121
- .font("Roboto-Regular", 11) \
122
- .line_spacing(1.1) \
123
- .move_to(72, 140) \
126
+ paragraph.edit()
127
+ .replace("Updated disclaimer text")
128
+ .font("Roboto-Regular", 11)
129
+ .line_spacing(1.1)
130
+ .move_to(72, 140)
124
131
  .apply()
125
132
 
126
133
  # …or use the context manager to auto-apply on success
@@ -141,16 +148,16 @@ with PDFDancer.open("report.pdf") as pdf:
141
148
  pdf.register_font("/path/to/custom.ttf")
142
149
 
143
150
  # Paragraphs
144
- pdf.new_paragraph() \
145
- .text("Greetings from PDFDancer!") \
146
- .font(fonts[0].name, fonts[0].size) \
147
- .at(page_index=0, x=220, y=480) \
151
+ pdf.new_paragraph()
152
+ .text("Greetings from PDFDancer!")
153
+ .font(fonts[0].name, fonts[0].size)
154
+ .at(page_index=0, x=220, y=480)
148
155
  .add()
149
156
 
150
157
  # Raster images
151
- pdf.new_image() \
152
- .from_file(Path("logo.png")) \
153
- .at(page=0, x=48, y=700) \
158
+ pdf.new_image()
159
+ .from_file(Path("logo.png"))
160
+ .at(page=0, x=48, y=700)
154
161
  .add()
155
162
  ```
156
163
 
@@ -1,10 +1,15 @@
1
1
  # PDFDancer Python Client
2
2
 
3
- Automate PDF clean-up, redaction, form filling, and content injection against the PDFDancer API from Python. The client gives you page-scoped selectors, fluent editors, and builders so you can read, modify, and export PDFs programmatically in just a few lines.
3
+ Automate PDF clean-up, redaction, form filling, and content injection against the PDFDancer API from Python. The client
4
+ gives you page-scoped selectors, fluent editors, and builders so you can read, modify, and export PDFs programmatically
5
+ in just a few lines.
6
+
7
+ Latest schema version available at https://bucket.pdfdancer.com/api-doc/development-0.0.yml.
4
8
 
5
9
  ## Highlights
6
10
 
7
- - Locate anything inside a PDF—paragraphs, text lines, images, vector paths, pages, AcroForm fields—by page, coordinates, or text prefixes
11
+ - Locate anything inside a PDF—paragraphs, text lines, images, vector paths, pages, AcroForm fields—by page,
12
+ coordinates, or text prefixes
8
13
  - Edit or delete existing content with fluent paragraph/text editors and safe apply-on-exit context managers
9
14
  - Fill or update form fields and propagate the changes back to the document instantly
10
15
  - Add brand-new content with paragraph/image builders, custom fonts, and precise page positioning
@@ -21,7 +26,7 @@ Automate PDF clean-up, redaction, form filling, and content injection against th
21
26
 
22
27
  ## Requirements
23
28
 
24
- - Python 3.9 or newer
29
+ - Python 3.10 or newer
25
30
  - A PDFDancer API token (set `PDFDANCER_TOKEN` or pass `token=...`)
26
31
  - Network access to a PDFDancer service (defaults to `https://api.pdfdancer.com`; override with `PDFDANCER_BASE_URL`)
27
32
 
@@ -41,21 +46,21 @@ from pathlib import Path
41
46
  from pdfdancer import Color, PDFDancer
42
47
 
43
48
  with PDFDancer.open(
44
- pdf_data=Path("input.pdf"),
45
- token="your-api-token", # optional when PDFDANCER_TOKEN is set
46
- base_url="https://api.pdfdancer.com",
49
+ pdf_data=Path("input.pdf"),
50
+ token="your-api-token", # optional when PDFDANCER_TOKEN is set
51
+ base_url="https://api.pdfdancer.com",
47
52
  ) as pdf:
48
53
  # Locate existing content
49
54
  heading = pdf.page(0).select_paragraphs_starting_with("Executive Summary")[0]
50
55
  heading.edit().replace("Overview").apply()
51
56
 
52
57
  # Add a new paragraph using the fluent builder
53
- pdf.new_paragraph() \
54
- .text("Generated with PDFDancer") \
55
- .font("Helvetica", 12) \
56
- .color(Color(70, 70, 70)) \
57
- .line_spacing(1.4) \
58
- .at(page_index=0, x=72, y=520) \
58
+ pdf.new_paragraph()
59
+ .text("Generated with PDFDancer")
60
+ .font("Helvetica", 12)
61
+ .color(Color(70, 70, 70))
62
+ .line_spacing(1.4)
63
+ .at(page_index=0, x=72, y=520)
59
64
  .add()
60
65
 
61
66
  # Persist the modified document
@@ -81,7 +86,8 @@ with PDFDancer.open("report.pdf") as pdf: # environment variables provide token
81
86
  print(page.internal_id, page.position.bounding_rect)
82
87
  ```
83
88
 
84
- Selectors return rich objects (`ParagraphObject`, `TextLineObject`, `ImageObject`, `FormFieldObject`, etc.) with helpers such as `delete()`, `move_to(x, y)`, or `edit()` depending on the object type.
89
+ Selectors return rich objects (`ParagraphObject`, `TextLineObject`, `ImageObject`, `FormFieldObject`, etc.) with helpers
90
+ such as `delete()`, `move_to(x, y)`, or `edit()` depending on the object type.
85
91
 
86
92
  ## Editing Text and Forms
87
93
 
@@ -90,11 +96,11 @@ with PDFDancer.open("report.pdf") as pdf:
90
96
  paragraph = pdf.page(0).select_paragraphs_starting_with("Disclaimer")[0]
91
97
 
92
98
  # Chain updates explicitly…
93
- paragraph.edit() \
94
- .replace("Updated disclaimer text") \
95
- .font("Roboto-Regular", 11) \
96
- .line_spacing(1.1) \
97
- .move_to(72, 140) \
99
+ paragraph.edit()
100
+ .replace("Updated disclaimer text")
101
+ .font("Roboto-Regular", 11)
102
+ .line_spacing(1.1)
103
+ .move_to(72, 140)
98
104
  .apply()
99
105
 
100
106
  # …or use the context manager to auto-apply on success
@@ -115,16 +121,16 @@ with PDFDancer.open("report.pdf") as pdf:
115
121
  pdf.register_font("/path/to/custom.ttf")
116
122
 
117
123
  # Paragraphs
118
- pdf.new_paragraph() \
119
- .text("Greetings from PDFDancer!") \
120
- .font(fonts[0].name, fonts[0].size) \
121
- .at(page_index=0, x=220, y=480) \
124
+ pdf.new_paragraph()
125
+ .text("Greetings from PDFDancer!")
126
+ .font(fonts[0].name, fonts[0].size)
127
+ .at(page_index=0, x=220, y=480)
122
128
  .add()
123
129
 
124
130
  # Raster images
125
- pdf.new_image() \
126
- .from_file(Path("logo.png")) \
127
- .at(page=0, x=48, y=700) \
131
+ pdf.new_image()
132
+ .from_file(Path("logo.png"))
133
+ .at(page=0, x=48, y=700)
128
134
  .add()
129
135
  ```
130
136
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pdfdancer-client-python"
7
- version = "0.2.11"
7
+ version = "0.2.13"
8
8
  description = "Python client for PDFDancer API"
9
9
  readme = "README.md"
10
10
  authors = [
@@ -15,11 +15,12 @@ classifiers = [
15
15
  "Development Status :: 4 - Beta",
16
16
  "Intended Audience :: Developers",
17
17
  "License :: OSI Approved :: MIT License",
18
- "Programming Language :: Python :: 3.9",
19
18
  "Programming Language :: Python :: 3.10",
20
19
  "Programming Language :: Python :: 3.11",
21
20
  "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
22
22
  ]
23
+ requires-python = ">=3.10"
23
24
  dependencies = [
24
25
  "requests>=2.25.0",
25
26
  "pydantic>=1.8.0",
@@ -44,8 +45,8 @@ where = ["src"]
44
45
 
45
46
  [tool.black]
46
47
  line-length = 88
47
- target-version = ['py39']
48
+ target-version = ['py310']
48
49
 
49
50
  [tool.mypy]
50
- python_version = "3.9"
51
+ python_version = "3.10"
51
52
  strict = true
@@ -12,7 +12,7 @@ from .exceptions import (
12
12
  )
13
13
  from .models import (
14
14
  ObjectRef, Position, ObjectType, Font, Color, Image, BoundingRect, Paragraph, FormFieldRef, TextObjectRef,
15
- PositionMode, ShapeType, Point, StandardFonts
15
+ PageRef, PositionMode, ShapeType, Point, StandardFonts, PageSize, Orientation
16
16
  )
17
17
  from .paragraph_builder import ParagraphBuilder
18
18
 
@@ -30,10 +30,13 @@ __all__ = [
30
30
  "Paragraph",
31
31
  "FormFieldRef",
32
32
  "TextObjectRef",
33
+ "PageRef",
33
34
  "PositionMode",
34
35
  "ShapeType",
35
36
  "Point",
36
37
  "StandardFonts",
38
+ "PageSize",
39
+ "Orientation",
37
40
  "PdfDancerException",
38
41
  "FontNotFoundException",
39
42
  "ValidationException",