diff --git a/backend/app/services/document_processor.py b/backend/app/services/document_processor.py index 65d88ae..f540c76 100644 --- a/backend/app/services/document_processor.py +++ b/backend/app/services/document_processor.py @@ -1,14 +1,44 @@ import mammoth from io import BytesIO +from bs4 import BeautifulSoup from docx import Document from docx.shared import Pt, Inches from docx.enum.text import WD_ALIGN_PARAGRAPH from app.services.file_storage import get_file_content +ALIGN_MAP = { + WD_ALIGN_PARAGRAPH.CENTER: "center", + WD_ALIGN_PARAGRAPH.RIGHT: "right", + WD_ALIGN_PARAGRAPH.JUSTIFY: "justify", +} + async def docx_to_html(file_content: bytes) -> str: result = mammoth.convert_to_html(BytesIO(file_content)) - return result.value + html = result.value + + try: + doc = Document(BytesIO(file_content)) + soup = BeautifulSoup(html, "html.parser") + paragraphs = doc.paragraphs + html_paras = soup.find_all("p") + + for i, para in enumerate(paragraphs): + if i >= len(html_paras): + break + if para.alignment and para.alignment in ALIGN_MAP: + css = ALIGN_MAP[para.alignment] + existing_style = html_paras[i].get("style", "") + styles = f"text-align:{css}" + if existing_style: + styles = existing_style.rstrip(";") + ";" + styles + html_paras[i]["style"] = styles + + html = str(soup) + except Exception: + pass + + return html def html_to_docx_bytes(html_content: str) -> bytes: @@ -25,7 +55,7 @@ def html_to_docx_bytes(html_content: str) -> bytes: def __init__(self): super().__init__() self.paragraphs: list[dict] = [] - self.current = {"runs": []} + self.current = {"runs": [], "align": None} self.in_paragraph = False self.current_run = {"text": "", "bold": False, "italic": False, "underline": False} self.tag_stack: list[str] = [] @@ -33,9 +63,18 @@ def html_to_docx_bytes(html_content: str) -> bytes: def handle_starttag(self, tag, attrs): tag_lower = tag.lower() + attrs_dict = dict(attrs) if tag_lower in ("p", "div", "li"): self.in_paragraph = True + self.current = {"runs": [], "align": None} self.current_run = {"text": "", "bold": False, "italic": False, "underline": False} + style = attrs_dict.get("style", "") + if "text-align:center" in style: + self.current["align"] = "center" + elif "text-align:right" in style: + self.current["align"] = "right" + elif "text-align:justify" in style: + self.current["align"] = "justify" elif tag_lower in ("h1", "h2", "h3", "h4", "h5", "h6"): self.in_paragraph = True self.heading_level = int(tag_lower[1]) @@ -82,6 +121,14 @@ def html_to_docx_bytes(html_content: str) -> bytes: else: p = doc.add_paragraph() + align_val = para_data.get("align") + if align_val == "center": + p.alignment = WD_ALIGN_PARAGRAPH.CENTER + elif align_val == "right": + p.alignment = WD_ALIGN_PARAGRAPH.RIGHT + elif align_val == "justify": + p.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY + for run_data in para_data.get("runs", []): run = p.add_run(run_data["text"]) if run_data.get("bold"):