mirror of
https://github.com/ciromattia/kcc
synced 2025-12-13 01:36:27 +00:00
Overhaul of converter internals
This commit is contained in:
@@ -33,5 +33,4 @@ from kcc.comic2ebook import main
|
||||
if __name__ == "__main__":
|
||||
freeze_support()
|
||||
print('comic2ebook v' + __version__ + ' - Written by Ciro Mattia Gonano and Pawel Jastrzebski.')
|
||||
main(sys.argv[1:])
|
||||
sys.exit(0)
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
|
||||
@@ -33,5 +33,4 @@ from kcc.comic2panel import main
|
||||
if __name__ == "__main__":
|
||||
freeze_support()
|
||||
print('comic2panel v' + __version__ + ' - Written by Ciro Mattia Gonano and Pawel Jastrzebski.')
|
||||
main(sys.argv[1:])
|
||||
sys.exit(0)
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
|
||||
@@ -62,56 +62,37 @@ def main(argv=None):
|
||||
optionstemplate, args = parser.parse_args(argv)
|
||||
if len(args) == 0:
|
||||
parser.print_help()
|
||||
return
|
||||
return 0
|
||||
sources = set([source for arg in args for source in glob(arg)])
|
||||
outputPath = []
|
||||
if len(sources) == 0:
|
||||
print('No matching files found.')
|
||||
return
|
||||
return 1
|
||||
for source in sources:
|
||||
source = source.rstrip('\\').rstrip('/')
|
||||
options = copy(optionstemplate)
|
||||
checkOptions()
|
||||
if len(sources) > 1:
|
||||
print('\nWorking on ' + source)
|
||||
outputPath = makeBook(source)
|
||||
return outputPath
|
||||
print('Working on ' + source + '...')
|
||||
makeBook(source)
|
||||
return 0
|
||||
|
||||
|
||||
def buildHTML(path, imgfile, imgfilepath, forcePV=False):
|
||||
def buildHTML(path, imgfile, imgfilepath):
|
||||
imgfilepath = md5Checksum(imgfilepath)
|
||||
filename = getImageFileName(imgfile)
|
||||
additionalStyle = ''
|
||||
if options.imgproc:
|
||||
if "Rotated" in options.imgIndex[imgfilepath]:
|
||||
rotatedPage = True
|
||||
else:
|
||||
rotatedPage = False
|
||||
if "NoPanelView" in options.imgIndex[imgfilepath]:
|
||||
noPV = True
|
||||
else:
|
||||
noPV = False
|
||||
if "NoHorizontalPanelView" in options.imgIndex[imgfilepath]:
|
||||
noHorizontalPV = True
|
||||
else:
|
||||
noHorizontalPV = False
|
||||
if "NoVerticalPanelView" in options.imgIndex[imgfilepath]:
|
||||
noVerticalPV = True
|
||||
else:
|
||||
noVerticalPV = False
|
||||
if "BlackFill" in options.imgIndex[imgfilepath]:
|
||||
additionalStyle = ' style="background-color:#000000" '
|
||||
deviceres = options.profileData[1]
|
||||
if "Rotated" in options.imgIndex[imgfilepath]:
|
||||
rotatedPage = True
|
||||
else:
|
||||
rotatedPage = False
|
||||
noPV = False
|
||||
noHorizontalPV = False
|
||||
noVerticalPV = False
|
||||
if forcePV and noPV:
|
||||
noPV = False
|
||||
noHorizontalPV = True
|
||||
noVerticalPV = True
|
||||
if "BlackFill" in options.imgIndex[imgfilepath]:
|
||||
additionalStyle = 'background-color:#000000;'
|
||||
else:
|
||||
additionalStyle = 'background-color:#FFFFFF;'
|
||||
htmlpath = ''
|
||||
postfix = ''
|
||||
size = ''
|
||||
imgfilepv = ''
|
||||
backref = 1
|
||||
head = path
|
||||
while True:
|
||||
@@ -123,100 +104,94 @@ def buildHTML(path, imgfile, imgfilepath, forcePV=False):
|
||||
backref += 1
|
||||
if not os.path.exists(htmlpath):
|
||||
os.makedirs(htmlpath)
|
||||
htmlfile = os.path.join(htmlpath, filename[0] + '.html')
|
||||
htmlfile = os.path.join(htmlpath, filename[0] + '.xhtml')
|
||||
f = open(htmlfile, "w", encoding='UTF-8')
|
||||
if options.iskindle:
|
||||
f.writelines(["<?xml version=\"1.0\" encoding=\"utf-8\"?>\n",
|
||||
"<!DOCTYPE html>\n",
|
||||
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n",
|
||||
"<head>\n",
|
||||
"<title>", filename[0], "</title>\n",
|
||||
"<link href=\"", "../" * (backref - 1), "style.css\" type=\"text/css\" rel=\"stylesheet\"/>\n",
|
||||
"<meta charset=\"utf-8\"/>\n",
|
||||
"</head>\n",
|
||||
"<body" + additionalStyle + ">\n",
|
||||
"<div class=\"fs\">\n",
|
||||
"<div><img src=\"", "../" * backref, "Images/", postfix, imgfile, "\" alt=\"",
|
||||
imgfile, "\" class=\"singlePage\"/></div>\n"
|
||||
])
|
||||
if (options.panelview or forcePV) and not noPV:
|
||||
options.panelviewused = True
|
||||
if not noHorizontalPV and not noVerticalPV:
|
||||
if rotatedPage:
|
||||
if options.righttoleft:
|
||||
order = [1, 3, 2, 4]
|
||||
else:
|
||||
order = [2, 4, 1, 3]
|
||||
f.writelines(["<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
|
||||
"<!DOCTYPE html>\n",
|
||||
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n",
|
||||
"<head>\n",
|
||||
"<title>", filename[0], "</title>\n",
|
||||
"<link href=\"", "../" * (backref - 1), "style.css\" type=\"text/css\" rel=\"stylesheet\"/>\n",
|
||||
"<meta name=\"viewport\" "
|
||||
"content=\"width=" + str(deviceres[0]) + ", height=" + str(deviceres[1]) + "\"/>\n"
|
||||
"</head>\n",
|
||||
"<body style=\"background-image: ",
|
||||
"url('", "../" * backref, "Images/", postfix, imgfile, "'); " + additionalStyle + "\">\n"])
|
||||
if options.iskindle and options.panelview:
|
||||
if options.hqmode:
|
||||
imgfilepv = list(os.path.splitext(imgfile))
|
||||
imgfilepv[0] += "-hq"
|
||||
imgfilepv = "".join(imgfilepv)
|
||||
if os.path.isfile(os.path.join(head, "Images", postfix, imgfilepv)):
|
||||
size = Image.open(os.path.join(head, "Images", postfix, imgfilepv)).size
|
||||
if not options.hqmode or not size:
|
||||
imgfilepv = imgfile
|
||||
sizeTmp = Image.open(os.path.join(head, "Images", postfix, imgfilepv)).size
|
||||
size = (int(sizeTmp[0] * 1.5), int(sizeTmp[1] * 1.5))
|
||||
if size[0] <= deviceres[0]:
|
||||
noHorizontalPV = True
|
||||
else:
|
||||
noHorizontalPV = False
|
||||
if size[1] <= deviceres[1]:
|
||||
noVerticalPV = True
|
||||
else:
|
||||
noVerticalPV = False
|
||||
x, y = getPanelViewSize(deviceres, size)
|
||||
boxStyles = {"PV-TL": "position:absolute;left:0;top:0;",
|
||||
"PV-TR": "position:absolute;right:0;top:0;",
|
||||
"PV-BL": "position:absolute;left:0;bottom:0;",
|
||||
"PV-BR": "position:absolute;right:0;bottom:0;",
|
||||
"PV-T": "position:absolute;top:0;left:" + x + "%;",
|
||||
"PV-B": "position:absolute;bottom:0;left:" + x + "%;",
|
||||
"PV-L": "position:absolute;left:0;top:" + y + "%;",
|
||||
"PV-R": "position:absolute;right:0;top:" + y + "%;"}
|
||||
f.write("<div id=\"PV\">\n")
|
||||
if not noHorizontalPV and not noVerticalPV:
|
||||
if rotatedPage:
|
||||
if options.righttoleft:
|
||||
order = [1, 3, 2, 4]
|
||||
else:
|
||||
if options.righttoleft:
|
||||
order = [2, 1, 4, 3]
|
||||
else:
|
||||
order = [1, 2, 3, 4]
|
||||
boxes = ["BoxTL", "BoxTR", "BoxBL", "BoxBR"]
|
||||
elif noHorizontalPV and not noVerticalPV:
|
||||
if rotatedPage:
|
||||
if options.righttoleft:
|
||||
order = [1, 2]
|
||||
else:
|
||||
order = [2, 1]
|
||||
order = [2, 4, 1, 3]
|
||||
else:
|
||||
if options.righttoleft:
|
||||
order = [2, 1, 4, 3]
|
||||
else:
|
||||
order = [1, 2]
|
||||
boxes = ["BoxT", "BoxB"]
|
||||
elif not noHorizontalPV and noVerticalPV:
|
||||
if rotatedPage:
|
||||
order = [1, 2, 3, 4]
|
||||
boxes = ["PV-TL", "PV-TR", "PV-BL", "PV-BR"]
|
||||
elif noHorizontalPV and not noVerticalPV:
|
||||
if rotatedPage:
|
||||
if options.righttoleft:
|
||||
order = [1, 2]
|
||||
else:
|
||||
if options.righttoleft:
|
||||
order = [2, 1]
|
||||
else:
|
||||
order = [1, 2]
|
||||
boxes = ["BoxL", "BoxR"]
|
||||
order = [2, 1]
|
||||
else:
|
||||
order = [1]
|
||||
boxes = ["BoxC"]
|
||||
for i in range(0, len(boxes)):
|
||||
f.writelines(["<div id=\"" + boxes[i] + "\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
|
||||
"'{\"targetId\":\"" + boxes[i] + "-Panel-Parent\", \"ordinal\":" + str(order[i]),
|
||||
"}'></a></div>\n"])
|
||||
if options.quality == 2 and not forcePV:
|
||||
imgfilepv = imgfile.split(".")
|
||||
imgfilepv[0] += "-hq"
|
||||
imgfilepv = ".".join(imgfilepv)
|
||||
order = [1, 2]
|
||||
boxes = ["PV-T", "PV-B"]
|
||||
elif not noHorizontalPV and noVerticalPV:
|
||||
if rotatedPage:
|
||||
order = [1, 2]
|
||||
else:
|
||||
imgfilepv = imgfile
|
||||
xl, yu, xr, yd = detectMargins(imgfilepath)
|
||||
boxStyles = {"BoxTL": "left:" + xl + ";top:" + yu + ";",
|
||||
"BoxTR": "right:" + xr + ";top:" + yu + ";",
|
||||
"BoxBL": "left:" + xl + ";bottom:" + yd + ";",
|
||||
"BoxBR": "right:" + xr + ";bottom:" + yd + ";",
|
||||
"BoxT": "left:-25%;top:" + yu + ";",
|
||||
"BoxB": "left:-25%;bottom:" + yd + ";",
|
||||
"BoxL": "left:" + xl + ";top:-25%;",
|
||||
"BoxR": "right:" + xr + ";top:-25%;",
|
||||
"BoxC": "left:-25%;top:-25%;"
|
||||
}
|
||||
for box in boxes:
|
||||
f.writelines(["<div id=\"" + box + "-Panel-Parent\" class=\"target-mag-parent\"><div id=\"",
|
||||
"Generic-Panel\" class=\"target-mag\"><img style=\"" + boxStyles[box] + "\" src=\"",
|
||||
"../" * backref, "Images/", postfix, imgfilepv, "\" alt=\"" + imgfilepv,
|
||||
"\"/></div></div>\n",
|
||||
])
|
||||
f.writelines(["</div>\n</body>\n</html>"])
|
||||
else:
|
||||
f.writelines(["<?xml version=\"1.0\" encoding=\"utf-8\"?>\n",
|
||||
"<!DOCTYPE html>\n",
|
||||
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n",
|
||||
"<head>\n",
|
||||
"<title>", filename[0], "</title>\n",
|
||||
"<link href=\"", "../" * (backref - 1), "style.css\" type=\"text/css\" rel=\"stylesheet\"/>\n",
|
||||
"<meta charset=\"utf-8\"/>\n",
|
||||
"</head>\n",
|
||||
"<body" + additionalStyle + ">\n",
|
||||
"<div class=\"epub:type=bodymatter\">\n",
|
||||
"<img src=\"", "../" * backref, "Images/", postfix, imgfile, "\"/>\n",
|
||||
"</div>\n",
|
||||
"</body>\n</html>"
|
||||
])
|
||||
if options.righttoleft:
|
||||
order = [2, 1]
|
||||
else:
|
||||
order = [1, 2]
|
||||
boxes = ["PV-L", "PV-R"]
|
||||
else:
|
||||
order = []
|
||||
boxes = []
|
||||
for i in range(0, len(boxes)):
|
||||
f.writelines(["<div id=\"" + boxes[i] + "\">\n",
|
||||
"<a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"" + boxes[i] +
|
||||
"-P\", \"ordinal\":" + str(order[i]) + "}'></a>\n",
|
||||
"</div>\n"])
|
||||
for box in boxes:
|
||||
f.writelines(["<div class=\"PV-P\" id=\"" + box + "-P\" style=\"" + additionalStyle + "\">\n",
|
||||
"<img style=\"" + boxStyles[box] + "\" src=\"", "../" * backref, "Images/", postfix,
|
||||
imgfilepv, "\" width=\"" + str(size[0]) + "\" height=\"" + str(size[1]) + "\"/>\n",
|
||||
"</div>\n"])
|
||||
f.write("</div>\n")
|
||||
f.writelines(["</body>\n",
|
||||
"</html>\n"])
|
||||
f.close()
|
||||
return path, imgfile
|
||||
|
||||
@@ -248,7 +223,7 @@ def buildNCX(dstdir, title, chapters, chapterNames):
|
||||
title = chapterNames[os.path.basename(folder)]
|
||||
f.write("<navPoint id=\"" + navID + "\"><navLabel><text>" +
|
||||
title + "</text></navLabel><content src=\"" + filename[0].replace("\\", "/") +
|
||||
".html\"/></navPoint>\n")
|
||||
".xhtml\"/></navPoint>\n")
|
||||
f.write("</navMap>\n</ncx>")
|
||||
f.close()
|
||||
|
||||
@@ -273,7 +248,7 @@ def buildNAV(dstdir, title, chapters, chapterNames):
|
||||
title = chapterNames[chapter[1]]
|
||||
elif os.path.basename(folder) != "Text":
|
||||
title = chapterNames[os.path.basename(folder)]
|
||||
f.write("<li><a href=\"" + filename[0].replace("\\", "/") + ".html\">" + title + "</a></li>\n")
|
||||
f.write("<li><a href=\"" + filename[0].replace("\\", "/") + ".xhtml\">" + title + "</a></li>\n")
|
||||
f.writelines(["</ol>\n",
|
||||
"</nav>\n",
|
||||
"<nav epub:type=\"page-list\">\n",
|
||||
@@ -286,14 +261,14 @@ def buildNAV(dstdir, title, chapters, chapterNames):
|
||||
title = chapterNames[chapter[1]]
|
||||
elif os.path.basename(folder) != "Text":
|
||||
title = chapterNames[os.path.basename(folder)]
|
||||
f.write("<li><a href=\"" + filename[0].replace("\\", "/") + ".html\">" + title + "</a></li>\n")
|
||||
f.write("<li><a href=\"" + filename[0].replace("\\", "/") + ".xhtml\">" + title + "</a></li>\n")
|
||||
f.write("</ol>\n</nav>\n</body>\n</html>")
|
||||
f.close()
|
||||
|
||||
|
||||
def buildOPF(dstdir, title, filelist, cover=None):
|
||||
opffile = os.path.join(dstdir, 'OEBPS', 'content.opf')
|
||||
profilelabel, deviceres, palette, gamma, panelviewsize = options.profileData
|
||||
deviceres = options.profileData[1]
|
||||
if options.righttoleft:
|
||||
writingmode = "horizontal-rl"
|
||||
else:
|
||||
@@ -301,15 +276,15 @@ def buildOPF(dstdir, title, filelist, cover=None):
|
||||
f = open(opffile, "w", encoding='UTF-8')
|
||||
f.writelines(["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
|
||||
"<package version=\"3.0\" unique-identifier=\"BookID\" ",
|
||||
"prefix=\"rendition: http://www.idpf.org/vocab/rendition/#\" ",
|
||||
"xmlns=\"http://www.idpf.org/2007/opf\">\n",
|
||||
"<metadata xmlns:opf=\"http://www.idpf.org/2007/opf\" ",
|
||||
"xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n",
|
||||
"<dc:title>", title, "</dc:title>\n",
|
||||
"<dc:language>en-US</dc:language>\n",
|
||||
"<dc:identifier id=\"BookID\">urn:uuid:", options.uuid, "</dc:identifier>\n",
|
||||
"<dc:contributor id=\"contributor\">KindleComicConverter-" + __version__ + "</dc:contributor>\n",
|
||||
"<dc:description>", options.summary, "</dc:description>\n"])
|
||||
"<dc:contributor id=\"contributor\">KindleComicConverter-" + __version__ + "</dc:contributor>\n"])
|
||||
if len(options.summary) > 0:
|
||||
f.writelines(["<dc:description>", options.summary, "</dc:description>\n"])
|
||||
for author in options.authors:
|
||||
f.writelines(["<dc:creator>", author, "</dc:creator>\n"])
|
||||
f.writelines(["<meta property=\"dcterms:modified\">" + strftime("%Y-%m-%dT%H:%M:%SZ", gmtime()) + "</meta>\n",
|
||||
@@ -347,7 +322,7 @@ def buildOPF(dstdir, title, filelist, cover=None):
|
||||
reflist.append(uniqueid)
|
||||
f.write("<item id=\"page_" + str(uniqueid) + "\" href=\"" +
|
||||
folder.replace('Images', 'Text') + "/" + filename[0] +
|
||||
".html\" media-type=\"application/xhtml+xml\"/>\n")
|
||||
".xhtml\" media-type=\"application/xhtml+xml\"/>\n")
|
||||
if '.png' == filename[1]:
|
||||
mt = 'image/png'
|
||||
else:
|
||||
@@ -359,6 +334,29 @@ def buildOPF(dstdir, title, filelist, cover=None):
|
||||
f.write("</manifest>\n<spine page-progression-direction=\"rtl\" toc=\"ncx\">\n")
|
||||
else:
|
||||
f.write("</manifest>\n<spine page-progression-direction=\"ltr\" toc=\"ncx\">\n")
|
||||
# if options.iskindle and options.profile != 'Custom':
|
||||
# if options.righttoleft:
|
||||
# nextflow = 'right'
|
||||
# else:
|
||||
# nextflow = 'left'
|
||||
# for entry in reflist:
|
||||
# if '-kcc-b' in entry:
|
||||
# if options.righttoleft:
|
||||
# f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-right\"/>\n")
|
||||
# else:
|
||||
# f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-left\"/>\n")
|
||||
# elif '-kcc-c' in entry:
|
||||
# if options.righttoleft:
|
||||
# f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-left\"/>\n")
|
||||
# else:
|
||||
# f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-right\"/>\n")
|
||||
# else:
|
||||
# f.write("<itemref idref=\"page_" + entry + "\" properties=\"facing-page-" + nextflow + "\"/>\n")
|
||||
# if nextflow == 'right':
|
||||
# nextflow = 'left'
|
||||
# else:
|
||||
# nextflow = 'right'
|
||||
# else:
|
||||
for entry in reflist:
|
||||
f.write("<itemref idref=\"page_" + entry + "\"/>\n")
|
||||
f.write("</spine>\n</package>\n")
|
||||
@@ -378,139 +376,89 @@ def buildEPUB(path, chapterNames, tomeNumber):
|
||||
filelist = []
|
||||
chapterlist = []
|
||||
cover = None
|
||||
lastfile = None
|
||||
_, deviceres, _, _, panelviewsize = options.profileData
|
||||
os.mkdir(os.path.join(path, 'OEBPS', 'Text'))
|
||||
f = open(os.path.join(path, 'OEBPS', 'Text', 'style.css'), 'w', encoding='UTF-8')
|
||||
if options.iskindle:
|
||||
f.writelines(["@page {\n",
|
||||
"margin-bottom: 0;\n",
|
||||
"margin-top: 0\n",
|
||||
"}\n",
|
||||
"body {\n",
|
||||
"display: block;\n",
|
||||
"margin-bottom: 0;\n",
|
||||
"margin-left: 0;\n",
|
||||
"margin-right: 0;\n",
|
||||
"margin-top: 0;\n",
|
||||
"padding-bottom: 0;\n",
|
||||
"padding-left: 0;\n",
|
||||
"padding-right: 0;\n",
|
||||
"padding-top: 0;\n",
|
||||
"text-align: left\n",
|
||||
"}\n",
|
||||
"div.fs {\n",
|
||||
"height: ", str(deviceres[1]), "px;\n",
|
||||
"width: ", str(deviceres[0]), "px;\n",
|
||||
"position: relative;\n",
|
||||
"display: block;\n",
|
||||
"text-align: center\n",
|
||||
"}\n",
|
||||
"div.fs a {\n",
|
||||
"display: block;\n",
|
||||
"width : 100%;\n",
|
||||
"height: 100%;\n",
|
||||
"}\n",
|
||||
"div.fs div {\n",
|
||||
"position: absolute;\n",
|
||||
"}\n",
|
||||
"img.singlePage {\n",
|
||||
"position: absolute;\n",
|
||||
"height: ", str(deviceres[1]), "px;\n",
|
||||
"width: ", str(deviceres[0]), "px;\n",
|
||||
"}\n",
|
||||
"div.target-mag-parent {\n",
|
||||
"width:100%;\n",
|
||||
"height:100%;\n",
|
||||
"display:none;\n",
|
||||
"}\n",
|
||||
"div.target-mag {\n",
|
||||
"position: absolute;\n",
|
||||
"display: block;\n",
|
||||
"overflow: hidden;\n",
|
||||
"}\n",
|
||||
"div.target-mag img {\n",
|
||||
"position: absolute;\n",
|
||||
"height: ", str(panelviewsize[1]), "px;\n",
|
||||
"width: ", str(panelviewsize[0]), "px;\n",
|
||||
"}\n",
|
||||
"#Generic-Panel {\n",
|
||||
"top: 0;\n",
|
||||
"height: 100%;\n",
|
||||
"width: 100%;\n",
|
||||
"}\n",
|
||||
"#BoxC {\n",
|
||||
"top: 0;\n",
|
||||
"height: 100%;\n",
|
||||
"width: 100%;\n",
|
||||
"}\n",
|
||||
"#BoxT {\n",
|
||||
"top: 0;\n",
|
||||
"height: 50%;\n",
|
||||
"width: 100%;\n",
|
||||
"}\n",
|
||||
"#BoxB {\n",
|
||||
"bottom: 0;\n",
|
||||
"height: 50%;\n",
|
||||
"width: 100%;\n",
|
||||
"}\n",
|
||||
"#BoxL {\n",
|
||||
"left: 0;\n",
|
||||
"height: 100%;\n",
|
||||
"width: 50%;\n",
|
||||
"}\n",
|
||||
"#BoxR {\n",
|
||||
"right: 0;\n",
|
||||
"height: 100%;\n",
|
||||
"width: 50%;\n",
|
||||
"}\n",
|
||||
"#BoxTL {\n",
|
||||
"top: 0;\n",
|
||||
"left: 0;\n",
|
||||
"height: 50%;\n",
|
||||
"width: 50%;\n",
|
||||
"}\n",
|
||||
"#BoxTR {\n",
|
||||
"top: 0;\n",
|
||||
"right: 0;\n",
|
||||
"height: 50%;\n",
|
||||
"width: 50%;\n",
|
||||
"}\n",
|
||||
"#BoxBL {\n",
|
||||
"bottom: 0;\n",
|
||||
"left: 0;\n",
|
||||
"height: 50%;\n",
|
||||
"width: 50%;\n",
|
||||
"}\n",
|
||||
"#BoxBR {\n",
|
||||
"bottom: 0;\n",
|
||||
"right: 0;\n",
|
||||
"height: 50%;\n",
|
||||
"width: 50%;\n",
|
||||
"}",
|
||||
])
|
||||
else:
|
||||
f.writelines([
|
||||
"@namespace epub \"http://www.idpf.org/2007/ops\";\n",
|
||||
"@charset \"UTF-8\";\n",
|
||||
"body {\n",
|
||||
"margin: 0;\n",
|
||||
"}\n",
|
||||
"img {\n",
|
||||
"position: absolute;\n",
|
||||
"margin: 0;\n",
|
||||
"z-index: 0;\n",
|
||||
"height: 100%;\n",
|
||||
"}"])
|
||||
f.writelines(["@page {\n",
|
||||
"margin: 0;\n",
|
||||
"}\n",
|
||||
"body {\n",
|
||||
"display: block;\n",
|
||||
"margin: 0;\n",
|
||||
"padding: 0;\n",
|
||||
"background-position: center center;\n",
|
||||
"background-repeat: no-repeat;\n",
|
||||
"background-size: auto auto;\n",
|
||||
"}\n",
|
||||
"#PV {\n",
|
||||
"position: absolute;\n",
|
||||
"width: 100%;\n",
|
||||
"height: 100%;\n",
|
||||
"}\n",
|
||||
"#PV-T {\n",
|
||||
"top: 0;\n",
|
||||
"width: 100%;\n",
|
||||
"height: 50%;\n",
|
||||
"}\n",
|
||||
"#PV-B {\n",
|
||||
"bottom: 0;\n",
|
||||
"width: 100%;\n",
|
||||
"height: 50%;\n",
|
||||
"}\n",
|
||||
"#PV-L {\n",
|
||||
"left: 0;\n",
|
||||
"width: 50%;\n",
|
||||
"height: 100%;\n",
|
||||
"float: left;\n",
|
||||
"}\n",
|
||||
"#PV-R {\n",
|
||||
"right: 0;\n",
|
||||
"width: 50%;\n",
|
||||
"height: 100%;\n",
|
||||
"float: right;\n",
|
||||
"}\n",
|
||||
"#PV-TL {\n",
|
||||
"top: 0;\n",
|
||||
"left: 0;\n",
|
||||
"width: 50%;\n",
|
||||
"height: 50%;\n",
|
||||
"float: left;\n",
|
||||
"}\n",
|
||||
"#PV-TR {\n",
|
||||
"top: 0;\n",
|
||||
"right: 0;\n",
|
||||
"width: 50%;\n",
|
||||
"height: 50%;\n",
|
||||
"float: right;\n",
|
||||
"}\n",
|
||||
"#PV-BL {\n",
|
||||
"bottom: 0;\n",
|
||||
"left: 0;\n",
|
||||
"width: 50%;\n",
|
||||
"height: 50%;\n",
|
||||
"float: left;\n",
|
||||
"}\n",
|
||||
"#PV-BR {\n",
|
||||
"bottom: 0;\n",
|
||||
"right: 0;\n",
|
||||
"width: 50%;\n",
|
||||
"height: 50%;\n",
|
||||
"float: right;\n",
|
||||
"}\n",
|
||||
".PV-P {\n",
|
||||
"width: 100%;\n",
|
||||
"height: 100%;\n",
|
||||
"top: 0;\n",
|
||||
"position: absolute;\n",
|
||||
"display: none;\n",
|
||||
"}\n"])
|
||||
f.close()
|
||||
for (dirpath, dirnames, filenames) in walk(os.path.join(path, 'OEBPS', 'Images')):
|
||||
chapter = False
|
||||
dirnames, filenames = walkSort(dirnames, filenames)
|
||||
for afile in filenames:
|
||||
filename = getImageFileName(afile)
|
||||
if '-kcc-hq' not in filename[0]:
|
||||
if not filename[0].endswith('-hq'):
|
||||
filelist.append(buildHTML(dirpath, afile, os.path.join(dirpath, afile)))
|
||||
lastfile = (dirpath, afile, os.path.join(dirpath, afile))
|
||||
if not chapter:
|
||||
chapterlist.append((dirpath.replace('Images', 'Text'), filelist[-1][1]))
|
||||
chapter = True
|
||||
@@ -518,10 +466,6 @@ def buildEPUB(path, chapterNames, tomeNumber):
|
||||
cover = os.path.join(os.path.join(path, 'OEBPS', 'Images'),
|
||||
'cover' + getImageFileName(filelist[-1][1])[1])
|
||||
image.Cover(os.path.join(filelist[-1][0], filelist[-1][1]), cover, options, tomeNumber)
|
||||
# Hack that force Panel View on at last one page
|
||||
if lastfile and not options.panelviewused and 'Ko' not in options.profile \
|
||||
and options.profile not in ['K1', 'K2', 'KDX', 'Custom']:
|
||||
filelist[-1] = buildHTML(lastfile[0], lastfile[1], lastfile[2], True)
|
||||
# Overwrite chapternames if tree is flat and ComicInfo.xml has bookmarks
|
||||
if not chapterNames and options.chapters:
|
||||
chapterlist = []
|
||||
@@ -529,9 +473,9 @@ def buildEPUB(path, chapterNames, tomeNumber):
|
||||
for aChapter in options.chapters:
|
||||
pageid = aChapter[0]
|
||||
for x in range(0, pageid + globaldiff + 1):
|
||||
if '-aaa-kcc' in filelist[x][1]:
|
||||
if '-kcc-b' in filelist[x][1]:
|
||||
pageid += 1
|
||||
if '-bbb-kcc' in filelist[pageid][1]:
|
||||
if '-kcc-c' in filelist[pageid][1]:
|
||||
pageid -= 1
|
||||
filename = filelist[pageid][1]
|
||||
chapterlist.append((filelist[pageid][0].replace('Images', 'Text'), filename))
|
||||
@@ -542,21 +486,6 @@ def buildEPUB(path, chapterNames, tomeNumber):
|
||||
buildOPF(path, options.title, filelist, cover)
|
||||
|
||||
|
||||
def imgOptimization(img, opt):
|
||||
if not img.fill:
|
||||
img.getImageFill()
|
||||
if not opt.webtoon:
|
||||
img.cropWhiteSpace()
|
||||
if opt.cutpagenumbers and not opt.webtoon:
|
||||
img.cutPageNumber()
|
||||
img.autocontrastImage()
|
||||
img.resizeImage()
|
||||
if not img.second and opt.panelview:
|
||||
img.calculateBorder()
|
||||
if opt.forcepng and not opt.forcecolor:
|
||||
img.quantizeImage()
|
||||
|
||||
|
||||
def imgDirectoryProcessing(path):
|
||||
global workerPool, workerOutput
|
||||
workerPool = Pool()
|
||||
@@ -597,10 +526,8 @@ def imgFileProcessingTick(output):
|
||||
else:
|
||||
for page in output:
|
||||
if page is not None:
|
||||
if isinstance(page, str):
|
||||
options.imgPurgeIndex.append(page)
|
||||
else:
|
||||
options.imgIndex[page[0]] = page[1]
|
||||
options.imgIndex[page[0]] = page[1]
|
||||
options.imgPurgeIndex.append(page[2])
|
||||
if GUI:
|
||||
GUI.progressBarTick.emit('tick')
|
||||
if not GUI.conversionAlive:
|
||||
@@ -613,38 +540,18 @@ def imgFileProcessing(work):
|
||||
dirpath = work[1]
|
||||
opt = work[2]
|
||||
output = []
|
||||
img = image.ComicPage(os.path.join(dirpath, afile), opt)
|
||||
if opt.nosplitrotate:
|
||||
splitter = None
|
||||
else:
|
||||
splitter = img.splitPage(dirpath)
|
||||
if splitter is not None:
|
||||
img0 = image.ComicPage(splitter[0], opt)
|
||||
imgOptimization(img0, opt)
|
||||
if not img0.noHQ:
|
||||
output.append(img0.saveToDir(dirpath))
|
||||
img1 = image.ComicPage(splitter[1], opt)
|
||||
imgOptimization(img1, opt)
|
||||
if not img1.noHQ:
|
||||
output.append(img1.saveToDir(dirpath))
|
||||
output.extend([img.origFileName, img0.origFileName, img1.origFileName])
|
||||
if opt.quality == 2:
|
||||
output.extend([img0.origFileName, img1.origFileName])
|
||||
img0b = image.ComicPage(splitter[0], opt, img0)
|
||||
imgOptimization(img0b, opt)
|
||||
output.append(img0b.saveToDir(dirpath))
|
||||
img1b = image.ComicPage(splitter[1], opt, img1)
|
||||
imgOptimization(img1b, opt)
|
||||
output.append(img1b.saveToDir(dirpath))
|
||||
else:
|
||||
output.append(img.origFileName)
|
||||
imgOptimization(img, opt)
|
||||
if not img.noHQ:
|
||||
output.append(img.saveToDir(dirpath))
|
||||
if opt.quality == 2:
|
||||
img2 = image.ComicPage(os.path.join(dirpath, afile), opt, img)
|
||||
imgOptimization(img2, opt)
|
||||
output.append(img2.saveToDir(dirpath))
|
||||
workImg = image.ComicPageParser((dirpath, afile), opt)
|
||||
for i in workImg.payload:
|
||||
img = image.ComicPage(i[0], i[1], i[2], i[3], i[4], opt)
|
||||
if not opt.webtoon:
|
||||
img.cropWhiteSpace()
|
||||
if opt.cutpagenumbers and not opt.webtoon:
|
||||
img.cutPageNumber()
|
||||
img.autocontrastImage()
|
||||
img.resizeImage()
|
||||
if opt.forcepng and not opt.forcecolor:
|
||||
img.quantizeImage()
|
||||
output.append(img.saveToDir())
|
||||
return output
|
||||
except Exception:
|
||||
return str(sys.exc_info()[1])
|
||||
@@ -789,6 +696,12 @@ def getDirectorySize(start_path='.'):
|
||||
return total_size
|
||||
|
||||
|
||||
def getPanelViewSize(deviceres, size):
|
||||
x = int(deviceres[0] / 2 - size[0] / 2) / deviceres[0] * 100
|
||||
y = int(deviceres[1] / 2 - size[1] / 2) / deviceres[1] * 100
|
||||
return str(int(x)), str(int(y))
|
||||
|
||||
|
||||
def sanitizeTree(filetree):
|
||||
chapterNames = {}
|
||||
for root, dirs, files in walk(filetree, False):
|
||||
@@ -851,7 +764,7 @@ def splitDirectory(path):
|
||||
mode = 0
|
||||
else:
|
||||
if filesNumber > 0:
|
||||
print('\nWARNING: Automatic output splitting failed.')
|
||||
print('WARNING: Automatic output splitting failed.')
|
||||
if GUI:
|
||||
GUI.addMessage.emit('Automatic output splitting failed. <a href='
|
||||
'"https://github.com/ciromattia/kcc/wiki'
|
||||
@@ -866,7 +779,7 @@ def splitDirectory(path):
|
||||
if len(dirs) != 0:
|
||||
detectedSubSubdirectories = True
|
||||
elif len(dirs) == 0 and detectedSubSubdirectories:
|
||||
print('\nWARNING: Automatic output splitting failed.')
|
||||
print('WARNING: Automatic output splitting failed.')
|
||||
if GUI:
|
||||
GUI.addMessage.emit('Automatic output splitting failed. <a href='
|
||||
'"https://github.com/ciromattia/kcc/wiki'
|
||||
@@ -883,7 +796,7 @@ def splitDirectory(path):
|
||||
# One level of subdirectories
|
||||
mode = 1
|
||||
if detectedFilesInSubdirectories and detectedSubSubdirectories:
|
||||
print('\nWARNING: Automatic output splitting failed.')
|
||||
print('WARNING: Automatic output splitting failed.')
|
||||
if GUI:
|
||||
GUI.addMessage.emit('Automatic output splitting failed. <a href='
|
||||
'"https://github.com/ciromattia/kcc/wiki'
|
||||
@@ -992,43 +905,14 @@ def detectCorruption(tmpPath, orgPath):
|
||||
else:
|
||||
saferRemove(os.path.join(root, name))
|
||||
if imageSmaller > imageNumber * 0.25 and not options.upscale and not options.stretch:
|
||||
print("\nMore than 25% of images are smaller than target device resolution. "
|
||||
print("WARNING: More than 1/4 of images are smaller than target device resolution. "
|
||||
"Consider enabling stretching or upscaling to improve readability.")
|
||||
if GUI:
|
||||
GUI.addMessage.emit('More than 25% of images are smaller than target device resolution.', 'warning', False)
|
||||
GUI.addMessage.emit('More than 1/4 of images are smaller than target device resolution.', 'warning', False)
|
||||
GUI.addMessage.emit('Consider enabling stretching or upscaling to improve readability.', 'warning', False)
|
||||
GUI.addMessage.emit('', '', False)
|
||||
|
||||
|
||||
def detectMargins(path):
|
||||
if options.imgproc:
|
||||
for flag in options.imgIndex[path]:
|
||||
if "Margins-" in flag:
|
||||
flag = flag.split('-')
|
||||
xl = flag[1]
|
||||
yu = flag[2]
|
||||
xr = flag[3]
|
||||
yd = flag[4]
|
||||
if xl != "0.0":
|
||||
xl = "-" + xl + "%"
|
||||
else:
|
||||
xl = "0%"
|
||||
if xr != "0.0":
|
||||
xr = "-" + xr + "%"
|
||||
else:
|
||||
xr = "0%"
|
||||
if yu != "0.0":
|
||||
yu = "-" + yu + "%"
|
||||
else:
|
||||
yu = "0%"
|
||||
if yd != "0.0":
|
||||
yd = "-" + yd + "%"
|
||||
else:
|
||||
yd = "0%"
|
||||
return xl, yu, xr, yd
|
||||
return '0%', '0%', '0%', '0%'
|
||||
|
||||
|
||||
def createNewTome():
|
||||
tomePathRoot = mkdtemp('', 'KCC-')
|
||||
tomePath = os.path.join(tomePathRoot, 'OEBPS', 'Images')
|
||||
@@ -1058,7 +942,6 @@ def makeZIP(zipFilename, baseDir, isEPUB=False):
|
||||
|
||||
|
||||
def makeParser():
|
||||
"""Create and return an option parser set up with KCC options."""
|
||||
psr = OptionParser(usage="Usage: kcc-c2e [options] comic_file|comic_folder", add_help_option=False)
|
||||
|
||||
mainOptions = OptionGroup(psr, "MAIN")
|
||||
@@ -1070,10 +953,8 @@ def makeParser():
|
||||
mainOptions.add_option("-p", "--profile", action="store", dest="profile", default="KV",
|
||||
help="Device profile (Available options: K1, K2, K345, KDX, KPW, KV, KoMT, KoG, KoGHD,"
|
||||
" KoA, KoAHD, KoAH2O) [Default=KV]")
|
||||
mainOptions.add_option("-q", "--quality", type="int", dest="quality", default="0",
|
||||
help="Quality of Panel View. 0 - Normal 1 - High 2 - Ultra [Default=0]")
|
||||
mainOptions.add_option("-m", "--manga-style", action="store_true", dest="righttoleft", default=False,
|
||||
help="Manga style (Right-to-left reading and splitting)")
|
||||
help="Manga style (right-to-left reading and splitting)")
|
||||
mainOptions.add_option("-w", "--webtoon", action="store_true", dest="webtoon", default=False,
|
||||
help="Webtoon processing mode"),
|
||||
|
||||
@@ -1083,9 +964,19 @@ def makeParser():
|
||||
help="Comic title [Default=filename or directory name]")
|
||||
outputOptions.add_option("-f", "--format", action="store", dest="format", default="Auto",
|
||||
help="Output format (Available options: Auto, MOBI, EPUB, CBZ) [Default=Auto]")
|
||||
outputOptions.add_option("--batchsplit", action="store_true", dest="batchsplit", default=False,
|
||||
outputOptions.add_option("-b", "--batchsplit", action="store_true", dest="batchsplit", default=False,
|
||||
help="Split output into multiple files"),
|
||||
|
||||
processingOptions.add_option("-u", "--upscale", action="store_true", dest="upscale", default=False,
|
||||
help="Resize images smaller than device's resolution")
|
||||
processingOptions.add_option("-s", "--stretch", action="store_true", dest="stretch", default=False,
|
||||
help="Stretch images to device's resolution")
|
||||
processingOptions.add_option("-r", "--splitter", type="int", dest="splitter", default="0",
|
||||
help="Double page parsing mode. 0: Split 1: Rotate 2: Both [Default=0]")
|
||||
processingOptions.add_option("-g", "--gamma", type="float", dest="gamma", default="0.0",
|
||||
help="Apply gamma correction to linearize the image [Default=Auto]")
|
||||
processingOptions.add_option("--hq", action="store_true", dest="hqmode", default=False,
|
||||
help="Enable high quality Panel View")
|
||||
processingOptions.add_option("--blackborders", action="store_true", dest="black_borders", default=False,
|
||||
help="Disable autodetection and force black borders")
|
||||
processingOptions.add_option("--whiteborders", action="store_true", dest="white_borders", default=False,
|
||||
@@ -1094,20 +985,8 @@ def makeParser():
|
||||
help="Don't convert images to grayscale")
|
||||
processingOptions.add_option("--forcepng", action="store_true", dest="forcepng", default=False,
|
||||
help="Create PNG files instead JPEG")
|
||||
processingOptions.add_option("--gamma", type="float", dest="gamma", default="0.0",
|
||||
help="Apply gamma correction to linearize the image [Default=Auto]")
|
||||
processingOptions.add_option("--nocutpagenumbers", action="store_false", dest="cutpagenumbers", default=True,
|
||||
help="Don't try to cut page numbering on images")
|
||||
processingOptions.add_option("--noprocessing", action="store_false", dest="imgproc", default=True,
|
||||
help="Don't apply image preprocessing")
|
||||
processingOptions.add_option("--nosplitrotate", action="store_true", dest="nosplitrotate", default=False,
|
||||
help="Disable splitting and rotation")
|
||||
processingOptions.add_option("--rotate", action="store_true", dest="rotate", default=False,
|
||||
help="Rotate landscape pages instead of splitting them")
|
||||
processingOptions.add_option("--stretch", action="store_true", dest="stretch", default=False,
|
||||
help="Stretch images to device's resolution")
|
||||
processingOptions.add_option("--upscale", action="store_true", dest="upscale", default=False,
|
||||
help="Resize images smaller than device's resolution")
|
||||
help="Don't try to cut page numbers from images")
|
||||
|
||||
customProfileOptions.add_option("--customwidth", type="int", dest="customwidth", default=0,
|
||||
help="Replace screen width provided by device profile")
|
||||
@@ -1128,7 +1007,6 @@ def makeParser():
|
||||
def checkOptions():
|
||||
global options
|
||||
options.panelview = True
|
||||
options.panelviewused = False
|
||||
options.iskindle = False
|
||||
options.bordersColor = None
|
||||
if options.format == 'Auto':
|
||||
@@ -1138,7 +1016,7 @@ def checkOptions():
|
||||
options.format = 'EPUB'
|
||||
elif options.profile in ['KDX']:
|
||||
options.format = 'CBZ'
|
||||
if options.profile in ['K1', 'K2', 'K345', 'KPW', 'KV', 'OTHER']:
|
||||
if options.profile in ['K1', 'K2', 'K345', 'KPW', 'KV']:
|
||||
options.iskindle = True
|
||||
if options.white_borders:
|
||||
options.bordersColor = 'white'
|
||||
@@ -1149,28 +1027,24 @@ def checkOptions():
|
||||
options.batchsplit = True
|
||||
# Older Kindle don't need higher resolution files due lack of Panel View.
|
||||
if options.profile == 'K1' or options.profile == 'K2' or options.profile == 'KDX':
|
||||
options.quality = 0
|
||||
options.panelview = False
|
||||
options.hqmode = False
|
||||
# Webtoon mode mandatory options
|
||||
if options.webtoon:
|
||||
options.nosplitrotate = True
|
||||
options.quality = 0
|
||||
options.panelview = False
|
||||
options.hqmode = False
|
||||
options.righttoleft = False
|
||||
options.upscale = True
|
||||
# Disable all Kindle features for other e-readers
|
||||
if options.profile == 'OTHER':
|
||||
options.panelview = False
|
||||
options.quality = 0
|
||||
options.hqmode = False
|
||||
if 'Ko' in options.profile:
|
||||
options.panelview = False
|
||||
# Kobo models can't use ultra quality mode
|
||||
if options.quality == 2:
|
||||
options.quality = 1
|
||||
options.hqmode = False
|
||||
# CBZ files on Kindle DX/DXG support higher resolution
|
||||
if options.profile == 'KDX' and options.format == 'CBZ':
|
||||
options.customheight = 1200
|
||||
# Ultra mode don't work with CBZ format
|
||||
if options.quality == 2 and options.format == 'CBZ':
|
||||
options.quality = 1
|
||||
# Override profile data
|
||||
if options.customwidth != 0 or options.customheight != 0:
|
||||
X = image.ProfileData.Profiles[options.profile][1][0]
|
||||
@@ -1192,18 +1066,18 @@ def checkTools(source):
|
||||
rarExitCode = Popen('unrar', stdout=PIPE, stderr=STDOUT, shell=True)
|
||||
rarExitCode = rarExitCode.wait()
|
||||
if rarExitCode != 0 and rarExitCode != 7:
|
||||
print('\nUnRAR is missing!')
|
||||
print('ERROR: UnRAR is missing!')
|
||||
exit(1)
|
||||
elif source.endswith('.CB7') or source.endswith('.7Z'):
|
||||
sevenzaExitCode = Popen('7za', stdout=PIPE, stderr=STDOUT, shell=True)
|
||||
sevenzaExitCode = sevenzaExitCode.wait()
|
||||
if sevenzaExitCode != 0 and sevenzaExitCode != 7:
|
||||
print('\n7za is missing!')
|
||||
print('ERROR: 7za is missing!')
|
||||
exit(1)
|
||||
if options.format == 'MOBI':
|
||||
kindleGenExitCode = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, shell=True)
|
||||
if kindleGenExitCode.wait() != 0:
|
||||
print('\nKindleGen is missing!')
|
||||
print('ERROR: KindleGen is missing!')
|
||||
exit(1)
|
||||
|
||||
|
||||
@@ -1226,7 +1100,6 @@ def checkPre(source):
|
||||
|
||||
|
||||
def makeBook(source, qtGUI=None):
|
||||
"""Generates MOBI/EPUB/CBZ comic ebook from a bunch of images."""
|
||||
global GUI
|
||||
GUI = qtGUI
|
||||
if GUI:
|
||||
@@ -1234,17 +1107,21 @@ def makeBook(source, qtGUI=None):
|
||||
else:
|
||||
checkTools(source)
|
||||
checkPre(source)
|
||||
print("Preparing source images...")
|
||||
path = getWorkFolder(source)
|
||||
print("\nChecking images...")
|
||||
print("Checking images...")
|
||||
getComicInfo(os.path.join(path, "OEBPS", "Images"), source)
|
||||
detectCorruption(os.path.join(path, "OEBPS", "Images"), source)
|
||||
if options.webtoon:
|
||||
comic2panel.main(['-y ' + str(image.ProfileData.Profiles[options.profile][1][1]), '-i', '-m', path], qtGUI)
|
||||
if options.imgproc:
|
||||
print("\nProcessing images...")
|
||||
if GUI:
|
||||
GUI.progressBarTick.emit('Processing images')
|
||||
imgDirectoryProcessing(os.path.join(path, "OEBPS", "Images"))
|
||||
if image.ProfileData.Profiles[options.profile][1][1] > 1000:
|
||||
y = 1000
|
||||
else:
|
||||
y = image.ProfileData.Profiles[options.profile][1][1]
|
||||
comic2panel.main(['-y ' + str(y), '-i', '-m', path], qtGUI)
|
||||
print("Processing images...")
|
||||
if GUI:
|
||||
GUI.progressBarTick.emit('Processing images')
|
||||
imgDirectoryProcessing(os.path.join(path, "OEBPS", "Images"))
|
||||
if GUI:
|
||||
GUI.progressBarTick.emit('1')
|
||||
chapterNames = sanitizeTree(os.path.join(path, 'OEBPS', 'Images'))
|
||||
@@ -1272,14 +1149,14 @@ def makeBook(source, qtGUI=None):
|
||||
tomeNumber += 1
|
||||
options.title = options.baseTitle + ' [' + str(tomeNumber) + '/' + str(len(tomes)) + ']'
|
||||
if options.format == 'CBZ':
|
||||
print("\nCreating CBZ file...")
|
||||
print("Creating CBZ file...")
|
||||
if len(tomes) > 1:
|
||||
filepath.append(getOutputFilename(source, options.output, '.cbz', ' ' + str(tomeNumber)))
|
||||
else:
|
||||
filepath.append(getOutputFilename(source, options.output, '.cbz', ''))
|
||||
makeZIP(tome + '_comic', os.path.join(tome, "OEBPS", "Images"))
|
||||
else:
|
||||
print("\nCreating EPUB file...")
|
||||
print("Creating EPUB file...")
|
||||
buildEPUB(tome, chapterNames, tomeNumber)
|
||||
if len(tomes) > 1:
|
||||
filepath.append(getOutputFilename(source, options.output, '.epub', ' ' + str(tomeNumber)))
|
||||
@@ -1291,20 +1168,20 @@ def makeBook(source, qtGUI=None):
|
||||
if GUI:
|
||||
GUI.progressBarTick.emit('tick')
|
||||
if not GUI and options.format == 'MOBI':
|
||||
print("\nCreating MOBI file...")
|
||||
print("Creating MOBI file...")
|
||||
work = []
|
||||
for i in filepath:
|
||||
work.append([i])
|
||||
output = makeMOBI(work, GUI)
|
||||
for errors in output:
|
||||
if errors[0] != 0:
|
||||
print('KINDLEGEN ERROR!')
|
||||
print('Error: KindleGen failed to create MOBI!')
|
||||
print(errors)
|
||||
return filepath
|
||||
for i in filepath:
|
||||
output = makeMOBIFix(i)
|
||||
if not output[0]:
|
||||
print('DUALMETAFIX ERROR!')
|
||||
print('Error: Failed to tweak KindleGen output!')
|
||||
return filepath
|
||||
else:
|
||||
os.remove(i.replace('.epub', '.mobi') + '_toclean')
|
||||
|
||||
@@ -232,7 +232,7 @@ def main(argv=None, qtGUI=None):
|
||||
GUI = None
|
||||
if len(args) != 1:
|
||||
parser.print_help()
|
||||
return
|
||||
return 1
|
||||
if options.height > 0:
|
||||
options.sourceDir = args[0]
|
||||
options.targetDir = args[0] + "-Splitted"
|
||||
@@ -244,7 +244,7 @@ def main(argv=None, qtGUI=None):
|
||||
splitWorkerOutput = []
|
||||
splitWorkerPool = Pool()
|
||||
if options.merge:
|
||||
print("\nMerging images...")
|
||||
print("Merging images...")
|
||||
directoryNumer = 1
|
||||
mergeWork = []
|
||||
mergeWorkerOutput = []
|
||||
@@ -268,7 +268,7 @@ def main(argv=None, qtGUI=None):
|
||||
if len(mergeWorkerOutput) > 0:
|
||||
rmtree(options.targetDir, True)
|
||||
raise RuntimeError("One of workers crashed. Cause: " + mergeWorkerOutput[0])
|
||||
print("\nSplitting images...")
|
||||
print("Splitting images...")
|
||||
for root, dirs, files in walk(options.targetDir, False):
|
||||
for name in files:
|
||||
if getImageFileName(name) is not None:
|
||||
|
||||
409
kcc/image.py
409
kcc/image.py
@@ -20,7 +20,6 @@ import os
|
||||
from io import BytesIO
|
||||
from urllib.request import Request, urlopen
|
||||
from urllib.parse import quote
|
||||
from functools import reduce
|
||||
from PIL import Image, ImageOps, ImageStat, ImageChops
|
||||
from .shared import md5Checksum
|
||||
from . import __version__
|
||||
@@ -94,73 +93,167 @@ class ProfileData:
|
||||
}
|
||||
|
||||
|
||||
class ComicPage:
|
||||
def __init__(self, source, options, original=None):
|
||||
try:
|
||||
self.profile_label, self.size, self.palette, self.gamma, self.panelviewsize = options.profileData
|
||||
except KeyError:
|
||||
raise RuntimeError('Unexpected output device %s' % options.profileData)
|
||||
self.origFileName = source
|
||||
self.filename = os.path.basename(self.origFileName)
|
||||
self.image = Image.open(source)
|
||||
self.image = self.image.convert('RGB')
|
||||
class ComicPageParser:
|
||||
def __init__(self, source, options):
|
||||
self.opt = options
|
||||
if original:
|
||||
self.second = True
|
||||
self.rotated = original.rotated
|
||||
self.border = original.border
|
||||
self.noHPV = original.noHPV
|
||||
self.noVPV = original.noVPV
|
||||
self.noPV = original.noPV
|
||||
self.noHQ = original.noHQ
|
||||
self.fill = original.fill
|
||||
self.color = original.color
|
||||
if self.rotated:
|
||||
self.image = self.image.rotate(90, Image.BICUBIC, True)
|
||||
self.opt.quality = 0
|
||||
else:
|
||||
self.second = False
|
||||
self.rotated = None
|
||||
self.border = None
|
||||
self.noHPV = None
|
||||
self.noVPV = None
|
||||
self.noPV = None
|
||||
self.fill = None
|
||||
self.noHQ = False
|
||||
if options.webtoon:
|
||||
self.color = True
|
||||
else:
|
||||
self.color = self.isImageColor()
|
||||
self.source = source
|
||||
self.size = self.opt.profileData[1]
|
||||
self.payload = []
|
||||
self.image = Image.open(os.path.join(source[0], source[1])).convert('RGB')
|
||||
self.color = self.colorCheck()
|
||||
self.fill = self.fillCheck()
|
||||
self.splitCheck()
|
||||
if self.opt.hqmode:
|
||||
self.sizeCheck()
|
||||
|
||||
def saveToDir(self, targetdir):
|
||||
def getImageHistogram(self, image):
|
||||
histogram = image.histogram()
|
||||
if histogram[0] == 0:
|
||||
return -1
|
||||
elif histogram[255] == 0:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
def splitCheck(self):
|
||||
width, height = self.image.size
|
||||
dstwidth, dstheight = self.size
|
||||
# Only split if origin is not oriented the same as target
|
||||
if (width > height) != (dstwidth > dstheight) and not self.opt.webtoon:
|
||||
if self.opt.splitter != 1:
|
||||
if width > height:
|
||||
# Source is landscape, so split by the width
|
||||
leftbox = (0, 0, int(width / 2), height)
|
||||
rightbox = (int(width / 2), 0, width, height)
|
||||
else:
|
||||
# Source is portrait and target is landscape, so split by the height
|
||||
leftbox = (0, 0, width, int(height / 2))
|
||||
rightbox = (0, int(height / 2), width, height)
|
||||
if self.opt.righttoleft:
|
||||
pageone = self.image.crop(rightbox)
|
||||
pagetwo = self.image.crop(leftbox)
|
||||
else:
|
||||
pageone = self.image.crop(leftbox)
|
||||
pagetwo = self.image.crop(rightbox)
|
||||
self.payload.append(['S1', self.source, pageone, self.color, self.fill])
|
||||
self.payload.append(['S2', self.source, pagetwo, self.color, self.fill])
|
||||
if self.opt.splitter > 0:
|
||||
self.payload.append(['R', self.source, self.image.rotate(90, Image.BICUBIC, True),
|
||||
self.color, self.fill])
|
||||
else:
|
||||
self.payload.append(['N', self.source, self.image, self.color, self.fill])
|
||||
|
||||
def colorCheck(self):
|
||||
if self.opt.webtoon:
|
||||
return True
|
||||
else:
|
||||
img = self.image.copy()
|
||||
bands = img.getbands()
|
||||
if bands == ('R', 'G', 'B') or bands == ('R', 'G', 'B', 'A'):
|
||||
thumb = img.resize((40, 40))
|
||||
SSE, bias = 0, [0, 0, 0]
|
||||
bias = ImageStat.Stat(thumb).mean[:3]
|
||||
bias = [b - sum(bias) / 3 for b in bias]
|
||||
for pixel in thumb.getdata():
|
||||
mu = sum(pixel) / 3
|
||||
SSE += sum((pixel[i] - mu - bias[i]) * (pixel[i] - mu - bias[i]) for i in [0, 1, 2])
|
||||
MSE = float(SSE) / (40 * 40)
|
||||
if MSE > 22:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
def fillCheck(self):
|
||||
if self.opt.bordersColor:
|
||||
return self.opt.bordersColor
|
||||
else:
|
||||
bw = self.image.convert('L').point(lambda x: 0 if x < 128 else 255, '1')
|
||||
imageBoxA = bw.getbbox()
|
||||
imageBoxB = ImageChops.invert(bw).getbbox()
|
||||
if imageBoxA is None or imageBoxB is None:
|
||||
surfaceB, surfaceW = 0, 0
|
||||
diff = 0
|
||||
else:
|
||||
surfaceB = (imageBoxA[2] - imageBoxA[0]) * (imageBoxA[3] - imageBoxA[1])
|
||||
surfaceW = (imageBoxB[2] - imageBoxB[0]) * (imageBoxB[3] - imageBoxB[1])
|
||||
diff = ((max(surfaceB, surfaceW) - min(surfaceB, surfaceW)) / min(surfaceB, surfaceW)) * 100
|
||||
if diff > 0.5:
|
||||
if surfaceW < surfaceB:
|
||||
return 'white'
|
||||
elif surfaceW > surfaceB:
|
||||
return 'black'
|
||||
else:
|
||||
fill = 0
|
||||
startY = 0
|
||||
while startY < bw.size[1]:
|
||||
if startY + 5 > bw.size[1]:
|
||||
startY = bw.size[1] - 5
|
||||
fill += self.getImageHistogram(bw.crop((0, startY, bw.size[0], startY + 5)))
|
||||
startY += 5
|
||||
startX = 0
|
||||
while startX < bw.size[0]:
|
||||
if startX + 5 > bw.size[0]:
|
||||
startX = bw.size[0] - 5
|
||||
fill += self.getImageHistogram(bw.crop((startX, 0, startX + 5, bw.size[1])))
|
||||
startX += 5
|
||||
if fill > 0:
|
||||
return 'black'
|
||||
else:
|
||||
return 'white'
|
||||
|
||||
def sizeCheck(self):
|
||||
additionalPayload = []
|
||||
width, height = self.image.size
|
||||
dstwidth, dstheight = self.size
|
||||
for work in self.payload:
|
||||
if width > dstwidth and height > dstheight:
|
||||
additionalPayload.append([work[0] + '+', work[1], work[2].copy(), work[3], work[4]])
|
||||
self.payload = self.payload + additionalPayload
|
||||
|
||||
|
||||
class ComicPage:
|
||||
def __init__(self, mode, path, image, color, fill, options):
|
||||
self.opt = options
|
||||
_, self.size, self.palette, self.gamma, self.panelviewsize = self.opt.profileData
|
||||
self.image = image
|
||||
self.color = color
|
||||
self.fill = fill
|
||||
self.rotated = False
|
||||
self.orgPath = os.path.join(path[0], path[1])
|
||||
if '+' in mode:
|
||||
self.hqMode = True
|
||||
else:
|
||||
self.hqMode = False
|
||||
if 'N' in mode:
|
||||
self.targetPath = os.path.join(path[0], os.path.splitext(path[1])[0]) + '-KCC'
|
||||
elif 'R' in mode:
|
||||
self.targetPath = os.path.join(path[0], os.path.splitext(path[1])[0]) + '-KCC-A'
|
||||
self.rotated = True
|
||||
elif 'S1' in mode:
|
||||
self.targetPath = os.path.join(path[0], os.path.splitext(path[1])[0]) + '-KCC-B'
|
||||
elif 'S2' in mode:
|
||||
self.targetPath = os.path.join(path[0], os.path.splitext(path[1])[0]) + '-KCC-C'
|
||||
|
||||
def saveToDir(self):
|
||||
try:
|
||||
flags = []
|
||||
filename = os.path.join(targetdir, os.path.splitext(self.filename)[0]) + '-KCC'
|
||||
if not self.opt.forcecolor and not self.opt.forcepng:
|
||||
self.image = self.image.convert('L')
|
||||
if self.rotated:
|
||||
flags.append('Rotated')
|
||||
if self.noPV:
|
||||
flags.append('NoPanelView')
|
||||
else:
|
||||
if self.noHPV:
|
||||
flags.append('NoHorizontalPanelView')
|
||||
if self.noVPV:
|
||||
flags.append('NoVerticalPanelView')
|
||||
if self.border:
|
||||
flags.append('Margins-' + str(self.border[0]) + '-' + str(self.border[1]) + '-' +
|
||||
str(self.border[2]) + '-' + str(self.border[3]))
|
||||
if self.fill != 'white':
|
||||
flags.append('BlackFill')
|
||||
if self.opt.quality == 2:
|
||||
filename += '-HQ'
|
||||
if self.hqMode:
|
||||
self.targetPath += '-HQ'
|
||||
if self.opt.forcepng:
|
||||
filename += '.png'
|
||||
self.image.save(filename, 'PNG', optimize=1)
|
||||
self.targetPath += '.png'
|
||||
self.image.save(self.targetPath, 'PNG', optimize=1)
|
||||
else:
|
||||
filename += '.jpg'
|
||||
self.image.save(filename, 'JPEG', optimize=1, quality=80)
|
||||
return [md5Checksum(filename), flags]
|
||||
self.targetPath += '.jpg'
|
||||
self.image.save(self.targetPath, 'JPEG', optimize=1, quality=80)
|
||||
return [md5Checksum(self.targetPath), flags, self.orgPath]
|
||||
except IOError as e:
|
||||
raise RuntimeError('Cannot write image in directory %s: %s' % (targetdir, e))
|
||||
|
||||
@@ -186,127 +279,42 @@ class ComicPage:
|
||||
# Quantize is deprecated but new function call it internally anyway...
|
||||
self.image = self.image.quantize(palette=palImg)
|
||||
|
||||
def calculateBorder(self):
|
||||
if self.noPV:
|
||||
self.border = [0.0, 0.0, 0.0, 0.0]
|
||||
return
|
||||
if self.fill == 'white':
|
||||
border = ImageChops.invert(self.image).getbbox()
|
||||
else:
|
||||
border = self.image.getbbox()
|
||||
if self.opt.quality == 2:
|
||||
multiplier = 1.0
|
||||
else:
|
||||
multiplier = 1.5
|
||||
if border is not None:
|
||||
self.border = [round(float(border[0]) / float(self.image.size[0]) * 150, 3),
|
||||
round(float(border[1]) / float(self.image.size[1]) * 150, 3),
|
||||
round(float(self.image.size[0] - border[2]) / float(self.image.size[0]) * 150, 3),
|
||||
round(float(self.image.size[1] - border[3]) / float(self.image.size[1]) * 150, 3)]
|
||||
if int((border[2] - border[0]) * multiplier) < self.size[0] + 10:
|
||||
self.noHPV = True
|
||||
if int((border[3] - border[1]) * multiplier) < self.size[1] + 10:
|
||||
self.noVPV = True
|
||||
else:
|
||||
self.border = [0.0, 0.0, 0.0, 0.0]
|
||||
self.noHPV = True
|
||||
self.noVPV = True
|
||||
|
||||
def resizeImage(self):
|
||||
if self.opt.bordersColor:
|
||||
fill = self.opt.bordersColor
|
||||
else:
|
||||
fill = self.fill
|
||||
# Set target size
|
||||
if self.opt.quality == 0:
|
||||
size = (self.size[0], self.size[1])
|
||||
elif self.opt.quality == 1 and not self.opt.stretch and not self.opt.upscale and self.image.size[0] <=\
|
||||
self.size[0] and self.image.size[1] <= self.size[1]:
|
||||
size = (self.size[0], self.size[1])
|
||||
elif self.opt.quality == 1:
|
||||
# Forcing upscale to make sure that margins will be not too big
|
||||
if not self.opt.stretch:
|
||||
self.opt.upscale = True
|
||||
if self.hqMode:
|
||||
size = (self.panelviewsize[0], self.panelviewsize[1])
|
||||
elif self.opt.quality == 2 and not self.opt.stretch and not self.opt.upscale and self.image.size[0] <=\
|
||||
self.size[0] and self.image.size[1] <= self.size[1]:
|
||||
# HQ version will not be needed
|
||||
self.noHQ = True
|
||||
return
|
||||
if self.image.size[0] > size[0] or self.image.size[1] > size[1]:
|
||||
self.image.thumbnail(size, Image.LANCZOS)
|
||||
else:
|
||||
size = (self.panelviewsize[0], self.panelviewsize[1])
|
||||
# If stretching is on - Resize without other considerations
|
||||
if self.opt.stretch:
|
||||
size = (self.size[0], self.size[1])
|
||||
if self.image.size[0] <= size[0] and self.image.size[1] <= size[1]:
|
||||
method = Image.BICUBIC
|
||||
else:
|
||||
method = Image.LANCZOS
|
||||
self.image = self.image.resize(size, method)
|
||||
return
|
||||
# If image is smaller than target resolution and upscale is off - Just expand it by adding margins
|
||||
if self.image.size[0] <= size[0] and self.image.size[1] <= size[1] and not self.opt.upscale:
|
||||
borderw = int((size[0] - self.image.size[0]) / 2)
|
||||
borderh = int((size[1] - self.image.size[1]) / 2)
|
||||
# PV is disabled when source image is smaller than device screen and upscale is off
|
||||
if self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1]:
|
||||
self.noPV = True
|
||||
self.image = ImageOps.expand(self.image, border=(borderw, borderh), fill=fill)
|
||||
# Border can't be float so sometimes image might be 1px too small/large
|
||||
if self.image.size[0] != size[0] or self.image.size[1] != size[1]:
|
||||
self.image = ImageOps.fit(self.image, size, method=Image.BICUBIC, centering=(0.5, 0.5))
|
||||
return
|
||||
# Otherwise - Upscale/Downscale
|
||||
ratioDev = float(size[0]) / float(size[1])
|
||||
if (float(self.image.size[0]) / float(self.image.size[1])) < ratioDev:
|
||||
diff = int(self.image.size[1] * ratioDev) - self.image.size[0]
|
||||
self.image = ImageOps.expand(self.image, border=(int(diff / 2), 0), fill=fill)
|
||||
elif (float(self.image.size[0]) / float(self.image.size[1])) > ratioDev:
|
||||
diff = int(self.image.size[0] / ratioDev) - self.image.size[1]
|
||||
self.image = ImageOps.expand(self.image, border=(0, int(diff / 2)), fill=fill)
|
||||
if self.image.size[0] <= size[0] and self.image.size[1] <= size[1]:
|
||||
method = Image.BICUBIC
|
||||
else:
|
||||
method = Image.LANCZOS
|
||||
self.image = ImageOps.fit(self.image, size, method=method, centering=(0.5, 0.5))
|
||||
return
|
||||
|
||||
def splitPage(self, targetdir):
|
||||
width, height = self.image.size
|
||||
dstwidth, dstheight = self.size
|
||||
# Only split if origin is not oriented the same as target
|
||||
if (width > height) != (dstwidth > dstheight):
|
||||
if self.opt.rotate:
|
||||
self.image = self.image.rotate(90, Image.BICUBIC, True)
|
||||
self.rotated = True
|
||||
return None
|
||||
if self.opt.stretch:
|
||||
self.image = self.image.resize(size, method)
|
||||
elif self.image.size[0] <= size[0] and self.image.size[1] <= size[1] and not self.opt.upscale:
|
||||
if self.opt.format == 'CBZ':
|
||||
borderw = int((size[0] - self.image.size[0]) / 2)
|
||||
borderh = int((size[1] - self.image.size[1]) / 2)
|
||||
self.image = ImageOps.expand(self.image, border=(borderw, borderh), fill=self.fill)
|
||||
if self.image.size[0] != size[0] or self.image.size[1] != size[1]:
|
||||
self.image = ImageOps.fit(self.image, size, method=Image.BICUBIC, centering=(0.5, 0.5))
|
||||
else:
|
||||
self.rotated = False
|
||||
if width > height:
|
||||
# Source is landscape, so split by the width
|
||||
leftbox = (0, 0, int(width / 2), height)
|
||||
rightbox = (int(width / 2), 0, width, height)
|
||||
if self.opt.format == 'CBZ':
|
||||
ratioDev = float(size[0]) / float(size[1])
|
||||
if (float(self.image.size[0]) / float(self.image.size[1])) < ratioDev:
|
||||
diff = int(self.image.size[1] * ratioDev) - self.image.size[0]
|
||||
self.image = ImageOps.expand(self.image, border=(int(diff / 2), 0), fill=self.fill)
|
||||
elif (float(self.image.size[0]) / float(self.image.size[1])) > ratioDev:
|
||||
diff = int(self.image.size[0] / ratioDev) - self.image.size[1]
|
||||
self.image = ImageOps.expand(self.image, border=(0, int(diff / 2)), fill=self.fill)
|
||||
self.image = ImageOps.fit(self.image, size, method=method, centering=(0.5, 0.5))
|
||||
else:
|
||||
# Source is portrait and target is landscape, so split by the height
|
||||
leftbox = (0, 0, width, int(height / 2))
|
||||
rightbox = (0, int(height / 2), width, height)
|
||||
filename = os.path.splitext(self.filename)[0]
|
||||
fileone = targetdir + '/' + filename + '-AAA.png'
|
||||
filetwo = targetdir + '/' + filename + '-BBB.png'
|
||||
try:
|
||||
if self.opt.righttoleft:
|
||||
pageone = self.image.crop(rightbox)
|
||||
pagetwo = self.image.crop(leftbox)
|
||||
else:
|
||||
pageone = self.image.crop(leftbox)
|
||||
pagetwo = self.image.crop(rightbox)
|
||||
pageone.save(fileone, 'PNG', optimize=1)
|
||||
pagetwo.save(filetwo, 'PNG', optimize=1)
|
||||
except IOError as e:
|
||||
raise RuntimeError('Cannot write image in directory %s: %s' % (targetdir, e))
|
||||
return fileone, filetwo
|
||||
else:
|
||||
self.rotated = False
|
||||
return None
|
||||
hpercent = size[1] / float(self.image.size[1])
|
||||
wsize = int((float(self.image.size[0]) * float(hpercent)))
|
||||
self.image = self.image.resize((wsize, size[1]), method)
|
||||
if self.image.size[0] > size[0] or self.image.size[1] > size[1]:
|
||||
self.image.thumbnail(size, Image.LANCZOS)
|
||||
|
||||
def cutPageNumber(self):
|
||||
if ImageChops.invert(self.image).getbbox() is not None:
|
||||
@@ -397,71 +405,6 @@ class ComicPage:
|
||||
diff -= delta
|
||||
self.image = self.image.crop((0, 0, widthImg - diff, heightImg))
|
||||
|
||||
def getImageHistogram(self, image):
|
||||
histogram = image.histogram()
|
||||
if histogram[0] == 0:
|
||||
return -1
|
||||
elif histogram[255] == 0:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
def getImageFill(self):
|
||||
bw = self.image.convert('L').point(lambda x: 0 if x < 128 else 255, '1')
|
||||
imageBoxA = bw.getbbox()
|
||||
imageBoxB = ImageChops.invert(bw).getbbox()
|
||||
if imageBoxA is None or imageBoxB is None:
|
||||
surfaceB, surfaceW = 0, 0
|
||||
diff = 0
|
||||
else:
|
||||
surfaceB = (imageBoxA[2] - imageBoxA[0]) * (imageBoxA[3] - imageBoxA[1])
|
||||
surfaceW = (imageBoxB[2] - imageBoxB[0]) * (imageBoxB[3] - imageBoxB[1])
|
||||
diff = ((max(surfaceB, surfaceW) - min(surfaceB, surfaceW)) / min(surfaceB, surfaceW)) * 100
|
||||
if diff > 0.5:
|
||||
if surfaceW < surfaceB:
|
||||
self.fill = 'white'
|
||||
elif surfaceW > surfaceB:
|
||||
self.fill = 'black'
|
||||
else:
|
||||
fill = 0
|
||||
startY = 0
|
||||
while startY < bw.size[1]:
|
||||
if startY + 5 > bw.size[1]:
|
||||
startY = bw.size[1] - 5
|
||||
fill += self.getImageHistogram(bw.crop((0, startY, bw.size[0], startY + 5)))
|
||||
startY += 5
|
||||
startX = 0
|
||||
while startX < bw.size[0]:
|
||||
if startX + 5 > bw.size[0]:
|
||||
startX = bw.size[0] - 5
|
||||
fill += self.getImageHistogram(bw.crop((startX, 0, startX + 5, bw.size[1])))
|
||||
startX += 5
|
||||
if fill > 0:
|
||||
self.fill = 'black'
|
||||
else:
|
||||
self.fill = 'white'
|
||||
|
||||
def isImageColor(self):
|
||||
img = self.image.copy()
|
||||
bands = img.getbands()
|
||||
if bands == ('R', 'G', 'B') or bands == ('R', 'G', 'B', 'A'):
|
||||
thumb = img.resize((40, 40))
|
||||
SSE, bias = 0, [0, 0, 0]
|
||||
bias = ImageStat.Stat(thumb).mean[:3]
|
||||
bias = [b - sum(bias) / 3 for b in bias]
|
||||
for pixel in thumb.getdata():
|
||||
mu = sum(pixel) / 3
|
||||
SSE += sum((pixel[i] - mu - bias[i]) * (pixel[i] - mu - bias[i]) for i in [0, 1, 2])
|
||||
MSE = float(SSE) / (40 * 40)
|
||||
if MSE <= 22:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
elif len(bands) == 1:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class Cover:
|
||||
def __init__(self, source, target, opt, tomeNumber):
|
||||
|
||||
Reference in New Issue
Block a user