pdfdancer-client-python 0.2.19__py3-none-any.whl → 0.2.21__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.
Potentially problematic release.
This version of pdfdancer-client-python might be problematic. Click here for more details.
- pdfdancer/__init__.py +2 -0
- pdfdancer/image_builder.py +30 -0
- pdfdancer/models.py +545 -58
- pdfdancer/page_builder.py +92 -0
- pdfdancer/paragraph_builder.py +392 -177
- pdfdancer/path_builder.py +252 -0
- pdfdancer/pdfdancer_v1.py +61 -22
- pdfdancer/types.py +112 -103
- {pdfdancer_client_python-0.2.19.dist-info → pdfdancer_client_python-0.2.21.dist-info}/METADATA +1 -1
- pdfdancer_client_python-0.2.21.dist-info/RECORD +16 -0
- pdfdancer_client_python-0.2.19.dist-info/RECORD +0 -15
- {pdfdancer_client_python-0.2.19.dist-info → pdfdancer_client_python-0.2.21.dist-info}/WHEEL +0 -0
- {pdfdancer_client_python-0.2.19.dist-info → pdfdancer_client_python-0.2.21.dist-info}/licenses/LICENSE +0 -0
- {pdfdancer_client_python-0.2.19.dist-info → pdfdancer_client_python-0.2.21.dist-info}/licenses/NOTICE +0 -0
- {pdfdancer_client_python-0.2.19.dist-info → pdfdancer_client_python-0.2.21.dist-info}/top_level.txt +0 -0
pdfdancer/models.py
CHANGED
|
@@ -9,7 +9,36 @@ from typing import Optional, List, Any, Dict, Mapping, Tuple, ClassVar, Union
|
|
|
9
9
|
|
|
10
10
|
@dataclass(frozen=True)
|
|
11
11
|
class PageSize:
|
|
12
|
-
"""Represents a page size specification, covering both standard and custom dimensions.
|
|
12
|
+
"""Represents a page size specification, covering both standard and custom dimensions.
|
|
13
|
+
|
|
14
|
+
Parameters:
|
|
15
|
+
- name: Optional canonical name of the size (e.g. "A4", "LETTER"). Will be upper‑cased.
|
|
16
|
+
- width: Page width in PDF points (1/72 inch).
|
|
17
|
+
- height: Page height in PDF points (1/72 inch).
|
|
18
|
+
|
|
19
|
+
Notes:
|
|
20
|
+
- Use `PageSize.from_name()` or the convenience constants (e.g. `PageSize.A4`) for common sizes.
|
|
21
|
+
- `width` and `height` must be positive numbers and are validated in `__post_init__`.
|
|
22
|
+
|
|
23
|
+
Examples:
|
|
24
|
+
- From standard name:
|
|
25
|
+
```python
|
|
26
|
+
size = PageSize.from_name("A4") # or PageSize.A4
|
|
27
|
+
```
|
|
28
|
+
- From custom dimensions:
|
|
29
|
+
```python
|
|
30
|
+
size = PageSize(name=None, width=500.0, height=700.0)
|
|
31
|
+
```
|
|
32
|
+
- From dict (e.g. deserialized JSON):
|
|
33
|
+
```python
|
|
34
|
+
size = PageSize.from_dict({"width": 612, "height": 792, "name": "letter"})
|
|
35
|
+
```
|
|
36
|
+
- Coercion utility:
|
|
37
|
+
```python
|
|
38
|
+
size = PageSize.coerce("A4")
|
|
39
|
+
size = PageSize.coerce({"width": 300, "height": 300})
|
|
40
|
+
```
|
|
41
|
+
"""
|
|
13
42
|
|
|
14
43
|
name: Optional[str]
|
|
15
44
|
width: float
|
|
@@ -154,6 +183,7 @@ class StandardFonts(Enum):
|
|
|
154
183
|
|
|
155
184
|
|
|
156
185
|
class ObjectType(Enum):
|
|
186
|
+
"""Server object type discriminator used in refs, requests, and snapshots."""
|
|
157
187
|
FORM_FIELD = "FORM_FIELD"
|
|
158
188
|
IMAGE = "IMAGE"
|
|
159
189
|
FORM_X_OBJECT = "FORM_X_OBJECT"
|
|
@@ -166,6 +196,7 @@ class ObjectType(Enum):
|
|
|
166
196
|
RADIO_BUTTON = "RADIO_BUTTON"
|
|
167
197
|
BUTTON = "BUTTON"
|
|
168
198
|
DROPDOWN = "DROPDOWN"
|
|
199
|
+
TEXT_ELEMENT = "TEXT_ELEMENT"
|
|
169
200
|
|
|
170
201
|
|
|
171
202
|
class PositionMode(Enum):
|
|
@@ -215,7 +246,39 @@ class BoundingRect:
|
|
|
215
246
|
@dataclass
|
|
216
247
|
class Position:
|
|
217
248
|
"""
|
|
218
|
-
|
|
249
|
+
Spatial locator used to find or place objects on a page.
|
|
250
|
+
|
|
251
|
+
Parameters:
|
|
252
|
+
- page_index: Zero-based page index this position refers to. Required for most operations
|
|
253
|
+
that place or search on a specific page; use `Position.at_page()` as a shortcut.
|
|
254
|
+
- shape: Optional geometric shape used when matching by area (`POINT`, `LINE`, `CIRCLE`, `RECT`).
|
|
255
|
+
- mode: How to match objects relative to the shape (`INTERSECT` or `CONTAINS`).
|
|
256
|
+
- bounding_rect: Rectangle describing the area or point (for `POINT`, width/height are 0).
|
|
257
|
+
- text_starts_with: Filter for text objects whose content starts with this string.
|
|
258
|
+
- text_pattern: Regex pattern to match text content.
|
|
259
|
+
- name: Named anchor or element name to target (e.g. form field name).
|
|
260
|
+
|
|
261
|
+
Builder helpers:
|
|
262
|
+
- `Position.at_page(page_index)` – target a whole page.
|
|
263
|
+
- `Position.at_page_coordinates(page_index, x, y)` – target a point on a page.
|
|
264
|
+
- `Position.by_name(name)` – target object(s) by name.
|
|
265
|
+
- `pos.at_coordinates(Point(x, y))` – switch to a point on the current page.
|
|
266
|
+
- `pos.move_x(dx)`, `pos.move_y(dy)` – offset the current coordinates.
|
|
267
|
+
|
|
268
|
+
Examples:
|
|
269
|
+
```python
|
|
270
|
+
# A point on page 0
|
|
271
|
+
pos = Position.at_page_coordinates(0, x=72, y=720)
|
|
272
|
+
|
|
273
|
+
# Search by name (e.g. a form field) and then move down 12 points
|
|
274
|
+
pos = Position.by_name("Email").move_y(-12)
|
|
275
|
+
|
|
276
|
+
# Match anything intersecting a rectangular area on page 1
|
|
277
|
+
pos = Position.at_page(1)
|
|
278
|
+
pos.shape = ShapeType.RECT
|
|
279
|
+
pos.mode = PositionMode.INTERSECT
|
|
280
|
+
pos.bounding_rect = BoundingRect(x=100, y=100, width=200, height=50)
|
|
281
|
+
```
|
|
219
282
|
"""
|
|
220
283
|
page_index: Optional[int] = None
|
|
221
284
|
shape: Optional[ShapeType] = None
|
|
@@ -287,7 +350,24 @@ class Position:
|
|
|
287
350
|
@dataclass
|
|
288
351
|
class ObjectRef:
|
|
289
352
|
"""
|
|
290
|
-
|
|
353
|
+
Reference to an object in a PDF document returned by the server.
|
|
354
|
+
|
|
355
|
+
Parameters:
|
|
356
|
+
- internal_id: Server-side identifier for the object.
|
|
357
|
+
- position: Position information describing where the object is.
|
|
358
|
+
- type: Object type (see `ObjectType`).
|
|
359
|
+
|
|
360
|
+
Usage:
|
|
361
|
+
- Instances are typically returned in snapshots or find results.
|
|
362
|
+
- Pass an `ObjectRef` to request objects such as `MoveRequest`, `DeleteRequest`,
|
|
363
|
+
`ModifyRequest`, or `ModifyTextRequest`.
|
|
364
|
+
|
|
365
|
+
Example:
|
|
366
|
+
```python
|
|
367
|
+
# Move an object to a new position
|
|
368
|
+
new_pos = Position.at_page_coordinates(0, 120, 500)
|
|
369
|
+
payload = MoveRequest(object_ref=obj_ref, position=new_pos).to_dict()
|
|
370
|
+
```
|
|
291
371
|
"""
|
|
292
372
|
internal_id: str
|
|
293
373
|
position: Position
|
|
@@ -325,7 +405,23 @@ class ObjectRef:
|
|
|
325
405
|
|
|
326
406
|
@dataclass
|
|
327
407
|
class Color:
|
|
328
|
-
"""
|
|
408
|
+
"""RGB color with optional alpha channel.
|
|
409
|
+
|
|
410
|
+
Parameters:
|
|
411
|
+
- r: Red component (0-255)
|
|
412
|
+
- g: Green component (0-255)
|
|
413
|
+
- b: Blue component (0-255)
|
|
414
|
+
- a: Alpha component (0-255), default 255 (opaque)
|
|
415
|
+
|
|
416
|
+
Raises:
|
|
417
|
+
- ValueError: If any component is outside 0-255.
|
|
418
|
+
|
|
419
|
+
Example:
|
|
420
|
+
```python
|
|
421
|
+
red = Color(255, 0, 0)
|
|
422
|
+
semi_transparent_black = Color(0, 0, 0, a=128)
|
|
423
|
+
```
|
|
424
|
+
"""
|
|
329
425
|
r: int
|
|
330
426
|
g: int
|
|
331
427
|
b: int
|
|
@@ -339,7 +435,23 @@ class Color:
|
|
|
339
435
|
|
|
340
436
|
@dataclass
|
|
341
437
|
class Font:
|
|
342
|
-
"""
|
|
438
|
+
"""Font face and size.
|
|
439
|
+
|
|
440
|
+
Parameters:
|
|
441
|
+
- name: Font family name. Can be one of `StandardFonts` values or any embedded font name.
|
|
442
|
+
- size: Font size in points (> 0).
|
|
443
|
+
|
|
444
|
+
Raises:
|
|
445
|
+
- ValueError: If `size` is not positive.
|
|
446
|
+
|
|
447
|
+
Example:
|
|
448
|
+
```python
|
|
449
|
+
from pdfdancer.models import Font, StandardFonts
|
|
450
|
+
|
|
451
|
+
title_font = Font(name=StandardFonts.HELVETICA_BOLD.value, size=16)
|
|
452
|
+
body_font = Font(name="MyEmbeddedFont", size=10.5)
|
|
453
|
+
```
|
|
454
|
+
"""
|
|
343
455
|
name: str
|
|
344
456
|
size: float
|
|
345
457
|
|
|
@@ -351,10 +463,18 @@ class Font:
|
|
|
351
463
|
@dataclass
|
|
352
464
|
class PathSegment:
|
|
353
465
|
"""
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
466
|
+
Base class for vector path segments.
|
|
467
|
+
|
|
468
|
+
Parameters:
|
|
469
|
+
- stroke_color: Outline color for the segment (`Color`).
|
|
470
|
+
- fill_color: Fill color for closed shapes when applicable (`Color`).
|
|
471
|
+
- stroke_width: Line width in points.
|
|
472
|
+
- dash_array: Dash pattern (e.g. `[3, 2]` for 3 on, 2 off). None or empty for solid.
|
|
473
|
+
- dash_phase: Offset into the dash pattern.
|
|
474
|
+
|
|
475
|
+
Notes:
|
|
476
|
+
- Concrete subclasses are `Line` and `Bezier`.
|
|
477
|
+
- Used inside `Path.path_segments` and serialized by `AddRequest`.
|
|
358
478
|
"""
|
|
359
479
|
stroke_color: Optional[Color] = None
|
|
360
480
|
fill_color: Optional[Color] = None
|
|
@@ -386,9 +506,19 @@ class PathSegment:
|
|
|
386
506
|
@dataclass
|
|
387
507
|
class Line(PathSegment):
|
|
388
508
|
"""
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
509
|
+
Straight line segment between two points.
|
|
510
|
+
|
|
511
|
+
Parameters:
|
|
512
|
+
- p0: Start point.
|
|
513
|
+
- p1: End point.
|
|
514
|
+
|
|
515
|
+
Example:
|
|
516
|
+
```python
|
|
517
|
+
from pdfdancer.models import Line, Point, Path
|
|
518
|
+
|
|
519
|
+
line = Line(p0=Point(10, 10), p1=Point(100, 10))
|
|
520
|
+
path = Path(path_segments=[line])
|
|
521
|
+
```
|
|
392
522
|
"""
|
|
393
523
|
p0: Optional[Point] = None
|
|
394
524
|
p1: Optional[Point] = None
|
|
@@ -405,9 +535,20 @@ class Line(PathSegment):
|
|
|
405
535
|
@dataclass
|
|
406
536
|
class Bezier(PathSegment):
|
|
407
537
|
"""
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
538
|
+
Cubic Bezier curve segment defined by 4 points.
|
|
539
|
+
|
|
540
|
+
Parameters:
|
|
541
|
+
- p0: Start point.
|
|
542
|
+
- p1: First control point.
|
|
543
|
+
- p2: Second control point.
|
|
544
|
+
- p3: End point.
|
|
545
|
+
|
|
546
|
+
Example:
|
|
547
|
+
```python
|
|
548
|
+
curve = Bezier(
|
|
549
|
+
p0=Point(10, 10), p1=Point(50, 80), p2=Point(80, 50), p3=Point(120, 10)
|
|
550
|
+
)
|
|
551
|
+
```
|
|
411
552
|
"""
|
|
412
553
|
p0: Optional[Point] = None
|
|
413
554
|
p1: Optional[Point] = None
|
|
@@ -434,9 +575,28 @@ class Bezier(PathSegment):
|
|
|
434
575
|
@dataclass
|
|
435
576
|
class Path:
|
|
436
577
|
"""
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
578
|
+
Vector path composed of one or more `PathSegment`s.
|
|
579
|
+
|
|
580
|
+
Parameters:
|
|
581
|
+
- position: Where to place the path on the page.
|
|
582
|
+
- path_segments: List of `Line` and/or `Bezier` segments.
|
|
583
|
+
- even_odd_fill: If True, use even-odd rule for fills; otherwise nonzero winding.
|
|
584
|
+
|
|
585
|
+
Example (adding a triangle to a page):
|
|
586
|
+
```python
|
|
587
|
+
from pdfdancer.models import Path, Line, Point, Position, AddRequest
|
|
588
|
+
|
|
589
|
+
tri = Path(
|
|
590
|
+
position=Position.at_page_coordinates(0, 100, 100),
|
|
591
|
+
path_segments=[
|
|
592
|
+
Line(Point(0, 0), Point(50, 100)),
|
|
593
|
+
Line(Point(50, 100), Point(100, 0)),
|
|
594
|
+
Line(Point(100, 0), Point(0, 0)),
|
|
595
|
+
],
|
|
596
|
+
even_odd_fill=True,
|
|
597
|
+
)
|
|
598
|
+
payload = AddRequest(tri).to_dict()
|
|
599
|
+
```
|
|
440
600
|
"""
|
|
441
601
|
position: Optional[Position] = None
|
|
442
602
|
path_segments: Optional[List[PathSegment]] = None
|
|
@@ -462,7 +622,28 @@ class Path:
|
|
|
462
622
|
@dataclass
|
|
463
623
|
class Image:
|
|
464
624
|
"""
|
|
465
|
-
|
|
625
|
+
Raster image to be placed on a page.
|
|
626
|
+
|
|
627
|
+
Parameters:
|
|
628
|
+
- position: Where to place the image. Use `Position.at_page_coordinates(page, x, y)`.
|
|
629
|
+
- format: Image format hint for the server (e.g. "PNG", "JPEG"). Optional.
|
|
630
|
+
- width: Target width in points. Optional; server may infer from data.
|
|
631
|
+
- height: Target height in points. Optional; server may infer from data.
|
|
632
|
+
- data: Raw image bytes. If provided, it will be base64-encoded in `AddRequest.to_dict()`.
|
|
633
|
+
|
|
634
|
+
Example:
|
|
635
|
+
```python
|
|
636
|
+
from pdfdancer.models import Image, Position, AddRequest
|
|
637
|
+
|
|
638
|
+
img = Image(
|
|
639
|
+
position=Position.at_page_coordinates(0, 72, 600),
|
|
640
|
+
format="PNG",
|
|
641
|
+
width=128,
|
|
642
|
+
height=64,
|
|
643
|
+
data=open("/path/logo.png", "rb").read(),
|
|
644
|
+
)
|
|
645
|
+
payload = AddRequest(img).to_dict()
|
|
646
|
+
```
|
|
466
647
|
"""
|
|
467
648
|
position: Optional[Position] = None
|
|
468
649
|
format: Optional[str] = None
|
|
@@ -479,16 +660,67 @@ class Image:
|
|
|
479
660
|
self.position = position
|
|
480
661
|
|
|
481
662
|
|
|
663
|
+
@dataclass
|
|
664
|
+
class TextLine:
|
|
665
|
+
"""
|
|
666
|
+
One line of text to add to a page.
|
|
667
|
+
|
|
668
|
+
Parameters:
|
|
669
|
+
- position: Anchor position where the first line begins.
|
|
670
|
+
- text: the text
|
|
671
|
+
provide separate entries for multiple lines.
|
|
672
|
+
- font: Font to use for all text elements unless overridden later.
|
|
673
|
+
- color: Text color.
|
|
674
|
+
|
|
675
|
+
"""
|
|
676
|
+
position: Optional[Position] = None
|
|
677
|
+
font: Optional[Font] = None
|
|
678
|
+
color: Optional[Color] = None
|
|
679
|
+
line_spacing: float = 1.2
|
|
680
|
+
text: str = ""
|
|
681
|
+
|
|
682
|
+
def get_position(self) -> Optional[Position]:
|
|
683
|
+
"""Returns the position of this paragraph."""
|
|
684
|
+
return self.position
|
|
685
|
+
|
|
686
|
+
def set_position(self, position: Position) -> None:
|
|
687
|
+
"""Sets the position of this paragraph."""
|
|
688
|
+
self.position = position
|
|
689
|
+
|
|
690
|
+
|
|
482
691
|
@dataclass
|
|
483
692
|
class Paragraph:
|
|
484
693
|
"""
|
|
485
|
-
|
|
694
|
+
Multi-line text paragraph to add to a page.
|
|
695
|
+
|
|
696
|
+
Parameters:
|
|
697
|
+
- position: Anchor position where the first line begins.
|
|
698
|
+
- text_lines: List of strings, one per line. Use `\n` within a string only if desired; normally
|
|
699
|
+
provide separate entries for multiple lines.
|
|
700
|
+
- font: Font to use for all text elements unless overridden later.
|
|
701
|
+
- color: Text color.
|
|
702
|
+
- line_spacing: Distance multiplier between lines. Server expects a list, handled for you by `AddRequest`.
|
|
703
|
+
|
|
704
|
+
Example:
|
|
705
|
+
```python
|
|
706
|
+
from pdfdancer.models import Paragraph, Position, Font, Color, StandardFonts, AddRequest
|
|
707
|
+
|
|
708
|
+
para = Paragraph(
|
|
709
|
+
position=Position.at_page_coordinates(0, 72, 700),
|
|
710
|
+
text_lines=["Hello", "PDFDancer!"],
|
|
711
|
+
font=Font(StandardFonts.HELVETICA.value, 12),
|
|
712
|
+
color=Color(50, 50, 50),
|
|
713
|
+
line_spacing=1.4,
|
|
714
|
+
)
|
|
715
|
+
payload = AddRequest(para).to_dict()
|
|
716
|
+
```
|
|
486
717
|
"""
|
|
487
718
|
position: Optional[Position] = None
|
|
488
|
-
text_lines: Optional[List[
|
|
719
|
+
text_lines: Optional[List[TextLine]] = None
|
|
489
720
|
font: Optional[Font] = None
|
|
490
721
|
color: Optional[Color] = None
|
|
491
722
|
line_spacing: float = 1.2
|
|
723
|
+
line_spacings: Optional[List[float]] = None
|
|
492
724
|
|
|
493
725
|
def get_position(self) -> Optional[Position]:
|
|
494
726
|
"""Returns the position of this paragraph."""
|
|
@@ -498,11 +730,54 @@ class Paragraph:
|
|
|
498
730
|
"""Sets the position of this paragraph."""
|
|
499
731
|
self.position = position
|
|
500
732
|
|
|
733
|
+
def clear_lines(self) -> None:
|
|
734
|
+
"""Removes all text lines from this paragraph."""
|
|
735
|
+
self.text_lines = []
|
|
736
|
+
|
|
737
|
+
def add_line(self, text_line: TextLine) -> None:
|
|
738
|
+
"""Appends a text line to this paragraph."""
|
|
739
|
+
if self.text_lines is None:
|
|
740
|
+
self.text_lines = []
|
|
741
|
+
self.text_lines.append(text_line)
|
|
742
|
+
|
|
743
|
+
def get_lines(self) -> List[TextLine]:
|
|
744
|
+
"""Returns the list of text lines, defaulting to an empty list."""
|
|
745
|
+
if self.text_lines is None:
|
|
746
|
+
self.text_lines = []
|
|
747
|
+
return self.text_lines
|
|
748
|
+
|
|
749
|
+
def set_lines(self, lines: List[TextLine]) -> None:
|
|
750
|
+
"""Replaces the current text lines with the provided list."""
|
|
751
|
+
self.text_lines = list(lines)
|
|
752
|
+
|
|
753
|
+
def set_line_spacings(self, spacings: Optional[List[float]]) -> None:
|
|
754
|
+
"""Sets the per-line spacing factors for this paragraph."""
|
|
755
|
+
self.line_spacings = list(spacings) if spacings else None
|
|
756
|
+
|
|
757
|
+
def get_line_spacings(self) -> Optional[List[float]]:
|
|
758
|
+
"""Returns the per-line spacing factors if present."""
|
|
759
|
+
return list(self.line_spacings) if self.line_spacings else None
|
|
760
|
+
|
|
501
761
|
|
|
502
762
|
# Request classes for API communication
|
|
503
763
|
@dataclass
|
|
504
764
|
class FindRequest:
|
|
505
|
-
"""Request
|
|
765
|
+
"""Request for locating objects.
|
|
766
|
+
|
|
767
|
+
Parameters:
|
|
768
|
+
- object_type: Filter by `ObjectType` (optional). If None, all types may be returned.
|
|
769
|
+
- position: `Position` describing where/how to search.
|
|
770
|
+
- hint: Optional backend hint or free-form note to influence matching.
|
|
771
|
+
|
|
772
|
+
Usage:
|
|
773
|
+
```python
|
|
774
|
+
req = FindRequest(
|
|
775
|
+
object_type=ObjectType.TEXT_LINE,
|
|
776
|
+
position=Position.at_page_coordinates(0, 72, 700).with_text_starts("Hello"),
|
|
777
|
+
)
|
|
778
|
+
payload = req.to_dict()
|
|
779
|
+
```
|
|
780
|
+
"""
|
|
506
781
|
object_type: Optional[ObjectType]
|
|
507
782
|
position: Optional[Position]
|
|
508
783
|
hint: Optional[str] = None
|
|
@@ -541,7 +816,16 @@ class FindRequest:
|
|
|
541
816
|
|
|
542
817
|
@dataclass
|
|
543
818
|
class DeleteRequest:
|
|
544
|
-
"""Request
|
|
819
|
+
"""Request to delete an existing object.
|
|
820
|
+
|
|
821
|
+
Parameters:
|
|
822
|
+
- object_ref: The object to delete.
|
|
823
|
+
|
|
824
|
+
Example:
|
|
825
|
+
```python
|
|
826
|
+
payload = DeleteRequest(object_ref=obj_ref).to_dict()
|
|
827
|
+
```
|
|
828
|
+
"""
|
|
545
829
|
object_ref: ObjectRef
|
|
546
830
|
|
|
547
831
|
def to_dict(self) -> dict:
|
|
@@ -554,7 +838,19 @@ class DeleteRequest:
|
|
|
554
838
|
|
|
555
839
|
@dataclass
|
|
556
840
|
class MoveRequest:
|
|
557
|
-
"""Request object
|
|
841
|
+
"""Request to move an existing object to a new position.
|
|
842
|
+
|
|
843
|
+
Parameters:
|
|
844
|
+
- object_ref: The object to move (obtained from a snapshot or find call).
|
|
845
|
+
- position: The new target `Position` (commonly a point created with `Position.at_page_coordinates`).
|
|
846
|
+
|
|
847
|
+
Example:
|
|
848
|
+
```python
|
|
849
|
+
new_pos = Position.at_page_coordinates(0, 200, 500)
|
|
850
|
+
req = MoveRequest(object_ref=obj_ref, position=new_pos)
|
|
851
|
+
payload = req.to_dict()
|
|
852
|
+
```
|
|
853
|
+
"""
|
|
558
854
|
object_ref: ObjectRef
|
|
559
855
|
position: Position
|
|
560
856
|
|
|
@@ -570,7 +866,19 @@ class MoveRequest:
|
|
|
570
866
|
|
|
571
867
|
@dataclass
|
|
572
868
|
class PageMoveRequest:
|
|
573
|
-
"""Request
|
|
869
|
+
"""Request to reorder pages.
|
|
870
|
+
|
|
871
|
+
Parameters:
|
|
872
|
+
- from_page_index: Zero-based index of the page to move.
|
|
873
|
+
- to_page_index: Zero-based destination index.
|
|
874
|
+
|
|
875
|
+
Example:
|
|
876
|
+
```python
|
|
877
|
+
# Move first page to the end
|
|
878
|
+
req = PageMoveRequest(from_page_index=0, to_page_index=doc_page_count - 1)
|
|
879
|
+
payload = req.to_dict()
|
|
880
|
+
```
|
|
881
|
+
"""
|
|
574
882
|
from_page_index: int
|
|
575
883
|
to_page_index: int
|
|
576
884
|
|
|
@@ -581,9 +889,60 @@ class PageMoveRequest:
|
|
|
581
889
|
}
|
|
582
890
|
|
|
583
891
|
|
|
892
|
+
@dataclass
|
|
893
|
+
class AddPageRequest:
|
|
894
|
+
"""Request to add a new page to the document.
|
|
895
|
+
|
|
896
|
+
Parameters:
|
|
897
|
+
- page_index: Optional zero-based index where the new page should be inserted.
|
|
898
|
+
- orientation: Optional page orientation (portrait or landscape).
|
|
899
|
+
- page_size: Optional size of the page.
|
|
900
|
+
|
|
901
|
+
Only populated fields are sent to the server to maintain backward compatibility
|
|
902
|
+
with default server behavior.
|
|
903
|
+
"""
|
|
904
|
+
page_index: Optional[int] = None
|
|
905
|
+
orientation: Optional[Orientation] = None
|
|
906
|
+
page_size: Optional[PageSize] = None
|
|
907
|
+
|
|
908
|
+
def to_dict(self) -> dict:
|
|
909
|
+
payload: Dict[str, Any] = {}
|
|
910
|
+
if self.page_index is not None:
|
|
911
|
+
payload["pageIndex"] = int(self.page_index)
|
|
912
|
+
if self.orientation is not None:
|
|
913
|
+
orientation_value: Orientation
|
|
914
|
+
if isinstance(self.orientation, Orientation):
|
|
915
|
+
orientation_value = self.orientation
|
|
916
|
+
elif isinstance(self.orientation, str):
|
|
917
|
+
normalized = self.orientation.strip().upper()
|
|
918
|
+
orientation_value = Orientation(normalized)
|
|
919
|
+
else:
|
|
920
|
+
raise TypeError("Orientation must be an Orientation enum or string value")
|
|
921
|
+
payload["orientation"] = orientation_value.value
|
|
922
|
+
if self.page_size is not None:
|
|
923
|
+
page_size = PageSize.coerce(self.page_size)
|
|
924
|
+
payload["pageSize"] = page_size.to_dict()
|
|
925
|
+
return payload
|
|
926
|
+
|
|
927
|
+
|
|
584
928
|
@dataclass
|
|
585
929
|
class AddRequest:
|
|
586
|
-
"""Request object
|
|
930
|
+
"""Request to add a new object to the document.
|
|
931
|
+
|
|
932
|
+
Parameters:
|
|
933
|
+
- pdf_object: The object to add (e.g. `Image`, `Paragraph`, or `Path`).
|
|
934
|
+
|
|
935
|
+
Usage:
|
|
936
|
+
```python
|
|
937
|
+
para = Paragraph(position=Position.at_page_coordinates(0, 72, 700), text_lines=["Hello"])
|
|
938
|
+
req = AddRequest(para)
|
|
939
|
+
payload = req.to_dict() # ready to send to the server API
|
|
940
|
+
```
|
|
941
|
+
|
|
942
|
+
Notes:
|
|
943
|
+
- Serialization details (like base64 for image `data`, or per-segment position for paths)
|
|
944
|
+
are handled for you in `to_dict()`.
|
|
945
|
+
"""
|
|
587
946
|
pdf_object: Any # Can be Image, Paragraph, etc.
|
|
588
947
|
|
|
589
948
|
def to_dict(self) -> dict:
|
|
@@ -599,7 +958,7 @@ class AddRequest:
|
|
|
599
958
|
def _object_to_dict(self, obj: Any) -> dict:
|
|
600
959
|
"""Convert PDF object to dictionary for JSON serialization."""
|
|
601
960
|
import base64
|
|
602
|
-
from .models import Path as PathModel
|
|
961
|
+
from .models import Path as PathModel
|
|
603
962
|
|
|
604
963
|
if isinstance(obj, PathModel):
|
|
605
964
|
# Serialize Path object
|
|
@@ -634,37 +993,55 @@ class AddRequest:
|
|
|
634
993
|
"data": data_b64
|
|
635
994
|
}
|
|
636
995
|
elif isinstance(obj, Paragraph):
|
|
637
|
-
|
|
638
|
-
|
|
996
|
+
def _font_to_dict(font: Optional[Font]) -> Optional[dict]:
|
|
997
|
+
if font:
|
|
998
|
+
return {"name": font.name, "size": font.size}
|
|
999
|
+
return None
|
|
1000
|
+
|
|
1001
|
+
def _color_to_dict(color: Optional[Color]) -> Optional[dict]:
|
|
1002
|
+
if color:
|
|
1003
|
+
return {"red": color.r, "green": color.g, "blue": color.b, "alpha": color.a}
|
|
1004
|
+
return None
|
|
1005
|
+
|
|
1006
|
+
lines_payload = []
|
|
639
1007
|
if obj.text_lines:
|
|
640
1008
|
for line in obj.text_lines:
|
|
1009
|
+
if isinstance(line, TextLine):
|
|
1010
|
+
line_text = line.text
|
|
1011
|
+
line_font = line.font or obj.font
|
|
1012
|
+
line_color = line.color or obj.color
|
|
1013
|
+
line_position = line.position or obj.position
|
|
1014
|
+
else:
|
|
1015
|
+
line_text = str(line)
|
|
1016
|
+
line_font = obj.font
|
|
1017
|
+
line_color = obj.color
|
|
1018
|
+
line_position = obj.position
|
|
1019
|
+
|
|
641
1020
|
text_element = {
|
|
642
|
-
"text":
|
|
643
|
-
"font":
|
|
644
|
-
"color":
|
|
645
|
-
|
|
646
|
-
"position": FindRequest._position_to_dict(obj.position) if obj.position else None
|
|
647
|
-
}
|
|
648
|
-
text_line = {
|
|
649
|
-
"textElements": [text_element]
|
|
1021
|
+
"text": line_text,
|
|
1022
|
+
"font": _font_to_dict(line_font),
|
|
1023
|
+
"color": _color_to_dict(line_color),
|
|
1024
|
+
"position": FindRequest._position_to_dict(line_position) if line_position else None
|
|
650
1025
|
}
|
|
651
|
-
|
|
652
|
-
if
|
|
653
|
-
text_line["color"] =
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
1026
|
+
text_line = {"textElements": [text_element]}
|
|
1027
|
+
if line_color:
|
|
1028
|
+
text_line["color"] = _color_to_dict(line_color)
|
|
1029
|
+
if line_position:
|
|
1030
|
+
text_line["position"] = FindRequest._position_to_dict(line_position)
|
|
1031
|
+
lines_payload.append(text_line)
|
|
1032
|
+
|
|
658
1033
|
line_spacings = None
|
|
659
|
-
if
|
|
660
|
-
|
|
1034
|
+
if getattr(obj, "line_spacings", None):
|
|
1035
|
+
line_spacings = list(obj.line_spacings)
|
|
1036
|
+
elif getattr(obj, "line_spacing", None) is not None:
|
|
661
1037
|
line_spacings = [obj.line_spacing]
|
|
1038
|
+
|
|
662
1039
|
return {
|
|
663
1040
|
"type": "PARAGRAPH",
|
|
664
1041
|
"position": FindRequest._position_to_dict(obj.position) if obj.position else None,
|
|
665
|
-
"lines":
|
|
1042
|
+
"lines": lines_payload if lines_payload else None,
|
|
666
1043
|
"lineSpacings": line_spacings,
|
|
667
|
-
"font":
|
|
1044
|
+
"font": _font_to_dict(obj.font)
|
|
668
1045
|
}
|
|
669
1046
|
else:
|
|
670
1047
|
raise ValueError(f"Unsupported object type: {type(obj)}")
|
|
@@ -727,7 +1104,19 @@ class AddRequest:
|
|
|
727
1104
|
|
|
728
1105
|
@dataclass
|
|
729
1106
|
class ModifyRequest:
|
|
730
|
-
"""Request object
|
|
1107
|
+
"""Request to replace an object with a new one of possibly different type.
|
|
1108
|
+
|
|
1109
|
+
Parameters:
|
|
1110
|
+
- object_ref: The existing object to replace.
|
|
1111
|
+
- new_object: The replacement object (e.g. `Paragraph`, `Image`, or `Path`).
|
|
1112
|
+
|
|
1113
|
+
Example:
|
|
1114
|
+
```python
|
|
1115
|
+
new_para = Paragraph(position=old.position, text_lines=["Updated text"])
|
|
1116
|
+
req = ModifyRequest(object_ref=old, new_object=new_para)
|
|
1117
|
+
payload = req.to_dict()
|
|
1118
|
+
```
|
|
1119
|
+
"""
|
|
731
1120
|
object_ref: ObjectRef
|
|
732
1121
|
new_object: Any
|
|
733
1122
|
|
|
@@ -742,7 +1131,18 @@ class ModifyRequest:
|
|
|
742
1131
|
|
|
743
1132
|
@dataclass
|
|
744
1133
|
class ModifyTextRequest:
|
|
745
|
-
"""Request
|
|
1134
|
+
"""Request to change the text content of a text object.
|
|
1135
|
+
|
|
1136
|
+
Parameters:
|
|
1137
|
+
- object_ref: The text object to modify (e.g. a `TextObjectRef`).
|
|
1138
|
+
- new_text: Replacement text content.
|
|
1139
|
+
|
|
1140
|
+
Example:
|
|
1141
|
+
```python
|
|
1142
|
+
req = ModifyTextRequest(object_ref=text_ref, new_text="Hello world")
|
|
1143
|
+
payload = req.to_dict()
|
|
1144
|
+
```
|
|
1145
|
+
"""
|
|
746
1146
|
object_ref: ObjectRef
|
|
747
1147
|
new_text: str
|
|
748
1148
|
|
|
@@ -757,6 +1157,19 @@ class ModifyTextRequest:
|
|
|
757
1157
|
|
|
758
1158
|
@dataclass
|
|
759
1159
|
class ChangeFormFieldRequest:
|
|
1160
|
+
"""Request to set a form field's value.
|
|
1161
|
+
|
|
1162
|
+
Parameters:
|
|
1163
|
+
- object_ref: A `FormFieldRef` (or generic `ObjectRef`) identifying the field.
|
|
1164
|
+
- value: The new value as a string. For checkboxes/radio buttons, use the
|
|
1165
|
+
appropriate on/off/selection string per the document's field options.
|
|
1166
|
+
|
|
1167
|
+
Example:
|
|
1168
|
+
```python
|
|
1169
|
+
req = ChangeFormFieldRequest(object_ref=field_ref, value="Jane Doe")
|
|
1170
|
+
payload = req.to_dict()
|
|
1171
|
+
```
|
|
1172
|
+
"""
|
|
760
1173
|
object_ref: ObjectRef
|
|
761
1174
|
value: str
|
|
762
1175
|
|
|
@@ -772,8 +1185,20 @@ class ChangeFormFieldRequest:
|
|
|
772
1185
|
@dataclass
|
|
773
1186
|
class FormFieldRef(ObjectRef):
|
|
774
1187
|
"""
|
|
775
|
-
|
|
776
|
-
|
|
1188
|
+
Reference to a form field object with name and value.
|
|
1189
|
+
|
|
1190
|
+
Parameters (usually provided by the server):
|
|
1191
|
+
- internal_id: Identifier of the form field object.
|
|
1192
|
+
- position: Position of the field.
|
|
1193
|
+
- type: One of `ObjectType.TEXT_FIELD`, `ObjectType.CHECK_BOX`, etc.
|
|
1194
|
+
- name: Field name (as defined inside the PDF).
|
|
1195
|
+
- value: Current field value (string representation).
|
|
1196
|
+
|
|
1197
|
+
Usage:
|
|
1198
|
+
- You can pass a `FormFieldRef` to `ChangeFormFieldRequest` to update its value.
|
|
1199
|
+
```python
|
|
1200
|
+
payload = ChangeFormFieldRequest(object_ref=field_ref, value="john@doe.com").to_dict()
|
|
1201
|
+
```
|
|
777
1202
|
"""
|
|
778
1203
|
name: Optional[str] = None
|
|
779
1204
|
value: Optional[str] = None
|
|
@@ -841,8 +1266,23 @@ class TextStatus:
|
|
|
841
1266
|
|
|
842
1267
|
class TextObjectRef(ObjectRef):
|
|
843
1268
|
"""
|
|
844
|
-
Represents a text object
|
|
845
|
-
|
|
1269
|
+
Represents a text object with additional properties and optional hierarchy.
|
|
1270
|
+
|
|
1271
|
+
Parameters (typically provided by the server):
|
|
1272
|
+
- internal_id: Identifier of the text object.
|
|
1273
|
+
- position: Position of the text object.
|
|
1274
|
+
- object_type: `ObjectType.TEXT_LINE` or another text-related type.
|
|
1275
|
+
- text: Text content, when available.
|
|
1276
|
+
- font_name: Name of the font used for the text.
|
|
1277
|
+
- font_size: Size of the font in points.
|
|
1278
|
+
- line_spacings: Optional list of line spacing values for multi-line objects.
|
|
1279
|
+
- color: Text color.
|
|
1280
|
+
- status: `TextStatus` providing modification/encoding info.
|
|
1281
|
+
|
|
1282
|
+
Usage:
|
|
1283
|
+
- Instances are returned by find/snapshot APIs. You generally should not instantiate
|
|
1284
|
+
them manually, but you may read their properties or pass their `ObjectRef`-like
|
|
1285
|
+
identity to modification requests (e.g., `ModifyTextRequest`).
|
|
846
1286
|
"""
|
|
847
1287
|
|
|
848
1288
|
def __init__(self, internal_id: str, position: Position, object_type: ObjectType,
|
|
@@ -890,8 +1330,18 @@ class TextObjectRef(ObjectRef):
|
|
|
890
1330
|
@dataclass
|
|
891
1331
|
class PageRef(ObjectRef):
|
|
892
1332
|
"""
|
|
893
|
-
|
|
894
|
-
|
|
1333
|
+
Reference to a page with size and orientation metadata.
|
|
1334
|
+
|
|
1335
|
+
Parameters (usually provided by the server):
|
|
1336
|
+
- internal_id: Identifier of the page object.
|
|
1337
|
+
- position: Position referencing the page (often via `Position.at_page(page_index)`).
|
|
1338
|
+
- type: Should be `ObjectType.PAGE`.
|
|
1339
|
+
- page_size: `PageSize` of the page.
|
|
1340
|
+
- orientation: `Orientation.PORTRAIT` or `Orientation.LANDSCAPE`.
|
|
1341
|
+
|
|
1342
|
+
Usage:
|
|
1343
|
+
- Returned inside `PageSnapshot` objects. You can inspect page size/orientation
|
|
1344
|
+
and use the page index for subsequent operations.
|
|
895
1345
|
"""
|
|
896
1346
|
page_size: Optional[PageSize]
|
|
897
1347
|
orientation: Optional[Orientation]
|
|
@@ -908,7 +1358,22 @@ class PageRef(ObjectRef):
|
|
|
908
1358
|
@dataclass
|
|
909
1359
|
class CommandResult:
|
|
910
1360
|
"""
|
|
911
|
-
|
|
1361
|
+
Outcome returned by certain API endpoints.
|
|
1362
|
+
|
|
1363
|
+
Parameters:
|
|
1364
|
+
- command_name: Name of the executed command on the server.
|
|
1365
|
+
- element_id: Optional related element ID (when applicable).
|
|
1366
|
+
- message: Informational message or error description.
|
|
1367
|
+
- success: Whether the command succeeded.
|
|
1368
|
+
- warning: Optional warning details.
|
|
1369
|
+
|
|
1370
|
+
Example:
|
|
1371
|
+
```python
|
|
1372
|
+
# Parse from a server JSON response dict
|
|
1373
|
+
result = CommandResult.from_dict(resp_json)
|
|
1374
|
+
if not result.success:
|
|
1375
|
+
print("Operation failed:", result.message)
|
|
1376
|
+
```
|
|
912
1377
|
"""
|
|
913
1378
|
command_name: str
|
|
914
1379
|
element_id: str | None
|
|
@@ -936,6 +1401,14 @@ class CommandResult:
|
|
|
936
1401
|
class PageSnapshot:
|
|
937
1402
|
"""
|
|
938
1403
|
Snapshot of a single page containing all elements and page metadata.
|
|
1404
|
+
|
|
1405
|
+
Parameters (provided by the server):
|
|
1406
|
+
- page_ref: `PageRef` describing the page (size, orientation, etc.).
|
|
1407
|
+
- elements: List of `ObjectRef` (and subclasses) present on the page.
|
|
1408
|
+
|
|
1409
|
+
Usage:
|
|
1410
|
+
- Iterate over `elements` to find items to modify or move.
|
|
1411
|
+
- Use `page_ref.position.page_index` as the page index for follow-up operations.
|
|
939
1412
|
"""
|
|
940
1413
|
page_ref: PageRef
|
|
941
1414
|
elements: List[ObjectRef]
|
|
@@ -952,7 +1425,21 @@ class PageSnapshot:
|
|
|
952
1425
|
@dataclass
|
|
953
1426
|
class DocumentSnapshot:
|
|
954
1427
|
"""
|
|
955
|
-
Snapshot of
|
|
1428
|
+
Snapshot of a document including pages and fonts used.
|
|
1429
|
+
|
|
1430
|
+
Parameters (provided by the server):
|
|
1431
|
+
- page_count: Number of pages in the document.
|
|
1432
|
+
- fonts: List of `FontRecommendation` entries summarizing fonts in the document.
|
|
1433
|
+
- pages: Ordered list of `PageSnapshot` objects, one per page.
|
|
1434
|
+
|
|
1435
|
+
Usage:
|
|
1436
|
+
```python
|
|
1437
|
+
# Iterate pages and elements
|
|
1438
|
+
for page in snapshot.pages:
|
|
1439
|
+
for el in page.elements:
|
|
1440
|
+
if isinstance(el, TextObjectRef) and el.get_text():
|
|
1441
|
+
print(el.get_text())
|
|
1442
|
+
```
|
|
956
1443
|
"""
|
|
957
1444
|
page_count: int
|
|
958
1445
|
fonts: List[FontRecommendation]
|