mirror of
https://github.com/ciromattia/kcc
synced 2026-04-24 18:09:01 +00:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5122cc188 | ||
|
|
61be6aa78e | ||
|
|
d6834063c1 | ||
|
|
1a8d74de4a | ||
|
|
a0a194ecf1 | ||
|
|
290578d66e | ||
|
|
f97398d481 | ||
|
|
a7a9f35686 | ||
|
|
d5146d02fc |
19
README.md
19
README.md
@@ -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 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.
|
||||
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
|
||||
- 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.
|
||||
Additionally, it will break page numbers.
|
||||
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.
|
||||
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.
|
||||
- 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.
|
||||
- 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.
|
||||
- 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?
|
||||
@@ -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.
|
||||
- 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.
|
||||
- [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?
|
||||
- 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),
|
||||
'KPW34': ("Kindle Paperwhite 3/4", (1072, 1448), 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),
|
||||
'KCS': ("Kindle Colorsoft", (1264, 1680), Palette16, 1.0),
|
||||
'KPW6': ("Kindle Paperwhite 6", (1272, 1696), 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),
|
||||
'KS1920': ("Kindle 1920", (1920, 1920), Palette16, 1.0),
|
||||
'KS': ("Kindle Scribe 1/2", (1860, 2480), Palette16, 1.0),
|
||||
|
||||
@@ -795,8 +795,8 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
GUI.rotateBox.setChecked(False)
|
||||
GUI.borderBox.setEnabled(False)
|
||||
GUI.borderBox.setCheckState(Qt.CheckState.PartiallyChecked)
|
||||
GUI.upscaleBox.setEnabled(False)
|
||||
GUI.upscaleBox.setChecked(False)
|
||||
# GUI.upscaleBox.setEnabled(False)
|
||||
# GUI.upscaleBox.setChecked(False)
|
||||
GUI.croppingBox.setEnabled(False)
|
||||
GUI.croppingBox.setChecked(False)
|
||||
GUI.interPanelCropBox.setEnabled(False)
|
||||
@@ -813,7 +813,7 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
GUI.rotateBox.setEnabled(True)
|
||||
GUI.borderBox.setEnabled(True)
|
||||
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.croppingBox.setEnabled(True)
|
||||
GUI.interPanelCropBox.setEnabled(True)
|
||||
@@ -908,10 +908,10 @@ class KCCGUI(KCC_ui.Ui_mainWindow):
|
||||
if not GUI.webtoonBox.isChecked():
|
||||
GUI.qualityBox.setEnabled(profile['PVOptions'])
|
||||
GUI.upscaleBox.setChecked(profile['DefaultUpscale'])
|
||||
if profile['Label'].startswith('KS'):
|
||||
if profile['Label'].startswith('KS') and False:
|
||||
GUI.upscaleBox.setDisabled(True)
|
||||
else:
|
||||
if not GUI.webtoonBox.isChecked():
|
||||
if not GUI.webtoonBox.isChecked() or True:
|
||||
GUI.upscaleBox.setEnabled(True)
|
||||
if profile['Label'] == 'KCS':
|
||||
current_format = GUI.formats[str(GUI.formatBox.currentText())]['format']
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
__version__ = '9.7.2'
|
||||
__version__ = '10.0.1'
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = '2012-2022, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>, darodi'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
@@ -136,14 +136,14 @@ def buildHTML(path, imgfile, imgfilepath, imgfile2=None):
|
||||
"content=\"width=" + str(imgsizeframe[0]) + ", height=" + str(imgsizeframe[1]) + "\"/>\n"
|
||||
"</head>\n",
|
||||
"<body style=\"" + additionalStyle + "\">\n",
|
||||
"<div style=\"text-align:center;top:" + getTopMargin(deviceres, imgsizeframe) + "%;\">\n",
|
||||
"<div style=\"text-align:center;\">\n",
|
||||
])
|
||||
if options.iskindle:
|
||||
# this display none div fixes formatting issues with virtual panel mode, for some reason
|
||||
f.write('<div style="display:none;">.</div>\n')
|
||||
f.write(f'<img width="{imgsize[0]}" height="{imgsize[1]}" src="{"../" * backref}Images/{postfix}{imgfile}"/>\n')
|
||||
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")
|
||||
if options.iskindle and options.panelview:
|
||||
if options.autoscale:
|
||||
@@ -324,8 +324,8 @@ def buildOPF(dstdir, title, filelist, originalpath, cover=None):
|
||||
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=\"",
|
||||
# str(deviceres[0]) + "x" + str(deviceres[1]) + "\"/>\n",
|
||||
"<meta name=\"original-resolution\" content=\"",
|
||||
str(deviceres[0]) + "x" + str(deviceres[1]) + "\"/>\n",
|
||||
"<meta name=\"book-type\" content=\"comic\"/>\n",
|
||||
"<meta name=\"primary-writing-mode\" content=\"" + writingmode + "\"/>\n",
|
||||
"<meta name=\"zero-gutter\" content=\"true\"/>\n",
|
||||
@@ -873,14 +873,18 @@ def mupdf_pdf_process_pages_parallel(filename, output_dir, target_width, target_
|
||||
|
||||
def getWorkFolder(afile, workdir=None):
|
||||
if not workdir:
|
||||
workdir = mkdtemp('', 'KCC-')
|
||||
if options.tempdir:
|
||||
workdir = mkdtemp('', 'KCC-', os.path.dirname(afile))
|
||||
else:
|
||||
workdir = mkdtemp('', 'KCC-')
|
||||
fullPath = os.path.join(workdir, 'OEBPS', 'Images')
|
||||
else:
|
||||
fullPath = workdir
|
||||
check_path = gettempdir()
|
||||
if options.tempdir:
|
||||
check_path = os.path.dirname(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.")
|
||||
try:
|
||||
copytree(afile, fullPath)
|
||||
@@ -890,7 +894,7 @@ def getWorkFolder(afile, workdir=None):
|
||||
rmtree(workdir, True)
|
||||
raise UserWarning("Failed to prepare a workspace.")
|
||||
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.")
|
||||
if afile.lower().endswith('.pdf'):
|
||||
if not os.path.exists(fullPath):
|
||||
@@ -1075,11 +1079,6 @@ def getDirectorySize(start_path='.'):
|
||||
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):
|
||||
scale = float(deviceres[0]) / float(imagesize[0])
|
||||
return int(deviceres[0]), int(scale * imagesize[1])
|
||||
@@ -1471,6 +1470,15 @@ def checkOptions(options):
|
||||
options.isKobo = False
|
||||
options.bordersColor = None
|
||||
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):
|
||||
raise UserWarning('MOBI/EPUB-200MB not supported for non-Kindle profiles')
|
||||
|
||||
if options.format == 'PDF-200MB':
|
||||
options.targetsize = 195
|
||||
options.format = 'PDF'
|
||||
@@ -1502,10 +1510,7 @@ def checkOptions(options):
|
||||
options.format = 'PDF'
|
||||
else:
|
||||
options.format = 'EPUB'
|
||||
if options.profile in image.ProfileData.ProfilesKindle.keys():
|
||||
options.iskindle = True
|
||||
else:
|
||||
options.isKobo = True
|
||||
|
||||
if options.white_borders:
|
||||
options.bordersColor = 'white'
|
||||
if options.black_borders:
|
||||
@@ -1557,6 +1562,7 @@ def checkOptions(options):
|
||||
options.jpegquality = 90
|
||||
else:
|
||||
options.jpegquality = 85
|
||||
|
||||
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
|
||||
|
||||
@@ -1715,7 +1721,7 @@ 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:
|
||||
if cover and 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':
|
||||
@@ -1723,7 +1729,7 @@ def makeBook(source, qtgui=None, job_progress=''):
|
||||
# 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:
|
||||
if cover and 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)
|
||||
|
||||
Reference in New Issue
Block a user