mirror of
https://github.com/ciromattia/kcc
synced 2026-04-28 11:59:01 +00:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3672f7a1e | ||
|
|
1b48a9fc5e | ||
|
|
d5dde46989 | ||
|
|
92c85c18e9 | ||
|
|
e5122cc188 | ||
|
|
61be6aa78e | ||
|
|
d6834063c1 | ||
|
|
1a8d74de4a | ||
|
|
a0a194ecf1 | ||
|
|
290578d66e | ||
|
|
f97398d481 | ||
|
|
a7a9f35686 | ||
|
|
d5146d02fc | ||
|
|
b0374e127d | ||
|
|
894dbfc8a2 | ||
|
|
72f98bb032 | ||
|
|
96bf14d386 | ||
|
|
d4e1565e4a | ||
|
|
c3030e8bd1 |
19
README.md
19
README.md
@@ -11,7 +11,7 @@
|
|||||||
like Kindle, Kobo, ReMarkable, and more.
|
like Kindle, Kobo, ReMarkable, and more.
|
||||||
Pages display in fullscreen without margins,
|
Pages display in fullscreen without margins,
|
||||||
with proper fixed layout support.
|
with proper fixed layout support.
|
||||||
Supported input formats include JPG/PNG image files in folders, archives, or PDFs.
|
Supported input formats include JPG/PNG image files in folders, archives like CBZ, or PDFs.
|
||||||
Supported output formats include MOBI/AZW3, EPUB, KEPUB, CBZ, and PDF.
|
Supported output formats include MOBI/AZW3, EPUB, KEPUB, CBZ, and PDF.
|
||||||
KCC runs on Windows, macOS, and Linux.
|
KCC runs on Windows, macOS, and Linux.
|
||||||
|
|
||||||
@@ -115,15 +115,14 @@ For flatpak, Docker, and AppImage versions, refer to the wiki: https://github.co
|
|||||||
## FAQ
|
## FAQ
|
||||||
- Should I use Calibre?
|
- Should I use Calibre?
|
||||||
- No. Calibre doesn't properly support fixed layout EPUB/MOBI, so modifying KCC output (even just metadata!) in Calibre can break the formatting.
|
- No. Calibre doesn't properly support fixed layout EPUB/MOBI, so modifying KCC output (even just metadata!) in Calibre can break the formatting.
|
||||||
|
Additionally, it will break page numbers.
|
||||||
Viewing KCC output in Calibre will also not work properly.
|
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.
|
Direct USB dropping is reccomended.
|
||||||
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?
|
- 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.
|
- 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. You can try PDF output.
|
||||||
Going back a few pages and exiting and re-entering book should fix it temporarily.
|
Going back a few pages and exiting and re-entering book should fix it temporarily.
|
||||||
- What output format should I use?
|
- What output format should I use?
|
||||||
- MOBI for Kindles. CBZ for Kindle DX. CBZ for Koreader. KEPUB for Kobo. PDF for ReMarkable.
|
- MOBI for Kindles. CBZ for Kindle DX. CBZ for Koreader. KEPUB for Kobo. PDF for ReMarkable or Kindle Scribe 2025.
|
||||||
- All options have additional information in tooltips if you hover over the option.
|
- 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
|
- 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
|
||||||
- Kindle panel view not working?
|
- Kindle panel view not working?
|
||||||
@@ -137,9 +136,6 @@ For flatpak, Docker, and AppImage versions, refer to the wiki: https://github.co
|
|||||||
(no login required). Works much better than previously recommended Android File Transfer. Cannot run simutaneously with other transfer apps.
|
(no login required). Works much better than previously recommended Android File Transfer. Cannot run simutaneously with other transfer apps.
|
||||||
- How to make AZW3 instead of MOBI?
|
- How to make AZW3 instead of MOBI?
|
||||||
- The `.mobi` file generated by KCC is a dual filetype, it's both MOBI and AZW3. The file extension is `.mobi` for compatibility reasons.
|
- The `.mobi` file generated by KCC is a dual filetype, it's both MOBI and AZW3. The file extension is `.mobi` for compatibility reasons.
|
||||||
- [Windows 7 support](https://github.com/ciromattia/kcc/issues/678)
|
|
||||||
- Image too dark?
|
|
||||||
- The default gamma correction of 1.8 makes the image darker, and is useful for faded/gray artwork/text. Disable by setting gamma = 1.0
|
|
||||||
- Huge margins / slow page turns?
|
- Huge margins / slow page turns?
|
||||||
- You likely modified the file during transfer using a 3rd party app. Try simply dragging and dropping the final mobi/kepub file into the Kindle documents folder via USB.
|
- You likely modified the file during transfer using a 3rd party app. Try simply dragging and dropping the final mobi/kepub file into the Kindle documents folder via USB.
|
||||||
|
|
||||||
@@ -198,8 +194,9 @@ sudo apt-get install python3 p7zip-full python3-pil python3-psutil python3-slugi
|
|||||||
'KV': ("Kindle Voyage", (1072, 1448), Palette16, 1.0),
|
'KV': ("Kindle Voyage", (1072, 1448), Palette16, 1.0),
|
||||||
'KPW34': ("Kindle Paperwhite 3/4", (1072, 1448), Palette16, 1.0),
|
'KPW34': ("Kindle Paperwhite 3/4", (1072, 1448), Palette16, 1.0),
|
||||||
'KPW5': ("Kindle Paperwhite 5/Signature Edition", (1236, 1648), Palette16, 1.0),
|
'KPW5': ("Kindle Paperwhite 5/Signature Edition", (1236, 1648), Palette16, 1.0),
|
||||||
'KO': ("Kindle Oasis 2/3/Paperwhite 12", (1264, 1680), Palette16, 1.0),
|
'KPW6': ("Kindle Paperwhite 6", (1272, 1696), Palette16, 1.0),
|
||||||
'KCS': ("Kindle Colorsoft", (1264, 1680), Palette16, 1.0),
|
'KO': ("Kindle Oasis 2/3", (1264, 1680), Palette16, 1.0),
|
||||||
|
'KCS': ("Kindle Colorsoft", (1272, 1696), Palette16, 1.0),
|
||||||
'KS1860': ("Kindle 1860", (1860, 1920), Palette16, 1.0),
|
'KS1860': ("Kindle 1860", (1860, 1920), Palette16, 1.0),
|
||||||
'KS1920': ("Kindle 1920", (1920, 1920), Palette16, 1.0),
|
'KS1920': ("Kindle 1920", (1920, 1920), Palette16, 1.0),
|
||||||
'KS': ("Kindle Scribe 1/2", (1860, 2480), Palette16, 1.0),
|
'KS': ("Kindle Scribe 1/2", (1860, 2480), Palette16, 1.0),
|
||||||
|
|||||||
@@ -866,7 +866,7 @@ Useful if you plan to crop a little off the top and bottom to fill screen.</stri
|
|||||||
<string><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></string>
|
<string><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></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Autocontrast</string>
|
<string>Custom Autocontrast</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="tristate">
|
<property name="tristate">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
|||||||
@@ -795,8 +795,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
GUI.rotateBox.setChecked(False)
|
GUI.rotateBox.setChecked(False)
|
||||||
GUI.borderBox.setEnabled(False)
|
GUI.borderBox.setEnabled(False)
|
||||||
GUI.borderBox.setCheckState(Qt.CheckState.PartiallyChecked)
|
GUI.borderBox.setCheckState(Qt.CheckState.PartiallyChecked)
|
||||||
GUI.upscaleBox.setEnabled(False)
|
# GUI.upscaleBox.setEnabled(False)
|
||||||
GUI.upscaleBox.setChecked(False)
|
# GUI.upscaleBox.setChecked(False)
|
||||||
GUI.croppingBox.setEnabled(False)
|
GUI.croppingBox.setEnabled(False)
|
||||||
GUI.croppingBox.setChecked(False)
|
GUI.croppingBox.setChecked(False)
|
||||||
GUI.interPanelCropBox.setEnabled(False)
|
GUI.interPanelCropBox.setEnabled(False)
|
||||||
@@ -813,7 +813,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
GUI.rotateBox.setEnabled(True)
|
GUI.rotateBox.setEnabled(True)
|
||||||
GUI.borderBox.setEnabled(True)
|
GUI.borderBox.setEnabled(True)
|
||||||
profile = GUI.profiles[str(GUI.deviceBox.currentText())]
|
profile = GUI.profiles[str(GUI.deviceBox.currentText())]
|
||||||
if not profile['Label'].startswith('KS'):
|
if not profile['Label'].startswith('KS') or True:
|
||||||
GUI.upscaleBox.setEnabled(True)
|
GUI.upscaleBox.setEnabled(True)
|
||||||
GUI.croppingBox.setEnabled(True)
|
GUI.croppingBox.setEnabled(True)
|
||||||
GUI.interPanelCropBox.setEnabled(True)
|
GUI.interPanelCropBox.setEnabled(True)
|
||||||
@@ -908,10 +908,10 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
if not GUI.webtoonBox.isChecked():
|
if not GUI.webtoonBox.isChecked():
|
||||||
GUI.qualityBox.setEnabled(profile['PVOptions'])
|
GUI.qualityBox.setEnabled(profile['PVOptions'])
|
||||||
GUI.upscaleBox.setChecked(profile['DefaultUpscale'])
|
GUI.upscaleBox.setChecked(profile['DefaultUpscale'])
|
||||||
if profile['Label'].startswith('KS'):
|
if profile['Label'].startswith('KS') and False:
|
||||||
GUI.upscaleBox.setDisabled(True)
|
GUI.upscaleBox.setDisabled(True)
|
||||||
else:
|
else:
|
||||||
if not GUI.webtoonBox.isChecked():
|
if not GUI.webtoonBox.isChecked() or True:
|
||||||
GUI.upscaleBox.setEnabled(True)
|
GUI.upscaleBox.setEnabled(True)
|
||||||
if profile['Label'] == 'KCS':
|
if profile['Label'] == 'KCS':
|
||||||
current_format = GUI.formats[str(GUI.formatBox.currentText())]['format']
|
current_format = GUI.formats[str(GUI.formatBox.currentText())]['format']
|
||||||
@@ -946,10 +946,15 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
GUI.formats[str(GUI.formatBox.currentText())]['format'] == 'MOBI+EPUB-200MB'):
|
GUI.formats[str(GUI.formatBox.currentText())]['format'] == 'MOBI+EPUB-200MB'):
|
||||||
GUI.chunkSizeCheckBox.setEnabled(False)
|
GUI.chunkSizeCheckBox.setEnabled(False)
|
||||||
GUI.chunkSizeCheckBox.setChecked(False)
|
GUI.chunkSizeCheckBox.setChecked(False)
|
||||||
|
elif GUI.formats[str(GUI.formatBox.currentText())]['format'] == 'KFX':
|
||||||
|
GUI.mozJpegBox.setCheckState(Qt.CheckState.PartiallyChecked)
|
||||||
elif not GUI.webtoonBox.isChecked():
|
elif not GUI.webtoonBox.isChecked():
|
||||||
GUI.chunkSizeCheckBox.setEnabled(True)
|
GUI.chunkSizeCheckBox.setEnabled(True)
|
||||||
if GUI.formats[str(GUI.formatBox.currentText())]['format'] in ('CBZ', 'PDF') and not GUI.webtoonBox.isChecked():
|
if GUI.formats[str(GUI.formatBox.currentText())]['format'] in ('CBZ', 'PDF') and not GUI.webtoonBox.isChecked():
|
||||||
self.addMessage("Partially check W/B Margins if you don't want KCC to extend the image margins.", 'info')
|
self.addMessage("Partially check W/B Margins if you don't want KCC to extend the image margins.", 'info')
|
||||||
|
GUI.borderBox.setCheckState(Qt.CheckState.PartiallyChecked)
|
||||||
|
else:
|
||||||
|
GUI.borderBox.setCheckState(Qt.CheckState.Unchecked)
|
||||||
|
|
||||||
def stripTags(self, html):
|
def stripTags(self, html):
|
||||||
s = HTMLStripper()
|
s = HTMLStripper()
|
||||||
@@ -1231,7 +1236,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
"CBZ": {'icon': 'CBZ', 'format': 'CBZ'},
|
"CBZ": {'icon': 'CBZ', 'format': 'CBZ'},
|
||||||
"PDF": {'icon': 'EPUB', 'format': 'PDF'},
|
"PDF": {'icon': 'EPUB', 'format': 'PDF'},
|
||||||
"PDF (200MB limit)": {'icon': 'EPUB', 'format': 'PDF-200MB'},
|
"PDF (200MB limit)": {'icon': 'EPUB', 'format': 'PDF-200MB'},
|
||||||
"KFX (does not work)": {'icon': 'KFX', 'format': 'KFX'},
|
"KFX (Send to Kindle EPUB)": {'icon': 'KFX', 'format': 'KFX'},
|
||||||
"MOBI + EPUB": {'icon': 'MOBI', 'format': 'MOBI+EPUB'},
|
"MOBI + EPUB": {'icon': 'MOBI', 'format': 'MOBI+EPUB'},
|
||||||
"EPUB (200MB limit)": {'icon': 'EPUB', 'format': 'EPUB-200MB'},
|
"EPUB (200MB limit)": {'icon': 'EPUB', 'format': 'EPUB-200MB'},
|
||||||
"MOBI + EPUB (200MB limit)": {'icon': 'MOBI', 'format': 'MOBI+EPUB-200MB'},
|
"MOBI + EPUB (200MB limit)": {'icon': 'MOBI', 'format': 'MOBI+EPUB-200MB'},
|
||||||
@@ -1260,10 +1265,10 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KS',
|
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KS',
|
||||||
},
|
},
|
||||||
"Kindle Scribe 3": {
|
"Kindle Scribe 3": {
|
||||||
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KS3',
|
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 3, 'DefaultUpscale': False, 'ForceColor': False, 'Label': 'KS3',
|
||||||
},
|
},
|
||||||
"Kindle Scribe Colorsoft": {
|
"Kindle Scribe Colorsoft": {
|
||||||
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'ForceColor': True, 'Label': 'KSCS',
|
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 3, 'DefaultUpscale': False, 'ForceColor': True, 'Label': 'KSCS',
|
||||||
},
|
},
|
||||||
"Kindle 11": {
|
"Kindle 11": {
|
||||||
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'K11',
|
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'K11',
|
||||||
@@ -1272,7 +1277,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
|||||||
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KPW5',
|
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KPW5',
|
||||||
},
|
},
|
||||||
"Kindle Paperwhite 12": {
|
"Kindle Paperwhite 12": {
|
||||||
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KO',
|
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': False, 'Label': 'KPW6',
|
||||||
},
|
},
|
||||||
"Kindle Colorsoft": {
|
"Kindle Colorsoft": {
|
||||||
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': True, 'Label': 'KCS',
|
'PVOptions': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'ForceColor': True, 'Label': 'KCS',
|
||||||
|
|||||||
@@ -819,7 +819,7 @@ class Ui_mainWindow(object):
|
|||||||
#if QT_CONFIG(tooltip)
|
#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))
|
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)
|
#endif // QT_CONFIG(tooltip)
|
||||||
self.autocontrastBox.setText(QCoreApplication.translate("mainWindow", u"Autocontrast", None))
|
self.autocontrastBox.setText(QCoreApplication.translate("mainWindow", u"Custom Autocontrast", None))
|
||||||
#if QT_CONFIG(tooltip)
|
#if QT_CONFIG(tooltip)
|
||||||
self.webpBox.setToolTip(QCoreApplication.translate("mainWindow", u"Replace JPG with lossy WebP and PNG with lossless WebP. This includes the JPG Quality.\n"
|
self.webpBox.setToolTip(QCoreApplication.translate("mainWindow", u"Replace JPG with lossy WebP and PNG with lossless WebP. This includes the JPG Quality.\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
__version__ = '9.7.1'
|
__version__ = '10.1.0'
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = '2012-2022, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>, darodi'
|
__copyright__ = '2012-2022, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>, darodi'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
# PERFORMANCE OF THIS SOFTWARE.
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
from collections import Counter
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
import re
|
import re
|
||||||
@@ -43,7 +44,7 @@ from psutil import virtual_memory, disk_usage
|
|||||||
from html import escape as hescape
|
from html import escape as hescape
|
||||||
import pymupdf
|
import pymupdf
|
||||||
|
|
||||||
from .shared import IMAGE_TYPES, getImageFileName, walkSort, walkLevel, sanitizeTrace, subprocess_run, dot_clean
|
from .shared import IMAGE_TYPES, getImageFileName, walkSort, walkLevel, sanitizeTrace, subprocess_run, dot_clean, get_contain_resolution
|
||||||
from .comicarchive import SEVENZIP, available_archive_tools
|
from .comicarchive import SEVENZIP, available_archive_tools
|
||||||
from . import comic2panel
|
from . import comic2panel
|
||||||
from . import image
|
from . import image
|
||||||
@@ -136,14 +137,14 @@ def buildHTML(path, imgfile, imgfilepath, imgfile2=None):
|
|||||||
"content=\"width=" + str(imgsizeframe[0]) + ", height=" + str(imgsizeframe[1]) + "\"/>\n"
|
"content=\"width=" + str(imgsizeframe[0]) + ", height=" + str(imgsizeframe[1]) + "\"/>\n"
|
||||||
"</head>\n",
|
"</head>\n",
|
||||||
"<body style=\"" + additionalStyle + "\">\n",
|
"<body style=\"" + additionalStyle + "\">\n",
|
||||||
"<div style=\"text-align:center;top:" + getTopMargin(deviceres, imgsizeframe) + "%;\">\n",
|
"<div style=\"text-align:center;\">\n",
|
||||||
])
|
])
|
||||||
if options.iskindle:
|
if options.iskindle:
|
||||||
# this display none div fixes formatting issues with virtual panel mode, for some reason
|
# this display none div fixes formatting issues with virtual panel mode, for some reason
|
||||||
f.write('<div style="display:none;">.</div>\n')
|
f.write('<div style="display:none;">.</div>\n')
|
||||||
f.write(f'<img width="{imgsize[0]}" height="{imgsize[1]}" src="{"../" * backref}Images/{postfix}{imgfile}"/>\n')
|
f.write(f'<img width="{imgsize[0]}" height="{imgsize[1]}" src="{"../" * backref}Images/{postfix}{imgfile}"/>\n')
|
||||||
if imgfile2:
|
if imgfile2:
|
||||||
f.write(f'<img width="{imgsize2[0]}" height="{imgsize2[1]}" src="{"../" * backref}Images/{postfix}{imgfile2}"/>\n')
|
f.write(f'<img style="bottom: 0" width="{imgsize2[0]}" height="{imgsize2[1]}" src="{"../" * backref}Images/{postfix}{imgfile2}"/>\n')
|
||||||
f.write("</div>\n")
|
f.write("</div>\n")
|
||||||
if options.iskindle and options.panelview:
|
if options.iskindle and options.panelview:
|
||||||
if options.autoscale:
|
if options.autoscale:
|
||||||
@@ -324,8 +325,19 @@ def buildOPF(dstdir, title, filelist, originalpath, cover=None):
|
|||||||
f.write("<meta name=\"cover\" content=\"cover\"/>\n")
|
f.write("<meta name=\"cover\" content=\"cover\"/>\n")
|
||||||
if options.iskindle and options.profile != 'Custom':
|
if options.iskindle and options.profile != 'Custom':
|
||||||
f.writelines(["<meta name=\"fixed-layout\" content=\"true\"/>\n",
|
f.writelines(["<meta name=\"fixed-layout\" content=\"true\"/>\n",
|
||||||
"<meta name=\"original-resolution\" content=\"",
|
])
|
||||||
str(deviceres[0]) + "x" + str(deviceres[1]) + "\"/>\n",
|
if not options.kfx_resolution:
|
||||||
|
f.writelines([
|
||||||
|
"<meta name=\"original-resolution\" content=\"",
|
||||||
|
str(deviceres[0]) + "x" + str(deviceres[1]) + "\"/>\n",
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
x, y = options.kfx_resolution
|
||||||
|
f.writelines([
|
||||||
|
"<meta name=\"original-resolution\" content=\"",
|
||||||
|
str(x) + "x" + str(y) + "\"/>\n",
|
||||||
|
])
|
||||||
|
f.writelines([
|
||||||
"<meta name=\"book-type\" content=\"comic\"/>\n",
|
"<meta name=\"book-type\" content=\"comic\"/>\n",
|
||||||
"<meta name=\"primary-writing-mode\" content=\"" + writingmode + "\"/>\n",
|
"<meta name=\"primary-writing-mode\" content=\"" + writingmode + "\"/>\n",
|
||||||
"<meta name=\"zero-gutter\" content=\"true\"/>\n",
|
"<meta name=\"zero-gutter\" content=\"true\"/>\n",
|
||||||
@@ -873,14 +885,18 @@ def mupdf_pdf_process_pages_parallel(filename, output_dir, target_width, target_
|
|||||||
|
|
||||||
def getWorkFolder(afile, workdir=None):
|
def getWorkFolder(afile, workdir=None):
|
||||||
if not workdir:
|
if not workdir:
|
||||||
workdir = mkdtemp('', 'KCC-')
|
|
||||||
if options.tempdir:
|
if options.tempdir:
|
||||||
workdir = mkdtemp('', 'KCC-', os.path.dirname(afile))
|
workdir = mkdtemp('', 'KCC-', os.path.dirname(afile))
|
||||||
|
else:
|
||||||
|
workdir = mkdtemp('', 'KCC-')
|
||||||
fullPath = os.path.join(workdir, 'OEBPS', 'Images')
|
fullPath = os.path.join(workdir, 'OEBPS', 'Images')
|
||||||
else:
|
else:
|
||||||
fullPath = workdir
|
fullPath = workdir
|
||||||
|
check_path = gettempdir()
|
||||||
|
if options.tempdir:
|
||||||
|
check_path = os.path.dirname(afile)
|
||||||
if os.path.isdir(afile):
|
if os.path.isdir(afile):
|
||||||
if disk_usage(gettempdir())[2] < getDirectorySize(afile) * 2.5:
|
if disk_usage(check_path)[2] < getDirectorySize(afile) * 2.5:
|
||||||
raise UserWarning("Not enough disk space to perform conversion.")
|
raise UserWarning("Not enough disk space to perform conversion.")
|
||||||
try:
|
try:
|
||||||
copytree(afile, fullPath)
|
copytree(afile, fullPath)
|
||||||
@@ -890,7 +906,7 @@ def getWorkFolder(afile, workdir=None):
|
|||||||
rmtree(workdir, True)
|
rmtree(workdir, True)
|
||||||
raise UserWarning("Failed to prepare a workspace.")
|
raise UserWarning("Failed to prepare a workspace.")
|
||||||
elif os.path.isfile(afile):
|
elif os.path.isfile(afile):
|
||||||
if disk_usage(gettempdir())[2] < os.path.getsize(afile) * 2.5:
|
if disk_usage(check_path)[2]< os.path.getsize(afile) * 2.5:
|
||||||
raise UserWarning("Not enough disk space to perform conversion.")
|
raise UserWarning("Not enough disk space to perform conversion.")
|
||||||
if afile.lower().endswith('.pdf'):
|
if afile.lower().endswith('.pdf'):
|
||||||
if not os.path.exists(fullPath):
|
if not os.path.exists(fullPath):
|
||||||
@@ -1075,11 +1091,6 @@ def getDirectorySize(start_path='.'):
|
|||||||
return total_size
|
return total_size
|
||||||
|
|
||||||
|
|
||||||
def getTopMargin(deviceres, size):
|
|
||||||
y = int((deviceres[1] - size[1]) / 2) / deviceres[1] * 100
|
|
||||||
return str(round(y, 1))
|
|
||||||
|
|
||||||
|
|
||||||
def getPanelViewResolution(imagesize, deviceres):
|
def getPanelViewResolution(imagesize, deviceres):
|
||||||
scale = float(deviceres[0]) / float(imagesize[0])
|
scale = float(deviceres[0]) / float(imagesize[0])
|
||||||
return int(deviceres[0]), int(scale * imagesize[1])
|
return int(deviceres[0]), int(scale * imagesize[1])
|
||||||
@@ -1471,6 +1482,15 @@ def checkOptions(options):
|
|||||||
options.isKobo = False
|
options.isKobo = False
|
||||||
options.bordersColor = None
|
options.bordersColor = None
|
||||||
options.keep_epub = False
|
options.keep_epub = False
|
||||||
|
|
||||||
|
if options.profile in image.ProfileData.ProfilesKindle.keys():
|
||||||
|
options.iskindle = True
|
||||||
|
else:
|
||||||
|
options.isKobo = True
|
||||||
|
|
||||||
|
if not options.iskindle and ('MOBI' in options.format or 'EPUB-200MB' in options.format or 'KFX' in options.format):
|
||||||
|
raise UserWarning('MOBI/Send to Kindle not supported for non-Kindle profiles')
|
||||||
|
|
||||||
if options.format == 'PDF-200MB':
|
if options.format == 'PDF-200MB':
|
||||||
options.targetsize = 195
|
options.targetsize = 195
|
||||||
options.format = 'PDF'
|
options.format = 'PDF'
|
||||||
@@ -1502,10 +1522,7 @@ def checkOptions(options):
|
|||||||
options.format = 'PDF'
|
options.format = 'PDF'
|
||||||
else:
|
else:
|
||||||
options.format = 'EPUB'
|
options.format = 'EPUB'
|
||||||
if options.profile in image.ProfileData.ProfilesKindle.keys():
|
|
||||||
options.iskindle = True
|
|
||||||
else:
|
|
||||||
options.isKobo = True
|
|
||||||
if options.white_borders:
|
if options.white_borders:
|
||||||
options.bordersColor = 'white'
|
options.bordersColor = 'white'
|
||||||
if options.black_borders:
|
if options.black_borders:
|
||||||
@@ -1536,6 +1553,7 @@ def checkOptions(options):
|
|||||||
options.hq = False
|
options.hq = False
|
||||||
# KFX output create EPUB that might be can be by jhowell KFX Output Calibre plugin
|
# KFX output create EPUB that might be can be by jhowell KFX Output Calibre plugin
|
||||||
if options.format == 'KFX':
|
if options.format == 'KFX':
|
||||||
|
options.targetsize = 195
|
||||||
options.format = 'EPUB'
|
options.format = 'EPUB'
|
||||||
options.kfx = True
|
options.kfx = True
|
||||||
options.panelview = False
|
options.panelview = False
|
||||||
@@ -1557,6 +1575,7 @@ def checkOptions(options):
|
|||||||
options.jpegquality = 90
|
options.jpegquality = 90
|
||||||
else:
|
else:
|
||||||
options.jpegquality = 85
|
options.jpegquality = 85
|
||||||
|
|
||||||
options.kindle_azw3 = options.iskindle and ('MOBI' in options.format or 'EPUB' in options.format)
|
options.kindle_azw3 = options.iskindle and ('MOBI' in options.format or 'EPUB' in options.format)
|
||||||
options.kindle_scribe_azw3 = options.profile.startswith('KS') and options.kindle_azw3
|
options.kindle_scribe_azw3 = options.profile.startswith('KS') and options.kindle_azw3
|
||||||
|
|
||||||
@@ -1672,9 +1691,43 @@ def makeBook(source, qtgui=None, job_progress=''):
|
|||||||
if not options.webtoon:
|
if not options.webtoon:
|
||||||
cover = image.Cover(cover_path, options)
|
cover = image.Cover(cover_path, options)
|
||||||
|
|
||||||
|
x, y = image.ProfileData.Profiles[options.profile][1]
|
||||||
if options.webtoon:
|
if options.webtoon:
|
||||||
x, y = image.ProfileData.Profiles[options.profile][1]
|
|
||||||
comic2panel.main(['-y ' + str(y), '-x' + str(x), '-i', '-m', path], job_progress, qtgui)
|
comic2panel.main(['-y ' + str(y), '-x' + str(x), '-i', '-m', path], job_progress, qtgui)
|
||||||
|
|
||||||
|
options.kfx_resolution = None
|
||||||
|
if options.kfx:
|
||||||
|
original_resolutions = []
|
||||||
|
normalized_resolutions = []
|
||||||
|
for root, _, files in os.walk(os.path.join(path, "OEBPS", "Images")):
|
||||||
|
for file in files:
|
||||||
|
with Image.open(os.path.join(root, file)) as imagef:
|
||||||
|
original_resolutions.append(imagef.size)
|
||||||
|
size = get_contain_resolution(imagef, (x, y))
|
||||||
|
normalized_resolutions.append(size)
|
||||||
|
|
||||||
|
counter = Counter(normalized_resolutions)
|
||||||
|
|
||||||
|
aspect_ratios = []
|
||||||
|
filtered_resolutions = []
|
||||||
|
for w, h in normalized_resolutions:
|
||||||
|
aspect_ratio = h / w
|
||||||
|
# page-like aspect ratios, could be improved
|
||||||
|
if aspect_ratio > 1.3 and aspect_ratio < 1.7:
|
||||||
|
aspect_ratios.append(aspect_ratio)
|
||||||
|
filtered_resolutions.append((w, h))
|
||||||
|
|
||||||
|
most_common_res, most_common_count = counter.most_common(1)[0]
|
||||||
|
options.kfx_resolution = most_common_res
|
||||||
|
if most_common_count / counter.total() > .6:
|
||||||
|
pass
|
||||||
|
#elif max(aspect_ratios) - min(aspect_ratios) < .2:
|
||||||
|
else:
|
||||||
|
# get the widest resolution
|
||||||
|
options.kfx_resolution = max(filtered_resolutions)
|
||||||
|
# else:
|
||||||
|
# raise UserWarning('Aspect ratio of pages too different for KFX conversion')
|
||||||
|
|
||||||
if options.noprocessing:
|
if options.noprocessing:
|
||||||
print(f"{job_progress}Do not process image, ignore any profile or processing option")
|
print(f"{job_progress}Do not process image, ignore any profile or processing option")
|
||||||
else:
|
else:
|
||||||
@@ -1715,7 +1768,7 @@ def makeBook(source, qtgui=None, job_progress=''):
|
|||||||
filepath.append(getOutputFilename(source, options.output, '.cbz', ' ' + str(tomeNumber)))
|
filepath.append(getOutputFilename(source, options.output, '.cbz', ' ' + str(tomeNumber)))
|
||||||
else:
|
else:
|
||||||
filepath.append(getOutputFilename(source, options.output, '.cbz', ''))
|
filepath.append(getOutputFilename(source, options.output, '.cbz', ''))
|
||||||
if cover.smartcover:
|
if cover and cover.smartcover:
|
||||||
cover.save_to_folder(os.path.join(tome, 'OEBPS', 'Images', 'cover.jpg'), tomeNumber, len(tomes))
|
cover.save_to_folder(os.path.join(tome, 'OEBPS', 'Images', 'cover.jpg'), tomeNumber, len(tomes))
|
||||||
makeZIP(tome + '_comic', os.path.join(tome, "OEBPS", "Images"), job_progress)
|
makeZIP(tome + '_comic', os.path.join(tome, "OEBPS", "Images"), job_progress)
|
||||||
elif options.format == 'PDF':
|
elif options.format == 'PDF':
|
||||||
@@ -1723,7 +1776,7 @@ def makeBook(source, qtgui=None, job_progress=''):
|
|||||||
# determine output filename based on source and tome count
|
# determine output filename based on source and tome count
|
||||||
suffix = (' ' + str(tomeNumber)) if len(tomes) > 1 else ''
|
suffix = (' ' + str(tomeNumber)) if len(tomes) > 1 else ''
|
||||||
output_file = getOutputFilename(source, options.output, '.pdf', suffix)
|
output_file = getOutputFilename(source, options.output, '.pdf', suffix)
|
||||||
if cover.smartcover:
|
if cover and cover.smartcover:
|
||||||
cover.save_to_folder(os.path.join(tome, 'OEBPS', 'Images', 'cover.jpg'), tomeNumber, len(tomes))
|
cover.save_to_folder(os.path.join(tome, 'OEBPS', 'Images', 'cover.jpg'), tomeNumber, len(tomes))
|
||||||
# use optimized buildPDF logic with streaming and compression
|
# use optimized buildPDF logic with streaming and compression
|
||||||
output_pdf = buildPDF(tome, options.title, job_progress, None, output_file)
|
output_pdf = buildPDF(tome, options.title, job_progress, None, output_file)
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ from PIL import Image, ImageOps, ImageFile, ImageChops, ImageDraw
|
|||||||
from .rainbow_artifacts_eraser import erase_rainbow_artifacts
|
from .rainbow_artifacts_eraser import erase_rainbow_artifacts
|
||||||
from .page_number_crop_alg import get_bbox_crop_margin_page_number, get_bbox_crop_margin
|
from .page_number_crop_alg import get_bbox_crop_margin_page_number, get_bbox_crop_margin
|
||||||
from .inter_panel_crop_alg import crop_empty_inter_panel
|
from .inter_panel_crop_alg import crop_empty_inter_panel
|
||||||
|
from .shared import get_contain_resolution
|
||||||
|
|
||||||
AUTO_CROP_THRESHOLD = 0.015
|
AUTO_CROP_THRESHOLD = 0.015
|
||||||
ImageFile.LOAD_TRUNCATED_IMAGES = True
|
ImageFile.LOAD_TRUNCATED_IMAGES = True
|
||||||
@@ -98,14 +99,15 @@ class ProfileData:
|
|||||||
'KV': ("Kindle Voyage", (1072, 1448), Palette16, 1.0),
|
'KV': ("Kindle Voyage", (1072, 1448), Palette16, 1.0),
|
||||||
'KPW34': ("Kindle Paperwhite 3/4/Oasis", (1072, 1448), Palette16, 1.0),
|
'KPW34': ("Kindle Paperwhite 3/4/Oasis", (1072, 1448), Palette16, 1.0),
|
||||||
'K810': ("Kindle 8/10", (600, 800), Palette16, 1.0),
|
'K810': ("Kindle 8/10", (600, 800), Palette16, 1.0),
|
||||||
'KO': ("Kindle Oasis 2/3/Paperwhite 12", (1264, 1680), Palette16, 1.0),
|
'KO': ("Kindle Oasis 2/3", (1264, 1680), Palette16, 1.0),
|
||||||
'K11': ("Kindle 11", (1072, 1448), Palette16, 1.0),
|
'K11': ("Kindle 11", (1072, 1448), Palette16, 1.0),
|
||||||
'KPW5': ("Kindle Paperwhite 5/Signature Edition", (1236, 1648), Palette16, 1.0),
|
'KPW5': ("Kindle Paperwhite 5/Signature Edition", (1236, 1648), Palette16, 1.0),
|
||||||
|
'KPW6': ("Kindle Paperwhite 6", (1272, 1696), Palette16, 1.0),
|
||||||
'KS1860': ("Kindle 1860", (1860, 1920), Palette16, 1.0),
|
'KS1860': ("Kindle 1860", (1860, 1920), Palette16, 1.0),
|
||||||
'KS1920': ("Kindle 1920", (1920, 1920), Palette16, 1.0),
|
'KS1920': ("Kindle 1920", (1920, 1920), Palette16, 1.0),
|
||||||
'KS1240': ("Kindle 1240", (1240, 1860), Palette16, 1.0),
|
'KS1240': ("Kindle 1240", (1240, 1860), Palette16, 1.0),
|
||||||
'KS': ("Kindle Scribe 1/2", (1860, 2480), Palette16, 1.0),
|
'KS': ("Kindle Scribe 1/2", (1860, 2480), Palette16, 1.0),
|
||||||
'KCS': ("Kindle Colorsoft", (1264, 1680), Palette16, 1.0),
|
'KCS': ("Kindle Colorsoft", (1272, 1696), Palette16, 1.0),
|
||||||
'KS3': ("Kindle Scribe 3", (1986, 2648), Palette16, 1.0),
|
'KS3': ("Kindle Scribe 3", (1986, 2648), Palette16, 1.0),
|
||||||
'KSCS': ("Kindle Scribe Colorsoft", (1986, 2648), Palette16, 1.0),
|
'KSCS': ("Kindle Scribe Colorsoft", (1986, 2648), Palette16, 1.0),
|
||||||
}
|
}
|
||||||
@@ -193,8 +195,10 @@ class ComicPageParser:
|
|||||||
new_image.paste(pageone, (0, 0))
|
new_image.paste(pageone, (0, 0))
|
||||||
new_image.paste(pagetwo, (0, height))
|
new_image.paste(pagetwo, (0, height))
|
||||||
self.payload.append(['N', self.source, new_image, self.fill])
|
self.payload.append(['N', self.source, new_image, self.fill])
|
||||||
elif (width > height) != (dstwidth > dstheight) and width <= dstheight and height <= dstwidth \
|
elif self.opt.webtoon:
|
||||||
and not self.opt.webtoon and self.opt.splitter == 1:
|
self.payload.append(['N', self.source, self.image, self.fill])
|
||||||
|
# rotate only TODO dead code?
|
||||||
|
elif (width > height) != (dstwidth > dstheight) and width <= dstheight and height <= dstwidth and self.opt.splitter == 1:
|
||||||
spread = self.image
|
spread = self.image
|
||||||
if not self.opt.norotate:
|
if not self.opt.norotate:
|
||||||
if not self.opt.rotateright:
|
if not self.opt.rotateright:
|
||||||
@@ -202,8 +206,10 @@ class ComicPageParser:
|
|||||||
else:
|
else:
|
||||||
spread = spread.rotate(-90, Image.Resampling.BICUBIC, True)
|
spread = spread.rotate(-90, Image.Resampling.BICUBIC, True)
|
||||||
self.payload.append(['R', self.source, spread, self.fill])
|
self.payload.append(['R', self.source, spread, self.fill])
|
||||||
elif (width > height) != (dstwidth > dstheight) and not self.opt.webtoon:
|
# elif wide enough to split
|
||||||
if self.opt.splitter != 1 and width / height < 2:
|
elif (width > height) != (dstwidth > dstheight) and width / height > 1.16:
|
||||||
|
# if (split) or (split and rotate)
|
||||||
|
if self.opt.splitter != 1 and width / height < 1.75:
|
||||||
if width > height:
|
if width > height:
|
||||||
leftbox = (0, 0, int(width / 2), height)
|
leftbox = (0, 0, int(width / 2), height)
|
||||||
rightbox = (int(width / 2), 0, width, height)
|
rightbox = (int(width / 2), 0, width, height)
|
||||||
@@ -218,7 +224,9 @@ class ComicPageParser:
|
|||||||
pagetwo = self.image.crop(rightbox)
|
pagetwo = self.image.crop(rightbox)
|
||||||
self.payload.append(['S1', self.source, pageone, self.fill])
|
self.payload.append(['S1', self.source, pageone, self.fill])
|
||||||
self.payload.append(['S2', self.source, pagetwo, self.fill])
|
self.payload.append(['S2', self.source, pagetwo, self.fill])
|
||||||
if self.opt.splitter > 0 or (self.opt.splitter == 0 and width / height >= 2):
|
|
||||||
|
# if (rotate) or (split and rotate)
|
||||||
|
if self.opt.splitter > 0 or (self.opt.splitter == 0 and width / height >= 1.75):
|
||||||
spread = self.image
|
spread = self.image
|
||||||
if not self.opt.norotate:
|
if not self.opt.norotate:
|
||||||
if not self.opt.rotateright:
|
if not self.opt.rotateright:
|
||||||
@@ -510,7 +518,17 @@ class ComicPage:
|
|||||||
ratio_device = float(self.size[1]) / float(self.size[0])
|
ratio_device = float(self.size[1]) / float(self.size[0])
|
||||||
ratio_image = float(self.image.size[1]) / float(self.image.size[0])
|
ratio_image = float(self.image.size[1]) / float(self.image.size[0])
|
||||||
method = self.resize_method()
|
method = self.resize_method()
|
||||||
if self.opt.stretch:
|
if self.opt.kfx:
|
||||||
|
ratio_kfx = self.opt.kfx_resolution[1] / self.opt.kfx_resolution[0]
|
||||||
|
contain_size = get_contain_resolution(self.image, self.size)
|
||||||
|
if abs(ratio_image - ratio_kfx) < AUTO_CROP_THRESHOLD:
|
||||||
|
if contain_size[0] > self.opt.kfx_resolution[0] or contain_size[1] > self.opt.kfx_resolution[1]:
|
||||||
|
self.image = ImageOps.fit(self.image, self.opt.kfx_resolution, method=method)
|
||||||
|
else:
|
||||||
|
self.image = ImageOps.pad(self.image, self.opt.kfx_resolution, method=method, color=self.fill)
|
||||||
|
else:
|
||||||
|
self.image = ImageOps.pad(self.image, self.opt.kfx_resolution, method=method, color=self.fill)
|
||||||
|
elif self.opt.stretch:
|
||||||
self.image = self.image.resize(self.size, method)
|
self.image = self.image.resize(self.size, method)
|
||||||
elif method == Image.Resampling.BICUBIC and not self.opt.upscale:
|
elif method == Image.Resampling.BICUBIC and not self.opt.upscale:
|
||||||
pass
|
pass
|
||||||
@@ -519,7 +537,7 @@ class ComicPage:
|
|||||||
self.image = ImageOps.fit(self.image, self.size, method=method)
|
self.image = ImageOps.fit(self.image, self.size, method=method)
|
||||||
elif abs(ratio_image - ratio_device) < AUTO_CROP_THRESHOLD:
|
elif abs(ratio_image - ratio_device) < AUTO_CROP_THRESHOLD:
|
||||||
self.image = ImageOps.fit(self.image, self.size, method=method)
|
self.image = ImageOps.fit(self.image, self.size, method=method)
|
||||||
elif (self.opt.format in ('CBZ', 'PDF') or self.opt.kfx) and not self.opt.white_borders:
|
elif (self.opt.format in ('CBZ', 'PDF')) and not self.opt.white_borders:
|
||||||
self.image = ImageOps.pad(self.image, self.size, method=method, color=self.fill)
|
self.image = ImageOps.pad(self.image, self.size, method=method, color=self.fill)
|
||||||
else:
|
else:
|
||||||
self.image = ImageOps.contain(self.image, self.size, method=method)
|
self.image = ImageOps.contain(self.image, self.size, method=method)
|
||||||
@@ -601,12 +619,30 @@ class Cover:
|
|||||||
self.image = self.image.crop((w/6, 0, w/2 - w * 0.02, h))
|
self.image = self.image.crop((w/6, 0, w/2 - w * 0.02, h))
|
||||||
else:
|
else:
|
||||||
self.image = self.image.crop((w/2 + w * 0.02, 0, 5/6 * w, h))
|
self.image = self.image.crop((w/2 + w * 0.02, 0, 5/6 * w, h))
|
||||||
|
elif w / h > 1.83:
|
||||||
|
self.smartcover = True
|
||||||
|
if self.options.righttoleft:
|
||||||
|
self.image = self.image.crop((w * .19, 0, w * .575, h))
|
||||||
|
else:
|
||||||
|
self.image = self.image.crop((w * .425, 0, .81 * w, h))
|
||||||
|
elif w / h > 1.7:
|
||||||
|
self.smartcover = True
|
||||||
|
if self.options.righttoleft:
|
||||||
|
self.image = self.image.crop((w * .2, 0, w * .583, h))
|
||||||
|
else:
|
||||||
|
self.image = self.image.crop((w * .417, 0, .8 * w, h))
|
||||||
elif w / h > 1.34:
|
elif w / h > 1.34:
|
||||||
self.smartcover = True
|
self.smartcover = True
|
||||||
if self.options.righttoleft:
|
if self.options.righttoleft:
|
||||||
self.image = self.image.crop((0, 0, w/2 - w * 0.03, h))
|
self.image = self.image.crop((0, 0, w/2 - w * 0.03, h))
|
||||||
else:
|
else:
|
||||||
self.image = self.image.crop((w/2 + w * 0.03, 0, w, h))
|
self.image = self.image.crop((w/2 + w * 0.03, 0, w, h))
|
||||||
|
elif w / h > 1.0:
|
||||||
|
self.smartcover = True
|
||||||
|
if self.options.righttoleft:
|
||||||
|
self.image = self.image.crop((w * .36, 0, w, h))
|
||||||
|
else:
|
||||||
|
self.image = self.image.crop((w, 0, .64 * w, h))
|
||||||
|
|
||||||
def save_to_folder(self, target, tomeid, len_tomes=0):
|
def save_to_folder(self, target, tomeid, len_tomes=0):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -61,6 +61,23 @@ def getImageFileName(imgfile):
|
|||||||
ext = ext.lower()
|
ext = ext.lower()
|
||||||
return [name, ext]
|
return [name, ext]
|
||||||
|
|
||||||
|
def get_contain_resolution(image, size):
|
||||||
|
'''same code as Pillow ImageOps.contain()'''
|
||||||
|
im_ratio = image.width / image.height
|
||||||
|
dest_ratio = size[0] / size[1]
|
||||||
|
|
||||||
|
if im_ratio != dest_ratio:
|
||||||
|
if im_ratio > dest_ratio:
|
||||||
|
new_height = round(image.height / image.width * size[0])
|
||||||
|
if new_height != size[1]:
|
||||||
|
size = (size[0], new_height)
|
||||||
|
else:
|
||||||
|
new_width = round(image.width / image.height * size[1])
|
||||||
|
if new_width != size[0]:
|
||||||
|
size = (new_width, size[1])
|
||||||
|
|
||||||
|
return size
|
||||||
|
|
||||||
|
|
||||||
def walkSort(dirnames, filenames):
|
def walkSort(dirnames, filenames):
|
||||||
convert = lambda text: int(text) if text.isdigit() else text
|
convert = lambda text: int(text) if text.isdigit() else text
|
||||||
|
|||||||
Reference in New Issue
Block a user