fabriquedoc-backend/main.py

187 lines
5.5 KiB
Python

import datetime
import logging
from fastapi import FastAPI
from fastapi.responses import FileResponse
from pydantic import BaseModel
from typing import List
import pypandoc
import json
from fastapi.testclient import TestClient
import os
from wand.image import Image
from wand.color import Color
import shutil
class DocumentSpecs(BaseModel):
format: str
style: str
linkcolor: str
tocdepth: int
pdfengine: str
content: str
fontsize: int
paperwidth: int
paperheight: int
ratio: int
margin: int
vmargin: int
extension: str
class FormatParameters(BaseModel):
linkcolor: str
tocdepth: int
pdfengine: str
fontsize: int
paperwidth: int
paperheight: int
ratio: int
margin: int
vmargin: int
extension: str
class Styles(BaseModel):
styles: List[str]
class Formats(BaseModel):
formats: List[str]
class App(BaseModel):
app: str
def convert_pdf(filename, filetype, output_path, resolution=300):
""" Convert a PDF into images.
All the pages will give a single png file with format:
{pdf_filename}-{page_number}.png
The function removes the alpha channel from the image and
replace it with a white background.
"""
all_pages = Image(filename=filename, resolution=resolution)
for i, page in enumerate(all_pages.sequence):
with Image(page) as img:
img.format = filetype
img.background_color = Color('white')
img.alpha_channel = 'remove'
image_filename = os.path.splitext(os.path.basename(filename))[0]
image_filename = f'{image_filename}-{i}.{filetype}'
image_filename = os.path.join(output_path, image_filename)
img.save(filename=image_filename)
app = FastAPI()
@app.get("/")
async def get_root():
app = App(app='fabriquedoc')
return app
# function to list only directories inside given directory
def list_dir(path):
return [f for f in os.listdir(path) if os.path.isdir(os.path.join(path, f))]
@app.get("/styles/")
async def get_styles():
styles = Styles(styles=list_dir("./styles"))
return styles
@app.get("/formats/{style}/")
async def get_formats(style: str):
formats = Formats(formats=list_dir(f"./styles/{style}/"))
return formats
@app.get("/format_parameters/{style}/{format}/")
async def get_format_parameters(style: str, format: str):
# open styles/format_parameters.json as a dictionary
with open(f"./styles/{style}/format_parameters.json", "r") as f:
format_data = json.load(f).get(format)
logging.log(logging.INFO, str(format_data))
# load data from format_data into the FormatParameters object
parameters = FormatParameters(**format_data)
return parameters
@app.get("/generer/")
async def generer(specs: DocumentSpecs):
header_file = f'{os.getcwd()}/styles/{specs.style}/{specs.format}/header.tex'
cover_file = f'{os.getcwd()}/styles/{specs.style}/{specs.format}/cover.tex'
datef = datetime.datetime.now().strftime("%m-%d-%Y")
os.makedirs("out", exist_ok=True)
output_file = f"./out/{specs.style}-{specs.format}-{datef}-output.pdf"
filters = ['latex-emoji.lua']
pdoc_args = [
f'--include-in-header={header_file}',
f'--include-after-body={cover_file}',
'--listings',
'--dpi=300',
f'--toc-depth={specs.tocdepth}',
f'--pdf-engine={specs.pdfengine}',
'-V', f'linkcolor={specs.linkcolor}',
'-V', f'fontsize={specs.fontsize}pt',
'-V', f'geometry:paperwidth={round(specs.paperwidth * specs.ratio / 100,-1) / 300}in',
'-V', f'geometry:paperheight={round(specs.paperwidth * specs.ratio / 100,-1) / 300}in',
'-V', f'geometry:left={specs.margin / 300}in',
'-V', f'geometry:right={specs.margin / 300}in',
'-V', f'geometry:top={specs.vmargin / 300}in',
'-V', f'geometry:bottom={specs.vmargin / 300}in'
]
try:
logging.info("Dossier courant = " + os.getcwd())
result = pypandoc.convert_text(source=specs.content,
to='pdf',
format='markdown+implicit_figures+smart+emoji',
encoding='utf-8',
extra_args=pdoc_args,
filters=filters,
cworkdir=os.getcwd(),
outputfile=output_file
)
except RuntimeError as rerr:
logging.exception(rerr)
except OSError as oerr:
logging.exception(oerr)
if specs.extension in ["png", "jpg"]:
zip_filename = os.path.join("out", os.path.splitext(os.path.basename(output_file))[0])
png_output_dir = "./out/png_output"
if not os.path.exists(png_output_dir):
os.mkdir(png_output_dir)
try:
convert_pdf(output_file,
specs.extension,
png_output_dir,
resolution=300)
shutil.make_archive(base_name=zip_filename,
format='zip',
root_dir=png_output_dir)
shutil.rmtree(png_output_dir)
except Exception as e:
logging.exception(e)
return FileResponse(zip_filename + ".zip")
elif specs.extension == "pdf":
return FileResponse(output_file)
else:
return 0
client = TestClient(app)
def test_getroot():
response = client.get("/")
assert response.status_code == 200