super-document-processor 0.1.3__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.
- super_document_processor/__init__.py +28 -0
- super_document_processor/document_processors/process_docx.py +824 -0
- super_document_processor/document_processors/process_images.py +39 -0
- super_document_processor/document_processors/process_msg.py +62 -0
- super_document_processor/document_processors/process_pdf.py +715 -0
- super_document_processor/document_processors/process_plain_text.py +25 -0
- super_document_processor/document_processors/process_pptx.py +367 -0
- super_document_processor/main.py +826 -0
- super_document_processor/processor.py +135 -0
- super_document_processor/utils/aspose_slide_renderer.py +103 -0
- super_document_processor/utils/chunker.py +127 -0
- super_document_processor/utils/docx_image_utils.py +167 -0
- super_document_processor/utils/docx_xml_parser.py +73 -0
- super_document_processor/utils/optimizer.py +49 -0
- super_document_processor/utils/pdf_image_utils.py +90 -0
- super_document_processor/utils/pdf_table_utils.py +627 -0
- super_document_processor/utils/pptx_components_extract.py +2197 -0
- super_document_processor/utils/pptx_converter.py +170 -0
- super_document_processor/utils/vlm.py +176 -0
- super_document_processor-0.1.3.dist-info/METADATA +376 -0
- super_document_processor-0.1.3.dist-info/RECORD +25 -0
- super_document_processor-0.1.3.dist-info/WHEEL +5 -0
- super_document_processor-0.1.3.dist-info/entry_points.txt +2 -0
- super_document_processor-0.1.3.dist-info/licenses/LICENSE +21 -0
- super_document_processor-0.1.3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import base64
|
|
3
|
+
import logging
|
|
4
|
+
import threading
|
|
5
|
+
import concurrent.futures
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger("super_document_processor.pdf_images")
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
from super_document_processor.utils.vlm import generate_image_description
|
|
11
|
+
except ImportError:
|
|
12
|
+
from utils.vlm import generate_image_description
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def process_pdf_full_page_image(page, page_num, descriptions, model=None):
|
|
16
|
+
"""Helper function to process the entire page as an image."""
|
|
17
|
+
pix = page.get_pixmap(dpi=200)
|
|
18
|
+
full_page_bytes = pix.tobytes("png")
|
|
19
|
+
raw_description = generate_image_description(full_page_bytes, model=model)
|
|
20
|
+
labeled_description = f"[Image description for full page {page_num + 1}]:\n{raw_description}"
|
|
21
|
+
descriptions.append((0, labeled_description))
|
|
22
|
+
return descriptions, True
|
|
23
|
+
|
|
24
|
+
def process_image(seen_image_hashes, image_data_store, image_page_map, page, page_num, doc, lock=None, model=None):
|
|
25
|
+
if lock is None:
|
|
26
|
+
lock = threading.Lock()
|
|
27
|
+
descriptions = []
|
|
28
|
+
need_full_page_ocr = False
|
|
29
|
+
|
|
30
|
+
text = page.get_text().strip()
|
|
31
|
+
page_text_detected = bool(text)
|
|
32
|
+
|
|
33
|
+
# 1st Condition: If vector drawings exist and no text detected
|
|
34
|
+
drawings = page.get_drawings()
|
|
35
|
+
logger.debug(f"Vector paths found on page {page_num + 1}: {len(drawings)}")
|
|
36
|
+
|
|
37
|
+
if len(drawings) > 0 and page_text_detected == False:
|
|
38
|
+
logger.debug(f"Processing full page image for description on page {page_num + 1}")
|
|
39
|
+
return process_pdf_full_page_image(page, page_num, descriptions, model=model)
|
|
40
|
+
|
|
41
|
+
# 2nd Condition: Otherwise, process individual images on the page
|
|
42
|
+
images = page.get_images(full=True)
|
|
43
|
+
logger.debug(f"Found {len(images)} images on page {page_num + 1}")
|
|
44
|
+
|
|
45
|
+
if len(images) > 5:
|
|
46
|
+
logger.debug(f"More than 5 images on page {page_num + 1}, processing full page image")
|
|
47
|
+
return process_pdf_full_page_image(page, page_num, descriptions, model=model)
|
|
48
|
+
|
|
49
|
+
# 3rd Condition: Otherwise, process each image individually (in parallel)
|
|
50
|
+
def process_single_image(img_index, img_info):
|
|
51
|
+
xref = img_info[0]
|
|
52
|
+
base_image = doc.extract_image(xref)
|
|
53
|
+
img_bytes = base_image["image"]
|
|
54
|
+
|
|
55
|
+
# Hash image bytes
|
|
56
|
+
img_hash = hashlib.sha256(img_bytes).hexdigest()
|
|
57
|
+
|
|
58
|
+
with lock:
|
|
59
|
+
if (page_num + 1) not in image_page_map[img_hash]:
|
|
60
|
+
image_page_map[img_hash].append(page_num + 1)
|
|
61
|
+
if img_hash not in image_data_store:
|
|
62
|
+
image_data_store[img_hash] = base64.b64encode(img_bytes).decode("utf-8")
|
|
63
|
+
description = seen_image_hashes.get(img_hash)
|
|
64
|
+
|
|
65
|
+
if description is None:
|
|
66
|
+
logger.debug(f"Generating LLM description for image {img_index + 1} on page {page_num + 1}")
|
|
67
|
+
description = generate_image_description(img_bytes, model=model)
|
|
68
|
+
with lock:
|
|
69
|
+
seen_image_hashes[img_hash] = description
|
|
70
|
+
else:
|
|
71
|
+
logger.debug(f"Reusing description for duplicate image on page {page_num + 1}")
|
|
72
|
+
|
|
73
|
+
labeled_description = f"[Image description for image {img_index + 1} on page {page_num + 1}]:\n{description}\n[Image description {img_index+1} end]"
|
|
74
|
+
|
|
75
|
+
image_rects = page.get_image_rects(img_info)
|
|
76
|
+
if not image_rects:
|
|
77
|
+
logger.debug(f"No bbox for image {img_index + 1} on page {page_num + 1}, skipping")
|
|
78
|
+
return None
|
|
79
|
+
img_bbox = image_rects[0]
|
|
80
|
+
return (img_bbox.y0, labeled_description)
|
|
81
|
+
|
|
82
|
+
workers = int(os.environ.get("MAX_WORKERS", 3))
|
|
83
|
+
with concurrent.futures.ThreadPoolExecutor(max_workers=workers) as executor:
|
|
84
|
+
futures = [executor.submit(process_single_image, img_index, img_info) for img_index, img_info in enumerate(images)]
|
|
85
|
+
for future in concurrent.futures.as_completed(futures):
|
|
86
|
+
result = future.result()
|
|
87
|
+
if result:
|
|
88
|
+
descriptions.append(result)
|
|
89
|
+
|
|
90
|
+
return descriptions, need_full_page_ocr
|