diff --git a/README.md b/README.md
index 908d5f9..32b25d5 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,8 @@ It also optimizes comic images by:
- flat folders
- PDF *(extracting only contained JPG images)*
-For now the script does not understand folder depth, so it will work on flat folders/archives only.
+~~For now the script does not understand folder depth, so it will work on flat folders/archives only.~~
+As of v. 1.50, KCC supports subfolders!
## REQUIREMENTS
- `kindlegen` in /usr/local/bin/
@@ -90,6 +91,7 @@ and installed in `/usr/local/bin/`
- 1.40 - Added some options for controlling image optimization
Further optimization (ImageOps, page numbering cut, autocontrast)
- 1.41 - Fixed a serious bug on resizing when img ratio was bigger than device one
+ - 1.50 - Added subfolder support for multiple chapters.
- 2.00 - GUI! AppleScript is gone and Tk is used to provide cross-platform GUI support.
## TODO
diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py
index 0220d67..8d4d428 100755
--- a/kcc/comic2ebook.py
+++ b/kcc/comic2ebook.py
@@ -29,6 +29,7 @@
# 1.40 - Added some options for controlling image optimization
# Further optimization (ImageOps, page numbering cut, autocontrast)
# 1.41 - Fixed a serious bug on resizing when img ratio was bigger than device one
+# 1.50 - Support for subfolders
#
# Todo:
# - Add gracefully exit for CBR if no rarfile.py and no unrar
@@ -36,85 +37,84 @@
# - Improve error reporting
# - recurse into dirtree for multiple comics
-__version__ = '1.30'
+__version__ = '1.50'
import os
import sys
from optparse import OptionParser
import image, cbxarchive, pdfjpgextract
-class HTMLbuilder:
-
- def getResult(self):
- return getImageFileName(self.file)
-
- def __init__(self, dstdir, file):
- self.file = file
- filename = getImageFileName(file)
- if filename is not None:
- htmlfile = dstdir + '/' + filename[0] + '.html'
- f = open(htmlfile, "w")
- f.writelines(["\n",
- "\n",
- "
\n",
- "",filename[0],"\n",
- "\n",
- "\n",
- "\n",
- "\n",
- "\n",
- ""
- ])
- f.close()
- return
-
-class NCXbuilder:
- def __init__(self, dstdir, title):
- ncxfile = dstdir + '/content.ncx'
- f = open(ncxfile, "w")
- f.writelines(["\n",
- "\n",
- "\n",
- "\n\n",
- "",title,"\n",
- "\n"
- ])
+def buildHTML(path,file):
+ filename = getImageFileName(file)
+ if filename is not None:
+ htmlfile = os.path.join(path,filename[0] + '.html')
+ f = open(htmlfile, "w")
+ f.writelines(["\n",
+ "\n",
+ "\n",
+ "",filename[0],"\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ ""
+ ])
f.close()
- return
+ return path,file
-class OPFBuilder:
- def __init__(self, profile, dstdir, title, filelist):
- opffile = dstdir + '/content.opf'
- # read the first file resolution
- profilelabel, deviceres, palette = image.ProfileData.Profiles[profile]
- imgres = str(deviceres[0]) + "x" + str(deviceres[1])
- f = open(opffile, "w")
- f.writelines(["\n",
- "\n",
- "\n",
- "",title,"\n",
- "en-US\n",
- "\n",
- "\n",
- "\n",
- "\n",
- "\n",
- "\n",
- "- \n"])
- for filename in filelist:
- f.write("
- \n")
- for filename in filelist:
- if '.png' == filename[1]:
- mt = 'image/png'
- else:
- mt = 'image/jpeg'
- f.write("
\n")
- f.write(" \n\n")
- for filename in filelist:
- f.write("\n")
- f.write("\n\n\n\n")
- f.close()
- return
+def buildNCX(dstdir, title):
+ ncxfile = dstdir + '/toc.ncx'
+ f = open(ncxfile, "w")
+ f.writelines(["\n",
+ "\n",
+ "\n",
+ "\n\n",
+ "",title,"\n",
+ "\n"
+ ])
+ f.close()
+ return
+
+def buildOPF(profile, dstdir, title, filelist):
+ opffile = dstdir + '/content.opf'
+ # read the first file resolution
+ profilelabel, deviceres, palette = image.ProfileData.Profiles[profile]
+ imgres = str(deviceres[0]) + "x" + str(deviceres[1])
+ f = open(opffile, "w")
+ f.writelines(["\n",
+ "\n",
+ "\n",
+ "",title,"\n",
+ "en-US\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "- \n"])
+ for path in filelist:
+ folder = path[0].replace(dstdir,'').lstrip('/')
+ filename = getImageFileName(path[1])
+ uniqueid = os.path.join(folder,filename[0]).replace('/','_')
+ f.write("
- \n")
+ #for filename in filelist:
+ if '.png' == filename[1]:
+ mt = 'image/png'
+ else:
+ mt = 'image/jpeg'
+ f.write("
\n")
+ f.write(" \n\n")
+ for path in filelist:
+ folder = path[0].replace(dstdir,'').lstrip('/')
+ filename = getImageFileName(path[1])
+ uniqueid = os.path.join(folder,filename[0]).replace('/','_')
+ f.write("\n")
+ f.write("\n\n\n\n")
+ f.close()
+ return
def getImageFileName(file):
filename = os.path.splitext(file)
@@ -130,6 +130,78 @@ def isInFilelist(file,list):
seen = True
return seen
+def applyImgOptimization(img):
+ img.optimizeImage()
+ img.cropWhiteSpace(10.0)
+ if options.cutpagenumbers:
+ img.cutPageNumber()
+ img.resizeImage(options.upscale,options.stretch)
+ img.quantizeImage()
+
+
+def dirImgProcess(path):
+ global options
+
+ for (dirpath, dirnames, filenames) in os.walk(path):
+ for file in filenames:
+ if getImageFileName(file) is not None:
+ if options.verbose:
+ print "Optimizing " + file + " for " + options.profile
+ else:
+ print ".",
+ img = image.ComicPage(os.path.join(dirpath,file), options.profile)
+ split = img.splitPage(dirpath, options.righttoleft)
+ if split is not None:
+ print "Splitted " + file
+ img0 = image.ComicPage(split[0],options.profile)
+ img1 = image.ComicPage(split[1],options.profile)
+ applyImgOptimization(img0)
+ img0.saveToDir(dirpath)
+ applyImgOptimization(img1)
+ img1.saveToDir(dirpath)
+ else:
+ applyImgOptimization(img)
+ img.saveToDir(dirpath)
+
+def genEpubStruct(path):
+ global options
+ filelist = []
+ for (dirpath, dirnames, filenames) in os.walk(path):
+ for file in filenames:
+ if getImageFileName(file) is not None:
+ # put credits at the end
+ if "credits" in file.lower():
+ os.rename(os.path.join(dirpath,file), os.path.join(dirpath,'ZZZ999_'+file))
+ file = 'ZZZ999_'+file
+ filelist.append(buildHTML(dirpath,file))
+ #filelist.extend(filenames)
+ if options.title == 'defaulttitle':
+ options.title = os.path.basename(path)
+ buildNCX(path,options.title)
+ # ensure we're sorting files alphabetically
+ filelist = sorted(filelist, key=lambda name: (name[0].lower(), name[1].lower()))
+ buildOPF(options.profile,path,options.title,filelist)
+
+def getWorkFolder(file):
+ fname = os.path.splitext(file)
+ if fname[1].lower() == '.pdf':
+ pdf = pdfjpgextract.PdfJpgExtract(file)
+ pdf.extract()
+ return pdf.getPath()
+ else:
+ cbx = cbxarchive.CBxArchive(file)
+ if cbx.isCbxFile():
+ cbx.extract()
+ return cbx.getPath()
+ else:
+ try:
+ import shutil
+ if not os.path.isdir(file + "_orig"):
+ shutil.copytree(file, file + "_orig")
+ return file
+ except OSError:
+ raise
+
def Copyright():
print ('comic2ebook v%(__version__)s. '
'Written 2012 by Ciro Mattia Gonano.' % globals())
@@ -140,7 +212,7 @@ def Usage():
parser.print_help()
def main(argv=None):
- global parser
+ global parser, options
usage = "Usage: %prog [options] comic_file|comic_folder"
parser = OptionParser(usage=usage, version=__version__)
parser.add_option("-p", "--profile", action="store", dest="profile", default="KHD",
@@ -163,68 +235,14 @@ def main(argv=None):
if len(args) != 1:
parser.print_help()
return
- dir = args[0]
- fname = os.path.splitext(dir)
- if fname[1].lower() == '.pdf':
- pdf = pdfjpgextract.PdfJpgExtract(dir)
- pdf.extract()
- dir = pdf.getPath()
- else:
- cbx = cbxarchive.CBxArchive(dir)
- if cbx.isCbxFile():
- cbx.extract()
- dir = cbx.getPath()
- else:
- try:
- import shutil
- shutil.copytree(dir, dir + "_orig")
- #dir = dir + "_orig"
- except OSError as exc:
- raise
- filelist = []
+ path = args[0]
+ path = getWorkFolder(path)
if options.imgproc:
print "Processing images..."
- try:
- if options.verbose:
- print "Splitting double pages..."
- for file in os.listdir(dir):
- if getImageFileName(file) is not None:
- print ".",
- img = image.ComicPage(dir+'/'+file, options.profile)
- img.splitPage(dir, options.righttoleft)
- for file in os.listdir(dir):
- if getImageFileName(file) is not None:
- if options.verbose:
- print "Optimizing " + file + " for " + options.profile
- else:
- print ".",
- img = image.ComicPage(dir+'/'+file, options.profile)
- img.optimizeImage()
- img.cropWhiteSpace(10.0)
- if options.cutpagenumbers:
- img.cutPageNumber()
- img.resizeImage(options.upscale,options.stretch)
- img.quantizeImage()
- img.saveToDir(dir)
- except ImportError:
- print "Could not load PIL, not optimizing image"
+ dirImgProcess(path)
print "Creating ePub structure..."
- for file in os.listdir(dir):
- if getImageFileName(file) is not None and isInFilelist(file,filelist) == False:
- # put credits at the end
- if "credits" in file.lower():
- os.rename(dir+'/'+file, dir+'/ZZZ999_'+file)
- file = 'ZZZ999_'+file
- filename = HTMLbuilder(dir,file).getResult()
- if filename is not None:
- filelist.append(filename)
- if options.title == 'defaulttitle':
- options.title = os.path.basename(dir)
- NCXbuilder(dir,options.title)
- # ensure we're sorting files alphabetically
- filelist = sorted(filelist, key=lambda name: name[0].lower())
- OPFBuilder(options.profile,dir,options.title,filelist)
+ genEpubStruct(path)
if __name__ == "__main__":
diff --git a/kcc/image.py b/kcc/image.py
index c7406bd..9a889a8 100755
--- a/kcc/image.py
+++ b/kcc/image.py
@@ -118,6 +118,12 @@ class ComicPage:
method = Image.ANTIALIAS
if self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1]:
if not upscale:
+ # do not upscale but center image in a device-sized image
+ newImage = Image.new('RGB', (self.size[0], self.size[1]), (255,255,255))
+ newImage.paste(self.image, (
+ (self.size[0] - self.image.size[0]) / 2,
+ (self.size[1] - self.image.size[1]) / 2))
+ self.image = newImage
return self.image
else:
method = Image.NEAREST
@@ -170,7 +176,8 @@ class ComicPage:
except IOError as e:
raise RuntimeError('Cannot write image in directory %s: %s' %(targetdir,e))
return fileone,filetwo
- return None
+ else:
+ return None
def frameImage(self):
foreground = tuple(self.palette[:3])