pdfdancer-client-python 0.2.18__py3-none-any.whl → 0.2.20__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 CHANGED
@@ -16,12 +16,14 @@ from .models import (
16
16
  FontType, PathSegment, Line, Bezier, Path
17
17
  )
18
18
  from .paragraph_builder import ParagraphBuilder
19
+ from .page_builder import PageBuilder
19
20
  from .path_builder import PathBuilder, LineBuilder, BezierBuilder
20
21
 
21
22
  __version__ = "1.0.0"
22
23
  __all__ = [
23
24
  "PDFDancer",
24
25
  "ParagraphBuilder",
26
+ "PageBuilder",
25
27
  "PathBuilder",
26
28
  "LineBuilder",
27
29
  "BezierBuilder",
@@ -27,4 +27,34 @@ class ImageBuilder:
27
27
  return self
28
28
 
29
29
  def add(self) -> bool:
30
+ # noinspection PyProtectedMember
31
+ return self._client._add_image(self._image, self._image.position)
32
+
33
+
34
+ class ImageOnPageBuilder:
35
+
36
+ def __init__(self, client: 'PDFDancer', page_index: int):
37
+ """
38
+ Initialize the image builder with a client reference.
39
+
40
+ Args:
41
+ client: The PDFDancer instance for font registration
42
+ """
43
+ if client is None:
44
+ raise ValidationException("Client cannot be null")
45
+
46
+ self._client = client
47
+ self._image = Image()
48
+ self._page_index = page_index
49
+
50
+ def from_file(self, img_path: Path) -> 'ImageOnPageBuilder':
51
+ self._image.data = img_path.read_bytes()
52
+ return self
53
+
54
+ def at(self, x, y) -> 'ImageOnPageBuilder':
55
+ self._image.position = Position.at_page_coordinates(self._page_index, x, y)
56
+ return self
57
+
58
+ def add(self) -> bool:
59
+ # noinspection PyProtectedMember
30
60
  return self._client._add_image(self._image, self._image.position)
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"
@@ -215,7 +245,39 @@ class BoundingRect:
215
245
  @dataclass
216
246
  class Position:
217
247
  """
218
- Represents spatial positioning and location information for PDF objects.
248
+ Spatial locator used to find or place objects on a page.
249
+
250
+ Parameters:
251
+ - page_index: Zero-based page index this position refers to. Required for most operations
252
+ that place or search on a specific page; use `Position.at_page()` as a shortcut.
253
+ - shape: Optional geometric shape used when matching by area (`POINT`, `LINE`, `CIRCLE`, `RECT`).
254
+ - mode: How to match objects relative to the shape (`INTERSECT` or `CONTAINS`).
255
+ - bounding_rect: Rectangle describing the area or point (for `POINT`, width/height are 0).
256
+ - text_starts_with: Filter for text objects whose content starts with this string.
257
+ - text_pattern: Regex pattern to match text content.
258
+ - name: Named anchor or element name to target (e.g. form field name).
259
+
260
+ Builder helpers:
261
+ - `Position.at_page(page_index)` – target a whole page.
262
+ - `Position.at_page_coordinates(page_index, x, y)` – target a point on a page.
263
+ - `Position.by_name(name)` – target object(s) by name.
264
+ - `pos.at_coordinates(Point(x, y))` – switch to a point on the current page.
265
+ - `pos.move_x(dx)`, `pos.move_y(dy)` – offset the current coordinates.
266
+
267
+ Examples:
268
+ ```python
269
+ # A point on page 0
270
+ pos = Position.at_page_coordinates(0, x=72, y=720)
271
+
272
+ # Search by name (e.g. a form field) and then move down 12 points
273
+ pos = Position.by_name("Email").move_y(-12)
274
+
275
+ # Match anything intersecting a rectangular area on page 1
276
+ pos = Position.at_page(1)
277
+ pos.shape = ShapeType.RECT
278
+ pos.mode = PositionMode.INTERSECT
279
+ pos.bounding_rect = BoundingRect(x=100, y=100, width=200, height=50)
280
+ ```
219
281
  """
220
282
  page_index: Optional[int] = None
221
283
  shape: Optional[ShapeType] = None
@@ -287,7 +349,24 @@ class Position:
287
349
  @dataclass
288
350
  class ObjectRef:
289
351
  """
290
- Lightweight reference to a PDF object providing identity and type information.
352
+ Reference to an object in a PDF document returned by the server.
353
+
354
+ Parameters:
355
+ - internal_id: Server-side identifier for the object.
356
+ - position: Position information describing where the object is.
357
+ - type: Object type (see `ObjectType`).
358
+
359
+ Usage:
360
+ - Instances are typically returned in snapshots or find results.
361
+ - Pass an `ObjectRef` to request objects such as `MoveRequest`, `DeleteRequest`,
362
+ `ModifyRequest`, or `ModifyTextRequest`.
363
+
364
+ Example:
365
+ ```python
366
+ # Move an object to a new position
367
+ new_pos = Position.at_page_coordinates(0, 120, 500)
368
+ payload = MoveRequest(object_ref=obj_ref, position=new_pos).to_dict()
369
+ ```
291
370
  """
292
371
  internal_id: str
293
372
  position: Position
@@ -325,7 +404,23 @@ class ObjectRef:
325
404
 
326
405
  @dataclass
327
406
  class Color:
328
- """Represents an RGB color with optional alpha channel, values from 0-255."""
407
+ """RGB color with optional alpha channel.
408
+
409
+ Parameters:
410
+ - r: Red component (0-255)
411
+ - g: Green component (0-255)
412
+ - b: Blue component (0-255)
413
+ - a: Alpha component (0-255), default 255 (opaque)
414
+
415
+ Raises:
416
+ - ValueError: If any component is outside 0-255.
417
+
418
+ Example:
419
+ ```python
420
+ red = Color(255, 0, 0)
421
+ semi_transparent_black = Color(0, 0, 0, a=128)
422
+ ```
423
+ """
329
424
  r: int
330
425
  g: int
331
426
  b: int
@@ -339,7 +434,23 @@ class Color:
339
434
 
340
435
  @dataclass
341
436
  class Font:
342
- """Represents a font with name and size."""
437
+ """Font face and size.
438
+
439
+ Parameters:
440
+ - name: Font family name. Can be one of `StandardFonts` values or any embedded font name.
441
+ - size: Font size in points (> 0).
442
+
443
+ Raises:
444
+ - ValueError: If `size` is not positive.
445
+
446
+ Example:
447
+ ```python
448
+ from pdfdancer.models import Font, StandardFonts
449
+
450
+ title_font = Font(name=StandardFonts.HELVETICA_BOLD.value, size=16)
451
+ body_font = Font(name="MyEmbeddedFont", size=10.5)
452
+ ```
453
+ """
343
454
  name: str
344
455
  size: float
345
456
 
@@ -351,10 +462,18 @@ class Font:
351
462
  @dataclass
352
463
  class PathSegment:
353
464
  """
354
- Abstract base class for individual path segments within vector paths.
355
- This class provides common properties for path elements including stroke and fill colors,
356
- line width, and positioning. Concrete subclasses implement specific geometric shapes
357
- like lines, curves, and bezier segments.
465
+ Base class for vector path segments.
466
+
467
+ Parameters:
468
+ - stroke_color: Outline color for the segment (`Color`).
469
+ - fill_color: Fill color for closed shapes when applicable (`Color`).
470
+ - stroke_width: Line width in points.
471
+ - dash_array: Dash pattern (e.g. `[3, 2]` for 3 on, 2 off). None or empty for solid.
472
+ - dash_phase: Offset into the dash pattern.
473
+
474
+ Notes:
475
+ - Concrete subclasses are `Line` and `Bezier`.
476
+ - Used inside `Path.path_segments` and serialized by `AddRequest`.
358
477
  """
359
478
  stroke_color: Optional[Color] = None
360
479
  fill_color: Optional[Color] = None
@@ -386,9 +505,19 @@ class PathSegment:
386
505
  @dataclass
387
506
  class Line(PathSegment):
388
507
  """
389
- Represents a straight line path segment between two points.
390
- This class defines a linear path element connecting two coordinate points,
391
- commonly used in vector graphics and geometric shapes within PDF documents.
508
+ Straight line segment between two points.
509
+
510
+ Parameters:
511
+ - p0: Start point.
512
+ - p1: End point.
513
+
514
+ Example:
515
+ ```python
516
+ from pdfdancer.models import Line, Point, Path
517
+
518
+ line = Line(p0=Point(10, 10), p1=Point(100, 10))
519
+ path = Path(path_segments=[line])
520
+ ```
392
521
  """
393
522
  p0: Optional[Point] = None
394
523
  p1: Optional[Point] = None
@@ -405,9 +534,20 @@ class Line(PathSegment):
405
534
  @dataclass
406
535
  class Bezier(PathSegment):
407
536
  """
408
- Represents a cubic Bezier curve path segment defined by four control points.
409
- This class implements a cubic Bezier curve with start point, two control points,
410
- and end point, providing smooth curved path segments for complex vector graphics.
537
+ Cubic Bezier curve segment defined by 4 points.
538
+
539
+ Parameters:
540
+ - p0: Start point.
541
+ - p1: First control point.
542
+ - p2: Second control point.
543
+ - p3: End point.
544
+
545
+ Example:
546
+ ```python
547
+ curve = Bezier(
548
+ p0=Point(10, 10), p1=Point(50, 80), p2=Point(80, 50), p3=Point(120, 10)
549
+ )
550
+ ```
411
551
  """
412
552
  p0: Optional[Point] = None
413
553
  p1: Optional[Point] = None
@@ -434,9 +574,28 @@ class Bezier(PathSegment):
434
574
  @dataclass
435
575
  class Path:
436
576
  """
437
- Represents a complex vector path consisting of multiple path segments.
438
- This class encapsulates vector graphics data within PDF documents, composed of
439
- various path elements like lines, curves, and shapes.
577
+ Vector path composed of one or more `PathSegment`s.
578
+
579
+ Parameters:
580
+ - position: Where to place the path on the page.
581
+ - path_segments: List of `Line` and/or `Bezier` segments.
582
+ - even_odd_fill: If True, use even-odd rule for fills; otherwise nonzero winding.
583
+
584
+ Example (adding a triangle to a page):
585
+ ```python
586
+ from pdfdancer.models import Path, Line, Point, Position, AddRequest
587
+
588
+ tri = Path(
589
+ position=Position.at_page_coordinates(0, 100, 100),
590
+ path_segments=[
591
+ Line(Point(0, 0), Point(50, 100)),
592
+ Line(Point(50, 100), Point(100, 0)),
593
+ Line(Point(100, 0), Point(0, 0)),
594
+ ],
595
+ even_odd_fill=True,
596
+ )
597
+ payload = AddRequest(tri).to_dict()
598
+ ```
440
599
  """
441
600
  position: Optional[Position] = None
442
601
  path_segments: Optional[List[PathSegment]] = None
@@ -462,7 +621,28 @@ class Path:
462
621
  @dataclass
463
622
  class Image:
464
623
  """
465
- Represents an image object in a PDF document.
624
+ Raster image to be placed on a page.
625
+
626
+ Parameters:
627
+ - position: Where to place the image. Use `Position.at_page_coordinates(page, x, y)`.
628
+ - format: Image format hint for the server (e.g. "PNG", "JPEG"). Optional.
629
+ - width: Target width in points. Optional; server may infer from data.
630
+ - height: Target height in points. Optional; server may infer from data.
631
+ - data: Raw image bytes. If provided, it will be base64-encoded in `AddRequest.to_dict()`.
632
+
633
+ Example:
634
+ ```python
635
+ from pdfdancer.models import Image, Position, AddRequest
636
+
637
+ img = Image(
638
+ position=Position.at_page_coordinates(0, 72, 600),
639
+ format="PNG",
640
+ width=128,
641
+ height=64,
642
+ data=open("/path/logo.png", "rb").read(),
643
+ )
644
+ payload = AddRequest(img).to_dict()
645
+ ```
466
646
  """
467
647
  position: Optional[Position] = None
468
648
  format: Optional[str] = None
@@ -482,7 +662,29 @@ class Image:
482
662
  @dataclass
483
663
  class Paragraph:
484
664
  """
485
- Represents a paragraph of text in a PDF document.
665
+ Multi-line text paragraph to add to a page.
666
+
667
+ Parameters:
668
+ - position: Anchor position where the first line begins.
669
+ - text_lines: List of strings, one per line. Use `\n` within a string only if desired; normally
670
+ provide separate entries for multiple lines.
671
+ - font: Font to use for all text elements unless overridden later.
672
+ - color: Text color.
673
+ - line_spacing: Distance multiplier between lines. Server expects a list, handled for you by `AddRequest`.
674
+
675
+ Example:
676
+ ```python
677
+ from pdfdancer.models import Paragraph, Position, Font, Color, StandardFonts, AddRequest
678
+
679
+ para = Paragraph(
680
+ position=Position.at_page_coordinates(0, 72, 700),
681
+ text_lines=["Hello", "PDFDancer!"],
682
+ font=Font(StandardFonts.HELVETICA.value, 12),
683
+ color=Color(50, 50, 50),
684
+ line_spacing=1.4,
685
+ )
686
+ payload = AddRequest(para).to_dict()
687
+ ```
486
688
  """
487
689
  position: Optional[Position] = None
488
690
  text_lines: Optional[List[str]] = None
@@ -502,7 +704,22 @@ class Paragraph:
502
704
  # Request classes for API communication
503
705
  @dataclass
504
706
  class FindRequest:
505
- """Request object for find operations."""
707
+ """Request for locating objects.
708
+
709
+ Parameters:
710
+ - object_type: Filter by `ObjectType` (optional). If None, all types may be returned.
711
+ - position: `Position` describing where/how to search.
712
+ - hint: Optional backend hint or free-form note to influence matching.
713
+
714
+ Usage:
715
+ ```python
716
+ req = FindRequest(
717
+ object_type=ObjectType.TEXT_LINE,
718
+ position=Position.at_page_coordinates(0, 72, 700).with_text_starts("Hello"),
719
+ )
720
+ payload = req.to_dict()
721
+ ```
722
+ """
506
723
  object_type: Optional[ObjectType]
507
724
  position: Optional[Position]
508
725
  hint: Optional[str] = None
@@ -541,7 +758,16 @@ class FindRequest:
541
758
 
542
759
  @dataclass
543
760
  class DeleteRequest:
544
- """Request object for delete operations."""
761
+ """Request to delete an existing object.
762
+
763
+ Parameters:
764
+ - object_ref: The object to delete.
765
+
766
+ Example:
767
+ ```python
768
+ payload = DeleteRequest(object_ref=obj_ref).to_dict()
769
+ ```
770
+ """
545
771
  object_ref: ObjectRef
546
772
 
547
773
  def to_dict(self) -> dict:
@@ -554,7 +780,19 @@ class DeleteRequest:
554
780
 
555
781
  @dataclass
556
782
  class MoveRequest:
557
- """Request object for move operations."""
783
+ """Request to move an existing object to a new position.
784
+
785
+ Parameters:
786
+ - object_ref: The object to move (obtained from a snapshot or find call).
787
+ - position: The new target `Position` (commonly a point created with `Position.at_page_coordinates`).
788
+
789
+ Example:
790
+ ```python
791
+ new_pos = Position.at_page_coordinates(0, 200, 500)
792
+ req = MoveRequest(object_ref=obj_ref, position=new_pos)
793
+ payload = req.to_dict()
794
+ ```
795
+ """
558
796
  object_ref: ObjectRef
559
797
  position: Position
560
798
 
@@ -570,7 +808,19 @@ class MoveRequest:
570
808
 
571
809
  @dataclass
572
810
  class PageMoveRequest:
573
- """Request object for moving pages within the document."""
811
+ """Request to reorder pages.
812
+
813
+ Parameters:
814
+ - from_page_index: Zero-based index of the page to move.
815
+ - to_page_index: Zero-based destination index.
816
+
817
+ Example:
818
+ ```python
819
+ # Move first page to the end
820
+ req = PageMoveRequest(from_page_index=0, to_page_index=doc_page_count - 1)
821
+ payload = req.to_dict()
822
+ ```
823
+ """
574
824
  from_page_index: int
575
825
  to_page_index: int
576
826
 
@@ -581,9 +831,60 @@ class PageMoveRequest:
581
831
  }
582
832
 
583
833
 
834
+ @dataclass
835
+ class AddPageRequest:
836
+ """Request to add a new page to the document.
837
+
838
+ Parameters:
839
+ - page_index: Optional zero-based index where the new page should be inserted.
840
+ - orientation: Optional page orientation (portrait or landscape).
841
+ - page_size: Optional size of the page.
842
+
843
+ Only populated fields are sent to the server to maintain backward compatibility
844
+ with default server behavior.
845
+ """
846
+ page_index: Optional[int] = None
847
+ orientation: Optional[Orientation] = None
848
+ page_size: Optional[PageSize] = None
849
+
850
+ def to_dict(self) -> dict:
851
+ payload: Dict[str, Any] = {}
852
+ if self.page_index is not None:
853
+ payload["pageIndex"] = int(self.page_index)
854
+ if self.orientation is not None:
855
+ orientation_value: Orientation
856
+ if isinstance(self.orientation, Orientation):
857
+ orientation_value = self.orientation
858
+ elif isinstance(self.orientation, str):
859
+ normalized = self.orientation.strip().upper()
860
+ orientation_value = Orientation(normalized)
861
+ else:
862
+ raise TypeError("Orientation must be an Orientation enum or string value")
863
+ payload["orientation"] = orientation_value.value
864
+ if self.page_size is not None:
865
+ page_size = PageSize.coerce(self.page_size)
866
+ payload["pageSize"] = page_size.to_dict()
867
+ return payload
868
+
869
+
584
870
  @dataclass
585
871
  class AddRequest:
586
- """Request object for add operations."""
872
+ """Request to add a new object to the document.
873
+
874
+ Parameters:
875
+ - pdf_object: The object to add (e.g. `Image`, `Paragraph`, or `Path`).
876
+
877
+ Usage:
878
+ ```python
879
+ para = Paragraph(position=Position.at_page_coordinates(0, 72, 700), text_lines=["Hello"])
880
+ req = AddRequest(para)
881
+ payload = req.to_dict() # ready to send to the server API
882
+ ```
883
+
884
+ Notes:
885
+ - Serialization details (like base64 for image `data`, or per-segment position for paths)
886
+ are handled for you in `to_dict()`.
887
+ """
587
888
  pdf_object: Any # Can be Image, Paragraph, etc.
588
889
 
589
890
  def to_dict(self) -> dict:
@@ -727,7 +1028,19 @@ class AddRequest:
727
1028
 
728
1029
  @dataclass
729
1030
  class ModifyRequest:
730
- """Request object for modify operations."""
1031
+ """Request to replace an object with a new one of possibly different type.
1032
+
1033
+ Parameters:
1034
+ - object_ref: The existing object to replace.
1035
+ - new_object: The replacement object (e.g. `Paragraph`, `Image`, or `Path`).
1036
+
1037
+ Example:
1038
+ ```python
1039
+ new_para = Paragraph(position=old.position, text_lines=["Updated text"])
1040
+ req = ModifyRequest(object_ref=old, new_object=new_para)
1041
+ payload = req.to_dict()
1042
+ ```
1043
+ """
731
1044
  object_ref: ObjectRef
732
1045
  new_object: Any
733
1046
 
@@ -742,7 +1055,18 @@ class ModifyRequest:
742
1055
 
743
1056
  @dataclass
744
1057
  class ModifyTextRequest:
745
- """Request object for text modification operations."""
1058
+ """Request to change the text content of a text object.
1059
+
1060
+ Parameters:
1061
+ - object_ref: The text object to modify (e.g. a `TextObjectRef`).
1062
+ - new_text: Replacement text content.
1063
+
1064
+ Example:
1065
+ ```python
1066
+ req = ModifyTextRequest(object_ref=text_ref, new_text="Hello world")
1067
+ payload = req.to_dict()
1068
+ ```
1069
+ """
746
1070
  object_ref: ObjectRef
747
1071
  new_text: str
748
1072
 
@@ -757,6 +1081,19 @@ class ModifyTextRequest:
757
1081
 
758
1082
  @dataclass
759
1083
  class ChangeFormFieldRequest:
1084
+ """Request to set a form field's value.
1085
+
1086
+ Parameters:
1087
+ - object_ref: A `FormFieldRef` (or generic `ObjectRef`) identifying the field.
1088
+ - value: The new value as a string. For checkboxes/radio buttons, use the
1089
+ appropriate on/off/selection string per the document's field options.
1090
+
1091
+ Example:
1092
+ ```python
1093
+ req = ChangeFormFieldRequest(object_ref=field_ref, value="Jane Doe")
1094
+ payload = req.to_dict()
1095
+ ```
1096
+ """
760
1097
  object_ref: ObjectRef
761
1098
  value: str
762
1099
 
@@ -772,8 +1109,20 @@ class ChangeFormFieldRequest:
772
1109
  @dataclass
773
1110
  class FormFieldRef(ObjectRef):
774
1111
  """
775
- Represents a form field reference with additional form-specific properties.
776
- Extends ObjectRef to include form field name and value.
1112
+ Reference to a form field object with name and value.
1113
+
1114
+ Parameters (usually provided by the server):
1115
+ - internal_id: Identifier of the form field object.
1116
+ - position: Position of the field.
1117
+ - type: One of `ObjectType.TEXT_FIELD`, `ObjectType.CHECK_BOX`, etc.
1118
+ - name: Field name (as defined inside the PDF).
1119
+ - value: Current field value (string representation).
1120
+
1121
+ Usage:
1122
+ - You can pass a `FormFieldRef` to `ChangeFormFieldRequest` to update its value.
1123
+ ```python
1124
+ payload = ChangeFormFieldRequest(object_ref=field_ref, value="john@doe.com").to_dict()
1125
+ ```
777
1126
  """
778
1127
  name: Optional[str] = None
779
1128
  value: Optional[str] = None
@@ -841,8 +1190,23 @@ class TextStatus:
841
1190
 
842
1191
  class TextObjectRef(ObjectRef):
843
1192
  """
844
- Represents a text object reference with additional text-specific properties.
845
- Extends ObjectRef to include text content, font information, and hierarchical structure.
1193
+ Represents a text object with additional properties and optional hierarchy.
1194
+
1195
+ Parameters (typically provided by the server):
1196
+ - internal_id: Identifier of the text object.
1197
+ - position: Position of the text object.
1198
+ - object_type: `ObjectType.TEXT_LINE` or another text-related type.
1199
+ - text: Text content, when available.
1200
+ - font_name: Name of the font used for the text.
1201
+ - font_size: Size of the font in points.
1202
+ - line_spacings: Optional list of line spacing values for multi-line objects.
1203
+ - color: Text color.
1204
+ - status: `TextStatus` providing modification/encoding info.
1205
+
1206
+ Usage:
1207
+ - Instances are returned by find/snapshot APIs. You generally should not instantiate
1208
+ them manually, but you may read their properties or pass their `ObjectRef`-like
1209
+ identity to modification requests (e.g., `ModifyTextRequest`).
846
1210
  """
847
1211
 
848
1212
  def __init__(self, internal_id: str, position: Position, object_type: ObjectType,
@@ -890,8 +1254,18 @@ class TextObjectRef(ObjectRef):
890
1254
  @dataclass
891
1255
  class PageRef(ObjectRef):
892
1256
  """
893
- Represents a page reference with additional page-specific properties.
894
- Extends ObjectRef to include page size and orientation.
1257
+ Reference to a page with size and orientation metadata.
1258
+
1259
+ Parameters (usually provided by the server):
1260
+ - internal_id: Identifier of the page object.
1261
+ - position: Position referencing the page (often via `Position.at_page(page_index)`).
1262
+ - type: Should be `ObjectType.PAGE`.
1263
+ - page_size: `PageSize` of the page.
1264
+ - orientation: `Orientation.PORTRAIT` or `Orientation.LANDSCAPE`.
1265
+
1266
+ Usage:
1267
+ - Returned inside `PageSnapshot` objects. You can inspect page size/orientation
1268
+ and use the page index for subsequent operations.
895
1269
  """
896
1270
  page_size: Optional[PageSize]
897
1271
  orientation: Optional[Orientation]
@@ -908,7 +1282,22 @@ class PageRef(ObjectRef):
908
1282
  @dataclass
909
1283
  class CommandResult:
910
1284
  """
911
- Result object returned by certain API endpoints indicating the outcome of an operation.
1285
+ Outcome returned by certain API endpoints.
1286
+
1287
+ Parameters:
1288
+ - command_name: Name of the executed command on the server.
1289
+ - element_id: Optional related element ID (when applicable).
1290
+ - message: Informational message or error description.
1291
+ - success: Whether the command succeeded.
1292
+ - warning: Optional warning details.
1293
+
1294
+ Example:
1295
+ ```python
1296
+ # Parse from a server JSON response dict
1297
+ result = CommandResult.from_dict(resp_json)
1298
+ if not result.success:
1299
+ print("Operation failed:", result.message)
1300
+ ```
912
1301
  """
913
1302
  command_name: str
914
1303
  element_id: str | None
@@ -936,6 +1325,14 @@ class CommandResult:
936
1325
  class PageSnapshot:
937
1326
  """
938
1327
  Snapshot of a single page containing all elements and page metadata.
1328
+
1329
+ Parameters (provided by the server):
1330
+ - page_ref: `PageRef` describing the page (size, orientation, etc.).
1331
+ - elements: List of `ObjectRef` (and subclasses) present on the page.
1332
+
1333
+ Usage:
1334
+ - Iterate over `elements` to find items to modify or move.
1335
+ - Use `page_ref.position.page_index` as the page index for follow-up operations.
939
1336
  """
940
1337
  page_ref: PageRef
941
1338
  elements: List[ObjectRef]
@@ -952,7 +1349,21 @@ class PageSnapshot:
952
1349
  @dataclass
953
1350
  class DocumentSnapshot:
954
1351
  """
955
- Snapshot of the entire document containing all pages and font information.
1352
+ Snapshot of a document including pages and fonts used.
1353
+
1354
+ Parameters (provided by the server):
1355
+ - page_count: Number of pages in the document.
1356
+ - fonts: List of `FontRecommendation` entries summarizing fonts in the document.
1357
+ - pages: Ordered list of `PageSnapshot` objects, one per page.
1358
+
1359
+ Usage:
1360
+ ```python
1361
+ # Iterate pages and elements
1362
+ for page in snapshot.pages:
1363
+ for el in page.elements:
1364
+ if isinstance(el, TextObjectRef) and el.get_text():
1365
+ print(el.get_text())
1366
+ ```
956
1367
  """
957
1368
  page_count: int
958
1369
  fonts: List[FontRecommendation]
@@ -0,0 +1,92 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Optional
4
+
5
+ from .exceptions import ValidationException
6
+ from .models import Orientation, PageRef, PageSize, AddPageRequest
7
+
8
+
9
+ class PageBuilder:
10
+ """
11
+ Fluent builder for adding pages with optional orientation, size, and index.
12
+
13
+ Usage:
14
+ pdf.new_page().at_index(1).landscape().a4().add()
15
+ """
16
+
17
+ def __init__(self, client: 'PDFDancer') -> None:
18
+ if client is None:
19
+ raise ValidationException("Client cannot be null")
20
+
21
+ self._client = client
22
+ self._page_index: Optional[int] = None
23
+ self._orientation: Optional[Orientation] = None
24
+ self._page_size: Optional[PageSize] = None
25
+
26
+ def at_index(self, page_index: int) -> 'PageBuilder':
27
+ if page_index is None:
28
+ raise ValidationException("Page index cannot be null")
29
+ if page_index < 0:
30
+ raise ValidationException("Page index must be greater than or equal to 0")
31
+ self._page_index = int(page_index)
32
+ return self
33
+
34
+ def orientation(self, orientation: Orientation) -> 'PageBuilder':
35
+ if orientation is None:
36
+ raise ValidationException("Orientation cannot be null")
37
+ if isinstance(orientation, str):
38
+ normalized = orientation.strip().upper()
39
+ orientation = Orientation(normalized)
40
+ self._orientation = orientation
41
+ return self
42
+
43
+ def portrait(self) -> 'PageBuilder':
44
+ self._orientation = Orientation.PORTRAIT
45
+ return self
46
+
47
+ def landscape(self) -> 'PageBuilder':
48
+ self._orientation = Orientation.LANDSCAPE
49
+ return self
50
+
51
+ def page_size(self, page_size: PageSize) -> 'PageBuilder':
52
+ if page_size is None:
53
+ raise ValidationException("Page size cannot be null")
54
+ self._page_size = PageSize.coerce(page_size)
55
+ return self
56
+
57
+ def a4(self) -> 'PageBuilder':
58
+ self._page_size = PageSize.A4
59
+ return self
60
+
61
+ def letter(self) -> 'PageBuilder':
62
+ self._page_size = PageSize.LETTER
63
+ return self
64
+
65
+ def a3(self) -> 'PageBuilder':
66
+ self._page_size = PageSize.A3
67
+ return self
68
+
69
+ def a5(self) -> 'PageBuilder':
70
+ self._page_size = PageSize.A5
71
+ return self
72
+
73
+ def legal(self) -> 'PageBuilder':
74
+ self._page_size = PageSize.LEGAL
75
+ return self
76
+
77
+ def custom_size(self, width: float, height: float) -> 'PageBuilder':
78
+ self._page_size = PageSize(name=None, width=width, height=height)
79
+ return self
80
+
81
+ def add(self) -> PageRef:
82
+ request = self._build_request()
83
+ return self._client._add_page(request)
84
+
85
+ def _build_request(self) -> Optional[AddPageRequest]:
86
+ if self._page_index is None and self._orientation is None and self._page_size is None:
87
+ return None
88
+ return AddPageRequest(
89
+ page_index=self._page_index,
90
+ orientation=self._orientation,
91
+ page_size=self._page_size
92
+ )
pdfdancer/path_builder.py CHANGED
@@ -154,6 +154,38 @@ class PathBuilder:
154
154
  self._segments.append(bezier)
155
155
  return self
156
156
 
157
+ def add_rectangle(self, x: float, y: float, width: float, height: float) -> 'PathBuilder':
158
+ """
159
+ Convenient method to add a rectangle as four line segments to the path.
160
+
161
+ Args:
162
+ x: X coordinate of bottom-left corner
163
+ y: Y coordinate of bottom-left corner
164
+ width: Rectangle width
165
+ height: Rectangle height
166
+
167
+ Returns:
168
+ Self for method chaining
169
+ """
170
+ if width <= 0:
171
+ raise ValidationException("Rectangle width must be positive")
172
+ if height <= 0:
173
+ raise ValidationException("Rectangle height must be positive")
174
+
175
+ # Create rectangle as 4 line segments (clockwise from bottom-left)
176
+ bottom_left = Point(x, y)
177
+ bottom_right = Point(x + width, y)
178
+ top_right = Point(x + width, y + height)
179
+ top_left = Point(x, y + height)
180
+
181
+ # Add four lines forming the rectangle
182
+ self.add_line(bottom_left, bottom_right)
183
+ self.add_line(bottom_right, top_right)
184
+ self.add_line(top_right, top_left)
185
+ self.add_line(top_left, bottom_left)
186
+
187
+ return self
188
+
157
189
  def even_odd_fill(self, enabled: bool = True) -> 'PathBuilder':
158
190
  """
159
191
  Set the fill rule to even-odd (vs nonzero winding).
@@ -555,3 +587,223 @@ class BezierBuilder:
555
587
  # Add it using the client's internal method
556
588
  # noinspection PyProtectedMember
557
589
  return self._client._add_path(path)
590
+
591
+
592
+ class RectangleBuilder:
593
+ """
594
+ Builder class for constructing Rectangle objects with fluent interface.
595
+ Provides a convenient way to create a rectangle path with a single builder.
596
+ """
597
+
598
+ def __init__(self, client: 'PDFDancer', page_index: int):
599
+ """
600
+ Initialize the rectangle builder.
601
+
602
+ Args:
603
+ client: The PDFDancer instance for adding the rectangle
604
+ page_index: The page number (0-indexed)
605
+ """
606
+ if client is None:
607
+ raise ValidationException("Client cannot be null")
608
+
609
+ self._client = client
610
+ self._page_index = page_index
611
+ self._x: Optional[float] = None
612
+ self._y: Optional[float] = None
613
+ self._width: Optional[float] = None
614
+ self._height: Optional[float] = None
615
+ self._stroke_color: Optional[Color] = Color(0, 0, 0) # Black default
616
+ self._fill_color: Optional[Color] = None
617
+ self._stroke_width: float = 1.0
618
+ self._dash_array: Optional[List[float]] = None
619
+ self._dash_phase: Optional[float] = None
620
+ self._even_odd_fill: bool = False
621
+
622
+ def at_coordinates(self, x: float, y: float) -> 'RectangleBuilder':
623
+ """
624
+ Set the bottom-left corner position of the rectangle.
625
+
626
+ Args:
627
+ x: X coordinate on the page
628
+ y: Y coordinate on the page
629
+
630
+ Returns:
631
+ Self for method chaining
632
+ """
633
+ self._x = x
634
+ self._y = y
635
+ return self
636
+
637
+ def with_size(self, width: float, height: float) -> 'RectangleBuilder':
638
+ """
639
+ Set the dimensions of the rectangle.
640
+
641
+ Args:
642
+ width: Rectangle width
643
+ height: Rectangle height
644
+
645
+ Returns:
646
+ Self for method chaining
647
+ """
648
+ self._width = width
649
+ self._height = height
650
+ return self
651
+
652
+ def stroke_color(self, color: Color) -> 'RectangleBuilder':
653
+ """
654
+ Set the stroke color.
655
+
656
+ Args:
657
+ color: The stroke color
658
+
659
+ Returns:
660
+ Self for method chaining
661
+ """
662
+ self._stroke_color = color
663
+ return self
664
+
665
+ def fill_color(self, color: Color) -> 'RectangleBuilder':
666
+ """
667
+ Set the fill color.
668
+
669
+ Args:
670
+ color: The fill color
671
+
672
+ Returns:
673
+ Self for method chaining
674
+ """
675
+ self._fill_color = color
676
+ return self
677
+
678
+ def stroke_width(self, width: float) -> 'RectangleBuilder':
679
+ """
680
+ Set the stroke width.
681
+
682
+ Args:
683
+ width: The stroke width in points
684
+
685
+ Returns:
686
+ Self for method chaining
687
+ """
688
+ if width <= 0:
689
+ raise ValidationException("Stroke width must be positive")
690
+ self._stroke_width = width
691
+ return self
692
+
693
+ def dash_pattern(self, dash_array: List[float], dash_phase: float = 0.0) -> 'RectangleBuilder':
694
+ """
695
+ Set a dash pattern.
696
+
697
+ Args:
698
+ dash_array: List of on/off lengths (e.g., [10, 5] = 10pt on, 5pt off)
699
+ dash_phase: Offset into the pattern
700
+
701
+ Returns:
702
+ Self for method chaining
703
+ """
704
+ self._dash_array = dash_array
705
+ self._dash_phase = dash_phase
706
+ return self
707
+
708
+ def solid(self) -> 'RectangleBuilder':
709
+ """
710
+ Set rectangle to solid (no dash pattern).
711
+
712
+ Returns:
713
+ Self for method chaining
714
+ """
715
+ self._dash_array = None
716
+ self._dash_phase = None
717
+ return self
718
+
719
+ def even_odd_fill(self, enabled: bool = True) -> 'RectangleBuilder':
720
+ """
721
+ Set the fill rule to even-odd (vs nonzero winding).
722
+
723
+ Args:
724
+ enabled: True for even-odd, False for nonzero winding
725
+
726
+ Returns:
727
+ Self for method chaining
728
+ """
729
+ self._even_odd_fill = enabled
730
+ return self
731
+
732
+ def add(self) -> bool:
733
+ """
734
+ Build the rectangle and add it to the PDF document.
735
+
736
+ Returns:
737
+ True if successful
738
+
739
+ Raises:
740
+ ValidationException: If required properties are missing
741
+ """
742
+ if self._x is None or self._y is None:
743
+ raise ValidationException("Rectangle position must be set using at_coordinates()")
744
+ if self._width is None or self._height is None:
745
+ raise ValidationException("Rectangle dimensions must be set using with_size()")
746
+ if self._width <= 0:
747
+ raise ValidationException("Rectangle width must be positive")
748
+ if self._height <= 0:
749
+ raise ValidationException("Rectangle height must be positive")
750
+
751
+ # Create rectangle as 4 line segments
752
+ bottom_left = Point(self._x, self._y)
753
+ bottom_right = Point(self._x + self._width, self._y)
754
+ top_right = Point(self._x + self._width, self._y + self._height)
755
+ top_left = Point(self._x, self._y + self._height)
756
+
757
+ # Create four lines forming the rectangle
758
+ lines = [
759
+ Line(
760
+ p0=bottom_left,
761
+ p1=bottom_right,
762
+ stroke_color=self._stroke_color,
763
+ fill_color=self._fill_color,
764
+ stroke_width=self._stroke_width,
765
+ dash_array=self._dash_array,
766
+ dash_phase=self._dash_phase
767
+ ),
768
+ Line(
769
+ p0=bottom_right,
770
+ p1=top_right,
771
+ stroke_color=self._stroke_color,
772
+ fill_color=self._fill_color,
773
+ stroke_width=self._stroke_width,
774
+ dash_array=self._dash_array,
775
+ dash_phase=self._dash_phase
776
+ ),
777
+ Line(
778
+ p0=top_right,
779
+ p1=top_left,
780
+ stroke_color=self._stroke_color,
781
+ fill_color=self._fill_color,
782
+ stroke_width=self._stroke_width,
783
+ dash_array=self._dash_array,
784
+ dash_phase=self._dash_phase
785
+ ),
786
+ Line(
787
+ p0=top_left,
788
+ p1=bottom_left,
789
+ stroke_color=self._stroke_color,
790
+ fill_color=self._fill_color,
791
+ stroke_width=self._stroke_width,
792
+ dash_array=self._dash_array,
793
+ dash_phase=self._dash_phase
794
+ )
795
+ ]
796
+
797
+ # Create position with only page index set
798
+ position = Position.at_page_coordinates(self._page_index, 0, 0)
799
+
800
+ # Wrap in Path with four line segments
801
+ path = Path(
802
+ position=position,
803
+ path_segments=lines,
804
+ even_odd_fill=self._even_odd_fill
805
+ )
806
+
807
+ # Add it using the client's internal method
808
+ # noinspection PyProtectedMember
809
+ return self._client._add_path(path)
pdfdancer/pdfdancer_v1.py CHANGED
@@ -116,7 +116,7 @@ def _log_generated_at_header(response: httpx.Response, method: str, path: str) -
116
116
  print(f"{time.time()}|{method} {path} - Header parse error: {e}")
117
117
 
118
118
 
119
- from . import ParagraphBuilder
119
+ from . import ParagraphBuilder, BezierBuilder, PathBuilder, LineBuilder
120
120
  from .exceptions import (
121
121
  PdfDancerException,
122
122
  FontNotFoundException,
@@ -124,15 +124,16 @@ from .exceptions import (
124
124
  SessionException,
125
125
  ValidationException
126
126
  )
127
- from .image_builder import ImageBuilder
127
+ from .image_builder import ImageBuilder, ImageOnPageBuilder
128
128
  from .models import (
129
129
  ObjectRef, Position, ObjectType, Font, Image, Paragraph, FormFieldRef, TextObjectRef, PageRef,
130
- FindRequest, DeleteRequest, MoveRequest, PageMoveRequest, AddRequest, ModifyRequest, ModifyTextRequest,
130
+ FindRequest, DeleteRequest, MoveRequest, PageMoveRequest, AddPageRequest, AddRequest, ModifyRequest, ModifyTextRequest,
131
131
  ChangeFormFieldRequest, CommandResult,
132
132
  ShapeType, PositionMode, PageSize, Orientation,
133
133
  PageSnapshot, DocumentSnapshot, FontRecommendation, FontType
134
134
  )
135
135
  from .paragraph_builder import ParagraphPageBuilder
136
+ from .page_builder import PageBuilder
136
137
  from .types import PathObject, ParagraphObject, TextLineObject, ImageObject, FormObject, FormFieldObject
137
138
 
138
139
 
@@ -270,21 +271,25 @@ class PageClient:
270
271
  def _ref(self):
271
272
  return ObjectRef(internal_id=self.internal_id, position=self.position, type=self.object_type)
272
273
 
273
- def new_paragraph(self):
274
+ def new_paragraph(self) -> ParagraphBuilder:
274
275
  return ParagraphPageBuilder(self.root, self.page_index)
275
276
 
276
- def new_path(self):
277
- from .path_builder import PathBuilder
277
+ def new_path(self) -> PathBuilder:
278
278
  return PathBuilder(self.root, self.page_index)
279
279
 
280
- def new_line(self):
281
- from .path_builder import LineBuilder
280
+ def new_image(self) -> ImageOnPageBuilder:
281
+ return ImageOnPageBuilder(self.root, self.page_index)
282
+
283
+ def new_line(self) -> LineBuilder:
282
284
  return LineBuilder(self.root, self.page_index)
283
285
 
284
- def new_bezier(self):
285
- from .path_builder import BezierBuilder
286
+ def new_bezier(self) -> BezierBuilder:
286
287
  return BezierBuilder(self.root, self.page_index)
287
288
 
289
+ def new_rectangle(self) -> 'RectangleBuilder':
290
+ from .path_builder import RectangleBuilder
291
+ return RectangleBuilder(self.root, self.page_index)
292
+
288
293
  def select_paths(self):
289
294
  # noinspection PyProtectedMember
290
295
  return self.root._to_path_objects(self.root._find_paths(Position.at_page(self.page_index)))
@@ -900,11 +905,25 @@ class PDFDancer:
900
905
  all_elements.extend(page_snap.elements)
901
906
  return self._filter_snapshot_elements(all_elements, object_type, position, tolerance)
902
907
 
903
- def select_paragraphs(self) -> List[TextObjectRef]:
908
+ def select_paragraphs(self) -> List[ParagraphObject]:
904
909
  """
905
- Searches for paragraph objects returning TextObjectRef with hierarchical structure.
910
+ Searches for paragraph objects returning ParagraphObject instances.
906
911
  """
907
- return self._find_paragraphs(None)
912
+ return self._to_paragraph_objects(self._find_paragraphs(None))
913
+
914
+ def select_paragraphs_matching(self, pattern: str) -> List[ParagraphObject]:
915
+ """
916
+ Searches for paragraph objects matching a regex pattern.
917
+
918
+ Args:
919
+ pattern: Regex pattern to match against paragraph text
920
+
921
+ Returns:
922
+ List of ParagraphObject instances matching the pattern
923
+ """
924
+ position = Position()
925
+ position.text_pattern = pattern
926
+ return self._to_paragraph_objects(self._find_paragraphs(position))
908
927
 
909
928
  def _find_paragraphs(self, position: Optional[Position] = None, tolerance: float = DEFAULT_TOLERANCE) -> List[
910
929
  TextObjectRef]:
@@ -1270,7 +1289,6 @@ class PDFDancer:
1270
1289
  """
1271
1290
  Internal method to add a path to the document after validation.
1272
1291
  """
1273
- from .models import Path as PathModel
1274
1292
 
1275
1293
  if path is None:
1276
1294
  raise ValidationException("Path cannot be null")
@@ -1299,11 +1317,16 @@ class PDFDancer:
1299
1317
 
1300
1318
  return result
1301
1319
 
1302
- def new_paragraph(self) -> ParagraphBuilder:
1303
- return ParagraphBuilder(self)
1320
+ def _add_page(self, request: Optional[AddPageRequest]) -> PageRef:
1321
+ """
1322
+ Internal helper to add a page with optional parameters.
1323
+ """
1324
+ request_data = None
1325
+ if request is not None:
1326
+ payload = request.to_dict()
1327
+ request_data = payload or None
1304
1328
 
1305
- def new_page(self):
1306
- response = self._make_request('POST', '/pdf/page/add', data=None)
1329
+ response = self._make_request('POST', '/pdf/page/add', data=request_data)
1307
1330
  result = self._parse_page_ref(response.json())
1308
1331
 
1309
1332
  # Invalidate snapshot caches after adding page
@@ -1311,6 +1334,17 @@ class PDFDancer:
1311
1334
 
1312
1335
  return result
1313
1336
 
1337
+ def new_paragraph(self) -> ParagraphBuilder:
1338
+ return ParagraphBuilder(self)
1339
+
1340
+ def new_page(self, orientation=Orientation.PORTRAIT, size=PageSize.A4) -> PageBuilder:
1341
+ builder = PageBuilder(self)
1342
+ if orientation is not None:
1343
+ builder.orientation(orientation)
1344
+ if size is not None:
1345
+ builder.page_size(size)
1346
+ return builder
1347
+
1314
1348
  def new_image(self) -> ImageBuilder:
1315
1349
  return ImageBuilder(self)
1316
1350
 
pdfdancer/types.py CHANGED
@@ -266,6 +266,13 @@ class ParagraphObject(PDFObjectBase):
266
266
  super().__init__(client, object_ref.internal_id, object_ref.type, object_ref.position)
267
267
  self._object_ref = object_ref
268
268
 
269
+ def __getattr__(self, name):
270
+ """
271
+ Automatically delegate attribute/method lookup to _object_ref
272
+ if it's not found on this object.
273
+ """
274
+ return getattr(self._object_ref, name)
275
+
269
276
  def edit(self) -> ParagraphEdit:
270
277
  return ParagraphEdit(self, self.object_ref())
271
278
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pdfdancer-client-python
3
- Version: 0.2.18
3
+ Version: 0.2.20
4
4
  Summary: Python client for PDFDancer API
5
5
  Author-email: "The Famous Cat Ltd." <hi@thefamouscat.com>
6
6
  License:
@@ -0,0 +1,16 @@
1
+ pdfdancer/__init__.py,sha256=NKvSJY10p4TCc4uRC9wkDnkYJdvSdm2ry_D-fBgGtX8,2207
2
+ pdfdancer/exceptions.py,sha256=WAcyTacykJwjiaURrQamEgizLxv0vSlSio6NMikg4D0,1558
3
+ pdfdancer/fingerprint.py,sha256=Ue5QzpqsKlbYefvKU0ULV4NgMU3AOTcseeV9HfiPJXI,3093
4
+ pdfdancer/image_builder.py,sha256=ee-y7IzjZqpMN8O8ZzEm8-lOnOnqoQ7LQTzVWg4mHWg,1760
5
+ pdfdancer/models.py,sha256=uyVYlswBUcHMSXSRjeQOa9WBc2tJU3-Q6nKquwlbT2c,45712
6
+ pdfdancer/page_builder.py,sha256=HiMEPYWhaIWS9dZ1jxIA-XIeAwgJVXMoEAYsm3TxOt8,2969
7
+ pdfdancer/paragraph_builder.py,sha256=x5XhPo-GUeovQuGbrH7MyyenITcy6in7bz6CD9r-ZqU,9458
8
+ pdfdancer/path_builder.py,sha256=G5vpVAH9OB3zV5gaSA0MKWIygmieVhwQ8dxnt_hgF7A,23693
9
+ pdfdancer/pdfdancer_v1.py,sha256=WMRdxQwpfnOvotsaP6jVcLw1_TYe4XG7M0AAoWeSkdo,88700
10
+ pdfdancer/types.py,sha256=fMYNmT73ism5bN8ij8G8hELKWQA8-8o7AlIX6YFcSEw,12652
11
+ pdfdancer_client_python-0.2.20.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
12
+ pdfdancer_client_python-0.2.20.dist-info/licenses/NOTICE,sha256=xaC4l-IChAmtViNDie8ZWzUk0O6XRMyzOl0zLmVZ2HE,232
13
+ pdfdancer_client_python-0.2.20.dist-info/METADATA,sha256=vup4o7ROABx7YHuxTlA1oSlmg3KYS9Oarl9rOELaj4g,24668
14
+ pdfdancer_client_python-0.2.20.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
+ pdfdancer_client_python-0.2.20.dist-info/top_level.txt,sha256=ICwSVRpcCKrdBF9QlaX9Y0e_N3Nk1p7QVxadGOnbxeY,10
16
+ pdfdancer_client_python-0.2.20.dist-info/RECORD,,
@@ -1,15 +0,0 @@
1
- pdfdancer/__init__.py,sha256=ZZOarTVp9OJQUYA8PsBn8FWNCEHIaCcSHitd4dpBR5w,2150
2
- pdfdancer/exceptions.py,sha256=WAcyTacykJwjiaURrQamEgizLxv0vSlSio6NMikg4D0,1558
3
- pdfdancer/fingerprint.py,sha256=Ue5QzpqsKlbYefvKU0ULV4NgMU3AOTcseeV9HfiPJXI,3093
4
- pdfdancer/image_builder.py,sha256=Omxc2LcieJ1MbvWBXR5_sfia--eAucTUe0KWgr22HYo,842
5
- pdfdancer/models.py,sha256=kK2tIRwJ8XQJZZ__uSgvU6E0NRtj11Jzwwhy-d8EKSQ,32210
6
- pdfdancer/paragraph_builder.py,sha256=x5XhPo-GUeovQuGbrH7MyyenITcy6in7bz6CD9r-ZqU,9458
7
- pdfdancer/path_builder.py,sha256=KJC8toLhbCbtnxz8pphtpzgoBemGHlrcC1s02RAvCJ8,15862
8
- pdfdancer/pdfdancer_v1.py,sha256=pR2TnA57l2tXzVZHaBz_QFvZcVAdeWgwUXtO5OhPOPc,87350
9
- pdfdancer/types.py,sha256=SYbyzFJu1nDartalqgrceSTiLtsqZgIFQndvHz5Tnxw,12435
10
- pdfdancer_client_python-0.2.18.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
11
- pdfdancer_client_python-0.2.18.dist-info/licenses/NOTICE,sha256=xaC4l-IChAmtViNDie8ZWzUk0O6XRMyzOl0zLmVZ2HE,232
12
- pdfdancer_client_python-0.2.18.dist-info/METADATA,sha256=37zeBth-hMmDWCMBdqShEsC8neWz8kqFOZIC-ALm4RA,24668
13
- pdfdancer_client_python-0.2.18.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
14
- pdfdancer_client_python-0.2.18.dist-info/top_level.txt,sha256=ICwSVRpcCKrdBF9QlaX9Y0e_N3Nk1p7QVxadGOnbxeY,10
15
- pdfdancer_client_python-0.2.18.dist-info/RECORD,,