Merge branch 'master' of git://github.com/openlibrary/bookreader into thumbnail_feature
[bookreader.git] / BookReaderIA / datanode / BookReaderJSIA.php
1 <?
2 /*
3 Copyright(c)2008 Internet Archive. Software license AGPL version 3.
4
5 This file is part of BookReader.
6
7     BookReader 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     BookReader 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 BookReader.  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     BRFatal("No identifier specified!");
42 }
43
44 if ("" == $itemPath) {
45     BRFatal("No itemPath specified!");
46 }
47
48 if ("" == $server) {
49     BRFatal("No server specified!");
50 }
51
52 if (!preg_match("|^/[0-3]/items/{$id}$|", $itemPath)) {
53     BRFatal("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   BRfatal("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) BRFatal("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     BRFatal("ScanData file not found!");
90 }
91
92 $metaDataFile = "$itemPath/{$id}_meta.xml";
93 if (!file_exists($metaDataFile)) {
94     BRFatal("MetaData file not found!");
95 }
96
97
98 $metaData = simplexml_load_file($metaDataFile);
99
100 //$firstLeaf = $scanData->pageData->page[0]['leafNum'];
101 ?>
102
103 br = new BookReader();
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("br.titleLeaf = %d;\n", $titleLeaf);
117 }
118 ?>
119
120 br.getPageWidth = function(index) {
121     return this.pageW[index];
122 }
123
124 br.getPageHeight = function(index) {
125     return this.pageH[index];
126 }
127
128 // Returns true if page image is available rotated
129 br.canRotatePage = function(index) {
130     return 'jp2' == this.imageFormat; // Assume single format for now
131 }
132
133 // reduce defaults to 1 (no reduction)
134 // rotate defaults to 0 (no rotation)
135 br.getPageURI = function(index, reduce, rotate) {
136     var _reduce;
137     var _rotate;
138
139     if ('undefined' == typeof(reduce)) {
140         _reduce = 1;
141     } else {
142         _reduce = reduce;
143     }
144     if ('undefined' == typeof(rotate)) {
145         _rotate = 0;
146     } else {
147         _rotate = rotate;
148     }
149     
150     var file = this._getPageFile(index);
151         
152     // $$$ add more image stack formats here
153     if (1==this.mode) {
154         var url = 'http://'+this.server+'/BookReader/BookReaderImages.php?zip='+this.zip+'&file='+file+'&scale='+_reduce+'&rotate='+_rotate;
155     } else {
156         if ('undefined' == typeof(reduce)) {
157             // reduce not passed in
158             var ratio = this.getPageHeight(index) / this.twoPage.height;
159             var scale;
160             // $$$ we make an assumption here that the scales are available pow2 (like kakadu)
161             if (ratio < 2) {
162                 scale = 1;
163             } else if (ratio < 4) {
164                 scale = 2;
165             } else if (ratio < 8) {
166                 scale = 4;
167             } else if (ratio < 16) {
168                 scale = 8;
169             } else  if (ratio < 32) {
170                 scale = 16;
171             } else {
172                 scale = 32;
173             }
174             _reduce = scale;
175         }
176     
177         var url = 'http://'+this.server+'/BookReader/BookReaderImages.php?zip='+this.zip+'&file='+file+'&scale='+_reduce+'&rotate='+_rotate;
178         
179     }
180     return url;
181 }
182
183 br._getPageFile = function(index) {
184     var leafStr = '0000';
185     var imgStr = this.leafMap[index].toString();
186     var re = new RegExp("0{"+imgStr.length+"}$");
187     
188     var insideZipPrefix = this.subPrefix.match('[^/]+$');
189     var file = insideZipPrefix + '_' + this.imageFormat + '/' + insideZipPrefix + '_' + leafStr.replace(re, imgStr) + '.' + this.imageFormat;
190     
191     return file;
192 }
193
194 br.getPageSide = function(index) {
195     //assume the book starts with a cover (right-hand leaf)
196     //we should really get handside from scandata.xml
197     
198     <? // Use special function if we should infer the page sides based off the title page index
199     if (preg_match('/goog$/', $id) && ('' != $titleLeaf)) {
200     ?>
201     // assume page side based on title pagex
202     var titleIndex = br.leafNumToIndex(br.titleLeaf);
203     // assume title page is RHS
204     var delta = titleIndex - index;
205     if (0 == (delta & 0x1)) {
206         // even delta
207         return 'R';
208     } else {
209         return 'L';
210     }
211     <?
212     }
213     ?>
214     
215     // $$$ we should get this from scandata instead of assuming the accessible
216     //     leafs are contiguous
217     if ('rl' != this.pageProgression) {
218         // If pageProgression is not set RTL we assume it is LTR
219         if (0 == (index & 0x1)) {
220             // Even-numbered page
221             return 'R';
222         } else {
223             // Odd-numbered page
224             return 'L';
225         }
226     } else {
227         // RTL
228         if (0 == (index & 0x1)) {
229             return 'L';
230         } else {
231             return 'R';
232         }
233     }
234 }
235
236 br.getPageNum = function(index) {
237     var pageNum = this.pageNums[index];
238     if (pageNum) {
239         return pageNum;
240     } else {
241         return 'n' + index;
242     }
243 }
244
245 br.leafNumToIndex = function(leafNum) {
246     var index = jQuery.inArray(leafNum, this.leafMap);
247     if (-1 == index) {
248         return null;
249     } else {
250         return index;
251     }
252 }
253
254 // This function returns the left and right indices for the user-visible
255 // spread that contains the given index.  The return values may be
256 // null if there is no facing page or the index is invalid.
257 br.getSpreadIndices = function(pindex) {
258     // $$$ we could make a separate function for the RTL case and
259     //      only bind it if necessary instead of always checking
260     // $$$ we currently assume there are no gaps
261     
262     var spreadIndices = [null, null]; 
263     if ('rl' == this.pageProgression) {
264         // Right to Left
265         if (this.getPageSide(pindex) == 'R') {
266             spreadIndices[1] = pindex;
267             spreadIndices[0] = pindex + 1;
268         } else {
269             // Given index was LHS
270             spreadIndices[0] = pindex;
271             spreadIndices[1] = pindex - 1;
272         }
273     } else {
274         // Left to right
275         if (this.getPageSide(pindex) == 'L') {
276             spreadIndices[0] = pindex;
277             spreadIndices[1] = pindex + 1;
278         } else {
279             // Given index was RHS
280             spreadIndices[1] = pindex;
281             spreadIndices[0] = pindex - 1;
282         }
283     }
284     
285     //console.log("   index %d mapped to spread %d,%d", pindex, spreadIndices[0], spreadIndices[1]);
286     
287     return spreadIndices;
288 }
289
290 // Remove the page number assertions for all but the highest index page with
291 // a given assertion.  Ensures there is only a single page "{pagenum}"
292 // e.g. the last page asserted as page 5 retains that assertion.
293 br.uniquifyPageNums = function() {
294     var seen = {};
295     
296     for (var i = br.pageNums.length - 1; i--; i >= 0) {
297         var pageNum = br.pageNums[i];
298         if ( !seen[pageNum] ) {
299             seen[pageNum] = true;
300         } else {
301             br.pageNums[i] = null;
302         }
303     }
304
305 }
306
307 br.cleanupMetadata = function() {
308     br.uniquifyPageNums();
309 }
310
311 // getEmbedURL
312 //________
313 // Returns a URL for an embedded version of the current book
314 br.getEmbedURL = function() {
315     // We could generate a URL hash fragment here but for now we just leave at defaults
316     var url = 'http://' + window.location.host + '/stream/'+this.bookId;
317     if (this.subPrefix != this.bookId) { // Only include if needed
318         url += '/' + this.subPrefix;
319     }
320     url += '?ui=embed';
321     return url;
322 }
323
324 // getEmbedCode
325 //________
326 // Returns the embed code HTML fragment suitable for copy and paste
327 br.getEmbedCode = function() {
328     return "<iframe src='" + this.getEmbedURL() + "' width='480px' height='430px'></iframe>";
329 }
330
331 br.pageW =              [
332             <?
333             $i=0;
334             foreach ($scanData->pageData->page as $page) {
335                 if (shouldAddPage($page)) {
336                     if(0 != $i) echo ",";   //stupid IE
337                     echo "{$page->cropBox->w}";
338                     $i++;
339                 }
340             }
341             ?>
342             ];
343
344 br.pageH =              [
345             <?
346             $totalHeight = 0;
347             $i=0;            
348             foreach ($scanData->pageData->page as $page) {
349                 if (shouldAddPage($page)) {
350                     if(0 != $i) echo ",";   //stupid IE                
351                     echo "{$page->cropBox->h}";
352                     $totalHeight += intval($page->cropBox->h/4) + 10;
353                     $i++;
354                 }
355             }
356             ?>
357             ];
358 br.leafMap = [
359             <?
360             $i=0;
361             foreach ($scanData->pageData->page as $page) {
362                 if (shouldAddPage($page)) {
363                     if(0 != $i) echo ",";   //stupid IE
364                     echo "{$page['leafNum']}";
365                     $i++;
366                 }
367             }
368             ?>    
369             ];
370
371 br.pageNums = [
372             <?
373             $i=0;
374             foreach ($scanData->pageData->page as $page) {
375                 if (shouldAddPage($page)) {
376                     if(0 != $i) echo ",";   //stupid IE                
377                     if (array_key_exists('pageNumber', $page) && ('' != $page->pageNumber)) {
378                         echo "'{$page->pageNumber}'";
379                     } else {
380                         echo "null";
381                     }
382                     $i++;
383                 }
384             }
385             ?>    
386             ];
387             
388       
389 br.numLeafs = br.pageW.length;
390
391 br.bookId   = '<?echo $id;?>';
392 br.zip      = '<?echo $zipFile;?>';
393 br.subPrefix = '<?echo $subPrefix;?>';
394 br.server   = '<?echo $server;?>';
395 br.bookTitle= '<?echo preg_replace("/\'/", "\\'", $metaData->title);?>';
396 br.bookPath = '<?echo $subItemPath;?>';
397 br.bookUrl  = '<?echo "http://www.archive.org/details/$id";?>';
398 br.imageFormat = '<?echo $imageFormat;?>';
399
400 <?
401
402 # Load some values from meta.xml
403 if ('' != $metaData->{'page-progression'}) {
404   echo "br.pageProgression = '" . $metaData->{"page-progression"} . "';";
405 } else {
406   // Assume page progression is Left To Right
407   echo "br.pageProgression = 'lr';";
408 }
409
410 # Special cases
411 if ('bandersnatchhsye00scarrich' == $id) {
412     echo "br.mode     = 2;\n";
413     echo "br.auto     = true;\n";
414 }
415
416 ?>
417
418 // Check for config object
419 // $$$ change this to use the newer params object
420 if (typeof(brConfig) != 'undefined') {
421     if (typeof(brConfig["ui"]) != 'undefined') {
422         br.ui = brConfig["ui"];
423     }
424
425     if (brConfig['mode'] == 1) {
426         br.mode = 1;
427         if (typeof(brConfig['reduce'] != 'undefined')) {
428             br.reduce = brConfig['reduce'];
429         }
430     } else if (brConfig['mode'] == 2) {
431         br.mode = 2;
432       
433 <?
434         //$$$mang hack to override request for 2up for books with attribution page
435         //   as first page until we can display that page in 2up
436         $needle = 'goog';
437         if (strrpos($id, $needle) === strlen($id)-strlen($needle)) {
438             print "// override for books with attribution page\n";
439             print "br.mode = 1;\n";
440         }
441 ?>
442     }
443 } // brConfig
444
445 br.cleanupMetadata();
446 br.init();
447
448 <?
449
450
451 function BRFatal($string) {
452     echo "alert('$string')\n";
453     die(-1);
454 }
455
456 // Returns true if a page should be added based on it's information in
457 // the metadata
458 function shouldAddPage($page) {
459     // Return false only if the page is marked addToAccessFormats false.
460     // If there is no assertion we assume it should be added.
461     if (isset($page->addToAccessFormats)) {
462         if ("false" == strtolower(trim($page->addToAccessFormats))) {
463             return false;
464         }
465     }
466     
467     return true;
468 }
469
470 ?>