Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
mayurrawte committed Jun 30, 2023
0 parents commit 18bbc0f
Show file tree
Hide file tree
Showing 6 changed files with 384 additions and 0 deletions.
28 changes: 28 additions & 0 deletions Dockerfile
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"]
44 changes: 44 additions & 0 deletions constant.py
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'''
25 changes: 25 additions & 0 deletions main.py
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")
229 changes: 229 additions & 0 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions pyproject.toml
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"
40 changes: 40 additions & 0 deletions utils.py
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"

0 comments on commit 18bbc0f

Please sign in to comment.