1
0
mirror of https://github.com/ciromattia/kcc synced 2026-04-18 06:58:58 +00:00

Compare commits

...

39 Commits
2.7 ... 2.8

Author SHA1 Message Date
Paweł Jastrzębski
d923299230 Updated README 2013-03-21 09:02:17 +01:00
Ciro Mattia Gonano
a79ebcec86 Version bump 2013-03-21 01:28:27 +01:00
Ciro Mattia Gonano
69bbee7648 Merge pull request #41 from ciromattia/kcc-outputformat2
Change default file output format
2013-03-20 14:33:43 -07:00
Paweł Jastrzębski
57d7729d05 Decreasing quality
File size/quality ratio is too high. It is not worth it.
Sadly huffman table optimization build-in PIL is just simply broken.
2013-03-20 21:12:30 +01:00
Paweł Jastrzębski
25a1dcb72c Updated GUI 2013-03-20 16:34:04 +01:00
Paweł Jastrzębski
cff9b73b80 Force JPEG output 2013-03-20 16:20:17 +01:00
Paweł Jastrzębski
23336de5b5 Version bump 2013-03-20 10:58:27 +01:00
Ciro Mattia Gonano
d346ca0466 Update version and readme 2013-03-20 10:39:42 +01:00
Ciro Mattia Gonano
22fadf078e Optimize archive extraction for zip/rar files (closes #40) 2013-03-20 10:35:50 +01:00
Ciro Mattia Gonano
74acf85683 Merge pull request #36 from ciromattia/kcc-panelview
Panel View support
2013-03-20 02:14:17 -07:00
Ciro Mattia Gonano
713de03d1d Merge branch 'master' into kcc-panelview 2013-03-20 10:12:57 +01:00
Ciro Mattia Gonano
5817f52110 Update rarfile 2013-03-20 10:10:01 +01:00
Paweł Jastrzębski
dfc03aee38 Restoring more safe filesize limit 2013-03-18 14:13:53 +01:00
Paweł Jastrzębski
6bc8038068 Yet another refactoring of resizeImage
I hope all bugs are dead this time!
2013-03-17 12:52:33 +01:00
Paweł Jastrzębski
eb7d56c1b9 Force upscaling for spitted pages
Only when landscape mode is on.
Without it smaller splitted pages looks really bad on PW.
2013-03-17 10:45:46 +01:00
Paweł Jastrzębski
f5b515ef79 GUI update 2013-03-17 09:37:57 +01:00
Paweł Jastrzębski
943431346d MAJOR Landscape Mode improvement
No idea how this change will impact Non-Kindle devices but quality
increase of spitted pages is amazing.
2013-03-17 08:47:35 +01:00
Paweł Jastrzębski
a597173b71 Added high quality Panel View 2013-03-16 21:53:23 +01:00
Paweł Jastrzębski
c484cc8fff Code inspection 2013-03-16 20:05:27 +01:00
Paweł Jastrzębski
f195a4ccbf Preliminary Panel View support for K3/KNT 2013-03-16 19:51:23 +01:00
Paweł Jastrzębski
f55bb6dce6 Code cleaning 2013-03-16 13:33:13 +01:00
Paweł Jastrzębski
e7f49f8330 New profiles
Kindle Touch support Virtual Panel View.
Kindle Classic/NT is not.
2013-03-16 10:19:57 +01:00
Paweł Jastrzębski
cec9356fc2 Code cleaning 2013-03-16 10:10:19 +01:00
Paweł Jastrzębski
6d704b382d Fixed merge bug 2013-03-13 21:10:50 +01:00
Ciro Mattia Gonano
1c5f67111f Merge branch 'master' into kcc-fakepanelview 2013-03-13 00:38:20 +01:00
Ciro Mattia Gonano
660df4b370 Bump to 2.8-dev 2013-03-13 00:38:03 +01:00
Ciro Mattia Gonano
19dc5cc6f2 Merge branch 'master' into kcc-fakepanelview
Apply PEP-8 codestyle
2013-03-13 00:21:09 +01:00
Ciro Mattia Gonano
67baf90d04 Merge branch 'master' into kcc-fakepanelview
Apply PEP-8 codestyle
2013-03-13 00:20:38 +01:00
Paweł Jastrzębski
07bf41ea6c Made commands little shorter 2013-03-12 19:56:19 +01:00
Ciro Mattia Gonano
c2cfc9b8db Apply the correct ordering for image processing (closes #33, thanks to @devernay) 2013-03-12 16:54:14 +01:00
Ciro Mattia Gonano
7a7a2bc10b Don't set topmost as we don't want GUI to be ALWAYS on top 2013-03-12 16:47:11 +01:00
Paweł Jastrzębski
e3127ed516 Code cleanup 2013-03-12 14:00:52 +01:00
Paweł Jastrzębski
93a98db43e Disabled unused functions 2013-03-12 10:48:30 +01:00
Paweł Jastrzębski
750b55649a Draft of FakePanelView Landscape mode 2013-03-12 10:47:37 +01:00
Paweł Jastrzębski
ca5854a8bd Real Panel View don't split in half 2013-03-10 18:18:20 +01:00
Paweł Jastrzębski
712f728a5e Draft of FakePanelView mode #2 2013-03-10 17:29:06 +01:00
Paweł Jastrzębski
eb7766a5e5 Draft of FakePanelView mode 2013-03-10 11:58:52 +01:00
Paweł Jastrzębski
0cabbfde96 Additional tweaks of Landscape mode 2013-03-10 10:43:34 +01:00
Paweł Jastrzębski
c5983276e5 Added --fakepanelview option 2013-03-10 00:03:11 +01:00
9 changed files with 413 additions and 241 deletions

View File

@@ -1,21 +1,16 @@
# KCC # KCC
`KCC` (a.k.a. `KindleComicConverter`) is a Python app to convert comic files or folders to ePub or Panel View MOBI. `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 as of version 2.2 it outputs valid ePub 2.0 so _**despite its name, KCC is It was initally developed for Kindle but since v2.2 it outputs valid ePub 2.0 so _**despite its name, KCC is
actually a comic 2 epub converter that every ereader owner can happily use**_. actually a comic 2 epub converter that every ereader owner can happily use**_.
It also optimizes comic images by: It can also optionally optimize images by applying a number of transformations.
- Enhancing contrast.
- Cutting page numbering.
- Cropping white borders.
- Resizing larger images to device's native resolution.
- Quantizing images to device's palette.
## BINARY RELEASES ## BINARY RELEASES
You can find the latest released binary at the following links: You can find the latest released binary at the following links:
- OS X: [https://dl.dropbox.com/u/16806101/KindleComicConverter_osx_2.7.zip](https://dl.dropbox.com/u/16806101/KindleComicConverter_osx_2.7.zip) - OS X: [https://dl.dropbox.com/u/16806101/KindleComicConverter_osx_2.8.zip](https://dl.dropbox.com/u/16806101/KindleComicConverter_osx_2.8.zip)
- Win64: [https://dl.dropbox.com/u/16806101/KindleComicConverter_win-amd64_2.7.zip](https://dl.dropbox.com/u/16806101/KindleComicConverter_win-amd64_2.7.zip) - Win64: [https://dl.dropbox.com/u/16806101/KindleComicConverter_win-amd64_2.8.zip](https://dl.dropbox.com/u/16806101/KindleComicConverter_win-amd64_2.7.zip)
- Win32: [http://pawelj.vulturis.eu/Shared/KindleComicConverter_win-x86_2.7.zip](http://pawelj.vulturis.eu/Shared/KindleComicConverter_win-x86_2.7.zip) *(thanks to [AcidWeb](https://github.com/AcidWeb))* - Win32: [http://pawelj.vulturis.eu/Shared/KindleComicConverter_win-x86_2.8.zip](http://pawelj.vulturis.eu/Shared/KindleComicConverter_win-x86_2.8.zip) *(thanks to [AcidWeb](https://github.com/AcidWeb))*
- Linux: Just download sourcecode and launch `python kcc.py` *(Provided you have Python and Pillow installed)* - Linux: Just download sourcecode and launch `python kcc.py` *(Provided you have Python and Pillow installed)*
## INPUT FORMATS ## INPUT FORMATS
@@ -52,12 +47,13 @@ Options:
--version show program's version number and exit --version show program's version number and exit
-h, --help show this help message and exit -h, --help show this help message and exit
-p PROFILE, --profile=PROFILE -p PROFILE, --profile=PROFILE
Device profile (Choose one among K1, K2, K3, K4, KDX, KDXG or KHD) [Default=KHD] Device profile (Choose one among K1, K2, K3, K4NT, K4T, KDX, KDXG or KHD) [Default=KHD]
-t TITLE, --title=TITLE -t TITLE, --title=TITLE
Comic title [Default=filename] Comic title [Default=filename]
-m, --manga-style Manga style (Right-to-left reading and splitting) [Default=False] -m, --manga-style Manga style (Right-to-left reading and splitting) [Default=False]
--nopanelviewhq Disable high quality Panel View [Default=False]
--noprocessing Do not apply image preprocessing (Page splitting and optimizations) [Default=True] --noprocessing Do not apply image preprocessing (Page splitting and optimizations) [Default=True]
--nodithering Disable image quantization [Default=False] --forcepng Create PNG files instead JPEG (For non-Kindle devices) [Default=False]
--gamma=GAMMA Apply gamma correction to linearize the image [Default=Auto] --gamma=GAMMA Apply gamma correction to linearize the image [Default=Auto]
--upscale Resize images smaller than device's resolution [Default=False] --upscale Resize images smaller than device's resolution [Default=False]
--stretch Stretch images to device's resolution [Default=False] --stretch Stretch images to device's resolution [Default=False]
@@ -119,6 +115,14 @@ The app relies and includes the following scripts/binaries:
Do not call kindlegen if source epub is bigger than 320MB (#17) Do not call kindlegen if source epub is bigger than 320MB (#17)
Get filetype from magic number (#14) Get filetype from magic number (#14)
PDF conversion works again 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)
## COPYRIGHT ## COPYRIGHT

4
kcc.py
View File

@@ -16,7 +16,7 @@
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE. # PERFORMANCE OF THIS SOFTWARE.
# #
__version__ = '2.7' __version__ = '2.8'
__license__ = 'ISC' __license__ = 'ISC'
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>' __copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
@@ -30,7 +30,7 @@ root = Tk()
root.resizable(width=False, height=False) root.resizable(width=False, height=False)
root.config(padx=5, pady=5, takefocus=True) root.config(padx=5, pady=5, takefocus=True)
root.title("Kindle Comic Converter v" + __version__) root.title("Kindle Comic Converter v" + __version__)
root.wm_attributes("-topmost", 1) #root.wm_attributes("-topmost", 1)
if platform == 'darwin': if platform == 'darwin':
os.environ['PATH'] = '/usr/local/bin:' + os.environ['PATH'] os.environ['PATH'] = '/usr/local/bin:' + os.environ['PATH']
elif platform == 'win32': elif platform == 'win32':

View File

@@ -38,6 +38,7 @@ class CBxArchive:
def extractCBZ(self, targetdir): def extractCBZ(self, targetdir):
cbzFile = zipfile.ZipFile(self.origFileName) cbzFile = zipfile.ZipFile(self.origFileName)
filelist = []
for f in cbzFile.namelist(): for f in cbzFile.namelist():
if f.startswith('__MACOSX') or f.endswith('.DS_Store'): if f.startswith('__MACOSX') or f.endswith('.DS_Store'):
pass # skip MacOS special files pass # skip MacOS special files
@@ -47,10 +48,12 @@ class CBxArchive:
except: except:
pass # the dir exists so we are going to extract the images only. pass # the dir exists so we are going to extract the images only.
else: else:
cbzFile.extract(f, targetdir) filelist.append(f)
cbzFile.extractall(targetdir, filelist)
def extractCBR(self, targetdir): def extractCBR(self, targetdir):
cbrFile = rarfile.RarFile(self.origFileName) cbrFile = rarfile.RarFile(self.origFileName)
filelist = []
for f in cbrFile.namelist(): for f in cbrFile.namelist():
if f.startswith('__MACOSX') or f.endswith('.DS_Store'): if f.startswith('__MACOSX') or f.endswith('.DS_Store'):
pass # skip MacOS special files pass # skip MacOS special files
@@ -60,9 +63,11 @@ class CBxArchive:
except: except:
pass # the dir exists so we are going to extract the images only. pass # the dir exists so we are going to extract the images only.
else: else:
cbrFile.extract(f, targetdir) filelist.append(f)
cbrFile.extractall(targetdir, filelist)
def extract(self, targetdir): def extract(self, targetdir):
print "\n" + targetdir + "\n"
if self.compressor == 'rar': if self.compressor == 'rar':
self.extractCBR(targetdir) self.extractCBR(targetdir)
elif self.compressor == 'zip': elif self.compressor == 'zip':

View File

@@ -16,7 +16,7 @@
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE. # PERFORMANCE OF THIS SOFTWARE.
# #
__version__ = '2.7' __version__ = '2.8'
__license__ = 'ISC' __license__ = 'ISC'
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>' __copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
@@ -61,26 +61,48 @@ def buildHTML(path, imgfile):
"<title>", filename[0], "</title>\n", "<title>", filename[0], "</title>\n",
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n", "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n",
"<link href=\"", "../" * (backref - 1), "<link href=\"", "../" * (backref - 1),
"stylesheet.css\" type=\"text/css\" rel=\"stylesheet\"/>\n", "style.css\" type=\"text/css\" rel=\"stylesheet\"/>\n",
"<link href=\"", "../" * (backref - 1),
"page_styles.css\" type=\"text/css\" rel=\"stylesheet\"/>\n",
"</head>\n", "</head>\n",
"<body class=\"kcc\">\n", "<body>\n",
"<div class=\"kcc1\"><img src=\"", "../" * backref, "Images/", postfix, imgfile, "\" alt=\"", "<div class=\"fs\">\n",
imgfile, "\" class=\"kcc2\"/></div>\n", "<div><img src=\"", "../" * backref, "Images/", postfix, imgfile, "\" alt=\"",
#"<div id=\"", filename[0], "-1\">\n", imgfile, "\" class=\"singlePage\"/></div>\n"
#"<a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"", filename[0],
#"-1-magTargetParent\", \"ordinal\":1}'></a>\n",
#"</div>\n",
#"<div id=\"", filename[0], "-1-magTargetParent\" class=\"target-mag-parent\">\n",
#"<div class=\"target-mag-lb\">\n",
#"</div>\n",
#"<div id=\"", filename[0], "-1-magTarget\" class=\"target-mag\">\n",
#"<img src=\"../" * backref, "Images/", postfix, imgfile, "\" alt=\"", imgfile, "\"/>\n",
#"</div></div>\n",
"</body>\n",
"</html>"
]) ])
if options.panelview:
if options.righttoleft:
f.writelines(["<div id=\"BoxTL\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"",
"BoxTL-Panel-Parent\", \"ordinal\":2}'></a></div>\n",
"<div id=\"BoxTR\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"",
"BoxTR-Panel-Parent\", \"ordinal\":1}'></a></div>\n",
"<div id=\"BoxBL\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"",
"BoxBL-Panel-Parent\", \"ordinal\":4}'></a></div>\n",
"<div id=\"BoxBR\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"",
"BoxBR-Panel-Parent\", \"ordinal\":3}'></a></div>\n"
])
else:
f.writelines(["<div id=\"BoxTL\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"",
"BoxTL-Panel-Parent\", \"ordinal\":1}'></a></div>\n",
"<div id=\"BoxTR\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"",
"BoxTR-Panel-Parent\", \"ordinal\":2}'></a></div>\n",
"<div id=\"BoxBL\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"",
"BoxBL-Panel-Parent\", \"ordinal\":3}'></a></div>\n",
"<div id=\"BoxBR\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"",
"BoxBR-Panel-Parent\", \"ordinal\":4}'></a></div>\n"
])
f.writelines(["<div id=\"BoxTL-Panel-Parent\" class=\"target-mag-parent\"><div id=\"BoxTL-Panel\" class=\"",
"target-mag\"><img src=\"", "../" * backref, "Images/", postfix, imgfile, "\" alt=\"",
imgfile, "\"/></div></div>\n",
"<div id=\"BoxTR-Panel-Parent\" class=\"target-mag-parent\"><div id=\"BoxTR-Panel\" class=\"",
"target-mag\"><img src=\"", "../" * backref, "Images/", postfix, imgfile, "\" alt=\"",
imgfile, "\"/></div></div>\n",
"<div id=\"BoxBL-Panel-Parent\" class=\"target-mag-parent\"><div id=\"BoxBL-Panel\" class=\"",
"target-mag\"><img src=\"", "../" * backref, "Images/", postfix, imgfile, "\" alt=\"",
imgfile, "\"/></div></div>\n",
"<div id=\"BoxBR-Panel-Parent\" class=\"target-mag-parent\"><div id=\"BoxBR-Panel\" class=\"",
"target-mag\"><img src=\"", "../" * backref, "Images/", postfix, imgfile, "\" alt=\"",
imgfile, "\"/></div></div>\n"
])
f.writelines(["</div>\n</body>\n</html>"])
f.close() f.close()
return path, imgfile return path, imgfile
@@ -128,12 +150,12 @@ def buildNCX(dstdir, title, chapters):
return return
def buildOPF(profile, dstdir, title, filelist, cover=None, righttoleft=False): def buildOPF(profile, dstdir, title, filelist, cover=None):
opffile = os.path.join(dstdir, 'OEBPS', 'content.opf') opffile = os.path.join(dstdir, 'OEBPS', 'content.opf')
# read the first file resolution # read the first file resolution
profilelabel, deviceres, palette, gamma = image.ProfileData.Profiles[profile] profilelabel, deviceres, palette, gamma, panelviewsize = image.ProfileData.Profiles[profile]
imgres = str(deviceres[0]) + "x" + str(deviceres[1]) imgres = str(deviceres[0]) + "x" + str(deviceres[1])
if righttoleft: if options.righttoleft:
writingmode = "horizontal-rl" writingmode = "horizontal-rl"
facing = "right" facing = "right"
facing1 = "right" facing1 = "right"
@@ -159,16 +181,19 @@ def buildOPF(profile, dstdir, title, filelist, cover=None, righttoleft=False):
"<meta name=\"book-type\" content=\"comic\"/>\n", "<meta name=\"book-type\" content=\"comic\"/>\n",
"<meta name=\"zero-gutter\" content=\"true\"/>\n", "<meta name=\"zero-gutter\" content=\"true\"/>\n",
"<meta name=\"zero-margin\" content=\"true\"/>\n", "<meta name=\"zero-margin\" content=\"true\"/>\n",
"<meta name=\"fixed-layout\" content=\"true\"/>\n", "<meta name=\"fixed-layout\" content=\"true\"/>\n"
"<meta name=\"orientation-lock\" content=\"none\"/>\n", ])
"<meta name=\"original-resolution\" content=\"", imgres, "\"/>\n", if options.landscapemode:
f.writelines(["<meta name=\"rendition:orientation\" content=\"auto\"/>\n",
"<meta name=\"orientation-lock\" content=\"none\"/>\n"])
else:
f.writelines(["<meta name=\"rendition:orientation\" content=\"portrait\"/>\n",
"<meta name=\"orientation-lock\" content=\"portrait\"/>\n"])
f.writelines(["<meta name=\"original-resolution\" content=\"", imgres, "\"/>\n",
"<meta name=\"primary-writing-mode\" content=\"", writingmode, "\"/>\n", "<meta name=\"primary-writing-mode\" content=\"", writingmode, "\"/>\n",
"<meta name=\"rendition:layout\" content=\"pre-paginated\"/>\n", "<meta name=\"rendition:layout\" content=\"pre-paginated\"/>\n",
"<meta name=\"rendition:orientation\" content=\"auto\"/>\n",
"</metadata>\n<manifest>\n<item id=\"ncx\" href=\"toc.ncx\" ", "</metadata>\n<manifest>\n<item id=\"ncx\" href=\"toc.ncx\" ",
"media-type=\"application/x-dtbncx+xml\"/>\n" "media-type=\"application/x-dtbncx+xml\"/>\n"])
])
# set cover
if cover is not None: if cover is not None:
filename = getImageFileName(cover.replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')) filename = getImageFileName(cover.replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\'))
if '.png' == filename[1]: if '.png' == filename[1]:
@@ -191,36 +216,46 @@ def buildOPF(profile, dstdir, title, filelist, cover=None, righttoleft=False):
mt = 'image/jpeg' mt = 'image/jpeg'
f.write("<item id=\"img_" + uniqueid + "\" href=\"" + folder + "/" + path[1] + "\" media-type=\"" f.write("<item id=\"img_" + uniqueid + "\" href=\"" + folder + "/" + path[1] + "\" media-type=\""
+ mt + "\"/>\n") + mt + "\"/>\n")
if (options.profile == 'K4' or options.profile == 'KHD') and splitCount > 0: if options.landscapemode and splitCount > 0:
splitCountUsed = 1 splitCountUsed = 1
while splitCountUsed <= splitCount: while splitCountUsed <= splitCount:
f.write("<item id=\"blank-page" + str(splitCountUsed) + f.write("<item id=\"blank-page" + str(splitCountUsed) +
"\" href=\"Text/blank.html\" media-type=\"application/xhtml+xml\"/>\n") "\" href=\"Text/blank.html\" media-type=\"application/xhtml+xml\"/>\n")
splitCountUsed += 1 splitCountUsed += 1
f.write("<item id=\"css\" href=\"Text/style.css\" media-type=\"text/css\"/>\n")
f.write("</manifest>\n<spine toc=\"ncx\">\n") f.write("</manifest>\n<spine toc=\"ncx\">\n")
splitCountUsed = 1 splitCountUsed = 1
for entry in reflist: for entry in reflist:
if entry.endswith("-1"): if entry.endswith("-1"):
if ((righttoleft and facing == 'left') or (not righttoleft and facing == 'right')) and \ # noinspection PyRedundantParentheses
(options.profile == 'K4' or options.profile == 'KHD'): if ((options.righttoleft and facing == 'left') or (not options.righttoleft and facing == 'right')) and\
options.landscapemode:
f.write("<itemref idref=\"blank-page" + str(splitCountUsed) + "\" properties=\"layout-blank\"/>\n") f.write("<itemref idref=\"blank-page" + str(splitCountUsed) + "\" properties=\"layout-blank\"/>\n")
splitCountUsed += 1 splitCountUsed += 1
if options.landscapemode:
f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing1 + "\"/>\n") f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing1 + "\"/>\n")
else:
f.write("<itemref idref=\"page_" + entry + "\"/>\n")
elif entry.endswith("-2"): elif entry.endswith("-2"):
if options.landscapemode:
f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing2 + "\"/>\n") f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing2 + "\"/>\n")
if righttoleft: else:
f.write("<itemref idref=\"page_" + entry + "\"/>\n")
if options.righttoleft:
facing = "right" facing = "right"
else: else:
facing = "left" facing = "left"
else: else:
if options.landscapemode:
f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing + "\"/>\n") f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing + "\"/>\n")
else:
f.write("<itemref idref=\"page_" + entry + "\"/>\n")
if facing == 'right': if facing == 'right':
facing = 'left' facing = 'left'
else: else:
facing = 'right' facing = 'right'
f.write("</spine>\n<guide>\n</guide>\n</package>\n") f.write("</spine>\n<guide>\n</guide>\n</package>\n")
f.close() f.close()
# finish with standard ePub folders
os.mkdir(os.path.join(dstdir, 'META-INF')) os.mkdir(os.path.join(dstdir, 'META-INF'))
f = open(os.path.join(dstdir, 'mimetype'), 'w') f = open(os.path.join(dstdir, 'mimetype'), 'w')
f.write('application/epub+zip') f.write('application/epub+zip')
@@ -256,12 +291,13 @@ def isInFilelist(filename, filelist):
def applyImgOptimization(img, isSplit=False, toRight=False): def applyImgOptimization(img, isSplit=False, toRight=False):
img.optimizeImage(options.gamma)
img.cropWhiteSpace(10.0) img.cropWhiteSpace(10.0)
if options.cutpagenumbers: if options.cutpagenumbers:
img.cutPageNumber() img.cutPageNumber()
img.resizeImage(options.upscale, options.stretch, options.black_borders, isSplit, toRight) img.resizeImage(options.upscale, options.stretch, options.black_borders, isSplit, toRight, options.landscapemode,
if not options.notquantize: options.nopanelviewhq)
img.optimizeImage(options.gamma)
if options.forcepng:
img.quantizeImage() img.quantizeImage()
@@ -290,30 +326,28 @@ def dirImgProcess(path):
if options.righttoleft: if options.righttoleft:
toRight1 = False toRight1 = False
toRight2 = True toRight2 = True
else:
toRight1 = True
toRight2 = False
if options.righttoleft:
if facing == "left": if facing == "left":
splitCount += 1 splitCount += 1
facing = "right" facing = "right"
else: else:
toRight1 = True
toRight2 = False
if facing == "right": if facing == "right":
splitCount += 1 splitCount += 1
facing = "left" facing = "left"
img0 = image.ComicPage(split[0], options.profile) img0 = image.ComicPage(split[0], options.profile)
applyImgOptimization(img0, True, toRight1) applyImgOptimization(img0, True, toRight1)
img0.saveToDir(dirpath, options.notquantize) img0.saveToDir(dirpath, options.forcepng)
img1 = image.ComicPage(split[1], options.profile) img1 = image.ComicPage(split[1], options.profile)
applyImgOptimization(img1, True, toRight2) applyImgOptimization(img1, True, toRight2)
img1.saveToDir(dirpath, options.notquantize) img1.saveToDir(dirpath, options.forcepng)
else: else:
if facing == "right": if facing == "right":
facing = "left" facing = "left"
else: else:
facing = "right" facing = "right"
applyImgOptimization(img) applyImgOptimization(img)
img.saveToDir(dirpath, options.notquantize) img.saveToDir(dirpath, options.forcepng)
def genEpubStruct(path): def genEpubStruct(path):
@@ -321,15 +355,16 @@ def genEpubStruct(path):
filelist = [] filelist = []
chapterlist = [] chapterlist = []
cover = None cover = None
_, deviceres, _, _, panelviewsize = image.ProfileData.Profiles[options.profile]
os.mkdir(os.path.join(path, 'OEBPS', 'Text')) os.mkdir(os.path.join(path, 'OEBPS', 'Text'))
f = open(os.path.join(path, 'OEBPS', 'Text', 'page_styles.css'), 'w') 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.
f.writelines(["@page {\n", f.writelines(["@page {\n",
"margin-bottom: 0;\n", "margin-bottom: 0;\n",
"margin-top: 0\n", "margin-top: 0\n",
"}\n"]) "}\n",
f.close() "body {\n",
f = open(os.path.join(path, 'OEBPS', 'Text', 'stylesheet.css'), 'w')
f.writelines([".kcc {\n",
"display: block;\n", "display: block;\n",
"margin-bottom: 0;\n", "margin-bottom: 0;\n",
"margin-left: 0;\n", "margin-left: 0;\n",
@@ -341,21 +376,112 @@ def genEpubStruct(path):
"padding-top: 0;\n", "padding-top: 0;\n",
"text-align: left\n", "text-align: left\n",
"}\n", "}\n",
".kcc1 {\n", "div.fs {\n",
"height: ", str(deviceres[1]), "px;\n",
"width: ", str(deviceres[0]), "px;\n",
"position: relative;\n",
"display: block;\n", "display: block;\n",
"text-align: center\n", "text-align: center\n",
"}\n", "}\n",
".kcc2 {\n", "div.fs a {\n",
" height: auto;\n", "display: block;\n",
" width: auto\n", "width : 100%;\n",
"}\n"]) "height: 100%;\n",
"}\n",
"div.fs div {\n",
"position: absolute;\n",
"}\n",
"img.singlePage {\n",
"position: absolute;\n",
"height: ", str(deviceres[1]), "px;\n",
"width: ", str(deviceres[0]), "px;\n",
"}\n",
"div.target-mag-parent {\n",
"width:100%;\n",
"height:100%;\n",
"display:none;\n",
"}\n",
"div.target-mag {\n",
"position: absolute;\n",
"display: block;\n",
"overflow: hidden;\n",
"}\n",
"div.target-mag img {\n",
"position: absolute;\n",
"height: ", str(panelviewsize[1]), "px;\n",
"width: ", str(panelviewsize[0]), "px;\n",
"}\n",
"#BoxTL {\n",
"top: 0;\n",
"left: 0;\n",
"height: 50%;\n",
"width: 50%;\n",
"}\n",
"#BoxTR {\n",
"top: 0;\n",
"right: 0;\n",
"height: 50%;\n",
"width: 50%;\n",
"}\n",
"#BoxBL {\n",
"bottom: 0;\n",
"left: 0;\n",
"height: 50%;\n",
"width: 50%;\n",
"}\n",
"#BoxBR {\n",
"bottom: 0;\n",
"right: 0;\n",
"height: 50%;\n",
"width: 50%;\n",
"}\n",
"#BoxTL-Panel {\n",
"top: 0;\n",
"left: 0;\n",
"height: 100%;\n",
"width: 100%;\n",
"}\n",
"#BoxTL-Panel img {\n",
"top: 0%;\n",
"left: 0%;\n",
"}\n",
"#BoxTR-Panel {\n",
"top: 0;\n",
"right: 0;\n",
"height: 100%;\n",
"width: 100%;\n",
"}\n",
"#BoxTR-Panel img {\n",
"top: 0%;\n",
"right: 0%;\n",
"}\n",
"#BoxBL-Panel {\n",
"bottom: 0;\n",
"left: 0;\n",
"height: 100%;\n",
"width: 100%;\n",
"}\n",
"#BoxBL-Panel img {\n",
"bottom: 0%;\n",
"left: 0%;\n",
"}\n",
"#BoxBR-Panel {\n",
"bottom: 0;\n",
"right: 0;\n",
"height: 100%;\n",
"width: 100%;\n",
"}\n",
"#BoxBR-Panel img {\n",
"bottom: 0%;\n",
"right: 0%;\n",
"}"
])
f.close() f.close()
for (dirpath, dirnames, filenames) in os.walk(os.path.join(path, 'OEBPS', 'Images')): for (dirpath, dirnames, filenames) in os.walk(os.path.join(path, 'OEBPS', 'Images')):
chapter = False chapter = False
for afile in filenames: for afile in filenames:
filename = getImageFileName(afile) filename = getImageFileName(afile)
if filename is not None: if filename is not None:
# put credits at the end
if "credit" in afile.lower(): if "credit" in afile.lower():
os.rename(os.path.join(dirpath, afile), os.path.join(dirpath, 'ZZZ999_' + afile)) os.rename(os.path.join(dirpath, afile), os.path.join(dirpath, 'ZZZ999_' + afile))
afile = 'ZZZ999_' + afile afile = 'ZZZ999_' + afile
@@ -375,8 +501,8 @@ def genEpubStruct(path):
convert = lambda text: int(text) if text.isdigit() else text convert = lambda text: int(text) if text.isdigit() else text
alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)] 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()))) filelist.sort(key=lambda name: (alphanum_key(name[0].lower()), alphanum_key(name[1].lower())))
buildOPF(options.profile, path, options.title, filelist, cover, options.righttoleft) buildOPF(options.profile, path, options.title, filelist, cover)
if (options.profile == 'K4' or options.profile == 'KHD') and splitCount > 0: if options.landscapemode and splitCount > 0:
filelist.append(buildBlankHTML(os.path.join(path, 'OEBPS', 'Text'))) filelist.append(buildBlankHTML(os.path.join(path, 'OEBPS', 'Text')))
@@ -425,15 +551,17 @@ def main(argv=None):
usage = "Usage: %prog [options] comic_file|comic_folder" usage = "Usage: %prog [options] comic_file|comic_folder"
parser = OptionParser(usage=usage, version=__version__) parser = OptionParser(usage=usage, version=__version__)
parser.add_option("-p", "--profile", action="store", dest="profile", default="KHD", parser.add_option("-p", "--profile", action="store", dest="profile", default="KHD",
help="Device profile (Choose one among K1, K2, K3, K4, KDX, KDXG or KHD) [Default=KHD]") help="Device profile (Choose one among K1, K2, K3, K4NT, K4T, KDX, KDXG or KHD) [Default=KHD]")
parser.add_option("-t", "--title", action="store", dest="title", default="defaulttitle", parser.add_option("-t", "--title", action="store", dest="title", default="defaulttitle",
help="Comic title [Default=filename]") help="Comic title [Default=filename]")
parser.add_option("-m", "--manga-style", action="store_true", dest="righttoleft", default=False, parser.add_option("-m", "--manga-style", action="store_true", dest="righttoleft", default=False,
help="Manga style (Right-to-left reading and splitting) [Default=False]") help="Manga style (Right-to-left reading and splitting) [Default=False]")
parser.add_option("--nopanelviewhq", action="store_true", dest="nopanelviewhq", default=False,
help="Disable high quality Panel View [Default=False]")
parser.add_option("--noprocessing", action="store_false", dest="imgproc", default=True, parser.add_option("--noprocessing", action="store_false", dest="imgproc", default=True,
help="Do not apply image preprocessing (Page splitting and optimizations) [Default=True]") help="Do not apply image preprocessing (Page splitting and optimizations) [Default=True]")
parser.add_option("--nodithering", action="store_true", dest="notquantize", default=False, parser.add_option("--forcepng", action="store_true", dest="forcepng", default=False,
help="Disable image quantization [Default=False]") help="Create PNG files instead JPEG (For non-Kindle devices) [Default=False]")
parser.add_option("--gamma", type="float", dest="gamma", default="0.0", parser.add_option("--gamma", type="float", dest="gamma", default="0.0",
help="Apply gamma correction to linearize the image [Default=Auto]") help="Apply gamma correction to linearize the image [Default=Auto]")
parser.add_option("--upscale", action="store_true", dest="upscale", default=False, parser.add_option("--upscale", action="store_true", dest="upscale", default=False,
@@ -454,6 +582,7 @@ def main(argv=None):
parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False, parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False,
help="Verbose output [Default=False]") help="Verbose output [Default=False]")
options, args = parser.parse_args(argv) options, args = parser.parse_args(argv)
checkOptions()
if len(args) != 1: if len(args) != 1:
parser.print_help() parser.print_help()
return return
@@ -485,6 +614,22 @@ def main(argv=None):
return epubpath return epubpath
def checkOptions():
global options
if options.profile == 'K4T' or options.profile == 'KHD':
options.landscapemode = True
else:
options.landscapemode = False
if options.profile == 'K3' or options.profile == 'K4NT':
#Real Panel View
options.panelview = True
else:
#Virtual Panel View
options.panelview = False
if options.profile == 'K1' or options.profile == 'K2' or options.profile == 'KDX' or options.profile == 'KDXG':
options.nopanelviewhq = True
def getEpubPath(): def getEpubPath():
global epub_path global epub_path
return epub_path return epub_path

View File

@@ -28,7 +28,6 @@ import comic2ebook
import kindlestrip import kindlestrip
from image import ProfileData from image import ProfileData
from subprocess import call from subprocess import call
from subprocess import check_call
import os import os
import shutil import shutil
import stat import stat
@@ -95,28 +94,30 @@ class MainWindow:
self.options = { self.options = {
'Aepub_only': IntVar(None, 0), 'Aepub_only': IntVar(None, 0),
'Bmangastyle': IntVar(None, 0), 'Bmangastyle': IntVar(None, 0),
'Cimage_preprocess': IntVar(None, 0), 'Cnopanelviewhq': IntVar(None, 0),
'Dnotquantize': IntVar(None, 0), 'Dimage_preprocess': IntVar(None, 0),
'Eimage_gamma': DoubleVar(None, 0.0), 'Eforcepng': IntVar(None, 0),
'Fimage_upscale': IntVar(None, 0), 'Fimage_gamma': DoubleVar(None, 0.0),
'Gimage_stretch': IntVar(None, 0), 'Gimage_upscale': IntVar(None, 0),
'Hblack_borders': IntVar(None, 0), 'Himage_stretch': IntVar(None, 0),
'Irotate': IntVar(None, 0), 'Iblack_borders': IntVar(None, 0),
'Jnosplitrotate': IntVar(None, 0), 'Jrotate': IntVar(None, 0),
'Kcut_page_numbers': IntVar(None, 0) 'Knosplitrotate': IntVar(None, 0),
'Lcut_page_numbers': IntVar(None, 0)
} }
self.optionlabels = { self.optionlabels = {
'Aepub_only': "Generate EPUB only", 'Aepub_only': "Generate EPUB only",
'Cimage_preprocess': "Disable image optimizations",
'Dnotquantize': "Disable image quantization",
'Jnosplitrotate': "Disable splitting and rotation",
'Irotate': "Rotate images instead splitting them",
'Kcut_page_numbers': "Disable page numbers cutting",
'Bmangastyle': "Manga mode", 'Bmangastyle': "Manga mode",
'Eimage_gamma': "Custom gamma correction", 'Cnopanelviewhq': "Disable high quality Panel View",
'Fimage_upscale': "Allow image upscaling", 'Dimage_preprocess': "Disable image optimizations",
'Gimage_stretch': "Stretch images", 'Eforcepng': "Create PNG files instead JPEG",
'Hblack_borders': "Use black borders" 'Fimage_gamma': "Custom gamma correction",
'Gimage_upscale': "Allow image upscaling",
'Himage_stretch': "Stretch images",
'Iblack_borders': "Use black borders",
'Jrotate': "Rotate images instead splitting them",
'Knosplitrotate': "Disable splitting and rotation",
'Lcut_page_numbers': "Disable page numbers cutting"
} }
self.optionsButtons = {} self.optionsButtons = {}
for key in sorted(self.options): for key in sorted(self.options):
@@ -161,27 +162,29 @@ class MainWindow:
return return
profilekey = ProfileData.ProfileLabels[self.profile.get()] profilekey = ProfileData.ProfileLabels[self.profile.get()]
argv = ["-p", profilekey] argv = ["-p", profilekey]
if self.options['Eimage_gamma'].get() != 0.0:
argv.append("--gamma")
argv.append(self.options['Eimage_gamma'].get())
if self.options['Cimage_preprocess'].get() == 1:
argv.append("--noprocessing")
if self.options['Dnotquantize'].get() == 1:
argv.append("--nodithering")
if self.options['Jnosplitrotate'].get() == 1:
argv.append("--nosplitrotate")
if self.options['Irotate'].get() == 1:
argv.append("--rotate")
if self.options['Kcut_page_numbers'].get() == 1:
argv.append("--nocutpagenumbers")
if self.options['Bmangastyle'].get() == 1: if self.options['Bmangastyle'].get() == 1:
argv.append("-m") argv.append("-m")
if self.options['Fimage_upscale'].get() == 1: if self.options['Cnopanelviewhq'].get() == 1:
argv.append("--nopanelviewhq")
if self.options['Dimage_preprocess'].get() == 1:
argv.append("--noprocessing")
if self.options['Eforcepng'].get() == 1:
argv.append("--forcepng")
if self.options['Fimage_gamma'].get() != 0.0:
argv.append("--gamma")
argv.append(self.options['Fimage_gamma'].get())
if self.options['Gimage_upscale'].get() == 1:
argv.append("--upscale") argv.append("--upscale")
if self.options['Gimage_stretch'].get() == 1: if self.options['Himage_stretch'].get() == 1:
argv.append("--stretch") argv.append("--stretch")
if self.options['Hblack_borders'].get() == 1: if self.options['Iblack_borders'].get() == 1:
argv.append("--blackborders") argv.append("--blackborders")
if self.options['Jrotate'].get() == 1:
argv.append("--rotate")
if self.options['Knosplitrotate'].get() == 1:
argv.append("--nosplitrotate")
if self.options['Lcut_page_numbers'].get() == 1:
argv.append("--nocutpagenumbers")
errors = False errors = False
left_files = len(self.filelist) left_files = len(self.filelist)
filenum = 0 filenum = 0
@@ -206,10 +209,10 @@ class MainWindow:
continue continue
if self.options['Aepub_only'].get() == 0: if self.options['Aepub_only'].get() == 0:
try: try:
if os.path.getsize(epub_path) > 335544320: if os.path.getsize(epub_path) > 314572800:
# do not call kindlegen if source is bigger than 320MB # do not call kindlegen if source is bigger than 300MB
tkMessageBox.showwarning('KindleGen Warning', tkMessageBox.showwarning('KindleGen Warning',
"ePub file %s is bigger than 320MB, not suitable for kindlegen" % "ePub file %s is bigger than 300MB, not suitable for kindlegen" %
epub_path) epub_path)
continue continue
retcode = call("kindlegen \"" + epub_path + "\"", shell=True) retcode = call("kindlegen \"" + epub_path + "\"", shell=True)

View File

@@ -20,7 +20,7 @@ __copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import os import os
from PIL import Image, ImageOps, ImageDraw, ImageStat from PIL import Image, ImageOps, ImageStat
class ImageFlags: class ImageFlags:
@@ -77,20 +77,22 @@ class ProfileData:
] ]
Profiles = { Profiles = {
'K1': ("Kindle", (600, 800), Palette4, 1.8), 'K1': ("Kindle 1", (600, 800), Palette4, 1.8, (900, 1200)),
'K2': ("Kindle 2", (600, 800), Palette15, 1.8), 'K2': ("Kindle 2", (600, 800), Palette15, 1.8, (900, 1200)),
'K3': ("Kindle 3/Keyboard", (600, 800), Palette16, 1.8), 'K3': ("Kindle Keyboard", (600, 800), Palette16, 1.8, (900, 1200)),
'K4': ("Kindle 4/NT/Touch", (600, 800), Palette16, 1.8), 'K4NT': ("Kindle Non-Touch", (600, 800), Palette16, 1.8, (900, 1200)),
'KHD': ("Kindle Paperwhite", (758, 1024), Palette16, 1.8), 'K4T': ("Kindle Touch", (600, 800), Palette16, 1.8, (900, 1200)),
'KDX': ("Kindle DX", (824, 1200), Palette15, 1.8), 'KHD': ("Kindle Paperwhite", (758, 1024), Palette16, 1.8, (1137, 1536)),
'KDXG': ("Kindle DXG", (824, 1200), Palette16, 1.8) 'KDX': ("Kindle DX", (824, 1200), Palette15, 1.8, (1236, 1800)),
'KDXG': ("Kindle DXG", (824, 1200), Palette16, 1.8, (1236, 1800))
} }
ProfileLabels = { ProfileLabels = {
"Kindle": 'K1', "Kindle 1": 'K1',
"Kindle 2": 'K2', "Kindle 2": 'K2',
"Kindle 3/Keyboard": 'K3', "Kindle 3/Keyboard": 'K3',
"Kindle 4/NT/Touch": 'K4', "Kindle 4/Non-Touch": 'K4NT',
"Kindle 4/Touch": 'K4T',
"Kindle Paperwhite": 'KHD', "Kindle Paperwhite": 'KHD',
"Kindle DX": 'KDX', "Kindle DX": 'KDX',
"Kindle DXG": 'KDXG' "Kindle DXG": 'KDXG'
@@ -101,7 +103,7 @@ class ComicPage:
def __init__(self, source, device): def __init__(self, source, device):
try: try:
self.profile = device self.profile = device
self.profile_label, self.size, self.palette, self.gamma = ProfileData.Profiles[device] self.profile_label, self.size, self.palette, self.gamma, self.panelviewsize = ProfileData.Profiles[device]
except KeyError: except KeyError:
raise RuntimeError('Unexpected output device %s' % device) raise RuntimeError('Unexpected output device %s' % device)
try: try:
@@ -111,15 +113,15 @@ class ComicPage:
raise RuntimeError('Cannot read image file %s' % source) raise RuntimeError('Cannot read image file %s' % source)
self.image = self.image.convert('RGB') self.image = self.image.convert('RGB')
def saveToDir(self, targetdir, notquantize): def saveToDir(self, targetdir, forcepng):
filename = os.path.basename(self.origFileName) filename = os.path.basename(self.origFileName)
try: try:
self.image = self.image.convert('L') # convert to grayscale self.image = self.image.convert('L') # convert to grayscale
os.remove(os.path.join(targetdir, filename)) os.remove(os.path.join(targetdir, filename))
if notquantize: if forcepng:
self.image.save(os.path.join(targetdir, os.path.splitext(filename)[0] + ".jpg"), "JPEG")
else:
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] + ".png"), "PNG")
else:
self.image.save(os.path.join(targetdir, os.path.splitext(filename)[0] + ".jpg"), "JPEG")
except IOError as e: except IOError as e:
raise RuntimeError('Cannot write image in directory %s: %s' % (targetdir, e)) raise RuntimeError('Cannot write image in directory %s: %s' % (targetdir, e))
@@ -132,6 +134,8 @@ class ComicPage:
self.image = ImageOps.autocontrast(Image.eval(self.image, lambda a: 255 * (a / 255.) ** gamma)) self.image = ImageOps.autocontrast(Image.eval(self.image, lambda a: 255 * (a / 255.) ** gamma))
def quantizeImage(self): def quantizeImage(self):
self.image = self.image.convert('L') # convert to grayscale
self.image = self.image.convert("RGB") # convert back to RGB
colors = len(self.palette) / 3 colors = len(self.palette) / 3
if colors < 256: if colors < 256:
self.palette += self.palette[:3] * (256 - colors) self.palette += self.palette[:3] * (256 - colors)
@@ -139,46 +143,48 @@ class ComicPage:
palImg.putpalette(self.palette) palImg.putpalette(self.palette)
self.image = self.image.quantize(palette=palImg) self.image = self.image.quantize(palette=palImg)
def resizeImage(self, upscale=False, stretch=False, black_borders=False, isSplit=False, toRight=False): def resizeImage(self, upscale=False, stretch=False, black_borders=False, isSplit=False, toRight=False,
landscapeMode=False, noPanelViewHQ=False):
method = Image.ANTIALIAS method = Image.ANTIALIAS
if black_borders: if black_borders:
fill = 'black' fill = 'black'
else: else:
fill = 'white' fill = 'white'
if noPanelViewHQ:
size = (self.size[0], self.size[1])
else:
size = (self.panelviewsize[0], self.panelviewsize[1])
if isSplit and landscapeMode:
upscale = True
if self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1]: if self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1]:
if not upscale: if not upscale:
if isSplit and (self.profile == 'K4' or self.profile == 'KHD'):
borderw = (self.size[0] - self.image.size[0])
borderh = (self.size[1] - self.image.size[1]) / 2
self.image = ImageOps.expand(self.image, border=(0, borderh), fill=fill)
tempImg = Image.new(self.image.mode, (self.image.size[0] + borderw, self.image.size[1]), fill)
if toRight:
tempImg.paste(self.image, (borderw, 0))
else:
tempImg.paste(self.image, (0, 0))
self.image = tempImg
else:
borderw = (self.size[0] - self.image.size[0]) / 2 borderw = (self.size[0] - self.image.size[0]) / 2
borderh = (self.size[1] - self.image.size[1]) / 2 borderh = (self.size[1] - self.image.size[1]) / 2
self.image = ImageOps.expand(self.image, border=(borderw, borderh), fill=fill) self.image = ImageOps.expand(self.image, border=(borderw, borderh), fill=fill)
return self.image return self.image
else: else:
method = Image.NEAREST 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(self.size, method) self.image = self.image.resize(size, method)
return self.image return self.image
ratioDev = float(self.size[0]) / float(self.size[1]) ratioDev = float(self.size[0]) / float(self.size[1])
if (float(self.image.size[0]) / float(self.image.size[1])) < ratioDev: if (float(self.image.size[0]) / float(self.image.size[1])) < ratioDev:
if isSplit and landscapeMode:
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] diff = int(self.image.size[1] * ratioDev) - self.image.size[0]
if isSplit and (self.profile == 'K4' or self.profile == 'KHD'):
diff = 2
self.image = ImageOps.expand(self.image, border=(diff / 2, 0), fill=fill) self.image = ImageOps.expand(self.image, border=(diff / 2, 0), fill=fill)
elif (float(self.image.size[0]) / float(self.image.size[1])) > ratioDev: elif (float(self.image.size[0]) / float(self.image.size[1])) > ratioDev:
diff = int(self.image.size[0] / ratioDev) - self.image.size[1] 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.expand(self.image, border=(0, diff / 2), fill=fill)
self.image = ImageOps.fit(self.image, self.size, method=method, centering=(0.5, 0.5)) self.image = ImageOps.fit(self.image, size, method=method, centering=(0.5, 0.5))
return self.image return self.image
def splitPage(self, targetdir, righttoleft=False, rotate=False): def splitPage(self, targetdir, righttoleft=False, rotate=False):
@@ -218,29 +224,6 @@ class ComicPage:
else: else:
return None return None
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
def cutPageNumber(self): def cutPageNumber(self):
widthImg, heightImg = self.image.size widthImg, heightImg = self.image.size
delta = 2 delta = 2
@@ -333,37 +316,60 @@ class ComicPage:
# print "New size: %sx%s"%(self.image.size[0],self.image.size[1]) # print "New size: %sx%s"%(self.image.size[0],self.image.size[1])
return self.image return self.image
def addProgressbar(self, file_number, files_totalnumber, size, howoften): # def addProgressbar(self, file_number, files_totalnumber, size, howoften):
if file_number // howoften != float(file_number) / howoften: # if file_number // howoften != float(file_number) / howoften:
return self.image # return self.image
white = (255, 255, 255) # white = (255, 255, 255)
black = (0, 0, 0) # black = (0, 0, 0)
widthDev, heightDev = size # widthDev, heightDev = size
widthImg, heightImg = self.image.size # widthImg, heightImg = self.image.size
pastePt = ( # pastePt = (
max(0, (widthDev - widthImg) / 2), # max(0, (widthDev - widthImg) / 2),
max(0, (heightDev - heightImg) / 2) # max(0, (heightDev - heightImg) / 2)
) # )
imageBg = Image.new('RGB', size, white) # imageBg = Image.new('RGB', size, white)
imageBg.paste(self.image, pastePt) # imageBg.paste(self.image, pastePt)
self.image = imageBg # self.image = imageBg
widthImg, heightImg = self.image.size # widthImg, heightImg = self.image.size
draw = ImageDraw.Draw(self.image) # draw = ImageDraw.Draw(self.image)
#Black rectangle # #Black rectangle
draw.rectangle([(0, heightImg - 3), (widthImg, heightImg)], outline=black, fill=black) # draw.rectangle([(0, heightImg - 3), (widthImg, heightImg)], outline=black, fill=black)
#White rectangle # #White rectangle
draw.rectangle([(widthImg * file_number / files_totalnumber, heightImg - 3), (widthImg - 1, heightImg)], # draw.rectangle([(widthImg * file_number / files_totalnumber, heightImg - 3), (widthImg - 1, heightImg)],
outline=black, fill=white) # outline=black, fill=white)
#Making notches # #Making notches
for i in range(1, 10): # for i in range(1, 10):
if i <= (10 * file_number / files_totalnumber): # if i <= (10 * file_number / files_totalnumber):
notch_colour = white # White # notch_colour = white # White
else: # else:
notch_colour = black # Black # notch_colour = black # Black
draw.line([(widthImg * float(i) / 10, heightImg - 3), (widthImg * float(i) / 10, heightImg)], # draw.line([(widthImg * float(i) / 10, heightImg - 3), (widthImg * float(i) / 10, heightImg)],
fill=notch_colour) # fill=notch_colour)
#The 50% # #The 50%
if i == 5: # if i == 5:
draw.rectangle([(widthImg / 2 - 1, heightImg - 5), (widthImg / 2 + 1, heightImg)], # draw.rectangle([(widthImg / 2 - 1, heightImg - 5), (widthImg / 2 + 1, heightImg)],
outline=black, fill=notch_colour) # outline=black, fill=notch_colour)
return self.image # 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

View File

@@ -73,7 +73,7 @@ __all__ = ['is_rarfile', 'RarInfo', 'RarFile', 'RarExtFile']
## Imports and compat - support both Python 2.x and 3.x ## Imports and compat - support both Python 2.x and 3.x
## ##
import sys, os, struct import sys, os, struct, errno
from struct import pack, unpack from struct import pack, unpack
from binascii import crc32 from binascii import crc32
from tempfile import mkstemp from tempfile import mkstemp
@@ -320,6 +320,8 @@ class RarMemoryError(RarExecError):
"""Memory error""" """Memory error"""
class RarCreateError(RarExecError): class RarCreateError(RarExecError):
"""Create error""" """Create error"""
class RarNoFilesError(RarExecError):
"""No files that match pattern were found"""
class RarUserBreak(RarExecError): class RarUserBreak(RarExecError):
"""User stop""" """User stop"""
class RarUnknownError(RarExecError): class RarUnknownError(RarExecError):
@@ -1757,8 +1759,15 @@ def custom_popen(cmd):
creationflags = 0x08000000 # CREATE_NO_WINDOW creationflags = 0x08000000 # CREATE_NO_WINDOW
# run command # run command
p = Popen(cmd, bufsize = 0, stdout = PIPE, stdin = PIPE, stderr = STDOUT, try:
p = Popen(cmd, bufsize = 0,
stdout = PIPE, stdin = PIPE, stderr = STDOUT,
creationflags = creationflags) creationflags = creationflags)
except OSError:
ex = sys.exc_info()[1]
if ex.errno == errno.ENOENT:
raise RarExecError("Unrar not installed? (rarfile.UNRAR_TOOL=%r)" % UNRAR_TOOL)
raise
return p return p
def check_returncode(p, out): def check_returncode(p, out):
@@ -1772,7 +1781,7 @@ def check_returncode(p, out):
errmap = [None, errmap = [None,
RarWarning, RarFatalError, RarCRCError, RarLockedArchiveError, RarWarning, RarFatalError, RarCRCError, RarLockedArchiveError,
RarWriteError, RarOpenError, RarUserError, RarMemoryError, RarWriteError, RarOpenError, RarUserError, RarMemoryError,
RarCreateError] # codes from rar.txt RarCreateError, RarNoFilesError] # codes from rar.txt
if code > 0 and code < len(errmap): if code > 0 and code < len(errmap):
exc = errmap[code] exc = errmap[code]
elif code == 255: elif code == 255:

View File

@@ -15,7 +15,7 @@ use_setuptools()
import sys import sys
NAME = "KindleComicConverter" NAME = "KindleComicConverter"
VERSION = "2.7" VERSION = "2.8"
MAIN = "kcc.py" MAIN = "kcc.py"
includefiles = ['README.md', 'MANIFEST.in', 'LICENSE.txt', 'comic2ebook.ico', 'comic2ebook.icns'] includefiles = ['README.md', 'MANIFEST.in', 'LICENSE.txt', 'comic2ebook.ico', 'comic2ebook.icns']

View File

@@ -10,7 +10,7 @@ sys.path.insert(0, 'kcc')
setup( setup(
name = "KindleComicConverter", name = "KindleComicConverter",
version = "2.7", version = "2.8",
author = "Ciro Mattia Gonano", author = "Ciro Mattia Gonano",
author_email = "ciromattia@gmail.com", author_email = "ciromattia@gmail.com",
description = "A tool to convert comics (CBR/CBZ/PDFs/image folders) to MOBI.", description = "A tool to convert comics (CBR/CBZ/PDFs/image folders) to MOBI.",