7f5030e54af0158d8d560f37b0397f391718ce2a
[bookreader.git] / GnuBookIA / datanode / GnuBookJSIA.php
1 <?
2 /*
3 Copyright(c)2008 Internet Archive. Software license AGPL version 3.
4
5 This file is part of GnuBook.
6
7     GnuBook is free software: you can redistribute it and/or modify
8     it under the terms of the GNU Affero General Public License as published by
9     the Free Software Foundation, either version 3 of the License, or
10     (at your option) any later version.
11
12     GnuBook is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU Affero General Public License for more details.
16
17     You should have received a copy of the GNU Affero General Public License
18     along with GnuBook.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 $id = $_REQUEST['id'];
22 $itemPath = $_REQUEST['itemPath'];
23 $subPrefix = $_REQUEST['subPrefix'];
24 $server = $_REQUEST['server'];
25
26 // Check if we're on a dev vhost and point to JSIA in the user's public_html on the datanode
27 // $$$ TODO consolidate this logic
28 if (strpos($_SERVER["REQUEST_URI"], "/~mang") === 0) { // Serving out of home dir
29     $server .= ':80/~mang';
30 } else if (strpos($_SERVER["REQUEST_URI"], "/~testflip") === 0) { // Serving out of home dir
31     $server .= ':80/~testflip';
32 }
33
34 if ($subPrefix) {
35     $subItemPath = $itemPath . '/' . $subPrefix;
36 } else {
37     $subItemPath = $itemPath . '/' . $id;
38 }
39
40 if ("" == $id) {
41     GBFatal("No identifier specified!");
42 }
43
44 if ("" == $itemPath) {
45     GBFatal("No itemPath specified!");
46 }
47
48 if ("" == $server) {
49     GBFatal("No server specified!");
50 }
51
52 if (!preg_match("|^/[0-3]/items/{$id}$|", $itemPath)) {
53     GBFatal("Bad id!");
54 }
55
56 // XXX check here that subitem is okay
57
58 $imageFormat = 'unknown';
59 $zipFile = "${subItemPath}_jp2.zip";
60
61 if (file_exists($zipFile)) {
62     $imageFormat = 'jp2';
63 } else {
64   $zipFile = "${subItemPath}_tif.zip";
65   if (file_exists($zipFile)) {
66     $imageFormat = 'tif';
67   }
68 }
69
70 if ("unknown" == $imageFormat) {
71   GBfatal("Unknown image format");
72 }
73
74 $scanDataFile = "${subItemPath}_scandata.xml";
75 $scanDataZip  = "$itemPath/scandata.zip";
76 if (file_exists($scanDataFile)) {
77     $scanData = simplexml_load_file($scanDataFile);
78 } else if (file_exists($scanDataZip)) {
79     $cmd  = 'unzip -p ' . escapeshellarg($scanDataZip) . ' scandata.xml';
80     exec($cmd, $output, $retval);
81     if ($retval != 0) GBFatal("Could not unzip ScanData!");
82     
83     $dump = join("\n", $output);
84     $scanData = simplexml_load_string($dump);
85 } else if (file_exists("$itemPath/scandata.xml")) {
86     // For e.g. Scribe v.0 books!
87     $scanData = simplexml_load_file("$itemPath/scandata.xml");
88 } else {
89     GBFatal("ScanData file not found!");
90 }
91
92 $metaDataFile = "$itemPath/{$id}_meta.xml";
93 if (!file_exists($metaDataFile)) {
94     GBFatal("MetaData file not found!");
95 }
96
97
98 $metaData = simplexml_load_file($metaDataFile);
99
100 //$firstLeaf = $scanData->pageData->page[0]['leafNum'];
101 ?>
102
103 gb = new GnuBook();
104
105 <?
106 /* Output title leaf if marked */
107 $titleLeaf = '';
108 foreach ($scanData->pageData->page as $page) {
109     if (("Title Page" == $page->pageType) || ("Title" == $page->pageType)) {
110         $titleLeaf = "{$page['leafNum']}";
111         break;
112     }
113 }
114     
115 if ('' != $titleLeaf) {
116     printf("gb.titleLeaf = %d;\n", $titleLeaf);
117 }
118 ?>
119
120 gb.getPageWidth = function(index) {
121     //return parseInt(this.pageW[index]/this.reduce);
122     
123     // XXX
124     if (index < 0) { // Synthesize
125         return this.pageW[0];
126     } else if (index >= this.numLeafs) {
127         return this.pageW[this.numLeafs - 1];
128     }
129     
130     return this.pageW[index];
131 }
132
133 gb.getPageHeight = function(index) {
134     //return parseInt(this.pageH[index]/this.reduce);
135
136     // XXX
137     if (index < 0) { // Synthesize
138         return this.pageH[0];
139     } else if (index >= gb.numLeafs) {
140         return this.pageH[gb.numLeafs - 1];
141     }
142     
143     return this.pageH[index];
144 }
145
146 // Returns true if page image is available rotated
147 gb.canRotatePage = function(index) {
148     return 'jp2' == this.imageFormat; // Assume single format for now
149 }
150
151 // reduce defaults to 1 (no reduction)
152 // rotate defaults to 0 (no rotation)
153 gb.getPageURI = function(index, reduce, rotate) {
154
155     // XXX
156     if (index < 0 || index >= this.numLeafs) { // Synthesize page
157         return "/bookreader/images/transparent.png";
158     }
159
160     var _reduce;
161     var _rotate;
162
163     if ('undefined' == typeof(reduce)) {
164         _reduce = 1;
165     } else {
166         _reduce = reduce;
167     }
168     if ('undefined' == typeof(rotate)) {
169         _rotate = 0;
170     } else {
171         _rotate = rotate;
172     }
173     
174     var file = this._getPageFile(index);
175         
176     // $$$ add more image stack formats here
177     if (1==this.mode) {
178         var url = 'http://'+this.server+'/GnuBook/GnuBookImages.php?zip='+this.zip+'&file='+file+'&scale='+_reduce+'&rotate='+_rotate;
179     } else {
180         if ('undefined' == typeof(reduce)) {
181             // reduce not passed in
182             var ratio = this.getPageHeight(index) / this.twoPage.height;
183             var scale;
184             // $$$ we make an assumption here that the scales are available pow2 (like kakadu)
185             if (ratio < 2) {
186                 scale = 1;
187             } else if (ratio < 4) {
188                 scale = 2;
189             } else if (ratio < 8) {
190                 scale = 4;
191             } else if (ratio < 16) {
192                 scale = 8;
193             } else  if (ratio < 32) {
194                 scale = 16;
195             } else {
196                 scale = 32;
197             }
198             _reduce = scale;
199         }
200     
201         var url = 'http://'+this.server+'/GnuBook/GnuBookImages.php?zip='+this.zip+'&file='+file+'&scale='+_reduce+'&rotate='+_rotate;
202         
203     }
204     return url;
205 }
206
207 gb._getPageFile = function(index) {
208     var leafStr = '0000';
209     var imgStr = this.leafMap[index].toString();
210     var re = new RegExp("0{"+imgStr.length+"}$");
211     
212     var insideZipPrefix = this.subPrefix.match('[^/]+$');
213     var file = insideZipPrefix + '_' + this.imageFormat + '/' + insideZipPrefix + '_' + leafStr.replace(re, imgStr) + '.' + this.imageFormat;
214     
215     return file;
216 }
217
218 gb.getPageSide = function(index) {
219     //assume the book starts with a cover (right-hand leaf)
220     //we should really get handside from scandata.xml
221     
222     <? // Use special function if we should infer the page sides based off the title page index
223     if (preg_match('/goog$/', $id) && ('' != $titleLeaf)) {
224     ?>
225     // assume page side based on title pagex
226     var titleIndex = gb.leafNumToIndex(gb.titleLeaf);
227     // assume title page is RHS
228     var delta = titleIndex - index;
229     if (0 == (delta & 0x1)) {
230         // even delta
231         return 'R';
232     } else {
233         return 'L';
234     }
235     <?
236     }
237     ?>
238     
239     // $$$ we should get this from scandata instead of assuming the accessible
240     //     leafs are contiguous
241     if ('rl' != this.pageProgression) {
242         // If pageProgression is not set RTL we assume it is LTR
243         if (0 == (index & 0x1)) {
244             // Even-numbered page
245             return 'R';
246         } else {
247             // Odd-numbered page
248             return 'L';
249         }
250     } else {
251         // RTL
252         if (0 == (index & 0x1)) {
253             return 'L';
254         } else {
255             return 'R';
256         }
257     }
258 }
259
260 gb.getPageNum = function(index) {
261     var pageNum = this.pageNums[index];
262     if (pageNum) {
263         return pageNum;
264     } else {
265         return 'n' + index;
266     }
267 }
268
269 gb.leafNumToIndex = function(leafNum) {
270     var index = jQuery.inArray(leafNum, this.leafMap);
271     if (-1 == index) {
272         return null;
273     } else {
274         return index;
275     }
276 }
277
278 // This function returns the left and right indices for the user-visible
279 // spread that contains the given index.  The return values may be
280 // null if there is no facing page or the index is invalid.
281 gb.getSpreadIndices = function(pindex) {
282     // $$$ we could make a separate function for the RTL case and
283     //      only bind it if necessary instead of always checking
284     // $$$ we currently assume there are no gaps
285     
286     var spreadIndices = [null, null]; 
287     if ('rl' == this.pageProgression) {
288         // Right to Left
289         if (this.getPageSide(pindex) == 'R') {
290             spreadIndices[1] = pindex;
291             spreadIndices[0] = pindex + 1;
292         } else {
293             // Given index was LHS
294             spreadIndices[0] = pindex;
295             spreadIndices[1] = pindex - 1;
296         }
297     } else {
298         // Left to right
299         if (this.getPageSide(pindex) == 'L') {
300             spreadIndices[0] = pindex;
301             spreadIndices[1] = pindex + 1;
302         } else {
303             // Given index was RHS
304             spreadIndices[1] = pindex;
305             spreadIndices[0] = pindex - 1;
306         }
307     }
308     
309     //console.log("   index %d mapped to spread %d,%d", pindex, spreadIndices[0], spreadIndices[1]);
310     
311     return spreadIndices;
312 }
313
314 // Remove the page number assertions for all but the highest index page with
315 // a given assertion.  Ensures there is only a single page "{pagenum}"
316 // e.g. the last page asserted as page 5 retains that assertion.
317 gb.uniquifyPageNums = function() {
318     var seen = {};
319     
320     for (var i = gb.pageNums.length - 1; i--; i >= 0) {
321         var pageNum = gb.pageNums[i];
322         if ( !seen[pageNum] ) {
323             seen[pageNum] = true;
324         } else {
325             gb.pageNums[i] = null;
326         }
327     }
328
329 }
330
331 gb.cleanupMetadata = function() {
332     gb.uniquifyPageNums();
333 }
334
335 // getEmbedURL
336 //________
337 // Returns a URL for an embedded version of the current book
338 gb.getEmbedURL = function() {
339     // We could generate a URL hash fragment here but for now we just leave at defaults
340     var url = 'http://' + window.location.host + '/stream/'+this.bookId;
341     if (this.subPrefix != this.bookId) { // Only include if needed
342         url += '/' + this.subPrefix;
343     }
344     url += '?ui=embed';
345     return url;
346 }
347
348 // getEmbedCode
349 //________
350 // Returns the embed code HTML fragment suitable for copy and paste
351 gb.getEmbedCode = function() {
352     return "<iframe src='" + this.getEmbedURL() + "' width='480px' height='430px'></iframe>";
353 }
354
355 gb.pageW =              [
356             <?
357             $i=0;
358             foreach ($scanData->pageData->page as $page) {
359                 if (shouldAddPage($page)) {
360                     if(0 != $i) echo ",";   //stupid IE
361                     echo "{$page->cropBox->w}";
362                     $i++;
363                 }
364             }
365             ?>
366             ];
367
368 gb.pageH =              [
369             <?
370             $totalHeight = 0;
371             $i=0;            
372             foreach ($scanData->pageData->page as $page) {
373                 if (shouldAddPage($page)) {
374                     if(0 != $i) echo ",";   //stupid IE                
375                     echo "{$page->cropBox->h}";
376                     $totalHeight += intval($page->cropBox->h/4) + 10;
377                     $i++;
378                 }
379             }
380             ?>
381             ];
382 gb.leafMap = [
383             <?
384             $i=0;
385             foreach ($scanData->pageData->page as $page) {
386                 if (shouldAddPage($page)) {
387                     if(0 != $i) echo ",";   //stupid IE
388                     echo "{$page['leafNum']}";
389                     $i++;
390                 }
391             }
392             ?>    
393             ];
394
395 gb.pageNums = [
396             <?
397             $i=0;
398             foreach ($scanData->pageData->page as $page) {
399                 if (shouldAddPage($page)) {
400                     if(0 != $i) echo ",";   //stupid IE                
401                     if (array_key_exists('pageNumber', $page) && ('' != $page->pageNumber)) {
402                         echo "'{$page->pageNumber}'";
403                     } else {
404                         echo "null";
405                     }
406                     $i++;
407                 }
408             }
409             ?>    
410             ];
411             
412       
413 gb.numLeafs = gb.pageW.length;
414
415 gb.bookId   = '<?echo $id;?>';
416 gb.zip      = '<?echo $zipFile;?>';
417 gb.subPrefix = '<?echo $subPrefix;?>';
418 gb.server   = '<?echo $server;?>';
419 gb.bookTitle= '<?echo preg_replace("/\'/", "\\'", $metaData->title);?>';
420 gb.bookPath = '<?echo $subItemPath;?>';
421 gb.bookUrl  = '<?echo "http://www.archive.org/details/$id";?>';
422 gb.imageFormat = '<?echo $imageFormat;?>';
423
424 <?
425
426 # Load some values from meta.xml
427 if ('' != $metaData->{'page-progression'}) {
428   echo "gb.pageProgression = '" . $metaData->{"page-progression"} . "';";
429 } else {
430   // Assume page progression is Left To Right
431   echo "gb.pageProgression = 'lr';";
432 }
433
434 # Special cases
435 if ('bandersnatchhsye00scarrich' == $id) {
436     echo "gb.mode     = 2;\n";
437     echo "gb.auto     = true;\n";
438 }
439
440 ?>
441
442 // Check for config object
443 // $$$ change this to use the newer params object
444 if (typeof(gbConfig) != 'undefined') {
445     if (typeof(gbConfig["ui"]) != 'undefined') {
446         gb.ui = gbConfig["ui"];
447     }
448
449     if (gbConfig['mode'] == 1) {
450         gb.mode = 1;
451         if (typeof(gbConfig['reduce'] != 'undefined')) {
452             gb.reduce = gbConfig['reduce'];
453         }
454     } else if (gbConfig['mode'] == 2) {
455         gb.mode = 2;
456       
457 <?
458         //$$$mang hack to override request for 2up for books with attribution page
459         //   as first page until we can display that page in 2up
460         $needle = 'goog';
461         if (strrpos($id, $needle) === strlen($id)-strlen($needle)) {
462             print "// override for books with attribution page\n";
463             print "gb.mode = 1;\n";
464         }
465 ?>
466     }
467 } // gbConfig
468
469 gb.cleanupMetadata();
470 gb.init();
471
472 <?
473
474
475 function GBFatal($string) {
476     echo "alert('$string')\n";
477     die(-1);
478 }
479
480 // Returns true if a page should be added based on it's information in
481 // the metadata
482 function shouldAddPage($page) {
483     // Return false only if the page is marked addToAccessFormats false.
484     // If there is no assertion we assume it should be added.
485     if (isset($page->addToAccessFormats)) {
486         if ("false" == strtolower(trim($page->addToAccessFormats))) {
487             return false;
488         }
489     }
490     
491     return true;
492 }
493
494 ?>