pdfdancer-client-python 0.2.28__tar.gz → 0.2.29__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.2.28 → pdfdancer_client_python-0.2.29}/PKG-INFO +1 -1
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/pyproject.toml +1 -1
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/src/pdfdancer/types.py +52 -13
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/src/pdfdancer_client_python.egg-info/PKG-INFO +1 -1
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_text_line_edit.py +75 -79
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/.claude/commands/discuss.md +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/.flake8 +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/.github/workflows/ci.yml +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/.github/workflows/daily-tests.yml +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/.gitignore +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/CLAUDE.md +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/LICENSE +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/NOTICE +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/README.md +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/TODO.md +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/check.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/docs/openapi.yml +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/media/logo-orange-512h.webp +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/media/logo-orange-60h.webp +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/release.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/setup.cfg +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/src/pdfdancer/__init__.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/src/pdfdancer/exceptions.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/src/pdfdancer/fingerprint.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/src/pdfdancer/image_builder.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/src/pdfdancer/models.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/src/pdfdancer/page_builder.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/src/pdfdancer/paragraph_builder.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/src/pdfdancer/path_builder.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/src/pdfdancer/pdfdancer_v1.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/src/pdfdancer/text_line_builder.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/src/pdfdancer_client_python.egg-info/SOURCES.txt +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/src/pdfdancer_client_python.egg-info/dependency_links.txt +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/src/pdfdancer_client_python.egg-info/requires.txt +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/src/pdfdancer_client_python.egg-info/top_level.txt +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/test.sh +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/__init__.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/conftest.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/__init__.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/pdf_assertions.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_acroform.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_bezier_builder.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_context_manager.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_form_x_objects.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_image.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_line.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_line_builder.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_new_pdf.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_page.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_paragraph.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_path.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_path_builder.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_path_builder_rectangle.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_path_comprehensive.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_pdfdancer.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_positioning.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_rectangle_builder.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_singular_selection.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_snapshot.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/fixtures/DancingScript-Regular.ttf +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/fixtures/Empty.pdf +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/fixtures/JetBrainsMono-Regular.ttf +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/fixtures/Showcase.pdf +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/fixtures/basic-paths.pdf +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/fixtures/form-xobject-example.pdf +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/fixtures/logo-80.png +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/fixtures/mixed-form-types.pdf +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/test_anonymous_token.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/test_fingerprint.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/test_models.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/test_openapi_compliance.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/test_path_models.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/test_pdf_object_equality.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/test_rate_limit.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/test_standard_fonts.py +0 -0
- {pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/update-api-spec.sh +0 -0
|
@@ -160,14 +160,14 @@ class BaseTextEdit:
|
|
|
160
160
|
|
|
161
161
|
class TextLineEdit(BaseTextEdit):
|
|
162
162
|
def apply(self) -> bool:
|
|
163
|
-
#
|
|
163
|
+
# Line spacing is NOT supported for text lines - fail hard
|
|
164
164
|
if self._line_spacing is not None:
|
|
165
|
-
|
|
166
|
-
"
|
|
167
|
-
|
|
165
|
+
raise UnsupportedOperation(
|
|
166
|
+
"Line spacing changes are not supported for individual text lines. "
|
|
167
|
+
"Line spacing can only be modified on paragraphs, not individual text lines."
|
|
168
168
|
)
|
|
169
169
|
|
|
170
|
-
#
|
|
170
|
+
# If only text changed (no font, color, or position), use simple text modification
|
|
171
171
|
only_text_changed = (
|
|
172
172
|
self._new_text is not None
|
|
173
173
|
and self._font_name is None
|
|
@@ -185,7 +185,7 @@ class TextLineEdit(BaseTextEdit):
|
|
|
185
185
|
print(f"WARNING: {result.warning}", file=sys.stderr)
|
|
186
186
|
return result
|
|
187
187
|
|
|
188
|
-
#
|
|
188
|
+
# If only position changed (move operation)
|
|
189
189
|
only_move = (
|
|
190
190
|
self._position is not None
|
|
191
191
|
and self._new_text is None
|
|
@@ -216,15 +216,54 @@ class TextLineEdit(BaseTextEdit):
|
|
|
216
216
|
result = self._target_obj._client._move(self._object_ref, position)
|
|
217
217
|
return result
|
|
218
218
|
|
|
219
|
-
#
|
|
220
|
-
#
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
"To modify text styling, please modify the parent paragraph instead."
|
|
219
|
+
# For font/color changes or combined operations, use TextLineBuilder
|
|
220
|
+
# This ensures proper handling of font/color fallbacks just like ParagraphEditSession
|
|
221
|
+
from .text_line_builder import TextLineBuilder
|
|
222
|
+
|
|
223
|
+
builder = TextLineBuilder.from_object_ref(
|
|
224
|
+
self._target_obj._client, self._object_ref
|
|
226
225
|
)
|
|
227
226
|
|
|
227
|
+
# Apply modifications to builder
|
|
228
|
+
# IMPORTANT: Always explicitly set text to ensure it's preserved
|
|
229
|
+
if self._new_text is not None:
|
|
230
|
+
builder.text(self._new_text)
|
|
231
|
+
elif hasattr(self._object_ref, "text") and self._object_ref.text:
|
|
232
|
+
# Preserve original text when only changing font/color/position
|
|
233
|
+
builder.text(self._object_ref.text)
|
|
234
|
+
|
|
235
|
+
# IMPORTANT: Always explicitly set font to ensure it's preserved
|
|
236
|
+
if self._font_name is not None and self._font_size is not None:
|
|
237
|
+
builder.font(self._font_name, self._font_size)
|
|
238
|
+
elif hasattr(self._object_ref, "font_name") and hasattr(self._object_ref, "font_size"):
|
|
239
|
+
if self._object_ref.font_name and self._object_ref.font_size:
|
|
240
|
+
# Preserve original font when only changing color/position
|
|
241
|
+
builder.font(self._object_ref.font_name, self._object_ref.font_size)
|
|
242
|
+
|
|
243
|
+
if self._color is not None:
|
|
244
|
+
builder.color(self._color)
|
|
245
|
+
if self._position is not None:
|
|
246
|
+
x = self._position.x()
|
|
247
|
+
y = self._position.y()
|
|
248
|
+
if x is None or y is None:
|
|
249
|
+
raise ValidationException("Position must have x and y coordinates")
|
|
250
|
+
page_index = (
|
|
251
|
+
self._object_ref.position.page_index
|
|
252
|
+
if self._object_ref.position
|
|
253
|
+
else None
|
|
254
|
+
)
|
|
255
|
+
if page_index is None:
|
|
256
|
+
raise ValidationException(
|
|
257
|
+
"Text line position must include a page index"
|
|
258
|
+
)
|
|
259
|
+
builder.at(page_index, x, y)
|
|
260
|
+
|
|
261
|
+
# Use builder's modify method which handles all the complexity
|
|
262
|
+
result = builder.modify(self._object_ref)
|
|
263
|
+
if result.warning:
|
|
264
|
+
print(f"WARNING: {result.warning}", file=sys.stderr)
|
|
265
|
+
return result
|
|
266
|
+
|
|
228
267
|
|
|
229
268
|
class ParagraphObject(PDFObjectBase):
|
|
230
269
|
"""Represents a paragraph text block inside a PDF page."""
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_text_line_edit.py
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import pytest
|
|
2
|
-
|
|
3
2
|
from pdfdancer import Color
|
|
4
3
|
from pdfdancer.pdfdancer_v1 import PDFDancer
|
|
4
|
+
|
|
5
5
|
from tests.e2e import _require_env_and_fixture
|
|
6
6
|
from tests.e2e.pdf_assertions import PDFAssertions
|
|
7
7
|
|
|
@@ -15,8 +15,8 @@ def test_text_line_edit_text_only():
|
|
|
15
15
|
text_lines = pdf.page(0).select_text_lines_starting_with(
|
|
16
16
|
"This is regular Sans text showing alignment and styles."
|
|
17
17
|
)
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
|
|
19
|
+
assert len(text_lines) >= 1
|
|
20
20
|
|
|
21
21
|
text_line = text_lines[0]
|
|
22
22
|
|
|
@@ -34,49 +34,45 @@ def test_text_line_edit_text_only():
|
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
def test_text_line_edit_font_only():
|
|
37
|
-
"""Test
|
|
37
|
+
"""Test text line font-only changes"""
|
|
38
38
|
base_url, token, pdf_path = _require_env_and_fixture("Showcase.pdf")
|
|
39
39
|
|
|
40
40
|
with PDFDancer.open(pdf_path, token=token, base_url=base_url) as pdf:
|
|
41
41
|
text_lines = pdf.page(0).select_text_lines_starting_with(
|
|
42
42
|
"This is regular Sans text showing alignment and styles."
|
|
43
43
|
)
|
|
44
|
-
|
|
45
|
-
pytest.skip("Required text line not found in test PDF")
|
|
44
|
+
assert len(text_lines) >= 1
|
|
46
45
|
|
|
47
46
|
text_line = text_lines[0]
|
|
47
|
+
original_text = text_line.text
|
|
48
48
|
|
|
49
|
-
# Font changes on text lines should
|
|
50
|
-
|
|
49
|
+
# Font changes on text lines should work
|
|
50
|
+
with text_line.edit() as editor:
|
|
51
|
+
editor.font("Helvetica", 28)
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
):
|
|
55
|
-
with text_line.edit() as editor:
|
|
56
|
-
editor.font("Helvetica", 28)
|
|
53
|
+
# Verify the text still exists (font changed but text preserved)
|
|
54
|
+
PDFAssertions(pdf).assert_textline_exists(original_text)
|
|
57
55
|
|
|
58
56
|
|
|
59
57
|
def test_text_line_edit_color_only():
|
|
60
|
-
"""Test
|
|
58
|
+
"""Test text line color-only changes"""
|
|
61
59
|
base_url, token, pdf_path = _require_env_and_fixture("Showcase.pdf")
|
|
62
60
|
|
|
63
61
|
with PDFDancer.open(pdf_path, token=token, base_url=base_url) as pdf:
|
|
64
62
|
text_lines = pdf.page(0).select_text_lines_starting_with(
|
|
65
63
|
"This is regular Sans text showing alignment and styles."
|
|
66
64
|
)
|
|
67
|
-
|
|
68
|
-
pytest.skip("Required text line not found in test PDF")
|
|
65
|
+
assert len(text_lines) >= 1
|
|
69
66
|
|
|
70
67
|
text_line = text_lines[0]
|
|
68
|
+
original_text = text_line.text
|
|
71
69
|
|
|
72
|
-
# Color changes on text lines should
|
|
73
|
-
|
|
70
|
+
# Color changes on text lines should work
|
|
71
|
+
with text_line.edit() as editor:
|
|
72
|
+
editor.color(Color(0, 255, 0))
|
|
74
73
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
):
|
|
78
|
-
with text_line.edit() as editor:
|
|
79
|
-
editor.color(Color(0, 255, 0))
|
|
74
|
+
# Verify the text still exists (color changed but text preserved)
|
|
75
|
+
PDFAssertions(pdf).assert_textline_exists(original_text)
|
|
80
76
|
|
|
81
77
|
|
|
82
78
|
def test_text_line_edit_move_only():
|
|
@@ -87,8 +83,7 @@ def test_text_line_edit_move_only():
|
|
|
87
83
|
text_lines = pdf.page(0).select_text_lines_starting_with(
|
|
88
84
|
"This is regular Sans text showing alignment and styles."
|
|
89
85
|
)
|
|
90
|
-
|
|
91
|
-
pytest.skip("Required text line not found in test PDF")
|
|
86
|
+
assert len(text_lines) >= 1
|
|
92
87
|
|
|
93
88
|
text_line = text_lines[0]
|
|
94
89
|
|
|
@@ -108,100 +103,105 @@ def test_text_line_edit_move_only():
|
|
|
108
103
|
|
|
109
104
|
|
|
110
105
|
def test_text_line_edit_text_and_font():
|
|
111
|
-
"""Test
|
|
106
|
+
"""Test text+font changes work together"""
|
|
112
107
|
base_url, token, pdf_path = _require_env_and_fixture("Showcase.pdf")
|
|
113
108
|
|
|
114
109
|
with PDFDancer.open(pdf_path, token=token, base_url=base_url) as pdf:
|
|
115
110
|
text_lines = pdf.page(0).select_text_lines_starting_with(
|
|
116
111
|
"This is regular Sans text showing alignment and styles."
|
|
117
112
|
)
|
|
118
|
-
|
|
119
|
-
pytest.skip("Required text line not found in test PDF")
|
|
113
|
+
assert len(text_lines) >= 1
|
|
120
114
|
|
|
121
115
|
text_line = text_lines[0]
|
|
122
116
|
|
|
123
|
-
# Text + Font changes should
|
|
124
|
-
|
|
117
|
+
# Text + Font changes should work
|
|
118
|
+
with text_line.edit() as editor:
|
|
119
|
+
editor.replace("New Text Here")
|
|
120
|
+
editor.font("Helvetica", 16)
|
|
125
121
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
122
|
+
# Verify the new text exists
|
|
123
|
+
(
|
|
124
|
+
PDFAssertions(pdf)
|
|
125
|
+
.assert_textline_exists("New Text Here")
|
|
126
|
+
.assert_textline_does_not_exist(
|
|
127
|
+
"This is regular Sans text showing alignment and styles."
|
|
128
|
+
)
|
|
129
|
+
)
|
|
132
130
|
|
|
133
131
|
|
|
134
132
|
def test_text_line_edit_all_properties():
|
|
135
|
-
"""Test that combined property changes
|
|
133
|
+
"""Test that combined property changes work (except line spacing)"""
|
|
136
134
|
base_url, token, pdf_path = _require_env_and_fixture("Showcase.pdf")
|
|
137
135
|
|
|
138
136
|
with PDFDancer.open(pdf_path, token=token, base_url=base_url) as pdf:
|
|
139
137
|
text_lines = pdf.page(0).select_text_lines_starting_with(
|
|
140
138
|
"This is regular Sans text showing alignment and styles."
|
|
141
139
|
)
|
|
142
|
-
|
|
143
|
-
pytest.skip("Required text line not found in test PDF")
|
|
140
|
+
assert len(text_lines) >= 1
|
|
144
141
|
|
|
145
142
|
text_line = text_lines[0]
|
|
146
143
|
|
|
147
|
-
# Combined changes including font/color should
|
|
148
|
-
|
|
144
|
+
# Combined changes including font/color/position should work
|
|
145
|
+
with text_line.edit() as editor:
|
|
146
|
+
editor.replace("Fully Modified")
|
|
147
|
+
editor.font("Helvetica", 18)
|
|
148
|
+
editor.color(Color(255, 0, 0))
|
|
149
|
+
editor.move_to(100, 200)
|
|
149
150
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
):
|
|
153
|
-
with text_line.edit() as editor:
|
|
154
|
-
editor.replace("Fully Modified")
|
|
155
|
-
editor.font("Helvetica", 18)
|
|
156
|
-
editor.color(Color(255, 0, 0))
|
|
157
|
-
editor.move_to(100, 200)
|
|
151
|
+
# Verify the modified text exists
|
|
152
|
+
PDFAssertions(pdf).assert_textline_exists("Fully Modified")
|
|
158
153
|
|
|
159
154
|
|
|
160
|
-
def
|
|
161
|
-
"""Test that line spacing
|
|
155
|
+
def test_text_line_edit_line_spacing_fails():
|
|
156
|
+
"""Test that line spacing changes fail hard for text lines"""
|
|
162
157
|
base_url, token, pdf_path = _require_env_and_fixture("Showcase.pdf")
|
|
163
158
|
|
|
164
159
|
with PDFDancer.open(pdf_path, token=token, base_url=base_url) as pdf:
|
|
165
160
|
text_lines = pdf.page(0).select_text_lines_starting_with(
|
|
166
161
|
"This is regular Sans text showing alignment and styles."
|
|
167
162
|
)
|
|
168
|
-
|
|
169
|
-
pytest.skip("Required text line not found in test PDF")
|
|
163
|
+
assert len(text_lines) >= 1
|
|
170
164
|
|
|
171
165
|
text_line = text_lines[0]
|
|
172
166
|
|
|
173
|
-
# Line spacing should
|
|
174
|
-
|
|
175
|
-
editor.line_spacing(2.0) # This should be ignored
|
|
176
|
-
editor.replace("Text with ignored spacing")
|
|
167
|
+
# Line spacing should raise UnsupportedOperation
|
|
168
|
+
from pdfdancer.types import UnsupportedOperation
|
|
177
169
|
|
|
178
|
-
|
|
179
|
-
|
|
170
|
+
with pytest.raises(
|
|
171
|
+
UnsupportedOperation,
|
|
172
|
+
match="Line spacing changes are not supported for individual text lines",
|
|
173
|
+
):
|
|
174
|
+
with text_line.edit() as editor:
|
|
175
|
+
editor.line_spacing(2.0)
|
|
176
|
+
editor.replace("Text with spacing")
|
|
180
177
|
|
|
181
178
|
|
|
182
179
|
def test_text_line_edit_chaining():
|
|
183
|
-
"""Test that chained font/color changes
|
|
180
|
+
"""Test that chained font/color changes work"""
|
|
184
181
|
base_url, token, pdf_path = _require_env_and_fixture("Showcase.pdf")
|
|
185
182
|
|
|
186
183
|
with PDFDancer.open(pdf_path, token=token, base_url=base_url) as pdf:
|
|
187
184
|
text_lines = pdf.page(0).select_text_lines_starting_with(
|
|
188
185
|
"This is regular Sans text showing alignment and styles."
|
|
189
186
|
)
|
|
190
|
-
|
|
191
|
-
pytest.skip("Required text line not found in test PDF")
|
|
187
|
+
assert len(text_lines) >= 1
|
|
192
188
|
|
|
193
189
|
text_line = text_lines[0]
|
|
194
190
|
|
|
195
|
-
# Chained font/color changes should
|
|
196
|
-
|
|
191
|
+
# Chained font/color changes should work
|
|
192
|
+
with text_line.edit() as editor:
|
|
193
|
+
editor.replace("Chained Edits").font("Helvetica", 15).color(
|
|
194
|
+
Color(128, 128, 128)
|
|
195
|
+
)
|
|
197
196
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
197
|
+
# Verify the new text exists
|
|
198
|
+
(
|
|
199
|
+
PDFAssertions(pdf)
|
|
200
|
+
.assert_textline_exists("Chained Edits")
|
|
201
|
+
.assert_textline_does_not_exist(
|
|
202
|
+
"This is regular Sans text showing alignment and styles."
|
|
203
|
+
)
|
|
204
|
+
)
|
|
205
205
|
|
|
206
206
|
|
|
207
207
|
def test_text_line_edit_with_exception_no_apply():
|
|
@@ -212,8 +212,7 @@ def test_text_line_edit_with_exception_no_apply():
|
|
|
212
212
|
text_lines = pdf.page(0).select_text_lines_starting_with(
|
|
213
213
|
"This is regular Sans text showing alignment and styles."
|
|
214
214
|
)
|
|
215
|
-
|
|
216
|
-
pytest.skip("Required text line not found in test PDF")
|
|
215
|
+
assert len(text_lines) >= 1
|
|
217
216
|
|
|
218
217
|
text_line = text_lines[0]
|
|
219
218
|
|
|
@@ -238,8 +237,7 @@ def test_text_line_edit_multiple_sequential():
|
|
|
238
237
|
text_lines = pdf.page(0).select_text_lines_starting_with(
|
|
239
238
|
"This is regular Sans text showing alignment and styles."
|
|
240
239
|
)
|
|
241
|
-
|
|
242
|
-
pytest.skip("Required text line not found in test PDF")
|
|
240
|
+
assert len(text_lines) >= 1
|
|
243
241
|
|
|
244
242
|
text_line = text_lines[0]
|
|
245
243
|
with text_line.edit() as editor:
|
|
@@ -270,8 +268,7 @@ def test_text_line_edit_vs_manual_apply():
|
|
|
270
268
|
text_lines = pdf1.page(0).select_text_lines_starting_with(
|
|
271
269
|
"This is regular Sans text showing alignment and styles."
|
|
272
270
|
)
|
|
273
|
-
|
|
274
|
-
pytest.skip("Required text line not found in test PDF")
|
|
271
|
+
assert len(text_lines) >= 1
|
|
275
272
|
|
|
276
273
|
text_line = text_lines[0]
|
|
277
274
|
|
|
@@ -285,8 +282,7 @@ def test_text_line_edit_vs_manual_apply():
|
|
|
285
282
|
text_lines = pdf2.page(0).select_text_lines_starting_with(
|
|
286
283
|
"This is regular Sans text showing alignment and styles."
|
|
287
284
|
)
|
|
288
|
-
|
|
289
|
-
pytest.skip("Required text line not found in test PDF")
|
|
285
|
+
assert len(text_lines) >= 1
|
|
290
286
|
|
|
291
287
|
text_line = text_lines[0]
|
|
292
288
|
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/.claude/commands/discuss.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/.github/workflows/daily-tests.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/media/logo-orange-512h.webp
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/media/logo-orange-60h.webp
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/src/pdfdancer/exceptions.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/src/pdfdancer/fingerprint.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/src/pdfdancer/image_builder.py
RENAMED
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/src/pdfdancer/page_builder.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/src/pdfdancer/paragraph_builder.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/src/pdfdancer/path_builder.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/src/pdfdancer/pdfdancer_v1.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/src/pdfdancer/text_line_builder.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/pdf_assertions.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_acroform.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_bezier_builder.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_context_manager.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_form_x_objects.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_line_builder.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_paragraph.py
RENAMED
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_path_builder.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_pdfdancer.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_positioning.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/e2e/test_snapshot.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/fixtures/Showcase.pdf
RENAMED
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/fixtures/basic-paths.pdf
RENAMED
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/fixtures/logo-80.png
RENAMED
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/test_anonymous_token.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/test_openapi_compliance.py
RENAMED
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/test_pdf_object_equality.py
RENAMED
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.2.28 → pdfdancer_client_python-0.2.29}/tests/test_standard_fonts.py
RENAMED
|
File without changes
|
|
File without changes
|