4 Copyright(c)2008 Internet Archive. Software license AGPL version 3.
6 This file is part of BookReader.
8 BookReader is free software: you can redistribute it and/or modify
9 it under the terms of the GNU Affero General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
13 BookReader is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU Affero General Public License for more details.
18 You should have received a copy of the GNU Affero General Public License
19 along with BookReader. If not, see <http://www.gnu.org/licenses/>.
22 $MIMES = array('jpg' => 'image/jpeg',
23 'png' => 'image/png');
25 $exiftool = '/petabox/sw/books/exiftool/exiftool';
27 // Process some of the request parameters
28 $zipPath = $_REQUEST['zip'];
29 $file = $_REQUEST['file'];
30 if (isset($_REQUEST['ext'])) {
31 $ext = $_REQUEST['ext'];
36 if (isset($_REQUEST['callback'])) {
38 $callback = $_REQUEST['callback'];
46 * Get info about requested image (input)
47 * Get info about requested output format
48 * Determine processing parameters
51 * Clean up temporary files
54 function getUnarchiveCommand($archivePath, $file)
56 $lowerPath = strtolower($archivePath);
57 if (preg_match('/\.([^\.]+)$/', $lowerPath, $matches)) {
58 $suffix = $matches[1];
60 if ($suffix == 'zip') {
62 . escapeshellarg($archivePath)
63 . ' ' . escapeshellarg($file);
64 } else if ($suffix == 'tar') {
66 . escapeshellarg($archivePath)
67 . ' ' . escapeshellarg($file);
69 BRfatal('Incompatible archive format');
73 BRfatal('Bad image stack path');
76 BRfatal('Bad image stack path or archive format');
81 * Get the image width, height and depth from a jp2 file in zip.
83 function getImageInfo($zipPath, $file)
87 // $$$ will exiftool work for *all* of our images?
88 // BitsPerComponent present in jp2. Not present in jpeg.
89 $cmd = getUnarchiveCommand($zipPath, $file)
90 . ' | '. $exiftool . ' -s -s -s -ImageWidth -ImageHeight -BitsPerComponent -';
93 preg_match('/^(\d+)/', $output[2], $groups);
94 $bits = intval($groups[1]);
96 $retval = Array('width' => intval($output[0]), 'height' => intval($output[1]),
103 * Output JSON given the imageInfo associative array
105 function outputJSON($imageInfo, $callback)
107 header('Content-type: text/plain');
108 $jsonOutput = json_encode($imageInfo);
110 $jsonOutput = $callback . '(' . $jsonOutput . ');';
115 // Get the image size and depth
116 $imageInfo = getImageInfo($zipPath, $file);
118 // Output json if requested
119 if ('json' == $ext) {
120 outputJSON($imageInfo, $callback);
124 // Unfortunately kakadu requires us to know a priori if the
125 // output file should be .ppm or .pgm. By decompressing to
126 // .bmp kakadu will write a file we can consistently turn into
127 // .pnm. Really kakadu should support .pnm as the file output
128 // extension and automatically write ppm or pgm format as
130 $decompressToBmp = true;
131 if ($decompressToBmp) {
132 $stdoutLink = '/tmp/stdout.bmp';
134 $stdoutLink = '/tmp/stdout.ppm';
137 $fileExt = strtolower(pathinfo($file, PATHINFO_EXTENSION));
139 // Rotate is currently only supported for jp2 since it does not add server load
140 $allowedRotations = array("0", "90", "180", "270");
141 $rotate = $_REQUEST['rotate'];
142 if ( !in_array($rotate, $allowedRotations) ) {
146 // Image conversion options
148 $jpegOptions = '-quality 75';
150 // The pbmreduce reduction factor produces an image with dimension 1/n
151 // The kakadu reduction factor produceds an image with dimension 1/(2^n)
153 if (isset($_REQUEST['height'])) {
154 $ratio = floatval($_REQUEST['origHeight']) / floatval($_REQUEST['height']);
158 } else if ($ratio <= 4) {
162 //$powReduce = 3; //too blurry!
168 $scale = $_REQUEST['scale'];
172 } else if (2 == $scale) {
174 } else if (4 == $scale) {
176 } else if (8 == $scale) {
178 } else if (16 == $scale) {
180 } else if (32 == $scale) {
183 // $$$ Leaving this in as default though I'm not sure why it is...
189 if (!file_exists($stdoutLink))
191 system('ln -s /dev/stdout ' . $stdoutLink);
195 putenv('LD_LIBRARY_PATH=/petabox/sw/lib/kakadu');
197 $unzipCmd = getUnarchiveCommand($zipPath, $file);
199 if ('jp2' == $fileExt) {
201 " | /petabox/sw/bin/kdu_expand -no_seek -quiet -reduce $powReduce -rotate $rotate -i /dev/stdin -o " . $stdoutLink;
202 if ($decompressToBmp) {
203 $decompressCmd .= ' | bmptopnm ';
206 } else if ('tif' == $fileExt) {
207 // We need to create a temporary file for tifftopnm since it cannot
208 // work on a pipe (the file must be seekable).
209 // We use the BookReaderTiff prefix to give a hint in case things don't
211 $tempFile = tempnam("/tmp", "BookReaderTiff");
213 $pbmReduce = reduceCommand($scale);
216 ' > ' . $tempFile . ' ; tifftopnm ' . $tempFile . ' 2>/dev/null' . $pbmReduce;
218 } else if ('jpg' == $fileExt) {
219 $decompressCmd = ' | jpegtopnm ' . reduceCommand($scale);
222 BRfatal('Unknown source file extension: ' . $fileExt);
225 // Non-integer scaling is currently disabled on the cluster
226 // if (isset($_REQUEST['height'])) {
227 // $cmd .= " | pnmscale -height {$_REQUEST['height']} ";
231 $compressCmd = ' | pnmtojpeg ' . $jpegOptions;
232 } else if ('png' == $ext) {
233 $compressCmd = ' | pnmtopng ' . $pngOptions;
236 if (($ext == $fileExt) && ($scale == 1) && ($rotate === "0")) {
237 // Just pass through original data if same format and size
240 $cmd = $unzipCmd . $decompressCmd . $compressCmd;
246 header('Content-type: ' . $MIMES[$ext]);
247 header('Cache-Control: max-age=15552000');
248 passthru ($cmd); # cmd returns image data
250 if (isset($tempFile)) {
254 function BRFatal($string) {
255 echo "alert('$string');\n";
259 // Returns true if using a power node
260 function onPowerNode() {
261 exec("lspci | fgrep -c Realtek", $output, $return);
262 if ("0" != $output[0]) {
265 exec("egrep -q AMD /proc/cpuinfo", $output, $return);
273 function reduceCommand($scale) {
276 return ' | pnmscale -reduce ' . $scale;
278 return ' | pnmscale -nomix -reduce ' . $scale;