diff --git a/.gitignore b/.gitignore
index 24db16d..14d4c97 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,4 +7,5 @@ dist
kindlegen*
.DS_Store
Thumbs.db
-UnRAR.exe
+/UnRAR.exe
+/7za.exe
diff --git a/KCC-Linux.ui b/KCC-Linux.ui
new file mode 100644
index 0000000..40d408b
--- /dev/null
+++ b/KCC-Linux.ui
@@ -0,0 +1,812 @@
+
+
+ KCC
+
+
+
+ 0
+ 0
+ 420
+ 380
+
+
+
+
+ 420
+ 380
+
+
+
+
+ 420
+ 380
+
+
+
+
+ 9
+
+
+
+ Qt::NoFocus
+
+
+ Kindle Comic Converter
+
+
+
+ :/Icon/icons/comic2ebook.png:/Icon/icons/comic2ebook.png
+
+
+
+
+
+
+
+ true
+
+
+
+ 1
+ 254
+ 421
+ 61
+
+
+
+
+ DejaVu Sans
+ 9
+
+
+
+
+ 9
+
+ -
+
+
+
+ DejaVu Sans
+
+
+
+ Qt::NoFocus
+
+
+ Disable image optimizations.
+
+
+ No optimisation
+
+
+
+ -
+
+
+
+ DejaVu Sans
+
+
+
+ Qt::NoFocus
+
+
+ <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>
+
+
+ Stretch/Upscale
+
+
+ true
+
+
+
+ -
+
+
+
+ DejaVu Sans
+
+
+
+ Qt::NoFocus
+
+
+ <html><head/><body><p>Enable auto-splitting of webtoons like <span style=" font-style:italic;">Tower of God</span> or <span style=" font-style:italic;">Noblesse</span>.<br/>Pages with a low width, high height and vertical panel flow.</p></body></html>
+
+
+ Webtoon mode
+
+
+
+ -
+
+
+
+ DejaVu Sans
+
+
+
+ Qt::NoFocus
+
+
+ <html><head/><body><p>Create PNG files instead JPEG.</p></body></html>
+
+
+ PNG output
+
+
+
+ -
+
+
+
+ DejaVu Sans
+
+
+
+ Qt::NoFocus
+
+
+ <html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Autodetection<br/></span>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 filled with white color.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Black<br/></span>Margins will be filled with black color.</p></body></html>
+
+
+ W/B margins
+
+
+ true
+
+
+
+ -
+
+
+
+ DejaVu Sans
+
+
+
+ Qt::NoFocus
+
+
+ <html><head/><body><p>Disable splitting and rotation.</p></body></html>
+
+
+ No split/rotate
+
+
+
+
+
+
+
+
+ 10
+ 200
+ 141
+ 31
+
+
+
+
+ DejaVu Sans
+ 8
+
+
+
+ Qt::NoFocus
+
+
+ Target device.
+
+
+
+
+
+ 260
+ 200
+ 151
+ 31
+
+
+
+
+ DejaVu Sans
+ 8
+
+
+
+ Qt::NoFocus
+
+
+ Output format.
+
+
+
+
+
+ 160
+ 200
+ 91
+ 32
+
+
+
+
+ DejaVu Sans
+ 9
+ 75
+ true
+
+
+
+ Qt::NoFocus
+
+
+ Convert
+
+
+
+ :/Other/icons/convert.png:/Other/icons/convert.png
+
+
+
+
+
+ 10
+ 160
+ 141
+ 32
+
+
+
+
+ DejaVu Sans
+ 8
+
+
+
+ Qt::NoFocus
+
+
+ Add directory
+
+
+
+ :/Other/icons/folder_new.png:/Other/icons/folder_new.png
+
+
+
+
+
+ 260
+ 160
+ 151
+ 32
+
+
+
+
+ DejaVu Sans
+ 8
+
+
+
+ Qt::NoFocus
+
+
+ Add file
+
+
+
+ :/Other/icons/document_new.png:/Other/icons/document_new.png
+
+
+
+
+
+ 160
+ 160
+ 91
+ 32
+
+
+
+
+ DejaVu Sans
+ 8
+
+
+
+ Qt::NoFocus
+
+
+ Clear list
+
+
+
+ :/Other/icons/clear.png:/Other/icons/clear.png
+
+
+
+
+
+ 1
+ 230
+ 421
+ 41
+
+
+
+
+ DejaVu Sans
+ 9
+
+
+
+
+
+ 9
+ 10
+ 130
+ 18
+
+
+
+
+ DejaVu Sans
+
+
+
+ Qt::NoFocus
+
+
+ Enable right-to-left reading.
+
+
+ Manga mode
+
+
+
+
+
+ 282
+ 10
+ 135
+ 18
+
+
+
+
+ DejaVu Sans
+
+
+
+ Qt::NoFocus
+
+
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<html><head><meta name="qrichtext" content="1" /><style type="text/css">
+p, li { white-space: pre-wrap; }
+</style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;">
+<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-weight:600; text-decoration: underline;">Unchecked - Normal quality mode<br /></span><span style=" font-family:'MS Shell Dlg 2'; font-style:italic;">Use it when Panel View support is not needed.</span><span style=" font-family:'MS Shell Dlg 2'; font-weight:600; text-decoration: underline;"><br /></span><span style=" font-family:'MS Shell Dlg 2';">- Maximum quality when zoom is not enabled.<br />- Poor quality when zoom is enabled.<br />- Lowest file size.</span></p>
+<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-weight:600; text-decoration: underline;">Indeterminate - High quality mode<br /></span><span style=" font-family:'MS Shell Dlg 2'; font-style:italic;">Not zoomed image </span><span style=" font-family:'MS Shell Dlg 2'; font-weight:600; font-style:italic;">might </span><span style=" font-family:'MS Shell Dlg 2'; font-style:italic;">be a little blurry.</span><span style=" font-family:'MS Shell Dlg 2'; font-weight:600; text-decoration: underline;"><br /></span><span style=" font-family:'MS Shell Dlg 2';">- Medium/High quality when zoom is not enabled.<br />- Maximum quality when zoom is enabled.</span></p>
+<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-weight:600; text-decoration: underline;">Checked - Ultra quality mode<br /></span><span style=" font-family:'MS Shell Dlg 2'; font-style:italic;">Maximum possible quality.</span><span style=" font-family:'MS Shell Dlg 2'; font-weight:600; text-decoration: underline;"><br /></span><span style=" font-family:'MS Shell Dlg 2';">- Maximum quality when zoom is not enabled.<br />- Maximum quality when zoom is enabled.<br />- Very high file size.</span></p></body></html>
+
+
+ High/Ultra quality
+
+
+ true
+
+
+
+
+
+ 145
+ 10
+ 130
+ 18
+
+
+
+
+ DejaVu Sans
+
+
+
+ Qt::NoFocus
+
+
+ <html><head/><body><p>Disable page spliting.<br/>They will be rotated instead.</p></body></html>
+
+
+ Horizontal mode
+
+
+ RotateBox
+ MangaBox
+ QualityBox
+
+
+
+
+ 10
+ 50
+ 401
+ 101
+
+
+
+
+ DejaVu Sans
+ 8
+ false
+
+
+
+ Qt::NoFocus
+
+
+ false
+
+
+ QAbstractItemView::NoSelection
+
+
+
+ 18
+ 18
+
+
+
+
+
+
+ 10
+ 10
+ 195
+ 32
+
+
+
+
+ DejaVu Sans
+ 9
+
+
+
+ Qt::NoFocus
+
+
+ Basic
+
+
+
+
+
+ 217
+ 10
+ 195
+ 32
+
+
+
+
+ DejaVu Sans
+ 9
+
+
+
+ Qt::NoFocus
+
+
+ Advanced
+
+
+
+
+ true
+
+
+
+ 10
+ 305
+ 401
+ 41
+
+
+
+
+ DejaVu Sans
+ 9
+
+
+
+
+
+ 15
+ 0
+ 100
+ 40
+
+
+
+
+ DejaVu Sans
+
+
+
+ <html><head/><body><p>When converting color images setting this option to 1.0 <span style=" font-weight:600;">might</span> improve readability.</p></body></html>
+
+
+ Gamma: Auto
+
+
+
+
+
+ 110
+ 10
+ 275
+ 22
+
+
+
+
+ DejaVu Sans
+
+
+
+ Qt::ClickFocus
+
+
+ <html><head/><body><p>When converting color images setting this option to 1.0 <span style=" font-weight:600;">might</span> improve readability.</p></body></html>
+
+
+ 500
+
+
+ 5
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+ 10
+ 10
+ 401
+ 31
+
+
+
+
+ DejaVu Sans
+ 10
+ 75
+ true
+
+
+
+ 0
+
+
+ Qt::AlignJustify|Qt::AlignVCenter
+
+
+
+
+
+
+
+
+ 1
+ 337
+ 421
+ 41
+
+
+
+
+ DejaVu Sans
+ 9
+
+
+
+
+
+ 9
+ 11
+ 130
+ 18
+
+
+
+
+ DejaVu Sans
+
+
+
+ Qt::NoFocus
+
+
+ Do not convert images to grayscale.
+
+
+ Color mode
+
+
+
+
+
+ 105
+ 0
+ 295
+ 40
+
+
+
+
+ DejaVu Sans
+
+
+
+ -
+
+
+
+ DejaVu Sans
+
+
+
+ Resolution of target device.
+
+
+ Custom width:
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 40
+ 16777215
+
+
+
+
+ DejaVu Sans
+
+
+
+ Qt::ClickFocus
+
+
+ false
+
+
+ Resolution of target device.
+
+
+ 0000;
+
+
+ 4
+
+
+
+ -
+
+
+
+ DejaVu Sans
+
+
+
+ Resolution of target device.
+
+
+ Custom height:
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 40
+ 16777215
+
+
+
+
+ DejaVu Sans
+
+
+
+ Qt::ClickFocus
+
+
+ false
+
+
+ Resolution of target device.
+
+
+ 0000;
+
+
+ 4
+
+
+
+
+
+
+ OptionsAdvanced
+ DeviceBox
+ FormatBox
+ ConvertButton
+ DirectoryButton
+ FileButton
+ ClearButton
+ OptionsBasic
+ JobList
+ BasicModeButton
+ AdvModeButton
+ OptionsAdvancedGamma
+ OptionsExpert
+ ProgressBar
+
+
+
+ true
+
+
+ false
+
+
+ Basic
+
+
+
+
+
+
+
+ true
+
+
+ Advanced
+
+
+
+
+ DirectoryButton
+ FileButton
+ ConvertButton
+ ClearButton
+
+
+
+
+
+
diff --git a/KCC-OSX.ui b/KCC-OSX.ui
index d1e7289..2e57eb7 100644
--- a/KCC-OSX.ui
+++ b/KCC-OSX.ui
@@ -47,7 +47,7 @@
- 9
+ 4
253
421
61
@@ -55,6 +55,7 @@
+ Lucida Grande
9
@@ -63,7 +64,8 @@
- 11
+ Lucida Grande
+ 12
@@ -81,14 +83,15 @@
- 11
+ Lucida Grande
+ 12
Qt::NoFocus
- <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>
+ <html><head/><body><p><span style=" font-size:12pt; font-weight:600; text-decoration: underline;">Unchecked - Nothing<br/></span><span style=" font-size:12pt;">Images smaller than device resolution will not be resized.</span></p><p><span style=" font-size:12pt; font-weight:600; text-decoration: underline;">Indeterminate - Stretching<br/></span><span style=" font-size:12pt;">Images smaller than device resolution will be resized. Aspect ratio will be not preserved.</span></p><p><span style=" font-size:12pt; font-weight:600; text-decoration: underline;">Checked - Upscaling<br/></span><span style=" font-size:12pt;">Images smaller than device resolution will be resized. Aspect ratio will be preserved.</span></p></body></html>
Stretch/Upscale
@@ -102,14 +105,15 @@
- 11
+ Lucida Grande
+ 12
Qt::NoFocus
- <html><head/><body><p><span style=" font-weight:600;">EXPERIMENTAL!<br/></span>Enable auto-splitting of webtoons like <span style=" font-style:italic;">Tower of God</span> or <span style=" font-style:italic;">Noblesse</span>.<br/>Pages with a low width, high height and vertical panel flow.</p></body></html>
+ <html><head/><body><p><span style=" font-size:12pt;">Enable auto-splitting of webtoons like </span><span style=" font-size:12pt; font-style:italic;">Tower of God</span><span style=" font-size:12pt;"> or </span><span style=" font-size:12pt; font-style:italic;">Noblesse</span><span style=" font-size:12pt;">.<br/>Pages with a low width, high height and vertical panel flow.</span></p></body></html>
Webtoon mode
@@ -120,14 +124,15 @@
- 11
+ Lucida Grande
+ 12
Qt::NoFocus
- <html><head/><body><p><span style=" font-size:12pt;">Create PNG files instead JPEG.<br/></span><span style=" font-size:12pt; font-weight:600;">Only for non-Kindle devices!</span></p></body></html>
+ <html><head/><body><p><span style=" font-size:12pt;">Create PNG files instead JPEG.</span></p></body></html>
PNG output
@@ -138,17 +143,21 @@
- 11
+ Lucida Grande
+ 12
Qt::NoFocus
- <html><head/><body><p><span style=" font-size:12pt;">Fill space around images with black color.</span></p></body></html>
+ <html><head/><body><p><span style=" font-size:12pt; font-weight:600; text-decoration: underline;">Unchecked - Autodetection<br/></span><span style=" font-size:12pt;">Color of margins fill will be detected automatically.</span></p><p><span style=" font-size:12pt; font-weight:600; text-decoration: underline;">Indeterminate - White<br/></span><span style=" font-size:12pt;">Margins will be filled with white color.</span></p><p><span style=" font-size:12pt; font-weight:600; text-decoration: underline;">Checked - Black<br/></span><span style=" font-size:12pt;">Margins will be filled with black color.</span></p></body></html>
- Black borders
+ W/B margins
+
+
+ true
@@ -156,7 +165,8 @@
- 11
+ Lucida Grande
+ 12
@@ -183,6 +193,7 @@
+ Lucida Grande
11
@@ -190,7 +201,7 @@
Qt::NoFocus
- Target device.
+ <html><head/><body><p><span style=" font-size:12pt;">Target device.</span></p></body></html>
@@ -204,6 +215,7 @@
+ Lucida Grande
11
@@ -211,7 +223,7 @@
Qt::NoFocus
- Output format.
+ <html><head/><body><p><span style=" font-size:12pt;">Output format.</span></p></body></html>
@@ -225,6 +237,7 @@
+ Lucida Grande
11
75
true
@@ -252,6 +265,7 @@
+ Lucida Grande
11
@@ -277,6 +291,7 @@
+ Lucida Grande
11
@@ -302,6 +317,7 @@
+ Lucida Grande
11
@@ -319,7 +335,7 @@
- 10
+ 5
233
421
41
@@ -327,7 +343,8 @@
- 9
+ Lucida Grande
+ 12
@@ -341,7 +358,8 @@
- 11
+ Lucida Grande
+ 12
@@ -359,20 +377,21 @@
282
10
- 130
+ 135
18
- 11
+ Lucida Grande
+ 12
Qt::NoFocus
- <html><head/><body><p><span style=" font-family:'MS Shell Dlg 2'; font-size:14pt; font-weight:600; text-decoration: underline;">Unchecked - Normal quality mode<br/></span><span style=" font-family:'MS Shell Dlg 2'; font-size:14pt; font-style:italic;">Use it when Panel View support is not needed.</span><span style=" font-family:'MS Shell Dlg 2'; font-size:14pt; font-weight:600; text-decoration: underline;"><br/></span><span style=" font-family:'MS Shell Dlg 2'; font-size:14pt;">- Maximum quality when zoom is not enabled.<br/>- Poor quality when zoom is enabled.<br/>- Lowest file size.</span></p><p><span style=" font-family:'MS Shell Dlg 2'; font-size:14pt; font-weight:600; text-decoration: underline;">Indeterminate - High quality mode<br/></span><span style=" font-family:'MS Shell Dlg 2'; font-size:14pt; font-style:italic;">Not zoomed image </span><span style=" font-family:'MS Shell Dlg 2'; font-size:14pt; font-weight:600; font-style:italic;">might </span><span style=" font-family:'MS Shell Dlg 2'; font-size:14pt; font-style:italic;">be a little blurry.</span><span style=" font-family:'MS Shell Dlg 2'; font-size:14pt; font-weight:600; text-decoration: underline;"><br/></span><span style=" font-family:'MS Shell Dlg 2'; font-size:14pt;">- Medium/High quality when zoom is not enabled.<br/>- Maximum quality when zoom is enabled.</span></p><p><span style=" font-family:'MS Shell Dlg 2'; font-size:14pt; font-weight:600; text-decoration: underline;">Checked - Ultra quality mode<br/></span><span style=" font-family:'MS Shell Dlg 2'; font-size:14pt; font-style:italic;">Maximum possible quality.</span><span style=" font-family:'MS Shell Dlg 2'; font-size:14pt; font-weight:600; text-decoration: underline;"><br/></span><span style=" font-family:'MS Shell Dlg 2'; font-size:14pt;">- Maximum quality when zoom is not enabled.<br/>- Maximum quality when zoom is enabled.<br/>- Very high file size.</span></p></body></html>
+ <html><head/><body><p><span style="font-size:12pt; font-weight:600; text-decoration: underline;">Unchecked - Normal quality mode<br/></span><span style="font-size:12pt; font-style:italic;">Use it when Panel View support is not needed.</span><span style="font-size:12pt; font-weight:600; text-decoration: underline;"><br/></span><span style="font-size:12pt;">- Maximum quality when zoom is not enabled.<br/>- Poor quality when zoom is enabled.<br/>- Lowest file size.</span></p><p><span style="font-size:12pt; font-weight:600; text-decoration: underline;">Indeterminate - High quality mode<br/></span><span style="font-size:12pt; font-style:italic;">Not zoomed image </span><span style="font-size:12pt; font-weight:600; font-style:italic;">might </span><span style="font-size:12pt; font-style:italic;">be a little blurry.</span><span style="font-size:12pt; font-weight:600; text-decoration: underline;"><br/></span><span style="font-size:12pt;">- Medium/High quality when zoom is not enabled.<br/>- Maximum quality when zoom is enabled.</span></p><p><span style="font-size:12pt; font-weight:600; text-decoration: underline;">Checked - Ultra quality mode<br/></span><span style="font-size:12pt; font-style:italic;">Maximum possible quality.</span><span style="font-size:12pt; font-weight:600; text-decoration: underline;"><br/></span><span style="font-size:12pt;">- Maximum quality when zoom is not enabled.<br/>- Maximum quality when zoom is enabled.<br/>- Very high file size.</span></p></body></html>
High/Ultra quality
@@ -392,7 +411,8 @@
- 11
+ Lucida Grande
+ 12
@@ -420,6 +440,7 @@
+ Lucida Grande
11
@@ -444,6 +465,7 @@
+ Lucida Grande
12
50
false
@@ -467,6 +489,7 @@
+ Lucida Grande
12
50
false
@@ -485,7 +508,7 @@
- 10
+ 5
303
401
41
@@ -493,6 +516,7 @@
+ Lucida Grande
9
@@ -507,13 +531,14 @@
+ Lucida Grande
12
50
false
- <html><head/><body><p><span style=" font-size:12pt;">When converting color images setting this option to 1.0 MIGHT improve readability.</span></p></body></html>
+ <html><head/><body><p><span style=" font-size:12pt;">When converting color images setting this option to 1.0 </span><span style=" font-size:12pt; font-weight:600;">might</span><span style=" font-size:12pt;"> improve readability.</span></p></body></html>
Gamma: Auto
@@ -524,10 +549,15 @@
110
10
- 280
+ 290
22
+
+
+ Lucida Grande
+
+
Qt::ClickFocus
@@ -556,6 +586,7 @@
+ Lucida Grande
10
75
true
@@ -577,7 +608,7 @@
- 10
+ 5
335
421
41
@@ -585,6 +616,7 @@
+ Lucida Grande
9
@@ -599,7 +631,8 @@
- 11
+ Lucida Grande
+ 12
@@ -615,17 +648,23 @@
- 90
+ 95
0
315
40
+
+
+ Lucida Grande
+
+
-
+ Lucida Grande
12
50
false
@@ -655,6 +694,7 @@
+ Lucida Grande
12
@@ -679,6 +719,7 @@
+ Lucida Grande
12
50
false
@@ -708,6 +749,7 @@
+ Lucida Grande
12
diff --git a/KCC.ui b/KCC.ui
index 5387341..9d90feb 100644
--- a/KCC.ui
+++ b/KCC.ui
@@ -97,7 +97,7 @@
Qt::NoFocus
- <html><head/><body><p><span style=" font-weight:600;">EXPERIMENTAL!<br/></span>Enable auto-splitting of webtoons like <span style=" font-style:italic;">Tower of God</span> or <span style=" font-style:italic;">Noblesse</span>.<br/>Pages with a low width, high height and vertical panel flow.</p></body></html>
+ <html><head/><body><p>Enable auto-splitting of webtoons like <span style=" font-style:italic;">Tower of God</span> or <span style=" font-style:italic;">Noblesse</span>.<br/>Pages with a low width, high height and vertical panel flow.</p></body></html>
Webtoon mode
@@ -110,7 +110,7 @@
Qt::NoFocus
- <html><head/><body><p>Create PNG files instead JPEG.<br/><span style=" font-weight:600;">Only for non-Kindle devices!</span></p></body></html>
+ <html><head/><body><p>Create PNG files instead JPEG.</p></body></html>
PNG output
@@ -123,10 +123,13 @@
Qt::NoFocus
- Fill space around images with black color.
+ <html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Unchecked - Autodetection<br/></span>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 filled with white color.</p><p><span style=" font-weight:600; text-decoration: underline;">Checked - Black<br/></span>Margins will be filled with black color.</p></body></html>
- Black borders
+ W/B margins
+
+
+ true
diff --git a/README.md b/README.md
index 57dbb5f..795ebd2 100644
--- a/README.md
+++ b/README.md
@@ -30,11 +30,13 @@ You can find the latest released binary at the following links:
- Folders
- CBZ, ZIP
- CBR, RAR *(With `unrar` executable)*
+- CB7, 7Z *(With `7za` executable)*
- PDF *(Extracting only contained JPG images)*
## OPTIONAL REQUIREMENTS
- [KindleGen](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211) v2.9+ in a directory reachable by your _PATH_ or in _KCC_ directory *(For .mobi generation)*
- [UnRAR](http://www.rarlab.com/download.htm) *(For CBR/RAR support)*
+- [7za](http://www.7-zip.org/download.html) *(For 7z/CB7 support)*
### For compiling/running from source:
- Python 2.7 - Included in MacOS and Linux, follow the [official documentation](http://www.python.org/getit/windows/) to install on Windows.
@@ -52,7 +54,7 @@ You can find the latest released binary at the following links:
* Check our [wiki](https://github.com/ciromattia/kcc/wiki/Other-devices) for a list of tested Non-Kindle E-Readers.
* The first image found will be set as the comic's cover.
* All files/directories will be added to EPUB in alphabetical order.
-* Output MOBI file should be uploaded via USB. Other methods (e.g. via Calibre) might corrupt it.
+* Output MOBI file should be uploaded via USB. Other methods might corrupt it.
### GUI
@@ -67,12 +69,10 @@ Usage: comic2ebook.py [options] comic_file|comic_folder
Options:
MAIN:
-p PROFILE, --profile=PROFILE
- Device profile (Choose one among K1, K2, K3, K4NT, K4T, KDX, KDXG, KHD, KF, KFHD, KFHD8, KFA) [Default=KHD]
+ Device profile (Choose one among K1, K2, K345, KDX, KDXG, KHD, KF, KFHD, KFHD8, KFHDX, KFHDX8, KFA) [Default=KHD]
-q QUALITY, --quality=QUALITY
Quality of Panel View. 0 - Normal 1 - High 2 - Ultra [Default=0]
-m, --manga-style Manga style (Right-to-left reading and splitting)
-
- EXPERIMENTAL:
-w, --webtoon Webtoon processing mode
OUTPUT SETTINGS:
@@ -84,7 +84,8 @@ Options:
--batchsplit Split output into multiple files
PROCESSING:
- --blackborders Use black borders instead of white ones
+ --blackborders Disable autodetection and force black borders
+ --whiteborders Disable autodetection and force white borders
--forcecolor Don't convert images to grayscale
--forcepng Create PNG files instead JPEG (For non-Kindle devices)
--gamma=GAMMA Apply gamma correction to linearize the image [Default=Auto]
@@ -129,22 +130,20 @@ This script born as a cross-platform alternative to `KindleComicParser` by **Dc5
The app relies and includes the following scripts/binaries:
- - `KindleStrip` script © 2010-2012 by **Paul Durrant** and released in public domain
-([forum thread](http://www.mobileread.com/forums/showthread.php?t=96903))
- - `rarfile.py` script © 2005-2011 **Marko Kreen** , released with ISC License
- - `image.py` class from **Alex Yatskov**'s [Mangle](http://foosoft.net/mangle/) with subsequent [proDOOMman](https://github.com/proDOOMman/Mangle)'s and [Birua](https://github.com/Birua/Mangle)'s patches
- - Icon is by **Nikolay Verin** ([http://ncrow.deviantart.com/](http://ncrow.deviantart.com/)) and released under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/) License
+ - `KindleUnpack` script by Charles **M. Hannum, P. Durrant, K. Hendricks, S. Siebert, fandrieu, DiapDealer, nickredding**. Released with GPLv3 License.
+ - `rarfile.py` script © 2005-2011 **Marko Kreen** . Released with ISC License.
+ - `image.py` class from **Alex Yatskov**'s [Mangle](http://foosoft.net/mangle/) with subsequent [proDOOMman](https://github.com/proDOOMman/Mangle)'s and [Birua](https://github.com/Birua/Mangle)'s patches.
+ - Icon is by **Nikolay Verin** ([http://ncrow.deviantart.com/](http://ncrow.deviantart.com/)) and released under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/) License.
## SAMPLE FILES CREATED BY KCC
-* [Kindle Keyboard](http://kcc.vulturis.eu/Samples/Ubunchu!-K3.mobi)
-* [Kindle DX](http://kcc.vulturis.eu/Samples/Ubunchu!-KDX.mobi)
-* [Kindle DXG](http://kcc.vulturis.eu/Samples/Ubunchu!-KDXG.mobi)
-* [Kindle Non-Touch](http://kcc.vulturis.eu/Samples/Ubunchu!-K4NT.mobi)
-* [Kindle Touch](http://kcc.vulturis.eu/Samples/Ubunchu!-K4T.mobi)
* [Kindle Paperwhite](http://kcc.vulturis.eu/Samples/Ubunchu!-KPW.mobi)
+* [Kindle](http://kcc.vulturis.eu/Samples/Ubunchu!-K345.mobi)
+* [Kindle DX/DXG](http://kcc.vulturis.eu/Samples/Ubunchu!-KDX.mobi)
* [Kindle Fire](http://kcc.vulturis.eu/Samples/Ubunchu!-KF.mobi)
* [Kindle Fire HD](http://kcc.vulturis.eu/Samples/Ubunchu!-KFHD.mobi)
* [Kindle Fire HD 8.9"](http://kcc.vulturis.eu/Samples/Ubunchu!-KFHD8.mobi)
+* [Kindle Fire HDX](http://kcc.vulturis.eu/Samples/Ubunchu!-KFHDX.mobi)
+* [Kindle Fire HDX 8.9"](http://kcc.vulturis.eu/Samples/Ubunchu!-KFHDX8.mobi)
## CHANGELOG
####1.00
@@ -255,8 +254,19 @@ The app relies and includes the following scripts/binaries:
####3.2.1:
* Hotfixed crash occurring on OS with Russian locale
-## KNOWN ISSUES
-* Removing SRCS headers sometimes fail in 32bit enviroments. Due to memory limitations.
+####3.3:
+* Margins are now automatically omitted in Panel View mode
+* Margin color fill is now autodetected
+* Created MOBI files are not longer marked as _Personal_ on newer Kindle models
+* Layout of panels in Panel View mode is now automatically adjusted to content
+* Fixed Kindle 2/DX/DXG profiles - no more blank pages
+* All Kindle Fire profiles now support hiqh quality Panel View
+* Added support of 7z/CB7 files
+* Added Kindle Fire HDX profile
+* Support for Virtual Panel View was removed
+* Profiles for Kindle Keyboard, Touch and Non-Touch are now merged
+* Windows release is now bundled with UnRAR and 7za
+* Small GUI tweaks
## COPYRIGHT
diff --git a/kcc.py b/kcc.py
index bcab374..60a0646 100644
--- a/kcc.py
+++ b/kcc.py
@@ -1,7 +1,8 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
-# Copyright (c) 2012 Ciro Mattia Gonano
+# Copyright (c) 2012-2013 Ciro Mattia Gonano
+# Copyright (c) 2013 Pawel Jastrzebski
#
# Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the
@@ -17,7 +18,7 @@
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
-__version__ = '3.2.1'
+__version__ = '3.3'
__license__ = 'ISC'
__copyright__ = '2012-2013, Ciro Mattia Gonano , Pawel Jastrzebski '
__docformat__ = 'restructuredtext en'
@@ -32,9 +33,10 @@ except ImportError:
exit(1)
from kcc import KCC_gui
from multiprocessing import freeze_support
-
-if sys.platform == 'darwin':
+if sys.platform.startswith('darwin'):
from kcc import KCC_ui_osx as KCC_ui
+elif sys.platform.startswith('linux'):
+ from kcc import KCC_ui_linux as KCC_ui
else:
from kcc import KCC_ui
diff --git a/kcc/KCC_gui.py b/kcc/KCC_gui.py
index e86e93e..4b6e022 100644
--- a/kcc/KCC_gui.py
+++ b/kcc/KCC_gui.py
@@ -1,7 +1,8 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
-# Copyright (c) 2013 Ciro Mattia Gonano
+# Copyright (c) 2012-2013 Ciro Mattia Gonano
+# Copyright (c) 2013 Pawel Jastrzebski
#
# Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the
@@ -17,7 +18,7 @@
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
-__version__ = '3.2.1'
+__version__ = '3.3'
__license__ = 'ISC'
__copyright__ = '2012-2013, Ciro Mattia Gonano , Pawel Jastrzebski '
__docformat__ = 'restructuredtext en'
@@ -29,7 +30,7 @@ import traceback
import urllib2
import time
import comic2ebook
-import kindlestrip
+import kindlesplit
from image import ProfileData
from subprocess import call, Popen, STDOUT, PIPE
from PyQt4 import QtGui, QtCore
@@ -98,6 +99,10 @@ class WorkerThread(QtCore.QThread):
def __init__(self, parent):
QtCore.QThread.__init__(self)
self.parent = parent
+ self.conversionAlive = False
+ self.errors = False
+ self.kindlegenErrorCode = 0
+ self.kindlegenError = None
def __del__(self):
self.wait()
@@ -111,7 +116,6 @@ class WorkerThread(QtCore.QThread):
self.emit(QtCore.SIGNAL("addMessage"), 'Conversion interrupted.', 'error')
self.emit(QtCore.SIGNAL("modeConvert"), True)
- # noinspection PyUnboundLocalVariable
def run(self):
self.emit(QtCore.SIGNAL("modeConvert"), False)
profile = ProfileData.ProfileLabels[str(GUI.DeviceBox.currentText())]
@@ -130,12 +134,14 @@ class WorkerThread(QtCore.QThread):
argv.append("--noprocessing")
if GUI.NoRotateBox.isChecked():
argv.append("--nosplitrotate")
- if GUI.BorderBox.isChecked():
- argv.append("--blackborders")
if GUI.UpscaleBox.checkState() == 1:
argv.append("--stretch")
elif GUI.UpscaleBox.checkState() == 2:
argv.append("--upscale")
+ if GUI.BorderBox.checkState() == 1:
+ argv.append("--whiteborders")
+ elif GUI.BorderBox.checkState() == 2:
+ argv.append("--blackborders")
if GUI.NoDitheringBox.isChecked():
argv.append("--forcepng")
if GUI.WebtoonBox.isChecked():
@@ -209,7 +215,8 @@ class WorkerThread(QtCore.QThread):
try:
self.kindlegenErrorCode = 0
if os.path.getsize(item) < 367001600:
- output = Popen('kindlegen -locale en "' + item + '"', stdout=PIPE, stderr=STDOUT, shell=True)
+ output = Popen('kindlegen -locale en "' + item + '"', stdout=PIPE, stderr=STDOUT,
+ shell=True)
for line in output.stdout:
# ERROR: Generic error
if "Error(" in line:
@@ -242,23 +249,27 @@ class WorkerThread(QtCore.QThread):
True)
else:
self.emit(QtCore.SIGNAL("addMessage"), 'Creating MOBI file... Done!', 'info', True)
- self.emit(QtCore.SIGNAL("addMessage"), 'Removing SRCS header...', 'info')
+ self.emit(QtCore.SIGNAL("addMessage"), 'Cleaning MOBI file...', 'info')
os.remove(item)
mobiPath = item.replace('.epub', '.mobi')
- shutil.move(mobiPath, mobiPath + '_tostrip')
+ shutil.move(mobiPath, mobiPath + '_toclean')
try:
- kindlestrip.main((mobiPath + '_tostrip', mobiPath))
+ if profile in ['K345', 'KHD', 'KF', 'KFHD', 'KFHD8', 'KFHDX', 'KFHDX8', 'KFA']:
+ newKindle = True
+ else:
+ newKindle = False
+ mobisplit = kindlesplit.mobi_split(mobiPath + '_toclean', newKindle)
+ open(mobiPath, 'wb').write(mobisplit.getResult())
except Exception:
self.errors = True
if not self.errors:
- os.remove(mobiPath + '_tostrip')
- self.emit(QtCore.SIGNAL("addMessage"), 'Removing SRCS header... Done!', 'info', True)
+ os.remove(mobiPath + '_toclean')
+ self.emit(QtCore.SIGNAL("addMessage"), 'Cleaning MOBI file... Done!', 'info', True)
else:
- shutil.move(mobiPath + '_tostrip', mobiPath)
+ os.remove(mobiPath + '_toclean')
+ os.remove(mobiPath)
self.emit(QtCore.SIGNAL("addMessage"),
- 'KindleStrip failed to remove SRCS header!', 'warning')
- self.emit(QtCore.SIGNAL("addMessage"),
- 'MOBI file will work correctly but it will be highly oversized.', 'warning')
+ 'KindleUnpack failed to clean MOBI file!', 'error')
else:
epubSize = (os.path.getsize(item))/1024/1024
os.remove(item)
@@ -318,11 +329,19 @@ class Ui_KCC(object):
self.needClean = False
GUI.JobList.clear()
if self.UnRAR:
- fnames = QtGui.QFileDialog.getOpenFileNames(MainWindow, 'Select file', self.lastPath,
- '*.cbz *.cbr *.zip *.rar *.pdf')
+ if self.sevenza:
+ fnames = QtGui.QFileDialog.getOpenFileNames(MainWindow, 'Select file', self.lastPath,
+ '*.cbz *.cbr *.cb7 *.zip *.rar *.7z *.pdf')
+ else:
+ fnames = QtGui.QFileDialog.getOpenFileNames(MainWindow, 'Select file', self.lastPath,
+ '*.cbz *.cbr *.zip *.rar *.pdf')
else:
- fnames = QtGui.QFileDialog.getOpenFileNames(MainWindow, 'Select file', self.lastPath,
- '*.cbz *.zip *.pdf')
+ if self.sevenza:
+ fnames = QtGui.QFileDialog.getOpenFileNames(MainWindow, 'Select file', self.lastPath,
+ '*.cbz *.cb7 *.zip *.7z *.pdf')
+ else:
+ fnames = QtGui.QFileDialog.getOpenFileNames(MainWindow, 'Select file', self.lastPath,
+ '*.cbz *.zip *.pdf')
# Lame UTF-8 security measure
for fname in fnames:
try:
@@ -438,13 +457,13 @@ class Ui_KCC(object):
GUI.NoRotateBox.setChecked(True)
GUI.QualityBox.setEnabled(False)
GUI.QualityBox.setChecked(False)
- GUI.BorderBox.setEnabled(False)
- GUI.BorderBox.setChecked(False)
+ GUI.MangaBox.setEnabled(False)
+ GUI.MangaBox.setChecked(False)
self.addMessage('If images are color setting Gamma to 1.0 is recommended.', 'info')
else:
GUI.NoRotateBox.setEnabled(True)
GUI.QualityBox.setEnabled(True)
- GUI.BorderBox.setEnabled(True)
+ GUI.MangaBox.setEnabled(True)
def toggleNoSplitRotate(self, value):
if value:
@@ -454,13 +473,13 @@ class Ui_KCC(object):
GUI.RotateBox.setEnabled(True)
def changeDevice(self, value):
- if value == 12:
+ if value == 9:
GUI.BasicModeButton.setEnabled(False)
GUI.AdvModeButton.setEnabled(False)
self.addMessage(''
'List of supported Non-Kindle devices', 'info')
self.modeExpert()
- elif value == 11:
+ elif value == 8:
GUI.BasicModeButton.setEnabled(False)
GUI.AdvModeButton.setEnabled(False)
self.modeExpert(True)
@@ -468,11 +487,17 @@ class Ui_KCC(object):
GUI.BasicModeButton.setEnabled(True)
GUI.AdvModeButton.setEnabled(True)
self.modeBasic()
- if value in [0, 1, 5, 6, 7, 8, 9, 12]:
+ if value in [9, 11, 12, 13, 14]:
GUI.QualityBox.setCheckState(0)
GUI.QualityBox.setEnabled(False)
else:
- GUI.QualityBox.setEnabled(True)
+ if not GUI.WebtoonBox.isChecked():
+ GUI.QualityBox.setEnabled(True)
+ if value in [3, 4, 5, 6, 8, 15]:
+ GUI.NoDitheringBox.setCheckState(0)
+ GUI.NoDitheringBox.setEnabled(False)
+ else:
+ GUI.NoDitheringBox.setEnabled(True)
def stripTags(self, html):
s = HTMLStripper()
@@ -544,10 +569,12 @@ class Ui_KCC(object):
event.ignore()
if not GUI.ConvertButton.isEnabled():
event.ignore()
+ self.settings.setValue('settingsVersion', __version__)
self.settings.setValue('lastPath', self.lastPath)
self.settings.setValue('lastDevice', GUI.DeviceBox.currentIndex())
self.settings.setValue('currentFormat', GUI.FormatBox.currentIndex())
self.settings.setValue('currentMode', self.currentMode)
+ self.settings.setValue('firstStart', False)
self.settings.setValue('options', QtCore.QVariant({'MangaBox': GUI.MangaBox.checkState(),
'RotateBox': GUI.RotateBox.checkState(),
'QualityBox': GUI.QualityBox.checkState(),
@@ -567,22 +594,33 @@ class Ui_KCC(object):
global GUI, MainWindow
GUI = UI
MainWindow = KCC
- profiles = sorted(ProfileData.ProfileLabels.iterkeys())
+ # User settings will be reverted to default ones if were created in one of the following versions
+ # Empty string cover all versions before this system was implemented
+ purgeSettingsVersions = ['']
self.icons = Icons()
self.settings = QtCore.QSettings('KindleComicConverter', 'KindleComicConverter')
+ self.settingsVersion = self.settings.value('settingsVersion', '', type=str)
+ if self.settingsVersion in purgeSettingsVersions:
+ QtCore.QSettings.clear(self.settings)
+ self.settingsVersion = self.settings.value('settingsVersion', '', type=str)
self.lastPath = self.settings.value('lastPath', '', type=str)
- self.lastDevice = self.settings.value('lastDevice', 10, type=int)
+ self.lastDevice = self.settings.value('lastDevice', 0, type=int)
self.currentMode = self.settings.value('currentMode', 1, type=int)
self.currentFormat = self.settings.value('currentFormat', 0, type=int)
+ self.firstStart = self.settings.value('firstStart', True, type=bool)
self.options = self.settings.value('options', QtCore.QVariant({'GammaSlider': 0}))
self.options = self.options.toPyObject()
self.worker = WorkerThread(self)
self.versionCheck = VersionThread(self)
self.conversionAlive = False
self.needClean = True
+ self.GammaValue = 1.0
self.addMessage('Welcome!', 'info')
self.addMessage('Remember: All options have additional informations in tooltips.', 'info')
+ if self.firstStart:
+ self.addMessage('Since you are using KCC for first time please see few '
+ 'important tips.', 'info')
if call('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, shell=True) == 0:
self.KindleGen = True
formats = ['MOBI', 'EPUB', 'CBZ']
@@ -608,6 +646,13 @@ class Ui_KCC(object):
self.UnRAR = False
self.addMessage('Cannot find UnRAR!'
' Processing of CBR/RAR files will be disabled.', 'warning')
+ sevenzaExitCode = call('7za', stdout=PIPE, stderr=STDOUT, shell=True)
+ if sevenzaExitCode == 0 or sevenzaExitCode == 7:
+ self.sevenza = True
+ else:
+ self.sevenza = False
+ self.addMessage('Cannot find 7za!'
+ ' Processing of CB7/7Z files will be disabled.', 'warning')
GUI.BasicModeButton.clicked.connect(self.modeBasic)
GUI.AdvModeButton.clicked.connect(self.modeAdvanced)
@@ -627,12 +672,18 @@ class Ui_KCC(object):
KCC.connect(self.versionCheck, QtCore.SIGNAL("addMessage"), self.addMessage)
KCC.closeEvent = self.saveSettings
- for profile in profiles:
- if profile != "Other":
- GUI.DeviceBox.addItem(self.icons.deviceKindle, profile)
- else:
+ for profile in ProfileData.ProfileLabelsGUI:
+ if profile == "Other":
GUI.DeviceBox.addItem(self.icons.deviceOther, profile)
- GUI.DeviceBox.setCurrentIndex(self.lastDevice)
+ elif profile == "Separator":
+ GUI.DeviceBox.insertSeparator(GUI.DeviceBox.count()+1)
+ else:
+ GUI.DeviceBox.addItem(self.icons.deviceKindle, profile)
+ if self.lastDevice > GUI.DeviceBox.count():
+ GUI.DeviceBox.setCurrentIndex(0)
+ self.lastDevice = 0
+ else:
+ GUI.DeviceBox.setCurrentIndex(self.lastDevice)
for f in formats:
GUI.FormatBox.addItem(eval('self.icons.' + f + 'Format'), f)
@@ -650,8 +701,6 @@ class Ui_KCC(object):
elif str(option) == "GammaSlider":
GUI.GammaSlider.setValue(int(self.options[option]))
self.changeGamma(int(self.options[option]))
- elif str(option) == "StretchBox" or str(option) == "WebstripBox":
- pass
else:
eval('GUI.' + str(option)).setCheckState(self.options[option])
if self.currentMode == 1:
diff --git a/kcc/KCC_ui.py b/kcc/KCC_ui.py
index 757b2a8..a997ead 100644
--- a/kcc/KCC_ui.py
+++ b/kcc/KCC_ui.py
@@ -2,8 +2,8 @@
# Form implementation generated from reading ui file 'KCC.ui'
#
-# Created: Wed Aug 14 08:39:46 2013
-# by: PyQt4 UI code generator 4.10.2
+# Created: Wed Sep 18 12:12:45 2013
+# by: PyQt4 UI code generator 4.10.3
#
# WARNING! All changes made in this file will be lost!
@@ -68,6 +68,7 @@ class Ui_KCC(object):
self.gridLayout.addWidget(self.NoDitheringBox, 3, 2, 1, 1)
self.BorderBox = QtGui.QCheckBox(self.OptionsAdvanced)
self.BorderBox.setFocusPolicy(QtCore.Qt.NoFocus)
+ self.BorderBox.setTristate(True)
self.BorderBox.setObjectName(_fromUtf8("BorderBox"))
self.gridLayout.addWidget(self.BorderBox, 3, 0, 1, 1)
self.NoRotateBox = QtGui.QCheckBox(self.OptionsAdvanced)
@@ -266,12 +267,12 @@ class Ui_KCC(object):
self.ProcessingBox.setText(_translate("KCC", "No optimisation", None))
self.UpscaleBox.setToolTip(_translate("KCC", "Unchecked - Nothing
Images smaller than device resolution will not be resized.
Indeterminate - Stretching
Images smaller than device resolution will be resized. Aspect ratio will be not preserved.
Checked - Upscaling
Images smaller than device resolution will be resized. Aspect ratio will be preserved.
", None))
self.UpscaleBox.setText(_translate("KCC", "Stretch/Upscale", None))
- self.WebtoonBox.setToolTip(_translate("KCC", "EXPERIMENTAL!
Enable auto-splitting of webtoons like Tower of God or Noblesse.
Pages with a low width, high height and vertical panel flow.
", None))
+ self.WebtoonBox.setToolTip(_translate("KCC", "Enable auto-splitting of webtoons like Tower of God or Noblesse.
Pages with a low width, high height and vertical panel flow.
", None))
self.WebtoonBox.setText(_translate("KCC", "Webtoon mode", None))
- self.NoDitheringBox.setToolTip(_translate("KCC", "Create PNG files instead JPEG.
Only for non-Kindle devices!
", None))
+ self.NoDitheringBox.setToolTip(_translate("KCC", "Create PNG files instead JPEG.
", None))
self.NoDitheringBox.setText(_translate("KCC", "PNG output", None))
- self.BorderBox.setToolTip(_translate("KCC", "Fill space around images with black color.", None))
- self.BorderBox.setText(_translate("KCC", "Black borders", None))
+ self.BorderBox.setToolTip(_translate("KCC", "Unchecked - Autodetection
Color of margins fill will be detected automatically.
Indeterminate - White
Margins will be filled with white color.
Checked - Black
Margins will be filled with black color.
", None))
+ self.BorderBox.setText(_translate("KCC", "W/B margins", None))
self.NoRotateBox.setToolTip(_translate("KCC", "Disable splitting and rotation.
", None))
self.NoRotateBox.setText(_translate("KCC", "No split/rotate", None))
self.DeviceBox.setToolTip(_translate("KCC", "Target device.", None))
diff --git a/kcc/KCC_ui_linux.py b/kcc/KCC_ui_linux.py
new file mode 100644
index 0000000..416bb16
--- /dev/null
+++ b/kcc/KCC_ui_linux.py
@@ -0,0 +1,383 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'KCC-Linux.ui'
+#
+# Created: Fri Sep 20 10:25:30 2013
+# by: PyQt4 UI code generator 4.10
+#
+# WARNING! All changes made in this file will be lost!
+
+from PyQt4 import QtCore, QtGui
+
+try:
+ _fromUtf8 = QtCore.QString.fromUtf8
+except AttributeError:
+ def _fromUtf8(s):
+ return s
+
+try:
+ _encoding = QtGui.QApplication.UnicodeUTF8
+ def _translate(context, text, disambig):
+ return QtGui.QApplication.translate(context, text, disambig, _encoding)
+except AttributeError:
+ def _translate(context, text, disambig):
+ return QtGui.QApplication.translate(context, text, disambig)
+
+class Ui_KCC(object):
+ def setupUi(self, KCC):
+ KCC.setObjectName(_fromUtf8("KCC"))
+ KCC.resize(420, 380)
+ KCC.setMinimumSize(QtCore.QSize(420, 380))
+ KCC.setMaximumSize(QtCore.QSize(420, 380))
+ font = QtGui.QFont()
+ font.setPointSize(9)
+ KCC.setFont(font)
+ KCC.setFocusPolicy(QtCore.Qt.NoFocus)
+ icon = QtGui.QIcon()
+ icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/Icon/icons/comic2ebook.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ KCC.setWindowIcon(icon)
+ KCC.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry))
+ self.Form = QtGui.QWidget(KCC)
+ self.Form.setObjectName(_fromUtf8("Form"))
+ self.OptionsAdvanced = QtGui.QFrame(self.Form)
+ self.OptionsAdvanced.setEnabled(True)
+ self.OptionsAdvanced.setGeometry(QtCore.QRect(1, 254, 421, 61))
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ font.setPointSize(9)
+ self.OptionsAdvanced.setFont(font)
+ self.OptionsAdvanced.setObjectName(_fromUtf8("OptionsAdvanced"))
+ self.gridLayout = QtGui.QGridLayout(self.OptionsAdvanced)
+ self.gridLayout.setContentsMargins(9, -1, -1, -1)
+ self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
+ self.ProcessingBox = QtGui.QCheckBox(self.OptionsAdvanced)
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ self.ProcessingBox.setFont(font)
+ self.ProcessingBox.setFocusPolicy(QtCore.Qt.NoFocus)
+ self.ProcessingBox.setObjectName(_fromUtf8("ProcessingBox"))
+ self.gridLayout.addWidget(self.ProcessingBox, 1, 0, 1, 1)
+ self.UpscaleBox = QtGui.QCheckBox(self.OptionsAdvanced)
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ self.UpscaleBox.setFont(font)
+ self.UpscaleBox.setFocusPolicy(QtCore.Qt.NoFocus)
+ self.UpscaleBox.setTristate(True)
+ self.UpscaleBox.setObjectName(_fromUtf8("UpscaleBox"))
+ self.gridLayout.addWidget(self.UpscaleBox, 1, 1, 1, 1)
+ self.WebtoonBox = QtGui.QCheckBox(self.OptionsAdvanced)
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ self.WebtoonBox.setFont(font)
+ self.WebtoonBox.setFocusPolicy(QtCore.Qt.NoFocus)
+ self.WebtoonBox.setObjectName(_fromUtf8("WebtoonBox"))
+ self.gridLayout.addWidget(self.WebtoonBox, 3, 1, 1, 1)
+ self.NoDitheringBox = QtGui.QCheckBox(self.OptionsAdvanced)
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ self.NoDitheringBox.setFont(font)
+ self.NoDitheringBox.setFocusPolicy(QtCore.Qt.NoFocus)
+ self.NoDitheringBox.setObjectName(_fromUtf8("NoDitheringBox"))
+ self.gridLayout.addWidget(self.NoDitheringBox, 3, 2, 1, 1)
+ self.BorderBox = QtGui.QCheckBox(self.OptionsAdvanced)
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ self.BorderBox.setFont(font)
+ self.BorderBox.setFocusPolicy(QtCore.Qt.NoFocus)
+ self.BorderBox.setTristate(True)
+ self.BorderBox.setObjectName(_fromUtf8("BorderBox"))
+ self.gridLayout.addWidget(self.BorderBox, 3, 0, 1, 1)
+ self.NoRotateBox = QtGui.QCheckBox(self.OptionsAdvanced)
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ self.NoRotateBox.setFont(font)
+ self.NoRotateBox.setFocusPolicy(QtCore.Qt.NoFocus)
+ self.NoRotateBox.setObjectName(_fromUtf8("NoRotateBox"))
+ self.gridLayout.addWidget(self.NoRotateBox, 1, 2, 1, 1)
+ self.DeviceBox = QtGui.QComboBox(self.Form)
+ self.DeviceBox.setGeometry(QtCore.QRect(10, 200, 141, 31))
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ font.setPointSize(8)
+ self.DeviceBox.setFont(font)
+ self.DeviceBox.setFocusPolicy(QtCore.Qt.NoFocus)
+ self.DeviceBox.setObjectName(_fromUtf8("DeviceBox"))
+ self.FormatBox = QtGui.QComboBox(self.Form)
+ self.FormatBox.setGeometry(QtCore.QRect(260, 200, 151, 31))
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ font.setPointSize(8)
+ self.FormatBox.setFont(font)
+ self.FormatBox.setFocusPolicy(QtCore.Qt.NoFocus)
+ self.FormatBox.setObjectName(_fromUtf8("FormatBox"))
+ self.ConvertButton = QtGui.QPushButton(self.Form)
+ self.ConvertButton.setGeometry(QtCore.QRect(160, 200, 91, 32))
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ font.setPointSize(9)
+ font.setBold(True)
+ font.setWeight(75)
+ self.ConvertButton.setFont(font)
+ self.ConvertButton.setFocusPolicy(QtCore.Qt.NoFocus)
+ icon1 = QtGui.QIcon()
+ icon1.addPixmap(QtGui.QPixmap(_fromUtf8(":/Other/icons/convert.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ self.ConvertButton.setIcon(icon1)
+ self.ConvertButton.setObjectName(_fromUtf8("ConvertButton"))
+ self.DirectoryButton = QtGui.QPushButton(self.Form)
+ self.DirectoryButton.setGeometry(QtCore.QRect(10, 160, 141, 32))
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ font.setPointSize(8)
+ self.DirectoryButton.setFont(font)
+ self.DirectoryButton.setFocusPolicy(QtCore.Qt.NoFocus)
+ icon2 = QtGui.QIcon()
+ icon2.addPixmap(QtGui.QPixmap(_fromUtf8(":/Other/icons/folder_new.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ self.DirectoryButton.setIcon(icon2)
+ self.DirectoryButton.setObjectName(_fromUtf8("DirectoryButton"))
+ self.FileButton = QtGui.QPushButton(self.Form)
+ self.FileButton.setGeometry(QtCore.QRect(260, 160, 151, 32))
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ font.setPointSize(8)
+ self.FileButton.setFont(font)
+ self.FileButton.setFocusPolicy(QtCore.Qt.NoFocus)
+ icon3 = QtGui.QIcon()
+ icon3.addPixmap(QtGui.QPixmap(_fromUtf8(":/Other/icons/document_new.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ self.FileButton.setIcon(icon3)
+ self.FileButton.setObjectName(_fromUtf8("FileButton"))
+ self.ClearButton = QtGui.QPushButton(self.Form)
+ self.ClearButton.setGeometry(QtCore.QRect(160, 160, 91, 32))
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ font.setPointSize(8)
+ self.ClearButton.setFont(font)
+ self.ClearButton.setFocusPolicy(QtCore.Qt.NoFocus)
+ icon4 = QtGui.QIcon()
+ icon4.addPixmap(QtGui.QPixmap(_fromUtf8(":/Other/icons/clear.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ self.ClearButton.setIcon(icon4)
+ self.ClearButton.setObjectName(_fromUtf8("ClearButton"))
+ self.OptionsBasic = QtGui.QFrame(self.Form)
+ self.OptionsBasic.setGeometry(QtCore.QRect(1, 230, 421, 41))
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ font.setPointSize(9)
+ self.OptionsBasic.setFont(font)
+ self.OptionsBasic.setObjectName(_fromUtf8("OptionsBasic"))
+ self.MangaBox = QtGui.QCheckBox(self.OptionsBasic)
+ self.MangaBox.setGeometry(QtCore.QRect(9, 10, 130, 18))
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ self.MangaBox.setFont(font)
+ self.MangaBox.setFocusPolicy(QtCore.Qt.NoFocus)
+ self.MangaBox.setObjectName(_fromUtf8("MangaBox"))
+ self.QualityBox = QtGui.QCheckBox(self.OptionsBasic)
+ self.QualityBox.setGeometry(QtCore.QRect(282, 10, 135, 18))
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ self.QualityBox.setFont(font)
+ self.QualityBox.setFocusPolicy(QtCore.Qt.NoFocus)
+ self.QualityBox.setTristate(True)
+ self.QualityBox.setObjectName(_fromUtf8("QualityBox"))
+ self.RotateBox = QtGui.QCheckBox(self.OptionsBasic)
+ self.RotateBox.setGeometry(QtCore.QRect(145, 10, 130, 18))
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ self.RotateBox.setFont(font)
+ self.RotateBox.setFocusPolicy(QtCore.Qt.NoFocus)
+ self.RotateBox.setObjectName(_fromUtf8("RotateBox"))
+ self.JobList = QtGui.QListWidget(self.Form)
+ self.JobList.setGeometry(QtCore.QRect(10, 50, 401, 101))
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ font.setPointSize(8)
+ font.setItalic(False)
+ self.JobList.setFont(font)
+ self.JobList.setFocusPolicy(QtCore.Qt.NoFocus)
+ self.JobList.setProperty("showDropIndicator", False)
+ self.JobList.setSelectionMode(QtGui.QAbstractItemView.NoSelection)
+ self.JobList.setIconSize(QtCore.QSize(18, 18))
+ self.JobList.setObjectName(_fromUtf8("JobList"))
+ self.BasicModeButton = QtGui.QPushButton(self.Form)
+ self.BasicModeButton.setGeometry(QtCore.QRect(10, 10, 195, 32))
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ font.setPointSize(9)
+ self.BasicModeButton.setFont(font)
+ self.BasicModeButton.setFocusPolicy(QtCore.Qt.NoFocus)
+ self.BasicModeButton.setObjectName(_fromUtf8("BasicModeButton"))
+ self.AdvModeButton = QtGui.QPushButton(self.Form)
+ self.AdvModeButton.setGeometry(QtCore.QRect(217, 10, 195, 32))
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ font.setPointSize(9)
+ self.AdvModeButton.setFont(font)
+ self.AdvModeButton.setFocusPolicy(QtCore.Qt.NoFocus)
+ self.AdvModeButton.setObjectName(_fromUtf8("AdvModeButton"))
+ self.OptionsAdvancedGamma = QtGui.QFrame(self.Form)
+ self.OptionsAdvancedGamma.setEnabled(True)
+ self.OptionsAdvancedGamma.setGeometry(QtCore.QRect(10, 305, 401, 41))
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ font.setPointSize(9)
+ self.OptionsAdvancedGamma.setFont(font)
+ self.OptionsAdvancedGamma.setObjectName(_fromUtf8("OptionsAdvancedGamma"))
+ self.GammaLabel = QtGui.QLabel(self.OptionsAdvancedGamma)
+ self.GammaLabel.setGeometry(QtCore.QRect(15, 0, 100, 40))
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ self.GammaLabel.setFont(font)
+ self.GammaLabel.setObjectName(_fromUtf8("GammaLabel"))
+ self.GammaSlider = QtGui.QSlider(self.OptionsAdvancedGamma)
+ self.GammaSlider.setGeometry(QtCore.QRect(110, 10, 275, 22))
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ self.GammaSlider.setFont(font)
+ self.GammaSlider.setFocusPolicy(QtCore.Qt.ClickFocus)
+ self.GammaSlider.setMaximum(500)
+ self.GammaSlider.setSingleStep(5)
+ self.GammaSlider.setOrientation(QtCore.Qt.Horizontal)
+ self.GammaSlider.setObjectName(_fromUtf8("GammaSlider"))
+ self.ProgressBar = QtGui.QProgressBar(self.Form)
+ self.ProgressBar.setGeometry(QtCore.QRect(10, 10, 401, 31))
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ font.setPointSize(10)
+ font.setBold(True)
+ font.setWeight(75)
+ self.ProgressBar.setFont(font)
+ self.ProgressBar.setProperty("value", 0)
+ self.ProgressBar.setAlignment(QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter)
+ self.ProgressBar.setFormat(_fromUtf8(""))
+ self.ProgressBar.setObjectName(_fromUtf8("ProgressBar"))
+ self.OptionsExpert = QtGui.QFrame(self.Form)
+ self.OptionsExpert.setGeometry(QtCore.QRect(1, 337, 421, 41))
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ font.setPointSize(9)
+ self.OptionsExpert.setFont(font)
+ self.OptionsExpert.setObjectName(_fromUtf8("OptionsExpert"))
+ self.ColorBox = QtGui.QCheckBox(self.OptionsExpert)
+ self.ColorBox.setGeometry(QtCore.QRect(9, 11, 130, 18))
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ self.ColorBox.setFont(font)
+ self.ColorBox.setFocusPolicy(QtCore.Qt.NoFocus)
+ self.ColorBox.setObjectName(_fromUtf8("ColorBox"))
+ self.OptionsExpertInternal = QtGui.QFrame(self.OptionsExpert)
+ self.OptionsExpertInternal.setGeometry(QtCore.QRect(105, 0, 295, 40))
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ self.OptionsExpertInternal.setFont(font)
+ self.OptionsExpertInternal.setObjectName(_fromUtf8("OptionsExpertInternal"))
+ self.gridLayout_2 = QtGui.QGridLayout(self.OptionsExpertInternal)
+ self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2"))
+ self.wLabel = QtGui.QLabel(self.OptionsExpertInternal)
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ self.wLabel.setFont(font)
+ self.wLabel.setObjectName(_fromUtf8("wLabel"))
+ self.gridLayout_2.addWidget(self.wLabel, 0, 0, 1, 1)
+ self.customWidth = QtGui.QLineEdit(self.OptionsExpertInternal)
+ sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.customWidth.sizePolicy().hasHeightForWidth())
+ self.customWidth.setSizePolicy(sizePolicy)
+ self.customWidth.setMaximumSize(QtCore.QSize(40, 16777215))
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ self.customWidth.setFont(font)
+ self.customWidth.setFocusPolicy(QtCore.Qt.ClickFocus)
+ self.customWidth.setAcceptDrops(False)
+ self.customWidth.setMaxLength(4)
+ self.customWidth.setObjectName(_fromUtf8("customWidth"))
+ self.gridLayout_2.addWidget(self.customWidth, 0, 1, 1, 1)
+ self.hLabel = QtGui.QLabel(self.OptionsExpertInternal)
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ self.hLabel.setFont(font)
+ self.hLabel.setObjectName(_fromUtf8("hLabel"))
+ self.gridLayout_2.addWidget(self.hLabel, 0, 2, 1, 1)
+ self.customHeight = QtGui.QLineEdit(self.OptionsExpertInternal)
+ sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.customHeight.sizePolicy().hasHeightForWidth())
+ self.customHeight.setSizePolicy(sizePolicy)
+ self.customHeight.setMaximumSize(QtCore.QSize(40, 16777215))
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("DejaVu Sans"))
+ self.customHeight.setFont(font)
+ self.customHeight.setFocusPolicy(QtCore.Qt.ClickFocus)
+ self.customHeight.setAcceptDrops(False)
+ self.customHeight.setMaxLength(4)
+ self.customHeight.setObjectName(_fromUtf8("customHeight"))
+ self.gridLayout_2.addWidget(self.customHeight, 0, 3, 1, 1)
+ KCC.setCentralWidget(self.Form)
+ self.ActionBasic = QtGui.QAction(KCC)
+ self.ActionBasic.setCheckable(True)
+ self.ActionBasic.setChecked(False)
+ font = QtGui.QFont()
+ self.ActionBasic.setFont(font)
+ self.ActionBasic.setObjectName(_fromUtf8("ActionBasic"))
+ self.ActionAdvanced = QtGui.QAction(KCC)
+ self.ActionAdvanced.setCheckable(True)
+ self.ActionAdvanced.setObjectName(_fromUtf8("ActionAdvanced"))
+
+ self.retranslateUi(KCC)
+ QtCore.QMetaObject.connectSlotsByName(KCC)
+ KCC.setTabOrder(self.DirectoryButton, self.FileButton)
+ KCC.setTabOrder(self.FileButton, self.ConvertButton)
+ KCC.setTabOrder(self.ConvertButton, self.ClearButton)
+
+ def retranslateUi(self, KCC):
+ KCC.setWindowTitle(_translate("KCC", "Kindle Comic Converter", None))
+ self.ProcessingBox.setToolTip(_translate("KCC", "Disable image optimizations.", None))
+ self.ProcessingBox.setText(_translate("KCC", "No optimisation", None))
+ self.UpscaleBox.setToolTip(_translate("KCC", "Unchecked - Nothing
Images smaller than device resolution will not be resized.
Indeterminate - Stretching
Images smaller than device resolution will be resized. Aspect ratio will be not preserved.
Checked - Upscaling
Images smaller than device resolution will be resized. Aspect ratio will be preserved.
", None))
+ self.UpscaleBox.setText(_translate("KCC", "Stretch/Upscale", None))
+ self.WebtoonBox.setToolTip(_translate("KCC", "Enable auto-splitting of webtoons like Tower of God or Noblesse.
Pages with a low width, high height and vertical panel flow.
", None))
+ self.WebtoonBox.setText(_translate("KCC", "Webtoon mode", None))
+ self.NoDitheringBox.setToolTip(_translate("KCC", "Create PNG files instead JPEG.
", None))
+ self.NoDitheringBox.setText(_translate("KCC", "PNG output", None))
+ self.BorderBox.setToolTip(_translate("KCC", "Unchecked - Autodetection
Color of margins fill will be detected automatically.
Indeterminate - White
Margins will be filled with white color.
Checked - Black
Margins will be filled with black color.
", None))
+ self.BorderBox.setText(_translate("KCC", "W/B margins", None))
+ self.NoRotateBox.setToolTip(_translate("KCC", "Disable splitting and rotation.
", None))
+ self.NoRotateBox.setText(_translate("KCC", "No split/rotate", None))
+ self.DeviceBox.setToolTip(_translate("KCC", "Target device.", None))
+ self.FormatBox.setToolTip(_translate("KCC", "Output format.", None))
+ self.ConvertButton.setText(_translate("KCC", "Convert", None))
+ self.DirectoryButton.setText(_translate("KCC", "Add directory", None))
+ self.FileButton.setText(_translate("KCC", "Add file", None))
+ self.ClearButton.setText(_translate("KCC", "Clear list", None))
+ self.MangaBox.setToolTip(_translate("KCC", "Enable right-to-left reading.", None))
+ self.MangaBox.setText(_translate("KCC", "Manga mode", None))
+ self.QualityBox.setToolTip(_translate("KCC", "\n"
+"\n"
+"Unchecked - Normal quality mode
Use it when Panel View support is not needed.
- Maximum quality when zoom is not enabled.
- Poor quality when zoom is enabled.
- Lowest file size.
\n"
+"Indeterminate - High quality mode
Not zoomed image might be a little blurry.
- Medium/High quality when zoom is not enabled.
- Maximum quality when zoom is enabled.
\n"
+"Checked - Ultra quality mode
Maximum possible quality.
- Maximum quality when zoom is not enabled.
- Maximum quality when zoom is enabled.
- Very high file size.
", None))
+ self.QualityBox.setText(_translate("KCC", "High/Ultra quality", None))
+ self.RotateBox.setToolTip(_translate("KCC", "Disable page spliting.
They will be rotated instead.
", None))
+ self.RotateBox.setText(_translate("KCC", "Horizontal mode", None))
+ self.BasicModeButton.setText(_translate("KCC", "Basic", None))
+ self.AdvModeButton.setText(_translate("KCC", "Advanced", None))
+ self.GammaLabel.setToolTip(_translate("KCC", "
When converting color images setting this option to 1.0 might improve readability.
", None))
+ self.GammaLabel.setText(_translate("KCC", "Gamma: Auto", None))
+ self.GammaSlider.setToolTip(_translate("KCC", "When converting color images setting this option to 1.0 might improve readability.
", None))
+ self.ColorBox.setToolTip(_translate("KCC", "Do not convert images to grayscale.", None))
+ self.ColorBox.setText(_translate("KCC", "Color mode", None))
+ self.wLabel.setToolTip(_translate("KCC", "Resolution of target device.", None))
+ self.wLabel.setText(_translate("KCC", "Custom width: ", None))
+ self.customWidth.setToolTip(_translate("KCC", "Resolution of target device.", None))
+ self.customWidth.setInputMask(_translate("KCC", "0000; ", None))
+ self.hLabel.setToolTip(_translate("KCC", "Resolution of target device.", None))
+ self.hLabel.setText(_translate("KCC", "Custom height: ", None))
+ self.customHeight.setToolTip(_translate("KCC", "Resolution of target device.", None))
+ self.customHeight.setInputMask(_translate("KCC", "0000; ", None))
+ self.ActionBasic.setText(_translate("KCC", "Basic", None))
+ self.ActionAdvanced.setText(_translate("KCC", "Advanced", None))
+
+import KCC_rc
diff --git a/kcc/KCC_ui_osx.py b/kcc/KCC_ui_osx.py
index 997a35b..5813d40 100644
--- a/kcc/KCC_ui_osx.py
+++ b/kcc/KCC_ui_osx.py
@@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'KCC-OSX.ui'
#
-# Created: Wed Aug 14 08:39:45 2013
+# Created: Fri Sep 20 10:57:31 2013
# by: PyQt4 UI code generator 4.10.2
#
# WARNING! All changes made in this file will be lost!
@@ -41,8 +41,9 @@ class Ui_KCC(object):
self.Form.setObjectName(_fromUtf8("Form"))
self.OptionsAdvanced = QtGui.QFrame(self.Form)
self.OptionsAdvanced.setEnabled(True)
- self.OptionsAdvanced.setGeometry(QtCore.QRect(9, 253, 421, 61))
+ self.OptionsAdvanced.setGeometry(QtCore.QRect(4, 253, 421, 61))
font = QtGui.QFont()
+ font.setFamily(_fromUtf8("Lucida Grande"))
font.setPointSize(9)
self.OptionsAdvanced.setFont(font)
self.OptionsAdvanced.setObjectName(_fromUtf8("OptionsAdvanced"))
@@ -50,14 +51,16 @@ class Ui_KCC(object):
self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
self.ProcessingBox = QtGui.QCheckBox(self.OptionsAdvanced)
font = QtGui.QFont()
- font.setPointSize(11)
+ font.setFamily(_fromUtf8("Lucida Grande"))
+ font.setPointSize(12)
self.ProcessingBox.setFont(font)
self.ProcessingBox.setFocusPolicy(QtCore.Qt.NoFocus)
self.ProcessingBox.setObjectName(_fromUtf8("ProcessingBox"))
self.gridLayout.addWidget(self.ProcessingBox, 1, 0, 1, 1)
self.UpscaleBox = QtGui.QCheckBox(self.OptionsAdvanced)
font = QtGui.QFont()
- font.setPointSize(11)
+ font.setFamily(_fromUtf8("Lucida Grande"))
+ font.setPointSize(12)
self.UpscaleBox.setFont(font)
self.UpscaleBox.setFocusPolicy(QtCore.Qt.NoFocus)
self.UpscaleBox.setTristate(True)
@@ -65,28 +68,33 @@ class Ui_KCC(object):
self.gridLayout.addWidget(self.UpscaleBox, 1, 1, 1, 1)
self.WebtoonBox = QtGui.QCheckBox(self.OptionsAdvanced)
font = QtGui.QFont()
- font.setPointSize(11)
+ font.setFamily(_fromUtf8("Lucida Grande"))
+ font.setPointSize(12)
self.WebtoonBox.setFont(font)
self.WebtoonBox.setFocusPolicy(QtCore.Qt.NoFocus)
self.WebtoonBox.setObjectName(_fromUtf8("WebtoonBox"))
self.gridLayout.addWidget(self.WebtoonBox, 3, 1, 1, 1)
self.NoDitheringBox = QtGui.QCheckBox(self.OptionsAdvanced)
font = QtGui.QFont()
- font.setPointSize(11)
+ font.setFamily(_fromUtf8("Lucida Grande"))
+ font.setPointSize(12)
self.NoDitheringBox.setFont(font)
self.NoDitheringBox.setFocusPolicy(QtCore.Qt.NoFocus)
self.NoDitheringBox.setObjectName(_fromUtf8("NoDitheringBox"))
self.gridLayout.addWidget(self.NoDitheringBox, 3, 2, 1, 1)
self.BorderBox = QtGui.QCheckBox(self.OptionsAdvanced)
font = QtGui.QFont()
- font.setPointSize(11)
+ font.setFamily(_fromUtf8("Lucida Grande"))
+ font.setPointSize(12)
self.BorderBox.setFont(font)
self.BorderBox.setFocusPolicy(QtCore.Qt.NoFocus)
+ self.BorderBox.setTristate(True)
self.BorderBox.setObjectName(_fromUtf8("BorderBox"))
self.gridLayout.addWidget(self.BorderBox, 3, 0, 1, 1)
self.NoRotateBox = QtGui.QCheckBox(self.OptionsAdvanced)
font = QtGui.QFont()
- font.setPointSize(11)
+ font.setFamily(_fromUtf8("Lucida Grande"))
+ font.setPointSize(12)
self.NoRotateBox.setFont(font)
self.NoRotateBox.setFocusPolicy(QtCore.Qt.NoFocus)
self.NoRotateBox.setObjectName(_fromUtf8("NoRotateBox"))
@@ -94,6 +102,7 @@ class Ui_KCC(object):
self.DeviceBox = QtGui.QComboBox(self.Form)
self.DeviceBox.setGeometry(QtCore.QRect(8, 200, 151, 34))
font = QtGui.QFont()
+ font.setFamily(_fromUtf8("Lucida Grande"))
font.setPointSize(11)
self.DeviceBox.setFont(font)
self.DeviceBox.setFocusPolicy(QtCore.Qt.NoFocus)
@@ -101,6 +110,7 @@ class Ui_KCC(object):
self.FormatBox = QtGui.QComboBox(self.Form)
self.FormatBox.setGeometry(QtCore.QRect(262, 200, 152, 34))
font = QtGui.QFont()
+ font.setFamily(_fromUtf8("Lucida Grande"))
font.setPointSize(11)
self.FormatBox.setFont(font)
self.FormatBox.setFocusPolicy(QtCore.Qt.NoFocus)
@@ -108,6 +118,7 @@ class Ui_KCC(object):
self.ConvertButton = QtGui.QPushButton(self.Form)
self.ConvertButton.setGeometry(QtCore.QRect(160, 200, 101, 41))
font = QtGui.QFont()
+ font.setFamily(_fromUtf8("Lucida Grande"))
font.setPointSize(11)
font.setBold(True)
font.setWeight(75)
@@ -120,6 +131,7 @@ class Ui_KCC(object):
self.DirectoryButton = QtGui.QPushButton(self.Form)
self.DirectoryButton.setGeometry(QtCore.QRect(5, 160, 156, 41))
font = QtGui.QFont()
+ font.setFamily(_fromUtf8("Lucida Grande"))
font.setPointSize(11)
self.DirectoryButton.setFont(font)
self.DirectoryButton.setFocusPolicy(QtCore.Qt.NoFocus)
@@ -130,6 +142,7 @@ class Ui_KCC(object):
self.FileButton = QtGui.QPushButton(self.Form)
self.FileButton.setGeometry(QtCore.QRect(260, 160, 157, 41))
font = QtGui.QFont()
+ font.setFamily(_fromUtf8("Lucida Grande"))
font.setPointSize(11)
self.FileButton.setFont(font)
self.FileButton.setFocusPolicy(QtCore.Qt.NoFocus)
@@ -140,6 +153,7 @@ class Ui_KCC(object):
self.ClearButton = QtGui.QPushButton(self.Form)
self.ClearButton.setGeometry(QtCore.QRect(160, 160, 101, 41))
font = QtGui.QFont()
+ font.setFamily(_fromUtf8("Lucida Grande"))
font.setPointSize(11)
self.ClearButton.setFont(font)
self.ClearButton.setFocusPolicy(QtCore.Qt.NoFocus)
@@ -148,22 +162,25 @@ class Ui_KCC(object):
self.ClearButton.setIcon(icon4)
self.ClearButton.setObjectName(_fromUtf8("ClearButton"))
self.OptionsBasic = QtGui.QFrame(self.Form)
- self.OptionsBasic.setGeometry(QtCore.QRect(10, 233, 421, 41))
+ self.OptionsBasic.setGeometry(QtCore.QRect(5, 233, 421, 41))
font = QtGui.QFont()
- font.setPointSize(9)
+ font.setFamily(_fromUtf8("Lucida Grande"))
+ font.setPointSize(12)
self.OptionsBasic.setFont(font)
self.OptionsBasic.setObjectName(_fromUtf8("OptionsBasic"))
self.MangaBox = QtGui.QCheckBox(self.OptionsBasic)
self.MangaBox.setGeometry(QtCore.QRect(9, 10, 130, 18))
font = QtGui.QFont()
- font.setPointSize(11)
+ font.setFamily(_fromUtf8("Lucida Grande"))
+ font.setPointSize(12)
self.MangaBox.setFont(font)
self.MangaBox.setFocusPolicy(QtCore.Qt.NoFocus)
self.MangaBox.setObjectName(_fromUtf8("MangaBox"))
self.QualityBox = QtGui.QCheckBox(self.OptionsBasic)
- self.QualityBox.setGeometry(QtCore.QRect(282, 10, 130, 18))
+ self.QualityBox.setGeometry(QtCore.QRect(282, 10, 135, 18))
font = QtGui.QFont()
- font.setPointSize(11)
+ font.setFamily(_fromUtf8("Lucida Grande"))
+ font.setPointSize(12)
self.QualityBox.setFont(font)
self.QualityBox.setFocusPolicy(QtCore.Qt.NoFocus)
self.QualityBox.setTristate(True)
@@ -171,13 +188,15 @@ class Ui_KCC(object):
self.RotateBox = QtGui.QCheckBox(self.OptionsBasic)
self.RotateBox.setGeometry(QtCore.QRect(145, 10, 130, 18))
font = QtGui.QFont()
- font.setPointSize(11)
+ font.setFamily(_fromUtf8("Lucida Grande"))
+ font.setPointSize(12)
self.RotateBox.setFont(font)
self.RotateBox.setFocusPolicy(QtCore.Qt.NoFocus)
self.RotateBox.setObjectName(_fromUtf8("RotateBox"))
self.JobList = QtGui.QListWidget(self.Form)
self.JobList.setGeometry(QtCore.QRect(10, 50, 401, 101))
font = QtGui.QFont()
+ font.setFamily(_fromUtf8("Lucida Grande"))
font.setPointSize(11)
self.JobList.setFont(font)
self.JobList.setFocusPolicy(QtCore.Qt.NoFocus)
@@ -187,6 +206,7 @@ class Ui_KCC(object):
self.BasicModeButton = QtGui.QPushButton(self.Form)
self.BasicModeButton.setGeometry(QtCore.QRect(5, 10, 210, 41))
font = QtGui.QFont()
+ font.setFamily(_fromUtf8("Lucida Grande"))
font.setPointSize(12)
font.setBold(False)
font.setWeight(50)
@@ -196,6 +216,7 @@ class Ui_KCC(object):
self.AdvModeButton = QtGui.QPushButton(self.Form)
self.AdvModeButton.setGeometry(QtCore.QRect(207, 10, 210, 41))
font = QtGui.QFont()
+ font.setFamily(_fromUtf8("Lucida Grande"))
font.setPointSize(12)
font.setBold(False)
font.setWeight(50)
@@ -204,21 +225,26 @@ class Ui_KCC(object):
self.AdvModeButton.setObjectName(_fromUtf8("AdvModeButton"))
self.OptionsAdvancedGamma = QtGui.QFrame(self.Form)
self.OptionsAdvancedGamma.setEnabled(True)
- self.OptionsAdvancedGamma.setGeometry(QtCore.QRect(10, 303, 401, 41))
+ self.OptionsAdvancedGamma.setGeometry(QtCore.QRect(5, 303, 401, 41))
font = QtGui.QFont()
+ font.setFamily(_fromUtf8("Lucida Grande"))
font.setPointSize(9)
self.OptionsAdvancedGamma.setFont(font)
self.OptionsAdvancedGamma.setObjectName(_fromUtf8("OptionsAdvancedGamma"))
self.GammaLabel = QtGui.QLabel(self.OptionsAdvancedGamma)
self.GammaLabel.setGeometry(QtCore.QRect(20, 0, 100, 40))
font = QtGui.QFont()
+ font.setFamily(_fromUtf8("Lucida Grande"))
font.setPointSize(12)
font.setBold(False)
font.setWeight(50)
self.GammaLabel.setFont(font)
self.GammaLabel.setObjectName(_fromUtf8("GammaLabel"))
self.GammaSlider = QtGui.QSlider(self.OptionsAdvancedGamma)
- self.GammaSlider.setGeometry(QtCore.QRect(110, 10, 280, 22))
+ self.GammaSlider.setGeometry(QtCore.QRect(110, 10, 290, 22))
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("Lucida Grande"))
+ self.GammaSlider.setFont(font)
self.GammaSlider.setFocusPolicy(QtCore.Qt.ClickFocus)
self.GammaSlider.setMaximum(500)
self.GammaSlider.setSingleStep(5)
@@ -227,6 +253,7 @@ class Ui_KCC(object):
self.ProgressBar = QtGui.QProgressBar(self.Form)
self.ProgressBar.setGeometry(QtCore.QRect(10, 10, 401, 35))
font = QtGui.QFont()
+ font.setFamily(_fromUtf8("Lucida Grande"))
font.setPointSize(10)
font.setBold(True)
font.setWeight(75)
@@ -237,25 +264,31 @@ class Ui_KCC(object):
self.ProgressBar.setFormat(_fromUtf8(""))
self.ProgressBar.setObjectName(_fromUtf8("ProgressBar"))
self.OptionsExpert = QtGui.QFrame(self.Form)
- self.OptionsExpert.setGeometry(QtCore.QRect(10, 335, 421, 41))
+ self.OptionsExpert.setGeometry(QtCore.QRect(5, 335, 421, 41))
font = QtGui.QFont()
+ font.setFamily(_fromUtf8("Lucida Grande"))
font.setPointSize(9)
self.OptionsExpert.setFont(font)
self.OptionsExpert.setObjectName(_fromUtf8("OptionsExpert"))
self.ColorBox = QtGui.QCheckBox(self.OptionsExpert)
self.ColorBox.setGeometry(QtCore.QRect(9, 11, 130, 18))
font = QtGui.QFont()
- font.setPointSize(11)
+ font.setFamily(_fromUtf8("Lucida Grande"))
+ font.setPointSize(12)
self.ColorBox.setFont(font)
self.ColorBox.setFocusPolicy(QtCore.Qt.NoFocus)
self.ColorBox.setObjectName(_fromUtf8("ColorBox"))
self.OptionsExpertInternal = QtGui.QFrame(self.OptionsExpert)
- self.OptionsExpertInternal.setGeometry(QtCore.QRect(90, 0, 315, 40))
+ self.OptionsExpertInternal.setGeometry(QtCore.QRect(95, 0, 315, 40))
+ font = QtGui.QFont()
+ font.setFamily(_fromUtf8("Lucida Grande"))
+ self.OptionsExpertInternal.setFont(font)
self.OptionsExpertInternal.setObjectName(_fromUtf8("OptionsExpertInternal"))
self.gridLayout_2 = QtGui.QGridLayout(self.OptionsExpertInternal)
self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2"))
self.wLabel = QtGui.QLabel(self.OptionsExpertInternal)
font = QtGui.QFont()
+ font.setFamily(_fromUtf8("Lucida Grande"))
font.setPointSize(12)
font.setBold(False)
font.setWeight(50)
@@ -270,6 +303,7 @@ class Ui_KCC(object):
self.customWidth.setSizePolicy(sizePolicy)
self.customWidth.setMaximumSize(QtCore.QSize(45, 16777215))
font = QtGui.QFont()
+ font.setFamily(_fromUtf8("Lucida Grande"))
font.setPointSize(12)
self.customWidth.setFont(font)
self.customWidth.setFocusPolicy(QtCore.Qt.ClickFocus)
@@ -279,6 +313,7 @@ class Ui_KCC(object):
self.gridLayout_2.addWidget(self.customWidth, 0, 1, 1, 1)
self.hLabel = QtGui.QLabel(self.OptionsExpertInternal)
font = QtGui.QFont()
+ font.setFamily(_fromUtf8("Lucida Grande"))
font.setPointSize(12)
font.setBold(False)
font.setWeight(50)
@@ -293,6 +328,7 @@ class Ui_KCC(object):
self.customHeight.setSizePolicy(sizePolicy)
self.customHeight.setMaximumSize(QtCore.QSize(45, 16777215))
font = QtGui.QFont()
+ font.setFamily(_fromUtf8("Lucida Grande"))
font.setPointSize(12)
self.customHeight.setFont(font)
self.customHeight.setFocusPolicy(QtCore.Qt.ClickFocus)
@@ -322,31 +358,31 @@ class Ui_KCC(object):
KCC.setWindowTitle(_translate("KCC", "Kindle Comic Converter", None))
self.ProcessingBox.setToolTip(_translate("KCC", "Disable image optimizations.
", None))
self.ProcessingBox.setText(_translate("KCC", "No optimisation", None))
- self.UpscaleBox.setToolTip(_translate("KCC", "Unchecked - Nothing
Images smaller than device resolution will not be resized.
Indeterminate - Stretching
Images smaller than device resolution will be resized. Aspect ratio will be not preserved.
Checked - Upscaling
Images smaller than device resolution will be resized. Aspect ratio will be preserved.
", None))
+ self.UpscaleBox.setToolTip(_translate("KCC", "Unchecked - Nothing
Images smaller than device resolution will not be resized.
Indeterminate - Stretching
Images smaller than device resolution will be resized. Aspect ratio will be not preserved.
Checked - Upscaling
Images smaller than device resolution will be resized. Aspect ratio will be preserved.
", None))
self.UpscaleBox.setText(_translate("KCC", "Stretch/Upscale", None))
- self.WebtoonBox.setToolTip(_translate("KCC", "EXPERIMENTAL!
Enable auto-splitting of webtoons like Tower of God or Noblesse.
Pages with a low width, high height and vertical panel flow.
", None))
+ self.WebtoonBox.setToolTip(_translate("KCC", "Enable auto-splitting of webtoons like Tower of God or Noblesse.
Pages with a low width, high height and vertical panel flow.
", None))
self.WebtoonBox.setText(_translate("KCC", "Webtoon mode", None))
- self.NoDitheringBox.setToolTip(_translate("KCC", "Create PNG files instead JPEG.
Only for non-Kindle devices!
", None))
+ self.NoDitheringBox.setToolTip(_translate("KCC", "Create PNG files instead JPEG.
", None))
self.NoDitheringBox.setText(_translate("KCC", "PNG output", None))
- self.BorderBox.setToolTip(_translate("KCC", "Fill space around images with black color.
", None))
- self.BorderBox.setText(_translate("KCC", "Black borders", None))
+ self.BorderBox.setToolTip(_translate("KCC", "Unchecked - Autodetection
Color of margins fill will be detected automatically.
Indeterminate - White
Margins will be filled with white color.
Checked - Black
Margins will be filled with black color.
", None))
+ self.BorderBox.setText(_translate("KCC", "W/B margins", None))
self.NoRotateBox.setToolTip(_translate("KCC", "Disable splitting and rotation.
", None))
self.NoRotateBox.setText(_translate("KCC", "No split/rotate", None))
- self.DeviceBox.setToolTip(_translate("KCC", "Target device.", None))
- self.FormatBox.setToolTip(_translate("KCC", "Output format.", None))
+ self.DeviceBox.setToolTip(_translate("KCC", "Target device.
", None))
+ self.FormatBox.setToolTip(_translate("KCC", "Output format.
", None))
self.ConvertButton.setText(_translate("KCC", "Convert", None))
self.DirectoryButton.setText(_translate("KCC", "Add directory", None))
self.FileButton.setText(_translate("KCC", "Add file", None))
self.ClearButton.setText(_translate("KCC", "Clear list", None))
self.MangaBox.setToolTip(_translate("KCC", "Enable right-to-left reading.
", None))
self.MangaBox.setText(_translate("KCC", "Manga mode", None))
- self.QualityBox.setToolTip(_translate("KCC", "Unchecked - Normal quality mode
Use it when Panel View support is not needed.
- Maximum quality when zoom is not enabled.
- Poor quality when zoom is enabled.
- Lowest file size.
Indeterminate - High quality mode
Not zoomed image might be a little blurry.
- Medium/High quality when zoom is not enabled.
- Maximum quality when zoom is enabled.
Checked - Ultra quality mode
Maximum possible quality.
- Maximum quality when zoom is not enabled.
- Maximum quality when zoom is enabled.
- Very high file size.
", None))
+ self.QualityBox.setToolTip(_translate("KCC", "Unchecked - Normal quality mode
Use it when Panel View support is not needed.
- Maximum quality when zoom is not enabled.
- Poor quality when zoom is enabled.
- Lowest file size.
Indeterminate - High quality mode
Not zoomed image might be a little blurry.
- Medium/High quality when zoom is not enabled.
- Maximum quality when zoom is enabled.
Checked - Ultra quality mode
Maximum possible quality.
- Maximum quality when zoom is not enabled.
- Maximum quality when zoom is enabled.
- Very high file size.
", None))
self.QualityBox.setText(_translate("KCC", "High/Ultra quality", None))
self.RotateBox.setToolTip(_translate("KCC", "Disable page spliting.
They will be rotated instead.
", None))
self.RotateBox.setText(_translate("KCC", "Horizontal mode", None))
self.BasicModeButton.setText(_translate("KCC", "Basic", None))
self.AdvModeButton.setText(_translate("KCC", "Advanced", None))
- self.GammaLabel.setToolTip(_translate("KCC", "When converting color images setting this option to 1.0 MIGHT improve readability.
", None))
+ self.GammaLabel.setToolTip(_translate("KCC", "When converting color images setting this option to 1.0 might improve readability.
", None))
self.GammaLabel.setText(_translate("KCC", "Gamma: Auto", None))
self.GammaSlider.setToolTip(_translate("KCC", "When converting color images setting this option to 1.0 might improve readability.
", None))
self.ColorBox.setToolTip(_translate("KCC", "Do not convert images to grayscale.
", None))
diff --git a/kcc/__init__.py b/kcc/__init__.py
index f9bbb7e..9e6b234 100644
--- a/kcc/__init__.py
+++ b/kcc/__init__.py
@@ -1,4 +1,4 @@
-__version__ = '3.2.1'
+__version__ = '3.3'
__license__ = 'ISC'
__copyright__ = '2012-2013, Ciro Mattia Gonano , Pawel Jastrzebski '
__docformat__ = 'restructuredtext en'
\ No newline at end of file
diff --git a/kcc/cbxarchive.py b/kcc/cbxarchive.py
index d4b8a86..226e77a 100644
--- a/kcc/cbxarchive.py
+++ b/kcc/cbxarchive.py
@@ -1,4 +1,5 @@
-# Copyright (c) 2012 Ciro Mattia Gonano
+# Copyright (c) 2012-2013 Ciro Mattia Gonano
+# Copyright (c) 2013 Pawel Jastrzebski
#
# Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the
@@ -21,6 +22,7 @@ __docformat__ = 'restructuredtext en'
import os
import zipfile
import rarfile
+from subprocess import Popen, STDOUT, PIPE
# noinspection PyBroadException
@@ -31,6 +33,8 @@ class CBxArchive:
self.compressor = 'zip'
elif rarfile.is_rarfile(origFileName):
self.compressor = 'rar'
+ elif origFileName.endswith('.7z') or origFileName.endswith('.cb7'):
+ self.compressor = '7z'
else:
self.compressor = None
@@ -67,12 +71,24 @@ class CBxArchive:
filelist.append(f)
cbrFile.extractall(targetdir, filelist)
+ def extractCB7(self, targetdir):
+ output = Popen('7za x "' + self.origFileName + '" -xr!__MACOSX -xr!.DS_Store -xr!thumbs.db -o"' + targetdir +
+ '"', stdout=PIPE, stderr=STDOUT, shell=True)
+ extracted = False
+ for line in output.stdout:
+ if "Everything is Ok" in line:
+ extracted = True
+ if not extracted:
+ raise OSError
+
def extract(self, targetdir):
print "\n" + targetdir + "\n"
if self.compressor == 'rar':
self.extractCBR(targetdir)
elif self.compressor == 'zip':
self.extractCBZ(targetdir)
+ elif self.compressor == '7z':
+ self.extractCB7(targetdir)
adir = os.listdir(targetdir)
if len(adir) == 1 and os.path.isdir(os.path.join(targetdir, adir[0])):
import shutil
diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py
index d56e5e7..cb0204a 100755
--- a/kcc/comic2ebook.py
+++ b/kcc/comic2ebook.py
@@ -1,7 +1,8 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
-# Copyright (c) 2012 Ciro Mattia Gonano
+# Copyright (c) 2012-2013 Ciro Mattia Gonano
+# Copyright (c) 2013 Pawel Jastrzebski
#
# Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the
@@ -17,7 +18,7 @@
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
#
-__version__ = '3.2.1'
+__version__ = '3.3'
__license__ = 'ISC'
__copyright__ = '2012-2013, Ciro Mattia Gonano , Pawel Jastrzebski '
__docformat__ = 'restructuredtext en'
@@ -44,11 +45,18 @@ import pdfjpgextract
def buildHTML(path, imgfile):
filename = getImageFileName(imgfile)
if filename is not None:
- # All files marked with this sufix need horizontal Panel View.
- if "_kccrotated" in str(filename):
- rotate = True
+ if "_kccrot" in str(filename):
+ rotatedPage = True
else:
- rotate = False
+ rotatedPage = False
+ if "_kccnh" in str(filename):
+ noHorizontalPV = True
+ else:
+ noHorizontalPV = False
+ if "_kccnv" in str(filename):
+ noVerticalPV = True
+ else:
+ noVerticalPV = False
htmlpath = ''
postfix = ''
backref = 1
@@ -79,88 +87,82 @@ def buildHTML(path, imgfile):
imgfile, "\" class=\"singlePage\"/>\n"
])
if options.panelview:
- if rotate:
- if options.righttoleft:
- f.writelines(["\n",
- "\n",
- "\n",
- "\n"
- ])
+ if not noHorizontalPV and not noVerticalPV:
+ if rotatedPage:
+ if options.righttoleft:
+ order = [1, 3, 2, 4]
+ else:
+ order = [2, 4, 1, 3]
else:
- f.writelines(["\n",
- "\n",
- "\n",
- "\n"
- ])
+ if options.righttoleft:
+ order = [2, 1, 4, 3]
+ else:
+ order = [1, 2, 3, 4]
+ boxes = ["BoxTL", "BoxTR", "BoxBL", "BoxBR"]
+ elif noHorizontalPV and not noVerticalPV:
+ if rotatedPage:
+ if options.righttoleft:
+ order = [2, 1]
+ else:
+ order = [1, 2]
+ else:
+ order = [1, 2]
+ boxes = ["BoxT", "BoxB"]
+ elif not noHorizontalPV and noVerticalPV:
+ if rotatedPage:
+ order = [1, 2]
+ else:
+ if options.righttoleft:
+ order = [2, 1]
+ else:
+ order = [1, 2]
+ boxes = ["BoxL", "BoxR"]
else:
- if options.righttoleft:
- f.writelines(["\n",
- "\n",
- "\n",
- "\n"
- ])
- else:
- f.writelines(["\n",
- "\n",
- "\n",
- "\n"
- ])
+ order = [1]
+ boxes = ["BoxC"]
+ for i in range(0, len(boxes)):
+ f.writelines(["\n"])
if options.quality == 2:
imgfilepv = string.split(imgfile, ".")
+ imgfilepv[0] = imgfilepv[0].split("_kccx")[0].replace("_kccnh", "").replace("_kccnv", "")
imgfilepv[0] += "_kcchq"
imgfilepv = string.join(imgfilepv, ".")
else:
imgfilepv = imgfile
- f.writelines(["\n",
- "\n",
- "\n",
- "\n"
- ])
+ xy = string.split(filename[0], "_kccx")[1]
+ x = string.split(xy, "_kccy")[0].lstrip("0")
+ y = string.split(xy, "_kccy")[1].lstrip("0")
+ if x != "":
+ x = "-" + str(float(x)/100) + "%"
+ else:
+ x = "0%"
+ if y != "":
+ y = "-" + str(float(y)/100) + "%"
+ else:
+ y = "0%"
+ boxStyles = {"BoxTL": "left:" + x + ";top:" + y + ";",
+ "BoxTR": "right:" + x + ";top:" + y + ";",
+ "BoxBL": "left:" + x + ";bottom:" + y + ";",
+ "BoxBR": "right:" + x + ";bottom:" + y + ";",
+ "BoxT": "left:-25%;top:" + y + ";",
+ "BoxB": "left:-25%;bottom:" + y + ";",
+ "BoxL": "left:" + x + ";top:-25%;",
+ "BoxR": "right:" + x + ";top:-25%;",
+ "BoxC": "right:-25%;top:-25%;"
+ }
+ for box in boxes:
+ f.writelines(["\n",
+ ])
f.writelines(["\n\n"])
f.close()
return path, imgfile
-def buildBlankHTML(path):
- f = open(os.path.join(path, 'blank.html'), "w")
- f.writelines(["\n",
- "\n",
- "\n",
- "\n",
- "\n",
- "\n",
- "\n",
- "\n",
- ""])
- f.close()
- return path
-
-
def buildNCX(dstdir, title, chapters):
from uuid import uuid4
options.uuid = str(uuid4())
@@ -173,16 +175,18 @@ def buildNCX(dstdir, title, chapters):
"\n",
"\n",
"\n",
- "\n",
+ "\n",
"\n",
"\n",
+ "\n",
"\n",
"", title, "\n",
""
])
for chapter in chapters:
folder = chapter[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
- title = os.path.basename(folder)
+ if os.path.basename(folder) != "Text":
+ title = os.path.basename(folder)
filename = getImageFileName(os.path.join(folder, chapter[1]))
f.write("" + title
+ "\n")
@@ -197,38 +201,33 @@ def buildOPF(dstdir, title, filelist, cover=None):
imgres = str(deviceres[0]) + "x" + str(deviceres[1])
if options.righttoleft:
writingmode = "horizontal-rl"
- facing = "right"
- facing1 = "right"
- facing2 = "left"
else:
writingmode = "horizontal-lr"
- facing = "left"
- facing1 = "left"
- facing2 = "right"
f = open(opffile, "w")
f.writelines(["\n",
- "\n",
- "\n",
+ "\n",
+ "\n",
"", title, "\n",
"en-US\n",
"", options.uuid, "\n",
+ "KCC\n",
+ "\n",
"\n",
+ "\n",
"\n",
"\n",
+ "\n",
"\n",
"\n",
"\n"
- ])
- if options.landscapemode:
- f.writelines(["\n",
- "\n"])
- else:
- f.writelines(["\n",
- "\n"])
- f.writelines(["\n",
+ "\n",
+ "\n",
+ "\n",
"\n",
- "\n",
+ "\n",
+ "\n",
"\n\n \n"])
if cover is not None:
@@ -253,44 +252,10 @@ def buildOPF(dstdir, title, filelist, cover=None):
mt = 'image/jpeg'
f.write(" \n")
- if options.landscapemode and splitCount > 0:
- splitCountUsed = 1
- while splitCountUsed <= splitCount:
- f.write("- \n")
- splitCountUsed += 1
f.write("
- \n")
f.write("
\n\n")
- splitCountUsed = 1
for entry in reflist:
- if "_kcca" in str(entry):
- # noinspection PyRedundantParentheses
- if ((options.righttoleft and facing == 'left') or (not options.righttoleft and facing == 'right')) and\
- options.landscapemode:
- f.write("\n")
- splitCountUsed += 1
- if options.landscapemode:
- f.write("\n")
- else:
- f.write("\n")
- elif "_kccb" in str(entry):
- if options.landscapemode:
- f.write("\n")
- else:
- f.write("\n")
- if options.righttoleft:
- facing = "right"
- else:
- facing = "left"
- else:
- if options.landscapemode:
- f.write("\n")
- else:
- f.write("\n")
- if facing == 'right':
- facing = 'left'
- else:
- facing = 'right'
+ f.write("\n")
f.write("\n\n\n\n")
f.close()
os.mkdir(os.path.join(dstdir, 'META-INF'))
@@ -322,24 +287,22 @@ def getImageFileName(imgfile):
return filename
-def applyImgOptimization(img, isSplit, toRight, options, overrideQuality=5):
- if not options.webtoon:
+def applyImgOptimization(img, opt, overrideQuality=5):
+ img.getImageFill(opt.webtoon)
+ if not opt.webtoon:
img.cropWhiteSpace(10.0)
- if options.cutpagenumbers and not options.webtoon:
+ if opt.cutpagenumbers and not opt.webtoon:
img.cutPageNumber()
- img.optimizeImage(options.gamma)
+ img.optimizeImage(opt.gamma)
if overrideQuality != 5:
- img.resizeImage(options.upscale, options.stretch, options.black_borders, isSplit, toRight,
- options.landscapemode, overrideQuality)
+ img.resizeImage(opt.upscale, opt.stretch, opt.bordersColor, overrideQuality)
else:
- img.resizeImage(options.upscale, options.stretch, options.black_borders, isSplit, toRight,
- options.landscapemode, options.quality)
- if options.forcepng and not options.forcecolor:
+ img.resizeImage(opt.upscale, opt.stretch, opt.bordersColor, opt.quality)
+ if opt.forcepng and not opt.forcecolor:
img.quantizeImage()
def dirImgProcess(path):
- global options, splitCount
work = []
pagenumber = 0
pagenumbermodifier = 0
@@ -378,7 +341,6 @@ def dirImgProcess(path):
splitpages.sort()
for page in splitpages:
if (page + pagenumbermodifier) % 2 == 0:
- splitCount += 1
pagenumbermodifier += 1
pagenumbermodifier += 1
else:
@@ -386,9 +348,9 @@ def dirImgProcess(path):
raise UserWarning("Source directory is empty.")
-def fileImgProcess_init(queue, options):
+def fileImgProcess_init(queue, opt):
fileImgProcess.queue = queue
- fileImgProcess.options = options
+ fileImgProcess.options = opt
# noinspection PyUnresolvedReferences
@@ -396,59 +358,53 @@ def fileImgProcess(work):
afile = work[0]
dirpath = work[1]
pagenumber = work[2]
- options = fileImgProcess.options
+ opt = fileImgProcess.options
output = None
- if options.verbose:
- print "Optimizing " + afile + " for " + options.profile
+ if opt.verbose:
+ print "Optimizing " + afile + " for " + opt.profile
else:
print ".",
fileImgProcess.queue.put(".")
- img = image.ComicPage(os.path.join(dirpath, afile), options.profileData)
- if options.quality == 2:
+ img = image.ComicPage(os.path.join(dirpath, afile), opt.profileData)
+ if opt.quality == 2:
wipe = False
else:
wipe = True
- if options.nosplitrotate:
+ if opt.nosplitrotate:
split = None
else:
- split = img.splitPage(dirpath, options.righttoleft, options.rotate)
- if split is not None and split is not "R":
- if options.verbose:
+ split = img.splitPage(dirpath, opt.righttoleft, opt.rotate)
+ if split is not None:
+ if opt.verbose:
print "Splitted " + afile
- if options.righttoleft:
- toRight1 = False
- toRight2 = True
- else:
- toRight1 = True
- toRight2 = False
output = pagenumber
- img0 = image.ComicPage(split[0], options.profileData)
- applyImgOptimization(img0, True, toRight1, options)
- img0.saveToDir(dirpath, options.forcepng, options.forcecolor, wipe)
- img1 = image.ComicPage(split[1], options.profileData)
- applyImgOptimization(img1, True, toRight2, options)
- img1.saveToDir(dirpath, options.forcepng, options.forcecolor, wipe)
- if options.quality == 2:
- img3 = image.ComicPage(split[0], options.profileData)
- applyImgOptimization(img3, True, toRight1, options, 0)
- img3.saveToDir(dirpath, options.forcepng, options.forcecolor, True)
- img4 = image.ComicPage(split[1], options.profileData)
- applyImgOptimization(img4, True, toRight2, options, 0)
- img4.saveToDir(dirpath, options.forcepng, options.forcecolor, True)
+ img0 = image.ComicPage(split[0], opt.profileData)
+ applyImgOptimization(img0, opt)
+ img0.saveToDir(dirpath, opt.forcepng, opt.forcecolor, wipe)
+ img1 = image.ComicPage(split[1], opt.profileData)
+ applyImgOptimization(img1, opt)
+ img1.saveToDir(dirpath, opt.forcepng, opt.forcecolor, wipe)
+ if opt.quality == 2:
+ img3 = image.ComicPage(split[0], opt.profileData)
+ applyImgOptimization(img3, opt, 0)
+ img3.saveToDir(dirpath, opt.forcepng, opt.forcecolor, True)
+ img4 = image.ComicPage(split[1], opt.profileData)
+ applyImgOptimization(img4, opt, 0)
+ img4.saveToDir(dirpath, opt.forcepng, opt.forcecolor, True)
else:
- applyImgOptimization(img, False, False, options)
- img.saveToDir(dirpath, options.forcepng, options.forcecolor, wipe, split)
- if options.quality == 2:
- img2 = image.ComicPage(os.path.join(dirpath, afile), options.profileData)
- if split == "R":
+ applyImgOptimization(img, opt)
+ img.saveToDir(dirpath, opt.forcepng, opt.forcecolor, wipe)
+ if opt.quality == 2:
+ img2 = image.ComicPage(os.path.join(dirpath, afile), opt.profileData)
+ if img.rotated:
img2.image = img2.image.rotate(90)
- applyImgOptimization(img2, False, False, options, 0)
- img2.saveToDir(dirpath, options.forcepng, options.forcecolor, True, split)
+ img2.rotated = True
+ applyImgOptimization(img2, opt, 0)
+ img2.saveToDir(dirpath, opt.forcepng, opt.forcecolor, True)
return output
def genEpubStruct(path):
- global options
filelist = []
chapterlist = []
cover = None
@@ -508,6 +464,36 @@ def genEpubStruct(path):
"height: ", str(panelviewsize[1]), "px;\n",
"width: ", str(panelviewsize[0]), "px;\n",
"}\n",
+ "#Generic-Panel {\n",
+ "top: 0;\n",
+ "height: 100%;\n",
+ "width: 100%;\n",
+ "}\n",
+ "#BoxC {\n",
+ "top: 0;\n",
+ "height: 100%;\n",
+ "width: 100%;\n",
+ "}\n",
+ "#BoxT {\n",
+ "top: 0;\n",
+ "height: 50%;\n",
+ "width: 100%;\n",
+ "}\n",
+ "#BoxB {\n",
+ "bottom: 0;\n",
+ "height: 50%;\n",
+ "width: 100%;\n",
+ "}\n",
+ "#BoxL {\n",
+ "left: 0;\n",
+ "height: 100%;\n",
+ "width: 50%;\n",
+ "}\n",
+ "#BoxR {\n",
+ "right: 0;\n",
+ "height: 100%;\n",
+ "width: 50%;\n",
+ "}\n",
"#BoxTL {\n",
"top: 0;\n",
"left: 0;\n",
@@ -531,47 +517,7 @@ def genEpubStruct(path):
"right: 0;\n",
"height: 50%;\n",
"width: 50%;\n",
- "}\n",
- "#BoxTL-Panel {\n",
- "top: 0;\n",
- "left: 0;\n",
- "height: 100%;\n",
- "width: 100%;\n",
- "}\n",
- "#BoxTL-Panel img {\n",
- "top: 0%;\n",
- "left: 0%;\n",
- "}\n",
- "#BoxTR-Panel {\n",
- "top: 0;\n",
- "right: 0;\n",
- "height: 100%;\n",
- "width: 100%;\n",
- "}\n",
- "#BoxTR-Panel img {\n",
- "top: 0%;\n",
- "right: 0%;\n",
- "}\n",
- "#BoxBL-Panel {\n",
- "bottom: 0;\n",
- "left: 0;\n",
- "height: 100%;\n",
- "width: 100%;\n",
- "}\n",
- "#BoxBL-Panel img {\n",
- "bottom: 0%;\n",
- "left: 0%;\n",
- "}\n",
- "#BoxBR-Panel {\n",
- "bottom: 0;\n",
- "right: 0;\n",
- "height: 100%;\n",
- "width: 100%;\n",
- "}\n",
- "#BoxBR-Panel img {\n",
- "bottom: 0%;\n",
- "right: 0%;\n",
- "}"
+ "}",
])
f.close()
for (dirpath, dirnames, filenames) in os.walk(os.path.join(path, 'OEBPS', 'Images')):
@@ -588,13 +534,11 @@ def genEpubStruct(path):
'cover' + getImageFileName(filelist[-1][1])[1])
copyfile(os.path.join(filelist[-1][0], filelist[-1][1]), cover)
buildNCX(path, options.title, chapterlist)
- # ensure we're sorting files alphabetically
+ # Ensure we're sorting files alphabetically
convert = lambda text: int(text) if text.isdigit() else text
alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)]
filelist.sort(key=lambda name: (alphanum_key(name[0].lower()), alphanum_key(name[1].lower())))
buildOPF(path, options.title, filelist, cover)
- if options.landscapemode and splitCount > 0:
- filelist.append(buildBlankHTML(os.path.join(path, 'OEBPS', 'Text')))
def getWorkFolder(afile):
@@ -625,8 +569,7 @@ def getWorkFolder(afile):
path = cbx.extract(workdir)
except OSError:
rmtree(workdir)
- print 'Unrar not found, please download from ' + \
- 'http://www.rarlab.com/download.htm and put into your PATH.'
+ print 'UnRAR/7za not found or file failed to extract!'
sys.exit(21)
else:
rmtree(workdir)
@@ -637,9 +580,9 @@ def getWorkFolder(afile):
def slugify(value):
- # Normalizes string, converts to lowercase, removes non-alpha characters,
- # and converts spaces to hyphens.
+ # Normalizes string, converts to lowercase, removes non-alpha characters and converts spaces to hyphens.
import unicodedata
+ #noinspection PyArgumentList
value = unicodedata.normalize('NFKD', unicode(value, 'latin1')).encode('ascii', 'ignore')
value = re.sub('[^\w\s\.-]', '', value).strip().lower()
value = re.sub('[-\.\s]+', '-', value)
@@ -689,6 +632,7 @@ def getDirectorySize(start_path='.'):
return total_size
+# noinspection PyUnusedLocal
def createNewTome(parentPath):
tomePathRoot = tempfile.mkdtemp('', 'KCC-TMP-')
#tomePathRoot = tempfile.mkdtemp('', 'KCC-TMP-', parentPath)
@@ -843,21 +787,22 @@ def Usage():
def main(argv=None, qtGUI=None):
- global parser, options, epub_path, splitCount, GUI
+ global parser, options, GUI
parser = OptionParser(usage="Usage: %prog [options] comic_file|comic_folder", add_help_option=False)
mainOptions = OptionGroup(parser, "MAIN")
- experimentalOptions = OptionGroup(parser, "EXPERIMENTAL")
processingOptions = OptionGroup(parser, "PROCESSING")
outputOptions = OptionGroup(parser, "OUTPUT SETTINGS")
customProfileOptions = OptionGroup(parser, "CUSTOM PROFILE")
otherOptions = OptionGroup(parser, "OTHER")
mainOptions.add_option("-p", "--profile", action="store", dest="profile", default="KHD",
- help="Device profile (Choose one among K1, K2, K3, K4NT, K4T, KDX, KDXG, KHD, KF, KFHD,"
- " KFHD8, KFA) [Default=KHD]")
+ help="Device profile (Choose one among K1, K2, K345, KDX, KDXG, KHD, KF, KFHD, KFHD8, KFHDX,"
+ " KFHDX8, KFA) [Default=KHD]")
mainOptions.add_option("-q", "--quality", type="int", dest="quality", default="0",
help="Quality of Panel View. 0 - Normal 1 - High 2 - Ultra [Default=0]")
mainOptions.add_option("-m", "--manga-style", action="store_true", dest="righttoleft", default=False,
help="Manga style (Right-to-left reading and splitting)")
+ mainOptions.add_option("-w", "--webtoon", action="store_true", dest="webtoon", default=False,
+ help="Webtoon processing mode"),
outputOptions.add_option("-o", "--output", action="store", dest="output", default=None,
help="Output generated file to specified directory or file")
outputOptions.add_option("-t", "--title", action="store", dest="title", default="defaulttitle",
@@ -866,10 +811,10 @@ def main(argv=None, qtGUI=None):
help="Outputs a CBZ archive and does not generate EPUB")
outputOptions.add_option("--batchsplit", action="store_true", dest="batchsplit", default=False,
help="Split output into multiple files"),
- experimentalOptions.add_option("-w", "--webtoon", action="store_true", dest="webtoon", default=False,
- help="Webtoon processing mode"),
processingOptions.add_option("--blackborders", action="store_true", dest="black_borders", default=False,
- help="Use black borders instead of white ones")
+ help="Disable autodetection and force black borders")
+ processingOptions.add_option("--whiteborders", action="store_true", dest="white_borders", default=False,
+ help="Disable autodetection and force white borders")
processingOptions.add_option("--forcecolor", action="store_true", dest="forcecolor", default=False,
help="Don't convert images to grayscale")
processingOptions.add_option("--forcepng", action="store_true", dest="forcepng", default=False,
@@ -897,7 +842,6 @@ def main(argv=None, qtGUI=None):
otherOptions.add_option("-h", "--help", action="help",
help="Show this help message and exit")
parser.add_option_group(mainOptions)
- parser.add_option_group(experimentalOptions)
parser.add_option_group(outputOptions)
parser.add_option_group(processingOptions)
parser.add_option_group(customProfileOptions)
@@ -920,7 +864,6 @@ def main(argv=None, qtGUI=None):
comic2panel.main(['-y ' + str(options.customheight), '-i', path], qtGUI)
else:
comic2panel.main(['-y ' + str(image.ProfileData.Profiles[options.profile][1][1]), '-i', path], qtGUI)
- splitCount = 0
if options.imgproc:
print "\nProcessing images..."
if GUI:
@@ -984,45 +927,29 @@ def getOutputFilename(srcpath, wantedname, ext, tomeNumber):
def checkOptions():
global options
- # Webtoon mode mandatory options
- if options.webtoon:
- options.nosplitrotate = True
- options.black_borders = False
- options.quality = 0
- # Landscape mode is only supported by Kindle Touch and Paperwhite.
- if options.profile == 'K4T' or options.profile == 'KHD':
- options.landscapemode = True
- else:
- options.landscapemode = False
- # Older Kindle don't support Virtual Panel View. We providing them configuration that will fake that feature.
- # Ultra quality mode require Real Panel View. Landscape mode don't work correcly without Virtual Panel View.
- if options.profile == 'K3' or options.profile == 'K4NT' or options.quality == 2:
- # Real Panel View
- options.panelview = True
- options.landscapemode = False
- else:
- # Virtual Panel View
- options.panelview = False
+ options.panelview = True
+ options.bordersColor = None
+ if options.white_borders:
+ options.bordersColor = "white"
+ if options.black_borders:
+ options.bordersColor = "black"
# Disabling grayscale conversion for Kindle Fire family.
- if options.profile == 'KF' or options.profile == 'KFHD' or options.profile == 'KFHD8' or options.forcecolor:
+ if options.profile == 'KF' or options.profile == 'KFHD' or options.profile == 'KFHD8' or options.profile == 'KFHDX'\
+ or options.profile == 'KFHDX8' or options.forcecolor:
options.forcecolor = True
else:
options.forcecolor = False
- # Mixing vertical and horizontal pages require real Panel View.
- # Landscape mode don't work correcly without Virtual Panel View.
- if options.rotate:
- options.panelview = True
- options.landscapemode = False
# Older Kindle don't need higher resolution files due lack of Panel View.
- # Kindle Fire family have very high resolution. Bigger images are not needed.
- if options.profile == 'K1' or options.profile == 'K2' or options.profile == 'KDX' or options.profile == 'KDXG'\
- or options.profile == 'KF' or options.profile == 'KFHD' or options.profile == 'KFHD8':
+ if options.profile == 'K1' or options.profile == 'K2' or options.profile == 'KDX' or options.profile == 'KDXG':
options.quality = 0
- if options.profile == 'K1' or options.profile == 'K2' or options.profile == 'KDX' or options.profile == 'KDXG':
- options.panelview = False
- # Disable all Kindle features
+ options.panelview = False
+ # Webtoon mode mandatory options
+ if options.webtoon:
+ options.nosplitrotate = True
+ options.quality = 0
+ options.panelview = False
+ # Disable all Kindle features for other e-readers
if options.profile == 'OTHER':
- options.landscapemode = False
options.panelview = False
options.quality = 0
# Kindle for Android profile require target resolution.
diff --git a/kcc/comic2panel.py b/kcc/comic2panel.py
index 57a39eb..637ea28 100644
--- a/kcc/comic2panel.py
+++ b/kcc/comic2panel.py
@@ -1,7 +1,8 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
-# Copyright (c) 2012 Ciro Mattia Gonano
+# Copyright (c) 2012-2013 Ciro Mattia Gonano
+# Copyright (c) 2013 Pawel Jastrzebski
#
# Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the
@@ -17,7 +18,7 @@
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
#
-__version__ = '3.2.1'
+__version__ = '3.3'
__license__ = 'ISC'
__copyright__ = '2012-2013, Ciro Mattia Gonano , Pawel Jastrzebski '
__docformat__ = 'restructuredtext en'
@@ -28,7 +29,7 @@ from shutil import rmtree, copytree, move
from optparse import OptionParser, OptionGroup
from multiprocessing import Pool, Queue, freeze_support
try:
- # noinspection PyUnresolvedReferences,PyPackageRequirements
+ # noinspection PyUnresolvedReferences
from PIL import Image, ImageStat
except ImportError:
print "ERROR: Pillow is not installed!"
@@ -53,48 +54,9 @@ def getImageFileName(imgfile):
return filename
-def getImageHistogram(image):
- histogram = image.histogram()
- RBGW = []
- for i in range(256):
- RBGW.append(histogram[i] + histogram[256 + i] + histogram[512 + i])
- white = 0
- black = 0
- for i in range(245, 256):
- white += RBGW[i]
- for i in range(11):
- black += RBGW[i]
- if white > black:
- return False
- else:
- return True
-
-
-def getImageFill(image):
- imageSize = image.size
- imageT = image.crop((0, 0, imageSize[0], 1))
- imageB = image.crop((0, imageSize[1]-1, imageSize[0], imageSize[1]))
- fill = 0
- fill += getImageHistogram(imageT)
- fill += getImageHistogram(imageB)
- if fill == 2:
- return 'KCCFB'
- elif fill == 0:
- return 'KCCFW'
- else:
- imageL = image.crop((0, 0, 1, imageSize[1]))
- imageR = image.crop((imageSize[0]-1, 0, imageSize[0], imageSize[1]))
- fill += getImageHistogram(imageL)
- fill += getImageHistogram(imageR)
- if fill >= 2:
- return 'KCCFB'
- else:
- return 'KCCFW'
-
-
-def sanitizePanelSize(panel, options):
+def sanitizePanelSize(panel, opt):
newPanels = []
- if panel[2] > 8 * options.height:
+ if panel[2] > 8 * opt.height:
diff = (panel[2] / 8)
newPanels.append([panel[0], panel[1] - diff*7, diff])
newPanels.append([panel[1] - diff*7, panel[1] - diff*6, diff])
@@ -104,13 +66,13 @@ def sanitizePanelSize(panel, options):
newPanels.append([panel[1] - diff*3, panel[1] - diff*2, diff])
newPanels.append([panel[1] - diff*2, panel[1] - diff, diff])
newPanels.append([panel[1] - diff, panel[1], diff])
- elif panel[2] > 4 * options.height:
+ elif panel[2] > 4 * opt.height:
diff = (panel[2] / 4)
newPanels.append([panel[0], panel[1] - diff*3, diff])
newPanels.append([panel[1] - diff*3, panel[1] - diff*2, diff])
newPanels.append([panel[1] - diff*2, panel[1] - diff, diff])
newPanels.append([panel[1] - diff, panel[1], diff])
- elif panel[2] > 2 * options.height:
+ elif panel[2] > 2 * opt.height:
newPanels.append([panel[0], panel[1] - (panel[2] / 2), (panel[2] / 2)])
newPanels.append([panel[1] - (panel[2] / 2), panel[1], (panel[2] / 2)])
else:
@@ -118,17 +80,17 @@ def sanitizePanelSize(panel, options):
return newPanels
-def splitImage_init(queue, options):
+def splitImage_init(queue, opt):
splitImage.queue = queue
- splitImage.options = options
+ splitImage.options = opt
# noinspection PyUnresolvedReferences
def splitImage(work):
path = work[0]
name = work[1]
- options = splitImage.options
- # Harcoded options
+ opt = splitImage.options
+ # Harcoded opttions
threshold = 1.0
delta = 15
print ".",
@@ -137,7 +99,7 @@ def splitImage(work):
filePath = os.path.join(path, name)
# Detect corrupted files
try:
- image = Image.open(filePath)
+ Image.open(filePath)
except IOError:
raise RuntimeError('Cannot read image file %s' % filePath)
try:
@@ -153,8 +115,8 @@ def splitImage(work):
image = Image.open(filePath)
image = image.convert('RGB')
widthImg, heightImg = image.size
- if heightImg > options.height:
- if options.debug:
+ if heightImg > opt.height:
+ if opt.debug:
from PIL import ImageDraw
debugImage = Image.open(filePath)
draw = ImageDraw.Draw(debugImage)
@@ -176,23 +138,23 @@ def splitImage(work):
if y1 + delta >= heightImg:
y1 = heightImg - 1
y2Temp = y1
- if options.debug:
+ if opt.debug:
draw.line([(0, y1Temp), (widthImg, y1Temp)], fill=(0, 255, 0))
draw.line([(0, y2Temp), (widthImg, y2Temp)], fill=(255, 0, 0))
panelHeight = y2Temp - y1Temp
if panelHeight > delta:
# Panels that can't be cut nicely will be forcefully splitted
- panelsCleaned = sanitizePanelSize([y1Temp, y2Temp, panelHeight], options)
+ panelsCleaned = sanitizePanelSize([y1Temp, y2Temp, panelHeight], opt)
for panel in panelsCleaned:
panels.append(panel)
- if options.debug:
+ if opt.debug:
# noinspection PyUnboundLocalVariable
debugImage.save(os.path.join(path, fileExpanded[0] + '-debug.png'), 'PNG')
# Create virtual pages
pages = []
currentPage = []
- pageLeft = options.height
+ pageLeft = opt.height
panelNumber = 0
for panel in panels:
if pageLeft - panel[2] > 0:
@@ -202,7 +164,7 @@ def splitImage(work):
else:
if len(currentPage) > 0:
pages.append(currentPage)
- pageLeft = options.height - panel[2]
+ pageLeft = opt.height - panel[2]
currentPage = [panelNumber]
panelNumber += 1
if len(currentPage) > 0:
@@ -222,7 +184,7 @@ def splitImage(work):
newPage.paste(panelImg, (0, targetHeight))
targetHeight += panels[panel][2]
newPage.save(os.path.join(path, fileExpanded[0] + '-' +
- str(pageNumber) + '-' + getImageFill(newPage) + '.png'), 'PNG')
+ str(pageNumber) + '.png'), 'PNG')
pageNumber += 1
os.remove(filePath)
diff --git a/kcc/image.py b/kcc/image.py
index 9958f3d..97c97b8 100755
--- a/kcc/image.py
+++ b/kcc/image.py
@@ -1,6 +1,7 @@
# Copyright (C) 2010 Alex Yatskov
# Copyright (C) 2011 Stanislav (proDOOMman) Kosolapov
# Copyright (C) 2012-2013 Ciro Mattia Gonano
+# Copyright (C) 2013 Pawel Jastrzebski
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -21,7 +22,7 @@ __docformat__ = 'restructuredtext en'
import os
try:
- # noinspection PyUnresolvedReferences,PyPackageRequirements
+ # noinspection PyUnresolvedReferences
from PIL import Image, ImageOps, ImageStat, ImageChops
except ImportError:
print "ERROR: Pillow is not installed!"
@@ -29,6 +30,9 @@ except ImportError:
class ProfileData:
+ def __init__(self):
+ pass
+
Palette4 = [
0x00, 0x00, 0x00,
0x55, 0x55, 0x55,
@@ -73,38 +77,60 @@ class ProfileData:
0xff, 0xff, 0xff,
]
+ PalleteNull = [
+ ]
+
Profiles = {
- 'K1': ("Kindle 1", (600, 800), Palette4, 1.8, (900, 1200)),
- 'K2': ("Kindle 2", (600, 800), Palette15, 1.8, (900, 1200)),
- 'K3': ("Kindle Keyboard", (600, 800), Palette16, 1.8, (900, 1200)),
- 'K4NT': ("Kindle Non-Touch", (600, 800), Palette16, 1.8, (900, 1200)),
- 'K4T': ("Kindle Touch", (600, 800), Palette16, 1.8, (900, 1200)),
+ 'K1': ("Kindle 1", (600, 670), Palette4, 1.8, (900, 1005)),
+ 'K2': ("Kindle 2", (600, 670), Palette15, 1.8, (900, 1005)),
+ 'K345': ("Kindle", (600, 800), Palette16, 1.8, (900, 1200)),
'KHD': ("Kindle Paperwhite", (758, 1024), Palette16, 1.8, (1137, 1536)),
- 'KDX': ("Kindle DX", (824, 1200), Palette15, 1.8, (1236, 1800)),
- 'KDXG': ("Kindle DXG", (824, 1200), Palette16, 1.8, (1236, 1800)),
- 'KF': ("Kindle Fire", (600, 1024), Palette16, 1.0, (900, 1536)),
- 'KFHD': ("Kindle Fire HD 7\"", (800, 1280), Palette16, 1.0, (1200, 1920)),
- 'KFHD8': ("Kindle Fire HD 8.9\"", (1200, 1920), Palette16, 1.0, (1800, 2880)),
- 'KFA': ("Kindle for Android", (0, 0), Palette16, 1.0, (0, 0)),
+ 'KDX': ("Kindle DX", (824, 1000), Palette15, 1.8, (1236, 1500)),
+ 'KDXG': ("Kindle DXG", (824, 1000), Palette16, 1.8, (1236, 1500)),
+ 'KF': ("Kindle Fire", (600, 1024), PalleteNull, 1.0, (900, 1536)),
+ 'KFHD': ("K. Fire HD 7\"", (800, 1280), PalleteNull, 1.0, (1200, 1920)),
+ 'KFHD8': ("K. Fire HD 8.9\"", (1200, 1920), PalleteNull, 1.0, (1800, 2880)),
+ 'KFHDX': ("K. Fire HDX 7\"", (1200, 1920), PalleteNull, 1.0, (1800, 2880)),
+ 'KFHDX8': ("K. Fire HDX 8.9\"", (1600, 2560), PalleteNull, 1.0, (2400, 3840)),
+ 'KFA': ("Kindle for Android", (0, 0), PalleteNull, 1.0, (0, 0)),
'OTHER': ("Other", (0, 0), Palette16, 1.8, (0, 0)),
}
ProfileLabels = {
"Kindle 1": 'K1',
"Kindle 2": 'K2',
- "Kindle 3/Keyboard": 'K3',
- "Kindle 4/Non-Touch": 'K4NT',
- "Kindle 4/Touch": 'K4T',
+ "Kindle": 'K345',
"Kindle Paperwhite": 'KHD',
"Kindle DX": 'KDX',
"Kindle DXG": 'KDXG',
"Kindle Fire": 'KF',
- "Kindle Fire HD 7\"": 'KFHD',
- "Kindle Fire HD 8.9\"": 'KFHD8',
+ "K. Fire HD 7\"": 'KFHD',
+ "K. Fire HD 8.9\"": 'KFHD8',
+ "K. Fire HDX 7\"": 'KFHDX',
+ "K. Fire HDX 8.9\"": 'KFHDX8',
"Kindle for Android": 'KFA',
"Other": 'OTHER'
}
+ ProfileLabelsGUI = [
+ "Kindle Paperwhite",
+ "Kindle",
+ "Separator",
+ "K. Fire HD 7\"",
+ "K. Fire HD 8.9\"",
+ "K. Fire HDX 7\"",
+ "K. Fire HDX 8.9\"",
+ "Separator",
+ "Kindle for Android",
+ "Other",
+ "Separator",
+ "Kindle 1",
+ "Kindle 2",
+ "Kindle DX",
+ "Kindle DXG",
+ "Kindle Fire"
+ ]
+
class ComicPage:
def __init__(self, source, device):
@@ -133,19 +159,29 @@ class ComicPage:
raise RuntimeError('Image file %s is corrupted' % source)
self.image = Image.open(source)
self.image = self.image.convert('RGB')
+ self.rotated = None
+ self.border = None
+ self.noHPV = None
+ self.noVPV = None
+ self.fill = None
- def saveToDir(self, targetdir, forcepng, color, wipe, suffix=None):
+ def saveToDir(self, targetdir, forcepng, color, wipe):
try:
+ suffix = ""
if not color:
self.image = self.image.convert('L') # convert to grayscale
- if suffix == "R":
- suffix = "_kccrotated"
- else:
- suffix = ""
+ if self.rotated:
+ suffix += "_kccrot"
if wipe:
os.remove(os.path.join(targetdir, self.filename))
else:
suffix += "_kcchq"
+ if self.noHPV:
+ suffix += "_kccnh"
+ if self.noVPV:
+ suffix += "_kccnv"
+ if self.border:
+ suffix += "_kccx" + str(self.border[0]) + "_kccy" + str(self.border[1])
if forcepng:
self.image.save(os.path.join(targetdir, os.path.splitext(self.filename)[0] + suffix + ".png"), "PNG")
else:
@@ -171,72 +207,98 @@ class ComicPage:
palImg.putpalette(self.palette)
self.image = self.image.quantize(palette=palImg)
- def resizeImage(self, upscale=False, stretch=False, black_borders=False, isSplit=False, toRight=False,
- landscapeMode=False, qualityMode=0):
+ def resizeImage(self, upscale=False, stretch=False, bordersColor=None, qualityMode=0):
method = Image.ANTIALIAS
- if '-KCCFW' in str(self.filename):
- fill = 'white'
- elif '-KCCFB' in str(self.filename):
- fill = 'black'
+ if bordersColor:
+ fill = bordersColor
else:
- if black_borders:
- fill = 'black'
- else:
- fill = 'white'
+ fill = self.fill
if qualityMode == 0:
size = (self.size[0], self.size[1])
+ generateBorder = True
+ elif qualityMode == 1:
+ size = (self.panelviewsize[0], self.panelviewsize[1])
+ generateBorder = True
else:
size = (self.panelviewsize[0], self.panelviewsize[1])
- # Kindle Paperwhite/Touch - Force upscale of splited pages to increase readability
- if isSplit and landscapeMode:
- upscale = True
+ generateBorder = False
if self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1]:
if not upscale:
borderw = (self.size[0] - self.image.size[0]) / 2
borderh = (self.size[1] - self.image.size[1]) / 2
self.image = ImageOps.expand(self.image, border=(borderw, borderh), fill=fill)
+ if generateBorder:
+ if (self.image.size[0]-(2*borderw))*1.5 < self.size[0]:
+ self.noHPV = True
+ if (self.image.size[1]-(2*borderh))*1.5 < self.size[1]:
+ self.noVPV = True
+ self.border = [int(round(float(borderw)/float(self.image.size[0])*100, 2)*100*1.5),
+ int(round(float(borderh)/float(self.image.size[1])*100, 2)*100*1.5)]
return self.image
else:
method = Image.BILINEAR
- if stretch: # if stretching call directly resize() without other considerations.
+ if stretch: # If stretching call directly resize() without other considerations.
self.image = self.image.resize(size, method)
+ if generateBorder:
+ if fill == 'white':
+ border = ImageOps.invert(self.image).getbbox()
+ else:
+ border = self.image.getbbox()
+ if border is not None:
+ if (border[2]-border[0])*1.5 < self.size[0]:
+ self.noHPV = True
+ if (border[3]-border[1])*1.5 < self.size[1]:
+ self.noVPV = True
+ self.border = [int(round(float(border[0])/float(self.image.size[0])*100, 2)*100*1.5),
+ int(round(float(border[1])/float(self.image.size[1])*100, 2)*100*1.5)]
+ else:
+ self.border = [0, 0]
+ self.noHPV = True
+ self.noVPV = True
return self.image
ratioDev = float(self.size[0]) / float(self.size[1])
if (float(self.image.size[0]) / float(self.image.size[1])) < ratioDev:
- if isSplit and landscapeMode:
- diff = int(self.image.size[1] * ratioDev) - self.image.size[0]
- self.image = ImageOps.expand(self.image, border=(diff / 2, 0), fill=fill)
- tempImg = Image.new(self.image.mode, (self.image.size[0] + diff, self.image.size[1]), fill)
- if toRight:
- tempImg.paste(self.image, (diff, 0))
- else:
- tempImg.paste(self.image, (0, 0))
- self.image = tempImg
- else:
- diff = int(self.image.size[1] * ratioDev) - self.image.size[0]
- self.image = ImageOps.expand(self.image, border=(diff / 2, 0), fill=fill)
+ diff = int(self.image.size[1] * ratioDev) - self.image.size[0]
+ self.image = ImageOps.expand(self.image, border=(diff / 2, 0), fill=fill)
elif (float(self.image.size[0]) / float(self.image.size[1])) > ratioDev:
diff = int(self.image.size[0] / ratioDev) - self.image.size[1]
self.image = ImageOps.expand(self.image, border=(0, diff / 2), fill=fill)
self.image = ImageOps.fit(self.image, size, method=method, centering=(0.5, 0.5))
+ if generateBorder:
+ if fill == 'white':
+ border = ImageOps.invert(self.image).getbbox()
+ else:
+ border = self.image.getbbox()
+ if border is not None:
+ if (border[2]-border[0])*1.5 < self.size[0]:
+ self.noHPV = True
+ if (border[3]-border[1])*1.5 < self.size[1]:
+ self.noVPV = True
+ self.border = [int(round(float(border[0])/float(self.image.size[0])*100, 2)*100*1.5),
+ int(round(float(border[1])/float(self.image.size[1])*100, 2)*100*1.5)]
+ else:
+ self.border = [0, 0]
+ self.noHPV = True
+ self.noVPV = True
return self.image
def splitPage(self, targetdir, righttoleft=False, rotate=False):
width, height = self.image.size
dstwidth, dstheight = self.size
- #print "Image is %d x %d" % (width,height)
- # only split if origin is not oriented the same as target
+ # Only split if origin is not oriented the same as target
if (width > height) != (dstwidth > dstheight):
if rotate:
self.image = self.image.rotate(90)
- return "R"
+ self.rotated = True
+ return None
else:
+ self.rotated = False
if width > height:
- # source is landscape, so split by the width
+ # Source is landscape, so split by the width
leftbox = (0, 0, width / 2, height)
rightbox = (width / 2, 0, width, height)
else:
- # source is portrait and target is landscape, so split by the height
+ # Source is portrait and target is landscape, so split by the height
leftbox = (0, 0, width, height / 2)
rightbox = (0, height / 2, width, height)
filename = os.path.splitext(self.filename)
@@ -256,6 +318,7 @@ class ComicPage:
raise RuntimeError('Cannot write image in directory %s: %s' % (targetdir, e))
return fileone, filetwo
else:
+ self.rotated = False
return None
def cutPageNumber(self):
@@ -351,4 +414,48 @@ class ComicPage:
# print "Right crop: %s"%diff
self.image = self.image.crop((0, 0, widthImg - diff, heightImg))
# print "New size: %sx%s"%(self.image.size[0],self.image.size[1])
- return self.image
\ No newline at end of file
+ return self.image
+
+ def getImageHistogram(self, image):
+ histogram = image.histogram()
+ RBGW = []
+ pixelCount = 0
+ for i in range(256):
+ pixelCount += histogram[i] + histogram[256 + i] + histogram[512 + i]
+ RBGW.append(histogram[i] + histogram[256 + i] + histogram[512 + i])
+ white = 0
+ black = 0
+ for i in range(245, 256):
+ white += RBGW[i]
+ for i in range(11):
+ black += RBGW[i]
+ if black > white and black > pixelCount*0.5:
+ return True
+ else:
+ return False
+
+ def getImageFill(self, isWebToon):
+ fill = 0
+ if isWebToon or self.rotated:
+ fill += self.getImageHistogram(self.image.crop((0, 0, self.image.size[0], 5)))
+ fill += self.getImageHistogram(self.image.crop((0, self.image.size[1]-5, self.image.size[0],
+ self.image.size[1])))
+ else:
+ fill += self.getImageHistogram(self.image.crop((0, 0, 5, self.image.size[1])))
+ fill += self.getImageHistogram(self.image.crop((self.image.size[0]-5, 0, self.image.size[0],
+ self.image.size[1])))
+ if fill == 2:
+ self.fill = 'black'
+ elif fill == 0:
+ self.fill = 'white'
+ else:
+ fill = 0
+ fill += self.getImageHistogram(self.image.crop((0, 0, 5, 5)))
+ fill += self.getImageHistogram(self.image.crop((self.image.size[0]-5, 0, self.image.size[0], 5)))
+ fill += self.getImageHistogram(self.image.crop((0, self.image.size[1]-5, 5, self.image.size[1])))
+ fill += self.getImageHistogram(self.image.crop((self.image.size[0]-5, self.image.size[1]-5,
+ self.image.size[0], self.image.size[1])))
+ if fill > 1:
+ self.fill = 'black'
+ else:
+ self.fill = 'white'
\ No newline at end of file
diff --git a/kcc/kindlesplit.py b/kcc/kindlesplit.py
new file mode 100644
index 0000000..c8ae74d
--- /dev/null
+++ b/kcc/kindlesplit.py
@@ -0,0 +1,384 @@
+# Based on initial version of KindleUnpack. Copyright (C) 2009 Charles M. Hannum
+# Improvements Copyright (C) 2009-2012 P. Durrant, K. Hendricks, S. Siebert, fandrieu, DiapDealer, nickredding
+# Copyright (C) 2013 Pawel Jastrzebski
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+__license__ = 'ISC'
+__copyright__ = '2012-2013, Ciro Mattia Gonano , Pawel Jastrzebski '
+__docformat__ = 'restructuredtext en'
+
+import struct
+# from uuid import uuid4
+
+# important pdb header offsets
+unique_id_seed = 68
+number_of_pdb_records = 76
+
+# important palmdoc header offsets
+book_length = 4
+book_record_count = 8
+first_pdb_record = 78
+
+# important rec0 offsets
+length_of_book = 4
+mobi_header_base = 16
+mobi_header_length = 20
+mobi_type = 24
+mobi_version = 36
+first_non_text = 80
+title_offset = 84
+first_image_record = 108
+first_content_index = 192
+last_content_index = 194
+kf8_last_content_index = 192 # for KF8 mobi headers
+fcis_index = 200
+flis_index = 208
+srcs_index = 224
+srcs_count = 228
+primary_index = 244
+datp_index = 256
+huffoff = 112
+hufftbloff = 120
+
+
+def getint(datain, ofs, sz='L'):
+ i, = struct.unpack_from('>'+sz, datain, ofs)
+ return i
+
+
+def writeint(datain, ofs, n, length='L'):
+ if length == 'L':
+ return datain[:ofs]+struct.pack('>L', n)+datain[ofs+4:]
+ else:
+ return datain[:ofs]+struct.pack('>H', n)+datain[ofs+2:]
+
+
+def getsecaddr(datain, secno):
+ nsec = getint(datain, number_of_pdb_records, 'H')
+ assert secno >= 0 & secno < nsec, 'secno %d out of range (nsec=%d)' % (secno, nsec)
+ secstart = getint(datain, first_pdb_record+secno*8)
+ if secno == nsec-1:
+ secend = len(datain)
+ else:
+ secend = getint(datain, first_pdb_record+(secno+1)*8)
+ return secstart, secend
+
+
+def readsection(datain, secno):
+ secstart, secend = getsecaddr(datain, secno)
+ return datain[secstart:secend]
+
+
+def writesection(datain, secno, secdata): # overwrite, accounting for different length
+ dataout = deletesectionrange(datain, secno, secno)
+ return insertsection(dataout, secno, secdata)
+
+
+def nullsection(datain, secno): # make it zero-length without deleting it
+ datalst = []
+ nsec = getint(datain, number_of_pdb_records, 'H')
+ secstart, secend = getsecaddr(datain, secno)
+ zerosecstart, zerosecend = getsecaddr(datain, 0)
+ dif = secend-secstart
+ datalst.append(datain[:first_pdb_record])
+ for i in range(0, secno+1):
+ ofs, flgval = struct.unpack_from('>2L', datain, first_pdb_record+i*8)
+ datalst.append(struct.pack('>L', ofs) + struct.pack('>L', flgval))
+ for i in range(secno+1, nsec):
+ ofs, flgval = struct.unpack_from('>2L', datain, first_pdb_record+i*8)
+ ofs -= dif
+ datalst.append(struct.pack('>L', ofs) + struct.pack('>L', flgval))
+ lpad = zerosecstart - (first_pdb_record + 8*nsec)
+ if lpad > 0:
+ datalst.append('\0' * lpad)
+ datalst.append(datain[zerosecstart: secstart])
+ datalst.append(datain[secend:])
+ dataout = "".join(datalst)
+ return dataout
+
+
+def deletesectionrange(datain, firstsec, lastsec): # delete a range of sections
+ datalst = []
+ firstsecstart, firstsecend = getsecaddr(datain, firstsec)
+ lastsecstart, lastsecend = getsecaddr(datain, lastsec)
+ zerosecstart, zerosecend = getsecaddr(datain, 0)
+ dif = lastsecend - firstsecstart + 8*(lastsec-firstsec+1)
+ nsec = getint(datain, number_of_pdb_records, 'H')
+ datalst.append(datain[:unique_id_seed])
+ datalst.append(struct.pack('>L', 2*(nsec-(lastsec-firstsec+1))+1))
+ datalst.append(datain[unique_id_seed+4:number_of_pdb_records])
+ datalst.append(struct.pack('>H', nsec-(lastsec-firstsec+1)))
+ newstart = zerosecstart - 8*(lastsec-firstsec+1)
+ for i in range(0, firstsec):
+ ofs, flgval = struct.unpack_from('>2L', datain, first_pdb_record+i*8)
+ ofs -= 8 * (lastsec - firstsec + 1)
+ datalst.append(struct.pack('>L', ofs) + struct.pack('>L', flgval))
+ for i in range(lastsec+1, nsec):
+ ofs, flgval = struct.unpack_from('>2L', datain, first_pdb_record+i*8)
+ ofs -= dif
+ flgval = 2*(i-(lastsec-firstsec+1))
+ datalst.append(struct.pack('>L', ofs) + struct.pack('>L', flgval))
+ lpad = newstart - (first_pdb_record + 8*(nsec - (lastsec - firstsec + 1)))
+ if lpad > 0:
+ datalst.append('\0' * lpad)
+ datalst.append(datain[zerosecstart:firstsecstart])
+ datalst.append(datain[lastsecend:])
+ dataout = "".join(datalst)
+ return dataout
+
+
+def insertsection(datain, secno, secdata): # insert a new section
+ datalst = []
+ nsec = getint(datain, number_of_pdb_records, 'H')
+ secstart, secend = getsecaddr(datain, secno)
+ zerosecstart, zerosecend = getsecaddr(datain, 0)
+ dif = len(secdata)
+ datalst.append(datain[:unique_id_seed])
+ datalst.append(struct.pack('>L', 2*(nsec+1)+1))
+ datalst.append(datain[unique_id_seed+4:number_of_pdb_records])
+ datalst.append(struct.pack('>H', nsec+1))
+ newstart = zerosecstart + 8
+ for i in range(0, secno):
+ ofs, flgval = struct.unpack_from('>2L', datain, first_pdb_record+i*8)
+ ofs += 8
+ datalst.append(struct.pack('>L', ofs) + struct.pack('>L', flgval))
+ datalst.append(struct.pack('>L', secstart + 8) + struct.pack('>L', (2*secno)))
+ for i in range(secno, nsec):
+ ofs, flgval = struct.unpack_from('>2L', datain, first_pdb_record+i*8)
+ ofs = ofs + dif + 8
+ flgval = 2*(i+1)
+ datalst.append(struct.pack('>L', ofs) + struct.pack('>L', flgval))
+ lpad = newstart - (first_pdb_record + 8*(nsec + 1))
+ if lpad > 0:
+ datalst.append('\0' * lpad)
+ datalst.append(datain[zerosecstart:secstart])
+ datalst.append(secdata)
+ datalst.append(datain[secstart:])
+ dataout = "".join(datalst)
+ return dataout
+
+
+def insertsectionrange(sectionsource, firstsec, lastsec, sectiontarget, targetsec): # insert a range of sections
+ dataout = sectiontarget
+ for idx in range(lastsec, firstsec-1, -1):
+ dataout = insertsection(dataout, targetsec, readsection(sectionsource, idx))
+ return dataout
+
+
+def get_exth_params(rec0):
+ ebase = mobi_header_base + getint(rec0, mobi_header_length)
+ elen = getint(rec0, ebase+4)
+ enum = getint(rec0, ebase+8)
+ return ebase, elen, enum
+
+
+def add_exth(rec0, exth_num, exth_bytes):
+ ebase, elen, enum = get_exth_params(rec0)
+ newrecsize = 8+len(exth_bytes)
+ newrec0 = rec0[0:ebase+4]+struct.pack('>L', elen+newrecsize)+struct.pack('>L', enum+1) +\
+ struct.pack('>L', exth_num) + struct.pack('>L', newrecsize)+exth_bytes+rec0[ebase+12:]
+ newrec0 = writeint(newrec0, title_offset, getint(newrec0, title_offset)+newrecsize)
+ return newrec0
+
+
+def read_exth(rec0, exth_num):
+ exth_values = []
+ ebase, elen, enum = get_exth_params(rec0)
+ ebase += 12
+ while enum > 0:
+ exth_id = getint(rec0, ebase)
+ if exth_id == exth_num:
+ # We might have multiple exths, so build a list.
+ exth_values.append(rec0[ebase+8:ebase+getint(rec0, ebase+4)])
+ enum -= 1
+ ebase = ebase+getint(rec0, ebase+4)
+ return exth_values
+
+
+def write_exth(rec0, exth_num, exth_bytes):
+ ebase, elen, enum = get_exth_params(rec0)
+ ebase_idx = ebase+12
+ enum_idx = enum
+ while enum_idx > 0:
+ exth_id = getint(rec0, ebase_idx)
+ if exth_id == exth_num:
+ dif = len(exth_bytes)+8-getint(rec0, ebase_idx+4)
+ newrec0 = rec0
+ if dif != 0:
+ newrec0 = writeint(newrec0, title_offset, getint(newrec0, title_offset)+dif)
+ return newrec0[:ebase+4]+struct.pack('>L', elen+len(exth_bytes)+8-getint(rec0, ebase_idx+4)) +\
+ struct.pack('>L', enum)+rec0[ebase+12:ebase_idx+4] +\
+ struct.pack('>L', len(exth_bytes)+8)+exth_bytes +\
+ rec0[ebase_idx+getint(rec0, ebase_idx+4):]
+ enum_idx -= 1
+ ebase_idx = ebase_idx+getint(rec0, ebase_idx+4)
+ return rec0
+
+
+def del_exth(rec0, exth_num):
+ ebase, elen, enum = get_exth_params(rec0)
+ ebase_idx = ebase+12
+ enum_idx = 0
+ while enum_idx < enum:
+ exth_id = getint(rec0, ebase_idx)
+ exth_size = getint(rec0, ebase_idx+4)
+ if exth_id == exth_num:
+ newrec0 = rec0
+ newrec0 = writeint(newrec0, title_offset, getint(newrec0, title_offset)-exth_size)
+ newrec0 = newrec0[:ebase_idx]+newrec0[ebase_idx+exth_size:]
+ newrec0 = newrec0[0:ebase+4]+struct.pack('>L', elen-exth_size)+struct.pack('>L', enum-1)+newrec0[ebase+12:]
+ return newrec0
+ enum_idx += 1
+ ebase_idx = ebase_idx+exth_size
+ return rec0
+
+
+class mobi_split:
+ def __init__(self, infile, newKindle):
+ try:
+ datain = open(infile, 'rb').read()
+ datain_rec0 = readsection(datain, 0)
+ ver = getint(datain_rec0, mobi_version)
+ # fake_asin = str(uuid4())
+ self.combo = (ver != 8)
+ if not self.combo:
+ return
+ exth121 = read_exth(datain_rec0, 121)
+ if len(exth121) == 0:
+ self.combo = False
+ return
+ else:
+ # only pay attention to first exth121
+ # (there should only be one)
+ datain_kf8, = struct.unpack_from('>L', exth121[0], 0)
+ if datain_kf8 == 0xffffffff:
+ self.combo = False
+ return
+ datain_kfrec0 = readsection(datain, datain_kf8)
+ firstimage = getint(datain_rec0, first_image_record)
+ lastimage = getint(datain_rec0, last_content_index, 'H')
+
+ if not newKindle:
+ # create the standalone mobi7
+ num_sec = getint(datain, number_of_pdb_records, 'H')
+ # remove BOUNDARY up to but not including ELF record
+ self.result_file = deletesectionrange(datain, datain_kf8-1, num_sec-2)
+ # check if there are SRCS records and delete them
+ srcs = getint(datain_rec0, srcs_index)
+ num_srcs = getint(datain_rec0, srcs_count)
+ if srcs != 0xffffffff and num_srcs > 0:
+ self.result_file = deletesectionrange(self.result_file, srcs, srcs+num_srcs-1)
+ datain_rec0 = writeint(datain_rec0, srcs_index, 0xffffffff)
+ datain_rec0 = writeint(datain_rec0, srcs_count, 0)
+ # reset the EXTH 121 KF8 Boundary meta data to 0xffffffff
+ datain_rec0 = write_exth(datain_rec0, 121, struct.pack('>L', 0xffffffff))
+ # datain_rec0 = del_exth(datain_rec0,121)
+ # datain_rec0 = del_exth(datain_rec0,534)
+ # don't remove the EXTH 125 KF8 Count of Resources, seems to be present in mobi6 files as well
+ # set the EXTH 129 KF8 Masthead / Cover Image string to the null string
+ datain_rec0 = write_exth(datain_rec0, 129, '')
+ # don't remove the EXTH 131 KF8 Unidentified Count, seems to be present in mobi6 files as well
+
+ # Make sure we have an ASIN & cdeType set...
+ # if len(read_exth(datain_rec0, 113)) == 0:
+ # datain_rec0 = add_exth(datain_rec0, 113, fake_asin)
+ # if len(read_exth(datain_rec0, 504)) == 0:
+ # datain_rec0 = add_exth(datain_rec0, 504, fake_asin)
+ if len(read_exth(datain_rec0, 501)) == 0:
+ datain_rec0 = add_exth(datain_rec0, 501, b'EBOK')
+
+ # need to reset flags stored in 0x80-0x83
+ # old mobi with exth: 0x50, mobi7 part with exth: 0x1850, mobi8 part with exth: 0x1050
+ # Bit Flags
+ # 0x1000 = Bit 12 indicates if embedded fonts are used or not
+ # 0x0800 = means this Header points to *shared* images/resource/fonts ??
+ # 0x0080 = unknown new flag, why is this now being set by Kindlegen 2.8?
+ # 0x0040 = exth exists
+ # 0x0010 = Not sure but this is always set so far
+ fval, = struct.unpack_from('>L', datain_rec0, 0x80)
+ # need to remove flag 0x0800 for KindlePreviewer 2.8 and unset Bit 12 for embedded fonts
+ fval &= 0x07FF
+ datain_rec0 = datain_rec0[:0x80] + struct.pack('>L', fval) + datain_rec0[0x84:]
+ self.result_file = writesection(self.result_file, 0, datain_rec0)
+ if lastimage == 0xffff:
+ # find the lowest of the next sections and copy up to that.
+ ofs_list = [(kf8_last_content_index, 'L'), (fcis_index, 'L'), (flis_index, 'L'), (datp_index, 'L'),
+ (hufftbloff, 'L')]
+ for ofs, sz in ofs_list:
+ n = getint(datain_kfrec0, ofs, sz)
+ if 0 < n < lastimage:
+ lastimage = n-1
+
+ # Try to null out FONT and RES, but leave the (empty) PDB record so image refs remain valid
+ for i in range(firstimage, lastimage):
+ imgsec = readsection(self.result_file, i)
+ if imgsec[0:4] in ['RESC', 'FONT']:
+ self.result_file = nullsection(self.result_file, i)
+ # mobi7 finished
+ else:
+ # create standalone mobi8
+ self.result_file = deletesectionrange(datain, 0, datain_kf8-1)
+ target = getint(datain_kfrec0, first_image_record)
+ self.result_file = insertsectionrange(datain, firstimage, lastimage, self.result_file, target)
+ datain_kfrec0 = readsection(self.result_file, 0)
+
+ # Only keep the correct EXTH 116 StartOffset, KG 2.5 carries over the one from the mobi7 part,
+ # which then points at garbage in the mobi8 part, and confuses FW 3.4
+ kf8starts = read_exth(datain_kfrec0, 116)
+ # If we have multiple StartOffset, keep only the last one
+ kf8start_count = len(kf8starts)
+ while kf8start_count > 1:
+ kf8start_count -= 1
+ datain_kfrec0 = del_exth(datain_kfrec0, 116)
+
+ # update the EXTH 125 KF8 Count of Images/Fonts/Resources
+ datain_kfrec0 = write_exth(datain_kfrec0, 125, struct.pack('>L', lastimage-firstimage+1))
+
+ # Same dance for the KF8, we want an ASIN & cdeType :)
+ # if len(read_exth(datain_kfrec0, 113)) == 0:
+ # datain_kfrec0 = add_exth(datain_kfrec0, 113, fake_asin)
+ # if len(read_exth(datain_kfrec0, 504)) == 0:
+ # datain_kfrec0 = add_exth(datain_kfrec0, 504, fake_asin)
+ if len(read_exth(datain_kfrec0, 501)) == 0:
+ datain_kfrec0 = add_exth(datain_kfrec0, 501, b'EBOK')
+
+ # need to reset flags stored in 0x80-0x83
+ # old mobi with exth: 0x50, mobi7 part with exth: 0x1850, mobi8 part with exth: 0x1050
+ # standalone mobi8 with exth: 0x0050
+ # Bit Flags
+ # 0x1000 = Bit 12 indicates if embedded fonts are used or not
+ # 0x0800 = means this Header points to *shared* images/resource/fonts ??
+ # 0x0080 = unknown new flag, why is this now being set by Kindlegen 2.8?
+ # 0x0040 = exth exists
+ # 0x0010 = Not sure but this is always set so far
+ fval, = struct.unpack_from('>L', datain_kfrec0, 0x80)
+ fval &= 0x1FFF
+ fval |= 0x0800
+ datain_kfrec0 = datain_kfrec0[:0x80] + struct.pack('>L', fval) + datain_kfrec0[0x84:]
+
+ # properly update other index pointers that have been shifted by the insertion of images
+ ofs_list = [(kf8_last_content_index, 'L'), (fcis_index, 'L'), (flis_index, 'L'), (datp_index, 'L'),
+ (hufftbloff, 'L')]
+ for ofs, sz in ofs_list:
+ n = getint(datain_kfrec0, ofs, sz)
+ if n != 0xffffffff:
+ datain_kfrec0 = writeint(datain_kfrec0, ofs, n+lastimage-firstimage+1, sz)
+ self.result_file = writesection(self.result_file, 0, datain_kfrec0)
+ # mobi8 finished
+ except Exception:
+ raise
+
+ def getResult(self):
+ return self.result_file
\ No newline at end of file
diff --git a/kcc/kindlestrip.py b/kcc/kindlestrip.py
deleted file mode 100755
index 4aea003..0000000
--- a/kcc/kindlestrip.py
+++ /dev/null
@@ -1,236 +0,0 @@
-#!/usr/bin/env python
-# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
-#
-# This is a python script. You need a Python interpreter to run it.
-# For example, ActiveState Python, which exists for windows.
-#
-# This script strips the penultimate record from a Mobipocket file.
-# This is useful because the current KindleGen add a compressed copy
-# of the source files used in this record, making the ebook produced
-# about twice as big as it needs to be.
-#
-#
-# This is free and unencumbered software released into the public domain.
-#
-# Anyone is free to copy, modify, publish, use, compile, sell, or
-# distribute this software, either in source code form or as a compiled
-# binary, for any purpose, commercial or non-commercial, and by any
-# means.
-#
-# In jurisdictions that recognize copyright laws, the author or authors
-# of this software dedicate any and all copyright interest in the
-# software to the public domain. We make this dedication for the benefit
-# of the public at large and to the detriment of our heirs and
-# successors. We intend this dedication to be an overt act of
-# relinquishment in perpetuity of all present and future rights to this
-# software under copyright law.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
-# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
-# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-# OTHER DEALINGS IN THE SOFTWARE.
-#
-# For more information, please refer to
-#
-# Written by Paul Durrant, 2010-2011, paul@durrant.co.uk, pdurrant on mobileread.com
-# With enhancements by Kevin Hendricks, KevinH on mobileread.com
-#
-# Changelog
-# 1.00 - Initial version
-# 1.10 - Added an option to output the stripped data
-# 1.20 - Added check for source files section (thanks Piquan)
-# 1.30 - Added prelim Support for K8 style mobis
-# 1.31 - removed the SRCS section but kept a 0 size entry for it
-# 1.32 - removes the SRCS section and its entry, now updates metadata 121 if needed
-# 1.33 - now uses and modifies mobiheader SRCS and CNT
-# 1.34 - added credit for Kevin Hendricks
-# 1.35 - fixed bug when more than one compilation (SRCS/CMET) records
-
-__version__ = '1.35'
-
-import sys
-import struct
-import binascii
-
-class Unbuffered:
- def __init__(self, stream):
- self.stream = stream
- def write(self, data):
- self.stream.write(data)
- self.stream.flush()
- def __getattr__(self, attr):
- return getattr(self.stream, attr)
-
-
-class StripException(Exception):
- pass
-
-
-class SectionStripper:
- def loadSection(self, section):
- if (section + 1 == self.num_sections):
- endoff = len(self.data_file)
- else:
- endoff = self.sections[section + 1][0]
- off = self.sections[section][0]
- return self.data_file[off:endoff]
-
- def patch(self, off, new):
- self.data_file = self.data_file[:off] + new + self.data_file[off+len(new):]
-
- def strip(self, off, len):
- self.data_file = self.data_file[:off] + self.data_file[off+len:]
-
- def patchSection(self, section, new, in_off = 0):
- if (section + 1 == self.num_sections):
- endoff = len(self.data_file)
- else:
- endoff = self.sections[section + 1][0]
- off = self.sections[section][0]
- assert off + in_off + len(new) <= endoff
- self.patch(off + in_off, new)
-
- def updateEXTH121(self, srcs_secnum, srcs_cnt, mobiheader):
- mobi_length, = struct.unpack('>L',mobiheader[0x14:0x18])
- exth_flag, = struct.unpack('>L', mobiheader[0x80:0x84])
- exth = 'NONE'
- try:
- if exth_flag & 0x40:
- exth = mobiheader[16 + mobi_length:]
- if (len(exth) >= 4) and (exth[:4] == 'EXTH'):
- nitems, = struct.unpack('>I', exth[8:12])
- pos = 12
- for i in xrange(nitems):
- type, size = struct.unpack('>II', exth[pos: pos + 8])
- # print type, size
- if type == 121:
- boundaryptr, =struct.unpack('>L',exth[pos+8: pos + size])
- if srcs_secnum <= boundaryptr:
- boundaryptr -= srcs_cnt
- prefix = mobiheader[0:16 + mobi_length + pos + 8]
- suffix = mobiheader[16 + mobi_length + pos + 8 + 4:]
- nval = struct.pack('>L',boundaryptr)
- mobiheader = prefix + nval + suffix
- pos += size
- except:
- pass
- return mobiheader
-
- def __init__(self, datain):
- if datain[0x3C:0x3C+8] != 'BOOKMOBI':
- raise StripException("invalid file format")
- self.num_sections, = struct.unpack('>H', datain[76:78])
-
- # get mobiheader and check SRCS section number and count
- offset0, = struct.unpack_from('>L', datain, 78)
- offset1, = struct.unpack_from('>L', datain, 86)
- mobiheader = datain[offset0:offset1]
- srcs_secnum, srcs_cnt = struct.unpack_from('>2L', mobiheader, 0xe0)
- if srcs_secnum == 0xffffffff or srcs_cnt == 0:
- raise StripException("File doesn't contain the sources section.")
-
- print "Found SRCS section number %d, and count %d" % (srcs_secnum, srcs_cnt)
- # find its offset and length
- next = srcs_secnum + srcs_cnt
- srcs_offset, flgval = struct.unpack_from('>2L', datain, 78+(srcs_secnum*8))
- next_offset, flgval = struct.unpack_from('>2L', datain, 78+(next*8))
- srcs_length = next_offset - srcs_offset
- if datain[srcs_offset:srcs_offset+4] != 'SRCS':
- raise StripException("SRCS section num does not point to SRCS.")
- print " beginning at offset %0x and ending at offset %0x" % (srcs_offset, srcs_length)
-
- # it appears bytes 68-71 always contain (2*num_sections) + 1
- # this is not documented anyplace at all but it appears to be some sort of next
- # available unique_id used to identify specific sections in the palm db
- self.data_file = datain[:68] + struct.pack('>L',((self.num_sections-srcs_cnt)*2+1))
- self.data_file += datain[72:76]
-
- # write out the number of sections reduced by srtcs_cnt
- self.data_file = self.data_file + struct.pack('>H',self.num_sections-srcs_cnt)
-
- # we are going to remove srcs_cnt SRCS sections so the offset of every entry in the table
- # up to the srcs secnum must begin 8 bytes earlier per section removed (each table entry is 8 )
- delta = -8 * srcs_cnt
- for i in xrange(srcs_secnum):
- offset, flgval = struct.unpack_from('>2L', datain, 78+(i*8))
- offset += delta
- self.data_file += struct.pack('>L',offset) + struct.pack('>L',flgval)
-
- # for every record after the srcs_cnt SRCS records we must start it
- # earlier by 8*srcs_cnt + the length of the srcs sections themselves)
- delta = delta - srcs_length
- for i in xrange(srcs_secnum+srcs_cnt,self.num_sections):
- offset, flgval = struct.unpack_from('>2L', datain, 78+(i*8))
- offset += delta
- flgval = 2 * (i - srcs_cnt)
- self.data_file += struct.pack('>L',offset) + struct.pack('>L',flgval)
-
- # now pad it out to begin right at the first offset
- # typically this is 2 bytes of nulls
- first_offset, flgval = struct.unpack_from('>2L', self.data_file, 78)
- self.data_file += '\0' * (first_offset - len(self.data_file))
-
- # now finally add on every thing up to the original src_offset
- self.data_file += datain[offset0: srcs_offset]
-
- # and everything afterwards
- self.data_file += datain[srcs_offset+srcs_length:]
-
- #store away the SRCS section in case the user wants it output
- self.stripped_data_header = datain[srcs_offset:srcs_offset+16]
- self.stripped_data = datain[srcs_offset+16:srcs_offset+srcs_length]
-
- # update the number of sections count
- self.num_section = self.num_sections - srcs_cnt
-
- # update the srcs_secnum and srcs_cnt in the mobiheader
- offset0, flgval0 = struct.unpack_from('>2L', self.data_file, 78)
- offset1, flgval1 = struct.unpack_from('>2L', self.data_file, 86)
- mobiheader = self.data_file[offset0:offset1]
- mobiheader = mobiheader[:0xe0]+ struct.pack('>L', 0xffffffff) + struct.pack('>L', 0) + mobiheader[0xe8:]
-
- # if K8 mobi, handle metadata 121 in old mobiheader
- mobiheader = self.updateEXTH121(srcs_secnum, srcs_cnt, mobiheader)
- self.data_file = self.data_file[0:offset0] + mobiheader + self.data_file[offset1:]
- print "done"
-
- def getResult(self):
- return self.data_file
-
- def getStrippedData(self):
- return self.stripped_data
-
- def getHeader(self):
- return self.stripped_data_header
-
-def main(argv=None):
- infile = argv[0]
- outfile = argv[1]
- data_file = file(infile, 'rb').read()
- try:
- strippedFile = SectionStripper(data_file)
- file(outfile, 'wb').write(strippedFile.getResult())
- print "Header Bytes: " + binascii.b2a_hex(strippedFile.getHeader())
- if len(argv)==3:
- file(argv[2], 'wb').write(strippedFile.getStrippedData())
- except StripException, e:
- print "Error: %s" % e
- sys.exit(1)
-
-if __name__ == "__main__":
- sys.stdout=Unbuffered(sys.stdout)
- print ('KindleStrip v%(__version__)s. '
- 'Written 2010-2012 by Paul Durrant and Kevin Hendricks.' % globals())
- if len(sys.argv)<3 or len(sys.argv)>4:
- print "Strips the Sources record from Mobipocket ebooks"
- print "For ebooks generated using KindleGen 1.1 and later that add the source"
- print "Usage:"
- print " %s " % sys.argv[0]
- print " is optional."
- sys.exit(1)
- else:
- main(sys.argv[1:])
- sys.exit(0)
diff --git a/kcc/pdfjpgextract.py b/kcc/pdfjpgextract.py
index dd5f067..d9dad09 100644
--- a/kcc/pdfjpgextract.py
+++ b/kcc/pdfjpgextract.py
@@ -1,4 +1,5 @@
-# Copyright (c) 2012 Ciro Mattia Gonano
+# Copyright (c) 2012-2013 Ciro Mattia Gonano
+# Copyright (c) 2013 Pawel Jastrzebski
#
# Based upon the code snippet by Ned Batchelder
# (http://nedbatchelder.com/blog/200712/extracting_jpgs_from_pdfs.html)
diff --git a/other/Additional-LICENSE.txt b/other/Additional-LICENSE.txt
new file mode 100644
index 0000000..891d453
--- /dev/null
+++ b/other/Additional-LICENSE.txt
@@ -0,0 +1,91 @@
+ ****** ***** ****** UnRAR - free utility for RAR archives
+ ** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ****** ******* ****** License for use and distribution of
+ ** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ** ** ** ** ** ** FREEWARE version
+ ~~~~~~~~~~~~~~~~
+
+ The UnRAR utility is freeware. This means:
+
+ 1. All copyrights to RAR and the utility UnRAR are exclusively
+ owned by the author - Alexander Roshal.
+
+ 2. The UnRAR utility may be freely distributed. It is allowed
+ to distribute UnRAR inside of other software packages.
+
+ 3. THE RAR ARCHIVER AND THE UnRAR UTILITY ARE DISTRIBUTED "AS IS".
+ NO WARRANTY OF ANY KIND IS EXPRESSED OR IMPLIED. YOU USE AT
+ YOUR OWN RISK. THE AUTHOR WILL NOT BE LIABLE FOR DATA LOSS,
+ DAMAGES, LOSS OF PROFITS OR ANY OTHER KIND OF LOSS WHILE USING
+ OR MISUSING THIS SOFTWARE.
+
+ 4. Neither RAR binary code, WinRAR binary code, UnRAR source or UnRAR
+ binary code may be used or reverse engineered to re-create the RAR
+ compression algorithm, which is proprietary, without written
+ permission of the author.
+
+ 5. If you don't agree with terms of the license you must remove
+ UnRAR files from your storage devices and cease to use the
+ utility.
+
+ Thank you for your interest in RAR and UnRAR.
+
+
+ Alexander L. Roshal
+
+ 7-Zip
+ ~~~~~
+ License for use and distribution
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ 7-Zip Copyright (C) 1999-2012 Igor Pavlov.
+
+ Licenses for files are:
+
+ 1) 7z.dll: GNU LGPL + unRAR restriction
+ 2) All other files: GNU LGPL
+
+ The GNU LGPL + unRAR restriction means that you must follow both
+ GNU LGPL rules and unRAR restriction rules.
+
+
+ Note:
+ You can use 7-Zip on any computer, including a computer in a commercial
+ organization. You don't need to register or pay for 7-Zip.
+
+
+ GNU LGPL information
+ --------------------
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You can receive a copy of the GNU Lesser General Public License from
+ http://www.gnu.org/
+
+
+ unRAR restriction
+ -----------------
+
+ The decompression engine for RAR archives was developed using source
+ code of unRAR program.
+ All copyrights to original unRAR code are owned by Alexander Roshal.
+
+ The license for original unRAR code has the following restriction:
+
+ The unRAR sources cannot be used to re-create the RAR compression algorithm,
+ which is proprietary. Distribution of modified unRAR sources in separate form
+ or as a part of other software is permitted, provided that it is clearly
+ stated in the documentation and source comments that the code may
+ not be used to develop a RAR (WinRAR) compatible archiver.
+
+
+ --
+ Igor Pavlov
\ No newline at end of file
diff --git a/setup.py b/setup.py
index c17d4e6..35edd24 100644
--- a/setup.py
+++ b/setup.py
@@ -10,7 +10,7 @@ Usage (Windows):
from sys import platform
NAME = "KindleComicConverter"
-VERSION = "3.2.1"
+VERSION = "3.3"
MAIN = "kcc.py"
if platform == "darwin":
@@ -41,7 +41,11 @@ elif platform == "win32":
from cx_Freeze import setup, Executable
base = "Win32GUI"
extra_options = dict(
- options={"build_exe": {"include_files": ['LICENSE.txt'], "compressed": True}},
+ options={"build_exe": {"include_files": ['LICENSE.txt',
+ ['other/UnRAR.exe', 'UnRAR.exe'],
+ ['other/7za.exe', '7za.exe'],
+ ['other/Additional-LICENSE.txt', 'Additional-LICENSE.txt']
+ ], "compressed": True}},
executables=[Executable(MAIN,
base=base,
targetName="KCC.exe",