X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=BookReaderIA%2Fdatanode%2FBookReaderImages.inc.php;h=1c9c44b55e1f58f093b24aafc6192c680b9a34e3;hb=74f8b211ee5894c3188eb969fc5ad6fe66d70ac0;hp=a5db85976c304254c7e16d77569325747b99f2e9;hpb=84b2fff983e744a6b1b31e86fddb7f6504f9e937;p=bookreader.git diff --git a/BookReaderIA/datanode/BookReaderImages.inc.php b/BookReaderIA/datanode/BookReaderImages.inc.php index a5db859..1c9c44b 100644 --- a/BookReaderIA/datanode/BookReaderImages.inc.php +++ b/BookReaderIA/datanode/BookReaderImages.inc.php @@ -24,9 +24,11 @@ the MIME type is "image/jpeg". along with BookReader. If not, see . */ +require_once("BookReaderMeta.inc.php"); + class BookReaderImages { - public $MIMES = array('gif' => 'image/gif', + public static $MIMES = array('gif' => 'image/gif', 'jp2' => 'image/jp2', 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', @@ -34,7 +36,7 @@ class BookReaderImages 'tif' => 'image/tiff', 'tiff' => 'image/tiff'); - public $EXTENSIONS = array('gif' => 'gif', + public static $EXTENSIONS = array('gif' => 'gif', 'jp2' => 'jp2', 'jpeg' => 'jpeg', 'jpg' => 'jpeg', @@ -43,17 +45,150 @@ class BookReaderImages 'tiff' => 'tiff'); // Width when generating thumbnails - public $imageSizes = array( + public static $imageSizes = array( 'thumb' => 100, - 'small' => 240, - 'medium' => 500, - 'large' => 1024, + 'small' => 256, + 'medium' => 512, + 'large' => 2048, + ); + + // Keys in the image permalink urls, e.g. http://www.archive.org/download/itemid/page/cover_{keyval}_{keyval}.jpg + public static $imageUrlKeys = array( + //'r' => 'reduce', + 's' => 'scale', + 'region' => 'region', + 'tile' => 'tile', + 'w' => 'width', + 'h' => 'height', + 'rotate' => 'rotate' ); // Paths to command-line tools var $exiftool = '/petabox/sw/books/exiftool/exiftool'; var $kduExpand = '/petabox/sw/bin/kdu_expand'; + /* + * Serve an image request that requires looking up the book metadata + * + * Code path: + * - Get book metadata + * - Parse the requested page (e.g. cover_t.jpg, n5_r4.jpg) to determine which page type, + * size and format (etc) is being requested + * - Determine the leaf number corresponding to the page + * - Determine scaling values + * - Serve image request now that all information has been gathered + */ + + function serveLookupRequest($requestEnv) { + $brm = new BookReaderMeta(); + try { + $metadata = $brm->buildMetadata($_REQUEST['id'], $_REQUEST['itemPath'], $_REQUEST['subPrefix'], $_REQUEST['server']); + } catch (Exception $e) { + $this->BRfatal($e->getMessage); + } + + $page = $_REQUEST['page']; + + // Index of image to return + $imageIndex = null; + + // deal with subPrefix + if ($_REQUEST['subPrefix']) { + $parts = split('/', $_REQUEST['subPrefix']); + $bookId = $parts[count($parts) - 1 ]; + } else { + $bookId = $_REQUEST['id']; + } + + $pageInfo = $this->parsePageRequest($page, $bookId); + + $basePage = $pageInfo['type']; + + switch ($basePage) { + case 'title': + if (! array_key_exists('titleIndex', $metadata)) { + $this->BRfatal("No title page asserted in book"); + } + $imageIndex = $metadata['titleIndex']; + break; + + case 'cover': + if (! array_key_exists('coverIndices', $metadata)) { + $this->BRfatal("No cover asserted in book"); + } + $imageIndex = $metadata['coverIndices'][0]; // $$$ TODO add support for other covers + break; + + case 'preview': + // Preference is: + // Cover page if book was published >= 1950 + // Title page + // Cover page + // Page 0 + + if ( array_key_exists('date', $metadata) && array_key_exists('coverIndices', $metadata) ) { + if ($brm->parseYear($metadata['date']) >= 1950) { + $imageIndex = $metadata['coverIndices'][0]; + break; + } + } + if (array_key_exists('titleIndex', $metadata)) { + $imageIndex = $metadata['titleIndex']; + break; + } + if (array_key_exists('coverIndices', $metadata)) { + $imageIndex = $metadata['coverIndices'][0]; + break; + } + + // First page + $imageIndex = 0; + break; + + case 'n': + // Accessible index page + $imageIndex = intval($pageInfo['value']); + break; + + case 'page': + // Named page + $index = array_search($pageInfo['value'], $metadata['pageNums']); + if ($index === FALSE) { + // Not found + $this->BRfatal("Page not found"); + break; + } + + $imageIndex = $index; + break; + + default: + // Shouldn't be possible + $this->BRfatal("Unrecognized page type requested"); + break; + + } + + $leaf = $brm->leafForIndex($imageIndex, $metadata['leafNums']); + + $requestEnv = array( + 'zip' => $metadata['zip'], + 'file' => $brm->imageFilePath($leaf, $metadata['subPrefix'], $metadata['imageFormat']), + 'ext' => 'jpg', + ); + + // remove non-passthrough keys from pageInfo + unset($pageInfo['type']); + unset($pageInfo['value']); + + // add pageinfo to request + $requestEnv = array_merge($pageInfo, $requestEnv); + + // Return image data - will check privs + $this->serveRequest($requestEnv); + + } + /* * Returns a page image when all parameters such as the image stack location are * passed in. @@ -89,7 +224,7 @@ class BookReaderImages } if ( !file_exists($zipPath) ) { - $this->BRfatal('Image stack does not exist'); + $this->BRfatal('Image stack does not exist at ' . $zipPath); } // Make sure the image stack is readable - return 403 if not $this->checkPrivs($zipPath); @@ -133,6 +268,8 @@ class BookReaderImages // The pbmreduce reduction factor produces an image with dimension 1/n // The kakadu reduction factor produceds an image with dimension 1/(2^n) + + // Set scale from height or width if set if (isset($requestEnv['height'])) { $powReduce = $this->nearestPow2Reduce($requestEnv['height'], $imageInfo['height']); $scale = pow(2, $powReduce); @@ -143,11 +280,16 @@ class BookReaderImages } else { // $$$ could be cleaner // Provide next smaller power of two reduction + + // Set scale from 'scale' if set $scale = $requestEnv['scale']; if (!$scale) { $scale = 1; } - if (array_key_exists($scale, $this->imageSizes)) { + + // Set scale from named size (e.g. 'large') if set + $size = $requestEnv['size']; + if ( $size && array_key_exists($size, self::$imageSizes)) { $srcRatio = floatval($imageInfo['width']) / floatval($imageInfo['height']); if ($srcRatio > 1) { // wide @@ -155,10 +297,13 @@ class BookReaderImages } else { $dimension = 'height'; } - $powReduce = $this->nearestPow2Reduce($this->imageSizes[$scale], $imageInfo[$dimension]); + $powReduce = $this->nearestPow2Reduce(self::$imageSizes[$size], $imageInfo[$dimension]); } else { - $powReduce = $this->nearestPow2ForScale($scale); + // No named size - update powReduce from scale + $powReduce = $this->nearestPow2ForScale($sale); } + + // Make sure scale matches powReduce $scale = pow(2, $powReduce); } @@ -219,7 +364,7 @@ class BookReaderImages $filenameForClient = $this->filenameForClient($file, $ext); - $headers = array('Content-type: '. $MIMES[$ext], // XXX is nginx swallowing this? + $headers = array('Content-type: '. self::$MIMES[$ext], 'Cache-Control: max-age=15552000', 'Content-disposition: inline; filename=' . $filenameForClient); @@ -291,8 +436,8 @@ class BookReaderImages function imageExtensionToType($extension) { - if (array_key_exists($extension, $this->EXTENSIONS)) { - return $this->EXTENSIONS[$extension]; + if (array_key_exists($extension, self::$EXTENSIONS)) { + return self::$EXTENSIONS[$extension]; } else { $this->BRfatal('Unknown image extension'); } @@ -384,7 +529,7 @@ class BookReaderImages $bits = intval($tags["BitDepth"]); break; default: - $this->BRfatal("Unsupported image type"); + $this->BRfatal("Unsupported image type $type for file $file in $zipPath"); break; } @@ -529,8 +674,6 @@ class BookReaderImages function BRfatal($string) { throw new Exception("Image error: $string"); - //echo "alert('$string');\n"; - //die(-1); } // Returns true if using a power node @@ -592,6 +735,100 @@ class BookReaderImages return strlen($binStr) - 1; } + /* + * Parses a page request like "page5_r2.jpg" or "cover_t.jpg" to corresponding + * page type, size, reduce, and format + */ + function parsePageRequest($pageRequest, $bookPrefix) { + + // Will hold parsed results + $pageInfo = array(); + + // Normalize + $pageRequest = strtolower($pageRequest); + + // Pull off extension + if (preg_match('#(.*)\.([^.]+)$#', $pageRequest, $matches) === 1) { + $pageRequest = $matches[1]; + $extension = $matches[2]; + if ($extension == 'jpeg') { + $extension = 'jpg'; + } + } else { + $extension = 'jpg'; + } + $pageInfo['extension'] = $extension; + + // Split parts out + $parts = explode('_', $pageRequest); + + // Remove book prefix if it was included (historical) + if ($parts[0] == $bookPrefix) { + array_shift($parts); + } + + if (count($parts) === 0) { + $this->BRfatal('No page type specified'); + } + $page = array_shift($parts); + + $pageTypes = array( + 'page' => 'str', + 'n' => 'num', + 'cover' => 'single', + 'preview' => 'single', + 'title' => 'single' + ); + + // Look for known page types + foreach ( $pageTypes as $pageName => $kind ) { + if ( preg_match('#^(' . $pageName . ')(.*)#', $page, $matches) === 1 ) { + $pageInfo['type'] = $matches[1]; + switch ($kind) { + case 'str': + $pageInfo['value'] = $matches[2]; + break; + case 'num': + $pageInfo['value'] = intval($matches[2]); + break; + case 'single': + break; + } + } + } + + if ( !array_key_exists('type', $pageInfo) ) { + $this->BRfatal('Unrecognized page type'); + } + + // Look for other known parts + foreach ($parts as $part) { + if ( array_key_exists($part, self::$imageSizes) ) { + $pageInfo['size'] = $part; + continue; + } + + // Key must be alpha, value must start with digit and contain digits, alpha, ',' or '.' + // Should prevent injection of strange values into the redirect to datanode + if ( preg_match('#^([a-z]+)(\d[a-z0-9,.]*)#', $part, $matches) === 0) { + // Not recognized + continue; + } + + $key = $matches[1]; + $value = $matches[2]; + + if ( array_key_exists($key, self::$imageUrlKeys) ) { + $pageInfo[self::$imageUrlKeys[$key]] = $value; + continue; + } + + // If we hit here, was unrecognized (no action) + } + + return $pageInfo; + } + } ?> \ No newline at end of file