diff --git a/.gitignore b/.gitignore
index d10b6b6..ae3a8a2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@ dist
*.pyo
*.mngl
*.cbz
+*.bat
diff --git a/README.md b/README.md
index 5068319..42cbdda 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,4 @@
+<<<<<<< HEAD
## Downloads
- [Mangle for Windows](ftp://foosoft.net/releases/mangle/mangle_win.zip)
- [Mangle for MacOS (Old)](ftp://foosoft.net/releases/mangle/mangle_osx.zip)
@@ -8,4 +9,101 @@
- [PyQT4](http://www.riverbankcomputing.com/software/pyqt/download)
- [ReportLab](https://pypi.python.org/pypi/reportlab)
- [natsort](https://pypi.python.org/pypi/natsort/3.0.1)
-- [py2exe](http://www.py2exe.org/) (optional, for Windows distribution only)
\ No newline at end of file
+- [py2exe](http://www.py2exe.org/) (optional, for Windows distribution only)
+=======
+# Mangle #
+
+Many years ago I received an [Amazon Kindle](http://en.wikipedia.org/wiki/Kindle) gift. I immediately began playing
+around with it and reading about certain undocumented features that the Kindle has to offer. After a couple of hours I
+discovered it to be the perfect device for reading [Manga](http://en.wikipedia.org/wiki/Manga) is almost always
+grayscale, and the aspect ratio fits the Kindle's 600x800 pixel screen almost perfectly. Better yet, the Kindle's
+undocumented image viewer actually keeps track of the last image you viewed and thus you are always able to return to
+the page you left off on when you power on your Kindle. The device supports several popular image formats (jpeg, png,
+gif, etc), and is able to dither and downscale images to fit the screen.
+
+However... The Kindle's image viewer does have certain shortcomings:
+
+* The Kindle is very picky about file format; any additional embedded data (thumbnails, comments, possibly even exif
+ data) can confuse it. As a result, images may not display properly or even not at all (which actually prevents you
+ from reading the given book, as one bad panel will prevent you from viewing subsequent images).
+* The first image that you view in a Manga (until the Kindle first writes the "bookmark" file) seems to be arbitrary
+ even when files are named sequentially. About half the time it will correctly pick the first file in the batch, at
+ other times it will pick out some other image seemingly at random.
+* Normally for Kindle to find your Manga scans you have to press Alt+Z on the home screen. I haven't always had luck
+ with it correctly identifying image directories. At other times, after finding an image directory the Kindle will
+ appear to hang while trying to access it (forcing you to return to the home screen).
+* The Kindle image viewer has no functionality to rotate images. So if there is a horizontally large image (such as
+ what often happens with dual-page scans), it can be difficult to make out the text because the image is simply
+ scaled to fit (consequently leaving a lot of wasted space at the bottom of the screen).
+* Scanlation images are oftentimes much larger than the 600x800 screen; not only does this make them take more space
+ on your memory card but it also slows down image loading (the Kindle has to read more data off of the slow SD card
+ and scale the image). Scanlations often also include color scans of covers and inserts which take up more space than
+ a grayscale equivalent (which is would be fine for the Kindle's limited display).
+* Kindle's image viewer provides no way to sort images (to determine in which order they are shown). This can be very
+ problematic especially considering that scanlation groups have differing naming conventions, and as a result files
+ from later chapters may appear before earlier ones when you are reading your Manga (spoilers ftl).
+
+I was annoyed with these issues and thus Mangle was born (the program name is a mix of "Manga" and "Kindle" in case you
+haven't figured it out yet; I thought it was pretty clever at the time). Fortunately you can get all the benefits of my
+work without really doing anything (and it won't even cost you anything since Mangle is free,
+[GPL](http://www.gnu.org/licenses/gpl-3.0.txt) software. With Mangle you can easily:
+
+* Sort and organize images from different directories; bulk rename feature for output to the Kindle.
+* Optionally re-save images in a format Kindle will be sure to understand with no visible quality loss.
+* Downsample and rotate images for optimal viewing on Kindle, convert to grayscale to save space and improve contrast.
+* Automatically generate book meta-data so that your Manga is always properly detected and viewable in-order.
+
+Here is a recent screenshot showing off some of the export options that you can configure on a per-book basis in Mangle:
+
+![Mangle options dialog](http://foosoft.net/projects/mangle/img/options.png)
+
+You can also check out what Mangle output looks like on the Kindle on the [action
+shots](http://foosoft.net/projects/mangle/action/) page.
+
+Mangle is cross platform, and doesn't require an install (it's a standalone executable that you can run from anywhere).
+It is also "environmentally friendly" by not messing with your registry or modifying your system in any way. If you
+ever want to uninstall it, just delete the executable and you're done.
+
+## Usage Instructions ##
+
+Mangle is pretty easy to use, so this won't be really in-depth. If you have any questions drop me a line though.
+
+1. Add images to the current book by selecting the `Book | Add | Files` or `Book | Add | Directory` menu items.
+2. If certain images are not in the order you want, select them in the window, and select the `Book | Shift | Up` or
+ `Book | Shift | Down` menu items.
+3. Configure the book title and image processing options by selecting `Book | Options`; this will be the title you see
+ in the Kindle home menu.
+4. Create a root-level directory on your SD memory card/Kindle called `pictures` (case might matter).
+5. Once you are satisfied with the your images and options select `Book | Export` and select the `pictures` directory
+ you just created.
+6. After the export is complete your new Manga books will show up along with all your other books (if they don't for
+ some reason, press `Alt+Z` while on the home menu).
+
+## The Usual Disclaimer ##
+
+You probably know how this goes by now... Mess around with your Kindle at your own risk. Honestly, nothing bad is going
+to happen; however if something *does* then it's your problem.
+
+## Running From Source ##
+
+Because Mangle is written in Python, a scripting language, it's trivial to get it up and running on the operating system
+of your choice. First you should make sure that you have the required dependencies installed:
+
+* [PyQT4](http://www.riverbankcomputing.com/software/pyqt/download)
+* [Python 2.7](http://www.python.org/download/releases/2.7/)
+* [Python Imaging Library (PIL)](http://www.pythonware.com/products/pil/)
+* [ReportLab](https://pypi.python.org/pypi/reportlab)
+* [py2exe](http://www.py2exe.org/) (optional, for Windows distribution only)
+
+Now you can fetch the [latest version of the code](https://github.com/FooSoft/mangle/) and run the `mangle.pyw` script
+to execute Mangle.
+
+## Downloads ##
+
+If you don't want to run Mangle from source, you can use the following pre-built binaries. As I don't have the means to
+make MacOS X releases myself, I am providing the slightly out of date (and unsupported) package built by Rob White in
+its place. Linux users should execute the Python scripts with the interpreter and libraries installed on their system.
+
+* [magnle_win.zip](http://dl.foosoft.net/mangle/mangle_win.zip)
+* [mangle_osx.zip](http://dl.foosoft.net/mangle/mangle_osx.zip)
+>>>>>>> cedfe3fc512a8f58680d8fa0618143d423c3112d
diff --git a/mangle/book.py b/mangle/book.py
index 1bc8c28..2079f8a 100644
--- a/mangle/book.py
+++ b/mangle/book.py
@@ -13,14 +13,13 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-
+import re
from os.path import basename
import os.path
import tempfile
from zipfile import ZipFile
from PyQt4 import QtGui, QtCore, QtXml, uic
-from natsort import natsorted
from about import DialogAbout
from convert import DialogConvert
@@ -29,6 +28,13 @@ from options import DialogOptions
import util
+# Sort function use to sort files in a natural order, by lowering
+# characters, and manage multi levels of integers (tome 1/ page 1.jpg, etc etc)
+def natural_key(string_):
+ """See http://www.codinghorror.com/blog/archives/001018.html"""
+ return [int(s) if s.isdigit() else s.lower() for s in re.split(r'(\d+)', string_)]
+
+
class Book(object):
DefaultDevice = 'Kindle Paperwhite'
DefaultOutputFormat = 'CBZ only'
@@ -367,14 +373,16 @@ class MainWindowBook(QtGui.QMainWindow):
filenamesListed = []
for i in xrange(0, self.listWidgetFiles.count()):
filenamesListed.append(self.listWidgetFiles.item(i).text())
-
- for filename in natsorted(filenames):
+
+ # Get files but in a natural sorted order
+ for filename in sorted(filenames, key=natural_key):
if filename not in filenamesListed:
filename = QtCore.QString(filename)
self.listWidgetFiles.addItem(filename)
self.book.images.append(filename)
self.book.modified = True
+
def addImageDirs(self, directories):
filenames = []
@@ -387,6 +395,7 @@ class MainWindowBook(QtGui.QMainWindow):
self.addImageFiles(filenames)
+
def addCBZFiles(self, filenames):
directories = []
tempDir = tempfile.gettempdir()
diff --git a/mangle/convert.py b/mangle/convert.py
index a9258fb..d9bdf8f 100644
--- a/mangle/convert.py
+++ b/mangle/convert.py
@@ -129,6 +129,14 @@ class DialogConvert(QtGui.QProgressDialog):
# Change target once again for left page
target = os.path.join(self.bookPath, '%05d.png' % (index * 2 + 1))
+ # For right page (if requested), but in inverted mode
+ if(self.book.imageFlags & ImageFlags.SplitInverse):
+ # New path based on modified index
+ target = os.path.join(self.bookPath, '%05d.png' % (index * 2 + 0))
+ self.convertAndSave(source, target, device, flags ^ ImageFlags.SplitInverse | ImageFlags.SplitLeft, archive, pdf)
+ # Change target once again for left page
+ target = os.path.join(self.bookPath, '%05d.png' % (index * 2 + 1))
+
# Convert page
self.convertAndSave(source, target, device, flags, archive, pdf)
diff --git a/mangle/image.py b/mangle/image.py
index edf2928..2ce185d 100644
--- a/mangle/image.py
+++ b/mangle/image.py
@@ -19,14 +19,17 @@ import os
from PIL import Image, ImageDraw
+
class ImageFlags:
Orient = 1 << 0
Resize = 1 << 1
Frame = 1 << 2
Quantize = 1 << 3
Stretch = 1 << 4
- Split = 1 << 5
- SplitRight = 1 << 6
+ Split = 1 << 5 # split right then left
+ SplitRight = 1 << 6 # split only the right page
+ SplitLeft = 1 << 7 # split only the left page
+ SplitInverse = 1 << 8 # split left then right page
class KindleData:
@@ -82,22 +85,39 @@ class KindleData:
'Kindle DX': ((824, 1200), Palette15a),
'Kindle DXG': ((824, 1200), Palette15a),
'Kindle Touch': ((600, 800), Palette15a),
- 'Kindle Paperwhite': ((758, 1024), Palette15b) # resolution given in manual, see http://kindle.s3.amazonaws.com/Kindle_Paperwhite_Users_Guide.pdf
+ 'Kindle Paperwhite': ((758, 1024), Palette15b), # resolution given in manual, see http://kindle.s3.amazonaws.com/Kindle_Paperwhite_Users_Guide.pdf
+ 'KoBo Aura H2o': ((1080, 1430), Palette15a), # resolution from http://www.fnac.com/Liseuse-Numerique-Kobo-by-Fnac-Kobo-Aura-H2O-Noir/a7745120/w-4
}
+
+
+# decorate a function that use image, *** and if there
+# is an exception raise by PIL (IOError) then return
+# the original image because PIL cannot manage it
+def protect_bad_image(func):
+ def func_wrapper(*args, **kwargs):
+ # If cannot convert (like a bogus image) return the original one
+ # args will be "image" and other params are after
+ try:
+ return func(*args, **kwargs)
+ except IOError: # Exception from PIL about bad image
+ return args[0]
+ return func_wrapper
-
+
+@protect_bad_image
def splitLeft(image):
widthImg, heightImg = image.size
-
return image.crop((0, 0, widthImg / 2, heightImg))
+@protect_bad_image
def splitRight(image):
widthImg, heightImg = image.size
-
+
return image.crop((widthImg / 2, 0, widthImg, heightImg))
+@protect_bad_image
def quantizeImage(image, palette):
colors = len(palette) / 3
if colors < 256:
@@ -109,10 +129,14 @@ def quantizeImage(image, palette):
return image.quantize(palette=palImg)
+@protect_bad_image
def stretchImage(image, size):
widthDev, heightDev = size
+
return image.resize((widthDev, heightDev), Image.ANTIALIAS)
+
+@protect_bad_image
def resizeImage(image, size):
widthDev, heightDev = size
widthImg, heightImg = image.size
@@ -136,19 +160,21 @@ def resizeImage(image, size):
return image.resize((widthImg, heightImg), Image.ANTIALIAS)
+@protect_bad_image
def formatImage(image):
if image.mode == 'RGB':
return image
+
return image.convert('RGB')
+@protect_bad_image
def orientImage(image, size):
widthDev, heightDev = size
widthImg, heightImg = image.size
if (widthImg > heightImg) != (widthDev > heightDev):
return image.rotate(90, Image.BICUBIC, True)
-
return image
@@ -204,10 +230,20 @@ def convertImage(source, target, device, flags):
# Format according to palette
image = formatImage(image)
# Apply flag transforms
+
+ # Second pass of first split
if flags & ImageFlags.SplitRight:
image = splitRight(image)
- if flags & ImageFlags.Split:
+ # First pass of first split option
+ if (flags & ImageFlags.Split):
image = splitLeft(image)
+ # First pass of second splitting option
+ if flags & ImageFlags.SplitLeft:
+ image = splitLeft(image)
+ # second pass of second splitting option
+ if (flags & ImageFlags.SplitInverse):
+ image = splitRight(image)
+
if flags & ImageFlags.Orient:
image = orientImage(image, size)
if flags & ImageFlags.Resize:
diff --git a/mangle/img/book.png b/mangle/img/book.png
new file mode 100644
index 0000000..aee4342
Binary files /dev/null and b/mangle/img/book.png differ
diff --git a/mangle/img/export_book.png b/mangle/img/export_book.png
index 78525cc..f90be0a 100644
Binary files a/mangle/img/export_book.png and b/mangle/img/export_book.png differ
diff --git a/mangle/options.py b/mangle/options.py
index eb79f7f..eedfe88 100644
--- a/mangle/options.py
+++ b/mangle/options.py
@@ -66,6 +66,8 @@ class DialogOptions(QtGui.QDialog):
imageFlags |= ImageFlags.Frame
if self.checkboxSplit.isChecked():
imageFlags |= ImageFlags.Split
+ if self.checkboxSplitInverse.isChecked():
+ imageFlags |= ImageFlags.SplitInverse
modified = (
self.book.title != title or
diff --git a/mangle/ui/book.ui b/mangle/ui/book.ui
index a0121a4..8066c7c 100644
--- a/mangle/ui/book.ui
+++ b/mangle/ui/book.ui
@@ -180,6 +180,10 @@
+
+
+ ../img/book.png../img/book.png
+
&Options...
diff --git a/mangle/ui/options.ui b/mangle/ui/options.ui
index 1c903ae..e5af54a 100644
--- a/mangle/ui/options.ui
+++ b/mangle/ui/options.ui
@@ -101,6 +101,11 @@
Kindle Paperwhite
+ -
+
+ KoBo Aura H2o
+
+
-
@@ -170,6 +175,13 @@
Split images into two pages (right, left)
+
+ -
+
+
+ Split images into two pages (left, right)
+
+
-
diff --git a/setup.py b/setup.py
index 701ca74..1f774fc 100644
--- a/setup.py
+++ b/setup.py
@@ -32,6 +32,7 @@ setup(
('mangle/img', ['mangle/img/add_directory.png',
'mangle/img/add_file.png',
'mangle/img/banner_about.png',
+ 'mangle/img/book.png',
'mangle/img/export_book.png',
'mangle/img/file_new.png',
'mangle/img/file_open.png',