pdfdancer-client-python 0.1.2__tar.gz → 0.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pdfdancer-client-python might be problematic. Click here for more details.

Files changed (52) hide show
  1. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/CLAUDE.md +4 -4
  2. {pdfdancer_client_python-0.1.2/src/pdfdancer_client_python.egg-info → pdfdancer_client_python-0.2.2}/PKG-INFO +26 -25
  3. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/README.md +21 -19
  4. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/pyproject.toml +5 -6
  5. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/release.py +9 -3
  6. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/src/pdfdancer/__init__.py +5 -3
  7. pdfdancer_client_python-0.2.2/src/pdfdancer/image_builder.py +30 -0
  8. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/src/pdfdancer/models.py +58 -6
  9. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/src/pdfdancer/paragraph_builder.py +15 -12
  10. pdfdancer_client_python-0.1.2/src/pdfdancer/client_v1.py → pdfdancer_client_python-0.2.2/src/pdfdancer/pdfdancer_v1.py +236 -55
  11. pdfdancer_client_python-0.2.2/src/pdfdancer/types.py +263 -0
  12. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2/src/pdfdancer_client_python.egg-info}/PKG-INFO +26 -25
  13. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/src/pdfdancer_client_python.egg-info/SOURCES.txt +5 -6
  14. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/test.py +32 -32
  15. pdfdancer_client_python-0.2.2/tests/e2e/test_acroform.py +83 -0
  16. pdfdancer_client_python-0.2.2/tests/e2e/test_form_x_objects.py +36 -0
  17. pdfdancer_client_python-0.2.2/tests/e2e/test_image.py +91 -0
  18. pdfdancer_client_python-0.2.2/tests/e2e/test_line.py +83 -0
  19. pdfdancer_client_python-0.2.2/tests/e2e/test_page.py +33 -0
  20. pdfdancer_client_python-0.2.2/tests/e2e/test_paragraph.py +169 -0
  21. pdfdancer_client_python-0.2.2/tests/e2e/test_path.py +69 -0
  22. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/tests/test_models.py +8 -8
  23. pdfdancer_client_python-0.1.2/tests/e2e/test_form.py +0 -33
  24. pdfdancer_client_python-0.1.2/tests/e2e/test_image.py +0 -84
  25. pdfdancer_client_python-0.1.2/tests/e2e/test_line.py +0 -84
  26. pdfdancer_client_python-0.1.2/tests/e2e/test_page.py +0 -29
  27. pdfdancer_client_python-0.1.2/tests/e2e/test_paragraph.py +0 -165
  28. pdfdancer_client_python-0.1.2/tests/e2e/test_path.py +0 -56
  29. pdfdancer_client_python-0.1.2/tests/test_client_v1.py +0 -444
  30. pdfdancer_client_python-0.1.2/tests/test_error_extraction.py +0 -117
  31. pdfdancer_client_python-0.1.2/tests/test_exception_suppression.py +0 -53
  32. pdfdancer_client_python-0.1.2/tests/test_paragraph_builder.py +0 -324
  33. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/.github/workflows/ci.yml +0 -0
  34. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/.gitignore +0 -0
  35. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/docs/openapi.yml +0 -0
  36. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/requirements-dev.txt +0 -0
  37. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/requirements.txt +0 -0
  38. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/setup.cfg +0 -0
  39. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/src/pdfdancer/exceptions.py +0 -0
  40. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/src/pdfdancer_client_python.egg-info/dependency_links.txt +0 -0
  41. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/src/pdfdancer_client_python.egg-info/requires.txt +0 -0
  42. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/src/pdfdancer_client_python.egg-info/top_level.txt +0 -0
  43. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/tests/__init__.py +0 -0
  44. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/tests/e2e/__init__.py +0 -0
  45. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/tests/fixtures/DancingScript-Regular.ttf +0 -0
  46. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/tests/fixtures/JetBrainsMono-Regular.ttf +0 -0
  47. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/tests/fixtures/ObviouslyAwesome.pdf +0 -0
  48. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/tests/fixtures/basic-paths.pdf +0 -0
  49. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/tests/fixtures/form-xobject-example.pdf +0 -0
  50. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/tests/fixtures/logo-80.png +0 -0
  51. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/tests/fixtures/mixed-form-types.pdf +0 -0
  52. {pdfdancer_client_python-0.1.2 → pdfdancer_client_python-0.2.2}/tests/test_openapi_compliance.py +0 -0
@@ -38,12 +38,12 @@ The client is a pure manual implementation that closely mirrors the Java client:
38
38
  client = ClientV1(token="jwt-token", pdf_data="document.pdf")
39
39
 
40
40
  # Find operations (mirrors Java findParagraphs, findImages, etc.)
41
- paragraphs = client.find_paragraphs(position)
42
- images = client.find_images(position)
41
+ paragraphs = client._find_paragraphs(position)
42
+ images = client._find_images(position)
43
43
 
44
44
  # Manipulation operations (mirrors Java delete, move, etc.)
45
- client.delete(paragraphs[0])
46
- client.move(images[0], new_position)
45
+ client._delete(paragraphs[0])
46
+ client._move(images[0], new_position)
47
47
 
48
48
  # Builder pattern (mirrors Java ParagraphBuilder)
49
49
  paragraph = (client.paragraph_builder()
@@ -1,19 +1,18 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pdfdancer-client-python
3
- Version: 0.1.2
3
+ Version: 0.2.2
4
4
  Summary: Python client for PDFDancer API
5
- Author-email: TFC <info@example.com>
5
+ Author-email: "The Famous Cat Ltd." <hi@thefamouscat.com>
6
6
  License: MIT
7
- Project-URL: Homepage, https://github.com/tfc/pdfdancer-python
8
- Project-URL: Repository, https://github.com/tfc/pdfdancer-python.git
7
+ Project-URL: Homepage, https://www.pdfdancer.com/
8
+ Project-URL: Repository, https://github.com/MenschMachine/pdfdancer-client-python
9
9
  Classifier: Development Status :: 4 - Beta
10
10
  Classifier: Intended Audience :: Developers
11
11
  Classifier: License :: OSI Approved :: MIT License
12
- Classifier: Programming Language :: Python :: 3
13
- Classifier: Programming Language :: Python :: 3.8
14
12
  Classifier: Programming Language :: Python :: 3.9
15
13
  Classifier: Programming Language :: Python :: 3.10
16
14
  Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
17
16
  Description-Content-Type: text/markdown
18
17
  Requires-Dist: requests>=2.25.0
19
18
  Requires-Dist: pydantic>=1.8.0
@@ -60,8 +59,8 @@ paragraphs = client.find_paragraphs(None)
60
59
  images = client.find_images(Position.at_page(0))
61
60
 
62
61
  # Manipulation operations (mirrors Java client methods)
63
- client.delete(paragraphs[0])
64
- client.move(images[0], Position.at_page_coordinates(0, 100, 200))
62
+ client._delete(paragraphs[0])
63
+ client._move(images[0], Position.at_page_coordinates(0, 100, 200))
65
64
 
66
65
  # Builder pattern (mirrors Java ParagraphBuilder)
67
66
  paragraph = (client.paragraph_builder()
@@ -71,7 +70,7 @@ paragraph = (client.paragraph_builder()
71
70
  .with_position(Position.at_page(0))
72
71
  .build())
73
72
 
74
- client.add_paragraph(paragraph)
73
+ client._add_paragraph(paragraph)
75
74
 
76
75
  # Save result (mirrors Java savePDF)
77
76
  client.save_pdf("output.pdf")
@@ -85,7 +84,7 @@ from pdfdancer import ClientV1
85
84
  # Automatic resource management
86
85
  with ClientV1(token="jwt-token", pdf_data="input.pdf") as client:
87
86
  paragraphs = client.find_paragraphs(None)
88
- client.delete(paragraphs[0])
87
+ client._delete(paragraphs[0])
89
88
  client.save_pdf("output.pdf")
90
89
  # Session automatically cleaned up
91
90
  ```
@@ -105,37 +104,39 @@ client = ClientV1(token="jwt-token", pdf_data=pdf_file, base_url="https://api.se
105
104
  ```
106
105
 
107
106
  ### Find Operations
107
+
108
108
  ```python
109
109
  # Generic find (Java: client.find())
110
- objects = client.find(ObjectType.PARAGRAPH, position)
110
+ objects = client._find(ObjectType.PARAGRAPH, position)
111
111
 
112
112
  # Specific finders (Java: client.findParagraphs(), etc.)
113
- paragraphs = client.find_paragraphs(position)
114
- images = client.find_images(position)
115
- forms = client.find_forms(position)
116
- paths = client.find_paths(position)
117
- text_lines = client.find_text_lines(position)
113
+ paragraphs = client._find_paragraphs(position)
114
+ images = client._find_images(position)
115
+ forms = client._find_form_x_objects(position)
116
+ paths = client._find_paths(position)
117
+ text_lines = client._find_text_lines(position)
118
118
 
119
119
  # Page operations (Java: client.getPages(), client.getPage())
120
120
  pages = client.get_pages()
121
- page = client.get_page(1) # 1-based indexing
121
+ page = client._get_page(1) # 1-based indexing
122
122
  ```
123
123
 
124
124
  ### Manipulation Operations
125
+
125
126
  ```python
126
127
  # Delete (Java: client.delete(), client.deletePage())
127
- result = client.delete(object_ref)
128
- result = client.delete_page(page_ref)
128
+ result = client._delete(object_ref)
129
+ result = client._delete_page(page_ref)
129
130
 
130
131
  # Move (Java: client.move())
131
- result = client.move(object_ref, new_position)
132
+ result = client._move(object_ref, new_position)
132
133
 
133
134
  # Add (Java: client.addImage(), client.addParagraph())
134
- result = client.add_image(image, position)
135
- result = client.add_paragraph(paragraph)
135
+ result = client._add_image(image, position)
136
+ result = client._add_paragraph(paragraph)
136
137
 
137
138
  # Modify (Java: client.modifyParagraph(), client.modifyTextLine())
138
- result = client.modify_paragraph(ref, new_paragraph)
139
+ result = client._modify_paragraph(ref, new_paragraph)
139
140
  result = client.modify_text_line(ref, "new text")
140
141
  ```
141
142
 
@@ -171,8 +172,8 @@ position = Position.at_page(0)
171
172
  position = Position.at_page_coordinates(0, 100, 200)
172
173
 
173
174
  # Coordinate access (Java: position.getX(), position.getY())
174
- x = position.get_x()
175
- y = position.get_y()
175
+ x = position.x()
176
+ y = position.y()
176
177
 
177
178
  # Movement (Java: position.moveX(), position.moveY())
178
179
  position.move_x(50.0)
@@ -33,8 +33,8 @@ paragraphs = client.find_paragraphs(None)
33
33
  images = client.find_images(Position.at_page(0))
34
34
 
35
35
  # Manipulation operations (mirrors Java client methods)
36
- client.delete(paragraphs[0])
37
- client.move(images[0], Position.at_page_coordinates(0, 100, 200))
36
+ client._delete(paragraphs[0])
37
+ client._move(images[0], Position.at_page_coordinates(0, 100, 200))
38
38
 
39
39
  # Builder pattern (mirrors Java ParagraphBuilder)
40
40
  paragraph = (client.paragraph_builder()
@@ -44,7 +44,7 @@ paragraph = (client.paragraph_builder()
44
44
  .with_position(Position.at_page(0))
45
45
  .build())
46
46
 
47
- client.add_paragraph(paragraph)
47
+ client._add_paragraph(paragraph)
48
48
 
49
49
  # Save result (mirrors Java savePDF)
50
50
  client.save_pdf("output.pdf")
@@ -58,7 +58,7 @@ from pdfdancer import ClientV1
58
58
  # Automatic resource management
59
59
  with ClientV1(token="jwt-token", pdf_data="input.pdf") as client:
60
60
  paragraphs = client.find_paragraphs(None)
61
- client.delete(paragraphs[0])
61
+ client._delete(paragraphs[0])
62
62
  client.save_pdf("output.pdf")
63
63
  # Session automatically cleaned up
64
64
  ```
@@ -78,37 +78,39 @@ client = ClientV1(token="jwt-token", pdf_data=pdf_file, base_url="https://api.se
78
78
  ```
79
79
 
80
80
  ### Find Operations
81
+
81
82
  ```python
82
83
  # Generic find (Java: client.find())
83
- objects = client.find(ObjectType.PARAGRAPH, position)
84
+ objects = client._find(ObjectType.PARAGRAPH, position)
84
85
 
85
86
  # Specific finders (Java: client.findParagraphs(), etc.)
86
- paragraphs = client.find_paragraphs(position)
87
- images = client.find_images(position)
88
- forms = client.find_forms(position)
89
- paths = client.find_paths(position)
90
- text_lines = client.find_text_lines(position)
87
+ paragraphs = client._find_paragraphs(position)
88
+ images = client._find_images(position)
89
+ forms = client._find_form_x_objects(position)
90
+ paths = client._find_paths(position)
91
+ text_lines = client._find_text_lines(position)
91
92
 
92
93
  # Page operations (Java: client.getPages(), client.getPage())
93
94
  pages = client.get_pages()
94
- page = client.get_page(1) # 1-based indexing
95
+ page = client._get_page(1) # 1-based indexing
95
96
  ```
96
97
 
97
98
  ### Manipulation Operations
99
+
98
100
  ```python
99
101
  # Delete (Java: client.delete(), client.deletePage())
100
- result = client.delete(object_ref)
101
- result = client.delete_page(page_ref)
102
+ result = client._delete(object_ref)
103
+ result = client._delete_page(page_ref)
102
104
 
103
105
  # Move (Java: client.move())
104
- result = client.move(object_ref, new_position)
106
+ result = client._move(object_ref, new_position)
105
107
 
106
108
  # Add (Java: client.addImage(), client.addParagraph())
107
- result = client.add_image(image, position)
108
- result = client.add_paragraph(paragraph)
109
+ result = client._add_image(image, position)
110
+ result = client._add_paragraph(paragraph)
109
111
 
110
112
  # Modify (Java: client.modifyParagraph(), client.modifyTextLine())
111
- result = client.modify_paragraph(ref, new_paragraph)
113
+ result = client._modify_paragraph(ref, new_paragraph)
112
114
  result = client.modify_text_line(ref, "new text")
113
115
  ```
114
116
 
@@ -144,8 +146,8 @@ position = Position.at_page(0)
144
146
  position = Position.at_page_coordinates(0, 100, 200)
145
147
 
146
148
  # Coordinate access (Java: position.getX(), position.getY())
147
- x = position.get_x()
148
- y = position.get_y()
149
+ x = position.x()
150
+ y = position.y()
149
151
 
150
152
  # Movement (Java: position.moveX(), position.moveY())
151
153
  position.move_x(50.0)
@@ -4,22 +4,21 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pdfdancer-client-python"
7
- version = "0.1.2"
7
+ version = "0.2.2"
8
8
  description = "Python client for PDFDancer API"
9
9
  readme = "README.md"
10
10
  authors = [
11
- { name = "TFC", email = "info@example.com" }
11
+ { name = "The Famous Cat Ltd.", email = "hi@thefamouscat.com" }
12
12
  ]
13
13
  license = { text = "MIT" }
14
14
  classifiers = [
15
15
  "Development Status :: 4 - Beta",
16
16
  "Intended Audience :: Developers",
17
17
  "License :: OSI Approved :: MIT License",
18
- "Programming Language :: Python :: 3",
19
- "Programming Language :: Python :: 3.8",
20
18
  "Programming Language :: Python :: 3.9",
21
19
  "Programming Language :: Python :: 3.10",
22
20
  "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
23
22
  ]
24
23
  dependencies = [
25
24
  "requests>=2.25.0",
@@ -37,8 +36,8 @@ dev = [
37
36
  ]
38
37
 
39
38
  [project.urls]
40
- Homepage = "https://github.com/tfc/pdfdancer-python"
41
- Repository = "https://github.com/tfc/pdfdancer-python.git"
39
+ Homepage = "https://www.pdfdancer.com/"
40
+ Repository = "https://github.com/MenschMachine/pdfdancer-client-python"
42
41
 
43
42
  [tool.setuptools.packages.find]
44
43
  where = ["src"]
@@ -6,11 +6,12 @@ A tool to bump version and upload to PyPI.
6
6
  """
7
7
 
8
8
  import argparse
9
+ import glob
9
10
  import re
10
11
  import subprocess
11
12
  import sys
12
13
  from pathlib import Path
13
- from typing import List, Optional
14
+ from typing import List
14
15
 
15
16
 
16
17
  class ReleaseError(Exception):
@@ -142,7 +143,12 @@ class PyPIUploader:
142
143
  if include_e2e:
143
144
  test_path = "tests/"
144
145
  else:
145
- test_path = "tests/test_client_v1.py tests/test_models.py tests/test_paragraph_builder.py"
146
+ # Collect all test files except those in e2e/
147
+ test_files = [
148
+ f for f in glob.glob("tests/**/*.py", recursive=True)
149
+ if "e2e" not in f
150
+ ]
151
+ test_path = " ".join(test_files)
146
152
 
147
153
  self.run_command([str(self.python_exe), "-m", "pytest"] + test_path.split() + ["-v"])
148
154
  print("All tests passed")
@@ -263,4 +269,4 @@ def main():
263
269
 
264
270
 
265
271
  if __name__ == "__main__":
266
- main()
272
+ main()
@@ -6,20 +6,19 @@ Provides a clean, Pythonic interface for PDF operations that closely
6
6
  mirrors the Java client structure and functionality.
7
7
  """
8
8
 
9
- from .client_v1 import ClientV1
10
9
  from .exceptions import (
11
10
  PdfDancerException, FontNotFoundException, ValidationException,
12
11
  HttpClientException, SessionException
13
12
  )
14
13
  from .models import (
15
- ObjectRef, Position, ObjectType, Font, Color, Image, BoundingRect, Paragraph,
14
+ ObjectRef, Position, ObjectType, Font, Color, Image, BoundingRect, Paragraph, FormFieldRef,
16
15
  PositionMode, ShapeType, Point
17
16
  )
18
17
  from .paragraph_builder import ParagraphBuilder
19
18
 
20
19
  __version__ = "1.0.0"
21
20
  __all__ = [
22
- "ClientV1",
21
+ "PDFDancer",
23
22
  "ParagraphBuilder",
24
23
  "ObjectRef",
25
24
  "Position",
@@ -29,6 +28,7 @@ __all__ = [
29
28
  "Image",
30
29
  "BoundingRect",
31
30
  "Paragraph",
31
+ "FormFieldRef",
32
32
  "PositionMode",
33
33
  "ShapeType",
34
34
  "Point",
@@ -38,3 +38,5 @@ __all__ = [
38
38
  "HttpClientException",
39
39
  "SessionException"
40
40
  ]
41
+
42
+ from .pdfdancer_v1 import PDFDancer
@@ -0,0 +1,30 @@
1
+ from pathlib import Path
2
+
3
+ from pdfdancer import ValidationException, Image, Position
4
+
5
+
6
+ class ImageBuilder:
7
+
8
+ def __init__(self, client: 'PDFDancer'):
9
+ """
10
+ Initialize the image builder with a client reference.
11
+
12
+ Args:
13
+ client: The PDFDancer instance for font registration
14
+ """
15
+ if client is None:
16
+ raise ValidationException("Client cannot be null")
17
+
18
+ self._client = client
19
+ self._image = Image()
20
+
21
+ def from_file(self, img_path: Path) -> 'ImageBuilder':
22
+ self._image.data = img_path.read_bytes()
23
+ return self
24
+
25
+ def at(self, page, x, y) -> 'ImageBuilder':
26
+ self._image.position = Position.at_page_coordinates(page, x, y)
27
+ return self
28
+
29
+ def add(self) -> bool:
30
+ return self._client._add_image(self._image, self._image.position)
@@ -10,12 +10,16 @@ from typing import Optional, List, Any
10
10
 
11
11
  class ObjectType(Enum):
12
12
  """Object type enumeration matching the Java ObjectType."""
13
+ FORM_FIELD = "FORM_FIELD"
13
14
  IMAGE = "IMAGE"
14
15
  FORM_X_OBJECT = "FORM_X_OBJECT"
15
16
  PATH = "PATH"
16
17
  PARAGRAPH = "PARAGRAPH"
17
18
  TEXT_LINE = "TEXT_LINE"
18
19
  PAGE = "PAGE"
20
+ TEXT_FIELD = "TEXT_FIELD"
21
+ CHECK_BOX = "CHECK_BOX"
22
+ RADIO_BUTTON = "RADIO_BUTTON"
19
23
 
20
24
 
21
25
  class PositionMode(Enum):
@@ -74,6 +78,7 @@ class Position:
74
78
  mode: Optional[PositionMode] = None
75
79
  bounding_rect: Optional[BoundingRect] = None
76
80
  text_starts_with: Optional[str] = None
81
+ name: Optional[str] = None
77
82
 
78
83
  @staticmethod
79
84
  def at_page(page_index: int) -> 'Position':
@@ -93,7 +98,17 @@ class Position:
93
98
  position.at_coordinates(Point(x, y))
94
99
  return position
95
100
 
96
- def at_coordinates(self, point: Point) -> None:
101
+ @staticmethod
102
+ def by_name(name: str) -> 'Position':
103
+ """
104
+ Creates a position specification for finding objects by name.
105
+ Equivalent to Position.byName() in Java.
106
+ """
107
+ position = Position()
108
+ position.name = name
109
+ return position
110
+
111
+ def at_coordinates(self, point: Point) -> 'Position':
97
112
  """
98
113
  Sets the position to a specific point location.
99
114
  Equivalent to Position.set() in Java.
@@ -101,7 +116,7 @@ class Position:
101
116
  self.mode = PositionMode.CONTAINS
102
117
  self.shape = ShapeType.POINT
103
118
  self.bounding_rect = BoundingRect(point.x, point.y, 0, 0)
104
- return self;
119
+ return self
105
120
 
106
121
  def with_text_starts(self, text: str) -> 'Position':
107
122
  self.text_starts_with = text
@@ -110,20 +125,20 @@ class Position:
110
125
  def move_x(self, x_offset: float) -> 'Position':
111
126
  """Move the position horizontally by the specified offset."""
112
127
  if self.bounding_rect:
113
- self.at_coordinates(Point(self.get_x() + x_offset, self.get_y()))
128
+ self.at_coordinates(Point(self.x() + x_offset, self.y()))
114
129
  return self
115
130
 
116
131
  def move_y(self, y_offset: float) -> 'Position':
117
132
  """Move the position vertically by the specified offset."""
118
133
  if self.bounding_rect:
119
- self.at_coordinates(Point(self.get_x(), self.get_y() + y_offset))
134
+ self.at_coordinates(Point(self.x(), self.y() + y_offset))
120
135
  return self
121
136
 
122
- def get_x(self) -> Optional[float]:
137
+ def x(self) -> Optional[float]:
123
138
  """Returns the X coordinate of this position."""
124
139
  return self.bounding_rect.get_x() if self.bounding_rect else None
125
140
 
126
- def get_y(self) -> Optional[float]:
141
+ def y(self) -> Optional[float]:
127
142
  """Returns the Y coordinate of this position."""
128
143
  return self.bounding_rect.get_y() if self.bounding_rect else None
129
144
 
@@ -269,6 +284,8 @@ class FindRequest:
269
284
  "pageIndex": position.page_index,
270
285
  "textStartsWith": position.text_starts_with
271
286
  }
287
+ if position.name:
288
+ result["name"] = position.name
272
289
  if position.shape:
273
290
  result["shape"] = position.shape.value
274
291
  if position.mode:
@@ -420,3 +437,38 @@ class ModifyTextRequest:
420
437
  },
421
438
  "newTextLine": self.new_text
422
439
  }
440
+
441
+
442
+ @dataclass
443
+ class ChangeFormFieldRequest:
444
+ object_ref: ObjectRef
445
+ value: str
446
+
447
+ def to_dict(self) -> dict:
448
+ """Convert to dictionary for JSON serialization."""
449
+ return {
450
+ "ref": {
451
+ "internalId": self.object_ref.internal_id,
452
+ "position": FindRequest._position_to_dict(self.object_ref.position),
453
+ "type": self.object_ref.type.value
454
+ },
455
+ "value": self.value
456
+ }
457
+
458
+
459
+ @dataclass
460
+ class FormFieldRef(ObjectRef):
461
+ """
462
+ Represents a form field reference with additional form-specific properties.
463
+ Extends ObjectRef to include form field name and value.
464
+ """
465
+ name: Optional[str] = None
466
+ value: Optional[str] = None
467
+
468
+ def get_name(self) -> Optional[str]:
469
+ """Get the form field name."""
470
+ return self.name
471
+
472
+ def get_value(self) -> Optional[str]:
473
+ """Get the form field value."""
474
+ return self.value
@@ -4,14 +4,11 @@ Closely mirrors the Java ParagraphBuilder class with Python conventions.
4
4
  """
5
5
 
6
6
  from pathlib import Path
7
- from typing import Optional, Union, TYPE_CHECKING
7
+ from typing import Optional, Union
8
8
 
9
9
  from .exceptions import ValidationException
10
10
  from .models import Paragraph, Font, Color, Position
11
11
 
12
- if TYPE_CHECKING:
13
- from .client_v1 import ClientV1
14
-
15
12
 
16
13
  class ParagraphBuilder:
17
14
  """
@@ -19,7 +16,7 @@ class ParagraphBuilder:
19
16
  Mirrors the Java ParagraphBuilder class exactly.
20
17
  """
21
18
 
22
- def __init__(self, client: 'ClientV1'):
19
+ def __init__(self, client: 'PDFDancer'):
23
20
  """
24
21
  Initialize the paragraph builder with a client reference.
25
22
 
@@ -37,7 +34,7 @@ class ParagraphBuilder:
37
34
  self._ttf_file: Optional[Path] = None
38
35
  self._font: Optional[Font] = None
39
36
 
40
- def from_string(self, text: str, color: Optional[Color] = None) -> 'ParagraphBuilder':
37
+ def text(self, text: str, color: Optional[Color] = None) -> 'ParagraphBuilder':
41
38
  """
42
39
  Set the text content for the paragraph.
43
40
  Equivalent to fromString() methods in Java ParagraphBuilder.
@@ -63,13 +60,14 @@ class ParagraphBuilder:
63
60
 
64
61
  return self
65
62
 
66
- def with_font(self, font: Font) -> 'ParagraphBuilder':
63
+ def font(self, font_name: str, font_size: float) -> 'ParagraphBuilder':
67
64
  """
68
65
  Set the font for the paragraph using an existing Font object.
69
66
  Equivalent to withFont(Font) in Java ParagraphBuilder.
70
67
 
71
68
  Args:
72
- font: The Font object to use
69
+ font_name: The Font to use
70
+ font_size: The font size
73
71
 
74
72
  Returns:
75
73
  Self for method chaining
@@ -77,6 +75,7 @@ class ParagraphBuilder:
77
75
  Raises:
78
76
  ValidationException: If font is None
79
77
  """
78
+ font = Font(font_name, font_size)
80
79
  if font is None:
81
80
  raise ValidationException("Font cannot be null")
82
81
 
@@ -84,7 +83,7 @@ class ParagraphBuilder:
84
83
  self._ttf_file = None # Clear TTF file when using existing font
85
84
  return self
86
85
 
87
- def with_font_file(self, ttf_file: Union[Path, str], font_size: float) -> 'ParagraphBuilder':
86
+ def font_file(self, ttf_file: Union[Path, str], font_size: float) -> 'ParagraphBuilder':
88
87
  """
89
88
  Set the font for the paragraph using a TTF file.
90
89
  Equivalent to withFont(File, double) in Java ParagraphBuilder.
@@ -125,7 +124,7 @@ class ParagraphBuilder:
125
124
  self._font = self._register_ttf(ttf_path, font_size)
126
125
  return self
127
126
 
128
- def with_line_spacing(self, spacing: float) -> 'ParagraphBuilder':
127
+ def line_spacing(self, spacing: float) -> 'ParagraphBuilder':
129
128
  """
130
129
  Set the line spacing for the paragraph.
131
130
  Equivalent to withLineSpacing() in Java ParagraphBuilder.
@@ -145,7 +144,7 @@ class ParagraphBuilder:
145
144
  self._line_spacing = spacing
146
145
  return self
147
146
 
148
- def with_color(self, color: Color) -> 'ParagraphBuilder':
147
+ def color(self, color: Color) -> 'ParagraphBuilder':
149
148
  """
150
149
  Set the text color for the paragraph.
151
150
  Equivalent to withColor() in Java ParagraphBuilder.
@@ -165,7 +164,7 @@ class ParagraphBuilder:
165
164
  self._text_color = color
166
165
  return self
167
166
 
168
- def with_position(self, position: Position) -> 'ParagraphBuilder':
167
+ def at(self, page_index: int, x: float, y: float) -> 'ParagraphBuilder':
169
168
  """
170
169
  Set the position for the paragraph.
171
170
  Equivalent to withPosition() in Java ParagraphBuilder.
@@ -179,6 +178,7 @@ class ParagraphBuilder:
179
178
  Raises:
180
179
  ValidationException: If position is None
181
180
  """
181
+ position = Position.at_page_coordinates(page_index, x, y)
182
182
  if position is None:
183
183
  raise ValidationException("Position cannot be null")
184
184
 
@@ -265,3 +265,6 @@ class ParagraphBuilder:
265
265
  lines = ['']
266
266
 
267
267
  return lines
268
+
269
+ def add(self):
270
+ self._client._add_paragraph(self.build())