from flask import Flask, request, render_template_string, send_file import markdown import imgkit import os import traceback from io import BytesIO app = Flask(__name__) # Use a directory within the app's working directory to avoid permission issues. # This makes it more compatible with various deployment environments. TEMP_DIR = os.path.join(os.getcwd(), "temp") # Create the temporary directory if it doesn't exist. try: os.makedirs(TEMP_DIR, exist_ok=True) except Exception as e: # Log an error if the directory cannot be created. print(f"Error creating temp directory: {e}") # Define default values for styling options for a cleaner initial state. DEFAULT_STYLES = { "font_family": "'Arial', sans-serif", "font_size": "16", "text_color": "#333333", "background_color": "#ffffff", "code_bg_color": "#f4f4f4", "custom_css": "" } @app.route("/", methods=["GET", "POST"]) def index(): """ Main route to handle form submission, Markdown processing, and rendering. """ preview_html = None download_available = False error_message = None # On POST, process the form data. On GET, use defaults. if request.method == "POST": markdown_text = request.form.get("markdown_text", "") download_type = request.form.get("download_type", "png") # Get styling options from the form, falling back to defaults. styles = { "font_family": request.form.get("font_family", DEFAULT_STYLES["font_family"]), "font_size": request.form.get("font_size", DEFAULT_STYLES["font_size"]), "text_color": request.form.get("text_color", DEFAULT_STYLES["text_color"]), "background_color": request.form.get("background_color", DEFAULT_STYLES["background_color"]), "code_bg_color": request.form.get("code_bg_color", DEFAULT_STYLES["code_bg_color"]), "custom_css": request.form.get("custom_css", DEFAULT_STYLES["custom_css"]) } include_fontawesome = "include_fontawesome" in request.form else: # GET request markdown_text = "" download_type = "png" styles = DEFAULT_STYLES.copy() include_fontawesome = False if request.method == "POST" and markdown_text: try: # Convert Markdown to HTML using python-markdown library. # Extensions for tables and fenced code blocks are enabled. html_content = markdown.markdown(markdown_text, extensions=['fenced_code', 'tables']) # Optional: Include Font Awesome CSS if the user checked the box. fontawesome_link = "" if include_fontawesome: fontawesome_link = '' # Dynamically generate the CSS style block from user options. style_block = f""" """ # Combine everything into a full HTML document. full_html = f""" {fontawesome_link} {style_block} {html_content} """ # Set flags and content for the frontend preview. preview_html = full_html download_available = True # If the user clicked the "Download" button. if "download" in request.form: if download_type == "html": # For HTML download, send the generated HTML directly. return send_file( BytesIO(full_html.encode("utf-8")), as_attachment=True, download_name="output.html", mimetype="text/html" ) else: # For PNG download # Use imgkit to convert the HTML string to a PNG image. # The 'quiet' option suppresses console output from wkhtmltoimage. png_path = os.path.join(TEMP_DIR, "output.png") imgkit.from_string(full_html, png_path, options={"quiet": "", 'encoding': "UTF-8"}) return send_file( png_path, as_attachment=True, download_name="output.png", mimetype="image/png" ) except Exception as e: # In case of any error, display a detailed message to the user. error_message = f"An error occurred: {str(e)}" print(f"Error: {traceback.format_exc()}") # Render the main page template with all the necessary variables. return render_template_string(""" Advanced Markdown to PNG/HTML Converter

Advanced Markdown to PNG/HTML Converter

Tip: To include images, use full public URLs (e.g., `https://.../image.png`). To use icons, check "Include Font Awesome" below and use tags like ``.
Styling Options
{% if download_available %} {% endif %}
{% if error_message %}

{{ error_message }}

{% endif %} {% if preview_html %}

Preview

{{ preview_html | safe }}
{% endif %} """, styles=styles, markdown_text=markdown_text, download_type=download_type, include_fontawesome=include_fontawesome, download_available=download_available, preview_html=preview_html, error_message=error_message ) if __name__ == "__main__": # It's recommended to run Flask applications using a production-ready WSGI server. # The built-in development server is used here for convenience. app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 7860)))