# Word Generator Module # Contains create_word_document() and all Word-related functions import re import os import tempfile import time import io import sys from typing import List, Dict, Any # Add the parent directory to sys.path to import from models.py sys.path.append(os.path.dirname(os.path.dirname(__file__))) from models import ExportRequest # Import from the same directory (Document Generation) from document_helpers import ( filter_empty_sections, extract_business_name_from_content ) # from table_of_content import generate_table_of_contents # Not used in word generator from chart_generator import parse_embedded_chart_data, create_chart_from_data from markdown_renderer import ( render_markdown_to_docx_grouped, process_content_with_inline_charts, clean_text_for_pdf ) # Add Currency Mapping path sys.path.append(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'CurrencyMapping')) from CurrencyMapping.currency_mapping import convert_currency_symbols_to_iso # Import sort_sections_by_order from utils module from utils import sort_sections_by_order def create_word_document(request: ExportRequest): """Create a Word document from the business plan data with front page, TOC, and proper page breaks""" try: from docx import Document from docx.shared import Inches, Pt, RGBColor from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_TAB_ALIGNMENT from docx.oxml import OxmlElement from docx.oxml.ns import qn except ImportError: raise Exception("python-docx is not installed. Please install it with: pip install python-docx") # Filter out empty sections to prevent blank pages try: filtered_sections = filter_empty_sections(request.sections) except Exception as e: raise Exception(f"Failed to filter sections: {str(e)}") if not filtered_sections: raise Exception("No valid sections found after filtering. All sections appear to be empty.") doc = Document() # Add confidentiality footer to every page section = doc.sections[0] footer = section.footer footer_para = footer.paragraphs[0] footer_para.alignment = WD_ALIGN_PARAGRAPH.CENTER footer_run = footer_para.add_run("CONFIDENTIAL - This document contains proprietary information and may not be shared without consent") footer_run.font.size = Pt(8) footer_run.font.color.rgb = RGBColor(100, 100, 100) # Gray text # ===== FRONT PAGE ===== # Add company logo at the top try: # Use the MBD logo PNG file - now in app folder for HF deployment app_root = os.path.dirname(os.path.dirname(__file__)) logo_path = os.path.join(app_root, 'assets', 'logos', 'MBD logo.png') if os.path.exists(logo_path): # Add logo to Word document (centered, top of page) logo_para = doc.add_paragraph() logo_para.alignment = WD_ALIGN_PARAGRAPH.CENTER logo_run = logo_para.add_run() logo_run.add_picture(logo_path, width=Inches(3.0)) # Even larger size to show full logo print(f"Logo added successfully from: {logo_path}") else: print(f"Logo file not found at: {logo_path}") except Exception as e: # Log any errors with logo print(f"Logo error: {e}") pass # Add top spacing doc.add_paragraph() doc.add_paragraph() # Extract business name from content, fallback to businessIdea extracted_business_name = extract_business_name_from_content(filtered_sections) business_name = extracted_business_name or request.businessIdea or "Business Plan" # Business name as main title with enhanced styling - PURPLE THEME title = doc.add_heading(business_name, 0) title.alignment = WD_ALIGN_PARAGRAPH.CENTER # Style the title with larger font and PURPLE color for run in title.runs: run.font.size = Pt(36) run.font.color.rgb = RGBColor(139, 92, 246) # PURPLE # Removed decorative line under title # Add subtitle with enhanced styling - PURPLE doc.add_paragraph() subtitle = doc.add_paragraph("Professional Business Plan Document") subtitle.alignment = WD_ALIGN_PARAGRAPH.CENTER subtitle_run = subtitle.runs[0] subtitle_run.font.size = Pt(18) subtitle_run.font.color.rgb = RGBColor(124, 58, 237) # DARKER PURPLE subtitle_run.italic = True # Add more spacing doc.add_paragraph() doc.add_paragraph() doc.add_paragraph() # Add document info in a styled box - PURPLE BORDER info_para = doc.add_paragraph() info_para.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER # Add background color and border styling info_run = info_para.add_run("Prepared by: Business Plan Generator") info_run.font.size = Pt(14) info_run.font.color.rgb = RGBColor(139, 92, 246) # PURPLE doc.add_paragraph() date_para = doc.add_paragraph() date_para.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER date_run = date_para.add_run(f"Date: {time.strftime('%B %Y')}") date_run.font.size = Pt(14) date_run.font.color.rgb = RGBColor(139, 92, 246) # PURPLE # Confidentiality statement removed - now in footer of every page # Add more spacing before footer doc.add_paragraph() doc.add_paragraph() doc.add_paragraph() doc.add_paragraph() # Add footer with enhanced styling - PURPLE footer = doc.add_paragraph("Confidential Business Information") footer.alignment = WD_ALIGN_PARAGRAPH.CENTER footer_run = footer.runs[0] footer_run.font.size = Pt(12) footer_run.font.color.rgb = RGBColor(139, 92, 246) # PURPLE footer_run.bold = True # Removed decorative line above footer # ===== TABLE OF CONTENTS ===== # Add page break before TOC doc.add_page_break() # Add TOC title toc_title = doc.add_heading("Table of Contents", level=1) toc_title.alignment = WD_ALIGN_PARAGRAPH.CENTER # Removed decorative line under TOC title # Add spacing before TOC entries doc.add_paragraph() # Sort sections according to predefined order (do this before TOC generation) try: sorted_sections = sort_sections_by_order(filtered_sections) except Exception as e: # Fallback: use filtered order if sorting fails sorted_sections = filtered_sections # Generate TOC entries with actual page numbers # Since each section starts on a new page, we can calculate page numbers toc_items = [] current_page = 3 # Start after cover (1) and TOC (2) for section in sorted_sections: if section.content and section.content.strip(): # Add main section toc_items.append({ "title": section.title, "level": 1, "page": current_page }) # Skip subheadings and chart titles - only include main section headings current_page += 1 # Each section starts on a new page # Add the actual TOC entries for item in toc_items: # Create TOC entry paragraph toc_para = doc.add_paragraph() toc_para.paragraph_format.space_after = Pt(6) # Add section title title_run = toc_para.add_run(item["title"]) title_run.font.size = Pt(11) # Add dots and page number if item["level"] == 1: # Main section - bold title_run.bold = True # Add dots dots_run = toc_para.add_run(" " + "." * (60 - len(item["title"])) + " ") dots_run.font.size = Pt(11) dots_run.font.color.rgb = RGBColor(128, 128, 128) # Gray # Add page number page_run = toc_para.add_run(str(item["page"])) page_run.font.size = Pt(11) page_run.bold = True else: # Subsection - indented toc_para.paragraph_format.left_indent = Inches(0.3) # Add dots dots_run = toc_para.add_run(" " + "." * (50 - len(item["title"])) + " ") dots_run.font.size = Pt(11) dots_run.font.color.rgb = RGBColor(128, 128, 128) # Gray # Add page number page_run = toc_para.add_run(str(item["page"])) page_run.font.size = Pt(11) # logging.info(f"Generated {len(toc_items)} TOC items with actual page numbers") # Executive Summary is now handled as a regular section above # ===== MAIN SECTIONS ===== for i, section in enumerate(sorted_sections): if section.content.strip(): # Add page break before each section (including Executive Summary) doc.add_page_break() # Don't add section heading here - it's already in the TOC # The section title is already displayed in the Table of Contents # Process content with inline charts - use ORIGINAL content to preserve chart markers process_content_with_inline_charts(doc, section.content, request.businessIdea or "Business") doc.add_paragraph() # Charts are now handled inline with the sections above # Save to bytes buffer = io.BytesIO() doc.save(buffer) buffer.seek(0) doc_bytes = buffer.getvalue() buffer.close() filename = f"business_plan_{request.businessIdea or 'export'}.docx" return doc_bytes, filename