Module src.generatorEdited
Expand source code
import datetime
from pathlib import Path
import os
from fitz import fitz
import globals
from distutils.dir_util import copy_tree
import shutil
from bson import json_util, ObjectId
import io
import pymongo
import re
from flask_restful import Resource
from webargs.flaskparser import use_kwargs
from webargs import fields
from src import auth
from marshmallow import Schema
from fontTools import ttLib
from contextlib import redirect_stderr
from zipfile import ZipFile, ZIP_STORED
fileOutputFolder = ""
fileRedactedFolder = ""
fileOutputImageFolder = ""
fileOutputFontFolder = ""
fileOutputStylesFolder = ""
fileOutputTextFolder = ""
SAMPLEXHTML = ""
SAMPLECSS = ""
fileOutputOPFFile = ""
fileOutputTocNcxFile = ""
fileOutputTocFile = ""
SAMPLEXHTMLTEXT = ""
SAMPLECSSTEXT = ""
fileOutputBodtFile = ""
toc = ""
# COPY TEMPLATE TO STATIC FOLDER
def templateCopy(fileID):
global fileOutputFolder
global fileRedactedFolder
global fileOutputImageFolder
global fileOutputFontFolder
global fileOutputStylesFolder
global fileOutputTextFolder
global SAMPLEXHTML
global SAMPLECSS
global fileOutputOPFFile
global fileOutputTocNcxFile
global fileOutputTocFile
global SAMPLEXHTMLTEXT
global SAMPLECSSTEXT
global fileOutputBodtFile
fileOutputFolder = os.path.join(globals.FILES_FOLDER, fileID, "output")
fileRedactedFolder = os.path.join(globals.FILES_FOLDER, fileID, "redacted")
fileOutputImageFolder = os.path.join(
globals.FILES_FOLDER, fileID, "output", "OEBPS", "images")
fileOutputFontFolder = os.path.join(
globals.FILES_FOLDER, fileID, "output", "OEBPS", "Fonts")
fileOutputStylesFolder = os.path.join(
globals.FILES_FOLDER, fileID, "output", "OEBPS", "css")
fileOutputTextFolder = os.path.join(
globals.FILES_FOLDER, fileID, "output", "OEBPS")
SAMPLEXHTML = os.path.join(fileOutputTextFolder, "SAMPLE.xhtml")
SAMPLECSS = os.path.join(fileOutputStylesFolder, "style.css")
fileOutputOPFFile = os.path.join(
globals.FILES_FOLDER, fileID, "output", "OEBPS", "package.opf")
fileOutputTocNcxFile = os.path.join(
globals.FILES_FOLDER, fileID, "output", "OEBPS", "toc.ncx")
fileOutputTocFile = os.path.join(
globals.FILES_FOLDER, fileID, "output", "OEBPS", "TOC.xhtml")
fileOutputBodtFile = os.path.join(fileOutputStylesFolder, "body.css")
Path(fileOutputFolder).mkdir(parents=True, exist_ok=True)
Path(fileOutputStylesFolder).mkdir(parents=True, exist_ok=True)
Path(fileOutputTextFolder).mkdir(parents=True, exist_ok=True)
copy_tree("template2", fileOutputFolder)
# COPY REDACTED IMAGES
copy_tree(os.path.join(globals.FILES_FOLDER,
fileID, "images"), fileOutputImageFolder)
auth_args = {"Authorization": fields.Str(required=True)}
def valueReplacer(PageNum, pageCSS, pageImg, text, Properties, width, height, TotalPages):
"""
It takes a string, finds all instances of {{KEY}} and replaces them with the value of the key in the
Properties dictionary
:param PageNum: The page number of the current page
:param pageCSS: The path to the CSS file for the page
:param pageImg: The path to the image file for the page
:param text: the text to be replaced
:param Properties: A list of dictionaries, each dictionary has a key and a value
:param width: The width of the image
:param height: The height of the page in pixels
:param TotalPages: Total number of pages in the book
:return: The text of the template with the values replaced.
"""
matches = re.finditer(r"{{(\w+)}}", text, re.MULTILINE)
for matchNum, match in enumerate(matches, start=1):
for groups in match.groups():
if groups == "WIDTH":
value = width
elif groups == "HEIGHT":
value = height
elif groups == "FILE_REL_CSS":
value = re.sub(r"(.+)Styles", "./Styles",
pageCSS.replace("\\", "/"), 0, re.MULTILINE)
elif groups == "TITLEPAGE_REL_PATH":
titlepageList = list(
filter(lambda d: d['key'] in "TITLEPAGE_NO", Properties))
titlepageNum = titlepageList[0]["value"] if len(
titlepageList) > 0 else ""
value = f"page_{str(int(titlepageNum) - 1).zfill(3)}.xhtml"
elif groups == "CHAPTER1_REL_PATH":
chapter1List = list(
filter(lambda d: d['key'] in "CHAPTER1_NO", Properties))
chapter1Num = chapter1List[0]["value"] if len(
chapter1List) > 0 else ""
value = f"page_{str(int(chapter1Num) - 1).zfill(3)}.xhtml"
elif groups == "COPYRIGHT_REL_PATH":
copyrightList = list(
filter(lambda d: d['key'] in "COPYRIGHT_NO", Properties))
copyrightNum = copyrightList[0]["value"] if len(
copyrightList) > 0 else ""
value = f"page_{str(int(copyrightNum) - 1).zfill(3)}.xhtml"
elif groups == "COVER_REL_PATH":
value = "cover.xhtml"
elif groups == "TOTALPAGES":
value = TotalPages
elif groups == "RESOLUTION":
value = str(width) + "x" + str(height)
elif groups == "IMAGE_URL":
value = re.sub(r"(.+)Images", "./Images",
pageImg.replace("\\", "/"), 0, re.MULTILINE)
elif groups == "PAGENUM":
value = int(PageNum) - 1
if value == 0:
value = "cover"
else:
value = str(value).zfill(3)
elif groups == "BODY_REL_CSS":
value = "./Styles/body.css"
else:
valueList = list(
filter(lambda d: d['key'] in groups, Properties))
value = valueList[-1]["value"] if len(valueList) > 0 else ""
if value != "":
text = text.replace("{{" + str(groups) + "}}", str(value))
return text
def font_style(font_path):
"""
It takes a font file path as an argument, opens the font file, reads the name table, and returns a
dictionary of the name table entries
:param font_path: The path to the font file
:return: A dictionary of font details.
"""
font = ttLib.TTFont(font_path, ignoreDecompileErrors=True)
with redirect_stderr(None):
names = font['name'].names
details = {}
for x in names:
if x.langID == 0 or x.langID == 1033:
try:
details[x.nameID] = str.lower(x.toUnicode())
except UnicodeDecodeError:
details[x.nameID] = str.lower(x.string.decode(errors='ignore'))
return details
def suportingFilesCreation(Properties, width, height, Fonts, TotalPages):
"""
It creates the supporting files for the epub.
:param Properties: A dictionary of the book's metadata
:param width: the width of the page in pixels
:param height: the height of the page
:param Fonts: A list of dictionaries, each dictionary containing the following keys:
:param TotalPages: The number of pages in the PDF
"""
global fileOutputFolder
global fileRedactedFolder
global fileOutputImageFolder
global fileOutputFontFolder
global fileOutputStylesFolder
global fileOutputTextFolder
global SAMPLEXHTML
global SAMPLECSS
global fileOutputOPFFile
global fileOutputTocNcxFile
global fileOutputTocFile
global SAMPLEXHTMLTEXT
global SAMPLECSSTEXT
global fileOutputBodtFile
bookmarkChapterStart = - 1
while str(toc[bookmarkChapterStart + 1][1]).lower() not in ["contents", "table of content"]:
bookmarkChapterStart += 1
if TotalPages - 2 >= bookmarkChapterStart:
bookmarkChapterStart += 1
else:
bookmarkChapterStart = -1
contentNavs = []
navOrder = 3
sampleNav = '<navPoint id="navPoint-{{navOrder}}" playOrder="{{navOrder}}"><navLabel><text>{{topic}}</text></navLabel><content src="{{pagexhtml}}"/></navPoint>'
tocs = []
sampleToc = '<li id="NavPoint-{{navOrder}}"><a href="{{pagexhtml}}">{{topic}}</a></li>'
backmatter = '<li><a epub:type="backmatter" href="{{pagexhtml}}">{{topic}}</a></li>'
for content in range(bookmarkChapterStart, len(toc)):
if toc[content][1].lower() in ["contents", "table of content", "toc"]:
navOrder += 1
contentNavs.append(sampleNav.replace("{{navOrder}}", str(navOrder))
.replace("{{topic}}", "Table of Contents")
.replace("{{pagexhtml}}", f"page_{str(toc[content][2] - 1).zfill(3)}.xhtml"))
tocs.append(sampleToc.replace("{{navOrder}}", str(navOrder).zfill(2))
.replace("{{topic}}", "Table of Contents")
.replace("{{pagexhtml}}", f"page_{str(toc[content][2] - 1).zfill(3)}.xhtml"))
elif toc[content][1].lower() in ["back cover"]:
continue
else:
navOrder += 1
contentNavs.append(sampleNav.replace("{{navOrder}}", str(navOrder))
.replace("{{topic}}", toc[content][1])
.replace("{{pagexhtml}}", f"page_{str(toc[content][2] - 1).zfill(3)}.xhtml"))
tocs.append(sampleToc.replace("{{navOrder}}", str(navOrder).zfill(2))
.replace("{{topic}}", toc[content][1])
.replace("{{pagexhtml}}", f"page_{str(toc[content][2] - 1).zfill(3)}.xhtml"))
if content == len(toc) - 2:
backmatter = backmatter.replace("{{topic}}", toc[content][1]) \
.replace("{{pagexhtml}}", f"page_{str(toc[content][2] - 1).zfill(3)}.xhtml")
if "{{" in backmatter:
backmatter = ""
# TOC.ncx
with io.open(fileOutputTocNcxFile, 'r', encoding='utf8') as f:
tocNcxText = f.read()
tocNcxText = valueReplacer(
"", "", "", tocNcxText, Properties, width, height, TotalPages)
with io.open(fileOutputTocNcxFile, 'w', encoding='utf8', newline='\n') as f:
f.write(tocNcxText.replace(
"./Text", "Text").replace("<contentNavs/>", "\n".join(contentNavs)))
# TOC.xHTML
with io.open(fileOutputTocFile, 'r', encoding='utf8') as f:
tocXhtmlText = f.read()
tocXhtmlText = valueReplacer(
"", "", "", tocXhtmlText, Properties, width, height, TotalPages)
with io.open(fileOutputTocFile, 'w', encoding='utf8', newline='\n') as f:
f.write(tocXhtmlText.replace("./Text", "Text").replace("<toc/>",
"\n".join(tocs)).replace("<backmatter/>", backmatter))
# PACKAGE.OPF
with io.open(fileOutputOPFFile, 'r', encoding='utf8') as f:
opfFileText = f.read()
imgsTemplate = '<item id="{{IMG_ID}}" href="{{IMG_REL_PATH}}" media-type="image/jpeg"/>'
bodysTemplate = '<item id="{{ID}}" href="{{TEXT_REL_PATH}}" media-type="application/xhtml+xml"/>'
fontsTemplate = '<item id="font{{FONT_ID}}" href="{{FONT_REL_PATH}}" media-type="application/vnd.ms-opentype"/>'
bodyRefsTemplate = '<itemref idref="{{ID}}" linear="yes"/>'
csssTemplate = '<item href="{{CSS_REL_PATH}}" id="{{CSS_FILENAME}}" media-type="text/css" />'
csss = []
bodys = []
imgs = []
bodyRefs = []
fonts = []
for i in range(1, TotalPages + 1):
coverPage = "cover" if i == 1 else str(i).zfill(3)
No_of_the_pages = str(i).zfill(3)
if coverPage == "cover":
imgs.append(imgsTemplate
.replace("{{IMG_ID}}", "cover-image")
.replace("{{IMG_REL_PATH}}", f"images/{coverPage}.jpg"))
bodys.append(bodysTemplate
.replace("{{ID}}", f"cover")
.replace("{{TEXT_REL_PATH}}", f"{coverPage}.xhtml"))
bodyRefs.append(bodyRefsTemplate
.replace("{{ID}}", f"{coverPage}"))
else:
imgs.append(imgsTemplate
.replace("{{IMG_ID}}", f"img{str(int(coverPage) - 1).zfill(3)}")
.replace("{{IMG_REL_PATH}}",
f"images/page_{str(int(coverPage) - 1).zfill(3)}.jpg"))
bodys.append(bodysTemplate
.replace("{{ID}}", f"page{str(int(coverPage) - 1).zfill(3)}")
.replace("{{TEXT_REL_PATH}}",
f"page_{str(int(coverPage) - 1).zfill(3)}.xhtml"))
bodyRefs.append(bodyRefsTemplate
.replace("{{ID}}", f"page{str(int(coverPage) - 1).zfill(3)}"))
for i in range(0, len(Fonts)):
fontName = str(Fonts[i]["fontPath"]).replace("\\", "/").split("/")[-1]
fontOutputPath = os.path.join(fileOutputFontFolder, fontName)
fontPath = os.path.join(globals.FONT_FOLDER, fontName)
# shutil.copy2(fontPath, fontOutputPath)
Fonts[i]["FONT_REL_PATH"] = fontOutputPath.replace(
"\\", "/").split("output/OEBPS/")[-1]
fonts.append(fontsTemplate
.replace("{{FONT_ID}}", str(i + 1))
.replace("{{FONT_REL_PATH}}", Fonts[i]["FONT_REL_PATH"]))
opfFileText = opfFileText \
.replace("<imgs/>", "\n".join(imgs)) \
.replace("<bodys/>", "\n".join(bodys)) \
.replace("<bodyrefs/>", "\n".join(bodyRefs)) \
.replace("<fonts/>", "")\
.replace("{{NOW}}", datetime.datetime.now(datetime.timezone.utc).isoformat().split(".")[0] + "Z")
opfFileText = valueReplacer(
"", "", "", opfFileText, Properties, width, height, TotalPages)
with io.open(fileOutputOPFFile, 'w', encoding='utf8', newline='\n') as f:
f.write(opfFileText.replace("./Text", "Text"))
def xhtmlAndCssCreation(Properties, Fonts, TotalPages, Pages, fileID):
"""
It takes the data from the database and creates the xhtml and css files
:param Properties: A dictionary of properties that are used to replace values in the XHTML and CSS
files
:param Fonts: A list of font names used in the document
:param TotalPages: Total number of pages in the PDF
:param Pages: A list of dictionaries, each dictionary representing a page
:param fileID: The ID of the file being processed
"""
global fileOutputFolder
global fileRedactedFolder
global fileOutputImageFolder
global fileOutputFontFolder
global fileOutputStylesFolder
global fileOutputTextFolder
global SAMPLEXHTML
global SAMPLECSS
global fileOutputOPFFile
global fileOutputTocNcxFile
global fileOutputTocFile
global SAMPLEXHTMLTEXT
global SAMPLECSSTEXT
global fileOutputBodtFile
# GENERATING TEXT AND CSS FILES
with io.open(SAMPLEXHTML, 'r', encoding='utf8') as f:
SAMPLEXHTMLTEXT = f.read()
with io.open(SAMPLECSS, 'r', encoding='utf8') as f:
SAMPLEXCSSTEXT = f.read()
width = ""
height = ""
for page in Pages:
height = page["Image"]["Height"]
width = page["Image"]["Width"]
pageNum = str(page['PageNum'] - 1).zfill(3)
boxes = list(globals.boxesDB.find(
{
"pageId": page["PageID"]
}
).sort([("boxIndex", pymongo.ASCENDING)])
)
pageImgTmp = os.path.join(
fileOutputImageFolder, f"Page{page['PageNum']}.jpeg")
pageImg = ""
pageImgNum = ""
if page["PageNum"] == 1:
coverImg = pageImgTmp.replace(
f"Page{page['PageNum']}.jpeg", "cover.jpg")
pageImgNum = "cover.jpg"
if not os.path.exists(coverImg):
os.rename(pageImgTmp, coverImg)
if os.path.exists(pageImgTmp):
os.remove(pageImgTmp)
pageImg = coverImg
pageXhtml = os.path.join(fileOutputTextFolder, "cover.xhtml")
pageCSS = os.path.join(fileOutputStylesFolder, "style.css")
pageXhtmlText = SAMPLEXHTMLTEXT
pageCSSText = SAMPLEXCSSTEXT
# coverXhtmlText = coverXhtmlText.replace()
else:
pageImg = pageImgTmp.replace(
f"Page{page['PageNum']}.jpeg", f"page_{pageNum}.jpg")
if not os.path.exists(pageImg):
os.rename(pageImgTmp, pageImg)
if os.path.exists(pageImgTmp):
os.remove(pageImgTmp)
pageXhtml = os.path.join(
fileOutputTextFolder, f"page_{pageNum}.xhtml")
pageImgNum = f"page_{pageNum}.jpg"
pageCSS = os.path.join(fileOutputStylesFolder, f"style.css")
pageXhtmlText = SAMPLEXHTMLTEXT
pageCSSText = SAMPLEXCSSTEXT
bodyData = []
cssData = []
cssCounter = 0
lineCssCounter = 0
bookmarkChapterStart = - 1
while str(toc[bookmarkChapterStart+1][1]).lower() not in ["contents", "table of content"]:
bookmarkChapterStart += 1
if len(Pages) - 2 >= bookmarkChapterStart:
bookmarkChapterStart += 1
else:
bookmarkChapterStart = -1
bodyData.append(
f'<p style="background-repeat: no-repeat;"><img src="images/{pageImgNum}" alt=""/></p>')
for box in boxes:
pStyle = f'position: absolute;\n' \
f'top: {box["coordinates"][1]}px;\n' \
f'left: {box["coordinates"][0]}px;\n' \
f'width: {width - box["coordinates"][0]}px;\n' \
f'height: {box["coordinates"][3]}px;\n'
lineCssCounter += 1
lineCssSelector = "line" + str(lineCssCounter)
cssData.append(f".{lineCssSelector}" + "{\n" + pStyle + "\n}")
innerHtml = []
for line in box["linesData"]:
if "%" not in line["lineHeight"]:
line["lineHeight"] += "%"
innerCssData = f'position: {line["position"]};\n' \
f'top: {line["top"]};\n'\
f'font-family: sans-serif;\n'\
f'left: {line["left"]};\n'\
f'font-size: {line["fontSize"]};\n'\
f'-ms-transform: rotate({line["textRotate"]}deg);\n'\
f'-webkit-transform: rotate({line["textRotate"]}deg);\n'\
f'transform: rotate({line["textRotate"]}deg);\n'\
f'font-weight: normal;\n'\
f'line-height: {line["lineHeight"].replace("normal%","normal")};\n'\
f'color: {line["color"]};\n'\
f'word-spacing: {line["wordSpacing"]};\n'\
f'letter-spacing: {line["letterSpacing"]};\n'\
f'font-style: normal;\n'\
f'text-decoration: {line["textDecoration"]};\n'\
f'white-space: pre;\n'
cssCounter += 1
innerCssSelector = "span" + str(cssCounter)
data = line["text"].replace("\n", "<br>\n")
line["text"] = re.sub(
r"\s{2,}", " ", line["text"], 0, re.MULTILINE)
if line["text"].startswith(" "):
lastIndex = len(innerHtml) - 1
print(lastIndex)
if len(innerHtml) > 0:
lastElement = innerHtml[lastIndex]
lastElement = lastElement.replace(
"</span>", " </span>")
innerHtml[lastIndex] = lastElement
innerHtml.append(
f'<span class="{innerCssSelector}">{data.lstrip()}</span>')
if line["br"] is True and "<br>" not in data:
innerHtml.append("<br>")
cssData.append(f".{innerCssSelector}" +
"{\n" + innerCssData + "\n}")
bodyData.append(
f'<{box["tagName"]} class="{lineCssSelector}"><a href="page_{str(toc[bookmarkChapterStart+lineCssCounter][2] - 1).zfill(3)}.xhtml">{"".join(innerHtml)}</a></{box["tagName"]}>')
pageXhtmlText = pageXhtmlText.replace(
"<bodyData/>", "\n".join(bodyData)).replace("<br>", "")
cssDataFinal = "\n".join(cssData)
# cssDataFinal = re.sub(r"font-family:(.*?);", "font-family:\"\\1\";", cssDataFinal, 0, re.MULTILINE)
cssDataFinal = cssDataFinal.replace('" sans-serif"', "sans-serif")
# pageCSSText = pageCSSText.replace("{{BODY_CSS}}", cssDataFinal)
if len(boxes) == 0:
pageXhtmlText = pageXhtmlText.replace(
"<bodyData/>", "\n".join(bodyData)).replace("<br>", "")
cssDataFinal = cssDataFinal.replace(
"{{BODY_CSS}}", "").replace("{{FONTS_CSS}}", "")
# REPLACING PROPERTIES IN CSS AND XHTML FILES
if page['PageNum'] == 1:
pageNum = "cover"
pageXhtmlText = valueReplacer(page['PageNum'], pageCSS, pageImg, pageXhtmlText, Properties, width, height,
TotalPages).\
replace('<link href="./Styles/body.css" type="text/css" rel="stylesheet" />', "")\
.replace(f"static/files/{fileID}/output/OEBPS", ".").replace(' class="page"', "")
cssDataFinal = valueReplacer(page['PageNum'], pageCSS, pageImg, cssDataFinal, Properties, width, height,
TotalPages)
with io.open(pageXhtml, 'w', encoding='utf8', newline='\n') as f:
f.write(re.sub(r"^\s+", "", pageXhtmlText, 0, re.MULTILINE))
with io.open(pageCSS, 'a', encoding='utf8', newline='\n') as f:
f.write(cssDataFinal)
finalCss = ""
fileCss = os.path.join(fileOutputStylesFolder, "style.css")
with io.open(fileCss, 'r', encoding='utf8', newline='\n') as f:
finalCss = f.read()
with io.open(fileCss, 'w', encoding='utf8', newline='\n') as f:
f.write(finalCss.replace("{{WIDTH}}", str(
width)).replace("{{HEIGHT}}", str(height)))
os.remove(SAMPLEXHTML)
# os.remove(SAMPLECSS)
# It takes a fileID, verifies the user's token, and then creates an epub file from a pdf file.
class GenerateFile2(Resource):
@use_kwargs(auth_args, location="headers")
@use_kwargs({"fileID": fields.Str(required=True)}, location="query")
def post(self, Authorization, fileID):
"""
It takes a PDF file, converts it to an EPUB file, and returns the path to the EPUB file.
:param Authorization: The token that is generated when the user logs in
:param fileID: The ID of the file in the database
:return: The path to the epub file.
"""
if auth.verify(str(Authorization).split(" ")[1]):
templateCopy(fileID)
result = globals.filesDB.find_one(
{
"_id": ObjectId(fileID)
}
)
TotalPages = result["TotalPages"]
Pages = result["Pages"]
Fonts = result["Fonts"]
Properties = result["Properties"]
width = result["Pages"][0]["Image"]["Width"]
height = result["Pages"][0]["Image"]["Height"]
doc = fitz.open(os.path.join(
globals.FILES_FOLDER, fileID, f"{fileID}.pdf"))
global toc
toc = doc.get_toc()
suportingFilesCreation(
Properties, width, height, Fonts, TotalPages)
xhtmlAndCssCreation(Properties, Fonts, TotalPages, Pages, fileID)
isbn = list(filter(lambda d: d['key'] in "ISBN", Properties))
if len(isbn) > 0:
isbn = isbn[0]["value"]
else:
isbn = "undefined"
finalFile = os.path.join(fileOutputFolder, f'{isbn}.epub')
if os.path.exists(finalFile):
os.remove(finalFile)
zipobj = ZipFile(finalFile, 'w', ZIP_STORED)
rootlen = len(fileOutputFolder) + 1
for base, dirs, files in os.walk(fileOutputFolder):
for file in files:
if ".epub" not in file:
fn = os.path.join(base, file)
zipobj.write(fn, fn[rootlen:])
return {"msg": finalFile.replace("\\", "/")}, 200
else:
return {"msg": "Unauthorized! Access Denied"}, 401
Functions
def font_style(font_path)-
It takes a font file path as an argument, opens the font file, reads the name table, and returns a dictionary of the name table entries
:param font_path: The path to the font file :return: A dictionary of font details.
Expand source code
def font_style(font_path): """ It takes a font file path as an argument, opens the font file, reads the name table, and returns a dictionary of the name table entries :param font_path: The path to the font file :return: A dictionary of font details. """ font = ttLib.TTFont(font_path, ignoreDecompileErrors=True) with redirect_stderr(None): names = font['name'].names details = {} for x in names: if x.langID == 0 or x.langID == 1033: try: details[x.nameID] = str.lower(x.toUnicode()) except UnicodeDecodeError: details[x.nameID] = str.lower(x.string.decode(errors='ignore')) return details def suportingFilesCreation(Properties, width, height, Fonts, TotalPages)-
It creates the supporting files for the epub.
:param Properties: A dictionary of the book's metadata :param width: the width of the page in pixels :param height: the height of the page :param Fonts: A list of dictionaries, each dictionary containing the following keys: :param TotalPages: The number of pages in the PDF
Expand source code
def suportingFilesCreation(Properties, width, height, Fonts, TotalPages): """ It creates the supporting files for the epub. :param Properties: A dictionary of the book's metadata :param width: the width of the page in pixels :param height: the height of the page :param Fonts: A list of dictionaries, each dictionary containing the following keys: :param TotalPages: The number of pages in the PDF """ global fileOutputFolder global fileRedactedFolder global fileOutputImageFolder global fileOutputFontFolder global fileOutputStylesFolder global fileOutputTextFolder global SAMPLEXHTML global SAMPLECSS global fileOutputOPFFile global fileOutputTocNcxFile global fileOutputTocFile global SAMPLEXHTMLTEXT global SAMPLECSSTEXT global fileOutputBodtFile bookmarkChapterStart = - 1 while str(toc[bookmarkChapterStart + 1][1]).lower() not in ["contents", "table of content"]: bookmarkChapterStart += 1 if TotalPages - 2 >= bookmarkChapterStart: bookmarkChapterStart += 1 else: bookmarkChapterStart = -1 contentNavs = [] navOrder = 3 sampleNav = '<navPoint id="navPoint-{{navOrder}}" playOrder="{{navOrder}}"><navLabel><text>{{topic}}</text></navLabel><content src="{{pagexhtml}}"/></navPoint>' tocs = [] sampleToc = '<li id="NavPoint-{{navOrder}}"><a href="{{pagexhtml}}">{{topic}}</a></li>' backmatter = '<li><a epub:type="backmatter" href="{{pagexhtml}}">{{topic}}</a></li>' for content in range(bookmarkChapterStart, len(toc)): if toc[content][1].lower() in ["contents", "table of content", "toc"]: navOrder += 1 contentNavs.append(sampleNav.replace("{{navOrder}}", str(navOrder)) .replace("{{topic}}", "Table of Contents") .replace("{{pagexhtml}}", f"page_{str(toc[content][2] - 1).zfill(3)}.xhtml")) tocs.append(sampleToc.replace("{{navOrder}}", str(navOrder).zfill(2)) .replace("{{topic}}", "Table of Contents") .replace("{{pagexhtml}}", f"page_{str(toc[content][2] - 1).zfill(3)}.xhtml")) elif toc[content][1].lower() in ["back cover"]: continue else: navOrder += 1 contentNavs.append(sampleNav.replace("{{navOrder}}", str(navOrder)) .replace("{{topic}}", toc[content][1]) .replace("{{pagexhtml}}", f"page_{str(toc[content][2] - 1).zfill(3)}.xhtml")) tocs.append(sampleToc.replace("{{navOrder}}", str(navOrder).zfill(2)) .replace("{{topic}}", toc[content][1]) .replace("{{pagexhtml}}", f"page_{str(toc[content][2] - 1).zfill(3)}.xhtml")) if content == len(toc) - 2: backmatter = backmatter.replace("{{topic}}", toc[content][1]) \ .replace("{{pagexhtml}}", f"page_{str(toc[content][2] - 1).zfill(3)}.xhtml") if "{{" in backmatter: backmatter = "" # TOC.ncx with io.open(fileOutputTocNcxFile, 'r', encoding='utf8') as f: tocNcxText = f.read() tocNcxText = valueReplacer( "", "", "", tocNcxText, Properties, width, height, TotalPages) with io.open(fileOutputTocNcxFile, 'w', encoding='utf8', newline='\n') as f: f.write(tocNcxText.replace( "./Text", "Text").replace("<contentNavs/>", "\n".join(contentNavs))) # TOC.xHTML with io.open(fileOutputTocFile, 'r', encoding='utf8') as f: tocXhtmlText = f.read() tocXhtmlText = valueReplacer( "", "", "", tocXhtmlText, Properties, width, height, TotalPages) with io.open(fileOutputTocFile, 'w', encoding='utf8', newline='\n') as f: f.write(tocXhtmlText.replace("./Text", "Text").replace("<toc/>", "\n".join(tocs)).replace("<backmatter/>", backmatter)) # PACKAGE.OPF with io.open(fileOutputOPFFile, 'r', encoding='utf8') as f: opfFileText = f.read() imgsTemplate = '<item id="{{IMG_ID}}" href="{{IMG_REL_PATH}}" media-type="image/jpeg"/>' bodysTemplate = '<item id="{{ID}}" href="{{TEXT_REL_PATH}}" media-type="application/xhtml+xml"/>' fontsTemplate = '<item id="font{{FONT_ID}}" href="{{FONT_REL_PATH}}" media-type="application/vnd.ms-opentype"/>' bodyRefsTemplate = '<itemref idref="{{ID}}" linear="yes"/>' csssTemplate = '<item href="{{CSS_REL_PATH}}" id="{{CSS_FILENAME}}" media-type="text/css" />' csss = [] bodys = [] imgs = [] bodyRefs = [] fonts = [] for i in range(1, TotalPages + 1): coverPage = "cover" if i == 1 else str(i).zfill(3) No_of_the_pages = str(i).zfill(3) if coverPage == "cover": imgs.append(imgsTemplate .replace("{{IMG_ID}}", "cover-image") .replace("{{IMG_REL_PATH}}", f"images/{coverPage}.jpg")) bodys.append(bodysTemplate .replace("{{ID}}", f"cover") .replace("{{TEXT_REL_PATH}}", f"{coverPage}.xhtml")) bodyRefs.append(bodyRefsTemplate .replace("{{ID}}", f"{coverPage}")) else: imgs.append(imgsTemplate .replace("{{IMG_ID}}", f"img{str(int(coverPage) - 1).zfill(3)}") .replace("{{IMG_REL_PATH}}", f"images/page_{str(int(coverPage) - 1).zfill(3)}.jpg")) bodys.append(bodysTemplate .replace("{{ID}}", f"page{str(int(coverPage) - 1).zfill(3)}") .replace("{{TEXT_REL_PATH}}", f"page_{str(int(coverPage) - 1).zfill(3)}.xhtml")) bodyRefs.append(bodyRefsTemplate .replace("{{ID}}", f"page{str(int(coverPage) - 1).zfill(3)}")) for i in range(0, len(Fonts)): fontName = str(Fonts[i]["fontPath"]).replace("\\", "/").split("/")[-1] fontOutputPath = os.path.join(fileOutputFontFolder, fontName) fontPath = os.path.join(globals.FONT_FOLDER, fontName) # shutil.copy2(fontPath, fontOutputPath) Fonts[i]["FONT_REL_PATH"] = fontOutputPath.replace( "\\", "/").split("output/OEBPS/")[-1] fonts.append(fontsTemplate .replace("{{FONT_ID}}", str(i + 1)) .replace("{{FONT_REL_PATH}}", Fonts[i]["FONT_REL_PATH"])) opfFileText = opfFileText \ .replace("<imgs/>", "\n".join(imgs)) \ .replace("<bodys/>", "\n".join(bodys)) \ .replace("<bodyrefs/>", "\n".join(bodyRefs)) \ .replace("<fonts/>", "")\ .replace("{{NOW}}", datetime.datetime.now(datetime.timezone.utc).isoformat().split(".")[0] + "Z") opfFileText = valueReplacer( "", "", "", opfFileText, Properties, width, height, TotalPages) with io.open(fileOutputOPFFile, 'w', encoding='utf8', newline='\n') as f: f.write(opfFileText.replace("./Text", "Text")) def templateCopy(fileID)-
Expand source code
def templateCopy(fileID): global fileOutputFolder global fileRedactedFolder global fileOutputImageFolder global fileOutputFontFolder global fileOutputStylesFolder global fileOutputTextFolder global SAMPLEXHTML global SAMPLECSS global fileOutputOPFFile global fileOutputTocNcxFile global fileOutputTocFile global SAMPLEXHTMLTEXT global SAMPLECSSTEXT global fileOutputBodtFile fileOutputFolder = os.path.join(globals.FILES_FOLDER, fileID, "output") fileRedactedFolder = os.path.join(globals.FILES_FOLDER, fileID, "redacted") fileOutputImageFolder = os.path.join( globals.FILES_FOLDER, fileID, "output", "OEBPS", "images") fileOutputFontFolder = os.path.join( globals.FILES_FOLDER, fileID, "output", "OEBPS", "Fonts") fileOutputStylesFolder = os.path.join( globals.FILES_FOLDER, fileID, "output", "OEBPS", "css") fileOutputTextFolder = os.path.join( globals.FILES_FOLDER, fileID, "output", "OEBPS") SAMPLEXHTML = os.path.join(fileOutputTextFolder, "SAMPLE.xhtml") SAMPLECSS = os.path.join(fileOutputStylesFolder, "style.css") fileOutputOPFFile = os.path.join( globals.FILES_FOLDER, fileID, "output", "OEBPS", "package.opf") fileOutputTocNcxFile = os.path.join( globals.FILES_FOLDER, fileID, "output", "OEBPS", "toc.ncx") fileOutputTocFile = os.path.join( globals.FILES_FOLDER, fileID, "output", "OEBPS", "TOC.xhtml") fileOutputBodtFile = os.path.join(fileOutputStylesFolder, "body.css") Path(fileOutputFolder).mkdir(parents=True, exist_ok=True) Path(fileOutputStylesFolder).mkdir(parents=True, exist_ok=True) Path(fileOutputTextFolder).mkdir(parents=True, exist_ok=True) copy_tree("template2", fileOutputFolder) # COPY REDACTED IMAGES copy_tree(os.path.join(globals.FILES_FOLDER, fileID, "images"), fileOutputImageFolder) def valueReplacer(PageNum, pageCSS, pageImg, text, Properties, width, height, TotalPages)-
It takes a string, finds all instances of {{KEY}} and replaces them with the value of the key in the Properties dictionary
:param PageNum: The page number of the current page :param pageCSS: The path to the CSS file for the page :param pageImg: The path to the image file for the page :param text: the text to be replaced :param Properties: A list of dictionaries, each dictionary has a key and a value :param width: The width of the image :param height: The height of the page in pixels :param TotalPages: Total number of pages in the book :return: The text of the template with the values replaced.
Expand source code
def valueReplacer(PageNum, pageCSS, pageImg, text, Properties, width, height, TotalPages): """ It takes a string, finds all instances of {{KEY}} and replaces them with the value of the key in the Properties dictionary :param PageNum: The page number of the current page :param pageCSS: The path to the CSS file for the page :param pageImg: The path to the image file for the page :param text: the text to be replaced :param Properties: A list of dictionaries, each dictionary has a key and a value :param width: The width of the image :param height: The height of the page in pixels :param TotalPages: Total number of pages in the book :return: The text of the template with the values replaced. """ matches = re.finditer(r"{{(\w+)}}", text, re.MULTILINE) for matchNum, match in enumerate(matches, start=1): for groups in match.groups(): if groups == "WIDTH": value = width elif groups == "HEIGHT": value = height elif groups == "FILE_REL_CSS": value = re.sub(r"(.+)Styles", "./Styles", pageCSS.replace("\\", "/"), 0, re.MULTILINE) elif groups == "TITLEPAGE_REL_PATH": titlepageList = list( filter(lambda d: d['key'] in "TITLEPAGE_NO", Properties)) titlepageNum = titlepageList[0]["value"] if len( titlepageList) > 0 else "" value = f"page_{str(int(titlepageNum) - 1).zfill(3)}.xhtml" elif groups == "CHAPTER1_REL_PATH": chapter1List = list( filter(lambda d: d['key'] in "CHAPTER1_NO", Properties)) chapter1Num = chapter1List[0]["value"] if len( chapter1List) > 0 else "" value = f"page_{str(int(chapter1Num) - 1).zfill(3)}.xhtml" elif groups == "COPYRIGHT_REL_PATH": copyrightList = list( filter(lambda d: d['key'] in "COPYRIGHT_NO", Properties)) copyrightNum = copyrightList[0]["value"] if len( copyrightList) > 0 else "" value = f"page_{str(int(copyrightNum) - 1).zfill(3)}.xhtml" elif groups == "COVER_REL_PATH": value = "cover.xhtml" elif groups == "TOTALPAGES": value = TotalPages elif groups == "RESOLUTION": value = str(width) + "x" + str(height) elif groups == "IMAGE_URL": value = re.sub(r"(.+)Images", "./Images", pageImg.replace("\\", "/"), 0, re.MULTILINE) elif groups == "PAGENUM": value = int(PageNum) - 1 if value == 0: value = "cover" else: value = str(value).zfill(3) elif groups == "BODY_REL_CSS": value = "./Styles/body.css" else: valueList = list( filter(lambda d: d['key'] in groups, Properties)) value = valueList[-1]["value"] if len(valueList) > 0 else "" if value != "": text = text.replace("{{" + str(groups) + "}}", str(value)) return text def xhtmlAndCssCreation(Properties, Fonts, TotalPages, Pages, fileID)-
It takes the data from the database and creates the xhtml and css files
:param Properties: A dictionary of properties that are used to replace values in the XHTML and CSS files :param Fonts: A list of font names used in the document :param TotalPages: Total number of pages in the PDF :param Pages: A list of dictionaries, each dictionary representing a page :param fileID: The ID of the file being processed
Expand source code
def xhtmlAndCssCreation(Properties, Fonts, TotalPages, Pages, fileID): """ It takes the data from the database and creates the xhtml and css files :param Properties: A dictionary of properties that are used to replace values in the XHTML and CSS files :param Fonts: A list of font names used in the document :param TotalPages: Total number of pages in the PDF :param Pages: A list of dictionaries, each dictionary representing a page :param fileID: The ID of the file being processed """ global fileOutputFolder global fileRedactedFolder global fileOutputImageFolder global fileOutputFontFolder global fileOutputStylesFolder global fileOutputTextFolder global SAMPLEXHTML global SAMPLECSS global fileOutputOPFFile global fileOutputTocNcxFile global fileOutputTocFile global SAMPLEXHTMLTEXT global SAMPLECSSTEXT global fileOutputBodtFile # GENERATING TEXT AND CSS FILES with io.open(SAMPLEXHTML, 'r', encoding='utf8') as f: SAMPLEXHTMLTEXT = f.read() with io.open(SAMPLECSS, 'r', encoding='utf8') as f: SAMPLEXCSSTEXT = f.read() width = "" height = "" for page in Pages: height = page["Image"]["Height"] width = page["Image"]["Width"] pageNum = str(page['PageNum'] - 1).zfill(3) boxes = list(globals.boxesDB.find( { "pageId": page["PageID"] } ).sort([("boxIndex", pymongo.ASCENDING)]) ) pageImgTmp = os.path.join( fileOutputImageFolder, f"Page{page['PageNum']}.jpeg") pageImg = "" pageImgNum = "" if page["PageNum"] == 1: coverImg = pageImgTmp.replace( f"Page{page['PageNum']}.jpeg", "cover.jpg") pageImgNum = "cover.jpg" if not os.path.exists(coverImg): os.rename(pageImgTmp, coverImg) if os.path.exists(pageImgTmp): os.remove(pageImgTmp) pageImg = coverImg pageXhtml = os.path.join(fileOutputTextFolder, "cover.xhtml") pageCSS = os.path.join(fileOutputStylesFolder, "style.css") pageXhtmlText = SAMPLEXHTMLTEXT pageCSSText = SAMPLEXCSSTEXT # coverXhtmlText = coverXhtmlText.replace() else: pageImg = pageImgTmp.replace( f"Page{page['PageNum']}.jpeg", f"page_{pageNum}.jpg") if not os.path.exists(pageImg): os.rename(pageImgTmp, pageImg) if os.path.exists(pageImgTmp): os.remove(pageImgTmp) pageXhtml = os.path.join( fileOutputTextFolder, f"page_{pageNum}.xhtml") pageImgNum = f"page_{pageNum}.jpg" pageCSS = os.path.join(fileOutputStylesFolder, f"style.css") pageXhtmlText = SAMPLEXHTMLTEXT pageCSSText = SAMPLEXCSSTEXT bodyData = [] cssData = [] cssCounter = 0 lineCssCounter = 0 bookmarkChapterStart = - 1 while str(toc[bookmarkChapterStart+1][1]).lower() not in ["contents", "table of content"]: bookmarkChapterStart += 1 if len(Pages) - 2 >= bookmarkChapterStart: bookmarkChapterStart += 1 else: bookmarkChapterStart = -1 bodyData.append( f'<p style="background-repeat: no-repeat;"><img src="images/{pageImgNum}" alt=""/></p>') for box in boxes: pStyle = f'position: absolute;\n' \ f'top: {box["coordinates"][1]}px;\n' \ f'left: {box["coordinates"][0]}px;\n' \ f'width: {width - box["coordinates"][0]}px;\n' \ f'height: {box["coordinates"][3]}px;\n' lineCssCounter += 1 lineCssSelector = "line" + str(lineCssCounter) cssData.append(f".{lineCssSelector}" + "{\n" + pStyle + "\n}") innerHtml = [] for line in box["linesData"]: if "%" not in line["lineHeight"]: line["lineHeight"] += "%" innerCssData = f'position: {line["position"]};\n' \ f'top: {line["top"]};\n'\ f'font-family: sans-serif;\n'\ f'left: {line["left"]};\n'\ f'font-size: {line["fontSize"]};\n'\ f'-ms-transform: rotate({line["textRotate"]}deg);\n'\ f'-webkit-transform: rotate({line["textRotate"]}deg);\n'\ f'transform: rotate({line["textRotate"]}deg);\n'\ f'font-weight: normal;\n'\ f'line-height: {line["lineHeight"].replace("normal%","normal")};\n'\ f'color: {line["color"]};\n'\ f'word-spacing: {line["wordSpacing"]};\n'\ f'letter-spacing: {line["letterSpacing"]};\n'\ f'font-style: normal;\n'\ f'text-decoration: {line["textDecoration"]};\n'\ f'white-space: pre;\n' cssCounter += 1 innerCssSelector = "span" + str(cssCounter) data = line["text"].replace("\n", "<br>\n") line["text"] = re.sub( r"\s{2,}", " ", line["text"], 0, re.MULTILINE) if line["text"].startswith(" "): lastIndex = len(innerHtml) - 1 print(lastIndex) if len(innerHtml) > 0: lastElement = innerHtml[lastIndex] lastElement = lastElement.replace( "</span>", " </span>") innerHtml[lastIndex] = lastElement innerHtml.append( f'<span class="{innerCssSelector}">{data.lstrip()}</span>') if line["br"] is True and "<br>" not in data: innerHtml.append("<br>") cssData.append(f".{innerCssSelector}" + "{\n" + innerCssData + "\n}") bodyData.append( f'<{box["tagName"]} class="{lineCssSelector}"><a href="page_{str(toc[bookmarkChapterStart+lineCssCounter][2] - 1).zfill(3)}.xhtml">{"".join(innerHtml)}</a></{box["tagName"]}>') pageXhtmlText = pageXhtmlText.replace( "<bodyData/>", "\n".join(bodyData)).replace("<br>", "") cssDataFinal = "\n".join(cssData) # cssDataFinal = re.sub(r"font-family:(.*?);", "font-family:\"\\1\";", cssDataFinal, 0, re.MULTILINE) cssDataFinal = cssDataFinal.replace('" sans-serif"', "sans-serif") # pageCSSText = pageCSSText.replace("{{BODY_CSS}}", cssDataFinal) if len(boxes) == 0: pageXhtmlText = pageXhtmlText.replace( "<bodyData/>", "\n".join(bodyData)).replace("<br>", "") cssDataFinal = cssDataFinal.replace( "{{BODY_CSS}}", "").replace("{{FONTS_CSS}}", "") # REPLACING PROPERTIES IN CSS AND XHTML FILES if page['PageNum'] == 1: pageNum = "cover" pageXhtmlText = valueReplacer(page['PageNum'], pageCSS, pageImg, pageXhtmlText, Properties, width, height, TotalPages).\ replace('<link href="./Styles/body.css" type="text/css" rel="stylesheet" />', "")\ .replace(f"static/files/{fileID}/output/OEBPS", ".").replace(' class="page"', "") cssDataFinal = valueReplacer(page['PageNum'], pageCSS, pageImg, cssDataFinal, Properties, width, height, TotalPages) with io.open(pageXhtml, 'w', encoding='utf8', newline='\n') as f: f.write(re.sub(r"^\s+", "", pageXhtmlText, 0, re.MULTILINE)) with io.open(pageCSS, 'a', encoding='utf8', newline='\n') as f: f.write(cssDataFinal) finalCss = "" fileCss = os.path.join(fileOutputStylesFolder, "style.css") with io.open(fileCss, 'r', encoding='utf8', newline='\n') as f: finalCss = f.read() with io.open(fileCss, 'w', encoding='utf8', newline='\n') as f: f.write(finalCss.replace("{{WIDTH}}", str( width)).replace("{{HEIGHT}}", str(height))) os.remove(SAMPLEXHTML) # os.remove(SAMPLECSS)
Classes
class GenerateFile2-
Represents an abstract RESTful resource. Concrete resources should extend from this class and expose methods for each supported HTTP method. If a resource is invoked with an unsupported HTTP method, the API will return a response with status 405 Method Not Allowed. Otherwise the appropriate method is called and passed all arguments from the url rule used when adding the resource to an Api instance. See :meth:
~flask_restful.Api.add_resourcefor details.Expand source code
class GenerateFile2(Resource): @use_kwargs(auth_args, location="headers") @use_kwargs({"fileID": fields.Str(required=True)}, location="query") def post(self, Authorization, fileID): """ It takes a PDF file, converts it to an EPUB file, and returns the path to the EPUB file. :param Authorization: The token that is generated when the user logs in :param fileID: The ID of the file in the database :return: The path to the epub file. """ if auth.verify(str(Authorization).split(" ")[1]): templateCopy(fileID) result = globals.filesDB.find_one( { "_id": ObjectId(fileID) } ) TotalPages = result["TotalPages"] Pages = result["Pages"] Fonts = result["Fonts"] Properties = result["Properties"] width = result["Pages"][0]["Image"]["Width"] height = result["Pages"][0]["Image"]["Height"] doc = fitz.open(os.path.join( globals.FILES_FOLDER, fileID, f"{fileID}.pdf")) global toc toc = doc.get_toc() suportingFilesCreation( Properties, width, height, Fonts, TotalPages) xhtmlAndCssCreation(Properties, Fonts, TotalPages, Pages, fileID) isbn = list(filter(lambda d: d['key'] in "ISBN", Properties)) if len(isbn) > 0: isbn = isbn[0]["value"] else: isbn = "undefined" finalFile = os.path.join(fileOutputFolder, f'{isbn}.epub') if os.path.exists(finalFile): os.remove(finalFile) zipobj = ZipFile(finalFile, 'w', ZIP_STORED) rootlen = len(fileOutputFolder) + 1 for base, dirs, files in os.walk(fileOutputFolder): for file in files: if ".epub" not in file: fn = os.path.join(base, file) zipobj.write(fn, fn[rootlen:]) return {"msg": finalFile.replace("\\", "/")}, 200 else: return {"msg": "Unauthorized! Access Denied"}, 401Ancestors
- flask_restful.Resource
- flask.views.MethodView
- flask.views.View
Class variables
var methods : Optional[List[str]]
Methods
def post(self, Authorization, fileID)-
It takes a PDF file, converts it to an EPUB file, and returns the path to the EPUB file.
:param Authorization: The token that is generated when the user logs in :param fileID: The ID of the file in the database :return: The path to the epub file.
Expand source code
@use_kwargs(auth_args, location="headers") @use_kwargs({"fileID": fields.Str(required=True)}, location="query") def post(self, Authorization, fileID): """ It takes a PDF file, converts it to an EPUB file, and returns the path to the EPUB file. :param Authorization: The token that is generated when the user logs in :param fileID: The ID of the file in the database :return: The path to the epub file. """ if auth.verify(str(Authorization).split(" ")[1]): templateCopy(fileID) result = globals.filesDB.find_one( { "_id": ObjectId(fileID) } ) TotalPages = result["TotalPages"] Pages = result["Pages"] Fonts = result["Fonts"] Properties = result["Properties"] width = result["Pages"][0]["Image"]["Width"] height = result["Pages"][0]["Image"]["Height"] doc = fitz.open(os.path.join( globals.FILES_FOLDER, fileID, f"{fileID}.pdf")) global toc toc = doc.get_toc() suportingFilesCreation( Properties, width, height, Fonts, TotalPages) xhtmlAndCssCreation(Properties, Fonts, TotalPages, Pages, fileID) isbn = list(filter(lambda d: d['key'] in "ISBN", Properties)) if len(isbn) > 0: isbn = isbn[0]["value"] else: isbn = "undefined" finalFile = os.path.join(fileOutputFolder, f'{isbn}.epub') if os.path.exists(finalFile): os.remove(finalFile) zipobj = ZipFile(finalFile, 'w', ZIP_STORED) rootlen = len(fileOutputFolder) + 1 for base, dirs, files in os.walk(fileOutputFolder): for file in files: if ".epub" not in file: fn = os.path.join(base, file) zipobj.write(fn, fn[rootlen:]) return {"msg": finalFile.replace("\\", "/")}, 200 else: return {"msg": "Unauthorized! Access Denied"}, 401