* Introduced media handler modules for file-type specific operations: thumbnailing, img_metadata, capabilities, etc.
* Deprecated $wgUseImageResize, thumbnailing will be enabled unconditionally. * Fixed interaction of page parameter to ImagePage with the HTML file cache * Improved error reporting for image thumbnailing * Fixed MIME type for SVG files, will be silently changed from image/svg to image/svg+xml after loading from the database. * Workaround for djvutoxml bug #1704049 (poor performance). Use djvudump instead. * Fixed odd behaviour in ImagePage on DjVu thumbnailing errors * Improved error reporting for image thumbnailing * Added sharpening option for ImageMagick thumbnailing * Removed Image::selectPage(), added page parameters to getWidth() and getHeight(), deprecated Image::renderThumb() and Image::getThumbnail() * Changed default contents of img_metadata to empty string instead of a:0:{} * Moved responsibility for respecting $wgGenerateThumbnailOnParse from the UI to Image.php
This commit is contained in:
parent
ff9b558c05
commit
b15d8cffc4
23 changed files with 1658 additions and 984 deletions
|
|
@ -31,6 +31,7 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN
|
|||
* Added rate limiter for Special:Emailuser
|
||||
* Private logs can now be created using $wgLogRestrictions
|
||||
* (Bug 8590) limited HTML is now always enabled ($wgUserHtml = true).
|
||||
* Deprecated $wgUseImageResize, thumbnailing will be enabled unconditionally.
|
||||
|
||||
== New features since 1.9 ==
|
||||
|
||||
|
|
@ -117,6 +118,7 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN
|
|||
* Introduce 'SearchUpdate' hook; see docs/hooks.txt for more information
|
||||
* Introduce 'mywatchlist' message; used on personal menu to link to watchlist page
|
||||
* Introduce magic word {{NUMBEROFEDITS}}
|
||||
* Introduced media handlers for file-type specific operations.
|
||||
|
||||
== Bugfixes since 1.9 ==
|
||||
|
||||
|
|
@ -327,6 +329,8 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN
|
|||
a random page, and will give an error message if none really can be found
|
||||
instead of sending the user to the main page like they used to
|
||||
* Fix object variable used for displaying "not-patrolled" CSS class on list
|
||||
* Fixed interaction of page parameter to ImagePage with the HTML file cache
|
||||
*
|
||||
|
||||
== Maintenance ==
|
||||
|
||||
|
|
|
|||
|
|
@ -483,8 +483,6 @@ if( $conf->HaveGD ) {
|
|||
}
|
||||
}
|
||||
|
||||
$conf->UseImageResize = $conf->HaveGD || $conf->ImageMagick;
|
||||
|
||||
$conf->IP = dirname( dirname( __FILE__ ) );
|
||||
print "<li>Installation directory: <tt>" . htmlspecialchars( $conf->IP ) . "</tt></li>\n";
|
||||
|
||||
|
|
@ -1302,7 +1300,6 @@ function escapePhpString( $string ) {
|
|||
}
|
||||
|
||||
function writeLocalSettings( $conf ) {
|
||||
$conf->UseImageResize = $conf->UseImageResize ? 'true' : 'false';
|
||||
$conf->PasswordSender = $conf->EmergencyContact;
|
||||
$magic = ($conf->ImageMagick ? "" : "# ");
|
||||
$convert = ($conf->ImageMagick ? $conf->ImageMagick : "/usr/bin/convert" );
|
||||
|
|
@ -1448,7 +1445,6 @@ if ( \$wgCommandLineMode ) {
|
|||
## To enable image uploads, make sure the 'images' directory
|
||||
## is writable, then set this to true:
|
||||
\$wgEnableUploads = false;
|
||||
\$wgUseImageResize = {$conf->UseImageResize};
|
||||
{$magic}\$wgUseImageMagick = true;
|
||||
{$magic}\$wgImageMagickConvertCommand = \"{$convert}\";
|
||||
|
||||
|
|
|
|||
|
|
@ -2460,6 +2460,7 @@ class Article {
|
|||
$diff = $wgRequest->getVal( 'diff' );
|
||||
$redirect = $wgRequest->getVal( 'redirect' );
|
||||
$printable = $wgRequest->getVal( 'printable' );
|
||||
$page = $wgRequest->getVal( 'page' );
|
||||
|
||||
return $wgUseFileCache
|
||||
and (!$wgShowIPinHeader)
|
||||
|
|
@ -2472,6 +2473,7 @@ class Article {
|
|||
and (!isset($diff))
|
||||
and (!isset($redirect))
|
||||
and (!isset($printable))
|
||||
and !isset($page)
|
||||
and (!$this->mRedirectedFrom);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ function __autoload($className) {
|
|||
global $wgAutoloadClasses;
|
||||
|
||||
static $localClasses = array(
|
||||
# Includes
|
||||
'AjaxDispatcher' => 'includes/AjaxDispatcher.php',
|
||||
'AjaxCachePolicy' => 'includes/AjaxFunctions.php',
|
||||
'AjaxResponse' => 'includes/AjaxResponse.php',
|
||||
|
|
@ -115,6 +116,10 @@ function __autoload($className) {
|
|||
'MacBinary' => 'includes/MacBinary.php',
|
||||
'MagicWord' => 'includes/MagicWord.php',
|
||||
'MathRenderer' => 'includes/Math.php',
|
||||
'MediaTransformOutput' => 'includes/MediaTransformOutput.php',
|
||||
'ThumbnailImage' => 'includes/MediaTransformOutput.php',
|
||||
'MediaTransformError' => 'includes/MediaTransformOutput.php',
|
||||
'TransformParameterError' => 'includes/MediaTransformOutput.php',
|
||||
'MessageCache' => 'includes/MessageCache.php',
|
||||
'MimeMagic' => 'includes/MimeMagic.php',
|
||||
'Namespace' => 'includes/Namespace.php',
|
||||
|
|
@ -128,6 +133,7 @@ function __autoload($className) {
|
|||
'ParserOutput' => 'includes/ParserOutput.php',
|
||||
'ParserOptions' => 'includes/ParserOptions.php',
|
||||
'ParserCache' => 'includes/ParserCache.php',
|
||||
'PatrolLog' => 'includes/PatrolLog.php',
|
||||
'ProfilerSimple' => 'includes/ProfilerSimple.php',
|
||||
'ProfilerSimpleUDP' => 'includes/ProfilerSimpleUDP.php',
|
||||
'Profiler' => 'includes/Profiler.php',
|
||||
|
|
@ -196,6 +202,7 @@ function __autoload($className) {
|
|||
'PopularPagesPage' => 'includes/SpecialPopularpages.php',
|
||||
'PreferencesForm' => 'includes/SpecialPreferences.php',
|
||||
'SpecialPrefixindex' => 'includes/SpecialPrefixindex.php',
|
||||
'PasswordResetForm' => 'includes/SpecialResetpass.php',
|
||||
'RevisionDeleteForm' => 'includes/SpecialRevisiondelete.php',
|
||||
'RevisionDeleter' => 'includes/SpecialRevisiondelete.php',
|
||||
'SpecialSearch' => 'includes/SpecialSearch.php',
|
||||
|
|
@ -240,15 +247,26 @@ function __autoload($className) {
|
|||
'Xml' => 'includes/Xml.php',
|
||||
'ZhClient' => 'includes/ZhClient.php',
|
||||
'memcached' => 'includes/memcached-client.php',
|
||||
|
||||
# Media
|
||||
'BitmapHandler' => 'includes/media/Bitmap.php',
|
||||
'DjVuHandler' => 'includes/media/DjVu.php',
|
||||
'MediaHandler' => 'includes/media/Generic.php',
|
||||
'ImageHandler' => 'includes/media/Generic.php',
|
||||
'SvgHandler' => 'includes/media/SVG.php',
|
||||
|
||||
# Normal
|
||||
'UtfNormal' => 'includes/normal/UtfNormal.php',
|
||||
|
||||
# Templates
|
||||
'UsercreateTemplate' => 'includes/templates/Userlogin.php',
|
||||
'UserloginTemplate' => 'includes/templates/Userlogin.php',
|
||||
|
||||
# Languages
|
||||
'Language' => 'languages/Language.php',
|
||||
'PasswordResetForm' => 'includes/SpecialResetpass.php',
|
||||
'PatrolLog' => 'includes/PatrolLog.php',
|
||||
'RandomPage' => 'includes/SpecialRandompage.php',
|
||||
|
||||
// API classes
|
||||
# API
|
||||
'ApiBase' => 'includes/api/ApiBase.php',
|
||||
'ApiFormatFeedWrapper' => 'includes/api/ApiFormatBase.php',
|
||||
'ApiFeedWatchlist' => 'includes/api/ApiFeedWatchlist.php',
|
||||
|
|
|
|||
|
|
@ -1443,8 +1443,18 @@ $wgSiteNotice = '';
|
|||
# Images settings
|
||||
#
|
||||
|
||||
/** dynamic server side image resizing ("Thumbnails") */
|
||||
$wgUseImageResize = false;
|
||||
/**
|
||||
* Plugins for media file type handling.
|
||||
* Each entry in the array maps a MIME type to a class name
|
||||
*/
|
||||
$wgMediaHandlers = array(
|
||||
'image/jpeg' => 'BitmapHandler',
|
||||
'image/png' => 'BitmapHandler',
|
||||
'image/gif' => 'BitmapHandler',
|
||||
'image/svg+xml' => 'SvgHandler',
|
||||
'image/vnd.djvu' => 'DjVuHandler',
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Resizing can be done using PHP's internal image libraries or using
|
||||
|
|
@ -1458,6 +1468,12 @@ $wgUseImageMagick = false;
|
|||
/** The convert command shipped with ImageMagick */
|
||||
$wgImageMagickConvertCommand = '/usr/bin/convert';
|
||||
|
||||
/** Sharpening parameter to ImageMagick */
|
||||
$wgSharpenParameter = '0x0.4';
|
||||
|
||||
/** Reduction in linear dimensions below which sharpening will be enabled */
|
||||
$wgSharpenReductionThreshold = 0.85;
|
||||
|
||||
/**
|
||||
* Use another resizing converter, e.g. GraphicMagick
|
||||
* %s will be replaced with the source path, %d with the destination
|
||||
|
|
@ -1523,6 +1539,10 @@ $wgIgnoreImageErrors = false;
|
|||
*/
|
||||
$wgGenerateThumbnailOnParse = true;
|
||||
|
||||
/** Obsolete, always true, kept for compatibility with extensions */
|
||||
$wgUseImageResize = true;
|
||||
|
||||
|
||||
/** Set $wgCommandLineMode if it's not set already, to avoid notices */
|
||||
if( !isset( $wgCommandLineMode ) ) {
|
||||
$wgCommandLineMode = false;
|
||||
|
|
@ -2277,7 +2297,7 @@ $wgTrustedMediaFormats= array(
|
|||
MEDIATYPE_BITMAP, //all bitmap formats
|
||||
MEDIATYPE_AUDIO, //all audio formats
|
||||
MEDIATYPE_VIDEO, //all plain video formats
|
||||
"image/svg", //svg (only needed if inline rendering of svg is not supported)
|
||||
"image/svg+xml", //svg (only needed if inline rendering of svg is not supported)
|
||||
"application/pdf", //PDF files
|
||||
#"application/x-shockwave-flash", //flash/shockwave movie
|
||||
);
|
||||
|
|
@ -2380,7 +2400,7 @@ $wgReservedUsernames = array(
|
|||
* MediaWiki will reject HTMLesque tags in uploaded files due to idiotic browsers which can't
|
||||
* perform basic stuff like MIME detection and which are vulnerable to further idiots uploading
|
||||
* crap files as images. When this directive is on, <title> will be allowed in files with
|
||||
* an "image/svg" MIME type. You should leave this disabled if your web server is misconfigured
|
||||
* an "image/svg+xml" MIME type. You should leave this disabled if your web server is misconfigured
|
||||
* and doesn't send appropriate MIME types for SVG images.
|
||||
*/
|
||||
$wgAllowTitlesInSVG = false;
|
||||
|
|
@ -2406,18 +2426,31 @@ $wgMaxShellFileSize = 102400;
|
|||
|
||||
/**
|
||||
* DJVU settings
|
||||
* Path of the djvutoxml executable
|
||||
* Path of the djvudump executable
|
||||
* Enable this and $wgDjvuRenderer to enable djvu rendering
|
||||
*/
|
||||
# $wgDjvuDump = 'djvudump';
|
||||
$wgDjvuDump = null;
|
||||
|
||||
/**
|
||||
* Path of the ddjvu DJVU renderer
|
||||
* Enable this and $wgDjvuDump to enable djvu rendering
|
||||
*/
|
||||
# $wgDjvuRenderer = 'ddjvu';
|
||||
$wgDjvuRenderer = null;
|
||||
|
||||
/**
|
||||
* Path of the djvutoxml executable
|
||||
* This works like djvudump except much, much slower as of version 3.5.
|
||||
*
|
||||
* For now I recommend you use djvudump instead. The djvuxml output is
|
||||
* probably more stable, so we'll switch back to it as soon as they fix
|
||||
* the efficiency problem.
|
||||
* http://sourceforge.net/tracker/index.php?func=detail&aid=1704049&group_id=32953&atid=406583
|
||||
*/
|
||||
# $wgDjvuToXML = 'djvutoxml';
|
||||
$wgDjvuToXML = null;
|
||||
|
||||
/**
|
||||
* Path of the ddjvu DJVU renderer
|
||||
* Enable this and $wgDjvuToXML to enable djvu rendering
|
||||
*/
|
||||
# $wgDjvuRenderer = 'ddjvu';
|
||||
$wgDjvuRenderer = null;
|
||||
|
||||
/**
|
||||
* Shell command for the DJVU post processor
|
||||
|
|
|
|||
|
|
@ -220,17 +220,121 @@ class DjVuImage {
|
|||
* @return string
|
||||
*/
|
||||
function retrieveMetaData() {
|
||||
global $wgDjvuToXML;
|
||||
if ( isset( $wgDjvuToXML ) ) {
|
||||
global $wgDjvuToXML, $wgDjvuDump;
|
||||
if ( isset( $wgDjvuDump ) ) {
|
||||
# djvudump is faster as of version 3.5
|
||||
# http://sourceforge.net/tracker/index.php?func=detail&aid=1704049&group_id=32953&atid=406583
|
||||
wfProfileIn( 'djvudump' );
|
||||
$cmd = wfEscapeShellArg( $wgDjvuDump ) . ' ' . wfEscapeShellArg( $this->mFilename );
|
||||
$dump = wfShellExec( $cmd );
|
||||
$xml = $this->convertDumpToXML( $dump );
|
||||
wfProfileOut( 'djvudump' );
|
||||
} elseif ( isset( $wgDjvuToXML ) ) {
|
||||
wfProfileIn( 'djvutoxml' );
|
||||
$cmd = wfEscapeShellArg( $wgDjvuToXML ) . ' --without-anno --without-text ' .
|
||||
wfEscapeShellArg( $this->mFilename );
|
||||
$xml = wfShellExec( $cmd );
|
||||
wfProfileOut( 'djvutoxml' );
|
||||
} else {
|
||||
$xml = null;
|
||||
}
|
||||
return $xml;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Hack to temporarily work around djvutoxml bug
|
||||
*/
|
||||
function convertDumpToXML( $dump ) {
|
||||
if ( strval( $dump ) == '' ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$xml = <<<EOT
|
||||
<?xml version="1.0" ?>
|
||||
<!DOCTYPE DjVuXML PUBLIC "-//W3C//DTD DjVuXML 1.1//EN" "pubtext/DjVuXML-s.dtd">
|
||||
<DjVuXML>
|
||||
<HEAD></HEAD>
|
||||
<BODY>
|
||||
EOT;
|
||||
|
||||
$dump = str_replace( "\r", '', $dump );
|
||||
$line = strtok( $dump, "\n" );
|
||||
$m = false;
|
||||
$good = false;
|
||||
if ( preg_match( '/^( *)FORM:DJVU/', $line, $m ) ) {
|
||||
# Single-page
|
||||
if ( $this->parseFormDjvu( $line, $xml ) ) {
|
||||
$good = true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} elseif ( preg_match( '/^( *)FORM:DJVM/', $line, $m ) ) {
|
||||
# Multi-page
|
||||
$parentLevel = strlen( $m[1] );
|
||||
# Find DIRM
|
||||
$line = strtok( "\n" );
|
||||
while ( $line !== false ) {
|
||||
$childLevel = strspn( $line, ' ' );
|
||||
if ( $childLevel <= $parentLevel ) {
|
||||
# End of chunk
|
||||
break;
|
||||
}
|
||||
|
||||
if ( preg_match( '/^ *DIRM.*indirect/', $line ) ) {
|
||||
wfDebug( "Indirect multi-page DjVu document, bad for server!\n" );
|
||||
return false;
|
||||
}
|
||||
if ( preg_match( '/^ *FORM:DJVU/', $line ) ) {
|
||||
# Found page
|
||||
if ( $this->parseFormDjvu( $line, $xml ) ) {
|
||||
$good = true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$line = strtok( "\n" );
|
||||
}
|
||||
}
|
||||
if ( !$good ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$xml .= "</BODY>\n</DjVuXML>\n";
|
||||
return $xml;
|
||||
}
|
||||
|
||||
function parseFormDjvu( $line, &$xml ) {
|
||||
$parentLevel = strspn( $line, ' ' );
|
||||
$line = strtok( "\n" );
|
||||
|
||||
# Find INFO
|
||||
while ( $line !== false ) {
|
||||
$childLevel = strspn( $line, ' ' );
|
||||
if ( $childLevel <= $parentLevel ) {
|
||||
# End of chunk
|
||||
break;
|
||||
}
|
||||
|
||||
if ( preg_match( '/^ *INFO *\[\d*\] *DjVu *(\d+)x(\d+), *\w*, *(\d+) *dpi, *gamma=([0-9.-]+)/', $line, $m ) ) {
|
||||
$xml .= Xml::tags( 'OBJECT',
|
||||
array(
|
||||
#'data' => '',
|
||||
#'type' => 'image/x.djvu',
|
||||
'height' => $m[2],
|
||||
'width' => $m[1],
|
||||
#'usemap' => '',
|
||||
),
|
||||
"\n" .
|
||||
Xml::element( 'PARAM', array( 'name' => 'DPI', 'value' => $m[3] ) ) . "\n" .
|
||||
Xml::element( 'PARAM', array( 'name' => 'GAMMA', 'value' => $m[4] ) ) . "\n"
|
||||
) . "\n";
|
||||
return true;
|
||||
}
|
||||
$line = strtok( "\n" );
|
||||
}
|
||||
# Not found
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -93,9 +93,9 @@ class Exif {
|
|||
var $basename;
|
||||
|
||||
/**
|
||||
* The private log to log to
|
||||
* The private log to log to, e.g. 'exif'
|
||||
*/
|
||||
var $log = 'exif';
|
||||
var $log = false;
|
||||
|
||||
//@}
|
||||
|
||||
|
|
@ -561,7 +561,10 @@ class Exif {
|
|||
* @param $fname String:
|
||||
* @param $action Mixed: , default NULL.
|
||||
*/
|
||||
function debug( $in, $fname, $action = NULL ) {
|
||||
function debug( $in, $fname, $action = NULL ) {
|
||||
if ( !$this->log ) {
|
||||
return;
|
||||
}
|
||||
$type = gettype( $in );
|
||||
$class = ucfirst( __CLASS__ );
|
||||
if ( $type === 'array' )
|
||||
|
|
@ -586,6 +589,9 @@ class Exif {
|
|||
* @param $io Boolean: Specify whether we're beginning or ending
|
||||
*/
|
||||
function debugFile( $fname, $io ) {
|
||||
if ( !$this->log ) {
|
||||
return;
|
||||
}
|
||||
$class = ucfirst( __CLASS__ );
|
||||
if ( $io ) {
|
||||
wfDebugLog( $this->log, "$class::$fname: begin processing: '{$this->basename}'\n" );
|
||||
|
|
|
|||
1017
includes/Image.php
1017
includes/Image.php
File diff suppressed because it is too large
Load diff
|
|
@ -183,7 +183,7 @@ class ImageGallery
|
|||
*
|
||||
*/
|
||||
function toHTML() {
|
||||
global $wgLang, $wgGenerateThumbnailOnParse;
|
||||
global $wgLang;
|
||||
|
||||
$sk = $this->getSkin();
|
||||
|
||||
|
|
@ -191,6 +191,7 @@ class ImageGallery
|
|||
if( $this->mCaption )
|
||||
$s .= "\n\t<caption>{$this->mCaption}</caption>";
|
||||
|
||||
$params = array( 'width' => $this->mWidths, 'height' => $this->mHeights );
|
||||
$i = 0;
|
||||
foreach ( $this->mImages as $pair ) {
|
||||
$img =& $pair[0];
|
||||
|
|
@ -206,7 +207,7 @@ class ImageGallery
|
|||
# The image is blacklisted, just show it as a text link.
|
||||
$thumbhtml = "\n\t\t\t".'<div style="height: '.($this->mHeights*1.25+2).'px;">'
|
||||
. $sk->makeKnownLinkObj( $nt, htmlspecialchars( $nt->getText() ) ) . '</div>';
|
||||
} elseif( !( $thumb = $img->getThumbnail( $this->mWidths, $this->mHeights, $wgGenerateThumbnailOnParse ) ) ) {
|
||||
} elseif( !( $thumb = $img->transform( $params ) ) ) {
|
||||
# Error generating thumbnail.
|
||||
$thumbhtml = "\n\t\t\t".'<div style="height: '.($this->mHeights*1.25+2).'px;">'
|
||||
. htmlspecialchars( $img->getLastError() ) . '</div>';
|
||||
|
|
|
|||
|
|
@ -166,11 +166,9 @@ class ImagePage extends Article {
|
|||
|
||||
function openShowImage() {
|
||||
global $wgOut, $wgUser, $wgImageLimits, $wgRequest, $wgLang;
|
||||
global $wgUseImageResize, $wgGenerateThumbnailOnParse;
|
||||
|
||||
$full_url = $this->img->getURL();
|
||||
$anchoropen = '';
|
||||
$anchorclose = '';
|
||||
$linkAttribs = false;
|
||||
$sizeSel = intval( $wgUser->getOption( 'imagesize') );
|
||||
if( !isset( $wgImageLimits[$sizeSel] ) ) {
|
||||
$sizeSel = User::getDefaultOption( 'imagesize' );
|
||||
|
|
@ -190,10 +188,11 @@ class ImagePage extends Article {
|
|||
if ( $this->img->exists() ) {
|
||||
# image
|
||||
$page = $wgRequest->getIntOrNull( 'page' );
|
||||
if ( ! is_null( $page ) ) {
|
||||
$this->img->selectPage( $page );
|
||||
} else {
|
||||
if ( is_null( $page ) ) {
|
||||
$params = array();
|
||||
$page = 1;
|
||||
} else {
|
||||
$params = array( 'page' => $page );
|
||||
}
|
||||
$width_orig = $this->img->getWidth();
|
||||
$width = $width_orig;
|
||||
|
|
@ -201,6 +200,7 @@ class ImagePage extends Article {
|
|||
$height = $height_orig;
|
||||
$mime = $this->img->getMimeType();
|
||||
$showLink = false;
|
||||
$linkAttribs = array( 'href' => $full_url );
|
||||
|
||||
if ( $this->img->allowInlineDisplay() and $width and $height) {
|
||||
# image
|
||||
|
|
@ -223,39 +223,33 @@ class ImagePage extends Article {
|
|||
# Note that $height <= $maxHeight now, but might not be identical
|
||||
# because of rounding.
|
||||
}
|
||||
}
|
||||
$params['width'] = $width;
|
||||
$thumbnail = $this->img->transform( $params );
|
||||
|
||||
if( $wgUseImageResize ) {
|
||||
$thumbnail = $this->img->getThumbnail( $width, -1, $wgGenerateThumbnailOnParse );
|
||||
if ( $thumbnail == null ) {
|
||||
$url = $this->img->getViewURL();
|
||||
} else {
|
||||
$url = $thumbnail->getURL();
|
||||
}
|
||||
} else {
|
||||
# No resize ability? Show the full image, but scale
|
||||
# it down in the browser so it fits on the page.
|
||||
$url = $this->img->getViewURL();
|
||||
}
|
||||
$anchoropen = "<a href=\"{$full_url}\">";
|
||||
$anchorclose = "</a><br />";
|
||||
if( $this->img->mustRender() ) {
|
||||
$showLink = true;
|
||||
} else {
|
||||
$anchorclose .= wfMsg('show-big-image-thumb', $width, $height ) .
|
||||
'<br />' . "\n$anchoropen{$msgbig}</a> " . $msgsize;
|
||||
}
|
||||
} else {
|
||||
$url = $this->img->getViewURL();
|
||||
$anchorclose = "<br />";
|
||||
if( $this->img->mustRender() ) {
|
||||
$showLink = true;
|
||||
} else {
|
||||
$anchorclose .=
|
||||
wfMsg('show-big-image-thumb', $width, $height ) .
|
||||
'<br />' . Xml::tags( 'a', $linkAttribs, $msgbig ) . ' ' . $msgsize;
|
||||
}
|
||||
|
||||
if ( $this->img->isMultipage() ) {
|
||||
$wgOut->addHTML( '<table class="multipageimage"><tr><td>' );
|
||||
}
|
||||
|
||||
$wgOut->addHTML( '<div class="fullImageLink" id="file">' . $anchoropen .
|
||||
"<img border=\"0\" src=\"{$url}\" width=\"{$width}\" height=\"{$height}\" alt=\"" .
|
||||
htmlspecialchars( $this->img->getTitle()->getPrefixedText() ).'" />' . $anchorclose . '</div>' );
|
||||
$imgAttribs = array(
|
||||
'border' => 0,
|
||||
'alt' => $this->img->getTitle()->getPrefixedText()
|
||||
);
|
||||
|
||||
if ( $thumbnail ) {
|
||||
$wgOut->addHTML( '<div class="fullImageLink" id="file">' .
|
||||
$thumbnail->toHtml( $imgAttribs, $linkAttribs ) .
|
||||
$anchorclose . '</div>' );
|
||||
}
|
||||
|
||||
if ( $this->img->isMultipage() ) {
|
||||
$count = $this->img->pageCount();
|
||||
|
|
@ -263,17 +257,17 @@ class ImagePage extends Article {
|
|||
if ( $page > 1 ) {
|
||||
$label = $wgOut->parse( wfMsg( 'imgmultipageprev' ), false );
|
||||
$link = $sk->makeLinkObj( $this->mTitle, $label, 'page='. ($page-1) );
|
||||
$this->img->selectPage( $page - 1 );
|
||||
$thumb1 = $sk->makeThumbLinkObj( $this->img, $link, $label, 'none' );
|
||||
$thumb1 = $sk->makeThumbLinkObj( $this->img, $link, $label, 'none',
|
||||
array( 'page' => $page - 1 ) );
|
||||
} else {
|
||||
$thumb1 = '';
|
||||
}
|
||||
|
||||
if ( $page < $count ) {
|
||||
$label = wfMsg( 'imgmultipagenext' );
|
||||
$this->img->selectPage( $page + 1 );
|
||||
$link = $sk->makeLinkObj( $this->mTitle, $label, 'page='. ($page+1) );
|
||||
$thumb2 = $sk->makeThumbLinkObj( $this->img, $link, $label, 'none' );
|
||||
$thumb2 = $sk->makeThumbLinkObj( $this->img, $link, $label, 'none',
|
||||
array( 'page' => $page + 1 ) );
|
||||
} else {
|
||||
$thumb2 = '';
|
||||
}
|
||||
|
|
@ -294,7 +288,7 @@ class ImagePage extends Article {
|
|||
htmlspecialchars( wfMsg( 'imgmultigo' ) ) . '"></form>';
|
||||
|
||||
$wgOut->addHTML( '</td><td><div class="multipageimagenavbox">' .
|
||||
"$select<hr />$thumb1\n$thumb2<br clear=\"all\" /></div></td></tr></table>" );
|
||||
"$select<hr />$thumb1\n$thumb2<br clear=\"all\" /></div></td></tr></table>" );
|
||||
}
|
||||
} else {
|
||||
#if direct link is allowed but it's not a renderable image, show an icon.
|
||||
|
|
|
|||
|
|
@ -429,25 +429,19 @@ class Linker {
|
|||
}
|
||||
|
||||
/** @todo document */
|
||||
function makeImageLinkObj( $nt, $label, $alt, $align = '', $width = false, $height = false, $framed = false,
|
||||
$thumb = false, $manual_thumb = '', $page = null, $valign = '' )
|
||||
function makeImageLinkObj( $nt, $label, $alt, $align = '', $params = array(), $framed = false,
|
||||
$thumb = false, $manual_thumb = '', $valign = '' )
|
||||
{
|
||||
global $wgContLang, $wgUser, $wgThumbLimits, $wgGenerateThumbnailOnParse;
|
||||
global $wgContLang, $wgUser, $wgThumbLimits;
|
||||
|
||||
$img = new Image( $nt );
|
||||
|
||||
if ( ! is_null( $page ) ) {
|
||||
$img->selectPage( $page );
|
||||
}
|
||||
|
||||
if ( !$img->allowInlineDisplay() && $img->exists() ) {
|
||||
return $this->makeKnownLinkObj( $nt );
|
||||
}
|
||||
|
||||
$url = $img->getViewURL();
|
||||
$error = $prefix = $postfix = '';
|
||||
|
||||
wfDebug( "makeImageLinkObj: '$width'x'$height', \"$label\"\n" );
|
||||
$page = isset( $params['page'] ) ? $params['page'] : false;
|
||||
|
||||
if ( 'center' == $align )
|
||||
{
|
||||
|
|
@ -456,6 +450,16 @@ class Linker {
|
|||
$align = 'none';
|
||||
}
|
||||
|
||||
if ( !isset( $params['width'] ) ) {
|
||||
$wopt = $wgUser->getOption( 'thumbsize' );
|
||||
|
||||
if( !isset( $wgThumbLimits[$wopt] ) ) {
|
||||
$wopt = User::getDefaultOption( 'thumbsize' );
|
||||
}
|
||||
|
||||
$params['width'] = min( $img->getWidth( $page ), $wgThumbLimits[$wopt] );
|
||||
}
|
||||
|
||||
if ( $thumb || $framed ) {
|
||||
|
||||
# Create a thumbnail. Alignment depends on language
|
||||
|
|
@ -468,73 +472,39 @@ class Linker {
|
|||
if ( $align == '' ) {
|
||||
$align = $wgContLang->isRTL() ? 'left' : 'right';
|
||||
}
|
||||
|
||||
|
||||
if ( $width === false ) {
|
||||
$wopt = $wgUser->getOption( 'thumbsize' );
|
||||
|
||||
if( !isset( $wgThumbLimits[$wopt] ) ) {
|
||||
$wopt = User::getDefaultOption( 'thumbsize' );
|
||||
}
|
||||
|
||||
$width = min( $img->getWidth(), $wgThumbLimits[$wopt] );
|
||||
}
|
||||
|
||||
return $prefix.$this->makeThumbLinkObj( $img, $label, $alt, $align, $width, $height, $framed, $manual_thumb ).$postfix;
|
||||
return $prefix.$this->makeThumbLinkObj( $img, $label, $alt, $align, $params, $framed, $manual_thumb ).$postfix;
|
||||
}
|
||||
|
||||
if ( $width && $img->exists() ) {
|
||||
|
||||
# Create a resized image, without the additional thumbnail
|
||||
# features
|
||||
|
||||
if ( $height == false )
|
||||
$height = -1;
|
||||
if ( $manual_thumb == '') {
|
||||
$thumb = $img->getThumbnail( $width, $height, $wgGenerateThumbnailOnParse );
|
||||
if ( $thumb ) {
|
||||
// In most cases, $width = $thumb->width or $height = $thumb->height.
|
||||
// If not, we're scaling the image larger than it can be scaled,
|
||||
// so we send to the browser a smaller thumbnail, and let the client do the scaling.
|
||||
|
||||
if ($height != -1 && $width > $thumb->width * $height / $thumb->height) {
|
||||
// $height is the limiting factor, not $width
|
||||
// set $width to the largest it can be, such that the resulting
|
||||
// scaled height is at most $height
|
||||
$width = floor($thumb->width * $height / $thumb->height);
|
||||
}
|
||||
$height = round($thumb->height * $width / $thumb->width);
|
||||
|
||||
wfDebug( "makeImageLinkObj: client-size set to '$width x $height'\n" );
|
||||
$url = $thumb->getUrl();
|
||||
} else {
|
||||
$error = htmlspecialchars( $img->getLastError() );
|
||||
// Do client-side scaling...
|
||||
$height = intval( $img->getHeight() * $width / $img->getWidth() );
|
||||
}
|
||||
}
|
||||
if ( $params['width'] && $img->exists() ) {
|
||||
# Create a resized image, without the additional thumbnail features
|
||||
$thumb = $img->transform( $params );
|
||||
} else {
|
||||
$width = $img->width;
|
||||
$height = $img->height;
|
||||
$thumb = false;
|
||||
}
|
||||
|
||||
wfDebug( "makeImageLinkObj2: '$width'x'$height'\n" );
|
||||
$u = $nt->escapeLocalURL();
|
||||
if ( $error ) {
|
||||
$s = $error;
|
||||
} elseif ( $url == '' ) {
|
||||
if ( $page ) {
|
||||
$query = 'page=' . urlencode( $page );
|
||||
} else {
|
||||
$query = '';
|
||||
}
|
||||
$u = $nt->getLocalURL( $query );
|
||||
$imgAttribs = array(
|
||||
'alt' => $alt,
|
||||
'longdesc' => $u
|
||||
);
|
||||
if ( $valign ) {
|
||||
$imgAttribs['style'] = "vertical-align: $valign";
|
||||
}
|
||||
$linkAttribs = array(
|
||||
'href' => $u,
|
||||
'class' => 'image',
|
||||
'title' => $alt
|
||||
);
|
||||
|
||||
if ( !$thumb ) {
|
||||
$s = $this->makeBrokenImageLinkObj( $img->getTitle() );
|
||||
//$s .= "<br />{$alt}<br />{$url}<br />\n";
|
||||
} else {
|
||||
$s = '<a href="'.$u.'" class="image" title="'.$alt.'">' .
|
||||
'<img src="'.$url.'" alt="'.$alt.'" ' .
|
||||
( $width
|
||||
? ( 'width="'.$width.'" height="'.$height.'" ' )
|
||||
: '' ) .
|
||||
( $valign
|
||||
? ( 'style="vertical-align: '.$valign.'" ' )
|
||||
: '' ) .
|
||||
'longdesc="'.$u.'" /></a>';
|
||||
$s = $thumb->toHtml( $imgAttribs, $linkAttribs );
|
||||
}
|
||||
if ( '' != $align ) {
|
||||
$s = "<div class=\"float{$align}\"><span>{$s}</span></div>";
|
||||
|
|
@ -546,86 +516,64 @@ class Linker {
|
|||
* Make HTML for a thumbnail including image, border and caption
|
||||
* $img is an Image object
|
||||
*/
|
||||
function makeThumbLinkObj( $img, $label = '', $alt, $align = 'right', $boxwidth = 180, $boxheight=false, $framed=false , $manual_thumb = "" ) {
|
||||
global $wgStylePath, $wgContLang, $wgGenerateThumbnailOnParse;
|
||||
function makeThumbLinkObj( $img, $label = '', $alt, $align = 'right', $params = array(), $framed=false , $manual_thumb = "" ) {
|
||||
global $wgStylePath, $wgContLang;
|
||||
$thumbUrl = '';
|
||||
$error = '';
|
||||
|
||||
$width = $height = 0;
|
||||
if ( $img->exists() ) {
|
||||
$width = $img->getWidth();
|
||||
$height = $img->getHeight();
|
||||
}
|
||||
if ( 0 == $width || 0 == $height ) {
|
||||
$width = $height = 180;
|
||||
}
|
||||
if ( $boxwidth == 0 ) {
|
||||
$boxwidth = 180;
|
||||
}
|
||||
if ( $framed ) {
|
||||
// Use image dimensions, don't scale
|
||||
$boxwidth = $width;
|
||||
$boxheight = $height;
|
||||
$thumbUrl = $img->getViewURL();
|
||||
} else {
|
||||
if ( $boxheight === false )
|
||||
$boxheight = -1;
|
||||
if ( '' == $manual_thumb ) {
|
||||
$thumb = $img->getThumbnail( $boxwidth, $boxheight, $wgGenerateThumbnailOnParse );
|
||||
if ( $thumb ) {
|
||||
$thumbUrl = $thumb->getUrl();
|
||||
$boxwidth = $thumb->width;
|
||||
$boxheight = $thumb->height;
|
||||
} else {
|
||||
$error = $img->getLastError();
|
||||
}
|
||||
}
|
||||
}
|
||||
$oboxwidth = $boxwidth + 2;
|
||||
$page = isset( $params['page'] ) ? $params['page'] : false;
|
||||
|
||||
if ( $manual_thumb != '' ) # Use manually specified thumbnail
|
||||
{
|
||||
$manual_title = Title::makeTitleSafe( NS_IMAGE, $manual_thumb ); #new Title ( $manual_thumb ) ;
|
||||
if ( empty( $params['width'] ) ) {
|
||||
$params['width'] = 180;
|
||||
}
|
||||
$thumb = false;
|
||||
if ( $manual_thumb != '' ) {
|
||||
# Use manually specified thumbnail
|
||||
$manual_title = Title::makeTitleSafe( NS_IMAGE, $manual_thumb );
|
||||
if( $manual_title ) {
|
||||
$manual_img = new Image( $manual_title );
|
||||
$thumbUrl = $manual_img->getViewURL();
|
||||
if ( $manual_img->exists() )
|
||||
{
|
||||
$width = $manual_img->getWidth();
|
||||
$height = $manual_img->getHeight();
|
||||
$boxwidth = $width ;
|
||||
$boxheight = $height ;
|
||||
$oboxwidth = $boxwidth + 2 ;
|
||||
}
|
||||
$thumb = $manual_img->getUnscaledThumb();
|
||||
}
|
||||
} elseif ( $framed ) {
|
||||
// Use image dimensions, don't scale
|
||||
$thumb = $img->getUnscaledThumb( $page );
|
||||
} else {
|
||||
$thumb = $img->transform( $params );
|
||||
}
|
||||
|
||||
$u = $img->getEscapeLocalURL();
|
||||
if ( $thumb ) {
|
||||
$outerWidth = $thumb->getWidth() + 2;
|
||||
} else {
|
||||
$outerWidth = $params['width'] + 2;
|
||||
}
|
||||
|
||||
$query = $page ? 'page=' . urlencode( $page ) : '';
|
||||
$u = $img->getTitle()->getLocalURL( $query );
|
||||
|
||||
$more = htmlspecialchars( wfMsg( 'thumbnail-more' ) );
|
||||
$magnifyalign = $wgContLang->isRTL() ? 'left' : 'right';
|
||||
$textalign = $wgContLang->isRTL() ? ' style="text-align:right"' : '';
|
||||
|
||||
$s = "<div class=\"thumb t{$align}\"><div class=\"thumbinner\" style=\"width:{$oboxwidth}px;\">";
|
||||
if( $thumbUrl == '' ) {
|
||||
// Couldn't generate thumbnail? Scale the image client-side.
|
||||
$thumbUrl = $img->getViewURL();
|
||||
if( $boxheight == -1 ) {
|
||||
// Approximate...
|
||||
$boxheight = round( $height * $boxwidth / $width );
|
||||
}
|
||||
}
|
||||
if ( $error ) {
|
||||
$s .= htmlspecialchars( $error );
|
||||
$s = "<div class=\"thumb t{$align}\"><div class=\"thumbinner\" style=\"width:{$outerWidth}px;\">";
|
||||
if ( !$thumb ) {
|
||||
$s .= htmlspecialchars( wfMsg( 'thumbnail_error', '' ) );
|
||||
$zoomicon = '';
|
||||
} elseif( !$img->exists() ) {
|
||||
$s .= $this->makeBrokenImageLinkObj( $img->getTitle() );
|
||||
$zoomicon = '';
|
||||
} else {
|
||||
$s .= '<a href="'.$u.'" class="internal" title="'.$alt.'">'.
|
||||
'<img src="'.$thumbUrl.'" alt="'.$alt.'" ' .
|
||||
'width="'.$boxwidth.'" height="'.$boxheight.'" ' .
|
||||
'longdesc="'.$u.'" class="thumbimage" /></a>';
|
||||
$imgAttribs = array(
|
||||
'alt' => $alt,
|
||||
'longdesc' => $u,
|
||||
'class' => 'thumbimage'
|
||||
);
|
||||
$linkAttribs = array(
|
||||
'href' => $u,
|
||||
'title' => $alt,
|
||||
'class' => 'internal'
|
||||
);
|
||||
|
||||
$s .= $thumb->toHtml( $imgAttribs, $linkAttribs );
|
||||
if ( $framed ) {
|
||||
$zoomicon="";
|
||||
} else {
|
||||
|
|
|
|||
158
includes/MediaTransformOutput.php
Normal file
158
includes/MediaTransformOutput.php
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Base class for the output of MediaHandler::doTransform() and Image::transform().
|
||||
*/
|
||||
abstract class MediaTransformOutput {
|
||||
/**
|
||||
* Get the width of the output box
|
||||
*/
|
||||
function getWidth() {
|
||||
return $this->width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the height of the output box
|
||||
*/
|
||||
function getHeight() {
|
||||
return $this->height;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string The thumbnail URL
|
||||
*/
|
||||
function getUrl() {
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Destination file path (local filesystem)
|
||||
*/
|
||||
function getPath() {
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch HTML for this transform output
|
||||
* @param array $attribs Advisory associative array of HTML attributes supplied
|
||||
* by the linker. These can be incorporated into the output in any way.
|
||||
* @param array $linkAttribs Attributes of a suggested enclosing <a> tag.
|
||||
* May be ignored.
|
||||
*/
|
||||
abstract function toHtml( $attribs = array() , $linkAttribs = false );
|
||||
|
||||
/**
|
||||
* This will be overridden to return true in error classes
|
||||
*/
|
||||
function isError() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap some XHTML text in an anchor tag with the given attributes
|
||||
*/
|
||||
protected function linkWrap( $linkAttribs, $contents ) {
|
||||
if ( $linkAttribs ) {
|
||||
return Xml::tags( 'a', $linkAttribs, $contents );
|
||||
} else {
|
||||
return $contents;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Media transform output for images
|
||||
*/
|
||||
class ThumbnailImage extends MediaTransformOutput {
|
||||
/**
|
||||
* @param string $path Filesystem path to the thumb
|
||||
* @param string $url URL path to the thumb
|
||||
* @private
|
||||
*/
|
||||
function ThumbnailImage( $url, $width, $height, $path = false ) {
|
||||
$this->url = $url;
|
||||
# These should be integers when they get here.
|
||||
# If not, there's a bug somewhere. But let's at
|
||||
# least produce valid HTML code regardless.
|
||||
$this->width = round( $width );
|
||||
$this->height = round( $height );
|
||||
$this->path = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return HTML <img ... /> tag for the thumbnail, will include
|
||||
* width and height attributes and a blank alt text (as required).
|
||||
*
|
||||
* You can set or override additional attributes by passing an
|
||||
* associative array of name => data pairs. The data will be escaped
|
||||
* for HTML output, so should be in plaintext.
|
||||
*
|
||||
* If $linkAttribs is given, the image will be enclosed in an <a> tag.
|
||||
*
|
||||
* @param array $attribs
|
||||
* @param array $linkAttribs
|
||||
* @return string
|
||||
* @public
|
||||
*/
|
||||
function toHtml( $attribs = array(), $linkAttribs = false ) {
|
||||
$attribs['src'] = $this->url;
|
||||
$attribs['width'] = $this->width;
|
||||
$attribs['height'] = $this->height;
|
||||
if( !isset( $attribs['alt'] ) ) $attribs['alt'] = '';
|
||||
return $this->linkWrap( $linkAttribs, Xml::element( 'img', $attribs ) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic media transform error class
|
||||
*/
|
||||
class MediaTransformError extends MediaTransformOutput {
|
||||
var $htmlMsg, $textMsg, $width, $height, $url, $path;
|
||||
|
||||
function __construct( $msg, $width, $height /*, ... */ ) {
|
||||
$args = array_slice( func_get_args(), 3 );
|
||||
$htmlArgs = array_map( 'htmlspecialchars', $args );
|
||||
$htmlArgs = array_map( 'nl2br', $htmlArgs );
|
||||
|
||||
$this->htmlMsg = wfMsgReplaceArgs( htmlspecialchars( wfMsgGetKey( $msg, true ) ), $htmlArgs );
|
||||
$this->textMsg = wfMsgReal( $msg, $args );
|
||||
$this->width = intval( $width );
|
||||
$this->height = intval( $height );
|
||||
$this->url = false;
|
||||
$this->path = false;
|
||||
}
|
||||
|
||||
function toHtml( $attribs = array(), $linkAttribs = false ) {
|
||||
return "<table class=\"MediaTransformError\" style=\"" .
|
||||
"width: {$this->width}px; height: {$this->height}px;\"><tr><td>" .
|
||||
$this->htmlMsg .
|
||||
"</td></tr></table>";
|
||||
}
|
||||
|
||||
function toText() {
|
||||
return $this->textMsg;
|
||||
}
|
||||
|
||||
function getHtmlMsg() {
|
||||
return $this->htmlMsg;
|
||||
}
|
||||
|
||||
function isError() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut class for parameter validation errors
|
||||
*/
|
||||
class TransformParameterError extends MediaTransformError {
|
||||
function __construct( $params ) {
|
||||
parent::__construct( 'thumbnail_error',
|
||||
max( @$params['width'], 180 ), max( @$params['height'], 180 ),
|
||||
wfMsg( 'thumbnail_invalid_params' ) );
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
@ -22,7 +22,7 @@ image/x-bmp bmp
|
|||
image/gif gif
|
||||
image/jpeg jpeg jpg jpe
|
||||
image/png png
|
||||
image/svg+xml svg
|
||||
image/svg+xml image/svg svg
|
||||
image/tiff tiff tif
|
||||
image/vnd.djvu djvu
|
||||
image/x-portable-pixmap ppm
|
||||
|
|
@ -51,7 +51,7 @@ image/x-bmp image/bmp [BITMAP]
|
|||
image/gif [BITMAP]
|
||||
image/jpeg [BITMAP]
|
||||
image/png [BITMAP]
|
||||
image/svg image/svg+xml [DRAWING]
|
||||
image/svg+xml [DRAWING]
|
||||
image/tiff [BITMAP]
|
||||
image/vnd.djvu [BITMAP]
|
||||
image/x-portable-pixmap [BITMAP]
|
||||
|
|
|
|||
|
|
@ -4387,8 +4387,8 @@ class Parser
|
|||
* Parse image options text and use it to make an image
|
||||
*/
|
||||
function makeImage( $nt, $options ) {
|
||||
global $wgUseImageResize, $wgDjvuRenderer;
|
||||
|
||||
# @TODO: let the MediaHandler specify its transform parameters
|
||||
#
|
||||
# Check if the options text is of the form "options|alt text"
|
||||
# Options are:
|
||||
# * thumbnail make a thumbnail with enlarge-icon and caption, alignment depends on lang
|
||||
|
|
@ -4408,6 +4408,7 @@ class Parser
|
|||
# * bottom
|
||||
# * text-bottom
|
||||
|
||||
|
||||
$part = array_map( 'trim', explode( '|', $options) );
|
||||
|
||||
$mwAlign = array();
|
||||
|
|
@ -4422,13 +4423,14 @@ class Parser
|
|||
$mwPage =& MagicWord::get( 'img_page' );
|
||||
$caption = '';
|
||||
|
||||
$width = $height = $framed = $thumb = false;
|
||||
$page = null;
|
||||
$params = array();
|
||||
$framed = $thumb = false;
|
||||
$manual_thumb = '' ;
|
||||
$align = $valign = '';
|
||||
$sk = $this->mOptions->getSkin();
|
||||
|
||||
foreach( $part as $val ) {
|
||||
if ( $wgUseImageResize && ! is_null( $mwThumb->matchVariableStartToEnd($val) ) ) {
|
||||
if ( !is_null( $mwThumb->matchVariableStartToEnd($val) ) ) {
|
||||
$thumb=true;
|
||||
} elseif ( ! is_null( $match = $mwManualThumb->matchVariableStartToEnd($val) ) ) {
|
||||
# use manually specified thumbnail
|
||||
|
|
@ -4446,19 +4448,18 @@ class Parser
|
|||
continue 2;
|
||||
}
|
||||
}
|
||||
if ( isset( $wgDjvuRenderer ) && $wgDjvuRenderer
|
||||
&& ! is_null( $match = $mwPage->matchVariableStartToEnd($val) ) ) {
|
||||
if ( ! is_null( $match = $mwPage->matchVariableStartToEnd($val) ) ) {
|
||||
# Select a page in a multipage document
|
||||
$page = $match;
|
||||
} elseif ( $wgUseImageResize && !$width && ! is_null( $match = $mwWidth->matchVariableStartToEnd($val) ) ) {
|
||||
$params['page'] = $match;
|
||||
} elseif ( !isset( $params['width'] ) && ! is_null( $match = $mwWidth->matchVariableStartToEnd($val) ) ) {
|
||||
wfDebug( "img_width match: $match\n" );
|
||||
# $match is the image width in pixels
|
||||
$m = array();
|
||||
if ( preg_match( '/^([0-9]*)x([0-9]*)$/', $match, $m ) ) {
|
||||
$width = intval( $m[1] );
|
||||
$height = intval( $m[2] );
|
||||
$params['width'] = intval( $m[1] );
|
||||
$params['height'] = intval( $m[2] );
|
||||
} else {
|
||||
$width = intval($match);
|
||||
$params['width'] = intval($match);
|
||||
}
|
||||
} elseif ( ! is_null( $mwFramed->matchVariableStartToEnd($val) ) ) {
|
||||
$framed=true;
|
||||
|
|
@ -4477,8 +4478,7 @@ class Parser
|
|||
$alt = Sanitizer::stripAllTags( $alt );
|
||||
|
||||
# Linker does the rest
|
||||
$sk = $this->mOptions->getSkin();
|
||||
return $sk->makeImageLinkObj( $nt, $caption, $alt, $align, $width, $height, $framed, $thumb, $manual_thumb, $page, $valign );
|
||||
return $sk->makeImageLinkObj( $nt, $caption, $alt, $align, $params, $framed, $thumb, $manual_thumb, $valign );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
226
includes/media/Bitmap.php
Normal file
226
includes/media/Bitmap.php
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
<?php
|
||||
|
||||
class BitmapHandler extends ImageHandler {
|
||||
function normaliseParams( $image, &$params ) {
|
||||
global $wgMaxImageArea;
|
||||
if ( !parent::normaliseParams( $image, $params ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$mimeType = $image->getMimeType();
|
||||
$srcWidth = $image->getWidth( $params['page'] );
|
||||
$srcHeight = $image->getHeight( $params['page'] );
|
||||
|
||||
# Don't thumbnail an image so big that it will fill hard drives and send servers into swap
|
||||
# JPEG has the handy property of allowing thumbnailing without full decompression, so we make
|
||||
# an exception for it.
|
||||
if ( $mimeType !== 'image/jpeg' &&
|
||||
$srcWidth * $srcHeight > $wgMaxImageArea )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
# Don't make an image bigger than the source
|
||||
$params['physicalWidth'] = $params['width'];
|
||||
$params['physicalHeight'] = $params['height'];
|
||||
|
||||
if ( $params['physicalWidth'] >= $srcWidth ) {
|
||||
$params['physicalWidth'] = $srcWidth;
|
||||
$params['physicalHeight'] = $srcHeight;
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 ) {
|
||||
global $wgUseImageMagick, $wgImageMagickConvertCommand;
|
||||
global $wgCustomConvertCommand;
|
||||
global $wgSharpenParameter, $wgSharpenReductionThreshold;
|
||||
|
||||
if ( !$this->normaliseParams( $image, $params ) ) {
|
||||
return new TransformParameterError( $params );
|
||||
}
|
||||
$physicalWidth = $params['physicalWidth'];
|
||||
$physicalHeight = $params['physicalHeight'];
|
||||
$clientWidth = $params['width'];
|
||||
$clientHeight = $params['height'];
|
||||
$srcWidth = $image->getWidth();
|
||||
$srcHeight = $image->getHeight();
|
||||
$mimeType = $image->getMimeType();
|
||||
$srcPath = $image->getImagePath();
|
||||
$retval = 0;
|
||||
wfDebug( __METHOD__.": creating {$physicalWidth}x{$physicalHeight} thumbnail at $dstPath\n" );
|
||||
|
||||
if ( $physicalWidth == $srcWidth && $physicalHeight == $srcHeight ) {
|
||||
# normaliseParams (or the user) wants us to return the unscaled image
|
||||
wfDebug( __METHOD__.": returning unscaled image\n" );
|
||||
return new ThumbnailImage( $image->getURL(), $clientWidth, $clientHeight, $srcPath );
|
||||
}
|
||||
|
||||
if ( $wgUseImageMagick ) {
|
||||
$scaler = 'im';
|
||||
} elseif ( $wgCustomConvertCommand ) {
|
||||
$scaler = 'custom';
|
||||
} elseif ( function_exists( 'imagecreatetruecolor' ) ) {
|
||||
$scaler = 'gd';
|
||||
} else {
|
||||
$scaler = 'client';
|
||||
}
|
||||
|
||||
if ( $scaler == 'client' ) {
|
||||
# Client-side image scaling, use the source URL
|
||||
# Using the destination URL in a TRANSFORM_LATER request would be incorrect
|
||||
return new ThumbnailImage( $image->getURL(), $clientWidth, $clientHeight, $srcPath );
|
||||
}
|
||||
|
||||
if ( $flags & self::TRANSFORM_LATER ) {
|
||||
return new ThumbnailImage( $dstUrl, $clientWidth, $clientHeight, $dstPath );
|
||||
}
|
||||
|
||||
if ( !wfMkdirParents( dirname( $dstPath ) ) ) {
|
||||
return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight,
|
||||
wfMsg( 'thumbnail_dest_directory' ) );
|
||||
}
|
||||
|
||||
if ( $scaler == 'im' ) {
|
||||
# use ImageMagick
|
||||
|
||||
$sharpen = '';
|
||||
if ( $mimeType == 'image/jpeg' ) {
|
||||
$quality = "-quality 80"; // 80%
|
||||
# Sharpening, see bug 6193
|
||||
if ( ( $physicalWidth + $physicalHeight ) / ( $srcWidth + $srcHeight ) < $wgSharpenReductionThreshold ) {
|
||||
$sharpen = "-sharpen " . wfEscapeShellArg( $wgSharpenParameter );
|
||||
}
|
||||
} elseif ( $mimeType == 'image/png' ) {
|
||||
$quality = "-quality 95"; // zlib 9, adaptive filtering
|
||||
} else {
|
||||
$quality = ''; // default
|
||||
}
|
||||
|
||||
# Specify white background color, will be used for transparent images
|
||||
# in Internet Explorer/Windows instead of default black.
|
||||
|
||||
# Note, we specify "-size {$physicalWidth}" and NOT "-size {$physicalWidth}x{$physicalHeight}".
|
||||
# It seems that ImageMagick has a bug wherein it produces thumbnails of
|
||||
# the wrong size in the second case.
|
||||
|
||||
$cmd = wfEscapeShellArg($wgImageMagickConvertCommand) .
|
||||
" {$quality} -background white -size {$physicalWidth} ".
|
||||
wfEscapeShellArg($srcPath) .
|
||||
// Coalesce is needed to scale animated GIFs properly (bug 1017).
|
||||
' -coalesce ' .
|
||||
// For the -resize option a "!" is needed to force exact size,
|
||||
// or ImageMagick may decide your ratio is wrong and slice off
|
||||
// a pixel.
|
||||
" -thumbnail " . wfEscapeShellArg( "{$physicalWidth}x{$physicalHeight}!" ) .
|
||||
" -depth 8 $sharpen " .
|
||||
wfEscapeShellArg($dstPath) . " 2>&1";
|
||||
wfDebug( __METHOD__.": running ImageMagick: $cmd\n");
|
||||
wfProfileIn( 'convert' );
|
||||
$err = wfShellExec( $cmd, $retval );
|
||||
wfProfileOut( 'convert' );
|
||||
} elseif( $scaler == 'custom' ) {
|
||||
# Use a custom convert command
|
||||
# Variables: %s %d %w %h
|
||||
$src = wfEscapeShellArg( $srcPath );
|
||||
$dst = wfEscapeShellArg( $dstPath );
|
||||
$cmd = $wgCustomConvertCommand;
|
||||
$cmd = str_replace( '%s', $src, str_replace( '%d', $dst, $cmd ) ); # Filenames
|
||||
$cmd = str_replace( '%h', $physicalHeight, str_replace( '%w', $physicalWidth, $cmd ) ); # Size
|
||||
wfDebug( __METHOD__.": Running custom convert command $cmd\n" );
|
||||
wfProfileIn( 'convert' );
|
||||
$err = wfShellExec( $cmd, $retval );
|
||||
wfProfileOut( 'convert' );
|
||||
} else /* $scaler == 'gd' */ {
|
||||
# Use PHP's builtin GD library functions.
|
||||
#
|
||||
# First find out what kind of file this is, and select the correct
|
||||
# input routine for this.
|
||||
|
||||
$typemap = array(
|
||||
'image/gif' => array( 'imagecreatefromgif', 'palette', 'imagegif' ),
|
||||
'image/jpeg' => array( 'imagecreatefromjpeg', 'truecolor', array( __CLASS__, 'imageJpegWrapper' ) ),
|
||||
'image/png' => array( 'imagecreatefrompng', 'bits', 'imagepng' ),
|
||||
'image/vnd.wap.wbmp' => array( 'imagecreatefromwbmp', 'palette', 'imagewbmp' ),
|
||||
'image/xbm' => array( 'imagecreatefromxbm', 'palette', 'imagexbm' ),
|
||||
);
|
||||
if( !isset( $typemap[$mimeType] ) ) {
|
||||
$err = 'Image type not supported';
|
||||
wfDebug( "$err\n" );
|
||||
return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight, $err );
|
||||
}
|
||||
list( $loader, $colorStyle, $saveType ) = $typemap[$mimeType];
|
||||
|
||||
if( !function_exists( $loader ) ) {
|
||||
$err = "Incomplete GD library configuration: missing function $loader";
|
||||
wfDebug( "$err\n" );
|
||||
return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight, $err );
|
||||
}
|
||||
|
||||
$src_image = call_user_func( $loader, $srcPath );
|
||||
$dst_image = imagecreatetruecolor( $physicalWidth, $physicalHeight );
|
||||
imagecopyresampled( $dst_image, $src_image,
|
||||
0,0,0,0,
|
||||
$physicalWidth, $physicalHeight, imagesx( $src_image ), imagesy( $src_image ) );
|
||||
call_user_func( $saveType, $dst_image, $dstPath );
|
||||
imagedestroy( $dst_image );
|
||||
imagedestroy( $src_image );
|
||||
$retval = 0;
|
||||
}
|
||||
|
||||
$removed = $this->removeBadFile( $dstPath, $retval );
|
||||
if ( $retval != 0 || $removed ) {
|
||||
wfDebugLog( 'thumbnail',
|
||||
sprintf( 'thumbnail failed on %s: error %d "%s" from "%s"',
|
||||
wfHostname(), $retval, trim($err), $cmd ) );
|
||||
return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight, $err );
|
||||
} else {
|
||||
return new ThumbnailImage( $dstUrl, $clientWidth, $clientHeight, $dstPath );
|
||||
}
|
||||
}
|
||||
|
||||
static function imageJpegWrapper( $dst_image, $thumbPath ) {
|
||||
imageinterlace( $dst_image );
|
||||
imagejpeg( $dst_image, $thumbPath, 95 );
|
||||
}
|
||||
|
||||
|
||||
function getMetadata( $image, $filename ) {
|
||||
global $wgShowEXIF;
|
||||
if( $wgShowEXIF && file_exists( $filename ) ) {
|
||||
$exif = new Exif( $filename );
|
||||
return serialize( $exif->getFilteredData() );
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function getMetadataType( $image ) {
|
||||
return 'exif';
|
||||
}
|
||||
|
||||
function isMetadataValid( $image, $metadata ) {
|
||||
global $wgShowEXIF;
|
||||
if ( !$wgShowEXIF ) {
|
||||
# Metadata disabled and so an empty field is expected
|
||||
return true;
|
||||
}
|
||||
if ( $metadata === '0' ) {
|
||||
# Special value indicating that there is no EXIF data in the file
|
||||
return true;
|
||||
}
|
||||
$exif = @unserialize( $metadata );
|
||||
if ( !isset( $exif['MEDIAWIKI_EXIF_VERSION'] ) ||
|
||||
$exif['MEDIAWIKI_EXIF_VERSION'] != Exif::version() )
|
||||
{
|
||||
# Wrong version
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
203
includes/media/DjVu.php
Normal file
203
includes/media/DjVu.php
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
<?php
|
||||
|
||||
class DjVuHandler extends ImageHandler {
|
||||
function isEnabled() {
|
||||
global $wgDjvuRenderer, $wgDjvuDump, $wgDjvuToXML;
|
||||
if ( !$wgDjvuRenderer || ( !$wgDjvuDump && !$wgDjvuToXML ) ) {
|
||||
wfDebug( "DjVu is disabled, please set \$wgDjvuRenderer and \$wgDjvuDump\n" );
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function mustRender() { return true; }
|
||||
function isMultiPage() { return true; }
|
||||
|
||||
function validateParam( $name, $value ) {
|
||||
if ( in_array( $name, array( 'width', 'height', 'page' ) ) ) {
|
||||
if ( $value <= 0 ) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function makeParamString( $params ) {
|
||||
$page = isset( $params['page'] ) ? $params['page'] : 1;
|
||||
if ( !isset( $params['width'] ) ) {
|
||||
return false;
|
||||
}
|
||||
return "{$params['width']}px-page{$page}";
|
||||
}
|
||||
|
||||
function parseParamString( $str ) {
|
||||
$m = false;
|
||||
if ( preg_match( '/^(\d+)px-page(\d+)$/', $str, $m ) ) {
|
||||
return array( 'width' => $m[1], 'page' => $m[2] );
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function getScriptParams( $params ) {
|
||||
return array(
|
||||
'width' => $params['width'],
|
||||
'page' => $params['page'],
|
||||
);
|
||||
}
|
||||
|
||||
function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 ) {
|
||||
global $wgDjvuRenderer, $wgDjvuPostProcessor;
|
||||
|
||||
// Fetch XML and check it, to give a more informative error message than the one which
|
||||
// normaliseParams will inevitably give.
|
||||
$xml = $image->getMetadata();
|
||||
if ( !$xml ) {
|
||||
return new MediaTransformError( 'thumbnail_error', @$params['width'], @$params['height'],
|
||||
wfMsg( 'djvu_no_xml' ) );
|
||||
}
|
||||
|
||||
if ( !$this->normaliseParams( $image, $params ) ) {
|
||||
return new TransformParameterError( $params );
|
||||
}
|
||||
$width = $params['width'];
|
||||
$height = $params['height'];
|
||||
$srcPath = $image->getImagePath();
|
||||
$page = $params['page'];
|
||||
$pageCount = $this->pageCount( $image );
|
||||
if ( $page > $this->pageCount( $image ) ) {
|
||||
return new MediaTransformError( 'thumbnail_error', $width, $height, wfMsg( 'djvu_page_error' ) );
|
||||
}
|
||||
|
||||
if ( $flags & self::TRANSFORM_LATER ) {
|
||||
return new ThumbnailImage( $dstUrl, $width, $height, $dstPath );
|
||||
}
|
||||
|
||||
if ( !wfMkdirParents( dirname( $dstPath ) ) ) {
|
||||
return new MediaTransformError( 'thumbnail_error', $width, $height, wfMsg( 'thumbnail_dest_directory' ) );
|
||||
}
|
||||
|
||||
# Use a subshell (brackets) to aggregate stderr from both pipeline commands
|
||||
# before redirecting it to the overall stdout. This works in both Linux and Windows XP.
|
||||
$cmd = '(' . wfEscapeShellArg( $wgDjvuRenderer ) . " -format=ppm -page={$page} -size={$width}x{$height} " .
|
||||
wfEscapeShellArg( $srcPath );
|
||||
if ( $wgDjvuPostProcessor ) {
|
||||
$cmd .= " | {$wgDjvuPostProcessor}";
|
||||
}
|
||||
$cmd .= ' > ' . wfEscapeShellArg($dstPath) . ') 2>&1';
|
||||
wfProfileIn( 'ddjvu' );
|
||||
wfDebug( __METHOD__.": $cmd\n" );
|
||||
$err = wfShellExec( $cmd, $retval );
|
||||
wfProfileOut( 'ddjvu' );
|
||||
|
||||
$removed = $this->removeBadFile( $dstPath, $retval );
|
||||
if ( $retval != 0 || $removed ) {
|
||||
wfDebugLog( 'thumbnail',
|
||||
sprintf( 'thumbnail failed on %s: error %d "%s" from "%s"',
|
||||
wfHostname(), $retval, trim($err), $cmd ) );
|
||||
return new MediaTransformError( 'thumbnail_error', $width, $height, $err );
|
||||
} else {
|
||||
return new ThumbnailImage( $dstUrl, $width, $height, $dstPath );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache an instance of DjVuImage in an Image object, return that instance
|
||||
*/
|
||||
function getDjVuImage( $image, $path ) {
|
||||
if ( !$image ) {
|
||||
$deja = new DjVuImage( $path );
|
||||
} elseif ( !isset( $image->dejaImage ) ) {
|
||||
$deja = $image->dejaImage = new DjVuImage( $path );
|
||||
} else {
|
||||
$deja = $image->dejaImage;
|
||||
}
|
||||
return $deja;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache a document tree for the DjVu XML metadata
|
||||
*/
|
||||
function getMetaTree( $image ) {
|
||||
if ( isset( $image->dejaMetaTree ) ) {
|
||||
return $image->dejaMetaTree;
|
||||
}
|
||||
|
||||
$metadata = $image->getMetadata();
|
||||
if ( !$this->isMetadataValid( $image, $metadata ) ) {
|
||||
wfDebug( "DjVu XML metadata is invalid or missing, should have been fixed in upgradeRow\n" );
|
||||
return false;
|
||||
}
|
||||
wfProfileIn( __METHOD__ );
|
||||
|
||||
wfSuppressWarnings();
|
||||
try {
|
||||
$image->dejaMetaTree = new SimpleXMLElement( $metadata );
|
||||
} catch( Exception $e ) {
|
||||
wfDebug( "Bogus multipage XML metadata on '$image->name'\n" );
|
||||
// Set to false rather than null to avoid further attempts
|
||||
$image->dejaMetaTree = false;
|
||||
}
|
||||
wfRestoreWarnings();
|
||||
wfProfileOut( __METHOD__ );
|
||||
return $image->dejaMetaTree;
|
||||
}
|
||||
|
||||
function getImageSize( $image, $path ) {
|
||||
return $this->getDjVuImage( $image, $path )->getImageSize();
|
||||
}
|
||||
|
||||
function getThumbType( $ext, $mime ) {
|
||||
global $wgDjvuOutputExtension;
|
||||
static $mime;
|
||||
if ( !isset( $mime ) ) {
|
||||
$magic = MimeMagic::singleton();
|
||||
$mime = $magic->guessTypesForExtension( $wgDjvuOutputExtension );
|
||||
}
|
||||
return array( $wgDjvuOutputExtension, $mime );
|
||||
}
|
||||
|
||||
function getMetadata( $image, $path ) {
|
||||
wfDebug( "Getting DjVu metadata for $path\n" );
|
||||
return $this->getDjVuImage( $image, $path )->retrieveMetaData();
|
||||
}
|
||||
|
||||
function getMetadataType( $image ) {
|
||||
return 'djvuxml';
|
||||
}
|
||||
|
||||
function isMetadataValid( $image, $metadata ) {
|
||||
return !empty( $metadata ) && $metadata != serialize(array());
|
||||
}
|
||||
|
||||
function pageCount( $image ) {
|
||||
$tree = $this->getMetaTree( $image );
|
||||
if ( !$tree ) {
|
||||
return false;
|
||||
}
|
||||
return count( $tree->xpath( '//OBJECT' ) );
|
||||
}
|
||||
|
||||
function getPageDimensions( $image, $page ) {
|
||||
$tree = $this->getMetaTree( $image );
|
||||
if ( !$tree ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$o = $tree->BODY[0]->OBJECT[$page-1];
|
||||
if ( $o ) {
|
||||
return array(
|
||||
'width' => intval( $o['width'] ),
|
||||
'height' => intval( $o['height'] )
|
||||
);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
292
includes/media/Generic.php
Normal file
292
includes/media/Generic.php
Normal file
|
|
@ -0,0 +1,292 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Media-handling base classes and generic functionality
|
||||
*/
|
||||
|
||||
/**
|
||||
* Base media handler class
|
||||
*/
|
||||
abstract class MediaHandler {
|
||||
const TRANSFORM_LATER = 1;
|
||||
|
||||
/**
|
||||
* Instance cache
|
||||
*/
|
||||
static $handlers = array();
|
||||
|
||||
/**
|
||||
* Get a MediaHandler for a given MIME type from the instance cache
|
||||
*/
|
||||
static function getHandler( $type ) {
|
||||
global $wgMediaHandlers;
|
||||
if ( !isset( $wgMediaHandlers[$type] ) ) {
|
||||
return false;
|
||||
}
|
||||
$class = $wgMediaHandlers[$type];
|
||||
if ( !isset( self::$handlers[$class] ) ) {
|
||||
self::$handlers[$class] = new $class;
|
||||
if ( !self::$handlers[$class]->isEnabled() ) {
|
||||
self::$handlers[$class] = false;
|
||||
}
|
||||
}
|
||||
return self::$handlers[$class];
|
||||
}
|
||||
|
||||
/*
|
||||
* Validate a thumbnail parameter at parse time.
|
||||
* Return true to accept the parameter, and false to reject it.
|
||||
* If you return false, the parser will do something quiet and forgiving.
|
||||
*/
|
||||
abstract function validateParam( $name, $value );
|
||||
|
||||
/**
|
||||
* Merge a parameter array into a string appropriate for inclusion in filenames
|
||||
*/
|
||||
abstract function makeParamString( $params );
|
||||
|
||||
/**
|
||||
* Parse a param string made with makeParamString back into an array
|
||||
*/
|
||||
abstract function parseParamString( $str );
|
||||
|
||||
/**
|
||||
* Changes the parameter array as necessary, ready for transformation.
|
||||
* Should be idempotent.
|
||||
* Returns false if the parameters are unacceptable and the transform should fail
|
||||
*/
|
||||
abstract function normaliseParams( $image, &$params );
|
||||
|
||||
/**
|
||||
* Get an image size array like that returned by getimagesize(), or false if it
|
||||
* can't be determined.
|
||||
*
|
||||
* @param Image $image The image object, or false if there isn't one
|
||||
* @param string $fileName The filename
|
||||
* @return array
|
||||
*/
|
||||
abstract function getImageSize( $image, $path );
|
||||
|
||||
/**
|
||||
* Get handler-specific metadata which will be saved in the img_metadata field.
|
||||
*
|
||||
* @param Image $image The image object, or false if there isn't one
|
||||
* @param string $fileName The filename
|
||||
* @return string
|
||||
*/
|
||||
function getMetadata( $image, $path ) { return ''; }
|
||||
|
||||
/**
|
||||
* Get a string describing the type of metadata, for display purposes.
|
||||
*/
|
||||
function getMetadataType( $image ) { return false; }
|
||||
|
||||
/**
|
||||
* Check if the metadata string is valid for this handler.
|
||||
* If it returns false, Image will reload the metadata from the file and update the database
|
||||
*/
|
||||
function isMetadataValid( $image, $metadata ) { return true; }
|
||||
|
||||
/**
|
||||
* Get a MediaTransformOutput object representing the transformed output. Does not
|
||||
* actually do the transform.
|
||||
*
|
||||
* @param Image $image The image object
|
||||
* @param string $dstPath Filesystem destination path
|
||||
* @param string $dstUrl Destination URL to use in output HTML
|
||||
* @param array $params Arbitrary set of parameters validated by $this->validateParam()
|
||||
*/
|
||||
function getTransform( $image, $dstPath, $dstUrl, $params ) {
|
||||
return $this->doTransform( $image, $dstPath, $dstUrl, $params, self::TRANSFORM_LATER );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a MediaTransformOutput object representing the transformed output. Does the
|
||||
* transform unless $flags contains self::TRANSFORM_LATER.
|
||||
*
|
||||
* @param Image $image The image object
|
||||
* @param string $dstPath Filesystem destination path
|
||||
* @param string $dstUrl Destination URL to use in output HTML
|
||||
* @param array $params Arbitrary set of parameters validated by $this->validateParam()
|
||||
* @param integer $flags A bitfield, may contain self::TRANSFORM_LATER
|
||||
*/
|
||||
abstract function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 );
|
||||
|
||||
/**
|
||||
* Get the thumbnail extension and MIME type for a given source MIME type
|
||||
* @return array thumbnail extension and MIME type
|
||||
*/
|
||||
function getThumbType( $ext, $mime ) {
|
||||
return array( $ext, $mime );
|
||||
}
|
||||
|
||||
/**
|
||||
* True if the handled types can be transformed
|
||||
*/
|
||||
function canRender() { return true; }
|
||||
/**
|
||||
* True if handled types cannot be displayed directly in a browser
|
||||
* but can be rendered
|
||||
*/
|
||||
function mustRender() { return false; }
|
||||
/**
|
||||
* True if the type has multi-page capabilities
|
||||
*/
|
||||
function isMultiPage() { return false; }
|
||||
/**
|
||||
* Page count for a multi-page document, false if unsupported or unknown
|
||||
*/
|
||||
function pageCount() { return false; }
|
||||
/**
|
||||
* False if the handler is disabled for all files
|
||||
*/
|
||||
function isEnabled() { return true; }
|
||||
|
||||
/**
|
||||
* Get an associative array of page dimensions
|
||||
* Currently "width" and "height" are understood, but this might be
|
||||
* expanded in the future.
|
||||
* Returns false if unknown or if the document is not multi-page.
|
||||
*/
|
||||
function getPageDimensions( $image, $page ) {
|
||||
$gis = $this->getImageSize( $image, $image->getImagePath() );
|
||||
return array(
|
||||
'width' => $gis[0],
|
||||
'height' => $gis[1]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Media handler abstract base class for images
|
||||
*/
|
||||
abstract class ImageHandler extends MediaHandler {
|
||||
function validateParam( $name, $value ) {
|
||||
if ( in_array( $name, array( 'width', 'height' ) ) ) {
|
||||
if ( $value <= 0 ) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function makeParamString( $params ) {
|
||||
if ( isset( $params['physicalWidth'] ) ) {
|
||||
$width = $params['physicalWidth'];
|
||||
} else {
|
||||
$width = $params['width'];
|
||||
}
|
||||
$width = intval( $width );
|
||||
return "{$width}px";
|
||||
}
|
||||
|
||||
function parseParamString( $str ) {
|
||||
$m = false;
|
||||
if ( preg_match( '/^(\d+)px$/', $str, $m ) ) {
|
||||
return array( 'width' => $m[1] );
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function getScriptParams( $params ) {
|
||||
return array( 'width' => $params['width'] );
|
||||
}
|
||||
|
||||
function normaliseParams( $image, &$params ) {
|
||||
$mimeType = $image->getMimeType();
|
||||
|
||||
if ( !isset( $params['width'] ) ) {
|
||||
return false;
|
||||
}
|
||||
if ( !isset( $params['page'] ) ) {
|
||||
$params['page'] = 1;
|
||||
}
|
||||
$srcWidth = $image->getWidth( $params['page'] );
|
||||
$srcHeight = $image->getHeight( $params['page'] );
|
||||
if ( isset( $params['height'] ) && $params['height'] != -1 ) {
|
||||
if ( $params['width'] * $srcHeight > $params['height'] * $srcWidth ) {
|
||||
$params['width'] = wfFitBoxWidth( $srcWidth, $srcHeight, $params['height'] );
|
||||
}
|
||||
}
|
||||
$params['height'] = Image::scaleHeight( $srcWidth, $srcHeight, $params['width'] );
|
||||
if ( !$this->validateThumbParams( $params['width'], $params['height'], $srcWidth, $srcHeight, $mimeType ) ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a transform output object without actually doing the transform
|
||||
*/
|
||||
function getTransform( $image, $dstPath, $dstUrl, $params ) {
|
||||
return $this->doTransform( $image, $dstPath, $dstUrl, $params, self::TRANSFORM_LATER );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate thumbnail parameters and fill in the correct height
|
||||
*
|
||||
* @param integer &$width Specified width (input/output)
|
||||
* @param integer &$height Height (output only)
|
||||
* @return false to indicate that an error should be returned to the user.
|
||||
*/
|
||||
function validateThumbParams( &$width, &$height, $srcWidth, $srcHeight, $mimeType ) {
|
||||
$width = intval( $width );
|
||||
|
||||
# Sanity check $width
|
||||
if( $width <= 0) {
|
||||
wfDebug( __METHOD__.": Invalid destination width: $width\n" );
|
||||
return false;
|
||||
}
|
||||
if ( $srcWidth <= 0 ) {
|
||||
wfDebug( __METHOD__.": Invalid source width: $srcWidth\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
$height = Image::scaleHeight( $srcWidth, $srcHeight, $width );
|
||||
return true;
|
||||
}
|
||||
|
||||
function getScriptedTransform( $image, $script, $params ) {
|
||||
if ( !$this->normaliseParams( $image, $params ) ) {
|
||||
return false;
|
||||
}
|
||||
$url = $script . '&' . wfArrayToCGI( $this->getScriptParams( $params ) );
|
||||
return new ThumbnailImage( $url, $params['width'], $params['height'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for zero-sized thumbnails. These can be generated when
|
||||
* no disk space is available or some other error occurs
|
||||
*
|
||||
* @param $dstPath The location of the suspect file
|
||||
* @param $retval Return value of some shell process, file will be deleted if this is non-zero
|
||||
* @return true if removed, false otherwise
|
||||
*/
|
||||
function removeBadFile( $dstPath, $retval = 0 ) {
|
||||
$removed = false;
|
||||
if( file_exists( $dstPath ) ) {
|
||||
$thumbstat = stat( $dstPath );
|
||||
if( $thumbstat['size'] == 0 || $retval != 0 ) {
|
||||
wfDebugLog( 'thumbnail',
|
||||
sprintf( 'Removing bad %d-byte thumbnail "%s"',
|
||||
$thumbstat['size'], $dstPath ) );
|
||||
unlink( $dstPath );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getImageSize( $image, $path ) {
|
||||
wfSuppressWarnings();
|
||||
$gis = getimagesize( $path );
|
||||
wfRestoreWarnings();
|
||||
return $gis;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
94
includes/media/SVG.php
Normal file
94
includes/media/SVG.php
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
class SvgHandler extends ImageHandler {
|
||||
function isEnabled() {
|
||||
global $wgSVGConverters, $wgSVGConverter;
|
||||
if ( !isset( $wgSVGConverters[$wgSVGConverter] ) ) {
|
||||
wfDebug( "\$wgSVGConverter is invalid, disabling SVG rendering.\n" );
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function mustRender() {
|
||||
return true;
|
||||
}
|
||||
|
||||
function normaliseParams( $image, &$params ) {
|
||||
global $wgSVGMaxSize;
|
||||
if ( !parent::normaliseParams( $image, $params ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
# Don't make an image bigger than wgMaxSVGSize
|
||||
$params['physicalWidth'] = $params['width'];
|
||||
$params['physicalHeight'] = $params['height'];
|
||||
if ( $params['physicalWidth'] > $wgSVGMaxSize ) {
|
||||
$srcWidth = $image->getWidth( $params['page'] );
|
||||
$srcHeight = $image->getHeight( $params['page'] );
|
||||
$params['physicalWidth'] = $wgSVGMaxSize;
|
||||
$params['physicalHeight'] = Image::scaleHeight( $srcWidth, $srcHeight, $wgSVGMaxSize );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 ) {
|
||||
global $wgSVGConverters, $wgSVGConverter, $wgSVGConverterPath;
|
||||
|
||||
if ( !$this->normaliseParams( $image, $params ) ) {
|
||||
return new TransformParameterError( $params );
|
||||
}
|
||||
$clientWidth = $params['width'];
|
||||
$clientHeight = $params['height'];
|
||||
$physicalWidth = $params['physicalWidth'];
|
||||
$physicalHeight = $params['physicalHeight'];
|
||||
$srcWidth = $image->getWidth();
|
||||
$srcHeight = $image->getHeight();
|
||||
$srcPath = $image->getImagePath();
|
||||
|
||||
if ( $flags & self::TRANSFORM_LATER ) {
|
||||
return new ThumbnailImage( $dstUrl, $clientWidth, $clientHeight );
|
||||
}
|
||||
|
||||
if ( !wfMkdirParents( dirname( $dstPath ) ) ) {
|
||||
return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight,
|
||||
wfMsg( 'thumbnail_dest_directory' ) );
|
||||
}
|
||||
|
||||
$err = false;
|
||||
if( isset( $wgSVGConverters[$wgSVGConverter] ) ) {
|
||||
$cmd = str_replace(
|
||||
array( '$path/', '$width', '$height', '$input', '$output' ),
|
||||
array( $wgSVGConverterPath ? wfEscapeShellArg( "$wgSVGConverterPath/" ) : "",
|
||||
intval( $physicalWidth ),
|
||||
intval( $physicalHeight ),
|
||||
wfEscapeShellArg( $srcPath ),
|
||||
wfEscapeShellArg( $dstPath ) ),
|
||||
$wgSVGConverters[$wgSVGConverter] ) . " 2>&1";
|
||||
wfProfileIn( 'rsvg' );
|
||||
wfDebug( __METHOD__.": $cmd\n" );
|
||||
$err = wfShellExec( $cmd, $retval );
|
||||
wfProfileOut( 'rsvg' );
|
||||
}
|
||||
|
||||
$removed = $this->removeBadFile( $dstPath, $retval );
|
||||
if ( $retval != 0 || $removed ) {
|
||||
wfDebugLog( 'thumbnail',
|
||||
sprintf( 'thumbnail failed on %s: error %d "%s" from "%s"',
|
||||
wfHostname(), $retval, trim($err), $cmd ) );
|
||||
return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight, $err );
|
||||
} else {
|
||||
return new ThumbnailImage( $dstUrl, $clientWidth, $clientHeight );
|
||||
}
|
||||
}
|
||||
|
||||
function getImageSize( $image, $path ) {
|
||||
return wfGetSVGsize( $path );
|
||||
}
|
||||
|
||||
function getThumbType( $ext, $mime ) {
|
||||
return array( 'png', 'image/png' );
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
|
@ -19,7 +19,7 @@ image/x-portable-graymap image/x-portable-greymap [BITMAP]
|
|||
image/x-bmp image/bmp application/x-bmp application/bmp [BITMAP]
|
||||
image/x-photoshop image/psd image/x-psd image/photoshop [BITMAP]
|
||||
|
||||
image/svg image/svg+xml application/svg+xml application/svg [DRAWING]
|
||||
image/svg+xml application/svg+xml application/svg image/svg [DRAWING]
|
||||
application/postscript [DRAWING]
|
||||
application/x-latex [DRAWING]
|
||||
application/x-tex [DRAWING]
|
||||
|
|
|
|||
|
|
@ -2139,6 +2139,10 @@ In the latter case you can also use a link, e.g. [[{{ns:Special}}:Export/{{Media
|
|||
'missingimage' => '<b>Missing image</b><br /><i>$1</i>',
|
||||
'filemissing' => 'File missing',
|
||||
'thumbnail_error' => 'Error creating thumbnail: $1',
|
||||
'djvu_page_error' => 'DjVu page out of range',
|
||||
'djvu_no_xml' => 'Unable to fetch XML for DjVu file',
|
||||
'thumbnail_invalid_params' => 'Invalid thumbnail parameters',
|
||||
'thumbnail_dest_directory' => 'Unable to create destination directory',
|
||||
|
||||
# Special:Import
|
||||
'import' => 'Import pages',
|
||||
|
|
@ -2816,8 +2820,8 @@ Please confirm that really want to recreate this page.',
|
|||
* Nederlands|nl",
|
||||
|
||||
# Multipage image navigation
|
||||
'imgmultipageprev' => '← previous page',
|
||||
'imgmultipagenext' => 'next page →',
|
||||
'imgmultipageprev' => '← previous page',
|
||||
'imgmultipagenext' => 'next page →',
|
||||
'imgmultigo' => 'Go!',
|
||||
'imgmultigotopre' => 'Go to page',
|
||||
'imgmultigotopost' => '',
|
||||
|
|
|
|||
|
|
@ -480,4 +480,15 @@ p.mw-ipb-conveniencelinks {
|
|||
#file img, .gallerybox .thumb img {
|
||||
background: url(images/Checker-16x16.png) repeat;
|
||||
}
|
||||
*/
|
||||
*/
|
||||
.MediaTransformError {
|
||||
border: thin solid #777;
|
||||
background-color: #ccc;
|
||||
padding: 0.1em;
|
||||
}
|
||||
.MediaTransformError td {
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1626,3 +1626,13 @@ p.mw-ipb-conveniencelinks {
|
|||
.texvc { direction: ltr; unicode-bidi: embed; }
|
||||
/* Stop floats from intruding into edit area in previews */
|
||||
#toolbar, #wpTextbox1 { clear: both; }
|
||||
|
||||
.MediaTransformError {
|
||||
background-color: #ccc;
|
||||
padding: 0.1em;
|
||||
}
|
||||
.MediaTransformError td {
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
|
|
|||
95
thumb.php
95
thumb.php
|
|
@ -9,44 +9,49 @@ define( 'MW_NO_OUTPUT_COMPRESSION', 1 );
|
|||
require_once( './includes/WebStart.php' );
|
||||
wfProfileIn( 'thumb.php' );
|
||||
wfProfileIn( 'thumb.php-start' );
|
||||
require_once( './includes/GlobalFunctions.php' );
|
||||
require_once( './includes/ImageFunctions.php' );
|
||||
require_once( "$IP/includes/GlobalFunctions.php" );
|
||||
require_once( "$IP/includes/ImageFunctions.php" );
|
||||
|
||||
$wgTrivialMimeDetection = true; //don't use fancy mime detection, just check the file extension for jpg/gif/png.
|
||||
|
||||
require_once( './includes/Image.php' );
|
||||
require_once( './includes/StreamFile.php' );
|
||||
require_once( "$IP/includes/StreamFile.php" );
|
||||
require_once( "$IP/includes/AutoLoader.php" );
|
||||
|
||||
// Get input parameters
|
||||
$fileName = isset( $_REQUEST['f'] ) ? $_REQUEST['f'] : '';
|
||||
$width = isset( $_REQUEST['w'] ) ? intval( $_REQUEST['w'] ) : 0;
|
||||
$page = isset( $_REQUEST['p'] ) ? intval( $_REQUEST['p'] ) : null;
|
||||
|
||||
if ( get_magic_quotes_gpc() ) {
|
||||
$fileName = stripslashes( $fileName );
|
||||
$params = array_map( 'stripslashes', $_REQUEST );
|
||||
} else {
|
||||
$params = $_REQUEST;
|
||||
}
|
||||
|
||||
$pre_render= isset($_REQUEST['r']) && $_REQUEST['r']!="0";
|
||||
$fileName = isset( $params['f'] ) ? $params['f'] : '';
|
||||
unset( $params['f'] );
|
||||
|
||||
// Backwards compatibility parameters
|
||||
if ( isset( $params['w'] ) ) {
|
||||
$params['width'] = $params['w'];
|
||||
unset( $params['w'] );
|
||||
}
|
||||
if ( isset( $params['p'] ) ) {
|
||||
$params['page'] = $params['p'];
|
||||
}
|
||||
unset( $params['r'] );
|
||||
|
||||
// Some basic input validation
|
||||
$fileName = strtr( $fileName, '\\/', '__' );
|
||||
|
||||
// Work out paths, carefully avoiding constructing an Image object because that won't work yet
|
||||
$handler = thumbGetHandler( $fileName );
|
||||
if ( $handler ) {
|
||||
$imagePath = wfImageDir( $fileName ) . '/' . $fileName;
|
||||
$thumbName = $handler->makeParamString( $params ) . "-$fileName";
|
||||
$thumbPath = wfImageThumbDir( $fileName ) . '/' . $thumbName;
|
||||
|
||||
$imagePath = wfImageDir( $fileName ) . '/' . $fileName;
|
||||
$thumbName = "{$width}px-$fileName";
|
||||
if ( ! is_null( $page ) ) {
|
||||
$thumbName = 'page' . $page . '-' . $thumbName;
|
||||
}
|
||||
if ( $pre_render ) {
|
||||
$thumbName .= '.png';
|
||||
}
|
||||
$thumbPath = wfImageThumbDir( $fileName ) . '/' . $thumbName;
|
||||
|
||||
if ( is_file( $thumbPath ) && filemtime( $thumbPath ) >= filemtime( $imagePath ) ) {
|
||||
wfStreamFile( $thumbPath );
|
||||
// Can't log profiling data with no Setup.php
|
||||
exit;
|
||||
if ( is_file( $thumbPath ) && filemtime( $thumbPath ) >= filemtime( $imagePath ) ) {
|
||||
wfStreamFile( $thumbPath );
|
||||
// Can't log profiling data with no Setup.php
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// OK, no valid thumbnail, time to get out the heavy machinery
|
||||
|
|
@ -57,10 +62,7 @@ wfProfileIn( 'thumb.php-render' );
|
|||
$img = Image::newFromName( $fileName );
|
||||
try {
|
||||
if ( $img ) {
|
||||
if ( ! is_null( $page ) ) {
|
||||
$img->selectPage( $page );
|
||||
}
|
||||
$thumb = $img->renderThumb( $width, false );
|
||||
$thumb = $img->transform( $params, Image::RENDER_NOW );
|
||||
} else {
|
||||
$thumb = false;
|
||||
}
|
||||
|
|
@ -69,13 +71,33 @@ try {
|
|||
$thumb = false;
|
||||
}
|
||||
|
||||
if ( $thumb && $thumb->path ) {
|
||||
wfStreamFile( $thumb->path );
|
||||
if ( $thumb && $thumb->getPath() ) {
|
||||
wfStreamFile( $thumb->getPath() );
|
||||
} elseif ( $img ) {
|
||||
header( 'Cache-Control: no-cache' );
|
||||
header( 'Content-Type: text/html; charset=utf-8' );
|
||||
header( 'HTTP/1.1 500 Internal server error' );
|
||||
if ( !$thumb ) {
|
||||
$msg = wfMsgHtml( 'thumbnail_error', 'Image::transform() returned false' );
|
||||
} elseif ( $thumb->isError() ) {
|
||||
$msg = $thumb->toHtml();
|
||||
} else {
|
||||
$msg = wfMsgHtml( 'thumbnail_error', 'No path supplied in thumbnail object' );
|
||||
}
|
||||
echo <<<EOT
|
||||
<html><head><title>Error generating thumbnail</title></head>
|
||||
<body>
|
||||
$msg
|
||||
</body>
|
||||
</html>
|
||||
|
||||
EOT;
|
||||
} else {
|
||||
$badtitle = wfMsg( 'badtitle' );
|
||||
$badtitletext = wfMsg( 'badtitletext' );
|
||||
header( 'Cache-Control: no-cache' );
|
||||
header( 'Content-Type: text/html; charset=utf-8' );
|
||||
header( 'HTTP/1.1 500 Internal server error' );
|
||||
echo "<html><head>
|
||||
<title>$badtitle</title>
|
||||
<body>
|
||||
|
|
@ -89,4 +111,17 @@ wfProfileOut( 'thumb.php-render' );
|
|||
wfProfileOut( 'thumb.php' );
|
||||
wfLogProfilingData();
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
function thumbGetHandler( $fileName ) {
|
||||
// Determine type
|
||||
$magic = MimeMagic::singleton();
|
||||
$extPos = strrpos( $fileName, '.' );
|
||||
if ( $extPos === false ) {
|
||||
return false;
|
||||
}
|
||||
$mime = $magic->guessTypesForExtension( substr( $fileName, $extPos + 1 ) );
|
||||
return MediaHandler::getHandler( $mime );
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
|||
Loading…
Reference in a new issue