Spaces:
Running
Running
import sys | |
import subprocess | |
from pathlib import Path | |
from os import getenv | |
from importlib.metadata import version | |
from PIL import Image | |
from minijinja import Environment | |
import gradio as gr | |
# Config | |
document_name = "document-template.typ" | |
do_share = getenv("DO_SHARE") | |
concurrency_limit = getenv("CONCURRENCY_LIMIT", 1) | |
typst_bin_path = getenv("TYPST_BIN", "/home/user/app/typst") | |
# App description | |
title = "Typst-based PDF generation" | |
examples = [ | |
""" | |
I begin this story with a neutral statement. | |
Basically this is a very silly test. | |
""", | |
] | |
description_head = f""" | |
# {title} | |
## Overview | |
We use https://typst.app to generate a PDF file with some parameters from this Gradio app. | |
""".strip() | |
tech_env = f""" | |
#### Environment | |
- Python: {sys.version} | |
""".strip() | |
def app_version(bin_path): | |
return subprocess.run([bin_path, "--version"], capture_output=True, text=True) | |
def typst_compile(typst_bin_path, filename, export_template): | |
args = [typst_bin_path, "compile", filename, export_template] | |
print("Running:", args) | |
return subprocess.run( | |
args, | |
capture_output=False, | |
) | |
def convert_document(bin_paths, text): | |
print("Converting...") | |
env = Environment( | |
templates={ | |
"document": Path("document-template.typ").read_text(), | |
} | |
) | |
formatted_document = env.render_template("document", text=text) | |
# Write the rendered document to a temporary file | |
document_file = Path("document.typ") | |
document_file.write_text(formatted_document) | |
# Compile the .typ file to a .pdf file | |
c = typst_compile(bin_paths["typst"], "document.typ", "document.pdf") | |
if c.returncode != 0: | |
raise gr.Error("Typst compilation failed.") | |
print("Result:", c) | |
# Extract the first page of the PDF file | |
c = typst_compile(bin_paths["typst"], "document.typ", "file-{n}.png") | |
if c.returncode != 0: | |
raise gr.Error("Typst exporting to PNGs failed.") | |
print("Result:", c) | |
first_page = Path("file-1.png") | |
if not first_page.exists(): | |
raise gr.Error("The first page has not been exported.") | |
# Move the image to an object | |
image = Image.open(first_page.absolute()) | |
# Remove the temporary files | |
first_page.unlink(missing_ok=True) | |
document_file.unlink(missing_ok=True) | |
return image | |
typst_version_info = app_version(typst_bin_path) | |
if typst_version_info.returncode != 0: | |
print("Error: Typst version command failed.") | |
exit(1) | |
r_tech_env = f""" | |
#### Typst Environment | |
``` | |
{typst_version_info.stdout.strip()} | |
``` | |
""".strip() | |
tech_libraries = f""" | |
#### Libraries | |
- gradio: {version("gradio")} | |
""".strip() | |
def generate_pdf(text, progress=gr.Progress()): | |
if not text: | |
raise gr.Error("Please paste your text.") | |
# Remove the previous PDF file and Typst file | |
Path("document.pdf").unlink(missing_ok=True) | |
Path("document.typ").unlink(missing_ok=True) | |
gr.Info("Generating the PDF document", duration=1) | |
bin_paths = { | |
"typst": typst_bin_path, | |
} | |
image = convert_document(bin_paths, text) | |
gr.Success("Finished!", duration=2) | |
pdf_file = gr.DownloadButton( | |
label="Download document.pdf", | |
value="./document.pdf", | |
visible=True, | |
) | |
return [image, pdf_file] | |
demo = gr.Blocks( | |
title=title, | |
analytics_enabled=False, | |
theme=gr.themes.Base(), | |
) | |
with demo: | |
gr.Markdown(description_head) | |
gr.Markdown("## Usage") | |
with gr.Row(): | |
text = gr.Textbox(label="Text", autofocus=True, max_lines=50) | |
with gr.Column(): | |
pdf_file = gr.DownloadButton(label="Download PDF", visible=False) | |
preview_image = gr.Image( | |
label="Preview image", | |
) | |
gr.Button("Generate").click( | |
generate_pdf, | |
concurrency_limit=concurrency_limit, | |
inputs=text, | |
outputs=[preview_image, pdf_file], | |
) | |
with gr.Row(): | |
gr.Examples(label="Choose an example", inputs=text, examples=examples) | |
gr.Markdown("### Gradio app uses:") | |
gr.Markdown(tech_env) | |
gr.Markdown(r_tech_env) | |
gr.Markdown(tech_libraries) | |
if __name__ == "__main__": | |
demo.queue() | |
if do_share: | |
demo.launch(share=True) | |
else: | |
demo.launch() | |