Compare commits
1046 Commits
Author | SHA1 | Date |
---|---|---|
Tigran Aivazian | a4fa231b00 | 12 years ago |
Tigran Aivazian | 74741a7a8f | 12 years ago |
chrox | 31a61a995b | 12 years ago |
chrox | 10b4804d2e | 12 years ago |
Tigran Aivazian | 328a1a853f | 12 years ago |
{Qingping,Dave} Hou | f41d56d397 | 12 years ago |
chrox | 1b1b0e423f | 12 years ago |
{Qingping,Dave} Hou | f27ee2f5fa | 12 years ago |
{Qingping,Dave} Hou | 1b95b4b874 | 12 years ago |
chrox | f5b45aa253 | 12 years ago |
Tigran Aivazian | f047084320 | 12 years ago |
Tigran Aivazian | fa0c08cb56 | 12 years ago |
chrox | ff267ee735 | 12 years ago |
chrox | 9583a477fe | 12 years ago |
chrox | 0603dc8a38 | 12 years ago |
Tigran Aivazian | 4bc9f37b49 | 12 years ago |
Tigran Aivazian | 722ddc96f9 | 12 years ago |
Tigran Aivazian | 6a73dad4f5 | 12 years ago |
Tigran Aivazian | 7cfc505d21 | 12 years ago |
{Qingping,Dave} Hou | 13f67822a1 | 12 years ago |
{Qingping,Dave} Hou | c2ab146203 | 12 years ago |
{Qingping,Dave} Hou | b74fa0e0e2 | 12 years ago |
{Qingping,Dave} Hou | 7c1cf60438 | 12 years ago |
{Qingping,Dave} Hou | 5a6ffc5582 | 12 years ago |
Tigran Aivazian | 6c036b4933 | 12 years ago |
Tigran Aivazian | 4ae40aa1ed | 12 years ago |
Tigran Aivazian | 7d61ea20fd | 12 years ago |
Tigran Aivazian | 65e9f2fed3 | 12 years ago |
Tigran Aivazian | bb6480e458 | 12 years ago |
Tigran Aivazian | 72e0a314fb | 12 years ago |
Tigran Aivazian | 630d3f1002 | 12 years ago |
Tigran Aivazian | 7e5712946a | 12 years ago |
Tigran Aivazian | f0edb9fc55 | 12 years ago |
Tigran Aivazian | eb45c49fd6 | 12 years ago |
Tigran Aivazian | f2c161d45d | 12 years ago |
Tigran Aivazian | 93a0db59a1 | 12 years ago |
{Qingping,Dave} Hou | d3b0be1bf4 | 12 years ago |
kai771 | 6919e40db2 | 12 years ago |
Tigran Aivazian | c4246b3134 | 12 years ago |
chrox | 0b2a03b8fe | 12 years ago |
{Qingping,Dave} Hou | 7c7e2af4eb | 12 years ago |
{Qingping,Dave} Hou | d631445636 | 12 years ago |
Tigran Aivazian | 8fd76e243f | 12 years ago |
kai771 | c50b53b604 | 12 years ago |
Tigran Aivazian | e7c264c576 | 12 years ago |
Tigran Aivazian | d350c6005a | 12 years ago |
kai771 | f9be17ecad | 12 years ago |
Tigran Aivazian | 4b197ad382 | 12 years ago |
Tigran Aivazian | bf4d548aef | 12 years ago |
kai771 | ba90e9e97c | 12 years ago |
kai771 | c06f01ee21 | 12 years ago |
Tigran Aivazian | 8ae678ae5b | 12 years ago |
Tigran Aivazian | d1b6339f63 | 12 years ago |
kai771 | c049f26b52 | 12 years ago |
kai771 | dc3478f1e8 | 12 years ago |
kai771 | 326b9065de | 12 years ago |
kai771 | 7adb096c4d | 12 years ago |
Tigran Aivazian | 20fe6e9af9 | 12 years ago |
chrox | 38abe95ad3 | 12 years ago |
Tigran Aivazian | 07ff1474a3 | 12 years ago |
{Qingping,Dave} Hou | a100014c47 | 12 years ago |
chrox | e38e889e6d | 12 years ago |
{Qingping,Dave} Hou | bafcea0d33 | 12 years ago |
kai771 | 9f68c07b5a | 12 years ago |
chrox | b60566bfe2 | 12 years ago |
chrox | ffd49835af | 12 years ago |
Tigran Aivazian | f3f193c69b | 12 years ago |
chrox | f792eeb227 | 12 years ago |
chrox | 31e4f6ba89 | 12 years ago |
Tigran Aivazian | 7f3256511d | 12 years ago |
kai771 | fc604057b6 | 12 years ago |
kai771 | eafeb2fce3 | 12 years ago |
kai771 | 3f6bea1220 | 12 years ago |
Tigran Aivazian | b681967243 | 12 years ago |
kai771 | 47fd08a3a3 | 12 years ago |
kai771 | 2382d46c66 | 12 years ago |
kai771 | 2b5e161575 | 12 years ago |
kai771 | 427f1c6a10 | 12 years ago |
Tigran Aivazian | ccea393775 | 12 years ago |
kai771 | 4c3f9fdeb7 | 12 years ago |
Tigran Aivazian | 4dc1d479bc | 12 years ago |
Tigran Aivazian | 609089629c | 12 years ago |
Tigran Aivazian | 81b3da5bd8 | 12 years ago |
Tigran Aivazian | e6559f158e | 12 years ago |
chrox | 2d0659f33a | 12 years ago |
chrox | 974a6dd066 | 12 years ago |
Tigran Aivazian | d7d718fc53 | 12 years ago |
chrox | 3945ab232b | 12 years ago |
Tigran Aivazian | 95be75c87e | 12 years ago |
Tigran Aivazian | fd0120ad69 | 12 years ago |
Tigran Aivazian | 4e74b70b9f | 12 years ago |
Tigran Aivazian | 3268e487f0 | 12 years ago |
Tigran Aivazian | 8aa7530782 | 12 years ago |
Tigran Aivazian | ca12b469dc | 12 years ago |
kai771 | 5a3eca91d2 | 12 years ago |
chrox | 8d0a6f6ee2 | 12 years ago |
kai771 | 01331ed4d1 | 12 years ago |
kai771 | b14d79d225 | 12 years ago |
Tigran Aivazian | af463cd993 | 12 years ago |
Tigran Aivazian | 3fd89106bd | 12 years ago |
Tigran Aivazian | f3c5670016 | 12 years ago |
Tigran Aivazian | 348a496f8c | 12 years ago |
chrox | 5ca6c8728c | 12 years ago |
chrox | 7faeece952 | 12 years ago |
Tigran Aivazian | 96da77ba96 | 12 years ago |
Tigran Aivazian | edb2a1f678 | 12 years ago |
Tigran Aivazian | 12e713b285 | 12 years ago |
Tigran Aivazian | b67dcf8668 | 12 years ago |
Tigran Aivazian | 8f28fe8825 | 12 years ago |
kai771 | 77558cadd7 | 12 years ago |
kai771 | 1b5075ba51 | 12 years ago |
kai771 | 0f945dd9cd | 12 years ago |
Tigran Aivazian | d87c3050fb | 12 years ago |
Tigran Aivazian | b0f4614308 | 12 years ago |
Tigran Aivazian | 56d29d5588 | 12 years ago |
chrox | cd21b9f041 | 12 years ago |
chrox | c95b2a1dc5 | 12 years ago |
chrox | 262acbed09 | 12 years ago |
chrox | 49d4b5ff1e | 12 years ago |
chrox | 0ef75c65f0 | 12 years ago |
chrox | 9594895b26 | 12 years ago |
Tigran Aivazian | f549450898 | 12 years ago |
Tigran Aivazian | 7d52e65828 | 12 years ago |
chrox | 5022609bc2 | 12 years ago |
chrox | f2835d7a86 | 12 years ago |
chrox | 1d018ee5bf | 12 years ago |
Tigran Aivazian | 756bab8a9c | 12 years ago |
kai771 | ea98061c61 | 12 years ago |
{Qingping,Dave} Hou | 38e75c230e | 12 years ago |
{Qingping,Dave} Hou | 82221fa420 | 12 years ago |
Tigran Aivazian | d1a72f6f95 | 12 years ago |
kai771 | c12a02f236 | 12 years ago |
kai771 | 9c6ad7387a | 12 years ago |
Tigran Aivazian | d2cde652b7 | 12 years ago |
Tigran Aivazian | a81fe952fd | 12 years ago |
Tigran Aivazian | 4514e828b5 | 12 years ago |
chrox | 6df1a05264 | 12 years ago |
Tigran Aivazian | a209ed3549 | 12 years ago |
Tigran Aivazian | 20e0d3aef1 | 12 years ago |
kai771 | c13fe3c517 | 12 years ago |
chrox | f4eb9e7982 | 12 years ago |
kai771 | 12974cffbe | 12 years ago |
Tigran Aivazian | b949cec1aa | 12 years ago |
Tigran Aivazian | 8374aff5b7 | 12 years ago |
chrox | fc3ba20ab7 | 12 years ago |
kai771 | 39eb6e5e29 | 12 years ago |
chrox | ad19673bd7 | 12 years ago |
Dobrica Pavlinušić | 2a7912aa05 | 12 years ago |
kai771 | 9e9ffab969 | 12 years ago |
Tigran Aivazian | 6a53c2f864 | 12 years ago |
Tigran Aivazian | e8750d641a | 12 years ago |
chrox | 31f384255c | 12 years ago |
Dobrica Pavlinušić | 22b3e9749c | 12 years ago |
chrox | 0f8b56b795 | 12 years ago |
chrox | 68a0f34a19 | 12 years ago |
chrox | 0a427cc02f | 12 years ago |
chrox | 5c93a3a271 | 12 years ago |
chrox | 7a75742bab | 12 years ago |
chrox | 84aec5067b | 12 years ago |
chrox | 4248194e14 | 12 years ago |
chrox | f157b604c1 | 12 years ago |
chrox | 9cb906f031 | 12 years ago |
chrox | d0878f00a9 | 12 years ago |
chrox | cd9b6cc770 | 12 years ago |
chrox | 505f59fc87 | 12 years ago |
chrox | 73f796a13c | 12 years ago |
kai771 | 3293acd3a3 | 12 years ago |
{Qingping,Dave} Hou | 0b7fb32280 | 12 years ago |
chrox | e9217caa9c | 12 years ago |
chrox | 0d5249e75c | 12 years ago |
Tigran Aivazian | 7ba5d4a2bb | 12 years ago |
chrox | 2126166233 | 12 years ago |
chrox | af13491b6c | 12 years ago |
{Qingping,Dave} Hou | 2a43323ecd | 12 years ago |
chrox | ab407c896c | 12 years ago |
{Qingping,Dave} Hou | 1bbd353d95 | 12 years ago |
{Qingping,Dave} Hou | befad6bb1e | 12 years ago |
Tigran Aivazian | 27817d66d8 | 12 years ago |
Tigran Aivazian | dae7b95753 | 12 years ago |
Tigran Aivazian | 15aee5f4f2 | 12 years ago |
Tigran Aivazian | 4bc095122c | 12 years ago |
Tigran Aivazian | 60d667db63 | 12 years ago |
Tigran Aivazian | 8c43d8226f | 12 years ago |
{Qingping,Dave} Hou | fd095706c4 | 12 years ago |
Tigran Aivazian | 47fd12f3d2 | 12 years ago |
{Qingping,Dave} Hou | 1d277d3009 | 12 years ago |
chrox | 65bd523d4c | 12 years ago |
Tigran Aivazian | bade79138d | 12 years ago |
Tigran Aivazian | 69d58ae6f5 | 12 years ago |
Tigran Aivazian | 1ca3bf3592 | 12 years ago |
Tigran Aivazian | 3a1e65a570 | 12 years ago |
Tigran Aivazian | 3ca9d510e5 | 12 years ago |
Tigran Aivazian | 4afe0bc3be | 12 years ago |
kai771 | 84aa00bbe4 | 12 years ago |
kai771 | 255aa6c761 | 12 years ago |
kai771 | 57ce4b201f | 12 years ago |
HW | 618c218969 | 12 years ago |
{Qingping,Dave} Hou | 7a627836bb | 12 years ago |
HW | ec26c4cec1 | 12 years ago |
HW | a04823528b | 12 years ago |
HW | 613ccb1955 | 12 years ago |
HW | 5c1090866f | 12 years ago |
Tigran Aivazian | e8840e6059 | 12 years ago |
Tigran Aivazian | aeb6248424 | 12 years ago |
chrox | 0e042c0ff1 | 12 years ago |
{Qingping,Dave} Hou | 959ddad864 | 12 years ago |
{Qingping,Dave} Hou | 850db0475b | 12 years ago |
chrox | 3c197ccd78 | 12 years ago |
HW | f2e228f17f | 12 years ago |
HW | 5e51dd9f86 | 12 years ago |
chrox | 1e639cc97d | 12 years ago |
HW | eb7e15cfb2 | 12 years ago |
chrox | 655f2bb19b | 12 years ago |
chrox | 3bedc59287 | 12 years ago |
Tigran Aivazian | 504954433e | 12 years ago |
Tigran Aivazian | 3cf606ec81 | 12 years ago |
Tigran Aivazian | 108737f88e | 12 years ago |
chrox | c5ebd6c6a6 | 12 years ago |
Qingping Hou | fddcc51b72 | 12 years ago |
kai771 | cb1b0cdfb0 | 12 years ago |
kai771 | 692f5ec454 | 12 years ago |
Dobrica Pavlinušić | 5d34c16493 | 12 years ago |
Dobrica Pavlinušić | 770c76c33b | 12 years ago |
Tigran Aivazian | 4b5f3c7d4d | 12 years ago |
Tigran Aivazian | 60af1862ba | 12 years ago |
kai771 | 3f1c052024 | 12 years ago |
{Qingping,Dave} Hou | 7d62cad1ef | 12 years ago |
kai771 | c83b8b3c88 | 12 years ago |
kai771 | 424e29e7e4 | 12 years ago |
chrox | a8354db6ca | 12 years ago |
{Qingping,Dave} Hou | 5f00ab1f2a | 12 years ago |
{Qingping,Dave} Hou | f0a391fd66 | 12 years ago |
Tigran Aivazian | a907643279 | 12 years ago |
Tigran Aivazian | 694459df64 | 12 years ago |
Tigran Aivazian | bef40656a6 | 12 years ago |
Tigran Aivazian | 83fb0cd69b | 12 years ago |
Dobrica Pavlinušić | a4844cfba8 | 12 years ago |
Qingping Hou | e6d459970a | 12 years ago |
{Qingping,Dave} Hou | 795199d84a | 12 years ago |
kai771 | a127ec2c47 | 12 years ago |
Tigran Aivazian | 06f32ee5dd | 12 years ago |
Dobrica Pavlinušić | 1f1151391d | 12 years ago |
Tigran Aivazian | a4da5e8445 | 12 years ago |
{Qingping,Dave} Hou | 9f07996413 | 12 years ago |
{Qingping,Dave} Hou | df327b9fe1 | 12 years ago |
chrox | a06051932f | 12 years ago |
Tigran Aivazian | 4bea524f7c | 12 years ago |
chrox | 349d00c9b8 | 12 years ago |
chrox | 3c3fe9b0ef | 12 years ago |
kai771 | c64d07ff0a | 12 years ago |
kai771 | 969b26890e | 12 years ago |
Tigran Aivazian | 76987355a5 | 12 years ago |
kai771 | aada329165 | 12 years ago |
kai771 | 0d05db3a4b | 12 years ago |
kai771 | a4322e44b9 | 12 years ago |
kai771 | 2d09659e86 | 12 years ago |
Dobrica Pavlinušić | 8cc757503b | 12 years ago |
chrox | 47da06a29e | 12 years ago |
chrox | 2f0cc5b827 | 12 years ago |
chrox | 389177bd51 | 12 years ago |
Dobrica Pavlinušić | c8f5009b98 | 12 years ago |
chrox | 6c46977074 | 12 years ago |
chrox | cc9a47daa8 | 12 years ago |
chrox | 7557dbe05e | 12 years ago |
chrox | 138f9facf3 | 12 years ago |
chrox | f2d03daac4 | 12 years ago |
chrox | ce631f4827 | 12 years ago |
Dobrica Pavlinušić | 75638663dc | 12 years ago |
chrox | 83cc0ea24b | 12 years ago |
chrox | f3f5d0a920 | 12 years ago |
chrox | f4a2b5f3a0 | 12 years ago |
Dobrica Pavlinušić | 2621474916 | 12 years ago |
chrox | 496591730f | 12 years ago |
chrox | 251f002ee0 | 12 years ago |
chrox | 472e669670 | 12 years ago |
Dobrica Pavlinušić | ac218923d3 | 12 years ago |
chrox | c240f88b8d | 12 years ago |
chrox | b4417c73de | 12 years ago |
Dobrica Pavlinušić | a7b396b3b1 | 12 years ago |
Tigran Aivazian | 84571c315c | 12 years ago |
Dobrica Pavlinušić | 0f34ca7398 | 12 years ago |
Tigran Aivazian | 17726663b2 | 12 years ago |
Tigran Aivazian | 85a97a641b | 12 years ago |
Dobrica Pavlinušić | b16f33f8f8 | 12 years ago |
Tigran Aivazian | 47d507c406 | 12 years ago |
Tigran Aivazian | 90376422d9 | 12 years ago |
Tigran Aivazian | c2bc496d35 | 12 years ago |
Tigran Aivazian | 9ad3577be8 | 12 years ago |
chrox | e05366f464 | 12 years ago |
Tigran Aivazian | be1996d0a0 | 12 years ago |
Tigran Aivazian | 3eb1e83002 | 12 years ago |
chrox | 564f73d035 | 12 years ago |
Tigran Aivazian | 1264adcda1 | 12 years ago |
Tigran Aivazian | 0feb47335f | 12 years ago |
chrox | 88fb136376 | 12 years ago |
Tigran Aivazian | cbada0f8ef | 12 years ago |
Tigran Aivazian | ff96c601f1 | 12 years ago |
Tigran Aivazian | bb94c20bcf | 12 years ago |
Tigran Aivazian | 772dda7e57 | 12 years ago |
Dobrica Pavlinušić | 6311ff40e5 | 12 years ago |
Dobrica Pavlinušić | cdd2eddca9 | 12 years ago |
Tigran Aivazian | 47302a339a | 12 years ago |
chrox | d870d1c905 | 12 years ago |
Tigran Aivazian | 3305021115 | 12 years ago |
chrox | 76844ec996 | 12 years ago |
chrox | 5ad0805410 | 12 years ago |
Tigran Aivazian | d20ee50f10 | 12 years ago |
Tigran Aivazian | 357656f79d | 12 years ago |
chrox | f48a87bfdf | 12 years ago |
Tigran Aivazian | c3ded9f8fe | 12 years ago |
Tigran Aivazian | d82e7ca4d2 | 12 years ago |
chrox | 84971cd941 | 12 years ago |
chrox | 067be63258 | 12 years ago |
chrox | c3be6ef214 | 12 years ago |
chrox | 1c72becc71 | 12 years ago |
chrox | 34d8c32e26 | 12 years ago |
chrox | 2fd933abc9 | 12 years ago |
chrox | e5e99e3d7f | 12 years ago |
chrox | 46b6000cb1 | 12 years ago |
chrox | e42403bb85 | 12 years ago |
Dobrica Pavlinušić | a4e79be0b8 | 12 years ago |
Dobrica Pavlinušić | 9acea2e0ac | 12 years ago |
Tigran Aivazian | 791aee86c6 | 12 years ago |
Tigran Aivazian | 686cad7965 | 12 years ago |
Tigran Aivazian | 6d018da7ca | 12 years ago |
Dobrica Pavlinušić | d62c721bab | 12 years ago |
Tigran Aivazian | 73057bb8bc | 12 years ago |
Tigran Aivazian | c8e7b638fe | 12 years ago |
Tigran Aivazian | 4f2c452365 | 12 years ago |
Tigran Aivazian | 525e1bdfd9 | 12 years ago |
chrox | e2a4a0d3b6 | 12 years ago |
Dobrica Pavlinušić | 8bb8f7860f | 12 years ago |
Tigran Aivazian | 5c165a23ab | 12 years ago |
Tigran Aivazian | 2c1fbbc712 | 12 years ago |
Tigran Aivazian | 29762d02fe | 12 years ago |
Tigran Aivazian | 9bd6a4c63a | 12 years ago |
Tigran Aivazian | e97770d8ae | 12 years ago |
Dobrica Pavlinušić | 1be32e99a9 | 12 years ago |
Tigran Aivazian | 08e9240f10 | 12 years ago |
chrox | e0a9548975 | 12 years ago |
{Qingping,Dave} Hou | 5c4790af0e | 12 years ago |
{Qingping,Dave} Hou | d7b6ff45aa | 12 years ago |
Tigran Aivazian | 3b0fc4b659 | 12 years ago |
chrox | a6f83fb856 | 12 years ago |
Tigran Aivazian | 2fe8ea12eb | 12 years ago |
Tigran Aivazian | 0e6b099647 | 12 years ago |
{Qingping,Dave} Hou | d5f64aba91 | 12 years ago |
Tigran Aivazian | 5cd3cec652 | 12 years ago |
{Qingping,Dave} Hou | d8de1b66b1 | 12 years ago |
Tigran Aivazian | 1a7e805e4b | 12 years ago |
{Qingping,Dave} Hou | 8c0e6a53a4 | 12 years ago |
chrox | 6b79310bb1 | 12 years ago |
chrox | f7e4a548c7 | 12 years ago |
chrox | afd97e0d43 | 12 years ago |
chrox | 0695d9155e | 12 years ago |
chrox | a53fa980f8 | 12 years ago |
Dobrica Pavlinušić | b15eedeaaf | 12 years ago |
Tigran Aivazian | e57730b128 | 12 years ago |
chrox | f1977a470c | 12 years ago |
chrox | 717c2aec48 | 12 years ago |
chrox | 46b7b23a25 | 12 years ago |
chrox | fb53035484 | 12 years ago |
{Qingping,Dave} Hou | bda58e1364 | 12 years ago |
Tigran Aivazian | d76eb27fa3 | 12 years ago |
{Qingping,Dave} Hou | e97f009320 | 12 years ago |
chrox | d2dcbff4bb | 12 years ago |
Tigran Aivazian | ffaa770434 | 12 years ago |
Tigran Aivazian | e0777b6ed2 | 12 years ago |
Tigran Aivazian | 19fa375a1e | 12 years ago |
Dobrica Pavlinušić | 8a902d104b | 12 years ago |
Dobrica Pavlinušić | 98ceb7dc60 | 12 years ago |
Tigran Aivazian | cd5324c141 | 12 years ago |
Tigran Aivazian | d6a8d3be73 | 12 years ago |
Tigran Aivazian | 9fd7e36b07 | 12 years ago |
Dobrica Pavlinušić | 85d3ec3704 | 12 years ago |
Dobrica Pavlinušić | b6960ce274 | 12 years ago |
Dobrica Pavlinušić | 6911b0b0fe | 12 years ago |
Dobrica Pavlinušić | 9d9fdf3ce2 | 12 years ago |
Dobrica Pavlinušić | 9fa47dcd2a | 12 years ago |
Tigran Aivazian | 751eaefe05 | 12 years ago |
Tigran Aivazian | d58b9aa4e8 | 12 years ago |
Tigran Aivazian | 22614a4ade | 12 years ago |
Tigran Aivazian | 2d999e8c87 | 12 years ago |
Tigran Aivazian | e63a6cb8fe | 12 years ago |
Tigran Aivazian | 50d7594d7f | 12 years ago |
Tigran Aivazian | ce19393d0e | 12 years ago |
Qingping Hou | 857cd74ad3 | 12 years ago |
{Qingping,Dave} Hou | e629ff2960 | 12 years ago |
Tigran Aivazian | 45c0f7efb6 | 12 years ago |
Qingping Hou | cfbe9456ba | 12 years ago |
Qingping Hou | b2d1302b85 | 12 years ago |
Dobrica Pavlinušić | 042823d56c | 12 years ago |
Tigran Aivazian | 4cc3e570e3 | 12 years ago |
Qingping Hou | 4634c8e165 | 12 years ago |
Qingping Hou | a360cfbb17 | 12 years ago |
Dobrica Pavlinušić | 4df2eabf27 | 12 years ago |
Dobrica Pavlinusic | 5e1a6b3101 | 12 years ago |
Dobrica Pavlinusic | 534b8dc114 | 12 years ago |
Dobrica Pavlinušić | 53b6f9b7b3 | 12 years ago |
Dobrica Pavlinusic | 7b6ab1a486 | 12 years ago |
Dobrica Pavlinusic | 08f2c2e2f2 | 12 years ago |
Dobrica Pavlinušić | 6ab8e21375 | 12 years ago |
Dobrica Pavlinušić | 333360af0b | 12 years ago |
Dobrica Pavlinušić | 3a774380a4 | 12 years ago |
Tigran Aivazian | 59cfb63ea4 | 12 years ago |
Dobrica Pavlinusic | d64a4fb7a1 | 12 years ago |
Tigran Aivazian | d2a350eca8 | 12 years ago |
Tigran Aivazian | dfd12100f4 | 12 years ago |
Dobrica Pavlinusic | 17bf58b062 | 12 years ago |
Tigran Aivazian | 5b2ffc7a53 | 12 years ago |
Dobrica Pavlinusic | e52910a81f | 12 years ago |
Dobrica Pavlinušić | 7277149edb | 12 years ago |
Tigran Aivazian | 56946f7393 | 12 years ago |
Tigran Aivazian | 081172a2ae | 12 years ago |
{Qingping,Dave} Hou | f5d3db0341 | 12 years ago |
Tigran Aivazian | 4ff751d2f1 | 12 years ago |
Tigran Aivazian | 7d53f8d34c | 12 years ago |
Tigran Aivazian | dc52f81ec6 | 12 years ago |
Tigran Aivazian | f5ac464369 | 12 years ago |
Tigran Aivazian | a354d96ab8 | 12 years ago |
Dobrica Pavlinušić | 78c9223897 | 12 years ago |
Dobrica Pavlinušić | f58de262a6 | 12 years ago |
Tigran Aivazian | daeb7ae7c8 | 12 years ago |
Tigran Aivazian | 8953cc89f6 | 12 years ago |
Tigran Aivazian | aa2b21aa23 | 12 years ago |
Tigran Aivazian | a1f9068d5b | 12 years ago |
{Qingping,Dave} Hou | b6218b30a3 | 12 years ago |
{Qingping,Dave} Hou | 0c4f94dd19 | 12 years ago |
Tigran Aivazian | 2ca5c028c1 | 12 years ago |
Tigran Aivazian | b63edfca1f | 12 years ago |
{Qingping,Dave} Hou | 127a20146b | 12 years ago |
Tigran Aivazian | 669d84df18 | 12 years ago |
Tigran Aivazian | f0511ff22c | 12 years ago |
Tigran Aivazian | 5823ab0c44 | 12 years ago |
Tigran Aivazian | 2d2e2b6216 | 12 years ago |
Tigran Aivazian | 4f16f1d97e | 12 years ago |
Tigran Aivazian | 51b3d70165 | 12 years ago |
Dobrica Pavlinusic | 7bf9fadebe | 12 years ago |
Dobrica Pavlinusic | 3c755cbc2e | 12 years ago |
Dobrica Pavlinusic | 9d02f32b4c | 12 years ago |
Dobrica Pavlinusic | bbb5ec00a5 | 12 years ago |
Dobrica Pavlinusic | 1d4dc8f280 | 12 years ago |
Dobrica Pavlinusic | eb450f262f | 12 years ago |
Dobrica Pavlinusic | 99cf95c96f | 12 years ago |
Dobrica Pavlinusic | e8ac31e07c | 12 years ago |
Dobrica Pavlinusic | 60e01c8636 | 12 years ago |
{Qingping,Dave} Hou | 5f1ce0e82d | 12 years ago |
Tigran Aivazian | d86944cce0 | 12 years ago |
{Qingping,Dave} Hou | b74347afae | 12 years ago |
{Qingping,Dave} Hou | 6bb497cfae | 12 years ago |
{Qingping,Dave} Hou | fe62c577b0 | 12 years ago |
Tigran Aivazian | 8ab0e7fcb8 | 12 years ago |
Tigran Aivazian | f2c17ed435 | 12 years ago |
kai771 | 286f7caf2d | 12 years ago |
Tigran Aivazian | a5cd590e39 | 12 years ago |
kai771 | bb899cfad2 | 12 years ago |
Dobrica Pavlinušić | cdbacebd06 | 12 years ago |
Tigran Aivazian | 148d92037e | 12 years ago |
Tigran Aivazian | 98898dabda | 12 years ago |
Tigran Aivazian | 2b04e5ee86 | 12 years ago |
HW | b5bc152c3d | 12 years ago |
HW | 10806074b8 | 12 years ago |
Dobrica Pavlinušić | 31c10f09e7 | 12 years ago |
HW | 4810eded5c | 12 years ago |
Tigran Aivazian | 4705ec60ad | 12 years ago |
Tigran Aivazian | ba84bb56d3 | 12 years ago |
Tigran Aivazian | a68407a770 | 12 years ago |
Tigran Aivazian | 13646a3d51 | 12 years ago |
Tigran Aivazian | aa3bfb8439 | 12 years ago |
Tigran Aivazian | afc89beb80 | 12 years ago |
Tigran Aivazian | edd347eb55 | 12 years ago |
Tigran Aivazian | c64da132c5 | 12 years ago |
{Qingping,Dave} Hou | f4f29bb12b | 12 years ago |
{Qingping,Dave} Hou | fd27ba693b | 12 years ago |
{Qingping,Dave} Hou | 78301d98ff | 12 years ago |
Tigran Aivazian | 82831f4312 | 12 years ago |
Tigran Aivazian | 832a36c624 | 12 years ago |
Tigran Aivazian | 1dec3def12 | 12 years ago |
{Qingping,Dave} Hou | 83874931f5 | 12 years ago |
{Qingping,Dave} Hou | 80bdcd6cf6 | 12 years ago |
Tigran Aivazian | ef4d0a2ae4 | 12 years ago |
Tigran Aivazian | 339bda372a | 12 years ago |
Tigran Aivazian | 4d1945d0de | 12 years ago |
HW | 723362e469 | 12 years ago |
Tigran Aivazian | da3e336de5 | 12 years ago |
Dobrica Pavlinušić | 495a24b972 | 12 years ago |
Dobrica Pavlinušić | 224327406c | 12 years ago |
Dobrica Pavlinušić | 7d5b2e6962 | 12 years ago |
Tigran Aivazian | 4a727c8595 | 12 years ago |
Tigran Aivazian | 30415e8e3b | 12 years ago |
Tigran Aivazian | 62b2261555 | 12 years ago |
Tigran Aivazian | 55532b0cd6 | 12 years ago |
Tigran Aivazian | 0e84f27512 | 12 years ago |
Tigran Aivazian | 8d88aa8901 | 12 years ago |
Tigran Aivazian | 76d91a9ec4 | 12 years ago |
Tigran Aivazian | 64b6ef4afd | 12 years ago |
Tigran Aivazian | b2b9f425dd | 12 years ago |
Dobrica Pavlinušić | 08ef1e3666 | 12 years ago |
Tigran Aivazian | 7177f71c55 | 12 years ago |
Tigran Aivazian | 30de5e0a23 | 12 years ago |
Dobrica Pavlinušić | fe77be4830 | 12 years ago |
Tigran Aivazian | 0de624a7ea | 12 years ago |
Tigran Aivazian | 3dd2fba241 | 12 years ago |
Dobrica Pavlinušić | 436d738b43 | 12 years ago |
Dobrica Pavlinusic | d14d0ba137 | 12 years ago |
Dobrica Pavlinusic | 905a7e704d | 12 years ago |
Dobrica Pavlinusic | 4a331ab8e8 | 12 years ago |
Tigran Aivazian | cd48bc4d8b | 12 years ago |
Dobrica Pavlinušić | 5608759e0f | 12 years ago |
Dobrica Pavlinušić | 621b787a85 | 12 years ago |
Tigran Aivazian | c115127e1c | 12 years ago |
Tigran Aivazian | e06069c147 | 12 years ago |
Tigran Aivazian | a63140e9c8 | 12 years ago |
Tigran Aivazian | aeb8bcf729 | 12 years ago |
Tigran Aivazian | 33768b50a7 | 12 years ago |
Dobrica Pavlinušić | 5ce6bf87ca | 12 years ago |
Tigran Aivazian | cb25029ddd | 12 years ago |
Dobrica Pavlinušić | 36486d0697 | 12 years ago |
Dobrica Pavlinušić | 239c3bbcbc | 12 years ago |
Tigran Aivazian | 7fe288ace3 | 12 years ago |
Tigran Aivazian | e9913dce40 | 12 years ago |
Dobrica Pavlinušić | c1827b1d9a | 12 years ago |
Tigran Aivazian | ba0e4460d7 | 12 years ago |
Tigran Aivazian | cf557bdaf8 | 12 years ago |
Tigran Aivazian | 87712cf0e4 | 12 years ago |
Tigran Aivazian | c7b4cf71f8 | 12 years ago |
Dobrica Pavlinusic | fea2eea7a4 | 12 years ago |
Dobrica Pavlinusic | 6ab06cb584 | 12 years ago |
Dobrica Pavlinusic | cceae8ad0a | 12 years ago |
Dobrica Pavlinusic | d6bbf5d1ef | 12 years ago |
Tigran Aivazian | 379cba7b68 | 12 years ago |
Tigran Aivazian | ea92f8c1fc | 12 years ago |
Dobrica Pavlinušić | 1ced4ceb17 | 12 years ago |
{Qingping,Dave} Hou | d916c55a87 | 12 years ago |
Tigran Aivazian | e541a378a2 | 12 years ago |
Tigran Aivazian | ca5be5c0e6 | 12 years ago |
Tigran Aivazian | 4945994516 | 12 years ago |
Tigran Aivazian | 1dccc29c7d | 12 years ago |
Tigran Aivazian | 5eeda016a9 | 12 years ago |
Tigran Aivazian | 683ec7570c | 12 years ago |
{Qingping,Dave} Hou | d1d6cdb056 | 12 years ago |
Tigran Aivazian | a5032046d3 | 12 years ago |
Dobrica Pavlinušić | 4716321b0a | 12 years ago |
NuPogodi | 5a493c141f | 12 years ago |
NuPogodi | 81cb3a1f25 | 12 years ago |
NuPogodi | 55f4c278b7 | 12 years ago |
{Qingping,Dave} Hou | f7040b30af | 12 years ago |
NuPogodi | 10592c68b0 | 12 years ago |
{Qingping,Dave} Hou | 17acaa27b5 | 12 years ago |
Tigran Aivazian | 77273a84a7 | 12 years ago |
Tigran Aivazian | 057c461e7d | 12 years ago |
Tigran Aivazian | c7b82f938a | 12 years ago |
Dobrica Pavlinušić | 20e4b55965 | 12 years ago |
Tigran Aivazian | 723be220ac | 12 years ago |
Tigran Aivazian | c13a1553f4 | 12 years ago |
Dobrica Pavlinušić | 48fba48206 | 12 years ago |
Tigran Aivazian | a2970bafad | 12 years ago |
Tigran Aivazian | 8f68227c3c | 12 years ago |
{Qingping,Dave} Hou | d3c6ac816b | 12 years ago |
Tigran Aivazian | f2443a7d33 | 12 years ago |
{Qingping,Dave} Hou | f011bdc358 | 12 years ago |
Tigran Aivazian | d1d955f3a4 | 12 years ago |
Tigran Aivazian | f3306d5512 | 12 years ago |
{Qingping,Dave} Hou | 8308ce0218 | 12 years ago |
{Qingping,Dave} Hou | 6e9b809238 | 12 years ago |
Tigran Aivazian | 8107d84e9e | 12 years ago |
Tigran Aivazian | 37099d81f1 | 12 years ago |
NiLuJe | fab4b29502 | 12 years ago |
NiLuJe | 9cc3438fd2 | 12 years ago |
NiLuJe | 1f2b85e0de | 12 years ago |
NiLuJe | aa68e5250a | 12 years ago |
NiLuJe | ef94d27084 | 12 years ago |
NiLuJe | 1606cc195d | 12 years ago |
NiLuJe | 566d081503 | 12 years ago |
NiLuJe | c38c6dbea4 | 12 years ago |
NiLuJe | 2d5c952749 | 12 years ago |
NiLuJe | bd306da170 | 12 years ago |
NiLuJe | c312eaabf1 | 12 years ago |
NiLuJe | 50386743ec | 12 years ago |
NiLuJe | 29878a0a90 | 12 years ago |
NiLuJe | a79d29fd17 | 12 years ago |
NiLuJe | f817437660 | 12 years ago |
NiLuJe | 36d4b5e22a | 12 years ago |
NiLuJe | bf032e5c6a | 12 years ago |
NiLuJe | 392f29f522 | 12 years ago |
NiLuJe | d4782d05e8 | 12 years ago |
Tigran Aivazian | 28e9f06727 | 12 years ago |
{Qingping,Dave} Hou | bfe3c605be | 12 years ago |
Qingping Hou | 269de90598 | 12 years ago |
Qingping Hou | 14fdb90f58 | 12 years ago |
Tigran Aivazian | fe20d8a899 | 12 years ago |
Tigran Aivazian | 35c423328e | 12 years ago |
Tigran Aivazian | 4da5138bbe | 12 years ago |
Tigran Aivazian | b5f1ec08d0 | 12 years ago |
Tigran Aivazian | da766c09ce | 12 years ago |
Qingping Hou | 3623ad9afb | 12 years ago |
{Qingping,Dave} Hou | e5a2ac4344 | 12 years ago |
Qingping Hou | 44a4e32c93 | 12 years ago |
Qingping Hou | 9127d1c9a1 | 12 years ago |
NiLuJe | a3be10bb96 | 12 years ago |
Qingping Hou | f37d6a647f | 12 years ago |
{Qingping,Dave} Hou | c628100958 | 12 years ago |
{Qingping,Dave} Hou | e846f2089b | 12 years ago |
{Qingping,Dave} Hou | ab93d91056 | 12 years ago |
Qingping Hou | d2a1033baf | 12 years ago |
NiLuJe | c7fa11e7e9 | 12 years ago |
NiLuJe | 9acdedcaeb | 12 years ago |
NiLuJe | a2ce5dd101 | 12 years ago |
NiLuJe | 3c181ead77 | 12 years ago |
NiLuJe | 8b45833e2d | 12 years ago |
NuPogodi | 00c1f2ec9c | 12 years ago |
NiLuJe | 7bc80277e4 | 12 years ago |
NiLuJe | 4356622186 | 12 years ago |
NiLuJe | 9cc106995d | 12 years ago |
Dobrica Pavlinusic | ccff5c899d | 12 years ago |
NiLuJe | ffe7be7085 | 12 years ago |
NiLuJe | 76714c453a | 12 years ago |
NiLuJe | 4522b062cc | 12 years ago |
NiLuJe | d0573049c9 | 12 years ago |
NiLuJe | 5c76ef0f78 | 12 years ago |
Dobrica Pavlinusic | 53a60495b5 | 12 years ago |
Dobrica Pavlinusic | 6325183dc3 | 12 years ago |
NiLuJe | 4e861715e3 | 12 years ago |
NiLuJe | 3f5fe1e991 | 12 years ago |
NiLuJe | 8e7f4ba989 | 12 years ago |
NiLuJe | 44f69b55cd | 12 years ago |
NiLuJe | 6b245b9c80 | 12 years ago |
NiLuJe | 9fab02e33f | 12 years ago |
NiLuJe | d27f20d696 | 12 years ago |
NiLuJe | 1a746d6e8f | 12 years ago |
{Qingping,Dave} Hou | 3bd8c5c9f4 | 12 years ago |
Tigran Aivazian | 0c1eb1109e | 12 years ago |
Tigran Aivazian | b6936f9dc5 | 12 years ago |
Tigran Aivazian | 13d686f438 | 12 years ago |
Dobrica Pavlinusic | 2fc0cb2ddd | 12 years ago |
Tigran Aivazian | 2fefc96b79 | 12 years ago |
Tigran Aivazian | d2e3558add | 12 years ago |
Tigran Aivazian | 5459cb18ae | 12 years ago |
{Qingping,Dave} Hou | 69e43d1c4d | 12 years ago |
{Qingping,Dave} Hou | fcecd1425a | 12 years ago |
Tigran Aivazian | 72f669ff3c | 12 years ago |
Tigran Aivazian | 79c1dd44ca | 12 years ago |
Dobrica Pavlinusic | 4d187aad4e | 12 years ago |
Tigran Aivazian | 72b064fa9a | 12 years ago |
{Qingping,Dave} Hou | a8197950d0 | 12 years ago |
Dobrica Pavlinusic | 12e9930975 | 12 years ago |
Tigran Aivazian | 051926637b | 12 years ago |
Tigran Aivazian | b4c0162a40 | 12 years ago |
{Qingping,Dave} Hou | c3b0c343c9 | 12 years ago |
Tigran Aivazian | 1a3371c5bf | 12 years ago |
Dobrica Pavlinušić | 00bdcd7f14 | 12 years ago |
Tigran Aivazian | 5959c56937 | 12 years ago |
Tigran Aivazian | 24e9d77a37 | 12 years ago |
Tigran Aivazian | db077b2f34 | 12 years ago |
Tigran Aivazian | f77a201e0b | 12 years ago |
{Qingping,Dave} Hou | a416e35555 | 12 years ago |
Tigran Aivazian | 93e946b0a4 | 12 years ago |
Dobrica Pavlinušić | a6a4380d41 | 12 years ago |
NuPogodi | f872473604 | 12 years ago |
Tigran Aivazian | f3b52f269c | 12 years ago |
Tigran Aivazian | 90971a7bf7 | 12 years ago |
{Qingping,Dave} Hou | 33bb760576 | 12 years ago |
Tigran Aivazian | 496fcc089f | 12 years ago |
Tigran Aivazian | 58d19ea7fd | 12 years ago |
Dobrica Pavlinusic | 7b983ee7e6 | 12 years ago |
Dobrica Pavlinusic | 01a57b2fb9 | 12 years ago |
Tigran Aivazian | 850a777760 | 12 years ago |
Tigran Aivazian | 68c95e4bb2 | 12 years ago |
{Qingping,Dave} Hou | cc1ab45b1c | 12 years ago |
{Qingping,Dave} Hou | 8588b08761 | 12 years ago |
Dobrica Pavlinušić | d4ee7f2c53 | 12 years ago |
Dobrica Pavlinusic | 7ba8d6f877 | 12 years ago |
Tigran Aivazian | a7b22ba0d4 | 12 years ago |
Dobrica Pavlinusic | fe2517ecd2 | 12 years ago |
Dobrica Pavlinusic | c8b3a0d486 | 12 years ago |
Dobrica Pavlinušić | 44bb4d18f2 | 12 years ago |
Tigran Aivazian | 5d82ffe984 | 12 years ago |
Tigran Aivazian | 0987c2826e | 12 years ago |
Tigran Aivazian | 819efb55aa | 12 years ago |
Tigran Aivazian | 6710aa132f | 12 years ago |
Tigran Aivazian | 9e134ce976 | 12 years ago |
Tigran Aivazian | 3cdb57ccc7 | 12 years ago |
Dobrica Pavlinusic | 929334c00c | 12 years ago |
Dobrica Pavlinusic | 51e2a0c13b | 12 years ago |
Dobrica Pavlinusic | 771901d56e | 12 years ago |
Tigran Aivazian | a0cfcba266 | 12 years ago |
{Qingping,Dave} Hou | 3fd3ff3680 | 12 years ago |
Dobrica Pavlinusic | a9bff12d8e | 12 years ago |
Dobrica Pavlinusic | d936f86f04 | 12 years ago |
Dobrica Pavlinusic | 88828fd478 | 12 years ago |
Dobrica Pavlinusic | ee7b0ca41e | 12 years ago |
Dobrica Pavlinusic | fc22f5ed93 | 12 years ago |
Tigran Aivazian | df65245e70 | 12 years ago |
Tigran Aivazian | bc3c3f6a38 | 12 years ago |
Tigran Aivazian | 96ac2bef8d | 12 years ago |
Tigran Aivazian | 67efe2d4ca | 12 years ago |
Tigran Aivazian | 9e3ebe9496 | 12 years ago |
Tigran Aivazian | 37dc2d9530 | 12 years ago |
Dobrica Pavlinusic | 16c5171432 | 12 years ago |
Dobrica Pavlinusic | 01b1f5ba66 | 12 years ago |
Dobrica Pavlinušić | 4d09763224 | 12 years ago |
Dobrica Pavlinusic | cc222c6525 | 12 years ago |
Tigran Aivazian | 1a119660ea | 12 years ago |
Tigran Aivazian | 416c62ca61 | 12 years ago |
Tigran Aivazian | 385f74222a | 12 years ago |
Tigran Aivazian | ba1e739543 | 12 years ago |
Tigran Aivazian | b297ee2c71 | 12 years ago |
Tigran Aivazian | 2fdecb7b40 | 12 years ago |
Tigran Aivazian | ac46f7237f | 12 years ago |
Tigran Aivazian | 504425cd89 | 12 years ago |
Tigran Aivazian | 864be87fc2 | 12 years ago |
Tigran Aivazian | 06a0b27664 | 12 years ago |
Tigran Aivazian | 16623b5b08 | 12 years ago |
Tigran Aivazian | 4b5c33d4cd | 12 years ago |
Dobrica Pavlinušić | cee63d6078 | 12 years ago |
NuPogodi | 9403d1c120 | 12 years ago |
NuPogodi | d542982778 | 12 years ago |
Dobrica Pavlinusic | ac14ac4a21 | 12 years ago |
Dobrica Pavlinusic | 8a7f2bd562 | 12 years ago |
Dobrica Pavlinusic | a3f5de9e7a | 12 years ago |
Dobrica Pavlinusic | 1916f5038b | 12 years ago |
{Qingping,Dave} Hou | d1a4399170 | 12 years ago |
Tigran Aivazian | 1e2429b3be | 12 years ago |
Tigran Aivazian | 47b056861b | 12 years ago |
Tigran Aivazian | 43cbcbf319 | 12 years ago |
Tigran Aivazian | 6e0f0aef26 | 12 years ago |
Dobrica Pavlinusic | f2eeca73aa | 12 years ago |
Dobrica Pavlinusic | 2774350ec6 | 12 years ago |
Dobrica Pavlinusic | 0828468b12 | 12 years ago |
Dobrica Pavlinusic | 46afa82b52 | 12 years ago |
Dobrica Pavlinusic | 71ff602cd5 | 12 years ago |
Dobrica Pavlinusic | e1fc748faf | 12 years ago |
{Qingping,Dave} Hou | 73719e7b2e | 12 years ago |
{Qingping,Dave} Hou | 43b0dce322 | 12 years ago |
NuPogodi | 6a2b0d044d | 12 years ago |
NuPogodi | 59c212971f | 12 years ago |
NuPogodi | 7b7ba80f6e | 12 years ago |
Dobrica Pavlinušić | 6f0ea100ce | 12 years ago |
Tigran Aivazian | d338eff00d | 12 years ago |
{Qingping,Dave} Hou | 52d4b09d6b | 12 years ago |
Tigran Aivazian | 2c021d9d4e | 12 years ago |
Tigran Aivazian | 8b2143561e | 12 years ago |
Tigran Aivazian | de5cf52255 | 12 years ago |
NuPogodi | f7779d7c2a | 12 years ago |
NuPogodi | e621e006b9 | 12 years ago |
Tigran Aivazian | a9f4dd3b1f | 12 years ago |
Tigran Aivazian | de96f30eff | 12 years ago |
Tigran Aivazian | 3056bf440c | 12 years ago |
Dobrica Pavlinusic | 238e25e743 | 12 years ago |
Dobrica Pavlinušić | 940408fc51 | 12 years ago |
Tigran Aivazian | 6cd66a3e79 | 12 years ago |
Tigran Aivazian | b2f4d358ae | 12 years ago |
{Qingping,Dave} Hou | d77801d9a0 | 12 years ago |
Tigran Aivazian | b3f8ddf67d | 12 years ago |
Tigran Aivazian | b8e29b77bc | 12 years ago |
Tigran Aivazian | e5c859059d | 12 years ago |
{Qingping,Dave} Hou | f7c0166fb2 | 12 years ago |
Tigran Aivazian | 8496ccb073 | 12 years ago |
Tigran Aivazian | 69ac04500a | 12 years ago |
{Qingping,Dave} Hou | 720687648e | 12 years ago |
Tigran Aivazian | f11a006775 | 12 years ago |
Tigran Aivazian | eb002540a2 | 12 years ago |
Tigran Aivazian | f5f5040f20 | 12 years ago |
Tigran Aivazian | d2b2d8b18f | 12 years ago |
Tigran Aivazian | c57c319656 | 12 years ago |
{Qingping,Dave} Hou | 35695e2fe0 | 12 years ago |
Tigran Aivazian | 4b9cf4eaf5 | 12 years ago |
Tigran Aivazian | 456136ef71 | 12 years ago |
Tigran Aivazian | c43fb549d3 | 12 years ago |
Tigran Aivazian | a2cb1745d6 | 12 years ago |
Tigran Aivazian | c9c89ef56d | 12 years ago |
Dobrica Pavlinušić | 781bab1e40 | 12 years ago |
Qingping Hou | 5e6a0c82c9 | 12 years ago |
Dobrica Pavlinušić | 55bd3f1bd3 | 12 years ago |
Dobrica Pavlinusic | 263ab789c8 | 12 years ago |
Dobrica Pavlinušić | 3a8707a645 | 12 years ago |
Tigran Aivazian | 75bd54657b | 12 years ago |
Qingping Hou | 1d703f9fb6 | 12 years ago |
{Qingping,Dave} Hou | 16a8b2099b | 12 years ago |
Tigran Aivazian | 1694fc5b0a | 12 years ago |
Dobrica Pavlinušić | a4260c6fc0 | 12 years ago |
Qingping Hou | 8c994ee0bc | 12 years ago |
Qingping Hou | 686a823629 | 12 years ago |
Qingping Hou | 1b3e1db0d2 | 12 years ago |
Qingping Hou | ea325146d3 | 12 years ago |
{Qingping,Dave} Hou | a34696d96c | 12 years ago |
Tigran Aivazian | 51ccc20845 | 12 years ago |
Tigran Aivazian | 56f4ac42bd | 12 years ago |
Tigran Aivazian | 680866d4ad | 12 years ago |
Tigran Aivazian | cc5abd3b87 | 12 years ago |
Tigran Aivazian | 853f34b488 | 12 years ago |
Tigran Aivazian | dac88587bf | 12 years ago |
Tigran Aivazian | 42d9583319 | 12 years ago |
Tigran Aivazian | 527421eb86 | 12 years ago |
Tigran Aivazian | 3048311dd5 | 12 years ago |
{Qingping,Dave} Hou | 31fd466425 | 12 years ago |
Tigran Aivazian | 284ab49db4 | 12 years ago |
Tigran Aivazian | 28ba1c7e37 | 12 years ago |
Dobrica Pavlinušić | a99f429afc | 12 years ago |
Tigran Aivazian | 29b1ef5dde | 12 years ago |
Tigran Aivazian | 922b53ac60 | 12 years ago |
Dobrica Pavlinušić | 657bcee7d0 | 12 years ago |
{Qingping,Dave} Hou | b96edf3693 | 12 years ago |
Tigran Aivazian | 4f2be7a7e8 | 12 years ago |
Tigran Aivazian | da296e2e95 | 12 years ago |
Tigran Aivazian | b6a429a1d4 | 12 years ago |
Tigran Aivazian | dc473e85a9 | 12 years ago |
Tigran Aivazian | b0febe2d9b | 12 years ago |
{Qingping,Dave} Hou | 98b8399692 | 12 years ago |
Tigran Aivazian | 3984355c1c | 12 years ago |
Tigran Aivazian | 8312ad83c5 | 12 years ago |
Tigran Aivazian | e35a828fb0 | 12 years ago |
Qingping Hou | 2f847e6a5e | 12 years ago |
{Qingping,Dave} Hou | f9b8ee0513 | 12 years ago |
Tigran Aivazian | 49a76330ac | 12 years ago |
Tigran Aivazian | 1c32a24450 | 12 years ago |
Dobrica Pavlinušić | 44d9daf07a | 12 years ago |
{Qingping,Dave} Hou | 75d286257f | 12 years ago |
Tigran Aivazian | 253a0829ff | 12 years ago |
Dobrica Pavlinušić | bfc369062e | 12 years ago |
Qingping Hou | 1eb7162796 | 12 years ago |
{Qingping,Dave} Hou | 275e94c7a3 | 12 years ago |
Dobrica Pavlinusic | 880374f5db | 12 years ago |
{Qingping,Dave} Hou | 4cdbcac458 | 12 years ago |
Tigran Aivazian | 47556b5562 | 12 years ago |
Dobrica Pavlinusic | 397d3aa037 | 12 years ago |
Dobrica Pavlinusic | 2332691bb0 | 12 years ago |
{Qingping,Dave} Hou | 0100f33128 | 12 years ago |
{Qingping,Dave} Hou | 79a304bb5d | 12 years ago |
Dobrica Pavlinusic | b27b665a97 | 12 years ago |
Dobrica Pavlinušić | b75470365b | 12 years ago |
Tigran Aivazian | 4242cc9686 | 12 years ago |
Dobrica Pavlinusic | 4e1586fc81 | 12 years ago |
{Qingping,Dave} Hou | af53a78291 | 12 years ago |
Tigran Aivazian | 2c6dfae807 | 12 years ago |
Tigran Aivazian | f1e197cbfd | 12 years ago |
{Qingping,Dave} Hou | 329132d3df | 12 years ago |
{Qingping,Dave} Hou | 7e20d120db | 12 years ago |
Dobrica Pavlinušić | 4c5f488151 | 12 years ago |
Dobrica Pavlinusic | d3aa98abb9 | 12 years ago |
Dobrica Pavlinusic | 3fe518979c | 12 years ago |
Dobrica Pavlinusic | acc61e7c07 | 12 years ago |
{Qingping,Dave} Hou | 9c0b59c79e | 12 years ago |
Qingping Hou | 6a1de042f4 | 12 years ago |
Tigran Aivazian | 27340db68b | 12 years ago |
Tigran Aivazian | 44df8027f7 | 12 years ago |
Tigran Aivazian | e2a3f1de64 | 12 years ago |
Tigran Aivazian | e824fb24da | 12 years ago |
{Qingping,Dave} Hou | aa93a8f078 | 12 years ago |
Dobrica Pavlinusic | 406f3d72da | 12 years ago |
NuPogodi | a5496b83c6 | 12 years ago |
Qingping Hou | 6c9fb8b023 | 12 years ago |
Qingping Hou | 4199ac8e3c | 12 years ago |
{Qingping,Dave} Hou | f03e198573 | 12 years ago |
NuPogodi | d1ae0f3999 | 12 years ago |
NuPogodi | dddbea89df | 12 years ago |
NuPogodi | b052e3207c | 12 years ago |
NuPogodi | 92545a0cc2 | 12 years ago |
NuPogodi | 4196d98323 | 12 years ago |
NuPogodi | 8a792d2f31 | 12 years ago |
NuPogodi | dc2fcc8654 | 12 years ago |
NuPogodi | 5e6a93191a | 12 years ago |
NuPogodi | cfa4afe35a | 12 years ago |
NuPogodi | bd0c180c81 | 12 years ago |
NuPogodi | d58fa93285 | 12 years ago |
NuPogodi | fe5d39821f | 12 years ago |
{Qingping,Dave} Hou | 093f99aaa7 | 12 years ago |
NuPogodi | 676a4610cf | 12 years ago |
NuPogodi | a1c85affd1 | 12 years ago |
NuPogodi | fe2363b29f | 12 years ago |
Dobrica Pavlinušić | a52ce4d22a | 12 years ago |
NuPogodi | 1f89704b90 | 12 years ago |
{Qingping,Dave} Hou | f85614f1c7 | 12 years ago |
Tigran Aivazian | b70865286c | 12 years ago |
Tigran Aivazian | df18cae233 | 12 years ago |
{Qingping,Dave} Hou | c9dcf5bdd2 | 12 years ago |
Tigran Aivazian | af37e9f14d | 12 years ago |
Tigran Aivazian | 524c8209c0 | 12 years ago |
Tigran Aivazian | 95be71c1e1 | 12 years ago |
{Qingping,Dave} Hou | 1e45c06689 | 12 years ago |
Tigran Aivazian | b96f4f509f | 12 years ago |
Tigran Aivazian | 6f1dea294c | 12 years ago |
Tigran Aivazian | 2d4189fc84 | 12 years ago |
Dobrica Pavlinušić | a56e49c3f3 | 12 years ago |
Tigran Aivazian | b2e0ecb7b0 | 12 years ago |
Tigran Aivazian | 3b1da21cfd | 12 years ago |
{Qingping,Dave} Hou | 45d9b10aa6 | 12 years ago |
NuPogodi | 4bb8ef7f45 | 12 years ago |
NuPogodi | c9e20704a4 | 12 years ago |
NuPogodi | a2f7f62a3d | 12 years ago |
Tigran Aivazian | 10a23e81d6 | 12 years ago |
{Qingping,Dave} Hou | ee83b6e043 | 12 years ago |
Tigran Aivazian | 711cee787e | 12 years ago |
NuPogodi | 4d6b03052a | 12 years ago |
Tigran Aivazian | 60b87b4b95 | 12 years ago |
Tigran Aivazian | 9a22a022bf | 12 years ago |
Qingping Hou | 461c135c5d | 12 years ago |
Tigran Aivazian | 2e0876a4ae | 12 years ago |
NuPogodi | 25edd3101f | 12 years ago |
NuPogodi | 20fae9369e | 12 years ago |
Tigran Aivazian | c6a1d22bc9 | 12 years ago |
Tigran Aivazian | b0b0b85ca1 | 12 years ago |
Tigran Aivazian | 3fd33805c5 | 12 years ago |
Dobrica Pavlinusic | d577ed9b00 | 12 years ago |
Tigran Aivazian | 32333ca1d6 | 12 years ago |
Tigran Aivazian | 7000f349c0 | 12 years ago |
Tigran Aivazian | 42491bfefd | 12 years ago |
Tigran Aivazian | 2d5a936480 | 12 years ago |
Tigran Aivazian | 824e9d139e | 12 years ago |
Dobrica Pavlinusic | 989016ece5 | 12 years ago |
Dobrica Pavlinušić | cd3f72136e | 12 years ago |
NuPogodi | bfe8a5d84f | 12 years ago |
Dobrica Pavlinušić | ebe6b87587 | 12 years ago |
NuPogodi | 282d8e693e | 12 years ago |
NuPogodi | 63e1c4b66c | 12 years ago |
NuPogodi | 3f410bdb6b | 12 years ago |
Dobrica Pavlinusic | bc0a911e7e | 12 years ago |
Dobrica Pavlinusic | f0476ab481 | 12 years ago |
Dobrica Pavlinusic | 7d10a636e7 | 12 years ago |
Dobrica Pavlinusic | adf0b42e5b | 12 years ago |
Dobrica Pavlinusic | 5a897fba77 | 12 years ago |
Tigran Aivazian | 5c9a8fbec6 | 12 years ago |
Tigran Aivazian | 0a4e11d4af | 12 years ago |
Tigran Aivazian | 424a6a895b | 12 years ago |
Tigran Aivazian | c4833b7557 | 12 years ago |
Tigran Aivazian | 8a583311c8 | 12 years ago |
Tigran Aivazian | b605536567 | 12 years ago |
Tigran Aivazian | 601b7b9d69 | 12 years ago |
Tigran Aivazian | 5576890263 | 12 years ago |
Tigran Aivazian | de0f40ce9c | 12 years ago |
Tigran Aivazian | 2650faa000 | 12 years ago |
{Qingping,Dave} Hou | d952fff3f3 | 12 years ago |
Dobrica Pavlinusic | 1c6421dc3f | 12 years ago |
Dobrica Pavlinusic | 2dfee0a2fb | 12 years ago |
Dobrica Pavlinusic | d827301639 | 12 years ago |
Dobrica Pavlinusic | eb38dfedd4 | 12 years ago |
Dobrica Pavlinusic | e8ab2381c3 | 12 years ago |
Dobrica Pavlinusic | ed59adecea | 12 years ago |
Dobrica Pavlinušić | 2a24f5a13a | 12 years ago |
Qingping Hou | 5be38246f5 | 12 years ago |
Qingping Hou | 7a3de8b6b7 | 12 years ago |
NuPogodi | c06605e86c | 12 years ago |
NuPogodi | 333537dfd6 | 12 years ago |
NuPogodi | 17585c2bf4 | 12 years ago |
NuPogodi | 2e975fa18d | 12 years ago |
NuPogodi | ead3355a2b | 12 years ago |
NuPogodi | 13ac163c51 | 12 years ago |
NuPogodi | e580a7c278 | 12 years ago |
NuPogodi | f996a69568 | 12 years ago |
{Qingping,Dave} Hou | a505cdaf34 | 12 years ago |
Dobrica Pavlinusic | 2fd5c28007 | 12 years ago |
{Qingping,Dave} Hou | 692b06fdcb | 12 years ago |
Dobrica Pavlinusic | 89fd8d22d6 | 12 years ago |
HW | ee7472be96 | 12 years ago |
HW | a853dbbb1d | 12 years ago |
Kefu Chai | 896c435840 | 12 years ago |
Kefu Chai | 1ecab97c6f | 12 years ago |
HW | 4fb745a0df | 12 years ago |
Kefu Chai | 0e561c610a | 12 years ago |
Qingping Hou | 794e9188fc | 12 years ago |
Qingping Hou | 1e3d72df70 | 12 years ago |
Qingping Hou | ad7a8d044c | 12 years ago |
{Qingping,Dave} Hou | 9b263ca23a | 12 years ago |
Kefu Chai | 8665a7567f | 12 years ago |
Dobrica Pavlinušić | 388d4fb2ac | 12 years ago |
Qingping Hou | 0558a5ec5a | 12 years ago |
Qingping Hou | 2485a552eb | 12 years ago |
Dobrica Pavlinušić | 9b0cb24200 | 12 years ago |
Qingping Hou | 7ab59e9a5c | 12 years ago |
Dobrica Pavlinušić | b46a4f5dbb | 12 years ago |
Qingping Hou | 2dfbf3177f | 12 years ago |
Qingping Hou | dec86586e8 | 12 years ago |
Qingping Hou | 8ef6eb8b73 | 12 years ago |
Qingping Hou | 772a298d48 | 12 years ago |
HW | 454611afb7 | 12 years ago |
Qingping Hou | d0e7f2240d | 12 years ago |
Qingping Hou | d04370464b | 12 years ago |
Qingping Hou | 763e2223d2 | 12 years ago |
Qingping Hou | b76be2b87d | 12 years ago |
Qingping Hou | e2e3f12463 | 12 years ago |
Qingping Hou | 25e943cb84 | 12 years ago |
HW | 4660781c84 | 12 years ago |
HW | 906a5bd48c | 12 years ago |
HW | a0b538affd | 12 years ago |
HW | 0d12ff70cd | 12 years ago |
HW | ccd91b58e3 | 12 years ago |
Qingping Hou | eb0c6b3619 | 12 years ago |
HW | 942ab0d3d4 | 12 years ago |
HW | 978fd9313a | 12 years ago |
HW | e35339a398 | 12 years ago |
HW | b07470535f | 12 years ago |
HW | 7952220b41 | 12 years ago |
HW | 054c63704c | 12 years ago |
HW | 22ce736e11 | 12 years ago |
HW | e2af463bac | 12 years ago |
HW | 02c40b6078 | 12 years ago |
HW | 4d2036fa7b | 12 years ago |
HW | 202b6fc039 | 12 years ago |
HW | 497e1d89de | 12 years ago |
HW | ab400b97ac | 12 years ago |
Dobrica Pavlinusic | ae9be768ab | 12 years ago |
NuPogodi | 944a3b5b05 | 12 years ago |
{Qingping,Dave} Hou | 78efdc77e3 | 12 years ago |
NuPogodi | 1831004da5 | 12 years ago |
NuPogodi | cccdf42892 | 12 years ago |
Dobrica Pavlinušić | b1486ee4bd | 12 years ago |
Qingping Hou | d2d8ad84ed | 12 years ago |
{Qingping,Dave} Hou | 763b97744b | 12 years ago |
Dobrica Pavlinusic | f55654f03e | 12 years ago |
Dobrica Pavlinusic | 9de055eb5d | 12 years ago |
Dobrica Pavlinusic | d678510d9a | 12 years ago |
Dobrica Pavlinusic | a08c35a52c | 12 years ago |
NuPogodi | 86513c3793 | 12 years ago |
NuPogodi | 3a76ec91fd | 12 years ago |
NuPogodi | f595d42431 | 12 years ago |
Qingping Hou | 6f5bf4a3b8 | 12 years ago |
{Qingping,Dave} Hou | c43a312725 | 12 years ago |
Dobrica Pavlinusic | 28161ae3d0 | 12 years ago |
NuPogodi | 93b5da0d8d | 12 years ago |
NuPogodi | 773a4d69b6 | 12 years ago |
NuPogodi | cd046a0637 | 12 years ago |
{Qingping,Dave} Hou | eaaff0d9bc | 12 years ago |
Dobrica Pavlinusic | 54657bb74c | 12 years ago |
Dobrica Pavlinusic | bca64dd4ef | 12 years ago |
Qingping Hou | 121f154313 | 12 years ago |
Dobrica Pavlinusic | ba19ab7029 | 12 years ago |
Dobrica Pavlinušić | e73803603b | 12 years ago |
Qingping Hou | c2cb95f8e8 | 12 years ago |
Qingping Hou | 84eb0e338f | 12 years ago |
Dobrica Pavlinušić | 7e885198ec | 12 years ago |
Qingping Hou | a73d9a5a52 | 12 years ago |
Qingping Hou | a672cf4ee0 | 12 years ago |
Dobrica Pavlinušić | 89f9a8c711 | 12 years ago |
Qingping Hou | d21ea032e5 | 12 years ago |
Qingping Hou | c275862ffe | 12 years ago |
Dobrica Pavlinušić | 0d4d7e7fb9 | 12 years ago |
Qingping Hou | c818d7e90e | 12 years ago |
Qingping Hou | 381b34cc04 | 12 years ago |
Qingping Hou | eb1fbebc5b | 12 years ago |
Qingping Hou | 1e36018021 | 12 years ago |
{Qingping,Dave} Hou | dad5b32331 | 12 years ago |
Dobrica Pavlinusic | 55cdb26823 | 12 years ago |
Dobrica Pavlinusic | de12c6aac0 | 12 years ago |
{Qingping,Dave} Hou | 246619b9c4 | 12 years ago |
Dobrica Pavlinusic | 90b8860ed5 | 12 years ago |
Dobrica Pavlinusic | 31108d536c | 12 years ago |
@ -1,69 +0,0 @@
|
||||
KindlePDFViewer
|
||||
===============
|
||||
|
||||
This is a PDF viewer application, created for usage on the Kindle e-ink reader.
|
||||
It is currently restricted to 4bpp inverse grayscale displays. It's using the
|
||||
muPDF library (see http://mupdf.com/) and its UI is scripted using Lua (see
|
||||
http://www.lua.org/).
|
||||
|
||||
The application is licensed under the GPLv3 (see COPYING file).
|
||||
|
||||
|
||||
Building
|
||||
========
|
||||
|
||||
Follow these steps:
|
||||
|
||||
- install muPDF sources into subfolder "mupdf"
|
||||
- install muPDF third-party sources (see muPDF homepage) into a new subfolder
|
||||
"mupdf/thirdparty"
|
||||
|
||||
- install Lua sources into subfolder "lua"
|
||||
|
||||
=> note that there's a make target to do this. You need wget, unzip and git
|
||||
installed. Then just run "make fetchthirdparty".
|
||||
|
||||
- adapt Makefile to your needs
|
||||
|
||||
- run "make thirdparty". This will build MuPDF (plus the libraries it depends
|
||||
on) and Lua.
|
||||
|
||||
- run "make". This will build the kpdfview application
|
||||
|
||||
|
||||
Running
|
||||
=======
|
||||
|
||||
The user interface (or what's there yet) is scripted in Lua. See "reader.lua".
|
||||
It uses the Linux feature to run scripts by using a corresponding line at its
|
||||
start.
|
||||
So you might just call that script. Note that the script and the kpdfview
|
||||
binary currently must be in the same directory.
|
||||
You would then just call reader.lua, giving the document file path as its first
|
||||
argument. Run reader.lua without arguments to see usage notes.
|
||||
The reader.lua script can also show a file chooser: it will do this when you
|
||||
call it with a directory (instead of a file) as first argument.
|
||||
|
||||
|
||||
Device emulation
|
||||
================
|
||||
|
||||
The code also features a device emulation. You need SDL headers and library
|
||||
for this. It allows to develop on a standard PC and saves precious development
|
||||
time. It might also compose the most unfriendly desktop PDF reader, depending
|
||||
on your view.
|
||||
|
||||
To build in "emulation mode", you need to run make like this:
|
||||
make clean cleanthirdparty
|
||||
EMULATE_READER=1 make thirdparty kpdfview
|
||||
|
||||
The reader.lua script needs a device argument in order to cope with some
|
||||
slight differences between actual readers and the emulation. Run it like
|
||||
this:
|
||||
./reader.lua -d emu /PATH/TO/PDF.pdf
|
||||
|
||||
By default emulation will provide DXG resolution of 824*1200. It can be
|
||||
specified at compile time, this is example for Kindle 3:
|
||||
|
||||
EMULATE_READER_W=600 EMULATE_READER_H=800 EMULATE_READER=1 make kpdfview
|
||||
|
@ -0,0 +1,88 @@
|
||||
KindlePDFViewer
|
||||
===============
|
||||
|
||||
This is a document viewer application, created for usage on the Kindle e-ink reader.
|
||||
It is currently restricted to 4bpp inverse grayscale displays. For PDF files
|
||||
it is using the muPDF library (see http://mupdf.com/), for DjVu files djvulibre library
|
||||
and for ebooks (fb2, mobi, ePub, etc) crengine. It can also read JPEG images using
|
||||
libjpeg library. The user interface is scripted using Lua (see http://www.lua.org/).
|
||||
|
||||
The application is licensed under the GPLv3 (see COPYING file).
|
||||
|
||||
|
||||
Building
|
||||
========
|
||||
|
||||
|
||||
Follow these steps:
|
||||
|
||||
* fetch thirdparty sources
|
||||
* manually fetch all the thirdparty sources:
|
||||
* install muPDF sources into subfolder "mupdf"
|
||||
* install muPDF third-party sources (see muPDF homepage) into a new
|
||||
subfolder "mupdf/thirdparty"
|
||||
* install libDjvuLibre sources into subfolder "djvulibre"
|
||||
* install CREngine sources into subfolder "kpvcrlib/crengine"
|
||||
* install LuaJit sources into subfolder "luajit-2.0"
|
||||
* install popen_noshell sources into subfolder "popen-noshell"
|
||||
|
||||
* automatically fetch thirdparty sources with Makefile:
|
||||
* make sure you have patch, wget, unzip, git and svn installed
|
||||
* run `make fetchthirdparty`.
|
||||
|
||||
* adapt Makefile to your needs
|
||||
|
||||
* run `make thirdparty`. This will build MuPDF (plus the libraries it depends
|
||||
on), libDjvuLibre, CREngine and Lua.
|
||||
|
||||
* run `make`. This will build the kpdfview application
|
||||
|
||||
|
||||
Running
|
||||
=======
|
||||
|
||||
The user interface (or what's there yet) is scripted in Lua. See "reader.lua".
|
||||
It uses the Linux feature to run scripts by using a corresponding line at its
|
||||
start.
|
||||
|
||||
So you might just call that script. Note that the script and the kpdfview
|
||||
binary currently must be in the same directory.
|
||||
|
||||
You would then just call reader.lua, giving the document file path, or any
|
||||
directory path, as its first argument. Run reader.lua without arguments to see
|
||||
usage notes. The reader.lua script can also show a file chooser: it will do
|
||||
this when you call it with a directory (instead of a file) as first argument.
|
||||
|
||||
|
||||
Device emulation
|
||||
================
|
||||
|
||||
The code also features a device emulation. You need SDL headers and library
|
||||
for this. It allows to develop on a standard PC and saves precious development
|
||||
time. It might also compose the most unfriendly desktop PDF reader, depending
|
||||
on your view.
|
||||
|
||||
If you are using Fedora Core Linux, do `yum install SDL SDL-devel`.
|
||||
If you are using Ubuntu, install `libsdl-dev1.2` package.
|
||||
|
||||
To build in "emulation mode", you need to run make like this:
|
||||
make clean cleanthirdparty
|
||||
EMULATE_READER=1 make thirdparty kpdfview
|
||||
|
||||
And run the emulator like this:
|
||||
```
|
||||
./reader.lua /PATH/TO/PDF.pdf
|
||||
```
|
||||
|
||||
or:
|
||||
```
|
||||
./reader.lua /ANY/PATH
|
||||
```
|
||||
|
||||
By default emulation will provide DXG resolution of 824*1200. It can be
|
||||
specified at compile time, this is example for Kindle 3:
|
||||
|
||||
```
|
||||
EMULATE_READER_W=600 EMULATE_READER_H=800 EMULATE_READER=1 make kpdfview
|
||||
```
|
||||
|
@ -1,166 +0,0 @@
|
||||
-- Copyright (c) 2009 Aleksey Cheusov <vle@gmx.net>
|
||||
--
|
||||
-- Permission is hereby granted, free of charge, to any person obtaining
|
||||
-- a copy of this software and associated documentation files (the
|
||||
-- "Software"), to deal in the Software without restriction, including
|
||||
-- without limitation the rights to use, copy, modify, merge, publish,
|
||||
-- distribute, sublicense, and/or sell copies of the Software, and to
|
||||
-- permit persons to whom the Software is furnished to do so, subject to
|
||||
-- the following conditions:
|
||||
--
|
||||
-- The above copyright notice and this permission notice shall be
|
||||
-- included in all copies or substantial portions of the Software.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
-- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
-- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
-- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
-- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
-- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
local type, pairs, ipairs, io, os = type, pairs, ipairs, io, os
|
||||
|
||||
module ("alt_getopt")
|
||||
|
||||
local function convert_short2long (opts)
|
||||
local i = 1
|
||||
local len = #opts
|
||||
local ret = {}
|
||||
|
||||
for short_opt, accept_arg in opts:gmatch("(%w)(:?)") do
|
||||
ret[short_opt]=#accept_arg
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
local function exit_with_error (msg, exit_status)
|
||||
io.stderr:write (msg)
|
||||
os.exit (exit_status)
|
||||
end
|
||||
|
||||
local function err_unknown_opt (opt)
|
||||
exit_with_error ("Unknown option `-" ..
|
||||
(#opt > 1 and "-" or "") .. opt .. "'\n", 1)
|
||||
end
|
||||
|
||||
local function canonize (options, opt)
|
||||
if not options [opt] then
|
||||
err_unknown_opt (opt)
|
||||
end
|
||||
|
||||
while type (options [opt]) == "string" do
|
||||
opt = options [opt]
|
||||
|
||||
if not options [opt] then
|
||||
err_unknown_opt (opt)
|
||||
end
|
||||
end
|
||||
|
||||
return opt
|
||||
end
|
||||
|
||||
function get_ordered_opts (arg, sh_opts, long_opts)
|
||||
local i = 1
|
||||
local count = 1
|
||||
local opts = {}
|
||||
local optarg = {}
|
||||
|
||||
local options = convert_short2long (sh_opts)
|
||||
for k,v in pairs (long_opts) do
|
||||
options [k] = v
|
||||
end
|
||||
|
||||
while i <= #arg do
|
||||
local a = arg [i]
|
||||
|
||||
if a == "--" then
|
||||
i = i + 1
|
||||
break
|
||||
|
||||
elseif a == "-" then
|
||||
break
|
||||
|
||||
elseif a:sub (1, 2) == "--" then
|
||||
local pos = a:find ("=", 1, true)
|
||||
|
||||
if pos then
|
||||
local opt = a:sub (3, pos-1)
|
||||
|
||||
opt = canonize (options, opt)
|
||||
|
||||
if options [opt] == 0 then
|
||||
exit_with_error ("Bad usage of option `" .. a .. "'\n", 1)
|
||||
end
|
||||
|
||||
optarg [count] = a:sub (pos+1)
|
||||
opts [count] = opt
|
||||
else
|
||||
local opt = a:sub (3)
|
||||
|
||||
opt = canonize (options, opt)
|
||||
|
||||
if options [opt] == 0 then
|
||||
opts [count] = opt
|
||||
else
|
||||
if i == #arg then
|
||||
exit_with_error ("Missed value for option `" .. a .. "'\n", 1)
|
||||
end
|
||||
|
||||
optarg [count] = arg [i+1]
|
||||
opts [count] = opt
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
count = count + 1
|
||||
|
||||
elseif a:sub (1, 1) == "-" then
|
||||
local j
|
||||
for j=2,a:len () do
|
||||
local opt = canonize (options, a:sub (j, j))
|
||||
|
||||
if options [opt] == 0 then
|
||||
opts [count] = opt
|
||||
count = count + 1
|
||||
elseif a:len () == j then
|
||||
if i == #arg then
|
||||
exit_with_error ("Missed value for option `-" .. opt .. "'\n", 1)
|
||||
end
|
||||
|
||||
optarg [count] = arg [i+1]
|
||||
opts [count] = opt
|
||||
i = i + 1
|
||||
count = count + 1
|
||||
break
|
||||
else
|
||||
optarg [count] = a:sub (j+1)
|
||||
opts [count] = opt
|
||||
count = count + 1
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
break
|
||||
end
|
||||
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
return opts,i,optarg
|
||||
end
|
||||
|
||||
function get_opts (arg, sh_opts, long_opts)
|
||||
local ret = {}
|
||||
|
||||
local opts,optind,optarg = get_ordered_opts (arg, sh_opts, long_opts)
|
||||
for i,v in ipairs (opts) do
|
||||
if optarg [i] then
|
||||
ret [v] = optarg [i]
|
||||
else
|
||||
ret [v] = 1
|
||||
end
|
||||
end
|
||||
|
||||
return ret,optind
|
||||
end
|
@ -0,0 +1,24 @@
|
||||
-- return the current battery level
|
||||
function BatteryLevel()
|
||||
local p = io.popen("gasgauge-info -s 2> /dev/null", "r") -- io.popen() _never_ fails!
|
||||
local battery = p:read("*a") or "?"
|
||||
if battery == "" then battery = "?" end
|
||||
p:close()
|
||||
return string.gsub(battery, "[\n\r]+", "")
|
||||
end
|
||||
|
||||
-- log battery level in "batlog.txt" file, blevent can be any string
|
||||
function logBatteryLevel(blevent)
|
||||
if not G_battery_logging then return end
|
||||
|
||||
local file = io.open("batlog.txt", "a+")
|
||||
if file then
|
||||
if file:seek("end") == 0 then -- write the header only once
|
||||
file:write("DATE\t\tTIME\t\tBATTERY\tEVENT\n")
|
||||
end
|
||||
file:write(string.format("%s\t%s\t%s\t%s\n",
|
||||
os.date("%d-%b-%y"), os.date("%T"),
|
||||
BatteryLevel(), blevent or "RUNNING"))
|
||||
file:close()
|
||||
end
|
||||
end
|
@ -0,0 +1,94 @@
|
||||
-- framebuffer update policy state:
|
||||
DRCOUNT = 5
|
||||
-- default to full refresh on every page turn
|
||||
DRCOUNTMAX = 0
|
||||
|
||||
-- zoom state:
|
||||
DGLOBALZOOM = 1.0
|
||||
DGLOBALZOOM_ORIG = 1.0
|
||||
DGLOBALZOOM_MODE = -1 -- ZOOM_FIT_TO_PAGE
|
||||
|
||||
DGLOBALROTATE = 0
|
||||
|
||||
-- gamma setting:
|
||||
DGLOBALGAMMA = 1.0 -- GAMMA_NO_GAMMA
|
||||
|
||||
-- DjVu page rendering mode (used in djvu.c:drawPage())
|
||||
-- See comments in djvureader.lua:DJVUReader:select_render_mode()
|
||||
DRENDER_MODE = 0 -- COLOUR
|
||||
|
||||
-- set panning distance
|
||||
DSHIFT_X = 100
|
||||
DSHIFT_Y = 50
|
||||
-- step to change zoom manually, default = 16%
|
||||
DSTEP_MANUAL_ZOOM = 16
|
||||
DPAN_BY_PAGE = false -- using shift_[xy] or width/height
|
||||
DPAN_MARGIN = 5 -- horizontal margin for two-column zoom (in pixels)
|
||||
DPAN_OVERLAP_VERTICAL = 30
|
||||
|
||||
-- tile cache configuration:
|
||||
DCACHE_MAX_MEMSIZE = 1024*1024*5 -- 5MB tile cache
|
||||
DCACHE_MAX_TTL = 20 -- time to live
|
||||
|
||||
-- renderer cache size
|
||||
DCACHE_DOCUMENT_SIZE = 1024*1024*8 -- FIXME random, needs testing
|
||||
|
||||
-- default value for battery level logging
|
||||
DBATTERY_LOGGING = false
|
||||
|
||||
-- background colour: 8 = gray, 0 = white, 15 = black
|
||||
DBACKGROUND_COLOR = 8
|
||||
|
||||
-- delay for info messages in ms
|
||||
DINFO_NODELAY=0
|
||||
DINFO_DELAY=1500
|
||||
|
||||
-- toggle defaults
|
||||
DUNIREADER_SHOW_OVERLAP_ENABLE = true
|
||||
DUNIREADER_SHOW_LINKS_ENABLE = true
|
||||
DUNIREADER_COMICS_MODE_ENABLE = true
|
||||
DUNIREADER_RTL_MODE_ENABLE = false
|
||||
DUNIREADER_PAGE_MODE_ENABLE = false
|
||||
|
||||
DDJVUREADER_SHOW_OVERLAP_ENABLE = true
|
||||
DDJVUREADER_SHOW_LINKS_ENABLE = false
|
||||
DDJVUREADER_COMICS_MODE_ENABLE = true
|
||||
DDJVUREADER_RTL_MODE_ENABLE = false
|
||||
DDJVUREADER_PAGE_MODE_ENABLE = false
|
||||
|
||||
DKOPTREADER_SHOW_OVERLAP_ENABLE = true
|
||||
DKOPTREADER_SHOW_LINKS_ENABLE = false
|
||||
DKOPTREADER_COMICS_MODE_ENABLE = false
|
||||
DKOPTREADER_RTL_MODE_ENABLE = false
|
||||
DKOPTREADER_PAGE_MODE_ENABLE = false
|
||||
|
||||
DPICVIEWER_SHOW_OVERLAP_ENABLE = false
|
||||
DPICVIEWER_SHOW_LINKS_ENABLE = false
|
||||
DPICVIEWER_COMICS_MODE_ENABLE = true
|
||||
DPICVIEWER_RTL_MODE_ENABLE = false
|
||||
DPICVIEWER_PAGE_MODE_ENABLE = false
|
||||
|
||||
-- koptreader config defaults
|
||||
DKOPTREADER_CONFIG_FONT_SIZE = 1.0 -- range from 0.1 to 3.0
|
||||
DKOPTREADER_CONFIG_TEXT_WRAP = 1 -- 1 = on, 0 = off
|
||||
DKOPTREADER_CONFIG_TRIM_PAGE = 1 -- 1 = auto, 0 = manual
|
||||
DKOPTREADER_CONFIG_DETECT_INDENT = 1 -- 1 = enable, 0 = disable
|
||||
DKOPTREADER_CONFIG_DEFECT_SIZE = 1.0 -- range from 0.0 to 3.0
|
||||
DKOPTREADER_CONFIG_PAGE_MARGIN = 0.06 -- range from 0.0 to 1.0
|
||||
DKOPTREADER_CONFIG_LINE_SPACING = 1.2 -- range from 0.5 to 2.0
|
||||
DKOPTREADER_CONFIG_WORD_SAPCING = 0.15 -- range from 0.05 to 0.5
|
||||
DKOPTREADER_CONFIG_MULTI_THREADS = 1 -- 1 = on, 0 = off
|
||||
DKOPTREADER_CONFIG_RENDER_QUALITY = 1.0 -- range from 0.5 to 1.0
|
||||
DKOPTREADER_CONFIG_AUTO_STRAIGHTEN = 0 -- range from 0 to 10
|
||||
DKOPTREADER_CONFIG_JUSTIFICATION = -1 -- -1 = auto, 0 = left, 1 = center, 2 = right, 3 = full
|
||||
DKOPTREADER_CONFIG_MAX_COLUMNS = 2 -- range from 1 to 4
|
||||
DKOPTREADER_CONFIG_CONTRAST = 1.0 -- range from 0.2 to 2.0
|
||||
DKOPTREADER_CONFIG_SCREEN_ROTATION = 0 -- 0, 90, 180, 270 degrees
|
||||
|
||||
-- supported extensions
|
||||
DPDFREADER_EXT = ";pdf;xps;cbz;zip;"
|
||||
DDJVUREADER_EXT = ";djvu;"
|
||||
DPDFREFLOW_EXT = ";pdf;"
|
||||
DDJVUREFLOW_EXT = ";djvu;"
|
||||
DCREREADER_EXT = ";epub;txt;rtf;htm;html;mobi;prc;azw;fb2;chm;pdb;doc;tcr;zip;" -- seems to accept pdb-files for PalmDoc only
|
||||
DPICVIEWER_EXT = ";jpg;jpeg;"
|
@ -0,0 +1,105 @@
|
||||
/*
|
||||
extr: Extract attachments from PDF file
|
||||
|
||||
Usage: extr /dir/file.pdf pageno
|
||||
Returns 0 if one or more attachments saved, otherwise returns non-zero.
|
||||
Prints the number of saved attachments on stdout.
|
||||
Attachments are saved in /dir directory with the appropriate filenames.
|
||||
|
||||
Copyright (C) 2012 Tigran Aivazian <tigran@bibles.org.uk>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "mupdf-internal.h"
|
||||
#include <libgen.h>
|
||||
|
||||
static pdf_document *doc;
|
||||
|
||||
void dump_stream(int i, FILE *fout)
|
||||
{
|
||||
fz_stream *stm = pdf_open_stream(doc, i, 0);
|
||||
static unsigned char buf[8192];
|
||||
while (1) {
|
||||
int n = fz_read(stm, buf, sizeof buf);
|
||||
if (n == 0) break;
|
||||
fwrite(buf, 1, n, fout);
|
||||
}
|
||||
fz_close(stm);
|
||||
}
|
||||
|
||||
/* returns the number of attachments saved */
|
||||
int save_attachments(int pageno, char *targetdir)
|
||||
{
|
||||
pdf_page *page = pdf_load_page(doc, pageno-1);
|
||||
pdf_annot *annot;
|
||||
int saved_count = 0;
|
||||
|
||||
for (annot = page->annots; annot ; annot = annot->next) {
|
||||
pdf_obj *fs_obj = pdf_dict_gets(annot->obj, "FS");
|
||||
if (fs_obj) {
|
||||
pdf_obj *ef_obj;
|
||||
char *name = basename(strdup(pdf_to_str_buf(pdf_dict_gets(fs_obj, "F"))));
|
||||
ef_obj = pdf_dict_gets(fs_obj, "EF");
|
||||
if (ef_obj) {
|
||||
pdf_obj *f_obj = pdf_dict_gets(ef_obj, "F");
|
||||
if (f_obj && pdf_is_indirect(f_obj)) {
|
||||
static char pathname[PATH_MAX];
|
||||
sprintf(pathname, "%s/%s", targetdir, name);
|
||||
FILE *fout = fopen(pathname, "w");
|
||||
if (!fout) {
|
||||
fprintf(stderr, "extr: cannot write to file %s\n", pathname);
|
||||
exit(1);
|
||||
}
|
||||
dump_stream(pdf_to_num(f_obj), fout);
|
||||
fclose(fout);
|
||||
saved_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return saved_count;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int saved = 0;
|
||||
|
||||
if (argc != 3) {
|
||||
printf("Usage: extr file.pdf pageno\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char *filename = strdup(argv[1]);
|
||||
char *dir = dirname(strdup(filename));
|
||||
int pageno = atoi(argv[2]);
|
||||
|
||||
fz_context *ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED);
|
||||
if (!ctx) {
|
||||
fprintf(stderr, "extr: cannot create context\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fz_var(doc);
|
||||
fz_try(ctx) {
|
||||
doc = pdf_open_document(ctx, filename);
|
||||
saved = save_attachments(pageno, dir);
|
||||
}
|
||||
fz_catch(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
printf("%d\n", saved);
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,374 @@
|
||||
require "rendertext"
|
||||
require "keys"
|
||||
require "graphics"
|
||||
require "font"
|
||||
require "inputbox"
|
||||
require "dialog"
|
||||
require "filesearcher"
|
||||
require "settings"
|
||||
require "dialog"
|
||||
|
||||
FileHistory = {
|
||||
title_H = 40, -- title height
|
||||
spacing = 36, -- spacing between lines
|
||||
foot_H = 28, -- foot height
|
||||
margin_H = 10, -- horisontal margin
|
||||
|
||||
-- state buffer
|
||||
history_files = {},
|
||||
files = {},
|
||||
result = {},
|
||||
items = 0,
|
||||
page = 0,
|
||||
current = 1,
|
||||
oldcurrent = 1,
|
||||
commands = nil,
|
||||
}
|
||||
|
||||
function FileHistory:init(history_path)
|
||||
self:setPath("history")
|
||||
-- to initialize only once
|
||||
if not self.commands then self:addAllCommands() end
|
||||
end
|
||||
|
||||
function FileHistory:setPath(newPath)
|
||||
self.path = newPath
|
||||
self:readDir("-c ")
|
||||
self.items = #self.files
|
||||
if self.items == 1 then
|
||||
return nil
|
||||
end
|
||||
self.page = 1
|
||||
self.current = 1
|
||||
return true
|
||||
end
|
||||
|
||||
function FileHistory:readDir(order_criteria)
|
||||
self.history_files = {}
|
||||
self.files = {}
|
||||
local p = io.popen("ls "..order_criteria.."-1 "..self.path)
|
||||
for f in p:lines() do
|
||||
-- insert history files
|
||||
table.insert(self.history_files, {dir=self.path, name=f})
|
||||
-- and corresponding path & file items
|
||||
table.insert(self.files, {dir=HistoryToPath(f), name=HistoryToName(f)})
|
||||
end
|
||||
p:close()
|
||||
end
|
||||
|
||||
function FileHistory:setSearchResult(keywords)
|
||||
self.result = {}
|
||||
if keywords == "" or keywords == " " then
|
||||
-- show all history
|
||||
self.result = self.files
|
||||
else
|
||||
-- select history files with keywords in the filename
|
||||
for __,f in pairs(self.files) do
|
||||
if string.find(string.lower(f.name), keywords) then
|
||||
table.insert(self.result,f)
|
||||
end
|
||||
end
|
||||
end
|
||||
self.keywords = keywords
|
||||
self.items = #self.result
|
||||
self.page = 1
|
||||
self.current = 1
|
||||
return self.items
|
||||
end
|
||||
|
||||
function FileHistory:prevItem()
|
||||
if self.current == 1 then
|
||||
if self.page > 1 then
|
||||
self.current = self.perpage
|
||||
self.page = self.page - 1
|
||||
self.pagedirty = true
|
||||
end
|
||||
else
|
||||
self.current = self.current - 1
|
||||
self.markerdirty = true
|
||||
end
|
||||
end
|
||||
|
||||
function FileHistory:nextItem()
|
||||
if self.current == self.perpage then
|
||||
if self.page < (self.items / self.perpage) then
|
||||
self.current = 1
|
||||
self.page = self.page + 1
|
||||
self.pagedirty = true
|
||||
end
|
||||
else
|
||||
if self.page ~= math.floor(self.items / self.perpage) + 1
|
||||
or self.current + (self.page-1)*self.perpage < self.items then
|
||||
self.current = self.current + 1
|
||||
self.markerdirty = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function FileHistory:addAllCommands()
|
||||
self.commands = Commands:new{}
|
||||
self.commands:add(KEY_H, nil, "H",
|
||||
"show help page",
|
||||
function(self)
|
||||
HelpPage:show(0, G_height, self.commands)
|
||||
self.pagedirty = true
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_FW_UP, nil, "joypad up",
|
||||
"previous item",
|
||||
function(self)
|
||||
self:prevItem()
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_FW_DOWN, nil, "joypad down",
|
||||
"next item",
|
||||
function(self)
|
||||
self:nextItem()
|
||||
end
|
||||
)
|
||||
-- NuPogodi, 01.10.12: fast jumps to items at positions 10, 20, .. 90, 0% within the list
|
||||
local numeric_keydefs, i = {}
|
||||
for i=1, 10 do numeric_keydefs[i]=Keydef:new(KEY_1+i-1, nil, tostring(i%10)) end
|
||||
self.commands:addGroup("[1, 2 .. 9, 0]", numeric_keydefs,
|
||||
"item at position 0%, 10% .. 90%, 100%",
|
||||
function(self)
|
||||
local target_item = math.ceil(self.items * (keydef.keycode-KEY_1) / 9)
|
||||
self.current, self.page, self.markerdirty, self.pagedirty =
|
||||
gotoTargetItem(target_item, self.items, self.current, self.page, self.perpage)
|
||||
end
|
||||
)
|
||||
self.commands:add({KEY_PGFWD, KEY_LPGFWD}, nil, ">",
|
||||
"next page",
|
||||
function(self)
|
||||
if self.page < (self.items / self.perpage) then
|
||||
if self.current + self.page*self.perpage > self.items then
|
||||
self.current = self.items - self.page*self.perpage
|
||||
end
|
||||
self.page = self.page + 1
|
||||
self.pagedirty = true
|
||||
else
|
||||
self.current = self.items - (self.page-1)*self.perpage
|
||||
self.markerdirty = true
|
||||
end
|
||||
end
|
||||
)
|
||||
self.commands:add({KEY_PGBCK, KEY_LPGBCK}, nil, "<",
|
||||
"previous page",
|
||||
function(self)
|
||||
if self.page > 1 then
|
||||
self.page = self.page - 1
|
||||
self.pagedirty = true
|
||||
else
|
||||
self.current = 1
|
||||
self.markerdirty = true
|
||||
end
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_G, nil, "G", -- NuPogodi, 01.10.12: goto page No.
|
||||
"goto page",
|
||||
function(self)
|
||||
local n = math.ceil(self.items / self.perpage)
|
||||
local page = NumInputBox:input(G_height-100, 100, "Page:", "current page "..self.page.." of "..n, true)
|
||||
if pcall(function () page = math.floor(page) end) -- convert string to number
|
||||
and page ~= self.page and page > 0 and page <= n then
|
||||
self.page = page
|
||||
if self.current + (page-1)*self.perpage > self.items then
|
||||
self.current = self.items - (page-1)*self.perpage
|
||||
end
|
||||
end
|
||||
self.pagedirty = true
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_FW_RIGHT, nil, "joypad right",
|
||||
"document details",
|
||||
function(self)
|
||||
local file_entry = self.result[self.perpage*(self.page-1)+self.current]
|
||||
if file_entry.name == ".." then
|
||||
warningUnsupportedFunction()
|
||||
return
|
||||
end -- do not show details
|
||||
FileInfo:show(file_entry.dir,file_entry.name)
|
||||
self.pagedirty = true
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_S, nil, "S",
|
||||
"invoke search inputbox",
|
||||
function(self)
|
||||
-- NuPogodi, 30.09.12: be sure that something is found
|
||||
local old_keywords = self.keywords
|
||||
local old_data = self.result
|
||||
local old_page, old_current = self.page, self.current
|
||||
self.keywords = InputBox:input(G_height - 100, 100, "Search:", old_keywords)
|
||||
if self.keywords then
|
||||
self:setSearchResult(self.keywords)
|
||||
else
|
||||
self.keywords = old_keywords
|
||||
end
|
||||
if #self.result < 1 then
|
||||
InfoMessage:inform("No search hits ", DINFO_DELAY, 1, MSG_WARN)
|
||||
-- restoring the original data
|
||||
self.result = old_data
|
||||
self.items = #self.result
|
||||
self.keywords = old_keywords
|
||||
self.page = old_page
|
||||
self.current = old_current
|
||||
end
|
||||
self.pagedirty = true
|
||||
end
|
||||
)
|
||||
self.commands:add({KEY_F, KEY_AA}, nil, "F, Aa",
|
||||
"change font faces",
|
||||
function(self)
|
||||
Font:chooseFonts()
|
||||
self.pagedirty = true
|
||||
end
|
||||
)
|
||||
self.commands:add({KEY_ENTER, KEY_FW_PRESS}, nil, "Enter",
|
||||
"open selected document",
|
||||
function(self)
|
||||
local file_entry = self.result[self.perpage*(self.page-1)+self.current]
|
||||
file_full_path = file_entry.dir .. "/" .. file_entry.name
|
||||
if FileExists(file_full_path) then
|
||||
openFile(file_full_path)
|
||||
--[[
|
||||
-- reset height and item index if screen has been rotated
|
||||
-- not needed because we force portrait mode on document close
|
||||
local item_no = self.perpage * (self.page - 1) + self.current
|
||||
self.perpage = math.floor(G_height / self.spacing) - 2
|
||||
self.current = item_no % self.perpage
|
||||
self.page = math.floor(item_no / self.perpage) + 1
|
||||
--]]
|
||||
self:init()
|
||||
self:setSearchResult(self.keywords)
|
||||
self.pagedirty = true
|
||||
else
|
||||
InfoMessage:inform("File does not exist ", DINFO_DELAY, 1, MSG_ERROR)
|
||||
end
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_DEL, nil, "Del",
|
||||
"delete history entry",
|
||||
function(self)
|
||||
local file_entry = self.result[self.perpage*(self.page-1)+self.current]
|
||||
local file_to_del = file_entry.dir .. "/" .. file_entry.name
|
||||
if InfoMessage.InfoMethod[MSG_CONFIRM] == 0 then -- silent regime
|
||||
os.remove(DocToHistory(file_to_del))
|
||||
self:init()
|
||||
self:setSearchResult(self.keywords)
|
||||
else
|
||||
InfoMessage:inform("Press 'Y' to confirm ", DINFO_NODELAY, 0, MSG_CONFIRM)
|
||||
if ReturnKey() == KEY_Y then
|
||||
os.remove(DocToHistory(file_to_del))
|
||||
self:init()
|
||||
self:setSearchResult(self.keywords)
|
||||
end
|
||||
end
|
||||
self.pagedirty = true
|
||||
if self.items == 0 then
|
||||
return "break"
|
||||
end
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_SPACE, nil, "Space",
|
||||
"refresh page manually",
|
||||
function(self)
|
||||
self.pagedirty = true
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_BACK, nil, "Back",
|
||||
"back",
|
||||
function(self)
|
||||
return "break"
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
function FileHistory:choose(keywords)
|
||||
self.perpage = math.floor(G_height / self.spacing) - 2
|
||||
self.pagedirty = true
|
||||
self.markerdirty = false
|
||||
|
||||
-- NuPogodi, 30.09.12: immediate quit (no redraw), if empty
|
||||
if self:setSearchResult(keywords) < 1 then
|
||||
InfoMessage:inform("No reading history ", DINFO_DELAY, 1, MSG_WARN)
|
||||
return nil
|
||||
end
|
||||
|
||||
while true do
|
||||
local cface = Font:getFace("cfont", 22)
|
||||
local tface = Font:getFace("tfont", 25)
|
||||
local fface = Font:getFace("ffont", 16)
|
||||
|
||||
if self.pagedirty then
|
||||
self.markerdirty = true
|
||||
fb.bb:paintRect(0, 0, G_width, G_height, 0)
|
||||
-- draw header
|
||||
local header = "Last Documents ("..tostring(self.items).." items)"
|
||||
if self.keywords ~= "" and self.keywords ~= " " then
|
||||
--header = header .. " (filter: \'" .. string.upper(self.keywords) .. "\')"
|
||||
header = "Search Results for \'"..string.upper(self.keywords).."\'"
|
||||
end
|
||||
DrawTitle(header,self.margin_H,0,self.title_H,3,tface)
|
||||
-- draw found results
|
||||
for c = 1, self.perpage do
|
||||
local i = (self.page - 1) * self.perpage + c
|
||||
if i <= self.items then
|
||||
y = self.title_H + (self.spacing * c) + 4
|
||||
local ftype = string.lower(string.match(self.result[i].name, ".+%.([^.]+)") or "")
|
||||
DrawFileItem(self.result[i].name,self.margin_H,y,ftype)
|
||||
end
|
||||
end
|
||||
-- draw footer
|
||||
all_page = math.ceil(self.items/self.perpage)
|
||||
DrawFooter("Page "..self.page.." of "..all_page,fface,self.foot_H)
|
||||
end
|
||||
|
||||
if self.markerdirty then
|
||||
if not self.pagedirty then
|
||||
if self.oldcurrent > 0 then
|
||||
y = self.title_H + (self.spacing * self.oldcurrent) + 12
|
||||
fb.bb:paintRect(self.margin_H, y, G_width - 2 * self.margin_H, 3, 0)
|
||||
fb:refresh(1, self.margin_H, y, G_width - 2 * self.margin_H, 3)
|
||||
end
|
||||
end
|
||||
-- draw new marker line
|
||||
y = self.title_H + (self.spacing * self.current) + 12
|
||||
fb.bb:paintRect(self.margin_H, y, G_width - 2 * self.margin_H, 3, 15)
|
||||
if not self.pagedirty then
|
||||
fb:refresh(1, self.margin_H, y, G_width - 2 * self.margin_H, 3)
|
||||
end
|
||||
self.oldcurrent = self.current
|
||||
self.markerdirty = false
|
||||
end
|
||||
|
||||
if self.pagedirty then
|
||||
fb:refresh(0)
|
||||
self.pagedirty = false
|
||||
end
|
||||
|
||||
local ev = input.saveWaitForEvent()
|
||||
ev.code = adjustKeyEvents(ev)
|
||||
if ev.type == EV_KEY and ev.value ~= EVENT_VALUE_KEY_RELEASE then
|
||||
keydef = Keydef:new(ev.code, getKeyModifier())
|
||||
Debug("key pressed: "..tostring(keydef))
|
||||
|
||||
command = self.commands:getByKeydef(keydef)
|
||||
if command ~= nil then
|
||||
Debug("command to execute: "..tostring(command))
|
||||
ret_code = command.func(self, keydef)
|
||||
else
|
||||
Debug("command not found: "..tostring(command))
|
||||
end
|
||||
|
||||
if ret_code == "break" then
|
||||
break
|
||||
end
|
||||
|
||||
if self.selected_item ~= nil then
|
||||
Debug("# selected "..self.selected_item)
|
||||
return self.selected_item
|
||||
end
|
||||
end -- if
|
||||
end -- while true
|
||||
return nil
|
||||
end
|
@ -0,0 +1,252 @@
|
||||
require "rendertext"
|
||||
require "keys"
|
||||
require "graphics"
|
||||
require "font"
|
||||
require "inputbox"
|
||||
require "dialog"
|
||||
require "settings"
|
||||
require "readerchooser"
|
||||
|
||||
FileInfo = {
|
||||
title_H = 40, -- title height
|
||||
spacing = 36, -- spacing between lines
|
||||
foot_H = 28, -- foot height
|
||||
margin_H = 10, -- horisontal margin
|
||||
-- state buffer
|
||||
pagedirty = true,
|
||||
result = {},
|
||||
commands = nil,
|
||||
items = 0,
|
||||
pathfile = "",
|
||||
}
|
||||
|
||||
function FileInfo:FileCreated(fname, attr)
|
||||
return os.date("%d %b %Y, %H:%M:%S", lfs.attributes(fname,attr))
|
||||
end
|
||||
|
||||
function FileInfo:FormatSize(size)
|
||||
if not tonumber(size) then
|
||||
return "Invalid"
|
||||
elseif size < 1024 then
|
||||
return size.." Bytes"
|
||||
elseif size < 2^20 then
|
||||
return string.format("%.2f", size/2^10).."KB ("..size.." Bytes)"
|
||||
elseif size < 2^30 then
|
||||
return string.format("%.2f", size/2^20).."MB ("..size.." Bytes)"
|
||||
else
|
||||
return string.format("%.2f", size/2^30).."GB ("..size.." Bytes)"
|
||||
end
|
||||
end
|
||||
|
||||
function FileExists(path)
|
||||
local f = io.open(path, "r")
|
||||
if f then
|
||||
f:close()
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function getUnpackedZipSize(zipfile)
|
||||
-- adding quotes allows us to avoid crash on zips which filename contains space(s)
|
||||
local cmd='unzip -l \"'..zipfile..'\" | tail -1 | sed -e "s/^ *\\([0-9][0-9]*\\) *.*/\\1/"'
|
||||
local p = io.popen(cmd, "r")
|
||||
local res = p:read("*a")
|
||||
p:close()
|
||||
res = string.gsub(res, "[\n\r]+", "")
|
||||
return tonumber(res)
|
||||
end
|
||||
|
||||
function FileInfo:formatDiskSizeInfo()
|
||||
local t, f = util.df(".")
|
||||
return self:FormatSize(f)..string.format(", %.2f", 100*f/t).."%"
|
||||
end
|
||||
|
||||
function FileInfo:getFolderContent()
|
||||
local tmp = io.popen('du -a \"'..self.pathfile..'\"', "r")
|
||||
local dirs, files, books, size, name, output, ftype, j = -1, 0, 0, 0
|
||||
for output in tmp:lines() do
|
||||
j = output:find("/")
|
||||
name = output:sub(j, -1)
|
||||
size = tonumber(output:sub(1, j-1)) -- in kB
|
||||
j = lfs.attributes(name, "mode")
|
||||
if j == "file" then
|
||||
files = files + 1
|
||||
ftype = string.match(name, ".+%.([^.]+)")
|
||||
if ftype and ReaderChooser:getReaderByType(string.lower(ftype)) then
|
||||
books = books + 1
|
||||
end
|
||||
elseif j == "directory" then
|
||||
dirs = dirs + 1
|
||||
end
|
||||
end
|
||||
tmp:close()
|
||||
-- add 2 entries; might be joined / splitted
|
||||
table.insert(self.result, {dir = "Contents", name = dirs.." sub-folder(s) / "..files.." file(s) / "..books.." book(s)"})
|
||||
table.insert(self.result, {dir = "Size", name = self:FormatSize(size*1024)})
|
||||
end
|
||||
|
||||
function FileInfo:init(path, fname)
|
||||
-- add commands only once
|
||||
if not self.commands then
|
||||
self:addAllCommands()
|
||||
end
|
||||
|
||||
if fname then
|
||||
self.pathfile = path.."/"..fname
|
||||
table.insert(self.result, {dir = "Name", name = fname} )
|
||||
self.commands:add({KEY_ENTER, KEY_FW_PRESS}, nil, "Enter",
|
||||
"open document",
|
||||
function(self)
|
||||
openFile(self.pathfile)
|
||||
self.pagedirty = true
|
||||
end)
|
||||
else
|
||||
self.pathfile = path.."/"
|
||||
-- extracting folder name
|
||||
local i, j = 0, 0
|
||||
while true do
|
||||
i = string.find(path, "/", i+1)
|
||||
if i == nil then break else j=i end
|
||||
end
|
||||
table.insert(self.result, {dir = "Name", name = path:sub(j+1,-1) } )
|
||||
table.insert(self.result, {dir = "Path", name = path:sub(1,j) } )
|
||||
self.commands:add({KEY_ENTER, KEY_FW_PRESS}, nil, "Enter",
|
||||
"goto folder",
|
||||
function(self)
|
||||
return "goto"
|
||||
end)
|
||||
end
|
||||
|
||||
local tmp, output
|
||||
if fname then -- file info
|
||||
table.insert(self.result, {dir = "Path", name = path.."/"} )
|
||||
table.insert(self.result, {dir = "Size", name = self:FormatSize(lfs.attributes(self.pathfile, "size"))} )
|
||||
-- total size of all unzipped entries for zips
|
||||
local match = string.match(fname, ".+%.([^.]+)")
|
||||
if match and string.lower(match) == "zip" then
|
||||
table.insert(self.result, {dir = "Unpacked", name = self:FormatSize(getUnpackedZipSize(self.pathfile))} )
|
||||
end
|
||||
else -- folder info
|
||||
self:getFolderContent()
|
||||
end
|
||||
|
||||
table.insert(self.result, {dir = "Free space", name = self:formatDiskSizeInfo()})
|
||||
table.insert(self.result, {dir = "Status changed", name = self:FileCreated(self.pathfile, "change")})
|
||||
table.insert(self.result, {dir = "Modified", name = self:FileCreated(self.pathfile, "modification")})
|
||||
table.insert(self.result, {dir = "Accessed", name = self:FileCreated(self.pathfile, "access")})
|
||||
|
||||
if fname then
|
||||
-- if the document was already opened
|
||||
local history = DocToHistory(self.pathfile)
|
||||
if not FileExists(history) then
|
||||
table.insert(self.result, {dir = "Last read", name = "Never"})
|
||||
else
|
||||
table.insert(self.result, {dir = "Last read", name = self:FileCreated(history, "change")})
|
||||
local ext = string.match(self.pathfile, ".+%.([^.]+)")
|
||||
local file_type = ext and ext:lower() or "txt"
|
||||
local to_search, add, factor = "[\"last_percent\"]", "%", 100
|
||||
if ReaderChooser:getReaderByType(file_type) ~= CREReader then
|
||||
to_search = "[\"last_page\"]"
|
||||
add = " pages"
|
||||
factor = 1
|
||||
end
|
||||
for line in io.lines(history) do
|
||||
if string.match(line, "%b[]") == to_search then
|
||||
local cdc = tonumber(string.match(line, "%d+")) / factor
|
||||
table.insert(self.result, {dir = "Completed", name = string.format("%d", cdc)..add })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
self.items = #self.result
|
||||
end
|
||||
|
||||
function FileInfo:show(path, name)
|
||||
-- at first, one has to test whether the file still exists or not: necessary for last documents
|
||||
if name and not FileExists(path.."/"..name) then return nil end
|
||||
-- then goto main functions
|
||||
self:init(path,name)
|
||||
-- local variables
|
||||
local cface, lface, tface, fface, width, xrcol, c, dy, ev, keydef, ret_code
|
||||
while true do
|
||||
if self.pagedirty then
|
||||
-- refresh the fonts, if not yet defined or updated via 'F'
|
||||
cface = Font:getFace("cfont", 22)
|
||||
lface = Font:getFace("tfont", 22)
|
||||
tface = Font:getFace("tfont", 25)
|
||||
fface = Font:getFace("ffont", 16)
|
||||
-- drawing
|
||||
fb.bb:paintRect(0, 0, G_width, G_height, 0)
|
||||
DrawTitle(name and "Document Information" or "Folder Information", self.margin_H, 0, self.title_H, 3, tface)
|
||||
-- now calculating xrcol-position for the right column
|
||||
width = 0
|
||||
for c = 1, self.items do
|
||||
width = math.max(sizeUtf8Text(0, G_width, lface, self.result[c].dir, true).x, width)
|
||||
end
|
||||
xrcol = self.margin_H + width + 25
|
||||
dy = 5 -- to store the y-position correction 'cause of the multiline drawing
|
||||
for c = 1, self.items do
|
||||
y = self.title_H + self.spacing * c + dy
|
||||
renderUtf8Text(fb.bb, self.margin_H, y, lface, self.result[c].dir, true)
|
||||
dy = dy + renderUtf8Multiline(fb.bb, xrcol, y, cface, self.result[c].name, true,
|
||||
G_width - self.margin_H - xrcol, 1.65).y - y
|
||||
end
|
||||
-- NuPogodi, 29.09.12: restored footer > to see 'Press H for help'
|
||||
DrawFooter("Page 1 of 1",fface,self.foot_H)
|
||||
fb:refresh(0)
|
||||
self.pagedirty = false
|
||||
end
|
||||
-- waiting for user's commands
|
||||
ev = input.saveWaitForEvent()
|
||||
ev.code = adjustKeyEvents(ev)
|
||||
if ev.type == EV_KEY and ev.value ~= EVENT_VALUE_KEY_RELEASE then
|
||||
keydef = Keydef:new(ev.code, getKeyModifier())
|
||||
command = self.commands:getByKeydef(keydef)
|
||||
if command ~= nil then ret_code = command.func(self, keydef) end
|
||||
if ret_code == "break" or ret_code == "goto" then break end
|
||||
end -- if ev.type
|
||||
end -- while true
|
||||
self.pagedirty = true
|
||||
self.result = {}
|
||||
return ret_code
|
||||
end
|
||||
|
||||
function FileInfo:addAllCommands()
|
||||
self.commands = Commands:new{}
|
||||
self.commands:add(KEY_SPACE, nil, "Space",
|
||||
"refresh page manually",
|
||||
function(self)
|
||||
self.pagedirty = true
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_H,nil,"H",
|
||||
"show help page",
|
||||
function(self)
|
||||
HelpPage:show(0, G_height, self.commands)
|
||||
self.pagedirty = true
|
||||
end
|
||||
)
|
||||
self.commands:add({KEY_F, KEY_AA}, nil, "F, Aa",
|
||||
"change font faces",
|
||||
function(self)
|
||||
Font:chooseFonts()
|
||||
self.pagedirty = true
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_L, nil, "L",
|
||||
"last documents",
|
||||
function(self)
|
||||
FileHistory:init()
|
||||
FileHistory:choose("")
|
||||
self.pagedirty = true
|
||||
end
|
||||
)
|
||||
self.commands:add({KEY_BACK, KEY_FW_LEFT}, nil, "Back, FW-Left",
|
||||
"back",
|
||||
function(self)
|
||||
return "break"
|
||||
end
|
||||
)
|
||||
end
|
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
/mnt/us/kindlepdfviewer/kpdf.sh
|
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
/mnt/us/kindlepdfviewer/kpdf.sh /mnt/us/documents
|
@ -0,0 +1,470 @@
|
||||
require "font"
|
||||
require "keys"
|
||||
require "settings"
|
||||
require "defaults"
|
||||
|
||||
KOPTOptions = {
|
||||
{
|
||||
name="font_size",
|
||||
option_text="",
|
||||
items_text={"Aa","Aa","Aa","Aa","Aa","Aa","Aa","Aa","Aa","Aa"},
|
||||
text_font_size={14,16,20,23,26,30,34,38,42,46},
|
||||
current_item=nil,
|
||||
text_dirty=true,
|
||||
marker_dirty={true, true, true, true, true, true, true, true, true, true},
|
||||
values={0.2, 0.3, 0.4, 0.6, 0.8, 1.0, 1.2, 1.6, 2.2, 2.8},
|
||||
default_value=DKOPTREADER_CONFIG_FONT_SIZE,
|
||||
show = true,
|
||||
draw_index = nil,},
|
||||
{
|
||||
name="text_wrap",
|
||||
option_text="Reflow",
|
||||
items_text={"on","off"},
|
||||
current_item=nil,
|
||||
text_dirty=true,
|
||||
marker_dirty={true, true},
|
||||
values={1, 0},
|
||||
default_value=DKOPTREADER_CONFIG_TEXT_WRAP,
|
||||
show = true,
|
||||
draw_index = nil,},
|
||||
{
|
||||
name="trim_page",
|
||||
option_text="Trim Page",
|
||||
items_text={"auto","manual"},
|
||||
current_item=nil,
|
||||
text_dirty=true,
|
||||
marker_dirty={true, true},
|
||||
values={1, 0},
|
||||
default_value=DKOPTREADER_CONFIG_TRIM_PAGE,
|
||||
show = true,
|
||||
draw_index = nil,},
|
||||
{
|
||||
name="detect_indent",
|
||||
option_text="Indentation",
|
||||
items_text={"enable","disable"},
|
||||
current_item=nil,
|
||||
text_dirty=true,
|
||||
marker_dirty={true, true},
|
||||
values={1, 0},
|
||||
default_value=DKOPTREADER_CONFIG_DETECT_INDENT,
|
||||
show = false,
|
||||
draw_index = nil,},
|
||||
{
|
||||
name="defect_size",
|
||||
option_text="Defect Size",
|
||||
items_text={"small","medium","large"},
|
||||
current_item=nil,
|
||||
text_dirty=true,
|
||||
marker_dirty={true, true, true},
|
||||
values={0.5, 1.0, 2.0},
|
||||
default_value=DKOPTREADER_CONFIG_DEFECT_SIZE,
|
||||
show = true,
|
||||
draw_index = nil,},
|
||||
{
|
||||
name="page_margin",
|
||||
option_text="Page Margin",
|
||||
items_text={"small","medium","large"},
|
||||
current_item=nil,
|
||||
text_dirty=true,
|
||||
marker_dirty={true, true, true},
|
||||
values={0.02, 0.06, 0.10},
|
||||
default_value=DKOPTREADER_CONFIG_PAGE_MARGIN,
|
||||
show = true,
|
||||
draw_index = nil,},
|
||||
{
|
||||
name="line_spacing",
|
||||
option_text="Line Spacing",
|
||||
items_text={"small","medium","large"},
|
||||
current_item=nil,
|
||||
text_dirty=true,
|
||||
marker_dirty={true, true, true},
|
||||
values={1.0, 1.2, 1.4},
|
||||
default_value=DKOPTREADER_CONFIG_LINE_SPACING,
|
||||
show = true,
|
||||
draw_index = nil,},
|
||||
{
|
||||
name="word_spacing",
|
||||
option_text="Word Spacing",
|
||||
items_text={"small","medium","large"},
|
||||
current_item=nil,
|
||||
text_dirty=true,
|
||||
marker_dirty={true, true, true},
|
||||
values={0.05, 0.15, 0.375},
|
||||
default_value=DKOPTREADER_CONFIG_WORD_SAPCING,
|
||||
show = true,
|
||||
draw_index = nil,},
|
||||
{
|
||||
name="multi_threads",
|
||||
option_text="Multi Threads",
|
||||
items_text={"on","off"},
|
||||
current_item=nil,
|
||||
text_dirty=true,
|
||||
marker_dirty={true, true},
|
||||
values={1, 0},
|
||||
default_value=DKOPTREADER_CONFIG_MULTI_THREADS,
|
||||
show = true,
|
||||
draw_index = nil,},
|
||||
{
|
||||
name="quality",
|
||||
option_text="Render Quality",
|
||||
items_text={"low","medium","high"},
|
||||
current_item=nil,
|
||||
text_dirty=true,
|
||||
marker_dirty={true, true, true},
|
||||
values={0.5, 0.8, 1.0},
|
||||
default_value=DKOPTREADER_CONFIG_RENDER_QUALITY,
|
||||
show = true,
|
||||
draw_index = nil,},
|
||||
{
|
||||
name="auto_straighten",
|
||||
option_text="Auto Straighten",
|
||||
items_text={"0","5","10"},
|
||||
current_item=nil,
|
||||
text_dirty=true,
|
||||
marker_dirty={true, true, true},
|
||||
values={0, 5, 10},
|
||||
default_value=DKOPTREADER_CONFIG_AUTO_STRAIGHTEN,
|
||||
show = true,
|
||||
draw_index = nil,},
|
||||
{
|
||||
name="justification",
|
||||
option_text="Justification",
|
||||
items_text={"auto","left","center","right","full"},
|
||||
current_item=nil,
|
||||
text_dirty=true,
|
||||
marker_dirty={true, true, true, true, true},
|
||||
values={-1,0,1,2,3},
|
||||
default_value=DKOPTREADER_CONFIG_JUSTIFICATION,
|
||||
show = true,
|
||||
draw_index = nil,},
|
||||
{
|
||||
name="max_columns",
|
||||
option_text="Columns",
|
||||
items_text={"1","2","3","4"},
|
||||
current_item=nil,
|
||||
text_dirty=true,
|
||||
marker_dirty={true, true, true, true},
|
||||
values={1,2,3,4},
|
||||
default_value=DKOPTREADER_CONFIG_MAX_COLUMNS,
|
||||
show = true,
|
||||
draw_index = nil,},
|
||||
{
|
||||
name="contrast",
|
||||
option_text="Contrast",
|
||||
items_text={"lightest","lighter","default","darker","darkest"},
|
||||
current_item=nil,
|
||||
text_dirty=true,
|
||||
marker_dirty={true, true, true, true, true},
|
||||
values={2.0, 1.5, 1.0, 0.5, 0.2},
|
||||
default_value=DKOPTREADER_CONFIG_CONTRAST,
|
||||
show = true,
|
||||
draw_index = nil,},
|
||||
{
|
||||
name="screen_rotation",
|
||||
option_text="Screen Rotation",
|
||||
items_text={"0","90","180","270"},
|
||||
current_item=nil,
|
||||
text_dirty=true,
|
||||
marker_dirty={true, true, true, true},
|
||||
values={0, 90, 180, 270},
|
||||
default_value=DKOPTREADER_CONFIG_SCREEN_ROTATION,
|
||||
show = true,
|
||||
draw_index = nil,},
|
||||
}
|
||||
|
||||
KOPTConfig = {
|
||||
-- UI constants
|
||||
WIDTH = 550, -- width
|
||||
HEIGHT = nil, -- height, updated in run time
|
||||
MARGIN_BOTTOM = 25, -- window bottom margin
|
||||
OPTION_PADDING_T = 60, -- option top padding
|
||||
OPTION_PADDING_H = 70, -- option horizontal padding
|
||||
OPTION_SPACING_V = 30, -- options vertical spacing
|
||||
NAME_ALIGN_RIGHT = 0.28, -- align name right to the window width
|
||||
ITEM_ALIGN_LEFT = 0.30, -- align item left to the window width
|
||||
ITEM_SPACING_H = 10, -- items horisontal spacing
|
||||
OPT_NAME_FONT_SIZE = 20, -- option name font size
|
||||
OPT_ITEM_FONT_SIZE = 16, -- option item font size
|
||||
|
||||
-- last pos text is drawn
|
||||
text_pos = 0,
|
||||
-- current selected option
|
||||
current_option = 1,
|
||||
-- config change
|
||||
config_change = false,
|
||||
confirm_change = false,
|
||||
|
||||
-- reader object
|
||||
koptreader = nil
|
||||
}
|
||||
|
||||
function KOPTConfig:drawBox(xpos, ypos, width, hight, bgcolor, bdcolor)
|
||||
-- draw dialog border
|
||||
local r = 6 -- round corners
|
||||
fb.bb:paintRect(xpos, ypos+r, width, hight - 2*r, bgcolor)
|
||||
blitbuffer.paintBorder(fb.bb, xpos, ypos, width, r, r, bgcolor, r)
|
||||
blitbuffer.paintBorder(fb.bb, xpos, ypos+hight-2*r, width, r, r, bgcolor, r)
|
||||
end
|
||||
|
||||
function KOPTConfig:drawOptionName(xpos, ypos, option_index, text, font_face, redraw)
|
||||
local width = self.WIDTH
|
||||
local xpos, ypos = xpos+self.OPTION_PADDING_H+self.NAME_ALIGN_RIGHT*(width-2*self.OPTION_PADDING_H), ypos+self.OPTION_PADDING_T
|
||||
if KOPTOptions[option_index].text_dirty or redraw then
|
||||
--Debug("drawing option name:", KOPTOptions[option_index].option_text)
|
||||
local text_len = sizeUtf8Text(0, G_width, font_face, text, true).x
|
||||
local draw_index = KOPTOptions[option_index].draw_index
|
||||
renderUtf8Text(fb.bb, xpos-text_len, ypos+self.OPTION_SPACING_V*(draw_index-1), font_face, text, true)
|
||||
end
|
||||
end
|
||||
|
||||
function KOPTConfig:drawOptionItem(xpos, ypos, option_index, item_index, text, font_face, redraw, refresh)
|
||||
self.text_pos = (item_index == 1) and 0 or self.text_pos
|
||||
local width = self.WIDTH
|
||||
local offset = self.OPTION_PADDING_H+self.ITEM_ALIGN_LEFT*(width-2*self.OPTION_PADDING_H)
|
||||
local item_x_offset = (KOPTOptions[option_index].option_text == "") and self.OPTION_PADDING_H or offset
|
||||
local draw_index = KOPTOptions[option_index].draw_index
|
||||
local xpos = xpos+item_x_offset+self.ITEM_SPACING_H*(item_index-1)+self.text_pos
|
||||
local ypos = ypos+self.OPTION_PADDING_T+self.OPTION_SPACING_V*(draw_index-1)
|
||||
|
||||
if KOPTOptions[option_index].text_font_size then
|
||||
font_face = Font:getFace("cfont", KOPTOptions[option_index].text_font_size[item_index])
|
||||
end
|
||||
if KOPTOptions[option_index].text_dirty or redraw then
|
||||
--Debug("drawing option:", KOPTOptions[option_index].option_text, "item:", text)
|
||||
renderUtf8Text(fb.bb, xpos, ypos, font_face, text, true)
|
||||
end
|
||||
|
||||
local text_len = sizeUtf8Text(0, G_width, font_face, text, true).x
|
||||
self.text_pos = self.text_pos + text_len
|
||||
|
||||
if KOPTOptions[option_index].marker_dirty[item_index] or redraw then
|
||||
--Debug("drawing option:", KOPTOptions[option_index].option_text, "marker:", text)
|
||||
if item_index == KOPTOptions[option_index].current_item then
|
||||
fb.bb:paintRect(xpos, ypos+5, text_len, 3,(option_index == self.current_option) and 15 or 6)
|
||||
if refresh then
|
||||
fb:refresh(1, xpos, ypos+5, text_len, 3)
|
||||
end
|
||||
else
|
||||
fb.bb:paintRect(xpos, ypos+5, text_len, 3, 3)
|
||||
if refresh then
|
||||
fb:refresh(1, xpos, ypos+5, text_len, 3)
|
||||
end
|
||||
end
|
||||
KOPTOptions[option_index].marker_dirty[item_index] = false
|
||||
end
|
||||
end
|
||||
|
||||
function KOPTConfig:drawOptions(xpos, ypos, name_font, item_font, redraw, refresh)
|
||||
local width, height = self.WIDTH, self.HEIGHT
|
||||
for i=1,#KOPTOptions do
|
||||
if KOPTOptions[i].show then
|
||||
self:drawOptionName(xpos, ypos, i, KOPTOptions[i].option_text, name_font, redraw)
|
||||
for j=1,#KOPTOptions[i].items_text do
|
||||
self:drawOptionItem(xpos, ypos, i, j, KOPTOptions[i].items_text[j], item_font, redraw, refresh)
|
||||
end
|
||||
KOPTOptions[i].text_dirty = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function KOPTConfig:makeDefault(configurable)
|
||||
local draw_index = 1
|
||||
self.HEIGHT = self.OPTION_PADDING_T
|
||||
self.current_option = 1
|
||||
for i=1,#KOPTOptions do
|
||||
-- update draw index of each option in run time
|
||||
if KOPTOptions[i].show then
|
||||
KOPTOptions[i].draw_index = draw_index
|
||||
draw_index = draw_index + 1
|
||||
end
|
||||
-- update window height
|
||||
if KOPTOptions[i].show then
|
||||
self.HEIGHT = self.HEIGHT + self.OPTION_SPACING_V
|
||||
end
|
||||
-- make each option and marker dirty
|
||||
KOPTOptions[i].text_dirty = true
|
||||
for j=1,#KOPTOptions[i].items_text do
|
||||
KOPTOptions[i].marker_dirty[j] = true
|
||||
end
|
||||
-- make current index according to configurable table
|
||||
local option = KOPTOptions[i].name
|
||||
local val = configurable[option]
|
||||
local min_diff = math.abs(val - KOPTOptions[i].values[1])
|
||||
KOPTOptions[i].current_item = 1
|
||||
for index, val_ in pairs(KOPTOptions[i].values) do
|
||||
if val == val_ then
|
||||
KOPTOptions[i].current_item = index
|
||||
break
|
||||
else
|
||||
diff = math.abs(val - val_)
|
||||
if diff <= min_diff then
|
||||
min_diff = diff
|
||||
KOPTOptions[i].current_item = index
|
||||
end
|
||||
end
|
||||
end -- for index
|
||||
end -- for i
|
||||
end
|
||||
|
||||
function KOPTConfig:reconfigure(configurable)
|
||||
for i=1,#KOPTOptions do
|
||||
option = KOPTOptions[i].name
|
||||
configurable[option] = KOPTOptions[i].values[KOPTOptions[i].current_item]
|
||||
end
|
||||
end
|
||||
|
||||
function KOPTConfig:config(reader)
|
||||
self.koptreader = reader
|
||||
|
||||
self:makeDefault(self.koptreader.configurable)
|
||||
self:addAllCommands()
|
||||
|
||||
local name_font = Font:getFace("tfont", self.OPT_NAME_FONT_SIZE)
|
||||
local item_font = Font:getFace("cfont", self.OPT_ITEM_FONT_SIZE)
|
||||
|
||||
-- base window coordinates
|
||||
local width, height = self.WIDTH, self.HEIGHT
|
||||
local topleft_x, topleft_y = (fb.bb:getWidth()-width)/2, fb.bb:getHeight()-self.MARGIN_BOTTOM-height
|
||||
local botleft_x, botleft_y = topleft_x, topleft_y+height
|
||||
|
||||
self:drawBox(topleft_x, topleft_y, width, height, 3, 15)
|
||||
self:drawOptions(topleft_x, topleft_y, name_font, item_font, true, false)
|
||||
fb:refresh(1, topleft_x, topleft_y, width, height)
|
||||
|
||||
local ev, keydef, command, ret_code
|
||||
while true do
|
||||
self:reconfigure(self.koptreader.configurable)
|
||||
|
||||
if self.config_change and self.confirm_change then
|
||||
self.koptreader:redrawWithoutPrecache()
|
||||
self:drawBox(topleft_x, topleft_y, width, height, 3, 15)
|
||||
self:drawOptions(topleft_x, topleft_y, name_font, item_font, true, false)
|
||||
fb:refresh(1, topleft_x, topleft_y, width, height)
|
||||
self.config_change = false
|
||||
self.confirm_change = false
|
||||
end
|
||||
self:drawOptions(topleft_x, topleft_y, name_font, item_font, false, true)
|
||||
|
||||
ev = input.saveWaitForEvent()
|
||||
ev.code = adjustKeyEvents(ev)
|
||||
if ev.type == EV_KEY and ev.value ~= EVENT_VALUE_KEY_RELEASE then
|
||||
keydef = Keydef:new(ev.code, getKeyModifier())
|
||||
Debug("key pressed: "..tostring(keydef))
|
||||
command = self.commands:getByKeydef(keydef)
|
||||
if command ~= nil then
|
||||
Debug("command to execute: "..tostring(command))
|
||||
ret_code = command.func(self, keydef)
|
||||
else
|
||||
Debug("command not found: "..tostring(command))
|
||||
end
|
||||
if ret_code == "break" then
|
||||
ret_code = nil
|
||||
return nil
|
||||
end
|
||||
end -- if
|
||||
end -- while
|
||||
end
|
||||
|
||||
-- add available commands
|
||||
function KOPTConfig:addAllCommands()
|
||||
self.commands = Commands:new{}
|
||||
self.commands:add(KEY_FW_DOWN, nil, "joypad down",
|
||||
"next item",
|
||||
function(self)
|
||||
local last_option = self.current_option
|
||||
repeat
|
||||
self.current_option = (self.current_option + #KOPTOptions + 1)%#KOPTOptions
|
||||
self.current_option = (self.current_option == 0) and #KOPTOptions or self.current_option
|
||||
until KOPTOptions[self.current_option].show
|
||||
|
||||
last_option_item = KOPTOptions[last_option].current_item
|
||||
KOPTOptions[last_option].marker_dirty[last_option_item] = true
|
||||
current_option_item = KOPTOptions[self.current_option].current_item
|
||||
KOPTOptions[self.current_option].marker_dirty[current_option_item] = true
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_FW_UP, nil, "joypad up",
|
||||
"previous item",
|
||||
function(self)
|
||||
local last_option = self.current_option
|
||||
repeat
|
||||
self.current_option = (self.current_option + #KOPTOptions - 1)%#KOPTOptions
|
||||
self.current_option = (self.current_option == 0) and #KOPTOptions or self.current_option
|
||||
until KOPTOptions[self.current_option].show
|
||||
|
||||
last_option_item = KOPTOptions[last_option].current_item
|
||||
KOPTOptions[last_option].marker_dirty[last_option_item] = true
|
||||
current_option_item = KOPTOptions[self.current_option].current_item
|
||||
KOPTOptions[self.current_option].marker_dirty[current_option_item] = true
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_FW_LEFT, nil, "joypad left",
|
||||
"last item",
|
||||
function(self)
|
||||
local last_item = KOPTOptions[self.current_option].current_item
|
||||
local item_count = #KOPTOptions[self.current_option].items_text
|
||||
local current_item = (KOPTOptions[self.current_option].current_item + item_count - 1)%item_count
|
||||
current_item = (current_item == 0) and item_count or current_item
|
||||
KOPTOptions[self.current_option].current_item = current_item
|
||||
|
||||
KOPTOptions[self.current_option].marker_dirty[last_item] = true
|
||||
KOPTOptions[self.current_option].marker_dirty[current_item] = true
|
||||
self.config_change = true
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_FW_RIGHT, nil, "joypad right",
|
||||
"next item",
|
||||
function(self)
|
||||
local last_item = KOPTOptions[self.current_option].current_item
|
||||
local item_count = #KOPTOptions[self.current_option].items_text
|
||||
local current_item = (KOPTOptions[self.current_option].current_item + item_count + 1)%item_count
|
||||
current_item = (current_item == 0) and item_count or current_item
|
||||
KOPTOptions[self.current_option].current_item = current_item
|
||||
|
||||
KOPTOptions[self.current_option].marker_dirty[last_item] = true
|
||||
KOPTOptions[self.current_option].marker_dirty[current_item] = true
|
||||
self.config_change = true
|
||||
end
|
||||
)
|
||||
self.commands:add({KEY_F,KEY_AA,KEY_BACK}, nil, "Back",
|
||||
"back",
|
||||
function(self)
|
||||
return "break"
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_FW_PRESS, nil, "joypad press",
|
||||
"preview",
|
||||
function(self)
|
||||
self.confirm_change = true
|
||||
if KOPTOptions[self.current_option].name == "trim_page" then
|
||||
local option = KOPTOptions[self.current_option]
|
||||
local trim_mode = option.current_item
|
||||
if option.items_text[trim_mode] == 'manual' then
|
||||
self:modBBox(self.koptreader)
|
||||
self.config_change = true
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
function KOPTConfig:modBBox(koptreader)
|
||||
-- save variables that will be changed in modBBox
|
||||
local orig_globalzoom = koptreader.globalzoom
|
||||
local orig_dest_x = koptreader.dest_x
|
||||
local orig_dest_y = koptreader.dest_y
|
||||
local orig_offset_x = koptreader.offset_x
|
||||
local orig_offset_y = koptreader.offset_y
|
||||
|
||||
koptreader:showOrigPage()
|
||||
|
||||
koptreader:modBBox()
|
||||
|
||||
-- restore variables changed in modBBox
|
||||
koptreader.globalzoom = orig_globalzoom
|
||||
koptreader.dest_x = orig_dest_x
|
||||
koptreader.dest_y = orig_dest_y
|
||||
koptreader.offset_x = orig_offset_x
|
||||
koptreader.offset_y = orig_offset_y
|
||||
|
||||
end
|
@ -0,0 +1,278 @@
|
||||
/*
|
||||
KindlePDFViewer: a KOPTContext abstraction
|
||||
Copyright (C) 2012 Huang Xin <chrox.huang@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "koptcontext.h"
|
||||
|
||||
static int newKOPTContext(lua_State *L) {
|
||||
int trim = 1;
|
||||
int wrap = 1;
|
||||
int indent = 1;
|
||||
int rotate = 0;
|
||||
int columns = 2;
|
||||
int offset_x = 0;
|
||||
int offset_y = 0;
|
||||
int dev_width = 600;
|
||||
int dev_height = 800;
|
||||
int page_width = 600;
|
||||
int page_height = 800;
|
||||
int straighten = 0;
|
||||
int justification = -1;
|
||||
int read_max_width = 3000;
|
||||
int read_max_height = 4000;
|
||||
|
||||
double zoom = 1.0;
|
||||
double margin = 0.06;
|
||||
double quality = 1.0;
|
||||
double contrast = 1.0;
|
||||
double defect_size = 1.0;
|
||||
double line_spacing = 1.2;
|
||||
double word_spacing = 1.375;
|
||||
double shrink_factor = 0.9;
|
||||
|
||||
uint8_t *data = NULL;
|
||||
BBox bbox = {0, 0, 0, 0};
|
||||
WILLUSBITMAP *src;
|
||||
int precache = 0;
|
||||
|
||||
KOPTContext *kc = (KOPTContext*) lua_newuserdata(L, sizeof(KOPTContext));
|
||||
|
||||
kc->trim = trim;
|
||||
kc->wrap = wrap;
|
||||
kc->indent = indent;
|
||||
kc->rotate = rotate;
|
||||
kc->columns = columns;
|
||||
kc->offset_x = offset_x;
|
||||
kc->offset_y = offset_y;
|
||||
kc->dev_width = dev_width;
|
||||
kc->dev_height = dev_height;
|
||||
kc->page_width = page_width;
|
||||
kc->page_height = page_height;
|
||||
kc->straighten = straighten;
|
||||
kc->justification = justification;
|
||||
kc->read_max_width = read_max_width;
|
||||
kc->read_max_height = read_max_height;
|
||||
|
||||
kc->zoom = zoom;
|
||||
kc->margin = margin;
|
||||
kc->quality = quality;
|
||||
kc->contrast = contrast;
|
||||
kc->defect_size = defect_size;
|
||||
kc->line_spacing = line_spacing;
|
||||
kc->word_spacing = word_spacing;
|
||||
kc->shrink_factor = shrink_factor;
|
||||
|
||||
kc->data = data;
|
||||
kc->bbox = bbox;
|
||||
kc->src = src;
|
||||
kc->precache = precache;
|
||||
|
||||
luaL_getmetatable(L, "koptcontext");
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int kcSetBBox(lua_State *L) {
|
||||
KOPTContext *kc = (KOPTContext*) luaL_checkudata(L, 1, "koptcontext");
|
||||
kc->bbox.x0 = luaL_checknumber(L, 2);
|
||||
kc->bbox.y0 = luaL_checknumber(L, 3);
|
||||
kc->bbox.x1 = luaL_checknumber(L, 4);
|
||||
kc->bbox.y1 = luaL_checknumber(L, 5);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kcSetTrim(lua_State *L) {
|
||||
KOPTContext *kc = (KOPTContext*) luaL_checkudata(L, 1, "koptcontext");
|
||||
kc->trim = luaL_checkint(L, 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kcGetTrim(lua_State *L) {
|
||||
KOPTContext *kc = (KOPTContext*) luaL_checkudata(L, 1, "koptcontext");
|
||||
lua_pushinteger(L, kc->trim);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int kcSetWrap(lua_State *L) {
|
||||
KOPTContext *kc = (KOPTContext*) luaL_checkudata(L, 1, "koptcontext");
|
||||
kc->wrap = luaL_checkint(L, 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kcSetIndent(lua_State *L) {
|
||||
KOPTContext *kc = (KOPTContext*) luaL_checkudata(L, 1, "koptcontext");
|
||||
kc->indent = luaL_checkint(L, 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kcSetRotate(lua_State *L) {
|
||||
KOPTContext *kc = (KOPTContext*) luaL_checkudata(L, 1, "koptcontext");
|
||||
kc->rotate = luaL_checkint(L, 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kcSetColumns(lua_State *L) {
|
||||
KOPTContext *kc = (KOPTContext*) luaL_checkudata(L, 1, "koptcontext");
|
||||
kc->columns = luaL_checkint(L, 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kcSetOffset(lua_State *L) {
|
||||
KOPTContext *kc = (KOPTContext*) luaL_checkudata(L, 1, "koptcontext");
|
||||
kc->offset_x = luaL_checkint(L, 2);
|
||||
kc->offset_y = luaL_checkint(L, 3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kcGetOffset(lua_State *L) {
|
||||
KOPTContext *kc = (KOPTContext*) luaL_checkudata(L, 1, "koptcontext");
|
||||
lua_pushinteger(L, kc->offset_x);
|
||||
lua_pushinteger(L, kc->offset_y);
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int kcSetDeviceDim(lua_State *L) {
|
||||
KOPTContext *kc = (KOPTContext*) luaL_checkudata(L, 1, "koptcontext");
|
||||
kc->dev_width = luaL_checkint(L, 2);
|
||||
kc->dev_height = luaL_checkint(L, 3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kcGetPageDim(lua_State *L) {
|
||||
KOPTContext *kc = (KOPTContext*) luaL_checkudata(L, 1, "koptcontext");
|
||||
lua_pushinteger(L, kc->page_width);
|
||||
lua_pushinteger(L, kc->page_height);
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int kcSetStraighten(lua_State *L) {
|
||||
KOPTContext *kc = (KOPTContext*) luaL_checkudata(L, 1, "koptcontext");
|
||||
kc->straighten = luaL_checkint(L, 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kcSetJustification(lua_State *L) {
|
||||
KOPTContext *kc = (KOPTContext*) luaL_checkudata(L, 1, "koptcontext");
|
||||
kc->justification = luaL_checkint(L, 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kcSetZoom(lua_State *L) {
|
||||
KOPTContext *kc = (KOPTContext*) luaL_checkudata(L, 1, "koptcontext");
|
||||
kc->zoom = luaL_checknumber(L, 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kcGetZoom(lua_State *L) {
|
||||
KOPTContext *kc = (KOPTContext*) luaL_checkudata(L, 1, "koptcontext");
|
||||
lua_pushnumber(L, kc->zoom);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int kcSetMargin(lua_State *L) {
|
||||
KOPTContext *kc = (KOPTContext*) luaL_checkudata(L, 1, "koptcontext");
|
||||
kc->margin = luaL_checknumber(L, 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kcSetQuality(lua_State *L) {
|
||||
KOPTContext *kc = (KOPTContext*) luaL_checkudata(L, 1, "koptcontext");
|
||||
kc->quality = luaL_checknumber(L, 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kcSetContrast(lua_State *L) {
|
||||
KOPTContext *kc = (KOPTContext*) luaL_checkudata(L, 1, "koptcontext");
|
||||
kc->contrast = luaL_checknumber(L, 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kcSetDefectSize(lua_State *L) {
|
||||
KOPTContext *kc = (KOPTContext*) luaL_checkudata(L, 1, "koptcontext");
|
||||
kc->defect_size = luaL_checknumber(L, 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kcSetLineSpacing(lua_State *L) {
|
||||
KOPTContext *kc = (KOPTContext*) luaL_checkudata(L, 1, "koptcontext");
|
||||
kc->line_spacing = luaL_checknumber(L, 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kcSetWordSpacing(lua_State *L) {
|
||||
KOPTContext *kc = (KOPTContext*) luaL_checkudata(L, 1, "koptcontext");
|
||||
kc->word_spacing = luaL_checknumber(L, 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kcSetPreCache(lua_State *L) {
|
||||
KOPTContext *kc = (KOPTContext*) luaL_checkudata(L, 1, "koptcontext");
|
||||
kc->precache = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kcIsPreCache(lua_State *L) {
|
||||
KOPTContext *kc = (KOPTContext*) luaL_checkudata(L, 1, "koptcontext");
|
||||
lua_pushinteger(L, kc->precache);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct luaL_Reg koptcontext_meth[] = {
|
||||
{"setBBox", kcSetBBox},
|
||||
{"setTrim", kcSetTrim},
|
||||
{"getTrim", kcGetTrim},
|
||||
{"setWrap", kcSetWrap},
|
||||
{"setIndent", kcSetIndent},
|
||||
{"setRotate", kcSetRotate},
|
||||
{"setColumns", kcSetColumns},
|
||||
{"setOffset", kcSetOffset},
|
||||
{"getOffset", kcGetOffset},
|
||||
{"setDeviceDim", kcSetDeviceDim},
|
||||
{"getPageDim", kcGetPageDim},
|
||||
{"setStraighten", kcSetStraighten},
|
||||
{"setJustification", kcSetJustification},
|
||||
|
||||
{"setZoom", kcSetZoom},
|
||||
{"getZoom", kcGetZoom},
|
||||
{"setMargin", kcSetMargin},
|
||||
{"setQuality", kcSetQuality},
|
||||
{"setContrast", kcSetContrast},
|
||||
{"setDefectSize", kcSetDefectSize},
|
||||
{"setLineSpacing", kcSetLineSpacing},
|
||||
{"setWordSpacing", kcSetWordSpacing},
|
||||
|
||||
{"setPreCache", kcSetPreCache},
|
||||
{"isPreCache", kcIsPreCache},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static const struct luaL_Reg koptcontext_func[] = {
|
||||
{"new", newKOPTContext},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
int luaopen_koptcontext(lua_State *L) {
|
||||
luaL_newmetatable(L, "koptcontext");
|
||||
lua_pushstring(L, "__index");
|
||||
lua_pushvalue(L, -2);
|
||||
lua_settable(L, -3);
|
||||
luaL_register(L, NULL, koptcontext_meth);
|
||||
lua_pop(L, 1);
|
||||
luaL_register(L, "KOPTContext", koptcontext_func);
|
||||
return 1;
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
KindlePDFViewer: a KOPTContext abstraction
|
||||
Copyright (C) 2012 Huang Xin <chrox.huang@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef _KOPTCONTEXT_H
|
||||
#define _KOPTCONTEXT_H
|
||||
|
||||
#include <lua.h>
|
||||
#include <lualib.h>
|
||||
#include <lauxlib.h>
|
||||
#include "koptreflow.h"
|
||||
|
||||
int luaopen_koptcontext(lua_State *L);
|
||||
#endif
|
@ -0,0 +1,614 @@
|
||||
require "unireader"
|
||||
require "inputbox"
|
||||
require "koptconfig"
|
||||
|
||||
Configurable = {}
|
||||
|
||||
function Configurable:hash(sep)
|
||||
local hash = ""
|
||||
local excluded = {multi_threads = true,}
|
||||
for key,value in pairs(self) do
|
||||
if type(value) == "number" and not excluded[key] then
|
||||
hash = hash..sep..value
|
||||
end
|
||||
end
|
||||
return hash
|
||||
end
|
||||
|
||||
function Configurable:loadDefaults()
|
||||
for i=1,#KOPTOptions do
|
||||
local key = KOPTOptions[i].name
|
||||
self[key] = KOPTOptions[i].default_value
|
||||
end
|
||||
end
|
||||
|
||||
function Configurable:loadSettings(settings, prefix)
|
||||
for key,value in pairs(self) do
|
||||
if type(value) == "number" then
|
||||
saved_value = settings:readSetting(prefix..key)
|
||||
self[key] = (saved_value == nil) and self[key] or saved_value
|
||||
--Debug("Configurable:loadSettings", "key", key, "saved value", saved_value,"Configurable.key", self[key])
|
||||
end
|
||||
end
|
||||
--Debug("loaded config:", dump(Configurable))
|
||||
end
|
||||
|
||||
function Configurable:saveSettings(settings, prefix)
|
||||
for key,value in pairs(self) do
|
||||
if type(value) == "number" then
|
||||
settings:saveSetting(prefix..key, value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
KOPTReader = UniReader:new{
|
||||
configurable = {}
|
||||
}
|
||||
|
||||
function KOPTReader:makeContext()
|
||||
local kc = KOPTContext.new()
|
||||
kc:setTrim(self.configurable.trim_page)
|
||||
kc:setWrap(self.configurable.text_wrap)
|
||||
kc:setIndent(self.configurable.detect_indent)
|
||||
kc:setRotate(self.configurable.screen_rotation)
|
||||
kc:setColumns(self.configurable.max_columns)
|
||||
kc:setDeviceDim(G_width, G_height)
|
||||
kc:setStraighten(self.configurable.auto_straighten)
|
||||
kc:setJustification(self.configurable.justification)
|
||||
kc:setZoom(self.configurable.font_size)
|
||||
kc:setMargin(self.configurable.page_margin)
|
||||
kc:setQuality(self.configurable.quality)
|
||||
kc:setContrast(self.configurable.contrast)
|
||||
kc:setDefectSize(self.configurable.defect_size)
|
||||
kc:setLineSpacing(self.configurable.line_spacing)
|
||||
kc:setWordSpacing(self.configurable.word_spacing)
|
||||
|
||||
return kc
|
||||
end
|
||||
|
||||
-- open a PDF/DJVU file and its settings store
|
||||
function KOPTReader:open(filename)
|
||||
-- muPDF manages its own cache, set second parameter
|
||||
-- to the maximum size you want it to grow
|
||||
self.filename = filename
|
||||
local file_type = string.lower(string.match(filename, ".+%.([^.]+)") or "")
|
||||
|
||||
if file_type == "pdf" then
|
||||
local ok
|
||||
ok, self.doc = pcall(pdf.openDocument, filename, self.cache_document_size)
|
||||
if not ok then
|
||||
return false, self.doc -- will contain error message
|
||||
end
|
||||
if self.doc:needsPassword() then
|
||||
local password = InputBox:input(G_height-100, 100, "Pass:")
|
||||
if not password or not self.doc:authenticatePassword(password) then
|
||||
self.doc:close()
|
||||
self.doc = nil
|
||||
return false, "wrong or missing password"
|
||||
end
|
||||
-- password wrong or not entered
|
||||
end
|
||||
local ok, err = pcall(self.doc.getPages, self.doc)
|
||||
if not ok then
|
||||
-- for PDFs, they might trigger errors later when accessing page tree
|
||||
self.doc:close()
|
||||
self.doc = nil
|
||||
return false, "damaged page tree"
|
||||
end
|
||||
return true
|
||||
|
||||
elseif file_type == "djvu" then
|
||||
if not validDJVUFile(filename) then
|
||||
return false, "Not a valid DjVu file"
|
||||
end
|
||||
|
||||
local ok
|
||||
ok, self.doc = pcall(djvu.openDocument, filename, self.cache_document_size)
|
||||
if not ok then
|
||||
return ok, self.doc -- this will be the error message instead
|
||||
end
|
||||
return ok
|
||||
end
|
||||
end
|
||||
|
||||
-- draw original page
|
||||
function KOPTReader:showOrigPage()
|
||||
local no = self.pageno
|
||||
local ok, page = pcall(self.doc.openPage, self.doc, no)
|
||||
local width, height = G_width, G_height
|
||||
local pwidth, pheight = page:getSize(self.nulldc)
|
||||
if not ok then
|
||||
-- TODO: error handling
|
||||
return nil
|
||||
end
|
||||
|
||||
local dc = DrawContext.new()
|
||||
self.globalzoom = width / pwidth
|
||||
if height / pheight < self.globalzoom then
|
||||
self.globalzoom = height / pheight
|
||||
end
|
||||
dc:setZoom(self.globalzoom)
|
||||
|
||||
self.offset_x = 0
|
||||
self.offset_y = 0
|
||||
|
||||
local pagehash = no..'_orig_full_page'
|
||||
if self.cache[pagehash] ~= nil then
|
||||
page:close()
|
||||
|
||||
local bb = self.cache[pagehash].bb
|
||||
self.dest_x = 0
|
||||
self.dest_y = 0
|
||||
if bb:getWidth() < width then
|
||||
self.dest_x = (width - (bb:getWidth())) / 2
|
||||
end
|
||||
if bb:getHeight() < height then
|
||||
self.dest_y = (height - (bb:getHeight())) / 2
|
||||
end
|
||||
if self.dest_x or self.dest_y then
|
||||
fb.bb:paintRect(0, 0, width, height, DBACKGROUND_COLOR)
|
||||
end
|
||||
fb.bb:blitFrom(self.cache[pagehash].bb, self.dest_x, self.dest_y, 0, 0, width, height)
|
||||
fb:refresh(1)
|
||||
return
|
||||
end
|
||||
|
||||
local tile = { x = 0, y = 0, w = width, h = height }
|
||||
-- can we cache the full page?
|
||||
local max_cache = self.cache_max_memsize
|
||||
local fullwidth, fullheight = page:getSize(dc)
|
||||
if (fullwidth * fullheight / 2) <= max_cache then
|
||||
-- yes we can, so do this with offset 0, 0
|
||||
tile.x = 0
|
||||
tile.y = 0
|
||||
tile.w = fullwidth
|
||||
tile.h = fullheight
|
||||
else
|
||||
Debug("ERROR not enough memory in cache left, probably a bug.")
|
||||
return nil
|
||||
end
|
||||
self:cacheClaim(tile.w * tile.h / 2);
|
||||
self.cache[pagehash] = {
|
||||
x = tile.x,
|
||||
y = tile.y,
|
||||
w = tile.w,
|
||||
h = tile.h,
|
||||
ttl = self.cache_max_ttl,
|
||||
size = tile.w * tile.h / 2,
|
||||
bb = Blitbuffer.new(tile.w, tile.h)
|
||||
}
|
||||
--debug ("# new biltbuffer:"..dump(self.cache[pagehash]))
|
||||
dc:setOffset(-tile.x, -tile.y)
|
||||
Debug("rendering page", no)
|
||||
page:draw(dc, self.cache[pagehash].bb, 0, 0, self.render_mode)
|
||||
page:close()
|
||||
|
||||
local bb = self.cache[pagehash].bb
|
||||
self.dest_x = 0
|
||||
self.dest_y = 0
|
||||
if bb:getWidth() < width then
|
||||
self.dest_x = (width - (bb:getWidth())) / 2
|
||||
end
|
||||
if bb:getHeight() < height then
|
||||
self.dest_y = (height - (bb:getHeight())) / 2
|
||||
end
|
||||
if self.dest_x or self.dest_y then
|
||||
fb.bb:paintRect(0, 0, width, height, DBACKGROUND_COLOR)
|
||||
end
|
||||
fb.bb:blitFrom(bb, self.dest_x, self.dest_y, 0, 0, width, height)
|
||||
fb:refresh(1)
|
||||
end
|
||||
|
||||
function KOPTReader:drawOrCache(no, preCache)
|
||||
-- our general caching strategy is as follows:
|
||||
-- #1 goal: we must render the needed area.
|
||||
-- #2 goal: we render as much of the requested page as we can
|
||||
-- #3 goal: we render the full page
|
||||
-- #4 goal: we render next page, too. (TODO)
|
||||
|
||||
-- ideally, this should be factored out and only be called when needed (TODO)
|
||||
local ok, page = pcall(self.doc.openPage, self.doc, no)
|
||||
local width, height = G_width, G_height
|
||||
if not ok then
|
||||
-- TODO: error handling
|
||||
return nil
|
||||
end
|
||||
|
||||
local kc = self:getContext(page, no, preCache)
|
||||
self.globalzoom_mode = self.ZOOM_FIT_TO_CONTENT_WIDTH_PAN
|
||||
-- check if we have relevant cache contents
|
||||
local bbox = self.cur_bbox
|
||||
local pagehash = no..self.configurable:hash('_')..'_'..bbox.x0..'_'..bbox.y0..'_'..bbox.x1..'_'..bbox.y1
|
||||
Debug('page hash', pagehash)
|
||||
if self.cache[pagehash] ~= nil then
|
||||
-- we have something in cache
|
||||
-- requested part is within cached tile
|
||||
-- ...so properly clean page
|
||||
page:close()
|
||||
|
||||
self.min_offset_x = fb.bb:getWidth() - self.cache[pagehash].w
|
||||
self.min_offset_y = fb.bb:getHeight() - self.cache[pagehash].h
|
||||
if(self.min_offset_x > 0) then
|
||||
self.min_offset_x = 0
|
||||
end
|
||||
if(self.min_offset_y > 0) then
|
||||
self.min_offset_y = 0
|
||||
end
|
||||
|
||||
if self.offset_y <= -201253 then
|
||||
self.offset_y = self.min_offset_y
|
||||
end
|
||||
|
||||
-- offset_x_in_page & offset_y_in_page is the offset within zoomed page
|
||||
-- they are always positive.
|
||||
-- you can see self.offset_x_& self.offset_y as the offset within
|
||||
-- draw space, which includes the page. So it can be negative and positive.
|
||||
local offset_x_in_page = -self.offset_x
|
||||
local offset_y_in_page = -self.offset_y
|
||||
if offset_x_in_page < 0 then offset_x_in_page = 0 end
|
||||
if offset_y_in_page < 0 then offset_y_in_page = 0 end
|
||||
|
||||
Debug("cached page offset_x",self.offset_x,"offset_y",self.offset_y,"min_offset_x",self.min_offset_x,"min_offset_y",self.min_offset_y)
|
||||
-- ...and give it more time to live (ttl), except if we're precaching
|
||||
if not preCache then
|
||||
self.cache[pagehash].ttl = self.cache_max_ttl
|
||||
end
|
||||
-- ...and return blitbuffer plus offset into it
|
||||
self.cached_pagehash = pagehash
|
||||
self.cached_offset_x = offset_x_in_page - self.cache[pagehash].x
|
||||
self.cached_offset_y = offset_y_in_page - self.cache[pagehash].y
|
||||
return pagehash,
|
||||
offset_x_in_page - self.cache[pagehash].x,
|
||||
offset_y_in_page - self.cache[pagehash].y
|
||||
end
|
||||
|
||||
-- okay, we do not have it in cache yet.
|
||||
-- so render now.
|
||||
-- start off with the requested area
|
||||
local use_threads = self.configurable.multi_threads == 1 and true or false
|
||||
if use_threads and preCache then
|
||||
Debug("start precache on page", no)
|
||||
if self.precache_kc ~= nil then
|
||||
if self.precache_kc:isPreCache() == 1 then
|
||||
Debug("waiting threaded precache to finish.")
|
||||
return nil
|
||||
else
|
||||
Debug("threaded preCache is finished.")
|
||||
Debug("current pagehash", pagehash)
|
||||
Debug("precache pagehash", self.precache_pagehash)
|
||||
if self.precache_pagehash == pagehash then
|
||||
Debug("write cache ", self.precache_pagehash)
|
||||
return self:writeToCache(self.precache_kc, page, self.precache_pagehash, true)
|
||||
else
|
||||
self.precache_kc = nil
|
||||
self.precache_pagehash = nil
|
||||
Debug("discard cache ", self.precache_pagehash)
|
||||
return nil
|
||||
end
|
||||
end
|
||||
else
|
||||
self.precache_kc = kc
|
||||
self.precache_pagehash = pagehash
|
||||
self.precache_kc:setPreCache()
|
||||
page:reflow(self.precache_kc, self.render_mode)
|
||||
Debug("threaded preCache is returned.")
|
||||
end
|
||||
else
|
||||
if use_threads and self.precache_kc ~= nil then
|
||||
if self.precache_kc:isPreCache() == 1 and self.cache[self.cached_pagehash] then
|
||||
InfoMessage:inform("Rendering in background...", DINFO_DELAY, 1, MSG_WARN)
|
||||
return self.cached_pagehash, self.cached_offset_x, self.cached_offset_y
|
||||
elseif self.precache_kc:isPreCache() == 0 then -- cache is ready to be written
|
||||
Debug("write cache", self.precache_pagehash)
|
||||
self:writeToCache(self.precache_kc, page, self.precache_pagehash, true)
|
||||
Debug("reflow page", pagehash)
|
||||
local ok, page = pcall(self.doc.openPage, self.doc, no) -- reopen current page
|
||||
kc = self:getContext(page, no, true)
|
||||
page:reflow(kc, self.render_mode)
|
||||
return self:writeToCache(kc, page, pagehash, false)
|
||||
else
|
||||
Debug("ERROR something wrong happens .. why cached page is missing?")
|
||||
return nil
|
||||
end
|
||||
else
|
||||
--local secs, usecs = util.gettime()
|
||||
Debug("reflow page", pagehash)
|
||||
page:reflow(kc, self.render_mode)
|
||||
--local nsecs, nusecs = util.gettime()
|
||||
--local dur = (nsecs - secs) * 1000000 + nusecs - usecs
|
||||
--Debug("Reflow duration:", dur)
|
||||
--self:logReflowDuration(no, dur)
|
||||
return self:writeToCache(kc, page, pagehash, false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function KOPTReader:logReflowDuration(pageno, dur)
|
||||
local file = io.open("reflowlog.txt", "a+")
|
||||
if file then
|
||||
if file:seek("end") == 0 then -- write the header only once
|
||||
file:write("FILE\tPAGE\tDUR\n")
|
||||
end
|
||||
file:write(string.format("%s\t%s\t%s\n", self.filename, pageno, dur))
|
||||
file:close()
|
||||
end
|
||||
end
|
||||
|
||||
function KOPTReader:logMemoryUsage(pageno)
|
||||
local status_file = io.open("/proc/self/status", "r")
|
||||
local log_file = io.open("reflow_mem_log.txt", "a+")
|
||||
local data = -1
|
||||
if status_file then
|
||||
for line in status_file:lines() do
|
||||
local s, n
|
||||
s, n = line:gsub("VmData:%s-(%d+) kB", "%1")
|
||||
if n ~= 0 then data = tonumber(s) end
|
||||
if data ~= -1 then break end
|
||||
end
|
||||
status_file:close()
|
||||
end
|
||||
if log_file then
|
||||
if log_file:seek("end") == 0 then -- write the header only once
|
||||
log_file:write("PAGE\tMEM\n")
|
||||
end
|
||||
log_file:write(string.format("%s\t%s\n", pageno, data))
|
||||
log_file:close()
|
||||
end
|
||||
end
|
||||
|
||||
function KOPTReader:writeToCache(kc, page, pagehash, preCache)
|
||||
--self:logMemoryUsage(self.pageno)
|
||||
local tile = { x = 0, y = 0, w = G_width, h = G_height }
|
||||
-- can we cache the full page?
|
||||
local max_cache = self.cache_max_memsize
|
||||
local fullwidth, fullheight = kc:getPageDim()
|
||||
self.reflow_zoom = kc:getZoom()
|
||||
Debug("page::reflowPage:", "fullwidth:", fullwidth, "fullheight:", fullheight)
|
||||
|
||||
if (fullwidth * fullheight / 2) <= max_cache then
|
||||
-- yes we can, so do this with offset 0, 0
|
||||
tile.x = 0
|
||||
tile.y = 0
|
||||
tile.w = fullwidth
|
||||
tile.h = fullheight
|
||||
else
|
||||
Debug("ERROR not enough memory in cache left, reflowed page is too large.")
|
||||
return nil
|
||||
end
|
||||
Debug("cache capacity", max_cache, "cache claim", tile.w * tile.h / 2, "cache current", self.cache_current_memsize)
|
||||
if not self:cacheClaim(tile.w * tile.h / 2) then
|
||||
Debug("ERROR not enough memory in cache left, cache claim failed.")
|
||||
return nil
|
||||
else
|
||||
Debug("Cache claim succeed.")
|
||||
end
|
||||
self.cache[pagehash] = {
|
||||
x = tile.x,
|
||||
y = tile.y,
|
||||
w = tile.w,
|
||||
h = tile.h,
|
||||
ttl = self.cache_max_ttl,
|
||||
size = tile.w * tile.h / 2,
|
||||
bb = Blitbuffer.new(tile.w, tile.h)
|
||||
}
|
||||
--Debug ("new biltbuffer:"..dump(self.cache[pagehash]))
|
||||
Debug("page::drawReflowedPage", "width:", self.cache[pagehash].w, "height:", self.cache[pagehash].h)
|
||||
page:rfdraw(kc, self.cache[pagehash].bb)
|
||||
page:close()
|
||||
|
||||
if preCache then
|
||||
self.precache_kc = nil
|
||||
end
|
||||
|
||||
self.min_offset_x = fb.bb:getWidth() - self.cache[pagehash].w
|
||||
self.min_offset_y = fb.bb:getHeight() - self.cache[pagehash].h
|
||||
if(self.min_offset_x > 0) then
|
||||
self.min_offset_x = 0
|
||||
end
|
||||
if(self.min_offset_y > 0) then
|
||||
self.min_offset_y = 0
|
||||
end
|
||||
|
||||
if self.offset_y <= -201253 then
|
||||
self.offset_y = self.min_offset_y
|
||||
end
|
||||
|
||||
local offset_x_in_page = -self.offset_x
|
||||
local offset_y_in_page = -self.offset_y
|
||||
if offset_x_in_page < 0 then offset_x_in_page = 0 end
|
||||
if offset_y_in_page < 0 then offset_y_in_page = 0 end
|
||||
|
||||
-- return hash and offset within blitbuffer
|
||||
return pagehash,
|
||||
offset_x_in_page - tile.x,
|
||||
offset_y_in_page - tile.y
|
||||
end
|
||||
|
||||
-- get reflow context
|
||||
function KOPTReader:getContext(page, pnumber, preCache)
|
||||
local kc = self:makeContext()
|
||||
local pwidth, pheight = page:getSize(self.nulldc)
|
||||
local width, height = G_width, G_height
|
||||
-- rounds down pwidth and pheight to 2 decimals, because page:getUsedBBox() returns only 2 decimals.
|
||||
-- without it, later check whether to use margins will fail for some documents
|
||||
pwidth = math.floor(pwidth * 100) / 100
|
||||
pheight = math.floor(pheight * 100) / 100
|
||||
Debug("Context preCache:", preCache and "true" or "false")
|
||||
Debug("Context page::getSize",pwidth,pheight)
|
||||
|
||||
local x0, y0, x1, y1 = page:getUsedBBox()
|
||||
if x0 == 0.01 and y0 == 0.01 and x1 == -0.01 and y1 == -0.01 then
|
||||
x0 = 0
|
||||
y0 = 0
|
||||
x1 = pwidth
|
||||
y1 = pheight
|
||||
end
|
||||
if x1 == 0 then x1 = pwidth end
|
||||
if y1 == 0 then y1 = pheight end
|
||||
-- clamp to page BBox
|
||||
if x0 < 0 then x0 = 0 end
|
||||
if x1 > pwidth then x1 = pwidth end
|
||||
if y0 < 0 then y0 = 0 end
|
||||
if y1 > pheight then y1 = pheight end
|
||||
|
||||
if self.bbox.enabled then
|
||||
Debug("ORIGINAL page::getUsedBBox", x0,y0, x1,y1 )
|
||||
local bbox = self.bbox[pnumber] -- exact
|
||||
|
||||
local oddEven = self:oddEven(pnumber)
|
||||
if bbox ~= nil then
|
||||
Debug("bbox from", pnumber)
|
||||
else
|
||||
bbox = self.bbox[oddEven] -- odd/even
|
||||
end
|
||||
if bbox ~= nil then -- last used up to this page
|
||||
Debug("bbox from", oddEven)
|
||||
else
|
||||
for i = 0,pnumber do
|
||||
bbox = self.bbox[ pnumber - i ]
|
||||
if bbox ~= nil then
|
||||
Debug("bbox from", pnumber - i)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if bbox ~= nil then
|
||||
x0 = bbox["x0"]
|
||||
y0 = bbox["y0"]
|
||||
x1 = bbox["x1"]
|
||||
y1 = bbox["y1"]
|
||||
end
|
||||
end
|
||||
|
||||
Debug("Context page::getUsedBBox", x0, y0, x1, y1 )
|
||||
if kc:getTrim() == 1 then
|
||||
kc:setBBox(0, 0, pwidth, pheight)
|
||||
else
|
||||
kc:setBBox(x0, y0, x1, y1)
|
||||
end
|
||||
|
||||
self.cur_bbox = {
|
||||
["x0"] = x0,
|
||||
["y0"] = y0,
|
||||
["x1"] = x1,
|
||||
["y1"] = y1,
|
||||
}
|
||||
Debug("Context cur_bbox", self.cur_bbox)
|
||||
|
||||
return kc
|
||||
end
|
||||
|
||||
function KOPTReader:nextView()
|
||||
local pageno = self.pageno
|
||||
|
||||
Debug("nextView offset_y", self.offset_y, "min_offset_y", self.min_offset_y)
|
||||
if self.offset_y <= self.min_offset_y then
|
||||
-- hit content bottom, turn to next page top
|
||||
local numpages = self.doc:getPages()
|
||||
if pageno < numpages then
|
||||
self.offset_x = 0
|
||||
self.offset_y = 0
|
||||
end
|
||||
pageno = pageno + 1
|
||||
else
|
||||
-- goto next view of current page
|
||||
self.offset_y = self.offset_y - G_height + self.pan_overlap_vertical
|
||||
end
|
||||
|
||||
return pageno
|
||||
end
|
||||
|
||||
function KOPTReader:prevView()
|
||||
local pageno = self.pageno
|
||||
|
||||
Debug("preView offset_y", self.offset_y, "min_offset_y", self.min_offset_y)
|
||||
if self.offset_y >= 0 then
|
||||
-- hit content top, turn to previous page bottom
|
||||
if pageno > 1 then
|
||||
self.offset_x = 0
|
||||
self.offset_y = -2012534
|
||||
end
|
||||
pageno = pageno - 1
|
||||
else
|
||||
-- goto previous view of current page
|
||||
self.offset_y = self.offset_y + G_height - self.pan_overlap_vertical
|
||||
end
|
||||
|
||||
return pageno
|
||||
end
|
||||
|
||||
function KOPTReader:setDefaults()
|
||||
self.show_overlap_enable = DKOPTREADER_SHOW_OVERLAP_ENABLE
|
||||
self.show_links_enable = DKOPTREADER_SHOW_LINKS_ENABLE
|
||||
self.comics_mode_enable = DKOPTREADER_COMICS_MODE_ENABLE
|
||||
self.rtl_mode_enable = DKOPTREADER_RTL_MODE_ENABLE
|
||||
self.page_mode_enable = DKOPTREADER_PAGE_MODE_ENABLE
|
||||
end
|
||||
|
||||
-- backup global variables from UniReader
|
||||
function KOPTReader:loadSettings(filename)
|
||||
UniReader.loadSettings(self,filename)
|
||||
self.offset_y = self.settings:readSetting("kopt_offset_y") or 0
|
||||
self.configurable = Configurable
|
||||
self.configurable:loadDefaults()
|
||||
--Debug("default configurable:", dump(self.configurable))
|
||||
self.configurable:loadSettings(self.settings, 'kopt_')
|
||||
--Debug("loaded configurable:", dump(self.configurable))
|
||||
-- backup global variable that may be changed in koptreader
|
||||
self.orig_globalzoom_mode = self.settings:readSetting("globalzoom_mode") or -1
|
||||
self.orig_dbackground_color = DBACKGROUND_COLOR
|
||||
DBACKGROUND_COLOR = 0
|
||||
end
|
||||
|
||||
function KOPTReader:saveSpecialSettings()
|
||||
self.settings:saveSetting("kopt_offset_y", self.offset_y)
|
||||
self.configurable:saveSettings(self.settings, 'kopt_')
|
||||
--Debug("saved configurable:", dump(self.configurable))
|
||||
-- restore global variable from backups
|
||||
self.settings:saveSetting("globalzoom_mode", self.orig_globalzoom_mode)
|
||||
DBACKGROUND_COLOR = self.orig_dbackground_color
|
||||
end
|
||||
|
||||
function KOPTReader:init()
|
||||
self:addAllCommands()
|
||||
self:adjustCommands()
|
||||
end
|
||||
|
||||
function KOPTReader:redrawWithoutPrecache()
|
||||
self:show(self.pageno)
|
||||
end
|
||||
|
||||
function KOPTReader:adjustCommands()
|
||||
self.commands:del(KEY_A, nil,"A")
|
||||
self.commands:del(KEY_A, MOD_SHIFT, "A")
|
||||
self.commands:del(KEY_C, nil,"C")
|
||||
self.commands:del(KEY_U, nil,"U")
|
||||
self.commands:del(KEY_D, nil,"D")
|
||||
self.commands:del(KEY_D, MOD_SHIFT, "D")
|
||||
self.commands:del(KEY_S, nil,"S")
|
||||
self.commands:del(KEY_S, MOD_SHIFT, "S")
|
||||
self.commands:del(KEY_F, nil,"F")
|
||||
self.commands:del(KEY_F, MOD_SHIFT, "F")
|
||||
self.commands:del(KEY_Z, nil,"Z")
|
||||
self.commands:del(KEY_Z, MOD_ALT, "Z")
|
||||
self.commands:del(KEY_Z, MOD_SHIFT, "Z")
|
||||
self.commands:del(KEY_X, nil,"X")
|
||||
self.commands:del(KEY_X, MOD_SHIFT, "X")
|
||||
self.commands:del(KEY_N, nil,"N")
|
||||
self.commands:del(KEY_N, MOD_SHIFT, "N")
|
||||
self.commands:del(KEY_L, nil, "L")
|
||||
self.commands:del(KEY_L, MOD_SHIFT, "L")
|
||||
self.commands:del(KEY_M, nil, "M")
|
||||
self.commands:delGroup(MOD_ALT.."< >")
|
||||
self.commands:delGroup(MOD_SHIFT.."< >")
|
||||
self.commands:delGroup("vol-/+")
|
||||
self.commands:del(KEY_P, nil, "P")
|
||||
|
||||
self.commands:add({KEY_F,KEY_AA}, nil, "F",
|
||||
"change koptreader configuration",
|
||||
function(self)
|
||||
KOPTConfig:config(self)
|
||||
self:redrawCurrentPage()
|
||||
end
|
||||
)
|
||||
end
|
@ -0,0 +1,74 @@
|
||||
body { text-align: left; text-indent: 0px }
|
||||
p { text-align: justify; text-indent: 2em; margin-top:0em; margin-bottom: 0em }
|
||||
|
||||
empty-line { height: 1em }
|
||||
|
||||
hr { height: 1px; /* background-color: #808080; */ margin-top: 0.5em; margin-bottom: 0.5em }
|
||||
|
||||
sub { vertical-align: sub; font-size: 70% }
|
||||
sup { vertical-align: super; font-size: 70% }
|
||||
strong, b { font-weight: bold }
|
||||
emphasis, i { font-style: italic }
|
||||
|
||||
a { text-decoration: underline }
|
||||
a[type="note"] { vertical-align: super; font-size: 70%; text-decoration: none }
|
||||
|
||||
image { text-align: center; text-indent: 0px }
|
||||
p image { display: inline }
|
||||
|
||||
title p, subtitle p, h1 p, h2 p, h3 p, h4 p, h5 p, h6 p { text-align: center; text-indent: 0px }
|
||||
cite p, epigraph p { text-align: left; text-indent: 0px }
|
||||
v { text-align: left; text-indent: 0px }
|
||||
|
||||
stanza + stanza { margin-top: 1em; }
|
||||
stanza { margin-left: 30%; text-align: left; font-style: italic }
|
||||
poem { margin-top: 1em; margin-bottom: 1em; text-indent: 0px }
|
||||
text-author { font-weight: bold; font-style: italic; margin-left: 5%}
|
||||
epigraph { margin-left: 25%; margin-right: 1em; text-align: left; text-indent:
|
||||
1px; font-style: italic; margin-top: 15px; margin-bottom: 25px }
|
||||
cite { font-style: italic; margin-left: 5%; margin-right: 5%; text-align: justify;
|
||||
margin-top: 20px; margin-bottom: 20px }
|
||||
|
||||
title, h1, h2 { text-align: center; text-indent: 0px; font-weight: bold; hyphenate: none;
|
||||
page-break-before: always; page-break-inside: avoid; page-break-after: avoid; }
|
||||
subtitle, h3, h4, h5, h6 { text-align: center; text-indent: 0px; font-weight: bold;
|
||||
hyphenate: none; page-break-inside: avoid; page-break-after: avoid; }
|
||||
title { font-size: 110%; margin-top: 0.7em; margin-bottom: 0.5em }
|
||||
subtitle { font-style: italic; margin-top: 0.3em; margin-bottom: 0.3em }
|
||||
h1 { font-size: 150% }
|
||||
h2 { font-size: 140% }
|
||||
h3 { font-size: 130% }
|
||||
h4 { font-size: 120% }
|
||||
h5 { font-size: 110% }
|
||||
|
||||
|
||||
table { font-size: 80% }
|
||||
td, th { text-indent: 0px; padding: 3px }
|
||||
th { font-weight: bold; /* background-color: #DDD */ }
|
||||
table > caption { text-indent: 0px; padding: 4px; /* background-color: #EEE */ }
|
||||
|
||||
code, pre { display: block; white-space: pre; text-align: left;
|
||||
font-family: "Droid Sans Mono", monospace; text-align: left }
|
||||
|
||||
body[name="notes"] { font-size: 70%; }
|
||||
body[name="notes"] section title { display: run-in; text-align: left; font-size: 110%;
|
||||
font-weight: bold; page-break-before: auto; page-break-inside: auto;
|
||||
page-break-after: auto; }
|
||||
body[name="notes"] section title p { display: inline }
|
||||
|
||||
description { display: block; }
|
||||
title-info { display: block; }
|
||||
annotation { margin-left: 5%; margin-right: 5%; font-size: 80%; font-style: italic;
|
||||
text-align: justify; text-indent: 2em }
|
||||
date { display: block; font-size: 80%; font-style: italic; text-align: center }
|
||||
genre { display: none; }
|
||||
author { display: none; }
|
||||
book-title { display: none; }
|
||||
keywords { display: none; }
|
||||
lang { display: none; }
|
||||
src-lang { display: none; }
|
||||
translator { display: none; }
|
||||
document-info { display: none; }
|
||||
publish-info { display: none; }
|
||||
custom-info { display: none; }
|
||||
coverpage { display: none }
|
@ -1 +1 @@
|
||||
Subproject commit 7a73d1666538fe9dd7d84d7e18135b03c21be2ca
|
||||
Subproject commit a9516b455e811b9f57f2805d6c08b6744f797083
|
@ -1,15 +0,0 @@
|
||||
--- jcapimin.c 2012-04-04 00:02:30.000000000 +0800
|
||||
+++ jcapimin-patched.c 2012-04-04 00:02:26.000000000 +0800
|
||||
@@ -36,9 +36,9 @@
|
||||
cinfo->mem = NULL; /* so jpeg_destroy knows mem mgr not called */
|
||||
if (version != JPEG_LIB_VERSION)
|
||||
ERREXIT2(cinfo, JERR_BAD_LIB_VERSION, JPEG_LIB_VERSION, version);
|
||||
- if (structsize != SIZEOF(struct jpeg_compress_struct))
|
||||
- ERREXIT2(cinfo, JERR_BAD_STRUCT_SIZE,
|
||||
- (int) SIZEOF(struct jpeg_compress_struct), (int) structsize);
|
||||
+ /*if (structsize != SIZEOF(struct jpeg_compress_struct))*/
|
||||
+ /*ERREXIT2(cinfo, JERR_BAD_STRUCT_SIZE, */
|
||||
+ /*(int) SIZEOF(struct jpeg_compress_struct), (int) structsize);*/
|
||||
|
||||
/* For debugging purposes, we zero the whole master structure.
|
||||
* But the application has already set the err pointer, and may have set
|
@ -1,15 +0,0 @@
|
||||
--- jdapimin.c 2012-04-04 01:09:00.000000000 +0800
|
||||
+++ jdapimin-patched.c 2012-04-04 01:42:44.000000000 +0800
|
||||
@@ -36,9 +36,9 @@
|
||||
cinfo->mem = NULL; /* so jpeg_destroy knows mem mgr not called */
|
||||
if (version != JPEG_LIB_VERSION)
|
||||
ERREXIT2(cinfo, JERR_BAD_LIB_VERSION, JPEG_LIB_VERSION, version);
|
||||
- if (structsize != SIZEOF(struct jpeg_decompress_struct))
|
||||
- ERREXIT2(cinfo, JERR_BAD_STRUCT_SIZE,
|
||||
- (int) SIZEOF(struct jpeg_decompress_struct), (int) structsize);
|
||||
+ /*if (structsize != SIZEOF(struct jpeg_decompress_struct))*/
|
||||
+ /*ERREXIT2(cinfo, JERR_BAD_STRUCT_SIZE, */
|
||||
+ /*(int) SIZEOF(struct jpeg_decompress_struct), (int) structsize);*/
|
||||
|
||||
/* For debugging purposes, we zero the whole master structure.
|
||||
* But the application has already set the err pointer, and may have set
|
@ -0,0 +1,14 @@
|
||||
diff --git a/crengine/src/lvdocview.cpp b/crengine/src/lvdocview.cpp
|
||||
index e7a355a..e1178de 100755
|
||||
--- a/crengine/src/lvdocview.cpp
|
||||
+++ b/crengine/src/lvdocview.cpp
|
||||
@@ -4539,7 +4539,8 @@ void LVDocView::getCurrentPageLinks(ldomXRangeList & list) {
|
||||
if (_list[i]->getStart().getNode() == elem)
|
||||
return true; // don't add, duplicate found!
|
||||
}
|
||||
- _list.add(new ldomXRange(elem->getChildNode(0)));
|
||||
+ ldomNode * node = elem->getChildNode(0);
|
||||
+ if ( node ) _list.add(new ldomXRange(node));
|
||||
}
|
||||
return true;
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
[Actions]
|
||||
# start kindlepdfviewer with filebrowser in /mnt/us/documents
|
||||
P D = !/mnt/us/launchpad/kpdf.sh /mnt/us/documents
|
||||
P D = !/mnt/us/kindlepdfviewer/kpdf.sh /mnt/us/documents
|
||||
# start kindlepdfviewer with last document
|
||||
P P = !/mnt/us/launchpad/kpdf.sh
|
||||
P P = !/mnt/us/kindlepdfviewer/kpdf.sh
|
||||
# start kindlepdfviewer without framework in /mnt/us/documents
|
||||
P K = !/mnt/us/launchpad/kpdf.sh --framework_stop /mnt/us/documents
|
||||
P K = !/mnt/us/kindlepdfviewer/kpdf.sh --framework_stop /mnt/us/documents
|
||||
# start kindlepdfviewer without framework on last read document
|
||||
P L = !/mnt/us/launchpad/kpdf.sh --framework_stop
|
||||
P L = !/mnt/us/kindlepdfviewer/kpdf.sh --framework_stop
|
||||
# restart amazon framework - when it got irritated
|
||||
P R = !/etc/init.d/framework restart
|
||||
|
@ -0,0 +1 @@
|
||||
Subproject commit d9ed4f0b5d9212cdcc97fa432b83ba46eab21d09
|
@ -0,0 +1 @@
|
||||
Subproject commit eb6f890ebd01ee4cf4f31a66d8a946bc8dccd885
|
@ -1 +1 @@
|
||||
Subproject commit bdb6b688a238df56b2cf47fa17a08a4dd4b7a122
|
||||
Subproject commit 05219d086a5136ee19f643cf062bd5c0d3aef5d3
|
@ -0,0 +1,280 @@
|
||||
/*
|
||||
KindlePDFViewer: Picture viewer abstraction for Lua
|
||||
Copyright (C) 2012 Tigran Aivazian <tigran@bibles.org.uk>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "blitbuffer.h"
|
||||
#include "drawcontext.h"
|
||||
#include "pic.h"
|
||||
#include "pic_jpeg.h"
|
||||
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
typedef struct PicDocument {
|
||||
int width;
|
||||
int height;
|
||||
int components;
|
||||
uint8_t *image;
|
||||
} PicDocument;
|
||||
|
||||
typedef struct PicPage {
|
||||
int width;
|
||||
int height;
|
||||
uint8_t *image;
|
||||
PicDocument *doc;
|
||||
} PicPage;
|
||||
|
||||
/* Uses luminance match for approximating the human perception of colour,
|
||||
* as per http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
|
||||
* L = 0.299*Red + 0.587*Green + 0.114*Blue */
|
||||
static uint8_t *rgbToGrayscale(uint8_t *image, int width, int height)
|
||||
{
|
||||
int x, y;
|
||||
uint8_t *buf = malloc(width*height+1);
|
||||
|
||||
if (!buf) return NULL;
|
||||
|
||||
for (x=0; x<width; x++)
|
||||
for (y=0; y<height; y++) {
|
||||
int pos = 3*(x+y*width);
|
||||
buf[x+y*width] = (uint8_t)(0.299*((double)image[pos]) + 0.587*((double)image[pos+1]) + 0.114*((double)image[pos+2]));
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int openDocument(lua_State *L) {
|
||||
int width, height, components;
|
||||
const char *filename = luaL_checkstring(L, 1);
|
||||
|
||||
PicDocument *doc = (PicDocument*) lua_newuserdata(L, sizeof(PicDocument));
|
||||
luaL_getmetatable(L, "picdocument");
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
uint8_t *raw_image = jpegLoadFile(filename, &width, &height, &components);
|
||||
if (!raw_image)
|
||||
return luaL_error(L, "Cannot open jpeg file");
|
||||
|
||||
doc->image = NULL;
|
||||
if (components == 1)
|
||||
doc->image = raw_image;
|
||||
else if (components == 3) {
|
||||
uint8_t *gray_image = rgbToGrayscale(raw_image, width, height);
|
||||
free(raw_image);
|
||||
if (!gray_image)
|
||||
return luaL_error(L, "Cannot convert to grayscale");
|
||||
else
|
||||
doc->image = gray_image;
|
||||
} else {
|
||||
free(raw_image);
|
||||
return luaL_error(L, "Unsupported image format");
|
||||
}
|
||||
|
||||
doc->width = width;
|
||||
doc->height = height;
|
||||
doc->components = components;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int openPage(lua_State *L) {
|
||||
PicDocument *doc = (PicDocument*) luaL_checkudata(L, 1, "picdocument");
|
||||
PicPage *page = (PicPage*) lua_newuserdata(L, sizeof(PicPage));
|
||||
luaL_getmetatable(L, "picpage");
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
page->width = doc->width;
|
||||
page->height = doc->height;
|
||||
page->image = doc->image;
|
||||
page->doc = doc;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getNumberOfPages(lua_State *L) {
|
||||
lua_pushinteger(L, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getOriginalPageSize(lua_State *L) {
|
||||
PicDocument *doc = (PicDocument*) luaL_checkudata(L, 1, "picdocument");
|
||||
lua_pushnumber(L, doc->width);
|
||||
lua_pushnumber(L, doc->height);
|
||||
lua_pushnumber(L, doc->components);
|
||||
return 3;
|
||||
}
|
||||
|
||||
/* re-entrant */
|
||||
static int closeDocument(lua_State *L) {
|
||||
PicDocument *doc = (PicDocument*) luaL_checkudata(L, 1, "picdocument");
|
||||
if (doc->image != NULL) {
|
||||
free(doc->image);
|
||||
doc->image = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* uses very simple nearest neighbour scaling */
|
||||
static void scaleImage(uint8_t *result, uint8_t *image, int width, int height, int new_width, int new_height)
|
||||
{
|
||||
int x, y;
|
||||
|
||||
for (x=0; x<new_width; x++)
|
||||
for (y=0; y<new_height; y++)
|
||||
result[x+y*new_width] = image[(x*width/new_width) + (y*height/new_height)*width];
|
||||
}
|
||||
|
||||
static int drawPage(lua_State *L) {
|
||||
PicPage *page = (PicPage*) luaL_checkudata(L, 1, "picpage");
|
||||
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 2, "drawcontext");
|
||||
BlitBuffer *bb = (BlitBuffer*) luaL_checkudata(L, 3, "blitbuffer");
|
||||
int x_offset = MAX(0, dc->offset_x);
|
||||
int y_offset = MAX(0, dc->offset_y);
|
||||
int x, y;
|
||||
int img_width = page->width;
|
||||
int img_height = page->height;
|
||||
int img_new_width = bb->w;
|
||||
int img_new_height = bb->h;
|
||||
unsigned char adjusted_low[16], adjusted_high[16];
|
||||
int i, adjust_pixels = 0;
|
||||
|
||||
/* prepare the tables for adjusting the intensity of pixels */
|
||||
if (dc->gamma != -1.0) {
|
||||
for (i=0; i<16; i++) {
|
||||
adjusted_low[i] = MIN(15, (unsigned char)floorf(dc->gamma * (float)i));
|
||||
adjusted_high[i] = adjusted_low[i] << 4;
|
||||
}
|
||||
adjust_pixels = 1;
|
||||
}
|
||||
|
||||
uint8_t *scaled_image = malloc(img_new_width*img_new_height+1);
|
||||
if (!scaled_image)
|
||||
return 0;
|
||||
|
||||
scaleImage(scaled_image, page->image, img_width, img_height, img_new_width, img_new_height);
|
||||
|
||||
uint8_t *bbptr = bb->data;
|
||||
uint8_t *pmptr = scaled_image;
|
||||
bbptr += bb->pitch * y_offset;
|
||||
for(y = y_offset; y < img_new_height; y++) {
|
||||
for(x = x_offset/2; x < (img_new_width / 2); x++) {
|
||||
int p = x*2 - x_offset;
|
||||
unsigned char low = 15 - (pmptr[p + 1] >> 4);
|
||||
unsigned char high = 15 - (pmptr[p] >> 4);
|
||||
if (adjust_pixels)
|
||||
bbptr[x] = adjusted_high[high] | adjusted_low[low];
|
||||
else
|
||||
bbptr[x] = (high << 4) | low;
|
||||
}
|
||||
if (img_new_width & 1)
|
||||
bbptr[x] = 255 - (pmptr[x*2] & 0xF0);
|
||||
bbptr += bb->pitch;
|
||||
pmptr += img_new_width;
|
||||
}
|
||||
|
||||
free(scaled_image);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int getCacheSize(lua_State *L) {
|
||||
lua_pushnumber(L, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int cleanCache(lua_State *L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int getPageSize(lua_State *L) {
|
||||
PicPage *page = (PicPage*) luaL_checkudata(L, 1, "picpage");
|
||||
DrawContext *dc = (DrawContext*) luaL_checkudata(L, 2, "drawcontext");
|
||||
|
||||
lua_pushnumber(L, dc->zoom * page->width);
|
||||
lua_pushnumber(L, dc->zoom * page->height);
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
static int closePage(lua_State *L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* unsupported so fake it */
|
||||
static int getUsedBBox(lua_State *L) {
|
||||
lua_pushnumber(L, (double)0.01);
|
||||
lua_pushnumber(L, (double)0.01);
|
||||
lua_pushnumber(L, (double)-0.01);
|
||||
lua_pushnumber(L, (double)-0.01);
|
||||
return 4;
|
||||
}
|
||||
|
||||
static int getTableOfContent(lua_State *L) {
|
||||
lua_newtable(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct luaL_Reg pic_func[] = {
|
||||
{"openDocument", openDocument},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static const struct luaL_Reg picdocument_meth[] = {
|
||||
{"openPage", openPage},
|
||||
{"getPages", getNumberOfPages},
|
||||
{"getToc", getTableOfContent},
|
||||
{"getOriginalPageSize", getOriginalPageSize},
|
||||
{"getCacheSize", getCacheSize},
|
||||
{"close", closeDocument},
|
||||
{"cleanCache", cleanCache},
|
||||
{"__gc", closeDocument},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
static const struct luaL_Reg picpage_meth[] = {
|
||||
{"getSize", getPageSize},
|
||||
{"getUsedBBox", getUsedBBox},
|
||||
{"close", closePage},
|
||||
{"__gc", closePage},
|
||||
{"draw", drawPage},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
int luaopen_pic(lua_State *L) {
|
||||
luaL_newmetatable(L, "picdocument");
|
||||
lua_pushstring(L, "__index");
|
||||
lua_pushvalue(L, -2);
|
||||
lua_settable(L, -3);
|
||||
luaL_register(L, NULL, picdocument_meth);
|
||||
lua_pop(L, 1);
|
||||
|
||||
luaL_newmetatable(L, "picpage");
|
||||
lua_pushstring(L, "__index");
|
||||
lua_pushvalue(L, -2);
|
||||
lua_settable(L, -3);
|
||||
luaL_register(L, NULL, picpage_meth);
|
||||
lua_pop(L, 1);
|
||||
|
||||
luaL_register(L, "pic", pic_func);
|
||||
return 1;
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
KindlePDFViewer: JPEG Picture viewer abstraction for Lua
|
||||
Copyright (C) 2012 Tigran Aivazian <tigran@bibles.org.uk>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef _PIC_H
|
||||
#define _PIC_H
|
||||
|
||||
#include <lua.h>
|
||||
#include <lualib.h>
|
||||
#include <lauxlib.h>
|
||||
|
||||
int luaopen_pic(lua_State *L);
|
||||
|
||||
#endif
|
@ -0,0 +1,84 @@
|
||||
/*
|
||||
KindlePDFViewer: JPEG support for Picture Viewer module
|
||||
Copyright (C) 2012 Tigran Aivazian <tigran@bibles.org.uk>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <setjmp.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "jpeglib.h"
|
||||
|
||||
struct my_error_mgr {
|
||||
struct jpeg_error_mgr pub;
|
||||
jmp_buf setjmp_buffer;
|
||||
};
|
||||
|
||||
typedef struct my_error_mgr *my_error_ptr;
|
||||
|
||||
METHODDEF(void) my_error_exit(j_common_ptr cinfo)
|
||||
{
|
||||
my_error_ptr myerr = (my_error_ptr) cinfo->err;
|
||||
(*cinfo->err->output_message) (cinfo);
|
||||
longjmp(myerr->setjmp_buffer, 1);
|
||||
}
|
||||
|
||||
uint8_t *jpegLoadFile(const char *fname, int *width, int *height, int *components)
|
||||
{
|
||||
struct jpeg_decompress_struct cinfo;
|
||||
struct my_error_mgr jerr;
|
||||
FILE *infile;
|
||||
JSAMPARRAY buffer;
|
||||
int row_stride;
|
||||
long cont;
|
||||
JSAMPLE *image_buffer;
|
||||
|
||||
if ((infile = fopen(fname, "r")) == NULL) return NULL;
|
||||
cinfo.err = jpeg_std_error(&jerr.pub);
|
||||
jerr.pub.error_exit = my_error_exit;
|
||||
if (setjmp(jerr.setjmp_buffer)) {
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
fclose(infile);
|
||||
return NULL;
|
||||
}
|
||||
jpeg_create_decompress(&cinfo);
|
||||
jpeg_stdio_src(&cinfo, infile);
|
||||
(void) jpeg_read_header(&cinfo, TRUE);
|
||||
(void) jpeg_start_decompress(&cinfo);
|
||||
row_stride = cinfo.output_width * cinfo.output_components;
|
||||
buffer = (*cinfo.mem->alloc_sarray)
|
||||
((j_common_ptr) & cinfo, JPOOL_IMAGE, row_stride, 1);
|
||||
|
||||
image_buffer = (JSAMPLE *) malloc(cinfo.image_width*cinfo.image_height*cinfo.output_components);
|
||||
if (image_buffer == NULL) return NULL;
|
||||
*width = cinfo.image_width;
|
||||
*height = cinfo.image_height;
|
||||
|
||||
//cont = cinfo.output_height - 1;
|
||||
cont = 0;
|
||||
while (cinfo.output_scanline < cinfo.output_height) {
|
||||
(void) jpeg_read_scanlines(&cinfo, buffer, 1);
|
||||
memcpy(image_buffer + cinfo.image_width * cinfo.output_components * cont, buffer[0], row_stride);
|
||||
cont++;
|
||||
}
|
||||
|
||||
(void) jpeg_finish_decompress(&cinfo);
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
fclose(infile);
|
||||
*components = cinfo.output_components;
|
||||
return (uint8_t *)image_buffer;
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
KindlePDFViewer: Interface to JPEG module for picture viewer
|
||||
Copyright (C) 2012 Tigran Aivazian <tigran@bibles.org.uk>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef _PIC_JPEG_H
|
||||
#define _PIC_JPEG_H
|
||||
|
||||
/* each new image format must provide fmtLoadFile() function which
|
||||
* performs the following:
|
||||
* 1. Opens the file 'filename'
|
||||
* 2. Reads the image data from it into a buffer allocated with malloc()
|
||||
* 3. Fills in the image *width, *height and *components (number of bytes per pixel)
|
||||
* 4. Closes the file
|
||||
* 5. Returns the pointer to the image data
|
||||
*/
|
||||
extern uint8_t *jpegLoadFile(const char *fname, int *width, int *height, int *components);
|
||||
#endif
|
@ -0,0 +1,69 @@
|
||||
require "unireader"
|
||||
|
||||
PICViewer = UniReader:new{}
|
||||
|
||||
function PICViewer:setDefaults()
|
||||
self.show_overlap_enable = DPICVIEWER_SHOW_OVERLAP_ENABLE
|
||||
self.show_links_enable = DPICVIEWER_SHOW_LINKS_ENABLE
|
||||
self.comics_mode_enable = DPICVIEWER_COMICS_MODE_ENABLE
|
||||
self.rtl_mode_enable = DPICVIEWER_RTL_MODE_ENABLE
|
||||
self.page_mode_enable = DPICVIEWER_PAGE_MODE_ENABLE
|
||||
end
|
||||
|
||||
function PICViewer:open(filename)
|
||||
ok, self.doc = pcall(pic.openDocument, filename)
|
||||
if not ok then
|
||||
return ok, self.doc
|
||||
end
|
||||
return ok
|
||||
end
|
||||
|
||||
function PICViewer:_drawReadingInfo()
|
||||
local width = G_width
|
||||
local face = Font:getFace("rifont", 20)
|
||||
local rss, data, stack, lib, totalvm = memUsage()
|
||||
local page_width, page_height, page_components = self.doc:getOriginalPageSize()
|
||||
|
||||
-- display memory, time, battery and image info on top of page
|
||||
fb.bb:paintRect(0, 0, width, 60+6*2, 0)
|
||||
renderUtf8Text(fb.bb, 10, 15+6, face,
|
||||
"M: "..
|
||||
math.ceil( self.cache_current_memsize / 1024 ).."/"..math.ceil( self.cache_max_memsize / 1024 ).."k", true)
|
||||
local txt = os.date("%a %d %b %Y %T").." ["..BatteryLevel().."]"
|
||||
local w = sizeUtf8Text(0, width, face, txt, true).x
|
||||
renderUtf8Text(fb.bb, width - w - 10, 15+6, face, txt, true)
|
||||
renderUtf8Text(fb.bb, 10, 15+6+22, face,
|
||||
"Gm:"..string.format("%.1f",self.globalgamma)..", "..
|
||||
tostring(page_width).."x"..tostring(page_height).."x"..tostring(page_components)..
|
||||
" ("..tostring(math.ceil(page_width*page_height*page_components/1024)).."k), "..
|
||||
string.format("%.1fx", self.globalzoom), true)
|
||||
renderUtf8Text(fb.bb, 10, 15+6+44, face,
|
||||
"RSS:"..rss.." DAT:"..data.." STK:"..stack.." LIB:"..lib.." TOT:"..totalvm.."k", true)
|
||||
end
|
||||
|
||||
function PICViewer:init()
|
||||
self:addAllCommands()
|
||||
self:adjustCommands()
|
||||
end
|
||||
|
||||
function PICViewer:adjustCommands()
|
||||
self.commands:del(KEY_G, nil, "G")
|
||||
self.commands:del(KEY_T, nil, "T")
|
||||
self.commands:del(KEY_B, nil, "B")
|
||||
self.commands:del(KEY_B, MOD_ALT, "B")
|
||||
self.commands:del(KEY_B, MOD_SHIFT, "B")
|
||||
self.commands:del(KEY_R, MOD_SHIFT, "R")
|
||||
self.commands:del(KEY_DOT, nil, ".")
|
||||
self.commands:del(KEY_N, nil, "N")
|
||||
self.commands:del(KEY_L, nil, "L")
|
||||
self.commands:del(KEY_L, MOD_SHIFT, "L")
|
||||
self.commands:del(KEY_N, MOD_SHIFT, "N")
|
||||
self.commands:del(KEY_J, MOD_SHIFT,"J")
|
||||
self.commands:del(KEY_K, MOD_SHIFT,"K")
|
||||
self.commands:del(KEY_BACK, nil,"Back")
|
||||
self.commands:del(KEY_BACK, MOD_SHIFT,"Back")
|
||||
self.commands:delGroup("[1, 2 .. 9, 0]")
|
||||
self.commands:delGroup(MOD_ALT.."H/J")
|
||||
self.commands:delGroup(MOD_ALT.."K/L")
|
||||
self.commands:del(KEY_P, nil, "P")
|
||||
end
|
@ -0,0 +1,86 @@
|
||||
Index: CREDITS
|
||||
===================================================================
|
||||
--- CREDITS (revision 0)
|
||||
+++ CREDITS (working copy)
|
||||
@@ -0,0 +1 @@
|
||||
+Taken from http://code.google.com/p/popen-noshell/
|
||||
|
||||
Property changes on: CREDITS
|
||||
___________________________________________________________________
|
||||
Added: svn:keywords
|
||||
## -0,0 +1 ##
|
||||
+Id
|
||||
\ No newline at end of property
|
||||
Index: Makefile
|
||||
===================================================================
|
||||
--- Makefile (revision 0)
|
||||
+++ Makefile (working copy)
|
||||
@@ -0,0 +1,17 @@
|
||||
+SRCS=popen_noshell.c
|
||||
+
|
||||
+OBJS:=$(SRCS:%.c=%.o)
|
||||
+
|
||||
+%.o: %.c
|
||||
+ $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -c $<
|
||||
+
|
||||
+all: libpopen_noshell.a
|
||||
+
|
||||
+libpopen_noshell.a: $(OBJS)
|
||||
+ $(AR) rcs $@ $(OBJS)
|
||||
+
|
||||
+clean:
|
||||
+ rm -rf *.o
|
||||
+ rm -rf libpopen_noshell.a
|
||||
+
|
||||
+.PHONY: clean
|
||||
|
||||
Property changes on: Makefile
|
||||
___________________________________________________________________
|
||||
Added: svn:keywords
|
||||
## -0,0 +1 ##
|
||||
+Id
|
||||
\ No newline at end of property
|
||||
Index: popen_noshell.c
|
||||
===================================================================
|
||||
--- popen_noshell.c (revision 8)
|
||||
+++ popen_noshell.c (working copy)
|
||||
@@ -16,6 +16,10 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses>.
|
||||
*/
|
||||
|
||||
+#ifndef _GNU_SOURCE
|
||||
+#define _GNU_SOURCE
|
||||
+#endif
|
||||
+
|
||||
#include "popen_noshell.h"
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
@@ -28,10 +32,6 @@
|
||||
#include <sys/wait.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
-
|
||||
-#ifndef _GNU_SOURCE
|
||||
-#define _GNU_SOURCE
|
||||
-#endif
|
||||
#include <sched.h>
|
||||
|
||||
/*
|
||||
@@ -249,7 +249,7 @@
|
||||
* The above malloc() + align implementation is taken from:
|
||||
* http://stackoverflow.com/questions/227897/solve-the-memory-alignment-in-c-interview-question-that-stumped-me
|
||||
*/
|
||||
-
|
||||
+
|
||||
#ifndef POPEN_NOSHELL_VALGRIND_DEBUG
|
||||
pid = clone(fn, stack_aligned, CLONE_VM | SIGCHLD, arg);
|
||||
#else
|
||||
@@ -358,7 +358,7 @@
|
||||
|
||||
pclose_arg->fp = fp;
|
||||
pclose_arg->pid = pid;
|
||||
-
|
||||
+
|
||||
return fp; // we should never end up here
|
||||
}
|
||||
|
@ -0,0 +1,306 @@
|
||||
require "font"
|
||||
require "keys"
|
||||
require "settings"
|
||||
require "screen"
|
||||
require "pdfreader"
|
||||
require "djvureader"
|
||||
require "koptreader"
|
||||
require "picviewer"
|
||||
require "crereader"
|
||||
require "defaults"
|
||||
|
||||
registry = {
|
||||
-- registry format:
|
||||
-- reader_name = {reader_object, supported_formats, priority}
|
||||
PDFReader = {PDFReader, DPDFREADER_EXT, 1},
|
||||
DJVUReader = {DJVUReader, DDJVUREADER_EXT, 1},
|
||||
PDFReflow = {KOPTReader, DPDFREFLOW_EXT, 2},
|
||||
DJVUReflow = {KOPTReader, DDJVUREFLOW_EXT, 2},
|
||||
CREReader = {CREReader, DCREREADER_EXT, 2},
|
||||
PICViewer = {PICViewer, DPICVIEWER_EXT, 1},
|
||||
}
|
||||
|
||||
ReaderChooser = {
|
||||
-- UI constants
|
||||
title_H = 35, -- title height
|
||||
title_bar_H = 15, -- title bar height
|
||||
options_H = 35, -- options height
|
||||
options_bar_T = 2, -- options bar thickness
|
||||
spacing = 35, -- spacing between lines
|
||||
WIDTH = 380, -- window width
|
||||
HEIGHT = 220, -- window height
|
||||
margin_I = 50, -- reader item margin
|
||||
margin_O = 10, -- option margin
|
||||
title_font_size = 23, -- title font size
|
||||
item_font_size = 20, -- reader item font size
|
||||
option_font_size = 17, -- option font size
|
||||
|
||||
-- title text
|
||||
TITLE = "Complete action using",
|
||||
-- options text
|
||||
OPTION_TYPE = "Remember this type(T)",
|
||||
OPTION_FILE = "Remember this file(F)",
|
||||
|
||||
-- data variables
|
||||
readers = {},
|
||||
final_choice = nil,
|
||||
last_item = 0,
|
||||
current_item = 1,
|
||||
-- state variables
|
||||
dialogdirty = true,
|
||||
markerdirty = false,
|
||||
optiondirty = true,
|
||||
remember_preference = false,
|
||||
remember_association = false,
|
||||
}
|
||||
|
||||
function GetRegisteredReaders(ftype)
|
||||
local s = ";"
|
||||
local readers = {}
|
||||
for key,value in pairs(registry) do
|
||||
if string.find(value[2],s..ftype..s) then
|
||||
table.insert(readers,key)
|
||||
end
|
||||
end
|
||||
table.sort(readers, function(a,b) return registry[a][3]<registry[b][3] end)
|
||||
return readers
|
||||
end
|
||||
|
||||
-- find the first reader registered with this file type
|
||||
function ReaderChooser:getReaderByType(ftype)
|
||||
local readers = GetRegisteredReaders(ftype)
|
||||
if #readers >= 1 then
|
||||
return registry[readers[1]][1]
|
||||
else
|
||||
if FileChooser.filemanager_expert_mode > FileChooser.BEGINNERS_MODE then
|
||||
return CREReader
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ReaderChooser:getReaderByName(filename)
|
||||
local ext = string.match(filename, ".+%.([^.]+)")
|
||||
local file_type = ext and ext:lower() or "txt"
|
||||
local readers = GetRegisteredReaders(file_type)
|
||||
if #readers > 1 then -- more than one reader are registered with this file type
|
||||
local file_settings = DocSettings:open(filename)
|
||||
local reader_association = file_settings:readSetting("reader_association")
|
||||
local reader_preferences = G_reader_settings:readSetting("reader_preferences")
|
||||
Debug("Reading saved association:", reader_association)
|
||||
if reader_association and reader_association ~= "N/A" then
|
||||
file_settings:close()
|
||||
return registry[reader_association][1]
|
||||
|
||||
elseif reader_preferences and reader_association ~= "N/A" then
|
||||
default_reader = reader_preferences[file_type]
|
||||
if default_reader then
|
||||
return registry[default_reader][1]
|
||||
end
|
||||
end
|
||||
-- need to choose reader
|
||||
Screen:saveCurrentBB()
|
||||
local name = self:choose(readers)
|
||||
if name then
|
||||
if self.remember_preference then
|
||||
if not reader_preferences then
|
||||
reader_preferences = {}
|
||||
end
|
||||
reader_preferences[file_type] = name
|
||||
G_reader_settings:saveSetting("reader_preferences", reader_preferences)
|
||||
file_settings:delSetting("reader_association") --override reader association
|
||||
end
|
||||
if self.remember_association then
|
||||
Debug("Saving last reader:", name)
|
||||
file_settings:saveSetting("reader_association", name)
|
||||
end
|
||||
file_settings:close()
|
||||
Screen:restoreFromSavedBB()
|
||||
return registry[name][1]
|
||||
else
|
||||
file_settings:close()
|
||||
return nil
|
||||
end
|
||||
|
||||
elseif #readers == 1 then
|
||||
return registry[readers[1]][1]
|
||||
else
|
||||
if FileChooser.filemanager_expert_mode > FileChooser.BEGINNERS_MODE then
|
||||
return CREReader
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ReaderChooser:drawBox(xpos, ypos, w, h, bgcolor, bdcolor)
|
||||
-- draw dialog border
|
||||
local r = 6 -- round corners
|
||||
fb.bb:paintRect(xpos, ypos+r, w, h - r, bgcolor)
|
||||
blitbuffer.paintBorder(fb.bb, xpos, ypos, w, r, r, bdcolor, r)
|
||||
blitbuffer.paintBorder(fb.bb, xpos+2, ypos + 2, w - 4, r, r, bdcolor, r)
|
||||
end
|
||||
|
||||
function ReaderChooser:drawTitle(text, xpos, ypos, w, font_face)
|
||||
-- draw title text
|
||||
renderUtf8Text(fb.bb, xpos+10, ypos+self.title_H, font_face, text, true)
|
||||
-- draw title bar
|
||||
fb.bb:paintRect(xpos, ypos+self.title_H+self.title_bar_H, w, 3, 5)
|
||||
|
||||
end
|
||||
|
||||
function ReaderChooser:drawReaderItem(name, xpos, ypos, font_face)
|
||||
-- draw reader name
|
||||
renderUtf8Text(fb.bb, xpos+self.margin_I, ypos, font_face, name, true)
|
||||
return sizeUtf8Text(0, G_width, font_face, name, true).x
|
||||
end
|
||||
|
||||
function ReaderChooser:drawOptions(xpos, ypos, barcolor, bgcolor, font_face)
|
||||
local width, height = self.WIDTH, self.HEIGHT
|
||||
local optbar_T = self.options_bar_T
|
||||
-- draw option border
|
||||
fb.bb:paintRect(xpos, ypos, width, optbar_T, barcolor)
|
||||
fb.bb:paintRect(xpos+(width-optbar_T)/2, ypos, optbar_T, self.options_H, barcolor)
|
||||
-- draw option cell
|
||||
fb.bb:paintRect(xpos, ypos+optbar_T, (width-optbar_T)/2, self.options_H-optbar_T, bgcolor+3*(self.remember_preference and 1 or 0))
|
||||
fb.bb:paintRect(xpos+(width+optbar_T)/2, ypos+optbar_T, (width-optbar_T)/2, self.options_H-optbar_T, bgcolor+3*(self.remember_association and 1 or 0))
|
||||
-- draw option text
|
||||
renderUtf8Text(fb.bb, xpos+self.margin_O, ypos+self.options_H/2+8, font_face, self.OPTION_TYPE, true)
|
||||
renderUtf8Text(fb.bb, xpos+width/2+self.margin_O, ypos+self.options_H/2+8, font_face, self.OPTION_FILE, true)
|
||||
fb:refresh(1, xpos, ypos, width, self.options_H-optbar_T)
|
||||
end
|
||||
|
||||
function ReaderChooser:choose(readers)
|
||||
self.readers = {}
|
||||
self.final_choice = nil
|
||||
self.readers = readers
|
||||
self.dialogdirty = true
|
||||
self.markerdirty = false
|
||||
self.optiondirty = true
|
||||
self:addAllCommands()
|
||||
|
||||
local tface = Font:getFace("tfont", self.title_font_size)
|
||||
local cface = Font:getFace("cfont", self.item_font_size)
|
||||
local fface = Font:getFace("ffont", self.option_font_size)
|
||||
|
||||
local width, height = self.WIDTH, self.HEIGHT
|
||||
local topleft_x, topleft_y = (fb.bb:getWidth()-width)/2, (fb.bb:getHeight()-height)/2
|
||||
local botleft_x, botleft_y = topleft_x, topleft_y+height
|
||||
|
||||
Debug("Drawing box")
|
||||
self:drawBox(topleft_x, topleft_y, width, height, 3, 3)
|
||||
Debug("Drawing title")
|
||||
self:drawTitle(self.TITLE, topleft_x, topleft_y, width, tface)
|
||||
|
||||
local reader_text_width = {}
|
||||
for index,name in ipairs(self.readers) do
|
||||
Debug("Drawing reader:",index,name)
|
||||
reader_text_width[index] = self:drawReaderItem(name, topleft_x, topleft_y+self.title_H+self.spacing*index+10, cface)
|
||||
end
|
||||
|
||||
fb:refresh(1, topleft_x, topleft_y, width, height)
|
||||
|
||||
-- paint first reader marker
|
||||
local xmarker = topleft_x + self.margin_I
|
||||
local ymarker = topleft_y + self.title_H + self.title_bar_H
|
||||
fb.bb:paintRect(xmarker, ymarker+self.spacing*self.current_item, reader_text_width[self.current_item], 3, 15)
|
||||
fb:refresh(1, xmarker, ymarker+self.spacing*self.current_item, reader_text_width[self.current_item], 3)
|
||||
|
||||
local ev, keydef, command, ret_code
|
||||
while true do
|
||||
if self.markerdirty then
|
||||
fb.bb:paintRect(xmarker, ymarker+self.spacing*self.last_item, reader_text_width[self.last_item], 3, 3)
|
||||
fb:refresh(1, xmarker, ymarker+self.spacing*self.last_item, reader_text_width[self.last_item], 3)
|
||||
fb.bb:paintRect(xmarker, ymarker+self.spacing*self.current_item, reader_text_width[self.current_item], 3, 15)
|
||||
fb:refresh(1, xmarker, ymarker+self.spacing*self.current_item, reader_text_width[self.current_item], 3)
|
||||
self.markerdirty = false
|
||||
end
|
||||
|
||||
if self.optiondirty then
|
||||
self:drawOptions(botleft_x, botleft_y-self.options_H, 5, 3, fface)
|
||||
self.optiondirty = false
|
||||
end
|
||||
|
||||
ev = input.saveWaitForEvent()
|
||||
ev.code = adjustKeyEvents(ev)
|
||||
if ev.type == EV_KEY and ev.value ~= EVENT_VALUE_KEY_RELEASE then
|
||||
keydef = Keydef:new(ev.code, getKeyModifier())
|
||||
Debug("key pressed: "..tostring(keydef))
|
||||
command = self.commands:getByKeydef(keydef)
|
||||
if command ~= nil then
|
||||
Debug("command to execute: "..tostring(command))
|
||||
ret_code = command.func(self, keydef)
|
||||
else
|
||||
Debug("command not found: "..tostring(command))
|
||||
end
|
||||
if ret_code == "break" then
|
||||
ret_code = nil
|
||||
if self.final_choice then
|
||||
return self.readers[self.final_choice]
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
end -- if
|
||||
end -- while
|
||||
end
|
||||
|
||||
-- add available commands
|
||||
function ReaderChooser:addAllCommands()
|
||||
self.commands = Commands:new{}
|
||||
self.commands:add(KEY_FW_DOWN, nil, "joypad down",
|
||||
"next item",
|
||||
function(self)
|
||||
self.last_item = self.current_item
|
||||
self.current_item = (self.current_item + #self.readers + 1)%#self.readers
|
||||
if self.current_item == 0 then
|
||||
self.current_item = self.current_item + #self.readers
|
||||
end
|
||||
Debug("Last item:", self.last_item, "Current item:", self.current_item, "N items:", #self.readers)
|
||||
self.markerdirty = true
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_FW_UP, nil, "joypad up",
|
||||
"previous item",
|
||||
function(self)
|
||||
self.last_item = self.current_item
|
||||
self.current_item = (self.current_item + #self.readers - 1)%#self.readers
|
||||
if self.current_item == 0 then
|
||||
self.current_item = self.current_item + #self.readers
|
||||
end
|
||||
Debug("Last item:", self.last_item, "Current item:", self.current_item, "N items:", #self.readers)
|
||||
self.markerdirty = true
|
||||
end
|
||||
)
|
||||
|
||||
self.commands:add(KEY_T, nil, "T",
|
||||
"remember reader choice for this type",
|
||||
function(self)
|
||||
self.remember_preference = not self.remember_preference
|
||||
self.optiondirty = true
|
||||
end
|
||||
)
|
||||
|
||||
self.commands:add(KEY_F, nil, "F",
|
||||
"remember reader choice for this file",
|
||||
function(self)
|
||||
self.remember_association = not self.remember_association
|
||||
self.optiondirty = true
|
||||
end
|
||||
)
|
||||
|
||||
self.commands:add({KEY_ENTER, KEY_FW_PRESS}, nil, "Enter",
|
||||
"choose reader",
|
||||
function(self)
|
||||
self.final_choice = self.current_item
|
||||
return "break"
|
||||
end
|
||||
)
|
||||
self.commands:add(KEY_BACK, nil, "Back",
|
||||
"back",
|
||||
function(self)
|
||||
return "break"
|
||||
end
|
||||
)
|
||||
end
|
After Width: | Height: | Size: 259 B |
After Width: | Height: | Size: 288 B |
After Width: | Height: | Size: 321 B |
After Width: | Height: | Size: 237 B |
After Width: | Height: | Size: 249 B |
After Width: | Height: | Size: 191 B |
After Width: | Height: | Size: 326 B |
After Width: | Height: | Size: 311 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 858 B |
After Width: | Height: | Size: 950 B |
Before Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 844 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 268 B |
After Width: | Height: | Size: 276 B |
After Width: | Height: | Size: 269 B |
After Width: | Height: | Size: 266 B |
After Width: | Height: | Size: 276 B |
After Width: | Height: | Size: 304 B |
After Width: | Height: | Size: 292 B |
After Width: | Height: | Size: 334 B |
@ -0,0 +1,125 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# pdfattach --- embed specified file(s) in a specified PDF file
|
||||
# Requires pdfLaTeX and attachfile.sty package to run
|
||||
# Returns 0 on success or >0 on error
|
||||
#
|
||||
# Written by Tigran Aivazian <tigran@bibles.org.uk>
|
||||
#
|
||||
|
||||
progname=$(basename $0)
|
||||
|
||||
function escape_tex_specialchars()
|
||||
{
|
||||
local txt=$1
|
||||
local res=$(echo "$txt" | sed -e "s%_%\\\_%g" -e "s%&%\\\&%g")
|
||||
echo "$res"
|
||||
}
|
||||
|
||||
function usage()
|
||||
{
|
||||
echo "Usage: $progname -o file.pdf [-a author] file1.djvu [file2.mp3] ..."
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (! getopts ":o:" opt); then
|
||||
echo "$progname: Missing options." >&2
|
||||
usage
|
||||
fi
|
||||
|
||||
if ! type pdflatex > /dev/null 2>&1 ; then
|
||||
echo "$progname: pdfLaTeX program is required." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! kpsewhich attachfile.sty > /dev/null 2>&1 ; then
|
||||
echo "$progname: attachfile.sty package is required." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
declare outfile=""
|
||||
declare -a infiles=()
|
||||
declare -a infiles_texclean=()
|
||||
declare -a infilesize=()
|
||||
declare -i infcount=0 outfcount=0 totalsize=0
|
||||
declare author=""
|
||||
|
||||
while getopts ":o:a:" opt; do
|
||||
case $opt in
|
||||
a)
|
||||
author="$OPTARG"
|
||||
;;
|
||||
o)
|
||||
outfile=$(readlink -f "$OPTARG")
|
||||
((outfcount++))
|
||||
;;
|
||||
\?)
|
||||
echo "$progname: Invalid option: -$OPTARG" >&2
|
||||
usage
|
||||
;;
|
||||
:)
|
||||
echo "$progname: Option -$OPTARG requires an argument." >&2
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
shift $((OPTIND-1))
|
||||
|
||||
numargs=$#
|
||||
for ((i=1 ; i <= $numargs ; i++))
|
||||
do
|
||||
fullname=$(readlink -f "$1")
|
||||
if [ ! -r "$fullname" ] ; then
|
||||
echo "$progname: cannot access the file \"$fullname\"" >&2
|
||||
usage
|
||||
fi
|
||||
infiles[$infcount]="$fullname"
|
||||
infiles_texclean[$infcount]=$(escape_tex_specialchars $(basename "${infiles[$infcount]}"))
|
||||
infilesize[$infcount]=$(stat --print="%s" "$fullname")
|
||||
((totalsize=totalsize+${infilesize[$infcount]}))
|
||||
((infcount++))
|
||||
shift
|
||||
done
|
||||
|
||||
if ((infcount == 0)) ; then
|
||||
echo "$progname: No input file(s) specified." >&2
|
||||
usage
|
||||
fi
|
||||
|
||||
if ((outfcount != 1)) ; then
|
||||
echo "$progname: One (and only one) output file must be specified." >&2
|
||||
usage
|
||||
fi
|
||||
|
||||
workdir=$(mktemp --tmpdir -d pdfattach.XXXXXX)
|
||||
cd $workdir
|
||||
> tmp.tex
|
||||
# emit TeX preamble
|
||||
echo -E "\documentclass{book}" >> tmp.tex
|
||||
echo -E "\usepackage[margin={1mm},papersize={9cm,12cm}]{geometry}" >> tmp.tex
|
||||
echo -E "\usepackage{hyperref,attachfile}" >> tmp.tex
|
||||
if [ ! -z "$author" ] ; then
|
||||
echo -E "\AtBeginDocument{\hypersetup{pdfauthor={$author}}}" >> tmp.tex
|
||||
fi
|
||||
echo -E "\begin{document}" >> tmp.tex
|
||||
echo -E "\tolerance=10000\pagestyle{empty}\fontsize{7}{13}\selectfont" >> tmp.tex
|
||||
|
||||
# emit the list of all files
|
||||
for ((i = 0 ; i < ${#infiles[*]} ; i++));
|
||||
do
|
||||
echo -E "\noindent \hyperlink{L$i}{$((i+1))/${infcount}} \texttt{${infiles_texclean[$i]}} (${infilesize[$i]} bytes)" >> tmp.tex
|
||||
echo >> tmp.tex
|
||||
done
|
||||
echo -E "\noindent Total size $totalsize bytes\newpage" >> tmp.tex
|
||||
|
||||
# now emit all the attachments, one per page
|
||||
for ((i = 0 ; i < ${#infiles[*]} ; i++));
|
||||
do
|
||||
echo -E "\noindent\hypertarget{L$i}$((i+1))/${infcount}\\\\\texttt{${infiles_texclean[$i]}} (\textattachfile[color={0 0 0}]{${infiles[$i]}}{${infilesize[$i]} bytes})\newpage" >> tmp.tex
|
||||
done
|
||||
echo -E "\end{document}" >> tmp.tex
|
||||
pdflatex -halt-on-error tmp.tex > /dev/null && mv tmp.pdf "$outfile"
|
||||
cd - > /dev/null
|
||||
rm -rf $workdir
|