Merge pull request #24 from naparuba/master

Master
This commit is contained in:
Alex Yatskov 2015-10-23 10:54:31 +09:00
commit f1f94ccfba
6 changed files with 94 additions and 51 deletions

View File

@ -30,27 +30,36 @@ import util
# Sort function use to sort files in a natural order, by lowering # 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) # characters, and manage multi levels of integers (tome 1/ page 1.jpg, etc etc)
# cf: See http://www.codinghorror.com/blog/archives/001018.html
def natural_key(string_): def natural_key(string_):
"""See http://www.codinghorror.com/blog/archives/001018.html""" l = []
return [int(s) if s.isdigit() else s.lower() for s in re.split(r'(\d+)', string_)] for s in re.split(r'(\d+)', string_):
# QString do not have isdigit, so convert it if need
if isinstance(s, QtCore.QString):
s = unicode(s)
if s.isdigit():
l.append(int(s))
else:
l.append(s.lower())
return l
class Book(object): class Book(object):
DefaultDevice = 'Kindle Paperwhite' DefaultDevice = 'Kindle Paperwhite'
DefaultOutputFormat = 'CBZ only' DefaultOutputFormat = 'CBZ only'
DefaultOverwrite = True DefaultOverwrite = True
DefaultImageFlags = ImageFlags.Orient | ImageFlags.Resize | ImageFlags.Quantize DefaultImageFlags = ImageFlags.Orient | ImageFlags.Resize | ImageFlags.Quantize
def __init__(self): def __init__(self):
self.images = [] self.images = []
self.filename = None self.filename = None
self.modified = False self.modified = False
self.title = None self.title = None
self.titleSet = False self.titleSet = False
self.device = Book.DefaultDevice self.device = Book.DefaultDevice
self.overwrite = Book.DefaultOverwrite self.overwrite = Book.DefaultOverwrite
self.imageFlags = Book.DefaultImageFlags self.imageFlags = Book.DefaultImageFlags
self.outputFormat = Book.DefaultOutputFormat self.outputFormat = Book.DefaultOutputFormat
@ -101,14 +110,14 @@ class Book(object):
if root.tagName() != 'book': if root.tagName() != 'book':
raise RuntimeError('Unexpected book format in file %s' % filename) raise RuntimeError('Unexpected book format in file %s' % filename)
self.title = root.attribute('title', 'Untitled') self.title = root.attribute('title', 'Untitled')
self.overwrite = root.attribute('overwrite', 'true' if Book.DefaultOverwrite else 'false') == 'true' self.overwrite = root.attribute('overwrite', 'true' if Book.DefaultOverwrite else 'false') == 'true'
self.device = root.attribute('device', Book.DefaultDevice) self.device = root.attribute('device', Book.DefaultDevice)
self.outputFormat = root.attribute('outputFormat', Book.DefaultOutputFormat) self.outputFormat = root.attribute('outputFormat', Book.DefaultOutputFormat)
self.imageFlags = int(root.attribute('imageFlags', str(Book.DefaultImageFlags))) self.imageFlags = int(root.attribute('imageFlags', str(Book.DefaultImageFlags)))
self.filename = filename self.filename = filename
self.modified = False self.modified = False
self.images = [] self.images = []
items = root.elementsByTagName('image') items = root.elementsByTagName('image')
if items is None: if items is None:

View File

@ -21,9 +21,9 @@ from zipfile import ZipFile, ZIP_STORED
class Archive(object): class Archive(object):
def __init__(self, path): def __init__(self, path):
outputDirectory = os.path.dirname(path) outputDirectory = os.path.dirname(path)
outputFileName = '%s.cbz' % os.path.basename(path) outputFileName = '%s.cbz' % os.path.basename(path)
outputPath = os.path.join(outputDirectory, outputFileName) outputPath = os.path.join(outputDirectory, outputFileName)
self.zipfile = ZipFile(outputPath, 'w', ZIP_STORED) self.zipfile = ZipFile(outputPath, 'w', ZIP_STORED)
def addFile(self, filename): def addFile(self, filename):

View File

@ -16,10 +16,10 @@
import os import os
import shutil import shutil
from PyQt4 import QtGui, QtCore from PyQt4 import QtGui, QtCore
from image import ImageFlags
from image import ImageFlags
import cbz import cbz
import image import image
import pdfimage import pdfimage
@ -29,7 +29,7 @@ class DialogConvert(QtGui.QProgressDialog):
def __init__(self, parent, book, directory): def __init__(self, parent, book, directory):
QtGui.QProgressDialog.__init__(self) QtGui.QProgressDialog.__init__(self)
self.book = book self.book = book
self.bookPath = os.path.join(unicode(directory), unicode(self.book.title)) self.bookPath = os.path.join(unicode(directory), unicode(self.book.title))
self.timer = None self.timer = None
@ -121,7 +121,6 @@ class DialogConvert(QtGui.QProgressDialog):
archive = self.archive archive = self.archive
pdf = self.pdf pdf = self.pdf
# Maybe the user ask for a split, but the picture is not a large one, so skip # Maybe the user ask for a split, but the picture is not a large one, so skip
# it but only for this picture # it but only for this picture
if (flags & ImageFlags.Split) or (flags & ImageFlags.SplitInverse): if (flags & ImageFlags.Split) or (flags & ImageFlags.SplitInverse):
@ -132,7 +131,7 @@ class DialogConvert(QtGui.QProgressDialog):
flags &= ~f flags &= ~f
# For right page (if requested in options and need for this image) # For right page (if requested in options and need for this image)
if(flags & ImageFlags.Split): if (flags & ImageFlags.Split):
# New path based on modified index # New path based on modified index
target = os.path.join(self.bookPath, '%05d.png' % (index * 2 + 0)) target = os.path.join(self.bookPath, '%05d.png' % (index * 2 + 0))
self.convertAndSave(source, target, device, flags ^ ImageFlags.Split | ImageFlags.SplitRight, archive, pdf) self.convertAndSave(source, target, device, flags ^ ImageFlags.Split | ImageFlags.SplitRight, archive, pdf)
@ -140,7 +139,7 @@ class DialogConvert(QtGui.QProgressDialog):
target = os.path.join(self.bookPath, '%05d.png' % (index * 2 + 1)) target = os.path.join(self.bookPath, '%05d.png' % (index * 2 + 1))
# For right page (if requested), but in inverted mode # For right page (if requested), but in inverted mode
if(flags & ImageFlags.SplitInverse): if (flags & ImageFlags.SplitInverse):
# New path based on modified index # New path based on modified index
target = os.path.join(self.bookPath, '%05d.png' % (index * 2 + 0)) target = os.path.join(self.bookPath, '%05d.png' % (index * 2 + 0))
self.convertAndSave(source, target, device, flags ^ ImageFlags.SplitInverse | ImageFlags.SplitLeft, archive, pdf) self.convertAndSave(source, target, device, flags ^ ImageFlags.SplitInverse | ImageFlags.SplitLeft, archive, pdf)

View File

@ -16,20 +16,20 @@
import os import os
from PIL import Image, ImageDraw from PIL import Image, ImageDraw, ImageStat, ImageChops
class ImageFlags: class ImageFlags:
Orient = 1 << 0 Orient = 1 << 0
Resize = 1 << 1 Resize = 1 << 1
Frame = 1 << 2 Frame = 1 << 2
Quantize = 1 << 3 Quantize = 1 << 3
Stretch = 1 << 4 Stretch = 1 << 4
Split = 1 << 5 # split right then left Split = 1 << 5 # split right then left
SplitRight = 1 << 6 # split only the right page SplitRight = 1 << 6 # split only the right page
SplitLeft = 1 << 7 # split only the left page SplitLeft = 1 << 7 # split only the left page
SplitInverse = 1 << 8 # split left then right page SplitInverse = 1 << 8 # split left then right page
AutoCrop = 1 << 9 # split left then right page
class KindleData: class KindleData:
@ -179,6 +179,15 @@ def orientImage(image, size):
return image return image
# We will auto crop the image, by removing just white part around the image
# by inverting colors, and asking a bounder box ^^
@protect_bad_image
def autoCropImage(image):
x0, y0, xend, yend = ImageChops.invert(image).getbbox()
image = image.crop((x0, y0, xend, yend))
return image
def frameImage(image, foreground, background, size): def frameImage(image, foreground, background, size):
widthDev, heightDev = size widthDev, heightDev = size
widthImg, heightImg = image.size widthImg, heightImg = image.size
@ -240,6 +249,9 @@ def convertImage(source, target, device, flags):
raise RuntimeError('Unexpected output device %s' % device) raise RuntimeError('Unexpected output device %s' % device)
# Load image from source path # Load image from source path
image = loadImage(source) image = loadImage(source)
# Format according to palette # Format according to palette
image = formatImage(image) image = formatImage(image)
# Apply flag transforms # Apply flag transforms
@ -257,6 +269,10 @@ def convertImage(source, target, device, flags):
if (flags & ImageFlags.SplitInverse): if (flags & ImageFlags.SplitInverse):
image = splitRight(image) image = splitRight(image)
# Auto crop the image, but before manage size and co, clean the source so
if flags & ImageFlags.AutoCrop:
image = autoCropImage(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:
@ -265,6 +281,9 @@ def convertImage(source, target, device, flags):
image = stretchImage(image, size) 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)

View File

@ -35,6 +35,7 @@ class DialogOptions(QtGui.QDialog):
self.moveDialogToOptions() self.moveDialogToOptions()
# Get options from current book (like a loaded one) and set the dialog values
def moveOptionsToDialog(self): def moveOptionsToDialog(self):
self.lineEditTitle.setText(self.book.title or 'Untitled') self.lineEditTitle.setText(self.book.title or 'Untitled')
self.comboBoxDevice.setCurrentIndex(max(self.comboBoxDevice.findText(self.book.device), 0)) self.comboBoxDevice.setCurrentIndex(max(self.comboBoxDevice.findText(self.book.device), 0))
@ -47,12 +48,15 @@ class DialogOptions(QtGui.QDialog):
self.checkboxFrame.setChecked(self.book.imageFlags & ImageFlags.Frame) self.checkboxFrame.setChecked(self.book.imageFlags & ImageFlags.Frame)
# Save parameters set on the dialogs to the book object if need
def moveDialogToOptions(self): def moveDialogToOptions(self):
title = self.lineEditTitle.text() # First get dialog values
device = self.comboBoxDevice.currentText() title = self.lineEditTitle.text()
device = self.comboBoxDevice.currentText()
outputFormat = self.comboBoxFormat.currentText() outputFormat = self.comboBoxFormat.currentText()
overwrite = self.checkboxOverwrite.isChecked() overwrite = self.checkboxOverwrite.isChecked()
# Now compute flags
imageFlags = 0 imageFlags = 0
if self.checkboxOrient.isChecked(): if self.checkboxOrient.isChecked():
imageFlags |= ImageFlags.Orient imageFlags |= ImageFlags.Orient
@ -68,7 +72,12 @@ class DialogOptions(QtGui.QDialog):
imageFlags |= ImageFlags.Split imageFlags |= ImageFlags.Split
if self.checkboxSplitInverse.isChecked(): if self.checkboxSplitInverse.isChecked():
imageFlags |= ImageFlags.SplitInverse imageFlags |= ImageFlags.SplitInverse
if self.checkboxAutoCrop.isChecked():
imageFlags |= ImageFlags.AutoCrop
# If we did modified a value, update the book
# and only if we did change something to not
# warn for nothing the user
modified = ( modified = (
self.book.title != title or self.book.title != title or
self.book.device != device or self.book.device != device or
@ -78,9 +87,9 @@ class DialogOptions(QtGui.QDialog):
) )
if modified: if modified:
self.book.modified = True self.book.modified = True
self.book.title = title self.book.title = title
self.book.device = device self.book.device = device
self.book.overwrite = overwrite self.book.overwrite = overwrite
self.book.imageFlags = imageFlags self.book.imageFlags = imageFlags
self.book.outputFormat = outputFormat self.book.outputFormat = outputFormat

View File

@ -181,14 +181,21 @@
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="checkboxSplitInverse"> <widget class="QCheckBox" name="checkboxSplitInverse">
<property name="text"> <property name="text">
<string>Split images into two pages (left, right)</string> <string>Split images into two pages (left, right)</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="checkboxAutoCrop">
<property name="text">
<string>Auto crop image (remove white around the image)</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3"> <widget class="QLabel" name="label_3">
<property name="text"> <property name="text">
<string>Size</string> <string>Size</string>