pdfdancer-client-python 0.2.2__tar.gz → 0.2.3__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 (42) hide show
  1. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/CLAUDE.md +1 -1
  2. {pdfdancer_client_python-0.2.2/src/pdfdancer_client_python.egg-info → pdfdancer_client_python-0.2.3}/PKG-INFO +14 -13
  3. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/README.md +13 -12
  4. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/pyproject.toml +1 -1
  5. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/src/pdfdancer/pdfdancer_v1.py +27 -4
  6. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3/src/pdfdancer_client_python.egg-info}/PKG-INFO +14 -13
  7. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/src/pdfdancer_client_python.egg-info/SOURCES.txt +0 -1
  8. pdfdancer_client_python-0.2.2/test.py +0 -455
  9. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/.github/workflows/ci.yml +0 -0
  10. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/.gitignore +0 -0
  11. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/docs/openapi.yml +0 -0
  12. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/release.py +0 -0
  13. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/requirements-dev.txt +0 -0
  14. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/requirements.txt +0 -0
  15. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/setup.cfg +0 -0
  16. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/src/pdfdancer/__init__.py +0 -0
  17. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/src/pdfdancer/exceptions.py +0 -0
  18. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/src/pdfdancer/image_builder.py +0 -0
  19. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/src/pdfdancer/models.py +0 -0
  20. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/src/pdfdancer/paragraph_builder.py +0 -0
  21. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/src/pdfdancer/types.py +0 -0
  22. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/src/pdfdancer_client_python.egg-info/dependency_links.txt +0 -0
  23. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/src/pdfdancer_client_python.egg-info/requires.txt +0 -0
  24. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/src/pdfdancer_client_python.egg-info/top_level.txt +0 -0
  25. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/tests/__init__.py +0 -0
  26. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/tests/e2e/__init__.py +0 -0
  27. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/tests/e2e/test_acroform.py +0 -0
  28. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/tests/e2e/test_form_x_objects.py +0 -0
  29. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/tests/e2e/test_image.py +0 -0
  30. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/tests/e2e/test_line.py +0 -0
  31. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/tests/e2e/test_page.py +0 -0
  32. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/tests/e2e/test_paragraph.py +0 -0
  33. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/tests/e2e/test_path.py +0 -0
  34. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/tests/fixtures/DancingScript-Regular.ttf +0 -0
  35. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/tests/fixtures/JetBrainsMono-Regular.ttf +0 -0
  36. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/tests/fixtures/ObviouslyAwesome.pdf +0 -0
  37. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/tests/fixtures/basic-paths.pdf +0 -0
  38. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/tests/fixtures/form-xobject-example.pdf +0 -0
  39. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/tests/fixtures/logo-80.png +0 -0
  40. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/tests/fixtures/mixed-form-types.pdf +0 -0
  41. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/tests/test_models.py +0 -0
  42. {pdfdancer_client_python-0.2.2 → pdfdancer_client_python-0.2.3}/tests/test_openapi_compliance.py +0 -0
@@ -46,7 +46,7 @@ client._delete(paragraphs[0])
46
46
  client._move(images[0], new_position)
47
47
 
48
48
  # Builder pattern (mirrors Java ParagraphBuilder)
49
- paragraph = (client.paragraph_builder()
49
+ paragraph = (client._paragraph_builder()
50
50
  .from_string("Text content")
51
51
  .with_font(Font("Arial", 12))
52
52
  .with_position(Position.at_page_coordinates(0, 100, 200))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pdfdancer-client-python
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Python client for PDFDancer API
5
5
  Author-email: "The Famous Cat Ltd." <hi@thefamouscat.com>
6
6
  License: MIT
@@ -63,7 +63,7 @@ client._delete(paragraphs[0])
63
63
  client._move(images[0], Position.at_page_coordinates(0, 100, 200))
64
64
 
65
65
  # Builder pattern (mirrors Java ParagraphBuilder)
66
- paragraph = (client.paragraph_builder()
66
+ paragraph = (client._paragraph_builder()
67
67
  .from_string("Hello World")
68
68
  .with_font(Font("Arial", 12))
69
69
  .with_color(Color(255, 0, 0))
@@ -141,25 +141,26 @@ result = client.modify_text_line(ref, "new text")
141
141
  ```
142
142
 
143
143
  ### Builder Pattern
144
+
144
145
  ```python
145
146
  # Java: client.paragraphBuilder()
146
- builder = client.paragraph_builder()
147
+ builder = client._paragraph_builder()
147
148
 
148
149
  # Fluent interface (mirrors Java ParagraphBuilder)
149
150
  paragraph = (builder
150
- .from_string("Text content") # Java: fromString()
151
- .with_font(Font("Arial", 12)) # Java: withFont()
152
- .with_color(Color(255, 0, 0)) # Java: withColor()
153
- .with_line_spacing(1.5) # Java: withLineSpacing()
154
- .with_position(position) # Java: withPosition()
155
- .build()) # Java: build()
151
+ .from_string("Text content") # Java: fromString()
152
+ .with_font(Font("Arial", 12)) # Java: withFont()
153
+ .with_color(Color(255, 0, 0)) # Java: withColor()
154
+ .with_line_spacing(1.5) # Java: withLineSpacing()
155
+ .with_position(position) # Java: withPosition()
156
+ .build()) # Java: build()
156
157
 
157
158
  # Font file registration (Java: withFont(File, double))
158
159
  paragraph = (builder
159
- .with_font_file("custom.ttf", 14.0) # Java: withFont(File, double)
160
- .from_string("Custom font text")
161
- .with_position(position)
162
- .build())
160
+ .with_font_file("custom.ttf", 14.0) # Java: withFont(File, double)
161
+ .from_string("Custom font text")
162
+ .with_position(position)
163
+ .build())
163
164
  ```
164
165
 
165
166
  ### Position API
@@ -37,7 +37,7 @@ client._delete(paragraphs[0])
37
37
  client._move(images[0], Position.at_page_coordinates(0, 100, 200))
38
38
 
39
39
  # Builder pattern (mirrors Java ParagraphBuilder)
40
- paragraph = (client.paragraph_builder()
40
+ paragraph = (client._paragraph_builder()
41
41
  .from_string("Hello World")
42
42
  .with_font(Font("Arial", 12))
43
43
  .with_color(Color(255, 0, 0))
@@ -115,25 +115,26 @@ result = client.modify_text_line(ref, "new text")
115
115
  ```
116
116
 
117
117
  ### Builder Pattern
118
+
118
119
  ```python
119
120
  # Java: client.paragraphBuilder()
120
- builder = client.paragraph_builder()
121
+ builder = client._paragraph_builder()
121
122
 
122
123
  # Fluent interface (mirrors Java ParagraphBuilder)
123
124
  paragraph = (builder
124
- .from_string("Text content") # Java: fromString()
125
- .with_font(Font("Arial", 12)) # Java: withFont()
126
- .with_color(Color(255, 0, 0)) # Java: withColor()
127
- .with_line_spacing(1.5) # Java: withLineSpacing()
128
- .with_position(position) # Java: withPosition()
129
- .build()) # Java: build()
125
+ .from_string("Text content") # Java: fromString()
126
+ .with_font(Font("Arial", 12)) # Java: withFont()
127
+ .with_color(Color(255, 0, 0)) # Java: withColor()
128
+ .with_line_spacing(1.5) # Java: withLineSpacing()
129
+ .with_position(position) # Java: withPosition()
130
+ .build()) # Java: build()
130
131
 
131
132
  # Font file registration (Java: withFont(File, double))
132
133
  paragraph = (builder
133
- .with_font_file("custom.ttf", 14.0) # Java: withFont(File, double)
134
- .from_string("Custom font text")
135
- .with_position(position)
136
- .build())
134
+ .with_font_file("custom.ttf", 14.0) # Java: withFont(File, double)
135
+ .from_string("Custom font text")
136
+ .with_position(position)
137
+ .build())
137
138
  ```
138
139
 
139
140
  ### Position API
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pdfdancer-client-python"
7
- version = "0.2.2"
7
+ version = "0.2.3"
8
8
  description = "Python client for PDFDancer API"
9
9
  readme = "README.md"
10
10
  authors = [
@@ -6,6 +6,7 @@ Provides session-based PDF manipulation operations with strict validation.
6
6
  """
7
7
 
8
8
  import json
9
+ import os
9
10
  from pathlib import Path
10
11
  from typing import List, Optional, Union, BinaryIO
11
12
 
@@ -116,11 +117,33 @@ class PDFDancer:
116
117
  @classmethod
117
118
  def open(cls,
118
119
  pdf_data: Union[bytes, Path, str, BinaryIO],
119
- token: str,
120
- base_url: str = "http://localhost:8080",
120
+ token: Optional[str] = None,
121
+ base_url: Optional[str] = None,
121
122
  timeout: float = 30.0) -> "PDFDancer":
123
+ """
124
+ Create a client session, falling back to environment variables when needed.
125
+
126
+ Args:
127
+ pdf_data: PDF payload supplied directly or via filesystem handles.
128
+ token: Override for the API token; falls back to `PDFDANCER_TOKEN` environement variable.
129
+ base_url: Override for the API base URL; falls back to `PDFDANCER_BASE_URL`
130
+ or defaults to `https://api.pdfdancer.com`.
131
+ timeout: HTTP read timeout in seconds.
132
+
133
+ Returns:
134
+ A ready-to-use `PDFDancer` client instance.
135
+ """
136
+ resolved_token = token.strip() if token and token.strip() else None
137
+ if resolved_token is None:
138
+ env_token = os.getenv("PDFDANCER_TOKEN")
139
+ resolved_token = env_token.strip() if env_token and env_token.strip() else None
140
+
141
+ env_base_url = os.getenv("PDFDANCER_BASE_URL")
142
+ resolved_base_url = base_url or (env_base_url.strip() if env_base_url and env_base_url.strip() else None)
143
+ if resolved_base_url is None:
144
+ resolved_base_url = "https://api.pdfdancer.com"
122
145
 
123
- return PDFDancer(token, pdf_data, base_url, timeout)
146
+ return PDFDancer(resolved_token, pdf_data, resolved_base_url, timeout)
124
147
 
125
148
  def __init__(self, token: str, pdf_data: Union[bytes, Path, str, BinaryIO],
126
149
  base_url: str, read_timeout: float = 0):
@@ -811,7 +834,7 @@ class PDFDancer:
811
834
 
812
835
  # Builder Pattern Support
813
836
 
814
- def paragraph_builder(self) -> 'ParagraphBuilder':
837
+ def _paragraph_builder(self) -> 'ParagraphBuilder':
815
838
  """
816
839
  Creates a new ParagraphBuilder for fluent paragraph construction.
817
840
  Returns:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pdfdancer-client-python
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Python client for PDFDancer API
5
5
  Author-email: "The Famous Cat Ltd." <hi@thefamouscat.com>
6
6
  License: MIT
@@ -63,7 +63,7 @@ client._delete(paragraphs[0])
63
63
  client._move(images[0], Position.at_page_coordinates(0, 100, 200))
64
64
 
65
65
  # Builder pattern (mirrors Java ParagraphBuilder)
66
- paragraph = (client.paragraph_builder()
66
+ paragraph = (client._paragraph_builder()
67
67
  .from_string("Hello World")
68
68
  .with_font(Font("Arial", 12))
69
69
  .with_color(Color(255, 0, 0))
@@ -141,25 +141,26 @@ result = client.modify_text_line(ref, "new text")
141
141
  ```
142
142
 
143
143
  ### Builder Pattern
144
+
144
145
  ```python
145
146
  # Java: client.paragraphBuilder()
146
- builder = client.paragraph_builder()
147
+ builder = client._paragraph_builder()
147
148
 
148
149
  # Fluent interface (mirrors Java ParagraphBuilder)
149
150
  paragraph = (builder
150
- .from_string("Text content") # Java: fromString()
151
- .with_font(Font("Arial", 12)) # Java: withFont()
152
- .with_color(Color(255, 0, 0)) # Java: withColor()
153
- .with_line_spacing(1.5) # Java: withLineSpacing()
154
- .with_position(position) # Java: withPosition()
155
- .build()) # Java: build()
151
+ .from_string("Text content") # Java: fromString()
152
+ .with_font(Font("Arial", 12)) # Java: withFont()
153
+ .with_color(Color(255, 0, 0)) # Java: withColor()
154
+ .with_line_spacing(1.5) # Java: withLineSpacing()
155
+ .with_position(position) # Java: withPosition()
156
+ .build()) # Java: build()
156
157
 
157
158
  # Font file registration (Java: withFont(File, double))
158
159
  paragraph = (builder
159
- .with_font_file("custom.ttf", 14.0) # Java: withFont(File, double)
160
- .from_string("Custom font text")
161
- .with_position(position)
162
- .build())
160
+ .with_font_file("custom.ttf", 14.0) # Java: withFont(File, double)
161
+ .from_string("Custom font text")
162
+ .with_position(position)
163
+ .build())
163
164
  ```
164
165
 
165
166
  ### Position API
@@ -5,7 +5,6 @@ pyproject.toml
5
5
  release.py
6
6
  requirements-dev.txt
7
7
  requirements.txt
8
- test.py
9
8
  .github/workflows/ci.yml
10
9
  docs/openapi.yml
11
10
  src/pdfdancer/__init__.py
@@ -1,455 +0,0 @@
1
- """
2
- Comprehensive test showcasing all PDFDancer Python API features.
3
- This file demonstrates the complete functionality of the pdfdancer library.
4
- """
5
-
6
- from pathlib import Path
7
-
8
- from pdfdancer import (
9
- ClientV1, Font, Position, Color, Image, Paragraph, ObjectType,
10
- Point, BoundingRect, PositionMode, ShapeType,
11
- FontNotFoundException
12
- )
13
-
14
-
15
- # noinspection PyUnusedLocal
16
- def find_operations(client):
17
- """Test all find operations for different object types."""
18
- # Find all paragraphs
19
- paragraphs = client._find_paragraphs()
20
-
21
- # Find images
22
- images = client._find_images()
23
-
24
- # Find forms
25
- forms = client._find_form_x_objects()
26
-
27
- # Find paths
28
- paths = client._find_paths()
29
-
30
- # Find text lines
31
- text_lines = client._find_text_lines()
32
-
33
- # Find with specific object type and position
34
- specific_position = Position.at_page_coordinates(0, 100, 100)
35
- paragraphs_at_pos = client._find(ObjectType.PARAGRAPH, specific_position)
36
-
37
- return paragraphs, text_lines # Keep only what's needed for object_manipulation
38
-
39
-
40
- # noinspection PyUnusedLocal
41
- def page_management(client):
42
- """Test page management operations."""
43
- # Get all pages
44
- pages = client.get_pages()
45
-
46
- # Get specific page
47
- first_page = None
48
- if pages:
49
- first_page = client._get_page(0)
50
-
51
-
52
- # noinspection PyUnusedLocal
53
- def position_system(client):
54
- """Test position and coordinate system operations."""
55
- # Different ways to create positions
56
- page_pos = Position.at_page(0)
57
- coord_pos = Position.at_page_coordinates(0, 50, 100)
58
-
59
- # Position manipulation
60
- moved_pos = coord_pos.copy()
61
- moved_pos.move_x(25.0)
62
- moved_pos.move_y(15.0)
63
-
64
- # Point and BoundingRect
65
- point = Point(10.0, 20.0)
66
- bounding_rect = BoundingRect(0.0, 0.0, 100.0, 50.0)
67
-
68
-
69
- # noinspection PyUnusedLocal
70
- def font_management(client):
71
- """Test font creation and management operations."""
72
- # Create fonts
73
- arial_font = Font("Arial", 12.0)
74
- times_font = Font("Times New Roman", 14.0)
75
-
76
- # Find fonts in document
77
- found_fonts = None
78
- try:
79
- found_fonts = client.find_fonts("Arial", 12)
80
- except FontNotFoundException as e:
81
- pass
82
- except Exception as e:
83
- pass
84
-
85
-
86
- # noinspection PyUnusedLocal
87
- def color_system(client):
88
- """Test color creation and management operations."""
89
- # Create colors
90
- red_color = Color(255, 0, 0)
91
- blue_color = Color(0, 0, 255, 128) # With alpha
92
-
93
-
94
- # noinspection PyUnusedLocal
95
- def paragraph_operations(client):
96
- """Test paragraph builder and manipulation operations."""
97
- arial_font = Font("Arial", 12.0)
98
- arial_font_large = Font("Arial", 14.0) # Use Arial instead of Times New Roman
99
- red_color = Color(255, 0, 0)
100
- blue_color = Color(0, 0, 255, 128)
101
-
102
- # Basic paragraph creation
103
- basic_paragraph = (client.paragraph_builder()
104
- .from_string("Basic paragraph with default settings")
105
- .with_font(arial_font)
106
- .with_position(Position.at_page_coordinates(0, 50, 200))
107
- .build())
108
- client._add_paragraph(basic_paragraph)
109
-
110
- # Advanced paragraph with all features
111
- advanced_paragraph = (client.paragraph_builder()
112
- .from_string("Advanced paragraph\nwith multiple lines\nand styling", red_color)
113
- .with_font(arial_font_large)
114
- .with_line_spacing(1.5)
115
- .with_color(blue_color)
116
- .with_position(Position.at_page_coordinates(0, 50, 250))
117
- .build())
118
- client._add_paragraph(advanced_paragraph)
119
-
120
-
121
- # noinspection PyUnusedLocal
122
- def object_manipulation(client, paragraphs, text_lines):
123
- """Test object manipulation operations including delete, move, and modify."""
124
- arial_font = Font("Arial", 14.0) # Use Arial instead of Times New Roman
125
- red_color = Color(255, 0, 0)
126
-
127
- if paragraphs:
128
- # Delete operation
129
- first_paragraph = paragraphs[0]
130
- client._delete(first_paragraph)
131
-
132
- # Move operation (if we have more paragraphs)
133
- if len(paragraphs) > 1:
134
- second_paragraph = paragraphs[1]
135
- new_position = Position.at_page_coordinates(0, 100, 300)
136
- client._move(second_paragraph, new_position)
137
-
138
- # Modify operations
139
- if len(paragraphs) > 2:
140
- third_paragraph = paragraphs[2]
141
-
142
- # Modify with new paragraph object
143
- modified_paragraph = Paragraph(
144
- position=Position.at_page_coordinates(0, 50, 350),
145
- text_lines=["Modified paragraph text"],
146
- font=arial_font,
147
- color=red_color
148
- )
149
- client._modify_paragraph(third_paragraph, modified_paragraph)
150
-
151
- # Modify with just text
152
- if len(paragraphs) > 3:
153
- fourth_paragraph = paragraphs[3]
154
- client._modify_paragraph(fourth_paragraph, "Simple text modification")
155
-
156
- # Text line modification
157
- if text_lines:
158
- client.modify_text_line(text_lines[0], "Modified text line")
159
-
160
-
161
- # noinspection PyUnusedLocal
162
- def text_line_operations(client):
163
- """Test text line specific operations and showcases."""
164
- # Find all text lines in document
165
- all_text_lines = client._find_text_lines()
166
-
167
- # Find text lines at specific position
168
- specific_position = Position.at_page_coordinates(0, 100, 100)
169
- text_lines_at_position = client._find_text_lines(specific_position)
170
-
171
- # Find text lines on entire page
172
- page_position = Position.at_page(0)
173
- text_lines_on_page = client._find_text_lines(page_position)
174
-
175
- # Demonstrate text line modification if text lines are found
176
- modified_text_line = None
177
- if all_text_lines:
178
- first_text_line = all_text_lines[0]
179
-
180
- # Get text line properties via ObjectRef
181
- text_line_id = first_text_line.get_internal_id()
182
- text_line_type = first_text_line.get_type()
183
- text_line_position = first_text_line.get_position()
184
-
185
- # Modify text line content
186
- client.modify_text_line(first_text_line, "Modified text line content")
187
- modified_text_line = first_text_line
188
-
189
- # Demonstrate position modification for text lines
190
- if len(all_text_lines) > 1:
191
- second_text_line = all_text_lines[1]
192
- new_position = Position.at_page_coordinates(0, 120, 150)
193
- client._move(second_text_line, new_position)
194
-
195
-
196
- # noinspection PyUnusedLocal
197
- def form_operations(client):
198
- """Test form handling operations."""
199
- # Find all forms in document
200
- all_forms = client._find_form_x_objects()
201
-
202
- # Find forms at specific position
203
- specific_position = Position.at_page_coordinates(0, 150, 200)
204
- forms_at_position = client._find_form_x_objects(specific_position)
205
-
206
- # Find forms on entire page
207
- page_position = Position.at_page(0)
208
- forms_on_page = client._find_form_x_objects(page_position)
209
-
210
- # Demonstrate form manipulation if forms are found
211
- manipulated_form = None
212
- if all_forms:
213
- first_form = all_forms[0]
214
-
215
- # Get form properties via ObjectRef
216
- form_id = first_form.get_internal_id()
217
- form_type = first_form.get_type()
218
- form_position = first_form.get_position()
219
-
220
- # Demonstrate position modification for forms
221
- if len(all_forms) > 1:
222
- second_form = all_forms[1]
223
- new_position = Position.at_page_coordinates(0, 180, 250)
224
- client._move(second_form, new_position)
225
- manipulated_form = second_form
226
-
227
- # Demonstrate deletion (use last form to preserve others for testing)
228
- if len(all_forms) > 2:
229
- last_form = all_forms[-1]
230
- client._delete(last_form)
231
-
232
-
233
- # noinspection PyUnusedLocal
234
- def path_operations(client):
235
- """Test path handling operations."""
236
- # Find all paths in document
237
- all_paths = client._find_paths()
238
-
239
- # Find paths at specific position
240
- specific_position = Position.at_page_coordinates(0, 100, 150)
241
- paths_at_position = client._find_paths(specific_position)
242
-
243
- # Find paths on entire page
244
- page_position = Position.at_page(0)
245
- paths_on_page = client._find_paths(page_position)
246
-
247
- # Demonstrate path manipulation if paths are found
248
- manipulated_path = None
249
- if all_paths:
250
- first_path = all_paths[0]
251
-
252
- # Get path properties via ObjectRef
253
- path_id = first_path.get_internal_id()
254
- path_type = first_path.get_type()
255
- path_position = first_path.get_position()
256
-
257
- # Demonstrate position modification for paths
258
- if len(all_paths) > 1:
259
- second_path = all_paths[1]
260
- new_position = Position.at_page_coordinates(0, 120, 180)
261
- client._move(second_path, new_position)
262
- manipulated_path = second_path
263
-
264
- # Demonstrate deletion (use last path to preserve others for testing)
265
- if len(all_paths) > 2:
266
- last_path = all_paths[-1]
267
- client._delete(last_path)
268
-
269
-
270
- # noinspection PyUnusedLocal
271
- def register_font_operations(client):
272
- """Test font registration operations using JetBrainsMono-Regular.ttf."""
273
- # Register font using file path
274
- font_path = Path("tests/fixtures/JetBrainsMono-Regular.ttf")
275
- registered_font_name = client.register_font(font_path)
276
-
277
- # Create a Font object using the registered font
278
- jetbrains_font = Font(registered_font_name, 14.0)
279
-
280
- # Use the registered font in a paragraph
281
- paragraph_with_custom_font = (client.paragraph_builder()
282
- .from_string("This text uses JetBrains Mono font")
283
- .with_font(jetbrains_font)
284
- .with_position(Position.at_page_coordinates(0, 50, 450))
285
- .build())
286
- client._add_paragraph(paragraph_with_custom_font)
287
-
288
- # Register font using bytes (alternative method)
289
- with open(font_path, 'rb') as f:
290
- font_bytes = f.read()
291
- registered_font_name_bytes = client.register_font(font_bytes)
292
-
293
- # Create another Font object using the bytes-registered font
294
- jetbrains_font_bytes = Font(registered_font_name_bytes, 16.0)
295
-
296
-
297
- # noinspection PyUnusedLocal
298
- def image_operations(client):
299
- """Test image handling operations using logo-80.png."""
300
- # Load actual image file
301
- image_path = Path("tests/fixtures/logo-80.png")
302
- with open(image_path, 'rb') as f:
303
- image_bytes = f.read()
304
-
305
- # Create image object with real image data
306
- logo_image = Image(
307
- position=Position.at_page_coordinates(0, 200, 400),
308
- format="image/png",
309
- width=80.0,
310
- height=80.0,
311
- data=image_bytes
312
- )
313
-
314
- # Add image to PDF
315
- client._add_image(logo_image)
316
-
317
- # Find images in document
318
- found_images = client._find_images()
319
-
320
- # Demonstrate image manipulation if images are found
321
- if found_images:
322
- first_image = found_images[0]
323
-
324
- # Get image properties via ObjectRef
325
- image_id = first_image.get_internal_id()
326
- image_type = first_image.get_type()
327
- image_position = first_image.get_position()
328
-
329
- # Move image to a new position
330
- new_image_position = Position.at_page_coordinates(0, 250, 450)
331
- client._move(first_image, new_image_position)
332
-
333
-
334
- # noinspection PyUnusedLocal
335
- def objectref_operations(client, paragraphs):
336
- """Test ObjectRef operations."""
337
- if paragraphs and len(paragraphs) > 1: # Use remaining paragraphs after deletion
338
- obj_ref = paragraphs[1]
339
- internal_id = obj_ref.get_internal_id()
340
- obj_type = obj_ref.get_type()
341
- position = obj_ref.get_position()
342
- obj_dict = obj_ref.to_dict()
343
-
344
- # Set new position
345
- new_ref_position = Position.at_page_coordinates(0, 75, 125)
346
- obj_ref.set_position(new_ref_position)
347
- updated_position = obj_ref.get_position()
348
-
349
-
350
- # noinspection PyUnusedLocal
351
- def enumerations(client):
352
- """Test enumeration demonstrations."""
353
- object_types = list(ObjectType)
354
- position_modes = list(PositionMode)
355
- shape_types = list(ShapeType)
356
-
357
-
358
- # noinspection PyUnusedLocal
359
- def error_handling(client):
360
- """Test error handling demonstration."""
361
- exception_caught = None
362
- try:
363
- # This might trigger a font not found error
364
- client.find_fonts("NonExistentFont", 12)
365
- except FontNotFoundException as e:
366
- exception_caught = e
367
- except Exception as e:
368
- exception_caught = e
369
-
370
-
371
- # noinspection PyUnusedLocal
372
- def pdf_operations(client):
373
- """Test PDF operations."""
374
- # Get PDF data
375
- pdf_data = client.get_pdf_file()
376
-
377
- # Save PDF (original functionality + comprehensive output)
378
- output_path = "comprehensive_output.pdf"
379
- client.save_pdf(output_path)
380
- client.save_pdf("output.pdf") # Keep original output file
381
-
382
- # Verify file exists
383
- file_exists = Path(output_path).exists()
384
- file_size = Path(output_path).stat().st_size if file_exists else 0
385
-
386
-
387
- def context_management():
388
- """Test context manager capabilities."""
389
- with open("jwt-token-mlahr-20250829-160417.txt", "r", encoding="utf-8") as f:
390
- token = f.read()
391
-
392
- # Context manager automatically handles session lifecycle
393
- with ClientV1(token=token, pdf_data="tests/fixtures/ObviouslyAwesome.pdf") as client:
394
- paragraphs = client.find_paragraphs()
395
-
396
- # Builder pattern works seamlessly inside context (original functionality preserved)
397
- paragraph = (client.paragraph_builder()
398
- .from_string("Context managed")
399
- .with_font(Font("Arial", 12))
400
- .with_position(Position.at_page_coordinates(0, 50, 10))
401
- .build())
402
-
403
- client._add_paragraph(paragraph)
404
-
405
- return len(paragraphs)
406
-
407
-
408
- def advanced_positioning():
409
- """Test advanced positioning features."""
410
- # Create positions using different methods
411
- positions = [
412
- Position.at_page(0),
413
- Position.at_page_coordinates(1, 100, 200),
414
- Position.at_page_coordinates(2, 150, 250)
415
- ]
416
-
417
- modified_positions = []
418
- for pos in positions:
419
- # Demonstrate position copying and modification
420
- copied_pos = pos.copy()
421
- copied_pos.move_x(25).move_y(35)
422
- modified_positions.append(copied_pos)
423
-
424
- return positions, modified_positions
425
-
426
-
427
- def main():
428
- """Main function demonstrating all API features."""
429
- with open("jwt-token-mlahr-20250829-160417.txt", "r", encoding="utf-8") as f:
430
- token = f.read()
431
-
432
- with ClientV1(token=token, pdf_data="tests/fixtures/ObviouslyAwesome.pdf", read_timeout=30.0) as client:
433
- # Execute all test operations
434
- paragraphs, text_lines = find_operations(client)
435
- page_management(client)
436
- position_system(client)
437
- font_management(client)
438
- register_font_operations(client)
439
- color_system(client)
440
- paragraph_operations(client)
441
- object_manipulation(client, paragraphs, text_lines)
442
- text_line_operations(client)
443
- form_operations(client)
444
- path_operations(client)
445
- image_operations(client)
446
- objectref_operations(client, paragraphs)
447
- enumerations(client)
448
- error_handling(client)
449
- pdf_operations(client)
450
-
451
-
452
- if __name__ == "__main__":
453
- main()
454
- context_management()
455
- advanced_positioning()