import base64
import csv
from html import escape
from io import StringIO
import docx
import pdfplumber
# сигнатуры форматов: картинка из PDF — это сырой поток, не обязательно PNG
IMAGE_SIGNATURES = {
b"\x89PNG\r\n\x1a\n": "image/png",
b"\xff\xd8\xff": "image/jpeg",
b"GIF8": "image/gif",
}
def image_mime(data: bytes):
for signature, mime in IMAGE_SIGNATURES.items():
if data.startswith(signature):
return mime
return None
def img_tag(data: bytes, mime: str) -> str:
return f''
def table_to_pre(rows) -> str:
csv_output = StringIO()
csv.writer(csv_output).writerows([[cell if cell is not None else "" for cell in row] for row in rows])
return f"
{escape(csv_output.getvalue())}"
def extract_text_tables(file_path: str) -> str:
"""Текст, таблицы (CSV) и изображения документа одной HTML-строкой.
Текст экранируется: иначе содержимое документа становится разметкой (XSS у потребителя)"""
result = ""
path = file_path.lower() # раньше файл «.PDF» молча давал пустой результат
if path.endswith(".pdf"):
with pdfplumber.open(file_path) as pdf:
for page in pdf.pages:
text = page.extract_text()
if text:
result += "".join(f"{escape(line)}
" for line in text.split("\n")) for table in page.extract_tables() or []: result += table_to_pre(table) # Извлечение изображений — только тех, что лежат в PDF готовым файлом (PNG/JPEG) for img in page.images: data = img["stream"].get_data() mime = image_mime(data) if mime: result += img_tag(data, mime) elif path.endswith(".docx"): doc = docx.Document(file_path) result += "".join(f"{escape(para.text)}
" for para in doc.paragraphs if para.text.strip()) result += "".join(table_to_pre([[cell.text.strip() for cell in row.cells] for row in table.rows]) for table in doc.tables) # Извлечение изображений с их настоящим типом for rel in doc.part.rels.values(): if "image" in rel.reltype and not rel.is_external: part = rel.target_part result += img_tag(part.blob, part.content_type) return result