pdfdancer-client-python 0.3.12__py3-none-any.whl → 0.3.13__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 CHANGED
@@ -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.12"
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",
pdfdancer/models.py CHANGED
@@ -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
pdfdancer/pdfdancer_v1.py CHANGED
@@ -47,6 +47,7 @@ from .models import (
47
47
  FontType,
48
48
  FormFieldRef,
49
49
  Image,
50
+ ModifyPathRequest,
50
51
  ModifyRequest,
51
52
  ModifyTextRequest,
52
53
  MoveRequest,
@@ -58,6 +59,7 @@ from .models import (
58
59
  PageSize,
59
60
  PageSnapshot,
60
61
  Paragraph,
62
+ PathObjectRef,
61
63
  Position,
62
64
  PositionMode,
63
65
  RedactRequest,
@@ -2755,6 +2757,38 @@ class PDFDancer:
2755
2757
  self._invalidate_snapshots()
2756
2758
  return result
2757
2759
 
2760
+ def _modify_path(
2761
+ self,
2762
+ object_ref: ObjectRef,
2763
+ stroke_color: Optional[Color],
2764
+ fill_color: Optional[Color],
2765
+ ) -> CommandResult:
2766
+ """
2767
+ Modifies the stroke and fill colors of a path object.
2768
+
2769
+ Args:
2770
+ object_ref: Reference to the path to modify.
2771
+ stroke_color: New stroke color (None means don't change).
2772
+ fill_color: New fill color (None means don't change).
2773
+
2774
+ Returns:
2775
+ CommandResult indicating success or failure.
2776
+ """
2777
+ if object_ref is None:
2778
+ raise ValidationException("Object reference cannot be null")
2779
+
2780
+ request_data = ModifyPathRequest(
2781
+ object_ref=object_ref,
2782
+ stroke_color=stroke_color,
2783
+ fill_color=fill_color,
2784
+ ).to_dict()
2785
+ response = self._make_request("PUT", "/pdf/modify/path", data=request_data)
2786
+ result = CommandResult.from_dict(response.json())
2787
+
2788
+ # Invalidate snapshot caches after mutation
2789
+ self._invalidate_snapshots()
2790
+ return result
2791
+
2758
2792
  # Font Operations
2759
2793
 
2760
2794
  def find_fonts(self, font_name: str, font_size: int) -> List[Font]:
@@ -3159,6 +3193,43 @@ class PDFDancer:
3159
3193
  value=obj_data["value"] if "value" in obj_data else None,
3160
3194
  )
3161
3195
 
3196
+ def _parse_path_object_ref(self, obj_data: dict) -> PathObjectRef:
3197
+ """Parse JSON object data into PathObjectRef instance with color information."""
3198
+ position_data = obj_data.get("position", {})
3199
+ position = self._parse_position(position_data) if position_data else None
3200
+
3201
+ object_type = ObjectType(obj_data["type"])
3202
+
3203
+ # Parse stroke color if present
3204
+ stroke_color = None
3205
+ stroke_color_data = obj_data.get("strokeColor")
3206
+ if isinstance(stroke_color_data, dict):
3207
+ red = stroke_color_data.get("red")
3208
+ green = stroke_color_data.get("green")
3209
+ blue = stroke_color_data.get("blue")
3210
+ alpha = stroke_color_data.get("alpha", 255)
3211
+ if all(isinstance(v, int) for v in [red, green, blue]):
3212
+ stroke_color = Color(red, green, blue, alpha)
3213
+
3214
+ # Parse fill color if present
3215
+ fill_color = None
3216
+ fill_color_data = obj_data.get("fillColor")
3217
+ if isinstance(fill_color_data, dict):
3218
+ red = fill_color_data.get("red")
3219
+ green = fill_color_data.get("green")
3220
+ blue = fill_color_data.get("blue")
3221
+ alpha = fill_color_data.get("alpha", 255)
3222
+ if all(isinstance(v, int) for v in [red, green, blue]):
3223
+ fill_color = Color(red, green, blue, alpha)
3224
+
3225
+ return PathObjectRef(
3226
+ internal_id=obj_data["internalId"] if "internalId" in obj_data else None,
3227
+ position=position,
3228
+ object_type=object_type,
3229
+ stroke_color=stroke_color,
3230
+ fill_color=fill_color,
3231
+ )
3232
+
3162
3233
  @staticmethod
3163
3234
  def _parse_position(pos_data: dict) -> Position:
3164
3235
  """Parse JSON position data into Position instance."""
@@ -3473,6 +3544,9 @@ class PDFDancer:
3473
3544
  ):
3474
3545
  # Parse as FormFieldRef to capture name and value
3475
3546
  elements.append(self._parse_form_field_ref(elem_data))
3547
+ elif elem_type == ObjectType.PATH:
3548
+ # Parse as PathObjectRef to capture stroke/fill colors
3549
+ elements.append(self._parse_path_object_ref(elem_data))
3476
3550
  else:
3477
3551
  # Parse as basic ObjectRef
3478
3552
  elements.append(self._parse_object_ref(elem_data))
@@ -3526,9 +3600,7 @@ class PDFDancer:
3526
3600
  self._client.close()
3527
3601
 
3528
3602
  def _to_path_objects(self, refs: List[ObjectRef]) -> List[PathObject]:
3529
- return [
3530
- PathObject(self, ref.internal_id, ref.type, ref.position) for ref in refs
3531
- ]
3603
+ return [PathObject(self, ref) for ref in refs]
3532
3604
 
3533
3605
  def _to_paragraph_objects(self, refs: List[TextObjectRef]) -> List[ParagraphObject]:
3534
3606
  return [ParagraphObject(self, ref) for ref in refs]
pdfdancer/types.py CHANGED
@@ -4,7 +4,7 @@ import sys
4
4
  from dataclasses import dataclass
5
5
  from typing import TYPE_CHECKING, Optional
6
6
 
7
- from . import FormFieldRef, ObjectRef, ObjectType, Point, Position, TextObjectRef
7
+ from . import FormFieldRef, ObjectRef, ObjectType, PathObjectRef, Point, Position, TextObjectRef
8
8
  from .exceptions import ValidationException
9
9
 
10
10
  if TYPE_CHECKING:
@@ -86,11 +86,44 @@ class PDFObjectBase:
86
86
  class PathObject(PDFObjectBase):
87
87
  """Represents a vector path object inside a PDF page."""
88
88
 
89
+ def __init__(self, client: "PDFDancer", object_ref):
90
+ """
91
+ Initialize a PathObject.
92
+
93
+ Args:
94
+ client: PDFDancer client instance
95
+ object_ref: ObjectRef or PathObjectRef with path data
96
+ """
97
+ super().__init__(
98
+ client, object_ref.internal_id, object_ref.type, object_ref.position
99
+ )
100
+ self._object_ref = object_ref
101
+
89
102
  @property
90
103
  def bounding_box(self) -> Optional[BoundingRect]:
91
104
  """Optional bounding rectangle (if available)."""
92
105
  return self.position.bounding_rect
93
106
 
107
+ def edit(self) -> PathEditSession:
108
+ """Start a fluent editing session to modify path colors."""
109
+ return PathEditSession(self._client, self.object_ref())
110
+
111
+ def object_ref(self):
112
+ """Return an ObjectRef for this path."""
113
+ return self._object_ref
114
+
115
+ def get_stroke_color(self) -> Optional["Color"]:
116
+ """Get the stroke/outline color of the path, or None if not set."""
117
+ if isinstance(self._object_ref, PathObjectRef):
118
+ return self._object_ref.get_stroke_color()
119
+ return None
120
+
121
+ def get_fill_color(self) -> Optional["Color"]:
122
+ """Get the fill color of the path, or None if not set."""
123
+ if isinstance(self._object_ref, PathObjectRef):
124
+ return self._object_ref.get_fill_color()
125
+ return None
126
+
94
127
  def __eq__(self, other):
95
128
  if not isinstance(other, PathObject):
96
129
  return False
@@ -748,3 +781,61 @@ class FormFieldObject(PDFObjectBase):
748
781
  and self.name == other.name
749
782
  and self.value == other.value
750
783
  )
784
+
785
+
786
+ class PathEditSession:
787
+ """
788
+ Fluent editing helper for modifying path stroke and fill colors.
789
+ """
790
+
791
+ def __init__(self, client: "PDFDancer", object_ref):
792
+ self._client = client
793
+ self._object_ref = object_ref
794
+ self._stroke_color = None
795
+ self._fill_color = None
796
+
797
+ def __enter__(self):
798
+ return self
799
+
800
+ def __exit__(self, exc_type, exc_val, exc_tb):
801
+ if exc_type:
802
+ return False
803
+ self.apply()
804
+ return False
805
+
806
+ def stroke_color(self, color) -> "PathEditSession":
807
+ """
808
+ Set the stroke/outline color.
809
+
810
+ Args:
811
+ color: The stroke color (Color object)
812
+
813
+ Returns:
814
+ Self for method chaining
815
+ """
816
+ self._stroke_color = color
817
+ return self
818
+
819
+ def fill_color(self, color) -> "PathEditSession":
820
+ """
821
+ Set the fill color.
822
+
823
+ Args:
824
+ color: The fill color (Color object)
825
+
826
+ Returns:
827
+ Self for method chaining
828
+ """
829
+ self._fill_color = color
830
+ return self
831
+
832
+ def apply(self):
833
+ """
834
+ Apply the color modifications to the path.
835
+
836
+ Returns:
837
+ CommandResult indicating success or failure
838
+ """
839
+ return self._client._modify_path(
840
+ self._object_ref, self._stroke_color, self._fill_color
841
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pdfdancer-client-python
3
- Version: 0.3.12
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:
@@ -1,17 +1,17 @@
1
- pdfdancer/__init__.py,sha256=DiYQ2gDQ4kYu8eUXc7Z-0vgdoeBi0Cek5jusrq0LYVE,2884
1
+ pdfdancer/__init__.py,sha256=3gVfEG-Fgz2Zes2zS6QfWhE2_TbtSFvDeyc_EGD1azE,2972
2
2
  pdfdancer/exceptions.py,sha256=U6kD3NvcdNt05_OU11Tml5dXGtlodEtYC9zcV4fHXkc,2270
3
3
  pdfdancer/fingerprint.py,sha256=eL3PHPgv-knMya7s95RXg3qzzpkAA1aevxqb6tuOb34,3061
4
4
  pdfdancer/image_builder.py,sha256=aBxMFAMFzzbGTjlVH0hi94mA81cH8tp37Pk84HRPV00,1892
5
- pdfdancer/models.py,sha256=nrR8ssEIz7BttWCMv7F0oriTego5Jh-h-34GvJmoni8,60462
5
+ pdfdancer/models.py,sha256=D8v42xWAqXQ7-q3gjpb5z5Gd3dxvjc0NE779Gd2-fBE,62964
6
6
  pdfdancer/page_builder.py,sha256=ARWLRtlrLGbES-0nbiigTOsRVmodRX0DNK8YIAkA9Ig,3850
7
7
  pdfdancer/paragraph_builder.py,sha256=OmhzYazMnq0n0rhrjWcKbo0LQfEC7BZoiLB29ycF630,20504
8
8
  pdfdancer/path_builder.py,sha256=safKb_IeHRWlQbyBTIXfcoBfXxUZhuzYvBIob5Tbp-8,23938
9
- pdfdancer/pdfdancer_v1.py,sha256=JaiB47v_zgUwb7OtnmnVDt4jroxB5Bh6sGQpAU8ZIBg,138359
9
+ pdfdancer/pdfdancer_v1.py,sha256=u7LmwbVJ9szcS31aFYtSXo595nVZUP9RgTq1iskgzKI,141221
10
10
  pdfdancer/text_line_builder.py,sha256=8jYknV4hw0fyzwX0OI_oLvnh5aMmCV3jXaVkmYLu6MQ,10273
11
- pdfdancer/types.py,sha256=w0dJ6JcHgm62BFDzZTx7v10GymjNaZw8x8lKfg-U2zA,24949
12
- pdfdancer_client_python-0.3.12.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
13
- pdfdancer_client_python-0.3.12.dist-info/licenses/NOTICE,sha256=xaC4l-IChAmtViNDie8ZWzUk0O6XRMyzOl0zLmVZ2HE,232
14
- pdfdancer_client_python-0.3.12.dist-info/METADATA,sha256=8uO4RQWyE64otUm9cY_Vj9gZsBH5-JpajgDcvijv_JM,28756
15
- pdfdancer_client_python-0.3.12.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
16
- pdfdancer_client_python-0.3.12.dist-info/top_level.txt,sha256=ICwSVRpcCKrdBF9QlaX9Y0e_N3Nk1p7QVxadGOnbxeY,10
17
- pdfdancer_client_python-0.3.12.dist-info/RECORD,,
11
+ pdfdancer/types.py,sha256=GNThFde9dRloHB482nu3txhYc9wysI244cM1D7L8fa4,27501
12
+ pdfdancer_client_python-0.3.13.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
13
+ pdfdancer_client_python-0.3.13.dist-info/licenses/NOTICE,sha256=xaC4l-IChAmtViNDie8ZWzUk0O6XRMyzOl0zLmVZ2HE,232
14
+ pdfdancer_client_python-0.3.13.dist-info/METADATA,sha256=xMyuh8uwwfxel2KotjXORNIpB-fxPmFaXMZjsWRXd6k,28756
15
+ pdfdancer_client_python-0.3.13.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
16
+ pdfdancer_client_python-0.3.13.dist-info/top_level.txt,sha256=ICwSVRpcCKrdBF9QlaX9Y0e_N3Nk1p7QVxadGOnbxeY,10
17
+ pdfdancer_client_python-0.3.13.dist-info/RECORD,,