pdfdancer-client-python 0.3.10__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.10 → pdfdancer_client_python-0.3.12}/.github/workflows/ci.yml +31 -4
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/.github/workflows/daily-tests.yml +2 -0
- pdfdancer_client_python-0.3.12/.github/workflows/release.yml +84 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/.gitignore +2 -0
- pdfdancer_client_python-0.3.12/.gitmodules +3 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/PKG-INFO +11 -10
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/README.md +9 -8
- 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.10 → pdfdancer_client_python-0.3.12}/pyproject.toml +2 -2
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/release.py +51 -5
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/src/pdfdancer/__init__.py +7 -1
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/src/pdfdancer/exceptions.py +9 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/src/pdfdancer/models.py +19 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/src/pdfdancer/pdfdancer_v1.py +196 -4
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/src/pdfdancer/types.py +61 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/src/pdfdancer_client_python.egg-info/PKG-INFO +11 -10
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/src/pdfdancer_client_python.egg-info/SOURCES.txt +11 -3
- pdfdancer_client_python-0.3.12/tests/e2e/pdf_assertions.py +1783 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/e2e/test_acroform.py +2 -2
- pdfdancer_client_python-0.3.12/tests/e2e/test_clipping.py +323 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/e2e/test_form_x_objects.py +1 -1
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/e2e/test_image.py +3 -3
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/e2e/test_paragraph.py +24 -12
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/e2e/test_path.py +5 -5
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/e2e/test_path_comprehensive.py +1 -1
- pdfdancer_client_python-0.3.12/tests/e2e/test_path_group.py +244 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/e2e/test_redact.py +2 -2
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/e2e/test_template_replace.py +1 -1
- pdfdancer_client_python-0.3.12/tests/e2e/test_template_replace_linebreak.py +141 -0
- 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.10/docs/api-schemas/v0.yml +0 -5300
- pdfdancer_client_python-0.3.10/docs/api-schemas/v1.yml +0 -5387
- pdfdancer_client_python-0.3.10/tests/e2e/pdf_assertions.py +0 -817
- pdfdancer_client_python-0.3.10/update-api-spec.sh +0 -5
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/.claude/commands/discuss.md +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/.claude/commands/implement-new-api-features.md +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/.flake8 +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/CLAUDE.md +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/LICENSE +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/NOTICE +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/TODO.md +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/check.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/media/logo-orange-512h.webp +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/media/logo-orange-60h.webp +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/media/logo-silver-512h.webp +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/media/logo-silver-60h.webp +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/setup.cfg +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/src/pdfdancer/fingerprint.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/src/pdfdancer/image_builder.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/src/pdfdancer/page_builder.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/src/pdfdancer/paragraph_builder.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/src/pdfdancer/path_builder.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/src/pdfdancer/text_line_builder.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/src/pdfdancer_client_python.egg-info/dependency_links.txt +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/src/pdfdancer_client_python.egg-info/requires.txt +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/src/pdfdancer_client_python.egg-info/top_level.txt +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/test.sh +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/__init__.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/conftest.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/e2e/__init__.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/e2e/test_bezier_builder.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/e2e/test_context_manager.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/e2e/test_image_transform.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/e2e/test_line.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/e2e/test_line_builder.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/e2e/test_new_pdf.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/e2e/test_page.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/e2e/test_path_builder.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/e2e/test_path_builder_rectangle.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/e2e/test_pdfdancer.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/e2e/test_positioning.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/e2e/test_rectangle_builder.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/e2e/test_singular_selection.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/e2e/test_snapshot.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/e2e/test_text_line_edit.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/fixtures/DancingScript-Regular.ttf +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/fixtures/Empty.pdf +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/fixtures/JetBrainsMono-Regular.ttf +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/fixtures/Showcase.pdf +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/fixtures/basic-paths.pdf +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/fixtures/form-xobject-example.pdf +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/fixtures/logo-80.png +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/fixtures/mixed-form-types.pdf +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/test_anonymous_token.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/test_fingerprint.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/test_models.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/test_openapi_compliance.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/test_path_models.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/test_pdf_object_equality.py +0 -0
- {pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/tests/test_rate_limit.py +0 -0
- {pdfdancer_client_python-0.3.10 → 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
|
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
name: Release to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ['v*']
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v4
|
|
12
|
+
with:
|
|
13
|
+
submodules: true
|
|
14
|
+
|
|
15
|
+
- name: Set up Python 3.12
|
|
16
|
+
uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: '3.12'
|
|
19
|
+
|
|
20
|
+
- name: Create virtual environment
|
|
21
|
+
run: python -m venv venv
|
|
22
|
+
|
|
23
|
+
- name: Install dependencies
|
|
24
|
+
run: |
|
|
25
|
+
venv/bin/pip install --upgrade pip
|
|
26
|
+
venv/bin/pip install -e ".[dev]"
|
|
27
|
+
|
|
28
|
+
- name: Run linter
|
|
29
|
+
run: venv/bin/python -m flake8 src/
|
|
30
|
+
|
|
31
|
+
- name: Run tests
|
|
32
|
+
run: venv/bin/python -m pytest tests/ -v --maxfail=1 --ignore=tests/e2e/test_pdfdancer.py
|
|
33
|
+
env:
|
|
34
|
+
PDFDANCER_BASE_URL: https://api.pdfdancer.com
|
|
35
|
+
PDFDANCER_API_TOKEN: ${{ secrets.PDFDANCER_API_TOKEN_PRODUCTION }}
|
|
36
|
+
|
|
37
|
+
publish:
|
|
38
|
+
needs: test
|
|
39
|
+
runs-on: ubuntu-latest
|
|
40
|
+
permissions:
|
|
41
|
+
id-token: write
|
|
42
|
+
steps:
|
|
43
|
+
- uses: actions/checkout@v4
|
|
44
|
+
with:
|
|
45
|
+
submodules: true
|
|
46
|
+
|
|
47
|
+
- name: Set up Python 3.12
|
|
48
|
+
uses: actions/setup-python@v5
|
|
49
|
+
with:
|
|
50
|
+
python-version: '3.12'
|
|
51
|
+
|
|
52
|
+
- name: Verify tag matches package version
|
|
53
|
+
run: |
|
|
54
|
+
TAG_VERSION="${GITHUB_REF_NAME#v}"
|
|
55
|
+
PKG_VERSION=$(python -c "import re; print(re.search(r'version\s*=\s*\"([^\"]+)\"', open('pyproject.toml').read()).group(1))")
|
|
56
|
+
if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
|
|
57
|
+
echo "Tag version ($TAG_VERSION) does not match pyproject.toml version ($PKG_VERSION)"
|
|
58
|
+
exit 1
|
|
59
|
+
fi
|
|
60
|
+
echo "Version verified: $TAG_VERSION"
|
|
61
|
+
|
|
62
|
+
- name: Install build dependencies
|
|
63
|
+
run: pip install build
|
|
64
|
+
|
|
65
|
+
- name: Build package
|
|
66
|
+
run: python -m build
|
|
67
|
+
|
|
68
|
+
- name: Publish to PyPI
|
|
69
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
70
|
+
|
|
71
|
+
github-release:
|
|
72
|
+
needs: publish
|
|
73
|
+
runs-on: ubuntu-latest
|
|
74
|
+
permissions:
|
|
75
|
+
contents: write
|
|
76
|
+
steps:
|
|
77
|
+
- uses: actions/checkout@v4
|
|
78
|
+
with:
|
|
79
|
+
submodules: true
|
|
80
|
+
|
|
81
|
+
- name: Create GitHub Release
|
|
82
|
+
run: gh release create "$GITHUB_REF_NAME" --generate-notes
|
|
83
|
+
env:
|
|
84
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -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:
|
|
@@ -210,7 +210,7 @@ Project-URL: Homepage, https://www.pdfdancer.com/
|
|
|
210
210
|
Project-URL: Documentation, https://www.pdfdancer.com/
|
|
211
211
|
Project-URL: Source, https://github.com/MenschMachine/pdfdancer-client-python
|
|
212
212
|
Project-URL: Issues, https://github.com/MenschMachine/pdfdancer-client-python/issues
|
|
213
|
-
Classifier: Development Status ::
|
|
213
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
214
214
|
Classifier: Intended Audience :: Developers
|
|
215
215
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
216
216
|
Classifier: Programming Language :: Python :: 3.10
|
|
@@ -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
|
|
|
@@ -571,15 +571,16 @@ Artifacts will be created in the `dist/` directory.
|
|
|
571
571
|
|
|
572
572
|
#### Publishing to PyPI
|
|
573
573
|
|
|
574
|
-
|
|
575
|
-
# Test upload to TestPyPI (recommended first)
|
|
576
|
-
python -m twine upload --repository testpypi dist/*
|
|
574
|
+
Releases are published automatically to PyPI when a `v*` tag is pushed to GitHub (via GitHub Actions with Trusted Publishers).
|
|
577
575
|
|
|
578
|
-
|
|
579
|
-
|
|
576
|
+
```bash
|
|
577
|
+
# Set version, commit, and push tag — GitHub Actions handles the rest
|
|
578
|
+
python release.py tag --version 1.1.0
|
|
580
579
|
|
|
581
|
-
# Or
|
|
582
|
-
|
|
580
|
+
# Or manually:
|
|
581
|
+
# 1. Update version in pyproject.toml and src/pdfdancer/__init__.py
|
|
582
|
+
# 2. Commit
|
|
583
|
+
# 3. git tag v1.1.0 && git push origin HEAD && git push origin v1.1.0
|
|
583
584
|
```
|
|
584
585
|
|
|
585
586
|
#### Code Quality
|
|
@@ -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
|
|
|
@@ -332,15 +332,16 @@ Artifacts will be created in the `dist/` directory.
|
|
|
332
332
|
|
|
333
333
|
#### Publishing to PyPI
|
|
334
334
|
|
|
335
|
-
|
|
336
|
-
# Test upload to TestPyPI (recommended first)
|
|
337
|
-
python -m twine upload --repository testpypi dist/*
|
|
335
|
+
Releases are published automatically to PyPI when a `v*` tag is pushed to GitHub (via GitHub Actions with Trusted Publishers).
|
|
338
336
|
|
|
339
|
-
|
|
340
|
-
|
|
337
|
+
```bash
|
|
338
|
+
# Set version, commit, and push tag — GitHub Actions handles the rest
|
|
339
|
+
python release.py tag --version 1.1.0
|
|
341
340
|
|
|
342
|
-
# Or
|
|
343
|
-
|
|
341
|
+
# Or manually:
|
|
342
|
+
# 1. Update version in pyproject.toml and src/pdfdancer/__init__.py
|
|
343
|
+
# 2. Commit
|
|
344
|
+
# 3. git tag v1.1.0 && git push origin HEAD && git push origin v1.1.0
|
|
344
345
|
```
|
|
345
346
|
|
|
346
347
|
#### Code Quality
|
|
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.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pdfdancer-client-python"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.12"
|
|
8
8
|
description = "Python client for PDFDancer API"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [
|
|
@@ -12,7 +12,7 @@ authors = [
|
|
|
12
12
|
]
|
|
13
13
|
license = { file = "LICENSE" }
|
|
14
14
|
classifiers = [
|
|
15
|
-
"Development Status ::
|
|
15
|
+
"Development Status :: 5 - Production/Stable",
|
|
16
16
|
"Intended Audience :: Developers",
|
|
17
17
|
"License :: OSI Approved :: Apache Software License",
|
|
18
18
|
"Programming Language :: Python :: 3.10",
|
|
@@ -20,10 +20,12 @@ class ReleaseError(Exception):
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class VersionBumper:
|
|
23
|
-
"""Handles version bumping in pyproject.toml."""
|
|
23
|
+
"""Handles version bumping in pyproject.toml and __init__.py."""
|
|
24
24
|
|
|
25
|
-
def __init__(self, pyproject_path: Path = Path("pyproject.toml")
|
|
25
|
+
def __init__(self, pyproject_path: Path = Path("pyproject.toml"),
|
|
26
|
+
init_path: Path = Path("src/pdfdancer/__init__.py")):
|
|
26
27
|
self.pyproject_path = pyproject_path
|
|
28
|
+
self.init_path = init_path
|
|
27
29
|
if not self.pyproject_path.exists():
|
|
28
30
|
raise ReleaseError(f"pyproject.toml not found at {pyproject_path}")
|
|
29
31
|
|
|
@@ -36,7 +38,8 @@ class VersionBumper:
|
|
|
36
38
|
return match.group(1)
|
|
37
39
|
|
|
38
40
|
def set_version(self, new_version: str) -> None:
|
|
39
|
-
"""Set a new version in pyproject.toml."""
|
|
41
|
+
"""Set a new version in pyproject.toml and __init__.py."""
|
|
42
|
+
# Update pyproject.toml
|
|
40
43
|
content = self.pyproject_path.read_text()
|
|
41
44
|
new_content = re.sub(
|
|
42
45
|
r'^version\s*=\s*"[^"]+"',
|
|
@@ -48,6 +51,18 @@ class VersionBumper:
|
|
|
48
51
|
raise ReleaseError("Failed to update version in pyproject.toml")
|
|
49
52
|
self.pyproject_path.write_text(new_content)
|
|
50
53
|
|
|
54
|
+
# Update __init__.py
|
|
55
|
+
if self.init_path.exists():
|
|
56
|
+
init_content = self.init_path.read_text()
|
|
57
|
+
new_init = re.sub(
|
|
58
|
+
r'^__version__\s*=\s*"[^"]+"',
|
|
59
|
+
f'__version__ = "{new_version}"',
|
|
60
|
+
init_content,
|
|
61
|
+
flags=re.MULTILINE
|
|
62
|
+
)
|
|
63
|
+
self.init_path.write_text(new_init)
|
|
64
|
+
print(f"Updated {self.init_path} to {new_version}")
|
|
65
|
+
|
|
51
66
|
def bump_version(self, bump_type: str) -> str:
|
|
52
67
|
"""Bump version by type (major, minor, patch)."""
|
|
53
68
|
current = self.get_current_version()
|
|
@@ -159,8 +174,8 @@ def main():
|
|
|
159
174
|
parser = argparse.ArgumentParser(description="PDFDancer Python Client Release Tool")
|
|
160
175
|
parser.add_argument(
|
|
161
176
|
"action",
|
|
162
|
-
choices=["bump", "upload", "release"],
|
|
163
|
-
help="Action to perform: bump (version only), upload (build+upload), release (bump+test+build+upload)"
|
|
177
|
+
choices=["bump", "upload", "release", "tag"],
|
|
178
|
+
help="Action to perform: bump (version only), upload (build+upload), release (bump+test+build+upload), tag (set version + commit + push tag)"
|
|
164
179
|
)
|
|
165
180
|
parser.add_argument(
|
|
166
181
|
"--bump-type",
|
|
@@ -230,6 +245,37 @@ def main():
|
|
|
230
245
|
|
|
231
246
|
print(f"New version: {new_version}")
|
|
232
247
|
|
|
248
|
+
if args.action == "tag":
|
|
249
|
+
if not args.version:
|
|
250
|
+
raise ReleaseError("--version is required for the tag action")
|
|
251
|
+
|
|
252
|
+
new_version = args.version
|
|
253
|
+
current_version = version_bumper.get_current_version()
|
|
254
|
+
print(f"Current version: {current_version}")
|
|
255
|
+
|
|
256
|
+
if not args.dry_run:
|
|
257
|
+
version_bumper.set_version(new_version)
|
|
258
|
+
print(f"Version set to {new_version}")
|
|
259
|
+
|
|
260
|
+
# Commit the version bump
|
|
261
|
+
uploader.run_command(
|
|
262
|
+
["git", "add", "pyproject.toml", "src/pdfdancer/__init__.py"]
|
|
263
|
+
)
|
|
264
|
+
uploader.run_command(
|
|
265
|
+
["git", "commit", "-m", f"release: v{new_version}"]
|
|
266
|
+
)
|
|
267
|
+
print(f"Committed version bump")
|
|
268
|
+
|
|
269
|
+
# Create and push tag
|
|
270
|
+
tag_name = f"v{new_version}"
|
|
271
|
+
uploader.run_command(["git", "tag", tag_name])
|
|
272
|
+
uploader.run_command(["git", "push", "origin", "HEAD"])
|
|
273
|
+
uploader.run_command(["git", "push", "origin", tag_name])
|
|
274
|
+
print(f"Pushed tag {tag_name}")
|
|
275
|
+
else:
|
|
276
|
+
print(f"Would set version to {new_version}")
|
|
277
|
+
print(f"Would commit and push tag v{new_version}")
|
|
278
|
+
|
|
233
279
|
if args.action in ["upload", "release"]:
|
|
234
280
|
if args.action == "release" and not args.skip_tests:
|
|
235
281
|
if not args.dry_run:
|
|
@@ -12,6 +12,7 @@ from .exceptions import (
|
|
|
12
12
|
PdfDancerException,
|
|
13
13
|
RateLimitException,
|
|
14
14
|
SessionException,
|
|
15
|
+
SessionNotFoundException,
|
|
15
16
|
ValidationException,
|
|
16
17
|
)
|
|
17
18
|
from .models import (
|
|
@@ -34,6 +35,7 @@ from .models import (
|
|
|
34
35
|
PageSize,
|
|
35
36
|
Paragraph,
|
|
36
37
|
Path,
|
|
38
|
+
PathGroupInfo,
|
|
37
39
|
PathSegment,
|
|
38
40
|
Point,
|
|
39
41
|
Position,
|
|
@@ -47,12 +49,13 @@ from .models import (
|
|
|
47
49
|
TextObjectRef,
|
|
48
50
|
TextStatus,
|
|
49
51
|
)
|
|
52
|
+
from .types import PathGroupObject
|
|
50
53
|
from .page_builder import PageBuilder
|
|
51
54
|
from .paragraph_builder import ParagraphBuilder
|
|
52
55
|
from .path_builder import BezierBuilder, LineBuilder, PathBuilder
|
|
53
56
|
from .text_line_builder import TextLineBuilder
|
|
54
57
|
|
|
55
|
-
__version__ = "
|
|
58
|
+
__version__ = "0.3.12"
|
|
56
59
|
__all__ = [
|
|
57
60
|
"PDFDancer",
|
|
58
61
|
"ParagraphBuilder",
|
|
@@ -89,6 +92,8 @@ __all__ = [
|
|
|
89
92
|
"Line",
|
|
90
93
|
"Bezier",
|
|
91
94
|
"Path",
|
|
95
|
+
"PathGroupInfo",
|
|
96
|
+
"PathGroupObject",
|
|
92
97
|
"RedactTarget",
|
|
93
98
|
"RedactResponse",
|
|
94
99
|
"ReflowPreset",
|
|
@@ -97,6 +102,7 @@ __all__ = [
|
|
|
97
102
|
"ValidationException",
|
|
98
103
|
"HttpClientException",
|
|
99
104
|
"SessionException",
|
|
105
|
+
"SessionNotFoundException",
|
|
100
106
|
"RateLimitException",
|
|
101
107
|
"set_ssl_verify",
|
|
102
108
|
]
|
{pdfdancer_client_python-0.3.10 → pdfdancer_client_python-0.3.12}/src/pdfdancer/exceptions.py
RENAMED
|
@@ -55,6 +55,15 @@ class SessionException(PdfDancerException):
|
|
|
55
55
|
pass
|
|
56
56
|
|
|
57
57
|
|
|
58
|
+
class SessionNotFoundException(SessionException):
|
|
59
|
+
"""
|
|
60
|
+
Exception raised when a session is not found (expired or invalid).
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def __init__(self, message: str):
|
|
64
|
+
super().__init__(message)
|
|
65
|
+
|
|
66
|
+
|
|
58
67
|
class ValidationException(PdfDancerException):
|
|
59
68
|
"""
|
|
60
69
|
Exception raised for input validation errors.
|
|
@@ -1643,6 +1643,25 @@ class Size:
|
|
|
1643
1643
|
return {"width": self.width, "height": self.height}
|
|
1644
1644
|
|
|
1645
1645
|
|
|
1646
|
+
@dataclass
|
|
1647
|
+
class PathGroupInfo:
|
|
1648
|
+
group_id: str
|
|
1649
|
+
path_count: int
|
|
1650
|
+
bounding_box: Optional[Dict[str, Any]]
|
|
1651
|
+
x: float
|
|
1652
|
+
y: float
|
|
1653
|
+
|
|
1654
|
+
@staticmethod
|
|
1655
|
+
def from_dict(d: Dict[str, Any]) -> "PathGroupInfo":
|
|
1656
|
+
return PathGroupInfo(
|
|
1657
|
+
group_id=d.get("groupId", ""),
|
|
1658
|
+
path_count=d.get("pathCount", 0),
|
|
1659
|
+
bounding_box=d.get("boundingBox"),
|
|
1660
|
+
x=d.get("x", 0.0),
|
|
1661
|
+
y=d.get("y", 0.0),
|
|
1662
|
+
)
|
|
1663
|
+
|
|
1664
|
+
|
|
1646
1665
|
class ImageTransformType(Enum):
|
|
1647
1666
|
"""Type of image transformation operation."""
|
|
1648
1667
|
|