import streamlit as st import subprocess import tempfile import base64 from pathlib import Path import os import shutil import io from PIL import Image import fitz # PyMuPDF # Set page configuration st.set_page_config(page_title="LaTeX Editor & Compiler", page_icon="📝", layout="wide") # Check if pdflatex is available def is_pdflatex_installed(): return shutil.which("pdflatex") is not None # Function to convert LaTeX to PDF def latex_to_pdf(latex_code): # Check if pdflatex is installed if not is_pdflatex_installed(): st.error("pdflatex not found. Debug info:") st.code(f"PATH: {os.environ.get('PATH')}") result = subprocess.run(["which", "pdflatex"], capture_output=True, text=True) st.code(f"which pdflatex: {result.stdout} {result.stderr}") return None, "", "Error: pdflatex is not installed or not in PATH." with tempfile.TemporaryDirectory() as temp_dir: temp_path = Path(temp_dir) tex_file = temp_path / "document.tex" pdf_file = temp_path / "document.pdf" # Write LaTeX code to file with open(tex_file, "w") as f: f.write(latex_code) try: # Run pdflatex to compile the LaTeX file process = subprocess.run( ["pdflatex", "-interaction=nonstopmode", "-output-directory", temp_dir, str(tex_file)], capture_output=True, text=True ) # Check if PDF was created if pdf_file.exists(): with open(pdf_file, "rb") as file: pdf_data = file.read() return pdf_data, process.stdout, process.stderr else: return None, process.stdout, process.stderr except Exception as e: return None, "", str(e) # Function to create download link for PDF def get_download_link(pdf_data, filename="document.pdf"): b64_pdf = base64.b64encode(pdf_data).decode() return f'Download PDF' # Convert PDF to image for preview def render_pdf_preview(pdf_data): if not pdf_data: return None try: # Create a file-like object from the PDF data pdf_stream = io.BytesIO(pdf_data) # Open PDF with PyMuPDF (fitz) pdf_document = fitz.open(stream=pdf_stream, filetype="pdf") # Render pages as images images = [] for page_num in range(min(3, len(pdf_document))): # Preview first 3 pages max page = pdf_document.load_page(page_num) pix = page.get_pixmap(matrix=fitz.Matrix(2, 2)) # Zoom factor 2 for better resolution img_data = pix.tobytes("png") img = Image.open(io.BytesIO(img_data)) images.append(img) pdf_document.close() return images except Exception as e: st.error(f"Error rendering PDF preview: {str(e)}") return None # Default LaTeX template default_template = r"""\documentclass{article} \usepackage[utf8]{inputenc} \usepackage{amsmath} \usepackage{amssymb} \usepackage{graphicx} \usepackage{hyperref} \title{LaTeX Document} \author{Your Name} \date{\today} \begin{document} \maketitle \section{Introduction} Your introduction here. Insert some text to demonstrate LaTeX. \section{Mathematical Expressions} \subsection{Equations} The famous Einstein's equation: \begin{equation} E = mc^2 \end{equation} The quadratic formula: \begin{equation} x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a} \end{equation} \subsection{Calculus} An integral example: \begin{equation} \int_{0}^{\pi} \sin(x) \, dx = 2 \end{equation} \section{Lists and Items} \subsection{Bullet Points} \begin{itemize} \item First item \item Second item \item Third item \end{itemize} \subsection{Numbered List} \begin{enumerate} \item First step \item Second step \item Third step \end{enumerate} \section{Tables} \begin{table}[h] \centering \begin{tabular}{|c|c|c|} \hline Cell 1 & Cell 2 & Cell 3 \\ \hline Data 1 & Data 2 & Data 3 \\ \hline \end{tabular} \caption{A simple table} \label{tab:simple} \end{table} \section{Conclusion} Your conclusion here. \end{document} """ # Add VS Code-like styling st.markdown(""" """, unsafe_allow_html=True) # Function to create a VS Code-like editor def vs_code_editor(key, height=500): editor_html = f"""
document.tex - LaTeX UTF-8
""" st.markdown(editor_html, unsafe_allow_html=True) # Create the actual editor with VS Code styling return st.text_area("", value=st.session_state.get(key, ""), height=height, key=key, label_visibility="collapsed", help="Type your LaTeX code here") # Main application def main(): # Set up a clean, dark theme st.markdown("

LaTeX Editor

", unsafe_allow_html=True) # Initialize session state if 'latex_code' not in st.session_state: st.session_state.latex_code = default_template if 'show_preview' not in st.session_state: st.session_state.show_preview = False # Display installation status if not is_pdflatex_installed(): st.warning("⚠️ LaTeX is not installed correctly. The PDF compilation feature will not work.") # Create layout - full width editor col1, col2 = st.columns([3, 2]) with col1: # VS Code-like editor with custom class for styling st.markdown('
', unsafe_allow_html=True) latex_code = vs_code_editor("latex_editor", height=500) st.markdown('
', unsafe_allow_html=True) st.session_state.latex_code = latex_code # Control buttons with VS Code styling st.markdown("""
""", unsafe_allow_html=True) # Hidden buttons to handle the clicks if st.button("Compile PDF", key="compile", help="Compile LaTeX to PDF"): st.session_state.compile_clicked = True if st.button("Load Template", key="load_template", help="Load default template"): st.session_state.latex_code = default_template st.rerun() if st.button("Clear Editor", key="clear_editor", help="Clear editor content"): st.session_state.latex_code = "" st.rerun() with col2: st.markdown("

Output

", unsafe_allow_html=True) # PDF compilation if 'compile_clicked' in st.session_state and st.session_state.compile_clicked: with st.spinner("Compiling..."): pdf_data, stdout, stderr = latex_to_pdf(latex_code) if pdf_data: st.session_state.pdf_data = pdf_data st.success("Compilation successful") # Toggle button for preview if st.button("Toggle Preview", help="Show or hide the PDF preview"): st.session_state.show_preview = not st.session_state.show_preview # Download button always available st.markdown(get_download_link(pdf_data), unsafe_allow_html=True) # Optional preview if st.session_state.show_preview: st.markdown('
', unsafe_allow_html=True) preview_images = render_pdf_preview(pdf_data) if preview_images: for i, img in enumerate(preview_images): st.image(img, caption=f"Page {i+1}", use_container_width=True, output_format="PNG") st.markdown('
', unsafe_allow_html=True) # Terminal output in collapsible section with st.expander("Terminal Output"): st.markdown('
', unsafe_allow_html=True) st.text(stdout) st.markdown('
', unsafe_allow_html=True) st.session_state.compile_clicked = False else: st.error("Compilation failed") st.markdown('
', unsafe_allow_html=True) st.text(stderr) st.markdown('
', unsafe_allow_html=True) st.session_state.compile_clicked = False # Display previous PDF if available elif 'pdf_data' in st.session_state and st.session_state.pdf_data: # Toggle button for preview if st.button("Toggle Preview", help="Show or hide the PDF preview"): st.session_state.show_preview = not st.session_state.show_preview # Download button always available st.markdown(get_download_link(st.session_state.pdf_data), unsafe_allow_html=True) # Optional preview if st.session_state.show_preview: st.markdown('
', unsafe_allow_html=True) preview_images = render_pdf_preview(st.session_state.pdf_data) if preview_images: for i, img in enumerate(preview_images): st.image(img, caption=f"Page {i+1}", use_container_width=True, output_format="PNG") st.markdown('
', unsafe_allow_html=True) else: st.info("Click 'Compile PDF' to generate output") if __name__ == "__main__": main()