-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 18bbc0f
Showing
6 changed files
with
384 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# Use an official Python runtime as a parent image | ||
FROM python:3.10-slim-buster | ||
|
||
# Set the working directory in the container to /app | ||
WORKDIR /app | ||
|
||
# Copy the current directory contents into the container at /app | ||
ADD . /app | ||
|
||
# Update and install system libraries | ||
RUN apt-get update && apt-get install -y \ | ||
ghostscript \ | ||
--no-install-recommends && \ | ||
rm -rf /var/lib/apt/lists/* | ||
|
||
|
||
# Install Poetry | ||
RUN pip install poetry | ||
|
||
# Install project dependencies | ||
RUN poetry config virtualenvs.create false \ | ||
&& poetry install --no-interaction --no-ansi | ||
|
||
# Make port 80 available to the world outside this container | ||
EXPOSE 8000 | ||
|
||
# Run app.py when the container launches | ||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
ps_content_template = '''% Define an ICC profile : | ||
/ICCProfile (/usr/share/ghostscript/{gs_version}/iccprofiles/default_rgb.icc) def | ||
% Define an OutputConditionIdentifier : | ||
/OutputConditionIdentifier (default_rgb) def | ||
% Create a PDF stream object to hold the XML invoice : | ||
[ /_objdef {{InvoiceStream}} /type /stream /OBJ pdfmark | ||
% Add the required entries to that stream dictionary : | ||
[ {{InvoiceStream}} << /Type /EmbeddedFile /Subtype (text/xml) cvn /Params << /ModDate (D:20130121081433+01'00') >> >> /PUT pdfmark | ||
% Read the XML invoice data from the file and store it in the PDF stream : | ||
[ {{InvoiceStream}} ({xml_path}) (r) file /PUT pdfmark | ||
% Close the PDF stream : | ||
[ {{InvoiceStream}} /CLOSE pdfmark | ||
% Create a PDF file specification dictionary to hold the file info : | ||
[ /_objdef {{InvoiceFile}} /type /dict /OBJ pdfmark | ||
% Add the required entries to that file specification dictionary : | ||
[ {{InvoiceFile}} << | ||
/Type /Filespec | ||
/F ({xml_filename}) | ||
/EF << /F {{InvoiceStream}} >> | ||
>> /PUT pdfmark | ||
% Associate the file specification with the PDF document : | ||
[/Root << | ||
/Names << /EmbeddedFiles << /Names [ ({xml_filename}) {{InvoiceFile}} ] >> >> | ||
>> /pdfmark | ||
% Create an annotation to display the attachment in the PDF viewer: | ||
[{{Catalog}} << | ||
/Names << /EmbeddedFiles << /Names [ ({xml_filename}) {{InvoiceFile}} ] >> >> | ||
/ViewerPreferences << | ||
/DisplayDocTitle true | ||
>> | ||
/PageLayout /SinglePage | ||
/PageMode /UseAttachments | ||
>> /PUT pdfmark''' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
from fastapi import FastAPI, UploadFile, File | ||
from fastapi.responses import FileResponse | ||
import tempfile | ||
import secrets | ||
import subprocess | ||
import os | ||
|
||
from utils import save_temp_file, create_post_script_file | ||
|
||
app = FastAPI() | ||
|
||
|
||
|
||
@app.post("/upload") | ||
async def upload_files(pdf_file: UploadFile = File(...), xml_file: UploadFile = File(...)): | ||
session_id = secrets.token_hex(8) | ||
pdf_file_path = await save_temp_file(await pdf_file.read(), '.pdf', session_id) | ||
xml_file_path = await save_temp_file(await xml_file.read(), '.xml', session_id) | ||
ps_file = await create_post_script_file(xml_file_path, session_id) | ||
result_file_path = os.path.join(tempfile.mkdtemp(), session_id) + '.pdf' | ||
command = f"gs -sDEVICE=pdfwrite -dNOSAFER -dPDFA=3 -sColorConversionStrategy=RGB -dPDFACompatibilityPolicy=2 -o {result_file_path} {pdf_file_path} {ps_file} -dDEBUG" | ||
subprocess.run(command, shell=True) | ||
|
||
print(result_file_path) | ||
return FileResponse(result_file_path, filename="result.pdf") |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
[tool.poetry] | ||
name = "pypdfa3-zugferd" | ||
version = "0.1.0" | ||
description = "Pythonfastapi to convert pdf to zugferd format" | ||
authors = ["Mayur Rawte <[email protected]>"] | ||
license = "MIT" | ||
|
||
[tool.poetry.dependencies] | ||
python = "^3.10" | ||
fastapi = "^0.98.0" | ||
uvicorn = "^0.22.0" | ||
python-multipart = "^0.0.6" | ||
|
||
[tool.poetry.dev-dependencies] | ||
|
||
[build-system] | ||
requires = ["poetry-core>=1.0.0"] | ||
build-backend = "poetry.core.masonry.api" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import tempfile | ||
from constant import ps_content_template | ||
from fastapi import UploadFile | ||
import subprocess | ||
|
||
|
||
xml_path = '/home/thepsygeek/zugferd/sample.xml' | ||
xml_filename = 'sample.xml' | ||
|
||
|
||
|
||
|
||
async def save_temp_file(upload_file: UploadFile, file_extension: str) -> str: | ||
with tempfile.NamedTemporaryFile(suffix=file_extension, delete=False) as temp_file: | ||
# Save the file | ||
temp_file.write(await upload_file.read()) | ||
temp_file_path = temp_file.name | ||
return temp_file_path | ||
|
||
|
||
import os | ||
|
||
async def save_temp_file(file_data: bytes, file_extension: str, folder: str) -> str: | ||
os.makedirs(folder, exist_ok=True) | ||
with tempfile.NamedTemporaryFile(suffix=file_extension, delete=False, dir=folder) as temp_file: | ||
temp_file.write(file_data) | ||
temp_file_path = temp_file.name | ||
return temp_file_path | ||
|
||
|
||
async def create_post_script_file(xml_file_path: str, session_id): | ||
content = ps_content_template.format(xml_path=xml_file_path, xml_filename='invoice.xml', gs_version=get_gs_version()) | ||
return await save_temp_file(content.encode('utf-8'), '.ps', session_id) | ||
|
||
def get_gs_version(): | ||
try: | ||
gs_version = subprocess.check_output(["gs", "--version"]).strip().decode() | ||
return gs_version | ||
except subprocess.CalledProcessError: | ||
return "Ghostscript not found" |