natural-pdf 0.1.4__py3-none-any.whl → 0.1.5__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.
Files changed (132) hide show
  1. docs/api/index.md +386 -0
  2. docs/assets/favicon.png +3 -0
  3. docs/assets/favicon.svg +3 -0
  4. docs/assets/javascripts/custom.js +17 -0
  5. docs/assets/logo.svg +3 -0
  6. docs/assets/sample-screen.png +0 -0
  7. docs/assets/social-preview.png +17 -0
  8. docs/assets/social-preview.svg +17 -0
  9. docs/assets/stylesheets/custom.css +65 -0
  10. docs/document-qa/index.ipynb +435 -0
  11. docs/document-qa/index.md +79 -0
  12. docs/element-selection/index.ipynb +915 -0
  13. docs/element-selection/index.md +229 -0
  14. docs/index.md +170 -0
  15. docs/installation/index.md +69 -0
  16. docs/interactive-widget/index.ipynb +962 -0
  17. docs/interactive-widget/index.md +12 -0
  18. docs/layout-analysis/index.ipynb +818 -0
  19. docs/layout-analysis/index.md +185 -0
  20. docs/ocr/index.md +222 -0
  21. docs/pdf-navigation/index.ipynb +314 -0
  22. docs/pdf-navigation/index.md +97 -0
  23. docs/regions/index.ipynb +816 -0
  24. docs/regions/index.md +294 -0
  25. docs/tables/index.ipynb +658 -0
  26. docs/tables/index.md +144 -0
  27. docs/text-analysis/index.ipynb +370 -0
  28. docs/text-analysis/index.md +105 -0
  29. docs/text-extraction/index.ipynb +1478 -0
  30. docs/text-extraction/index.md +292 -0
  31. docs/tutorials/01-loading-and-extraction.ipynb +1696 -0
  32. docs/tutorials/01-loading-and-extraction.md +95 -0
  33. docs/tutorials/02-finding-elements.ipynb +340 -0
  34. docs/tutorials/02-finding-elements.md +149 -0
  35. docs/tutorials/03-extracting-blocks.ipynb +147 -0
  36. docs/tutorials/03-extracting-blocks.md +48 -0
  37. docs/tutorials/04-table-extraction.ipynb +114 -0
  38. docs/tutorials/04-table-extraction.md +50 -0
  39. docs/tutorials/05-excluding-content.ipynb +270 -0
  40. docs/tutorials/05-excluding-content.md +109 -0
  41. docs/tutorials/06-document-qa.ipynb +332 -0
  42. docs/tutorials/06-document-qa.md +91 -0
  43. docs/tutorials/07-layout-analysis.ipynb +260 -0
  44. docs/tutorials/07-layout-analysis.md +66 -0
  45. docs/tutorials/07-working-with-regions.ipynb +409 -0
  46. docs/tutorials/07-working-with-regions.md +151 -0
  47. docs/tutorials/08-spatial-navigation.ipynb +508 -0
  48. docs/tutorials/08-spatial-navigation.md +190 -0
  49. docs/tutorials/09-section-extraction.ipynb +2434 -0
  50. docs/tutorials/09-section-extraction.md +256 -0
  51. docs/tutorials/10-form-field-extraction.ipynb +484 -0
  52. docs/tutorials/10-form-field-extraction.md +201 -0
  53. docs/tutorials/11-enhanced-table-processing.ipynb +54 -0
  54. docs/tutorials/11-enhanced-table-processing.md +9 -0
  55. docs/tutorials/12-ocr-integration.ipynb +586 -0
  56. docs/tutorials/12-ocr-integration.md +188 -0
  57. docs/tutorials/13-semantic-search.ipynb +1888 -0
  58. docs/tutorials/13-semantic-search.md +77 -0
  59. docs/visual-debugging/index.ipynb +2970 -0
  60. docs/visual-debugging/index.md +157 -0
  61. docs/visual-debugging/region.png +0 -0
  62. natural_pdf/__init__.py +39 -20
  63. natural_pdf/analyzers/__init__.py +2 -1
  64. natural_pdf/analyzers/layout/base.py +32 -24
  65. natural_pdf/analyzers/layout/docling.py +131 -72
  66. natural_pdf/analyzers/layout/layout_analyzer.py +156 -113
  67. natural_pdf/analyzers/layout/layout_manager.py +98 -58
  68. natural_pdf/analyzers/layout/layout_options.py +32 -17
  69. natural_pdf/analyzers/layout/paddle.py +152 -95
  70. natural_pdf/analyzers/layout/surya.py +164 -92
  71. natural_pdf/analyzers/layout/tatr.py +149 -84
  72. natural_pdf/analyzers/layout/yolo.py +84 -44
  73. natural_pdf/analyzers/text_options.py +22 -15
  74. natural_pdf/analyzers/text_structure.py +131 -85
  75. natural_pdf/analyzers/utils.py +30 -23
  76. natural_pdf/collections/pdf_collection.py +125 -97
  77. natural_pdf/core/__init__.py +1 -1
  78. natural_pdf/core/element_manager.py +416 -337
  79. natural_pdf/core/highlighting_service.py +268 -196
  80. natural_pdf/core/page.py +907 -513
  81. natural_pdf/core/pdf.py +385 -287
  82. natural_pdf/elements/__init__.py +1 -1
  83. natural_pdf/elements/base.py +302 -214
  84. natural_pdf/elements/collections.py +708 -508
  85. natural_pdf/elements/line.py +39 -36
  86. natural_pdf/elements/rect.py +32 -30
  87. natural_pdf/elements/region.py +854 -883
  88. natural_pdf/elements/text.py +122 -99
  89. natural_pdf/exporters/__init__.py +0 -1
  90. natural_pdf/exporters/searchable_pdf.py +261 -102
  91. natural_pdf/ocr/__init__.py +23 -14
  92. natural_pdf/ocr/engine.py +17 -8
  93. natural_pdf/ocr/engine_easyocr.py +63 -47
  94. natural_pdf/ocr/engine_paddle.py +97 -68
  95. natural_pdf/ocr/engine_surya.py +54 -44
  96. natural_pdf/ocr/ocr_manager.py +88 -62
  97. natural_pdf/ocr/ocr_options.py +16 -10
  98. natural_pdf/qa/__init__.py +1 -1
  99. natural_pdf/qa/document_qa.py +119 -111
  100. natural_pdf/search/__init__.py +37 -31
  101. natural_pdf/search/haystack_search_service.py +312 -189
  102. natural_pdf/search/haystack_utils.py +186 -122
  103. natural_pdf/search/search_options.py +25 -14
  104. natural_pdf/search/search_service_protocol.py +12 -6
  105. natural_pdf/search/searchable_mixin.py +261 -176
  106. natural_pdf/selectors/__init__.py +2 -1
  107. natural_pdf/selectors/parser.py +159 -316
  108. natural_pdf/templates/__init__.py +1 -1
  109. natural_pdf/utils/highlighting.py +8 -2
  110. natural_pdf/utils/reading_order.py +65 -63
  111. natural_pdf/utils/text_extraction.py +195 -0
  112. natural_pdf/utils/visualization.py +70 -61
  113. natural_pdf/widgets/__init__.py +2 -3
  114. natural_pdf/widgets/viewer.py +749 -718
  115. {natural_pdf-0.1.4.dist-info → natural_pdf-0.1.5.dist-info}/METADATA +15 -1
  116. natural_pdf-0.1.5.dist-info/RECORD +134 -0
  117. natural_pdf-0.1.5.dist-info/top_level.txt +5 -0
  118. notebooks/Examples.ipynb +1293 -0
  119. pdfs/.gitkeep +0 -0
  120. pdfs/01-practice.pdf +543 -0
  121. pdfs/0500000US42001.pdf +0 -0
  122. pdfs/0500000US42007.pdf +0 -0
  123. pdfs/2014 Statistics.pdf +0 -0
  124. pdfs/2019 Statistics.pdf +0 -0
  125. pdfs/Atlanta_Public_Schools_GA_sample.pdf +0 -0
  126. pdfs/needs-ocr.pdf +0 -0
  127. tests/test_loading.py +50 -0
  128. tests/test_optional_deps.py +298 -0
  129. natural_pdf-0.1.4.dist-info/RECORD +0 -61
  130. natural_pdf-0.1.4.dist-info/top_level.txt +0 -1
  131. {natural_pdf-0.1.4.dist-info → natural_pdf-0.1.5.dist-info}/WHEEL +0 -0
  132. {natural_pdf-0.1.4.dist-info → natural_pdf-0.1.5.dist-info}/licenses/LICENSE +0 -0
@@ -1,15 +1,16 @@
1
1
  # layout_manager.py
2
+ import copy
2
3
  import logging
3
- from typing import Dict, List, Any, Optional, Union, Type
4
+ from typing import Any, Dict, List, Optional, Type, Union
5
+
4
6
  from PIL import Image
5
- import copy
6
7
 
7
8
  # --- Import detector classes and options ---
8
9
  # Use try-except blocks for robustness if some detectors might be missing dependencies
9
10
  try:
10
11
  from .base import LayoutDetector
11
12
  except ImportError:
12
- LayoutDetector = type('LayoutDetector', (), {})
13
+ LayoutDetector = type("LayoutDetector", (), {})
13
14
 
14
15
  try:
15
16
  from .yolo import YOLODocLayoutDetector
@@ -35,14 +36,20 @@ try:
35
36
  from .docling import DoclingLayoutDetector
36
37
  except ImportError:
37
38
  DoclingLayoutDetector = None
38
-
39
+
39
40
  from .layout_options import (
40
- BaseLayoutOptions, YOLOLayoutOptions, TATRLayoutOptions,
41
- PaddleLayoutOptions, SuryaLayoutOptions, DoclingLayoutOptions, LayoutOptions
41
+ BaseLayoutOptions,
42
+ DoclingLayoutOptions,
43
+ LayoutOptions,
44
+ PaddleLayoutOptions,
45
+ SuryaLayoutOptions,
46
+ TATRLayoutOptions,
47
+ YOLOLayoutOptions,
42
48
  )
43
49
 
44
50
  logger = logging.getLogger(__name__)
45
51
 
52
+
46
53
  class LayoutManager:
47
54
  """Manages layout detector selection, configuration, and execution."""
48
55
 
@@ -50,46 +57,70 @@ class LayoutManager:
50
57
  ENGINE_REGISTRY: Dict[str, Dict[str, Any]] = {}
51
58
 
52
59
  # Populate registry only with available detectors
53
- if YOLODocLayoutDetector: ENGINE_REGISTRY['yolo'] = {'class': YOLODocLayoutDetector, 'options_class': YOLOLayoutOptions}
54
- if TableTransformerDetector: ENGINE_REGISTRY['tatr'] = {'class': TableTransformerDetector, 'options_class': TATRLayoutOptions}
55
- if PaddleLayoutDetector: ENGINE_REGISTRY['paddle'] = {'class': PaddleLayoutDetector, 'options_class': PaddleLayoutOptions}
56
- if SuryaLayoutDetector: ENGINE_REGISTRY['surya'] = {'class': SuryaLayoutDetector, 'options_class': SuryaLayoutOptions}
57
- if DoclingLayoutDetector: ENGINE_REGISTRY['docling'] = {'class': DoclingLayoutDetector, 'options_class': DoclingLayoutOptions}
60
+ if YOLODocLayoutDetector:
61
+ ENGINE_REGISTRY["yolo"] = {
62
+ "class": YOLODocLayoutDetector,
63
+ "options_class": YOLOLayoutOptions,
64
+ }
65
+ if TableTransformerDetector:
66
+ ENGINE_REGISTRY["tatr"] = {
67
+ "class": TableTransformerDetector,
68
+ "options_class": TATRLayoutOptions,
69
+ }
70
+ if PaddleLayoutDetector:
71
+ ENGINE_REGISTRY["paddle"] = {
72
+ "class": PaddleLayoutDetector,
73
+ "options_class": PaddleLayoutOptions,
74
+ }
75
+ if SuryaLayoutDetector:
76
+ ENGINE_REGISTRY["surya"] = {
77
+ "class": SuryaLayoutDetector,
78
+ "options_class": SuryaLayoutOptions,
79
+ }
80
+ if DoclingLayoutDetector:
81
+ ENGINE_REGISTRY["docling"] = {
82
+ "class": DoclingLayoutDetector,
83
+ "options_class": DoclingLayoutOptions,
84
+ }
58
85
 
59
86
  # Define the limited set of kwargs allowed for the simple analyze_layout call
60
- SIMPLE_MODE_ALLOWED_KWARGS = {
61
- 'engine', 'confidence', 'classes', 'exclude_classes', 'device'
62
- }
87
+ SIMPLE_MODE_ALLOWED_KWARGS = {"engine", "confidence", "classes", "exclude_classes", "device"}
63
88
 
64
89
  def __init__(self):
65
90
  """Initializes the Layout Manager."""
66
91
  # Cache for detector instances (different from model cache inside detector)
67
92
  self._detector_instances: Dict[str, LayoutDetector] = {}
68
- logger.info(f"LayoutManager initialized. Available engines: {list(self.ENGINE_REGISTRY.keys())}")
93
+ logger.info(
94
+ f"LayoutManager initialized. Available engines: {list(self.ENGINE_REGISTRY.keys())}"
95
+ )
69
96
 
70
97
  def _get_engine_instance(self, engine_name: str) -> LayoutDetector:
71
98
  """Retrieves or creates an instance of the specified layout detector."""
72
99
  engine_name = engine_name.lower()
73
100
  if engine_name not in self.ENGINE_REGISTRY:
74
- raise ValueError(f"Unknown layout engine: '{engine_name}'. Available: {list(self.ENGINE_REGISTRY.keys())}")
101
+ raise ValueError(
102
+ f"Unknown layout engine: '{engine_name}'. Available: {list(self.ENGINE_REGISTRY.keys())}"
103
+ )
75
104
 
76
105
  if engine_name not in self._detector_instances:
77
106
  logger.info(f"Creating instance of layout engine: {engine_name}")
78
- engine_class = self.ENGINE_REGISTRY[engine_name]['class']
79
- detector_instance = engine_class() # Instantiate
107
+ engine_class = self.ENGINE_REGISTRY[engine_name]["class"]
108
+ detector_instance = engine_class() # Instantiate
80
109
  if not detector_instance.is_available():
81
- # Check availability before storing
82
- raise RuntimeError(f"Layout engine '{engine_name}' is not available. Please check dependencies.")
83
- self._detector_instances[engine_name] = detector_instance # Store if available
110
+ # Check availability before storing
111
+ raise RuntimeError(
112
+ f"Layout engine '{engine_name}' is not available. Please check dependencies."
113
+ )
114
+ self._detector_instances[engine_name] = detector_instance # Store if available
84
115
 
85
116
  return self._detector_instances[engine_name]
86
117
 
87
118
  def analyze_layout(
88
119
  self,
89
120
  image: Image.Image,
90
- engine: Optional[str] = None, # Default engine handled below
121
+ engine: Optional[str] = None, # Default engine handled below
91
122
  options: Optional[LayoutOptions] = None,
92
- **kwargs
123
+ **kwargs,
93
124
  ) -> List[Dict[str, Any]]:
94
125
  """
95
126
  Analyzes layout of a single image using simple args or an options object.
@@ -109,11 +140,11 @@ class LayoutManager:
109
140
  selected_engine_name: str
110
141
 
111
142
  if not isinstance(image, Image.Image):
112
- raise TypeError("Input 'image' must be a PIL Image.")
143
+ raise TypeError("Input 'image' must be a PIL Image.")
113
144
 
114
145
  available_engines = self.get_available_engines()
115
146
  if not available_engines:
116
- raise RuntimeError("No layout engines are available. Please check dependencies.")
147
+ raise RuntimeError("No layout engines are available. Please check dependencies.")
117
148
 
118
149
  # Determine default engine if not specified
119
150
  default_engine = engine if engine else available_engines[0]
@@ -123,46 +154,55 @@ class LayoutManager:
123
154
  # Advanced Mode: An options object was provided directly (or constructed by LayoutAnalyzer)
124
155
  # Use this object directly, do not deep copy or reconstruct.
125
156
  logger.debug(f"LayoutManager: Using provided options object: {type(options).__name__}")
126
- final_options = options # Use the provided object directly
157
+ final_options = options # Use the provided object directly
127
158
  found_engine = False
128
159
  for name, registry_entry in self.ENGINE_REGISTRY.items():
129
- if isinstance(options, registry_entry['options_class']):
160
+ if isinstance(options, registry_entry["options_class"]):
130
161
  selected_engine_name = name
131
162
  found_engine = True
132
163
  break
133
164
  if not found_engine:
134
- raise TypeError(f"Provided options object type '{type(options).__name__}' does not match any registered layout engine options.")
165
+ raise TypeError(
166
+ f"Provided options object type '{type(options).__name__}' does not match any registered layout engine options."
167
+ )
135
168
  # Ignore simple kwargs if options object is present
136
169
  if kwargs:
137
- logger.warning(f"Keyword arguments {list(kwargs.keys())} were provided alongside an 'options' object and will be ignored.")
170
+ logger.warning(
171
+ f"Keyword arguments {list(kwargs.keys())} were provided alongside an 'options' object and will be ignored."
172
+ )
138
173
  else:
139
- # Simple Mode: No options object provided initially.
174
+ # Simple Mode: No options object provided initially.
140
175
  # Determine engine from kwargs or default, then construct options.
141
176
  selected_engine_name = default_engine.lower()
142
- logger.debug(f"LayoutManager: Using simple mode. Engine: '{selected_engine_name}', kwargs: {kwargs}")
177
+ logger.debug(
178
+ f"LayoutManager: Using simple mode. Engine: '{selected_engine_name}', kwargs: {kwargs}"
179
+ )
143
180
 
144
181
  if selected_engine_name not in self.ENGINE_REGISTRY:
145
- raise ValueError(f"Unknown or unavailable layout engine: '{selected_engine_name}'. Available: {available_engines}")
182
+ raise ValueError(
183
+ f"Unknown or unavailable layout engine: '{selected_engine_name}'. Available: {available_engines}"
184
+ )
146
185
 
147
186
  unexpected_kwargs = set(kwargs.keys()) - self.SIMPLE_MODE_ALLOWED_KWARGS
148
187
  if unexpected_kwargs:
149
- raise TypeError(f"Got unexpected keyword arguments in simple mode: {list(unexpected_kwargs)}. Use the 'options' parameter for detailed configuration.")
188
+ raise TypeError(
189
+ f"Got unexpected keyword arguments in simple mode: {list(unexpected_kwargs)}. Use the 'options' parameter for detailed configuration."
190
+ )
150
191
 
151
- options_class = self.ENGINE_REGISTRY[selected_engine_name]['options_class']
192
+ options_class = self.ENGINE_REGISTRY[selected_engine_name]["options_class"]
152
193
  # Use BaseLayoutOptions defaults unless overridden by kwargs
153
194
  base_defaults = BaseLayoutOptions()
154
195
  simple_args = {
155
- 'confidence': kwargs.get('confidence', base_defaults.confidence),
156
- 'classes': kwargs.get('classes'),
157
- 'exclude_classes': kwargs.get('exclude_classes'),
158
- 'device': kwargs.get('device', base_defaults.device)
196
+ "confidence": kwargs.get("confidence", base_defaults.confidence),
197
+ "classes": kwargs.get("classes"),
198
+ "exclude_classes": kwargs.get("exclude_classes"),
199
+ "device": kwargs.get("device", base_defaults.device),
159
200
  }
160
201
  # Filter out None values before passing to constructor
161
202
  simple_args_filtered = {k: v for k, v in simple_args.items() if v is not None}
162
203
  final_options = options_class(**simple_args_filtered)
163
204
  logger.debug(f"LayoutManager: Constructed options for simple mode: {final_options}")
164
205
 
165
-
166
206
  # --- Get Engine Instance and Process ---
167
207
  try:
168
208
  engine_instance = self._get_engine_instance(selected_engine_name)
@@ -175,29 +215,29 @@ class LayoutManager:
175
215
  return detections
176
216
 
177
217
  except (ImportError, RuntimeError, ValueError, TypeError) as e:
178
- logger.error(f"Layout analysis failed for engine '{selected_engine_name}': {e}", exc_info=True)
179
- raise # Re-raise expected errors
218
+ logger.error(
219
+ f"Layout analysis failed for engine '{selected_engine_name}': {e}", exc_info=True
220
+ )
221
+ raise # Re-raise expected errors
180
222
  except Exception as e:
181
- logger.error(f"An unexpected error occurred during layout analysis: {e}", exc_info=True)
182
- raise # Re-raise unexpected errors
183
-
223
+ logger.error(f"An unexpected error occurred during layout analysis: {e}", exc_info=True)
224
+ raise # Re-raise unexpected errors
184
225
 
185
226
  def get_available_engines(self) -> List[str]:
186
227
  """Returns a list of registered layout engine names that are currently available."""
187
228
  available = []
188
229
  for name, registry_entry in self.ENGINE_REGISTRY.items():
189
- try:
190
- engine_class = registry_entry['class']
191
- # Check availability without full instantiation if possible
192
- if hasattr(engine_class, 'is_available') and callable(engine_class.is_available):
193
- # Create temporary instance only for check if needed, or use classmethod
194
- if engine_class().is_available(): # Assumes instance needed for check
195
- available.append(name)
196
- else:
197
- # Assume available if class exists (less robust)
198
- available.append(name)
199
- except Exception as e:
200
- logger.debug(f"Layout engine '{name}' check failed: {e}")
201
- pass
230
+ try:
231
+ engine_class = registry_entry["class"]
232
+ # Check availability without full instantiation if possible
233
+ if hasattr(engine_class, "is_available") and callable(engine_class.is_available):
234
+ # Create temporary instance only for check if needed, or use classmethod
235
+ if engine_class().is_available(): # Assumes instance needed for check
236
+ available.append(name)
237
+ else:
238
+ # Assume available if class exists (less robust)
239
+ available.append(name)
240
+ except Exception as e:
241
+ logger.debug(f"Layout engine '{name}' check failed: {e}")
242
+ pass
202
243
  return available
203
-
@@ -1,32 +1,40 @@
1
1
  # layout_options.py
2
2
  import logging
3
3
  from dataclasses import dataclass, field
4
- from typing import List, Optional, Dict, Any, Tuple, Union
4
+ from typing import Any, Dict, List, Optional, Tuple, Union
5
5
 
6
6
  logger = logging.getLogger(__name__)
7
7
 
8
+
8
9
  # --- Base Layout Options ---
9
10
  @dataclass
10
11
  class BaseLayoutOptions:
11
12
  """Base options for layout detection engines."""
12
- confidence: float = 0.5 # Minimum confidence threshold for detections
13
- classes: Optional[List[str]] = None # Specific classes to detect (None for all)
14
- exclude_classes: Optional[List[str]] = None # Classes to exclude
15
- device: Optional[str] = 'cpu' # Preferred device ('cpu', 'cuda', 'mps', etc.)
16
- extra_args: Dict[str, Any] = field(default_factory=dict) # For engine-specific args not yet fields
13
+
14
+ confidence: float = 0.5 # Minimum confidence threshold for detections
15
+ classes: Optional[List[str]] = None # Specific classes to detect (None for all)
16
+ exclude_classes: Optional[List[str]] = None # Classes to exclude
17
+ device: Optional[str] = "cpu" # Preferred device ('cpu', 'cuda', 'mps', etc.)
18
+ extra_args: Dict[str, Any] = field(
19
+ default_factory=dict
20
+ ) # For engine-specific args not yet fields
21
+
17
22
 
18
23
  # --- YOLO Specific Options ---
19
24
  @dataclass
20
25
  class YOLOLayoutOptions(BaseLayoutOptions):
21
26
  """Options specific to YOLO-based layout detection."""
27
+
22
28
  model_repo: str = "juliozhao/DocLayout-YOLO-DocStructBench"
23
29
  model_file: str = "doclayout_yolo_docstructbench_imgsz1024.pt"
24
- image_size: int = 1024 # Input image size for the model
30
+ image_size: int = 1024 # Input image size for the model
31
+
25
32
 
26
33
  # --- TATR Specific Options ---
27
34
  @dataclass
28
35
  class TATRLayoutOptions(BaseLayoutOptions):
29
36
  """Options specific to Table Transformer (TATR) layout detection."""
37
+
30
38
  # Which models to use (can be local paths or HF identifiers)
31
39
  detection_model: str = "microsoft/table-transformer-detection"
32
40
  structure_model: str = "microsoft/table-transformer-structure-recognition-v1.1-all"
@@ -36,35 +44,42 @@ class TATRLayoutOptions(BaseLayoutOptions):
36
44
  # Whether to create cell regions (can be slow)
37
45
  create_cells: bool = True
38
46
 
47
+
39
48
  # --- Paddle Specific Options ---
40
49
  @dataclass
41
50
  class PaddleLayoutOptions(BaseLayoutOptions):
42
51
  """Options specific to PaddlePaddle PP-Structure layout detection."""
43
- lang: str = "en" # Language ('en', 'ch', etc.)
44
- use_angle_cls: bool = False # Use text angle classification?
45
- enable_table: bool = True # Enable table structure detection?
46
- show_log: bool = False # Show Paddle internal logs?
47
- detect_text: bool = True # Also detect raw text boxes using PaddleOCR?
48
- verbose: bool = False # Verbose logging for the detector class
52
+
53
+ lang: str = "en" # Language ('en', 'ch', etc.)
54
+ use_angle_cls: bool = False # Use text angle classification?
55
+ enable_table: bool = True # Enable table structure detection?
56
+ show_log: bool = False # Show Paddle internal logs?
57
+ detect_text: bool = True # Also detect raw text boxes using PaddleOCR?
58
+ verbose: bool = False # Verbose logging for the detector class
59
+
49
60
 
50
61
  # --- Surya Specific Options ---
51
62
  @dataclass
52
63
  class SuryaLayoutOptions(BaseLayoutOptions):
53
64
  """Options specific to Surya layout detection."""
54
- model_name: str = "default" # Placeholder if different models become available
55
- recognize_table_structure: bool = True # Automatically run table structure recognition?
65
+
66
+ model_name: str = "default" # Placeholder if different models become available
67
+ recognize_table_structure: bool = True # Automatically run table structure recognition?
68
+
56
69
 
57
70
  # --- Docling Specific Options ---
58
71
  @dataclass
59
72
  class DoclingLayoutOptions(BaseLayoutOptions):
60
73
  """Options specific to Docling layout detection."""
74
+
61
75
  # Pass kwargs directly to Docling's DocumentConverter via extra_args
62
76
  # Common examples shown here for documentation, add others as needed to extra_args
63
77
  # model_name: str = "ds4sd/SmolDocling-256M-preview" # Example model (pass via extra_args)
64
78
  # prompt_text: Optional[str] = None # Optional prompt (pass via extra_args)
65
- verbose: bool = False # Verbose logging for the detector class
79
+ verbose: bool = False # Verbose logging for the detector class
66
80
  # Other kwargs like 'device', 'batch_size' can go in extra_args
67
81
 
82
+
68
83
  # --- Union Type ---
69
84
  LayoutOptions = Union[
70
85
  YOLOLayoutOptions,
@@ -72,5 +87,5 @@ LayoutOptions = Union[
72
87
  PaddleLayoutOptions,
73
88
  SuryaLayoutOptions,
74
89
  DoclingLayoutOptions,
75
- BaseLayoutOptions # Include base for typing flexibility
90
+ BaseLayoutOptions, # Include base for typing flexibility
76
91
  ]