pdfdancer-client-python 0.3.11__tar.gz → 0.3.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.
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/.github/workflows/ci.yml +74 -8
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/.github/workflows/daily-tests.yml +2 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/.github/workflows/release.yml +6 -0
- pdfdancer_client_python-0.3.13/.github/workflows/sdk-backward-compat.yml +88 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/.gitignore +2 -0
- pdfdancer_client_python-0.3.13/.gitmodules +3 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/PKG-INFO +2 -2
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/README.md +1 -1
- pdfdancer_client_python-0.3.13/docs/capabilities/.gitkeep +0 -0
- pdfdancer_client_python-0.3.13/docs/capabilities/CLEAR_CLIPPING.md +61 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/pyproject.toml +1 -1
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/src/pdfdancer/__init__.py +5 -1
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/src/pdfdancer/models.py +82 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/src/pdfdancer/pdfdancer_v1.py +168 -23
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/src/pdfdancer/types.py +103 -7
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/src/pdfdancer_client_python.egg-info/PKG-INFO +2 -2
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/src/pdfdancer_client_python.egg-info/SOURCES.txt +8 -3
- pdfdancer_client_python-0.3.13/tests/e2e/pdf_assertions.py +1783 -0
- pdfdancer_client_python-0.3.13/tests/e2e/test_clipping.py +323 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/e2e/test_context_manager.py +6 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/e2e/test_paragraph.py +15 -3
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/e2e/test_path.py +48 -1
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/e2e/test_redact.py +1 -0
- pdfdancer_client_python-0.3.13/tests/fixtures/Asimovian-Regular.ttf +0 -0
- pdfdancer_client_python-0.3.13/tests/fixtures/Roboto-Regular.ttf +0 -0
- pdfdancer_client_python-0.3.13/tests/fixtures/invisible-content-clipping-test.pdf +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/test_pdf_object_equality.py +19 -10
- 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.13}/.claude/commands/discuss.md +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/.claude/commands/implement-new-api-features.md +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/.flake8 +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/CLAUDE.md +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/LICENSE +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/NOTICE +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/TODO.md +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/check.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/media/logo-orange-512h.webp +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/media/logo-orange-60h.webp +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/media/logo-silver-512h.webp +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/media/logo-silver-60h.webp +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/release.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/setup.cfg +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/src/pdfdancer/exceptions.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/src/pdfdancer/fingerprint.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/src/pdfdancer/image_builder.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/src/pdfdancer/page_builder.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/src/pdfdancer/paragraph_builder.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/src/pdfdancer/path_builder.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/src/pdfdancer/text_line_builder.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/src/pdfdancer_client_python.egg-info/dependency_links.txt +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/src/pdfdancer_client_python.egg-info/requires.txt +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/src/pdfdancer_client_python.egg-info/top_level.txt +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/test.sh +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/__init__.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/conftest.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/e2e/__init__.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/e2e/test_acroform.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/e2e/test_bezier_builder.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/e2e/test_form_x_objects.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/e2e/test_image.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/e2e/test_image_transform.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/e2e/test_line.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/e2e/test_line_builder.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/e2e/test_new_pdf.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/e2e/test_page.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/e2e/test_path_builder.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/e2e/test_path_builder_rectangle.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/e2e/test_path_comprehensive.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/e2e/test_path_group.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/e2e/test_pdfdancer.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/e2e/test_positioning.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/e2e/test_rectangle_builder.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/e2e/test_singular_selection.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/e2e/test_snapshot.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/e2e/test_template_replace.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/e2e/test_template_replace_linebreak.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/e2e/test_text_line_edit.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/fixtures/DancingScript-Regular.ttf +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/fixtures/Empty.pdf +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/fixtures/JetBrainsMono-Regular.ttf +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/fixtures/Showcase.pdf +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/fixtures/basic-paths.pdf +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/fixtures/form-xobject-example.pdf +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/fixtures/logo-80.png +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/fixtures/mixed-form-types.pdf +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/test_anonymous_token.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/test_fingerprint.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/test_models.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/test_openapi_compliance.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/test_path_models.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/test_rate_limit.py +0 -0
- {pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/tests/test_standard_fonts.py +0 -0
|
@@ -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:
|
|
@@ -12,6 +12,10 @@ jobs:
|
|
|
12
12
|
# Quick minimal test on non-main branches
|
|
13
13
|
if: github.ref != 'refs/heads/main'
|
|
14
14
|
runs-on: ubuntu-latest
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
issues: read
|
|
18
|
+
pull-requests: read
|
|
15
19
|
strategy:
|
|
16
20
|
fail-fast: false
|
|
17
21
|
max-parallel: 2
|
|
@@ -20,6 +24,54 @@ jobs:
|
|
|
20
24
|
|
|
21
25
|
steps:
|
|
22
26
|
- uses: actions/checkout@v4
|
|
27
|
+
with:
|
|
28
|
+
submodules: true
|
|
29
|
+
|
|
30
|
+
- name: Resolve API base URL
|
|
31
|
+
id: resolve-api-base-url
|
|
32
|
+
uses: actions/github-script@v7
|
|
33
|
+
with:
|
|
34
|
+
github-token: ${{ secrets.PROPAGATE_TOKEN }}
|
|
35
|
+
script: |
|
|
36
|
+
const fallbackUrl = 'https://api-staging.pdfdancer.com';
|
|
37
|
+
const pr = context.payload.pull_request;
|
|
38
|
+
if (!pr) {
|
|
39
|
+
core.setOutput('base_url', fallbackUrl);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const body = pr.body || '';
|
|
44
|
+
const apiPrMatch = body.match(/## Source API PR[\s\S]*?^- Repository:\s*MenschMachine\/pdfdancer-api\s*$[\s\S]*?^- PR:\s*#?(\d+)\s*$/im);
|
|
45
|
+
if (!apiPrMatch) {
|
|
46
|
+
core.info('No Source API PR block found in the PR body; using staging API.');
|
|
47
|
+
core.setOutput('base_url', fallbackUrl);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const apiPrNumber = Number(apiPrMatch[1]);
|
|
52
|
+
const comments = await github.paginate(github.rest.issues.listComments, {
|
|
53
|
+
owner: 'MenschMachine',
|
|
54
|
+
repo: 'pdfdancer-api',
|
|
55
|
+
issue_number: apiPrNumber,
|
|
56
|
+
per_page: 100,
|
|
57
|
+
});
|
|
58
|
+
const previewComments = comments.filter((comment) =>
|
|
59
|
+
typeof comment.body === 'string' && comment.body.includes('### Preview Environment')
|
|
60
|
+
);
|
|
61
|
+
if (previewComments.length === 0) {
|
|
62
|
+
core.setFailed(`Source API PR #${apiPrNumber} is referenced by this PR, but no preview comment was found.`);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const previewComment = previewComments[previewComments.length - 1];
|
|
67
|
+
const urlMatch = previewComment.body.match(/\*\*API URL:\*\*\s*(https?:\/\/\S+)/);
|
|
68
|
+
if (!urlMatch) {
|
|
69
|
+
core.setFailed(`Preview comment on source API PR #${apiPrNumber} does not contain a parseable API URL.`);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
core.info(`Using preview API URL from source API PR #${apiPrNumber}: ${urlMatch[1]}`);
|
|
74
|
+
core.setOutput('base_url', urlMatch[1]);
|
|
23
75
|
|
|
24
76
|
- name: Set up Python ${{ matrix.python-version }}
|
|
25
77
|
uses: actions/setup-python@v5
|
|
@@ -40,8 +92,8 @@ jobs:
|
|
|
40
92
|
|
|
41
93
|
- name: Run tests
|
|
42
94
|
run: |
|
|
43
|
-
PDFDANCER_BASE_URL
|
|
44
|
-
|
|
95
|
+
PDFDANCER_BASE_URL=${{ steps.resolve-api-base-url.outputs.base_url }} \
|
|
96
|
+
PDFDANCER_API_TOKEN=42 \
|
|
45
97
|
venv/bin/python -m pytest tests/ -v --maxfail=3
|
|
46
98
|
|
|
47
99
|
- name: Build distribution packages
|
|
@@ -55,6 +107,10 @@ jobs:
|
|
|
55
107
|
# Full matrix only on main
|
|
56
108
|
if: github.ref == 'refs/heads/main'
|
|
57
109
|
runs-on: ${{ matrix.os }}
|
|
110
|
+
permissions:
|
|
111
|
+
contents: read
|
|
112
|
+
issues: read
|
|
113
|
+
pull-requests: read
|
|
58
114
|
strategy:
|
|
59
115
|
fail-fast: false
|
|
60
116
|
max-parallel: 2
|
|
@@ -64,6 +120,16 @@ jobs:
|
|
|
64
120
|
|
|
65
121
|
steps:
|
|
66
122
|
- uses: actions/checkout@v4
|
|
123
|
+
with:
|
|
124
|
+
submodules: true
|
|
125
|
+
|
|
126
|
+
- name: Resolve API base URL
|
|
127
|
+
id: resolve-api-base-url
|
|
128
|
+
uses: actions/github-script@v7
|
|
129
|
+
with:
|
|
130
|
+
github-token: ${{ secrets.PROPAGATE_TOKEN }}
|
|
131
|
+
script: |
|
|
132
|
+
core.setOutput('base_url', 'https://api-staging.pdfdancer.com');
|
|
67
133
|
|
|
68
134
|
- name: Set up Python ${{ matrix.python-version }}
|
|
69
135
|
uses: actions/setup-python@v5
|
|
@@ -88,8 +154,8 @@ jobs:
|
|
|
88
154
|
- name: Run tests (Unix)
|
|
89
155
|
if: runner.os != 'Windows'
|
|
90
156
|
run: |
|
|
91
|
-
PDFDANCER_BASE_URL
|
|
92
|
-
|
|
157
|
+
PDFDANCER_BASE_URL=${{ steps.resolve-api-base-url.outputs.base_url }} \
|
|
158
|
+
PDFDANCER_API_TOKEN=42 \
|
|
93
159
|
venv/bin/python -m pytest tests/ -v --maxfail=3
|
|
94
160
|
|
|
95
161
|
- name: Build & Validate (Unix)
|
|
@@ -119,8 +185,8 @@ jobs:
|
|
|
119
185
|
if: runner.os == 'Windows'
|
|
120
186
|
shell: cmd
|
|
121
187
|
run: |
|
|
122
|
-
set PDFDANCER_BASE_URL
|
|
123
|
-
set
|
|
188
|
+
set PDFDANCER_BASE_URL=${{ steps.resolve-api-base-url.outputs.base_url }}
|
|
189
|
+
set PDFDANCER_API_TOKEN=42
|
|
124
190
|
venv\Scripts\python -m pytest tests/ -v --maxfail=3
|
|
125
191
|
|
|
126
192
|
- name: Build & Validate (Windows)
|
|
@@ -128,4 +194,4 @@ jobs:
|
|
|
128
194
|
shell: cmd
|
|
129
195
|
run: |
|
|
130
196
|
venv\Scripts\python -m build
|
|
131
|
-
venv\Scripts\python -m twine check dist/*
|
|
197
|
+
venv\Scripts\python -m twine check dist/*
|
{pdfdancer_client_python-0.3.11 → pdfdancer_client_python-0.3.13}/.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
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
name: SDK Backward Compatibility Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
repository_dispatch:
|
|
5
|
+
types: [api-preview-deployed]
|
|
6
|
+
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
inputs:
|
|
9
|
+
api_url:
|
|
10
|
+
description: 'API base URL to test against'
|
|
11
|
+
required: true
|
|
12
|
+
type: string
|
|
13
|
+
sdk_token:
|
|
14
|
+
description: 'API token for testing'
|
|
15
|
+
required: true
|
|
16
|
+
type: string
|
|
17
|
+
default: '42'
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
get-tags:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
outputs:
|
|
23
|
+
tags: ${{ steps.tags.outputs.tags }}
|
|
24
|
+
steps:
|
|
25
|
+
- name: Fetch tags
|
|
26
|
+
id: tags
|
|
27
|
+
run: |
|
|
28
|
+
TAGS=$(git ls-remote --tags https://github.com/MenschMachine/pdfdancer-client-python.git \
|
|
29
|
+
| grep -v '\^{}' \
|
|
30
|
+
| awk -F'/' '{print $NF}' \
|
|
31
|
+
| grep '^v' \
|
|
32
|
+
| sort -V \
|
|
33
|
+
| tail -3 \
|
|
34
|
+
| jq -R -s -c 'split("\n") | map(select(length > 0))')
|
|
35
|
+
echo "tags=$TAGS" >> "$GITHUB_OUTPUT"
|
|
36
|
+
echo "Last 3 SDK tags: $TAGS"
|
|
37
|
+
|
|
38
|
+
test-compat:
|
|
39
|
+
needs: get-tags
|
|
40
|
+
runs-on: ubuntu-latest
|
|
41
|
+
timeout-minutes: 60
|
|
42
|
+
strategy:
|
|
43
|
+
fail-fast: false
|
|
44
|
+
max-parallel: 1
|
|
45
|
+
matrix:
|
|
46
|
+
tag: ${{ fromJSON(needs.get-tags.outputs.tags) }}
|
|
47
|
+
|
|
48
|
+
steps:
|
|
49
|
+
- name: Checkout SDK at ${{ matrix.tag }}
|
|
50
|
+
uses: actions/checkout@v4
|
|
51
|
+
with:
|
|
52
|
+
repository: MenschMachine/pdfdancer-client-python
|
|
53
|
+
ref: ${{ matrix.tag }}
|
|
54
|
+
path: sdk
|
|
55
|
+
|
|
56
|
+
- name: Set up Python
|
|
57
|
+
uses: actions/setup-python@v5
|
|
58
|
+
with:
|
|
59
|
+
python-version: '3.12'
|
|
60
|
+
|
|
61
|
+
- name: Create virtual environment
|
|
62
|
+
run: python -m venv venv
|
|
63
|
+
|
|
64
|
+
- name: Install SDK at ${{ matrix.tag }}
|
|
65
|
+
run: |
|
|
66
|
+
venv/bin/pip install --upgrade pip
|
|
67
|
+
venv/bin/pip install -e "./sdk[dev]"
|
|
68
|
+
|
|
69
|
+
- name: Run e2e tests
|
|
70
|
+
working-directory: sdk
|
|
71
|
+
run: |
|
|
72
|
+
PDFDANCER_BASE_URL=${{ github.event.client_payload.api_url || inputs.api_url }} \
|
|
73
|
+
PDFDANCER_API_TOKEN=${{ github.event.client_payload.sdk_token || inputs.sdk_token }} \
|
|
74
|
+
../venv/bin/python -m pytest -v -x \
|
|
75
|
+
-k "not test_redact_multiple_paths and not test_context_manager_vs_manual_apply" \
|
|
76
|
+
--junitxml=test-results.xml
|
|
77
|
+
env:
|
|
78
|
+
PYTHONWARNINGS: "ignore:Unverified HTTPS request"
|
|
79
|
+
|
|
80
|
+
- name: Upload test results
|
|
81
|
+
if: failure()
|
|
82
|
+
uses: actions/upload-artifact@v4
|
|
83
|
+
with:
|
|
84
|
+
name: test-results-${{ matrix.tag }}
|
|
85
|
+
path: |
|
|
86
|
+
sdk/test-results.xml
|
|
87
|
+
sdk/.pytest_cache/
|
|
88
|
+
retention-days: 7
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pdfdancer-client-python
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.13
|
|
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.
|
|
@@ -28,6 +28,7 @@ from .models import (
|
|
|
28
28
|
ImageTransformRequest,
|
|
29
29
|
ImageTransformType,
|
|
30
30
|
Line,
|
|
31
|
+
ModifyPathRequest,
|
|
31
32
|
ObjectRef,
|
|
32
33
|
ObjectType,
|
|
33
34
|
Orientation,
|
|
@@ -36,6 +37,7 @@ from .models import (
|
|
|
36
37
|
Paragraph,
|
|
37
38
|
Path,
|
|
38
39
|
PathGroupInfo,
|
|
40
|
+
PathObjectRef,
|
|
39
41
|
PathSegment,
|
|
40
42
|
Point,
|
|
41
43
|
Position,
|
|
@@ -55,7 +57,7 @@ from .paragraph_builder import ParagraphBuilder
|
|
|
55
57
|
from .path_builder import BezierBuilder, LineBuilder, PathBuilder
|
|
56
58
|
from .text_line_builder import TextLineBuilder
|
|
57
59
|
|
|
58
|
-
__version__ = "0.3.
|
|
60
|
+
__version__ = "0.3.13"
|
|
59
61
|
__all__ = [
|
|
60
62
|
"PDFDancer",
|
|
61
63
|
"ParagraphBuilder",
|
|
@@ -65,6 +67,8 @@ __all__ = [
|
|
|
65
67
|
"LineBuilder",
|
|
66
68
|
"BezierBuilder",
|
|
67
69
|
"ObjectRef",
|
|
70
|
+
"PathObjectRef",
|
|
71
|
+
"ModifyPathRequest",
|
|
68
72
|
"Position",
|
|
69
73
|
"ObjectType",
|
|
70
74
|
"Font",
|
|
@@ -1895,3 +1895,85 @@ class ImageTransformRequest:
|
|
|
1895
1895
|
result["fillColor"] = self.fill_color
|
|
1896
1896
|
|
|
1897
1897
|
return result
|
|
1898
|
+
|
|
1899
|
+
|
|
1900
|
+
@dataclass
|
|
1901
|
+
class ModifyPathRequest:
|
|
1902
|
+
"""Request to modify path stroke and fill colors.
|
|
1903
|
+
|
|
1904
|
+
Parameters:
|
|
1905
|
+
- object_ref: Reference to the path to modify.
|
|
1906
|
+
- stroke_color: New stroke color (optional - null means don't change).
|
|
1907
|
+
- fill_color: New fill color (optional - null means don't change).
|
|
1908
|
+
|
|
1909
|
+
Example:
|
|
1910
|
+
```python
|
|
1911
|
+
req = ModifyPathRequest(object_ref=path_ref, stroke_color=Color(255, 0, 0), fill_color=None)
|
|
1912
|
+
payload = req.to_dict()
|
|
1913
|
+
```
|
|
1914
|
+
"""
|
|
1915
|
+
|
|
1916
|
+
object_ref: ObjectRef
|
|
1917
|
+
stroke_color: Optional[Color] = None
|
|
1918
|
+
fill_color: Optional[Color] = None
|
|
1919
|
+
|
|
1920
|
+
def to_dict(self) -> dict:
|
|
1921
|
+
"""Convert to dictionary for JSON serialization."""
|
|
1922
|
+
result: Dict[str, Any] = {
|
|
1923
|
+
"ref": self.object_ref.to_dict(),
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
if self.stroke_color is not None:
|
|
1927
|
+
result["strokeColor"] = {
|
|
1928
|
+
"red": self.stroke_color.r,
|
|
1929
|
+
"green": self.stroke_color.g,
|
|
1930
|
+
"blue": self.stroke_color.b,
|
|
1931
|
+
"alpha": self.stroke_color.a,
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
if self.fill_color is not None:
|
|
1935
|
+
result["fillColor"] = {
|
|
1936
|
+
"red": self.fill_color.r,
|
|
1937
|
+
"green": self.fill_color.g,
|
|
1938
|
+
"blue": self.fill_color.b,
|
|
1939
|
+
"alpha": self.fill_color.a,
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
return result
|
|
1943
|
+
|
|
1944
|
+
|
|
1945
|
+
class PathObjectRef(ObjectRef):
|
|
1946
|
+
"""
|
|
1947
|
+
Reference to a path object with stroke and fill color information.
|
|
1948
|
+
|
|
1949
|
+
Parameters (typically provided by the server):
|
|
1950
|
+
- internal_id: Identifier of the path object.
|
|
1951
|
+
- position: Position of the path.
|
|
1952
|
+
- object_type: Should be ObjectType.PATH.
|
|
1953
|
+
- stroke_color: Stroke/outline color of the path (optional).
|
|
1954
|
+
- fill_color: Fill color of the path (optional).
|
|
1955
|
+
|
|
1956
|
+
Usage:
|
|
1957
|
+
- Returned by find/snapshot APIs when querying paths.
|
|
1958
|
+
- Pass to ModifyPathRequest to update path colors.
|
|
1959
|
+
"""
|
|
1960
|
+
|
|
1961
|
+
def __init__(
|
|
1962
|
+
self,
|
|
1963
|
+
internal_id: str,
|
|
1964
|
+
position: Position,
|
|
1965
|
+
object_type: ObjectType,
|
|
1966
|
+
stroke_color: Optional[Color] = None,
|
|
1967
|
+
fill_color: Optional[Color] = None,
|
|
1968
|
+
):
|
|
1969
|
+
super().__init__(internal_id, position, object_type)
|
|
1970
|
+
self.stroke_color = stroke_color
|
|
1971
|
+
self.fill_color = fill_color
|
|
1972
|
+
|
|
1973
|
+
def get_stroke_color(self) -> Optional[Color]:
|
|
1974
|
+
"""Get the stroke/outline color of the path."""
|
|
1975
|
+
return self.stroke_color
|
|
1976
|
+
|
|
1977
|
+
def get_fill_color(self) -> Optional[Color]:
|
|
1978
|
+
"""Get the fill color of the path."""
|
|
1979
|
+
return self.fill_color
|