from pathlib import Path import sys import markdown from fastapi import FastAPI from jinja2 import Environment, DictLoader from markupsafe import Markup from .image_service import ImageService, FileHandler class MarkdownRenderer: def __init__(self, file_path: str = None, app: FastAPI=None): """ Initialize the MarkdownRenderer with a Jinja2 environment and custom functions. """ self.app = app self.image_service = ImageService(self.app) self.jinja_env = self._create_jinja_environment() self.file_path = file_path def _create_jinja_environment(self) -> Environment: """ Create and configure the Jinja2 environment with custom functions. Returns: Environment: A configured Jinja2 environment. """ env = Environment(loader=DictLoader({"base_template": "{{ content | safe }}"})) env.globals.update({ "img_left_overlay": self.img_left_overlay, "box": self.box, "note": self.note, "warning": self.warning, "link_to": self.link_to, "slider": self.slider, "image": self.get_image, # Add image handler function }) return env def img_left_overlay(self, src: str) -> str: """Render an image with overlay.""" return f'''
Overlay Image
Overlay Text
''' def box(self, title: str, content: str) -> str: """Render a box component.""" return f'''
{title}

{content}

''' def note(self, content: str) -> str: """Render a note component.""" return f'''

{content}

''' def link_to(self, title: str, url: str) -> str: """Render a link component.""" return f''' {title} ''' def warning(self, content: str) -> str: """Render a warning component.""" return f'''
⚠️

{content}

''' def slider(self, options: dict, images: list) -> str: """Render a slider component.""" import uuid modal_id = uuid.uuid4().hex.upper()[0:6] html_content = [] html_content.append('
') for i, val in enumerate(images): self.image_service = ImageService( self.app ) modal_id_current = f"{modal_id}_{i}" modal_id_next = f"{modal_id}_{i + 1}" if i + 1 < len(images) else f"{modal_id}_0" category = FileHandler().get_category(self.file_path) thumbnal_img = self.image_service.image_tag(category = category, image_type = "thumbnails",filename = val,alt="A better description later on") modal_img = self.image_service.image_tag(category = category, image_type = "large",filename = val,alt="A better description later on") html_content.append(f""" """) html_content.append('
') return '\n'.join(html_content) def _get_category(self): if isinstance(self.file_path, str): this_path = Path(self.file_path) return this_path.parent.name return True def get_image(self, image_type: str, filename: str, alt: str = "", width: int = None, height: int = None,css_class:str=None) -> Markup: """ Generate a dynamic HTML tag for an image using ImageService's image_tag method. """ valid_types = ['thumbnails', 'large', 'small', 'original'] if image_type not in valid_types: sys.tracebacklimit = 0 raise ValueError( f"Invalid image type: {image_type}. Must be one of {valid_types}" ) tag = self.image_service.image_tag( category=self._get_category(), image_type=image_type, filename=filename, alt=alt, width=width, height=height, css_class=css_class ) return Markup(tag) def render_markdown_with_jinja(self, markdown_content: str): """ Convert Markdown to HTML and apply Jinja2 rendering for custom tags. Args: markdown_content (str): Raw Markdown content. Returns: tuple: Rendered HTML content and metadata as a dictionary. """ md = markdown.Markdown(extensions=["extra", "nl2br", "meta"]) intermediate_html = md.convert(markdown_content) metadata = {key: " ".join(value) for key, value in md.Meta.items()} if md.Meta else {} # Step 3: Pass the resulting HTML with Jinja2 custom tags through Jinja2 template = self.jinja_env.get_template("base_template") final_html = template.render(content=intermediate_html) # Step 4: Re-render final_html in Jinja2 for embedded tags like {{ image(...) }} final_output = self.jinja_env.from_string(final_html).render() return final_output, metadata