import aiofiles import urllib import mistune import os async def write_to_file(filename: str, text: str) -> None: """Asynchronously write text to a file in UTF-8 encoding. Args: filename (str): The filename to write to. text (str): The text to write. """ # Ensure text is a string if not isinstance(text, str): text = str(text) # Convert text to UTF-8, replacing any problematic characters text_utf8 = text.encode('utf-8', errors='replace').decode('utf-8') async with aiofiles.open(filename, "w", encoding='utf-8') as file: await file.write(text_utf8) async def write_text_to_md(text: str, filename: str = "") -> str: """Writes text to a Markdown file and returns the file path. Args: text (str): Text to write to the Markdown file. Returns: str: The file path of the generated Markdown file. """ file_path = f"outputs/{filename[:60]}.md" await write_to_file(file_path, text) return urllib.parse.quote(file_path) def _preprocess_images_for_pdf(text: str) -> str: """Convert web image URLs to absolute file paths for PDF generation. Transforms /outputs/images/... URLs to absolute file:// paths that weasyprint can resolve. """ import re base_path = os.path.abspath(".") # Pattern to find markdown images with /outputs/ URLs def replace_image_url(match): alt_text = match.group(1) url = match.group(2) # Convert /outputs/... to absolute path if url.startswith("/outputs/"): abs_path = os.path.join(base_path, url.lstrip("/")) return f"![{alt_text}]({abs_path})" return match.group(0) # Match ![alt text](/outputs/images/...) pattern = r'!\[([^\]]*)\]\((/outputs/[^)]+)\)' return re.sub(pattern, replace_image_url, text) async def write_md_to_pdf(text: str, filename: str = "") -> str: """Converts Markdown text to a PDF file and returns the file path. Args: text (str): Markdown text to convert. Returns: str: The encoded file path of the generated PDF. """ file_path = f"outputs/{filename[:60]}.pdf" try: # Resolve css path relative to this backend module to avoid # dependency on the current working directory. current_dir = os.path.dirname(os.path.abspath(__file__)) css_path = os.path.join(current_dir, "styles", "pdf_styles.css") # Preprocess image URLs for PDF compatibility processed_text = _preprocess_images_for_pdf(text) # Set base_url to current directory for resolving any remaining relative paths base_url = os.path.abspath(".") from md2pdf.core import md2pdf md2pdf( file_path, raw=processed_text, css=css_path, base_url=base_url, ) print(f"Report written to {file_path}") except Exception as e: print(f"Error in converting Markdown to PDF: {e}") return "" encoded_file_path = urllib.parse.quote(file_path) return encoded_file_path async def write_md_to_word(text: str, filename: str = "") -> str: """Converts Markdown text to a DOCX file and returns the file path. Args: text (str): Markdown text to convert. Returns: str: The encoded file path of the generated DOCX. """ file_path = f"outputs/{filename[:60]}.docx" try: from docx import Document from htmldocx import HtmlToDocx # Convert report markdown to HTML html = mistune.html(text) # Create a document object doc = Document() # Convert the html generated from the report to document format HtmlToDocx().add_html_to_document(html, doc) # Saving the docx document to file_path doc.save(file_path) print(f"Report written to {file_path}") encoded_file_path = urllib.parse.quote(file_path) return encoded_file_path except Exception as e: print(f"Error in converting Markdown to DOCX: {e}") return ""