From a28ad2b0c778789f0f7de0df142db0ac3022a003 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Wed, 11 Sep 2013 15:22:18 +0200 Subject: [PATCH 01/26] Little code cleanup --- kcc.py | 1 - kcc/KCC_gui.py | 1 - kcc/comic2ebook.py | 1 + kcc/comic2panel.py | 4 ++-- kcc/image.py | 2 +- 5 files changed, 4 insertions(+), 5 deletions(-) diff --git a/kcc.py b/kcc.py index c81a806b..5f4a4f94 100644 --- a/kcc.py +++ b/kcc.py @@ -32,7 +32,6 @@ exit(1) from kcc import KCC_gui from multiprocessing import freeze_support - if sys.platform == 'darwin': os.environ['PATH'] = '/usr/local/bin:' + os.environ['PATH'] from kcc import KCC_ui_osx as KCC_ui diff --git a/kcc/KCC_gui.py b/kcc/KCC_gui.py index e86e93e6..5318e49b 100644 --- a/kcc/KCC_gui.py +++ b/kcc/KCC_gui.py @@ -111,7 +111,6 @@ def clean(self): self.emit(QtCore.SIGNAL("addMessage"), 'Conversion interrupted.', 'error') self.emit(QtCore.SIGNAL("modeConvert"), True) - # noinspection PyUnboundLocalVariable def run(self): self.emit(QtCore.SIGNAL("modeConvert"), False) profile = ProfileData.ProfileLabels[str(GUI.DeviceBox.currentText())] diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py index d56e5e71..4f92f64d 100755 --- a/kcc/comic2ebook.py +++ b/kcc/comic2ebook.py @@ -689,6 +689,7 @@ def getDirectorySize(start_path='.'): return total_size +# noinspection PyUnusedLocal def createNewTome(parentPath): tomePathRoot = tempfile.mkdtemp('', 'KCC-TMP-') #tomePathRoot = tempfile.mkdtemp('', 'KCC-TMP-', parentPath) diff --git a/kcc/comic2panel.py b/kcc/comic2panel.py index 57a39eb6..d7b35039 100644 --- a/kcc/comic2panel.py +++ b/kcc/comic2panel.py @@ -28,7 +28,7 @@ from optparse import OptionParser, OptionGroup from multiprocessing import Pool, Queue, freeze_support try: - # noinspection PyUnresolvedReferences,PyPackageRequirements + # noinspection PyUnresolvedReferences from PIL import Image, ImageStat except ImportError: print "ERROR: Pillow is not installed!" @@ -137,7 +137,7 @@ def splitImage(work): filePath = os.path.join(path, name) # Detect corrupted files try: - image = Image.open(filePath) + Image.open(filePath) except IOError: raise RuntimeError('Cannot read image file %s' % filePath) try: diff --git a/kcc/image.py b/kcc/image.py index 9958f3d2..b028dd62 100755 --- a/kcc/image.py +++ b/kcc/image.py @@ -21,7 +21,7 @@ import os try: - # noinspection PyUnresolvedReferences,PyPackageRequirements + # noinspection PyUnresolvedReferences from PIL import Image, ImageOps, ImageStat, ImageChops except ImportError: print "ERROR: Pillow is not installed!" From 08a342c9091afceec7fb4b22247a49ad1f3748a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Sat, 14 Sep 2013 17:48:53 +0200 Subject: [PATCH 02/26] Webtoon mode tweaks --- KCC-OSX.ui | 2 +- KCC.ui | 2 +- kcc/KCC_ui.py | 6 +++--- kcc/KCC_ui_osx.py | 6 +++--- kcc/comic2ebook.py | 12 +++++++----- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/KCC-OSX.ui b/KCC-OSX.ui index d1e7289d..f11dc4a4 100644 --- a/KCC-OSX.ui +++ b/KCC-OSX.ui @@ -109,7 +109,7 @@ Qt::NoFocus - <html><head/><body><p><span style=" font-weight:600;">EXPERIMENTAL!<br/></span>Enable auto-splitting of webtoons like <span style=" font-style:italic;">Tower of God</span> or <span style=" font-style:italic;">Noblesse</span>.<br/>Pages with a low width, high height and vertical panel flow.</p></body></html> + <html><head/><body><p>Enable auto-splitting of webtoons like <span style=" font-style:italic;">Tower of God</span> or <span style=" font-style:italic;">Noblesse</span>.<br/>Pages with a low width, high height and vertical panel flow.</p></body></html> Webtoon mode diff --git a/KCC.ui b/KCC.ui index 5387341f..5960e234 100644 --- a/KCC.ui +++ b/KCC.ui @@ -97,7 +97,7 @@ Qt::NoFocus - <html><head/><body><p><span style=" font-weight:600;">EXPERIMENTAL!<br/></span>Enable auto-splitting of webtoons like <span style=" font-style:italic;">Tower of God</span> or <span style=" font-style:italic;">Noblesse</span>.<br/>Pages with a low width, high height and vertical panel flow.</p></body></html> + <html><head/><body><p>Enable auto-splitting of webtoons like <span style=" font-style:italic;">Tower of God</span> or <span style=" font-style:italic;">Noblesse</span>.<br/>Pages with a low width, high height and vertical panel flow.</p></body></html> Webtoon mode diff --git a/kcc/KCC_ui.py b/kcc/KCC_ui.py index 757b2a88..2589b640 100644 --- a/kcc/KCC_ui.py +++ b/kcc/KCC_ui.py @@ -2,8 +2,8 @@ # Form implementation generated from reading ui file 'KCC.ui' # -# Created: Wed Aug 14 08:39:46 2013 -# by: PyQt4 UI code generator 4.10.2 +# Created: Sat Sep 14 10:28:36 2013 +# by: PyQt4 UI code generator 4.10.3 # # WARNING! All changes made in this file will be lost! @@ -266,7 +266,7 @@ def retranslateUi(self, KCC): self.ProcessingBox.setText(_translate("KCC", "No optimisation", None)) self.UpscaleBox.setToolTip(_translate("KCC", "

Unchecked - Nothing
Images smaller than device resolution will not be resized.

Indeterminate - Stretching
Images smaller than device resolution will be resized. Aspect ratio will be not preserved.

Checked - Upscaling
Images smaller than device resolution will be resized. Aspect ratio will be preserved.

", None)) self.UpscaleBox.setText(_translate("KCC", "Stretch/Upscale", None)) - self.WebtoonBox.setToolTip(_translate("KCC", "

EXPERIMENTAL!
Enable auto-splitting of webtoons like Tower of God or Noblesse.
Pages with a low width, high height and vertical panel flow.

", None)) + self.WebtoonBox.setToolTip(_translate("KCC", "

Enable auto-splitting of webtoons like Tower of God or Noblesse.
Pages with a low width, high height and vertical panel flow.

", None)) self.WebtoonBox.setText(_translate("KCC", "Webtoon mode", None)) self.NoDitheringBox.setToolTip(_translate("KCC", "

Create PNG files instead JPEG.
Only for non-Kindle devices!

", None)) self.NoDitheringBox.setText(_translate("KCC", "PNG output", None)) diff --git a/kcc/KCC_ui_osx.py b/kcc/KCC_ui_osx.py index 997a35b8..06a940b8 100644 --- a/kcc/KCC_ui_osx.py +++ b/kcc/KCC_ui_osx.py @@ -2,8 +2,8 @@ # Form implementation generated from reading ui file 'KCC-OSX.ui' # -# Created: Wed Aug 14 08:39:45 2013 -# by: PyQt4 UI code generator 4.10.2 +# Created: Sat Sep 14 10:28:47 2013 +# by: PyQt4 UI code generator 4.10.3 # # WARNING! All changes made in this file will be lost! @@ -324,7 +324,7 @@ def retranslateUi(self, KCC): self.ProcessingBox.setText(_translate("KCC", "No optimisation", None)) self.UpscaleBox.setToolTip(_translate("KCC", "

Unchecked - Nothing
Images smaller than device resolution will not be resized.

Indeterminate - Stretching
Images smaller than device resolution will be resized. Aspect ratio will be not preserved.

Checked - Upscaling
Images smaller than device resolution will be resized. Aspect ratio will be preserved.

", None)) self.UpscaleBox.setText(_translate("KCC", "Stretch/Upscale", None)) - self.WebtoonBox.setToolTip(_translate("KCC", "

EXPERIMENTAL!
Enable auto-splitting of webtoons like Tower of God or Noblesse.
Pages with a low width, high height and vertical panel flow.

", None)) + self.WebtoonBox.setToolTip(_translate("KCC", "

Enable auto-splitting of webtoons like Tower of God or Noblesse.
Pages with a low width, high height and vertical panel flow.

", None)) self.WebtoonBox.setText(_translate("KCC", "Webtoon mode", None)) self.NoDitheringBox.setToolTip(_translate("KCC", "

Create PNG files instead JPEG.
Only for non-Kindle devices!

", None)) self.NoDitheringBox.setText(_translate("KCC", "PNG output", None)) diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py index 4f92f64d..246b565e 100755 --- a/kcc/comic2ebook.py +++ b/kcc/comic2ebook.py @@ -985,11 +985,6 @@ def getOutputFilename(srcpath, wantedname, ext, tomeNumber): def checkOptions(): global options - # Webtoon mode mandatory options - if options.webtoon: - options.nosplitrotate = True - options.black_borders = False - options.quality = 0 # Landscape mode is only supported by Kindle Touch and Paperwhite. if options.profile == 'K4T' or options.profile == 'KHD': options.landscapemode = True @@ -1021,6 +1016,13 @@ def checkOptions(): options.quality = 0 if options.profile == 'K1' or options.profile == 'K2' or options.profile == 'KDX' or options.profile == 'KDXG': options.panelview = False + # Webtoon mode mandatory options + if options.webtoon: + options.nosplitrotate = True + options.black_borders = False + options.quality = 0 + options.landscapemode = False + options.panelview = False # Disable all Kindle features if options.profile == 'OTHER': options.landscapemode = False From 1bfa0eb9c782260607c9a63ae2e5e632fe76e8b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Sat, 14 Sep 2013 17:57:11 +0200 Subject: [PATCH 03/26] Bundled UnRAR with Windows binary --- .gitignore | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 24db16d4..dc4e08c4 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,4 @@ dist kindlegen* .DS_Store Thumbs.db -UnRAR.exe +/UnRAR.exe diff --git a/setup.py b/setup.py index c17d4e63..ebe2bf39 100644 --- a/setup.py +++ b/setup.py @@ -41,7 +41,7 @@ from cx_Freeze import setup, Executable base = "Win32GUI" extra_options = dict( - options={"build_exe": {"include_files": ['LICENSE.txt'], "compressed": True}}, + options={"build_exe": {"include_files": ['LICENSE.txt', ['other/UnRAR.exe', 'UnRAR.exe']], "compressed": True}}, executables=[Executable(MAIN, base=base, targetName="KCC.exe", From 6e3888f295c4aa64ebae52679376b3bffe93f8f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Sun, 15 Sep 2013 13:03:30 +0200 Subject: [PATCH 04/26] Real Panel View - Ignore margins (close #60) --- kcc/comic2ebook.py | 98 +++++++++++++++++++++++----------------------- kcc/image.py | 31 +++++++++++++++ 2 files changed, 80 insertions(+), 49 deletions(-) diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py index 246b565e..48ff0531 100755 --- a/kcc/comic2ebook.py +++ b/kcc/comic2ebook.py @@ -81,65 +81,65 @@ def buildHTML(path, imgfile): if options.panelview: if rotate: if options.righttoleft: - f.writelines(["
\n", - "
\n", - "
\n", - "
\n" - ]) + order = [1, 3, 2, 4] else: - f.writelines(["
\n", - "
\n", - "
\n", - "
\n" - ]) + order = [2, 4, 1, 3] else: if options.righttoleft: - f.writelines(["
\n", - "
\n", - "
\n", - "
\n" - ]) + order = [2, 1, 4, 3] else: - f.writelines(["
\n", - "
\n", - "
\n", - "
\n" - ]) + order = [1, 2, 3, 4] + f.writelines(["
\n", + "
\n", + "
\n", + "
\n" + ]) if options.quality == 2: imgfilepv = string.split(imgfile, ".") + imgfilepv[0] = imgfilepv[0].split("_kccx")[0] imgfilepv[0] += "_kcchq" imgfilepv = string.join(imgfilepv, ".") else: imgfilepv = imgfile - f.writelines(["
\"",
\n", - "
\"",
\n", - "
\"",
\n", - "
\"",
\n" - ]) + if not "_kccx" in filename[0]: + for box in ["BoxTL", "BoxTR", "BoxBL", "BoxBR"]: + f.writelines(["
\""
\n" + ]) + else: + xy = string.split(filename[0], "_kccx")[1] + x = string.split(xy, "_kccy")[0].lstrip("0") + y = string.split(xy, "_kccy")[1].lstrip("0") + if x != "": + x = "-" + str(float(x)/100) + "%" + else: + x = "0%" + if y != "": + y = "-" + str(float(y)/100) + "%" + else: + y = "0%" + f.writelines(["
\""
\n", + "
\""
\n", + "
\""
\n", + "
\""
\n", + ]) f.writelines(["\n\n"]) f.close() return path, imgfile diff --git a/kcc/image.py b/kcc/image.py index b028dd62..d05b8c6e 100755 --- a/kcc/image.py +++ b/kcc/image.py @@ -146,6 +146,8 @@ def saveToDir(self, targetdir, forcepng, color, wipe, suffix=None): os.remove(os.path.join(targetdir, self.filename)) else: suffix += "_kcchq" + if self.border: + suffix += "_kccx" + str(self.border[0]) + "_kccy" + str(self.border[1]) if forcepng: self.image.save(os.path.join(targetdir, os.path.splitext(self.filename)[0] + suffix + ".png"), "PNG") else: @@ -185,8 +187,13 @@ def resizeImage(self, upscale=False, stretch=False, black_borders=False, isSplit fill = 'white' if qualityMode == 0: size = (self.size[0], self.size[1]) + generateBorder = True + elif qualityMode == 1: + size = (self.panelviewsize[0], self.panelviewsize[1]) + generateBorder = True else: size = (self.panelviewsize[0], self.panelviewsize[1]) + generateBorder = False # Kindle Paperwhite/Touch - Force upscale of splited pages to increase readability if isSplit and landscapeMode: upscale = True @@ -195,15 +202,30 @@ def resizeImage(self, upscale=False, stretch=False, black_borders=False, isSplit borderw = (self.size[0] - self.image.size[0]) / 2 borderh = (self.size[1] - self.image.size[1]) / 2 self.image = ImageOps.expand(self.image, border=(borderw, borderh), fill=fill) + if generateBorder: + self.border = [int(round(float(borderw)/float(self.image.size[0])*100, 2)*100*1.5), + int(round(float(borderh)/float(self.image.size[1])*100, 2)*100*1.5)] + else: + self.border = None return self.image else: method = Image.BILINEAR if stretch: # if stretching call directly resize() without other considerations. self.image = self.image.resize(size, method) + if generateBorder: + if fill == 'white': + border = ImageOps.invert(self.image).getbbox() + else: + border = self.image.getbbox() + self.border = [int(round(float(border[0])/float(self.image.size[0])*100, 2)*100*1.5), + int(round(float(border[1])/float(self.image.size[1])*100, 2)*100*1.5)] + else: + self.border = None return self.image ratioDev = float(self.size[0]) / float(self.size[1]) if (float(self.image.size[0]) / float(self.image.size[1])) < ratioDev: if isSplit and landscapeMode: + generateBorder = False diff = int(self.image.size[1] * ratioDev) - self.image.size[0] self.image = ImageOps.expand(self.image, border=(diff / 2, 0), fill=fill) tempImg = Image.new(self.image.mode, (self.image.size[0] + diff, self.image.size[1]), fill) @@ -219,6 +241,15 @@ def resizeImage(self, upscale=False, stretch=False, black_borders=False, isSplit diff = int(self.image.size[0] / ratioDev) - self.image.size[1] self.image = ImageOps.expand(self.image, border=(0, diff / 2), fill=fill) self.image = ImageOps.fit(self.image, size, method=method, centering=(0.5, 0.5)) + if generateBorder: + if fill == 'white': + border = ImageOps.invert(self.image).getbbox() + else: + border = self.image.getbbox() + self.border = [int(round(float(border[0])/float(self.image.size[0])*100, 2)*100*1.5), + int(round(float(border[1])/float(self.image.size[1])*100, 2)*100*1.5)] + else: + self.border = None return self.image def splitPage(self, targetdir, righttoleft=False, rotate=False): From 69b956904ca8af438529b9c22c1874a487e2c220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Sun, 15 Sep 2013 15:02:31 +0200 Subject: [PATCH 05/26] Removed support of Virtual Panel View --- kcc/KCC_gui.py | 8 ++- kcc/comic2ebook.py | 158 ++++++++++----------------------------------- kcc/image.py | 30 ++------- 3 files changed, 47 insertions(+), 149 deletions(-) diff --git a/kcc/KCC_gui.py b/kcc/KCC_gui.py index 5318e49b..af65a30e 100644 --- a/kcc/KCC_gui.py +++ b/kcc/KCC_gui.py @@ -208,7 +208,8 @@ def run(self): try: self.kindlegenErrorCode = 0 if os.path.getsize(item) < 367001600: - output = Popen('kindlegen -locale en "' + item + '"', stdout=PIPE, stderr=STDOUT, shell=True) + output = Popen('kindlegen -locale en "' + item + '"', stdout=PIPE, stderr=STDOUT, + shell=True) for line in output.stdout: # ERROR: Generic error if "Error(" in line: @@ -439,11 +440,14 @@ def toggleWebtoonBox(self, value): GUI.QualityBox.setChecked(False) GUI.BorderBox.setEnabled(False) GUI.BorderBox.setChecked(False) + GUI.MangaBox.setEnabled(False) + GUI.MangaBox.setChecked(False) self.addMessage('If images are color setting Gamma to 1.0 is recommended.', 'info') else: GUI.NoRotateBox.setEnabled(True) GUI.QualityBox.setEnabled(True) GUI.BorderBox.setEnabled(True) + GUI.MangaBox.setEnabled(True) def toggleNoSplitRotate(self, value): if value: @@ -467,7 +471,7 @@ def changeDevice(self, value): GUI.BasicModeButton.setEnabled(True) GUI.AdvModeButton.setEnabled(True) self.modeBasic() - if value in [0, 1, 5, 6, 7, 8, 9, 12]: + if value in [0, 1, 5, 6, 12]: GUI.QualityBox.setCheckState(0) GUI.QualityBox.setEnabled(False) else: diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py index 48ff0531..806d9495 100755 --- a/kcc/comic2ebook.py +++ b/kcc/comic2ebook.py @@ -145,22 +145,6 @@ def buildHTML(path, imgfile): return path, imgfile -def buildBlankHTML(path): - f = open(os.path.join(path, 'blank.html'), "w") - f.writelines(["\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - ""]) - f.close() - return path - - def buildNCX(dstdir, title, chapters): from uuid import uuid4 options.uuid = str(uuid4()) @@ -173,16 +157,18 @@ def buildNCX(dstdir, title, chapters): "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", + "\n", "\n", "", title, "\n", "" ]) for chapter in chapters: folder = chapter[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\') - title = os.path.basename(folder) + if os.path.basename(folder) != "Text": + title = os.path.basename(folder) filename = getImageFileName(os.path.join(folder, chapter[1])) f.write("" + title + "\n") @@ -197,38 +183,32 @@ def buildOPF(dstdir, title, filelist, cover=None): imgres = str(deviceres[0]) + "x" + str(deviceres[1]) if options.righttoleft: writingmode = "horizontal-rl" - facing = "right" - facing1 = "right" - facing2 = "left" else: writingmode = "horizontal-lr" - facing = "left" - facing1 = "left" - facing2 = "right" f = open(opffile, "w") f.writelines(["\n", - "\n", - "\n", + "\n", + "\n", "", title, "\n", "en-US\n", "", options.uuid, "\n", + "\n", "\n", + "\n", "\n", "\n", + "\n", "\n", "\n", "\n" - ]) - if options.landscapemode: - f.writelines(["\n", - "\n"]) - else: - f.writelines(["\n", - "\n"]) - f.writelines(["\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", + "\n", "\n\n\n"]) if cover is not None: @@ -253,44 +233,10 @@ def buildOPF(dstdir, title, filelist, cover=None): mt = 'image/jpeg' f.write("\n") - if options.landscapemode and splitCount > 0: - splitCountUsed = 1 - while splitCountUsed <= splitCount: - f.write("\n") - splitCountUsed += 1 f.write("\n") f.write("\n\n") - splitCountUsed = 1 for entry in reflist: - if "_kcca" in str(entry): - # noinspection PyRedundantParentheses - if ((options.righttoleft and facing == 'left') or (not options.righttoleft and facing == 'right')) and\ - options.landscapemode: - f.write("\n") - splitCountUsed += 1 - if options.landscapemode: - f.write("\n") - else: - f.write("\n") - elif "_kccb" in str(entry): - if options.landscapemode: - f.write("\n") - else: - f.write("\n") - if options.righttoleft: - facing = "right" - else: - facing = "left" - else: - if options.landscapemode: - f.write("\n") - else: - f.write("\n") - if facing == 'right': - facing = 'left' - else: - facing = 'right' + f.write("\n") f.write("\n\n\n\n") f.close() os.mkdir(os.path.join(dstdir, 'META-INF')) @@ -322,24 +268,22 @@ def getImageFileName(imgfile): return filename -def applyImgOptimization(img, isSplit, toRight, options, overrideQuality=5): +def applyImgOptimization(img, options, overrideQuality=5): if not options.webtoon: img.cropWhiteSpace(10.0) if options.cutpagenumbers and not options.webtoon: img.cutPageNumber() img.optimizeImage(options.gamma) if overrideQuality != 5: - img.resizeImage(options.upscale, options.stretch, options.black_borders, isSplit, toRight, - options.landscapemode, overrideQuality) + img.resizeImage(options.upscale, options.stretch, options.black_borders, overrideQuality) else: - img.resizeImage(options.upscale, options.stretch, options.black_borders, isSplit, toRight, - options.landscapemode, options.quality) + img.resizeImage(options.upscale, options.stretch, options.black_borders, options.quality) if options.forcepng and not options.forcecolor: img.quantizeImage() def dirImgProcess(path): - global options, splitCount + global options work = [] pagenumber = 0 pagenumbermodifier = 0 @@ -378,7 +322,6 @@ def dirImgProcess(path): splitpages.sort() for page in splitpages: if (page + pagenumbermodifier) % 2 == 0: - splitCount += 1 pagenumbermodifier += 1 pagenumbermodifier += 1 else: @@ -415,34 +358,28 @@ def fileImgProcess(work): if split is not None and split is not "R": if options.verbose: print "Splitted " + afile - if options.righttoleft: - toRight1 = False - toRight2 = True - else: - toRight1 = True - toRight2 = False output = pagenumber img0 = image.ComicPage(split[0], options.profileData) - applyImgOptimization(img0, True, toRight1, options) + applyImgOptimization(img0, options) img0.saveToDir(dirpath, options.forcepng, options.forcecolor, wipe) img1 = image.ComicPage(split[1], options.profileData) - applyImgOptimization(img1, True, toRight2, options) + applyImgOptimization(img1, options) img1.saveToDir(dirpath, options.forcepng, options.forcecolor, wipe) if options.quality == 2: img3 = image.ComicPage(split[0], options.profileData) - applyImgOptimization(img3, True, toRight1, options, 0) + applyImgOptimization(img3, options, 0) img3.saveToDir(dirpath, options.forcepng, options.forcecolor, True) img4 = image.ComicPage(split[1], options.profileData) - applyImgOptimization(img4, True, toRight2, options, 0) + applyImgOptimization(img4, options, 0) img4.saveToDir(dirpath, options.forcepng, options.forcecolor, True) else: - applyImgOptimization(img, False, False, options) + applyImgOptimization(img, options) img.saveToDir(dirpath, options.forcepng, options.forcecolor, wipe, split) if options.quality == 2: img2 = image.ComicPage(os.path.join(dirpath, afile), options.profileData) if split == "R": img2.image = img2.image.rotate(90) - applyImgOptimization(img2, False, False, options, 0) + applyImgOptimization(img2, options, 0) img2.saveToDir(dirpath, options.forcepng, options.forcecolor, True, split) return output @@ -588,13 +525,11 @@ def genEpubStruct(path): 'cover' + getImageFileName(filelist[-1][1])[1]) copyfile(os.path.join(filelist[-1][0], filelist[-1][1]), cover) buildNCX(path, options.title, chapterlist) - # ensure we're sorting files alphabetically + # Ensure we're sorting files alphabetically convert = lambda text: int(text) if text.isdigit() else text alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)] filelist.sort(key=lambda name: (alphanum_key(name[0].lower()), alphanum_key(name[1].lower()))) buildOPF(path, options.title, filelist, cover) - if options.landscapemode and splitCount > 0: - filelist.append(buildBlankHTML(os.path.join(path, 'OEBPS', 'Text'))) def getWorkFolder(afile): @@ -637,8 +572,7 @@ def getWorkFolder(afile): def slugify(value): - # Normalizes string, converts to lowercase, removes non-alpha characters, - # and converts spaces to hyphens. + # Normalizes string, converts to lowercase, removes non-alpha characters and converts spaces to hyphens. import unicodedata value = unicodedata.normalize('NFKD', unicode(value, 'latin1')).encode('ascii', 'ignore') value = re.sub('[^\w\s\.-]', '', value).strip().lower() @@ -844,7 +778,7 @@ def Usage(): def main(argv=None, qtGUI=None): - global parser, options, epub_path, splitCount, GUI + global parser, options, epub_path, GUI parser = OptionParser(usage="Usage: %prog [options] comic_file|comic_folder", add_help_option=False) mainOptions = OptionGroup(parser, "MAIN") experimentalOptions = OptionGroup(parser, "EXPERIMENTAL") @@ -921,7 +855,6 @@ def main(argv=None, qtGUI=None): comic2panel.main(['-y ' + str(options.customheight), '-i', path], qtGUI) else: comic2panel.main(['-y ' + str(image.ProfileData.Profiles[options.profile][1][1]), '-i', path], qtGUI) - splitCount = 0 if options.imgproc: print "\nProcessing images..." if GUI: @@ -985,47 +918,24 @@ def getOutputFilename(srcpath, wantedname, ext, tomeNumber): def checkOptions(): global options - # Landscape mode is only supported by Kindle Touch and Paperwhite. - if options.profile == 'K4T' or options.profile == 'KHD': - options.landscapemode = True - else: - options.landscapemode = False - # Older Kindle don't support Virtual Panel View. We providing them configuration that will fake that feature. - # Ultra quality mode require Real Panel View. Landscape mode don't work correcly without Virtual Panel View. - if options.profile == 'K3' or options.profile == 'K4NT' or options.quality == 2: - # Real Panel View - options.panelview = True - options.landscapemode = False - else: - # Virtual Panel View - options.panelview = False + options.panelview = True # Disabling grayscale conversion for Kindle Fire family. if options.profile == 'KF' or options.profile == 'KFHD' or options.profile == 'KFHD8' or options.forcecolor: options.forcecolor = True else: options.forcecolor = False - # Mixing vertical and horizontal pages require real Panel View. - # Landscape mode don't work correcly without Virtual Panel View. - if options.rotate: - options.panelview = True - options.landscapemode = False # Older Kindle don't need higher resolution files due lack of Panel View. - # Kindle Fire family have very high resolution. Bigger images are not needed. - if options.profile == 'K1' or options.profile == 'K2' or options.profile == 'KDX' or options.profile == 'KDXG'\ - or options.profile == 'KF' or options.profile == 'KFHD' or options.profile == 'KFHD8': + if options.profile == 'K1' or options.profile == 'K2' or options.profile == 'KDX' or options.profile == 'KDXG': options.quality = 0 - if options.profile == 'K1' or options.profile == 'K2' or options.profile == 'KDX' or options.profile == 'KDXG': - options.panelview = False + options.panelview = False # Webtoon mode mandatory options if options.webtoon: options.nosplitrotate = True options.black_borders = False options.quality = 0 - options.landscapemode = False options.panelview = False - # Disable all Kindle features + # Disable all Kindle features for other e-readers if options.profile == 'OTHER': - options.landscapemode = False options.panelview = False options.quality = 0 # Kindle for Android profile require target resolution. diff --git a/kcc/image.py b/kcc/image.py index d05b8c6e..f3cb6215 100755 --- a/kcc/image.py +++ b/kcc/image.py @@ -173,8 +173,7 @@ def quantizeImage(self): palImg.putpalette(self.palette) self.image = self.image.quantize(palette=palImg) - def resizeImage(self, upscale=False, stretch=False, black_borders=False, isSplit=False, toRight=False, - landscapeMode=False, qualityMode=0): + def resizeImage(self, upscale=False, stretch=False, black_borders=False, qualityMode=0): method = Image.ANTIALIAS if '-KCCFW' in str(self.filename): fill = 'white' @@ -194,9 +193,6 @@ def resizeImage(self, upscale=False, stretch=False, black_borders=False, isSplit else: size = (self.panelviewsize[0], self.panelviewsize[1]) generateBorder = False - # Kindle Paperwhite/Touch - Force upscale of splited pages to increase readability - if isSplit and landscapeMode: - upscale = True if self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1]: if not upscale: borderw = (self.size[0] - self.image.size[0]) / 2 @@ -210,7 +206,7 @@ def resizeImage(self, upscale=False, stretch=False, black_borders=False, isSplit return self.image else: method = Image.BILINEAR - if stretch: # if stretching call directly resize() without other considerations. + if stretch: # If stretching call directly resize() without other considerations. self.image = self.image.resize(size, method) if generateBorder: if fill == 'white': @@ -224,19 +220,8 @@ def resizeImage(self, upscale=False, stretch=False, black_borders=False, isSplit return self.image ratioDev = float(self.size[0]) / float(self.size[1]) if (float(self.image.size[0]) / float(self.image.size[1])) < ratioDev: - if isSplit and landscapeMode: - generateBorder = False - diff = int(self.image.size[1] * ratioDev) - self.image.size[0] - self.image = ImageOps.expand(self.image, border=(diff / 2, 0), fill=fill) - tempImg = Image.new(self.image.mode, (self.image.size[0] + diff, self.image.size[1]), fill) - if toRight: - tempImg.paste(self.image, (diff, 0)) - else: - tempImg.paste(self.image, (0, 0)) - self.image = tempImg - else: - diff = int(self.image.size[1] * ratioDev) - self.image.size[0] - self.image = ImageOps.expand(self.image, border=(diff / 2, 0), fill=fill) + diff = int(self.image.size[1] * ratioDev) - self.image.size[0] + self.image = ImageOps.expand(self.image, border=(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, diff / 2), fill=fill) @@ -255,19 +240,18 @@ def resizeImage(self, upscale=False, stretch=False, black_borders=False, isSplit def splitPage(self, targetdir, righttoleft=False, rotate=False): width, height = self.image.size dstwidth, dstheight = self.size - #print "Image is %d x %d" % (width,height) - # only split if origin is not oriented the same as target + # Only split if origin is not oriented the same as target if (width > height) != (dstwidth > dstheight): if rotate: self.image = self.image.rotate(90) return "R" else: if width > height: - # source is landscape, so split by the width + # Source is landscape, so split by the width leftbox = (0, 0, width / 2, height) rightbox = (width / 2, 0, width, height) else: - # source is portrait and target is landscape, so split by the height + # Source is portrait and target is landscape, so split by the height leftbox = (0, 0, width, height / 2) rightbox = (0, height / 2, width, height) filename = os.path.splitext(self.filename) From a484582b70b5fe999d1904195bda183fb45523c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Sun, 15 Sep 2013 15:42:23 +0200 Subject: [PATCH 06/26] Merged Kindle 3, 4 and 5 profiles --- kcc/KCC_gui.py | 23 ++++++++++++++--------- kcc/comic2ebook.py | 4 ++-- kcc/image.py | 25 +++++++++++++++++++------ 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/kcc/KCC_gui.py b/kcc/KCC_gui.py index af65a30e..3e5e5f48 100644 --- a/kcc/KCC_gui.py +++ b/kcc/KCC_gui.py @@ -457,13 +457,13 @@ def toggleNoSplitRotate(self, value): GUI.RotateBox.setEnabled(True) def changeDevice(self, value): - if value == 12: + if value == 8: GUI.BasicModeButton.setEnabled(False) GUI.AdvModeButton.setEnabled(False) self.addMessage('' 'List of supported Non-Kindle devices', 'info') self.modeExpert() - elif value == 11: + elif value == 7: GUI.BasicModeButton.setEnabled(False) GUI.AdvModeButton.setEnabled(False) self.modeExpert(True) @@ -471,7 +471,7 @@ def changeDevice(self, value): GUI.BasicModeButton.setEnabled(True) GUI.AdvModeButton.setEnabled(True) self.modeBasic() - if value in [0, 1, 5, 6, 12]: + if value in [8, 9, 10, 11, 12]: GUI.QualityBox.setCheckState(0) GUI.QualityBox.setEnabled(False) else: @@ -570,7 +570,6 @@ def __init__(self, UI, KCC): global GUI, MainWindow GUI = UI MainWindow = KCC - profiles = sorted(ProfileData.ProfileLabels.iterkeys()) self.icons = Icons() self.settings = QtCore.QSettings('KindleComicConverter', 'KindleComicConverter') self.lastPath = self.settings.value('lastPath', '', type=str) @@ -630,12 +629,18 @@ def __init__(self, UI, KCC): KCC.connect(self.versionCheck, QtCore.SIGNAL("addMessage"), self.addMessage) KCC.closeEvent = self.saveSettings - for profile in profiles: - if profile != "Other": - GUI.DeviceBox.addItem(self.icons.deviceKindle, profile) - else: + for profile in ProfileData.ProfileLabelsGUI: + if profile == "Other": GUI.DeviceBox.addItem(self.icons.deviceOther, profile) - GUI.DeviceBox.setCurrentIndex(self.lastDevice) + elif profile == "Separator": + GUI.DeviceBox.insertSeparator(GUI.DeviceBox.count()+1) + else: + GUI.DeviceBox.addItem(self.icons.deviceKindle, profile) + if self.lastDevice > GUI.DeviceBox.count(): + GUI.DeviceBox.setCurrentIndex(0) + self.lastDevice = 0 + else: + GUI.DeviceBox.setCurrentIndex(self.lastDevice) for f in formats: GUI.FormatBox.addItem(eval('self.icons.' + f + 'Format'), f) diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py index 806d9495..bc17424f 100755 --- a/kcc/comic2ebook.py +++ b/kcc/comic2ebook.py @@ -787,8 +787,8 @@ def main(argv=None, qtGUI=None): customProfileOptions = OptionGroup(parser, "CUSTOM PROFILE") otherOptions = OptionGroup(parser, "OTHER") mainOptions.add_option("-p", "--profile", action="store", dest="profile", default="KHD", - help="Device profile (Choose one among K1, K2, K3, K4NT, K4T, KDX, KDXG, KHD, KF, KFHD," - " KFHD8, KFA) [Default=KHD]") + help="Device profile (Choose one among K1, K2, K345, KDX, KDXG, KHD, KF, KFHD, KFHD8, KFA)" + "[Default=KHD]") 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, diff --git a/kcc/image.py b/kcc/image.py index f3cb6215..86abf442 100755 --- a/kcc/image.py +++ b/kcc/image.py @@ -76,9 +76,7 @@ class ProfileData: Profiles = { 'K1': ("Kindle 1", (600, 800), Palette4, 1.8, (900, 1200)), 'K2': ("Kindle 2", (600, 800), Palette15, 1.8, (900, 1200)), - 'K3': ("Kindle Keyboard", (600, 800), Palette16, 1.8, (900, 1200)), - 'K4NT': ("Kindle Non-Touch", (600, 800), Palette16, 1.8, (900, 1200)), - 'K4T': ("Kindle Touch", (600, 800), Palette16, 1.8, (900, 1200)), + 'K345': ("Kindle", (600, 800), Palette16, 1.8, (900, 1200)), 'KHD': ("Kindle Paperwhite", (758, 1024), Palette16, 1.8, (1137, 1536)), 'KDX': ("Kindle DX", (824, 1200), Palette15, 1.8, (1236, 1800)), 'KDXG': ("Kindle DXG", (824, 1200), Palette16, 1.8, (1236, 1800)), @@ -92,9 +90,7 @@ class ProfileData: ProfileLabels = { "Kindle 1": 'K1', "Kindle 2": 'K2', - "Kindle 3/Keyboard": 'K3', - "Kindle 4/Non-Touch": 'K4NT', - "Kindle 4/Touch": 'K4T', + "Kindle": 'K345', "Kindle Paperwhite": 'KHD', "Kindle DX": 'KDX', "Kindle DXG": 'KDXG', @@ -105,6 +101,23 @@ class ProfileData: "Other": 'OTHER' } + ProfileLabelsGUI = [ + "Kindle Paperwhite", + "Kindle", + "Separator", + "Kindle Fire", + "Kindle Fire HD 7\"", + "Kindle Fire HD 8.9\"", + "Separator", + "Kindle for Android", + "Other", + "Separator", + "Kindle 1", + "Kindle 2", + "Kindle DX", + "Kindle DXG" + ] + class ComicPage: def __init__(self, source, device): From a8c3ef7d0034efea19635c78e4cf341a643fd50d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Sun, 15 Sep 2013 16:24:14 +0200 Subject: [PATCH 07/26] Margins color detection now handles every file --- KCC-OSX.ui | 7 +++++-- KCC.ui | 7 +++++-- kcc/KCC_gui.py | 6 ++++-- kcc/KCC_ui.py | 7 ++++--- kcc/KCC_ui_osx.py | 7 ++++--- kcc/comic2ebook.py | 14 +++++++++---- kcc/comic2panel.py | 41 +------------------------------------ kcc/image.py | 51 +++++++++++++++++++++++++++++++++++++--------- 8 files changed, 74 insertions(+), 66 deletions(-) diff --git a/KCC-OSX.ui b/KCC-OSX.ui index f11dc4a4..33db33ca 100644 --- a/KCC-OSX.ui +++ b/KCC-OSX.ui @@ -145,10 +145,13 @@ Qt::NoFocus
- <html><head/><body><p><span style=" font-size:12pt;">Fill space around images with black color.</span></p></body></html> + <html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Autodetection<br/></span>Color of margins fill will be detected automatically.</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - White<br/></span>Margins will be filled with white color.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Black<br/></span>Margins will be filled with black color.</p></body></html> - Black borders + W/B margins + + + true diff --git a/KCC.ui b/KCC.ui index 5960e234..a627a2d4 100644 --- a/KCC.ui +++ b/KCC.ui @@ -123,10 +123,13 @@ Qt::NoFocus - Fill space around images with black color. + <html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Autodetection<br/></span>Color of margins fill will be detected automatically.</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - White<br/></span>Margins will be filled with white color.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Black<br/></span>Margins will be filled with black color.</p></body></html> - Black borders + W/B margins + + + true diff --git a/kcc/KCC_gui.py b/kcc/KCC_gui.py index 3e5e5f48..1f6e2893 100644 --- a/kcc/KCC_gui.py +++ b/kcc/KCC_gui.py @@ -129,12 +129,14 @@ def run(self): argv.append("--noprocessing") if GUI.NoRotateBox.isChecked(): argv.append("--nosplitrotate") - if GUI.BorderBox.isChecked(): - argv.append("--blackborders") if GUI.UpscaleBox.checkState() == 1: argv.append("--stretch") elif GUI.UpscaleBox.checkState() == 2: argv.append("--upscale") + if GUI.BorderBox.checkState() == 1: + argv.append("--whiteborders") + elif GUI.BorderBox.checkState() == 2: + argv.append("--blackborders") if GUI.NoDitheringBox.isChecked(): argv.append("--forcepng") if GUI.WebtoonBox.isChecked(): diff --git a/kcc/KCC_ui.py b/kcc/KCC_ui.py index 2589b640..2fd98d83 100644 --- a/kcc/KCC_ui.py +++ b/kcc/KCC_ui.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'KCC.ui' # -# Created: Sat Sep 14 10:28:36 2013 +# Created: Sun Sep 15 16:18:37 2013 # by: PyQt4 UI code generator 4.10.3 # # WARNING! All changes made in this file will be lost! @@ -68,6 +68,7 @@ def setupUi(self, KCC): self.gridLayout.addWidget(self.NoDitheringBox, 3, 2, 1, 1) self.BorderBox = QtGui.QCheckBox(self.OptionsAdvanced) self.BorderBox.setFocusPolicy(QtCore.Qt.NoFocus) + self.BorderBox.setTristate(True) self.BorderBox.setObjectName(_fromUtf8("BorderBox")) self.gridLayout.addWidget(self.BorderBox, 3, 0, 1, 1) self.NoRotateBox = QtGui.QCheckBox(self.OptionsAdvanced) @@ -270,8 +271,8 @@ def retranslateUi(self, KCC): self.WebtoonBox.setText(_translate("KCC", "Webtoon mode", None)) self.NoDitheringBox.setToolTip(_translate("KCC", "

Create PNG files instead JPEG.
Only for non-Kindle devices!

", None)) self.NoDitheringBox.setText(_translate("KCC", "PNG output", None)) - self.BorderBox.setToolTip(_translate("KCC", "Fill space around images with black color.", None)) - self.BorderBox.setText(_translate("KCC", "Black borders", None)) + self.BorderBox.setToolTip(_translate("KCC", "

Unchecked - Autodetection
Color of margins fill will be detected automatically.

Indeterminate - White
Margins will be filled with white color.

Checked - Black
Margins will be filled with black color.

", None)) + self.BorderBox.setText(_translate("KCC", "W/B margins", None)) self.NoRotateBox.setToolTip(_translate("KCC", "

Disable splitting and rotation.

", None)) self.NoRotateBox.setText(_translate("KCC", "No split/rotate", None)) self.DeviceBox.setToolTip(_translate("KCC", "Target device.", None)) diff --git a/kcc/KCC_ui_osx.py b/kcc/KCC_ui_osx.py index 06a940b8..84474e5f 100644 --- a/kcc/KCC_ui_osx.py +++ b/kcc/KCC_ui_osx.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'KCC-OSX.ui' # -# Created: Sat Sep 14 10:28:47 2013 +# Created: Sun Sep 15 16:18:48 2013 # by: PyQt4 UI code generator 4.10.3 # # WARNING! All changes made in this file will be lost! @@ -82,6 +82,7 @@ def setupUi(self, KCC): font.setPointSize(11) self.BorderBox.setFont(font) self.BorderBox.setFocusPolicy(QtCore.Qt.NoFocus) + self.BorderBox.setTristate(True) self.BorderBox.setObjectName(_fromUtf8("BorderBox")) self.gridLayout.addWidget(self.BorderBox, 3, 0, 1, 1) self.NoRotateBox = QtGui.QCheckBox(self.OptionsAdvanced) @@ -328,8 +329,8 @@ def retranslateUi(self, KCC): self.WebtoonBox.setText(_translate("KCC", "Webtoon mode", None)) self.NoDitheringBox.setToolTip(_translate("KCC", "

Create PNG files instead JPEG.
Only for non-Kindle devices!

", None)) self.NoDitheringBox.setText(_translate("KCC", "PNG output", None)) - self.BorderBox.setToolTip(_translate("KCC", "

Fill space around images with black color.

", None)) - self.BorderBox.setText(_translate("KCC", "Black borders", None)) + self.BorderBox.setToolTip(_translate("KCC", "

Unchecked - Autodetection
Color of margins fill will be detected automatically.

Indeterminate - White
Margins will be filled with white color.

Checked - Black
Margins will be filled with black color.

", None)) + self.BorderBox.setText(_translate("KCC", "W/B margins", None)) self.NoRotateBox.setToolTip(_translate("KCC", "

Disable splitting and rotation.

", None)) self.NoRotateBox.setText(_translate("KCC", "No split/rotate", None)) self.DeviceBox.setToolTip(_translate("KCC", "Target device.", None)) diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py index bc17424f..d5aff24b 100755 --- a/kcc/comic2ebook.py +++ b/kcc/comic2ebook.py @@ -275,9 +275,9 @@ def applyImgOptimization(img, options, overrideQuality=5): img.cutPageNumber() img.optimizeImage(options.gamma) if overrideQuality != 5: - img.resizeImage(options.upscale, options.stretch, options.black_borders, overrideQuality) + img.resizeImage(options.upscale, options.stretch, options.bordersColor, overrideQuality) else: - img.resizeImage(options.upscale, options.stretch, options.black_borders, options.quality) + img.resizeImage(options.upscale, options.stretch, options.bordersColor, options.quality) if options.forcepng and not options.forcecolor: img.quantizeImage() @@ -804,7 +804,9 @@ def main(argv=None, qtGUI=None): experimentalOptions.add_option("-w", "--webtoon", action="store_true", dest="webtoon", default=False, help="Webtoon processing mode"), processingOptions.add_option("--blackborders", action="store_true", dest="black_borders", default=False, - help="Use black borders instead of white ones") + help="Disable autodetection and force black borders") + processingOptions.add_option("--whiteborders", action="store_true", dest="white_borders", default=False, + help="Disable autodetection and force white borders") processingOptions.add_option("--forcecolor", action="store_true", dest="forcecolor", default=False, help="Don't convert images to grayscale") processingOptions.add_option("--forcepng", action="store_true", dest="forcepng", default=False, @@ -919,6 +921,11 @@ def getOutputFilename(srcpath, wantedname, ext, tomeNumber): def checkOptions(): global options options.panelview = True + options.bordersColor = None + if options.white_borders: + options.bordersColor = "white" + if options.black_borders: + options.bordersColor = "black" # Disabling grayscale conversion for Kindle Fire family. if options.profile == 'KF' or options.profile == 'KFHD' or options.profile == 'KFHD8' or options.forcecolor: options.forcecolor = True @@ -931,7 +938,6 @@ def checkOptions(): # Webtoon mode mandatory options if options.webtoon: options.nosplitrotate = True - options.black_borders = False options.quality = 0 options.panelview = False # Disable all Kindle features for other e-readers diff --git a/kcc/comic2panel.py b/kcc/comic2panel.py index d7b35039..f9a52b16 100644 --- a/kcc/comic2panel.py +++ b/kcc/comic2panel.py @@ -53,45 +53,6 @@ def getImageFileName(imgfile): return filename -def getImageHistogram(image): - histogram = image.histogram() - RBGW = [] - for i in range(256): - RBGW.append(histogram[i] + histogram[256 + i] + histogram[512 + i]) - white = 0 - black = 0 - for i in range(245, 256): - white += RBGW[i] - for i in range(11): - black += RBGW[i] - if white > black: - return False - else: - return True - - -def getImageFill(image): - imageSize = image.size - imageT = image.crop((0, 0, imageSize[0], 1)) - imageB = image.crop((0, imageSize[1]-1, imageSize[0], imageSize[1])) - fill = 0 - fill += getImageHistogram(imageT) - fill += getImageHistogram(imageB) - if fill == 2: - return 'KCCFB' - elif fill == 0: - return 'KCCFW' - else: - imageL = image.crop((0, 0, 1, imageSize[1])) - imageR = image.crop((imageSize[0]-1, 0, imageSize[0], imageSize[1])) - fill += getImageHistogram(imageL) - fill += getImageHistogram(imageR) - if fill >= 2: - return 'KCCFB' - else: - return 'KCCFW' - - def sanitizePanelSize(panel, options): newPanels = [] if panel[2] > 8 * options.height: @@ -222,7 +183,7 @@ def splitImage(work): newPage.paste(panelImg, (0, targetHeight)) targetHeight += panels[panel][2] newPage.save(os.path.join(path, fileExpanded[0] + '-' + - str(pageNumber) + '-' + getImageFill(newPage) + '.png'), 'PNG') + str(pageNumber) + '.png'), 'PNG') pageNumber += 1 os.remove(filePath) diff --git a/kcc/image.py b/kcc/image.py index 86abf442..b384ba69 100755 --- a/kcc/image.py +++ b/kcc/image.py @@ -186,17 +186,12 @@ def quantizeImage(self): palImg.putpalette(self.palette) self.image = self.image.quantize(palette=palImg) - def resizeImage(self, upscale=False, stretch=False, black_borders=False, qualityMode=0): + def resizeImage(self, upscale=False, stretch=False, bordersColor=None, qualityMode=0): method = Image.ANTIALIAS - if '-KCCFW' in str(self.filename): - fill = 'white' - elif '-KCCFB' in str(self.filename): - fill = 'black' + if bordersColor: + fill = bordersColor else: - if black_borders: - fill = 'black' - else: - fill = 'white' + fill = self.getImageFill() if qualityMode == 0: size = (self.size[0], self.size[1]) generateBorder = True @@ -379,4 +374,40 @@ def cropWhiteSpace(self, threshold): # print "Right crop: %s"%diff self.image = self.image.crop((0, 0, widthImg - diff, heightImg)) # print "New size: %sx%s"%(self.image.size[0],self.image.size[1]) - return self.image \ No newline at end of file + return self.image + + def getImageHistogram(self, image): + histogram = image.histogram() + RBGW = [] + for i in range(256): + RBGW.append(histogram[i] + histogram[256 + i] + histogram[512 + i]) + white = 0 + black = 0 + for i in range(245, 256): + white += RBGW[i] + for i in range(11): + black += RBGW[i] + if white > black: + return False + else: + return True + + def getImageFill(self): + imageT = self.image.crop((0, 0, self.image.size[0], 1)) + imageB = self.image.crop((0, self.image.size[1]-1, self.image.size[0], self.image.size[1])) + fill = 0 + fill += self.getImageHistogram(imageT) + fill += self.getImageHistogram(imageB) + if fill == 2: + return 'black' + elif fill == 0: + return 'white' + else: + imageL = self.image.crop((0, 0, 1, self.image.size[1])) + imageR = self.image.crop((self.image.size[0]-1, 0, self.image.size[0], self.image.size[1])) + fill += self.getImageHistogram(imageL) + fill += self.getImageHistogram(imageR) + if fill >= 2: + return 'black' + else: + return 'white' \ No newline at end of file From 9b400573c8dd19d2faad0853bbb07aa6e2a8f5be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Sun, 15 Sep 2013 21:05:53 +0200 Subject: [PATCH 08/26] Automatic matching of Panel View layout --- kcc/comic2ebook.py | 195 +++++++++++++++++++++++---------------------- kcc/image.py | 40 +++++++++- 2 files changed, 138 insertions(+), 97 deletions(-) diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py index d5aff24b..b93eb7e3 100755 --- a/kcc/comic2ebook.py +++ b/kcc/comic2ebook.py @@ -44,11 +44,18 @@ def buildHTML(path, imgfile): filename = getImageFileName(imgfile) if filename is not None: - # All files marked with this sufix need horizontal Panel View. - if "_kccrotated" in str(filename): - rotate = True + if "_kccrot" in str(filename): + rotatedPage = True else: - rotate = False + rotatedPage = False + if "_kccnh" in str(filename): + noHorizontalPV = True + else: + noHorizontalPV = False + if "_kccnv" in str(filename): + noVerticalPV = True + else: + noVerticalPV = False htmlpath = '' postfix = '' backref = 1 @@ -79,64 +86,74 @@ def buildHTML(path, imgfile): imgfile, "\" class=\"singlePage\"/>\n" ]) if options.panelview: - if rotate: - if options.righttoleft: - order = [1, 3, 2, 4] + if not noHorizontalPV and not noVerticalPV: + if rotatedPage: + if options.righttoleft: + order = [1, 3, 2, 4] + else: + order = [2, 4, 1, 3] else: - order = [2, 4, 1, 3] - else: - if options.righttoleft: - order = [2, 1, 4, 3] + 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 = [2, 1] + else: + order = [1, 2] + else: + order = [1, 2] + boxes = ["BoxT", "BoxB"] + elif not noHorizontalPV and noVerticalPV: + if rotatedPage: + order = [1, 2] else: - order = [1, 2, 3, 4] - f.writelines(["
\n", - "
\n", - "
\n", - "
\n" - ]) + if options.righttoleft: + order = [2, 1] + else: + order = [1, 2] + boxes = ["BoxL", "BoxR"] + else: + order = [1] + boxes = ["BoxC"] + for i in range(0, len(boxes)): + f.writelines(["
\n"]) if options.quality == 2: imgfilepv = string.split(imgfile, ".") - imgfilepv[0] = imgfilepv[0].split("_kccx")[0] + imgfilepv[0] = imgfilepv[0].split("_kccx")[0].replace("_kccnh", "").replace("_kccnv", "") imgfilepv[0] += "_kcchq" imgfilepv = string.join(imgfilepv, ".") else: imgfilepv = imgfile - if not "_kccx" in filename[0]: - for box in ["BoxTL", "BoxTR", "BoxBL", "BoxBR"]: - f.writelines(["
\""
\n" - ]) + xy = string.split(filename[0], "_kccx")[1] + x = string.split(xy, "_kccy")[0].lstrip("0") + y = string.split(xy, "_kccy")[1].lstrip("0") + if x != "": + x = "-" + str(float(x)/100) + "%" else: - xy = string.split(filename[0], "_kccx")[1] - x = string.split(xy, "_kccy")[0].lstrip("0") - y = string.split(xy, "_kccy")[1].lstrip("0") - if x != "": - x = "-" + str(float(x)/100) + "%" - else: - x = "0%" - if y != "": - y = "-" + str(float(y)/100) + "%" - else: - y = "0%" - f.writelines(["
\""
\n", - "
\""
\n", - "
\""
\n", - "
\""
\n", ]) @@ -445,70 +462,60 @@ def genEpubStruct(path): "height: ", str(panelviewsize[1]), "px;\n", "width: ", str(panelviewsize[0]), "px;\n", "}\n", - "#BoxTL {\n", + "#Generic-Panel {\n", "top: 0;\n", - "left: 0;\n", - "height: 50%;\n", - "width: 50%;\n", + "height: 100%;\n", + "width: 100%;\n", "}\n", - "#BoxTR {\n", + "#BoxC {\n", + "top: 0;\n", + "height: 100%;\n", + "width: 100%;\n", + "}\n", + "#BoxT {\n", "top: 0;\n", - "right: 0;\n", "height: 50%;\n", - "width: 50%;\n", + "width: 100%;\n", "}\n", - "#BoxBL {\n", + "#BoxB {\n", "bottom: 0;\n", - "left: 0;\n", "height: 50%;\n", + "width: 100%;\n", + "}\n", + "#BoxL {\n", + "left: 0;\n", + "height: 100%;\n", "width: 50%;\n", "}\n", - "#BoxBR {\n", - "bottom: 0;\n", + "#BoxR {\n", "right: 0;\n", - "height: 50%;\n", + "height: 100%;\n", "width: 50%;\n", "}\n", - "#BoxTL-Panel {\n", + "#BoxTL {\n", "top: 0;\n", "left: 0;\n", - "height: 100%;\n", - "width: 100%;\n", - "}\n", - "#BoxTL-Panel img {\n", - "top: 0%;\n", - "left: 0%;\n", + "height: 50%;\n", + "width: 50%;\n", "}\n", - "#BoxTR-Panel {\n", + "#BoxTR {\n", "top: 0;\n", "right: 0;\n", - "height: 100%;\n", - "width: 100%;\n", - "}\n", - "#BoxTR-Panel img {\n", - "top: 0%;\n", - "right: 0%;\n", + "height: 50%;\n", + "width: 50%;\n", "}\n", - "#BoxBL-Panel {\n", + "#BoxBL {\n", "bottom: 0;\n", "left: 0;\n", - "height: 100%;\n", - "width: 100%;\n", - "}\n", - "#BoxBL-Panel img {\n", - "bottom: 0%;\n", - "left: 0%;\n", + "height: 50%;\n", + "width: 50%;\n", "}\n", - "#BoxBR-Panel {\n", + "#BoxBR {\n", "bottom: 0;\n", "right: 0;\n", - "height: 100%;\n", - "width: 100%;\n", - "}\n", - "#BoxBR-Panel img {\n", - "bottom: 0%;\n", - "right: 0%;\n", - "}" + "height: 50%;\n", + "width: 50%;\n", + "}", ]) f.close() for (dirpath, dirnames, filenames) in os.walk(os.path.join(path, 'OEBPS', 'Images')): diff --git a/kcc/image.py b/kcc/image.py index b384ba69..31dcba43 100755 --- a/kcc/image.py +++ b/kcc/image.py @@ -152,13 +152,17 @@ def saveToDir(self, targetdir, forcepng, color, wipe, suffix=None): if not color: self.image = self.image.convert('L') # convert to grayscale if suffix == "R": - suffix = "_kccrotated" + suffix = "_kccrot" else: suffix = "" if wipe: os.remove(os.path.join(targetdir, self.filename)) else: suffix += "_kcchq" + if self.noHPV: + suffix += "_kccnh" + if self.noVPV: + suffix += "_kccnv" if self.border: suffix += "_kccx" + str(self.border[0]) + "_kccy" + str(self.border[1]) if forcepng: @@ -207,10 +211,20 @@ def resizeImage(self, upscale=False, stretch=False, bordersColor=None, qualityMo borderh = (self.size[1] - self.image.size[1]) / 2 self.image = ImageOps.expand(self.image, border=(borderw, borderh), fill=fill) if generateBorder: - self.border = [int(round(float(borderw)/float(self.image.size[0])*100, 2)*100*1.5), - int(round(float(borderh)/float(self.image.size[1])*100, 2)*100*1.5)] + if (self.image.size[0]-(2*borderw))*1.5 < self.size[0]: + self.noHPV = True + else: + self.noHPV = None + if (self.image.size[1]-(2*borderh))*1.5 < self.size[1]: + self.noVPV = True + else: + self.noVPV = None + self.border = [int(round(float(borderw)/float(self.image.size[0])*100, 2)*100*1.5), + int(round(float(borderh)/float(self.image.size[1])*100, 2)*100*1.5)] else: self.border = None + self.noHPV = None + self.noVPV = None return self.image else: method = Image.BILINEAR @@ -221,10 +235,20 @@ def resizeImage(self, upscale=False, stretch=False, bordersColor=None, qualityMo border = ImageOps.invert(self.image).getbbox() else: border = self.image.getbbox() + if (border[2]-border[0])*1.5 < self.size[0]: + self.noHPV = True + else: + self.noHPV = None + if (border[3]-border[1])*1.5 < self.size[1]: + self.noVPV = True + else: + self.noVPV = None self.border = [int(round(float(border[0])/float(self.image.size[0])*100, 2)*100*1.5), int(round(float(border[1])/float(self.image.size[1])*100, 2)*100*1.5)] else: self.border = None + self.noHPV = None + self.noVPV = None return self.image ratioDev = float(self.size[0]) / float(self.size[1]) if (float(self.image.size[0]) / float(self.image.size[1])) < ratioDev: @@ -239,10 +263,20 @@ def resizeImage(self, upscale=False, stretch=False, bordersColor=None, qualityMo border = ImageOps.invert(self.image).getbbox() else: border = self.image.getbbox() + if (border[2]-border[0])*1.5 < self.size[0]: + self.noHPV = True + else: + self.noHPV = None + if (border[3]-border[1])*1.5 < self.size[1]: + self.noVPV = True + else: + self.noVPV = None self.border = [int(round(float(border[0])/float(self.image.size[0])*100, 2)*100*1.5), int(round(float(border[1])/float(self.image.size[1])*100, 2)*100*1.5)] else: self.border = None + self.noHPV = None + self.noVPV = None return self.image def splitPage(self, targetdir, righttoleft=False, rotate=False): From 74add23c1414799ae2a4d3aa129365f0f05e498a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Mon, 16 Sep 2013 10:18:26 +0200 Subject: [PATCH 09/26] Panel View - Proper detection of blank pages --- kcc/image.py | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/kcc/image.py b/kcc/image.py index 31dcba43..7ee7182a 100755 --- a/kcc/image.py +++ b/kcc/image.py @@ -235,16 +235,21 @@ def resizeImage(self, upscale=False, stretch=False, bordersColor=None, qualityMo border = ImageOps.invert(self.image).getbbox() else: border = self.image.getbbox() - if (border[2]-border[0])*1.5 < self.size[0]: - self.noHPV = True + if border is not None: + if (border[2]-border[0])*1.5 < self.size[0]: + self.noHPV = True + else: + self.noHPV = None + if (border[3]-border[1])*1.5 < self.size[1]: + self.noVPV = True + else: + self.noVPV = None + self.border = [int(round(float(border[0])/float(self.image.size[0])*100, 2)*100*1.5), + int(round(float(border[1])/float(self.image.size[1])*100, 2)*100*1.5)] else: - self.noHPV = None - if (border[3]-border[1])*1.5 < self.size[1]: + self.border = [0, 0] + self.noHPV = True self.noVPV = True - else: - self.noVPV = None - self.border = [int(round(float(border[0])/float(self.image.size[0])*100, 2)*100*1.5), - int(round(float(border[1])/float(self.image.size[1])*100, 2)*100*1.5)] else: self.border = None self.noHPV = None @@ -263,16 +268,21 @@ def resizeImage(self, upscale=False, stretch=False, bordersColor=None, qualityMo border = ImageOps.invert(self.image).getbbox() else: border = self.image.getbbox() - if (border[2]-border[0])*1.5 < self.size[0]: - self.noHPV = True + if border is not None: + if (border[2]-border[0])*1.5 < self.size[0]: + self.noHPV = True + else: + self.noHPV = None + if (border[3]-border[1])*1.5 < self.size[1]: + self.noVPV = True + else: + self.noVPV = None + self.border = [int(round(float(border[0])/float(self.image.size[0])*100, 2)*100*1.5), + int(round(float(border[1])/float(self.image.size[1])*100, 2)*100*1.5)] else: - self.noHPV = None - if (border[3]-border[1])*1.5 < self.size[1]: + self.border = [0, 0] + self.noHPV = True self.noVPV = True - else: - self.noVPV = None - self.border = [int(round(float(border[0])/float(self.image.size[0])*100, 2)*100*1.5), - int(round(float(border[1])/float(self.image.size[1])*100, 2)*100*1.5)] else: self.border = None self.noHPV = None From 1085673010d833b28e4c2b3528fa1ad9d0ea1444 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Mon, 16 Sep 2013 11:20:42 +0200 Subject: [PATCH 10/26] Fill detection improvements --- kcc/comic2ebook.py | 4 ++-- kcc/image.py | 34 +++++++++++++++++++++++----------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py index b93eb7e3..e79bf244 100755 --- a/kcc/comic2ebook.py +++ b/kcc/comic2ebook.py @@ -292,9 +292,9 @@ def applyImgOptimization(img, options, overrideQuality=5): img.cutPageNumber() img.optimizeImage(options.gamma) if overrideQuality != 5: - img.resizeImage(options.upscale, options.stretch, options.bordersColor, overrideQuality) + img.resizeImage(options.upscale, options.stretch, options.bordersColor, overrideQuality, options.webtoon) else: - img.resizeImage(options.upscale, options.stretch, options.bordersColor, options.quality) + img.resizeImage(options.upscale, options.stretch, options.bordersColor, options.quality, options.webtoon) if options.forcepng and not options.forcecolor: img.quantizeImage() diff --git a/kcc/image.py b/kcc/image.py index 7ee7182a..60fca707 100755 --- a/kcc/image.py +++ b/kcc/image.py @@ -190,12 +190,12 @@ def quantizeImage(self): palImg.putpalette(self.palette) self.image = self.image.quantize(palette=palImg) - def resizeImage(self, upscale=False, stretch=False, bordersColor=None, qualityMode=0): + def resizeImage(self, upscale=False, stretch=False, bordersColor=None, qualityMode=0, isWebToon=False): method = Image.ANTIALIAS if bordersColor: fill = bordersColor else: - fill = self.getImageFill() + fill = self.getImageFill(isWebToon) if qualityMode == 0: size = (self.size[0], self.size[1]) generateBorder = True @@ -436,21 +436,33 @@ def getImageHistogram(self, image): else: return True - def getImageFill(self): - imageT = self.image.crop((0, 0, self.image.size[0], 1)) - imageB = self.image.crop((0, self.image.size[1]-1, self.image.size[0], self.image.size[1])) + def getImageFill(self, isWebToon): fill = 0 - fill += self.getImageHistogram(imageT) - fill += self.getImageHistogram(imageB) - if fill == 2: - return 'black' - elif fill == 0: - return 'white' + if isWebToon: + imageT = self.image.crop((0, 0, self.image.size[0], 1)) + imageB = self.image.crop((0, self.image.size[1]-1, self.image.size[0], self.image.size[1])) + fill += self.getImageHistogram(imageT) + fill += self.getImageHistogram(imageB) else: imageL = self.image.crop((0, 0, 1, self.image.size[1])) imageR = self.image.crop((self.image.size[0]-1, 0, self.image.size[0], self.image.size[1])) fill += self.getImageHistogram(imageL) fill += self.getImageHistogram(imageR) + if fill == 2: + return 'black' + elif fill == 0: + return 'white' + else: + if isWebToon: + imageL = self.image.crop((0, 0, 1, self.image.size[1])) + imageR = self.image.crop((self.image.size[0]-1, 0, self.image.size[0], self.image.size[1])) + fill += self.getImageHistogram(imageL) + fill += self.getImageHistogram(imageR) + else: + imageT = self.image.crop((0, 0, self.image.size[0], 1)) + imageB = self.image.crop((0, self.image.size[1]-1, self.image.size[0], self.image.size[1])) + fill += self.getImageHistogram(imageT) + fill += self.getImageHistogram(imageB) if fill >= 2: return 'black' else: From 8d5b2a9e88d6d2ee5a5a5e84b273752b614ad740 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Mon, 16 Sep 2013 17:17:13 +0200 Subject: [PATCH 11/26] General improvements --- kcc/comic2ebook.py | 9 +++++---- kcc/image.py | 46 ++++++++++++++-------------------------------- 2 files changed, 19 insertions(+), 36 deletions(-) diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py index e79bf244..a8953209 100755 --- a/kcc/comic2ebook.py +++ b/kcc/comic2ebook.py @@ -372,7 +372,7 @@ def fileImgProcess(work): split = None else: split = img.splitPage(dirpath, options.righttoleft, options.rotate) - if split is not None and split is not "R": + if split is not None: if options.verbose: print "Splitted " + afile output = pagenumber @@ -391,13 +391,14 @@ def fileImgProcess(work): img4.saveToDir(dirpath, options.forcepng, options.forcecolor, True) else: applyImgOptimization(img, options) - img.saveToDir(dirpath, options.forcepng, options.forcecolor, wipe, split) + img.saveToDir(dirpath, options.forcepng, options.forcecolor, wipe) if options.quality == 2: img2 = image.ComicPage(os.path.join(dirpath, afile), options.profileData) - if split == "R": + if img.rotated: img2.image = img2.image.rotate(90) + img2.rotated = True applyImgOptimization(img2, options, 0) - img2.saveToDir(dirpath, options.forcepng, options.forcecolor, True, split) + img2.saveToDir(dirpath, options.forcepng, options.forcecolor, True) return output diff --git a/kcc/image.py b/kcc/image.py index 60fca707..80e2e9af 100755 --- a/kcc/image.py +++ b/kcc/image.py @@ -146,15 +146,18 @@ def __init__(self, source, device): raise RuntimeError('Image file %s is corrupted' % source) self.image = Image.open(source) self.image = self.image.convert('RGB') + self.rotated = None + self.border = None + self.noHPV = None + self.noVPV = None - def saveToDir(self, targetdir, forcepng, color, wipe, suffix=None): + def saveToDir(self, targetdir, forcepng, color, wipe): try: + suffix = "" if not color: self.image = self.image.convert('L') # convert to grayscale - if suffix == "R": - suffix = "_kccrot" - else: - suffix = "" + if self.rotated: + suffix += "_kccrot" if wipe: os.remove(os.path.join(targetdir, self.filename)) else: @@ -213,18 +216,10 @@ def resizeImage(self, upscale=False, stretch=False, bordersColor=None, qualityMo if generateBorder: if (self.image.size[0]-(2*borderw))*1.5 < self.size[0]: self.noHPV = True - else: - self.noHPV = None if (self.image.size[1]-(2*borderh))*1.5 < self.size[1]: self.noVPV = True - else: - self.noVPV = None self.border = [int(round(float(borderw)/float(self.image.size[0])*100, 2)*100*1.5), int(round(float(borderh)/float(self.image.size[1])*100, 2)*100*1.5)] - else: - self.border = None - self.noHPV = None - self.noVPV = None return self.image else: method = Image.BILINEAR @@ -238,22 +233,14 @@ def resizeImage(self, upscale=False, stretch=False, bordersColor=None, qualityMo if border is not None: if (border[2]-border[0])*1.5 < self.size[0]: self.noHPV = True - else: - self.noHPV = None if (border[3]-border[1])*1.5 < self.size[1]: self.noVPV = True - else: - self.noVPV = None self.border = [int(round(float(border[0])/float(self.image.size[0])*100, 2)*100*1.5), int(round(float(border[1])/float(self.image.size[1])*100, 2)*100*1.5)] else: self.border = [0, 0] self.noHPV = True self.noVPV = True - else: - self.border = None - self.noHPV = None - self.noVPV = None return self.image ratioDev = float(self.size[0]) / float(self.size[1]) if (float(self.image.size[0]) / float(self.image.size[1])) < ratioDev: @@ -271,22 +258,14 @@ def resizeImage(self, upscale=False, stretch=False, bordersColor=None, qualityMo if border is not None: if (border[2]-border[0])*1.5 < self.size[0]: self.noHPV = True - else: - self.noHPV = None if (border[3]-border[1])*1.5 < self.size[1]: self.noVPV = True - else: - self.noVPV = None self.border = [int(round(float(border[0])/float(self.image.size[0])*100, 2)*100*1.5), int(round(float(border[1])/float(self.image.size[1])*100, 2)*100*1.5)] else: self.border = [0, 0] self.noHPV = True self.noVPV = True - else: - self.border = None - self.noHPV = None - self.noVPV = None return self.image def splitPage(self, targetdir, righttoleft=False, rotate=False): @@ -296,8 +275,10 @@ def splitPage(self, targetdir, righttoleft=False, rotate=False): if (width > height) != (dstwidth > dstheight): if rotate: self.image = self.image.rotate(90) - return "R" + self.rotated = True + return None else: + self.rotated = False if width > height: # Source is landscape, so split by the width leftbox = (0, 0, width / 2, height) @@ -323,6 +304,7 @@ def splitPage(self, targetdir, righttoleft=False, rotate=False): raise RuntimeError('Cannot write image in directory %s: %s' % (targetdir, e)) return fileone, filetwo else: + self.rotated = False return None def cutPageNumber(self): @@ -438,7 +420,7 @@ def getImageHistogram(self, image): def getImageFill(self, isWebToon): fill = 0 - if isWebToon: + if isWebToon or self.rotated: imageT = self.image.crop((0, 0, self.image.size[0], 1)) imageB = self.image.crop((0, self.image.size[1]-1, self.image.size[0], self.image.size[1])) fill += self.getImageHistogram(imageT) @@ -453,7 +435,7 @@ def getImageFill(self, isWebToon): elif fill == 0: return 'white' else: - if isWebToon: + if isWebToon or self.rotated: imageL = self.image.crop((0, 0, 1, self.image.size[1])) imageR = self.image.crop((self.image.size[0]-1, 0, self.image.size[0], self.image.size[1])) fill += self.getImageHistogram(imageL) From 0ee02f2efde76b499f0b04459b203a40619fcbf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Wed, 18 Sep 2013 10:35:07 +0200 Subject: [PATCH 12/26] Fill detection final improvements --- kcc/image.py | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/kcc/image.py b/kcc/image.py index 80e2e9af..dfa40b5c 100755 --- a/kcc/image.py +++ b/kcc/image.py @@ -421,31 +421,29 @@ def getImageHistogram(self, image): def getImageFill(self, isWebToon): fill = 0 if isWebToon or self.rotated: - imageT = self.image.crop((0, 0, self.image.size[0], 1)) - imageB = self.image.crop((0, self.image.size[1]-1, self.image.size[0], self.image.size[1])) - fill += self.getImageHistogram(imageT) - fill += self.getImageHistogram(imageB) + fill += self.getImageHistogram(self.image.crop((0, 0, self.image.size[0], 1))) + fill += self.getImageHistogram(self.image.crop((0, self.image.size[1]-1, self.image.size[0], + self.image.size[1]))) else: - imageL = self.image.crop((0, 0, 1, self.image.size[1])) - imageR = self.image.crop((self.image.size[0]-1, 0, self.image.size[0], self.image.size[1])) - fill += self.getImageHistogram(imageL) - fill += self.getImageHistogram(imageR) + fill += self.getImageHistogram(self.image.crop((0, 0, 1, self.image.size[1]))) + fill += self.getImageHistogram(self.image.crop((self.image.size[0]-1, 0, self.image.size[0], + self.image.size[1]))) if fill == 2: return 'black' elif fill == 0: return 'white' else: - if isWebToon or self.rotated: - imageL = self.image.crop((0, 0, 1, self.image.size[1])) - imageR = self.image.crop((self.image.size[0]-1, 0, self.image.size[0], self.image.size[1])) - fill += self.getImageHistogram(imageL) - fill += self.getImageHistogram(imageR) + bBox = self.image.getbbox() + wBox = ImageOps.invert(self.image).getbbox() + if bBox is None: + bBox = 0 else: - imageT = self.image.crop((0, 0, self.image.size[0], 1)) - imageB = self.image.crop((0, self.image.size[1]-1, self.image.size[0], self.image.size[1])) - fill += self.getImageHistogram(imageT) - fill += self.getImageHistogram(imageB) - if fill >= 2: - return 'black' + bBox = (bBox[2]-bBox[0])*(bBox[3]-bBox[1]) + if wBox is None: + wBox = 0 else: - return 'white' \ No newline at end of file + wBox = (wBox[2]-wBox[0])*(wBox[3]-wBox[1]) + if wBox <= bBox: + return "white" + else: + return "black" \ No newline at end of file From 431cb73e619e1554dffe26b9a1be6fb34ea1248e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Wed, 18 Sep 2013 12:21:05 +0200 Subject: [PATCH 13/26] GUI tweaks --- KCC-OSX.ui | 8 ++++---- KCC.ui | 2 +- kcc/KCC_gui.py | 28 +++++++++++++++++++++------- kcc/KCC_ui.py | 4 ++-- kcc/KCC_ui_osx.py | 10 +++++----- 5 files changed, 33 insertions(+), 19 deletions(-) diff --git a/KCC-OSX.ui b/KCC-OSX.ui index 33db33ca..b4f690d3 100644 --- a/KCC-OSX.ui +++ b/KCC-OSX.ui @@ -88,7 +88,7 @@ Qt::NoFocus - <html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html> + <html><head/><body><p><span style=" font-size:12pt; font-weight:600; text-decoration: underline;">Unchecked - Nothing<br/></span><span style=" font-size:12pt;">Images smaller than device resolution will not be resized.</span></p><p><span style=" font-size:12pt; font-weight:600; text-decoration: underline;">Indeterminate - Stretching<br/></span><span style=" font-size:12pt;">Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</span></p><p><span style=" font-size:12pt; font-weight:600; text-decoration: underline;">Checked - Upscaling<br/></span><span style=" font-size:12pt;">Images smaller than device resolution will be resized. Aspect ratio will be preserved.</span></p></body></html> Stretch/Upscale @@ -109,7 +109,7 @@ Qt::NoFocus - <html><head/><body><p>Enable auto-splitting of webtoons like <span style=" font-style:italic;">Tower of God</span> or <span style=" font-style:italic;">Noblesse</span>.<br/>Pages with a low width, high height and vertical panel flow.</p></body></html> + <html><head/><body><p><span style=" font-size:12pt;">Enable auto-splitting of webtoons like </span><span style=" font-size:12pt; font-style:italic;">Tower of God</span><span style=" font-size:12pt;"> or </span><span style=" font-size:12pt; font-style:italic;">Noblesse</span><span style=" font-size:12pt;">.<br/>Pages with a low width, high height and vertical panel flow.</span></p></body></html> Webtoon mode @@ -127,7 +127,7 @@ Qt::NoFocus - <html><head/><body><p><span style=" font-size:12pt;">Create PNG files instead JPEG.<br/></span><span style=" font-size:12pt; font-weight:600;">Only for non-Kindle devices!</span></p></body></html> + <html><head/><body><p><span style=" font-size:12pt;">Create PNG files instead JPEG.</span></p></body></html> PNG output @@ -145,7 +145,7 @@ Qt::NoFocus - <html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Autodetection<br/></span>Color of margins fill will be detected automatically.</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - White<br/></span>Margins will be filled with white color.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Black<br/></span>Margins will be filled with black color.</p></body></html> + <html><head/><body><p><span style=" font-size:12pt; font-weight:600; text-decoration: underline;">Unchecked - Autodetection<br/></span><span style=" font-size:12pt;">Color of margins fill will be detected automatically.</span></p><p><span style=" font-size:12pt; font-weight:600; text-decoration: underline;">Indeterminate - White<br/></span><span style=" font-size:12pt;">Margins will be filled with white color.</span></p><p><span style=" font-size:12pt; font-weight:600; text-decoration: underline;">Checked - Black<br/></span><span style=" font-size:12pt;">Margins will be filled with black color.</span></p></body></html> W/B margins diff --git a/KCC.ui b/KCC.ui index a627a2d4..9d90febd 100644 --- a/KCC.ui +++ b/KCC.ui @@ -110,7 +110,7 @@ Qt::NoFocus - <html><head/><body><p>Create PNG files instead JPEG.<br/><span style=" font-weight:600;">Only for non-Kindle devices!</span></p></body></html> + <html><head/><body><p>Create PNG files instead JPEG.</p></body></html> PNG output diff --git a/kcc/KCC_gui.py b/kcc/KCC_gui.py index 1f6e2893..fda5a2ed 100644 --- a/kcc/KCC_gui.py +++ b/kcc/KCC_gui.py @@ -440,15 +440,12 @@ def toggleWebtoonBox(self, value): GUI.NoRotateBox.setChecked(True) GUI.QualityBox.setEnabled(False) GUI.QualityBox.setChecked(False) - GUI.BorderBox.setEnabled(False) - GUI.BorderBox.setChecked(False) GUI.MangaBox.setEnabled(False) GUI.MangaBox.setChecked(False) self.addMessage('If images are color setting Gamma to 1.0 is recommended.', 'info') else: GUI.NoRotateBox.setEnabled(True) GUI.QualityBox.setEnabled(True) - GUI.BorderBox.setEnabled(True) GUI.MangaBox.setEnabled(True) def toggleNoSplitRotate(self, value): @@ -477,7 +474,13 @@ def changeDevice(self, value): GUI.QualityBox.setCheckState(0) GUI.QualityBox.setEnabled(False) else: - GUI.QualityBox.setEnabled(True) + if not GUI.WebtoonBox.isChecked(): + GUI.QualityBox.setEnabled(True) + if not value in [8]: + GUI.NoDitheringBox.setCheckState(0) + GUI.NoDitheringBox.setEnabled(False) + else: + GUI.NoDitheringBox.setEnabled(True) def stripTags(self, html): s = HTMLStripper() @@ -549,10 +552,12 @@ def saveSettings(self, event): event.ignore() if not GUI.ConvertButton.isEnabled(): event.ignore() + self.settings.setValue('settingsVersion', __version__) self.settings.setValue('lastPath', self.lastPath) self.settings.setValue('lastDevice', GUI.DeviceBox.currentIndex()) self.settings.setValue('currentFormat', GUI.FormatBox.currentIndex()) self.settings.setValue('currentMode', self.currentMode) + self.settings.setValue('firstStart', False) self.settings.setValue('options', QtCore.QVariant({'MangaBox': GUI.MangaBox.checkState(), 'RotateBox': GUI.RotateBox.checkState(), 'QualityBox': GUI.QualityBox.checkState(), @@ -572,12 +577,20 @@ def __init__(self, UI, KCC): global GUI, MainWindow GUI = UI MainWindow = KCC + # User settings will be reverted to default ones if were created in one of the following versions + # Empty string cover all versions before this system was implemented + purgeSettingsVersions = [''] self.icons = Icons() self.settings = QtCore.QSettings('KindleComicConverter', 'KindleComicConverter') + self.settingsVersion = self.settings.value('settingsVersion', '', type=str) + if self.settingsVersion in purgeSettingsVersions: + QtCore.QSettings.clear(self.settings) + self.settingsVersion = self.settings.value('settingsVersion', '', type=str) self.lastPath = self.settings.value('lastPath', '', type=str) - self.lastDevice = self.settings.value('lastDevice', 10, type=int) + self.lastDevice = self.settings.value('lastDevice', 0, type=int) self.currentMode = self.settings.value('currentMode', 1, type=int) self.currentFormat = self.settings.value('currentFormat', 0, type=int) + self.firstStart = self.settings.value('firstStart', True, type=bool) self.options = self.settings.value('options', QtCore.QVariant({'GammaSlider': 0})) self.options = self.options.toPyObject() self.worker = WorkerThread(self) @@ -587,6 +600,9 @@ def __init__(self, UI, KCC): self.addMessage('Welcome!', 'info') self.addMessage('Remember: All options have additional informations in tooltips.', 'info') + if self.firstStart: + self.addMessage('Since you are using KCC for first time please see few ' + 'important tips.', 'info') if call('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, shell=True) == 0: self.KindleGen = True formats = ['MOBI', 'EPUB', 'CBZ'] @@ -660,8 +676,6 @@ def __init__(self, UI, KCC): elif str(option) == "GammaSlider": GUI.GammaSlider.setValue(int(self.options[option])) self.changeGamma(int(self.options[option])) - elif str(option) == "StretchBox" or str(option) == "WebstripBox": - pass else: eval('GUI.' + str(option)).setCheckState(self.options[option]) if self.currentMode == 1: diff --git a/kcc/KCC_ui.py b/kcc/KCC_ui.py index 2fd98d83..a997ead3 100644 --- a/kcc/KCC_ui.py +++ b/kcc/KCC_ui.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'KCC.ui' # -# Created: Sun Sep 15 16:18:37 2013 +# Created: Wed Sep 18 12:12:45 2013 # by: PyQt4 UI code generator 4.10.3 # # WARNING! All changes made in this file will be lost! @@ -269,7 +269,7 @@ def retranslateUi(self, KCC): self.UpscaleBox.setText(_translate("KCC", "Stretch/Upscale", None)) self.WebtoonBox.setToolTip(_translate("KCC", "

Enable auto-splitting of webtoons like Tower of God or Noblesse.
Pages with a low width, high height and vertical panel flow.

", None)) self.WebtoonBox.setText(_translate("KCC", "Webtoon mode", None)) - self.NoDitheringBox.setToolTip(_translate("KCC", "

Create PNG files instead JPEG.
Only for non-Kindle devices!

", None)) + self.NoDitheringBox.setToolTip(_translate("KCC", "

Create PNG files instead JPEG.

", None)) self.NoDitheringBox.setText(_translate("KCC", "PNG output", None)) self.BorderBox.setToolTip(_translate("KCC", "

Unchecked - Autodetection
Color of margins fill will be detected automatically.

Indeterminate - White
Margins will be filled with white color.

Checked - Black
Margins will be filled with black color.

", None)) self.BorderBox.setText(_translate("KCC", "W/B margins", None)) diff --git a/kcc/KCC_ui_osx.py b/kcc/KCC_ui_osx.py index 84474e5f..8102940f 100644 --- a/kcc/KCC_ui_osx.py +++ b/kcc/KCC_ui_osx.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'KCC-OSX.ui' # -# Created: Sun Sep 15 16:18:48 2013 +# Created: Wed Sep 18 12:13:05 2013 # by: PyQt4 UI code generator 4.10.3 # # WARNING! All changes made in this file will be lost! @@ -323,13 +323,13 @@ def retranslateUi(self, KCC): KCC.setWindowTitle(_translate("KCC", "Kindle Comic Converter", None)) self.ProcessingBox.setToolTip(_translate("KCC", "

Disable image optimizations.

", None)) self.ProcessingBox.setText(_translate("KCC", "No optimisation", None)) - self.UpscaleBox.setToolTip(_translate("KCC", "

Unchecked - Nothing
Images smaller than device resolution will not be resized.

Indeterminate - Stretching
Images smaller than device resolution will be resized. Aspect ratio will be not preserved.

Checked - Upscaling
Images smaller than device resolution will be resized. Aspect ratio will be preserved.

", None)) + self.UpscaleBox.setToolTip(_translate("KCC", "

Unchecked - Nothing
Images smaller than device resolution will not be resized.

Indeterminate - Stretching
Images smaller than device resolution will be resized. Aspect ratio will be not preserved.

Checked - Upscaling
Images smaller than device resolution will be resized. Aspect ratio will be preserved.

", None)) self.UpscaleBox.setText(_translate("KCC", "Stretch/Upscale", None)) - self.WebtoonBox.setToolTip(_translate("KCC", "

Enable auto-splitting of webtoons like Tower of God or Noblesse.
Pages with a low width, high height and vertical panel flow.

", None)) + self.WebtoonBox.setToolTip(_translate("KCC", "

Enable auto-splitting of webtoons like Tower of God or Noblesse.
Pages with a low width, high height and vertical panel flow.

", None)) self.WebtoonBox.setText(_translate("KCC", "Webtoon mode", None)) - self.NoDitheringBox.setToolTip(_translate("KCC", "

Create PNG files instead JPEG.
Only for non-Kindle devices!

", None)) + self.NoDitheringBox.setToolTip(_translate("KCC", "

Create PNG files instead JPEG.

", None)) self.NoDitheringBox.setText(_translate("KCC", "PNG output", None)) - self.BorderBox.setToolTip(_translate("KCC", "

Unchecked - Autodetection
Color of margins fill will be detected automatically.

Indeterminate - White
Margins will be filled with white color.

Checked - Black
Margins will be filled with black color.

", None)) + self.BorderBox.setToolTip(_translate("KCC", "

Unchecked - Autodetection
Color of margins fill will be detected automatically.

Indeterminate - White
Margins will be filled with white color.

Checked - Black
Margins will be filled with black color.

", None)) self.BorderBox.setText(_translate("KCC", "W/B margins", None)) self.NoRotateBox.setToolTip(_translate("KCC", "

Disable splitting and rotation.

", None)) self.NoRotateBox.setText(_translate("KCC", "No split/rotate", None)) From a58d98f0dc6d1306c11bee729436f475cd7b44d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Wed, 18 Sep 2013 12:47:33 +0200 Subject: [PATCH 14/26] Updated README and version bump --- README.md | 24 +++++++++++++++--------- kcc.py | 5 +++-- kcc/KCC_gui.py | 5 +++-- kcc/__init__.py | 2 +- kcc/cbxarchive.py | 3 ++- kcc/comic2ebook.py | 11 +++++------ kcc/comic2panel.py | 5 +++-- kcc/image.py | 1 + kcc/pdfjpgextract.py | 3 ++- setup.py | 2 +- 10 files changed, 36 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 57dbb5fe..5cae032f 100644 --- a/README.md +++ b/README.md @@ -67,12 +67,10 @@ Usage: comic2ebook.py [options] comic_file|comic_folder Options: MAIN: -p PROFILE, --profile=PROFILE - Device profile (Choose one among K1, K2, K3, K4NT, K4T, KDX, KDXG, KHD, KF, KFHD, KFHD8, KFA) [Default=KHD] + Device profile (Choose one among K1, K2, K345, KDX, KDXG, KHD, KF, KFHD, KFHD8, KFA) [Default=KHD] -q QUALITY, --quality=QUALITY Quality of Panel View. 0 - Normal 1 - High 2 - Ultra [Default=0] -m, --manga-style Manga style (Right-to-left reading and splitting) - - EXPERIMENTAL: -w, --webtoon Webtoon processing mode OUTPUT SETTINGS: @@ -84,7 +82,8 @@ Options: --batchsplit Split output into multiple files PROCESSING: - --blackborders Use black borders instead of white ones + --blackborders Disable autodetection and force black borders + --whiteborders Disable autodetection and force white borders --forcecolor Don't convert images to grayscale --forcepng Create PNG files instead JPEG (For non-Kindle devices) --gamma=GAMMA Apply gamma correction to linearize the image [Default=Auto] @@ -136,15 +135,13 @@ The app relies and includes the following scripts/binaries: - Icon is by **Nikolay Verin** ([http://ncrow.deviantart.com/](http://ncrow.deviantart.com/)) and released under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/) License ## SAMPLE FILES CREATED BY KCC -* [Kindle Keyboard](http://kcc.vulturis.eu/Samples/Ubunchu!-K3.mobi) -* [Kindle DX](http://kcc.vulturis.eu/Samples/Ubunchu!-KDX.mobi) -* [Kindle DXG](http://kcc.vulturis.eu/Samples/Ubunchu!-KDXG.mobi) -* [Kindle Non-Touch](http://kcc.vulturis.eu/Samples/Ubunchu!-K4NT.mobi) -* [Kindle Touch](http://kcc.vulturis.eu/Samples/Ubunchu!-K4T.mobi) * [Kindle Paperwhite](http://kcc.vulturis.eu/Samples/Ubunchu!-KPW.mobi) +* [Kindle](http://kcc.vulturis.eu/Samples/Ubunchu!-K345.mobi) * [Kindle Fire](http://kcc.vulturis.eu/Samples/Ubunchu!-KF.mobi) * [Kindle Fire HD](http://kcc.vulturis.eu/Samples/Ubunchu!-KFHD.mobi) * [Kindle Fire HD 8.9"](http://kcc.vulturis.eu/Samples/Ubunchu!-KFHD8.mobi) +* [Kindle DX](http://kcc.vulturis.eu/Samples/Ubunchu!-KDX.mobi) +* [Kindle DXG](http://kcc.vulturis.eu/Samples/Ubunchu!-KDXG.mobi) ## CHANGELOG ####1.00 @@ -255,6 +252,15 @@ The app relies and includes the following scripts/binaries: ####3.2.1: * Hotfixed crash occurring on OS with Russian locale +####3.3: +* Margins are now automatically omitted in Panel View mode +* Layout of panels in Panel View mode is now automatically adjusted to content +* Support for Virtual Panel View was removed +* Margin color fill is now autodetected +* Profiles for Kindle Keyboard, Touch and Non-Touch are now merged +* Windows release is now bundled with UnRAR +* Small GUI tweaks + ## KNOWN ISSUES * Removing SRCS headers sometimes fail in 32bit enviroments. Due to memory limitations. diff --git a/kcc.py b/kcc.py index 5f4a4f94..bf341ce2 100644 --- a/kcc.py +++ b/kcc.py @@ -1,7 +1,8 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # -# Copyright (c) 2012 Ciro Mattia Gonano +# Copyright (c) 2012-2013 Ciro Mattia Gonano +# Copyright (c) 2013 Pawel Jastrzebski # # Permission to use, copy, modify, and/or distribute this software for # any purpose with or without fee is hereby granted, provided that the @@ -17,7 +18,7 @@ # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -__version__ = '3.2.1' +__version__ = '3.3' __license__ = 'ISC' __copyright__ = '2012-2013, Ciro Mattia Gonano , Pawel Jastrzebski ' __docformat__ = 'restructuredtext en' diff --git a/kcc/KCC_gui.py b/kcc/KCC_gui.py index fda5a2ed..6945e801 100644 --- a/kcc/KCC_gui.py +++ b/kcc/KCC_gui.py @@ -1,7 +1,8 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # -# Copyright (c) 2013 Ciro Mattia Gonano +# Copyright (c) 2012-2013 Ciro Mattia Gonano +# Copyright (c) 2013 Pawel Jastrzebski # # Permission to use, copy, modify, and/or distribute this software for # any purpose with or without fee is hereby granted, provided that the @@ -17,7 +18,7 @@ # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -__version__ = '3.2.1' +__version__ = '3.3' __license__ = 'ISC' __copyright__ = '2012-2013, Ciro Mattia Gonano , Pawel Jastrzebski ' __docformat__ = 'restructuredtext en' diff --git a/kcc/__init__.py b/kcc/__init__.py index f9bbb7e5..9e6b2348 100644 --- a/kcc/__init__.py +++ b/kcc/__init__.py @@ -1,4 +1,4 @@ -__version__ = '3.2.1' +__version__ = '3.3' __license__ = 'ISC' __copyright__ = '2012-2013, Ciro Mattia Gonano , Pawel Jastrzebski ' __docformat__ = 'restructuredtext en' \ No newline at end of file diff --git a/kcc/cbxarchive.py b/kcc/cbxarchive.py index d4b8a864..a2138f55 100644 --- a/kcc/cbxarchive.py +++ b/kcc/cbxarchive.py @@ -1,4 +1,5 @@ -# Copyright (c) 2012 Ciro Mattia Gonano +# Copyright (c) 2012-2013 Ciro Mattia Gonano +# Copyright (c) 2013 Pawel Jastrzebski # # Permission to use, copy, modify, and/or distribute this software for # any purpose with or without fee is hereby granted, provided that the diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py index a8953209..e4df1ec6 100755 --- a/kcc/comic2ebook.py +++ b/kcc/comic2ebook.py @@ -1,7 +1,8 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # -# Copyright (c) 2012 Ciro Mattia Gonano +# Copyright (c) 2012-2013 Ciro Mattia Gonano +# Copyright (c) 2013 Pawel Jastrzebski # # Permission to use, copy, modify, and/or distribute this software for # any purpose with or without fee is hereby granted, provided that the @@ -17,7 +18,7 @@ # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. # -__version__ = '3.2.1' +__version__ = '3.3' __license__ = 'ISC' __copyright__ = '2012-2013, Ciro Mattia Gonano , Pawel Jastrzebski ' __docformat__ = 'restructuredtext en' @@ -789,7 +790,6 @@ def main(argv=None, qtGUI=None): global parser, options, epub_path, GUI parser = OptionParser(usage="Usage: %prog [options] comic_file|comic_folder", add_help_option=False) mainOptions = OptionGroup(parser, "MAIN") - experimentalOptions = OptionGroup(parser, "EXPERIMENTAL") processingOptions = OptionGroup(parser, "PROCESSING") outputOptions = OptionGroup(parser, "OUTPUT SETTINGS") customProfileOptions = OptionGroup(parser, "CUSTOM PROFILE") @@ -801,6 +801,8 @@ def main(argv=None, qtGUI=None): 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)") + mainOptions.add_option("-w", "--webtoon", action="store_true", dest="webtoon", default=False, + help="Webtoon processing mode"), outputOptions.add_option("-o", "--output", action="store", dest="output", default=None, help="Output generated file to specified directory or file") outputOptions.add_option("-t", "--title", action="store", dest="title", default="defaulttitle", @@ -809,8 +811,6 @@ def main(argv=None, qtGUI=None): help="Outputs a CBZ archive and does not generate EPUB") outputOptions.add_option("--batchsplit", action="store_true", dest="batchsplit", default=False, help="Split output into multiple files"), - experimentalOptions.add_option("-w", "--webtoon", action="store_true", dest="webtoon", default=False, - help="Webtoon processing mode"), 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, @@ -842,7 +842,6 @@ def main(argv=None, qtGUI=None): otherOptions.add_option("-h", "--help", action="help", help="Show this help message and exit") parser.add_option_group(mainOptions) - parser.add_option_group(experimentalOptions) parser.add_option_group(outputOptions) parser.add_option_group(processingOptions) parser.add_option_group(customProfileOptions) diff --git a/kcc/comic2panel.py b/kcc/comic2panel.py index f9a52b16..8b2e8773 100644 --- a/kcc/comic2panel.py +++ b/kcc/comic2panel.py @@ -1,7 +1,8 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # -# Copyright (c) 2012 Ciro Mattia Gonano +# Copyright (c) 2012-2013 Ciro Mattia Gonano +# Copyright (c) 2013 Pawel Jastrzebski # # Permission to use, copy, modify, and/or distribute this software for # any purpose with or without fee is hereby granted, provided that the @@ -17,7 +18,7 @@ # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. # -__version__ = '3.2.1' +__version__ = '3.3' __license__ = 'ISC' __copyright__ = '2012-2013, Ciro Mattia Gonano , Pawel Jastrzebski ' __docformat__ = 'restructuredtext en' diff --git a/kcc/image.py b/kcc/image.py index dfa40b5c..eae916f4 100755 --- a/kcc/image.py +++ b/kcc/image.py @@ -1,6 +1,7 @@ # Copyright (C) 2010 Alex Yatskov # Copyright (C) 2011 Stanislav (proDOOMman) Kosolapov # Copyright (C) 2012-2013 Ciro Mattia Gonano +# Copyright (C) 2013 Pawel Jastrzebski # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/kcc/pdfjpgextract.py b/kcc/pdfjpgextract.py index dd5f0673..d9dad09d 100644 --- a/kcc/pdfjpgextract.py +++ b/kcc/pdfjpgextract.py @@ -1,4 +1,5 @@ -# Copyright (c) 2012 Ciro Mattia Gonano +# Copyright (c) 2012-2013 Ciro Mattia Gonano +# Copyright (c) 2013 Pawel Jastrzebski # # Based upon the code snippet by Ned Batchelder # (http://nedbatchelder.com/blog/200712/extracting_jpgs_from_pdfs.html) diff --git a/setup.py b/setup.py index ebe2bf39..9d95d635 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ from sys import platform NAME = "KindleComicConverter" -VERSION = "3.2.1" +VERSION = "3.3" MAIN = "kcc.py" if platform == "darwin": From ab8effbc3238355278a398a3646850fbcb449bb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Thu, 19 Sep 2013 11:01:15 +0200 Subject: [PATCH 15/26] Added 7z/CB7 support --- .gitignore | 1 + README.md | 7 ++- kcc/KCC_gui.py | 23 +++++++-- kcc/cbxarchive.py | 15 ++++++ kcc/comic2ebook.py | 3 +- other/Additional-LICENSE.txt | 91 ++++++++++++++++++++++++++++++++++++ setup.py | 6 ++- 7 files changed, 137 insertions(+), 9 deletions(-) create mode 100644 other/Additional-LICENSE.txt diff --git a/.gitignore b/.gitignore index dc4e08c4..14d4c97f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ kindlegen* .DS_Store Thumbs.db /UnRAR.exe +/7za.exe diff --git a/README.md b/README.md index 5cae032f..7a2ef489 100644 --- a/README.md +++ b/README.md @@ -30,11 +30,13 @@ You can find the latest released binary at the following links: - Folders - CBZ, ZIP - CBR, RAR *(With `unrar` executable)* +- CB7, 7Z *(With `7za` executable)* - PDF *(Extracting only contained JPG images)* ## OPTIONAL REQUIREMENTS - [KindleGen](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211) v2.9+ in a directory reachable by your _PATH_ or in _KCC_ directory *(For .mobi generation)* - [UnRAR](http://www.rarlab.com/download.htm) *(For CBR/RAR support)* +- [7za](http://www.7-zip.org/download.html) *(For 7z/CB7 support)* ### For compiling/running from source: - Python 2.7 - Included in MacOS and Linux, follow the [official documentation](http://www.python.org/getit/windows/) to install on Windows. @@ -52,7 +54,7 @@ You can find the latest released binary at the following links: * Check our [wiki](https://github.com/ciromattia/kcc/wiki/Other-devices) for a list of tested Non-Kindle E-Readers. * The first image found will be set as the comic's cover. * All files/directories will be added to EPUB in alphabetical order. -* Output MOBI file should be uploaded via USB. Other methods (e.g. via Calibre) might corrupt it. +* Output MOBI file should be uploaded via USB. Other methods might corrupt it. ### GUI @@ -257,8 +259,9 @@ The app relies and includes the following scripts/binaries: * Layout of panels in Panel View mode is now automatically adjusted to content * Support for Virtual Panel View was removed * Margin color fill is now autodetected +* Added support of 7z/CB7 files * Profiles for Kindle Keyboard, Touch and Non-Touch are now merged -* Windows release is now bundled with UnRAR +* Windows release is now bundled with UnRAR and 7za * Small GUI tweaks ## KNOWN ISSUES diff --git a/kcc/KCC_gui.py b/kcc/KCC_gui.py index 6945e801..e047446a 100644 --- a/kcc/KCC_gui.py +++ b/kcc/KCC_gui.py @@ -321,11 +321,19 @@ def selectFile(self): self.needClean = False GUI.JobList.clear() if self.UnRAR: - fnames = QtGui.QFileDialog.getOpenFileNames(MainWindow, 'Select file', self.lastPath, - '*.cbz *.cbr *.zip *.rar *.pdf') + if self.sevenza: + fnames = QtGui.QFileDialog.getOpenFileNames(MainWindow, 'Select file', self.lastPath, + '*.cbz *.cbr *.cb7 *.zip *.rar *.7z *.pdf') + else: + fnames = QtGui.QFileDialog.getOpenFileNames(MainWindow, 'Select file', self.lastPath, + '*.cbz *.cbr *.zip *.rar *.pdf') else: - fnames = QtGui.QFileDialog.getOpenFileNames(MainWindow, 'Select file', self.lastPath, - '*.cbz *.zip *.pdf') + if self.sevenza: + fnames = QtGui.QFileDialog.getOpenFileNames(MainWindow, 'Select file', self.lastPath, + '*.cbz *.cb7 *.zip *.7z *.pdf') + else: + fnames = QtGui.QFileDialog.getOpenFileNames(MainWindow, 'Select file', self.lastPath, + '*.cbz *.zip *.pdf') # Lame UTF-8 security measure for fname in fnames: try: @@ -629,6 +637,13 @@ def __init__(self, UI, KCC): self.UnRAR = False self.addMessage('Cannot find
UnRAR!' ' Processing of CBR/RAR files will be disabled.', 'warning') + sevenzaExitCode = call('7za', stdout=PIPE, stderr=STDOUT, shell=True) + if sevenzaExitCode == 0 or sevenzaExitCode == 7: + self.sevenza = True + else: + self.sevenza = False + self.addMessage('Cannot find Date: Thu, 19 Sep 2013 17:46:08 +0200 Subject: [PATCH 16/26] Fixed stupid typos --- kcc/KCC_gui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kcc/KCC_gui.py b/kcc/KCC_gui.py index e047446a..9ac509d9 100644 --- a/kcc/KCC_gui.py +++ b/kcc/KCC_gui.py @@ -611,7 +611,7 @@ def __init__(self, UI, KCC): self.addMessage('Remember: All options have additional informations in tooltips.', 'info') if self.firstStart: self.addMessage('Since you are using KCC for first time please see few ' - 'important tips.', 'info') + 'important tips.', 'info') if call('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, shell=True) == 0: self.KindleGen = True formats = ['MOBI', 'EPUB', 'CBZ'] @@ -642,7 +642,7 @@ def __init__(self, UI, KCC): self.sevenza = True else: self.sevenza = False - self.addMessage('Cannot find 7za!' ' Processing of CB7/7Z files will be disabled.', 'warning') GUI.BasicModeButton.clicked.connect(self.modeBasic) From 727bbba8151de2c968239ea389ce642e7cbba959 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Fri, 20 Sep 2013 10:39:38 +0200 Subject: [PATCH 17/26] Linux GUI --- KCC-Linux.ui | 812 ++++++++++++++++++++++++++++++++++++++++++++ kcc.py | 4 +- kcc/KCC_ui_linux.py | 383 +++++++++++++++++++++ 3 files changed, 1198 insertions(+), 1 deletion(-) create mode 100644 KCC-Linux.ui create mode 100644 kcc/KCC_ui_linux.py diff --git a/KCC-Linux.ui b/KCC-Linux.ui new file mode 100644 index 00000000..40d408b8 --- /dev/null +++ b/KCC-Linux.ui @@ -0,0 +1,812 @@ + + + KCC + + + + 0 + 0 + 420 + 380 + + + + + 420 + 380 + + + + + 420 + 380 + + + + + 9 + + + + Qt::NoFocus + + + Kindle Comic Converter + + + + :/Icon/icons/comic2ebook.png:/Icon/icons/comic2ebook.png + + + + + + + + true + + + + 1 + 254 + 421 + 61 + + + + + DejaVu Sans + 9 + + + + + 9 + + + + + + DejaVu Sans + + + + Qt::NoFocus + + + Disable image optimizations. + + + No optimisation + + + + + + + + DejaVu Sans + + + + Qt::NoFocus + + + <html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html> + + + Stretch/Upscale + + + true + + + + + + + + DejaVu Sans + + + + Qt::NoFocus + + + <html><head/><body><p>Enable auto-splitting of webtoons like <span style=" font-style:italic;">Tower of God</span> or <span style=" font-style:italic;">Noblesse</span>.<br/>Pages with a low width, high height and vertical panel flow.</p></body></html> + + + Webtoon mode + + + + + + + + DejaVu Sans + + + + Qt::NoFocus + + + <html><head/><body><p>Create PNG files instead JPEG.</p></body></html> + + + PNG output + + + + + + + + DejaVu Sans + + + + Qt::NoFocus + + + <html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Autodetection<br/></span>Color of margins fill will be detected automatically.</p><p><span style=" font-weight:600; text-decoration: underline;">Indeterminate - White<br/></span>Margins will be filled with white color.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Black<br/></span>Margins will be filled with black color.</p></body></html> + + + W/B margins + + + true + + + + + + + + DejaVu Sans + + + + Qt::NoFocus + + + <html><head/><body><p>Disable splitting and rotation.</p></body></html> + + + No split/rotate + + + + + + + + + 10 + 200 + 141 + 31 + + + + + DejaVu Sans + 8 + + + + Qt::NoFocus + + + Target device. + + + + + + 260 + 200 + 151 + 31 + + + + + DejaVu Sans + 8 + + + + Qt::NoFocus + + + Output format. + + + + + + 160 + 200 + 91 + 32 + + + + + DejaVu Sans + 9 + 75 + true + + + + Qt::NoFocus + + + Convert + + + + :/Other/icons/convert.png:/Other/icons/convert.png + + + + + + 10 + 160 + 141 + 32 + + + + + DejaVu Sans + 8 + + + + Qt::NoFocus + + + Add directory + + + + :/Other/icons/folder_new.png:/Other/icons/folder_new.png + + + + + + 260 + 160 + 151 + 32 + + + + + DejaVu Sans + 8 + + + + Qt::NoFocus + + + Add file + + + + :/Other/icons/document_new.png:/Other/icons/document_new.png + + + + + + 160 + 160 + 91 + 32 + + + + + DejaVu Sans + 8 + + + + Qt::NoFocus + + + Clear list + + + + :/Other/icons/clear.png:/Other/icons/clear.png + + + + + + 1 + 230 + 421 + 41 + + + + + DejaVu Sans + 9 + + + + + + 9 + 10 + 130 + 18 + + + + + DejaVu Sans + + + + Qt::NoFocus + + + Enable right-to-left reading. + + + Manga mode + + + + + + 282 + 10 + 135 + 18 + + + + + DejaVu Sans + + + + Qt::NoFocus + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-weight:600; text-decoration: underline;">Unchecked - Normal quality mode<br /></span><span style=" font-family:'MS Shell Dlg 2'; font-style:italic;">Use it when Panel View support is not needed.</span><span style=" font-family:'MS Shell Dlg 2'; font-weight:600; text-decoration: underline;"><br /></span><span style=" font-family:'MS Shell Dlg 2';">- Maximum quality when zoom is not enabled.<br />- Poor quality when zoom is enabled.<br />- Lowest file size.</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-weight:600; text-decoration: underline;">Indeterminate - High quality mode<br /></span><span style=" font-family:'MS Shell Dlg 2'; font-style:italic;">Not zoomed image </span><span style=" font-family:'MS Shell Dlg 2'; font-weight:600; font-style:italic;">might </span><span style=" font-family:'MS Shell Dlg 2'; font-style:italic;">be a little blurry.</span><span style=" font-family:'MS Shell Dlg 2'; font-weight:600; text-decoration: underline;"><br /></span><span style=" font-family:'MS Shell Dlg 2';">- Medium/High quality when zoom is not enabled.<br />- Maximum quality when zoom is enabled.</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-weight:600; text-decoration: underline;">Checked - Ultra quality mode<br /></span><span style=" font-family:'MS Shell Dlg 2'; font-style:italic;">Maximum possible quality.</span><span style=" font-family:'MS Shell Dlg 2'; font-weight:600; text-decoration: underline;"><br /></span><span style=" font-family:'MS Shell Dlg 2';">- Maximum quality when zoom is not enabled.<br />- Maximum quality when zoom is enabled.<br />- Very high file size.</span></p></body></html> + + + High/Ultra quality + + + true + + + + + + 145 + 10 + 130 + 18 + + + + + DejaVu Sans + + + + Qt::NoFocus + + + <html><head/><body><p>Disable page spliting.<br/>They will be rotated instead.</p></body></html> + + + Horizontal mode + + + RotateBox + MangaBox + QualityBox + + + + + 10 + 50 + 401 + 101 + + + + + DejaVu Sans + 8 + false + + + + Qt::NoFocus + + + false + + + QAbstractItemView::NoSelection + + + + 18 + 18 + + + + + + + 10 + 10 + 195 + 32 + + + + + DejaVu Sans + 9 + + + + Qt::NoFocus + + + Basic + + + + + + 217 + 10 + 195 + 32 + + + + + DejaVu Sans + 9 + + + + Qt::NoFocus + + + Advanced + + + + + true + + + + 10 + 305 + 401 + 41 + + + + + DejaVu Sans + 9 + + + + + + 15 + 0 + 100 + 40 + + + + + DejaVu Sans + + + + <html><head/><body><p>When converting color images setting this option to 1.0 <span style=" font-weight:600;">might</span> improve readability.</p></body></html> + + + Gamma: Auto + + + + + + 110 + 10 + 275 + 22 + + + + + DejaVu Sans + + + + Qt::ClickFocus + + + <html><head/><body><p>When converting color images setting this option to 1.0 <span style=" font-weight:600;">might</span> improve readability.</p></body></html> + + + 500 + + + 5 + + + Qt::Horizontal + + + + + + + 10 + 10 + 401 + 31 + + + + + DejaVu Sans + 10 + 75 + true + + + + 0 + + + Qt::AlignJustify|Qt::AlignVCenter + + + + + + + + + 1 + 337 + 421 + 41 + + + + + DejaVu Sans + 9 + + + + + + 9 + 11 + 130 + 18 + + + + + DejaVu Sans + + + + Qt::NoFocus + + + Do not convert images to grayscale. + + + Color mode + + + + + + 105 + 0 + 295 + 40 + + + + + DejaVu Sans + + + + + + + + DejaVu Sans + + + + Resolution of target device. + + + Custom width: + + + + + + + + 0 + 0 + + + + + 40 + 16777215 + + + + + DejaVu Sans + + + + Qt::ClickFocus + + + false + + + Resolution of target device. + + + 0000; + + + 4 + + + + + + + + DejaVu Sans + + + + Resolution of target device. + + + Custom height: + + + + + + + + 0 + 0 + + + + + 40 + 16777215 + + + + + DejaVu Sans + + + + Qt::ClickFocus + + + false + + + Resolution of target device. + + + 0000; + + + 4 + + + + + + + OptionsAdvanced + DeviceBox + FormatBox + ConvertButton + DirectoryButton + FileButton + ClearButton + OptionsBasic + JobList + BasicModeButton + AdvModeButton + OptionsAdvancedGamma + OptionsExpert + ProgressBar + + + + true + + + false + + + Basic + + + + + + + + true + + + Advanced + + + + + DirectoryButton + FileButton + ConvertButton + ClearButton + + + + + + diff --git a/kcc.py b/kcc.py index bf341ce2..dec47c25 100644 --- a/kcc.py +++ b/kcc.py @@ -33,9 +33,11 @@ exit(1) from kcc import KCC_gui from multiprocessing import freeze_support -if sys.platform == 'darwin': +if sys.platform.startswith('darwin'): os.environ['PATH'] = '/usr/local/bin:' + os.environ['PATH'] from kcc import KCC_ui_osx as KCC_ui +elif sys.platform.startswith('linux'): + from kcc import KCC_ui_linux as KCC_ui else: from kcc import KCC_ui diff --git a/kcc/KCC_ui_linux.py b/kcc/KCC_ui_linux.py new file mode 100644 index 00000000..416bb161 --- /dev/null +++ b/kcc/KCC_ui_linux.py @@ -0,0 +1,383 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'KCC-Linux.ui' +# +# Created: Fri Sep 20 10:25:30 2013 +# by: PyQt4 UI code generator 4.10 +# +# WARNING! All changes made in this file will be lost! + +from PyQt4 import QtCore, QtGui + +try: + _fromUtf8 = QtCore.QString.fromUtf8 +except AttributeError: + def _fromUtf8(s): + return s + +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def _translate(context, text, disambig): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def _translate(context, text, disambig): + return QtGui.QApplication.translate(context, text, disambig) + +class Ui_KCC(object): + def setupUi(self, KCC): + KCC.setObjectName(_fromUtf8("KCC")) + KCC.resize(420, 380) + KCC.setMinimumSize(QtCore.QSize(420, 380)) + KCC.setMaximumSize(QtCore.QSize(420, 380)) + font = QtGui.QFont() + font.setPointSize(9) + KCC.setFont(font) + KCC.setFocusPolicy(QtCore.Qt.NoFocus) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/Icon/icons/comic2ebook.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) + KCC.setWindowIcon(icon) + KCC.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry)) + self.Form = QtGui.QWidget(KCC) + self.Form.setObjectName(_fromUtf8("Form")) + self.OptionsAdvanced = QtGui.QFrame(self.Form) + self.OptionsAdvanced.setEnabled(True) + self.OptionsAdvanced.setGeometry(QtCore.QRect(1, 254, 421, 61)) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + font.setPointSize(9) + self.OptionsAdvanced.setFont(font) + self.OptionsAdvanced.setObjectName(_fromUtf8("OptionsAdvanced")) + self.gridLayout = QtGui.QGridLayout(self.OptionsAdvanced) + self.gridLayout.setContentsMargins(9, -1, -1, -1) + self.gridLayout.setObjectName(_fromUtf8("gridLayout")) + self.ProcessingBox = QtGui.QCheckBox(self.OptionsAdvanced) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + self.ProcessingBox.setFont(font) + self.ProcessingBox.setFocusPolicy(QtCore.Qt.NoFocus) + self.ProcessingBox.setObjectName(_fromUtf8("ProcessingBox")) + self.gridLayout.addWidget(self.ProcessingBox, 1, 0, 1, 1) + self.UpscaleBox = QtGui.QCheckBox(self.OptionsAdvanced) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + self.UpscaleBox.setFont(font) + self.UpscaleBox.setFocusPolicy(QtCore.Qt.NoFocus) + self.UpscaleBox.setTristate(True) + self.UpscaleBox.setObjectName(_fromUtf8("UpscaleBox")) + self.gridLayout.addWidget(self.UpscaleBox, 1, 1, 1, 1) + self.WebtoonBox = QtGui.QCheckBox(self.OptionsAdvanced) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + self.WebtoonBox.setFont(font) + self.WebtoonBox.setFocusPolicy(QtCore.Qt.NoFocus) + self.WebtoonBox.setObjectName(_fromUtf8("WebtoonBox")) + self.gridLayout.addWidget(self.WebtoonBox, 3, 1, 1, 1) + self.NoDitheringBox = QtGui.QCheckBox(self.OptionsAdvanced) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + self.NoDitheringBox.setFont(font) + self.NoDitheringBox.setFocusPolicy(QtCore.Qt.NoFocus) + self.NoDitheringBox.setObjectName(_fromUtf8("NoDitheringBox")) + self.gridLayout.addWidget(self.NoDitheringBox, 3, 2, 1, 1) + self.BorderBox = QtGui.QCheckBox(self.OptionsAdvanced) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + self.BorderBox.setFont(font) + self.BorderBox.setFocusPolicy(QtCore.Qt.NoFocus) + self.BorderBox.setTristate(True) + self.BorderBox.setObjectName(_fromUtf8("BorderBox")) + self.gridLayout.addWidget(self.BorderBox, 3, 0, 1, 1) + self.NoRotateBox = QtGui.QCheckBox(self.OptionsAdvanced) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + self.NoRotateBox.setFont(font) + self.NoRotateBox.setFocusPolicy(QtCore.Qt.NoFocus) + self.NoRotateBox.setObjectName(_fromUtf8("NoRotateBox")) + self.gridLayout.addWidget(self.NoRotateBox, 1, 2, 1, 1) + self.DeviceBox = QtGui.QComboBox(self.Form) + self.DeviceBox.setGeometry(QtCore.QRect(10, 200, 141, 31)) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + font.setPointSize(8) + self.DeviceBox.setFont(font) + self.DeviceBox.setFocusPolicy(QtCore.Qt.NoFocus) + self.DeviceBox.setObjectName(_fromUtf8("DeviceBox")) + self.FormatBox = QtGui.QComboBox(self.Form) + self.FormatBox.setGeometry(QtCore.QRect(260, 200, 151, 31)) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + font.setPointSize(8) + self.FormatBox.setFont(font) + self.FormatBox.setFocusPolicy(QtCore.Qt.NoFocus) + self.FormatBox.setObjectName(_fromUtf8("FormatBox")) + self.ConvertButton = QtGui.QPushButton(self.Form) + self.ConvertButton.setGeometry(QtCore.QRect(160, 200, 91, 32)) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + font.setPointSize(9) + font.setBold(True) + font.setWeight(75) + self.ConvertButton.setFont(font) + self.ConvertButton.setFocusPolicy(QtCore.Qt.NoFocus) + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap(_fromUtf8(":/Other/icons/convert.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.ConvertButton.setIcon(icon1) + self.ConvertButton.setObjectName(_fromUtf8("ConvertButton")) + self.DirectoryButton = QtGui.QPushButton(self.Form) + self.DirectoryButton.setGeometry(QtCore.QRect(10, 160, 141, 32)) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + font.setPointSize(8) + self.DirectoryButton.setFont(font) + self.DirectoryButton.setFocusPolicy(QtCore.Qt.NoFocus) + icon2 = QtGui.QIcon() + icon2.addPixmap(QtGui.QPixmap(_fromUtf8(":/Other/icons/folder_new.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.DirectoryButton.setIcon(icon2) + self.DirectoryButton.setObjectName(_fromUtf8("DirectoryButton")) + self.FileButton = QtGui.QPushButton(self.Form) + self.FileButton.setGeometry(QtCore.QRect(260, 160, 151, 32)) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + font.setPointSize(8) + self.FileButton.setFont(font) + self.FileButton.setFocusPolicy(QtCore.Qt.NoFocus) + icon3 = QtGui.QIcon() + icon3.addPixmap(QtGui.QPixmap(_fromUtf8(":/Other/icons/document_new.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.FileButton.setIcon(icon3) + self.FileButton.setObjectName(_fromUtf8("FileButton")) + self.ClearButton = QtGui.QPushButton(self.Form) + self.ClearButton.setGeometry(QtCore.QRect(160, 160, 91, 32)) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + font.setPointSize(8) + self.ClearButton.setFont(font) + self.ClearButton.setFocusPolicy(QtCore.Qt.NoFocus) + icon4 = QtGui.QIcon() + icon4.addPixmap(QtGui.QPixmap(_fromUtf8(":/Other/icons/clear.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.ClearButton.setIcon(icon4) + self.ClearButton.setObjectName(_fromUtf8("ClearButton")) + self.OptionsBasic = QtGui.QFrame(self.Form) + self.OptionsBasic.setGeometry(QtCore.QRect(1, 230, 421, 41)) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + font.setPointSize(9) + self.OptionsBasic.setFont(font) + self.OptionsBasic.setObjectName(_fromUtf8("OptionsBasic")) + self.MangaBox = QtGui.QCheckBox(self.OptionsBasic) + self.MangaBox.setGeometry(QtCore.QRect(9, 10, 130, 18)) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + self.MangaBox.setFont(font) + self.MangaBox.setFocusPolicy(QtCore.Qt.NoFocus) + self.MangaBox.setObjectName(_fromUtf8("MangaBox")) + self.QualityBox = QtGui.QCheckBox(self.OptionsBasic) + self.QualityBox.setGeometry(QtCore.QRect(282, 10, 135, 18)) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + self.QualityBox.setFont(font) + self.QualityBox.setFocusPolicy(QtCore.Qt.NoFocus) + self.QualityBox.setTristate(True) + self.QualityBox.setObjectName(_fromUtf8("QualityBox")) + self.RotateBox = QtGui.QCheckBox(self.OptionsBasic) + self.RotateBox.setGeometry(QtCore.QRect(145, 10, 130, 18)) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + self.RotateBox.setFont(font) + self.RotateBox.setFocusPolicy(QtCore.Qt.NoFocus) + self.RotateBox.setObjectName(_fromUtf8("RotateBox")) + self.JobList = QtGui.QListWidget(self.Form) + self.JobList.setGeometry(QtCore.QRect(10, 50, 401, 101)) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + font.setPointSize(8) + font.setItalic(False) + self.JobList.setFont(font) + self.JobList.setFocusPolicy(QtCore.Qt.NoFocus) + self.JobList.setProperty("showDropIndicator", False) + self.JobList.setSelectionMode(QtGui.QAbstractItemView.NoSelection) + self.JobList.setIconSize(QtCore.QSize(18, 18)) + self.JobList.setObjectName(_fromUtf8("JobList")) + self.BasicModeButton = QtGui.QPushButton(self.Form) + self.BasicModeButton.setGeometry(QtCore.QRect(10, 10, 195, 32)) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + font.setPointSize(9) + self.BasicModeButton.setFont(font) + self.BasicModeButton.setFocusPolicy(QtCore.Qt.NoFocus) + self.BasicModeButton.setObjectName(_fromUtf8("BasicModeButton")) + self.AdvModeButton = QtGui.QPushButton(self.Form) + self.AdvModeButton.setGeometry(QtCore.QRect(217, 10, 195, 32)) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + font.setPointSize(9) + self.AdvModeButton.setFont(font) + self.AdvModeButton.setFocusPolicy(QtCore.Qt.NoFocus) + self.AdvModeButton.setObjectName(_fromUtf8("AdvModeButton")) + self.OptionsAdvancedGamma = QtGui.QFrame(self.Form) + self.OptionsAdvancedGamma.setEnabled(True) + self.OptionsAdvancedGamma.setGeometry(QtCore.QRect(10, 305, 401, 41)) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + font.setPointSize(9) + self.OptionsAdvancedGamma.setFont(font) + self.OptionsAdvancedGamma.setObjectName(_fromUtf8("OptionsAdvancedGamma")) + self.GammaLabel = QtGui.QLabel(self.OptionsAdvancedGamma) + self.GammaLabel.setGeometry(QtCore.QRect(15, 0, 100, 40)) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + self.GammaLabel.setFont(font) + self.GammaLabel.setObjectName(_fromUtf8("GammaLabel")) + self.GammaSlider = QtGui.QSlider(self.OptionsAdvancedGamma) + self.GammaSlider.setGeometry(QtCore.QRect(110, 10, 275, 22)) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + self.GammaSlider.setFont(font) + self.GammaSlider.setFocusPolicy(QtCore.Qt.ClickFocus) + self.GammaSlider.setMaximum(500) + self.GammaSlider.setSingleStep(5) + self.GammaSlider.setOrientation(QtCore.Qt.Horizontal) + self.GammaSlider.setObjectName(_fromUtf8("GammaSlider")) + self.ProgressBar = QtGui.QProgressBar(self.Form) + self.ProgressBar.setGeometry(QtCore.QRect(10, 10, 401, 31)) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + font.setPointSize(10) + font.setBold(True) + font.setWeight(75) + self.ProgressBar.setFont(font) + self.ProgressBar.setProperty("value", 0) + self.ProgressBar.setAlignment(QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) + self.ProgressBar.setFormat(_fromUtf8("")) + self.ProgressBar.setObjectName(_fromUtf8("ProgressBar")) + self.OptionsExpert = QtGui.QFrame(self.Form) + self.OptionsExpert.setGeometry(QtCore.QRect(1, 337, 421, 41)) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + font.setPointSize(9) + self.OptionsExpert.setFont(font) + self.OptionsExpert.setObjectName(_fromUtf8("OptionsExpert")) + self.ColorBox = QtGui.QCheckBox(self.OptionsExpert) + self.ColorBox.setGeometry(QtCore.QRect(9, 11, 130, 18)) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + self.ColorBox.setFont(font) + self.ColorBox.setFocusPolicy(QtCore.Qt.NoFocus) + self.ColorBox.setObjectName(_fromUtf8("ColorBox")) + self.OptionsExpertInternal = QtGui.QFrame(self.OptionsExpert) + self.OptionsExpertInternal.setGeometry(QtCore.QRect(105, 0, 295, 40)) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + self.OptionsExpertInternal.setFont(font) + self.OptionsExpertInternal.setObjectName(_fromUtf8("OptionsExpertInternal")) + self.gridLayout_2 = QtGui.QGridLayout(self.OptionsExpertInternal) + self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2")) + self.wLabel = QtGui.QLabel(self.OptionsExpertInternal) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + self.wLabel.setFont(font) + self.wLabel.setObjectName(_fromUtf8("wLabel")) + self.gridLayout_2.addWidget(self.wLabel, 0, 0, 1, 1) + self.customWidth = QtGui.QLineEdit(self.OptionsExpertInternal) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.customWidth.sizePolicy().hasHeightForWidth()) + self.customWidth.setSizePolicy(sizePolicy) + self.customWidth.setMaximumSize(QtCore.QSize(40, 16777215)) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + self.customWidth.setFont(font) + self.customWidth.setFocusPolicy(QtCore.Qt.ClickFocus) + self.customWidth.setAcceptDrops(False) + self.customWidth.setMaxLength(4) + self.customWidth.setObjectName(_fromUtf8("customWidth")) + self.gridLayout_2.addWidget(self.customWidth, 0, 1, 1, 1) + self.hLabel = QtGui.QLabel(self.OptionsExpertInternal) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + self.hLabel.setFont(font) + self.hLabel.setObjectName(_fromUtf8("hLabel")) + self.gridLayout_2.addWidget(self.hLabel, 0, 2, 1, 1) + self.customHeight = QtGui.QLineEdit(self.OptionsExpertInternal) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.customHeight.sizePolicy().hasHeightForWidth()) + self.customHeight.setSizePolicy(sizePolicy) + self.customHeight.setMaximumSize(QtCore.QSize(40, 16777215)) + font = QtGui.QFont() + font.setFamily(_fromUtf8("DejaVu Sans")) + self.customHeight.setFont(font) + self.customHeight.setFocusPolicy(QtCore.Qt.ClickFocus) + self.customHeight.setAcceptDrops(False) + self.customHeight.setMaxLength(4) + self.customHeight.setObjectName(_fromUtf8("customHeight")) + self.gridLayout_2.addWidget(self.customHeight, 0, 3, 1, 1) + KCC.setCentralWidget(self.Form) + self.ActionBasic = QtGui.QAction(KCC) + self.ActionBasic.setCheckable(True) + self.ActionBasic.setChecked(False) + font = QtGui.QFont() + self.ActionBasic.setFont(font) + self.ActionBasic.setObjectName(_fromUtf8("ActionBasic")) + self.ActionAdvanced = QtGui.QAction(KCC) + self.ActionAdvanced.setCheckable(True) + self.ActionAdvanced.setObjectName(_fromUtf8("ActionAdvanced")) + + self.retranslateUi(KCC) + QtCore.QMetaObject.connectSlotsByName(KCC) + KCC.setTabOrder(self.DirectoryButton, self.FileButton) + KCC.setTabOrder(self.FileButton, self.ConvertButton) + KCC.setTabOrder(self.ConvertButton, self.ClearButton) + + def retranslateUi(self, KCC): + KCC.setWindowTitle(_translate("KCC", "Kindle Comic Converter", None)) + self.ProcessingBox.setToolTip(_translate("KCC", "Disable image optimizations.", None)) + self.ProcessingBox.setText(_translate("KCC", "No optimisation", None)) + self.UpscaleBox.setToolTip(_translate("KCC", "

Unchecked - Nothing
Images smaller than device resolution will not be resized.

Indeterminate - Stretching
Images smaller than device resolution will be resized. Aspect ratio will be not preserved.

Checked - Upscaling
Images smaller than device resolution will be resized. Aspect ratio will be preserved.

", None)) + self.UpscaleBox.setText(_translate("KCC", "Stretch/Upscale", None)) + self.WebtoonBox.setToolTip(_translate("KCC", "

Enable auto-splitting of webtoons like Tower of God or Noblesse.
Pages with a low width, high height and vertical panel flow.

", None)) + self.WebtoonBox.setText(_translate("KCC", "Webtoon mode", None)) + self.NoDitheringBox.setToolTip(_translate("KCC", "

Create PNG files instead JPEG.

", None)) + self.NoDitheringBox.setText(_translate("KCC", "PNG output", None)) + self.BorderBox.setToolTip(_translate("KCC", "

Unchecked - Autodetection
Color of margins fill will be detected automatically.

Indeterminate - White
Margins will be filled with white color.

Checked - Black
Margins will be filled with black color.

", None)) + self.BorderBox.setText(_translate("KCC", "W/B margins", None)) + self.NoRotateBox.setToolTip(_translate("KCC", "

Disable splitting and rotation.

", None)) + self.NoRotateBox.setText(_translate("KCC", "No split/rotate", None)) + self.DeviceBox.setToolTip(_translate("KCC", "Target device.", None)) + self.FormatBox.setToolTip(_translate("KCC", "Output format.", None)) + self.ConvertButton.setText(_translate("KCC", "Convert", None)) + self.DirectoryButton.setText(_translate("KCC", "Add directory", None)) + self.FileButton.setText(_translate("KCC", "Add file", None)) + self.ClearButton.setText(_translate("KCC", "Clear list", None)) + self.MangaBox.setToolTip(_translate("KCC", "Enable right-to-left reading.", None)) + self.MangaBox.setText(_translate("KCC", "Manga mode", None)) + self.QualityBox.setToolTip(_translate("KCC", "\n" +"\n" +"

Unchecked - Normal quality mode
Use it when Panel View support is not needed.
- Maximum quality when zoom is not enabled.
- Poor quality when zoom is enabled.
- Lowest file size.

\n" +"

Indeterminate - High quality mode
Not zoomed image might be a little blurry.
- Medium/High quality when zoom is not enabled.
- Maximum quality when zoom is enabled.

\n" +"

Checked - Ultra quality mode
Maximum possible quality.
- Maximum quality when zoom is not enabled.
- Maximum quality when zoom is enabled.
- Very high file size.

", None)) + self.QualityBox.setText(_translate("KCC", "High/Ultra quality", None)) + self.RotateBox.setToolTip(_translate("KCC", "

Disable page spliting.
They will be rotated instead.

", None)) + self.RotateBox.setText(_translate("KCC", "Horizontal mode", None)) + self.BasicModeButton.setText(_translate("KCC", "Basic", None)) + self.AdvModeButton.setText(_translate("KCC", "Advanced", None)) + self.GammaLabel.setToolTip(_translate("KCC", "

When converting color images setting this option to 1.0 might improve readability.

", None)) + self.GammaLabel.setText(_translate("KCC", "Gamma: Auto", None)) + self.GammaSlider.setToolTip(_translate("KCC", "

When converting color images setting this option to 1.0 might improve readability.

", None)) + self.ColorBox.setToolTip(_translate("KCC", "Do not convert images to grayscale.", None)) + self.ColorBox.setText(_translate("KCC", "Color mode", None)) + self.wLabel.setToolTip(_translate("KCC", "Resolution of target device.", None)) + self.wLabel.setText(_translate("KCC", "Custom width: ", None)) + self.customWidth.setToolTip(_translate("KCC", "Resolution of target device.", None)) + self.customWidth.setInputMask(_translate("KCC", "0000; ", None)) + self.hLabel.setToolTip(_translate("KCC", "Resolution of target device.", None)) + self.hLabel.setText(_translate("KCC", "Custom height: ", None)) + self.customHeight.setToolTip(_translate("KCC", "Resolution of target device.", None)) + self.customHeight.setInputMask(_translate("KCC", "0000; ", None)) + self.ActionBasic.setText(_translate("KCC", "Basic", None)) + self.ActionAdvanced.setText(_translate("KCC", "Advanced", None)) + +import KCC_rc From 7907a45ca25f9ad2ea64e4ecd7d06fd76df97fb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Fri, 20 Sep 2013 11:13:41 +0200 Subject: [PATCH 18/26] Updated OSX GUI --- KCC-OSX.ui | 83 ++++++++++++++++++++++++++++++++++------------- kcc/KCC_ui_osx.py | 83 +++++++++++++++++++++++++++++++++-------------- 2 files changed, 120 insertions(+), 46 deletions(-) diff --git a/KCC-OSX.ui b/KCC-OSX.ui index b4f690d3..2e57eb7f 100644 --- a/KCC-OSX.ui +++ b/KCC-OSX.ui @@ -47,7 +47,7 @@
- 9 + 4 253 421 61 @@ -55,6 +55,7 @@ + Lucida Grande 9 @@ -63,7 +64,8 @@ - 11 + Lucida Grande + 12 @@ -81,7 +83,8 @@ - 11 + Lucida Grande + 12 @@ -102,7 +105,8 @@ - 11 + Lucida Grande + 12 @@ -120,7 +124,8 @@ - 11 + Lucida Grande + 12 @@ -138,7 +143,8 @@ - 11 + Lucida Grande + 12 @@ -159,7 +165,8 @@ - 11 + Lucida Grande + 12 @@ -186,6 +193,7 @@ + Lucida Grande 11 @@ -193,7 +201,7 @@ Qt::NoFocus - Target device. + <html><head/><body><p><span style=" font-size:12pt;">Target device.</span></p></body></html> @@ -207,6 +215,7 @@ + Lucida Grande 11 @@ -214,7 +223,7 @@ Qt::NoFocus - Output format. + <html><head/><body><p><span style=" font-size:12pt;">Output format.</span></p></body></html> @@ -228,6 +237,7 @@ + Lucida Grande 11 75 true @@ -255,6 +265,7 @@ + Lucida Grande 11 @@ -280,6 +291,7 @@ + Lucida Grande 11 @@ -305,6 +317,7 @@ + Lucida Grande 11 @@ -322,7 +335,7 @@ - 10 + 5 233 421 41 @@ -330,7 +343,8 @@ - 9 + Lucida Grande + 12 @@ -344,7 +358,8 @@ - 11 + Lucida Grande + 12 @@ -362,20 +377,21 @@ 282 10 - 130 + 135 18 - 11 + Lucida Grande + 12 Qt::NoFocus - <html><head/><body><p><span style=" font-family:'MS Shell Dlg 2'; font-size:14pt; font-weight:600; text-decoration: underline;">Unchecked - Normal quality mode<br/></span><span style=" font-family:'MS Shell Dlg 2'; font-size:14pt; font-style:italic;">Use it when Panel View support is not needed.</span><span style=" font-family:'MS Shell Dlg 2'; font-size:14pt; font-weight:600; text-decoration: underline;"><br/></span><span style=" font-family:'MS Shell Dlg 2'; font-size:14pt;">- Maximum quality when zoom is not enabled.<br/>- Poor quality when zoom is enabled.<br/>- Lowest file size.</span></p><p><span style=" font-family:'MS Shell Dlg 2'; font-size:14pt; font-weight:600; text-decoration: underline;">Indeterminate - High quality mode<br/></span><span style=" font-family:'MS Shell Dlg 2'; font-size:14pt; font-style:italic;">Not zoomed image </span><span style=" font-family:'MS Shell Dlg 2'; font-size:14pt; font-weight:600; font-style:italic;">might </span><span style=" font-family:'MS Shell Dlg 2'; font-size:14pt; font-style:italic;">be a little blurry.</span><span style=" font-family:'MS Shell Dlg 2'; font-size:14pt; font-weight:600; text-decoration: underline;"><br/></span><span style=" font-family:'MS Shell Dlg 2'; font-size:14pt;">- Medium/High quality when zoom is not enabled.<br/>- Maximum quality when zoom is enabled.</span></p><p><span style=" font-family:'MS Shell Dlg 2'; font-size:14pt; font-weight:600; text-decoration: underline;">Checked - Ultra quality mode<br/></span><span style=" font-family:'MS Shell Dlg 2'; font-size:14pt; font-style:italic;">Maximum possible quality.</span><span style=" font-family:'MS Shell Dlg 2'; font-size:14pt; font-weight:600; text-decoration: underline;"><br/></span><span style=" font-family:'MS Shell Dlg 2'; font-size:14pt;">- Maximum quality when zoom is not enabled.<br/>- Maximum quality when zoom is enabled.<br/>- Very high file size.</span></p></body></html> + <html><head/><body><p><span style="font-size:12pt; font-weight:600; text-decoration: underline;">Unchecked - Normal quality mode<br/></span><span style="font-size:12pt; font-style:italic;">Use it when Panel View support is not needed.</span><span style="font-size:12pt; font-weight:600; text-decoration: underline;"><br/></span><span style="font-size:12pt;">- Maximum quality when zoom is not enabled.<br/>- Poor quality when zoom is enabled.<br/>- Lowest file size.</span></p><p><span style="font-size:12pt; font-weight:600; text-decoration: underline;">Indeterminate - High quality mode<br/></span><span style="font-size:12pt; font-style:italic;">Not zoomed image </span><span style="font-size:12pt; font-weight:600; font-style:italic;">might </span><span style="font-size:12pt; font-style:italic;">be a little blurry.</span><span style="font-size:12pt; font-weight:600; text-decoration: underline;"><br/></span><span style="font-size:12pt;">- Medium/High quality when zoom is not enabled.<br/>- Maximum quality when zoom is enabled.</span></p><p><span style="font-size:12pt; font-weight:600; text-decoration: underline;">Checked - Ultra quality mode<br/></span><span style="font-size:12pt; font-style:italic;">Maximum possible quality.</span><span style="font-size:12pt; font-weight:600; text-decoration: underline;"><br/></span><span style="font-size:12pt;">- Maximum quality when zoom is not enabled.<br/>- Maximum quality when zoom is enabled.<br/>- Very high file size.</span></p></body></html> High/Ultra quality @@ -395,7 +411,8 @@ - 11 + Lucida Grande + 12 @@ -423,6 +440,7 @@ + Lucida Grande 11 @@ -447,6 +465,7 @@ + Lucida Grande 12 50 false @@ -470,6 +489,7 @@ + Lucida Grande 12 50 false @@ -488,7 +508,7 @@ - 10 + 5 303 401 41 @@ -496,6 +516,7 @@ + Lucida Grande 9 @@ -510,13 +531,14 @@ + Lucida Grande 12 50 false - <html><head/><body><p><span style=" font-size:12pt;">When converting color images setting this option to 1.0 MIGHT improve readability.</span></p></body></html> + <html><head/><body><p><span style=" font-size:12pt;">When converting color images setting this option to 1.0 </span><span style=" font-size:12pt; font-weight:600;">might</span><span style=" font-size:12pt;"> improve readability.</span></p></body></html> Gamma: Auto @@ -527,10 +549,15 @@ 110 10 - 280 + 290 22 + + + Lucida Grande + + Qt::ClickFocus @@ -559,6 +586,7 @@ + Lucida Grande 10 75 true @@ -580,7 +608,7 @@ - 10 + 5 335 421 41 @@ -588,6 +616,7 @@ + Lucida Grande 9 @@ -602,7 +631,8 @@ - 11 + Lucida Grande + 12 @@ -618,17 +648,23 @@ - 90 + 95 0 315 40 + + + Lucida Grande + + + Lucida Grande 12 50 false @@ -658,6 +694,7 @@ + Lucida Grande 12 @@ -682,6 +719,7 @@ + Lucida Grande 12 50 false @@ -711,6 +749,7 @@ + Lucida Grande 12 diff --git a/kcc/KCC_ui_osx.py b/kcc/KCC_ui_osx.py index 8102940f..5813d408 100644 --- a/kcc/KCC_ui_osx.py +++ b/kcc/KCC_ui_osx.py @@ -2,8 +2,8 @@ # Form implementation generated from reading ui file 'KCC-OSX.ui' # -# Created: Wed Sep 18 12:13:05 2013 -# by: PyQt4 UI code generator 4.10.3 +# Created: Fri Sep 20 10:57:31 2013 +# by: PyQt4 UI code generator 4.10.2 # # WARNING! All changes made in this file will be lost! @@ -41,8 +41,9 @@ def setupUi(self, KCC): self.Form.setObjectName(_fromUtf8("Form")) self.OptionsAdvanced = QtGui.QFrame(self.Form) self.OptionsAdvanced.setEnabled(True) - self.OptionsAdvanced.setGeometry(QtCore.QRect(9, 253, 421, 61)) + self.OptionsAdvanced.setGeometry(QtCore.QRect(4, 253, 421, 61)) font = QtGui.QFont() + font.setFamily(_fromUtf8("Lucida Grande")) font.setPointSize(9) self.OptionsAdvanced.setFont(font) self.OptionsAdvanced.setObjectName(_fromUtf8("OptionsAdvanced")) @@ -50,14 +51,16 @@ def setupUi(self, KCC): self.gridLayout.setObjectName(_fromUtf8("gridLayout")) self.ProcessingBox = QtGui.QCheckBox(self.OptionsAdvanced) font = QtGui.QFont() - font.setPointSize(11) + font.setFamily(_fromUtf8("Lucida Grande")) + font.setPointSize(12) self.ProcessingBox.setFont(font) self.ProcessingBox.setFocusPolicy(QtCore.Qt.NoFocus) self.ProcessingBox.setObjectName(_fromUtf8("ProcessingBox")) self.gridLayout.addWidget(self.ProcessingBox, 1, 0, 1, 1) self.UpscaleBox = QtGui.QCheckBox(self.OptionsAdvanced) font = QtGui.QFont() - font.setPointSize(11) + font.setFamily(_fromUtf8("Lucida Grande")) + font.setPointSize(12) self.UpscaleBox.setFont(font) self.UpscaleBox.setFocusPolicy(QtCore.Qt.NoFocus) self.UpscaleBox.setTristate(True) @@ -65,21 +68,24 @@ def setupUi(self, KCC): self.gridLayout.addWidget(self.UpscaleBox, 1, 1, 1, 1) self.WebtoonBox = QtGui.QCheckBox(self.OptionsAdvanced) font = QtGui.QFont() - font.setPointSize(11) + font.setFamily(_fromUtf8("Lucida Grande")) + font.setPointSize(12) self.WebtoonBox.setFont(font) self.WebtoonBox.setFocusPolicy(QtCore.Qt.NoFocus) self.WebtoonBox.setObjectName(_fromUtf8("WebtoonBox")) self.gridLayout.addWidget(self.WebtoonBox, 3, 1, 1, 1) self.NoDitheringBox = QtGui.QCheckBox(self.OptionsAdvanced) font = QtGui.QFont() - font.setPointSize(11) + font.setFamily(_fromUtf8("Lucida Grande")) + font.setPointSize(12) self.NoDitheringBox.setFont(font) self.NoDitheringBox.setFocusPolicy(QtCore.Qt.NoFocus) self.NoDitheringBox.setObjectName(_fromUtf8("NoDitheringBox")) self.gridLayout.addWidget(self.NoDitheringBox, 3, 2, 1, 1) self.BorderBox = QtGui.QCheckBox(self.OptionsAdvanced) font = QtGui.QFont() - font.setPointSize(11) + font.setFamily(_fromUtf8("Lucida Grande")) + font.setPointSize(12) self.BorderBox.setFont(font) self.BorderBox.setFocusPolicy(QtCore.Qt.NoFocus) self.BorderBox.setTristate(True) @@ -87,7 +93,8 @@ def setupUi(self, KCC): self.gridLayout.addWidget(self.BorderBox, 3, 0, 1, 1) self.NoRotateBox = QtGui.QCheckBox(self.OptionsAdvanced) font = QtGui.QFont() - font.setPointSize(11) + font.setFamily(_fromUtf8("Lucida Grande")) + font.setPointSize(12) self.NoRotateBox.setFont(font) self.NoRotateBox.setFocusPolicy(QtCore.Qt.NoFocus) self.NoRotateBox.setObjectName(_fromUtf8("NoRotateBox")) @@ -95,6 +102,7 @@ def setupUi(self, KCC): self.DeviceBox = QtGui.QComboBox(self.Form) self.DeviceBox.setGeometry(QtCore.QRect(8, 200, 151, 34)) font = QtGui.QFont() + font.setFamily(_fromUtf8("Lucida Grande")) font.setPointSize(11) self.DeviceBox.setFont(font) self.DeviceBox.setFocusPolicy(QtCore.Qt.NoFocus) @@ -102,6 +110,7 @@ def setupUi(self, KCC): self.FormatBox = QtGui.QComboBox(self.Form) self.FormatBox.setGeometry(QtCore.QRect(262, 200, 152, 34)) font = QtGui.QFont() + font.setFamily(_fromUtf8("Lucida Grande")) font.setPointSize(11) self.FormatBox.setFont(font) self.FormatBox.setFocusPolicy(QtCore.Qt.NoFocus) @@ -109,6 +118,7 @@ def setupUi(self, KCC): self.ConvertButton = QtGui.QPushButton(self.Form) self.ConvertButton.setGeometry(QtCore.QRect(160, 200, 101, 41)) font = QtGui.QFont() + font.setFamily(_fromUtf8("Lucida Grande")) font.setPointSize(11) font.setBold(True) font.setWeight(75) @@ -121,6 +131,7 @@ def setupUi(self, KCC): self.DirectoryButton = QtGui.QPushButton(self.Form) self.DirectoryButton.setGeometry(QtCore.QRect(5, 160, 156, 41)) font = QtGui.QFont() + font.setFamily(_fromUtf8("Lucida Grande")) font.setPointSize(11) self.DirectoryButton.setFont(font) self.DirectoryButton.setFocusPolicy(QtCore.Qt.NoFocus) @@ -131,6 +142,7 @@ def setupUi(self, KCC): self.FileButton = QtGui.QPushButton(self.Form) self.FileButton.setGeometry(QtCore.QRect(260, 160, 157, 41)) font = QtGui.QFont() + font.setFamily(_fromUtf8("Lucida Grande")) font.setPointSize(11) self.FileButton.setFont(font) self.FileButton.setFocusPolicy(QtCore.Qt.NoFocus) @@ -141,6 +153,7 @@ def setupUi(self, KCC): self.ClearButton = QtGui.QPushButton(self.Form) self.ClearButton.setGeometry(QtCore.QRect(160, 160, 101, 41)) font = QtGui.QFont() + font.setFamily(_fromUtf8("Lucida Grande")) font.setPointSize(11) self.ClearButton.setFont(font) self.ClearButton.setFocusPolicy(QtCore.Qt.NoFocus) @@ -149,22 +162,25 @@ def setupUi(self, KCC): self.ClearButton.setIcon(icon4) self.ClearButton.setObjectName(_fromUtf8("ClearButton")) self.OptionsBasic = QtGui.QFrame(self.Form) - self.OptionsBasic.setGeometry(QtCore.QRect(10, 233, 421, 41)) + self.OptionsBasic.setGeometry(QtCore.QRect(5, 233, 421, 41)) font = QtGui.QFont() - font.setPointSize(9) + font.setFamily(_fromUtf8("Lucida Grande")) + font.setPointSize(12) self.OptionsBasic.setFont(font) self.OptionsBasic.setObjectName(_fromUtf8("OptionsBasic")) self.MangaBox = QtGui.QCheckBox(self.OptionsBasic) self.MangaBox.setGeometry(QtCore.QRect(9, 10, 130, 18)) font = QtGui.QFont() - font.setPointSize(11) + font.setFamily(_fromUtf8("Lucida Grande")) + font.setPointSize(12) self.MangaBox.setFont(font) self.MangaBox.setFocusPolicy(QtCore.Qt.NoFocus) self.MangaBox.setObjectName(_fromUtf8("MangaBox")) self.QualityBox = QtGui.QCheckBox(self.OptionsBasic) - self.QualityBox.setGeometry(QtCore.QRect(282, 10, 130, 18)) + self.QualityBox.setGeometry(QtCore.QRect(282, 10, 135, 18)) font = QtGui.QFont() - font.setPointSize(11) + font.setFamily(_fromUtf8("Lucida Grande")) + font.setPointSize(12) self.QualityBox.setFont(font) self.QualityBox.setFocusPolicy(QtCore.Qt.NoFocus) self.QualityBox.setTristate(True) @@ -172,13 +188,15 @@ def setupUi(self, KCC): self.RotateBox = QtGui.QCheckBox(self.OptionsBasic) self.RotateBox.setGeometry(QtCore.QRect(145, 10, 130, 18)) font = QtGui.QFont() - font.setPointSize(11) + font.setFamily(_fromUtf8("Lucida Grande")) + font.setPointSize(12) self.RotateBox.setFont(font) self.RotateBox.setFocusPolicy(QtCore.Qt.NoFocus) self.RotateBox.setObjectName(_fromUtf8("RotateBox")) self.JobList = QtGui.QListWidget(self.Form) self.JobList.setGeometry(QtCore.QRect(10, 50, 401, 101)) font = QtGui.QFont() + font.setFamily(_fromUtf8("Lucida Grande")) font.setPointSize(11) self.JobList.setFont(font) self.JobList.setFocusPolicy(QtCore.Qt.NoFocus) @@ -188,6 +206,7 @@ def setupUi(self, KCC): self.BasicModeButton = QtGui.QPushButton(self.Form) self.BasicModeButton.setGeometry(QtCore.QRect(5, 10, 210, 41)) font = QtGui.QFont() + font.setFamily(_fromUtf8("Lucida Grande")) font.setPointSize(12) font.setBold(False) font.setWeight(50) @@ -197,6 +216,7 @@ def setupUi(self, KCC): self.AdvModeButton = QtGui.QPushButton(self.Form) self.AdvModeButton.setGeometry(QtCore.QRect(207, 10, 210, 41)) font = QtGui.QFont() + font.setFamily(_fromUtf8("Lucida Grande")) font.setPointSize(12) font.setBold(False) font.setWeight(50) @@ -205,21 +225,26 @@ def setupUi(self, KCC): self.AdvModeButton.setObjectName(_fromUtf8("AdvModeButton")) self.OptionsAdvancedGamma = QtGui.QFrame(self.Form) self.OptionsAdvancedGamma.setEnabled(True) - self.OptionsAdvancedGamma.setGeometry(QtCore.QRect(10, 303, 401, 41)) + self.OptionsAdvancedGamma.setGeometry(QtCore.QRect(5, 303, 401, 41)) font = QtGui.QFont() + font.setFamily(_fromUtf8("Lucida Grande")) font.setPointSize(9) self.OptionsAdvancedGamma.setFont(font) self.OptionsAdvancedGamma.setObjectName(_fromUtf8("OptionsAdvancedGamma")) self.GammaLabel = QtGui.QLabel(self.OptionsAdvancedGamma) self.GammaLabel.setGeometry(QtCore.QRect(20, 0, 100, 40)) font = QtGui.QFont() + font.setFamily(_fromUtf8("Lucida Grande")) font.setPointSize(12) font.setBold(False) font.setWeight(50) self.GammaLabel.setFont(font) self.GammaLabel.setObjectName(_fromUtf8("GammaLabel")) self.GammaSlider = QtGui.QSlider(self.OptionsAdvancedGamma) - self.GammaSlider.setGeometry(QtCore.QRect(110, 10, 280, 22)) + self.GammaSlider.setGeometry(QtCore.QRect(110, 10, 290, 22)) + font = QtGui.QFont() + font.setFamily(_fromUtf8("Lucida Grande")) + self.GammaSlider.setFont(font) self.GammaSlider.setFocusPolicy(QtCore.Qt.ClickFocus) self.GammaSlider.setMaximum(500) self.GammaSlider.setSingleStep(5) @@ -228,6 +253,7 @@ def setupUi(self, KCC): self.ProgressBar = QtGui.QProgressBar(self.Form) self.ProgressBar.setGeometry(QtCore.QRect(10, 10, 401, 35)) font = QtGui.QFont() + font.setFamily(_fromUtf8("Lucida Grande")) font.setPointSize(10) font.setBold(True) font.setWeight(75) @@ -238,25 +264,31 @@ def setupUi(self, KCC): self.ProgressBar.setFormat(_fromUtf8("")) self.ProgressBar.setObjectName(_fromUtf8("ProgressBar")) self.OptionsExpert = QtGui.QFrame(self.Form) - self.OptionsExpert.setGeometry(QtCore.QRect(10, 335, 421, 41)) + self.OptionsExpert.setGeometry(QtCore.QRect(5, 335, 421, 41)) font = QtGui.QFont() + font.setFamily(_fromUtf8("Lucida Grande")) font.setPointSize(9) self.OptionsExpert.setFont(font) self.OptionsExpert.setObjectName(_fromUtf8("OptionsExpert")) self.ColorBox = QtGui.QCheckBox(self.OptionsExpert) self.ColorBox.setGeometry(QtCore.QRect(9, 11, 130, 18)) font = QtGui.QFont() - font.setPointSize(11) + font.setFamily(_fromUtf8("Lucida Grande")) + font.setPointSize(12) self.ColorBox.setFont(font) self.ColorBox.setFocusPolicy(QtCore.Qt.NoFocus) self.ColorBox.setObjectName(_fromUtf8("ColorBox")) self.OptionsExpertInternal = QtGui.QFrame(self.OptionsExpert) - self.OptionsExpertInternal.setGeometry(QtCore.QRect(90, 0, 315, 40)) + self.OptionsExpertInternal.setGeometry(QtCore.QRect(95, 0, 315, 40)) + font = QtGui.QFont() + font.setFamily(_fromUtf8("Lucida Grande")) + self.OptionsExpertInternal.setFont(font) self.OptionsExpertInternal.setObjectName(_fromUtf8("OptionsExpertInternal")) self.gridLayout_2 = QtGui.QGridLayout(self.OptionsExpertInternal) self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2")) self.wLabel = QtGui.QLabel(self.OptionsExpertInternal) font = QtGui.QFont() + font.setFamily(_fromUtf8("Lucida Grande")) font.setPointSize(12) font.setBold(False) font.setWeight(50) @@ -271,6 +303,7 @@ def setupUi(self, KCC): self.customWidth.setSizePolicy(sizePolicy) self.customWidth.setMaximumSize(QtCore.QSize(45, 16777215)) font = QtGui.QFont() + font.setFamily(_fromUtf8("Lucida Grande")) font.setPointSize(12) self.customWidth.setFont(font) self.customWidth.setFocusPolicy(QtCore.Qt.ClickFocus) @@ -280,6 +313,7 @@ def setupUi(self, KCC): self.gridLayout_2.addWidget(self.customWidth, 0, 1, 1, 1) self.hLabel = QtGui.QLabel(self.OptionsExpertInternal) font = QtGui.QFont() + font.setFamily(_fromUtf8("Lucida Grande")) font.setPointSize(12) font.setBold(False) font.setWeight(50) @@ -294,6 +328,7 @@ def setupUi(self, KCC): self.customHeight.setSizePolicy(sizePolicy) self.customHeight.setMaximumSize(QtCore.QSize(45, 16777215)) font = QtGui.QFont() + font.setFamily(_fromUtf8("Lucida Grande")) font.setPointSize(12) self.customHeight.setFont(font) self.customHeight.setFocusPolicy(QtCore.Qt.ClickFocus) @@ -333,21 +368,21 @@ def retranslateUi(self, KCC): self.BorderBox.setText(_translate("KCC", "W/B margins", None)) self.NoRotateBox.setToolTip(_translate("KCC", "

Disable splitting and rotation.

", None)) self.NoRotateBox.setText(_translate("KCC", "No split/rotate", None)) - self.DeviceBox.setToolTip(_translate("KCC", "Target device.", None)) - self.FormatBox.setToolTip(_translate("KCC", "Output format.", None)) + self.DeviceBox.setToolTip(_translate("KCC", "

Target device.

", None)) + self.FormatBox.setToolTip(_translate("KCC", "

Output format.

", None)) self.ConvertButton.setText(_translate("KCC", "Convert", None)) self.DirectoryButton.setText(_translate("KCC", "Add directory", None)) self.FileButton.setText(_translate("KCC", "Add file", None)) self.ClearButton.setText(_translate("KCC", "Clear list", None)) self.MangaBox.setToolTip(_translate("KCC", "

Enable right-to-left reading.

", None)) self.MangaBox.setText(_translate("KCC", "Manga mode", None)) - self.QualityBox.setToolTip(_translate("KCC", "

Unchecked - Normal quality mode
Use it when Panel View support is not needed.
- Maximum quality when zoom is not enabled.
- Poor quality when zoom is enabled.
- Lowest file size.

Indeterminate - High quality mode
Not zoomed image might be a little blurry.
- Medium/High quality when zoom is not enabled.
- Maximum quality when zoom is enabled.

Checked - Ultra quality mode
Maximum possible quality.
- Maximum quality when zoom is not enabled.
- Maximum quality when zoom is enabled.
- Very high file size.

", None)) + self.QualityBox.setToolTip(_translate("KCC", "

Unchecked - Normal quality mode
Use it when Panel View support is not needed.
- Maximum quality when zoom is not enabled.
- Poor quality when zoom is enabled.
- Lowest file size.

Indeterminate - High quality mode
Not zoomed image might be a little blurry.
- Medium/High quality when zoom is not enabled.
- Maximum quality when zoom is enabled.

Checked - Ultra quality mode
Maximum possible quality.
- Maximum quality when zoom is not enabled.
- Maximum quality when zoom is enabled.
- Very high file size.

", None)) self.QualityBox.setText(_translate("KCC", "High/Ultra quality", None)) self.RotateBox.setToolTip(_translate("KCC", "

Disable page spliting.
They will be rotated instead.

", None)) self.RotateBox.setText(_translate("KCC", "Horizontal mode", None)) self.BasicModeButton.setText(_translate("KCC", "Basic", None)) self.AdvModeButton.setText(_translate("KCC", "Advanced", None)) - self.GammaLabel.setToolTip(_translate("KCC", "

When converting color images setting this option to 1.0 MIGHT improve readability.

", None)) + self.GammaLabel.setToolTip(_translate("KCC", "

When converting color images setting this option to 1.0 might improve readability.

", None)) self.GammaLabel.setText(_translate("KCC", "Gamma: Auto", None)) self.GammaSlider.setToolTip(_translate("KCC", "

When converting color images setting this option to 1.0 might improve readability.

", None)) self.ColorBox.setToolTip(_translate("KCC", "

Do not convert images to grayscale.

", None)) From 4890727692f05129d5d38cd79e08902bf9cb0362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Mon, 23 Sep 2013 10:21:45 +0200 Subject: [PATCH 19/26] Replaced KindleStrip with KindleUnpack (close #6) --- README.md | 13 +- kcc/KCC_gui.py | 24 +-- kcc/kindlesplit.py | 384 +++++++++++++++++++++++++++++++++++++++++++++ kcc/kindlestrip.py | 236 ---------------------------- 4 files changed, 403 insertions(+), 254 deletions(-) create mode 100644 kcc/kindlesplit.py delete mode 100755 kcc/kindlestrip.py diff --git a/README.md b/README.md index 7a2ef489..fd068902 100644 --- a/README.md +++ b/README.md @@ -130,11 +130,10 @@ This script born as a cross-platform alternative to `KindleComicParser` by **Dc5 The app relies and includes the following scripts/binaries: - - `KindleStrip` script © 2010-2012 by **Paul Durrant** and released in public domain -([forum thread](http://www.mobileread.com/forums/showthread.php?t=96903)) - - `rarfile.py` script © 2005-2011 **Marko Kreen** , released with ISC License - - `image.py` class from **Alex Yatskov**'s [Mangle](http://foosoft.net/mangle/) with subsequent [proDOOMman](https://github.com/proDOOMman/Mangle)'s and [Birua](https://github.com/Birua/Mangle)'s patches - - Icon is by **Nikolay Verin** ([http://ncrow.deviantart.com/](http://ncrow.deviantart.com/)) and released under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/) License + - `KindleUnpack` script by Charles **M. Hannum, P. Durrant, K. Hendricks, S. Siebert, fandrieu, DiapDealer, nickredding**. Released with GPLv3 License. + - `rarfile.py` script © 2005-2011 **Marko Kreen** . Released with ISC License. + - `image.py` class from **Alex Yatskov**'s [Mangle](http://foosoft.net/mangle/) with subsequent [proDOOMman](https://github.com/proDOOMman/Mangle)'s and [Birua](https://github.com/Birua/Mangle)'s patches. + - Icon is by **Nikolay Verin** ([http://ncrow.deviantart.com/](http://ncrow.deviantart.com/)) and released under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/) License. ## SAMPLE FILES CREATED BY KCC * [Kindle Paperwhite](http://kcc.vulturis.eu/Samples/Ubunchu!-KPW.mobi) @@ -255,6 +254,7 @@ The app relies and includes the following scripts/binaries: * Hotfixed crash occurring on OS with Russian locale ####3.3: +* Created MOBI files are not longer marked as _Personal_ on newer Kindle models * Margins are now automatically omitted in Panel View mode * Layout of panels in Panel View mode is now automatically adjusted to content * Support for Virtual Panel View was removed @@ -264,9 +264,6 @@ The app relies and includes the following scripts/binaries: * Windows release is now bundled with UnRAR and 7za * Small GUI tweaks -## KNOWN ISSUES -* Removing SRCS headers sometimes fail in 32bit enviroments. Due to memory limitations. - ## COPYRIGHT Copyright (c) 2012-2013 Ciro Mattia Gonano and Paweł Jastrzębski. diff --git a/kcc/KCC_gui.py b/kcc/KCC_gui.py index 9ac509d9..dbc926b3 100644 --- a/kcc/KCC_gui.py +++ b/kcc/KCC_gui.py @@ -30,7 +30,7 @@ import urllib2 import time import comic2ebook -import kindlestrip +import kindlesplit from image import ProfileData from subprocess import call, Popen, STDOUT, PIPE from PyQt4 import QtGui, QtCore @@ -245,23 +245,27 @@ def run(self): True) else: self.emit(QtCore.SIGNAL("addMessage"), 'Creating MOBI file... Done!', 'info', True) - self.emit(QtCore.SIGNAL("addMessage"), 'Removing SRCS header...', 'info') + self.emit(QtCore.SIGNAL("addMessage"), 'Cleaning MOBI file...', 'info') os.remove(item) mobiPath = item.replace('.epub', '.mobi') - shutil.move(mobiPath, mobiPath + '_tostrip') + shutil.move(mobiPath, mobiPath + '_toclean') try: - kindlestrip.main((mobiPath + '_tostrip', mobiPath)) + if profile in ['K345', 'KHD', 'KF', 'KFHD', 'KFHD8', 'KFA']: + newKindle = True + else: + newKindle = False + mobisplit = kindlesplit.mobi_split(mobiPath + '_toclean', newKindle) + open(mobiPath, 'wb').write(mobisplit.getResult()) except Exception: self.errors = True if not self.errors: - os.remove(mobiPath + '_tostrip') - self.emit(QtCore.SIGNAL("addMessage"), 'Removing SRCS header... Done!', 'info', True) + os.remove(mobiPath + '_toclean') + self.emit(QtCore.SIGNAL("addMessage"), 'Cleaning MOBI file... Done!', 'info', True) else: - shutil.move(mobiPath + '_tostrip', mobiPath) + os.remove(mobiPath + '_toclean') + os.remove(mobiPath) self.emit(QtCore.SIGNAL("addMessage"), - 'KindleStrip failed to remove SRCS header!', 'warning') - self.emit(QtCore.SIGNAL("addMessage"), - 'MOBI file will work correctly but it will be highly oversized.', 'warning') + 'KindleUnpack failed to clean MOBI file!', 'error') else: epubSize = (os.path.getsize(item))/1024/1024 os.remove(item) diff --git a/kcc/kindlesplit.py b/kcc/kindlesplit.py new file mode 100644 index 00000000..3d2aeace --- /dev/null +++ b/kcc/kindlesplit.py @@ -0,0 +1,384 @@ +# Based on initial version of KindleUnpack. Copyright (C) 2009 Charles M. Hannum +# Improvements Copyright (C) 2009-2012 P. Durrant, K. Hendricks, S. Siebert, fandrieu, DiapDealer, nickredding +# Copyright (C) 2013 Pawel Jastrzebski +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# 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 , Pawel Jastrzebski ' +__docformat__ = 'restructuredtext en' + +import struct +from uuid import uuid4 + +# important pdb header offsets +unique_id_seed = 68 +number_of_pdb_records = 76 + +# important palmdoc header offsets +book_length = 4 +book_record_count = 8 +first_pdb_record = 78 + +# important rec0 offsets +length_of_book = 4 +mobi_header_base = 16 +mobi_header_length = 20 +mobi_type = 24 +mobi_version = 36 +first_non_text = 80 +title_offset = 84 +first_image_record = 108 +first_content_index = 192 +last_content_index = 194 +kf8_last_content_index = 192 # for KF8 mobi headers +fcis_index = 200 +flis_index = 208 +srcs_index = 224 +srcs_count = 228 +primary_index = 244 +datp_index = 256 +huffoff = 112 +hufftbloff = 120 + + +def getint(datain, ofs, sz='L'): + i, = struct.unpack_from('>'+sz, datain, ofs) + return i + + +def writeint(datain, ofs, n, length='L'): + if length == 'L': + return datain[:ofs]+struct.pack('>L', n)+datain[ofs+4:] + else: + return datain[:ofs]+struct.pack('>H', n)+datain[ofs+2:] + + +def getsecaddr(datain, secno): + nsec = getint(datain, number_of_pdb_records, 'H') + assert secno >= 0 & secno < nsec, 'secno %d out of range (nsec=%d)' % (secno, nsec) + secstart = getint(datain, first_pdb_record+secno*8) + if secno == nsec-1: + secend = len(datain) + else: + secend = getint(datain, first_pdb_record+(secno+1)*8) + return secstart, secend + + +def readsection(datain, secno): + secstart, secend = getsecaddr(datain, secno) + return datain[secstart:secend] + + +def writesection(datain, secno, secdata): # overwrite, accounting for different length + dataout = deletesectionrange(datain, secno, secno) + return insertsection(dataout, secno, secdata) + + +def nullsection(datain, secno): # make it zero-length without deleting it + datalst = [] + nsec = getint(datain, number_of_pdb_records, 'H') + secstart, secend = getsecaddr(datain, secno) + zerosecstart, zerosecend = getsecaddr(datain, 0) + dif = secend-secstart + datalst.append(datain[:first_pdb_record]) + for i in range(0, secno+1): + ofs, flgval = struct.unpack_from('>2L', datain, first_pdb_record+i*8) + datalst.append(struct.pack('>L', ofs) + struct.pack('>L', flgval)) + for i in range(secno+1, nsec): + ofs, flgval = struct.unpack_from('>2L', datain, first_pdb_record+i*8) + ofs -= dif + datalst.append(struct.pack('>L', ofs) + struct.pack('>L', flgval)) + lpad = zerosecstart - (first_pdb_record + 8*nsec) + if lpad > 0: + datalst.append('\0' * lpad) + datalst.append(datain[zerosecstart: secstart]) + datalst.append(datain[secend:]) + dataout = "".join(datalst) + return dataout + + +def deletesectionrange(datain, firstsec, lastsec): # delete a range of sections + datalst = [] + firstsecstart, firstsecend = getsecaddr(datain, firstsec) + lastsecstart, lastsecend = getsecaddr(datain, lastsec) + zerosecstart, zerosecend = getsecaddr(datain, 0) + dif = lastsecend - firstsecstart + 8*(lastsec-firstsec+1) + nsec = getint(datain, number_of_pdb_records, 'H') + datalst.append(datain[:unique_id_seed]) + datalst.append(struct.pack('>L', 2*(nsec-(lastsec-firstsec+1))+1)) + datalst.append(datain[unique_id_seed+4:number_of_pdb_records]) + datalst.append(struct.pack('>H', nsec-(lastsec-firstsec+1))) + newstart = zerosecstart - 8*(lastsec-firstsec+1) + for i in range(0, firstsec): + ofs, flgval = struct.unpack_from('>2L', datain, first_pdb_record+i*8) + ofs -= 8 * (lastsec - firstsec + 1) + datalst.append(struct.pack('>L', ofs) + struct.pack('>L', flgval)) + for i in range(lastsec+1, nsec): + ofs, flgval = struct.unpack_from('>2L', datain, first_pdb_record+i*8) + ofs -= dif + flgval = 2*(i-(lastsec-firstsec+1)) + datalst.append(struct.pack('>L', ofs) + struct.pack('>L', flgval)) + lpad = newstart - (first_pdb_record + 8*(nsec - (lastsec - firstsec + 1))) + if lpad > 0: + datalst.append('\0' * lpad) + datalst.append(datain[zerosecstart:firstsecstart]) + datalst.append(datain[lastsecend:]) + dataout = "".join(datalst) + return dataout + + +def insertsection(datain, secno, secdata): # insert a new section + datalst = [] + nsec = getint(datain, number_of_pdb_records, 'H') + secstart, secend = getsecaddr(datain, secno) + zerosecstart, zerosecend = getsecaddr(datain, 0) + dif = len(secdata) + datalst.append(datain[:unique_id_seed]) + datalst.append(struct.pack('>L', 2*(nsec+1)+1)) + datalst.append(datain[unique_id_seed+4:number_of_pdb_records]) + datalst.append(struct.pack('>H', nsec+1)) + newstart = zerosecstart + 8 + for i in range(0, secno): + ofs, flgval = struct.unpack_from('>2L', datain, first_pdb_record+i*8) + ofs += 8 + datalst.append(struct.pack('>L', ofs) + struct.pack('>L', flgval)) + datalst.append(struct.pack('>L', secstart + 8) + struct.pack('>L', (2*secno))) + for i in range(secno, nsec): + ofs, flgval = struct.unpack_from('>2L', datain, first_pdb_record+i*8) + ofs = ofs + dif + 8 + flgval = 2*(i+1) + datalst.append(struct.pack('>L', ofs) + struct.pack('>L', flgval)) + lpad = newstart - (first_pdb_record + 8*(nsec + 1)) + if lpad > 0: + datalst.append('\0' * lpad) + datalst.append(datain[zerosecstart:secstart]) + datalst.append(secdata) + datalst.append(datain[secstart:]) + dataout = "".join(datalst) + return dataout + + +def insertsectionrange(sectionsource, firstsec, lastsec, sectiontarget, targetsec): # insert a range of sections + dataout = sectiontarget + for idx in range(lastsec, firstsec-1, -1): + dataout = insertsection(dataout, targetsec, readsection(sectionsource, idx)) + return dataout + + +def get_exth_params(rec0): + ebase = mobi_header_base + getint(rec0, mobi_header_length) + elen = getint(rec0, ebase+4) + enum = getint(rec0, ebase+8) + return ebase, elen, enum + + +def add_exth(rec0, exth_num, exth_bytes): + ebase, elen, enum = get_exth_params(rec0) + newrecsize = 8+len(exth_bytes) + newrec0 = rec0[0:ebase+4]+struct.pack('>L', elen+newrecsize)+struct.pack('>L', enum+1) +\ + struct.pack('>L', exth_num) + struct.pack('>L', newrecsize)+exth_bytes+rec0[ebase+12:] + newrec0 = writeint(newrec0, title_offset, getint(newrec0, title_offset)+newrecsize) + return newrec0 + + +def read_exth(rec0, exth_num): + exth_values = [] + ebase, elen, enum = get_exth_params(rec0) + ebase += 12 + while enum > 0: + exth_id = getint(rec0, ebase) + if exth_id == exth_num: + # We might have multiple exths, so build a list. + exth_values.append(rec0[ebase+8:ebase+getint(rec0, ebase+4)]) + enum -= 1 + ebase = ebase+getint(rec0, ebase+4) + return exth_values + + +def write_exth(rec0, exth_num, exth_bytes): + ebase, elen, enum = get_exth_params(rec0) + ebase_idx = ebase+12 + enum_idx = enum + while enum_idx > 0: + exth_id = getint(rec0, ebase_idx) + if exth_id == exth_num: + dif = len(exth_bytes)+8-getint(rec0, ebase_idx+4) + newrec0 = rec0 + if dif != 0: + newrec0 = writeint(newrec0, title_offset, getint(newrec0, title_offset)+dif) + return newrec0[:ebase+4]+struct.pack('>L', elen+len(exth_bytes)+8-getint(rec0, ebase_idx+4)) +\ + struct.pack('>L', enum)+rec0[ebase+12:ebase_idx+4] +\ + struct.pack('>L', len(exth_bytes)+8)+exth_bytes +\ + rec0[ebase_idx+getint(rec0, ebase_idx+4):] + enum_idx -= 1 + ebase_idx = ebase_idx+getint(rec0, ebase_idx+4) + return rec0 + + +def del_exth(rec0, exth_num): + ebase, elen, enum = get_exth_params(rec0) + ebase_idx = ebase+12 + enum_idx = 0 + while enum_idx < enum: + exth_id = getint(rec0, ebase_idx) + exth_size = getint(rec0, ebase_idx+4) + if exth_id == exth_num: + newrec0 = rec0 + newrec0 = writeint(newrec0, title_offset, getint(newrec0, title_offset)-exth_size) + newrec0 = newrec0[:ebase_idx]+newrec0[ebase_idx+exth_size:] + newrec0 = newrec0[0:ebase+4]+struct.pack('>L', elen-exth_size)+struct.pack('>L', enum-1)+newrec0[ebase+12:] + return newrec0 + enum_idx += 1 + ebase_idx = ebase_idx+exth_size + return rec0 + + +class mobi_split: + def __init__(self, infile, newKindle): + try: + datain = open(infile, 'rb').read() + datain_rec0 = readsection(datain, 0) + ver = getint(datain_rec0, mobi_version) + fake_asin = str(uuid4()) + self.combo = (ver != 8) + if not self.combo: + return + exth121 = read_exth(datain_rec0, 121) + if len(exth121) == 0: + self.combo = False + return + else: + # only pay attention to first exth121 + # (there should only be one) + datain_kf8, = struct.unpack_from('>L', exth121[0], 0) + if datain_kf8 == 0xffffffff: + self.combo = False + return + datain_kfrec0 = readsection(datain, datain_kf8) + firstimage = getint(datain_rec0, first_image_record) + lastimage = getint(datain_rec0, last_content_index, 'H') + + if not newKindle: + # create the standalone mobi7 + num_sec = getint(datain, number_of_pdb_records, 'H') + # remove BOUNDARY up to but not including ELF record + self.result_file = deletesectionrange(datain, datain_kf8-1, num_sec-2) + # check if there are SRCS records and delete them + srcs = getint(datain_rec0, srcs_index) + num_srcs = getint(datain_rec0, srcs_count) + if srcs != 0xffffffff and num_srcs > 0: + self.result_file = deletesectionrange(self.result_file, srcs, srcs+num_srcs-1) + datain_rec0 = writeint(datain_rec0, srcs_index, 0xffffffff) + datain_rec0 = writeint(datain_rec0, srcs_count, 0) + # reset the EXTH 121 KF8 Boundary meta data to 0xffffffff + datain_rec0 = write_exth(datain_rec0, 121, struct.pack('>L', 0xffffffff)) + # datain_rec0 = del_exth(datain_rec0,121) + # datain_rec0 = del_exth(datain_rec0,534) + # don't remove the EXTH 125 KF8 Count of Resources, seems to be present in mobi6 files as well + # set the EXTH 129 KF8 Masthead / Cover Image string to the null string + datain_rec0 = write_exth(datain_rec0, 129, '') + # don't remove the EXTH 131 KF8 Unidentified Count, seems to be present in mobi6 files as well + + # Make sure we have an ASIN & cdeType set... + if len(read_exth(datain_rec0, 113)) == 0: + datain_rec0 = add_exth(datain_rec0, 113, fake_asin) + if len(read_exth(datain_rec0, 504)) == 0: + datain_rec0 = add_exth(datain_rec0, 504, fake_asin) + if len(read_exth(datain_rec0, 501)) == 0: + datain_rec0 = add_exth(datain_rec0, 501, b'EBOK') + + # need to reset flags stored in 0x80-0x83 + # old mobi with exth: 0x50, mobi7 part with exth: 0x1850, mobi8 part with exth: 0x1050 + # Bit Flags + # 0x1000 = Bit 12 indicates if embedded fonts are used or not + # 0x0800 = means this Header points to *shared* images/resource/fonts ?? + # 0x0080 = unknown new flag, why is this now being set by Kindlegen 2.8? + # 0x0040 = exth exists + # 0x0010 = Not sure but this is always set so far + fval, = struct.unpack_from('>L', datain_rec0, 0x80) + # need to remove flag 0x0800 for KindlePreviewer 2.8 and unset Bit 12 for embedded fonts + fval &= 0x07FF + datain_rec0 = datain_rec0[:0x80] + struct.pack('>L', fval) + datain_rec0[0x84:] + self.result_file = writesection(self.result_file, 0, datain_rec0) + if lastimage == 0xffff: + # find the lowest of the next sections and copy up to that. + ofs_list = [(kf8_last_content_index, 'L'), (fcis_index, 'L'), (flis_index, 'L'), (datp_index, 'L'), + (hufftbloff, 'L')] + for ofs, sz in ofs_list: + n = getint(datain_kfrec0, ofs, sz) + if 0 < n < lastimage: + lastimage = n-1 + + # Try to null out FONT and RES, but leave the (empty) PDB record so image refs remain valid + for i in range(firstimage, lastimage): + imgsec = readsection(self.result_file, i) + if imgsec[0:4] in ['RESC', 'FONT']: + self.result_file = nullsection(self.result_file, i) + # mobi7 finished + else: + # create standalone mobi8 + self.result_file = deletesectionrange(datain, 0, datain_kf8-1) + target = getint(datain_kfrec0, first_image_record) + self.result_file = insertsectionrange(datain, firstimage, lastimage, self.result_file, target) + datain_kfrec0 = readsection(self.result_file, 0) + + # Only keep the correct EXTH 116 StartOffset, KG 2.5 carries over the one from the mobi7 part, + # which then points at garbage in the mobi8 part, and confuses FW 3.4 + kf8starts = read_exth(datain_kfrec0, 116) + # If we have multiple StartOffset, keep only the last one + kf8start_count = len(kf8starts) + while kf8start_count > 1: + kf8start_count -= 1 + datain_kfrec0 = del_exth(datain_kfrec0, 116) + + # update the EXTH 125 KF8 Count of Images/Fonts/Resources + datain_kfrec0 = write_exth(datain_kfrec0, 125, struct.pack('>L', lastimage-firstimage+1)) + + # Same dance for the KF8, we want an ASIN & cdeType :) + if len(read_exth(datain_kfrec0, 113)) == 0: + datain_kfrec0 = add_exth(datain_kfrec0, 113, fake_asin) + if len(read_exth(datain_kfrec0, 504)) == 0: + datain_kfrec0 = add_exth(datain_kfrec0, 504, fake_asin) + if len(read_exth(datain_kfrec0, 501)) == 0: + datain_kfrec0 = add_exth(datain_kfrec0, 501, b'EBOK') + + # need to reset flags stored in 0x80-0x83 + # old mobi with exth: 0x50, mobi7 part with exth: 0x1850, mobi8 part with exth: 0x1050 + # standalone mobi8 with exth: 0x0050 + # Bit Flags + # 0x1000 = Bit 12 indicates if embedded fonts are used or not + # 0x0800 = means this Header points to *shared* images/resource/fonts ?? + # 0x0080 = unknown new flag, why is this now being set by Kindlegen 2.8? + # 0x0040 = exth exists + # 0x0010 = Not sure but this is always set so far + fval, = struct.unpack_from('>L', datain_kfrec0, 0x80) + fval &= 0x1FFF + fval |= 0x0800 + datain_kfrec0 = datain_kfrec0[:0x80] + struct.pack('>L', fval) + datain_kfrec0[0x84:] + + # properly update other index pointers that have been shifted by the insertion of images + ofs_list = [(kf8_last_content_index, 'L'), (fcis_index, 'L'), (flis_index, 'L'), (datp_index, 'L'), + (hufftbloff, 'L')] + for ofs, sz in ofs_list: + n = getint(datain_kfrec0, ofs, sz) + if n != 0xffffffff: + datain_kfrec0 = writeint(datain_kfrec0, ofs, n+lastimage-firstimage+1, sz) + self.result_file = writesection(self.result_file, 0, datain_kfrec0) + # mobi8 finished + except Exception: + raise + + def getResult(self): + return self.result_file \ No newline at end of file diff --git a/kcc/kindlestrip.py b/kcc/kindlestrip.py deleted file mode 100755 index 4aea003d..00000000 --- a/kcc/kindlestrip.py +++ /dev/null @@ -1,236 +0,0 @@ -#!/usr/bin/env python -# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai -# -# This is a python script. You need a Python interpreter to run it. -# For example, ActiveState Python, which exists for windows. -# -# This script strips the penultimate record from a Mobipocket file. -# This is useful because the current KindleGen add a compressed copy -# of the source files used in this record, making the ebook produced -# about twice as big as it needs to be. -# -# -# This is free and unencumbered software released into the public domain. -# -# Anyone is free to copy, modify, publish, use, compile, sell, or -# distribute this software, either in source code form or as a compiled -# binary, for any purpose, commercial or non-commercial, and by any -# means. -# -# In jurisdictions that recognize copyright laws, the author or authors -# of this software dedicate any and all copyright interest in the -# software to the public domain. We make this dedication for the benefit -# of the public at large and to the detriment of our heirs and -# successors. We intend this dedication to be an overt act of -# relinquishment in perpetuity of all present and future rights to this -# software under copyright law. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR -# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -# OTHER DEALINGS IN THE SOFTWARE. -# -# For more information, please refer to -# -# Written by Paul Durrant, 2010-2011, paul@durrant.co.uk, pdurrant on mobileread.com -# With enhancements by Kevin Hendricks, KevinH on mobileread.com -# -# Changelog -# 1.00 - Initial version -# 1.10 - Added an option to output the stripped data -# 1.20 - Added check for source files section (thanks Piquan) -# 1.30 - Added prelim Support for K8 style mobis -# 1.31 - removed the SRCS section but kept a 0 size entry for it -# 1.32 - removes the SRCS section and its entry, now updates metadata 121 if needed -# 1.33 - now uses and modifies mobiheader SRCS and CNT -# 1.34 - added credit for Kevin Hendricks -# 1.35 - fixed bug when more than one compilation (SRCS/CMET) records - -__version__ = '1.35' - -import sys -import struct -import binascii - -class Unbuffered: - def __init__(self, stream): - self.stream = stream - def write(self, data): - self.stream.write(data) - self.stream.flush() - def __getattr__(self, attr): - return getattr(self.stream, attr) - - -class StripException(Exception): - pass - - -class SectionStripper: - def loadSection(self, section): - if (section + 1 == self.num_sections): - endoff = len(self.data_file) - else: - endoff = self.sections[section + 1][0] - off = self.sections[section][0] - return self.data_file[off:endoff] - - def patch(self, off, new): - self.data_file = self.data_file[:off] + new + self.data_file[off+len(new):] - - def strip(self, off, len): - self.data_file = self.data_file[:off] + self.data_file[off+len:] - - def patchSection(self, section, new, in_off = 0): - if (section + 1 == self.num_sections): - endoff = len(self.data_file) - else: - endoff = self.sections[section + 1][0] - off = self.sections[section][0] - assert off + in_off + len(new) <= endoff - self.patch(off + in_off, new) - - def updateEXTH121(self, srcs_secnum, srcs_cnt, mobiheader): - mobi_length, = struct.unpack('>L',mobiheader[0x14:0x18]) - exth_flag, = struct.unpack('>L', mobiheader[0x80:0x84]) - exth = 'NONE' - try: - if exth_flag & 0x40: - exth = mobiheader[16 + mobi_length:] - if (len(exth) >= 4) and (exth[:4] == 'EXTH'): - nitems, = struct.unpack('>I', exth[8:12]) - pos = 12 - for i in xrange(nitems): - type, size = struct.unpack('>II', exth[pos: pos + 8]) - # print type, size - if type == 121: - boundaryptr, =struct.unpack('>L',exth[pos+8: pos + size]) - if srcs_secnum <= boundaryptr: - boundaryptr -= srcs_cnt - prefix = mobiheader[0:16 + mobi_length + pos + 8] - suffix = mobiheader[16 + mobi_length + pos + 8 + 4:] - nval = struct.pack('>L',boundaryptr) - mobiheader = prefix + nval + suffix - pos += size - except: - pass - return mobiheader - - def __init__(self, datain): - if datain[0x3C:0x3C+8] != 'BOOKMOBI': - raise StripException("invalid file format") - self.num_sections, = struct.unpack('>H', datain[76:78]) - - # get mobiheader and check SRCS section number and count - offset0, = struct.unpack_from('>L', datain, 78) - offset1, = struct.unpack_from('>L', datain, 86) - mobiheader = datain[offset0:offset1] - srcs_secnum, srcs_cnt = struct.unpack_from('>2L', mobiheader, 0xe0) - if srcs_secnum == 0xffffffff or srcs_cnt == 0: - raise StripException("File doesn't contain the sources section.") - - print "Found SRCS section number %d, and count %d" % (srcs_secnum, srcs_cnt) - # find its offset and length - next = srcs_secnum + srcs_cnt - srcs_offset, flgval = struct.unpack_from('>2L', datain, 78+(srcs_secnum*8)) - next_offset, flgval = struct.unpack_from('>2L', datain, 78+(next*8)) - srcs_length = next_offset - srcs_offset - if datain[srcs_offset:srcs_offset+4] != 'SRCS': - raise StripException("SRCS section num does not point to SRCS.") - print " beginning at offset %0x and ending at offset %0x" % (srcs_offset, srcs_length) - - # it appears bytes 68-71 always contain (2*num_sections) + 1 - # this is not documented anyplace at all but it appears to be some sort of next - # available unique_id used to identify specific sections in the palm db - self.data_file = datain[:68] + struct.pack('>L',((self.num_sections-srcs_cnt)*2+1)) - self.data_file += datain[72:76] - - # write out the number of sections reduced by srtcs_cnt - self.data_file = self.data_file + struct.pack('>H',self.num_sections-srcs_cnt) - - # we are going to remove srcs_cnt SRCS sections so the offset of every entry in the table - # up to the srcs secnum must begin 8 bytes earlier per section removed (each table entry is 8 ) - delta = -8 * srcs_cnt - for i in xrange(srcs_secnum): - offset, flgval = struct.unpack_from('>2L', datain, 78+(i*8)) - offset += delta - self.data_file += struct.pack('>L',offset) + struct.pack('>L',flgval) - - # for every record after the srcs_cnt SRCS records we must start it - # earlier by 8*srcs_cnt + the length of the srcs sections themselves) - delta = delta - srcs_length - for i in xrange(srcs_secnum+srcs_cnt,self.num_sections): - offset, flgval = struct.unpack_from('>2L', datain, 78+(i*8)) - offset += delta - flgval = 2 * (i - srcs_cnt) - self.data_file += struct.pack('>L',offset) + struct.pack('>L',flgval) - - # now pad it out to begin right at the first offset - # typically this is 2 bytes of nulls - first_offset, flgval = struct.unpack_from('>2L', self.data_file, 78) - self.data_file += '\0' * (first_offset - len(self.data_file)) - - # now finally add on every thing up to the original src_offset - self.data_file += datain[offset0: srcs_offset] - - # and everything afterwards - self.data_file += datain[srcs_offset+srcs_length:] - - #store away the SRCS section in case the user wants it output - self.stripped_data_header = datain[srcs_offset:srcs_offset+16] - self.stripped_data = datain[srcs_offset+16:srcs_offset+srcs_length] - - # update the number of sections count - self.num_section = self.num_sections - srcs_cnt - - # update the srcs_secnum and srcs_cnt in the mobiheader - offset0, flgval0 = struct.unpack_from('>2L', self.data_file, 78) - offset1, flgval1 = struct.unpack_from('>2L', self.data_file, 86) - mobiheader = self.data_file[offset0:offset1] - mobiheader = mobiheader[:0xe0]+ struct.pack('>L', 0xffffffff) + struct.pack('>L', 0) + mobiheader[0xe8:] - - # if K8 mobi, handle metadata 121 in old mobiheader - mobiheader = self.updateEXTH121(srcs_secnum, srcs_cnt, mobiheader) - self.data_file = self.data_file[0:offset0] + mobiheader + self.data_file[offset1:] - print "done" - - def getResult(self): - return self.data_file - - def getStrippedData(self): - return self.stripped_data - - def getHeader(self): - return self.stripped_data_header - -def main(argv=None): - infile = argv[0] - outfile = argv[1] - data_file = file(infile, 'rb').read() - try: - strippedFile = SectionStripper(data_file) - file(outfile, 'wb').write(strippedFile.getResult()) - print "Header Bytes: " + binascii.b2a_hex(strippedFile.getHeader()) - if len(argv)==3: - file(argv[2], 'wb').write(strippedFile.getStrippedData()) - except StripException, e: - print "Error: %s" % e - sys.exit(1) - -if __name__ == "__main__": - sys.stdout=Unbuffered(sys.stdout) - print ('KindleStrip v%(__version__)s. ' - 'Written 2010-2012 by Paul Durrant and Kevin Hendricks.' % globals()) - if len(sys.argv)<3 or len(sys.argv)>4: - print "Strips the Sources record from Mobipocket ebooks" - print "For ebooks generated using KindleGen 1.1 and later that add the source" - print "Usage:" - print " %s " % sys.argv[0] - print " is optional." - sys.exit(1) - else: - main(sys.argv[1:]) - sys.exit(0) From 4c49c6ffc92d5279f5c2490b9442ce16cb44d50d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Mon, 23 Sep 2013 11:31:44 +0200 Subject: [PATCH 20/26] False ASIN is breaking thumbnail --- kcc/kindlesplit.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/kcc/kindlesplit.py b/kcc/kindlesplit.py index 3d2aeace..c8ae74d5 100644 --- a/kcc/kindlesplit.py +++ b/kcc/kindlesplit.py @@ -20,7 +20,7 @@ __docformat__ = 'restructuredtext en' import struct -from uuid import uuid4 +# from uuid import uuid4 # important pdb header offsets unique_id_seed = 68 @@ -251,7 +251,7 @@ def __init__(self, infile, newKindle): datain = open(infile, 'rb').read() datain_rec0 = readsection(datain, 0) ver = getint(datain_rec0, mobi_version) - fake_asin = str(uuid4()) + # fake_asin = str(uuid4()) self.combo = (ver != 8) if not self.combo: return @@ -292,10 +292,10 @@ def __init__(self, infile, newKindle): # don't remove the EXTH 131 KF8 Unidentified Count, seems to be present in mobi6 files as well # Make sure we have an ASIN & cdeType set... - if len(read_exth(datain_rec0, 113)) == 0: - datain_rec0 = add_exth(datain_rec0, 113, fake_asin) - if len(read_exth(datain_rec0, 504)) == 0: - datain_rec0 = add_exth(datain_rec0, 504, fake_asin) + # if len(read_exth(datain_rec0, 113)) == 0: + # datain_rec0 = add_exth(datain_rec0, 113, fake_asin) + # if len(read_exth(datain_rec0, 504)) == 0: + # datain_rec0 = add_exth(datain_rec0, 504, fake_asin) if len(read_exth(datain_rec0, 501)) == 0: datain_rec0 = add_exth(datain_rec0, 501, b'EBOK') @@ -347,10 +347,10 @@ def __init__(self, infile, newKindle): datain_kfrec0 = write_exth(datain_kfrec0, 125, struct.pack('>L', lastimage-firstimage+1)) # Same dance for the KF8, we want an ASIN & cdeType :) - if len(read_exth(datain_kfrec0, 113)) == 0: - datain_kfrec0 = add_exth(datain_kfrec0, 113, fake_asin) - if len(read_exth(datain_kfrec0, 504)) == 0: - datain_kfrec0 = add_exth(datain_kfrec0, 504, fake_asin) + # if len(read_exth(datain_kfrec0, 113)) == 0: + # datain_kfrec0 = add_exth(datain_kfrec0, 113, fake_asin) + # if len(read_exth(datain_kfrec0, 504)) == 0: + # datain_kfrec0 = add_exth(datain_kfrec0, 504, fake_asin) if len(read_exth(datain_kfrec0, 501)) == 0: datain_kfrec0 = add_exth(datain_kfrec0, 501, b'EBOK') From 9168cd4109cfb344ba490464c396b76d3ff0b3d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Mon, 23 Sep 2013 17:17:46 +0200 Subject: [PATCH 21/26] Tag goodies made with KCC properly :-) --- kcc/comic2ebook.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py index 4ac8acbc..fd822a25 100755 --- a/kcc/comic2ebook.py +++ b/kcc/comic2ebook.py @@ -212,6 +212,7 @@ def buildOPF(dstdir, title, filelist, cover=None): "", title, "\n", "en-US\n", "", options.uuid, "\n", + "KCC\n", "\n", "\n", "\n", From f781b6785cf43b240e47d412fbfc978131ff5bbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Mon, 23 Sep 2013 18:35:30 +0200 Subject: [PATCH 22/26] Fill detection final, final improvements --- kcc/comic2ebook.py | 5 +++-- kcc/image.py | 39 ++++++++++++++++++--------------------- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py index fd822a25..480c03d8 100755 --- a/kcc/comic2ebook.py +++ b/kcc/comic2ebook.py @@ -288,15 +288,16 @@ def getImageFileName(imgfile): def applyImgOptimization(img, options, overrideQuality=5): + img.getImageFill(options.webtoon) if not options.webtoon: img.cropWhiteSpace(10.0) if options.cutpagenumbers and not options.webtoon: img.cutPageNumber() img.optimizeImage(options.gamma) if overrideQuality != 5: - img.resizeImage(options.upscale, options.stretch, options.bordersColor, overrideQuality, options.webtoon) + img.resizeImage(options.upscale, options.stretch, options.bordersColor, overrideQuality) else: - img.resizeImage(options.upscale, options.stretch, options.bordersColor, options.quality, options.webtoon) + img.resizeImage(options.upscale, options.stretch, options.bordersColor, options.quality) if options.forcepng and not options.forcecolor: img.quantizeImage() diff --git a/kcc/image.py b/kcc/image.py index eae916f4..37db5946 100755 --- a/kcc/image.py +++ b/kcc/image.py @@ -151,6 +151,7 @@ def __init__(self, source, device): self.border = None self.noHPV = None self.noVPV = None + self.fill = None def saveToDir(self, targetdir, forcepng, color, wipe): try: @@ -194,12 +195,12 @@ def quantizeImage(self): palImg.putpalette(self.palette) self.image = self.image.quantize(palette=palImg) - def resizeImage(self, upscale=False, stretch=False, bordersColor=None, qualityMode=0, isWebToon=False): + def resizeImage(self, upscale=False, stretch=False, bordersColor=None, qualityMode=0): method = Image.ANTIALIAS if bordersColor: fill = bordersColor else: - fill = self.getImageFill(isWebToon) + fill = self.fill if qualityMode == 0: size = (self.size[0], self.size[1]) generateBorder = True @@ -422,29 +423,25 @@ def getImageHistogram(self, image): def getImageFill(self, isWebToon): fill = 0 if isWebToon or self.rotated: - fill += self.getImageHistogram(self.image.crop((0, 0, self.image.size[0], 1))) - fill += self.getImageHistogram(self.image.crop((0, self.image.size[1]-1, self.image.size[0], + fill += self.getImageHistogram(self.image.crop((0, 0, self.image.size[0], 5))) + fill += self.getImageHistogram(self.image.crop((0, self.image.size[1]-5, self.image.size[0], self.image.size[1]))) else: - fill += self.getImageHistogram(self.image.crop((0, 0, 1, self.image.size[1]))) - fill += self.getImageHistogram(self.image.crop((self.image.size[0]-1, 0, self.image.size[0], + fill += self.getImageHistogram(self.image.crop((0, 0, 5, self.image.size[1]))) + fill += self.getImageHistogram(self.image.crop((self.image.size[0]-5, 0, self.image.size[0], self.image.size[1]))) if fill == 2: - return 'black' + self.fill = 'black' elif fill == 0: - return 'white' + self.fill = 'white' else: - bBox = self.image.getbbox() - wBox = ImageOps.invert(self.image).getbbox() - if bBox is None: - bBox = 0 + fill = 0 + fill += self.getImageHistogram(self.image.crop((0, 0, 5, 5))) + fill += self.getImageHistogram(self.image.crop((self.image.size[0]-5, 0, self.image.size[0], 5))) + fill += self.getImageHistogram(self.image.crop((0, self.image.size[1]-5, 5, self.image.size[1]))) + fill += self.getImageHistogram(self.image.crop((self.image.size[0]-5, self.image.size[1]-5, + self.image.size[0], self.image.size[1]))) + if fill > 1: + self.fill = 'black' else: - bBox = (bBox[2]-bBox[0])*(bBox[3]-bBox[1]) - if wBox is None: - wBox = 0 - else: - wBox = (wBox[2]-wBox[0])*(wBox[3]-wBox[1]) - if wBox <= bBox: - return "white" - else: - return "black" \ No newline at end of file + self.fill = 'white' \ No newline at end of file From 43638813d7fec6505e8c07dd18e8390cd52abbe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Wed, 25 Sep 2013 11:47:40 +0200 Subject: [PATCH 23/26] Fill detection final, final, final improvements --- kcc/image.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/kcc/image.py b/kcc/image.py index 37db5946..1db3510e 100755 --- a/kcc/image.py +++ b/kcc/image.py @@ -407,7 +407,9 @@ def cropWhiteSpace(self, threshold): def getImageHistogram(self, image): histogram = image.histogram() RBGW = [] + pixelCount = 0 for i in range(256): + pixelCount += histogram[i] + histogram[256 + i] + histogram[512 + i] RBGW.append(histogram[i] + histogram[256 + i] + histogram[512 + i]) white = 0 black = 0 @@ -415,10 +417,10 @@ def getImageHistogram(self, image): white += RBGW[i] for i in range(11): black += RBGW[i] - if white > black: - return False - else: + if black > white and black > pixelCount*0.5: return True + else: + return False def getImageFill(self, isWebToon): fill = 0 From 27e01657b160a337ba1bae27be0191ab367623ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Wed, 25 Sep 2013 12:33:47 +0200 Subject: [PATCH 24/26] Added support for Kindle Fire HDX --- README.md | 5 ++++- kcc/KCC_gui.py | 10 +++++----- kcc/comic2ebook.py | 7 ++++--- kcc/image.py | 29 +++++++++++++++++++---------- 4 files changed, 32 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index fd068902..90e987db 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ Usage: comic2ebook.py [options] comic_file|comic_folder Options: MAIN: -p PROFILE, --profile=PROFILE - Device profile (Choose one among K1, K2, K345, KDX, KDXG, KHD, KF, KFHD, KFHD8, KFA) [Default=KHD] + Device profile (Choose one among K1, K2, K345, KDX, KDXG, KHD, KF, KFHD, KFHD8, KFHDX, KFHDX8, KFA) [Default=KHD] -q QUALITY, --quality=QUALITY Quality of Panel View. 0 - Normal 1 - High 2 - Ultra [Default=0] -m, --manga-style Manga style (Right-to-left reading and splitting) @@ -141,6 +141,8 @@ The app relies and includes the following scripts/binaries: * [Kindle Fire](http://kcc.vulturis.eu/Samples/Ubunchu!-KF.mobi) * [Kindle Fire HD](http://kcc.vulturis.eu/Samples/Ubunchu!-KFHD.mobi) * [Kindle Fire HD 8.9"](http://kcc.vulturis.eu/Samples/Ubunchu!-KFHD8.mobi) +* [Kindle Fire HDX](http://kcc.vulturis.eu/Samples/Ubunchu!-KFHDX.mobi) +* [Kindle Fire HDX 8.9"](http://kcc.vulturis.eu/Samples/Ubunchu!-KFHDX8.mobi) * [Kindle DX](http://kcc.vulturis.eu/Samples/Ubunchu!-KDX.mobi) * [Kindle DXG](http://kcc.vulturis.eu/Samples/Ubunchu!-KDXG.mobi) @@ -260,6 +262,7 @@ The app relies and includes the following scripts/binaries: * Support for Virtual Panel View was removed * Margin color fill is now autodetected * Added support of 7z/CB7 files +* Added Kindle Fire HDX profile * Profiles for Kindle Keyboard, Touch and Non-Touch are now merged * Windows release is now bundled with UnRAR and 7za * Small GUI tweaks diff --git a/kcc/KCC_gui.py b/kcc/KCC_gui.py index dbc926b3..b022a9de 100644 --- a/kcc/KCC_gui.py +++ b/kcc/KCC_gui.py @@ -250,7 +250,7 @@ def run(self): mobiPath = item.replace('.epub', '.mobi') shutil.move(mobiPath, mobiPath + '_toclean') try: - if profile in ['K345', 'KHD', 'KF', 'KFHD', 'KFHD8', 'KFA']: + if profile in ['K345', 'KHD', 'KF', 'KFHD', 'KFHD8', 'KFHDX', 'KFHDX8', 'KFA']: newKindle = True else: newKindle = False @@ -469,13 +469,13 @@ def toggleNoSplitRotate(self, value): GUI.RotateBox.setEnabled(True) def changeDevice(self, value): - if value == 8: + if value == 9: GUI.BasicModeButton.setEnabled(False) GUI.AdvModeButton.setEnabled(False) self.addMessage('' 'List of supported Non-Kindle devices', 'info') self.modeExpert() - elif value == 7: + elif value == 8: GUI.BasicModeButton.setEnabled(False) GUI.AdvModeButton.setEnabled(False) self.modeExpert(True) @@ -483,13 +483,13 @@ def changeDevice(self, value): GUI.BasicModeButton.setEnabled(True) GUI.AdvModeButton.setEnabled(True) self.modeBasic() - if value in [8, 9, 10, 11, 12]: + if value in [9, 11, 12, 13, 14]: GUI.QualityBox.setCheckState(0) GUI.QualityBox.setEnabled(False) else: if not GUI.WebtoonBox.isChecked(): GUI.QualityBox.setEnabled(True) - if not value in [8]: + if value in [3, 4, 5, 6, 8, 15]: GUI.NoDitheringBox.setCheckState(0) GUI.NoDitheringBox.setEnabled(False) else: diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py index 480c03d8..0f5c1fed 100755 --- a/kcc/comic2ebook.py +++ b/kcc/comic2ebook.py @@ -796,8 +796,8 @@ def main(argv=None, qtGUI=None): customProfileOptions = OptionGroup(parser, "CUSTOM PROFILE") otherOptions = OptionGroup(parser, "OTHER") mainOptions.add_option("-p", "--profile", action="store", dest="profile", default="KHD", - help="Device profile (Choose one among K1, K2, K345, KDX, KDXG, KHD, KF, KFHD, KFHD8, KFA)" - "[Default=KHD]") + help="Device profile (Choose one among K1, K2, K345, KDX, KDXG, KHD, KF, KFHD, KFHD8, KFHDX," + " KFHDX8, KFA) [Default=KHD]") 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, @@ -935,7 +935,8 @@ def checkOptions(): if options.black_borders: options.bordersColor = "black" # Disabling grayscale conversion for Kindle Fire family. - if options.profile == 'KF' or options.profile == 'KFHD' or options.profile == 'KFHD8' or options.forcecolor: + if options.profile == 'KF' or options.profile == 'KFHD' or options.profile == 'KFHD8' or options.profile == 'KFHDX'\ + or options.profile == 'KFHDX8' or options.forcecolor: options.forcecolor = True else: options.forcecolor = False diff --git a/kcc/image.py b/kcc/image.py index 1db3510e..b029eb7b 100755 --- a/kcc/image.py +++ b/kcc/image.py @@ -74,6 +74,9 @@ class ProfileData: 0xff, 0xff, 0xff, ] + PalleteNull = [ + ] + Profiles = { 'K1': ("Kindle 1", (600, 800), Palette4, 1.8, (900, 1200)), 'K2': ("Kindle 2", (600, 800), Palette15, 1.8, (900, 1200)), @@ -81,10 +84,12 @@ class ProfileData: 'KHD': ("Kindle Paperwhite", (758, 1024), Palette16, 1.8, (1137, 1536)), 'KDX': ("Kindle DX", (824, 1200), Palette15, 1.8, (1236, 1800)), 'KDXG': ("Kindle DXG", (824, 1200), Palette16, 1.8, (1236, 1800)), - 'KF': ("Kindle Fire", (600, 1024), Palette16, 1.0, (900, 1536)), - 'KFHD': ("Kindle Fire HD 7\"", (800, 1280), Palette16, 1.0, (1200, 1920)), - 'KFHD8': ("Kindle Fire HD 8.9\"", (1200, 1920), Palette16, 1.0, (1800, 2880)), - 'KFA': ("Kindle for Android", (0, 0), Palette16, 1.0, (0, 0)), + 'KF': ("Kindle Fire", (600, 1024), PalleteNull, 1.0, (900, 1536)), + 'KFHD': ("K. Fire HD 7\"", (800, 1280), PalleteNull, 1.0, (1200, 1920)), + 'KFHD8': ("K. Fire HD 8.9\"", (1200, 1920), PalleteNull, 1.0, (1800, 2880)), + 'KFHDX': ("K. Fire HDX 7\"", (1200, 1920), PalleteNull, 1.0, (1800, 2880)), + 'KFHDX8': ("K. Fire HDX 8.9\"", (1600, 2560), PalleteNull, 1.0, (2400, 3840)), + 'KFA': ("Kindle for Android", (0, 0), PalleteNull, 1.0, (0, 0)), 'OTHER': ("Other", (0, 0), Palette16, 1.8, (0, 0)), } @@ -96,8 +101,10 @@ class ProfileData: "Kindle DX": 'KDX', "Kindle DXG": 'KDXG', "Kindle Fire": 'KF', - "Kindle Fire HD 7\"": 'KFHD', - "Kindle Fire HD 8.9\"": 'KFHD8', + "K. Fire HD 7\"": 'KFHD', + "K. Fire HD 8.9\"": 'KFHD8', + "K. Fire HDX 7\"": 'KFHDX', + "K. Fire HDX 8.9\"": 'KFHDX8', "Kindle for Android": 'KFA', "Other": 'OTHER' } @@ -106,9 +113,10 @@ class ProfileData: "Kindle Paperwhite", "Kindle", "Separator", - "Kindle Fire", - "Kindle Fire HD 7\"", - "Kindle Fire HD 8.9\"", + "K. Fire HD 7\"", + "K. Fire HD 8.9\"", + "K. Fire HDX 7\"", + "K. Fire HDX 8.9\"", "Separator", "Kindle for Android", "Other", @@ -116,7 +124,8 @@ class ProfileData: "Kindle 1", "Kindle 2", "Kindle DX", - "Kindle DXG" + "Kindle DXG", + "Kindle Fire" ] From 97b44a89d4d421a6a584c34c976444f7775ab49e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Sat, 28 Sep 2013 19:57:49 +0200 Subject: [PATCH 25/26] Code cleanup --- README.md | 3 +- kcc/KCC_gui.py | 5 +++ kcc/comic2ebook.py | 79 +++++++++++++++++++++++----------------------- kcc/comic2panel.py | 30 +++++++++--------- kcc/image.py | 3 ++ 5 files changed, 63 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 90e987db..a8eeaaaf 100644 --- a/README.md +++ b/README.md @@ -138,13 +138,12 @@ The app relies and includes the following scripts/binaries: ## SAMPLE FILES CREATED BY KCC * [Kindle Paperwhite](http://kcc.vulturis.eu/Samples/Ubunchu!-KPW.mobi) * [Kindle](http://kcc.vulturis.eu/Samples/Ubunchu!-K345.mobi) +* [Kindle DX](http://kcc.vulturis.eu/Samples/Ubunchu!-KDX.mobi) * [Kindle Fire](http://kcc.vulturis.eu/Samples/Ubunchu!-KF.mobi) * [Kindle Fire HD](http://kcc.vulturis.eu/Samples/Ubunchu!-KFHD.mobi) * [Kindle Fire HD 8.9"](http://kcc.vulturis.eu/Samples/Ubunchu!-KFHD8.mobi) * [Kindle Fire HDX](http://kcc.vulturis.eu/Samples/Ubunchu!-KFHDX.mobi) * [Kindle Fire HDX 8.9"](http://kcc.vulturis.eu/Samples/Ubunchu!-KFHDX8.mobi) -* [Kindle DX](http://kcc.vulturis.eu/Samples/Ubunchu!-KDX.mobi) -* [Kindle DXG](http://kcc.vulturis.eu/Samples/Ubunchu!-KDXG.mobi) ## CHANGELOG ####1.00 diff --git a/kcc/KCC_gui.py b/kcc/KCC_gui.py index b022a9de..4b6e022e 100644 --- a/kcc/KCC_gui.py +++ b/kcc/KCC_gui.py @@ -99,6 +99,10 @@ class WorkerThread(QtCore.QThread): def __init__(self, parent): QtCore.QThread.__init__(self) self.parent = parent + self.conversionAlive = False + self.errors = False + self.kindlegenErrorCode = 0 + self.kindlegenError = None def __del__(self): self.wait() @@ -610,6 +614,7 @@ def __init__(self, UI, KCC): self.versionCheck = VersionThread(self) self.conversionAlive = False self.needClean = True + self.GammaValue = 1.0 self.addMessage('Welcome!', 'info') self.addMessage('Remember: All options have additional informations in tooltips.', 'info') diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py index 0f5c1fed..cb0204ad 100755 --- a/kcc/comic2ebook.py +++ b/kcc/comic2ebook.py @@ -287,23 +287,22 @@ def getImageFileName(imgfile): return filename -def applyImgOptimization(img, options, overrideQuality=5): - img.getImageFill(options.webtoon) - if not options.webtoon: +def applyImgOptimization(img, opt, overrideQuality=5): + img.getImageFill(opt.webtoon) + if not opt.webtoon: img.cropWhiteSpace(10.0) - if options.cutpagenumbers and not options.webtoon: + if opt.cutpagenumbers and not opt.webtoon: img.cutPageNumber() - img.optimizeImage(options.gamma) + img.optimizeImage(opt.gamma) if overrideQuality != 5: - img.resizeImage(options.upscale, options.stretch, options.bordersColor, overrideQuality) + img.resizeImage(opt.upscale, opt.stretch, opt.bordersColor, overrideQuality) else: - img.resizeImage(options.upscale, options.stretch, options.bordersColor, options.quality) - if options.forcepng and not options.forcecolor: + img.resizeImage(opt.upscale, opt.stretch, opt.bordersColor, opt.quality) + if opt.forcepng and not opt.forcecolor: img.quantizeImage() def dirImgProcess(path): - global options work = [] pagenumber = 0 pagenumbermodifier = 0 @@ -349,9 +348,9 @@ def dirImgProcess(path): raise UserWarning("Source directory is empty.") -def fileImgProcess_init(queue, options): +def fileImgProcess_init(queue, opt): fileImgProcess.queue = queue - fileImgProcess.options = options + fileImgProcess.options = opt # noinspection PyUnresolvedReferences @@ -359,54 +358,53 @@ def fileImgProcess(work): afile = work[0] dirpath = work[1] pagenumber = work[2] - options = fileImgProcess.options + opt = fileImgProcess.options output = None - if options.verbose: - print "Optimizing " + afile + " for " + options.profile + if opt.verbose: + print "Optimizing " + afile + " for " + opt.profile else: print ".", fileImgProcess.queue.put(".") - img = image.ComicPage(os.path.join(dirpath, afile), options.profileData) - if options.quality == 2: + img = image.ComicPage(os.path.join(dirpath, afile), opt.profileData) + if opt.quality == 2: wipe = False else: wipe = True - if options.nosplitrotate: + if opt.nosplitrotate: split = None else: - split = img.splitPage(dirpath, options.righttoleft, options.rotate) + split = img.splitPage(dirpath, opt.righttoleft, opt.rotate) if split is not None: - if options.verbose: + if opt.verbose: print "Splitted " + afile output = pagenumber - img0 = image.ComicPage(split[0], options.profileData) - applyImgOptimization(img0, options) - img0.saveToDir(dirpath, options.forcepng, options.forcecolor, wipe) - img1 = image.ComicPage(split[1], options.profileData) - applyImgOptimization(img1, options) - img1.saveToDir(dirpath, options.forcepng, options.forcecolor, wipe) - if options.quality == 2: - img3 = image.ComicPage(split[0], options.profileData) - applyImgOptimization(img3, options, 0) - img3.saveToDir(dirpath, options.forcepng, options.forcecolor, True) - img4 = image.ComicPage(split[1], options.profileData) - applyImgOptimization(img4, options, 0) - img4.saveToDir(dirpath, options.forcepng, options.forcecolor, True) + img0 = image.ComicPage(split[0], opt.profileData) + applyImgOptimization(img0, opt) + img0.saveToDir(dirpath, opt.forcepng, opt.forcecolor, wipe) + img1 = image.ComicPage(split[1], opt.profileData) + applyImgOptimization(img1, opt) + img1.saveToDir(dirpath, opt.forcepng, opt.forcecolor, wipe) + if opt.quality == 2: + img3 = image.ComicPage(split[0], opt.profileData) + applyImgOptimization(img3, opt, 0) + img3.saveToDir(dirpath, opt.forcepng, opt.forcecolor, True) + img4 = image.ComicPage(split[1], opt.profileData) + applyImgOptimization(img4, opt, 0) + img4.saveToDir(dirpath, opt.forcepng, opt.forcecolor, True) else: - applyImgOptimization(img, options) - img.saveToDir(dirpath, options.forcepng, options.forcecolor, wipe) - if options.quality == 2: - img2 = image.ComicPage(os.path.join(dirpath, afile), options.profileData) + applyImgOptimization(img, opt) + img.saveToDir(dirpath, opt.forcepng, opt.forcecolor, wipe) + if opt.quality == 2: + img2 = image.ComicPage(os.path.join(dirpath, afile), opt.profileData) if img.rotated: img2.image = img2.image.rotate(90) img2.rotated = True - applyImgOptimization(img2, options, 0) - img2.saveToDir(dirpath, options.forcepng, options.forcecolor, True) + applyImgOptimization(img2, opt, 0) + img2.saveToDir(dirpath, opt.forcepng, opt.forcecolor, True) return output def genEpubStruct(path): - global options filelist = [] chapterlist = [] cover = None @@ -584,6 +582,7 @@ def getWorkFolder(afile): def slugify(value): # Normalizes string, converts to lowercase, removes non-alpha characters and converts spaces to hyphens. import unicodedata + #noinspection PyArgumentList value = unicodedata.normalize('NFKD', unicode(value, 'latin1')).encode('ascii', 'ignore') value = re.sub('[^\w\s\.-]', '', value).strip().lower() value = re.sub('[-\.\s]+', '-', value) @@ -788,7 +787,7 @@ def Usage(): def main(argv=None, qtGUI=None): - global parser, options, epub_path, GUI + global parser, options, GUI parser = OptionParser(usage="Usage: %prog [options] comic_file|comic_folder", add_help_option=False) mainOptions = OptionGroup(parser, "MAIN") processingOptions = OptionGroup(parser, "PROCESSING") diff --git a/kcc/comic2panel.py b/kcc/comic2panel.py index 8b2e8773..637ea289 100644 --- a/kcc/comic2panel.py +++ b/kcc/comic2panel.py @@ -54,9 +54,9 @@ def getImageFileName(imgfile): return filename -def sanitizePanelSize(panel, options): +def sanitizePanelSize(panel, opt): newPanels = [] - if panel[2] > 8 * options.height: + if panel[2] > 8 * opt.height: diff = (panel[2] / 8) newPanels.append([panel[0], panel[1] - diff*7, diff]) newPanels.append([panel[1] - diff*7, panel[1] - diff*6, diff]) @@ -66,13 +66,13 @@ def sanitizePanelSize(panel, options): newPanels.append([panel[1] - diff*3, panel[1] - diff*2, diff]) newPanels.append([panel[1] - diff*2, panel[1] - diff, diff]) newPanels.append([panel[1] - diff, panel[1], diff]) - elif panel[2] > 4 * options.height: + elif panel[2] > 4 * opt.height: diff = (panel[2] / 4) newPanels.append([panel[0], panel[1] - diff*3, diff]) newPanels.append([panel[1] - diff*3, panel[1] - diff*2, diff]) newPanels.append([panel[1] - diff*2, panel[1] - diff, diff]) newPanels.append([panel[1] - diff, panel[1], diff]) - elif panel[2] > 2 * options.height: + elif panel[2] > 2 * opt.height: newPanels.append([panel[0], panel[1] - (panel[2] / 2), (panel[2] / 2)]) newPanels.append([panel[1] - (panel[2] / 2), panel[1], (panel[2] / 2)]) else: @@ -80,17 +80,17 @@ def sanitizePanelSize(panel, options): return newPanels -def splitImage_init(queue, options): +def splitImage_init(queue, opt): splitImage.queue = queue - splitImage.options = options + splitImage.options = opt # noinspection PyUnresolvedReferences def splitImage(work): path = work[0] name = work[1] - options = splitImage.options - # Harcoded options + opt = splitImage.options + # Harcoded opttions threshold = 1.0 delta = 15 print ".", @@ -115,8 +115,8 @@ def splitImage(work): image = Image.open(filePath) image = image.convert('RGB') widthImg, heightImg = image.size - if heightImg > options.height: - if options.debug: + if heightImg > opt.height: + if opt.debug: from PIL import ImageDraw debugImage = Image.open(filePath) draw = ImageDraw.Draw(debugImage) @@ -138,23 +138,23 @@ def splitImage(work): if y1 + delta >= heightImg: y1 = heightImg - 1 y2Temp = y1 - if options.debug: + if opt.debug: draw.line([(0, y1Temp), (widthImg, y1Temp)], fill=(0, 255, 0)) draw.line([(0, y2Temp), (widthImg, y2Temp)], fill=(255, 0, 0)) panelHeight = y2Temp - y1Temp if panelHeight > delta: # Panels that can't be cut nicely will be forcefully splitted - panelsCleaned = sanitizePanelSize([y1Temp, y2Temp, panelHeight], options) + panelsCleaned = sanitizePanelSize([y1Temp, y2Temp, panelHeight], opt) for panel in panelsCleaned: panels.append(panel) - if options.debug: + if opt.debug: # noinspection PyUnboundLocalVariable debugImage.save(os.path.join(path, fileExpanded[0] + '-debug.png'), 'PNG') # Create virtual pages pages = [] currentPage = [] - pageLeft = options.height + pageLeft = opt.height panelNumber = 0 for panel in panels: if pageLeft - panel[2] > 0: @@ -164,7 +164,7 @@ def splitImage(work): else: if len(currentPage) > 0: pages.append(currentPage) - pageLeft = options.height - panel[2] + pageLeft = opt.height - panel[2] currentPage = [panelNumber] panelNumber += 1 if len(currentPage) > 0: diff --git a/kcc/image.py b/kcc/image.py index b029eb7b..0332b2d8 100755 --- a/kcc/image.py +++ b/kcc/image.py @@ -30,6 +30,9 @@ class ProfileData: + def __init__(self): + pass + Palette4 = [ 0x00, 0x00, 0x00, 0x55, 0x55, 0x55, From e7b47d28d9087694501b03bbe4958c51072e3649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Sat, 28 Sep 2013 23:21:09 +0200 Subject: [PATCH 26/26] Progress bar don't hide on older Kindle --- README.md | 10 ++++++---- kcc/image.py | 8 ++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index a8eeaaaf..795ebd21 100644 --- a/README.md +++ b/README.md @@ -138,7 +138,7 @@ The app relies and includes the following scripts/binaries: ## SAMPLE FILES CREATED BY KCC * [Kindle Paperwhite](http://kcc.vulturis.eu/Samples/Ubunchu!-KPW.mobi) * [Kindle](http://kcc.vulturis.eu/Samples/Ubunchu!-K345.mobi) -* [Kindle DX](http://kcc.vulturis.eu/Samples/Ubunchu!-KDX.mobi) +* [Kindle DX/DXG](http://kcc.vulturis.eu/Samples/Ubunchu!-KDX.mobi) * [Kindle Fire](http://kcc.vulturis.eu/Samples/Ubunchu!-KF.mobi) * [Kindle Fire HD](http://kcc.vulturis.eu/Samples/Ubunchu!-KFHD.mobi) * [Kindle Fire HD 8.9"](http://kcc.vulturis.eu/Samples/Ubunchu!-KFHD8.mobi) @@ -255,13 +255,15 @@ The app relies and includes the following scripts/binaries: * Hotfixed crash occurring on OS with Russian locale ####3.3: -* Created MOBI files are not longer marked as _Personal_ on newer Kindle models * Margins are now automatically omitted in Panel View mode -* Layout of panels in Panel View mode is now automatically adjusted to content -* Support for Virtual Panel View was removed * Margin color fill is now autodetected +* Created MOBI files are not longer marked as _Personal_ on newer Kindle models +* Layout of panels in Panel View mode is now automatically adjusted to content +* Fixed Kindle 2/DX/DXG profiles - no more blank pages +* All Kindle Fire profiles now support hiqh quality Panel View * Added support of 7z/CB7 files * Added Kindle Fire HDX profile +* Support for Virtual Panel View was removed * Profiles for Kindle Keyboard, Touch and Non-Touch are now merged * Windows release is now bundled with UnRAR and 7za * Small GUI tweaks diff --git a/kcc/image.py b/kcc/image.py index 0332b2d8..97c97b88 100755 --- a/kcc/image.py +++ b/kcc/image.py @@ -81,12 +81,12 @@ def __init__(self): ] Profiles = { - 'K1': ("Kindle 1", (600, 800), Palette4, 1.8, (900, 1200)), - 'K2': ("Kindle 2", (600, 800), Palette15, 1.8, (900, 1200)), + 'K1': ("Kindle 1", (600, 670), Palette4, 1.8, (900, 1005)), + 'K2': ("Kindle 2", (600, 670), Palette15, 1.8, (900, 1005)), 'K345': ("Kindle", (600, 800), Palette16, 1.8, (900, 1200)), 'KHD': ("Kindle Paperwhite", (758, 1024), Palette16, 1.8, (1137, 1536)), - 'KDX': ("Kindle DX", (824, 1200), Palette15, 1.8, (1236, 1800)), - 'KDXG': ("Kindle DXG", (824, 1200), Palette16, 1.8, (1236, 1800)), + 'KDX': ("Kindle DX", (824, 1000), Palette15, 1.8, (1236, 1500)), + 'KDXG': ("Kindle DXG", (824, 1000), Palette16, 1.8, (1236, 1500)), 'KF': ("Kindle Fire", (600, 1024), PalleteNull, 1.0, (900, 1536)), 'KFHD': ("K. Fire HD 7\"", (800, 1280), PalleteNull, 1.0, (1200, 1920)), 'KFHD8': ("K. Fire HD 8.9\"", (1200, 1920), PalleteNull, 1.0, (1800, 2880)),