pdfdancer-client-python 0.2.25__py3-none-any.whl → 0.2.27__py3-none-any.whl
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/__init__.py +2 -0
- pdfdancer/models.py +47 -0
- pdfdancer/pdfdancer_v1.py +28 -0
- pdfdancer/text_line_builder.py +290 -0
- pdfdancer/types.py +54 -7
- {pdfdancer_client_python-0.2.25.dist-info → pdfdancer_client_python-0.2.27.dist-info}/METADATA +1 -1
- pdfdancer_client_python-0.2.27.dist-info/RECORD +17 -0
- pdfdancer_client_python-0.2.25.dist-info/RECORD +0 -16
- {pdfdancer_client_python-0.2.25.dist-info → pdfdancer_client_python-0.2.27.dist-info}/WHEEL +0 -0
- {pdfdancer_client_python-0.2.25.dist-info → pdfdancer_client_python-0.2.27.dist-info}/licenses/LICENSE +0 -0
- {pdfdancer_client_python-0.2.25.dist-info → pdfdancer_client_python-0.2.27.dist-info}/licenses/NOTICE +0 -0
- {pdfdancer_client_python-0.2.25.dist-info → pdfdancer_client_python-0.2.27.dist-info}/top_level.txt +0 -0
pdfdancer/__init__.py
CHANGED
|
@@ -43,11 +43,13 @@ from .models import (
|
|
|
43
43
|
from .page_builder import PageBuilder
|
|
44
44
|
from .paragraph_builder import ParagraphBuilder
|
|
45
45
|
from .path_builder import BezierBuilder, LineBuilder, PathBuilder
|
|
46
|
+
from .text_line_builder import TextLineBuilder
|
|
46
47
|
|
|
47
48
|
__version__ = "1.0.0"
|
|
48
49
|
__all__ = [
|
|
49
50
|
"PDFDancer",
|
|
50
51
|
"ParagraphBuilder",
|
|
52
|
+
"TextLineBuilder",
|
|
51
53
|
"PageBuilder",
|
|
52
54
|
"PathBuilder",
|
|
53
55
|
"LineBuilder",
|
pdfdancer/models.py
CHANGED
|
@@ -1102,6 +1102,53 @@ class AddRequest:
|
|
|
1102
1102
|
"lineSpacings": line_spacings,
|
|
1103
1103
|
"font": _font_to_dict(obj.font),
|
|
1104
1104
|
}
|
|
1105
|
+
elif isinstance(obj, TextLine):
|
|
1106
|
+
|
|
1107
|
+
def _font_to_dict(font: Optional[Font]) -> Optional[dict]:
|
|
1108
|
+
if font:
|
|
1109
|
+
return {"name": font.name, "size": font.size}
|
|
1110
|
+
return None
|
|
1111
|
+
|
|
1112
|
+
def _color_to_dict(color: Optional[Color]) -> Optional[dict]:
|
|
1113
|
+
if color:
|
|
1114
|
+
return {
|
|
1115
|
+
"red": color.r,
|
|
1116
|
+
"green": color.g,
|
|
1117
|
+
"blue": color.b,
|
|
1118
|
+
"alpha": color.a,
|
|
1119
|
+
}
|
|
1120
|
+
return None
|
|
1121
|
+
|
|
1122
|
+
# Build textElement with only non-null fields
|
|
1123
|
+
text_element = {
|
|
1124
|
+
"text": obj.text,
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
if obj.font:
|
|
1128
|
+
text_element["font"] = _font_to_dict(obj.font)
|
|
1129
|
+
if obj.color:
|
|
1130
|
+
text_element["color"] = _color_to_dict(obj.color)
|
|
1131
|
+
if obj.position:
|
|
1132
|
+
text_element["position"] = FindRequest._position_to_dict(obj.position)
|
|
1133
|
+
|
|
1134
|
+
# TEXT_LINE structure matches paragraph line format (textElements only)
|
|
1135
|
+
result = {
|
|
1136
|
+
"type": "TEXT_LINE",
|
|
1137
|
+
"position": (
|
|
1138
|
+
FindRequest._position_to_dict(obj.position)
|
|
1139
|
+
if obj.position
|
|
1140
|
+
else None
|
|
1141
|
+
),
|
|
1142
|
+
"textElements": [text_element],
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
# Only include top-level font/color if they are not None
|
|
1146
|
+
if obj.font:
|
|
1147
|
+
result["font"] = _font_to_dict(obj.font)
|
|
1148
|
+
if obj.color:
|
|
1149
|
+
result["color"] = _color_to_dict(obj.color)
|
|
1150
|
+
|
|
1151
|
+
return result
|
|
1105
1152
|
else:
|
|
1106
1153
|
raise ValueError(f"Unsupported object type: {type(obj)}")
|
|
1107
1154
|
|
pdfdancer/pdfdancer_v1.py
CHANGED
|
@@ -58,6 +58,7 @@ from .models import (
|
|
|
58
58
|
Position,
|
|
59
59
|
PositionMode,
|
|
60
60
|
ShapeType,
|
|
61
|
+
TextLine,
|
|
61
62
|
TextObjectRef,
|
|
62
63
|
)
|
|
63
64
|
from .page_builder import PageBuilder
|
|
@@ -2203,6 +2204,33 @@ class PDFDancer:
|
|
|
2203
2204
|
self._invalidate_snapshots()
|
|
2204
2205
|
return result
|
|
2205
2206
|
|
|
2207
|
+
def _modify_text_line_full(
|
|
2208
|
+
self, object_ref: ObjectRef, new_text_line: TextLine
|
|
2209
|
+
) -> CommandResult:
|
|
2210
|
+
"""
|
|
2211
|
+
Modifies a text line object with full styling (font, color, position).
|
|
2212
|
+
|
|
2213
|
+
Args:
|
|
2214
|
+
object_ref: Reference to the text line to modify
|
|
2215
|
+
new_text_line: New text line object with styling
|
|
2216
|
+
|
|
2217
|
+
Returns:
|
|
2218
|
+
CommandResult indicating success or failure
|
|
2219
|
+
"""
|
|
2220
|
+
if object_ref is None:
|
|
2221
|
+
raise ValidationException("Object reference cannot be null")
|
|
2222
|
+
if new_text_line is None:
|
|
2223
|
+
raise ValidationException("New text line cannot be null")
|
|
2224
|
+
|
|
2225
|
+
# Use /pdf/modify endpoint for full object modification
|
|
2226
|
+
request_data = ModifyRequest(object_ref, new_text_line).to_dict()
|
|
2227
|
+
response = self._make_request("PUT", "/pdf/modify", data=request_data)
|
|
2228
|
+
result = CommandResult.from_dict(response.json())
|
|
2229
|
+
|
|
2230
|
+
# Invalidate snapshot caches after mutation
|
|
2231
|
+
self._invalidate_snapshots()
|
|
2232
|
+
return result
|
|
2233
|
+
|
|
2206
2234
|
# Font Operations
|
|
2207
2235
|
|
|
2208
2236
|
def find_fonts(self, font_name: str, font_size: int) -> List[Font]:
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TextLineBuilder for the PDFDancer Python client.
|
|
3
|
+
Mirrors the behaviour of ParagraphBuilder for single line text objects.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from copy import deepcopy
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import TYPE_CHECKING, Optional, Union
|
|
11
|
+
|
|
12
|
+
from . import StandardFonts
|
|
13
|
+
from .exceptions import ValidationException
|
|
14
|
+
from .models import (
|
|
15
|
+
Color,
|
|
16
|
+
Font,
|
|
17
|
+
ObjectRef,
|
|
18
|
+
Position,
|
|
19
|
+
TextLine,
|
|
20
|
+
TextObjectRef,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from .pdfdancer_v1 import PDFDancer
|
|
25
|
+
|
|
26
|
+
DEFAULT_TEXT_COLOR = Color(0, 0, 0)
|
|
27
|
+
_DEFAULT_BASE_FONT_SIZE = 12.0
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class TextLineBuilder:
|
|
31
|
+
"""
|
|
32
|
+
Fluent builder used to assemble `TextLine` instances.
|
|
33
|
+
Behaviour is aligned with ParagraphBuilder but simplified for single-line text.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(self, client: "PDFDancer"):
|
|
37
|
+
if client is None:
|
|
38
|
+
raise ValidationException("Client cannot be null")
|
|
39
|
+
|
|
40
|
+
self._client = client
|
|
41
|
+
self._text_line = TextLine()
|
|
42
|
+
self._text_color: Optional[Color] = None
|
|
43
|
+
self._text: Optional[str] = None
|
|
44
|
+
self._ttf_file: Optional[Path] = None
|
|
45
|
+
self._font: Optional[Font] = None
|
|
46
|
+
self._font_explicitly_changed = False
|
|
47
|
+
self._original_text_line_position: Optional[Position] = None
|
|
48
|
+
self._target_object_ref: Optional[ObjectRef] = None
|
|
49
|
+
self._original_font: Optional[Font] = None
|
|
50
|
+
self._original_color: Optional[Color] = None
|
|
51
|
+
self._position_changed = False
|
|
52
|
+
|
|
53
|
+
def only_text_changed(self) -> bool:
|
|
54
|
+
"""Return True when only the text payload has been modified."""
|
|
55
|
+
return (
|
|
56
|
+
self._text is not None
|
|
57
|
+
and self._text_color is None
|
|
58
|
+
and self._ttf_file is None
|
|
59
|
+
and (self._font is None or not self._font_explicitly_changed)
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def text(self, text: str, color: Optional[Color] = None) -> "TextLineBuilder":
|
|
63
|
+
if text is None:
|
|
64
|
+
raise ValidationException("Text cannot be null")
|
|
65
|
+
self._text = text
|
|
66
|
+
if color is not None:
|
|
67
|
+
self.color(color)
|
|
68
|
+
return self
|
|
69
|
+
|
|
70
|
+
def font(
|
|
71
|
+
self, font: Union[Font, str, StandardFonts], font_size: Optional[float] = None
|
|
72
|
+
) -> "TextLineBuilder":
|
|
73
|
+
"""
|
|
74
|
+
Configure the font either by providing a `Font` instance or name + size.
|
|
75
|
+
"""
|
|
76
|
+
if isinstance(font, Font):
|
|
77
|
+
resolved_font = font
|
|
78
|
+
else:
|
|
79
|
+
if isinstance(font, StandardFonts):
|
|
80
|
+
font = font.value
|
|
81
|
+
if font is None:
|
|
82
|
+
raise ValidationException("Font name cannot be null")
|
|
83
|
+
if font_size is None:
|
|
84
|
+
raise ValidationException(
|
|
85
|
+
"Font size must be provided when setting font by name"
|
|
86
|
+
)
|
|
87
|
+
resolved_font = Font(str(font), font_size)
|
|
88
|
+
|
|
89
|
+
self._font = resolved_font
|
|
90
|
+
self._ttf_file = None
|
|
91
|
+
self._font_explicitly_changed = True
|
|
92
|
+
return self
|
|
93
|
+
|
|
94
|
+
def font_file(
|
|
95
|
+
self, ttf_file: Union[Path, str], font_size: float
|
|
96
|
+
) -> "TextLineBuilder":
|
|
97
|
+
if ttf_file is None:
|
|
98
|
+
raise ValidationException("TTF file cannot be null")
|
|
99
|
+
if font_size <= 0:
|
|
100
|
+
raise ValidationException(f"Font size must be positive, got {font_size}")
|
|
101
|
+
|
|
102
|
+
ttf_path = Path(ttf_file)
|
|
103
|
+
|
|
104
|
+
if not ttf_path.exists():
|
|
105
|
+
raise ValidationException(f"TTF file does not exist: {ttf_path}")
|
|
106
|
+
if not ttf_path.is_file():
|
|
107
|
+
raise ValidationException(f"TTF file is not a file: {ttf_path}")
|
|
108
|
+
if ttf_path.stat().st_size <= 0:
|
|
109
|
+
raise ValidationException(f"TTF file is empty: {ttf_path}")
|
|
110
|
+
|
|
111
|
+
try:
|
|
112
|
+
with open(ttf_path, "rb") as handle:
|
|
113
|
+
handle.read(1)
|
|
114
|
+
except (IOError, OSError) as exc:
|
|
115
|
+
raise ValidationException(f"TTF file is not readable: {ttf_path}") from exc
|
|
116
|
+
|
|
117
|
+
self._ttf_file = ttf_path
|
|
118
|
+
self._font = self._register_ttf(ttf_path, font_size)
|
|
119
|
+
self._font_explicitly_changed = True
|
|
120
|
+
return self
|
|
121
|
+
|
|
122
|
+
def set_font_explicitly_changed(self, changed: bool) -> None:
|
|
123
|
+
self._font_explicitly_changed = bool(changed)
|
|
124
|
+
|
|
125
|
+
def set_original_text_line_position(self, position: Position) -> None:
|
|
126
|
+
self._original_text_line_position = position
|
|
127
|
+
if position and self._text_line.position is None:
|
|
128
|
+
self._text_line.position = deepcopy(position)
|
|
129
|
+
|
|
130
|
+
def target(self, object_ref: ObjectRef) -> "TextLineBuilder":
|
|
131
|
+
if object_ref is None:
|
|
132
|
+
raise ValidationException("Object reference cannot be null")
|
|
133
|
+
self._target_object_ref = object_ref
|
|
134
|
+
return self
|
|
135
|
+
|
|
136
|
+
def color(self, color: Color) -> "TextLineBuilder":
|
|
137
|
+
if color is None:
|
|
138
|
+
raise ValidationException("Color cannot be null")
|
|
139
|
+
self._text_color = color
|
|
140
|
+
return self
|
|
141
|
+
|
|
142
|
+
def move_to(self, x: float, y: float) -> "TextLineBuilder":
|
|
143
|
+
"""
|
|
144
|
+
Move the text line to new coordinates on the same page.
|
|
145
|
+
"""
|
|
146
|
+
position = self._text_line.position
|
|
147
|
+
if (
|
|
148
|
+
position is None
|
|
149
|
+
and self._target_object_ref
|
|
150
|
+
and self._target_object_ref.position
|
|
151
|
+
):
|
|
152
|
+
position = deepcopy(self._target_object_ref.position)
|
|
153
|
+
self._text_line.position = position
|
|
154
|
+
|
|
155
|
+
if position is None:
|
|
156
|
+
raise ValidationException(
|
|
157
|
+
"Cannot move text line without an existing position"
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
page_index = position.page_index
|
|
161
|
+
if page_index is None:
|
|
162
|
+
raise ValidationException(
|
|
163
|
+
"Text line position must include a page index to move"
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
self._position_changed = True
|
|
167
|
+
return self.at(page_index, x, y)
|
|
168
|
+
|
|
169
|
+
def at_position(self, position: Position) -> "TextLineBuilder":
|
|
170
|
+
if position is None:
|
|
171
|
+
raise ValidationException("Position cannot be null")
|
|
172
|
+
# Defensive copy so builder mutations do not alter original references
|
|
173
|
+
self._text_line.position = deepcopy(position)
|
|
174
|
+
self._position_changed = True
|
|
175
|
+
return self
|
|
176
|
+
|
|
177
|
+
def at(self, page_index: int, x: float, y: float) -> "TextLineBuilder":
|
|
178
|
+
return self.at_position(Position.at_page_coordinates(page_index, x, y))
|
|
179
|
+
|
|
180
|
+
def get_text(self) -> Optional[str]:
|
|
181
|
+
return self._text
|
|
182
|
+
|
|
183
|
+
def add(self) -> bool:
|
|
184
|
+
"""
|
|
185
|
+
Add a new text line to the document.
|
|
186
|
+
Note: Text lines are typically part of paragraphs. This method is not
|
|
187
|
+
currently supported for standalone text lines.
|
|
188
|
+
"""
|
|
189
|
+
raise NotImplementedError(
|
|
190
|
+
"Adding standalone text lines is not supported. "
|
|
191
|
+
"Text lines should be added as part of paragraphs."
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
def modify(self, object_ref: Optional[ObjectRef] = None):
|
|
195
|
+
target_ref = object_ref or self._target_object_ref
|
|
196
|
+
if target_ref is None:
|
|
197
|
+
raise ValidationException(
|
|
198
|
+
"Object reference must be provided to modify a text line"
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
if self.only_text_changed():
|
|
202
|
+
# Backend accepts plain text updates for simple edits
|
|
203
|
+
return self._client._modify_text_line(target_ref, self._text or "")
|
|
204
|
+
|
|
205
|
+
text_line = self._finalize_text_line()
|
|
206
|
+
# Use /pdf/modify endpoint for complex modifications
|
|
207
|
+
return self._client._modify_text_line_full(target_ref, text_line)
|
|
208
|
+
|
|
209
|
+
# ------------------------------------------------------------------ #
|
|
210
|
+
# Internal helpers
|
|
211
|
+
# ------------------------------------------------------------------ #
|
|
212
|
+
|
|
213
|
+
def _finalize_text_line(self) -> TextLine:
|
|
214
|
+
if self._text_line.position is None:
|
|
215
|
+
raise ValidationException("Position must be set before building text line")
|
|
216
|
+
|
|
217
|
+
if (
|
|
218
|
+
self._target_object_ref is None
|
|
219
|
+
and self._font is None
|
|
220
|
+
and self._text_line.font is None
|
|
221
|
+
):
|
|
222
|
+
raise ValidationException("Font must be set before building text line")
|
|
223
|
+
|
|
224
|
+
if self._text is not None:
|
|
225
|
+
self._text_line.text = self._text
|
|
226
|
+
elif not self._text_line.text:
|
|
227
|
+
raise ValidationException("Text must be provided for text line")
|
|
228
|
+
|
|
229
|
+
final_font = self._font if self._font is not None else self._original_font
|
|
230
|
+
if final_font is None:
|
|
231
|
+
final_font = Font(StandardFonts.HELVETICA.value, _DEFAULT_BASE_FONT_SIZE)
|
|
232
|
+
self._text_line.font = final_font
|
|
233
|
+
|
|
234
|
+
if self._text_color is not None:
|
|
235
|
+
final_color = self._text_color
|
|
236
|
+
elif self._text is not None:
|
|
237
|
+
final_color = self._original_color or DEFAULT_TEXT_COLOR
|
|
238
|
+
else:
|
|
239
|
+
final_color = self._original_color
|
|
240
|
+
|
|
241
|
+
# Ensure color is never None
|
|
242
|
+
if final_color is None:
|
|
243
|
+
final_color = DEFAULT_TEXT_COLOR
|
|
244
|
+
self._text_line.color = final_color
|
|
245
|
+
|
|
246
|
+
return self._text_line
|
|
247
|
+
|
|
248
|
+
def _register_ttf(self, ttf_file: Path, font_size: float) -> Font:
|
|
249
|
+
try:
|
|
250
|
+
font_name = self._client.register_font(ttf_file)
|
|
251
|
+
return Font(font_name, font_size)
|
|
252
|
+
except Exception as exc:
|
|
253
|
+
raise ValidationException(
|
|
254
|
+
f"Failed to register font file {ttf_file}: {exc}"
|
|
255
|
+
) from exc
|
|
256
|
+
|
|
257
|
+
@classmethod
|
|
258
|
+
def from_object_ref(
|
|
259
|
+
cls, client: "PDFDancer", object_ref: TextObjectRef
|
|
260
|
+
) -> "TextLineBuilder":
|
|
261
|
+
if object_ref is None:
|
|
262
|
+
raise ValidationException("Object reference cannot be null")
|
|
263
|
+
|
|
264
|
+
builder = cls(client)
|
|
265
|
+
builder.target(object_ref)
|
|
266
|
+
|
|
267
|
+
if object_ref.position:
|
|
268
|
+
builder.at_position(object_ref.position)
|
|
269
|
+
builder.set_original_text_line_position(object_ref.position)
|
|
270
|
+
|
|
271
|
+
if object_ref.font_name and object_ref.font_size:
|
|
272
|
+
builder._original_font = Font(object_ref.font_name, object_ref.font_size)
|
|
273
|
+
|
|
274
|
+
if object_ref.color:
|
|
275
|
+
builder._original_color = object_ref.color
|
|
276
|
+
|
|
277
|
+
if object_ref.text:
|
|
278
|
+
builder._text_line.text = object_ref.text
|
|
279
|
+
|
|
280
|
+
return builder
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
class TextLinePageBuilder(TextLineBuilder):
|
|
284
|
+
def __init__(self, client: "PDFDancer", page_index: int):
|
|
285
|
+
super().__init__(client)
|
|
286
|
+
self._page_index: Optional[int] = page_index
|
|
287
|
+
|
|
288
|
+
# noinspection PyMethodOverriding
|
|
289
|
+
def at(self, x: float, y: float) -> "TextLineBuilder":
|
|
290
|
+
return super().at(self._page_index, x, y)
|
pdfdancer/types.py
CHANGED
|
@@ -160,12 +160,23 @@ class BaseTextEdit:
|
|
|
160
160
|
|
|
161
161
|
class TextLineEdit(BaseTextEdit):
|
|
162
162
|
def apply(self) -> bool:
|
|
163
|
-
if
|
|
164
|
-
|
|
165
|
-
|
|
163
|
+
# Text lines don't support line spacing - ignore if set
|
|
164
|
+
if self._line_spacing is not None:
|
|
165
|
+
print(
|
|
166
|
+
"WARNING: Line spacing is not supported for text lines and will be ignored",
|
|
167
|
+
file=sys.stderr,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
# Simple text-only change
|
|
171
|
+
only_text_changed = (
|
|
172
|
+
self._new_text is not None
|
|
166
173
|
and self._font_name is None
|
|
174
|
+
and self._font_size is None
|
|
167
175
|
and self._color is None
|
|
168
|
-
|
|
176
|
+
and self._position is None
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
if only_text_changed:
|
|
169
180
|
# noinspection PyProtectedMember
|
|
170
181
|
result = self._target_obj._client._modify_text_line(
|
|
171
182
|
self._object_ref, self._new_text
|
|
@@ -173,10 +184,46 @@ class TextLineEdit(BaseTextEdit):
|
|
|
173
184
|
if result.warning:
|
|
174
185
|
print(f"WARNING: {result.warning}", file=sys.stderr)
|
|
175
186
|
return result
|
|
176
|
-
|
|
187
|
+
|
|
188
|
+
# Position-only change (move operation)
|
|
189
|
+
only_move = (
|
|
190
|
+
self._position is not None
|
|
191
|
+
and self._new_text is None
|
|
192
|
+
and self._font_name is None
|
|
193
|
+
and self._font_size is None
|
|
194
|
+
and self._color is None
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
if only_move:
|
|
198
|
+
page_index = (
|
|
199
|
+
self._object_ref.position.page_index
|
|
200
|
+
if self._object_ref.position
|
|
201
|
+
else None
|
|
202
|
+
)
|
|
203
|
+
if page_index is None:
|
|
204
|
+
raise ValidationException(
|
|
205
|
+
"Text line position must include a page index to move"
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
# Extract x, y from self._position
|
|
209
|
+
x = self._position.x()
|
|
210
|
+
y = self._position.y()
|
|
211
|
+
if x is None or y is None:
|
|
212
|
+
raise ValidationException("Position must have x and y coordinates")
|
|
213
|
+
|
|
214
|
+
position = Position.at_page_coordinates(page_index, x, y)
|
|
177
215
|
# noinspection PyProtectedMember
|
|
178
|
-
|
|
179
|
-
|
|
216
|
+
result = self._target_obj._client._move(self._object_ref, position)
|
|
217
|
+
return result
|
|
218
|
+
|
|
219
|
+
# Text lines with font/color styling changes are not supported by the PDF API
|
|
220
|
+
# Text lines are components of paragraphs and can only have their text modified
|
|
221
|
+
# or be moved, but not have their styling changed independently
|
|
222
|
+
raise UnsupportedOperation(
|
|
223
|
+
"Text lines can only have their text content modified or be moved. "
|
|
224
|
+
"Font and color changes are not supported for individual text lines. "
|
|
225
|
+
"To modify text styling, please modify the parent paragraph instead."
|
|
226
|
+
)
|
|
180
227
|
|
|
181
228
|
|
|
182
229
|
class ParagraphObject(PDFObjectBase):
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
pdfdancer/__init__.py,sha256=DkDPiTq-u5FWGpD5FHFXpAvZ7em79_PWauQgtE5VLIw,2427
|
|
2
|
+
pdfdancer/exceptions.py,sha256=mJacmUJTPGUsB8Bo_FgfeXYkvZkH5OPJCVBfilBVmQo,2058
|
|
3
|
+
pdfdancer/fingerprint.py,sha256=eL3PHPgv-knMya7s95RXg3qzzpkAA1aevxqb6tuOb34,3061
|
|
4
|
+
pdfdancer/image_builder.py,sha256=MdSvZYU7-tq5HcuIpj2cfsd1iJKL9nryp87pCGlMnpM,1888
|
|
5
|
+
pdfdancer/models.py,sha256=Ml1eS6u8A16W0xQAdkX7h7nLFwNRMHg5HzwBoIOKz8U,50663
|
|
6
|
+
pdfdancer/page_builder.py,sha256=ecEK0lXk-7CoWEDOftZ-GwgRfNu5h7AD_J__LNkxJwI,3092
|
|
7
|
+
pdfdancer/paragraph_builder.py,sha256=yXdn2hoxpJYcUVAmSEOgoq-ApGIe9k9GWkokIIQWIJA,20489
|
|
8
|
+
pdfdancer/path_builder.py,sha256=2w0LPJo1u8bSjGAbYevTtOF4VD8M9B4SLe_wSLIYJx8,23917
|
|
9
|
+
pdfdancer/pdfdancer_v1.py,sha256=gaI9oDq8lksEi4DJAiZNebnj5XVFB8lYtGFw-ziTZTw,119130
|
|
10
|
+
pdfdancer/text_line_builder.py,sha256=74zc9wDPtSngHVGz_ykB3Bci_rEcwLr0dXGSEbLH5eo,10262
|
|
11
|
+
pdfdancer/types.py,sha256=ghIGh1zVkLDvSYQuHpvBP6WPB5YGclXdLbZ9d2FLf_I,15010
|
|
12
|
+
pdfdancer_client_python-0.2.27.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
13
|
+
pdfdancer_client_python-0.2.27.dist-info/licenses/NOTICE,sha256=xaC4l-IChAmtViNDie8ZWzUk0O6XRMyzOl0zLmVZ2HE,232
|
|
14
|
+
pdfdancer_client_python-0.2.27.dist-info/METADATA,sha256=Ay5WpYTSKzwGjSJFGXuGC4K9MAYMxazRjC_4n29zJ1Y,24729
|
|
15
|
+
pdfdancer_client_python-0.2.27.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
16
|
+
pdfdancer_client_python-0.2.27.dist-info/top_level.txt,sha256=ICwSVRpcCKrdBF9QlaX9Y0e_N3Nk1p7QVxadGOnbxeY,10
|
|
17
|
+
pdfdancer_client_python-0.2.27.dist-info/RECORD,,
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
pdfdancer/__init__.py,sha256=8E5dsMRws_zZZpM0i8k3zc34wHauCFZOuVa5BkumuBE,2357
|
|
2
|
-
pdfdancer/exceptions.py,sha256=mJacmUJTPGUsB8Bo_FgfeXYkvZkH5OPJCVBfilBVmQo,2058
|
|
3
|
-
pdfdancer/fingerprint.py,sha256=eL3PHPgv-knMya7s95RXg3qzzpkAA1aevxqb6tuOb34,3061
|
|
4
|
-
pdfdancer/image_builder.py,sha256=MdSvZYU7-tq5HcuIpj2cfsd1iJKL9nryp87pCGlMnpM,1888
|
|
5
|
-
pdfdancer/models.py,sha256=feEZc7kr5aN2QgIHpayCWksPLhxJVnukkFfe_Ri1E_w,49003
|
|
6
|
-
pdfdancer/page_builder.py,sha256=ecEK0lXk-7CoWEDOftZ-GwgRfNu5h7AD_J__LNkxJwI,3092
|
|
7
|
-
pdfdancer/paragraph_builder.py,sha256=yXdn2hoxpJYcUVAmSEOgoq-ApGIe9k9GWkokIIQWIJA,20489
|
|
8
|
-
pdfdancer/path_builder.py,sha256=2w0LPJo1u8bSjGAbYevTtOF4VD8M9B4SLe_wSLIYJx8,23917
|
|
9
|
-
pdfdancer/pdfdancer_v1.py,sha256=pjNwBHF_tPCXv7fnjxcm-6m5g_u91773rtvz9j_W9Yg,118088
|
|
10
|
-
pdfdancer/types.py,sha256=9F5LqgNVz48s0Q6lGgOcIfIbvE3vkPI_Vr4fOQqFk2k,13225
|
|
11
|
-
pdfdancer_client_python-0.2.25.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
12
|
-
pdfdancer_client_python-0.2.25.dist-info/licenses/NOTICE,sha256=xaC4l-IChAmtViNDie8ZWzUk0O6XRMyzOl0zLmVZ2HE,232
|
|
13
|
-
pdfdancer_client_python-0.2.25.dist-info/METADATA,sha256=8N-JlJX5sli-sPJ4OhMnW7faak3mu_y0U4sy5Ynjp7I,24729
|
|
14
|
-
pdfdancer_client_python-0.2.25.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
15
|
-
pdfdancer_client_python-0.2.25.dist-info/top_level.txt,sha256=ICwSVRpcCKrdBF9QlaX9Y0e_N3Nk1p7QVxadGOnbxeY,10
|
|
16
|
-
pdfdancer_client_python-0.2.25.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-0.2.25.dist-info → pdfdancer_client_python-0.2.27.dist-info}/top_level.txt
RENAMED
|
File without changes
|