Yehor's picture
INit
39d4462
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()