pdfdancer-client-python 3.0.1__tar.gz → 3.0.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.
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/PKG-INFO +19 -5
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/README.md +18 -4
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/src/pdfdancer/__init__.py +20 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/src/pdfdancer/_version.py +2 -2
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/src/pdfdancer/models.py +113 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/src/pdfdancer/pdfdancer_v2.py +88 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/src/pdfdancer_client_python.egg-info/PKG-INFO +19 -5
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/.autosnap.toml +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/.flake8 +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/.github/dependabot.yml +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/.github/workflows/ci.yml +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/.github/workflows/daily-tests.yml +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/.github/workflows/release.yml +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/.github/workflows/sdk-backward-compat.yml +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/.gitignore +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/.gitmodules +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/LICENSE +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/MANIFEST.in +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/Makefile +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/NOTICE +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/TODO.md +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/check.py +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/docs/capabilities/.gitkeep +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/docs/capabilities/CLEAR_CLIPPING.md +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/media/logo-orange-512h.webp +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/media/logo-orange-60h.webp +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/media/logo-silver-512h.webp +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/media/logo-silver-60h.webp +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/pyproject.toml +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/setup.cfg +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/src/pdfdancer/_runtime_version.py +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/src/pdfdancer/exceptions.py +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/src/pdfdancer/fingerprint.py +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/src/pdfdancer/image_builder.py +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/src/pdfdancer/page_builder.py +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/src/pdfdancer/path_builder.py +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/src/pdfdancer/text_editing.py +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/src/pdfdancer/types.py +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/src/pdfdancer_client_python.egg-info/SOURCES.txt +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/src/pdfdancer_client_python.egg-info/dependency_links.txt +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/src/pdfdancer_client_python.egg-info/requires.txt +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/src/pdfdancer_client_python.egg-info/top_level.txt +0 -0
- {pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/test.sh +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pdfdancer-client-python
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.2
|
|
4
4
|
Summary: Python client for PDFDancer API
|
|
5
5
|
Author-email: "The Famous Cat Ltd." <hi@thefamouscat.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -36,7 +36,7 @@ Dynamic: license-file
|
|
|
36
36
|
|
|
37
37
|
# PDFDancer Python Client
|
|
38
38
|
|
|
39
|
-
This README documents `pdfdancer-client-python` version `3.0.
|
|
39
|
+
This README documents `pdfdancer-client-python` version `3.0.2`.
|
|
40
40
|
|
|
41
41
|

|
|
42
42
|
|
|
@@ -69,7 +69,7 @@ pixel-perfect control from Python. The same API is also available for TypeScript
|
|
|
69
69
|
## Installation
|
|
70
70
|
|
|
71
71
|
```bash
|
|
72
|
-
pip install pdfdancer-client-python==3.0.
|
|
72
|
+
pip install pdfdancer-client-python==3.0.2
|
|
73
73
|
|
|
74
74
|
# Editable install for local development
|
|
75
75
|
pip install -e .
|
|
@@ -139,6 +139,20 @@ snapshot = first_page.get_snapshot()
|
|
|
139
139
|
|
|
140
140
|
Page-scoped selectors, text editing, and builders automatically restrict the operation to that page.
|
|
141
141
|
|
|
142
|
+
## Reading Units
|
|
143
|
+
|
|
144
|
+
Reading-unit analysis returns semantically classified text blocks with reading order, source provenance, bounds, and
|
|
145
|
+
relationships. Each call analyzes the current session state and makes a fresh request.
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
document_analysis = pdf.analyze_reading_units()
|
|
149
|
+
page_analysis = pdf.page(1).analyze_reading_units()
|
|
150
|
+
|
|
151
|
+
for page in document_analysis.pages:
|
|
152
|
+
for unit in page.units:
|
|
153
|
+
print(unit.role, unit.text)
|
|
154
|
+
```
|
|
155
|
+
|
|
142
156
|
## Selection
|
|
143
157
|
|
|
144
158
|
Document- and page-scoped selectors return typed objects for images, paths, form XObjects, and form fields. Position
|
|
@@ -412,8 +426,8 @@ Releases are published automatically to PyPI when a `v*` tag is pushed to GitHub
|
|
|
412
426
|
|
|
413
427
|
```bash
|
|
414
428
|
# Create and push a release tag — GitHub Actions handles the rest
|
|
415
|
-
git tag
|
|
416
|
-
git push origin
|
|
429
|
+
git tag v3.0.2
|
|
430
|
+
git push origin v3.0.2
|
|
417
431
|
```
|
|
418
432
|
|
|
419
433
|
#### Code Quality
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# PDFDancer Python Client
|
|
2
2
|
|
|
3
|
-
This README documents `pdfdancer-client-python` version `3.0.
|
|
3
|
+
This README documents `pdfdancer-client-python` version `3.0.2`.
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|
|
|
@@ -33,7 +33,7 @@ pixel-perfect control from Python. The same API is also available for TypeScript
|
|
|
33
33
|
## Installation
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
|
-
pip install pdfdancer-client-python==3.0.
|
|
36
|
+
pip install pdfdancer-client-python==3.0.2
|
|
37
37
|
|
|
38
38
|
# Editable install for local development
|
|
39
39
|
pip install -e .
|
|
@@ -103,6 +103,20 @@ snapshot = first_page.get_snapshot()
|
|
|
103
103
|
|
|
104
104
|
Page-scoped selectors, text editing, and builders automatically restrict the operation to that page.
|
|
105
105
|
|
|
106
|
+
## Reading Units
|
|
107
|
+
|
|
108
|
+
Reading-unit analysis returns semantically classified text blocks with reading order, source provenance, bounds, and
|
|
109
|
+
relationships. Each call analyzes the current session state and makes a fresh request.
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
document_analysis = pdf.analyze_reading_units()
|
|
113
|
+
page_analysis = pdf.page(1).analyze_reading_units()
|
|
114
|
+
|
|
115
|
+
for page in document_analysis.pages:
|
|
116
|
+
for unit in page.units:
|
|
117
|
+
print(unit.role, unit.text)
|
|
118
|
+
```
|
|
119
|
+
|
|
106
120
|
## Selection
|
|
107
121
|
|
|
108
122
|
Document- and page-scoped selectors return typed objects for images, paths, form XObjects, and form fields. Position
|
|
@@ -376,8 +390,8 @@ Releases are published automatically to PyPI when a `v*` tag is pushed to GitHub
|
|
|
376
390
|
|
|
377
391
|
```bash
|
|
378
392
|
# Create and push a release tag — GitHub Actions handles the rest
|
|
379
|
-
git tag
|
|
380
|
-
git push origin
|
|
393
|
+
git tag v3.0.2
|
|
394
|
+
git push origin v3.0.2
|
|
381
395
|
```
|
|
382
396
|
|
|
383
397
|
#### Code Quality
|
|
@@ -42,6 +42,16 @@ from .models import (
|
|
|
42
42
|
Point,
|
|
43
43
|
Position,
|
|
44
44
|
PositionMode,
|
|
45
|
+
ReadingUnit,
|
|
46
|
+
ReadingUnitBounds,
|
|
47
|
+
ReadingUnitDocumentAnalysis,
|
|
48
|
+
ReadingUnitMode,
|
|
49
|
+
ReadingUnitPageAnalysis,
|
|
50
|
+
ReadingUnitProvenance,
|
|
51
|
+
ReadingUnitRelationship,
|
|
52
|
+
ReadingUnitRelationshipType,
|
|
53
|
+
ReadingUnitRole,
|
|
54
|
+
ReadingUnitStreamMembership,
|
|
45
55
|
ShapeType,
|
|
46
56
|
Size,
|
|
47
57
|
StandardFonts,
|
|
@@ -116,6 +126,16 @@ __all__ = [
|
|
|
116
126
|
"Point",
|
|
117
127
|
"StandardFonts",
|
|
118
128
|
"PageSize",
|
|
129
|
+
"ReadingUnit",
|
|
130
|
+
"ReadingUnitBounds",
|
|
131
|
+
"ReadingUnitDocumentAnalysis",
|
|
132
|
+
"ReadingUnitMode",
|
|
133
|
+
"ReadingUnitPageAnalysis",
|
|
134
|
+
"ReadingUnitProvenance",
|
|
135
|
+
"ReadingUnitRelationship",
|
|
136
|
+
"ReadingUnitRelationshipType",
|
|
137
|
+
"ReadingUnitRole",
|
|
138
|
+
"ReadingUnitStreamMembership",
|
|
119
139
|
"Orientation",
|
|
120
140
|
"FontRecommendation",
|
|
121
141
|
"FontType",
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '3.0.
|
|
22
|
-
__version_tuple__ = version_tuple = (3, 0,
|
|
21
|
+
__version__ = version = '3.0.2'
|
|
22
|
+
__version_tuple__ = version_tuple = (3, 0, 2)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -268,6 +268,119 @@ class ShapeType(Enum):
|
|
|
268
268
|
RECT = "RECT" # Rectangular area with width and height
|
|
269
269
|
|
|
270
270
|
|
|
271
|
+
class ReadingUnitRole(Enum):
|
|
272
|
+
HEADING = "HEADING"
|
|
273
|
+
PARAGRAPH = "PARAGRAPH"
|
|
274
|
+
LIST = "LIST"
|
|
275
|
+
TABLE = "TABLE"
|
|
276
|
+
CAPTION = "CAPTION"
|
|
277
|
+
CALLOUT = "CALLOUT"
|
|
278
|
+
PREFORMATTED_TEXT = "PREFORMATTED_TEXT"
|
|
279
|
+
PAGE_HEADER = "PAGE_HEADER"
|
|
280
|
+
PAGE_FOOTER = "PAGE_FOOTER"
|
|
281
|
+
PAGE_NUMBER = "PAGE_NUMBER"
|
|
282
|
+
FOOTNOTE = "FOOTNOTE"
|
|
283
|
+
WATERMARK = "WATERMARK"
|
|
284
|
+
UNKNOWN = "UNKNOWN"
|
|
285
|
+
|
|
286
|
+
@classmethod
|
|
287
|
+
def from_value(cls, value: Any) -> tuple["ReadingUnitRole", str]:
|
|
288
|
+
raw = value if isinstance(value, str) else "UNKNOWN"
|
|
289
|
+
try:
|
|
290
|
+
return cls(raw), raw
|
|
291
|
+
except ValueError:
|
|
292
|
+
return cls.UNKNOWN, raw
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
class ReadingUnitRelationshipType(Enum):
|
|
296
|
+
CAPTION_FOR = "CAPTION_FOR"
|
|
297
|
+
HEADING_PARENT_OF = "HEADING_PARENT_OF"
|
|
298
|
+
UNKNOWN = "UNKNOWN"
|
|
299
|
+
|
|
300
|
+
@classmethod
|
|
301
|
+
def from_value(cls, value: Any) -> tuple["ReadingUnitRelationshipType", str]:
|
|
302
|
+
raw = value if isinstance(value, str) else "UNKNOWN"
|
|
303
|
+
try:
|
|
304
|
+
return cls(raw), raw
|
|
305
|
+
except ValueError:
|
|
306
|
+
return cls.UNKNOWN, raw
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
class ReadingUnitMode(Enum):
|
|
310
|
+
PRIMARY = "PRIMARY"
|
|
311
|
+
UNKNOWN = "UNKNOWN"
|
|
312
|
+
|
|
313
|
+
@classmethod
|
|
314
|
+
def from_value(cls, value: Any) -> tuple["ReadingUnitMode", str]:
|
|
315
|
+
raw = value if isinstance(value, str) else "UNKNOWN"
|
|
316
|
+
try:
|
|
317
|
+
return cls(raw), raw
|
|
318
|
+
except ValueError:
|
|
319
|
+
return cls.UNKNOWN, raw
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
@dataclass(frozen=True)
|
|
323
|
+
class ReadingUnitBounds:
|
|
324
|
+
x: float
|
|
325
|
+
y: float
|
|
326
|
+
width: float
|
|
327
|
+
height: float
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
@dataclass(frozen=True)
|
|
331
|
+
class ReadingUnitProvenance:
|
|
332
|
+
page_number: int
|
|
333
|
+
source_element_ids: List[str]
|
|
334
|
+
bounds: ReadingUnitBounds
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
@dataclass(frozen=True)
|
|
338
|
+
class ReadingUnitStreamMembership:
|
|
339
|
+
included: bool
|
|
340
|
+
order: Optional[int]
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
@dataclass(frozen=True)
|
|
344
|
+
class ReadingUnitRelationship:
|
|
345
|
+
type: ReadingUnitRelationshipType
|
|
346
|
+
raw_type: str
|
|
347
|
+
target_unit_id: str
|
|
348
|
+
|
|
349
|
+
@classmethod
|
|
350
|
+
def from_dict(cls, data: Mapping[str, Any]) -> "ReadingUnitRelationship":
|
|
351
|
+
relationship_type, raw_type = ReadingUnitRelationshipType.from_value(
|
|
352
|
+
data.get("type")
|
|
353
|
+
)
|
|
354
|
+
return cls(relationship_type, raw_type, str(data.get("targetUnitId", "")))
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
@dataclass(frozen=True)
|
|
358
|
+
class ReadingUnit:
|
|
359
|
+
id: str
|
|
360
|
+
role: ReadingUnitRole
|
|
361
|
+
raw_role: str
|
|
362
|
+
text: str
|
|
363
|
+
stream: Mapping[str, ReadingUnitStreamMembership]
|
|
364
|
+
provenance: ReadingUnitProvenance
|
|
365
|
+
relationships: List[ReadingUnitRelationship]
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
@dataclass(frozen=True)
|
|
369
|
+
class ReadingUnitPageAnalysis:
|
|
370
|
+
page_number: int
|
|
371
|
+
mode: ReadingUnitMode
|
|
372
|
+
raw_mode: str
|
|
373
|
+
units: List[ReadingUnit]
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
@dataclass(frozen=True)
|
|
377
|
+
class ReadingUnitDocumentAnalysis:
|
|
378
|
+
page_count: int
|
|
379
|
+
mode: ReadingUnitMode
|
|
380
|
+
raw_mode: str
|
|
381
|
+
pages: List[ReadingUnitPageAnalysis]
|
|
382
|
+
|
|
383
|
+
|
|
271
384
|
@dataclass
|
|
272
385
|
class Point:
|
|
273
386
|
"""Represents a 2D point with x and y coordinates."""
|
{pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/src/pdfdancer/pdfdancer_v2.py
RENAMED
|
@@ -74,6 +74,15 @@ from .models import (
|
|
|
74
74
|
PathObjectRef,
|
|
75
75
|
Position,
|
|
76
76
|
PositionMode,
|
|
77
|
+
ReadingUnit,
|
|
78
|
+
ReadingUnitBounds,
|
|
79
|
+
ReadingUnitDocumentAnalysis,
|
|
80
|
+
ReadingUnitMode,
|
|
81
|
+
ReadingUnitPageAnalysis,
|
|
82
|
+
ReadingUnitProvenance,
|
|
83
|
+
ReadingUnitRelationship,
|
|
84
|
+
ReadingUnitRole,
|
|
85
|
+
ReadingUnitStreamMembership,
|
|
77
86
|
ShapeType,
|
|
78
87
|
TextObjectRef,
|
|
79
88
|
)
|
|
@@ -721,6 +730,11 @@ class PageClient:
|
|
|
721
730
|
def get_snapshot(self, types: Optional[str] = None) -> PageSnapshot:
|
|
722
731
|
return self.root.get_page_snapshot(self.page_number, types)
|
|
723
732
|
|
|
733
|
+
def analyze_reading_units(self) -> ReadingUnitPageAnalysis:
|
|
734
|
+
return cast(
|
|
735
|
+
ReadingUnitPageAnalysis, self.root.analyze_reading_units(self.page_number)
|
|
736
|
+
)
|
|
737
|
+
|
|
724
738
|
@property
|
|
725
739
|
def size(self) -> Optional[PageSize]:
|
|
726
740
|
"""Property alias for page size."""
|
|
@@ -2480,6 +2494,22 @@ class PDFDancer:
|
|
|
2480
2494
|
|
|
2481
2495
|
return self._parse_document_snapshot(data)
|
|
2482
2496
|
|
|
2497
|
+
def analyze_reading_units(
|
|
2498
|
+
self, page_number: Optional[int] = None
|
|
2499
|
+
) -> Union[ReadingUnitDocumentAnalysis, ReadingUnitPageAnalysis]:
|
|
2500
|
+
"""Analyze the current session PDF, or one one-based page, into reading units."""
|
|
2501
|
+
if page_number is not None:
|
|
2502
|
+
if page_number < 1:
|
|
2503
|
+
raise ValidationException(
|
|
2504
|
+
f"Page number must be >= 1 (1-based indexing), got {page_number}"
|
|
2505
|
+
)
|
|
2506
|
+
response = self._make_request(
|
|
2507
|
+
"GET", f"/pdf/page/{page_number}/reading-units"
|
|
2508
|
+
)
|
|
2509
|
+
return self._parse_reading_unit_page_analysis(response.json())
|
|
2510
|
+
response = self._make_request("GET", "/pdf/document/reading-units")
|
|
2511
|
+
return self._parse_reading_unit_document_analysis(response.json())
|
|
2512
|
+
|
|
2483
2513
|
def get_page_snapshot(
|
|
2484
2514
|
self, page_number: int, types: Optional[str] = None
|
|
2485
2515
|
) -> PageSnapshot:
|
|
@@ -3129,6 +3159,64 @@ class PDFDancer:
|
|
|
3129
3159
|
|
|
3130
3160
|
return DocumentSnapshot(page_count=page_count, fonts=fonts, pages=pages)
|
|
3131
3161
|
|
|
3162
|
+
def _parse_reading_unit_page_analysis(
|
|
3163
|
+
self, data: Mapping[str, Any]
|
|
3164
|
+
) -> ReadingUnitPageAnalysis:
|
|
3165
|
+
mode, raw_mode = ReadingUnitMode.from_value(data.get("mode"))
|
|
3166
|
+
return ReadingUnitPageAnalysis(
|
|
3167
|
+
page_number=int(data.get("pageNumber", 0)),
|
|
3168
|
+
mode=mode,
|
|
3169
|
+
raw_mode=raw_mode,
|
|
3170
|
+
units=[self._parse_reading_unit(unit) for unit in data.get("units", [])],
|
|
3171
|
+
)
|
|
3172
|
+
|
|
3173
|
+
def _parse_reading_unit_document_analysis(
|
|
3174
|
+
self, data: Mapping[str, Any]
|
|
3175
|
+
) -> ReadingUnitDocumentAnalysis:
|
|
3176
|
+
mode, raw_mode = ReadingUnitMode.from_value(data.get("mode"))
|
|
3177
|
+
return ReadingUnitDocumentAnalysis(
|
|
3178
|
+
page_count=int(data.get("pageCount", 0)),
|
|
3179
|
+
mode=mode,
|
|
3180
|
+
raw_mode=raw_mode,
|
|
3181
|
+
pages=[
|
|
3182
|
+
self._parse_reading_unit_page_analysis(page)
|
|
3183
|
+
for page in data.get("pages", [])
|
|
3184
|
+
],
|
|
3185
|
+
)
|
|
3186
|
+
|
|
3187
|
+
def _parse_reading_unit(self, data: Mapping[str, Any]) -> ReadingUnit:
|
|
3188
|
+
role, raw_role = ReadingUnitRole.from_value(data.get("role"))
|
|
3189
|
+
provenance_data = data.get("provenance") or {}
|
|
3190
|
+
bounds_data = provenance_data.get("bounds") or {}
|
|
3191
|
+
stream = {
|
|
3192
|
+
str(mode): ReadingUnitStreamMembership(
|
|
3193
|
+
bool(membership.get("included", False)), membership.get("order")
|
|
3194
|
+
)
|
|
3195
|
+
for mode, membership in (data.get("stream") or {}).items()
|
|
3196
|
+
}
|
|
3197
|
+
provenance = ReadingUnitProvenance(
|
|
3198
|
+
page_number=int(provenance_data.get("pageNumber", 0)),
|
|
3199
|
+
source_element_ids=list(provenance_data.get("sourceElementIds") or []),
|
|
3200
|
+
bounds=ReadingUnitBounds(
|
|
3201
|
+
float(bounds_data.get("x", 0)),
|
|
3202
|
+
float(bounds_data.get("y", 0)),
|
|
3203
|
+
float(bounds_data.get("width", 0)),
|
|
3204
|
+
float(bounds_data.get("height", 0)),
|
|
3205
|
+
),
|
|
3206
|
+
)
|
|
3207
|
+
return ReadingUnit(
|
|
3208
|
+
id=str(data.get("id", "")),
|
|
3209
|
+
role=role,
|
|
3210
|
+
raw_role=raw_role,
|
|
3211
|
+
text=str(data.get("text", "")),
|
|
3212
|
+
stream=stream,
|
|
3213
|
+
provenance=provenance,
|
|
3214
|
+
relationships=[
|
|
3215
|
+
ReadingUnitRelationship.from_dict(item)
|
|
3216
|
+
for item in data.get("relationships", [])
|
|
3217
|
+
],
|
|
3218
|
+
)
|
|
3219
|
+
|
|
3132
3220
|
# Context Manager Support (Python enhancement)
|
|
3133
3221
|
def __enter__(self) -> "PDFDancer":
|
|
3134
3222
|
"""Context manager entry."""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pdfdancer-client-python
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.2
|
|
4
4
|
Summary: Python client for PDFDancer API
|
|
5
5
|
Author-email: "The Famous Cat Ltd." <hi@thefamouscat.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -36,7 +36,7 @@ Dynamic: license-file
|
|
|
36
36
|
|
|
37
37
|
# PDFDancer Python Client
|
|
38
38
|
|
|
39
|
-
This README documents `pdfdancer-client-python` version `3.0.
|
|
39
|
+
This README documents `pdfdancer-client-python` version `3.0.2`.
|
|
40
40
|
|
|
41
41
|

|
|
42
42
|
|
|
@@ -69,7 +69,7 @@ pixel-perfect control from Python. The same API is also available for TypeScript
|
|
|
69
69
|
## Installation
|
|
70
70
|
|
|
71
71
|
```bash
|
|
72
|
-
pip install pdfdancer-client-python==3.0.
|
|
72
|
+
pip install pdfdancer-client-python==3.0.2
|
|
73
73
|
|
|
74
74
|
# Editable install for local development
|
|
75
75
|
pip install -e .
|
|
@@ -139,6 +139,20 @@ snapshot = first_page.get_snapshot()
|
|
|
139
139
|
|
|
140
140
|
Page-scoped selectors, text editing, and builders automatically restrict the operation to that page.
|
|
141
141
|
|
|
142
|
+
## Reading Units
|
|
143
|
+
|
|
144
|
+
Reading-unit analysis returns semantically classified text blocks with reading order, source provenance, bounds, and
|
|
145
|
+
relationships. Each call analyzes the current session state and makes a fresh request.
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
document_analysis = pdf.analyze_reading_units()
|
|
149
|
+
page_analysis = pdf.page(1).analyze_reading_units()
|
|
150
|
+
|
|
151
|
+
for page in document_analysis.pages:
|
|
152
|
+
for unit in page.units:
|
|
153
|
+
print(unit.role, unit.text)
|
|
154
|
+
```
|
|
155
|
+
|
|
142
156
|
## Selection
|
|
143
157
|
|
|
144
158
|
Document- and page-scoped selectors return typed objects for images, paths, form XObjects, and form fields. Position
|
|
@@ -412,8 +426,8 @@ Releases are published automatically to PyPI when a `v*` tag is pushed to GitHub
|
|
|
412
426
|
|
|
413
427
|
```bash
|
|
414
428
|
# Create and push a release tag — GitHub Actions handles the rest
|
|
415
|
-
git tag
|
|
416
|
-
git push origin
|
|
429
|
+
git tag v3.0.2
|
|
430
|
+
git push origin v3.0.2
|
|
417
431
|
```
|
|
418
432
|
|
|
419
433
|
#### Code Quality
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/.github/workflows/daily-tests.yml
RENAMED
|
File without changes
|
{pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/.github/workflows/release.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/docs/capabilities/CLEAR_CLIPPING.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/src/pdfdancer/_runtime_version.py
RENAMED
|
File without changes
|
|
File without changes
|
{pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/src/pdfdancer/fingerprint.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/src/pdfdancer/image_builder.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/src/pdfdancer/page_builder.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/src/pdfdancer/path_builder.py
RENAMED
|
File without changes
|
{pdfdancer_client_python-3.0.1 → pdfdancer_client_python-3.0.2}/src/pdfdancer/text_editing.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|