diff --git a/KindleComicConverter.app/Contents/Resources/cbxarchive.py b/KindleComicConverter.app/Contents/Resources/cbxarchive.py index c852bba..6b0e7ea 100644 --- a/KindleComicConverter.app/Contents/Resources/cbxarchive.py +++ b/KindleComicConverter.app/Contents/Resources/cbxarchive.py @@ -14,7 +14,9 @@ # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. # -__version__ = '1.0' +__license__ = 'ISC' +__copyright__ = '2012-2013, Ciro Mattia Gonano ' +__docformat__ = 'restructuredtext en' import os diff --git a/KindleComicConverter.app/Contents/Resources/comic2ebook.py b/KindleComicConverter.app/Contents/Resources/comic2ebook.py index 0220d67..0ccd616 100755 --- a/KindleComicConverter.app/Contents/Resources/comic2ebook.py +++ b/KindleComicConverter.app/Contents/Resources/comic2ebook.py @@ -29,92 +29,111 @@ # 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 # executable are found # - Improve error reporting -# - recurse into dirtree for multiple comics -__version__ = '1.30' +__version__ = '1.50' +__license__ = 'ISC' +__copyright__ = '2012-2013, Ciro Mattia Gonano ' +__docformat__ = 'restructuredtext en' 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, chapters): + ncxfile = dstdir + '/toc.ncx' + f = open(ncxfile, "w") + f.writelines(["\n", + "\n", + "\n", + "\n\n", + "",title,"\n", + "" + ]) + for chapter in chapters: + folder = chapter[0].replace(dstdir,'').lstrip('/') + title = os.path.basename(folder) + filename = getImageFileName(os.path.join(folder,chapter[1])) + f.write("" + title + + "\n") + f.write("\n") + f.close() + return + +def buildOPF(profile, dstdir, title, filelist, cover=None): + 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", + "\n\n\n" + ]) + # set cover + if cover is not None: + folder = cover[0].replace(dstdir,'').lstrip('/') + filename = getImageFileName(cover[1]) + if '.png' == filename[1]: + mt = 'image/png' + else: + mt = 'image/jpeg' + f.write("\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") + 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 +149,86 @@ 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: + if options.verbose: + 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 = [] + chapterlist = [] + cover = None + for (dirpath, dirnames, filenames) in os.walk(path): + chapter = False + 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)) + if not chapter: + chapterlist.append((dirpath,filelist[-1][1])) + chapter = True + if cover is None: + cover = filelist[-1] + if options.title == 'defaulttitle': + options.title = os.path.basename(path) + buildNCX(path,options.title,chapterlist) + # 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,cover) + +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 +239,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 +262,13 @@ 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/KindleComicConverter.app/Contents/Resources/image.py b/KindleComicConverter.app/Contents/Resources/image.py index c7406bd..b914df9 100755 --- a/KindleComicConverter.app/Contents/Resources/image.py +++ b/KindleComicConverter.app/Contents/Resources/image.py @@ -15,6 +15,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +__license__ = 'ISC' +__copyright__ = '2012-2013, Ciro Mattia Gonano ' +__docformat__ = 'restructuredtext en' + import os from PIL import Image, ImageOps, ImageDraw, ImageStat @@ -118,6 +122,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 +180,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])