MediaWiki
REL1_23
|
00001 <?php 00029 class DjVuHandler extends ImageHandler { 00033 function isEnabled() { 00034 global $wgDjvuRenderer, $wgDjvuDump, $wgDjvuToXML; 00035 if ( !$wgDjvuRenderer || ( !$wgDjvuDump && !$wgDjvuToXML ) ) { 00036 wfDebug( "DjVu is disabled, please set \$wgDjvuRenderer and \$wgDjvuDump\n" ); 00037 00038 return false; 00039 } else { 00040 return true; 00041 } 00042 } 00043 00048 function mustRender( $file ) { 00049 return true; 00050 } 00051 00056 function isMultiPage( $file ) { 00057 return true; 00058 } 00059 00063 function getParamMap() { 00064 return array( 00065 'img_width' => 'width', 00066 'img_page' => 'page', 00067 ); 00068 } 00069 00075 function validateParam( $name, $value ) { 00076 if ( in_array( $name, array( 'width', 'height', 'page' ) ) ) { 00077 if ( $value <= 0 ) { 00078 return false; 00079 } else { 00080 return true; 00081 } 00082 } else { 00083 return false; 00084 } 00085 } 00086 00091 function makeParamString( $params ) { 00092 $page = isset( $params['page'] ) ? $params['page'] : 1; 00093 if ( !isset( $params['width'] ) ) { 00094 return false; 00095 } 00096 00097 return "page{$page}-{$params['width']}px"; 00098 } 00099 00104 function parseParamString( $str ) { 00105 $m = false; 00106 if ( preg_match( '/^page(\d+)-(\d+)px$/', $str, $m ) ) { 00107 return array( 'width' => $m[2], 'page' => $m[1] ); 00108 } else { 00109 return false; 00110 } 00111 } 00112 00117 function getScriptParams( $params ) { 00118 return array( 00119 'width' => $params['width'], 00120 'page' => $params['page'], 00121 ); 00122 } 00123 00132 function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 ) { 00133 global $wgDjvuRenderer, $wgDjvuPostProcessor; 00134 00135 // Fetch XML and check it, to give a more informative error message than the one which 00136 // normaliseParams will inevitably give. 00137 $xml = $image->getMetadata(); 00138 if ( !$xml ) { 00139 $width = isset( $params['width'] ) ? $params['width'] : 0; 00140 $height = isset( $params['height'] ) ? $params['height'] : 0; 00141 00142 return new MediaTransformError( 'thumbnail_error', $width, $height, 00143 wfMessage( 'djvu_no_xml' )->text() ); 00144 } 00145 00146 if ( !$this->normaliseParams( $image, $params ) ) { 00147 return new TransformParameterError( $params ); 00148 } 00149 $width = $params['width']; 00150 $height = $params['height']; 00151 $page = $params['page']; 00152 if ( $page > $this->pageCount( $image ) ) { 00153 return new MediaTransformError( 00154 'thumbnail_error', 00155 $width, 00156 $height, 00157 wfMessage( 'djvu_page_error' )->text() 00158 ); 00159 } 00160 00161 if ( $flags & self::TRANSFORM_LATER ) { 00162 $params = array( 00163 'width' => $width, 00164 'height' => $height, 00165 'page' => $page 00166 ); 00167 00168 return new ThumbnailImage( $image, $dstUrl, $dstPath, $params ); 00169 } 00170 00171 if ( !wfMkdirParents( dirname( $dstPath ), null, __METHOD__ ) ) { 00172 return new MediaTransformError( 00173 'thumbnail_error', 00174 $width, 00175 $height, 00176 wfMessage( 'thumbnail_dest_directory' )->text() 00177 ); 00178 } 00179 00180 // Get local copy source for shell scripts 00181 // Thumbnail extraction is very inefficient for large files. 00182 // Provide a way to pool count limit the number of downloaders. 00183 if ( $image->getSize() >= 1e7 ) { // 10MB 00184 $work = new PoolCounterWorkViaCallback( 'GetLocalFileCopy', sha1( $image->getName() ), 00185 array( 00186 'doWork' => function() use ( $image ) { 00187 return $image->getLocalRefPath(); 00188 } 00189 ) 00190 ); 00191 $srcPath = $work->execute(); 00192 } else { 00193 $srcPath = $image->getLocalRefPath(); 00194 } 00195 00196 if ( $srcPath === false ) { // Failed to get local copy 00197 wfDebugLog( 'thumbnail', 00198 sprintf( 'Thumbnail failed on %s: could not get local copy of "%s"', 00199 wfHostname(), $image->getName() ) ); 00200 00201 return new MediaTransformError( 'thumbnail_error', 00202 $params['width'], $params['height'], 00203 wfMessage( 'filemissing' )->text() 00204 ); 00205 } 00206 00207 # Use a subshell (brackets) to aggregate stderr from both pipeline commands 00208 # before redirecting it to the overall stdout. This works in both Linux and Windows XP. 00209 $cmd = '(' . wfEscapeShellArg( 00210 $wgDjvuRenderer, 00211 "-format=ppm", 00212 "-page={$page}", 00213 "-size={$params['physicalWidth']}x{$params['physicalHeight']}", 00214 $srcPath ); 00215 if ( $wgDjvuPostProcessor ) { 00216 $cmd .= " | {$wgDjvuPostProcessor}"; 00217 } 00218 $cmd .= ' > ' . wfEscapeShellArg( $dstPath ) . ') 2>&1'; 00219 wfProfileIn( 'ddjvu' ); 00220 wfDebug( __METHOD__ . ": $cmd\n" ); 00221 $retval = ''; 00222 $err = wfShellExec( $cmd, $retval ); 00223 wfProfileOut( 'ddjvu' ); 00224 00225 $removed = $this->removeBadFile( $dstPath, $retval ); 00226 if ( $retval != 0 || $removed ) { 00227 $this->logErrorForExternalProcess( $retval, $err, $cmd ); 00228 return new MediaTransformError( 'thumbnail_error', $width, $height, $err ); 00229 } else { 00230 $params = array( 00231 'width' => $width, 00232 'height' => $height, 00233 'page' => $page 00234 ); 00235 00236 return new ThumbnailImage( $image, $dstUrl, $dstPath, $params ); 00237 } 00238 } 00239 00247 function getDjVuImage( $image, $path ) { 00248 if ( !$image ) { 00249 $deja = new DjVuImage( $path ); 00250 } elseif ( !isset( $image->dejaImage ) ) { 00251 $deja = $image->dejaImage = new DjVuImage( $path ); 00252 } else { 00253 $deja = $image->dejaImage; 00254 } 00255 00256 return $deja; 00257 } 00258 00265 private function getUnserializedMetadata( File $file ) { 00266 $metadata = $file->getMetadata(); 00267 if ( substr( $metadata, 0, 3 ) === '<?xml' ) { 00268 // Old style. Not serialized but instead just a raw string of XML. 00269 return $metadata; 00270 } 00271 00272 wfSuppressWarnings(); 00273 $unser = unserialize( $metadata ); 00274 wfRestoreWarnings(); 00275 if ( is_array( $unser ) ) { 00276 return $unser['xml']; 00277 } 00278 00279 // unserialize failed. Guess it wasn't really serialized after all, 00280 return $metadata; 00281 } 00282 00289 function getMetaTree( $image, $gettext = false ) { 00290 if ( $gettext && isset( $image->djvuTextTree ) ) { 00291 return $image->djvuTextTree; 00292 } 00293 if ( !$gettext && isset( $image->dejaMetaTree ) ) { 00294 return $image->dejaMetaTree; 00295 } 00296 00297 $metadata = $this->getUnserializedMetadata( $image ); 00298 if ( !$this->isMetadataValid( $image, $metadata ) ) { 00299 wfDebug( "DjVu XML metadata is invalid or missing, should have been fixed in upgradeRow\n" ); 00300 00301 return false; 00302 } 00303 wfProfileIn( __METHOD__ ); 00304 00305 wfSuppressWarnings(); 00306 try { 00307 // Set to false rather than null to avoid further attempts 00308 $image->dejaMetaTree = false; 00309 $image->djvuTextTree = false; 00310 $tree = new SimpleXMLElement( $metadata ); 00311 if ( $tree->getName() == 'mw-djvu' ) { 00313 foreach ( $tree->children() as $b ) { 00314 if ( $b->getName() == 'DjVuTxt' ) { 00315 // @todo File::djvuTextTree and File::dejaMetaTree are declared 00316 // dynamically. Add a public File::$data to facilitate this? 00317 $image->djvuTextTree = $b; 00318 } elseif ( $b->getName() == 'DjVuXML' ) { 00319 $image->dejaMetaTree = $b; 00320 } 00321 } 00322 } else { 00323 $image->dejaMetaTree = $tree; 00324 } 00325 } catch ( Exception $e ) { 00326 wfDebug( "Bogus multipage XML metadata on '{$image->getName()}'\n" ); 00327 } 00328 wfRestoreWarnings(); 00329 wfProfileOut( __METHOD__ ); 00330 if ( $gettext ) { 00331 return $image->djvuTextTree; 00332 } else { 00333 return $image->dejaMetaTree; 00334 } 00335 } 00336 00342 function getImageSize( $image, $path ) { 00343 return $this->getDjVuImage( $image, $path )->getImageSize(); 00344 } 00345 00346 function getThumbType( $ext, $mime, $params = null ) { 00347 global $wgDjvuOutputExtension; 00348 static $mime; 00349 if ( !isset( $mime ) ) { 00350 $magic = MimeMagic::singleton(); 00351 $mime = $magic->guessTypesForExtension( $wgDjvuOutputExtension ); 00352 } 00353 00354 return array( $wgDjvuOutputExtension, $mime ); 00355 } 00356 00357 function getMetadata( $image, $path ) { 00358 wfDebug( "Getting DjVu metadata for $path\n" ); 00359 00360 $xml = $this->getDjVuImage( $image, $path )->retrieveMetaData(); 00361 if ( $xml === false ) { 00362 return false; 00363 } else { 00364 return serialize( array( 'xml' => $xml ) ); 00365 } 00366 } 00367 00368 function getMetadataType( $image ) { 00369 return 'djvuxml'; 00370 } 00371 00372 function isMetadataValid( $image, $metadata ) { 00373 return !empty( $metadata ) && $metadata != serialize( array() ); 00374 } 00375 00376 function pageCount( $image ) { 00377 $tree = $this->getMetaTree( $image ); 00378 if ( !$tree ) { 00379 return false; 00380 } 00381 00382 return count( $tree->xpath( '//OBJECT' ) ); 00383 } 00384 00385 function getPageDimensions( $image, $page ) { 00386 $tree = $this->getMetaTree( $image ); 00387 if ( !$tree ) { 00388 return false; 00389 } 00390 00391 $o = $tree->BODY[0]->OBJECT[$page - 1]; 00392 if ( $o ) { 00393 return array( 00394 'width' => intval( $o['width'] ), 00395 'height' => intval( $o['height'] ) 00396 ); 00397 } else { 00398 return false; 00399 } 00400 } 00401 00407 function getPageText( $image, $page ) { 00408 $tree = $this->getMetaTree( $image, true ); 00409 if ( !$tree ) { 00410 return false; 00411 } 00412 00413 $o = $tree->BODY[0]->PAGE[$page - 1]; 00414 if ( $o ) { 00415 $txt = $o['value']; 00416 00417 return $txt; 00418 } else { 00419 return false; 00420 } 00421 } 00422 }