Compare commits
805 Commits
Author | SHA1 | Date |
---|---|---|
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,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,371 @@
|
||||
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 ", 2000, 1, MSG_WARN,
|
||||
"The search has given no results")
|
||||
-- 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
|
||||
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.pagedirty = true
|
||||
else
|
||||
InfoMessage:inform("File does not exist ", 2000, 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 ", nil, 0, MSG_CONFIRM,
|
||||
"Please, press key Y to delete the book history")
|
||||
if FileChooser: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 ", 2000, 1, MSG_WARN, "The reading history is empty!")
|
||||
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()
|
||||
InfoMessage:inform("Scanning folder...", nil, 1, MSG_AUX)
|
||||
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 file_type = string.lower(string.match(self.pathfile, ".+%.([^.]+)"))
|
||||
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,41 @@
|
||||
/*
|
||||
** k2pdfopt.h K2pdfopt optimizes PDF/DJVU files for mobile e-readers
|
||||
** (e.g. the Kindle) and smartphones. It works well on
|
||||
** multi-column PDF/DJVU files. K2pdfopt is freeware.
|
||||
**
|
||||
** Copyright (C) 2012 http://willus.com
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU Affero General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
**
|
||||
*/
|
||||
|
||||
#ifndef _K2PDFOPT_H
|
||||
#define _K2PDFOPT_H
|
||||
|
||||
#include <fitz/fitz-internal.h>
|
||||
#include <libdjvu/ddjvuapi.h>
|
||||
|
||||
void k2pdfopt_set_params(int bb_width, int bb_height, \
|
||||
double font_size, double page_margin, \
|
||||
double line_space, double word_space, \
|
||||
int wrapping, int straighten, int justification, \
|
||||
int columns, double contrast, int rotation);
|
||||
void k2pdfopt_mupdf_reflow(fz_document *doc, fz_page *page, fz_context *ctx);
|
||||
void k2pdfopt_djvu_reflow(ddjvu_page_t *page, ddjvu_context_t *ctx, ddjvu_render_mode_t mode, ddjvu_format_t *fmt);
|
||||
void k2pdfopt_rfbmp_size(int *width, int *height);
|
||||
void k2pdfopt_rfbmp_ptr(unsigned char** bmp_ptr_ptr);
|
||||
void k2pdfopt_rfbmp_zoom(double *zoom);
|
||||
|
||||
#endif
|
||||
|
@ -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,328 @@
|
||||
require "font"
|
||||
require "keys"
|
||||
require "settings"
|
||||
|
||||
KOPTOptions = {
|
||||
{
|
||||
name="font_size",
|
||||
option_text="",
|
||||
items_text={"Aa","Aa","Aa","Aa","Aa","Aa","Aa","Aa","Aa"},
|
||||
text_font_size={16,18,22,26,30,34,38,42,46},
|
||||
default_item=6,
|
||||
current_item=6,
|
||||
text_dirty=true,
|
||||
marker_dirty={true, true, true, true, true, true, true, true, true},
|
||||
value={0.2, 0.3, 0.4, 0.6, 1.0, 1.2, 1.6, 2.0, 2.6}},
|
||||
{
|
||||
name="page_margin",
|
||||
option_text="Page Margin",
|
||||
items_text={"small","medium","large"},
|
||||
default_item=2,
|
||||
current_item=2,
|
||||
text_dirty=true,
|
||||
marker_dirty={true, true, true},
|
||||
value={0.02, 0.06, 0.10}},
|
||||
{
|
||||
name="line_spacing",
|
||||
option_text="Line Spacing",
|
||||
items_text={"small","medium","large"},
|
||||
default_item=2,
|
||||
current_item=2,
|
||||
text_dirty=true,
|
||||
marker_dirty={true, true, true},
|
||||
value={1.0, 1.2, 1.4}},
|
||||
{
|
||||
name="word_spacing",
|
||||
option_text="Word Spacing",
|
||||
items_text={"smallest","smaller","small","medium","large"},
|
||||
default_item=4,
|
||||
current_item=4,
|
||||
text_dirty=true,
|
||||
marker_dirty={true, true, true, true, true},
|
||||
value={0.005, 0.01, 0.02, 0.375, 0.5}},
|
||||
{
|
||||
name="text_wrap",
|
||||
option_text="Text Wrap",
|
||||
items_text={"page fit","page reflow"},
|
||||
default_item=2,
|
||||
current_item=2,
|
||||
text_dirty=true,
|
||||
marker_dirty={true, true},
|
||||
value={0, 1}},
|
||||
{
|
||||
name="auto_straighten",
|
||||
option_text="Auto Straighten",
|
||||
items_text={"default","0","5","10"},
|
||||
default_item=1,
|
||||
current_item=1,
|
||||
text_dirty=true,
|
||||
marker_dirty={true, true, true, true},
|
||||
value={0, 0, 5, 10}},
|
||||
{
|
||||
name="justification",
|
||||
option_text="Justification",
|
||||
items_text={"default","left","center","right","full"},
|
||||
default_item=1,
|
||||
current_item=1,
|
||||
text_dirty=true,
|
||||
marker_dirty={true, true, true, true, true},
|
||||
value={-1,0,1,2,3}},
|
||||
{
|
||||
name="max_columns",
|
||||
option_text="Columns",
|
||||
items_text={"auto","1","2","3","4"},
|
||||
default_item=1,
|
||||
current_item=1,
|
||||
text_dirty=true,
|
||||
marker_dirty={true, true, true, true, true},
|
||||
value={2,1,2,3,4}},
|
||||
{
|
||||
name="contrast",
|
||||
option_text="Contrast",
|
||||
items_text={"lightest","lighter","default","darker","darkest"},
|
||||
default_item=3,
|
||||
current_item=3,
|
||||
text_dirty=true,
|
||||
marker_dirty={true, true, true, true, true},
|
||||
value={0.2, 0.4, 1.0, 1.8, 2.6}},
|
||||
{
|
||||
name="screen_rotation",
|
||||
option_text="Screen Rotation",
|
||||
items_text={"0","90","180","270"},
|
||||
default_item=1,
|
||||
current_item=1,
|
||||
text_dirty=true,
|
||||
marker_dirty={true, true, true, true},
|
||||
value={0, 90, 180, 270}},
|
||||
}
|
||||
|
||||
KOPTConfig = {
|
||||
-- UI constants
|
||||
WIDTH = 550, -- width
|
||||
HEIGHT = 400, -- height
|
||||
MARGIN_BOTTOM = 25, -- window bottom margin
|
||||
OPTION_PADDING_T = 70, -- option top padding
|
||||
OPTION_PADDING_H = 50, -- option horizontal padding
|
||||
OPTION_SPACING_V = 35, -- 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,
|
||||
-- page dirty
|
||||
page_dirty = false,
|
||||
}
|
||||
|
||||
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
|
||||
renderUtf8Text(fb.bb, xpos-text_len, ypos+self.OPTION_SPACING_V*(option_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 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*(option_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 5)
|
||||
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
|
||||
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
|
||||
|
||||
function KOPTConfig:makeDefault(configurable)
|
||||
for i=1,#KOPTOptions do
|
||||
KOPTOptions[i].text_dirty = true
|
||||
for j=1,#KOPTOptions[i].items_text do
|
||||
KOPTOptions[i].marker_dirty[j] = true
|
||||
end
|
||||
local option = KOPTOptions[i].name
|
||||
local value = configurable[option]
|
||||
KOPTOptions[i].current_item = KOPTOptions[i].default_item
|
||||
for index, val in pairs(KOPTOptions[i].value) do
|
||||
if val == value then
|
||||
KOPTOptions[i].current_item = index
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function KOPTConfig:reconfigure(configurable)
|
||||
for i=1,#KOPTOptions do
|
||||
option = KOPTOptions[i].name
|
||||
configurable[option] = KOPTOptions[i].value[KOPTOptions[i].current_item]
|
||||
end
|
||||
end
|
||||
|
||||
function KOPTConfig:config(callback, reader, configurable)
|
||||
local kopt_callback = callback
|
||||
local koptreader = reader
|
||||
--local configurable = configurable
|
||||
|
||||
self:makeDefault(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(configurable)
|
||||
|
||||
if self.page_dirty then
|
||||
kopt_callback(koptreader)
|
||||
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.page_dirty = 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
|
||||
self.current_option = (self.current_option + #KOPTOptions + 1)%#KOPTOptions
|
||||
self.current_option = (self.current_option == 0) and #KOPTOptions or self.current_option
|
||||
|
||||
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
|
||||
self.current_option = (self.current_option + #KOPTOptions - 1)%#KOPTOptions
|
||||
self.current_option = (self.current_option == 0) and #KOPTOptions or self.current_option
|
||||
|
||||
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.page_dirty = 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.page_dirty = true
|
||||
end
|
||||
)
|
||||
self.commands:add({KEY_F,KEY_AA,KEY_BACK}, nil, "Back",
|
||||
"back",
|
||||
function(self)
|
||||
return "break"
|
||||
end
|
||||
)
|
||||
end
|
@ -0,0 +1,363 @@
|
||||
require "unireader"
|
||||
require "inputbox"
|
||||
require "koptconfig"
|
||||
|
||||
Configurable = {
|
||||
font_size = 1.2,
|
||||
page_margin = 0.06,
|
||||
line_spacing = 1.2,
|
||||
word_spacing = 0.375,
|
||||
text_wrap = 1,
|
||||
auto_straighten = 0,
|
||||
justification = -1,
|
||||
max_columns = 2,
|
||||
contrast = 1.0,
|
||||
screen_rotation = 0,
|
||||
}
|
||||
|
||||
function Configurable:hash(sep)
|
||||
local hash = ""
|
||||
for key,value in pairs(self) do
|
||||
if type(value) == "number" then
|
||||
hash = hash..sep..value
|
||||
end
|
||||
end
|
||||
return hash
|
||||
end
|
||||
|
||||
function Configurable:loadDefaults()
|
||||
-- Configurable = {}
|
||||
for i=1,#KOPTOptions do
|
||||
local key = KOPTOptions[i].name
|
||||
local default_item = KOPTOptions[i].default_item
|
||||
self[key] = KOPTOptions[i].value[default_item]
|
||||
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 = {}
|
||||
}
|
||||
|
||||
-- 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
|
||||
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
|
||||
|
||||
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 dc = self:setzoom(page, preCache)
|
||||
|
||||
-- check if we have relevant cache contents
|
||||
local pagehash = no..self.configurable:hash('_')
|
||||
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_x < self.min_offset_x then
|
||||
self.offset_x = self.min_offset_x
|
||||
end
|
||||
|
||||
if self.offset_y < self.min_offset_y 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
|
||||
|
||||
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 tile = { x = offset_x_in_page, y = offset_y_in_page,
|
||||
w = width, h = height }
|
||||
-- can we cache the full page?
|
||||
local max_cache = self.cache_max_memsize
|
||||
if preCache then
|
||||
max_cache = max_cache - self.cache[self.pagehash].size
|
||||
end
|
||||
|
||||
Debug("page::reflowPage:", "width:", width, "height:", height)
|
||||
local font_size, page_margin = self.configurable.font_size, self.configurable.page_margin
|
||||
local line_spacing, word_spacing = self.configurable.line_spacing, self.configurable.word_spacing
|
||||
local text_wrap, justification = self.configurable.text_wrap, self.configurable.justification
|
||||
local max_columns, contrast = self.configurable.max_columns, self.configurable.contrast
|
||||
local auto_straighten = self.configurable.auto_straighten
|
||||
local screen_rotation = self.configurable.screen_rotation
|
||||
self.fullwidth, self.fullheight, self.reflow_zoom = page:reflow(dc, self.render_mode, width, height, font_size, page_margin, line_spacing, word_spacing, text_wrap, auto_straighten, justification, max_columns, contrast, screen_rotation)
|
||||
Debug("page::reflowPage:", "fullwidth:", self.fullwidth, "fullheight:", self.fullheight)
|
||||
|
||||
if (self.fullwidth * self.fullheight / 2) <= max_cache then
|
||||
-- yes we can, so do this with offset 0, 0
|
||||
tile.x = 0
|
||||
tile.y = 0
|
||||
tile.w = self.fullwidth
|
||||
tile.h = self.fullheight
|
||||
else
|
||||
if not preCache then
|
||||
Debug("ERROR not enough memory in cache left, probably a bug.")
|
||||
end
|
||||
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("page::drawReflowedPage:", "rendering page:", no, "width:", self.cache[pagehash].w, "height:", self.cache[pagehash].h)
|
||||
page:rfdraw(dc, self.cache[pagehash].bb)
|
||||
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_x < self.min_offset_x then
|
||||
self.offset_x = self.min_offset_x
|
||||
end
|
||||
|
||||
if self.offset_y < self.min_offset_y 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
|
||||
|
||||
-- set viewer state according to zoom state
|
||||
function KOPTReader:setzoom(page, preCache)
|
||||
local dc = DrawContext.new()
|
||||
|
||||
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
|
||||
|
||||
return dc
|
||||
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
|
||||
local offset_y_dec = self.offset_y - G_height + self.pan_overlap_vertical
|
||||
self.offset_y = offset_y_dec > self.min_offset_y and offset_y_dec or self.min_offset_y
|
||||
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
|
||||
local offset_y_inc = self.offset_y + G_height - self.pan_overlap_vertical
|
||||
self.offset_y = offset_y_inc < 0 and offset_y_inc or 0
|
||||
end
|
||||
|
||||
return pageno
|
||||
end
|
||||
|
||||
function KOPTReader:setDefaults()
|
||||
self.show_overlap_enable = true
|
||||
self.show_links_enable = false
|
||||
self.comics_mode_enable = false
|
||||
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))
|
||||
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))
|
||||
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_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:add({KEY_F,KEY_AA}, nil, "F",
|
||||
"change koptreader configuration",
|
||||
function(self)
|
||||
KOPTConfig:config(KOPTReader.redrawWithoutPrecache, self, self.configurable)
|
||||
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
|
@ -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 5bd133d6c89fb9ef5c501eb27d2d6221ec44d11d
|
@ -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,64 @@
|
||||
require "unireader"
|
||||
|
||||
PICViewer = UniReader:new{}
|
||||
|
||||
function PICViewer:setDefaults()
|
||||
self.show_overlap_enable = false
|
||||
self.show_links_enable = false
|
||||
self.comics_mode_enable = false
|
||||
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 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, 40+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)
|
||||
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_R, MOD_ALT,"R")
|
||||
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")
|
||||
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,294 @@
|
||||
require "font"
|
||||
require "keys"
|
||||
require "settings"
|
||||
require "pdfreader"
|
||||
require "djvureader"
|
||||
require "koptreader"
|
||||
require "picviewer"
|
||||
require "crereader"
|
||||
|
||||
registry = {
|
||||
-- registry format:
|
||||
-- reader_name = {reader_object, supported_formats, priority}
|
||||
PDFReader = {PDFReader, ";pdf;xps;cbz;", 1},
|
||||
DJVUReader = {DJVUReader, ";djvu;", 1},
|
||||
PDFReflow = {KOPTReader, ";pdf;", 2},
|
||||
DJVUReflow = {KOPTReader, ";djvu;", 2},
|
||||
CREReader = {CREReader, ";epub;txt;rtf;htm;html;mobi;prc;azw;fb2;chm;pdb;doc;tcr;zip;", 1},
|
||||
PICViewer = {PICViewer, ";jpg;jpeg;", 1},
|
||||
-- seems to accept pdb-files for PalmDoc only
|
||||
}
|
||||
|
||||
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_default = false,
|
||||
remember_last = 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
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
function ReaderChooser:getReaderByName(filename)
|
||||
local file_type = string.lower(string.match(filename, ".+%.([^.]+)"))
|
||||
local readers = GetRegisteredReaders(file_type)
|
||||
if #readers > 1 then -- more than one reader are registered with this file type
|
||||
local default_readers = G_reader_settings:readSetting("default_readers")
|
||||
|
||||
if default_readers then
|
||||
default_reader = default_readers[file_type]
|
||||
if default_reader then
|
||||
return registry[default_reader][1]
|
||||
end
|
||||
end
|
||||
|
||||
local file_settings = DocSettings:open(filename)
|
||||
local last_reader = file_settings:readSetting("last_reader")
|
||||
Debug("Reading saved preference:", last_reader)
|
||||
if last_reader then
|
||||
file_settings:close()
|
||||
return registry[last_reader][1]
|
||||
else
|
||||
local name = self:choose(readers)
|
||||
if name then
|
||||
if self.remember_default then
|
||||
if not default_readers then
|
||||
default_readers = {}
|
||||
end
|
||||
default_readers[file_type] = name
|
||||
G_reader_settings:saveSetting("default_readers", default_readers)
|
||||
end
|
||||
if self.remember_last then
|
||||
Debug("Saving last reader:", name)
|
||||
file_settings:saveSetting("last_reader", name)
|
||||
end
|
||||
file_settings:close()
|
||||
return registry[name][1]
|
||||
else
|
||||
file_settings:close()
|
||||
return nil
|
||||
end
|
||||
end
|
||||
elseif #readers == 1 then
|
||||
return registry[readers[1]][1]
|
||||
else
|
||||
return nil
|
||||
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_default 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_last 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_default = not self.remember_default
|
||||
self.optiondirty = true
|
||||
end
|
||||
)
|
||||
|
||||
self.commands:add(KEY_F, nil, "F",
|
||||
"remember reader choice for this file",
|
||||
function(self)
|
||||
self.remember_last = not self.remember_last
|
||||
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,118 @@
|
||||
#!/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 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
|
||||
|
||||
while getopts ":o:" opt; do
|
||||
case $opt in
|
||||
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
|
||||
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
|