diff --git a/README.md b/README.md index 9f40d6a..d228a64 100644 --- a/README.md +++ b/README.md @@ -249,6 +249,7 @@ MAIN: PROCESSING: -n, --noprocessing Do not modify image and ignore any profile or processing option --pdfextract Use legacy PDF image extraction method from KCC 8 and earlier. + --pdfwidth Render vector PDFs based on device width instead of height. -u, --upscale Resize images smaller than device's resolution -s, --stretch Stretch images to device's resolution -r SPLITTER, --splitter SPLITTER diff --git a/gui/KCC.ui b/gui/KCC.ui index d938ed0..3fed46e 100644 --- a/gui/KCC.ui +++ b/gui/KCC.ui @@ -943,6 +943,18 @@ Eink only has 16 shades of gray so you probably don't want this. + + + + Render vector PDFs to device width instead of height. + +Useful if you plan to crop a little off the top and bottom to fill screen. + + + PDF Width Render + + + diff --git a/kindlecomicconverter/KCC_gui.py b/kindlecomicconverter/KCC_gui.py index e11419d..7dc05a9 100644 --- a/kindlecomicconverter/KCC_gui.py +++ b/kindlecomicconverter/KCC_gui.py @@ -329,6 +329,8 @@ class WorkerThread(QThread): options.noprocessing = True if GUI.pdfExtractBox.isChecked(): options.pdfextract = True + if GUI.pdfWidthBox.isChecked(): + options.pdfwidth = True if GUI.coverFillBox.isChecked(): options.coverfill = True if GUI.metadataTitleBox.checkState() == Qt.CheckState.PartiallyChecked: @@ -888,6 +890,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow): self.addMessage('Colorsoft MOBI/EPUB can have blank pages. Just go back a few pages, exit, and reenter book.', 'info') break elif profile['Label'] == 'KDX': + GUI.mozJpegBox.setCheckState(Qt.CheckState.PartiallyChecked) GUI.borderBox.setCheckState(Qt.CheckState.PartiallyChecked) if not profile['PVOptions']: GUI.qualityBox.setChecked(False) @@ -1044,6 +1047,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow): 'eraseRainbowBox': GUI.eraseRainbowBox.checkState(), 'disableProcessingBox': GUI.disableProcessingBox.checkState(), 'pdfExtractBox': GUI.pdfExtractBox.checkState(), + 'pdfWidthBox': GUI.pdfWidthBox.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 115ded1..4a999c9 100644 --- a/kindlecomicconverter/KCC_ui.py +++ b/kindlecomicconverter/KCC_ui.py @@ -482,6 +482,11 @@ class Ui_mainWindow(object): self.gridLayout_2.addWidget(self.noQuantizeBox, 10, 2, 1, 1) + self.pdfWidthBox = QCheckBox(self.optionWidget) + self.pdfWidthBox.setObjectName(u"pdfWidthBox") + + self.gridLayout_2.addWidget(self.pdfWidthBox, 10, 0, 1, 1) + self.gridLayout.addWidget(self.optionWidget, 5, 0, 1, 2) @@ -777,6 +782,12 @@ class Ui_mainWindow(object): "Eink only has 16 shades of gray so you probably don't want this.", None)) #endif // QT_CONFIG(tooltip) self.noQuantizeBox.setText(QCoreApplication.translate("mainWindow", u"No Quantize", None)) +#if QT_CONFIG(tooltip) + self.pdfWidthBox.setToolTip(QCoreApplication.translate("mainWindow", u"Render vector PDFs to device width instead of height.\n" +"\n" +"Useful if you plan to crop a little off the top and bottom to fill screen.", None)) +#endif // QT_CONFIG(tooltip) + self.pdfWidthBox.setText(QCoreApplication.translate("mainWindow", u"PDF Width Render", None)) self.gammaLabel.setText(QCoreApplication.translate("mainWindow", u"Gamma: Auto", None)) self.jpegQualityLabel.setText(QCoreApplication.translate("mainWindow", u"JPEG Quality:", None)) # retranslateUi diff --git a/kindlecomicconverter/comic2ebook.py b/kindlecomicconverter/comic2ebook.py index 9aa1052..325c8c5 100755 --- a/kindlecomicconverter/comic2ebook.py +++ b/kindlecomicconverter/comic2ebook.py @@ -744,7 +744,9 @@ def render_page(vector): cpu = vector[1] # number of CPUs filename = vector[2] # document filename output_dir = vector[3] - target_height = vector[4] + target_width = vector[4] + target_height = vector[5] + pdf_width = vector[6] with pymupdf.open(filename) as doc: # open the document num_pages = doc.page_count # get number of pages @@ -755,7 +757,10 @@ def render_page(vector): for i in range(seg_from, seg_to): # work through our page segment page = doc[i] - zoom = target_height / page.rect.height + if not pdf_width or page.rect.width > page.rect.height: + zoom = target_height / page.rect.height + else: + zoom = target_width / page.rect.width 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 @@ -820,7 +825,7 @@ def extract_page(vector): -def mupdf_pdf_process_pages_parallel(filename, output_dir, target_height): +def mupdf_pdf_process_pages_parallel(filename, output_dir, target_width, target_height): render = False with pymupdf.open(filename) as doc: for page in doc: @@ -840,7 +845,7 @@ def mupdf_pdf_process_pages_parallel(filename, output_dir, target_height): cpu = cpu_count() # make vectors of arguments for the processes - vectors = [(i, cpu, filename, output_dir, target_height) for i in range(cpu)] + vectors = [(i, cpu, filename, output_dir, target_width, target_height, options.pdfwidth) for i in range(cpu)] print("Starting %i processes for '%s'." % (cpu, filename)) @@ -885,13 +890,13 @@ def getWorkFolder(afile, workdir=None): if njpg == 0: raise UserWarning("Failed to extract images from PDF file.") return workdir - target_height = options.profileData[1][1] + target_width, target_height = options.profileData[1] if options.cropping == 1: target_height = target_height + target_height*0.20 #Account for possible margin at the top and bottom elif options.cropping == 2: target_height = target_height + target_height*0.25 #Account for possible margin at the top and bottom with page number try: - mupdf_pdf_process_pages_parallel(afile, fullPath, target_height) + mupdf_pdf_process_pages_parallel(afile, fullPath, target_width, target_height) except Exception as e: rmtree(path, True) raise UserWarning(f"Failed to extract images from PDF file. {e}") @@ -1366,6 +1371,8 @@ def makeParser(): help="Do not modify image and ignore any profile or processing option") processing_options.add_argument("--pdfextract", action="store_true", dest="pdfextract", default=False, 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("--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, diff --git a/kindlecomicconverter/image.py b/kindlecomicconverter/image.py index 635886f..6583ace 100755 --- a/kindlecomicconverter/image.py +++ b/kindlecomicconverter/image.py @@ -507,7 +507,9 @@ class ComicPage: elif method == Image.Resampling.BICUBIC and not self.opt.upscale: pass else: # if image bigger than device resolution or smaller with upscaling - if abs(ratio_image - ratio_device) < AUTO_CROP_THRESHOLD: + if self.opt.profile == 'KDX' and abs(ratio_image - ratio_device) < AUTO_CROP_THRESHOLD * 3: + self.image = ImageOps.fit(self.image, self.size, method=method) + elif abs(ratio_image - ratio_device) < AUTO_CROP_THRESHOLD: 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: self.image = ImageOps.pad(self.image, self.size, method=method, color=self.fill)