X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=BookReaderIA%2Fdatanode%2FBookReaderImages.inc.php;h=9d2f45e17626ed8a6409f76a58d4e8b288c9319d;hb=2de0d1aa070a515d3b20b027a811208c9e8c3a26;hp=f6236ead613bafada724b317981cf5636a25fb37;hpb=9b948f46a09c7c2dd73f85ccdf08214482e831db;p=bookreader.git diff --git a/BookReaderIA/datanode/BookReaderImages.inc.php b/BookReaderIA/datanode/BookReaderImages.inc.php index f6236ea..9d2f45e 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,18 +45,152 @@ 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' ); + // 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', + ); + + if ($pageInfo['reduce']) { + $requestEnv['reduce'] = $pageInfo['reduce']; + } + // $$$ handle scale, other sizes, rotation, etc + + // 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. + * * Approach: * * Get info about requested image (input) @@ -64,7 +200,6 @@ class BookReaderImages * Return image data * Clean up temporary files */ - function serveRequest($requestEnv) { // Process some of the request parameters $zipPath = $requestEnv['zip']; @@ -87,7 +222,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); @@ -217,7 +352,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); @@ -289,8 +424,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'); } @@ -382,7 +517,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; } @@ -527,8 +662,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 @@ -590,6 +723,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 ( in_array($part, $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