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

Compare commits

..

61 Commits
2.6 ... 2.7

Author SHA1 Message Date
Ciro Mattia Gonano
bb0856c841 Binary release 2013-03-09 23:42:22 +01:00
Paweł Jastrzębski
c21e8e398e Small tweaks of setup files 2013-03-09 18:03:49 +01:00
Paweł Jastrzębski
bba0223efc Win32 section was doubled
It builds correctly on Win32. Please define "problematic files".
2013-03-09 17:57:10 +01:00
Ciro Mattia Gonano
9709bc9c12 comments! 2013-03-09 17:29:41 +01:00
Ciro Mattia Gonano
0d61442c18 Try with icon bundling... 2013-03-09 17:24:27 +01:00
Ciro Mattia Gonano
89fa1393db Merge remote-tracking branch 'origin/master' 2013-03-09 17:17:57 +01:00
Ciro Mattia Gonano
15e86b28ab Do not use python-magic, instead use is_zipfile and is_rarfile, falling back on extension for PDFs. 2013-03-09 17:17:40 +01:00
Paweł Jastrzębski
a72d1128dd Fixed OS X build script 2013-03-09 16:16:01 +01:00
Paweł Jastrzębski
0fd3f6bc0f setup.py py2exe/app -> cx_freeze
Not tested on OS X.
2013-03-08 17:55:26 +01:00
Paweł Jastrzębski
d76ce6c92f Improved setup_console.py 2013-03-08 17:27:54 +01:00
Paweł Jastrzębski
b6fa993c17 Fixed splitCount global 2013-03-08 17:05:17 +01:00
Paweł Jastrzębski
81509012b4 Limit is slightly higher 2013-03-07 13:43:09 +01:00
Ciro Mattia Gonano
6db1ab0792 Update README 2013-03-07 12:22:37 +01:00
Ciro Mattia Gonano
513ea0a7cd Use python-magic (https://github.com/ahupp/python-magic) to get mimetype from files (extension does not matter anymore) 2013-03-07 11:56:06 +01:00
Ciro Mattia Gonano
640b5117f9 Use python-magic (https://github.com/ahupp/python-magic) to get mimetype from files (extension does not matter anymore) 2013-03-07 11:54:37 +01:00
Ciro Mattia Gonano
c9d558a353 Do not call kindlegen if source epub is bigger than 300MB (closes #17) 2013-03-07 11:30:40 +01:00
Ciro Mattia Gonano
378aff4f35 Gracefully exit if unrar missing (#15) 2013-03-07 11:21:17 +01:00
Ciro Mattia Gonano
6142c87fa3 remove brackets 2013-03-07 11:04:28 +01:00
Ciro Mattia Gonano
b01e8e2bc2 Version bumping 2013-03-07 10:53:28 +01:00
Ciro Mattia Gonano
10724489fc Update icon and focusing (#12) 2013-03-07 10:51:45 +01:00
Paweł Jastrzębski
3365f111e4 setup_console.py py2exe -> cx_freeze 2013-03-07 09:26:41 +01:00
Ciro Mattia Gonano
b1d22cd05b Merge branch 'master' of https://github.com/ciromattia/kcc 2013-03-07 00:48:33 +01:00
Ciro Mattia Gonano
74f2250952 Icon! 2013-03-06 23:22:18 +01:00
Ciro Mattia Gonano
36516fd594 Convert button disabled if no file is on the list 2013-03-06 23:17:22 +01:00
Ciro Mattia Gonano
2dab7a707b Update setup with Windows icon 2013-03-06 23:16:50 +01:00
Paweł Jastrzębski
fc8b26e292 Mother of all bug fixes 2013-03-06 23:08:24 +01:00
Ciro Mattia Gonano
5c0964c8fa Use splitlines() instead of regexp (fixes #25) 2013-03-06 22:51:47 +01:00
Ciro Mattia Gonano
6836c6ee17 PEP-8 codestyle (sorry for newline on long lines, PEP-8 want at max 78char-long lines...) 2013-03-06 22:47:17 +01:00
Paweł Jastrzębski
6adc2dff93 Updated README.md 2013-03-06 21:19:12 +01:00
Paweł Jastrzębski
63deec88db Updated README.md 2013-03-06 21:13:38 +01:00
Paweł Jastrzębski
c37e281d69 GUI now will detect missing KindleGen 2013-03-06 21:03:04 +01:00
Paweł Jastrzębski
4a0497addc Fixed bug - CSS directory depth 2013-03-06 20:23:18 +01:00
Paweł Jastrzębski
e2f5c549aa GUI improvements
Option sorting resolved slighty lame but it work.
2013-03-06 20:12:27 +01:00
Paweł Jastrzębski
a8195d44ee Little cleaning and normalisation 2013-03-06 18:01:12 +01:00
Ciro Mattia Gonano
18a505637d Merge pull request #30 from ciromattia/kcc-epubmargins
Pull request for epub margin support
2013-03-06 07:31:49 -08:00
Ciro Mattia Gonano
62475e12c6 Merge branch 'master' 2013-03-06 16:29:56 +01:00
Ciro Mattia Gonano
46888e10d8 Merge pull request #27 from ciromattia/kcc-gamma
KCC Gamma + GUI
2013-03-06 07:19:06 -08:00
Ciro Mattia Gonano
eb406aada0 Merge branch 'master' into kcc-gamma 2013-03-06 16:18:45 +01:00
Ciro Mattia Gonano
1582d03fab Optimization: if gamma is 1.0 don't eval image 2013-03-06 15:15:24 +01:00
Ciro Mattia Gonano
4cfac52d6a Display progressbars (fixes #13)
Still to change into something less eye-bleeding ;)
2013-03-06 15:08:42 +01:00
Ciro Mattia Gonano
04ea816365 Merge branch 'master' of github.com:ciromattia/kcc 2013-03-06 14:27:25 +01:00
Paweł Jastrzębski
6a298175f0 CSS Formatting 2013-03-06 13:39:51 +01:00
Paweł Jastrzębski
690f0298e6 Merge https://github.com/devernay/kcc into kcc-epubmargins 2013-03-06 13:08:26 +01:00
Paweł Jastrzębski
fc93697c28 Merge https://github.com/ciromattia/kcc into kcc-epubmargins 2013-03-06 13:07:38 +01:00
Frédéric Devernay
32391f6a5a better epub support (grabbed from calibre): get rid of those small margins 2013-03-06 12:11:50 +01:00
Ciro Mattia Gonano
f163eac853 Make GUI error reporting more useful 2013-03-06 11:33:28 +01:00
Ciro Mattia Gonano
ce824f4cab Add default gamma by profile support 2013-03-06 11:32:20 +01:00
Ciro Mattia Gonano
df41ad405e Merge branch 'master' into kcc-gamma 2013-03-06 11:10:55 +01:00
Ciro Mattia Gonano
685cdc929b Missing return, now PDF conversion works again 2013-03-06 10:29:59 +01:00
Paweł Jastrzębski
5e65c5149c Litte cleanup of setup files
Added setup_console script that generate binaries used by AWKCC.
2013-03-06 09:41:54 +01:00
Paweł Jastrzębski
718af10be7 Updated GUI
Plus small notquantize tweaks.
2013-03-05 23:41:48 +01:00
Paweł Jastrzębski
b162425e52 Added option to disable dithering 2013-03-05 22:31:26 +01:00
Paweł Jastrzębski
f1b63420f6 Merge branch 'master' of https://github.com/devernay/kcc into kcc-gamma 2013-03-05 21:49:24 +01:00
Ciro Mattia Gonano
95e7329abf Revert to POSIX path - win7 kindlegen seems to want them 2013-03-05 19:25:00 +01:00
Frédéric Devernay
5d3b7e83f5 add --nosplitrotate option (is the name right?) 2013-03-05 17:58:24 +01:00
Frédéric Devernay
751e6eb4e7 save dithered images as PNG, and linearize (inverse gamma) before dithering 2013-03-05 17:56:43 +01:00
Ciro Mattia Gonano
074e31cb2e Revert to POSIX path - win kindlegen seems to work 2013-03-05 17:31:34 +01:00
Ciro Mattia Gonano
e7e87d03cd Replace backslashes in path, too (win paths) 2013-03-05 17:12:48 +01:00
Paweł Jastrzębski
38669d08ed Small .gitignore tweak 2013-03-05 16:55:14 +01:00
Paweł Jastrzębski
825eafad7f Fixed bug caused by formating
2:0 Mr. Ciromattia :-)
2013-03-05 16:52:22 +01:00
Paweł Jastrzębski
f805984c1c Fixed logic - Inserting blank-page records
Variable "facing" could be bool but this way code is more readable.
2013-03-05 16:28:22 +01:00
17 changed files with 419 additions and 434 deletions

2
.gitignore vendored
View File

@@ -3,3 +3,5 @@
*.cbr *.cbr
.idea .idea
build build
awkcc
.DS_Store

103
README.md
View File

@@ -1,38 +1,37 @@
# 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 as of version 2.2 it outputs valid ePub 2.0 so _**despite its name, KCC is
actually a comic 2 epub converter that every ereader owner can happily use**_. actually a comic 2 epub converter that every ereader owner can happily use**_.
It also optimizes comic images by: It also optimizes comic images by:
- enhancing contrast - Enhancing contrast.
- cutting page numbering - Cutting page numbering.
- cropping white borders - Cropping white borders.
- resizing larger images to device's native resolution - Resizing larger images to device's native resolution.
- quantizing images to device's palette - 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.7.zip](https://dl.dropbox.com/u/16806101/KindleComicConverter_osx_2.7.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.7.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.7.zip](http://pawelj.vulturis.eu/Shared/KindleComicConverter_win-x86_2.7.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,41 +46,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: Options:
--version show program's version number and exit --version show program's version number and exit
-h, --help show this help message and exit -h, --help show this help message and exit
-p PROFILE, --profile=PROFILE -p PROFILE, --profile=PROFILE
Device profile (choose one among K1, K2, K3, K4, KDX, Device profile (Choose one among K1, K2, K3, K4, KDX, KDXG or KHD) [Default=KHD]
KDXG or KHD) [default=KHD]
-t TITLE, --title=TITLE -t TITLE, --title=TITLE
Comic title [default=filename] Comic title [Default=filename]
-m, --manga-style 'Manga style' (right-to-left reading and splitting) -m, --manga-style Manga style (Right-to-left reading and splitting) [Default=False]
[default=False] --noprocessing Do not apply image preprocessing (Page splitting and optimizations) [Default=True]
-v, --verbose Verbose output [default=False] --nodithering Disable image quantization [Default=False]
--no-image-processing --gamma=GAMMA Apply gamma correction to linearize the image [Default=Auto]
Do not apply image preprocessing (page splitting and --upscale Resize images smaller than device's resolution [Default=False]
optimizations) [default=True] --stretch Stretch images to device's resolution [Default=False]
--upscale-images Resize images smaller than device's resolution --blackborders Use black borders instead of white ones when not stretching and ratio is not like the device's one [Default=False]
[default=False] --rotate Rotate landscape pages instead of splitting them [Default=False]
--stretch-images Stretch images to device's resolution [default=False] --nosplitrotate Disable splitting and rotation [Default=False]
--black-borders Use black borders (instead of white ones) when not --nocutpagenumbers Do not try to cut page numbering on images [Default=True]
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 -o OUTPUT, --output=OUTPUT
Output directory or file for generated ePub Output generated EPUB to specified directory or file
-v, --verbose Verbose output [Default=False]
``` ```
The script takes care of creating an *.epub* from your archive/folder, then:
1. Run `Kindlegen` on the generated *.epub*. Depending on how many images you have, this may take awhile. Once completed, the `.mobi` file should be in the directory.
2. (optionally) remove the SRCS record to reduce the `.mobi` filesize in half. You can use [Kindlestrip](http://www.mobileread.com/forums/showthread.php?t=96903).
3. Copy the `.mobi` file to your Kindle!
## 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 +82,7 @@ The app relies and includes the following scripts/binaries:
- `rarfile.py` script &copy; 2005-2011 **Marko Kreen** <markokr@gmail.com>, released with ISC License - `rarfile.py` script &copy; 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 +105,20 @@ 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
## COPYRIGHT ## COPYRIGHT

BIN
comic2ebook.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 KiB

13
kcc.py
View File

@@ -16,7 +16,7 @@
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE. # PERFORMANCE OF THIS SOFTWARE.
# #
__version__ = '2.6' __version__ = '2.7'
__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()

View File

@@ -19,23 +19,25 @@ __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
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
@@ -48,11 +50,6 @@ class CBxArchive:
cbzFile.extract(f, targetdir) cbzFile.extract(f, targetdir)
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)
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'):
@@ -66,9 +63,9 @@ class CBxArchive:
cbrFile.extract(f, targetdir) cbrFile.extract(f, targetdir)
def extract(self, targetdir): def extract(self, targetdir):
if '.cbr' == self.filename[1].lower() or '.rar' == self.filename[1].lower(): 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])):

View File

@@ -16,7 +16,7 @@
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE. # PERFORMANCE OF THIS SOFTWARE.
# #
__version__ = '2.6' __version__ = '2.7'
__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,10 +60,14 @@ 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),
"stylesheet.css\" type=\"text/css\" rel=\"stylesheet\"/>\n",
"<link href=\"", "../" * (backref - 1),
"page_styles.css\" type=\"text/css\" rel=\"stylesheet\"/>\n",
"</head>\n", "</head>\n",
"<body>\n", "<body class=\"kcc\">\n",
"<div><img src=\"", "../" * backref, "Images/", postfix, imgfile, "\" alt=\"", "<div class=\"kcc1\"><img src=\"", "../" * backref, "Images/", postfix, imgfile, "\" alt=\"",
imgfile, "\" class=\"singlePage\"/></div>\n", imgfile, "\" class=\"kcc2\"/></div>\n",
#"<div id=\"", filename[0], "-1\">\n", #"<div id=\"", filename[0], "-1\">\n",
#"<a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"", filename[0], #"<a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"", filename[0],
#"-1-magTargetParent\", \"ordinal\":1}'></a>\n", #"-1-magTargetParent\", \"ordinal\":1}'></a>\n",
@@ -117,7 +121,7 @@ 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()
@@ -127,7 +131,7 @@ def buildNCX(dstdir, title, chapters):
def buildOPF(profile, dstdir, title, filelist, cover=None, righttoleft=False): def buildOPF(profile, dstdir, title, filelist, cover=None, righttoleft=False):
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 = image.ProfileData.Profiles[profile]
imgres = str(deviceres[0]) + "x" + str(deviceres[1]) imgres = str(deviceres[0]) + "x" + str(deviceres[1])
if righttoleft: if righttoleft:
writingmode = "horizontal-rl" writingmode = "horizontal-rl"
@@ -176,27 +180,31 @@ 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.profile == 'K4' or options.profile == 'KHD') 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("</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 \ if ((righttoleft and facing == 'left') or (not righttoleft and facing == 'right')) and \
(options.profile == 'K4' or options.profile == 'KHD'): (options.profile == 'K4' or options.profile == 'KHD'):
f.write("<itemref idref=\"blank-page\" properties=\"layout-blank\"/>\n") f.write("<itemref idref=\"blank-page" + str(splitCountUsed) + "\" properties=\"layout-blank\"/>\n")
splitCountUsed += 1
f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing1 + "\"/>\n") f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing1 + "\"/>\n")
elif entry.endswith("-2"): elif entry.endswith("-2"):
f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing2 + "\"/>\n") f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing2 + "\"/>\n")
@@ -248,18 +256,21 @@ def isInFilelist(filename, filelist):
def applyImgOptimization(img, isSplit=False, toRight=False): def applyImgOptimization(img, isSplit=False, toRight=False):
img.optimizeImage() img.optimizeImage(options.gamma)
img.cropWhiteSpace(10.0) img.cropWhiteSpace(10.0)
if options.cutpagenumbers: if options.cutpagenumbers:
img.cutPageNumber() img.cutPageNumber()
img.resizeImage(options.upscale, options.stretch, options.black_borders, isSplit, toRight) img.resizeImage(options.upscale, options.stretch, options.black_borders, isSplit, toRight)
if not options.notquantize:
img.quantizeImage() 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,9 +280,11 @@ 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)
if options.nosplitrotate:
split = None
else:
split = img.splitPage(dirpath, options.righttoleft, options.rotate) 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:
@@ -280,15 +293,27 @@ def dirImgProcess(path):
else: else:
toRight1 = True toRight1 = True
toRight2 = False toRight2 = False
if options.righttoleft:
if facing == "left":
splitCount += 1
facing = "right"
else:
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.notquantize)
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.notquantize)
else: else:
if facing == "right":
facing = "left"
else:
facing = "right"
applyImgOptimization(img) applyImgOptimization(img)
img.saveToDir(dirpath) img.saveToDir(dirpath, options.notquantize)
def genEpubStruct(path): def genEpubStruct(path):
@@ -297,6 +322,34 @@ def genEpubStruct(path):
chapterlist = [] chapterlist = []
cover = None cover = None
os.mkdir(os.path.join(path, 'OEBPS', 'Text')) os.mkdir(os.path.join(path, 'OEBPS', 'Text'))
f = open(os.path.join(path, 'OEBPS', 'Text', 'page_styles.css'), 'w')
f.writelines(["@page {\n",
" margin-bottom: 0;\n",
" margin-top: 0\n",
"}\n"])
f.close()
f = open(os.path.join(path, 'OEBPS', 'Text', 'stylesheet.css'), 'w')
f.writelines([".kcc {\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",
".kcc1 {\n",
" display: block;\n",
" text-align: center\n",
"}\n",
".kcc2 {\n",
" height: auto;\n",
" width: auto\n",
"}\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:
@@ -323,13 +376,12 @@ def genEpubStruct(path):
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, options.righttoleft)
if (options.profile == 'K4' or options.profile == 'KHD') and splittedSomething: if (options.profile == 'K4' or options.profile == 'KHD') 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 +390,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():
try:
path = cbx.extract(workdir) 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,38 +415,44 @@ 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, K4, 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("--noprocessing", action="store_false", dest="imgproc", default=True,
help="Verbose output [default=False]") help="Do not apply image preprocessing (Page splitting and optimizations) [Default=True]")
parser.add_option("--no-image-processing", action="store_false", dest="imgproc", default=True, parser.add_option("--nodithering", action="store_true", dest="notquantize", default=False,
help="Do not apply image preprocessing (page splitting and optimizations) [default=True]") help="Disable image quantization [Default=False]")
parser.add_option("--upscale-images", action="store_true", dest="upscale", default=False, parser.add_option("--gamma", type="float", dest="gamma", default="0.0",
help="Resize images smaller than device's resolution [default=False]") help="Apply gamma correction to linearize the image [Default=Auto]")
parser.add_option("--stretch-images", action="store_true", dest="stretch", default=False, parser.add_option("--upscale", action="store_true", dest="upscale", default=False,
help="Stretch images to device's resolution [default=False]") help="Resize images smaller than device's resolution [Default=False]")
parser.add_option("--black-borders", action="store_true", dest="black_borders", default=False, parser.add_option("--stretch", action="store_true", dest="stretch", default=False,
help="Use black borders (instead of white ones) when not stretching and ratio " help="Stretch images to device's resolution [Default=False]")
+ "is not like the device's one [default=False]") parser.add_option("--blackborders", action="store_true", dest="black_borders", default=False,
parser.add_option("--no-cut-page-numbers", action="store_false", dest="cutpagenumbers", default=True, help="Use black borders instead of white ones when not stretching and ratio "
help="Do not try to cut page numbering on images [default=True]") + "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)
if len(args) != 1: if len(args) != 1:
parser.print_help() parser.print_help()
@@ -397,10 +460,11 @@ def main(argv=None):
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:

View File

@@ -28,9 +28,11 @@ import comic2ebook
import kindlestrip import kindlestrip
from image import ProfileData from image import ProfileData
from subprocess import call from subprocess import call
from subprocess import check_call
import os import os
import shutil import shutil
import stat import stat
import traceback
class MainWindow: class MainWindow:
@@ -39,25 +41,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 +66,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,97 +80,147 @@ 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), 'Cimage_preprocess': IntVar(None, 0),
'cut_page_numbers': IntVar(None, 1), 'Dnotquantize': IntVar(None, 0),
'mangastyle': IntVar(None, 0), 'Eimage_gamma': DoubleVar(None, 0.0),
'image_upscale': IntVar(None, 0), 'Fimage_upscale': IntVar(None, 0),
'image_stretch': IntVar(None, 0), 'Gimage_stretch': IntVar(None, 0),
'black_borders': IntVar(None, 0) 'Hblack_borders': IntVar(None, 0),
'Irotate': IntVar(None, 0),
'Jnosplitrotate': IntVar(None, 0),
'Kcut_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", 'Cimage_preprocess': "Disable image optimizations",
'rotate': "Rotate landscape images instead of splitting them", 'Dnotquantize': "Disable image quantization",
'cut_page_numbers': "Cut page numbers", 'Jnosplitrotate': "Disable splitting and rotation",
'mangastyle': "Manga-style (right-to-left reading, applies to reading and splitting)", 'Irotate': "Rotate images instead splitting them",
'image_upscale': "Allow image upscaling", 'Kcut_page_numbers': "Disable page numbers cutting",
'image_stretch': "Stretch images", 'Bmangastyle': "Manga mode",
'black_borders': "Use black borders" 'Eimage_gamma': "Custom gamma correction",
'Fimage_upscale': "Allow image upscaling",
'Gimage_stretch': "Stretch images",
'Hblack_borders': "Use black borders"
} }
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['Eimage_gamma'].get() != 0.0:
argv.append("--no-image-processing") argv.append("--gamma")
if self.options['rotate'].get() == 1: argv.append(self.options['Eimage_gamma'].get())
if self.options['Cimage_preprocess'].get() == 1:
argv.append("--noprocessing")
if self.options['Dnotquantize'].get() == 1:
argv.append("--nodithering")
if self.options['Jnosplitrotate'].get() == 1:
argv.append("--nosplitrotate")
if self.options['Irotate'].get() == 1:
argv.append("--rotate") argv.append("--rotate")
if self.options['cut_page_numbers'].get() == 0: if self.options['Kcut_page_numbers'].get() == 1:
argv.append("--no-cut-page-numbers") argv.append("--nocutpagenumbers")
if self.options['mangastyle'].get() == 1: if self.options['Bmangastyle'].get() == 1:
argv.append("-m") argv.append("-m")
if self.options['image_upscale'].get() == 1: if self.options['Fimage_upscale'].get() == 1:
argv.append("--upscale-images") argv.append("--upscale")
if self.options['image_stretch'].get() == 1: if self.options['Gimage_stretch'].get() == 1:
argv.append("--stretch-images") argv.append("--stretch")
if self.options['black_borders'].get() == 1: if self.options['Hblack_borders'].get() == 1:
argv.append("--black-borders") argv.append("--blackborders")
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()
except Exception as err:
type_, value_, traceback_ = sys.exc_info()
tkMessageBox.showerror('KCC Error', "Error on file %s:\n%s\nTraceback:\n%s" %
(subargv[-1], str(err), traceback.format_tb(traceback_)))
errors = True errors = True
continue continue
if self.options['epub_only'] == 1: if self.options['Aepub_only'].get() == 0:
continue
try: try:
if os.path.getsize(epub_path) > 335544320:
# do not call kindlegen if source is bigger than 320MB
tkMessageBox.showwarning('KindleGen Warning',
"ePub file %s is bigger than 320MB, not suitable for kindlegen" %
epub_path)
continue
retcode = call("kindlegen \"" + epub_path + "\"", shell=True) retcode = call("kindlegen \"" + epub_path + "\"", shell=True)
if retcode < 0: if retcode < 0:
print >>sys.stderr, "Child was terminated by signal", -retcode print >>sys.stderr, "Child was terminated by signal", -retcode
else: else:
print >>sys.stderr, "Child returned", retcode print >>sys.stderr, "Child returned", retcode
self.progress_file['value'] = 3
self.master.update()
except OSError as e: except OSError as e:
tkMessageBox.showerror('Error kindlegen', "Error on file %s:\n%s" % (epub_path, e)) tkMessageBox.showerror('KindleGen Error', "Error on file %s:\n%s" % (epub_path, e))
errors = True errors = True
continue continue
mobifile = epub_path.replace('.epub', '.mobi') mobifile = epub_path.replace('.epub', '.mobi')
@@ -172,20 +228,23 @@ class MainWindow:
shutil.move(mobifile, mobifile + '_tostrip') shutil.move(mobifile, mobifile + '_tostrip')
kindlestrip.main((mobifile + '_tostrip', mobifile)) kindlestrip.main((mobifile + '_tostrip', mobifile))
os.remove(mobifile + '_tostrip') os.remove(mobifile + '_tostrip')
self.progress_file['value'] = 4
self.master.update()
except Exception, err: except Exception, err:
tkMessageBox.showerror('Error', "Error on file %s:\n%s" % (mobifile, str(err))) tkMessageBox.showerror('KindleStrip Error', "Error on file %s:\n%s" % (mobifile, str(err)))
errors = True errors = True
continue continue
if errors:
tkMessageBox.showinfo(
"Done",
"Conversion finished (some errors have been reported)"
)
else: else:
tkMessageBox.showinfo( self.progress_file['value'] = 4
"Done", self.master.update()
"Conversion successfully done!" if errors:
) tkMessageBox.showwarning("Done", "Conversion completed with errors.")
else:
tkMessageBox.showinfo("Done", "Conversion successful!")
# reset progressbars
self.progress_overall['value'] = 0
self.progress_file['value'] = 0
self.master.update()
def remove_readonly(self, fn, path): def remove_readonly(self, fn, path):
if fn is os.rmdir: if fn is os.rmdir:
@@ -195,8 +254,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()

View File

@@ -77,13 +77,13 @@ class ProfileData:
] ]
Profiles = { Profiles = {
'K1': ("Kindle", (600, 800), Palette4), 'K1': ("Kindle", (600, 800), Palette4, 1.8),
'K2': ("Kindle 2", (600, 800), Palette15), 'K2': ("Kindle 2", (600, 800), Palette15, 1.8),
'K3': ("Kindle 3/Keyboard", (600, 800), Palette16), 'K3': ("Kindle 3/Keyboard", (600, 800), Palette16, 1.8),
'K4': ("Kindle 4/NT/Touch", (600, 800), Palette16), 'K4': ("Kindle 4/NT/Touch", (600, 800), Palette16, 1.8),
'KHD': ("Kindle Paperwhite", (758, 1024), Palette16), 'KHD': ("Kindle Paperwhite", (758, 1024), Palette16, 1.8),
'KDX': ("Kindle DX", (824, 1200), Palette15), 'KDX': ("Kindle DX", (824, 1200), Palette15, 1.8),
'KDXG': ("Kindle DXG", (824, 1200), Palette16) 'KDXG': ("Kindle DXG", (824, 1200), Palette16, 1.8)
} }
ProfileLabels = { ProfileLabels = {
@@ -101,7 +101,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 = ProfileData.Profiles[device]
except KeyError: except KeyError:
raise RuntimeError('Unexpected output device %s' % device) raise RuntimeError('Unexpected output device %s' % device)
try: try:
@@ -111,16 +111,25 @@ 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, notquantize):
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 notquantize:
self.image.save(os.path.join(targetdir, os.path.splitext(filename)[0] + ".jpg"), "JPEG")
else:
self.image.save(os.path.join(targetdir, os.path.splitext(filename)[0] + ".png"), "PNG")
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):
if gamma < 0.1:
gamma = self.gamma
if gamma == 1.0:
self.image = ImageOps.autocontrast(self.image) 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):
colors = len(self.palette) / 3 colors = len(self.palette) / 3

View File

@@ -70,3 +70,4 @@ class PdfJpgExtract:
njpg += 1 njpg += 1
i = iend i = iend
return self.path

View File

@@ -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.

View File

@@ -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

View File

@@ -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
NAME = "KindleComicConverter"
VERSION = "2.7"
MAIN = "kcc.py"
includefiles = ['README.md', 'MANIFEST.in', 'LICENSE.txt', 'comic2ebook.ico', 'comic2ebook.icns']
includes = []
excludes = []
if sys.platform == "darwin":
from setuptools import setup from setuptools import setup
NAME = 'KindleComicConverter'
VERSION = "2.6"
mainscript = 'kcc.py'
if sys.platform == 'darwin':
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
View 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.7",
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)]
)

View File

@@ -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
)