commit
6ca4accc7a
8
README
8
README
@ -0,0 +1,8 @@
|
|||||||
|
REQUIREMENTS
|
||||||
|
============
|
||||||
|
|
||||||
|
Python Image Library (PIL)
|
||||||
|
PyQT4
|
||||||
|
Reportlab
|
||||||
|
|
||||||
|
Mangle 2.4 Unofficial version
|
@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python2
|
#!/usr/bin/env python
|
||||||
|
|
||||||
# Copyright (C) 2010 Alex Yatskov
|
# Copyright (C) 2010 Alex Yatskov
|
||||||
#
|
#
|
||||||
|
@ -16,8 +16,11 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
|
from os.path import basename
|
||||||
import util
|
import util
|
||||||
|
import tempfile
|
||||||
from PyQt4 import QtGui, QtCore, QtXml, uic
|
from PyQt4 import QtGui, QtCore, QtXml, uic
|
||||||
|
from zipfile import ZipFile
|
||||||
from image import ImageFlags
|
from image import ImageFlags
|
||||||
from about import DialogAbout
|
from about import DialogAbout
|
||||||
from options import DialogOptions
|
from options import DialogOptions
|
||||||
@ -27,7 +30,7 @@ from natsort import natsorted
|
|||||||
|
|
||||||
class Book(object):
|
class Book(object):
|
||||||
DefaultDevice = 'Kindle 4'
|
DefaultDevice = 'Kindle 4'
|
||||||
DefaultOutputFormat = 'Images & CBZ'
|
DefaultOutputFormat = 'PDF only'
|
||||||
DefaultOverwrite = True
|
DefaultOverwrite = True
|
||||||
DefaultImageFlags = ImageFlags.Orient | ImageFlags.Resize | ImageFlags.Quantize
|
DefaultImageFlags = ImageFlags.Orient | ImageFlags.Resize | ImageFlags.Quantize
|
||||||
|
|
||||||
@ -208,9 +211,12 @@ class MainWindowBook(QtGui.QMainWindow):
|
|||||||
filenames = QtGui.QFileDialog.getOpenFileNames(
|
filenames = QtGui.QFileDialog.getOpenFileNames(
|
||||||
parent=self,
|
parent=self,
|
||||||
caption='Select image file(s) to add',
|
caption='Select image file(s) to add',
|
||||||
filter='Image files (*.jpeg *.jpg *.gif *.png);;All files (*.*)'
|
filter='Image files (*.jpeg *.jpg *.gif *.png);;Comic files (*.cbz)'
|
||||||
)
|
)
|
||||||
self.addImageFiles(filenames)
|
if(self.containsCbzFile(filenames)):
|
||||||
|
self.addCBZFiles(filenames)
|
||||||
|
else:
|
||||||
|
self.addImageFiles(filenames)
|
||||||
|
|
||||||
|
|
||||||
def onBookAddDirectory(self):
|
def onBookAddDirectory(self):
|
||||||
@ -364,7 +370,6 @@ class MainWindowBook(QtGui.QMainWindow):
|
|||||||
self.book.images.append(filename)
|
self.book.images.append(filename)
|
||||||
self.book.modified = True
|
self.book.modified = True
|
||||||
|
|
||||||
|
|
||||||
def addImageDirs(self, directories):
|
def addImageDirs(self, directories):
|
||||||
filenames = []
|
filenames = []
|
||||||
|
|
||||||
@ -377,6 +382,33 @@ class MainWindowBook(QtGui.QMainWindow):
|
|||||||
|
|
||||||
self.addImageFiles(filenames)
|
self.addImageFiles(filenames)
|
||||||
|
|
||||||
|
def addCBZFiles(self, filenames):
|
||||||
|
directories = []
|
||||||
|
tempDir = tempfile.gettempdir()
|
||||||
|
filenames.sort()
|
||||||
|
|
||||||
|
filenamesListed = []
|
||||||
|
for i in xrange(0, self.listWidgetFiles.count()):
|
||||||
|
filenamesListed.append(self.listWidgetFiles.item(i).text())
|
||||||
|
|
||||||
|
for filename in filenames:
|
||||||
|
folderName = os.path.splitext(basename(str(filename)))[0]
|
||||||
|
path = tempDir + "/" + folderName + "/"
|
||||||
|
cbzFile = ZipFile(str(filename))
|
||||||
|
for f in cbzFile.namelist():
|
||||||
|
if f.endswith('/'):
|
||||||
|
try:
|
||||||
|
os.makedirs(path+f)
|
||||||
|
except:
|
||||||
|
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)):
|
||||||
|
directories.append(path)
|
||||||
|
#Add the files
|
||||||
|
self.addImageDirs(directories)
|
||||||
|
|
||||||
|
|
||||||
def isImageFile(self, filename):
|
def isImageFile(self, filename):
|
||||||
imageExts = ['.jpeg', '.jpg', '.gif', '.png']
|
imageExts = ['.jpeg', '.jpg', '.gif', '.png']
|
||||||
@ -386,6 +418,17 @@ class MainWindowBook(QtGui.QMainWindow):
|
|||||||
os.path.splitext(filename)[1].lower() in imageExts
|
os.path.splitext(filename)[1].lower() in imageExts
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def containsCbzFile(self, filenames):
|
||||||
|
cbzExts = ['.cbz']
|
||||||
|
for filename in filenames:
|
||||||
|
filename = unicode(filename)
|
||||||
|
result = (
|
||||||
|
os.path.isfile(filename) and
|
||||||
|
os.path.splitext(filename)[1].lower() in cbzExts
|
||||||
|
)
|
||||||
|
if result == True:
|
||||||
|
return result
|
||||||
|
return False
|
||||||
|
|
||||||
def cleanupBookFile(self, filename):
|
def cleanupBookFile(self, filename):
|
||||||
if len(os.path.splitext(unicode(filename))[1]) == 0:
|
if len(os.path.splitext(unicode(filename))[1]) == 0:
|
||||||
|
@ -18,6 +18,7 @@ import os, shutil
|
|||||||
from PyQt4 import QtGui, QtCore
|
from PyQt4 import QtGui, QtCore
|
||||||
import image
|
import image
|
||||||
import cbz
|
import cbz
|
||||||
|
import pdfimage
|
||||||
|
|
||||||
|
|
||||||
class DialogConvert(QtGui.QProgressDialog):
|
class DialogConvert(QtGui.QProgressDialog):
|
||||||
@ -36,6 +37,11 @@ class DialogConvert(QtGui.QProgressDialog):
|
|||||||
if 'CBZ' in self.book.outputFormat:
|
if 'CBZ' in self.book.outputFormat:
|
||||||
self.archive = cbz.Archive(self.bookPath)
|
self.archive = cbz.Archive(self.bookPath)
|
||||||
|
|
||||||
|
self.pdf = None
|
||||||
|
if "PDF" in self.book.outputFormat:
|
||||||
|
self.pdf = pdfimage.PDFImage(self.bookPath, str(self.book.title), str(self.book.device))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def showEvent(self, event):
|
def showEvent(self, event):
|
||||||
if self.timer is None:
|
if self.timer is None:
|
||||||
@ -50,6 +56,9 @@ class DialogConvert(QtGui.QProgressDialog):
|
|||||||
# Close the archive if we created a CBZ file
|
# Close the archive if we created a CBZ file
|
||||||
if self.archive is not None:
|
if self.archive is not None:
|
||||||
self.archive.close()
|
self.archive.close()
|
||||||
|
#Close and generate the PDF File
|
||||||
|
if self.pdf is not None:
|
||||||
|
self.pdf.close()
|
||||||
|
|
||||||
# Remove image directory if the user didn't wish for images
|
# Remove image directory if the user didn't wish for images
|
||||||
if 'Image' not in self.book.outputFormat:
|
if 'Image' not in self.book.outputFormat:
|
||||||
@ -98,6 +107,8 @@ class DialogConvert(QtGui.QProgressDialog):
|
|||||||
image.convertImage(source, target, str(self.book.device), self.book.imageFlags)
|
image.convertImage(source, target, str(self.book.device), self.book.imageFlags)
|
||||||
if self.archive is not None:
|
if self.archive is not None:
|
||||||
self.archive.addFile(target)
|
self.archive.addFile(target)
|
||||||
|
if self.pdf is not None:
|
||||||
|
self.pdf.addImage(target)
|
||||||
except RuntimeError, error:
|
except RuntimeError, error:
|
||||||
result = QtGui.QMessageBox.critical(
|
result = QtGui.QMessageBox.critical(
|
||||||
self,
|
self,
|
||||||
|
@ -22,6 +22,7 @@ class ImageFlags:
|
|||||||
Resize = 1 << 1
|
Resize = 1 << 1
|
||||||
Frame = 1 << 2
|
Frame = 1 << 2
|
||||||
Quantize = 1 << 3
|
Quantize = 1 << 3
|
||||||
|
Stretch = 1 << 4
|
||||||
|
|
||||||
|
|
||||||
class KindleData:
|
class KindleData:
|
||||||
@ -90,6 +91,10 @@ def quantizeImage(image, palette):
|
|||||||
return image.quantize(palette=palImg)
|
return image.quantize(palette=palImg)
|
||||||
|
|
||||||
|
|
||||||
|
def stretchImage(image, size):
|
||||||
|
widthDev, heightDev = size
|
||||||
|
return image.resize((widthDev, heightDev), Image.ANTIALIAS)
|
||||||
|
|
||||||
def resizeImage(image, size):
|
def resizeImage(image, size):
|
||||||
widthDev, heightDev = size
|
widthDev, heightDev = size
|
||||||
widthImg, heightImg = image.size
|
widthImg, heightImg = image.size
|
||||||
@ -167,16 +172,17 @@ def convertImage(source, target, device, flags):
|
|||||||
image = Image.open(source)
|
image = Image.open(source)
|
||||||
except IOError:
|
except IOError:
|
||||||
raise RuntimeError('Cannot read image file %s' % source)
|
raise RuntimeError('Cannot read image file %s' % source)
|
||||||
|
|
||||||
image = formatImage(image)
|
image = formatImage(image)
|
||||||
if flags & ImageFlags.Orient:
|
if flags & ImageFlags.Orient:
|
||||||
image = orientImage(image, size)
|
image = orientImage(image, size)
|
||||||
if flags & ImageFlags.Resize:
|
if flags & ImageFlags.Resize:
|
||||||
image = resizeImage(image, size)
|
image = resizeImage(image, size)
|
||||||
|
if flags & ImageFlags.Stretch:
|
||||||
|
image = stretchImage(image, size)
|
||||||
if flags & ImageFlags.Frame:
|
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:
|
if flags & ImageFlags.Quantize:
|
||||||
image = quantizeImage(image, palette)
|
image = quantizeImage(image, palette)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
image.save(target)
|
image.save(target)
|
||||||
|
@ -42,6 +42,7 @@ class DialogOptions(QtGui.QDialog):
|
|||||||
self.checkboxOverwrite.setChecked(self.book.overwrite)
|
self.checkboxOverwrite.setChecked(self.book.overwrite)
|
||||||
self.checkboxOrient.setChecked(self.book.imageFlags & ImageFlags.Orient)
|
self.checkboxOrient.setChecked(self.book.imageFlags & ImageFlags.Orient)
|
||||||
self.checkboxResize.setChecked(self.book.imageFlags & ImageFlags.Resize)
|
self.checkboxResize.setChecked(self.book.imageFlags & ImageFlags.Resize)
|
||||||
|
self.checkboxStretch.setChecked(self.book.imageFlags & ImageFlags.Stretch)
|
||||||
self.checkboxQuantize.setChecked(self.book.imageFlags & ImageFlags.Quantize)
|
self.checkboxQuantize.setChecked(self.book.imageFlags & ImageFlags.Quantize)
|
||||||
self.checkboxFrame.setChecked(self.book.imageFlags & ImageFlags.Frame)
|
self.checkboxFrame.setChecked(self.book.imageFlags & ImageFlags.Frame)
|
||||||
|
|
||||||
@ -57,10 +58,12 @@ class DialogOptions(QtGui.QDialog):
|
|||||||
imageFlags |= ImageFlags.Orient
|
imageFlags |= ImageFlags.Orient
|
||||||
if self.checkboxResize.isChecked():
|
if self.checkboxResize.isChecked():
|
||||||
imageFlags |= ImageFlags.Resize
|
imageFlags |= ImageFlags.Resize
|
||||||
|
if self.checkboxStretch.isChecked():
|
||||||
|
imageFlags |= ImageFlags.Stretch
|
||||||
if self.checkboxQuantize.isChecked():
|
if self.checkboxQuantize.isChecked():
|
||||||
imageFlags |= ImageFlags.Quantize
|
imageFlags |= ImageFlags.Quantize
|
||||||
if self.checkboxFrame.isChecked():
|
if self.checkboxFrame.isChecked():
|
||||||
imageFlags |= ImageFlags.Frame
|
imageFlags |= ImageFlags.Frame
|
||||||
|
|
||||||
modified = (
|
modified = (
|
||||||
self.book.title != title or
|
self.book.title != title or
|
||||||
|
49
mangle/pdfimage.py
Normal file
49
mangle/pdfimage.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# Copyright (C) 2012 Cristian Lizana <cristian@lizana.in>
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
outputFileName = '%s.pdf' % os.path.basename(path)
|
||||||
|
outputPath = os.path.join(outputDirectory, outputFileName)
|
||||||
|
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
|
||||||
|
self.canvas = canvas.Canvas(outputPath, pagesize=self.pageSize)
|
||||||
|
self.canvas.setAuthor("Mangle")
|
||||||
|
self.canvas.setTitle(self.bookTitle)
|
||||||
|
self.canvas.setSubject("Created for " + self.currentDevice)
|
||||||
|
|
||||||
|
|
||||||
|
def addImage(self, filename):
|
||||||
|
self.canvas.drawImage(filename, 0, 0, width=self.pageSize[0], height=self.pageSize[1], preserveAspectRatio=True, anchor='c')
|
||||||
|
self.canvas.showPage()
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self.canvas.save()
|
@ -99,7 +99,7 @@
|
|||||||
<widget class="QComboBox" name="comboBoxFormat">
|
<widget class="QComboBox" name="comboBoxFormat">
|
||||||
<item>
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Images & CBZ</string>
|
<string>Images & CBZ & PDF</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
@ -107,6 +107,11 @@
|
|||||||
<string>Images only</string>
|
<string>Images only</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>PDF only</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>CBZ only</string>
|
<string>CBZ only</string>
|
||||||
@ -130,13 +135,6 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="checkboxResize">
|
|
||||||
<property name="text">
|
|
||||||
<string>Resize images to center on screen</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="checkboxQuantize">
|
<widget class="QCheckBox" name="checkboxQuantize">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -150,6 +148,27 @@
|
|||||||
<string>Draw frame around images</string>
|
<string>Draw frame around images</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Size</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="checkboxResize">
|
||||||
|
<property name="text">
|
||||||
|
<string>Resize images to center on screen</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="checkboxStretch">
|
||||||
|
<property name="text">
|
||||||
|
<string>Stretch images to fill the screen</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
Loading…
Reference in New Issue
Block a user