From 2e4d5eb9584c9a3cf11e5e6c933e000ff0d697d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Mon, 27 May 2013 08:54:51 +0200 Subject: [PATCH 01/12] Updated README --- README.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e067265..1302412 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# KCC +# KCC `KCC` (a.k.a. `KindleComicConverter`) is a Python app to convert comic files or folders to ePub or Panel View MOBI. It was initally developed for Kindle but since v2.2 it outputs valid ePub 2.0 so _**despite its name, KCC is @@ -16,9 +16,14 @@ _kc2_ in no way is a replacement for **KCC** so you can be quite confident we'll You can find the latest released binary at the following links: - OS X: [https://dl.dropbox.com/u/16806101/KindleComicConverter_osx_2.9.zip](https://dl.dropbox.com/u/16806101/KindleComicConverter_osx_2.9.zip) - Win64: [https://dl.dropbox.com/u/16806101/KindleComicConverter_win-amd64_2.9.zip](https://dl.dropbox.com/u/16806101/KindleComicConverter_win-amd64_2.9.zip) -- Win32: [http://pawelj.vulturis.eu/Shared/KindleComicConverter_win-x86_2.9.zip](http://pawelj.vulturis.eu/Shared/KindleComicConverter_win-x86_2.9.zip) *(thanks to [AcidWeb](https://github.com/AcidWeb))* +- Win32: [http://pawelj.vulturis.eu/Shared/KindleComicConverter_win-x86_2.9.zip](http://pawelj.vulturis.eu/Shared/KindleComicConverter_win-x86_2.9.zip) - Linux: Just download sourcecode and launch `python kcc.py` *(Provided you have Python and Pillow installed)* +## AWKCC .NET GUI +![AWKCC](http://pawelj.vulturis.eu/Shared/CurrentAWKCC.png) +All-in-one package for Windows users: +[Mobileread thread](http://www.mobileread.com/forums/showpost.php?p=2444957&postcount=3) + ## INPUT FORMATS `kcc` can understand and convert, at the moment, the following file types: - PNG, JPG, GIF, TIFF, BMP @@ -53,12 +58,13 @@ Options: --version show program's version number and exit -h, --help show this help message and exit -p PROFILE, --profile=PROFILE - Device profile (Choose one among K1, K2, K3, K4NT, K4T, KDX, KDXG or KHD) [Default=KHD] + Device profile (Choose one among K1, K2, K3, K4NT, K4T, KDX, KDXG, KHD, KF, KFHD, KFHD8) [Default=KHD] -t TITLE, --title=TITLE Comic title [Default=filename] -m, --manga-style Manga style (Right-to-left reading and splitting) [Default=False] -c, --cbz-output Outputs a CBZ archive and does not generate EPUB --nopanelviewhq Disable high quality Panel View [Default=False] + --panelviewhorizontal Enable horizontal Panel View [Default=False] --noprocessing Do not apply image preprocessing (Page splitting and optimizations) [Default=True] --forcepng Create PNG files instead JPEG (For non-Kindle devices) [Default=False] --gamma=GAMMA Apply gamma correction to linearize the image [Default=Auto] From aadb5407d22d682297363c3e64c172c83af92c12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Mon, 27 May 2013 09:00:01 +0200 Subject: [PATCH 02/12] Updated README --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1302412..b66514b 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,8 @@ You can find the latest released binary at the following links: ## AWKCC .NET GUI ![AWKCC](http://pawelj.vulturis.eu/Shared/CurrentAWKCC.png) -All-in-one package for Windows users: -[Mobileread thread](http://www.mobileread.com/forums/showpost.php?p=2444957&postcount=3) + +[All-in-one package for Windows users](http://www.mobileread.com/forums/showpost.php?p=2444957&postcount=3) ## INPUT FORMATS `kcc` can understand and convert, at the moment, the following file types: @@ -143,7 +143,6 @@ The app relies and includes the following scripts/binaries: - 2.10: Kindle Fire support (color ePub/Mobi) Panel View support for horizontal content - ## COPYRIGHT Copyright (c) 2012-2013 Ciro Mattia Gonano with further contributions by Paweł Jastrzębski. From ff8f6e073ff19ca664ce285862ffab0db3b5a0bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Mon, 27 May 2013 09:18:28 +0200 Subject: [PATCH 03/12] Updated README --- README.md | 113 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 65 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index b66514b..cdabacd 100644 --- a/README.md +++ b/README.md @@ -94,54 +94,71 @@ The app relies and includes the following scripts/binaries: - `magic.py` from [python-magic](https://github.com/ahupp/python-magic) library ## CHANGELOG - - 1.00: Initial version - - 1.10: Added support for CBZ/CBR files in comic2ebook.py - - 1.11: Added support for CBZ/CBR files in KindleComicConverter - - 1.20: Comic optimizations! Split pages not target-oriented (landscape with portrait target or portrait - with landscape target), add palette and other image optimizations from Mangle. - WARNING: PIL is required for all image mangling! - - 1.30: Fixed an issue in OPF generation for device resolution - Reworked options system (call with -h option to get the inline help) - - 1.40: Added some options for controlling image optimization - Further optimization (ImageOps, page numbering cut, autocontrast) - - 1.41: Fixed a serious bug on resizing when img ratio was bigger than device one - - 1.50: Added subfolder support for multiple chapters. - - 2.0: GUI! AppleScript is gone and Tk is used to provide cross-platform GUI support. - - 2.1: Added basic error reporting - - 2.2: Added (valid!) ePub 2.0 output - Rename .zip files to .cbz to avoid overwriting - - 2.3: Fixed win32 ePub generation, folder handling, filenames with spaces and subfolders - - 2.4: Use temporary directory as workdir (fixes converting from external volumes and zipfiles renaming) - Fixed "add folders" from GUI. - - 2.5: Added --black-borders option to set added borders black when page's ratio is not the device's one (#11). - Fixes epub containing zipped itself (#10) - - 2.6: Added --rotate option to rotate landscape images instead of splitting them (#16, #24) - Added --output option to customize ePub output dir/file (#22) - Add rendition:layout and rendition:orientation ePub meta tags (supported by new kindlegen 2.8) - Fixed natural sorting for files (#18) - - 2.7: Lots of GUI improvements (#27, #13) - Added gamma support within --gamma option (defaults to profile-specified gamma) (#26, #27) - Added --nodithering option to prevent dithering optimizations (#27) - Epub margins support (#30) - Fixed no file added if file has no spaces on Windows (#25) - Gracefully exit if unrar missing (#15) - Do not call kindlegen if source epub is bigger than 320MB (#17) - Get filetype from magic number (#14) - PDF conversion works again - - 2.8: updated rarfile library - Panel View support + HQ support (#36) - new option: --nopanelviewhq - Split profiles for K4NT and K4T - Rewrite of Landscape Mode support (huge readability improvement for KPW) - Upscale use now BILINEAR method - Added generic CSS file - Optimized archive extraction for zip/rar files (#40) - - 2.9: Added support for generating a plain CBZ (skipping all the EPUB/Mobi generation) (#45) - Prevent output file overwriting the source one: if a duplicate name is detected, append _kcc to the name - Rarfile library updated to 2.6 - Added GIF, TIFF and BMP to supported formats (#42) - Filenames slugifications (#28, #31, #9, #8) - - 2.10: Kindle Fire support (color ePub/Mobi) - Panel View support for horizontal content +####1.00 +* Initial version +####1.10 +* Added support for CBZ/CBR files in comic2ebook.py +####1.11 +* Added support for CBZ/CBR files in KindleComicConverter +####1.20 +* Comic optimizations! Split pages not target-oriented (landscape with portrait target or portrait with landscape target), add palette and other image optimizations from Mangle. WARNING: PIL is required for all image mangling! +####1.30 +* Fixed an issue in OPF generation for device resolution +* Reworked options system (call with -h option to get the inline help) +####1.40 +* Added some options for controlling image optimization +* Further optimization (ImageOps, page numbering cut, autocontrast) +####1.41 +* Fixed a serious bug on resizing when img ratio was bigger than device one +####1.50 +* Added subfolder support for multiple chapters. +####2.0 +* GUI! AppleScript is gone and Tk is used to provide cross-platform GUI support. +####2.1 +* Added basic error reporting +#### 2.2: +* Added (valid!) ePub 2.0 output +* Rename .zip files to .cbz to avoid overwriting +####2.3 +* Fixed win32 ePub generation, folder handling, filenames with spaces and subfolders +####2.4 +* Use temporary directory as workdir (fixes converting from external volumes and zipfiles renaming) +* Fixed "add folders" from GUI. +####2.5 +* Added --black-borders option to set added borders black when page's ratio is not the device's one (#11). +* Fixes epub containing zipped itself (#10) +####2.6 +* Added --rotate option to rotate landscape images instead of splitting them (#16, #24) +* Added --output option to customize ePub output dir/file (#22) +* Add rendition:layout and rendition:orientation ePub meta tags (supported by new kindlegen 2.8) +* Fixed natural sorting for files (#18) +####2.7: +* Lots of GUI improvements (#27, #13) +* Added gamma support within --gamma option (defaults to profile-specified gamma) (#26, #27) +* Added --nodithering option to prevent dithering optimizations (#27) +* Epub margins support (#30) +* Fixed no file added if file has no spaces on Windows (#25) +* Gracefully exit if unrar missing (#15) +* Do not call kindlegen if source epub is bigger than 320MB (#17) +* Get filetype from magic number (#14) +* PDF conversion works again +####2.8 +* Updated rarfile library +* Panel View support + HQ support (#36) - new option: --nopanelviewhq +* Split profiles for K4NT and K4T +* Rewrite of Landscape Mode support (huge readability improvement for KPW) +* Upscale use now BILINEAR method +* Added generic CSS file +* Optimized archive extraction for zip/rar files (#40) +####2.9: +* Added support for generating a plain CBZ (skipping all the EPUB/Mobi generation) (#45) +* Prevent output file overwriting the source one: if a duplicate name is detected, append _kcc to the name +* Rarfile library updated to 2.6 +* Added GIF, TIFF and BMP to supported formats (#42) +* Filenames slugifications (#28, #31, #9, #8) +####2.10: +* Kindle Fire support (color ePub/Mobi) +* Panel View support for horizontal content ## COPYRIGHT From c0610360a39b0fd40651f950bc07d5bf1b09b8bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Mon, 27 May 2013 09:21:09 +0200 Subject: [PATCH 04/12] Updated README (I hate Markdown) --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index cdabacd..497a386 100644 --- a/README.md +++ b/README.md @@ -96,42 +96,57 @@ The app relies and includes the following scripts/binaries: ## CHANGELOG ####1.00 * Initial version + ####1.10 * Added support for CBZ/CBR files in comic2ebook.py + ####1.11 * Added support for CBZ/CBR files in KindleComicConverter + ####1.20 * Comic optimizations! Split pages not target-oriented (landscape with portrait target or portrait with landscape target), add palette and other image optimizations from Mangle. WARNING: PIL is required for all image mangling! + ####1.30 * Fixed an issue in OPF generation for device resolution * Reworked options system (call with -h option to get the inline help) + ####1.40 * Added some options for controlling image optimization * Further optimization (ImageOps, page numbering cut, autocontrast) + ####1.41 * Fixed a serious bug on resizing when img ratio was bigger than device one + ####1.50 * Added subfolder support for multiple chapters. + ####2.0 * GUI! AppleScript is gone and Tk is used to provide cross-platform GUI support. + ####2.1 * Added basic error reporting + #### 2.2: * Added (valid!) ePub 2.0 output * Rename .zip files to .cbz to avoid overwriting + ####2.3 * Fixed win32 ePub generation, folder handling, filenames with spaces and subfolders + ####2.4 * Use temporary directory as workdir (fixes converting from external volumes and zipfiles renaming) * Fixed "add folders" from GUI. + ####2.5 * Added --black-borders option to set added borders black when page's ratio is not the device's one (#11). * Fixes epub containing zipped itself (#10) + ####2.6 * Added --rotate option to rotate landscape images instead of splitting them (#16, #24) * Added --output option to customize ePub output dir/file (#22) * Add rendition:layout and rendition:orientation ePub meta tags (supported by new kindlegen 2.8) * Fixed natural sorting for files (#18) + ####2.7: * Lots of GUI improvements (#27, #13) * Added gamma support within --gamma option (defaults to profile-specified gamma) (#26, #27) @@ -142,6 +157,7 @@ The app relies and includes the following scripts/binaries: * Do not call kindlegen if source epub is bigger than 320MB (#17) * Get filetype from magic number (#14) * PDF conversion works again + ####2.8 * Updated rarfile library * Panel View support + HQ support (#36) - new option: --nopanelviewhq @@ -150,12 +166,14 @@ The app relies and includes the following scripts/binaries: * Upscale use now BILINEAR method * Added generic CSS file * Optimized archive extraction for zip/rar files (#40) + ####2.9: * Added support for generating a plain CBZ (skipping all the EPUB/Mobi generation) (#45) * Prevent output file overwriting the source one: if a duplicate name is detected, append _kcc to the name * Rarfile library updated to 2.6 * Added GIF, TIFF and BMP to supported formats (#42) * Filenames slugifications (#28, #31, #9, #8) + ####2.10: * Kindle Fire support (color ePub/Mobi) * Panel View support for horizontal content From 3e007965b29505ca6139d84bd376bc652ac68f2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Mon, 27 May 2013 15:21:16 +0200 Subject: [PATCH 05/12] Fixed panel order for horizontal pages when --rotate is enabled --- README.md | 5 +++-- kcc/comic2ebook.py | 16 ++++++++++------ kcc/image.py | 12 ++++++++---- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 497a386..c5c3e69 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,7 @@ The app relies and includes the following scripts/binaries: * Add rendition:layout and rendition:orientation ePub meta tags (supported by new kindlegen 2.8) * Fixed natural sorting for files (#18) -####2.7: +####2.7 * Lots of GUI improvements (#27, #13) * Added gamma support within --gamma option (defaults to profile-specified gamma) (#26, #27) * Added --nodithering option to prevent dithering optimizations (#27) @@ -167,7 +167,7 @@ The app relies and includes the following scripts/binaries: * Added generic CSS file * Optimized archive extraction for zip/rar files (#40) -####2.9: +####2.9 * Added support for generating a plain CBZ (skipping all the EPUB/Mobi generation) (#45) * Prevent output file overwriting the source one: if a duplicate name is detected, append _kcc to the name * Rarfile library updated to 2.6 @@ -177,6 +177,7 @@ The app relies and includes the following scripts/binaries: ####2.10: * Kindle Fire support (color ePub/Mobi) * Panel View support for horizontal content +* Fixed panel order for horizontal pages when --rotate is enabled ## COPYRIGHT diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py index 36be9c0..68a22ce 100755 --- a/kcc/comic2ebook.py +++ b/kcc/comic2ebook.py @@ -40,6 +40,10 @@ import pdfjpgextract def buildHTML(path, imgfile): filename = getImageFileName(imgfile) if filename is not None: + if "_rotated" in str(filename): + rotate = True + else: + rotate = False htmlpath = '' postfix = '' backref = 1 @@ -70,7 +74,7 @@ def buildHTML(path, imgfile): imgfile, "\" class=\"singlePage\"/>\n" ]) if options.panelview: - if options.panelviewhorizontal: + if options.panelviewhorizontal or rotate: if options.righttoleft: f.writelines(["
\n", @@ -348,7 +352,7 @@ def dirImgProcess(path): split = None else: split = img.splitPage(dirpath, options.righttoleft, options.rotate) - if split is not None: + if split is not None and split is not "R": if options.verbose: print "Splitted " + afile if options.righttoleft: @@ -365,17 +369,17 @@ def dirImgProcess(path): facing = "left" img0 = image.ComicPage(split[0], options.profile) applyImgOptimization(img0, True, toRight1) - img0.saveToDir(dirpath, options.forcepng, options.forcecolor) + img0.saveToDir(dirpath, options.forcepng, options.forcecolor, split) img1 = image.ComicPage(split[1], options.profile) applyImgOptimization(img1, True, toRight2) - img1.saveToDir(dirpath, options.forcepng, options.forcecolor) + img1.saveToDir(dirpath, options.forcepng, options.forcecolor, split) else: if facing == "right": facing = "left" else: facing = "right" applyImgOptimization(img) - img.saveToDir(dirpath, options.forcepng, options.forcecolor) + img.saveToDir(dirpath, options.forcepng, options.forcecolor, split) def genEpubStruct(path): @@ -712,7 +716,7 @@ def checkOptions(): options.forcepng = False else: options.forcecolor = False - if options.panelviewhorizontal: + if options.panelviewhorizontal or options.rotate: options.panelview = True options.landscapemode = False diff --git a/kcc/image.py b/kcc/image.py index 896058a..e4fb7b2 100755 --- a/kcc/image.py +++ b/kcc/image.py @@ -119,16 +119,20 @@ class ComicPage: raise RuntimeError('Cannot read image file %s' % source) self.image = self.image.convert('RGB') - def saveToDir(self, targetdir, forcepng, color): + def saveToDir(self, targetdir, forcepng, color, sufix): filename = os.path.basename(self.origFileName) try: if not color: self.image = self.image.convert('L') # convert to grayscale + if sufix == "R": + sufix = "_rotated" + else: + sufix = "" os.remove(os.path.join(targetdir, filename)) if forcepng: - self.image.save(os.path.join(targetdir, os.path.splitext(filename)[0] + ".png"), "PNG") + self.image.save(os.path.join(targetdir, os.path.splitext(filename)[0] + sufix + ".png"), "PNG") else: - self.image.save(os.path.join(targetdir, os.path.splitext(filename)[0] + ".jpg"), "JPEG") + self.image.save(os.path.join(targetdir, os.path.splitext(filename)[0] + sufix + ".jpg"), "JPEG") except IOError as e: raise RuntimeError('Cannot write image in directory %s: %s' % (targetdir, e)) @@ -202,7 +206,7 @@ class ComicPage: if (width > height) != (dstwidth > dstheight): if rotate: self.image = self.image.rotate(90) - return None + return "R" else: if width > height: # source is landscape, so split by the width From e60362202159b881a65f44cdcc64cb0cf3515c59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Mon, 27 May 2013 18:26:53 +0200 Subject: [PATCH 06/12] Removed redundant --panelviewhorizontal --- README.md | 1 - kcc/comic2ebook.py | 6 ++---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index c5c3e69..640003e 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,6 @@ Options: -m, --manga-style Manga style (Right-to-left reading and splitting) [Default=False] -c, --cbz-output Outputs a CBZ archive and does not generate EPUB --nopanelviewhq Disable high quality Panel View [Default=False] - --panelviewhorizontal Enable horizontal Panel View [Default=False] --noprocessing Do not apply image preprocessing (Page splitting and optimizations) [Default=True] --forcepng Create PNG files instead JPEG (For non-Kindle devices) [Default=False] --gamma=GAMMA Apply gamma correction to linearize the image [Default=Auto] diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py index 68a22ce..52c6dd9 100755 --- a/kcc/comic2ebook.py +++ b/kcc/comic2ebook.py @@ -74,7 +74,7 @@ def buildHTML(path, imgfile): imgfile, "\" class=\"singlePage\"/>\n" ]) if options.panelview: - if options.panelviewhorizontal or rotate: + if rotate: if options.righttoleft: f.writelines(["
\n", @@ -623,8 +623,6 @@ def main(argv=None): help="Outputs a CBZ archive and does not generate EPUB") parser.add_option("--nopanelviewhq", action="store_true", dest="nopanelviewhq", default=False, help="Disable high quality Panel View [Default=False]") - parser.add_option("--panelviewhorizontal", action="store_true", dest="panelviewhorizontal", default=False, - help="Enable horizontal Panel View [Default=False]") parser.add_option("--noprocessing", action="store_false", dest="imgproc", default=True, help="Do not apply image preprocessing (Page splitting and optimizations) [Default=True]") parser.add_option("--forcepng", action="store_true", dest="forcepng", default=False, @@ -716,7 +714,7 @@ def checkOptions(): options.forcepng = False else: options.forcecolor = False - if options.panelviewhorizontal or options.rotate: + if options.rotate: options.panelview = True options.landscapemode = False From e981aa45203238b033c6a88850a1a2ba48101019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Mon, 27 May 2013 18:46:54 +0200 Subject: [PATCH 07/12] Added some comments --- kcc/comic2ebook.py | 12 ++++++++++-- kcc/image.py | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py index 52c6dd9..44d90c5 100755 --- a/kcc/comic2ebook.py +++ b/kcc/comic2ebook.py @@ -696,24 +696,32 @@ def getOutputFilename(srcpath, wantedname, ext): 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. if options.profile == 'K3' or options.profile == 'K4NT': - #Real Panel View + # Real Panel View options.panelview = True else: - #Virtual Panel View + # Virtual Panel View options.panelview = 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': options.nopanelviewhq = True + # Disabling grayscale conversion for Kindle Fire family. + # Forcing JPEG output. For now code can't provide color PNG files. if options.profile == 'KF' or options.profile == 'KFHD' or options.profile == 'KFHD8': options.forcecolor = True options.forcepng = False 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 diff --git a/kcc/image.py b/kcc/image.py index e4fb7b2..b4faded 100755 --- a/kcc/image.py +++ b/kcc/image.py @@ -124,6 +124,7 @@ class ComicPage: try: if not color: self.image = self.image.convert('L') # convert to grayscale + # Sufix is used to recognise which files need horizontal Panel View. if sufix == "R": sufix = "_rotated" else: From aba315866ebc0cb319ef6c5a32813e29d3e9c945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Mon, 27 May 2013 18:54:39 +0200 Subject: [PATCH 08/12] Small tweaks --- kcc/comic2ebook.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py index 44d90c5..0890885 100755 --- a/kcc/comic2ebook.py +++ b/kcc/comic2ebook.py @@ -40,6 +40,7 @@ import pdfjpgextract def buildHTML(path, imgfile): filename = getImageFileName(imgfile) if filename is not None: + # All files marked with this sufix need horizontal Panel View. if "_rotated" in str(filename): rotate = True else: @@ -369,10 +370,10 @@ def dirImgProcess(path): facing = "left" img0 = image.ComicPage(split[0], options.profile) applyImgOptimization(img0, True, toRight1) - img0.saveToDir(dirpath, options.forcepng, options.forcecolor, split) + img0.saveToDir(dirpath, options.forcepng, options.forcecolor, None) img1 = image.ComicPage(split[1], options.profile) applyImgOptimization(img1, True, toRight2) - img1.saveToDir(dirpath, options.forcepng, options.forcecolor, split) + img1.saveToDir(dirpath, options.forcepng, options.forcecolor, None) else: if facing == "right": facing = "left" From d090d8c2e882a79dfb4931868e4b8a410dbce9ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Mon, 27 May 2013 20:06:47 +0200 Subject: [PATCH 09/12] Refactoring of dirImgProcess --- kcc/comic2ebook.py | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py index 0890885..1d2fd1f 100755 --- a/kcc/comic2ebook.py +++ b/kcc/comic2ebook.py @@ -255,7 +255,7 @@ def buildOPF(profile, dstdir, title, filelist, cover=None): f.write("\n\n") splitCountUsed = 1 for entry in reflist: - if entry.endswith("-1"): + if entry.endswith("-000001"): # noinspection PyRedundantParentheses if ((options.righttoleft and facing == 'left') or (not options.righttoleft and facing == 'right')) and\ options.landscapemode: @@ -265,7 +265,7 @@ def buildOPF(profile, dstdir, title, filelist, cover=None): f.write("\n") else: f.write("\n") - elif entry.endswith("-2"): + elif entry.endswith("-000002"): if options.landscapemode: f.write("\n") else: @@ -336,14 +336,14 @@ def applyImgOptimization(img, isSplit=False, toRight=False): def dirImgProcess(path): global options, splitCount - if options.righttoleft: - facing = "right" - else: - facing = "left" + pagenumber = 0 + pagenumbermodifier = 0 + splitpages = [] for (dirpath, dirnames, filenames) in os.walk(path): for afile in filenames: if getImageFileName(afile) is not None: + pagenumber += 1 if options.verbose: print "Optimizing " + afile + " for " + options.profile else: @@ -359,15 +359,11 @@ def dirImgProcess(path): if options.righttoleft: toRight1 = False toRight2 = True - if facing == "left": - splitCount += 1 - facing = "right" + splitpages.append(pagenumber) else: toRight1 = True toRight2 = False - if facing == "right": - splitCount += 1 - facing = "left" + splitpages.append(pagenumber) img0 = image.ComicPage(split[0], options.profile) applyImgOptimization(img0, True, toRight1) img0.saveToDir(dirpath, options.forcepng, options.forcecolor, None) @@ -375,13 +371,15 @@ def dirImgProcess(path): applyImgOptimization(img1, True, toRight2) img1.saveToDir(dirpath, options.forcepng, options.forcecolor, None) else: - if facing == "right": - facing = "left" - else: - facing = "right" applyImgOptimization(img) img.saveToDir(dirpath, options.forcepng, options.forcecolor, split) + splitpages.sort() + for page in splitpages: + if (page + pagenumbermodifier) % 2 == 0: + splitCount += 1 + pagenumbermodifier += 1 + def genEpubStruct(path): global options From 6c468a529151ec69051df532f58a3097f48b4682 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Tue, 28 May 2013 08:47:24 +0200 Subject: [PATCH 10/12] dirImgProcess - multiprocessing --- kcc/comic2ebook.py | 83 +++++++++++++++++++++++++++------------------- kcc/image.py | 2 +- 2 files changed, 49 insertions(+), 36 deletions(-) diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py index 1d2fd1f..5d59f34 100755 --- a/kcc/comic2ebook.py +++ b/kcc/comic2ebook.py @@ -32,6 +32,7 @@ from shutil import copytree from shutil import rmtree from shutil import make_archive from optparse import OptionParser +from multiprocessing import Pool import image import cbxarchive import pdfjpgextract @@ -255,7 +256,7 @@ def buildOPF(profile, dstdir, title, filelist, cover=None): f.write("\n\n") splitCountUsed = 1 for entry in reflist: - if entry.endswith("-000001"): + if entry.endswith("-000001") or entry.endswith("-1"): # noinspection PyRedundantParentheses if ((options.righttoleft and facing == 'left') or (not options.righttoleft and facing == 'right')) and\ options.landscapemode: @@ -265,7 +266,7 @@ def buildOPF(profile, dstdir, title, filelist, cover=None): f.write("\n") else: f.write("\n") - elif entry.endswith("-000002"): + elif entry.endswith("-000002") or entry.endswith("-2"): if options.landscapemode: f.write("\n") else: @@ -323,7 +324,7 @@ def isInFilelist(filename, filelist): return seen -def applyImgOptimization(img, isSplit=False, toRight=False): +def applyImgOptimization(img, isSplit, toRight, options): img.cropWhiteSpace(10.0) if options.cutpagenumbers: img.cutPageNumber() @@ -336,44 +337,19 @@ def applyImgOptimization(img, isSplit=False, toRight=False): def dirImgProcess(path): global options, splitCount + work = [] pagenumber = 0 pagenumbermodifier = 0 - splitpages = [] - + pool = Pool() for (dirpath, dirnames, filenames) in os.walk(path): for afile in filenames: if getImageFileName(afile) is not None: pagenumber += 1 - if options.verbose: - print "Optimizing " + afile + " for " + options.profile - else: - print ".", - img = image.ComicPage(os.path.join(dirpath, afile), options.profile) - if options.nosplitrotate: - split = None - else: - split = img.splitPage(dirpath, options.righttoleft, options.rotate) - if split is not None and split is not "R": - if options.verbose: - print "Splitted " + afile - if options.righttoleft: - toRight1 = False - toRight2 = True - splitpages.append(pagenumber) - else: - toRight1 = True - toRight2 = False - splitpages.append(pagenumber) - img0 = image.ComicPage(split[0], options.profile) - applyImgOptimization(img0, True, toRight1) - img0.saveToDir(dirpath, options.forcepng, options.forcecolor, None) - img1 = image.ComicPage(split[1], options.profile) - applyImgOptimization(img1, True, toRight2) - img1.saveToDir(dirpath, options.forcepng, options.forcecolor, None) - else: - applyImgOptimization(img) - img.saveToDir(dirpath, options.forcepng, options.forcecolor, split) - + work.append([afile, dirpath, pagenumber, options]) + splitpages = pool.map(fileImgProcess, work) + pool.close() + pool.join() + splitpages = filter(None, splitpages) splitpages.sort() for page in splitpages: if (page + pagenumbermodifier) % 2 == 0: @@ -381,6 +357,43 @@ def dirImgProcess(path): pagenumbermodifier += 1 +def fileImgProcess(work): + afile = work[0] + dirpath = work[1] + pagenumber = work[2] + options = work[3] + output = None + if options.verbose: + print "Optimizing " + afile + " for " + options.profile + else: + print ".", + img = image.ComicPage(os.path.join(dirpath, afile), options.profile) + if options.nosplitrotate: + split = None + else: + split = img.splitPage(dirpath, options.righttoleft, options.rotate) + 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.profile) + applyImgOptimization(img0, True, toRight1, options) + img0.saveToDir(dirpath, options.forcepng, options.forcecolor) + img1 = image.ComicPage(split[1], options.profile) + applyImgOptimization(img1, True, toRight2, options) + img1.saveToDir(dirpath, options.forcepng, options.forcecolor) + else: + applyImgOptimization(img, False, False, options) + img.saveToDir(dirpath, options.forcepng, options.forcecolor, split) + return output + + def genEpubStruct(path): global options filelist = [] diff --git a/kcc/image.py b/kcc/image.py index b4faded..18d7f34 100755 --- a/kcc/image.py +++ b/kcc/image.py @@ -119,7 +119,7 @@ class ComicPage: raise RuntimeError('Cannot read image file %s' % source) self.image = self.image.convert('RGB') - def saveToDir(self, targetdir, forcepng, color, sufix): + def saveToDir(self, targetdir, forcepng, color, sufix=None): filename = os.path.basename(self.origFileName) try: if not color: From 28dcab8ee8f23943ced739f17d6400d1e7b6f189 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Tue, 28 May 2013 11:24:37 +0200 Subject: [PATCH 11/12] Multiprocessing - final final touches + Code cleanup (close #39) --- README.md | 1 + kcc/comic2ebook.py | 26 +++++---------------- kcc/image.py | 58 ---------------------------------------------- 3 files changed, 7 insertions(+), 78 deletions(-) diff --git a/README.md b/README.md index 640003e..08bfb21 100644 --- a/README.md +++ b/README.md @@ -174,6 +174,7 @@ The app relies and includes the following scripts/binaries: * Filenames slugifications (#28, #31, #9, #8) ####2.10: +* Multiprocessing support * Kindle Fire support (color ePub/Mobi) * Panel View support for horizontal content * Fixed panel order for horizontal pages when --rotate is enabled diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py index 5d59f34..1791cf9 100755 --- a/kcc/comic2ebook.py +++ b/kcc/comic2ebook.py @@ -315,15 +315,6 @@ def getImageFileName(imgfile): return filename -def isInFilelist(filename, filelist): - filename = os.path.splitext(filename) - seen = False - for item in filelist: - if filename[0] == item[0]: - seen = True - return seen - - def applyImgOptimization(img, isSplit, toRight, options): img.cropWhiteSpace(10.0) if options.cutpagenumbers: @@ -355,6 +346,7 @@ def dirImgProcess(path): if (page + pagenumbermodifier) % 2 == 0: splitCount += 1 pagenumbermodifier += 1 + pagenumbermodifier += 1 def fileImgProcess(work): @@ -403,8 +395,8 @@ def genEpubStruct(path): sanitizeTree(os.path.join(path, 'OEBPS', 'Images')) os.mkdir(os.path.join(path, 'OEBPS', 'Text')) f = open(os.path.join(path, 'OEBPS', 'Text', 'style.css'), 'w') - #DON'T COMPRESS CSS. KINDLE WILL FAIL TO PARSE IT. - #Generic Panel View support + Margins fix for Non-Kindle devices. + # DON'T COMPRESS CSS. KINDLE WILL FAIL TO PARSE IT. + # Generic Panel View support + Margins fix for Non-Kindle devices. f.writelines(["@page {\n", "margin-bottom: 0;\n", "margin-top: 0\n", @@ -527,13 +519,6 @@ def genEpubStruct(path): for afile in filenames: filename = getImageFileName(afile) if filename is not None: - if "credit" in afile.lower(): - os.rename(os.path.join(dirpath, afile), os.path.join(dirpath, 'ZZZ999_' + afile)) - afile = 'ZZZ999_' + afile - if "+" in afile.lower() or "#" in afile.lower(): - newfilename = afile.replace('+', '_').replace('#', '_') - os.rename(os.path.join(dirpath, afile), os.path.join(dirpath, newfilename)) - afile = newfilename filelist.append(buildHTML(dirpath, afile)) if not chapter: chapterlist.append((dirpath.replace('Images', 'Text'), filelist[-1][1])) @@ -612,11 +597,11 @@ def sanitizeTree(filetree): def Copyright(): print ('comic2ebook v%(__version__)s. ' - 'Written 2012 by Ciro Mattia Gonano.' % globals()) + 'Written 2013 by Ciro Mattia Gonano and Pawel Jastrzebski.' % globals()) def Usage(): - print "Generates HTML, NCX and OPF for a Comic ebook from a bunch of images." + print "Generates EPUB/CBZ comic ebook from a bunch of images." parser.print_help() @@ -743,6 +728,7 @@ def getEpubPath(): global epub_path return epub_path + if __name__ == "__main__": Copyright() main(sys.argv[1:]) diff --git a/kcc/image.py b/kcc/image.py index 18d7f34..7af6f39 100755 --- a/kcc/image.py +++ b/kcc/image.py @@ -327,61 +327,3 @@ class ComicPage: 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 - - # def addProgressbar(self, file_number, files_totalnumber, size, howoften): - # if file_number // howoften != float(file_number) / howoften: - # return self.image - # white = (255, 255, 255) - # black = (0, 0, 0) - # widthDev, heightDev = size - # widthImg, heightImg = self.image.size - # pastePt = ( - # max(0, (widthDev - widthImg) / 2), - # max(0, (heightDev - heightImg) / 2) - # ) - # imageBg = Image.new('RGB', size, white) - # imageBg.paste(self.image, pastePt) - # self.image = imageBg - # widthImg, heightImg = self.image.size - # draw = ImageDraw.Draw(self.image) - # #Black rectangle - # draw.rectangle([(0, heightImg - 3), (widthImg, heightImg)], outline=black, fill=black) - # #White rectangle - # draw.rectangle([(widthImg * file_number / files_totalnumber, heightImg - 3), (widthImg - 1, heightImg)], - # outline=black, fill=white) - # #Making notches - # for i in range(1, 10): - # if i <= (10 * file_number / files_totalnumber): - # notch_colour = white # White - # else: - # notch_colour = black # Black - # draw.line([(widthImg * float(i) / 10, heightImg - 3), (widthImg * float(i) / 10, heightImg)], - # fill=notch_colour) - # #The 50% - # if i == 5: - # draw.rectangle([(widthImg / 2 - 1, heightImg - 5), (widthImg / 2 + 1, heightImg)], - # outline=black, fill=notch_colour) - # return self.image - # - # def frameImage(self): - # foreground = tuple(self.palette[:3]) - # background = tuple(self.palette[-3:]) - # widthDev, heightDev = self.size - # widthImg, heightImg = self.image.size - # pastePt = ( - # max(0, (widthDev - widthImg) / 2), - # max(0, (heightDev - heightImg) / 2) - # ) - # corner1 = ( - # pastePt[0] - 1, - # pastePt[1] - 1 - # ) - # corner2 = ( - # pastePt[0] + widthImg + 1, - # pastePt[1] + heightImg + 1 - # ) - # imageBg = Image.new(self.image.mode, self.size, background) - # imageBg.paste(self.image, pastePt) - # draw = ImageDraw.Draw(imageBg) - # draw.rectangle([corner1, corner2], outline=foreground) - # self.image = imageBg From 0b90af77da2ba35c3fdd82f3b6ea5cf3f0b0f874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jastrz=C4=99bski?= Date: Tue, 28 May 2013 12:07:45 +0200 Subject: [PATCH 12/12] Disabled cropping and page number cutting for blank pages (close #43) --- README.md | 1 + kcc/image.py | 168 ++++++++++++++++++++++++++------------------------- 2 files changed, 86 insertions(+), 83 deletions(-) diff --git a/README.md b/README.md index 08bfb21..3664ee2 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,7 @@ The app relies and includes the following scripts/binaries: * Kindle Fire support (color ePub/Mobi) * Panel View support for horizontal content * Fixed panel order for horizontal pages when --rotate is enabled +* Disabled cropping and page number cutting for blank pages ## COPYRIGHT diff --git a/kcc/image.py b/kcc/image.py index 7af6f39..4dc7cc6 100755 --- a/kcc/image.py +++ b/kcc/image.py @@ -20,7 +20,7 @@ __copyright__ = '2012-2013, Ciro Mattia Gonano ' __docformat__ = 'restructuredtext en' import os -from PIL import Image, ImageOps, ImageStat +from PIL import Image, ImageOps, ImageStat, ImageChops class ImageFlags: @@ -237,93 +237,95 @@ class ComicPage: return None def cutPageNumber(self): - widthImg, heightImg = self.image.size - delta = 2 - diff = delta - fixedThreshold = 5 - if ImageStat.Stat(self.image).var[0] < 2 * fixedThreshold: - return self.image - while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] < fixedThreshold\ - and diff < heightImg: - diff += delta - diff -= delta - pageNumberCut1 = diff - if diff < delta: + if ImageChops.invert(self.image).getbbox() is not None: + widthImg, heightImg = self.image.size + delta = 2 diff = delta - oldStat = ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] - diff += delta - while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] - oldStat > 0\ - and diff < heightImg / 4: + fixedThreshold = 5 + if ImageStat.Stat(self.image).var[0] < 2 * fixedThreshold: + return self.image + while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] < fixedThreshold\ + and diff < heightImg: + diff += delta + diff -= delta + pageNumberCut1 = diff + if diff < delta: + diff = delta oldStat = ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] diff += delta - diff -= delta - pageNumberCut2 = diff - diff += delta - oldStat = ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg - pageNumberCut2))).var[0] - while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg - pageNumberCut2))).var[0]\ - < fixedThreshold + oldStat and diff < heightImg / 4: + while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] - oldStat > 0\ + and diff < heightImg / 4: + oldStat = ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] + diff += delta + diff -= delta + pageNumberCut2 = diff diff += delta - diff -= delta - pageNumberCut3 = diff - delta = 5 - diff = delta - while ImageStat.Stat(self.image.crop((0, heightImg - pageNumberCut2, diff, heightImg))).var[0] < fixedThreshold\ - and diff < widthImg: - diff += delta - diff -= delta - pageNumberX1 = diff - diff = delta - while ImageStat.Stat(self.image.crop((widthImg - diff, heightImg - pageNumberCut2, - widthImg, heightImg))).var[0] < fixedThreshold and diff < widthImg: - diff += delta - diff -= delta - pageNumberX2 = widthImg - diff - if pageNumberCut3 - pageNumberCut1 > 2 * delta\ - and float(pageNumberX2 - pageNumberX1) / float(pageNumberCut2 - pageNumberCut1) <= 9.0\ - and ImageStat.Stat(self.image.crop((0, heightImg - pageNumberCut3, widthImg, heightImg))).var[0]\ - / ImageStat.Stat(self.image).var[0] < 0.1\ - and pageNumberCut3 < heightImg / 4 - delta: - diff = pageNumberCut3 - else: - diff = pageNumberCut1 - self.image = self.image.crop((0, 0, widthImg, heightImg - diff)) + oldStat = ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg - pageNumberCut2))).var[0] + while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg - pageNumberCut2))).var[0]\ + < fixedThreshold + oldStat and diff < heightImg / 4: + diff += delta + diff -= delta + pageNumberCut3 = diff + delta = 5 + diff = delta + while ImageStat.Stat(self.image.crop((0, heightImg - pageNumberCut2, diff, heightImg))).var[0] < fixedThreshold\ + and diff < widthImg: + diff += delta + diff -= delta + pageNumberX1 = diff + diff = delta + while ImageStat.Stat(self.image.crop((widthImg - diff, heightImg - pageNumberCut2, + widthImg, heightImg))).var[0] < fixedThreshold and diff < widthImg: + diff += delta + diff -= delta + pageNumberX2 = widthImg - diff + if pageNumberCut3 - pageNumberCut1 > 2 * delta\ + and float(pageNumberX2 - pageNumberX1) / float(pageNumberCut2 - pageNumberCut1) <= 9.0\ + and ImageStat.Stat(self.image.crop((0, heightImg - pageNumberCut3, widthImg, heightImg))).var[0]\ + / ImageStat.Stat(self.image).var[0] < 0.1\ + and pageNumberCut3 < heightImg / 4 - delta: + diff = pageNumberCut3 + else: + diff = pageNumberCut1 + self.image = self.image.crop((0, 0, widthImg, heightImg - diff)) return self.image def cropWhiteSpace(self, threshold): - widthImg, heightImg = self.image.size - delta = 10 - diff = delta - # top - while ImageStat.Stat(self.image.crop((0, 0, widthImg, diff))).var[0] < threshold and diff < heightImg: - diff += delta - diff -= delta - # print "Top crop: %s"%diff - self.image = self.image.crop((0, diff, widthImg, heightImg)) - widthImg, heightImg = self.image.size - diff = delta - # left - while ImageStat.Stat(self.image.crop((0, 0, diff, heightImg))).var[0] < threshold and diff < widthImg: - diff += delta - diff -= delta - # print "Left crop: %s"%diff - self.image = self.image.crop((diff, 0, widthImg, heightImg)) - widthImg, heightImg = self.image.size - diff = delta - # down - while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] < threshold\ - and diff < heightImg: - diff += delta - diff -= delta - # print "Down crop: %s"%diff - self.image = self.image.crop((0, 0, widthImg, heightImg - diff)) - widthImg, heightImg = self.image.size - diff = delta - # right - while ImageStat.Stat(self.image.crop((widthImg - diff, 0, widthImg, heightImg))).var[0] < threshold\ - and diff < widthImg: - diff += delta - diff -= delta - # 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]) + if ImageChops.invert(self.image).getbbox() is not None: + widthImg, heightImg = self.image.size + delta = 10 + diff = delta + # top + while ImageStat.Stat(self.image.crop((0, 0, widthImg, diff))).var[0] < threshold and diff < heightImg: + diff += delta + diff -= delta + # print "Top crop: %s"%diff + self.image = self.image.crop((0, diff, widthImg, heightImg)) + widthImg, heightImg = self.image.size + diff = delta + # left + while ImageStat.Stat(self.image.crop((0, 0, diff, heightImg))).var[0] < threshold and diff < widthImg: + diff += delta + diff -= delta + # print "Left crop: %s"%diff + self.image = self.image.crop((diff, 0, widthImg, heightImg)) + widthImg, heightImg = self.image.size + diff = delta + # down + while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] < threshold\ + and diff < heightImg: + diff += delta + diff -= delta + # print "Down crop: %s"%diff + self.image = self.image.crop((0, 0, widthImg, heightImg - diff)) + widthImg, heightImg = self.image.size + diff = delta + # right + while ImageStat.Stat(self.image.crop((widthImg - diff, 0, widthImg, heightImg))).var[0] < threshold\ + and diff < widthImg: + diff += delta + diff -= delta + # 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