mirror of
https://github.com/ciromattia/kcc
synced 2026-04-16 22:18:51 +00:00
Compare commits
100 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d923299230 | ||
|
|
a79ebcec86 | ||
|
|
69bbee7648 | ||
|
|
57d7729d05 | ||
|
|
25a1dcb72c | ||
|
|
cff9b73b80 | ||
|
|
23336de5b5 | ||
|
|
d346ca0466 | ||
|
|
22fadf078e | ||
|
|
74acf85683 | ||
|
|
713de03d1d | ||
|
|
5817f52110 | ||
|
|
dfc03aee38 | ||
|
|
6bc8038068 | ||
|
|
eb7d56c1b9 | ||
|
|
f5b515ef79 | ||
|
|
943431346d | ||
|
|
a597173b71 | ||
|
|
c484cc8fff | ||
|
|
f195a4ccbf | ||
|
|
f55bb6dce6 | ||
|
|
e7f49f8330 | ||
|
|
cec9356fc2 | ||
|
|
6d704b382d | ||
|
|
1c5f67111f | ||
|
|
660df4b370 | ||
|
|
19dc5cc6f2 | ||
|
|
67baf90d04 | ||
|
|
07bf41ea6c | ||
|
|
c2cfc9b8db | ||
|
|
7a7a2bc10b | ||
|
|
e3127ed516 | ||
|
|
93a98db43e | ||
|
|
750b55649a | ||
|
|
ca5854a8bd | ||
|
|
712f728a5e | ||
|
|
eb7766a5e5 | ||
|
|
0cabbfde96 | ||
|
|
c5983276e5 | ||
|
|
bb0856c841 | ||
|
|
c21e8e398e | ||
|
|
bba0223efc | ||
|
|
9709bc9c12 | ||
|
|
0d61442c18 | ||
|
|
89fa1393db | ||
|
|
15e86b28ab | ||
|
|
a72d1128dd | ||
|
|
0fd3f6bc0f | ||
|
|
d76ce6c92f | ||
|
|
b6fa993c17 | ||
|
|
81509012b4 | ||
|
|
6db1ab0792 | ||
|
|
513ea0a7cd | ||
|
|
640b5117f9 | ||
|
|
c9d558a353 | ||
|
|
378aff4f35 | ||
|
|
6142c87fa3 | ||
|
|
b01e8e2bc2 | ||
|
|
10724489fc | ||
|
|
3365f111e4 | ||
|
|
b1d22cd05b | ||
|
|
74f2250952 | ||
|
|
36516fd594 | ||
|
|
2dab7a707b | ||
|
|
fc8b26e292 | ||
|
|
5c0964c8fa | ||
|
|
6836c6ee17 | ||
|
|
6adc2dff93 | ||
|
|
63deec88db | ||
|
|
c37e281d69 | ||
|
|
4a0497addc | ||
|
|
e2f5c549aa | ||
|
|
a8195d44ee | ||
|
|
18a505637d | ||
|
|
62475e12c6 | ||
|
|
46888e10d8 | ||
|
|
eb406aada0 | ||
|
|
1582d03fab | ||
|
|
4cfac52d6a | ||
|
|
04ea816365 | ||
|
|
6a298175f0 | ||
|
|
690f0298e6 | ||
|
|
fc93697c28 | ||
|
|
32391f6a5a | ||
|
|
f163eac853 | ||
|
|
ce824f4cab | ||
|
|
df41ad405e | ||
|
|
685cdc929b | ||
|
|
5e65c5149c | ||
|
|
718af10be7 | ||
|
|
b162425e52 | ||
|
|
f1b63420f6 | ||
|
|
95e7329abf | ||
|
|
5d3b7e83f5 | ||
|
|
751e6eb4e7 | ||
|
|
074e31cb2e | ||
|
|
e7e87d03cd | ||
|
|
38669d08ed | ||
|
|
825eafad7f | ||
|
|
f805984c1c |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -2,4 +2,6 @@
|
||||
*.cbz
|
||||
*.cbr
|
||||
.idea
|
||||
build
|
||||
build
|
||||
awkcc
|
||||
.DS_Store
|
||||
|
||||
127
README.md
127
README.md
@@ -1,38 +1,32 @@
|
||||
# KCC
|
||||
|
||||
`KCC` (a.k.a. `KindleComicConverter`) is a Python app to convert comic files or folders to ePub or Panel View Mobipocket.
|
||||
It was initally developed for Kindle but as of version 2.2 it outputs valid ePub 2.0 so _**despite its name, KCC is
|
||||
`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
|
||||
actually a comic 2 epub converter that every ereader owner can happily use**_.
|
||||
It also optimizes comic images by:
|
||||
- enhancing contrast
|
||||
- cutting page numbering
|
||||
- cropping white borders
|
||||
- resizing larger images to device's native resolution
|
||||
- quantizing images to device's palette
|
||||
|
||||
It can also optionally optimize images by applying a number of transformations.
|
||||
|
||||
## BINARY RELEASES
|
||||
You can find the latest released binary at the following links:
|
||||
- OS X: [https://dl.dropbox.com/u/16806101/KindleComicConverter_osx_2.6.zip](https://dl.dropbox.com/u/16806101/KindleComicConverter_osx_2.6.zip)
|
||||
- Win64: [https://dl.dropbox.com/u/16806101/KindleComicConverter_win-amd64_2.6.zip](https://dl.dropbox.com/u/16806101/KindleComicConverter_win-amd64_2.6.zip)
|
||||
- Win32: [http://pawelj.vulturis.eu/Shared/KindleComicConverter_win-x86_2.6.zip](http://pawelj.vulturis.eu/Shared/KindleComicConverter_win-x86_2.6.zip) *(thanks to [AcidWeb](https://github.com/AcidWeb))*
|
||||
- Linux: just download sourcecode and launch `python kcc.py` *(provided you have Python and Pillow installed)*
|
||||
- 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.8.zip](https://dl.dropbox.com/u/16806101/KindleComicConverter_win-amd64_2.7.zip)
|
||||
- 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)*
|
||||
|
||||
## INPUT FORMATS
|
||||
`kcc` can understand and convert, at the moment, the following file types:
|
||||
`kcc` can understand and convert, at the moment, the following file types:
|
||||
- PNG, JPG
|
||||
- Folders
|
||||
- CBZ, ZIP
|
||||
- CBR, RAR *(with `unrar` executable)*
|
||||
- folders
|
||||
- PDF *(extracting only contained JPG images)*
|
||||
|
||||
~~For now the script does not understand folder depth, so it will work on flat folders/archives only.~~
|
||||
As of v. 1.50, KCC supports subfolders!
|
||||
- CBR, RAR *(With `unrar` executable)*
|
||||
- PDF *(Extracting only contained JPG images)*
|
||||
|
||||
## OPTIONAL REQUIREMENTS
|
||||
- `kindlegen` in /usr/local/bin/ *(for .mobi generation)*
|
||||
- [unrar](http://www.rarlab.com/download.htm) *(for CBR support)*
|
||||
- `kindlegen` v2.7+ in a directory reachable by your PATH or in KCC directory *(For .mobi generation)*
|
||||
- [unrar](http://www.rarlab.com/download.htm) *(For CBR support)*
|
||||
|
||||
### for compiling/running from source:
|
||||
- Python 2.7+ (included in MacOS and Linux, follow the [official documentation](http://www.python.org/getit/windows/) to install on Windows)
|
||||
### For compiling/running from source:
|
||||
- Python 2.7+ (Included in MacOS and Linux, follow the [official documentation](http://www.python.org/getit/windows/) to install on Windows)
|
||||
- [Pillow](http://pypi.python.org/pypi/Pillow/) for comic optimizations like split double pages, resize to optimal resolution, improve contrast and palette, etc.
|
||||
Please refer to official documentation for installing into your system.
|
||||
|
||||
@@ -47,40 +41,30 @@ Conversion being done, you should find an .epub and a .mobi files alongside the
|
||||
### Standalone `comic2ebook.py` usage:
|
||||
|
||||
```
|
||||
comic2ebook.py [options] comic_file|comic_folder
|
||||
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, K4, KDX,
|
||||
KDXG or KHD) [default=KHD]
|
||||
-t TITLE, --title=TITLE
|
||||
Comic title [default=filename]
|
||||
-m, --manga-style 'Manga style' (right-to-left reading and splitting)
|
||||
[default=False]
|
||||
-v, --verbose Verbose output [default=False]
|
||||
--no-image-processing
|
||||
Do not apply image preprocessing (page splitting and
|
||||
optimizations) [default=True]
|
||||
--upscale-images Resize images smaller than device's resolution
|
||||
[default=False]
|
||||
--stretch-images Stretch images to device's resolution [default=False]
|
||||
--black-borders Use black borders (instead of white ones) when not
|
||||
stretching and ratio is not like the device's one
|
||||
[default=False]
|
||||
--no-cut-page-numbers
|
||||
Do not try to cut page numbering on images
|
||||
[default=True]
|
||||
--rotate Disable page spliting. Instead rotate images
|
||||
[default=False]
|
||||
-o OUTPUT, --output=OUTPUT
|
||||
Output directory or file for generated ePub
|
||||
```
|
||||
Usage: comic2ebook.py [options] comic_file|comic_folder
|
||||
|
||||
The script takes care of creating an *.epub* from your archive/folder, then:
|
||||
1. Run `Kindlegen` on the generated *.epub*. Depending on how many images you have, this may take awhile. Once completed, the `.mobi` file should be in the directory.
|
||||
2. (optionally) remove the SRCS record to reduce the `.mobi` filesize in half. You can use [Kindlestrip](http://www.mobileread.com/forums/showthread.php?t=96903).
|
||||
3. Copy the `.mobi` file to your Kindle!
|
||||
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]
|
||||
-t TITLE, --title=TITLE
|
||||
Comic title [Default=filename]
|
||||
-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]
|
||||
--forcepng Create PNG files instead JPEG (For non-Kindle devices) [Default=False]
|
||||
--gamma=GAMMA Apply gamma correction to linearize the image [Default=Auto]
|
||||
--upscale Resize images smaller than device's resolution [Default=False]
|
||||
--stretch Stretch images to device's resolution [Default=False]
|
||||
--blackborders Use black borders instead of white ones when not stretching and ratio is not like the device's one [Default=False]
|
||||
--rotate Rotate landscape pages instead of splitting them [Default=False]
|
||||
--nosplitrotate Disable splitting and rotation [Default=False]
|
||||
--nocutpagenumbers Do not try to cut page numbering on images [Default=True]
|
||||
-o OUTPUT, --output=OUTPUT
|
||||
Output generated EPUB to specified directory or file
|
||||
-v, --verbose Verbose output [Default=False]
|
||||
```
|
||||
|
||||
## CREDITS
|
||||
KCC is made by [Ciro Mattia Gonano](http://github.com/ciromattia) and [Paweł Jastrzębski](http://github.com/AcidWeb)
|
||||
@@ -94,10 +78,7 @@ The app relies and includes the following scripts/binaries:
|
||||
- `rarfile.py` script © 2005-2011 **Marko Kreen** <markokr@gmail.com>, released with ISC License
|
||||
- the icon is by **Nikolay Verin** ([http://ncrow.deviantart.com/](http://ncrow.deviantart.com/)) and released under [CC Attribution-NonCommercial-ShareAlike 3.0 Unported](http://creativecommons.org/licenses/by-nc-sa/3.0/) License
|
||||
- `image.py` class from **Alex Yatskov**'s [Mangle](http://foosoft.net/mangle/) with subsequent [proDOOMman](https://github.com/proDOOMman/Mangle)'s and [Birua](https://github.com/Birua/Mangle)'s patches
|
||||
|
||||
Also, for .mobi generation you need to have `kindlegen` v2.7+ (with KF8 support) which is downloadable from Amazon website
|
||||
and installed in a directory reachable by your PATH (e.g. `/usr/local/bin/` or `C:\Windows\`)
|
||||
|
||||
- `magic.py` from [python-magic](https://github.com/ahupp/python-magic) library
|
||||
|
||||
## CHANGELOG
|
||||
- 1.00: Initial version
|
||||
@@ -120,12 +101,30 @@ and installed in a directory reachable by your PATH (e.g. `/usr/local/bin/` or `
|
||||
- 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.
|
||||
Added --output option to customize ePub output dir/file.
|
||||
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)
|
||||
|
||||
|
||||
## COPYRIGHT
|
||||
|
||||
Copyright (c) 2012-2013 Ciro Mattia Gonano with further contributions by Paweł Jastrzębski.
|
||||
KCC is released under ISC LICENSE; see LICENSE.txt for further details.
|
||||
KCC is released under ISC LICENSE; see LICENSE.txt for further details.
|
||||
|
||||
BIN
comic2ebook.ico
Normal file
BIN
comic2ebook.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 345 KiB |
15
kcc.py
15
kcc.py
@@ -16,8 +16,8 @@
|
||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
# PERFORMANCE OF THIS SOFTWARE.
|
||||
#
|
||||
__version__ = '2.6'
|
||||
__license__ = 'ISC'
|
||||
__version__ = '2.8'
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
@@ -26,9 +26,14 @@ from kcc import gui
|
||||
from sys import platform
|
||||
import os
|
||||
|
||||
root = Tk()
|
||||
root.resizable(width=False, height=False)
|
||||
root.config(padx=5, pady=5, takefocus=True)
|
||||
root.title("Kindle Comic Converter v" + __version__)
|
||||
#root.wm_attributes("-topmost", 1)
|
||||
if platform == 'darwin':
|
||||
os.environ['PATH'] = '/usr/local/bin:' + os.environ['PATH']
|
||||
root = Tk()
|
||||
app = gui.MainWindow(master=root,title="Kindle Comic Converter v" + __version__)
|
||||
root.tkraise()
|
||||
elif platform == 'win32':
|
||||
root.iconbitmap(default='comic2ebook.ico')
|
||||
gui.MainWindow(master=root)
|
||||
root.mainloop()
|
||||
|
||||
@@ -19,56 +19,58 @@ __copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import os
|
||||
import zipfile
|
||||
import rarfile
|
||||
|
||||
|
||||
class CBxArchive:
|
||||
def __init__(self, origFileName):
|
||||
self.cbxexts = ['.zip', '.cbz', '.rar', '.cbr']
|
||||
self.origFileName = origFileName
|
||||
self.filename = os.path.splitext(origFileName)
|
||||
if zipfile.is_zipfile(origFileName):
|
||||
self.compressor = 'zip'
|
||||
elif rarfile.is_rarfile(origFileName):
|
||||
self.compressor = 'rar'
|
||||
else:
|
||||
self.compressor = None
|
||||
|
||||
def isCbxFile(self):
|
||||
return self.filename[1].lower() in self.cbxexts
|
||||
return self.compressor is not None
|
||||
|
||||
def extractCBZ(self, targetdir):
|
||||
try:
|
||||
from zipfile import ZipFile
|
||||
except ImportError:
|
||||
self.cbzFile = None
|
||||
cbzFile = ZipFile(self.origFileName)
|
||||
cbzFile = zipfile.ZipFile(self.origFileName)
|
||||
filelist = []
|
||||
for f in cbzFile.namelist():
|
||||
if f.startswith('__MACOSX') or f.endswith('.DS_Store'):
|
||||
pass # skip MacOS special files
|
||||
pass # skip MacOS special files
|
||||
elif f.endswith('/'):
|
||||
try:
|
||||
os.makedirs(os.path.join(targetdir, f))
|
||||
except:
|
||||
pass # the dir exists so we are going to extract the images only.
|
||||
else:
|
||||
cbzFile.extract(f, targetdir)
|
||||
filelist.append(f)
|
||||
cbzFile.extractall(targetdir, filelist)
|
||||
|
||||
def extractCBR(self, targetdir):
|
||||
try:
|
||||
import rarfile
|
||||
except ImportError:
|
||||
self.cbrFile = None
|
||||
return
|
||||
cbrFile = rarfile.RarFile(self.origFileName)
|
||||
filelist = []
|
||||
for f in cbrFile.namelist():
|
||||
if f.startswith('__MACOSX') or f.endswith('.DS_Store'):
|
||||
pass # skip MacOS special files
|
||||
elif f.endswith('/'):
|
||||
try:
|
||||
os.makedirs(os.path.join(targetdir,f))
|
||||
os.makedirs(os.path.join(targetdir, f))
|
||||
except:
|
||||
pass # the dir exists so we are going to extract the images only.
|
||||
else:
|
||||
cbrFile.extract(f, targetdir)
|
||||
filelist.append(f)
|
||||
cbrFile.extractall(targetdir, filelist)
|
||||
|
||||
def extract(self, targetdir):
|
||||
if '.cbr' == self.filename[1].lower() or '.rar' == self.filename[1].lower():
|
||||
print "\n" + targetdir + "\n"
|
||||
if self.compressor == 'rar':
|
||||
self.extractCBR(targetdir)
|
||||
elif '.cbz' == self.filename[1].lower() or '.zip' == self.filename[1].lower():
|
||||
elif self.compressor == 'zip':
|
||||
self.extractCBZ(targetdir)
|
||||
adir = os.listdir(targetdir)
|
||||
if len(adir) == 1 and os.path.isdir(os.path.join(targetdir, adir[0])):
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
# PERFORMANCE OF THIS SOFTWARE.
|
||||
#
|
||||
__version__ = '2.6'
|
||||
__version__ = '2.8'
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
@@ -60,23 +60,49 @@ def buildHTML(path, imgfile):
|
||||
"<head>\n",
|
||||
"<title>", filename[0], "</title>\n",
|
||||
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n",
|
||||
"<link href=\"", "../" * (backref - 1),
|
||||
"style.css\" type=\"text/css\" rel=\"stylesheet\"/>\n",
|
||||
"</head>\n",
|
||||
"<body>\n",
|
||||
"<div class=\"fs\">\n",
|
||||
"<div><img src=\"", "../" * backref, "Images/", postfix, imgfile, "\" alt=\"",
|
||||
imgfile, "\" class=\"singlePage\"/></div>\n",
|
||||
#"<div id=\"", filename[0], "-1\">\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>"
|
||||
imgfile, "\" class=\"singlePage\"/></div>\n"
|
||||
])
|
||||
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()
|
||||
return path, imgfile
|
||||
|
||||
@@ -117,19 +143,19 @@ def buildNCX(dstdir, title, chapters):
|
||||
folder = chapter[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
|
||||
title = os.path.basename(folder)
|
||||
filename = getImageFileName(os.path.join(folder, chapter[1]))
|
||||
f.write("<navPoint id=\"" + folder.replace('/', '_') + "\"><navLabel><text>" + title
|
||||
f.write("<navPoint id=\"" + folder.replace('/', '_').replace('\\', '_') + "\"><navLabel><text>" + title
|
||||
+ "</text></navLabel><content src=\"" + filename[0] + ".html\"/></navPoint>\n")
|
||||
f.write("</navMap>\n</ncx>")
|
||||
f.close()
|
||||
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')
|
||||
# read the first file resolution
|
||||
profilelabel, deviceres, palette = image.ProfileData.Profiles[profile]
|
||||
profilelabel, deviceres, palette, gamma, panelviewsize = image.ProfileData.Profiles[profile]
|
||||
imgres = str(deviceres[0]) + "x" + str(deviceres[1])
|
||||
if righttoleft:
|
||||
if options.righttoleft:
|
||||
writingmode = "horizontal-rl"
|
||||
facing = "right"
|
||||
facing1 = "right"
|
||||
@@ -155,16 +181,19 @@ def buildOPF(profile, dstdir, title, filelist, cover=None, righttoleft=False):
|
||||
"<meta name=\"book-type\" content=\"comic\"/>\n",
|
||||
"<meta name=\"zero-gutter\" content=\"true\"/>\n",
|
||||
"<meta name=\"zero-margin\" content=\"true\"/>\n",
|
||||
"<meta name=\"fixed-layout\" content=\"true\"/>\n",
|
||||
"<meta name=\"orientation-lock\" content=\"none\"/>\n",
|
||||
"<meta name=\"original-resolution\" content=\"", imgres, "\"/>\n",
|
||||
"<meta name=\"fixed-layout\" content=\"true\"/>\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=\"rendition:layout\" content=\"pre-paginated\"/>\n",
|
||||
"<meta name=\"rendition:orientation\" content=\"auto\"/>\n",
|
||||
"</metadata>\n<manifest>\n<item id=\"ncx\" href=\"toc.ncx\" ",
|
||||
"media-type=\"application/x-dtbncx+xml\"/>\n"
|
||||
])
|
||||
# set cover
|
||||
"media-type=\"application/x-dtbncx+xml\"/>\n"])
|
||||
if cover is not None:
|
||||
filename = getImageFileName(cover.replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\'))
|
||||
if '.png' == filename[1]:
|
||||
@@ -176,43 +205,57 @@ def buildOPF(profile, dstdir, title, filelist, cover=None, righttoleft=False):
|
||||
for path in filelist:
|
||||
folder = path[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
|
||||
filename = getImageFileName(path[1])
|
||||
uniqueid = os.path.join(folder, filename[0]).replace('/', '_')
|
||||
uniqueid = os.path.join(folder, filename[0]).replace('/', '_').replace('\\', '_')
|
||||
reflist.append(uniqueid)
|
||||
f.write("<item id=\"page_" + uniqueid + "\" href=\""
|
||||
+ os.path.join(folder.replace('Images', 'Text'), filename[0])
|
||||
+ folder.replace('Images', 'Text') + "/" + filename[0]
|
||||
+ ".html\" media-type=\"application/xhtml+xml\"/>\n")
|
||||
if '.png' == filename[1]:
|
||||
mt = 'image/png'
|
||||
else:
|
||||
mt = 'image/jpeg'
|
||||
f.write("<item id=\"img_" + uniqueid + "\" href=\"" + os.path.join(folder, path[1]) + "\" media-type=\""
|
||||
f.write("<item id=\"img_" + uniqueid + "\" href=\"" + folder + "/" + path[1] + "\" media-type=\""
|
||||
+ mt + "\"/>\n")
|
||||
if (options.profile == 'K4' or options.profile == 'KHD') and splittedSomething:
|
||||
f.write("<item id=\"blank-page\" href=\""
|
||||
+ os.path.join('Text', 'blank.html')
|
||||
+ "\" media-type=\"application/xhtml+xml\"/>\n")
|
||||
if options.landscapemode and splitCount > 0:
|
||||
splitCountUsed = 1
|
||||
while splitCountUsed <= splitCount:
|
||||
f.write("<item id=\"blank-page" + str(splitCountUsed) +
|
||||
"\" href=\"Text/blank.html\" media-type=\"application/xhtml+xml\"/>\n")
|
||||
splitCountUsed += 1
|
||||
f.write("<item id=\"css\" href=\"Text/style.css\" media-type=\"text/css\"/>\n")
|
||||
f.write("</manifest>\n<spine toc=\"ncx\">\n")
|
||||
splitCountUsed = 1
|
||||
for entry in reflist:
|
||||
if entry.endswith("-1"):
|
||||
if (righttoleft and facing == 'left') or (not righttoleft and facing == 'right') and \
|
||||
(options.profile == 'K4' or options.profile == 'KHD'):
|
||||
f.write("<itemref idref=\"blank-page\" properties=\"layout-blank\"/>\n")
|
||||
f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing1 + "\"/>\n")
|
||||
# noinspection PyRedundantParentheses
|
||||
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")
|
||||
splitCountUsed += 1
|
||||
if options.landscapemode:
|
||||
f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing1 + "\"/>\n")
|
||||
else:
|
||||
f.write("<itemref idref=\"page_" + entry + "\"/>\n")
|
||||
elif entry.endswith("-2"):
|
||||
f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing2 + "\"/>\n")
|
||||
if righttoleft:
|
||||
if options.landscapemode:
|
||||
f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing2 + "\"/>\n")
|
||||
else:
|
||||
f.write("<itemref idref=\"page_" + entry + "\"/>\n")
|
||||
if options.righttoleft:
|
||||
facing = "right"
|
||||
else:
|
||||
facing = "left"
|
||||
else:
|
||||
f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing + "\"/>\n")
|
||||
if options.landscapemode:
|
||||
f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing + "\"/>\n")
|
||||
else:
|
||||
f.write("<itemref idref=\"page_" + entry + "\"/>\n")
|
||||
if facing == 'right':
|
||||
facing = 'left'
|
||||
else:
|
||||
facing = 'right'
|
||||
f.write("</spine>\n<guide>\n</guide>\n</package>\n")
|
||||
f.close()
|
||||
# finish with standard ePub folders
|
||||
os.mkdir(os.path.join(dstdir, 'META-INF'))
|
||||
f = open(os.path.join(dstdir, 'mimetype'), 'w')
|
||||
f.write('application/epub+zip')
|
||||
@@ -248,18 +291,22 @@ def isInFilelist(filename, filelist):
|
||||
|
||||
|
||||
def applyImgOptimization(img, isSplit=False, toRight=False):
|
||||
img.optimizeImage()
|
||||
img.cropWhiteSpace(10.0)
|
||||
if options.cutpagenumbers:
|
||||
img.cutPageNumber()
|
||||
img.resizeImage(options.upscale, options.stretch, options.black_borders, isSplit, toRight)
|
||||
img.quantizeImage()
|
||||
img.resizeImage(options.upscale, options.stretch, options.black_borders, isSplit, toRight, options.landscapemode,
|
||||
options.nopanelviewhq)
|
||||
img.optimizeImage(options.gamma)
|
||||
if options.forcepng:
|
||||
img.quantizeImage()
|
||||
|
||||
|
||||
def dirImgProcess(path):
|
||||
global options
|
||||
global splittedSomething
|
||||
splittedSomething = False
|
||||
global options, splitCount
|
||||
if options.righttoleft:
|
||||
facing = "right"
|
||||
else:
|
||||
facing = "left"
|
||||
|
||||
for (dirpath, dirnames, filenames) in os.walk(path):
|
||||
for afile in filenames:
|
||||
@@ -269,26 +316,38 @@ def dirImgProcess(path):
|
||||
else:
|
||||
print ".",
|
||||
img = image.ComicPage(os.path.join(dirpath, afile), options.profile)
|
||||
split = img.splitPage(dirpath, options.righttoleft, options.rotate)
|
||||
if options.nosplitrotate:
|
||||
split = None
|
||||
else:
|
||||
split = img.splitPage(dirpath, options.righttoleft, options.rotate)
|
||||
if split is not None:
|
||||
splittedSomething = True
|
||||
if options.verbose:
|
||||
print "Splitted " + afile
|
||||
if options.righttoleft:
|
||||
toRight1 = False
|
||||
toRight2 = True
|
||||
if facing == "left":
|
||||
splitCount += 1
|
||||
facing = "right"
|
||||
else:
|
||||
toRight1 = True
|
||||
toRight2 = False
|
||||
if facing == "right":
|
||||
splitCount += 1
|
||||
facing = "left"
|
||||
img0 = image.ComicPage(split[0], options.profile)
|
||||
applyImgOptimization(img0, True, toRight1)
|
||||
img0.saveToDir(dirpath)
|
||||
img0.saveToDir(dirpath, options.forcepng)
|
||||
img1 = image.ComicPage(split[1], options.profile)
|
||||
applyImgOptimization(img1, True, toRight2)
|
||||
img1.saveToDir(dirpath)
|
||||
img1.saveToDir(dirpath, options.forcepng)
|
||||
else:
|
||||
if facing == "right":
|
||||
facing = "left"
|
||||
else:
|
||||
facing = "right"
|
||||
applyImgOptimization(img)
|
||||
img.saveToDir(dirpath)
|
||||
img.saveToDir(dirpath, options.forcepng)
|
||||
|
||||
|
||||
def genEpubStruct(path):
|
||||
@@ -296,13 +355,133 @@ def genEpubStruct(path):
|
||||
filelist = []
|
||||
chapterlist = []
|
||||
cover = None
|
||||
_, deviceres, _, _, panelviewsize = image.ProfileData.Profiles[options.profile]
|
||||
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.
|
||||
f.writelines(["@page {\n",
|
||||
"margin-bottom: 0;\n",
|
||||
"margin-top: 0\n",
|
||||
"}\n",
|
||||
"body {\n",
|
||||
"display: block;\n",
|
||||
"margin-bottom: 0;\n",
|
||||
"margin-left: 0;\n",
|
||||
"margin-right: 0;\n",
|
||||
"margin-top: 0;\n",
|
||||
"padding-bottom: 0;\n",
|
||||
"padding-left: 0;\n",
|
||||
"padding-right: 0;\n",
|
||||
"padding-top: 0;\n",
|
||||
"text-align: left\n",
|
||||
"}\n",
|
||||
"div.fs {\n",
|
||||
"height: ", str(deviceres[1]), "px;\n",
|
||||
"width: ", str(deviceres[0]), "px;\n",
|
||||
"position: relative;\n",
|
||||
"display: block;\n",
|
||||
"text-align: center\n",
|
||||
"}\n",
|
||||
"div.fs a {\n",
|
||||
"display: block;\n",
|
||||
"width : 100%;\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()
|
||||
for (dirpath, dirnames, filenames) in os.walk(os.path.join(path, 'OEBPS', 'Images')):
|
||||
chapter = False
|
||||
for afile in filenames:
|
||||
filename = getImageFileName(afile)
|
||||
if filename is not None:
|
||||
# put credits at the end
|
||||
if "credit" in afile.lower():
|
||||
os.rename(os.path.join(dirpath, afile), os.path.join(dirpath, 'ZZZ999_' + afile))
|
||||
afile = 'ZZZ999_' + afile
|
||||
@@ -322,14 +501,13 @@ def genEpubStruct(path):
|
||||
convert = lambda text: int(text) if text.isdigit() else text
|
||||
alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)]
|
||||
filelist.sort(key=lambda name: (alphanum_key(name[0].lower()), alphanum_key(name[1].lower())))
|
||||
buildOPF(options.profile, path, options.title, filelist, cover, options.righttoleft)
|
||||
if (options.profile == 'K4' or options.profile == 'KHD') and splittedSomething:
|
||||
buildOPF(options.profile, path, options.title, filelist, cover)
|
||||
if options.landscapemode and splitCount > 0:
|
||||
filelist.append(buildBlankHTML(os.path.join(path, 'OEBPS', 'Text')))
|
||||
|
||||
|
||||
def getWorkFolder(afile):
|
||||
workdir = tempfile.mkdtemp()
|
||||
fname = os.path.splitext(afile)
|
||||
if os.path.isdir(afile):
|
||||
try:
|
||||
import shutil
|
||||
@@ -338,13 +516,18 @@ def getWorkFolder(afile):
|
||||
path = workdir
|
||||
except OSError:
|
||||
raise
|
||||
elif fname[1].lower() == '.pdf':
|
||||
elif afile.lower().endswith('.pdf'):
|
||||
pdf = pdfjpgextract.PdfJpgExtract(afile)
|
||||
path = pdf.extract()
|
||||
else:
|
||||
cbx = cbxarchive.CBxArchive(afile)
|
||||
if cbx.isCbxFile():
|
||||
path = cbx.extract(workdir)
|
||||
try:
|
||||
path = cbx.extract(workdir)
|
||||
except OSError:
|
||||
print 'Unrar not found, please download from ' + \
|
||||
'http://www.rarlab.com/download.htm and put into your PATH.'
|
||||
sys.exit(21)
|
||||
else:
|
||||
raise TypeError
|
||||
move(path, path + "_temp")
|
||||
@@ -358,49 +541,59 @@ def Copyright():
|
||||
|
||||
|
||||
def Usage():
|
||||
print "Generates HTML, NCX and OPF for a Comic ebook from a bunch of images"
|
||||
print "Optimized for creating Mobipockets to be read into Kindle Paperwhite"
|
||||
print "Generates HTML, NCX and OPF for a Comic ebook from a bunch of images."
|
||||
print "Optimized for creating MOBI files to be read on Kindle Paperwhite."
|
||||
parser.print_help()
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
global parser, options, epub_path
|
||||
global parser, options, epub_path, splitCount
|
||||
usage = "Usage: %prog [options] comic_file|comic_folder"
|
||||
parser = OptionParser(usage=usage, version=__version__)
|
||||
parser.add_option("-p", "--profile", action="store", dest="profile", default="KHD",
|
||||
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",
|
||||
help="Comic title [default=filename]")
|
||||
help="Comic title [Default=filename]")
|
||||
parser.add_option("-m", "--manga-style", action="store_true", dest="righttoleft", default=False,
|
||||
help="'Manga style' (right-to-left reading and splitting) [default=False]")
|
||||
parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False,
|
||||
help="Verbose output [default=False]")
|
||||
parser.add_option("--no-image-processing", action="store_false", dest="imgproc", default=True,
|
||||
help="Do not apply image preprocessing (page splitting and optimizations) [default=True]")
|
||||
parser.add_option("--upscale-images", action="store_true", dest="upscale", default=False,
|
||||
help="Resize images smaller than device's resolution [default=False]")
|
||||
parser.add_option("--stretch-images", action="store_true", dest="stretch", default=False,
|
||||
help="Stretch images to device's resolution [default=False]")
|
||||
parser.add_option("--black-borders", action="store_true", dest="black_borders", default=False,
|
||||
help="Use black borders (instead of white ones) when not stretching and ratio "
|
||||
+ "is not like the device's one [default=False]")
|
||||
parser.add_option("--no-cut-page-numbers", action="store_false", dest="cutpagenumbers", default=True,
|
||||
help="Do not try to cut page numbering on images [default=True]")
|
||||
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,
|
||||
help="Do not apply image preprocessing (Page splitting and optimizations) [Default=True]")
|
||||
parser.add_option("--forcepng", action="store_true", dest="forcepng", 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",
|
||||
help="Apply gamma correction to linearize the image [Default=Auto]")
|
||||
parser.add_option("--upscale", action="store_true", dest="upscale", default=False,
|
||||
help="Resize images smaller than device's resolution [Default=False]")
|
||||
parser.add_option("--stretch", action="store_true", dest="stretch", default=False,
|
||||
help="Stretch images to device's resolution [Default=False]")
|
||||
parser.add_option("--blackborders", action="store_true", dest="black_borders", default=False,
|
||||
help="Use black borders instead of white ones when not stretching and ratio "
|
||||
+ "is not like the device's one [Default=False]")
|
||||
parser.add_option("--rotate", action="store_true", dest="rotate", default=False,
|
||||
help="Rotate landscape pages instead of splitting them [default=False]")
|
||||
help="Rotate landscape pages instead of splitting them [Default=False]")
|
||||
parser.add_option("--nosplitrotate", action="store_true", dest="nosplitrotate", default=False,
|
||||
help="Disable splitting and rotation [Default=False]")
|
||||
parser.add_option("--nocutpagenumbers", action="store_false", dest="cutpagenumbers", default=True,
|
||||
help="Do not try to cut page numbering on images [Default=True]")
|
||||
parser.add_option("-o", "--output", action="store", dest="output", default=None,
|
||||
help="Output directory or file for generated ePub")
|
||||
help="Output generated EPUB to specified directory or file")
|
||||
parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False,
|
||||
help="Verbose output [Default=False]")
|
||||
options, args = parser.parse_args(argv)
|
||||
checkOptions()
|
||||
if len(args) != 1:
|
||||
parser.print_help()
|
||||
return
|
||||
path = getWorkFolder(args[0])
|
||||
if options.title == 'defaulttitle':
|
||||
options.title = os.path.splitext(os.path.basename(args[0]))[0]
|
||||
splitCount = 0
|
||||
if options.imgproc:
|
||||
print "Processing images..."
|
||||
dirImgProcess(path + "/OEBPS/Images/")
|
||||
print "Creating ePub structure..."
|
||||
print "\nCreating ePub structure..."
|
||||
genEpubStruct(path)
|
||||
# actually zip the ePub
|
||||
if options.output is not None:
|
||||
@@ -421,6 +614,22 @@ def main(argv=None):
|
||||
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():
|
||||
global epub_path
|
||||
return epub_path
|
||||
|
||||
249
kcc/gui.py
249
kcc/gui.py
@@ -31,6 +31,7 @@ from subprocess import call
|
||||
import os
|
||||
import shutil
|
||||
import stat
|
||||
import traceback
|
||||
|
||||
|
||||
class MainWindow:
|
||||
@@ -39,25 +40,22 @@ class MainWindow:
|
||||
self.filelist = []
|
||||
self.refresh_list()
|
||||
|
||||
def change_gamma(self):
|
||||
if self.aEntry['state'] == DISABLED:
|
||||
self.aEntry['state'] = NORMAL
|
||||
else:
|
||||
self.aEntry['state'] = DISABLED
|
||||
|
||||
def open_files(self):
|
||||
filetypes = [('all files', '.*'), ('Comic files', ('*.cbr', '*.cbz', '*.zip', '*.rar', '*.pdf'))]
|
||||
f = tkFileDialog.askopenfilenames(title="Choose a file...", filetypes=filetypes)
|
||||
filetypes = [('All files', '.*'), ('Comic files', ('*.cbr', '*.cbz', '*.zip', '*.rar', '*.pdf'))]
|
||||
f = tkFileDialog.askopenfilenames(title="Choose files", filetypes=filetypes)
|
||||
if not isinstance(f, tuple):
|
||||
try:
|
||||
import re
|
||||
f = re.findall('\{(.*?)\}', f)
|
||||
except:
|
||||
import tkMessageBox
|
||||
tkMessageBox.showerror(
|
||||
"Open file",
|
||||
"askopenfilename() returned other than a tuple and no regex module could be found"
|
||||
)
|
||||
sys.exit(1)
|
||||
f = self.master.tk.splitlist(f)
|
||||
self.filelist.extend(f)
|
||||
self.refresh_list()
|
||||
|
||||
def open_folder(self):
|
||||
f = tkFileDialog.askdirectory(title="Choose a folder...")
|
||||
f = tkFileDialog.askdirectory(title="Choose folder:")
|
||||
self.filelist.extend([f])
|
||||
self.refresh_list()
|
||||
|
||||
@@ -67,6 +65,13 @@ class MainWindow:
|
||||
for afile in self.filelist:
|
||||
self.filelocation.insert(END, afile)
|
||||
self.filelocation.config(state=DISABLED)
|
||||
try:
|
||||
if len(self.filelist) > 0:
|
||||
self.submit['state'] = NORMAL
|
||||
else:
|
||||
self.submit['state'] = DISABLED
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def initialize(self):
|
||||
self.filelocation = Listbox(self.master)
|
||||
@@ -74,118 +79,175 @@ class MainWindow:
|
||||
self.refresh_list()
|
||||
|
||||
self.clear_file = Button(self.master, text="Clear files", command=self.clear_files)
|
||||
self.clear_file.grid(row=4, column=0, rowspan=3)
|
||||
self.open_file = Button(self.master, text="Add files...", command=self.open_files)
|
||||
self.open_file.grid(row=4, column=1, rowspan=3)
|
||||
self.open_folder = Button(self.master, text="Add folder...", command=self.open_folder)
|
||||
self.open_folder.grid(row=4, column=2, rowspan=3)
|
||||
self.clear_file.grid(row=4, column=0, sticky=W + E + N + S)
|
||||
self.open_file = Button(self.master, text="Add files", command=self.open_files)
|
||||
self.open_file.grid(row=4, column=1, sticky=W + E + N + S)
|
||||
self.open_folder = Button(self.master, text="Add folder", command=self.open_folder)
|
||||
self.open_folder.grid(row=4, column=2, sticky=W + E + N + S)
|
||||
|
||||
self.profile = StringVar()
|
||||
profiles = sorted(ProfileData.ProfileLabels.iterkeys())
|
||||
self.profile.set(profiles[-1])
|
||||
w = apply(OptionMenu, (self.master, self.profile) + tuple(profiles))
|
||||
w.grid(row=1, column=3)
|
||||
w.grid(row=4, column=3, sticky=W + E + N + S)
|
||||
|
||||
self.options = {
|
||||
'epub_only': IntVar(None, 0),
|
||||
'image_preprocess': IntVar(None, 1),
|
||||
'rotate': IntVar(None, 0),
|
||||
'cut_page_numbers': IntVar(None, 1),
|
||||
'mangastyle': IntVar(None, 0),
|
||||
'image_upscale': IntVar(None, 0),
|
||||
'image_stretch': IntVar(None, 0),
|
||||
'black_borders': IntVar(None, 0)
|
||||
'Aepub_only': IntVar(None, 0),
|
||||
'Bmangastyle': IntVar(None, 0),
|
||||
'Cnopanelviewhq': IntVar(None, 0),
|
||||
'Dimage_preprocess': IntVar(None, 0),
|
||||
'Eforcepng': IntVar(None, 0),
|
||||
'Fimage_gamma': DoubleVar(None, 0.0),
|
||||
'Gimage_upscale': IntVar(None, 0),
|
||||
'Himage_stretch': IntVar(None, 0),
|
||||
'Iblack_borders': IntVar(None, 0),
|
||||
'Jrotate': IntVar(None, 0),
|
||||
'Knosplitrotate': IntVar(None, 0),
|
||||
'Lcut_page_numbers': IntVar(None, 0)
|
||||
}
|
||||
self.optionlabels = {
|
||||
'epub_only': "Generate ePub only (does not call 'kindlegen')",
|
||||
'image_preprocess': "Apply image optimizations",
|
||||
'rotate': "Rotate landscape images instead of splitting them",
|
||||
'cut_page_numbers': "Cut page numbers",
|
||||
'mangastyle': "Manga-style (right-to-left reading, applies to reading and splitting)",
|
||||
'image_upscale': "Allow image upscaling",
|
||||
'image_stretch': "Stretch images",
|
||||
'black_borders': "Use black borders"
|
||||
'Aepub_only': "Generate EPUB only",
|
||||
'Bmangastyle': "Manga mode",
|
||||
'Cnopanelviewhq': "Disable high quality Panel View",
|
||||
'Dimage_preprocess': "Disable image optimizations",
|
||||
'Eforcepng': "Create PNG files instead JPEG",
|
||||
'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"
|
||||
}
|
||||
for key in self.options:
|
||||
aCheckButton = Checkbutton(self.master, text=self.optionlabels[key], variable=self.options[key])
|
||||
aCheckButton.grid(column=3, sticky='w')
|
||||
self.progressbar = ttk.Progressbar(orient=HORIZONTAL, length=200, mode='determinate')
|
||||
self.optionsButtons = {}
|
||||
for key in sorted(self.options):
|
||||
if isinstance(self.options[key], IntVar) or isinstance(self.options[key], BooleanVar):
|
||||
self.optionsButtons[key] = Checkbutton(self.master, text=self.optionlabels[key],
|
||||
variable=self.options[key])
|
||||
self.optionsButtons[key].grid(columnspan=4, sticky=W + N + S)
|
||||
elif isinstance(self.options[key], DoubleVar):
|
||||
self.optionsButtons[key] = Checkbutton(self.master, text=self.optionlabels[key],
|
||||
command=self.change_gamma)
|
||||
self.optionsButtons[key].grid(columnspan=4, sticky=W + N + S)
|
||||
self.aEntry = Entry(self.master, textvariable=self.options[key])
|
||||
self.aEntry['state'] = DISABLED
|
||||
self.aEntry.grid(column=3, row=(self.master.grid_size()[1] - 1), sticky=W + N + S)
|
||||
|
||||
self.submit = Button(self.master, text="Execute!", command=self.start_conversion, fg="red")
|
||||
self.submit.grid(column=3)
|
||||
self.progressbar.grid(column=0, columnspan=4, sticky=W + E + N + S)
|
||||
self.submit = Button(self.master, text="CONVERT", command=self.start_conversion, fg="red", state=DISABLED)
|
||||
self.submit.grid(columnspan=4, sticky=W + E + N + S)
|
||||
aLabel = Label(self.master, text="File progress:", anchor=W, justify=LEFT)
|
||||
aLabel.grid(column=0, sticky=E)
|
||||
self.progress_file = ttk.Progressbar(orient=HORIZONTAL, length=200, mode='determinate', maximum=4)
|
||||
self.progress_file.grid(column=1, columnspan=3, row=(self.master.grid_size()[1] - 1), sticky=W + E + N + S)
|
||||
aLabel = Label(self.master, text="Overall progress:", anchor=W, justify=LEFT)
|
||||
aLabel.grid(column=0, sticky=E)
|
||||
self.progress_overall = ttk.Progressbar(orient=HORIZONTAL, length=200, mode='determinate')
|
||||
self.progress_overall.grid(column=1, columnspan=3, row=(self.master.grid_size()[1] - 1), sticky=W + E + N + S)
|
||||
|
||||
self.notelabel = Label(self.master,
|
||||
text="GUI can seem frozen while converting, kindly wait until some message appears!")
|
||||
self.notelabel.grid(column=0, columnspan=4, sticky=W + E + N + S)
|
||||
retcode = call("kindlegen", shell=True)
|
||||
if retcode == 1:
|
||||
self.optionsButtons['Aepub_only'].select()
|
||||
self.optionsButtons['Aepub_only']['state'] = DISABLED
|
||||
|
||||
def start_conversion(self):
|
||||
self.progressbar.start()
|
||||
self.submit['state'] = DISABLED
|
||||
self.master.update()
|
||||
self.convert()
|
||||
self.progressbar.stop()
|
||||
self.submit['state'] = NORMAL
|
||||
self.master.update()
|
||||
|
||||
def convert(self):
|
||||
if len(self.filelist) < 1:
|
||||
tkMessageBox.showwarning('No file selected', "You should really select some files to convert...")
|
||||
tkMessageBox.showwarning('No files selected!', "Please choose files to convert.")
|
||||
return
|
||||
profilekey = ProfileData.ProfileLabels[self.profile.get()]
|
||||
argv = ["-p", profilekey]
|
||||
if self.options['image_preprocess'].get() == 0:
|
||||
argv.append("--no-image-processing")
|
||||
if self.options['rotate'].get() == 1:
|
||||
argv.append("--rotate")
|
||||
if self.options['cut_page_numbers'].get() == 0:
|
||||
argv.append("--no-cut-page-numbers")
|
||||
if self.options['mangastyle'].get() == 1:
|
||||
if self.options['Bmangastyle'].get() == 1:
|
||||
argv.append("-m")
|
||||
if self.options['image_upscale'].get() == 1:
|
||||
argv.append("--upscale-images")
|
||||
if self.options['image_stretch'].get() == 1:
|
||||
argv.append("--stretch-images")
|
||||
if self.options['black_borders'].get() == 1:
|
||||
argv.append("--black-borders")
|
||||
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")
|
||||
if self.options['Himage_stretch'].get() == 1:
|
||||
argv.append("--stretch")
|
||||
if self.options['Iblack_borders'].get() == 1:
|
||||
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
|
||||
left_files = len(self.filelist)
|
||||
filenum = 0
|
||||
self.progress_overall['value'] = 0
|
||||
self.progress_overall['maximum'] = left_files
|
||||
for entry in self.filelist:
|
||||
self.progress_overall['value'] = filenum
|
||||
self.progress_file['value'] = 1
|
||||
self.master.update()
|
||||
filenum += 1
|
||||
subargv = list(argv)
|
||||
try:
|
||||
subargv.append(entry)
|
||||
epub_path = comic2ebook.main(subargv)
|
||||
except Exception, err:
|
||||
tkMessageBox.showerror('Error comic2ebook', "Error on file %s:\n%s" % (subargv[-1], str(err)))
|
||||
errors = True
|
||||
continue
|
||||
if self.options['epub_only'] == 1:
|
||||
continue
|
||||
try:
|
||||
retcode = call("kindlegen \"" + epub_path + "\"", shell=True)
|
||||
if retcode < 0:
|
||||
print >>sys.stderr, "Child was terminated by signal", -retcode
|
||||
else:
|
||||
print >>sys.stderr, "Child returned", retcode
|
||||
except OSError as e:
|
||||
tkMessageBox.showerror('Error kindlegen', "Error on file %s:\n%s" % (epub_path, e))
|
||||
errors = True
|
||||
continue
|
||||
mobifile = epub_path.replace('.epub', '.mobi')
|
||||
try:
|
||||
shutil.move(mobifile, mobifile + '_tostrip')
|
||||
kindlestrip.main((mobifile + '_tostrip', mobifile))
|
||||
os.remove(mobifile + '_tostrip')
|
||||
except Exception, err:
|
||||
tkMessageBox.showerror('Error', "Error on file %s:\n%s" % (mobifile, str(err)))
|
||||
self.progress_file['value'] = 2
|
||||
self.master.update()
|
||||
except Exception as err:
|
||||
type_, value_, traceback_ = sys.exc_info()
|
||||
tkMessageBox.showerror('KCC Error', "Error on file %s:\n%s\nTraceback:\n%s" %
|
||||
(subargv[-1], str(err), traceback.format_tb(traceback_)))
|
||||
errors = True
|
||||
continue
|
||||
if self.options['Aepub_only'].get() == 0:
|
||||
try:
|
||||
if os.path.getsize(epub_path) > 314572800:
|
||||
# do not call kindlegen if source is bigger than 300MB
|
||||
tkMessageBox.showwarning('KindleGen Warning',
|
||||
"ePub file %s is bigger than 300MB, not suitable for kindlegen" %
|
||||
epub_path)
|
||||
continue
|
||||
retcode = call("kindlegen \"" + epub_path + "\"", shell=True)
|
||||
if retcode < 0:
|
||||
print >>sys.stderr, "Child was terminated by signal", -retcode
|
||||
else:
|
||||
print >>sys.stderr, "Child returned", retcode
|
||||
self.progress_file['value'] = 3
|
||||
self.master.update()
|
||||
except OSError as e:
|
||||
tkMessageBox.showerror('KindleGen Error', "Error on file %s:\n%s" % (epub_path, e))
|
||||
errors = True
|
||||
continue
|
||||
mobifile = epub_path.replace('.epub', '.mobi')
|
||||
try:
|
||||
shutil.move(mobifile, mobifile + '_tostrip')
|
||||
kindlestrip.main((mobifile + '_tostrip', mobifile))
|
||||
os.remove(mobifile + '_tostrip')
|
||||
self.progress_file['value'] = 4
|
||||
self.master.update()
|
||||
except Exception, err:
|
||||
tkMessageBox.showerror('KindleStrip Error', "Error on file %s:\n%s" % (mobifile, str(err)))
|
||||
errors = True
|
||||
continue
|
||||
else:
|
||||
self.progress_file['value'] = 4
|
||||
self.master.update()
|
||||
if errors:
|
||||
tkMessageBox.showinfo(
|
||||
"Done",
|
||||
"Conversion finished (some errors have been reported)"
|
||||
)
|
||||
tkMessageBox.showwarning("Done", "Conversion completed with errors.")
|
||||
else:
|
||||
tkMessageBox.showinfo(
|
||||
"Done",
|
||||
"Conversion successfully done!"
|
||||
)
|
||||
tkMessageBox.showinfo("Done", "Conversion successful!")
|
||||
# reset progressbars
|
||||
self.progress_overall['value'] = 0
|
||||
self.progress_file['value'] = 0
|
||||
self.master.update()
|
||||
|
||||
def remove_readonly(self, fn, path):
|
||||
if fn is os.rmdir:
|
||||
@@ -195,8 +257,7 @@ class MainWindow:
|
||||
os.chmod(path, stat.S_IWRITE)
|
||||
os.remove(path)
|
||||
|
||||
def __init__(self, master, title):
|
||||
def __init__(self, master):
|
||||
self.filelist = []
|
||||
self.master = master
|
||||
self.master.title(title)
|
||||
self.initialize()
|
||||
|
||||
207
kcc/image.py
207
kcc/image.py
@@ -20,7 +20,7 @@ __copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import os
|
||||
from PIL import Image, ImageOps, ImageDraw, ImageStat
|
||||
from PIL import Image, ImageOps, ImageStat
|
||||
|
||||
|
||||
class ImageFlags:
|
||||
@@ -77,20 +77,22 @@ class ProfileData:
|
||||
]
|
||||
|
||||
Profiles = {
|
||||
'K1': ("Kindle", (600, 800), Palette4),
|
||||
'K2': ("Kindle 2", (600, 800), Palette15),
|
||||
'K3': ("Kindle 3/Keyboard", (600, 800), Palette16),
|
||||
'K4': ("Kindle 4/NT/Touch", (600, 800), Palette16),
|
||||
'KHD': ("Kindle Paperwhite", (758, 1024), Palette16),
|
||||
'KDX': ("Kindle DX", (824, 1200), Palette15),
|
||||
'KDXG': ("Kindle DXG", (824, 1200), Palette16)
|
||||
'K1': ("Kindle 1", (600, 800), Palette4, 1.8, (900, 1200)),
|
||||
'K2': ("Kindle 2", (600, 800), Palette15, 1.8, (900, 1200)),
|
||||
'K3': ("Kindle Keyboard", (600, 800), Palette16, 1.8, (900, 1200)),
|
||||
'K4NT': ("Kindle Non-Touch", (600, 800), Palette16, 1.8, (900, 1200)),
|
||||
'K4T': ("Kindle Touch", (600, 800), Palette16, 1.8, (900, 1200)),
|
||||
'KHD': ("Kindle Paperwhite", (758, 1024), Palette16, 1.8, (1137, 1536)),
|
||||
'KDX': ("Kindle DX", (824, 1200), Palette15, 1.8, (1236, 1800)),
|
||||
'KDXG': ("Kindle DXG", (824, 1200), Palette16, 1.8, (1236, 1800))
|
||||
}
|
||||
|
||||
ProfileLabels = {
|
||||
"Kindle": 'K1',
|
||||
"Kindle 1": 'K1',
|
||||
"Kindle 2": 'K2',
|
||||
"Kindle 3/Keyboard": 'K3',
|
||||
"Kindle 4/NT/Touch": 'K4',
|
||||
"Kindle 4/Non-Touch": 'K4NT',
|
||||
"Kindle 4/Touch": 'K4T',
|
||||
"Kindle Paperwhite": 'KHD',
|
||||
"Kindle DX": 'KDX',
|
||||
"Kindle DXG": 'KDXG'
|
||||
@@ -101,7 +103,7 @@ class ComicPage:
|
||||
def __init__(self, source, device):
|
||||
try:
|
||||
self.profile = device
|
||||
self.profile_label, self.size, self.palette = ProfileData.Profiles[device]
|
||||
self.profile_label, self.size, self.palette, self.gamma, self.panelviewsize = ProfileData.Profiles[device]
|
||||
except KeyError:
|
||||
raise RuntimeError('Unexpected output device %s' % device)
|
||||
try:
|
||||
@@ -111,18 +113,29 @@ class ComicPage:
|
||||
raise RuntimeError('Cannot read image file %s' % source)
|
||||
self.image = self.image.convert('RGB')
|
||||
|
||||
def saveToDir(self, targetdir):
|
||||
def saveToDir(self, targetdir, forcepng):
|
||||
filename = os.path.basename(self.origFileName)
|
||||
try:
|
||||
self.image = self.image.convert('L') # convert to grayscale
|
||||
self.image.save(os.path.join(targetdir, filename), "JPEG")
|
||||
os.remove(os.path.join(targetdir, filename))
|
||||
if forcepng:
|
||||
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:
|
||||
raise RuntimeError('Cannot write image in directory %s: %s' % (targetdir, e))
|
||||
|
||||
def optimizeImage(self):
|
||||
self.image = ImageOps.autocontrast(self.image)
|
||||
def optimizeImage(self, gamma):
|
||||
if gamma < 0.1:
|
||||
gamma = self.gamma
|
||||
if gamma == 1.0:
|
||||
self.image = ImageOps.autocontrast(self.image)
|
||||
else:
|
||||
self.image = ImageOps.autocontrast(Image.eval(self.image, lambda a: 255 * (a / 255.) ** gamma))
|
||||
|
||||
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
|
||||
if colors < 256:
|
||||
self.palette += self.palette[:3] * (256 - colors)
|
||||
@@ -130,46 +143,48 @@ class ComicPage:
|
||||
palImg.putpalette(self.palette)
|
||||
self.image = self.image.quantize(palette=palImg)
|
||||
|
||||
def resizeImage(self, upscale=False, stretch=False, black_borders=False, isSplit=False, toRight=False):
|
||||
def resizeImage(self, upscale=False, stretch=False, black_borders=False, isSplit=False, toRight=False,
|
||||
landscapeMode=False, noPanelViewHQ=False):
|
||||
method = Image.ANTIALIAS
|
||||
if black_borders:
|
||||
fill = 'black'
|
||||
else:
|
||||
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 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
|
||||
borderh = (self.size[1] - self.image.size[1]) / 2
|
||||
self.image = ImageOps.expand(self.image, border=(borderw, borderh), fill=fill)
|
||||
borderw = (self.size[0] - self.image.size[0]) / 2
|
||||
borderh = (self.size[1] - self.image.size[1]) / 2
|
||||
self.image = ImageOps.expand(self.image, border=(borderw, borderh), fill=fill)
|
||||
return self.image
|
||||
else:
|
||||
method = Image.NEAREST
|
||||
|
||||
method = Image.BILINEAR
|
||||
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
|
||||
|
||||
ratioDev = float(self.size[0]) / float(self.size[1])
|
||||
if (float(self.image.size[0]) / float(self.image.size[1])) < ratioDev:
|
||||
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)
|
||||
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]
|
||||
self.image = ImageOps.expand(self.image, border=(diff / 2, 0), fill=fill)
|
||||
elif (float(self.image.size[0]) / float(self.image.size[1])) > ratioDev:
|
||||
diff = int(self.image.size[0] / ratioDev) - self.image.size[1]
|
||||
self.image = ImageOps.expand(self.image, border=(0, diff / 2), fill=fill)
|
||||
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
|
||||
|
||||
def splitPage(self, targetdir, righttoleft=False, rotate=False):
|
||||
@@ -209,29 +224,6 @@ class ComicPage:
|
||||
else:
|
||||
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):
|
||||
widthImg, heightImg = self.image.size
|
||||
delta = 2
|
||||
@@ -324,37 +316,60 @@ class ComicPage:
|
||||
# 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 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
|
||||
|
||||
@@ -70,3 +70,4 @@ class PdfJpgExtract:
|
||||
|
||||
njpg += 1
|
||||
i = iend
|
||||
return self.path
|
||||
|
||||
@@ -73,7 +73,7 @@ __all__ = ['is_rarfile', 'RarInfo', 'RarFile', 'RarExtFile']
|
||||
## 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 binascii import crc32
|
||||
from tempfile import mkstemp
|
||||
@@ -320,6 +320,8 @@ class RarMemoryError(RarExecError):
|
||||
"""Memory error"""
|
||||
class RarCreateError(RarExecError):
|
||||
"""Create error"""
|
||||
class RarNoFilesError(RarExecError):
|
||||
"""No files that match pattern were found"""
|
||||
class RarUserBreak(RarExecError):
|
||||
"""User stop"""
|
||||
class RarUnknownError(RarExecError):
|
||||
@@ -417,7 +419,7 @@ class RarInfo(object):
|
||||
'header_offset',
|
||||
'salt',
|
||||
'volume_file',
|
||||
)
|
||||
)
|
||||
|
||||
def isdir(self):
|
||||
'''Returns True if the entry is a directory.'''
|
||||
@@ -729,7 +731,7 @@ class RarFile(object):
|
||||
# RAR 2.x does not write RAR_BLOCK_ENDARC
|
||||
if h.flags & RAR_FILE_SPLIT_AFTER:
|
||||
more_vols = 1
|
||||
# RAR 2.x does not set RAR_MAIN_FIRSTVOLUME
|
||||
# RAR 2.x does not set RAR_MAIN_FIRSTVOLUME
|
||||
if volume == 0 and h.flags & RAR_FILE_SPLIT_BEFORE:
|
||||
raise NeedFirstVolume("Need to start from first volume")
|
||||
|
||||
@@ -950,7 +952,7 @@ class RarFile(object):
|
||||
pos += S_COMMENT_HDR.size
|
||||
data = hdata[pos : pos_next]
|
||||
cmt = rar_decompress(ver, meth, data, declen, sflags,
|
||||
crc, self._password)
|
||||
crc, self._password)
|
||||
if not self._crc_check:
|
||||
h.comment = self._decode_comment(cmt)
|
||||
elif crc32(cmt) & 0xFFFF == crc:
|
||||
@@ -1073,7 +1075,7 @@ class RarFile(object):
|
||||
|
||||
# decompress
|
||||
cmt = rar_decompress(inf.extract_version, inf.compress_type, data,
|
||||
inf.file_size, inf.flags, inf.CRC, psw, inf.salt)
|
||||
inf.file_size, inf.flags, inf.CRC, psw, inf.salt)
|
||||
|
||||
# check crc
|
||||
if self._crc_check:
|
||||
@@ -1668,7 +1670,7 @@ def rar_decompress(vers, meth, data, declen=0, flags=0, crc=0, psw=None, salt=No
|
||||
date = 0
|
||||
mode = 0x20
|
||||
fhdr = S_FILE_HDR.pack(len(data), declen, RAR_OS_MSDOS, crc,
|
||||
date, vers, meth, len(fname), mode)
|
||||
date, vers, meth, len(fname), mode)
|
||||
fhdr += fname
|
||||
if flags & RAR_FILE_SALT:
|
||||
if not salt:
|
||||
@@ -1757,8 +1759,15 @@ def custom_popen(cmd):
|
||||
creationflags = 0x08000000 # CREATE_NO_WINDOW
|
||||
|
||||
# run command
|
||||
p = Popen(cmd, bufsize = 0, stdout = PIPE, stdin = PIPE, stderr = STDOUT,
|
||||
creationflags = creationflags)
|
||||
try:
|
||||
p = Popen(cmd, bufsize = 0,
|
||||
stdout = PIPE, stdin = PIPE, stderr = STDOUT,
|
||||
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
|
||||
|
||||
def check_returncode(p, out):
|
||||
@@ -1770,9 +1779,9 @@ def check_returncode(p, out):
|
||||
|
||||
# map return code to exception class
|
||||
errmap = [None,
|
||||
RarWarning, RarFatalError, RarCRCError, RarLockedArchiveError,
|
||||
RarWriteError, RarOpenError, RarUserError, RarMemoryError,
|
||||
RarCreateError] # codes from rar.txt
|
||||
RarWarning, RarFatalError, RarCRCError, RarLockedArchiveError,
|
||||
RarWriteError, RarOpenError, RarUserError, RarMemoryError,
|
||||
RarCreateError, RarNoFilesError] # codes from rar.txt
|
||||
if code > 0 and code < len(errmap):
|
||||
exc = errmap[code]
|
||||
elif code == 255:
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleAllowMixedLocalizations</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>*</string>
|
||||
</array>
|
||||
<key>CFBundleTypeOSTypes</key>
|
||||
<array>
|
||||
<string>****</string>
|
||||
</array>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>droplet</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>KindleComicConverter 2.0, Written 2012 by Ciro Mattia Gonano</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>droplet</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.github.ciromattia.kcc</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>KindleComicConverter 1.20</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>dplt</string>
|
||||
<key>LSMinimumSystemVersionByArchitecture</key>
|
||||
<dict>
|
||||
<key>x86_64</key>
|
||||
<string>10.6</string>
|
||||
</dict>
|
||||
<key>LSRequiresCarbon</key>
|
||||
<true/>
|
||||
<key>WindowState</key>
|
||||
<dict>
|
||||
<key>dividerCollapsed</key>
|
||||
<true/>
|
||||
<key>eventLogLevel</key>
|
||||
<integer>-1</integer>
|
||||
<key>name</key>
|
||||
<string>ScriptWindowState</string>
|
||||
<key>positionOfDivider</key>
|
||||
<real>568</real>
|
||||
<key>savedFrame</key>
|
||||
<string>144 338 889 690 0 0 1680 1028 </string>
|
||||
<key>selectedTabView</key>
|
||||
<string>event log</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
Binary file not shown.
@@ -1,22 +0,0 @@
|
||||
{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340
|
||||
{\fonttbl\f0\fnil\fcharset0 Verdana;}
|
||||
{\colortbl;\red255\green255\blue255;\red76\green78\blue78;}
|
||||
\pard\tx576\tx1152\tx1728\tx2304\tx2880\tx3456\tx4032\tx4608\tx5184\tx5760\tx6337\tx6913\tx7489\tx8065\tx8641\tx9217\tx9793\tx10369\tx10945\tx11521\tx12097\tx12674\tx13250\tx13826\tx14402\tx14978\tx15554\tx16130\tx16706\tx17282\tx17858\tx18435\tx19011\tx19587\tx20163\tx20739\tx21315\tx21891\tx22467\tx23043\tx23619\tx24195\tx24772\tx25348\tx25924\tx26500\tx27076\tx27652\tx28228\tx28804\tx29380\tx29956\tx30532\tx31109\tx31685\tx32261\tx32837\tx33413\tx33989\tx34565\tx35141\tx35717\tx36293\tx36870\tx37446\tx38022\tx38598\tx39174\tx39750\tx40326\tx40902\tx41478\tx42054\tx42630\tx43207\tx43783\tx44359\tx44935\tx45511\tx46087\tx46663\tx47239\tx47815\tx48391\tx48967\tx49544\tx50120\tx50696\tx51272\tx51848\tx52424\tx53000\tx53576\tx54152\tx54728\tx55305\tx55881\tx56457\tx57033\tx57609\li785\fi-786\pardirnatural
|
||||
|
||||
\f0\fs24 \cf2 \CocoaLigature0 Copyright (c) 2012 Ciro Mattia Gonano <ciromattia@gmail.com>\
|
||||
\
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.\
|
||||
\
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\
|
||||
\
|
||||
This script heavily relies on KindleStrip (C) by Paul Durrant and released in public domain (http://www.mobileread.com/forums/showthread.php?t=96903)\
|
||||
Also, you need to have kindlegen v2.7 (with KF8 support) which is downloadable from Amazon website.\
|
||||
\
|
||||
Changelog:\
|
||||
1.0: first release\
|
||||
1.10: add CBZ/CBR support to comic2ebook.py\
|
||||
1.11: add CBZ/CBR support to KindleComicConverter\
|
||||
1.2: added image page splitting and optimizations\
|
||||
\
|
||||
Todo:\
|
||||
- bundle a script to manipulate images (to get rid of Mangle/E-nki/whatsoever)}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 362 B |
47
setup.py
47
setup.py
@@ -1,33 +1,36 @@
|
||||
"""
|
||||
py2app/py2exe build script for MyApplication.
|
||||
py2app/cx_Freeze build script for KCC.
|
||||
|
||||
Will automatically ensure that all build prerequisites are available
|
||||
via ez_setup
|
||||
Will automatically ensure that all build prerequisites are available via ez_setup
|
||||
|
||||
Usage (Mac OS X):
|
||||
python setup.py py2app
|
||||
|
||||
Usage (Windows):
|
||||
python setup.py py2exe
|
||||
python setup.py build
|
||||
"""
|
||||
import ez_setup
|
||||
ez_setup.use_setuptools()
|
||||
from ez_setup import use_setuptools
|
||||
use_setuptools()
|
||||
|
||||
import sys
|
||||
from setuptools import setup
|
||||
|
||||
NAME = 'KindleComicConverter'
|
||||
VERSION = "2.6"
|
||||
mainscript = 'kcc.py'
|
||||
NAME = "KindleComicConverter"
|
||||
VERSION = "2.8"
|
||||
MAIN = "kcc.py"
|
||||
|
||||
if sys.platform == 'darwin':
|
||||
includefiles = ['README.md', 'MANIFEST.in', 'LICENSE.txt', 'comic2ebook.ico', 'comic2ebook.icns']
|
||||
includes = []
|
||||
excludes = []
|
||||
|
||||
if sys.platform == "darwin":
|
||||
from setuptools import setup
|
||||
extra_options = dict(
|
||||
setup_requires=['py2app'],
|
||||
app=[mainscript],
|
||||
app=[MAIN],
|
||||
options=dict(
|
||||
py2app=dict(
|
||||
argv_emulation=True,
|
||||
iconfile='resources/comic2ebook.icns',
|
||||
iconfile='comic2ebook.icns',
|
||||
plist=dict(
|
||||
CFBundleName=NAME,
|
||||
CFBundleShortVersionString=VERSION,
|
||||
@@ -43,13 +46,17 @@ elif sys.platform == 'win32':
|
||||
from cx_Freeze import setup, Executable
|
||||
base = "Win32GUI"
|
||||
extra_options = dict(
|
||||
executables=[Executable("kcc.py", base=base)]
|
||||
)
|
||||
options={"build_exe": {"include_files": includefiles, 'excludes': excludes, 'compressed': True}},
|
||||
executables=[Executable(MAIN,
|
||||
base=base,
|
||||
icon="comic2ebook.ico",
|
||||
copyDependentFiles=True,
|
||||
appendScriptToExe=True,
|
||||
appendScriptToLibrary=False,
|
||||
compress=True)])
|
||||
else:
|
||||
extra_options = dict(
|
||||
# Normally unix-like platforms will use "setup.py install"
|
||||
# and install the main script as such
|
||||
scripts=[mainscript],
|
||||
scripts=[MAIN],
|
||||
)
|
||||
|
||||
setup(
|
||||
@@ -71,12 +78,10 @@ setup(
|
||||
'Intended Audience :: End Users/Desktop',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Topic :: Multimedia :: Graphics :: Graphics Conversion',
|
||||
'Topic :: Utilities'
|
||||
],
|
||||
packages=['kcc'],
|
||||
# make sure to add custom_fixers to the MANIFEST.in
|
||||
include_package_data=True,
|
||||
**extra_options
|
||||
)
|
||||
|
||||
22
setup_console.py
Normal file
22
setup_console.py
Normal file
@@ -0,0 +1,22 @@
|
||||
"""
|
||||
cx_freeze build script for Windows KCC No-GUI release.
|
||||
|
||||
Usage (Windows):
|
||||
python setup_console.py build
|
||||
"""
|
||||
import sys
|
||||
from cx_Freeze import setup, Executable
|
||||
sys.path.insert(0, 'kcc')
|
||||
|
||||
setup(
|
||||
name = "KindleComicConverter",
|
||||
version = "2.8",
|
||||
author = "Ciro Mattia Gonano",
|
||||
author_email = "ciromattia@gmail.com",
|
||||
description = "A tool to convert comics (CBR/CBZ/PDFs/image folders) to MOBI.",
|
||||
license= "ISC License (ISCL)",
|
||||
keywords= "kindle comic mobipocket mobi cbz cbr manga",
|
||||
url = "http://github.com/ciromattia/kcc",
|
||||
executables = [Executable("kcc/comic2ebook.py", compress=True, copyDependentFiles=True, appendScriptToExe=True, appendScriptToLibrary=False),
|
||||
Executable("kcc/kindlestrip.py", compress=True, copyDependentFiles=True, appendScriptToExe=True, appendScriptToLibrary=False)]
|
||||
)
|
||||
86
setup_cx.py
86
setup_cx.py
@@ -1,86 +0,0 @@
|
||||
"""
|
||||
py2app/py2exe build script for MyApplication.
|
||||
|
||||
Will automatically ensure that all build prerequisites are available
|
||||
via ez_setup
|
||||
|
||||
Usage (Mac OS X):
|
||||
python setup_cx.py bdist_dmg
|
||||
|
||||
Usage (Windows):
|
||||
python setup_cx.py bdist_msi
|
||||
"""
|
||||
import sys
|
||||
from cx_Freeze import setup, Executable
|
||||
|
||||
NAME='KindleComicConverter'
|
||||
VERSION="2.4"
|
||||
mainscript = 'kcc.py'
|
||||
|
||||
base = None
|
||||
|
||||
if sys.platform == 'darwin':
|
||||
extra_options = dict(
|
||||
bundle-iconfile='resources/comic2ebook.icns',
|
||||
volume-label=NAME + " " + VERSION
|
||||
app=[mainscript],
|
||||
options=dict(
|
||||
py2app=dict(
|
||||
argv_emulation=True,
|
||||
iconfile='resources/comic2ebook.icns',
|
||||
plist=dict(
|
||||
CFBundleName = NAME,
|
||||
CFBundleShortVersionString = VERSION,
|
||||
CFBundleGetInfoString = NAME + " " + VERSION + ", written 2012-2013 by Ciro Mattia Gonano",
|
||||
CFBundleExecutable = NAME,
|
||||
CFBundleIdentifier = 'com.github.ciromattia.kcc',
|
||||
CFBundleSignature = 'dplt'
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
elif sys.platform == 'win32':
|
||||
base = "Win32GUI"
|
||||
extra_options = dict(
|
||||
upgrade-code = 'KindleComicConverter',
|
||||
executables = [Executable("kcc.py", base=base)]
|
||||
)
|
||||
else:
|
||||
extra_options = dict(
|
||||
# Normally unix-like platforms will use "setup.py install"
|
||||
# and install the main script as such
|
||||
scripts=[mainscript],
|
||||
)
|
||||
|
||||
options = dict(
|
||||
|
||||
)
|
||||
|
||||
setup(
|
||||
name=NAME,
|
||||
version=VERSION,
|
||||
author="Ciro Mattia Gonano",
|
||||
author_email="ciromattia@gmail.com",
|
||||
description=("A tool to convert comics (CBR/CBZ/PDFs/image folders) to Mobipocket."),
|
||||
license = "ISC License (ISCL)",
|
||||
keywords = "kindle comic mobipocket mobi cbz cbr manga",
|
||||
url = "http://github.com/ciromattia/kcc",
|
||||
classifiers=[
|
||||
'Development Status :: 4 - Beta'
|
||||
'License :: OSI Approved :: ISC License (ISCL)',
|
||||
'Environment :: Console',
|
||||
'Environment :: MacOS X',
|
||||
'Environment :: Win32 (MS Windows)',
|
||||
'Environment :: X11 Applications',
|
||||
'Intended Audience :: End Users/Desktop',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Topic :: Multimedia :: Graphics :: Graphics Conversion',
|
||||
'Topic :: Utilities'
|
||||
],
|
||||
# make sure to add custom_fixers to the MANIFEST.in
|
||||
include_package_data=True,
|
||||
executables = [Executable("kcc.py", base=base)]),
|
||||
**extra_options
|
||||
)
|
||||
Reference in New Issue
Block a user