pdfdancer-client-python 0.2.12__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.
- pdfdancer_client_python-0.2.13/.claude/commands/discuss.md +4 -0
- pdfdancer_client_python-0.2.13/.github/workflows/ci.yml +40 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/.gitignore +1 -0
- pdfdancer_client_python-0.2.13/CLAUDE.md +206 -0
- {pdfdancer_client_python-0.2.12/src/pdfdancer_client_python.egg-info → pdfdancer_client_python-0.2.13}/PKG-INFO +34 -27
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/README.md +31 -25
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/pyproject.toml +5 -4
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/src/pdfdancer/__init__.py +4 -1
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/src/pdfdancer/models.py +135 -2
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/src/pdfdancer/paragraph_builder.py +8 -3
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/src/pdfdancer/pdfdancer_v1.py +337 -20
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13/src/pdfdancer_client_python.egg-info}/PKG-INFO +34 -27
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/src/pdfdancer_client_python.egg-info/SOURCES.txt +3 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/tests/conftest.py +3 -1
- pdfdancer_client_python-0.2.13/tests/e2e/pdf_assertions.py +230 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/tests/e2e/test_acroform.py +19 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/tests/e2e/test_form_x_objects.py +11 -4
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/tests/e2e/test_image.py +32 -10
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/tests/e2e/test_line.py +25 -15
- pdfdancer_client_python-0.2.13/tests/e2e/test_new_pdf.py +121 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/tests/e2e/test_page.py +33 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/tests/e2e/test_path.py +19 -0
- pdfdancer_client_python-0.2.13/tests/e2e/test_positioning.py +2 -0
- pdfdancer_client_python-0.2.12/.github/workflows/ci.yml +0 -40
- pdfdancer_client_python-0.2.12/CLAUDE.md +0 -121
- pdfdancer_client_python-0.2.12/tests/e2e/pdf_assertions.py +0 -100
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/docs/openapi.yml +0 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/release.py +0 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/requirements-dev.txt +0 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/requirements.txt +0 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/setup.cfg +0 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/src/pdfdancer/exceptions.py +0 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/src/pdfdancer/image_builder.py +0 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/src/pdfdancer/types.py +0 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/src/pdfdancer_client_python.egg-info/dependency_links.txt +0 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/src/pdfdancer_client_python.egg-info/requires.txt +0 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/src/pdfdancer_client_python.egg-info/top_level.txt +0 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/tests/__init__.py +0 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/tests/e2e/__init__.py +0 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/tests/e2e/test_paragraph.py +0 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/tests/e2e/test_pdfdancer.py +0 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/tests/fixtures/DancingScript-Regular.ttf +0 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/tests/fixtures/Empty.pdf +0 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/tests/fixtures/JetBrainsMono-Regular.ttf +0 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/tests/fixtures/ObviouslyAwesome.pdf +0 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/tests/fixtures/basic-paths.pdf +0 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/tests/fixtures/form-xobject-example.pdf +0 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/tests/fixtures/logo-80.png +0 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/tests/fixtures/mixed-form-types.pdf +0 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/tests/test_models.py +0 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/tests/test_openapi_compliance.py +0 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/tests/test_pdf_object_equality.py +0 -0
- {pdfdancer_client_python-0.2.12 → pdfdancer_client_python-0.2.13}/tests/test_standard_fonts.py +0 -0
|
@@ -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/*
|
|
@@ -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.
|
|
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
|
|
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,
|
|
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.
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
|
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
|
|
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,
|
|
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.
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
|
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.
|
|
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 = ['
|
|
48
|
+
target-version = ['py310']
|
|
48
49
|
|
|
49
50
|
[tool.mypy]
|
|
50
|
-
python_version = "3.
|
|
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",
|