1
0
mirror of https://github.com/ciromattia/kcc synced 2025-12-13 01:36:27 +00:00

Merge pull request #233 from ciromattia/dev

5.4
This commit is contained in:
Paweł Jastrzębski
2017-04-09 15:35:41 +02:00
committed by GitHub
16 changed files with 185 additions and 123 deletions

View File

@@ -1,6 +1,6 @@
# KCC
[![GitHub release](https://img.shields.io/github/release/ciromattia/kcc.svg)]() [![PyPI](https://img.shields.io/pypi/v/KindleComicConverter.svg)]() [![AUR](https://img.shields.io/aur/version/kcc.svg)]()
[![GitHub release](https://img.shields.io/github/release/ciromattia/kcc.svg)](https://github.com/ciromattia/kcc/releases) [![PyPI](https://img.shields.io/pypi/v/KindleComicConverter.svg)](https://pypi.python.org/pypi/KindleComicConverter) [![AUR](https://img.shields.io/aur/version/kcc.svg)](https://aur.archlinux.org/packages/kcc/)
**Kindle Comic Converter** is a Python app to convert comic/manga files or folders to EPUB, Panel View MOBI or E-Ink optimized CBZ.
It was initially developed for Kindle but since version 4.6 it outputs valid EPUB 3.0 so _**despite its name, KCC is
@@ -72,7 +72,7 @@ After completed conversion you should find ready file alongside the original inp
Please check [our wiki](https://github.com/ciromattia/kcc/wiki/) for more details.
CLI version of **KCC** is intended for power users. It is not idiot-proof like GUI :-)
CLI version of **KCC** is intended for power users. It allow to use options that might not be compatible and decrease quality of output.
### Standalone `kcc-c2e.py` usage:
@@ -86,6 +86,7 @@ Options:
KDX, KPW, KV, KoMT, KoG, KoGHD, KoA, KoAHD, KoAH2O,
KoAO) [Default=KV]
-m, --manga-style Manga style (right-to-left reading and splitting)
-q, --hq Try to increase the quality of magnification
-2, --two-panel Display two not four panels in Panel View mode
-w, --webtoon Webtoon processing mode
@@ -170,6 +171,11 @@ The app relies and includes the following scripts:
* [Kobo Aura ONE](http://kcc.iosphe.re/Samples/Ubunchu-KoAO.kepub.epub)
## CHANGELOG
#### 5.4:
* Reimplemented high quality Panel View option
* Improved webtoon splitter
* Fixed page splitter
#### 5.3.1:
* Small increase of output quality
* Improved error reporting

View File

@@ -66,7 +66,16 @@
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="2">
@@ -127,7 +136,16 @@
<item row="4" column="0" colspan="2">
<widget class="QWidget" name="optionWidget" native="true">
<layout class="QGridLayout" name="gridLayout_2">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
@@ -156,10 +174,13 @@
<item row="0" column="2">
<widget class="QCheckBox" name="qualityBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style=\'white-space:pre\'&gt;&lt;span style=\&quot; font-weight:600; text-decoration: underline;\&quot;&gt;Unchecked - 4 panels&lt;br/&gt;&lt;/span&gt;Zoom each corner separately.&lt;/p&gt;&lt;p style=\'white-space:pre\'&gt;&lt;span style=\&quot; font-weight:600; text-decoration: underline;\&quot;&gt;Checked - 2 panels&lt;br/&gt;&lt;/span&gt;Zoom only the top and bottom of the page.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style='white-space:pre'&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - 4 panels&lt;br/&gt;&lt;/span&gt;Zoom each corner separately.&lt;/p&gt;&lt;p style='white-space:pre'&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - 2 panels&lt;br/&gt;&lt;/span&gt;Zoom only the top and bottom of the page.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - 4 high quality panels&lt;br/&gt;&lt;/span&gt;Zoom each corner separately. Try to increase the quality of magnification. Check wiki for more details.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Panel View 4/2</string>
<string>Panel View 4/2/HQ</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
</item>
@@ -238,7 +259,16 @@
<bool>false</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@@ -267,7 +297,16 @@
<item row="0" column="0" colspan="2">
<widget class="QWidget" name="toolWidget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@@ -316,7 +355,16 @@
</sizepolicy>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">

View File

@@ -28,4 +28,4 @@ from kindlecomicconverter.startup import startC2P
if __name__ == "__main__":
freeze_support()
startC2P()
startC2P()

View File

@@ -1,5 +1,5 @@
#define MyAppName "Kindle Comic Converter"
#define MyAppVersion "5.3.1"
#define MyAppVersion "5.4"
#define MyAppPublisher "Ciro Mattia Gonano, Paweł Jastrzębski"
#define MyAppURL "http://kcc.iosphe.re/"
#define MyAppExeName "KCC.exe"

2
kcc.py
View File

@@ -39,7 +39,7 @@ elif sys.platform.startswith('win'):
class _Popen(forking.Popen):
def __init__(self, *args, **kw):
if hasattr(sys, 'frozen'):
# noinspection PyProtectedMember
# noinspection PyUnresolvedReferences,PyProtectedMember
os.putenv('_MEIPASS2', sys._MEIPASS)
try:
super(_Popen, self).__init__(*args, **kw)

View File

@@ -254,8 +254,10 @@ class WorkerThread(QtCore.QThread):
options.splitter = 2
elif GUI.rotateBox.checkState() == 2:
options.splitter = 1
if GUI.qualityBox.isChecked():
if GUI.qualityBox.checkState() == 1:
options.autoscale = True
elif GUI.qualityBox.checkState() == 2:
options.hq = True
if GUI.webtoonBox.isChecked():
options.webtoon = True
if GUI.upscaleBox.checkState() == 1:
@@ -531,8 +533,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
def clearJobs(self):
GUI.jobList.clear()
# noinspection PyCallByClass,PyTypeChecker
def openWiki(self):
# noinspection PyCallByClass
QtGui.QDesktopServices.openUrl(QtCore.QUrl('https://github.com/ciromattia/kcc/wiki'))
def modeChange(self, mode):
@@ -613,6 +615,18 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
GUI.rotateBox.setEnabled(True)
GUI.upscaleBox.setEnabled(True)
def togglequalityBox(self, value):
profile = GUI.profiles[str(GUI.deviceBox.currentText())]
if value == 2:
if profile['Label'] in ['KV']:
self.addMessage('This option is intended for older Kindle models.', 'warning')
self.addMessage('It might not provide any quality increase in this case.', 'warning')
GUI.upscaleBox.setEnabled(False)
GUI.upscaleBox.setChecked(True)
else:
GUI.upscaleBox.setEnabled(True)
GUI.upscaleBox.setChecked(profile['DefaultUpscale'])
def changeGamma(self, value):
valueRaw = int(5 * round(float(value) / 5))
value = '%.2f' % (float(valueRaw) / 100)
@@ -634,7 +648,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.changeFormat()
GUI.gammaSlider.setValue(0)
self.changeGamma(0)
GUI.qualityBox.setEnabled(profile['PVOptions'])
if not GUI.webtoonBox.isChecked():
GUI.qualityBox.setEnabled(profile['PVOptions'])
GUI.upscaleBox.setChecked(profile['DefaultUpscale'])
if not profile['PVOptions']:
GUI.qualityBox.setChecked(False)
@@ -648,7 +663,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
GUI.formatBox.setCurrentIndex(outputFormat)
else:
GUI.formatBox.setCurrentIndex(profile['DefaultFormat'])
GUI.qualityBox.setEnabled(profile['PVOptions'])
if not GUI.webtoonBox.isChecked():
GUI.qualityBox.setEnabled(profile['PVOptions'])
if str(GUI.formatBox.currentText()) == 'MOBI/AZW3':
GUI.outputSplit.setEnabled(True)
else:
@@ -1002,6 +1018,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
GUI.gammaSlider.valueChanged.connect(self.changeGamma)
GUI.gammaBox.stateChanged.connect(self.togglegammaBox)
GUI.webtoonBox.stateChanged.connect(self.togglewebtoonBox)
GUI.qualityBox.stateChanged.connect(self.togglequalityBox)
GUI.deviceBox.activated.connect(self.changeDevice)
GUI.formatBox.activated.connect(self.changeFormat)
MW.progressBarTick.connect(self.updateProgressbar)

View File

@@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'gui\KCC.ui'
#
# Created by: PyQt5 UI code generator 5.6
# Created by: PyQt5 UI code generator 5.8.1
#
# WARNING! All changes made in this file will be lost!
@@ -81,6 +81,7 @@ class Ui_mainWindow(object):
self.rotateBox.setObjectName("rotateBox")
self.gridLayout_2.addWidget(self.rotateBox, 0, 1, 1, 1)
self.qualityBox = QtWidgets.QCheckBox(self.optionWidget)
self.qualityBox.setTristate(True)
self.qualityBox.setObjectName("qualityBox")
self.gridLayout_2.addWidget(self.qualityBox, 0, 2, 1, 1)
self.webtoonBox = QtWidgets.QCheckBox(self.optionWidget)
@@ -241,8 +242,8 @@ class Ui_mainWindow(object):
self.mangaBox.setText(_translate("mainWindow", "Manga mode"))
self.rotateBox.setToolTip(_translate("mainWindow", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Split<br/></span>Double page spreads will be cut into two separate pages.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Rotate and split<br/></span>Double page spreads will be displayed twice. First rotated and then split. </p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Rotate<br/></span>Double page spreads will be rotated.</p></body></html>"))
self.rotateBox.setText(_translate("mainWindow", "Spread splitter"))
self.qualityBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - 4 panels<br/></span>Zoom each corner separately.</p><p style=\'white-space:pre\'><span style=\" font-weight:600; text-decoration: underline;\">Checked - 2 panels<br/></span>Zoom only the top and bottom of the page.</p></body></html>"))
self.qualityBox.setText(_translate("mainWindow", "Panel View 4/2"))
self.qualityBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - 4 panels<br/></span>Zoom each corner separately.</p><p style=\'white-space:pre\'><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - 2 panels<br/></span>Zoom only the top and bottom of the page.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - 4 high quality panels<br/></span>Zoom each corner separately. Try to increase the quality of magnification. Check wiki for more details.</p></body></html>"))
self.qualityBox.setText(_translate("mainWindow", "Panel View 4/2/HQ"))
self.webtoonBox.setToolTip(_translate("mainWindow", "<html><head/><body><p style=\'white-space:pre\'>Enable special parsing mode for Korean Webtoons.</p></body></html>"))
self.webtoonBox.setText(_translate("mainWindow", "Webtoon mode"))
self.upscaleBox.setToolTip(_translate("mainWindow", "<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Nothing<br/></span>Images smaller than device resolution will not be resized.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Stretching<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Upscaling<br/></span>Images smaller than device resolution will be resized. Aspect ratio will be preserved.</p></body></html>"))

View File

@@ -1,4 +1,4 @@
__version__ = '5.3.1'
__version__ = '5.4'
__license__ = 'ISC'
__copyright__ = '2012-2017, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>'
__docformat__ = 'restructuredtext en'

View File

@@ -16,12 +16,11 @@
# PERFORMANCE OF THIS SOFTWARE.
#
import sys
import os
from zipfile import is_zipfile, ZipFile
from subprocess import STDOUT, PIPE
from psutil import Popen
from shutil import move, copy
from shutil import move
from . import rarfile
from .shared import check7ZFile as is_7zfile

View File

@@ -103,6 +103,10 @@ def buildHTML(path, imgfile, imgfilepath):
os.makedirs(htmlpath)
htmlfile = os.path.join(htmlpath, filename[0] + '.xhtml')
imgsize = Image.open(os.path.join(head, "Images", postfix, imgfile)).size
if options.hq:
imgsizeframe = deviceres
else:
imgsizeframe = imgsize
f = open(htmlfile, "w", encoding='UTF-8')
f.writelines(["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
"<!DOCTYPE html>\n",
@@ -111,17 +115,20 @@ def buildHTML(path, imgfile, imgfilepath):
"<title>", escape(filename[0]), "</title>\n",
"<link href=\"", "../" * (backref - 1), "style.css\" type=\"text/css\" rel=\"stylesheet\"/>\n",
"<meta name=\"viewport\" "
"content=\"width=" + str(deviceres[0]) + ", height=" + str(deviceres[1]) + "\"/>\n"
"content=\"width=" + str(imgsize[0]) + ", height=" + str(imgsize[1]) + "\"/>\n"
"</head>\n",
"<body style=\"" + additionalStyle + "\">\n",
"<div style=\"text-align:center;top:" + getTopMargin(deviceres, imgsize) + "%;\">\n",
"<img width=\"" + str(imgsize[0]) + "\" height=\"" + str(imgsize[1]) + "\" ",
"<img width=\"" + str(imgsizeframe[0]) + "\" height=\"" + str(imgsizeframe[1]) + "\" ",
"src=\"", "../" * backref, "Images/", postfix, imgfile, "\"/>\n</div>\n"])
if options.iskindle and options.panelview:
if options.autoscale:
size = (getPanelViewResolution(imgsize, deviceres))
else:
size = (int(imgsize[0] * 1.5), int(imgsize[1] * 1.5))
if options.hq:
size = imgsize
else:
size = (int(imgsize[0] * 1.5), int(imgsize[1] * 1.5))
if size[0] - deviceres[0] < deviceres[0] * 0.01:
noHorizontalPV = True
else:
@@ -455,7 +462,7 @@ def buildEPUB(path, chapterNames, tomeNumber):
def imgDirectoryProcessing(path):
global workerPool, workerOutput
workerPool = Pool()
workerPool = Pool(maxtasksperchild=100)
workerOutput = []
options.imgMetadata = {}
options.imgOld = []
@@ -884,6 +891,8 @@ def makeParser():
" KoA, KoAHD, KoAH2O, KoAO) [Default=KV]")
mainOptions.add_option("-m", "--manga-style", action="store_true", dest="righttoleft", default=False,
help="Manga style (right-to-left reading and splitting)")
mainOptions.add_option("-q", "--hq", action="store_true", dest="hq", default=False,
help="Try to increase the quality of magnification")
mainOptions.add_option("-2", "--two-panel", action="store_true", dest="autoscale", default=False,
help="Display two not four panels in Panel View mode")
mainOptions.add_option("-w", "--webtoon", action="store_true", dest="webtoon", default=False,
@@ -960,16 +969,20 @@ def checkOptions():
# Older Kindle models don't support Panel View.
if options.profile == 'K1' or options.profile == 'K2' or options.profile == 'KDX':
options.panelview = False
options.hq = False
# Webtoon mode mandatory options
if options.webtoon:
options.panelview = False
options.righttoleft = False
options.upscale = True
options.hq = False
# Disable all Kindle features for other e-readers
if options.profile == 'OTHER':
options.panelview = False
options.hq = False
if 'Ko' in options.profile:
options.panelview = False
options.hq = False
# CBZ files on Kindle DX/DXG support higher resolution
if options.profile == 'KDX' and options.format == 'CBZ':
options.customheight = 1200
@@ -1185,9 +1198,11 @@ def makeMOBI(work, qtGUI=None):
threadNumber = 1
elif 2 < availableMemory <= 4:
threadNumber = 2
else:
elif 4 < availableMemory <= 8:
threadNumber = 4
makeMOBIWorkerPool = Pool(threadNumber)
else:
threadNumber = None
makeMOBIWorkerPool = Pool(threadNumber, maxtasksperchild=10)
for i in work:
makeMOBIWorkerPool.apply_async(func=makeMOBIWorker, args=(i, ), callback=makeMOBIWorkerTick)
makeMOBIWorkerPool.close()

View File

@@ -23,7 +23,7 @@ import sys
from shutil import rmtree, copytree, move
from optparse import OptionParser, OptionGroup
from multiprocessing import Pool
from PIL import Image, ImageStat, ImageOps
from PIL import Image, ImageChops, ImageOps, ImageDraw
from .shared import getImageFileName, walkLevel, walkSort, sanitizeTrace
try:
from PyQt5 import QtCore
@@ -67,8 +67,7 @@ def mergeDirectory(work):
result = Image.new('RGB', (targetWidth, targetHeight))
y = 0
for i in imagesValid:
img = Image.open(i)
img = img.convert('RGB')
img = Image.open(i).convert('RGB')
if img.size[0] < targetWidth:
img = ImageOps.fit(img, (targetWidth, img.size[1]), method=Image.BICUBIC, centering=(0.5, 0.5))
result.paste(img, (0, y))
@@ -80,30 +79,8 @@ def mergeDirectory(work):
return str(sys.exc_info()[1]), sanitizeTrace(sys.exc_info()[2])
def sanitizePanelSize(panel, opt):
newPanels = []
if panel[2] > 6 * opt.height:
diff = int(panel[2] / 8)
newPanels.append([panel[0], panel[1] - diff * 7, diff])
newPanels.append([panel[1] - diff * 7, panel[1] - diff * 6, diff])
newPanels.append([panel[1] - diff * 6, panel[1] - diff * 5, diff])
newPanels.append([panel[1] - diff * 5, panel[1] - diff * 4, diff])
newPanels.append([panel[1] - diff * 4, panel[1] - diff * 3, diff])
newPanels.append([panel[1] - diff * 3, panel[1] - diff * 2, diff])
newPanels.append([panel[1] - diff * 2, panel[1] - diff, diff])
newPanels.append([panel[1] - diff, panel[1], diff])
elif panel[2] > 3 * opt.height:
diff = int(panel[2] / 4)
newPanels.append([panel[0], panel[1] - diff * 3, diff])
newPanels.append([panel[1] - diff * 3, panel[1] - diff * 2, diff])
newPanels.append([panel[1] - diff * 2, panel[1] - diff, diff])
newPanels.append([panel[1] - diff, panel[1], diff])
elif panel[2] > 1.5 * opt.height:
newPanels.append([panel[0], panel[1] - int(panel[2] / 2), int(panel[2] / 2)])
newPanels.append([panel[1] - int(panel[2] / 2), panel[1], int(panel[2] / 2)])
else:
newPanels = [panel]
return newPanels
def detectSolid(img):
return not ImageChops.invert(img).getbbox() or not img.getbbox()
def splitImageTick(output):
@@ -121,56 +98,60 @@ def splitImage(work):
path = work[0]
name = work[1]
opt = work[2]
# Hardcoded options
threshold = 1.0
delta = 15
fileExpanded = os.path.splitext(name)
filePath = os.path.join(path, name)
image = Image.open(filePath)
image = image.convert('RGB')
widthImg, heightImg = image.size
imgOrg = Image.open(filePath).convert('RGB')
imgProcess = Image.open(filePath).convert('1')
widthImg, heightImg = imgOrg.size
if heightImg > opt.height:
if opt.debug:
from PIL import ImageDraw
debugImage = Image.open(filePath)
draw = ImageDraw.Draw(debugImage)
drawImg = Image.open(filePath).convert(mode='RGBA')
draw = ImageDraw.Draw(drawImg)
# Find panels
y1 = 0
y2 = 15
yWork = 0
panelDetected = False
panels = []
while y2 < heightImg:
while ImageStat.Stat(image.crop([0, y1, widthImg, y2])).var[0] < threshold and y2 < heightImg:
y2 += delta
y2 -= delta
y1Temp = y2
y1 = y2 + delta
y2 = y1 + delta
while ImageStat.Stat(image.crop([0, y1, widthImg, y2])).var[0] >= threshold and y2 < heightImg:
y1 += delta
y2 += delta
if y1 + delta >= heightImg:
y1 = heightImg - 1
y2Temp = y1
if opt.debug:
draw.line([(0, y1Temp), (widthImg, y1Temp)], fill=(0, 255, 0))
draw.line([(0, y2Temp), (widthImg, y2Temp)], fill=(255, 0, 0))
panelHeight = y2Temp - y1Temp
if panelHeight > delta:
# Panels that can't be cut nicely will be forcefully splitted
panelsCleaned = sanitizePanelSize([y1Temp, y2Temp, panelHeight], opt)
for panel in panelsCleaned:
panels.append(panel)
while yWork < heightImg:
tmpImg = imgProcess.crop([0, yWork, widthImg, yWork + 4])
solid = detectSolid(tmpImg)
if not solid and not panelDetected:
panelDetected = True
panelY1 = yWork - 2
if solid and panelDetected:
panelDetected = False
panelY2 = yWork + 6
panels.append((panelY1, panelY2, panelY2 - panelY1))
yWork += 5
# Split too big panels
panelsProcessed = []
for panel in panels:
if panel[2] <= opt.height * 1.5:
panelsProcessed.append(panel)
elif panel[2] < opt.height * 2:
diff = panel[2] - opt.height
panelsProcessed.append((panel[0], panel[1] - diff, opt.height))
panelsProcessed.append((panel[1] - opt.height, panel[1], opt.height))
else:
parts = round(panel[2] / opt.height)
diff = panel[2] // parts
for x in range(0, parts):
panelsProcessed.append((panel[0] + (x * diff), panel[1] - ((parts - x - 1) * diff), diff))
if opt.debug:
for panel in panelsProcessed:
# noinspection PyUnboundLocalVariable
draw.rectangle([(0, panel[0]), (widthImg, panel[1])], (0, 255, 0, 128), (0, 0, 255, 255))
# noinspection PyUnboundLocalVariable
debugImage.save(os.path.join(path, fileExpanded[0] + '-debug.png'), 'PNG')
debugImage = Image.alpha_composite(imgOrg.convert(mode='RGBA'), drawImg)
debugImage.save(os.path.join(path, os.path.splitext(name)[0] + '-debug.png'), 'PNG')
# Create virtual pages
pages = []
currentPage = []
pageLeft = opt.height
panelNumber = 0
for panel in panels:
for panel in panelsProcessed:
if pageLeft - panel[2] > 0:
pageLeft -= panel[2]
currentPage.append(panelNumber)
@@ -190,14 +171,14 @@ def splitImage(work):
pageHeight = 0
targetHeight = 0
for panel in page:
pageHeight += panels[panel][2]
if pageHeight > delta:
pageHeight += panelsProcessed[panel][2]
if pageHeight > 15:
newPage = Image.new('RGB', (widthImg, pageHeight))
for panel in page:
panelImg = image.crop([0, panels[panel][0], widthImg, panels[panel][1]])
panelImg = imgOrg.crop([0, panelsProcessed[panel][0], widthImg, panelsProcessed[panel][1]])
newPage.paste(panelImg, (0, targetHeight))
targetHeight += panels[panel][2]
newPage.save(os.path.join(path, fileExpanded[0] + '-' + str(pageNumber) + '.png'), 'PNG')
targetHeight += panelsProcessed[panel][2]
newPage.save(os.path.join(path, os.path.splitext(name)[0] + '-' + str(pageNumber) + '.png'), 'PNG')
pageNumber += 1
os.remove(filePath)
except Exception:
@@ -238,13 +219,13 @@ def main(argv=None, qtGUI=None):
work = []
pagenumber = 1
splitWorkerOutput = []
splitWorkerPool = Pool()
splitWorkerPool = Pool(maxtasksperchild=10)
if options.merge:
print("Merging images...")
directoryNumer = 1
mergeWork = []
mergeWorkerOutput = []
mergeWorkerPool = Pool()
mergeWorkerPool = Pool(maxtasksperchild=10)
mergeWork.append([options.targetDir])
for root, dirs, files in os.walk(options.targetDir, False):
dirs, files = walkSort(dirs, files)
@@ -263,7 +244,8 @@ def main(argv=None, qtGUI=None):
raise UserWarning("Conversion interrupted.")
if len(mergeWorkerOutput) > 0:
rmtree(options.targetDir, True)
raise RuntimeError("One of workers crashed. Cause: " + mergeWorkerOutput[0][0], mergeWorkerOutput[0][1])
raise RuntimeError("One of workers crashed. Cause: " + mergeWorkerOutput[0][0],
mergeWorkerOutput[0][1])
print("Splitting images...")
for root, _, files in os.walk(options.targetDir, False):
for name in files:
@@ -286,7 +268,8 @@ def main(argv=None, qtGUI=None):
raise UserWarning("Conversion interrupted.")
if len(splitWorkerOutput) > 0:
rmtree(options.targetDir, True)
raise RuntimeError("One of workers crashed. Cause: " + splitWorkerOutput[0][0], splitWorkerOutput[0][1])
raise RuntimeError("One of workers crashed. Cause: " + splitWorkerOutput[0][0],
splitWorkerOutput[0][1])
if options.inPlace:
rmtree(options.sourceDir)
move(options.targetDir, options.sourceDir)

View File

@@ -120,7 +120,7 @@ class ComicPageParser:
width, height = self.image.size
dstwidth, dstheight = self.size
if (width > height) != (dstwidth > dstheight) and width <= dstheight and height <= dstwidth \
and not self.opt.webtoon:
and not self.opt.webtoon and self.opt.splitter == 1:
self.payload.append(['R', self.source, self.image.rotate(90, Image.BICUBIC, True), self.color, self.fill])
elif (width > height) != (dstwidth > dstheight) and not self.opt.webtoon:
if self.opt.splitter != 1:
@@ -209,6 +209,8 @@ class ComicPage:
def __init__(self, options, mode, path, image, color, fill):
self.opt = options
_, self.size, self.palette, self.gamma = self.opt.profileData
if self.opt.hq:
self.size = (int(self.size[0] * 1.5), int(self.size[1] * 1.5))
self.image = image
self.color = color
self.fill = fill

View File

@@ -17,11 +17,9 @@
#
import os
from sys import version_info
from hashlib import md5
from html.parser import HTMLParser
from distutils.version import StrictVersion
from time import sleep
from shutil import rmtree, copy
from tempfile import mkdtemp
from zipfile import ZipFile, ZIP_DEFLATED

View File

@@ -23,6 +23,7 @@ import sys
from . import __version__
from .shared import dependencyCheck
def start():
dependencyCheck(3)
from . import KCC_gui
@@ -40,14 +41,16 @@ def start():
KCCUI.handleMessage(sys.argv[1])
sys.exit(KCCAplication.exec_())
def startC2E():
dependencyCheck(2)
from .comic2ebook import main
print('comic2ebook v' + __version__ + ' - Written by Ciro Mattia Gonano and Pawel Jastrzebski.')
sys.exit(main(sys.argv[1:]))
def startC2P():
dependencyCheck(1)
from .comic2panel import main
print('comic2panel v' + __version__ + ' - Written by Ciro Mattia Gonano and Pawel Jastrzebski.')
sys.exit(main(sys.argv[1:]))
sys.exit(main(sys.argv[1:]))

View File

@@ -30,7 +30,7 @@
<key>CFBundleExecutable</key>
<string>MacOS/Kindle Comic Converter</string>
<key>CFBundleGetInfoString</key>
<string>KindleComicConverter 5.3.1, written 2012-2017 by Ciro Mattia Gonano and Pawel Jastrzebski</string>
<string>KindleComicConverter 5.4.0, written 2012-2017 by Ciro Mattia Gonano and Pawel Jastrzebski</string>
<key>CFBundleIconFile</key>
<string>comic2ebook.icns</string>
<key>CFBundleIdentifier</key>
@@ -42,11 +42,11 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>5.3.1</string>
<string>5.4.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>5.3.1</string>
<string>5.4.0</string>
<key>LSEnvironment</key>
<dict>
<key>PATH</key>

View File

@@ -2,10 +2,10 @@
"""
pip/pyinstaller build script for KCC.
Usage (Windows):
py -3 setup.py build_binary
Install as Python package:
python3 setup.py install
Usage (Linux/OS X):
Create EXE/APP/DEB:
python3 setup.py build_binary
"""
@@ -14,32 +14,22 @@ import sys
import shutil
import setuptools
import distutils.cmd
from distutils.command.build import build
from kindlecomicconverter import __version__
NAME = 'KindleComicConverter'
MAIN = 'kcc.py'
VERSION = __version__
class BuildBinaryCommand(distutils.cmd.Command):
description = 'build binary release'
user_options = [
('pyz', None, 'build PYZ package'),
]
def initialize_options(self):
# noinspection PyAttributeOutsideInit
self.pyz = False
def finalize_options(self):
pass
def run(self):
if sys.platform == 'darwin':
if os.path.isfile('Kindle Comic Converter.spec'):
os.system('pyinstaller "Kindle Comic Converter.spec"')
else:
os.system('pyinstaller -y -F -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s --noupx kcc.py')
os.system('pyinstaller -y -F -i icons/comic2ebook.icns -n "Kindle Comic Converter" -w -s kcc.py')
shutil.copy('other/osx/7za', 'dist/Kindle Comic Converter.app/Contents/Resources')
shutil.copy('other/osx/unrar', 'dist/Kindle Comic Converter.app/Contents/Resources')
shutil.copy('other/osx/Info.plist', 'dist/Kindle Comic Converter.app/Contents')
@@ -92,6 +82,6 @@ setuptools.setup(
'python-slugify>=1.2.1',
'raven>=6.0.0',
],
classifiers = [],
classifiers=[],
zip_safe=False,
)