mirror of
https://github.com/ciromattia/kcc
synced 2026-04-17 22:48:53 +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 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -3,3 +3,5 @@
|
|||||||
*.cbr
|
*.cbr
|
||||||
.idea
|
.idea
|
||||||
build
|
build
|
||||||
|
awkcc
|
||||||
|
.DS_Store
|
||||||
|
|||||||
123
README.md
123
README.md
@@ -1,38 +1,32 @@
|
|||||||
# KCC
|
# KCC
|
||||||
|
|
||||||
`KCC` (a.k.a. `KindleComicConverter`) is a Python app to convert comic files or folders to ePub or Panel View Mobipocket.
|
`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:
|
|
||||||
- enhancing contrast
|
It can also optionally optimize images by applying a number of transformations.
|
||||||
- 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.6.zip](https://dl.dropbox.com/u/16806101/KindleComicConverter_osx_2.6.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.6.zip](https://dl.dropbox.com/u/16806101/KindleComicConverter_win-amd64_2.6.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.6.zip](http://pawelj.vulturis.eu/Shared/KindleComicConverter_win-x86_2.6.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
|
||||||
`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
|
- CBZ, ZIP
|
||||||
- CBR, RAR *(with `unrar` executable)*
|
- CBR, RAR *(With `unrar` executable)*
|
||||||
- folders
|
- PDF *(Extracting only contained JPG images)*
|
||||||
- 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!
|
|
||||||
|
|
||||||
## OPTIONAL REQUIREMENTS
|
## OPTIONAL REQUIREMENTS
|
||||||
- `kindlegen` in /usr/local/bin/ *(for .mobi generation)*
|
- `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)*
|
- [unrar](http://www.rarlab.com/download.htm) *(For CBR support)*
|
||||||
|
|
||||||
### for compiling/running from source:
|
### 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)
|
- 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.
|
- [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.
|
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:
|
### Standalone `comic2ebook.py` usage:
|
||||||
|
|
||||||
```
|
```
|
||||||
comic2ebook.py [options] comic_file|comic_folder
|
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
|
|
||||||
```
|
|
||||||
|
|
||||||
The script takes care of creating an *.epub* from your archive/folder, then:
|
Options:
|
||||||
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.
|
--version show program's version number and exit
|
||||||
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).
|
-h, --help show this help message and exit
|
||||||
3. Copy the `.mobi` file to your Kindle!
|
-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
|
## CREDITS
|
||||||
KCC is made by [Ciro Mattia Gonano](http://github.com/ciromattia) and [Paweł Jastrzębski](http://github.com/AcidWeb)
|
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
|
- `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
|
- 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
|
- `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
|
||||||
|
- `magic.py` from [python-magic](https://github.com/ahupp/python-magic) library
|
||||||
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\`)
|
|
||||||
|
|
||||||
|
|
||||||
## CHANGELOG
|
## CHANGELOG
|
||||||
- 1.00: Initial version
|
- 1.00: Initial version
|
||||||
@@ -120,10 +101,28 @@ 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)
|
- 2.4: Use temporary directory as workdir (fixes converting from external volumes and zipfiles renaming)
|
||||||
Fixed "add folders" from GUI.
|
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).
|
- 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).
|
Fixes epub containing zipped itself (#10)
|
||||||
- 2.6: Added --rotate option to rotate landscape images instead of splitting them.
|
- 2.6: Added --rotate option to rotate landscape images instead of splitting them (#16, #24)
|
||||||
Added --output option to customize ePub output dir/file.
|
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)
|
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
|
||||||
|
|
||||||
|
|||||||
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
|
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
#
|
#
|
||||||
__version__ = '2.6'
|
__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'
|
||||||
|
|
||||||
@@ -26,9 +26,14 @@ from kcc import gui
|
|||||||
from sys import platform
|
from sys import platform
|
||||||
import os
|
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':
|
if platform == 'darwin':
|
||||||
os.environ['PATH'] = '/usr/local/bin:' + os.environ['PATH']
|
os.environ['PATH'] = '/usr/local/bin:' + os.environ['PATH']
|
||||||
root = Tk()
|
elif platform == 'win32':
|
||||||
app = gui.MainWindow(master=root,title="Kindle Comic Converter v" + __version__)
|
root.iconbitmap(default='comic2ebook.ico')
|
||||||
root.tkraise()
|
gui.MainWindow(master=root)
|
||||||
root.mainloop()
|
root.mainloop()
|
||||||
|
|||||||
@@ -19,56 +19,58 @@ __copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>'
|
|||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import zipfile
|
||||||
|
import rarfile
|
||||||
|
|
||||||
|
|
||||||
class CBxArchive:
|
class CBxArchive:
|
||||||
def __init__(self, origFileName):
|
def __init__(self, origFileName):
|
||||||
self.cbxexts = ['.zip', '.cbz', '.rar', '.cbr']
|
|
||||||
self.origFileName = origFileName
|
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):
|
def isCbxFile(self):
|
||||||
return self.filename[1].lower() in self.cbxexts
|
return self.compressor is not None
|
||||||
|
|
||||||
def extractCBZ(self, targetdir):
|
def extractCBZ(self, targetdir):
|
||||||
try:
|
cbzFile = zipfile.ZipFile(self.origFileName)
|
||||||
from zipfile import ZipFile
|
filelist = []
|
||||||
except ImportError:
|
|
||||||
self.cbzFile = None
|
|
||||||
cbzFile = ZipFile(self.origFileName)
|
|
||||||
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
|
||||||
elif f.endswith('/'):
|
elif f.endswith('/'):
|
||||||
try:
|
try:
|
||||||
os.makedirs(os.path.join(targetdir, f))
|
os.makedirs(os.path.join(targetdir, f))
|
||||||
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):
|
||||||
try:
|
|
||||||
import rarfile
|
|
||||||
except ImportError:
|
|
||||||
self.cbrFile = None
|
|
||||||
return
|
|
||||||
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
|
||||||
elif f.endswith('/'):
|
elif f.endswith('/'):
|
||||||
try:
|
try:
|
||||||
os.makedirs(os.path.join(targetdir,f))
|
os.makedirs(os.path.join(targetdir, f))
|
||||||
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):
|
||||||
if '.cbr' == self.filename[1].lower() or '.rar' == self.filename[1].lower():
|
print "\n" + targetdir + "\n"
|
||||||
|
if self.compressor == 'rar':
|
||||||
self.extractCBR(targetdir)
|
self.extractCBR(targetdir)
|
||||||
elif '.cbz' == self.filename[1].lower() or '.zip' == self.filename[1].lower():
|
elif self.compressor == 'zip':
|
||||||
self.extractCBZ(targetdir)
|
self.extractCBZ(targetdir)
|
||||||
adir = os.listdir(targetdir)
|
adir = os.listdir(targetdir)
|
||||||
if len(adir) == 1 and os.path.isdir(os.path.join(targetdir, adir[0])):
|
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
|
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
#
|
#
|
||||||
__version__ = '2.6'
|
__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'
|
||||||
@@ -60,23 +60,49 @@ def buildHTML(path, imgfile):
|
|||||||
"<head>\n",
|
"<head>\n",
|
||||||
"<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),
|
||||||
|
"style.css\" type=\"text/css\" rel=\"stylesheet\"/>\n",
|
||||||
"</head>\n",
|
"</head>\n",
|
||||||
"<body>\n",
|
"<body>\n",
|
||||||
|
"<div class=\"fs\">\n",
|
||||||
"<div><img src=\"", "../" * backref, "Images/", postfix, imgfile, "\" alt=\"",
|
"<div><img src=\"", "../" * backref, "Images/", postfix, imgfile, "\" alt=\"",
|
||||||
imgfile, "\" class=\"singlePage\"/></div>\n",
|
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>"
|
|
||||||
])
|
])
|
||||||
|
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
|
||||||
|
|
||||||
@@ -117,19 +143,19 @@ def buildNCX(dstdir, title, chapters):
|
|||||||
folder = chapter[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
|
folder = chapter[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
|
||||||
title = os.path.basename(folder)
|
title = os.path.basename(folder)
|
||||||
filename = getImageFileName(os.path.join(folder, chapter[1]))
|
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")
|
+ "</text></navLabel><content src=\"" + filename[0] + ".html\"/></navPoint>\n")
|
||||||
f.write("</navMap>\n</ncx>")
|
f.write("</navMap>\n</ncx>")
|
||||||
f.close()
|
f.close()
|
||||||
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 = 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"
|
||||||
@@ -155,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]:
|
||||||
@@ -176,43 +205,57 @@ def buildOPF(profile, dstdir, title, filelist, cover=None, righttoleft=False):
|
|||||||
for path in filelist:
|
for path in filelist:
|
||||||
folder = path[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
|
folder = path[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
|
||||||
filename = getImageFileName(path[1])
|
filename = getImageFileName(path[1])
|
||||||
uniqueid = os.path.join(folder, filename[0]).replace('/', '_')
|
uniqueid = os.path.join(folder, filename[0]).replace('/', '_').replace('\\', '_')
|
||||||
reflist.append(uniqueid)
|
reflist.append(uniqueid)
|
||||||
f.write("<item id=\"page_" + uniqueid + "\" href=\""
|
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")
|
+ ".html\" media-type=\"application/xhtml+xml\"/>\n")
|
||||||
if '.png' == filename[1]:
|
if '.png' == filename[1]:
|
||||||
mt = 'image/png'
|
mt = 'image/png'
|
||||||
else:
|
else:
|
||||||
mt = 'image/jpeg'
|
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")
|
+ mt + "\"/>\n")
|
||||||
if (options.profile == 'K4' or options.profile == 'KHD') and splittedSomething:
|
if options.landscapemode and splitCount > 0:
|
||||||
f.write("<item id=\"blank-page\" href=\""
|
splitCountUsed = 1
|
||||||
+ os.path.join('Text', 'blank.html')
|
while splitCountUsed <= splitCount:
|
||||||
+ "\" media-type=\"application/xhtml+xml\"/>\n")
|
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")
|
f.write("</manifest>\n<spine toc=\"ncx\">\n")
|
||||||
|
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\
|
||||||
f.write("<itemref idref=\"blank-page\" properties=\"layout-blank\"/>\n")
|
options.landscapemode:
|
||||||
f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing1 + "\"/>\n")
|
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"):
|
elif entry.endswith("-2"):
|
||||||
f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing2 + "\"/>\n")
|
if options.landscapemode:
|
||||||
if righttoleft:
|
f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing2 + "\"/>\n")
|
||||||
|
else:
|
||||||
|
f.write("<itemref idref=\"page_" + entry + "\"/>\n")
|
||||||
|
if options.righttoleft:
|
||||||
facing = "right"
|
facing = "right"
|
||||||
else:
|
else:
|
||||||
facing = "left"
|
facing = "left"
|
||||||
else:
|
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':
|
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')
|
||||||
@@ -248,18 +291,22 @@ def isInFilelist(filename, filelist):
|
|||||||
|
|
||||||
|
|
||||||
def applyImgOptimization(img, isSplit=False, toRight=False):
|
def applyImgOptimization(img, isSplit=False, toRight=False):
|
||||||
img.optimizeImage()
|
|
||||||
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,
|
||||||
img.quantizeImage()
|
options.nopanelviewhq)
|
||||||
|
img.optimizeImage(options.gamma)
|
||||||
|
if options.forcepng:
|
||||||
|
img.quantizeImage()
|
||||||
|
|
||||||
|
|
||||||
def dirImgProcess(path):
|
def dirImgProcess(path):
|
||||||
global options
|
global options, splitCount
|
||||||
global splittedSomething
|
if options.righttoleft:
|
||||||
splittedSomething = False
|
facing = "right"
|
||||||
|
else:
|
||||||
|
facing = "left"
|
||||||
|
|
||||||
for (dirpath, dirnames, filenames) in os.walk(path):
|
for (dirpath, dirnames, filenames) in os.walk(path):
|
||||||
for afile in filenames:
|
for afile in filenames:
|
||||||
@@ -269,26 +316,38 @@ def dirImgProcess(path):
|
|||||||
else:
|
else:
|
||||||
print ".",
|
print ".",
|
||||||
img = image.ComicPage(os.path.join(dirpath, afile), options.profile)
|
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:
|
if split is not None:
|
||||||
splittedSomething = True
|
|
||||||
if options.verbose:
|
if options.verbose:
|
||||||
print "Splitted " + afile
|
print "Splitted " + afile
|
||||||
if options.righttoleft:
|
if options.righttoleft:
|
||||||
toRight1 = False
|
toRight1 = False
|
||||||
toRight2 = True
|
toRight2 = True
|
||||||
|
if facing == "left":
|
||||||
|
splitCount += 1
|
||||||
|
facing = "right"
|
||||||
else:
|
else:
|
||||||
toRight1 = True
|
toRight1 = True
|
||||||
toRight2 = False
|
toRight2 = False
|
||||||
|
if facing == "right":
|
||||||
|
splitCount += 1
|
||||||
|
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)
|
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)
|
img1.saveToDir(dirpath, options.forcepng)
|
||||||
else:
|
else:
|
||||||
|
if facing == "right":
|
||||||
|
facing = "left"
|
||||||
|
else:
|
||||||
|
facing = "right"
|
||||||
applyImgOptimization(img)
|
applyImgOptimization(img)
|
||||||
img.saveToDir(dirpath)
|
img.saveToDir(dirpath, options.forcepng)
|
||||||
|
|
||||||
|
|
||||||
def genEpubStruct(path):
|
def genEpubStruct(path):
|
||||||
@@ -296,13 +355,133 @@ 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', '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')):
|
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
|
||||||
@@ -322,14 +501,13 @@ 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 splittedSomething:
|
if options.landscapemode and splitCount > 0:
|
||||||
filelist.append(buildBlankHTML(os.path.join(path, 'OEBPS', 'Text')))
|
filelist.append(buildBlankHTML(os.path.join(path, 'OEBPS', 'Text')))
|
||||||
|
|
||||||
|
|
||||||
def getWorkFolder(afile):
|
def getWorkFolder(afile):
|
||||||
workdir = tempfile.mkdtemp()
|
workdir = tempfile.mkdtemp()
|
||||||
fname = os.path.splitext(afile)
|
|
||||||
if os.path.isdir(afile):
|
if os.path.isdir(afile):
|
||||||
try:
|
try:
|
||||||
import shutil
|
import shutil
|
||||||
@@ -338,13 +516,18 @@ def getWorkFolder(afile):
|
|||||||
path = workdir
|
path = workdir
|
||||||
except OSError:
|
except OSError:
|
||||||
raise
|
raise
|
||||||
elif fname[1].lower() == '.pdf':
|
elif afile.lower().endswith('.pdf'):
|
||||||
pdf = pdfjpgextract.PdfJpgExtract(afile)
|
pdf = pdfjpgextract.PdfJpgExtract(afile)
|
||||||
path = pdf.extract()
|
path = pdf.extract()
|
||||||
else:
|
else:
|
||||||
cbx = cbxarchive.CBxArchive(afile)
|
cbx = cbxarchive.CBxArchive(afile)
|
||||||
if cbx.isCbxFile():
|
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:
|
else:
|
||||||
raise TypeError
|
raise TypeError
|
||||||
move(path, path + "_temp")
|
move(path, path + "_temp")
|
||||||
@@ -358,49 +541,59 @@ def Copyright():
|
|||||||
|
|
||||||
|
|
||||||
def Usage():
|
def Usage():
|
||||||
print "Generates HTML, NCX and OPF for a Comic ebook from a bunch of images"
|
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 "Optimized for creating MOBI files to be read on Kindle Paperwhite."
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
|
|
||||||
|
|
||||||
def main(argv=None):
|
def main(argv=None):
|
||||||
global parser, options, epub_path
|
global parser, options, epub_path, splitCount
|
||||||
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("-v", "--verbose", action="store_true", dest="verbose", default=False,
|
parser.add_option("--nopanelviewhq", action="store_true", dest="nopanelviewhq", default=False,
|
||||||
help="Verbose output [default=False]")
|
help="Disable high quality Panel View [Default=False]")
|
||||||
parser.add_option("--no-image-processing", 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("--upscale-images", action="store_true", dest="upscale", default=False,
|
parser.add_option("--forcepng", action="store_true", dest="forcepng", default=False,
|
||||||
help="Resize images smaller than device's resolution [default=False]")
|
help="Create PNG files instead JPEG (For non-Kindle devices) [Default=False]")
|
||||||
parser.add_option("--stretch-images", action="store_true", dest="stretch", default=False,
|
parser.add_option("--gamma", type="float", dest="gamma", default="0.0",
|
||||||
help="Stretch images to device's resolution [default=False]")
|
help="Apply gamma correction to linearize the image [Default=Auto]")
|
||||||
parser.add_option("--black-borders", action="store_true", dest="black_borders", default=False,
|
parser.add_option("--upscale", action="store_true", dest="upscale", default=False,
|
||||||
help="Use black borders (instead of white ones) when not stretching and ratio "
|
help="Resize images smaller than device's resolution [Default=False]")
|
||||||
+ "is not like the device's one [default=False]")
|
parser.add_option("--stretch", action="store_true", dest="stretch", default=False,
|
||||||
parser.add_option("--no-cut-page-numbers", action="store_false", dest="cutpagenumbers", default=True,
|
help="Stretch images to device's resolution [Default=False]")
|
||||||
help="Do not try to cut page numbering on images [default=True]")
|
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,
|
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,
|
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)
|
options, args = parser.parse_args(argv)
|
||||||
|
checkOptions()
|
||||||
if len(args) != 1:
|
if len(args) != 1:
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
return
|
return
|
||||||
path = getWorkFolder(args[0])
|
path = getWorkFolder(args[0])
|
||||||
if options.title == 'defaulttitle':
|
if options.title == 'defaulttitle':
|
||||||
options.title = os.path.splitext(os.path.basename(args[0]))[0]
|
options.title = os.path.splitext(os.path.basename(args[0]))[0]
|
||||||
|
splitCount = 0
|
||||||
if options.imgproc:
|
if options.imgproc:
|
||||||
print "Processing images..."
|
print "Processing images..."
|
||||||
dirImgProcess(path + "/OEBPS/Images/")
|
dirImgProcess(path + "/OEBPS/Images/")
|
||||||
print "Creating ePub structure..."
|
print "\nCreating ePub structure..."
|
||||||
genEpubStruct(path)
|
genEpubStruct(path)
|
||||||
# actually zip the ePub
|
# actually zip the ePub
|
||||||
if options.output is not None:
|
if options.output is not None:
|
||||||
@@ -421,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
|
||||||
|
|||||||
249
kcc/gui.py
249
kcc/gui.py
@@ -31,6 +31,7 @@ from subprocess import call
|
|||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import stat
|
import stat
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
|
||||||
class MainWindow:
|
class MainWindow:
|
||||||
@@ -39,25 +40,22 @@ class MainWindow:
|
|||||||
self.filelist = []
|
self.filelist = []
|
||||||
self.refresh_list()
|
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):
|
def open_files(self):
|
||||||
filetypes = [('all files', '.*'), ('Comic files', ('*.cbr', '*.cbz', '*.zip', '*.rar', '*.pdf'))]
|
filetypes = [('All files', '.*'), ('Comic files', ('*.cbr', '*.cbz', '*.zip', '*.rar', '*.pdf'))]
|
||||||
f = tkFileDialog.askopenfilenames(title="Choose a file...", filetypes=filetypes)
|
f = tkFileDialog.askopenfilenames(title="Choose files", filetypes=filetypes)
|
||||||
if not isinstance(f, tuple):
|
if not isinstance(f, tuple):
|
||||||
try:
|
f = self.master.tk.splitlist(f)
|
||||||
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)
|
|
||||||
self.filelist.extend(f)
|
self.filelist.extend(f)
|
||||||
self.refresh_list()
|
self.refresh_list()
|
||||||
|
|
||||||
def open_folder(self):
|
def open_folder(self):
|
||||||
f = tkFileDialog.askdirectory(title="Choose a folder...")
|
f = tkFileDialog.askdirectory(title="Choose folder:")
|
||||||
self.filelist.extend([f])
|
self.filelist.extend([f])
|
||||||
self.refresh_list()
|
self.refresh_list()
|
||||||
|
|
||||||
@@ -67,6 +65,13 @@ class MainWindow:
|
|||||||
for afile in self.filelist:
|
for afile in self.filelist:
|
||||||
self.filelocation.insert(END, afile)
|
self.filelocation.insert(END, afile)
|
||||||
self.filelocation.config(state=DISABLED)
|
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):
|
def initialize(self):
|
||||||
self.filelocation = Listbox(self.master)
|
self.filelocation = Listbox(self.master)
|
||||||
@@ -74,118 +79,175 @@ class MainWindow:
|
|||||||
self.refresh_list()
|
self.refresh_list()
|
||||||
|
|
||||||
self.clear_file = Button(self.master, text="Clear files", command=self.clear_files)
|
self.clear_file = Button(self.master, text="Clear files", command=self.clear_files)
|
||||||
self.clear_file.grid(row=4, column=0, 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 = Button(self.master, text="Add files", command=self.open_files)
|
||||||
self.open_file.grid(row=4, column=1, rowspan=3)
|
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 = Button(self.master, text="Add folder", command=self.open_folder)
|
||||||
self.open_folder.grid(row=4, column=2, rowspan=3)
|
self.open_folder.grid(row=4, column=2, sticky=W + E + N + S)
|
||||||
|
|
||||||
self.profile = StringVar()
|
self.profile = StringVar()
|
||||||
profiles = sorted(ProfileData.ProfileLabels.iterkeys())
|
profiles = sorted(ProfileData.ProfileLabels.iterkeys())
|
||||||
self.profile.set(profiles[-1])
|
self.profile.set(profiles[-1])
|
||||||
w = apply(OptionMenu, (self.master, self.profile) + tuple(profiles))
|
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 = {
|
self.options = {
|
||||||
'epub_only': IntVar(None, 0),
|
'Aepub_only': IntVar(None, 0),
|
||||||
'image_preprocess': IntVar(None, 1),
|
'Bmangastyle': IntVar(None, 0),
|
||||||
'rotate': IntVar(None, 0),
|
'Cnopanelviewhq': IntVar(None, 0),
|
||||||
'cut_page_numbers': IntVar(None, 1),
|
'Dimage_preprocess': IntVar(None, 0),
|
||||||
'mangastyle': IntVar(None, 0),
|
'Eforcepng': IntVar(None, 0),
|
||||||
'image_upscale': IntVar(None, 0),
|
'Fimage_gamma': DoubleVar(None, 0.0),
|
||||||
'image_stretch': IntVar(None, 0),
|
'Gimage_upscale': IntVar(None, 0),
|
||||||
'black_borders': 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 = {
|
self.optionlabels = {
|
||||||
'epub_only': "Generate ePub only (does not call 'kindlegen')",
|
'Aepub_only': "Generate EPUB only",
|
||||||
'image_preprocess': "Apply image optimizations",
|
'Bmangastyle': "Manga mode",
|
||||||
'rotate': "Rotate landscape images instead of splitting them",
|
'Cnopanelviewhq': "Disable high quality Panel View",
|
||||||
'cut_page_numbers': "Cut page numbers",
|
'Dimage_preprocess': "Disable image optimizations",
|
||||||
'mangastyle': "Manga-style (right-to-left reading, applies to reading and splitting)",
|
'Eforcepng': "Create PNG files instead JPEG",
|
||||||
'image_upscale': "Allow image upscaling",
|
'Fimage_gamma': "Custom gamma correction",
|
||||||
'image_stretch': "Stretch images",
|
'Gimage_upscale': "Allow image upscaling",
|
||||||
'black_borders': "Use black borders"
|
'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:
|
self.optionsButtons = {}
|
||||||
aCheckButton = Checkbutton(self.master, text=self.optionlabels[key], variable=self.options[key])
|
for key in sorted(self.options):
|
||||||
aCheckButton.grid(column=3, sticky='w')
|
if isinstance(self.options[key], IntVar) or isinstance(self.options[key], BooleanVar):
|
||||||
self.progressbar = ttk.Progressbar(orient=HORIZONTAL, length=200, mode='determinate')
|
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 = Button(self.master, text="CONVERT", command=self.start_conversion, fg="red", state=DISABLED)
|
||||||
self.submit.grid(column=3)
|
self.submit.grid(columnspan=4, sticky=W + E + N + S)
|
||||||
self.progressbar.grid(column=0, 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,
|
retcode = call("kindlegen", shell=True)
|
||||||
text="GUI can seem frozen while converting, kindly wait until some message appears!")
|
if retcode == 1:
|
||||||
self.notelabel.grid(column=0, columnspan=4, sticky=W + E + N + S)
|
self.optionsButtons['Aepub_only'].select()
|
||||||
|
self.optionsButtons['Aepub_only']['state'] = DISABLED
|
||||||
|
|
||||||
def start_conversion(self):
|
def start_conversion(self):
|
||||||
self.progressbar.start()
|
self.submit['state'] = DISABLED
|
||||||
|
self.master.update()
|
||||||
self.convert()
|
self.convert()
|
||||||
self.progressbar.stop()
|
self.submit['state'] = NORMAL
|
||||||
|
self.master.update()
|
||||||
|
|
||||||
def convert(self):
|
def convert(self):
|
||||||
if len(self.filelist) < 1:
|
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
|
return
|
||||||
profilekey = ProfileData.ProfileLabels[self.profile.get()]
|
profilekey = ProfileData.ProfileLabels[self.profile.get()]
|
||||||
argv = ["-p", profilekey]
|
argv = ["-p", profilekey]
|
||||||
if self.options['image_preprocess'].get() == 0:
|
if self.options['Bmangastyle'].get() == 1:
|
||||||
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:
|
|
||||||
argv.append("-m")
|
argv.append("-m")
|
||||||
if self.options['image_upscale'].get() == 1:
|
if self.options['Cnopanelviewhq'].get() == 1:
|
||||||
argv.append("--upscale-images")
|
argv.append("--nopanelviewhq")
|
||||||
if self.options['image_stretch'].get() == 1:
|
if self.options['Dimage_preprocess'].get() == 1:
|
||||||
argv.append("--stretch-images")
|
argv.append("--noprocessing")
|
||||||
if self.options['black_borders'].get() == 1:
|
if self.options['Eforcepng'].get() == 1:
|
||||||
argv.append("--black-borders")
|
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
|
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:
|
for entry in self.filelist:
|
||||||
|
self.progress_overall['value'] = filenum
|
||||||
|
self.progress_file['value'] = 1
|
||||||
self.master.update()
|
self.master.update()
|
||||||
|
filenum += 1
|
||||||
subargv = list(argv)
|
subargv = list(argv)
|
||||||
try:
|
try:
|
||||||
subargv.append(entry)
|
subargv.append(entry)
|
||||||
epub_path = comic2ebook.main(subargv)
|
epub_path = comic2ebook.main(subargv)
|
||||||
except Exception, err:
|
self.progress_file['value'] = 2
|
||||||
tkMessageBox.showerror('Error comic2ebook', "Error on file %s:\n%s" % (subargv[-1], str(err)))
|
self.master.update()
|
||||||
errors = True
|
except Exception as err:
|
||||||
continue
|
type_, value_, traceback_ = sys.exc_info()
|
||||||
if self.options['epub_only'] == 1:
|
tkMessageBox.showerror('KCC Error', "Error on file %s:\n%s\nTraceback:\n%s" %
|
||||||
continue
|
(subargv[-1], str(err), traceback.format_tb(traceback_)))
|
||||||
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)))
|
|
||||||
errors = True
|
errors = True
|
||||||
continue
|
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:
|
if errors:
|
||||||
tkMessageBox.showinfo(
|
tkMessageBox.showwarning("Done", "Conversion completed with errors.")
|
||||||
"Done",
|
|
||||||
"Conversion finished (some errors have been reported)"
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
tkMessageBox.showinfo(
|
tkMessageBox.showinfo("Done", "Conversion successful!")
|
||||||
"Done",
|
# reset progressbars
|
||||||
"Conversion successfully done!"
|
self.progress_overall['value'] = 0
|
||||||
)
|
self.progress_file['value'] = 0
|
||||||
|
self.master.update()
|
||||||
|
|
||||||
def remove_readonly(self, fn, path):
|
def remove_readonly(self, fn, path):
|
||||||
if fn is os.rmdir:
|
if fn is os.rmdir:
|
||||||
@@ -195,8 +257,7 @@ class MainWindow:
|
|||||||
os.chmod(path, stat.S_IWRITE)
|
os.chmod(path, stat.S_IWRITE)
|
||||||
os.remove(path)
|
os.remove(path)
|
||||||
|
|
||||||
def __init__(self, master, title):
|
def __init__(self, master):
|
||||||
self.filelist = []
|
self.filelist = []
|
||||||
self.master = master
|
self.master = master
|
||||||
self.master.title(title)
|
|
||||||
self.initialize()
|
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'
|
__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),
|
'K1': ("Kindle 1", (600, 800), Palette4, 1.8, (900, 1200)),
|
||||||
'K2': ("Kindle 2", (600, 800), Palette15),
|
'K2': ("Kindle 2", (600, 800), Palette15, 1.8, (900, 1200)),
|
||||||
'K3': ("Kindle 3/Keyboard", (600, 800), Palette16),
|
'K3': ("Kindle Keyboard", (600, 800), Palette16, 1.8, (900, 1200)),
|
||||||
'K4': ("Kindle 4/NT/Touch", (600, 800), Palette16),
|
'K4NT': ("Kindle Non-Touch", (600, 800), Palette16, 1.8, (900, 1200)),
|
||||||
'KHD': ("Kindle Paperwhite", (758, 1024), Palette16),
|
'K4T': ("Kindle Touch", (600, 800), Palette16, 1.8, (900, 1200)),
|
||||||
'KDX': ("Kindle DX", (824, 1200), Palette15),
|
'KHD': ("Kindle Paperwhite", (758, 1024), Palette16, 1.8, (1137, 1536)),
|
||||||
'KDXG': ("Kindle DXG", (824, 1200), Palette16)
|
'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 = 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,18 +113,29 @@ 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):
|
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
|
||||||
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:
|
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))
|
||||||
|
|
||||||
def optimizeImage(self):
|
def optimizeImage(self, gamma):
|
||||||
self.image = ImageOps.autocontrast(self.image)
|
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):
|
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)
|
||||||
@@ -130,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]) / 2
|
||||||
borderw = (self.size[0] - self.image.size[0])
|
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=(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)
|
|
||||||
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:
|
||||||
diff = int(self.image.size[1] * ratioDev) - self.image.size[0]
|
if isSplit and landscapeMode:
|
||||||
if isSplit and (self.profile == 'K4' or self.profile == 'KHD'):
|
diff = int(self.image.size[1] * ratioDev) - self.image.size[0]
|
||||||
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)
|
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:
|
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):
|
||||||
@@ -209,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
|
||||||
@@ -324,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
|
||||||
|
|||||||
@@ -70,3 +70,4 @@ class PdfJpgExtract:
|
|||||||
|
|
||||||
njpg += 1
|
njpg += 1
|
||||||
i = iend
|
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
|
## 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):
|
||||||
@@ -417,7 +419,7 @@ class RarInfo(object):
|
|||||||
'header_offset',
|
'header_offset',
|
||||||
'salt',
|
'salt',
|
||||||
'volume_file',
|
'volume_file',
|
||||||
)
|
)
|
||||||
|
|
||||||
def isdir(self):
|
def isdir(self):
|
||||||
'''Returns True if the entry is a directory.'''
|
'''Returns True if the entry is a directory.'''
|
||||||
@@ -729,7 +731,7 @@ class RarFile(object):
|
|||||||
# RAR 2.x does not write RAR_BLOCK_ENDARC
|
# RAR 2.x does not write RAR_BLOCK_ENDARC
|
||||||
if h.flags & RAR_FILE_SPLIT_AFTER:
|
if h.flags & RAR_FILE_SPLIT_AFTER:
|
||||||
more_vols = 1
|
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:
|
if volume == 0 and h.flags & RAR_FILE_SPLIT_BEFORE:
|
||||||
raise NeedFirstVolume("Need to start from first volume")
|
raise NeedFirstVolume("Need to start from first volume")
|
||||||
|
|
||||||
@@ -950,7 +952,7 @@ class RarFile(object):
|
|||||||
pos += S_COMMENT_HDR.size
|
pos += S_COMMENT_HDR.size
|
||||||
data = hdata[pos : pos_next]
|
data = hdata[pos : pos_next]
|
||||||
cmt = rar_decompress(ver, meth, data, declen, sflags,
|
cmt = rar_decompress(ver, meth, data, declen, sflags,
|
||||||
crc, self._password)
|
crc, self._password)
|
||||||
if not self._crc_check:
|
if not self._crc_check:
|
||||||
h.comment = self._decode_comment(cmt)
|
h.comment = self._decode_comment(cmt)
|
||||||
elif crc32(cmt) & 0xFFFF == crc:
|
elif crc32(cmt) & 0xFFFF == crc:
|
||||||
@@ -1073,7 +1075,7 @@ class RarFile(object):
|
|||||||
|
|
||||||
# decompress
|
# decompress
|
||||||
cmt = rar_decompress(inf.extract_version, inf.compress_type, data,
|
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
|
# check crc
|
||||||
if self._crc_check:
|
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
|
date = 0
|
||||||
mode = 0x20
|
mode = 0x20
|
||||||
fhdr = S_FILE_HDR.pack(len(data), declen, RAR_OS_MSDOS, crc,
|
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
|
fhdr += fname
|
||||||
if flags & RAR_FILE_SALT:
|
if flags & RAR_FILE_SALT:
|
||||||
if not salt:
|
if not salt:
|
||||||
@@ -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:
|
||||||
creationflags = creationflags)
|
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
|
return p
|
||||||
|
|
||||||
def check_returncode(p, out):
|
def check_returncode(p, out):
|
||||||
@@ -1770,9 +1779,9 @@ def check_returncode(p, out):
|
|||||||
|
|
||||||
# map return code to exception class
|
# map return code to exception class
|
||||||
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:
|
||||||
|
|||||||
@@ -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
|
Will automatically ensure that all build prerequisites are available via ez_setup
|
||||||
via ez_setup
|
|
||||||
|
|
||||||
Usage (Mac OS X):
|
Usage (Mac OS X):
|
||||||
python setup.py py2app
|
python setup.py py2app
|
||||||
|
|
||||||
Usage (Windows):
|
Usage (Windows):
|
||||||
python setup.py py2exe
|
python setup.py build
|
||||||
"""
|
"""
|
||||||
import ez_setup
|
from ez_setup import use_setuptools
|
||||||
ez_setup.use_setuptools()
|
use_setuptools()
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from setuptools import setup
|
|
||||||
|
|
||||||
NAME = 'KindleComicConverter'
|
NAME = "KindleComicConverter"
|
||||||
VERSION = "2.6"
|
VERSION = "2.8"
|
||||||
mainscript = 'kcc.py'
|
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(
|
extra_options = dict(
|
||||||
setup_requires=['py2app'],
|
setup_requires=['py2app'],
|
||||||
app=[mainscript],
|
app=[MAIN],
|
||||||
options=dict(
|
options=dict(
|
||||||
py2app=dict(
|
py2app=dict(
|
||||||
argv_emulation=True,
|
argv_emulation=True,
|
||||||
iconfile='resources/comic2ebook.icns',
|
iconfile='comic2ebook.icns',
|
||||||
plist=dict(
|
plist=dict(
|
||||||
CFBundleName=NAME,
|
CFBundleName=NAME,
|
||||||
CFBundleShortVersionString=VERSION,
|
CFBundleShortVersionString=VERSION,
|
||||||
@@ -43,13 +46,17 @@ elif sys.platform == 'win32':
|
|||||||
from cx_Freeze import setup, Executable
|
from cx_Freeze import setup, Executable
|
||||||
base = "Win32GUI"
|
base = "Win32GUI"
|
||||||
extra_options = dict(
|
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:
|
else:
|
||||||
extra_options = dict(
|
extra_options = dict(
|
||||||
# Normally unix-like platforms will use "setup.py install"
|
scripts=[MAIN],
|
||||||
# and install the main script as such
|
|
||||||
scripts=[mainscript],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
@@ -71,12 +78,10 @@ setup(
|
|||||||
'Intended Audience :: End Users/Desktop',
|
'Intended Audience :: End Users/Desktop',
|
||||||
'Operating System :: OS Independent',
|
'Operating System :: OS Independent',
|
||||||
'Programming Language :: Python',
|
'Programming Language :: Python',
|
||||||
'Programming Language :: Python :: 3',
|
'Programming Language :: Python :: 2.7',
|
||||||
'Topic :: Multimedia :: Graphics :: Graphics Conversion',
|
'Topic :: Multimedia :: Graphics :: Graphics Conversion',
|
||||||
'Topic :: Utilities'
|
'Topic :: Utilities'
|
||||||
],
|
],
|
||||||
packages=['kcc'],
|
packages=['kcc'],
|
||||||
# make sure to add custom_fixers to the MANIFEST.in
|
|
||||||
include_package_data=True,
|
|
||||||
**extra_options
|
**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