/*
Copyright(c)2008 Internet Archive. Software license AGPL version 3.
This file is part of BookReader.
BookReader 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.
BookReader 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 BookReader. If not, see .
*/
$id = $_REQUEST['id'];
$itemPath = $_REQUEST['itemPath'];
$subPrefix = $_REQUEST['subPrefix'];
$server = $_REQUEST['server'];
// Check if we're on a dev vhost and point to JSIA in the user's public_html on the datanode
// $$$ TODO consolidate this logic
if (strpos($_SERVER["REQUEST_URI"], "/~mang") === 0) { // Serving out of home dir
$server .= ':80/~mang';
} else if (strpos($_SERVER["REQUEST_URI"], "/~testflip") === 0) { // Serving out of home dir
$server .= ':80/~testflip';
}
if (! $subPrefix) {
$subPrefix = $id;
}
$subItemPath = $itemPath . '/' . $subPrefix;
if ("" == $id) {
BRFatal("No identifier specified!");
}
if ("" == $itemPath) {
BRFatal("No itemPath specified!");
}
if ("" == $server) {
BRFatal("No server specified!");
}
if (!preg_match("|^/\d+/items/{$id}$|", $itemPath)) {
BRFatal("Bad id!");
}
// XXX check here that subitem is okay
$filesDataFile = "$itemPath/${id}_files.xml";
if (file_exists($filesDataFile)) {
$filesData = simplexml_load_file("$itemPath/${id}_files.xml");
} else {
BRfatal("File metadata not found!");
}
$imageStackInfo = findImageStack($subPrefix, $filesData);
if ($imageStackInfo['imageFormat'] == 'unknown') {
BRfatal('Couldn\'t find image stack');
}
$imageFormat = $imageStackInfo['imageFormat'];
$archiveFormat = $imageStackInfo['archiveFormat'];
$imageStackFile = $itemPath . "/" . $imageStackInfo['imageStackFile'];
if ("unknown" == $imageFormat) {
BRfatal("Unknown image format");
}
if ("unknown" == $archiveFormat) {
BRfatal("Unknown archive format");
}
$scanDataFile = "${subItemPath}_scandata.xml";
$scanDataZip = "$itemPath/scandata.zip";
if (file_exists($scanDataFile)) {
$scanData = simplexml_load_file($scanDataFile);
} else if (file_exists($scanDataZip)) {
$cmd = 'unzip -p ' . escapeshellarg($scanDataZip) . ' scandata.xml';
exec($cmd, $output, $retval);
if ($retval != 0) BRFatal("Could not unzip ScanData!");
$dump = join("\n", $output);
$scanData = simplexml_load_string($dump);
} else if (file_exists("$itemPath/scandata.xml")) {
// For e.g. Scribe v.0 books!
$scanData = simplexml_load_file("$itemPath/scandata.xml");
} else {
BRFatal("ScanData file not found!");
}
$metaDataFile = "$itemPath/{$id}_meta.xml";
if (!file_exists($metaDataFile)) {
BRFatal("MetaData file not found!");
}
$metaData = simplexml_load_file($metaDataFile);
//$firstLeaf = $scanData->pageData->page[0]['leafNum'];
?>
br = new BookReader();
/* Output title leaf if marked */
$titleLeaf = '';
foreach ($scanData->pageData->page as $page) {
if (("Title Page" == $page->pageType) || ("Title" == $page->pageType)) {
$titleLeaf = "{$page['leafNum']}";
break;
}
}
if ('' != $titleLeaf) {
printf("br.titleLeaf = %d;\n", $titleLeaf);
}
?>
br.getPageWidth = function(index) {
return this.pageW[index];
}
br.getPageHeight = function(index) {
return this.pageH[index];
}
// Returns true if page image is available rotated
br.canRotatePage = function(index) {
return 'jp2' == this.imageFormat; // Assume single format for now
}
// reduce defaults to 1 (no reduction)
// rotate defaults to 0 (no rotation)
br.getPageURI = function(index, reduce, rotate) {
var _reduce;
var _rotate;
if ('undefined' == typeof(reduce)) {
_reduce = 1;
} else {
_reduce = reduce;
}
if ('undefined' == typeof(rotate)) {
_rotate = 0;
} else {
_rotate = rotate;
}
var file = this._getPageFile(index);
// $$$ add more image stack formats here
return 'http://'+this.server+'/BookReader/BookReaderImages.php?zip='+this.zip+'&file='+file+'&scale='+_reduce+'&rotate='+_rotate;
}
br._getPageFile = function(index) {
var leafStr = '0000';
var imgStr = this.leafMap[index].toString();
var re = new RegExp("0{"+imgStr.length+"}$");
var insideZipPrefix = this.subPrefix.match('[^/]+$');
var file = insideZipPrefix + '_' + this.imageFormat + '/' + insideZipPrefix + '_' + leafStr.replace(re, imgStr) + '.' + this.imageFormat;
return file;
}
br.getPageSide = function(index) {
//assume the book starts with a cover (right-hand leaf)
//we should really get handside from scandata.xml
// Use special function if we should infer the page sides based off the title page index
if (preg_match('/goog$/', $id) && ('' != $titleLeaf)) {
?>
// assume page side based on title pagex
var titleIndex = br.leafNumToIndex(br.titleLeaf);
// assume title page is RHS
var delta = titleIndex - index;
if (0 == (delta & 0x1)) {
// even delta
return 'R';
} else {
return 'L';
}
}
?>
// $$$ we should get this from scandata instead of assuming the accessible
// leafs are contiguous
if ('rl' != this.pageProgression) {
// If pageProgression is not set RTL we assume it is LTR
if (0 == (index & 0x1)) {
// Even-numbered page
return 'R';
} else {
// Odd-numbered page
return 'L';
}
} else {
// RTL
if (0 == (index & 0x1)) {
return 'L';
} else {
return 'R';
}
}
}
br.getPageNum = function(index) {
var pageNum = this.pageNums[index];
if (pageNum) {
return pageNum;
} else {
return 'n' + index;
}
}
// Single images in the Internet Archive scandata.xml metadata are (somewhat incorrectly)
// given a "leaf" number. Some of these images from the scanning process should not
// be displayed in the BookReader (for example colour calibration cards). Since some
// of the scanned images will not be displayed in the BookReader (those marked with
// addToAccessFormats false in the scandata.xml) leaf numbers and BookReader page
// indexes are generally not the same. This function returns the BookReader page
// index given a scanned leaf number.
//
// This function is used, for example, to map between search results (that use the
// leaf numbers) and the displayed pages in the BookReader.
br.leafNumToIndex = function(leafNum) {
for (var index = 0; index < this.leafMap.length; index++) {
if (this.leafMap[index] == leafNum) {
return index;
}
}
return null;
}
// This function returns the left and right indices for the user-visible
// spread that contains the given index. The return values may be
// null if there is no facing page or the index is invalid.
br.getSpreadIndices = function(pindex) {
// $$$ we could make a separate function for the RTL case and
// only bind it if necessary instead of always checking
// $$$ we currently assume there are no gaps
var spreadIndices = [null, null];
if ('rl' == this.pageProgression) {
// Right to Left
if (this.getPageSide(pindex) == 'R') {
spreadIndices[1] = pindex;
spreadIndices[0] = pindex + 1;
} else {
// Given index was LHS
spreadIndices[0] = pindex;
spreadIndices[1] = pindex - 1;
}
} else {
// Left to right
if (this.getPageSide(pindex) == 'L') {
spreadIndices[0] = pindex;
spreadIndices[1] = pindex + 1;
} else {
// Given index was RHS
spreadIndices[1] = pindex;
spreadIndices[0] = pindex - 1;
}
}
//console.log(" index %d mapped to spread %d,%d", pindex, spreadIndices[0], spreadIndices[1]);
return spreadIndices;
}
// Remove the page number assertions for all but the highest index page with
// a given assertion. Ensures there is only a single page "{pagenum}"
// e.g. the last page asserted as page 5 retains that assertion.
br.uniquifyPageNums = function() {
var seen = {};
for (var i = br.pageNums.length - 1; i--; i >= 0) {
var pageNum = br.pageNums[i];
if ( !seen[pageNum] ) {
seen[pageNum] = true;
} else {
br.pageNums[i] = null;
}
}
}
br.cleanupMetadata = function() {
br.uniquifyPageNums();
}
// getEmbedURL
//________
// Returns a URL for an embedded version of the current book
br.getEmbedURL = function() {
// We could generate a URL hash fragment here but for now we just leave at defaults
var url = 'http://' + window.location.host + '/stream/'+this.bookId;
if (this.subPrefix != this.bookId) { // Only include if needed
url += '/' + this.subPrefix;
}
url += '?ui=embed';
return url;
}
// getEmbedCode
//________
// Returns the embed code HTML fragment suitable for copy and paste
br.getEmbedCode = function() {
return "";
}
br.pageW = [
$i=0;
foreach ($scanData->pageData->page as $page) {
if (shouldAddPage($page)) {
if(0 != $i) echo ","; //stupid IE
echo "{$page->cropBox->w}";
$i++;
}
}
?>
];
br.pageH = [
$totalHeight = 0;
$i=0;
foreach ($scanData->pageData->page as $page) {
if (shouldAddPage($page)) {
if(0 != $i) echo ","; //stupid IE
echo "{$page->cropBox->h}";
$totalHeight += intval($page->cropBox->h/4) + 10;
$i++;
}
}
?>
];
br.leafMap = [
$i=0;
foreach ($scanData->pageData->page as $page) {
if (shouldAddPage($page)) {
if(0 != $i) echo ","; //stupid IE
echo "{$page['leafNum']}";
$i++;
}
}
?>
];
br.pageNums = [
$i=0;
foreach ($scanData->pageData->page as $page) {
if (shouldAddPage($page)) {
if(0 != $i) echo ","; //stupid IE
if (array_key_exists('pageNumber', $page) && ('' != $page->pageNumber)) {
echo "'{$page->pageNumber}'";
} else {
echo "null";
}
$i++;
}
}
?>
];
br.numLeafs = br.pageW.length;
br.bookId = '';
br.zip = '';
br.subPrefix = '';
br.server = '';
br.bookTitle= 'title);?>';
br.bookPath = '';
br.bookUrl = '';
br.imageFormat = '';
br.archiveFormat = '';
# Load some values from meta.xml
if ('' != $metaData->{'page-progression'}) {
echo "br.pageProgression = '" . $metaData->{"page-progression"} . "';";
} else {
// Assume page progression is Left To Right
echo "br.pageProgression = 'lr';";
}
# Special cases
if ('bandersnatchhsye00scarrich' == $id) {
echo "br.mode = 2;\n";
echo "br.auto = true;\n";
}
?>
// Check for config object
// $$$ change this to use the newer params object
if (typeof(brConfig) != 'undefined') {
if (typeof(brConfig["ui"]) != 'undefined') {
br.ui = brConfig["ui"];
}
if (brConfig['mode'] == 1) {
br.mode = 1;
if (typeof(brConfig['reduce'] != 'undefined')) {
br.reduce = brConfig['reduce'];
}
} else if (brConfig['mode'] == 2) {
br.mode = 2;
//$$$mang hack to override request for 2up for books with attribution page
// as first page until we can display that page in 2up
$needle = 'goog';
if (strrpos($id, $needle) === strlen($id)-strlen($needle)) {
print "// override for books with attribution page\n";
print "br.mode = 1;\n";
}
?>
}
} // brConfig
br.cleanupMetadata();
br.init();
function BRFatal($string) {
// $$$ TODO log error
echo "alert('$string')\n";
die(-1);
}
// Returns true if a page should be added based on it's information in
// the metadata
function shouldAddPage($page) {
// Return false only if the page is marked addToAccessFormats false.
// If there is no assertion we assume it should be added.
if (isset($page->addToAccessFormats)) {
if ("false" == strtolower(trim($page->addToAccessFormats))) {
return false;
}
}
return true;
}
// Returns { 'imageFormat' => , 'archiveFormat' => '} given a sub-item prefix and loaded xml data
function findImageStack($subPrefix, $filesData) {
// $$$ The order of the image formats determines which will be returned first
$imageFormats = array('JP2' => 'jp2', 'TIFF' => 'tif', 'JPEG' => 'jpg');
$archiveFormats = array('ZIP' => 'zip', 'Tar' => 'tar');
$imageGroup = implode('|', array_keys($imageFormats));
$archiveGroup = implode('|', array_keys($archiveFormats));
// $$$ Currently only return processed images
$imageStackRegex = "/Single Page (Processed) (${imageGroup}) (${archiveGroup})/";
foreach ($filesData->file as $file) {
if (strpos($file['name'], $subPrefix) === 0) { // subprefix matches beginning
if (preg_match($imageStackRegex, $file->format, $matches)) {
// Make sure we have a regular image stack
$imageFormat = $imageFormats[$matches[2]];
if (strpos($file['name'], $subPrefix . '_' . $imageFormat) === 0) {
return array('imageFormat' => $imageFormat,
'archiveFormat' => $archiveFormats[$matches[3]],
'imageStackFile' => $file['name']);
}
}
}
}
return array('imageFormat' => 'unknown', 'archiveFormat' => 'unknown', 'imageStackFile' => 'unknown');
}
?>