Merge remote-tracking branch 'catmanjan/master' into dev

This commit is contained in:
Alex Yatskov 2013-11-22 07:43:22 -08:00
commit be4cd7e44c
14 changed files with 180 additions and 79 deletions

10
README.md Normal file
View File

@ -0,0 +1,10 @@
## Downloads
- [Mangle Version 3.0.0](https://github.com/catmanjan/mangle/releases/download/3/mangle_3.zip) (2013-10-11)
## For Developers
- [Python 2.7](http://www.python.org/download/releases/2.7/)
- [Python Image Library (PIL)](http://www.pythonware.com/products/pil/)
- [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)

View File

@ -17,7 +17,9 @@
import sys
from PyQt4 import QtGui
from mangle.book import MainWindowBook

View File

@ -14,12 +14,12 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os.path
import util
from PyQt4 import QtGui, uic
import util
class DialogAbout(QtGui.QDialog):
def __init__(self, parent):
QtGui.QDialog.__init__(self, parent)
uic.loadUi(util.buildResPath('ui/about.ui'), self)
uic.loadUi(util.buildResPath('mangle/ui/about.ui'), self)

View File

@ -14,22 +14,24 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import os.path
from os.path import basename
import util
import os.path
import tempfile
from PyQt4 import QtGui, QtCore, QtXml, uic
from zipfile import ZipFile
from image import ImageFlags
from PyQt4 import QtGui, QtCore, QtXml, uic
from natsort import natsorted
from about import DialogAbout
from options import DialogOptions
from convert import DialogConvert
from image import ImageFlags
from options import DialogOptions
import util
class Book(object):
DefaultDevice = 'Kindle 4'
DefaultOutputFormat = 'PDF only'
DefaultDevice = 'Kindle Paperwhite'
DefaultOutputFormat = 'CBZ only'
DefaultOverwrite = True
DefaultImageFlags = ImageFlags.Orient | ImageFlags.Resize | ImageFlags.Quantize
@ -39,6 +41,7 @@ class Book(object):
self.filename = None
self.modified = False
self.title = None
self.titleSet = False
self.device = Book.DefaultDevice
self.overwrite = Book.DefaultOverwrite
self.imageFlags = Book.DefaultImageFlags
@ -115,7 +118,7 @@ class MainWindowBook(QtGui.QMainWindow):
def __init__(self, filename=None):
QtGui.QMainWindow.__init__(self)
uic.loadUi(util.buildResPath('ui/book.ui'), self)
uic.loadUi(util.buildResPath('mangle/ui/book.ui'), self)
self.listWidgetFiles.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.actionFileNew.triggered.connect(self.onFileNew)
self.actionFileOpen.triggered.connect(self.onFileOpen)
@ -221,6 +224,7 @@ class MainWindowBook(QtGui.QMainWindow):
def onBookAddDirectory(self):
directory = QtGui.QFileDialog.getExistingDirectory(self, 'Select an image directory to add')
if not directory.isNull():
self.book.title = os.path.basename(os.path.normpath(unicode(directory)))
self.addImageDirs([directory])
@ -238,7 +242,8 @@ class MainWindowBook(QtGui.QMainWindow):
def onBookOptions(self):
dialog = DialogOptions(self, self.book)
dialog.exec_()
if dialog.exec_() == QtGui.QDialog.Accepted:
self.book.titleSet = True
def onBookExport(self):
@ -246,10 +251,12 @@ class MainWindowBook(QtGui.QMainWindow):
QtGui.QMessageBox.warning(self, 'Mangle', 'This book has no images to export')
return
if self.book.title is None:
if not self.book.titleSet: # if self.book.title is None:
dialog = DialogOptions(self, self.book)
if dialog.exec_() == QtGui.QDialog.Rejected:
return
else:
self.book.titleSet = True
directory = QtGui.QFileDialog.getExistingDirectory(self, 'Select a directory to export book to')
if not directory.isNull():
@ -259,7 +266,7 @@ class MainWindowBook(QtGui.QMainWindow):
def onHelpHomepage(self):
services = QtGui.QDesktopServices()
services.openUrl(QtCore.QUrl('http://foosoft.net/mangle'))
services.openUrl(QtCore.QUrl('https://github.com/catmanjan/mangle'))
def onHelpAbout(self):
@ -357,13 +364,11 @@ class MainWindowBook(QtGui.QMainWindow):
def addImageFiles(self, filenames):
filenames.sort()
filenamesListed = []
for i in xrange(0, self.listWidgetFiles.count()):
filenamesListed.append(self.listWidgetFiles.item(i).text())
for filename in filenames:
for filename in natsorted(filenames):
if filename not in filenamesListed:
filename = QtCore.QString(filename)
self.listWidgetFiles.addItem(filename)
@ -374,7 +379,7 @@ class MainWindowBook(QtGui.QMainWindow):
filenames = []
for directory in directories:
for root, subdirs, subfiles in os.walk(unicode(directory)):
for root, _, subfiles in os.walk(unicode(directory)):
for filename in subfiles:
path = os.path.join(root, filename)
if self.isImageFile(path):
@ -398,16 +403,15 @@ class MainWindowBook(QtGui.QMainWindow):
for f in cbzFile.namelist():
if f.endswith('/'):
try:
os.makedirs(path+f)
os.makedirs(path + f)
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:
cbzFile.extract(f, path)
#Add the directories
if os.path.isdir(unicode(path)):
if os.path.isdir(unicode(path)): # Add the directories
directories.append(path)
#Add the files
self.addImageDirs(directories)
self.addImageDirs(directories) # Add the files
def isImageFile(self, filename):

View File

@ -14,10 +14,14 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os, shutil
import os
import shutil
from PyQt4 import QtGui, QtCore
import image
from image import ImageFlags
import cbz
import image
import pdfimage
@ -39,7 +43,7 @@ class DialogConvert(QtGui.QProgressDialog):
self.pdf = None
if "PDF" in self.book.outputFormat:
self.pdf = pdfimage.PDFImage(self.bookPath, str(self.book.title), str(self.book.device))
self.pdf = pdfimage.PDFImage(self.bookPath, str(self.book.title), str(self.book.device))
@ -56,15 +60,23 @@ class DialogConvert(QtGui.QProgressDialog):
# Close the archive if we created a CBZ file
if self.archive is not None:
self.archive.close()
#Close and generate the PDF File
# Close and generate the PDF File
if self.pdf is not None:
self.pdf.close()
self.pdf.close()
# Remove image directory if the user didn't wish for images
if 'Image' not in self.book.outputFormat:
shutil.rmtree(self.bookPath)
def convertAndSave(self, source, target, device, flags, archive, pdf):
image.convertImage(source, target, device, flags)
if archive is not None:
archive.addFile(target)
if pdf is not None:
pdf.addImage(target)
def onTimer(self):
index = self.value()
target = os.path.join(self.bookPath, '%05d.png' % index)
@ -104,11 +116,22 @@ class DialogConvert(QtGui.QProgressDialog):
try:
if self.book.overwrite or not os.path.isfile(target):
image.convertImage(source, target, str(self.book.device), self.book.imageFlags)
if self.archive is not None:
self.archive.addFile(target)
if self.pdf is not None:
self.pdf.addImage(target)
device = str(self.book.device)
flags = self.book.imageFlags
archive = self.archive
pdf = self.pdf
# For right page (if requested)
if(self.book.imageFlags & ImageFlags.Split):
# New path based on modified index
target = os.path.join(self.bookPath, '%05d.png' % (index * 2 + 0))
self.convertAndSave(source, target, device, flags ^ ImageFlags.Split | ImageFlags.SplitRight, 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)
except RuntimeError, error:
result = QtGui.QMessageBox.critical(
self,

View File

@ -14,6 +14,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
from PIL import Image, ImageDraw
@ -23,6 +25,8 @@ class ImageFlags:
Frame = 1 << 2
Quantize = 1 << 3
Stretch = 1 << 4
Split = 1 << 5
SplitRight = 1 << 6
class KindleData:
@ -74,11 +78,25 @@ class KindleData:
'Kindle 2': ((600, 800), Palette15a),
'Kindle 3': ((600, 800), Palette15a),
'Kindle 4': ((600, 800), Palette15b),
'Kindle 5': ((600, 800), Palette15b),
'Kindle DX': ((824, 1200), Palette15a),
'Kindle DXG': ((824, 1200), Palette15a)
'Kindle DXG': ((824, 1200), Palette15a),
'Kindle Paperwhite': ((758, 1024), Palette15b)
}
def splitLeft(image):
widthImg, heightImg = image.size
return image.crop((0, 0, widthImg / 2, heightImg))
def splitRight(image):
widthImg, heightImg = image.size
return image.crop((widthImg / 2, 0, widthImg, heightImg))
def quantizeImage(image, palette):
colors = len(palette) / 3
if colors < 256:
@ -91,8 +109,8 @@ def quantizeImage(image, palette):
def stretchImage(image, size):
widthDev, heightDev = size
return image.resize((widthDev, heightDev), Image.ANTIALIAS)
widthDev, heightDev = size
return image.resize((widthDev, heightDev), Image.ANTIALIAS)
def resizeImage(image, size):
widthDev, heightDev = size
@ -161,29 +179,43 @@ def frameImage(image, foreground, background, size):
return imageBg
def loadImage(source):
try:
return Image.open(source)
except IOError:
raise RuntimeError('Cannot read image file %s' % source)
def saveImage(image, target):
try:
image.save(target)
except IOError:
raise RuntimeError('Cannot write image file %s' % target)
def convertImage(source, target, device, flags):
try:
size, palette = KindleData.Profiles[device]
except KeyError:
raise RuntimeError('Unexpected output device %s' % device)
try:
image = Image.open(source)
except IOError:
raise RuntimeError('Cannot read image file %s' % source)
# Load image from source path
image = loadImage(source)
# Format according to palette
image = formatImage(image)
# Apply flag transforms
if flags & ImageFlags.SplitRight:
image = splitRight(image)
if flags & ImageFlags.Split:
image = splitLeft(image)
if flags & ImageFlags.Orient:
image = orientImage(image, size)
image = orientImage(image, size)
if flags & ImageFlags.Resize:
image = resizeImage(image, size)
image = resizeImage(image, size)
if flags & ImageFlags.Stretch:
image = stretchImage(image, size)
image = stretchImage(image, size)
if flags & ImageFlags.Frame:
image = frameImage(image, tuple(palette[:3]), tuple(palette[-3:]), size)
image = frameImage(image, tuple(palette[:3]), tuple(palette[-3:]), size)
if flags & ImageFlags.Quantize:
image = quantizeImage(image, palette)
image = quantizeImage(image, palette)
try:
image.save(target)
except IOError:
raise RuntimeError('Cannot write image file %s' % target)
saveImage(image, target)

View File

@ -14,17 +14,17 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os.path
import util
from PyQt4 import QtGui, QtCore, uic
from PyQt4 import QtGui, uic
from image import ImageFlags
import util
class DialogOptions(QtGui.QDialog):
def __init__(self, parent, book):
QtGui.QDialog.__init__(self, parent)
uic.loadUi(util.buildResPath('ui/options.ui'), self)
uic.loadUi(util.buildResPath('mangle/ui/options.ui'), self)
self.accepted.connect(self.onAccept)
self.book = book
@ -59,11 +59,13 @@ class DialogOptions(QtGui.QDialog):
if self.checkboxResize.isChecked():
imageFlags |= ImageFlags.Resize
if self.checkboxStretch.isChecked():
imageFlags |= ImageFlags.Stretch
imageFlags |= ImageFlags.Stretch
if self.checkboxQuantize.isChecked():
imageFlags |= ImageFlags.Quantize
if self.checkboxFrame.isChecked():
imageFlags |= ImageFlags.Frame
imageFlags |= ImageFlags.Frame
if self.checkboxSplit.isChecked():
imageFlags |= ImageFlags.Split
modified = (
self.book.title != title or

View File

@ -17,9 +17,10 @@
import os.path
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter
from image import KindleData
class PDFImage(object):
def __init__(self, path, title, device):
outputDirectory = os.path.dirname(path)
@ -28,7 +29,7 @@ class PDFImage(object):
self.currentDevice = device
self.bookTitle = title
self.pageSize = KindleData.Profiles[self.currentDevice][0]
#pagesize could be letter or A4 for standarization but we need to control some image sizes
# pagesize could be letter or A4 for standarization but we need to control some image sizes
self.canvas = canvas.Canvas(outputPath, pagesize=self.pageSize)
self.canvas.setAuthor("Mangle")
self.canvas.setTitle(self.bookTitle)

View File

@ -49,9 +49,9 @@
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Sans'; font-size:14pt;&quot;&gt;Mangle&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Sans';&quot;&gt;Version 2.3&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Sans';&quot;&gt;Version 3&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans';&quot;&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Sans';&quot;&gt;Manga processor by Alex Yatskov for the Kindle e-book reader. Please see &lt;/span&gt;&lt;span style=&quot; font-family:'Sans'; font-style:italic;&quot;&gt;license.txt&lt;/span&gt;&lt;span style=&quot; font-family:'Sans';&quot;&gt; for licensing information. Visit the homepage at &lt;/span&gt;&lt;a href=&quot;http://foosoft.net/mangle&quot;&gt;&lt;span style=&quot; font-family:'Sans'; text-decoration: underline; color:#0000ff;&quot;&gt;http://foosoft.net/mangle&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-family:'Sans';&quot;&gt;.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Sans';&quot;&gt;Manga processor for the Kindle e-book reader. Please see &lt;/span&gt;&lt;span style=&quot; font-family:'Sans'; font-style:italic;&quot;&gt;license.txt&lt;/span&gt;&lt;span style=&quot; font-family:'Sans';&quot;&gt; for licensing information. Visit the homepage at &lt;/span&gt;&lt;a href=&quot;https://github.com/catmanjan/mangle&quot;&gt;&lt;span style=&quot; font-family:'Sans'; text-decoration: underline; color:#0000ff;&quot;&gt;https://github.com/catmanjan/mangle&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-family:'Sans';&quot;&gt;.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>

View File

@ -76,6 +76,11 @@
<string>Kindle 4</string>
</property>
</item>
<item>
<property name="text">
<string>Kindle 5</string>
</property>
</item>
<item>
<property name="text">
<string>Kindle DX</string>
@ -86,6 +91,11 @@
<string>Kindle DXG</string>
</property>
</item>
<item>
<property name="text">
<string>Kindle Paperwhite</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
@ -148,6 +158,13 @@
<string>Draw frame around images</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkboxSplit">
<property name="text">
<string>Split images into two pages (right, left)</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3">

View File

@ -15,8 +15,9 @@
import os.path
import sys
def buildResPath(relative):
directory = os.path.dirname(__file__)
directory = os.path.dirname(os.path.realpath(sys.argv[0]))
return os.path.join(directory, relative)

10
nodist
View File

@ -1,10 +0,0 @@
.git
.gitignore
ref
build
dist
nodist
README
*.pyw
*.pyc
*.py

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python
# Copyright (C) 2010 Alex Yatskov
# Copyright (C) 2013 Jan Martin
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -16,14 +16,33 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys
from distutils.core import setup
import py2exe
import sys
sys.argv.append('py2exe')
setup(
name='Mangle',
windows=[{'script': 'mangle.pyw'}],
options={'py2exe': {'bundle_files': 1, 'includes': ['sip']}},
data_files=[('', ['LICENSE']),
('mangle/ui', ['mangle/ui/book.ui',
'mangle/ui/about.ui',
'mangle/ui/options.ui']),
('mangle/img', ['mangle/img/add_directory.png',
'mangle/img/add_file.png',
'mangle/img/banner_about.png',
'mangle/img/export_book.png',
'mangle/img/file_new.png',
'mangle/img/file_open.png',
'mangle/img/remove_files.png',
'mangle/img/save_file.png',
'mangle/img/shift_down.png',
'mangle/img/shift_up.png'])],
options={'py2exe': {
'bundle_files': 1,
'includes': ['sip'],
'packages': ['reportlab.pdfbase'],
'dll_excludes': ['w9xpopen.exe']
}},
zipfile=None
)