1
0
mirror of https://github.com/ciromattia/kcc synced 2026-04-17 14:38:47 +00:00

Compare commits

..

59 Commits

Author SHA1 Message Date
Alex Xu
0e56cde791 Bump version to 9.2.0 2025-10-21 13:46:14 -07:00
Alex Xu
9a021ad5d4 no autocontrast option, don't autocontrast extremely low contrast images (#1128) 2025-10-21 13:13:42 -07:00
Alex Xu
d648081086 Add comic bundle announcements 2025-10-21 12:43:07 -07:00
Alex Xu
b2a8b364d9 disable RTL on webtoon 2025-10-21 12:04:35 -07:00
Alex Xu
1f935a0635 add webtoon tip 2025-10-21 11:54:47 -07:00
Alex Xu
58b9651ff3 add colorsoft warning (#1131) 2025-10-21 11:49:31 -07:00
Alex Xu
11a56dd892 add webtoon note 2025-10-21 10:46:27 -07:00
Alex Xu
c23af709cf Revert "webtoon increase minimum vertical blank space threshold"
This reverts commit 80ea17ff02.
2025-10-20 23:47:36 -07:00
Alex Xu
87a7197c16 warn scribe png mobi is broken (#1130) 2025-10-20 20:44:24 -07:00
Alex Xu
5813f914fc split image in half for better color detection comment 2025-10-20 19:29:54 -07:00
Alex Xu
530ae410d4 detect silver color (#1129) 2025-10-20 18:38:35 -07:00
Alex Xu
a7428f18b6 Fine tune color detection (#1126)
* initial commit

* refactor

* pdf colorspace note

* refactor

* webtoons are always color
2025-10-20 17:09:01 -07:00
Alex Xu
06194b33ad disable default gamma correction/darkening of 1.8 (1.0 is disabled) (#1030)
* disable default gamma correction of 1.8 to 1.0 (disabled)

* update gamma tooltip

* custom gamma
2025-10-20 11:42:59 -07:00
Alex Xu
80ea17ff02 webtoon increase minimum vertical blank space threshold 2025-10-20 10:53:10 -07:00
dependabot[bot]
e0466ad040 Bump actions/setup-node from 5 to 6 (#1125)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 5 to 6.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-19 19:32:25 -07:00
Alex Xu
df924824aa Update package-windows7.yml 2025-10-19 11:33:32 -07:00
Alex Xu
82f1c1bb0a remove webtoon cover (#1124) 2025-10-19 11:27:28 -07:00
Alex Xu
6f801a3334 fix all other webtoon bugs (#1123)
* adjust webtoon thresholds

* fix arbitrary width webtoons

* virtual pages can exceed height by 20%

* prevent webtoon bad options

* adjust virtual page ratio

* don't change history too much

* GUI change

* add TODO

* add scroll illusion
2025-10-19 11:15:14 -07:00
Alex Xu
ee8fc77ca9 webtoon avoid tiny panels (#1122) 2025-10-17 14:06:36 -07:00
Alex Xu
1da06b43e2 windows7-build 2025-10-16 22:56:00 -07:00
Alex Xu
d28740491e adjust webtoon edge detection threshold 2025-10-16 22:47:37 -07:00
Alex Xu
e2b98db1a2 fix webtoon perf_counters 2025-10-16 22:06:21 -07:00
Alex Xu
4c5ec95a9b fix webtoon source directory is empty 2025-10-16 22:06:21 -07:00
Alex Xu
e56612228c make webtoon faster/more accurate (#1117) 2025-10-16 11:44:40 -07:00
Alex Xu
3470d01367 fix webtoon panel detection by doubling max height (#1115) 2025-10-15 14:31:06 -07:00
Alex Xu
a795a84899 raise maximum dimensions from 2400x3840 tp 3200x5120 (#1114) 2025-10-14 14:24:53 -07:00
Alex Xu
ef44f6f285 Differentiate source directory is empty errors (#1113) 2025-10-14 14:13:36 -07:00
Alex Xu
6acdebae3c fix webtoon cannot identify image file (#1112) 2025-10-14 13:03:02 -07:00
Alex Xu
dc8136f6fd fix webtoon panel detection (#1111) 2025-10-14 12:51:40 -07:00
kiryl85
28ec2d23fc Remove duplicated import from PySide6.QtWidgets (#1107)
- QApplication imported twice
2025-10-13 13:40:17 -07:00
dependabot[bot]
108363ce2f Bump github/codeql-action from 3 to 4 (#1105)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-12 18:49:51 -07:00
Alex Xu
543fb81027 limit pyside6 to 6.9 2025-10-12 09:48:42 -07:00
Airat Valiullin
e6c6c05d40 fix typo: change "profil" to "profile" in the cli and README (#1103) 2025-10-12 08:58:24 -07:00
kiryl85
18cd55f439 Open metadata editor after doubleclick on source (#1102) 2025-10-10 19:38:41 -07:00
Alex Xu
7ce4438886 Merge pull request #1101 from kiryl85/update-title-generation
Update title generation
2025-10-09 11:10:49 -07:00
kiryl
841ec517ac Allow custom title on fusion or single source only 2025-10-09 09:17:43 +02:00
kiryl
4614fdd07a Add Title gui
Allow to set custom title on mobi without editing metadata.
2025-10-09 09:15:29 +02:00
kiryl
d0b72e1c83 Update Metadata Title checkbox behaviour
- `Metadata Title` is now tristate checkbox, where metadata title
is combined with generated title or used explicit or just ignored
2025-10-09 09:15:29 +02:00
Alex Xu
e11755b118 Update README.md 2025-10-07 19:34:51 -07:00
Alex Xu
a9bfc152bf Update README.md 2025-10-06 23:51:31 -07:00
Alex Xu
f228ed261e Update README.md 2025-10-06 23:49:45 -07:00
Alex Xu
b8020758a6 Update README.md 2025-10-06 23:46:16 -07:00
Alex Xu
6540638e03 Update README.md 2025-10-06 23:37:03 -07:00
Alex Xu
565e4aec45 don't modify metadata in calibre note 2025-10-06 23:36:00 -07:00
Alex Xu
0453fcfc90 add blank page to readme 2025-10-06 23:34:56 -07:00
kiryl85
a6ee867da2 fix bookmarks generation (#1100)
- generate bookmarks if they are present in
ComicInfo.xml file
2025-10-06 13:01:39 -07:00
Alex Xu
1e4434fc7c fix various metadata editor bugs (#1099) 2025-10-05 18:05:16 -07:00
kiryl85
7122945fa2 Add Title field to Metadata editor (#1096)
* Add Title field to Metadata editor

* Add missing colon to Title label
2025-10-05 16:42:49 -07:00
Alex Xu
788163f3df label win7 as legacy 2025-10-02 10:22:09 -07:00
Alex Xu
3fa90735d5 fix interpanel crop with color images with black borders; (#1094) 2025-09-30 10:27:52 -07:00
Luís Melo
e6cd26c773 feat: update image.py to support remarkable paper pro move profile (#1092)
* feat: update image.py to support remarkable paper pro move profile

* fix: update KCC_gui.py to include "reMarkable Paper Pro Move"
2025-09-29 10:58:31 -07:00
Alex Xu
e92b5c74de fix color detection false positives (#1088) 2025-09-21 09:12:41 -07:00
Alex Xu
a031e4622e fix multiframe gif images (#1089) 2025-09-21 09:12:26 -07:00
Alex Xu
ae1989335e Update README.md 2025-09-16 12:50:00 -07:00
Alex Xu
a7cb3565aa Merge pull request #1087 from jtronicus/fix-zip-folders
Fix makeZIP function to allow for special characters in folder names
2025-09-16 12:48:27 -07:00
Alex Xu
ec8098291f cwd in all 7z locations 2025-09-15 14:50:35 -07:00
Alex Xu
297e8757f0 Update README.md 2025-09-15 11:24:11 -07:00
Jtronicus
2cfd6d9728 Fix makeZIP function to allow for special characters in folder names 2025-09-15 07:23:17 -07:00
Alex Xu
5733786dc6 Update README.md 2025-09-13 20:22:59 -07:00
19 changed files with 1386 additions and 1065 deletions

View File

@@ -42,7 +42,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -56,7 +56,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v3
uses: github/codeql-action/autobuild@v4
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -69,6 +69,6 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v4
with:
category: "/language:${{matrix.language}}"

View File

@@ -69,7 +69,7 @@ jobs:
# apply provisioning profile
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles
- uses: actions/setup-node@v5
- uses: actions/setup-node@v6
with:
node-version: 16
- run: npm install -g appdmg

View File

@@ -43,7 +43,7 @@ jobs:
pip3 install --upgrade pip setuptools wheel pyinstaller certifi
pip3 install --upgrade -r requirements-osx-legacy.txt
./gen_ui_files.sh
- uses: actions/setup-node@v5
- uses: actions/setup-node@v6
with:
node-version: 16
- run: npm install -g appdmg

View File

@@ -48,7 +48,7 @@ jobs:
id: upload-unsigned-artifact
uses: actions/upload-artifact@v4
with:
name: windows-build
name: windows7-build
path: dist/*.exe
- name: Release
uses: softprops/action-gh-release@v2
@@ -57,5 +57,4 @@ jobs:
prerelease: true
generate_release_notes: true
files: |
LICENSE.txt
dist/*.exe

View File

@@ -11,7 +11,7 @@
like Kindle, Kobo, ReMarkable, and more.
Pages display in fullscreen without margins,
with proper fixed layout support.
Supported input formats include JPG/PNG/GIF image files in folders, archives, or PDFs.
Supported input formats include JPG/PNG image files in folders, archives, or PDFs.
Supported output formats include MOBI/AZW3, EPUB, KEPUB, CBZ, and PDF.
**NEW**: PDF output is now supported for direct conversion to reMarkable devices!
@@ -92,25 +92,28 @@ Click on **Assets** of the latest release.
You probably want either
- `KCC_*.*.*.exe` (Windows)
- `kcc_macos_arm_*.*.*.dmg` (recent Mac with Apple Silicon M1 chip or later)
- `kcc_macos_i386_*.*.*.dmg` (older Mac with Intel chip)
- `kcc_macos_i386_*.*.*.dmg` (older Mac with Intel chip macOS 12+)
There are also legacy macOS 10.14+ and Windows 7 experimental versions available.
The `c2e` and `c2p` versions are command line tools for power users.
On Windows 11, you may need to run in compatibility mode for an older Windows version.
On Mac, right click open to get past the security warning. macOS 12 Monterey or later is required, you can use https://dortania.github.io/OpenCore-Legacy-Patcher/ to get a newer macOS on unsupported hardware.
On Mac, right click open to get past the security warning.
For flatpak, Docker, and AppImage versions, refer to the wiki: https://github.com/ciromattia/kcc/wiki/Installation
## FAQ
- Should I use Calibre?
- No. Calibre doesn't properly support fixed layout EPUB/MOBI, so modifying KCC output in Calibre will break the formatting.
- No. Calibre doesn't properly support fixed layout EPUB/MOBI, so modifying KCC output (even just metadata!) in Calibre will break the formatting.
Viewing KCC output in Calibre will also not work properly.
On 7th gen and later Kindles running firmware 5.15.1+, you can get cover thumbnails simply by USB dropping into documents folder.
On 6th gen and older, you can get cover thumbnails by keeping Kindle plugged in during conversion.
If you are careful to not modify the file however, you can still use Calibre, but direct USB dropping is reccomended.
- Blank pages?
- May happen when [using PNG with Kindle Scribe](https://github.com/ciromattia/kcc/issues/665) or [any format with a Kindle Colorsoft](https://github.com/ciromattia/kcc/issues/768). Solve by using JPG with Kindle Scribe or buying a Kobo Colour. Happens more often when turning pages really fast.
Going back a few pages and exiting and re-entering book should fix it temporarily.
- What output format should I use?
- MOBI for Kindles. CBZ for Kindle DX. CBZ for Koreader. KEPUB for Kobo.
- MOBI for Kindles. CBZ for Kindle DX. CBZ for Koreader. KEPUB for Kobo. PDF for ReMarkable.
- All options have additional information in tooltips if you hover over the option.
- To get the converted book onto your Kindle/Kobo, just drag and drop the mobi/kepub into the documents folder on your Kindle/Kobo via USB
- Right to left mode not working?
@@ -226,7 +229,7 @@ MAIN:
the maximal size of output file in MB. [Default=100MB for webtoon and 400MB for others]
PROCESSING:
-n, --noprocessing Do not modify image and ignore any profil or processing option
-n, --noprocessing Do not modify image and ignore any profile or processing option
-u, --upscale Resize images smaller than device's resolution
-s, --stretch Stretch images to device's resolution
-r SPLITTER, --splitter SPLITTER
@@ -234,6 +237,8 @@ PROCESSING:
-g GAMMA, --gamma GAMMA
Apply gamma correction to linearize the image [Default=Auto]
--autolevel Set most common dark pixel value to be black point for leveling.
--noautocontrast Disable autocontrast
--colorautocontrast Force autocontrast for all pages. Skipped when near blacks and whites don't exist
-c CROPPING, --cropping CROPPING
Set cropping mode. 0: Disabled 1: Margins 2: Margins + page numbers [Default=2]
--cp CROPPINGP, --croppingpower CROPPINGP
@@ -256,7 +261,7 @@ OUTPUT SETTINGS:
Output generated file to specified directory or file
-t TITLE, --title TITLE
Comic title [Default=filename or directory name]
--metadatatitle Write title from ComicInfo.xml or other embedded metadata
--metadatatitle Write title using ComicInfo.xml or other embedded metadata. 0: Don't use Title from metadata 1: Combine Title with default schema 2: Use Title only [Default=0]
-a AUTHOR, --author AUTHOR
Author name [Default=KCC]
-f FORMAT, --format FORMAT

1035
gui/KCC.ui

File diff suppressed because it is too large Load Diff

View File

@@ -62,56 +62,66 @@
<item row="1" column="1">
<widget class="QLineEdit" name="volumeLine"/>
</item>
<item row="2" column="0">
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Number:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<item row="3" column="1">
<widget class="QLineEdit" name="numberLine"/>
</item>
<item row="3" column="0">
<item row="4" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Writer:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<item row="4" column="1">
<widget class="QLineEdit" name="writerLine"/>
</item>
<item row="4" column="0">
<item row="5" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Penciller:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<item row="5" column="1">
<widget class="QLineEdit" name="pencillerLine"/>
</item>
<item row="5" column="0">
<item row="6" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Inker:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<item row="6" column="1">
<widget class="QLineEdit" name="inkerLine"/>
</item>
<item row="6" column="0">
<item row="7" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Colorist:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<item row="7" column="1">
<widget class="QLineEdit" name="coloristLine"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Title:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="titleLine"/>
</item>
</layout>
</widget>
</item>

View File

@@ -22,7 +22,7 @@ import itertools
from pathlib import Path
from PySide6.QtCore import (QSize, QUrl, Qt, Signal, QIODeviceBase, QEvent, QThread, QSettings)
from PySide6.QtGui import (QColor, QIcon, QPixmap, QDesktopServices)
from PySide6.QtWidgets import (QApplication, QLabel, QListWidgetItem, QMainWindow, QApplication, QSystemTrayIcon, QFileDialog, QMessageBox, QDialog)
from PySide6.QtWidgets import (QApplication, QLabel, QListWidgetItem, QMainWindow, QSystemTrayIcon, QFileDialog, QMessageBox, QDialog)
from PySide6.QtNetwork import (QLocalSocket, QLocalServer)
import os
@@ -189,7 +189,9 @@ class VersionThread(QThread):
delta = expiration - datetime.now(timezone.utc)
time_left = f"{delta.days} day(s) left"
icon = 'info'
if category == 'humbleBundles':
if category == 'humbleMangaBundles':
icon = 'humble'
if category == 'humbleComicBundles':
icon = 'bindle'
if category == 'kofi':
icon = 'kofi'
@@ -290,6 +292,10 @@ class WorkerThread(QThread):
options.gamma = float(GUI.gammaValue)
if GUI.autoLevelBox.isChecked():
options.autolevel = True
if GUI.autocontrastBox.checkState() == Qt.CheckState.PartiallyChecked:
options.noautocontrast = True
elif GUI.autocontrastBox.checkState() == Qt.CheckState.Checked:
options.colorautocontrast = True
if GUI.croppingBox.isChecked():
if GUI.croppingBox.checkState() == Qt.CheckState.PartiallyChecked:
options.cropping = 1
@@ -321,8 +327,10 @@ class WorkerThread(QThread):
options.maximizestrips = True
if GUI.disableProcessingBox.isChecked():
options.noprocessing = True
if GUI.metadataTitleBox.isChecked():
options.metadatatitle = True
if GUI.metadataTitleBox.checkState() == Qt.CheckState.PartiallyChecked:
options.metadatatitle = 1
elif GUI.metadataTitleBox.checkState() == Qt.CheckState.Checked:
options.metadatatitle = 2
if GUI.deleteBox.isChecked():
options.delete = True
if GUI.spreadShiftBox.isChecked():
@@ -344,6 +352,8 @@ class WorkerThread(QThread):
options.customheight = str(GUI.heightBox.value())
if GUI.targetDirectory != '':
options.output = GUI.targetDirectory
if GUI.titleEdit.text():
options.title = str(GUI.titleEdit.text())
if GUI.authorEdit.text():
options.author = str(GUI.authorEdit.text())
if GUI.chunkSizeCheckBox.isChecked():
@@ -367,6 +377,11 @@ class WorkerThread(QThread):
except Exception as e:
print('Fusion Failed. ' + str(e))
MW.addMessage.emit('Fusion Failed. ' + str(e), 'error', True)
elif len(currentJobs) > 1 and options.title != 'defaulttitle':
currentJobs.clear()
error_message = 'Process Failed. Custom title can\'t be set when processing more than 1 source.\nDid you forget to check fusion?'
print(error_message)
MW.addMessage.emit(error_message, 'error', True)
for job in currentJobs:
sleep(0.5)
if not self.conversionAlive:
@@ -479,8 +494,10 @@ class WorkerThread(QThread):
k = kindle.Kindle(options.profile)
if k.path and k.coverSupport:
for item in outputPath:
comic2ebook.options.covers[outputPath.index(item)][0].saveToKindle(
k, comic2ebook.options.covers[outputPath.index(item)][1])
cover = comic2ebook.options.covers[outputPath.index(item)][0]
if cover:
cover.saveToKindle(
k, comic2ebook.options.covers[outputPath.index(item)][1])
MW.addMessage.emit('Kindle detected. Uploading covers... <b>Done!</b>', 'info', False)
else:
GUI.progress.content = ''
@@ -598,31 +615,26 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
GUI.jobList.addItem(fname)
GUI.jobList.scrollToBottom()
def selectFileMetaEditor(self):
sname = ''
if QApplication.keyboardModifiers() == Qt.ShiftModifier:
dname = QFileDialog.getExistingDirectory(MW, 'Select directory', self.lastPath)
if dname != '':
sname = os.path.join(dname, 'ComicInfo.xml')
if sys.platform.startswith('win'):
sname = sname.replace('/', '\\')
self.lastPath = os.path.abspath(sname)
else:
if self.sevenzip:
fname = QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
'Comic (*.cbz *.cbr *.cb7)')
def selectFileMetaEditor(self, sname):
if not sname:
if QApplication.keyboardModifiers() == Qt.ShiftModifier:
dname = QFileDialog.getExistingDirectory(MW, 'Select directory', self.lastPath)
if dname != '':
sname = os.path.join(dname, 'ComicInfo.xml')
self.lastPath = os.path.dirname(sname)
else:
fname = ['']
self.showDialog("Editor is disabled due to a lack of 7z.", 'error')
self.addMessage('<a href="https://github.com/ciromattia/kcc#7-zip">Install 7z (link)</a>'
' to enable metadata editing.', 'warning')
if fname[0] != '':
if sys.platform.startswith('win'):
sname = fname[0].replace('/', '\\')
if self.sevenzip:
fname = QFileDialog.getOpenFileName(MW, 'Select file', self.lastPath,
'Comic (*.cbz *.cbr *.cb7)')
else:
fname = ['']
self.showDialog("Editor is disabled due to a lack of 7z.", 'error')
self.addMessage('<a href="https://github.com/ciromattia/kcc#7-zip">Install 7z (link)</a>'
' to enable metadata editing.', 'warning')
if fname[0] != '':
sname = fname[0]
self.lastPath = os.path.abspath(os.path.join(sname, os.pardir))
if sname != '':
self.lastPath = os.path.abspath(os.path.join(sname, os.pardir))
if sname:
try:
self.editor.loadData(sname)
except Exception as err:
@@ -713,24 +725,42 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
def togglewebtoonBox(self, value):
if value:
self.addMessage('You can choose a taller device profile to get taller cuts in webtoon mode.', 'info')
self.addMessage('Try reading webtoon panels side by side in landscape!', 'info')
GUI.qualityBox.setEnabled(False)
GUI.qualityBox.setChecked(False)
GUI.mangaBox.setEnabled(False)
GUI.mangaBox.setChecked(False)
GUI.rotateBox.setEnabled(False)
GUI.rotateBox.setChecked(False)
GUI.borderBox.setEnabled(False)
GUI.borderBox.setCheckState(Qt.CheckState.PartiallyChecked)
GUI.upscaleBox.setEnabled(False)
GUI.upscaleBox.setChecked(True)
GUI.chunkSizeCheckBox.setEnabled(False)
GUI.chunkSizeCheckBox.setChecked(False)
GUI.upscaleBox.setChecked(False)
GUI.croppingBox.setEnabled(False)
GUI.croppingBox.setChecked(False)
GUI.interPanelCropBox.setEnabled(False)
GUI.interPanelCropBox.setChecked(False)
GUI.autoLevelBox.setEnabled(False)
GUI.autoLevelBox.setChecked(False)
GUI.autocontrastBox.setEnabled(False)
GUI.autocontrastBox.setChecked(False)
else:
profile = GUI.profiles[str(GUI.deviceBox.currentText())]
if profile['PVOptions']:
GUI.qualityBox.setEnabled(True)
GUI.mangaBox.setEnabled(True)
GUI.rotateBox.setEnabled(True)
GUI.upscaleBox.setEnabled(True)
GUI.chunkSizeCheckBox.setEnabled(True)
GUI.borderBox.setEnabled(True)
profile = GUI.profiles[str(GUI.deviceBox.currentText())]
if profile['Label'] != 'KS':
GUI.upscaleBox.setEnabled(True)
GUI.croppingBox.setEnabled(True)
GUI.interPanelCropBox.setEnabled(True)
GUI.autoLevelBox.setEnabled(True)
GUI.autocontrastBox.setEnabled(True)
GUI.autocontrastBox.setChecked(True)
def togglequalityBox(self, value):
profile = GUI.profiles[str(GUI.deviceBox.currentText())]
@@ -744,10 +774,42 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
else:
GUI.upscaleBox.setEnabled(True)
GUI.upscaleBox.setChecked(profile['DefaultUpscale'])
def toggleImageFormatBox(self, value):
profile = GUI.profiles[str(GUI.deviceBox.currentText())]
if value == 1:
if profile['Label'] == 'KS':
current_format = GUI.formats[str(GUI.formatBox.currentText())]['format']
for bad_format in ('MOBI', 'EPUB'):
if bad_format in current_format:
self.addMessage('Scribe PNG MOBI/EPUB has a lot of problems like blank pages/sections. Use JPG instead.', 'warning')
break
def togglechunkSizeCheckBox(self, value):
GUI.chunkSizeWidget.setVisible(value)
def toggletitleEdit(self, value):
if value:
self.metadataTitleBox.setChecked(False)
def togglefileFusionBox(self, value):
if value:
GUI.metadataTitleBox.setChecked(False)
GUI.metadataTitleBox.setEnabled(False)
else:
GUI.metadataTitleBox.setEnabled(True)
def togglemetadataTitleBox(self, value):
if value:
GUI.titleEdit.setText(None)
def editSourceMetadata(self, item):
if item.icon().isNull():
sname = item.text()
if os.path.isdir(sname):
sname = os.path.join(sname, "ComicInfo.xml")
self.selectFileMetaEditor(sname)
def changeGamma(self, value):
valueRaw = int(5 * round(float(value) / 5))
value = '%.2f' % (float(valueRaw) / 100)
@@ -775,15 +837,20 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
self.modeChange(1)
GUI.colorBox.setChecked(profile['ForceColor'])
self.changeFormat()
GUI.gammaSlider.setValue(0)
self.changeGamma(0)
if not GUI.webtoonBox.isChecked():
GUI.qualityBox.setEnabled(profile['PVOptions'])
GUI.upscaleBox.setChecked(profile['DefaultUpscale'])
if profile['Label'] == 'KS':
GUI.upscaleBox.setDisabled(True)
else:
GUI.upscaleBox.setEnabled(True)
if not GUI.webtoonBox.isChecked():
GUI.upscaleBox.setEnabled(True)
if profile['Label'] == 'KCS':
current_format = GUI.formats[str(GUI.formatBox.currentText())]['format']
for bad_format in ('MOBI', 'EPUB'):
if bad_format in current_format:
self.addMessage('Colorsoft MOBI/EPUB can have blank pages. Just go back a few pages, exit, and reenter book.', 'info')
break
if not profile['PVOptions']:
GUI.qualityBox.setChecked(False)
if str(GUI.deviceBox.currentText()) == 'Other':
@@ -924,6 +991,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
'qualityBox': GUI.qualityBox.checkState(),
'gammaBox': GUI.gammaBox.checkState(),
'autoLevelBox': GUI.autoLevelBox.checkState(),
'autocontrastBox': GUI.autocontrastBox.checkState(),
'croppingBox': GUI.croppingBox.checkState(),
'croppingPowerSlider': float(self.croppingPowerValue) * 100,
'preserveMarginBox': self.preserveMarginBox.value(),
@@ -1103,7 +1171,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KO',
},
"Kindle Colorsoft": {
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': True, 'Label': 'KO',
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': True, 'Label': 'KCS',
},
"Kindle Paperwhite 7/10": {'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0,
'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KPW34'},
@@ -1161,6 +1229,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
'Label': 'Rmk2'},
"reMarkable Paper Pro": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 3, 'DefaultUpscale': True, 'ForceColor': True,
'Label': 'RmkPP'},
"reMarkable Paper Pro Move": {'PVOptions': False, 'ForceExpert': False, 'DefaultFormat': 3, 'DefaultUpscale': True, 'ForceColor': True,
'Label': 'RmkPPMove'},
"Other": {'PVOptions': False, 'ForceExpert': True, 'DefaultFormat': 1, 'DefaultUpscale': False, 'ForceColor': False,
'Label': 'OTHER'},
}
@@ -1183,6 +1253,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
"reMarkable 1",
"reMarkable 2",
"reMarkable Paper Pro",
"reMarkable Paper Pro Move",
"Separator",
"Other",
"Separator",
@@ -1254,9 +1325,14 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
GUI.croppingPowerSlider.valueChanged.connect(self.changeCroppingPower)
GUI.webtoonBox.stateChanged.connect(self.togglewebtoonBox)
GUI.qualityBox.stateChanged.connect(self.togglequalityBox)
GUI.mozJpegBox.stateChanged.connect(self.toggleImageFormatBox)
GUI.chunkSizeCheckBox.stateChanged.connect(self.togglechunkSizeCheckBox)
GUI.deviceBox.activated.connect(self.changeDevice)
GUI.formatBox.activated.connect(self.changeFormat)
GUI.titleEdit.textChanged.connect(self.toggletitleEdit)
GUI.fileFusionBox.stateChanged.connect(self.togglefileFusionBox)
GUI.metadataTitleBox.stateChanged.connect(self.togglemetadataTitleBox)
GUI.jobList.itemDoubleClicked.connect(self.editSourceMetadata)
MW.progressBarTick.connect(self.updateProgressbar)
MW.modeConvert.connect(self.modeConvert)
MW.addMessage.connect(self.addMessage)
@@ -1345,15 +1421,17 @@ class KCCGUI_MetaEditor(KCC_ui_editor.Ui_editorDialog):
self.editorWidget.setEnabled(True)
self.okButton.setEnabled(True)
self.statusLabel.setText('Separate authors with a comma.')
for field in (self.seriesLine, self.volumeLine, self.numberLine):
for field in (self.seriesLine, self.volumeLine, self.numberLine, self.titleLine):
field.setText(self.parser.data[field.objectName().capitalize()[:-4]])
for field in (self.writerLine, self.pencillerLine, self.inkerLine, self.coloristLine):
field.setText(', '.join(self.parser.data[field.objectName().capitalize()[:-4] + 's']))
if self.seriesLine.text() == '':
if file.endswith('.xml'):
self.seriesLine.setText(file.split('\\')[-2])
else:
self.seriesLine.setText(file.split('\\')[-1].split('/')[-1].split('.')[0])
for field in (self.seriesLine, self.titleLine):
if field.text() == '':
path = Path(file)
if file.endswith('.xml'):
field.setText(path.parent.name)
else:
field.setText(path.stem)
def saveData(self):
for field in (self.volumeLine, self.numberLine):
@@ -1363,7 +1441,8 @@ class KCCGUI_MetaEditor(KCC_ui_editor.Ui_editorDialog):
self.statusLabel.setText(field.objectName().capitalize()[:-4] + ' field must be a number.')
break
else:
self.parser.data['Series'] = self.cleanData(self.seriesLine.text())
for field in (self.seriesLine, self.titleLine):
self.parser.data[field.objectName().capitalize()[:-4]] = self.cleanData(field.text())
for field in (self.writerLine, self.pencillerLine, self.inkerLine, self.coloristLine):
values = self.cleanData(field.text()).split(',')
tmpData = []

View File

@@ -1,6 +1,6 @@
# Resource object code (Python 3)
# Created by: object code
# Created by: The Resource Compiler for Qt version 6.9.1
# Created by: The Resource Compiler for Qt version 6.9.3
# WARNING! All changes made in this file will be lost!
from PySide6 import QtCore
@@ -13813,9 +13813,9 @@ qt_resource_struct = b"\
\x00\x00\x00\xe4\x00\x00\x00\x00\x00\x01\x00\x01~z\
\x00\x00\x01\x98\x8a\xa4\xaa\xe2\
\x00\x00\x00\x9c\x00\x00\x00\x00\x00\x01\x00\x01\x1d\x90\
\x00\x00\x01\x99:\x8c\x10\x01\
\x00\x00\x01\x9a\x02\xcf\x8cb\
\x00\x00\x00\xbe\x00\x00\x00\x00\x00\x01\x00\x01=\xd0\
\x00\x00\x01\x99:}N\xda\
\x00\x00\x01\x9a\x02\xcf\x8cc\
\x00\x00\x00h\x00\x02\x00\x00\x00\x01\x00\x00\x00\x22\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00x\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\

View File

@@ -3,7 +3,7 @@
################################################################################
## Form generated from reading UI file 'KCC.ui'
##
## Created by: Qt User Interface Compiler version 6.9.1
## Created by: Qt User Interface Compiler version 6.9.3
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
@@ -35,6 +35,164 @@ class Ui_mainWindow(object):
self.gridLayout = QGridLayout(self.centralWidget)
self.gridLayout.setObjectName(u"gridLayout")
self.gridLayout.setContentsMargins(-1, -1, -1, 5)
self.optionWidget = QWidget(self.centralWidget)
self.optionWidget.setObjectName(u"optionWidget")
self.gridLayout_2 = QGridLayout(self.optionWidget)
self.gridLayout_2.setObjectName(u"gridLayout_2")
self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
self.titleEdit = QLineEdit(self.optionWidget)
self.titleEdit.setObjectName(u"titleEdit")
sizePolicy = QSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.titleEdit.sizePolicy().hasHeightForWidth())
self.titleEdit.setSizePolicy(sizePolicy)
self.titleEdit.setFocusPolicy(Qt.FocusPolicy.ClickFocus)
self.titleEdit.setClearButtonEnabled(False)
self.gridLayout_2.addWidget(self.titleEdit, 0, 0, 1, 1)
self.mangaBox = QCheckBox(self.optionWidget)
self.mangaBox.setObjectName(u"mangaBox")
self.gridLayout_2.addWidget(self.mangaBox, 1, 0, 1, 1)
self.webtoonBox = QCheckBox(self.optionWidget)
self.webtoonBox.setObjectName(u"webtoonBox")
self.gridLayout_2.addWidget(self.webtoonBox, 2, 0, 1, 1)
self.croppingBox = QCheckBox(self.optionWidget)
self.croppingBox.setObjectName(u"croppingBox")
self.croppingBox.setTristate(True)
self.gridLayout_2.addWidget(self.croppingBox, 4, 2, 1, 1)
self.maximizeStrips = QCheckBox(self.optionWidget)
self.maximizeStrips.setObjectName(u"maximizeStrips")
self.gridLayout_2.addWidget(self.maximizeStrips, 4, 1, 1, 1)
self.deleteBox = QCheckBox(self.optionWidget)
self.deleteBox.setObjectName(u"deleteBox")
self.gridLayout_2.addWidget(self.deleteBox, 5, 1, 1, 1)
self.rotateFirstBox = QCheckBox(self.optionWidget)
self.rotateFirstBox.setObjectName(u"rotateFirstBox")
self.gridLayout_2.addWidget(self.rotateFirstBox, 8, 1, 1, 1)
self.outputSplit = QCheckBox(self.optionWidget)
self.outputSplit.setObjectName(u"outputSplit")
self.gridLayout_2.addWidget(self.outputSplit, 3, 1, 1, 1)
self.borderBox = QCheckBox(self.optionWidget)
self.borderBox.setObjectName(u"borderBox")
self.borderBox.setTristate(True)
self.gridLayout_2.addWidget(self.borderBox, 3, 0, 1, 1)
self.gammaBox = QCheckBox(self.optionWidget)
self.gammaBox.setObjectName(u"gammaBox")
self.gridLayout_2.addWidget(self.gammaBox, 2, 2, 1, 1)
self.upscaleBox = QCheckBox(self.optionWidget)
self.upscaleBox.setObjectName(u"upscaleBox")
self.upscaleBox.setTristate(True)
self.gridLayout_2.addWidget(self.upscaleBox, 2, 1, 1, 1)
self.chunkSizeCheckBox = QCheckBox(self.optionWidget)
self.chunkSizeCheckBox.setObjectName(u"chunkSizeCheckBox")
self.gridLayout_2.addWidget(self.chunkSizeCheckBox, 7, 1, 1, 1)
self.colorBox = QCheckBox(self.optionWidget)
self.colorBox.setObjectName(u"colorBox")
self.gridLayout_2.addWidget(self.colorBox, 3, 2, 1, 1)
self.rotateBox = QCheckBox(self.optionWidget)
self.rotateBox.setObjectName(u"rotateBox")
self.rotateBox.setTristate(True)
self.gridLayout_2.addWidget(self.rotateBox, 1, 1, 1, 1)
self.spreadShiftBox = QCheckBox(self.optionWidget)
self.spreadShiftBox.setObjectName(u"spreadShiftBox")
self.gridLayout_2.addWidget(self.spreadShiftBox, 5, 0, 1, 1)
self.disableProcessingBox = QCheckBox(self.optionWidget)
self.disableProcessingBox.setObjectName(u"disableProcessingBox")
self.gridLayout_2.addWidget(self.disableProcessingBox, 5, 2, 1, 1)
self.eraseRainbowBox = QCheckBox(self.optionWidget)
self.eraseRainbowBox.setObjectName(u"eraseRainbowBox")
self.gridLayout_2.addWidget(self.eraseRainbowBox, 7, 2, 1, 1)
self.noRotateBox = QCheckBox(self.optionWidget)
self.noRotateBox.setObjectName(u"noRotateBox")
self.gridLayout_2.addWidget(self.noRotateBox, 6, 1, 1, 1)
self.fileFusionBox = QCheckBox(self.optionWidget)
self.fileFusionBox.setObjectName(u"fileFusionBox")
self.gridLayout_2.addWidget(self.fileFusionBox, 6, 0, 1, 1)
self.authorEdit = QLineEdit(self.optionWidget)
self.authorEdit.setObjectName(u"authorEdit")
sizePolicy.setHeightForWidth(self.authorEdit.sizePolicy().hasHeightForWidth())
self.authorEdit.setSizePolicy(sizePolicy)
self.authorEdit.setFocusPolicy(Qt.FocusPolicy.ClickFocus)
self.authorEdit.setClearButtonEnabled(False)
self.gridLayout_2.addWidget(self.authorEdit, 0, 1, 1, 1)
self.qualityBox = QCheckBox(self.optionWidget)
self.qualityBox.setObjectName(u"qualityBox")
self.qualityBox.setTristate(True)
self.gridLayout_2.addWidget(self.qualityBox, 1, 2, 1, 1)
self.interPanelCropBox = QCheckBox(self.optionWidget)
self.interPanelCropBox.setObjectName(u"interPanelCropBox")
self.interPanelCropBox.setTristate(True)
self.gridLayout_2.addWidget(self.interPanelCropBox, 6, 2, 1, 1)
self.metadataTitleBox = QCheckBox(self.optionWidget)
self.metadataTitleBox.setObjectName(u"metadataTitleBox")
self.metadataTitleBox.setTristate(True)
self.gridLayout_2.addWidget(self.metadataTitleBox, 7, 0, 1, 1)
self.mozJpegBox = QCheckBox(self.optionWidget)
self.mozJpegBox.setObjectName(u"mozJpegBox")
self.mozJpegBox.setTristate(True)
self.gridLayout_2.addWidget(self.mozJpegBox, 4, 0, 1, 1)
self.autoLevelBox = QCheckBox(self.optionWidget)
self.autoLevelBox.setObjectName(u"autoLevelBox")
self.gridLayout_2.addWidget(self.autoLevelBox, 8, 2, 1, 1)
self.autocontrastBox = QCheckBox(self.optionWidget)
self.autocontrastBox.setObjectName(u"autocontrastBox")
self.autocontrastBox.setTristate(True)
self.gridLayout_2.addWidget(self.autocontrastBox, 9, 2, 1, 1)
self.gridLayout.addWidget(self.optionWidget, 5, 0, 1, 2)
self.jobList = QListWidget(self.centralWidget)
self.jobList.setObjectName(u"jobList")
self.jobList.setMinimumSize(QSize(0, 150))
@@ -45,6 +203,66 @@ class Ui_mainWindow(object):
self.gridLayout.addWidget(self.jobList, 2, 0, 1, 2)
self.customWidget = QWidget(self.centralWidget)
self.customWidget.setObjectName(u"customWidget")
self.customWidget.setVisible(False)
self.gridLayout_3 = QGridLayout(self.customWidget)
self.gridLayout_3.setObjectName(u"gridLayout_3")
self.gridLayout_3.setContentsMargins(0, 0, 0, 0)
self.hLabel = QLabel(self.customWidget)
self.hLabel.setObjectName(u"hLabel")
sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred)
sizePolicy1.setHorizontalStretch(0)
sizePolicy1.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.hLabel.sizePolicy().hasHeightForWidth())
self.hLabel.setSizePolicy(sizePolicy1)
self.gridLayout_3.addWidget(self.hLabel, 0, 2, 1, 1)
self.widthBox = QSpinBox(self.customWidget)
self.widthBox.setObjectName(u"widthBox")
self.widthBox.setMaximum(3200)
self.gridLayout_3.addWidget(self.widthBox, 0, 1, 1, 1)
self.wLabel = QLabel(self.customWidget)
self.wLabel.setObjectName(u"wLabel")
sizePolicy1.setHeightForWidth(self.wLabel.sizePolicy().hasHeightForWidth())
self.wLabel.setSizePolicy(sizePolicy1)
self.gridLayout_3.addWidget(self.wLabel, 0, 0, 1, 1)
self.heightBox = QSpinBox(self.customWidget)
self.heightBox.setObjectName(u"heightBox")
self.heightBox.setMaximum(5120)
self.gridLayout_3.addWidget(self.heightBox, 0, 3, 1, 1)
self.gridLayout.addWidget(self.customWidget, 8, 0, 1, 2)
self.gammaWidget = QWidget(self.centralWidget)
self.gammaWidget.setObjectName(u"gammaWidget")
self.gammaWidget.setVisible(False)
self.horizontalLayout_2 = QHBoxLayout(self.gammaWidget)
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
self.gammaLabel = QLabel(self.gammaWidget)
self.gammaLabel.setObjectName(u"gammaLabel")
self.horizontalLayout_2.addWidget(self.gammaLabel)
self.gammaSlider = QSlider(self.gammaWidget)
self.gammaSlider.setObjectName(u"gammaSlider")
self.gammaSlider.setMaximum(250)
self.gammaSlider.setSingleStep(5)
self.gammaSlider.setOrientation(Qt.Orientation.Horizontal)
self.horizontalLayout_2.addWidget(self.gammaSlider)
self.gridLayout.addWidget(self.gammaWidget, 7, 0, 1, 2)
self.toolWidget = QWidget(self.centralWidget)
self.toolWidget.setObjectName(u"toolWidget")
self.horizontalLayout = QHBoxLayout(self.toolWidget)
@@ -81,21 +299,70 @@ class Ui_mainWindow(object):
self.gridLayout.addWidget(self.toolWidget, 0, 0, 1, 2)
self.progressBar = QProgressBar(self.centralWidget)
self.progressBar.setObjectName(u"progressBar")
self.progressBar.setMinimumSize(QSize(0, 30))
font = QFont()
font.setBold(True)
self.progressBar.setFont(font)
self.progressBar.setVisible(False)
self.progressBar.setAlignment(Qt.AlignmentFlag.AlignJustify|Qt.AlignmentFlag.AlignVCenter)
self.gridLayout.addWidget(self.progressBar, 1, 0, 1, 2)
self.croppingWidget = QWidget(self.centralWidget)
self.croppingWidget.setObjectName(u"croppingWidget")
self.croppingWidget.setVisible(False)
self.gridLayout_5 = QGridLayout(self.croppingWidget)
self.gridLayout_5.setObjectName(u"gridLayout_5")
self.gridLayout_5.setContentsMargins(0, 0, 0, 0)
self.preserveMarginLabel = QLabel(self.croppingWidget)
self.preserveMarginLabel.setObjectName(u"preserveMarginLabel")
self.gridLayout_5.addWidget(self.preserveMarginLabel, 1, 0, 1, 1)
self.croppingPowerLabel = QLabel(self.croppingWidget)
self.croppingPowerLabel.setObjectName(u"croppingPowerLabel")
self.gridLayout_5.addWidget(self.croppingPowerLabel, 0, 0, 1, 1)
self.croppingPowerSlider = QSlider(self.croppingWidget)
self.croppingPowerSlider.setObjectName(u"croppingPowerSlider")
self.croppingPowerSlider.setMaximum(300)
self.croppingPowerSlider.setSingleStep(1)
self.croppingPowerSlider.setOrientation(Qt.Orientation.Horizontal)
self.gridLayout_5.addWidget(self.croppingPowerSlider, 0, 1, 1, 1)
self.preserveMarginBox = QSpinBox(self.croppingWidget)
self.preserveMarginBox.setObjectName(u"preserveMarginBox")
sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
sizePolicy2.setHorizontalStretch(0)
sizePolicy2.setVerticalStretch(0)
sizePolicy2.setHeightForWidth(self.preserveMarginBox.sizePolicy().hasHeightForWidth())
self.preserveMarginBox.setSizePolicy(sizePolicy2)
self.preserveMarginBox.setMaximum(99)
self.preserveMarginBox.setSingleStep(5)
self.preserveMarginBox.setValue(0)
self.gridLayout_5.addWidget(self.preserveMarginBox, 1, 1, 1, 1)
self.gridLayout.addWidget(self.croppingWidget, 9, 0, 1, 2)
self.buttonWidget = QWidget(self.centralWidget)
self.buttonWidget.setObjectName(u"buttonWidget")
sizePolicy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.buttonWidget.sizePolicy().hasHeightForWidth())
self.buttonWidget.setSizePolicy(sizePolicy)
sizePolicy3 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
sizePolicy3.setHorizontalStretch(0)
sizePolicy3.setVerticalStretch(0)
sizePolicy3.setHeightForWidth(self.buttonWidget.sizePolicy().hasHeightForWidth())
self.buttonWidget.setSizePolicy(sizePolicy3)
self.gridLayout_4 = QGridLayout(self.buttonWidget)
self.gridLayout_4.setObjectName(u"gridLayout_4")
self.gridLayout_4.setContentsMargins(0, 0, 0, 0)
self.convertButton = QPushButton(self.buttonWidget)
self.convertButton.setObjectName(u"convertButton")
self.convertButton.setMinimumSize(QSize(0, 30))
font = QFont()
font.setBold(True)
self.convertButton.setFont(font)
icon4 = QIcon()
icon4.addFile(u":/Other/icons/convert.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
@@ -138,11 +405,8 @@ class Ui_mainWindow(object):
self.defaultOutputFolderBox = QCheckBox(self.buttonWidget)
self.defaultOutputFolderBox.setObjectName(u"defaultOutputFolderBox")
sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
sizePolicy1.setHorizontalStretch(0)
sizePolicy1.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.defaultOutputFolderBox.sizePolicy().hasHeightForWidth())
self.defaultOutputFolderBox.setSizePolicy(sizePolicy1)
sizePolicy2.setHeightForWidth(self.defaultOutputFolderBox.sizePolicy().hasHeightForWidth())
self.defaultOutputFolderBox.setSizePolicy(sizePolicy2)
self.defaultOutputFolderBox.setTristate(True)
self.gridLayout_4.addWidget(self.defaultOutputFolderBox, 0, 4, 1, 1)
@@ -163,258 +427,10 @@ class Ui_mainWindow(object):
self.gridLayout.addWidget(self.buttonWidget, 3, 0, 1, 2)
self.progressBar = QProgressBar(self.centralWidget)
self.progressBar.setObjectName(u"progressBar")
self.progressBar.setMinimumSize(QSize(0, 30))
self.progressBar.setFont(font)
self.progressBar.setVisible(False)
self.progressBar.setAlignment(Qt.AlignmentFlag.AlignJustify|Qt.AlignmentFlag.AlignVCenter)
self.gridLayout.addWidget(self.progressBar, 1, 0, 1, 2)
self.customWidget = QWidget(self.centralWidget)
self.customWidget.setObjectName(u"customWidget")
self.customWidget.setVisible(False)
self.gridLayout_3 = QGridLayout(self.customWidget)
self.gridLayout_3.setObjectName(u"gridLayout_3")
self.gridLayout_3.setContentsMargins(0, 0, 0, 0)
self.hLabel = QLabel(self.customWidget)
self.hLabel.setObjectName(u"hLabel")
sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred)
sizePolicy2.setHorizontalStretch(0)
sizePolicy2.setVerticalStretch(0)
sizePolicy2.setHeightForWidth(self.hLabel.sizePolicy().hasHeightForWidth())
self.hLabel.setSizePolicy(sizePolicy2)
self.gridLayout_3.addWidget(self.hLabel, 0, 2, 1, 1)
self.widthBox = QSpinBox(self.customWidget)
self.widthBox.setObjectName(u"widthBox")
self.widthBox.setMaximum(2400)
self.gridLayout_3.addWidget(self.widthBox, 0, 1, 1, 1)
self.wLabel = QLabel(self.customWidget)
self.wLabel.setObjectName(u"wLabel")
sizePolicy2.setHeightForWidth(self.wLabel.sizePolicy().hasHeightForWidth())
self.wLabel.setSizePolicy(sizePolicy2)
self.gridLayout_3.addWidget(self.wLabel, 0, 0, 1, 1)
self.heightBox = QSpinBox(self.customWidget)
self.heightBox.setObjectName(u"heightBox")
self.heightBox.setMaximum(3840)
self.gridLayout_3.addWidget(self.heightBox, 0, 3, 1, 1)
self.gridLayout.addWidget(self.customWidget, 8, 0, 1, 2)
self.croppingWidget = QWidget(self.centralWidget)
self.croppingWidget.setObjectName(u"croppingWidget")
self.croppingWidget.setVisible(False)
self.gridLayout_5 = QGridLayout(self.croppingWidget)
self.gridLayout_5.setObjectName(u"gridLayout_5")
self.gridLayout_5.setContentsMargins(0, 0, 0, 0)
self.preserveMarginLabel = QLabel(self.croppingWidget)
self.preserveMarginLabel.setObjectName(u"preserveMarginLabel")
self.gridLayout_5.addWidget(self.preserveMarginLabel, 1, 0, 1, 1)
self.croppingPowerLabel = QLabel(self.croppingWidget)
self.croppingPowerLabel.setObjectName(u"croppingPowerLabel")
self.gridLayout_5.addWidget(self.croppingPowerLabel, 0, 0, 1, 1)
self.croppingPowerSlider = QSlider(self.croppingWidget)
self.croppingPowerSlider.setObjectName(u"croppingPowerSlider")
self.croppingPowerSlider.setMaximum(300)
self.croppingPowerSlider.setSingleStep(1)
self.croppingPowerSlider.setOrientation(Qt.Orientation.Horizontal)
self.gridLayout_5.addWidget(self.croppingPowerSlider, 0, 1, 1, 1)
self.preserveMarginBox = QSpinBox(self.croppingWidget)
self.preserveMarginBox.setObjectName(u"preserveMarginBox")
sizePolicy1.setHeightForWidth(self.preserveMarginBox.sizePolicy().hasHeightForWidth())
self.preserveMarginBox.setSizePolicy(sizePolicy1)
self.preserveMarginBox.setMaximum(99)
self.preserveMarginBox.setSingleStep(5)
self.preserveMarginBox.setValue(0)
self.gridLayout_5.addWidget(self.preserveMarginBox, 1, 1, 1, 1)
self.gridLayout.addWidget(self.croppingWidget, 9, 0, 1, 2)
self.optionWidget = QWidget(self.centralWidget)
self.optionWidget.setObjectName(u"optionWidget")
self.gridLayout_2 = QGridLayout(self.optionWidget)
self.gridLayout_2.setObjectName(u"gridLayout_2")
self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
self.gammaBox = QCheckBox(self.optionWidget)
self.gammaBox.setObjectName(u"gammaBox")
self.gridLayout_2.addWidget(self.gammaBox, 2, 2, 1, 1)
self.mangaBox = QCheckBox(self.optionWidget)
self.mangaBox.setObjectName(u"mangaBox")
self.gridLayout_2.addWidget(self.mangaBox, 1, 0, 1, 1)
self.borderBox = QCheckBox(self.optionWidget)
self.borderBox.setObjectName(u"borderBox")
self.borderBox.setTristate(True)
self.gridLayout_2.addWidget(self.borderBox, 3, 0, 1, 1)
self.interPanelCropBox = QCheckBox(self.optionWidget)
self.interPanelCropBox.setObjectName(u"interPanelCropBox")
self.interPanelCropBox.setTristate(True)
self.gridLayout_2.addWidget(self.interPanelCropBox, 6, 2, 1, 1)
self.fileFusionBox = QCheckBox(self.optionWidget)
self.fileFusionBox.setObjectName(u"fileFusionBox")
self.gridLayout_2.addWidget(self.fileFusionBox, 6, 0, 1, 1)
self.authorEdit = QLineEdit(self.optionWidget)
self.authorEdit.setObjectName(u"authorEdit")
sizePolicy3 = QSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed)
sizePolicy3.setHorizontalStretch(0)
sizePolicy3.setVerticalStretch(0)
sizePolicy3.setHeightForWidth(self.authorEdit.sizePolicy().hasHeightForWidth())
self.authorEdit.setSizePolicy(sizePolicy3)
self.authorEdit.setFocusPolicy(Qt.FocusPolicy.ClickFocus)
self.authorEdit.setClearButtonEnabled(False)
self.gridLayout_2.addWidget(self.authorEdit, 0, 0, 1, 1)
self.rotateFirstBox = QCheckBox(self.optionWidget)
self.rotateFirstBox.setObjectName(u"rotateFirstBox")
self.gridLayout_2.addWidget(self.rotateFirstBox, 8, 1, 1, 1)
self.eraseRainbowBox = QCheckBox(self.optionWidget)
self.eraseRainbowBox.setObjectName(u"eraseRainbowBox")
self.gridLayout_2.addWidget(self.eraseRainbowBox, 7, 2, 1, 1)
self.chunkSizeCheckBox = QCheckBox(self.optionWidget)
self.chunkSizeCheckBox.setObjectName(u"chunkSizeCheckBox")
self.gridLayout_2.addWidget(self.chunkSizeCheckBox, 7, 1, 1, 1)
self.rotateBox = QCheckBox(self.optionWidget)
self.rotateBox.setObjectName(u"rotateBox")
self.rotateBox.setTristate(True)
self.gridLayout_2.addWidget(self.rotateBox, 1, 1, 1, 1)
self.outputSplit = QCheckBox(self.optionWidget)
self.outputSplit.setObjectName(u"outputSplit")
self.gridLayout_2.addWidget(self.outputSplit, 3, 1, 1, 1)
self.metadataTitleBox = QCheckBox(self.optionWidget)
self.metadataTitleBox.setObjectName(u"metadataTitleBox")
self.gridLayout_2.addWidget(self.metadataTitleBox, 7, 0, 1, 1)
self.qualityBox = QCheckBox(self.optionWidget)
self.qualityBox.setObjectName(u"qualityBox")
self.qualityBox.setTristate(True)
self.gridLayout_2.addWidget(self.qualityBox, 1, 2, 1, 1)
self.spreadShiftBox = QCheckBox(self.optionWidget)
self.spreadShiftBox.setObjectName(u"spreadShiftBox")
self.gridLayout_2.addWidget(self.spreadShiftBox, 5, 0, 1, 1)
self.disableProcessingBox = QCheckBox(self.optionWidget)
self.disableProcessingBox.setObjectName(u"disableProcessingBox")
self.gridLayout_2.addWidget(self.disableProcessingBox, 5, 2, 1, 1)
self.webtoonBox = QCheckBox(self.optionWidget)
self.webtoonBox.setObjectName(u"webtoonBox")
self.gridLayout_2.addWidget(self.webtoonBox, 2, 0, 1, 1)
self.colorBox = QCheckBox(self.optionWidget)
self.colorBox.setObjectName(u"colorBox")
self.gridLayout_2.addWidget(self.colorBox, 3, 2, 1, 1)
self.croppingBox = QCheckBox(self.optionWidget)
self.croppingBox.setObjectName(u"croppingBox")
self.croppingBox.setTristate(True)
self.gridLayout_2.addWidget(self.croppingBox, 4, 2, 1, 1)
self.maximizeStrips = QCheckBox(self.optionWidget)
self.maximizeStrips.setObjectName(u"maximizeStrips")
self.gridLayout_2.addWidget(self.maximizeStrips, 4, 1, 1, 1)
self.noRotateBox = QCheckBox(self.optionWidget)
self.noRotateBox.setObjectName(u"noRotateBox")
self.gridLayout_2.addWidget(self.noRotateBox, 6, 1, 1, 1)
self.deleteBox = QCheckBox(self.optionWidget)
self.deleteBox.setObjectName(u"deleteBox")
self.gridLayout_2.addWidget(self.deleteBox, 5, 1, 1, 1)
self.upscaleBox = QCheckBox(self.optionWidget)
self.upscaleBox.setObjectName(u"upscaleBox")
self.upscaleBox.setTristate(True)
self.gridLayout_2.addWidget(self.upscaleBox, 2, 1, 1, 1)
self.mozJpegBox = QCheckBox(self.optionWidget)
self.mozJpegBox.setObjectName(u"mozJpegBox")
self.mozJpegBox.setTristate(True)
self.gridLayout_2.addWidget(self.mozJpegBox, 4, 0, 1, 1)
self.autoLevelBox = QCheckBox(self.optionWidget)
self.autoLevelBox.setObjectName(u"autoLevelBox")
self.gridLayout_2.addWidget(self.autoLevelBox, 8, 2, 1, 1)
self.gridLayout.addWidget(self.optionWidget, 5, 0, 1, 2)
self.gammaWidget = QWidget(self.centralWidget)
self.gammaWidget.setObjectName(u"gammaWidget")
self.gammaWidget.setVisible(False)
self.horizontalLayout_2 = QHBoxLayout(self.gammaWidget)
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
self.gammaLabel = QLabel(self.gammaWidget)
self.gammaLabel.setObjectName(u"gammaLabel")
self.horizontalLayout_2.addWidget(self.gammaLabel)
self.gammaSlider = QSlider(self.gammaWidget)
self.gammaSlider.setObjectName(u"gammaSlider")
self.gammaSlider.setMaximum(250)
self.gammaSlider.setSingleStep(5)
self.gammaSlider.setOrientation(Qt.Orientation.Horizontal)
self.horizontalLayout_2.addWidget(self.gammaSlider)
self.gridLayout.addWidget(self.gammaWidget, 7, 0, 1, 2)
self.chunkSizeWidget = QWidget(self.centralWidget)
self.chunkSizeWidget.setObjectName(u"chunkSizeWidget")
sizePolicy3.setHeightForWidth(self.chunkSizeWidget.sizePolicy().hasHeightForWidth())
self.chunkSizeWidget.setSizePolicy(sizePolicy3)
sizePolicy.setHeightForWidth(self.chunkSizeWidget.sizePolicy().hasHeightForWidth())
self.chunkSizeWidget.setSizePolicy(sizePolicy)
self.chunkSizeWidget.setVisible(False)
self.horizontalLayout_4 = QHBoxLayout(self.chunkSizeWidget)
self.horizontalLayout_4.setSpacing(0)
@@ -490,12 +506,139 @@ class Ui_mainWindow(object):
def retranslateUi(self, mainWindow):
mainWindow.setWindowTitle(QCoreApplication.translate("mainWindow", u"Kindle Comic Converter", None))
#if QT_CONFIG(tooltip)
self.titleEdit.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>Default Title</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.titleEdit.setPlaceholderText(QCoreApplication.translate("mainWindow", u"Default Title", None))
#if QT_CONFIG(tooltip)
self.mangaBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Enable right-to-left reading.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.mangaBox.setText(QCoreApplication.translate("mainWindow", u"Right-to-left mode", None))
#if QT_CONFIG(tooltip)
self.webtoonBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Enable special parsing mode for Korean Webtoons.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.webtoonBox.setText(QCoreApplication.translate("mainWindow", u"Webtoon mode", None))
#if QT_CONFIG(tooltip)
self.croppingBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Disabled</span></p><p>Disabled</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Margins<br/></span>Margins</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Margins + page numbers<br/></span>Margins +page numbers</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.croppingBox.setText(QCoreApplication.translate("mainWindow", u"Cropping mode", None))
#if QT_CONFIG(tooltip)
self.maximizeStrips.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - 1x4<br/></span>Keep format 1x4 panels strips.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - 2x2<br/></span>Turn 1x4 strips to 2x2 to maximize screen usage.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.maximizeStrips.setText(QCoreApplication.translate("mainWindow", u"1x4 to 2x2 strips", None))
#if QT_CONFIG(tooltip)
self.deleteBox.setToolTip(QCoreApplication.translate("mainWindow", u"Delete input file(s) or directory. It's not recoverable!", None))
#endif // QT_CONFIG(tooltip)
self.deleteBox.setText(QCoreApplication.translate("mainWindow", u"Delete input", None))
#if QT_CONFIG(tooltip)
self.rotateFirstBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>When the spread splitter option is partially checked,</p><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Rotate Last<br/></span>Put the rotated 2 page spread after the split spreads.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Rotate First<br/></span>Put the rotated 2 page spread before the split spreads.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.rotateFirstBox.setText(QCoreApplication.translate("mainWindow", u"Rotate First", None))
#if QT_CONFIG(tooltip)
self.outputSplit.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Automatic mode<br/></span>The output will be split automatically.</p><p style='white-space:pre'><span style=\" font-weight:600; text-decoration: underline;\">Checked - Volume mode<br/></span>Every subdirectory will be considered as a separate volume.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.outputSplit.setText(QCoreApplication.translate("mainWindow", u"Output split", None))
#if QT_CONFIG(tooltip)
self.borderBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Autodetection<br/></span>The color of margins fill will be detected automatically.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - White<br/></span>Margins will be untouched.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Black<br/></span>Margins will be filled with black color.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.borderBox.setText(QCoreApplication.translate("mainWindow", u"W/B margins", None))
#if QT_CONFIG(tooltip)
self.gammaBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>Set a custom gamma correction.</p><p>1.0 is default (disabled).<br/>&lt; 1.0 makes the image brighter.<br/>&gt; 1.0 makes the image darker. </p><p>1.8 was the default in KCC 9.1.0 and earlier.</p><p>Use if you want to make midtones darker.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.gammaBox.setText(QCoreApplication.translate("mainWindow", u"Custom gamma", None))
#if QT_CONFIG(tooltip)
self.upscaleBox.setToolTip(QCoreApplication.translate("mainWindow", u"<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>", None))
#endif // QT_CONFIG(tooltip)
self.upscaleBox.setText(QCoreApplication.translate("mainWindow", u"Stretch/Upscale", None))
#if QT_CONFIG(tooltip)
self.chunkSizeCheckBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:700; text-decoration: underline;\">Unchecked<br/></span>Maximal output file size is 100 MB for Webtoon, 400 MB for others before split occurs.</p><p><span style=\" font-weight:700; text-decoration: underline;\">Checked</span><br/>Output file size specified in &quot;Chunk size MB&quot; before split occurs.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.chunkSizeCheckBox.setText(QCoreApplication.translate("mainWindow", u"Chunk size", None))
#if QT_CONFIG(tooltip)
self.colorBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Disable conversion to grayscale.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.colorBox.setText(QCoreApplication.translate("mainWindow", u"Color mode", None))
#if QT_CONFIG(tooltip)
self.rotateBox.setToolTip(QCoreApplication.translate("mainWindow", u"<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 - Split and rotate<br/></span>Double page spreads will be displayed twice. First split and then rotated. </p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Rotate<br/></span>Double page spreads will be rotated.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.rotateBox.setText(QCoreApplication.translate("mainWindow", u"Spread splitter", None))
#if QT_CONFIG(tooltip)
self.spreadShiftBox.setToolTip(QCoreApplication.translate("mainWindow", u"Shift first page to opposite side in landscape for two page spread alignment", None))
#endif // QT_CONFIG(tooltip)
self.spreadShiftBox.setText(QCoreApplication.translate("mainWindow", u"Spread shift", None))
#if QT_CONFIG(tooltip)
self.disableProcessingBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Do not process any image, ignore profile and processing options.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.disableProcessingBox.setText(QCoreApplication.translate("mainWindow", u"Disable processing", None))
#if QT_CONFIG(tooltip)
self.eraseRainbowBox.setToolTip(QCoreApplication.translate("mainWindow", u"Erase rainbow effect on color eink screen by attenuating interfering frequencies", None))
#endif // QT_CONFIG(tooltip)
self.eraseRainbowBox.setText(QCoreApplication.translate("mainWindow", u"Rainbow eraser", None))
#if QT_CONFIG(tooltip)
self.noRotateBox.setToolTip(QCoreApplication.translate("mainWindow", u"Do not rotate double page spreads in spread splitter option.", None))
#endif // QT_CONFIG(tooltip)
self.noRotateBox.setText(QCoreApplication.translate("mainWindow", u"No rotate", None))
#if QT_CONFIG(tooltip)
self.fileFusionBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>Combines all selected files into a single file. (Helpful for combining chapters into volumes.)</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.fileFusionBox.setText(QCoreApplication.translate("mainWindow", u"File Fusion", None))
#if QT_CONFIG(tooltip)
self.authorEdit.setToolTip(QCoreApplication.translate("mainWindow", u"Default Author is KCC", None))
#endif // QT_CONFIG(tooltip)
self.authorEdit.setPlaceholderText(QCoreApplication.translate("mainWindow", u"Default Author", None))
#if QT_CONFIG(tooltip)
self.qualityBox.setToolTip(QCoreApplication.translate("mainWindow", u"<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>", None))
#endif // QT_CONFIG(tooltip)
self.qualityBox.setText(QCoreApplication.translate("mainWindow", u"Panel View 4/2/HQ", None))
#if QT_CONFIG(tooltip)
self.interPanelCropBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Disabled<br/></span>Disabled</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Horizontal<br/></span>Crop empty horizontal lines.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Both<br/></span>Crop empty horizontal and vertical lines.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.interPanelCropBox.setText(QCoreApplication.translate("mainWindow", u"Inter-panel crop", None))
#if QT_CONFIG(tooltip)
self.metadataTitleBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Don't use metadata Title<br/></span>Write default title.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Add metadata Title to the default schema<br/></span>Write default title with Title from ComicInfo.xml or other embedded metadata.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Use metadata Title only<br/></span>Write Title from ComicInfo.xml or other embedded metadata.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.metadataTitleBox.setText(QCoreApplication.translate("mainWindow", u"Metadata Title", None))
#if QT_CONFIG(tooltip)
self.mozJpegBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - JPEG<br/></span>Use JPEG files</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - force PNG<br/></span>Create PNG files instead JPEG</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - mozJpeg<br/></span>10-20% smaller JPEG file, with the same image quality, but processing time multiplied by 2</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.mozJpegBox.setText(QCoreApplication.translate("mainWindow", u"JPEG/PNG/mozJpeg", None))
#if QT_CONFIG(tooltip)
self.autoLevelBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>By default, KCC maps the darkest pixel value to pure black (the black point.)</p><p>Extreme black point sets the black point to be the most common dark pixel value.</p><p>Useful when text is black but artwork is gray.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.autoLevelBox.setText(QCoreApplication.translate("mainWindow", u"Extreme Black Point", None))
#if QT_CONFIG(tooltip)
self.autocontrastBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - BW only<br/></span>Only autocontrast bw pages. Ignored for pages where near blacks or whites don't exist.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Disabled<br/></span>Disable autocontrast</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - BW and Color<br/></span>BW and color images will be autocontrasted. Ignored for pages where near blacks or whites don't exist.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.autocontrastBox.setText(QCoreApplication.translate("mainWindow", u"Autocontrast", None))
#if QT_CONFIG(tooltip)
self.jobList.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>Double click on source to open it in metadata editor.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
#if QT_CONFIG(tooltip)
self.hLabel.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.hLabel.setText(QCoreApplication.translate("mainWindow", u"Custom height:", None))
#if QT_CONFIG(tooltip)
self.widthBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
#if QT_CONFIG(tooltip)
self.wLabel.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.wLabel.setText(QCoreApplication.translate("mainWindow", u"Custom width:", None))
#if QT_CONFIG(tooltip)
self.heightBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.gammaLabel.setText(QCoreApplication.translate("mainWindow", u"Gamma: Auto", None))
#if QT_CONFIG(tooltip)
self.editorButton.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Shift+Click to edit directory.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.editorButton.setText(QCoreApplication.translate("mainWindow", u"Metadata Editor", None))
self.kofiButton.setText(QCoreApplication.translate("mainWindow", u"Support me on Ko-fi", None))
self.wikiButton.setText(QCoreApplication.translate("mainWindow", u"Wiki", None))
#if QT_CONFIG(tooltip)
self.preserveMarginLabel.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>After calculating the cropping boundaries, &quot;back up&quot; a specified percentage amount.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.preserveMarginLabel.setText(QCoreApplication.translate("mainWindow", u"Preserve Margin %", None))
self.croppingPowerLabel.setText(QCoreApplication.translate("mainWindow", u"Cropping power:", None))
#if QT_CONFIG(tooltip)
self.convertButton.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Shift+Click to select the output directory for this list.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
@@ -519,122 +662,6 @@ class Ui_mainWindow(object):
#if QT_CONFIG(tooltip)
self.formatBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Output format.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
#if QT_CONFIG(tooltip)
self.hLabel.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.hLabel.setText(QCoreApplication.translate("mainWindow", u"Custom height:", None))
#if QT_CONFIG(tooltip)
self.widthBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
#if QT_CONFIG(tooltip)
self.wLabel.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.wLabel.setText(QCoreApplication.translate("mainWindow", u"Custom width:", None))
#if QT_CONFIG(tooltip)
self.heightBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Resolution of the target device.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
#if QT_CONFIG(tooltip)
self.preserveMarginLabel.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>After calculating the cropping boundaries, &quot;back up&quot; a specified percentage amount.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.preserveMarginLabel.setText(QCoreApplication.translate("mainWindow", u"Preserve Margin %", None))
self.croppingPowerLabel.setText(QCoreApplication.translate("mainWindow", u"Cropping power:", None))
#if QT_CONFIG(tooltip)
self.gammaBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Disable automatic gamma correction.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.gammaBox.setText(QCoreApplication.translate("mainWindow", u"Custom gamma", None))
#if QT_CONFIG(tooltip)
self.mangaBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Enable right-to-left reading.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.mangaBox.setText(QCoreApplication.translate("mainWindow", u"Right-to-left mode", None))
#if QT_CONFIG(tooltip)
self.borderBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Autodetection<br/></span>The color of margins fill will be detected automatically.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - White<br/></span>Margins will be untouched.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Black<br/></span>Margins will be filled with black color.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.borderBox.setText(QCoreApplication.translate("mainWindow", u"W/B margins", None))
#if QT_CONFIG(tooltip)
self.interPanelCropBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Disabled<br/></span>Disabled</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Horizontal<br/></span>Crop empty horizontal lines.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Both<br/></span>Crop empty horizontal and vertical lines.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.interPanelCropBox.setText(QCoreApplication.translate("mainWindow", u"Inter-panel crop", None))
#if QT_CONFIG(tooltip)
self.fileFusionBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>Combines all selected files into a single file. (Helpful for combining chapters into volumes.)</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.fileFusionBox.setText(QCoreApplication.translate("mainWindow", u"File Fusion", None))
#if QT_CONFIG(tooltip)
self.authorEdit.setToolTip(QCoreApplication.translate("mainWindow", u"Default Author is KCC", None))
#endif // QT_CONFIG(tooltip)
self.authorEdit.setPlaceholderText(QCoreApplication.translate("mainWindow", u"Default Author", None))
#if QT_CONFIG(tooltip)
self.rotateFirstBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>When the spread splitter option is partially checked,</p><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Rotate Last<br/></span>Put the rotated 2 page spread after the split spreads.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Rotate First<br/></span>Put the rotated 2 page spread before the split spreads.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.rotateFirstBox.setText(QCoreApplication.translate("mainWindow", u"Rotate First", None))
#if QT_CONFIG(tooltip)
self.eraseRainbowBox.setToolTip(QCoreApplication.translate("mainWindow", u"Erase rainbow effect on color eink screen by attenuating interfering frequencies", None))
#endif // QT_CONFIG(tooltip)
self.eraseRainbowBox.setText(QCoreApplication.translate("mainWindow", u"Rainbow eraser", None))
#if QT_CONFIG(tooltip)
self.chunkSizeCheckBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:700; text-decoration: underline;\">Unchecked<br/></span>Maximal output file size is 100 MB for Webtoon, 400 MB for others before split occurs.</p><p><span style=\" font-weight:700; text-decoration: underline;\">Checked</span><br/>Output file size specified in &quot;Chunk size MB&quot; before split occurs.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.chunkSizeCheckBox.setText(QCoreApplication.translate("mainWindow", u"Chunk size", None))
#if QT_CONFIG(tooltip)
self.rotateBox.setToolTip(QCoreApplication.translate("mainWindow", u"<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 - Split and rotate<br/></span>Double page spreads will be displayed twice. First split and then rotated. </p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Rotate<br/></span>Double page spreads will be rotated.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.rotateBox.setText(QCoreApplication.translate("mainWindow", u"Spread splitter", None))
#if QT_CONFIG(tooltip)
self.outputSplit.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Automatic mode<br/></span>The output will be split automatically.</p><p style='white-space:pre'><span style=\" font-weight:600; text-decoration: underline;\">Checked - Volume mode<br/></span>Every subdirectory will be considered as a separate volume.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.outputSplit.setText(QCoreApplication.translate("mainWindow", u"Output split", None))
#if QT_CONFIG(tooltip)
self.metadataTitleBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>Write Title from ComicInfo.xml or other embedded metadata.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.metadataTitleBox.setText(QCoreApplication.translate("mainWindow", u"Metadata Title", None))
#if QT_CONFIG(tooltip)
self.qualityBox.setToolTip(QCoreApplication.translate("mainWindow", u"<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>", None))
#endif // QT_CONFIG(tooltip)
self.qualityBox.setText(QCoreApplication.translate("mainWindow", u"Panel View 4/2/HQ", None))
#if QT_CONFIG(tooltip)
self.spreadShiftBox.setToolTip(QCoreApplication.translate("mainWindow", u"Shift first page to opposite side in landscape for two page spread alignment", None))
#endif // QT_CONFIG(tooltip)
self.spreadShiftBox.setText(QCoreApplication.translate("mainWindow", u"Spread shift", None))
#if QT_CONFIG(tooltip)
self.disableProcessingBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Do not process any image, ignore profile and processing options.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.disableProcessingBox.setText(QCoreApplication.translate("mainWindow", u"Disable processing", None))
#if QT_CONFIG(tooltip)
self.webtoonBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Enable special parsing mode for Korean Webtoons.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.webtoonBox.setText(QCoreApplication.translate("mainWindow", u"Webtoon mode", None))
#if QT_CONFIG(tooltip)
self.colorBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p style='white-space:pre'>Disable conversion to grayscale.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.colorBox.setText(QCoreApplication.translate("mainWindow", u"Color mode", None))
#if QT_CONFIG(tooltip)
self.croppingBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Disabled</span></p><p>Disabled</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - Margins<br/></span>Margins</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - Margins + page numbers<br/></span>Margins +page numbers</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.croppingBox.setText(QCoreApplication.translate("mainWindow", u"Cropping mode", None))
#if QT_CONFIG(tooltip)
self.maximizeStrips.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - 1x4<br/></span>Keep format 1x4 panels strips.</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - 2x2<br/></span>Turn 1x4 strips to 2x2 to maximize screen usage.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.maximizeStrips.setText(QCoreApplication.translate("mainWindow", u"1x4 to 2x2 strips", None))
#if QT_CONFIG(tooltip)
self.noRotateBox.setToolTip(QCoreApplication.translate("mainWindow", u"Do not rotate double page spreads in spread splitter option.", None))
#endif // QT_CONFIG(tooltip)
self.noRotateBox.setText(QCoreApplication.translate("mainWindow", u"No rotate", None))
#if QT_CONFIG(tooltip)
self.deleteBox.setToolTip(QCoreApplication.translate("mainWindow", u"Delete input file(s) or directory. It's not recoverable!", None))
#endif // QT_CONFIG(tooltip)
self.deleteBox.setText(QCoreApplication.translate("mainWindow", u"Delete input", None))
#if QT_CONFIG(tooltip)
self.upscaleBox.setToolTip(QCoreApplication.translate("mainWindow", u"<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>", None))
#endif // QT_CONFIG(tooltip)
self.upscaleBox.setText(QCoreApplication.translate("mainWindow", u"Stretch/Upscale", None))
#if QT_CONFIG(tooltip)
self.mozJpegBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - JPEG<br/></span>Use JPEG files</p><p><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - force PNG<br/></span>Create PNG files instead JPEG</p><p><span style=\" font-weight:600; text-decoration: underline;\">Checked - mozJpeg<br/></span>10-20% smaller JPEG file, with the same image quality, but processing time multiplied by 2</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.mozJpegBox.setText(QCoreApplication.translate("mainWindow", u"JPEG/PNG/mozJpeg", None))
#if QT_CONFIG(tooltip)
self.autoLevelBox.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>Set the most common dark pixel value to be the black point for leveling on a page by page basis.</p><p>Skipped for any images that were originally color.</p><p>Use only if default autocontrast still results in very gray faded blacks. </p><p>Reccomended to use with Custom Gamma = 1.0 (Disabled).</p></body></html>", None))
#endif // QT_CONFIG(tooltip)
self.autoLevelBox.setText(QCoreApplication.translate("mainWindow", u"Aggressive Black Point", None))
self.gammaLabel.setText(QCoreApplication.translate("mainWindow", u"Gamma: Auto", None))
#if QT_CONFIG(tooltip)
self.chunkSizeWidget.setToolTip(QCoreApplication.translate("mainWindow", u"<html><head/><body><p>Warning: chunk size greater than default may cause<br/>performance/battery issues, especially on older devices.</p></body></html>", None))
#endif // QT_CONFIG(tooltip)

View File

@@ -3,7 +3,7 @@
################################################################################
## Form generated from reading UI file 'MetaEditor.ui'
##
## Created by: Qt User Interface Compiler version 6.9.1
## Created by: Qt User Interface Compiler version 6.9.3
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
@@ -60,52 +60,62 @@ class Ui_editorDialog(object):
self.label_3 = QLabel(self.editorWidget)
self.label_3.setObjectName(u"label_3")
self.gridLayout.addWidget(self.label_3, 2, 0, 1, 1)
self.gridLayout.addWidget(self.label_3, 3, 0, 1, 1)
self.numberLine = QLineEdit(self.editorWidget)
self.numberLine.setObjectName(u"numberLine")
self.gridLayout.addWidget(self.numberLine, 2, 1, 1, 1)
self.gridLayout.addWidget(self.numberLine, 3, 1, 1, 1)
self.label_4 = QLabel(self.editorWidget)
self.label_4.setObjectName(u"label_4")
self.gridLayout.addWidget(self.label_4, 3, 0, 1, 1)
self.gridLayout.addWidget(self.label_4, 4, 0, 1, 1)
self.writerLine = QLineEdit(self.editorWidget)
self.writerLine.setObjectName(u"writerLine")
self.gridLayout.addWidget(self.writerLine, 3, 1, 1, 1)
self.gridLayout.addWidget(self.writerLine, 4, 1, 1, 1)
self.label_5 = QLabel(self.editorWidget)
self.label_5.setObjectName(u"label_5")
self.gridLayout.addWidget(self.label_5, 4, 0, 1, 1)
self.gridLayout.addWidget(self.label_5, 5, 0, 1, 1)
self.pencillerLine = QLineEdit(self.editorWidget)
self.pencillerLine.setObjectName(u"pencillerLine")
self.gridLayout.addWidget(self.pencillerLine, 4, 1, 1, 1)
self.gridLayout.addWidget(self.pencillerLine, 5, 1, 1, 1)
self.label_6 = QLabel(self.editorWidget)
self.label_6.setObjectName(u"label_6")
self.gridLayout.addWidget(self.label_6, 5, 0, 1, 1)
self.gridLayout.addWidget(self.label_6, 6, 0, 1, 1)
self.inkerLine = QLineEdit(self.editorWidget)
self.inkerLine.setObjectName(u"inkerLine")
self.gridLayout.addWidget(self.inkerLine, 5, 1, 1, 1)
self.gridLayout.addWidget(self.inkerLine, 6, 1, 1, 1)
self.label_7 = QLabel(self.editorWidget)
self.label_7.setObjectName(u"label_7")
self.gridLayout.addWidget(self.label_7, 6, 0, 1, 1)
self.gridLayout.addWidget(self.label_7, 7, 0, 1, 1)
self.coloristLine = QLineEdit(self.editorWidget)
self.coloristLine.setObjectName(u"coloristLine")
self.gridLayout.addWidget(self.coloristLine, 6, 1, 1, 1)
self.gridLayout.addWidget(self.coloristLine, 7, 1, 1, 1)
self.label_8 = QLabel(self.editorWidget)
self.label_8.setObjectName(u"label_8")
self.gridLayout.addWidget(self.label_8, 2, 0, 1, 1)
self.titleLine = QLineEdit(self.editorWidget)
self.titleLine.setObjectName(u"titleLine")
self.gridLayout.addWidget(self.titleLine, 2, 1, 1, 1)
self.verticalLayout.addWidget(self.editorWidget)
@@ -161,6 +171,7 @@ class Ui_editorDialog(object):
self.label_5.setText(QCoreApplication.translate("editorDialog", u"Penciller:", None))
self.label_6.setText(QCoreApplication.translate("editorDialog", u"Inker:", None))
self.label_7.setText(QCoreApplication.translate("editorDialog", u"Colorist:", None))
self.label_8.setText(QCoreApplication.translate("editorDialog", u"Title:", None))
self.statusLabel.setText("")
self.okButton.setText(QCoreApplication.translate("editorDialog", u"Save", None))
self.cancelButton.setText(QCoreApplication.translate("editorDialog", u"Cancel", None))

View File

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

View File

@@ -42,7 +42,6 @@ from subprocess import STDOUT, PIPE, CalledProcessError
from psutil import virtual_memory, disk_usage
from html import escape as hescape
import pymupdf
import numpy as np
from .shared import getImageFileName, walkSort, walkLevel, sanitizeTrace, subprocess_run, dot_clean
from .comicarchive import SEVENZIP, available_archive_tools
@@ -299,8 +298,9 @@ def buildOPF(dstdir, title, filelist, originalpath, cover=None):
f.writelines(["<dc:description>", hescape(options.summary), "</dc:description>\n"])
for author in options.authors:
f.writelines(["<dc:creator>", hescape(author), "</dc:creator>\n"])
f.writelines(["<meta property=\"dcterms:modified\">" + strftime("%Y-%m-%dT%H:%M:%SZ", gmtime()) + "</meta>\n",
"<meta name=\"cover\" content=\"cover\"/>\n"])
f.write("<meta property=\"dcterms:modified\">" + strftime("%Y-%m-%dT%H:%M:%SZ", gmtime()) + "</meta>\n")
if cover:
f.write("<meta name=\"cover\" content=\"cover\"/>\n")
if options.iskindle and options.profile != 'Custom':
f.writelines(["<meta name=\"fixed-layout\" content=\"true\"/>\n",
"<meta name=\"original-resolution\" content=\"",
@@ -532,7 +532,8 @@ def buildEPUB(path, chapternames, tomenumber, ischunked, cover: image.Cover, ori
"}\n"])
f.close()
build_html_start = perf_counter()
cover.save_to_epub(os.path.join(path, 'OEBPS', 'Images', 'cover.jpg'), tomenumber, len_tomes)
if cover:
cover.save_to_epub(os.path.join(path, 'OEBPS', 'Images', 'cover.jpg'), tomenumber, len_tomes)
dot_clean(path)
options.covers.append((cover, options.uuid))
for dirpath, dirnames, filenames in os.walk(os.path.join(path, 'OEBPS', 'Images')):
@@ -553,11 +554,11 @@ def buildEPUB(path, chapternames, tomenumber, ischunked, cover: image.Cover, ori
filelist.append(buildHTML(dirpath, afile, os.path.join(dirpath, afile)))
build_html_end = perf_counter()
print(f"buildHTML: {build_html_end - build_html_start} seconds")
# Overwrite chapternames if tree is flat and ComicInfo.xml has bookmarks
# Overwrite chapternames if ComicInfo.xml has bookmarks
if ischunked:
options.comicinfo_chapters = []
if not chapternames and options.comicinfo_chapters:
if options.comicinfo_chapters:
chapterlist = []
global_diff = 0
@@ -650,7 +651,7 @@ def imgDirectoryProcessing(path):
raise RuntimeError("One of workers crashed. Cause: " + workerOutput[0][0], workerOutput[0][1])
else:
rmtree(os.path.join(path, '..', '..'), True)
raise UserWarning("Source directory is empty.")
raise UserWarning("C2E: Source directory is empty.")
def imgFileProcessingTick(output):
@@ -741,6 +742,7 @@ def render_page(vector):
zoom = target_height / page.rect.height
mat = pymupdf.Matrix(zoom, zoom)
# TODO: decide colorspace earlier so later color check is cheaper.
# This is actually pretty hard when you have to deal with color vector text
pix = page.get_pixmap(matrix=mat, colorspace='RGB', alpha=False)
pix.save(os.path.join(output_dir, "p-%i.png" % i))
print("Processed page numbers %i through %i" % (seg_from, seg_to - 1))
@@ -958,15 +960,17 @@ def getMetadata(path, originalpath):
except Exception:
os.remove(xmlPath)
return
if options.metadatatitle:
if options.metadatatitle == 2:
options.title = xml.data['Title']
elif defaultTitle:
if xml.data['Series']:
options.title = xml.data['Series']
if xml.data['Volume']:
titleSuffix += ' V' + xml.data['Volume'].zfill(2)
titleSuffix += ' Vol. ' + xml.data['Volume'].zfill(2)
if xml.data['Number']:
titleSuffix += ' #' + xml.data['Number'].zfill(3)
if options.metadatatitle == 1 and xml.data['Title']:
titleSuffix += ': ' + xml.data['Title']
options.title += titleSuffix
if defaultAuthor:
options.authors = []
@@ -1242,7 +1246,7 @@ def makeZIP(zipfilename, basedir, isepub=False):
mimetypeFile = open(os.path.join(basedir, 'mimetype'), 'w')
mimetypeFile.write('application/epub+zip')
mimetypeFile.close()
subprocess_run([SEVENZIP, 'a', '-tzip', zipfilename, os.path.join(basedir, "*")], capture_output=True, check=True)
subprocess_run([SEVENZIP, 'a', '-tzip', zipfilename, "*"], capture_output=True, check=True, cwd=basedir)
else:
zipOutput = ZipFile(zipfilename, 'w', ZIP_DEFLATED)
if isepub:
@@ -1290,8 +1294,9 @@ def makeParser():
help="Output generated file to specified directory or file")
output_options.add_argument("-t", "--title", action="store", dest="title", default="defaulttitle",
help="Comic title [Default=filename or directory name]")
output_options.add_argument("--metadatatitle", action="store_true", dest="metadatatitle", default=False,
help="Write Title from ComicInfo.xml or other embedded metadata")
output_options.add_argument("--metadatatitle", type=int, dest="metadatatitle", default=0,
help="Write title using ComicInfo.xml or other embedded metadata. 1: Combine Title with default schema "
"2: Use Title only")
output_options.add_argument("-a", "--author", action="store", dest="author", default="defaultauthor",
help="Author name [Default=KCC]")
output_options.add_argument("-f", "--format", action="store", dest="format", default="Auto",
@@ -1310,7 +1315,7 @@ def makeParser():
help="Put rotated 2 page spread first in spread splitter option.")
processing_options.add_argument("-n", "--noprocessing", action="store_true", dest="noprocessing", default=False,
help="Do not modify image and ignore any profil or processing option")
help="Do not modify image and ignore any profile or processing option")
processing_options.add_argument("-u", "--upscale", action="store_true", dest="upscale", default=False,
help="Resize images smaller than device's resolution")
processing_options.add_argument("-s", "--stretch", action="store_true", dest="stretch", default=False,
@@ -1321,6 +1326,10 @@ def makeParser():
help="Apply gamma correction to linearize the image [Default=Auto]")
output_options.add_argument("--autolevel", action="store_true", dest="autolevel", default=False,
help="Set most common dark pixel value to be black point for leveling.")
output_options.add_argument("--noautocontrast", action="store_true", dest="noautocontrast", default=False,
help="Disable autocontrast.")
output_options.add_argument("--colorautocontrast", action="store_true", dest="colorautocontrast", default=False,
help="Autocontrast color pages too. Skipped for pages without near blacks or whites.")
processing_options.add_argument("-c", "--cropping", type=int, dest="cropping", default="2",
help="Set cropping mode. 0: Disabled 1: Margins 2: Margins + page numbers [Default=2]")
processing_options.add_argument("--cp", "--croppingpower", type=float, dest="croppingp", default="1.0",
@@ -1410,8 +1419,10 @@ def checkOptions(options):
if options.webtoon:
options.panelview = False
options.righttoleft = False
options.upscale = True
options.upscale = False
options.hq = False
options.white_borders = True
options.bordersColor = 'white'
# Disable all Kindle features for other e-readers
if options.profile == 'OTHER':
options.panelview = False
@@ -1527,11 +1538,13 @@ def makeBook(source, qtgui=None):
removeNonImages(os.path.join(path, "OEBPS", "Images"))
detectSuboptimalProcessing(os.path.join(path, "OEBPS", "Images"), source)
chapterNames, cover_path = sanitizeTree(os.path.join(path, 'OEBPS', 'Images'))
cover = image.Cover(cover_path, options)
cover = None
if not options.webtoon:
cover = image.Cover(cover_path, options)
if options.webtoon:
y = image.ProfileData.Profiles[options.profile][1][1]
comic2panel.main(['-y ' + str(y), '-i', '-m', path], qtgui)
x, y = image.ProfileData.Profiles[options.profile][1]
comic2panel.main(['-y ' + str(y), '-x' + str(x), '-i', '-m', path], qtgui)
if options.noprocessing:
print("Do not process image, ignore any profile or processing option")
else:
@@ -1622,7 +1635,7 @@ def makeBook(source, qtgui=None):
return filepath
else:
os.remove(i.replace('.epub', '.mobi') + '_toclean')
if k.path and k.coverSupport:
if cover and k.path and k.coverSupport:
options.covers[filepath.index(i)][0].saveToKindle(k, options.covers[filepath.index(i)][1])
if options.delete:
if os.path.isfile(source):

View File

@@ -18,12 +18,14 @@
# PERFORMANCE OF THIS SOFTWARE.
#
import math
import os
import sys
from argparse import ArgumentParser
from shutil import rmtree, copytree, move
from shutil import rmtree
from multiprocessing import Pool
from PIL import Image, ImageChops, ImageOps, ImageDraw
from PIL import Image, ImageChops, ImageOps, ImageDraw, ImageFilter
from PIL.Image import Dither
from .shared import dot_clean, getImageFileName, walkLevel, walkSort, sanitizeTrace
@@ -58,8 +60,8 @@ def mergeDirectory(work):
imagesValid.append(i[0])
# Silently drop directories that contain too many images
# 131072 = GIMP_MAX_IMAGE_SIZE / 4
if targetHeight > 131072:
return None
if targetHeight > 131072 * 2:
raise RuntimeError(f'Image too tall at {targetHeight} pixels.')
result = Image.new('RGB', (targetWidth, targetHeight))
y = 0
for i in imagesValid:
@@ -101,7 +103,11 @@ def splitImage(work):
Image.warnings.simplefilter('error', Image.DecompressionBombWarning)
Image.MAX_IMAGE_PIXELS = 1000000000
imgOrg = Image.open(filePath).convert('RGB')
imgProcess = Image.open(filePath).convert('1')
# I experimented with custom vertical edge kernel [-1, 2, -1] but got poor results
imgEdges = Image.open(filePath).convert('L').filter(ImageFilter.FIND_EDGES)
# threshold of 8 is too high. 5 is too low.
imgProcess = imgEdges.point(lambda p: 255 if p > 6 else 0).convert('1', dither=Dither.NONE)
widthImg, heightImg = imgOrg.size
if heightImg > opt.height:
if opt.debug:
@@ -112,47 +118,71 @@ def splitImage(work):
yWork = 0
panelDetected = False
panels = []
# check git history for how these constant values changed
h_pad = int(widthImg / 20)
v_pad = int(widthImg / 80)
if v_pad % 2:
v_pad += 1
while yWork < heightImg:
tmpImg = imgProcess.crop((4, yWork, widthImg-4, yWork + 4))
tmpImg = imgProcess.crop((h_pad, yWork, widthImg - h_pad, yWork + v_pad))
solid = detectSolid(tmpImg)
if not solid and not panelDetected:
panelDetected = True
panelY1 = yWork - 2
if heightImg - yWork <= 5:
panelY1 = yWork
if heightImg - yWork <= (v_pad // 2):
if not solid and panelDetected:
panelY2 = heightImg
panelDetected = False
panels.append((panelY1, panelY2, panelY2 - panelY1))
if solid and panelDetected:
panelDetected = False
panelY2 = yWork + 6
panelY2 = yWork
# skip short panel at start
if panelY1 < v_pad * 2 and panelY2 - panelY1 < v_pad * 2:
continue
panels.append((panelY1, panelY2, panelY2 - panelY1))
yWork += 5
yWork += v_pad // 2
max_width = 1072
virtual_width = min((max_width, opt.width, widthImg))
if opt.width > max_width:
virtual_height = int(opt.height/max_width*virtual_width)
else:
virtual_height = int(opt.height/opt.width*virtual_width)
opt.height = virtual_height
# Split too big panels
panelsProcessed = []
for panel in panels:
# 1.52 too high
if panel[2] <= opt.height * 1.5:
panelsProcessed.append(panel)
elif panel[2] < opt.height * 2:
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)
# split super long panels with overlap
parts = math.ceil(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))
panelsProcessed.append((panel[0], panel[0] + opt.height, opt.height))
for x in range(1, parts - 1):
start = panel[0] + (x * diff)
panelsProcessed.append((start, start + opt.height, opt.height))
panelsProcessed.append((panel[1] - opt.height, panel[1], opt.height))
if opt.debug:
for panel in panelsProcessed:
draw.rectangle(((0, panel[0]), (widthImg, panel[1])), (0, 255, 0, 128), (0, 0, 255, 255))
debugImage = Image.alpha_composite(imgOrg.convert(mode='RGBA'), drawImg)
# debugImage.show()
debugImage.save(os.path.join(path, os.path.splitext(name)[0] + '-debug.png'), 'PNG')
# Create virtual pages
pages = []
currentPage = []
# TODO: 1.25 way too high, 1.1 too high, 1.05 slightly too high(?), optimized for 2 page landscape reading
# opt.height = max_height = virtual_height * 1.00
pageLeft = opt.height
panelNumber = 0
for panel in panelsProcessed:
@@ -201,6 +231,8 @@ def main(argv=None, qtgui=None):
" with spaces.")
main_options.add_argument("-y", "--height", type=int, dest="height", default=0,
help="Height of the target device screen")
main_options.add_argument("-x", "--width", type=int, dest="width", default=0,
help="Width of the target device screen")
main_options.add_argument("-i", "--in-place", action="store_true", dest="inPlace", default=False,
help="Overwrite source directory")
main_options.add_argument("-m", "--merge", action="store_true", dest="merge", default=False,
@@ -222,7 +254,7 @@ def main(argv=None, qtgui=None):
targetDir = sourceDir + "-Splitted"
if os.path.isdir(sourceDir):
rmtree(targetDir, True)
copytree(sourceDir, targetDir)
os.renames(sourceDir, targetDir)
work = []
pagenumber = 1
splitWorkerOutput = []
@@ -271,6 +303,7 @@ def main(argv=None, qtgui=None):
splitWorkerPool.apply_async(func=splitImage, args=(i, ), callback=splitImageTick)
splitWorkerPool.close()
splitWorkerPool.join()
dot_clean(targetDir)
if GUI and not GUI.conversionAlive:
rmtree(targetDir, True)
raise UserWarning("Conversion interrupted.")
@@ -279,11 +312,10 @@ def main(argv=None, qtgui=None):
raise RuntimeError("One of workers crashed. Cause: " + splitWorkerOutput[0][0],
splitWorkerOutput[0][1])
if args.inPlace:
rmtree(sourceDir, True)
move(targetDir, sourceDir)
os.renames(targetDir, sourceDir)
else:
rmtree(targetDir, True)
raise UserWarning("Source directory is empty.")
raise UserWarning("C2P: Source directory is empty.")
else:
raise UserWarning("Provided input is not a directory.")
else:

View File

@@ -36,21 +36,22 @@ class ComicArchive:
self.filepath = filepath
if not os.path.isfile(self.filepath):
raise OSError('File not found.')
self.dirname, self.basename = os.path.split(filepath)
@cached_property
def type(self):
extraction_commands = [
[SEVENZIP, 'l', '-y', '-p1', self.filepath],
[SEVENZIP, 'l', '-y', '-p1', self.basename],
]
if distro.id() == 'fedora' or distro.like() == 'fedora':
extraction_commands.append(
['unrar', 'l', '-y', '-p1', self.filepath],
['unrar', 'l', '-y', '-p1', self.basename],
)
for cmd in extraction_commands:
try:
process = subprocess_run(cmd, capture_output=True, check=True)
process = subprocess_run(cmd, capture_output=True, check=True, cwd=self.dirname)
for line in process.stdout.splitlines():
if b'Type =' in line:
return line.rstrip().decode().split(' = ')[1].upper()
@@ -68,25 +69,25 @@ class ComicArchive:
missing = []
extraction_commands = [
['tar', '--exclude', '__MACOSX', '--exclude', '.DS_Store', '--exclude', 'thumbs.db', '--exclude', 'Thumbs.db', '-xf', self.filepath, '-C', targetdir],
[SEVENZIP, 'x', '-y', '-xr!__MACOSX', '-xr!.DS_Store', '-xr!thumbs.db', '-xr!Thumbs.db', '-o' + targetdir, self.filepath],
['tar', '--exclude', '__MACOSX', '--exclude', '.DS_Store', '--exclude', 'thumbs.db', '--exclude', 'Thumbs.db', '-xf', self.basename, '-C', targetdir],
[SEVENZIP, 'x', '-y', '-xr!__MACOSX', '-xr!.DS_Store', '-xr!thumbs.db', '-xr!Thumbs.db', '-o' + targetdir, self.basename],
]
if platform.system() == 'Darwin':
extraction_commands.append(
['unar', self.filepath, '-D', '-f', '-o', targetdir]
['unar', self.basename, '-D', '-f', '-o', targetdir]
)
extraction_commands.reverse()
if distro.id() == 'fedora' or distro.like() == 'fedora':
extraction_commands.append(
['unrar', 'x', '-y', '-x__MACOSX', '-x.DS_Store', '-xthumbs.db', '-xThumbs.db', self.filepath, targetdir]
['unrar', 'x', '-y', '-x__MACOSX', '-x.DS_Store', '-xthumbs.db', '-xThumbs.db', self.basename, targetdir]
)
for cmd in extraction_commands:
try:
subprocess_run(cmd, capture_output=True, check=True)
subprocess_run(cmd, capture_output=True, check=True, cwd=self.dirname)
return targetdir
except FileNotFoundError:
missing.append(cmd[0])
@@ -101,14 +102,14 @@ class ComicArchive:
def addFile(self, sourcefile):
if self.type in ['RAR', 'RAR5']:
raise NotImplementedError
process = subprocess_run([SEVENZIP, 'a', '-y', self.filepath, sourcefile],
stdout=PIPE, stderr=STDOUT)
process = subprocess_run([SEVENZIP, 'a', '-y', self.basename, sourcefile],
stdout=PIPE, stderr=STDOUT, cwd=self.dirname)
if process.returncode != 0:
raise OSError('Failed to add the file.')
def extractMetadata(self):
process = subprocess_run([SEVENZIP, 'x', '-y', '-so', self.filepath, 'ComicInfo.xml'],
stdout=PIPE, stderr=STDOUT)
process = subprocess_run([SEVENZIP, 'x', '-y', '-so', self.basename, 'ComicInfo.xml'],
stdout=PIPE, stderr=STDOUT, cwd=self.dirname)
if process.returncode != 0:
raise OSError(EXTRACTION_ERROR)
try:

View File

@@ -85,22 +85,23 @@ class ProfileData:
]
ProfilesKindleEBOK = {
'K1': ("Kindle 1", (600, 670), Palette4, 1.8),
'K2': ("Kindle 2", (600, 670), Palette15, 1.8),
'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.8),
'K34': ("Kindle Keyboard/Touch", (600, 800), Palette16, 1.8),
'K57': ("Kindle 5/7", (600, 800), Palette16, 1.8),
'KPW': ("Kindle Paperwhite 1/2", (758, 1024), Palette16, 1.8),
'KV': ("Kindle Voyage", (1072, 1448), Palette16, 1.8),
'K1': ("Kindle 1", (600, 670), Palette4, 1.0),
'K2': ("Kindle 2", (600, 670), Palette15, 1.0),
'KDX': ("Kindle DX/DXG", (824, 1000), Palette16, 1.0),
'K34': ("Kindle Keyboard/Touch", (600, 800), Palette16, 1.0),
'K57': ("Kindle 5/7", (600, 800), Palette16, 1.0),
'KPW': ("Kindle Paperwhite 1/2", (758, 1024), Palette16, 1.0),
'KV': ("Kindle Voyage", (1072, 1448), Palette16, 1.0),
}
ProfilesKindlePDOC = {
'KPW34': ("Kindle Paperwhite 3/4/Oasis", (1072, 1448), Palette16, 1.8),
'K810': ("Kindle 8/10", (600, 800), Palette16, 1.8),
'KO': ("Kindle Oasis 2/3/Paperwhite 12/Colorsoft 12", (1264, 1680), Palette16, 1.8),
'K11': ("Kindle 11", (1072, 1448), Palette16, 1.8),
'KPW5': ("Kindle Paperwhite 5/Signature Edition", (1236, 1648), Palette16, 1.8),
'KS': ("Kindle Scribe", (1860, 2480), Palette16, 1.8),
'KPW34': ("Kindle Paperwhite 3/4/Oasis", (1072, 1448), Palette16, 1.0),
'K810': ("Kindle 8/10", (600, 800), Palette16, 1.0),
'KO': ("Kindle Oasis 2/3/Paperwhite 12", (1264, 1680), Palette16, 1.0),
'K11': ("Kindle 11", (1072, 1448), Palette16, 1.0),
'KPW5': ("Kindle Paperwhite 5/Signature Edition", (1236, 1648), Palette16, 1.0),
'KS': ("Kindle Scribe", (1860, 2480), Palette16, 1.0),
'KCS': ("Kindle Colorsoft", (1264, 1680), Palette16, 1.0),
}
ProfilesKindle = {
@@ -109,34 +110,35 @@ class ProfileData:
}
ProfilesKobo = {
'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.8),
'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.8),
'KoGHD': ("Kobo Glo HD", (1072, 1448), Palette16, 1.8),
'KoA': ("Kobo Aura", (758, 1024), Palette16, 1.8),
'KoAHD': ("Kobo Aura HD", (1080, 1440), Palette16, 1.8),
'KoAH2O': ("Kobo Aura H2O", (1080, 1430), Palette16, 1.8),
'KoAO': ("Kobo Aura ONE", (1404, 1872), Palette16, 1.8),
'KoN': ("Kobo Nia", (758, 1024), Palette16, 1.8),
'KoC': ("Kobo Clara HD/Kobo Clara 2E", (1072, 1448), Palette16, 1.8),
'KoCC': ("Kobo Clara Colour", (1072, 1448), Palette16, 1.8),
'KoL': ("Kobo Libra H2O/Kobo Libra 2", (1264, 1680), Palette16, 1.8),
'KoLC': ("Kobo Libra Colour", (1264, 1680), Palette16, 1.8),
'KoF': ("Kobo Forma", (1440, 1920), Palette16, 1.8),
'KoS': ("Kobo Sage", (1440, 1920), Palette16, 1.8),
'KoE': ("Kobo Elipsa", (1404, 1872), Palette16, 1.8),
'KoMT': ("Kobo Mini/Touch", (600, 800), Palette16, 1.0),
'KoG': ("Kobo Glo", (768, 1024), Palette16, 1.0),
'KoGHD': ("Kobo Glo HD", (1072, 1448), Palette16, 1.0),
'KoA': ("Kobo Aura", (758, 1024), Palette16, 1.0),
'KoAHD': ("Kobo Aura HD", (1080, 1440), Palette16, 1.0),
'KoAH2O': ("Kobo Aura H2O", (1080, 1430), Palette16, 1.0),
'KoAO': ("Kobo Aura ONE", (1404, 1872), Palette16, 1.0),
'KoN': ("Kobo Nia", (758, 1024), Palette16, 1.0),
'KoC': ("Kobo Clara HD/Kobo Clara 2E", (1072, 1448), Palette16, 1.0),
'KoCC': ("Kobo Clara Colour", (1072, 1448), Palette16, 1.0),
'KoL': ("Kobo Libra H2O/Kobo Libra 2", (1264, 1680), Palette16, 1.0),
'KoLC': ("Kobo Libra Colour", (1264, 1680), Palette16, 1.0),
'KoF': ("Kobo Forma", (1440, 1920), Palette16, 1.0),
'KoS': ("Kobo Sage", (1440, 1920), Palette16, 1.0),
'KoE': ("Kobo Elipsa", (1404, 1872), Palette16, 1.0),
}
ProfilesRemarkable = {
'Rmk1': ("reMarkable 1", (1404, 1872), Palette16, 1.8),
'Rmk2': ("reMarkable 2", (1404, 1872), Palette16, 1.8),
'RmkPP': ("reMarkable Paper Pro", (1620, 2160), Palette16, 1.8),
'Rmk1': ("reMarkable 1", (1404, 1872), Palette16, 1.0),
'Rmk2': ("reMarkable 2", (1404, 1872), Palette16, 1.0),
'RmkPP': ("reMarkable Paper Pro", (1620, 2160), Palette16, 1.0),
'RmkPPMove': ("reMarkable Paper Pro Move", (954, 1696), Palette16, 1.0),
}
Profiles = {
**ProfilesKindle,
**ProfilesKobo,
**ProfilesRemarkable,
'OTHER': ("Other", (0, 0), Palette16, 1.8),
'OTHER': ("Other", (0, 0), Palette16, 1.0),
}
@@ -151,7 +153,8 @@ class ComicPageParser:
# Detect corruption in source image, let caller catch any exceptions triggered.
srcImgPath = os.path.join(source[0], source[1])
Image.open(srcImgPath).verify()
self.image = Image.open(srcImgPath)
with Image.open(srcImgPath) as im:
self.image = im.copy()
self.fill = self.fillCheck()
# backwards compatibility for Pillow >9.1.0
@@ -261,6 +264,7 @@ class ComicPage:
self.size = (int(self.size[0] * 1.5), int(self.size[1] * 1.5))
self.kindle_scribe_azw3 = (options.profile == 'KS') and (options.format in ('MOBI', 'EPUB'))
self.original_color_mode = image.mode
# TODO: color check earlier
self.image = image.convert("RGB")
self.fill = fill
self.rotated = False
@@ -284,21 +288,99 @@ class ComicPage:
def color(self):
if self.original_color_mode in ("L", "1"):
return False
if self.opt.webtoon:
return True
if self.calculate_color():
return True
return False
# cut off pixels from both ends of the histogram to remove jpg compression artifacts
# for better accuracy, you could split the image in half and analyze each half separately
def histograms_cutoff(self, cb, cr, cutoff=(2, 2)):
cb_hist = cb.histogram()
cr_hist = cr.histogram()
for h in cb_hist, cr_hist:
# get number of pixels
n = sum(h)
# remove cutoff% pixels from the low end
cut = int(n * cutoff[0] // 100)
for lo in range(256):
if cut > h[lo]:
cut = cut - h[lo]
h[lo] = 0
else:
h[lo] -= cut
cut = 0
if cut <= 0:
break
# remove cutoff% samples from the high end
cut = int(n * cutoff[1] // 100)
for hi in range(255, -1, -1):
if cut > h[hi]:
cut = cut - h[hi]
h[hi] = 0
else:
h[hi] -= cut
cut = 0
if cut <= 0:
break
return cb_hist, cr_hist
def calculate_color(self):
img = self.image.convert("YCbCr")
_, cb, cr = img.split()
cb_hist = cb.histogram()
cr_hist = cr.histogram()
# get rid of some jpg compression
cutoff = (.2, .2)
cb_hist, cr_hist = self.histograms_cutoff(cb, cr, cutoff)
cb_nonzero = [i for i, e in enumerate(cb_hist) if e]
cr_nonzero = [i for i, e in enumerate(cr_hist) if e]
cb_spread = cb_nonzero[-1] - cb_nonzero[0] if len(cb_nonzero) else 0
cr_spread = cr_nonzero[-1] - cr_nonzero[0] if len(cr_nonzero) else 0
cb_spread = cb_nonzero[-1] - cb_nonzero[0]
cr_spread = cr_nonzero[-1] - cr_nonzero[0]
SPREAD_THRESHOLD=20
if cb_spread < SPREAD_THRESHOLD and cr_spread < SPREAD_THRESHOLD:
# bias adjustment
SPREAD_THRESHOLD = 5
if not self.opt.forcecolor and cb_spread < SPREAD_THRESHOLD and cr_spread < SPREAD_THRESHOLD:
return False
else:
# check for large amount of extreme colors
# 11 if too high. 10 is barely enough. If needed make it magnitude of both
DIFF_THRESHOLD = 10
if any([
cb_nonzero[0] <= 128 - DIFF_THRESHOLD,
cr_nonzero[0] <= 128 - DIFF_THRESHOLD,
cb_nonzero[-1] >= 128 + DIFF_THRESHOLD,
cr_nonzero[-1] >= 128 + DIFF_THRESHOLD,
]):
return True
# get ride of most jpg compression
cutoff = (2, 2)
cb_hist, cr_hist = self.histograms_cutoff(cb, cr, cutoff)
cb_nonzero = [i for i, e in enumerate(cb_hist) if e]
cr_nonzero = [i for i, e in enumerate(cr_hist) if e]
cb_spread = cb_nonzero[-1] - cb_nonzero[0]
cr_spread = cr_nonzero[-1] - cr_nonzero[0]
# bias adjustment
SPREAD_THRESHOLD = 5
if not self.opt.forcecolor and cb_spread < SPREAD_THRESHOLD and cr_spread < SPREAD_THRESHOLD:
return False
# check for any amount of mild colors still remaining
DIFF_THRESHOLD = 6
if any([
cb_nonzero[0] <= 128 - DIFF_THRESHOLD,
cr_nonzero[0] <= 128 - DIFF_THRESHOLD,
cb_nonzero[-1] >= 128 + DIFF_THRESHOLD,
cr_nonzero[-1] >= 128 + DIFF_THRESHOLD,
]):
return True
else:
return False
def saveToDir(self):
try:
@@ -355,20 +437,41 @@ class ComicPage:
self.image = Image.eval(self.image, lambda a: int(255 * (a / 255.) ** gamma))
def autocontrastImage(self):
if self.opt.autolevel and not self.color:
self.convertToGrayscale()
h = self.image.histogram()
most_common_dark_pixel_count = max(h[:64])
black_point = h.index(most_common_dark_pixel_count)
bp = black_point
self.image = self.image.point(lambda p: p if p > bp else bp)
# don't autocontrast grayscale pages that were originally color
if not self.opt.forcecolor and self.color:
if self.opt.webtoon:
return
if self.opt.noautocontrast:
return
if self.color and not self.opt.colorautocontrast:
return
# if image is extremely low contrast, that was probably intentional
extrema = self.image.convert('L').getextrema()
if extrema[1] - extrema[0] < (255 - 32 * 3):
return
if self.opt.autolevel:
self.autolevelImage()
self.image = ImageOps.autocontrast(self.image, preserve_tone=True)
def autolevelImage(self):
img = self.image
if self.color:
img = self.image.convert("YCbCr")
y, cb, cr = img.split()
img = y
else:
img = img.convert('L')
h = img.histogram()
most_common_dark_pixel_count = max(h[:64])
black_point = h.index(most_common_dark_pixel_count)
bp = black_point
img = img.point(lambda p: p if p > bp else bp)
if self.color:
self.image = Image.merge(mode='YCbCr', bands=[img, cb, cr]).convert('RGB')
else:
self.image = img
def convertToGrayscale(self):
self.image = self.image.convert('L')

View File

@@ -19,10 +19,10 @@ def crop_empty_inter_panel(img, direction: Literal["horizontal", "vertical", "bo
img_temp = img
if img.mode != 'L':
img_temp = ImageOps.grayscale(img)
img_temp = ImageOps.grayscale(img_temp)
if background_color != 'white':
img_temp = ImageOps.invert(img)
img_temp = ImageOps.invert(img_temp)
img_mat = np.array(img)

View File

@@ -1,4 +1,4 @@
PySide6>=6.5.1
PySide6<6.10
Pillow>=11.3.0
psutil>=5.9.5
requests>=2.31.0

View File

@@ -46,7 +46,7 @@ class BuildBinaryCommand(setuptools.Command):
sys.exit(0)
elif sys.platform == 'win32':
if os.getenv('WINDOWS_7'):
os.system('pyinstaller --hidden-import=_cffi_backend -y -F -i icons\\comic2ebook.ico -n kcc_win7_' + VERSION + ' -w --noupx kcc.py')
os.system('pyinstaller --hidden-import=_cffi_backend -y -F -i icons\\comic2ebook.ico -n kcc_win7_legacy_' + VERSION + ' -w --noupx kcc.py')
else:
os.system('pyinstaller --hidden-import=_cffi_backend -y -F -i icons\\comic2ebook.ico -n KCC_' + VERSION + ' -w --noupx kcc.py')
sys.exit(0)