1
0
mirror of https://github.com/ciromattia/kcc synced 2026-04-15 13:38:46 +00:00

Compare commits

...

256 Commits
2.6 ... 3.1

Author SHA1 Message Date
Paweł Jastrzębski
9d267a6cc4 Updated README and version bump 2013-07-22 08:43:35 +02:00
Paweł Jastrzębski
462f24149b Implemented Devernay idea 2013-07-22 08:32:59 +02:00
Paweł Jastrzębski
4744b62f91 Interruption fix 2013-07-21 20:25:14 +02:00
Paweł Jastrzębski
08244e7fdc Conversion can be now interrupted 2013-07-21 20:18:04 +02:00
Paweł Jastrzębski
f64fb1bee1 Multiselect - OSX fix 2013-07-21 19:13:39 +02:00
Paweł Jastrzębski
743c3b2466 Added multiselect to add file/directory dialogs 2013-07-21 18:24:24 +02:00
Paweł Jastrzębski
aefa36fef8 Refactored KindleGen error handling 2013-07-11 13:25:32 +02:00
Paweł Jastrzębski
d7f6503196 Added profile: Kindle for Android 2013-07-11 11:47:18 +02:00
Paweł Jastrzębski
3375b8c898 Don't stop conversion if KindleGen return only warnings 2013-07-11 11:01:35 +02:00
Paweł Jastrzębski
52e5919ccd Bucket of dirty hax to improve readability on Linux/OSX 2013-07-10 09:05:44 +02:00
Paweł Jastrzębski
1c2e57adb6 Updated status messages 2013-07-10 08:50:52 +02:00
Paweł Jastrzębski
2e99d6ee0a Updated status messages 2013-07-10 08:29:14 +02:00
Paweł Jastrzębski
6744815f77 Fixed JobList scroll bars 2013-07-10 08:24:13 +02:00
Paweł Jastrzębski
0ba44ab2d3 HTML tag support for addMessage() 2013-07-10 07:51:19 +02:00
Paweł Jastrzębski
a6006450de Improved upscale/stretch options handling 2013-07-08 11:07:37 +02:00
Paweł Jastrzębski
c20e2ba451 Fixed row alignment (#55) 2013-07-04 15:32:15 +02:00
Paweł Jastrzębski
7a0b387c1c Small tweak of NoSplit option 2013-07-04 15:05:16 +02:00
Ciro Mattia Gonano
fdfe5fbe39 Disable horizontal mode check when "no split/rotate" is checked. 2013-07-04 11:50:20 +02:00
Paweł Jastrzębski
35751efad5 Little tweaks 2013-06-28 00:08:36 +02:00
Mateusz
7005c9e40a Fix misleading message when processing empty directories 2013-06-27 23:40:30 +02:00
Paweł Jastrzębski
118cf25ff7 Older UnRAR exit code is 7 when it is called without parameters 2013-06-27 18:13:39 +02:00
Ciro Mattia Gonano
01f65220ec Merge pull request #54 from ciromattia/new-gui
KCC 3.0 - New GUI
2013-06-26 03:21:35 -07:00
Paweł Jastrzębski
078c7b78da Last used path - Bugfix 2013-06-25 16:38:21 +02:00
Paweł Jastrzębski
25f00db9cf EPUBs created on Windows are now valid ePub 2.0 2013-06-25 16:32:50 +02:00
Ciro Mattia Gonano
576ee17c4d Use caps for PATH 2013-06-25 11:51:46 +02:00
Ciro Mattia Gonano
e7f654f56a Remove URL 2013-06-25 11:40:23 +02:00
Ciro Mattia Gonano
014277b574 Messages adjust 2013-06-25 11:24:53 +02:00
Paweł Jastrzębski
043a49152f Fixed K1/K2/KDX/KDXG profiles 2013-06-24 19:11:04 +02:00
Ciro Mattia Gonano
40592d197d Merge branch 'new-gui' of github.com:ciromattia/kcc into new-gui 2013-06-24 12:30:16 +02:00
Paweł Jastrzębski
80e3d0eab4 Refactored KindleGen version check 2013-06-24 11:40:09 +02:00
Ciro Mattia Gonano
40da090fe8 Merge branch 'new-gui' of github.com:ciromattia/kcc into new-gui 2013-06-24 10:56:46 +02:00
Paweł Jastrzębski
795ccd9782 Small tweaks 2013-06-23 11:20:30 +02:00
Paweł Jastrzębski
57bfd6b968 Updated README 2013-06-22 20:02:12 +02:00
Paweł Jastrzębski
02d1fe308d Small cleanup 2013-06-21 18:30:11 +02:00
Paweł Jastrzębski
3e117f46d5 Added KindleGen version check 2013-06-21 14:35:06 +02:00
Paweł Jastrzębski
dbb14e37fa Updated setup.py - OSX use py2app again 2013-06-21 13:23:14 +02:00
Ciro Mattia Gonano
8f2b534a68 Merge branch 'new-gui' of github.com:ciromattia/kcc into new-gui 2013-06-20 12:51:53 +02:00
Ciro Mattia Gonano
224d2b89b1 Merge branch 'new-gui' of github.com:ciromattia/kcc into new-gui 2013-06-20 12:51:47 +02:00
Paweł Jastrzębski
4c2b5b010b Expert mode tweaks 2013-06-20 12:31:58 +02:00
Paweł Jastrzębski
b98a1fa100 Using non-existent options is also not very bright 2013-06-19 23:45:30 +02:00
Paweł Jastrzębski
bb51b53e11 Forbidding OSX/Linux to open subdirectories is not idea of the year 2013-06-19 14:33:51 +02:00
Paweł Jastrzębski
19bd2431aa OSX GUI fixes 2013-06-19 14:02:15 +02:00
Paweł Jastrzębski
977ece64e3 QSettings now save all options 2013-06-19 13:44:58 +02:00
Paweł Jastrzębski
f8f362fc10 Expert mode tweaks 2013-06-19 12:36:09 +02:00
Paweł Jastrzębski
fdc265eaea Moved expert mode to separate profile 2013-06-19 11:37:44 +02:00
Ciro Mattia Gonano
941a4fa14f Merge branch 'new-gui' of github.com:ciromattia/kcc into new-gui 2013-06-19 10:25:04 +02:00
Ciro Mattia Gonano
e4fdbdaf8d Explicitize arguments. 2013-06-19 10:05:20 +02:00
Ciro Mattia Gonano
c09e050084 Orthographic 2013-06-19 10:04:58 +02:00
Paweł Jastrzębski
d12a94401d GUI tweak 2013-06-19 09:38:11 +02:00
Paweł Jastrzębski
e14d0d7c7f Ultra mode tweaks 2013-06-19 09:21:51 +02:00
Paweł Jastrzębski
ee9c94aceb Added ultra quality mode 2013-06-18 23:49:37 +02:00
Paweł Jastrzębski
4b4424ae92 "Crash like professional" - Improved error handling 2013-06-18 15:52:21 +02:00
Paweł Jastrzębski
8a9f11b0f5 Refactored sanitizeTreeReadOnly 2013-06-18 14:30:46 +02:00
Paweł Jastrzębski
fd493dba9a More user-friendly import errors 2013-06-18 10:56:27 +02:00
Paweł Jastrzębski
75bc7c51b5 UUpdated README 2013-06-18 10:35:54 +02:00
Paweł Jastrzębski
aab166c466 Updated README 2013-06-18 10:30:37 +02:00
Paweł Jastrzębski
94c49f500f Updated README 2013-06-18 10:08:33 +02:00
Paweł Jastrzębski
f353c4edf2 ReadOnly clean should run only on Windows 2013-06-17 18:01:07 +02:00
Paweł Jastrzębski
acb3e5058d Added function that remove ReadOnly flag from temp dir 2013-06-17 17:15:06 +02:00
Paweł Jastrzębski
6858aecda7 Improved error handling 2013-06-17 12:43:22 +02:00
Paweł Jastrzębski
4411ca18aa Updated GUI - OSX 2013-06-16 23:29:00 +02:00
Paweł Jastrzębski
dc9dbed683 Updated GUI - Windows/Linux 2013-06-16 23:05:18 +02:00
Paweł Jastrzębski
860673bb0e Implemented --forcecolor 2013-06-16 22:53:18 +02:00
Paweł Jastrzębski
ac54a40af5 Version check - path fix 2013-06-15 13:32:46 +02:00
Paweł Jastrzębski
92df52867d Implemented version check 2013-06-15 13:21:51 +02:00
Paweł Jastrzębski
ab209c6f39 Little code cleaning 2013-06-15 09:03:59 +02:00
Paweł Jastrzębski
69dd23f27b Fixed icon bug 2013-06-14 15:06:39 +02:00
Paweł Jastrzębski
c5577317f9 OSX Force window focus 2013-06-14 10:10:22 +02:00
Paweł Jastrzębski
0d7cf6f37d OSX GUI tweaks 2013-06-13 18:55:48 +02:00
Paweł Jastrzębski
1d425b3cd6 Added OSX GUI 2013-06-13 18:36:29 +02:00
Ciro Mattia Gonano
9b62a45100 Back to lowercase 2013-06-13 17:50:00 +02:00
Ciro Mattia Gonano
db1f745a50 Amend includes. 2013-06-13 17:18:39 +02:00
Ciro Mattia Gonano
d56e9ef298 Merge branch 'new-gui' of github.com:ciromattia/kcc into new-gui 2013-06-13 16:07:37 +02:00
Paweł Jastrzębski
f278b7c2c5 Updated README 2013-06-13 15:13:26 +02:00
Ciro Mattia Gonano
916f3ca9ec Merge branch 'new-gui' of github.com:ciromattia/kcc into 2013-06-13 15:08:21 +02:00
Ciro Mattia Gonano
30c695d988 Merge branch 'new-gui' of github.com:ciromattia/kcc into 2013-06-13 15:08:12 +02:00
Ciro Mattia Gonano
7dcb120edf Depending on Pillow 2013-06-13 15:06:25 +02:00
Paweł Jastrzębski
e570e25b24 cx_Freeze Linux support 2013-06-13 14:33:51 +02:00
Paweł Jastrzębski
3cbc7f163a Not using Pixmaps outside GUI thread anymore - Part 2 2013-06-13 13:04:54 +02:00
Paweł Jastrzębski
21a8001f56 Not using Pixmaps outside GUI thread anymore 2013-06-13 12:36:34 +02:00
Ciro Mattia Gonano
ebafb0006a Ignore Thumbs.db 2013-06-13 10:51:20 +02:00
Paweł Jastrzębski
37e42b2f0b Command-line version don't require PyQT anymore 2013-06-12 16:44:17 +02:00
Paweł Jastrzębski
1ae5b210b0 Implemented screen size override 2013-06-12 11:09:20 +02:00
Paweł Jastrzębski
3f2fb67c04 cx_Freeze tweaks 2013-06-12 09:47:54 +02:00
Paweł Jastrzębski
dded30d6a6 Minor tweaks 2013-06-12 09:29:20 +02:00
Paweł Jastrzębski
047a585cc2 cx_Freeze support 2013-06-12 09:28:50 +02:00
Paweł Jastrzębski
8cb7aebf86 Icons .ico -> .png 2013-06-12 07:55:54 +02:00
Paweł Jastrzębski
439eedcec7 UI tweaks 2013-06-11 23:28:58 +02:00
Paweł Jastrzębski
ed94e6508c New icons 2013-06-11 20:09:00 +02:00
Paweł Jastrzębski
9f33e756d5 QSettings + Thread proofing code + misc changes 2013-06-11 19:52:13 +02:00
Paweł Jastrzębski
9fee6568cd Updated progress bar 2013-06-11 17:39:33 +02:00
Paweł Jastrzębski
2a169d50ec Made code more thread-safe 2013-06-11 13:56:44 +02:00
Paweł Jastrzębski
b2d90c2ac1 Remember last path 2013-06-11 13:39:47 +02:00
Paweł Jastrzębski
64cd253618 Litte cleanup 2013-06-11 13:27:06 +02:00
Paweł Jastrzębski
98bc6fbe4c Added progress bar 2013-06-11 12:54:23 +02:00
Paweł Jastrzębski
70e41eb0e3 Moved conversion to separate thread. No more UI freezes. 2013-06-11 08:31:30 +02:00
Paweł Jastrzębski
b5e08df5d3 Fixed infinite loop 2013-06-10 22:26:16 +02:00
Paweł Jastrzębski
46034b0629 Additional ground work for expert mode 2013-06-10 22:04:22 +02:00
Paweł Jastrzębski
109da1056e Expert mode GUI 2013-06-10 20:11:44 +02:00
Paweł Jastrzębski
e046565f22 "Fixed" UTF-8 problems 2013-06-10 19:34:12 +02:00
Paweł Jastrzębski
ff99472278 Updated README 2013-06-10 18:12:12 +02:00
Paweł Jastrzębski
a5a2c2a93f Updated README 2013-06-10 18:04:29 +02:00
Paweł Jastrzębski
4cf7a531c2 Replaced placeholder icon with real one 2013-06-10 17:53:19 +02:00
Paweł Jastrzębski
b101dc780d Alpha version of QT GUI #2 2013-06-10 17:43:06 +02:00
Paweł Jastrzębski
1c7ff5c597 Alpha version of QT GUI 2013-06-10 17:18:30 +02:00
Paweł Jastrzębski
880f220355 Small cleanup 2013-06-09 09:42:10 +02:00
Paweł Jastrzębski
a1d2acc845 Updated README 2013-06-05 21:04:04 +02:00
Ciro Mattia Gonano
60caf896bd Add notice for OSX crashign and 2.9 download link. 2013-06-04 20:24:45 +03:00
Ciro Mattia Gonano
f9d0ec864d Add donate buttons 2013-05-28 16:54:09 +03:00
Ciro Mattia Gonano
6ca1aa532c Add important tips from Pawel 2013-05-28 15:23:23 +02:00
Paweł Jastrzębski
b068d82ccf Made split filenames more idiot-proof 2013-05-28 13:57:11 +02:00
Paweł Jastrzębski
43ca5ac5b9 py2exe fix 2013-05-28 13:28:09 +02:00
Ciro Mattia Gonano
18c3ab2340 2.10 release 2013-05-28 12:56:59 +02:00
Ciro Mattia Gonano
87eaba184e Update README 2013-05-28 12:21:58 +02:00
Ciro Mattia Gonano
97b5d8a2ee Merge branch 'master' of github.com:ciromattia/kcc 2013-05-28 12:18:58 +02:00
Ciro Mattia Gonano
7c3a762107 Add "1" if sanitized filename already exists (fixes #50) 2013-05-28 12:17:08 +02:00
Paweł Jastrzębski
0b90af77da Disabled cropping and page number cutting for blank pages (close #43) 2013-05-28 12:07:45 +02:00
Paweł Jastrzębski
28dcab8ee8 Multiprocessing - final final touches + Code cleanup (close #39) 2013-05-28 11:24:37 +02:00
Paweł Jastrzębski
6c468a5291 dirImgProcess - multiprocessing 2013-05-28 08:47:24 +02:00
Paweł Jastrzębski
d090d8c2e8 Refactoring of dirImgProcess 2013-05-27 20:06:47 +02:00
Paweł Jastrzębski
aba315866e Small tweaks 2013-05-27 18:54:39 +02:00
Paweł Jastrzębski
e981aa4520 Added some comments 2013-05-27 18:46:54 +02:00
Paweł Jastrzębski
e603622021 Removed redundant --panelviewhorizontal 2013-05-27 18:26:53 +02:00
Paweł Jastrzębski
3e007965b2 Fixed panel order for horizontal pages when --rotate is enabled 2013-05-27 15:21:16 +02:00
Paweł Jastrzębski
c0610360a3 Updated README (I hate Markdown) 2013-05-27 09:21:09 +02:00
Paweł Jastrzębski
ff8f6e073f Updated README 2013-05-27 09:18:28 +02:00
Paweł Jastrzębski
aadb5407d2 Updated README 2013-05-27 09:00:01 +02:00
Paweł Jastrzębski
2e4d5eb958 Updated README 2013-05-27 08:54:51 +02:00
Ciro Mattia Gonano
33fb13a66e Merge branch 'master' of github.com:ciromattia/kcc 2013-05-20 18:23:03 +02:00
Ciro Mattia Gonano
4dc69aa1c4 Add default value for saveToDir() last param 2013-05-20 18:22:35 +02:00
Paweł Jastrzębski
b3681a3ceb Fixed merge 2013-05-16 11:49:49 +02:00
Ciro Mattia Gonano
348dcc4275 Merge pull request #49 from ciromattia/HorizontalPanelView
Horizontal Panel View
2013-05-16 01:20:31 -07:00
Ciro Mattia Gonano
39e69119ac Merge branch 'master' into HorizontalPanelView
Conflicts:
	kcc/comic2ebook.py
2013-05-16 10:19:48 +02:00
Paweł Jastrzębski
8c57926978 Disabling landscape mode as it is broken in this mode 2013-05-07 22:16:33 +02:00
Paweł Jastrzębski
8b1965054f Added --panelviewhorizontal option 2013-05-07 11:03:44 +02:00
Paweł Jastrzębski
18993069e3 Quantization must be disabled. 2013-04-30 12:57:32 +02:00
Ciro Mattia Gonano
7e6c8cc768 Update README 2013-04-30 10:29:40 +02:00
Ciro Mattia Gonano
3ae44d2fcb Merge pull request #47 from ciromattia/KindleFire
Kindle Fire support
2013-04-30 01:27:30 -07:00
Ciro Mattia Gonano
a6eb3936e4 Don't force upscale on Kindle Fire (let users choose if they want it) 2013-04-30 10:27:04 +02:00
Paweł Jastrzębski
bb24d3ca30 Preliminary support for horizontal Panel View. 2013-04-29 22:38:24 +02:00
Paweł Jastrzębski
7e191c0be5 Experimental support of Kindle Fire - Tweaks 2013-04-29 17:51:14 +02:00
Paweł Jastrzębski
d9ea165bbb Experimental support of Kindle Fire 2013-04-29 17:19:06 +02:00
Ciro Mattia Gonano
148211a5c7 Update for 2.9 release. 2013-04-19 12:14:24 +02:00
Ciro Mattia Gonano
23e07f47f0 Merge pull request #46 from ciromattia/slugify
Filenames slugification
2013-04-19 02:27:54 -07:00
Ciro Mattia Gonano
724156c554 Small fixes 2013-04-12 01:36:51 +02:00
Ciro Mattia Gonano
b972e4c746 Remove Windows silly 'thumbs.db' too 2013-04-11 12:33:14 +02:00
Ciro Mattia Gonano
f0afa1fff2 Convert dot char to hyphen.
Removes UNIX-hidden files and dirs from the final archive (prevents .DS_Store and stuff)
2013-04-11 12:18:02 +02:00
Ciro Mattia Gonano
a36c05f0c5 Merge from master 2013-04-11 12:00:51 +02:00
Ciro Mattia Gonano
4f3a66b4eb Update README.md 2013-04-11 12:59:01 +03:00
Ciro Mattia Gonano
6369c7ea44 Update after merging of #44 2013-04-11 12:54:16 +03:00
Ciro Mattia Gonano
f1b8aff8d4 Merge pull request #44 from devernay/master
Support more input image formats: GIF, TIFF, ...
2013-04-11 02:50:37 -07:00
Ciro Mattia Gonano
be270aa797 Add number padding and lowering for file names (not directory) 2013-04-11 11:49:29 +02:00
Ciro Mattia Gonano
f33d355024 Filenames slugifications (#28, #31, #9, #8) 2013-04-11 10:34:33 +02:00
Ciro Mattia Gonano
6f913b026e rarfile updated to 2.6 2013-04-11 09:34:20 +02:00
Ciro Mattia Gonano
220b4e0954 Add an option to generate a CBZ skipping all the EPUB/Mobi stuff.
Prevent output files to overwrite the source (add _kcc if duplicate is detected)
Fixes #45
2013-04-10 12:29:31 +02:00
Frédéric Devernay
bac4a4fd86 support more input image formats 2013-04-04 14:09:21 +02:00
Paweł Jastrzębski
d923299230 Updated README 2013-03-21 09:02:17 +01:00
Ciro Mattia Gonano
a79ebcec86 Version bump 2013-03-21 01:28:27 +01:00
Ciro Mattia Gonano
69bbee7648 Merge pull request #41 from ciromattia/kcc-outputformat2
Change default file output format
2013-03-20 14:33:43 -07:00
Paweł Jastrzębski
57d7729d05 Decreasing quality
File size/quality ratio is too high. It is not worth it.
Sadly huffman table optimization build-in PIL is just simply broken.
2013-03-20 21:12:30 +01:00
Paweł Jastrzębski
25a1dcb72c Updated GUI 2013-03-20 16:34:04 +01:00
Paweł Jastrzębski
cff9b73b80 Force JPEG output 2013-03-20 16:20:17 +01:00
Paweł Jastrzębski
23336de5b5 Version bump 2013-03-20 10:58:27 +01:00
Ciro Mattia Gonano
d346ca0466 Update version and readme 2013-03-20 10:39:42 +01:00
Ciro Mattia Gonano
22fadf078e Optimize archive extraction for zip/rar files (closes #40) 2013-03-20 10:35:50 +01:00
Ciro Mattia Gonano
74acf85683 Merge pull request #36 from ciromattia/kcc-panelview
Panel View support
2013-03-20 02:14:17 -07:00
Ciro Mattia Gonano
713de03d1d Merge branch 'master' into kcc-panelview 2013-03-20 10:12:57 +01:00
Ciro Mattia Gonano
5817f52110 Update rarfile 2013-03-20 10:10:01 +01:00
Paweł Jastrzębski
dfc03aee38 Restoring more safe filesize limit 2013-03-18 14:13:53 +01:00
Paweł Jastrzębski
6bc8038068 Yet another refactoring of resizeImage
I hope all bugs are dead this time!
2013-03-17 12:52:33 +01:00
Paweł Jastrzębski
eb7d56c1b9 Force upscaling for spitted pages
Only when landscape mode is on.
Without it smaller splitted pages looks really bad on PW.
2013-03-17 10:45:46 +01:00
Paweł Jastrzębski
f5b515ef79 GUI update 2013-03-17 09:37:57 +01:00
Paweł Jastrzębski
943431346d MAJOR Landscape Mode improvement
No idea how this change will impact Non-Kindle devices but quality
increase of spitted pages is amazing.
2013-03-17 08:47:35 +01:00
Paweł Jastrzębski
a597173b71 Added high quality Panel View 2013-03-16 21:53:23 +01:00
Paweł Jastrzębski
c484cc8fff Code inspection 2013-03-16 20:05:27 +01:00
Paweł Jastrzębski
f195a4ccbf Preliminary Panel View support for K3/KNT 2013-03-16 19:51:23 +01:00
Paweł Jastrzębski
f55bb6dce6 Code cleaning 2013-03-16 13:33:13 +01:00
Paweł Jastrzębski
e7f49f8330 New profiles
Kindle Touch support Virtual Panel View.
Kindle Classic/NT is not.
2013-03-16 10:19:57 +01:00
Paweł Jastrzębski
cec9356fc2 Code cleaning 2013-03-16 10:10:19 +01:00
Paweł Jastrzębski
6d704b382d Fixed merge bug 2013-03-13 21:10:50 +01:00
Ciro Mattia Gonano
1c5f67111f Merge branch 'master' into kcc-fakepanelview 2013-03-13 00:38:20 +01:00
Ciro Mattia Gonano
660df4b370 Bump to 2.8-dev 2013-03-13 00:38:03 +01:00
Ciro Mattia Gonano
19dc5cc6f2 Merge branch 'master' into kcc-fakepanelview
Apply PEP-8 codestyle
2013-03-13 00:21:09 +01:00
Ciro Mattia Gonano
67baf90d04 Merge branch 'master' into kcc-fakepanelview
Apply PEP-8 codestyle
2013-03-13 00:20:38 +01:00
Paweł Jastrzębski
07bf41ea6c Made commands little shorter 2013-03-12 19:56:19 +01:00
Ciro Mattia Gonano
c2cfc9b8db Apply the correct ordering for image processing (closes #33, thanks to @devernay) 2013-03-12 16:54:14 +01:00
Ciro Mattia Gonano
7a7a2bc10b Don't set topmost as we don't want GUI to be ALWAYS on top 2013-03-12 16:47:11 +01:00
Paweł Jastrzębski
e3127ed516 Code cleanup 2013-03-12 14:00:52 +01:00
Paweł Jastrzębski
93a98db43e Disabled unused functions 2013-03-12 10:48:30 +01:00
Paweł Jastrzębski
750b55649a Draft of FakePanelView Landscape mode 2013-03-12 10:47:37 +01:00
Paweł Jastrzębski
ca5854a8bd Real Panel View don't split in half 2013-03-10 18:18:20 +01:00
Paweł Jastrzębski
712f728a5e Draft of FakePanelView mode #2 2013-03-10 17:29:06 +01:00
Paweł Jastrzębski
eb7766a5e5 Draft of FakePanelView mode 2013-03-10 11:58:52 +01:00
Paweł Jastrzębski
0cabbfde96 Additional tweaks of Landscape mode 2013-03-10 10:43:34 +01:00
Paweł Jastrzębski
c5983276e5 Added --fakepanelview option 2013-03-10 00:03:11 +01:00
Ciro Mattia Gonano
bb0856c841 Binary release 2013-03-09 23:42:22 +01:00
Paweł Jastrzębski
c21e8e398e Small tweaks of setup files 2013-03-09 18:03:49 +01:00
Paweł Jastrzębski
bba0223efc Win32 section was doubled
It builds correctly on Win32. Please define "problematic files".
2013-03-09 17:57:10 +01:00
Ciro Mattia Gonano
9709bc9c12 comments! 2013-03-09 17:29:41 +01:00
Ciro Mattia Gonano
0d61442c18 Try with icon bundling... 2013-03-09 17:24:27 +01:00
Ciro Mattia Gonano
89fa1393db Merge remote-tracking branch 'origin/master' 2013-03-09 17:17:57 +01:00
Ciro Mattia Gonano
15e86b28ab Do not use python-magic, instead use is_zipfile and is_rarfile, falling back on extension for PDFs. 2013-03-09 17:17:40 +01:00
Paweł Jastrzębski
a72d1128dd Fixed OS X build script 2013-03-09 16:16:01 +01:00
Paweł Jastrzębski
0fd3f6bc0f setup.py py2exe/app -> cx_freeze
Not tested on OS X.
2013-03-08 17:55:26 +01:00
Paweł Jastrzębski
d76ce6c92f Improved setup_console.py 2013-03-08 17:27:54 +01:00
Paweł Jastrzębski
b6fa993c17 Fixed splitCount global 2013-03-08 17:05:17 +01:00
Paweł Jastrzębski
81509012b4 Limit is slightly higher 2013-03-07 13:43:09 +01:00
Ciro Mattia Gonano
6db1ab0792 Update README 2013-03-07 12:22:37 +01:00
Ciro Mattia Gonano
513ea0a7cd Use python-magic (https://github.com/ahupp/python-magic) to get mimetype from files (extension does not matter anymore) 2013-03-07 11:56:06 +01:00
Ciro Mattia Gonano
640b5117f9 Use python-magic (https://github.com/ahupp/python-magic) to get mimetype from files (extension does not matter anymore) 2013-03-07 11:54:37 +01:00
Ciro Mattia Gonano
c9d558a353 Do not call kindlegen if source epub is bigger than 300MB (closes #17) 2013-03-07 11:30:40 +01:00
Ciro Mattia Gonano
378aff4f35 Gracefully exit if unrar missing (#15) 2013-03-07 11:21:17 +01:00
Ciro Mattia Gonano
6142c87fa3 remove brackets 2013-03-07 11:04:28 +01:00
Ciro Mattia Gonano
b01e8e2bc2 Version bumping 2013-03-07 10:53:28 +01:00
Ciro Mattia Gonano
10724489fc Update icon and focusing (#12) 2013-03-07 10:51:45 +01:00
Paweł Jastrzębski
3365f111e4 setup_console.py py2exe -> cx_freeze 2013-03-07 09:26:41 +01:00
Ciro Mattia Gonano
b1d22cd05b Merge branch 'master' of https://github.com/ciromattia/kcc 2013-03-07 00:48:33 +01:00
Ciro Mattia Gonano
74f2250952 Icon! 2013-03-06 23:22:18 +01:00
Ciro Mattia Gonano
36516fd594 Convert button disabled if no file is on the list 2013-03-06 23:17:22 +01:00
Ciro Mattia Gonano
2dab7a707b Update setup with Windows icon 2013-03-06 23:16:50 +01:00
Paweł Jastrzębski
fc8b26e292 Mother of all bug fixes 2013-03-06 23:08:24 +01:00
Ciro Mattia Gonano
5c0964c8fa Use splitlines() instead of regexp (fixes #25) 2013-03-06 22:51:47 +01:00
Ciro Mattia Gonano
6836c6ee17 PEP-8 codestyle (sorry for newline on long lines, PEP-8 want at max 78char-long lines...) 2013-03-06 22:47:17 +01:00
Paweł Jastrzębski
6adc2dff93 Updated README.md 2013-03-06 21:19:12 +01:00
Paweł Jastrzębski
63deec88db Updated README.md 2013-03-06 21:13:38 +01:00
Paweł Jastrzębski
c37e281d69 GUI now will detect missing KindleGen 2013-03-06 21:03:04 +01:00
Paweł Jastrzębski
4a0497addc Fixed bug - CSS directory depth 2013-03-06 20:23:18 +01:00
Paweł Jastrzębski
e2f5c549aa GUI improvements
Option sorting resolved slighty lame but it work.
2013-03-06 20:12:27 +01:00
Paweł Jastrzębski
a8195d44ee Little cleaning and normalisation 2013-03-06 18:01:12 +01:00
Ciro Mattia Gonano
18a505637d Merge pull request #30 from ciromattia/kcc-epubmargins
Pull request for epub margin support
2013-03-06 07:31:49 -08:00
Ciro Mattia Gonano
62475e12c6 Merge branch 'master' 2013-03-06 16:29:56 +01:00
Ciro Mattia Gonano
46888e10d8 Merge pull request #27 from ciromattia/kcc-gamma
KCC Gamma + GUI
2013-03-06 07:19:06 -08:00
Ciro Mattia Gonano
eb406aada0 Merge branch 'master' into kcc-gamma 2013-03-06 16:18:45 +01:00
Ciro Mattia Gonano
1582d03fab Optimization: if gamma is 1.0 don't eval image 2013-03-06 15:15:24 +01:00
Ciro Mattia Gonano
4cfac52d6a Display progressbars (fixes #13)
Still to change into something less eye-bleeding ;)
2013-03-06 15:08:42 +01:00
Ciro Mattia Gonano
04ea816365 Merge branch 'master' of github.com:ciromattia/kcc 2013-03-06 14:27:25 +01:00
Paweł Jastrzębski
6a298175f0 CSS Formatting 2013-03-06 13:39:51 +01:00
Paweł Jastrzębski
690f0298e6 Merge https://github.com/devernay/kcc into kcc-epubmargins 2013-03-06 13:08:26 +01:00
Paweł Jastrzębski
fc93697c28 Merge https://github.com/ciromattia/kcc into kcc-epubmargins 2013-03-06 13:07:38 +01:00
Frédéric Devernay
32391f6a5a better epub support (grabbed from calibre): get rid of those small margins 2013-03-06 12:11:50 +01:00
Ciro Mattia Gonano
f163eac853 Make GUI error reporting more useful 2013-03-06 11:33:28 +01:00
Ciro Mattia Gonano
ce824f4cab Add default gamma by profile support 2013-03-06 11:32:20 +01:00
Ciro Mattia Gonano
df41ad405e Merge branch 'master' into kcc-gamma 2013-03-06 11:10:55 +01:00
Ciro Mattia Gonano
685cdc929b Missing return, now PDF conversion works again 2013-03-06 10:29:59 +01:00
Paweł Jastrzębski
5e65c5149c Litte cleanup of setup files
Added setup_console script that generate binaries used by AWKCC.
2013-03-06 09:41:54 +01:00
Paweł Jastrzębski
718af10be7 Updated GUI
Plus small notquantize tweaks.
2013-03-05 23:41:48 +01:00
Paweł Jastrzębski
b162425e52 Added option to disable dithering 2013-03-05 22:31:26 +01:00
Paweł Jastrzębski
f1b63420f6 Merge branch 'master' of https://github.com/devernay/kcc into kcc-gamma 2013-03-05 21:49:24 +01:00
Ciro Mattia Gonano
95e7329abf Revert to POSIX path - win7 kindlegen seems to want them 2013-03-05 19:25:00 +01:00
Frédéric Devernay
5d3b7e83f5 add --nosplitrotate option (is the name right?) 2013-03-05 17:58:24 +01:00
Frédéric Devernay
751e6eb4e7 save dithered images as PNG, and linearize (inverse gamma) before dithering 2013-03-05 17:56:43 +01:00
Ciro Mattia Gonano
074e31cb2e Revert to POSIX path - win kindlegen seems to work 2013-03-05 17:31:34 +01:00
Ciro Mattia Gonano
e7e87d03cd Replace backslashes in path, too (win paths) 2013-03-05 17:12:48 +01:00
Paweł Jastrzębski
38669d08ed Small .gitignore tweak 2013-03-05 16:55:14 +01:00
Paweł Jastrzębski
825eafad7f Fixed bug caused by formating
2:0 Mr. Ciromattia :-)
2013-03-05 16:52:22 +01:00
Paweł Jastrzębski
f805984c1c Fixed logic - Inserting blank-page records
Variable "facing" could be bool but this way code is more readable.
2013-03-05 16:28:22 +01:00
42 changed files with 11931 additions and 1289 deletions

6
.gitignore vendored
View File

@@ -2,4 +2,8 @@
*.cbz
*.cbr
.idea
build
build
dist
kindlegen*
.DS_Store
Thumbs.db

781
KCC-OSX.ui Normal file
View File

@@ -0,0 +1,781 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>KCC</class>
<widget class="QMainWindow" name="KCC">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>420</width>
<height>380</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>420</width>
<height>380</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>420</width>
<height>380</height>
</size>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="windowTitle">
<string>Kindle Comic Converter</string>
</property>
<property name="windowIcon">
<iconset resource="KCC.qrc">
<normaloff>:/Icon/icons/comic2ebook.png</normaloff>:/Icon/icons/comic2ebook.png</iconset>
</property>
<property name="locale">
<locale language="C" country="AnyCountry"/>
</property>
<widget class="QWidget" name="Form">
<widget class="QFrame" name="OptionsAdvanced">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>9</x>
<y>253</y>
<width>421</width>
<height>61</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QCheckBox" name="ProcessingBox">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Disable image optimizations.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>No optimisation</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="UpscaleBox">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Enable image upscaling.&lt;br/&gt;Aspect ratio will be preserved.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Upscale images</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="StretchBox">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Enable image stretching.&lt;br/&gt;Aspect ratio will be not preserved.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Stretch images</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QCheckBox" name="NoDitheringBox">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Create PNG files instead JPEG.&lt;br/&gt;&lt;/span&gt;&lt;span style=&quot; font-size:12pt; font-weight:600;&quot;&gt;Only for non-Kindle devices!&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>PNG output</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="BorderBox">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Fill space around images with black color.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Black borders</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QCheckBox" name="NoRotateBox">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Disable splitting and rotation.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>No split/rotate</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QComboBox" name="DeviceBox">
<property name="geometry">
<rect>
<x>8</x>
<y>200</y>
<width>151</width>
<height>34</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>Target device.</string>
</property>
</widget>
<widget class="QComboBox" name="FormatBox">
<property name="geometry">
<rect>
<x>262</x>
<y>200</y>
<width>152</width>
<height>34</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>Output format.</string>
</property>
</widget>
<widget class="QPushButton" name="ConvertButton">
<property name="geometry">
<rect>
<x>160</x>
<y>200</y>
<width>101</width>
<height>41</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Convert</string>
</property>
<property name="icon">
<iconset resource="KCC.qrc">
<normaloff>:/Other/icons/convert.png</normaloff>:/Other/icons/convert.png</iconset>
</property>
</widget>
<widget class="QPushButton" name="DirectoryButton">
<property name="geometry">
<rect>
<x>5</x>
<y>160</y>
<width>156</width>
<height>41</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Add directory</string>
</property>
<property name="icon">
<iconset resource="KCC.qrc">
<normaloff>:/Other/icons/folder_new.png</normaloff>:/Other/icons/folder_new.png</iconset>
</property>
</widget>
<widget class="QPushButton" name="FileButton">
<property name="geometry">
<rect>
<x>260</x>
<y>160</y>
<width>157</width>
<height>41</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Add file</string>
</property>
<property name="icon">
<iconset resource="KCC.qrc">
<normaloff>:/Other/icons/document_new.png</normaloff>:/Other/icons/document_new.png</iconset>
</property>
</widget>
<widget class="QPushButton" name="ClearButton">
<property name="geometry">
<rect>
<x>160</x>
<y>160</y>
<width>101</width>
<height>41</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Clear list</string>
</property>
<property name="icon">
<iconset resource="KCC.qrc">
<normaloff>:/Other/icons/clear.png</normaloff>:/Other/icons/clear.png</iconset>
</property>
</widget>
<widget class="QFrame" name="OptionsBasic">
<property name="geometry">
<rect>
<x>10</x>
<y>233</y>
<width>421</width>
<height>41</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<widget class="QCheckBox" name="MangaBox">
<property name="geometry">
<rect>
<x>9</x>
<y>10</y>
<width>130</width>
<height>18</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Enable right-to-left reading.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Manga mode</string>
</property>
</widget>
<widget class="QCheckBox" name="QualityBox">
<property name="geometry">
<rect>
<x>282</x>
<y>10</y>
<width>130</width>
<height>18</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:14pt; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - Normal quality mode&lt;br/&gt;&lt;/span&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:14pt; font-style:italic;&quot;&gt;Use it when Panel View support is not needed.&lt;/span&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:14pt; font-weight:600; text-decoration: underline;&quot;&gt;&lt;br/&gt;&lt;/span&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:14pt;&quot;&gt;- Maximum quality when zoom is not enabled.&lt;br/&gt;- Poor quality when zoom is enabled.&lt;br/&gt;- Lowest file size.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:14pt; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - High quality mode&lt;br/&gt;&lt;/span&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:14pt; font-style:italic;&quot;&gt;Not zoomed image &lt;/span&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:14pt; font-weight:600; font-style:italic;&quot;&gt;might &lt;/span&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:14pt; font-style:italic;&quot;&gt;be a little blurry.&lt;/span&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:14pt; font-weight:600; text-decoration: underline;&quot;&gt;&lt;br/&gt;&lt;/span&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:14pt;&quot;&gt;- Medium/High quality when zoom is not enabled.&lt;br/&gt;- Maximum quality when zoom is enabled.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:14pt; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Ultra quality mode&lt;br/&gt;&lt;/span&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:14pt; font-style:italic;&quot;&gt;Maximum possible quality.&lt;/span&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:14pt; font-weight:600; text-decoration: underline;&quot;&gt;&lt;br/&gt;&lt;/span&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:14pt;&quot;&gt;- Maximum quality when zoom is not enabled.&lt;br/&gt;- Maximum quality when zoom is enabled.&lt;br/&gt;- Very high file size.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>High/Ultra quality</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
<widget class="QCheckBox" name="RotateBox">
<property name="geometry">
<rect>
<x>145</x>
<y>10</y>
<width>130</width>
<height>18</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Disable page spliting.&lt;br/&gt;They will be rotated instead.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Horizontal mode</string>
</property>
</widget>
<zorder>RotateBox</zorder>
<zorder>MangaBox</zorder>
<zorder>QualityBox</zorder>
</widget>
<widget class="QListWidget" name="JobList">
<property name="geometry">
<rect>
<x>10</x>
<y>50</y>
<width>401</width>
<height>101</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
</widget>
<widget class="QPushButton" name="BasicModeButton">
<property name="geometry">
<rect>
<x>5</x>
<y>10</y>
<width>210</width>
<height>41</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Basic</string>
</property>
</widget>
<widget class="QPushButton" name="AdvModeButton">
<property name="geometry">
<rect>
<x>207</x>
<y>10</y>
<width>210</width>
<height>41</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Advanced</string>
</property>
</widget>
<widget class="QFrame" name="OptionsAdvancedGamma">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>10</x>
<y>303</y>
<width>401</width>
<height>41</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<widget class="QLabel" name="GammaLabel">
<property name="geometry">
<rect>
<x>20</x>
<y>0</y>
<width>100</width>
<height>40</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;When converting color images setting this option to 1.0 MIGHT improve readability.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Gamma: Auto</string>
</property>
</widget>
<widget class="QSlider" name="GammaSlider">
<property name="geometry">
<rect>
<x>110</x>
<y>10</y>
<width>280</width>
<height>22</height>
</rect>
</property>
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;When converting color images setting this option to 1.0 &lt;/span&gt;&lt;span style=&quot; font-size:12pt; font-weight:600;&quot;&gt;might&lt;/span&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt; improve readability.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="maximum">
<number>500</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</widget>
<widget class="QProgressBar" name="ProgressBar">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>401</width>
<height>35</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="value">
<number>0</number>
</property>
<property name="alignment">
<set>Qt::AlignJustify|Qt::AlignVCenter</set>
</property>
<property name="format">
<string/>
</property>
</widget>
<widget class="QFrame" name="OptionsExpert">
<property name="geometry">
<rect>
<x>10</x>
<y>335</y>
<width>421</width>
<height>41</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<widget class="QCheckBox" name="ColorBox">
<property name="geometry">
<rect>
<x>9</x>
<y>11</y>
<width>130</width>
<height>18</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Do not convert images to grayscale.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Color mode</string>
</property>
</widget>
<widget class="QFrame" name="OptionsExpertInternal">
<property name="geometry">
<rect>
<x>90</x>
<y>0</y>
<width>315</width>
<height>40</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="wLabel">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Resolution of target device.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Custom width: </string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="customWidth">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>45</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="acceptDrops">
<bool>false</bool>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Resolution of target device.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="inputMask">
<string>0000; </string>
</property>
<property name="maxLength">
<number>4</number>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="hLabel">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Resolution of target device.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Custom height: </string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLineEdit" name="customHeight">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>45</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="acceptDrops">
<bool>false</bool>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Resolution of target device.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="inputMask">
<string>0000; </string>
</property>
<property name="maxLength">
<number>4</number>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<zorder>BasicModeButton</zorder>
<zorder>AdvModeButton</zorder>
<zorder>ProgressBar</zorder>
<zorder>JobList</zorder>
<zorder>OptionsAdvanced</zorder>
<zorder>DeviceBox</zorder>
<zorder>FormatBox</zorder>
<zorder>ConvertButton</zorder>
<zorder>DirectoryButton</zorder>
<zorder>FileButton</zorder>
<zorder>ClearButton</zorder>
<zorder>OptionsBasic</zorder>
<zorder>OptionsAdvancedGamma</zorder>
<zorder>OptionsExpert</zorder>
</widget>
<action name="ActionBasic">
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<property name="text">
<string>Basic</string>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
</action>
<action name="ActionAdvanced">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Advanced</string>
</property>
</action>
</widget>
<tabstops>
<tabstop>DirectoryButton</tabstop>
<tabstop>FileButton</tabstop>
<tabstop>ConvertButton</tabstop>
<tabstop>ClearButton</tabstop>
</tabstops>
<resources>
<include location="KCC.qrc"/>
</resources>
<connections/>
</ui>

25
KCC.qrc Normal file
View File

@@ -0,0 +1,25 @@
<RCC>
<qresource prefix="Icon">
<file>icons/comic2ebook.png</file>
</qresource>
<qresource prefix="Devices">
<file>icons/Other.png</file>
<file>icons/Kindle.png</file>
</qresource>
<qresource prefix="Formats">
<file>icons/CBZ.png</file>
<file>icons/EPUB.png</file>
<file>icons/MOBI.png</file>
</qresource>
<qresource prefix="Status">
<file>icons/error.png</file>
<file>icons/info.png</file>
<file>icons/warning.png</file>
</qresource>
<qresource prefix="Other">
<file>icons/clear.png</file>
<file>icons/convert.png</file>
<file>icons/document_new.png</file>
<file>icons/folder_new.png</file>
</qresource>
</RCC>

697
KCC.ui Normal file
View File

@@ -0,0 +1,697 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>KCC</class>
<widget class="QMainWindow" name="KCC">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>420</width>
<height>380</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>420</width>
<height>380</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>420</width>
<height>380</height>
</size>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="windowTitle">
<string>Kindle Comic Converter</string>
</property>
<property name="windowIcon">
<iconset resource="KCC.qrc">
<normaloff>:/Icon/icons/comic2ebook.png</normaloff>:/Icon/icons/comic2ebook.png</iconset>
</property>
<property name="locale">
<locale language="C" country="AnyCountry"/>
</property>
<widget class="QWidget" name="Form">
<widget class="QFrame" name="OptionsAdvanced">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>10</x>
<y>254</y>
<width>421</width>
<height>61</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>9</number>
</property>
<item row="1" column="0">
<widget class="QCheckBox" name="ProcessingBox">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>Disable image optimizations.</string>
</property>
<property name="text">
<string>No optimisation</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="UpscaleBox">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable image upscaling.&lt;br/&gt;Aspect ratio will be preserved.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Upscale images</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="StretchBox">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable image stretching.&lt;br/&gt;Aspect ratio will be not preserved.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Stretch images</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QCheckBox" name="NoDitheringBox">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Create PNG files instead JPEG.&lt;br/&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Only for non-Kindle devices!&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>PNG output</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="BorderBox">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>Fill space around images with black color.</string>
</property>
<property name="text">
<string>Black borders</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QCheckBox" name="NoRotateBox">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Disable splitting and rotation.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>No split/rotate</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QComboBox" name="DeviceBox">
<property name="geometry">
<rect>
<x>10</x>
<y>200</y>
<width>141</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>Target device.</string>
</property>
</widget>
<widget class="QComboBox" name="FormatBox">
<property name="geometry">
<rect>
<x>260</x>
<y>200</y>
<width>151</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>Output format.</string>
</property>
</widget>
<widget class="QPushButton" name="ConvertButton">
<property name="geometry">
<rect>
<x>160</x>
<y>200</y>
<width>91</width>
<height>32</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Convert</string>
</property>
<property name="icon">
<iconset resource="KCC.qrc">
<normaloff>:/Other/icons/convert.png</normaloff>:/Other/icons/convert.png</iconset>
</property>
</widget>
<widget class="QPushButton" name="DirectoryButton">
<property name="geometry">
<rect>
<x>10</x>
<y>160</y>
<width>141</width>
<height>32</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Add directory</string>
</property>
<property name="icon">
<iconset resource="KCC.qrc">
<normaloff>:/Other/icons/folder_new.png</normaloff>:/Other/icons/folder_new.png</iconset>
</property>
</widget>
<widget class="QPushButton" name="FileButton">
<property name="geometry">
<rect>
<x>260</x>
<y>160</y>
<width>151</width>
<height>32</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Add file</string>
</property>
<property name="icon">
<iconset resource="KCC.qrc">
<normaloff>:/Other/icons/document_new.png</normaloff>:/Other/icons/document_new.png</iconset>
</property>
</widget>
<widget class="QPushButton" name="ClearButton">
<property name="geometry">
<rect>
<x>160</x>
<y>160</y>
<width>91</width>
<height>32</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Clear list</string>
</property>
<property name="icon">
<iconset resource="KCC.qrc">
<normaloff>:/Other/icons/clear.png</normaloff>:/Other/icons/clear.png</iconset>
</property>
</widget>
<widget class="QFrame" name="OptionsBasic">
<property name="geometry">
<rect>
<x>10</x>
<y>230</y>
<width>421</width>
<height>41</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<widget class="QCheckBox" name="MangaBox">
<property name="geometry">
<rect>
<x>9</x>
<y>10</y>
<width>130</width>
<height>18</height>
</rect>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>Enable right-to-left reading.</string>
</property>
<property name="text">
<string>Manga mode</string>
</property>
</widget>
<widget class="QCheckBox" name="QualityBox">
<property name="geometry">
<rect>
<x>282</x>
<y>10</y>
<width>130</width>
<height>18</height>
</rect>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Unchecked - Normal quality mode&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;Use it when Panel View support is not needed.&lt;/span&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;&lt;br /&gt;&lt;/span&gt;- Maximum quality when zoom is not enabled.&lt;br /&gt;- Poor quality when zoom is enabled.&lt;br /&gt;- Lowest file size.&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Indeterminate - High quality mode&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;Not zoomed image &lt;/span&gt;&lt;span style=&quot; font-weight:600; font-style:italic;&quot;&gt;might &lt;/span&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;be &lt;/span&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;a little blurry.&lt;/span&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;&lt;br /&gt;&lt;/span&gt;- Medium/High quality when zoom is not enabled.&lt;br /&gt;- Maximum quality when zoom is enabled.&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;Checked - Ultra quality mode&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;Maximum possible quality.&lt;/span&gt;&lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;&lt;br /&gt;&lt;/span&gt;- Maximum quality when zoom is not enabled.&lt;br /&gt;- Maximum quality when zoom is enabled.&lt;br /&gt;- Very high file size.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>High/Ultra quality</string>
</property>
<property name="tristate">
<bool>true</bool>
</property>
</widget>
<widget class="QCheckBox" name="RotateBox">
<property name="geometry">
<rect>
<x>145</x>
<y>10</y>
<width>130</width>
<height>18</height>
</rect>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Disable page spliting.&lt;br/&gt;They will be rotated instead.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Horizontal mode</string>
</property>
</widget>
<zorder>RotateBox</zorder>
<zorder>MangaBox</zorder>
<zorder>QualityBox</zorder>
</widget>
<widget class="QListWidget" name="JobList">
<property name="geometry">
<rect>
<x>10</x>
<y>50</y>
<width>401</width>
<height>101</height>
</rect>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
</widget>
<widget class="QPushButton" name="BasicModeButton">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>195</width>
<height>32</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Basic</string>
</property>
</widget>
<widget class="QPushButton" name="AdvModeButton">
<property name="geometry">
<rect>
<x>217</x>
<y>10</y>
<width>195</width>
<height>32</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Advanced</string>
</property>
</widget>
<widget class="QFrame" name="OptionsAdvancedGamma">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>10</x>
<y>305</y>
<width>401</width>
<height>41</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<widget class="QLabel" name="GammaLabel">
<property name="geometry">
<rect>
<x>15</x>
<y>0</y>
<width>100</width>
<height>40</height>
</rect>
</property>
<property name="toolTip">
<string>When converting color images setting this option to 1.0 MIGHT improve readability.</string>
</property>
<property name="text">
<string>Gamma: Auto</string>
</property>
</widget>
<widget class="QSlider" name="GammaSlider">
<property name="geometry">
<rect>
<x>110</x>
<y>10</y>
<width>270</width>
<height>22</height>
</rect>
</property>
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When converting color images setting this option to 1.0 &lt;span style=&quot; font-weight:600;&quot;&gt;might&lt;/span&gt; improve readability.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="maximum">
<number>500</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</widget>
<widget class="QProgressBar" name="ProgressBar">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>401</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="value">
<number>0</number>
</property>
<property name="alignment">
<set>Qt::AlignJustify|Qt::AlignVCenter</set>
</property>
<property name="format">
<string/>
</property>
</widget>
<widget class="QFrame" name="OptionsExpert">
<property name="geometry">
<rect>
<x>10</x>
<y>337</y>
<width>421</width>
<height>41</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<widget class="QCheckBox" name="ColorBox">
<property name="geometry">
<rect>
<x>9</x>
<y>11</y>
<width>130</width>
<height>18</height>
</rect>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>Do not convert images to grayscale.</string>
</property>
<property name="text">
<string>Color mode</string>
</property>
</widget>
<widget class="QFrame" name="OptionsExpertInternal">
<property name="geometry">
<rect>
<x>100</x>
<y>0</y>
<width>295</width>
<height>40</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="wLabel">
<property name="toolTip">
<string>Resolution of target device.</string>
</property>
<property name="text">
<string>Custom width: </string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="customWidth">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>40</width>
<height>16777215</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="acceptDrops">
<bool>false</bool>
</property>
<property name="toolTip">
<string>Resolution of target device.</string>
</property>
<property name="inputMask">
<string>0000; </string>
</property>
<property name="maxLength">
<number>4</number>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="hLabel">
<property name="toolTip">
<string>Resolution of target device.</string>
</property>
<property name="text">
<string>Custom height: </string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLineEdit" name="customHeight">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>40</width>
<height>16777215</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="acceptDrops">
<bool>false</bool>
</property>
<property name="toolTip">
<string>Resolution of target device.</string>
</property>
<property name="inputMask">
<string>0000; </string>
</property>
<property name="maxLength">
<number>4</number>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<zorder>OptionsAdvanced</zorder>
<zorder>DeviceBox</zorder>
<zorder>FormatBox</zorder>
<zorder>ConvertButton</zorder>
<zorder>DirectoryButton</zorder>
<zorder>FileButton</zorder>
<zorder>ClearButton</zorder>
<zorder>OptionsBasic</zorder>
<zorder>JobList</zorder>
<zorder>BasicModeButton</zorder>
<zorder>AdvModeButton</zorder>
<zorder>OptionsAdvancedGamma</zorder>
<zorder>OptionsExpert</zorder>
<zorder>ProgressBar</zorder>
</widget>
<action name="ActionBasic">
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<property name="text">
<string>Basic</string>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
</action>
<action name="ActionAdvanced">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Advanced</string>
</property>
</action>
</widget>
<tabstops>
<tabstop>DirectoryButton</tabstop>
<tabstop>FileButton</tabstop>
<tabstop>ConvertButton</tabstop>
<tabstop>ClearButton</tabstop>
</tabstops>
<resources>
<include location="KCC.qrc"/>
</resources>
<connections/>
</ui>

View File

@@ -1,6 +1,7 @@
ISC LICENSE
Copyright (c) 2012 Ciro Mattia Gonano <ciromattia@gmail.com>
Copyright (c) 2013 Ciro Mattia Gonano <ciromattia@gmail.com>
Copyright (c) 2013 Paweł Jastrzębski <pawelj@vulturis.eu>
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the

View File

@@ -1 +0,0 @@
include README.md MANIFEST.in LICENSE.txt

289
README.md
View File

@@ -1,131 +1,224 @@
# KCC
# KCC
`KCC` (a.k.a. `KindleComicConverter`) is a Python app to convert comic files or folders to ePub or Panel View Mobipocket.
It was initally developed for Kindle but as of version 2.2 it outputs valid ePub 2.0 so _**despite its name, KCC is
actually a comic 2 epub converter that every ereader owner can happily use**_.
It also optimizes comic images by:
- enhancing contrast
- cutting page numbering
- cropping white borders
- resizing larger images to device's native resolution
- quantizing images to device's palette
**KindleComicConverter** is a Python app to convert comic files or folders to ePub or Panel View MOBI.
It was initally developed for Kindle but since v2.2 it outputs valid ePub 2.0 so _**despite its name, KCC is
actually a comic to EPUB converter that every e-reader owner can happily use**_.
It can also optionally optimize images by applying a number of transformations.
### A word of warning
**KCC** _is not_ [Amazon's Kindle Comic Creator](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1001103761) nor is in any way endorsed by Amazon.
Amazon's tool is for comic publishers and involves a lot of manual effort, while **KCC** is for comic readers.
If you want to read some comments over *Amazon's KC2* you can take a look at [this](http://www.mobileread.com/forums/showthread.php?t=207461&page=7#96) and [that](http://www.mobileread.com/forums/showthread.php?t=211047) threads on Mobileread.
_KC2_ in no way is a replacement for **KCC** so you can be quite confident we'll going to carry on developing our little monster ;)
### Donations
If you find **KCC** valuable you can consider donating to the authors:
* Ciro Mattia Gonano [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=D8WNYNPBGDAS2)
* Paweł Jastrzębski [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YTTJ4LK2JDHPS)
## BINARY RELEASES
You can find the latest released binary at the following links:
- OS X: [https://dl.dropbox.com/u/16806101/KindleComicConverter_osx_2.6.zip](https://dl.dropbox.com/u/16806101/KindleComicConverter_osx_2.6.zip)
- Win64: [https://dl.dropbox.com/u/16806101/KindleComicConverter_win-amd64_2.6.zip](https://dl.dropbox.com/u/16806101/KindleComicConverter_win-amd64_2.6.zip)
- Win32: [http://pawelj.vulturis.eu/Shared/KindleComicConverter_win-x86_2.6.zip](http://pawelj.vulturis.eu/Shared/KindleComicConverter_win-x86_2.6.zip) *(thanks to [AcidWeb](https://github.com/AcidWeb))*
- Linux: just download sourcecode and launch `python kcc.py` *(provided you have Python and Pillow installed)*
You can find the latest released binary at the following links:
- **Win64:** [http://kcc.vulturis.eu/Win64/](http://kcc.vulturis.eu/Win64/)
- **Win32:** [http://kcc.vulturis.eu/Win32/](http://kcc.vulturis.eu/Win32/)
- **OS X:** [http://kcc.vulturis.eu/OSX/](http://kcc.vulturis.eu/OSX/)
- **Linux:** Just download sourcecode and launch: `python kcc.py`
## INPUT FORMATS
`kcc` can understand and convert, at the moment, the following file types:
**KCC** can understand and convert, at the moment, the following file types:
- PNG, JPG, GIF, TIFF, BMP
- Folders
- CBZ, ZIP
- CBR, RAR *(with `unrar` executable)*
- folders
- PDF *(extracting only contained JPG images)*
~~For now the script does not understand folder depth, so it will work on flat folders/archives only.~~
As of v. 1.50, KCC supports subfolders!
- CBR, RAR *(With `unrar` executable)*
- PDF *(Extracting only contained JPG images)*
## OPTIONAL REQUIREMENTS
- `kindlegen` in /usr/local/bin/ *(for .mobi generation)*
- [unrar](http://www.rarlab.com/download.htm) *(for CBR support)*
- [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)*
### 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)
- [Pillow](http://pypi.python.org/pypi/Pillow/) for comic optimizations like split double pages, resize to optimal resolution, improve contrast and palette, etc.
Please refer to official documentation for installing into your system.
### 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.
- PyQt4 - Please refer to official documentation for installing into your system.
- [Pillow](http://pypi.python.org/pypi/Pillow/) - For comic optimizations. Please refer to official documentation for installing into your system.
## USAGE
### GUI
### Important tips:
* Use high quality source files. **This little detail have a major impact on the final result.**
* Read tooltip of _High/Ultra quality_ option. There are many important informations there.
* When converting images smaller than device resolution remember to enable upscaling.
* 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.
* If you're converting color images and the end result is not satisfactory, experiment with gamma correction option (check 1.0 setting first).
Should be pretty self-explanatory, just keep in mind that it's still in development ;)
While working it seems frozen, I'll try to fix the aesthetics later.
Conversion being done, you should find an .epub and a .mobi files alongside the original input file (same directory)
### GUI
Should be pretty self-explanatory.
After completed conversion you should find ready file alongside the original input file (same directory).
### Standalone `comic2ebook.py` usage:
```
comic2ebook.py [options] comic_file|comic_folder
Options:
--version show program's version number and exit
-h, --help show this help message and exit
-p PROFILE, --profile=PROFILE
Device profile (choose one among K1, K2, K3, K4, KDX,
KDXG or KHD) [default=KHD]
-t TITLE, --title=TITLE
Comic title [default=filename]
-m, --manga-style 'Manga style' (right-to-left reading and splitting)
[default=False]
-v, --verbose Verbose output [default=False]
--no-image-processing
Do not apply image preprocessing (page splitting and
optimizations) [default=True]
--upscale-images Resize images smaller than device's resolution
[default=False]
--stretch-images Stretch images to device's resolution [default=False]
--black-borders Use black borders (instead of white ones) when not
stretching and ratio is not like the device's one
[default=False]
--no-cut-page-numbers
Do not try to cut page numbering on images
[default=True]
--rotate Disable page spliting. Instead rotate images
[default=False]
-o OUTPUT, --output=OUTPUT
Output directory or file for generated ePub
Usage: comic2ebook.py [options] comic_file|comic_folder
Options:
--version show program's version number and exit
-h, --help show this help message and exit
-p PROFILE, --profile=PROFILE
Device profile (Choose one among K1, K2, K3, K4NT, K4T, KDX, KDXG, KHD, KF, KFHD, KFHD8, KFA) [Default=KHD]
-t TITLE, --title=TITLE
Comic title [Default=filename]
-m, --manga-style Manga style (Right-to-left reading and splitting) [Default=False]
--quality=QUALITY Output quality. 0 - Normal 1 - High 2 - Ultra [Default=0]
-c, --cbz-output Outputs a CBZ archive and does not generate EPUB
--noprocessing Do not apply image preprocessing (Page splitting and optimizations) [Default=True]
--forcepng Create PNG files instead JPEG (For non-Kindle devices) [Default=False]
--gamma=GAMMA Apply gamma correction to linearize the image [Default=Auto]
--upscale Resize images smaller than device's resolution [Default=False]
--stretch Stretch images to device's resolution [Default=False]
--blackborders Use black borders instead of white ones when not stretching and ratio is not like the device's one [Default=False]
--rotate Rotate landscape pages instead of splitting them [Default=False]
--nosplitrotate Disable splitting and rotation [Default=False]
--nocutpagenumbers Do not try to cut page numbering on images [Default=True]
-o OUTPUT, --output=OUTPUT
Output generated file (EPUB or CBZ) to specified directory or file
--forcecolor Do not convert images to grayscale [Default=False]
--customwidth=WIDTH Replace screen width provided by device profile [Default=0]
--customheight=HEIGHT Replace screen height provided by device profile [Default=0]
-v, --verbose Verbose output [Default=False]
```
The script takes care of creating an *.epub* from your archive/folder, then:
1. Run `Kindlegen` on the generated *.epub*. Depending on how many images you have, this may take awhile. Once completed, the `.mobi` file should be in the directory.
2. (optionally) remove the SRCS record to reduce the `.mobi` filesize in half. You can use [Kindlestrip](http://www.mobileread.com/forums/showthread.php?t=96903).
3. Copy the `.mobi` file to your Kindle!
## CREDITS
KCC is made by [Ciro Mattia Gonano](http://github.com/ciromattia) and [Paweł Jastrzębski](http://github.com/AcidWeb)
**KCC** is made by [Ciro Mattia Gonano](http://github.com/ciromattia) and [Paweł Jastrzębski](http://github.com/AcidWeb)
This script born as a cross-platform alternative to `KindleComicParser` by **Dc5e** (published in [this mobileread forum thread](http://www.mobileread.com/forums/showthread.php?t=192783))
This script born as a cross-platform alternative to `KindleComicParser` by **Dc5e** (published [here](http://www.mobileread.com/forums/showthread.php?t=192783))
The app relies and includes the following scripts/binaries:
- `KindleStrip` script &copy; 2010-2012 by **Paul Durrant** and released in public domain
([mobileread forum thread](http://www.mobileread.com/forums/showthread.php?t=96903))
([forum thread](http://www.mobileread.com/forums/showthread.php?t=96903))
- `rarfile.py` script &copy; 2005-2011 **Marko Kreen** <markokr@gmail.com>, released with ISC License
- the icon is by **Nikolay Verin** ([http://ncrow.deviantart.com/](http://ncrow.deviantart.com/)) and released under [CC Attribution-NonCommercial-ShareAlike 3.0 Unported](http://creativecommons.org/licenses/by-nc-sa/3.0/) 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
Also, for .mobi generation you need to have `kindlegen` v2.7+ (with KF8 support) which is downloadable from Amazon website
and installed in a directory reachable by your PATH (e.g. `/usr/local/bin/` or `C:\Windows\`)
## 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 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)
## CHANGELOG
- 1.00: Initial version
- 1.10: Added support for CBZ/CBR files in comic2ebook.py
- 1.11: Added support for CBZ/CBR files in KindleComicConverter
- 1.20: Comic optimizations! Split pages not target-oriented (landscape with portrait target or portrait
with landscape target), add palette and other image optimizations from Mangle.
WARNING: PIL is required for all image mangling!
- 1.30: Fixed an issue in OPF generation for device resolution
Reworked options system (call with -h option to get the inline help)
- 1.40: Added some options for controlling image optimization
Further optimization (ImageOps, page numbering cut, autocontrast)
- 1.41: Fixed a serious bug on resizing when img ratio was bigger than device one
- 1.50: Added subfolder support for multiple chapters.
- 2.0: GUI! AppleScript is gone and Tk is used to provide cross-platform GUI support.
- 2.1: Added basic error reporting
- 2.2: Added (valid!) ePub 2.0 output
Rename .zip files to .cbz to avoid overwriting
- 2.3: Fixed win32 ePub generation, folder handling, filenames with spaces and subfolders
- 2.4: Use temporary directory as workdir (fixes converting from external volumes and zipfiles renaming)
Fixed "add folders" from GUI.
- 2.5: Added --black-borders option to set added borders black when page's ratio is not the device's one (#11).
Fixes epub containing zipped itself (#10).
- 2.6: Added --rotate option to rotate landscape images instead of splitting them.
Added --output option to customize ePub output dir/file.
Add rendition:layout and rendition:orientation ePub meta tags (supported by new kindlegen 2.8)
####1.00
* Initial version
####1.10
* Added support for CBZ/CBR files in comic2ebook.py
####1.11
* Added support for CBZ/CBR files in KindleComicConverter
####1.20
* Comic optimizations! Split pages not target-oriented (landscape with portrait target or portrait with landscape target), add palette and other image optimizations from Mangle. WARNING: PIL is required for all image mangling!
####1.30
* Fixed an issue in OPF generation for device resolution
* Reworked options system (call with -h option to get the inline help)
####1.40
* Added some options for controlling image optimization
* Further optimization (ImageOps, page numbering cut, autocontrast)
####1.41
* Fixed a serious bug on resizing when img ratio was bigger than device one
####1.50
* Added subfolder support for multiple chapters.
####2.0
* GUI! AppleScript is gone and Tk is used to provide cross-platform GUI support.
####2.1
* Added basic error reporting
#### 2.2:
* Added (valid!) ePub 2.0 output
* Rename .zip files to .cbz to avoid overwriting
####2.3
* Fixed win32 ePub generation, folder handling, filenames with spaces and subfolders
####2.4
* Use temporary directory as workdir (fixes converting from external volumes and zipfiles renaming)
* Fixed "add folders" from GUI.
####2.5
* Added --black-borders option to set added borders black when page's ratio is not the device's one (#11).
* Fixes epub containing zipped itself (#10)
####2.6
* Added --rotate option to rotate landscape images instead of splitting them (#16, #24)
* Added --output option to customize ePub output dir/file (#22)
* Add rendition:layout and rendition:orientation ePub meta tags (supported by new kindlegen 2.8)
* Fixed natural sorting for files (#18)
####2.7
* Lots of GUI improvements (#27, #13)
* Added gamma support within --gamma option (defaults to profile-specified gamma) (#26, #27)
* Added --nodithering option to prevent dithering optimizations (#27)
* Epub margins support (#30)
* Fixed no file added if file has no spaces on Windows (#25)
* Gracefully exit if unrar missing (#15)
* Do not call kindlegen if source epub is bigger than 320MB (#17)
* Get filetype from magic number (#14)
* PDF conversion works again
####2.8
* Updated rarfile library
* Panel View support + HQ support (#36) - new option: --nopanelviewhq
* Split profiles for K4NT and K4T
* Rewrite of Landscape Mode support (huge readability improvement for KPW)
* Upscale use now BILINEAR method
* Added generic CSS file
* Optimized archive extraction for zip/rar files (#40)
####2.9
* Added support for generating a plain CBZ (skipping all the EPUB/Mobi generation) (#45)
* Prevent output file overwriting the source one: if a duplicate name is detected, append _kcc to the name
* Rarfile library updated to 2.6
* Added GIF, TIFF and BMP to supported formats (#42)
* Filenames slugifications (#28, #31, #9, #8)
####2.10:
* Multiprocessing support
* Kindle Fire support (color ePub/Mobi)
* Panel View support for horizontal content
* Fixed panel order for horizontal pages when --rotate is enabled
* Disabled cropping and page number cutting for blank pages
* Fixed some slugify issues with specific file naming conventions (#50, #51)
####3.0:
* New QT GUI
* Merge with AWKCC
* Added ultra quality mode
* Added support for custom width/height
* Added option to disable color conversion
####3.1:
* Added profile: Kindle for Android
* Add file/directory dialogs now support multiselect
* Many small fixes and tweaks
## KNOWN ISSUES
* Removing SRCS headers sometimes fail in 32bit enviroments. Due to memory limitations.
## COPYRIGHT
Copyright (c) 2012-2013 Ciro Mattia Gonano with further contributions by Paweł Jastrzębski.
KCC is released under ISC LICENSE; see LICENSE.txt for further details.
Copyright (c) 2012-2013 Ciro Mattia Gonano and Paweł Jastrzębski.
**KCC** is released under ISC LICENSE; see LICENSE.txt for further details.

View File

@@ -1,284 +0,0 @@
#!python
"""Bootstrap setuptools installation
If you want to use setuptools in your package's setup.py, just include this
file in the same directory with it, and add this to the top of your setup.py::
from ez_setup import use_setuptools
use_setuptools()
If you want to require a specific version of setuptools, set a download
mirror, or use an alternate download directory, you can do so by supplying
the appropriate options to ``use_setuptools()``.
This file can also be run as a script to install or upgrade setuptools.
"""
import sys
DEFAULT_VERSION = "0.6c11"
DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
md5_data = {
'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
'setuptools-0.6c10-py2.3.egg': 'ce1e2ab5d3a0256456d9fc13800a7090',
'setuptools-0.6c10-py2.4.egg': '57d6d9d6e9b80772c59a53a8433a5dd4',
'setuptools-0.6c10-py2.5.egg': 'de46ac8b1c97c895572e5e8596aeb8c7',
'setuptools-0.6c10-py2.6.egg': '58ea40aef06da02ce641495523a0b7f5',
'setuptools-0.6c11-py2.3.egg': '2baeac6e13d414a9d28e7ba5b5a596de',
'setuptools-0.6c11-py2.4.egg': 'bd639f9b0eac4c42497034dec2ec0c2b',
'setuptools-0.6c11-py2.5.egg': '64c94f3bf7a72a13ec83e0b24f2749b2',
'setuptools-0.6c11-py2.6.egg': 'bfa92100bd772d5a213eedd356d64086',
'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902',
'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de',
'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b',
'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03',
'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a',
'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6',
'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a',
}
import sys, os
try: from hashlib import md5
except ImportError: from md5 import md5
def _validate_md5(egg_name, data):
if egg_name in md5_data:
digest = md5(data).hexdigest()
if digest != md5_data[egg_name]:
print >>sys.stderr, (
"md5 validation of %s failed! (Possible download problem?)"
% egg_name
)
sys.exit(2)
return data
def use_setuptools(
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
download_delay=15
):
"""Automatically find/download setuptools and make it available on sys.path
`version` should be a valid setuptools version number that is available
as an egg for download under the `download_base` URL (which should end with
a '/'). `to_dir` is the directory where setuptools will be downloaded, if
it is not already available. If `download_delay` is specified, it should
be the number of seconds that will be paused before initiating a download,
should one be required. If an older version of setuptools is installed,
this routine will print a message to ``sys.stderr`` and raise SystemExit in
an attempt to abort the calling script.
"""
was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules
def do_download():
egg = download_setuptools(version, download_base, to_dir, download_delay)
sys.path.insert(0, egg)
import setuptools; setuptools.bootstrap_install_from = egg
try:
import pkg_resources
except ImportError:
return do_download()
try:
pkg_resources.require("setuptools>="+version); return
except pkg_resources.VersionConflict, e:
if was_imported:
print >>sys.stderr, (
"The required version of setuptools (>=%s) is not available, and\n"
"can't be installed while this script is running. Please install\n"
" a more recent version first, using 'easy_install -U setuptools'."
"\n\n(Currently using %r)"
) % (version, e.args[0])
sys.exit(2)
except pkg_resources.DistributionNotFound:
pass
del pkg_resources, sys.modules['pkg_resources'] # reload ok
return do_download()
def download_setuptools(
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
delay = 15
):
"""Download setuptools from a specified location and return its filename
`version` should be a valid setuptools version number that is available
as an egg for download under the `download_base` URL (which should end
with a '/'). `to_dir` is the directory where the egg will be downloaded.
`delay` is the number of seconds to pause before an actual download attempt.
"""
import urllib2, shutil
egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
url = download_base + egg_name
saveto = os.path.join(to_dir, egg_name)
src = dst = None
if not os.path.exists(saveto): # Avoid repeated downloads
try:
from distutils import log
if delay:
log.warn("""
---------------------------------------------------------------------------
This script requires setuptools version %s to run (even to display
help). I will attempt to download it for you (from
%s), but
you may need to enable firewall access for this script first.
I will start the download in %d seconds.
(Note: if this machine does not have network access, please obtain the file
%s
and place it in this directory before rerunning this script.)
---------------------------------------------------------------------------""",
version, download_base, delay, url
); from time import sleep; sleep(delay)
log.warn("Downloading %s", url)
src = urllib2.urlopen(url)
# Read/write all in one block, so we don't create a corrupt file
# if the download is interrupted.
data = _validate_md5(egg_name, src.read())
dst = open(saveto,"wb"); dst.write(data)
finally:
if src: src.close()
if dst: dst.close()
return os.path.realpath(saveto)
def main(argv, version=DEFAULT_VERSION):
"""Install or upgrade setuptools and EasyInstall"""
try:
import setuptools
except ImportError:
egg = None
try:
egg = download_setuptools(version, delay=0)
sys.path.insert(0,egg)
from setuptools.command.easy_install import main
return main(list(argv)+[egg]) # we're done here
finally:
if egg and os.path.exists(egg):
os.unlink(egg)
else:
if setuptools.__version__ == '0.0.1':
print >>sys.stderr, (
"You have an obsolete version of setuptools installed. Please\n"
"remove it from your system entirely before rerunning this script."
)
sys.exit(2)
req = "setuptools>="+version
import pkg_resources
try:
pkg_resources.require(req)
except pkg_resources.VersionConflict:
try:
from setuptools.command.easy_install import main
except ImportError:
from easy_install import main
main(list(argv)+[download_setuptools(delay=0)])
sys.exit(0) # try to force an exit
else:
if argv:
from setuptools.command.easy_install import main
main(argv)
else:
print "Setuptools version",version,"or greater has been installed."
print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
def update_md5(filenames):
"""Update our built-in md5 registry"""
import re
for name in filenames:
base = os.path.basename(name)
f = open(name,'rb')
md5_data[base] = md5(f.read()).hexdigest()
f.close()
data = [" %r: %r,\n" % it for it in md5_data.items()]
data.sort()
repl = "".join(data)
import inspect
srcfile = inspect.getsourcefile(sys.modules[__name__])
f = open(srcfile, 'rb'); src = f.read(); f.close()
match = re.search("\nmd5_data = {\n([^}]+)}", src)
if not match:
print >>sys.stderr, "Internal error!"
sys.exit(2)
src = src[:match.start(1)] + repl + src[match.end(1):]
f = open(srcfile,'w')
f.write(src)
f.close()
if __name__=='__main__':
if len(sys.argv)>2 and sys.argv[1]=='--md5update':
update_md5(sys.argv[2:])
else:
main(sys.argv[1:])

BIN
icons/CBZ.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
icons/EPUB.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

BIN
icons/Kindle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
icons/MOBI.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

BIN
icons/Other.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
icons/clear.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
icons/comic2ebook.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 KiB

BIN
icons/comic2ebook.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

BIN
icons/convert.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
icons/document_new.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
icons/error.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
icons/folder_new.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
icons/info.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
icons/warning.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

39
kcc.py
View File

@@ -1,4 +1,5 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012 Ciro Mattia Gonano <ciromattia@gmail.com>
#
@@ -16,19 +17,35 @@
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
#
__version__ = '2.6'
__license__ = 'ISC'
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>'
__version__ = '3.1'
__license__ = 'ISC'
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
__docformat__ = 'restructuredtext en'
from Tkinter import *
from kcc import gui
from sys import platform
import sys
import os
try:
# noinspection PyUnresolvedReferences
from PyQt4 import QtGui
except ImportError:
print "ERROR: PyQT4 is not installed!"
exit(1)
from kcc import KCC_gui
from multiprocessing import freeze_support
if platform == 'darwin':
if sys.platform == 'darwin':
os.environ['PATH'] = '/usr/local/bin:' + os.environ['PATH']
root = Tk()
app = gui.MainWindow(master=root,title="Kindle Comic Converter v" + __version__)
root.tkraise()
root.mainloop()
from kcc import KCC_ui_osx as KCC_ui
else:
from kcc import KCC_ui
freeze_support()
APP = QtGui.QApplication(sys.argv)
KCC = QtGui.QMainWindow()
UI = KCC_ui.Ui_KCC()
UI.setupUi(KCC)
GUI = KCC_gui.Ui_KCC(UI, KCC)
KCC.setWindowTitle("Kindle Comic Converter " + __version__)
KCC.show()
KCC.raise_()
sys.exit(APP.exec_())

628
kcc/KCC_gui.py Normal file
View File

@@ -0,0 +1,628 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2013 Ciro Mattia Gonano <ciromattia@gmail.com>
#
# Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the
# above copyright notice and this permission notice appear in all
# copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
# OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
__version__ = '3.1'
__license__ = 'ISC'
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
__docformat__ = 'restructuredtext en'
import os
import sys
import shutil
import traceback
import urllib2
import time
import comic2ebook
import kindlestrip
from image import ProfileData
from subprocess import call, Popen, STDOUT, PIPE
from PyQt4 import QtGui, QtCore
from xml.dom.minidom import parse
from HTMLParser import HTMLParser
class Icons:
def __init__(self):
self.deviceKindle = QtGui.QIcon()
self.deviceKindle.addPixmap(QtGui.QPixmap(":/Devices/icons/Kindle.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.deviceOther = QtGui.QIcon()
self.deviceOther.addPixmap(QtGui.QPixmap(":/Devices/icons/Other.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.MOBIFormat = QtGui.QIcon()
self.MOBIFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/MOBI.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.CBZFormat = QtGui.QIcon()
self.CBZFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/CBZ.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.EPUBFormat = QtGui.QIcon()
self.EPUBFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/EPUB.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.info = QtGui.QIcon()
self.info.addPixmap(QtGui.QPixmap(":/Status/icons/info.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.warning = QtGui.QIcon()
self.warning.addPixmap(QtGui.QPixmap(":/Status/icons/warning.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.error = QtGui.QIcon()
self.error.addPixmap(QtGui.QPixmap(":/Status/icons/error.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
class HTMLStripper(HTMLParser):
def __init__(self):
HTMLParser.__init__(self)
self.reset()
self.fed = []
def handle_data(self, d):
self.fed.append(d)
def get_data(self):
return ''.join(self.fed)
# noinspection PyBroadException
class VersionThread(QtCore.QThread):
def __init__(self, parent):
QtCore.QThread.__init__(self)
self.parent = parent
def __del__(self):
self.wait()
def run(self):
try:
XML = urllib2.urlopen('http://kcc.vulturis.eu/Version.php')
XML = parse(XML)
except Exception:
return
latestVersion = XML.childNodes[0].getElementsByTagName('latest')[0].childNodes[0].toxml()
if tuple(map(int, (latestVersion.split(".")))) > tuple(map(int, (__version__.split(".")))):
self.emit(QtCore.SIGNAL("addMessage"), '<a href="http://kcc.vulturis.eu/">'
'<b>New version is available!</b></a>', 'warning')
# noinspection PyBroadException
class WorkerThread(QtCore.QThread):
def __init__(self, parent):
QtCore.QThread.__init__(self)
self.parent = parent
def __del__(self):
self.wait()
def sync(self):
self.conversionAlive = self.parent.conversionAlive
def clean(self):
self.parent.needClean = True
self.emit(QtCore.SIGNAL("hideProgressBar"))
self.emit(QtCore.SIGNAL("addMessage"), '<b>Conversion interrupted.</b>', 'error')
self.emit(QtCore.SIGNAL("modeConvert"), True)
def run(self):
self.emit(QtCore.SIGNAL("modeConvert"), False)
profile = ProfileData.ProfileLabels[str(GUI.DeviceBox.currentText())]
argv = ["--profile=" + profile]
currentJobs = []
if GUI.MangaBox.isChecked():
argv.append("--manga-style")
if GUI.RotateBox.isChecked():
argv.append("--rotate")
if GUI.QualityBox.checkState() == 1:
argv.append("--quality=1")
elif GUI.QualityBox.checkState() == 2:
argv.append("--quality=2")
if self.parent.currentMode > 1:
if GUI.ProcessingBox.isChecked():
argv.append("--noprocessing")
if GUI.UpscaleBox.isChecked():
argv.append("--upscale")
if GUI.NoRotateBox.isChecked():
argv.append("--nosplitrotate")
if GUI.BorderBox.isChecked():
argv.append("--blackborders")
if GUI.StretchBox.isChecked():
argv.append("--stretch")
if GUI.NoDitheringBox.isChecked():
argv.append("--forcepng")
if float(self.parent.GammaValue) > 0.09:
argv.append("--gamma=" + self.parent.GammaValue)
if str(GUI.FormatBox.currentText()) == 'CBZ':
argv.append("--cbz-output")
if self.parent.currentMode > 2:
argv.append("--customwidth=" + str(GUI.customWidth.text()))
argv.append("--customheight=" + str(GUI.customHeight.text()))
if GUI.ColorBox.isChecked():
argv.append("--forcecolor")
for i in range(GUI.JobList.count()):
currentJobs.append(str(GUI.JobList.item(i).text()))
GUI.JobList.clear()
for job in currentJobs:
time.sleep(0.5)
if not self.conversionAlive:
self.clean()
return
self.errors = False
self.emit(QtCore.SIGNAL("addMessage"), '<b>Source:</b> ' + job, 'info')
if str(GUI.FormatBox.currentText()) == 'CBZ':
self.emit(QtCore.SIGNAL("addMessage"), 'Creating CBZ file...', 'info')
else:
self.emit(QtCore.SIGNAL("addMessage"), 'Creating EPUB file...', 'info')
jobargv = list(argv)
jobargv.append(job)
try:
outputPath = comic2ebook.main(jobargv, self)
self.emit(QtCore.SIGNAL("hideProgressBar"))
except UserWarning as warn:
if not self.conversionAlive:
self.clean()
return
else:
self.errors = True
self.emit(QtCore.SIGNAL("addMessage"), str(warn), 'warning')
self.emit(QtCore.SIGNAL("addMessage"), 'KCC failed to create output file!', 'warning')
except Exception as err:
self.errors = True
type_, value_, traceback_ = sys.exc_info()
self.emit(QtCore.SIGNAL("showDialog"), "Error during conversion %s:\n\n%s\n\nTraceback:\n%s"
% (jobargv[-1], str(err), traceback.format_tb(traceback_)))
self.emit(QtCore.SIGNAL("addMessage"), 'KCC failed to create EPUB!', 'error')
if not self.conversionAlive:
os.remove(outputPath)
self.clean()
return
if not self.errors:
if str(GUI.FormatBox.currentText()) == 'CBZ':
self.emit(QtCore.SIGNAL("addMessage"), 'Creating CBZ file... Done!', 'info', True)
else:
self.emit(QtCore.SIGNAL("addMessage"), 'Creating EPUB file... Done!', 'info', True)
if str(GUI.FormatBox.currentText()) == 'MOBI':
self.emit(QtCore.SIGNAL("addMessage"), 'Creating MOBI file...', 'info')
self.emit(QtCore.SIGNAL("progressBarTick"), 1)
try:
self.kindlegenErrorCode = 0
if os.path.getsize(outputPath) < 367001600:
output = Popen('kindlegen "' + outputPath + '"', stdout=PIPE, stderr=STDOUT, shell=True)
for line in output.stdout:
# ERROR: Generic error
if "Error(" in line:
self.kindlegenErrorCode = 1
self.kindlegenError = line
# ERROR: EPUB too big
if ":E23026:" in line:
self.kindlegenErrorCode = 23026
else:
# ERROR: EPUB too big
self.kindlegenErrorCode = 23026
except:
# ERROR: Unknown generic error
self.kindlegenErrorCode = 1
continue
if not self.conversionAlive:
os.remove(outputPath)
os.remove(outputPath.replace('.epub', '.mobi'))
self.clean()
return
if self.kindlegenErrorCode == 0:
self.emit(QtCore.SIGNAL("addMessage"), 'Creating MOBI file... Done!', 'info', True)
self.emit(QtCore.SIGNAL("addMessage"), 'Removing SRCS header...', 'info')
os.remove(outputPath)
mobiPath = outputPath.replace('.epub', '.mobi')
shutil.move(mobiPath, mobiPath + '_tostrip')
try:
kindlestrip.main((mobiPath + '_tostrip', mobiPath))
except Exception:
self.errors = True
if not self.errors:
os.remove(mobiPath + '_tostrip')
self.emit(QtCore.SIGNAL("addMessage"), 'Removing SRCS header... Done!', 'info', True)
else:
shutil.move(mobiPath + '_tostrip', 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')
else:
epubSize = (os.path.getsize(outputPath))/1024/1024
os.remove(outputPath)
if os.path.exists(outputPath.replace('.epub', '.mobi')):
os.remove(outputPath.replace('.epub', '.mobi'))
self.emit(QtCore.SIGNAL("addMessage"), 'KindleGen failed to create MOBI!', 'error')
if self.kindlegenErrorCode == 1 and self.kindlegenError:
self.emit(QtCore.SIGNAL("showDialog"), "KindleGen error:\n\n" + self.kindlegenError)
if self.kindlegenErrorCode == 23026:
self.emit(QtCore.SIGNAL("addMessage"), 'Created EPUB file was too big.',
'error')
self.emit(QtCore.SIGNAL("addMessage"), 'EPUB file: ' + str(epubSize) + 'MB.'
' Supported size: ~300MB.', 'error')
self.emit(QtCore.SIGNAL("hideProgressBar"))
self.parent.needClean = True
self.emit(QtCore.SIGNAL("addMessage"), '<b>All jobs completed.</b>', 'info')
self.emit(QtCore.SIGNAL("modeConvert"), True)
# noinspection PyBroadException
class Ui_KCC(object):
def selectDir(self):
if self.needClean:
self.needClean = False
GUI.JobList.clear()
# Dirty, dirty way but OS native QFileDialogs don't support directory multiselect
dirDialog = QtGui.QFileDialog(MainWindow, 'Select directory', self.lastPath)
dirDialog.setFileMode(dirDialog.Directory)
dirDialog.setOption(dirDialog.ShowDirsOnly, True)
dirDialog.setOption(dirDialog.DontUseNativeDialog, True)
l = dirDialog.findChild(QtGui.QListView, "listView")
t = dirDialog.findChild(QtGui.QTreeView)
if l:
l.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
if t:
t.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
if dirDialog.exec_() == 1:
dnames = dirDialog.selectedFiles()
else:
dnames = ""
# Lame UTF-8 security measure
for dname in dnames:
try:
str(dname)
except Exception:
QtGui.QMessageBox.critical(MainWindow, 'KCC Error', "Path cannot contain non-ASCII characters.",
QtGui.QMessageBox.Ok)
return
if str(dname) != "":
if sys.platform == 'win32':
dname = dname.replace('/', '\\')
self.lastPath = os.path.abspath(os.path.join(str(dname), os.pardir))
GUI.JobList.addItem(dname)
def selectFile(self):
if self.needClean:
self.needClean = False
GUI.JobList.clear()
if self.UnRAR:
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')
# Lame UTF-8 security measure
for fname in fnames:
try:
str(fname)
except Exception:
QtGui.QMessageBox.critical(MainWindow, 'KCC Error', "Path cannot contain non-ASCII characters.",
QtGui.QMessageBox.Ok)
return
if str(fname) != "":
self.lastPath = os.path.abspath(os.path.join(str(fname), os.pardir))
GUI.JobList.addItem(fname)
def clearJobs(self):
GUI.JobList.clear()
def modeBasic(self):
self.currentMode = 1
MainWindow.setMinimumSize(QtCore.QSize(420, 270))
MainWindow.setMaximumSize(QtCore.QSize(420, 270))
MainWindow.resize(420, 270)
GUI.BasicModeButton.setStyleSheet('font-weight:Bold;')
GUI.AdvModeButton.setStyleSheet('font-weight:Normal;')
GUI.FormatBox.setCurrentIndex(0)
GUI.FormatBox.setEnabled(False)
GUI.OptionsAdvanced.setEnabled(False)
GUI.OptionsAdvancedGamma.setEnabled(False)
GUI.OptionsExpert.setEnabled(False)
GUI.ProcessingBox.hide()
GUI.UpscaleBox.hide()
GUI.NoRotateBox.hide()
GUI.MangaBox.setEnabled(True)
def modeAdvanced(self):
self.currentMode = 2
MainWindow.setMinimumSize(QtCore.QSize(420, 345))
MainWindow.setMaximumSize(QtCore.QSize(420, 345))
MainWindow.resize(420, 345)
GUI.BasicModeButton.setStyleSheet('font-weight:Normal;')
GUI.AdvModeButton.setStyleSheet('font-weight:Bold;')
GUI.FormatBox.setEnabled(True)
GUI.ProcessingBox.show()
GUI.UpscaleBox.show()
GUI.NoRotateBox.show()
GUI.OptionsAdvancedGamma.setEnabled(True)
GUI.OptionsAdvanced.setEnabled(True)
GUI.OptionsExpert.setEnabled(False)
GUI.MangaBox.setEnabled(True)
def modeExpert(self, KFA=False):
self.modeAdvanced()
self.currentMode = 3
MainWindow.setMinimumSize(QtCore.QSize(420, 380))
MainWindow.setMaximumSize(QtCore.QSize(420, 380))
MainWindow.resize(420, 380)
GUI.OptionsExpert.setEnabled(True)
if KFA:
GUI.ColorBox.setCheckState(2)
GUI.FormatBox.setCurrentIndex(0)
GUI.FormatBox.setEnabled(False)
else:
GUI.FormatBox.setEnabled(True)
GUI.MangaBox.setCheckState(0)
GUI.MangaBox.setEnabled(False)
def modeConvert(self, enable):
if self.currentMode != 3:
GUI.BasicModeButton.setEnabled(enable)
GUI.AdvModeButton.setEnabled(enable)
GUI.DirectoryButton.setEnabled(enable)
GUI.ClearButton.setEnabled(enable)
GUI.FileButton.setEnabled(enable)
GUI.DeviceBox.setEnabled(enable)
GUI.FormatBox.setEnabled(enable)
GUI.OptionsBasic.setEnabled(enable)
GUI.OptionsAdvanced.setEnabled(enable)
GUI.OptionsAdvancedGamma.setEnabled(enable)
GUI.OptionsExpert.setEnabled(enable)
if enable:
self.conversionAlive = False
self.worker.sync()
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(":/Other/icons/convert.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
GUI.ConvertButton.setIcon(icon)
GUI.ConvertButton.setText('Convert')
GUI.ConvertButton.setEnabled(True)
if self.currentMode == 1:
self.modeBasic()
elif self.currentMode == 2:
self.modeAdvanced()
elif self.currentMode == 3:
self.modeExpert()
else:
self.conversionAlive = True
self.worker.sync()
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(":/Other/icons/clear.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
GUI.ConvertButton.setIcon(icon)
GUI.ConvertButton.setText('Abort')
GUI.ConvertButton.setEnabled(True)
def changeGamma(self, value):
value = float(value)
value = '%.2f' % (value/100)
if float(value) <= 0.09:
GUI.GammaLabel.setText('Gamma: Auto')
else:
GUI.GammaLabel.setText('Gamma: ' + str(value))
self.GammaValue = value
def toggleNoSplitRotate(self, value):
if value:
GUI.RotateBox.setEnabled(False)
GUI.RotateBox.setChecked(False)
else:
GUI.RotateBox.setEnabled(True)
def toggleUpscale(self, value):
if value:
GUI.StretchBox.setChecked(False)
def toggleStretch(self, value):
if value:
GUI.UpscaleBox.setChecked(False)
def changeDevice(self, value):
if value == 12:
GUI.BasicModeButton.setEnabled(False)
GUI.AdvModeButton.setEnabled(False)
self.addMessage('<a href="https://github.com/ciromattia/kcc/wiki/NonKindle-devices">'
'List of supported Non-Kindle devices</a>', 'info')
self.modeExpert()
elif value == 11:
GUI.BasicModeButton.setEnabled(False)
GUI.AdvModeButton.setEnabled(False)
self.modeExpert(True)
elif self.currentMode == 3:
GUI.BasicModeButton.setEnabled(True)
GUI.AdvModeButton.setEnabled(True)
self.modeBasic()
if value in [0, 1, 5, 6, 7, 8, 9, 12]:
GUI.QualityBox.setCheckState(0)
GUI.QualityBox.setEnabled(False)
else:
GUI.QualityBox.setEnabled(True)
def stripTags(self, html):
s = HTMLStripper()
s.feed(html)
return s.get_data()
def addMessage(self, message, icon=None, replace=False):
if icon:
icon = eval('self.icons.' + icon)
item = QtGui.QListWidgetItem(icon, ' ' + self.stripTags(message))
else:
item = QtGui.QListWidgetItem(' ' + self.stripTags(message))
if replace:
GUI.JobList.takeItem(GUI.JobList.count()-1)
label = QtGui.QLabel(message)
label.setOpenExternalLinks(True)
if sys.platform == 'darwin':
font = QtGui.QFont()
font.setPointSize(11)
label.setFont(font)
item.setTextColor(QtGui.QColor("white"))
GUI.JobList.addItem(item)
GUI.JobList.setItemWidget(item, label)
GUI.JobList.scrollToBottom()
def showDialog(self, message):
QtGui.QMessageBox.critical(MainWindow, 'KCC Error', message, QtGui.QMessageBox.Ok)
def updateProgressbar(self, new=False, status=False):
if new == "status":
pass
GUI.ProgressBar.setFormat(status)
elif new:
GUI.ProgressBar.setMaximum(new - 1)
GUI.ProgressBar.reset()
GUI.ProgressBar.show()
else:
GUI.ProgressBar.setValue(GUI.ProgressBar.value() + 1)
def convertStart(self):
if self.conversionAlive:
GUI.ConvertButton.setEnabled(False)
self.addMessage('Process will be interrupted. Please wait.', 'warning')
self.conversionAlive = False
self.worker.sync()
else:
if self.needClean:
self.needClean = False
GUI.JobList.clear()
if GUI.JobList.count() == 0:
self.addMessage('No files selected! Please choose files to convert.', 'error')
self.needClean = True
return
if self.currentMode > 2 and (str(GUI.customWidth.text()) == '' or str(GUI.customHeight.text()) == ''):
GUI.JobList.clear()
self.addMessage('Target resolution is not set!', 'error')
self.needClean = True
return
self.worker.start()
def hideProgressBar(self):
GUI.ProgressBar.hide()
# noinspection PyUnusedLocal
def saveSettings(self, event):
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('options', QtCore.QVariant({'MangaBox': GUI.MangaBox.checkState(),
'RotateBox': GUI.RotateBox.checkState(),
'QualityBox': GUI.QualityBox.checkState(),
'ProcessingBox': GUI.ProcessingBox.checkState(),
'UpscaleBox': GUI.UpscaleBox.checkState(),
'NoRotateBox': GUI.NoRotateBox.checkState(),
'BorderBox': GUI.BorderBox.checkState(),
'StretchBox': GUI.StretchBox.checkState(),
'NoDitheringBox': GUI.NoDitheringBox.checkState(),
'ColorBox': GUI.ColorBox.checkState(),
'customWidth': GUI.customWidth.text(),
'customHeight': GUI.customHeight.text(),
'GammaSlider': float(self.GammaValue)*100}))
self.settings.sync()
def __init__(self, UI, KCC):
global GUI, MainWindow
GUI = UI
MainWindow = KCC
profiles = sorted(ProfileData.ProfileLabels.iterkeys())
self.icons = Icons()
self.settings = QtCore.QSettings('KindleComicConverter', 'KindleComicConverter')
self.lastPath = self.settings.value('lastPath', '', type=str)
self.lastDevice = self.settings.value('lastDevice', 10, type=int)
self.currentMode = self.settings.value('currentMode', 1, type=int)
self.currentFormat = self.settings.value('currentFormat', 0, type=int)
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.addMessage('<b>Welcome!</b>', 'info')
self.addMessage('<b>Remember:</b> All options have additional informations in tooltips.', 'info')
if call('kindlegen', stdout=PIPE, stderr=STDOUT, shell=True) == 0:
self.KindleGen = True
formats = ['MOBI', 'EPUB', 'CBZ']
versionCheck = Popen('kindlegen', stdout=PIPE, stderr=STDOUT, shell=True)
for line in versionCheck.stdout:
if "Amazon kindlegen" in line:
versionCheck = line.split('V')[1].split(' ')[0]
if tuple(map(int, (versionCheck.split(".")))) < tuple(map(int, ('2.9'.split(".")))):
self.addMessage('Your <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId='
'1000765211">kindlegen</a> is outdated! Creating MOBI might fail.'
' Please update <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId='
'1000765211">kindlegen</a> from Amazon\'s website.', 'warning')
break
else:
self.KindleGen = False
formats = ['EPUB', 'CBZ']
self.addMessage('Cannot find <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId='
'1000765211">kindlegen</a> in PATH! MOBI creation will be disabled.', 'warning')
rarExitCode = call('unrar', stdout=PIPE, stderr=STDOUT, shell=True)
if rarExitCode == 0 or rarExitCode == 7:
self.UnRAR = True
else:
self.UnRAR = False
self.addMessage('Cannot find <a href="http://www.rarlab.com/rar_add.htm">UnRAR</a>!'
' Processing of CBR/RAR files will be disabled.', 'warning')
GUI.BasicModeButton.clicked.connect(self.modeBasic)
GUI.AdvModeButton.clicked.connect(self.modeAdvanced)
GUI.DirectoryButton.clicked.connect(self.selectDir)
GUI.ClearButton.clicked.connect(self.clearJobs)
GUI.FileButton.clicked.connect(self.selectFile)
GUI.ConvertButton.clicked.connect(self.convertStart)
GUI.GammaSlider.valueChanged.connect(self.changeGamma)
GUI.NoRotateBox.stateChanged.connect(self.toggleNoSplitRotate)
GUI.UpscaleBox.stateChanged.connect(self.toggleUpscale)
GUI.StretchBox.stateChanged.connect(self.toggleStretch)
GUI.DeviceBox.activated.connect(self.changeDevice)
KCC.connect(self.worker, QtCore.SIGNAL("progressBarTick"), self.updateProgressbar)
KCC.connect(self.worker, QtCore.SIGNAL("modeConvert"), self.modeConvert)
KCC.connect(self.worker, QtCore.SIGNAL("addMessage"), self.addMessage)
KCC.connect(self.worker, QtCore.SIGNAL("showDialog"), self.showDialog)
KCC.connect(self.worker, QtCore.SIGNAL("hideProgressBar"), self.hideProgressBar)
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:
GUI.DeviceBox.addItem(self.icons.deviceOther, profile)
GUI.DeviceBox.setCurrentIndex(self.lastDevice)
for f in formats:
GUI.FormatBox.addItem(eval('self.icons.' + f + 'Format'), f)
if self.currentFormat > GUI.FormatBox.count():
GUI.FormatBox.setCurrentIndex(0)
self.currentFormat = 0
else:
GUI.FormatBox.setCurrentIndex(self.currentFormat)
for option in self.options:
if str(option) == "customWidth":
GUI.customWidth.setText(str(self.options[option]))
elif str(option) == "customHeight":
GUI.customHeight.setText(str(self.options[option]))
elif str(option) == "GammaSlider":
GUI.GammaSlider.setValue(int(self.options[option]))
self.changeGamma(int(self.options[option]))
else:
eval('GUI.' + str(option)).setCheckState(self.options[option])
if self.currentMode == 1:
self.modeBasic()
elif self.currentMode == 2:
self.modeAdvanced()
elif self.currentMode == 3:
self.modeExpert()
self.versionCheck.start()
self.hideProgressBar()
self.changeDevice(self.lastDevice)
self.worker.sync()

7893
kcc/KCC_rc.py Normal file

File diff suppressed because it is too large Load Diff

312
kcc/KCC_ui.py Normal file
View File

@@ -0,0 +1,312 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'KCC.ui'
#
# Created: Thu Jul 04 15:30:15 2013
# by: PyQt4 UI code generator 4.10.2
#
# 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(10, 254, 421, 61))
font = QtGui.QFont()
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)
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)
self.UpscaleBox.setFocusPolicy(QtCore.Qt.NoFocus)
self.UpscaleBox.setObjectName(_fromUtf8("UpscaleBox"))
self.gridLayout.addWidget(self.UpscaleBox, 1, 1, 1, 1)
self.StretchBox = QtGui.QCheckBox(self.OptionsAdvanced)
self.StretchBox.setFocusPolicy(QtCore.Qt.NoFocus)
self.StretchBox.setObjectName(_fromUtf8("StretchBox"))
self.gridLayout.addWidget(self.StretchBox, 3, 1, 1, 1)
self.NoDitheringBox = QtGui.QCheckBox(self.OptionsAdvanced)
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)
self.BorderBox.setFocusPolicy(QtCore.Qt.NoFocus)
self.BorderBox.setObjectName(_fromUtf8("BorderBox"))
self.gridLayout.addWidget(self.BorderBox, 3, 0, 1, 1)
self.NoRotateBox = QtGui.QCheckBox(self.OptionsAdvanced)
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.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.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.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.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.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.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(10, 230, 421, 41))
font = QtGui.QFont()
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))
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.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))
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))
self.JobList.setFocusPolicy(QtCore.Qt.NoFocus)
self.JobList.setProperty("showDropIndicator", False)
self.JobList.setSelectionMode(QtGui.QAbstractItemView.NoSelection)
self.JobList.setObjectName(_fromUtf8("JobList"))
self.BasicModeButton = QtGui.QPushButton(self.Form)
self.BasicModeButton.setGeometry(QtCore.QRect(10, 10, 195, 32))
font = QtGui.QFont()
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.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.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))
self.GammaLabel.setObjectName(_fromUtf8("GammaLabel"))
self.GammaSlider = QtGui.QSlider(self.OptionsAdvancedGamma)
self.GammaSlider.setGeometry(QtCore.QRect(110, 10, 270, 22))
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.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(10, 337, 421, 41))
font = QtGui.QFont()
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))
self.ColorBox.setFocusPolicy(QtCore.Qt.NoFocus)
self.ColorBox.setObjectName(_fromUtf8("ColorBox"))
self.OptionsExpertInternal = QtGui.QFrame(self.OptionsExpert)
self.OptionsExpertInternal.setGeometry(QtCore.QRect(100, 0, 295, 40))
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)
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))
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)
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))
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()
font.setPointSize(9)
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", "<html><head/><body><p>Enable image upscaling.<br/>Aspect ratio will be preserved.</p></body></html>", None))
self.UpscaleBox.setText(_translate("KCC", "Upscale images", None))
self.StretchBox.setToolTip(_translate("KCC", "<html><head/><body><p>Enable image stretching.<br/>Aspect ratio will be not preserved.</p></body></html>", None))
self.StretchBox.setText(_translate("KCC", "Stretch images", None))
self.NoDitheringBox.setToolTip(_translate("KCC", "<html><head/><body><p>Create PNG files instead JPEG.<br/><span style=\" font-weight:600;\">Only for non-Kindle devices!</span></p></body></html>", 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.NoRotateBox.setToolTip(_translate("KCC", "<html><head/><body><p>Disable splitting and rotation.</p></body></html>", 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", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'MS Shell Dlg 2\'; font-size:8pt; font-weight:400; font-style:normal;\">\n"
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-weight:600; text-decoration: underline;\">Unchecked - Normal quality mode<br /></span><span style=\" font-style:italic;\">Use it when Panel View support is not needed.</span><span style=\" font-weight:600; text-decoration: underline;\"><br /></span>- Maximum quality when zoom is not enabled.<br />- Poor quality when zoom is enabled.<br />- Lowest file size.</p>\n"
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-weight:600; text-decoration: underline;\">Indeterminate - High quality mode<br /></span><span style=\" font-style:italic;\">Not zoomed image </span><span style=\" font-weight:600; font-style:italic;\">might </span><span style=\" font-style:italic;\">be </span><span style=\" font-style:italic;\">a little blurry.</span><span style=\" font-weight:600; text-decoration: underline;\"><br /></span>- Medium/High quality when zoom is not enabled.<br />- Maximum quality when zoom is enabled.</p>\n"
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-weight:600; text-decoration: underline;\">Checked - Ultra quality mode<br /></span><span style=\" font-style:italic;\">Maximum possible quality.</span><span style=\" font-weight:600; text-decoration: underline;\"><br /></span>- Maximum quality when zoom is not enabled.<br />- Maximum quality when zoom is enabled.<br />- Very high file size.</p></body></html>", None))
self.QualityBox.setText(_translate("KCC", "High/Ultra quality", None))
self.RotateBox.setToolTip(_translate("KCC", "<html><head/><body><p>Disable page spliting.<br/>They will be rotated instead.</p></body></html>", 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", "<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>", 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

364
kcc/KCC_ui_osx.py Normal file
View File

@@ -0,0 +1,364 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'KCC-OSX.ui'
#
# Created: Fri Jun 21 18:23:35 2013
# by: PyQt4 UI code generator 4.10.1
#
# 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(9, 253, 421, 61))
font = QtGui.QFont()
font.setPointSize(9)
self.OptionsAdvanced.setFont(font)
self.OptionsAdvanced.setObjectName(_fromUtf8("OptionsAdvanced"))
self.gridLayout = QtGui.QGridLayout(self.OptionsAdvanced)
self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
self.ProcessingBox = QtGui.QCheckBox(self.OptionsAdvanced)
font = QtGui.QFont()
font.setPointSize(11)
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)
self.UpscaleBox.setFont(font)
self.UpscaleBox.setFocusPolicy(QtCore.Qt.NoFocus)
self.UpscaleBox.setObjectName(_fromUtf8("UpscaleBox"))
self.gridLayout.addWidget(self.UpscaleBox, 1, 1, 1, 1)
self.StretchBox = QtGui.QCheckBox(self.OptionsAdvanced)
font = QtGui.QFont()
font.setPointSize(11)
self.StretchBox.setFont(font)
self.StretchBox.setFocusPolicy(QtCore.Qt.NoFocus)
self.StretchBox.setObjectName(_fromUtf8("StretchBox"))
self.gridLayout.addWidget(self.StretchBox, 3, 1, 1, 1)
self.NoDitheringBox = QtGui.QCheckBox(self.OptionsAdvanced)
font = QtGui.QFont()
font.setPointSize(11)
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)
self.BorderBox.setFont(font)
self.BorderBox.setFocusPolicy(QtCore.Qt.NoFocus)
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)
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(8, 200, 151, 34))
font = QtGui.QFont()
font.setPointSize(11)
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(262, 200, 152, 34))
font = QtGui.QFont()
font.setPointSize(11)
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, 101, 41))
font = QtGui.QFont()
font.setPointSize(11)
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(5, 160, 156, 41))
font = QtGui.QFont()
font.setPointSize(11)
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, 157, 41))
font = QtGui.QFont()
font.setPointSize(11)
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, 101, 41))
font = QtGui.QFont()
font.setPointSize(11)
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(10, 233, 421, 41))
font = QtGui.QFont()
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.setPointSize(11)
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))
font = QtGui.QFont()
font.setPointSize(11)
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.setPointSize(11)
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.setPointSize(11)
self.JobList.setFont(font)
self.JobList.setFocusPolicy(QtCore.Qt.NoFocus)
self.JobList.setProperty("showDropIndicator", False)
self.JobList.setSelectionMode(QtGui.QAbstractItemView.NoSelection)
self.JobList.setObjectName(_fromUtf8("JobList"))
self.BasicModeButton = QtGui.QPushButton(self.Form)
self.BasicModeButton.setGeometry(QtCore.QRect(5, 10, 210, 41))
font = QtGui.QFont()
font.setPointSize(12)
font.setBold(False)
font.setWeight(50)
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(207, 10, 210, 41))
font = QtGui.QFont()
font.setPointSize(12)
font.setBold(False)
font.setWeight(50)
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, 303, 401, 41))
font = QtGui.QFont()
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.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.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, 35))
font = QtGui.QFont()
font.setPointSize(10)
font.setBold(True)
font.setWeight(75)
self.ProgressBar.setFont(font)
self.ProgressBar.setAutoFillBackground(True)
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(10, 335, 421, 41))
font = QtGui.QFont()
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)
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.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.setPointSize(12)
font.setBold(False)
font.setWeight(50)
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(45, 16777215))
font = QtGui.QFont()
font.setPointSize(12)
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.setPointSize(12)
font.setBold(False)
font.setWeight(50)
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.Minimum, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.customHeight.sizePolicy().hasHeightForWidth())
self.customHeight.setSizePolicy(sizePolicy)
self.customHeight.setMaximumSize(QtCore.QSize(45, 16777215))
font = QtGui.QFont()
font.setPointSize(12)
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()
font.setPointSize(9)
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", "<html><head/><body><p><span style=\" font-size:12pt;\">Disable image optimizations.</span></p></body></html>", None))
self.ProcessingBox.setText(_translate("KCC", "No optimisation", None))
self.UpscaleBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-size:12pt;\">Enable image upscaling.<br/>Aspect ratio will be preserved.</span></p></body></html>", None))
self.UpscaleBox.setText(_translate("KCC", "Upscale images", None))
self.StretchBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-size:12pt;\">Enable image stretching.<br/>Aspect ratio will be not preserved.</span></p></body></html>", None))
self.StretchBox.setText(_translate("KCC", "Stretch images", None))
self.NoDitheringBox.setToolTip(_translate("KCC", "<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>", None))
self.NoDitheringBox.setText(_translate("KCC", "PNG output", None))
self.BorderBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-size:12pt;\">Fill space around images with black color.</span></p></body></html>", None))
self.BorderBox.setText(_translate("KCC", "Black borders", None))
self.NoRotateBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-size:12pt;\">Disable splitting and rotation.</span></p></body></html>", 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", "<html><head/><body><p><span style=\" font-size:12pt;\">Enable right-to-left reading.</span></p></body></html>", None))
self.MangaBox.setText(_translate("KCC", "Manga mode", None))
self.QualityBox.setToolTip(_translate("KCC", "<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>", None))
self.QualityBox.setText(_translate("KCC", "High/Ultra quality", None))
self.RotateBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-size:12pt;\">Disable page spliting.<br/>They will be rotated instead.</span></p></body></html>", 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", "<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>", None))
self.GammaLabel.setText(_translate("KCC", "Gamma: Auto", None))
self.GammaSlider.setToolTip(_translate("KCC", "<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>", None))
self.ColorBox.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-size:12pt;\">Do not convert images to grayscale.</span></p></body></html>", None))
self.ColorBox.setText(_translate("KCC", "Color mode", None))
self.wLabel.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-size:12pt;\">Resolution of target device.</span></p></body></html>", None))
self.wLabel.setText(_translate("KCC", "Custom width: ", None))
self.customWidth.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-size:12pt;\">Resolution of target device.</span></p></body></html>", None))
self.customWidth.setInputMask(_translate("KCC", "0000; ", None))
self.hLabel.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-size:12pt;\">Resolution of target device.</span></p></body></html>", None))
self.hLabel.setText(_translate("KCC", "Custom height: ", None))
self.customHeight.setToolTip(_translate("KCC", "<html><head/><body><p><span style=\" font-size:12pt;\">Resolution of target device.</span></p></body></html>", 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

View File

@@ -1,4 +1,4 @@
__version__ = '2.0'
__license__ = 'ISC'
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>'
__version__ = '3.1'
__license__ = 'ISC'
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
__docformat__ = 'restructuredtext en'

View File

@@ -15,60 +15,63 @@
# PERFORMANCE OF THIS SOFTWARE.
#
__license__ = 'ISC'
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>'
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
__docformat__ = 'restructuredtext en'
import os
import zipfile
import rarfile
# noinspection PyBroadException
class CBxArchive:
def __init__(self, origFileName):
self.cbxexts = ['.zip', '.cbz', '.rar', '.cbr']
self.origFileName = origFileName
self.filename = os.path.splitext(origFileName)
if zipfile.is_zipfile(origFileName):
self.compressor = 'zip'
elif rarfile.is_rarfile(origFileName):
self.compressor = 'rar'
else:
self.compressor = None
def isCbxFile(self):
return self.filename[1].lower() in self.cbxexts
return self.compressor is not None
def extractCBZ(self, targetdir):
try:
from zipfile import ZipFile
except ImportError:
self.cbzFile = None
cbzFile = ZipFile(self.origFileName)
cbzFile = zipfile.ZipFile(self.origFileName)
filelist = []
for f in cbzFile.namelist():
if f.startswith('__MACOSX') or f.endswith('.DS_Store'):
pass # skip MacOS special files
if f.startswith('__MACOSX') or f.endswith('.DS_Store') or f.endswith('thumbs.db'):
pass # skip MacOS special files
elif f.endswith('/'):
try:
os.makedirs(os.path.join(targetdir, f))
except:
pass # the dir exists so we are going to extract the images only.
else:
cbzFile.extract(f, targetdir)
filelist.append(f)
cbzFile.extractall(targetdir, filelist)
def extractCBR(self, targetdir):
try:
import rarfile
except ImportError:
self.cbrFile = None
return
cbrFile = rarfile.RarFile(self.origFileName)
filelist = []
for f in cbrFile.namelist():
if f.startswith('__MACOSX') or f.endswith('.DS_Store'):
if f.startswith('__MACOSX') or f.endswith('.DS_Store') or f.endswith('thumbs.db'):
pass # skip MacOS special files
elif f.endswith('/'):
try:
os.makedirs(os.path.join(targetdir,f))
os.makedirs(os.path.join(targetdir, f))
except:
pass # the dir exists so we are going to extract the images only.
else:
cbrFile.extract(f, targetdir)
filelist.append(f)
cbrFile.extractall(targetdir, filelist)
def extract(self, targetdir):
if '.cbr' == self.filename[1].lower() or '.rar' == self.filename[1].lower():
print "\n" + targetdir + "\n"
if self.compressor == 'rar':
self.extractCBR(targetdir)
elif '.cbz' == self.filename[1].lower() or '.zip' == self.filename[1].lower():
elif self.compressor == 'zip':
self.extractCBZ(targetdir)
adir = os.listdir(targetdir)
if len(adir) == 1 and os.path.isdir(os.path.join(targetdir, adir[0])):

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012 Ciro Mattia Gonano <ciromattia@gmail.com>
#
@@ -16,21 +17,28 @@
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
#
__version__ = '2.6'
__version__ = '3.1'
__license__ = 'ISC'
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>'
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
__docformat__ = 'restructuredtext en'
import os
import sys
import tempfile
import re
import stat
import string
from shutil import move
from shutil import copyfile
from shutil import copytree
from shutil import rmtree
from shutil import make_archive
from optparse import OptionParser
from multiprocessing import Pool, Queue, freeze_support
try:
from PyQt4 import QtCore
except ImportError:
QtCore = None
import image
import cbxarchive
import pdfjpgextract
@@ -39,6 +47,11 @@ 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
else:
rotate = False
htmlpath = ''
postfix = ''
backref = 1
@@ -60,23 +73,77 @@ def buildHTML(path, imgfile):
"<head>\n",
"<title>", filename[0], "</title>\n",
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n",
"<link href=\"", "../" * (backref - 1),
"style.css\" type=\"text/css\" rel=\"stylesheet\"/>\n",
"</head>\n",
"<body>\n",
"<div class=\"fs\">\n",
"<div><img src=\"", "../" * backref, "Images/", postfix, imgfile, "\" alt=\"",
imgfile, "\" class=\"singlePage\"/></div>\n",
#"<div id=\"", filename[0], "-1\">\n",
#"<a class=\"app-amzn-magnify\" data-app-amzn-magnify='{\"targetId\":\"", filename[0],
#"-1-magTargetParent\", \"ordinal\":1}'></a>\n",
#"</div>\n",
#"<div id=\"", filename[0], "-1-magTargetParent\" class=\"target-mag-parent\">\n",
#"<div class=\"target-mag-lb\">\n",
#"</div>\n",
#"<div id=\"", filename[0], "-1-magTarget\" class=\"target-mag\">\n",
#"<img src=\"../" * backref, "Images/", postfix, imgfile, "\" alt=\"", imgfile, "\"/>\n",
#"</div></div>\n",
"</body>\n",
"</html>"
imgfile, "\" class=\"singlePage\"/></div>\n"
])
if options.panelview:
if rotate:
if options.righttoleft:
f.writelines(["<div id=\"BoxTL\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
"'{\"targetId\":\"BoxTL-Panel-Parent\", \"ordinal\":1}'></a></div>\n",
"<div id=\"BoxTR\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
"'{\"targetId\":\"BoxTR-Panel-Parent\", \"ordinal\":3}'></a></div>\n",
"<div id=\"BoxBL\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
"'{\"targetId\":\"BoxBL-Panel-Parent\", \"ordinal\":2}'></a></div>\n",
"<div id=\"BoxBR\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify="
"'{\"targetId\":\"BoxBR-Panel-Parent\", \"ordinal\":4}'></a></div>\n"
])
else:
f.writelines(["<div id=\"BoxTL\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
"'{\"targetId\":\"BoxTL-Panel-Parent\", \"ordinal\":2}'></a></div>\n",
"<div id=\"BoxTR\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
"'{\"targetId\":\"BoxTR-Panel-Parent\", \"ordinal\":4}'></a></div>\n",
"<div id=\"BoxBL\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
"'{\"targetId\":\"BoxBL-Panel-Parent\", \"ordinal\":1}'></a></div>\n",
"<div id=\"BoxBR\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify="
"'{\"targetId\":\"BoxBR-Panel-Parent\", \"ordinal\":3}'></a></div>\n"
])
else:
if options.righttoleft:
f.writelines(["<div id=\"BoxTL\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
"'{\"targetId\":\"BoxTL-Panel-Parent\", \"ordinal\":2}'></a></div>\n",
"<div id=\"BoxTR\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
"'{\"targetId\":\"BoxTR-Panel-Parent\", \"ordinal\":1}'></a></div>\n",
"<div id=\"BoxBL\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
"'{\"targetId\":\"BoxBL-Panel-Parent\", \"ordinal\":4}'></a></div>\n",
"<div id=\"BoxBR\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify="
"'{\"targetId\":\"BoxBR-Panel-Parent\", \"ordinal\":3}'></a></div>\n"
])
else:
f.writelines(["<div id=\"BoxTL\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
"'{\"targetId\":\"BoxTL-Panel-Parent\", \"ordinal\":1}'></a></div>\n",
"<div id=\"BoxTR\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
"'{\"targetId\":\"BoxTR-Panel-Parent\", \"ordinal\":2}'></a></div>\n",
"<div id=\"BoxBL\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify=",
"'{\"targetId\":\"BoxBL-Panel-Parent\", \"ordinal\":3}'></a></div>\n",
"<div id=\"BoxBR\"><a class=\"app-amzn-magnify\" data-app-amzn-magnify="
"'{\"targetId\":\"BoxBR-Panel-Parent\", \"ordinal\":4}'></a></div>\n"
])
if options.quality == 2:
imgfilepv = string.split(imgfile, ".")
imgfilepv[0] += "_kcchq"
imgfilepv = string.join(imgfilepv, ".")
else:
imgfilepv = imgfile
f.writelines(["<div id=\"BoxTL-Panel-Parent\" class=\"target-mag-parent\"><div id=\"BoxTL-Panel\" class=\"",
"target-mag\"><img src=\"", "../" * backref, "Images/", postfix, imgfilepv, "\" alt=\"",
imgfilepv, "\"/></div></div>\n",
"<div id=\"BoxTR-Panel-Parent\" class=\"target-mag-parent\"><div id=\"BoxTR-Panel\" class=\"",
"target-mag\"><img src=\"", "../" * backref, "Images/", postfix, imgfilepv, "\" alt=\"",
imgfilepv, "\"/></div></div>\n",
"<div id=\"BoxBL-Panel-Parent\" class=\"target-mag-parent\"><div id=\"BoxBL-Panel\" class=\"",
"target-mag\"><img src=\"", "../" * backref, "Images/", postfix, imgfilepv, "\" alt=\"",
imgfilepv, "\"/></div></div>\n",
"<div id=\"BoxBR-Panel-Parent\" class=\"target-mag-parent\"><div id=\"BoxBR-Panel\" class=\"",
"target-mag\"><img src=\"", "../" * backref, "Images/", postfix, imgfilepv, "\" alt=\"",
imgfilepv, "\"/></div></div>\n"
])
f.writelines(["</div>\n</body>\n</html>"])
f.close()
return path, imgfile
@@ -98,14 +165,17 @@ def buildBlankHTML(path):
def buildNCX(dstdir, title, chapters):
from uuid import uuid4
options.uuid = str(uuid4())
options.uuid = options.uuid.encode('utf-8')
ncxfile = os.path.join(dstdir, 'OEBPS', 'toc.ncx')
f = open(ncxfile, "w")
f.writelines(["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
"<!DOCTYPE ncx PUBLIC \"-//NISO//DTD ncx 2005-1//EN\" ",
"http://www.daisy.org/z3986/2005/ncx-2005-1.dtd\">\n",
"\"http://www.daisy.org/z3986/2005/ncx-2005-1.dtd\">\n",
"<ncx version=\"2005-1\" xml:lang=\"en-US\" xmlns=\"http://www.daisy.org/z3986/2005/ncx/\">\n",
"<head>\n",
"<meta name=\"dtb:uid\" content=\"015ffaec-9340-42f8-b163-a0c5ab7d0611\"/>\n",
"<meta name=\"dtb:uid\" content=\"", options.uuid, "\"/>\n",
"<meta name=\"dtb:depth\" content=\"2\"/>\n",
"<meta name=\"dtb:totalPageCount\" content=\"0\"/>\n",
"<meta name=\"dtb:maxPageNumber\" content=\"0\"/>\n",
@@ -117,19 +187,18 @@ def buildNCX(dstdir, title, chapters):
folder = chapter[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
title = os.path.basename(folder)
filename = getImageFileName(os.path.join(folder, chapter[1]))
f.write("<navPoint id=\"" + folder.replace('/', '_') + "\"><navLabel><text>" + title
+ "</text></navLabel><content src=\"" + filename[0] + ".html\"/></navPoint>\n")
f.write("<navPoint id=\"" + folder.replace('/', '_').replace('\\', '_') + "\"><navLabel><text>" + title
+ "</text></navLabel><content src=\"" + filename[0].replace("\\", "/") + ".html\"/></navPoint>\n")
f.write("</navMap>\n</ncx>")
f.close()
return
def buildOPF(profile, dstdir, title, filelist, cover=None, righttoleft=False):
def buildOPF(dstdir, title, filelist, cover=None):
opffile = os.path.join(dstdir, 'OEBPS', 'content.opf')
# read the first file resolution
profilelabel, deviceres, palette = image.ProfileData.Profiles[profile]
profilelabel, deviceres, palette, gamma, panelviewsize = options.profileData
imgres = str(deviceres[0]) + "x" + str(deviceres[1])
if righttoleft:
if options.righttoleft:
writingmode = "horizontal-rl"
facing = "right"
facing1 = "right"
@@ -139,9 +208,6 @@ def buildOPF(profile, dstdir, title, filelist, cover=None, righttoleft=False):
facing = "left"
facing1 = "left"
facing2 = "right"
from uuid import uuid4
uuid = str(uuid4())
uuid = uuid.encode('utf-8')
f = open(opffile, "w")
f.writelines(["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
"<package version=\"2.0\" unique-identifier=\"BookID\" xmlns=\"http://www.idpf.org/2007/opf\">\n",
@@ -149,70 +215,87 @@ def buildOPF(profile, dstdir, title, filelist, cover=None, righttoleft=False):
"xmlns:opf=\"http://www.idpf.org/2007/opf\">\n",
"<dc:title>", title, "</dc:title>\n",
"<dc:language>en-US</dc:language>\n",
"<dc:identifier id=\"BookID\" opf:scheme=\"UUID\">", uuid, "</dc:identifier>\n",
"<dc:identifier id=\"BookID\" opf:scheme=\"UUID\">", options.uuid, "</dc:identifier>\n",
"<meta name=\"RegionMagnification\" content=\"true\"/>\n",
"<meta name=\"cover\" content=\"cover\"/>\n",
"<meta name=\"book-type\" content=\"comic\"/>\n",
"<meta name=\"zero-gutter\" content=\"true\"/>\n",
"<meta name=\"zero-margin\" content=\"true\"/>\n",
"<meta name=\"fixed-layout\" content=\"true\"/>\n",
"<meta name=\"orientation-lock\" content=\"none\"/>\n",
"<meta name=\"original-resolution\" content=\"", imgres, "\"/>\n",
"<meta name=\"fixed-layout\" content=\"true\"/>\n"
])
if options.landscapemode:
f.writelines(["<meta name=\"rendition:orientation\" content=\"auto\"/>\n",
"<meta name=\"orientation-lock\" content=\"none\"/>\n"])
else:
f.writelines(["<meta name=\"rendition:orientation\" content=\"portrait\"/>\n",
"<meta name=\"orientation-lock\" content=\"portrait\"/>\n"])
f.writelines(["<meta name=\"original-resolution\" content=\"", imgres, "\"/>\n",
"<meta name=\"primary-writing-mode\" content=\"", writingmode, "\"/>\n",
"<meta name=\"rendition:layout\" content=\"pre-paginated\"/>\n",
"<meta name=\"rendition:orientation\" content=\"auto\"/>\n",
"</metadata>\n<manifest>\n<item id=\"ncx\" href=\"toc.ncx\" ",
"media-type=\"application/x-dtbncx+xml\"/>\n"
])
# set cover
"media-type=\"application/x-dtbncx+xml\"/>\n"])
if cover is not None:
filename = getImageFileName(cover.replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\'))
if '.png' == filename[1]:
mt = 'image/png'
else:
mt = 'image/jpeg'
f.write("<item id=\"cover\" href=\"" + filename[0] + filename[1] + "\" media-type=\"" + mt + "\"/>\n")
f.write("<item id=\"cover\" href=\"Images/cover" + filename[1] + "\" media-type=\"" + mt + "\"/>\n")
reflist = []
for path in filelist:
folder = path[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\')
folder = path[0].replace(os.path.join(dstdir, 'OEBPS'), '').lstrip('/').lstrip('\\\\').replace("\\", "/")
filename = getImageFileName(path[1])
uniqueid = os.path.join(folder, filename[0]).replace('/', '_')
uniqueid = os.path.join(folder, filename[0]).replace('/', '_').replace('\\', '_')
reflist.append(uniqueid)
f.write("<item id=\"page_" + uniqueid + "\" href=\""
+ os.path.join(folder.replace('Images', 'Text'), filename[0])
+ folder.replace('Images', 'Text') + "/" + filename[0]
+ ".html\" media-type=\"application/xhtml+xml\"/>\n")
if '.png' == filename[1]:
mt = 'image/png'
else:
mt = 'image/jpeg'
f.write("<item id=\"img_" + uniqueid + "\" href=\"" + os.path.join(folder, path[1]) + "\" media-type=\""
f.write("<item id=\"img_" + uniqueid + "\" href=\"" + folder + "/" + path[1] + "\" media-type=\""
+ mt + "\"/>\n")
if (options.profile == 'K4' or options.profile == 'KHD') and splittedSomething:
f.write("<item id=\"blank-page\" href=\""
+ os.path.join('Text', 'blank.html')
+ "\" media-type=\"application/xhtml+xml\"/>\n")
if options.landscapemode and splitCount > 0:
splitCountUsed = 1
while splitCountUsed <= splitCount:
f.write("<item id=\"blank-page" + str(splitCountUsed) +
"\" href=\"Text/blank.html\" media-type=\"application/xhtml+xml\"/>\n")
splitCountUsed += 1
f.write("<item id=\"css\" href=\"Text/style.css\" media-type=\"text/css\"/>\n")
f.write("</manifest>\n<spine toc=\"ncx\">\n")
splitCountUsed = 1
for entry in reflist:
if entry.endswith("-1"):
if (righttoleft and facing == 'left') or (not righttoleft and facing == 'right') and \
(options.profile == 'K4' or options.profile == 'KHD'):
f.write("<itemref idref=\"blank-page\" properties=\"layout-blank\"/>\n")
f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing1 + "\"/>\n")
elif entry.endswith("-2"):
f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing2 + "\"/>\n")
if righttoleft:
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("<itemref idref=\"blank-page" + str(splitCountUsed) + "\" properties=\"layout-blank\"/>\n")
splitCountUsed += 1
if options.landscapemode:
f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing1 + "\"/>\n")
else:
f.write("<itemref idref=\"page_" + entry + "\"/>\n")
elif "_kccb" in str(entry):
if options.landscapemode:
f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing2 + "\"/>\n")
else:
f.write("<itemref idref=\"page_" + entry + "\"/>\n")
if options.righttoleft:
facing = "right"
else:
facing = "left"
else:
f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing + "\"/>\n")
if options.landscapemode:
f.write("<itemref idref=\"page_" + entry + "\" properties=\"page-spread-" + facing + "\"/>\n")
else:
f.write("<itemref idref=\"page_" + entry + "\"/>\n")
if facing == 'right':
facing = 'left'
else:
facing = 'right'
f.write("</spine>\n<guide>\n</guide>\n</package>\n")
f.close()
# finish with standard ePub folders
os.mkdir(os.path.join(dstdir, 'META-INF'))
f = open(os.path.join(dstdir, 'mimetype'), 'w')
f.write('application/epub+zip')
@@ -233,62 +316,137 @@ def getImageFileName(imgfile):
if filename[0].startswith('.') or\
(filename[1].lower() != '.png' and
filename[1].lower() != '.jpg' and
filename[1].lower() != '.gif' and
filename[1].lower() != '.tif' and
filename[1].lower() != '.tiff' and
filename[1].lower() != '.bmp' and
filename[1].lower() != '.jpeg'):
return None
return filename
def isInFilelist(filename, filelist):
filename = os.path.splitext(filename)
seen = False
for item in filelist:
if filename[0] == item[0]:
seen = True
return seen
def applyImgOptimization(img, isSplit=False, toRight=False):
img.optimizeImage()
def applyImgOptimization(img, isSplit, toRight, options, overrideQuality=5):
img.cropWhiteSpace(10.0)
if options.cutpagenumbers:
img.cutPageNumber()
img.resizeImage(options.upscale, options.stretch, options.black_borders, isSplit, toRight)
img.quantizeImage()
img.optimizeImage(options.gamma)
if overrideQuality != 5:
img.resizeImage(options.upscale, options.stretch, options.black_borders, isSplit, toRight,
options.landscapemode, 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.quantizeImage()
def dirImgProcess(path):
global options
global splittedSomething
splittedSomething = False
global options, splitCount
work = []
pagenumber = 0
pagenumbermodifier = 0
queue = Queue()
pool = Pool(None, fileImgProcess_init, [queue, options])
for (dirpath, dirnames, filenames) in os.walk(path):
for afile in filenames:
if getImageFileName(afile) is not None:
if options.verbose:
print "Optimizing " + afile + " for " + options.profile
else:
print ".",
img = image.ComicPage(os.path.join(dirpath, afile), options.profile)
split = img.splitPage(dirpath, options.righttoleft, options.rotate)
if split is not None:
splittedSomething = True
if options.verbose:
print "Splitted " + afile
if options.righttoleft:
toRight1 = False
toRight2 = True
else:
toRight1 = True
toRight2 = False
img0 = image.ComicPage(split[0], options.profile)
applyImgOptimization(img0, True, toRight1)
img0.saveToDir(dirpath)
img1 = image.ComicPage(split[1], options.profile)
applyImgOptimization(img1, True, toRight2)
img1.saveToDir(dirpath)
else:
applyImgOptimization(img)
img.saveToDir(dirpath)
pagenumber += 1
work.append([afile, dirpath, pagenumber])
if GUI:
GUI.emit(QtCore.SIGNAL("progressBarTick"), pagenumber)
if len(work) > 0:
splitpages = pool.map_async(func=fileImgProcess, iterable=work)
pool.close()
if GUI:
while not splitpages.ready():
# noinspection PyBroadException
try:
queue.get(True, 1)
except:
pass
if not GUI.conversionAlive:
pool.terminate()
rmtree(path)
raise UserWarning("Conversion interrupted.")
GUI.emit(QtCore.SIGNAL("progressBarTick"))
pool.join()
queue.close()
try:
splitpages = splitpages.get()
except:
rmtree(path)
raise RuntimeError("One of workers crashed. Cause: " + str(sys.exc_info()[1]))
splitpages = filter(None, splitpages)
splitpages.sort()
for page in splitpages:
if (page + pagenumbermodifier) % 2 == 0:
splitCount += 1
pagenumbermodifier += 1
pagenumbermodifier += 1
else:
rmtree(path)
raise UserWarning("Source directory is empty.")
def fileImgProcess_init(queue, options):
fileImgProcess.queue = queue
fileImgProcess.options = options
# noinspection PyUnresolvedReferences
def fileImgProcess(work):
afile = work[0]
dirpath = work[1]
pagenumber = work[2]
options = fileImgProcess.options
output = None
if options.verbose:
print "Optimizing " + afile + " for " + options.profile
else:
print ".",
fileImgProcess.queue.put(".")
img = image.ComicPage(os.path.join(dirpath, afile), options.profileData)
if options.quality == 2:
wipe = False
else:
wipe = True
if options.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:
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)
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":
img2.image = img2.image.rotate(90)
applyImgOptimization(img2, False, False, options, 0)
img2.saveToDir(dirpath, options.forcepng, options.forcecolor, True, split)
return output
def genEpubStruct(path):
@@ -296,136 +454,401 @@ def genEpubStruct(path):
filelist = []
chapterlist = []
cover = None
_, deviceres, _, _, panelviewsize = options.profileData
sanitizeTree(os.path.join(path, 'OEBPS', 'Images'))
os.mkdir(os.path.join(path, 'OEBPS', 'Text'))
f = open(os.path.join(path, 'OEBPS', 'Text', 'style.css'), 'w')
# DON'T COMPRESS CSS. KINDLE WILL FAIL TO PARSE IT.
# Generic Panel View support + Margins fix for Non-Kindle devices.
f.writelines(["@page {\n",
"margin-bottom: 0;\n",
"margin-top: 0\n",
"}\n",
"body {\n",
"display: block;\n",
"margin-bottom: 0;\n",
"margin-left: 0;\n",
"margin-right: 0;\n",
"margin-top: 0;\n",
"padding-bottom: 0;\n",
"padding-left: 0;\n",
"padding-right: 0;\n",
"padding-top: 0;\n",
"text-align: left\n",
"}\n",
"div.fs {\n",
"height: ", str(deviceres[1]), "px;\n",
"width: ", str(deviceres[0]), "px;\n",
"position: relative;\n",
"display: block;\n",
"text-align: center\n",
"}\n",
"div.fs a {\n",
"display: block;\n",
"width : 100%;\n",
"height: 100%;\n",
"}\n",
"div.fs div {\n",
"position: absolute;\n",
"}\n",
"img.singlePage {\n",
"position: absolute;\n",
"height: ", str(deviceres[1]), "px;\n",
"width: ", str(deviceres[0]), "px;\n",
"}\n",
"div.target-mag-parent {\n",
"width:100%;\n",
"height:100%;\n",
"display:none;\n",
"}\n",
"div.target-mag {\n",
"position: absolute;\n",
"display: block;\n",
"overflow: hidden;\n",
"}\n",
"div.target-mag img {\n",
"position: absolute;\n",
"height: ", str(panelviewsize[1]), "px;\n",
"width: ", str(panelviewsize[0]), "px;\n",
"}\n",
"#BoxTL {\n",
"top: 0;\n",
"left: 0;\n",
"height: 50%;\n",
"width: 50%;\n",
"}\n",
"#BoxTR {\n",
"top: 0;\n",
"right: 0;\n",
"height: 50%;\n",
"width: 50%;\n",
"}\n",
"#BoxBL {\n",
"bottom: 0;\n",
"left: 0;\n",
"height: 50%;\n",
"width: 50%;\n",
"}\n",
"#BoxBR {\n",
"bottom: 0;\n",
"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')):
chapter = False
for afile in filenames:
filename = getImageFileName(afile)
if filename is not None:
# put credits at the end
if "credit" in afile.lower():
os.rename(os.path.join(dirpath, afile), os.path.join(dirpath, 'ZZZ999_' + afile))
afile = 'ZZZ999_' + afile
if "+" in afile.lower() or "#" in afile.lower():
newfilename = afile.replace('+', '_').replace('#', '_')
os.rename(os.path.join(dirpath, afile), os.path.join(dirpath, newfilename))
afile = newfilename
if filename is not None and not "_kcchq" in filename[0]:
filelist.append(buildHTML(dirpath, afile))
if not chapter:
chapterlist.append((dirpath.replace('Images', 'Text'), filelist[-1][1]))
chapter = True
if cover is None:
cover = os.path.join(filelist[-1][0], 'cover' + getImageFileName(filelist[-1][1])[1])
cover = os.path.join(os.path.join(path, 'OEBPS', 'Images'),
'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
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(options.profile, path, options.title, filelist, cover, options.righttoleft)
if (options.profile == 'K4' or options.profile == 'KHD') and splittedSomething:
buildOPF(path, options.title, filelist, cover)
if options.landscapemode and splitCount > 0:
filelist.append(buildBlankHTML(os.path.join(path, 'OEBPS', 'Text')))
def getWorkFolder(afile):
workdir = tempfile.mkdtemp()
fname = os.path.splitext(afile)
if os.path.isdir(afile):
try:
import shutil
os.rmdir(workdir) # needed for copytree() fails if dst already exists
copytree(afile, workdir)
path = workdir
fullPath = os.path.join(workdir, 'OEBPS', 'Images')
copytree(afile, fullPath)
sanitizeTreeBeforeConversion(fullPath)
return workdir
except OSError:
rmtree(workdir)
raise
elif fname[1].lower() == '.pdf':
elif afile.lower().endswith('.pdf'):
pdf = pdfjpgextract.PdfJpgExtract(afile)
path = pdf.extract()
else:
cbx = cbxarchive.CBxArchive(afile)
if cbx.isCbxFile():
path = cbx.extract(workdir)
try:
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.'
sys.exit(21)
else:
rmtree(workdir)
raise TypeError
move(path, path + "_temp")
move(path + "_temp", os.path.join(path, 'OEBPS', 'Images'))
return path
def slugify(value):
# Normalizes string, converts to lowercase, removes non-alpha characters,
# and converts spaces to hyphens.
import unicodedata
value = unicodedata.normalize('NFKD', unicode(value, 'latin1')).encode('ascii', 'ignore')
value = re.sub('[^\w\s\.-]', '', value).strip().lower()
value = re.sub('[-\.\s]+', '-', value)
value = re.sub(r'([0-9]+)', r'00000\1', value)
value = re.sub(r'0*([0-9]{6,})', r'\1', value)
return value
def sanitizeTree(filetree):
for root, dirs, files in os.walk(filetree, False):
for name in files:
if name.startswith('.') or name.lower() == 'thumbs.db':
os.remove(os.path.join(root, name))
else:
splitname = os.path.splitext(name)
slugified = slugify(splitname[0])
while os.path.exists(os.path.join(root, slugified + splitname[1])):
slugified += "1"
os.rename(os.path.join(root, name), os.path.join(root, slugified + splitname[1]))
for name in dirs:
if name.startswith('.'):
os.remove(os.path.join(root, name))
else:
os.rename(os.path.join(root, name), os.path.join(root, slugify(name)))
def sanitizeTreeBeforeConversion(filetree):
for root, dirs, files in os.walk(filetree, False):
for name in files:
os.chmod(os.path.join(root, name), stat.S_IWRITE | stat.S_IREAD)
# Detect corrupted files - Phase 1
if os.path.getsize(os.path.join(root, name)) == 0:
os.remove(os.path.join(root, name))
for name in dirs:
os.chmod(os.path.join(root, name), stat.S_IWRITE | stat.S_IREAD | stat.S_IEXEC)
def Copyright():
print ('comic2ebook v%(__version__)s. '
'Written 2012 by Ciro Mattia Gonano.' % globals())
'Written 2013 by Ciro Mattia Gonano and Pawel Jastrzebski.' % globals())
def Usage():
print "Generates HTML, NCX and OPF for a Comic ebook from a bunch of images"
print "Optimized for creating Mobipockets to be read into Kindle Paperwhite"
print "Generates EPUB/CBZ comic ebook from a bunch of images."
parser.print_help()
def main(argv=None):
global parser, options, epub_path
def main(argv=None, qtGUI=None):
global parser, options, epub_path, splitCount, GUI
usage = "Usage: %prog [options] comic_file|comic_folder"
parser = OptionParser(usage=usage, version=__version__)
parser.add_option("-p", "--profile", action="store", dest="profile", default="KHD",
help="Device profile (choose one among K1, K2, K3, K4, KDX, KDXG or KHD) [default=KHD]")
help="Device profile (Choose one among K1, K2, K3, K4NT, K4T, KDX, KDXG, KHD, KF, KFHD, KFHD8,"
" KFA) [Default=KHD]")
parser.add_option("-t", "--title", action="store", dest="title", default="defaulttitle",
help="Comic title [default=filename]")
help="Comic title [Default=filename]")
parser.add_option("-m", "--manga-style", action="store_true", dest="righttoleft", default=False,
help="'Manga style' (right-to-left reading and splitting) [default=False]")
parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False,
help="Verbose output [default=False]")
parser.add_option("--no-image-processing", action="store_false", dest="imgproc", default=True,
help="Do not apply image preprocessing (page splitting and optimizations) [default=True]")
parser.add_option("--upscale-images", action="store_true", dest="upscale", default=False,
help="Resize images smaller than device's resolution [default=False]")
parser.add_option("--stretch-images", action="store_true", dest="stretch", default=False,
help="Stretch images to device's resolution [default=False]")
parser.add_option("--black-borders", action="store_true", dest="black_borders", default=False,
help="Use black borders (instead of white ones) when not stretching and ratio "
+ "is not like the device's one [default=False]")
parser.add_option("--no-cut-page-numbers", action="store_false", dest="cutpagenumbers", default=True,
help="Do not try to cut page numbering on images [default=True]")
help="Manga style (Right-to-left reading and splitting) [Default=False]")
parser.add_option("--quality", type="int", dest="quality", default="0",
help="Output quality. 0 - Normal 1 - High 2 - Ultra [Default=0]")
parser.add_option("-c", "--cbz-output", action="store_true", dest="cbzoutput", default=False,
help="Outputs a CBZ archive and does not generate EPUB")
parser.add_option("--noprocessing", action="store_false", dest="imgproc", default=True,
help="Do not apply image preprocessing (Page splitting and optimizations) [Default=True]")
parser.add_option("--forcepng", action="store_true", dest="forcepng", default=False,
help="Create PNG files instead JPEG (For non-Kindle devices) [Default=False]")
parser.add_option("--gamma", type="float", dest="gamma", default="0.0",
help="Apply gamma correction to linearize the image [Default=Auto]")
parser.add_option("--upscale", action="store_true", dest="upscale", default=False,
help="Resize images smaller than device's resolution [Default=False]")
parser.add_option("--stretch", action="store_true", dest="stretch", default=False,
help="Stretch images to device's resolution [Default=False]")
parser.add_option("--blackborders", action="store_true", dest="black_borders", default=False,
help="Use black borders instead of white ones when not stretching and ratio "
+ "is not like the device's one [Default=False]")
parser.add_option("--rotate", action="store_true", dest="rotate", default=False,
help="Rotate landscape pages instead of splitting them [default=False]")
help="Rotate landscape pages instead of splitting them [Default=False]")
parser.add_option("--nosplitrotate", action="store_true", dest="nosplitrotate", default=False,
help="Disable splitting and rotation [Default=False]")
parser.add_option("--nocutpagenumbers", action="store_false", dest="cutpagenumbers", default=True,
help="Do not try to cut page numbering on images [Default=True]")
parser.add_option("-o", "--output", action="store", dest="output", default=None,
help="Output directory or file for generated ePub")
help="Output generated file (EPUB or CBZ) to specified directory or file")
parser.add_option("--forcecolor", action="store_true", dest="forcecolor", default=False,
help="Do not convert images to grayscale [Default=False]")
parser.add_option("--customwidth", type="int", dest="customwidth", default=0,
help="Replace screen width provided by device profile [Default=0]")
parser.add_option("--customheight", type="int", dest="customheight", default=0,
help="Replace screen height provided by device profile [Default=0]")
parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False,
help="Verbose output [Default=False]")
options, args = parser.parse_args(argv)
checkOptions()
if qtGUI:
GUI = qtGUI
GUI.emit(QtCore.SIGNAL("progressBarTick"), 1)
else:
GUI = None
if len(args) != 1:
parser.print_help()
return
path = getWorkFolder(args[0])
if options.title == 'defaulttitle':
options.title = os.path.splitext(os.path.basename(args[0]))[0]
splitCount = 0
if options.imgproc:
print "Processing images..."
if GUI:
GUI.emit(QtCore.SIGNAL("progressBarTick"), 'status', 'Processing images')
dirImgProcess(path + "/OEBPS/Images/")
print "Creating ePub structure..."
genEpubStruct(path)
# actually zip the ePub
if options.output is not None:
if options.output.endswith('.epub'):
epubpath = os.path.abspath(options.output)
elif os.path.isdir(args[0]):
epubpath = os.path.abspath(options.output) + "/" + os.path.basename(args[0]) + '.epub'
else:
epubpath = os.path.abspath(options.output) + "/" \
+ os.path.basename(os.path.splitext(args[0])[0]) + '.epub'
elif os.path.isdir(args[0]):
epubpath = args[0] + '.epub'
if GUI:
GUI.emit(QtCore.SIGNAL("progressBarTick"), 1)
if options.cbzoutput:
# if CBZ output wanted, compress all images and return filepath
print "\nCreating CBZ file..."
filepath = getOutputFilename(args[0], options.output, '.cbz')
make_archive(path + '_comic', 'zip', path + '/OEBPS/Images')
else:
epubpath = os.path.splitext(args[0])[0] + '.epub'
make_archive(path + '_comic', 'zip', path)
move(path + '_comic.zip', epubpath)
print "\nCreating EPUB structure..."
genEpubStruct(path)
# actually zip the ePub
filepath = getOutputFilename(args[0], options.output, '.epub')
make_archive(path + '_comic', 'zip', path)
move(path + '_comic.zip', filepath)
rmtree(path)
return epubpath
return filepath
def getOutputFilename(srcpath, wantedname, ext):
if not ext.startswith('.'):
ext = '.' + ext
if wantedname is not None:
if wantedname.endswith(ext):
filename = os.path.abspath(wantedname)
elif os.path.isdir(srcpath):
filename = os.path.abspath(options.output) + "/" + os.path.basename(srcpath) + ext
else:
filename = os.path.abspath(options.output) + "/" \
+ os.path.basename(os.path.splitext(srcpath)[0]) + ext
elif os.path.isdir(srcpath):
filename = srcpath + ext
else:
filename = os.path.splitext(srcpath)[0] + ext
if os.path.isfile(filename):
filename = os.path.splitext(filename)[0] + '_kcc' + ext
return filename
def checkOptions():
global options
# 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
# Disabling grayscale conversion for Kindle Fire family.
if options.profile == 'KF' or options.profile == 'KFHD' or options.profile == 'KFHD8' 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':
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
if options.profile == 'OTHER':
options.landscapemode = False
options.panelview = False
options.quality = 0
# Kindle for Android profile require target resolution.
if options.profile == 'KFA' and (options.customwidth == 0 or options.customheight == 0):
print "ERROR: Kindle for Android profile require --customwidth and --customheight options!"
sys.exit(1)
# Override profile data
if options.customwidth != 0 or options.customheight != 0:
X = image.ProfileData.Profiles[options.profile][1][0]
Y = image.ProfileData.Profiles[options.profile][1][1]
if options.customwidth != 0:
X = options.customwidth
if options.customheight != 0:
Y = options.customheight
newProfile = ("Custom", (X, Y), image.ProfileData.Palette16, image.ProfileData.Profiles[options.profile][3],
(int(X*1.5), int(Y*1.5)))
image.ProfileData.Profiles["Custom"] = newProfile
options.profile = "Custom"
options.profileData = image.ProfileData.Profiles[options.profile]
def getEpubPath():
global epub_path
return epub_path
if __name__ == "__main__":
freeze_support()
Copyright()
main(sys.argv[1:])
sys.exit(0)

View File

@@ -1,202 +0,0 @@
#!/usr/bin/env python
#
# Copyright (c) 2013 Ciro Mattia Gonano <ciromattia@gmail.com>
#
# Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the
# above copyright notice and this permission notice appear in all
# copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
# OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
__license__ = 'ISC'
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>'
__docformat__ = 'restructuredtext en'
from Tkinter import *
import tkFileDialog
import tkMessageBox
import ttk
import comic2ebook
import kindlestrip
from image import ProfileData
from subprocess import call
import os
import shutil
import stat
class MainWindow:
def clear_files(self):
self.filelist = []
self.refresh_list()
def open_files(self):
filetypes = [('all files', '.*'), ('Comic files', ('*.cbr', '*.cbz', '*.zip', '*.rar', '*.pdf'))]
f = tkFileDialog.askopenfilenames(title="Choose a file...", filetypes=filetypes)
if not isinstance(f, tuple):
try:
import re
f = re.findall('\{(.*?)\}', f)
except:
import tkMessageBox
tkMessageBox.showerror(
"Open file",
"askopenfilename() returned other than a tuple and no regex module could be found"
)
sys.exit(1)
self.filelist.extend(f)
self.refresh_list()
def open_folder(self):
f = tkFileDialog.askdirectory(title="Choose a folder...")
self.filelist.extend([f])
self.refresh_list()
def refresh_list(self):
self.filelocation.config(state=NORMAL)
self.filelocation.delete(0, END)
for afile in self.filelist:
self.filelocation.insert(END, afile)
self.filelocation.config(state=DISABLED)
def initialize(self):
self.filelocation = Listbox(self.master)
self.filelocation.grid(row=0, columnspan=4, sticky=W + E + N + S)
self.refresh_list()
self.clear_file = Button(self.master, text="Clear files", command=self.clear_files)
self.clear_file.grid(row=4, column=0, rowspan=3)
self.open_file = Button(self.master, text="Add files...", command=self.open_files)
self.open_file.grid(row=4, column=1, rowspan=3)
self.open_folder = Button(self.master, text="Add folder...", command=self.open_folder)
self.open_folder.grid(row=4, column=2, rowspan=3)
self.profile = StringVar()
profiles = sorted(ProfileData.ProfileLabels.iterkeys())
self.profile.set(profiles[-1])
w = apply(OptionMenu, (self.master, self.profile) + tuple(profiles))
w.grid(row=1, column=3)
self.options = {
'epub_only': IntVar(None, 0),
'image_preprocess': IntVar(None, 1),
'rotate': IntVar(None, 0),
'cut_page_numbers': IntVar(None, 1),
'mangastyle': IntVar(None, 0),
'image_upscale': IntVar(None, 0),
'image_stretch': IntVar(None, 0),
'black_borders': IntVar(None, 0)
}
self.optionlabels = {
'epub_only': "Generate ePub only (does not call 'kindlegen')",
'image_preprocess': "Apply image optimizations",
'rotate': "Rotate landscape images instead of splitting them",
'cut_page_numbers': "Cut page numbers",
'mangastyle': "Manga-style (right-to-left reading, applies to reading and splitting)",
'image_upscale': "Allow image upscaling",
'image_stretch': "Stretch images",
'black_borders': "Use black borders"
}
for key in self.options:
aCheckButton = Checkbutton(self.master, text=self.optionlabels[key], variable=self.options[key])
aCheckButton.grid(column=3, sticky='w')
self.progressbar = ttk.Progressbar(orient=HORIZONTAL, length=200, mode='determinate')
self.submit = Button(self.master, text="Execute!", command=self.start_conversion, fg="red")
self.submit.grid(column=3)
self.progressbar.grid(column=0, columnspan=4, sticky=W + E + N + S)
self.notelabel = Label(self.master,
text="GUI can seem frozen while converting, kindly wait until some message appears!")
self.notelabel.grid(column=0, columnspan=4, sticky=W + E + N + S)
def start_conversion(self):
self.progressbar.start()
self.convert()
self.progressbar.stop()
def convert(self):
if len(self.filelist) < 1:
tkMessageBox.showwarning('No file selected', "You should really select some files to convert...")
return
profilekey = ProfileData.ProfileLabels[self.profile.get()]
argv = ["-p", profilekey]
if self.options['image_preprocess'].get() == 0:
argv.append("--no-image-processing")
if self.options['rotate'].get() == 1:
argv.append("--rotate")
if self.options['cut_page_numbers'].get() == 0:
argv.append("--no-cut-page-numbers")
if self.options['mangastyle'].get() == 1:
argv.append("-m")
if self.options['image_upscale'].get() == 1:
argv.append("--upscale-images")
if self.options['image_stretch'].get() == 1:
argv.append("--stretch-images")
if self.options['black_borders'].get() == 1:
argv.append("--black-borders")
errors = False
for entry in self.filelist:
self.master.update()
subargv = list(argv)
try:
subargv.append(entry)
epub_path = comic2ebook.main(subargv)
except Exception, err:
tkMessageBox.showerror('Error comic2ebook', "Error on file %s:\n%s" % (subargv[-1], str(err)))
errors = True
continue
if self.options['epub_only'] == 1:
continue
try:
retcode = call("kindlegen \"" + epub_path + "\"", shell=True)
if retcode < 0:
print >>sys.stderr, "Child was terminated by signal", -retcode
else:
print >>sys.stderr, "Child returned", retcode
except OSError as e:
tkMessageBox.showerror('Error kindlegen', "Error on file %s:\n%s" % (epub_path, e))
errors = True
continue
mobifile = epub_path.replace('.epub', '.mobi')
try:
shutil.move(mobifile, mobifile + '_tostrip')
kindlestrip.main((mobifile + '_tostrip', mobifile))
os.remove(mobifile + '_tostrip')
except Exception, err:
tkMessageBox.showerror('Error', "Error on file %s:\n%s" % (mobifile, str(err)))
errors = True
continue
if errors:
tkMessageBox.showinfo(
"Done",
"Conversion finished (some errors have been reported)"
)
else:
tkMessageBox.showinfo(
"Done",
"Conversion successfully done!"
)
def remove_readonly(self, fn, path):
if fn is os.rmdir:
os.chmod(path, stat.S_IWRITE)
os.rmdir(path)
elif fn is os.remove:
os.chmod(path, stat.S_IWRITE)
os.remove(path)
def __init__(self, master, title):
self.filelist = []
self.master = master
self.master.title(title)
self.initialize()

View File

@@ -16,19 +16,16 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
__license__ = 'ISC'
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>'
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
__docformat__ = 'restructuredtext en'
import os
from PIL import Image, ImageOps, ImageDraw, ImageStat
class ImageFlags:
Orient = 1 << 0
Resize = 1 << 1
Frame = 1 << 2
Quantize = 1 << 3
Stretch = 1 << 4
try:
# noinspection PyUnresolvedReferences
from PIL import Image, ImageOps, ImageStat, ImageChops
except ImportError:
print "ERROR: Pillow is not installed!"
exit(1)
class ProfileData:
@@ -77,52 +74,89 @@ class ProfileData:
]
Profiles = {
'K1': ("Kindle", (600, 800), Palette4),
'K2': ("Kindle 2", (600, 800), Palette15),
'K3': ("Kindle 3/Keyboard", (600, 800), Palette16),
'K4': ("Kindle 4/NT/Touch", (600, 800), Palette16),
'KHD': ("Kindle Paperwhite", (758, 1024), Palette16),
'KDX': ("Kindle DX", (824, 1200), Palette15),
'KDXG': ("Kindle DXG", (824, 1200), Palette16)
'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)),
'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)),
'OTHER': ("Other", (0, 0), Palette16, 1.8, (0, 0)),
}
ProfileLabels = {
"Kindle": 'K1',
"Kindle 1": 'K1',
"Kindle 2": 'K2',
"Kindle 3/Keyboard": 'K3',
"Kindle 4/NT/Touch": 'K4',
"Kindle 4/Non-Touch": 'K4NT',
"Kindle 4/Touch": 'K4T',
"Kindle Paperwhite": 'KHD',
"Kindle DX": 'KDX',
"Kindle DXG": 'KDXG'
"Kindle DXG": 'KDXG',
"Kindle Fire": 'KF',
"Kindle Fire HD 7\"": 'KFHD',
"Kindle Fire HD 8.9\"": 'KFHD8',
"Kindle for Android": 'KFA',
"Other": 'OTHER'
}
class ComicPage:
def __init__(self, source, device):
try:
self.profile = device
self.profile_label, self.size, self.palette = ProfileData.Profiles[device]
self.profile_label, self.size, self.palette, self.gamma, self.panelviewsize = device
except KeyError:
raise RuntimeError('Unexpected output device %s' % device)
# Detect corrupted files - Phase 2
try:
self.origFileName = source
self.image = Image.open(source)
except IOError:
raise RuntimeError('Cannot read image file %s' % source)
# Detect corrupted files - Phase 3
try:
self.image.verify()
except:
raise RuntimeError('Image file %s is corrupted' % source)
self.image = Image.open(source)
self.image = self.image.convert('RGB')
def saveToDir(self, targetdir):
def saveToDir(self, targetdir, forcepng, color, wipe, suffix=None):
filename = os.path.basename(self.origFileName)
try:
self.image = self.image.convert('L') # convert to grayscale
self.image.save(os.path.join(targetdir, filename), "JPEG")
if not color:
self.image = self.image.convert('L') # convert to grayscale
if suffix == "R":
suffix = "_kccrotated"
else:
suffix = ""
if wipe:
os.remove(os.path.join(targetdir, filename))
else:
suffix += "_kcchq"
if forcepng:
self.image.save(os.path.join(targetdir, os.path.splitext(filename)[0] + suffix + ".png"), "PNG")
else:
self.image.save(os.path.join(targetdir, os.path.splitext(filename)[0] + suffix + ".jpg"), "JPEG")
except IOError as e:
raise RuntimeError('Cannot write image in directory %s: %s' % (targetdir, e))
def optimizeImage(self):
self.image = ImageOps.autocontrast(self.image)
def optimizeImage(self, gamma):
if gamma < 0.1:
gamma = self.gamma
if gamma == 1.0:
self.image = ImageOps.autocontrast(self.image)
else:
self.image = ImageOps.autocontrast(Image.eval(self.image, lambda a: 255 * (a / 255.) ** gamma))
def quantizeImage(self):
self.image = self.image.convert('L') # convert to grayscale
self.image = self.image.convert("RGB") # convert back to RGB
colors = len(self.palette) / 3
if colors < 256:
self.palette += self.palette[:3] * (256 - colors)
@@ -130,46 +164,49 @@ 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):
def resizeImage(self, upscale=False, stretch=False, black_borders=False, isSplit=False, toRight=False,
landscapeMode=False, qualityMode=0):
method = Image.ANTIALIAS
if black_borders:
fill = 'black'
else:
fill = 'white'
if qualityMode == 0:
size = (self.size[0], self.size[1])
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
if self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1]:
if not upscale:
if isSplit and (self.profile == 'K4' or self.profile == 'KHD'):
borderw = (self.size[0] - self.image.size[0])
borderh = (self.size[1] - self.image.size[1]) / 2
self.image = ImageOps.expand(self.image, border=(0, borderh), fill=fill)
tempImg = Image.new(self.image.mode, (self.image.size[0] + borderw, self.image.size[1]), fill)
if toRight:
tempImg.paste(self.image, (borderw, 0))
else:
tempImg.paste(self.image, (0, 0))
self.image = tempImg
else:
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)
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)
return self.image
else:
method = Image.NEAREST
method = Image.BILINEAR
if stretch: # if stretching call directly resize() without other considerations.
self.image = self.image.resize(self.size, method)
self.image = self.image.resize(size, method)
return self.image
ratioDev = float(self.size[0]) / float(self.size[1])
if (float(self.image.size[0]) / float(self.image.size[1])) < ratioDev:
diff = int(self.image.size[1] * ratioDev) - self.image.size[0]
if isSplit and (self.profile == 'K4' or self.profile == 'KHD'):
diff = 2
self.image = ImageOps.expand(self.image, border=(diff / 2, 0), fill=fill)
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)
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, self.size, method=method, centering=(0.5, 0.5))
self.image = ImageOps.fit(self.image, size, method=method, centering=(0.5, 0.5))
return self.image
def splitPage(self, targetdir, righttoleft=False, rotate=False):
@@ -180,7 +217,7 @@ class ComicPage:
if (width > height) != (dstwidth > dstheight):
if rotate:
self.image = self.image.rotate(90)
return None
return "R"
else:
if width > height:
# source is landscape, so split by the width
@@ -191,8 +228,8 @@ class ComicPage:
leftbox = (0, 0, width, height / 2)
rightbox = (0, height / 2, width, height)
filename = os.path.splitext(os.path.basename(self.origFileName))
fileone = targetdir + '/' + filename[0] + '-1' + filename[1]
filetwo = targetdir + '/' + filename[0] + '-2' + filename[1]
fileone = targetdir + '/' + filename[0] + '_kcca' + filename[1]
filetwo = targetdir + '/' + filename[0] + '_kccb' + filename[1]
try:
if righttoleft:
pageone = self.image.crop(rightbox)
@@ -209,152 +246,97 @@ class ComicPage:
else:
return None
def frameImage(self):
foreground = tuple(self.palette[:3])
background = tuple(self.palette[-3:])
widthDev, heightDev = self.size
widthImg, heightImg = self.image.size
pastePt = (
max(0, (widthDev - widthImg) / 2),
max(0, (heightDev - heightImg) / 2)
)
corner1 = (
pastePt[0] - 1,
pastePt[1] - 1
)
corner2 = (
pastePt[0] + widthImg + 1,
pastePt[1] + heightImg + 1
)
imageBg = Image.new(self.image.mode, self.size, background)
imageBg.paste(self.image, pastePt)
draw = ImageDraw.Draw(imageBg)
draw.rectangle([corner1, corner2], outline=foreground)
self.image = imageBg
def cutPageNumber(self):
widthImg, heightImg = self.image.size
delta = 2
diff = delta
fixedThreshold = 5
if ImageStat.Stat(self.image).var[0] < 2 * fixedThreshold:
return self.image
while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] < fixedThreshold\
and diff < heightImg:
diff += delta
diff -= delta
pageNumberCut1 = diff
if diff < delta:
if ImageChops.invert(self.image).getbbox() is not None:
widthImg, heightImg = self.image.size
delta = 2
diff = delta
oldStat = ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0]
diff += delta
while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] - oldStat > 0\
and diff < heightImg / 4:
fixedThreshold = 5
if ImageStat.Stat(self.image).var[0] < 2 * fixedThreshold:
return self.image
while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] < fixedThreshold\
and diff < heightImg:
diff += delta
diff -= delta
pageNumberCut1 = diff
if diff < delta:
diff = delta
oldStat = ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0]
diff += delta
diff -= delta
pageNumberCut2 = diff
diff += delta
oldStat = ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg - pageNumberCut2))).var[0]
while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg - pageNumberCut2))).var[0]\
< fixedThreshold + oldStat and diff < heightImg / 4:
while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] - oldStat > 0\
and diff < heightImg / 4:
oldStat = ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0]
diff += delta
diff -= delta
pageNumberCut2 = diff
diff += delta
diff -= delta
pageNumberCut3 = diff
delta = 5
diff = delta
while ImageStat.Stat(self.image.crop((0, heightImg - pageNumberCut2, diff, heightImg))).var[0] < fixedThreshold\
and diff < widthImg:
diff += delta
diff -= delta
pageNumberX1 = diff
diff = delta
while ImageStat.Stat(self.image.crop((widthImg - diff, heightImg - pageNumberCut2,
widthImg, heightImg))).var[0] < fixedThreshold and diff < widthImg:
diff += delta
diff -= delta
pageNumberX2 = widthImg - diff
if pageNumberCut3 - pageNumberCut1 > 2 * delta\
and float(pageNumberX2 - pageNumberX1) / float(pageNumberCut2 - pageNumberCut1) <= 9.0\
and ImageStat.Stat(self.image.crop((0, heightImg - pageNumberCut3, widthImg, heightImg))).var[0]\
/ ImageStat.Stat(self.image).var[0] < 0.1\
and pageNumberCut3 < heightImg / 4 - delta:
diff = pageNumberCut3
else:
diff = pageNumberCut1
self.image = self.image.crop((0, 0, widthImg, heightImg - diff))
oldStat = ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg,
heightImg - pageNumberCut2))).var[0]
while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg - pageNumberCut2))).var[0]\
< fixedThreshold + oldStat and diff < heightImg / 4:
diff += delta
diff -= delta
pageNumberCut3 = diff
delta = 5
diff = delta
while ImageStat.Stat(self.image.crop((0, heightImg - pageNumberCut2, diff, heightImg))).var[0]\
< fixedThreshold and diff < widthImg:
diff += delta
diff -= delta
pageNumberX1 = diff
diff = delta
while ImageStat.Stat(self.image.crop((widthImg - diff, heightImg - pageNumberCut2,
widthImg, heightImg))).var[0] < fixedThreshold and diff < widthImg:
diff += delta
diff -= delta
pageNumberX2 = widthImg - diff
if pageNumberCut3 - pageNumberCut1 > 2 * delta\
and float(pageNumberX2 - pageNumberX1) / float(pageNumberCut2 - pageNumberCut1) <= 9.0\
and ImageStat.Stat(self.image.crop((0, heightImg - pageNumberCut3, widthImg, heightImg))).var[0]\
/ ImageStat.Stat(self.image).var[0] < 0.1\
and pageNumberCut3 < heightImg / 4 - delta:
diff = pageNumberCut3
else:
diff = pageNumberCut1
self.image = self.image.crop((0, 0, widthImg, heightImg - diff))
return self.image
def cropWhiteSpace(self, threshold):
widthImg, heightImg = self.image.size
delta = 10
diff = delta
# top
while ImageStat.Stat(self.image.crop((0, 0, widthImg, diff))).var[0] < threshold and diff < heightImg:
diff += delta
diff -= delta
# print "Top crop: %s"%diff
self.image = self.image.crop((0, diff, widthImg, heightImg))
widthImg, heightImg = self.image.size
diff = delta
# left
while ImageStat.Stat(self.image.crop((0, 0, diff, heightImg))).var[0] < threshold and diff < widthImg:
diff += delta
diff -= delta
# print "Left crop: %s"%diff
self.image = self.image.crop((diff, 0, widthImg, heightImg))
widthImg, heightImg = self.image.size
diff = delta
# down
while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] < threshold\
and diff < heightImg:
diff += delta
diff -= delta
# print "Down crop: %s"%diff
self.image = self.image.crop((0, 0, widthImg, heightImg - diff))
widthImg, heightImg = self.image.size
diff = delta
# right
while ImageStat.Stat(self.image.crop((widthImg - diff, 0, widthImg, heightImg))).var[0] < threshold\
and diff < widthImg:
diff += delta
diff -= delta
# 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
def addProgressbar(self, file_number, files_totalnumber, size, howoften):
if file_number // howoften != float(file_number) / howoften:
return self.image
white = (255, 255, 255)
black = (0, 0, 0)
widthDev, heightDev = size
widthImg, heightImg = self.image.size
pastePt = (
max(0, (widthDev - widthImg) / 2),
max(0, (heightDev - heightImg) / 2)
)
imageBg = Image.new('RGB', size, white)
imageBg.paste(self.image, pastePt)
self.image = imageBg
widthImg, heightImg = self.image.size
draw = ImageDraw.Draw(self.image)
#Black rectangle
draw.rectangle([(0, heightImg - 3), (widthImg, heightImg)], outline=black, fill=black)
#White rectangle
draw.rectangle([(widthImg * file_number / files_totalnumber, heightImg - 3), (widthImg - 1, heightImg)],
outline=black, fill=white)
#Making notches
for i in range(1, 10):
if i <= (10 * file_number / files_totalnumber):
notch_colour = white # White
else:
notch_colour = black # Black
draw.line([(widthImg * float(i) / 10, heightImg - 3), (widthImg * float(i) / 10, heightImg)],
fill=notch_colour)
#The 50%
if i == 5:
draw.rectangle([(widthImg / 2 - 1, heightImg - 5), (widthImg / 2 + 1, heightImg)],
outline=black, fill=notch_colour)
return self.image
if ImageChops.invert(self.image).getbbox() is not None:
widthImg, heightImg = self.image.size
delta = 10
diff = delta
# top
while ImageStat.Stat(self.image.crop((0, 0, widthImg, diff))).var[0] < threshold and diff < heightImg:
diff += delta
diff -= delta
# print "Top crop: %s"%diff
self.image = self.image.crop((0, diff, widthImg, heightImg))
widthImg, heightImg = self.image.size
diff = delta
# left
while ImageStat.Stat(self.image.crop((0, 0, diff, heightImg))).var[0] < threshold and diff < widthImg:
diff += delta
diff -= delta
# print "Left crop: %s"%diff
self.image = self.image.crop((diff, 0, widthImg, heightImg))
widthImg, heightImg = self.image.size
diff = delta
# down
while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] < threshold\
and diff < heightImg:
diff += delta
diff -= delta
# print "Down crop: %s"%diff
self.image = self.image.crop((0, 0, widthImg, heightImg - diff))
widthImg, heightImg = self.image.size
diff = delta
# right
while ImageStat.Stat(self.image.crop((widthImg - diff, 0, widthImg, heightImg))).var[0] < threshold\
and diff < widthImg:
diff += delta
diff -= delta
# 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

View File

@@ -19,7 +19,7 @@
#
__license__ = 'ISC'
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>'
__copyright__ = '2012-2013, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@vulturis.eu>'
__docformat__ = 'restructuredtext en'
import os
@@ -70,3 +70,4 @@ class PdfJpgExtract:
njpg += 1
i = iend
return self.path

View File

@@ -1,6 +1,6 @@
# rarfile.py
#
# Copyright (c) 2005-2012 Marko Kreen <markokr@gmail.com>
# Copyright (c) 2005-2013 Marko Kreen <markokr@gmail.com>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
@@ -17,7 +17,7 @@
r"""RAR archive reader.
This is Python module for Rar archive reading. The interface
is made as zipfile like as possible.
is made as :mod:`zipfile`-like as possible.
Basic logic:
- Parse archive structure with Python.
@@ -34,7 +34,17 @@ Example::
for f in rf.infolist():
print f.filename, f.file_size
if f.filename == 'README':
print rf.read(f)
print(rf.read(f))
Archive files can also be accessed via file-like object returned
by :meth:`RarFile.open`::
import rarfile
with rarfile.RarFile('archive.rar') as rf:
with rf.open('README') as f:
for ln in f:
print(ln.strip())
There are few module-level parameters to tune behaviour,
here they are with defaults, and reason to change it::
@@ -64,7 +74,7 @@ For more details, refer to source.
"""
__version__ = '2.5'
__version__ = '2.6'
# export only interesting items
__all__ = ['is_rarfile', 'RarInfo', 'RarFile', 'RarExtFile']
@@ -73,7 +83,7 @@ __all__ = ['is_rarfile', 'RarInfo', 'RarFile', 'RarExtFile']
## Imports and compat - support both Python 2.x and 3.x
##
import sys, os, struct
import sys, os, struct, errno
from struct import pack, unpack
from binascii import crc32
from tempfile import mkstemp
@@ -148,45 +158,45 @@ except ImportError:
## Module configuration. Can be tuned after importing.
##
# default fallback charset
#: default fallback charset
DEFAULT_CHARSET = "windows-1252"
# list of encodings to try, with fallback to DEFAULT_CHARSET if none succeed
#: list of encodings to try, with fallback to DEFAULT_CHARSET if none succeed
TRY_ENCODINGS = ('utf8', 'utf-16le')
# 'unrar', 'rar' or full path to either one
#: 'unrar', 'rar' or full path to either one
UNRAR_TOOL = "unrar"
# Command line args to use for opening file for reading.
#: Command line args to use for opening file for reading.
OPEN_ARGS = ('p', '-inul')
# Command line args to use for extracting file to disk.
#: Command line args to use for extracting file to disk.
EXTRACT_ARGS = ('x', '-y', '-idq')
# args for testrar()
#: args for testrar()
TEST_ARGS = ('t', '-idq')
# whether to speed up decompression by using tmp archive
#: whether to speed up decompression by using tmp archive
USE_EXTRACT_HACK = 1
# limit the filesize for tmp archive usage
#: limit the filesize for tmp archive usage
HACK_SIZE_LIMIT = 20*1024*1024
# whether to parse file/archive comments.
#: whether to parse file/archive comments.
NEED_COMMENTS = 1
# whether to convert comments to unicode strings
#: whether to convert comments to unicode strings
UNICODE_COMMENTS = 0
# When RAR is corrupt, stopping on bad header is better
# On unknown/misparsed RAR headers reporting is better
#: When RAR is corrupt, stopping on bad header is better
#: On unknown/misparsed RAR headers reporting is better
REPORT_BAD_HEADER = 0
# Convert RAR time tuple into datetime() object
#: Convert RAR time tuple into datetime() object
USE_DATETIME = 0
# Separator for path name components. RAR internally uses '\\'.
# Use '/' to be similar with zipfile.
#: Separator for path name components. RAR internally uses '\\'.
#: Use '/' to be similar with zipfile.
PATH_SEP = '\\'
##
@@ -320,6 +330,8 @@ class RarMemoryError(RarExecError):
"""Memory error"""
class RarCreateError(RarExecError):
"""Create error"""
class RarNoFilesError(RarExecError):
"""No files that match pattern were found"""
class RarUserBreak(RarExecError):
"""User stop"""
class RarUnknownError(RarExecError):
@@ -335,49 +347,57 @@ def is_rarfile(fn):
class RarInfo(object):
'''An entry in rar archive.
@ivar filename:
File name with relative path.
Default path separator is '/', to change set rarfile.PATH_SEP.
Always unicode string.
@ivar date_time:
Modification time, tuple of (year, month, day, hour, minute, second).
Or datetime() object if USE_DATETIME is set.
@ivar file_size:
Uncompressed size.
@ivar compress_size:
Compressed size.
@ivar compress_type:
Compression method: 0x30 - 0x35.
@ivar extract_version:
Minimal Rar version needed for decompressing.
@ivar host_os:
Host OS type, one of RAR_OS_* constants.
@ivar mode:
File attributes. May be either dos-style or unix-style, depending on host_os.
@ivar CRC:
CRC-32 of uncompressed file, unsigned int.
@ivar volume:
Volume nr, starting from 0.
@ivar volume_file:
Volume file name, where file starts.
@ivar type:
One of RAR_BLOCK_* types. Only entries with type==RAR_BLOCK_FILE are shown in .infolist().
@ivar flags:
For files, RAR_FILE_* bits.
@ivar comment:
File comment (unicode string or None).
r'''An entry in rar archive.
@ivar mtime:
Optional time field: Modification time, with float seconds.
Same as .date_time but with more precision.
@ivar ctime:
Optional time field: creation time, with float seconds.
@ivar atime:
Optional time field: last access time, with float seconds.
@ivar arctime:
Optional time field: archival time, with float seconds.
:mod:`zipfile`-compatible fields:
filename
File name with relative path.
Default path separator is '\\', to change set rarfile.PATH_SEP.
Always unicode string.
date_time
Modification time, tuple of (year, month, day, hour, minute, second).
Or datetime() object if USE_DATETIME is set.
file_size
Uncompressed size.
compress_size
Compressed size.
CRC
CRC-32 of uncompressed file, unsigned int.
comment
File comment. Byte string or None. Use UNICODE_COMMENTS
to get automatic decoding to unicode.
volume
Volume nr, starting from 0.
RAR-specific fields:
compress_type
Compression method: 0x30 - 0x35.
extract_version
Minimal Rar version needed for decompressing.
host_os
Host OS type, one of RAR_OS_* constants.
mode
File attributes. May be either dos-style or unix-style, depending on host_os.
volume_file
Volume file name, where file starts.
mtime
Optional time field: Modification time, with float seconds.
Same as .date_time but with more precision.
ctime
Optional time field: creation time, with float seconds.
atime
Optional time field: last access time, with float seconds.
arctime
Optional time field: archival time, with float seconds.
Internal fields:
type
One of RAR_BLOCK_* types. Only entries with type==RAR_BLOCK_FILE are shown in .infolist().
flags
For files, RAR_FILE_* bits.
'''
__slots__ = (
@@ -417,7 +437,7 @@ class RarInfo(object):
'header_offset',
'salt',
'volume_file',
)
)
def isdir(self):
'''Returns True if the entry is a directory.'''
@@ -431,19 +451,27 @@ class RarInfo(object):
class RarFile(object):
'''Parse RAR structure, provide access to files in archive.
@ivar comment:
Archive comment (unicode string or None).
'''
#: Archive comment. Byte string or None. Use UNICODE_COMMENTS
#: to get automatic decoding to unicode.
comment = None
def __init__(self, rarfile, mode="r", charset=None, info_callback=None, crc_check = True):
"""Open and parse a RAR archive.
@param rarfile: archive file name
@param mode: only 'r' is supported.
@param charset: fallback charset to use, if filenames are not already Unicode-enabled.
@param info_callback: debug callback, gets to see all archive entries.
@param crc_check: set to False to disable CRC checks
Parameters:
rarfile
archive file name
mode
only 'r' is supported.
charset
fallback charset to use, if filenames are not already Unicode-enabled.
info_callback
debug callback, gets to see all archive entries.
crc_check
set to False to disable CRC checks
"""
self.rarfile = rarfile
self.comment = None
@@ -455,6 +483,7 @@ class RarFile(object):
self._needs_password = False
self._password = None
self._crc_check = crc_check
self._vol_list = []
self._main = None
@@ -487,6 +516,14 @@ class RarFile(object):
'''Return RarInfo objects for all files/directories in archive.'''
return self._info_list
def volumelist(self):
'''Returns filenames of archive volumes.
In case of single-volume archive, the list contains
just the name of main archive file.
'''
return self._vol_list
def getinfo(self, fname):
'''Return RarInfo for file.'''
@@ -508,7 +545,8 @@ class RarFile(object):
raise NoRarEntry("No such file: "+fname)
def open(self, fname, mode = 'r', psw = None):
'''Return open file object, where the data can be read.
'''Returns file-like object (:class:`RarExtFile`),
from where the data can be read.
The object implements io.RawIOBase interface, so it can
be further wrapped with io.BufferedReader and io.TextIOWrapper.
@@ -520,9 +558,14 @@ class RarFile(object):
uncompressed files, on compressed files the seeking is implemented
by reading ahead and/or restarting the decompression.
@param fname: file name or RarInfo instance.
@param mode: must be 'r'
@param psw: password to use for extracting.
Parameters:
fname
file name or RarInfo instance.
mode
must be 'r'
psw
password to use for extracting.
'''
if mode != 'r':
@@ -569,8 +612,12 @@ class RarFile(object):
For longer files using .open() may be better idea.
@param fname: filename or RarInfo instance
@param psw: password to use for extracting.
Parameters:
fname
filename or RarInfo instance
psw
password to use for extracting.
"""
f = self.open(fname, 'r', psw)
@@ -591,9 +638,14 @@ class RarFile(object):
def extract(self, member, path=None, pwd=None):
"""Extract single file into current directory.
@param member: filename or RarInfo instance
@param path: optional destination path
@param pwd: optional password to use
Parameters:
member
filename or RarInfo instance
path
optional destination path
pwd
optional password to use
"""
if isinstance(member, RarInfo):
fname = member.filename
@@ -604,9 +656,14 @@ class RarFile(object):
def extractall(self, path=None, members=None, pwd=None):
"""Extract all files into current directory.
@param path: optional destination path
@param members: optional filename or RarInfo instance list to extract
@param pwd: optional password to use
Parameters:
path
optional destination path
members
optional filename or RarInfo instance list to extract
pwd
optional password to use
"""
fnlist = []
if members is not None:
@@ -691,6 +748,7 @@ class RarFile(object):
more_vols = 0
endarc = 0
volfile = self.rarfile
self._vol_list = [self.rarfile]
while 1:
if endarc:
h = None # don't read past ENDARC
@@ -705,6 +763,7 @@ class RarFile(object):
self._fd = fd
more_vols = 0
endarc = 0
self._vol_list.append(volfile)
continue
break
h.volume = volume
@@ -729,7 +788,7 @@ class RarFile(object):
# RAR 2.x does not write RAR_BLOCK_ENDARC
if h.flags & RAR_FILE_SPLIT_AFTER:
more_vols = 1
# RAR 2.x does not set RAR_MAIN_FIRSTVOLUME
# RAR 2.x does not set RAR_MAIN_FIRSTVOLUME
if volume == 0 and h.flags & RAR_FILE_SPLIT_BEFORE:
raise NeedFirstVolume("Need to start from first volume")
@@ -950,7 +1009,7 @@ class RarFile(object):
pos += S_COMMENT_HDR.size
data = hdata[pos : pos_next]
cmt = rar_decompress(ver, meth, data, declen, sflags,
crc, self._password)
crc, self._password)
if not self._crc_check:
h.comment = self._decode_comment(cmt)
elif crc32(cmt) & 0xFFFF == crc:
@@ -1073,7 +1132,7 @@ class RarFile(object):
# decompress
cmt = rar_decompress(inf.extract_version, inf.compress_type, data,
inf.file_size, inf.flags, inf.CRC, psw, inf.salt)
inf.file_size, inf.flags, inf.CRC, psw, inf.salt)
# check crc
if self._crc_check:
@@ -1208,7 +1267,7 @@ class UnicodeFilename:
class RarExtFile(RawIOBase):
"""Base class for 'file-like' object that RarFile.open() returns.
"""Base class for file-like object that :meth:`RarFile.open` returns.
Provides public methods and common crc checking.
@@ -1216,13 +1275,15 @@ class RarExtFile(RawIOBase):
- no short reads - .read() and .readinfo() read as much as requested.
- no internal buffer, use io.BufferedReader for that.
@ivar name:
filename of the archive entry.
If :mod:`io` module is available (Python 2.6+, 3.x), then this calls
will inherit from :class:`io.RawIOBase` class. This makes line-based
access available: :meth:`RarExtFile.readline` and ``for ln in f``.
"""
def __init__(self, rf, inf):
"""Fill common fields"""
#: Filename of the archive entry
name = None
def __init__(self, rf, inf):
RawIOBase.__init__(self)
# standard io.* properties
@@ -1323,7 +1384,13 @@ class RarExtFile(RawIOBase):
return self.inf.file_size - self.remain
def seek(self, ofs, whence = 0):
"""Seek in data."""
"""Seek in data.
On uncompressed files, the seeking works by actual
seeks so it's fast. On compresses files its slow
- forward seeking happends by reading ahead,
backwards by re-opening and decompressing from the start.
"""
# disable crc check when seeking
self.crc_check = 0
@@ -1372,8 +1439,17 @@ class RarExtFile(RawIOBase):
"""Returns True"""
return True
def writable(self):
"""Returns False.
Writing is not supported."""
return False
def seekable(self):
"""Returns True"""
"""Returns True.
Seeking is supported, although it's slow on compressed files.
"""
return True
def readall(self):
@@ -1668,7 +1744,7 @@ def rar_decompress(vers, meth, data, declen=0, flags=0, crc=0, psw=None, salt=No
date = 0
mode = 0x20
fhdr = S_FILE_HDR.pack(len(data), declen, RAR_OS_MSDOS, crc,
date, vers, meth, len(fname), mode)
date, vers, meth, len(fname), mode)
fhdr += fname
if flags & RAR_FILE_SALT:
if not salt:
@@ -1757,8 +1833,15 @@ def custom_popen(cmd):
creationflags = 0x08000000 # CREATE_NO_WINDOW
# run command
p = Popen(cmd, bufsize = 0, stdout = PIPE, stdin = PIPE, stderr = STDOUT,
creationflags = creationflags)
try:
p = Popen(cmd, bufsize = 0,
stdout = PIPE, stdin = PIPE, stderr = STDOUT,
creationflags = creationflags)
except OSError:
ex = sys.exc_info()[1]
if ex.errno == errno.ENOENT:
raise RarExecError("Unrar not installed? (rarfile.UNRAR_TOOL=%r)" % UNRAR_TOOL)
raise
return p
def check_returncode(p, out):
@@ -1770,9 +1853,9 @@ def check_returncode(p, out):
# map return code to exception class
errmap = [None,
RarWarning, RarFatalError, RarCRCError, RarLockedArchiveError,
RarWriteError, RarOpenError, RarUserError, RarMemoryError,
RarCreateError] # codes from rar.txt
RarWarning, RarFatalError, RarCRCError, RarLockedArchiveError,
RarWriteError, RarOpenError, RarUserError, RarMemoryError,
RarCreateError, RarNoFilesError] # codes from rar.txt
if code > 0 and code < len(errmap):
exc = errmap[code]
elif code == 255:

2
other/qt.conf Normal file
View File

@@ -0,0 +1,2 @@
[Paths]
Plugins = DumbHackThatFixPY2APP

View File

@@ -1,65 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleAllowMixedLocalizations</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>*</string>
</array>
<key>CFBundleTypeOSTypes</key>
<array>
<string>****</string>
</array>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
</array>
<key>CFBundleExecutable</key>
<string>droplet</string>
<key>CFBundleGetInfoString</key>
<string>KindleComicConverter 2.0, Written 2012 by Ciro Mattia Gonano</string>
<key>CFBundleIconFile</key>
<string>droplet</string>
<key>CFBundleIdentifier</key>
<string>com.github.ciromattia.kcc</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>KindleComicConverter 1.20</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>dplt</string>
<key>LSMinimumSystemVersionByArchitecture</key>
<dict>
<key>x86_64</key>
<string>10.6</string>
</dict>
<key>LSRequiresCarbon</key>
<true/>
<key>WindowState</key>
<dict>
<key>dividerCollapsed</key>
<true/>
<key>eventLogLevel</key>
<integer>-1</integer>
<key>name</key>
<string>ScriptWindowState</string>
<key>positionOfDivider</key>
<real>568</real>
<key>savedFrame</key>
<string>144 338 889 690 0 0 1680 1028 </string>
<key>selectedTabView</key>
<string>event log</string>
</dict>
</dict>
</plist>

Binary file not shown.

View File

@@ -1,22 +0,0 @@
{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340
{\fonttbl\f0\fnil\fcharset0 Verdana;}
{\colortbl;\red255\green255\blue255;\red76\green78\blue78;}
\pard\tx576\tx1152\tx1728\tx2304\tx2880\tx3456\tx4032\tx4608\tx5184\tx5760\tx6337\tx6913\tx7489\tx8065\tx8641\tx9217\tx9793\tx10369\tx10945\tx11521\tx12097\tx12674\tx13250\tx13826\tx14402\tx14978\tx15554\tx16130\tx16706\tx17282\tx17858\tx18435\tx19011\tx19587\tx20163\tx20739\tx21315\tx21891\tx22467\tx23043\tx23619\tx24195\tx24772\tx25348\tx25924\tx26500\tx27076\tx27652\tx28228\tx28804\tx29380\tx29956\tx30532\tx31109\tx31685\tx32261\tx32837\tx33413\tx33989\tx34565\tx35141\tx35717\tx36293\tx36870\tx37446\tx38022\tx38598\tx39174\tx39750\tx40326\tx40902\tx41478\tx42054\tx42630\tx43207\tx43783\tx44359\tx44935\tx45511\tx46087\tx46663\tx47239\tx47815\tx48391\tx48967\tx49544\tx50120\tx50696\tx51272\tx51848\tx52424\tx53000\tx53576\tx54152\tx54728\tx55305\tx55881\tx56457\tx57033\tx57609\li785\fi-786\pardirnatural
\f0\fs24 \cf2 \CocoaLigature0 Copyright (c) 2012 Ciro Mattia Gonano <ciromattia@gmail.com>\
\
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.\
\
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\
\
This script heavily relies on KindleStrip (C) by Paul Durrant and released in public domain (http://www.mobileread.com/forums/showthread.php?t=96903)\
Also, you need to have kindlegen v2.7 (with KF8 support) which is downloadable from Amazon website.\
\
Changelog:\
1.0: first release\
1.10: add CBZ/CBR support to comic2ebook.py\
1.11: add CBZ/CBR support to KindleComicConverter\
1.2: added image page splitting and optimizations\
\
Todo:\
- bundle a script to manipulate images (to get rid of Mangle/E-nki/whatsoever)}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 362 B

View File

@@ -1,82 +1,75 @@
"""
py2app/py2exe build script for MyApplication.
Will automatically ensure that all build prerequisites are available
via ez_setup
cx_Freeze build script for KCC.
Usage (Mac OS X):
python setup.py py2app
Usage (Windows):
python setup.py py2exe
python setup.py build
"""
import ez_setup
ez_setup.use_setuptools()
from sys import platform
import sys
from setuptools import setup
NAME = "KindleComicConverter"
VERSION = "3.1"
MAIN = "kcc.py"
NAME = 'KindleComicConverter'
VERSION = "2.6"
mainscript = 'kcc.py'
if sys.platform == 'darwin':
if platform == "darwin":
from setuptools import setup
extra_options = dict(
setup_requires=['py2app'],
app=[mainscript],
app=[MAIN],
options=dict(
py2app=dict(
argv_emulation=True,
iconfile='resources/comic2ebook.icns',
iconfile='icons/comic2ebook.icns',
includes=['PIL', 'sip', 'PyQt4', 'PyQt4.QtCore', 'PyQt4.QtGui'],
resources=['other/qt.conf', 'LICENSE.txt'],
plist=dict(
CFBundleName=NAME,
CFBundleShortVersionString=VERSION,
CFBundleGetInfoString=NAME + " " + VERSION + ", written 2012-2013 by Ciro Mattia Gonano",
CFBundleGetInfoString=NAME + " " + VERSION +
", written 2012-2013 by Ciro Mattia Gonano and Pawel Jastrzebski",
CFBundleExecutable=NAME,
CFBundleIdentifier='com.github.ciromattia.kcc',
CFBundleSignature='dplt'
CFBundleSignature='dplt',
NSHumanReadableCopyright='ISC License (ISCL)'
)
)
)
)
elif sys.platform == 'win32':
elif platform == "win32":
from cx_Freeze import setup, Executable
base = "Win32GUI"
extra_options = dict(
executables=[Executable("kcc.py", base=base)]
)
options={"build_exe": {"include_files": ['LICENSE.txt'], "compressed": True}},
executables=[Executable(MAIN,
base=base,
targetName="KCC.exe",
icon="icons/comic2ebook.ico",
copyDependentFiles=True,
appendScriptToExe=True,
appendScriptToLibrary=False,
compress=True)])
else:
from cx_Freeze import setup, Executable
extra_options = dict(
# Normally unix-like platforms will use "setup.py install"
# and install the main script as such
scripts=[mainscript],
)
options={"build_exe": {"include_files": ['LICENSE.txt'], "compressed": True}},
executables=[Executable(MAIN,
icon="icons/comic2ebook.png",
copyDependentFiles=True,
appendScriptToExe=True,
appendScriptToLibrary=False,
compress=True)])
setup(
name=NAME,
version=VERSION,
author="Ciro Mattia Gonano",
author_email="ciromattia@gmail.com",
description="A tool to convert comics (CBR/CBZ/PDFs/image folders) to Mobipocket.",
author="Ciro Mattia Gonano, Pawel Jastrzebski",
author_email="ciromattia@gmail.com, pawelj@vulturis.eu",
description="A tool to convert comics (CBR/CBZ/PDFs/image folders) to MOBI.",
license="ISC License (ISCL)",
keywords="kindle comic mobipocket mobi cbz cbr manga",
url="http://github.com/ciromattia/kcc",
classifiers=[
'Development Status :: 4 - Beta'
'License :: OSI Approved :: ISC License (ISCL)',
'Environment :: Console',
'Environment :: MacOS X',
'Environment :: Win32 (MS Windows)',
'Environment :: X11 Applications',
'Intended Audience :: End Users/Desktop',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Topic :: Multimedia :: Graphics :: Graphics Conversion',
'Topic :: Utilities'
],
packages=['kcc'],
# make sure to add custom_fixers to the MANIFEST.in
include_package_data=True,
packages=['kcc'], requires=['Pillow'],
**extra_options
)

View File

@@ -1,86 +0,0 @@
"""
py2app/py2exe build script for MyApplication.
Will automatically ensure that all build prerequisites are available
via ez_setup
Usage (Mac OS X):
python setup_cx.py bdist_dmg
Usage (Windows):
python setup_cx.py bdist_msi
"""
import sys
from cx_Freeze import setup, Executable
NAME='KindleComicConverter'
VERSION="2.4"
mainscript = 'kcc.py'
base = None
if sys.platform == 'darwin':
extra_options = dict(
bundle-iconfile='resources/comic2ebook.icns',
volume-label=NAME + " " + VERSION
app=[mainscript],
options=dict(
py2app=dict(
argv_emulation=True,
iconfile='resources/comic2ebook.icns',
plist=dict(
CFBundleName = NAME,
CFBundleShortVersionString = VERSION,
CFBundleGetInfoString = NAME + " " + VERSION + ", written 2012-2013 by Ciro Mattia Gonano",
CFBundleExecutable = NAME,
CFBundleIdentifier = 'com.github.ciromattia.kcc',
CFBundleSignature = 'dplt'
)
)
)
)
elif sys.platform == 'win32':
base = "Win32GUI"
extra_options = dict(
upgrade-code = 'KindleComicConverter',
executables = [Executable("kcc.py", base=base)]
)
else:
extra_options = dict(
# Normally unix-like platforms will use "setup.py install"
# and install the main script as such
scripts=[mainscript],
)
options = dict(
)
setup(
name=NAME,
version=VERSION,
author="Ciro Mattia Gonano",
author_email="ciromattia@gmail.com",
description=("A tool to convert comics (CBR/CBZ/PDFs/image folders) to Mobipocket."),
license = "ISC License (ISCL)",
keywords = "kindle comic mobipocket mobi cbz cbr manga",
url = "http://github.com/ciromattia/kcc",
classifiers=[
'Development Status :: 4 - Beta'
'License :: OSI Approved :: ISC License (ISCL)',
'Environment :: Console',
'Environment :: MacOS X',
'Environment :: Win32 (MS Windows)',
'Environment :: X11 Applications',
'Intended Audience :: End Users/Desktop',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Topic :: Multimedia :: Graphics :: Graphics Conversion',
'Topic :: Utilities'
],
# make sure to add custom_fixers to the MANIFEST.in
include_package_data=True,
executables = [Executable("kcc.py", base=base)]),
**extra_options
)