From 2dacaa6abf0e67e0cdf36e30b00bbe6768a7b3cc Mon Sep 17 00:00:00 2001 From: Reza Behzadan Date: Thu, 25 Jan 2024 21:38:13 +0330 Subject: [PATCH] It seems to work! --- .gitignore | 1 + Dockerfile | 2 +- main.py | 63 ++++++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 51 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index d80b05b..8193fb4 100644 --- a/.gitignore +++ b/.gitignore @@ -161,6 +161,7 @@ cython_debug/ ## By rbehzadan@gmail.com .archive/ +.texlive/ samples/ *_[0-9] *_[0-9][0-9] diff --git a/Dockerfile b/Dockerfile index d3022d2..decabde 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ FROM archlinux:base RUN pacman -Sy --noconfirm curl reflector && \ reflector --latest 5 --sort rate --save /etc/pacman.d/mirrorlist && \ sed -i '/\[options\]/a XferCommand = /usr/bin/curl -C - --fail --retry 3 --retry-delay 3 -o %o %u' /etc/pacman.conf && \ - pacman -Syu --noconfirm --needed texlive-bin texlive-core texlive-latexextra texlive-fontsrecommended texlive-langenglish texlive-langgerman && \ + pacman -Syu --noconfirm --needed texlive-basic texlive-bibtexextra texlive-bin texlive-binextra texlive-context texlive-fontsrecommended texlive-fontsextra texlive-fontutils texlive-formatsextra texlive-langenglish texlive-langeuropean texlive-langfrench texlive-langgerman texlive-latex texlive-latexextra texlive-latexrecommended texlive-luatex texlive-mathscience texlive-metapost texlive-music texlive-pictures texlive-plaingeneric texlive-pstricks texlive-publishers && \ pacman -Syu --noconfirm --needed python-fastapi uvicorn python-python-multipart && \ yes | pacman -Scc diff --git a/main.py b/main.py index 0b7fa8b..6fc2f06 100644 --- a/main.py +++ b/main.py @@ -1,25 +1,60 @@ -import asyncio -from fastapi import FastAPI, File, UploadFile +from fastapi import FastAPI, File, UploadFile, HTTPException from fastapi.responses import StreamingResponse from io import BytesIO +import asyncio +import tempfile +import zipfile +import os app = FastAPI() @app.post("/tex2pdf") -async def convert_to_pdf(file: UploadFile = File(...)): - input_file = BytesIO(await file.read()) +async def convert_to_pdf(zip_file: UploadFile = File(...)): + if zip_file.filename.endswith('.zip'): + with tempfile.TemporaryDirectory() as tmpdirname: + # Unpack the zip file + with zipfile.ZipFile(BytesIO(await zip_file.read())) as z: + z.extractall(tmpdirname) + + # Change working directory to tmpdirname + os.chdir(tmpdirname) + + # Find the main LaTeX file (assuming a convention, e.g., main.tex) + main_tex_file = 'main.tex' + main_tex_path = os.path.join(tmpdirname, main_tex_file) + if not os.path.exists(main_tex_path): + raise HTTPException(status_code=400, detail="Main LaTeX file (main.tex) not found in the zip.") - process = await asyncio.create_subprocess_exec( - 'pdflatex', '-f', 'pdf', - stdin=asyncio.subprocess.PIPE, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) + # Compile the LaTeX document + cmd = ['pdflatex', '-interaction=nonstopmode', '-output-directory', tmpdirname, main_tex_path] + process = await asyncio.create_subprocess_exec(*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) + try: + print(f"Running pdflatex on {main_tex_path}") + stdout, stderr = await asyncio.wait_for(process.communicate(), timeout=120) + # print(f"pdflatex output: {stdout.decode()}") + # print(f"pdflatex errors: {stderr.decode()}") + except asyncio.TimeoutError: + return {"error": "Conversion timed out."} - output_pdf, error = await process.communicate(input=input_file.getvalue()) - if process.returncode == 0: - return StreamingResponse(BytesIO(output_pdf), media_type='application/pdf') + if process.returncode != 0: + # Compilation failed + return { + "error": "Conversion failed.", + "details": { + "stderr": stderr.decode(), + "stdout": stdout.decode(), + }, + } + + # Assuming the output PDF has the same base name as the main LaTeX file + output_pdf_path = os.path.join(tmpdirname, 'main.pdf') + if os.path.exists(output_pdf_path): + with open(output_pdf_path, 'rb') as f: + pdf_content = f.read() + return StreamingResponse(BytesIO(pdf_content), media_type='application/pdf') + else: + return {"error": "PDF file not generated."} else: - return {"error": "Conversion failed.", "details": error.decode()} + raise HTTPException(status_code=400, detail="Uploaded file is not a zip file.")