Latex_builder / app.py
euler314's picture
Create app.py
b7d8ac9 verified
raw
history blame
14.5 kB
import streamlit as st
import subprocess
import tempfile
import base64
from pathlib import Path
import os
# Set page configuration
st.set_page_config(page_title="LaTeX Editor & Compiler", page_icon="πŸ“", layout="wide")
# Function to convert LaTeX to PDF
def latex_to_pdf(latex_code):
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 display PDF
def display_pdf(pdf_data):
base64_pdf = base64.b64encode(pdf_data).decode('utf-8')
pdf_display = f"""
<iframe
src="data:application/pdf;base64,{base64_pdf}"
width="100%"
height="600"
style="border: none;"
></iframe>
"""
st.markdown(pdf_display, unsafe_allow_html=True)
# 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'<a href="data:application/pdf;base64,{b64_pdf}" download="{filename}" class="download-button">Download PDF</a>'
# LaTeX package reference
latex_packages = {
"Document": {
"\\usepackage{geometry}": "Page layout customization",
"\\usepackage{fancyhdr}": "Custom headers and footers",
"\\usepackage{titlesec}": "Title formatting",
"\\usepackage{hyperref}": "Hyperlinks and PDF metadata"
},
"Math": {
"\\usepackage{amsmath}": "Enhanced math formatting",
"\\usepackage{amssymb}": "Mathematical symbols",
"\\usepackage{mathtools}": "Extensions to amsmath",
"\\usepackage{physics}": "Physics notation"
},
"Graphics": {
"\\usepackage{graphicx}": "Include images",
"\\usepackage{tikz}": "Create vector graphics",
"\\usepackage{pgfplots}": "Create plots",
"\\usepackage{float}": "Better figure placement"
},
"Tables": {
"\\usepackage{tabularx}": "Flexible tables",
"\\usepackage{booktabs}": "Professional tables",
"\\usepackage{colortbl}": "Colored tables",
"\\usepackage{multirow}": "Multi-row cells"
},
"Content": {
"\\usepackage{listings}": "Code syntax highlighting",
"\\usepackage{minted}": "Advanced code highlighting",
"\\usepackage{biblatex}": "Bibliography management",
"\\usepackage{xcolor}": "Color support"
}
}
# LaTeX commands reference
latex_commands = {
"Document Structure": {
"\\documentclass{article}": "Specifies the type of document",
"\\begin{document}": "Starts the document content",
"\\end{document}": "Ends the document content",
"\\title{...}": "Sets the document title",
"\\author{...}": "Sets the document author",
"\\date{...}": "Sets the document date",
"\\maketitle": "Prints the title, author, and date"
},
"Sections": {
"\\section{...}": "Creates a section",
"\\subsection{...}": "Creates a subsection",
"\\subsubsection{...}": "Creates a subsubsection",
"\\paragraph{...}": "Creates a paragraph heading",
"\\tableofcontents": "Generates a table of contents"
},
"Text Formatting": {
"\\textbf{...}": "Bold text",
"\\textit{...}": "Italic text",
"\\underline{...}": "Underlined text",
"\\emph{...}": "Emphasized text",
"\\texttt{...}": "Typewriter text",
"\\textsc{...}": "Small caps text",
"\\textsf{...}": "Sans-serif text",
"\\color{red}{...}": "Colored text (requires xcolor)"
},
"Math": {
"$...$": "Inline math mode",
"$$...$$": "Display math mode",
"\\begin{equation}...\\end{equation}": "Numbered equation",
"\\begin{align}...\\end{align}": "Aligned equations",
"\\frac{num}{denom}": "Fraction",
"\\dfrac{num}{denom}": "Display fraction",
"\\sqrt{...}": "Square root",
"\\sqrt[n]{...}": "nth root",
"\\sum_{lower}^{upper}": "Summation",
"\\prod_{lower}^{upper}": "Product",
"\\int_{lower}^{upper}": "Integral",
"\\lim_{x \\to value}": "Limit",
"\\vec{...}": "Vector",
"\\overline{...}": "Overline",
"\\hat{...}": "Hat accent",
"\\partial": "Partial derivative"
},
"Lists": {
"\\begin{itemize}...\\end{itemize}": "Bulleted list",
"\\begin{enumerate}...\\end{enumerate}": "Numbered list",
"\\begin{description}...\\end{description}": "Description list",
"\\item": "List item",
"\\item[custom]": "Custom label item"
},
"Tables": {
"\\begin{table}...\\end{table}": "Table environment",
"\\begin{tabular}{cols}...\\end{tabular}": "Create a table",
"\\hline": "Horizontal line in table",
"\\cline{i-j}": "Partial horizontal line",
"cell1 & cell2 & cell3 \\\\": "Table row",
"\\multicolumn{n}{align}{content}": "Span multiple columns"
},
"Figures": {
"\\begin{figure}...\\end{figure}": "Figure environment",
"\\includegraphics[width=0.8\\textwidth]{filename}": "Include an image",
"\\caption{...}": "Add a caption to a figure or table",
"\\label{...}": "Add a label for cross-referencing",
"\\ref{...}": "Reference a labeled item"
},
"Citations": {
"\\cite{key}": "Citation",
"\\bibliography{file}": "Bibliography source",
"\\bibliographystyle{style}": "Bibliography style"
}
}
# 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 custom CSS
st.markdown("""
<style>
.editor-container {
border: 1px solid #ccc;
border-radius: 5px;
padding: 10px;
background-color: #f8f9fa;
}
.download-button {
display: inline-block;
padding: 0.5em 1em;
background-color: #4CAF50;
color: white !important;
text-align: center;
text-decoration: none;
font-size: 16px;
border-radius: 4px;
transition: background-color 0.3s;
margin-top: 10px;
}
.download-button:hover {
background-color: #45a049;
}
.stTextArea textarea {
font-family: 'Courier New', Courier, monospace !important;
font-size: 14px !important;
line-height: 1.5 !important;
}
.latex-command {
background-color: #f1f1f1;
padding: 2px 4px;
border-radius: 3px;
font-family: monospace;
cursor: pointer;
}
.reference-tabs .stTabs {
background-color: #f5f5f5;
border-radius: 5px;
padding: 10px;
}
.stMarkdown h4 {
margin-top: 0.5rem !important;
margin-bottom: 0.5rem !important;
}
.command-category {
margin-bottom: 15px !important;
}
</style>
""", unsafe_allow_html=True)
# JavaScript for copying commands to editor
st.markdown("""
<script>
function copyToEditor(text) {
const textareas = document.getElementsByTagName('textarea');
if (textareas.length > 0) {
const editor = textareas[0];
const start = editor.selectionStart;
const end = editor.selectionEnd;
const value = editor.value;
editor.value = value.substring(0, start) + text + value.substring(end);
editor.selectionStart = editor.selectionEnd = start + text.length;
editor.focus();
}
}
</script>
""", unsafe_allow_html=True)
# Main application
def main():
st.title("LaTeX Editor & PDF Compiler")
# Create layout with sidebar
col1, col2 = st.columns([3, 2])
with col1:
st.subheader("LaTeX Editor")
# Initialize session state
if 'latex_code' not in st.session_state:
st.session_state.latex_code = default_template
# LaTeX editor
latex_code = st.text_area(
"Edit your LaTeX document:",
value=st.session_state.latex_code,
height=500,
key="latex_editor"
)
st.session_state.latex_code = latex_code
# Control buttons
col1_1, col1_2, col1_3 = st.columns(3)
with col1_1:
if st.button("Compile PDF", use_container_width=True):
st.session_state.compile_clicked = True
with col1_2:
if st.button("Load Template", use_container_width=True):
st.session_state.latex_code = default_template
st.rerun()
with col1_3:
if st.button("Clear Editor", use_container_width=True):
st.session_state.latex_code = ""
st.rerun()
with col2:
st.subheader("PDF Preview")
# PDF preview
if 'compile_clicked' in st.session_state and st.session_state.compile_clicked:
with st.spinner("Compiling LaTeX to PDF..."):
pdf_data, stdout, stderr = latex_to_pdf(latex_code)
if pdf_data:
st.session_state.pdf_data = pdf_data
display_pdf(pdf_data)
# Download button
st.markdown(get_download_link(pdf_data), unsafe_allow_html=True)
st.session_state.compile_clicked = False
else:
st.error("Compilation Error")
with st.expander("Error Details"):
st.text(stdout)
st.text(stderr)
st.session_state.compile_clicked = False
# Display previous PDF if available
elif 'pdf_data' in st.session_state and st.session_state.pdf_data:
display_pdf(st.session_state.pdf_data)
st.markdown(get_download_link(st.session_state.pdf_data), unsafe_allow_html=True)
else:
st.info("Compile your LaTeX document to see the PDF preview")
# LaTeX Reference Sidebar
st.sidebar.title("LaTeX Reference")
# Search functionality
search_query = st.sidebar.text_input("Search commands or packages", "")
if search_query:
st.sidebar.subheader("Search Results")
found = False
# Search commands
for category, commands in latex_commands.items():
filtered_commands = {cmd: desc for cmd, desc in commands.items()
if search_query.lower() in cmd.lower() or search_query.lower() in desc.lower()}
if filtered_commands:
found = True
with st.sidebar.expander(f"{category} ({len(filtered_commands)} results)"):
for cmd, desc in filtered_commands.items():
st.markdown(f"<div class='latex-command' onclick=\"copyToEditor('{cmd}')\">{cmd}</div> - {desc}", unsafe_allow_html=True)
# Search packages
for category, packages in latex_packages.items():
filtered_packages = {pkg: desc for pkg, desc in packages.items()
if search_query.lower() in pkg.lower() or search_query.lower() in desc.lower()}
if filtered_packages:
found = True
with st.sidebar.expander(f"Packages: {category} ({len(filtered_packages)} results)"):
for pkg, desc in filtered_packages.items():
st.markdown(f"<div class='latex-command' onclick=\"copyToEditor('{pkg}')\">{pkg}</div> - {desc}", unsafe_allow_html=True)
if not found:
st.sidebar.info("No matching commands or packages found")
else:
# Display full reference when not searching
tabs = st.sidebar.tabs(["Commands", "Packages"])
with tabs[0]:
for category, commands in latex_commands.items():
with st.expander(category):
for cmd, desc in commands.items():
st.markdown(f"<div class='latex-command' onclick=\"copyToEditor('{cmd}')\">{cmd}</div> - {desc}", unsafe_allow_html=True)
with tabs[1]:
for category, packages in latex_packages.items():
with st.expander(category):
for pkg, desc in packages.items():
st.markdown(f"<div class='latex-command' onclick=\"copyToEditor('{pkg}')\">{pkg}</div> - {desc}", unsafe_allow_html=True)
if __name__ == "__main__":
main()