pdfdancer-client-python 0.3.11__tar.gz → 0.3.12__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.12/.api-url +1 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/.github/workflows/ci.yml +31 -4
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/.github/workflows/daily-tests.yml +2 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/.github/workflows/release.yml +6 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/.gitignore +2 -0
- pdfdancer_client_python-0.3.12/.gitmodules +3 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/PKG-INFO +2 -2
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/README.md +1 -1
- pdfdancer_client_python-0.3.12/docs/capabilities/.gitkeep +0 -0
- pdfdancer_client_python-0.3.12/docs/capabilities/CLEAR_CLIPPING.md +61 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/pyproject.toml +1 -1
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/src/pdfdancer/__init__.py +1 -1
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/src/pdfdancer/pdfdancer_v1.py +93 -20
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/src/pdfdancer/types.py +11 -6
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/src/pdfdancer_client_python.egg-info/PKG-INFO +2 -2
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/src/pdfdancer_client_python.egg-info/SOURCES.txt +8 -3
- pdfdancer_client_python-0.3.12/tests/e2e/pdf_assertions.py +1783 -0
- pdfdancer_client_python-0.3.12/tests/e2e/test_clipping.py +323 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/e2e/test_paragraph.py +15 -3
- pdfdancer_client_python-0.3.12/tests/fixtures/Asimovian-Regular.ttf +0 -0
- pdfdancer_client_python-0.3.12/tests/fixtures/Roboto-Regular.ttf +0 -0
- pdfdancer_client_python-0.3.12/tests/fixtures/invisible-content-clipping-test.pdf +0 -0
- pdfdancer_client_python-0.3.11/docs/api-schemas/v0.yml +0 -5300
- pdfdancer_client_python-0.3.11/docs/api-schemas/v1.yml +0 -5387
- pdfdancer_client_python-0.3.11/tests/e2e/pdf_assertions.py +0 -842
- pdfdancer_client_python-0.3.11/update-api-spec.sh +0 -5
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/.claude/commands/discuss.md +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/.claude/commands/implement-new-api-features.md +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/.flake8 +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/CLAUDE.md +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/LICENSE +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/NOTICE +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/TODO.md +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/check.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/media/logo-orange-512h.webp +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/media/logo-orange-60h.webp +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/media/logo-silver-512h.webp +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/media/logo-silver-60h.webp +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/release.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/setup.cfg +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/src/pdfdancer/exceptions.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/src/pdfdancer/fingerprint.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/src/pdfdancer/image_builder.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/src/pdfdancer/models.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/src/pdfdancer/page_builder.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/src/pdfdancer/paragraph_builder.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/src/pdfdancer/path_builder.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/src/pdfdancer/text_line_builder.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/src/pdfdancer_client_python.egg-info/dependency_links.txt +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/src/pdfdancer_client_python.egg-info/requires.txt +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/src/pdfdancer_client_python.egg-info/top_level.txt +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/test.sh +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/__init__.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/conftest.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/e2e/__init__.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/e2e/test_acroform.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/e2e/test_bezier_builder.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/e2e/test_context_manager.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/e2e/test_form_x_objects.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/e2e/test_image.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/e2e/test_image_transform.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/e2e/test_line.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/e2e/test_line_builder.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/e2e/test_new_pdf.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/e2e/test_page.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/e2e/test_path.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/e2e/test_path_builder.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/e2e/test_path_builder_rectangle.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/e2e/test_path_comprehensive.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/e2e/test_path_group.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/e2e/test_pdfdancer.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/e2e/test_positioning.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/e2e/test_rectangle_builder.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/e2e/test_redact.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/e2e/test_singular_selection.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/e2e/test_snapshot.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/e2e/test_template_replace.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/e2e/test_template_replace_linebreak.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/e2e/test_text_line_edit.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/fixtures/DancingScript-Regular.ttf +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/fixtures/Empty.pdf +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/fixtures/JetBrainsMono-Regular.ttf +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/fixtures/Showcase.pdf +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/fixtures/basic-paths.pdf +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/fixtures/form-xobject-example.pdf +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/fixtures/logo-80.png +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/fixtures/mixed-form-types.pdf +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/test_anonymous_token.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/test_fingerprint.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/test_models.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/test_openapi_compliance.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/test_path_models.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/test_pdf_object_equality.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/test_rate_limit.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/tests/test_standard_fonts.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
http://46.225.120.69:8080
|
|
@@ -2,7 +2,7 @@ name: CI
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
push:
|
|
5
|
-
branches: [ '
|
|
5
|
+
branches: [ 'main' ]
|
|
6
6
|
pull_request:
|
|
7
7
|
branches: [ '**' ]
|
|
8
8
|
workflow_dispatch:
|
|
@@ -20,6 +20,19 @@ jobs:
|
|
|
20
20
|
|
|
21
21
|
steps:
|
|
22
22
|
- uses: actions/checkout@v4
|
|
23
|
+
with:
|
|
24
|
+
submodules: true
|
|
25
|
+
|
|
26
|
+
- name: Detect API URL override
|
|
27
|
+
if: github.event_name == 'pull_request'
|
|
28
|
+
id: api-url
|
|
29
|
+
run: |
|
|
30
|
+
if [ -f .api-url ]; then
|
|
31
|
+
url=$(cat .api-url | tr -d '[:space:]')
|
|
32
|
+
if [ -n "$url" ]; then
|
|
33
|
+
echo "base_url=$url" >> "$GITHUB_OUTPUT"
|
|
34
|
+
fi
|
|
35
|
+
fi
|
|
23
36
|
|
|
24
37
|
- name: Set up Python ${{ matrix.python-version }}
|
|
25
38
|
uses: actions/setup-python@v5
|
|
@@ -40,7 +53,7 @@ jobs:
|
|
|
40
53
|
|
|
41
54
|
- name: Run tests
|
|
42
55
|
run: |
|
|
43
|
-
PDFDANCER_BASE_URL
|
|
56
|
+
PDFDANCER_BASE_URL=${{ steps.api-url.outputs.base_url || 'https://api-staging.pdfdancer.com' }} \
|
|
44
57
|
PDFDANCER_TOKEN=42 \
|
|
45
58
|
venv/bin/python -m pytest tests/ -v --maxfail=3
|
|
46
59
|
|
|
@@ -64,6 +77,20 @@ jobs:
|
|
|
64
77
|
|
|
65
78
|
steps:
|
|
66
79
|
- uses: actions/checkout@v4
|
|
80
|
+
with:
|
|
81
|
+
submodules: true
|
|
82
|
+
|
|
83
|
+
- name: Detect API URL override
|
|
84
|
+
if: github.event_name == 'pull_request'
|
|
85
|
+
id: api-url
|
|
86
|
+
shell: bash
|
|
87
|
+
run: |
|
|
88
|
+
if [ -f .api-url ]; then
|
|
89
|
+
url=$(cat .api-url | tr -d '[:space:]')
|
|
90
|
+
if [ -n "$url" ]; then
|
|
91
|
+
echo "base_url=$url" >> "$GITHUB_OUTPUT"
|
|
92
|
+
fi
|
|
93
|
+
fi
|
|
67
94
|
|
|
68
95
|
- name: Set up Python ${{ matrix.python-version }}
|
|
69
96
|
uses: actions/setup-python@v5
|
|
@@ -88,7 +115,7 @@ jobs:
|
|
|
88
115
|
- name: Run tests (Unix)
|
|
89
116
|
if: runner.os != 'Windows'
|
|
90
117
|
run: |
|
|
91
|
-
PDFDANCER_BASE_URL
|
|
118
|
+
PDFDANCER_BASE_URL=${{ steps.api-url.outputs.base_url || 'https://api-staging.pdfdancer.com' }} \
|
|
92
119
|
PDFDANCER_TOKEN=42 \
|
|
93
120
|
venv/bin/python -m pytest tests/ -v --maxfail=3
|
|
94
121
|
|
|
@@ -119,7 +146,7 @@ jobs:
|
|
|
119
146
|
if: runner.os == 'Windows'
|
|
120
147
|
shell: cmd
|
|
121
148
|
run: |
|
|
122
|
-
set PDFDANCER_BASE_URL
|
|
149
|
+
set PDFDANCER_BASE_URL=${{ steps.api-url.outputs.base_url || 'https://api-staging.pdfdancer.com' }}
|
|
123
150
|
set PDFDANCER_TOKEN=42
|
|
124
151
|
venv\Scripts\python -m pytest tests/ -v --maxfail=3
|
|
125
152
|
|
{pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/.github/workflows/release.yml
RENAMED
|
@@ -9,6 +9,8 @@ jobs:
|
|
|
9
9
|
runs-on: ubuntu-latest
|
|
10
10
|
steps:
|
|
11
11
|
- uses: actions/checkout@v4
|
|
12
|
+
with:
|
|
13
|
+
submodules: true
|
|
12
14
|
|
|
13
15
|
- name: Set up Python 3.12
|
|
14
16
|
uses: actions/setup-python@v5
|
|
@@ -39,6 +41,8 @@ jobs:
|
|
|
39
41
|
id-token: write
|
|
40
42
|
steps:
|
|
41
43
|
- uses: actions/checkout@v4
|
|
44
|
+
with:
|
|
45
|
+
submodules: true
|
|
42
46
|
|
|
43
47
|
- name: Set up Python 3.12
|
|
44
48
|
uses: actions/setup-python@v5
|
|
@@ -71,6 +75,8 @@ jobs:
|
|
|
71
75
|
contents: write
|
|
72
76
|
steps:
|
|
73
77
|
- uses: actions/checkout@v4
|
|
78
|
+
with:
|
|
79
|
+
submodules: true
|
|
74
80
|
|
|
75
81
|
- name: Create GitHub Release
|
|
76
82
|
run: gh release create "$GITHUB_REF_NAME" --generate-notes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pdfdancer-client-python
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.12
|
|
4
4
|
Summary: Python client for PDFDancer API
|
|
5
5
|
Author-email: "The Famous Cat Ltd." <hi@thefamouscat.com>
|
|
6
6
|
License:
|
|
@@ -359,7 +359,7 @@ with PDFDancer.open("contract.pdf") as pdf:
|
|
|
359
359
|
```
|
|
360
360
|
|
|
361
361
|
Selectors return typed objects (`ParagraphObject`, `TextLineObject`, `ImageObject`, `FormFieldObject`, `PageClient`, …)
|
|
362
|
-
with helpers such as `delete()`, `move_to(x, y)`, `redact()`, or `edit()` depending on the object type.
|
|
362
|
+
with helpers such as `delete()`, `move_to(x, y)`, `clear_clipping()`, `redact()`, or `edit()` depending on the object type.
|
|
363
363
|
|
|
364
364
|
**Singular selection methods** return the first match (or `None`) for convenience:
|
|
365
365
|
|
|
@@ -120,7 +120,7 @@ with PDFDancer.open("contract.pdf") as pdf:
|
|
|
120
120
|
```
|
|
121
121
|
|
|
122
122
|
Selectors return typed objects (`ParagraphObject`, `TextLineObject`, `ImageObject`, `FormFieldObject`, `PageClient`, …)
|
|
123
|
-
with helpers such as `delete()`, `move_to(x, y)`, `redact()`, or `edit()` depending on the object type.
|
|
123
|
+
with helpers such as `delete()`, `move_to(x, y)`, `clear_clipping()`, `redact()`, or `edit()` depending on the object type.
|
|
124
124
|
|
|
125
125
|
**Singular selection methods** return the first match (or `None`) for convenience:
|
|
126
126
|
|
|
File without changes
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Clear Clipping Capability
|
|
2
|
+
|
|
3
|
+
Most of the PDF model classes including container classes like PDFParagraph/PDFTextLine/PDFPathGroup now implement ClippingDetachable with the method clearClipping().
|
|
4
|
+
This removes any clipping path which was active for this element.
|
|
5
|
+
|
|
6
|
+
This is useful in case clients want to move an element but the new position is hidden by the clipping path. clearing it makes the element visible again.
|
|
7
|
+
|
|
8
|
+
The new backend is version 1.8.6-rc2, available in the local m2 repository
|
|
9
|
+
|
|
10
|
+
## Test PDFs to use
|
|
11
|
+
|
|
12
|
+
examples/clipping/invisible-content-clipping-test.pdf
|
|
13
|
+
|
|
14
|
+
- A PDF where content is present but not visible due to clipping paths that exclude the content areas. Contains an image clipped away by one clipping path and vector paths clipped away by another clipping path.
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## Api Docs
|
|
18
|
+
|
|
19
|
+
Explain for all elements and under the clipping-section.
|
|
20
|
+
|
|
21
|
+
## Website and Marketing
|
|
22
|
+
|
|
23
|
+
Not to mention there.
|
|
24
|
+
|
|
25
|
+
## What's new Newsletter
|
|
26
|
+
|
|
27
|
+
include
|
|
28
|
+
|
|
29
|
+
## Changelog
|
|
30
|
+
|
|
31
|
+
include
|
|
32
|
+
|
|
33
|
+
## Implementation in pdfdancer-api
|
|
34
|
+
|
|
35
|
+
- Updated backend dependency to `com.tfc.pdf:pdfdancer-backend:1.8.6-rc2` in `build.gradle.kts` to use the clipping-detach support.
|
|
36
|
+
- Added client-facing helpers:
|
|
37
|
+
- `BaseReference.clearClipping()` for any object reference implementing clipping detach semantics via the API.
|
|
38
|
+
- `PathGroupReference.clearClipping()` for grouped vector paths.
|
|
39
|
+
- `PDFDancer.clearClipping(ObjectRef)` calling `PUT /pdf/clipping/clear`.
|
|
40
|
+
- `PDFDancer.clearPathGroupClipping(pageIndex, groupId)` calling `PUT /pdf/path-group/clipping/clear`.
|
|
41
|
+
- Both client calls invalidate local snapshot caches after mutation.
|
|
42
|
+
- Added server endpoints in both controllers:
|
|
43
|
+
- `PDFController` and `PDFControllerV1` expose `PUT /pdf/clipping/clear` and `PUT /pdf/path-group/clipping/clear`.
|
|
44
|
+
- V1 uses `ClearClippingRequestV1` and `ClearPathGroupClippingRequestV1`, converting both to internal requests via `toInternal()`.
|
|
45
|
+
- Added controller orchestration in `ControllerOps`:
|
|
46
|
+
- `clearClipping(...)` validates `objectRef`, executes `ClearObjectClippingCommand`, and publishes `PDF_OBJECT_MODIFIED` metric with operation `clear_clipping`.
|
|
47
|
+
- `clearPathGroupClipping(...)` validates `groupId` and `pageIndex`, executes `ClearPathGroupClippingCommand`, and publishes `VECTOR_MANIPULATION` metric with operation `clear_path_group_clipping`.
|
|
48
|
+
- Wired session and replay support:
|
|
49
|
+
- `Session.clearClipping(...)` and `Session.clearPathGroupClipping(...)` execute commands inside `SessionContext` and record commands for session history.
|
|
50
|
+
- `CommandDeserializer` now reconstructs `ClearObjectClippingCommand` and `ClearPathGroupClippingCommand` for debug archive replay.
|
|
51
|
+
- Added tests and assertions:
|
|
52
|
+
- `ClippingTest` verifies clearing clipping on `PathReference`, `PathGroupReference`, and `TextLineReference`.
|
|
53
|
+
- `DirectPDFAssertions`/`PDFAssertions` gained helpers to detect clipped paths and assert clipping present/removed.
|
|
54
|
+
|
|
55
|
+
## Implementation in pdfdancer-client-python
|
|
56
|
+
|
|
57
|
+
- Added top-level client methods in `src/pdfdancer/pdfdancer_v1.py`: `PDFDancer.clear_clipping(object_ref)` calls `PUT /pdf/clipping/clear`, and `PDFDancer.clear_path_group_clipping(page_number, group_id)` calls `PUT /pdf/path-group/clipping/clear`.
|
|
58
|
+
- The Python client validates required inputs before sending the request: `object_ref` must be present, `page_number` is a 1-based integer for path groups, and `group_id` must be non-empty. Both methods coerce the API response to `bool` and invalidate cached snapshots after a successful mutation.
|
|
59
|
+
- Added object-level convenience methods in `src/pdfdancer/types.py`. `PDFObjectBase.clear_clipping()` makes the capability available on typed selections that expose `object_ref()` such as paths, images, and text lines, while `PathGroupObject.clear_clipping()` forwards to the path-group API using `self._page_index + 1` to convert the internal 0-based index to the public 1-based page number.
|
|
60
|
+
- Updated `README.md` to list `clear_clipping()` alongside other typed-object helper methods so the feature is discoverable from the main usage guide.
|
|
61
|
+
- Added end-to-end coverage in `tests/e2e/test_clipping.py` for direct object calls and top-level client calls on paths, grouped paths, images, and clipped text lines, including a case where clipping spans multiple content streams. `tests/e2e/pdf_assertions.py` was extended with PDF draw-event inspection helpers that assert whether clipping is present or removed.
|
|
@@ -55,7 +55,7 @@ from .paragraph_builder import ParagraphBuilder
|
|
|
55
55
|
from .path_builder import BezierBuilder, LineBuilder, PathBuilder
|
|
56
56
|
from .text_line_builder import TextLineBuilder
|
|
57
57
|
|
|
58
|
-
__version__ = "0.3.
|
|
58
|
+
__version__ = "0.3.12"
|
|
59
59
|
__all__ = [
|
|
60
60
|
"PDFDancer",
|
|
61
61
|
"ParagraphBuilder",
|
{pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.12}/src/pdfdancer/pdfdancer_v1.py
RENAMED
|
@@ -134,7 +134,7 @@ DEFAULT_RETRY_BACKOFF_FACTOR = float(
|
|
|
134
134
|
|
|
135
135
|
|
|
136
136
|
def _dict_to_replacements(
|
|
137
|
-
replacements: Dict[str, Union[str, dict]]
|
|
137
|
+
replacements: Dict[str, Union[str, dict]],
|
|
138
138
|
) -> List[TemplateReplacement]:
|
|
139
139
|
"""Convert dict-based replacements to TemplateReplacement list."""
|
|
140
140
|
result = []
|
|
@@ -2292,6 +2292,37 @@ class PDFDancer:
|
|
|
2292
2292
|
targets = [RedactTarget(obj.internal_id, replacement) for obj in objects]
|
|
2293
2293
|
return self._redact(targets, replacement, placeholder_color)
|
|
2294
2294
|
|
|
2295
|
+
def clear_clipping(self, object_ref: ObjectRef) -> bool:
|
|
2296
|
+
"""
|
|
2297
|
+
Clear clipping on a single PDF object.
|
|
2298
|
+
|
|
2299
|
+
Args:
|
|
2300
|
+
object_ref: Reference to the object whose clipping should be removed
|
|
2301
|
+
|
|
2302
|
+
Returns:
|
|
2303
|
+
True when the server cleared clipping successfully
|
|
2304
|
+
"""
|
|
2305
|
+
return self._clear_clipping(object_ref)
|
|
2306
|
+
|
|
2307
|
+
def _clear_clipping(self, object_ref: ObjectRef) -> bool:
|
|
2308
|
+
"""
|
|
2309
|
+
Internal helper to clear clipping from a single object.
|
|
2310
|
+
"""
|
|
2311
|
+
if object_ref is None:
|
|
2312
|
+
raise ValidationException("Object reference cannot be null")
|
|
2313
|
+
|
|
2314
|
+
response = self._make_request(
|
|
2315
|
+
"PUT",
|
|
2316
|
+
"/pdf/clipping/clear",
|
|
2317
|
+
data={"objectRef": object_ref.to_dict()},
|
|
2318
|
+
)
|
|
2319
|
+
result = bool(response.json())
|
|
2320
|
+
|
|
2321
|
+
if result:
|
|
2322
|
+
self._invalidate_snapshots()
|
|
2323
|
+
|
|
2324
|
+
return result
|
|
2325
|
+
|
|
2295
2326
|
# Template Replacement Operations
|
|
2296
2327
|
|
|
2297
2328
|
def _apply_replacements(
|
|
@@ -2506,19 +2537,21 @@ class PDFDancer:
|
|
|
2506
2537
|
data["pathIds"] = path_ids
|
|
2507
2538
|
if region is not None:
|
|
2508
2539
|
data["region"] = {
|
|
2509
|
-
"x": region.x,
|
|
2510
|
-
"
|
|
2540
|
+
"x": region.x,
|
|
2541
|
+
"y": region.y,
|
|
2542
|
+
"width": region.width,
|
|
2543
|
+
"height": region.height,
|
|
2511
2544
|
}
|
|
2512
|
-
response = self._make_request(
|
|
2513
|
-
"POST", "/pdf/path-group/create", data=data
|
|
2514
|
-
)
|
|
2545
|
+
response = self._make_request("POST", "/pdf/path-group/create", data=data)
|
|
2515
2546
|
self._invalidate_snapshots()
|
|
2516
2547
|
return PathGroupInfo.from_dict(response.json())
|
|
2517
2548
|
|
|
2518
2549
|
def _move_path_group(self, page_index, group_id, x, y):
|
|
2519
2550
|
data = {
|
|
2520
|
-
"pageIndex": page_index,
|
|
2521
|
-
"
|
|
2551
|
+
"pageIndex": page_index,
|
|
2552
|
+
"groupId": group_id,
|
|
2553
|
+
"x": x,
|
|
2554
|
+
"y": y,
|
|
2522
2555
|
}
|
|
2523
2556
|
response = self._make_request("PUT", "/pdf/path-group/move", data=data)
|
|
2524
2557
|
self._invalidate_snapshots()
|
|
@@ -2526,13 +2559,12 @@ class PDFDancer:
|
|
|
2526
2559
|
|
|
2527
2560
|
def _transform_path_group(self, page_index, group_id, transform_type, **kwargs):
|
|
2528
2561
|
data = {
|
|
2529
|
-
"pageIndex": page_index,
|
|
2562
|
+
"pageIndex": page_index,
|
|
2563
|
+
"groupId": group_id,
|
|
2530
2564
|
"transformType": transform_type,
|
|
2531
2565
|
}
|
|
2532
2566
|
data.update({k: v for k, v in kwargs.items() if v is not None})
|
|
2533
|
-
response = self._make_request(
|
|
2534
|
-
"PUT", "/pdf/path-group/transform", data=data
|
|
2535
|
-
)
|
|
2567
|
+
response = self._make_request("PUT", "/pdf/path-group/transform", data=data)
|
|
2536
2568
|
self._invalidate_snapshots()
|
|
2537
2569
|
return response.json()
|
|
2538
2570
|
|
|
@@ -2552,15 +2584,16 @@ class PDFDancer:
|
|
|
2552
2584
|
if width <= 0 or height <= 0:
|
|
2553
2585
|
raise ValidationException("Width and height must be positive")
|
|
2554
2586
|
return self._transform_path_group(
|
|
2555
|
-
page_index,
|
|
2556
|
-
|
|
2587
|
+
page_index,
|
|
2588
|
+
group_id,
|
|
2589
|
+
"RESIZE",
|
|
2590
|
+
targetWidth=width,
|
|
2591
|
+
targetHeight=height,
|
|
2557
2592
|
)
|
|
2558
2593
|
|
|
2559
2594
|
def _remove_path_group(self, page_index, group_id):
|
|
2560
2595
|
data = {"pageIndex": page_index, "groupId": group_id}
|
|
2561
|
-
response = self._make_request(
|
|
2562
|
-
"DELETE", "/pdf/path-group/remove", data=data
|
|
2563
|
-
)
|
|
2596
|
+
response = self._make_request("DELETE", "/pdf/path-group/remove", data=data)
|
|
2564
2597
|
self._invalidate_snapshots()
|
|
2565
2598
|
return response.json()
|
|
2566
2599
|
|
|
@@ -2568,12 +2601,52 @@ class PDFDancer:
|
|
|
2568
2601
|
from .models import PathGroupInfo
|
|
2569
2602
|
from .types import PathGroupObject
|
|
2570
2603
|
|
|
2571
|
-
response = self._make_request(
|
|
2572
|
-
"GET", f"/pdf/page/{page_index}/path-groups"
|
|
2573
|
-
)
|
|
2604
|
+
response = self._make_request("GET", f"/pdf/page/{page_index}/path-groups")
|
|
2574
2605
|
infos = [PathGroupInfo.from_dict(d) for d in response.json()]
|
|
2575
2606
|
return [PathGroupObject(self, page_index, info) for info in infos]
|
|
2576
2607
|
|
|
2608
|
+
def clear_path_group_clipping(self, page_number: int, group_id: str) -> bool:
|
|
2609
|
+
"""
|
|
2610
|
+
Clear clipping for a grouped set of paths.
|
|
2611
|
+
|
|
2612
|
+
Args:
|
|
2613
|
+
page_number: 1-based page number containing the path group
|
|
2614
|
+
group_id: Identifier returned by `group_paths(...)`
|
|
2615
|
+
|
|
2616
|
+
Returns:
|
|
2617
|
+
True when the server cleared clipping successfully
|
|
2618
|
+
"""
|
|
2619
|
+
return self._clear_path_group_clipping(page_number, group_id)
|
|
2620
|
+
|
|
2621
|
+
def _clear_path_group_clipping(self, page_number: int, group_id: str) -> bool:
|
|
2622
|
+
"""
|
|
2623
|
+
Internal helper to clear clipping from a path group using the v1 API.
|
|
2624
|
+
"""
|
|
2625
|
+
if page_number is None:
|
|
2626
|
+
raise ValidationException("page_number cannot be null")
|
|
2627
|
+
if not isinstance(page_number, int):
|
|
2628
|
+
raise ValidationException(
|
|
2629
|
+
f"page_number must be an integer, got {type(page_number)}"
|
|
2630
|
+
)
|
|
2631
|
+
if page_number < 1:
|
|
2632
|
+
raise ValidationException(
|
|
2633
|
+
f"page_number must be >= 1 (1-based indexing), got {page_number}"
|
|
2634
|
+
)
|
|
2635
|
+
if group_id is None or not str(group_id).strip():
|
|
2636
|
+
raise ValidationException("group_id cannot be null or empty")
|
|
2637
|
+
|
|
2638
|
+
response = self._make_request(
|
|
2639
|
+
"PUT",
|
|
2640
|
+
"/pdf/path-group/clipping/clear",
|
|
2641
|
+
data={"pageNumber": page_number, "groupId": str(group_id).strip()},
|
|
2642
|
+
)
|
|
2643
|
+
result = bool(response.json())
|
|
2644
|
+
|
|
2645
|
+
if result:
|
|
2646
|
+
self._invalidate_snapshots()
|
|
2647
|
+
|
|
2648
|
+
return result
|
|
2649
|
+
|
|
2577
2650
|
def new_paragraph(self) -> ParagraphBuilder:
|
|
2578
2651
|
return ParagraphBuilder(self)
|
|
2579
2652
|
|
|
@@ -65,6 +65,10 @@ class PDFObjectBase:
|
|
|
65
65
|
Position.at_page_coordinates(self.position.page_number, x, y),
|
|
66
66
|
)
|
|
67
67
|
|
|
68
|
+
def clear_clipping(self) -> bool:
|
|
69
|
+
"""Detach any active clipping path from this object."""
|
|
70
|
+
return self._client.clear_clipping(self.object_ref())
|
|
71
|
+
|
|
68
72
|
def redact(self, replacement: str = "[REDACTED]") -> bool:
|
|
69
73
|
"""Redact this object from the PDF document."""
|
|
70
74
|
from .models import RedactTarget
|
|
@@ -329,21 +333,22 @@ class PathGroupObject:
|
|
|
329
333
|
return True
|
|
330
334
|
|
|
331
335
|
def rotate(self, degrees: float) -> bool:
|
|
332
|
-
self._client._rotate_path_group(
|
|
333
|
-
self._page_index, self.group_id, degrees
|
|
334
|
-
)
|
|
336
|
+
self._client._rotate_path_group(self._page_index, self.group_id, degrees)
|
|
335
337
|
return True
|
|
336
338
|
|
|
337
339
|
def resize(self, width: float, height: float) -> bool:
|
|
338
|
-
self._client._resize_path_group(
|
|
339
|
-
self._page_index, self.group_id, width, height
|
|
340
|
-
)
|
|
340
|
+
self._client._resize_path_group(self._page_index, self.group_id, width, height)
|
|
341
341
|
return True
|
|
342
342
|
|
|
343
343
|
def remove(self) -> bool:
|
|
344
344
|
self._client._remove_path_group(self._page_index, self.group_id)
|
|
345
345
|
return True
|
|
346
346
|
|
|
347
|
+
def clear_clipping(self) -> bool:
|
|
348
|
+
return self._client.clear_path_group_clipping(
|
|
349
|
+
self._page_index + 1, self.group_id
|
|
350
|
+
)
|
|
351
|
+
|
|
347
352
|
def __repr__(self):
|
|
348
353
|
return f"PathGroupObject(group_id={self.group_id!r}, path_count={self.path_count}, page_index={self._page_index})"
|
|
349
354
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pdfdancer-client-python
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.12
|
|
4
4
|
Summary: Python client for PDFDancer API
|
|
5
5
|
Author-email: "The Famous Cat Ltd." <hi@thefamouscat.com>
|
|
6
6
|
License:
|
|
@@ -359,7 +359,7 @@ with PDFDancer.open("contract.pdf") as pdf:
|
|
|
359
359
|
```
|
|
360
360
|
|
|
361
361
|
Selectors return typed objects (`ParagraphObject`, `TextLineObject`, `ImageObject`, `FormFieldObject`, `PageClient`, …)
|
|
362
|
-
with helpers such as `delete()`, `move_to(x, y)`, `redact()`, or `edit()` depending on the object type.
|
|
362
|
+
with helpers such as `delete()`, `move_to(x, y)`, `clear_clipping()`, `redact()`, or `edit()` depending on the object type.
|
|
363
363
|
|
|
364
364
|
**Singular selection methods** return the first match (or `None`) for convenience:
|
|
365
365
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
.api-url
|
|
1
2
|
.flake8
|
|
2
3
|
.gitignore
|
|
4
|
+
.gitmodules
|
|
3
5
|
CLAUDE.md
|
|
4
6
|
LICENSE
|
|
5
7
|
NOTICE
|
|
@@ -9,14 +11,13 @@ check.py
|
|
|
9
11
|
pyproject.toml
|
|
10
12
|
release.py
|
|
11
13
|
test.sh
|
|
12
|
-
update-api-spec.sh
|
|
13
14
|
.claude/commands/discuss.md
|
|
14
15
|
.claude/commands/implement-new-api-features.md
|
|
15
16
|
.github/workflows/ci.yml
|
|
16
17
|
.github/workflows/daily-tests.yml
|
|
17
18
|
.github/workflows/release.yml
|
|
18
|
-
docs/
|
|
19
|
-
docs/
|
|
19
|
+
docs/capabilities/.gitkeep
|
|
20
|
+
docs/capabilities/CLEAR_CLIPPING.md
|
|
20
21
|
media/logo-orange-512h.webp
|
|
21
22
|
media/logo-orange-60h.webp
|
|
22
23
|
media/logo-silver-512h.webp
|
|
@@ -51,6 +52,7 @@ tests/e2e/__init__.py
|
|
|
51
52
|
tests/e2e/pdf_assertions.py
|
|
52
53
|
tests/e2e/test_acroform.py
|
|
53
54
|
tests/e2e/test_bezier_builder.py
|
|
55
|
+
tests/e2e/test_clipping.py
|
|
54
56
|
tests/e2e/test_context_manager.py
|
|
55
57
|
tests/e2e/test_form_x_objects.py
|
|
56
58
|
tests/e2e/test_image.py
|
|
@@ -74,11 +76,14 @@ tests/e2e/test_snapshot.py
|
|
|
74
76
|
tests/e2e/test_template_replace.py
|
|
75
77
|
tests/e2e/test_template_replace_linebreak.py
|
|
76
78
|
tests/e2e/test_text_line_edit.py
|
|
79
|
+
tests/fixtures/Asimovian-Regular.ttf
|
|
77
80
|
tests/fixtures/DancingScript-Regular.ttf
|
|
78
81
|
tests/fixtures/Empty.pdf
|
|
79
82
|
tests/fixtures/JetBrainsMono-Regular.ttf
|
|
83
|
+
tests/fixtures/Roboto-Regular.ttf
|
|
80
84
|
tests/fixtures/Showcase.pdf
|
|
81
85
|
tests/fixtures/basic-paths.pdf
|
|
82
86
|
tests/fixtures/form-xobject-example.pdf
|
|
87
|
+
tests/fixtures/invisible-content-clipping-test.pdf
|
|
83
88
|
tests/fixtures/logo-80.png
|
|
84
89
|
tests/fixtures/mixed-form-types.pdf
|