From e7b7054b0e27330e475e548ef35918f8ff953934 Mon Sep 17 00:00:00 2001 From: Alex Xu Date: Wed, 15 Apr 2026 21:25:08 -0700 Subject: [PATCH] smart cover crop is default on and implemented for all formats (#1295) --- README.md | 2 +- gui/KCC.ui | 6 +++--- kindlecomicconverter/KCC_gui.py | 6 +++--- kindlecomicconverter/KCC_ui.py | 10 +++++----- kindlecomicconverter/comic2ebook.py | 10 +++++++--- kindlecomicconverter/image.py | 7 +++++-- 6 files changed, 24 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index a65f357..01cebcc 100644 --- a/README.md +++ b/README.md @@ -270,7 +270,7 @@ PROCESSING: Crop empty sections. 0: Disabled 1: Horizontally 2: Both [Default=0] --blackborders Disable autodetection and force black borders --whiteborders Disable autodetection and force white borders - --smartcovercrop Attempt to crop main cover from wide image + --nosmartcovercrop Disable attempt to crop main cover from wide image --coverfill Center-crop only the cover to fill target device screen --forcecolor Don't convert images to grayscale --forcepng Create PNG files instead JPEG for black and white images diff --git a/gui/KCC.ui b/gui/KCC.ui index 63cd15d..b2c960d 100644 --- a/gui/KCC.ui +++ b/gui/KCC.ui @@ -655,12 +655,12 @@ Higher values are larger and higher quality, and may resolve blank page issues.< - + - Attempt to crop main cover from wide image. + Disable attempt to crop main cover from wide image. - Smart Cover Crop + No Smart Cover Crop diff --git a/kindlecomicconverter/KCC_gui.py b/kindlecomicconverter/KCC_gui.py index 0a2826c..089af51 100644 --- a/kindlecomicconverter/KCC_gui.py +++ b/kindlecomicconverter/KCC_gui.py @@ -331,8 +331,8 @@ class WorkerThread(QThread): options.pdfextract = True if GUI.pdfWidthBox.isChecked(): options.pdfwidth = True - if GUI.smartCoverCropBox.isChecked(): - options.smartcovercrop = True + if GUI.noSmartCoverCropBox.isChecked(): + options.nosmartcovercrop = True if GUI.coverFillBox.isChecked(): options.coverfill = True if GUI.metadataTitleBox.checkState() == Qt.CheckState.PartiallyChecked: @@ -1077,7 +1077,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow): 'disableProcessingBox': GUI.disableProcessingBox.checkState(), 'pdfExtractBox': GUI.pdfExtractBox.checkState(), 'pdfWidthBox': GUI.pdfWidthBox.checkState(), - 'smartCoverCropBox': GUI.smartCoverCropBox.checkState(), + 'noSmartCoverCropBox': GUI.noSmartCoverCropBox.checkState(), 'coverFillBox': GUI.coverFillBox.checkState(), 'metadataTitleBox': GUI.metadataTitleBox.checkState(), 'mozJpegBox': GUI.mozJpegBox.checkState(), diff --git a/kindlecomicconverter/KCC_ui.py b/kindlecomicconverter/KCC_ui.py index b5f5c9d..cf11c6e 100644 --- a/kindlecomicconverter/KCC_ui.py +++ b/kindlecomicconverter/KCC_ui.py @@ -344,10 +344,10 @@ class Ui_mainWindow(object): self.gridLayout_2.addWidget(self.metadataTitleBox, 7, 0, 1, 1) - self.smartCoverCropBox = QCheckBox(self.optionWidget) - self.smartCoverCropBox.setObjectName(u"smartCoverCropBox") + self.noSmartCoverCropBox = QCheckBox(self.optionWidget) + self.noSmartCoverCropBox.setObjectName(u"noSmartCoverCropBox") - self.gridLayout_2.addWidget(self.smartCoverCropBox, 11, 1, 1, 1) + self.gridLayout_2.addWidget(self.noSmartCoverCropBox, 11, 1, 1, 1) self.rotateFirstBox = QCheckBox(self.optionWidget) self.rotateFirstBox.setObjectName(u"rotateFirstBox") @@ -748,9 +748,9 @@ class Ui_mainWindow(object): #endif // QT_CONFIG(tooltip) self.metadataTitleBox.setText(QCoreApplication.translate("mainWindow", u"Metadata Title", None)) #if QT_CONFIG(tooltip) - self.smartCoverCropBox.setToolTip(QCoreApplication.translate("mainWindow", u"Attempt to crop main cover from wide image.", None)) + self.noSmartCoverCropBox.setToolTip(QCoreApplication.translate("mainWindow", u"Disable attempt to crop main cover from wide image.", None)) #endif // QT_CONFIG(tooltip) - self.smartCoverCropBox.setText(QCoreApplication.translate("mainWindow", u"Smart Cover Crop", None)) + self.noSmartCoverCropBox.setText(QCoreApplication.translate("mainWindow", u"No Smart Cover Crop", None)) #if QT_CONFIG(tooltip) self.rotateFirstBox.setToolTip(QCoreApplication.translate("mainWindow", u"

When the spread splitter option is partially checked,

Unchecked - Rotate Last
Put the rotated 2 page spread after the split spreads.

Checked - Rotate First
Put the rotated 2 page spread before the split spreads.

", None)) #endif // QT_CONFIG(tooltip) diff --git a/kindlecomicconverter/comic2ebook.py b/kindlecomicconverter/comic2ebook.py index 59a1408..11ffabd 100755 --- a/kindlecomicconverter/comic2ebook.py +++ b/kindlecomicconverter/comic2ebook.py @@ -556,7 +556,7 @@ def buildEPUB(path, chapternames, tomenumber, ischunked, cover: image.Cover, ori f.close() build_html_start = perf_counter() if cover: - cover.save_to_epub(os.path.join(path, 'OEBPS', 'Images', 'cover.jpg'), tomenumber, len_tomes) + cover.save_to_folder(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')): @@ -1394,8 +1394,8 @@ def makeParser(): help="Use the legacy PDF image extraction method from KCC 8 and earlier") processing_options.add_argument("--pdfwidth", action="store_true", dest="pdfwidth", default=False, help="Render vector PDFs to device width instead of height.") - processing_options.add_argument("--smartcovercrop", action="store_true", dest="smartcovercrop", default=False, - help="Attempt to crop main cover from wide image") + processing_options.add_argument("--nosmartcovercrop", action="store_true", dest="nosmartcovercrop", default=False, + help="Disable attempt to crop main cover from wide image") processing_options.add_argument("--coverfill", action="store_true", dest="coverfill", default=False, help="Crop cover to fill screen") processing_options.add_argument("-u", "--upscale", action="store_true", dest="upscale", default=False, @@ -1712,12 +1712,16 @@ def makeBook(source, qtgui=None, job_progress=''): filepath.append(getOutputFilename(source, options.output, '.cbz', ' ' + str(tomeNumber))) else: filepath.append(getOutputFilename(source, options.output, '.cbz', '')) + if cover.smartcover: + 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) elif options.format == 'PDF': print(f"{job_progress}Creating PDF file with PyMuPDF...") # determine output filename based on source and tome count suffix = (' ' + str(tomeNumber)) if len(tomes) > 1 else '' output_file = getOutputFilename(source, options.output, '.pdf', suffix) + if cover.smartcover: + cover.save_to_folder(os.path.join(tome, 'OEBPS', 'Images', 'cover.jpg'), tomeNumber, len(tomes)) # use optimized buildPDF logic with streaming and compression output_pdf = buildPDF(tome, options.title, job_progress, None, output_file) filepath.append(output_pdf) diff --git a/kindlecomicconverter/image.py b/kindlecomicconverter/image.py index 892dc77..98ec5de 100755 --- a/kindlecomicconverter/image.py +++ b/kindlecomicconverter/image.py @@ -569,6 +569,7 @@ class Cover: self.options = opt self.source = source self.image = Image.open(source) + self.smartcover = False # backwards compatibility for Pillow >9.1.0 if not hasattr(Image, 'Resampling'): Image.Resampling = Image @@ -579,7 +580,7 @@ class Cover: self.image = ImageOps.autocontrast(self.image, preserve_tone=True) if not self.options.forcecolor: self.image = self.image.convert('L') - if self.options.smartcovercrop: + if not self.options.nosmartcovercrop: self.crop_main_cover() size = list(self.options.profileData[1]) @@ -595,17 +596,19 @@ class Cover: def crop_main_cover(self): w, h = self.image.size if w / h > 2: + self.smartcover = True if self.options.righttoleft: self.image = self.image.crop((w/6, 0, w/2 - w * 0.02, h)) else: self.image = self.image.crop((w/2 + w * 0.02, 0, 5/6 * w, h)) elif w / h > 1.34: + self.smartcover = True if self.options.righttoleft: self.image = self.image.crop((0, 0, w/2 - w * 0.03, h)) else: self.image = self.image.crop((w/2 + w * 0.03, 0, w, h)) - def save_to_epub(self, target, tomeid, len_tomes=0): + def save_to_folder(self, target, tomeid, len_tomes=0): try: if tomeid == 0: self.image.save(target, "JPEG", optimize=1, quality=self.options.jpegquality)